/** * jquery masonry v2.1.03 * a dynamic layout plugin for jquery * the flip-side of css floats * http://masonry.desandro.com * * licensed under the mit license. * copyright 2011 david desandro */ /*jshint browser: true, curly: true, eqeqeq: true, forin: false, immed: false, newcap: true, noempty: true, strict: true, undef: true */ /*global jquery: false */ (function( window, $, undefined ){ 'use strict'; /* * smartresize: debounced resize event for jquery * * latest version and complete readme available on github: * https://github.com/louisremi/jquery.smartresize.js * * copyright 2011 @louis_remi * licensed under the mit license. */ var $event = $.event, resizetimeout; $event.special.smartresize = { setup: function() { $(this).bind( "resize", $event.special.smartresize.handler ); }, teardown: function() { $(this).unbind( "resize", $event.special.smartresize.handler ); }, handler: function( event, execasap ) { // save the context var context = this, args = arguments; // set correct event type event.type = "smartresize"; if ( resizetimeout ) { cleartimeout( resizetimeout ); } resizetimeout = settimeout(function() { jquery.event.handle.apply( context, args ); }, execasap === "execasap"? 0 : 100 ); } }; $.fn.smartresize = function( fn ) { return fn ? this.bind( "smartresize", fn ) : this.trigger( "smartresize", ["execasap"] ); }; // ========================= masonry =============================== // our "widget" object constructor $.mason = function( options, element ){ this.element = $( element ); this._create( options ); this._init(); }; $.mason.settings = { isresizable: true, isanimated: false, animationoptions: { queue: false, duration: 500 }, gutterwidth: 0, isrtl: false, isfitwidth: false, containerstyle: { position: 'relative' } }; $.mason.prototype = { _filterfindbricks: function( $elems ) { var selector = this.options.itemselector; // if there is a selector // filter/find appropriate item elements return !selector ? $elems : $elems.filter( selector ).add( $elems.find( selector ) ); }, _getbricks: function( $elems ) { var $bricks = this._filterfindbricks( $elems ) .css({ position: 'absolute' }) .addclass('masonry-brick'); return $bricks; }, // sets up widget _create : function( options ) { this.options = $.extend( true, {}, $.mason.settings, options ); this.stylequeue = []; // get original styles in case we re-apply them in .destroy() var elemstyle = this.element[0].style; this.originalstyle = { // get height height: elemstyle.height || '' }; // get other styles that will be overwritten var containerstyle = this.options.containerstyle; for ( var prop in containerstyle ) { this.originalstyle[ prop ] = elemstyle[ prop ] || ''; } this.element.css( containerstyle ); this.horizontaldirection = this.options.isrtl ? 'right' : 'left'; this.offset = { x: parseint( this.element.css( 'padding-' + this.horizontaldirection ), 10 ), y: parseint( this.element.css( 'padding-top' ), 10 ) }; //console.log(this.offset.y); this.isfluid = this.options.columnwidth && typeof this.options.columnwidth === 'function'; // add masonry class first time around var instance = this; settimeout( function() { instance.element.addclass('masonry'); }, 0 ); // bind resize method if ( this.options.isresizable ) { $(window).bind( 'smartresize.masonry', function() { instance.resize(); }); } // need to get bricks this.reloaditems(); }, // _init fires when instance is first created // and when instance is triggered again -> $el.masonry(); _init : function( callback ) { this._getcolumns(); this._relayout( callback ); }, option: function( key, value ){ // set options after initialization: // signature: $('#foo').bar({ cool:false }); if ( $.isplainobject( key ) ){ this.options = $.extend(true, this.options, key); } }, // ====================== general layout ====================== // used on collection of atoms (should be filtered, and sorted before ) // accepts atoms-to-be-laid-out to start with layout : function( $bricks, callback ) { // place each brick for (var i=0, len = $bricks.length; i < len; i++) { this._placebrick( $bricks[i] ); } // set the size of the container var containersize = {}; containersize.height = math.max.apply( math, this.colys ); if ( this.options.isfitwidth ) { var unusedcols = 0; i = this.cols; // count unused columns while ( --i ) { if ( this.colys[i] !== 0 ) { break; } unusedcols++; } // fit container to columns that have been used; containersize.width = (this.cols - unusedcols) * this.columnwidth - this.options.gutterwidth; } this.stylequeue.push({ $el: this.element, style: containersize }); // are we animating the layout arrangement? // use plugin-ish syntax for css or animate var stylefn = !this.islaidout ? 'css' : ( this.options.isanimated ? 'animate' : 'css' ), animopts = this.options.animationoptions; // process stylequeue var obj; for (i=0, len = this.stylequeue.length; i < len; i++) { obj = this.stylequeue[i]; obj.$el[ stylefn ]( obj.style, animopts ); } // clear out queue for next time this.stylequeue = []; // provide $elems as context for the callback if ( callback ) { callback.call( $bricks ); } this.islaidout = true; }, // calculates number of columns // i.e. this.columnwidth = 200 _getcolumns : function() { var container = this.options.isfitwidth ? this.element.parent() : this.element, containerwidth = container.width(); // use fluid columnwidth function if there this.columnwidth = this.isfluid ? this.options.columnwidth( containerwidth ) : // if not, how about the explicitly set option? this.options.columnwidth || // or use the size of the first item this.$bricks.outerwidth(true) || // if there's no items, use size of container containerwidth; this.columnwidth += this.options.gutterwidth; this.cols = math.floor( ( containerwidth + this.options.gutterwidth ) / this.columnwidth ); this.cols = math.max( this.cols, 1 ); }, // layout logic _placebrick: function( brick ) { var $brick = $(brick), colspan, groupcount, groupy, groupcoly, j; //how many columns does this brick span colspan = math.ceil( $brick.outerwidth(true) / ( this.columnwidth + this.options.gutterwidth ) ); colspan = math.min( colspan, this.cols ); if ( colspan === 1 ) { // if brick spans only one column, just like singlemode groupy = this.colys; } else { // brick spans more than one column // how many different places could this brick fit horizontally groupcount = this.cols + 1 - colspan; groupy = []; // for each group potential horizontal position for ( j=0; j < groupcount; j++ ) { // make an array of coly values for that one group groupcoly = this.colys.slice( j, j+colspan ); // and get the max value of the array groupy[j] = math.max.apply( math, groupcoly ); } } // get the minimum y value from the columns var minimumy = math.min.apply( math, groupy ), shortcol = 0; //console.log(math); // find index of short column, the first from the left for (var i=0, len = groupy.length; i < len; i++) { if ( groupy[i] === minimumy ) { shortcol = i; break; } } // position the brick var position = { top: minimumy + this.offset.y }; // position.left or position.right position[ this.horizontaldirection ] = this.columnwidth * shortcol + this.offset.x; this.stylequeue.push({ $el: $brick, style: position }); // apply setheight to necessary columns var setheight = minimumy + $brick.outerheight(true), setspan = this.cols + 1 - len; for ( i=0; i < setspan; i++ ) { this.colys[ shortcol + i ] = setheight; } }, resize: function() { var prevcolcount = this.cols; // get updated colcount this._getcolumns(); if ( this.isfluid || this.cols !== prevcolcount ) { // if column count has changed, trigger new layout this._relayout(); } }, _relayout : function( callback ) { // reset columns var i = this.cols; this.colys = []; while (i--) { this.colys.push( 0 ); } // apply layout logic to all bricks this.layout( this.$bricks, callback ); }, // ====================== convenience methods ====================== // goes through all children again and gets bricks in proper order reloaditems : function() { this.$bricks = this._getbricks( this.element.children() ); }, reload : function( callback ) { this.reloaditems(); this._init( callback ); }, // convienence method for working with infinite scroll appended : function( $content, isanimatedfrombottom, callback ) { if ( isanimatedfrombottom ) { // set new stuff to the bottom this._filterfindbricks( $content ).css({ top: this.element.height() }); var instance = this; settimeout( function(){ instance._appended( $content, callback ); }, 1 ); } else { this._appended( $content, callback ); } }, _appended : function( $content, callback ) { var $newbricks = this._getbricks( $content ); // add new bricks to brick pool this.$bricks = this.$bricks.add( $newbricks ); this.layout( $newbricks, callback ); }, // removes elements from masonry widget remove : function( $content ) { this.$bricks = this.$bricks.not( $content ); $content.remove(); }, // destroys widget, returns elements and container back (close) to original style destroy : function() { this.$bricks .removeclass('masonry-brick') .each(function(){ this.style.position = ''; this.style.top = ''; this.style.left = ''; }); // re-apply saved container styles var elemstyle = this.element[0].style; for ( var prop in this.originalstyle ) { elemstyle[ prop ] = this.originalstyle[ prop ]; } this.element .unbind('.masonry') .removeclass('masonry') .removedata('masonry'); $(window).unbind('.masonry'); } }; // ======================= imagesloaded plugin =============================== /*! * jquery imagesloaded plugin v1.1.0 * http://github.com/desandro/imagesloaded * * mit license. by paul irish et al. */ // $('#my-container').imagesloaded(myfunction) // or // $('img').imagesloaded(myfunction) // execute a callback when all images have loaded. // needed because .load() doesn't work on cached images // callback function gets image collection as argument // `this` is the container $.fn.imagesloaded = function( callback ) { var $this = this, $images = $this.find('img').add( $this.filter('img') ), len = $images.length, blank = 'data:image/gif;base64,r0lgodlhaqabaiaaaaaaap///ywaaaaaaqabaaacauwaow==', loaded = []; function triggercallback() { callback.call( $this, $images ); } function imgloaded( event ) { var img = event.target; if ( img.src !== blank && $.inarray( img, loaded ) === -1 ){ loaded.push( img ); if ( --len <= 0 ){ settimeout( triggercallback ); $images.unbind( '.imagesloaded', imgloaded ); } } } // if no images, trigger immediately if ( !len ) { triggercallback(); } $images.bind( 'load.imagesloaded error.imagesloaded', imgloaded ).each( function() { // cached images don't fire load sometimes, so we reset src. var src = this.src; // webkit hack from http://groups.google.com/group/jquery-dev/browse_thread/thread/eee6ab7b2da50e1f // data uri bypasses webkit log warning (thx doug jones) this.src = blank; this.src = src; }); return $this; }; // helper function for logging errors // $.error breaks jquery chaining var logerror = function( message ) { if ( window.console ) { window.console.error( message ); } }; // ======================= plugin bridge =============================== // leverages data method to either create or return $.mason constructor // a bit from jquery ui // https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js // a bit from jcarousel // https://github.com/jsor/jcarousel/blob/master/lib/jquery.jcarousel.js $.fn.masonry = function( options ) { if ( typeof options === 'string' ) { // call method var args = array.prototype.slice.call( arguments, 1 ); this.each(function(){ var instance = $.data( this, 'masonry' ); if ( !instance ) { logerror( "cannot call methods on masonry prior to initialization; " + "attempted to call method '" + options + "'" ); return; } if ( !$.isfunction( instance[options] ) || options.charat(0) === "_" ) { logerror( "no such method '" + options + "' for masonry instance" ); return; } // apply method instance[ options ].apply( instance, args ); }); } else { this.each(function() { var instance = $.data( this, 'masonry' ); if ( instance ) { // apply options & init instance.option( options || {} ); instance._init(); } else { // initialize new instance $.data( this, 'masonry', new $.mason( options, this ) ); } }); } return this; }; })( window, jquery );/* 閰风珯浠g爜鏁寸悊 http://www.5icool.org */