allow string based values for comboboxarray
[roojs1] / Roo / bootstrap / LayoutMasonryAuto.js
1 /**
2  *
3  * This is based on 
4  * http://masonry.desandro.com
5  *
6  * The idea is to render all the bricks based on vertical width...
7  *
8  * The original code extends 'outlayer' - we might need to use that....
9  * 
10  */
11
12
13 /**
14  * @class Roo.bootstrap.LayoutMasonryAuto
15  * @extends Roo.bootstrap.Component
16  * Bootstrap Layout Masonry class
17  * 
18  * @constructor
19  * Create a new Element
20  * @param {Object} config The config object
21  */
22
23 Roo.bootstrap.LayoutMasonryAuto = function(config){
24     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
25 };
26
27 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
28     
29       /**
30      * @cfg {Boolean} isFitWidth  - resize the width..
31      */   
32     isFitWidth : false,  // options..
33     /**
34      * @cfg {Boolean} isOriginLeft = left align?
35      */   
36     isOriginLeft : true,
37     /**
38      * @cfg {Boolean} isOriginTop = top align?
39      */   
40     isOriginTop : false,
41     /**
42      * @cfg {Boolean} isLayoutInstant = no animation?
43      */   
44     isLayoutInstant : false, // needed?
45     /**
46      * @cfg {Boolean} isResizingContainer = not sure if this is used..
47      */   
48     isResizingContainer : true,
49     /**
50      * @cfg {Number} columnWidth  width of the columns 
51      */   
52     
53     columnWidth : 0,
54     
55     /**
56      * @cfg {Number} maxCols maximum number of columns
57      */   
58     
59     maxCols: 0,
60     /**
61      * @cfg {Number} padHeight padding below box..
62      */   
63     
64     padHeight : 10, 
65     
66     /**
67      * @cfg {Boolean} isAutoInitial defalut true
68      */   
69     
70     isAutoInitial : true, 
71     
72     // private?
73     gutter : 0,
74     
75     containerWidth: 0,
76     initialColumnWidth : 0,
77     currentSize : null,
78     
79     colYs : null, // array.
80     maxY : 0,
81     padWidth: 10,
82     
83     
84     tag: 'div',
85     cls: '',
86     bricks: null, //CompositeElement
87     cols : 0, // array?
88     // element : null, // wrapped now this.el
89     _isLayoutInited : null, 
90     
91     
92     getAutoCreate : function(){
93         
94         var cfg = {
95             tag: this.tag,
96             cls: 'blog-masonary-wrapper ' + this.cls,
97             cn : {
98                 cls : 'mas-boxes masonary'
99             }
100         };
101         
102         return cfg;
103     },
104     
105     getChildContainer: function( )
106     {
107         if (this.boxesEl) {
108             return this.boxesEl;
109         }
110         
111         this.boxesEl = this.el.select('.mas-boxes').first();
112         
113         return this.boxesEl;
114     },
115     
116     
117     initEvents : function()
118     {
119         var _this = this;
120         
121         if(this.isAutoInitial){
122             Roo.log('hook children rendered');
123             this.on('childrenrendered', function() {
124                 Roo.log('children rendered');
125                 _this.initial();
126             } ,this);
127         }
128         
129     },
130     
131     initial : function()
132     {
133         this.reloadItems();
134
135         this.currentSize = this.el.getBox(true);
136
137         /// was window resize... - let's see if this works..
138         Roo.EventManager.onWindowResize(this.resize, this); 
139
140         if(!this.isAutoInitial){
141             this.layout();
142             return;
143         }
144         
145         this.layout.defer(500,this);
146     },
147     
148     reloadItems: function()
149     {
150         this.bricks = this.el.select('.masonry-brick', true);
151         
152         this.bricks.each(function(b) {
153             //Roo.log(b.getSize());
154             if (!b.attr('originalwidth')) {
155                 b.attr('originalwidth',  b.getSize().width);
156             }
157             
158         });
159         
160         Roo.log(this.bricks.elements.length);
161     },
162     
163     resize : function()
164     {
165         Roo.log('resize');
166         var cs = this.el.getBox(true);
167         
168         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
169             Roo.log("no change in with or X");
170             return;
171         }
172         this.currentSize = cs;
173         this.layout();
174     },
175     
176     layout : function()
177     {
178          Roo.log('layout');
179         this._resetLayout();
180         //this._manageStamps();
181       
182         // don't animate first layout
183         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
184         this.layoutItems( isInstant );
185       
186         // flag for initalized
187         this._isLayoutInited = true;
188     },
189     
190     layoutItems : function( isInstant )
191     {
192         //var items = this._getItemsForLayout( this.items );
193         // original code supports filtering layout items.. we just ignore it..
194         
195         this._layoutItems( this.bricks , isInstant );
196       
197         this._postLayout();
198     },
199     _layoutItems : function ( items , isInstant)
200     {
201        //this.fireEvent( 'layout', this, items );
202     
203
204         if ( !items || !items.elements.length ) {
205           // no items, emit event with empty array
206             return;
207         }
208
209         var queue = [];
210         items.each(function(item) {
211             Roo.log("layout item");
212             Roo.log(item);
213             // get x/y object from method
214             var position = this._getItemLayoutPosition( item );
215             // enqueue
216             position.item = item;
217             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
218             queue.push( position );
219         }, this);
220       
221         this._processLayoutQueue( queue );
222     },
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
228     */
229     _processLayoutQueue : function( queue )
230     {
231         for ( var i=0, len = queue.length; i < len; i++ ) {
232             var obj = queue[i];
233             obj.item.position('absolute');
234             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
235         }
236     },
237       
238     
239     /**
240     * Any logic you want to do after each layout,
241     * i.e. size the container
242     */
243     _postLayout : function()
244     {
245         this.resizeContainer();
246     },
247     
248     resizeContainer : function()
249     {
250         if ( !this.isResizingContainer ) {
251             return;
252         }
253         var size = this._getContainerSize();
254         if ( size ) {
255             this.el.setSize(size.width,size.height);
256             this.boxesEl.setSize(size.width,size.height);
257         }
258     },
259     
260     
261     
262     _resetLayout : function()
263     {
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(); 
267         
268         this.measureColumns();
269
270         // reset column Y
271         var i = this.cols;
272         this.colYs = [];
273         while (i--) {
274             this.colYs.push( 0 );
275         }
276     
277         this.maxY = 0;
278     },
279
280     measureColumns : function()
281     {
282         this.getContainerWidth();
283       // if columnWidth is 0, default to outerWidth of first item
284         if ( !this.columnWidth ) {
285             var firstItem = this.bricks.first();
286             Roo.log(firstItem);
287             this.columnWidth  = this.containerWidth;
288             if (firstItem && firstItem.attr('originalwidth') ) {
289                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
290             }
291             // columnWidth fall back to item of first element
292             Roo.log("set column width?");
293                         this.initialColumnWidth = this.columnWidth  ;
294
295             // if first elem has no width, default to size of container
296             
297         }
298         
299         
300         if (this.initialColumnWidth) {
301             this.columnWidth = this.initialColumnWidth;
302         }
303         
304         
305             
306         // column width is fixed at the top - however if container width get's smaller we should
307         // reduce it...
308         
309         // this bit calcs how man columns..
310             
311         var columnWidth = this.columnWidth += this.gutter;
312       
313         // calculate columns
314         var containerWidth = this.containerWidth + this.gutter;
315         
316         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
317         // fix rounding errors, typically with gutters
318         var excess = columnWidth - containerWidth % columnWidth;
319         
320         
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;
326         
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'
331         
332         var padNeeded = (1+this.cols) * this.padWidth;
333         
334         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
335         
336         this.columnWidth += padExtra
337         //this.padWidth = Math.floor(padavail /  ( this.cols));
338         
339         // adjust colum width so that padding is fixed??
340         
341         // we have 3 columns ... total = width * 3
342         // we have X left over... that should be used by 
343         
344         //if (this.expandC) {
345             
346         //}
347         
348         
349         
350     },
351     
352     getContainerWidth : function()
353     {
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
358         
359         var size = getSize( container );  //FIXME
360         this.containerWidth = size && size.innerWidth; //FIXME
361         */
362          
363         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
364         
365     },
366     
367     _getItemLayoutPosition : function( item )  // what is item?
368     {
369         // we resize the item to our columnWidth..
370       
371         item.setWidth(this.columnWidth);
372         item.autoBoxAdjust  = false;
373         
374         var sz = item.getSize();
375  
376         // how many columns does this brick span
377         var remainder = this.containerWidth % this.columnWidth;
378         
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 );
383         
384         // normally this should be '1' as we dont' currently allow multi width columns..
385         
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 ]);
390         
391         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
392          
393         // position the brick
394         var position = {
395             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
396             y: this.currentSize.y + minimumY + this.padHeight
397         };
398         
399         Roo.log(position);
400         // apply setHeight to necessary columns
401         var setHeight = minimumY + sz.height + this.padHeight;
402         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
403         
404         var setSpan = this.cols + 1 - colGroup.length;
405         for ( var i = 0; i < setSpan; i++ ) {
406           this.colYs[ shortColIndex + i ] = setHeight ;
407         }
408       
409         return position;
410     },
411     
412     /**
413      * @param {Number} colSpan - number of columns the element spans
414      * @returns {Array} colGroup
415      */
416     _getColGroup : function( colSpan )
417     {
418         if ( colSpan < 2 ) {
419           // if brick spans only one column, use all the column Ys
420           return this.colYs;
421         }
422       
423         var colGroup = [];
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 );
432         }
433         return colGroup;
434     },
435     /*
436     _manageStamp : function( stamp )
437     {
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 );
445         
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 );
450         
451         // set colYs to bottom of the stamp
452         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
453             stampSize.height;
454             
455         for ( var i = firstCol; i <= lastCol; i++ ) {
456           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
457         }
458     },
459     */
460     
461     _getContainerSize : function()
462     {
463         this.maxY = Math.max.apply( Math, this.colYs );
464         var size = {
465             height: this.maxY
466         };
467       
468         if ( this.isFitWidth ) {
469             size.width = this._getContainerFitWidth();
470         }
471       
472         return size;
473     },
474     
475     _getContainerFitWidth : function()
476     {
477         var unusedCols = 0;
478         // count unused columns
479         var i = this.cols;
480         while ( --i ) {
481           if ( this.colYs[i] !== 0 ) {
482             break;
483           }
484           unusedCols++;
485         }
486         // fit container to columns that have been used
487         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
488     },
489     
490     needsResizeLayout : function()
491     {
492         var previousWidth = this.containerWidth;
493         this.getContainerWidth();
494         return previousWidth !== this.containerWidth;
495     }
496  
497 });
498
499  
500
501