Blame | Last modification | View Log | RSS feed
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)); };
}
}
}