1 | /* |
---|
2 | * Licensed to the Apache Software Foundation (ASF) under one |
---|
3 | * or more contributor license agreements. See the NOTICE file |
---|
4 | * distributed with this work for additional information |
---|
5 | * regarding copyright ownership. The ASF licenses this file |
---|
6 | * to you under the Apache License, Version 2.0 (the |
---|
7 | * "License"); you may not use this file except in compliance |
---|
8 | * with the License. You may obtain a copy of the License at |
---|
9 | * |
---|
10 | * http://www.apache.org/licenses/LICENSE-2.0 |
---|
11 | * |
---|
12 | * Unless required by applicable law or agreed to in writing, |
---|
13 | * software distributed under the License is distributed on an |
---|
14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
---|
15 | * KIND, either express or implied. See the License for the |
---|
16 | * specific language governing permissions and limitations |
---|
17 | * under the License. |
---|
18 | */ |
---|
19 | |
---|
20 | var Guacamole = Guacamole || {}; |
---|
21 | |
---|
22 | /** |
---|
23 | * Provides cross-browser and cross-keyboard keyboard for a specific element. |
---|
24 | * Browser and keyboard layout variation is abstracted away, providing events |
---|
25 | * which represent keys as their corresponding X11 keysym. |
---|
26 | * |
---|
27 | * @constructor |
---|
28 | * @param {Element} element The Element to use to provide keyboard events. |
---|
29 | */ |
---|
30 | Guacamole.Keyboard = function(element) { |
---|
31 | |
---|
32 | /** |
---|
33 | * Reference to this Guacamole.Keyboard. |
---|
34 | * @private |
---|
35 | */ |
---|
36 | var guac_keyboard = this; |
---|
37 | |
---|
38 | /** |
---|
39 | * Fired whenever the user presses a key with the element associated |
---|
40 | * with this Guacamole.Keyboard in focus. |
---|
41 | * |
---|
42 | * @event |
---|
43 | * @param {Number} keysym The keysym of the key being pressed. |
---|
44 | * @return {Boolean} true if the key event should be allowed through to the |
---|
45 | * browser, false otherwise. |
---|
46 | */ |
---|
47 | this.onkeydown = null; |
---|
48 | |
---|
49 | /** |
---|
50 | * Fired whenever the user releases a key with the element associated |
---|
51 | * with this Guacamole.Keyboard in focus. |
---|
52 | * |
---|
53 | * @event |
---|
54 | * @param {Number} keysym The keysym of the key being released. |
---|
55 | */ |
---|
56 | this.onkeyup = null; |
---|
57 | |
---|
58 | /** |
---|
59 | * A key event having a corresponding timestamp. This event is non-specific. |
---|
60 | * Its subclasses should be used instead when recording specific key |
---|
61 | * events. |
---|
62 | * |
---|
63 | * @private |
---|
64 | * @constructor |
---|
65 | */ |
---|
66 | var KeyEvent = function() { |
---|
67 | |
---|
68 | /** |
---|
69 | * Reference to this key event. |
---|
70 | */ |
---|
71 | var key_event = this; |
---|
72 | |
---|
73 | /** |
---|
74 | * An arbitrary timestamp in milliseconds, indicating this event's |
---|
75 | * position in time relative to other events. |
---|
76 | * |
---|
77 | * @type {Number} |
---|
78 | */ |
---|
79 | this.timestamp = new Date().getTime(); |
---|
80 | |
---|
81 | /** |
---|
82 | * Whether the default action of this key event should be prevented. |
---|
83 | * |
---|
84 | * @type {Boolean} |
---|
85 | */ |
---|
86 | this.defaultPrevented = false; |
---|
87 | |
---|
88 | /** |
---|
89 | * The keysym of the key associated with this key event, as determined |
---|
90 | * by a best-effort guess using available event properties and keyboard |
---|
91 | * state. |
---|
92 | * |
---|
93 | * @type {Number} |
---|
94 | */ |
---|
95 | this.keysym = null; |
---|
96 | |
---|
97 | /** |
---|
98 | * Whether the keysym value of this key event is known to be reliable. |
---|
99 | * If false, the keysym may still be valid, but it's only a best guess, |
---|
100 | * and future key events may be a better source of information. |
---|
101 | * |
---|
102 | * @type {Boolean} |
---|
103 | */ |
---|
104 | this.reliable = false; |
---|
105 | |
---|
106 | /** |
---|
107 | * Returns the number of milliseconds elapsed since this event was |
---|
108 | * received. |
---|
109 | * |
---|
110 | * @return {Number} The number of milliseconds elapsed since this |
---|
111 | * event was received. |
---|
112 | */ |
---|
113 | this.getAge = function() { |
---|
114 | return new Date().getTime() - key_event.timestamp; |
---|
115 | }; |
---|
116 | |
---|
117 | }; |
---|
118 | |
---|
119 | /** |
---|
120 | * Information related to the pressing of a key, which need not be a key |
---|
121 | * associated with a printable character. The presence or absence of any |
---|
122 | * information within this object is browser-dependent. |
---|
123 | * |
---|
124 | * @private |
---|
125 | * @constructor |
---|
126 | * @augments Guacamole.Keyboard.KeyEvent |
---|
127 | * @param {Number} keyCode The JavaScript key code of the key pressed. |
---|
128 | * @param {String} keyIdentifier The legacy DOM3 "keyIdentifier" of the key |
---|
129 | * pressed, as defined at: |
---|
130 | * http://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908/#events-Events-KeyboardEvent |
---|
131 | * @param {String} key The standard name of the key pressed, as defined at: |
---|
132 | * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent |
---|
133 | * @param {Number} location The location on the keyboard corresponding to |
---|
134 | * the key pressed, as defined at: |
---|
135 | * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent |
---|
136 | */ |
---|
137 | var KeydownEvent = function(keyCode, keyIdentifier, key, location) { |
---|
138 | |
---|
139 | // We extend KeyEvent |
---|
140 | KeyEvent.apply(this); |
---|
141 | |
---|
142 | /** |
---|
143 | * The JavaScript key code of the key pressed. |
---|
144 | * |
---|
145 | * @type {Number} |
---|
146 | */ |
---|
147 | this.keyCode = keyCode; |
---|
148 | |
---|
149 | /** |
---|
150 | * The legacy DOM3 "keyIdentifier" of the key pressed, as defined at: |
---|
151 | * http://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908/#events-Events-KeyboardEvent |
---|
152 | * |
---|
153 | * @type {String} |
---|
154 | */ |
---|
155 | this.keyIdentifier = keyIdentifier; |
---|
156 | |
---|
157 | /** |
---|
158 | * The standard name of the key pressed, as defined at: |
---|
159 | * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent |
---|
160 | * |
---|
161 | * @type {String} |
---|
162 | */ |
---|
163 | this.key = key; |
---|
164 | |
---|
165 | /** |
---|
166 | * The location on the keyboard corresponding to the key pressed, as |
---|
167 | * defined at: |
---|
168 | * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent |
---|
169 | * |
---|
170 | * @type {Number} |
---|
171 | */ |
---|
172 | this.location = location; |
---|
173 | |
---|
174 | // If key is known from keyCode or DOM3 alone, use that |
---|
175 | this.keysym = keysym_from_key_identifier(key, location) |
---|
176 | || keysym_from_keycode(keyCode, location); |
---|
177 | |
---|
178 | // DOM3 and keyCode are reliable sources if the corresponding key is |
---|
179 | // not a printable key |
---|
180 | if (this.keysym && !isPrintable(this.keysym)) |
---|
181 | this.reliable = true; |
---|
182 | |
---|
183 | // Use legacy keyIdentifier as a last resort, if it looks sane |
---|
184 | if (!this.keysym && key_identifier_sane(keyCode, keyIdentifier)) |
---|
185 | this.keysym = keysym_from_key_identifier(keyIdentifier, location, guac_keyboard.modifiers.shift); |
---|
186 | |
---|
187 | // Determine whether default action for Alt+combinations must be prevented |
---|
188 | var prevent_alt = !guac_keyboard.modifiers.ctrl |
---|
189 | && !(navigator && navigator.platform && navigator.platform.match(/^mac/i)); |
---|
190 | |
---|
191 | // Determine whether default action for Ctrl+combinations must be prevented |
---|
192 | var prevent_ctrl = !guac_keyboard.modifiers.alt; |
---|
193 | |
---|
194 | // We must rely on the (potentially buggy) keyIdentifier if preventing |
---|
195 | // the default action is important |
---|
196 | if ((prevent_ctrl && guac_keyboard.modifiers.ctrl) |
---|
197 | || (prevent_alt && guac_keyboard.modifiers.alt) |
---|
198 | || guac_keyboard.modifiers.meta |
---|
199 | || guac_keyboard.modifiers.hyper) |
---|
200 | this.reliable = true; |
---|
201 | |
---|
202 | // Record most recently known keysym by associated key code |
---|
203 | recentKeysym[keyCode] = this.keysym; |
---|
204 | |
---|
205 | }; |
---|
206 | |
---|
207 | KeydownEvent.prototype = new KeyEvent(); |
---|
208 | |
---|
209 | /** |
---|
210 | * Information related to the pressing of a key, which MUST be |
---|
211 | * associated with a printable character. The presence or absence of any |
---|
212 | * information within this object is browser-dependent. |
---|
213 | * |
---|
214 | * @private |
---|
215 | * @constructor |
---|
216 | * @augments Guacamole.Keyboard.KeyEvent |
---|
217 | * @param {Number} charCode The Unicode codepoint of the character that |
---|
218 | * would be typed by the key pressed. |
---|
219 | */ |
---|
220 | var KeypressEvent = function(charCode) { |
---|
221 | |
---|
222 | // We extend KeyEvent |
---|
223 | KeyEvent.apply(this); |
---|
224 | |
---|
225 | /** |
---|
226 | * The Unicode codepoint of the character that would be typed by the |
---|
227 | * key pressed. |
---|
228 | * |
---|
229 | * @type {Number} |
---|
230 | */ |
---|
231 | this.charCode = charCode; |
---|
232 | |
---|
233 | // Pull keysym from char code |
---|
234 | this.keysym = keysym_from_charcode(charCode); |
---|
235 | |
---|
236 | // Keypress is always reliable |
---|
237 | this.reliable = true; |
---|
238 | |
---|
239 | }; |
---|
240 | |
---|
241 | KeypressEvent.prototype = new KeyEvent(); |
---|
242 | |
---|
243 | /** |
---|
244 | * Information related to the pressing of a key, which need not be a key |
---|
245 | * associated with a printable character. The presence or absence of any |
---|
246 | * information within this object is browser-dependent. |
---|
247 | * |
---|
248 | * @private |
---|
249 | * @constructor |
---|
250 | * @augments Guacamole.Keyboard.KeyEvent |
---|
251 | * @param {Number} keyCode The JavaScript key code of the key released. |
---|
252 | * @param {String} keyIdentifier The legacy DOM3 "keyIdentifier" of the key |
---|
253 | * released, as defined at: |
---|
254 | * http://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908/#events-Events-KeyboardEvent |
---|
255 | * @param {String} key The standard name of the key released, as defined at: |
---|
256 | * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent |
---|
257 | * @param {Number} location The location on the keyboard corresponding to |
---|
258 | * the key released, as defined at: |
---|
259 | * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent |
---|
260 | */ |
---|
261 | var KeyupEvent = function(keyCode, keyIdentifier, key, location) { |
---|
262 | |
---|
263 | // We extend KeyEvent |
---|
264 | KeyEvent.apply(this); |
---|
265 | |
---|
266 | /** |
---|
267 | * The JavaScript key code of the key released. |
---|
268 | * |
---|
269 | * @type {Number} |
---|
270 | */ |
---|
271 | this.keyCode = keyCode; |
---|
272 | |
---|
273 | /** |
---|
274 | * The legacy DOM3 "keyIdentifier" of the key released, as defined at: |
---|
275 | * http://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908/#events-Events-KeyboardEvent |
---|
276 | * |
---|
277 | * @type {String} |
---|
278 | */ |
---|
279 | this.keyIdentifier = keyIdentifier; |
---|
280 | |
---|
281 | /** |
---|
282 | * The standard name of the key released, as defined at: |
---|
283 | * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent |
---|
284 | * |
---|
285 | * @type {String} |
---|
286 | */ |
---|
287 | this.key = key; |
---|
288 | |
---|
289 | /** |
---|
290 | * The location on the keyboard corresponding to the key released, as |
---|
291 | * defined at: |
---|
292 | * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent |
---|
293 | * |
---|
294 | * @type {Number} |
---|
295 | */ |
---|
296 | this.location = location; |
---|
297 | |
---|
298 | // If key is known from keyCode or DOM3 alone, use that |
---|
299 | this.keysym = recentKeysym[keyCode] |
---|
300 | || keysym_from_keycode(keyCode, location) |
---|
301 | || keysym_from_key_identifier(key, location); // keyCode is still more reliable for keyup when dead keys are in use |
---|
302 | |
---|
303 | // Keyup is as reliable as it will ever be |
---|
304 | this.reliable = true; |
---|
305 | |
---|
306 | }; |
---|
307 | |
---|
308 | KeyupEvent.prototype = new KeyEvent(); |
---|
309 | |
---|
310 | /** |
---|
311 | * An array of recorded events, which can be instances of the private |
---|
312 | * KeydownEvent, KeypressEvent, and KeyupEvent classes. |
---|
313 | * |
---|
314 | * @private |
---|
315 | * @type {KeyEvent[]} |
---|
316 | */ |
---|
317 | var eventLog = []; |
---|
318 | |
---|
319 | /** |
---|
320 | * Map of known JavaScript keycodes which do not map to typable characters |
---|
321 | * to their X11 keysym equivalents. |
---|
322 | * @private |
---|
323 | */ |
---|
324 | var keycodeKeysyms = { |
---|
325 | 8: [0xFF08], // backspace |
---|
326 | 9: [0xFF09], // tab |
---|
327 | 12: [0xFF0B, 0xFF0B, 0xFF0B, 0xFFB5], // clear / KP 5 |
---|
328 | 13: [0xFF0D], // enter |
---|
329 | 16: [0xFFE1, 0xFFE1, 0xFFE2], // shift |
---|
330 | 17: [0xFFE3, 0xFFE3, 0xFFE4], // ctrl |
---|
331 | 18: [0xFFE9, 0xFFE9, 0xFE03], // alt |
---|
332 | 19: [0xFF13], // pause/break |
---|
333 | 20: [0xFFE5], // caps lock |
---|
334 | 27: [0xFF1B], // escape |
---|
335 | 32: [0x0020], // space |
---|
336 | 33: [0xFF55, 0xFF55, 0xFF55, 0xFFB9], // page up / KP 9 |
---|
337 | 34: [0xFF56, 0xFF56, 0xFF56, 0xFFB3], // page down / KP 3 |
---|
338 | 35: [0xFF57, 0xFF57, 0xFF57, 0xFFB1], // end / KP 1 |
---|
339 | 36: [0xFF50, 0xFF50, 0xFF50, 0xFFB7], // home / KP 7 |
---|
340 | 37: [0xFF51, 0xFF51, 0xFF51, 0xFFB4], // left arrow / KP 4 |
---|
341 | 38: [0xFF52, 0xFF52, 0xFF52, 0xFFB8], // up arrow / KP 8 |
---|
342 | 39: [0xFF53, 0xFF53, 0xFF53, 0xFFB6], // right arrow / KP 6 |
---|
343 | 40: [0xFF54, 0xFF54, 0xFF54, 0xFFB2], // down arrow / KP 2 |
---|
344 | 45: [0xFF63, 0xFF63, 0xFF63, 0xFFB0], // insert / KP 0 |
---|
345 | 46: [0xFFFF, 0xFFFF, 0xFFFF, 0xFFAE], // delete / KP decimal |
---|
346 | 91: [0xFFEB], // left window key (hyper_l) |
---|
347 | 92: [0xFF67], // right window key (menu key?) |
---|
348 | 93: null, // select key |
---|
349 | 96: [0xFFB0], // KP 0 |
---|
350 | 97: [0xFFB1], // KP 1 |
---|
351 | 98: [0xFFB2], // KP 2 |
---|
352 | 99: [0xFFB3], // KP 3 |
---|
353 | 100: [0xFFB4], // KP 4 |
---|
354 | 101: [0xFFB5], // KP 5 |
---|
355 | 102: [0xFFB6], // KP 6 |
---|
356 | 103: [0xFFB7], // KP 7 |
---|
357 | 104: [0xFFB8], // KP 8 |
---|
358 | 105: [0xFFB9], // KP 9 |
---|
359 | 106: [0xFFAA], // KP multiply |
---|
360 | 107: [0xFFAB], // KP add |
---|
361 | 109: [0xFFAD], // KP subtract |
---|
362 | 110: [0xFFAE], // KP decimal |
---|
363 | 111: [0xFFAF], // KP divide |
---|
364 | 112: [0xFFBE], // f1 |
---|
365 | 113: [0xFFBF], // f2 |
---|
366 | 114: [0xFFC0], // f3 |
---|
367 | 115: [0xFFC1], // f4 |
---|
368 | 116: [0xFFC2], // f5 |
---|
369 | 117: [0xFFC3], // f6 |
---|
370 | 118: [0xFFC4], // f7 |
---|
371 | 119: [0xFFC5], // f8 |
---|
372 | 120: [0xFFC6], // f9 |
---|
373 | 121: [0xFFC7], // f10 |
---|
374 | 122: [0xFFC8], // f11 |
---|
375 | 123: [0xFFC9], // f12 |
---|
376 | 144: [0xFF7F], // num lock |
---|
377 | 145: [0xFF14], // scroll lock |
---|
378 | 225: [0xFE03] // altgraph (iso_level3_shift) |
---|
379 | }; |
---|
380 | |
---|
381 | /** |
---|
382 | * Map of known JavaScript keyidentifiers which do not map to typable |
---|
383 | * characters to their unshifted X11 keysym equivalents. |
---|
384 | * @private |
---|
385 | */ |
---|
386 | var keyidentifier_keysym = { |
---|
387 | "Again": [0xFF66], |
---|
388 | "AllCandidates": [0xFF3D], |
---|
389 | "Alphanumeric": [0xFF30], |
---|
390 | "Alt": [0xFFE9, 0xFFE9, 0xFE03], |
---|
391 | "Attn": [0xFD0E], |
---|
392 | "AltGraph": [0xFE03], |
---|
393 | "ArrowDown": [0xFF54], |
---|
394 | "ArrowLeft": [0xFF51], |
---|
395 | "ArrowRight": [0xFF53], |
---|
396 | "ArrowUp": [0xFF52], |
---|
397 | "Backspace": [0xFF08], |
---|
398 | "CapsLock": [0xFFE5], |
---|
399 | "Cancel": [0xFF69], |
---|
400 | "Clear": [0xFF0B], |
---|
401 | "Convert": [0xFF21], |
---|
402 | "Copy": [0xFD15], |
---|
403 | "Crsel": [0xFD1C], |
---|
404 | "CrSel": [0xFD1C], |
---|
405 | "CodeInput": [0xFF37], |
---|
406 | "Compose": [0xFF20], |
---|
407 | "Control": [0xFFE3, 0xFFE3, 0xFFE4], |
---|
408 | "ContextMenu": [0xFF67], |
---|
409 | "DeadGrave": [0xFE50], |
---|
410 | "DeadAcute": [0xFE51], |
---|
411 | "DeadCircumflex": [0xFE52], |
---|
412 | "DeadTilde": [0xFE53], |
---|
413 | "DeadMacron": [0xFE54], |
---|
414 | "DeadBreve": [0xFE55], |
---|
415 | "DeadAboveDot": [0xFE56], |
---|
416 | "DeadUmlaut": [0xFE57], |
---|
417 | "DeadAboveRing": [0xFE58], |
---|
418 | "DeadDoubleacute": [0xFE59], |
---|
419 | "DeadCaron": [0xFE5A], |
---|
420 | "DeadCedilla": [0xFE5B], |
---|
421 | "DeadOgonek": [0xFE5C], |
---|
422 | "DeadIota": [0xFE5D], |
---|
423 | "DeadVoicedSound": [0xFE5E], |
---|
424 | "DeadSemivoicedSound": [0xFE5F], |
---|
425 | "Delete": [0xFFFF], |
---|
426 | "Down": [0xFF54], |
---|
427 | "End": [0xFF57], |
---|
428 | "Enter": [0xFF0D], |
---|
429 | "EraseEof": [0xFD06], |
---|
430 | "Escape": [0xFF1B], |
---|
431 | "Execute": [0xFF62], |
---|
432 | "Exsel": [0xFD1D], |
---|
433 | "ExSel": [0xFD1D], |
---|
434 | "F1": [0xFFBE], |
---|
435 | "F2": [0xFFBF], |
---|
436 | "F3": [0xFFC0], |
---|
437 | "F4": [0xFFC1], |
---|
438 | "F5": [0xFFC2], |
---|
439 | "F6": [0xFFC3], |
---|
440 | "F7": [0xFFC4], |
---|
441 | "F8": [0xFFC5], |
---|
442 | "F9": [0xFFC6], |
---|
443 | "F10": [0xFFC7], |
---|
444 | "F11": [0xFFC8], |
---|
445 | "F12": [0xFFC9], |
---|
446 | "F13": [0xFFCA], |
---|
447 | "F14": [0xFFCB], |
---|
448 | "F15": [0xFFCC], |
---|
449 | "F16": [0xFFCD], |
---|
450 | "F17": [0xFFCE], |
---|
451 | "F18": [0xFFCF], |
---|
452 | "F19": [0xFFD0], |
---|
453 | "F20": [0xFFD1], |
---|
454 | "F21": [0xFFD2], |
---|
455 | "F22": [0xFFD3], |
---|
456 | "F23": [0xFFD4], |
---|
457 | "F24": [0xFFD5], |
---|
458 | "Find": [0xFF68], |
---|
459 | "GroupFirst": [0xFE0C], |
---|
460 | "GroupLast": [0xFE0E], |
---|
461 | "GroupNext": [0xFE08], |
---|
462 | "GroupPrevious": [0xFE0A], |
---|
463 | "FullWidth": null, |
---|
464 | "HalfWidth": null, |
---|
465 | "HangulMode": [0xFF31], |
---|
466 | "Hankaku": [0xFF29], |
---|
467 | "HanjaMode": [0xFF34], |
---|
468 | "Help": [0xFF6A], |
---|
469 | "Hiragana": [0xFF25], |
---|
470 | "HiraganaKatakana": [0xFF27], |
---|
471 | "Home": [0xFF50], |
---|
472 | "Hyper": [0xFFED, 0xFFED, 0xFFEE], |
---|
473 | "Insert": [0xFF63], |
---|
474 | "JapaneseHiragana": [0xFF25], |
---|
475 | "JapaneseKatakana": [0xFF26], |
---|
476 | "JapaneseRomaji": [0xFF24], |
---|
477 | "JunjaMode": [0xFF38], |
---|
478 | "KanaMode": [0xFF2D], |
---|
479 | "KanjiMode": [0xFF21], |
---|
480 | "Katakana": [0xFF26], |
---|
481 | "Left": [0xFF51], |
---|
482 | "Meta": [0xFFE7, 0xFFE7, 0xFFE8], |
---|
483 | "ModeChange": [0xFF7E], |
---|
484 | "NumLock": [0xFF7F], |
---|
485 | "PageDown": [0xFF56], |
---|
486 | "PageUp": [0xFF55], |
---|
487 | "Pause": [0xFF13], |
---|
488 | "Play": [0xFD16], |
---|
489 | "PreviousCandidate": [0xFF3E], |
---|
490 | "PrintScreen": [0xFD1D], |
---|
491 | "Redo": [0xFF66], |
---|
492 | "Right": [0xFF53], |
---|
493 | "RomanCharacters": null, |
---|
494 | "Scroll": [0xFF14], |
---|
495 | "Select": [0xFF60], |
---|
496 | "Separator": [0xFFAC], |
---|
497 | "Shift": [0xFFE1, 0xFFE1, 0xFFE2], |
---|
498 | "SingleCandidate": [0xFF3C], |
---|
499 | "Super": [0xFFEB, 0xFFEB, 0xFFEC], |
---|
500 | "Tab": [0xFF09], |
---|
501 | "Up": [0xFF52], |
---|
502 | "Undo": [0xFF65], |
---|
503 | "Win": [0xFFEB], |
---|
504 | "Zenkaku": [0xFF28], |
---|
505 | "ZenkakuHankaku": [0xFF2A] |
---|
506 | }; |
---|
507 | |
---|
508 | /** |
---|
509 | * All keysyms which should not repeat when held down. |
---|
510 | * @private |
---|
511 | */ |
---|
512 | var no_repeat = { |
---|
513 | 0xFE03: true, // ISO Level 3 Shift (AltGr) |
---|
514 | 0xFFE1: true, // Left shift |
---|
515 | 0xFFE2: true, // Right shift |
---|
516 | 0xFFE3: true, // Left ctrl |
---|
517 | 0xFFE4: true, // Right ctrl |
---|
518 | 0xFFE7: true, // Left meta |
---|
519 | 0xFFE8: true, // Right meta |
---|
520 | 0xFFE9: true, // Left alt |
---|
521 | 0xFFEA: true, // Right alt |
---|
522 | 0xFFEB: true, // Left hyper |
---|
523 | 0xFFEC: true // Right hyper |
---|
524 | }; |
---|
525 | |
---|
526 | /** |
---|
527 | * All modifiers and their states. |
---|
528 | */ |
---|
529 | this.modifiers = new Guacamole.Keyboard.ModifierState(); |
---|
530 | |
---|
531 | /** |
---|
532 | * The state of every key, indexed by keysym. If a particular key is |
---|
533 | * pressed, the value of pressed for that keysym will be true. If a key |
---|
534 | * is not currently pressed, it will not be defined. |
---|
535 | */ |
---|
536 | this.pressed = {}; |
---|
537 | |
---|
538 | /** |
---|
539 | * The last result of calling the onkeydown handler for each key, indexed |
---|
540 | * by keysym. This is used to prevent/allow default actions for key events, |
---|
541 | * even when the onkeydown handler cannot be called again because the key |
---|
542 | * is (theoretically) still pressed. |
---|
543 | * |
---|
544 | * @private |
---|
545 | */ |
---|
546 | var last_keydown_result = {}; |
---|
547 | |
---|
548 | /** |
---|
549 | * The keysym most recently associated with a given keycode when keydown |
---|
550 | * fired. This object maps keycodes to keysyms. |
---|
551 | * |
---|
552 | * @private |
---|
553 | * @type {Object.<Number, Number>} |
---|
554 | */ |
---|
555 | var recentKeysym = {}; |
---|
556 | |
---|
557 | /** |
---|
558 | * Timeout before key repeat starts. |
---|
559 | * @private |
---|
560 | */ |
---|
561 | var key_repeat_timeout = null; |
---|
562 | |
---|
563 | /** |
---|
564 | * Interval which presses and releases the last key pressed while that |
---|
565 | * key is still being held down. |
---|
566 | * @private |
---|
567 | */ |
---|
568 | var key_repeat_interval = null; |
---|
569 | |
---|
570 | /** |
---|
571 | * Given an array of keysyms indexed by location, returns the keysym |
---|
572 | * for the given location, or the keysym for the standard location if |
---|
573 | * undefined. |
---|
574 | * |
---|
575 | * @private |
---|
576 | * @param {Number[]} keysyms |
---|
577 | * An array of keysyms, where the index of the keysym in the array is |
---|
578 | * the location value. |
---|
579 | * |
---|
580 | * @param {Number} location |
---|
581 | * The location on the keyboard corresponding to the key pressed, as |
---|
582 | * defined at: http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent |
---|
583 | */ |
---|
584 | var get_keysym = function get_keysym(keysyms, location) { |
---|
585 | |
---|
586 | if (!keysyms) |
---|
587 | return null; |
---|
588 | |
---|
589 | return keysyms[location] || keysyms[0]; |
---|
590 | }; |
---|
591 | |
---|
592 | /** |
---|
593 | * Returns true if the given keysym corresponds to a printable character, |
---|
594 | * false otherwise. |
---|
595 | * |
---|
596 | * @param {Number} keysym |
---|
597 | * The keysym to check. |
---|
598 | * |
---|
599 | * @returns {Boolean} |
---|
600 | * true if the given keysym corresponds to a printable character, |
---|
601 | * false otherwise. |
---|
602 | */ |
---|
603 | var isPrintable = function isPrintable(keysym) { |
---|
604 | |
---|
605 | // Keysyms with Unicode equivalents are printable |
---|
606 | return (keysym >= 0x00 && keysym <= 0xFF) |
---|
607 | || (keysym & 0xFFFF0000) === 0x01000000; |
---|
608 | |
---|
609 | }; |
---|
610 | |
---|
611 | function keysym_from_key_identifier(identifier, location, shifted) { |
---|
612 | |
---|
613 | if (!identifier) |
---|
614 | return null; |
---|
615 | |
---|
616 | var typedCharacter; |
---|
617 | |
---|
618 | // If identifier is U+xxxx, decode Unicode character |
---|
619 | var unicodePrefixLocation = identifier.indexOf("U+"); |
---|
620 | if (unicodePrefixLocation >= 0) { |
---|
621 | var hex = identifier.substring(unicodePrefixLocation+2); |
---|
622 | typedCharacter = String.fromCharCode(parseInt(hex, 16)); |
---|
623 | } |
---|
624 | |
---|
625 | // If single character and not keypad, use that as typed character |
---|
626 | else if (identifier.length === 1 && location !== 3) |
---|
627 | typedCharacter = identifier; |
---|
628 | |
---|
629 | // Otherwise, look up corresponding keysym |
---|
630 | else |
---|
631 | return get_keysym(keyidentifier_keysym[identifier], location); |
---|
632 | |
---|
633 | // Alter case if necessary |
---|
634 | if (shifted === true) |
---|
635 | typedCharacter = typedCharacter.toUpperCase(); |
---|
636 | else if (shifted === false) |
---|
637 | typedCharacter = typedCharacter.toLowerCase(); |
---|
638 | |
---|
639 | // Get codepoint |
---|
640 | var codepoint = typedCharacter.charCodeAt(0); |
---|
641 | return keysym_from_charcode(codepoint); |
---|
642 | |
---|
643 | } |
---|
644 | |
---|
645 | function isControlCharacter(codepoint) { |
---|
646 | return codepoint <= 0x1F || (codepoint >= 0x7F && codepoint <= 0x9F); |
---|
647 | } |
---|
648 | |
---|
649 | function keysym_from_charcode(codepoint) { |
---|
650 | |
---|
651 | // Keysyms for control characters |
---|
652 | if (isControlCharacter(codepoint)) return 0xFF00 | codepoint; |
---|
653 | |
---|
654 | // Keysyms for ASCII chars |
---|
655 | if (codepoint >= 0x0000 && codepoint <= 0x00FF) |
---|
656 | return codepoint; |
---|
657 | |
---|
658 | // Keysyms for Unicode |
---|
659 | if (codepoint >= 0x0100 && codepoint <= 0x10FFFF) |
---|
660 | return 0x01000000 | codepoint; |
---|
661 | |
---|
662 | return null; |
---|
663 | |
---|
664 | } |
---|
665 | |
---|
666 | function keysym_from_keycode(keyCode, location) { |
---|
667 | return get_keysym(keycodeKeysyms[keyCode], location); |
---|
668 | } |
---|
669 | |
---|
670 | /** |
---|
671 | * Heuristically detects if the legacy keyIdentifier property of |
---|
672 | * a keydown/keyup event looks incorrectly derived. Chrome, and |
---|
673 | * presumably others, will produce the keyIdentifier by assuming |
---|
674 | * the keyCode is the Unicode codepoint for that key. This is not |
---|
675 | * correct in all cases. |
---|
676 | * |
---|
677 | * @private |
---|
678 | * @param {Number} keyCode |
---|
679 | * The keyCode from a browser keydown/keyup event. |
---|
680 | * |
---|
681 | * @param {String} keyIdentifier |
---|
682 | * The legacy keyIdentifier from a browser keydown/keyup event. |
---|
683 | * |
---|
684 | * @returns {Boolean} |
---|
685 | * true if the keyIdentifier looks sane, false if the keyIdentifier |
---|
686 | * appears incorrectly derived or is missing entirely. |
---|
687 | */ |
---|
688 | var key_identifier_sane = function key_identifier_sane(keyCode, keyIdentifier) { |
---|
689 | |
---|
690 | // Missing identifier is not sane |
---|
691 | if (!keyIdentifier) |
---|
692 | return false; |
---|
693 | |
---|
694 | // Assume non-Unicode keyIdentifier values are sane |
---|
695 | var unicodePrefixLocation = keyIdentifier.indexOf("U+"); |
---|
696 | if (unicodePrefixLocation === -1) |
---|
697 | return true; |
---|
698 | |
---|
699 | // If the Unicode codepoint isn't identical to the keyCode, |
---|
700 | // then the identifier is likely correct |
---|
701 | var codepoint = parseInt(keyIdentifier.substring(unicodePrefixLocation+2), 16); |
---|
702 | if (keyCode !== codepoint) |
---|
703 | return true; |
---|
704 | |
---|
705 | // The keyCodes for A-Z and 0-9 are actually identical to their |
---|
706 | // Unicode codepoints |
---|
707 | if ((keyCode >= 65 && keyCode <= 90) || (keyCode >= 48 && keyCode <= 57)) |
---|
708 | return true; |
---|
709 | |
---|
710 | // The keyIdentifier does NOT appear sane |
---|
711 | return false; |
---|
712 | |
---|
713 | }; |
---|
714 | |
---|
715 | /** |
---|
716 | * Marks a key as pressed, firing the keydown event if registered. Key |
---|
717 | * repeat for the pressed key will start after a delay if that key is |
---|
718 | * not a modifier. The return value of this function depends on the |
---|
719 | * return value of the keydown event handler, if any. |
---|
720 | * |
---|
721 | * @param {Number} keysym The keysym of the key to press. |
---|
722 | * @return {Boolean} true if event should NOT be canceled, false otherwise. |
---|
723 | */ |
---|
724 | this.press = function(keysym) { |
---|
725 | |
---|
726 | // Don't bother with pressing the key if the key is unknown |
---|
727 | if (keysym === null) return; |
---|
728 | |
---|
729 | // Only press if released |
---|
730 | if (!guac_keyboard.pressed[keysym]) { |
---|
731 | |
---|
732 | // Mark key as pressed |
---|
733 | guac_keyboard.pressed[keysym] = true; |
---|
734 | |
---|
735 | // Send key event |
---|
736 | if (guac_keyboard.onkeydown) { |
---|
737 | var result = guac_keyboard.onkeydown(keysym); |
---|
738 | last_keydown_result[keysym] = result; |
---|
739 | |
---|
740 | // Stop any current repeat |
---|
741 | window.clearTimeout(key_repeat_timeout); |
---|
742 | window.clearInterval(key_repeat_interval); |
---|
743 | |
---|
744 | // Repeat after a delay as long as pressed |
---|
745 | if (!no_repeat[keysym]) |
---|
746 | key_repeat_timeout = window.setTimeout(function() { |
---|
747 | key_repeat_interval = window.setInterval(function() { |
---|
748 | guac_keyboard.onkeyup(keysym); |
---|
749 | guac_keyboard.onkeydown(keysym); |
---|
750 | }, 50); |
---|
751 | }, 500); |
---|
752 | |
---|
753 | return result; |
---|
754 | } |
---|
755 | } |
---|
756 | |
---|
757 | // Return the last keydown result by default, resort to false if unknown |
---|
758 | return last_keydown_result[keysym] || false; |
---|
759 | |
---|
760 | }; |
---|
761 | |
---|
762 | /** |
---|
763 | * Marks a key as released, firing the keyup event if registered. |
---|
764 | * |
---|
765 | * @param {Number} keysym The keysym of the key to release. |
---|
766 | */ |
---|
767 | this.release = function(keysym) { |
---|
768 | |
---|
769 | // Only release if pressed |
---|
770 | if (guac_keyboard.pressed[keysym]) { |
---|
771 | |
---|
772 | // Mark key as released |
---|
773 | delete guac_keyboard.pressed[keysym]; |
---|
774 | |
---|
775 | // Stop repeat |
---|
776 | window.clearTimeout(key_repeat_timeout); |
---|
777 | window.clearInterval(key_repeat_interval); |
---|
778 | |
---|
779 | // Send key event |
---|
780 | if (keysym !== null && guac_keyboard.onkeyup) |
---|
781 | guac_keyboard.onkeyup(keysym); |
---|
782 | |
---|
783 | } |
---|
784 | |
---|
785 | }; |
---|
786 | |
---|
787 | /** |
---|
788 | * Resets the state of this keyboard, releasing all keys, and firing keyup |
---|
789 | * events for each released key. |
---|
790 | */ |
---|
791 | this.reset = function() { |
---|
792 | |
---|
793 | // Release all pressed keys |
---|
794 | for (var keysym in guac_keyboard.pressed) |
---|
795 | guac_keyboard.release(parseInt(keysym)); |
---|
796 | |
---|
797 | // Clear event log |
---|
798 | eventLog = []; |
---|
799 | |
---|
800 | }; |
---|
801 | |
---|
802 | /** |
---|
803 | * Given a keyboard event, updates the local modifier state and remote |
---|
804 | * key state based on the modifier flags within the event. This function |
---|
805 | * pays no attention to keycodes. |
---|
806 | * |
---|
807 | * @private |
---|
808 | * @param {KeyboardEvent} e |
---|
809 | * The keyboard event containing the flags to update. |
---|
810 | */ |
---|
811 | var update_modifier_state = function update_modifier_state(e) { |
---|
812 | |
---|
813 | // Get state |
---|
814 | var state = Guacamole.Keyboard.ModifierState.fromKeyboardEvent(e); |
---|
815 | |
---|
816 | // Release alt if implicitly released |
---|
817 | if (guac_keyboard.modifiers.alt && state.alt === false) { |
---|
818 | guac_keyboard.release(0xFFE9); // Left alt |
---|
819 | guac_keyboard.release(0xFFEA); // Right alt |
---|
820 | guac_keyboard.release(0xFE03); // AltGr |
---|
821 | } |
---|
822 | |
---|
823 | // Release shift if implicitly released |
---|
824 | if (guac_keyboard.modifiers.shift && state.shift === false) { |
---|
825 | guac_keyboard.release(0xFFE1); // Left shift |
---|
826 | guac_keyboard.release(0xFFE2); // Right shift |
---|
827 | } |
---|
828 | |
---|
829 | // Release ctrl if implicitly released |
---|
830 | if (guac_keyboard.modifiers.ctrl && state.ctrl === false) { |
---|
831 | guac_keyboard.release(0xFFE3); // Left ctrl |
---|
832 | guac_keyboard.release(0xFFE4); // Right ctrl |
---|
833 | } |
---|
834 | |
---|
835 | // Release meta if implicitly released |
---|
836 | if (guac_keyboard.modifiers.meta && state.meta === false) { |
---|
837 | guac_keyboard.release(0xFFE7); // Left meta |
---|
838 | guac_keyboard.release(0xFFE8); // Right meta |
---|
839 | } |
---|
840 | |
---|
841 | // Release hyper if implicitly released |
---|
842 | if (guac_keyboard.modifiers.hyper && state.hyper === false) { |
---|
843 | guac_keyboard.release(0xFFEB); // Left hyper |
---|
844 | guac_keyboard.release(0xFFEC); // Right hyper |
---|
845 | } |
---|
846 | |
---|
847 | // Update state |
---|
848 | guac_keyboard.modifiers = state; |
---|
849 | |
---|
850 | }; |
---|
851 | |
---|
852 | /** |
---|
853 | * Reads through the event log, removing events from the head of the log |
---|
854 | * when the corresponding true key presses are known (or as known as they |
---|
855 | * can be). |
---|
856 | * |
---|
857 | * @private |
---|
858 | * @return {Boolean} Whether the default action of the latest event should |
---|
859 | * be prevented. |
---|
860 | */ |
---|
861 | function interpret_events() { |
---|
862 | |
---|
863 | // Do not prevent default if no event could be interpreted |
---|
864 | var handled_event = interpret_event(); |
---|
865 | if (!handled_event) |
---|
866 | return false; |
---|
867 | |
---|
868 | // Interpret as much as possible |
---|
869 | var last_event; |
---|
870 | do { |
---|
871 | last_event = handled_event; |
---|
872 | handled_event = interpret_event(); |
---|
873 | } while (handled_event !== null); |
---|
874 | |
---|
875 | return last_event.defaultPrevented; |
---|
876 | |
---|
877 | } |
---|
878 | |
---|
879 | /** |
---|
880 | * Releases Ctrl+Alt, if both are currently pressed and the given keysym |
---|
881 | * looks like a key that may require AltGr. |
---|
882 | * |
---|
883 | * @private |
---|
884 | * @param {Number} keysym The key that was just pressed. |
---|
885 | */ |
---|
886 | var release_simulated_altgr = function release_simulated_altgr(keysym) { |
---|
887 | |
---|
888 | // Both Ctrl+Alt must be pressed if simulated AltGr is in use |
---|
889 | if (!guac_keyboard.modifiers.ctrl || !guac_keyboard.modifiers.alt) |
---|
890 | return; |
---|
891 | |
---|
892 | // Assume [A-Z] never require AltGr |
---|
893 | if (keysym >= 0x0041 && keysym <= 0x005A) |
---|
894 | return; |
---|
895 | |
---|
896 | // Assume [a-z] never require AltGr |
---|
897 | if (keysym >= 0x0061 && keysym <= 0x007A) |
---|
898 | return; |
---|
899 | |
---|
900 | // Release Ctrl+Alt if the keysym is printable |
---|
901 | if (keysym <= 0xFF || (keysym & 0xFF000000) === 0x01000000) { |
---|
902 | guac_keyboard.release(0xFFE3); // Left ctrl |
---|
903 | guac_keyboard.release(0xFFE4); // Right ctrl |
---|
904 | guac_keyboard.release(0xFFE9); // Left alt |
---|
905 | guac_keyboard.release(0xFFEA); // Right alt |
---|
906 | } |
---|
907 | |
---|
908 | }; |
---|
909 | |
---|
910 | /** |
---|
911 | * Reads through the event log, interpreting the first event, if possible, |
---|
912 | * and returning that event. If no events can be interpreted, due to a |
---|
913 | * total lack of events or the need for more events, null is returned. Any |
---|
914 | * interpreted events are automatically removed from the log. |
---|
915 | * |
---|
916 | * @private |
---|
917 | * @return {KeyEvent} |
---|
918 | * The first key event in the log, if it can be interpreted, or null |
---|
919 | * otherwise. |
---|
920 | */ |
---|
921 | var interpret_event = function interpret_event() { |
---|
922 | |
---|
923 | // Peek at first event in log |
---|
924 | var first = eventLog[0]; |
---|
925 | if (!first) |
---|
926 | return null; |
---|
927 | |
---|
928 | // Keydown event |
---|
929 | if (first instanceof KeydownEvent) { |
---|
930 | |
---|
931 | var keysym = null; |
---|
932 | var accepted_events = []; |
---|
933 | |
---|
934 | // If event itself is reliable, no need to wait for other events |
---|
935 | if (first.reliable) { |
---|
936 | keysym = first.keysym; |
---|
937 | accepted_events = eventLog.splice(0, 1); |
---|
938 | } |
---|
939 | |
---|
940 | // If keydown is immediately followed by a keypress, use the indicated character |
---|
941 | else if (eventLog[1] instanceof KeypressEvent) { |
---|
942 | keysym = eventLog[1].keysym; |
---|
943 | accepted_events = eventLog.splice(0, 2); |
---|
944 | } |
---|
945 | |
---|
946 | // If keydown is immediately followed by anything else, then no |
---|
947 | // keypress can possibly occur to clarify this event, and we must |
---|
948 | // handle it now |
---|
949 | else if (eventLog[1]) { |
---|
950 | keysym = first.keysym; |
---|
951 | accepted_events = eventLog.splice(0, 1); |
---|
952 | } |
---|
953 | |
---|
954 | // Fire a key press if valid events were found |
---|
955 | if (accepted_events.length > 0) { |
---|
956 | |
---|
957 | if (keysym) { |
---|
958 | |
---|
959 | // Fire event |
---|
960 | release_simulated_altgr(keysym); |
---|
961 | var defaultPrevented = !guac_keyboard.press(keysym); |
---|
962 | recentKeysym[first.keyCode] = keysym; |
---|
963 | |
---|
964 | // If a key is pressed while meta is held down, the keyup will |
---|
965 | // never be sent in Chrome, so send it now. (bug #108404) |
---|
966 | if (guac_keyboard.modifiers.meta && keysym !== 0xFFE7 && keysym !== 0xFFE8) |
---|
967 | guac_keyboard.release(keysym); |
---|
968 | |
---|
969 | // Record whether default was prevented |
---|
970 | for (var i=0; i<accepted_events.length; i++) |
---|
971 | accepted_events[i].defaultPrevented = defaultPrevented; |
---|
972 | |
---|
973 | } |
---|
974 | |
---|
975 | return first; |
---|
976 | |
---|
977 | } |
---|
978 | |
---|
979 | } // end if keydown |
---|
980 | |
---|
981 | // Keyup event |
---|
982 | else if (first instanceof KeyupEvent) { |
---|
983 | |
---|
984 | // Release specific key if known |
---|
985 | var keysym = first.keysym; |
---|
986 | if (keysym) { |
---|
987 | guac_keyboard.release(keysym); |
---|
988 | first.defaultPrevented = true; |
---|
989 | } |
---|
990 | |
---|
991 | // Otherwise, fall back to releasing all keys |
---|
992 | else { |
---|
993 | guac_keyboard.reset(); |
---|
994 | return first; |
---|
995 | } |
---|
996 | |
---|
997 | return eventLog.shift(); |
---|
998 | |
---|
999 | } // end if keyup |
---|
1000 | |
---|
1001 | // Ignore any other type of event (keypress by itself is invalid) |
---|
1002 | else |
---|
1003 | return eventLog.shift(); |
---|
1004 | |
---|
1005 | // No event interpreted |
---|
1006 | return null; |
---|
1007 | |
---|
1008 | }; |
---|
1009 | |
---|
1010 | /** |
---|
1011 | * Returns the keyboard location of the key associated with the given |
---|
1012 | * keyboard event. The location differentiates key events which otherwise |
---|
1013 | * have the same keycode, such as left shift vs. right shift. |
---|
1014 | * |
---|
1015 | * @private |
---|
1016 | * @param {KeyboardEvent} e |
---|
1017 | * A JavaScript keyboard event, as received through the DOM via a |
---|
1018 | * "keydown", "keyup", or "keypress" handler. |
---|
1019 | * |
---|
1020 | * @returns {Number} |
---|
1021 | * The location of the key event on the keyboard, as defined at: |
---|
1022 | * http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent |
---|
1023 | */ |
---|
1024 | var getEventLocation = function getEventLocation(e) { |
---|
1025 | |
---|
1026 | // Use standard location, if possible |
---|
1027 | if ('location' in e) |
---|
1028 | return e.location; |
---|
1029 | |
---|
1030 | // Failing that, attempt to use deprecated keyLocation |
---|
1031 | if ('keyLocation' in e) |
---|
1032 | return e.keyLocation; |
---|
1033 | |
---|
1034 | // If no location is available, assume left side |
---|
1035 | return 0; |
---|
1036 | |
---|
1037 | }; |
---|
1038 | |
---|
1039 | // When key pressed |
---|
1040 | element.addEventListener("keydown", function(e) { |
---|
1041 | |
---|
1042 | // Only intercept if handler set |
---|
1043 | if (!guac_keyboard.onkeydown) return; |
---|
1044 | |
---|
1045 | var keyCode; |
---|
1046 | if (window.event) keyCode = window.event.keyCode; |
---|
1047 | else if (e.which) keyCode = e.which; |
---|
1048 | |
---|
1049 | // Fix modifier states |
---|
1050 | update_modifier_state(e); |
---|
1051 | |
---|
1052 | // Ignore (but do not prevent) the "composition" keycode sent by some |
---|
1053 | // browsers when an IME is in use (see: http://lists.w3.org/Archives/Public/www-dom/2010JulSep/att-0182/keyCode-spec.html) |
---|
1054 | if (keyCode === 229) |
---|
1055 | return; |
---|
1056 | |
---|
1057 | // Log event |
---|
1058 | var keydownEvent = new KeydownEvent(keyCode, e.keyIdentifier, e.key, getEventLocation(e)); |
---|
1059 | eventLog.push(keydownEvent); |
---|
1060 | |
---|
1061 | // Interpret as many events as possible, prevent default if indicated |
---|
1062 | if (interpret_events()) |
---|
1063 | e.preventDefault(); |
---|
1064 | |
---|
1065 | }, true); |
---|
1066 | |
---|
1067 | // When key pressed |
---|
1068 | element.addEventListener("keypress", function(e) { |
---|
1069 | |
---|
1070 | // Only intercept if handler set |
---|
1071 | if (!guac_keyboard.onkeydown && !guac_keyboard.onkeyup) return; |
---|
1072 | |
---|
1073 | var charCode; |
---|
1074 | if (window.event) charCode = window.event.keyCode; |
---|
1075 | else if (e.which) charCode = e.which; |
---|
1076 | |
---|
1077 | // Fix modifier states |
---|
1078 | update_modifier_state(e); |
---|
1079 | |
---|
1080 | // Log event |
---|
1081 | var keypressEvent = new KeypressEvent(charCode); |
---|
1082 | eventLog.push(keypressEvent); |
---|
1083 | |
---|
1084 | // Interpret as many events as possible, prevent default if indicated |
---|
1085 | if (interpret_events()) |
---|
1086 | e.preventDefault(); |
---|
1087 | |
---|
1088 | }, true); |
---|
1089 | |
---|
1090 | // When key released |
---|
1091 | element.addEventListener("keyup", function(e) { |
---|
1092 | |
---|
1093 | // Only intercept if handler set |
---|
1094 | if (!guac_keyboard.onkeyup) return; |
---|
1095 | |
---|
1096 | e.preventDefault(); |
---|
1097 | |
---|
1098 | var keyCode; |
---|
1099 | if (window.event) keyCode = window.event.keyCode; |
---|
1100 | else if (e.which) keyCode = e.which; |
---|
1101 | |
---|
1102 | // Fix modifier states |
---|
1103 | update_modifier_state(e); |
---|
1104 | |
---|
1105 | // Log event, call for interpretation |
---|
1106 | var keyupEvent = new KeyupEvent(keyCode, e.keyIdentifier, e.key, getEventLocation(e)); |
---|
1107 | eventLog.push(keyupEvent); |
---|
1108 | interpret_events(); |
---|
1109 | |
---|
1110 | }, true); |
---|
1111 | |
---|
1112 | }; |
---|
1113 | |
---|
1114 | /** |
---|
1115 | * The state of all supported keyboard modifiers. |
---|
1116 | * @constructor |
---|
1117 | */ |
---|
1118 | Guacamole.Keyboard.ModifierState = function() { |
---|
1119 | |
---|
1120 | /** |
---|
1121 | * Whether shift is currently pressed. |
---|
1122 | * @type {Boolean} |
---|
1123 | */ |
---|
1124 | this.shift = false; |
---|
1125 | |
---|
1126 | /** |
---|
1127 | * Whether ctrl is currently pressed. |
---|
1128 | * @type {Boolean} |
---|
1129 | */ |
---|
1130 | this.ctrl = false; |
---|
1131 | |
---|
1132 | /** |
---|
1133 | * Whether alt is currently pressed. |
---|
1134 | * @type {Boolean} |
---|
1135 | */ |
---|
1136 | this.alt = false; |
---|
1137 | |
---|
1138 | /** |
---|
1139 | * Whether meta (apple key) is currently pressed. |
---|
1140 | * @type {Boolean} |
---|
1141 | */ |
---|
1142 | this.meta = false; |
---|
1143 | |
---|
1144 | /** |
---|
1145 | * Whether hyper (windows key) is currently pressed. |
---|
1146 | * @type {Boolean} |
---|
1147 | */ |
---|
1148 | this.hyper = false; |
---|
1149 | |
---|
1150 | }; |
---|
1151 | |
---|
1152 | /** |
---|
1153 | * Returns the modifier state applicable to the keyboard event given. |
---|
1154 | * |
---|
1155 | * @param {KeyboardEvent} e The keyboard event to read. |
---|
1156 | * @returns {Guacamole.Keyboard.ModifierState} The current state of keyboard |
---|
1157 | * modifiers. |
---|
1158 | */ |
---|
1159 | Guacamole.Keyboard.ModifierState.fromKeyboardEvent = function(e) { |
---|
1160 | |
---|
1161 | var state = new Guacamole.Keyboard.ModifierState(); |
---|
1162 | |
---|
1163 | // Assign states from old flags |
---|
1164 | state.shift = e.shiftKey; |
---|
1165 | state.ctrl = e.ctrlKey; |
---|
1166 | state.alt = e.altKey; |
---|
1167 | state.meta = e.metaKey; |
---|
1168 | |
---|
1169 | // Use DOM3 getModifierState() for others |
---|
1170 | if (e.getModifierState) { |
---|
1171 | state.hyper = e.getModifierState("OS") |
---|
1172 | || e.getModifierState("Super") |
---|
1173 | || e.getModifierState("Hyper") |
---|
1174 | || e.getModifierState("Win"); |
---|
1175 | } |
---|
1176 | |
---|
1177 | return state; |
---|
1178 | |
---|
1179 | }; |
---|