novnc-rails 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +0 -0
  3. data/LICENSE.txt +0 -0
  4. data/README.md +0 -0
  5. data/lib/novnc-rails.rb +8 -0
  6. data/lib/novnc-rails/version.rb +5 -0
  7. data/vendor/assets/javascripts/noVNC/Orbitron700.ttf +0 -0
  8. data/vendor/assets/javascripts/noVNC/Orbitron700.woff +0 -0
  9. data/vendor/assets/javascripts/noVNC/base.css +512 -0
  10. data/vendor/assets/javascripts/noVNC/base64.js +113 -0
  11. data/vendor/assets/javascripts/noVNC/black.css +71 -0
  12. data/vendor/assets/javascripts/noVNC/blue.css +64 -0
  13. data/vendor/assets/javascripts/noVNC/des.js +276 -0
  14. data/vendor/assets/javascripts/noVNC/display.js +751 -0
  15. data/vendor/assets/javascripts/noVNC/input.js +388 -0
  16. data/vendor/assets/javascripts/noVNC/jsunzip.js +676 -0
  17. data/vendor/assets/javascripts/noVNC/keyboard.js +543 -0
  18. data/vendor/assets/javascripts/noVNC/keysym.js +378 -0
  19. data/vendor/assets/javascripts/noVNC/keysymdef.js +15 -0
  20. data/vendor/assets/javascripts/noVNC/logo.js +1 -0
  21. data/vendor/assets/javascripts/noVNC/playback.js +102 -0
  22. data/vendor/assets/javascripts/noVNC/rfb.js +1889 -0
  23. data/vendor/assets/javascripts/noVNC/ui.js +979 -0
  24. data/vendor/assets/javascripts/noVNC/util.js +656 -0
  25. data/vendor/assets/javascripts/noVNC/web-socket-js/README.txt +109 -0
  26. data/vendor/assets/javascripts/noVNC/web-socket-js/WebSocketMain.swf +0 -0
  27. data/vendor/assets/javascripts/noVNC/web-socket-js/swfobject.js +4 -0
  28. data/vendor/assets/javascripts/noVNC/web-socket-js/web_socket.js +391 -0
  29. data/vendor/assets/javascripts/noVNC/websock.js +388 -0
  30. data/vendor/assets/javascripts/noVNC/webutil.js +239 -0
  31. metadata +86 -0
