Upgrade to ExtJS 3.1.0 - Released 12/16/2009
[extjs.git] / src / widgets / layout / BorderLayout.js
1 /*!
2  * Ext JS Library 3.1.0
3  * Copyright(c) 2006-2009 Ext JS, LLC
4  * licensing@extjs.com
5  * http://www.extjs.com/license
6  */
7 /**
8  * @class Ext.layout.BorderLayout
9  * @extends Ext.layout.ContainerLayout
10  * <p>This is a multi-pane, application-oriented UI layout style that supports multiple
11  * nested panels, automatic {@link Ext.layout.BorderLayout.Region#split split} bars between
12  * {@link Ext.layout.BorderLayout.Region#BorderLayout.Region regions} and built-in
13  * {@link Ext.layout.BorderLayout.Region#collapsible expanding and collapsing} of regions.</p>
14  * <p>This class is intended to be extended or created via the <tt>layout:'border'</tt>
15  * {@link Ext.Container#layout} config, and should generally not need to be created directly
16  * via the new keyword.</p>
17  * <p>BorderLayout does not have any direct config options (other than inherited ones).
18  * All configuration options available for customizing the BorderLayout are at the
19  * {@link Ext.layout.BorderLayout.Region} and {@link Ext.layout.BorderLayout.SplitRegion}
20  * levels.</p>
21  * <p>Example usage:</p>
22  * <pre><code>
23 var myBorderPanel = new Ext.Panel({
24     {@link Ext.Component#renderTo renderTo}: document.body,
25     {@link Ext.BoxComponent#width width}: 700,
26     {@link Ext.BoxComponent#height height}: 500,
27     {@link Ext.Panel#title title}: 'Border Layout',
28     {@link Ext.Container#layout layout}: 'border',
29     {@link Ext.Container#items items}: [{
30         {@link Ext.Panel#title title}: 'South Region is resizable',
31         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'south',     // position for region
32         {@link Ext.BoxComponent#height height}: 100,
33         {@link Ext.layout.BorderLayout.Region#split split}: true,         // enable resizing
34         {@link Ext.SplitBar#minSize minSize}: 75,         // defaults to {@link Ext.layout.BorderLayout.Region#minHeight 50}
35         {@link Ext.SplitBar#maxSize maxSize}: 150,
36         {@link Ext.layout.BorderLayout.Region#margins margins}: '0 5 5 5'
37     },{
38         // xtype: 'panel' implied by default
39         {@link Ext.Panel#title title}: 'West Region is collapsible',
40         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}:'west',
41         {@link Ext.layout.BorderLayout.Region#margins margins}: '5 0 0 5',
42         {@link Ext.BoxComponent#width width}: 200,
43         {@link Ext.layout.BorderLayout.Region#collapsible collapsible}: true,   // make collapsible
44         {@link Ext.layout.BorderLayout.Region#cmargins cmargins}: '5 5 0 5', // adjust top margin when collapsed
45         {@link Ext.Component#id id}: 'west-region-container',
46         {@link Ext.Container#layout layout}: 'fit',
47         {@link Ext.Panel#unstyled unstyled}: true
48     },{
49         {@link Ext.Panel#title title}: 'Center Region',
50         {@link Ext.layout.BorderLayout.Region#BorderLayout.Region region}: 'center',     // center region is required, no width/height specified
51         {@link Ext.Component#xtype xtype}: 'container',
52         {@link Ext.Container#layout layout}: 'fit',
53         {@link Ext.layout.BorderLayout.Region#margins margins}: '5 5 0 0'
54     }]
55 });
56 </code></pre>
57  * <p><b><u>Notes</u></b>:</p><div class="mdetail-params"><ul>
58  * <li>Any container using the BorderLayout <b>must</b> have a child item with <tt>region:'center'</tt>.
59  * The child item in the center region will always be resized to fill the remaining space not used by
60  * the other regions in the layout.</li>
61  * <li>Any child items with a region of <tt>west</tt> or <tt>east</tt> must have <tt>width</tt> defined
62  * (an integer representing the number of pixels that the region should take up).</li>
63  * <li>Any child items with a region of <tt>north</tt> or <tt>south</tt> must have <tt>height</tt> defined.</li>
64  * <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
65  * Components within a BorderLayout, have them wrapped by an additional Container which is directly
66  * managed by the BorderLayout.  If the region is to be collapsible, the Container used directly
67  * by the BorderLayout manager should be a Panel.  In the following example a Container (an Ext.Panel)
68  * is added to the west region:
69  * <div style="margin-left:16px"><pre><code>
70 wrc = {@link Ext#getCmp Ext.getCmp}('west-region-container');
71 wrc.{@link Ext.Panel#removeAll removeAll}();
72 wrc.{@link Ext.Container#add add}({
73     title: 'Added Panel',
74     html: 'Some content'
75 });
76 wrc.{@link Ext.Container#doLayout doLayout}();
77  * </code></pre></div>
78  * </li>
79  * <li> To reference a {@link Ext.layout.BorderLayout.Region Region}:
80  * <div style="margin-left:16px"><pre><code>
81 wr = myBorderPanel.layout.west;
82  * </code></pre></div>
83  * </li>
84  * </ul></div>
85  */
86 Ext.layout.BorderLayout = Ext.extend(Ext.layout.ContainerLayout, {
87     // private
88     monitorResize:true,
89     // private
90     rendered : false,
91
92     targetCls: 'x-border-layout-ct',
93
94     // private
95     onLayout : function(ct, target){
96         var collapsed;
97         if(!this.rendered){
98             var items = ct.items.items;
99             collapsed = [];
100             for(var i = 0, len = items.length; i < len; i++) {
101                 var c = items[i];
102                 var pos = c.region;
103                 if(c.collapsed){
104                     collapsed.push(c);
105                 }
106                 c.collapsed = false;
107                 if(!c.rendered){
108                     c.render(target, i);
109                     c.getPositionEl().addClass('x-border-panel');
110                 }
111                 this[pos] = pos != 'center' && c.split ?
112                     new Ext.layout.BorderLayout.SplitRegion(this, c.initialConfig, pos) :
113                     new Ext.layout.BorderLayout.Region(this, c.initialConfig, pos);
114                 this[pos].render(target, c);
115             }
116             this.rendered = true;
117         }
118
119         var size = target.getViewSize(false);
120         if(size.width < 20 || size.height < 20){ // display none?
121             if(collapsed){
122                 this.restoreCollapsed = collapsed;
123             }
124             return;
125         }else if(this.restoreCollapsed){
126             collapsed = this.restoreCollapsed;
127             delete this.restoreCollapsed;
128         }
129
130         var w = size.width, h = size.height;
131         var centerW = w, centerH = h, centerY = 0, centerX = 0;
132
133         var n = this.north, s = this.south, west = this.west, e = this.east, c = this.center;
134         if(!c && Ext.layout.BorderLayout.WARN !== false){
135             throw 'No center region defined in BorderLayout ' + ct.id;
136         }
137
138         if(n && n.isVisible()){
139             var b = n.getSize();
140             var m = n.getMargins();
141             b.width = w - (m.left+m.right);
142             b.x = m.left;
143             b.y = m.top;
144             centerY = b.height + b.y + m.bottom;
145             centerH -= centerY;
146             n.applyLayout(b);
147         }
148         if(s && s.isVisible()){
149             var b = s.getSize();
150             var m = s.getMargins();
151             b.width = w - (m.left+m.right);
152             b.x = m.left;
153             var totalHeight = (b.height + m.top + m.bottom);
154             b.y = h - totalHeight + m.top;
155             centerH -= totalHeight;
156             s.applyLayout(b);
157         }
158         if(west && west.isVisible()){
159             var b = west.getSize();
160             var m = west.getMargins();
161             b.height = centerH - (m.top+m.bottom);
162             b.x = m.left;
163             b.y = centerY + m.top;
164             var totalWidth = (b.width + m.left + m.right);
165             centerX += totalWidth;
166             centerW -= totalWidth;
167             west.applyLayout(b);
168         }
169         if(e && e.isVisible()){
170             var b = e.getSize();
171             var m = e.getMargins();
172             b.height = centerH - (m.top+m.bottom);
173             var totalWidth = (b.width + m.left + m.right);
174             b.x = w - totalWidth + m.left;
175             b.y = centerY + m.top;
176             centerW -= totalWidth;
177             e.applyLayout(b);
178         }
179         if(c){
180             var m = c.getMargins();
181             var centerBox = {
182                 x: centerX + m.left,
183                 y: centerY + m.top,
184                 width: centerW - (m.left+m.right),
185                 height: centerH - (m.top+m.bottom)
186             };
187             c.applyLayout(centerBox);
188         }
189         if(collapsed){
190             for(var i = 0, len = collapsed.length; i < len; i++){
191                 collapsed[i].collapse(false);
192             }
193         }
194         if(Ext.isIE && Ext.isStrict){ // workaround IE strict repainting issue
195             target.repaint();
196         }
197     },
198
199     destroy: function() {
200         var r = ['north', 'south', 'east', 'west'];
201         for (var i = 0; i < r.length; i++) {
202             var region = this[r[i]];
203             if(region){
204                 if(region.destroy){
205                     region.destroy();
206                 }else if (region.split){
207                     region.split.destroy(true);
208                 }
209             }
210         }
211         Ext.layout.BorderLayout.superclass.destroy.call(this);
212     }
213
214     /**
215      * @property activeItem
216      * @hide
217      */
218 });
219
220 /**
221  * @class Ext.layout.BorderLayout.Region
222  * <p>This is a region of a {@link Ext.layout.BorderLayout BorderLayout} that acts as a subcontainer
223  * within the layout.  Each region has its own {@link Ext.layout.ContainerLayout layout} that is
224  * independent of other regions and the containing BorderLayout, and can be any of the
225  * {@link Ext.layout.ContainerLayout valid Ext layout types}.</p>
226  * <p>Region size is managed automatically and cannot be changed by the user -- for
227  * {@link #split resizable regions}, see {@link Ext.layout.BorderLayout.SplitRegion}.</p>
228  * @constructor
229  * Create a new Region.
230  * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region.
231  * @param {Object} config The configuration options
232  * @param {String} position The region position.  Valid values are: <tt>north</tt>, <tt>south</tt>,
233  * <tt>east</tt>, <tt>west</tt> and <tt>center</tt>.  Every {@link Ext.layout.BorderLayout BorderLayout}
234  * <b>must have a center region</b> for the primary content -- all other regions are optional.
235  */
236 Ext.layout.BorderLayout.Region = function(layout, config, pos){
237     Ext.apply(this, config);
238     this.layout = layout;
239     this.position = pos;
240     this.state = {};
241     if(typeof this.margins == 'string'){
242         this.margins = this.layout.parseMargins(this.margins);
243     }
244     this.margins = Ext.applyIf(this.margins || {}, this.defaultMargins);
245     if(this.collapsible){
246         if(typeof this.cmargins == 'string'){
247             this.cmargins = this.layout.parseMargins(this.cmargins);
248         }
249         if(this.collapseMode == 'mini' && !this.cmargins){
250             this.cmargins = {left:0,top:0,right:0,bottom:0};
251         }else{
252             this.cmargins = Ext.applyIf(this.cmargins || {},
253                 pos == 'north' || pos == 'south' ? this.defaultNSCMargins : this.defaultEWCMargins);
254         }
255     }
256 };
257
258 Ext.layout.BorderLayout.Region.prototype = {
259     /**
260      * @cfg {Boolean} animFloat
261      * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated
262      * panel that will close again once the user mouses out of that panel (or clicks out if
263      * <tt>{@link #autoHide} = false</tt>).  Setting <tt>{@link #animFloat} = false</tt> will
264      * prevent the open and close of these floated panels from being animated (defaults to <tt>true</tt>).
265      */
266     /**
267      * @cfg {Boolean} autoHide
268      * When a collapsed region's bar is clicked, the region's panel will be displayed as a floated
269      * panel.  If <tt>autoHide = true</tt>, the panel will automatically hide after the user mouses
270      * out of the panel.  If <tt>autoHide = false</tt>, the panel will continue to display until the
271      * user clicks outside of the panel (defaults to <tt>true</tt>).
272      */
273     /**
274      * @cfg {String} collapseMode
275      * <tt>collapseMode</tt> supports two configuration values:<div class="mdetail-params"><ul>
276      * <li><b><tt>undefined</tt></b> (default)<div class="sub-desc">By default, {@link #collapsible}
277      * regions are collapsed by clicking the expand/collapse tool button that renders into the region's
278      * title bar.</div></li>
279      * <li><b><tt>'mini'</tt></b><div class="sub-desc">Optionally, when <tt>collapseMode</tt> is set to
280      * <tt>'mini'</tt> the region's split bar will also display a small collapse button in the center of
281      * the bar. In <tt>'mini'</tt> mode the region will collapse to a thinner bar than in normal mode.
282      * </div></li>
283      * </ul></div></p>
284      * <p><b>Note</b>: if a collapsible region does not have a title bar, then set <tt>collapseMode =
285      * 'mini'</tt> and <tt>{@link #split} = true</tt> in order for the region to be {@link #collapsible}
286      * by the user as the expand/collapse tool button (that would go in the title bar) will not be rendered.</p>
287      * <p>See also <tt>{@link #cmargins}</tt>.</p>
288      */
289     /**
290      * @cfg {Object} margins
291      * An object containing margins to apply to the region when in the expanded state in the
292      * format:<pre><code>
293 {
294     top: (top margin),
295     right: (right margin),
296     bottom: (bottom margin),
297     left: (left margin)
298 }</code></pre>
299      * <p>May also be a string containing space-separated, numeric margin values. The order of the
300      * sides associated with each value matches the way CSS processes margin values:</p>
301      * <p><div class="mdetail-params"><ul>
302      * <li>If there is only one value, it applies to all sides.</li>
303      * <li>If there are two values, the top and bottom borders are set to the first value and the
304      * right and left are set to the second.</li>
305      * <li>If there are three values, the top is set to the first value, the left and right are set
306      * to the second, and the bottom is set to the third.</li>
307      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
308      * </ul></div></p>
309      * <p>Defaults to:</p><pre><code>
310      * {top:0, right:0, bottom:0, left:0}
311      * </code></pre>
312      */
313     /**
314      * @cfg {Object} cmargins
315      * An object containing margins to apply to the region when in the collapsed state in the
316      * format:<pre><code>
317 {
318     top: (top margin),
319     right: (right margin),
320     bottom: (bottom margin),
321     left: (left margin)
322 }</code></pre>
323      * <p>May also be a string containing space-separated, numeric margin values. The order of the
324      * sides associated with each value matches the way CSS processes margin values.</p>
325      * <p><ul>
326      * <li>If there is only one value, it applies to all sides.</li>
327      * <li>If there are two values, the top and bottom borders are set to the first value and the
328      * right and left are set to the second.</li>
329      * <li>If there are three values, the top is set to the first value, the left and right are set
330      * to the second, and the bottom is set to the third.</li>
331      * <li>If there are four values, they apply to the top, right, bottom, and left, respectively.</li>
332      * </ul></p>
333      */
334     /**
335      * @cfg {Boolean} collapsible
336      * <p><tt>true</tt> to allow the user to collapse this region (defaults to <tt>false</tt>).  If
337      * <tt>true</tt>, an expand/collapse tool button will automatically be rendered into the title
338      * bar of the region, otherwise the button will not be shown.</p>
339      * <p><b>Note</b>: that a title bar is required to display the collapse/expand toggle button -- if
340      * no <tt>title</tt> is specified for the region's panel, the region will only be collapsible if
341      * <tt>{@link #collapseMode} = 'mini'</tt> and <tt>{@link #split} = true</tt>.
342      */
343     collapsible : false,
344     /**
345      * @cfg {Boolean} split
346      * <p><tt>true</tt> to create a {@link Ext.layout.BorderLayout.SplitRegion SplitRegion} and
347      * display a 5px wide {@link Ext.SplitBar} between this region and its neighbor, allowing the user to
348      * resize the regions dynamically.  Defaults to <tt>false</tt> creating a
349      * {@link Ext.layout.BorderLayout.Region Region}.</p><br>
350      * <p><b>Notes</b>:</p><div class="mdetail-params"><ul>
351      * <li>this configuration option is ignored if <tt>region='center'</tt></li>
352      * <li>when <tt>split == true</tt>, it is common to specify a
353      * <tt>{@link Ext.SplitBar#minSize minSize}</tt> and <tt>{@link Ext.SplitBar#maxSize maxSize}</tt>
354      * for the {@link Ext.BoxComponent BoxComponent} representing the region. These are not native
355      * configs of {@link Ext.BoxComponent BoxComponent}, and are used only by this class.</li>
356      * <li>if <tt>{@link #collapseMode} = 'mini'</tt> requires <tt>split = true</tt> to reserve space
357      * for the collapse tool</tt></li>
358      * </ul></div>
359      */
360     split:false,
361     /**
362      * @cfg {Boolean} floatable
363      * <tt>true</tt> to allow clicking a collapsed region's bar to display the region's panel floated
364      * above the layout, <tt>false</tt> to force the user to fully expand a collapsed region by
365      * clicking the expand button to see it again (defaults to <tt>true</tt>).
366      */
367     floatable: true,
368     /**
369      * @cfg {Number} minWidth
370      * <p>The minimum allowable width in pixels for this region (defaults to <tt>50</tt>).
371      * <tt>maxWidth</tt> may also be specified.</p><br>
372      * <p><b>Note</b>: setting the <tt>{@link Ext.SplitBar#minSize minSize}</tt> /
373      * <tt>{@link Ext.SplitBar#maxSize maxSize}</tt> supersedes any specified
374      * <tt>minWidth</tt> / <tt>maxWidth</tt>.</p>
375      */
376     minWidth:50,
377     /**
378      * @cfg {Number} minHeight
379      * The minimum allowable height in pixels for this region (defaults to <tt>50</tt>)
380      * <tt>maxHeight</tt> may also be specified.</p><br>
381      * <p><b>Note</b>: setting the <tt>{@link Ext.SplitBar#minSize minSize}</tt> /
382      * <tt>{@link Ext.SplitBar#maxSize maxSize}</tt> supersedes any specified
383      * <tt>minHeight</tt> / <tt>maxHeight</tt>.</p>
384      */
385     minHeight:50,
386
387     // private
388     defaultMargins : {left:0,top:0,right:0,bottom:0},
389     // private
390     defaultNSCMargins : {left:5,top:5,right:5,bottom:5},
391     // private
392     defaultEWCMargins : {left:5,top:0,right:5,bottom:0},
393     floatingZIndex: 100,
394
395     /**
396      * True if this region is collapsed. Read-only.
397      * @type Boolean
398      * @property
399      */
400     isCollapsed : false,
401
402     /**
403      * This region's panel.  Read-only.
404      * @type Ext.Panel
405      * @property panel
406      */
407     /**
408      * This region's layout.  Read-only.
409      * @type Layout
410      * @property layout
411      */
412     /**
413      * This region's layout position (north, south, east, west or center).  Read-only.
414      * @type String
415      * @property position
416      */
417
418     // private
419     render : function(ct, p){
420         this.panel = p;
421         p.el.enableDisplayMode();
422         this.targetEl = ct;
423         this.el = p.el;
424
425         var gs = p.getState, ps = this.position;
426         p.getState = function(){
427             return Ext.apply(gs.call(p) || {}, this.state);
428         }.createDelegate(this);
429
430         if(ps != 'center'){
431             p.allowQueuedExpand = false;
432             p.on({
433                 beforecollapse: this.beforeCollapse,
434                 collapse: this.onCollapse,
435                 beforeexpand: this.beforeExpand,
436                 expand: this.onExpand,
437                 hide: this.onHide,
438                 show: this.onShow,
439                 scope: this
440             });
441             if(this.collapsible || this.floatable){
442                 p.collapseEl = 'el';
443                 p.slideAnchor = this.getSlideAnchor();
444             }
445             if(p.tools && p.tools.toggle){
446                 p.tools.toggle.addClass('x-tool-collapse-'+ps);
447                 p.tools.toggle.addClassOnOver('x-tool-collapse-'+ps+'-over');
448             }
449         }
450     },
451
452     // private
453     getCollapsedEl : function(){
454         if(!this.collapsedEl){
455             if(!this.toolTemplate){
456                 var tt = new Ext.Template(
457                      '<div class="x-tool x-tool-{id}">&#160;</div>'
458                 );
459                 tt.disableFormats = true;
460                 tt.compile();
461                 Ext.layout.BorderLayout.Region.prototype.toolTemplate = tt;
462             }
463             this.collapsedEl = this.targetEl.createChild({
464                 cls: "x-layout-collapsed x-layout-collapsed-"+this.position,
465                 id: this.panel.id + '-xcollapsed'
466             });
467             this.collapsedEl.enableDisplayMode('block');
468
469             if(this.collapseMode == 'mini'){
470                 this.collapsedEl.addClass('x-layout-cmini-'+this.position);
471                 this.miniCollapsedEl = this.collapsedEl.createChild({
472                     cls: "x-layout-mini x-layout-mini-"+this.position, html: "&#160;"
473                 });
474                 this.miniCollapsedEl.addClassOnOver('x-layout-mini-over');
475                 this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
476                 this.collapsedEl.on('click', this.onExpandClick, this, {stopEvent:true});
477             }else {
478                 if(this.collapsible !== false && !this.hideCollapseTool) {
479                     var t = this.toolTemplate.append(
480                             this.collapsedEl.dom,
481                             {id:'expand-'+this.position}, true);
482                     t.addClassOnOver('x-tool-expand-'+this.position+'-over');
483                     t.on('click', this.onExpandClick, this, {stopEvent:true});
484                 }
485                 if(this.floatable !== false || this.titleCollapse){
486                    this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
487                    this.collapsedEl.on("click", this[this.floatable ? 'collapseClick' : 'onExpandClick'], this);
488                 }
489             }
490         }
491         return this.collapsedEl;
492     },
493
494     // private
495     onExpandClick : function(e){
496         if(this.isSlid){
497             this.panel.expand(false);
498         }else{
499             this.panel.expand();
500         }
501     },
502
503     // private
504     onCollapseClick : function(e){
505         this.panel.collapse();
506     },
507
508     // private
509     beforeCollapse : function(p, animate){
510         this.lastAnim = animate;
511         if(this.splitEl){
512             this.splitEl.hide();
513         }
514         this.getCollapsedEl().show();
515         var el = this.panel.getEl();
516         this.originalZIndex = el.getStyle('z-index');
517         el.setStyle('z-index', 100);
518         this.isCollapsed = true;
519         this.layout.layout();
520     },
521
522     // private
523     onCollapse : function(animate){
524         this.panel.el.setStyle('z-index', 1);
525         if(this.lastAnim === false || this.panel.animCollapse === false){
526             this.getCollapsedEl().dom.style.visibility = 'visible';
527         }else{
528             this.getCollapsedEl().slideIn(this.panel.slideAnchor, {duration:.2});
529         }
530         this.state.collapsed = true;
531         this.panel.saveState();
532     },
533
534     // private
535     beforeExpand : function(animate){
536         if(this.isSlid){
537             this.afterSlideIn();
538         }
539         var c = this.getCollapsedEl();
540         this.el.show();
541         if(this.position == 'east' || this.position == 'west'){
542             this.panel.setSize(undefined, c.getHeight());
543         }else{
544             this.panel.setSize(c.getWidth(), undefined);
545         }
546         c.hide();
547         c.dom.style.visibility = 'hidden';
548         this.panel.el.setStyle('z-index', this.floatingZIndex);
549     },
550
551     // private
552     onExpand : function(){
553         this.isCollapsed = false;
554         if(this.splitEl){
555             this.splitEl.show();
556         }
557         this.layout.layout();
558         this.panel.el.setStyle('z-index', this.originalZIndex);
559         this.state.collapsed = false;
560         this.panel.saveState();
561     },
562
563     // private
564     collapseClick : function(e){
565         if(this.isSlid){
566            e.stopPropagation();
567            this.slideIn();
568         }else{
569            e.stopPropagation();
570            this.slideOut();
571         }
572     },
573
574     // private
575     onHide : function(){
576         if(this.isCollapsed){
577             this.getCollapsedEl().hide();
578         }else if(this.splitEl){
579             this.splitEl.hide();
580         }
581     },
582
583     // private
584     onShow : function(){
585         if(this.isCollapsed){
586             this.getCollapsedEl().show();
587         }else if(this.splitEl){
588             this.splitEl.show();
589         }
590     },
591
592     /**
593      * True if this region is currently visible, else false.
594      * @return {Boolean}
595      */
596     isVisible : function(){
597         return !this.panel.hidden;
598     },
599
600     /**
601      * Returns the current margins for this region.  If the region is collapsed, the
602      * {@link #cmargins} (collapsed margins) value will be returned, otherwise the
603      * {@link #margins} value will be returned.
604      * @return {Object} An object containing the element's margins: <tt>{left: (left
605      * margin), top: (top margin), right: (right margin), bottom: (bottom margin)}</tt>
606      */
607     getMargins : function(){
608         return this.isCollapsed && this.cmargins ? this.cmargins : this.margins;
609     },
610
611     /**
612      * Returns the current size of this region.  If the region is collapsed, the size of the
613      * collapsedEl will be returned, otherwise the size of the region's panel will be returned.
614      * @return {Object} An object containing the element's size: <tt>{width: (element width),
615      * height: (element height)}</tt>
616      */
617     getSize : function(){
618         return this.isCollapsed ? this.getCollapsedEl().getSize() : this.panel.getSize();
619     },
620
621     /**
622      * Sets the specified panel as the container element for this region.
623      * @param {Ext.Panel} panel The new panel
624      */
625     setPanel : function(panel){
626         this.panel = panel;
627     },
628
629     /**
630      * Returns the minimum allowable width for this region.
631      * @return {Number} The minimum width
632      */
633     getMinWidth: function(){
634         return this.minWidth;
635     },
636
637     /**
638      * Returns the minimum allowable height for this region.
639      * @return {Number} The minimum height
640      */
641     getMinHeight: function(){
642         return this.minHeight;
643     },
644
645     // private
646     applyLayoutCollapsed : function(box){
647         var ce = this.getCollapsedEl();
648         ce.setLeftTop(box.x, box.y);
649         ce.setSize(box.width, box.height);
650     },
651
652     // private
653     applyLayout : function(box){
654         if(this.isCollapsed){
655             this.applyLayoutCollapsed(box);
656         }else{
657             this.panel.setPosition(box.x, box.y);
658             this.panel.setSize(box.width, box.height);
659         }
660     },
661
662     // private
663     beforeSlide: function(){
664         this.panel.beforeEffect();
665     },
666
667     // private
668     afterSlide : function(){
669         this.panel.afterEffect();
670     },
671
672     // private
673     initAutoHide : function(){
674         if(this.autoHide !== false){
675             if(!this.autoHideHd){
676                 var st = new Ext.util.DelayedTask(this.slideIn, this);
677                 this.autoHideHd = {
678                     "mouseout": function(e){
679                         if(!e.within(this.el, true)){
680                             st.delay(500);
681                         }
682                     },
683                     "mouseover" : function(e){
684                         st.cancel();
685                     },
686                     scope : this
687                 };
688             }
689             this.el.on(this.autoHideHd);
690             this.collapsedEl.on(this.autoHideHd);
691         }
692     },
693
694     // private
695     clearAutoHide : function(){
696         if(this.autoHide !== false){
697             this.el.un("mouseout", this.autoHideHd.mouseout);
698             this.el.un("mouseover", this.autoHideHd.mouseover);
699             this.collapsedEl.un("mouseout", this.autoHideHd.mouseout);
700             this.collapsedEl.un("mouseover", this.autoHideHd.mouseover);
701         }
702     },
703
704     // private
705     clearMonitor : function(){
706         Ext.getDoc().un("click", this.slideInIf, this);
707     },
708
709     /**
710      * If this Region is {@link #floatable}, this method slides this Region into full visibility <i>over the top
711      * of the center Region</i> where it floats until either {@link #slideIn} is called, or other regions of the layout
712      * are clicked, or the mouse exits the Region.
713      */
714     slideOut : function(){
715         if(this.isSlid || this.el.hasActiveFx()){
716             return;
717         }
718         this.isSlid = true;
719         var ts = this.panel.tools;
720         if(ts && ts.toggle){
721             ts.toggle.hide();
722         }
723         this.el.show();
724         if(this.position == 'east' || this.position == 'west'){
725             this.panel.setSize(undefined, this.collapsedEl.getHeight());
726         }else{
727             this.panel.setSize(this.collapsedEl.getWidth(), undefined);
728         }
729         this.restoreLT = [this.el.dom.style.left, this.el.dom.style.top];
730         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
731         this.el.setStyle("z-index", this.floatingZIndex+2);
732         this.panel.el.replaceClass('x-panel-collapsed', 'x-panel-floating');
733         if(this.animFloat !== false){
734             this.beforeSlide();
735             this.el.slideIn(this.getSlideAnchor(), {
736                 callback: function(){
737                     this.afterSlide();
738                     this.initAutoHide();
739                     Ext.getDoc().on("click", this.slideInIf, this);
740                 },
741                 scope: this,
742                 block: true
743             });
744         }else{
745             this.initAutoHide();
746              Ext.getDoc().on("click", this.slideInIf, this);
747         }
748     },
749
750     // private
751     afterSlideIn : function(){
752         this.clearAutoHide();
753         this.isSlid = false;
754         this.clearMonitor();
755         this.el.setStyle("z-index", "");
756         this.panel.el.replaceClass('x-panel-floating', 'x-panel-collapsed');
757         this.el.dom.style.left = this.restoreLT[0];
758         this.el.dom.style.top = this.restoreLT[1];
759
760         var ts = this.panel.tools;
761         if(ts && ts.toggle){
762             ts.toggle.show();
763         }
764     },
765
766     /**
767      * If this Region is {@link #floatable}, and this Region has been slid into floating visibility, then this method slides
768      * this region back into its collapsed state.
769      */
770     slideIn : function(cb){
771         if(!this.isSlid || this.el.hasActiveFx()){
772             Ext.callback(cb);
773             return;
774         }
775         this.isSlid = false;
776         if(this.animFloat !== false){
777             this.beforeSlide();
778             this.el.slideOut(this.getSlideAnchor(), {
779                 callback: function(){
780                     this.el.hide();
781                     this.afterSlide();
782                     this.afterSlideIn();
783                     Ext.callback(cb);
784                 },
785                 scope: this,
786                 block: true
787             });
788         }else{
789             this.el.hide();
790             this.afterSlideIn();
791         }
792     },
793
794     // private
795     slideInIf : function(e){
796         if(!e.within(this.el)){
797             this.slideIn();
798         }
799     },
800
801     // private
802     anchors : {
803         "west" : "left",
804         "east" : "right",
805         "north" : "top",
806         "south" : "bottom"
807     },
808
809     // private
810     sanchors : {
811         "west" : "l",
812         "east" : "r",
813         "north" : "t",
814         "south" : "b"
815     },
816
817     // private
818     canchors : {
819         "west" : "tl-tr",
820         "east" : "tr-tl",
821         "north" : "tl-bl",
822         "south" : "bl-tl"
823     },
824
825     // private
826     getAnchor : function(){
827         return this.anchors[this.position];
828     },
829
830     // private
831     getCollapseAnchor : function(){
832         return this.canchors[this.position];
833     },
834
835     // private
836     getSlideAnchor : function(){
837         return this.sanchors[this.position];
838     },
839
840     // private
841     getAlignAdj : function(){
842         var cm = this.cmargins;
843         switch(this.position){
844             case "west":
845                 return [0, 0];
846             break;
847             case "east":
848                 return [0, 0];
849             break;
850             case "north":
851                 return [0, 0];
852             break;
853             case "south":
854                 return [0, 0];
855             break;
856         }
857     },
858
859     // private
860     getExpandAdj : function(){
861         var c = this.collapsedEl, cm = this.cmargins;
862         switch(this.position){
863             case "west":
864                 return [-(cm.right+c.getWidth()+cm.left), 0];
865             break;
866             case "east":
867                 return [cm.right+c.getWidth()+cm.left, 0];
868             break;
869             case "north":
870                 return [0, -(cm.top+cm.bottom+c.getHeight())];
871             break;
872             case "south":
873                 return [0, cm.top+cm.bottom+c.getHeight()];
874             break;
875         }
876     },
877
878     destroy : function(){
879         Ext.destroy(this.miniCollapsedEl, this.collapsedEl);
880     }
881 };
882
883 /**
884  * @class Ext.layout.BorderLayout.SplitRegion
885  * @extends Ext.layout.BorderLayout.Region
886  * <p>This is a specialized type of {@link Ext.layout.BorderLayout.Region BorderLayout region} that
887  * has a built-in {@link Ext.SplitBar} for user resizing of regions.  The movement of the split bar
888  * is configurable to move either {@link #tickSize smooth or incrementally}.</p>
889  * @constructor
890  * Create a new SplitRegion.
891  * @param {Layout} layout The {@link Ext.layout.BorderLayout BorderLayout} instance that is managing this Region.
892  * @param {Object} config The configuration options
893  * @param {String} position The region position.  Valid values are: north, south, east, west and center.  Every
894  * BorderLayout must have a center region for the primary content -- all other regions are optional.
895  */
896 Ext.layout.BorderLayout.SplitRegion = function(layout, config, pos){
897     Ext.layout.BorderLayout.SplitRegion.superclass.constructor.call(this, layout, config, pos);
898     // prevent switch
899     this.applyLayout = this.applyFns[pos];
900 };
901
902 Ext.extend(Ext.layout.BorderLayout.SplitRegion, Ext.layout.BorderLayout.Region, {
903     /**
904      * @cfg {Number} tickSize
905      * The increment, in pixels by which to move this Region's {@link Ext.SplitBar SplitBar}.
906      * By default, the {@link Ext.SplitBar SplitBar} moves smoothly.
907      */
908     /**
909      * @cfg {String} splitTip
910      * The tooltip to display when the user hovers over a
911      * {@link Ext.layout.BorderLayout.Region#collapsible non-collapsible} region's split bar
912      * (defaults to <tt>"Drag to resize."</tt>).  Only applies if
913      * <tt>{@link #useSplitTips} = true</tt>.
914      */
915     splitTip : "Drag to resize.",
916     /**
917      * @cfg {String} collapsibleSplitTip
918      * The tooltip to display when the user hovers over a
919      * {@link Ext.layout.BorderLayout.Region#collapsible collapsible} region's split bar
920      * (defaults to "Drag to resize. Double click to hide."). Only applies if
921      * <tt>{@link #useSplitTips} = true</tt>.
922      */
923     collapsibleSplitTip : "Drag to resize. Double click to hide.",
924     /**
925      * @cfg {Boolean} useSplitTips
926      * <tt>true</tt> to display a tooltip when the user hovers over a region's split bar
927      * (defaults to <tt>false</tt>).  The tooltip text will be the value of either
928      * <tt>{@link #splitTip}</tt> or <tt>{@link #collapsibleSplitTip}</tt> as appropriate.
929      */
930     useSplitTips : false,
931
932     // private
933     splitSettings : {
934         north : {
935             orientation: Ext.SplitBar.VERTICAL,
936             placement: Ext.SplitBar.TOP,
937             maxFn : 'getVMaxSize',
938             minProp: 'minHeight',
939             maxProp: 'maxHeight'
940         },
941         south : {
942             orientation: Ext.SplitBar.VERTICAL,
943             placement: Ext.SplitBar.BOTTOM,
944             maxFn : 'getVMaxSize',
945             minProp: 'minHeight',
946             maxProp: 'maxHeight'
947         },
948         east : {
949             orientation: Ext.SplitBar.HORIZONTAL,
950             placement: Ext.SplitBar.RIGHT,
951             maxFn : 'getHMaxSize',
952             minProp: 'minWidth',
953             maxProp: 'maxWidth'
954         },
955         west : {
956             orientation: Ext.SplitBar.HORIZONTAL,
957             placement: Ext.SplitBar.LEFT,
958             maxFn : 'getHMaxSize',
959             minProp: 'minWidth',
960             maxProp: 'maxWidth'
961         }
962     },
963
964     // private
965     applyFns : {
966         west : function(box){
967             if(this.isCollapsed){
968                 return this.applyLayoutCollapsed(box);
969             }
970             var sd = this.splitEl.dom, s = sd.style;
971             this.panel.setPosition(box.x, box.y);
972             var sw = sd.offsetWidth;
973             s.left = (box.x+box.width-sw)+'px';
974             s.top = (box.y)+'px';
975             s.height = Math.max(0, box.height)+'px';
976             this.panel.setSize(box.width-sw, box.height);
977         },
978         east : function(box){
979             if(this.isCollapsed){
980                 return this.applyLayoutCollapsed(box);
981             }
982             var sd = this.splitEl.dom, s = sd.style;
983             var sw = sd.offsetWidth;
984             this.panel.setPosition(box.x+sw, box.y);
985             s.left = (box.x)+'px';
986             s.top = (box.y)+'px';
987             s.height = Math.max(0, box.height)+'px';
988             this.panel.setSize(box.width-sw, box.height);
989         },
990         north : function(box){
991             if(this.isCollapsed){
992                 return this.applyLayoutCollapsed(box);
993             }
994             var sd = this.splitEl.dom, s = sd.style;
995             var sh = sd.offsetHeight;
996             this.panel.setPosition(box.x, box.y);
997             s.left = (box.x)+'px';
998             s.top = (box.y+box.height-sh)+'px';
999             s.width = Math.max(0, box.width)+'px';
1000             this.panel.setSize(box.width, box.height-sh);
1001         },
1002         south : function(box){
1003             if(this.isCollapsed){
1004                 return this.applyLayoutCollapsed(box);
1005             }
1006             var sd = this.splitEl.dom, s = sd.style;
1007             var sh = sd.offsetHeight;
1008             this.panel.setPosition(box.x, box.y+sh);
1009             s.left = (box.x)+'px';
1010             s.top = (box.y)+'px';
1011             s.width = Math.max(0, box.width)+'px';
1012             this.panel.setSize(box.width, box.height-sh);
1013         }
1014     },
1015
1016     // private
1017     render : function(ct, p){
1018         Ext.layout.BorderLayout.SplitRegion.superclass.render.call(this, ct, p);
1019
1020         var ps = this.position;
1021
1022         this.splitEl = ct.createChild({
1023             cls: "x-layout-split x-layout-split-"+ps, html: "&#160;",
1024             id: this.panel.id + '-xsplit'
1025         });
1026
1027         if(this.collapseMode == 'mini'){
1028             this.miniSplitEl = this.splitEl.createChild({
1029                 cls: "x-layout-mini x-layout-mini-"+ps, html: "&#160;"
1030             });
1031             this.miniSplitEl.addClassOnOver('x-layout-mini-over');
1032             this.miniSplitEl.on('click', this.onCollapseClick, this, {stopEvent:true});
1033         }
1034
1035         var s = this.splitSettings[ps];
1036
1037         this.split = new Ext.SplitBar(this.splitEl.dom, p.el, s.orientation);
1038         this.split.tickSize = this.tickSize;
1039         this.split.placement = s.placement;
1040         this.split.getMaximumSize = this[s.maxFn].createDelegate(this);
1041         this.split.minSize = this.minSize || this[s.minProp];
1042         this.split.on("beforeapply", this.onSplitMove, this);
1043         this.split.useShim = this.useShim === true;
1044         this.maxSize = this.maxSize || this[s.maxProp];
1045
1046         if(p.hidden){
1047             this.splitEl.hide();
1048         }
1049
1050         if(this.useSplitTips){
1051             this.splitEl.dom.title = this.collapsible ? this.collapsibleSplitTip : this.splitTip;
1052         }
1053         if(this.collapsible){
1054             this.splitEl.on("dblclick", this.onCollapseClick,  this);
1055         }
1056     },
1057
1058     //docs inherit from superclass
1059     getSize : function(){
1060         if(this.isCollapsed){
1061             return this.collapsedEl.getSize();
1062         }
1063         var s = this.panel.getSize();
1064         if(this.position == 'north' || this.position == 'south'){
1065             s.height += this.splitEl.dom.offsetHeight;
1066         }else{
1067             s.width += this.splitEl.dom.offsetWidth;
1068         }
1069         return s;
1070     },
1071
1072     // private
1073     getHMaxSize : function(){
1074          var cmax = this.maxSize || 10000;
1075          var center = this.layout.center;
1076          return Math.min(cmax, (this.el.getWidth()+center.el.getWidth())-center.getMinWidth());
1077     },
1078
1079     // private
1080     getVMaxSize : function(){
1081         var cmax = this.maxSize || 10000;
1082         var center = this.layout.center;
1083         return Math.min(cmax, (this.el.getHeight()+center.el.getHeight())-center.getMinHeight());
1084     },
1085
1086     // private
1087     onSplitMove : function(split, newSize){
1088         var s = this.panel.getSize();
1089         this.lastSplitSize = newSize;
1090         if(this.position == 'north' || this.position == 'south'){
1091             this.panel.setSize(s.width, newSize);
1092             this.state.height = newSize;
1093         }else{
1094             this.panel.setSize(newSize, s.height);
1095             this.state.width = newSize;
1096         }
1097         this.layout.layout();
1098         this.panel.saveState();
1099         return false;
1100     },
1101
1102     /**
1103      * Returns a reference to the split bar in use by this region.
1104      * @return {Ext.SplitBar} The split bar
1105      */
1106     getSplitBar : function(){
1107         return this.split;
1108     },
1109
1110     // inherit docs
1111     destroy : function() {
1112         Ext.destroy(this.miniSplitEl, this.split, this.splitEl);
1113         Ext.layout.BorderLayout.SplitRegion.superclass.destroy.call(this);
1114     }
1115 });
1116
1117 Ext.Container.LAYOUTS['border'] = Ext.layout.BorderLayout;