zoukankan      html  css  js  c++  java
  • mass Framework emitter模块

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

    //==================================================
    // 事件发送器模块
    //==================================================
    (function(global,DOC){
        var dom = global[DOC.URL.replace(/(#.+|\W)/g,'')];
        dom.define("emitter","data", function(){
            var fireType = "", blank = ""
            var ensure = function(array,neo){
                var obj = {}
                for(var i=0,el;el = array[i++];){
                    obj[ el.uuid ] =1
                }
                if(!obj[neo.uuid]){
                    array.push(neo)
                };
            }
            var events = dom.events = {
                special:{},//用于处理个别的DOM事件
                bind : function(type, callback, phase){
                    //它将在原生事件发送器或任何能成为事件发送器的普通JS对象添加一个名叫uniqueID的属性,用于关联一个缓存体,
                    //把需要的数据储存到里面,而现在我们就把一个叫@events的对象储放都它里面,
                    //而这个@event的表将用来放置各种事件类型与对应的回调函数
                    var target = this, table = dom.data( target,"@events") || dom.data( target,"@events",{});
                    if(!table || !callback) return ;
                    var bag = callback.callback ? callback :{
                        callback:callback,
                        uuid: dom.uuid++,
                        type:type
                    }
                    //确保UUID,bag与callback的UUID一致
                    bag.callback.uuid = bag.uuid;
                    //原生的DOM事件是不允许绑定同一个回调函数,详见下面的测试
                    //  function callback(){  alert(1)  }
                    //  document.addEventListener("click",callback,false)
                    //  document.addEventListener("click",callback,false)
                    type = bag.type;
                    var queue = table[ type ] = table[ type ] ||  [];
                    ensure(queue,bag);
                    if(dom["@emitter"] in target){//如果是原生的事件发送体
                        var special = events.special[ bag.type ] , setup = events.setup, tag = "@"+type
                        if(special){
                            type = special.type || type
                            tag = (bag.live ? "@live_" :"@special_" )+type;
                            setup = bag.live ? special.liveSetup : special.setup
                        }
                        bag.tag = tag;
                        if(!table[tag]){
                            dom.log("setup "+type+" event...")
                            setup(target, type, events.handle, !!phase);
                            table[tag] = 1
                        }
                    }
                },
      
                unbind:function(type ,bag, phase){
                    var target = this, table = dom.data( target,"@events") ;
                    if(!table) return;
                    if(typeof type === "string"){//如果指定了要移除何种事件类型
                        type = bag && bag.type || type;
                        var queue = table[ type ];
                        if(queue){
                            var callback = bag.callback || bag;
                            queue =  callback ? table[type].filter(function(bag) {
                                return  bag.callback != callback;
                            }) : [];
                            if(dom["@emitter"] in target){//如果是原生的事件发送体
                                var special = events.special[ type ] ,  teardown = events.teardown, tag = "@"+type
                                if(special){
                                    type = special.type || type
                                    tag = (bag.live ? "@live_" :"@special_" )+type;
                                    teardown = bag.live ? special.liveTeardown : special.teardown
                                }   
                                var length =  queue.filter(function(bag){
                                    return bag.tag == tag
                                }).length;
                                if(!length){
                                    teardown(target, type, events.handle, !!phase) ;
                                    dom.log("teardown "+type+" event...")
                                    delete table[tag]
                                }
                            }
                        }
                    }else{
                        for (type in table ) {
                            if(type.charAt(0) !== "@")
                                events.unbind( target, type );
                        }
                    }
                },
                fire:function(type){
                    var target = this, table = dom.data( target,"@events") ,args = dom.slice(arguments,1), event
                    if(!table) return;
                    event = type instanceof jEvent ? type : new jEvent(type);
                    event.target = target;
                    event.fireArgs = args;
                    if( dom["@emitter"] in target){
                        var cur = target,  ontype = "on" + type;
                        do{//模拟事件冒泡与执行内联事件
                            events.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;
                            try {
                                if (ontype && target[ type ]) {
                                    // 不用再触发内事件
                                    old = target[ ontype ];
                                    if (old) {
                                        target[ ontype ] = null;
                                    }
                                    fireType = type;
                                    target[ type ]();
                                }
                            } catch (e) {
                                dom.log("dom.events.fire("+type+") throw errer " + e);
                            }
                            if (old) {
                                target[ ontype ] = old;
                            }
                            fireType = blank;
                        }
      
                    }else{//普通对象的自定义事件
                        events.handle.call(target, event);
                    }
                },
                filter:function(target, parent, expr){
                    if(dom.contains(parent,target) ){
                        if(typeof expr === "function"  ){
                            return expr.call(target)
                        }else{
                            return dom.matchesSelector(target, expr) ;//需要travel模块
                        }
                    }
                },
                handle: function( event ) {
                    if(fireType === event.type)
                        return undefined;
                    var queue = dom.data(this,"@events")[event.type];
                    if (  queue ) {
                        if(!event.uuid){
                            event = events.fix(event);
                        }
                        event.currentTarget = this;
                        var emitter = event.target, result,
                        //取得参数(只有自定义才有多个参数)
                        args = "fireArgs" in event ? [event].concat(event.fireArgs) : arguments;
                        //复制数组以防影响下一次的操作
                        queue = queue.concat();
                        //开始进行拆包操作
                        for ( var i = 0, bag; bag = queue[i++]; ) {
                            //如果是事件代理,确保元素处于enabled状态,并且满足过滤条件
                            if(bag.live && emitter.disabled && !events.filter(emitter, this, bag.live) ){
                                continue;
                            }
                            //取得回调函数
                            result = bag.callback.apply( emitter, 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(event.uuid){
                        return event;
                    }
                    var originalEvent = event
                    event = new jEvent(originalEvent);
                    for(var prop in originalEvent){
                        if(typeof originalEvent[prop] !== "function"){
                            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;
                    }
                    //如果事件源对象为文本节点,则置入其父元素
                    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 html = dom.HTML, 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:DOC.dispatchEvent ? function(target, type, fn,phase){
                    target.removeEventListener( type, fn,phase );
                } : function(target, type, fn)  {
                    target.detachEvent( "on" + type, fn );
                }
            }
            function jEvent( event ) {
                this.originalEvent = event.substr ? {} : event
                this.type = event.type || event
                this.timeStamp = Date.now();
                this.uuid = dom.uuid++;
            };
            // 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(){
                    events[name].apply(this, arguments);
                    return this;
                }
            });
            dom.emitter.uniqueID = ++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, Array.prototype.concat.apply([name],arguments));
                        };
                    }
                },this);
            }
        });
    })(this,this.document);
    //2011.8.14
    //更改隐藏namespace,让自定义对象的回调函数也有事件对象
    //2011.8.17
    //事件发送器增加一个uniqueID属性
    //2011.8.21
    //重构bind与unbind方法
    

    示例:

           dom.require("ready,class,emitter",function(){
            var A = dom.factory({
              include:dom.emitter,
              init:function(){
                this.defineEvents("click,mouseover")
              }
            });
    
            var a = new A;
            a.bind("click",function(){
              alert([].slice.call(arguments))//[object Event],1,2,3
            })
            a.fire("click",1,2,3);
            a.onMouseover(function(){
              alert("司徒正美")
            });
            a.fire("mouseover")
            dom.log(a)
          })
    

    依次弹出两个窗口,第一个为"1,2,3",第二个为"司徒正美".在firebug下查看a实例的构造如下:

    第二个例子,只依赖emitter模块.

          dom.require("emitter",function(){
            var a  = {};
            dom.mix(a,dom.emitter);
            a.bind("data",function(){
              alert("11111111")
            });
            a.bind("data",function(e){
              alert([].slice.call(arguments));//[object Event],3,5
              alert(e.type) //data
            });
            a.fire("data",3,5)
     
          });
    
    
  • 相关阅读:
    C++标准转换运算符(2)
    C++标准转换运算符(1)
    未能加载视图状态。正在向其中加载视图状态的控件树必须与前一请求期间用于……
    我的第一篇博客
    C语言C语言程序
    C语言基本运算符
    C语言流程控制
    C语言关键字、标识(zhi)符、注释
    msado15.dll版本引发的离奇故障
    mySQL错误: The used table type doesn't support FULLTEXT indexes
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/2104756.html
Copyright © 2011-2022 走看看