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
55 * @cfg {Number} padHeight padding below box..
61 * @cfg {Boolean} isAutoInitial defalut true
70 initialColumnWidth : 0,
73 colYs : null, // array.
80 bricks: null, //CompositeElement
82 // element : null, // wrapped now this.el
83 _isLayoutInited : null,
86 getAutoCreate : function(){
90 cls: 'blog-masonary-wrapper ' + this.cls,
92 cls : 'mas-boxes masonary'
99 getChildContainer: function( )
105 this.boxesEl = this.el.select('.mas-boxes').first();
111 initEvents : function()
115 if(this.isAutoInitial){
116 Roo.log('hook children rendered');
117 this.on('childrenrendered', function() {
118 Roo.log('children rendered');
129 this.currentSize = this.el.getBox(true);
131 /// was window resize... - let's see if this works..
132 Roo.EventManager.onWindowResize(this.resize, this);
134 if(!this.isAutoInitial){
139 this.layout.defer(500,this);
142 reloadItems: function()
146 this.bricks = this.el.select('.masonry-brick', true);
148 this.bricks.each(function(b) {
149 //Roo.log(b.getSize());
150 if (!b.attr('originalwidth')) {
151 b.attr('originalwidth', b.getSize().width);
156 Roo.log(this.bricks.elements.length);
162 var cs = this.el.getBox(true);
164 if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
165 Roo.log("no change in with or X");
168 this.currentSize = cs;
176 //this._manageStamps();
178 // don't animate first layout
179 var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
180 this.layoutItems( isInstant );
182 // flag for initalized
183 this._isLayoutInited = true;
186 layoutItems : function( isInstant )
188 //var items = this._getItemsForLayout( this.items );
189 // original code supports filtering layout items.. we just ignore it..
191 this._layoutItems( this.bricks , isInstant );
195 _layoutItems : function ( items , isInstant)
197 //this.fireEvent( 'layout', this, items );
200 if ( !items || !items.elements.length ) {
201 // no items, emit event with empty array
206 items.each(function(item) {
207 Roo.log("layout item");
209 // get x/y object from method
210 var position = this._getItemLayoutPosition( item );
212 position.item = item;
213 position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
214 queue.push( position );
217 this._processLayoutQueue( queue );
219 /** Sets position of item in DOM
220 * @param {Element} item
221 * @param {Number} x - horizontal position
222 * @param {Number} y - vertical position
223 * @param {Boolean} isInstant - disables transitions
225 _processLayoutQueue : function( queue )
227 for ( var i=0, len = queue.length; i < len; i++ ) {
229 obj.item.position('absolute');
230 obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
236 * Any logic you want to do after each layout,
237 * i.e. size the container
239 _postLayout : function()
241 this.resizeContainer();
244 resizeContainer : function()
246 if ( !this.isResizingContainer ) {
249 var size = this._getContainerSize();
251 this.el.setSize(size.width,size.height);
252 this.boxesEl.setSize(size.width,size.height);
258 _resetLayout : function()
260 //this.getSize(); // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
261 this.colWidth = this.el.getWidth();
262 //this.gutter = this.el.getWidth();
264 this.measureColumns();
270 this.colYs.push( 0 );
276 measureColumns : function()
278 this.getContainerWidth();
279 // if columnWidth is 0, default to outerWidth of first item
280 if ( !this.columnWidth ) {
281 var firstItem = this.bricks.first();
283 this.columnWidth = this.containerWidth;
284 if (firstItem && firstItem.attr('originalwidth') ) {
285 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
287 // columnWidth fall back to item of first element
288 Roo.log("set column width?");
289 this.initialColumnWidth = this.columnWidth ;
291 // if first elem has no width, default to size of container
296 if (this.initialColumnWidth) {
297 this.columnWidth = this.initialColumnWidth;
302 // column width is fixed at the top - however if container width get's smaller we should
305 // this bit calcs how man columns..
307 var columnWidth = this.columnWidth += this.gutter;
310 var containerWidth = this.containerWidth + this.gutter;
312 var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
313 // fix rounding errors, typically with gutters
314 var excess = columnWidth - containerWidth % columnWidth;
317 // if overshoot is less than a pixel, round up, otherwise floor it
318 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
319 cols = Math[ mathMethod ]( cols );
320 this.cols = Math.max( cols, 1 );
323 // padding positioning..
324 var totalColWidth = this.cols * this.columnWidth;
325 var padavail = this.containerWidth - totalColWidth;
326 // so for 2 columns - we need 3 'pads'
328 var padNeeded = (1+this.cols) * this.padWidth;
330 var padExtra = Math.floor((padavail - padNeeded) / this.cols);
332 this.columnWidth += padExtra
333 //this.padWidth = Math.floor(padavail / ( this.cols));
335 // adjust colum width so that padding is fixed??
337 // we have 3 columns ... total = width * 3
338 // we have X left over... that should be used by
340 //if (this.expandC) {
348 getContainerWidth : function()
350 /* // container is parent if fit width
351 var container = this.isFitWidth ? this.element.parentNode : this.element;
352 // check that this.size and size are there
353 // IE8 triggers resize on body size change, so they might not be
355 var size = getSize( container ); //FIXME
356 this.containerWidth = size && size.innerWidth; //FIXME
359 this.containerWidth = this.el.getBox(true).width; //maybe use getComputedWidth
363 _getItemLayoutPosition : function( item ) // what is item?
365 // we resize the item to our columnWidth..
367 item.setWidth(this.columnWidth);
368 item.autoBoxAdjust = false;
370 var sz = item.getSize();
372 // how many columns does this brick span
373 var remainder = this.containerWidth % this.columnWidth;
375 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
376 // round if off by 1 pixel, otherwise use ceil
377 var colSpan = Math[ mathMethod ]( sz.width / this.columnWidth );
378 colSpan = Math.min( colSpan, this.cols );
380 // normally this should be '1' as we dont' currently allow multi width columns..
382 var colGroup = this._getColGroup( colSpan );
383 // get the minimum Y value from the columns
384 var minimumY = Math.min.apply( Math, colGroup );
385 Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
387 var shortColIndex = colGroup.indexOf( minimumY ); // broken on ie8..?? probably...
389 // position the brick
391 x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
392 y: this.currentSize.y + minimumY + this.padHeight
396 // apply setHeight to necessary columns
397 var setHeight = minimumY + sz.height + this.padHeight;
398 //Roo.log([ 'setHeight', minimumY, sz.height, setHeight ]);
400 var setSpan = this.cols + 1 - colGroup.length;
401 for ( var i = 0; i < setSpan; i++ ) {
402 this.colYs[ shortColIndex + i ] = setHeight ;
409 * @param {Number} colSpan - number of columns the element spans
410 * @returns {Array} colGroup
412 _getColGroup : function( colSpan )
415 // if brick spans only one column, use all the column Ys
420 // how many different places could this brick fit horizontally
421 var groupCount = this.cols + 1 - colSpan;
422 // for each group potential horizontal position
423 for ( var i = 0; i < groupCount; i++ ) {
424 // make an array of colY values for that one group
425 var groupColYs = this.colYs.slice( i, i + colSpan );
426 // and get the max value of the array
427 colGroup[i] = Math.max.apply( Math, groupColYs );
432 _manageStamp : function( stamp )
434 var stampSize = stamp.getSize();
435 var offset = stamp.getBox();
436 // get the columns that this stamp affects
437 var firstX = this.isOriginLeft ? offset.x : offset.right;
438 var lastX = firstX + stampSize.width;
439 var firstCol = Math.floor( firstX / this.columnWidth );
440 firstCol = Math.max( 0, firstCol );
442 var lastCol = Math.floor( lastX / this.columnWidth );
443 // lastCol should not go over if multiple of columnWidth #425
444 lastCol -= lastX % this.columnWidth ? 0 : 1;
445 lastCol = Math.min( this.cols - 1, lastCol );
447 // set colYs to bottom of the stamp
448 var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
451 for ( var i = firstCol; i <= lastCol; i++ ) {
452 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
457 _getContainerSize : function()
459 this.maxY = Math.max.apply( Math, this.colYs );
464 if ( this.isFitWidth ) {
465 size.width = this._getContainerFitWidth();
471 _getContainerFitWidth : function()
474 // count unused columns
477 if ( this.colYs[i] !== 0 ) {
482 // fit container to columns that have been used
483 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
486 needsResizeLayout : function()
488 var previousWidth = this.containerWidth;
489 this.getContainerWidth();
490 return previousWidth !== this.containerWidth;