mass Framework event模块 v8

最近对事件模块进行疯狂升级的成果。

define("event", top.dispatchEvent ?  ["$node"] : ["$node","$event_fix"],function(){
    $.log("已加载event模块v8")
    var facade = $.event || ($.event = {});
    var adapter = $.eventAdapter || ($.eventAdapter = {})
    var rhoverHack = /(?:^|\s)hover(\.\S+|)\b/
    var bindTop = !adapter.change;//如果没有加载event_fix模块,也就没有change分支,也就说明其是支持dispatchEvent API
    $.eventSupport = function( eventName, el ) {
        el = el || document.createElement("div");
        eventName = "on" + eventName;
        var ret = eventName in el;
        if ( el.setAttribute && !ret ) {
            el.setAttribute( eventName, "" );
            ret = typeof el[ eventName ] === "function";
            el.removeAttribute(eventName);
        }
        el = null;
        return ret;
    };
    /**
     * 从事件类型中分解出有用的信息
     * @param {String} event 事件类型
     * @param {String|Boolean|Undefined} live 用于判定是否使用代理
     */
    var Event = function(event, live){
        var parts = event.split('.');
        var ns = parts.slice(1).sort().join(' ');
        var type = parts[0], hack, tmp;//input -> change -> propertychange
        while( (hack = adapter[ type ]) ){
            tmp = hack[ live ? "delegateType" : "bindType" ];
            if( tmp ){
                type = tmp
            }else{
                break
            }
        }
        //比如在chrome下fire mouseenter, 到这里type为mouseover, origType为mouseenter
        this.type = type;          //事件类型
        this.origType = parts[0]   //原事件类型
        this.live = live;          //是否使用了事件代理,可以是正则,字符串,布尔或空值
        this.ns =   ns,            //命名空间
        this.rns =  ns ? new RegExp("(^|\\.)" + ns.replace(' ', ' .* ?') + "(\\.|$)") : null
    }
    //events为要过滤的集合,后面个参数为过滤条件
    function findHandlers( events, hash, fn, live ) {
        return events.filter(function(desc) {
            return desc && (!hash.rns || hash.rns.test(desc.ns))  //通过事件类型进行过滤
            && (!hash.origType || hash.origType === desc.origType) //通过命名空间进行进行过滤
            && (!fn || fn.uniqueNumber === desc.uuid)              //通过uuid进行过滤
            && (!live || live === desc.live || live === "**" && desc.live )//通过选择器进行过滤
        })
    }
    Event.prototype = {
        toString: function(){
            return "[object Event]"
        },
        preventDefault: function() {
            this.defaultPrevented = true;
            var e = this.originalEvent || {};
            if (e && e.preventDefault ) {
                e.preventDefault();
            }// 如果存在returnValue 那么就将它设为false
            e.returnValue = false;
            return this;
        },
        stopPropagation: function() {
            var e = this.originalEvent || {};
            if (e && e.stopPropagation ) {
                e.stopPropagation();
            } 
            //http://opera.im/kb/userjs/
            e.cancelBubble = this.propagationStopped = true;
            return this;
        },
        stopImmediatePropagation: function() {
            this.isImmediatePropagationStopped = true;
            this.stopPropagation();
            return this;
        }
    }
    $.Event = Event;
    $.mix(facade,{
        //addEventListner API的支持情况:chrome 1+ FF1.6+	IE9+ opera 7+ safari 1+;
        //http://functionsource.com/post/addeventlistener-all-the-way-back-to-ie-6
        bind: function( target, hash ){//事件系统三大核心方法之一,绑定事件
            var bindTarget =  $[ "@bind" ] in target,//是否能直接绑定到目标对象上
            data = $._data( target ),              //是否能绑定事件
            types  = hash.type,                      //原有的事件类型,可能是复数个
            live   = hash.live;                      //是否使用事件代理
            if( !data ){
                return
            }
            if( bindTarget ){                       //处理DOM的hover事件
                types = types.replace( rhoverHack, "mouseover$1 mouseout$1" );
            }
            var events = data.events || (data.events = []);
            hash.uuid = $.getUid( hash.fn );       //确保hash.uuid与fn.uuid一致
            types.replace( $.rword, function( t ){
                var desc = new $.Event( t, live), type = desc.origType;
                $.mix(desc, {
                    currentTarget: target,          //this,用于绑定数据的
                    index:  events.length           //记录其在列表的位置,在卸载事件时用
                }, hash, false);
                events.push( desc );               //用于事件拷贝
                var count = events[ type+"_count" ] = ( events[ type+"_count" ] | 0 )+ 1;
                var hack = adapter[ desc.type ] || {};
                if(hack.add){
                    hack.add(desc)
                }
                if( count == 1 ){
                    var handle = data[type+"_handle"] = facade.curry( desc );     //  一个curry
                    if( !hack.setup || hack.setup( desc ) === false  ) {
                        if( bindTop && !bindTarget  ){//如果不能绑到当前对象上,尝试绑到window上
                            target = window;
                        }
                        //此元素在此事件类型只绑定一个回调
                        $.bind(target, desc.type, handle);
                    }
                }
            });
        },

        curry: function( hash ){// 这是元信息,不要污染这个对象
            var fn =  function( event){//真正的事件对象
                var type = hash.origType;//用户在调用API时绑定的事件
                var ctarget = hash.currentTarget//原来绑定事件的对象
                var more = event.more || {};
                //第一个分支防止在旧式IE下,fire click时二次触发 
                //第二个分支防止在chrome下,fire mouseover时,把用于冒充mouseenter用的mouseover也触发了
                if(facade.type == type || more.origType && more.origType !== type ){
                    return
                }
                var queue = ( $._data( ctarget, "events") || [] );
                //如果是自定义事件, 或者旧式IE678, 或者需要事件冒充
                if( !event.originalEvent || !bindTop || hash.type !== type ){
                    event = facade.fix( hash, event, type );
                }
                var args = [ event ].concat( event.args ||  [] ), result,lives = [],  handlers = []
                for ( var i = 0, desc; desc = queue[i++]; ) {
                    if(desc.live){
                        lives.push(desc)
                    }else{
                        handlers.push(desc)
                    }
                }
                //DOM树的每一个元素都有可以作为代理节点
                if ( lives.length && !(event.button && type === "click") ) {
                    for ( var k = 0, cur; (cur = lives[k++]) ; ) {
                        var cc = cur.currentTarget
                        var nodes = $(cc).find(cur.live);
                        for(var node = event.target; node != cc; node = node.parentNode || cc ){
                            if ( node.disabled !== true || type !== "click" ) {
                                if( nodes.index(node) !== -1){
                                    handlers.push({
                                        elem: node,
                                        fn:   cur.fn,
                                        origType: cur.origType,
                                        ns:   cur.ns
                                    });
                                }
                            }
                        }
                    }
                }

                for ( var i = 0, desc; desc = handlers[i++]; ) {
                    if ( ( event.type == desc.origType ) &&
                        (!event.rns || event.rns.test( desc.ns )) ) {
                        //谁绑定了事件,谁就是事件回调中的this
                        if(desc.preHandle && desc.preHandle(desc.elem || ctarget, event, desc) === false){
                            return
                        }
                        result = desc.fn.apply( desc.elem || ctarget, args);
                        if(desc.postHandle && desc.postHandle(desc.elem || ctarget, event, desc) === false){
                            return
                        }
                        desc.times--;
                        if(desc.times === 0){//如果有次数限制并到用光所有次数,则移除它
                            facade.unbind( this, desc)
                        }
                        if ( result !== void 0 ) {
                            event.result = result;
                            if ( result === false ) {
                                event.preventDefault();
                                event.stopPropagation();//这个参照jQuery的行为办事
                            }
                        }
                        if ( event.propagationStopped ) {
                            break;
                        }
                    }
                }

            }
            return fn;
        },
        dispatch: function( target, event, type ){// level2 API 用于旧式的$.event.fire中
            var handle = $._data(target, (type || event.type) +"_handle" );//取得此元素此类型的第一个quark
            handle && handle.call( target, event )
        },
        //将真事件对象的成员赋给伪事件对象,抹平浏览器差异
        fix: function( event, real, type){
            if( !event.originalEvent ){
                var hash = event, toString = hash.toString;//IE无法遍历出toString;
                event = $.Object.merge({}, hash);//这里event只是一个伪事件对象
                for( var p in real ){
                    if( !(p in hash) ){
                        event[p] = real[p]
                    }
                }
                for( var p in real.more ){
                    if( !(p in hash) ){
                        event[p] = real.more[p]
                    }
                }
                event.toString = toString;
                event.originalEvent = real;
                event.timeStamp = Date.now();
                //如果不存在target属性,为它添加一个
                if ( !event.target ) {
                    event.target = event.srcElement || document;
                }
                //safari的事件源对象可能为文本节点,应代入其父节点
                if ( event.target.nodeType === 3 ) {
                    event.target = event.target.parentNode;
                }
                event.metaKey = !!event.ctrlKey; // 处理IE678的组合键

                if( /^(?:mouse|contextmenu)|click/.test( type ) ){
                    if ( event.pageX == null && event.clientX != null ) {  // 处理鼠标事件
                        var doc = event.target.ownerDocument || document;
                        var box = document.compatMode == "BackCompat" ?  doc.body : doc.documentElement
                        event.pageX = event.clientX + (box && box.scrollLeft  || 0) - (box && box.clientLeft || 0);
                        event.pageY = event.clientY + (box && box.scrollTop   || 0) - (box && box.clientTop  || 0);
                    }
                    //如果不存在relatedTarget属性,为它添加一个
                    if ( !event.relatedTarget && event.fromElement ) {
                        event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
                    }
                    //标准浏览判定按下鼠标哪个键,左1中2右3
                    var button = event.button
                    //IE event.button的意义 0:没有键被按下 1:按下左键 2:按下右键 3:左键与右键同时被按下 4:按下中键 5:左键与中键同时被按下 6:中键与右键同时被按下 7:三个键同时被按下
                    if ( !event.which && isFinite(button) ) {
                        event.which  = [0,1,3,0,2,0,0,0][button];//0现在代表没有意义
                    }
                    if( type === "mousewheel" ){ //处理滚轮事件
                        if ("wheelDelta" in real){//统一为±120,其中正数表示为向上滚动,负数表示向下滚动
                            // http://www.w3help.org/zh-cn/causes/SD9015
                            var delta = real.wheelDelta
                            //opera 9x系列的滚动方向与IE保持一致,10后修正
                            if( window.opera && opera.version() < 10 )
                                delta = -delta;
                            event.wheelDelta = Math.round(delta); //修正safari的浮点 bug
                        }else if( "detail" in real ){
                            event.wheelDelta = -real.detail * 40;//修正FF的detail 为更大众化的wheelDelta
                        }
                    }
                }else if ( event.which == null ) {//处理键盘事件
                    event.which = event.charCode != null ? event.charCode : event.keyCode;
                }else if( window.Touch && event.touches && event.touches[0] ){
                    event.pageX = event.touches[0].pageX//处理触摸事件
                    event.pageY = event.touches[0].pageY
                }
            }
            if( type ){
                event.type = type
            }
            return event;
        },
        //外部的API已经确保typesr至少为空字符串
        unbind: function( target, hash ) {//事件系统三大核心方法之一,卸载事件
            var events = $._data( target, "events");
            if( !events ) return;
            var types = hash.type || "", live = hash.live, bindTarget = $["@bind"] in target;
            if( bindTarget ){ //处理DOM的hover事件
                types = types.replace( rhoverHack, "mouseover$1 mouseout$1" );
            }
            types.replace( $.rword, function( t ){
                var desc = new $.Event( t, live ), type = desc.origType, hack = adapter[ type ] || {};
                findHandlers( events, desc , hash.fn, live ).forEach( function(desc){
                    if( --events[type+"_count"] == 0 ){
                        if( !hack.teardown || hack.teardown( desc ) === false  ) {
                            if( bindTarget === false && bindTop ){//如果不能绑到当前对象上,尝试绑到window上
                                target = window;
                            }
                            var handle = $._data(target, type+"_handle");
                            $.unbind( target, desc.type, handle );
                        }
                        $.removeData( target, type +"_handle", true );
                        delete events[ type+"_count"];
                    }
                    events[ desc.index ] = null;
                })
            });
            for ( var i = events.length; i >= 0; i-- ) {
                if (events[i] == null){
                    events.splice(i, 1);
                }
            }
            if( !events.length ){
                $.removeData( target, "events") ;
            }
        }
    })
    var unbubbleEvents = $.oneObject("load unload focus blur mouseenter mouseleave",1)
    if( bindTop ){//事件系统三大核心方法之一,触发事件
        facade.fire = function( type ){
            var bindTarget = $["@bind"] in this, more, event
            var target = bindTarget ? this : window;
            if(typeof type == "string"){
                more = new Event( type );
            }else if(type && type.preventDefault){
                if(!( type instanceof $.Event) ){//如果是真的事件对象
                    more = new Event( type.type );
                    event = type;
                }else{
                    more = type;//如果是$.Event实例
                }
            }
            if( more ){
                type = more.type;
                var doc = target.ownerDocument || target.document || target || document;
                if(!event){
                    event = doc.createEvent(eventMap[type] || "CustomEvent");
                    event.initEvent( type,!unbubbleEvents[type],true, doc.defaultView, 1);//, doc.defaultView
                }
                event.args = [].slice.call( arguments, 1 ) ;
                event.more = more
                target.dispatchEvent(event);
                if(/^(focus|blur|select|reset)$/.test(type)){
                    target[type] && target[type]()
                }
            }else{
                throw "fire的第一个参数是必须是事件类或真伪事件对象 "
            }
        }
    }
    var rmapper = /(\w+)_(\w+)/g;
    //以下是用户使用的API
    $.implement({
        hover: function( fnIn, fnOut ) {
            return this.mouseenter( fnIn ).mouseleave( fnOut || fnIn );
        },
        delegate: function( selector, types, fn, times ) {
            return this.on( types, selector, fn, times);
        },
        live: function( types, fn, times ) {
            $( this.ownerDocument ).on( types, this.selector, fn, times );
            return this;
        },
        one: function( types, fn ) {
            return this.on( types, fn, 1 );
        },
        undelegate: function(selector, types, fn ) {/*顺序不能乱*/
            return arguments.length == 1 ? this.off( selector, "**" ) : this.off( types, fn, selector );
        },
        die: function( types, fn ) {
            $( this.ownerDocument ).off( types, fn, this.selector || "**", fn );
            return this;
        },
        fire: function() {
            var args = arguments;
            return this.each(function() {
                $.event.fire.apply(this, args );
            });
        }
    });

    //这个迭代器产生四个重要的事件绑定API on off bind unbind
    var rtypes = /^[a-z0-9_\-\.\s\,]+$/i
    "on_bind,off_unbind".replace( rmapper, function(_,method, mapper){
        $.fn[ method ] = function(types, selector, fn ){
            if ( typeof types === "object" ) {
                for ( var type in types ) {
                    $.fn[ method ](this, type, selector, types[ type ], fn );
                }
                return this;
            }
            var hash = {};
            for(var i = 0 ; i < arguments.length; i++ ){
                var el = arguments[i];
                if(typeof el == "number"){
                    hash.times = el;
                }else if(typeof el == "function"){
                    hash.fn = el
                }
                if(typeof el === "string"){
                    if(hash.type != null){
                        hash.live = el.trim();
                    }else{
                        hash.type = el.trim();//只能为字母数字-_.空格
                        if(!rtypes.test(hash.type)){
                            throw "事件类型格式不正确"
                        }
                    }
                }
            }
            if(!hash.type){
                throw "必须指明事件类型"
            }
            if(method === "on" && !hash.fn ){
                throw "必须指明事件回调"
            }
            hash.times = hash.times > 0  ? hash.times : Infinity;
            return this.each(function() {
                facade[ mapper ]( this, hash );
            });
        }
        $.fn[ mapper ] = function(){// $.fn.bind $.fn.unbind
            return $.fn[ method ].apply(this, arguments );
        }
    });
    var mouseEvents =  "contextmenu,click,dblclick,mouseout,mouseover,mouseenter,mouseleave,mousemove,mousedown,mouseup,mousewheel,"
    var eventMap = $.oneObject(mouseEvents, "MouseEvents");
    var types = mouseEvents +",keypress,keydown,keyup," + "blur,focus,focusin,focusout,"+
    "abort,error,load,unload,resize,scroll,change,input,select,reset,submit"//input
    types.replace( $.rword, function( type ){//这里产生以事件名命名的快捷方法
        eventMap[type] = eventMap[type] || (/key/.test(type) ? "KeyboardEvent" : "HTMLEvents")
        $.fn[ type ] = function( callback ){
            return callback?  this.bind( type, callback ) : this.fire( type );
        }
    });
    /**
mouseenter/mouseleave/focusin/focusout已为标准事件,经测试IE5+,opera11,FF10+都支持它们
详见http://www.filehippo.com/pl/download_opera/changelog/9476/
         */
    if( !+"\v1" || !$.eventSupport("mouseenter")){//IE6789不能实现捕获与safari chrome不支持
        "mouseenter_mouseover,mouseleave_mouseout".replace(rmapper, function(_, type, mapper){
            adapter[ type ]  = {
                add: function(desc){
                    desc.preHandle = function(target, event){
                        var related = event.relatedTarget;
                        return !related || (related !== target && !$.contains( target, related ))
                    }
                }
            };
            if(!$.eventSupport("mouseenter")){
                adapter[ type ].bindType =  adapter[ type ].delegateType = mapper
            }

        });
    }

    //现在只有firefox不支持focusin,focusout事件,并且它也不支持DOMFocusIn,DOMFocusOut,不能像DOMMouseScroll那样简单冒充
    if( !$.support.focusin ){
        "focusin_focus,focusout_blur".replace(rmapper, function(_,type, mapper){
            var notice = 0, handler = function (event) {
                var src = event.target;
                do{//模拟冒泡
                    if( $._data(src, "events") ) {
                        event.more = event.more ||{}
                        event.more.type = type
                        facade.dispatch( src, event, type );
                    }
                } while (src = src.parentNode );
            }
            adapter[ type ] = {
                setup: function( ) {
                    if ( notice++ === 0 ) {
                        document.addEventListener( mapper, handler, true );
                    }
                },
                teardown: function() {
                    if ( --notice === 0 ) {
                        document.removeEventListener( mapper, handler, true );
                    }
                }
            };
        });
    }
    try{
        //FF需要用DOMMouseScroll事件模拟mousewheel事件
        document.createEvent("MouseScrollEvents");
        adapter.mousewheel = {
            bindType    : "DOMMouseScroll",
            delegateType: "DOMMouseScroll"
        }
        if($.eventSupport("mousewheel")){
            delete adapter.mousewheel;
        }
    }catch(e){};
})

