]>
git.wh0rd.org - tt-rss.git/blob - lib/dojo/on.js.uncompressed.js
1 define ( "dojo/on" , [ "./has!dom-addeventlistener?:./aspect" , "./_base/kernel" , "./has" ], function ( aspect
, dojo
, has
){
4 if ( 1 ){ // 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
);
10 var on = function ( target
, type
, listener
, dontFix
){
12 // A function that provides core event listening functionality. With this function
13 // you can provide a target, event type, and listener to be notified of
14 // future matching events that are fired.
15 // target: Element|Object
16 // This is the target object or DOM element that to receive events from
17 // type: String|Function
18 // This is the name of the event to listen for or an extension event type.
20 // This is the function that should be called when the event fires.
22 // An object with a remove() method that can be used to stop listening for this
25 // To listen for "click" events on a button node, we can do:
26 // | define(["dojo/on"], function(listen){
27 // | on(button, "click", clickHandler);
29 // Evented JavaScript objects can also have their own events.
30 // | var obj = new Evented;
31 // | on(obj, "foo", fooHandler);
32 // And then we could publish a "foo" event:
33 // | on.emit(obj, "foo", {key: "value"});
34 // We can use extension events as well. For example, you could listen for a tap gesture:
35 // | define(["dojo/on", "dojo/gesture/tap", function(listen, tap){
36 // | on(button, tap, tapHandler);
38 // which would trigger fooHandler. Note that for a simple object this is equivalent to calling:
39 // | obj.onfoo({key:"value"});
40 // If you use on.emit on a DOM node, it will use native event dispatching when possible.
42 if ( typeof target
. on
== "function" && typeof type
!= "function" ){
43 // delegate to the target's on() method, so it can handle it's own listening if it wants
44 return target
. on ( type
, listener
);
46 // delegate to main listener code
47 return on
. parse ( target
, type
, listener
, addListener
, dontFix
, this );
49 on
. pausable = function ( target
, type
, listener
, dontFix
){
51 // This function acts the same as on(), but with pausable functionality. The
52 // returned signal object has pause() and resume() functions. Calling the
53 // pause() method will cause the listener to not be called for future events. Calling the
54 // resume() method will cause the listener to again be called for future events.
56 var signal
= on ( target
, type
, function (){
58 return listener
. apply ( this , arguments
);
61 signal
. pause = function (){
64 signal
. resume = function (){
69 on
. once = function ( target
, type
, listener
, dontFix
){
71 // This function acts the same as on(), but will only call the listener once. The
72 // listener will be called for the first
73 // event that takes place and then listener will automatically be removed.
74 var signal
= on ( target
, type
, function (){
75 // remove this listener
77 // proceed to call the listener
78 return listener
. apply ( this , arguments
);
82 on
. parse = function ( target
, type
, listener
, addListener
, dontFix
, matchesTarget
){
84 // event handler function
85 // on(node, touch.press, touchListener);
86 return type
. call ( matchesTarget
, target
, listener
);
89 if ( type
. indexOf ( "," ) > - 1 ){
90 // we allow comma delimited event names, so you can register for multiple events at once
91 var events
= type
. split ( /\s*,\s*/ );
95 while ( eventName
= events
[ i
++]){
96 handles
. push ( addListener ( target
, eventName
, listener
, dontFix
, matchesTarget
));
98 handles
. remove = function (){
99 for ( var i
= 0 ; i
< handles
. length
; i
++){
105 return addListener ( target
, type
, listener
, dontFix
, matchesTarget
);
107 var touchEvents
= /^touch/ ;
108 function addListener ( target
, type
, listener
, dontFix
, matchesTarget
){
110 var selector
= type
. match ( /(.*):(.*)/ );
111 // if we have a selector:event, the last one is interpreted as an event, and we use event delegation
114 selector
= selector
[ 1 ];
115 // create the extension event for selectors and directly call it
116 return on
. selector ( selector
, type
). call ( matchesTarget
, target
, listener
);
118 // test to see if it a touch event right now, so we don't have to do it every time it fires
120 if ( touchEvents
. test ( type
)){
121 // touch event, fix it
122 listener
= fixTouchListener ( listener
);
124 if (! has ( "event-orientationchange" ) && ( type
== "orientationchange" )){
125 //"orientationchange" not supported <= Android 2.1,
126 //but works through "resize" on window
129 listener
= fixTouchListener ( listener
);
132 if ( addStopImmediate
){
133 // add stopImmediatePropagation if it doesn't exist
134 listener
= addStopImmediate ( listener
);
136 // normal path, the target is |this|
137 if ( target
. addEventListener
){
138 // 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)
139 // check for capture conversions
140 var capture
= type
in captures
,
141 adjustedType
= capture
? captures
[ type
] : type
;
142 target
. addEventListener ( adjustedType
, listener
, capture
);
143 // create and return the signal
146 target
. removeEventListener ( adjustedType
, listener
, capture
);
151 if ( fixAttach
&& target
. attachEvent
){
152 return fixAttach ( target
, type
, listener
);
154 throw new Error ( "Target must be an event emitter" );
157 on
. selector = function ( selector
, eventType
, children
){
159 // Creates a new extension event with event delegation. This is based on
160 // the provided event type (can be extension event) that
161 // only calls the listener when the CSS selector matches the target of the event.
163 // The application must require() an appropriate level of dojo/query to handle the selector.
165 // The CSS selector to use for filter events and determine the |this| of the event listener.
167 // The event to listen for
169 // Indicates if children elements of the selector should be allowed. This defaults to
172 // | require(["dojo/on", "dojo/mouse", "dojo/query!css2"], function(listen, mouse){
173 // | on(node, on.selector(".my-class", mouse.enter), handlerForMyHover);
174 return function ( target
, listener
){
175 // if the selector is function, use it to select the node, otherwise use the matches method
176 var matchesTarget
= typeof selector
== "function" ? { matches
: selector
} : this ,
177 bubble
= eventType
. bubble
;
178 function select ( eventTarget
){
179 // see if we have a valid matchesTarget or default to dojo.query
180 matchesTarget
= matchesTarget
&& matchesTarget
. matches
? matchesTarget
: dojo
. query
;
181 // there is a selector, so make sure it matches
182 while (! matchesTarget
. matches ( eventTarget
, selector
, target
)){
183 if ( eventTarget
== target
|| children
=== false || !( eventTarget
= eventTarget
. parentNode
) || eventTarget
. nodeType
!= 1 ){ // intentional assignment
190 // 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
191 return on ( target
, bubble ( select
), listener
);
193 // standard event delegation
194 return on ( target
, eventType
, function ( event
){
195 // call select to see if we match
196 var eventTarget
= select ( event
. target
);
197 // if it matches we call the listener
198 return eventTarget
&& listener
. call ( eventTarget
, event
);
203 function syntheticPreventDefault (){
204 this . cancelable
= false ;
206 function syntheticStopPropagation (){
207 this . bubbles
= false ;
209 var slice
= []. slice
,
210 syntheticDispatch
= on
. emit = function ( target
, type
, event
){
212 // Fires an event on the target object.
214 // The target object to fire the event on. This can be a DOM element or a plain
215 // JS object. If the target is a DOM element, native event emiting mechanisms
216 // are used when possible.
218 // The event type name. You can emulate standard native events like "click" and
219 // "mouseover" or create custom events like "open" or "finish".
221 // An object that provides the properties for the event. See https://developer.mozilla.org/en/DOM/event.initEvent
222 // for some of the properties. These properties are copied to the event object.
223 // Of particular importance are the cancelable and bubbles properties. The
224 // cancelable property indicates whether or not the event has a default action
225 // that can be cancelled. The event is cancelled by calling preventDefault() on
226 // the event object. The bubbles property indicates whether or not the
227 // event will bubble up the DOM tree. If bubbles is true, the event will be called
228 // on the target and then each parent successively until the top of the tree
229 // is reached or stopPropagation() is called. Both bubbles and cancelable
232 // If the event is cancelable and the event is not cancelled,
233 // emit will return true. If the event is cancelable and the event is cancelled,
234 // emit will return false.
236 // Note that this is designed to emit events for listeners registered through
237 // dojo/on. It should actually work with any event listener except those
238 // added through IE's attachEvent (IE8 and below's non-W3C event emiting
239 // doesn't support custom event types). It should work with all events registered
240 // through dojo/on. Also note that the emit method does do any default
241 // action, it only returns a value to indicate if the default action should take
242 // place. For example, emiting a keypress event would not cause a character
243 // to appear in a textbox.
245 // To fire our own click event
246 // | on.emit(dojo.byId("button"), "click", {
247 // | cancelable: true,
252 // We can also fire our own custom events:
253 // | on.emit(dojo.byId("slider"), "slide", {
254 // | cancelable: true,
256 // | direction: "left-to-right"
258 var args
= slice
. call ( arguments
, 2 );
259 var method
= "on" + type
;
260 if ( "parentNode" in target
){
261 // node (or node-like), create event controller methods
262 var newEvent
= args
[ 0 ] = {};
264 newEvent
[ i
] = event
[ i
];
266 newEvent
. preventDefault
= syntheticPreventDefault
;
267 newEvent
. stopPropagation
= syntheticStopPropagation
;
268 newEvent
. target
= target
;
269 newEvent
. type
= type
;
273 // 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)
274 target
[ method
] && target
[ method
]. apply ( target
, args
);
275 // and then continue up the parent node chain if it is still bubbling (if started as bubbles and stopPropagation hasn't been called)
276 } while ( event
&& event
. bubbles
&& ( target
= target
. parentNode
));
277 return event
&& event
. cancelable
&& event
; // if it is still true (was cancelable and was cancelled), return the event to indicate default action should happen
280 if (! has ( "event-stopimmediatepropagation" )){
281 var stopImmediatePropagation = function (){
282 this . immediatelyStopped
= true ;
283 this . modified
= true ; // mark it as modified so the event will be cached in IE
285 var addStopImmediate = function ( listener
){
286 return function ( event
){
287 if (! event
. immediatelyStopped
){ // check to make sure it hasn't been stopped immediately
288 event
. stopImmediatePropagation
= stopImmediatePropagation
;
289 return listener
. apply ( this , arguments
);
294 if ( has ( "dom-addeventlistener" )){
295 // normalize focusin and focusout
301 // emiter that works with native event handling
302 on
. emit = function ( target
, type
, event
){
303 if ( target
. dispatchEvent
&& document
. createEvent
){
304 // use the native event emiting mechanism if it is available on the target object
305 // create a generic event
306 // we could create branch into the different types of event constructors, but
307 // that would be a lot of extra code, with little benefit that I can see, seems
308 // best to use the generic constructor and copy properties over, making it
309 // easy to have events look like the ones created with specific initializers
310 var nativeEvent
= target
. ownerDocument
. createEvent ( "HTMLEvents" );
311 nativeEvent
. initEvent ( type
, !! event
. bubbles
, !! event
. cancelable
);
312 // and copy all our properties over
314 var value
= event
[ i
];
315 if (!( i
in nativeEvent
)){
316 nativeEvent
[ i
] = event
[ i
];
319 return target
. dispatchEvent ( nativeEvent
) && nativeEvent
;
321 return syntheticDispatch
. apply ( on
, arguments
); // emit for a non-node
324 // no addEventListener, basically old IE event normalization
325 on
. _fixEvent = function ( evt
, sender
){
327 // normalizes properties on the event object including event
328 // bubbling methods, keystroke normalization, and x/y positions
330 // native event object
332 // node to treat as "currentTarget"
334 var w
= sender
&& ( sender
. ownerDocument
|| sender
. document
|| sender
). parentWindow
|| window
;
337 if (! evt
){ return evt
;}
338 if ( lastEvent
&& evt
. type
== lastEvent
. type
){
339 // should be same event, reuse event object (so it can be augmented)
342 if (! evt
. target
){ // check to see if it has been fixed yet
343 evt
. target
= evt
. srcElement
;
344 evt
. currentTarget
= ( sender
|| evt
. srcElement
);
345 if ( evt
. type
== "mouseover" ){
346 evt
. relatedTarget
= evt
. fromElement
;
348 if ( evt
. type
== "mouseout" ){
349 evt
. relatedTarget
= evt
. toElement
;
351 if (! evt
. stopPropagation
){
352 evt
. stopPropagation
= stopPropagation
;
353 evt
. preventDefault
= preventDefault
;
357 var c
= ( "charCode" in evt
? evt
. charCode
: evt
. keyCode
);
359 // CTRL-ENTER is CTRL-ASCII(10) on IE, but CTRL-ENTER on Mozilla
362 } else if ( c
== 13 || c
== 27 ){
363 c
= 0 ; // Mozilla considers ENTER and ESC non-printable
365 c
= 99 ; // Mozilla maps CTRL-BREAK to CTRL-c
367 // Mozilla sets keyCode to 0 when there is a charCode
368 // but that stops the event on IE.
376 var lastEvent
, IESignal = function ( handle
){
377 this . handle
= handle
;
379 IESignal
. prototype . remove = function (){
380 delete _dojoIEListeners_
[ this . handle
];
382 var fixListener = function ( listener
){
383 // this is a minimal function for closing on the previous listener with as few as variables as possible
384 return function ( evt
){
385 evt
= on
. _fixEvent ( evt
, this );
386 var result
= listener
. call ( this , evt
);
388 // cache the last event and reuse it if we can
390 setTimeout ( function (){
399 var fixAttach = function ( target
, type
, listener
){
400 listener
= fixListener ( listener
);
401 if ((( target
. ownerDocument
? target
. ownerDocument
. parentWindow
: target
. parentWindow
|| target
. window
|| window
) != top
||
402 has ( "jscript" ) < 5.8 ) &&
403 ! has ( "config-_allow_leaks" )){
404 // IE will leak memory on certain handlers in frames (IE8 and earlier) and in unattached DOM nodes for JScript 5.7 and below.
405 // Here we use global redirection to solve the memory leaks
406 if ( typeof _dojoIEListeners_
== "undefined" ){
407 _dojoIEListeners_
= [];
409 var emiter
= target
[ type
];
410 if (! emiter
|| ! emiter
. listeners
){
411 var oldListener
= emiter
;
412 emiter
= 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);}}' );
413 emiter
. listeners
= [];
414 target
[ type
] = emiter
;
415 emiter
. global
= this ;
417 emiter
. listeners
. push ( _dojoIEListeners_
. push ( oldListener
) - 1 );
421 emiter
. listeners
. push ( handle
= ( emiter
. global
. _dojoIEListeners_
. push ( listener
) - 1 ));
422 return new IESignal ( handle
);
424 return aspect
. after ( target
, type
, listener
, true );
427 var _setKeyChar = function ( evt
){
428 evt
. keyChar
= evt
. charCode
? String
. fromCharCode ( evt
. charCode
) : '' ;
429 evt
. charOrCode
= evt
. keyChar
|| evt
. keyCode
;
431 // Called in Event scope
432 var stopPropagation = function (){
433 this . cancelBubble
= true ;
435 var preventDefault
= on
. _preventDefault = function (){
436 // Setting keyCode to 0 is the only way to prevent certain keypresses (namely
437 // ctrl-combinations that correspond to menu accelerator keys).
438 // Otoh, it prevents upstream listeners from getting this information
439 // Try to split the difference here by clobbering keyCode only for ctrl
440 // combinations. If you still need to access the key upstream, bubbledKeyCode is
441 // provided as a workaround.
442 this . bubbledKeyCode
= this . keyCode
;
445 // squelch errors when keyCode is read-only
446 // (e.g. if keyCode is ctrl or shift)
451 this . defaultPrevented
= true ;
452 this . returnValue
= false ;
456 var Event = function (){};
457 var windowOrientation
= window
. orientation
;
458 var fixTouchListener = function ( listener
){
459 return function ( originalEvent
){
460 //Event normalization(for ontouchxxx and resize):
461 //1.incorrect e.pageX|pageY in iOS
462 //2.there are no "e.rotation", "e.scale" and "onorientationchange" in Andriod
463 //3.More TBD e.g. force | screenX | screenX | clientX | clientY | radiusX | radiusY
465 // see if it has already been corrected
466 var event
= originalEvent
. corrected
;
468 var type
= originalEvent
. type
;
470 delete originalEvent
. type
; // on some JS engines (android), deleting properties make them mutable
472 if ( originalEvent
. type
){
473 // deleting properties doesn't work (older iOS), have to use delegation
474 Event
. prototype = originalEvent
;
475 var event
= new Event
;
476 // have to delegate methods to make them work
477 event
. preventDefault = function (){
478 originalEvent
. preventDefault ();
480 event
. stopPropagation = function (){
481 originalEvent
. stopPropagation ();
484 // deletion worked, use property as is
485 event
= originalEvent
;
488 originalEvent
. corrected
= event
;
489 if ( type
== 'resize' ){
490 if ( windowOrientation
== window
. orientation
){
491 return null ; //double tap causes an unexpected 'resize' in Andriod
493 windowOrientation
= window
. orientation
;
494 event
. type
= "orientationchange" ;
495 return listener
. call ( this , event
);
497 // We use the original event and augment, rather than doing an expensive mixin operation
498 if (!( "rotation" in event
)){ // test to see if it has rotation
502 //use event.changedTouches[0].pageX|pageY|screenX|screenY|clientX|clientY|target
503 var firstChangeTouch
= event
. changedTouches
[ 0 ];
504 for ( var i
in firstChangeTouch
){ // use for-in, we don't need to have dependency on dojo/_base/lang here
505 delete event
[ i
]; // delete it first to make it mutable
506 event
[ i
] = firstChangeTouch
[ i
];
509 return listener
. call ( this , event
);