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