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