mass Framework emitter模块 v2

此模块用于提供自定义事件,并把实现此接口的对象变成一个事件发送器。

//==================================================
// 事件发送器模块
//==================================================
(function(global,DOC){
    var dom = global[DOC.URL.replace(/(#.+|\W)/g,'')];
    dom.define("emitter","data", function(){
        var fireType = "", blank = ""
        var rhoverHack =  /\bhover(\.\S+)?/,
        rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/, reprop = /[a-z]/;
        var system = dom.event = {
            special:{},//用于处理个别的DOM事件
            bind : function( types, handler, selector){
                //它将在原生事件发送器或任何能成为事件发送器的普通JS对象添加一个名叫uniqueNumber的属性,用于关联一个缓存体,
                //把需要的数据储存到里面,而现在我们就把一个叫@events的对象储放都它里面,
                //而这个@event的表将用来放置各种事件类型与对应的回调函数
                var target = this, events = dom._data( target) , nativeEmitter =  dom["@emitter"] in target,
                all, tns ,type, namespace, special, handlerObj, handlers, fn;
                if(target.nodeType === 3 || target.nodeType === 8 || !types || typeof handler !=="function" || !events) return ;
                all = {
                    handler:handler,
                    uuid: dom.uuid++
                }
                //确保UUID,bag与callback的UUID一致
                all.handler.uuid = all.uuid;
                if(nativeEmitter ){
                    //处理DOM事件
                    fn = events.handle ||  (events.handle = function( e ) {
                        return ((e || event).type !== fireType) ? system.handle.apply( fn.target, arguments ) :void 0;
                    });
                    fn.target = target;
                    types = types.replace( rhoverHack, "mouseover$1 mouseout$1" )
                }
                events = events.events || (events.events = {});
                //对多个事件进行绑定
                types.replace(dom.rword,function(type){
                    tns = rtypenamespace.exec( type ) || [];
                    type = tns[1];//取得事件类型
                    namespace = (tns[2] || "").split( "." ).sort();//取得命名空间
                    //事件冒充只用于原生事件发送器
                    special = nativeEmitter && system.special[ type ] || {};
                    type = (selector? special.delegateType : special.bindType ) || type;
                    special = nativeEmitter && system.special[ type ] || {};
                    handlerObj = dom.mix({
                        type: type,
                        origType: tns[1],
                        selector: selector,
                        namespace: namespace.join(".")
                    }, all);
                    //创建事件队列
                    handlers = events[ type ] = events[ type ] ||  [];
                    //只有原生事件发送器才能进行DOM level2 多投事件绑定
                    if(nativeEmitter && !handlers.length  ){
                        if (!special.setup || special.setup( target, selector, fn ) === false ) {
                            // 为此元素这种事件类型绑定一个全局的回调,用户的回调则在此回调中执行
                            dom.bind(target,type,fn,!!selector)
                        }
                    }
                    handlers.push( handlerObj );
                });
            },

            unbind: function( types, handler, selector ) {
                var target = this, events = dom._data( target,"events")
                if(!events) return;
                var t, tns, type, namespace, origCount,nativeEmitter =  dom["@emitter"] in target,
                j, special, handlers, handlerObj;
         
                types = nativeEmitter ? (types || "").replace( rhoverHack, "mouseover$1 mouseout$1" ) : types;
                types = (types || "").split(" ");
                for ( t = 0; t < types.length; t++ ) {
                    tns = rtypenamespace.exec( types[t] ) || [];
                    type = tns[1];
                    namespace = tns[2];
                    // 如果types只包含命名空间,则去掉所有拥有此命名空间的事件类型的回调
                    if ( !type  ) {
                        namespace = namespace? "." + namespace : "";
                        for ( j in events ) {
                            system.unbind.call( target, j + namespace, handler, selector );
                        }
                        return;
                    }
                    //如果使用事件冒充则找到其正确事件类型
                    special = system.special[ type ] || {};
                    type = (selector? special.delegateType : special.bindType ) || type;
                    handlers = events[ type ] || [];
                    origCount = handlers.length;
                    namespace =  namespace?  namespace.split( "." ).sort().join(".") : null;
                    //只有指定了命名空间,回调或选择器才能进入此分支
                    if ( handler || namespace || selector ) {
                        for ( j = 0; j < handlers.length; j++ ) {
                            handlerObj = handlers[ j ];
                            if ( !handler || handler.uuid === handlerObj.uuid ) {
                                if ( !namespace || namespace === handlerObj.namespace  ) {
                                    if ( !selector || selector === handlerObj.selector || selector === "**" && handlerObj.selector ) {
                                        handlers.splice( j--, 1 );
                                    }
                                }
                            }
                        }
                    } else {
                        //移除此类事件的所有回调
                        handlers.length = 0;
                    }
                    if (nativeEmitter && (handlers.length === 0 && origCount !== handlers.length) ) {
                        if ( !special.teardown || special.teardown( target, selector, handler ) === false ) {
                            dom.unbind( target, type, dom._data(target,"handle") );
                        }
                        delete events[ type ];
                    }
                }
                if(dom.isEmptyObject(events)){
                    var handle = dom.removeData( target,"handle") ;
                    handle.elem = null;
                    dom.removeData( target,"events") ;
                }
            },

            fire:function(event){
                var target = this, namespace = [], type = event.type || event
                if ( type.indexOf( "." ) !== -1 ) {
                    namespace = type.split(".");
                    type = namespace.shift();
                    namespace.sort();
                }
                var events = dom._data( target,"events") ,args = dom.slice(arguments,1)
                if(!events) return;
                event = (typeof event == "object" && "namespace" in event)? type : new jEvent(type);
                event.target = target;
                event.fireArgs = args;
                event.namespace = namespace.join( "." );
                if( dom["@emitter"] in target){
                    var special = system.special[ type ] || {};
                    if ( special.fire && special.fire.call( target, event ) === false ) {
                        return;
                    }
                    var cur = target,  ontype = "on" + type;
                    do{//模拟事件冒泡与执行内联事件
                        system.handle.call(cur, event);
                        if (cur[ ontype ] && cur[ ontype ].call(cur) === false) {
                            event.preventDefault();
                        }
                        cur = cur.parentNode ||
                        cur.ownerDocument ||
                        cur === target.ownerDocument && global;
                    } while (cur && !event.isPropagationStopped);
                    if (!event.isDefaultPrevented) {//模拟默认行为 click() submit() reset() focus() blur()
                        var old;
                        if (ontype && target[ type ] && ((type !== "focus" && type !== "blur") || target.offsetWidth !== 0) && !target.document) {
                            old = target[ ontype ];
                            if (old) {   // 不用再触发内联事件
                                target[ ontype ] = null;
                            }
                            fireType = type;
                            target[ type ]();
                        }
                        fireType = blank;
                        if (old) {
                            target[ ontype ] = old;
                        }
                    }

                }else{//普通对象的自定义事件
                    system.handle.call(target, event);
                }
            },
            filter:function(cur, parent, expr){
                for ( ; cur != parent; cur = cur.parentNode || parent ) {
                    if(dom.matchesSelector(cur, expr))
                        return true
                }
                return false;
            },
            handle: function( e ) {
                var event = system.fix( e || event ),
                handlers = dom._data(this,"events");
                if (  handlers ) {
                    handlers = handlers[event.type]||[]
                    arguments[0] = event;
                    event.currentTarget = this;
                    var src = event.target, result,
                    //取得参数(只有自定义才有多个参数)
                    args = "fireArgs" in event ? [event].concat(event.fireArgs) : arguments;
                    //复制数组以防影响下一次的操作
                    handlers = handlers.concat();
                    //开始进行拆包操作
                    for ( var i = 0, obj; obj = handlers[i++]; ) {
                        //如果是事件代理,确保元素处于enabled状态,并且满足过滤条件
                        if ( !src.disabled && !(event.button && event.type === "click")
                            && (!obj.selector  || system.filter(src, this, obj.selector))
                            && (!event.namespace || event.namespace === obj.namespace ) ) {
                            //取得回调函数
                            result = obj.handler.apply( src, args );
                            if ( result !== undefined ) {
                                event.result = result;
                                if ( result === false ) {
                                    event.preventDefault();
                                    event.stopPropagation();
                                }
                            }
                            if ( event.isImmediatePropagationStopped ) {
                                break;
                            }
                        }
                    }
                }

                return event.result;
            },

            fix :function(event){
                if(!("namespace" in event)){
                    var originalEvent = event
                    event = new jEvent(originalEvent);
                    for(var prop in originalEvent){
                        //去掉所有方法与常量
                        if(typeof originalEvent[prop] !== "function" && reprop.test(prop)){
                            event[prop] = originalEvent[prop]
                        }
                    }
                    event.wheelDelta = 0;
                    //mousewheel
                    if ("wheelDelta" in originalEvent){
                        var detail = originalEvent.wheelDelta;
                        //opera 9x系列的滚动方向与IE保持一致,10后修正
                        if(global.opera && global.opera.version() < 10)
                            detail = -detail;
                        event.wheelDelta = Math.round(detail); //修正safari的浮点 bug
                    }else {
                        //DOMMouseScroll
                        event.wheelDelta = -originalEvent.detail*40;
                    }
                    //如果不存在target属性,为它添加一个
                    if ( !event.target ) {
                        // 判定鼠标事件按下的是哪个键,1 === left; 2 === middle; 3 === right
                        event.which  = event.button === 2 ? 3 : event.button === 4 ? 2 : 1;
                        event.target = event.srcElement || DOC;
                    }
                    //如果事件源对象为文本节点,则置入其父元素
                    if ( event.target.nodeType === 3 ) {
                        event.target = event.target.parentNode;
                    }
                    //如果不存在relatedTarget属性,为它添加一个
                    if ( !event.relatedTarget && event.fromElement ) {
                        event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
                    }

                    //如果不存在pageX/Y则结合clientX/Y做一双出来
                    if ( event.pageX == null && event.clientX != null ) {
                        var doc = event.target.ownerDocument || DOC,
                        html = doc.documentElement, body = doc.body;
                        event.pageX = event.clientX + (html && html.scrollLeft || body && body.scrollLeft || 0) - (html && html.clientLeft || body && body.clientLeft || 0);
                        event.pageY = event.clientY + (html && html.scrollTop  || body && body.scrollTop  || 0) - (html && html.clientTop  || body && body.clientTop  || 0);
                    }
                    // 为键盘事件添加which事件
                    if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
                        event.which = event.charCode || event.keyCode;
                    }
                    // Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
                    if ( !event.metaKey && event.ctrlKey ) {
                        event.metaKey = event.ctrlKey;
                    }
                }
                return event;
            },

            setup: dom.bind,

            teardown:dom.unbind
        }
        var jEvent = dom.Event = function ( event ) {
            this.originalEvent = event.substr ? {} : event;
            this.type = event.type || event;
            this.namespace = "";//用于判定是否为伪事件对象
        };
        // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
        jEvent.prototype = {
            constructor:jEvent,
            //http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/events.html#Conformance
            toString:function(){
                return "[object Event]"
            },
            preventDefault: function() {
                this.isDefaultPrevented = true;
                var e = this.originalEvent;
                // 如果存在preventDefault 那么就调用它
                if ( e.preventDefault ) {
                    e.preventDefault();
                }
                // 如果存在returnValue 那么就将它设为false
                e.returnValue = false;
                return this;
            },
            stopPropagation: function() {
                this.isPropagationStopped = true;
                var e = this.originalEvent;
                // 如果存在preventDefault 那么就调用它
                if ( e.stopPropagation ) {
                    e.stopPropagation();
                }
                // 如果存在returnValue 那么就将它设为true
                e.cancelBubble = true;
                return this;
            },
            stopImmediatePropagation: function() {
                this.isImmediatePropagationStopped = true;
                this.stopPropagation();
                return this;
            }
        };
        //事件发射体emitter的接口
        //实现了这些接口的对象将具有注册事件和触发事件的功能
        dom.emitter = {};
        "bind,unbind,fire".replace(dom.rword,function(name){
            dom.emitter[name] = function(){
                system[name].apply(this, arguments);
                return this;
            }
        });
        dom.emitter.uniqueNumber = ++dom.uuid;
        dom.emitter.defineEvents = function(names){
            var events = [];
            if(typeof names == "string"){
                events = names.match(dom.rword);
            }else if(dom.isArray(names)){
                events = names;
            }
            events.forEach(function(name){
                var method = 'on'+name.replace(/(^|_|:)([a-z])/g,function($, $1, $2) {
                    return $2.toUpperCase();
                });
                if (!(method in this)) {
                    this[method] = function() {
                        return this.bind.apply(this, [].concat.apply([name],arguments));
                    };
                }
            },this);
        }
    });
})(this,this.document);
//2011.8.14 更改隐藏namespace,让自定义对象的回调函数也有事件对象
//2011.9.17 事件发送器增加一个uniqueID属性
//2011.9.21 重构bind与unbind方法 支持命名空间与多事件处理
//2011.9.27 uniqueID改为uniqueNumber 使用dom._data存取数据
//2011.9.29 简化bind与unbind
原文地址:https://www.cnblogs.com/rubylouvre/p/2189933.html