/**
 * 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,
 *             // One header just for show. There's no data
 *             columns: [{ header: 'World' }],
 *             store: Ext.create('Ext.data.ArrayStore', {}) // A dummy empty data store
 *         }
 *     }).show();
 */
Ext.define('Ext.window.Window', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.window',
    alternateClassName: 'Ext.Window',
 
    requires: [
        'Ext.util.ComponentDragger',
        'Ext.util.Region'
    ],
 
    /**
     * @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
    * 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,
 
    /**
     * @cfg minHeight
     * @inheritdoc
     */
    minHeight: 50,
 
    /**
     * @cfg minWidth
     * @inheritdoc
     */
    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,
 
    /**
     * @cfg collapsible
     * @inheritdoc
     */
    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 autoRender
     * @inheritdoc
     * Windows render to the body on first show.
     */
    autoRender: true,
 
    /**
     * @cfg hideMode
     * @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 floating
     * @inheritdoc Ext.Component#cfg!floating
     */
    floating: true,
 
    /**
     * @cfg alignOnScroll
     * @inheritdoc
     */
    alignOnScroll: false,
 
    /**
     * @cfg 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',
 
    /**
     * @cfg overlapHeader
     * @inheritdoc
     */
    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,
 
    /**
     * @property ariaRole
     * @inheritdoc
     */
    ariaRole: 'dialog',
    
    /**
     * @property focusable
     * @inheritdoc
     */
    focusable: true,
    
    /**
     * @cfg tabGuard
     * @inheritdoc
     */
    tabGuard: true,
 
    /**
     * @cfg closeToolText
     * @inheritdoc
     */
    closeToolText: 'Close dialog',
 
    /**
     * @cfg keyMap
     * @inheritdoc
     */
    keyMap: {
        scope: 'this',
        ESC: 'onEsc'
    },
    
    /**
     * @cfg {String} maskClickAction
     * 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.
     * @param {Boolean} initial (private)
     * @return {Ext.window.Window} this
     */
    maximize: function(animate, initial) {
        var me = this,
            header = me.header,
            tools = me.tools,
            width = me.width,
            height = me.height,
            changed;
 
        if (!me.maximized && !me.maximizing) {
            me.maximizing = true;
            me.expand(false);
            
            if (!me.hasSavedRestore) {
                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);
            }
        }
    }
});