Extjs源码之-Ext.lib.Event(事件的容易封装)

Extjs源码之--Ext.lib.Event(事件的简单封装)这个对象是对事件的基础封装,在实际使用中,我们往往不从这里

Extjs源码之--Ext.lib.Event(事件的简单封装)

这个对象是对事件的基础封装,在实际使用中,我们往往不从这里去引用,而更多的是从Ext.EventManager中引用,这个只是为Ext.EventManager的接口做准备,这里定义:添加监听,删除监听,取消冒泡,阻止事件默认行为,事件目标,事件类型,事件源,以及鼠标事件的客户区坐标位置,屏幕坐标位置,键码,字符编码等方面对浏览器兼容性进行了处理,事件是兼容性最差的一块,所以再写自己应用的时候一定要多查相关资料,还是Nicholas C. Zakas《javascript 高级程序设计》第二版 第12章,可以说EXT源码中涉及到的,这本书里面都有说到!所以我这里也没有每个方法都去解释一遍

Ext.lib.Event = function() {//一堆的变量/常量的定义,有些我们可能觉得是没必要的,但Ext就是这么做,如SCROLLLEFT 这样的,但win和doc我是觉得完全有必要的,这样在压缩的时候能减少字节数,同时也能减少对象查找,这里定义后查找的函数内的局部变量,但不定义查询的是全局变量,所以EXT这么做都是又其道理的    var loadComplete = false,        unloadListeners = {},        retryCount = 0,        onAvailStack = [],        _interval,        locked = false,        win = window,        doc = document,        // constants        POLL_RETRYS = 200,        POLL_INTERVAL = 20,        EL = 0,        TYPE = 0,        FN = 1,        WFN = 2,        OBJ = 2,        ADJ_SCOPE = 3,        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') {//ie专有的事件,只有第一次才被激活,这里通过MOUSEOVER模拟mouseenter事件,这里的做法是通过拦截器实现                        fn = fn.createInterceptor(checkRelatedTarget);                        el.addEventListener(MOUSEOVER, fn, (capture));                    } else if (eventName == 'mouseleave') {//ie专有的事件,只有第一次才被激活                        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') {//因为添加是由MOUSEOVER模拟的所以这里删除就只要删除MOUSEOVER就行了                        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;        }();    function checkRelatedTarget(e) {        return !elContains(e.currentTarget, pub.getRelatedTarget(e));    }    //这里为什么这么写,我又点迷惑,为什么不像 Ext.lib.Dom 中 isAncestor一样写,估计是EXT开发人员自己忽视了    function elContains(parent, child) {       if(parent && parent.firstChild){         while(child) {            if(child === parent) {                return true;            }            child = child.parentNode;            if(child && (child.nodeType != 1)) {                child = null;            }          }        }        return false;    }    // private    function _tryPreloadAttach() {        var ret = false,            notAvail = [],            element, i, len, v,            tryAgain = !loadComplete || (retryCount > 0);        if (!locked) {            locked = true;            for (i = 0, len = onAvailStack.length; i < len; i++) {                v = onAvailStack[i];                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.remove(v);                    } 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 =  {        extAdapter: true,        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();        },//这个接口一般用户不直接调用,而是调用Ext.EventManager        // This function should ALWAYS be called from Ext.EventManager        addListener: function(el, eventName, fn) {            el = Ext.getDom(el);            if (el && fn) {                if (eventName == UNLOAD) {                    if (unloadListeners[el.id] === undefined) {                        unloadListeners[el.id] = [];                    }                    unloadListeners[el.id].push([eventName, fn]);                    return fn;                }                return doAdd(el, eventName, fn, false);            }            return false;        },        // This function should ALWAYS be called from Ext.EventManager        removeListener: function(el, eventName, fn) {            el = Ext.getDom(el);            var i, len, li, lis;            if (el && fn) {                if(eventName == UNLOAD){                    if((lis = unloadListeners[el.id]) !== undefined){                        for(i = 0, len = lis.length; i < len; i++){                            if((li = lis[i]) && li[TYPE] == eventName && li[FN] == fn){                                unloadListeners[id].splice(i, 1);                            }                        }                    }                    return;                }                doRemove(el, eventName, fn, false);            }        },//返回Target         getTarget : function(ev) {            ev = ev.browserEvent || ev;            return this.resolveTextNode(ev.target || ev.srcElement);        },        resolveTextNode : Ext.isGecko ? function(node){            if(!node){                return;            }            // work around firefox bug, https://bugzilla.mozilla.org/show_bug.cgi?id=101197            var s = HTMLElement.prototype.toString.call(node);            if(s == '[xpconnect wrapped native prototype]' || s == '[object XULElement]'){                return;            }            return node.nodeType == 3 ? node.parentNode : node;        } : function(node){            return node && node.nodeType == 3 ? node.parentNode : node;        },//返回相关/临近的Target         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)];        },        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() {},        // deprecated, call from EventManager        getListeners : function(el, eventName) {            Ext.EventManager.getListeners(el, eventName);        },        // deprecated, call from EventManager        purgeElement : function(el, recurse, eventName) {            Ext.EventManager.purgeElement(el, recurse, eventName);        },        _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);            }        },        _unload : function(e) {             var EU = Ext.lib.Event,                i, j, l, v, ul, id, len, index, scope;            for (id in unloadListeners) {                ul = unloadListeners[id];                for (i = 0, len = ul.length; i < len; i++) {                    v = ul[i];                    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;            Ext.EventManager._unload();            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;}();