zoukankan      html  css  js  c++  java
  • 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 );
                })
            };
        });
    })
    
  • 相关阅读:
    Struts2 采用Convention-plugin 实现零配置
    xml中批量新增数据或者批量修改数据
    Linux基础笔记_05
    Linux基础笔记_03
    Linux基础笔记_02
    Linux基础笔记_01
    玩转spring boot——websocket
    获取一天中的起始时间与结束时间
    《浅谈架构之路:前后端分离模式》
    区域语言与区域语言包对照关系表
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/2729309.html
Copyright © 2011-2022 走看看