commit extjs-2.2.1
[extjs.git] / source / widgets / Window.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.Window\r
11  * @extends Ext.Panel\r
12  * A specialized panel intended for use as an application window.  Windows are floated and draggable by default, and\r
13  * also provide specific behavior like the ability to maximize and restore (with an event for minimizing, since the\r
14  * minimize behavior is application-specific).  Windows can also be linked to a {@link Ext.WindowGroup} or managed\r
15  * by the {@link Ext.WindowMgr} to provide grouping, activation, to front/back and other application-specific behavior.\r
16  * @constructor\r
17  * @param {Object} config The config object\r
18  */\r
19 Ext.Window = Ext.extend(Ext.Panel, {\r
20     /**\r
21      * @cfg {Number} x\r
22      * The X position of the left edge of the Window on initial showing. Defaults to centering the Window within\r
23      * the width of the Window's container {@link Ext.Element Element) (The Element that the Window is rendered to).\r
24      */\r
25     /**\r
26      * @cfg {Number} y\r
27      * The Y position of the top edge of the Window on initial showing. Defaults to centering the Window within\r
28      * the height of the Window's container {@link Ext.Element Element) (The Element that the Window is rendered to).\r
29      */\r
30     /**\r
31      * @cfg {Boolean} modal\r
32      * True to make the window modal and mask everything behind it when displayed, false to display it without\r
33      * restricting access to other UI elements (defaults to false).\r
34      */\r
35     /**\r
36      * @cfg {String/Element} animateTarget\r
37      * Id or element from which the window should animate while opening (defaults to null with no animation).\r
38      */\r
39     /**\r
40      * @cfg {String} resizeHandles\r
41      * A valid {@link Ext.Resizable} handles config string (defaults to 'all').  Only applies when resizable = true.\r
42      */\r
43     /**\r
44      * @cfg {Ext.WindowGroup} manager\r
45      * A reference to the WindowGroup that should manage this window (defaults to {@link Ext.WindowMgr}).\r
46      */\r
47     /**\r
48     * @cfg {String/Number/Button} defaultButton\r
49     * The id / index of a button or a button instance to focus when this window received the focus.\r
50     */\r
51     /**\r
52     * @cfg {Function} onEsc\r
53     * Allows override of the built-in processing for the escape key. Default action\r
54     * is to close the Window (performing whatever action is specified in {@link #closeAction}.\r
55     * To prevent the Window closing when the escape key is pressed, specify this as\r
56     * Ext.emptyFn (See {@link Ext#emptyFn}).\r
57     */\r
58     /**\r
59      * @cfg {Boolean} collapsed\r
60      * True to render the window collapsed, false to render it expanded (defaults to false). Note that if \r
61      * {@link #expandOnShow} is true (the default) it will override the <tt>collapsed</tt> config and the window \r
62      * will always be expanded when shown.\r
63      */\r
64     /**\r
65      * @cfg {Boolean} maximized\r
66      * True to initially display the window in a maximized state. (Defaults to false).\r
67      */\r
68     \r
69     /**\r
70     * @cfg {String} baseCls\r
71     * The base CSS class to apply to this panel's element (defaults to 'x-window').\r
72     */\r
73     baseCls : 'x-window',\r
74     /**\r
75      * @cfg {Boolean} resizable\r
76      * True to allow user resizing at each edge and corner of the window, false to disable resizing (defaults to true).\r
77      */\r
78     resizable:true,\r
79     /**\r
80      * @cfg {Boolean} draggable\r
81      * True to allow the window to be dragged by the header bar, false to disable dragging (defaults to true).  Note\r
82      * that by default the window will be centered in the viewport, so if dragging is disabled the window may need\r
83      * to be positioned programmatically after render (e.g., myWindow.setPosition(100, 100);).\r
84      */\r
85     draggable:true,\r
86     /**\r
87      * @cfg {Boolean} closable\r
88      * <p>True to display the 'close' tool button and allow the user to close the window, false to\r
89      * hide the button and disallow closing the window (default to true).</p>\r
90      * <p>By default, when close is requested by either clicking the close button in the header\r
91      * or pressing ESC when the Window has focus, the {@link #close} method will be called. This\r
92      * will <i>destroy</i> the Window and its content meaning that it may not be reused.</p>\r
93      * <p>To make closing a Window <i>hide</i> the Window so that it may be reused, set\r
94      * {@link #closeAction} to 'hide'.\r
95      */\r
96     closable : true,\r
97     /**\r
98      * @cfg {Boolean} constrain\r
99      * True to constrain the window to the viewport, false to allow it to fall outside of the viewport\r
100      * (defaults to false).  Optionally the header only can be constrained using {@link #constrainHeader}.\r
101      */\r
102     constrain:false,\r
103     /**\r
104      * @cfg {Boolean} constrainHeader\r
105      * True to constrain the window header to the viewport, allowing the window body to fall outside of the viewport,\r
106      * false to allow the header to fall outside the viewport (defaults to false).  Optionally the entire window\r
107      * can be constrained using {@link #constrain}.\r
108      */\r
109     constrainHeader:false,\r
110     /**\r
111      * @cfg {Boolean} plain\r
112      * True to render the window body with a transparent background so that it will blend into the framing\r
113      * elements, false to add a lighter background color to visually highlight the body element and separate it\r
114      * more distinctly from the surrounding frame (defaults to false).\r
115      */\r
116     plain:false,\r
117     /**\r
118      * @cfg {Boolean} minimizable\r
119      * True to display the 'minimize' tool button and allow the user to minimize the window, false to hide the button\r
120      * and disallow minimizing the window (defaults to false).  Note that this button provides no implementation --\r
121      * the behavior of minimizing a window is implementation-specific, so the minimize event must be handled and a\r
122      * custom minimize behavior implemented for this option to be useful.\r
123      */\r
124     minimizable : false,\r
125     /**\r
126      * @cfg {Boolean} maximizable\r
127      * True to display the 'maximize' tool button and allow the user to maximize the window, false to hide the button\r
128      * and disallow maximizing the window (defaults to false).  Note that when a window is maximized, the tool button\r
129      * will automatically change to a 'restore' button with the appropriate behavior already built-in that will\r
130      * restore the window to its previous size.\r
131      */\r
132     maximizable : false,\r
133     /**\r
134      * @cfg {Number} minHeight\r
135      * The minimum height in pixels allowed for this window (defaults to 100).  Only applies when resizable = true.\r
136      */\r
137     minHeight: 100,\r
138     /**\r
139      * @cfg {Number} minWidth\r
140      * The minimum width in pixels allowed for this window (defaults to 200).  Only applies when resizable = true.\r
141      */\r
142     minWidth: 200,\r
143     /**\r
144      * @cfg {Boolean} expandOnShow\r
145      * True to always expand the window when it is displayed, false to keep it in its current state (which may be\r
146      * {@link #collapsed}) when displayed (defaults to true).\r
147      */\r
148     expandOnShow: true,\r
149     /**\r
150      * @cfg {String} closeAction\r
151      * The action to take when the close button is clicked.  The default action is 'close' which will actually remove\r
152      * the window from the DOM and destroy it.  The other valid option is 'hide' which will simply hide the window\r
153      * by setting visibility to hidden and applying negative offsets, keeping the window available to be redisplayed\r
154      * via the {@link #show} method.\r
155      */\r
156     closeAction: 'close',\r
157     /**\r
158      * @cfg {String} elements\r
159      * A comma-delimited list of panel elements to initialize when the window is rendered.  Normally, this list will be\r
160      * generated automatically based on the items added to the window at config time, but sometimes it might be useful to\r
161      * make sure a structural element is rendered even if not specified at config time (for example, you may want\r
162      * to add a button or toolbar dynamically after the window has been rendered).  Adding those elements to this\r
163      * list will allocate the required placeholders in the window when it is rendered.  Valid values are<ul>\r
164      * <li><b>header</b> (required)</li>\r
165      * <li><b>tbar</b> (top bar)</li>\r
166      * <li><b>body</b> (required)</li>\r
167      * <li><b>bbar</b> (bottom bar)</li>\r
168      * <li><b>footer</b><li>\r
169      * </ul>\r
170      * Defaults to 'header,body'.\r
171      */\r
172     elements: 'header,body',\r
173 \r
174     // inherited docs, same default\r
175     collapsible:false,\r
176 \r
177     // private\r
178     initHidden : true,\r
179     /**\r
180     * @cfg {Boolean} monitorResize @hide\r
181     * This is automatically managed based on the value of constrain and constrainToHeader\r
182     */\r
183     monitorResize : true,\r
184     /** @cfg {Boolean} frame @hide */\r
185     frame:true,\r
186     /** @cfg {Boolean} floating @hide */\r
187     floating:true,\r
188 \r
189     // private\r
190     initComponent : function(){\r
191         Ext.Window.superclass.initComponent.call(this);\r
192         this.addEvents(\r
193             /**\r
194              * @event activate\r
195              * Fires after the window has been visually activated via {@link setActive}.\r
196              * @param {Ext.Window} this\r
197              */\r
198             /**\r
199              * @event deactivate\r
200              * Fires after the window has been visually deactivated via {@link setActive}.\r
201              * @param {Ext.Window} this\r
202              */\r
203             /**\r
204              * @event resize\r
205              * Fires after the window has been resized.\r
206              * @param {Ext.Window} this\r
207              * @param {Number} width The window's new width\r
208              * @param {Number} height The window's new height\r
209              */\r
210             'resize',\r
211             /**\r
212              * @event maximize\r
213              * Fires after the window has been maximized.\r
214              * @param {Ext.Window} this\r
215              */\r
216             'maximize',\r
217             /**\r
218              * @event minimize\r
219              * Fires after the window has been minimized.\r
220              * @param {Ext.Window} this\r
221              */\r
222             'minimize',\r
223             /**\r
224              * @event restore\r
225              * Fires after the window has been restored to its original size after being maximized.\r
226              * @param {Ext.Window} this\r
227              */\r
228             'restore'\r
229         );\r
230     },\r
231 \r
232     // private\r
233     getState : function(){\r
234         return Ext.apply(Ext.Window.superclass.getState.call(this) || {}, this.getBox());\r
235     },\r
236 \r
237     // private\r
238     onRender : function(ct, position){\r
239         Ext.Window.superclass.onRender.call(this, ct, position);\r
240 \r
241         if(this.plain){\r
242             this.el.addClass('x-window-plain');\r
243         }\r
244 \r
245         // this element allows the Window to be focused for keyboard events\r
246         this.focusEl = this.el.createChild({\r
247                     tag: "a", href:"#", cls:"x-dlg-focus",\r
248                     tabIndex:"-1", html: "&#160;"});\r
249         this.focusEl.swallowEvent('click', true);\r
250 \r
251         this.proxy = this.el.createProxy("x-window-proxy");\r
252         this.proxy.enableDisplayMode('block');\r
253 \r
254         if(this.modal){\r
255             this.mask = this.container.createChild({cls:"ext-el-mask"}, this.el.dom);\r
256             this.mask.enableDisplayMode("block");\r
257             this.mask.hide();\r
258             this.mask.on('click', this.focus, this);\r
259         }\r
260     },\r
261 \r
262     // private\r
263     initEvents : function(){\r
264         Ext.Window.superclass.initEvents.call(this);\r
265         if(this.animateTarget){\r
266             this.setAnimateTarget(this.animateTarget);\r
267         }\r
268 \r
269         if(this.resizable){\r
270             this.resizer = new Ext.Resizable(this.el, {\r
271                 minWidth: this.minWidth,\r
272                 minHeight:this.minHeight,\r
273                 handles: this.resizeHandles || "all",\r
274                 pinned: true,\r
275                 resizeElement : this.resizerAction\r
276             });\r
277             this.resizer.window = this;\r
278             this.resizer.on("beforeresize", this.beforeResize, this);\r
279         }\r
280 \r
281         if(this.draggable){\r
282             this.header.addClass("x-window-draggable");\r
283         }\r
284         this.initTools();\r
285 \r
286         this.el.on("mousedown", this.toFront, this);\r
287         this.manager = this.manager || Ext.WindowMgr;\r
288         this.manager.register(this);\r
289         this.hidden = true;\r
290         if(this.maximized){\r
291             this.maximized = false;\r
292             this.maximize();\r
293         }\r
294         if(this.closable){\r
295             var km = this.getKeyMap();\r
296             km.on(27, this.onEsc, this);\r
297             km.disable();\r
298         }\r
299     },\r
300 \r
301     initDraggable : function(){\r
302         /**\r
303          * If this Window is configured {@link #draggable}, this property will contain\r
304          * an instance of {@link Ext.dd.DD} which handles dragging the Window's DOM Element.\r
305          * @type Ext.dd.DD\r
306          * @property dd\r
307          */\r
308         this.dd = new Ext.Window.DD(this);\r
309     },\r
310 \r
311    // private\r
312     onEsc : function(){\r
313         this[this.closeAction]();\r
314     },\r
315 \r
316     // private\r
317     beforeDestroy : function(){\r
318         this.hide();\r
319         if(this.doAnchor){\r
320             Ext.EventManager.removeResizeListener(this.doAnchor, this);\r
321             Ext.EventManager.un(window, 'scroll', this.doAnchor, this);\r
322         }\r
323         Ext.destroy(\r
324             this.focusEl,\r
325             this.resizer,\r
326             this.dd,\r
327             this.proxy,\r
328             this.mask\r
329         );\r
330         Ext.Window.superclass.beforeDestroy.call(this);\r
331     },\r
332 \r
333     // private\r
334     onDestroy : function(){\r
335         if(this.manager){\r
336             this.manager.unregister(this);\r
337         }\r
338         Ext.Window.superclass.onDestroy.call(this);\r
339     },\r
340 \r
341     // private\r
342     initTools : function(){\r
343         if(this.minimizable){\r
344             this.addTool({\r
345                 id: 'minimize',\r
346                 handler: this.minimize.createDelegate(this, [])\r
347             });\r
348         }\r
349         if(this.maximizable){\r
350             this.addTool({\r
351                 id: 'maximize',\r
352                 handler: this.maximize.createDelegate(this, [])\r
353             });\r
354             this.addTool({\r
355                 id: 'restore',\r
356                 handler: this.restore.createDelegate(this, []),\r
357                 hidden:true\r
358             });\r
359             this.header.on('dblclick', this.toggleMaximize, this);\r
360         }\r
361         if(this.closable){\r
362             this.addTool({\r
363                 id: 'close',\r
364                 handler: this[this.closeAction].createDelegate(this, [])\r
365             });\r
366         }\r
367     },\r
368 \r
369     // private\r
370     resizerAction : function(){\r
371         var box = this.proxy.getBox();\r
372         this.proxy.hide();\r
373         this.window.handleResize(box);\r
374         return box;\r
375     },\r
376 \r
377     // private\r
378     beforeResize : function(){\r
379         this.resizer.minHeight = Math.max(this.minHeight, this.getFrameHeight() + 40); // 40 is a magic minimum content size?\r
380         this.resizer.minWidth = Math.max(this.minWidth, this.getFrameWidth() + 40);\r
381         this.resizeBox = this.el.getBox();\r
382     },\r
383 \r
384     // private\r
385     updateHandles : function(){\r
386         if(Ext.isIE && this.resizer){\r
387             this.resizer.syncHandleHeight();\r
388             this.el.repaint();\r
389         }\r
390     },\r
391 \r
392     // private\r
393     handleResize : function(box){\r
394         var rz = this.resizeBox;\r
395         if(rz.x != box.x || rz.y != box.y){\r
396             this.updateBox(box);\r
397         }else{\r
398             this.setSize(box);\r
399         }\r
400         this.focus();\r
401         this.updateHandles();\r
402         this.saveState();\r
403         if(this.layout){\r
404             this.doLayout();\r
405         }\r
406         this.fireEvent("resize", this, box.width, box.height);\r
407     },\r
408 \r
409     /**\r
410      * Focuses the window.  If a defaultButton is set, it will receive focus, otherwise the\r
411      * window itself will receive focus.\r
412      */\r
413     focus : function(){\r
414         var f = this.focusEl, db = this.defaultButton, t = typeof db;\r
415         if(t != 'undefined'){\r
416             if(t == 'number'){\r
417                 f = this.buttons[db];\r
418             }else if(t == 'string'){\r
419                 f = Ext.getCmp(db);\r
420             }else{\r
421                 f = db;\r
422             }\r
423         }\r
424         f.focus.defer(10, f);\r
425     },\r
426 \r
427     /**\r
428      * Sets the target element from which the window should animate while opening.\r
429      * @param {String/Element} el The target element or id\r
430      */\r
431     setAnimateTarget : function(el){\r
432         el = Ext.get(el);\r
433         this.animateTarget = el;\r
434     },\r
435 \r
436     // private\r
437     beforeShow : function(){\r
438         delete this.el.lastXY;\r
439         delete this.el.lastLT;\r
440         if(this.x === undefined || this.y === undefined){\r
441             var xy = this.el.getAlignToXY(this.container, 'c-c');\r
442             var pos = this.el.translatePoints(xy[0], xy[1]);\r
443             this.x = this.x === undefined? pos.left : this.x;\r
444             this.y = this.y === undefined? pos.top : this.y;\r
445         }\r
446         this.el.setLeftTop(this.x, this.y);\r
447 \r
448         if(this.expandOnShow){\r
449             this.expand(false);\r
450         }\r
451 \r
452         if(this.modal){\r
453             Ext.getBody().addClass("x-body-masked");\r
454             this.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));\r
455             this.mask.show();\r
456         }\r
457     },\r
458 \r
459     /**\r
460      * Shows the window, rendering it first if necessary, or activates it and brings it to front if hidden.\r
461      * @param {String/Element} animateTarget (optional) The target element or id from which the window should\r
462      * animate while opening (defaults to undefined with no animation)\r
463      * @param {Function} callback (optional) A callback function to call after the window is displayed\r
464      * @param {Object} scope (optional) The scope in which to execute the callback\r
465      */\r
466     show : function(animateTarget, cb, scope){\r
467         if(!this.rendered){\r
468             this.render(Ext.getBody());\r
469         }\r
470         if(this.hidden === false){\r
471             this.toFront();\r
472             return;\r
473         }\r
474         if(this.fireEvent("beforeshow", this) === false){\r
475             return;\r
476         }\r
477         if(cb){\r
478             this.on('show', cb, scope, {single:true});\r
479         }\r
480         this.hidden = false;\r
481         if(animateTarget !== undefined){\r
482             this.setAnimateTarget(animateTarget);\r
483         }\r
484         this.beforeShow();\r
485         if(this.animateTarget){\r
486             this.animShow();\r
487         }else{\r
488             this.afterShow();\r
489         }\r
490     },\r
491 \r
492     // private\r
493     afterShow : function(){\r
494         this.proxy.hide();\r
495         this.el.setStyle('display', 'block');\r
496         this.el.show();\r
497         if(this.maximized){\r
498             this.fitContainer();\r
499         }\r
500         if(Ext.isMac && Ext.isGecko){ // work around stupid FF 2.0/Mac scroll bar bug\r
501                 this.cascade(this.setAutoScroll);\r
502         }\r
503 \r
504         if(this.monitorResize || this.modal || this.constrain || this.constrainHeader){\r
505             Ext.EventManager.onWindowResize(this.onWindowResize, this);\r
506         }\r
507         this.doConstrain();\r
508         if(this.layout){\r
509             this.doLayout();\r
510         }\r
511         if(this.keyMap){\r
512             this.keyMap.enable();\r
513         }\r
514         this.toFront();\r
515         this.updateHandles();\r
516         this.fireEvent("show", this);\r
517     },\r
518 \r
519     // private\r
520     animShow : function(){\r
521         this.proxy.show();\r
522         this.proxy.setBox(this.animateTarget.getBox());\r
523         this.proxy.setOpacity(0);\r
524         var b = this.getBox(false);\r
525         b.callback = this.afterShow;\r
526         b.scope = this;\r
527         b.duration = .25;\r
528         b.easing = 'easeNone';\r
529         b.opacity = .5;\r
530         b.block = true;\r
531         this.el.setStyle('display', 'none');\r
532         this.proxy.shift(b);\r
533     },\r
534 \r
535     /**\r
536      * Hides the window, setting it to invisible and applying negative offsets.\r
537      * @param {String/Element} animateTarget (optional) The target element or id to which the window should\r
538      * animate while hiding (defaults to null with no animation)\r
539      * @param {Function} callback (optional) A callback function to call after the window is hidden\r
540      * @param {Object} scope (optional) The scope in which to execute the callback\r
541      */\r
542     hide : function(animateTarget, cb, scope){\r
543         if(this.activeGhost){ // drag active?\r
544             this.hide.defer(100, this, [animateTarget, cb, scope]);\r
545             return;\r
546         }\r
547         if(this.hidden || this.fireEvent("beforehide", this) === false){\r
548             return;\r
549         }\r
550         if(cb){\r
551             this.on('hide', cb, scope, {single:true});\r
552         }\r
553         this.hidden = true;\r
554         if(animateTarget !== undefined){\r
555             this.setAnimateTarget(animateTarget);\r
556         }\r
557         if(this.animateTarget){\r
558             this.animHide();\r
559         }else{\r
560             this.el.hide();\r
561             this.afterHide();\r
562         }\r
563     },\r
564 \r
565     // private\r
566     afterHide : function(){\r
567         this.proxy.hide();\r
568         if(this.monitorResize || this.modal || this.constrain || this.constrainHeader){\r
569             Ext.EventManager.removeResizeListener(this.onWindowResize, this);\r
570         }\r
571         if(this.modal){\r
572             this.mask.hide();\r
573             Ext.getBody().removeClass("x-body-masked");\r
574         }\r
575         if(this.keyMap){\r
576             this.keyMap.disable();\r
577         }\r
578         this.fireEvent("hide", this);\r
579     },\r
580 \r
581     // private\r
582     animHide : function(){\r
583         this.proxy.setOpacity(.5);\r
584         this.proxy.show();\r
585         var tb = this.getBox(false);\r
586         this.proxy.setBox(tb);\r
587         this.el.hide();\r
588         var b = this.animateTarget.getBox();\r
589         b.callback = this.afterHide;\r
590         b.scope = this;\r
591         b.duration = .25;\r
592         b.easing = 'easeNone';\r
593         b.block = true;\r
594         b.opacity = 0;\r
595         this.proxy.shift(b);\r
596     },\r
597 \r
598     // private\r
599     onWindowResize : function(){\r
600         if(this.maximized){\r
601             this.fitContainer();\r
602         }\r
603         if(this.modal){\r
604             this.mask.setSize('100%', '100%');\r
605             var force = this.mask.dom.offsetHeight;\r
606             this.mask.setSize(Ext.lib.Dom.getViewWidth(true), Ext.lib.Dom.getViewHeight(true));\r
607         }\r
608         this.doConstrain();\r
609     },\r
610 \r
611     // private\r
612     doConstrain : function(){\r
613         if(this.constrain || this.constrainHeader){\r
614             var offsets;\r
615             if(this.constrain){\r
616                 offsets = {\r
617                     right:this.el.shadowOffset,\r
618                     left:this.el.shadowOffset,\r
619                     bottom:this.el.shadowOffset\r
620                 };\r
621             }else {\r
622                 var s = this.getSize();\r
623                 offsets = {\r
624                     right:-(s.width - 100),\r
625                     bottom:-(s.height - 25)\r
626                 };\r
627             }\r
628 \r
629             var xy = this.el.getConstrainToXY(this.container, true, offsets);\r
630             if(xy){\r
631                 this.setPosition(xy[0], xy[1]);\r
632             }\r
633         }\r
634     },\r
635 \r
636     // private - used for dragging\r
637     ghost : function(cls){\r
638         var ghost = this.createGhost(cls);\r
639         var box = this.getBox(true);\r
640         ghost.setLeftTop(box.x, box.y);\r
641         ghost.setWidth(box.width);\r
642         this.el.hide();\r
643         this.activeGhost = ghost;\r
644         return ghost;\r
645     },\r
646 \r
647     // private\r
648     unghost : function(show, matchPosition){\r
649         if(show !== false){\r
650             this.el.show();\r
651             this.focus();\r
652                 if(Ext.isMac && Ext.isGecko){ // work around stupid FF 2.0/Mac scroll bar bug\r
653                         this.cascade(this.setAutoScroll);\r
654                 }\r
655         }\r
656         if(matchPosition !== false){\r
657             this.setPosition(this.activeGhost.getLeft(true), this.activeGhost.getTop(true));\r
658         }\r
659         this.activeGhost.hide();\r
660         this.activeGhost.remove();\r
661         delete this.activeGhost;\r
662     },\r
663 \r
664     /**\r
665      * Placeholder method for minimizing the window.  By default, this method simply fires the {@link #minimize} event\r
666      * since the behavior of minimizing a window is application-specific.  To implement custom minimize behavior,\r
667      * either the minimize event can be handled or this method can be overridden.\r
668      */\r
669     minimize : function(){\r
670         this.fireEvent('minimize', this);\r
671     },\r
672 \r
673     /**\r
674      * Closes the window, removes it from the DOM and destroys the window object.  The beforeclose event is fired\r
675      * before the close happens and will cancel the close action if it returns false.\r
676      */\r
677     close : function(){\r
678         if(this.fireEvent("beforeclose", this) !== false){\r
679             this.hide(null, function(){\r
680                 this.fireEvent('close', this);\r
681                 this.destroy();\r
682             }, this);\r
683         }\r
684     },\r
685 \r
686     /**\r
687      * Fits the window within its current container and automatically replaces the 'maximize' tool button with\r
688      * the 'restore' tool button.\r
689      */\r
690     maximize : function(){\r
691         if(!this.maximized){\r
692             this.expand(false);\r
693             this.restoreSize = this.getSize();\r
694             this.restorePos = this.getPosition(true);\r
695             if (this.maximizable){\r
696                 this.tools.maximize.hide();\r
697                 this.tools.restore.show();\r
698             }\r
699             this.maximized = true;\r
700             this.el.disableShadow();\r
701 \r
702             if(this.dd){\r
703                 this.dd.lock();\r
704             }\r
705             if(this.collapsible){\r
706                 this.tools.toggle.hide();\r
707             }\r
708             this.el.addClass('x-window-maximized');\r
709             this.container.addClass('x-window-maximized-ct');\r
710 \r
711             this.setPosition(0, 0);\r
712             this.fitContainer();\r
713             this.fireEvent('maximize', this);\r
714         }\r
715     },\r
716 \r
717     /**\r
718      * Restores a maximized window back to its original size and position prior to being maximized and also replaces\r
719      * the 'restore' tool button with the 'maximize' tool button.\r
720      */\r
721     restore : function(){\r
722         if(this.maximized){\r
723             this.el.removeClass('x-window-maximized');\r
724             this.tools.restore.hide();\r
725             this.tools.maximize.show();\r
726             this.setPosition(this.restorePos[0], this.restorePos[1]);\r
727             this.setSize(this.restoreSize.width, this.restoreSize.height);\r
728             delete this.restorePos;\r
729             delete this.restoreSize;\r
730             this.maximized = false;\r
731             this.el.enableShadow(true);\r
732 \r
733             if(this.dd){\r
734                 this.dd.unlock();\r
735             }\r
736             if(this.collapsible){\r
737                 this.tools.toggle.show();\r
738             }\r
739             this.container.removeClass('x-window-maximized-ct');\r
740 \r
741             this.doConstrain();\r
742             this.fireEvent('restore', this);\r
743         }\r
744     },\r
745 \r
746     /**\r
747      * A shortcut method for toggling between {@link #maximize} and {@link #restore} based on the current maximized\r
748      * state of the window.\r
749      */\r
750     toggleMaximize : function(){\r
751         this[this.maximized ? 'restore' : 'maximize']();\r
752     },\r
753 \r
754     // private\r
755     fitContainer : function(){\r
756         var vs = this.container.getViewSize();\r
757         this.setSize(vs.width, vs.height);\r
758     },\r
759 \r
760     // private\r
761     // z-index is managed by the WindowManager and may be overwritten at any time\r
762     setZIndex : function(index){\r
763         if(this.modal){\r
764             this.mask.setStyle("z-index", index);\r
765         }\r
766         this.el.setZIndex(++index);\r
767         index += 5;\r
768 \r
769         if(this.resizer){\r
770             this.resizer.proxy.setStyle("z-index", ++index);\r
771         }\r
772 \r
773         this.lastZIndex = index;\r
774     },\r
775 \r
776     /**\r
777      * Aligns the window to the specified element\r
778      * @param {Mixed} element The element to align to.\r
779      * @param {String} position The position to align to (see {@link Ext.Element#alignTo} for more details).\r
780      * @param {Array} offsets (optional) Offset the positioning by [x, y]\r
781      * @return {Ext.Window} this\r
782      */\r
783     alignTo : function(element, position, offsets){\r
784         var xy = this.el.getAlignToXY(element, position, offsets);\r
785         this.setPagePosition(xy[0], xy[1]);\r
786         return this;\r
787     },\r
788 \r
789     /**\r
790      * Anchors this window to another element and realigns it when the window is resized or scrolled.\r
791      * @param {Mixed} element The element to align to.\r
792      * @param {String} position The position to align to (see {@link Ext.Element#alignTo} for more details)\r
793      * @param {Array} offsets (optional) Offset the positioning by [x, y]\r
794      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter\r
795      * is a number, it is used as the buffer delay (defaults to 50ms).\r
796      * @return {Ext.Window} this\r
797      */\r
798     anchorTo : function(el, alignment, offsets, monitorScroll){\r
799       if(this.doAnchor){\r
800           Ext.EventManager.removeResizeListener(this.doAnchor, this);\r
801           Ext.EventManager.un(window, 'scroll', this.doAnchor, this);\r
802       }\r
803       this.doAnchor = function(){\r
804           this.alignTo(el, alignment, offsets);\r
805       };\r
806       Ext.EventManager.onWindowResize(this.doAnchor, this);\r
807       \r
808       var tm = typeof monitorScroll;\r
809       if(tm != 'undefined'){\r
810           Ext.EventManager.on(window, 'scroll', this.doAnchor, this,\r
811               {buffer: tm == 'number' ? monitorScroll : 50});\r
812       }\r
813       this.doAnchor();\r
814       return this;\r
815     },\r
816 \r
817     /**\r
818      * Brings this window to the front of any other visible windows\r
819      * @return {Ext.Window} this\r
820      */\r
821     toFront : function(e){\r
822         if(this.manager.bringToFront(this)){\r
823             if(!e || !e.getTarget().focus){\r
824                 this.focus();\r
825             }\r
826         }\r
827         return this;\r
828     },\r
829 \r
830     /**\r
831      * Makes this the active window by showing its shadow, or deactivates it by hiding its shadow.  This method also\r
832      * fires the {@link #activate} or {@link #deactivate} event depending on which action occurred.\r
833      * @param {Boolean} active True to activate the window, false to deactivate it (defaults to false)\r
834      */\r
835     setActive : function(active){\r
836         if(active){\r
837             if(!this.maximized){\r
838                 this.el.enableShadow(true);\r
839             }\r
840             this.fireEvent('activate', this);\r
841         }else{\r
842             this.el.disableShadow();\r
843             this.fireEvent('deactivate', this);\r
844         }\r
845     },\r
846 \r
847     /**\r
848      * Sends this window to the back of (lower z-index than) any other visible windows\r
849      * @return {Ext.Window} this\r
850      */\r
851     toBack : function(){\r
852         this.manager.sendToBack(this);\r
853         return this;\r
854     },\r
855 \r
856     /**\r
857      * Centers this window in the viewport\r
858      * @return {Ext.Window} this\r
859      */\r
860     center : function(){\r
861         var xy = this.el.getAlignToXY(this.container, 'c-c');\r
862         this.setPagePosition(xy[0], xy[1]);\r
863         return this;\r
864     }\r
865 \r
866     /**\r
867      * @cfg {Boolean} autoWidth @hide\r
868      **/\r
869 });\r
870 Ext.reg('window', Ext.Window);\r
871 \r
872 // private - custom Window DD implementation\r
873 Ext.Window.DD = function(win){\r
874     this.win = win;\r
875     Ext.Window.DD.superclass.constructor.call(this, win.el.id, 'WindowDD-'+win.id);\r
876     this.setHandleElId(win.header.id);\r
877     this.scroll = false;\r
878 };\r
879 \r
880 Ext.extend(Ext.Window.DD, Ext.dd.DD, {\r
881     moveOnly:true,\r
882     headerOffsets:[100, 25],\r
883     startDrag : function(){\r
884         var w = this.win;\r
885         this.proxy = w.ghost();\r
886         if(w.constrain !== false){\r
887             var so = w.el.shadowOffset;\r
888             this.constrainTo(w.container, {right: so, left: so, bottom: so});\r
889         }else if(w.constrainHeader !== false){\r
890             var s = this.proxy.getSize();\r
891             this.constrainTo(w.container, {right: -(s.width-this.headerOffsets[0]), bottom: -(s.height-this.headerOffsets[1])});\r
892         }\r
893     },\r
894     b4Drag : Ext.emptyFn,\r
895 \r
896     onDrag : function(e){\r
897         this.alignElWithMouse(this.proxy, e.getPageX(), e.getPageY());\r
898     },\r
899 \r
900     endDrag : function(e){\r
901         this.win.unghost();\r
902         this.win.saveState();\r
903     }\r
904 });\r