/**
 * A specialized panel intended for use as an application window. Windows are floated, {@link #resizable}, and
 * {@link #cfg-draggable} by default. Windows can be {@link #maximizable maximized} to fill the viewport, restored to
 * their prior size, and can be {@link #method-minimize}d.
 *
 * Windows can also be linked to a {@link Ext.ZIndexManager} or managed by the {@link Ext.WindowManager} to provide
 * grouping, activation, to front, to back and other application-specific behavior.
 *
 * By default, Windows will be rendered to document.body. To {@link #constrain} a Window to another element specify
 * {@link Ext.Component#renderTo renderTo}.
 *
 * **As with all {@link Ext.container.Container Container}s, it is important to consider how you want the Window to size
 * and arrange any child Components. Choose an appropriate {@link #layout} configuration which lays out child Components
 * in the required manner.**
 *
 *     @example
 *     Ext.create('Ext.window.Window', {
 *         title: 'Hello',
 *         height: 200,
 *         width: 400,
 *         layout: 'fit',
 *         items: {  // Let's put an empty grid in just to illustrate fit layout
 *             xtype: 'grid',
 *             border: false,
 *             columns: [{header: 'World'}],                 // One header just for show. There's no data,
 *             store: Ext.create('Ext.data.ArrayStore', {}) // A dummy empty data store
 *         }
 *     }).show();
 */
