Groups
Groups
Sign in
Groups
Groups
鸿蒙的个人论坛
Conversations
About
Send feedback
Help
ExtJs源码分析:class Ext.EventManager
0 views
Skip to first unread message
鸿蒙
unread,
Dec 9, 2008, 3:05:37 AM
12/9/08
Reply to author
Sign in to reply to author
Forward
Sign in to forward
Delete
You do not have permission to delete messages in this group
Copy link
Report message
Show original message
Either email addresses are anonymous for this group or you need the view member email addresses permission to view the original message
to 鸿蒙.技术, 软思论坛
原文出处:
http://jljlpch.javaeye.com/blog/223726
/**
* author:prk
* date:2008-08-01
* comment:event analyse.
*
*/
/*
* Ext JS Library 2.0
* Copyright(c) 2006-2007, Ext JS, LLC.
*
lice...@extjs.com
*
*
http://extjs.com/license
*/
/**
* @class Ext.EventManager
* Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides
* several useful events directly.
* See {@link Ext.EventObject} for more details on normalized event objects.
* @singleton
*/
Ext.EventManager = function(){
var docReadyEvent, docReadyProcId, docReadyState =
false
;
var resizeEvent, resizeTask, textEvent, textSize;
var E = Ext.lib.Event;
var D = Ext.lib.Dom;
//docReady的监听函数
var fireDocReady = function(){
if
(!docReadyState){
//设定加载完成的标识
docReadyState =
true
;
Ext.isReady =
true
;
//对于Safari,成功刚应该clearInterval
if
(docReadyProcId){
clearInterval(docReadyProcId);
}
//现在到了remove DOMContentLoaded的事件监听了。
if
(Ext.isGecko || Ext.isOpera) {
document.removeEventListener(
"DOMContentLoaded"
, fireDocReady,
false
);
}
//Ie,则remove 生成的元素。
if
(Ext.isIE){
var defer = document.getElementById(
"ie-deferred-loader"
);
if
(defer){
defer.onreadystatechange =
null
;
defer.parentNode.removeChild(defer);
}
}
//执行docReadyEvent中监听之后除去Listeners
if
(docReadyEvent){
docReadyEvent.fire();
docReadyEvent.clearListeners();
}
}
};
//初始化docReady
var initDocReady = function(){
docReadyEvent =
new
Ext.util.Event();
//对于Opera、Gecko.为document的DOMContentLoaded事件注册fireDocReady监听。
if
(Ext.isGecko || Ext.isOpera) {
document.addEventListener(
"DOMContentLoaded"
, fireDocReady,
false
);
}
//对于IE,通过强行在document加入一个元素,判断这个元素是否加载完来判断文档是否加载完
//如果加载完了该元素的onreadystatechange的回调函数中的readyState == "complete"
else
if
(Ext.isIE){
document.write(
"<s"
+
'cript id="ie-deferred-loader" defer="defer" src="/'
+
'/:"></s'
+
"cript>"
);
var defer = document.getElementById(
"ie-deferred-loader"
);
defer.onreadystatechange = function(){
if
(
this
.readyState ==
"complete"
){
fireDocReady();
}
};
}
//对于Safari,每隔10ms监听它的document.readyState是否==="complete"
else
if
(Ext.isSafari){
docReadyProcId = setInterval(function(){
var rs = document.readyState;
if
(rs ==
"complete"
) {
fireDocReady();
}
},
10
);
}
//觉得这里没有必要采用Ext.lib.event.
// no matter what, make sure it fires on load
E.on(window,
"load"
, fireDocReady);
};
//隔多长时间执行一次h函数。
var createBuffered = function(h, o){
var task =
new
Ext.util.DelayedTask(h);
return
function(e){
// create new event object impl so new events don't wipe out properties
e =
new
Ext.EventObjectImpl(e);
task.delay(o.buffer, h,
null
, [e]);
};
};
//一个监听执行一次就被remove
var createSingle = function(h, el, ename, fn){
return
function(e){
Ext.EventManager.removeListener(el, ename, fn);
h(e);
};
};
//多长时间之后执行h函数
var createDelayed = function(h, o){
return
function(e){
// create new event object impl so new events don't wipe out properties
e =
new
Ext.EventObjectImpl(e);
setTimeout(function(){
h(e);
}, o.delay ||
10
);
};
};
//为element的ename事件添加fn的监听函数。
var listen = function(element, ename, opt, fn, scope){
var o = (!opt || typeof opt ==
"boolean"
) ? {} : opt;
fn = fn || o.fn; scope = scope || o.scope;
var el = Ext.getDom(element);
if
(!el){
throw
"Error listening for \""
+ ename +
'\". Element "'
+ element +
'" doesn\'t exist.'
;
}
//对传进来的fn进行包装
var h = function(e){
e = Ext.EventObject.setEvent(e);
var t;
//找到事件的元素或附近的元素,o.delegate是select
if
(o.delegate){
t = e.getTarget(o.delegate, el);
if
(!t){
return
;
}
}
else
{
t = e.target;
}
//对元素事件的三种操作,一:stop,二、preventDefault,三、stopPropagation
if
(o.stopEvent ===
true
){
e.stopEvent();
}
if
(o.preventDefault ===
true
){
e.preventDefault();
}
if
(o.stopPropagation ===
true
){
e.stopPropagation();
}
//是否采用原始的浏览事件e,而不是进行了包装的事件。
if
(o.normalized ===
false
){
e = e.browserEvent;
}
//fn(event,this,options)
fn.call(scope || el, e, t, o);
};
//推迟执行
if
(o.delay){
h = createDelayed(h, o);
}
//监听只执行一次
if
(o.single){
h = createSingle(h, el, ename, fn);
}
//隔多少时间就执行一次
if
(o.buffer){
h = createBuffered(h, o);
}
fn._handlers = fn._handlers || [];
fn._handlers.push([Ext.id(el), ename, h]);
//为el的ename事件注册h函数。
E.on(el, ename, h);
if
(ename ==
"mousewheel"
&& el.addEventListener){
// workaround for jQuery
el.addEventListener(
"DOMMouseScroll"
, h,
false
);
E.on(window,
'unload'
, function(){
el.removeEventListener(
"DOMMouseScroll"
, h,
false
);
});
}
//对document的mousedown,在stopEvent等中要先fire,之后才stop.
if
(ename ==
"mousedown"
&& el == document){
// fix stopped mousedowns on the document
Ext.EventManager.stoppedMouseDownEvent.addListener(h);
}
return
h;
};
var stopListening = function(el, ename, fn){
var id = Ext.id(el), hds = fn._handlers, hd = fn;
if
(hds){
for
(var i =
0
, len = hds.length; i < len; i++){
var h = hds[i];
if
(h[
0
] == id && h[
1
] == ename){
hd = h[
2
];
hds.splice(i,
1
);
break
;
}
}
}
E.un(el, ename, hd);
el = Ext.getDom(el);
if
(ename ==
"mousewheel"
&& el.addEventListener){
el.removeEventListener(
"DOMMouseScroll"
, hd,
false
);
}
if
(ename ==
"mousedown"
&& el == document){
// fix stopped mousedowns on the document
Ext.EventManager.stoppedMouseDownEvent.removeListener(hd);
}
};
var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
var pub = {
/**
* Appends an event handler to an element. The shorthand version {@link #on} is equivalent. Typically you will
* use {@link Ext.Element#addListener} directly on an Element in favor of calling this version.
* @param {String/HTMLElement} el The html element or id to assign the event handler to
* @param {String} eventName The type of event to listen for
* @param {Function} handler The handler function the event invokes
* @param {Object} scope (optional) The scope in which to execute the handler
* function (the handler function's "this" context)
* @param {Object} options (optional) An object containing handler configuration properties.
* This may contain any of the following properties:<ul>
* <li>scope {Object} : The scope in which to execute the handler function. The handler function's "this" context.</li>
* <li>delegate {String} : A simple selector to filter the target or look for a descendant of the target</li>
* <li>stopEvent {Boolean} : True to stop the event. That is stop propagation, and prevent the default action.</li>
* <li>preventDefault {Boolean} : True to prevent the default action</li>
* <li>stopPropagation {Boolean} : True to prevent event propagation</li>
* <li>normalized {Boolean} : False to pass a browser event to the handler function instead of an Ext.EventObject</li>
* <li>delay {Number} : The number of milliseconds to delay the invocation of the handler after te event fires.</li>
* <li>single {Boolean} : True to add a handler to handle just the next firing of the event, and then remove itself.</li>
* <li>buffer {Number} : Causes the handler to be scheduled to run in an {@link Ext.util.DelayedTask} delayed
* by the specified number of milliseconds. If the event fires again within that time, the original
* handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
* </ul><br>
* <p>See {@link Ext.Element#addListener} for examples of how to use these options.</p>
*/
//支持和observable中addListener一样的传参的形式
//这个函数和observable中addListener的结构差不多。不同的把具体的处理放到listen中。
addListener : function(element, eventName, fn, scope, options){
if
(typeof eventName ==
"object"
){
var o = eventName;
for
(var e in o){
if
(propRe.test(e)){
continue
;
}
if
(typeof o[e] ==
"function"
){
// shared options
listen(element, e, o, o[e], o.scope);
}
else
{
// individual options
listen(element, e, o[e]);
}
}
return
;
}
return
listen(element, eventName, options, fn, scope);
},
/**
* Removes an event handler from an element. The shorthand version {@link #un} is equivalent. Typically
* you will use {@link Ext.Element#removeListener} directly on an Element in favor of calling this version.
* @param {String/HTMLElement} el The id or html element from which to remove the event
* @param {String} eventName The type of event
* @param {Function} fn The handler function to remove
* @return {Boolean} True if a listener was actually removed, else false
*/
removeListener : function(element, eventName, fn){
return
stopListening(element, eventName, fn);
},
/**
* Fires when the document is ready (before onload and before images are loaded). Can be
* accessed shorthanded as Ext.onReady().
* @param {Function} fn The method the event invokes
* @param {Object} scope (optional) An object that becomes the scope of the handler
* @param {boolean} options (optional) An object containing standard {@link #addListener} options
*/
onDocumentReady : function(fn, scope, options){
//document已经load,为docReadyEvent 进行addListener、fire(),clearListeners();
if
(docReadyState){
// if it already fired
docReadyEvent.addListener(fn, scope, options);
docReadyEvent.fire();
docReadyEvent.clearListeners();
return
;
}
//初始化Document Ready.
if
(!docReadyEvent){
initDocReady();
}
//初始化之后再addListener
docReadyEvent.addListener(fn, scope, options);
},
/**
* Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
* @param {Function} fn The method the event invokes
* @param {Object} scope An object that becomes the scope of the handler
* @param {boolean} options
*/
onWindowResize : function(fn, scope, options){
if
(!resizeEvent){
resizeEvent =
new
Ext.util.Event();
resizeTask =
new
Ext.util.DelayedTask(function(){
resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
});
E.on(window,
"resize"
,
this
.fireWindowResize,
this
);
}
resizeEvent.addListener(fn, scope, options);
},
// exposed only to allow manual firing
fireWindowResize : function(){
if
(resizeEvent){
if
((Ext.isIE||Ext.isAir) && resizeTask){
resizeTask.delay(
50
);
}
else
{
resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
}
}
},
/**
* Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
* @param {Function} fn The method the event invokes
* @param {Object} scope An object that becomes the scope of the handler
* @param {boolean} options
*/
onTextResize : function(fn, scope, options){
if
(!textEvent){
textEvent =
new
Ext.util.Event();
var textEl =
new
Ext.Element(document.createElement(
'div'
));
textEl.dom.className =
'x-text-resize'
;
textEl.dom.innerHTML =
'X'
;
textEl.appendTo(document.body);
textSize = textEl.dom.offsetHeight;
setInterval(function(){
if
(textEl.dom.offsetHeight != textSize){
textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
}
},
this
.textResizeInterval);
}
textEvent.addListener(fn, scope, options);
},
/**
* Removes the passed window resize listener.
* @param {Function} fn The method the event invokes
* @param {Object} scope The scope of handler
*/
removeResizeListener : function(fn, scope){
if
(resizeEvent){
resizeEvent.removeListener(fn, scope);
}
},
// private
fireResize : function(){
if
(resizeEvent){
resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
}
},
/**
* Url used for onDocumentReady with using SSL (defaults to Ext.SSL_SECURE_URL)
*/
ieDeferSrc :
false
,
/**
* The frequency, in milliseconds, to check for text resize events (defaults to 50)
*/
textResizeInterval :
50
};
/**
* Appends an event handler to an element. Shorthand for {@link #addListener}.
* @param {String/HTMLElement} el The html element or id to assign the event handler to
* @param {String} eventName The type of event to listen for
* @param {Function} handler The handler function the event invokes
* @param {Object} scope (optional) The scope in which to execute the handler
* function (the handler function's "this" context)
* @param {Object} options (optional) An object containing standard {@link #addListener} options
* @member Ext.EventManager
* @method on
*/
pub.on = pub.addListener;
/**
* Removes an event handler from an element. Shorthand for {@link #removeListener}.
* @param {String/HTMLElement} el The id or html element from which to remove the event
* @param {String} eventName The type of event
* @param {Function} fn The handler function to remove
* @return {Boolean} True if a listener was actually removed, else false
* @member Ext.EventManager
* @method un
*/
pub.un = pub.removeListener;
pub.stoppedMouseDownEvent =
new
Ext.util.Event();
return
pub;
}();
/**
* Fires when the document is ready (before onload and before images are loaded). Shorthand of {@link Ext.EventManager#onDocumentReady}.
* @param {Function} fn The method the event invokes
* @param {Object} scope An object that becomes the scope of the handler
* @param {boolean} override If true, the obj passed in becomes
* the execution scope of the listener
* @member Ext
* @method onReady
*/
Ext.onReady = Ext.EventManager.onDocumentReady;
Ext.onReady(function(){
var bd = Ext.getBody();
if
(!bd){
return
; }
var cls = [
Ext.isIE ?
"ext-ie "
+ (Ext.isIE6 ?
'ext-ie6'
:
'ext-ie7'
)
: Ext.isGecko ?
"ext-gecko"
: Ext.isOpera ?
"ext-opera"
: Ext.isSafari ?
"ext-safari"
:
""
];
if
(Ext.isMac){
cls.push(
"ext-mac"
);
}
if
(Ext.isLinux){
cls.push(
"ext-linux"
);
}
if
(Ext.isBorderBox){
cls.push(
'ext-border-box'
);
}
if
(Ext.isStrict){
// add to the parent to allow for selectors like ".ext-strict .ext-ie"
var p = bd.dom.parentNode;
if
(p){
p.className +=
' ext-strict'
;
}
}
bd.addClass(cls.join(
' '
));
});
/**
* @class Ext.EventObject
* EventObject exposes the Yahoo! UI Event functionality directly on the object
* passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code
* Example:
* <pre><code>
function handleClick(e){ // e is not a standard event object, it is a Ext.EventObject
e.preventDefault();
var target = e.getTarget();
...
}
var myDiv = Ext.get("myDiv");
myDiv.on("click", handleClick);
//or
Ext.EventManager.on("myDiv", 'click', handleClick);
Ext.EventManager.addListener("myDiv", 'click', handleClick);
</code></pre>
* @singleton
*/
Ext.EventObject = function(){
var E = Ext.lib.Event;
// safari keypress events for special keys return bad keycodes
var safariKeys = {
63234
:
37
,
// left
63235
:
39
,
// right
63232
:
38
,
// up
63233
:
40
,
// down
63276
:
33
,
// page up
63277
:
34
,
// page down
63272
:
46
,
// delete
63273
:
36
,
// home
63275
:
35
// end
};
// normalize button clicks
//表示鼠标的左,中,右键,每种浏览器不一同,统一为0,1,2.
var btnMap = Ext.isIE ? {
1
:
0
,
4
:
1
,
2
:
2
} :
(Ext.isSafari ? {
1
:
0
,
2
:
1
,
3
:
2
} : {
0
:
0
,
1
:
1
,
2
:
2
});
Ext.EventObjectImpl = function(e){
if
(e){
this
.setEvent(e.browserEvent || e);
}
};
Ext.EventObjectImpl.prototype = {
/** The normal browser event */
browserEvent :
null
,
/** The button pressed in a mouse event */
button : -
1
,
/** True if the shift key was down during the event */
shiftKey :
false
,
/** True if the control key was down during the event */
ctrlKey :
false
,
/** True if the alt key was down during the event */
altKey :
false
,
//一些常用的按键。
/** Key constant @type Number */
BACKSPACE :
8
,
/** Key constant @type Number */
TAB :
9
,
/** Key constant @type Number */
RETURN :
13
,
/** Key constant @type Number */
ENTER :
13
,
/** Key constant @type Number */
SHIFT :
16
,
/** Key constant @type Number */
CONTROL :
17
,
/** Key constant @type Number */
ESC :
27
,
/** Key constant @type Number */
SPACE :
32
,
/** Key constant @type Number */
PAGEUP :
33
,
/** Key constant @type Number */
PAGEDOWN :
34
,
/** Key constant @type Number */
END :
35
,
/** Key constant @type Number */
HOME :
36
,
/** Key constant @type Number */
LEFT :
37
,
/** Key constant @type Number */
UP :
38
,
/** Key constant @type Number */
RIGHT :
39
,
/** Key constant @type Number */
DOWN :
40
,
/** Key constant @type Number */
DELETE :
46
,
/** Key constant @type Number */
F5 :
116
,
/** @private */
//包装事件。
setEvent : function(e){
if
(e ==
this
|| (e && e.browserEvent)){
// already wrapped
return
e;
}
//设定 this.browserEvent=传入的浏览器的event,做为是否包装的标识。
this
.browserEvent = e;
if
(e){
// normalize buttons,比prototype实现简单多了。表示鼠标按键。
this
.button = e.button ? btnMap[e.button] : (e.which ? e.which-
1
: -
1
);
if
(e.type ==
'click'
&&
this
.button == -
1
){
this
.button =
0
;
}
//事件名
this
.type = e.type;
//三个功能组合键
this
.shiftKey = e.shiftKey;
// mac metaKey behaves like ctrlKey
this
.ctrlKey = e.ctrlKey || e.metaKey;
this
.altKey = e.altKey;
//键盘事件的keywode,and charcode
// in getKey these will be normalized for the mac
this
.keyCode = e.keyCode;
this
.charCode = e.charCode;
//我倒是觉得这里的不好,不应该采用 Ext.lib.Event,组合在一起很方便啊
//可能是出于版本兼容等问题的考虑吧。
// cache the target for the delayed and or buffered events
this
.target = E.getTarget(e);
// same for XY
this
.xy = E.getXY(e);
}
else
{
this
.button = -
1
;
this
.shiftKey =
false
;
this
.ctrlKey =
false
;
this
.altKey =
false
;
this
.keyCode =
0
;
this
.charCode =
0
;
this
.target =
null
;
this
.xy = [
0
,
0
];
}
return
this
;
},
/**
* Stop the event (preventDefault and stopPropagation)
*/
//觉得stopEvent,preventDefault,stopPropagation完全可以独立实现。
stopEvent : function(){
if
(
this
.browserEvent){
if
(
this
.browserEvent.type ==
'mousedown'
){
Ext.EventManager.stoppedMouseDownEvent.fire(
this
);
}
E.stopEvent(
this
.browserEvent);
}
},
/**
* Prevents the browsers default handling of the event.
*/
// this.browserEven.returnValue = false;
preventDefault : function(){
if
(
this
.browserEvent){
E.preventDefault(
this
.browserEvent);
}
},
/** @private */
//判断键是不是字符按键,除去了一些功能键
isNavKeyPress : function(){
var k =
this
.keyCode;
k = Ext.isSafari ? (safariKeys[k] || k) : k;
return
(k >=
33
&& k <=
40
) || k ==
this
.RETURN || k ==
this
.TAB || k ==
this
.ESC;
},
//判断是不是特殊键。
isSpecialKey : function(){
var k =
this
.keyCode;
return
(
this
.type ==
'keypress'
&&
this
.ctrlKey) || k ==
9
|| k ==
13
|| k ==
40
|| k ==
27
||
(k ==
16
) || (k ==
17
) ||
(k >=
18
&& k <=
20
) ||
(k >=
33
&& k <=
35
) ||
(k >=
36
&& k <=
39
) ||
(k >=
44
&& k <=
45
);
},
/**
* Cancels bubbling of the event.
*/
//this.browserEvent.cancelBubble=false
stopPropagation : function(){
if
(
this
.browserEvent){
if
(
this
.browserEvent.type ==
'mousedown'
){
Ext.EventManager.stoppedMouseDownEvent.fire(
this
);
}
E.stopPropagation(
this
.browserEvent);
}
},
//取得键值和其代码
/**
* Gets the key code for the event.
* @return {Number}
*/
getCharCode : function(){
return
this
.charCode ||
this
.keyCode;
},
/**
* Returns a normalized keyCode for the event.
* @return {Number} The key code
*/
getKey : function(){
var k =
this
.keyCode ||
this
.charCode;
return
Ext.isSafari ? (safariKeys[k] || k) : k;
},
//取得事件页面的位置
/**
* Gets the x coordinate of the event.
* @return {Number}
*/
getPageX : function(){
return
this
.xy[
0
];
},
/**
* Gets the y coordinate of the event.
* @return {Number}
*/
getPageY : function(){
return
this
.xy[
1
];
},
/**
* Gets the time of the event.
* @return {Number}
*/
getTime : function(){
if
(
this
.browserEvent){
return
E.getTime(
this
.browserEvent);
}
return
null
;
},
/**
* Gets the page coordinates of the event.
* @return {Array} The xy values like [x, y]
*/
getXY : function(){
return
this
.xy;
},
/**
* Gets the target for the event.
* @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
* @param {Number/Mixed} maxDepth (optional) The max depth to
search as a number or element (defaults to 10 || document.body)
* @param {Boolean} returnEl (optional) True to return a Ext.Element object instead of DOM node
* @return {HTMLelement}
*/
//和prototype中findElement差不多,找到事件源元素附近的元素。
getTarget : function(selector, maxDepth, returnEl){
var t = Ext.get(
this
.target);
return
selector ? t.findParent(selector, maxDepth, returnEl) : (returnEl ? t :
this
.target);
},
/**
* Gets the related target.
* @return {HTMLElement}
*/
getRelatedTarget : function(){
if
(
this
.browserEvent){
return
E.getRelatedTarget(
this
.browserEvent);
}
return
null
;
},
/**
* Normalizes mouse wheel delta across browsers
* @return {Number} The delta
*/
getWheelDelta : function(){
var e =
this
.browserEvent;
var delta =
0
;
if
(e.wheelDelta){
/* IE/Opera. */
delta = e.wheelDelta/
120
;
}
else
if
(e.detail){
/* Mozilla case. */
delta = -e.detail/
3
;
}
return
delta;
},
/**
* Returns true if the control, meta, shift or alt key was pressed during this event.
* @return {Boolean}
*/
hasModifier : function(){
return
((
this
.ctrlKey ||
this
.altKey) ||
this
.shiftKey) ?
true
:
false
;
},
/**
* Returns true if the target of this event equals el or is a child of el
* @param {Mixed} el
* @param {Boolean} related (optional) true to test if the related target is within el instead of the target
* @return {Boolean}
*/
within : function(el, related){
var t =
this
[related ?
"getRelatedTarget"
:
"getTarget"
]();
return
t && Ext.fly(el).contains(t);
},
getPoint : function(){
return
new
Ext.lib.Point(
this
.xy[
0
],
this
.xy[
1
]);
}
};
return
new
Ext.EventObjectImpl();
}();
Reply all
Reply to author
Forward
0 new messages