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