Ext.lib.Event = function() {
   
var loadComplete = false,
        listeners
= [],
        unloadListeners
= [],
        retryCount
= 0,
        onAvailStack
= [],
        _interval
,
        locked
= false,
        win
= window,
        doc
= document,
       
       
// constants            
        POLL_RETRYS
= 200,
        POLL_INTERVAL
= 20,
        EL
= 0,
        TYPE
= 1,
        FN
= 2,
        WFN
= 3,
        OBJ
= 3,
        ADJ_SCOPE
= 4,  
        SCROLLLEFT
= 'scrollLeft',
        SCROLLTOP
= 'scrollTop',
        UNLOAD
= 'unload',
        MOUSEOVER
= 'mouseover',
        MOUSEOUT
= 'mouseout',
       
// private
        doAdd
= function() {
           
var ret;
           
if (win.addEventListener) {
                ret
= function(el, eventName, fn, capture) {
                   
if (eventName == 'mouseenter') {
                        fn
= fn.createInterceptor(checkRelatedTarget);
                        el
.addEventListener(MOUSEOVER, fn, (capture));
                   
} else if (eventName == 'mouseleave') {
                        fn
= fn.createInterceptor(checkRelatedTarget);
                        el
.addEventListener(MOUSEOUT, fn, (capture));
                   
} else {
                        el
.addEventListener(eventName, fn, (capture));
                   
}
                   
return fn;
               
};
           
} else if (win.attachEvent) {
                ret
= function(el, eventName, fn, capture) {
                    el
.attachEvent("on" + eventName, fn);
                   
return fn;
               
};
           
} else {
                ret
= function(){};
           
}
           
return ret;
       
}(),    
       
// private
        doRemove
= function(){
           
var ret;
           
if (win.removeEventListener) {
                ret
= function (el, eventName, fn, capture) {
                   
if (eventName == 'mouseenter') {
                        eventName
= MOUSEOVER;
                   
} else if (eventName == 'mouseleave') {
                        eventName
= MOUSEOUT;
                   
}                        
                    el
.removeEventListener(eventName, fn, (capture));
               
};
           
} else if (win.detachEvent) {
                ret
= function (el, eventName, fn) {
                    el
.detachEvent("on" + eventName, fn);
               
};
           
} else {
                ret
= function(){};
           
}
           
return ret;
       
}();        

   
var isXUL = Ext.isGecko ? function(node){
       
return Object.prototype.toString.call(node) == '[object XULElement]';
   
} : function(){};
       
   
var isTextNode = Ext.isGecko ? function(node){
       
try{
           
return node.nodeType == 3;
       
}catch(e) {
           
return false;
       
}

   
} : function(node){
       
return node.nodeType == 3;
   
};
       
   
function checkRelatedTarget(e) {
       
var related = pub.getRelatedTarget(e);
       
return !(isXUL(related) || elContains(e.currentTarget,related));
   
}

   
function elContains(parent, child) {
       
if(parent && parent.firstChild){  
         
while(child) {
           
if(child === parent) {
               
return true;
           
}
           
try {
                child
= child.parentNode;
           
} catch(e) {
               
// In FF if you mouseout an text input element
               
// thats inside a div sometimes it randomly throws
               
// Permission denied to get property HTMLDivElement.parentNode
               
// See https://bugzilla.mozilla.org/show_bug.cgi?id=208427
               
               
return false;
           
}                
           
if(child && (child.nodeType != 1)) {
                child
= null;
           
}
         
}
       
}
       
return false;
   
}

       
   
// private  
   
function _getCacheIndex(el, eventName, fn) {
       
var index = -1;
       
Ext.each(listeners, function (v,i) {
           
if(v && v[FN] == fn && v[EL] == el && v[TYPE] == eventName) {
                index
= i;
           
}
       
});
       
return index;
   
}
                   
   
// private
   
function _tryPreloadAttach() {
       
var ret = false,                
            notAvail
= [],
            element
,
            tryAgain
= !loadComplete || (retryCount > 0);                      
       
       
if (!locked) {
            locked
= true;
           
           
Ext.each(onAvailStack, function (v,i,a){
               
if(v && (element = doc.getElementById(v.id))){
                   
if(!v.checkReady || loadComplete || element.nextSibling || (doc && doc.body)) {
                        element
= v.override ? (v.override === true ? v.obj : v.override) : element;
                        v
.fn.call(element, v.obj);
                        onAvailStack
[i] = null;
                   
} else {
                        notAvail
.push(v);
                   
}
               
}  
           
});

            retryCount
= (notAvail.length === 0) ? 0 : retryCount - 1;

           
if (tryAgain) {
                startInterval
();
           
} else {
                clearInterval
(_interval);
                _interval
= null;
           
}

            ret
= !(locked = false);
       
}
       
return ret;
   
}
   
   
// private              
   
function startInterval() {            
       
if(!_interval){                    
           
var callback = function() {
                _tryPreloadAttach
();
           
};
            _interval
= setInterval(callback, POLL_INTERVAL);
       
}
   
}
   
   
// private
   
function getScroll() {
       
var dd = doc.documentElement,
            db
= doc.body;
       
if(dd && (dd[SCROLLTOP] || dd[SCROLLLEFT])){
           
return [dd[SCROLLLEFT], dd[SCROLLTOP]];
       
}else if(db){
           
return [db[SCROLLLEFT], db[SCROLLTOP]];
       
}else{
           
return [0, 0];
       
}
   
}
       
   
// private
   
function getPageCoord (ev, xy) {
        ev
= ev.browserEvent || ev;
       
var coord  = ev['page' + xy];
       
if (!coord && coord !== 0) {
            coord
= ev['client' + xy] || 0;

           
if (Ext.isIE) {
                coord
+= getScroll()[xy == "X" ? 0 : 1];
           
}
       
}

       
return coord;
   
}

   
var pub =  {
        onAvailable
: function(p_id, p_fn, p_obj, p_override) {            
            onAvailStack
.push({
                id
:         p_id,
                fn
:         p_fn,
                obj
:        p_obj,
                override
:   p_override,
                checkReady
: false });

            retryCount
= POLL_RETRYS;
            startInterval
();
       
},


        addListener
: function(el, eventName, fn) {
           
var ret;                
            el
= Ext.getDom(el);                
           
if (el && fn) {
               
if (UNLOAD == eventName) {
                    ret
= !!(unloadListeners[unloadListeners.length] = [el, eventName, fn]);                    
               
} else {
                    listeners
.push([el, eventName, fn, ret = doAdd(el, eventName, fn, false)]);
               
}
           
}
           
return !!ret;
       
},

        removeListener
: function(el, eventName, fn) {
           
var ret = false,
                index
,
                cacheItem
;

            el
= Ext.getDom(el);

           
if(!fn) {                  
                ret
= this.purgeElement(el, false, eventName);
           
} else if (UNLOAD == eventName) {  
               
Ext.each(unloadListeners, function(v, i, a) {
                   
if( v && v[0] == el && v[1] == eventName && v[2] == fn) {
                        unloadListeners
.splice(i, 1);
                        ret
= true;
                   
}
               
});
           
} else {    
                index
= arguments[3] || _getCacheIndex(el, eventName, fn);
                cacheItem
= listeners[index];
               
               
if (el && cacheItem) {
                    doRemove
(el, eventName, cacheItem[WFN], false);    
                    cacheItem
[WFN] = cacheItem[FN] = null;                      
                    listeners
.splice(index, 1);    
                    ret
= true;
               
}
           
}
           
return ret;
       
},

        getTarget
: function(ev) {
            ev
= ev.browserEvent || ev;                
           
return this.resolveTextNode(ev.target || ev.srcElement);
       
},

        resolveTextNode
: function(node) {
           
return node && !isXUL(node) && isTextNode(node) ? node.parentNode : node;
       
},

        getRelatedTarget
: function(ev) {
            ev
= ev.browserEvent || ev;
           
return this.resolveTextNode(ev.relatedTarget ||
                   
(ev.type == MOUSEOUT ? ev.toElement :
                     ev
.type == MOUSEOVER ? ev.fromElement : null));
       
},
       
        getPageX
: function(ev) {
           
return getPageCoord(ev, "X");
       
},

        getPageY
: function(ev) {
           
return getPageCoord(ev, "Y");
       
},


        getXY
: function(ev) {                            
           
return [this.getPageX(ev), this.getPageY(ev)];
       
},

// Is this useful?  Removing to save space unless use case exists.
//             getTime: function(ev) {
//                 ev = ev.browserEvent || ev;
//                 if (!ev.time) {
//                     var t = new Date().getTime();
//                     try {
//                         ev.time = t;
//                     } catch(ex) {
//                         return t;
//                     }
//                 }

//                 return ev.time;
//             },

        stopEvent
: function(ev) {                            
           
this.stopPropagation(ev);
           
this.preventDefault(ev);
       
},

        stopPropagation
: function(ev) {
            ev
= ev.browserEvent || ev;
           
if (ev.stopPropagation) {
                ev
.stopPropagation();
           
} else {
                ev
.cancelBubble = true;
           
}
       
},

        preventDefault
: function(ev) {
            ev
= ev.browserEvent || ev;
           
if (ev.preventDefault) {
                ev
.preventDefault();
           
} else {
                ev
.returnValue = false;
           
}
       
},
       
        getEvent
: function(e) {
            e
= e || win.event;
           
if (!e) {
               
var c = this.getEvent.caller;
               
while (c) {
                    e
= c.arguments[0];
                   
if (e && Event == e.constructor) {
                       
break;
                   
}
                    c
= c.caller;
               
}
           
}
           
return e;
       
},

        getCharCode
: function(ev) {
            ev
= ev.browserEvent || ev;
           
return ev.charCode || ev.keyCode || 0;
       
},

       
//clearCache: function() {},

        _load
: function(e) {
            loadComplete
= true;
           
var EU = Ext.lib.Event;    
           
if (Ext.isIE && e !== true) {
       
// IE8 complains that _load is null or not an object
       
// so lets remove self via arguments.callee
                doRemove
(win, "load", arguments.callee);
           
}
       
},            
       
        purgeElement
: function(el, recurse, eventName) {
           
var me = this;
           
Ext.each( me.getListeners(el, eventName), function(v){
               
if(v){
                    me
.removeListener(el, v.type, v.fn);
               
}
           
});

           
if (recurse && el && el.childNodes) {
               
Ext.each(el.childNodes, function(v){
                    me
.purgeElement(v, recurse, eventName);
               
});
           
}
       
},

        getListeners
: function(el, eventName) {
           
var me = this,
                results
= [],
                searchLists
;

           
if (eventName){  
                searchLists
= eventName == UNLOAD ? unloadListeners : listeners;
           
}else{
                searchLists
= listeners.concat(unloadListeners);
           
}

           
Ext.each(searchLists, function(v, i){
               
if (v && v[EL] == el && (!eventName || eventName == v[TYPE])) {
                    results
.push({
                                type
:   v[TYPE],
                                fn
:     v[FN],
                                obj
:    v[OBJ],
                                adjust
: v[ADJ_SCOPE],
                                index
:  i
                           
});
               
}  
           
});                

           
return results.length ? results : null;
       
},

        _unload
: function(e) {
             
var EU = Ext.lib.Event,
                i
,
                j
,
                l
,
                len
,
                index
,
                scope
;
               

           
Ext.each(unloadListeners, function(v) {
               
if (v) {
                   
try{
                        scope
=  v[ADJ_SCOPE] ? (v[ADJ_SCOPE] === true ? v[OBJ] : v[ADJ_SCOPE]) :  win;
                        v
[FN].call(scope, EU.getEvent(e), v[OBJ]);
                   
}catch(ex){}
               
}  
           
});    

            unloadListeners
= null;

           
if(listeners && (j = listeners.length)){                    
               
while(j){                        
                   
if((l = listeners[index = --j])){
                        EU
.removeListener(l[EL], l[TYPE], l[FN], index);
                   
}                        
               
}
               
//EU.clearCache();
           
}

            doRemove
(win, UNLOAD, EU._unload);
       
}            
   
};        
   
   
// Initialize stuff.
    pub
.on = pub.addListener;
    pub
.un = pub.removeListener;
   
if (doc && doc.body) {
        pub
._load(true);
   
} else {
        doAdd
(win, "load", pub._load);
   
}
    doAdd
(win, UNLOAD, pub._unload);    
    _tryPreloadAttach
();
   
   
return pub;
}();