blob: e869aa69605292cb2223e3cc1dbd1d1665c8ca49 [file] [log] [blame]
San Mehata430b2b2014-09-23 08:30:51 -07001/*
2 * noVNC: HTML5 VNC client
Greg Hartmanbec50fd2015-06-17 22:16:13 -07003 * Copyright (C) 2012 Joel Martin
4 * Copyright (C) 2013 Samuel Mannehed for Cendio AB
5 * Licensed under MPL 2.0 (see LICENSE.txt)
San Mehata430b2b2014-09-23 08:30:51 -07006 *
7 * See README.md for usage and integration instructions.
8 */
9
Greg Hartmanbec50fd2015-06-17 22:16:13 -070010/* jslint white: false, browser: true */
11/* global window, $D, Util, WebUtil, RFB, Display */
San Mehata430b2b2014-09-23 08:30:51 -070012
Greg Hartmanbec50fd2015-06-17 22:16:13 -070013var UI;
San Mehata430b2b2014-09-23 08:30:51 -070014
Greg Hartmanbec50fd2015-06-17 22:16:13 -070015(function () {
16 "use strict";
San Mehata430b2b2014-09-23 08:30:51 -070017
Greg Hartmanbec50fd2015-06-17 22:16:13 -070018 // Load supporting scripts
19 window.onscriptsload = function () { UI.load(); };
20 window.onload = function () { UI.keyboardinputReset(); };
21 Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
22 "keysymdef.js", "keyboard.js", "input.js", "display.js",
23 "jsunzip.js", "rfb.js", "keysym.js"]);
San Mehata430b2b2014-09-23 08:30:51 -070024
Greg Hartmanbec50fd2015-06-17 22:16:13 -070025 var UI = {
San Mehata430b2b2014-09-23 08:30:51 -070026
Greg Hartmanbec50fd2015-06-17 22:16:13 -070027 rfb_state : 'loaded',
28 settingsOpen : false,
29 connSettingsOpen : false,
30 popupStatusOpen : false,
31 clipboardOpen: false,
32 keyboardVisible: false,
33 hideKeyboardTimeout: null,
34 lastKeyboardinput: null,
35 defaultKeyboardinputLen: 100,
36 extraKeysVisible: false,
37 ctrlOn: false,
38 altOn: false,
39 isTouchDevice: false,
San Mehata430b2b2014-09-23 08:30:51 -070040
Greg Hartmanbec50fd2015-06-17 22:16:13 -070041 // Setup rfb object, load settings from browser storage, then call
42 // UI.init to setup the UI/menus
43 load: function (callback) {
44 WebUtil.initSettings(UI.start, callback);
45 },
San Mehata430b2b2014-09-23 08:30:51 -070046
Greg Hartmanbec50fd2015-06-17 22:16:13 -070047 // Render default UI and initialize settings menu
48 start: function(callback) {
49 UI.isTouchDevice = 'ontouchstart' in document.documentElement;
San Mehata430b2b2014-09-23 08:30:51 -070050
Greg Hartmanbec50fd2015-06-17 22:16:13 -070051 // Stylesheet selection dropdown
52 var sheet = WebUtil.selectStylesheet();
53 var sheets = WebUtil.getStylesheets();
54 var i;
55 for (i = 0; i < sheets.length; i += 1) {
56 UI.addOption($D('noVNC_stylesheet'),sheets[i].title, sheets[i].title);
San Mehata430b2b2014-09-23 08:30:51 -070057 }
San Mehata430b2b2014-09-23 08:30:51 -070058
Greg Hartmanbec50fd2015-06-17 22:16:13 -070059 // Logging selection dropdown
60 var llevels = ['error', 'warn', 'info', 'debug'];
61 for (i = 0; i < llevels.length; i += 1) {
62 UI.addOption($D('noVNC_logging'),llevels[i], llevels[i]);
63 }
San Mehata430b2b2014-09-23 08:30:51 -070064
Greg Hartmanbec50fd2015-06-17 22:16:13 -070065 // Settings with immediate effects
66 UI.initSetting('logging', 'warn');
67 WebUtil.init_logging(UI.getSetting('logging'));
San Mehata430b2b2014-09-23 08:30:51 -070068
Greg Hartmanbec50fd2015-06-17 22:16:13 -070069 UI.initSetting('stylesheet', 'default');
70 WebUtil.selectStylesheet(null);
71 // call twice to get around webkit bug
72 WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
San Mehata430b2b2014-09-23 08:30:51 -070073
Greg Hartmanbec50fd2015-06-17 22:16:13 -070074 // if port == 80 (or 443) then it won't be present and should be
75 // set manually
76 var port = window.location.port;
77 if (!port) {
78 if (window.location.protocol.substring(0,5) == 'https') {
79 port = 443;
80 }
81 else if (window.location.protocol.substring(0,4) == 'http') {
82 port = 80;
83 }
84 }
85
86 /* Populate the controls if defaults are provided in the URL */
87 UI.initSetting('host', window.location.hostname);
88 UI.initSetting('port', port);
89 UI.initSetting('password', '');
90 UI.initSetting('encrypt', (window.location.protocol === "https:"));
91 UI.initSetting('true_color', true);
92 UI.initSetting('cursor', !UI.isTouchDevice);
93 UI.initSetting('shared', true);
94 UI.initSetting('view_only', false);
95 UI.initSetting('path', 'websockify');
96 UI.initSetting('repeaterID', '');
97
98 UI.rfb = new RFB({'target': $D('noVNC_canvas'),
99 'onUpdateState': UI.updateState,
100 'onXvpInit': UI.updateXvpVisualState,
101 'onClipboard': UI.clipReceive,
102 'onDesktopName': UI.updateDocumentTitle});
103
104 var autoconnect = WebUtil.getQueryVar('autoconnect', false);
105 if (autoconnect === 'true' || autoconnect == '1') {
106 autoconnect = true;
107 UI.connect();
108 } else {
109 autoconnect = false;
110 }
111
112 UI.updateVisualState();
113
114 // Show mouse selector buttons on touch screen devices
115 if (UI.isTouchDevice) {
116 // Show mobile buttons
117 $D('noVNC_mobile_buttons').style.display = "inline";
118 UI.setMouseButton();
119 // Remove the address bar
120 setTimeout(function() { window.scrollTo(0, 1); }, 100);
121 UI.forceSetting('clip', true);
122 $D('noVNC_clip').disabled = true;
123 } else {
124 UI.initSetting('clip', false);
125 }
126
127 //iOS Safari does not support CSS position:fixed.
128 //This detects iOS devices and enables javascript workaround.
129 if ((navigator.userAgent.match(/iPhone/i)) ||
130 (navigator.userAgent.match(/iPod/i)) ||
131 (navigator.userAgent.match(/iPad/i))) {
132 //UI.setOnscroll();
133 //UI.setResize();
134 }
135 UI.setBarPosition();
136
137 $D('noVNC_host').focus();
138
139 UI.setViewClip();
140 Util.addEvent(window, 'resize', UI.setViewClip);
141
142 Util.addEvent(window, 'beforeunload', function () {
143 if (UI.rfb_state === 'normal') {
144 return "You are currently connected.";
145 }
146 } );
147
148 // Show description by default when hosted at for kanaka.github.com
149 if (location.host === "kanaka.github.io") {
150 // Open the description dialog
151 $D('noVNC_description').style.display = "block";
152 } else {
153 // Show the connect panel on first load unless autoconnecting
154 if (autoconnect === UI.connSettingsOpen) {
155 UI.toggleConnectPanel();
156 }
157 }
158
159 // Add mouse event click/focus/blur event handlers to the UI
160 UI.addMouseHandlers();
161
162 if (typeof callback === "function") {
163 callback(UI.rfb);
164 }
165 },
166
167 addMouseHandlers: function() {
168 // Setup interface handlers that can't be inline
169 $D("noVNC_view_drag_button").onclick = UI.setViewDrag;
170 $D("noVNC_mouse_button0").onclick = function () { UI.setMouseButton(1); };
171 $D("noVNC_mouse_button1").onclick = function () { UI.setMouseButton(2); };
172 $D("noVNC_mouse_button2").onclick = function () { UI.setMouseButton(4); };
173 $D("noVNC_mouse_button4").onclick = function () { UI.setMouseButton(0); };
174 $D("showKeyboard").onclick = UI.showKeyboard;
175
176 $D("keyboardinput").oninput = UI.keyInput;
177 $D("keyboardinput").onblur = UI.keyInputBlur;
178
179 $D("showExtraKeysButton").onclick = UI.showExtraKeys;
180 $D("toggleCtrlButton").onclick = UI.toggleCtrl;
181 $D("toggleAltButton").onclick = UI.toggleAlt;
182 $D("sendTabButton").onclick = UI.sendTab;
183 $D("sendEscButton").onclick = UI.sendEsc;
184
185 $D("sendCtrlAltDelButton").onclick = UI.sendCtrlAltDel;
186 $D("xvpShutdownButton").onclick = UI.xvpShutdown;
187 $D("xvpRebootButton").onclick = UI.xvpReboot;
188 $D("xvpResetButton").onclick = UI.xvpReset;
189 $D("noVNC_status").onclick = UI.togglePopupStatusPanel;
190 $D("noVNC_popup_status_panel").onclick = UI.togglePopupStatusPanel;
191 $D("xvpButton").onclick = UI.toggleXvpPanel;
192 $D("clipboardButton").onclick = UI.toggleClipboardPanel;
193 $D("settingsButton").onclick = UI.toggleSettingsPanel;
194 $D("connectButton").onclick = UI.toggleConnectPanel;
195 $D("disconnectButton").onclick = UI.disconnect;
196 $D("descriptionButton").onclick = UI.toggleConnectPanel;
197
198 $D("noVNC_clipboard_text").onfocus = UI.displayBlur;
199 $D("noVNC_clipboard_text").onblur = UI.displayFocus;
200 $D("noVNC_clipboard_text").onchange = UI.clipSend;
201 $D("noVNC_clipboard_clear_button").onclick = UI.clipClear;
202
203 $D("noVNC_settings_menu").onmouseover = UI.displayBlur;
204 $D("noVNC_settings_menu").onmouseover = UI.displayFocus;
205 $D("noVNC_apply").onclick = UI.settingsApply;
206
207 $D("noVNC_connect_button").onclick = UI.connect;
208 },
209
210 // Read form control compatible setting from cookie
211 getSetting: function(name) {
212 var ctrl = $D('noVNC_' + name);
213 var val = WebUtil.readSetting(name);
214 if (val !== null && ctrl.type === 'checkbox') {
215 if (val.toString().toLowerCase() in {'0':1, 'no':1, 'false':1}) {
216 val = false;
217 } else {
218 val = true;
219 }
220 }
221 return val;
222 },
223
224 // Update cookie and form control setting. If value is not set, then
225 // updates from control to current cookie setting.
226 updateSetting: function(name, value) {
227
228 // Save the cookie for this session
229 if (typeof value !== 'undefined') {
230 WebUtil.writeSetting(name, value);
231 }
232
233 // Update the settings control
234 value = UI.getSetting(name);
235
236 var ctrl = $D('noVNC_' + name);
237 if (ctrl.type === 'checkbox') {
238 ctrl.checked = value;
239
240 } else if (typeof ctrl.options !== 'undefined') {
241 for (var i = 0; i < ctrl.options.length; i += 1) {
242 if (ctrl.options[i].value === value) {
243 ctrl.selectedIndex = i;
244 break;
245 }
246 }
247 } else {
248 /*Weird IE9 error leads to 'null' appearring
249 in textboxes instead of ''.*/
250 if (value === null) {
251 value = "";
252 }
253 ctrl.value = value;
254 }
255 },
256
257 // Save control setting to cookie
258 saveSetting: function(name) {
259 var val, ctrl = $D('noVNC_' + name);
260 if (ctrl.type === 'checkbox') {
261 val = ctrl.checked;
262 } else if (typeof ctrl.options !== 'undefined') {
263 val = ctrl.options[ctrl.selectedIndex].value;
264 } else {
265 val = ctrl.value;
266 }
267 WebUtil.writeSetting(name, val);
268 //Util.Debug("Setting saved '" + name + "=" + val + "'");
269 return val;
270 },
271
272 // Initial page load read/initialization of settings
273 initSetting: function(name, defVal) {
274 // Check Query string followed by cookie
275 var val = WebUtil.getQueryVar(name);
276 if (val === null) {
277 val = WebUtil.readSetting(name, defVal);
278 }
279 UI.updateSetting(name, val);
280 return val;
281 },
282
283 // Force a setting to be a certain value
284 forceSetting: function(name, val) {
285 UI.updateSetting(name, val);
286 return val;
287 },
San Mehata430b2b2014-09-23 08:30:51 -0700288
289
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700290 // Show the popup status panel
291 togglePopupStatusPanel: function() {
292 var psp = $D('noVNC_popup_status_panel');
293 if (UI.popupStatusOpen === true) {
294 psp.style.display = "none";
295 UI.popupStatusOpen = false;
296 } else {
297 psp.innerHTML = $D('noVNC_status').innerHTML;
298 psp.style.display = "block";
299 psp.style.left = window.innerWidth/2 -
300 parseInt(window.getComputedStyle(psp, false).width)/2 -30 + "px";
301 UI.popupStatusOpen = true;
302 }
303 },
San Mehata430b2b2014-09-23 08:30:51 -0700304
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700305 // Show the XVP panel
306 toggleXvpPanel: function() {
307 // Close the description panel
308 $D('noVNC_description').style.display = "none";
309 // Close settings if open
310 if (UI.settingsOpen === true) {
311 UI.settingsApply();
312 UI.closeSettingsMenu();
313 }
314 // Close connection settings if open
315 if (UI.connSettingsOpen === true) {
316 UI.toggleConnectPanel();
317 }
318 // Close popup status panel if open
319 if (UI.popupStatusOpen === true) {
320 UI.togglePopupStatusPanel();
321 }
322 // Close clipboard panel if open
323 if (UI.clipboardOpen === true) {
324 UI.toggleClipboardPanel();
325 }
326 // Toggle XVP panel
327 if (UI.xvpOpen === true) {
328 $D('noVNC_xvp').style.display = "none";
329 $D('xvpButton').className = "noVNC_status_button";
330 UI.xvpOpen = false;
331 } else {
332 $D('noVNC_xvp').style.display = "block";
333 $D('xvpButton').className = "noVNC_status_button_selected";
334 UI.xvpOpen = true;
335 }
336 },
San Mehata430b2b2014-09-23 08:30:51 -0700337
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700338 // Show the clipboard panel
339 toggleClipboardPanel: function() {
340 // Close the description panel
341 $D('noVNC_description').style.display = "none";
342 // Close settings if open
343 if (UI.settingsOpen === true) {
344 UI.settingsApply();
345 UI.closeSettingsMenu();
346 }
347 // Close connection settings if open
348 if (UI.connSettingsOpen === true) {
349 UI.toggleConnectPanel();
350 }
351 // Close popup status panel if open
352 if (UI.popupStatusOpen === true) {
353 UI.togglePopupStatusPanel();
354 }
355 // Close XVP panel if open
356 if (UI.xvpOpen === true) {
357 UI.toggleXvpPanel();
358 }
359 // Toggle Clipboard Panel
360 if (UI.clipboardOpen === true) {
361 $D('noVNC_clipboard').style.display = "none";
362 $D('clipboardButton').className = "noVNC_status_button";
363 UI.clipboardOpen = false;
364 } else {
365 $D('noVNC_clipboard').style.display = "block";
366 $D('clipboardButton').className = "noVNC_status_button_selected";
367 UI.clipboardOpen = true;
368 }
369 },
San Mehata430b2b2014-09-23 08:30:51 -0700370
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700371 // Show the connection settings panel/menu
372 toggleConnectPanel: function() {
373 // Close the description panel
374 $D('noVNC_description').style.display = "none";
375 // Close connection settings if open
376 if (UI.settingsOpen === true) {
377 UI.settingsApply();
378 UI.closeSettingsMenu();
379 $D('connectButton').className = "noVNC_status_button";
380 }
381 // Close clipboard panel if open
382 if (UI.clipboardOpen === true) {
383 UI.toggleClipboardPanel();
384 }
385 // Close popup status panel if open
386 if (UI.popupStatusOpen === true) {
387 UI.togglePopupStatusPanel();
388 }
389 // Close XVP panel if open
390 if (UI.xvpOpen === true) {
391 UI.toggleXvpPanel();
392 }
San Mehata430b2b2014-09-23 08:30:51 -0700393
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700394 // Toggle Connection Panel
395 if (UI.connSettingsOpen === true) {
396 $D('noVNC_controls').style.display = "none";
397 $D('connectButton').className = "noVNC_status_button";
398 UI.connSettingsOpen = false;
399 UI.saveSetting('host');
400 UI.saveSetting('port');
401 //UI.saveSetting('password');
402 } else {
403 $D('noVNC_controls').style.display = "block";
404 $D('connectButton').className = "noVNC_status_button_selected";
405 UI.connSettingsOpen = true;
406 $D('noVNC_host').focus();
407 }
408 },
San Mehata430b2b2014-09-23 08:30:51 -0700409
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700410 // Toggle the settings menu:
411 // On open, settings are refreshed from saved cookies.
412 // On close, settings are applied
413 toggleSettingsPanel: function() {
414 // Close the description panel
415 $D('noVNC_description').style.display = "none";
416 if (UI.settingsOpen) {
417 UI.settingsApply();
418 UI.closeSettingsMenu();
419 } else {
420 UI.updateSetting('encrypt');
421 UI.updateSetting('true_color');
422 if (UI.rfb.get_display().get_cursor_uri()) {
423 UI.updateSetting('cursor');
424 } else {
425 UI.updateSetting('cursor', !UI.isTouchDevice);
426 $D('noVNC_cursor').disabled = true;
427 }
428 UI.updateSetting('clip');
429 UI.updateSetting('shared');
430 UI.updateSetting('view_only');
431 UI.updateSetting('path');
432 UI.updateSetting('repeaterID');
433 UI.updateSetting('stylesheet');
434 UI.updateSetting('logging');
San Mehata430b2b2014-09-23 08:30:51 -0700435
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700436 UI.openSettingsMenu();
437 }
438 },
San Mehata430b2b2014-09-23 08:30:51 -0700439
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700440 // Open menu
441 openSettingsMenu: function() {
442 // Close the description panel
443 $D('noVNC_description').style.display = "none";
444 // Close clipboard panel if open
445 if (UI.clipboardOpen === true) {
446 UI.toggleClipboardPanel();
447 }
448 // Close connection settings if open
449 if (UI.connSettingsOpen === true) {
450 UI.toggleConnectPanel();
451 }
452 // Close popup status panel if open
453 if (UI.popupStatusOpen === true) {
454 UI.togglePopupStatusPanel();
455 }
456 // Close XVP panel if open
457 if (UI.xvpOpen === true) {
458 UI.toggleXvpPanel();
459 }
460 $D('noVNC_settings').style.display = "block";
461 $D('settingsButton').className = "noVNC_status_button_selected";
462 UI.settingsOpen = true;
463 },
San Mehata430b2b2014-09-23 08:30:51 -0700464
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700465 // Close menu (without applying settings)
466 closeSettingsMenu: function() {
467 $D('noVNC_settings').style.display = "none";
468 $D('settingsButton').className = "noVNC_status_button";
469 UI.settingsOpen = false;
470 },
471
472 // Save/apply settings when 'Apply' button is pressed
473 settingsApply: function() {
474 //Util.Debug(">> settingsApply");
475 UI.saveSetting('encrypt');
476 UI.saveSetting('true_color');
477 if (UI.rfb.get_display().get_cursor_uri()) {
478 UI.saveSetting('cursor');
479 }
480 UI.saveSetting('clip');
481 UI.saveSetting('shared');
482 UI.saveSetting('view_only');
483 UI.saveSetting('path');
484 UI.saveSetting('repeaterID');
485 UI.saveSetting('stylesheet');
486 UI.saveSetting('logging');
487
488 // Settings with immediate (non-connected related) effect
489 WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
490 WebUtil.init_logging(UI.getSetting('logging'));
491 UI.setViewClip();
492 UI.setViewDrag(UI.rfb.get_viewportDrag());
493 //Util.Debug("<< settingsApply");
494 },
San Mehata430b2b2014-09-23 08:30:51 -0700495
496
497
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700498 setPassword: function() {
499 UI.rfb.sendPassword($D('noVNC_password').value);
500 //Reset connect button.
501 $D('noVNC_connect_button').value = "Connect";
502 $D('noVNC_connect_button').onclick = UI.Connect;
503 //Hide connection panel.
504 UI.toggleConnectPanel();
505 return false;
506 },
San Mehata430b2b2014-09-23 08:30:51 -0700507
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700508 sendCtrlAltDel: function() {
509 UI.rfb.sendCtrlAltDel();
510 },
San Mehata430b2b2014-09-23 08:30:51 -0700511
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700512 xvpShutdown: function() {
513 UI.rfb.xvpShutdown();
514 },
San Mehata430b2b2014-09-23 08:30:51 -0700515
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700516 xvpReboot: function() {
517 UI.rfb.xvpReboot();
518 },
San Mehata430b2b2014-09-23 08:30:51 -0700519
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700520 xvpReset: function() {
521 UI.rfb.xvpReset();
522 },
San Mehata430b2b2014-09-23 08:30:51 -0700523
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700524 setMouseButton: function(num) {
525 if (typeof num === 'undefined') {
526 // Disable mouse buttons
527 num = -1;
528 }
529 if (UI.rfb) {
530 UI.rfb.get_mouse().set_touchButton(num);
531 }
532
533 var blist = [0, 1,2,4];
534 for (var b = 0; b < blist.length; b++) {
535 var button = $D('noVNC_mouse_button' + blist[b]);
536 if (blist[b] === num) {
537 button.style.display = "";
538 } else {
539 button.style.display = "none";
540 }
541 }
542 },
543
544 updateState: function(rfb, state, oldstate, msg) {
545 UI.rfb_state = state;
546 var klass;
547 switch (state) {
548 case 'failed':
549 case 'fatal':
550 klass = "noVNC_status_error";
551 break;
552 case 'normal':
553 klass = "noVNC_status_normal";
554 break;
555 case 'disconnected':
556 $D('noVNC_logo').style.display = "block";
557 /* falls through */
558 case 'loaded':
559 klass = "noVNC_status_normal";
560 break;
561 case 'password':
562 UI.toggleConnectPanel();
563
564 $D('noVNC_connect_button').value = "Send Password";
565 $D('noVNC_connect_button').onclick = UI.setPassword;
566 $D('noVNC_password').focus();
567
568 klass = "noVNC_status_warn";
569 break;
570 default:
571 klass = "noVNC_status_warn";
572 break;
573 }
574
575 if (typeof(msg) !== 'undefined') {
576 $D('noVNC-control-bar').setAttribute("class", klass);
577 $D('noVNC_status').innerHTML = msg;
578 }
579
580 UI.updateVisualState();
581 },
582
583 // Disable/enable controls depending on connection state
584 updateVisualState: function() {
585 var connected = UI.rfb_state === 'normal' ? true : false;
586
587 //Util.Debug(">> updateVisualState");
588 $D('noVNC_encrypt').disabled = connected;
589 $D('noVNC_true_color').disabled = connected;
590 if (UI.rfb && UI.rfb.get_display() &&
591 UI.rfb.get_display().get_cursor_uri()) {
592 $D('noVNC_cursor').disabled = connected;
593 } else {
594 UI.updateSetting('cursor', !UI.isTouchDevice);
595 $D('noVNC_cursor').disabled = true;
596 }
597 $D('noVNC_shared').disabled = connected;
598 $D('noVNC_view_only').disabled = connected;
599 $D('noVNC_path').disabled = connected;
600 $D('noVNC_repeaterID').disabled = connected;
601
602 if (connected) {
603 UI.setViewClip();
604 UI.setMouseButton(1);
605 $D('clipboardButton').style.display = "inline";
606 $D('showKeyboard').style.display = "inline";
607 $D('noVNC_extra_keys').style.display = "";
608 $D('sendCtrlAltDelButton').style.display = "inline";
609 } else {
610 UI.setMouseButton();
611 $D('clipboardButton').style.display = "none";
612 $D('showKeyboard').style.display = "none";
613 $D('noVNC_extra_keys').style.display = "none";
614 $D('sendCtrlAltDelButton').style.display = "none";
615 UI.updateXvpVisualState(0);
616 }
617
618 // State change disables viewport dragging.
619 // It is enabled (toggled) by direct click on the button
620 UI.setViewDrag(false);
621
622 switch (UI.rfb_state) {
623 case 'fatal':
624 case 'failed':
625 case 'loaded':
626 case 'disconnected':
627 $D('connectButton').style.display = "";
628 $D('disconnectButton').style.display = "none";
629 break;
630 default:
631 $D('connectButton').style.display = "none";
632 $D('disconnectButton').style.display = "";
633 break;
634 }
635
636 //Util.Debug("<< updateVisualState");
637 },
638
639 // Disable/enable XVP button
640 updateXvpVisualState: function(ver) {
641 if (ver >= 1) {
642 $D('xvpButton').style.display = 'inline';
643 } else {
644 $D('xvpButton').style.display = 'none';
645 // Close XVP panel if open
646 if (UI.xvpOpen === true) {
647 UI.toggleXvpPanel();
648 }
649 }
650 },
651
652 // Display the desktop name in the document title
653 updateDocumentTitle: function(rfb, name) {
654 document.title = name + " - noVNC";
655 },
656
657 clipReceive: function(rfb, text) {
658 Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "...");
659 $D('noVNC_clipboard_text').value = text;
660 Util.Debug("<< UI.clipReceive");
661 },
662
663 connect: function() {
664 UI.closeSettingsMenu();
San Mehata430b2b2014-09-23 08:30:51 -0700665 UI.toggleConnectPanel();
666
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700667 var host = $D('noVNC_host').value;
668 var port = $D('noVNC_port').value;
669 var password = $D('noVNC_password').value;
670 var path = $D('noVNC_path').value;
671 if ((!host) || (!port)) {
672 throw new Error("Must set host and port");
673 }
San Mehata430b2b2014-09-23 08:30:51 -0700674
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700675 UI.rfb.set_encrypt(UI.getSetting('encrypt'));
676 UI.rfb.set_true_color(UI.getSetting('true_color'));
677 UI.rfb.set_local_cursor(UI.getSetting('cursor'));
678 UI.rfb.set_shared(UI.getSetting('shared'));
679 UI.rfb.set_view_only(UI.getSetting('view_only'));
680 UI.rfb.set_repeaterID(UI.getSetting('repeaterID'));
San Mehata430b2b2014-09-23 08:30:51 -0700681
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700682 UI.rfb.connect(host, port, password, path);
San Mehata430b2b2014-09-23 08:30:51 -0700683
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700684 //Close dialog.
685 setTimeout(UI.setBarPosition, 100);
686 $D('noVNC_logo').style.display = "none";
687 },
San Mehata430b2b2014-09-23 08:30:51 -0700688
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700689 disconnect: function() {
690 UI.closeSettingsMenu();
691 UI.rfb.disconnect();
San Mehata430b2b2014-09-23 08:30:51 -0700692
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700693 $D('noVNC_logo').style.display = "block";
694 UI.connSettingsOpen = false;
695 UI.toggleConnectPanel();
696 },
San Mehata430b2b2014-09-23 08:30:51 -0700697
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700698 displayBlur: function() {
699 UI.rfb.get_keyboard().set_focused(false);
700 UI.rfb.get_mouse().set_focused(false);
701 },
San Mehata430b2b2014-09-23 08:30:51 -0700702
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700703 displayFocus: function() {
704 UI.rfb.get_keyboard().set_focused(true);
705 UI.rfb.get_mouse().set_focused(true);
706 },
San Mehata430b2b2014-09-23 08:30:51 -0700707
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700708 clipClear: function() {
709 $D('noVNC_clipboard_text').value = "";
710 UI.rfb.clipboardPasteFrom("");
711 },
San Mehata430b2b2014-09-23 08:30:51 -0700712
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700713 clipSend: function() {
714 var text = $D('noVNC_clipboard_text').value;
715 Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "...");
716 UI.rfb.clipboardPasteFrom(text);
717 Util.Debug("<< UI.clipSend");
718 },
San Mehata430b2b2014-09-23 08:30:51 -0700719
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700720 // Enable/disable and configure viewport clipping
721 setViewClip: function(clip) {
722 var display;
723 if (UI.rfb) {
724 display = UI.rfb.get_display();
725 } else {
726 return;
727 }
San Mehata430b2b2014-09-23 08:30:51 -0700728
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700729 var cur_clip = display.get_viewport();
San Mehata430b2b2014-09-23 08:30:51 -0700730
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700731 if (typeof(clip) !== 'boolean') {
732 // Use current setting
733 clip = UI.getSetting('clip');
734 }
San Mehata430b2b2014-09-23 08:30:51 -0700735
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700736 if (clip && !cur_clip) {
737 // Turn clipping on
738 UI.updateSetting('clip', true);
739 } else if (!clip && cur_clip) {
740 // Turn clipping off
741 UI.updateSetting('clip', false);
742 display.set_viewport(false);
743 $D('noVNC_canvas').style.position = 'static';
744 display.viewportChange();
745 }
746 if (UI.getSetting('clip')) {
747 // If clipping, update clipping settings
748 $D('noVNC_canvas').style.position = 'absolute';
749 var pos = Util.getPosition($D('noVNC_canvas'));
750 var new_w = window.innerWidth - pos.x;
751 var new_h = window.innerHeight - pos.y;
752 display.set_viewport(true);
753 display.viewportChange(0, 0, new_w, new_h);
754 }
755 },
San Mehata430b2b2014-09-23 08:30:51 -0700756
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700757 // Toggle/set/unset the viewport drag/move button
758 setViewDrag: function(drag) {
759 var vmb = $D('noVNC_view_drag_button');
760 if (!UI.rfb) { return; }
San Mehata430b2b2014-09-23 08:30:51 -0700761
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700762 if (UI.rfb_state === 'normal' &&
763 UI.rfb.get_display().get_viewport()) {
764 vmb.style.display = "inline";
765 } else {
766 vmb.style.display = "none";
767 }
San Mehata430b2b2014-09-23 08:30:51 -0700768
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700769 if (typeof(drag) === "undefined" ||
770 typeof(drag) === "object") {
771 // If not specified, then toggle
772 drag = !UI.rfb.get_viewportDrag();
773 }
774 if (drag) {
775 vmb.className = "noVNC_status_button_selected";
776 UI.rfb.set_viewportDrag(true);
777 } else {
778 vmb.className = "noVNC_status_button";
779 UI.rfb.set_viewportDrag(false);
780 }
781 },
San Mehata430b2b2014-09-23 08:30:51 -0700782
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700783 // On touch devices, show the OS keyboard
784 showKeyboard: function() {
785 var kbi = $D('keyboardinput');
786 var skb = $D('showKeyboard');
787 var l = kbi.value.length;
788 if(UI.keyboardVisible === false) {
789 kbi.focus();
790 try { kbi.setSelectionRange(l, l); } // Move the caret to the end
791 catch (err) {} // setSelectionRange is undefined in Google Chrome
792 UI.keyboardVisible = true;
793 skb.className = "noVNC_status_button_selected";
794 } else if(UI.keyboardVisible === true) {
795 kbi.blur();
796 skb.className = "noVNC_status_button";
797 UI.keyboardVisible = false;
798 }
799 },
San Mehata430b2b2014-09-23 08:30:51 -0700800
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700801 keepKeyboard: function() {
802 clearTimeout(UI.hideKeyboardTimeout);
803 if(UI.keyboardVisible === true) {
804 $D('keyboardinput').focus();
805 $D('showKeyboard').className = "noVNC_status_button_selected";
806 } else if(UI.keyboardVisible === false) {
807 $D('keyboardinput').blur();
808 $D('showKeyboard').className = "noVNC_status_button";
809 }
810 },
San Mehata430b2b2014-09-23 08:30:51 -0700811
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700812 keyboardinputReset: function() {
813 var kbi = $D('keyboardinput');
814 kbi.value = new Array(UI.defaultKeyboardinputLen).join("_");
815 UI.lastKeyboardinput = kbi.value;
816 },
San Mehata430b2b2014-09-23 08:30:51 -0700817
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700818 // When normal keyboard events are left uncought, use the input events from
819 // the keyboardinput element instead and generate the corresponding key events.
820 // This code is required since some browsers on Android are inconsistent in
821 // sending keyCodes in the normal keyboard events when using on screen keyboards.
822 keyInput: function(event) {
823 var newValue = event.target.value;
824 var oldValue = UI.lastKeyboardinput;
San Mehata430b2b2014-09-23 08:30:51 -0700825
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700826 var newLen;
827 try {
828 // Try to check caret position since whitespace at the end
829 // will not be considered by value.length in some browsers
830 newLen = Math.max(event.target.selectionStart, newValue.length);
831 } catch (err) {
832 // selectionStart is undefined in Google Chrome
833 newLen = newValue.length;
834 }
835 var oldLen = oldValue.length;
San Mehata430b2b2014-09-23 08:30:51 -0700836
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700837 var backspaces;
838 var inputs = newLen - oldLen;
839 if (inputs < 0) {
840 backspaces = -inputs;
841 } else {
842 backspaces = 0;
843 }
San Mehata430b2b2014-09-23 08:30:51 -0700844
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700845 // Compare the old string with the new to account for
846 // text-corrections or other input that modify existing text
847 var i;
848 for (i = 0; i < Math.min(oldLen, newLen); i++) {
849 if (newValue.charAt(i) != oldValue.charAt(i)) {
850 inputs = newLen - i;
851 backspaces = oldLen - i;
852 break;
853 }
854 }
San Mehata430b2b2014-09-23 08:30:51 -0700855
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700856 // Send the key events
857 for (i = 0; i < backspaces; i++) {
858 UI.rfb.sendKey(XK_BackSpace);
859 }
860 for (i = newLen - inputs; i < newLen; i++) {
861 UI.rfb.sendKey(newValue.charCodeAt(i));
862 }
San Mehata430b2b2014-09-23 08:30:51 -0700863
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700864 // Control the text content length in the keyboardinput element
865 if (newLen > 2 * UI.defaultKeyboardinputLen) {
866 UI.keyboardinputReset();
867 } else if (newLen < 1) {
868 // There always have to be some text in the keyboardinput
869 // element with which backspace can interact.
870 UI.keyboardinputReset();
871 // This sometimes causes the keyboard to disappear for a second
872 // but it is required for the android keyboard to recognize that
873 // text has been added to the field
874 event.target.blur();
875 // This has to be ran outside of the input handler in order to work
876 setTimeout(function() { UI.keepKeyboard(); }, 0);
877 } else {
878 UI.lastKeyboardinput = newValue;
879 }
880 },
San Mehata430b2b2014-09-23 08:30:51 -0700881
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700882 keyInputBlur: function() {
883 $D('showKeyboard').className = "noVNC_status_button";
884 //Weird bug in iOS if you change keyboardVisible
885 //here it does not actually occur so next time
886 //you click keyboard icon it doesnt work.
887 UI.hideKeyboardTimeout = setTimeout(function() { UI.setKeyboard(); },100);
888 },
San Mehata430b2b2014-09-23 08:30:51 -0700889
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700890 showExtraKeys: function() {
891 UI.keepKeyboard();
892 if(UI.extraKeysVisible === false) {
893 $D('toggleCtrlButton').style.display = "inline";
894 $D('toggleAltButton').style.display = "inline";
895 $D('sendTabButton').style.display = "inline";
896 $D('sendEscButton').style.display = "inline";
897 $D('showExtraKeysButton').className = "noVNC_status_button_selected";
898 UI.extraKeysVisible = true;
899 } else if(UI.extraKeysVisible === true) {
900 $D('toggleCtrlButton').style.display = "";
901 $D('toggleAltButton').style.display = "";
902 $D('sendTabButton').style.display = "";
903 $D('sendEscButton').style.display = "";
904 $D('showExtraKeysButton').className = "noVNC_status_button";
905 UI.extraKeysVisible = false;
906 }
907 },
San Mehata430b2b2014-09-23 08:30:51 -0700908
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700909 toggleCtrl: function() {
910 UI.keepKeyboard();
911 if(UI.ctrlOn === false) {
912 UI.rfb.sendKey(XK_Control_L, true);
913 $D('toggleCtrlButton').className = "noVNC_status_button_selected";
914 UI.ctrlOn = true;
915 } else if(UI.ctrlOn === true) {
916 UI.rfb.sendKey(XK_Control_L, false);
917 $D('toggleCtrlButton').className = "noVNC_status_button";
918 UI.ctrlOn = false;
919 }
920 },
San Mehata430b2b2014-09-23 08:30:51 -0700921
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700922 toggleAlt: function() {
923 UI.keepKeyboard();
924 if(UI.altOn === false) {
925 UI.rfb.sendKey(XK_Alt_L, true);
926 $D('toggleAltButton').className = "noVNC_status_button_selected";
927 UI.altOn = true;
928 } else if(UI.altOn === true) {
929 UI.rfb.sendKey(XK_Alt_L, false);
930 $D('toggleAltButton').className = "noVNC_status_button";
931 UI.altOn = false;
932 }
933 },
San Mehata430b2b2014-09-23 08:30:51 -0700934
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700935 sendTab: function() {
936 UI.keepKeyboard();
937 UI.rfb.sendKey(XK_Tab);
938 },
San Mehata430b2b2014-09-23 08:30:51 -0700939
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700940 sendEsc: function() {
941 UI.keepKeyboard();
942 UI.rfb.sendKey(XK_Escape);
943 },
San Mehata430b2b2014-09-23 08:30:51 -0700944
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700945 setKeyboard: function() {
946 UI.keyboardVisible = false;
947 },
San Mehata430b2b2014-09-23 08:30:51 -0700948
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700949 // iOS < Version 5 does not support position fixed. Javascript workaround:
950 setOnscroll: function() {
951 window.onscroll = function() {
952 UI.setBarPosition();
953 };
954 },
San Mehata430b2b2014-09-23 08:30:51 -0700955
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700956 setResize: function () {
957 window.onResize = function() {
958 UI.setBarPosition();
959 };
960 },
San Mehata430b2b2014-09-23 08:30:51 -0700961
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700962 //Helper to add options to dropdown.
963 addOption: function(selectbox, text, value) {
964 var optn = document.createElement("OPTION");
965 optn.text = text;
966 optn.value = value;
967 selectbox.options.add(optn);
968 },
969
970 setBarPosition: function() {
971 $D('noVNC-control-bar').style.top = (window.pageYOffset) + 'px';
972 $D('noVNC_mobile_buttons').style.left = (window.pageXOffset) + 'px';
973
974 var vncwidth = $D('noVNC_screen').style.offsetWidth;
975 $D('noVNC-control-bar').style.width = vncwidth + 'px';
976 }
977
San Mehata430b2b2014-09-23 08:30:51 -0700978 };
Greg Hartmanbec50fd2015-06-17 22:16:13 -0700979})();