mass Framework event模块 v4
event.js
//==========================================
// 事件模块(包括伪事件对象,事件绑定与事件代理)
//==========================================
$.define("event",document.dispatchEvent ? "node" : "node,event_fix",function(){
// $.log("已加载target模块")
var rhoverHack = /(?:^|\s)hover(\.\S+)?\b/, rmapper = /(\w+)_(\w+)/g,
rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/, revent = /(^|_|:)([a-z])/g
function addCallback(queue, obj){//添加回调包到列队中
var check = true, fn = obj.callback;
for ( var i = 0, el; el = queue[i++]; ) {
if( el.callback === fn ){
check = false;
break;
}
}
if( check ){
queue.push(obj);
}
}
function quickIs( elem, m ) {
var attrs = elem.attributes || {};
return (
(!m[1] || elem.nodeName.toLowerCase() === m[1]) &&
(!m[2] || (attrs.id || {}).value === m[2]) &&
(!m[3] || m[3].test( (attrs[ "class" ] || {}).value ))
);
}
//如果不存在添加一个
var facade = $.event = $.event || {};
//添加或增强二级属性eventAdapter
$.Object.merge(facade,{
eventAdapter:{
focus: {
delegateType: "focusin"
},
blur: {
delegateType: "focusout"
},
beforeunload: {
setup: function(src, _, _, fn ) {
// We only want to do this special case on windows
if ( $.type(src, "Window") ) {
src.onbeforeunload = fn;
}
},
teardown: function( src, _, _, fn ) {
if ( src.onbeforeunload === fn ) {
src.onbeforeunload = null;
}
}
}
}
});
var eventAdapter = $.event.eventAdapter;
$.mix(facade,{
bind : function( hash ){
//它将在原生事件派发器或任何能成为事件派发器的普通JS对象添加一个名叫uniqueNumber的属性,用于关联一个缓存体,
//把需要的数据储存到里面,而现在我们就把一个叫events的对象储放都它里面,
//而这个event的表将用来放置各种事件类型与对应的回调函数
if(arguments.length > 1 ){
throw "$.event bind method only need one argument, and it's a hash!"
}
var target = this, DOM = $[ "@target" ] in target, events = $._data( target),
types = hash.type, fn = hash.callback,selector = hash.selector, callback;
if(target.nodeType === 3 || target.nodeType === 8 || !events){
return
}
hash.uuid = $.getUid(fn); //确保UUID,bag与callback的UUID一致
if( DOM ){ //处理DOM事件
callback = events.callback || (events.callback = function( e ) {
return ((e || event).type !== facade.fireType ) ? facade.dispatch.apply( callback.target, arguments ) : void 0;
});
callback.target = target;
types = types.replace( rhoverHack, "mouseover$1 mouseout$1" )
}
events = events.events || (events.events = {});
//对多个事件进行绑定
types.replace( $.rword, function( old ){
var
tns = rtypenamespace.exec( old ) || [],//"focusin.aaa.bbb"
namespace = ( tns[2] || "" ).split( "." ).sort(),//取得命名空间 "aaa.bbb"
adapter = DOM && eventAdapter[ tns[1] ] || {},// focusin -> focus
type = (selector ? adapter.delegateType : adapter.bindType ) || tns[1],//focus
queue = events[ type ] = events[ type ] || [], //创建事件队列
item = $.mix({
type: type,
origType: tns[1],
namespace: namespace.join(".")
}, hash, false);
//只有原生事件发送器才能进行DOM level2 多投事件绑定
if( DOM && !queue.length ){
adapter = eventAdapter[ type ] || {};
if (!adapter.setup || adapter.setup( target, selector, item.origType, callback ) === false ) {
// 为此元素这种事件类型绑定一个全局的回调,用户的回调则在此回调中执行
$.bind(target, type, callback, !!selector)
}
}
addCallback( queue, item );//同一事件不能绑定重复回调
});
return this;
},
//外部的API已经确保typesr至少为空字符串
unbind: function( hash, mappedTypes ) {
var target = this, events = $._data( target, "events");
if(!events ) return;
var types = hash.type || "", selector = hash.selector, fn = hash.callback,
tns, type, origType, namespace, origCount, DOM = $["@target"] in target,
j, adapter, queue, item;
//将types进行映射并转换为数组
types = DOM ? types.replace( rhoverHack, "mouseover$1 mouseout$1" ) : types;
types = types.match( $.rword ) || [];
for (var t = 0; t < types.length; t++ ) {
//"aaa.bbb.ccc" -> ["aaa.bbb.ccc", "aaa", "bbb.ccc"]
tns = rtypenamespace.exec( types[t] ) || []
origType = type = tns[1];
namespace = tns[2];
// 如果types只包含命名空间,则去掉所有拥有此命名空间的事件类型的回调
if ( !type ) {
for ( j in events ) {
facade.unbind.call( target, {
type: j + types[t],//说明这个types[t]为命名空间
selector: selector,
callback: fn
}, true );
}
continue;
}
//如果使用事件冒充则找到其正确事件类型
adapter = eventAdapter[ type ] || {};
type = ( selector ? adapter.delegateType: adapter.bindType ) || type;
queue = events[ type ] || [];
origCount = queue.length;
namespace = namespace ? new RegExp("(^|\\.)" + namespace.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
// namespace = namespace? namespace.split( "." ).sort().join(".") : null;
//只有指定了命名空间,回调或选择器才能进入此分支
if ( fn || namespace || selector ) {
for ( j = 0; j < queue.length; j++ ) {
item = queue[ j ];
if ( ( mappedTypes || origType === item.origType ) &&
( !fn || fn.uuid === item.uuid ) &&//如果指定了回调,只检测其UUID
( !namespace || namespace.test( item.namespace ) ) &&//如果指定了命名空间
( !selector || selector === item.selector || selector === "**" && item.selector ) ) {
queue.splice( j--, 1 );
}
}
} else {
//移除此类事件的所有回调
queue.length = 0;
}
if ( DOM && (queue.length === 0 && origCount !== queue.length) ) {//如果在回调队列的长度发生变化时才进行此分支
if ( !adapter.teardown || adapter.teardown( target, selector, origType, fn ) === false ) {
$.unbind( target, type, $._data( target, "callback") );
}
delete events[ type ];
}
}
if( $.isEmptyObject( events ) ){
fn = $.removeData( target,"callback") ;
fn.target = null;
$.removeData( target, "events") ;
}
return this;
},
fire: function( event ){//event的类型可能是字符串,原生事件对象,伪事件对象
var target = this, namespace = [], type = event.type || event;
if(!isFinite(event.mass)){
event = new jEvent(event);
if( ~type.indexOf( "." ) ) {//处理命名空间
namespace = type.split(".");
type = namespace.shift();
namespace.sort();
event.namespace = namespace.join( "." );
event.namespace_re = event.namespace ? new RegExp("(^|\\.)" + namespace.join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
}
event.target = target;
}
var args = [ event ].concat( $.slice(arguments,1) );
if( $["@target"] in target){
var cur = target, ontype = "on" + type;
do{//模拟事件冒泡与执行内联事件
if( ($._data(cur,"events")|| {})[type] ){
facade.dispatch.apply( cur, args );
}
if (cur[ ontype ] && cur[ ontype ].call(cur) === false) {
event.preventDefault();
}
cur = cur.parentNode ||
cur.ownerDocument ||
cur === target.ownerDocument && window;
} while ( cur && !event.isPropagationStopped );
if ( !event.isDefaultPrevented ) {//模拟默认行为 click() submit() reset() focus() blur()
var old;//在opera 中节点与window都有document属性
if (ontype && target[ type ] && ((type !== "focus" && type !== "blur") || target.offsetWidth !== 0) && !target.eval) {
old = target[ ontype ];
if (old) { // 不用再触发内联事件
target[ ontype ] = null;
}
facade.fireType = type;
target[ type ]();
}
delete facade.fireType ;
if ( old ) {
target[ ontype ] = old;
}
}
}else{//普通对象的自定义事件
facade.dispatch.apply(target, args);
}
return this;
},
filter: function( cur, parent, expr ){
var matcher = typeof expr === "function"? expr : expr.input ? quickIs : $.match
for ( ; cur != parent; cur = cur.parentNode || parent ) {
if(matcher(cur, expr))
return true
}
return false;
},
dispatch: function( e ) {
var win = ( this.ownerDocument || this.document || this ).parentWindow || window,
event = facade.fix( e || win.event ),
queue = $._data(this,"events");//这个其实是对象events
if ( queue ) {
queue = queue[ event.type] || [];//到此处时才是数组
event.currentTarget = this;
var src = event.target,args = [event].concat($.slice(arguments,1)), result;
//复制数组以防影响下一次的操作
queue = queue.concat();
//开始进行拆包操作
for ( var i = 0, item; item = queue[i++]; ) {
//如果是事件代理,确保元素处于enabled状态,并且满足过滤条件
if ( !src.disabled && !(event.button && event.type === "click")
&& (!item.selector || facade.filter(src, this, item.selector))
&& (!event.namespace || event.namespace_re.test( item.namespace ) ) ) {
//取得回调函数
event.type = item.origType;
result = item.callback.apply( item.selector ? src : this, args );
item.times--;
if(item.times === 0){
facade.unbind.call( this, item)
}
if ( result !== void 0 ) {
event.result = result;
if ( result === false ) {
event.preventDefault();
event.stopPropagation();
}
}
if ( event.isImmediatePropagationStopped ) {
break;
}
}
}
}
return event.result;
},
_dispatch: function( src, type, e ){
e = facade.fix( e );
e.type = type;
for(var i in src){
if(src.hasOwnProperty(i)){
facade.dispatch.call( src[ i ], e );
}
}
},
fix: function( event ){
if( !isFinite(event.mass) ){
var originalEvent = event
event = new jEvent(originalEvent);
for( var prop in originalEvent ){
//去掉所有方法与常量
if( typeof originalEvent[prop] !== "function" && prop !== "type" ){
if(/^[A-Z_]+$/.test(prop))
continue
event[prop] = originalEvent[prop]
}
}
//如果不存在target属性,为它添加一个
if ( !event.target ) {
event.target = event.srcElement || document;
}
//safari的事件源对象可能为文本节点,应代入其父节点
if ( event.target.nodeType === 3 ) {
event.target = event.target.parentNode;
}
// 处理鼠标事件
if( /^(?:mouse|contextmenu)|click/.test(event.type) ){
//如果不存在pageX/Y则结合clientX/Y做一双出来
if ( event.pageX == null && event.clientX != null ) {
var doc = event.target.ownerDocument || document,
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);
}
//如果不存在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 ( event.which == null ) {//处理键盘事件
event.which = event.charCode != null ? event.charCode : event.keyCode;
}
if( window.Touch && event.touches && event.touches[0] ){
$.log("fix touch pageXY")
event.pageX = event.touches[0].pageX
event.pageY = event.touches[0].pageY
}
//处理滚轮事件
if( event.type === "mousewheel" ){
if ("wheelDelta" in originalEvent){
var delta = originalEvent.wheelDelta/120;
//opera 9x系列的滚动方向与IE保持一致,10后修正
if(top.opera && opera.version() < 10)
delta = -delta;
event.wheelDelta = Math.round(delta); //修正safari的浮点 bug
}else if("detail" in originalEvent){
event.wheelDelta = -event.detail/3;
}
}
// 处理组合键
if ( event.metaKey === void 0 ) {
event.metaKey = event.ctrlKey;
}
}
return event;
}
});
var jEvent = $.Event = function ( event ) {
this.originalEvent = event.type ? event: {};
this.type = (event.type || event).replace(/\..*/g,"");
this.timeStamp = Date.now();
this.mass = $.mass;//用于判定是否为伪事件对象
};
// 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;
}
};
var types = "contextmenu,click,dblclick,mouseout,mouseover,mouseenter,mouseleave,mousemove,mousedown,mouseup,mousewheel," +
"abort,error,load,unload,resize,scroll,change,input,select,reset,submit,input,"+"blur,focus,focusin,focusout,"+"keypress,keydown,keyup"
//事件派发器的接口
//实现了这些接口的对象将具有注册事件和广播事件的功能
//http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget
$.EventTarget = {
uniqueNumber : $.getUid({}),
defineEvents : function( names ){
var events = [];
if(typeof names == "string"){
events = names.match( $.rword ) || [];
}else if($.isArray(names)){
events = names;
}
events.forEach(function(name){
var method = 'on'+name.replace(revent,function($, $1, $2) {
return $2.toUpperCase();
});
if (!(method in this)) {
this[method] = function() {
return $.fn.on.apply(this, [].concat.apply([name], arguments));
};
}
},this);
}
};
"bind_on,unbind_off,fire_fire".replace( rmapper,function(_, type, mapper){
$.EventTarget[ type ] = function(){
$.fn[ mapper ].apply(this, arguments);
return this;
}
});
$.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;
};
/**
用于在标准浏览器下模拟mouseenter与mouseleave
现在除了IE系列支持mouseenter/mouseleave/focusin/focusout外
opera11,FF10也支持这四个事件,同时它们也成为w3c DOM3 Event的规范
详见http://www.filehippo.com/pl/download_opera/changelog/9476/
http://dev.w3.org/2006/webapi/DOM-Level-3-Events/html/DOM3-Events.html
*/
if( !+"\v1" || !$.eventSupport("mouseenter")){
"mouseenter_mouseover,mouseleave_mouseout".replace(rmapper, function(_, type, mapper){
eventAdapter[ type ] = {
setup: function( src ){//使用事件冒充
$._data( src, type+"_handle", $.bind( src, mapper, function( e ){
var parent = e.relatedTarget;
try {
while ( parent && parent !== src ) {
parent = parent.parentNode;
}
if ( parent !== src ) {
facade._dispatch( [ src ], type, e );
}
} catch(err) { };
}));
},
teardown: function(){
$.unbind( this, mapper, $._data( type+"_handle" ) );
}
};
});
}
//在标准浏览器里面模拟focusin
if( !$.eventSupport("focusin") ){
"focusin_focus,focusout_blur".replace(rmapper, function(_,type, mapper){
var notice = 0, focusinNotify = function (e) {
var src = e.target
do{//模拟冒泡
var events = $._data( src, "events" );
if(events && events[ type ]){
facade._dispatch( [ src ], type, e );
}
} while (src = src.parentNode );
}
eventAdapter[ type ] = {
setup: function( ) {
if ( notice++ === 0 ) {
document.addEventListener( mapper, focusinNotify, true );
}
},
teardown: function() {
if ( --notice === 0 ) {
document.removeEventListener( mapper, focusinNotify, true );
}
}
};
});
}
try{
//FF需要用DOMMouseScroll事件模拟mousewheel事件
document.createEvent("MouseScrollEvents");
eventAdapter.mousewheel = {
bindType : "DOMMouseScroll",
delegateType: "DOMMouseScroll"
}
try{
//可能末来FF会支持标准的mousewheel事件,则需要删除此分支
document.createEvent("WheelEvent");
delete eventAdapter.mousewheel;
}catch(e){};
}catch(e){};
//当一个元素,或者其内部任何一个元素获得焦点的时候会触发这个事件。
//这跟focus事件区别在于,他可以在父元素上检测子元素获取焦点的情况。
var rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/
function quickParse( selector ) {
var quick = rquickIs.exec( selector );
if ( quick ) {
// 0 1 2 3
// [ _, tag, id, class ]
quick[1] = ( quick[1] || "" ).toLowerCase();
quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" );
}
return quick;
}
"on_bind,off_unbind".replace( rmapper, function(_,method, mapper){
$.fn[ method ] = function(types, selector, fn ){//$.fn.on $.fn.off
if ( typeof types === "object" ) {
for ( var type in types ) {
$.fn[ method ].call(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.callback = el
}if(typeof el === "string"){
if(hash.type != null){
hash.selector = el.trim()
}else{
hash.type = el.trim()
}
}
}
if(method === "on"){
if( !hash.type || !hash.callback ){//必须指定事件类型与回调
return this;
}
hash.times = hash.times > 0 ? hash.times : Infinity;
hash.selector = hash.selector ? quickParse( hash.selector ) : false
}
if(this.mass && this.each){
return this.each(function() {
facade[ mapper ].call( this, hash );
});
}else{
return facade[ mapper ].call( this, hash );
}
}
$.fn[ mapper ] = function(){// $.fn.bind $.fn.unbind
return $.fn[ method ].apply(this, arguments );
}
});
$.implement({
toggle: function(/*fn1,fn2,fn3*/){
var fns = [].slice.call(arguments), i = 0;
return this.click(function(e){
var fn = fns[i++] || fns[i = 0, i++];
fn.call( this, e );
})
},
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;
if(this.mass && this.each){
return this.each(function() {
$.event.fire.apply(this, args );
});
}else{
return $.event.fire.apply(this, args );
}
}
});
types.replace( $.rword, function( type ){
$.fn[ type ] = function( callback ){
return callback? this.bind( type, callback ) : this.fire( type );
}
});
});
event_fix.js
//=========================================
// 事件补丁模块
//==========================================
$.define("event_fix", !!document.dispatchEvent, function(){
//模拟IE678的reset,submit,change的事件代理
var rform = /^(?:textarea|input|select)$/i ,
changeType = {
"select-one": "selectedIndex",
"select-multiple": "selectedIndex",
"radio": "checked",
"checkbox": "checked"
}
function changeNotify( e ){
if( e.propertyName === ( changeType[ this.type ] || "value") ){
$._data( this, "_just_changed", true );
$.event._dispatch( $._data( this, "publisher" ), "change", e );
}
}
function changeFire( e ){
if( !$._data( this,"_just_changed" ) ){
$.event._dispatch( $._data( this ,"publisher"), "change", e );
}else{
$.removeData( this, "_just_changed", true );
}
}
function delegate( fn ){
return function( src, selector, type ){
var adapter = $.event.eventAdapter,
fix = adapter[ type ] && adapter[ type ].check && adapter[ type ].check( src );
return (fix || selector) ? fn(src, type, fix) : false;
}
}
var facade = $.event = {
eventAdapter:{
//input事件的支持情况:IE9+,chrome+, gecko2+, opera10+,safari+
input: {
check: function(src){
return rform.test(src.tagName) && !/^select/.test(src.type);
},
bindType: "change",
delegateType: "change"
},
change: {//change事件的冒泡情况 IE6-9全灭
check: function(src){
return rform.test(src.tagName) && /radio|checkbox/.test(src.type)
},
setup: delegate(function( src, type, fix ){
var subscriber = $._data( src, "subscriber", {} );//用于保存订阅者的UUID
$._data( src, "_beforeactivate", $.bind( src, "beforeactivate", function() {
var e = src.document.parentWindow.event, target = e.srcElement, tid = $.getUid( target )
//如果发现孩子是表单元素并且没有注册propertychange事件,则为其注册一个,那么它们在变化时就会发过来通知顶层元素
if ( rform.test( target.tagName) && !subscriber[ tid ] ) {
subscriber[ tid] = target;//表明其已注册
var publisher = $._data( target,"publisher") || $._data( target,"publisher",{} );
publisher[ $.getUid(src) ] = src;//此孩子可能同时要向N个顶层元素报告变化
$.fn.on.call( target,"propertychange._change", changeNotify );
//允许change事件可以通过fireEvent("onchange")触发
if(type === "change"){
$._data(src, "_change_fire", $.bind(target, "change", changeFire.bind(target, e) ));
}
}
}));
if( fix ){//如果是事件绑定
src.fireEvent("onbeforeactivate")
}
}),
teardown: delegate(function( src, els, i ){
$.unbind( src, "beforeactive", $._data( src, "_beforeactivate") );
$.unbind( src, "change", $._data(src, "_change_fire") );
els = $.removeData( src, "subscriber", true ) || {};
for( i in els){
facade.unbind.call( els[i], "._change" );
var publisher = $._data( els[i], "publisher");
if(publisher){
delete publisher[ src.uniqueNumber ];
}
}
})
}
}
}
var adapter = facade.eventAdapter;
//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( src ){
$.fn.on.call( src, "click._"+type+" keypress._"+type, function( e ) {
var el = e.target;
if( el.form && (adapter[ type ].keyCode[ e.which] || adapter[ type ].kind[ el.type ] ) ){
facade._dispatch( [ src ], type, e );
}
});
}),
keyCode: $.oneObject(type == "submit" ? "13,108" : "27"),
kind: $.oneObject(type == "submit" ? "submit,image" : "reset"),
teardown: delegate(function( src ){
facade.unbind.call( src, "._"+type );
})
};
});
});
//2012.5.1 fix delegate BUG将submit与reset这两个适配器合而为一
机器瞎学/数据掩埋/模式混淆/人工智障/深度遗忘/神经掉线/计算机幻觉/专注单身二十五年
浙公网安备 33010602011771号