/**
 * @class Ext.FocusManager

The FocusManager is responsible for globally:

1. Managing component focus
2. Providing basic keyboard navigation
3. (optional) Provide a visual cue for focused components, in the form of a focus ring/frame.

To activate the FocusManager, simply call {@link #enable `Ext.FocusManager.enable();`}. In turn, you may
deactivate the FocusManager by subsequently calling {@link #disable `Ext.FocusManager.disable();`}.  The
FocusManager is disabled by default.

To enable the optional focus frame, pass `true` or `{focusFrame: true}` to {@link #enable}.

Another feature of the FocusManager is to provide basic keyboard focus navigation scoped to any {@link Ext.container.Container}
that would like to have navigation between its child {@link Ext.Component}'s. The {@link Ext.container.Container} can simply
call {@link #subscribe Ext.FocusManager.subscribe} to take advantage of this feature, and can at any time call
{@link #unsubscribe Ext.FocusManager.unsubscribe} to turn the navigation off.

 * @singleton
 * @markdown
 * @author Jarred Nicholls <jarred@sencha.com>
 * @docauthor Jarred Nicholls <jarred@sencha.com>
 */

Ext.define('Ext.FocusManager', {
    singleton
: true,
    alternateClassName
: 'Ext.FocusMgr',

    mixins
: {
        observable
: 'Ext.util.Observable'
   
},

    requires
: [
       
'Ext.ComponentManager',
       
'Ext.ComponentQuery',
       
'Ext.util.HashMap',
       
'Ext.util.KeyNav'
   
],

    /**
     * @property {Boolean} enabled
     * Whether or not the FocusManager is currently enabled
     */

    enabled
: false,

    /**
     * @property {Ext.Component} focusedCmp
     * The currently focused component. Defaults to `undefined`.
     * @markdown
     */


    focusElementCls
: Ext.baseCSSPrefix + 'focus-element',

    focusFrameCls
: Ext.baseCSSPrefix + 'focus-frame',

    /**
     * @property {Array} whitelist
     * A list of xtypes that should ignore certain navigation input keys and
     * allow for the default browser event/behavior. These input keys include:
     *
     * 1. Backspace
     * 2. Delete
     * 3. Left
     * 4. Right
     * 5. Up
     * 6. Down
     *
     * The FocusManager will not attempt to navigate when a component is an xtype (or descendents thereof)
     * that belongs to this whitelist. E.g., an {@link Ext.form.field.Text} should allow
     * the user to move the input cursor left and right, and to delete characters, etc.
     *
     * This whitelist currently defaults to `['textfield']`.
     * @markdown
     */

    whitelist
: [
       
'textfield'
   
],

    tabIndexWhitelist
: [
       
'a',
       
'button',
       
'embed',
       
'frame',
       
'iframe',
       
'img',
       
'input',
       
'object',
       
'select',
       
'textarea'
   
],

    constructor
: function() {
       
var me = this,
            CQ
= Ext.ComponentQuery;

        me
.addEvents(
            /**
             * @event beforecomponentfocus
             * Fires before a component becomes focused. Return `false` to prevent
             * the component from gaining focus.
             * @param {Ext.FocusManager} fm A reference to the FocusManager singleton
             * @param {Ext.Component} cmp The component that is being focused
             * @param {Ext.Component} previousCmp The component that was previously focused,
             * or `undefined` if there was no previously focused component.
             * @markdown
             */

           
'beforecomponentfocus',

            /**
             * @event componentfocus
             * Fires after a component becomes focused.
             * @param {Ext.FocusManager} fm A reference to the FocusManager singleton
             * @param {Ext.Component} cmp The component that has been focused
             * @param {Ext.Component} previousCmp The component that was previously focused,
             * or `undefined` if there was no previously focused component.
             * @markdown
             */

           
'componentfocus',

            /**
             * @event disable
             * Fires when the FocusManager is disabled
             * @param {Ext.FocusManager} fm A reference to the FocusManager singleton
             */

           
'disable',

            /**
             * @event enable
             * Fires when the FocusManager is enabled
             * @param {Ext.FocusManager} fm A reference to the FocusManager singleton
             */

           
'enable'
       
);

       
// Setup KeyNav that's bound to document to catch all
       
// unhandled/bubbled key events for navigation
        me
.keyNav = Ext.create('Ext.util.KeyNav', Ext.getDoc(), {
            disabled
: true,
            scope
: me,

            backspace
: me.focusLast,
            enter
: me.navigateIn,
            esc
: me.navigateOut,
            tab
: me.navigateSiblings

           
//space: me.navigateIn,
           
//del: me.focusLast,
           
//left: me.navigateSiblings,
           
//right: me.navigateSiblings,
           
//down: me.navigateSiblings,
           
//up: me.navigateSiblings
       
});

        me
.focusData = {};
        me
.subscribers = Ext.create('Ext.util.HashMap');
        me
.focusChain = {};

       
// Setup some ComponentQuery pseudos
       
Ext.apply(CQ.pseudos, {
            focusable
: function(cmps) {
               
var len = cmps.length,
                    results
= [],
                    i
= 0,
                    c
,

                    isFocusable
= function(x) {
                       
return x && x.focusable !== false && CQ.is(x, '[rendered]:not([destroying]):not([isDestroyed]):not([disabled]){isVisible(true)}{el && c.el.dom && c.el.isVisible()}');
                   
};

               
for (; i < len; i++) {
                    c
= cmps[i];
                   
if (isFocusable(c)) {
                        results
.push(c);
                   
}
               
}

               
return results;
           
},

            nextFocus
: function(cmps, idx, step) {
                step
= step || 1;
                idx
= parseInt(idx, 10);

               
var len = cmps.length,
                    i
= idx + step,
                    c
;

               
for (; i != idx; i += step) {
                   
if (i >= len) {
                        i
= 0;
                   
} else if (i < 0) {
                        i
= len - 1;
                   
}

                    c
= cmps[i];
                   
if (CQ.is(c, ':focusable')) {
                       
return [c];
                   
} else if (c.placeholder && CQ.is(c.placeholder, ':focusable')) {
                       
return [c.placeholder];
                   
}
               
}

               
return [];
           
},

            prevFocus
: function(cmps, idx) {
               
return this.nextFocus(cmps, idx, -1);
           
},

            root
: function(cmps) {
               
var len = cmps.length,
                    results
= [],
                    i
= 0,
                    c
;

               
for (; i < len; i++) {
                    c
= cmps[i];
                   
if (!c.ownerCt) {
                        results
.push(c);
                   
}
               
}

               
return results;
           
}
       
});
   
},

    /**
     * Adds the specified xtype to the {@link #whitelist}.
     * @param {String/Array} xtype Adds the xtype(s) to the {@link #whitelist}.
     */

    addXTypeToWhitelist
: function(xtype) {
       
var me = this;

       
if (Ext.isArray(xtype)) {
           
Ext.Array.forEach(xtype, me.addXTypeToWhitelist, me);
           
return;
       
}

       
if (!Ext.Array.contains(me.whitelist, xtype)) {
            me
.whitelist.push(xtype);
       
}
   
},

    clearComponent
: function(cmp) {
        clearTimeout
(this.cmpFocusDelay);
       
if (!cmp.isDestroyed) {
            cmp
.blur();
       
}
   
},

    /**
     * Disables the FocusManager by turning of all automatic focus management and keyboard navigation
     */

    disable
: function() {
       
var me = this;

       
if (!me.enabled) {
           
return;
       
}

       
delete me.options;
        me
.enabled = false;

       
Ext.ComponentManager.all.un('add', me.onComponentCreated, me);

        me
.removeDOM();

       
// Stop handling key navigation
        me
.keyNav.disable();

       
// disable focus for all components
        me
.setFocusAll(false);

        me
.fireEvent('disable', me);
   
},

    /**
     * Enables the FocusManager by turning on all automatic focus management and keyboard navigation
     * @param {Boolean/Object} options Either `true`/`false` to turn on the focus frame, or an object of the following options:
        - focusFrame : Boolean
            `true` to show the focus frame around a component when it is focused. Defaults to `false`.
     * @markdown
     */

    enable
: function(options) {
       
var me = this;

       
if (options === true) {
            options
= { focusFrame: true };
       
}
        me
.options = options = options || {};

       
if (me.enabled) {
           
return;
       
}

       
// Handle components that are newly added after we are enabled
       
Ext.ComponentManager.all.on('add', me.onComponentCreated, me);

        me
.initDOM(options);

       
// Start handling key navigation
        me
.keyNav.enable();

       
// enable focus for all components
        me
.setFocusAll(true, options);

       
// Finally, let's focus our global focus el so we start fresh
        me
.focusEl.focus();
       
delete me.focusedCmp;

        me
.enabled = true;
        me
.fireEvent('enable', me);
   
},

    focusLast
: function(e) {
       
var me = this;

       
if (me.isWhitelisted(me.focusedCmp)) {
           
return true;
       
}

       
// Go back to last focused item
       
if (me.previousFocusedCmp) {
            me
.previousFocusedCmp.focus();
       
}
   
},

    getRootComponents
: function() {
       
var me = this,
            CQ
= Ext.ComponentQuery,
           
inline = CQ.query(':focusable:root:not([floating])'),
            floating
= CQ.query(':focusable:root[floating]');

       
// Floating items should go to the top of our root stack, and be ordered
       
// by their z-index (highest first)
        floating
.sort(function(a, b) {
           
return a.el.getZIndex() > b.el.getZIndex();
       
});

       
return floating.concat(inline);
   
},

    initDOM
: function(options) {
       
var me = this,
            sp
= '&#160',
            cls
= me.focusFrameCls;

       
if (!Ext.isReady) {
           
Ext.onReady(me.initDOM, me);
           
return;
       
}

       
// Create global focus element
       
if (!me.focusEl) {
            me
.focusEl = Ext.getBody().createChild({
                tabIndex
: '-1',
                cls
: me.focusElementCls,
                html
: sp
           
});
       
}

       
// Create global focus frame
       
if (!me.focusFrame && options.focusFrame) {
            me
.focusFrame = Ext.getBody().createChild({
                cls
: cls,
                children
: [
                   
{ cls: cls + '-top' },
                   
{ cls: cls + '-bottom' },
                   
{ cls: cls + '-left' },
                   
{ cls: cls + '-right' }
               
],
                style
: 'top: -100px; left: -100px;'
           
});
            me
.focusFrame.setVisibilityMode(Ext.core.Element.DISPLAY);
            me
.focusFrameWidth = me.focusFrame.child('.' + cls + '-top').getHeight();
            me
.focusFrame.hide().setLeftTop(0, 0);
       
}
   
},

    isWhitelisted
: function(cmp) {
       
return cmp && Ext.Array.some(this.whitelist, function(x) {
           
return cmp.isXType(x);
       
});
   
},

    navigateIn
: function(e) {
       
var me = this,
            focusedCmp
= me.focusedCmp,
            rootCmps
,
            firstChild
;

       
if (!focusedCmp) {
           
// No focus yet, so focus the first root cmp on the page
            rootCmps
= me.getRootComponents();
           
if (rootCmps.length) {
                rootCmps
[0].focus();
           
}
       
} else {
           
// Drill into child ref items of the focused cmp, if applicable.
           
// This works for any Component with a getRefItems implementation.
            firstChild
= Ext.ComponentQuery.query('>:focusable', focusedCmp)[0];
           
if (firstChild) {
                firstChild
.focus();
           
} else {
               
// Let's try to fire a click event, as if it came from the mouse
               
if (Ext.isFunction(focusedCmp.onClick)) {
                    e
.button = 0;
                    focusedCmp
.onClick(e);
                    focusedCmp
.focus();
               
}
           
}
       
}
   
},

    navigateOut
: function(e) {
       
var me = this,
            parent
;

       
if (!me.focusedCmp || !(parent = me.focusedCmp.up(':focusable'))) {
            me
.focusEl.focus();
           
return;
       
}

        parent
.focus();
   
},

    navigateSiblings
: function(e, source, parent) {
       
var me = this,
            src
= source || me,
            key
= e.getKey(),
            EO
= Ext.EventObject,
            goBack
= e.shiftKey || key == EO.LEFT || key == EO.UP,
            checkWhitelist
= key == EO.LEFT || key == EO.RIGHT || key == EO.UP || key == EO.DOWN,
            nextSelector
= goBack ? 'prev' : 'next',
            idx
, next, focusedCmp;

        focusedCmp
= (src.focusedCmp && src.focusedCmp.comp) || src.focusedCmp;
       
if (!focusedCmp && !parent) {
           
return;
       
}

       
if (checkWhitelist && me.isWhitelisted(focusedCmp)) {
           
return true;
       
}

        parent
= parent || focusedCmp.up();
       
if (parent) {
            idx
= focusedCmp ? Ext.Array.indexOf(parent.getRefItems(), focusedCmp) : -1;
           
next = Ext.ComponentQuery.query('>:' + nextSelector + 'Focus(' + idx + ')', parent)[0];
           
if (next && focusedCmp !== next) {
               
next.focus();
               
return next;
           
}
       
}
   
},

    onComponentBlur
: function(cmp, e) {
       
var me = this;

       
if (me.focusedCmp === cmp) {
            me
.previousFocusedCmp = cmp;
           
delete me.focusedCmp;
       
}

       
if (me.focusFrame) {
            me
.focusFrame.hide();
       
}
   
},

    onComponentCreated
: function(hash, id, cmp) {
       
this.setFocus(cmp, true, this.options);
   
},

    onComponentDestroy
: function(cmp) {
       
this.setFocus(cmp, false);
   
},

    onComponentFocus
: function(cmp, e) {
       
var me = this,
            chain
= me.focusChain;

       
if (!Ext.ComponentQuery.is(cmp, ':focusable')) {
            me
.clearComponent(cmp);

           
// Check our focus chain, so we don't run into a never ending recursion
           
// If we've attempted (unsuccessfully) to focus this component before,
           
// then we're caught in a loop of child->parent->...->child and we
           
// need to cut the loop off rather than feed into it.
           
if (chain[cmp.id]) {
               
return;
           
}

           
// Try to focus the parent instead
           
var parent = cmp.up();
           
if (parent) {
               
// Add component to our focus chain to detect infinite focus loop
               
// before we fire off an attempt to focus our parent.
               
// See the comments above.
                chain
[cmp.id] = true;
                parent
.focus();
           
}

           
return;
       
}

       
// Clear our focus chain when we have a focusable component
        me
.focusChain = {};

       
// Defer focusing for 90ms so components can do a layout/positioning
       
// and give us an ability to buffer focuses
        clearTimeout
(me.cmpFocusDelay);
       
if (arguments.length !== 2) {
            me
.cmpFocusDelay = Ext.defer(me.onComponentFocus, 90, me, [cmp, e]);
           
return;
       
}

       
if (me.fireEvent('beforecomponentfocus', me, cmp, me.previousFocusedCmp) === false) {
            me
.clearComponent(cmp);
           
return;
       
}

        me
.focusedCmp = cmp;

       
// If we have a focus frame, show it around the focused component
       
if (me.shouldShowFocusFrame(cmp)) {
           
var cls = '.' + me.focusFrameCls + '-',
                ff
= me.focusFrame,
                fw
= me.focusFrameWidth,
                box
= cmp.el.getPageBox(),

           
// Size the focus frame's t/b/l/r according to the box
           
// This leaves a hole in the middle of the frame so user
           
// interaction w/ the mouse can continue
                bt
= box.top,
                bl
= box.left,
                bw
= box.width,
                bh
= box.height,
                ft
= ff.child(cls + 'top'),
                fb
= ff.child(cls + 'bottom'),
                fl
= ff.child(cls + 'left'),
                fr
= ff.child(cls + 'right');

            ft
.setWidth(bw - 2).setLeftTop(bl + 1, bt);
            fb
.setWidth(bw - 2).setLeftTop(bl + 1, bt + bh - fw);
            fl
.setHeight(bh - 2).setLeftTop(bl, bt + 1);
            fr
.setHeight(bh - 2).setLeftTop(bl + bw - fw, bt + 1);

            ff
.show();
       
}

        me
.fireEvent('componentfocus', me, cmp, me.previousFocusedCmp);
   
},

    onComponentHide
: function(cmp) {
       
var me = this,
            CQ
= Ext.ComponentQuery,
            cmpHadFocus
= false,
            focusedCmp
,
            parent
;

       
if (me.focusedCmp) {
            focusedCmp
= CQ.query('[id=' + me.focusedCmp.id + ']', cmp)[0];
            cmpHadFocus
= me.focusedCmp.id === cmp.id || focusedCmp;

           
if (focusedCmp) {
                me
.clearComponent(focusedCmp);
           
}
       
}

        me
.clearComponent(cmp);

       
if (cmpHadFocus) {
            parent
= CQ.query('^:focusable', cmp)[0];
           
if (parent) {
                parent
.focus();
           
}
       
}
   
},

    removeDOM
: function() {
       
var me = this;

       
// If we are still enabled globally, or there are still subscribers
       
// then we will halt here, since our DOM stuff is still being used
       
if (me.enabled || me.subscribers.length) {
           
return;
       
}

       
Ext.destroy(
            me
.focusEl,
            me
.focusFrame
       
);
       
delete me.focusEl;
       
delete me.focusFrame;
       
delete me.focusFrameWidth;
   
},

    /**
     * Removes the specified xtype from the {@link #whitelist}.
     * @param {String/Array} xtype Removes the xtype(s) from the {@link #whitelist}.
     */

    removeXTypeFromWhitelist
: function(xtype) {
       
var me = this;

       
if (Ext.isArray(xtype)) {
           
Ext.Array.forEach(xtype, me.removeXTypeFromWhitelist, me);
           
return;
       
}

       
Ext.Array.remove(me.whitelist, xtype);
   
},

    setFocus
: function(cmp, focusable, options) {
       
var me = this,
            el
, dom, data,

            needsTabIndex
= function(n) {
               
return !Ext.Array.contains(me.tabIndexWhitelist, n.tagName.toLowerCase())
                   
&& n.tabIndex <= 0;
           
};

        options
= options || {};

       
// Come back and do this after the component is rendered
       
if (!cmp.rendered) {
            cmp
.on('afterrender', Ext.pass(me.setFocus, arguments, me), me, { single: true });
           
return;
       
}

        el
= cmp.getFocusEl();
        dom
= el.dom;

       
// Decorate the component's focus el for focus-ability
       
if ((focusable && !me.focusData[cmp.id]) || (!focusable && me.focusData[cmp.id])) {
           
if (focusable) {
                data
= {
                    focusFrame
: options.focusFrame
               
};

               
// Only set -1 tabIndex if we need it
               
// inputs, buttons, and anchor tags do not need it,
               
// and neither does any DOM that has it set already
               
// programmatically or in markup.
               
if (needsTabIndex(dom)) {
                    data
.tabIndex = dom.tabIndex;
                    dom
.tabIndex = -1;
               
}

                el
.on({
                    focus
: data.focusFn = Ext.bind(me.onComponentFocus, me, [cmp], 0),
                    blur
: data.blurFn = Ext.bind(me.onComponentBlur, me, [cmp], 0),
                    scope
: me
               
});
                cmp
.on({
                    hide
: me.onComponentHide,
                    close
: me.onComponentHide,
                    beforedestroy
: me.onComponentDestroy,
                    scope
: me
               
});

                me
.focusData[cmp.id] = data;
           
} else {
                data
= me.focusData[cmp.id];
               
if ('tabIndex' in data) {
                    dom
.tabIndex = data.tabIndex;
               
}
                el
.un('focus', data.focusFn, me);
                el
.un('blur', data.blurFn, me);
                cmp
.un('hide', me.onComponentHide, me);
                cmp
.un('close', me.onComponentHide, me);
                cmp
.un('beforedestroy', me.onComponentDestroy, me);

               
delete me.focusData[cmp.id];
           
}
       
}
   
},

    setFocusAll
: function(focusable, options) {
       
var me = this,
            cmps
= Ext.ComponentManager.all.getArray(),
            len
= cmps.length,
            cmp
,
            i
= 0;

       
for (; i < len; i++) {
            me
.setFocus(cmps[i], focusable, options);
       
}
   
},

    setupSubscriberKeys
: function(container, keys) {
       
var me = this,
            el
= container.getFocusEl(),
            scope
= keys.scope,
            handlers
= {
                backspace
: me.focusLast,
                enter
: me.navigateIn,
                esc
: me.navigateOut,
                scope
: me
           
},

            navSiblings
= function(e) {
               
if (me.focusedCmp === container) {
                   
// Root the sibling navigation to this container, so that we
                   
// can automatically dive into the container, rather than forcing
                   
// the user to hit the enter key to dive in.
                   
return me.navigateSiblings(e, me, container);
               
} else {
                   
return me.navigateSiblings(e);
               
}
           
};

       
Ext.iterate(keys, function(key, cb) {
            handlers
[key] = function(e) {
               
var ret = navSiblings(e);

               
if (Ext.isFunction(cb) && cb.call(scope || container, e, ret) === true) {
                   
return true;
               
}

               
return ret;
           
};
       
}, me);

       
return Ext.create('Ext.util.KeyNav', el, handlers);
   
},

    shouldShowFocusFrame
: function(cmp) {
       
var me = this,
            opts
= me.options || {};

       
if (!me.focusFrame || !cmp) {
           
return false;
       
}

       
// Global trumps
       
if (opts.focusFrame) {
           
return true;
       
}

       
if (me.focusData[cmp.id].focusFrame) {
           
return true;
       
}

       
return false;
   
},

    /**
     * Subscribes an {@link Ext.container.Container} to provide basic keyboard focus navigation between its child {@link Ext.Component}'s.
     * @param {Ext.container.Container} container A reference to the {@link Ext.container.Container} on which to enable keyboard functionality and focus management.
     * @param {Boolean/Object} options An object of the following options:
        - keys : Array/Object
            An array containing the string names of navigation keys to be supported. The allowed values are:

            - 'left'
            - 'right'
            - 'up'
            - 'down'

            Or, an object containing those key names as keys with `true` or a callback function as their value. A scope may also be passed. E.g.:

                {
                    left: this.onLeftKey,
                    right: this.onRightKey,
                    scope: this
                }

        - focusFrame : Boolean (optional)
            `true` to show the focus frame around a component when it is focused. Defaults to `false`.
     * @markdown
     */

    subscribe
: function(container, options) {
       
var me = this,
            EA
= Ext.Array,
            data
= {},
            subs
= me.subscribers,

           
// Recursively add focus ability as long as a descendent container isn't
           
// itself subscribed to the FocusManager, or else we'd have unwanted side
           
// effects for subscribing a descendent container twice.
            safeSetFocus
= function(cmp) {
               
if (cmp.isContainer && !subs.containsKey(cmp.id)) {
                    EA
.forEach(cmp.query('>'), safeSetFocus);
                    me
.setFocus(cmp, true, options);
                    cmp
.on('add', data.onAdd, me);
               
} else if (!cmp.isContainer) {
                    me
.setFocus(cmp, true, options);
               
}
           
};

       
// We only accept containers
       
if (!container || !container.isContainer) {
           
return;
       
}

       
if (!container.rendered) {
            container
.on('afterrender', Ext.pass(me.subscribe, arguments, me), me, { single: true });
           
return;
       
}

       
// Init the DOM, incase this is the first time it will be used
        me
.initDOM(options);

       
// Create key navigation for subscriber based on keys option
        data
.keyNav = me.setupSubscriberKeys(container, options.keys);

       
// We need to keep track of components being added to our subscriber
       
// and any containers nested deeply within it (omg), so let's do that.
       
// Components that are removed are globally handled.
       
// Also keep track of destruction of our container for auto-unsubscribe.
        data
.onAdd = function(ct, cmp, idx) {
            safeSetFocus
(cmp);
       
};
        container
.on('beforedestroy', me.unsubscribe, me);

       
// Now we setup focusing abilities for the container and all its components
        safeSetFocus
(container);

       
// Add to our subscribers list
        subs
.add(container.id, data);
   
},

    /**
     * Unsubscribes an {@link Ext.container.Container} from keyboard focus management.
     * @param {Ext.container.Container} container A reference to the {@link Ext.container.Container} to unsubscribe from the FocusManager.
     * @markdown
     */

    unsubscribe
: function(container) {
       
var me = this,
            EA
= Ext.Array,
            subs
= me.subscribers,
            data
,

           
// Recursively remove focus ability as long as a descendent container isn't
           
// itself subscribed to the FocusManager, or else we'd have unwanted side
           
// effects for unsubscribing an ancestor container.
            safeSetFocus
= function(cmp) {
               
if (cmp.isContainer && !subs.containsKey(cmp.id)) {
                    EA
.forEach(cmp.query('>'), safeSetFocus);
                    me
.setFocus(cmp, false);
                    cmp
.un('add', data.onAdd, me);
               
} else if (!cmp.isContainer) {
                    me
.setFocus(cmp, false);
               
}
           
};

       
if (!container || !subs.containsKey(container.id)) {
           
return;
       
}

        data
= subs.get(container.id);
        data
.keyNav.destroy();
        container
.un('beforedestroy', me.unsubscribe, me);
        subs
.removeAtKey(container.id);
        safeSetFocus
(container);
        me
.removeDOM();
   
}
});