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