1 if(!dojo._hasResource["dojo._base.event"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
2 dojo._hasResource["dojo._base.event"] = true;
3 dojo.provide("dojo._base.event");
4 dojo.require("dojo._base.connect");
6 // this file courtesy of the TurboAjax Group, licensed under a Dojo CLA
9 // DOM event listener machinery
10 var del = (dojo._event_listener = {
11 add: function(/*DOMNode*/node, /*String*/name, /*Function*/fp){
13 name = del._normalizeEventName(name);
14 fp = del._fixCallback(name, fp);
16 if(!dojo.isIE && (name == "mouseenter" || name == "mouseleave")){
19 name = (name == "mouseenter") ? "mouseover" : "mouseout";
22 if(!dojo.isDescendant(e.relatedTarget, node)){
23 // e.type = oname; // FIXME: doesn't take? SJM: event.type is generally immutable.
24 return ofp.call(this, e);
28 node.addEventListener(name, fp, false);
31 remove: function(/*DOMNode*/node, /*String*/event, /*Handle*/handle){
33 // clobbers the listener from the node
35 // DOM node to attach the event to
37 // the name of the handler to remove the function from
39 // the handle returned from add
41 node.removeEventListener(del._normalizeEventName(event), handle, false);
44 _normalizeEventName: function(/*String*/name){
45 // Generally, name should be lower case, unless it is special
46 // somehow (e.g. a Mozilla DOM event).
48 return name.slice(0,2) =="on" ? name.slice(2) : name;
50 _fixCallback: function(/*String*/name, fp){
51 // By default, we only invoke _fixEvent for 'keypress'
52 // If code is added to _fixEvent for other events, we have
53 // to revisit this optimization.
54 // This also applies to _fixEvent overrides for Safari and Opera
56 return name != "keypress" ? fp : function(e){ return fp.call(this, del._fixEvent(e, this)); };
58 _fixEvent: function(evt, sender){
59 // _fixCallback only attaches us to keypress.
60 // Switch on evt.type anyway because we might
61 // be called directly from dojo.fixEvent.
69 _setKeyChar: function(evt){
70 evt.keyChar = evt.charCode ? String.fromCharCode(evt.charCode) : '';
76 dojo.fixEvent = function(/*Event*/evt, /*DOMNode*/sender){
78 // normalizes properties on the event object including event
79 // bubbling methods, keystroke normalization, and x/y positions
81 // native event object
83 // node to treat as "currentTarget"
84 return del._fixEvent(evt, sender);
87 dojo.stopEvent = function(/*Event*/evt){
89 // prevents propagation and clobbers the default action of the
92 // The event object. If omitted, window.event is used on IE.
94 evt.stopPropagation();
95 // NOTE: below, this method is overridden for IE
98 // the default listener to use on dontFix nodes, overriden for IE
99 var node_listener = dojo._listener;
101 // Unify connect and event listeners
102 dojo._connect = function(obj, event, context, method, dontFix){
103 // FIXME: need a more strict test
104 var isNode = obj && (obj.nodeType||obj.attachEvent||obj.addEventListener);
105 // choose one of three listener options: raw (connect.js), DOM event on a Node, custom event on a Node
106 // we need the third option to provide leak prevention on broken browsers (IE)
107 var lid = !isNode ? 0 : (!dontFix ? 1 : 2), l = [dojo._listener, del, node_listener][lid];
109 var h = l.add(obj, event, dojo.hitch(context, method));
110 // formerly, the disconnect package contained "l" directly, but if client code
111 // leaks the disconnect package (by connecting it to a node), referencing "l"
112 // compounds the problem.
113 // instead we return a listener id, which requires custom _disconnect below.
114 // return disconnect package
115 return [ obj, event, h, lid ];
118 dojo._disconnect = function(obj, event, handle, listener){
119 ([dojo._listener, del, node_listener][listener]).remove(obj, event, handle);
124 // Public: client code should test
125 // keyCode against these named constants, as the
126 // actual codes can vary by browser.
128 // summary: definitions for common key values
164 NUMPAD_MULTIPLY: 106,
189 // IE event normalization
191 var _trySetKeyCode = function(e, code){
193 // squelch errors when keyCode is read-only
194 // (e.g. if keyCode is ctrl or shift)
195 return (e.keyCode = code);
201 // by default, use the standard listener
202 var iel = dojo._listener;
203 // dispatcher tracking property
204 if(!dojo.config._allow_leaks){
205 // custom listener that handles leak protection for DOM events
206 node_listener = iel = dojo._ie_listener = {
207 // support handler indirection: event handler functions are
208 // referenced here. Event dispatchers hold only indices.
210 // add a listener to an object
211 add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){
212 source = source || dojo.global;
213 var f = source[method];
214 if(!f||!f._listeners){
215 var d = dojo._getIeDispatcher();
216 // original target function is special
217 d.target = f && (ieh.push(f) - 1);
218 // dispatcher holds a list of indices into handlers table
220 // redirect source to dispatcher
221 f = source[method] = d;
223 return f._listeners.push(ieh.push(listener) - 1) ; /*Handle*/
225 // remove a listener from an object
226 remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){
227 var f = (source||dojo.global)[method], l = f && f._listeners;
228 if(f && l && handle--){
229 delete ieh[l[handle]];
235 var ieh = iel.handlers;
239 add: function(/*DOMNode*/node, /*String*/event, /*Function*/fp){
240 if(!node){return;} // undefined
241 event = del._normalizeEventName(event);
242 if(event=="onkeypress"){
243 // we need to listen to onkeydown to synthesize
244 // keypress events that otherwise won't fire
246 var kd = node.onkeydown;
247 if(!kd || !kd._listeners || !kd._stealthKeydownHandle){
248 var h = del.add(node, "onkeydown", del._stealthKeyDown);
250 kd._stealthKeydownHandle = h;
251 kd._stealthKeydownRefs = 1;
253 kd._stealthKeydownRefs++;
256 return iel.add(node, event, del._fixCallback(fp));
258 remove: function(/*DOMNode*/node, /*String*/event, /*Handle*/handle){
259 event = del._normalizeEventName(event);
260 iel.remove(node, event, handle);
261 if(event=="onkeypress"){
262 var kd = node.onkeydown;
263 if(--kd._stealthKeydownRefs <= 0){
264 iel.remove(node, "onkeydown", kd._stealthKeydownHandle);
265 delete kd._stealthKeydownHandle;
269 _normalizeEventName: function(/*String*/eventName){
270 // Generally, eventName should be lower case, unless it is
271 // special somehow (e.g. a Mozilla event)
273 return eventName.slice(0,2) != "on" ? "on" + eventName : eventName;
276 _fixEvent: function(/*Event*/evt, /*DOMNode*/sender){
278 // normalizes properties on the event object including event
279 // bubbling methods, keystroke normalization, and x/y positions
280 // evt: native event object
281 // sender: node to treat as "currentTarget"
283 var w = sender && (sender.ownerDocument || sender.document || sender).parentWindow || window;
286 if(!evt){return(evt);}
287 evt.target = evt.srcElement;
288 evt.currentTarget = (sender || evt.srcElement);
289 evt.layerX = evt.offsetX;
290 evt.layerY = evt.offsetY;
291 // FIXME: scroll position query is duped from dojo.html to
292 // avoid dependency on that entire module. Now that HTML is in
293 // Base, we should convert back to something similar there.
294 var se = evt.srcElement, doc = (se && se.ownerDocument) || document;
295 // DO NOT replace the following to use dojo.body(), in IE, document.documentElement should be used
296 // here rather than document.body
297 var docBody = ((dojo.isIE < 6) || (doc["compatMode"] == "BackCompat")) ? doc.body : doc.documentElement;
298 var offset = dojo._getIeDocumentElementOffset();
299 evt.pageX = evt.clientX + dojo._fixIeBiDiScrollLeft(docBody.scrollLeft || 0) - offset.x;
300 evt.pageY = evt.clientY + (docBody.scrollTop || 0) - offset.y;
301 if(evt.type == "mouseover"){
302 evt.relatedTarget = evt.fromElement;
304 if(evt.type == "mouseout"){
305 evt.relatedTarget = evt.toElement;
307 evt.stopPropagation = del._stopPropagation;
308 evt.preventDefault = del._preventDefault;
309 return del._fixKeys(evt);
311 _fixKeys: function(evt){
314 var c = ("charCode" in evt ? evt.charCode : evt.keyCode);
316 // CTRL-ENTER is CTRL-ASCII(10) on IE, but CTRL-ENTER on Mozilla
319 }else if(c==13||c==27){
320 c=0; // Mozilla considers ENTER and ESC non-printable
322 c=99; // Mozilla maps CTRL-BREAK to CTRL-c
324 // Mozilla sets keyCode to 0 when there is a charCode
325 // but that stops the event on IE.
327 del._setKeyChar(evt);
332 // some ctrl-key combinations (mostly w/punctuation) do not emit a char code in IE
333 // we map those virtual key codes to ascii here
334 // not valid for all (non-US) keyboards, so maybe we shouldn't bother
350 _stealthKeyDown: function(evt){
351 // IE doesn't fire keypress for most non-printable characters.
352 // other browsers do, we simulate it here.
353 var kp = evt.currentTarget.onkeypress;
354 // only works if kp exists and is a dispatcher
355 if(!kp || !kp._listeners){ return; }
356 // munge key/charCode
358 // These are Windows Virtual Key Codes
359 // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp
360 var unprintable = (k!=13)&&(k!=32)&&(k!=27)&&(k<48||k>90)&&(k<96||k>111)&&(k<186||k>192)&&(k<219||k>222);
361 // synthesize keypress for most unprintables and CTRL-keys
362 if(unprintable||evt.ctrlKey){
363 var c = unprintable ? 0 : k;
366 return; // IE will post CTRL-BREAK, CTRL-ENTER as keypress natively
367 }else if(c>95 && c<106){
368 c -= 48; // map CTRL-[numpad 0-9] to ASCII
369 }else if((!evt.shiftKey)&&(c>=65&&c<=90)){
370 c += 32; // map CTRL-[A-Z] to lowercase
372 c = del._punctMap[c] || c; // map other problematic CTRL combinations to ASCII
375 // simulate a keypress event
376 var faux = del._synthesizeEvent(evt, {type: 'keypress', faux: true, charCode: c});
377 kp.call(evt.currentTarget, faux);
378 evt.cancelBubble = faux.cancelBubble;
379 evt.returnValue = faux.returnValue;
380 _trySetKeyCode(evt, faux.keyCode);
383 // Called in Event scope
384 _stopPropagation: function(){
385 this.cancelBubble = true;
387 _preventDefault: function(){
388 // Setting keyCode to 0 is the only way to prevent certain keypresses (namely
389 // ctrl-combinations that correspond to menu accelerator keys).
390 // Otoh, it prevents upstream listeners from getting this information
391 // Try to split the difference here by clobbering keyCode only for ctrl
392 // combinations. If you still need to access the key upstream, bubbledKeyCode is
393 // provided as a workaround.
394 this.bubbledKeyCode = this.keyCode;
395 if(this.ctrlKey){_trySetKeyCode(this, 0);}
396 this.returnValue = false;
400 // override stopEvent for IE
401 dojo.stopEvent = function(evt){
402 evt = evt || window.event;
403 del._stopPropagation.call(evt);
404 del._preventDefault.call(evt);
408 del._synthesizeEvent = function(evt, props){
409 var faux = dojo.mixin({}, evt, props);
410 del._setKeyChar(faux);
411 // FIXME: would prefer to use dojo.hitch: dojo.hitch(evt, evt.preventDefault);
412 // but it throws an error when preventDefault is invoked on Safari
413 // does Event.preventDefault not support "apply" on Safari?
414 faux.preventDefault = function(){ evt.preventDefault(); };
415 faux.stopPropagation = function(){ evt.stopPropagation(); };
419 // Opera event normalization
422 _fixEvent: function(evt, sender){
427 c=99; // Mozilla maps CTRL-BREAK to CTRL-c
429 // can't trap some keys at all, like INSERT and DELETE
430 // there is no differentiating info between DELETE and ".", or INSERT and "-"
431 c = ((c<41)&&(!evt.shiftKey) ? 0 : c);
432 if((evt.ctrlKey)&&(!evt.shiftKey)&&(c>=65)&&(c<=90)){
433 // lowercase CTRL-[A-Z] keys
436 return del._synthesizeEvent(evt, { charCode: c });
443 // Safari event normalization
446 _fixEvent: function(evt, sender){
449 var c = evt.charCode, s = evt.shiftKey, k = evt.keyCode;
450 // FIXME: This is a hack, suggest we rethink keyboard strategy.
451 // Arrow and page keys have 0 "keyCode" in keypress events.on Safari for Windows
452 k = k || identifierMap[evt.keyIdentifier] || 0;
453 if(evt.keyIdentifier=="Enter"){
454 c = 0; // differentiate Enter from CTRL-m (both code 13)
455 }else if((evt.ctrlKey)&&(c>0)&&(c<27)){
456 c += 96; // map CTRL-[A-Z] codes to ASCII
457 } else if (c==dojo.keys.SHIFT_TAB) {
458 c = dojo.keys.TAB; // morph SHIFT_TAB into TAB + shiftKey: true
461 c = (c>=32 && c<63232 ? c : 0); // avoid generating keyChar for non-printables
463 return del._synthesizeEvent(evt, {charCode: c, shiftKey: s, keyCode: k});
469 dojo.mixin(dojo.keys, {
498 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 };
503 // keep this out of the closure
504 // closing over 'iel' or 'ieh' b0rks leak prevention
505 // ls[i] is an index into the master handler array
506 dojo._ieDispatcher = function(args, sender){
507 var ap=Array.prototype, h=dojo._ie_listener.handlers, c=args.callee, ls=c._listeners, t=h[c.target];
508 // return value comes from original target function
509 var r = t && t.apply(sender, args);
510 // invoke listeners after target function
513 h[ls[i]].apply(sender, args);
518 dojo._getIeDispatcher = function(){
519 // ensure the returned function closes over nothing
520 return new Function(dojo._scopeName + "._ieDispatcher(arguments, this)"); // function
522 // keep this out of the closure to reduce RAM allocation
523 dojo._event_listener._fixCallback = function(fp){
524 var f = dojo._event_listener._fixEvent;
525 return function(e){ return fp.call(this, f(e, this)); };