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