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