Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/events/keyhandler.js
2868 views
1
// Copyright 2007 The Closure Library Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
/**
16
* @fileoverview This file contains a class for working with keyboard events
17
* that repeat consistently across browsers and platforms. It also unifies the
18
* key code so that it is the same in all browsers and platforms.
19
*
20
* Different web browsers have very different keyboard event handling. Most
21
* importantly is that only certain browsers repeat keydown events:
22
* IE, Opera, FF/Win32, and Safari 3 repeat keydown events.
23
* FF/Mac and Safari 2 do not.
24
*
25
* For the purposes of this code, "Safari 3" means WebKit 525+, when WebKit
26
* decided that they should try to match IE's key handling behavior.
27
* Safari 3.0.4, which shipped with Leopard (WebKit 523), has the
28
* Safari 2 behavior.
29
*
30
* Firefox, Safari, Opera prevent on keypress
31
*
32
* IE prevents on keydown
33
*
34
* Firefox does not fire keypress for shift, ctrl, alt
35
* Firefox does fire keydown for shift, ctrl, alt, meta
36
* Firefox does not repeat keydown for shift, ctrl, alt, meta
37
*
38
* Firefox does not fire keypress for up and down in an input
39
*
40
* Opera fires keypress for shift, ctrl, alt, meta
41
* Opera does not repeat keypress for shift, ctrl, alt, meta
42
*
43
* Safari 2 and 3 do not fire keypress for shift, ctrl, alt
44
* Safari 2 does not fire keydown for shift, ctrl, alt
45
* Safari 3 *does* fire keydown for shift, ctrl, alt
46
*
47
* IE provides the keycode for keyup/down events and the charcode (in the
48
* keycode field) for keypress.
49
*
50
* Mozilla provides the keycode for keyup/down and the charcode for keypress
51
* unless it's a non text modifying key in which case the keycode is provided.
52
*
53
* Safari 3 provides the keycode and charcode for all events.
54
*
55
* Opera provides the keycode for keyup/down event and either the charcode or
56
* the keycode (in the keycode field) for keypress events.
57
*
58
* Firefox x11 doesn't fire keydown events if a another key is already held down
59
* until the first key is released. This can cause a key event to be fired with
60
* a keyCode for the first key and a charCode for the second key.
61
*
62
* Safari in keypress
63
*
64
* charCode keyCode which
65
* ENTER: 13 13 13
66
* F1: 63236 63236 63236
67
* F8: 63243 63243 63243
68
* ...
69
* p: 112 112 112
70
* P: 80 80 80
71
*
72
* Firefox, keypress:
73
*
74
* charCode keyCode which
75
* ENTER: 0 13 13
76
* F1: 0 112 0
77
* F8: 0 119 0
78
* ...
79
* p: 112 0 112
80
* P: 80 0 80
81
*
82
* Opera, Mac+Win32, keypress:
83
*
84
* charCode keyCode which
85
* ENTER: undefined 13 13
86
* F1: undefined 112 0
87
* F8: undefined 119 0
88
* ...
89
* p: undefined 112 112
90
* P: undefined 80 80
91
*
92
* IE7, keydown
93
*
94
* charCode keyCode which
95
* ENTER: undefined 13 undefined
96
* F1: undefined 112 undefined
97
* F8: undefined 119 undefined
98
* ...
99
* p: undefined 80 undefined
100
* P: undefined 80 undefined
101
*
102
* @author [email protected] (Erik Arvidsson)
103
* @author [email protected] (Emil A Eklund)
104
* @see ../demos/keyhandler.html
105
*/
106
107
goog.provide('goog.events.KeyEvent');
108
goog.provide('goog.events.KeyHandler');
109
goog.provide('goog.events.KeyHandler.EventType');
110
111
goog.require('goog.events');
112
goog.require('goog.events.BrowserEvent');
113
goog.require('goog.events.EventTarget');
114
goog.require('goog.events.EventType');
115
goog.require('goog.events.KeyCodes');
116
goog.require('goog.userAgent');
117
118
119
120
/**
121
* A wrapper around an element that you want to listen to keyboard events on.
122
* @param {Element|Document=} opt_element The element or document to listen on.
123
* @param {boolean=} opt_capture Whether to listen for browser events in
124
* capture phase (defaults to false).
125
* @constructor
126
* @extends {goog.events.EventTarget}
127
* @final
128
*/
129
goog.events.KeyHandler = function(opt_element, opt_capture) {
130
goog.events.EventTarget.call(this);
131
132
if (opt_element) {
133
this.attach(opt_element, opt_capture);
134
}
135
};
136
goog.inherits(goog.events.KeyHandler, goog.events.EventTarget);
137
138
139
/**
140
* This is the element that we will listen to the real keyboard events on.
141
* @type {Element|Document|null}
142
* @private
143
*/
144
goog.events.KeyHandler.prototype.element_ = null;
145
146
147
/**
148
* The key for the key press listener.
149
* @type {goog.events.Key}
150
* @private
151
*/
152
goog.events.KeyHandler.prototype.keyPressKey_ = null;
153
154
155
/**
156
* The key for the key down listener.
157
* @type {goog.events.Key}
158
* @private
159
*/
160
goog.events.KeyHandler.prototype.keyDownKey_ = null;
161
162
163
/**
164
* The key for the key up listener.
165
* @type {goog.events.Key}
166
* @private
167
*/
168
goog.events.KeyHandler.prototype.keyUpKey_ = null;
169
170
171
/**
172
* Used to detect keyboard repeat events.
173
* @private
174
* @type {number}
175
*/
176
goog.events.KeyHandler.prototype.lastKey_ = -1;
177
178
179
/**
180
* Keycode recorded for key down events. As most browsers don't report the
181
* keycode in the key press event we need to record it in the key down phase.
182
* @private
183
* @type {number}
184
*/
185
goog.events.KeyHandler.prototype.keyCode_ = -1;
186
187
188
/**
189
* Alt key recorded for key down events. FF on Mac does not report the alt key
190
* flag in the key press event, we need to record it in the key down phase.
191
* @type {boolean}
192
* @private
193
*/
194
goog.events.KeyHandler.prototype.altKey_ = false;
195
196
197
/**
198
* Enum type for the events fired by the key handler
199
* @enum {string}
200
*/
201
goog.events.KeyHandler.EventType = {
202
KEY: 'key'
203
};
204
205
206
/**
207
* An enumeration of key codes that Safari 2 does incorrectly
208
* @type {Object}
209
* @private
210
*/
211
goog.events.KeyHandler.safariKey_ = {
212
'3': goog.events.KeyCodes.ENTER, // 13
213
'12': goog.events.KeyCodes.NUMLOCK, // 144
214
'63232': goog.events.KeyCodes.UP, // 38
215
'63233': goog.events.KeyCodes.DOWN, // 40
216
'63234': goog.events.KeyCodes.LEFT, // 37
217
'63235': goog.events.KeyCodes.RIGHT, // 39
218
'63236': goog.events.KeyCodes.F1, // 112
219
'63237': goog.events.KeyCodes.F2, // 113
220
'63238': goog.events.KeyCodes.F3, // 114
221
'63239': goog.events.KeyCodes.F4, // 115
222
'63240': goog.events.KeyCodes.F5, // 116
223
'63241': goog.events.KeyCodes.F6, // 117
224
'63242': goog.events.KeyCodes.F7, // 118
225
'63243': goog.events.KeyCodes.F8, // 119
226
'63244': goog.events.KeyCodes.F9, // 120
227
'63245': goog.events.KeyCodes.F10, // 121
228
'63246': goog.events.KeyCodes.F11, // 122
229
'63247': goog.events.KeyCodes.F12, // 123
230
'63248': goog.events.KeyCodes.PRINT_SCREEN, // 44
231
'63272': goog.events.KeyCodes.DELETE, // 46
232
'63273': goog.events.KeyCodes.HOME, // 36
233
'63275': goog.events.KeyCodes.END, // 35
234
'63276': goog.events.KeyCodes.PAGE_UP, // 33
235
'63277': goog.events.KeyCodes.PAGE_DOWN, // 34
236
'63289': goog.events.KeyCodes.NUMLOCK, // 144
237
'63302': goog.events.KeyCodes.INSERT // 45
238
};
239
240
241
/**
242
* An enumeration of key identifiers currently part of the W3C draft for DOM3
243
* and their mappings to keyCodes.
244
* http://www.w3.org/TR/DOM-Level-3-Events/keyset.html#KeySet-Set
245
* This is currently supported in Safari and should be platform independent.
246
* @type {Object}
247
* @private
248
*/
249
goog.events.KeyHandler.keyIdentifier_ = {
250
'Up': goog.events.KeyCodes.UP, // 38
251
'Down': goog.events.KeyCodes.DOWN, // 40
252
'Left': goog.events.KeyCodes.LEFT, // 37
253
'Right': goog.events.KeyCodes.RIGHT, // 39
254
'Enter': goog.events.KeyCodes.ENTER, // 13
255
'F1': goog.events.KeyCodes.F1, // 112
256
'F2': goog.events.KeyCodes.F2, // 113
257
'F3': goog.events.KeyCodes.F3, // 114
258
'F4': goog.events.KeyCodes.F4, // 115
259
'F5': goog.events.KeyCodes.F5, // 116
260
'F6': goog.events.KeyCodes.F6, // 117
261
'F7': goog.events.KeyCodes.F7, // 118
262
'F8': goog.events.KeyCodes.F8, // 119
263
'F9': goog.events.KeyCodes.F9, // 120
264
'F10': goog.events.KeyCodes.F10, // 121
265
'F11': goog.events.KeyCodes.F11, // 122
266
'F12': goog.events.KeyCodes.F12, // 123
267
'U+007F': goog.events.KeyCodes.DELETE, // 46
268
'Home': goog.events.KeyCodes.HOME, // 36
269
'End': goog.events.KeyCodes.END, // 35
270
'PageUp': goog.events.KeyCodes.PAGE_UP, // 33
271
'PageDown': goog.events.KeyCodes.PAGE_DOWN, // 34
272
'Insert': goog.events.KeyCodes.INSERT // 45
273
};
274
275
276
/**
277
* If true, the KeyEvent fires on keydown. Otherwise, it fires on keypress.
278
*
279
* @type {boolean}
280
* @private
281
*/
282
goog.events.KeyHandler.USES_KEYDOWN_ = goog.userAgent.IE ||
283
goog.userAgent.EDGE ||
284
goog.userAgent.WEBKIT && goog.userAgent.isVersionOrHigher('525');
285
286
287
/**
288
* If true, the alt key flag is saved during the key down and reused when
289
* handling the key press. FF on Mac does not set the alt flag in the key press
290
* event.
291
* @type {boolean}
292
* @private
293
*/
294
goog.events.KeyHandler.SAVE_ALT_FOR_KEYPRESS_ =
295
goog.userAgent.MAC && goog.userAgent.GECKO;
296
297
298
/**
299
* Records the keycode for browsers that only returns the keycode for key up/
300
* down events. For browser/key combinations that doesn't trigger a key pressed
301
* event it also fires the patched key event.
302
* @param {goog.events.BrowserEvent} e The key down event.
303
* @private
304
*/
305
goog.events.KeyHandler.prototype.handleKeyDown_ = function(e) {
306
// Ctrl-Tab and Alt-Tab can cause the focus to be moved to another window
307
// before we've caught a key-up event. If the last-key was one of these we
308
// reset the state.
309
if (goog.userAgent.WEBKIT || goog.userAgent.EDGE) {
310
if (this.lastKey_ == goog.events.KeyCodes.CTRL && !e.ctrlKey ||
311
this.lastKey_ == goog.events.KeyCodes.ALT && !e.altKey ||
312
goog.userAgent.MAC && this.lastKey_ == goog.events.KeyCodes.META &&
313
!e.metaKey) {
314
this.resetState();
315
}
316
}
317
318
if (this.lastKey_ == -1) {
319
if (e.ctrlKey && e.keyCode != goog.events.KeyCodes.CTRL) {
320
this.lastKey_ = goog.events.KeyCodes.CTRL;
321
} else if (e.altKey && e.keyCode != goog.events.KeyCodes.ALT) {
322
this.lastKey_ = goog.events.KeyCodes.ALT;
323
} else if (e.metaKey && e.keyCode != goog.events.KeyCodes.META) {
324
this.lastKey_ = goog.events.KeyCodes.META;
325
}
326
}
327
328
if (goog.events.KeyHandler.USES_KEYDOWN_ &&
329
!goog.events.KeyCodes.firesKeyPressEvent(
330
e.keyCode, this.lastKey_, e.shiftKey, e.ctrlKey, e.altKey,
331
e.metaKey)) {
332
this.handleEvent(e);
333
} else {
334
this.keyCode_ = goog.events.KeyCodes.normalizeKeyCode(e.keyCode);
335
if (goog.events.KeyHandler.SAVE_ALT_FOR_KEYPRESS_) {
336
this.altKey_ = e.altKey;
337
}
338
}
339
};
340
341
342
/**
343
* Resets the stored previous values. Needed to be called for webkit which will
344
* not generate a key up for meta key operations. This should only be called
345
* when having finished with repeat key possibilities.
346
*/
347
goog.events.KeyHandler.prototype.resetState = function() {
348
this.lastKey_ = -1;
349
this.keyCode_ = -1;
350
};
351
352
353
/**
354
* Clears the stored previous key value, resetting the key repeat status. Uses
355
* -1 because the Safari 3 Windows beta reports 0 for certain keys (like Home
356
* and End.)
357
* @param {goog.events.BrowserEvent} e The keyup event.
358
* @private
359
*/
360
goog.events.KeyHandler.prototype.handleKeyup_ = function(e) {
361
this.resetState();
362
this.altKey_ = e.altKey;
363
};
364
365
366
/**
367
* Handles the events on the element.
368
* @param {goog.events.BrowserEvent} e The keyboard event sent from the
369
* browser.
370
*/
371
goog.events.KeyHandler.prototype.handleEvent = function(e) {
372
var be = e.getBrowserEvent();
373
var keyCode, charCode;
374
var altKey = be.altKey;
375
376
// IE reports the character code in the keyCode field for keypress events.
377
// There are two exceptions however, Enter and Escape.
378
if (goog.userAgent.IE && e.type == goog.events.EventType.KEYPRESS) {
379
keyCode = this.keyCode_;
380
charCode = keyCode != goog.events.KeyCodes.ENTER &&
381
keyCode != goog.events.KeyCodes.ESC ?
382
be.keyCode :
383
0;
384
385
// Safari reports the character code in the keyCode field for keypress
386
// events but also has a charCode field.
387
} else if (
388
(goog.userAgent.WEBKIT || goog.userAgent.EDGE) &&
389
e.type == goog.events.EventType.KEYPRESS) {
390
keyCode = this.keyCode_;
391
charCode = be.charCode >= 0 && be.charCode < 63232 &&
392
goog.events.KeyCodes.isCharacterKey(keyCode) ?
393
be.charCode :
394
0;
395
396
// Opera reports the keycode or the character code in the keyCode field.
397
} else if (goog.userAgent.OPERA && !goog.userAgent.WEBKIT) {
398
keyCode = this.keyCode_;
399
charCode = goog.events.KeyCodes.isCharacterKey(keyCode) ? be.keyCode : 0;
400
401
// Mozilla reports the character code in the charCode field.
402
} else {
403
keyCode = be.keyCode || this.keyCode_;
404
charCode = be.charCode || 0;
405
if (goog.events.KeyHandler.SAVE_ALT_FOR_KEYPRESS_) {
406
altKey = this.altKey_;
407
}
408
// On the Mac, shift-/ triggers a question mark char code and no key code
409
// (normalized to WIN_KEY), so we synthesize the latter.
410
if (goog.userAgent.MAC && charCode == goog.events.KeyCodes.QUESTION_MARK &&
411
keyCode == goog.events.KeyCodes.WIN_KEY) {
412
keyCode = goog.events.KeyCodes.SLASH;
413
}
414
}
415
416
keyCode = goog.events.KeyCodes.normalizeKeyCode(keyCode);
417
var key = keyCode;
418
419
// Correct the key value for certain browser-specific quirks.
420
if (keyCode) {
421
if (keyCode >= 63232 && keyCode in goog.events.KeyHandler.safariKey_) {
422
// NOTE(nicksantos): Safari 3 has fixed this problem,
423
// this is only needed for Safari 2.
424
key = goog.events.KeyHandler.safariKey_[keyCode];
425
} else {
426
// Safari returns 25 for Shift+Tab instead of 9.
427
if (keyCode == 25 && e.shiftKey) {
428
key = 9;
429
}
430
}
431
} else if (
432
be.keyIdentifier &&
433
be.keyIdentifier in goog.events.KeyHandler.keyIdentifier_) {
434
// This is needed for Safari Windows because it currently doesn't give a
435
// keyCode/which for non printable keys.
436
key = goog.events.KeyHandler.keyIdentifier_[be.keyIdentifier];
437
}
438
439
// If we get the same keycode as a keydown/keypress without having seen a
440
// keyup event, then this event was caused by key repeat.
441
var repeat = key == this.lastKey_;
442
this.lastKey_ = key;
443
444
var event = new goog.events.KeyEvent(key, charCode, repeat, be);
445
event.altKey = altKey;
446
this.dispatchEvent(event);
447
};
448
449
450
/**
451
* Returns the element listened on for the real keyboard events.
452
* @return {Element|Document|null} The element listened on for the real
453
* keyboard events.
454
*/
455
goog.events.KeyHandler.prototype.getElement = function() {
456
return this.element_;
457
};
458
459
460
/**
461
* Adds the proper key event listeners to the element.
462
* @param {Element|Document} element The element to listen on.
463
* @param {boolean=} opt_capture Whether to listen for browser events in
464
* capture phase (defaults to false).
465
*/
466
goog.events.KeyHandler.prototype.attach = function(element, opt_capture) {
467
if (this.keyUpKey_) {
468
this.detach();
469
}
470
471
this.element_ = element;
472
473
this.keyPressKey_ = goog.events.listen(
474
this.element_, goog.events.EventType.KEYPRESS, this, opt_capture);
475
476
// Most browsers (Safari 2 being the notable exception) doesn't include the
477
// keyCode in keypress events (IE has the char code in the keyCode field and
478
// Mozilla only included the keyCode if there's no charCode). Thus we have to
479
// listen for keydown to capture the keycode.
480
this.keyDownKey_ = goog.events.listen(
481
this.element_, goog.events.EventType.KEYDOWN, this.handleKeyDown_,
482
opt_capture, this);
483
484
485
this.keyUpKey_ = goog.events.listen(
486
this.element_, goog.events.EventType.KEYUP, this.handleKeyup_,
487
opt_capture, this);
488
};
489
490
491
/**
492
* Removes the listeners that may exist.
493
*/
494
goog.events.KeyHandler.prototype.detach = function() {
495
if (this.keyPressKey_) {
496
goog.events.unlistenByKey(this.keyPressKey_);
497
goog.events.unlistenByKey(this.keyDownKey_);
498
goog.events.unlistenByKey(this.keyUpKey_);
499
this.keyPressKey_ = null;
500
this.keyDownKey_ = null;
501
this.keyUpKey_ = null;
502
}
503
this.element_ = null;
504
this.lastKey_ = -1;
505
this.keyCode_ = -1;
506
};
507
508
509
/** @override */
510
goog.events.KeyHandler.prototype.disposeInternal = function() {
511
goog.events.KeyHandler.superClass_.disposeInternal.call(this);
512
this.detach();
513
};
514
515
516
517
/**
518
* This class is used for the goog.events.KeyHandler.EventType.KEY event and
519
* it overrides the key code with the fixed key code.
520
* @param {number} keyCode The adjusted key code.
521
* @param {number} charCode The unicode character code.
522
* @param {boolean} repeat Whether this event was generated by keyboard repeat.
523
* @param {Event} browserEvent Browser event object.
524
* @constructor
525
* @extends {goog.events.BrowserEvent}
526
* @final
527
*/
528
goog.events.KeyEvent = function(keyCode, charCode, repeat, browserEvent) {
529
goog.events.BrowserEvent.call(this, browserEvent);
530
this.type = goog.events.KeyHandler.EventType.KEY;
531
532
/**
533
* Keycode of key press.
534
* @type {number}
535
*/
536
this.keyCode = keyCode;
537
538
/**
539
* Unicode character code.
540
* @type {number}
541
*/
542
this.charCode = charCode;
543
544
/**
545
* True if this event was generated by keyboard auto-repeat (i.e., the user is
546
* holding the key down.)
547
* @type {boolean}
548
*/
549
this.repeat = repeat;
550
};
551
goog.inherits(goog.events.KeyEvent, goog.events.BrowserEvent);
552
553