Upgrade to ExtJS 3.1.0 - Released 12/16/2009
[extjs.git] / src / widgets / layout / ContainerLayout.js
1 /*!
2  * Ext JS Library 3.1.0
3  * Copyright(c) 2006-2009 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**
8  * @class Ext.layout.ContainerLayout
9  * <p>The ContainerLayout class is the default layout manager delegated by {@link Ext.Container} to
10  * render any child Components when no <tt>{@link Ext.Container#layout layout}</tt> is configured into
11  * a {@link Ext.Container Container}. ContainerLayout provides the basic foundation for all other layout
12  * classes in Ext. It simply renders all child Components into the Container, performing no sizing or
13  * positioning services. To utilize a layout that provides sizing and positioning of child Components,
14  * specify an appropriate <tt>{@link Ext.Container#layout layout}</tt>.</p>
15  * <p>This class is intended to be extended or created via the <tt><b>{@link Ext.Container#layout layout}</b></tt>
16  * configuration property.  See <tt><b>{@link Ext.Container#layout}</b></tt> for additional details.</p>
17  */
18 Ext.layout.ContainerLayout = Ext.extend(Object, {
19     /**
20      * @cfg {String} extraCls
21      * <p>An optional extra CSS class that will be added to the container. This can be useful for adding
22      * customized styles to the container or any of its children using standard CSS rules. See
23      * {@link Ext.Component}.{@link Ext.Component#ctCls ctCls} also.</p>
24      * <p><b>Note</b>: <tt>extraCls</tt> defaults to <tt>''</tt> except for the following classes
25      * which assign a value by default:
26      * <div class="mdetail-params"><ul>
27      * <li>{@link Ext.layout.AbsoluteLayout Absolute Layout} : <tt>'x-abs-layout-item'</tt></li>
28      * <li>{@link Ext.layout.Box Box Layout} : <tt>'x-box-item'</tt></li>
29      * <li>{@link Ext.layout.ColumnLayout Column Layout} : <tt>'x-column'</tt></li>
30      * </ul></div>
31      * To configure the above Classes with an extra CSS class append to the default.  For example,
32      * for ColumnLayout:<pre><code>
33      * extraCls: 'x-column custom-class'
34      * </code></pre>
35      * </p>
36      */
37     /**
38      * @cfg {Boolean} renderHidden
39      * True to hide each contained item on render (defaults to false).
40      */
41
42     /**
43      * A reference to the {@link Ext.Component} that is active.  For example, <pre><code>
44      * if(myPanel.layout.activeItem.id == 'item-1') { ... }
45      * </code></pre>
46      * <tt>activeItem</tt> only applies to layout styles that can display items one at a time
47      * (like {@link Ext.layout.AccordionLayout}, {@link Ext.layout.CardLayout}
48      * and {@link Ext.layout.FitLayout}).  Read-only.  Related to {@link Ext.Container#activeItem}.
49      * @type {Ext.Component}
50      * @property activeItem
51      */
52
53     // private
54     monitorResize:false,
55     // private
56     activeItem : null,
57
58     constructor : function(config){
59         Ext.apply(this, config);
60     },
61
62     // private
63     layout : function(){
64         var target = this.container.getLayoutTarget();
65         if(!(this.hasLayout || Ext.isEmpty(this.targetCls))){
66             target.addClass(this.targetCls)
67         }
68         this.onLayout(this.container, target);
69         this.container.fireEvent('afterlayout', this.container, this);
70         this.hasLayout = true;
71     },
72
73     // private
74     onLayout : function(ct, target){
75         this.renderAll(ct, target);
76     },
77
78     // private
79     isValidParent : function(c, target){
80         return target && c.getPositionEl().dom.parentNode == (target.dom || target);
81     },
82
83     // private
84     renderAll : function(ct, target){
85         var items = ct.items.items;
86         for(var i = 0, len = items.length; i < len; i++) {
87             var c = items[i];
88             if(c && (!c.rendered || !this.isValidParent(c, target))){
89                 this.renderItem(c, i, target);
90             }
91         }
92     },
93
94     // private
95     renderItem : function(c, position, target){
96         if(c && !c.rendered){
97             c.render(target, position);
98             this.configureItem(c, position);
99         }else if(c && !this.isValidParent(c, target)){
100             if(Ext.isNumber(position)){
101                 position = target.dom.childNodes[position];
102             }
103             target.dom.insertBefore(c.getPositionEl().dom, position || null);
104             c.container = target;
105             this.configureItem(c, position);
106         }
107     },
108
109     // private
110     configureItem: function(c, position){
111         if(this.extraCls){
112             var t = c.getPositionEl ? c.getPositionEl() : c;
113             t.addClass(this.extraCls);
114         }
115         // If we are forcing a layout, do so *before* we hide so elements have height/width
116         if(c.doLayout && this.forceLayout){
117             c.doLayout(false, true);
118         }
119         if (this.renderHidden && c != this.activeItem) {
120             c.hide();
121         }
122     },
123
124     onRemove: function(c){
125          if(this.activeItem == c){
126             delete this.activeItem;
127          }
128          if(c.rendered && this.extraCls){
129             var t = c.getPositionEl ? c.getPositionEl() : c;
130             t.removeClass(this.extraCls);
131         }
132     },
133
134     // private
135     onResize: function(){
136         var ct = this.container,
137             b = ct.bufferResize;
138
139         if (ct.collapsed){
140             return;
141         }
142
143         // Not having an ownerCt negates the buffering: floating and top level
144         // Containers (Viewport, Window, ToolTip, Menu) need to lay out ASAP.
145         if (b && ct.ownerCt) {
146             // If we do NOT already have a layout pending from an ancestor, schedule one.
147             // If there is a layout pending, we do nothing here.
148             // buffering to be deprecated soon
149             if (!ct.hasLayoutPending()){
150                 if(!this.resizeTask){
151                     this.resizeTask = new Ext.util.DelayedTask(this.runLayout, this);
152                     this.resizeBuffer = Ext.isNumber(b) ? b : 50;
153                 }
154                 ct.layoutPending = true;
155                 this.resizeTask.delay(this.resizeBuffer);
156             }
157         }else{
158             ct.doLayout(false, this.forceLayout);
159         }
160     },
161
162     // private
163     runLayout: function(){
164         var ct = this.container;
165         ct.doLayout();
166         delete ct.layoutPending;
167     },
168
169     // private
170     setContainer : function(ct){
171         // No longer use events to handle resize. Instead this will be handled through a direct function call.
172         /*
173         if(this.monitorResize && ct != this.container){
174             var old = this.container;
175             if(old){
176                 old.un(old.resizeEvent, this.onResize, this);
177             }
178             if(ct){
179                 ct.on(ct.resizeEvent, this.onResize, this);
180             }
181         }
182         */
183         this.container = ct;
184     },
185
186     // private
187     parseMargins : function(v){
188         if(Ext.isNumber(v)){
189             v = v.toString();
190         }
191         var ms = v.split(' ');
192         var len = ms.length;
193         if(len == 1){
194             ms[1] = ms[0];
195             ms[2] = ms[0];
196             ms[3] = ms[0];
197         }
198         if(len == 2){
199             ms[2] = ms[0];
200             ms[3] = ms[1];
201         }
202         if(len == 3){
203             ms[3] = ms[1];
204         }
205         return {
206             top:parseInt(ms[0], 10) || 0,
207             right:parseInt(ms[1], 10) || 0,
208             bottom:parseInt(ms[2], 10) || 0,
209             left:parseInt(ms[3], 10) || 0
210         };
211     },
212
213     /**
214      * The {@link Ext.Template Ext.Template} used by Field rendering layout classes (such as
215      * {@link Ext.layout.FormLayout}) to create the DOM structure of a fully wrapped,
216      * labeled and styled form Field. A default Template is supplied, but this may be
217      * overriden to create custom field structures. The template processes values returned from
218      * {@link Ext.layout.FormLayout#getTemplateArgs}.
219      * @property fieldTpl
220      * @type Ext.Template
221      */
222     fieldTpl: (function() {
223         var t = new Ext.Template(
224             '<div class="x-form-item {itemCls}" tabIndex="-1">',
225                 '<label for="{id}" style="{labelStyle}" class="x-form-item-label">{label}{labelSeparator}</label>',
226                 '<div class="x-form-element" id="x-form-el-{id}" style="{elementStyle}">',
227                 '</div><div class="{clearCls}"></div>',
228             '</div>'
229         );
230         t.disableFormats = true;
231         return t.compile();
232     })(),
233
234     /*
235      * Destroys this layout. This is a template method that is empty by default, but should be implemented
236      * by subclasses that require explicit destruction to purge event handlers or remove DOM nodes.
237      * @protected
238      */
239     destroy : function(){
240         if(!Ext.isEmpty(this.targetCls)){
241             var target = this.container.getLayoutTarget();
242             if(target){
243                 target.removeClass(this.targetCls);
244             }
245         }
246     }
247 });
248 Ext.Container.LAYOUTS['auto'] = Ext.layout.ContainerLayout;