@illgrenoble/webx-client 1.11.0 → 1.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/GuacamoleKeyboard.js +1551 -0
- package/dist/WebXClient.d.ts +1 -1
- package/dist/common/WebXImageBlenderFunc.d.ts +26 -0
- package/dist/common/WebXSubImage.d.ts +50 -0
- package/dist/common/WebXWindowProperties.d.ts +44 -0
- package/dist/common/index.d.ts +3 -0
- package/dist/display/WebXCursor.d.ts +5 -17
- package/dist/display/WebXCursor.js +22 -33
- package/dist/display/WebXCursor.js.map +1 -1
- package/dist/display/WebXCursorFactory.d.ts +2 -2
- package/dist/display/WebXDisplay.d.ts +11 -9
- package/dist/display/WebXDisplay.js +10 -4
- package/dist/display/WebXDisplay.js.map +1 -1
- package/dist/display/WebXDisplayOverlay.d.ts +10 -0
- package/dist/display/WebXDisplayOverlay.js +31 -0
- package/dist/display/WebXDisplayOverlay.js.map +1 -0
- package/dist/display/WebXTexture.d.ts +6 -0
- package/dist/display/WebXTextureFunc.d.ts +3 -0
- package/dist/display/WebXWindow.d.ts +4 -4
- package/dist/display/WebXWindow.js +12 -9
- package/dist/display/WebXWindow.js.map +1 -1
- package/dist/display/WebXWindowImageFactory.d.ts +31 -0
- package/dist/display/index.d.ts +0 -6
- package/dist/index.d.ts +2 -0
- package/dist/message/WebXCursorImageMessage.d.ts +3 -3
- package/dist/message/WebXImageMessage.d.ts +4 -4
- package/dist/message/WebXMessage.d.ts +13 -1
- package/dist/message/WebXShapeMessage.d.ts +3 -3
- package/dist/message/WebXSubImagesMessage.d.ts +1 -1
- package/dist/message/WebXWindowsMessage.d.ts +1 -1
- package/dist/renderer/WebXCanvasRenderer.d.ts +1 -1
- package/dist/renderer/WebXImageBlender.d.ts +30 -0
- package/dist/renderer/WebXImageBlenderFunc.d.ts +8 -0
- package/dist/renderer/WebXImageBlenderWorker.d.ts +1 -0
- package/dist/renderer/WebXWindowCanvas.d.ts +27 -8
- package/dist/renderer/WebXWindowCanvas.js +10 -4
- package/dist/renderer/WebXWindowCanvas.js.map +1 -1
- package/dist/renderer/index.d.ts +1 -1
- package/dist/texture/WebXTexture.d.ts +18 -0
- package/dist/texture/WebXTextureFactory.d.ts +23 -0
- package/dist/texture/WebXTextureFunc.d.ts +3 -0
- package/dist/texture/index.d.ts +3 -0
- package/dist/transport/WebXBinarySerializer.d.ts +13 -8
- package/dist/transport/WebXMessageBuffer.d.ts +4 -0
- package/dist/transport/WebXMessageDecoder.d.ts +2 -5
- package/dist/transport/WebXMessageDecoderWorker.d.ts +1 -0
- package/dist/transport/WebXMessageFunc.d.ts +22 -0
- package/dist/transport/index.d.ts +0 -2
- package/dist/tunnel/WebXTunnel.d.ts +5 -2
- package/dist/tunnel/WebXWebSocketTunnel.d.ts +2 -4
- package/dist/webx-client.cjs +5391 -0
- package/dist/webx-client.cjs.map +1 -0
- package/dist/webx-client.esm.js +5322 -0
- package/dist/webx-client.esm.js.map +1 -0
- package/package.json +11 -9
- package/dist/renderer/WebXBlenderWorker.d.ts +0 -1
- package/dist/renderer/WebXBlenderWorker.js +0 -33
- package/dist/renderer/WebXBlenderWorker.js.map +0 -1
- package/dist/renderer/WebXWindowBlender.d.ts +0 -11
- package/dist/renderer/WebXWindowBlender.js +0 -27
- package/dist/renderer/WebXWindowBlender.js.map +0 -1
- package/dist/renderer/blender.d.ts +0 -5
- package/dist/renderer/blender.js +0 -201
- package/dist/renderer/blender.js.map +0 -1
|
@@ -0,0 +1,1551 @@
|
|
|
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
|
+
/* eslint-disable */
|
|
21
|
+
|
|
22
|
+
var Guacamole = Guacamole || {};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Provides cross-browser and cross-keyboard keyboard for a specific element.
|
|
26
|
+
* Browser and keyboard layout variation is abstracted away, providing events
|
|
27
|
+
* which represent keys as their corresponding X11 keysym.
|
|
28
|
+
*
|
|
29
|
+
* @constructor
|
|
30
|
+
* @param {Element|Document} [element]
|
|
31
|
+
* The Element to use to provide keyboard events. If omitted, at least one
|
|
32
|
+
* Element must be manually provided through the listenTo() function for
|
|
33
|
+
* the Guacamole.Keyboard instance to have any effect.
|
|
34
|
+
*/
|
|
35
|
+
Guacamole.Keyboard = function Keyboard(element) {
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Reference to this Guacamole.Keyboard.
|
|
39
|
+
*
|
|
40
|
+
* @private
|
|
41
|
+
* @type {!Guacamole.Keyboard}
|
|
42
|
+
*/
|
|
43
|
+
var guac_keyboard = this;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* An integer value which uniquely identifies this Guacamole.Keyboard
|
|
47
|
+
* instance with respect to other Guacamole.Keyboard instances.
|
|
48
|
+
*
|
|
49
|
+
* @private
|
|
50
|
+
* @type {!number}
|
|
51
|
+
*/
|
|
52
|
+
var guacKeyboardID = Guacamole.Keyboard._nextID++;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The name of the property which is added to event objects via markEvent()
|
|
56
|
+
* to note that they have already been handled by this Guacamole.Keyboard.
|
|
57
|
+
*
|
|
58
|
+
* @private
|
|
59
|
+
* @constant
|
|
60
|
+
* @type {!string}
|
|
61
|
+
*/
|
|
62
|
+
var EVENT_MARKER = '_GUAC_KEYBOARD_HANDLED_BY_' + guacKeyboardID;
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Fired whenever the user presses a key with the element associated
|
|
66
|
+
* with this Guacamole.Keyboard in focus.
|
|
67
|
+
*
|
|
68
|
+
* @event
|
|
69
|
+
* @param {!number} keysym
|
|
70
|
+
* The keysym of the key being pressed.
|
|
71
|
+
*
|
|
72
|
+
* @return {!boolean}
|
|
73
|
+
* true if the key event should be allowed through to the browser,
|
|
74
|
+
* false otherwise.
|
|
75
|
+
*/
|
|
76
|
+
this.onkeydown = null;
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Fired whenever the user releases a key with the element associated
|
|
80
|
+
* with this Guacamole.Keyboard in focus.
|
|
81
|
+
*
|
|
82
|
+
* @event
|
|
83
|
+
* @param {!number} keysym
|
|
84
|
+
* The keysym of the key being released.
|
|
85
|
+
*/
|
|
86
|
+
this.onkeyup = null;
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Set of known platform-specific or browser-specific quirks which must be
|
|
90
|
+
* accounted for to properly interpret key events, even if the only way to
|
|
91
|
+
* reliably detect that quirk is to platform/browser-sniff.
|
|
92
|
+
*
|
|
93
|
+
* @private
|
|
94
|
+
* @type {!Object.<string, boolean>}
|
|
95
|
+
*/
|
|
96
|
+
var quirks = {
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Whether keyup events are universally unreliable.
|
|
100
|
+
*
|
|
101
|
+
* @type {!boolean}
|
|
102
|
+
*/
|
|
103
|
+
keyupUnreliable: false,
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Whether the Alt key is actually a modifier for typable keys and is
|
|
107
|
+
* thus never used for keyboard shortcuts.
|
|
108
|
+
*
|
|
109
|
+
* @type {!boolean}
|
|
110
|
+
*/
|
|
111
|
+
altIsTypableOnly: false,
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Whether we can rely on receiving a keyup event for the Caps Lock
|
|
115
|
+
* key.
|
|
116
|
+
*
|
|
117
|
+
* @type {!boolean}
|
|
118
|
+
*/
|
|
119
|
+
capsLockKeyupUnreliable: false
|
|
120
|
+
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Set quirk flags depending on platform/browser, if such information is
|
|
124
|
+
// available
|
|
125
|
+
if (navigator && navigator.platform) {
|
|
126
|
+
|
|
127
|
+
// All keyup events are unreliable on iOS (sadly)
|
|
128
|
+
if (navigator.platform.match(/ipad|iphone|ipod/i))
|
|
129
|
+
quirks.keyupUnreliable = true;
|
|
130
|
+
|
|
131
|
+
// The Alt key on Mac is never used for keyboard shortcuts, and the
|
|
132
|
+
// Caps Lock key never dispatches keyup events
|
|
133
|
+
else if (navigator.platform.match(/^mac/i)) {
|
|
134
|
+
quirks.altIsTypableOnly = true;
|
|
135
|
+
quirks.capsLockKeyupUnreliable = true;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* A key event having a corresponding timestamp. This event is non-specific.
|
|
142
|
+
* Its subclasses should be used instead when recording specific key
|
|
143
|
+
* events.
|
|
144
|
+
*
|
|
145
|
+
* @private
|
|
146
|
+
* @constructor
|
|
147
|
+
* @param {KeyboardEvent} [orig]
|
|
148
|
+
* The relevant DOM keyboard event.
|
|
149
|
+
*/
|
|
150
|
+
var KeyEvent = function KeyEvent(orig) {
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Reference to this key event.
|
|
154
|
+
*
|
|
155
|
+
* @private
|
|
156
|
+
* @type {!KeyEvent}
|
|
157
|
+
*/
|
|
158
|
+
var key_event = this;
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* The JavaScript key code of the key pressed. For most events (keydown
|
|
162
|
+
* and keyup), this is a scancode-like value related to the position of
|
|
163
|
+
* the key on the US English "Qwerty" keyboard. For keypress events,
|
|
164
|
+
* this is the Unicode codepoint of the character that would be typed
|
|
165
|
+
* by the key pressed.
|
|
166
|
+
*
|
|
167
|
+
* @type {!number}
|
|
168
|
+
*/
|
|
169
|
+
this.keyCode = orig ? (orig.which || orig.keyCode) : 0;
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* The legacy DOM3 "keyIdentifier" of the key pressed, as defined at:
|
|
173
|
+
* http://www.w3.org/TR/2009/WD-DOM-Level-3-Events-20090908/#events-Events-KeyboardEvent
|
|
174
|
+
*
|
|
175
|
+
* @type {!string}
|
|
176
|
+
*/
|
|
177
|
+
this.keyIdentifier = orig && orig.keyIdentifier;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* The standard name of the key pressed, as defined at:
|
|
181
|
+
* http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent
|
|
182
|
+
*
|
|
183
|
+
* @type {!string}
|
|
184
|
+
*/
|
|
185
|
+
this.key = orig && orig.key;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* The location on the keyboard corresponding to the key pressed, as
|
|
189
|
+
* defined at:
|
|
190
|
+
* http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent
|
|
191
|
+
*
|
|
192
|
+
* @type {!number}
|
|
193
|
+
*/
|
|
194
|
+
this.location = orig ? getEventLocation(orig) : 0;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* The state of all local keyboard modifiers at the time this event was
|
|
198
|
+
* received.
|
|
199
|
+
*
|
|
200
|
+
* @type {!Guacamole.Keyboard.ModifierState}
|
|
201
|
+
*/
|
|
202
|
+
this.modifiers = orig ? Guacamole.Keyboard.ModifierState.fromKeyboardEvent(orig) : new Guacamole.Keyboard.ModifierState();
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* An arbitrary timestamp in milliseconds, indicating this event's
|
|
206
|
+
* position in time relative to other events.
|
|
207
|
+
*
|
|
208
|
+
* @type {!number}
|
|
209
|
+
*/
|
|
210
|
+
this.timestamp = new Date().getTime();
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Whether the default action of this key event should be prevented.
|
|
214
|
+
*
|
|
215
|
+
* @type {!boolean}
|
|
216
|
+
*/
|
|
217
|
+
this.defaultPrevented = false;
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* The keysym of the key associated with this key event, as determined
|
|
221
|
+
* by a best-effort guess using available event properties and keyboard
|
|
222
|
+
* state.
|
|
223
|
+
*
|
|
224
|
+
* @type {number}
|
|
225
|
+
*/
|
|
226
|
+
this.keysym = null;
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Whether the keysym value of this key event is known to be reliable.
|
|
230
|
+
* If false, the keysym may still be valid, but it's only a best guess,
|
|
231
|
+
* and future key events may be a better source of information.
|
|
232
|
+
*
|
|
233
|
+
* @type {!boolean}
|
|
234
|
+
*/
|
|
235
|
+
this.reliable = false;
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Returns the number of milliseconds elapsed since this event was
|
|
239
|
+
* received.
|
|
240
|
+
*
|
|
241
|
+
* @return {!number}
|
|
242
|
+
* The number of milliseconds elapsed since this event was
|
|
243
|
+
* received.
|
|
244
|
+
*/
|
|
245
|
+
this.getAge = function() {
|
|
246
|
+
return new Date().getTime() - key_event.timestamp;
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Information related to the pressing of a key, which need not be a key
|
|
253
|
+
* associated with a printable character. The presence or absence of any
|
|
254
|
+
* information within this object is browser-dependent.
|
|
255
|
+
*
|
|
256
|
+
* @private
|
|
257
|
+
* @constructor
|
|
258
|
+
* @augments Guacamole.Keyboard.KeyEvent
|
|
259
|
+
* @param {!KeyboardEvent} orig
|
|
260
|
+
* The relevant DOM "keydown" event.
|
|
261
|
+
*/
|
|
262
|
+
var KeydownEvent = function KeydownEvent(orig) {
|
|
263
|
+
|
|
264
|
+
// We extend KeyEvent
|
|
265
|
+
KeyEvent.call(this, orig);
|
|
266
|
+
|
|
267
|
+
// If key is known from keyCode or DOM3 alone, use that
|
|
268
|
+
this.keysym = keysym_from_key_identifier(this.key, this.location)
|
|
269
|
+
|| keysym_from_keycode(this.keyCode, this.location);
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Whether the keyup following this keydown event is known to be
|
|
273
|
+
* reliable. If false, we cannot rely on the keyup event to occur.
|
|
274
|
+
*
|
|
275
|
+
* @type {!boolean}
|
|
276
|
+
*/
|
|
277
|
+
this.keyupReliable = !quirks.keyupUnreliable;
|
|
278
|
+
|
|
279
|
+
// DOM3 and keyCode are reliable sources if the corresponding key is
|
|
280
|
+
// not a printable key
|
|
281
|
+
if (this.keysym && !isPrintable(this.keysym))
|
|
282
|
+
this.reliable = true;
|
|
283
|
+
|
|
284
|
+
// Use legacy keyIdentifier as a last resort, if it looks sane
|
|
285
|
+
if (!this.keysym && key_identifier_sane(this.keyCode, this.keyIdentifier))
|
|
286
|
+
this.keysym = keysym_from_key_identifier(this.keyIdentifier, this.location, this.modifiers.shift);
|
|
287
|
+
|
|
288
|
+
// If a key is pressed while meta is held down, the keyup will
|
|
289
|
+
// never be sent in Chrome (bug #108404)
|
|
290
|
+
if (this.modifiers.meta && this.keysym !== 0xFFE7 && this.keysym !== 0xFFE8)
|
|
291
|
+
this.keyupReliable = false;
|
|
292
|
+
|
|
293
|
+
// We cannot rely on receiving keyup for Caps Lock on certain platforms
|
|
294
|
+
else if (this.keysym === 0xFFE5 && quirks.capsLockKeyupUnreliable)
|
|
295
|
+
this.keyupReliable = false;
|
|
296
|
+
|
|
297
|
+
// Determine whether default action for Alt+combinations must be prevented
|
|
298
|
+
var prevent_alt = !this.modifiers.ctrl && !quirks.altIsTypableOnly;
|
|
299
|
+
|
|
300
|
+
// If alt is typeable only, and this is actually an alt key event, treat as AltGr instead
|
|
301
|
+
if (quirks.altIsTypableOnly && (this.keysym === 0xFFE9 || this.keysym === 0xFFEA))
|
|
302
|
+
this.keysym = 0xFE03;
|
|
303
|
+
|
|
304
|
+
// Determine whether default action for Ctrl+combinations must be prevented
|
|
305
|
+
var prevent_ctrl = !this.modifiers.alt;
|
|
306
|
+
|
|
307
|
+
// We must rely on the (potentially buggy) keyIdentifier if preventing
|
|
308
|
+
// the default action is important
|
|
309
|
+
if ((prevent_ctrl && this.modifiers.ctrl)
|
|
310
|
+
|| (prevent_alt && this.modifiers.alt)
|
|
311
|
+
|| this.modifiers.meta
|
|
312
|
+
|| this.modifiers.hyper)
|
|
313
|
+
this.reliable = true;
|
|
314
|
+
|
|
315
|
+
// Record most recently known keysym by associated key code
|
|
316
|
+
recentKeysym[this.keyCode] = this.keysym;
|
|
317
|
+
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
KeydownEvent.prototype = new KeyEvent();
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Information related to the pressing of a key, which MUST be
|
|
324
|
+
* associated with a printable character. The presence or absence of any
|
|
325
|
+
* information within this object is browser-dependent.
|
|
326
|
+
*
|
|
327
|
+
* @private
|
|
328
|
+
* @constructor
|
|
329
|
+
* @augments Guacamole.Keyboard.KeyEvent
|
|
330
|
+
* @param {!KeyboardEvent} orig
|
|
331
|
+
* The relevant DOM "keypress" event.
|
|
332
|
+
*/
|
|
333
|
+
var KeypressEvent = function KeypressEvent(orig) {
|
|
334
|
+
|
|
335
|
+
// We extend KeyEvent
|
|
336
|
+
KeyEvent.call(this, orig);
|
|
337
|
+
|
|
338
|
+
// Pull keysym from char code
|
|
339
|
+
this.keysym = keysym_from_charcode(this.keyCode);
|
|
340
|
+
|
|
341
|
+
// Keypress is always reliable
|
|
342
|
+
this.reliable = true;
|
|
343
|
+
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
KeypressEvent.prototype = new KeyEvent();
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Information related to the releasing of a key, which need not be a key
|
|
350
|
+
* associated with a printable character. The presence or absence of any
|
|
351
|
+
* information within this object is browser-dependent.
|
|
352
|
+
*
|
|
353
|
+
* @private
|
|
354
|
+
* @constructor
|
|
355
|
+
* @augments Guacamole.Keyboard.KeyEvent
|
|
356
|
+
* @param {!KeyboardEvent} orig
|
|
357
|
+
* The relevant DOM "keyup" event.
|
|
358
|
+
*/
|
|
359
|
+
var KeyupEvent = function KeyupEvent(orig) {
|
|
360
|
+
|
|
361
|
+
// We extend KeyEvent
|
|
362
|
+
KeyEvent.call(this, orig);
|
|
363
|
+
|
|
364
|
+
// If key is known from keyCode or DOM3 alone, use that (keyCode is
|
|
365
|
+
// still more reliable for keyup when dead keys are in use)
|
|
366
|
+
this.keysym = keysym_from_keycode(this.keyCode, this.location)
|
|
367
|
+
|| keysym_from_key_identifier(this.key, this.location);
|
|
368
|
+
|
|
369
|
+
// Fall back to the most recently pressed keysym associated with the
|
|
370
|
+
// keyCode if the inferred key doesn't seem to actually be pressed
|
|
371
|
+
if (!guac_keyboard.pressed[this.keysym])
|
|
372
|
+
this.keysym = recentKeysym[this.keyCode] || this.keysym;
|
|
373
|
+
|
|
374
|
+
// Keyup is as reliable as it will ever be
|
|
375
|
+
this.reliable = true;
|
|
376
|
+
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
KeyupEvent.prototype = new KeyEvent();
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* An array of recorded events, which can be instances of the private
|
|
383
|
+
* KeydownEvent, KeypressEvent, and KeyupEvent classes.
|
|
384
|
+
*
|
|
385
|
+
* @private
|
|
386
|
+
* @type {!KeyEvent[]}
|
|
387
|
+
*/
|
|
388
|
+
var eventLog = [];
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Map of known JavaScript keycodes which do not map to typable characters
|
|
392
|
+
* to their X11 keysym equivalents.
|
|
393
|
+
*
|
|
394
|
+
* @private
|
|
395
|
+
* @type {!Object.<number, number[]>}
|
|
396
|
+
*/
|
|
397
|
+
var keycodeKeysyms = {
|
|
398
|
+
8: [0xFF08], // backspace
|
|
399
|
+
9: [0xFF09], // tab
|
|
400
|
+
12: [0xFF0B, 0xFF0B, 0xFF0B, 0xFFB5], // clear / KP 5
|
|
401
|
+
13: [0xFF0D], // enter
|
|
402
|
+
16: [0xFFE1, 0xFFE1, 0xFFE2], // shift
|
|
403
|
+
17: [0xFFE3, 0xFFE3, 0xFFE4], // ctrl
|
|
404
|
+
18: [0xFFE9, 0xFFE9, 0xFFEA], // alt
|
|
405
|
+
19: [0xFF13], // pause/break
|
|
406
|
+
20: [0xFFE5], // caps lock
|
|
407
|
+
27: [0xFF1B], // escape
|
|
408
|
+
32: [0x0020], // space
|
|
409
|
+
33: [0xFF55, 0xFF55, 0xFF55, 0xFFB9], // page up / KP 9
|
|
410
|
+
34: [0xFF56, 0xFF56, 0xFF56, 0xFFB3], // page down / KP 3
|
|
411
|
+
35: [0xFF57, 0xFF57, 0xFF57, 0xFFB1], // end / KP 1
|
|
412
|
+
36: [0xFF50, 0xFF50, 0xFF50, 0xFFB7], // home / KP 7
|
|
413
|
+
37: [0xFF51, 0xFF51, 0xFF51, 0xFFB4], // left arrow / KP 4
|
|
414
|
+
38: [0xFF52, 0xFF52, 0xFF52, 0xFFB8], // up arrow / KP 8
|
|
415
|
+
39: [0xFF53, 0xFF53, 0xFF53, 0xFFB6], // right arrow / KP 6
|
|
416
|
+
40: [0xFF54, 0xFF54, 0xFF54, 0xFFB2], // down arrow / KP 2
|
|
417
|
+
45: [0xFF63, 0xFF63, 0xFF63, 0xFFB0], // insert / KP 0
|
|
418
|
+
46: [0xFFFF, 0xFFFF, 0xFFFF, 0xFFAE], // delete / KP decimal
|
|
419
|
+
91: [0xFFE7], // left windows/command key (meta_l)
|
|
420
|
+
92: [0xFFE8], // right window/command key (meta_r)
|
|
421
|
+
93: [0xFF67], // menu key
|
|
422
|
+
96: [0xFFB0], // KP 0
|
|
423
|
+
97: [0xFFB1], // KP 1
|
|
424
|
+
98: [0xFFB2], // KP 2
|
|
425
|
+
99: [0xFFB3], // KP 3
|
|
426
|
+
100: [0xFFB4], // KP 4
|
|
427
|
+
101: [0xFFB5], // KP 5
|
|
428
|
+
102: [0xFFB6], // KP 6
|
|
429
|
+
103: [0xFFB7], // KP 7
|
|
430
|
+
104: [0xFFB8], // KP 8
|
|
431
|
+
105: [0xFFB9], // KP 9
|
|
432
|
+
106: [0xFFAA], // KP multiply
|
|
433
|
+
107: [0xFFAB], // KP add
|
|
434
|
+
109: [0xFFAD], // KP subtract
|
|
435
|
+
110: [0xFFAE], // KP decimal
|
|
436
|
+
111: [0xFFAF], // KP divide
|
|
437
|
+
112: [0xFFBE], // f1
|
|
438
|
+
113: [0xFFBF], // f2
|
|
439
|
+
114: [0xFFC0], // f3
|
|
440
|
+
115: [0xFFC1], // f4
|
|
441
|
+
116: [0xFFC2], // f5
|
|
442
|
+
117: [0xFFC3], // f6
|
|
443
|
+
118: [0xFFC4], // f7
|
|
444
|
+
119: [0xFFC5], // f8
|
|
445
|
+
120: [0xFFC6], // f9
|
|
446
|
+
121: [0xFFC7], // f10
|
|
447
|
+
122: [0xFFC8], // f11
|
|
448
|
+
123: [0xFFC9], // f12
|
|
449
|
+
144: [0xFF7F], // num lock
|
|
450
|
+
145: [0xFF14], // scroll lock
|
|
451
|
+
225: [0xFE03] // altgraph (iso_level3_shift)
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Map of known JavaScript keyidentifiers which do not map to typable
|
|
456
|
+
* characters to their unshifted X11 keysym equivalents.
|
|
457
|
+
*
|
|
458
|
+
* @private
|
|
459
|
+
* @type {!Object.<string, number[]>}
|
|
460
|
+
*/
|
|
461
|
+
var keyidentifier_keysym = {
|
|
462
|
+
"Again": [0xFF66],
|
|
463
|
+
"AllCandidates": [0xFF3D],
|
|
464
|
+
"Alphanumeric": [0xFF30],
|
|
465
|
+
"Alt": [0xFFE9, 0xFFE9, 0xFFEA],
|
|
466
|
+
"Attn": [0xFD0E],
|
|
467
|
+
"AltGraph": [0xFE03],
|
|
468
|
+
"ArrowDown": [0xFF54],
|
|
469
|
+
"ArrowLeft": [0xFF51],
|
|
470
|
+
"ArrowRight": [0xFF53],
|
|
471
|
+
"ArrowUp": [0xFF52],
|
|
472
|
+
"Backspace": [0xFF08],
|
|
473
|
+
"CapsLock": [0xFFE5],
|
|
474
|
+
"Cancel": [0xFF69],
|
|
475
|
+
"Clear": [0xFF0B],
|
|
476
|
+
"Convert": [0xFF23],
|
|
477
|
+
"Copy": [0xFD15],
|
|
478
|
+
"Crsel": [0xFD1C],
|
|
479
|
+
"CrSel": [0xFD1C],
|
|
480
|
+
"CodeInput": [0xFF37],
|
|
481
|
+
"Compose": [0xFF20],
|
|
482
|
+
"Control": [0xFFE3, 0xFFE3, 0xFFE4],
|
|
483
|
+
"ContextMenu": [0xFF67],
|
|
484
|
+
"Delete": [0xFFFF],
|
|
485
|
+
"Down": [0xFF54],
|
|
486
|
+
"End": [0xFF57],
|
|
487
|
+
"Enter": [0xFF0D],
|
|
488
|
+
"EraseEof": [0xFD06],
|
|
489
|
+
"Escape": [0xFF1B],
|
|
490
|
+
"Execute": [0xFF62],
|
|
491
|
+
"Exsel": [0xFD1D],
|
|
492
|
+
"ExSel": [0xFD1D],
|
|
493
|
+
"F1": [0xFFBE],
|
|
494
|
+
"F2": [0xFFBF],
|
|
495
|
+
"F3": [0xFFC0],
|
|
496
|
+
"F4": [0xFFC1],
|
|
497
|
+
"F5": [0xFFC2],
|
|
498
|
+
"F6": [0xFFC3],
|
|
499
|
+
"F7": [0xFFC4],
|
|
500
|
+
"F8": [0xFFC5],
|
|
501
|
+
"F9": [0xFFC6],
|
|
502
|
+
"F10": [0xFFC7],
|
|
503
|
+
"F11": [0xFFC8],
|
|
504
|
+
"F12": [0xFFC9],
|
|
505
|
+
"F13": [0xFFCA],
|
|
506
|
+
"F14": [0xFFCB],
|
|
507
|
+
"F15": [0xFFCC],
|
|
508
|
+
"F16": [0xFFCD],
|
|
509
|
+
"F17": [0xFFCE],
|
|
510
|
+
"F18": [0xFFCF],
|
|
511
|
+
"F19": [0xFFD0],
|
|
512
|
+
"F20": [0xFFD1],
|
|
513
|
+
"F21": [0xFFD2],
|
|
514
|
+
"F22": [0xFFD3],
|
|
515
|
+
"F23": [0xFFD4],
|
|
516
|
+
"F24": [0xFFD5],
|
|
517
|
+
"Find": [0xFF68],
|
|
518
|
+
"GroupFirst": [0xFE0C],
|
|
519
|
+
"GroupLast": [0xFE0E],
|
|
520
|
+
"GroupNext": [0xFE08],
|
|
521
|
+
"GroupPrevious": [0xFE0A],
|
|
522
|
+
"FullWidth": null,
|
|
523
|
+
"HalfWidth": null,
|
|
524
|
+
"HangulMode": [0xFF31],
|
|
525
|
+
"Hankaku": [0xFF29],
|
|
526
|
+
"HanjaMode": [0xFF34],
|
|
527
|
+
"Help": [0xFF6A],
|
|
528
|
+
"Hiragana": [0xFF25],
|
|
529
|
+
"HiraganaKatakana": [0xFF27],
|
|
530
|
+
"Home": [0xFF50],
|
|
531
|
+
"Hyper": [0xFFED, 0xFFED, 0xFFEE],
|
|
532
|
+
"Insert": [0xFF63],
|
|
533
|
+
"JapaneseHiragana": [0xFF25],
|
|
534
|
+
"JapaneseKatakana": [0xFF26],
|
|
535
|
+
"JapaneseRomaji": [0xFF24],
|
|
536
|
+
"JunjaMode": [0xFF38],
|
|
537
|
+
"KanaMode": [0xFF2D],
|
|
538
|
+
"KanjiMode": [0xFF21],
|
|
539
|
+
"Katakana": [0xFF26],
|
|
540
|
+
"Left": [0xFF51],
|
|
541
|
+
"Meta": [0xFFE7, 0xFFE7, 0xFFE8],
|
|
542
|
+
"ModeChange": [0xFF7E],
|
|
543
|
+
"NonConvert": [0xFF22],
|
|
544
|
+
"NumLock": [0xFF7F],
|
|
545
|
+
"PageDown": [0xFF56],
|
|
546
|
+
"PageUp": [0xFF55],
|
|
547
|
+
"Pause": [0xFF13],
|
|
548
|
+
"Play": [0xFD16],
|
|
549
|
+
"PreviousCandidate": [0xFF3E],
|
|
550
|
+
"PrintScreen": [0xFF61],
|
|
551
|
+
"Redo": [0xFF66],
|
|
552
|
+
"Right": [0xFF53],
|
|
553
|
+
"Romaji": [0xFF24],
|
|
554
|
+
"RomanCharacters": null,
|
|
555
|
+
"Scroll": [0xFF14],
|
|
556
|
+
"Select": [0xFF60],
|
|
557
|
+
"Separator": [0xFFAC],
|
|
558
|
+
"Shift": [0xFFE1, 0xFFE1, 0xFFE2],
|
|
559
|
+
"SingleCandidate": [0xFF3C],
|
|
560
|
+
"Super": [0xFFEB, 0xFFEB, 0xFFEC],
|
|
561
|
+
"Tab": [0xFF09],
|
|
562
|
+
"UIKeyInputDownArrow": [0xFF54],
|
|
563
|
+
"UIKeyInputEscape": [0xFF1B],
|
|
564
|
+
"UIKeyInputLeftArrow": [0xFF51],
|
|
565
|
+
"UIKeyInputRightArrow": [0xFF53],
|
|
566
|
+
"UIKeyInputUpArrow": [0xFF52],
|
|
567
|
+
"Up": [0xFF52],
|
|
568
|
+
"Undo": [0xFF65],
|
|
569
|
+
"Win": [0xFFE7, 0xFFE7, 0xFFE8],
|
|
570
|
+
"Zenkaku": [0xFF28],
|
|
571
|
+
"ZenkakuHankaku": [0xFF2A]
|
|
572
|
+
};
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* All keysyms which should not repeat when held down.
|
|
576
|
+
*
|
|
577
|
+
* @private
|
|
578
|
+
* @type {!Object.<number, boolean>}
|
|
579
|
+
*/
|
|
580
|
+
var no_repeat = {
|
|
581
|
+
0xFE03: true, // ISO Level 3 Shift (AltGr)
|
|
582
|
+
0xFFE1: true, // Left shift
|
|
583
|
+
0xFFE2: true, // Right shift
|
|
584
|
+
0xFFE3: true, // Left ctrl
|
|
585
|
+
0xFFE4: true, // Right ctrl
|
|
586
|
+
0xFFE5: true, // Caps Lock
|
|
587
|
+
0xFFE7: true, // Left meta
|
|
588
|
+
0xFFE8: true, // Right meta
|
|
589
|
+
0xFFE9: true, // Left alt
|
|
590
|
+
0xFFEA: true, // Right alt
|
|
591
|
+
0xFFEB: true, // Left super/hyper
|
|
592
|
+
0xFFEC: true // Right super/hyper
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* All modifiers and their states.
|
|
597
|
+
*
|
|
598
|
+
* @type {!Guacamole.Keyboard.ModifierState}
|
|
599
|
+
*/
|
|
600
|
+
this.modifiers = new Guacamole.Keyboard.ModifierState();
|
|
601
|
+
|
|
602
|
+
/**
|
|
603
|
+
* The state of every key, indexed by keysym. If a particular key is
|
|
604
|
+
* pressed, the value of pressed for that keysym will be true. If a key
|
|
605
|
+
* is not currently pressed, it will not be defined.
|
|
606
|
+
*
|
|
607
|
+
* @type {!Object.<number, boolean>}
|
|
608
|
+
*/
|
|
609
|
+
this.pressed = {};
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* The state of every key, indexed by keysym, for strictly those keys whose
|
|
613
|
+
* status has been indirectly determined thorugh observation of other key
|
|
614
|
+
* events. If a particular key is implicitly pressed, the value of
|
|
615
|
+
* implicitlyPressed for that keysym will be true. If a key
|
|
616
|
+
* is not currently implicitly pressed (the key is not pressed OR the state
|
|
617
|
+
* of the key is explicitly known), it will not be defined.
|
|
618
|
+
*
|
|
619
|
+
* @private
|
|
620
|
+
* @type {!Object.<number, boolean>}
|
|
621
|
+
*/
|
|
622
|
+
var implicitlyPressed = {};
|
|
623
|
+
|
|
624
|
+
/**
|
|
625
|
+
* The last result of calling the onkeydown handler for each key, indexed
|
|
626
|
+
* by keysym. This is used to prevent/allow default actions for key events,
|
|
627
|
+
* even when the onkeydown handler cannot be called again because the key
|
|
628
|
+
* is (theoretically) still pressed.
|
|
629
|
+
*
|
|
630
|
+
* @private
|
|
631
|
+
* @type {!Object.<number, boolean>}
|
|
632
|
+
*/
|
|
633
|
+
var last_keydown_result = {};
|
|
634
|
+
|
|
635
|
+
/**
|
|
636
|
+
* The keysym most recently associated with a given keycode when keydown
|
|
637
|
+
* fired. This object maps keycodes to keysyms.
|
|
638
|
+
*
|
|
639
|
+
* @private
|
|
640
|
+
* @type {!Object.<number, number>}
|
|
641
|
+
*/
|
|
642
|
+
var recentKeysym = {};
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* Timeout before key repeat starts.
|
|
646
|
+
*
|
|
647
|
+
* @private
|
|
648
|
+
* @type {number}
|
|
649
|
+
*/
|
|
650
|
+
var key_repeat_timeout = null;
|
|
651
|
+
|
|
652
|
+
/**
|
|
653
|
+
* Interval which presses and releases the last key pressed while that
|
|
654
|
+
* key is still being held down.
|
|
655
|
+
*
|
|
656
|
+
* @private
|
|
657
|
+
* @type {number}
|
|
658
|
+
*/
|
|
659
|
+
var key_repeat_interval = null;
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Given an array of keysyms indexed by location, returns the keysym
|
|
663
|
+
* for the given location, or the keysym for the standard location if
|
|
664
|
+
* undefined.
|
|
665
|
+
*
|
|
666
|
+
* @private
|
|
667
|
+
* @param {number[]} keysyms
|
|
668
|
+
* An array of keysyms, where the index of the keysym in the array is
|
|
669
|
+
* the location value.
|
|
670
|
+
*
|
|
671
|
+
* @param {!number} location
|
|
672
|
+
* The location on the keyboard corresponding to the key pressed, as
|
|
673
|
+
* defined at: http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent
|
|
674
|
+
*/
|
|
675
|
+
var get_keysym = function get_keysym(keysyms, location) {
|
|
676
|
+
|
|
677
|
+
if (!keysyms)
|
|
678
|
+
return null;
|
|
679
|
+
|
|
680
|
+
return keysyms[location] || keysyms[0];
|
|
681
|
+
};
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Returns true if the given keysym corresponds to a printable character,
|
|
685
|
+
* false otherwise.
|
|
686
|
+
*
|
|
687
|
+
* @param {!number} keysym
|
|
688
|
+
* The keysym to check.
|
|
689
|
+
*
|
|
690
|
+
* @returns {!boolean}
|
|
691
|
+
* true if the given keysym corresponds to a printable character,
|
|
692
|
+
* false otherwise.
|
|
693
|
+
*/
|
|
694
|
+
var isPrintable = function isPrintable(keysym) {
|
|
695
|
+
|
|
696
|
+
// Keysyms with Unicode equivalents are printable
|
|
697
|
+
return (keysym >= 0x00 && keysym <= 0xFF)
|
|
698
|
+
|| (keysym & 0xFFFF0000) === 0x01000000;
|
|
699
|
+
|
|
700
|
+
};
|
|
701
|
+
|
|
702
|
+
function keysym_from_key_identifier(identifier, location, shifted) {
|
|
703
|
+
|
|
704
|
+
if (!identifier)
|
|
705
|
+
return null;
|
|
706
|
+
|
|
707
|
+
var typedCharacter;
|
|
708
|
+
|
|
709
|
+
// If identifier is U+xxxx, decode Unicode character
|
|
710
|
+
var unicodePrefixLocation = identifier.indexOf("U+");
|
|
711
|
+
if (unicodePrefixLocation >= 0) {
|
|
712
|
+
var hex = identifier.substring(unicodePrefixLocation+2);
|
|
713
|
+
typedCharacter = String.fromCharCode(parseInt(hex, 16));
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// If single character and not keypad, use that as typed character
|
|
717
|
+
else if (identifier.length === 1 && location !== 3)
|
|
718
|
+
typedCharacter = identifier;
|
|
719
|
+
|
|
720
|
+
// Otherwise, look up corresponding keysym
|
|
721
|
+
else
|
|
722
|
+
return get_keysym(keyidentifier_keysym[identifier], location);
|
|
723
|
+
|
|
724
|
+
// Alter case if necessary
|
|
725
|
+
if (shifted === true)
|
|
726
|
+
typedCharacter = typedCharacter.toUpperCase();
|
|
727
|
+
else if (shifted === false)
|
|
728
|
+
typedCharacter = typedCharacter.toLowerCase();
|
|
729
|
+
|
|
730
|
+
// Get codepoint
|
|
731
|
+
var codepoint = typedCharacter.charCodeAt(0);
|
|
732
|
+
return keysym_from_charcode(codepoint);
|
|
733
|
+
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
function isControlCharacter(codepoint) {
|
|
737
|
+
return codepoint <= 0x1F || (codepoint >= 0x7F && codepoint <= 0x9F);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
function keysym_from_charcode(codepoint) {
|
|
741
|
+
|
|
742
|
+
// Keysyms for control characters
|
|
743
|
+
if (isControlCharacter(codepoint)) return 0xFF00 | codepoint;
|
|
744
|
+
|
|
745
|
+
// Keysyms for ASCII chars
|
|
746
|
+
if (codepoint >= 0x0000 && codepoint <= 0x00FF)
|
|
747
|
+
return codepoint;
|
|
748
|
+
|
|
749
|
+
// Keysyms for Unicode
|
|
750
|
+
if (codepoint >= 0x0100 && codepoint <= 0x10FFFF)
|
|
751
|
+
return 0x01000000 | codepoint;
|
|
752
|
+
|
|
753
|
+
return null;
|
|
754
|
+
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
function keysym_from_keycode(keyCode, location) {
|
|
758
|
+
return get_keysym(keycodeKeysyms[keyCode], location);
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Heuristically detects if the legacy keyIdentifier property of
|
|
763
|
+
* a keydown/keyup event looks incorrectly derived. Chrome, and
|
|
764
|
+
* presumably others, will produce the keyIdentifier by assuming
|
|
765
|
+
* the keyCode is the Unicode codepoint for that key. This is not
|
|
766
|
+
* correct in all cases.
|
|
767
|
+
*
|
|
768
|
+
* @private
|
|
769
|
+
* @param {!number} keyCode
|
|
770
|
+
* The keyCode from a browser keydown/keyup event.
|
|
771
|
+
*
|
|
772
|
+
* @param {string} keyIdentifier
|
|
773
|
+
* The legacy keyIdentifier from a browser keydown/keyup event.
|
|
774
|
+
*
|
|
775
|
+
* @returns {!boolean}
|
|
776
|
+
* true if the keyIdentifier looks sane, false if the keyIdentifier
|
|
777
|
+
* appears incorrectly derived or is missing entirely.
|
|
778
|
+
*/
|
|
779
|
+
var key_identifier_sane = function key_identifier_sane(keyCode, keyIdentifier) {
|
|
780
|
+
|
|
781
|
+
// Missing identifier is not sane
|
|
782
|
+
if (!keyIdentifier)
|
|
783
|
+
return false;
|
|
784
|
+
|
|
785
|
+
// Assume non-Unicode keyIdentifier values are sane
|
|
786
|
+
var unicodePrefixLocation = keyIdentifier.indexOf("U+");
|
|
787
|
+
if (unicodePrefixLocation === -1)
|
|
788
|
+
return true;
|
|
789
|
+
|
|
790
|
+
// If the Unicode codepoint isn't identical to the keyCode,
|
|
791
|
+
// then the identifier is likely correct
|
|
792
|
+
var codepoint = parseInt(keyIdentifier.substring(unicodePrefixLocation+2), 16);
|
|
793
|
+
if (keyCode !== codepoint)
|
|
794
|
+
return true;
|
|
795
|
+
|
|
796
|
+
// The keyCodes for A-Z and 0-9 are actually identical to their
|
|
797
|
+
// Unicode codepoints
|
|
798
|
+
if ((keyCode >= 65 && keyCode <= 90) || (keyCode >= 48 && keyCode <= 57))
|
|
799
|
+
return true;
|
|
800
|
+
|
|
801
|
+
// The keyIdentifier does NOT appear sane
|
|
802
|
+
return false;
|
|
803
|
+
|
|
804
|
+
};
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* Marks a key as pressed, firing the keydown event if registered. Key
|
|
808
|
+
* repeat for the pressed key will start after a delay if that key is
|
|
809
|
+
* not a modifier. The return value of this function depends on the
|
|
810
|
+
* return value of the keydown event handler, if any.
|
|
811
|
+
*
|
|
812
|
+
* @param {number} keysym
|
|
813
|
+
* The keysym of the key to press.
|
|
814
|
+
*
|
|
815
|
+
* @return {boolean}
|
|
816
|
+
* true if event should NOT be canceled, false otherwise.
|
|
817
|
+
*/
|
|
818
|
+
this.press = function(keysym) {
|
|
819
|
+
|
|
820
|
+
// Don't bother with pressing the key if the key is unknown
|
|
821
|
+
if (keysym === null) return;
|
|
822
|
+
|
|
823
|
+
// Only press if released
|
|
824
|
+
if (!guac_keyboard.pressed[keysym]) {
|
|
825
|
+
|
|
826
|
+
// Mark key as pressed
|
|
827
|
+
guac_keyboard.pressed[keysym] = true;
|
|
828
|
+
|
|
829
|
+
// Send key event
|
|
830
|
+
if (guac_keyboard.onkeydown) {
|
|
831
|
+
var result = guac_keyboard.onkeydown(keysym);
|
|
832
|
+
last_keydown_result[keysym] = result;
|
|
833
|
+
|
|
834
|
+
// Stop any current repeat
|
|
835
|
+
window.clearTimeout(key_repeat_timeout);
|
|
836
|
+
window.clearInterval(key_repeat_interval);
|
|
837
|
+
|
|
838
|
+
// Repeat after a delay as long as pressed
|
|
839
|
+
if (!no_repeat[keysym])
|
|
840
|
+
key_repeat_timeout = window.setTimeout(function() {
|
|
841
|
+
key_repeat_interval = window.setInterval(function() {
|
|
842
|
+
guac_keyboard.onkeyup(keysym);
|
|
843
|
+
guac_keyboard.onkeydown(keysym);
|
|
844
|
+
}, 50);
|
|
845
|
+
}, 500);
|
|
846
|
+
|
|
847
|
+
return result;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// Return the last keydown result by default, resort to false if unknown
|
|
852
|
+
return last_keydown_result[keysym] || false;
|
|
853
|
+
|
|
854
|
+
};
|
|
855
|
+
|
|
856
|
+
/**
|
|
857
|
+
* Marks a key as released, firing the keyup event if registered.
|
|
858
|
+
*
|
|
859
|
+
* @param {number} keysym
|
|
860
|
+
* The keysym of the key to release.
|
|
861
|
+
*/
|
|
862
|
+
this.release = function(keysym) {
|
|
863
|
+
|
|
864
|
+
// Only release if pressed
|
|
865
|
+
if (guac_keyboard.pressed[keysym]) {
|
|
866
|
+
|
|
867
|
+
// Mark key as released
|
|
868
|
+
delete guac_keyboard.pressed[keysym];
|
|
869
|
+
delete implicitlyPressed[keysym];
|
|
870
|
+
|
|
871
|
+
// Stop repeat
|
|
872
|
+
window.clearTimeout(key_repeat_timeout);
|
|
873
|
+
window.clearInterval(key_repeat_interval);
|
|
874
|
+
|
|
875
|
+
// Send key event
|
|
876
|
+
if (keysym !== null && guac_keyboard.onkeyup)
|
|
877
|
+
guac_keyboard.onkeyup(keysym);
|
|
878
|
+
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* Presses and releases the keys necessary to type the given string of
|
|
885
|
+
* text.
|
|
886
|
+
*
|
|
887
|
+
* @param {!string} str
|
|
888
|
+
* The string to type.
|
|
889
|
+
*/
|
|
890
|
+
this.type = function type(str) {
|
|
891
|
+
|
|
892
|
+
// Press/release the key corresponding to each character in the string
|
|
893
|
+
for (var i = 0; i < str.length; i++) {
|
|
894
|
+
|
|
895
|
+
// Determine keysym of current character
|
|
896
|
+
var codepoint = str.codePointAt ? str.codePointAt(i) : str.charCodeAt(i);
|
|
897
|
+
var keysym = keysym_from_charcode(codepoint);
|
|
898
|
+
|
|
899
|
+
// Press and release key for current character
|
|
900
|
+
guac_keyboard.press(keysym);
|
|
901
|
+
guac_keyboard.release(keysym);
|
|
902
|
+
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
};
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Resets the state of this keyboard, releasing all keys, and firing keyup
|
|
909
|
+
* events for each released key.
|
|
910
|
+
*/
|
|
911
|
+
this.reset = function() {
|
|
912
|
+
|
|
913
|
+
// Release all pressed keys
|
|
914
|
+
for (var keysym in guac_keyboard.pressed)
|
|
915
|
+
guac_keyboard.release(parseInt(keysym));
|
|
916
|
+
|
|
917
|
+
// Clear event log
|
|
918
|
+
eventLog = [];
|
|
919
|
+
|
|
920
|
+
};
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* Resynchronizes the remote state of the given modifier with its
|
|
924
|
+
* corresponding local modifier state, as dictated by
|
|
925
|
+
* {@link KeyEvent#modifiers} within the given key event, by pressing or
|
|
926
|
+
* releasing keysyms.
|
|
927
|
+
*
|
|
928
|
+
* @private
|
|
929
|
+
* @param {!string} modifier
|
|
930
|
+
* The name of the {@link Guacamole.Keyboard.ModifierState} property
|
|
931
|
+
* being updated.
|
|
932
|
+
*
|
|
933
|
+
* @param {!number[]} keysyms
|
|
934
|
+
* The keysyms which represent the modifier being updated.
|
|
935
|
+
*
|
|
936
|
+
* @param {!KeyEvent} keyEvent
|
|
937
|
+
* Guacamole's current best interpretation of the key event being
|
|
938
|
+
* processed.
|
|
939
|
+
*/
|
|
940
|
+
var updateModifierState = function updateModifierState(modifier,
|
|
941
|
+
keysyms, keyEvent) {
|
|
942
|
+
|
|
943
|
+
var localState = keyEvent.modifiers[modifier];
|
|
944
|
+
var remoteState = guac_keyboard.modifiers[modifier];
|
|
945
|
+
|
|
946
|
+
var i;
|
|
947
|
+
|
|
948
|
+
// Do not trust changes in modifier state for events directly involving
|
|
949
|
+
// that modifier: (1) the flag may erroneously be cleared despite
|
|
950
|
+
// another version of the same key still being held and (2) the change
|
|
951
|
+
// in flag may be due to the current event being processed, thus
|
|
952
|
+
// updating things here is at best redundant and at worst incorrect
|
|
953
|
+
if (keysyms.indexOf(keyEvent.keysym) !== -1)
|
|
954
|
+
return;
|
|
955
|
+
|
|
956
|
+
// Release all related keys if modifier is implicitly released
|
|
957
|
+
if (remoteState && localState === false) {
|
|
958
|
+
for (i = 0; i < keysyms.length; i++) {
|
|
959
|
+
guac_keyboard.release(keysyms[i]);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// Press if modifier is implicitly pressed
|
|
964
|
+
else if (!remoteState && localState) {
|
|
965
|
+
|
|
966
|
+
// Verify that modifier flag isn't already pressed or already set
|
|
967
|
+
// due to another version of the same key being held down
|
|
968
|
+
for (i = 0; i < keysyms.length; i++) {
|
|
969
|
+
if (guac_keyboard.pressed[keysyms[i]])
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
// Mark as implicitly pressed only if there is other information
|
|
974
|
+
// within the key event relating to a different key. Some
|
|
975
|
+
// platforms, such as iOS, will send essentially empty key events
|
|
976
|
+
// for modifier keys, using only the modifier flags to signal the
|
|
977
|
+
// identity of the key.
|
|
978
|
+
var keysym = keysyms[0];
|
|
979
|
+
if (keyEvent.keysym)
|
|
980
|
+
implicitlyPressed[keysym] = true;
|
|
981
|
+
|
|
982
|
+
guac_keyboard.press(keysym);
|
|
983
|
+
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
};
|
|
987
|
+
|
|
988
|
+
/**
|
|
989
|
+
* Given a keyboard event, updates the remote key state to match the local
|
|
990
|
+
* modifier state and remote based on the modifier flags within the event.
|
|
991
|
+
* This function pays no attention to keycodes.
|
|
992
|
+
*
|
|
993
|
+
* @private
|
|
994
|
+
* @param {!KeyEvent} keyEvent
|
|
995
|
+
* Guacamole's current best interpretation of the key event being
|
|
996
|
+
* processed.
|
|
997
|
+
*/
|
|
998
|
+
var syncModifierStates = function syncModifierStates(keyEvent) {
|
|
999
|
+
|
|
1000
|
+
// Resync state of alt
|
|
1001
|
+
updateModifierState('alt', [
|
|
1002
|
+
0xFFE9, // Left alt
|
|
1003
|
+
0xFFEA, // Right alt
|
|
1004
|
+
0xFE03 // AltGr
|
|
1005
|
+
], keyEvent);
|
|
1006
|
+
|
|
1007
|
+
// Resync state of shift
|
|
1008
|
+
updateModifierState('shift', [
|
|
1009
|
+
0xFFE1, // Left shift
|
|
1010
|
+
0xFFE2 // Right shift
|
|
1011
|
+
], keyEvent);
|
|
1012
|
+
|
|
1013
|
+
// Resync state of ctrl
|
|
1014
|
+
updateModifierState('ctrl', [
|
|
1015
|
+
0xFFE3, // Left ctrl
|
|
1016
|
+
0xFFE4 // Right ctrl
|
|
1017
|
+
], keyEvent);
|
|
1018
|
+
|
|
1019
|
+
// Resync state of meta
|
|
1020
|
+
updateModifierState('meta', [
|
|
1021
|
+
0xFFE7, // Left meta
|
|
1022
|
+
0xFFE8 // Right meta
|
|
1023
|
+
], keyEvent);
|
|
1024
|
+
|
|
1025
|
+
// Resync state of hyper
|
|
1026
|
+
updateModifierState('hyper', [
|
|
1027
|
+
0xFFEB, // Left super/hyper
|
|
1028
|
+
0xFFEC // Right super/hyper
|
|
1029
|
+
], keyEvent);
|
|
1030
|
+
|
|
1031
|
+
// Update state
|
|
1032
|
+
guac_keyboard.modifiers = keyEvent.modifiers;
|
|
1033
|
+
|
|
1034
|
+
};
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* Returns whether all currently pressed keys were implicitly pressed. A
|
|
1038
|
+
* key is implicitly pressed if its status was inferred indirectly from
|
|
1039
|
+
* inspection of other key events.
|
|
1040
|
+
*
|
|
1041
|
+
* @private
|
|
1042
|
+
* @returns {!boolean}
|
|
1043
|
+
* true if all currently pressed keys were implicitly pressed, false
|
|
1044
|
+
* otherwise.
|
|
1045
|
+
*/
|
|
1046
|
+
var isStateImplicit = function isStateImplicit() {
|
|
1047
|
+
|
|
1048
|
+
for (var keysym in guac_keyboard.pressed) {
|
|
1049
|
+
if (!implicitlyPressed[keysym])
|
|
1050
|
+
return false;
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
return true;
|
|
1054
|
+
|
|
1055
|
+
};
|
|
1056
|
+
|
|
1057
|
+
/**
|
|
1058
|
+
* Reads through the event log, removing events from the head of the log
|
|
1059
|
+
* when the corresponding true key presses are known (or as known as they
|
|
1060
|
+
* can be).
|
|
1061
|
+
*
|
|
1062
|
+
* @private
|
|
1063
|
+
* @return {boolean}
|
|
1064
|
+
* Whether the default action of the latest event should be prevented.
|
|
1065
|
+
*/
|
|
1066
|
+
function interpret_events() {
|
|
1067
|
+
|
|
1068
|
+
// Do not prevent default if no event could be interpreted
|
|
1069
|
+
var handled_event = interpret_event();
|
|
1070
|
+
if (!handled_event)
|
|
1071
|
+
return false;
|
|
1072
|
+
|
|
1073
|
+
// Interpret as much as possible
|
|
1074
|
+
var last_event;
|
|
1075
|
+
do {
|
|
1076
|
+
last_event = handled_event;
|
|
1077
|
+
handled_event = interpret_event();
|
|
1078
|
+
} while (handled_event !== null);
|
|
1079
|
+
|
|
1080
|
+
// Reset keyboard state if we cannot expect to receive any further
|
|
1081
|
+
// keyup events
|
|
1082
|
+
if (isStateImplicit())
|
|
1083
|
+
guac_keyboard.reset();
|
|
1084
|
+
|
|
1085
|
+
return last_event.defaultPrevented;
|
|
1086
|
+
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
/**
|
|
1090
|
+
* Releases Ctrl+Alt, if both are currently pressed and the given keysym
|
|
1091
|
+
* looks like a key that may require AltGr.
|
|
1092
|
+
*
|
|
1093
|
+
* @private
|
|
1094
|
+
* @param {!number} keysym
|
|
1095
|
+
* The key that was just pressed.
|
|
1096
|
+
*/
|
|
1097
|
+
var release_simulated_altgr = function release_simulated_altgr(keysym) {
|
|
1098
|
+
|
|
1099
|
+
// Both Ctrl+Alt must be pressed if simulated AltGr is in use
|
|
1100
|
+
if (!guac_keyboard.modifiers.ctrl || !guac_keyboard.modifiers.alt)
|
|
1101
|
+
return;
|
|
1102
|
+
|
|
1103
|
+
// Assume [A-Z] never require AltGr
|
|
1104
|
+
if (keysym >= 0x0041 && keysym <= 0x005A)
|
|
1105
|
+
return;
|
|
1106
|
+
|
|
1107
|
+
// Assume [a-z] never require AltGr
|
|
1108
|
+
if (keysym >= 0x0061 && keysym <= 0x007A)
|
|
1109
|
+
return;
|
|
1110
|
+
|
|
1111
|
+
// Release Ctrl+Alt if the keysym is printable
|
|
1112
|
+
if (keysym <= 0xFF || (keysym & 0xFF000000) === 0x01000000) {
|
|
1113
|
+
guac_keyboard.release(0xFFE3); // Left ctrl
|
|
1114
|
+
guac_keyboard.release(0xFFE4); // Right ctrl
|
|
1115
|
+
guac_keyboard.release(0xFFE9); // Left alt
|
|
1116
|
+
guac_keyboard.release(0xFFEA); // Right alt
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
};
|
|
1120
|
+
|
|
1121
|
+
/**
|
|
1122
|
+
* Reads through the event log, interpreting the first event, if possible,
|
|
1123
|
+
* and returning that event. If no events can be interpreted, due to a
|
|
1124
|
+
* total lack of events or the need for more events, null is returned. Any
|
|
1125
|
+
* interpreted events are automatically removed from the log.
|
|
1126
|
+
*
|
|
1127
|
+
* @private
|
|
1128
|
+
* @return {KeyEvent}
|
|
1129
|
+
* The first key event in the log, if it can be interpreted, or null
|
|
1130
|
+
* otherwise.
|
|
1131
|
+
*/
|
|
1132
|
+
var interpret_event = function interpret_event() {
|
|
1133
|
+
|
|
1134
|
+
// Peek at first event in log
|
|
1135
|
+
var first = eventLog[0];
|
|
1136
|
+
if (!first)
|
|
1137
|
+
return null;
|
|
1138
|
+
|
|
1139
|
+
// Keydown event
|
|
1140
|
+
if (first instanceof KeydownEvent) {
|
|
1141
|
+
|
|
1142
|
+
var keysym = null;
|
|
1143
|
+
var accepted_events = [];
|
|
1144
|
+
|
|
1145
|
+
// Defer handling of Meta until it is known to be functioning as a
|
|
1146
|
+
// modifier (it may otherwise actually be an alternative method for
|
|
1147
|
+
// pressing a single key, such as Meta+Left for Home on ChromeOS)
|
|
1148
|
+
if (first.keysym === 0xFFE7 || first.keysym === 0xFFE8) {
|
|
1149
|
+
|
|
1150
|
+
// Defer handling until further events exist to provide context
|
|
1151
|
+
if (eventLog.length === 1)
|
|
1152
|
+
return null;
|
|
1153
|
+
|
|
1154
|
+
// Drop keydown if it turns out Meta does not actually apply
|
|
1155
|
+
if (eventLog[1].keysym !== first.keysym) {
|
|
1156
|
+
if (!eventLog[1].modifiers.meta)
|
|
1157
|
+
return eventLog.shift();
|
|
1158
|
+
}
|
|
1159
|
+
|
|
1160
|
+
// Drop duplicate keydown events while waiting to determine
|
|
1161
|
+
// whether to acknowledge Meta (browser may repeat keydown
|
|
1162
|
+
// while the key is held)
|
|
1163
|
+
else if (eventLog[1] instanceof KeydownEvent)
|
|
1164
|
+
return eventLog.shift();
|
|
1165
|
+
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1168
|
+
// If event itself is reliable, no need to wait for other events
|
|
1169
|
+
if (first.reliable) {
|
|
1170
|
+
keysym = first.keysym;
|
|
1171
|
+
accepted_events = eventLog.splice(0, 1);
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
// If keydown is immediately followed by a keypress, use the indicated character
|
|
1175
|
+
else if (eventLog[1] instanceof KeypressEvent) {
|
|
1176
|
+
keysym = eventLog[1].keysym;
|
|
1177
|
+
accepted_events = eventLog.splice(0, 2);
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
// If keydown is immediately followed by anything else, then no
|
|
1181
|
+
// keypress can possibly occur to clarify this event, and we must
|
|
1182
|
+
// handle it now
|
|
1183
|
+
else if (eventLog[1]) {
|
|
1184
|
+
keysym = first.keysym;
|
|
1185
|
+
accepted_events = eventLog.splice(0, 1);
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
// Fire a key press if valid events were found
|
|
1189
|
+
if (accepted_events.length > 0) {
|
|
1190
|
+
|
|
1191
|
+
syncModifierStates(first);
|
|
1192
|
+
|
|
1193
|
+
if (keysym) {
|
|
1194
|
+
|
|
1195
|
+
// Fire event
|
|
1196
|
+
release_simulated_altgr(keysym);
|
|
1197
|
+
var defaultPrevented = !guac_keyboard.press(keysym);
|
|
1198
|
+
recentKeysym[first.keyCode] = keysym;
|
|
1199
|
+
|
|
1200
|
+
// Release the key now if we cannot rely on the associated
|
|
1201
|
+
// keyup event
|
|
1202
|
+
if (!first.keyupReliable)
|
|
1203
|
+
guac_keyboard.release(keysym);
|
|
1204
|
+
|
|
1205
|
+
// Record whether default was prevented
|
|
1206
|
+
for (var i=0; i<accepted_events.length; i++)
|
|
1207
|
+
accepted_events[i].defaultPrevented = defaultPrevented;
|
|
1208
|
+
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
return first;
|
|
1212
|
+
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
} // end if keydown
|
|
1216
|
+
|
|
1217
|
+
// Keyup event
|
|
1218
|
+
else if (first instanceof KeyupEvent && !quirks.keyupUnreliable) {
|
|
1219
|
+
|
|
1220
|
+
// Release specific key if known
|
|
1221
|
+
var keysym = first.keysym;
|
|
1222
|
+
if (keysym) {
|
|
1223
|
+
guac_keyboard.release(keysym);
|
|
1224
|
+
delete recentKeysym[first.keyCode];
|
|
1225
|
+
first.defaultPrevented = true;
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
// Otherwise, fall back to releasing all keys
|
|
1229
|
+
else {
|
|
1230
|
+
guac_keyboard.reset();
|
|
1231
|
+
return first;
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
syncModifierStates(first);
|
|
1235
|
+
return eventLog.shift();
|
|
1236
|
+
|
|
1237
|
+
} // end if keyup
|
|
1238
|
+
|
|
1239
|
+
// Ignore any other type of event (keypress by itself is invalid, and
|
|
1240
|
+
// unreliable keyup events should simply be dumped)
|
|
1241
|
+
else
|
|
1242
|
+
return eventLog.shift();
|
|
1243
|
+
|
|
1244
|
+
// No event interpreted
|
|
1245
|
+
return null;
|
|
1246
|
+
|
|
1247
|
+
};
|
|
1248
|
+
|
|
1249
|
+
/**
|
|
1250
|
+
* Returns the keyboard location of the key associated with the given
|
|
1251
|
+
* keyboard event. The location differentiates key events which otherwise
|
|
1252
|
+
* have the same keycode, such as left shift vs. right shift.
|
|
1253
|
+
*
|
|
1254
|
+
* @private
|
|
1255
|
+
* @param {!KeyboardEvent} e
|
|
1256
|
+
* A JavaScript keyboard event, as received through the DOM via a
|
|
1257
|
+
* "keydown", "keyup", or "keypress" handler.
|
|
1258
|
+
*
|
|
1259
|
+
* @returns {!number}
|
|
1260
|
+
* The location of the key event on the keyboard, as defined at:
|
|
1261
|
+
* http://www.w3.org/TR/DOM-Level-3-Events/#events-KeyboardEvent
|
|
1262
|
+
*/
|
|
1263
|
+
var getEventLocation = function getEventLocation(e) {
|
|
1264
|
+
|
|
1265
|
+
// Use standard location, if possible
|
|
1266
|
+
if ('location' in e)
|
|
1267
|
+
return e.location;
|
|
1268
|
+
|
|
1269
|
+
// Failing that, attempt to use deprecated keyLocation
|
|
1270
|
+
if ('keyLocation' in e)
|
|
1271
|
+
return e.keyLocation;
|
|
1272
|
+
|
|
1273
|
+
// If no location is available, assume left side
|
|
1274
|
+
return 0;
|
|
1275
|
+
|
|
1276
|
+
};
|
|
1277
|
+
|
|
1278
|
+
/**
|
|
1279
|
+
* Attempts to mark the given Event as having been handled by this
|
|
1280
|
+
* Guacamole.Keyboard. If the Event has already been marked as handled,
|
|
1281
|
+
* false is returned.
|
|
1282
|
+
*
|
|
1283
|
+
* @param {!Event} e
|
|
1284
|
+
* The Event to mark.
|
|
1285
|
+
*
|
|
1286
|
+
* @returns {!boolean}
|
|
1287
|
+
* true if the given Event was successfully marked, false if the given
|
|
1288
|
+
* Event was already marked.
|
|
1289
|
+
*/
|
|
1290
|
+
var markEvent = function markEvent(e) {
|
|
1291
|
+
|
|
1292
|
+
// Fail if event is already marked
|
|
1293
|
+
if (e[EVENT_MARKER])
|
|
1294
|
+
return false;
|
|
1295
|
+
|
|
1296
|
+
// Mark event otherwise
|
|
1297
|
+
e[EVENT_MARKER] = true;
|
|
1298
|
+
return true;
|
|
1299
|
+
|
|
1300
|
+
};
|
|
1301
|
+
|
|
1302
|
+
var keyDownListener = function(e) {
|
|
1303
|
+
|
|
1304
|
+
// Only intercept if handler set
|
|
1305
|
+
if (!guac_keyboard.onkeydown) return;
|
|
1306
|
+
|
|
1307
|
+
// Ignore events which have already been handled
|
|
1308
|
+
if (!markEvent(e)) return;
|
|
1309
|
+
|
|
1310
|
+
var keydownEvent = new KeydownEvent(e);
|
|
1311
|
+
|
|
1312
|
+
// Ignore (but do not prevent) the event if explicitly marked as composing,
|
|
1313
|
+
// or when the "composition" keycode sent by some browsers when an IME is in use
|
|
1314
|
+
// (see: http://lists.w3.org/Archives/Public/www-dom/2010JulSep/att-0182/keyCode-spec.html)
|
|
1315
|
+
if (e.isComposing || keydownEvent.keyCode === 229)
|
|
1316
|
+
return;
|
|
1317
|
+
|
|
1318
|
+
// Log event
|
|
1319
|
+
eventLog.push(keydownEvent);
|
|
1320
|
+
|
|
1321
|
+
// Interpret as many events as possible, prevent default if indicated
|
|
1322
|
+
if (interpret_events())
|
|
1323
|
+
e.preventDefault();
|
|
1324
|
+
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
var keyPressListener = function(e) {
|
|
1328
|
+
|
|
1329
|
+
// Only intercept if handler set
|
|
1330
|
+
if (!guac_keyboard.onkeydown && !guac_keyboard.onkeyup) return;
|
|
1331
|
+
|
|
1332
|
+
// Ignore events which have already been handled
|
|
1333
|
+
if (!markEvent(e)) return;
|
|
1334
|
+
|
|
1335
|
+
// Log event
|
|
1336
|
+
eventLog.push(new KeypressEvent(e));
|
|
1337
|
+
|
|
1338
|
+
// Interpret as many events as possible, prevent default if indicated
|
|
1339
|
+
if (interpret_events())
|
|
1340
|
+
e.preventDefault();
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
var keyUpListener = function(e) {
|
|
1344
|
+
|
|
1345
|
+
// Only intercept if handler set
|
|
1346
|
+
if (!guac_keyboard.onkeyup) return;
|
|
1347
|
+
|
|
1348
|
+
// Ignore events which have already been handled
|
|
1349
|
+
if (!markEvent(e)) return;
|
|
1350
|
+
|
|
1351
|
+
e.preventDefault();
|
|
1352
|
+
|
|
1353
|
+
// Log event, call for interpretation
|
|
1354
|
+
eventLog.push(new KeyupEvent(e));
|
|
1355
|
+
interpret_events();
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
/**
|
|
1359
|
+
* Handles the given "input" event, typing the data within the input text.
|
|
1360
|
+
*
|
|
1361
|
+
* @private
|
|
1362
|
+
* @param {!InputEvent} e
|
|
1363
|
+
* The "input" event to handle.
|
|
1364
|
+
*/
|
|
1365
|
+
var handleInput = function handleInput(e) {
|
|
1366
|
+
|
|
1367
|
+
// Only intercept if handler set
|
|
1368
|
+
if (!guac_keyboard.onkeydown && !guac_keyboard.onkeyup) return;
|
|
1369
|
+
|
|
1370
|
+
// Ignore events which have already been handled
|
|
1371
|
+
if (!markEvent(e)) return;
|
|
1372
|
+
|
|
1373
|
+
// Type all content written
|
|
1374
|
+
if (e.data && !e.isComposing)
|
|
1375
|
+
guac_keyboard.type(e.data);
|
|
1376
|
+
|
|
1377
|
+
};
|
|
1378
|
+
|
|
1379
|
+
/**
|
|
1380
|
+
* Handles the given "compositionstart" event, automatically removing
|
|
1381
|
+
* the "input" event handler, as "input" events should only be handled
|
|
1382
|
+
* if composition events are not provided by the browser.
|
|
1383
|
+
*
|
|
1384
|
+
* @private
|
|
1385
|
+
* @param {!CompositionEvent} e
|
|
1386
|
+
* The "compositionstart" event to handle.
|
|
1387
|
+
*/
|
|
1388
|
+
var handleCompositionStart = function handleCompositionStart(e) {
|
|
1389
|
+
|
|
1390
|
+
// Remove the "input" event handler now that the browser is known
|
|
1391
|
+
// to send composition events
|
|
1392
|
+
element.removeEventListener("input", handleInput, false);
|
|
1393
|
+
|
|
1394
|
+
};
|
|
1395
|
+
|
|
1396
|
+
/**
|
|
1397
|
+
* Handles the given "compositionend" event, typing the data within the
|
|
1398
|
+
* composed text.
|
|
1399
|
+
*
|
|
1400
|
+
* @private
|
|
1401
|
+
* @param {!CompositionEvent} e
|
|
1402
|
+
* The "compositionend" event to handle.
|
|
1403
|
+
*/
|
|
1404
|
+
var handleCompositionEnd = function handleCompositionEnd(e) {
|
|
1405
|
+
|
|
1406
|
+
// Only intercept if handler set
|
|
1407
|
+
if (!guac_keyboard.onkeydown && !guac_keyboard.onkeyup) return;
|
|
1408
|
+
|
|
1409
|
+
// Ignore events which have already been handled
|
|
1410
|
+
if (!markEvent(e)) return;
|
|
1411
|
+
|
|
1412
|
+
// Type all content written
|
|
1413
|
+
if (e.data)
|
|
1414
|
+
guac_keyboard.type(e.data);
|
|
1415
|
+
|
|
1416
|
+
};
|
|
1417
|
+
|
|
1418
|
+
/**
|
|
1419
|
+
* Attaches event listeners to the given Element, automatically translating
|
|
1420
|
+
* received key, input, and composition events into simple keydown/keyup
|
|
1421
|
+
* events signalled through this Guacamole.Keyboard's onkeydown and
|
|
1422
|
+
* onkeyup handlers.
|
|
1423
|
+
*
|
|
1424
|
+
* @param {!(Element|Document)} element
|
|
1425
|
+
* The Element to attach event listeners to for the sake of handling
|
|
1426
|
+
* key or input events.
|
|
1427
|
+
*/
|
|
1428
|
+
this.listenTo = function listenTo(element) {
|
|
1429
|
+
|
|
1430
|
+
// When key pressed
|
|
1431
|
+
element.addEventListener("keydown", keyDownListener, {passive: false});
|
|
1432
|
+
|
|
1433
|
+
// When key pressed
|
|
1434
|
+
element.addEventListener("keypress", keyPressListener, {passive: false});
|
|
1435
|
+
|
|
1436
|
+
// When key released
|
|
1437
|
+
element.addEventListener("keyup", keyUpListener, {passive: false});
|
|
1438
|
+
|
|
1439
|
+
|
|
1440
|
+
// Automatically type text entered into the wrapped field
|
|
1441
|
+
element.addEventListener("input", handleInput, false);
|
|
1442
|
+
element.addEventListener("compositionend", handleCompositionEnd, false);
|
|
1443
|
+
element.addEventListener("compositionstart", handleCompositionStart, false);
|
|
1444
|
+
|
|
1445
|
+
};
|
|
1446
|
+
|
|
1447
|
+
// Listen to given element, if any
|
|
1448
|
+
if (element)
|
|
1449
|
+
guac_keyboard.listenTo(element);
|
|
1450
|
+
|
|
1451
|
+
this.dispose = function dispose() {
|
|
1452
|
+
// Stop listening to key events
|
|
1453
|
+
if (element) {
|
|
1454
|
+
element.removeEventListener("keydown", keyDownListener, true);
|
|
1455
|
+
element.removeEventListener("keypress", keyPressListener, true);
|
|
1456
|
+
element.removeEventListener("keyup", keyUpListener, true);
|
|
1457
|
+
element.removeEventListener("input", handleInput, false);
|
|
1458
|
+
element.removeEventListener("compositionend", handleCompositionEnd, false);
|
|
1459
|
+
element.removeEventListener("compositionstart", handleCompositionStart, false);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
};
|
|
1464
|
+
|
|
1465
|
+
|
|
1466
|
+
|
|
1467
|
+
/**
|
|
1468
|
+
* The unique numerical identifier to assign to the next Guacamole.Keyboard
|
|
1469
|
+
* instance.
|
|
1470
|
+
*
|
|
1471
|
+
* @private
|
|
1472
|
+
* @type {!number}
|
|
1473
|
+
*/
|
|
1474
|
+
Guacamole.Keyboard._nextID = 0;
|
|
1475
|
+
|
|
1476
|
+
/**
|
|
1477
|
+
* The state of all supported keyboard modifiers.
|
|
1478
|
+
* @constructor
|
|
1479
|
+
*/
|
|
1480
|
+
Guacamole.Keyboard.ModifierState = function() {
|
|
1481
|
+
|
|
1482
|
+
/**
|
|
1483
|
+
* Whether shift is currently pressed.
|
|
1484
|
+
*
|
|
1485
|
+
* @type {!boolean}
|
|
1486
|
+
*/
|
|
1487
|
+
this.shift = false;
|
|
1488
|
+
|
|
1489
|
+
/**
|
|
1490
|
+
* Whether ctrl is currently pressed.
|
|
1491
|
+
*
|
|
1492
|
+
* @type {!boolean}
|
|
1493
|
+
*/
|
|
1494
|
+
this.ctrl = false;
|
|
1495
|
+
|
|
1496
|
+
/**
|
|
1497
|
+
* Whether alt is currently pressed.
|
|
1498
|
+
*
|
|
1499
|
+
* @type {!boolean}
|
|
1500
|
+
*/
|
|
1501
|
+
this.alt = false;
|
|
1502
|
+
|
|
1503
|
+
/**
|
|
1504
|
+
* Whether meta (apple key) is currently pressed.
|
|
1505
|
+
*
|
|
1506
|
+
* @type {!boolean}
|
|
1507
|
+
*/
|
|
1508
|
+
this.meta = false;
|
|
1509
|
+
|
|
1510
|
+
/**
|
|
1511
|
+
* Whether hyper (windows key) is currently pressed.
|
|
1512
|
+
*
|
|
1513
|
+
* @type {!boolean}
|
|
1514
|
+
*/
|
|
1515
|
+
this.hyper = false;
|
|
1516
|
+
|
|
1517
|
+
};
|
|
1518
|
+
|
|
1519
|
+
/**
|
|
1520
|
+
* Returns the modifier state applicable to the keyboard event given.
|
|
1521
|
+
*
|
|
1522
|
+
* @param {!KeyboardEvent} e
|
|
1523
|
+
* The keyboard event to read.
|
|
1524
|
+
*
|
|
1525
|
+
* @returns {!Guacamole.Keyboard.ModifierState}
|
|
1526
|
+
* The current state of keyboard modifiers.
|
|
1527
|
+
*/
|
|
1528
|
+
Guacamole.Keyboard.ModifierState.fromKeyboardEvent = function(e) {
|
|
1529
|
+
|
|
1530
|
+
var state = new Guacamole.Keyboard.ModifierState();
|
|
1531
|
+
|
|
1532
|
+
// Assign states from old flags
|
|
1533
|
+
state.shift = e.shiftKey;
|
|
1534
|
+
state.ctrl = e.ctrlKey;
|
|
1535
|
+
state.alt = e.altKey;
|
|
1536
|
+
state.meta = e.metaKey;
|
|
1537
|
+
|
|
1538
|
+
// Use DOM3 getModifierState() for others
|
|
1539
|
+
if (e.getModifierState) {
|
|
1540
|
+
state.hyper = e.getModifierState("OS")
|
|
1541
|
+
|| e.getModifierState("Super")
|
|
1542
|
+
|| e.getModifierState("Hyper")
|
|
1543
|
+
|| e.getModifierState("Win");
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
return state;
|
|
1547
|
+
|
|
1548
|
+
};
|
|
1549
|
+
|
|
1550
|
+
|
|
1551
|
+
module.exports = Guacamole;
|