New file |
0,0 → 1,519 |
if(!dojo._hasResource["dojo._base.event"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
dojo._hasResource["dojo._base.event"] = true; |
dojo.provide("dojo._base.event"); |
dojo.require("dojo._base.connect"); |
|
// this file courtesy of the TurboAjax Group, licensed under a Dojo CLA |
|
(function(){ |
// DOM event listener machinery |
var del = dojo._event_listener = { |
add: function(/*DOMNode*/node, /*String*/name, /*Function*/fp){ |
if(!node){return;} |
name = del._normalizeEventName(name); |
|
fp = del._fixCallback(name, fp); |
|
var oname = name; |
if((!dojo.isIE)&&((name == "mouseenter")||(name == "mouseleave"))){ |
var oname = name; |
var ofp = fp; |
name = (name == "mouseenter") ? "mouseover" : "mouseout"; |
fp = function(e){ |
// thanks ben! |
var id = dojo.isDescendant(e.relatedTarget, node); |
if(id == false){ |
// e.type = oname; // FIXME: doesn't take? |
return ofp.call(this, e); |
} |
} |
} |
|
node.addEventListener(name, fp, false); |
return fp; /*Handle*/ |
}, |
remove: function(/*DOMNode*/node, /*String*/event, /*Handle*/handle){ |
// summary: |
// clobbers the listener from the node |
// node: |
// DOM node to attach the event to |
// event: |
// the name of the handler to remove the function from |
// handle: |
// the handle returned from add |
(node)&&(node.removeEventListener(del._normalizeEventName(event), handle, false)); |
}, |
_normalizeEventName: function(/*String*/name){ |
// Generally, name should be lower case, unless it is special |
// somehow (e.g. a Mozilla DOM event). |
// Remove 'on'. |
return (name.slice(0,2)=="on" ? name.slice(2) : name); |
}, |
_fixCallback: function(/*String*/name, fp){ |
// By default, we only invoke _fixEvent for 'keypress' |
// If code is added to _fixEvent for other events, we have |
// to revisit this optimization. |
// This also applies to _fixEvent overrides for Safari and Opera |
// below. |
return (name!="keypress" ? fp : function(e){ return fp.call(this, del._fixEvent(e, this)); }); |
}, |
_fixEvent: function(evt, sender){ |
// _fixCallback only attaches us to keypress. |
// Switch on evt.type anyway because we might |
// be called directly from dojo.fixEvent. |
switch(evt.type){ |
case "keypress": |
del._setKeyChar(evt); |
break; |
} |
return evt; |
}, |
_setKeyChar: function(evt){ |
evt.keyChar = (evt.charCode ? String.fromCharCode(evt.charCode) : ''); |
} |
}; |
|
// DOM events |
|
dojo.fixEvent = function(/*Event*/evt, /*DOMNode*/sender){ |
// summary: |
// normalizes properties on the event object including event |
// bubbling methods, keystroke normalization, and x/y positions |
// evt: Event |
// native event object |
// sender: DOMNode |
// node to treat as "currentTarget" |
return del._fixEvent(evt, sender); |
} |
|
dojo.stopEvent = function(/*Event*/evt){ |
// summary: |
// prevents propagation and clobbers the default action of the |
// passed event |
// evt: Event |
// The event object. If omitted, window.event is used on IE. |
evt.preventDefault(); |
evt.stopPropagation(); |
// NOTE: below, this method is overridden for IE |
} |
|
// the default listener to use on dontFix nodes, overriden for IE |
var node_listener = dojo._listener; |
|
// Unify connect and event listeners |
dojo._connect = function(obj, event, context, method, dontFix){ |
// FIXME: need a more strict test |
var isNode = obj && (obj.nodeType||obj.attachEvent||obj.addEventListener); |
// choose one of three listener options: raw (connect.js), DOM event on a Node, custom event on a Node |
// we need the third option to provide leak prevention on broken browsers (IE) |
var lid = !isNode ? 0 : (!dontFix ? 1 : 2), l = [dojo._listener, del, node_listener][lid]; |
// create a listener |
var h = l.add(obj, event, dojo.hitch(context, method)); |
// formerly, the disconnect package contained "l" directly, but if client code |
// leaks the disconnect package (by connecting it to a node), referencing "l" |
// compounds the problem. |
// instead we return a listener id, which requires custom _disconnect below. |
// return disconnect package |
return [ obj, event, h, lid ]; |
} |
|
dojo._disconnect = function(obj, event, handle, listener){ |
([dojo._listener, del, node_listener][listener]).remove(obj, event, handle); |
} |
|
// Constants |
|
// Public: client code should test |
// keyCode against these named constants, as the |
// actual codes can vary by browser. |
dojo.keys = { |
BACKSPACE: 8, |
TAB: 9, |
CLEAR: 12, |
ENTER: 13, |
SHIFT: 16, |
CTRL: 17, |
ALT: 18, |
PAUSE: 19, |
CAPS_LOCK: 20, |
ESCAPE: 27, |
SPACE: 32, |
PAGE_UP: 33, |
PAGE_DOWN: 34, |
END: 35, |
HOME: 36, |
LEFT_ARROW: 37, |
UP_ARROW: 38, |
RIGHT_ARROW: 39, |
DOWN_ARROW: 40, |
INSERT: 45, |
DELETE: 46, |
HELP: 47, |
LEFT_WINDOW: 91, |
RIGHT_WINDOW: 92, |
SELECT: 93, |
NUMPAD_0: 96, |
NUMPAD_1: 97, |
NUMPAD_2: 98, |
NUMPAD_3: 99, |
NUMPAD_4: 100, |
NUMPAD_5: 101, |
NUMPAD_6: 102, |
NUMPAD_7: 103, |
NUMPAD_8: 104, |
NUMPAD_9: 105, |
NUMPAD_MULTIPLY: 106, |
NUMPAD_PLUS: 107, |
NUMPAD_ENTER: 108, |
NUMPAD_MINUS: 109, |
NUMPAD_PERIOD: 110, |
NUMPAD_DIVIDE: 111, |
F1: 112, |
F2: 113, |
F3: 114, |
F4: 115, |
F5: 116, |
F6: 117, |
F7: 118, |
F8: 119, |
F9: 120, |
F10: 121, |
F11: 122, |
F12: 123, |
F13: 124, |
F14: 125, |
F15: 126, |
NUM_LOCK: 144, |
SCROLL_LOCK: 145 |
}; |
|
// IE event normalization |
if(dojo.isIE){ |
var _trySetKeyCode = function(e, code){ |
try{ |
// squelch errors when keyCode is read-only |
// (e.g. if keyCode is ctrl or shift) |
return (e.keyCode = code); |
}catch(e){ |
return 0; |
} |
} |
|
// by default, use the standard listener |
var iel = dojo._listener; |
// dispatcher tracking property |
if(!djConfig._allow_leaks){ |
// custom listener that handles leak protection for DOM events |
node_listener = iel = dojo._ie_listener = { |
// support handler indirection: event handler functions are |
// referenced here. Event dispatchers hold only indices. |
handlers: [], |
// add a listener to an object |
add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){ |
source = source || dojo.global; |
var f = source[method]; |
if(!f||!f._listeners){ |
var d = dojo._getIeDispatcher(); |
// original target function is special |
d.target = f && (ieh.push(f) - 1); |
// dispatcher holds a list of indices into handlers table |
d._listeners = []; |
// redirect source to dispatcher |
f = source[method] = d; |
} |
return f._listeners.push(ieh.push(listener) - 1) ; /*Handle*/ |
}, |
// remove a listener from an object |
remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){ |
var f = (source||dojo.global)[method], l = f&&f._listeners; |
if(f && l && handle--){ |
delete ieh[l[handle]]; |
delete l[handle]; |
} |
} |
}; |
// alias used above |
var ieh = iel.handlers; |
} |
|
dojo.mixin(del, { |
add: function(/*DOMNode*/node, /*String*/event, /*Function*/fp){ |
if(!node){return;} // undefined |
event = del._normalizeEventName(event); |
if(event=="onkeypress"){ |
// we need to listen to onkeydown to synthesize |
// keypress events that otherwise won't fire |
// on IE |
var kd = node.onkeydown; |
if(!kd||!kd._listeners||!kd._stealthKeydown){ |
// we simply ignore this connection when disconnecting |
// because it's side-effects are harmless |
del.add(node, "onkeydown", del._stealthKeyDown); |
// we only want one stealth listener per node |
node.onkeydown._stealthKeydown = true; |
} |
} |
return iel.add(node, event, del._fixCallback(fp)); |
}, |
remove: function(/*DOMNode*/node, /*String*/event, /*Handle*/handle){ |
iel.remove(node, del._normalizeEventName(event), handle); |
}, |
_normalizeEventName: function(/*String*/eventName){ |
// Generally, eventName should be lower case, unless it is |
// special somehow (e.g. a Mozilla event) |
// ensure 'on' |
return (eventName.slice(0,2)!="on" ? "on"+eventName : eventName); |
}, |
_nop: function(){}, |
_fixEvent: function(/*Event*/evt, /*DOMNode*/sender){ |
// summary: |
// normalizes properties on the event object including event |
// bubbling methods, keystroke normalization, and x/y positions |
// evt: native event object |
// sender: node to treat as "currentTarget" |
if(!evt){ |
var w = (sender)&&((sender.ownerDocument || sender.document || sender).parentWindow)||window; |
evt = w.event; |
} |
if(!evt){return(evt);} |
evt.target = evt.srcElement; |
evt.currentTarget = (sender || evt.srcElement); |
evt.layerX = evt.offsetX; |
evt.layerY = evt.offsetY; |
// FIXME: scroll position query is duped from dojo.html to |
// avoid dependency on that entire module. Now that HTML is in |
// Base, we should convert back to something similar there. |
var se = evt.srcElement, doc = (se && se.ownerDocument) || document; |
// DO NOT replace the following to use dojo.body(), in IE, document.documentElement should be used |
// here rather than document.body |
var docBody = ((dojo.isIE<6)||(doc["compatMode"]=="BackCompat")) ? doc.body : doc.documentElement; |
var offset = dojo._getIeDocumentElementOffset(); |
evt.pageX = evt.clientX + dojo._fixIeBiDiScrollLeft(docBody.scrollLeft || 0) - offset.x; |
evt.pageY = evt.clientY + (docBody.scrollTop || 0) - offset.y; |
if(evt.type == "mouseover"){ |
evt.relatedTarget = evt.fromElement; |
} |
if(evt.type == "mouseout"){ |
evt.relatedTarget = evt.toElement; |
} |
evt.stopPropagation = del._stopPropagation; |
evt.preventDefault = del._preventDefault; |
return del._fixKeys(evt); |
}, |
_fixKeys: function(evt){ |
switch(evt.type){ |
case "keypress": |
var c = ("charCode" in evt ? evt.charCode : evt.keyCode); |
if (c==10){ |
// CTRL-ENTER is CTRL-ASCII(10) on IE, but CTRL-ENTER on Mozilla |
c=0; |
evt.keyCode = 13; |
}else if(c==13||c==27){ |
c=0; // Mozilla considers ENTER and ESC non-printable |
}else if(c==3){ |
c=99; // Mozilla maps CTRL-BREAK to CTRL-c |
} |
// Mozilla sets keyCode to 0 when there is a charCode |
// but that stops the event on IE. |
evt.charCode = c; |
del._setKeyChar(evt); |
break; |
} |
return evt; |
}, |
// some ctrl-key combinations (mostly w/punctuation) do not emit a char code in IE |
// we map those virtual key codes to ascii here |
// not valid for all (non-US) keyboards, so maybe we shouldn't bother |
_punctMap: { |
106:42, |
111:47, |
186:59, |
187:43, |
188:44, |
189:45, |
190:46, |
191:47, |
192:96, |
219:91, |
220:92, |
221:93, |
222:39 |
}, |
_stealthKeyDown: function(evt){ |
// IE doesn't fire keypress for most non-printable characters. |
// other browsers do, we simulate it here. |
var kp=evt.currentTarget.onkeypress; |
// only works if kp exists and is a dispatcher |
if(!kp||!kp._listeners)return; |
// munge key/charCode |
var k=evt.keyCode; |
// These are Windows Virtual Key Codes |
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp |
var unprintable = (k!=13)&&(k!=32)&&(k!=27)&&(k<48||k>90)&&(k<96||k>111)&&(k<186||k>192)&&(k<219||k>222); |
// synthesize keypress for most unprintables and CTRL-keys |
if(unprintable||evt.ctrlKey){ |
var c = (unprintable ? 0 : k); |
if(evt.ctrlKey){ |
if(k==3 || k==13){ |
return; // IE will post CTRL-BREAK, CTRL-ENTER as keypress natively |
}else if(c>95 && c<106){ |
c -= 48; // map CTRL-[numpad 0-9] to ASCII |
}else if((!evt.shiftKey)&&(c>=65&&c<=90)){ |
c += 32; // map CTRL-[A-Z] to lowercase |
}else{ |
c = del._punctMap[c] || c; // map other problematic CTRL combinations to ASCII |
} |
} |
// simulate a keypress event |
var faux = del._synthesizeEvent(evt, {type: 'keypress', faux: true, charCode: c}); |
kp.call(evt.currentTarget, faux); |
evt.cancelBubble = faux.cancelBubble; |
evt.returnValue = faux.returnValue; |
_trySetKeyCode(evt, faux.keyCode); |
} |
}, |
// Called in Event scope |
_stopPropagation: function(){ |
this.cancelBubble = true; |
}, |
_preventDefault: function(){ |
// Setting keyCode to 0 is the only way to prevent certain keypresses (namely |
// ctrl-combinations that correspond to menu accelerator keys). |
// Otoh, it prevents upstream listeners from getting this information |
// Try to split the difference here by clobbering keyCode only for ctrl |
// combinations. If you still need to access the key upstream, bubbledKeyCode is |
// provided as a workaround. |
this.bubbledKeyCode = this.keyCode; |
if(this.ctrlKey){_trySetKeyCode(this, 0);} |
this.returnValue = false; |
} |
}); |
|
// override stopEvent for IE |
dojo.stopEvent = function(evt){ |
evt = evt || window.event; |
del._stopPropagation.call(evt); |
del._preventDefault.call(evt); |
} |
} |
|
del._synthesizeEvent = function(evt, props){ |
var faux = dojo.mixin({}, evt, props); |
del._setKeyChar(faux); |
// FIXME: would prefer to use dojo.hitch: dojo.hitch(evt, evt.preventDefault); |
// but it throws an error when preventDefault is invoked on Safari |
// does Event.preventDefault not support "apply" on Safari? |
faux.preventDefault = function(){ evt.preventDefault(); }; |
faux.stopPropagation = function(){ evt.stopPropagation(); }; |
return faux; |
} |
|
// Opera event normalization |
if(dojo.isOpera){ |
dojo.mixin(del, { |
_fixEvent: function(evt, sender){ |
switch(evt.type){ |
case "keypress": |
var c = evt.which; |
if(c==3){ |
c=99; // Mozilla maps CTRL-BREAK to CTRL-c |
} |
// can't trap some keys at all, like INSERT and DELETE |
// there is no differentiating info between DELETE and ".", or INSERT and "-" |
c = ((c<41)&&(!evt.shiftKey) ? 0 : c); |
if((evt.ctrlKey)&&(!evt.shiftKey)&&(c>=65)&&(c<=90)){ |
// lowercase CTRL-[A-Z] keys |
c += 32; |
} |
return del._synthesizeEvent(evt, { charCode: c }); |
} |
return evt; |
} |
}); |
} |
|
// Safari event normalization |
if(dojo.isSafari){ |
dojo.mixin(del, { |
_fixEvent: function(evt, sender){ |
switch(evt.type){ |
case "keypress": |
var c = evt.charCode, s = evt.shiftKey, k = evt.keyCode; |
// FIXME: This is a hack, suggest we rethink keyboard strategy. |
// Arrow and page keys have 0 "keyCode" in keypress events.on Safari for Windows |
k = k || identifierMap[evt.keyIdentifier] || 0; |
if(evt.keyIdentifier=="Enter"){ |
c = 0; // differentiate Enter from CTRL-m (both code 13) |
}else if((evt.ctrlKey)&&(c>0)&&(c<27)){ |
c += 96; // map CTRL-[A-Z] codes to ASCII |
} else if (c==dojo.keys.SHIFT_TAB) { |
c = dojo.keys.TAB; // morph SHIFT_TAB into TAB + shiftKey: true |
s = true; |
} else { |
c = (c>=32 && c<63232 ? c : 0); // avoid generating keyChar for non-printables |
} |
return del._synthesizeEvent(evt, {charCode: c, shiftKey: s, keyCode: k}); |
} |
return evt; |
} |
}); |
|
dojo.mixin(dojo.keys, { |
SHIFT_TAB: 25, |
UP_ARROW: 63232, |
DOWN_ARROW: 63233, |
LEFT_ARROW: 63234, |
RIGHT_ARROW: 63235, |
F1: 63236, |
F2: 63237, |
F3: 63238, |
F4: 63239, |
F5: 63240, |
F6: 63241, |
F7: 63242, |
F8: 63243, |
F9: 63244, |
F10: 63245, |
F11: 63246, |
F12: 63247, |
PAUSE: 63250, |
DELETE: 63272, |
HOME: 63273, |
END: 63275, |
PAGE_UP: 63276, |
PAGE_DOWN: 63277, |
INSERT: 63302, |
PRINT_SCREEN: 63248, |
SCROLL_LOCK: 63249, |
NUM_LOCK: 63289 |
}); |
var dk = dojo.keys, identifierMap = { "Up": dk.UP_ARROW, "Down": dk.DOWN_ARROW, "Left": dk.LEFT_ARROW, "Right": dk.RIGHT_ARROW, "PageUp": dk.PAGE_UP, "PageDown": dk.PAGE_DOWN }; |
} |
})(); |
|
if(dojo.isIE){ |
// keep this out of the closure |
// closing over 'iel' or 'ieh' b0rks leak prevention |
// ls[i] is an index into the master handler array |
dojo._getIeDispatcher = function(){ |
return function(){ |
var ap=Array.prototype, h=dojo._ie_listener.handlers, c=arguments.callee, ls=c._listeners, t=h[c.target]; |
// return value comes from original target function |
var r = t && t.apply(this, arguments); |
// invoke listeners after target function |
for(var i in ls){ |
if(!(i in ap)){ |
h[ls[i]].apply(this, arguments); |
} |
} |
return r; |
} |
} |
// keep this out of the closure to reduce RAM allocation |
dojo._event_listener._fixCallback = function(fp){ |
var f = dojo._event_listener._fixEvent; |
return function(e){ return fp.call(this, f(e, this)); }; |
} |
} |
|
} |