]> git.proxmox.com Git - mirror_novnc.git/blame - app/ui.js
Clean up unused files in tests
[mirror_novnc.git] / app / ui.js
CommitLineData
15046f00
JM
1/*
2 * noVNC: HTML5 VNC client
d58f8b51 3 * Copyright (C) 2012 Joel Martin
777cb7a0 4 * Copyright (C) 2016 Samuel Mannehed for Cendio AB
1d728ace 5 * Licensed under MPL 2.0 (see LICENSE.txt)
15046f00
JM
6 *
7 * See README.md for usage and integration instructions.
8 */
43cf7bd8 9
bbbf42bb 10/* jslint white: false, browser: true */
ae510306
SR
11/* global window, document.getElementById, Util, WebUtil, RFB, Display */
12
13/* [module]
14 * import Util from "../core/util";
15 * import KeyTable from "../core/keysym";
16 * import RFB from "../core/rfb";
17 * import Display from "../core/display";
18 * import WebUtil from "./webutil";
19 */
bbbf42bb
SR
20
21var UI;
22
23(function () {
24 "use strict";
25
ae510306 26 /* [begin skip-as-module] */
bbbf42bb 27 // Load supporting scripts
ae510306
SR
28 Util.load_scripts(
29 {'core': ["base64.js", "websock.js", "des.js", "keysymdef.js",
30 "xtscancodes.js", "keyboard.js", "input.js", "display.js",
31 "inflator.js", "rfb.js", "keysym.js"],
32 '.': ["webutil.js"]});
33
bbbf42bb 34 window.onscriptsload = function () { UI.load(); };
ae510306 35 /* [end skip-as-module] */
bbbf42bb 36
bd6874e0 37 UI = {
bbbf42bb 38
045d9224 39 rfb_state: 'loaded',
529c64e1 40
045d9224 41 resizeTimeout: null,
529c64e1 42 popupStatusTimeout: null,
43 hideKeyboardTimeout: null,
44
045d9224 45 settingsOpen: false,
46 connSettingsOpen: false,
bbbf42bb
SR
47 clipboardOpen: false,
48 keyboardVisible: false,
9d16e512 49 extraKeysVisible: false,
529c64e1 50
bbbf42bb 51 isTouchDevice: false,
f620259b 52 isSafari: false,
a6357e82 53 rememberedClipSetting: null,
529c64e1 54 lastKeyboardinput: null,
55 defaultKeyboardinputLen: 100,
56
e961641f
SM
57 ctrlOn: false,
58 altOn: false,
bbbf42bb
SR
59
60 // Setup rfb object, load settings from browser storage, then call
61 // UI.init to setup the UI/menus
0bd2cbac 62 load: function(callback) {
bbbf42bb
SR
63 WebUtil.initSettings(UI.start, callback);
64 },
65
66 // Render default UI and initialize settings menu
67 start: function(callback) {
68 UI.isTouchDevice = 'ontouchstart' in document.documentElement;
69
70 // Stylesheet selection dropdown
71 var sheet = WebUtil.selectStylesheet();
72 var sheets = WebUtil.getStylesheets();
73 var i;
74 for (i = 0; i < sheets.length; i += 1) {
ae510306 75 UI.addOption(document.getElementById('noVNC_setting_stylesheet'),sheets[i].title, sheets[i].title);
bbbf42bb
SR
76 }
77
78 // Logging selection dropdown
79 var llevels = ['error', 'warn', 'info', 'debug'];
80 for (i = 0; i < llevels.length; i += 1) {
ae510306 81 UI.addOption(document.getElementById('noVNC_setting_logging'),llevels[i], llevels[i]);
bbbf42bb
SR
82 }
83
84 // Settings with immediate effects
85 UI.initSetting('logging', 'warn');
86 WebUtil.init_logging(UI.getSetting('logging'));
87
88 UI.initSetting('stylesheet', 'default');
89 WebUtil.selectStylesheet(null);
90 // call twice to get around webkit bug
91 WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
92
93 // if port == 80 (or 443) then it won't be present and should be
94 // set manually
95 var port = window.location.port;
96 if (!port) {
97 if (window.location.protocol.substring(0,5) == 'https') {
98 port = 443;
99 }
100 else if (window.location.protocol.substring(0,4) == 'http') {
101 port = 80;
102 }
103 }
104
105 /* Populate the controls if defaults are provided in the URL */
106 UI.initSetting('host', window.location.hostname);
107 UI.initSetting('port', port);
108 UI.initSetting('password', '');
109 UI.initSetting('encrypt', (window.location.protocol === "https:"));
110 UI.initSetting('true_color', true);
111 UI.initSetting('cursor', !UI.isTouchDevice);
8b46c0de 112 UI.initSetting('resize', 'off');
bbbf42bb
SR
113 UI.initSetting('shared', true);
114 UI.initSetting('view_only', false);
115 UI.initSetting('path', 'websockify');
116 UI.initSetting('repeaterID', '');
c55f05f6 117 UI.initSetting('token', '');
bbbf42bb 118
494b407a 119 var autoconnect = WebUtil.getConfigVar('autoconnect', false);
bbbf42bb
SR
120 if (autoconnect === 'true' || autoconnect == '1') {
121 autoconnect = true;
122 UI.connect();
123 } else {
124 autoconnect = false;
125 }
126
127 UI.updateVisualState();
128
ae510306 129 document.getElementById('noVNC_setting_host').focus();
fdedbafb 130
bbbf42bb
SR
131 // Show mouse selector buttons on touch screen devices
132 if (UI.isTouchDevice) {
133 // Show mobile buttons
ae510306 134 document.getElementById('noVNC_mobile_buttons').style.display = "inline";
bbbf42bb
SR
135 UI.setMouseButton();
136 // Remove the address bar
137 setTimeout(function() { window.scrollTo(0, 1); }, 100);
138 UI.forceSetting('clip', true);
bbbf42bb
SR
139 } else {
140 UI.initSetting('clip', false);
141 }
142
bbbf42bb 143 UI.setViewClip();
fdedbafb 144 UI.setBarPosition();
f8b399d7 145
146 Util.addEvent(window, 'resize', function () {
777cb7a0 147 UI.applyResizeMode();
f8b399d7 148 UI.setViewClip();
31ddaa1c 149 UI.updateViewDrag();
fdedbafb 150 UI.setBarPosition();
f8b399d7 151 } );
bbbf42bb 152
f620259b 153 UI.isSafari = (navigator.userAgent.indexOf('Safari') != -1 &&
154 navigator.userAgent.indexOf('Chrome') == -1);
a6357e82 155
156 // Only show the button if fullscreen is properly supported
157 // * Safari doesn't support alphanumerical input while in fullscreen
f620259b 158 if (!UI.isSafari &&
a6357e82 159 (document.documentElement.requestFullscreen ||
160 document.documentElement.mozRequestFullScreen ||
161 document.documentElement.webkitRequestFullscreen ||
162 document.body.msRequestFullscreen)) {
ae510306 163 document.getElementById('noVNC_fullscreen_button').style.display = "inline";
7d1dc09a 164 Util.addEvent(window, 'fullscreenchange', UI.updateFullscreenButton);
165 Util.addEvent(window, 'mozfullscreenchange', UI.updateFullscreenButton);
166 Util.addEvent(window, 'webkitfullscreenchange', UI.updateFullscreenButton);
167 Util.addEvent(window, 'msfullscreenchange', UI.updateFullscreenButton);
168 }
169
5b7598ac 170 Util.addEvent(window, 'load', UI.keyboardinputReset);
171
b9efece4
SM
172 // While connected we want to display a confirmation dialogue
173 // if the user tries to leave the page
da17d036 174 Util.addEvent(window, 'beforeunload', function (e) {
58ded70d 175 if (UI.rfb && UI.rfb_state === 'normal') {
b9efece4
SM
176 var msg = "You are currently connected.";
177 e.returnValue = msg;
178 return msg;
da17d036 179 } else {
b9efece4 180 return void 0; // To prevent the dialogue when disconnected
bbbf42bb 181 }
b9efece4 182 });
bbbf42bb
SR
183
184 // Show description by default when hosted at for kanaka.github.com
185 if (location.host === "kanaka.github.io") {
186 // Open the description dialog
ae510306 187 document.getElementById('noVNC_description').style.display = "block";
bbbf42bb
SR
188 } else {
189 // Show the connect panel on first load unless autoconnecting
190 if (autoconnect === UI.connSettingsOpen) {
191 UI.toggleConnectPanel();
192 }
193 }
194
195 // Add mouse event click/focus/blur event handlers to the UI
196 UI.addMouseHandlers();
197
198 if (typeof callback === "function") {
199 callback(UI.rfb);
200 }
201 },
202
0bd2cbac 203 initRFB: function() {
d9fc1c7b 204 try {
ae510306 205 UI.rfb = new RFB({'target': document.getElementById('noVNC_canvas'),
d9fc1c7b 206 'onUpdateState': UI.updateState,
9e45354e 207 'onXvpInit': UI.updateXvpButton,
4d26f58e 208 'onClipboard': UI.clipboardReceive,
777cb7a0 209 'onFBUComplete': UI.initialResize,
31ddaa1c 210 'onFBResize': UI.updateViewDrag,
d9fc1c7b
SR
211 'onDesktopName': UI.updateDocumentTitle});
212 return true;
213 } catch (exc) {
214 UI.updateState(null, 'fatal', null, 'Unable to create RFB client -- ' + exc);
215 return false;
216 }
e543525f
SR
217 },
218
bbbf42bb
SR
219 addMouseHandlers: function() {
220 // Setup interface handlers that can't be inline
ae510306
SR
221 document.getElementById("noVNC_view_drag_button").onclick = UI.toggleViewDrag;
222 document.getElementById("noVNC_mouse_button0").onclick = function () { UI.setMouseButton(1); };
223 document.getElementById("noVNC_mouse_button1").onclick = function () { UI.setMouseButton(2); };
224 document.getElementById("noVNC_mouse_button2").onclick = function () { UI.setMouseButton(4); };
225 document.getElementById("noVNC_mouse_button4").onclick = function () { UI.setMouseButton(0); };
226 document.getElementById("noVNC_keyboard_button").onclick = UI.showKeyboard;
227
228 document.getElementById("noVNC_keyboardinput").oninput = UI.keyInput;
229 document.getElementById("noVNC_keyboardinput").onblur = UI.hideKeyboard;
230 document.getElementById("noVNC_keyboardinput").onsubmit = function () { return false; };
231
232 document.getElementById("noVNC_toggleExtraKeys_button").onclick = UI.toggleExtraKeys;
233 document.getElementById("noVNC_toggleCtrl_button").onclick = UI.toggleCtrl;
234 document.getElementById("noVNC_toggleAlt_button").onclick = UI.toggleAlt;
235 document.getElementById("noVNC_sendTab_button").onclick = UI.sendTab;
236 document.getElementById("noVNC_sendEsc_button").onclick = UI.sendEsc;
237
238 document.getElementById("noVNC_sendCtrlAltDel_button").onclick = UI.sendCtrlAltDel;
239 document.getElementById("noVNC_xvpShutdown_button").onclick = function() { UI.rfb.xvpShutdown(); },
240 document.getElementById("noVNC_xvpReboot_button").onclick = function() { UI.rfb.xvpReboot(); },
241 document.getElementById("noVNC_xvpReset_button").onclick = function() { UI.rfb.xvpReset(); },
242 document.getElementById("noVNC_status").onclick = UI.popupStatus;
243 document.getElementById("noVNC_popup_status").onclick = UI.closePopup;
244 document.getElementById("noVNC_toggleXvp_button").onclick = UI.toggleXvpPanel;
245 document.getElementById("noVNC_clipboard_button").onclick = UI.toggleClipboardPanel;
246 document.getElementById("noVNC_fullscreen_button").onclick = UI.toggleFullscreen;
247 document.getElementById("noVNC_settings_button").onclick = UI.toggleSettingsPanel;
248 document.getElementById("noVNC_connectPanel_button").onclick = UI.toggleConnectPanel;
249 document.getElementById("noVNC_disconnect_button").onclick = UI.disconnect;
250 document.getElementById("noVNC_description_button").onclick = UI.toggleConnectPanel;
251
252 document.getElementById("noVNC_clipboard_text").onfocus = UI.displayBlur;
253 document.getElementById("noVNC_clipboard_text").onblur = UI.displayFocus;
254 document.getElementById("noVNC_clipboard_text").onchange = UI.clipboardSend;
255 document.getElementById("noVNC_clipboard_clear_button").onclick = UI.clipboardClear;
256
257 document.getElementById("noVNC_settings_menu").onmouseover = UI.displayBlur;
258 document.getElementById("noVNC_settings_menu").onmouseover = UI.displayFocus;
259 document.getElementById("noVNC_settings_apply").onclick = UI.settingsApply;
260
261 document.getElementById("noVNC_connect_button").onclick = UI.connect;
262
263 document.getElementById("noVNC_setting_resize").onchange = UI.enableDisableViewClip;
bbbf42bb
SR
264 },
265
95dd6001 266/* ------^-------
267 * /INIT
268 * ==============
269 * VISUAL
270 * ------v------*/
58ded70d 271
29475d77 272 updateState: function(rfb, state, oldstate, msg) {
273 UI.rfb_state = state;
274 var klass;
275 switch (state) {
276 case 'failed':
277 case 'fatal':
278 klass = "noVNC_status_error";
279 break;
280 case 'normal':
281 klass = "noVNC_status_normal";
282 break;
283 case 'disconnected':
ae510306
SR
284 document.getElementById('noVNC_logo').style.display = "block";
285 document.getElementById('noVNC_screen').style.display = "none";
29475d77 286 /* falls through */
287 case 'loaded':
288 klass = "noVNC_status_normal";
289 break;
290 case 'password':
291 UI.toggleConnectPanel();
fdedbafb 292
ae510306
SR
293 document.getElementById('noVNC_connect_button').value = "Send Password";
294 document.getElementById('noVNC_connect_button').onclick = UI.setPassword;
295 document.getElementById('noVNC_setting_password').focus();
fdedbafb 296
29475d77 297 klass = "noVNC_status_warn";
298 break;
299 default:
300 klass = "noVNC_status_warn";
301 break;
302 }
fdedbafb 303
29475d77 304 if (typeof(msg) !== 'undefined') {
ae510306
SR
305 document.getElementById('noVNC_control_bar').setAttribute("class", klass);
306 document.getElementById('noVNC_status').innerHTML = msg;
fdedbafb 307 }
29475d77 308
309 UI.updateVisualState();
fdedbafb 310 },
311
29475d77 312 // Disable/enable controls depending on connection state
313 updateVisualState: function() {
314 var connected = UI.rfb && UI.rfb_state === 'normal';
fdedbafb 315
29475d77 316 //Util.Debug(">> updateVisualState");
ae510306
SR
317 document.getElementById('noVNC_setting_encrypt').disabled = connected;
318 document.getElementById('noVNC_setting_true_color').disabled = connected;
29475d77 319 if (Util.browserSupportsCursorURIs()) {
ae510306 320 document.getElementById('noVNC_setting_cursor').disabled = connected;
29475d77 321 } else {
322 UI.updateSetting('cursor', !UI.isTouchDevice);
ae510306 323 document.getElementById('noVNC_setting_cursor').disabled = true;
29475d77 324 }
fdedbafb 325
29475d77 326 UI.enableDisableViewClip();
ae510306
SR
327 document.getElementById('noVNC_setting_resize').disabled = connected;
328 document.getElementById('noVNC_setting_shared').disabled = connected;
329 document.getElementById('noVNC_setting_view_only').disabled = connected;
330 document.getElementById('noVNC_setting_path').disabled = connected;
331 document.getElementById('noVNC_setting_repeaterID').disabled = connected;
fdedbafb 332
29475d77 333 if (connected) {
334 UI.setViewClip();
335 UI.setMouseButton(1);
ae510306
SR
336 document.getElementById('noVNC_clipboard_button').style.display = "inline";
337 document.getElementById('noVNC_keyboard_button').style.display = "inline";
338 document.getElementById('noVNC_extra_keys').style.display = "";
339 document.getElementById('noVNC_sendCtrlAltDel_button').style.display = "inline";
29475d77 340 } else {
341 UI.setMouseButton();
ae510306
SR
342 document.getElementById('noVNC_clipboard_button').style.display = "none";
343 document.getElementById('noVNC_keyboard_button').style.display = "none";
344 document.getElementById('noVNC_extra_keys').style.display = "none";
345 document.getElementById('noVNC_sendCtrlAltDel_button').style.display = "none";
9e45354e 346 UI.updateXvpButton(0);
29475d77 347 }
fdedbafb 348
29475d77 349 // State change disables viewport dragging.
350 // It is enabled (toggled) by direct click on the button
351 UI.updateViewDrag(false);
352
353 switch (UI.rfb_state) {
354 case 'fatal':
355 case 'failed':
356 case 'disconnected':
ae510306
SR
357 document.getElementById('noVNC_connectPanel_button').style.display = "";
358 document.getElementById('noVNC_disconnect_button').style.display = "none";
29475d77 359 UI.connSettingsOpen = false;
360 UI.toggleConnectPanel();
361 break;
362 case 'loaded':
ae510306
SR
363 document.getElementById('noVNC_connectPanel_button').style.display = "";
364 document.getElementById('noVNC_disconnect_button').style.display = "none";
29475d77 365 break;
366 default:
ae510306
SR
367 document.getElementById('noVNC_connectPanel_button').style.display = "none";
368 document.getElementById('noVNC_disconnect_button').style.display = "";
29475d77 369 break;
370 }
371
372 //Util.Debug("<< updateVisualState");
373 },
374
4e471b5b 375 popupStatus: function(text) {
ae510306 376 var psp = document.getElementById('noVNC_popup_status');
4e471b5b 377
378 clearTimeout(UI.popupStatusTimeout);
379
380 if (typeof text === 'string') {
381 psp.innerHTML = text;
fdedbafb 382 } else {
ae510306 383 psp.innerHTML = document.getElementById('noVNC_status').innerHTML;
fdedbafb 384 }
4e471b5b 385 psp.style.display = "block";
386 psp.style.left = window.innerWidth/2 -
387 parseInt(window.getComputedStyle(psp).width)/2 -30 + "px";
388
389 // Show the popup for a maximum of 1.5 seconds
67685d07 390 UI.popupStatusTimeout = setTimeout(UI.closePopup, 1500);
fdedbafb 391 },
392
4e471b5b 393 closePopup: function() {
394 clearTimeout(UI.popupStatusTimeout);
ae510306 395 document.getElementById('noVNC_popup_status').style.display = "none";
4e471b5b 396 },
397
95dd6001 398/* ------^-------
399 * /VISUAL
400 * ==============
401 * SETTINGS
402 * ------v------*/
403
45c70c9e 404 // Initial page load read/initialization of settings
405 initSetting: function(name, defVal) {
406 // Check Query string followed by cookie
407 var val = WebUtil.getConfigVar(name);
408 if (val === null) {
409 val = WebUtil.readSetting(name, defVal);
bbbf42bb 410 }
45c70c9e 411 UI.updateSetting(name, val);
bbbf42bb
SR
412 return val;
413 },
414
415 // Update cookie and form control setting. If value is not set, then
416 // updates from control to current cookie setting.
417 updateSetting: function(name, value) {
418
419 // Save the cookie for this session
420 if (typeof value !== 'undefined') {
421 WebUtil.writeSetting(name, value);
422 }
423
424 // Update the settings control
425 value = UI.getSetting(name);
426
ae510306 427 var ctrl = document.getElementById('noVNC_setting_' + name);
bbbf42bb
SR
428 if (ctrl.type === 'checkbox') {
429 ctrl.checked = value;
430
431 } else if (typeof ctrl.options !== 'undefined') {
432 for (var i = 0; i < ctrl.options.length; i += 1) {
433 if (ctrl.options[i].value === value) {
434 ctrl.selectedIndex = i;
435 break;
436 }
437 }
438 } else {
439 /*Weird IE9 error leads to 'null' appearring
440 in textboxes instead of ''.*/
441 if (value === null) {
442 value = "";
443 }
444 ctrl.value = value;
445 }
446 },
447
448 // Save control setting to cookie
449 saveSetting: function(name) {
ae510306 450 var val, ctrl = document.getElementById('noVNC_setting_' + name);
bbbf42bb
SR
451 if (ctrl.type === 'checkbox') {
452 val = ctrl.checked;
453 } else if (typeof ctrl.options !== 'undefined') {
454 val = ctrl.options[ctrl.selectedIndex].value;
455 } else {
456 val = ctrl.value;
457 }
458 WebUtil.writeSetting(name, val);
459 //Util.Debug("Setting saved '" + name + "=" + val + "'");
460 return val;
461 },
462
bbbf42bb
SR
463 // Force a setting to be a certain value
464 forceSetting: function(name, val) {
465 UI.updateSetting(name, val);
466 return val;
467 },
468
45c70c9e 469 // Read form control compatible setting from cookie
470 getSetting: function(name) {
ae510306 471 var ctrl = document.getElementById('noVNC_setting_' + name);
45c70c9e 472 var val = WebUtil.readSetting(name);
473 if (typeof val !== 'undefined' && val !== null && ctrl.type === 'checkbox') {
474 if (val.toString().toLowerCase() in {'0':1, 'no':1, 'false':1}) {
475 val = false;
4f19e5c6 476 } else {
45c70c9e 477 val = true;
4f19e5c6 478 }
bbbf42bb 479 }
bbbf42bb 480 return val;
bbbf42bb
SR
481 },
482
95dd6001 483 // Save/apply settings when 'Apply' button is pressed
484 settingsApply: function() {
485 //Util.Debug(">> settingsApply");
486 UI.saveSetting('encrypt');
487 UI.saveSetting('true_color');
488 if (Util.browserSupportsCursorURIs()) {
489 UI.saveSetting('cursor');
bbbf42bb 490 }
bbbf42bb 491
95dd6001 492 UI.saveSetting('resize');
bbbf42bb 493
95dd6001 494 if (UI.getSetting('resize') === 'downscale' || UI.getSetting('resize') === 'scale') {
495 UI.forceSetting('clip', false);
7d1dc09a 496 }
7d1dc09a 497
95dd6001 498 UI.saveSetting('clip');
499 UI.saveSetting('shared');
500 UI.saveSetting('view_only');
501 UI.saveSetting('path');
502 UI.saveSetting('repeaterID');
503 UI.saveSetting('stylesheet');
504 UI.saveSetting('logging');
505
506 // Settings with immediate (non-connected related) effect
507 WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
508 WebUtil.init_logging(UI.getSetting('logging'));
509 UI.setViewClip();
510 UI.updateViewDrag();
511 //Util.Debug("<< settingsApply");
7d1dc09a 512 },
513
45c70c9e 514 // Open menu
515 openSettingsMenu: function() {
bbbf42bb 516 // Close the description panel
ae510306 517 document.getElementById('noVNC_description').style.display = "none";
bbbf42bb
SR
518 // Close clipboard panel if open
519 if (UI.clipboardOpen === true) {
520 UI.toggleClipboardPanel();
521 }
45c70c9e 522 // Close connection settings if open
523 if (UI.connSettingsOpen === true) {
524 UI.toggleConnectPanel();
525 }
bbbf42bb
SR
526 // Close XVP panel if open
527 if (UI.xvpOpen === true) {
528 UI.toggleXvpPanel();
529 }
ae510306
SR
530 document.getElementById('noVNC_settings').style.display = "block";
531 document.getElementById('noVNC_settings_button').className = "noVNC_status_button_selected";
45c70c9e 532 UI.settingsOpen = true;
533 },
bbbf42bb 534
45c70c9e 535 // Close menu (without applying settings)
536 closeSettingsMenu: function() {
ae510306
SR
537 document.getElementById('noVNC_settings').style.display = "none";
538 document.getElementById('noVNC_settings_button').className = "noVNC_status_button";
45c70c9e 539 UI.settingsOpen = false;
bbbf42bb
SR
540 },
541
542 // Toggle the settings menu:
543 // On open, settings are refreshed from saved cookies.
544 // On close, settings are applied
545 toggleSettingsPanel: function() {
546 // Close the description panel
ae510306 547 document.getElementById('noVNC_description').style.display = "none";
bbbf42bb
SR
548 if (UI.settingsOpen) {
549 UI.settingsApply();
550 UI.closeSettingsMenu();
551 } else {
552 UI.updateSetting('encrypt');
553 UI.updateSetting('true_color');
58ded70d 554 if (Util.browserSupportsCursorURIs()) {
bbbf42bb
SR
555 UI.updateSetting('cursor');
556 } else {
557 UI.updateSetting('cursor', !UI.isTouchDevice);
ae510306 558 document.getElementById('noVNC_setting_cursor').disabled = true;
bbbf42bb
SR
559 }
560 UI.updateSetting('clip');
f8b399d7 561 UI.updateSetting('resize');
bbbf42bb
SR
562 UI.updateSetting('shared');
563 UI.updateSetting('view_only');
564 UI.updateSetting('path');
565 UI.updateSetting('repeaterID');
566 UI.updateSetting('stylesheet');
567 UI.updateSetting('logging');
568
569 UI.openSettingsMenu();
570 }
571 },
572
95dd6001 573/* ------^-------
574 * /SETTINGS
575 * ==============
576 * XVP
577 * ------v------*/
578
bbbf42bb
SR
579 // Show the XVP panel
580 toggleXvpPanel: function() {
bbbf42bb 581 // Close the description panel
ae510306 582 document.getElementById('noVNC_description').style.display = "none";
bbbf42bb
SR
583 // Close settings if open
584 if (UI.settingsOpen === true) {
585 UI.settingsApply();
586 UI.closeSettingsMenu();
bbbf42bb
SR
587 }
588 // Close connection settings if open
589 if (UI.connSettingsOpen === true) {
590 UI.toggleConnectPanel();
591 }
bbbf42bb
SR
592 // Close clipboard panel if open
593 if (UI.clipboardOpen === true) {
594 UI.toggleClipboardPanel();
bbbf42bb 595 }
bbbf42bb
SR
596 // Toggle XVP panel
597 if (UI.xvpOpen === true) {
ae510306
SR
598 document.getElementById('noVNC_xvp').style.display = "none";
599 document.getElementById('noVNC_toggleXvp_button').className = "noVNC_status_button";
bbbf42bb
SR
600 UI.xvpOpen = false;
601 } else {
ae510306
SR
602 document.getElementById('noVNC_xvp').style.display = "block";
603 document.getElementById('noVNC_toggleXvp_button').className = "noVNC_status_button_selected";
bbbf42bb 604 UI.xvpOpen = true;
bbbf42bb 605 }
bbbf42bb
SR
606 },
607
608 // Disable/enable XVP button
9e45354e 609 updateXvpButton: function(ver) {
bbbf42bb 610 if (ver >= 1) {
ae510306 611 document.getElementById('noVNC_toggleXvp_button').style.display = 'inline';
bbbf42bb 612 } else {
ae510306 613 document.getElementById('noVNC_toggleXvp_button').style.display = 'none';
bbbf42bb
SR
614 // Close XVP panel if open
615 if (UI.xvpOpen === true) {
616 UI.toggleXvpPanel();
617 }
618 }
619 },
620
95dd6001 621/* ------^-------
622 * /XVP
623 * ==============
624 * CLIPBOARD
625 * ------v------*/
f8b399d7 626
bbbf42bb
SR
627 // Show the clipboard panel
628 toggleClipboardPanel: function() {
629 // Close the description panel
ae510306 630 document.getElementById('noVNC_description').style.display = "none";
bbbf42bb
SR
631 // Close settings if open
632 if (UI.settingsOpen === true) {
633 UI.settingsApply();
634 UI.closeSettingsMenu();
635 }
636 // Close connection settings if open
637 if (UI.connSettingsOpen === true) {
638 UI.toggleConnectPanel();
639 }
bbbf42bb
SR
640 // Close XVP panel if open
641 if (UI.xvpOpen === true) {
642 UI.toggleXvpPanel();
643 }
644 // Toggle Clipboard Panel
645 if (UI.clipboardOpen === true) {
ae510306
SR
646 document.getElementById('noVNC_clipboard').style.display = "none";
647 document.getElementById('noVNC_clipboard_button').className = "noVNC_status_button";
bbbf42bb
SR
648 UI.clipboardOpen = false;
649 } else {
ae510306
SR
650 document.getElementById('noVNC_clipboard').style.display = "block";
651 document.getElementById('noVNC_clipboard_button').className = "noVNC_status_button_selected";
bbbf42bb
SR
652 UI.clipboardOpen = true;
653 }
bbbf42bb
SR
654 },
655
4d26f58e 656 clipboardReceive: function(rfb, text) {
657 Util.Debug(">> UI.clipboardReceive: " + text.substr(0,40) + "...");
ae510306 658 document.getElementById('noVNC_clipboard_text').value = text;
4d26f58e 659 Util.Debug("<< UI.clipboardReceive");
95dd6001 660 },
661
4d26f58e 662 clipboardClear: function() {
ae510306 663 document.getElementById('noVNC_clipboard_text').value = "";
95dd6001 664 UI.rfb.clipboardPasteFrom("");
665 },
666
4d26f58e 667 clipboardSend: function() {
ae510306 668 var text = document.getElementById('noVNC_clipboard_text').value;
4d26f58e 669 Util.Debug(">> UI.clipboardSend: " + text.substr(0,40) + "...");
95dd6001 670 UI.rfb.clipboardPasteFrom(text);
4d26f58e 671 Util.Debug("<< UI.clipboardSend");
95dd6001 672 },
673
674/* ------^-------
675 * /CLIPBOARD
676 * ==============
677 * CONNECTION
678 * ------v------*/
679
ab81ddf5 680 // Show the connection settings panel/menu
681 toggleConnectPanel: function() {
682 // Close the description panel
ae510306 683 document.getElementById('noVNC_description').style.display = "none";
ab81ddf5 684 // Close connection settings if open
685 if (UI.settingsOpen === true) {
686 UI.settingsApply();
687 UI.closeSettingsMenu();
ae510306 688 document.getElementById('noVNC_connectPanel_button').className = "noVNC_status_button";
ab81ddf5 689 }
690 // Close clipboard panel if open
691 if (UI.clipboardOpen === true) {
692 UI.toggleClipboardPanel();
693 }
694 // Close XVP panel if open
695 if (UI.xvpOpen === true) {
696 UI.toggleXvpPanel();
697 }
698
699 // Toggle Connection Panel
700 if (UI.connSettingsOpen === true) {
ae510306
SR
701 document.getElementById('noVNC_controls').style.display = "none";
702 document.getElementById('noVNC_connectPanel_button').className = "noVNC_status_button";
ab81ddf5 703 UI.connSettingsOpen = false;
704 UI.saveSetting('host');
705 UI.saveSetting('port');
706 UI.saveSetting('token');
707 //UI.saveSetting('password');
708 } else {
ae510306
SR
709 document.getElementById('noVNC_controls').style.display = "block";
710 document.getElementById('noVNC_connectPanel_button').className = "noVNC_status_button_selected";
ab81ddf5 711 UI.connSettingsOpen = true;
ae510306 712 document.getElementById('noVNC_setting_host').focus();
ab81ddf5 713 }
bbbf42bb
SR
714 },
715
716 connect: function() {
717 UI.closeSettingsMenu();
c506a481 718 UI.toggleConnectPanel();
f00b1e37 719
ae510306
SR
720 var host = document.getElementById('noVNC_setting_host').value;
721 var port = document.getElementById('noVNC_setting_port').value;
722 var password = document.getElementById('noVNC_setting_password').value;
723 var token = document.getElementById('noVNC_setting_token').value;
724 var path = document.getElementById('noVNC_setting_path').value;
c55f05f6
MXPN
725
726 //if token is in path then ignore the new token variable
727 if (token) {
728 path = WebUtil.injectParamIfMissing(path, "token", token);
729 }
730
bbbf42bb
SR
731 if ((!host) || (!port)) {
732 throw new Error("Must set host and port");
733 }
53fc7392 734
d9fc1c7b 735 if (!UI.initRFB()) return;
58ded70d 736
bbbf42bb
SR
737 UI.rfb.set_encrypt(UI.getSetting('encrypt'));
738 UI.rfb.set_true_color(UI.getSetting('true_color'));
739 UI.rfb.set_local_cursor(UI.getSetting('cursor'));
740 UI.rfb.set_shared(UI.getSetting('shared'));
741 UI.rfb.set_view_only(UI.getSetting('view_only'));
742 UI.rfb.set_repeaterID(UI.getSetting('repeaterID'));
53fc7392 743
bbbf42bb 744 UI.rfb.connect(host, port, password, path);
8e0f0088 745
bbbf42bb 746 //Close dialog.
67685d07 747 setTimeout(UI.setBarPosition, 100);
ae510306
SR
748 document.getElementById('noVNC_logo').style.display = "none";
749 document.getElementById('noVNC_screen').style.display = "inline";
bbbf42bb 750 },
5299db1a 751
bbbf42bb
SR
752 disconnect: function() {
753 UI.closeSettingsMenu();
754 UI.rfb.disconnect();
8e0f0088 755
f8b399d7 756 // Restore the callback used for initial resize
ab81ddf5 757 UI.rfb.set_onFBUComplete(UI.initialResize);
f8b399d7 758
ae510306
SR
759 document.getElementById('noVNC_logo').style.display = "block";
760 document.getElementById('noVNC_screen').style.display = "none";
fdedbafb 761
e543525f 762 // Don't display the connection settings until we're actually disconnected
bbbf42bb
SR
763 },
764
95dd6001 765 setPassword: function() {
ae510306 766 UI.rfb.sendPassword(document.getElementById('noVNC_setting_password').value);
95dd6001 767 //Reset connect button.
ae510306
SR
768 document.getElementById('noVNC_connect_button').value = "Connect";
769 document.getElementById('noVNC_connect_button').onclick = UI.connect;
95dd6001 770 //Hide connection panel.
771 UI.toggleConnectPanel();
772 return false;
773 },
58ded70d 774
95dd6001 775/* ------^-------
776 * /CONNECTION
777 * ==============
778 * FULLSCREEN
779 * ------v------*/
780
7d1dc09a 781 toggleFullscreen: function() {
782 if (document.fullscreenElement || // alternative standard method
783 document.mozFullScreenElement || // currently working methods
784 document.webkitFullscreenElement ||
a6357e82 785 document.msFullscreenElement) {
7d1dc09a 786 if (document.exitFullscreen) {
787 document.exitFullscreen();
788 } else if (document.mozCancelFullScreen) {
789 document.mozCancelFullScreen();
790 } else if (document.webkitExitFullscreen) {
791 document.webkitExitFullscreen();
792 } else if (document.msExitFullscreen) {
793 document.msExitFullscreen();
794 }
795 } else {
796 if (document.documentElement.requestFullscreen) {
797 document.documentElement.requestFullscreen();
798 } else if (document.documentElement.mozRequestFullScreen) {
799 document.documentElement.mozRequestFullScreen();
800 } else if (document.documentElement.webkitRequestFullscreen) {
801 document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
802 } else if (document.body.msRequestFullscreen) {
803 document.body.msRequestFullscreen();
804 }
805 }
a6357e82 806 UI.enableDisableViewClip();
7d1dc09a 807 UI.updateFullscreenButton();
bbbf42bb
SR
808 },
809
7d1dc09a 810 updateFullscreenButton: function() {
811 if (document.fullscreenElement || // alternative standard method
812 document.mozFullScreenElement || // currently working methods
813 document.webkitFullscreenElement ||
814 document.msFullscreenElement ) {
ae510306 815 document.getElementById('noVNC_fullscreen_button').className = "noVNC_status_button_selected";
7d1dc09a 816 } else {
ae510306 817 document.getElementById('noVNC_fullscreen_button').className = "noVNC_status_button";
7d1dc09a 818 }
819 },
820
95dd6001 821/* ------^-------
822 * /FULLSCREEN
823 * ==============
824 * RESIZE
825 * ------v------*/
777cb7a0 826
827 // Apply remote resizing or local scaling
0bd2cbac 828 applyResizeMode: function() {
58ded70d
SR
829 if (!UI.rfb) return;
830
777cb7a0 831 var screen = UI.screenSize();
832
833 if (screen && UI.rfb_state === 'normal' && UI.rfb.get_display()) {
834
835 var display = UI.rfb.get_display();
836 var resizeMode = UI.getSetting('resize');
837
838 if (resizeMode === 'remote') {
839
840 // Request changing the resolution of the remote display to
841 // the size of the local browser viewport.
842
843 // In order to not send multiple requests before the browser-resize
844 // is finished we wait 0.5 seconds before sending the request.
845 clearTimeout(UI.resizeTimeout);
846 UI.resizeTimeout = setTimeout(function(){
847
848 // Limit the viewport to the size of the browser window
849 display.set_maxWidth(screen.w);
850 display.set_maxHeight(screen.h);
851
5fd3f88e 852 Util.Debug('Attempting requestDesktopSize(' +
777cb7a0 853 screen.w + ', ' + screen.h + ')');
854
855 // Request a remote size covering the viewport
5fd3f88e 856 UI.rfb.requestDesktopSize(screen.w, screen.h);
777cb7a0 857 }, 500);
858
859 } else if (resizeMode === 'scale' || resizeMode === 'downscale') {
860 var downscaleOnly = resizeMode === 'downscale';
861 var scaleRatio = display.autoscale(screen.w, screen.h, downscaleOnly);
862 UI.rfb.get_mouse().set_scale(scaleRatio);
863 Util.Debug('Scaling by ' + UI.rfb.get_mouse().get_scale());
864 }
865 }
bbbf42bb
SR
866 },
867
777cb7a0 868 // The screen is always the same size as the available viewport
869 // in the browser window minus the height of the control bar
0bd2cbac 870 screenSize: function() {
ae510306 871 var screen = document.getElementById('noVNC_screen');
777cb7a0 872
873 // Hide the scrollbars until the size is calculated
874 screen.style.overflow = "hidden";
875
876 var pos = Util.getPosition(screen);
877 var w = pos.width;
878 var h = pos.height;
879
880 screen.style.overflow = "visible";
881
882 if (isNaN(w) || isNaN(h)) {
883 return false;
884 } else {
885 return {w: w, h: h};
886 }
bbbf42bb
SR
887 },
888
777cb7a0 889 // Normally we only apply the current resize mode after a window resize
890 // event. This means that when a new connection is opened, there is no
891 // resize mode active.
892 // We have to wait until the first FBU because this is where the client
893 // will find the supported encodings of the server. Some calls later in
894 // the chain is dependant on knowing the server-capabilities.
895 initialResize: function(rfb, fbu) {
896 UI.applyResizeMode();
897 // After doing this once, we remove the callback.
898 UI.rfb.set_onFBUComplete(function() { });
bbbf42bb
SR
899 },
900
95dd6001 901/* ------^-------
902 * /RESIZE
903 * ==============
904 * CLIPPING
905 * ------v------*/
906
30bfff81 907 // Set and configure viewport clipping
bbbf42bb
SR
908 setViewClip: function(clip) {
909 var display;
910 if (UI.rfb) {
911 display = UI.rfb.get_display();
912 } else {
a6357e82 913 UI.forceSetting('clip', clip);
bbbf42bb
SR
914 return;
915 }
8e0f0088 916
bbbf42bb
SR
917 var cur_clip = display.get_viewport();
918
919 if (typeof(clip) !== 'boolean') {
920 // Use current setting
921 clip = UI.getSetting('clip');
922 }
8e0f0088 923
bbbf42bb
SR
924 if (clip && !cur_clip) {
925 // Turn clipping on
926 UI.updateSetting('clip', true);
927 } else if (!clip && cur_clip) {
928 // Turn clipping off
929 UI.updateSetting('clip', false);
930 display.set_viewport(false);
31ddaa1c 931 // Disable max dimensions
fdedbafb 932 display.set_maxWidth(0);
933 display.set_maxHeight(0);
f8b399d7 934 display.viewportChangeSize();
bbbf42bb
SR
935 }
936 if (UI.getSetting('clip')) {
937 // If clipping, update clipping settings
bbbf42bb 938 display.set_viewport(true);
fdedbafb 939
553864e8 940 var size = UI.screenSize();
fdedbafb 941 if (size) {
942 display.set_maxWidth(size.w);
943 display.set_maxHeight(size.h);
944
945 // Hide potential scrollbars that can skew the position
ae510306 946 document.getElementById('noVNC_screen').style.overflow = "hidden";
fdedbafb 947
948 // The x position marks the left margin of the canvas,
949 // remove the margin from both sides to keep it centered
ae510306 950 var new_w = size.w - (2 * Util.getPosition(document.getElementById('noVNC_canvas')).x);
fdedbafb 951
ae510306 952 document.getElementById('noVNC_screen').style.overflow = "visible";
fdedbafb 953
954 display.viewportChangeSize(new_w, size.h);
955 }
bbbf42bb
SR
956 }
957 },
958
30bfff81 959 // Handle special cases where clipping is forced on/off or locked
0bd2cbac 960 enableDisableViewClip: function() {
ae510306 961 var resizeSetting = document.getElementById('noVNC_setting_resize');
a6357e82 962 var connected = UI.rfb && UI.rfb_state === 'normal';
963
f620259b 964 if (UI.isSafari) {
965 // Safari auto-hides the scrollbars which makes them
966 // impossible to use in most cases
967 UI.setViewClip(true);
ae510306 968 document.getElementById('noVNC_setting_clip').disabled = true;
682fd02b 969 } else if (resizeSetting.value === 'downscale' || resizeSetting.value === 'scale') {
a6357e82 970 // Disable clipping if we are scaling
971 UI.setViewClip(false);
ae510306 972 document.getElementById('noVNC_setting_clip').disabled = true;
a6357e82 973 } else if (document.msFullscreenElement) {
974 // The browser is IE and we are in fullscreen mode.
975 // - We need to force clipping while in fullscreen since
976 // scrollbars doesn't work.
4e471b5b 977 UI.popupStatus("Forcing clipping mode since scrollbars aren't supported by IE in fullscreen");
a6357e82 978 UI.rememberedClipSetting = UI.getSetting('clip');
979 UI.setViewClip(true);
ae510306 980 document.getElementById('noVNC_setting_clip').disabled = true;
a6357e82 981 } else if (document.body.msRequestFullscreen && UI.rememberedClip !== null) {
982 // Restore view clip to what it was before fullscreen on IE
983 UI.setViewClip(UI.rememberedClipSetting);
ae510306 984 document.getElementById('noVNC_setting_clip').disabled = connected || UI.isTouchDevice;
30bfff81 985 } else {
ae510306 986 document.getElementById('noVNC_setting_clip').disabled = connected || UI.isTouchDevice;
30bfff81 987 if (UI.isTouchDevice) {
a6357e82 988 UI.setViewClip(true);
30bfff81 989 }
990 }
991 },
992
95dd6001 993/* ------^-------
994 * /CLIPPING
995 * ==============
996 * VIEWDRAG
997 * ------v------*/
998
e00698fe 999 // Update the viewport drag state
31ddaa1c 1000 updateViewDrag: function(drag) {
58ded70d 1001 if (!UI.rfb) return;
bbbf42bb 1002
ae510306 1003 var viewDragButton = document.getElementById('noVNC_view_drag_button');
bbbf42bb 1004
e00698fe 1005 // Check if viewport drag is possible. It is only possible
1006 // if the remote display is clipping the client display.
f8b399d7 1007 if (UI.rfb_state === 'normal' &&
1008 UI.rfb.get_display().get_viewport() &&
fdedbafb 1009 UI.rfb.get_display().clippingDisplay()) {
31ddaa1c 1010
e00698fe 1011 viewDragButton.style.display = "inline";
1012 viewDragButton.disabled = false;
29a0e6a8 1013
31ddaa1c 1014 } else {
e00698fe 1015 // The size of the remote display is the same or smaller
1016 // than the client display. Make sure viewport drag isn't
1017 // active when it can't be used.
31ddaa1c 1018 if (UI.rfb.get_viewportDrag) {
e00698fe 1019 viewDragButton.className = "noVNC_status_button";
31ddaa1c 1020 UI.rfb.set_viewportDrag(false);
1021 }
1022
e00698fe 1023 // The button is disabled instead of hidden on touch devices
31ddaa1c 1024 if (UI.rfb_state === 'normal' && UI.isTouchDevice) {
e00698fe 1025 viewDragButton.style.display = "inline";
1026 viewDragButton.disabled = true;
31ddaa1c 1027 } else {
e00698fe 1028 viewDragButton.style.display = "none";
31ddaa1c 1029 }
1030 return;
1031 }
1032
1033 if (typeof(drag) !== "undefined" &&
1034 typeof(drag) !== "object") {
1035 if (drag) {
e00698fe 1036 viewDragButton.className = "noVNC_status_button_selected";
31ddaa1c 1037 UI.rfb.set_viewportDrag(true);
1038 } else {
e00698fe 1039 viewDragButton.className = "noVNC_status_button";
31ddaa1c 1040 UI.rfb.set_viewportDrag(false);
1041 }
1042 }
1043 },
29a0e6a8 1044
31ddaa1c 1045 toggleViewDrag: function() {
1046 if (!UI.rfb) return;
1047
ae510306 1048 var viewDragButton = document.getElementById('noVNC_view_drag_button');
31ddaa1c 1049 if (UI.rfb.get_viewportDrag()) {
e00698fe 1050 viewDragButton.className = "noVNC_status_button";
31ddaa1c 1051 UI.rfb.set_viewportDrag(false);
f8b399d7 1052 } else {
e00698fe 1053 viewDragButton.className = "noVNC_status_button_selected";
31ddaa1c 1054 UI.rfb.set_viewportDrag(true);
f8b399d7 1055 }
1056 },
1057
95dd6001 1058/* ------^-------
1059 * /VIEWDRAG
1060 * ==============
1061 * KEYBOARD
1062 * ------v------*/
fdf21468 1063
bbbf42bb
SR
1064 // On touch devices, show the OS keyboard
1065 showKeyboard: function() {
ae510306
SR
1066 var kbi = document.getElementById('noVNC_keyboardinput');
1067 var skb = document.getElementById('noVNC_keyboard_button');
bbbf42bb
SR
1068 var l = kbi.value.length;
1069 if(UI.keyboardVisible === false) {
1070 kbi.focus();
1071 try { kbi.setSelectionRange(l, l); } // Move the caret to the end
1072 catch (err) {} // setSelectionRange is undefined in Google Chrome
1073 UI.keyboardVisible = true;
1074 skb.className = "noVNC_status_button_selected";
1075 } else if(UI.keyboardVisible === true) {
1076 kbi.blur();
1077 skb.className = "noVNC_status_button";
1078 UI.keyboardVisible = false;
1079 }
1080 },
1081
fdf21468 1082 hideKeyboard: function() {
ae510306 1083 document.getElementById('noVNC_keyboard_button').className = "noVNC_status_button";
fdf21468 1084 //Weird bug in iOS if you change keyboardVisible
1085 //here it does not actually occur so next time
1086 //you click keyboard icon it doesnt work.
1087 UI.hideKeyboardTimeout = setTimeout(function() {
1088 UI.keyboardVisible = false;
1089 },100);
1090 },
1091
bbbf42bb
SR
1092 keepKeyboard: function() {
1093 clearTimeout(UI.hideKeyboardTimeout);
1094 if(UI.keyboardVisible === true) {
ae510306
SR
1095 document.getElementById('noVNC_keyboardinput').focus();
1096 document.getElementById('noVNC_keyboard_button').className = "noVNC_status_button_selected";
bbbf42bb 1097 } else if(UI.keyboardVisible === false) {
ae510306
SR
1098 document.getElementById('noVNC_keyboardinput').blur();
1099 document.getElementById('noVNC_keyboard_button').className = "noVNC_status_button";
bbbf42bb
SR
1100 }
1101 },
1102
1103 keyboardinputReset: function() {
ae510306 1104 var kbi = document.getElementById('noVNC_keyboardinput');
bbbf42bb
SR
1105 kbi.value = new Array(UI.defaultKeyboardinputLen).join("_");
1106 UI.lastKeyboardinput = kbi.value;
1107 },
1108
1109 // When normal keyboard events are left uncought, use the input events from
1110 // the keyboardinput element instead and generate the corresponding key events.
1111 // This code is required since some browsers on Android are inconsistent in
1112 // sending keyCodes in the normal keyboard events when using on screen keyboards.
1113 keyInput: function(event) {
3b8ec46f 1114
58ded70d 1115 if (!UI.rfb) return;
3b8ec46f 1116
bbbf42bb 1117 var newValue = event.target.value;
1138bdd4 1118
1119 if (!UI.lastKeyboardinput) {
1120 UI.keyboardinputReset();
1121 }
cb3e4deb 1122 var oldValue = UI.lastKeyboardinput;
bbbf42bb
SR
1123
1124 var newLen;
1125 try {
1126 // Try to check caret position since whitespace at the end
1127 // will not be considered by value.length in some browsers
1128 newLen = Math.max(event.target.selectionStart, newValue.length);
1129 } catch (err) {
1130 // selectionStart is undefined in Google Chrome
1131 newLen = newValue.length;
1132 }
1133 var oldLen = oldValue.length;
1134
1135 var backspaces;
1136 var inputs = newLen - oldLen;
1137 if (inputs < 0) {
1138 backspaces = -inputs;
1139 } else {
1140 backspaces = 0;
1141 }
8e0f0088 1142
bbbf42bb
SR
1143 // Compare the old string with the new to account for
1144 // text-corrections or other input that modify existing text
1145 var i;
1146 for (i = 0; i < Math.min(oldLen, newLen); i++) {
1147 if (newValue.charAt(i) != oldValue.charAt(i)) {
1148 inputs = newLen - i;
1149 backspaces = oldLen - i;
1150 break;
1151 }
1152 }
1153
1154 // Send the key events
1155 for (i = 0; i < backspaces; i++) {
ae510306 1156 UI.rfb.sendKey(KeyTable.XK_BackSpace);
bbbf42bb
SR
1157 }
1158 for (i = newLen - inputs; i < newLen; i++) {
1159 UI.rfb.sendKey(newValue.charCodeAt(i));
1160 }
1161
1162 // Control the text content length in the keyboardinput element
1163 if (newLen > 2 * UI.defaultKeyboardinputLen) {
1164 UI.keyboardinputReset();
1165 } else if (newLen < 1) {
1166 // There always have to be some text in the keyboardinput
1167 // element with which backspace can interact.
1168 UI.keyboardinputReset();
1169 // This sometimes causes the keyboard to disappear for a second
1170 // but it is required for the android keyboard to recognize that
1171 // text has been added to the field
1172 event.target.blur();
1173 // This has to be ran outside of the input handler in order to work
67685d07 1174 setTimeout(UI.keepKeyboard, 0);
bbbf42bb
SR
1175 } else {
1176 UI.lastKeyboardinput = newValue;
1177 }
1178 },
1179
cd611a53 1180 toggleExtraKeys: function() {
bbbf42bb
SR
1181 UI.keepKeyboard();
1182 if(UI.extraKeysVisible === false) {
ae510306
SR
1183 document.getElementById('noVNC_toggleCtrl_button').style.display = "inline";
1184 document.getElementById('noVNC_toggleAlt_button').style.display = "inline";
1185 document.getElementById('noVNC_sendTab_button').style.display = "inline";
1186 document.getElementById('noVNC_sendEsc_button').style.display = "inline";
1187 document.getElementById('noVNC_toggleExtraKeys_button').className = "noVNC_status_button_selected";
bbbf42bb
SR
1188 UI.extraKeysVisible = true;
1189 } else if(UI.extraKeysVisible === true) {
ae510306
SR
1190 document.getElementById('noVNC_toggleCtrl_button').style.display = "";
1191 document.getElementById('noVNC_toggleAlt_button').style.display = "";
1192 document.getElementById('noVNC_sendTab_button').style.display = "";
1193 document.getElementById('noVNC_sendEsc_button').style.display = "";
1194 document.getElementById('noVNC_toggleExtraKeys_button').className = "noVNC_status_button";
bbbf42bb
SR
1195 UI.extraKeysVisible = false;
1196 }
1197 },
1198
fdf21468 1199 sendEsc: function() {
1200 UI.keepKeyboard();
ae510306 1201 UI.rfb.sendKey(KeyTable.XK_Escape);
fdf21468 1202 },
1203
1204 sendTab: function() {
1205 UI.keepKeyboard();
ae510306 1206 UI.rfb.sendKey(KeyTable.XK_Tab);
fdf21468 1207 },
1208
bbbf42bb
SR
1209 toggleCtrl: function() {
1210 UI.keepKeyboard();
1211 if(UI.ctrlOn === false) {
ae510306
SR
1212 UI.rfb.sendKey(KeyTable.XK_Control_L, true);
1213 document.getElementById('noVNC_toggleCtrl_button').className = "noVNC_status_button_selected";
bbbf42bb
SR
1214 UI.ctrlOn = true;
1215 } else if(UI.ctrlOn === true) {
ae510306
SR
1216 UI.rfb.sendKey(KeyTable.XK_Control_L, false);
1217 document.getElementById('noVNC_toggleCtrl_button').className = "noVNC_status_button";
bbbf42bb
SR
1218 UI.ctrlOn = false;
1219 }
1220 },
1221
1222 toggleAlt: function() {
1223 UI.keepKeyboard();
1224 if(UI.altOn === false) {
ae510306
SR
1225 UI.rfb.sendKey(KeyTable.XK_Alt_L, true);
1226 document.getElementById('noVNC_toggleAlt_button').className = "noVNC_status_button_selected";
bbbf42bb
SR
1227 UI.altOn = true;
1228 } else if(UI.altOn === true) {
ae510306
SR
1229 UI.rfb.sendKey(KeyTable.XK_Alt_L, false);
1230 document.getElementById('noVNC_toggleAlt_button').className = "noVNC_status_button";
bbbf42bb
SR
1231 UI.altOn = false;
1232 }
1233 },
1234
fdf21468 1235 sendCtrlAltDel: function() {
1236 UI.rfb.sendCtrlAltDel();
bbbf42bb
SR
1237 },
1238
95dd6001 1239/* ------^-------
1240 * /KEYBOARD
1241 * ==============
1242 * MISC
1243 * ------v------*/
1244
1245 setMouseButton: function(num) {
1246 if (typeof num === 'undefined') {
1247 // Disable mouse buttons
1248 num = -1;
1249 }
1250 if (UI.rfb) {
1251 UI.rfb.get_mouse().set_touchButton(num);
1252 }
1253
1254 var blist = [0, 1,2,4];
1255 for (var b = 0; b < blist.length; b++) {
ae510306 1256 var button = document.getElementById('noVNC_mouse_button' + blist[b]);
95dd6001 1257 if (blist[b] === num) {
1258 button.style.display = "";
1259 } else {
1260 button.style.display = "none";
1261 }
1262 }
1263 },
1264
1265 displayBlur: function() {
1266 if (!UI.rfb) return;
1267
1268 UI.rfb.get_keyboard().set_focused(false);
1269 UI.rfb.get_mouse().set_focused(false);
1270 },
1271
1272 displayFocus: function() {
1273 if (!UI.rfb) return;
1274
1275 UI.rfb.get_keyboard().set_focused(true);
1276 UI.rfb.get_mouse().set_focused(true);
bbbf42bb
SR
1277 },
1278
95dd6001 1279 // Display the desktop name in the document title
1280 updateDocumentTitle: function(rfb, name) {
1281 document.title = name + " - noVNC";
bbbf42bb
SR
1282 },
1283
bbbf42bb
SR
1284 //Helper to add options to dropdown.
1285 addOption: function(selectbox, text, value) {
1286 var optn = document.createElement("OPTION");
1287 optn.text = text;
1288 optn.value = value;
1289 selectbox.options.add(optn);
1290 },
1291
1292 setBarPosition: function() {
ae510306
SR
1293 document.getElementById('noVNC_control_bar').style.top = (window.pageYOffset) + 'px';
1294 document.getElementById('noVNC_mobile_buttons').style.left = (window.pageXOffset) + 'px';
bbbf42bb 1295
ae510306
SR
1296 var vncwidth = document.getElementById('noVNC_container').style.offsetWidth;
1297 document.getElementById('noVNC_control_bar').style.width = vncwidth + 'px';
bbbf42bb
SR
1298 }
1299
95dd6001 1300/* ------^-------
1301 * /MISC
1302 * ==============
1303 */
bbbf42bb 1304 };
ae510306
SR
1305
1306 /* [module] UI.load(); */
bbbf42bb 1307})();
ae510306
SR
1308
1309/* [module] export default UI; */