1 | define([ |
---|
2 | "dojo/on", |
---|
3 | "dojo/_base/array", // array.forEach |
---|
4 | "dojo/keys", // keys.ENTER keys.SPACE |
---|
5 | "dojo/_base/declare", // declare |
---|
6 | "dojo/_base/sniff", // has("ie") |
---|
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/_OnDijitClickMixin |
---|
13 | // summary: |
---|
14 | // Mixin so you can pass "ondijitclick" to this.connect() method, |
---|
15 | // as a way to handle clicks by mouse, or by keyboard (SPACE/ENTER key) |
---|
16 | |
---|
17 | |
---|
18 | // Keep track of where the last keydown event was, to help avoid generating |
---|
19 | // spurious ondijitclick events when: |
---|
20 | // 1. focus is on a <button> or <a> |
---|
21 | // 2. user presses then releases the ENTER key |
---|
22 | // 3. onclick handler fires and shifts focus to another node, with an ondijitclick handler |
---|
23 | // 4. onkeyup event fires, causing the ondijitclick handler to fire |
---|
24 | var lastKeyDownNode = null; |
---|
25 | if(has("ie")){ |
---|
26 | (function(){ |
---|
27 | var keydownCallback = function(evt){ |
---|
28 | lastKeyDownNode = evt.srcElement; |
---|
29 | }; |
---|
30 | win.doc.attachEvent('onkeydown', keydownCallback); |
---|
31 | unload.addOnWindowUnload(function(){ |
---|
32 | win.doc.detachEvent('onkeydown', keydownCallback); |
---|
33 | }); |
---|
34 | })(); |
---|
35 | }else{ |
---|
36 | win.doc.addEventListener('keydown', function(evt){ |
---|
37 | lastKeyDownNode = evt.target; |
---|
38 | }, true); |
---|
39 | } |
---|
40 | |
---|
41 | // Custom a11yclick (a.k.a. ondijitclick) event |
---|
42 | var a11yclick = function(node, listener){ |
---|
43 | if(/input|button/i.test(node.nodeName)){ |
---|
44 | // pass through, the browser already generates click event on SPACE/ENTER key |
---|
45 | return on(node, "click", listener); |
---|
46 | }else{ |
---|
47 | // Don't fire the click event unless both the keydown and keyup occur on this node. |
---|
48 | // Avoids problems where focus shifted to this node or away from the node on keydown, |
---|
49 | // either causing this node to process a stray keyup event, or causing another node |
---|
50 | // to get a stray keyup event. |
---|
51 | |
---|
52 | function clickKey(/*Event*/ e){ |
---|
53 | return (e.keyCode == keys.ENTER || e.keyCode == keys.SPACE) && |
---|
54 | !e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey; |
---|
55 | } |
---|
56 | var handles = [ |
---|
57 | on(node, "keypress", function(e){ |
---|
58 | //console.log(this.id + ": onkeydown, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode)); |
---|
59 | if(clickKey(e)){ |
---|
60 | // needed on IE for when focus changes between keydown and keyup - otherwise dropdown menus do not work |
---|
61 | lastKeyDownNode = e.target; |
---|
62 | |
---|
63 | // Prevent viewport scrolling on space key in IE<9. |
---|
64 | // (Reproducible on test_Button.html on any of the first dijit.form.Button examples) |
---|
65 | // Do this onkeypress rather than onkeydown because onkeydown.preventDefault() will |
---|
66 | // suppress the onkeypress event, breaking _HasDropDown |
---|
67 | e.preventDefault(); |
---|
68 | } |
---|
69 | }), |
---|
70 | |
---|
71 | on(node, "keyup", function(e){ |
---|
72 | //console.log(this.id + ": onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode)); |
---|
73 | if(clickKey(e) && e.target == lastKeyDownNode){ // === breaks greasemonkey |
---|
74 | //need reset here or have problems in FF when focus returns to trigger element after closing popup/alert |
---|
75 | lastKeyDownNode = null; |
---|
76 | listener.call(this, e); |
---|
77 | } |
---|
78 | }), |
---|
79 | |
---|
80 | on(node, "click", function(e){ |
---|
81 | // and connect for mouse clicks too (or touch-clicks on mobile) |
---|
82 | listener.call(this, e); |
---|
83 | }) |
---|
84 | ]; |
---|
85 | |
---|
86 | return { |
---|
87 | remove: function(){ |
---|
88 | array.forEach(handles, function(h){ h.remove(); }); |
---|
89 | } |
---|
90 | }; |
---|
91 | } |
---|
92 | }; |
---|
93 | |
---|
94 | return declare("dijit._OnDijitClickMixin", null, { |
---|
95 | connect: function( |
---|
96 | /*Object|null*/ obj, |
---|
97 | /*String|Function*/ event, |
---|
98 | /*String|Function*/ method){ |
---|
99 | // summary: |
---|
100 | // Connects specified obj/event to specified method of this object |
---|
101 | // and registers for disconnect() on widget destroy. |
---|
102 | // description: |
---|
103 | // Provide widget-specific analog to connect.connect, except with the |
---|
104 | // implicit use of this widget as the target object. |
---|
105 | // This version of connect also provides a special "ondijitclick" |
---|
106 | // event which triggers on a click or space or enter keyup. |
---|
107 | // Events connected with `this.connect` are disconnected upon |
---|
108 | // destruction. |
---|
109 | // returns: |
---|
110 | // A handle that can be passed to `disconnect` in order to disconnect before |
---|
111 | // the widget is destroyed. |
---|
112 | // example: |
---|
113 | // | var btn = new dijit.form.Button(); |
---|
114 | // | // when foo.bar() is called, call the listener we're going to |
---|
115 | // | // provide in the scope of btn |
---|
116 | // | btn.connect(foo, "bar", function(){ |
---|
117 | // | console.debug(this.toString()); |
---|
118 | // | }); |
---|
119 | // tags: |
---|
120 | // protected |
---|
121 | |
---|
122 | return this.inherited(arguments, [obj, event == "ondijitclick" ? a11yclick : event, method]); |
---|
123 | } |
---|
124 | }); |
---|
125 | }); |
---|