/**
 * @class Ext.window.Window
 * @extends Ext.panel.Panel
 * <p>A specialized panel intended for use as an application window.  Windows are floated, {@link #resizable}, and
 * {@link #draggable} by default.  Windows can be {@link #maximizable maximized} to fill the viewport,
 * restored to their prior size, and can be {@link #minimize}d.</p>
 * <p>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.</p>
 * <p>By default, Windows will be rendered to document.body. To {@link #constrain} a Window to another element
 * specify {@link Ext.Component#renderTo renderTo}.</p>
 * <p><b>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.</b></p>
 * {@img Ext.window.Window/Ext.window.Window.png Window component}
 * Example:<code><pre>
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();
</pre></code>
 * @constructor
 * @param {Object} config The config object
 * @xtype window
 */

Ext.define('Ext.window.Window', {
    extend
: 'Ext.panel.Panel',

    alternateClassName
: 'Ext.Window',

    requires
: ['Ext.util.ComponentDragger', 'Ext.util.Region', 'Ext.EventManager'],

   
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.core.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.core.Element Element) (The Element that the Window is rendered to).
     */

    /**
     * @cfg {Boolean} modal
     * True to make the window modal and mask everything behind it when displayed, false to display it without
     * restricting access to other UI elements (defaults to false).
     */

    /**
     * @cfg {String/Element} animateTarget
     * Id or element from which the window should animate while opening (defaults to null with no animation).
     */

    /**
     * @cfg {String/Number/Component} defaultFocus
     * <p>Specifies a Component to receive focus when this Window is focused.</p>
     * <p>This may be one of:</p><div class="mdetail-params"><ul>
     * <li>The index of a footer Button.</li>
     * <li>The id or {@link Ext.AbstractComponent#itemId} of a descendant Component.</li>
     * <li>A Component.</li>
     * </ul></div>
     */

    /**
     * @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
     * Ext.emptyFn (See {@link Ext#emptyFn Ext.emptyFn}).
     */

    /**
     * @cfg {Boolean} collapsed
     * True to render the window collapsed, false to render it expanded (defaults to false). Note that if
     * {@link #expandOnShow} is true (the default) it will override the <code>collapsed</code> config and the window
     * will always be expanded when shown.
     */

    /**
     * @cfg {Boolean} maximized
     * True to initially display the window in a maximized state. (Defaults to false).
     */


    /**
    * @cfg {String} baseCls
    * The base CSS class to apply to this panel's element (defaults to 'x-window').
    */

    baseCls
: Ext.baseCSSPrefix + 'window',

    /**
     * @cfg {Mixed} resizable
     * <p>Specify as <code>true</code> to allow user resizing at each edge and corner of the window, false to disable
     * resizing (defaults to true).</p>
     * <p>This may also be specified as a config object to </p>
     */

    resizable
: true,

    /**
     * @cfg {Boolean} draggable
     * <p>True to allow the window to be dragged by the header bar, false to disable dragging (defaults to true).  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);).<p>
     */

    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}.
     * (defaults to false).  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 (defaults to
     * false). Optionally the entire window can be constrained using {@link #constrain}.
     */

    constrainHeader
: false,

    /**
     * @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 (defaults to false).
     */

    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 (defaults to false).  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 (defaults to false).  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,

   
// inherit docs
    minHeight
: 100,

   
// inherit docs
    minWidth
: 200,

    /**
     * @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 (defaults to true).
     */

    expandOnShow
: true,

   
// inherited docs, same default
    collapsible
: false,

    /**
     * @cfg {Boolean} closable
     * <p>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 (defaults to <code>true</code>).</p>
     * <p>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 #close} method will be called. This
     * will <i>{@link Ext.Component#destroy destroy}</i> the Window and its content meaning that
     * it may not be reused.</p>
     * <p>To make closing a Window <i>hide</i> the Window so that it may be reused, set
     * {@link #closeAction} to 'hide'.</p>
     */

    closable
: true,

    /**
     * @cfg {Boolean} hidden
     * Render this Window hidden (default is <code>true</code>). If <code>true</code>, the
     * {@link #hide} method will be called internally.
     */

    hidden
: true,

   
// Inherit docs from Component. Windows render to the body on first show.
    autoRender
: true,

   
// Inherit docs from Component. Windows hide using visibility.
    hideMode
: 'visibility',

    /** @cfg {Boolean} floating @hide Windows are always floating*/
    floating: true,

    ariaRole
: 'alertdialog',
   
    itemCls
: 'x-window-item',

    overlapHeader
: true,
   
    ignoreHeaderBorderManagement
: true,

   
// private
    initComponent
: function() {
       
var me = this;
        me
.callParent();
        me
.addEvents(
            /**
             * @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 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
             */

           
'resize',
            /**
             * @event maximize
             * Fires after the window has been maximized.
             * @param {Ext.window.Window} this
             */

           
'maximize',
            /**
             * @event minimize
             * Fires after the window has been minimized.
             * @param {Ext.window.Window} this
             */

           
'minimize',
            /**
             * @event restore
             * Fires after the window has been restored to its original size after being maximized.
             * @param {Ext.window.Window} this
             */

           
'restore'
       
);

       
if (me.plain) {
            me
.addClsWithUI('plain');
       
}

       
if (me.modal) {
            me
.ariaRole = 'dialog';
       
}
   
},

   
// State Management
   
// private

    initStateEvents
: function(){
       
var events = this.stateEvents;
       
// push on stateEvents if they don't exist
       
Ext.each(['maximize', 'restore', 'resize', 'dragend'], function(event){
           
if (Ext.Array.indexOf(events, event)) {
                events
.push(event);
           
}
       
});
       
this.callParent();
   
},

    getState
: function() {
       
var me = this,
            state
= me.callParent() || {},
            maximized
= !!me.maximized;

        state
.maximized = maximized;
       
Ext.apply(state, {
            size
: maximized ? me.restoreSize : me.getSize(),
            pos
: maximized ? me.restorePos : me.getPosition()
       
});
       
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]
               
});
           
}
       
}
   
},

   
// private
    onMouseDown
