]>
Commit | Line | Data |
---|---|---|
2f01fe57 | 1 | /* |
81bea17a | 2 | Copyright (c) 2004-2011, The Dojo Foundation All Rights Reserved. |
2f01fe57 AD |
3 | Available via Academic Free License >= 2.1 OR the modified BSD license. |
4 | see: http://dojotoolkit.org/license for details | |
5 | */ | |
6 | ||
7 | ||
a089699c AD |
8 | if(!dojo._hasResource["dojo._base.event"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code. |
9 | dojo._hasResource["dojo._base.event"] = true; | |
2f01fe57 AD |
10 | dojo.provide("dojo._base.event"); |
11 | dojo.require("dojo._base.connect"); | |
a089699c | 12 | |
81bea17a | 13 | |
a089699c AD |
14 | // this file courtesy of the TurboAjax Group, licensed under a Dojo CLA |
15 | ||
2f01fe57 | 16 | (function(){ |
a089699c AD |
17 | // DOM event listener machinery |
18 | var del = (dojo._event_listener = { | |
19 | add: function(/*DOMNode*/ node, /*String*/ name, /*Function*/ fp){ | |
81bea17a | 20 | if(!node){return;} |
a089699c AD |
21 | name = del._normalizeEventName(name); |
22 | fp = del._fixCallback(name, fp); | |
a089699c | 23 | if( |
81bea17a | 24 | !dojo.isIE && |
a089699c AD |
25 | (name == "mouseenter" || name == "mouseleave") |
26 | ){ | |
27 | var ofp = fp; | |
a089699c AD |
28 | name = (name == "mouseenter") ? "mouseover" : "mouseout"; |
29 | fp = function(e){ | |
30 | if(!dojo.isDescendant(e.relatedTarget, node)){ | |
31 | // e.type = oname; // FIXME: doesn't take? SJM: event.type is generally immutable. | |
81bea17a | 32 | return ofp.call(this, e); |
a089699c AD |
33 | } |
34 | } | |
35 | } | |
36 | node.addEventListener(name, fp, false); | |
37 | return fp; /*Handle*/ | |
38 | }, | |
39 | remove: function(/*DOMNode*/ node, /*String*/ event, /*Handle*/ handle){ | |
40 | // summary: | |
41 | // clobbers the listener from the node | |
42 | // node: | |
43 | // DOM node to attach the event to | |
44 | // event: | |
45 | // the name of the handler to remove the function from | |
46 | // handle: | |
47 | // the handle returned from add | |
48 | if(node){ | |
49 | event = del._normalizeEventName(event); | |
50 | if(!dojo.isIE && (event == "mouseenter" || event == "mouseleave")){ | |
51 | event = (event == "mouseenter") ? "mouseover" : "mouseout"; | |
52 | } | |
53 | ||
54 | node.removeEventListener(event, handle, false); | |
55 | } | |
56 | }, | |
57 | _normalizeEventName: function(/*String*/ name){ | |
58 | // Generally, name should be lower case, unless it is special | |
59 | // somehow (e.g. a Mozilla DOM event). | |
60 | // Remove 'on'. | |
61 | return name.slice(0,2) =="on" ? name.slice(2) : name; | |
62 | }, | |
63 | _fixCallback: function(/*String*/ name, fp){ | |
64 | // By default, we only invoke _fixEvent for 'keypress' | |
65 | // If code is added to _fixEvent for other events, we have | |
66 | // to revisit this optimization. | |
67 | // This also applies to _fixEvent overrides for Safari and Opera | |
68 | // below. | |
69 | return name != "keypress" ? fp : function(e){ return fp.call(this, del._fixEvent(e, this)); }; | |
70 | }, | |
71 | _fixEvent: function(evt, sender){ | |
72 | // _fixCallback only attaches us to keypress. | |
81bea17a | 73 | // Switch on evt.type anyway because we might |
a089699c AD |
74 | // be called directly from dojo.fixEvent. |
75 | switch(evt.type){ | |
76 | case "keypress": | |
77 | del._setKeyChar(evt); | |
78 | break; | |
79 | } | |
80 | return evt; | |
81 | }, | |
82 | _setKeyChar: function(evt){ | |
81bea17a | 83 | evt.keyChar = evt.charCode >= 32 ? String.fromCharCode(evt.charCode) : ''; |
a089699c AD |
84 | evt.charOrCode = evt.keyChar || evt.keyCode; |
85 | }, | |
86 | // For IE and Safari: some ctrl-key combinations (mostly w/punctuation) do not emit a char code in IE | |
87 | // we map those virtual key codes to ascii here | |
88 | // not valid for all (non-US) keyboards, so maybe we shouldn't bother | |
81bea17a AD |
89 | _punctMap: { |
90 | 106:42, | |
91 | 111:47, | |
92 | 186:59, | |
93 | 187:43, | |
94 | 188:44, | |
95 | 189:45, | |
96 | 190:46, | |
97 | 191:47, | |
98 | 192:96, | |
99 | 219:91, | |
100 | 220:92, | |
101 | 221:93, | |
102 | 222:39 | |
a089699c AD |
103 | } |
104 | }); | |
105 | ||
106 | // DOM events | |
107 | ||
108 | dojo.fixEvent = function(/*Event*/ evt, /*DOMNode*/ sender){ | |
109 | // summary: | |
110 | // normalizes properties on the event object including event | |
111 | // bubbling methods, keystroke normalization, and x/y positions | |
112 | // evt: Event | |
113 | // native event object | |
114 | // sender: DOMNode | |
115 | // node to treat as "currentTarget" | |
116 | return del._fixEvent(evt, sender); | |
81bea17a | 117 | }; |
a089699c AD |
118 | |
119 | dojo.stopEvent = function(/*Event*/ evt){ | |
120 | // summary: | |
121 | // prevents propagation and clobbers the default action of the | |
122 | // passed event | |
123 | // evt: Event | |
124 | // The event object. If omitted, window.event is used on IE. | |
125 | evt.preventDefault(); | |
126 | evt.stopPropagation(); | |
127 | // NOTE: below, this method is overridden for IE | |
81bea17a | 128 | }; |
a089699c AD |
129 | |
130 | // the default listener to use on dontFix nodes, overriden for IE | |
131 | var node_listener = dojo._listener; | |
132 | ||
133 | // Unify connect and event listeners | |
134 | dojo._connect = function(obj, event, context, method, dontFix){ | |
135 | // FIXME: need a more strict test | |
136 | var isNode = obj && (obj.nodeType||obj.attachEvent||obj.addEventListener); | |
137 | // choose one of three listener options: raw (connect.js), DOM event on a Node, custom event on a Node | |
138 | // we need the third option to provide leak prevention on broken browsers (IE) | |
139 | var lid = isNode ? (dontFix ? 2 : 1) : 0, l = [dojo._listener, del, node_listener][lid]; | |
140 | // create a listener | |
141 | var h = l.add(obj, event, dojo.hitch(context, method)); | |
142 | // formerly, the disconnect package contained "l" directly, but if client code | |
81bea17a | 143 | // leaks the disconnect package (by connecting it to a node), referencing "l" |
a089699c AD |
144 | // compounds the problem. |
145 | // instead we return a listener id, which requires custom _disconnect below. | |
146 | // return disconnect package | |
147 | return [ obj, event, h, lid ]; | |
81bea17a | 148 | }; |
a089699c AD |
149 | |
150 | dojo._disconnect = function(obj, event, handle, listener){ | |
151 | ([dojo._listener, del, node_listener][listener]).remove(obj, event, handle); | |
81bea17a | 152 | }; |
a089699c AD |
153 | |
154 | // Constants | |
155 | ||
156 | // Public: client code should test | |
157 | // keyCode against these named constants, as the | |
158 | // actual codes can vary by browser. | |
159 | dojo.keys = { | |
160 | // summary: | |
161 | // Definitions for common key values | |
162 | BACKSPACE: 8, | |
163 | TAB: 9, | |
164 | CLEAR: 12, | |
165 | ENTER: 13, | |
166 | SHIFT: 16, | |
167 | CTRL: 17, | |
168 | ALT: 18, | |
169 | META: dojo.isSafari ? 91 : 224, // the apple key on macs | |
170 | PAUSE: 19, | |
171 | CAPS_LOCK: 20, | |
172 | ESCAPE: 27, | |
173 | SPACE: 32, | |
174 | PAGE_UP: 33, | |
175 | PAGE_DOWN: 34, | |
176 | END: 35, | |
177 | HOME: 36, | |
178 | LEFT_ARROW: 37, | |
179 | UP_ARROW: 38, | |
180 | RIGHT_ARROW: 39, | |
181 | DOWN_ARROW: 40, | |
182 | INSERT: 45, | |
183 | DELETE: 46, | |
184 | HELP: 47, | |
185 | LEFT_WINDOW: 91, | |
186 | RIGHT_WINDOW: 92, | |
187 | SELECT: 93, | |
188 | NUMPAD_0: 96, | |
189 | NUMPAD_1: 97, | |
190 | NUMPAD_2: 98, | |
191 | NUMPAD_3: 99, | |
192 | NUMPAD_4: 100, | |
193 | NUMPAD_5: 101, | |
194 | NUMPAD_6: 102, | |
195 | NUMPAD_7: 103, | |
196 | NUMPAD_8: 104, | |
197 | NUMPAD_9: 105, | |
198 | NUMPAD_MULTIPLY: 106, | |
199 | NUMPAD_PLUS: 107, | |
200 | NUMPAD_ENTER: 108, | |
201 | NUMPAD_MINUS: 109, | |
202 | NUMPAD_PERIOD: 110, | |
203 | NUMPAD_DIVIDE: 111, | |
204 | F1: 112, | |
205 | F2: 113, | |
206 | F3: 114, | |
207 | F4: 115, | |
208 | F5: 116, | |
209 | F6: 117, | |
210 | F7: 118, | |
211 | F8: 119, | |
212 | F9: 120, | |
213 | F10: 121, | |
214 | F11: 122, | |
215 | F12: 123, | |
216 | F13: 124, | |
217 | F14: 125, | |
218 | F15: 126, | |
219 | NUM_LOCK: 144, | |
220 | SCROLL_LOCK: 145, | |
221 | // virtual key mapping | |
222 | copyKey: dojo.isMac && !dojo.isAIR ? (dojo.isSafari ? 91 : 224 ) : 17 | |
223 | }; | |
224 | ||
225 | var evtCopyKey = dojo.isMac ? "metaKey" : "ctrlKey"; | |
226 | ||
227 | dojo.isCopyKey = function(e){ | |
228 | // summary: | |
229 | // Checks an event for the copy key (meta on Mac, and ctrl anywhere else) | |
230 | // e: Event | |
231 | // Event object to examine | |
232 | return e[evtCopyKey]; // Boolean | |
233 | }; | |
234 | ||
235 | // Public: decoding mouse buttons from events | |
236 | ||
237 | /*===== | |
238 | dojo.mouseButtons = { | |
239 | // LEFT: Number | |
240 | // Numeric value of the left mouse button for the platform. | |
241 | LEFT: 0, | |
242 | // MIDDLE: Number | |
243 | // Numeric value of the middle mouse button for the platform. | |
244 | MIDDLE: 1, | |
245 | // RIGHT: Number | |
246 | // Numeric value of the right mouse button for the platform. | |
247 | RIGHT: 2, | |
248 | ||
249 | isButton: function(e, button){ | |
250 | // summary: | |
251 | // Checks an event object for a pressed button | |
252 | // e: Event | |
253 | // Event object to examine | |
254 | // button: Number | |
255 | // The button value (example: dojo.mouseButton.LEFT) | |
256 | return e.button == button; // Boolean | |
257 | }, | |
258 | isLeft: function(e){ | |
259 | // summary: | |
260 | // Checks an event object for the pressed left button | |
261 | // e: Event | |
262 | // Event object to examine | |
263 | return e.button == 0; // Boolean | |
264 | }, | |
265 | isMiddle: function(e){ | |
266 | // summary: | |
267 | // Checks an event object for the pressed middle button | |
268 | // e: Event | |
269 | // Event object to examine | |
270 | return e.button == 1; // Boolean | |
271 | }, | |
272 | isRight: function(e){ | |
273 | // summary: | |
274 | // Checks an event object for the pressed right button | |
275 | // e: Event | |
276 | // Event object to examine | |
277 | return e.button == 2; // Boolean | |
278 | } | |
279 | }; | |
280 | =====*/ | |
281 | ||
81bea17a | 282 | if(dojo.isIE < 9 || (dojo.isIE && dojo.isQuirks)){ |
a089699c AD |
283 | dojo.mouseButtons = { |
284 | LEFT: 1, | |
285 | MIDDLE: 4, | |
286 | RIGHT: 2, | |
287 | // helper functions | |
288 | isButton: function(e, button){ return e.button & button; }, | |
289 | isLeft: function(e){ return e.button & 1; }, | |
290 | isMiddle: function(e){ return e.button & 4; }, | |
291 | isRight: function(e){ return e.button & 2; } | |
292 | }; | |
293 | }else{ | |
294 | dojo.mouseButtons = { | |
295 | LEFT: 0, | |
296 | MIDDLE: 1, | |
297 | RIGHT: 2, | |
298 | // helper functions | |
299 | isButton: function(e, button){ return e.button == button; }, | |
300 | isLeft: function(e){ return e.button == 0; }, | |
301 | isMiddle: function(e){ return e.button == 1; }, | |
302 | isRight: function(e){ return e.button == 2; } | |
303 | }; | |
304 | } | |
305 | ||
306 | // IE event normalization | |
81bea17a | 307 | if(dojo.isIE){ |
a089699c AD |
308 | var _trySetKeyCode = function(e, code){ |
309 | try{ | |
310 | // squelch errors when keyCode is read-only | |
311 | // (e.g. if keyCode is ctrl or shift) | |
312 | return (e.keyCode = code); | |
313 | }catch(e){ | |
314 | return 0; | |
315 | } | |
81bea17a | 316 | }; |
a089699c AD |
317 | |
318 | // by default, use the standard listener | |
319 | var iel = dojo._listener; | |
320 | var listenersName = (dojo._ieListenersName = "_" + dojo._scopeName + "_listeners"); | |
321 | // dispatcher tracking property | |
322 | if(!dojo.config._allow_leaks){ | |
323 | // custom listener that handles leak protection for DOM events | |
324 | node_listener = iel = dojo._ie_listener = { | |
81bea17a | 325 | // support handler indirection: event handler functions are |
a089699c AD |
326 | // referenced here. Event dispatchers hold only indices. |
327 | handlers: [], | |
328 | // add a listener to an object | |
329 | add: function(/*Object*/ source, /*String*/ method, /*Function*/ listener){ | |
330 | source = source || dojo.global; | |
331 | var f = source[method]; | |
332 | if(!f||!f[listenersName]){ | |
333 | var d = dojo._getIeDispatcher(); | |
334 | // original target function is special | |
335 | d.target = f && (ieh.push(f) - 1); | |
336 | // dispatcher holds a list of indices into handlers table | |
337 | d[listenersName] = []; | |
338 | // redirect source to dispatcher | |
339 | f = source[method] = d; | |
340 | } | |
341 | return f[listenersName].push(ieh.push(listener) - 1) ; /*Handle*/ | |
342 | }, | |
343 | // remove a listener from an object | |
344 | remove: function(/*Object*/ source, /*String*/ method, /*Handle*/ handle){ | |
345 | var f = (source||dojo.global)[method], l = f && f[listenersName]; | |
346 | if(f && l && handle--){ | |
347 | delete ieh[l[handle]]; | |
348 | delete l[handle]; | |
349 | } | |
350 | } | |
351 | }; | |
352 | // alias used above | |
353 | var ieh = iel.handlers; | |
354 | } | |
355 | ||
356 | dojo.mixin(del, { | |
357 | add: function(/*DOMNode*/ node, /*String*/ event, /*Function*/ fp){ | |
358 | if(!node){return;} // undefined | |
359 | event = del._normalizeEventName(event); | |
360 | if(event=="onkeypress"){ | |
361 | // we need to listen to onkeydown to synthesize | |
362 | // keypress events that otherwise won't fire | |
363 | // on IE | |
364 | var kd = node.onkeydown; | |
365 | if(!kd || !kd[listenersName] || !kd._stealthKeydownHandle){ | |
366 | var h = del.add(node, "onkeydown", del._stealthKeyDown); | |
367 | kd = node.onkeydown; | |
368 | kd._stealthKeydownHandle = h; | |
369 | kd._stealthKeydownRefs = 1; | |
370 | }else{ | |
371 | kd._stealthKeydownRefs++; | |
372 | } | |
373 | } | |
374 | return iel.add(node, event, del._fixCallback(fp)); | |
375 | }, | |
376 | remove: function(/*DOMNode*/ node, /*String*/ event, /*Handle*/ handle){ | |
377 | event = del._normalizeEventName(event); | |
81bea17a | 378 | iel.remove(node, event, handle); |
a089699c AD |
379 | if(event=="onkeypress"){ |
380 | var kd = node.onkeydown; | |
381 | if(--kd._stealthKeydownRefs <= 0){ | |
382 | iel.remove(node, "onkeydown", kd._stealthKeydownHandle); | |
383 | delete kd._stealthKeydownHandle; | |
384 | } | |
385 | } | |
386 | }, | |
387 | _normalizeEventName: function(/*String*/ eventName){ | |
388 | // Generally, eventName should be lower case, unless it is | |
389 | // special somehow (e.g. a Mozilla event) | |
390 | // ensure 'on' | |
391 | return eventName.slice(0,2) != "on" ? "on" + eventName : eventName; | |
392 | }, | |
393 | _nop: function(){}, | |
394 | _fixEvent: function(/*Event*/ evt, /*DOMNode*/ sender){ | |
395 | // summary: | |
396 | // normalizes properties on the event object including event | |
397 | // bubbling methods, keystroke normalization, and x/y positions | |
398 | // evt: | |
399 | // native event object | |
400 | // sender: | |
401 | // node to treat as "currentTarget" | |
402 | if(!evt){ | |
403 | var w = sender && (sender.ownerDocument || sender.document || sender).parentWindow || window; | |
81bea17a | 404 | evt = w.event; |
a089699c AD |
405 | } |
406 | if(!evt){return(evt);} | |
81bea17a AD |
407 | evt.target = evt.srcElement; |
408 | evt.currentTarget = (sender || evt.srcElement); | |
a089699c AD |
409 | evt.layerX = evt.offsetX; |
410 | evt.layerY = evt.offsetY; | |
411 | // FIXME: scroll position query is duped from dojo.html to | |
412 | // avoid dependency on that entire module. Now that HTML is in | |
413 | // Base, we should convert back to something similar there. | |
414 | var se = evt.srcElement, doc = (se && se.ownerDocument) || document; | |
415 | // DO NOT replace the following to use dojo.body(), in IE, document.documentElement should be used | |
416 | // here rather than document.body | |
417 | var docBody = ((dojo.isIE < 6) || (doc["compatMode"] == "BackCompat")) ? doc.body : doc.documentElement; | |
418 | var offset = dojo._getIeDocumentElementOffset(); | |
419 | evt.pageX = evt.clientX + dojo._fixIeBiDiScrollLeft(docBody.scrollLeft || 0) - offset.x; | |
420 | evt.pageY = evt.clientY + (docBody.scrollTop || 0) - offset.y; | |
81bea17a | 421 | if(evt.type == "mouseover"){ |
a089699c AD |
422 | evt.relatedTarget = evt.fromElement; |
423 | } | |
81bea17a | 424 | if(evt.type == "mouseout"){ |
a089699c AD |
425 | evt.relatedTarget = evt.toElement; |
426 | } | |
81bea17a AD |
427 | if (dojo.isIE < 9 || dojo.isQuirks) { |
428 | evt.stopPropagation = del._stopPropagation; | |
429 | evt.preventDefault = del._preventDefault; | |
430 | } | |
a089699c AD |
431 | return del._fixKeys(evt); |
432 | }, | |
433 | _fixKeys: function(evt){ | |
434 | switch(evt.type){ | |
435 | case "keypress": | |
436 | var c = ("charCode" in evt ? evt.charCode : evt.keyCode); | |
437 | if (c==10){ | |
438 | // CTRL-ENTER is CTRL-ASCII(10) on IE, but CTRL-ENTER on Mozilla | |
439 | c=0; | |
440 | evt.keyCode = 13; | |
441 | }else if(c==13||c==27){ | |
442 | c=0; // Mozilla considers ENTER and ESC non-printable | |
443 | }else if(c==3){ | |
444 | c=99; // Mozilla maps CTRL-BREAK to CTRL-c | |
445 | } | |
446 | // Mozilla sets keyCode to 0 when there is a charCode | |
447 | // but that stops the event on IE. | |
448 | evt.charCode = c; | |
449 | del._setKeyChar(evt); | |
450 | break; | |
451 | } | |
452 | return evt; | |
453 | }, | |
454 | _stealthKeyDown: function(evt){ | |
455 | // IE doesn't fire keypress for most non-printable characters. | |
456 | // other browsers do, we simulate it here. | |
457 | var kp = evt.currentTarget.onkeypress; | |
458 | // only works if kp exists and is a dispatcher | |
459 | if(!kp || !kp[listenersName]){ return; } | |
460 | // munge key/charCode | |
461 | var k=evt.keyCode; | |
462 | // These are Windows Virtual Key Codes | |
463 | // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp | |
81bea17a AD |
464 | var unprintable = (k!=13 || (dojo.isIE >= 9 && !dojo.isQuirks)) && k!=32 && k!=27 && (k<48||k>90) && (k<96||k>111) && (k<186||k>192) && (k<219||k>222); |
465 | ||
a089699c AD |
466 | // synthesize keypress for most unprintables and CTRL-keys |
467 | if(unprintable||evt.ctrlKey){ | |
468 | var c = unprintable ? 0 : k; | |
469 | if(evt.ctrlKey){ | |
470 | if(k==3 || k==13){ | |
81bea17a AD |
471 | return; // IE will post CTRL-BREAK, CTRL-ENTER as keypress natively |
472 | }else if(c>95 && c<106){ | |
a089699c | 473 | c -= 48; // map CTRL-[numpad 0-9] to ASCII |
81bea17a | 474 | }else if((!evt.shiftKey)&&(c>=65&&c<=90)){ |
a089699c | 475 | c += 32; // map CTRL-[A-Z] to lowercase |
81bea17a | 476 | }else{ |
a089699c AD |
477 | c = del._punctMap[c] || c; // map other problematic CTRL combinations to ASCII |
478 | } | |
479 | } | |
480 | // simulate a keypress event | |
481 | var faux = del._synthesizeEvent(evt, {type: 'keypress', faux: true, charCode: c}); | |
482 | kp.call(evt.currentTarget, faux); | |
81bea17a AD |
483 | if(dojo.isIE < 9 || (dojo.isIE && dojo.isQuirks)){ |
484 | evt.cancelBubble = faux.cancelBubble; | |
485 | } | |
a089699c AD |
486 | evt.returnValue = faux.returnValue; |
487 | _trySetKeyCode(evt, faux.keyCode); | |
488 | } | |
489 | }, | |
490 | // Called in Event scope | |
491 | _stopPropagation: function(){ | |
81bea17a | 492 | this.cancelBubble = true; |
a089699c AD |
493 | }, |
494 | _preventDefault: function(){ | |
495 | // Setting keyCode to 0 is the only way to prevent certain keypresses (namely | |
496 | // ctrl-combinations that correspond to menu accelerator keys). | |
497 | // Otoh, it prevents upstream listeners from getting this information | |
81bea17a | 498 | // Try to split the difference here by clobbering keyCode only for ctrl |
a089699c AD |
499 | // combinations. If you still need to access the key upstream, bubbledKeyCode is |
500 | // provided as a workaround. | |
501 | this.bubbledKeyCode = this.keyCode; | |
502 | if(this.ctrlKey){_trySetKeyCode(this, 0);} | |
503 | this.returnValue = false; | |
504 | } | |
505 | }); | |
506 | ||
507 | // override stopEvent for IE | |
81bea17a | 508 | dojo.stopEvent = (dojo.isIE < 9 || dojo.isQuirks) ? function(evt){ |
a089699c AD |
509 | evt = evt || window.event; |
510 | del._stopPropagation.call(evt); | |
511 | del._preventDefault.call(evt); | |
81bea17a | 512 | } : dojo.stopEvent; |
a089699c AD |
513 | } |
514 | ||
515 | del._synthesizeEvent = function(evt, props){ | |
516 | var faux = dojo.mixin({}, evt, props); | |
517 | del._setKeyChar(faux); | |
81bea17a | 518 | // FIXME: would prefer to use dojo.hitch: dojo.hitch(evt, evt.preventDefault); |
a089699c AD |
519 | // but it throws an error when preventDefault is invoked on Safari |
520 | // does Event.preventDefault not support "apply" on Safari? | |
81bea17a AD |
521 | faux.preventDefault = function(){ evt.preventDefault(); }; |
522 | faux.stopPropagation = function(){ evt.stopPropagation(); }; | |
a089699c | 523 | return faux; |
81bea17a | 524 | }; |
a089699c AD |
525 | |
526 | // Opera event normalization | |
527 | if(dojo.isOpera){ | |
528 | dojo.mixin(del, { | |
529 | _fixEvent: function(evt, sender){ | |
530 | switch(evt.type){ | |
531 | case "keypress": | |
532 | var c = evt.which; | |
533 | if(c==3){ | |
534 | c=99; // Mozilla maps CTRL-BREAK to CTRL-c | |
535 | } | |
536 | // can't trap some keys at all, like INSERT and DELETE | |
537 | // there is no differentiating info between DELETE and ".", or INSERT and "-" | |
538 | c = c<41 && !evt.shiftKey ? 0 : c; | |
539 | if(evt.ctrlKey && !evt.shiftKey && c>=65 && c<=90){ | |
540 | // lowercase CTRL-[A-Z] keys | |
541 | c += 32; | |
542 | } | |
543 | return del._synthesizeEvent(evt, { charCode: c }); | |
544 | } | |
545 | return evt; | |
546 | } | |
547 | }); | |
548 | } | |
549 | ||
550 | // Webkit event normalization | |
551 | if(dojo.isWebKit){ | |
552 | del._add = del.add; | |
553 | del._remove = del.remove; | |
554 | ||
555 | dojo.mixin(del, { | |
556 | add: function(/*DOMNode*/ node, /*String*/ event, /*Function*/ fp){ | |
557 | if(!node){return;} // undefined | |
558 | var handle = del._add(node, event, fp); | |
559 | if(del._normalizeEventName(event) == "keypress"){ | |
560 | // we need to listen to onkeydown to synthesize | |
561 | // keypress events that otherwise won't fire | |
562 | // in Safari 3.1+: https://lists.webkit.org/pipermail/webkit-dev/2007-December/002992.html | |
563 | handle._stealthKeyDownHandle = del._add(node, "keydown", function(evt){ | |
564 | //A variation on the IE _stealthKeydown function | |
565 | //Synthesize an onkeypress event, but only for unprintable characters. | |
566 | var k=evt.keyCode; | |
567 | // These are Windows Virtual Key Codes | |
568 | // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp | |
569 | var unprintable = k!=13 && k!=32 && (k<48 || k>90) && (k<96 || k>111) && (k<186 || k>192) && (k<219 || k>222); | |
570 | // synthesize keypress for most unprintables and CTRL-keys | |
571 | if(unprintable || evt.ctrlKey){ | |
572 | var c = unprintable ? 0 : k; | |
573 | if(evt.ctrlKey){ | |
574 | if(k==3 || k==13){ | |
81bea17a AD |
575 | return; // IE will post CTRL-BREAK, CTRL-ENTER as keypress natively |
576 | }else if(c>95 && c<106){ | |
a089699c | 577 | c -= 48; // map CTRL-[numpad 0-9] to ASCII |
81bea17a | 578 | }else if(!evt.shiftKey && c>=65 && c<=90){ |
a089699c | 579 | c += 32; // map CTRL-[A-Z] to lowercase |
81bea17a | 580 | }else{ |
a089699c AD |
581 | c = del._punctMap[c] || c; // map other problematic CTRL combinations to ASCII |
582 | } | |
583 | } | |
584 | // simulate a keypress event | |
585 | var faux = del._synthesizeEvent(evt, {type: 'keypress', faux: true, charCode: c}); | |
586 | fp.call(evt.currentTarget, faux); | |
587 | } | |
588 | }); | |
589 | } | |
590 | return handle; /*Handle*/ | |
591 | }, | |
592 | ||
593 | remove: function(/*DOMNode*/ node, /*String*/ event, /*Handle*/ handle){ | |
594 | if(node){ | |
595 | if(handle._stealthKeyDownHandle){ | |
596 | del._remove(node, "keydown", handle._stealthKeyDownHandle); | |
597 | } | |
598 | del._remove(node, event, handle); | |
599 | } | |
600 | }, | |
601 | _fixEvent: function(evt, sender){ | |
602 | switch(evt.type){ | |
603 | case "keypress": | |
604 | if(evt.faux){ return evt; } | |
605 | var c = evt.charCode; | |
606 | c = c>=32 ? c : 0; | |
607 | return del._synthesizeEvent(evt, {charCode: c, faux: true}); | |
608 | } | |
609 | return evt; | |
610 | } | |
611 | }); | |
612 | } | |
613 | })(); | |
614 | ||
2f01fe57 | 615 | if(dojo.isIE){ |
a089699c AD |
616 | // keep this out of the closure |
617 | // closing over 'iel' or 'ieh' b0rks leak prevention | |
618 | // ls[i] is an index into the master handler array | |
619 | dojo._ieDispatcher = function(args, sender){ | |
620 | var ap = Array.prototype, | |
621 | h = dojo._ie_listener.handlers, | |
622 | c = args.callee, | |
623 | ls = c[dojo._ieListenersName], | |
624 | t = h[c.target]; | |
625 | // return value comes from original target function | |
626 | var r = t && t.apply(sender, args); | |
627 | // make local copy of listener array so it's immutable during processing | |
628 | var lls = [].concat(ls); | |
629 | // invoke listeners after target function | |
630 | for(var i in lls){ | |
631 | var f = h[lls[i]]; | |
632 | if(!(i in ap) && f){ | |
633 | f.apply(sender, args); | |
634 | } | |
635 | } | |
636 | return r; | |
81bea17a | 637 | }; |
a089699c AD |
638 | dojo._getIeDispatcher = function(){ |
639 | // ensure the returned function closes over nothing ("new Function" apparently doesn't close) | |
640 | return new Function(dojo._scopeName + "._ieDispatcher(arguments, this)"); // function | |
81bea17a | 641 | }; |
a089699c AD |
642 | // keep this out of the closure to reduce RAM allocation |
643 | dojo._event_listener._fixCallback = function(fp){ | |
644 | var f = dojo._event_listener._fixEvent; | |
645 | return function(e){ return fp.call(this, f(e, this)); }; | |
81bea17a | 646 | }; |
2f01fe57 | 647 | } |
a089699c | 648 | |
2f01fe57 | 649 | } |