4 * http://masonry.desandro.com
6 * The idea is to render all the bricks based on vertical width...
8 * The original code extends 'outlayer' - we might need to use that....
14 * @class Roo.bootstrap.LayoutMasonryAuto
15 * @extends Roo.bootstrap.Component
16 * Bootstrap Layout Masonry class
19 * Create a new Element
20 * @param {Object} config The config object
23 Roo.bootstrap.LayoutMasonryAuto = function(config){
24 Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
27 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component, {
30 * @cfg {Boolean} isFitWidth - resize the width..
32 isFitWidth : false, // options..
34 * @cfg {Boolean} isOriginLeft = left align?
38 * @cfg {Boolean} isOriginTop = top align?
42 * @cfg {Boolean} isLayoutInstant = no animation?
44 isLayoutInstant : false, // needed?
46 * @cfg {Boolean} isResizingContainer = not sure if this is used..
48 isResizingContainer : true,
50 * @cfg {Number} columnWidth width of the columns
56 * @cfg {Number} maxCols maximum number of columns
61 * @cfg {Number} padHeight padding below box..
67 * @cfg {Boolean} isAutoInitial defalut true
76 initialColumnWidth : 0,
79 colYs : null, // array.
86 bricks: null, //CompositeElement
88 // element : null, // wrapped now this.el
89 _isLayoutInited : null,
92 getAutoCreate : function(){
96 cls: 'blog-masonary-wrapper ' + this.cls,
98 cls : 'mas-boxes masonary'
105 getChildContainer: function( )
111 this.boxesEl = this.el.select('.mas-boxes').first();
117 initEvents : function()
121 if(this.isAutoInitial){
122 Roo.log('hook children rendered');
123 this.on('childrenrendered', function() {
124 Roo.log('children rendered');
135 this.currentSize = this.el.getBox(true);
137 /// was window resize... - let's see if this works..
138 Roo.EventManager.onWindowResize(this.resize, this);
140 if(!this.isAutoInitial){
145 this.layout.defer(500,this);
148 reloadItems: function()
150 this.bricks = this.el.select('.masonry-brick', true);
152 this.bricks.each(function(b) {
153 //Roo.log(b.getSize());
154 if (!b.attr('originalwidth')) {
155 b.attr('originalwidth', b.getSize().width);
160 Roo.log(this.bricks.elements.length);
166 var cs = this.el.getBox(true);
168 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
169 Roo.log("no change in with or X");
172 this.currentSize = cs;
180 //this._manageStamps();
182 // don't animate first layout
183 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
184 this.layoutItems( isInstant );
186 // flag for initalized
187 this._isLayoutInited = true;
190 layoutItems : function( isInstant )
192 //var items = this._getItemsForLayout( this.items );
193 // original code supports filtering layout items.. we just ignore it..
195 this._layoutItems( this.bricks , isInstant );
199 _layoutItems : function ( items , isInstant)
201 //this.fireEvent( 'layout', this, items );
204 if ( !items || !items.elements.length ) {
205 // no items, emit event with empty array
210 items.each(function(item) {
211 Roo.log("layout item");
213 // get x/y object from method
214 var position = this._getItemLayoutPosition( item );
216 position.item = item;
217 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
218 queue.push( position );
221 this._processLayoutQueue( queue );
223 /** Sets position of item in DOM
224 * @param {Element} item
225 * @param {Number} x - horizontal position
226 * @param {Number} y - vertical position
227 * @param {Boolean} isInstant - disables transitions
229 _processLayoutQueue : function( queue )
231 for ( var i=0, len = queue.length; i < len; i++ ) {
233 obj.item.position('absolute');
234 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
240 * Any logic you want to do after each layout,
241 * i.e. size the container
243 _postLayout : function()
245 this.resizeContainer();
248 resizeContainer : function()
250 if ( !this.isResizingContainer ) {
253 var size = this._getContainerSize();
255 this.el.setSize(size.width,size.height);
256 this.boxesEl.setSize(size.width,size.height);
262 _resetLayout : function()
264 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
265 this.colWidth = this.el.getWidth();
266 //this.gutter = this.el.getWidth();
268 this.measureColumns();
274 this.colYs.push( 0 );
280 measureColumns : function()
282 this.getContainerWidth();
283 // if columnWidth is 0, default to outerWidth of first item
284 if ( !this.columnWidth ) {
285 var firstItem = this.bricks.first();
287 this.columnWidth = this.containerWidth;
288 if (firstItem && firstItem.attr('originalwidth') ) {
289 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
291 // columnWidth fall back to item of first element
292 Roo.log("set column width?");
293 this.initialColumnWidth = this.columnWidth ;
295 // if first elem has no width, default to size of container
300 if (this.initialColumnWidth) {
301 this.columnWidth = this.initialColumnWidth;
306 // column width is fixed at the top - however if container width get's smaller we should
309 // this bit calcs how man columns..
311 var columnWidth = this.columnWidth += this.gutter;
314 var containerWidth = this.containerWidth + this.gutter;
316 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
317 // fix rounding errors, typically with gutters
318 var excess = columnWidth - containerWidth % columnWidth;
321 // if overshoot is less than a pixel, round up, otherwise floor it
322 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
323 cols = Math[ mathMethod ]( cols );
324 this.cols = Math.max( cols, 1 );
325 this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
327 // padding positioning..
328 var totalColWidth = this.cols * this.columnWidth;
329 var padavail = this.containerWidth - totalColWidth;
330 // so for 2 columns - we need 3 'pads'
332 var padNeeded = (1+this.cols) * this.padWidth;
334 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
336 this.columnWidth += padExtra
337 //this.padWidth = Math.floor(padavail / ( this.cols));
339 // adjust colum width so that padding is fixed??
341 // we have 3 columns ... total = width * 3
342 // we have X left over... that should be used by
344 //if (this.expandC) {
352 getContainerWidth : function()
354 /* // container is parent if fit width
355 var container = this.isFitWidth ? this.element.parentNode : this.element;
356 // check that this.size and size are there
357 // IE8 triggers resize on body size change, so they might not be
359 var size = getSize( container ); //FIXME
360 this.containerWidth = size && size.innerWidth; //FIXME
363 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
367 _getItemLayoutPosition : function( item ) // what is item?
369 // we resize the item to our columnWidth..
371 item.setWidth(this.columnWidth);
372 item.autoBoxAdjust = false;
374 var sz = item.getSize();
376 // how many columns does this brick span
377 var remainder = this.containerWidth % this.columnWidth;
379 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
380 // round if off by 1 pixel, otherwise use ceil
381 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
382 colSpan = Math.min( colSpan, this.cols );
384 // normally this should be '1' as we dont' currently allow multi width columns..
386 var colGroup = this._getColGroup( colSpan );
387 // get the minimum Y value from the columns
388 var minimumY = Math.min.apply( Math, colGroup );
389 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
391 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
393 // position the brick
395 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
396 y: this.currentSize.y + minimumY + this.padHeight
400 // apply setHeight to necessary columns
401 var setHeight = minimumY + sz.height + this.padHeight;
402 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
404 var setSpan = this.cols + 1 - colGroup.length;
405 for ( var i = 0; i < setSpan; i++ ) {
406 this.colYs[ shortColIndex + i ] = setHeight ;
413 * @param {Number} colSpan - number of columns the element spans
414 * @returns {Array} colGroup
416 _getColGroup : function( colSpan )
419 // if brick spans only one column, use all the column Ys
424 // how many different places could this brick fit horizontally
425 var groupCount = this.cols + 1 - colSpan;
426 // for each group potential horizontal position
427 for ( var i = 0; i < groupCount; i++ ) {
428 // make an array of colY values for that one group
429 var groupColYs = this.colYs.slice( i, i + colSpan );
430 // and get the max value of the array
431 colGroup[i] = Math.max.apply( Math, groupColYs );
436 _manageStamp : function( stamp )
438 var stampSize = stamp.getSize();
439 var offset = stamp.getBox();
440 // get the columns that this stamp affects
441 var firstX = this.isOriginLeft ? offset.x : offset.right;
442 var lastX = firstX + stampSize.width;
443 var firstCol = Math.floor( firstX / this.columnWidth );
444 firstCol = Math.max( 0, firstCol );
446 var lastCol = Math.floor( lastX / this.columnWidth );
447 // lastCol should not go over if multiple of columnWidth #425
448 lastCol -= lastX % this.columnWidth ? 0 : 1;
449 lastCol = Math.min( this.cols - 1, lastCol );
451 // set colYs to bottom of the stamp
452 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
455 for ( var i = firstCol; i <= lastCol; i++ ) {
456 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
461 _getContainerSize : function()
463 this.maxY = Math.max.apply( Math, this.colYs );
468 if ( this.isFitWidth ) {
469 size.width = this._getContainerFitWidth();
475 _getContainerFitWidth : function()
478 // count unused columns
481 if ( this.colYs[i] !== 0 ) {
486 // fit container to columns that have been used
487 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
490 needsResizeLayout : function()
492 var previousWidth = this.containerWidth;
493 this.getContainerWidth();
494 return previousWidth !== this.containerWidth;