]>
Commit | Line | Data |
---|---|---|
1354d172 AD |
1 | define("dojo/_base/connect", ["./kernel", "../on", "../topic", "../aspect", "./event", "../mouse", "./sniff", "./lang", "../keys"], function(kernel, on, hub, aspect, eventModule, mouse, has, lang){ |
2 | // module: | |
3 | // dojo/_base/connect | |
4 | // summary: | |
5 | // This module defines the dojo.connect API. | |
6 | // This modules also provides keyboard event handling helpers. | |
7 | // This module exports an extension event for emulating Firefox's keypress handling. | |
8 | // However, this extension event exists primarily for backwards compatibility and | |
9 | // is not recommended. WebKit and IE uses an alternate keypress handling (only | |
10 | // firing for printable characters, to distinguish from keydown events), and most | |
11 | // consider the WebKit/IE behavior more desirable. | |
12 | has.add("events-keypress-typed", function(){ // keypresses should only occur a printable character is hit | |
13 | var testKeyEvent = {charCode: 0}; | |
14 | try{ | |
15 | testKeyEvent = document.createEvent("KeyboardEvent"); | |
16 | (testKeyEvent.initKeyboardEvent || testKeyEvent.initKeyEvent).call(testKeyEvent, "keypress", true, true, null, false, false, false, false, 9, 3); | |
17 | }catch(e){} | |
18 | return testKeyEvent.charCode == 0 && !has("opera"); | |
19 | }); | |
20 | ||
21 | function connect_(obj, event, context, method, dontFix){ | |
22 | method = lang.hitch(context, method); | |
23 | if(!obj || !(obj.addEventListener || obj.attachEvent)){ | |
24 | // it is a not a DOM node and we are using the dojo.connect style of treating a | |
25 | // method like an event, must go right to aspect | |
26 | return aspect.after(obj || kernel.global, event, method, true); | |
27 | } | |
28 | if(typeof event == "string" && event.substring(0, 2) == "on"){ | |
29 | event = event.substring(2); | |
30 | } | |
31 | if(!obj){ | |
32 | obj = kernel.global; | |
33 | } | |
34 | if(!dontFix){ | |
35 | switch(event){ | |
36 | // dojo.connect has special handling for these event types | |
37 | case "keypress": | |
38 | event = keypress; | |
39 | break; | |
40 | case "mouseenter": | |
41 | event = mouse.enter; | |
42 | break; | |
43 | case "mouseleave": | |
44 | event = mouse.leave; | |
45 | break; | |
46 | } | |
47 | } | |
48 | return on(obj, event, method, dontFix); | |
49 | } | |
50 | ||
51 | var _punctMap = { | |
52 | 106:42, | |
53 | 111:47, | |
54 | 186:59, | |
55 | 187:43, | |
56 | 188:44, | |
57 | 189:45, | |
58 | 190:46, | |
59 | 191:47, | |
60 | 192:96, | |
61 | 219:91, | |
62 | 220:92, | |
63 | 221:93, | |
64 | 222:39, | |
65 | 229:113 | |
66 | }; | |
67 | var evtCopyKey = has("mac") ? "metaKey" : "ctrlKey"; | |
68 | ||
69 | ||
70 | var _synthesizeEvent = function(evt, props){ | |
71 | var faux = lang.mixin({}, evt, props); | |
72 | setKeyChar(faux); | |
73 | // FIXME: would prefer to use lang.hitch: lang.hitch(evt, evt.preventDefault); | |
74 | // but it throws an error when preventDefault is invoked on Safari | |
75 | // does Event.preventDefault not support "apply" on Safari? | |
76 | faux.preventDefault = function(){ evt.preventDefault(); }; | |
77 | faux.stopPropagation = function(){ evt.stopPropagation(); }; | |
78 | return faux; | |
79 | }; | |
80 | function setKeyChar(evt){ | |
81 | evt.keyChar = evt.charCode ? String.fromCharCode(evt.charCode) : ''; | |
82 | evt.charOrCode = evt.keyChar || evt.keyCode; | |
83 | } | |
84 | var keypress; | |
85 | if(has("events-keypress-typed")){ | |
86 | // this emulates Firefox's keypress behavior where every keydown can correspond to a keypress | |
87 | var _trySetKeyCode = function(e, code){ | |
88 | try{ | |
89 | // squelch errors when keyCode is read-only | |
90 | // (e.g. if keyCode is ctrl or shift) | |
91 | return (e.keyCode = code); | |
92 | }catch(e){ | |
93 | return 0; | |
94 | } | |
95 | }; | |
96 | keypress = function(object, listener){ | |
97 | var keydownSignal = on(object, "keydown", function(evt){ | |
98 | // munge key/charCode | |
99 | var k=evt.keyCode; | |
100 | // These are Windows Virtual Key Codes | |
101 | // http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp | |
102 | var unprintable = (k!=13 || (has("ie") >= 9 && !has("quirks"))) && k!=32 && (k!=27||!has("ie")) && (k<48||k>90) && (k<96||k>111) && (k<186||k>192) && (k<219||k>222) && k!=229; | |
103 | // synthesize keypress for most unprintables and CTRL-keys | |
104 | if(unprintable||evt.ctrlKey){ | |
105 | var c = unprintable ? 0 : k; | |
106 | if(evt.ctrlKey){ | |
107 | if(k==3 || k==13){ | |
108 | return listener.call(evt.currentTarget, evt); // IE will post CTRL-BREAK, CTRL-ENTER as keypress natively | |
109 | }else if(c>95 && c<106){ | |
110 | c -= 48; // map CTRL-[numpad 0-9] to ASCII | |
111 | }else if((!evt.shiftKey)&&(c>=65&&c<=90)){ | |
112 | c += 32; // map CTRL-[A-Z] to lowercase | |
113 | }else{ | |
114 | c = _punctMap[c] || c; // map other problematic CTRL combinations to ASCII | |
115 | } | |
116 | } | |
117 | // simulate a keypress event | |
118 | var faux = _synthesizeEvent(evt, {type: 'keypress', faux: true, charCode: c}); | |
119 | listener.call(evt.currentTarget, faux); | |
120 | if(has("ie")){ | |
121 | _trySetKeyCode(evt, faux.keyCode); | |
122 | } | |
123 | } | |
124 | }); | |
125 | var keypressSignal = on(object, "keypress", function(evt){ | |
126 | var c = evt.charCode; | |
127 | c = c>=32 ? c : 0; | |
128 | evt = _synthesizeEvent(evt, {charCode: c, faux: true}); | |
129 | return listener.call(this, evt); | |
130 | }); | |
131 | return { | |
132 | remove: function(){ | |
133 | keydownSignal.remove(); | |
134 | keypressSignal.remove(); | |
135 | } | |
136 | }; | |
137 | }; | |
138 | }else{ | |
139 | if(has("opera")){ | |
140 | keypress = function(object, listener){ | |
141 | return on(object, "keypress", function(evt){ | |
142 | var c = evt.which; | |
143 | if(c==3){ | |
144 | c=99; // Mozilla maps CTRL-BREAK to CTRL-c | |
145 | } | |
146 | // can't trap some keys at all, like INSERT and DELETE | |
147 | // there is no differentiating info between DELETE and ".", or INSERT and "-" | |
148 | c = c<32 && !evt.shiftKey ? 0 : c; | |
149 | if(evt.ctrlKey && !evt.shiftKey && c>=65 && c<=90){ | |
150 | // lowercase CTRL-[A-Z] keys | |
151 | c += 32; | |
152 | } | |
153 | return listener.call(this, _synthesizeEvent(evt, { charCode: c })); | |
154 | }); | |
155 | }; | |
156 | }else{ | |
157 | keypress = function(object, listener){ | |
158 | return on(object, "keypress", function(evt){ | |
159 | setKeyChar(evt); | |
160 | return listener.call(this, evt); | |
161 | }); | |
162 | }; | |
163 | } | |
164 | } | |
165 | ||
166 | var connect = { | |
167 | _keypress:keypress, | |
168 | ||
169 | connect:function(obj, event, context, method, dontFix){ | |
170 | // normalize arguments | |
171 | var a=arguments, args=[], i=0; | |
172 | // if a[0] is a String, obj was omitted | |
173 | args.push(typeof a[0] == "string" ? null : a[i++], a[i++]); | |
174 | // if the arg-after-next is a String or Function, context was NOT omitted | |
175 | var a1 = a[i+1]; | |
176 | args.push(typeof a1 == "string" || typeof a1 == "function" ? a[i++] : null, a[i++]); | |
177 | // absorb any additional arguments | |
178 | for(var l=a.length; i<l; i++){ args.push(a[i]); } | |
179 | return connect_.apply(this, args); | |
180 | }, | |
181 | ||
182 | disconnect:function(handle){ | |
183 | if(handle){ | |
184 | handle.remove(); | |
185 | } | |
186 | }, | |
187 | ||
188 | subscribe:function(topic, context, method){ | |
189 | return hub.subscribe(topic, lang.hitch(context, method)); | |
190 | }, | |
191 | ||
192 | publish:function(topic, args){ | |
193 | return hub.publish.apply(hub, [topic].concat(args)); | |
194 | }, | |
195 | ||
196 | connectPublisher:function(topic, obj, event){ | |
197 | var pf = function(){ connect.publish(topic, arguments); }; | |
198 | return event ? connect.connect(obj, event, pf) : connect.connect(obj, pf); //Handle | |
199 | }, | |
200 | ||
201 | isCopyKey: function(e){ | |
202 | return e[evtCopyKey]; // Boolean | |
203 | } | |
204 | }; | |
205 | connect.unsubscribe = connect.disconnect; | |
206 | ||
207 | 1 && lang.mixin(kernel, connect); | |
208 | return connect; | |
209 | ||
210 | /*===== | |
211 | dojo.connect = function(obj, event, context, method, dontFix){ | |
212 | // summary: | |
213 | // `dojo.connect` is the core event handling and delegation method in | |
214 | // Dojo. It allows one function to "listen in" on the execution of | |
215 | // any other, triggering the second whenever the first is called. Many | |
216 | // listeners may be attached to a function, and source functions may | |
217 | // be either regular function calls or DOM events. | |
218 | // | |
219 | // description: | |
220 | // Connects listeners to actions, so that after event fires, a | |
221 | // listener is called with the same arguments passed to the original | |
222 | // function. | |
223 | // | |
224 | // Since `dojo.connect` allows the source of events to be either a | |
225 | // "regular" JavaScript function or a DOM event, it provides a uniform | |
226 | // interface for listening to all the types of events that an | |
227 | // application is likely to deal with though a single, unified | |
228 | // interface. DOM programmers may want to think of it as | |
229 | // "addEventListener for everything and anything". | |
230 | // | |
231 | // When setting up a connection, the `event` parameter must be a | |
232 | // string that is the name of the method/event to be listened for. If | |
233 | // `obj` is null, `kernel.global` is assumed, meaning that connections | |
234 | // to global methods are supported but also that you may inadvertently | |
235 | // connect to a global by passing an incorrect object name or invalid | |
236 | // reference. | |
237 | // | |
238 | // `dojo.connect` generally is forgiving. If you pass the name of a | |
239 | // function or method that does not yet exist on `obj`, connect will | |
240 | // not fail, but will instead set up a stub method. Similarly, null | |
241 | // arguments may simply be omitted such that fewer than 4 arguments | |
242 | // may be required to set up a connection See the examples for details. | |
243 | // | |
244 | // The return value is a handle that is needed to | |
245 | // remove this connection with `dojo.disconnect`. | |
246 | // | |
247 | // obj: Object|null: | |
248 | // The source object for the event function. | |
249 | // Defaults to `kernel.global` if null. | |
250 | // If obj is a DOM node, the connection is delegated | |
251 | // to the DOM event manager (unless dontFix is true). | |
252 | // | |
253 | // event: String: | |
254 | // String name of the event function in obj. | |
255 | // I.e. identifies a property `obj[event]`. | |
256 | // | |
257 | // context: Object|null | |
258 | // The object that method will receive as "this". | |
259 | // | |
260 | // If context is null and method is a function, then method | |
261 | // inherits the context of event. | |
262 | // | |
263 | // If method is a string then context must be the source | |
264 | // object object for method (context[method]). If context is null, | |
265 | // kernel.global is used. | |
266 | // | |
267 | // method: String|Function: | |
268 | // A function reference, or name of a function in context. | |
269 | // The function identified by method fires after event does. | |
270 | // method receives the same arguments as the event. | |
271 | // See context argument comments for information on method's scope. | |
272 | // | |
273 | // dontFix: Boolean? | |
274 | // If obj is a DOM node, set dontFix to true to prevent delegation | |
275 | // of this connection to the DOM event manager. | |
276 | // | |
277 | // example: | |
278 | // When obj.onchange(), do ui.update(): | |
279 | // | dojo.connect(obj, "onchange", ui, "update"); | |
280 | // | dojo.connect(obj, "onchange", ui, ui.update); // same | |
281 | // | |
282 | // example: | |
283 | // Using return value for disconnect: | |
284 | // | var link = dojo.connect(obj, "onchange", ui, "update"); | |
285 | // | ... | |
286 | // | dojo.disconnect(link); | |
287 | // | |
288 | // example: | |
289 | // When onglobalevent executes, watcher.handler is invoked: | |
290 | // | dojo.connect(null, "onglobalevent", watcher, "handler"); | |
291 | // | |
292 | // example: | |
293 | // When ob.onCustomEvent executes, customEventHandler is invoked: | |
294 | // | dojo.connect(ob, "onCustomEvent", null, "customEventHandler"); | |
295 | // | dojo.connect(ob, "onCustomEvent", "customEventHandler"); // same | |
296 | // | |
297 | // example: | |
298 | // When ob.onCustomEvent executes, customEventHandler is invoked | |
299 | // with the same scope (this): | |
300 | // | dojo.connect(ob, "onCustomEvent", null, customEventHandler); | |
301 | // | dojo.connect(ob, "onCustomEvent", customEventHandler); // same | |
302 | // | |
303 | // example: | |
304 | // When globalEvent executes, globalHandler is invoked | |
305 | // with the same scope (this): | |
306 | // | dojo.connect(null, "globalEvent", null, globalHandler); | |
307 | // | dojo.connect("globalEvent", globalHandler); // same | |
308 | } | |
309 | =====*/ | |
310 | ||
311 | /*===== | |
312 | dojo.disconnect = function(handle){ | |
313 | // summary: | |
314 | // Remove a link created by dojo.connect. | |
315 | // description: | |
316 | // Removes the connection between event and the method referenced by handle. | |
317 | // handle: Handle: | |
318 | // the return value of the dojo.connect call that created the connection. | |
319 | } | |
320 | =====*/ | |
321 | ||
322 | /*===== | |
323 | dojo.subscribe = function(topic, context, method){ | |
324 | // summary: | |
325 | // Attach a listener to a named topic. The listener function is invoked whenever the | |
326 | // named topic is published (see: dojo.publish). | |
327 | // Returns a handle which is needed to unsubscribe this listener. | |
328 | // topic: String: | |
329 | // The topic to which to subscribe. | |
330 | // context: Object|null: | |
331 | // Scope in which method will be invoked, or null for default scope. | |
332 | // method: String|Function: | |
333 | // The name of a function in context, or a function reference. This is the function that | |
334 | // is invoked when topic is published. | |
335 | // example: | |
336 | // | dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); }); | |
337 | // | dojo.publish("alerts", [ "read this", "hello world" ]); | |
338 | } | |
339 | =====*/ | |
340 | ||
341 | /*===== | |
342 | dojo.unsubscribe = function(handle){ | |
343 | // summary: | |
344 | // Remove a topic listener. | |
345 | // handle: Handle | |
346 | // The handle returned from a call to subscribe. | |
347 | // example: | |
348 | // | var alerter = dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); }; | |
349 | // | ... | |
350 | // | dojo.unsubscribe(alerter); | |
351 | } | |
352 | =====*/ | |
353 | ||
354 | /*===== | |
355 | dojo.publish = function(topic, args){ | |
356 | // summary: | |
357 | // Invoke all listener method subscribed to topic. | |
358 | // topic: String: | |
359 | // The name of the topic to publish. | |
360 | // args: Array? | |
361 | // An array of arguments. The arguments will be applied | |
362 | // to each topic subscriber (as first class parameters, via apply). | |
363 | // example: | |
364 | // | dojo.subscribe("alerts", null, function(caption, message){ alert(caption + "\n" + message); }; | |
365 | // | dojo.publish("alerts", [ "read this", "hello world" ]); | |
366 | } | |
367 | =====*/ | |
368 | ||
369 | /*===== | |
370 | dojo.connectPublisher = function(topic, obj, event){ | |
371 | // summary: | |
372 | // Ensure that every time obj.event() is called, a message is published | |
373 | // on the topic. Returns a handle which can be passed to | |
374 | // dojo.disconnect() to disable subsequent automatic publication on | |
375 | // the topic. | |
376 | // topic: String: | |
377 | // The name of the topic to publish. | |
378 | // obj: Object|null: | |
379 | // The source object for the event function. Defaults to kernel.global | |
380 | // if null. | |
381 | // event: String: | |
382 | // The name of the event function in obj. | |
383 | // I.e. identifies a property obj[event]. | |
384 | // example: | |
385 | // | dojo.connectPublisher("/ajax/start", dojo, "xhrGet"); | |
386 | } | |
387 | =====*/ | |
388 | ||
389 | /*===== | |
390 | dojo.isCopyKey = function(e){ | |
391 | // summary: | |
392 | // Checks an event for the copy key (meta on Mac, and ctrl anywhere else) | |
393 | // e: Event | |
394 | // Event object to examine | |
395 | } | |
396 | =====*/ | |
397 | ||
398 | }); | |
399 | ||
400 |