/**
 * @class Ext.ZIndexManager
 * <p>A class that manages a group of {@link Ext.Component#floating} Components and provides z-order management,
 * and Component activation behavior, including masking below the active (topmost) Component.</p>
 * <p>{@link Ext.Component#floating Floating} Components which are rendered directly into the document (Such as {@link Ext.window.Window Window}s which are
 * {@link Ext.Component#show show}n are managed by a {@link Ext.WindowManager global instance}.</p>
 * <p>{@link Ext.Component#floating Floating} Components which are descendants of {@link Ext.Component#floating floating} <i>Containers</i>
 * (For example a {Ext.view.BoundList BoundList} within an {@link Ext.window.Window Window}, or a {@link Ext.menu.Menu Menu}),
 * are managed by a ZIndexManager owned by that floating Container. So ComboBox dropdowns within Windows will have managed z-indices
 * guaranteed to be correct, relative to the Window.</p>
 * @constructor
 */

Ext.define('Ext.ZIndexManager', {

    alternateClassName
: 'Ext.WindowGroup',

    statics
: {
        zBase
: 9000
   
},

    constructor
: function(container) {
       
var me = this;

        me
.list = {};
        me
.zIndexStack = [];
        me
.front = null;

       
if (container) {

           
// This is the ZIndexManager for an Ext.container.Container, base its zseed on the zIndex of the Container's element
           
if (container.isContainer) {
                container
.on('resize', me._onContainerResize, me);
                me
.zseed = Ext.Number.from(container.getEl().getStyle('zIndex'), me.getNextZSeed());
               
// The containing element we will be dealing with (eg masking) is the content target
                me
.targetEl = container.getTargetEl();
                me
.container = container;
           
}
           
// This is the ZIndexManager for a DOM element
           
else {
               
Ext.EventManager.onWindowResize(me._onContainerResize, me);
                me
.zseed = me.getNextZSeed();
                me
.targetEl = Ext.get(container);
           
}
       
}
       
// No container passed means we are the global WindowManager. Our target is the doc body.
       
// DOM must be ready to collect that ref.
       
else {
           
Ext.EventManager.onWindowResize(me._onContainerResize, me);
            me
.zseed = me.getNextZSeed();
           
Ext.onDocumentReady(function() {
                me
.targetEl = Ext.getBody();
           
});
       
}
   
},

    getNextZSeed
: function() {
       
return (Ext.ZIndexManager.zBase += 10000);
   
},

    setBase
: function(baseZIndex) {
       
this.zseed = baseZIndex;
       
return this.assignZIndices();
   
},

   
// private
    assignZIndices
: function() {
       
var a = this.zIndexStack,
            len
= a.length,
            i
= 0,
            zIndex
= this.zseed,
            comp
;

       
for (; i < len; i++) {
            comp
= a[i];
           
if (comp && !comp.hidden) {

               
// Setting the zIndex of a Component returns the topmost zIndex consumed by
               
// that Component.
               
// If it's just a plain floating Component such as a BoundList, then the
               
// return value is the passed value plus 10, ready for the next item.
               
// If a floating *Container* has its zIndex set, it re-orders its managed
               
// floating children, starting from that new base, and returns a value 10000 above
               
// the highest zIndex which it allocates.
                zIndex
= comp.setZIndex(zIndex);
           
}
       
}
       
this._activateLast();
       
return zIndex;
   
},

   
// private
    _setActiveChild
: function(comp) {
       
if (comp != this.front) {

           
if (this.front) {
               
this.front.setActive(false, comp);
           
}
           
this.front = comp;
           
if (comp) {
                comp
.setActive(true);
               
if (comp.modal) {
                   
this._showModalMask(comp.el.getStyle('zIndex') - 4);
               
}
           
}
       
}
   
},

   
// private
    _activateLast
: function(justHidden) {
       
var comp,
            lastActivated
= false,
            i
;

       
// Go down through the z-index stack.
       
// Activate the next visible one down.
       
// Keep going down to find the next visible modal one to shift the modal mask down under
       
for (i = this.zIndexStack.length-1; i >= 0; --i) {
            comp
= this.zIndexStack[i];
           
if (!comp.hidden) {
               
if (!lastActivated) {
                   
this._setActiveChild(comp);
                    lastActivated
= true;
               
}

               
// Move any modal mask down to just under the next modal floater down the stack
               
if (comp.modal) {
                   
this._showModalMask(comp.el.getStyle('zIndex') - 4);
                   
return;
               
}
           
}
       
}

       
// none to activate, so there must be no modal mask.
       
// And clear the currently active property
       
this._hideModalMask();
       
if (!lastActivated) {
           
this._setActiveChild(null);
       
}
   
},

    _showModalMask
: function(zIndex) {
       
if (!this.mask) {
           
this.mask = this.targetEl.createChild({
                cls
: Ext.baseCSSPrefix + 'mask'
           
});
           
this.mask.setVisibilityMode(Ext.core.Element.DISPLAY);
           
this.mask.on('click', this._onMaskClick, this);
       
}
       
Ext.getBody().addCls(Ext.baseCSSPrefix + 'body-masked');
       
this.mask.setSize(this.targetEl.getViewSize(true));
       
this.mask.setStyle('zIndex', zIndex);
       
this.mask.show();
   
},

    _hideModalMask
: function() {
       
if (this.mask) {
           
Ext.getBody().removeCls(Ext.baseCSSPrefix + 'body-masked');
           
this.mask.hide();
       
}
   
},

    _onMaskClick
: function() {
       
if (this.front) {
           
this.front.focus();
       
}
   
},

    _onContainerResize
: function() {
       
if (this.mask && this.mask.isVisible()) {
           
this.mask.setSize(this.targetEl.getViewSize(true));
       
}
   
},

    /**
     * <p>Registers a floating {@link Ext.Component} with this ZIndexManager. This should not
     * need to be called under normal circumstances. Floating Components (such as Windows, BoundLists and Menus) are automatically registered
     * with a {@link Ext.Component#zIndexManager zIndexManager} at render time.</p>
     * <p>Where this may be useful is moving Windows between two ZIndexManagers. For example,
     * to bring the Ext.MessageBox dialog under the same manager as the Desktop's
     * ZIndexManager in the desktop sample app:</p><code><pre>
MyDesktop.getDesktop().getManager().register(Ext.MessageBox);
</pre></code>
     * @param {Component} comp The Component to register.
     */

   
register : function(comp) {
       
if (comp.zIndexManager) {
            comp
.zIndexManager.unregister(comp);
       
}
        comp
.zIndexManager = this;

       
this.list[comp.id] = comp;
       
this.zIndexStack.push(comp);
        comp
.on('hide', this._activateLast, this);
   
},

    /**
     * <p>Unregisters a {@link Ext.Component} from this ZIndexManager. This should not
     * need to be called. Components are automatically unregistered upon destruction.
     * See {@link #register}.</p>
     * @param {Component} comp The Component to unregister.
     */

    unregister
: function(comp) {
       
delete comp.zIndexManager;
       
if (this.list && this.list[comp.id]) {
           
delete this.list[comp.id];
            comp
.un('hide', this._activateLast);
           
Ext.Array.remove(this.zIndexStack, comp);

           
// Destruction requires that the topmost visible floater be activated. Same as hiding.
           
this._activateLast(comp);
       
}
   
},

    /**
     * Gets a registered Component by id.
     * @param {String/Object} id The id of the Component or a {@link Ext.Component} instance
     * @return {Ext.Component}
     */

   
get : function(id) {
       
return typeof id == "object" ? id : this.list[id];
   
},

   /**
     * Brings the specified Component to the front of any other active Components in this ZIndexManager.
     * @param {String/Object} comp The id of the Component or a {@link Ext.Component} instance
     * @return {Boolean} True if the dialog was brought to the front, else false
     * if it was already in front
     */

    bringToFront
: function(comp) {
        comp
= this.get(comp);
       
if (comp != this.front) {
           
Ext.Array.remove(this.zIndexStack, comp);
           
this.zIndexStack.push(comp);
           
this.assignZIndices();
           
return true;
       
}
       
if (comp.modal) {
           
Ext.getBody().addCls(Ext.baseCSSPrefix + 'body-masked');
           
this.mask.setSize(Ext.core.Element.getViewWidth(true), Ext.core.Element.getViewHeight(true));
           
this.mask.show();
       
}
       
return false;
   
},

    /**
     * Sends the specified Component to the back of other active Components in this ZIndexManager.
     * @param {String/Object} comp The id of the Component or a {@link Ext.Component} instance
     * @return {Ext.Component} The Component
     */

    sendToBack
: function(comp) {
        comp
= this.get(comp);
       
Ext.Array.remove(this.zIndexStack, comp);
       
this.zIndexStack.unshift(comp);
       
this.assignZIndices();
       
return comp;
   
},

    /**
     * Hides all Components managed by this ZIndexManager.
     */

    hideAll
: function() {
       
for (var id in this.list) {
           
if (this.list[id].isComponent && this.list[id].isVisible()) {
               
this.list[id].hide();
           
}
       
}
   
},

    /**
     * @private
     * Temporarily hides all currently visible managed Components. This is for when
     * dragging a Window which may manage a set of floating descendants in its ZIndexManager;
     * they should all be hidden just for the duration of the drag.
     */

    hide
: function() {
       
var i = 0,
            ln
= this.zIndexStack.length,
            comp
;

       
this.tempHidden = [];
       
for (; i < ln; i++) {
            comp
= this.zIndexStack[i];
           
if (comp.isVisible()) {
               
this.tempHidden.push(comp);
                comp
.hide();
           
}
       
}
   
},

    /**
     * @private
     * Restores temporarily hidden managed Components to visibility.
     */

    show
: function() {
       
var i = 0,
            ln
= this.tempHidden.length,
            comp
,
            x
,
            y
;

       
for (; i < ln; i++) {
            comp
= this.tempHidden[i];
            x
= comp.x;
            y
= comp.y;
            comp
.show();
            comp
.setPosition(x, y);
       
}
       
delete this.tempHidden;
   
},

    /**
     * Gets the currently-active Component in this ZIndexManager.
     * @return {Ext.Component} The active Component
     */

    getActive
: function() {
       
return this.front;
   
},

    /**
     * Returns zero or more Components in this ZIndexManager using the custom search function passed to this method.
     * The function should accept a single {@link Ext.Component} reference as its only argument and should
     * return true if the Component matches the search criteria, otherwise it should return false.
     * @param {Function} fn The search function
     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the Component being tested.
     * that gets passed to the function if not specified)
     * @return {Array} An array of zero or more matching windows
     */

    getBy
: function(fn, scope) {
       
var r = [],
            i
= 0,
            len
= this.zIndexStack.length,
            comp
;

       
for (; i < len; i++) {
            comp
= this.zIndexStack[i];
           
if (fn.call(scope||comp, comp) !== false) {
                r
.push(comp);
           
}
       
}
       
return r;
   
},

    /**
     * Executes the specified function once for every Component in this ZIndexManager, passing each
     * Component as the only parameter. Returning false from the function will stop the iteration.
     * @param {Function} fn The function to execute for each item
     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function is executed. Defaults to the current Component in the iteration.
     */

    each
: function(fn, scope) {
       
var comp;
       
for (var id in this.list) {
            comp
= this.list[id];
           
if (comp.isComponent && fn.call(scope || comp, comp) === false) {
               
return;
           
}
       
}
   
},

    /**
     * Executes the specified function once for every Component in this ZIndexManager, passing each
     * Component as the only parameter. Returning false from the function will stop the iteration.
     * The components are passed to the function starting at the bottom and proceeding to the top.
     * @param {Function} fn The function to execute for each item
     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function
     * is executed. Defaults to the current Component in the iteration.
     */

    eachBottomUp
: function (fn, scope) {
       
var comp,
            stack
= this.zIndexStack,
            i
, n;

       
for (i = 0, n = stack.length ; i < n; i++) {
            comp
= stack[i];
           
if (comp.isComponent && fn.call(scope || comp, comp) === false) {
               
return;
           
}
       
}
   
},

    /**
     * Executes the specified function once for every Component in this ZIndexManager, passing each
     * Component as the only parameter. Returning false from the function will stop the iteration.
     * The components are passed to the function starting at the top and proceeding to the bottom.
     * @param {Function} fn The function to execute for each item
     * @param {Object} scope (optional) The scope (<code>this</code> reference) in which the function
     * is executed. Defaults to the current Component in the iteration.
     */

    eachTopDown
: function (fn, scope) {
       
var comp,
            stack
= this.zIndexStack,
            i
;

       
for (i = stack.length ; i-- > 0; ) {
            comp
= stack[i];
           
if (comp.isComponent && fn.call(scope || comp, comp) === false) {
               
return;
           
}
       
}
   
},

    destroy
: function() {
       
delete this.zIndexStack;
       
delete this.list;
       
delete this.container;
       
delete this.targetEl;
   
}
}, function() {
    /**
     * @class Ext.WindowManager
     * @extends Ext.ZIndexManager
     * <p>The default global floating Component group that is available automatically.</p>
     * <p>This manages instances of floating Components which were rendered programatically without
     * being added to a {@link Ext.container.Container Container}, and for floating Components which were added into non-floating Containers.</p>
     * <p><i>Floating</i> Containers create their own instance of ZIndexManager, and floating Components added at any depth below
     * there are managed by that ZIndexManager.</p>
     * @singleton
     */

   
Ext.WindowManager = Ext.WindowMgr = new this();
});