Ext.define('Ext.window.Window', {
    extend: 'Ext.panel.Panel',
 
    alternateClassName: 'Ext.Window',
 
    requires: [
        'Ext.util.ComponentDragger',
        'Ext.util.Region'
    ],
 
    alias: 'widget.window',
 
    /**
     * @cfg {Number} x 
     * The X position of the left edge of the window on initial showing. Defaults to centering the Window within the
     * width of the Window's container {@link Ext.dom.Element Element} (The Element that the Window is rendered to).
     */
 
    /**
     * @cfg {Number} y 
     * The Y position of the top edge of the window on initial showing. Defaults to centering the Window within the
     * height of the Window's container {@link Ext.dom.Element Element} (The Element that the Window is rendered to).
     */
 
    /**
     * @cfg {String/Ext.dom.Element/Ext.Component/Boolean} [animateTarget=null]
     * Id, Component element, or Component from which the window should animate when
     * shown or hidden.
     *
     * You may also pass true to have the Window animate when maximizing and restoring
     * using the maximize / restore tools created via the {@link #maximizable} config.
     *
     *     var btn, win;
     *
     *     btn = Ext.create({
     *         xtype: 'button',
     *         renderTo: Ext.getBody(),
     *         text: 'Show Window',
     *         handler: function() {
     *             win.show();
     *         }
     *     });
     *
     *     win = Ext.create({
     *         xtype: 'window',
     *         title: 'Animate from the Show Window Button',
     *         height: 300,
     *         width: 400,
     *         modal: true,
     *         closeAction: 'hide',
     *         animateTarget: btn
     *         // or btn.getId()
     *         // or btn.getEl()
     *         // or true (when maximizable is true)
     *     });
     */
 
    /**
     * @cfg {Boolean/Function} ghost
     * Set to false to disable the ghost panel during dragging the window.
     * Do note that you should not set this to true, by default it is a function.
     */
 
    /**
     * @cfg {String/Number/Ext.Component} defaultFocus
     * Specifies a Component to receive focus when this Window is focused.
     *
     * If a String is provided, the Component will be resolved using the {@link #down} method which uses {@link Ext.ComponentQuery}.
     * If the string begins with an alphanumeric value, it will first attempt to find the Component based on the {@link Ext.Component#id} or {@link Ext.Component#itemId}.
     * If a matching component is not found via id, then an attempt to do a query to find a matching component.
     *
     * An example of finding the Component with an id/itemId:
     *
     *     Ext.create('Ext.window.Window', {
     *         autoShow     : true,
     *         width        : 300,
     *         title        : 'Login',
     *         defaultFocus : 'username',
     *         items        : [
     *             {
     *                 xtype      : 'textfield',
     *                 fieldLabel : 'Username',
     *                 itemId     : 'username',
     *                 name       : 'username'
     *             },
     *             {
     *                 xtype      : 'textfield',
     *                 inputType  : 'password',
     *                 fieldLabel : 'Password',
     *                 itemId     : 'password',
     *                 name       : 'password'
     *             }
     *         ]
     *     });
     *
     * If a Number is provided, this will resolve an {@link Ext.button.Button} at that index. This is very useful if
     * the window has buttons in the {@link #buttons} config and you want to provide default focus to one of them.
     *
     * An example of this would be:
     *
     *     Ext.create('Ext.window.Window', {
     *         autoShow     : true,
     *         width        : 300,
     *         title        : 'Login',
     *         defaultFocus : 1,
     *         items        : [
     *             {
     *                 xtype      : 'textfield',
     *                 fieldLabel : 'Username',
     *                 name       : 'username'
     *            },
     *            {
     *                 xtype      : 'textfield',
     *                 inputType  : 'password',
     *                 fieldLabel : 'Password',
     *                 name       : 'password'
     *             }
     *         ],
     *         buttons      : [
     *             {
     *                 text : 'Cancel'
     *             },
     *             {
     *                 text : 'Login'
     *             }
     *         ]
     *     });
     *
     * In summary, defaultFocus may be one of:
     *
     *   - The index of a footer Button.
     *   - The id or {@link Ext.Component#itemId} of a descendant Component.
     *   - A {@link Ext.ComponentQuery query} to find a {@link Ext.Component}.
     *   - A descendant {@link Ext.Component}.
     */
 
    /**
     * @cfg {Function} onEsc 
     * Allows override of the built-in processing for the escape key. Default action is to close the Window (performing
     * whatever action is specified in {@link #closeAction}. To prevent the Window closing when the escape key is
     * pressed, specify this as {@link Ext#emptyFn Ext.emptyFn}.
     */
 
    /**
     * @cfg {Boolean} [collapsed=false]
     * True to render the window collapsed, false to render it expanded. Note that if {@link #expandOnShow}
     * is true (the default) it will override the `collapsed` config and the window will always be
     * expanded when shown.
     */
 
    /**
     * @cfg {Boolean} [maximized=false]
     * True to initially display the window in a maximized state.
     */
 
    /**
     * @cfg {Boolean} [hideShadowOnDeactivate=false]
     * True to hide this Window's shadow when another floating item in the same z-index stack is activated.
     */
 
    /**
    * @cfg {String} [baseCls='x-window']
    * The base CSS class to apply to this panel's element.
    */
    baseCls: Ext.baseCSSPrefix + 'window',
 
    /**
     * @cfg {Boolean/Object} resizable
     * Specify as `true` to allow user resizing at each edge and corner of the window, false to disable resizing.
     *
     * This may also be specified as a config object to Ext.resizer.Resizer
     */
    resizable: true,
 
    /**
     * @cfg {Boolean} draggable 
     * True to allow the window to be dragged by the header bar, false to disable dragging. Note that
     * by default the window will be centered in the viewport, so if dragging is disabled the window may need to be
     * positioned programmatically after render (e.g., `myWindow.setPosition(100, 100);`).
     */
    draggable: true,
 
    /**
     * @cfg {Boolean} constrain 
     * True to constrain the window within its containing element, false to allow it to fall outside of its containing
     * element. By default the window will be rendered to `document.body`. To render and constrain the window within
     * another element specify {@link #renderTo}. Optionally the header only can be constrained
     * using {@link #constrainHeader}.
     */
    constrain: false,
 
    /**
     * @cfg {Boolean} constrainHeader 
     * True to constrain the window header within its containing element (allowing the window body to fall outside of
     * its containing element) or false to allow the header to fall outside its containing element.
     * Optionally the entire window can be constrained using {@link #constrain}.
     */
    constrainHeader: false,
 
    /**
     * @cfg simpleDrag
     * @hide
     */
 
    /**
     * @cfg {Boolean} plain 
     * True to render the window body with a transparent background so that it will blend into the framing elements,
     * false to add a lighter background color to visually highlight the body element and separate it more distinctly
     * from the surrounding frame.
     */
    plain: false,
 
    /**
     * @cfg {Boolean} minimizable 
     * True to display the 'minimize' tool button and allow the user to minimize the window, false to hide the button
     * and disallow minimizing the window. Note that this button provides no implementation -- the
     * behavior of minimizing a window is implementation-specific, so the minimize event must be handled and a custom
     * minimize behavior implemented for this option to be useful.
     */
    minimizable: false,
 
    /**
     * @cfg {Boolean} maximizable 
     * True to display the 'maximize' tool button and allow the user to maximize the window, false to hide the button
     * and disallow maximizing the window. Note that when a window is maximized, the tool button
     * will automatically change to a 'restore' button with the appropriate behavior already built-in that will restore
     * the window to its previous size.
     */
    maximizable: false,
 
    minHeight: 50,
 
    minWidth: 50,
 
    /**
     * @cfg {Boolean} expandOnShow 
     * True to always expand the window when it is displayed, false to keep it in its current state (which may be
     * {@link #collapsed}) when displayed.
     */
    expandOnShow: true,
 
    collapsible: false,
 
    /**
     * @cfg {Boolean} closable 
     * True to display the 'close' tool button and allow the user to close the window, false to hide the button and
     * disallow closing the window.
     *
     * By default, when close is requested by either clicking the close button in the header or pressing ESC when the
     * Window has focus, the {@link #method-close} method will be called. This will _{@link Ext.Component#method-destroy destroy}_ the
     * Window and its content meaning that it may not be reused.
     *
     * To make closing a Window _hide_ the Window so that it may be reused, set {@link #closeAction} to 'hide'.
     */
    closable: true,
 
    /**
     * @cfg {Boolean} monitorResize 
     * `true` to listen to the viewport resize event and perform any layout updating if necessary.
     * This is useful if using sizes as percentages for the window.
     */
 
    /**
     * @cfg {Boolean} hidden 
     * Render this Window hidden. If `true`, the {@link #method-hide} method will be called internally.
     */
    hidden: true,
 
    /**
     * @cfg {Boolean}
     * @inheritdoc
     * Windows render to the body on first show.
     */
    autoRender: true,
 
    /**
     * @cfg {String}
     * @inheritdoc
     * Windows hide using offsets in order to preserve the scroll positions of their descendants.  You may review
     * other configuration options here: {@link Ext.Component#hideMode}.
     */
    hideMode: 'offsets',
 
    /**
     * @cfg {Boolean} [floating=true]
     * @inheritdoc Ext.Component#cfg!floating
     */
    floating: true,
 
    alignOnScroll: false,
 
    /**
     * @cfg {String[]} stateEvents
     * @inheritdoc Ext.state.Stateful#cfg-stateEvents
     * @localdoc By default the following stateEvents are added:
     *
     *  - {@link #event-resize} - _(added by Ext.Component)_
     *  - {@link #event-collapse} - _(added by Ext.panel.Panel)_
     *  - {@link #event-expand} - _(added by Ext.panel.Panel)_
     *  - {@link #event-maximize}
     *  - {@link #event-restore}
     *  - {@link #event-resize}
     *  - {@link #event-dragend}
     */
 
    itemCls: Ext.baseCSSPrefix + 'window-item',
 
    overlapHeader: true,
 
    ignoreHeaderBorderManagement: true,
 
    // Flag to Renderable to always look up the framing styles for this Component 
    alwaysFramed: true,
 
    // Buffer this so we don't recreate the same object 
    isRootCfg: {
        isRoot: true
    },
 
    /**
     * @property {Boolean} isWindow 
     * `true` in this class to identify an object as an instantiated Window, or subclass thereof.
     */
    isWindow: true,
 
    ariaRole: 'dialog',
    focusable: true,
    tabGuard: true,
 
    /**
     * @inheritdoc
     */
    closeToolText: 'Close dialog',
 
    keyMap: {
        scope: 'this',
        ESC: 'onEsc'
    },
    
    /**
     * @cfg {String} [maskClickAction=focus]
     * The method to call when the window's modal mask is clicked or tapped:
     *
     * - **`'{@link #method-focus}'`** :
     *
     *   The default. Focus the window, which will then pass focus into its {@link #cfg-defaultFocus} delegate.
     *
     * - **`'{@link #method-destroy}'`** :
     *
     *   Remove  the window from the DOM and {@link Ext.Component#method-destroy destroy} it and all descendant
     *   Components. The window will **not** be available to be redisplayed via the {@link #method-show} method.
     *
     * - **`'{@link #method-hide}'`** :
     *
     *   {@link #method-hide} the window by setting visibility to hidden and applying negative offsets. The window will be
     *   available to be redisplayed via the {@link #method-show} method.
     *   @since 6.2.0
     */
    maskClickAction: 'focus',
 
    /**
     * @event activate
     * Fires after the window has been visually activated via {@link #setActive}.
     * @param {Ext.window.Window} this
     */
 
    /**
     * @event deactivate
     * Fires after the window has been visually deactivated via {@link #setActive}.
     * @param {Ext.window.Window} this
     */
 
    /**
     * @event maskclick
     * Fires when this Window's modal mask is clicked or tapped. Returning `false` from 
     * a handler will veto the subsequent preocessing of the {@link #cfg-maskClickAction}..
     * @param {Ext.window.Window} this
     */
 
    /**
     * @event resize
     * Fires after the window has been resized.
     * @param {Ext.window.Window} this
     * @param {Number} width The window's new width
     * @param {Number} height The window's new height
     */
 
    /**
     * @event maximize
     * Fires after the window has been maximized.
     * @param {Ext.window.Window} this
     */
 
    /**
     * @event minimize
     * Fires after the window has been minimized.
     * @param {Ext.window.Window} this
     */
 
    /**
     * @event restore
     * Fires after the window has been restored to its original size after being maximized.
     * @param {Ext.window.Window} this
     */
    
    disableCloseToolFocus: true,
 
    /**
     * @private
     */
    initComponent: function() {
        var me = this;
 
        // Explicitly set frame to false, since alwaysFramed is 
        // true, we only want to lookup framing in a specific instance 
        me.frame = false;
 
        me.callParent();
 
        if (me.plain) {
            me.addClsWithUI('plain');
        }
 
        me.addStateEvents(['maximize', 'restore', 'resize', 'dragend']);
    },
 
    getElConfig: function () {
        var me = this,
            elConfig;
 
        elConfig = me.callParent();
        elConfig.tabIndex = -1;
        return elConfig;
    },
 
    /**
     * @protected
     * Returns the focus holder element associated with this Window.
     * By default, this is the Window's element; this can be overridden
     * by setting {@link #defaultFocus} property.
     *
     * @return {Ext.dom.Element/Ext.Component} the focus holding element or Component.
     */
    getFocusEl: function() {
        return this.getDefaultFocus() || this.el;
    },
 
    // State Management 
 
    /**
     * @private
     */
    getState: function() {
        var me = this,
            state = me.callParent() || {},
            maximized = !!me.maximized,
            ghostBox = me.ghostBox,
            pos;
 
 
        state.maximized = maximized;
        if (maximized) {
            pos = me.restorePos;
        } else if (ghostBox) {
            // If we're animating a show, it will be from offscreen, so 
            // grab the position from the final box 
            pos = [ghostBox.x, ghostBox.y];
        } else {
            pos = me.getPosition(true);
        }
        Ext.apply(state, {
            size: maximized ? me.restoreSize : me.getSize(),
            pos: pos
        });
        return state;
    },
 
    applyState: function(state){
        var me = this;
 
        if (state) {
            me.maximized = state.maximized;
            if (me.maximized) {
                me.hasSavedRestore = true;
                me.restoreSize = state.size;
                me.restorePos = state.pos;
            } else {
                Ext.apply(me, {
                    width: state.size.width,
                    height: state.size.height,
                    x: state.pos[0],
                    y: state.pos[1]
                });
            }
        }
    },
 
    onRender: function(ct, position) {
        var me = this;
 
        me.callParent(arguments);
 
        // Single clicking a header will focus the defaultFocus child 
        if (me.header) {
            me.header.on({
                scope: me,
                click: me.onHeaderClick
            });
        }
 
        // Double clicking a header will toggleMaximize 
        if (me.maximizable) {
            me.header.on({
                scope: me,
                dblclick: me.toggleMaximize
            });
        }
    },
 
    afterRender: function() {
        var me = this,
            header = me.header;
 
        // Initialize 
        if (me.maximized) {
            me.maximized = false;
            me.maximize(null, true);
            if (header) {
                header.removeCls(header.indicateDragCls);
            }
        }
 
        me.callParent();
        
        me.initTabGuards();
    },
 
    /**
     * @private
     */
    onEsc: function(e) {
        if (this.closable) {
            e.stopEvent();
            this.close();
            return false;
        }
    },
 
    doDestroy: function() {
        var me = this;
        
        if (me.rendered) {
            Ext.un('resize', me.onWindowResize, me);
            delete me.animateTarget;
            me.hide();
        }
        
        me.callParent();
    },
 
    /**
     * @private
     * Contribute class-specific tools to the header.
     *
     * Called by Panel's initTools at initialization time.
     *
     * Implementations should jst add new tool config objects to `this.tools`
     */
    addTools: function() {
        var me = this,
            tools = [];
 
        // Call Panel's addTools 
        me.callParent();
 
        if (me.minimizable) {
            tools.push({
                type: 'minimize',
                handler: 'minimize',
                scope:  me
            });
        }
        if (me.maximizable) {
            tools.push({
                type: 'maximize',
                handler: 'toggleMaximize',
                scope: me
            });
        }
 
        if (tools.length) {
            me.addTool(tools);
        }
    },
    
    addTool: function(tools) {
        var me = this;
        
        me.callParent([tools]);
        
        if (me.rendered && me.tabGuard) {
            me.initTabGuards();
        }
    },
    
    add: function() {
        var me = this,
            ret;
        
        ret = me.callParent(arguments);
        
        if (me.rendered && me.tabGuard) {
            me.initTabGuards();
        }
        
        return ret;
    },
    
    remove: function() {
        var me = this,
            ret;
        
        ret = me.callParent(arguments);
        
        if (me.rendered && me.tabGuard) {
            me.initTabGuards();
        }
        
        return ret;
    },
    
    addDocked: function() {
        var me = this,
            ret;
        
        ret = me.callParent(arguments);
        
        if (me.rendered && me.tabGuard) {
            me.initTabGuards();
        }
        
        return ret;
    },
    
    removeDocked: function() {
        var me = this,
            ret;
        
        ret = me.callParent(arguments);
        
        if (me.rendered && me.tabGuard) {
            me.initTabGuards();
        }
        
        return ret;
    },
 
    onShow: function() {
        var me = this;
 
        me.callParent(arguments);
        
        if (me.expandOnShow) {
            me.expand(false);
        }
        
        me.syncMonitorWindowResize();
        
        if (me.rendered && me.tabGuard) {
            me.initTabGuards();
        }
   },
 
    /**
     * @private
     */
    doClose: function() {
        var me = this;
 
        // Being called as callback after going through the hide call below 
        if (me.hidden) {
            me.fireEvent('close', me);
            
            // This method can be called from hide() which in turn can be called 
            // from destroy() 
            if (me.closeAction === 'destroy' && !me.destroying && !me.destroyed) {
                me.destroy();
            }
        } else {
            // close after hiding 
            me.hide(me.animateTarget, me.doClose, me);
        }
    },
 
    /**
     * @private
     */
    afterHide: function() {
        var me = this;
 
        // No longer subscribe to resizing now that we're hidden 
        me.syncMonitorWindowResize();
 
        // Perform superclass's afterHide tasks. 
        me.callParent(arguments);
 
        // Hide may have destroyed a Window. 
        if (!me.destroyed && me.rendered && me.tabGuard) {
            me.initTabGuards();
        }
    },
 
    /**
     * @private
     */
    onWindowResize: function() {
        var me = this,
            sizeModel;
 
        // This is called on a timer. Window may have been destroyed in the interval. 
        if (!me.destroyed) {
            if (me.maximized) {
                me.fitContainer();
            } else {
                sizeModel = me.getSizeModel();
                if (sizeModel.width.natural || sizeModel.height.natural) {
                    me.updateLayout();
                }
                me.doConstrain();
            }
        }
    },
 
    /**
     * Placeholder method for minimizing the window. By default, this method simply fires the {@link #event-minimize} event
     * since the behavior of minimizing a window is application-specific. To implement custom minimize behavior, either
     * the minimize event can be handled or this method can be overridden.
     * @return {Ext.window.Window} this
     */
    minimize: function() {
        this.fireEvent('minimize', this);
        return this;
    },
 
    resumeHeaderLayout: function(changed) {
        this.header.resumeLayouts(changed ? this.isRootCfg : null);
    },
 
    afterCollapse: function() {
        var me = this,
            header = me.header,
            tools = me.tools;
 
        if (header && me.maximizable) {
            header.suspendLayouts();
            tools.maximize.hide();
            this.resumeHeaderLayout(true);
        }
        if (me.resizer) {
            me.resizer.disable();
        }
        me.callParent(arguments);
    },
 
    afterExpand: function() {
        var me = this,
            header = me.header,
            tools = me.tools,
            changed;
 
 
        if (header) {
            header.suspendLayouts();
            if (me.maximizable) {
                tools.maximize.show();
                changed = true;
            }
            this.resumeHeaderLayout(changed);
        }
        if (me.resizer) {
            me.resizer.enable();
        }
        me.callParent(arguments);
    },
 
    /**
     * Fits the window within its current container and automatically replaces the {@link #maximizable 'maximize' tool
     * button} with the 'restore' tool button. Also see {@link #toggleMaximize}.
     * @param {Boolean} [animate=false] Pass `true` to animate this Window to full size.
     * @return {Ext.window.Window} this
     */
    maximize: function(animate, /* private */ initial) {
        var me = this,
            header = me.header,
            tools = me.tools,
            width = me.width,
            height = me.height,
            restore, changed;
 
        if (!me.maximized && !me.maximizing) {
            me.maximizing = true;
            me.expand(false);
            if (!me.hasSavedRestore) {
                restore = me.restoreSize = {
                    width: width ? width : null,
                    height: height ? height : null
                };
 
                // If we're not positioned yet, default back to 0,0 
                if (initial) {
                    me.restorePos = [me.x || 0, me.y || 0];
                } else {
                    me.restorePos = me.getPosition();
                }
            }
 
            // Manipulate visibility of header tools if there is a header 
            if (header) {
                header.suspendLayouts();
                if (tools.maximize) {
                    tools.maximize.setType('restore');
                }
                if (me.collapseTool) {
                    me.collapseTool.hide();
                    changed = true;
                }
                me.resumeHeaderLayout(changed);
            }
 
            me.el.disableShadow();
 
            if (me.dd) {
                me.dd.disable();
                if (header) {
                   header.removeCls(header.indicateDragCls);
                }
            }
            if (me.resizer) {
                me.resizer.disable();
            }
 
            me.el.addCls(Ext.baseCSSPrefix + 'window-maximized');
            me.container.addCls(Ext.baseCSSPrefix + 'window-maximized-ct');
 
            me.syncMonitorWindowResize();
            me.fitContainer(animate = (animate || !!me.animateTarget) ? {
                callback: function() {
                    me.maximizing = false;
                    me.maximized = true;
                    if (!initial) {
                        me.fireEvent('maximize', me);
                    }
                }
            } : null);
            if (!animate) {
                me.maximizing = false;
                me.maximized = true;
                if (!initial) {
                    me.fireEvent('maximize', me);
                }
            }
        }
        return me;
    },
 
    /**
     * Restores a {@link #maximizable maximized} window back to its original size and position prior to being maximized
     * and also replaces the 'restore' tool button with the 'maximize' tool button. Also see {@link #toggleMaximize}.
     * @param {Boolean} [animate=false] Pass `true` to animate the restore.
     * @return {Ext.window.Window} this
     */
    restore: function(animate) {
        var me = this,
            tools = me.tools,
            header = me.header,
            newBox = me.restoreSize,
            changed;
 
        if (me.maximized) {
            me.hasSavedRestore = null;
            me.removeCls(Ext.baseCSSPrefix + 'window-maximized');
 
            // Manipulate visibility of header tools if there is a header 
            if (header) {
                header.suspendLayouts();
                if (tools.maximize) {
                    tools.maximize.setType('maximize');
                }
                if (me.collapseTool) {
                    me.collapseTool.show();
                    changed = true;
                }
                me.resumeHeaderLayout(changed);
            }
 
            // Restore the position/sizing 
            newBox.x = me.restorePos[0];
            newBox.y = me.restorePos[1];
            me.setBox(newBox, animate = (animate || !!me.animateTarget) ? {
                callback: function() {
                    me.el.enableShadow(null, true);
                    me.maximized = false;
                    me.fireEvent('restore', me);
                }
            } : null);
 
            // Unset old position/sizing 
            me.restorePos = me.restoreSize = null;
 
            // Allow users to drag and drop again 
            if (me.dd) {
                me.dd.enable();
                if (header) {
                    header.addCls(header.indicateDragCls);
                }
            }
 
            if (me.resizer) {
                me.resizer.enable();
            }
 
            me.container.removeCls(Ext.baseCSSPrefix + 'window-maximized-ct');
 
            me.syncMonitorWindowResize();
 
            if (!animate) {
                me.el.enableShadow(null, true);
                me.maximized = false;
                me.fireEvent('restore', me);
            }
        }
        return me;
    },
 
    /**
     * Synchronizes the presence of our listener for window resize events. This method
     * should be called whenever this status might change.
     * @private
     */
    syncMonitorWindowResize: function () {
        var me = this,
            currentlyMonitoring = me._monitoringResize,
            // all the states where we should be listening to window resize: 
            yes = me.monitorResize || me.constrain || me.constrainHeader || me.maximized,
            // all the states where we veto this: 
            veto = me.hidden || me.destroying || me.destroyed;
 
        if (yes && !veto) {
            // we should be listening... 
            if (!currentlyMonitoring) {
                // but we aren't, so set it up. 
                // Delay so that we jump over any Viewport resize activity 
                Ext.on('resize', me.onWindowResize, me, {buffer: 1});
                me._monitoringResize = true;
            }
        } else if (currentlyMonitoring) {
            // we should not be listening, but we are, so tear it down 
            Ext.un('resize', me.onWindowResize, me);
            me._monitoringResize = false;
        }
    },
 
    /**
     * A shortcut method for toggling between {@link #method-maximize} and {@link #method-restore} based on the current maximized
     * state of the window.
     * @return {Ext.window.Window} this
     */
    toggleMaximize: function() {
        return this[this.maximized ? 'restore': 'maximize']();
    },
 
    createGhost: function() {
        var ghost = this.callParent(arguments);
        ghost.xtype = 'window';
        ghost.focusOnToFront = false;
        return ghost;
    },
 
    /**
     * Gets the configured default focus item.  If a {@link #defaultFocus} is set, it will
     * receive focus when the Window's `focus` method is called, otherwise the
     * Window itself will receive focus.
     */
    getDefaultFocus: function() {
        var me = this,
            result,
            defaultComp = me.defaultFocus,
            selector;
 
        if (defaultComp !== undefined) {
            // Number is index of Button 
            if (Ext.isNumber(defaultComp)) {
                result = me.query('button')[defaultComp];
            }
            // String is ID or CQ selector 
            else if (Ext.isString(defaultComp)) {
                selector = defaultComp;
 
                // Try id/itemId match if selector begins with alphanumeric 
                // and is not compound xtype/id selector with # in the middle 
                // (https://sencha.jira.com/browse/EXTJS-14925) 
                if (Ext.validIdRe.test(selector)) {
                    result = me.down(Ext.makeIdSelector(selector));
                }
                // If not found, use as selector 
                if (!result) {
                    result = me.down(selector);
                }
            }
            // Otherwise, if it's got a focus method, use it 
            else if (defaultComp.focus) {
                result = defaultComp;
            }
        }
 
        return result;
    },
 
    privates: {
        // Override. Windows are always simple draggable, they do not use Ext.Panel.DDs 
        // The dd property in a Window is always a ComponentDragger 
        initDraggable: function() {
            /**
             * @property {Ext.util.ComponentDragger} dd
             * If this Window is configured {@link #cfg-draggable}, this property will contain an instance of
             * {@link Ext.util.ComponentDragger} (A subclass of {@link Ext.dd.DragTracker DragTracker}) which handles dragging
             * the Window's DOM Element, and constraining according to the {@link #constrain} and {@link #constrainHeader} .
             *
             * This has implementations of `onBeforeStart`, `onDrag` and `onEnd` which perform the dragging action. If
             * extra logic is needed at these points, use {@link Ext.Function#createInterceptor createInterceptor} or
             * {@link Ext.Function#createSequence createSequence} to augment the existing implementations.
             */
            this.initSimpleDraggable();
        },
 
        onHeaderClick: function(header, e) {
            var delegate;
 
            if (header.el.contains(e.getTarget())) {
                delegate = this.getDefaultFocus();
 
                if (delegate) {
                    delegate.focus();
                }
            }
        },
 
        initResizable: function(resizable) {
            var me = this;
            me.callParent([resizable]);
            if (me.maximized || me.maximizing) {
                me.resizer.disable();
            }
        },
 
        initSimpleDraggable: function() {
            var me = this,
                dd;
 
            me.callParent();
            dd = me.dd;
            if (dd && me.maximized || me.maximizing) {
                dd.disable();
            }
        },
        
        onTabGuardFocusEnter: function(e, target) {
            var me = this,
                el = me.el,
                beforeGuard = me.tabGuardBeforeEl,
                afterGuard = me.tabGuardAfterEl,
                from = e.relatedTarget,
                nodes, forward, nextFocus;
 
            nodes = el.findTabbableElements({
                skipSelf: true
            });
            
            // Tabbables might include two tab guards, so remove them 
            if (nodes[0] === beforeGuard.dom) {
                nodes.shift();
            }
            
            if (nodes[nodes.length - 1] === afterGuard.dom) {
                nodes.pop();
            }
            
            // Totally possible not to have anything tabbable within the window 
            // but we have to do something so focus back the window el. At least 
            // in that case the user will be able to press Escape key to close it. 
            if (nodes.length === 0) {
                nextFocus = el;
            }
            // The window itself was focused, possibly by clicking or programmatically; 
            // but this time we do have something tabbable to choose from. 
            else if (from === el.dom) {
                forward = target === beforeGuard.dom;
            }
            // Focus was within the window and is trying to escape;  
            // for topmost guard we need to bounce focus back to the last tabbable 
            // element in the window, and vice versa for the bottom guard. 
            else if (el.contains(from)) {
                forward = !!e.forwardTab;
            }
            // It is entirely possible that focus was outside the window and 
            // the user tabbed into the window. In that case we forward the focus 
            // to the next available element in the natural tab order, i.e. the element 
            // after the topmost guard, or the element before the bottom guard. 
            else {
                forward = target === beforeGuard.dom;
            }
            
            nextFocus = nextFocus || (forward ? nodes[0] : nodes[nodes.length - 1]);
            
            if (nextFocus) {
                // If there is only one focusable node in the window, focusing it 
                // while we're in focusenter handler for the tab guard might cause 
                // race condition where the focusable node will be refocused first 
                // and then its original blur handler will kick in, removing focus 
                // styling erroneously. 
                Ext.fly(nextFocus).focus(nodes.length === 1 ? 1 : 0);
            }
        }
    }
});