Upgrade to ExtJS 4.0.1 - Released 05/18/2011
[extjs.git] / docs / source / Border.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4   <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5   <title>The source code</title>
6   <link href="../prettify/prettify.css" type="text/css" rel="stylesheet" />
7   <script type="text/javascript" src="../prettify/prettify.js"></script>
8   <style type="text/css">
9     .highlight { display: block; background-color: #ddd; }
10   </style>
11   <script type="text/javascript">
12     function highlight() {
13       document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
14     }
15   </script>
16 </head>
17 <body onload="prettyPrint(); highlight();">
18   <pre class="prettyprint lang-js"><span id='Ext-layout-container-Border'>/**
19 </span> * @class Ext.layout.container.Border
20  * @extends Ext.layout.container.Container
21  * &lt;p&gt;This is a multi-pane, application-oriented UI layout style that supports multiple
22  * nested panels, automatic bars between regions and built-in
23  * {@link Ext.panel.Panel#collapsible expanding and collapsing} of regions.&lt;/p&gt;
24  * &lt;p&gt;This class is intended to be extended or created via the &lt;code&gt;layout:'border'&lt;/code&gt;
25  * {@link Ext.container.Container#layout} config, and should generally not need to be created directly
26  * via the new keyword.&lt;/p&gt;
27  * {@img Ext.layout.container.Border/Ext.layout.container.Border.png Ext.layout.container.Border container layout}
28  * &lt;p&gt;Example usage:&lt;/p&gt;
29  * &lt;pre&gt;&lt;code&gt;
30      Ext.create('Ext.panel.Panel', {
31         width: 500,
32         height: 400,
33         title: 'Border Layout',
34         layout: 'border',
35         items: [{
36             title: 'South Region is resizable',
37             region: 'south',     // position for region
38             xtype: 'panel',
39             height: 100,
40             split: true,         // enable resizing
41             margins: '0 5 5 5'
42         },{
43             // xtype: 'panel' implied by default
44             title: 'West Region is collapsible',
45             region:'west',
46             xtype: 'panel',
47             margins: '5 0 0 5',
48             width: 200,
49             collapsible: true,   // make collapsible
50             id: 'west-region-container',
51             layout: 'fit'
52         },{
53             title: 'Center Region',
54             region: 'center',     // center region is required, no width/height specified
55             xtype: 'panel',
56             layout: 'fit',
57             margins: '5 5 0 0'
58         }],
59         renderTo: Ext.getBody()
60     });
61 &lt;/code&gt;&lt;/pre&gt;
62  * &lt;p&gt;&lt;b&gt;&lt;u&gt;Notes&lt;/u&gt;&lt;/b&gt;:&lt;/p&gt;&lt;div class=&quot;mdetail-params&quot;&gt;&lt;ul&gt;
63  * &lt;li&gt;Any Container using the Border layout &lt;b&gt;must&lt;/b&gt; have a child item with &lt;code&gt;region:'center'&lt;/code&gt;.
64  * The child item in the center region will always be resized to fill the remaining space not used by
65  * the other regions in the layout.&lt;/li&gt;
66  * &lt;li&gt;Any child items with a region of &lt;code&gt;west&lt;/code&gt; or &lt;code&gt;east&lt;/code&gt; may be configured with either
67  * an initial &lt;code&gt;width&lt;/code&gt;, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage width &lt;b&gt;string&lt;/b&gt; (Which is simply divided by 100 and used as a flex value). The 'center' region has a flex value of &lt;code&gt;1&lt;/code&gt;.&lt;/li&gt;
68  * &lt;li&gt;Any child items with a region of &lt;code&gt;north&lt;/code&gt; or &lt;code&gt;south&lt;/code&gt; may be configured with either
69  * an initial &lt;code&gt;height&lt;/code&gt;, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage height &lt;b&gt;string&lt;/b&gt; (Which is simply divided by 100 and used as a flex value). The 'center' region has a flex value of &lt;code&gt;1&lt;/code&gt;.&lt;/li&gt;
70  * &lt;li&gt;The regions of a BorderLayout are &lt;b&gt;fixed at render time&lt;/b&gt; and thereafter, its child Components may not be removed or added&lt;/b&gt;.To add/remove
71  * Components within a BorderLayout, have them wrapped by an additional Container which is directly
72  * managed by the BorderLayout.  If the region is to be collapsible, the Container used directly
73  * by the BorderLayout manager should be a Panel.  In the following example a Container (an Ext.panel.Panel)
74  * is added to the west region:&lt;pre&gt;&lt;code&gt;
75 wrc = {@link Ext#getCmp Ext.getCmp}('west-region-container');
76 wrc.{@link Ext.container.Container#removeAll removeAll}();
77 wrc.{@link Ext.container.Container#add add}({
78     title: 'Added Panel',
79     html: 'Some content'
80 });
81  * &lt;/code&gt;&lt;/pre&gt;
82  * &lt;/li&gt;
83  * &lt;li&gt;&lt;b&gt;There is no BorderLayout.Region class in ExtJS 4.0+&lt;/b&gt;&lt;/li&gt;
84  * &lt;/ul&gt;&lt;/div&gt;
85  */
86 Ext.define('Ext.layout.container.Border', {
87
88     alias: ['layout.border'],
89     extend: 'Ext.layout.container.Container',
90     requires: ['Ext.resizer.Splitter', 'Ext.container.Container', 'Ext.fx.Anim'],
91     alternateClassName: 'Ext.layout.BorderLayout',
92
93     targetCls: Ext.baseCSSPrefix + 'border-layout-ct',
94
95     itemCls: Ext.baseCSSPrefix + 'border-item',
96
97     bindToOwnerCtContainer: true,
98
99     fixedLayout: false,
100
101     percentageRe: /(\d+)%/,
102
103     slideDirection: {
104         north: 't',
105         south: 'b',
106         west: 'l',
107         east: 'r'
108     },
109
110     constructor: function(config) {
111         this.initialConfig = config;
112         this.callParent(arguments);
113     },
114
115     onLayout: function() {
116         var me = this;
117         if (!me.borderLayoutInitialized) {
118             me.initializeBorderLayout();
119         }
120
121         // Delegate this operation to the shadow &quot;V&quot; or &quot;H&quot; box layout, and then down to any embedded layout.
122         me.fixHeightConstraints();
123         me.shadowLayout.onLayout();
124         if (me.embeddedContainer) {
125             me.embeddedContainer.layout.onLayout();
126         }
127
128         // If the panel was originally configured with collapsed: true, it will have
129         // been initialized with a &quot;borderCollapse&quot; flag: Collapse it now before the first layout.
130         if (!me.initialCollapsedComplete) {
131             Ext.iterate(me.regions, function(name, region){
132                 if (region.borderCollapse) {
133                     me.onBeforeRegionCollapse(region, region.collapseDirection, false, 0);
134                 }
135             });
136             me.initialCollapsedComplete = true;
137         }
138     },
139
140     isValidParent : function(item, target, position) {
141         if (!this.borderLayoutInitialized) {
142             this.initializeBorderLayout();
143         }
144
145         // Delegate this operation to the shadow &quot;V&quot; or &quot;H&quot; box layout.
146         return this.shadowLayout.isValidParent(item, target, position);
147     },
148
149     beforeLayout: function() {
150         if (!this.borderLayoutInitialized) {
151             this.initializeBorderLayout();
152         }
153
154         // Delegate this operation to the shadow &quot;V&quot; or &quot;H&quot; box layout.
155         this.shadowLayout.beforeLayout();
156     },
157
158     renderItems: function(items, target) {
159         //&lt;debug&gt;
160         Ext.Error.raise('This should not be called');
161         //&lt;/debug&gt;
162     },
163
164     renderItem: function(item) {
165         //&lt;debug&gt;
166         Ext.Error.raise('This should not be called');
167         //&lt;/debug&gt;
168     },
169
170     initializeBorderLayout: function() {
171         var me = this,
172             i = 0,
173             items = me.getLayoutItems(),
174             ln = items.length,
175             regions = (me.regions = {}),
176             vBoxItems = [],
177             hBoxItems = [],
178             horizontalFlex = 0,
179             verticalFlex = 0,
180             comp, percentage;
181
182         // Map of Splitters for each region
183         me.splitters = {};
184
185         // Map of regions
186         for (; i &lt; ln; i++) {
187             comp = items[i];
188             regions[comp.region] = comp;
189
190             // Intercept collapsing to implement showing an alternate Component as a collapsed placeholder
191             if (comp.region != 'center' &amp;&amp; comp.collapsible &amp;&amp; comp.collapseMode != 'header') {
192
193                 // This layout intercepts any initial collapsed state. Panel must not do this itself.
194                 comp.borderCollapse = comp.collapsed;
195                 delete comp.collapsed;
196
197                 comp.on({
198                     beforecollapse: me.onBeforeRegionCollapse,
199                     beforeexpand: me.onBeforeRegionExpand,
200                     destroy: me.onRegionDestroy,
201                     scope: me
202                 });
203                 me.setupState(comp);
204             }
205         }
206         //&lt;debug&gt;
207         if (!regions.center) {
208             Ext.Error.raise(&quot;You must specify a center region when defining a BorderLayout.&quot;);
209         }
210         //&lt;/debug&gt;
211         comp = regions.center;
212         if (!comp.flex) {
213             comp.flex = 1;
214         }
215         delete comp.width;
216         comp.maintainFlex = true;
217
218         // Begin the VBox and HBox item list.
219         comp = regions.west;
220         if (comp) {
221             comp.collapseDirection = Ext.Component.DIRECTION_LEFT;
222             hBoxItems.push(comp);
223             if (comp.split) {
224                 hBoxItems.push(me.splitters.west = me.createSplitter(comp));
225             }
226             percentage = Ext.isString(comp.width) &amp;&amp; comp.width.match(me.percentageRe);
227             if (percentage) {
228                 horizontalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
229                 delete comp.width;
230             }
231         }
232         comp = regions.north;
233         if (comp) {
234             comp.collapseDirection = Ext.Component.DIRECTION_TOP;
235             vBoxItems.push(comp);
236             if (comp.split) {
237                 vBoxItems.push(me.splitters.north = me.createSplitter(comp));
238             }
239             percentage = Ext.isString(comp.height) &amp;&amp; comp.height.match(me.percentageRe);
240             if (percentage) {
241                 verticalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
242                 delete comp.height;
243             }
244         }
245
246         // Decide into which Collection the center region goes.
247         if (regions.north || regions.south) {
248             if (regions.east || regions.west) {
249
250                 // Create the embedded center. Mark it with the region: 'center' property so that it can be identified as the center.
251                 vBoxItems.push(me.embeddedContainer = Ext.create('Ext.container.Container', {
252                     xtype: 'container',
253                     region: 'center',
254                     id: me.owner.id + '-embedded-center',
255                     cls: Ext.baseCSSPrefix + 'border-item',
256                     flex: regions.center.flex,
257                     maintainFlex: true,
258                     layout: {
259                         type: 'hbox',
260                         align: 'stretch'
261                     }
262                 }));
263                 hBoxItems.push(regions.center);
264             }
265             // No east or west: the original center goes straight into the vbox
266             else {
267                 vBoxItems.push(regions.center);
268             }
269         }
270         // If we have no north or south, then the center is part of the HBox items
271         else {
272             hBoxItems.push(regions.center);
273         }
274
275         // Finish off the VBox and HBox item list.
276         comp = regions.south;
277         if (comp) {
278             comp.collapseDirection = Ext.Component.DIRECTION_BOTTOM;
279             if (comp.split) {
280                 vBoxItems.push(me.splitters.south = me.createSplitter(comp));
281             }
282             percentage = Ext.isString(comp.height) &amp;&amp; comp.height.match(me.percentageRe);
283             if (percentage) {
284                 verticalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
285                 delete comp.height;
286             }
287             vBoxItems.push(comp);
288         }
289         comp = regions.east;
290         if (comp) {
291             comp.collapseDirection = Ext.Component.DIRECTION_RIGHT;
292             if (comp.split) {
293                 hBoxItems.push(me.splitters.east = me.createSplitter(comp));
294             }
295             percentage = Ext.isString(comp.width) &amp;&amp; comp.width.match(me.percentageRe);
296             if (percentage) {
297                 horizontalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
298                 delete comp.width;
299             }
300             hBoxItems.push(comp);
301         }
302
303         // Create the injected &quot;items&quot; collections for the Containers.
304         // If we have north or south, then the shadow Container will be a VBox.
305         // If there are also east or west regions, its center will be a shadow HBox.
306         // If there are *only* east or west regions, then the shadow layout will be an HBox (or Fit).
307         if (regions.north || regions.south) {
308
309             me.shadowContainer = Ext.create('Ext.container.Container', {
310                 ownerCt: me.owner,
311                 el: me.getTarget(),
312                 layout: Ext.applyIf({
313                     type: 'vbox',
314                     align: 'stretch'
315                 }, me.initialConfig)
316             });
317             me.createItems(me.shadowContainer, vBoxItems);
318
319             // Allow the Splitters to orientate themselves
320             if (me.splitters.north) {
321                 me.splitters.north.ownerCt = me.shadowContainer;
322             }
323             if (me.splitters.south) {
324                 me.splitters.south.ownerCt = me.shadowContainer;
325             }
326
327             // Inject items into the HBox Container if there is one - if there was an east or west.
328             if (me.embeddedContainer) {
329                 me.embeddedContainer.ownerCt = me.shadowContainer;
330                 me.createItems(me.embeddedContainer, hBoxItems);
331
332                 // Allow the Splitters to orientate themselves
333                 if (me.splitters.east) {
334                     me.splitters.east.ownerCt = me.embeddedContainer;
335                 }
336                 if (me.splitters.west) {
337                     me.splitters.west.ownerCt = me.embeddedContainer;
338                 }
339
340                 // These spliiters need to be constrained by components one-level below
341                 // the component in their vobx. We update the min/maxHeight on the helper
342                 // (embeddedContainer) prior to starting the split/drag. This has to be
343                 // done on-the-fly to allow min/maxHeight of the E/C/W regions to be set
344                 // dynamically.
345                 Ext.each([me.splitters.north, me.splitters.south], function (splitter) {
346                     if (splitter) {
347                         splitter.on('beforedragstart', me.fixHeightConstraints, me);
348                     }
349                 });
350
351                 // The east or west region wanted a percentage
352                 if (horizontalFlex) {
353                     regions.center.flex -= horizontalFlex;
354                 }
355                 // The north or south region wanted a percentage
356                 if (verticalFlex) {
357                     me.embeddedContainer.flex -= verticalFlex;
358                 }
359             } else {
360                 // The north or south region wanted a percentage
361                 if (verticalFlex) {
362                     regions.center.flex -= verticalFlex;
363                 }
364             }
365         }
366         // If we have no north or south, then there's only one Container, and it's
367         // an HBox, or, if only a center region was specified, a Fit.
368         else {
369             me.shadowContainer = Ext.create('Ext.container.Container', {
370                 ownerCt: me.owner,
371                 el: me.getTarget(),
372                 layout: Ext.applyIf({
373                     type: (hBoxItems.length == 1) ? 'fit' : 'hbox',
374                     align: 'stretch'
375                 }, me.initialConfig)
376             });
377             me.createItems(me.shadowContainer, hBoxItems);
378
379             // Allow the Splitters to orientate themselves
380             if (me.splitters.east) {
381                 me.splitters.east.ownerCt = me.shadowContainer;
382             }
383             if (me.splitters.west) {
384                 me.splitters.west.ownerCt = me.shadowContainer;
385             }
386
387             // The east or west region wanted a percentage
388             if (horizontalFlex) {
389                 regions.center.flex -= verticalFlex;
390             }
391         }
392
393         // Create upward links from the region Components to their shadow ownerCts
394         for (i = 0, items = me.shadowContainer.items.items, ln = items.length; i &lt; ln; i++) {
395             items[i].shadowOwnerCt = me.shadowContainer;
396         }
397         if (me.embeddedContainer) {
398             for (i = 0, items = me.embeddedContainer.items.items, ln = items.length; i &lt; ln; i++) {
399                 items[i].shadowOwnerCt = me.embeddedContainer;
400             }
401         }
402
403         // This is the layout that we delegate all operations to
404         me.shadowLayout = me.shadowContainer.getLayout();
405
406         me.borderLayoutInitialized = true;
407     },
408
409     setupState: function(comp){
410         var getState = comp.getState;
411         comp.getState = function(){
412             // call the original getState
413             var state = getState.call(comp) || {},
414                 region = comp.region;
415
416             state.collapsed = !!comp.collapsed;
417             if (region == 'west' || region == 'east') {
418                 state.width = comp.getWidth();
419             } else {
420                 state.height = comp.getHeight();
421             }
422             return state;
423         };
424         comp.addStateEvents(['collapse', 'expand', 'resize']);
425     },
426
427 <span id='Ext-layout-container-Border-method-createItems'>    /**
428 </span>     * Create the items collection for our shadow/embedded containers
429      * @private
430      */
431     createItems: function(container, items){
432         // Have to inject an items Collection *after* construction.
433         // The child items of the shadow layout must retain their original, user-defined ownerCt
434         delete container.items;
435         container.initItems();
436         container.items.addAll(items);
437     },
438
439     // Private
440     // Create a splitter for a child of the layout.
441     createSplitter: function(comp) {
442         var me = this,
443             interceptCollapse = (comp.collapseMode != 'header'),
444             resizer;
445
446         resizer = Ext.create('Ext.resizer.Splitter', {
447             hidden: !!comp.hidden,
448             collapseTarget: comp,
449             performCollapse: !interceptCollapse,
450             listeners: interceptCollapse ? {
451                 click: {
452                     fn: Ext.Function.bind(me.onSplitterCollapseClick, me, [comp]),
453                     element: 'collapseEl'
454                 }
455             } : null
456         });
457
458         // Mini collapse means that the splitter is the placeholder Component
459         if (comp.collapseMode == 'mini') {
460             comp.placeholder = resizer;
461             resizer.collapsedCls = comp.collapsedCls;
462         }
463
464         // Arrange to hide/show a region's associated splitter when the region is hidden/shown
465         comp.on({
466             hide: me.onRegionVisibilityChange,
467             show: me.onRegionVisibilityChange,
468             scope: me
469         });
470         return resizer;
471     },
472
473     // Private
474     // Propogates the min/maxHeight values from the inner hbox items to its container.
475     fixHeightConstraints: function () {
476         var me = this,
477             ct = me.embeddedContainer,
478             maxHeight = 1e99, minHeight = -1;
479
480         if (!ct) {
481             return;
482         }
483
484         ct.items.each(function (item) {
485             if (Ext.isNumber(item.maxHeight)) {
486                 maxHeight = Math.max(maxHeight, item.maxHeight);
487             }
488             if (Ext.isNumber(item.minHeight)) {
489                 minHeight = Math.max(minHeight, item.minHeight);
490             }
491         });
492
493         ct.maxHeight = maxHeight;
494         ct.minHeight = minHeight;
495     },
496
497     // Hide/show a region's associated splitter when the region is hidden/shown
498     onRegionVisibilityChange: function(comp){
499         this.splitters[comp.region][comp.hidden ? 'hide' : 'show']();
500         this.layout();
501     },
502
503     // Called when a splitter mini-collapse tool is clicked on.
504     // The listener is only added if this layout is controlling collapsing,
505     // not if the component's collapseMode is 'mini' or 'header'.
506     onSplitterCollapseClick: function(comp) {
507         if (comp.collapsed) {
508             this.onPlaceHolderToolClick(null, null, null, {client: comp});
509         } else {
510             comp.collapse();
511         }
512     },
513
514 <span id='Ext-layout-container-Border-method-getPlaceholder'>    /**
515 </span>     * &lt;p&gt;Return the {@link Ext.panel.Panel#placeholder placeholder} Component to which the passed child Panel of the layout will collapse.
516      * By default, this will be a {@link Ext.panel.Header Header} component (Docked to the appropriate border). See {@link Ext.panel.Panel#placeholder placeholder}.
517      * config to customize this.&lt;/p&gt;
518      * &lt;p&gt;&lt;b&gt;Note that this will be a fully instantiated Component, but will only be &lt;i&gt;rendered&lt;/i&gt; when the Panel is first collapsed.&lt;/b&gt;&lt;/p&gt;
519      * @param {Panel} panel The child Panel of the layout for which to return the {@link Ext.panel.Panel#placeholder placeholder}.
520      * @returns {Component} The Panel's {@link Ext.panel.Panel#placeholder placeholder} unless the {@link Ext.panel.Panel#collapseMode collapseMode} is
521      * &lt;code&gt;'header'&lt;/code&gt;, in which case &lt;i&gt;undefined&lt;/i&gt; is returned.
522      */
523     getPlaceholder: function(comp) {
524         var me = this,
525             placeholder = comp.placeholder,
526             shadowContainer = comp.shadowOwnerCt,
527             shadowLayout = shadowContainer.layout,
528             oppositeDirection = Ext.panel.Panel.prototype.getOppositeDirection(comp.collapseDirection),
529             horiz = (comp.region == 'north' || comp.region == 'south');
530
531         // No placeholder if the collapse mode is not the Border layout default
532         if (comp.collapseMode == 'header') {
533             return;
534         }
535
536         // Provide a replacement Container with an expand tool
537         if (!placeholder) {
538             if (comp.collapseMode == 'mini') {
539                 placeholder = Ext.create('Ext.resizer.Splitter', {
540                     id: 'collapse-placeholder-' + comp.id,
541                     collapseTarget: comp,
542                     performCollapse: false,
543                     listeners: {
544                         click: {
545                             fn: Ext.Function.bind(me.onSplitterCollapseClick, me, [comp]),
546                             element: 'collapseEl'
547                         }
548                     }
549                 });
550                 placeholder.addCls(placeholder.collapsedCls);
551             } else {
552                 placeholder = {
553                     id: 'collapse-placeholder-' + comp.id,
554                     margins: comp.initialConfig.margins || Ext.getClass(comp).prototype.margins,
555                     xtype: 'header',
556                     orientation: horiz ? 'horizontal' : 'vertical',
557                     title: comp.title,
558                     textCls: comp.headerTextCls,
559                     iconCls: comp.iconCls,
560                     baseCls: comp.baseCls + '-header',
561                     ui: comp.ui,
562                     indicateDrag: comp.draggable,
563                     cls: Ext.baseCSSPrefix + 'region-collapsed-placeholder ' + Ext.baseCSSPrefix + 'region-collapsed-' + comp.collapseDirection + '-placeholder ' + comp.collapsedCls,
564                     listeners: comp.floatable ? {
565                         click: {
566                             fn: function(e) {
567                                 me.floatCollapsedPanel(e, comp);
568                             },
569                             element: 'el'
570                         }
571                     } : null
572                 };
573                 // Hack for IE6/7/IEQuirks's inability to display an inline-block
574                 if ((Ext.isIE6 || Ext.isIE7 || (Ext.isIEQuirks)) &amp;&amp; !horiz) {
575                     placeholder.width = 25;
576                 }
577                 if (!comp.hideCollapseTool) {
578                     placeholder[horiz ? 'tools' : 'items'] = [{
579                         xtype: 'tool',
580                         client: comp,
581                         type: 'expand-' + oppositeDirection,
582                         handler: me.onPlaceHolderToolClick,
583                         scope: me
584                     }];
585                 }
586             }
587             placeholder = me.owner.createComponent(placeholder);
588             if (comp.isXType('panel')) {
589                 comp.on({
590                     titlechange: me.onRegionTitleChange,
591                     iconchange: me.onRegionIconChange,
592                     scope: me
593                 });
594             }
595         }
596
597         // The collapsed Component holds a reference to its placeholder and vice versa
598         comp.placeholder = placeholder;
599         placeholder.comp = comp;
600
601         return placeholder;
602     },
603
604 <span id='Ext-layout-container-Border-method-onRegionTitleChange'>    /**
605 </span>     * @private
606      * Update the placeholder title when panel title has been set or changed.
607      */
608     onRegionTitleChange: function(comp, newTitle) {
609         comp.placeholder.setTitle(newTitle);
610     },
611
612 <span id='Ext-layout-container-Border-method-onRegionIconChange'>    /**
613 </span>     * @private
614      * Update the placeholder iconCls when panel iconCls has been set or changed.
615      */
616     onRegionIconChange: function(comp, newIconCls) {
617         comp.placeholder.setIconCls(newIconCls);
618     },
619
620 <span id='Ext-layout-container-Border-method-calculateChildBox'>    /**
621 </span>     * @private
622      * Calculates the size and positioning of the passed child item. Must be present because Panel's expand,
623      * when configured with a flex, calls this method on its ownerCt's layout.
624      * @param {Component} child The child Component to calculate the box for
625      * @return {Object} Object containing box measurements for the child. Properties are left,top,width,height.
626      */
627     calculateChildBox: function(comp) {
628         var me = this;
629         if (me.shadowContainer.items.contains(comp)) {
630             return me.shadowContainer.layout.calculateChildBox(comp);
631         }
632         else if (me.embeddedContainer &amp;&amp; me.embeddedContainer.items.contains(comp)) {
633             return me.embeddedContainer.layout.calculateChildBox(comp);
634         }
635     },
636
637 <span id='Ext-layout-container-Border-method-onBeforeRegionCollapse'>    /**
638 </span>     * @private
639      * Intercepts the Panel's own collapse event and perform's substitution of the Panel
640      * with a placeholder Header orientated in the appropriate dimension.
641      * @param comp The Panel being collapsed.
642      * @param direction
643      * @param animate
644      * @returns {Boolean} false to inhibit the Panel from performing its own collapse.
645      */
646     onBeforeRegionCollapse: function(comp, direction, animate) {
647         var me = this,
648             compEl = comp.el,
649             width,
650             miniCollapse = comp.collapseMode == 'mini',
651             shadowContainer = comp.shadowOwnerCt,
652             shadowLayout = shadowContainer.layout,
653             placeholder = comp.placeholder,
654             sl = me.owner.suspendLayout,
655             scsl = shadowContainer.suspendLayout,
656             isNorthOrWest = (comp.region == 'north' || comp.region == 'west'); // Flag to keep the placeholder non-adjacent to any Splitter
657
658         // Do not trigger a layout during transition to collapsed Component
659         me.owner.suspendLayout = true;
660         shadowContainer.suspendLayout = true;
661
662         // Prevent upward notifications from downstream layouts
663         shadowLayout.layoutBusy = true;
664         if (shadowContainer.componentLayout) {
665             shadowContainer.componentLayout.layoutBusy = true;
666         }
667         me.shadowContainer.layout.layoutBusy = true;
668         me.layoutBusy = true;
669         me.owner.componentLayout.layoutBusy = true;
670
671         // Provide a replacement Container with an expand tool
672         if (!placeholder) {
673             placeholder = me.getPlaceholder(comp);
674         }
675
676         // placeholder already in place; show it.
677         if (placeholder.shadowOwnerCt === shadowContainer) {
678             placeholder.show();
679         }
680         // Insert the collapsed placeholder Component into the appropriate Box layout shadow Container
681         // It must go next to its client Component, but non-adjacent to the splitter so splitter can find its collapse client.
682         // Inject an ownerCt value pointing to the owner, border layout Container as the user will expect.
683         else {
684             shadowContainer.insert(shadowContainer.items.indexOf(comp) + (isNorthOrWest ? 0 : 1), placeholder);
685             placeholder.shadowOwnerCt = shadowContainer;
686             placeholder.ownerCt = me.owner;
687         }
688
689         // Flag the collapsing Component as hidden and show the placeholder.
690         // This causes the shadow Box layout's calculateChildBoxes to calculate the correct new arrangement.
691         // We hide or slideOut the Component's element
692         comp.hidden = true;
693
694         if (!placeholder.rendered) {
695             shadowLayout.renderItem(placeholder, shadowLayout.innerCt);
696
697             // The inserted placeholder does not have the proper size, so copy the width
698             // for N/S or the height for E/W from the component. This fixes EXTJSIV-1562
699             // without recursive layouts. This is only an issue initially. After this time,
700             // placeholder will have the correct width/height set by the layout (which has
701             // already happened when we get here initially).
702             if (comp.region == 'north' || comp.region == 'south') {
703                 placeholder.setCalculatedSize(comp.getWidth());
704             } else {
705                 placeholder.setCalculatedSize(undefined, comp.getHeight());
706             }
707         }
708
709         // Jobs to be done after the collapse has been done
710         function afterCollapse() {
711             // Reinstate automatic laying out.
712             me.owner.suspendLayout = sl;
713             shadowContainer.suspendLayout = scsl;
714             delete shadowLayout.layoutBusy;
715             if (shadowContainer.componentLayout) {
716                 delete shadowContainer.componentLayout.layoutBusy;
717             }
718             delete me.shadowContainer.layout.layoutBusy;
719             delete me.layoutBusy;
720             delete me.owner.componentLayout.layoutBusy;
721
722             // Fire the collapse event: The Panel has in fact been collapsed, but by substitution of an alternative Component
723             comp.collapsed = true;
724             comp.fireEvent('collapse', comp);
725         }
726
727         /*
728          * Set everything to the new positions. Note that we
729          * only want to animate the collapse if it wasn't configured
730          * initially with collapsed: true
731          */
732         if (comp.animCollapse &amp;&amp; me.initialCollapsedComplete) {
733             shadowLayout.layout();
734             compEl.dom.style.zIndex = 100;
735
736             // If we're mini-collapsing, the placholder is a Splitter. We don't want it to &quot;bounce in&quot;
737             if (!miniCollapse) {
738                 placeholder.el.hide();
739             }
740             compEl.slideOut(me.slideDirection[comp.region], {
741                 duration: Ext.Number.from(comp.animCollapse, Ext.fx.Anim.prototype.duration),
742                 listeners: {
743                     afteranimate: function() {
744                         compEl.show().setLeftTop(-10000, -10000);
745                         compEl.dom.style.zIndex = '';
746
747                         // If we're mini-collapsing, the placholder is a Splitter. We don't want it to &quot;bounce in&quot;
748                        if (!miniCollapse) {
749                             placeholder.el.slideIn(me.slideDirection[comp.region], {
750                                 easing: 'linear',
751                                 duration: 100
752                             });
753                         }
754                         afterCollapse();
755                     }
756                 }
757             });
758         } else {
759             compEl.setLeftTop(-10000, -10000);
760             shadowLayout.layout();
761             afterCollapse();
762         }
763
764         return false;
765     },
766
767     // Hijack the expand operation to remove the placeholder and slide the region back in.
768     onBeforeRegionExpand: function(comp, animate) {
769         this.onPlaceHolderToolClick(null, null, null, {client: comp});
770         return false;
771     },
772
773     // Called when the collapsed placeholder is clicked to reinstate a &quot;collapsed&quot; (in reality hidden) Panel.
774     onPlaceHolderToolClick: function(e, target, owner, tool) {
775         var me = this,
776             comp = tool.client,
777
778             // Hide the placeholder unless it was the Component's preexisting splitter
779             hidePlaceholder = (comp.collapseMode != 'mini') || !comp.split,
780             compEl = comp.el,
781             toCompBox,
782             placeholder = comp.placeholder,
783             placeholderEl = placeholder.el,
784             shadowContainer = comp.shadowOwnerCt,
785             shadowLayout = shadowContainer.layout,
786             curSize,
787             sl = me.owner.suspendLayout,
788             scsl = shadowContainer.suspendLayout,
789             isFloating;
790
791         // If the slide in is still going, stop it.
792         // This will either leave the Component in its fully floated state (which is processed below)
793         // or in its collapsed state. Either way, we expand it..
794         if (comp.getActiveAnimation()) {
795             comp.stopAnimation();
796         }
797
798         // If the Component is fully floated when they click the placeholder Tool,
799         // it will be primed with a slide out animation object... so delete that
800         // and remove the mouseout listeners
801         if (comp.slideOutAnim) {
802             // Remove mouse leave monitors
803             compEl.un(comp.panelMouseMon);
804             placeholderEl.un(comp.placeholderMouseMon);
805
806             delete comp.slideOutAnim;
807             delete comp.panelMouseMon;
808             delete comp.placeholderMouseMon;
809
810             // If the Panel was floated and primed with a slideOut animation, we don't want to animate its layout operation.
811             isFloating = true;
812         }
813
814         // Do not trigger a layout during transition to expanded Component
815         me.owner.suspendLayout = true;
816         shadowContainer.suspendLayout = true;
817
818         // Prevent upward notifications from downstream layouts
819         shadowLayout.layoutBusy = true;
820         if (shadowContainer.componentLayout) {
821             shadowContainer.componentLayout.layoutBusy = true;
822         }
823         me.shadowContainer.layout.layoutBusy = true;
824         me.layoutBusy = true;
825         me.owner.componentLayout.layoutBusy = true;
826
827         // Unset the hidden and collapsed flags set in onBeforeRegionCollapse. The shadowLayout will now take it into account
828         // Find where the shadow Box layout plans to put the expanding Component.
829         comp.hidden = false;
830         comp.collapsed = false;
831         if (hidePlaceholder) {
832             placeholder.hidden = true;
833         }
834         toCompBox = shadowLayout.calculateChildBox(comp);
835
836         // Show the collapse tool in case it was hidden by the slide-in
837         if (comp.collapseTool) {
838             comp.collapseTool.show();
839         }
840
841         // If we're going to animate, we need to hide the component before moving it back into position
842         if (comp.animCollapse &amp;&amp; !isFloating) {
843             compEl.setStyle('visibility', 'hidden');
844         }
845         compEl.setLeftTop(toCompBox.left, toCompBox.top);
846
847         // Equalize the size of the expanding Component prior to animation
848         // in case the layout area has changed size during the time it was collapsed.
849         curSize = comp.getSize();
850         if (curSize.height != toCompBox.height || curSize.width != toCompBox.width) {
851             me.setItemSize(comp, toCompBox.width, toCompBox.height);
852         }
853
854         // Jobs to be done after the expand has been done
855         function afterExpand() {
856             // Reinstate automatic laying out.
857             me.owner.suspendLayout = sl;
858             shadowContainer.suspendLayout = scsl;
859             delete shadowLayout.layoutBusy;
860             if (shadowContainer.componentLayout) {
861                 delete shadowContainer.componentLayout.layoutBusy;
862             }
863             delete me.shadowContainer.layout.layoutBusy;
864             delete me.layoutBusy;
865             delete me.owner.componentLayout.layoutBusy;
866
867             // In case it was floated out and they clicked the re-expand tool
868             comp.removeCls(Ext.baseCSSPrefix + 'border-region-slide-in');
869
870             // Fire the expand event: The Panel has in fact been expanded, but by removal of an alternative Component
871             comp.fireEvent('expand', comp);
872         }
873
874         // Hide the placeholder
875         if (hidePlaceholder) {
876             placeholder.el.hide();
877         }
878
879         // Slide the expanding Component to its new position.
880         // When that is done, layout the layout.
881         if (comp.animCollapse &amp;&amp; !isFloating) {
882             compEl.dom.style.zIndex = 100;
883             compEl.slideIn(me.slideDirection[comp.region], {
884                 duration: Ext.Number.from(comp.animCollapse, Ext.fx.Anim.prototype.duration),
885                 listeners: {
886                     afteranimate: function() {
887                         compEl.dom.style.zIndex = '';
888                         comp.hidden = false;
889                         shadowLayout.onLayout();
890                         afterExpand();
891                     }
892                 }
893             });
894         } else {
895             shadowLayout.onLayout();
896             afterExpand();
897         }
898     },
899
900     floatCollapsedPanel: function(e, comp) {
901
902         if (comp.floatable === false) {
903             return;
904         }
905
906         var me = this,
907             compEl = comp.el,
908             placeholder = comp.placeholder,
909             placeholderEl = placeholder.el,
910             shadowContainer = comp.shadowOwnerCt,
911             shadowLayout = shadowContainer.layout,
912             placeholderBox = shadowLayout.getChildBox(placeholder),
913             scsl = shadowContainer.suspendLayout,
914             curSize, toCompBox, compAnim;
915
916         // Ignore clicks on tools.
917         if (e.getTarget('.' + Ext.baseCSSPrefix + 'tool')) {
918             return;
919         }
920
921         // It's *being* animated, ignore the click.
922         // Possible future enhancement: Stop and *reverse* the current active Fx.
923         if (compEl.getActiveAnimation()) {
924             return;
925         }
926
927         // If the Component is already fully floated when they click the placeholder,
928         // it will be primed with a slide out animation object... so slide it out.
929         if (comp.slideOutAnim) {
930             me.slideOutFloatedComponent(comp);
931             return;
932         }
933
934         // Function to be called when the mouse leaves the floated Panel
935         // Slide out when the mouse leaves the region bounded by the slid Component and its placeholder.
936         function onMouseLeaveFloated(e) {
937             var slideRegion = compEl.getRegion().union(placeholderEl.getRegion()).adjust(1, -1, -1, 1);
938
939             // If mouse is not within slide Region, slide it out
940             if (!slideRegion.contains(e.getPoint())) {
941                 me.slideOutFloatedComponent(comp);
942             }
943         }
944
945         // Monitor for mouseouting of the placeholder. Hide it if they exit for half a second or more
946         comp.placeholderMouseMon = placeholderEl.monitorMouseLeave(500, onMouseLeaveFloated);
947
948         // Do not trigger a layout during slide out of the Component
949         shadowContainer.suspendLayout = true;
950
951         // Prevent upward notifications from downstream layouts
952         me.layoutBusy = true;
953         me.owner.componentLayout.layoutBusy = true;
954
955         // The collapse tool is hidden while slid.
956         // It is re-shown on expand.
957         if (comp.collapseTool) {
958             comp.collapseTool.hide();
959         }
960
961         // Set flags so that the layout will calculate the boxes for what we want
962         comp.hidden = false;
963         comp.collapsed = false;
964         placeholder.hidden = true;
965
966         // Recalculate new arrangement of the Component being floated.
967         toCompBox = shadowLayout.calculateChildBox(comp);
968         placeholder.hidden = false;
969
970         // Component to appear just after the placeholder, whatever &quot;after&quot; means in the context of the shadow Box layout.
971         if (comp.region == 'north' || comp.region == 'west') {
972             toCompBox[shadowLayout.parallelBefore] += placeholderBox[shadowLayout.parallelPrefix] - 1;
973         } else {
974             toCompBox[shadowLayout.parallelBefore] -= (placeholderBox[shadowLayout.parallelPrefix] - 1);
975         }
976         compEl.setStyle('visibility', 'hidden');
977         compEl.setLeftTop(toCompBox.left, toCompBox.top);
978
979         // Equalize the size of the expanding Component prior to animation
980         // in case the layout area has changed size during the time it was collapsed.
981         curSize = comp.getSize();
982         if (curSize.height != toCompBox.height || curSize.width != toCompBox.width) {
983             me.setItemSize(comp, toCompBox.width, toCompBox.height);
984         }
985
986         // This animation slides the collapsed Component's el out to just beyond its placeholder
987         compAnim = {
988             listeners: {
989                 afteranimate: function() {
990                     shadowContainer.suspendLayout = scsl;
991                     delete me.layoutBusy;
992                     delete me.owner.componentLayout.layoutBusy;
993
994                     // Prime the Component with an Anim config object to slide it back out
995                     compAnim.listeners = {
996                         afterAnimate: function() {
997                             compEl.show().removeCls(Ext.baseCSSPrefix + 'border-region-slide-in').setLeftTop(-10000, -10000);
998
999                             // Reinstate the correct, current state after slide out animation finishes
1000                             comp.hidden = true;
1001                             comp.collapsed = true;
1002                             delete comp.slideOutAnim;
1003                             delete comp.panelMouseMon;
1004                             delete comp.placeholderMouseMon;
1005                         }
1006                     };
1007                     comp.slideOutAnim = compAnim;
1008                 }
1009             },
1010             duration: 500
1011         };
1012
1013         // Give the element the correct class which places it at a high z-index
1014         compEl.addCls(Ext.baseCSSPrefix + 'border-region-slide-in');
1015
1016         // Begin the slide in
1017         compEl.slideIn(me.slideDirection[comp.region], compAnim);
1018
1019         // Monitor for mouseouting of the slid area. Hide it if they exit for half a second or more
1020         comp.panelMouseMon = compEl.monitorMouseLeave(500, onMouseLeaveFloated);
1021
1022     },
1023
1024     slideOutFloatedComponent: function(comp) {
1025         var compEl = comp.el,
1026             slideOutAnim;
1027
1028         // Remove mouse leave monitors
1029         compEl.un(comp.panelMouseMon);
1030         comp.placeholder.el.un(comp.placeholderMouseMon);
1031
1032         // Slide the Component out
1033         compEl.slideOut(this.slideDirection[comp.region], comp.slideOutAnim);
1034
1035         delete comp.slideOutAnim;
1036         delete comp.panelMouseMon;
1037         delete comp.placeholderMouseMon;
1038     },
1039
1040     /*
1041      * @private
1042      * Ensure any collapsed placeholder Component is destroyed along with its region.
1043      * Can't do this in onDestroy because they may remove a Component and use it elsewhere.
1044      */
1045     onRegionDestroy: function(comp) {
1046         var placeholder = comp.placeholder;
1047         if (placeholder) {
1048             delete placeholder.ownerCt;
1049             placeholder.destroy();
1050         }
1051     },
1052
1053     /*
1054      * @private
1055      * Ensure any shadow Containers are destroyed.
1056      * Ensure we don't keep references to Components.
1057      */
1058     onDestroy: function() {
1059         var me = this,
1060             shadowContainer = me.shadowContainer,
1061             embeddedContainer = me.embeddedContainer;
1062
1063         if (shadowContainer) {
1064             delete shadowContainer.ownerCt;
1065             Ext.destroy(shadowContainer);
1066         }
1067
1068         if (embeddedContainer) {
1069             delete embeddedContainer.ownerCt;
1070             Ext.destroy(embeddedContainer);
1071         }
1072         delete me.regions;
1073         delete me.splitters;
1074         delete me.shadowContainer;
1075         delete me.embeddedContainer;
1076         me.callParent(arguments);
1077     }
1078 });
1079 </pre>
1080 </body>
1081 </html>