Upgrade to ExtJS 4.0.7 - Released 10/19/2011
[extjs.git] / src / Component.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 class for all Ext components. All subclasses of Component may participate in the automated Ext component
17  * lifecycle of creation, rendering and destruction which is provided by the {@link Ext.container.Container Container}
18  * class. Components may be added to a Container through the {@link Ext.container.Container#items items} config option
19  * at the time the Container is created, or they may be added dynamically via the
20  * {@link Ext.container.Container#add add} method.
21  *
22  * The Component base class has built-in support for basic hide/show and enable/disable and size control behavior.
23  *
24  * All Components are registered with the {@link Ext.ComponentManager} on construction so that they can be referenced at
25  * any time via {@link Ext#getCmp Ext.getCmp}, passing the {@link #id}.
26  *
27  * All user-developed visual widgets that are required to participate in automated lifecycle and size management should
28  * subclass Component.
29  *
30  * See the [Creating new UI controls][1] tutorial for details on how and to either extend or augment ExtJs base classes
31  * to create custom Components.
32  *
33  * Every component has a specific xtype, which is its Ext-specific type name, along with methods for checking the xtype
34  * like {@link #getXType} and {@link #isXType}. See the [Component Guide][2] for more information on xtypes and the
35  * Component hierarchy.
36  *
37  * This is the list of all valid xtypes:
38  *
39  *     xtype            Class
40  *     -------------    ------------------
41  *     button           {@link Ext.button.Button}
42  *     buttongroup      {@link Ext.container.ButtonGroup}
43  *     colorpalette     {@link Ext.picker.Color}
44  *     component        {@link Ext.Component}
45  *     container        {@link Ext.container.Container}
46  *     cycle            {@link Ext.button.Cycle}
47  *     dataview         {@link Ext.view.View}
48  *     datepicker       {@link Ext.picker.Date}
49  *     editor           {@link Ext.Editor}
50  *     editorgrid       {@link Ext.grid.plugin.Editing}
51  *     grid             {@link Ext.grid.Panel}
52  *     multislider      {@link Ext.slider.Multi}
53  *     panel            {@link Ext.panel.Panel}
54  *     progressbar      {@link Ext.ProgressBar}
55  *     slider           {@link Ext.slider.Single}
56  *     splitbutton      {@link Ext.button.Split}
57  *     tabpanel         {@link Ext.tab.Panel}
58  *     treepanel        {@link Ext.tree.Panel}
59  *     viewport         {@link Ext.container.Viewport}
60  *     window           {@link Ext.window.Window}
61  *
62  *     Toolbar components
63  *     ---------------------------------------
64  *     pagingtoolbar    {@link Ext.toolbar.Paging}
65  *     toolbar          {@link Ext.toolbar.Toolbar}
66  *     tbfill           {@link Ext.toolbar.Fill}
67  *     tbitem           {@link Ext.toolbar.Item}
68  *     tbseparator      {@link Ext.toolbar.Separator}
69  *     tbspacer         {@link Ext.toolbar.Spacer}
70  *     tbtext           {@link Ext.toolbar.TextItem}
71  *
72  *     Menu components
73  *     ---------------------------------------
74  *     menu             {@link Ext.menu.Menu}
75  *     menucheckitem    {@link Ext.menu.CheckItem}
76  *     menuitem         {@link Ext.menu.Item}
77  *     menuseparator    {@link Ext.menu.Separator}
78  *     menutextitem     {@link Ext.menu.Item}
79  *
80  *     Form components
81  *     ---------------------------------------
82  *     form             {@link Ext.form.Panel}
83  *     checkbox         {@link Ext.form.field.Checkbox}
84  *     combo            {@link Ext.form.field.ComboBox}
85  *     datefield        {@link Ext.form.field.Date}
86  *     displayfield     {@link Ext.form.field.Display}
87  *     field            {@link Ext.form.field.Base}
88  *     fieldset         {@link Ext.form.FieldSet}
89  *     hidden           {@link Ext.form.field.Hidden}
90  *     htmleditor       {@link Ext.form.field.HtmlEditor}
91  *     label            {@link Ext.form.Label}
92  *     numberfield      {@link Ext.form.field.Number}
93  *     radio            {@link Ext.form.field.Radio}
94  *     radiogroup       {@link Ext.form.RadioGroup}
95  *     textarea         {@link Ext.form.field.TextArea}
96  *     textfield        {@link Ext.form.field.Text}
97  *     timefield        {@link Ext.form.field.Time}
98  *     trigger          {@link Ext.form.field.Trigger}
99  *
100  *     Chart components
101  *     ---------------------------------------
102  *     chart            {@link Ext.chart.Chart}
103  *     barchart         {@link Ext.chart.series.Bar}
104  *     columnchart      {@link Ext.chart.series.Column}
105  *     linechart        {@link Ext.chart.series.Line}
106  *     piechart         {@link Ext.chart.series.Pie}
107  *
108  * It should not usually be necessary to instantiate a Component because there are provided subclasses which implement
109  * specialized Component use cases which cover most application needs. However it is possible to instantiate a base
110  * Component, and it will be renderable, or will particpate in layouts as the child item of a Container:
111  *
112  *     @example
113  *     Ext.create('Ext.Component', {
114  *         html: 'Hello world!',
115  *         width: 300,
116  *         height: 200,
117  *         padding: 20,
118  *         style: {
119  *             color: '#FFFFFF',
120  *             backgroundColor:'#000000'
121  *         },
122  *         renderTo: Ext.getBody()
123  *     });
124  *
125  * The Component above creates its encapsulating `div` upon render, and use the configured HTML as content. More complex
126  * internal structure may be created using the {@link #renderTpl} configuration, although to display database-derived
127  * mass data, it is recommended that an ExtJS data-backed Component such as a {@link Ext.view.View View}, or {@link
128  * Ext.grid.Panel GridPanel}, or {@link Ext.tree.Panel TreePanel} be used.
129  *
130  * [1]: http://sencha.com/learn/Tutorial:Creating_new_UI_controls
131  */
132 Ext.define('Ext.Component', {
133
134     /* Begin Definitions */
135
136     alias: ['widget.component', 'widget.box'],
137
138     extend: 'Ext.AbstractComponent',
139
140     requires: [
141         'Ext.util.DelayedTask'
142     ],
143
144     uses: [
145         'Ext.Layer',
146         'Ext.resizer.Resizer',
147         'Ext.util.ComponentDragger'
148     ],
149
150     mixins: {
151         floating: 'Ext.util.Floating'
152     },
153
154     statics: {
155         // Collapse/expand directions
156         DIRECTION_TOP: 'top',
157         DIRECTION_RIGHT: 'right',
158         DIRECTION_BOTTOM: 'bottom',
159         DIRECTION_LEFT: 'left',
160
161         VERTICAL_DIRECTION_Re: /^(?:top|bottom)$/,
162
163         // RegExp whih specifies characters in an xtype which must be translated to '-' when generating auto IDs.
164         // This includes dot, comma and whitespace
165         INVALID_ID_CHARS_Re: /[\.,\s]/g
166     },
167
168     /* End Definitions */
169
170     /**
171      * @cfg {Boolean/Object} resizable
172      * Specify as `true` to apply a {@link Ext.resizer.Resizer Resizer} to this Component after rendering.
173      *
174      * May also be specified as a config object to be passed to the constructor of {@link Ext.resizer.Resizer Resizer}
175      * to override any defaults. By default the Component passes its minimum and maximum size, and uses
176      * `{@link Ext.resizer.Resizer#dynamic}: false`
177      */
178
179     /**
180      * @cfg {String} resizeHandles
181      * A valid {@link Ext.resizer.Resizer} handles config string. Only applies when resizable = true.
182      */
183     resizeHandles: 'all',
184
185     /**
186      * @cfg {Boolean} [autoScroll=false]
187      * `true` to use overflow:'auto' on the components layout element and show scroll bars automatically when necessary,
188      * `false` to clip any overflowing content.
189      */
190
191     /**
192      * @cfg {Boolean} floating
193      * Specify as true to float the Component outside of the document flow using CSS absolute positioning.
194      *
195      * Components such as {@link Ext.window.Window Window}s and {@link Ext.menu.Menu Menu}s are floating by default.
196      *
197      * Floating Components that are programatically {@link Ext.Component#render rendered} will register themselves with
198      * the global {@link Ext.WindowManager ZIndexManager}
199      *
200      * ### Floating Components as child items of a Container
201      *
202      * A floating Component may be used as a child item of a Container. This just allows the floating Component to seek
203      * a ZIndexManager by examining the ownerCt chain.
204      *
205      * When configured as floating, Components acquire, at render time, a {@link Ext.ZIndexManager ZIndexManager} which
206      * manages a stack of related floating Components. The ZIndexManager brings a single floating Component to the top
207      * of its stack when the Component's {@link #toFront} method is called.
208      *
209      * The ZIndexManager is found by traversing up the {@link #ownerCt} chain to find an ancestor which itself is
210      * floating. This is so that descendant floating Components of floating _Containers_ (Such as a ComboBox dropdown
211      * within a Window) can have its zIndex managed relative to any siblings, but always **above** that floating
212      * ancestor Container.
213      *
214      * If no floating ancestor is found, a floating Component registers itself with the default {@link Ext.WindowManager
215      * ZIndexManager}.
216      *
217      * Floating components _do not participate in the Container's layout_. Because of this, they are not rendered until
218      * you explicitly {@link #show} them.
219      *
220      * After rendering, the ownerCt reference is deleted, and the {@link #floatParent} property is set to the found
221      * floating ancestor Container. If no floating ancestor Container was found the {@link #floatParent} property will
222      * not be set.
223      */
224     floating: false,
225
226     /**
227      * @cfg {Boolean} toFrontOnShow
228      * True to automatically call {@link #toFront} when the {@link #show} method is called on an already visible,
229      * floating component.
230      */
231     toFrontOnShow: true,
232
233     /**
234      * @property {Ext.ZIndexManager} zIndexManager
235      * Only present for {@link #floating} Components after they have been rendered.
236      *
237      * A reference to the ZIndexManager which is managing this Component's z-index.
238      *
239      * The {@link Ext.ZIndexManager ZIndexManager} maintains a stack of floating Component z-indices, and also provides
240      * a single modal mask which is insert just beneath the topmost visible modal floating Component.
241      *
242      * Floating Components may be {@link #toFront brought to the front} or {@link #toBack sent to the back} of the
243      * z-index stack.
244      *
245      * This defaults to the global {@link Ext.WindowManager ZIndexManager} for floating Components that are
246      * programatically {@link Ext.Component#render rendered}.
247      *
248      * For {@link #floating} Components which are added to a Container, the ZIndexManager is acquired from the first
249      * ancestor Container found which is floating, or if not found the global {@link Ext.WindowManager ZIndexManager} is
250      * used.
251      *
252      * See {@link #floating} and {@link #floatParent}
253      */
254
255     /**
256      * @property {Ext.Container} floatParent
257      * Only present for {@link #floating} Components which were inserted as descendant items of floating Containers.
258      *
259      * Floating Components that are programatically {@link Ext.Component#render rendered} will not have a `floatParent`
260      * property.
261      *
262      * For {@link #floating} Components which are child items of a Container, the floatParent will be the floating
263      * ancestor Container which is responsible for the base z-index value of all its floating descendants. It provides
264      * a {@link Ext.ZIndexManager ZIndexManager} which provides z-indexing services for all its descendant floating
265      * Components.
266      *
267      * For example, the dropdown {@link Ext.view.BoundList BoundList} of a ComboBox which is in a Window will have the
268      * Window as its `floatParent`
269      *
270      * See {@link #floating} and {@link #zIndexManager}
271      */
272
273     /**
274      * @cfg {Boolean/Object} [draggable=false]
275      * Specify as true to make a {@link #floating} Component draggable using the Component's encapsulating element as
276      * the drag handle.
277      *
278      * This may also be specified as a config object for the {@link Ext.util.ComponentDragger ComponentDragger} which is
279      * instantiated to perform dragging.
280      *
281      * For example to create a Component which may only be dragged around using a certain internal element as the drag
282      * handle, use the delegate option:
283      *
284      *     new Ext.Component({
285      *         constrain: true,
286      *         floating: true,
287      *         style: {
288      *             backgroundColor: '#fff',
289      *             border: '1px solid black'
290      *         },
291      *         html: '<h1 style="cursor:move">The title</h1><p>The content</p>',
292      *         draggable: {
293      *             delegate: 'h1'
294      *         }
295      *     }).show();
296      */
297
298     /**
299      * @cfg {Boolean} [maintainFlex=false]
300      * **Only valid when a sibling element of a {@link Ext.resizer.Splitter Splitter} within a
301      * {@link Ext.layout.container.VBox VBox} or {@link Ext.layout.container.HBox HBox} layout.**
302      *
303      * Specifies that if an immediate sibling Splitter is moved, the Component on the *other* side is resized, and this
304      * Component maintains its configured {@link Ext.layout.container.Box#flex flex} value.
305      */
306
307     hideMode: 'display',
308     // Deprecate 5.0
309     hideParent: false,
310
311     ariaRole: 'presentation',
312
313     bubbleEvents: [],
314
315     actionMode: 'el',
316     monPropRe: /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/,
317
318     //renderTpl: new Ext.XTemplate(
319     //    '<div id="{id}" class="{baseCls} {cls} {cmpCls}<tpl if="typeof ui !== \'undefined\'"> {uiBase}-{ui}</tpl>"<tpl if="typeof style !== \'undefined\'"> style="{style}"</tpl>></div>', {
320     //        compiled: true,
321     //        disableFormats: true
322     //    }
323     //),
324
325     /**
326      * Creates new Component.
327      * @param {Ext.Element/String/Object} config The configuration options may be specified as either:
328      *
329      * - **an element** : it is set as the internal element and its id used as the component id
330      * - **a string** : it is assumed to be the id of an existing element and is used as the component id
331      * - **anything else** : it is assumed to be a standard config object and is applied to the component
332      */
333     constructor: function(config) {
334         var me = this;
335
336         config = config || {};
337         if (config.initialConfig) {
338
339             // Being initialized from an Ext.Action instance...
340             if (config.isAction) {
341                 me.baseAction = config;
342             }
343             config = config.initialConfig;
344             // component cloning / action set up
345         }
346         else if (config.tagName || config.dom || Ext.isString(config)) {
347             // element object
348             config = {
349                 applyTo: config,
350                 id: config.id || config
351             };
352         }
353
354         me.callParent([config]);
355
356         // If we were configured from an instance of Ext.Action, (or configured with a baseAction option),
357         // register this Component as one of its items
358         if (me.baseAction){
359             me.baseAction.addComponent(me);
360         }
361     },
362
363     /**
364      * The initComponent template method is an important initialization step for a Component. It is intended to be
365      * implemented by each subclass of Ext.Component to provide any needed constructor logic. The
366      * initComponent method of the class being created is called first, with each initComponent method
367      * up the hierarchy to Ext.Component being called thereafter. This makes it easy to implement and,
368      * if needed, override the constructor logic of the Component at any step in the hierarchy.
369      *
370      * The initComponent method **must** contain a call to {@link Ext.Base#callParent callParent} in order
371      * to ensure that the parent class' initComponent method is also called.
372      *
373      * The following example demonstrates using a dynamic string for the text of a button at the time of
374      * instantiation of the class.
375      *
376      *     Ext.define('DynamicButtonText', {
377      *         extend: 'Ext.button.Button',
378      *
379      *         initComponent: function() {
380      *             this.text = new Date();
381      *             this.renderTo = Ext.getBody();
382      *             this.callParent();
383      *         }
384      *     });
385      *
386      *     Ext.onReady(function() {
387      *         Ext.create('DynamicButtonText');
388      *     });
389      *
390      * @template
391      */
392     initComponent: function() {
393         var me = this;
394
395         me.callParent();
396
397         if (me.listeners) {
398             me.on(me.listeners);
399             delete me.listeners;
400         }
401         me.enableBubble(me.bubbleEvents);
402         me.mons = [];
403     },
404
405     // private
406     afterRender: function() {
407         var me = this,
408             resizable = me.resizable;
409
410         if (me.floating) {
411             me.makeFloating(me.floating);
412         } else {
413             me.el.setVisibilityMode(Ext.Element[me.hideMode.toUpperCase()]);
414         }
415
416         if (Ext.isDefined(me.autoScroll)) {
417             me.setAutoScroll(me.autoScroll);
418         }
419         me.callParent();
420
421         if (!(me.x && me.y) && (me.pageX || me.pageY)) {
422             me.setPagePosition(me.pageX, me.pageY);
423         }
424
425         if (resizable) {
426             me.initResizable(resizable);
427         }
428
429         if (me.draggable) {
430             me.initDraggable();
431         }
432
433         me.initAria();
434     },
435
436     initAria: function() {
437         var actionEl = this.getActionEl(),
438             role = this.ariaRole;
439         if (role) {
440             actionEl.dom.setAttribute('role', role);
441         }
442     },
443
444     /**
445      * Sets the overflow on the content element of the component.
446      * @param {Boolean} scroll True to allow the Component to auto scroll.
447      * @return {Ext.Component} this
448      */
449     setAutoScroll : function(scroll){
450         var me = this,
451             targetEl;
452         scroll = !!scroll;
453         if (me.rendered) {
454             targetEl = me.getTargetEl();
455             targetEl.setStyle('overflow', scroll ? 'auto' : '');
456             if (scroll && (Ext.isIE6 || Ext.isIE7)) {
457                 // The scrollable container element must be non-statically positioned or IE6/7 will make
458                 // positioned children stay in place rather than scrolling with the rest of the content
459                 targetEl.position();
460             }
461         }
462         me.autoScroll = scroll;
463         return me;
464     },
465
466     // private
467     makeFloating : function(cfg){
468         this.mixins.floating.constructor.call(this, cfg);
469     },
470
471     initResizable: function(resizable) {
472         var me = this;
473
474         resizable = Ext.apply({
475             target: me,
476             dynamic: false,
477             constrainTo: me.constrainTo || (me.floatParent ? me.floatParent.getTargetEl() : me.el.getScopeParent()),
478             handles: me.resizeHandles
479         }, resizable);
480         resizable.target = me;
481         me.resizer = Ext.create('Ext.resizer.Resizer', resizable);
482     },
483
484     getDragEl: function() {
485         return this.el;
486     },
487
488     initDraggable: function() {
489         var me = this,
490             ddConfig = Ext.applyIf({
491                 el: me.getDragEl(),
492                 constrainTo: me.constrain ? (me.constrainTo || (me.floatParent ? me.floatParent.getTargetEl() : me.el.getScopeParent())) : undefined
493             }, me.draggable);
494
495         // Add extra configs if Component is specified to be constrained
496         if (me.constrain || me.constrainDelegate) {
497             ddConfig.constrain = me.constrain;
498             ddConfig.constrainDelegate = me.constrainDelegate;
499         }
500
501         me.dd = Ext.create('Ext.util.ComponentDragger', me, ddConfig);
502     },
503
504     /**
505      * Sets the left and top of the component. To set the page XY position instead, use {@link #setPagePosition}. This
506      * method fires the {@link #move} event.
507      * @param {Number} left The new left
508      * @param {Number} top The new top
509      * @param {Boolean/Object} [animate] If true, the Component is _animated_ into its new position. You may also pass an
510      * animation configuration.
511      * @return {Ext.Component} this
512      */
513     setPosition: function(x, y, animate) {
514         var me = this,
515             el = me.el,
516             to = {},
517             adj, adjX, adjY, xIsNumber, yIsNumber;
518
519         if (Ext.isArray(x)) {
520             animate = y;
521             y = x[1];
522             x = x[0];
523         }
524         me.x = x;
525         me.y = y;
526
527         if (!me.rendered) {
528             return me;
529         }
530
531         adj = me.adjustPosition(x, y);
532         adjX = adj.x;
533         adjY = adj.y;
534         xIsNumber = Ext.isNumber(adjX);
535         yIsNumber = Ext.isNumber(adjY);
536
537         if (xIsNumber || yIsNumber) {
538             if (animate) {
539                 if (xIsNumber) {
540                     to.left = adjX;
541                 }
542                 if (yIsNumber) {
543                     to.top = adjY;
544                 }
545
546                 me.stopAnimation();
547                 me.animate(Ext.apply({
548                     duration: 1000,
549                     listeners: {
550                         afteranimate: Ext.Function.bind(me.afterSetPosition, me, [adjX, adjY])
551                     },
552                     to: to
553                 }, animate));
554             }
555             else {
556                 if (!xIsNumber) {
557                     el.setTop(adjY);
558                 }
559                 else if (!yIsNumber) {
560                     el.setLeft(adjX);
561                 }
562                 else {
563                     el.setLeftTop(adjX, adjY);
564                 }
565                 me.afterSetPosition(adjX, adjY);
566             }
567         }
568         return me;
569     },
570
571     /**
572      * @private
573      * @template
574      * Template method called after a Component has been positioned.
575      */
576     afterSetPosition: function(ax, ay) {
577         this.onPosition(ax, ay);
578         this.fireEvent('move', this, ax, ay);
579     },
580
581     /**
582      * Displays component at specific xy position.
583      * A floating component (like a menu) is positioned relative to its ownerCt if any.
584      * Useful for popping up a context menu:
585      *
586      *     listeners: {
587      *         itemcontextmenu: function(view, record, item, index, event, options) {
588      *             Ext.create('Ext.menu.Menu', {
589      *                 width: 100,
590      *                 height: 100,
591      *                 margin: '0 0 10 0',
592      *                 items: [{
593      *                     text: 'regular item 1'
594      *                 },{
595      *                     text: 'regular item 2'
596      *                 },{
597      *                     text: 'regular item 3'
598      *                 }]
599      *             }).showAt(event.getXY());
600      *         }
601      *     }
602      *
603      * @param {Number} x The new x position
604      * @param {Number} y The new y position
605      * @param {Boolean/Object} [animate] True to animate the Component into its new position. You may also pass an
606      * animation configuration.
607      */
608     showAt: function(x, y, animate) {
609         var me = this;
610
611         if (me.floating) {
612             me.setPosition(x, y, animate);
613         } else {
614             me.setPagePosition(x, y, animate);
615         }
616         me.show();
617     },
618
619     /**
620      * Sets the page XY position of the component. To set the left and top instead, use {@link #setPosition}.
621      * This method fires the {@link #move} event.
622      * @param {Number} x The new x position
623      * @param {Number} y The new y position
624      * @param {Boolean/Object} [animate] True to animate the Component into its new position. You may also pass an
625      * animation configuration.
626      * @return {Ext.Component} this
627      */
628     setPagePosition: function(x, y, animate) {
629         var me = this,
630             p;
631
632         if (Ext.isArray(x)) {
633             y = x[1];
634             x = x[0];
635         }
636         me.pageX = x;
637         me.pageY = y;
638         if (me.floating && me.floatParent) {
639             // Floating Components being positioned in their ownerCt have to be made absolute
640             p = me.floatParent.getTargetEl().getViewRegion();
641             if (Ext.isNumber(x) && Ext.isNumber(p.left)) {
642                 x -= p.left;
643             }
644             if (Ext.isNumber(y) && Ext.isNumber(p.top)) {
645                 y -= p.top;
646             }
647             me.setPosition(x, y, animate);
648         }
649         else {
650             p = me.el.translatePoints(x, y);
651             me.setPosition(p.left, p.top, animate);
652         }
653         return me;
654     },
655
656     /**
657      * Gets the current box measurements of the component's underlying element.
658      * @param {Boolean} [local=false] If true the element's left and top are returned instead of page XY.
659      * @return {Object} box An object in the format {x, y, width, height}
660      */
661     getBox : function(local){
662         var pos = this.getPosition(local),
663             size = this.getSize();
664
665         size.x = pos[0];
666         size.y = pos[1];
667         return size;
668     },
669
670     /**
671      * Sets the current box measurements of the component's underlying element.
672      * @param {Object} box An object in the format {x, y, width, height}
673      * @return {Ext.Component} this
674      */
675     updateBox : function(box){
676         this.setSize(box.width, box.height);
677         this.setPagePosition(box.x, box.y);
678         return this;
679     },
680
681     // Include margins
682     getOuterSize: function() {
683         var el = this.el;
684         return {
685             width: el.getWidth() + el.getMargin('lr'),
686             height: el.getHeight() + el.getMargin('tb')
687         };
688     },
689
690     // private
691     adjustPosition: function(x, y) {
692
693         // Floating Components being positioned in their ownerCt have to be made absolute
694         if (this.floating && this.floatParent) {
695             var o = this.floatParent.getTargetEl().getViewRegion();
696             x += o.left;
697             y += o.top;
698         }
699
700         return {
701             x: x,
702             y: y
703         };
704     },
705
706     /**
707      * Gets the current XY position of the component's underlying element.
708      * @param {Boolean} [local=false] If true the element's left and top are returned instead of page XY.
709      * @return {Number[]} The XY position of the element (e.g., [100, 200])
710      */
711     getPosition: function(local) {
712         var me = this,
713             el = me.el,
714             xy,
715             o;
716
717         // Floating Components which were just rendered with no ownerCt return local position.
718         if ((local === true) || (me.floating && !me.floatParent)) {
719             return [el.getLeft(true), el.getTop(true)];
720         }
721         xy = me.xy || el.getXY();
722
723         // Floating Components in an ownerCt have to have their positions made relative
724         if (me.floating) {
725             o = me.floatParent.getTargetEl().getViewRegion();
726             xy[0] -= o.left;
727             xy[1] -= o.top;
728         }
729         return xy;
730     },
731
732     getId: function() {
733         var me = this,
734             xtype;
735
736         if (!me.id) {
737             xtype = me.getXType();
738             xtype = xtype ? xtype.replace(Ext.Component.INVALID_ID_CHARS_Re, '-') : 'ext-comp';
739             me.id = xtype + '-' + me.getAutoId();
740         }
741         return me.id;
742     },
743
744     onEnable: function() {
745         var actionEl = this.getActionEl();
746         actionEl.dom.removeAttribute('aria-disabled');
747         actionEl.dom.disabled = false;
748         this.callParent();
749     },
750
751     onDisable: function() {
752         var actionEl = this.getActionEl();
753         actionEl.dom.setAttribute('aria-disabled', true);
754         actionEl.dom.disabled = true;
755         this.callParent();
756     },
757
758     /**
759      * Shows this Component, rendering it first if {@link #autoRender} or {@link #floating} are `true`.
760      *
761      * After being shown, a {@link #floating} Component (such as a {@link Ext.window.Window}), is activated it and
762      * brought to the front of its {@link #zIndexManager z-index stack}.
763      *
764      * @param {String/Ext.Element} [animateTarget=null] **only valid for {@link #floating} Components such as {@link
765      * Ext.window.Window Window}s or {@link Ext.tip.ToolTip ToolTip}s, or regular Components which have been configured
766      * with `floating: true`.** The target from which the Component should animate from while opening.
767      * @param {Function} [callback] A callback function to call after the Component is displayed.
768      * Only necessary if animation was specified.
769      * @param {Object} [scope] The scope (`this` reference) in which the callback is executed.
770      * Defaults to this Component.
771      * @return {Ext.Component} this
772      */
773     show: function(animateTarget, cb, scope) {
774         var me = this;
775
776         if (me.rendered && me.isVisible()) {
777             if (me.toFrontOnShow && me.floating) {
778                 me.toFront();
779             }
780         } else if (me.fireEvent('beforeshow', me) !== false) {
781             me.hidden = false;
782
783             // Render on first show if there is an autoRender config, or if this is a floater (Window, Menu, BoundList etc).
784             if (!me.rendered && (me.autoRender || me.floating)) {
785                 me.doAutoRender();
786             }
787             if (me.rendered) {
788                 me.beforeShow();
789                 me.onShow.apply(me, arguments);
790
791                 // Notify any owning Container unless it's suspended.
792                 // Floating Components do not participate in layouts.
793                 if (me.ownerCt && !me.floating && !(me.ownerCt.suspendLayout || me.ownerCt.layout.layoutBusy)) {
794                     me.ownerCt.doLayout();
795                 }
796                 me.afterShow.apply(me, arguments);
797             }
798         }
799         return me;
800     },
801
802     beforeShow: Ext.emptyFn,
803
804     // Private. Override in subclasses where more complex behaviour is needed.
805     onShow: function() {
806         var me = this;
807
808         me.el.show();
809         me.callParent(arguments);
810         if (me.floating && me.constrain) {
811             me.doConstrain();
812         }
813     },
814
815     afterShow: function(animateTarget, cb, scope) {
816         var me = this,
817             fromBox,
818             toBox,
819             ghostPanel;
820
821         // Default to configured animate target if none passed
822         animateTarget = animateTarget || me.animateTarget;
823
824         // Need to be able to ghost the Component
825         if (!me.ghost) {
826             animateTarget = null;
827         }
828         // If we're animating, kick of an animation of the ghost from the target to the *Element* current box
829         if (animateTarget) {
830             animateTarget = animateTarget.el ? animateTarget.el : Ext.get(animateTarget);
831             toBox = me.el.getBox();
832             fromBox = animateTarget.getBox();
833             me.el.addCls(Ext.baseCSSPrefix + 'hide-offsets');
834             ghostPanel = me.ghost();
835             ghostPanel.el.stopAnimation();
836
837             // Shunting it offscreen immediately, *before* the Animation class grabs it ensure no flicker.
838             ghostPanel.el.setX(-10000);
839
840             ghostPanel.el.animate({
841                 from: fromBox,
842                 to: toBox,
843                 listeners: {
844                     afteranimate: function() {
845                         delete ghostPanel.componentLayout.lastComponentSize;
846                         me.unghost();
847                         me.el.removeCls(Ext.baseCSSPrefix + 'hide-offsets');
848                         me.onShowComplete(cb, scope);
849                     }
850                 }
851             });
852         }
853         else {
854             me.onShowComplete(cb, scope);
855         }
856     },
857
858     onShowComplete: function(cb, scope) {
859         var me = this;
860         if (me.floating) {
861             me.toFront();
862         }
863         Ext.callback(cb, scope || me);
864         me.fireEvent('show', me);
865     },
866
867     /**
868      * Hides this Component, setting it to invisible using the configured {@link #hideMode}.
869      * @param {String/Ext.Element/Ext.Component} [animateTarget=null] **only valid for {@link #floating} Components
870      * such as {@link Ext.window.Window Window}s or {@link Ext.tip.ToolTip ToolTip}s, or regular Components which have
871      * been configured with `floating: true`.**. The target to which the Component should animate while hiding.
872      * @param {Function} [callback] A callback function to call after the Component is hidden.
873      * @param {Object} [scope] The scope (`this` reference) in which the callback is executed.
874      * Defaults to this Component.
875      * @return {Ext.Component} this
876      */
877     hide: function() {
878         var me = this;
879
880         // Clear the flag which is set if a floatParent was hidden while this is visible.
881         // If a hide operation was subsequently called, that pending show must be hidden.
882         me.showOnParentShow = false;
883
884         if (!(me.rendered && !me.isVisible()) && me.fireEvent('beforehide', me) !== false) {
885             me.hidden = true;
886             if (me.rendered) {
887                 me.onHide.apply(me, arguments);
888
889                 // Notify any owning Container unless it's suspended.
890                 // Floating Components do not participate in layouts.
891                 if (me.ownerCt && !me.floating && !(me.ownerCt.suspendLayout || me.ownerCt.layout.layoutBusy)) {
892                     me.ownerCt.doLayout();
893                 }
894             }
895         }
896         return me;
897     },
898
899     // Possibly animate down to a target element.
900     onHide: function(animateTarget, cb, scope) {
901         var me = this,
902             ghostPanel,
903             toBox;
904
905         // Default to configured animate target if none passed
906         animateTarget = animateTarget || me.animateTarget;
907
908         // Need to be able to ghost the Component
909         if (!me.ghost) {
910             animateTarget = null;
911         }
912         // If we're animating, kick off an animation of the ghost down to the target
913         if (animateTarget) {
914             animateTarget = animateTarget.el ? animateTarget.el : Ext.get(animateTarget);
915             ghostPanel = me.ghost();
916             ghostPanel.el.stopAnimation();
917             toBox = animateTarget.getBox();
918             toBox.width += 'px';
919             toBox.height += 'px';
920             ghostPanel.el.animate({
921                 to: toBox,
922                 listeners: {
923                     afteranimate: function() {
924                         delete ghostPanel.componentLayout.lastComponentSize;
925                         ghostPanel.el.hide();
926                         me.afterHide(cb, scope);
927                     }
928                 }
929             });
930         }
931         me.el.hide();
932         if (!animateTarget) {
933             me.afterHide(cb, scope);
934         }
935     },
936
937     afterHide: function(cb, scope) {
938         Ext.callback(cb, scope || this);
939         this.fireEvent('hide', this);
940     },
941
942     /**
943      * @private
944      * @template
945      * Template method to contribute functionality at destroy time.
946      */
947     onDestroy: function() {
948         var me = this;
949
950         // Ensure that any ancillary components are destroyed.
951         if (me.rendered) {
952             Ext.destroy(
953                 me.proxy,
954                 me.proxyWrap,
955                 me.resizer
956             );
957             // Different from AbstractComponent
958             if (me.actionMode == 'container' || me.removeMode == 'container') {
959                 me.container.remove();
960             }
961         }
962         delete me.focusTask;
963         me.callParent();
964     },
965
966     deleteMembers: function() {
967         var args = arguments,
968             len = args.length,
969             i = 0;
970         for (; i < len; ++i) {
971             delete this[args[i]];
972         }
973     },
974
975     /**
976      * Try to focus this component.
977      * @param {Boolean} [selectText] If applicable, true to also select the text in this component
978      * @param {Boolean/Number} [delay] Delay the focus this number of milliseconds (true for 10 milliseconds).
979      * @return {Ext.Component} this
980      */
981     focus: function(selectText, delay) {
982         var me = this,
983                 focusEl;
984
985         if (delay) {
986             if (!me.focusTask) {
987                 me.focusTask = Ext.create('Ext.util.DelayedTask', me.focus);
988             }
989             me.focusTask.delay(Ext.isNumber(delay) ? delay : 10, null, me, [selectText, false]);
990             return me;
991         }
992
993         if (me.rendered && !me.isDestroyed) {
994             // getFocusEl could return a Component.
995             focusEl = me.getFocusEl();
996             focusEl.focus();
997             if (focusEl.dom && selectText === true) {
998                 focusEl.dom.select();
999             }
1000
1001             // Focusing a floating Component brings it to the front of its stack.
1002             // this is performed by its zIndexManager. Pass preventFocus true to avoid recursion.
1003             if (me.floating) {
1004                 me.toFront(true);
1005             }
1006         }
1007         return me;
1008     },
1009
1010     /**
1011      * @private
1012      * Returns the focus holder element associated with this Component. By default, this is the Component's encapsulating
1013      * element. Subclasses which use embedded focusable elements (such as Window and Button) should override this for use
1014      * by the {@link #focus} method.
1015      * @returns {Ext.Element} the focus holing element.
1016      */
1017     getFocusEl: function() {
1018         return this.el;
1019     },
1020
1021     // private
1022     blur: function() {
1023         if (this.rendered) {
1024             this.getFocusEl().blur();
1025         }
1026         return this;
1027     },
1028
1029     getEl: function() {
1030         return this.el;
1031     },
1032
1033     // Deprecate 5.0
1034     getResizeEl: function() {
1035         return this.el;
1036     },
1037
1038     // Deprecate 5.0
1039     getPositionEl: function() {
1040         return this.el;
1041     },
1042
1043     // Deprecate 5.0
1044     getActionEl: function() {
1045         return this.el;
1046     },
1047
1048     // Deprecate 5.0
1049     getVisibilityEl: function() {
1050         return this.el;
1051     },
1052
1053     // Deprecate 5.0
1054     onResize: Ext.emptyFn,
1055
1056     // private
1057     getBubbleTarget: function() {
1058         return this.ownerCt;
1059     },
1060
1061     // private
1062     getContentTarget: function() {
1063         return this.el;
1064     },
1065
1066     /**
1067      * Clone the current component using the original config values passed into this instance by default.
1068      * @param {Object} overrides A new config containing any properties to override in the cloned version.
1069      * An id property can be passed on this object, otherwise one will be generated to avoid duplicates.
1070      * @return {Ext.Component} clone The cloned copy of this component
1071      */
1072     cloneConfig: function(overrides) {
1073         overrides = overrides || {};
1074         var id = overrides.id || Ext.id(),
1075             cfg = Ext.applyIf(overrides, this.initialConfig),
1076             self;
1077
1078         cfg.id = id;
1079
1080         self = Ext.getClass(this);
1081
1082         // prevent dup id
1083         return new self(cfg);
1084     },
1085
1086     /**
1087      * Gets the xtype for this component as registered with {@link Ext.ComponentManager}. For a list of all available
1088      * xtypes, see the {@link Ext.Component} header. Example usage:
1089      *
1090      *     var t = new Ext.form.field.Text();
1091      *     alert(t.getXType());  // alerts 'textfield'
1092      *
1093      * @return {String} The xtype
1094      */
1095     getXType: function() {
1096         return this.self.xtype;
1097     },
1098
1099     /**
1100      * Find a container above this component at any level by a custom function. If the passed function returns true, the
1101      * container will be returned.
1102      * @param {Function} fn The custom function to call with the arguments (container, this component).
1103      * @return {Ext.container.Container} The first Container for which the custom function returns true
1104      */
1105     findParentBy: function(fn) {
1106         var p;
1107
1108         // Iterate up the ownerCt chain until there's no ownerCt, or we find an ancestor which matches using the selector function.
1109         for (p = this.ownerCt; p && !fn(p, this); p = p.ownerCt);
1110         return p || null;
1111     },
1112
1113     /**
1114      * Find a container above this component at any level by xtype or class
1115      *
1116      * See also the {@link Ext.Component#up up} method.
1117      *
1118      * @param {String/Ext.Class} xtype The xtype string for a component, or the class of the component directly
1119      * @return {Ext.container.Container} The first Container which matches the given xtype or class
1120      */
1121     findParentByType: function(xtype) {
1122         return Ext.isFunction(xtype) ?
1123             this.findParentBy(function(p) {
1124                 return p.constructor === xtype;
1125             })
1126         :
1127             this.up(xtype);
1128     },
1129
1130     /**
1131      * Bubbles up the component/container heirarchy, calling the specified function with each component. The scope
1132      * (*this*) of function call will be the scope provided or the current component. The arguments to the function will
1133      * be the args provided or the current component. If the function returns false at any point, the bubble is stopped.
1134      *
1135      * @param {Function} fn The function to call
1136      * @param {Object} [scope] The scope of the function. Defaults to current node.
1137      * @param {Array} [args] The args to call the function with. Defaults to passing the current component.
1138      * @return {Ext.Component} this
1139      */
1140     bubble: function(fn, scope, args) {
1141         var p = this;
1142         while (p) {
1143             if (fn.apply(scope || p, args || [p]) === false) {
1144                 break;
1145             }
1146             p = p.ownerCt;
1147         }
1148         return this;
1149     },
1150
1151     getProxy: function() {
1152         var me = this,
1153             target;
1154
1155         if (!me.proxy) {
1156             target = Ext.getBody();
1157             if (Ext.scopeResetCSS) {
1158                 me.proxyWrap = target = Ext.getBody().createChild({
1159                     cls: Ext.baseCSSPrefix + 'reset'
1160                 });
1161             }
1162             me.proxy = me.el.createProxy(Ext.baseCSSPrefix + 'proxy-el', target, true);
1163         }
1164         return me.proxy;
1165     }
1166
1167 });
1168