event_fix模块,重构更多!

//=========================================
//  事件补丁模块
//==========================================
define("event_fix", !!document.dispatchEvent, function(){
    $.log("已加载event_fix模块",7)
    var facade = $.event = {
        fire: function( init ){
            //这里的代码仅用于IE678
            var transfer;
            if( typeof init == "string"){
                transfer = new $.Event(init);
                init = false;
            }
            if( init && typeof init == "object"){
                if( init instanceof $.Event ){//如果是伪的
                    transfer = init;
                }else if( "cancelBubble" in init){
                    transfer = new $.Event(init.type);
                    transfer.originalEvent = init
                }
            }
            if(!transfer){
                throw "fire的第一个参数是必须是事件类或真伪事件对象"
            }
            transfer.target = this;
            transfer.args = [].slice.call(arguments,1) ;
            var type =  transfer.origType || transfer.type
            if( $["@bind"] in this ){
                var cur = this,  ontype = "on" + type;
                do{//模拟事件冒泡与执行内联事件
                    facade.dispatch( cur, transfer, type );
                    if (cur[ ontype ] && cur[ ontype ].call(cur) === false) {
                        transfer.preventDefault();
                    }
                    cur = cur.parentNode ||
                    cur.ownerDocument ||
                    cur === cur.ownerDocument && window;  //在opera 中节点与window都有document属性
                } while ( cur && !transfer.propagationStopped );

                if ( !transfer.defaultPrevented ) {//如果用户没有阻止普通行为,defaultPrevented
                    if( !(type === "click" && this.nodeName === "A") ) { //并且事件源不为window,并且是原生事件
                        if ( ontype && this[ type ] && ((type !== "focus" && type !== "blur") || this.offsetWidth !== 0) &&  !this.eval ) {
                            var inline = this[ ontype ];
                            //当我们直接调用元素的click,submit,reset,focus,blur
                            //会触发其默认行为与内联事件,但IE下会再次触发内联事件与多投事件
                            this[ ontype ] = null;
                            facade.type = type
                            if(type == "click" && /checkbox|radio/.test(this.type)){
                                this.checked = !this.checked
                            }
                            this[ type ]();
                            facade.type = void 0
                            this[ ontype ] = inline;
                        }
                    }

                }

            }else{//普通对象的自定义事件
                facade.dispatch(this, transfer);
            }
        }
    }
    //模拟IE678的reset,submit,change的事件代理
    var rform  = /^(?:textarea|input|select)$/i 
    function changeNotify( event ){
        if( event.type == "change" || event.propertyName == "checked" ){
            $.event.fire.call(this,"change")
        }
    }
    function delegate( fn ){
        return function( item ){//用于判定是否要使用代理
            return item.live  ? fn( item.currentTarget, item ) : false;
        }
    }
    var adapter = $.eventAdapter = {
        focus: {
            delegateType: "focusin"
        },
        blur: {
            delegateType: "focusout"
        },
        change: {//change事件的冒泡情况 IE6-9全灭
            //详见这里https://github.com/RubyLouvre/mass-Framework/issues/13
            setup: delegate(function( node, desc ){
                var subscriber = desc.subscriber || ( desc.subscriber = {}) //用于保存订阅者的UUID
                desc.__beforeactive__ = $.bind( node, "beforeactivate", function(event) {
                    var target = event.srcElement;
                    var tid = $.getUid( target )
                    //如果发现孩子是表单元素并且没有注册propertychange事件,则为其注册一个,那么它们在变化时就会发过来通知顶层元素
                    if ( rform.test( target.tagName) && !subscriber[ tid ] ) {
                        subscriber[ tid ] = target;//将select, checkbox, radio, text, textarea等表单元素注册其上
                        if(/checkbox|radio/.test(target.type)){
                            desc.__change__ = $.bind( target, "propertychange", changeNotify.bind(target, event) );
                        }else{
                            desc.__change__ = $.bind( target, "change", changeNotify.bind(target, event) );
                        }
                    }
                });//如果是事件绑定
            // node.fireEvent("onbeforeactivate")
            }),
            teardown: delegate(function( node, desc ){
                $.unbind( node, "beforeactive", desc.__beforeactive__ );
                var els = desc.subscriber ;
                for(var i in els){
                    $.unbind( els[i], "propertychange",  desc.__change__) ;
                    $.unbind( els[i], "change",  desc.__change__);
                }
            })
        }
    }
    //submit事件的冒泡情况----IE6-9 :form ;FF: document; chrome: window;safari:window;opera:window
    //同reset事件的冒泡情况----FF与opera能冒泡到document,其他浏览器只能到form
    "submit,reset".replace( $.rword, function( type ){
        adapter[ type ] = {
            setup: delegate(function( node ){
                $(node).bind( "click._"+type+" keypress._"+type, function( event ) {
                    var el = event.target;
                    if( el.form && (adapter[ type ].keyCode[ event.which ] || adapter[ type ].input[  el.type ] ) ){
                        $.event.fire.call(el, type)
                    }
                });
            }),
            keyCode: $.oneObject(type == "submit" ? "13,108" : "27"),
            input:  $.oneObject(type == "submit" ? "submit,image" : "reset"),
            teardown: delegate(function( node ){
                $( node ).unbind( "._"+type );
            })
        };
    });
})
原文地址:https://www.cnblogs.com/rubylouvre/p/2729309.html