]> git.wh0rd.org Git - tt-rss.git/blob - lib/dijit/a11yclick.js.uncompressed.js
ed92308dd074e48d0ebe5608bf979fccf7ad059b
[tt-rss.git] / lib / dijit / a11yclick.js.uncompressed.js
1 define("dijit/a11yclick", [
2         "dojo/on",
3         "dojo/_base/array", // array.forEach
4         "dojo/keys", // keys.ENTER keys.SPACE
5         "dojo/_base/declare", // declare
6         "dojo/has", // has("dom-addeventlistener")
7         "dojo/_base/unload", // unload.addOnWindowUnload
8         "dojo/_base/window" // win.doc.addEventListener win.doc.attachEvent win.doc.detachEvent
9 ], function(on, array, keys, declare, has, unload, win){
10
11         // module:
12         //              dijit/a11yclick
13
14         // Keep track of where the last keydown event was, to help avoid generating
15         // spurious ondijitclick events when:
16         // 1. focus is on a <button> or <a>
17         // 2. user presses then releases the ENTER key
18         // 3. onclick handler fires and shifts focus to another node, with an ondijitclick handler
19         // 4. onkeyup event fires, causing the ondijitclick handler to fire
20         var lastKeyDownNode = null;
21         if(has("dom-addeventlistener")){
22                 win.doc.addEventListener('keydown', function(evt){
23                         lastKeyDownNode = evt.target;
24                 }, true);
25         }else{
26                 // Fallback path for IE6-8
27                 (function(){
28                         var keydownCallback = function(evt){
29                                 lastKeyDownNode = evt.srcElement;
30                         };
31                         win.doc.attachEvent('onkeydown', keydownCallback);
32                         unload.addOnWindowUnload(function(){
33                                 win.doc.detachEvent('onkeydown', keydownCallback);
34                         });
35                 })();
36         }
37
38         function clickKey(/*Event*/ e){
39                 return (e.keyCode === keys.ENTER || e.keyCode === keys.SPACE) &&
40                         !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey;
41         }
42
43         return function(node, listener){
44                 // summary:
45                 //              Custom a11yclick (a.k.a. ondijitclick) event
46                 //              which triggers on a mouse click, touch, or space/enter keyup.
47
48                 if(/input|button/i.test(node.nodeName)){
49                         // pass through, the browser already generates click event on SPACE/ENTER key
50                         return on(node, "click", listener);
51                 }else{
52                         // Don't fire the click event unless both the keydown and keyup occur on this node.
53                         // Avoids problems where focus shifted to this node or away from the node on keydown,
54                         // either causing this node to process a stray keyup event, or causing another node
55                         // to get a stray keyup event.
56
57                         var handles = [
58                                 on(node, "keydown", function(e){
59                                         //console.log(this.id + ": onkeydown, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
60                                         if(clickKey(e)){
61                                                 // needed on IE for when focus changes between keydown and keyup - otherwise dropdown menus do not work
62                                                 lastKeyDownNode = e.target;
63
64                                                 // Prevent viewport scrolling on space key in IE<9.
65                                                 // (Reproducible on test_Button.html on any of the first dijit/form/Button examples)
66                                                 e.preventDefault();
67                                         }
68                                 }),
69
70                                 on(node, "keyup", function(e){
71                                         //console.log(this.id + ": onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
72                                         if(clickKey(e) && e.target == lastKeyDownNode){ // === breaks greasemonkey
73                                                 //need reset here or have problems in FF when focus returns to trigger element after closing popup/alert
74                                                 lastKeyDownNode = null;
75                                                 on.emit(e.target, "click", {
76                                                         cancelable: true,
77                                                         bubbles: true
78                                                 });
79                                         }
80                                 }),
81
82                                 on(node, "click", function(e){
83                                         // catch mouse clicks, plus the on.emit() calls from above and below
84                                         listener.call(this, e);
85                                 })
86                         ];
87
88                         if(has("touch")){
89                                 // touchstart-->touchend will automatically generate a click event, but there are problems
90                                 // on iOS after focus has been programatically shifted (#14604, #14918), so setup a failsafe
91                                 // if click doesn't fire naturally.
92
93                                 var clickTimer;
94                                 handles.push(
95                                         on(node, "touchend", function(e){
96                                                 var target = e.target;
97                                                 clickTimer = setTimeout(function(){
98                                                         clickTimer = null;
99                                                         on.emit(target, "click", {
100                                                                 cancelable: true,
101                                                                 bubbles: true
102                                                         });
103                                                 }, 600);
104                                         }),
105                                         on(node, "click", function(e){
106                                                 // If browser generates a click naturally, clear the timer to fire a synthetic click event
107                                                 if(clickTimer){
108                                                         clearTimeout(clickTimer);
109                                                 }
110                                         })
111                                         // TODO: if the touchstart and touchend were <100ms apart, and then there's another touchstart
112                                         // event <300ms after the touchend event, then clear the synthetic click timer, because user
113                                         // is doing a zoom.   Alternately monitor screen.deviceXDPI (or something similar) to see if
114                                         // zoom level has changed.
115                                 );
116                         }
117
118                         return {
119                                 remove: function(){
120                                         array.forEach(handles, function(h){ h.remove(); });
121                                         if(clickTimer){
122                                                 clearTimeout(clickTimer);
123                                                 clickTimer = null;
124                                         }
125                                 }
126                         };
127                 }
128         };
129
130         return ret;
131 });