: function () {
       
if (this.floating) {
           
this.toFront();
       
}
   
},

   
// private
    onRender
: function(ct, position) {
       
var me = this;
        me
.callParent(arguments);
        me
.focusEl = me.el;

       
// Double clicking a header will toggleMaximize
       
if (me.maximizable) {
            me
.header.on({
                dblclick
: {
                    fn
: me.toggleMaximize,
                    element
: 'el',
                    scope
: me
               
}
           
});
       
}
   
},

   
// private
    afterRender
: function() {
       
var me = this,
            hidden
= me.hidden,
            keyMap
;

        me
.hidden = false;
       
// Component's afterRender sizes and positions the Component
        me
.callParent();
        me
.hidden = hidden;

       
// Create the proxy after the size has been applied in Component.afterRender
        me
.proxy = me.getProxy();

       
// clickToRaise
        me
.mon(me.el, 'mousedown', me.onMouseDown, me);

       
// Initialize
       
if (me.maximized) {
            me
.maximized = false;
            me
.maximize();
       
}

       
if (me.closable) {
            keyMap
= me.getKeyMap();
            keyMap
.on(27, me.onEsc, me);
            keyMap
.disable();
       
}
   
},

    /**
     * @private
     * @override
     * Override Component.initDraggable.
     * Window uses the header element as the delegate.
     */

    initDraggable
: function() {
       
var me = this,
            ddConfig
;

       
if (!me.header) {
            me
.updateHeader(true);
       
}

        ddConfig
= Ext.applyIf({
            el
: me.el,
           
delegate: '#' + me.header.id
       
}, me.draggable);

       
// Add extra configs if Window is specified to be constrained
       
if (me.constrain || me.constrainHeader) {
            ddConfig
.constrain = me.constrain;
            ddConfig
.constrainDelegate = me.constrainHeader;
            ddConfig
.constrainTo = me.constrainTo || me.container;
       
}

        /**
         * <p>If this Window is configured {@link #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} .</p>
         * <p>This has implementations of <code>onBeforeStart</code>, <code>onDrag</code> and <code>onEnd</code>
         * 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.</p>
         * @type Ext.util.ComponentDragger
         * @property dd
         */

        me
.dd = Ext.create('Ext.util.ComponentDragger', this, ddConfig);
        me
.relayEvents(me.dd, ['dragstart', 'drag', 'dragend']);
   
},

   
// private
    onEsc
: function(k, e) {
        e
.stopEvent();
       
this[this.closeAction]();
   
},

   
// private
    beforeDestroy
: function() {
       
var me = this;
       
if (me.rendered) {
           
delete this.animateTarget;
            me
.hide();
           
Ext.destroy(
                me
.keyMap
           
);
       
}
        me
.callParent();
   
},

    /**
     * @private
     * @override
     * Contribute class-specific tools to the header.
     * Called by Panel's initTools.
     */

    addTools
: function() {
       
var me = this;

       
// Call Panel's initTools
        me
.callParent();

       
if (me.minimizable) {
            me
.addTool({
                type
: 'minimize',
                handler
: Ext.Function.bind(me.minimize, me, [])
           
});
       
}
       
if (me.maximizable) {
            me
.addTool({
                type
: 'maximize',
                handler
: Ext.Function.bind(me.maximize, me, [])
           
});
            me
.addTool({
                type
: 'restore',
                handler
: Ext.Function.bind(me.restore, me, []),
                hidden
: true
           
});
       
}
   
},

    /**
     * Gets the configured default focus item.  If a {@link #defaultFocus} is set, it will receive focus, otherwise the
     * Container itself will receive focus.
     */

    getFocusEl