@@ -0,0 +1,543 @@
1
+ var kbdUtil = (function() {
2
+ "use strict";
3
+
4
+ function substituteCodepoint(cp) {
5
+ // Any Unicode code points which do not have corresponding keysym entries
6
+ // can be swapped out for another code point by adding them to this table
7
+ var substitutions = {
8
+ // {S,s} with comma below -> {S,s} with cedilla
9
+ 0x218 : 0x15e,
10
+ 0x219 : 0x15f,
11
+ // {T,t} with comma below -> {T,t} with cedilla
12
+ 0x21a : 0x162,
13
+ 0x21b : 0x163
14
+ };
15
+
16
+ var sub = substitutions[cp];
17
+ return sub ? sub : cp;
18
+ }
19
+
20
+ function isMac() {
21
+ return navigator && !!(/mac/i).exec(navigator.platform);
22
+ }
23
+ function isWindows() {
24
+ return navigator && !!(/win/i).exec(navigator.platform);
25
+ }
26
+ function isLinux() {
27
+ return navigator && !!(/linux/i).exec(navigator.platform);
28
+ }
29
+
30
+ // Return true if a modifier which is not the specified char modifier (and is not shift) is down
31
+ function hasShortcutModifier(charModifier, currentModifiers) {
32
+ var mods = {};
33
+ for (var key in currentModifiers) {
34
+ if (parseInt(key) !== XK_Shift_L) {
35
+ mods[key] = currentModifiers[key];
36
+ }
37
+ }
38
+
39
+ var sum = 0;
40
+ for (var k in currentModifiers) {
41
+ if (mods[k]) {
42
+ ++sum;
43
+ }
44
+ }
45
+ if (hasCharModifier(charModifier, mods)) {
46
+ return sum > charModifier.length;
47
+ }
48
+ else {
49
+ return sum > 0;
50
+ }
51
+ }
52
+
53
+ // Return true if the specified char modifier is currently down
54
+ function hasCharModifier(charModifier, currentModifiers) {
55
+ if (charModifier.length === 0) { return false; }
56
+
57
+ for (var i = 0; i < charModifier.length; ++i) {
58
+ if (!currentModifiers[charModifier[i]]) {
59
+ return false;
60
+ }
61
+ }
62
+ return true;
63
+ }
64
+
65
+ // Helper object tracking modifier key state
66
+ // and generates fake key events to compensate if it gets out of sync
67
+ function ModifierSync(charModifier) {
68
+ if (!charModifier) {
69
+ if (isMac()) {
70
+ // on Mac, Option (AKA Alt) is used as a char modifier
71
+ charModifier = [XK_Alt_L];
72
+ }
73
+ else if (isWindows()) {
74
+ // on Windows, Ctrl+Alt is used as a char modifier
75
+ charModifier = [XK_Alt_L, XK_Control_L];
76
+ }
77
+ else if (isLinux()) {
78
+ // on Linux, ISO Level 3 Shift (AltGr) is used as a char modifier
79
+ charModifier = [XK_ISO_Level3_Shift];
80
+ }
81
+ else {
82
+ charModifier = [];
83
+ }
84
+ }
85
+
86
+ var state = {};
87
+ state[XK_Control_L] = false;
88
+ state[XK_Alt_L] = false;
89
+ state[XK_ISO_Level3_Shift] = false;
90
+ state[XK_Shift_L] = false;
91
+ state[XK_Meta_L] = false;
92
+
93
+ function sync(evt, keysym) {
94
+ var result = [];
95
+ function syncKey(keysym) {
96
+ return {keysym: keysyms.lookup(keysym), type: state[keysym] ? 'keydown' : 'keyup'};
97
+ }
98
+
99
+ if (evt.ctrlKey !== undefined &&
100
+ evt.ctrlKey !== state[XK_Control_L] && keysym !== XK_Control_L) {
101
+ state[XK_Control_L] = evt.ctrlKey;
102
+ result.push(syncKey(XK_Control_L));
103
+ }
104
+ if (evt.altKey !== undefined &&
105
+ evt.altKey !== state[XK_Alt_L] && keysym !== XK_Alt_L) {
106
+ state[XK_Alt_L] = evt.altKey;
107
+ result.push(syncKey(XK_Alt_L));
108
+ }
109
+ if (evt.altGraphKey !== undefined &&
110
+ evt.altGraphKey !== state[XK_ISO_Level3_Shift] && keysym !== XK_ISO_Level3_Shift) {
111
+ state[XK_ISO_Level3_Shift] = evt.altGraphKey;
112
+ result.push(syncKey(XK_ISO_Level3_Shift));
113
+ }
114
+ if (evt.shiftKey !== undefined &&
115
+ evt.shiftKey !== state[XK_Shift_L] && keysym !== XK_Shift_L) {
116
+ state[XK_Shift_L] = evt.shiftKey;
117
+ result.push(syncKey(XK_Shift_L));
118
+ }
119
+ if (evt.metaKey !== undefined &&
120
+ evt.metaKey !== state[XK_Meta_L] && keysym !== XK_Meta_L) {
121
+ state[XK_Meta_L] = evt.metaKey;
122
+ result.push(syncKey(XK_Meta_L));
123
+ }
124
+ return result;
125
+ }
126
+ function syncKeyEvent(evt, down) {
127
+ var obj = getKeysym(evt);
128
+ var keysym = obj ? obj.keysym : null;
129
+
130
+ // first, apply the event itself, if relevant
131
+ if (keysym !== null && state[keysym] !== undefined) {
132
+ state[keysym] = down;
133
+ }
134
+ return sync(evt, keysym);
135
+ }
136
+
137
+ return {
138
+ // sync on the appropriate keyboard event
139
+ keydown: function(evt) { return syncKeyEvent(evt, true);},
140
+ keyup: function(evt) { return syncKeyEvent(evt, false);},
141
+ // Call this with a non-keyboard event (such as mouse events) to use its modifier state to synchronize anyway
142
+ syncAny: function(evt) { return sync(evt);},
143
+
144
+ // is a shortcut modifier down?
145
+ hasShortcutModifier: function() { return hasShortcutModifier(charModifier, state); },
146
+ // if a char modifier is down, return the keys it consists of, otherwise return null
147
+ activeCharModifier: function() { return hasCharModifier(charModifier, state) ? charModifier : null; }
148
+ };
149
+ }
150
+
151
+ // Get a key ID from a keyboard event
152
+ // May be a string or an integer depending on the available properties
153
+ function getKey(evt){
154
+ if ('keyCode' in evt && 'key' in evt) {
155
+ return evt.key + ':' + evt.keyCode;
156
+ }
157
+ else if ('keyCode' in evt) {
158
+ return evt.keyCode;
159
+ }
160
+ else {
161
+ return evt.key;
162
+ }
163
+ }
164
+
165
+ // Get the most reliable keysym value we can get from a key event
166
+ // if char/charCode is available, prefer those, otherwise fall back to key/keyCode/which
167
+ function getKeysym(evt){
168
+ var codepoint;
169
+ if (evt.char && evt.char.length === 1) {
170
+ codepoint = evt.char.charCodeAt();
171
+ }
172
+ else if (evt.charCode) {
173
+ codepoint = evt.charCode;
174
+ }
175
+ else if (evt.keyCode && evt.type === 'keypress') {
176
+ // IE10 stores the char code as keyCode, and has no other useful properties
177
+ codepoint = evt.keyCode;
178
+ }
179
+ if (codepoint) {
180
+ var res = keysyms.fromUnicode(substituteCodepoint(codepoint));
181
+ if (res) {
182
+ return res;
183
+ }
184
+ }
185
+ // we could check evt.key here.
186
+ // Legal values are defined in http://www.w3.org/TR/DOM-Level-3-Events/#key-values-list,
187
+ // so we "just" need to map them to keysym, but AFAIK this is only available in IE10, which also provides evt.key
188
+ // so we don't *need* it yet
189
+ if (evt.keyCode) {
190
+ return keysyms.lookup(keysymFromKeyCode(evt.keyCode, evt.shiftKey));
191
+ }
192
+ if (evt.which) {
193
+ return keysyms.lookup(keysymFromKeyCode(evt.which, evt.shiftKey));
194
+ }
195
+ return null;
196
+ }
197
+
198
+ // Given a keycode, try to predict which keysym it might be.
199
+ // If the keycode is unknown, null is returned.
200
+ function keysymFromKeyCode(keycode, shiftPressed) {
201
+ if (typeof(keycode) !== 'number') {
202
+ return null;
203
+ }
204
+ // won't be accurate for azerty
205
+ if (keycode >= 0x30 && keycode <= 0x39) {
206
+ return keycode; // digit
207
+ }
208
+ if (keycode >= 0x41 && keycode <= 0x5a) {
209
+ // remap to lowercase unless shift is down
210
+ return shiftPressed ? keycode : keycode + 32; // A-Z
211
+ }
212
+ if (keycode >= 0x60 && keycode <= 0x69) {
213
+ return XK_KP_0 + (keycode - 0x60); // numpad 0-9
214
+ }
215
+
216
+ switch(keycode) {
217
+ case 0x20: return XK_space;
218
+ case 0x6a: return XK_KP_Multiply;
219
+ case 0x6b: return XK_KP_Add;
220
+ case 0x6c: return XK_KP_Separator;
221
+ case 0x6d: return XK_KP_Subtract;
222
+ case 0x6e: return XK_KP_Decimal;
223
+ case 0x6f: return XK_KP_Divide;
224
+ case 0xbb: return XK_plus;
225
+ case 0xbc: return XK_comma;
226
+ case 0xbd: return XK_minus;
227
+ case 0xbe: return XK_period;
228
+ }
229
+
230
+ return nonCharacterKey({keyCode: keycode});
231
+ }
232
+
233
+ // if the key is a known non-character key (any key which doesn't generate character data)
234
+ // return its keysym value. Otherwise return null
235
+ function nonCharacterKey(evt) {
236
+ // evt.key not implemented yet
237
+ if (!evt.keyCode) { return null; }
238
+ var keycode = evt.keyCode;
239
+
240
+ if (keycode >= 0x70 && keycode <= 0x87) {
241
+ return XK_F1 + keycode - 0x70; // F1-F24
242
+ }
243
+ switch (keycode) {
244
+
245
+ case 8 : return XK_BackSpace;
246
+ case 13 : return XK_Return;
247
+
248
+ case 9 : return XK_Tab;
249
+
250
+ case 27 : return XK_Escape;
251
+ case 46 : return XK_Delete;
252
+
253
+ case 36 : return XK_Home;
254
+ case 35 : return XK_End;
255
+ case 33 : return XK_Page_Up;
256
+ case 34 : return XK_Page_Down;
257
+ case 45 : return XK_Insert;
258
+
259
+ case 37 : return XK_Left;
260
+ case 38 : return XK_Up;
261
+ case 39 : return XK_Right;
262
+ case 40 : return XK_Down;
263
+
264
+ case 16 : return XK_Shift_L;
265
+ case 17 : return XK_Control_L;
266
+ case 18 : return XK_Alt_L; // also: Option-key on Mac
267
+
268
+ case 224 : return XK_Meta_L;
269
+ case 225 : return XK_ISO_Level3_Shift; // AltGr
270
+ case 91 : return XK_Super_L; // also: Windows-key
271
+ case 92 : return XK_Super_R; // also: Windows-key
272
+ case 93 : return XK_Menu; // also: Windows-Menu, Command on Mac
273
+ default: return null;
274
+ }
275
+ }
276
+ return {
277
+ hasShortcutModifier : hasShortcutModifier,
278
+ hasCharModifier : hasCharModifier,
279
+ ModifierSync : ModifierSync,
280
+ getKey : getKey,
281
+ getKeysym : getKeysym,
282
+ keysymFromKeyCode : keysymFromKeyCode,
283
+ nonCharacterKey : nonCharacterKey,
284
+ substituteCodepoint : substituteCodepoint
285
+ };
286
+ })();
287
+
288
+ // Takes a DOM keyboard event and:
289
+ // - determines which keysym it represents
290
+ // - determines a keyId identifying the key that was pressed (corresponding to the key/keyCode properties on the DOM event)
291
+ // - synthesizes events to synchronize modifier key state between which modifiers are actually down, and which we thought were down
292
+ // - marks each event with an 'escape' property if a modifier was down which should be "escaped"
293
+ // - generates a "stall" event in cases where it might be necessary to wait and see if a keypress event follows a keydown
294
+ // This information is collected into an object which is passed to the next() function. (one call per event)
295
+ function KeyEventDecoder(modifierState, next) {
296
+ "use strict";
297
+ function sendAll(evts) {
298
+ for (var i = 0; i < evts.length; ++i) {
299
+ next(evts[i]);
300
+ }
301
+ }
302
+ function process(evt, type) {
303
+ var result = {type: type};
304
+ var keyId = kbdUtil.getKey(evt);
305
+ if (keyId) {
306
+ result.keyId = keyId;
307
+ }
308
+
309
+ var keysym = kbdUtil.getKeysym(evt);
310
+
311
+ var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
312
+ // Is this a case where we have to decide on the keysym right away, rather than waiting for the keypress?
313
+ // "special" keys like enter, tab or backspace don't send keypress events,
314
+ // and some browsers don't send keypresses at all if a modifier is down
315
+ if (keysym && (type !== 'keydown' || kbdUtil.nonCharacterKey(evt) || hasModifier)) {
316
+ result.keysym = keysym;
317
+ }
318
+
319
+ var isShift = evt.keyCode === 0x10 || evt.key === 'Shift';
320
+
321
+ // Should we prevent the browser from handling the event?
322
+ // Doing so on a keydown (in most browsers) prevents keypress from being generated
323
+ // so only do that if we have to.
324
+ var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!kbdUtil.nonCharacterKey(evt));
325
+
326
+ // If a char modifier is down on a keydown, we need to insert a stall,
327
+ // so VerifyCharModifier knows to wait and see if a keypress is comnig
328
+ var stall = type === 'keydown' && modifierState.activeCharModifier() && !kbdUtil.nonCharacterKey(evt);
329
+
330
+ // if a char modifier is pressed, get the keys it consists of (on Windows, AltGr is equivalent to Ctrl+Alt)
331
+ var active = modifierState.activeCharModifier();
332
+
333
+ // If we have a char modifier down, and we're able to determine a keysym reliably
334
+ // then (a) we know to treat the modifier as a char modifier,
335
+ // and (b) we'll have to "escape" the modifier to undo the modifier when sending the char.
336
+ if (active && keysym) {
337
+ var isCharModifier = false;
338
+ for (var i = 0; i < active.length; ++i) {
339
+ if (active[i] === keysym.keysym) {
340
+ isCharModifier = true;
341
+ }
342
+ }
343
+ if (type === 'keypress' && !isCharModifier) {
344
+ result.escape = modifierState.activeCharModifier();
345
+ }
346
+ }
347
+
348
+ if (stall) {
349
+ // insert a fake "stall" event
350
+ next({type: 'stall'});
351
+ }
352
+ next(result);
353
+
354
+ return suppress;
355
+ }
356
+
357
+ return {
358
+ keydown: function(evt) {
359
+ sendAll(modifierState.keydown(evt));
360
+ return process(evt, 'keydown');
361
+ },
362
+ keypress: function(evt) {
363
+ return process(evt, 'keypress');
364
+ },
365
+ keyup: function(evt) {
366
+ sendAll(modifierState.keyup(evt));
367
+ return process(evt, 'keyup');
368
+ },
369
+ syncModifiers: function(evt) {
370
+ sendAll(modifierState.syncAny(evt));
371
+ },
372
+ releaseAll: function() { next({type: 'releaseall'}); }
373
+ };
374
+ }
375
+
376
+ // Combines keydown and keypress events where necessary to handle char modifiers.
377
+ // On some OS'es, a char modifier is sometimes used as a shortcut modifier.
378
+ // For example, on Windows, AltGr is synonymous with Ctrl-Alt. On a Danish keyboard layout, AltGr-2 yields a @, but Ctrl-Alt-D does nothing
379
+ // so when used with the '2' key, Ctrl-Alt counts as a char modifier (and should be escaped), but when used with 'D', it does not.
380
+ // The only way we can distinguish these cases is to wait and see if a keypress event arrives
381
+ // When we receive a "stall" event, wait a few ms before processing the next keydown. If a keypress has also arrived, merge the two
382
+ function VerifyCharModifier(next) {
383
+ "use strict";
384
+ var queue = [];
385
+ var timer = null;
386
+ function process() {
387
+ if (timer) {
388
+ return;
389
+ }
390
+
391
+ var delayProcess = function () {
392
+ clearTimeout(timer);
393
+ timer = null;
394
+ process();
395
+ };
396
+
397
+ while (queue.length !== 0) {
398
+ var cur = queue[0];
399
+ queue = queue.splice(1);
400
+ switch (cur.type) {
401
+ case 'stall':
402
+ // insert a delay before processing available events.
403
+ /* jshint loopfunc: true */
404
+ timer = setTimeout(delayProcess, 5);
405
+ /* jshint loopfunc: false */
406
+ return;
407
+ case 'keydown':
408
+ // is the next element a keypress? Then we should merge the two
409
+ if (queue.length !== 0 && queue[0].type === 'keypress') {
410
+ // Firefox sends keypress even when no char is generated.
411
+ // so, if keypress keysym is the same as we'd have guessed from keydown,
412
+ // the modifier didn't have any effect, and should not be escaped
413
+ if (queue[0].escape && (!cur.keysym || cur.keysym.keysym !== queue[0].keysym.keysym)) {
414
+ cur.escape = queue[0].escape;
415
+ }
416
+ cur.keysym = queue[0].keysym;
417
+ queue = queue.splice(1);
418
+ }
419
+ break;
420
+ }
421
+
422
+ // swallow stall events, and pass all others to the next stage
423
+ if (cur.type !== 'stall') {
424
+ next(cur);
425
+ }
426
+ }
427
+ }
428
+ return function(evt) {
429
+ queue.push(evt);
430
+ process();
431
+ };
432
+ }
433
+
434
+ // Keeps track of which keys we (and the server) believe are down
435
+ // When a keyup is received, match it against this list, to determine the corresponding keysym(s)
436
+ // in some cases, a single key may produce multiple keysyms, so the corresponding keyup event must release all of these chars
437
+ // key repeat events should be merged into a single entry.
438
+ // Because we can't always identify which entry a keydown or keyup event corresponds to, we sometimes have to guess
439
+ function TrackKeyState(next) {
440
+ "use strict";
441
+ var state = [];
442
+
443
+ return function (evt) {
444
+ var last = state.length !== 0 ? state[state.length-1] : null;
445
+
446
+ switch (evt.type) {
447
+ case 'keydown':
448
+ // insert a new entry if last seen key was different.
449
+ if (!last || !evt.keyId || last.keyId !== evt.keyId) {
450
+ last = {keyId: evt.keyId, keysyms: {}};
451
+ state.push(last);
452
+ }
453
+ if (evt.keysym) {
454
+ // make sure last event contains this keysym (a single "logical" keyevent
455
+ // can cause multiple key events to be sent to the VNC server)
456
+ last.keysyms[evt.keysym.keysym] = evt.keysym;
457
+ last.ignoreKeyPress = true;
458
+ next(evt);
459
+ }
460
+ break;
461
+ case 'keypress':
462
+ if (!last) {
463
+ last = {keyId: evt.keyId, keysyms: {}};
464
+ state.push(last);
465
+ }
466
+ if (!evt.keysym) {
467
+ console.log('keypress with no keysym:', evt);
468
+ }
469
+
470
+ // If we didn't expect a keypress, and already sent a keydown to the VNC server
471
+ // based on the keydown, make sure to skip this event.
472
+ if (evt.keysym && !last.ignoreKeyPress) {
473
+ last.keysyms[evt.keysym.keysym] = evt.keysym;
474
+ evt.type = 'keydown';
475
+ next(evt);
476
+ }
477
+ break;
478
+ case 'keyup':
479
+ if (state.length === 0) {
480
+ return;
481
+ }
482
+ var idx = null;
483
+ // do we have a matching key tracked as being down?
484
+ for (var i = 0; i !== state.length; ++i) {
485
+ if (state[i].keyId === evt.keyId) {
486
+ idx = i;
487
+ break;
488
+ }
489
+ }
490
+ // if we couldn't find a match (it happens), assume it was the last key pressed
491
+ if (idx === null) {
492
+ idx = state.length - 1;
493
+ }
494
+
495
+ var item = state.splice(idx, 1)[0];
496
+ // for each keysym tracked by this key entry, clone the current event and override the keysym
497
+ var clone = (function(){
498
+ function Clone(){}
499
+ return function (obj) { Clone.prototype=obj; return new Clone(); };
500
+ }());
501
+ for (var key in item.keysyms) {
502
+ var out = clone(evt);
503
+ out.keysym = item.keysyms[key];
504
+ next(out);
505
+ }
506
+ break;
507
+ case 'releaseall':
508
+ /* jshint shadow: true */
509
+ for (var i = 0; i < state.length; ++i) {
510
+ for (var key in state[i].keysyms) {
511
+ var keysym = state[i].keysyms[key];
512
+ next({keyId: 0, keysym: keysym, type: 'keyup'});
513
+ }
514
+ }
515
+ /* jshint shadow: false */
516
+ state = [];
517
+ }
518
+ };
519
+ }
520
+
521
+ // Handles "escaping" of modifiers: if a char modifier is used to produce a keysym (such as AltGr-2 to generate an @),
522
+ // then the modifier must be "undone" before sending the @, and "redone" afterwards.
523
+ function EscapeModifiers(next) {
524
+ "use strict";
525
+ return function(evt) {
526
+ if (evt.type !== 'keydown' || evt.escape === undefined) {
527
+ next(evt);
528
+ return;
529
+ }
530
+ // undo modifiers
531
+ for (var i = 0; i < evt.escape.length; ++i) {
532
+ next({type: 'keyup', keyId: 0, keysym: keysyms.lookup(evt.escape[i])});
533
+ }
534
+ // send the character event
535
+ next(evt);
536
+ // redo modifiers
537
+ /* jshint shadow: true */
538
+ for (var i = 0; i < evt.escape.length; ++i) {
539
+ next({type: 'keydown', keyId: 0, keysym: keysyms.lookup(evt.escape[i])});
540
+ }
541
+ /* jshint shadow: false */
542
+ };
543
+ }