Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / layout / Layout.js
1 /*
2
3 This file is part of Ext JS 4
4
5 Copyright (c) 2011 Sencha Inc
6
7 Contact:  http://www.sencha.com/contact
8
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file.  Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
11
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
13
14 */
15 /**
16  * Base Layout class - extended by ComponentLayout and ContainerLayout
17  */
18 Ext.define('Ext.layout.Layout', {
19
20     /* Begin Definitions */
21
22     /* End Definitions */
23
24     isLayout: true,
25     initialized: false,
26
27     statics: {
28         create: function(layout, defaultType) {
29             var type;
30             if (layout instanceof Ext.layout.Layout) {
31                 return Ext.createByAlias('layout.' + layout);
32             } else {
33                 if (!layout || typeof layout === 'string') {
34                     type = layout || defaultType;
35                     layout = {};                    
36                 }
37                 else {
38                     type = layout.type || defaultType;
39                 }
40                 return Ext.createByAlias('layout.' + type, layout || {});
41             }
42         }
43     },
44
45     constructor : function(config) {
46         this.id = Ext.id(null, this.type + '-');
47         Ext.apply(this, config);
48     },
49
50     /**
51      * @private
52      */
53     layout : function() {
54         var me = this;
55         me.layoutBusy = true;
56         me.initLayout();
57
58         if (me.beforeLayout.apply(me, arguments) !== false) {
59             me.layoutCancelled = false;
60             me.onLayout.apply(me, arguments);
61             me.childrenChanged = false;
62             me.owner.needsLayout = false;
63             me.layoutBusy = false;
64             me.afterLayout.apply(me, arguments);
65         }
66         else {
67             me.layoutCancelled = true;
68         }
69         me.layoutBusy = false;
70         me.doOwnerCtLayouts();
71     },
72
73     beforeLayout : function() {
74         this.renderChildren();
75         return true;
76     },
77
78     renderChildren: function () {
79         this.renderItems(this.getLayoutItems(), this.getRenderTarget());
80     },
81
82     /**
83      * @private
84      * Iterates over all passed items, ensuring they are rendered.  If the items are already rendered,
85      * also determines if the items are in the proper place dom.
86      */
87     renderItems : function(items, target) {
88         var me = this,
89             ln = items.length,
90             i = 0,
91             item;
92
93         for (; i < ln; i++) {
94             item = items[i];
95             if (item && !item.rendered) {
96                 me.renderItem(item, target, i);
97             } else if (!me.isValidParent(item, target, i)) {
98                 me.moveItem(item, target, i);
99             } else {
100                 // still need to configure the item, it may have moved in the container.
101                 me.configureItem(item);
102             }
103         }
104     },
105
106     // @private - Validates item is in the proper place in the dom.
107     isValidParent : function(item, target, position) {
108         var dom = item.el ? item.el.dom : Ext.getDom(item);
109         if (dom && target && target.dom) {
110             if (Ext.isNumber(position) && dom !== target.dom.childNodes[position]) {
111                 return false;
112             }
113             return (dom.parentNode == (target.dom || target));
114         }
115         return false;
116     },
117
118     /**
119      * @private
120      * Renders the given Component into the target Element.
121      * @param {Ext.Component} item The Component to render
122      * @param {Ext.Element} target The target Element
123      * @param {Number} position The position within the target to render the item to
124      */
125     renderItem : function(item, target, position) {
126         var me = this;
127         if (!item.rendered) {
128             if (me.itemCls) {
129                 item.addCls(me.itemCls);
130             }
131             if (me.owner.itemCls) {
132                 item.addCls(me.owner.itemCls);
133             }
134             item.render(target, position);
135             me.configureItem(item);
136             me.childrenChanged = true;
137         }
138     },
139
140     /**
141      * @private
142      * Moved Component to the provided target instead.
143      */
144     moveItem : function(item, target, position) {
145         // Make sure target is a dom element
146         target = target.dom || target;
147         if (typeof position == 'number') {
148             position = target.childNodes[position];
149         }
150         target.insertBefore(item.el.dom, position || null);
151         item.container = Ext.get(target);
152         this.configureItem(item);
153         this.childrenChanged = true;
154     },
155
156     /**
157      * @private
158      * Adds the layout's targetCls if necessary and sets
159      * initialized flag when complete.
160      */
161     initLayout : function() {
162         var me = this,
163             targetCls = me.targetCls;
164             
165         if (!me.initialized && !Ext.isEmpty(targetCls)) {
166             me.getTarget().addCls(targetCls);
167         }
168         me.initialized = true;
169     },
170
171     // @private Sets the layout owner
172     setOwner : function(owner) {
173         this.owner = owner;
174     },
175
176     // @private - Returns empty array
177     getLayoutItems : function() {
178         return [];
179     },
180
181     /**
182      * @private
183      * Applies itemCls
184      * Empty template method
185      */
186     configureItem: Ext.emptyFn,
187     
188     // Placeholder empty functions for subclasses to extend
189     onLayout : Ext.emptyFn,
190     afterLayout : Ext.emptyFn,
191     onRemove : Ext.emptyFn,
192     onDestroy : Ext.emptyFn,
193     doOwnerCtLayouts : Ext.emptyFn,
194
195     /**
196      * @private
197      * Removes itemCls
198      */
199     afterRemove : function(item) {
200         var el = item.el,
201             owner = this.owner,
202             itemCls = this.itemCls,
203             ownerCls = owner.itemCls;
204             
205         // Clear managed dimensions flag when removed from the layout.
206         if (item.rendered && !item.isDestroyed) {
207             if (itemCls) {
208                 el.removeCls(itemCls);
209             }
210             if (ownerCls) {
211                 el.removeCls(ownerCls);
212             }
213         }
214
215         // These flags are set at the time a child item is added to a layout.
216         // The layout must decide if it is managing the item's width, or its height, or both.
217         // See AbstractComponent for docs on these properties.
218         delete item.layoutManagedWidth;
219         delete item.layoutManagedHeight;
220     },
221
222     /**
223      * Destroys this layout. This is a template method that is empty by default, but should be implemented
224      * by subclasses that require explicit destruction to purge event handlers or remove DOM nodes.
225      * @template
226      */
227     destroy : function() {
228         var targetCls = this.targetCls,
229             target;
230         
231         if (!Ext.isEmpty(targetCls)) {
232             target = this.getTarget();
233             if (target) {
234                 target.removeCls(targetCls);
235             }
236         }
237         this.onDestroy();
238     }
239 });