forked from playcanvas/engine
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathkeyboard.js
More file actions
324 lines (277 loc) · 10.6 KB
/
keyboard.js
File metadata and controls
324 lines (277 loc) · 10.6 KB
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
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
pc.extend(pc, function(){
/**
* @constructor
* @name pc.KeyboardEvent
* @classdesc The KeyboardEvent is passed into all event callbacks from the {@link pc.Keyboard}. It corresponds to a key press or release.
* @description Create a new KeyboardEvent
* @param {pc.Keyboard} keyboard The keyboard object which is firing the event.
* @param {KeyboardEvent} event The original browser event that was fired.
* @property {Number} key The keyCode of the key that has changed. See the pc.KEY_* constants.
* @property {Element} element The element that fired the keyboard event.
* @property {KeyboardEvent} event The original browser event which was fired.
* @example
* var onKeyDown = function (e) {
* if (e.key === pc.KEY_SPACE) {
* // space key pressed
* }
* e.event.preventDefault(); // Use original browser event to prevent browser action.
* };
* app.keyboard.on("keydown", onKeyDown, this);
*/
var KeyboardEvent = function (keyboard, event) {
if (event) {
this.key = event.keyCode;
this.element = event.target;
this.event = event;
} else {
this.key = null;
this.element = null;
this.event = null;
}
};
// internal global keyboard events
var _keyboardEvent = new KeyboardEvent();
function makeKeyboardEvent(event) {
_keyboardEvent.key = event.keyCode;
_keyboardEvent.element = event.target;
_keyboardEvent.event = event;
return _keyboardEvent;
}
/**
* @private
* @function
* @name pc.toKeyCode
* @description Convert a string or keycode to a keycode
* @param {String | Number} s Either a character code or the key character.
* @returns {Number} The character code.
*/
function toKeyCode(s){
if (typeof(s) == "string") {
return s.toUpperCase().charCodeAt(0);
} else {
return s;
}
}
var _keyCodeToKeyIdentifier = {
'9': 'Tab',
'13': 'Enter',
'16': 'Shift',
'17': 'Control',
'18': 'Alt',
'27': 'Escape',
'37': 'Left',
'38': 'Up',
'39': 'Right',
'40': 'Down',
'46': 'Delete',
'91': 'Win'
};
/**
* @event
* @name pc.Keyboard#keydown
* @description Event fired when a key is pressed.
* @param {pc.KeyboardEvent} event The Keyboard event object. Note, this event is only valid for the current callback.
* @example
* var onKeyDown = function (e) {
* if (e.key === pc.KEY_SPACE) {
* // space key pressed
* }
* e.event.preventDefault(); // Use original browser event to prevent browser action.
* };
* app.keyboard.on("keydown", onKeyDown, this);
*/
/**
* @event
* @name pc.Keyboard#keyup
* @description Event fired when a key is released.
* @param {pc.KeyboardEvent} event The Keyboard event object. Note, this event is only valid for the current callback.
* @example
* var onKeyUp = function (e) {
* if (e.key === pc.KEY_SPACE) {
* // space key released
* }
* e.event.preventDefault(); // Use original browser event to prevent browser action.
* };
* app.keyboard.on("keyup", onKeyUp, this);
*/
/**
* @constructor
* @name pc.Keyboard
* @classdesc A Keyboard device bound to an Element. Allows you to detect the state of the key presses.
* Note, Keyboard object must be attached to an Element before it can detect any key presses.
* @description Create a new Keyboard object
* @param {Element} [element] Element to attach Keyboard to. Note that elements like <div> can't
* accept focus by default. To use keyboard events on an element like this it must have a value of 'tabindex' e.g. tabindex="0". For more details: <a href="http://www.w3.org/WAI/GL/WCAG20/WD-WCAG20-TECHS/SCR29.html">http://www.w3.org/WAI/GL/WCAG20/WD-WCAG20-TECHS/SCR29.html</a>
* @param {Object} [options] Optional options object.
* @param {Boolean} [options.preventDefault] Call preventDefault() in key event handlers. This stops the default action of the event occurring. e.g. Ctrl+T will not open a new browser tab
* @param {Boolean} [options.stopPropagation] Call stopPropagation() in key event handlers. This stops the event bubbling up the DOM so no parent handlers will be notified of the event
* @example
* var keyboard = new pc.Keyboard(window); // attach keyboard listeners to the window
*/
var Keyboard = function(element, options) {
options = options || {};
this._element = null;
this._keyDownHandler = this._handleKeyDown.bind(this);
this._keyUpHandler = this._handleKeyUp.bind(this);
this._keyPressHandler = this._handleKeyPress.bind(this);
pc.events.attach(this);
this._keymap = {};
this._lastmap = {};
if (element) {
this.attach(element);
}
this.preventDefault = options.preventDefault || false;
this.stopPropagation = options.stopPropagation || false;
};
/**
* @function
* @name pc.Keyboard#attach
* @description Attach the keyboard event handlers to an Element
* @param {Element} element The element to listen for keyboard events on.
*/
Keyboard.prototype.attach = function (element) {
if (this._element) {
// remove previous attached element
this.detach();
}
this._element = element;
this._element.addEventListener("keydown", this._keyDownHandler, false);
this._element.addEventListener("keypress", this._keyPressHandler, false);
this._element.addEventListener("keyup", this._keyUpHandler, false);
};
/**
* @function
* @name pc.Keyboard#detach
* @description Detach the keyboard event handlers from the element it is attached to.
*/
Keyboard.prototype.detach = function () {
this._element.removeEventListener("keydown", this._keyDownHandler);
this._element.removeEventListener("keypress", this._keyPressHandler);
this._element.removeEventListener("keyup", this._keyUpHandler);
this._element = null;
};
/**
* @private
* @function
* @name pc.Keyboard#toKeyIdentifier
* @description Convert a key code into a key identifier
* @param {Number} keyCode The key code.
* @returns {String} The key identifier.
*/
Keyboard.prototype.toKeyIdentifier = function(keyCode){
keyCode = toKeyCode(keyCode);
var count;
var hex;
var length;
var id = _keyCodeToKeyIdentifier[keyCode.toString()];
if (id) {
return id;
}
// Convert to hex and add leading 0's
hex = keyCode.toString(16).toUpperCase();
length = hex.length;
for (count = 0; count < (4 - length); count++) {
hex = '0' + hex;
}
return 'U+' + hex;
};
Keyboard.prototype._handleKeyDown = function(event) {
var code = event.keyCode || event.charCode;
// Google Chrome auto-filling of login forms could raise a malformed event
if (code === undefined) return;
var id = this.toKeyIdentifier(code);
this._keymap[id] = true;
// Patch on the keyIdentifier property in non-webkit browsers
//event.keyIdentifier = event.keyIdentifier || id;
this.fire("keydown", makeKeyboardEvent(event));
if (this.preventDefault) {
event.preventDefault();
}
if (this.stopPropagation) {
event.stopPropagation();
}
};
Keyboard.prototype._handleKeyUp = function(event){
var code = event.keyCode || event.charCode;
// Google Chrome auto-filling of login forms could raise a malformed event
if (code === undefined) return;
var id = this.toKeyIdentifier(code);
delete this._keymap[id];
// Patch on the keyIdentifier property in non-webkit browsers
//event.keyIdentifier = event.keyIdentifier || id;
this.fire("keyup", makeKeyboardEvent(event));
if (this.preventDefault) {
event.preventDefault();
}
if (this.stopPropagation) {
event.stopPropagation();
}
};
Keyboard.prototype._handleKeyPress = function(event){
this.fire("keypress", makeKeyboardEvent(event));
if (this.preventDefault) {
event.preventDefault();
}
if (this.stopPropagation) {
event.stopPropagation();
}
};
/**
* @private
* @function
* @name pc.Keyboard#update
* @description Called once per frame to update internal state.
*/
Keyboard.prototype.update = function () {
var prop;
// clear all keys
for (prop in this._lastmap) {
delete this._lastmap[prop];
}
for (prop in this._keymap) {
if (this._keymap.hasOwnProperty(prop)) {
this._lastmap[prop] = this._keymap[prop];
}
}
};
/**
* @function
* @name pc.Keyboard#isPressed
* @description Return true if the key is currently down.
* @param {Number} key The keyCode of the key to test. See the pc.KEY_* constants.
* @returns {Boolean} True if the key was pressed, false if not.
*/
Keyboard.prototype.isPressed = function (key) {
var keyCode = toKeyCode(key);
var id = this.toKeyIdentifier(keyCode);
return !!(this._keymap[id]);
};
/**
* @function
* @name pc.Keyboard#wasPressed
* @description Returns true if the key was pressed since the last update.
* @param {Number} key The keyCode of the key to test. See the pc.KEY_* constants.
* @returns {Boolean} true if the key was pressed.
*/
Keyboard.prototype.wasPressed = function (key) {
var keyCode = toKeyCode(key);
var id = this.toKeyIdentifier(keyCode);
return (!!(this._keymap[id]) && !!!(this._lastmap[id]));
};
/**
* @function
* @name pc.Keyboard#wasReleased
* @description Returns true if the key was released since the last update.
* @param {Number} key The keyCode of the key to test. See the pc.KEY_* constants.
* @returns {Boolean} true if the key was pressed.
*/
Keyboard.prototype.wasReleased = function (key) {
var keyCode = toKeyCode(key);
var id = this.toKeyIdentifier(keyCode);
return (!!!(this._keymap[id]) && !!(this._lastmap[id]));
};
return {
Keyboard: Keyboard,
KeyboardEvent: KeyboardEvent
};
}());