/** * Flowr.js - Simple jQuery plugin to emulate Flickr's justified view * For usage information refer to http://github.com/kalyan02/flowr-js * * * @author: Kalyan Chakravarthy (http://KalyanChakravarthy.net) * @version: v0.1 */ (function($){ //$("#container2").css( 'border', '1px solid #ccc'); $.fn.flowr = function(options) { $this = this; var ROW_CLASS_NAME = 'flowr-row'; // Class name for the row of flowy var MAX_LAST_ROW_GAP = 25; // If the width of last row is lesser than max-width, recalculation is needed var NO_COPY_FIELDS = [ 'complete', 'data', 'responsive' ]; // these attributes will not be carried forward for append related calls var DEFAULTS = { 'data' : [], 'padding' : 5, // whats the padding between flowy items 'height' : 240, // Minimum height an image row should take 'render' : null, // callback function to get the tag 'append' : false, // TODO 'widthAttr' : 'width', // a custom data structure can specify which attribute refers to height/width 'heightAttr' : 'height', 'maxScale' : 1.5, // In case there is only 1 elment in last row 'maxWidth' : this.width()-1, // 1px is just for offset 'itemWidth' : null, // callback function for width 'itemHeight' : null, // callback function for height 'complete' : null, // complete callback 'rowClassName' : ROW_CLASS_NAME, 'rows' : -1, // Maximum number of rows to render. -1 for no limit. 'responsive' : true // make content responsive }; var settings = $.extend( DEFAULTS, options); // If data is being appended, we already have settings // If we already have settings, retrieve them if( settings.append && $this.data('lastSettings') ) { lastSettings = $this.data('lastSettings'); // Copy over the settings from previous init for( attr in DEFAULTS ) { if( NO_COPY_FIELDS.indexOf(attr)<0 && settings[attr] == DEFAULTS[attr] ) { settings[attr] = lastSettings[attr]; } } // Check if we have an incomplete last row lastRow = $this.data('lastRow'); if( lastRow.data.length > 0 && settings.maxWidth-lastRow.width > MAX_LAST_ROW_GAP ) { // Prepend the incomplete row to newly loaded data and redraw lastRowData = lastSettings.data.slice( lastSettings.data.length - lastRow.data.length - 1 ); settings.data = lastRowData.concat(settings.data); // Remove the incomplete row // TODO: Don't reload this stuff later. Reattach to new row. $( '.' + settings.rowClassName + ':last', $this ).detach(); } else { // console.log( lastRow.data.length ); // console.log( lastRow.width ); } } // only on the first initial call if( !settings.responsive && !settings.append ) $this.width( $this.width() ); // Basic sanity checks if( !(settings.data instanceof Array) ) return; if( typeof(settings.padding) != 'number' ) settings.padding = parseInt( settings.padding ); if( typeof(settings.itemWidth) != 'function' ) { settings.itemWidth = function(data) { return data[ settings.widthAttr ]; } } if( typeof(settings.itemHeight) != 'function' ) { settings.itemHeight = function(data) { return data[ settings.heightAttr ]; } } // A standalone utility to calculate the item widths for a particular row // Returns rowWidth: width occupied & data : the items in the new row var utils = { getNextRow : function( data, settings ) { var itemIndex = 0; var itemsLength = data.length; var lineItems = []; var lineWidth = 0; var maxWidth = settings.maxWidth; var paddingSize = settings.padding; // console.log( 'maxItems=' + data.length ); requiredPadding = function() { var extraPads = arguments.length == 1 ? arguments[0] : 0; return (lineItems.length - 1 + extraPads) * settings.padding; } while( lineWidth + requiredPadding() < settings.maxWidth && (itemIndex < itemsLength) ) { var itemData = data[ itemIndex ]; var itemWidth = settings.itemWidth.call( $this, itemData ); var itemHeight = settings.itemHeight.call( $this, itemData ); var minHeight = settings.height; var minWidth = Math.floor( itemWidth * settings.height / itemHeight ); var newLineWidth = lineWidth + minWidth + requiredPadding(1); if (minWidth > settings.maxWidth) { // very short+wide images like panoramas // show them even if ugly, as wide as possible minWidth = settings.maxWidth-1; minHeight = settings.height * minHeight / minWidth; } // console.log( 'lineWidth = ' + lineWidth ); // console.log( 'newLineWidth = ' + newLineWidth ); if( newLineWidth < settings.maxWidth ) { lineItems.push({ 'height' : minHeight, 'width' : minWidth, 'itemData' : itemData }); lineWidth += minWidth; itemIndex ++; } else { // We'd have exceeded width. So break off to scale. // console.log( 'breaking off = ' + itemIndex ); // console.log( 'leave off size = ' + lineItems.length ); break; } } //while // Scale the size to max width testWidth=0; if( lineWidth < settings.maxWidth ) { var fullScaleWidth = settings.maxWidth - requiredPadding() - 10; var currScaleWidth = lineWidth; var scaleFactor = fullScaleWidth / currScaleWidth; if( scaleFactor > settings.maxScale ) scaleFactor = 1; var newHeight = Math.round( settings.height * scaleFactor ); for( i=0; i 0 ) { if( settings.rows > 0 && currentRow >= settings.rows ) break; // remove the number of elements in the new row from the top of data stack data.splice( 0, rowData.data.length ); // Create a new row div, add class, append the htmls and insert the flowy items var $row = $('
').addClass(settings.rowClassName); for( i=0; i rowData.data.length="+rowData.data.length +" rowData.width="+rowData.width ); currentRow++; $this.data('lastRow', rowData ); } // store the current state of settings and the items in last row // we'll need this info when we append more items $this.data('lastSettings', settings ); // onComplete callback // pass back info about list of rows and items rendered if( typeof (settings.complete) == 'function' ) { var completeData = { renderedRows : currentRow, renderedItems : currentItem } settings.complete.call( $this, completeData ); } }); }; })(jQuery);