commit extjs-2.2.1
[extjs.git] / source / widgets / BoxComponent.js
1 /*\r
2  * Ext JS Library 2.2.1\r
3  * Copyright(c) 2006-2009, Ext JS, LLC.\r
4  * licensing@extjs.com\r
5  * \r
6  * http://extjs.com/license\r
7  */\r
8 \r
9 /**\r
10  * @class Ext.BoxComponent\r
11  * @extends Ext.Component\r
12  * <p>Base class for any visual {@link Ext.Component} that uses a box container.  BoxComponent provides automatic box\r
13  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All\r
14  * container classes should subclass BoxComponent so that they will work consistently when nested within other Ext\r
15  * layout containers.</p>\r
16  * <p>A BoxComponent may be created as a custom Component which encapsulates any HTML element, either a pre-existing\r
17  * element, or one that is created to your specifications at render time. Usually, to participate in layouts,\r
18  * a Component will need to be a <b>Box</b>Component in order to have its width and height managed.</p>\r
19  * <p>To use a pre-existing element as a BoxComponent, configure it so that you preset the <b>el</b> property to the\r
20  * element to reference:<pre><code>\r
21 var pageHeader = new Ext.BoxComponent({\r
22     el: 'my-header-div'\r
23 });</code></pre>\r
24  * This may then be {@link Ext.Container#add added} to a {@link Ext.Container Container} as a child item.</p>\r
25  * <p>To create a BoxComponent based around a HTML element to be created at render time, use the\r
26  * {@link Ext.Component#autoEl autoEl} config option which takes the form of a\r
27  * {@link Ext.DomHelper DomHelper} specification:<pre><code>\r
28 var myImage = new Ext.BoxComponent({\r
29     autoEl: {\r
30         tag: 'img',\r
31         src: '/images/my-image.jpg'\r
32     }\r
33 });</code></pre></p>\r
34  * @constructor\r
35  * @param {Ext.Element/String/Object} config The configuration options.\r
36  */\r
37 Ext.BoxComponent = Ext.extend(Ext.Component, {\r
38     /**\r
39      * @cfg {Number} x\r
40      * The local x (left) coordinate for this component if contained within a positioning container.\r
41      */\r
42     /**\r
43      * @cfg {Number} y\r
44      * The local y (top) coordinate for this component if contained within a positioning container.\r
45      */\r
46     /**\r
47      * @cfg {Number} pageX\r
48      * The page level x coordinate for this component if contained within a positioning container.\r
49      */\r
50     /**\r
51      * @cfg {Number} pageY\r
52      * The page level y coordinate for this component if contained within a positioning container.\r
53      */\r
54     /**\r
55      * @cfg {Number} height\r
56      * The height of this component in pixels (defaults to auto).\r
57      */\r
58     /**\r
59      * @cfg {Number} width\r
60      * The width of this component in pixels (defaults to auto).\r
61      */\r
62     /**\r
63      * @cfg {Boolean} autoHeight\r
64      * True to use height:'auto', false to use fixed height (defaults to false). <b>Note</b>: Although many components \r
65      * inherit this config option, not all will function as expected with a height of 'auto'. Setting autoHeight:true \r
66      * means that the browser will manage height based on the element's contents, and that Ext will not manage it at all.\r
67      */\r
68     /**\r
69      * @cfg {Boolean} autoWidth\r
70      * True to use width:'auto', false to use fixed width (defaults to false). <b>Note</b>: Although many components \r
71      * inherit this config option, not all will function as expected with a width of 'auto'. Setting autoWidth:true \r
72      * means that the browser will manage width based on the element's contents, and that Ext will not manage it at all.\r
73      */\r
74 \r
75     /* // private internal config\r
76      * {Boolean} deferHeight\r
77      * True to defer height calculations to an external component, false to allow this component to set its own\r
78      * height (defaults to false).\r
79      */\r
80 \r
81         // private\r
82     initComponent : function(){\r
83         Ext.BoxComponent.superclass.initComponent.call(this);\r
84         this.addEvents(\r
85             /**\r
86              * @event resize\r
87              * Fires after the component is resized.\r
88              * @param {Ext.Component} this\r
89              * @param {Number} adjWidth The box-adjusted width that was set\r
90              * @param {Number} adjHeight The box-adjusted height that was set\r
91              * @param {Number} rawWidth The width that was originally specified\r
92              * @param {Number} rawHeight The height that was originally specified\r
93              */\r
94             'resize',\r
95             /**\r
96              * @event move\r
97              * Fires after the component is moved.\r
98              * @param {Ext.Component} this\r
99              * @param {Number} x The new x position\r
100              * @param {Number} y The new y position\r
101              */\r
102             'move'\r
103         );\r
104     },\r
105 \r
106     // private, set in afterRender to signify that the component has been rendered\r
107     boxReady : false,\r
108     // private, used to defer height settings to subclasses\r
109     deferHeight: false,\r
110 \r
111     /**\r
112      * Sets the width and height of the component.  This method fires the {@link #resize} event.  This method can accept\r
113      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.\r
114      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}\r
115      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)\r
116      * @return {Ext.BoxComponent} this\r
117      */\r
118     setSize : function(w, h){\r
119         // support for standard size objects\r
120         if(typeof w == 'object'){\r
121             h = w.height;\r
122             w = w.width;\r
123         }\r
124         // not rendered\r
125         if(!this.boxReady){\r
126             this.width = w;\r
127             this.height = h;\r
128             return this;\r
129         }\r
130 \r
131         // prevent recalcs when not needed\r
132         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){\r
133             return this;\r
134         }\r
135         this.lastSize = {width: w, height: h};\r
136         var adj = this.adjustSize(w, h);\r
137         var aw = adj.width, ah = adj.height;\r
138         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters\r
139             var rz = this.getResizeEl();\r
140             if(!this.deferHeight && aw !== undefined && ah !== undefined){\r
141                 rz.setSize(aw, ah);\r
142             }else if(!this.deferHeight && ah !== undefined){\r
143                 rz.setHeight(ah);\r
144             }else if(aw !== undefined){\r
145                 rz.setWidth(aw);\r
146             }\r
147             this.onResize(aw, ah, w, h);\r
148             this.fireEvent('resize', this, aw, ah, w, h);\r
149         }\r
150         return this;\r
151     },\r
152 \r
153     /**\r
154      * Sets the width of the component.  This method fires the {@link #resize} event.\r
155      * @param {Number} width The new width to set\r
156      * @return {Ext.BoxComponent} this\r
157      */\r
158     setWidth : function(width){\r
159         return this.setSize(width);\r
160     },\r
161 \r
162     /**\r
163      * Sets the height of the component.  This method fires the {@link #resize} event.\r
164      * @param {Number} height The new height to set\r
165      * @return {Ext.BoxComponent} this\r
166      */\r
167     setHeight : function(height){\r
168         return this.setSize(undefined, height);\r
169     },\r
170 \r
171     /**\r
172      * Gets the current size of the component's underlying element.\r
173      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}\r
174      */\r
175     getSize : function(){\r
176         return this.el.getSize();\r
177     },\r
178 \r
179     /**\r
180      * Gets the current XY position of the component's underlying element.\r
181      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)\r
182      * @return {Array} The XY position of the element (e.g., [100, 200])\r
183      */\r
184     getPosition : function(local){\r
185         if(local === true){\r
186             return [this.el.getLeft(true), this.el.getTop(true)];\r
187         }\r
188         return this.xy || this.el.getXY();\r
189     },\r
190 \r
191     /**\r
192      * Gets the current box measurements of the component's underlying element.\r
193      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)\r
194      * @return {Object} box An object in the format {x, y, width, height}\r
195      */\r
196     getBox : function(local){\r
197         var s = this.el.getSize();\r
198         if(local === true){\r
199             s.x = this.el.getLeft(true);\r
200             s.y = this.el.getTop(true);\r
201         }else{\r
202             var xy = this.xy || this.el.getXY();\r
203             s.x = xy[0];\r
204             s.y = xy[1];\r
205         }\r
206         return s;\r
207     },\r
208 \r
209     /**\r
210      * Sets the current box measurements of the component's underlying element.\r
211      * @param {Object} box An object in the format {x, y, width, height}\r
212      * @return {Ext.BoxComponent} this\r
213      */\r
214     updateBox : function(box){\r
215         this.setSize(box.width, box.height);\r
216         this.setPagePosition(box.x, box.y);\r
217         return this;\r
218     },\r
219 \r
220     // protected\r
221     getResizeEl : function(){\r
222         return this.resizeEl || this.el;\r
223     },\r
224 \r
225     // protected\r
226     getPositionEl : function(){\r
227         return this.positionEl || this.el;\r
228     },\r
229 \r
230     /**\r
231      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.\r
232      * This method fires the {@link #move} event.\r
233      * @param {Number} left The new left\r
234      * @param {Number} top The new top\r
235      * @return {Ext.BoxComponent} this\r
236      */\r
237     setPosition : function(x, y){\r
238         if(x && typeof x[1] == 'number'){\r
239             y = x[1];\r
240             x = x[0];\r
241         }\r
242         this.x = x;\r
243         this.y = y;\r
244         if(!this.boxReady){\r
245             return this;\r
246         }\r
247         var adj = this.adjustPosition(x, y);\r
248         var ax = adj.x, ay = adj.y;\r
249 \r
250         var el = this.getPositionEl();\r
251         if(ax !== undefined || ay !== undefined){\r
252             if(ax !== undefined && ay !== undefined){\r
253                 el.setLeftTop(ax, ay);\r
254             }else if(ax !== undefined){\r
255                 el.setLeft(ax);\r
256             }else if(ay !== undefined){\r
257                 el.setTop(ay);\r
258             }\r
259             this.onPosition(ax, ay);\r
260             this.fireEvent('move', this, ax, ay);\r
261         }\r
262         return this;\r
263     },\r
264 \r
265     /**\r
266      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.\r
267      * This method fires the {@link #move} event.\r
268      * @param {Number} x The new x position\r
269      * @param {Number} y The new y position\r
270      * @return {Ext.BoxComponent} this\r
271      */\r
272     setPagePosition : function(x, y){\r
273         if(x && typeof x[1] == 'number'){\r
274             y = x[1];\r
275             x = x[0];\r
276         }\r
277         this.pageX = x;\r
278         this.pageY = y;\r
279         if(!this.boxReady){\r
280             return;\r
281         }\r
282         if(x === undefined || y === undefined){ // cannot translate undefined points\r
283             return;\r
284         }\r
285         var p = this.el.translatePoints(x, y);\r
286         this.setPosition(p.left, p.top);\r
287         return this;\r
288     },\r
289 \r
290     // private\r
291     onRender : function(ct, position){\r
292         Ext.BoxComponent.superclass.onRender.call(this, ct, position);\r
293         if(this.resizeEl){\r
294             this.resizeEl = Ext.get(this.resizeEl);\r
295         }\r
296         if(this.positionEl){\r
297             this.positionEl = Ext.get(this.positionEl);\r
298         }\r
299     },\r
300 \r
301     // private\r
302     afterRender : function(){\r
303         Ext.BoxComponent.superclass.afterRender.call(this);\r
304         this.boxReady = true;\r
305         this.setSize(this.width, this.height);\r
306         if(this.x || this.y){\r
307             this.setPosition(this.x, this.y);\r
308         }else if(this.pageX || this.pageY){\r
309             this.setPagePosition(this.pageX, this.pageY);\r
310         }\r
311     },\r
312 \r
313     /**\r
314      * Force the component's size to recalculate based on the underlying element's current height and width.\r
315      * @return {Ext.BoxComponent} this\r
316      */\r
317     syncSize : function(){\r
318         delete this.lastSize;\r
319         this.setSize(this.autoWidth ? undefined : this.el.getWidth(), this.autoHeight ? undefined : this.el.getHeight());\r
320         return this;\r
321     },\r
322 \r
323     /* // protected\r
324      * Called after the component is resized, this method is empty by default but can be implemented by any\r
325      * subclass that needs to perform custom logic after a resize occurs.\r
326      * @param {Number} adjWidth The box-adjusted width that was set\r
327      * @param {Number} adjHeight The box-adjusted height that was set\r
328      * @param {Number} rawWidth The width that was originally specified\r
329      * @param {Number} rawHeight The height that was originally specified\r
330      */\r
331     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){\r
332 \r
333     },\r
334 \r
335     /* // protected\r
336      * Called after the component is moved, this method is empty by default but can be implemented by any\r
337      * subclass that needs to perform custom logic after a move occurs.\r
338      * @param {Number} x The new x position\r
339      * @param {Number} y The new y position\r
340      */\r
341     onPosition : function(x, y){\r
342 \r
343     },\r
344 \r
345     // private\r
346     adjustSize : function(w, h){\r
347         if(this.autoWidth){\r
348             w = 'auto';\r
349         }\r
350         if(this.autoHeight){\r
351             h = 'auto';\r
352         }\r
353         return {width : w, height: h};\r
354     },\r
355 \r
356     // private\r
357     adjustPosition : function(x, y){\r
358         return {x : x, y: y};\r
359     }\r
360 });\r
361 Ext.reg('box', Ext.BoxComponent);