1 /**2 * jQuery Masonry v2.1.053 * A dynamic layout plugin for jQuery4 * The flip-side of CSS Floats5 * http://masonry.desandro.com6 *7 * Licensed under the MIT license.8 * Copyright 2012 David DeSandro9 */1011 /*jshint browser: true, curly: true, eqeqeq: true, forin: false, immed: false, newcap: true, noempty: true, strict: true, undef: true */12 /*global jQuery: false */1314 (function( window, $, undefined ){1516 'use strict';1718 /*19 * smartresize: debounced resize event for jQuery20 *21 * latest version and complete README available on Github:22 * https://github.com/louisremi/jquery.smartresize.js23 *24 * Copyright 2011 @louis_remi25 * Licensed under the MIT license.26 */2728 var $event = $.event,29 resizeTimeout;3031 $event.special.smartresize = {32 setup: function() {33 $(this).bind( "resize", $event.special.smartresize.handler );34 },35 teardown: function() {36 $(this).unbind( "resize", $event.special.smartresize.handler );37 },38 handler: function( event, execAsap ) {39 // Save the context40 var context = this,41 args = arguments;4243 // set correct event type44 event.type = "smartresize";4546 if ( resizeTimeout ) { clearTimeout( resizeTimeout ); }47 resizeTimeout = setTimeout(function() {48 $.event.handle.apply( context, args );49 }, execAsap === "execAsap"? 0 : 100 );50 }51 };5253 $.fn.smartresize = function( fn ) {54 return fn ? this.bind( "smartresize", fn ) : this.trigger( "smartresize", ["execAsap"] );55 };56575859 // ========================= Masonry ===============================606162 // our "Widget" object constructor63 $.Mason = function( options, element ){64 this.element = $( element );6566 this._create( options );67 this._init();68 };6970 $.Mason.settings = {71 isResizable: true,72 isAnimated: false,73 animationOptions: {74 queue: false,75 duration: 50076 },77 gutterWidth: 0,78 isRTL: false,79 isFitWidth: false,80 containerStyle: {81 position: 'relative'82 }83 };8485 $.Mason.prototype = {8687 _filterFindBricks: function( $elems ) {88 var selector = this.options.itemSelector;89 // if there is a selector90 // filter/find appropriate item elements91 return !selector ? $elems : $elems.filter( selector ).add( $elems.find( selector ) );92 },9394 _getBricks: function( $elems ) {95 var $bricks = this._filterFindBricks( $elems )96 .css({ position: 'absolute' })97 .addClass('masonry-brick');98 return $bricks;99 },100101 // sets up widget102 _create : function( options ) {103104 this.options = $.extend( true, {}, $.Mason.settings, options );105 this.styleQueue = [];106107 // get original styles in case we re-apply them in .destroy()108 var elemStyle = this.element[0].style;109 this.originalStyle = {110 // get height111 height: elemStyle.height || ''112 };113 // get other styles that will be overwritten114 var containerStyle = this.options.containerStyle;115 for ( var prop in containerStyle ) {116 this.originalStyle[ prop ] = elemStyle[ prop ] || '';117 }118119 this.element.css( containerStyle );120121 this.horizontalDirection = this.options.isRTL ? 'right' : 'left';122123 this.offset = {124 x: parseInt( this.element.css( 'padding-' + this.horizontalDirection ), 10 ),125 y: parseInt( this.element.css( 'padding-top' ), 10 )126 };127128 this.isFluid = this.options.columnWidth && typeof this.options.columnWidth === 'function';129130 // add masonry class first time around131 var instance = this;132 setTimeout( function() {133 instance.element.addClass('masonry');134 }, 0 );135136 // bind resize method137 if ( this.options.isResizable ) {138 $(window).bind( 'smartresize.masonry', function() {139 instance.resize();140 });141 }142143144 // need to get bricks145 this.reloadItems();146147 },148149 // _init fires when instance is first created150 // and when instance is triggered again -> $el.masonry();151 _init : function( callback ) {152 this._getColumns();153 this._reLayout( callback );154 },155156 option: function( key, value ){157 // set options AFTER initialization:158 // signature: $('#foo').bar({ cool:false });159 if ( $.isPlainObject( key ) ){160 this.options = $.extend(true, this.options, key);161 }162 },163164 // ====================== General Layout ======================165166 // used on collection of atoms (should be filtered, and sorted before )167 // accepts atoms-to-be-laid-out to start with168 layout : function( $bricks, callback ) {169170 // place each brick171 for (var i=0, len = $bricks.length; i < len; i++) {172 this._placeBrick( $bricks[i] );173 }174175 // set the size of the container176 var containerSize = {};177 containerSize.height = Math.max.apply( Math, this.colYs );178 if ( this.options.isFitWidth ) {179 var unusedCols = 0;180 i = this.cols;181 // count unused columns182 while ( --i ) {183 if ( this.colYs[i] !== 0 ) {184 break;185 }186 unusedCols++;187 }188 // fit container to columns that have been used;189 containerSize.width = (this.cols - unusedCols) * this.columnWidth - this.options.gutterWidth;190 }191 this.styleQueue.push({ $el: this.element, style: containerSize });192193 // are we animating the layout arrangement?194 // use plugin-ish syntax for css or animate195 var styleFn = !this.isLaidOut ? 'css' : (196 this.options.isAnimated ? 'animate' : 'css'197 ),198 animOpts = this.options.animationOptions;199200 // process styleQueue201 var obj;202 for (i=0, len = this.styleQueue.length; i < len; i++) {203 obj = this.styleQueue[i];204 obj.$el[ styleFn ]( obj.style, animOpts );205 }206207 // clear out queue for next time208 this.styleQueue = [];209210 // provide $elems as context for the callback211 if ( callback ) {212 callback.call( $bricks );213 }214215 this.isLaidOut = true;216 },217218 // calculates number of columns219 // i.e. this.columnWidth = 200220 _getColumns : function() {221 var container = this.options.isFitWidth ? this.element.parent() : this.element,222 containerWidth = container.width();223224 // use fluid columnWidth function if there225 this.columnWidth = this.isFluid ? this.options.columnWidth( containerWidth ) :226 // if not, how about the explicitly set option?227 this.options.columnWidth ||228 // or use the size of the first item229 this.$bricks.outerWidth(true) ||230 // if there's no items, use size of container231 containerWidth;232233 this.columnWidth += this.options.gutterWidth;234235 this.cols = Math.floor( ( containerWidth + this.options.gutterWidth ) / this.columnWidth );236 this.cols = Math.max( this.cols, 1 );237238 },239240 // layout logic241 _placeBrick: function( brick ) {242 var $brick = $(brick),243 colSpan, groupCount, groupY, groupColY, j;244245 //how many columns does this brick span246 colSpan = Math.ceil( $brick.outerWidth(true) / this.columnWidth );247 colSpan = Math.min( colSpan, this.cols );248249 if ( colSpan === 1 ) {250 // if brick spans only one column, just like singleMode251 groupY = this.colYs;252 } else {253 // brick spans more than one column254 // how many different places could this brick fit horizontally255 groupCount = this.cols + 1 - colSpan;256 groupY = [];257258 // for each group potential horizontal position259 for ( j=0; j < groupCount; j++ ) {260 // make an array of colY values for that one group261 groupColY = this.colYs.slice( j, j+colSpan );262 // and get the max value of the array263 groupY[j] = Math.max.apply( Math, groupColY );264 }265266 }267268 // get the minimum Y value from the columns269 var minimumY = Math.min.apply( Math, groupY ),270 shortCol = 0;271272 // Find index of short column, the first from the left273 for (var i=0, len = groupY.length; i < len; i++) {274 if ( groupY[i] === minimumY ) {275 shortCol = i;276 break;277 }278 }279280 // position the brick281 var position = {282 top: minimumY + this.offset.y283 };284 // position.left or position.right285 position[ this.horizontalDirection ] = this.columnWidth * shortCol + this.offset.x;286 this.styleQueue.push({ $el: $brick, style: position });287288 // apply setHeight to necessary columns289 var setHeight = minimumY + $brick.outerHeight(true),290 setSpan = this.cols + 1 - len;291 for ( i=0; i < setSpan; i++ ) {292 this.colYs[ shortCol + i ] = setHeight;293 }294295 },296297298 resize: function() {299 var prevColCount = this.cols;300 // get updated colCount301 this._getColumns();302 if ( this.isFluid || this.cols !== prevColCount ) {303 // if column count has changed, trigger new layout304 this._reLayout();305 }306 },307308309 _reLayout : function( callback ) {310 // reset columns311 var i = this.cols;312 this.colYs = [];313 while (i--) {314 this.colYs.push( 0 );315 }316 // apply layout logic to all bricks317 this.layout( this.$bricks, callback );318 },319320 // ====================== Convenience methods ======================321322 // goes through all children again and gets bricks in proper order323 reloadItems : function() {324 this.$bricks = this._getBricks( this.element.children() );325 },326327328 reload : function( callback ) {329 this.reloadItems();330 this._init( callback );331 },332333334 // convienence method for working with Infinite Scroll335 appended : function( $content, isAnimatedFromBottom, callback ) {336 if ( isAnimatedFromBottom ) {337 // set new stuff to the bottom338 this._filterFindBricks( $content ).css({ top: this.element.height() });339 var instance = this;340 setTimeout( function(){341 instance._appended( $content, callback );342 }, 1 );343 } else {344 this._appended( $content, callback );345 }346 },347348 _appended : function( $content, callback ) {349 var $newBricks = this._getBricks( $content );350 // add new bricks to brick pool351 this.$bricks = this.$bricks.add( $newBricks );352 this.layout( $newBricks, callback );353 },354355 // removes elements from Masonry widget356 remove : function( $content ) {357 this.$bricks = this.$bricks.not( $content );358 $content.remove();359 },360361 // destroys widget, returns elements and container back (close) to original style362 destroy : function() {363364 this.$bricks365 .removeClass('masonry-brick')366 .each(function(){367 this.style.position = '';368 this.style.top = '';369 this.style.left = '';370 });371372 // re-apply saved container styles373 var elemStyle = this.element[0].style;374 for ( var prop in this.originalStyle ) {375 elemStyle[ prop ] = this.originalStyle[ prop ];376 }377378 this.element379 .unbind('.masonry')380 .removeClass('masonry')381 .removeData('masonry');382383 $(window).unbind('.masonry');384385 }386387 };388389390 // ======================= imagesLoaded Plugin ===============================391 /*!392 * jQuery imagesLoaded plugin v1.1.0393 * http://github.com/desandro/imagesloaded394 *395 * MIT License. by Paul Irish et al.396 */397398399 // $('#my-container').imagesLoaded(myFunction)400 // or401 // $('img').imagesLoaded(myFunction)402403 // execute a callback when all images have loaded.404 // needed because .load() doesn't work on cached images405406 // callback function gets image collection as argument407 // `this` is the container408409 $.fn.imagesLoaded = function( callback ) {410 var $this = this,411 $images = $this.find('img').add( $this.filter('img') ),412 len = $images.length,413 blank = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==',414 loaded = [];415416 function triggerCallback() {417 callback.call( $this, $images );418 }419420 function imgLoaded( event ) {421 var img = event.target;422 if ( img.src !== blank && $.inArray( img, loaded ) === -1 ){423 loaded.push( img );424 if ( --len <= 0 ){425 setTimeout( triggerCallback );426 $images.unbind( '.imagesLoaded', imgLoaded );427 }428 }429 }430431 // if no images, trigger immediately432 if ( !len ) {433 triggerCallback();434 }435436 $images.bind( 'load.imagesLoaded error.imagesLoaded', imgLoaded ).each( function() {437 // cached images don't fire load sometimes, so we reset src.438 var src = this.src;439 // webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f440 // data uri bypasses webkit log warning (thx doug jones)441 this.src = blank;442 this.src = src;443 });444445 return $this;446 };447448449 // helper function for logging errors450 // $.error breaks jQuery chaining451 var logError = function( message ) {452 if ( window.console ) {453 window.console.error( message );454 }455 };456457 // ======================= Plugin bridge ===============================458 // leverages data method to either create or return $.Mason constructor459 // A bit from jQuery UI460 // https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js461 // A bit from jcarousel462 // https://github.com/jsor/jcarousel/blob/master/lib/jquery.jcarousel.js463464 $.fn.masonry = function( options ) {465 if ( typeof options === 'string' ) {466 // call method467 var args = Array.prototype.slice.call( arguments, 1 );468469 this.each(function(){470 var instance = $.data( this, 'masonry' );471 if ( !instance ) {472 logError( "cannot call methods on masonry prior to initialization; " +473 "attempted to call method '" + options + "'" );474 return;475 }476 if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) {477 logError( "no such method '" + options + "' for masonry instance" );478 return;479 }480 // apply method481 instance[ options ].apply( instance, args );482 });483 } else {484 this.each(function() {485 var instance = $.data( this, 'masonry' );486 if ( instance ) {487 // apply options & init488 instance.option( options || {} );489 instance._init();490 } else {491 // initialize new instance492 $.data( this, 'masonry', new $.Mason( options, this ) );493 }494 });495 }496 return this;497 };498499 })( window, jQuery );1 /** 2 * jQuery Masonry v2.1.05 3 * A dynamic layout plugin for jQuery 4 * The flip-side of CSS Floats 5 * http://masonry.desandro.com 6 * 7 * Licensed under the MIT license. 8 * Copyright 2012 David DeSandro 9 */ 10 11 /*jshint browser: true, curly: true, eqeqeq: true, forin: false, immed: false, newcap: true, noempty: true, strict: true, undef: true */ 12 /*global jQuery: false */ 13 14 (function( window, $, undefined ){ 15 16 'use strict'; 17 18 /* 19 * smartresize: debounced resize event for jQuery 20 * 21 * latest version and complete README available on Github: 22 * https://github.com/louisremi/jquery.smartresize.js 23 * 24 * Copyright 2011 @louis_remi 25 * Licensed under the MIT license. 26 */ 27 28 var $event = $.event, 29 resizeTimeout; 30 31 $event.special.smartresize = { 32 setup: function() { 33 $(this).bind( "resize", $event.special.smartresize.handler ); 34 }, 35 teardown: function() { 36 $(this).unbind( "resize", $event.special.smartresize.handler ); 37 }, 38 handler: function( event, execAsap ) { 39 // Save the context 40 var context = this, 41 args = arguments; 42 43 // set correct event type 44 event.type = "smartresize"; 45 46 if ( resizeTimeout ) { clearTimeout( resizeTimeout ); } 47 resizeTimeout = setTimeout(function() { 48 $.event.handle.apply( context, args ); 49 }, execAsap === "execAsap"? 0 : 100 ); 50 } 51 }; 52 53 $.fn.smartresize = function( fn ) { 54 return fn ? this.bind( "smartresize", fn ) : this.trigger( "smartresize", ["execAsap"] ); 55 }; 56 57 58 59 // ========================= Masonry =============================== 60 61 62 // our "Widget" object constructor 63 $.Mason = function( options, element ){ 64 this.element = $( element ); 65 66 this._create( options ); 67 this._init(); 68 }; 69 70 $.Mason.settings = { 71 isResizable: true, 72 isAnimated: false, 73 animationOptions: { 74 queue: false, 75 duration: 500 76 }, 77 gutterWidth: 0, 78 isRTL: false, 79 isFitWidth: false, 80 containerStyle: { 81 position: 'relative' 82 } 83 }; 84 85 $.Mason.prototype = { 86 87 _filterFindBricks: function( $elems ) { 88 var selector = this.options.itemSelector; 89 // if there is a selector 90 // filter/find appropriate item elements 91 return !selector ? $elems : $elems.filter( selector ).add( $elems.find( selector ) ); 92 }, 93 94 _getBricks: function( $elems ) { 95 var $bricks = this._filterFindBricks( $elems ) 96 .css({ position: 'absolute' }) 97 .addClass('masonry-brick'); 98 return $bricks; 99 }, 100 101 // sets up widget 102 _create : function( options ) { 103 104 this.options = $.extend( true, {}, $.Mason.settings, options ); 105 this.styleQueue = []; 106 107 // get original styles in case we re-apply them in .destroy() 108 var elemStyle = this.element[0].style; 109 this.originalStyle = { 110 // get height 111 height: elemStyle.height || '' 112 }; 113 // get other styles that will be overwritten 114 var containerStyle = this.options.containerStyle; 115 for ( var prop in containerStyle ) { 116 this.originalStyle[ prop ] = elemStyle[ prop ] || ''; 117 } 118 119 this.element.css( containerStyle ); 120 121 this.horizontalDirection = this.options.isRTL ? 'right' : 'left'; 122 123 this.offset = { 124 x: parseInt( this.element.css( 'padding-' + this.horizontalDirection ), 10 ), 125 y: parseInt( this.element.css( 'padding-top' ), 10 ) 126 }; 127 128 this.isFluid = this.options.columnWidth && typeof this.options.columnWidth === 'function'; 129 130 // add masonry class first time around 131 var instance = this; 132 setTimeout( function() { 133 instance.element.addClass('masonry'); 134 }, 0 ); 135 136 // bind resize method 137 if ( this.options.isResizable ) { 138 $(window).bind( 'smartresize.masonry', function() { 139 instance.resize(); 140 }); 141 } 142 143 144 // need to get bricks 145 this.reloadItems(); 146 147 }, 148 149 // _init fires when instance is first created 150 // and when instance is triggered again -> $el.masonry(); 151 _init : function( callback ) { 152 this._getColumns(); 153 this._reLayout( callback ); 154 }, 155 156 option: function( key, value ){ 157 // set options AFTER initialization: 158 // signature: $('#foo').bar({ cool:false }); 159 if ( $.isPlainObject( key ) ){ 160 this.options = $.extend(true, this.options, key); 161 } 162 }, 163 164 // ====================== General Layout ====================== 165 166 // used on collection of atoms (should be filtered, and sorted before ) 167 // accepts atoms-to-be-laid-out to start with 168 layout : function( $bricks, callback ) { 169 170 // place each brick 171 for (var i=0, len = $bricks.length; i < len; i++) { 172 this._placeBrick( $bricks[i] ); 173 } 174 175 // set the size of the container 176 var containerSize = {}; 177 containerSize.height = Math.max.apply( Math, this.colYs ); 178 if ( this.options.isFitWidth ) { 179 var unusedCols = 0; 180 i = this.cols; 181 // count unused columns 182 while ( --i ) { 183 if ( this.colYs[i] !== 0 ) { 184 break; 185 } 186 unusedCols++; 187 } 188 // fit container to columns that have been used; 189 containerSize.width = (this.cols - unusedCols) * this.columnWidth - this.options.gutterWidth; 190 } 191 this.styleQueue.push({ $el: this.element, style: containerSize }); 192 193 // are we animating the layout arrangement? 194 // use plugin-ish syntax for css or animate 195 var styleFn = !this.isLaidOut ? 'css' : ( 196 this.options.isAnimated ? 'animate' : 'css' 197 ), 198 animOpts = this.options.animationOptions; 199 200 // process styleQueue 201 var obj; 202 for (i=0, len = this.styleQueue.length; i < len; i++) { 203 obj = this.styleQueue[i]; 204 obj.$el[ styleFn ]( obj.style, animOpts ); 205 } 206 207 // clear out queue for next time 208 this.styleQueue = []; 209 210 // provide $elems as context for the callback 211 if ( callback ) { 212 callback.call( $bricks ); 213 } 214 215 this.isLaidOut = true; 216 }, 217 218 // calculates number of columns 219 // i.e. this.columnWidth = 200 220 _getColumns : function() { 221 var container = this.options.isFitWidth ? this.element.parent() : this.element, 222 containerWidth = container.width(); 223 224 // use fluid columnWidth function if there 225 this.columnWidth = this.isFluid ? this.options.columnWidth( containerWidth ) : 226 // if not, how about the explicitly set option? 227 this.options.columnWidth || 228 // or use the size of the first item 229 this.$bricks.outerWidth(true) || 230 // if there's no items, use size of container 231 containerWidth; 232 233 this.columnWidth += this.options.gutterWidth; 234 235 this.cols = Math.floor( ( containerWidth + this.options.gutterWidth ) / this.columnWidth ); 236 this.cols = Math.max( this.cols, 1 ); 237 238 }, 239 240 // layout logic 241 _placeBrick: function( brick ) { 242 var $brick = $(brick), 243 colSpan, groupCount, groupY, groupColY, j; 244 245 //how many columns does this brick span 246 colSpan = Math.ceil( $brick.outerWidth(true) / this.columnWidth ); 247 colSpan = Math.min( colSpan, this.cols ); 248 249 if ( colSpan === 1 ) { 250 // if brick spans only one column, just like singleMode 251 groupY = this.colYs; 252 } else { 253 // brick spans more than one column 254 // how many different places could this brick fit horizontally 255 groupCount = this.cols + 1 - colSpan; 256 groupY = []; 257 258 // for each group potential horizontal position 259 for ( j=0; j < groupCount; j++ ) { 260 // make an array of colY values for that one group 261 groupColY = this.colYs.slice( j, j+colSpan ); 262 // and get the max value of the array 263 groupY[j] = Math.max.apply( Math, groupColY ); 264 } 265 266 } 267 268 // get the minimum Y value from the columns 269 var minimumY = Math.min.apply( Math, groupY ), 270 shortCol = 0; 271 272 // Find index of short column, the first from the left 273 for (var i=0, len = groupY.length; i < len; i++) { 274 if ( groupY[i] === minimumY ) { 275 shortCol = i; 276 break; 277 } 278 } 279 280 // position the brick 281 var position = { 282 top: minimumY + this.offset.y 283 }; 284 // position.left or position.right 285 position[ this.horizontalDirection ] = this.columnWidth * shortCol + this.offset.x; 286 this.styleQueue.push({ $el: $brick, style: position }); 287 288 // apply setHeight to necessary columns 289 var setHeight = minimumY + $brick.outerHeight(true), 290 setSpan = this.cols + 1 - len; 291 for ( i=0; i < setSpan; i++ ) { 292 this.colYs[ shortCol + i ] = setHeight; 293 } 294 295 }, 296 297 298 resize: function() { 299 var prevColCount = this.cols; 300 // get updated colCount 301 this._getColumns(); 302 if ( this.isFluid || this.cols !== prevColCount ) { 303 // if column count has changed, trigger new layout 304 this._reLayout(); 305 } 306 }, 307 308 309 _reLayout : function( callback ) { 310 // reset columns 311 var i = this.cols; 312 this.colYs = []; 313 while (i--) { 314 this.colYs.push( 0 ); 315 } 316 // apply layout logic to all bricks 317 this.layout( this.$bricks, callback ); 318 }, 319 320 // ====================== Convenience methods ====================== 321 322 // goes through all children again and gets bricks in proper order 323 reloadItems : function() { 324 this.$bricks = this._getBricks( this.element.children() ); 325 }, 326 327 328 reload : function( callback ) { 329 this.reloadItems(); 330 this._init( callback ); 331 }, 332 333 334 // convienence method for working with Infinite Scroll 335 appended : function( $content, isAnimatedFromBottom, callback ) { 336 if ( isAnimatedFromBottom ) { 337 // set new stuff to the bottom 338 this._filterFindBricks( $content ).css({ top: this.element.height() }); 339 var instance = this; 340 setTimeout( function(){ 341 instance._appended( $content, callback ); 342 }, 1 ); 343 } else { 344 this._appended( $content, callback ); 345 } 346 }, 347 348 _appended : function( $content, callback ) { 349 var $newBricks = this._getBricks( $content ); 350 // add new bricks to brick pool 351 this.$bricks = this.$bricks.add( $newBricks ); 352 this.layout( $newBricks, callback ); 353 }, 354 355 // removes elements from Masonry widget 356 remove : function( $content ) { 357 this.$bricks = this.$bricks.not( $content ); 358 $content.remove(); 359 }, 360 361 // destroys widget, returns elements and container back (close) to original style 362 destroy : function() { 363 364 this.$bricks 365 .removeClass('masonry-brick') 366 .each(function(){ 367 this.style.position = ''; 368 this.style.top = ''; 369 this.style.left = ''; 370 }); 371 372 // re-apply saved container styles 373 var elemStyle = this.element[0].style; 374 for ( var prop in this.originalStyle ) { 375 elemStyle[ prop ] = this.originalStyle[ prop ]; 376 } 377 378 this.element 379 .unbind('.masonry') 380 .removeClass('masonry') 381 .removeData('masonry'); 382 383 $(window).unbind('.masonry'); 384 385 } 386 387 }; 388 389 390 // ======================= imagesLoaded Plugin =============================== 391 /*! 392 * jQuery imagesLoaded plugin v1.1.0 393 * http://github.com/desandro/imagesloaded 394 * 395 * MIT License. by Paul Irish et al. 396 */ 397 398 399 // $('#my-container').imagesLoaded(myFunction) 400 // or 401 // $('img').imagesLoaded(myFunction) 402 403 // execute a callback when all images have loaded. 404 // needed because .load() doesn't work on cached images 405 406 // callback function gets image collection as argument 407 // `this` is the container 408 409 $.fn.imagesLoaded = function( callback ) { 410 var $this = this, 411 $images = $this.find('img').add( $this.filter('img') ), 412 len = $images.length, 413 blank = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==', 414 loaded = []; 415 416 function triggerCallback() { 417 callback.call( $this, $images ); 418 } 419 420 function imgLoaded( event ) { 421 var img = event.target; 422 if ( img.src !== blank && $.inArray( img, loaded ) === -1 ){ 423 loaded.push( img ); 424 if ( --len <= 0 ){ 425 setTimeout( triggerCallback ); 426 $images.unbind( '.imagesLoaded', imgLoaded ); 427 } 428 } 429 } 430 431 // if no images, trigger immediately 432 if ( !len ) { 433 triggerCallback(); 434 } 435 436 $images.bind( 'load.imagesLoaded error.imagesLoaded', imgLoaded ).each( function() { 437 // cached images don't fire load sometimes, so we reset src. 438 var src = this.src; 439 // webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f 440 // data uri bypasses webkit log warning (thx doug jones) 441 this.src = blank; 442 this.src = src; 443 }); 444 445 return $this; 446 }; 447 448 449 // helper function for logging errors 450 // $.error breaks jQuery chaining 451 var logError = function( message ) { 452 if ( window.console ) { 453 window.console.error( message ); 454 } 455 }; 456 457 // ======================= Plugin bridge =============================== 458 // leverages data method to either create or return $.Mason constructor 459 // A bit from jQuery UI 460 // https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js 461 // A bit from jcarousel 462 // https://github.com/jsor/jcarousel/blob/master/lib/jquery.jcarousel.js 463 464 $.fn.masonry = function( options ) { 465 if ( typeof options === 'string' ) { 466 // call method 467 var args = Array.prototype.slice.call( arguments, 1 ); 468 469 this.each(function(){ 470 var instance = $.data( this, 'masonry' ); 471 if ( !instance ) { 472 logError( "cannot call methods on masonry prior to initialization; " + 473 "attempted to call method '" + options + "'" ); 474 return; 475 } 476 if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) { 477 logError( "no such method '" + options + "' for masonry instance" ); 478 return; 479 } 480 // apply method 481 instance[ options ].apply( instance, args ); 482 }); 483 } else { 484 this.each(function() { 485 var instance = $.data( this, 'masonry' ); 486 if ( instance ) { 487 // apply options & init 488 instance.option( options || {} ); 489 instance._init(); 490 } else { 491 // initialize new instance 492 $.data( this, 'masonry', new $.Mason( options, this ) ); 493 } 494 }); 495 } 496 return this; 497 }; 498 499 })( window, jQuery );1 /** 2 * jQuery Masonry v2.1.05 3 * A dynamic layout plugin for jQuery 4 * The flip-side of CSS Floats 5 * http://masonry.desandro.com 6 * 7 * Licensed under the MIT license. 8 * Copyright 2012 David DeSandro 9 */ 10 11 /*jshint browser: true, curly: true, eqeqeq: true, forin: false, immed: false, newcap: true, noempty: true, strict: true, undef: true */ 12 /*global jQuery: false */ 13 14 (function( window, $, undefined ){ 15 16 'use strict'; 17 18 /* 19 * smartresize: debounced resize event for jQuery 20 * 21 * latest version and complete README available on Github: 22 * https://github.com/louisremi/jquery.smartresize.js 23 * 24 * Copyright 2011 @louis_remi 25 * Licensed under the MIT license. 26 */ 27 28 var $event = $.event, 29 resizeTimeout; 30 31 $event.special.smartresize = { 32 setup: function() { 33 $(this).bind( "resize", $event.special.smartresize.handler ); 34 }, 35 teardown: function() { 36 $(this).unbind( "resize", $event.special.smartresize.handler ); 37 }, 38 handler: function( event, execAsap ) { 39 // Save the context 40 var context = this, 41 args = arguments; 42 43 // set correct event type 44 event.type = "smartresize"; 45 46 if ( resizeTimeout ) { clearTimeout( resizeTimeout ); } 47 resizeTimeout = setTimeout(function() { 48 $.event.handle.apply( context, args ); 49 }, execAsap === "execAsap"? 0 : 100 ); 50 } 51 }; 52 53 $.fn.smartresize = function( fn ) { 54 return fn ? this.bind( "smartresize", fn ) : this.trigger( "smartresize", ["execAsap"] ); 55 }; 56 57 58 59 // ========================= Masonry =============================== 60 61 62 // our "Widget" object constructor 63 $.Mason = function( options, element ){ 64 this.element = $( element ); 65 66 this._create( options ); 67 this._init(); 68 }; 69 70 $.Mason.settings = { 71 isResizable: true, 72 isAnimated: false, 73 animationOptions: { 74 queue: false, 75 duration: 500 76 }, 77 gutterWidth: 0, 78 isRTL: false, 79 isFitWidth: false, 80 containerStyle: { 81 position: 'relative' 82 } 83 }; 84 85 $.Mason.prototype = { 86 87 _filterFindBricks: function( $elems ) { 88 var selector = this.options.itemSelector; 89 // if there is a selector 90 // filter/find appropriate item elements 91 return !selector ? $elems : $elems.filter( selector ).add( $elems.find( selector ) ); 92 }, 93 94 _getBricks: function( $elems ) { 95 var $bricks = this._filterFindBricks( $elems ) 96 .css({ position: 'absolute' }) 97 .addClass('masonry-brick'); 98 return $bricks; 99 }, 100 101 // sets up widget 102 _create : function( options ) { 103 104 this.options = $.extend( true, {}, $.Mason.settings, options ); 105 this.styleQueue = []; 106 107 // get original styles in case we re-apply them in .destroy() 108 var elemStyle = this.element[0].style; 109 this.originalStyle = { 110 // get height 111 height: elemStyle.height || '' 112 }; 113 // get other styles that will be overwritten 114 var containerStyle = this.options.containerStyle; 115 for ( var prop in containerStyle ) { 116 this.originalStyle[ prop ] = elemStyle[ prop ] || ''; 117 } 118 119 this.element.css( containerStyle ); 120 121 this.horizontalDirection = this.options.isRTL ? 'right' : 'left'; 122 123 this.offset = { 124 x: parseInt( this.element.css( 'padding-' + this.horizontalDirection ), 10 ), 125 y: parseInt( this.element.css( 'padding-top' ), 10 ) 126 }; 127 128 this.isFluid = this.options.columnWidth && typeof this.options.columnWidth === 'function'; 129 130 // add masonry class first time around 131 var instance = this; 132 setTimeout( function() { 133 instance.element.addClass('masonry'); 134 }, 0 ); 135 136 // bind resize method 137 if ( this.options.isResizable ) { 138 $(window).bind( 'smartresize.masonry', function() { 139 instance.resize(); 140 }); 141 } 142 143 144 // need to get bricks 145 this.reloadItems(); 146 147 }, 148 149 // _init fires when instance is first created 150 // and when instance is triggered again -> $el.masonry(); 151 _init : function( callback ) { 152 this._getColumns(); 153 this._reLayout( callback ); 154 }, 155 156 option: function( key, value ){ 157 // set options AFTER initialization: 158 // signature: $('#foo').bar({ cool:false }); 159 if ( $.isPlainObject( key ) ){ 160 this.options = $.extend(true, this.options, key); 161 } 162 }, 163 164 // ====================== General Layout ====================== 165 166 // used on collection of atoms (should be filtered, and sorted before ) 167 // accepts atoms-to-be-laid-out to start with 168 layout : function( $bricks, callback ) { 169 170 // place each brick 171 for (var i=0, len = $bricks.length; i < len; i++) { 172 this._placeBrick( $bricks[i] ); 173 } 174 175 // set the size of the container 176 var containerSize = {}; 177 containerSize.height = Math.max.apply( Math, this.colYs ); 178 if ( this.options.isFitWidth ) { 179 var unusedCols = 0; 180 i = this.cols; 181 // count unused columns 182 while ( --i ) { 183 if ( this.colYs[i] !== 0 ) { 184 break; 185 } 186 unusedCols++; 187 } 188 // fit container to columns that have been used; 189 containerSize.width = (this.cols - unusedCols) * this.columnWidth - this.options.gutterWidth; 190 } 191 this.styleQueue.push({ $el: this.element, style: containerSize }); 192 193 // are we animating the layout arrangement? 194 // use plugin-ish syntax for css or animate 195 var styleFn = !this.isLaidOut ? 'css' : ( 196 this.options.isAnimated ? 'animate' : 'css' 197 ), 198 animOpts = this.options.animationOptions; 199 200 // process styleQueue 201 var obj; 202 for (i=0, len = this.styleQueue.length; i < len; i++) { 203 obj = this.styleQueue[i]; 204 obj.$el[ styleFn ]( obj.style, animOpts ); 205 } 206 207 // clear out queue for next time 208 this.styleQueue = []; 209 210 // provide $elems as context for the callback 211 if ( callback ) { 212 callback.call( $bricks ); 213 } 214 215 this.isLaidOut = true; 216 }, 217 218 // calculates number of columns 219 // i.e. this.columnWidth = 200 220 _getColumns : function() { 221 var container = this.options.isFitWidth ? this.element.parent() : this.element, 222 containerWidth = container.width(); 223 224 // use fluid columnWidth function if there 225 this.columnWidth = this.isFluid ? this.options.columnWidth( containerWidth ) : 226 // if not, how about the explicitly set option? 227 this.options.columnWidth || 228 // or use the size of the first item 229 this.$bricks.outerWidth(true) || 230 // if there's no items, use size of container 231 containerWidth; 232 233 this.columnWidth += this.options.gutterWidth; 234 235 this.cols = Math.floor( ( containerWidth + this.options.gutterWidth ) / this.columnWidth ); 236 this.cols = Math.max( this.cols, 1 ); 237 238 }, 239 240 // layout logic 241 _placeBrick: function( brick ) { 242 var $brick = $(brick), 243 colSpan, groupCount, groupY, groupColY, j; 244 245 //how many columns does this brick span 246 colSpan = Math.ceil( $brick.outerWidth(true) / this.columnWidth ); 247 colSpan = Math.min( colSpan, this.cols ); 248 249 if ( colSpan === 1 ) { 250 // if brick spans only one column, just like singleMode 251 groupY = this.colYs; 252 } else { 253 // brick spans more than one column 254 // how many different places could this brick fit horizontally 255 groupCount = this.cols + 1 - colSpan; 256 groupY = []; 257 258 // for each group potential horizontal position 259 for ( j=0; j < groupCount; j++ ) { 260 // make an array of colY values for that one group 261 groupColY = this.colYs.slice( j, j+colSpan ); 262 // and get the max value of the array 263 groupY[j] = Math.max.apply( Math, groupColY ); 264 } 265 266 } 267 268 // get the minimum Y value from the columns 269 var minimumY = Math.min.apply( Math, groupY ), 270 shortCol = 0; 271 272 // Find index of short column, the first from the left 273 for (var i=0, len = groupY.length; i < len; i++) { 274 if ( groupY[i] === minimumY ) { 275 shortCol = i; 276 break; 277 } 278 } 279 280 // position the brick 281 var position = { 282 top: minimumY + this.offset.y 283 }; 284 // position.left or position.right 285 position[ this.horizontalDirection ] = this.columnWidth * shortCol + this.offset.x; 286 this.styleQueue.push({ $el: $brick, style: position }); 287 288 // apply setHeight to necessary columns 289 var setHeight = minimumY + $brick.outerHeight(true), 290 setSpan = this.cols + 1 - len; 291 for ( i=0; i < setSpan; i++ ) { 292 this.colYs[ shortCol + i ] = setHeight; 293 } 294 295 }, 296 297 298 resize: function() { 299 var prevColCount = this.cols; 300 // get updated colCount 301 this._getColumns(); 302 if ( this.isFluid || this.cols !== prevColCount ) { 303 // if column count has changed, trigger new layout 304 this._reLayout(); 305 } 306 }, 307 308 309 _reLayout : function( callback ) { 310 // reset columns 311 var i = this.cols; 312 this.colYs = []; 313 while (i--) { 314 this.colYs.push( 0 ); 315 } 316 // apply layout logic to all bricks 317 this.layout( this.$bricks, callback ); 318 }, 319 320 // ====================== Convenience methods ====================== 321 322 // goes through all children again and gets bricks in proper order 323 reloadItems : function() { 324 this.$bricks = this._getBricks( this.element.children() ); 325 }, 326 327 328 reload : function( callback ) { 329 this.reloadItems(); 330 this._init( callback ); 331 }, 332 333 334 // convienence method for working with Infinite Scroll 335 appended : function( $content, isAnimatedFromBottom, callback ) { 336 if ( isAnimatedFromBottom ) { 337 // set new stuff to the bottom 338 this._filterFindBricks( $content ).css({ top: this.element.height() }); 339 var instance = this; 340 setTimeout( function(){ 341 instance._appended( $content, callback ); 342 }, 1 ); 343 } else { 344 this._appended( $content, callback ); 345 } 346 }, 347 348 _appended : function( $content, callback ) { 349 var $newBricks = this._getBricks( $content ); 350 // add new bricks to brick pool 351 this.$bricks = this.$bricks.add( $newBricks ); 352 this.layout( $newBricks, callback ); 353 }, 354 355 // removes elements from Masonry widget 356 remove : function( $content ) { 357 this.$bricks = this.$bricks.not( $content ); 358 $content.remove(); 359 }, 360 361 // destroys widget, returns elements and container back (close) to original style 362 destroy : function() { 363 364 this.$bricks 365 .removeClass('masonry-brick') 366 .each(function(){ 367 this.style.position = ''; 368 this.style.top = ''; 369 this.style.left = ''; 370 }); 371 372 // re-apply saved container styles 373 var elemStyle = this.element[0].style; 374 for ( var prop in this.originalStyle ) { 375 elemStyle[ prop ] = this.originalStyle[ prop ]; 376 } 377 378 this.element 379 .unbind('.masonry') 380 .removeClass('masonry') 381 .removeData('masonry'); 382 383 $(window).unbind('.masonry'); 384 385 } 386 387 }; 388 389 390 // ======================= imagesLoaded Plugin =============================== 391 /*! 392 * jQuery imagesLoaded plugin v1.1.0 393 * http://github.com/desandro/imagesloaded 394 * 395 * MIT License. by Paul Irish et al. 396 */ 397 398 399 // $('#my-container').imagesLoaded(myFunction) 400 // or 401 // $('img').imagesLoaded(myFunction) 402 403 // execute a callback when all images have loaded. 404 // needed because .load() doesn't work on cached images 405 406 // callback function gets image collection as argument 407 // `this` is the container 408 409 $.fn.imagesLoaded = function( callback ) { 410 var $this = this, 411 $images = $this.find('img').add( $this.filter('img') ), 412 len = $images.length, 413 blank = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==', 414 loaded = []; 415 416 function triggerCallback() { 417 callback.call( $this, $images ); 418 } 419 420 function imgLoaded( event ) { 421 var img = event.target; 422 if ( img.src !== blank && $.inArray( img, loaded ) === -1 ){ 423 loaded.push( img ); 424 if ( --len <= 0 ){ 425 setTimeout( triggerCallback ); 426 $images.unbind( '.imagesLoaded', imgLoaded ); 427 } 428 } 429 } 430 431 // if no images, trigger immediately 432 if ( !len ) { 433 triggerCallback(); 434 } 435 436 $images.bind( 'load.imagesLoaded error.imagesLoaded', imgLoaded ).each( function() { 437 // cached images don't fire load sometimes, so we reset src. 438 var src = this.src; 439 // webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f 440 // data uri bypasses webkit log warning (thx doug jones) 441 this.src = blank; 442 this.src = src; 443 }); 444 445 return $this; 446 }; 447 448 449 // helper function for logging errors 450 // $.error breaks jQuery chaining 451 var logError = function( message ) { 452 if ( window.console ) { 453 window.console.error( message ); 454 } 455 }; 456 457 // ======================= Plugin bridge =============================== 458 // leverages data method to either create or return $.Mason constructor 459 // A bit from jQuery UI 460 // https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js 461 // A bit from jcarousel 462 // https://github.com/jsor/jcarousel/blob/master/lib/jquery.jcarousel.js 463 464 $.fn.masonry = function( options ) { 465 if ( typeof options === 'string' ) { 466 // call method 467 var args = Array.prototype.slice.call( arguments, 1 ); 468 469 this.each(function(){ 470 var instance = $.data( this, 'masonry' ); 471 if ( !instance ) { 472 logError( "cannot call methods on masonry prior to initialization; " + 473 "attempted to call method '" + options + "'" ); 474 return; 475 } 476 if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) { 477 logError( "no such method '" + options + "' for masonry instance" ); 478 return; 479 } 480 // apply method 481 instance[ options ].apply( instance, args ); 482 }); 483 } else { 484 this.each(function() { 485 var instance = $.data( this, 'masonry' ); 486 if ( instance ) { 487 // apply options & init 488 instance.option( options || {} ); 489 instance._init(); 490 } else { 491 // initialize new instance 492 $.data( this, 'masonry', new $.Mason( options, this ) ); 493 } 494 }); 495 } 496 return this; 497 }; 498 499 })( window, jQuery );
© Reprint statement
This article was written by Harry
Link to this article:https://www.361sale.com/en/12266
The article is copyrighted and must be reproduced with attribution.
THE END
No comments