]> git.pond.sub.org Git - eow/blob - static/dojo-release-1.1.1/dojo/_base/event.js
Comment class stub
[eow] / static / dojo-release-1.1.1 / dojo / _base / event.js
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");
5
6 // this file courtesy of the TurboAjax Group, licensed under a Dojo CLA
7
8 (function(){
9         // DOM event listener machinery
10         var del = (dojo._event_listener = {
11                 add: function(/*DOMNode*/node, /*String*/name, /*Function*/fp){
12                         if(!node){return;} 
13                         name = del._normalizeEventName(name);
14                         fp = del._fixCallback(name, fp);
15                         var oname = name;
16                         if(!dojo.isIE && (name == "mouseenter" || name == "mouseleave")){
17                                 var ofp = fp;
18                                 //oname = name;
19                                 name = (name == "mouseenter") ? "mouseover" : "mouseout";
20                                 fp = function(e){
21                                         // thanks ben!
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); 
25                                         }
26                                 }
27                         }
28                         node.addEventListener(name, fp, false);
29                         return fp; /*Handle*/
30                 },
31                 remove: function(/*DOMNode*/node, /*String*/event, /*Handle*/handle){
32                         // summary:
33                         //              clobbers the listener from the node
34                         // node:
35                         //              DOM node to attach the event to
36                         // event:
37                         //              the name of the handler to remove the function from
38                         // handle:
39                         //              the handle returned from add
40                         if (node){
41                                 node.removeEventListener(del._normalizeEventName(event), handle, false);
42                         }
43                 },
44                 _normalizeEventName: function(/*String*/name){
45                         // Generally, name should be lower case, unless it is special
46                         // somehow (e.g. a Mozilla DOM event).
47                         // Remove 'on'.
48                         return name.slice(0,2) =="on" ? name.slice(2) : name;
49                 },
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
55                         // below.
56                         return name != "keypress" ? fp : function(e){ return fp.call(this, del._fixEvent(e, this)); };
57                 },
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.
62                         switch(evt.type){
63                                 case "keypress":
64                                         del._setKeyChar(evt);
65                                         break;
66                         }
67                         return evt;
68                 },
69                 _setKeyChar: function(evt){
70                         evt.keyChar = evt.charCode ? String.fromCharCode(evt.charCode) : '';
71                 }
72         });
73
74         // DOM events
75         
76         dojo.fixEvent = function(/*Event*/evt, /*DOMNode*/sender){
77                 // summary:
78                 //              normalizes properties on the event object including event
79                 //              bubbling methods, keystroke normalization, and x/y positions
80                 // evt: Event
81                 //              native event object
82                 // sender: DOMNode
83                 //              node to treat as "currentTarget"
84                 return del._fixEvent(evt, sender);
85         }
86
87         dojo.stopEvent = function(/*Event*/evt){
88                 // summary:
89                 //              prevents propagation and clobbers the default action of the
90                 //              passed event
91                 // evt: Event
92                 //              The event object. If omitted, window.event is used on IE.
93                 evt.preventDefault();
94                 evt.stopPropagation();
95                 // NOTE: below, this method is overridden for IE
96         }
97
98         // the default listener to use on dontFix nodes, overriden for IE
99         var node_listener = dojo._listener;
100         
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];
108                 // create a listener
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 ];
116         }
117
118         dojo._disconnect = function(obj, event, handle, listener){
119                 ([dojo._listener, del, node_listener][listener]).remove(obj, event, handle);
120         }
121
122         // Constants
123
124         // Public: client code should test
125         // keyCode against these named constants, as the
126         // actual codes can vary by browser.
127         dojo.keys = {
128                 // summary: definitions for common key values
129                 BACKSPACE: 8,
130                 TAB: 9,
131                 CLEAR: 12,
132                 ENTER: 13,
133                 SHIFT: 16,
134                 CTRL: 17,
135                 ALT: 18,
136                 PAUSE: 19,
137                 CAPS_LOCK: 20,
138                 ESCAPE: 27,
139                 SPACE: 32,
140                 PAGE_UP: 33,
141                 PAGE_DOWN: 34,
142                 END: 35,
143                 HOME: 36,
144                 LEFT_ARROW: 37,
145                 UP_ARROW: 38,
146                 RIGHT_ARROW: 39,
147                 DOWN_ARROW: 40,
148                 INSERT: 45,
149                 DELETE: 46,
150                 HELP: 47,
151                 LEFT_WINDOW: 91,
152                 RIGHT_WINDOW: 92,
153                 SELECT: 93,
154                 NUMPAD_0: 96,
155                 NUMPAD_1: 97,
156                 NUMPAD_2: 98,
157                 NUMPAD_3: 99,
158                 NUMPAD_4: 100,
159                 NUMPAD_5: 101,
160                 NUMPAD_6: 102,
161                 NUMPAD_7: 103,
162                 NUMPAD_8: 104,
163                 NUMPAD_9: 105,
164                 NUMPAD_MULTIPLY: 106,
165                 NUMPAD_PLUS: 107,
166                 NUMPAD_ENTER: 108,
167                 NUMPAD_MINUS: 109,
168                 NUMPAD_PERIOD: 110,
169                 NUMPAD_DIVIDE: 111,
170                 F1: 112,
171                 F2: 113,
172                 F3: 114,
173                 F4: 115,
174                 F5: 116,
175                 F6: 117,
176                 F7: 118,
177                 F8: 119,
178                 F9: 120,
179                 F10: 121,
180                 F11: 122,
181                 F12: 123,
182                 F13: 124,
183                 F14: 125,
184                 F15: 126,
185                 NUM_LOCK: 144,
186                 SCROLL_LOCK: 145
187         };
188         
189         // IE event normalization
190         if(dojo.isIE){ 
191                 var _trySetKeyCode = function(e, code){
192                         try{
193                                 // squelch errors when keyCode is read-only
194                                 // (e.g. if keyCode is ctrl or shift)
195                                 return (e.keyCode = code);
196                         }catch(e){
197                                 return 0;
198                         }
199                 }
200
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.
209                                 handlers: [],
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
219                                                 d._listeners = [];
220                                                 // redirect source to dispatcher
221                                                 f = source[method] = d;
222                                         }
223                                         return f._listeners.push(ieh.push(listener) - 1) ; /*Handle*/
224                                 },
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]];
230                                                 delete l[handle];
231                                         }
232                                 }
233                         };
234                         // alias used above
235                         var ieh = iel.handlers;
236                 }
237
238                 dojo.mixin(del, {
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
245                                         // on IE
246                                         var kd = node.onkeydown;
247                                         if(!kd || !kd._listeners || !kd._stealthKeydownHandle){
248                                                 var h = del.add(node, "onkeydown", del._stealthKeyDown);
249                                                 kd = node.onkeydown;
250                                                 kd._stealthKeydownHandle = h;
251                                                 kd._stealthKeydownRefs = 1;
252                                         }else{
253                                                 kd._stealthKeydownRefs++;
254                                         }
255                                 }
256                                 return iel.add(node, event, del._fixCallback(fp));
257                         },
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;
266                                         }
267                                 }
268                         },
269                         _normalizeEventName: function(/*String*/eventName){
270                                 // Generally, eventName should be lower case, unless it is
271                                 // special somehow (e.g. a Mozilla event)
272                                 // ensure 'on'
273                                 return eventName.slice(0,2) != "on" ? "on" + eventName : eventName;
274                         },
275                         _nop: function(){},
276                         _fixEvent: function(/*Event*/evt, /*DOMNode*/sender){
277                                 // summary:
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"
282                                 if(!evt){
283                                         var w = sender && (sender.ownerDocument || sender.document || sender).parentWindow || window;
284                                         evt = w.event; 
285                                 }
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;
303                                 }
304                                 if(evt.type == "mouseout"){ 
305                                         evt.relatedTarget = evt.toElement;
306                                 }
307                                 evt.stopPropagation = del._stopPropagation;
308                                 evt.preventDefault = del._preventDefault;
309                                 return del._fixKeys(evt);
310                         },
311                         _fixKeys: function(evt){
312                                 switch(evt.type){
313                                         case "keypress":
314                                                 var c = ("charCode" in evt ? evt.charCode : evt.keyCode);
315                                                 if (c==10){
316                                                         // CTRL-ENTER is CTRL-ASCII(10) on IE, but CTRL-ENTER on Mozilla
317                                                         c=0;
318                                                         evt.keyCode = 13;
319                                                 }else if(c==13||c==27){
320                                                         c=0; // Mozilla considers ENTER and ESC non-printable
321                                                 }else if(c==3){
322                                                         c=99; // Mozilla maps CTRL-BREAK to CTRL-c
323                                                 }
324                                                 // Mozilla sets keyCode to 0 when there is a charCode
325                                                 // but that stops the event on IE.
326                                                 evt.charCode = c;
327                                                 del._setKeyChar(evt);
328                                                 break;
329                                 }
330                                 return evt;
331                         },
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
335                         _punctMap: { 
336                                 106:42, 
337                                 111:47, 
338                                 186:59, 
339                                 187:43, 
340                                 188:44, 
341                                 189:45, 
342                                 190:46, 
343                                 191:47, 
344                                 192:96, 
345                                 219:91, 
346                                 220:92, 
347                                 221:93, 
348                                 222:39 
349                         },
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
357                                 var k=evt.keyCode;
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;
364                                         if(evt.ctrlKey){
365                                                 if(k==3 || k==13){
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
371                                                 }else{ 
372                                                         c = del._punctMap[c] || c; // map other problematic CTRL combinations to ASCII
373                                                 }
374                                         }
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);
381                                 }
382                         },
383                         // Called in Event scope
384                         _stopPropagation: function(){
385                                 this.cancelBubble = true; 
386                         },
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;
397                         }
398                 });
399                                 
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);
405                 }
406         }
407
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(); }; 
416                         return faux;
417         }
418         
419         // Opera event normalization
420         if(dojo.isOpera){
421                 dojo.mixin(del, {
422                         _fixEvent: function(evt, sender){
423                                 switch(evt.type){
424                                         case "keypress":
425                                                 var c = evt.which;
426                                                 if(c==3){
427                                                         c=99; // Mozilla maps CTRL-BREAK to CTRL-c
428                                                 }
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
434                                                         c += 32;
435                                                 }
436                                                 return del._synthesizeEvent(evt, { charCode: c });
437                                 }
438                                 return evt;
439                         }
440                 });
441         }
442
443         // Safari event normalization
444         if(dojo.isSafari){
445                 dojo.mixin(del, {
446                         _fixEvent: function(evt, sender){
447                                 switch(evt.type){
448                                         case "keypress":
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
459                                                         s = true;
460                                                 } else {
461                                                         c = (c>=32 && c<63232 ? c : 0); // avoid generating keyChar for non-printables
462                                                 }
463                                                 return del._synthesizeEvent(evt, {charCode: c, shiftKey: s, keyCode: k});
464                                 }
465                                 return evt;
466                         }
467                 });
468                 
469                 dojo.mixin(dojo.keys, {
470                         SHIFT_TAB: 25,
471                         UP_ARROW: 63232,
472                         DOWN_ARROW: 63233,
473                         LEFT_ARROW: 63234,
474                         RIGHT_ARROW: 63235,
475                         F1: 63236,
476                         F2: 63237,
477                         F3: 63238,
478                         F4: 63239,
479                         F5: 63240,
480                         F6: 63241,
481                         F7: 63242,
482                         F8: 63243,
483                         F9: 63244,
484                         F10: 63245,
485                         F11: 63246,
486                         F12: 63247,
487                         PAUSE: 63250,
488                         DELETE: 63272,
489                         HOME: 63273,
490                         END: 63275,
491                         PAGE_UP: 63276,
492                         PAGE_DOWN: 63277,
493                         INSERT: 63302,
494                         PRINT_SCREEN: 63248,
495                         SCROLL_LOCK: 63249,
496                         NUM_LOCK: 63289
497                 });
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 }; 
499         }
500 })();
501
502 if(dojo.isIE){
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
511                 for(var i in ls){
512                         if(!(i in ap)){
513                                 h[ls[i]].apply(sender, args);
514                         }
515                 }
516                 return r;
517         }
518         dojo._getIeDispatcher = function(){
519                 // ensure the returned function closes over nothing
520                 return new Function(dojo._scopeName + "._ieDispatcher(arguments, this)"); // function
521         }
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)); };
526         }
527 }
528
529 }