WordPress source code - jquery-plugins (jquery.masonry-2.1.05.js)

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 );
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 );

Contact Us
Can't read the article? Contact us for free answers! Free help for personal, small business sites!
Tel: 020-2206-9892
QQ咨询:1025174874
(iii) E-mail: info@361sale.com
Working hours: Monday to Friday, 9:30-18:30, holidays off
© Reprint statement
This article was written by Harry
THE END
If you like it, support it.
kudos0 share (joys, benefits, privileges etc) with others
commentaries sofa-buying

Please log in to post a comment

    No comments