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; }
11 <script type="text/javascript">
12 function highlight() {
13 document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
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 * <p>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.</p>
24 * <p>This class is intended to be extended or created via the <code>layout:'border'</code>
25 * {@link Ext.container.Container#layout} config, and should generally not need to be created directly
26 * via the new keyword.</p>
27 * {@img Ext.layout.container.Border/Ext.layout.container.Border.png Ext.layout.container.Border container layout}
28 * <p>Example usage:</p>
29 * <pre><code>
30 Ext.create('Ext.panel.Panel', {
33 title: 'Border Layout',
36 title: 'South Region is resizable',
37 region: 'south', // position for region
40 split: true, // enable resizing
43 // xtype: 'panel' implied by default
44 title: 'West Region is collapsible',
49 collapsible: true, // make collapsible
50 id: 'west-region-container',
53 title: 'Center Region',
54 region: 'center', // center region is required, no width/height specified
59 renderTo: Ext.getBody()
61 </code></pre>
62 * <p><b><u>Notes</u></b>:</p><div class="mdetail-params"><ul>
63 * <li>Any Container using the Border layout <b>must</b> have a child item with <code>region:'center'</code>.
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.</li>
66 * <li>Any child items with a region of <code>west</code> or <code>east</code> may be configured with either
67 * an initial <code>width</code>, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage width <b>string</b> (Which is simply divided by 100 and used as a flex value). The 'center' region has a flex value of <code>1</code>.</li>
68 * <li>Any child items with a region of <code>north</code> or <code>south</code> may be configured with either
69 * an initial <code>height</code>, or a {@link Ext.layout.container.Box#flex} value, or an initial percentage height <b>string</b> (Which is simply divided by 100 and used as a flex value). The 'center' region has a flex value of <code>1</code>.</li>
70 * <li>The regions of a BorderLayout are <b>fixed at render time</b> and thereafter, its child Components may not be removed or added</b>.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:<pre><code>
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}({
81 * </code></pre>
83 * <li><b>There is no BorderLayout.Region class in ExtJS 4.0+</b></li>
84 * </ul></div>
86 Ext.define('Ext.layout.container.Border', {
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',
93 targetCls: Ext.baseCSSPrefix + 'border-layout-ct',
95 itemCls: Ext.baseCSSPrefix + 'border-item',
97 bindToOwnerCtContainer: true,
99 percentageRe: /(\d+)%/,
108 constructor: function(config) {
109 this.initialConfig = config;
110 this.callParent(arguments);
113 onLayout: function() {
115 if (!me.borderLayoutInitialized) {
116 me.initializeBorderLayout();
119 // Delegate this operation to the shadow "V" or "H" 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();
126 // If the panel was originally configured with collapsed: true, it will have
127 // been initialized with a "borderCollapse" 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);
134 me.initialCollapsedComplete = true;
138 isValidParent : function(item, target, position) {
139 if (!this.borderLayoutInitialized) {
140 this.initializeBorderLayout();
143 // Delegate this operation to the shadow "V" or "H" box layout.
144 return this.shadowLayout.isValidParent(item, target, position);
147 beforeLayout: function() {
148 if (!this.borderLayoutInitialized) {
149 this.initializeBorderLayout();
152 // Delegate this operation to the shadow "V" or "H" box layout.
153 this.shadowLayout.beforeLayout();
156 renderItems: function(items, target) {
158 Ext.Error.raise('This should not be called');
162 renderItem: function(item) {
164 Ext.Error.raise('This should not be called');
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)
172 getVisibleItems: function() {
173 return Ext.ComponentQuery.query(':not([slideOutAnim])', this.callParent(arguments));
176 initializeBorderLayout: function() {
179 items = me.getLayoutItems(),
181 regions = (me.regions = {}),
188 // Map of Splitters for each region
192 for (; i < ln; i++) {
194 regions[comp.region] = comp;
196 // Intercept collapsing to implement showing an alternate Component as a collapsed placeholder
197 if (comp.region != 'center' && comp.collapsible && comp.collapseMode != 'header') {
199 // This layout intercepts any initial collapsed state. Panel must not do this itself.
200 comp.borderCollapse = comp.collapsed;
201 delete comp.collapsed;
204 beforecollapse: me.onBeforeRegionCollapse,
205 beforeexpand: me.onBeforeRegionExpand,
206 destroy: me.onRegionDestroy,
213 if (!regions.center) {
214 Ext.Error.raise("You must specify a center region when defining a BorderLayout.");
217 comp = regions.center;
222 comp.maintainFlex = true;
224 // Begin the VBox and HBox item list.
227 comp.collapseDirection = Ext.Component.DIRECTION_LEFT;
228 hBoxItems.push(comp);
230 hBoxItems.push(me.splitters.west = me.createSplitter(comp));
232 percentage = Ext.isString(comp.width) && comp.width.match(me.percentageRe);
234 horizontalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
238 comp = regions.north;
240 comp.collapseDirection = Ext.Component.DIRECTION_TOP;
241 vBoxItems.push(comp);
243 vBoxItems.push(me.splitters.north = me.createSplitter(comp));
245 percentage = Ext.isString(comp.height) && comp.height.match(me.percentageRe);
247 verticalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
252 // Decide into which Collection the center region goes.
253 if (regions.north || regions.south) {
254 if (regions.east || regions.west) {
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', {
260 id: me.owner.id + '-embedded-center',
261 cls: Ext.baseCSSPrefix + 'border-item',
262 flex: regions.center.flex,
267 getVisibleItems: me.getVisibleItems
270 hBoxItems.push(regions.center);
272 // No east or west: the original center goes straight into the vbox
274 vBoxItems.push(regions.center);
277 // If we have no north or south, then the center is part of the HBox items
279 hBoxItems.push(regions.center);
282 // Finish off the VBox and HBox item list.
283 comp = regions.south;
285 comp.collapseDirection = Ext.Component.DIRECTION_BOTTOM;
287 vBoxItems.push(me.splitters.south = me.createSplitter(comp));
289 percentage = Ext.isString(comp.height) && comp.height.match(me.percentageRe);
291 verticalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
294 vBoxItems.push(comp);
298 comp.collapseDirection = Ext.Component.DIRECTION_RIGHT;
300 hBoxItems.push(me.splitters.east = me.createSplitter(comp));
302 percentage = Ext.isString(comp.width) && comp.width.match(me.percentageRe);
304 horizontalFlex += (comp.flex = parseInt(percentage[1], 10) / 100);
307 hBoxItems.push(comp);
310 // Create the injected "items" 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) {
316 me.shadowContainer = Ext.create('Ext.container.Container', {
319 layout: Ext.applyIf({
322 getVisibleItems: me.getVisibleItems
325 me.createItems(me.shadowContainer, vBoxItems);
327 // Allow the Splitters to orientate themselves
328 if (me.splitters.north) {
329 me.splitters.north.ownerCt = me.shadowContainer;
331 if (me.splitters.south) {
332 me.splitters.south.ownerCt = me.shadowContainer;
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);
340 // Allow the Splitters to orientate themselves
341 if (me.splitters.east) {
342 me.splitters.east.ownerCt = me.embeddedContainer;
344 if (me.splitters.west) {
345 me.splitters.west.ownerCt = me.embeddedContainer;
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
353 Ext.each([me.splitters.north, me.splitters.south], function (splitter) {
355 splitter.on('beforedragstart', me.fixHeightConstraints, me);
359 // The east or west region wanted a percentage
360 if (horizontalFlex) {
361 regions.center.flex -= horizontalFlex;
363 // The north or south region wanted a percentage
365 me.embeddedContainer.flex -= verticalFlex;
368 // The north or south region wanted a percentage
370 regions.center.flex -= verticalFlex;
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.
377 me.shadowContainer = Ext.create('Ext.container.Container', {
380 layout: Ext.applyIf({
381 type: (hBoxItems.length == 1) ? 'fit' : 'hbox',
385 me.createItems(me.shadowContainer, hBoxItems);
387 // Allow the Splitters to orientate themselves
388 if (me.splitters.east) {
389 me.splitters.east.ownerCt = me.shadowContainer;
391 if (me.splitters.west) {
392 me.splitters.west.ownerCt = me.shadowContainer;
395 // The east or west region wanted a percentage
396 if (horizontalFlex) {
397 regions.center.flex -= verticalFlex;
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 < ln; i++) {
403 items[i].shadowOwnerCt = me.shadowContainer;
405 if (me.embeddedContainer) {
406 for (i = 0, items = me.embeddedContainer.items.items, ln = items.length; i < ln; i++) {
407 items[i].shadowOwnerCt = me.embeddedContainer;
411 // This is the layout that we delegate all operations to
412 me.shadowLayout = me.shadowContainer.getLayout();
414 me.borderLayoutInitialized = true;
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;
424 state.collapsed = !!comp.collapsed;
425 if (region == 'west' || region == 'east') {
426 state.width = comp.getWidth();
428 state.height = comp.getHeight();
432 comp.addStateEvents(['collapse', 'expand', 'resize']);
435 <span id='Ext-layout-container-Border-method-createItems'> /**
436 </span> * Create the items collection for our shadow/embedded containers
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);
448 // Create a splitter for a child of the layout.
449 createSplitter: function(comp) {
451 interceptCollapse = (comp.collapseMode != 'header'),
454 resizer = Ext.create('Ext.resizer.Splitter', {
455 hidden: !!comp.hidden,
456 collapseTarget: comp,
457 performCollapse: !interceptCollapse,
458 listeners: interceptCollapse ? {
460 fn: Ext.Function.bind(me.onSplitterCollapseClick, me, [comp]),
461 element: 'collapseEl'
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;
472 // Arrange to hide/show a region's associated splitter when the region is hidden/shown
474 hide: me.onRegionVisibilityChange,
475 show: me.onRegionVisibilityChange,
482 // Propagates the min/maxHeight values from the inner hbox items to its container.
483 fixHeightConstraints: function () {
485 ct = me.embeddedContainer,
486 maxHeight = 1e99, minHeight = -1;
492 ct.items.each(function (item) {
493 if (Ext.isNumber(item.maxHeight)) {
494 maxHeight = Math.max(maxHeight, item.maxHeight);
496 if (Ext.isNumber(item.minHeight)) {
497 minHeight = Math.max(minHeight, item.minHeight);
501 ct.maxHeight = maxHeight;
502 ct.minHeight = minHeight;
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']();
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});
522 <span id='Ext-layout-container-Border-method-getPlaceholder'> /**
523 </span> * <p>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.</p>
526 * <p><b>Note that this will be a fully instantiated Component, but will only be <i>rendered</i> when the Panel is first collapsed.</b></p>
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 * <code>'header'</code>, in which case <i>undefined</i> is returned.
531 getPlaceholder: function(comp) {
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');
539 // No placeholder if the collapse mode is not the Border layout default
540 if (comp.collapseMode == 'header') {
544 // Provide a replacement Container with an expand tool
546 if (comp.collapseMode == 'mini') {
547 placeholder = Ext.create('Ext.resizer.Splitter', {
548 id: 'collapse-placeholder-' + comp.id,
549 collapseTarget: comp,
550 performCollapse: false,
553 fn: Ext.Function.bind(me.onSplitterCollapseClick, me, [comp]),
554 element: 'collapseEl'
558 placeholder.addCls(placeholder.collapsedCls);
561 id: 'collapse-placeholder-' + comp.id,
562 margins: comp.initialConfig.margins || Ext.getClass(comp).prototype.margins,
564 orientation: horiz ? 'horizontal' : 'vertical',
566 textCls: comp.headerTextCls,
567 iconCls: comp.iconCls,
568 baseCls: comp.baseCls + '-header',
570 indicateDrag: comp.draggable,
571 cls: Ext.baseCSSPrefix + 'region-collapsed-placeholder ' + Ext.baseCSSPrefix + 'region-collapsed-' + comp.collapseDirection + '-placeholder ' + comp.collapsedCls,
572 listeners: comp.floatable ? {
575 me.floatCollapsedPanel(e, comp);
581 // Hack for IE6/7/IEQuirks's inability to display an inline-block
582 if ((Ext.isIE6 || Ext.isIE7 || (Ext.isIEQuirks)) && !horiz) {
583 placeholder.width = 25;
585 if (!comp.hideCollapseTool) {
586 placeholder[horiz ? 'tools' : 'items'] = [{
589 type: 'expand-' + oppositeDirection,
590 handler: me.onPlaceHolderToolClick,
595 placeholder = me.owner.createComponent(placeholder);
596 if (comp.isXType('panel')) {
598 titlechange: me.onRegionTitleChange,
599 iconchange: me.onRegionIconChange,
605 // The collapsed Component holds a reference to its placeholder and vice versa
606 comp.placeholder = placeholder;
607 placeholder.comp = comp;
612 <span id='Ext-layout-container-Border-method-onRegionTitleChange'> /**
614 * Update the placeholder title when panel title has been set or changed.
616 onRegionTitleChange: function(comp, newTitle) {
617 comp.placeholder.setTitle(newTitle);
620 <span id='Ext-layout-container-Border-method-onRegionIconChange'> /**
622 * Update the placeholder iconCls when panel iconCls has been set or changed.
624 onRegionIconChange: function(comp, newIconCls) {
625 comp.placeholder.setIconCls(newIconCls);
628 <span id='Ext-layout-container-Border-method-calculateChildBox'> /**
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.
635 calculateChildBox: function(comp) {
637 if (me.shadowContainer.items.contains(comp)) {
638 return me.shadowContainer.layout.calculateChildBox(comp);
640 else if (me.embeddedContainer && me.embeddedContainer.items.contains(comp)) {
641 return me.embeddedContainer.layout.calculateChildBox(comp);
645 <span id='Ext-layout-container-Border-method-onBeforeRegionCollapse'> /**
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.
652 * @returns {Boolean} false to inhibit the Panel from performing its own collapse.
654 onBeforeRegionCollapse: function(comp, direction, animate) {
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
666 // Do not trigger a layout during transition to collapsed Component
667 me.owner.suspendLayout = true;
668 shadowContainer.suspendLayout = true;
670 // Prevent upward notifications from downstream layouts
671 shadowLayout.layoutBusy = true;
672 if (shadowContainer.componentLayout) {
673 shadowContainer.componentLayout.layoutBusy = true;
675 me.shadowContainer.layout.layoutBusy = true;
676 me.layoutBusy = true;
677 me.owner.componentLayout.layoutBusy = true;
679 // Provide a replacement Container with an expand tool
681 placeholder = me.getPlaceholder(comp);
684 // placeholder already in place; show it.
685 if (placeholder.shadowOwnerCt === shadowContainer) {
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.
692 shadowContainer.insert(shadowContainer.items.indexOf(comp) + (isNorthOrWest ? 0 : 1), placeholder);
693 placeholder.shadowOwnerCt = shadowContainer;
694 placeholder.ownerCt = me.owner;
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
702 if (!placeholder.rendered) {
703 shadowLayout.renderItem(placeholder, shadowLayout.innerCt);
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());
713 placeholder.setCalculatedSize(undefined, comp.getHeight());
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;
726 delete me.shadowContainer.layout.layoutBusy;
727 delete me.layoutBusy;
728 delete me.owner.componentLayout.layoutBusy;
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);
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
740 if (comp.animCollapse && me.initialCollapsedComplete) {
741 shadowLayout.layout();
742 compEl.dom.style.zIndex = 100;
744 // If we're mini-collapsing, the placholder is a Splitter. We don't want it to "bounce in"
746 placeholder.el.hide();
748 compEl.slideOut(me.slideDirection[comp.region], {
749 duration: Ext.Number.from(comp.animCollapse, Ext.fx.Anim.prototype.duration),
751 afteranimate: function() {
752 compEl.show().setLeftTop(-10000, -10000);
753 compEl.dom.style.zIndex = '';
755 // If we're mini-collapsing, the placholder is a Splitter. We don't want it to "bounce in"
757 placeholder.el.slideIn(me.slideDirection[comp.region], {
767 compEl.setLeftTop(-10000, -10000);
768 shadowLayout.layout();
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});
781 // Called when the collapsed placeholder is clicked to reinstate a "collapsed" (in reality hidden) Panel.
782 onPlaceHolderToolClick: function(e, target, owner, tool) {
786 // Hide the placeholder unless it was the Component's preexisting splitter
787 hidePlaceholder = (comp.collapseMode != 'mini') || !comp.split,
790 placeholder = comp.placeholder,
791 placeholderEl = placeholder.el,
792 shadowContainer = comp.shadowOwnerCt,
793 shadowLayout = shadowContainer.layout,
795 sl = me.owner.suspendLayout,
796 scsl = shadowContainer.suspendLayout,
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();
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);
814 delete comp.slideOutAnim;
815 delete comp.panelMouseMon;
816 delete comp.placeholderMouseMon;
818 // If the Panel was floated and primed with a slideOut animation, we don't want to animate its layout operation.
822 // Do not trigger a layout during transition to expanded Component
823 me.owner.suspendLayout = true;
824 shadowContainer.suspendLayout = true;
826 // Prevent upward notifications from downstream layouts
827 shadowLayout.layoutBusy = true;
828 if (shadowContainer.componentLayout) {
829 shadowContainer.componentLayout.layoutBusy = true;
831 me.shadowContainer.layout.layoutBusy = true;
832 me.layoutBusy = true;
833 me.owner.componentLayout.layoutBusy = true;
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.
838 comp.collapsed = false;
839 if (hidePlaceholder) {
840 placeholder.hidden = true;
842 toCompBox = shadowLayout.calculateChildBox(comp);
844 // Show the collapse tool in case it was hidden by the slide-in
845 if (comp.collapseTool) {
846 comp.collapseTool.show();
849 // If we're going to animate, we need to hide the component before moving it back into position
850 if (comp.animCollapse && !isFloating) {
851 compEl.setStyle('visibility', 'hidden');
853 compEl.setLeftTop(toCompBox.left, toCompBox.top);
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);
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;
871 delete me.shadowContainer.layout.layoutBusy;
872 delete me.layoutBusy;
873 delete me.owner.componentLayout.layoutBusy;
875 // In case it was floated out and they clicked the re-expand tool
876 comp.removeCls(Ext.baseCSSPrefix + 'border-region-slide-in');
878 // Fire the expand event: The Panel has in fact been expanded, but by removal of an alternative Component
879 comp.fireEvent('expand', comp);
882 // Hide the placeholder
883 if (hidePlaceholder) {
884 placeholder.el.hide();
887 // Slide the expanding Component to its new position.
888 // When that is done, layout the layout.
889 if (comp.animCollapse && !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),
894 afteranimate: function() {
895 compEl.dom.style.zIndex = '';
897 shadowLayout.onLayout();
903 shadowLayout.onLayout();
908 floatCollapsedPanel: function(e, comp) {
910 if (comp.floatable === false) {
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;
924 // Ignore clicks on tools.
925 if (e.getTarget('.' + Ext.baseCSSPrefix + 'tool')) {
929 // It's *being* animated, ignore the click.
930 // Possible future enhancement: Stop and *reverse* the current active Fx.
931 if (compEl.getActiveAnimation()) {
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);
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);
947 // If mouse is not within slide Region, slide it out
948 if (!slideRegion.contains(e.getPoint())) {
949 me.slideOutFloatedComponent(comp);
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);
956 // Do not trigger a layout during slide out of the Component
957 shadowContainer.suspendLayout = true;
959 // Prevent upward notifications from downstream layouts
960 me.layoutBusy = true;
961 me.owner.componentLayout.layoutBusy = true;
963 // The collapse tool is hidden while slid.
964 // It is re-shown on expand.
965 if (comp.collapseTool) {
966 comp.collapseTool.hide();
969 // Set flags so that the layout will calculate the boxes for what we want
971 comp.collapsed = false;
972 placeholder.hidden = true;
974 // Recalculate new arrangement of the Component being floated.
975 toCompBox = shadowLayout.calculateChildBox(comp);
976 placeholder.hidden = false;
978 // Component to appear just after the placeholder, whatever "after" 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;
982 toCompBox[shadowLayout.parallelBefore] -= (placeholderBox[shadowLayout.parallelPrefix] - 1);
984 compEl.setStyle('visibility', 'hidden');
985 compEl.setLeftTop(toCompBox.left, toCompBox.top);
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);
994 // This animation slides the collapsed Component's el out to just beyond its placeholder
997 afteranimate: function() {
998 shadowContainer.suspendLayout = scsl;
999 delete me.layoutBusy;
1000 delete me.owner.componentLayout.layoutBusy;
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);
1007 // Reinstate the correct, current state after slide out animation finishes
1009 comp.collapsed = true;
1010 delete comp.slideOutAnim;
1011 delete comp.panelMouseMon;
1012 delete comp.placeholderMouseMon;
1015 comp.slideOutAnim = compAnim;
1021 // Give the element the correct class which places it at a high z-index
1022 compEl.addCls(Ext.baseCSSPrefix + 'border-region-slide-in');
1024 // Begin the slide in
1025 compEl.slideIn(me.slideDirection[comp.region], compAnim);
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);
1032 slideOutFloatedComponent: function(comp) {
1033 var compEl = comp.el,
1036 // Remove mouse leave monitors
1037 compEl.un(comp.panelMouseMon);
1038 comp.placeholder.el.un(comp.placeholderMouseMon);
1040 // Slide the Component out
1041 compEl.slideOut(this.slideDirection[comp.region], comp.slideOutAnim);
1043 delete comp.slideOutAnim;
1044 delete comp.panelMouseMon;
1045 delete comp.placeholderMouseMon;
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.
1053 onRegionDestroy: function(comp) {
1054 var placeholder = comp.placeholder;
1056 delete placeholder.ownerCt;
1057 placeholder.destroy();
1063 * Ensure any shadow Containers are destroyed.
1064 * Ensure we don't keep references to Components.
1066 onDestroy: function() {
1068 shadowContainer = me.shadowContainer,
1069 embeddedContainer = me.embeddedContainer;
1071 if (shadowContainer) {
1072 delete shadowContainer.ownerCt;
1073 Ext.destroy(shadowContainer);
1076 if (embeddedContainer) {
1077 delete embeddedContainer.ownerCt;
1078 Ext.destroy(embeddedContainer);
1081 delete me.splitters;
1082 delete me.shadowContainer;
1083 delete me.embeddedContainer;
1084 me.callParent(arguments);