forked from dojo/dijit-oldmirror
-
Notifications
You must be signed in to change notification settings - Fork 0
/
_OnDijitClickMixin.js
154 lines (139 loc) · 5.56 KB
/
_OnDijitClickMixin.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
define([
"dojo/on",
"dojo/_base/array", // array.forEach
"dojo/keys", // keys.ENTER keys.SPACE
"dojo/_base/declare", // declare
"dojo/has", // has("dom-addeventlistener")
"dojo/_base/unload", // unload.addOnWindowUnload
"dojo/_base/window" // win.doc.addEventListener win.doc.attachEvent win.doc.detachEvent
], function(on, array, keys, declare, has, unload, win){
// module:
// dijit/_OnDijitClickMixin
// summary:
// Mixin so you can pass "ondijitclick" to this.connect() method,
// as a way to handle clicks by mouse, or by keyboard (SPACE/ENTER key).
// description:
// Setting an ondijitclick handler on a node has two effects:
// 1. converts keyboard "click" events into actual click events, so
// that a "click" event bubbles up from the widget to any listeners on ancestor nodes.
// 2. sets up a click listener on the node, which catches native click events plus
// the events generated from the previous step.
// Keep track of where the last keydown event was, to help avoid generating
// spurious ondijitclick events when:
// 1. focus is on a <button> or <a>
// 2. user presses then releases the ENTER key
// 3. onclick handler fires and shifts focus to another node, with an ondijitclick handler
// 4. onkeyup event fires, causing the ondijitclick handler to fire
var lastKeyDownNode = null;
if(has("dom-addeventlistener")){
win.doc.addEventListener('keydown', function(evt){
lastKeyDownNode = evt.target;
}, true);
}else{
// Fallback path for IE6-8
(function(){
var keydownCallback = function(evt){
lastKeyDownNode = evt.srcElement;
};
win.doc.attachEvent('onkeydown', keydownCallback);
unload.addOnWindowUnload(function(){
win.doc.detachEvent('onkeydown', keydownCallback);
});
})();
}
function clickKey(/*Event*/ e){
return (e.keyCode === keys.ENTER || e.keyCode === keys.SPACE) &&
!e.ctrlKey && !e.shiftKey && !e.altKey && !e.metaKey;
}
// Custom a11yclick (a.k.a. ondijitclick) event
var a11yclick = function(node, listener){
if(/input|button/i.test(node.nodeName)){
// pass through, the browser already generates click event on SPACE/ENTER key
return on(node, "click", listener);
}else{
// Don't fire the click event unless both the keydown and keyup occur on this node.
// Avoids problems where focus shifted to this node or away from the node on keydown,
// either causing this node to process a stray keyup event, or causing another node
// to get a stray keyup event.
var handles = [
on(node, "keypress", function(e){
//console.log(this.id + ": onkeydown, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
if(clickKey(e)){
// needed on IE for when focus changes between keydown and keyup - otherwise dropdown menus do not work
lastKeyDownNode = e.target;
// Prevent viewport scrolling on space key in IE<9.
// (Reproducible on test_Button.html on any of the first dijit.form.Button examples)
// Do this onkeypress rather than onkeydown because onkeydown.preventDefault() will
// suppress the onkeypress event, breaking _HasDropDown
e.preventDefault();
}
}),
on(node, "keyup", function(e){
//console.log(this.id + ": onkeyup, e.target = ", e.target, ", lastKeyDownNode was ", lastKeyDownNode, ", equality is ", (e.target === lastKeyDownNode));
if(clickKey(e) && e.target == lastKeyDownNode){ // === breaks greasemonkey
//need reset here or have problems in FF when focus returns to trigger element after closing popup/alert
lastKeyDownNode = null;
on.emit(e.target, "click", {
cancelable: true,
bubbles: true
});
}
}),
on(node, "click", function(e){
// catch mouse clicks, plus the on.emit() calls from above and below
listener.call(this, e);
})
];
if(has("touch")){
handles.push(
on(node, "touchend", function(e){
// touchstart-->touchend will automatically generate a click event, but there are problems
// on iOS after focus has been programatically shifted (#14604, #14918), so do it manually.
e.preventDefault();
on.emit(e.target, "click", {
cancelable: true,
bubbles: true
});
})
);
}
return {
remove: function(){
array.forEach(handles, function(h){ h.remove(); });
}
};
}
};
var ret = declare("dijit._OnDijitClickMixin", null, {
connect: function(
/*Object|null*/ obj,
/*String|Function*/ event,
/*String|Function*/ method){
// summary:
// Connects specified obj/event to specified method of this object
// and registers for disconnect() on widget destroy.
// description:
// Provide widget-specific analog to connect.connect, except with the
// implicit use of this widget as the target object.
// This version of connect also provides a special "ondijitclick"
// event which triggers on a click or space or enter keyup.
// Events connected with `this.connect` are disconnected upon
// destruction.
// returns:
// A handle that can be passed to `disconnect` in order to disconnect before
// the widget is destroyed.
// example:
// | var btn = new dijit.form.Button();
// | // when foo.bar() is called, call the listener we're going to
// | // provide in the scope of btn
// | btn.connect(foo, "bar", function(){
// | console.debug(this.toString());
// | });
// tags:
// protected
return this.inherited(arguments, [obj, event == "ondijitclick" ? a11yclick : event, method]);
}
});
ret.a11yclick = a11yclick;
return ret;
});