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