Upgrade to ExtJS 4.0.2 - Released 06/09/2011
[extjs.git] / src / panel / Panel.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  * @class Ext.panel.Panel
17  * @extends Ext.panel.AbstractPanel
18  * <p>Panel is a container that has specific functionality and structural components that make
19  * it the perfect building block for application-oriented user interfaces.</p>
20  * <p>Panels are, by virtue of their inheritance from {@link Ext.container.Container}, capable
21  * of being configured with a {@link Ext.container.Container#layout layout}, and containing child Components.</p>
22  * <p>When either specifying child {@link Ext.Component#items items} of a Panel, or dynamically {@link Ext.container.Container#add adding} Components
23  * to a Panel, remember to consider how you wish the Panel to arrange those child elements, and whether
24  * those child elements need to be sized using one of Ext&#39;s built-in <code><b>{@link Ext.container.Container#layout layout}</b></code> schemes. By
25  * default, Panels use the {@link Ext.layout.container.Auto Auto} scheme. This simply renders
26  * child components, appending them one after the other inside the Container, and <b>does not apply any sizing</b>
27  * at all.</p>
28  * {@img Ext.panel.Panel/panel.png Panel components}
29  * <p>A Panel may also contain {@link #bbar bottom} and {@link #tbar top} toolbars, along with separate
30  * {@link #header}, {@link #footer} and {@link #body} sections (see {@link #frame} for additional
31  * information).</p>
32  * <p>Panel also provides built-in {@link #collapsible collapsible, expandable} and {@link #closable} behavior.
33  * Panels can be easily dropped into any {@link Ext.container.Container Container} or layout, and the
34  * layout and rendering pipeline is {@link Ext.container.Container#add completely managed by the framework}.</p>
35  * <p><b>Note:</b> By default, the <code>{@link #closable close}</code> header tool <i>destroys</i> the Panel resulting in removal of the Panel
36  * and the destruction of any descendant Components. This makes the Panel object, and all its descendants <b>unusable</b>. To enable the close
37  * tool to simply <i>hide</i> a Panel for later re-use, configure the Panel with <b><code>{@link #closeAction closeAction: 'hide'}</code></b>.</p>
38  * <p>Usually, Panels are used as constituents within an application, in which case, they would be used as child items of Containers,
39  * and would themselves use Ext.Components as child {@link #items}. However to illustrate simply rendering a Panel into the document,
40  * here&#39;s how to do it:<pre><code>
41 Ext.create('Ext.panel.Panel', {
42     title: 'Hello',
43     width: 200,
44     html: '&lt;p&gt;World!&lt;/p&gt;',
45     renderTo: document.body
46 });
47 </code></pre></p>
48  * <p>A more realistic scenario is a Panel created to house input fields which will not be rendered, but used as a constituent part of a Container:<pre><code>
49 var filterPanel = Ext.create('Ext.panel.Panel', {
50     bodyPadding: 5,  // Don&#39;t want content to crunch against the borders
51     title: 'Filters',
52     items: [{
53         xtype: 'datefield',
54         fieldLabel: 'Start date'
55     }, {
56         xtype: 'datefield',
57         fieldLabel: 'End date'
58     }]
59 });
60 </code></pre></p>
61  * <p>Note that the Panel above is not configured to render into the document, nor is it configured with a size or position. In a real world scenario,
62  * the Container into which the Panel is added will use a {@link #layout} to render, size and position its child Components.</p>
63  * <p>Panels will often use specific {@link #layout}s to provide an application with shape and structure by containing and arranging child
64  * Components: <pre><code>
65 var resultsPanel = Ext.create('Ext.panel.Panel', {
66     title: 'Results',
67     width: 600,
68     height: 400,
69     renderTo: document.body,
70     layout: {
71         type: 'vbox',       // Arrange child items vertically
72         align: 'stretch',    // Each takes up full width
73         padding: 5
74     },
75     items: [{               // Results grid specified as a config object with an xtype of 'grid'
76         xtype: 'grid',
77         columns: [{header: 'Column One'}],            // One header just for show. There&#39;s no data,
78         store: Ext.create('Ext.data.ArrayStore', {}), // A dummy empty data store
79         flex: 1                                       // Use 1/3 of Container&#39;s height (hint to Box layout)
80     }, {
81         xtype: 'splitter'   // A splitter between the two child items
82     }, {                    // Details Panel specified as a config object (no xtype defaults to 'panel').
83         title: 'Details',
84         bodyPadding: 5,
85         items: [{
86             fieldLabel: 'Data item',
87             xtype: 'textfield'
88         }], // An array of form fields
89         flex: 2             // Use 2/3 of Container&#39;s height (hint to Box layout)
90     }]
91 });
92 </code></pre>
93  * The example illustrates one possible method of displaying search results. The Panel contains a grid with the resulting data arranged
94  * in rows. Each selected row may be displayed in detail in the Panel below. The {@link Ext.layout.container.VBox vbox} layout is used
95  * to arrange the two vertically. It is configured to stretch child items horizontally to full width. Child items may either be configured
96  * with a numeric height, or with a <code>flex</code> value to distribute available space proportionately.</p>
97  * <p>This Panel itself may be a child item of, for exaple, a {@link Ext.tab.Panel} which will size its child items to fit within its
98  * content area.</p>
99  * <p>Using these techniques, as long as the <b>layout</b> is chosen and configured correctly, an application may have any level of
100  * nested containment, all dynamically sized according to configuration, the user&#39;s preference and available browser size.</p>
101  */
102 Ext.define('Ext.panel.Panel', {
103     extend: 'Ext.panel.AbstractPanel',
104     requires: [
105         'Ext.panel.Header',
106         'Ext.fx.Anim',
107         'Ext.util.KeyMap',
108         'Ext.panel.DD',
109         'Ext.XTemplate',
110         'Ext.layout.component.Dock',
111         'Ext.util.Memento'
112     ],
113     alias: 'widget.panel',
114     alternateClassName: 'Ext.Panel',
115
116     /**
117      * @cfg {String} collapsedCls
118      * A CSS class to add to the panel&#39;s element after it has been collapsed (defaults to
119      * <code>'collapsed'</code>).
120      */
121     collapsedCls: 'collapsed',
122
123     /**
124      * @cfg {Boolean} animCollapse
125      * <code>true</code> to animate the transition when the panel is collapsed, <code>false</code> to skip the
126      * animation (defaults to <code>true</code> if the {@link Ext.fx.Anim} class is available, otherwise <code>false</code>).
127      * May also be specified as the animation duration in milliseconds.
128      */
129     animCollapse: Ext.enableFx,
130
131     /**
132      * @cfg {Number} minButtonWidth
133      * Minimum width of all footer toolbar buttons in pixels (defaults to <tt>75</tt>). If set, this will
134      * be used as the default value for the <tt>{@link Ext.button.Button#minWidth}</tt> config of
135      * each Button added to the <b>footer toolbar</b> via the {@link #fbar} or {@link #buttons} configurations.
136      * It will be ignored for buttons that have a minWidth configured some other way, e.g. in their own config
137      * object or via the {@link Ext.container.Container#config-defaults defaults} of their parent container.
138      */
139     minButtonWidth: 75,
140
141     /**
142      * @cfg {Boolean} collapsed
143      * <code>true</code> to render the panel collapsed, <code>false</code> to render it expanded (defaults to
144      * <code>false</code>).
145      */
146     collapsed: false,
147
148     /**
149      * @cfg {Boolean} collapseFirst
150      * <code>true</code> to make sure the collapse/expand toggle button always renders first (to the left of)
151      * any other tools in the panel&#39;s title bar, <code>false</code> to render it last (defaults to <code>true</code>).
152      */
153     collapseFirst: true,
154
155     /**
156      * @cfg {Boolean} hideCollapseTool
157      * <code>true</code> to hide the expand/collapse toggle button when <code>{@link #collapsible} == true</code>,
158      * <code>false</code> to display it (defaults to <code>false</code>).
159      */
160     hideCollapseTool: false,
161
162     /**
163      * @cfg {Boolean} titleCollapse
164      * <code>true</code> to allow expanding and collapsing the panel (when <code>{@link #collapsible} = true</code>)
165      * by clicking anywhere in the header bar, <code>false</code>) to allow it only by clicking to tool button
166      * (defaults to <code>false</code>)).
167      */
168     titleCollapse: false,
169
170     /**
171      * @cfg {String} collapseMode
172      * <p><b>Important: this config is only effective for {@link #collapsible} Panels which are direct child items of a {@link Ext.layout.container.Border border layout}.</b></p>
173      * <p>When <i>not</i> a direct child item of a {@link Ext.layout.container.Border border layout}, then the Panel&#39;s header remains visible, and the body is collapsed to zero dimensions.
174      * If the Panel has no header, then a new header (orientated correctly depending on the {@link #collapseDirection}) will be inserted to show a the title and a re-expand tool.</p>
175      * <p>When a child item of a {@link Ext.layout.container.Border border layout}, this config has two options:
176      * <div class="mdetail-params"><ul>
177      * <li><b><code>undefined/omitted</code></b><div class="sub-desc">When collapsed, a placeholder {@link Ext.panel.Header Header} is injected into the layout to represent the Panel
178      * and to provide a UI with a Tool to allow the user to re-expand the Panel.</div></li>
179      * <li><b><code>header</code></b> : <div class="sub-desc">The Panel collapses to leave its header visible as when not inside a {@link Ext.layout.container.Border border layout}.</div></li>
180      * </ul></div></p>
181      */
182
183     /**
184      * @cfg {Mixed} placeholder
185      * <p><b>Important: This config is only effective for {@link #collapsible} Panels which are direct child items of a {@link Ext.layout.container.Border border layout}
186      * when not using the <code>'header'</code> {@link #collapseMode}.</b></p>
187      * <p><b>Optional.</b> A Component (or config object for a Component) to show in place of this Panel when this Panel is collapsed by a
188      * {@link Ext.layout.container.Border border layout}. Defaults to a generated {@link Ext.panel.Header Header}
189      * containing a {@link Ext.panel.Tool Tool} to re-expand the Panel.</p>
190      */
191
192     /**
193      * @cfg {Boolean} floatable
194      * <p><b>Important: This config is only effective for {@link #collapsible} Panels which are direct child items of a {@link Ext.layout.container.Border border layout}.</b></p>
195      * <tt>true</tt> to allow clicking a collapsed Panel&#39;s {@link #placeholder} to display the Panel floated
196      * above the layout, <tt>false</tt> to force the user to fully expand a collapsed region by
197      * clicking the expand button to see it again (defaults to <tt>true</tt>).
198      */
199     floatable: true,
200
201     /**
202      * @cfg {Mixed} overlapHeader
203      * True to overlap the header in a panel over the framing of the panel itself. This is needed when frame:true (and is done automatically for you). Otherwise it is undefined.
204      * If you manually add rounded corners to a panel header which does not have frame:true, this will need to be set to true.
205      */
206
207     /**
208      * @cfg {Boolean} collapsible
209      * <p>True to make the panel collapsible and have an expand/collapse toggle Tool added into
210      * the header tool button area. False to keep the panel sized either statically, or by an owning layout manager, with no toggle Tool (defaults to false).</p>
211      * See {@link #collapseMode} and {@link #collapseDirection}
212      */
213     collapsible: false,
214
215     /**
216      * @cfg {Boolean} collapseDirection
217      * <p>The direction to collapse the Panel when the toggle button is clicked.</p>
218      * <p>Defaults to the {@link #headerPosition}</p>
219      * <p><b>Important: This config is <u>ignored</u> for {@link #collapsible} Panels which are direct child items of a {@link Ext.layout.container.Border border layout}.</b></p>
220      * <p>Specify as <code>'top'</code>, <code>'bottom'</code>, <code>'left'</code> or <code>'right'</code>.</p>
221      */
222
223     /**
224      * @cfg {Boolean} closable
225      * <p>True to display the 'close' tool button and allow the user to close the window, false to
226      * hide the button and disallow closing the window (defaults to <code>false</code>).</p>
227      * <p>By default, when close is requested by clicking the close button in the header, the {@link #close}
228      * method will be called. This will <i>{@link Ext.Component#destroy destroy}</i> the Panel and its content
229      * meaning that it may not be reused.</p>
230      * <p>To make closing a Panel <i>hide</i> the Panel so that it may be reused, set
231      * {@link #closeAction} to 'hide'.</p>
232      */
233     closable: false,
234
235     /**
236      * @cfg {String} closeAction
237      * <p>The action to take when the close header tool is clicked:
238      * <div class="mdetail-params"><ul>
239      * <li><b><code>'{@link #destroy}'</code></b> : <b>Default</b><div class="sub-desc">
240      * {@link #destroy remove} the window from the DOM and {@link Ext.Component#destroy destroy}
241      * it and all descendant Components. The window will <b>not</b> be available to be
242      * redisplayed via the {@link #show} method.
243      * </div></li>
244      * <li><b><code>'{@link #hide}'</code></b> : <div class="sub-desc">
245      * {@link #hide} the window by setting visibility to hidden and applying negative offsets.
246      * The window will be available to be redisplayed via the {@link #show} method.
247      * </div></li>
248      * </ul></div>
249      * <p><b>Note:</b> This behavior has changed! setting *does* affect the {@link #close} method
250      * which will invoke the approriate closeAction.
251      */
252     closeAction: 'destroy',
253
254     /**
255      * @cfg {Object/Array} dockedItems
256      * A component or series of components to be added as docked items to this panel.
257      * The docked items can be docked to either the top, right, left or bottom of a panel.
258      * This is typically used for things like toolbars or tab bars:
259      * <pre><code>
260 var panel = new Ext.panel.Panel({
261     dockedItems: [{
262         xtype: 'toolbar',
263         dock: 'top',
264         items: [{
265             text: 'Docked to the top'
266         }]
267     }]
268 });</pre></code>
269      */
270
271     /**
272       * @cfg {Boolean} preventHeader Prevent a Header from being created and shown. Defaults to false.
273       */
274     preventHeader: false,
275
276      /**
277       * @cfg {String} headerPosition Specify as <code>'top'</code>, <code>'bottom'</code>, <code>'left'</code> or <code>'right'</code>. Defaults to <code>'top'</code>.
278       */
279     headerPosition: 'top',
280
281      /**
282      * @cfg {Boolean} frame
283      * True to apply a frame to the panel.
284      */
285     frame: false,
286
287     /**
288      * @cfg {Boolean} frameHeader
289      * True to apply a frame to the panel panels header (if 'frame' is true).
290      */
291     frameHeader: true,
292
293     /**
294      * @cfg {Array} tools
295      * An array of {@link Ext.panel.Tool} configs/instances to be added to the header tool area. The tools are stored as child
296      * components of the header container. They can be accessed using {@link #down} and {#query}, as well as the other
297      * component methods. The toggle tool is automatically created if {@link #collapsible} is set to true.
298      * <p>Note that, apart from the toggle tool which is provided when a panel is collapsible, these
299      * tools only provide the visual button. Any required functionality must be provided by adding
300      * handlers that implement the necessary behavior.</p>
301      * <p>Example usage:</p>
302      * <pre><code>
303 tools:[{
304     type:'refresh',
305     qtip: 'Refresh form Data',
306     // hidden:true,
307     handler: function(event, toolEl, panel){
308         // refresh logic
309     }
310 },
311 {
312     type:'help',
313     qtip: 'Get Help',
314     handler: function(event, toolEl, panel){
315         // show help here
316     }
317 }]
318 </code></pre>
319      */
320
321     /**
322      * @cfg {String} title
323      * The title text to be used to display in the {@link Ext.panel.Header panel header} (defaults to '').
324      * When a `title` is specified the {@link Ext.panel.Header} will automatically be created and displayed unless
325      * {@link #preventHeader} is set to `true`.
326      */
327
328     initComponent: function() {
329         var me = this,
330             cls;
331
332         me.addEvents(
333             /**
334              * @event beforeexpand
335              * Fires before this panel is expanded. Return false to prevent the expand.
336              * @param {Ext.panel.Panel} p The Panel being expanded.
337              * @param {Boolean} animate True if the expand is animated, else false.
338              */
339             "beforeexpand",
340
341             /**
342              * @event beforecollapse
343              * Fires before this panel is collapsed. Return false to prevent the collapse.
344              * @param {Ext.panel.Panel} p The Panel being collapsed.
345              * @param {String} direction. The direction of the collapse. One of<ul>
346              * <li>Ext.Component.DIRECTION_TOP</li>
347              * <li>Ext.Component.DIRECTION_RIGHT</li>
348              * <li>Ext.Component.DIRECTION_BOTTOM</li>
349              * <li>Ext.Component.DIRECTION_LEFT</li></ul>
350              * @param {Boolean} animate True if the collapse is animated, else false.
351              */
352             "beforecollapse",
353
354             /**
355              * @event expand
356              * Fires after this Panel has expanded.
357              * @param {Ext.panel.Panel} p The Panel that has been expanded.
358              */
359             "expand",
360
361             /**
362              * @event collapse
363              * Fires after this Panel hass collapsed.
364              * @param {Ext.panel.Panel} p The Panel that has been collapsed.
365              */
366             "collapse",
367
368             /**
369              * @event titlechange
370              * Fires after the Panel title has been set or changed.
371              * @param {Ext.panel.Panel} p the Panel which has been resized.
372              * @param {String} newTitle The new title.
373              * @param {String} oldTitle The previous panel title.
374              */
375             'titlechange',
376
377             /**
378              * @event iconchange
379              * Fires after the Panel iconCls has been set or changed.
380              * @param {Ext.panel.Panel} p the Panel which has been resized.
381              * @param {String} newIconCls The new iconCls.
382              * @param {String} oldIconCls The previous panel iconCls.
383              */
384             'iconchange'
385         );
386
387         // Save state on these two events.
388         this.addStateEvents('expand', 'collapse');
389
390         if (me.unstyled) {
391             me.setUI('plain');
392         }
393
394         if (me.frame) {
395             me.setUI(me.ui + '-framed');
396         }
397
398         me.callParent();
399
400         me.collapseDirection = me.collapseDirection || me.headerPosition || Ext.Component.DIRECTION_TOP;
401
402         // Backwards compatibility
403         me.bridgeToolbars();
404     },
405
406     setBorder: function(border) {
407         // var me     = this,
408         //     method = (border === false || border === 0) ? 'addClsWithUI' : 'removeClsWithUI';
409         //
410         // me.callParent(arguments);
411         //
412         // if (me.collapsed) {
413         //     me[method](me.collapsedCls + '-noborder');
414         // }
415         //
416         // if (me.header) {
417         //     me.header.setBorder(border);
418         //     if (me.collapsed) {
419         //         me.header[method](me.collapsedCls + '-noborder');
420         //     }
421         // }
422
423         this.callParent(arguments);
424     },
425
426     beforeDestroy: function() {
427         Ext.destroy(
428             this.ghostPanel,
429             this.dd
430         );
431         this.callParent();
432     },
433
434     initAria: function() {
435         this.callParent();
436         this.initHeaderAria();
437     },
438
439     initHeaderAria: function() {
440         var me = this,
441             el = me.el,
442             header = me.header;
443         if (el && header) {
444             el.dom.setAttribute('aria-labelledby', header.titleCmp.id);
445         }
446     },
447
448     getHeader: function() {
449         return this.header;
450     },
451
452     /**
453      * Set a title for the panel&#39;s header. See {@link Ext.panel.Header#title}.
454      * @param {String} newTitle
455      */
456     setTitle: function(newTitle) {
457         var me = this,
458         oldTitle = this.title;
459
460         me.title = newTitle;
461         if (me.header) {
462             me.header.setTitle(newTitle);
463         } else {
464             me.updateHeader();
465         }
466
467         if (me.reExpander) {
468             me.reExpander.setTitle(newTitle);
469         }
470         me.fireEvent('titlechange', me, newTitle, oldTitle);
471     },
472
473     /**
474      * Set the iconCls for the panel&#39;s header. See {@link Ext.panel.Header#iconCls}.
475      * @param {String} newIconCls
476      */
477     setIconCls: function(newIconCls) {
478         var me = this,
479             oldIconCls = me.iconCls;
480
481         me.iconCls = newIconCls;
482         var header = me.header;
483         if (header) {
484             header.setIconCls(newIconCls);
485         }
486         me.fireEvent('iconchange', me, newIconCls, oldIconCls);
487     },
488
489     bridgeToolbars: function() {
490         var me = this,
491             fbar,
492             fbarDefaults,
493             minButtonWidth = me.minButtonWidth;
494
495         function initToolbar (toolbar, pos, useButtonAlign) {
496             if (Ext.isArray(toolbar)) {
497                 toolbar = {
498                     xtype: 'toolbar',
499                     items: toolbar
500                 };
501             }
502             else if (!toolbar.xtype) {
503                 toolbar.xtype = 'toolbar';
504             }
505             toolbar.dock = pos;
506             if (pos == 'left' || pos == 'right') {
507                 toolbar.vertical = true;
508             }
509
510             // Legacy support for buttonAlign (only used by buttons/fbar)
511             if (useButtonAlign) {
512                 toolbar.layout = Ext.applyIf(toolbar.layout || {}, {
513                     // default to 'end' (right-aligned) if me.buttonAlign is undefined or invalid
514                     pack: { left:'start', center:'center' }[me.buttonAlign] || 'end'
515                 });
516             }
517             return toolbar;
518         }
519
520         // Short-hand toolbars (tbar, bbar and fbar plus new lbar and rbar):
521
522     /**
523      * @cfg {String} buttonAlign
524      * <p>The alignment of any buttons added to this panel.  Valid values are 'right',
525      * 'left' and 'center' (defaults to 'right' for buttons/fbar, 'left' for other toolbar types).</p>
526      * <p><b>NOTE:</b> The newer way to specify toolbars is to use the dockedItems config, and
527      * instead of buttonAlign you would add the layout: { pack: 'start' | 'center' | 'end' }
528      * option to the dockedItem config.</p>
529      */
530
531         /**
532          * @cfg {Object/Array} tbar
533
534 Convenience method. Short for 'Top Bar'.
535
536     tbar: [
537       { xtype: 'button', text: 'Button 1' }
538     ]
539
540 is equivalent to
541
542     dockedItems: [{
543         xtype: 'toolbar',
544         dock: 'top',
545         items: [
546             { xtype: 'button', text: 'Button 1' }
547         ]
548     }]
549
550          * @markdown
551          */
552         if (me.tbar) {
553             me.addDocked(initToolbar(me.tbar, 'top'));
554             me.tbar = null;
555         }
556
557         /**
558          * @cfg {Object/Array} bbar
559
560 Convenience method. Short for 'Bottom Bar'.
561
562     bbar: [
563       { xtype: 'button', text: 'Button 1' }
564     ]
565
566 is equivalent to
567
568     dockedItems: [{
569         xtype: 'toolbar',
570         dock: 'bottom',
571         items: [
572             { xtype: 'button', text: 'Button 1' }
573         ]
574     }]
575
576          * @markdown
577          */
578         if (me.bbar) {
579             me.addDocked(initToolbar(me.bbar, 'bottom'));
580             me.bbar = null;
581         }
582
583         /**
584          * @cfg {Object/Array} buttons
585
586 Convenience method used for adding buttons docked to the bottom of the panel. This is a
587 synonym for the {@link #fbar} config.
588
589     buttons: [
590       { text: 'Button 1' }
591     ]
592
593 is equivalent to
594
595     dockedItems: [{
596         xtype: 'toolbar',
597         dock: 'bottom',
598         ui: 'footer',
599         defaults: {minWidth: {@link #minButtonWidth}},
600         items: [
601             { xtype: 'component', flex: 1 },
602             { xtype: 'button', text: 'Button 1' }
603         ]
604     }]
605
606 The {@link #minButtonWidth} is used as the default {@link Ext.button.Button#minWidth minWidth} for
607 each of the buttons in the buttons toolbar.
608
609          * @markdown
610          */
611         if (me.buttons) {
612             me.fbar = me.buttons;
613             me.buttons = null;
614         }
615
616         /**
617          * @cfg {Object/Array} fbar
618
619 Convenience method used for adding items to the bottom of the panel. Short for Footer Bar.
620
621     fbar: [
622       { type: 'button', text: 'Button 1' }
623     ]
624
625 is equivalent to
626
627     dockedItems: [{
628         xtype: 'toolbar',
629         dock: 'bottom',
630         ui: 'footer',
631         defaults: {minWidth: {@link #minButtonWidth}},
632         items: [
633             { xtype: 'component', flex: 1 },
634             { xtype: 'button', text: 'Button 1' }
635         ]
636     }]
637
638 The {@link #minButtonWidth} is used as the default {@link Ext.button.Button#minWidth minWidth} for
639 each of the buttons in the fbar.
640
641          * @markdown
642          */
643         if (me.fbar) {
644             fbar = initToolbar(me.fbar, 'bottom', true); // only we useButtonAlign
645             fbar.ui = 'footer';
646
647             // Apply the minButtonWidth config to buttons in the toolbar
648             if (minButtonWidth) {
649                 fbarDefaults = fbar.defaults;
650                 fbar.defaults = function(config) {
651                     var defaults = fbarDefaults || {};
652                     if ((!config.xtype || config.xtype === 'button' || (config.isComponent && config.isXType('button'))) &&
653                             !('minWidth' in defaults)) {
654                         defaults = Ext.apply({minWidth: minButtonWidth}, defaults);
655                     }
656                     return defaults;
657                 };
658             }
659
660             me.addDocked(fbar);
661             me.fbar = null;
662         }
663
664         /**
665          * @cfg {Object/Array} lbar
666          *
667          * Convenience method. Short for 'Left Bar' (left-docked, vertical toolbar).
668          *
669          *     lbar: [
670          *       { xtype: 'button', text: 'Button 1' }
671          *     ]
672          *
673          * is equivalent to
674          *
675          *     dockedItems: [{
676          *         xtype: 'toolbar',
677          *         dock: 'left',
678          *         items: [
679          *             { xtype: 'button', text: 'Button 1' }
680          *         ]
681          *     }]
682          *
683          * @markdown
684          */
685         if (me.lbar) {
686             me.addDocked(initToolbar(me.lbar, 'left'));
687             me.lbar = null;
688         }
689
690         /**
691          * @cfg {Object/Array} rbar
692          *
693          * Convenience method. Short for 'Right Bar' (right-docked, vertical toolbar).
694          *
695          *     rbar: [
696          *       { xtype: 'button', text: 'Button 1' }
697          *     ]
698          *
699          * is equivalent to
700          *
701          *     dockedItems: [{
702          *         xtype: 'toolbar',
703          *         dock: 'right',
704          *         items: [
705          *             { xtype: 'button', text: 'Button 1' }
706          *         ]
707          *     }]
708          *
709          * @markdown
710          */
711         if (me.rbar) {
712             me.addDocked(initToolbar(me.rbar, 'right'));
713             me.rbar = null;
714         }
715     },
716
717     /**
718      * @private
719      * Tools are a Panel-specific capabilty.
720      * Panel uses initTools. Subclasses may contribute tools by implementing addTools.
721      */
722     initTools: function() {
723         var me = this;
724
725         me.tools = me.tools || [];
726
727         // Add a collapse tool unless configured to not show a collapse tool
728         // or to not even show a header.
729         if (me.collapsible && !(me.hideCollapseTool || me.header === false)) {
730             me.collapseDirection = me.collapseDirection || me.headerPosition || 'top';
731             me.collapseTool = me.expandTool = me.createComponent({
732                 xtype: 'tool',
733                 type: 'collapse-' + me.collapseDirection,
734                 expandType: me.getOppositeDirection(me.collapseDirection),
735                 handler: me.toggleCollapse,
736                 scope: me
737             });
738
739             // Prepend collapse tool is configured to do so.
740             if (me.collapseFirst) {
741                 me.tools.unshift(me.collapseTool);
742             }
743         }
744
745         // Add subclass-specific tools.
746         me.addTools();
747
748         // Make Panel closable.
749         if (me.closable) {
750             me.addClsWithUI('closable');
751             me.addTool({
752                 type: 'close',
753                 handler: Ext.Function.bind(me.close, this, [])
754             });
755         }
756
757         // Append collapse tool if needed.
758         if (me.collapseTool && !me.collapseFirst) {
759             me.tools.push(me.collapseTool);
760         }
761     },
762
763     /**
764      * @private
765      * Template method to be implemented in subclasses to add their tools after the collapsible tool.
766      */
767     addTools: Ext.emptyFn,
768
769     /**
770      * <p>Closes the Panel. By default, this method, removes it from the DOM, {@link Ext.Component#destroy destroy}s
771      * the Panel object and all its descendant Components. The {@link #beforeclose beforeclose}
772      * event is fired before the close happens and will cancel the close action if it returns false.<p>
773      * <p><b>Note:</b> This method is not affected by the {@link #closeAction} setting which
774      * only affects the action triggered when clicking the {@link #closable 'close' tool in the header}.
775      * To hide the Panel without destroying it, call {@link #hide}.</p>
776      */
777     close: function() {
778         if (this.fireEvent('beforeclose', this) !== false) {
779             this.doClose();
780         }
781     },
782
783     // private
784     doClose: function() {
785         this.fireEvent('close', this);
786         this[this.closeAction]();
787     },
788
789     onRender: function(ct, position) {
790         var me = this,
791             topContainer;
792
793         // Add class-specific header tools.
794         // Panel adds collapsible and closable.
795         me.initTools();
796
797         // Dock the header/title
798         me.updateHeader();
799
800         // Call to super after adding the header, to prevent an unnecessary re-layout
801         me.callParent(arguments);
802     },
803
804     afterRender: function() {
805         var me = this;
806         me.callParent(arguments);
807         if (me.collapsed) {
808             me.collapsed = false;
809             me.collapse(null, false, true);
810         }
811     },
812
813     /**
814      * Create, hide, or show the header component as appropriate based on the current config.
815      * @private
816      * @param {Boolean} force True to force the header to be created
817      */
818     updateHeader: function(force) {
819         var me = this,
820             header = me.header,
821             title = me.title,
822             tools = me.tools;
823
824         if (!me.preventHeader && (force || title || (tools && tools.length))) {
825             if (!header) {
826                 header = me.header = Ext.create('Ext.panel.Header', {
827                     title       : title,
828                     orientation : (me.headerPosition == 'left' || me.headerPosition == 'right') ? 'vertical' : 'horizontal',
829                     dock        : me.headerPosition || 'top',
830                     textCls     : me.headerTextCls,
831                     iconCls     : me.iconCls,
832                     baseCls     : me.baseCls + '-header',
833                     tools       : tools,
834                     ui          : me.ui,
835                     indicateDrag: me.draggable,
836                     border      : me.border,
837                     frame       : me.frame && me.frameHeader,
838                     ignoreParentFrame : me.frame || me.overlapHeader,
839                     ignoreBorderManagement: me.frame || me.ignoreHeaderBorderManagement,
840                     listeners   : me.collapsible && me.titleCollapse ? {
841                         click: me.toggleCollapse,
842                         scope: me
843                     } : null
844                 });
845                 me.addDocked(header, 0);
846
847                 // Reference the Header's tool array.
848                 // Header injects named references.
849                 me.tools = header.tools;
850             }
851             header.show();
852             me.initHeaderAria();
853         } else if (header) {
854             header.hide();
855         }
856     },
857
858     // inherit docs
859     setUI: function(ui) {
860         var me = this;
861
862         me.callParent(arguments);
863
864         if (me.header) {
865             me.header.setUI(ui);
866         }
867     },
868
869     // private
870     getContentTarget: function() {
871         return this.body;
872     },
873
874     getTargetEl: function() {
875         return this.body || this.frameBody || this.el;
876     },
877
878     addTool: function(tool) {
879         this.tools.push(tool);
880         var header = this.header;
881         if (header) {
882             header.addTool(tool);
883         }
884         this.updateHeader();
885     },
886
887     getOppositeDirection: function(d) {
888         var c = Ext.Component;
889         switch (d) {
890             case c.DIRECTION_TOP:
891                 return c.DIRECTION_BOTTOM;
892             case c.DIRECTION_RIGHT:
893                 return c.DIRECTION_LEFT;
894             case c.DIRECTION_BOTTOM:
895                 return c.DIRECTION_TOP;
896             case c.DIRECTION_LEFT:
897                 return c.DIRECTION_RIGHT;
898         }
899     },
900
901     /**
902      * Collapses the panel body so that the body becomes hidden. Docked Components parallel to the
903      * border towards which the collapse takes place will remain visible.  Fires the {@link #beforecollapse} event which will
904      * cancel the collapse action if it returns false.
905      * @param {String} direction. The direction to collapse towards. Must be one of<ul>
906      * <li>Ext.Component.DIRECTION_TOP</li>
907      * <li>Ext.Component.DIRECTION_RIGHT</li>
908      * <li>Ext.Component.DIRECTION_BOTTOM</li>
909      * <li>Ext.Component.DIRECTION_LEFT</li></ul>
910      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
911      * {@link #animCollapse} panel config)
912      * @return {Ext.panel.Panel} this
913      */
914     collapse: function(direction, animate, /* private - passed if called at render time */ internal) {
915         var me = this,
916             c = Ext.Component,
917             height = me.getHeight(),
918             width = me.getWidth(),
919             frameInfo,
920             newSize = 0,
921             dockedItems = me.dockedItems.items,
922             dockedItemCount = dockedItems.length,
923             i = 0,
924             comp,
925             pos,
926             anim = {
927                 from: {
928                     height: height,
929                     width: width
930                 },
931                 to: {
932                     height: height,
933                     width: width
934                 },
935                 listeners: {
936                     afteranimate: me.afterCollapse,
937                     scope: me
938                 },
939                 duration: Ext.Number.from(animate, Ext.fx.Anim.prototype.duration)
940             },
941             reExpander,
942             reExpanderOrientation,
943             reExpanderDock,
944             getDimension,
945             setDimension,
946             collapseDimension;
947
948         if (!direction) {
949             direction = me.collapseDirection;
950         }
951
952         // If internal (Called because of initial collapsed state), then no animation, and no events.
953         if (internal) {
954             animate = false;
955         } else if (me.collapsed || me.fireEvent('beforecollapse', me, direction, animate) === false) {
956             return false;
957         }
958
959         reExpanderDock = direction;
960         me.expandDirection = me.getOppositeDirection(direction);
961
962         // Track docked items which we hide during collapsed state
963         me.hiddenDocked = [];
964
965         switch (direction) {
966             case c.DIRECTION_TOP:
967             case c.DIRECTION_BOTTOM:
968                 me.expandedSize = me.getHeight();
969                 reExpanderOrientation = 'horizontal';
970                 collapseDimension = 'height';
971                 getDimension = 'getHeight';
972                 setDimension = 'setHeight';
973
974                 // Collect the height of the visible header.
975                 // Hide all docked items except the header.
976                 // Hide *ALL* docked items if we're going to end up hiding the whole Panel anyway
977                 for (; i < dockedItemCount; i++) {
978                     comp = dockedItems[i];
979                     if (comp.isVisible()) {
980                         if (comp.isHeader && (!comp.dock || comp.dock == 'top' || comp.dock == 'bottom')) {
981                             reExpander = comp;
982                         } else {
983                             me.hiddenDocked.push(comp);
984                         }
985                     }
986                 }
987
988                 if (direction == Ext.Component.DIRECTION_BOTTOM) {
989                     pos = me.getPosition()[1] - Ext.fly(me.el.dom.offsetParent).getRegion().top;
990                     anim.from.top = pos;
991                 }
992                 break;
993
994             case c.DIRECTION_LEFT:
995             case c.DIRECTION_RIGHT:
996                 me.expandedSize = me.getWidth();
997                 reExpanderOrientation = 'vertical';
998                 collapseDimension = 'width';
999                 getDimension = 'getWidth';
1000                 setDimension = 'setWidth';
1001
1002                 // Collect the height of the visible header.
1003                 // Hide all docked items except the header.
1004                 // Hide *ALL* docked items if we're going to end up hiding the whole Panel anyway
1005                 for (; i < dockedItemCount; i++) {
1006                     comp = dockedItems[i];
1007                     if (comp.isVisible()) {
1008                         if (comp.isHeader && (comp.dock == 'left' || comp.dock == 'right')) {
1009                             reExpander = comp;
1010                         } else {
1011                             me.hiddenDocked.push(comp);
1012                         }
1013                     }
1014                 }
1015
1016                 if (direction == Ext.Component.DIRECTION_RIGHT) {
1017                     pos = me.getPosition()[0] - Ext.fly(me.el.dom.offsetParent).getRegion().left;
1018                     anim.from.left = pos;
1019                 }
1020                 break;
1021
1022             default:
1023                 throw('Panel collapse must be passed a valid Component collapse direction');
1024         }
1025
1026         // No scrollbars when we shrink this Panel
1027         // And no laying out of any children... we're effectively *hiding* the body
1028         me.setAutoScroll(false);
1029         me.suspendLayout = true;
1030         me.body.setVisibilityMode(Ext.core.Element.DISPLAY);
1031
1032         // Disable toggle tool during animated collapse
1033         if (animate && me.collapseTool) {
1034             me.collapseTool.disable();
1035         }
1036
1037         // Add the collapsed class now, so that collapsed CSS rules are applied before measurements are taken.
1038         me.addClsWithUI(me.collapsedCls);
1039         // if (me.border === false) {
1040         //     me.addClsWithUI(me.collapsedCls + '-noborder');
1041         // }
1042
1043         // We found a header: Measure it to find the collapse-to size.
1044         if (reExpander) {
1045             //we must add the collapsed cls to the header and then remove to get the proper height
1046             reExpander.addClsWithUI(me.collapsedCls);
1047             reExpander.addClsWithUI(me.collapsedCls + '-' + reExpander.dock);
1048             if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) {
1049                 reExpander.addClsWithUI(me.collapsedCls + '-border-' + reExpander.dock);
1050             }
1051
1052             frameInfo = reExpander.getFrameInfo();
1053
1054             //get the size
1055             newSize = reExpander[getDimension]() + (frameInfo ? frameInfo[direction] : 0);
1056
1057             //and remove
1058             reExpander.removeClsWithUI(me.collapsedCls);
1059             reExpander.removeClsWithUI(me.collapsedCls + '-' + reExpander.dock);
1060             if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) {
1061                 reExpander.removeClsWithUI(me.collapsedCls + '-border-' + reExpander.dock);
1062             }
1063         }
1064         // No header: Render and insert a temporary one, and then measure it.
1065         else {
1066             reExpander = {
1067                 hideMode: 'offsets',
1068                 temporary: true,
1069                 title: me.title,
1070                 orientation: reExpanderOrientation,
1071                 dock: reExpanderDock,
1072                 textCls: me.headerTextCls,
1073                 iconCls: me.iconCls,
1074                 baseCls: me.baseCls + '-header',
1075                 ui: me.ui,
1076                 frame: me.frame && me.frameHeader,
1077                 ignoreParentFrame: me.frame || me.overlapHeader,
1078                 indicateDrag: me.draggable,
1079                 cls: me.baseCls + '-collapsed-placeholder ' + ' ' + Ext.baseCSSPrefix + 'docked ' + me.baseCls + '-' + me.ui + '-collapsed',
1080                 renderTo: me.el
1081             };
1082             if (!me.hideCollapseTool) {
1083                 reExpander[(reExpander.orientation == 'horizontal') ? 'tools' : 'items'] = [{
1084                     xtype: 'tool',
1085                     type: 'expand-' + me.expandDirection,
1086                     handler: me.toggleCollapse,
1087                     scope: me
1088                 }];
1089             }
1090
1091             // Capture the size of the re-expander.
1092             // For vertical headers in IE6 and IE7, this will be sized by a CSS rule in _panel.scss
1093             reExpander = me.reExpander = Ext.create('Ext.panel.Header', reExpander);
1094             newSize = reExpander[getDimension]() + ((reExpander.frame) ? reExpander.frameSize[direction] : 0);
1095             reExpander.hide();
1096
1097             // Insert the new docked item
1098             me.insertDocked(0, reExpander);
1099         }
1100
1101         me.reExpander = reExpander;
1102         me.reExpander.addClsWithUI(me.collapsedCls);
1103         me.reExpander.addClsWithUI(me.collapsedCls + '-' + reExpander.dock);
1104         if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) {
1105             me.reExpander.addClsWithUI(me.collapsedCls + '-border-' + me.reExpander.dock);
1106         }
1107
1108         // If collapsing right or down, we'll be also animating the left or top.
1109         if (direction == Ext.Component.DIRECTION_RIGHT) {
1110             anim.to.left = pos + (width - newSize);
1111         } else if (direction == Ext.Component.DIRECTION_BOTTOM) {
1112             anim.to.top = pos + (height - newSize);
1113         }
1114
1115         // Animate to the new size
1116         anim.to[collapseDimension] = newSize;
1117
1118         // When we collapse a panel, the panel is in control of one dimension (depending on
1119         // collapse direction) and sets that on the component. We must restore the user's
1120         // original value (including non-existance) when we expand. Using this technique, we
1121         // mimic setCalculatedSize for the dimension we do not control and setSize for the
1122         // one we do (only while collapsed).
1123         if (!me.collapseMemento) {
1124             me.collapseMemento = new Ext.util.Memento(me);
1125         }
1126         me.collapseMemento.capture(['width', 'height', 'minWidth', 'minHeight']);
1127
1128         // Remove any flex config before we attempt to collapse.
1129         me.savedFlex = me.flex;
1130         me.minWidth = 0;
1131         me.minHeight = 0;
1132         delete me.flex;
1133
1134         if (animate) {
1135             me.animate(anim);
1136         } else {
1137             me.setSize(anim.to.width, anim.to.height);
1138             if (Ext.isDefined(anim.to.left) || Ext.isDefined(anim.to.top)) {
1139                 me.setPosition(anim.to.left, anim.to.top);
1140             }
1141             me.afterCollapse(false, internal);
1142         }
1143         return me;
1144     },
1145
1146     afterCollapse: function(animated, internal) {
1147         var me = this,
1148             i = 0,
1149             l = me.hiddenDocked.length;
1150
1151         me.collapseMemento.restore(['minWidth', 'minHeight']);
1152
1153         me.body.hide();
1154         for (; i < l; i++) {
1155             me.hiddenDocked[i].hide();
1156         }
1157         if (me.reExpander) {
1158             me.reExpander.updateFrame();
1159             me.reExpander.show();
1160         }
1161         me.collapsed = true;
1162
1163         if (!internal) {
1164             me.doComponentLayout();
1165         }
1166
1167         if (me.resizer) {
1168             me.resizer.disable();
1169         }
1170
1171         // Now we can restore the dimension we don't control to its original state
1172         if (Ext.Component.VERTICAL_DIRECTION.test(me.expandDirection)) {
1173             me.collapseMemento.restore('width');
1174         } else {
1175             me.collapseMemento.restore('height');
1176         }
1177
1178         // If me Panel was configured with a collapse tool in its header, flip it's type
1179         if (me.collapseTool) {
1180             me.collapseTool.setType('expand-' + me.expandDirection);
1181         }
1182         if (!internal) {
1183             me.fireEvent('collapse', me);
1184         }
1185
1186         // Re-enable the toggle tool after an animated collapse
1187         if (animated && me.collapseTool) {
1188             me.collapseTool.enable();
1189         }
1190     },
1191
1192     /**
1193      * Expands the panel body so that it becomes visible.  Fires the {@link #beforeexpand} event which will
1194      * cancel the expand action if it returns false.
1195      * @param {Boolean} animate True to animate the transition, else false (defaults to the value of the
1196      * {@link #animCollapse} panel config)
1197      * @return {Ext.panel.Panel} this
1198      */
1199     expand: function(animate) {
1200         var me = this;
1201         if (!me.collapsed || me.fireEvent('beforeexpand', me, animate) === false) {
1202             return false;
1203         }
1204
1205         var i = 0,
1206             l = me.hiddenDocked.length,
1207             direction = me.expandDirection,
1208             height = me.getHeight(),
1209             width = me.getWidth(),
1210             pos, anim;
1211
1212         // Disable toggle tool during animated expand
1213         if (animate && me.collapseTool) {
1214             me.collapseTool.disable();
1215         }
1216
1217         // Show any docked items that we hid on collapse
1218         // And hide the injected reExpander Header
1219         for (; i < l; i++) {
1220             me.hiddenDocked[i].hidden = false;
1221             me.hiddenDocked[i].el.show();
1222         }
1223         if (me.reExpander) {
1224             if (me.reExpander.temporary) {
1225                 me.reExpander.hide();
1226             } else {
1227                 me.reExpander.removeClsWithUI(me.collapsedCls);
1228                 me.reExpander.removeClsWithUI(me.collapsedCls + '-' + me.reExpander.dock);
1229                 if (me.border && (!me.frame || (me.frame && Ext.supports.CSS3BorderRadius))) {
1230                     me.reExpander.removeClsWithUI(me.collapsedCls + '-border-' + me.reExpander.dock);
1231                 }
1232                 me.reExpander.updateFrame();
1233             }
1234         }
1235
1236         // If me Panel was configured with a collapse tool in its header, flip it's type
1237         if (me.collapseTool) {
1238             me.collapseTool.setType('collapse-' + me.collapseDirection);
1239         }
1240
1241         // Unset the flag before the potential call to calculateChildBox to calculate our newly flexed size
1242         me.collapsed = false;
1243
1244         // Collapsed means body element was hidden
1245         me.body.show();
1246
1247         // Remove any collapsed styling before any animation begins
1248         me.removeClsWithUI(me.collapsedCls);
1249         // if (me.border === false) {
1250         //     me.removeClsWithUI(me.collapsedCls + '-noborder');
1251         // }
1252
1253         anim = {
1254             to: {
1255             },
1256             from: {
1257                 height: height,
1258                 width: width
1259             },
1260             listeners: {
1261                 afteranimate: me.afterExpand,
1262                 scope: me
1263             }
1264         };
1265
1266         if ((direction == Ext.Component.DIRECTION_TOP) || (direction == Ext.Component.DIRECTION_BOTTOM)) {
1267
1268             // If autoHeight, measure the height now we have shown the body element.
1269             if (me.autoHeight) {
1270                 me.setCalculatedSize(me.width, null);
1271                 anim.to.height = me.getHeight();
1272
1273                 // Must size back down to collapsed for the animation.
1274                 me.setCalculatedSize(me.width, anim.from.height);
1275             }
1276             // If we were flexed, then we can't just restore to the saved size.
1277             // We must restore to the currently correct, flexed size, so we much ask the Box layout what that is.
1278             else if (me.savedFlex) {
1279                 me.flex = me.savedFlex;
1280                 anim.to.height = me.ownerCt.layout.calculateChildBox(me).height;
1281                 delete me.flex;
1282             }
1283             // Else, restore to saved height
1284             else {
1285                 anim.to.height = me.expandedSize;
1286             }
1287
1288             // top needs animating upwards
1289             if (direction == Ext.Component.DIRECTION_TOP) {
1290                 pos = me.getPosition()[1] - Ext.fly(me.el.dom.offsetParent).getRegion().top;
1291                 anim.from.top = pos;
1292                 anim.to.top = pos - (anim.to.height - height);
1293             }
1294         } else if ((direction == Ext.Component.DIRECTION_LEFT) || (direction == Ext.Component.DIRECTION_RIGHT)) {
1295
1296             // If autoWidth, measure the width now we have shown the body element.
1297             if (me.autoWidth) {
1298                 me.setCalculatedSize(null, me.height);
1299                 anim.to.width = me.getWidth();
1300
1301                 // Must size back down to collapsed for the animation.
1302                 me.setCalculatedSize(anim.from.width, me.height);
1303             }
1304             // If we were flexed, then we can't just restore to the saved size.
1305             // We must restore to the currently correct, flexed size, so we much ask the Box layout what that is.
1306             else if (me.savedFlex) {
1307                 me.flex = me.savedFlex;
1308                 anim.to.width = me.ownerCt.layout.calculateChildBox(me).width;
1309                 delete me.flex;
1310             }
1311             // Else, restore to saved width
1312             else {
1313                 anim.to.width = me.expandedSize;
1314             }
1315
1316             // left needs animating leftwards
1317             if (direction == Ext.Component.DIRECTION_LEFT) {
1318                 pos = me.getPosition()[0] - Ext.fly(me.el.dom.offsetParent).getRegion().left;
1319                 anim.from.left = pos;
1320                 anim.to.left = pos - (anim.to.width - width);
1321             }
1322         }
1323
1324         if (animate) {
1325             me.animate(anim);
1326         } else {
1327             me.setCalculatedSize(anim.to.width, anim.to.height);
1328             if (anim.to.x) {
1329                 me.setLeft(anim.to.x);
1330             }
1331             if (anim.to.y) {
1332                 me.setTop(anim.to.y);
1333             }
1334             me.afterExpand(false);
1335         }
1336
1337         return me;
1338     },
1339
1340     afterExpand: function(animated) {
1341         var me = this;
1342
1343         if (me.collapseMemento) {
1344             // collapse has to use setSize (since it takes control of the component's size in
1345             // collapsed mode) and so we restore the original size now that the component has
1346             // been expanded.
1347             me.collapseMemento.restoreAll();
1348         }
1349
1350         me.setAutoScroll(me.initialConfig.autoScroll);
1351
1352         // Restored to a calculated flex. Delete the set width and height properties so that flex works from now on.
1353         if (me.savedFlex) {
1354             me.flex = me.savedFlex;
1355             delete me.savedFlex;
1356             delete me.width;
1357             delete me.height;
1358         }
1359
1360         // Reinstate layout out after Panel has re-expanded
1361         delete me.suspendLayout;
1362         if (animated && me.ownerCt) {
1363             // IE 6 has an intermittent repaint issue in this case so give
1364             // it a little extra time to catch up before laying out.
1365             Ext.defer(me.ownerCt.doLayout, Ext.isIE6 ? 1 : 0, me);
1366         }
1367
1368         if (me.resizer) {
1369             me.resizer.enable();
1370         }
1371
1372         me.fireEvent('expand', me);
1373
1374         // Re-enable the toggle tool after an animated expand
1375         if (animated && me.collapseTool) {
1376             me.collapseTool.enable();
1377         }
1378     },
1379
1380     /**
1381      * Shortcut for performing an {@link #expand} or {@link #collapse} based on the current state of the panel.
1382      * @return {Ext.panel.Panel} this
1383      */
1384     toggleCollapse: function() {
1385         if (this.collapsed) {
1386             this.expand(this.animCollapse);
1387         } else {
1388             this.collapse(this.collapseDirection, this.animCollapse);
1389         }
1390         return this;
1391     },
1392
1393     // private
1394     getKeyMap : function(){
1395         if(!this.keyMap){
1396             this.keyMap = Ext.create('Ext.util.KeyMap', this.el, this.keys);
1397         }
1398         return this.keyMap;
1399     },
1400
1401     // private
1402     initDraggable : function(){
1403         /**
1404          * <p>If this Panel is configured {@link #draggable}, this property will contain
1405          * an instance of {@link Ext.dd.DragSource} which handles dragging the Panel.</p>
1406          * The developer must provide implementations of the abstract methods of {@link Ext.dd.DragSource}
1407          * in order to supply behaviour for each stage of the drag/drop process. See {@link #draggable}.
1408          * @type Ext.dd.DragSource.
1409          * @property dd
1410          */
1411         this.dd = Ext.create('Ext.panel.DD', this, Ext.isBoolean(this.draggable) ? null : this.draggable);
1412     },
1413
1414     // private - helper function for ghost
1415     ghostTools : function() {
1416         var tools = [],
1417             origTools = this.initialConfig.tools;
1418
1419         if (origTools) {
1420             Ext.each(origTools, function(tool) {
1421                 // Some tools can be full components, and copying them into the ghost
1422                 // actually removes them from the owning panel. You could also potentially
1423                 // end up with duplicate DOM ids as well. To avoid any issues we just make
1424                 // a simple bare-minimum clone of each tool for ghosting purposes.
1425                 tools.push({
1426                     type: tool.type
1427                 });
1428             });
1429         }
1430         else {
1431             tools = [{
1432                 type: 'placeholder'
1433             }];
1434         }
1435         return tools;
1436     },
1437
1438     // private - used for dragging
1439     ghost: function(cls) {
1440         var me = this,
1441             ghostPanel = me.ghostPanel,
1442             box = me.getBox();
1443
1444         if (!ghostPanel) {
1445             ghostPanel = Ext.create('Ext.panel.Panel', {
1446                 renderTo: document.body,
1447                 floating: {
1448                     shadow: false
1449                 },
1450                 frame: Ext.supports.CSS3BorderRadius ? me.frame : false,
1451                 title: me.title,
1452                 overlapHeader: me.overlapHeader,
1453                 headerPosition: me.headerPosition,
1454                 width: me.getWidth(),
1455                 height: me.getHeight(),
1456                 iconCls: me.iconCls,
1457                 baseCls: me.baseCls,
1458                 tools: me.ghostTools(),
1459                 cls: me.baseCls + '-ghost ' + (cls ||'')
1460             });
1461             me.ghostPanel = ghostPanel;
1462         }
1463         ghostPanel.floatParent = me.floatParent;
1464         if (me.floating) {
1465             ghostPanel.setZIndex(Ext.Number.from(me.el.getStyle('zIndex'), 0));
1466         } else {
1467             ghostPanel.toFront();
1468         }
1469         ghostPanel.el.show();
1470         ghostPanel.setPosition(box.x, box.y);
1471         ghostPanel.setSize(box.width, box.height);
1472         me.el.hide();
1473         if (me.floatingItems) {
1474             me.floatingItems.hide();
1475         }
1476         return ghostPanel;
1477     },
1478
1479     // private
1480     unghost: function(show, matchPosition) {
1481         var me = this;
1482         if (!me.ghostPanel) {
1483             return;
1484         }
1485         if (show !== false) {
1486             me.el.show();
1487             if (matchPosition !== false) {
1488                 me.setPosition(me.ghostPanel.getPosition());
1489             }
1490             if (me.floatingItems) {
1491                 me.floatingItems.show();
1492             }
1493             Ext.defer(me.focus, 10, me);
1494         }
1495         me.ghostPanel.el.hide();
1496     },
1497
1498     initResizable: function(resizable) {
1499         if (this.collapsed) {
1500             resizable.disabled = true;
1501         }
1502         this.callParent([resizable]);
1503     }
1504 });
1505