[483] | 1 | define(["./has!dom-addeventlistener?:./aspect", "./_base/kernel", "./sniff"], function(aspect, dojo, has){ |
---|
| 2 | |
---|
| 3 | "use strict"; |
---|
| 4 | if(has("dom")){ // check to make sure we are in a browser, this module should work anywhere |
---|
| 5 | var major = window.ScriptEngineMajorVersion; |
---|
| 6 | has.add("jscript", major && (major() + ScriptEngineMinorVersion() / 10)); |
---|
| 7 | has.add("event-orientationchange", has("touch") && !has("android")); // TODO: how do we detect this? |
---|
| 8 | has.add("event-stopimmediatepropagation", window.Event && !!window.Event.prototype && !!window.Event.prototype.stopImmediatePropagation); |
---|
| 9 | has.add("event-focusin", function(global, doc, element){ |
---|
| 10 | return 'onfocusin' in element; |
---|
| 11 | }); |
---|
| 12 | } |
---|
| 13 | var on = function(target, type, listener, dontFix){ |
---|
| 14 | // summary: |
---|
| 15 | // A function that provides core event listening functionality. With this function |
---|
| 16 | // you can provide a target, event type, and listener to be notified of |
---|
| 17 | // future matching events that are fired. |
---|
| 18 | // target: Element|Object |
---|
| 19 | // This is the target object or DOM element that to receive events from |
---|
| 20 | // type: String|Function |
---|
| 21 | // This is the name of the event to listen for or an extension event type. |
---|
| 22 | // listener: Function |
---|
| 23 | // This is the function that should be called when the event fires. |
---|
| 24 | // returns: Object |
---|
| 25 | // An object with a remove() method that can be used to stop listening for this |
---|
| 26 | // event. |
---|
| 27 | // description: |
---|
| 28 | // To listen for "click" events on a button node, we can do: |
---|
| 29 | // | define(["dojo/on"], function(listen){ |
---|
| 30 | // | on(button, "click", clickHandler); |
---|
| 31 | // | ... |
---|
| 32 | // Evented JavaScript objects can also have their own events. |
---|
| 33 | // | var obj = new Evented; |
---|
| 34 | // | on(obj, "foo", fooHandler); |
---|
| 35 | // And then we could publish a "foo" event: |
---|
| 36 | // | on.emit(obj, "foo", {key: "value"}); |
---|
| 37 | // We can use extension events as well. For example, you could listen for a tap gesture: |
---|
| 38 | // | define(["dojo/on", "dojo/gesture/tap", function(listen, tap){ |
---|
| 39 | // | on(button, tap, tapHandler); |
---|
| 40 | // | ... |
---|
| 41 | // which would trigger fooHandler. Note that for a simple object this is equivalent to calling: |
---|
| 42 | // | obj.onfoo({key:"value"}); |
---|
| 43 | // If you use on.emit on a DOM node, it will use native event dispatching when possible. |
---|
| 44 | |
---|
| 45 | if(typeof target.on == "function" && typeof type != "function" && !target.nodeType){ |
---|
| 46 | // delegate to the target's on() method, so it can handle it's own listening if it wants (unless it |
---|
| 47 | // is DOM node and we may be dealing with jQuery or Prototype's incompatible addition to the |
---|
| 48 | // Element prototype |
---|
| 49 | return target.on(type, listener); |
---|
| 50 | } |
---|
| 51 | // delegate to main listener code |
---|
| 52 | return on.parse(target, type, listener, addListener, dontFix, this); |
---|
| 53 | }; |
---|
| 54 | on.pausable = function(target, type, listener, dontFix){ |
---|
| 55 | // summary: |
---|
| 56 | // This function acts the same as on(), but with pausable functionality. The |
---|
| 57 | // returned signal object has pause() and resume() functions. Calling the |
---|
| 58 | // pause() method will cause the listener to not be called for future events. Calling the |
---|
| 59 | // resume() method will cause the listener to again be called for future events. |
---|
| 60 | var paused; |
---|
| 61 | var signal = on(target, type, function(){ |
---|
| 62 | if(!paused){ |
---|
| 63 | return listener.apply(this, arguments); |
---|
| 64 | } |
---|
| 65 | }, dontFix); |
---|
| 66 | signal.pause = function(){ |
---|
| 67 | paused = true; |
---|
| 68 | }; |
---|
| 69 | signal.resume = function(){ |
---|
| 70 | paused = false; |
---|
| 71 | }; |
---|
| 72 | return signal; |
---|
| 73 | }; |
---|
| 74 | on.once = function(target, type, listener, dontFix){ |
---|
| 75 | // summary: |
---|
| 76 | // This function acts the same as on(), but will only call the listener once. The |
---|
| 77 | // listener will be called for the first |
---|
| 78 | // event that takes place and then listener will automatically be removed. |
---|
| 79 | var signal = on(target, type, function(){ |
---|
| 80 | // remove this listener |
---|
| 81 | signal.remove(); |
---|
| 82 | // proceed to call the listener |
---|
| 83 | return listener.apply(this, arguments); |
---|
| 84 | }); |
---|
| 85 | return signal; |
---|
| 86 | }; |
---|
| 87 | on.parse = function(target, type, listener, addListener, dontFix, matchesTarget){ |
---|
| 88 | if(type.call){ |
---|
| 89 | // event handler function |
---|
| 90 | // on(node, touch.press, touchListener); |
---|
| 91 | return type.call(matchesTarget, target, listener); |
---|
| 92 | } |
---|
| 93 | |
---|
| 94 | if(type.indexOf(",") > -1){ |
---|
| 95 | // we allow comma delimited event names, so you can register for multiple events at once |
---|
| 96 | var events = type.split(/\s*,\s*/); |
---|
| 97 | var handles = []; |
---|
| 98 | var i = 0; |
---|
| 99 | var eventName; |
---|
| 100 | while(eventName = events[i++]){ |
---|
| 101 | handles.push(addListener(target, eventName, listener, dontFix, matchesTarget)); |
---|
| 102 | } |
---|
| 103 | handles.remove = function(){ |
---|
| 104 | for(var i = 0; i < handles.length; i++){ |
---|
| 105 | handles[i].remove(); |
---|
| 106 | } |
---|
| 107 | }; |
---|
| 108 | return handles; |
---|
| 109 | } |
---|
| 110 | return addListener(target, type, listener, dontFix, matchesTarget); |
---|
| 111 | }; |
---|
| 112 | var touchEvents = /^touch/; |
---|
| 113 | function addListener(target, type, listener, dontFix, matchesTarget){ |
---|
| 114 | // event delegation: |
---|
| 115 | var selector = type.match(/(.*):(.*)/); |
---|
| 116 | // if we have a selector:event, the last one is interpreted as an event, and we use event delegation |
---|
| 117 | if(selector){ |
---|
| 118 | type = selector[2]; |
---|
| 119 | selector = selector[1]; |
---|
| 120 | // create the extension event for selectors and directly call it |
---|
| 121 | return on.selector(selector, type).call(matchesTarget, target, listener); |
---|
| 122 | } |
---|
| 123 | // test to see if it a touch event right now, so we don't have to do it every time it fires |
---|
| 124 | if(has("touch")){ |
---|
| 125 | if(touchEvents.test(type)){ |
---|
| 126 | // touch event, fix it |
---|
| 127 | listener = fixTouchListener(listener); |
---|
| 128 | } |
---|
| 129 | if(!has("event-orientationchange") && (type == "orientationchange")){ |
---|
| 130 | //"orientationchange" not supported <= Android 2.1, |
---|
| 131 | //but works through "resize" on window |
---|
| 132 | type = "resize"; |
---|
| 133 | target = window; |
---|
| 134 | listener = fixTouchListener(listener); |
---|
| 135 | } |
---|
| 136 | } |
---|
| 137 | if(addStopImmediate){ |
---|
| 138 | // add stopImmediatePropagation if it doesn't exist |
---|
| 139 | listener = addStopImmediate(listener); |
---|
| 140 | } |
---|
| 141 | // normal path, the target is |this| |
---|
| 142 | if(target.addEventListener){ |
---|
| 143 | // the target has addEventListener, which should be used if available (might or might not be a node, non-nodes can implement this method as well) |
---|
| 144 | // check for capture conversions |
---|
| 145 | var capture = type in captures, |
---|
| 146 | adjustedType = capture ? captures[type] : type; |
---|
| 147 | target.addEventListener(adjustedType, listener, capture); |
---|
| 148 | // create and return the signal |
---|
| 149 | return { |
---|
| 150 | remove: function(){ |
---|
| 151 | target.removeEventListener(adjustedType, listener, capture); |
---|
| 152 | } |
---|
| 153 | }; |
---|
| 154 | } |
---|
| 155 | type = "on" + type; |
---|
| 156 | if(fixAttach && target.attachEvent){ |
---|
| 157 | return fixAttach(target, type, listener); |
---|
| 158 | } |
---|
| 159 | throw new Error("Target must be an event emitter"); |
---|
| 160 | } |
---|
| 161 | |
---|
| 162 | on.selector = function(selector, eventType, children){ |
---|
| 163 | // summary: |
---|
| 164 | // Creates a new extension event with event delegation. This is based on |
---|
| 165 | // the provided event type (can be extension event) that |
---|
| 166 | // only calls the listener when the CSS selector matches the target of the event. |
---|
| 167 | // |
---|
| 168 | // The application must require() an appropriate level of dojo/query to handle the selector. |
---|
| 169 | // selector: |
---|
| 170 | // The CSS selector to use for filter events and determine the |this| of the event listener. |
---|
| 171 | // eventType: |
---|
| 172 | // The event to listen for |
---|
| 173 | // children: |
---|
| 174 | // Indicates if children elements of the selector should be allowed. This defaults to |
---|
| 175 | // true |
---|
| 176 | // example: |
---|
| 177 | // | require(["dojo/on", "dojo/mouse", "dojo/query!css2"], function(listen, mouse){ |
---|
| 178 | // | on(node, on.selector(".my-class", mouse.enter), handlerForMyHover); |
---|
| 179 | return function(target, listener){ |
---|
| 180 | // if the selector is function, use it to select the node, otherwise use the matches method |
---|
| 181 | var matchesTarget = typeof selector == "function" ? {matches: selector} : this, |
---|
| 182 | bubble = eventType.bubble; |
---|
| 183 | function select(eventTarget){ |
---|
| 184 | // see if we have a valid matchesTarget or default to dojo/query |
---|
| 185 | matchesTarget = matchesTarget && matchesTarget.matches ? matchesTarget : dojo.query; |
---|
| 186 | // there is a selector, so make sure it matches |
---|
| 187 | while(!matchesTarget.matches(eventTarget, selector, target)){ |
---|
| 188 | if(eventTarget == target || children === false || !(eventTarget = eventTarget.parentNode) || eventTarget.nodeType != 1){ // intentional assignment |
---|
| 189 | return; |
---|
| 190 | } |
---|
| 191 | } |
---|
| 192 | return eventTarget; |
---|
| 193 | } |
---|
| 194 | if(bubble){ |
---|
| 195 | // the event type doesn't naturally bubble, but has a bubbling form, use that, and give it the selector so it can perform the select itself |
---|
| 196 | return on(target, bubble(select), listener); |
---|
| 197 | } |
---|
| 198 | // standard event delegation |
---|
| 199 | return on(target, eventType, function(event){ |
---|
| 200 | // call select to see if we match |
---|
| 201 | var eventTarget = select(event.target); |
---|
| 202 | // if it matches we call the listener |
---|
| 203 | return eventTarget && listener.call(eventTarget, event); |
---|
| 204 | }); |
---|
| 205 | }; |
---|
| 206 | }; |
---|
| 207 | |
---|
| 208 | function syntheticPreventDefault(){ |
---|
| 209 | this.cancelable = false; |
---|
| 210 | this.defaultPrevented = true; |
---|
| 211 | } |
---|
| 212 | function syntheticStopPropagation(){ |
---|
| 213 | this.bubbles = false; |
---|
| 214 | } |
---|
| 215 | var slice = [].slice, |
---|
| 216 | syntheticDispatch = on.emit = function(target, type, event){ |
---|
| 217 | // summary: |
---|
| 218 | // Fires an event on the target object. |
---|
| 219 | // target: |
---|
| 220 | // The target object to fire the event on. This can be a DOM element or a plain |
---|
| 221 | // JS object. If the target is a DOM element, native event emitting mechanisms |
---|
| 222 | // are used when possible. |
---|
| 223 | // type: |
---|
| 224 | // The event type name. You can emulate standard native events like "click" and |
---|
| 225 | // "mouseover" or create custom events like "open" or "finish". |
---|
| 226 | // event: |
---|
| 227 | // An object that provides the properties for the event. See https://developer.mozilla.org/en/DOM/event.initEvent |
---|
| 228 | // for some of the properties. These properties are copied to the event object. |
---|
| 229 | // Of particular importance are the cancelable and bubbles properties. The |
---|
| 230 | // cancelable property indicates whether or not the event has a default action |
---|
| 231 | // that can be cancelled. The event is cancelled by calling preventDefault() on |
---|
| 232 | // the event object. The bubbles property indicates whether or not the |
---|
| 233 | // event will bubble up the DOM tree. If bubbles is true, the event will be called |
---|
| 234 | // on the target and then each parent successively until the top of the tree |
---|
| 235 | // is reached or stopPropagation() is called. Both bubbles and cancelable |
---|
| 236 | // default to false. |
---|
| 237 | // returns: |
---|
| 238 | // If the event is cancelable and the event is not cancelled, |
---|
| 239 | // emit will return true. If the event is cancelable and the event is cancelled, |
---|
| 240 | // emit will return false. |
---|
| 241 | // details: |
---|
| 242 | // Note that this is designed to emit events for listeners registered through |
---|
| 243 | // dojo/on. It should actually work with any event listener except those |
---|
| 244 | // added through IE's attachEvent (IE8 and below's non-W3C event emitting |
---|
| 245 | // doesn't support custom event types). It should work with all events registered |
---|
| 246 | // through dojo/on. Also note that the emit method does do any default |
---|
| 247 | // action, it only returns a value to indicate if the default action should take |
---|
| 248 | // place. For example, emitting a keypress event would not cause a character |
---|
| 249 | // to appear in a textbox. |
---|
| 250 | // example: |
---|
| 251 | // To fire our own click event |
---|
| 252 | // | require(["dojo/on", "dojo/dom" |
---|
| 253 | // | ], function(on, dom){ |
---|
| 254 | // | on.emit(dom.byId("button"), "click", { |
---|
| 255 | // | cancelable: true, |
---|
| 256 | // | bubbles: true, |
---|
| 257 | // | screenX: 33, |
---|
| 258 | // | screenY: 44 |
---|
| 259 | // | }); |
---|
| 260 | // We can also fire our own custom events: |
---|
| 261 | // | on.emit(dom.byId("slider"), "slide", { |
---|
| 262 | // | cancelable: true, |
---|
| 263 | // | bubbles: true, |
---|
| 264 | // | direction: "left-to-right" |
---|
| 265 | // | }); |
---|
| 266 | // | }); |
---|
| 267 | var args = slice.call(arguments, 2); |
---|
| 268 | var method = "on" + type; |
---|
| 269 | if("parentNode" in target){ |
---|
| 270 | // node (or node-like), create event controller methods |
---|
| 271 | var newEvent = args[0] = {}; |
---|
| 272 | for(var i in event){ |
---|
| 273 | newEvent[i] = event[i]; |
---|
| 274 | } |
---|
| 275 | newEvent.preventDefault = syntheticPreventDefault; |
---|
| 276 | newEvent.stopPropagation = syntheticStopPropagation; |
---|
| 277 | newEvent.target = target; |
---|
| 278 | newEvent.type = type; |
---|
| 279 | event = newEvent; |
---|
| 280 | } |
---|
| 281 | do{ |
---|
| 282 | // call any node which has a handler (note that ideally we would try/catch to simulate normal event propagation but that causes too much pain for debugging) |
---|
| 283 | target[method] && target[method].apply(target, args); |
---|
| 284 | // and then continue up the parent node chain if it is still bubbling (if started as bubbles and stopPropagation hasn't been called) |
---|
| 285 | }while(event && event.bubbles && (target = target.parentNode)); |
---|
| 286 | return event && event.cancelable && event; // if it is still true (was cancelable and was cancelled), return the event to indicate default action should happen |
---|
| 287 | }; |
---|
| 288 | var captures = has("event-focusin") ? {} : {focusin: "focus", focusout: "blur"}; |
---|
| 289 | if(!has("event-stopimmediatepropagation")){ |
---|
| 290 | var stopImmediatePropagation =function(){ |
---|
| 291 | this.immediatelyStopped = true; |
---|
| 292 | this.modified = true; // mark it as modified so the event will be cached in IE |
---|
| 293 | }; |
---|
| 294 | var addStopImmediate = function(listener){ |
---|
| 295 | return function(event){ |
---|
| 296 | if(!event.immediatelyStopped){// check to make sure it hasn't been stopped immediately |
---|
| 297 | event.stopImmediatePropagation = stopImmediatePropagation; |
---|
| 298 | return listener.apply(this, arguments); |
---|
| 299 | } |
---|
| 300 | }; |
---|
| 301 | } |
---|
| 302 | } |
---|
| 303 | if(has("dom-addeventlistener")){ |
---|
| 304 | // emitter that works with native event handling |
---|
| 305 | on.emit = function(target, type, event){ |
---|
| 306 | if(target.dispatchEvent && document.createEvent){ |
---|
| 307 | // use the native event emitting mechanism if it is available on the target object |
---|
| 308 | // create a generic event |
---|
| 309 | // we could create branch into the different types of event constructors, but |
---|
| 310 | // that would be a lot of extra code, with little benefit that I can see, seems |
---|
| 311 | // best to use the generic constructor and copy properties over, making it |
---|
| 312 | // easy to have events look like the ones created with specific initializers |
---|
| 313 | var ownerDocument = target.ownerDocument || document; |
---|
| 314 | var nativeEvent = ownerDocument.createEvent("HTMLEvents"); |
---|
| 315 | nativeEvent.initEvent(type, !!event.bubbles, !!event.cancelable); |
---|
| 316 | // and copy all our properties over |
---|
| 317 | for(var i in event){ |
---|
| 318 | if(!(i in nativeEvent)){ |
---|
| 319 | nativeEvent[i] = event[i]; |
---|
| 320 | } |
---|
| 321 | } |
---|
| 322 | return target.dispatchEvent(nativeEvent) && nativeEvent; |
---|
| 323 | } |
---|
| 324 | return syntheticDispatch.apply(on, arguments); // emit for a non-node |
---|
| 325 | }; |
---|
| 326 | }else{ |
---|
| 327 | // no addEventListener, basically old IE event normalization |
---|
| 328 | on._fixEvent = function(evt, sender){ |
---|
| 329 | // summary: |
---|
| 330 | // normalizes properties on the event object including event |
---|
| 331 | // bubbling methods, keystroke normalization, and x/y positions |
---|
| 332 | // evt: |
---|
| 333 | // native event object |
---|
| 334 | // sender: |
---|
| 335 | // node to treat as "currentTarget" |
---|
| 336 | if(!evt){ |
---|
| 337 | var w = sender && (sender.ownerDocument || sender.document || sender).parentWindow || window; |
---|
| 338 | evt = w.event; |
---|
| 339 | } |
---|
| 340 | if(!evt){return evt;} |
---|
| 341 | try{ |
---|
| 342 | if(lastEvent && evt.type == lastEvent.type && evt.srcElement == lastEvent.target){ |
---|
| 343 | // should be same event, reuse event object (so it can be augmented); |
---|
| 344 | // accessing evt.srcElement rather than evt.target since evt.target not set on IE until fixup below |
---|
| 345 | evt = lastEvent; |
---|
| 346 | } |
---|
| 347 | }catch(e){ |
---|
| 348 | // will occur on IE on lastEvent.type reference if lastEvent points to a previous event that already |
---|
| 349 | // finished bubbling, but the setTimeout() to clear lastEvent hasn't fired yet |
---|
| 350 | } |
---|
| 351 | if(!evt.target){ // check to see if it has been fixed yet |
---|
| 352 | evt.target = evt.srcElement; |
---|
| 353 | evt.currentTarget = (sender || evt.srcElement); |
---|
| 354 | if(evt.type == "mouseover"){ |
---|
| 355 | evt.relatedTarget = evt.fromElement; |
---|
| 356 | } |
---|
| 357 | if(evt.type == "mouseout"){ |
---|
| 358 | evt.relatedTarget = evt.toElement; |
---|
| 359 | } |
---|
| 360 | if(!evt.stopPropagation){ |
---|
| 361 | evt.stopPropagation = stopPropagation; |
---|
| 362 | evt.preventDefault = preventDefault; |
---|
| 363 | } |
---|
| 364 | switch(evt.type){ |
---|
| 365 | case "keypress": |
---|
| 366 | var c = ("charCode" in evt ? evt.charCode : evt.keyCode); |
---|
| 367 | if (c==10){ |
---|
| 368 | // CTRL-ENTER is CTRL-ASCII(10) on IE, but CTRL-ENTER on Mozilla |
---|
| 369 | c=0; |
---|
| 370 | evt.keyCode = 13; |
---|
| 371 | }else if(c==13||c==27){ |
---|
| 372 | c=0; // Mozilla considers ENTER and ESC non-printable |
---|
| 373 | }else if(c==3){ |
---|
| 374 | c=99; // Mozilla maps CTRL-BREAK to CTRL-c |
---|
| 375 | } |
---|
| 376 | // Mozilla sets keyCode to 0 when there is a charCode |
---|
| 377 | // but that stops the event on IE. |
---|
| 378 | evt.charCode = c; |
---|
| 379 | _setKeyChar(evt); |
---|
| 380 | break; |
---|
| 381 | } |
---|
| 382 | } |
---|
| 383 | return evt; |
---|
| 384 | }; |
---|
| 385 | var lastEvent, IESignal = function(handle){ |
---|
| 386 | this.handle = handle; |
---|
| 387 | }; |
---|
| 388 | IESignal.prototype.remove = function(){ |
---|
| 389 | delete _dojoIEListeners_[this.handle]; |
---|
| 390 | }; |
---|
| 391 | var fixListener = function(listener){ |
---|
| 392 | // this is a minimal function for closing on the previous listener with as few as variables as possible |
---|
| 393 | return function(evt){ |
---|
| 394 | evt = on._fixEvent(evt, this); |
---|
| 395 | var result = listener.call(this, evt); |
---|
| 396 | if(evt.modified){ |
---|
| 397 | // cache the last event and reuse it if we can |
---|
| 398 | if(!lastEvent){ |
---|
| 399 | setTimeout(function(){ |
---|
| 400 | lastEvent = null; |
---|
| 401 | }); |
---|
| 402 | } |
---|
| 403 | lastEvent = evt; |
---|
| 404 | } |
---|
| 405 | return result; |
---|
| 406 | }; |
---|
| 407 | }; |
---|
| 408 | var fixAttach = function(target, type, listener){ |
---|
| 409 | listener = fixListener(listener); |
---|
| 410 | if(((target.ownerDocument ? target.ownerDocument.parentWindow : target.parentWindow || target.window || window) != top || |
---|
| 411 | has("jscript") < 5.8) && |
---|
| 412 | !has("config-_allow_leaks")){ |
---|
| 413 | // IE will leak memory on certain handlers in frames (IE8 and earlier) and in unattached DOM nodes for JScript 5.7 and below. |
---|
| 414 | // Here we use global redirection to solve the memory leaks |
---|
| 415 | if(typeof _dojoIEListeners_ == "undefined"){ |
---|
| 416 | _dojoIEListeners_ = []; |
---|
| 417 | } |
---|
| 418 | var emitter = target[type]; |
---|
| 419 | if(!emitter || !emitter.listeners){ |
---|
| 420 | var oldListener = emitter; |
---|
| 421 | emitter = Function('event', 'var callee = arguments.callee; for(var i = 0; i<callee.listeners.length; i++){var listener = _dojoIEListeners_[callee.listeners[i]]; if(listener){listener.call(this,event);}}'); |
---|
| 422 | emitter.listeners = []; |
---|
| 423 | target[type] = emitter; |
---|
| 424 | emitter.global = this; |
---|
| 425 | if(oldListener){ |
---|
| 426 | emitter.listeners.push(_dojoIEListeners_.push(oldListener) - 1); |
---|
| 427 | } |
---|
| 428 | } |
---|
| 429 | var handle; |
---|
| 430 | emitter.listeners.push(handle = (emitter.global._dojoIEListeners_.push(listener) - 1)); |
---|
| 431 | return new IESignal(handle); |
---|
| 432 | } |
---|
| 433 | return aspect.after(target, type, listener, true); |
---|
| 434 | }; |
---|
| 435 | |
---|
| 436 | var _setKeyChar = function(evt){ |
---|
| 437 | evt.keyChar = evt.charCode ? String.fromCharCode(evt.charCode) : ''; |
---|
| 438 | evt.charOrCode = evt.keyChar || evt.keyCode; |
---|
| 439 | }; |
---|
| 440 | // Called in Event scope |
---|
| 441 | var stopPropagation = function(){ |
---|
| 442 | this.cancelBubble = true; |
---|
| 443 | }; |
---|
| 444 | var preventDefault = on._preventDefault = function(){ |
---|
| 445 | // Setting keyCode to 0 is the only way to prevent certain keypresses (namely |
---|
| 446 | // ctrl-combinations that correspond to menu accelerator keys). |
---|
| 447 | // Otoh, it prevents upstream listeners from getting this information |
---|
| 448 | // Try to split the difference here by clobbering keyCode only for ctrl |
---|
| 449 | // combinations. If you still need to access the key upstream, bubbledKeyCode is |
---|
| 450 | // provided as a workaround. |
---|
| 451 | this.bubbledKeyCode = this.keyCode; |
---|
| 452 | if(this.ctrlKey){ |
---|
| 453 | try{ |
---|
| 454 | // squelch errors when keyCode is read-only |
---|
| 455 | // (e.g. if keyCode is ctrl or shift) |
---|
| 456 | this.keyCode = 0; |
---|
| 457 | }catch(e){ |
---|
| 458 | } |
---|
| 459 | } |
---|
| 460 | this.defaultPrevented = true; |
---|
| 461 | this.returnValue = false; |
---|
| 462 | this.modified = true; // mark it as modified (for defaultPrevented flag) so the event will be cached in IE |
---|
| 463 | }; |
---|
| 464 | } |
---|
| 465 | if(has("touch")){ |
---|
| 466 | var Event = function(){}; |
---|
| 467 | var windowOrientation = window.orientation; |
---|
| 468 | var fixTouchListener = function(listener){ |
---|
| 469 | return function(originalEvent){ |
---|
| 470 | //Event normalization(for ontouchxxx and resize): |
---|
| 471 | //1.incorrect e.pageX|pageY in iOS |
---|
| 472 | //2.there are no "e.rotation", "e.scale" and "onorientationchange" in Android |
---|
| 473 | //3.More TBD e.g. force | screenX | screenX | clientX | clientY | radiusX | radiusY |
---|
| 474 | |
---|
| 475 | // see if it has already been corrected |
---|
| 476 | var event = originalEvent.corrected; |
---|
| 477 | if(!event){ |
---|
| 478 | var type = originalEvent.type; |
---|
| 479 | try{ |
---|
| 480 | delete originalEvent.type; // on some JS engines (android), deleting properties make them mutable |
---|
| 481 | }catch(e){} |
---|
| 482 | if(originalEvent.type){ |
---|
| 483 | // deleting properties doesn't work (older iOS), have to use delegation |
---|
| 484 | if(has('mozilla')){ |
---|
| 485 | // Firefox doesn't like delegated properties, so we have to copy |
---|
| 486 | var event = {}; |
---|
| 487 | for(var name in originalEvent){ |
---|
| 488 | event[name] = originalEvent[name]; |
---|
| 489 | } |
---|
| 490 | }else{ |
---|
| 491 | // old iOS branch |
---|
| 492 | Event.prototype = originalEvent; |
---|
| 493 | var event = new Event; |
---|
| 494 | } |
---|
| 495 | // have to delegate methods to make them work |
---|
| 496 | event.preventDefault = function(){ |
---|
| 497 | originalEvent.preventDefault(); |
---|
| 498 | }; |
---|
| 499 | event.stopPropagation = function(){ |
---|
| 500 | originalEvent.stopPropagation(); |
---|
| 501 | }; |
---|
| 502 | }else{ |
---|
| 503 | // deletion worked, use property as is |
---|
| 504 | event = originalEvent; |
---|
| 505 | event.type = type; |
---|
| 506 | } |
---|
| 507 | originalEvent.corrected = event; |
---|
| 508 | if(type == 'resize'){ |
---|
| 509 | if(windowOrientation == window.orientation){ |
---|
| 510 | return null;//double tap causes an unexpected 'resize' in Android |
---|
| 511 | } |
---|
| 512 | windowOrientation = window.orientation; |
---|
| 513 | event.type = "orientationchange"; |
---|
| 514 | return listener.call(this, event); |
---|
| 515 | } |
---|
| 516 | // We use the original event and augment, rather than doing an expensive mixin operation |
---|
| 517 | if(!("rotation" in event)){ // test to see if it has rotation |
---|
| 518 | event.rotation = 0; |
---|
| 519 | event.scale = 1; |
---|
| 520 | } |
---|
| 521 | //use event.changedTouches[0].pageX|pageY|screenX|screenY|clientX|clientY|target |
---|
| 522 | var firstChangeTouch = event.changedTouches[0]; |
---|
| 523 | for(var i in firstChangeTouch){ // use for-in, we don't need to have dependency on dojo/_base/lang here |
---|
| 524 | delete event[i]; // delete it first to make it mutable |
---|
| 525 | event[i] = firstChangeTouch[i]; |
---|
| 526 | } |
---|
| 527 | } |
---|
| 528 | return listener.call(this, event); |
---|
| 529 | }; |
---|
| 530 | }; |
---|
| 531 | } |
---|
| 532 | return on; |
---|
| 533 | }); |
---|