: function() {
       
var me = this,
            f
= me.focusEl,
            defaultComp
= me.defaultButton || me.defaultFocus,
            t
= typeof db,
            el
,
            ct
;

       
if (Ext.isDefined(defaultComp)) {
           
if (Ext.isNumber(defaultComp)) {
                f
= me.query('button')[defaultComp];
           
} else if (Ext.isString(defaultComp)) {
                f
= me.down('#' + defaultComp);
           
} else {
                f
= defaultComp;
           
}
       
}
       
return f || me.focusEl;
   
},

   
// private
    beforeShow
: function() {
       
this.callParent();

       
if (this.expandOnShow) {
           
this.expand(false);
       
}
   
},

   
// private
    afterShow
: function(animateTarget) {
       
var me = this,
            size
;

       
// Perform superclass's afterShow tasks
       
// Which might include animating a proxy from an animTarget
        me
.callParent(arguments);

       
if (me.maximized) {
            me
.fitContainer();
       
}

       
if (me.monitorResize || me.constrain || me.constrainHeader) {
           
Ext.EventManager.onWindowResize(me.onWindowResize, me);
       
}
        me
.doConstrain();
       
if (me.keyMap) {
            me
.keyMap.enable();
       
}
   
},

   
// private
    doClose
: function() {
       
var me = this;

       
// immediate close
       
if (me.hidden) {
            me
.fireEvent('close', me);
            me
[me.closeAction]();
       
} else {
           
// close after hiding
            me
.hide(me.animTarget, me.doClose, me);
       
}
   
},

   
// private
    afterHide
: function() {
       
var me = this;

       
// No longer subscribe to resizing now that we're hidden
       
if (me.monitorResize || me.constrain || me.constrainHeader) {
           
Ext.EventManager.removeResizeListener(me.onWindowResize, me);
       
}

       
// Turn off keyboard handling once window is hidden
       
if (me.keyMap) {
            me
.keyMap.disable();
       
}

       
// Perform superclass's afterHide tasks.
        me
.callParent(arguments);
   
},

   
// private
    onWindowResize
: function() {
       
if (this.maximized) {
           
this.fitContainer();
       
}
       
this.doConstrain();
   
},

    /**
     * Placeholder method for minimizing the window.  By default, this method simply fires the {@link #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;
   
},

    afterCollapse
: function() {
       
var me = this;

       
if (me.maximizable) {
            me
.tools.maximize.hide();
            me
.tools.restore.hide();
       
}
       
if (me.resizer) {
            me
.resizer.disable();
       
}
        me
.callParent(arguments);
   
},

    afterExpand
: function() {
       
var me = this;

       
if (me.maximized) {
            me
.tools.restore.show();
       
} else if (me.maximizable) {
            me
.tools.maximize.show();
       
}
       
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}.
     * @return {Ext.window.Window} this
     */

    maximize
: function() {
       
var me = this;

       
if (!me.maximized) {
            me
.expand(false);
           
if (!me.hasSavedRestore) {
                me
.restoreSize = me.getSize();
                me
.restorePos = me.getPosition(true);
           
}
           
if (me.maximizable) {
                me
.tools.maximize.hide();
                me
.tools.restore.show();
           
}
            me
.maximized = true;
            me
.el.disableShadow();

           
if (me.dd) {
                me
.dd.disable();
           
}
           
if (me.collapseTool) {
                me
.collapseTool.hide();
           
}
            me
.el.addCls(Ext.baseCSSPrefix + 'window-maximized');
            me
.container.addCls(Ext.baseCSSPrefix + 'window-maximized-ct');

            me
.setPosition(0, 0);
            me
.fitContainer();
            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}.
     * @return {Ext.window.Window} this
     */

    restore
: function() {
       
var me = this,
            tools
= me.tools;

       
if (me.maximized) {
           
delete me.hasSavedRestore;
            me
.removeCls(Ext.baseCSSPrefix + 'window-maximized');

           
// Toggle tool visibility
           
if (tools.restore) {
                tools
.restore.hide();
           
}
           
if (tools.maximize) {
                tools
.maximize.show();
           
}
           
if (me.collapseTool) {
                me
.collapseTool.show();
           
}

           
// Restore the position/sizing
            me
.setPosition(me.restorePos);
            me
.setSize(me.restoreSize);

           
// Unset old position/sizing
           
delete me.restorePos;
           
delete me.restoreSize;

            me
.maximized = false;

            me
.el.enableShadow(true);

           
// Allow users to drag and drop again
           
if (me.dd) {
                me
.dd.enable();
           
}

            me
.container.removeCls(Ext.baseCSSPrefix + 'window-maximized-ct');

            me
.doConstrain();
            me
.fireEvent('restore', me);
       
}
       
return me;
   
},

    /**
     * A shortcut method for toggling between {@link #maximize} and {@link #restore} based on the current maximized
     * state of the window.
     * @return {Ext.window.Window} this
     */

    toggleMaximize
: function() {
       
return this[this.maximized ? 'restore': 'maximize']();
   
}

    /**
     * @cfg {Boolean} autoWidth @hide
     * Absolute positioned element and therefore cannot support autoWidth.
     * A width is a required configuration.
     **/

});