/** * jQuery Masonry v2.0.110531 - corner-stamp branch * 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 */ (function( window, $, undefined ){ /* * 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(); }; // styles of container element we want to keep track of var masonryContainerStyles = [ 'position', 'height' ]; $.Mason.settings = { isResizable: true, isAnimated: false, animationOptions: { queue: false, duration: 500 }, gutterWidth: 0, isRTL: false, isFitWidth: false }; $.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 = []; // need to get bricks this.reloadItems(); // get original styles in case we re-apply them in .destroy() var elemStyle = this.element[0].style; this.originalStyle = {}; for ( var i=0, len = masonryContainerStyles.length; i < len; i++ ) { var prop = masonryContainerStyles[i]; this.originalStyle[ prop ] = elemStyle[ prop ] || null; } this.element.css({ position : 'relative' }); this.horizontalDirection = this.options.isRTL ? 'right' : 'left'; this.offset = {}; // get top left position of where the bricks should be var $cursor = $( document.createElement('div') ); this.element.prepend( $cursor ); this.offset.y = Math.round( $cursor.position().top ); // get horizontal offset if ( !this.options.isRTL ) { this.offset.x = Math.round( $cursor.position().left ); } else { $cursor.css({ 'float': 'right', display: 'inline-block'}); this.offset.x = Math.round( this.element.outerWidth() - $cursor.position().left ); } $cursor.remove(); // 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(); }); } }, // _init fires when instance is first created // and when instance is triggered again -> $el.masonry(); _init : function( callback ) { this._getColumns('masonry'); 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 ) { // layout logic var $brick, colSpan, groupCount, groupY, groupColY, j; for (var i=0, len = $bricks.length; i < len; i++) { $brick = $( $bricks[i] ); //how many columns does this brick span colSpan = Math.ceil( $brick.outerWidth(true) / this.columnWidth ); colSpan = Math.min( colSpan, this.cols ); if ( colSpan === 1 ) { // if brick spans only one column, just like singleMode this._placeBrick( $brick, 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 ); } this._placeBrick( $brick, groupY ); } } // set the size of the container var containerSize = {}; containerSize.height = Math.max.apply( Math, this.colYs ) - this.offset.y; if ( this.options.isFitWidth ) { containerSize.width = this.cols * 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(); this.columnWidth = 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 ); }, _placeBrick : function( $brick, setY ) { // get the minimum Y value from the columns var minimumY = Math.min.apply( Math, setY ), shortCol = 0; // Find index of short column, the first from the left for (var i=0, len = setY.length; i < len; i++) { if ( setY[i] === minimumY ) { shortCol = i; break; } } // position the brick var position = { top : minimumY }; // 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() { this._getColumns('masonry'); this._reLayout(); }, _reLayout : function( callback ) { var cornerCols = 0; if ( this.options.cornerStampSelector ) { var $cornerStamp = this.element.find( this.options.cornerStampSelector ), stampWidth = $cornerStamp.outerWidth(true) - ( this.element.width() % this.columnWidth ) ; cornerCols = Math.ceil( stampWidth / this.columnWidth ); } // reset columns var i = this.cols; this.colYs = []; while (i--) { this.colYs.push( this.offset.y ); } if ( cornerCols ) { for ( i = Math.max( this.cols - cornerCols, cornerCols ); i < this.cols; i++ ) { this.colYs[i] = this.offset.y + $cornerStamp.outerHeight(true); } } // 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 = null; this.style.top = null; this.style.left = null; }); // re-apply saved container styles var elemStyle = this.element[0].style; for ( var i=0, len = masonryContainerStyles.length; i < len; i++ ) { var prop = masonryContainerStyles[i]; elemStyle[ prop ] = this.originalStyle[ prop ]; } this.element .unbind('.masonry') .removeClass('masonry') .removeData('masonry'); $(window).unbind('.masonry'); } }; // ======================= imagesLoaded Plugin =============================== // A fork of http://gist.github.com/268257 by Paul Irish // mit license. paul irish. 2010. // webkit fix from Oren Solomianik. thx! $.fn.imagesLoaded = function(callback){ var elems = this.find('img'), len = elems.length, blank = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==", _this = this, loadImage = function(){ if ( --len <= 0 && this.src !== blank ) { callback.call( _this ); elems.unbind( 'load', loadImage ) } }; // if no images, trigger immediately if ( !len ) { callback.call( this ); return this; } elems.bind( 'load', loadImage ).each(function(){ // cached images don't fire load sometimes, so we reset src. if (this.complete || this.complete === undefined){ 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; }; // ======================= 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 ) { return $.error( "cannot call methods on masonry prior to initialization; " + "attempted to call method '" + options + "'" ); } if ( !$.isFunction( instance[options] ) || options.charAt(0) === "_" ) { return $.error( "no such method '" + options + "' for masonry instance" ); } // 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 ); $(function(){ $('#container').masonry({ itemSelector: '.box', columnWidth: 100, cornerStampSelector: '.corner-stamp' }); });
.corner-stamp { width: 280px; height: 340px; padding: 10px; margin: 10px; float: right; background: red; color: white; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } /**** Base styles ****/ html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, figcaption, figure, footer, header, hgroup, menu, nav, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } html { overflow-y: scroll; } body { font: 13px 'Helvetica Neue', Arial, sans-serif; background: #D8D5D2; color: #222; line-height: 1.6em; } a { color: #A2C; text-decoration: none; font-weight: bold; } a:hover { color: #D26; } a:active { background: hsla( 0, 100%, 100%, 0.5 ); } h1, h2 { font-weight: 100; line-height: 1.2em; margin-bottom: 0.6em; } h1 { font-size: 36px; } h2 { font-size: 24px; } h3 { font-size: 17px; font-weight: bold; } h3, p, ul, ol, pre, dl { margin-bottom: 1.0em; } strong { font-weight: bold; } /**** Content ****/ #content { padding: 10px 10px 10px 210px; } .copy h2 { clear: both; } a img { border: none; } pre, code { font-family: Monaco, monospace; font-size: 12px; background: #111; color: #F5F5F5; } p code { padding: 1px 3px; } pre { padding: 10px; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } ul, ol { padding-left: 1.3em;} .hidden { display: none; } .copy { width: 600px; line-height: 1.55em; } blockquote { margin: 0; font: italic 18px Georgia, serif; } dt { font-weight: bold; font-size: 14px; } dd + dt { margin-top: 0.5em; } dd { margin-left: 1.0em; } button { -webkit-appearance: push-button; } #site-footer { clear: both; margin: 20px 0px; border-top: 2px solid white; padding-top: 10px; line-height: 30px; font-size: 95%; font-style: italic; } .license-copy { font-size: 85%; } .demos #copy, .docs #content { max-width: 640px; } .docs #content h2 { border-top: 2px solid #FFF; padding-top: 10px; } .docs #content h2:target { background: #D26; color: white; padding: 10px 5px 5px; } /**** Demos ****/ #container { background: #FFF; padding: 5px; margin-bottom: 20px; border-radius: 5px; clear: both; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } .box { margin: 5px; padding: 5px; background: #D8D5D2; font-size: 11px; line-height: 1.4em; float: left; -webkit-border-radius: 5px; -moz-border-radius: 5px; border-radius: 5px; } .box h2 { font-size: 14px; font-weight: 200; } .box img, #tumblelog img { display: block; width: 100%; } .rtl .box { float: right; text-align: right; direction: rtl; } .col1 { width: 80px; } .col2 { width: 180px; } .col3 { width: 280px; } .col4 { width: 380px; } .col5 { width: 480px; } /**** Transitions ****/ .transitions-enabled.masonry, .transitions-enabled.masonry .masonry-brick { -webkit-transition-duration: 0.7s; -moz-transition-duration: 0.7s; -o-transition-duration: 0.7s; transition-duration: 0.7s; } .transitions-enabled.masonry { -webkit-transition-property: height, width; -moz-transition-property: height, width; -o-transition-property: height, width; transition-property: height, width; } .transitions-enabled.masonry .masonry-brick { -webkit-transition-property: left, right, top; -moz-transition-property: left, right, top; -o-transition-property: left, right, top; transition-property: left, right, top; } /* disable transitions on container */ .transitions-enabled.infinite-scroll.masonry { -webkit-transition-property: none; -moz-transition-property: none; -o-transition-property: none; transition-property: none; } /**** Pygments ****/ code .s1, code .s { color: #78BD55; } /* string */ code .mi, /* integer */ code .cp, /* doctype */ code .kc { color: #5298D4; } /*boolean*/ code .k { color: #E39B79; } /* keyword */ code .kd, /* storage */ code .na { color: #A9D866; } /* markup attribute */ code .p { color: #EDB; } /* punctuation */ code .o { color: #F63; } /* operator */ code .nb { color: #AA97AC;} /* support */ /* comment */ code .c, code .c1 { color: #666; font-style: italic; } code .nt { color: #A0C8FC; } /* Markup open tag */ code .nf { color: #9EA8B8; } /* css id */ code .nc { color: #A78352; } /* CSS class */ code .m { color: #DE8E50; } /* CSS value */ code .nd { color: #9FAD7E; } /* CSS pseudo selector */ /**** Clearfix ****/ .clearfix:before, .clearfix:after { content: ""; display: table; } .clearfix:after { clear: both; } .clearfix { zoom: 1; }