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