forked from ibm-js/delite
-
Notifications
You must be signed in to change notification settings - Fork 0
/
typematic.js
246 lines (233 loc) · 10.2 KB
/
typematic.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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
/**
* These functions are used to repetitively call a user specified callback
* method when a specific key or mouse click over a specific DOM node is
* held down for a specific amount of time.
* Only one such event is allowed to occur on the browser page at one time.
* @module delite/typematic
*/
define([], function () {
// TODO: do we still need this module at all?
// TODO: wouldn't this be easier to use as a widget base class (although we would need to make sure the function
// names wouldn't likely conflict with other general method names)?
var typematic = /** @lends module:delite/typematic */ {
_fireEventAndReload: function () {
this._timer = null;
this._callback(++this._count, this._node, this._evt);
// Schedule next event, timer is at most minDelay (default 10ms) to avoid
// browser overload (particularly avoiding starving DOH robot so it never gets to send a mouseup)
this._currentTimeout = Math.max(
this._currentTimeout < 0 ? this._initialDelay :
(this._subsequentDelay > 1 ? this._subsequentDelay :
Math.round(this._currentTimeout * this._subsequentDelay)),
this._minDelay);
this._timer = setTimeout(this._fireEventAndReload.bind(this), this._currentTimeout);
},
/**
* Callback function passed to addKeyListener(), addMouseListener(), and addListener().
* @callback module:delite/typematic.TriggerCallback
* @param {number} count - Integer representing number of repeated calls (0..n)
* with -1 indicating the iteration has stopped.
* @param {Element} node - The DOM node object passed in.
* @param {Event} evt - Key or mouse event object.
*/
/**
* Start a timed, repeating callback sequence.
* If already started, the function call is ignored.
* This method is not normally called by the user but can be
* when the normal listener code is insufficient.
* @param {Event} evt - Key or mouse event object to pass to the user callback.
* @param {module:delite/Widget} _this - Pointer to the user's widget space.
* @param {Element} node - The DOM node object to pass the the callback function.
* @param {module:delite/typematic.TriggerCallback} callback - Function to call until the sequence is stopped.
* @param {Object} obj - User space object used to uniquely identify each typematic sequence.
* @param {number} [subsequentDelay] - If > 1, the number of milliseconds until the 3->n events occur
* or else the fractional time multiplier for the next event's delay, default=0.9.
* @param {number} [initialDelay] - The number of milliseconds until the 2nd event occurs, default=500ms.
* @param {number} [minDelay] - The maximum delay in milliseconds for event to fire, default=10ms.
*/
trigger: function (evt, _this, node, callback, obj,
subsequentDelay, initialDelay, minDelay) {
if (obj !== this._obj) {
this.stop();
this._initialDelay = initialDelay || 500;
this._subsequentDelay = subsequentDelay || 0.90;
this._minDelay = minDelay || 10;
this._obj = obj;
this._node = node;
this._currentTimeout = -1;
this._count = -1;
this._callback = callback.bind(_this);
this._evt = { faux: true };
for (var attr in evt) {
if (attr !== "layerX" && attr !== "layerY") { // prevent WebKit warnings
var v = evt[attr];
if (typeof v !== "function" && typeof v !== "undefined") {
this._evt[attr] = v;
}
}
}
this._fireEventAndReload();
}
},
/**
* Stop an ongoing timed, repeating callback sequence.
*/
stop: function () {
if (this._timer) {
clearTimeout(this._timer);
this._timer = null;
}
if (this._obj) {
this._callback(-1, this._node, this._evt);
this._obj = null;
}
},
/**
* Handle to cancel a listener.
* @typedef module:delite/typematic.Handle
* @property {Function} remove - cancel the listener
*/
/**
* Start listening for a specific typematic key.
* See also the trigger method for other parameters.
* @param {Element} node
* @param {Object} keyObject - An object defining the key to listen for:
* - keyCode: the keyCode (number) to listen for, used for non-printable keys
* - charCode: the charCode (number) to listen for, used for printable keys
* - ctrlKey: desired ctrl key state to initiate the callback sequence:
* - pressed (true)
* - released (false)
* - either (unspecified)
* - altKey: same as ctrlKey but for the alt key
* - shiftKey: same as ctrlKey but for the shift key
* @param {module:delite/Widget} _this - Pointer to the user's widget space.
* @param {module:delite/typematic.TriggerCallback} callback - Function to call until the sequence is stopped.
* @param {number} [subsequentDelay] - If > 1, the number of milliseconds until the 3->n events occur
* or else the fractional time multiplier for the next event's delay, default=0.9.
* @param {number} [initialDelay] - The number of milliseconds until the 2nd event occurs, default=500ms.
* @param {number} [minDelay] - The maximum delay in milliseconds for event to fire, default=10ms.
* @returns {module:delite/typematic.Handle} A connection handle.
*/
addKeyListener: function (node, keyObject, _this, callback,
subsequentDelay, initialDelay, minDelay) {
// Setup keydown or keypress listener depending on whether keyCode or charCode was specified.
var type = "keyCode" in keyObject ? "keydown" : "keypress",
attr = "keyCode" in keyObject ? "keyCode" : "charCode";
var handles = [
_this.on(type, function (evt) {
if (evt[attr] === keyObject[attr] &&
(keyObject.ctrlKey === undefined || keyObject.ctrlKey === evt.ctrlKey) &&
(keyObject.altKey === undefined || keyObject.altKey === evt.altKey) &&
(keyObject.metaKey === undefined || keyObject.metaKey === evt.metaKey) &&
(keyObject.shiftKey === undefined || keyObject.shiftKey === evt.shiftKey)) {
evt.stopPropagation();
evt.preventDefault();
typematic.trigger(evt, _this, node, callback, keyObject,
subsequentDelay, initialDelay, minDelay);
} else if (typematic._obj === keyObject) {
typematic.stop();
}
}, node),
_this.on("keyup", function () {
if (typematic._obj === keyObject) {
typematic.stop();
}
}, node)
];
return {
remove: function () {
handles.forEach(function (h) {
h.remove();
});
}
};
},
/**
* Start listening for a typematic mouse click.
* See also the trigger method for other parameters.
* @param {Element} node
* @param {Object} keyObject - An object defining the key to listen for:
* - keyCode: the keyCode (number) to listen for, used for non-printable keys
* - charCode: the charCode (number) to listen for, used for printable keys
* - ctrlKey: desired ctrl key state to initiate the callback sequence:
* - pressed (true)
* - released (false)
* - either (unspecified)
* - altKey: same as ctrlKey but for the alt key
* - shiftKey: same as ctrlKey but for the shift key
* @param {module:delite/Widget} _this - Pointer to the user's widget space.
* @param {module:delite/typematic.TriggerCallback} callback - Function to call untilthe sequence is stopped.
* @param {number} [subsequentDelay] - If > 1, the number of milliseconds until the 3->n events occur
* or else the fractional time multiplier for the next event's delay, default=0.9.
* @param {number} [initialDelay] - The number of milliseconds until the 2nd event occurs, default=500ms.
* @param {number} [minDelay] - The maximum delay in milliseconds for event to fire, default=10ms.
* @returns {module:delite/typematic.Handle} A connection handle.
*/
addMouseListener: function (node, _this, callback,
subsequentDelay, initialDelay, minDelay) {
var handles = [
_this.on("mousedown", function (evt) {
evt.preventDefault();
typematic.trigger(evt, _this, node, callback, node, subsequentDelay, initialDelay, minDelay);
}, node),
_this.on("mouseup", function (evt) {
if (this._obj) {
evt.preventDefault();
}
typematic.stop();
}.bind(this), node),
_this.on("mouseout", function (evt) {
if (this._obj) {
evt.preventDefault();
}
typematic.stop();
}.bind(this), node),
_this.on("dblclick", function (evt) {
evt.preventDefault();
}, node)
];
return { remove: function () {
handles.forEach(function (h) {
h.remove();
});
} };
},
/**
* Start listening for a specific typematic key and mouseclick.
* This is a thin wrapper to addKeyListener and addMouseListener.
* @param {Element} mouseNode - The DOM node object to listen on for mouse events.
* @param {Element} keyNodeNode - The DOM node object to listen on for key events.
* @param {Object} keyObject - An object defining the key to listen for:
* - keyCode: the keyCode (number) to listen for, used for non-printable keys
* - charCode: the charCode (number) to listen for, used for printable keys
* - ctrlKey: desired ctrl key state to initiate the callback sequence:
* - pressed (true)
* - released (false)
* - either (unspecified)
* - altKey: same as ctrlKey but for the alt key
* - shiftKey: same as ctrlKey but for the shift key
* @param {module:delite/Widget} _this - Pointer to the user's widget space.
* @param {module:delite/typematic.TriggerCallback} callback - Function to call until the sequence is stopped.
* @param {number} [subsequentDelay] - If > 1, the number of milliseconds until the 3->n events occur
* or else the fractional time multiplier for the next event's delay, default=0.9.
* @param {number} [initialDelay] - The number of milliseconds until the 2nd event occurs, default=500ms.
* @param {number} [minDelay] - The maximum delay in milliseconds for event to fire, default=10ms.
* @returns {module:delite/typematic.Handle} A connection handle.
*/
addListener: function (mouseNode, keyNode, keyObject, _this,
callback, subsequentDelay, initialDelay, minDelay) {
var handles = [
this.addKeyListener(keyNode, keyObject, _this, callback, subsequentDelay, initialDelay, minDelay),
this.addMouseListener(mouseNode, _this, callback, subsequentDelay, initialDelay, minDelay)
];
return {
remove: function () {
handles.forEach(function (h) {
h.remove();
});
}
};
}
};
return typematic;
});