]> git.proxmox.com Git - mirror_novnc.git/blame - include/ui.js
Remove last bits of websockify cruft
[mirror_novnc.git] / include / ui.js
CommitLineData
15046f00
JM
1/*
2 * noVNC: HTML5 VNC client
d58f8b51 3 * Copyright (C) 2012 Joel Martin
406a8b4e 4 * Copyright (C) 2013 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
f8b399d7 18 var resizeTimeout;
19
bbbf42bb
SR
20 // Load supporting scripts
21 window.onscriptsload = function () { UI.load(); };
bbbf42bb
SR
22 Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
23 "keysymdef.js", "keyboard.js", "input.js", "display.js",
24 "jsunzip.js", "rfb.js", "keysym.js"]);
25
bd6874e0 26 UI = {
bbbf42bb
SR
27
28 rfb_state : 'loaded',
29 settingsOpen : false,
30 connSettingsOpen : false,
31 popupStatusOpen : false,
32 clipboardOpen: false,
33 keyboardVisible: false,
34 hideKeyboardTimeout: null,
35 lastKeyboardinput: null,
36 defaultKeyboardinputLen: 100,
37 extraKeysVisible: false,
38 ctrlOn: false,
39 altOn: false,
40 isTouchDevice: false,
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
f8b399d7 48 onresize: function (callback) {
49 if (UI.getSetting('resize')) {
50 var innerW = window.innerWidth;
51 var innerH = window.innerHeight;
52 var controlbarH = $D('noVNC-control-bar').offsetHeight;
53 // For some unknown reason the container is higher than the canvas,
54 // 5px higher in Firefox and 4px higher in Chrome
55 var padding = 5;
56 if (innerW !== undefined && innerH !== undefined)
57 UI.rfb.setDesktopSize(innerW, innerH - controlbarH - padding);
58 }
59 },
60
bbbf42bb
SR
61 // Render default UI and initialize settings menu
62 start: function(callback) {
63 UI.isTouchDevice = 'ontouchstart' in document.documentElement;
64
65 // Stylesheet selection dropdown
66 var sheet = WebUtil.selectStylesheet();
67 var sheets = WebUtil.getStylesheets();
68 var i;
69 for (i = 0; i < sheets.length; i += 1) {
70 UI.addOption($D('noVNC_stylesheet'),sheets[i].title, sheets[i].title);
71 }
72
73 // Logging selection dropdown
74 var llevels = ['error', 'warn', 'info', 'debug'];
75 for (i = 0; i < llevels.length; i += 1) {
76 UI.addOption($D('noVNC_logging'),llevels[i], llevels[i]);
77 }
78
79 // Settings with immediate effects
80 UI.initSetting('logging', 'warn');
81 WebUtil.init_logging(UI.getSetting('logging'));
82
83 UI.initSetting('stylesheet', 'default');
84 WebUtil.selectStylesheet(null);
85 // call twice to get around webkit bug
86 WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
87
88 // if port == 80 (or 443) then it won't be present and should be
89 // set manually
90 var port = window.location.port;
91 if (!port) {
92 if (window.location.protocol.substring(0,5) == 'https') {
93 port = 443;
94 }
95 else if (window.location.protocol.substring(0,4) == 'http') {
96 port = 80;
97 }
98 }
99
100 /* Populate the controls if defaults are provided in the URL */
101 UI.initSetting('host', window.location.hostname);
102 UI.initSetting('port', port);
103 UI.initSetting('password', '');
104 UI.initSetting('encrypt', (window.location.protocol === "https:"));
105 UI.initSetting('true_color', true);
106 UI.initSetting('cursor', !UI.isTouchDevice);
f8b399d7 107 UI.initSetting('resize', false);
bbbf42bb
SR
108 UI.initSetting('shared', true);
109 UI.initSetting('view_only', false);
110 UI.initSetting('path', 'websockify');
111 UI.initSetting('repeaterID', '');
112
113 UI.rfb = new RFB({'target': $D('noVNC_canvas'),
114 'onUpdateState': UI.updateState,
115 'onXvpInit': UI.updateXvpVisualState,
116 'onClipboard': UI.clipReceive,
f8b399d7 117 'onFBUComplete': UI.FBUComplete,
118 'onFBResize': UI.updateViewDragButton,
bbbf42bb
SR
119 'onDesktopName': UI.updateDocumentTitle});
120
121 var autoconnect = WebUtil.getQueryVar('autoconnect', false);
122 if (autoconnect === 'true' || autoconnect == '1') {
123 autoconnect = true;
124 UI.connect();
125 } else {
126 autoconnect = false;
127 }
128
129 UI.updateVisualState();
130
131 // Show mouse selector buttons on touch screen devices
132 if (UI.isTouchDevice) {
133 // Show mobile buttons
134 $D('noVNC_mobile_buttons').style.display = "inline";
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
143 //iOS Safari does not support CSS position:fixed.
144 //This detects iOS devices and enables javascript workaround.
145 if ((navigator.userAgent.match(/iPhone/i)) ||
146 (navigator.userAgent.match(/iPod/i)) ||
147 (navigator.userAgent.match(/iPad/i))) {
148 //UI.setOnscroll();
149 //UI.setResize();
150 }
151 UI.setBarPosition();
152
153 $D('noVNC_host').focus();
154
155 UI.setViewClip();
f8b399d7 156
157 Util.addEvent(window, 'resize', function () {
158 UI.setViewClip();
159 // When the window has been resized, wait until the size remains
160 // the same for 0.5 seconds before sending the request for changing
161 // the resolution of the session
162 clearTimeout(resizeTimeout);
163 resizeTimeout = setTimeout(function(){
164 UI.onresize();
165 }, 500);
166 } );
bbbf42bb 167
5b7598ac 168 Util.addEvent(window, 'load', UI.keyboardinputReset);
169
bbbf42bb
SR
170 Util.addEvent(window, 'beforeunload', function () {
171 if (UI.rfb_state === 'normal') {
172 return "You are currently connected.";
173 }
174 } );
175
176 // Show description by default when hosted at for kanaka.github.com
177 if (location.host === "kanaka.github.io") {
178 // Open the description dialog
179 $D('noVNC_description').style.display = "block";
180 } else {
181 // Show the connect panel on first load unless autoconnecting
182 if (autoconnect === UI.connSettingsOpen) {
183 UI.toggleConnectPanel();
184 }
185 }
186
187 // Add mouse event click/focus/blur event handlers to the UI
188 UI.addMouseHandlers();
189
190 if (typeof callback === "function") {
191 callback(UI.rfb);
192 }
193 },
194
195 addMouseHandlers: function() {
196 // Setup interface handlers that can't be inline
197 $D("noVNC_view_drag_button").onclick = UI.setViewDrag;
198 $D("noVNC_mouse_button0").onclick = function () { UI.setMouseButton(1); };
199 $D("noVNC_mouse_button1").onclick = function () { UI.setMouseButton(2); };
200 $D("noVNC_mouse_button2").onclick = function () { UI.setMouseButton(4); };
201 $D("noVNC_mouse_button4").onclick = function () { UI.setMouseButton(0); };
202 $D("showKeyboard").onclick = UI.showKeyboard;
203
204 $D("keyboardinput").oninput = UI.keyInput;
205 $D("keyboardinput").onblur = UI.keyInputBlur;
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;
217 $D("noVNC_status").onclick = UI.togglePopupStatusPanel;
218 $D("noVNC_popup_status_panel").onclick = UI.togglePopupStatusPanel;
219 $D("xvpButton").onclick = UI.toggleXvpPanel;
220 $D("clipboardButton").onclick = UI.toggleClipboardPanel;
221 $D("settingsButton").onclick = UI.toggleSettingsPanel;
222 $D("connectButton").onclick = UI.toggleConnectPanel;
223 $D("disconnectButton").onclick = UI.disconnect;
224 $D("descriptionButton").onclick = UI.toggleConnectPanel;
225
226 $D("noVNC_clipboard_text").onfocus = UI.displayBlur;
227 $D("noVNC_clipboard_text").onblur = UI.displayFocus;
228 $D("noVNC_clipboard_text").onchange = UI.clipSend;
229 $D("noVNC_clipboard_clear_button").onclick = UI.clipClear;
230
231 $D("noVNC_settings_menu").onmouseover = UI.displayBlur;
232 $D("noVNC_settings_menu").onmouseover = UI.displayFocus;
233 $D("noVNC_apply").onclick = UI.settingsApply;
234
235 $D("noVNC_connect_button").onclick = UI.connect;
236 },
237
238 // Read form control compatible setting from cookie
239 getSetting: function(name) {
240 var ctrl = $D('noVNC_' + name);
241 var val = WebUtil.readSetting(name);
f8b399d7 242 if (typeof val !== 'undefined' && val !== null && ctrl.type === 'checkbox') {
bbbf42bb
SR
243 if (val.toString().toLowerCase() in {'0':1, 'no':1, 'false':1}) {
244 val = false;
245 } else {
246 val = true;
247 }
248 }
249 return val;
250 },
251
252 // Update cookie and form control setting. If value is not set, then
253 // updates from control to current cookie setting.
254 updateSetting: function(name, value) {
255
256 // Save the cookie for this session
257 if (typeof value !== 'undefined') {
258 WebUtil.writeSetting(name, value);
259 }
260
261 // Update the settings control
262 value = UI.getSetting(name);
263
264 var ctrl = $D('noVNC_' + name);
265 if (ctrl.type === 'checkbox') {
266 ctrl.checked = value;
267
268 } else if (typeof ctrl.options !== 'undefined') {
269 for (var i = 0; i < ctrl.options.length; i += 1) {
270 if (ctrl.options[i].value === value) {
271 ctrl.selectedIndex = i;
272 break;
273 }
274 }
275 } else {
276 /*Weird IE9 error leads to 'null' appearring
277 in textboxes instead of ''.*/
278 if (value === null) {
279 value = "";
280 }
281 ctrl.value = value;
282 }
283 },
284
285 // Save control setting to cookie
286 saveSetting: function(name) {
287 var val, ctrl = $D('noVNC_' + name);
288 if (ctrl.type === 'checkbox') {
289 val = ctrl.checked;
290 } else if (typeof ctrl.options !== 'undefined') {
291 val = ctrl.options[ctrl.selectedIndex].value;
292 } else {
293 val = ctrl.value;
294 }
295 WebUtil.writeSetting(name, val);
296 //Util.Debug("Setting saved '" + name + "=" + val + "'");
297 return val;
298 },
299
300 // Initial page load read/initialization of settings
301 initSetting: function(name, defVal) {
302 // Check Query string followed by cookie
303 var val = WebUtil.getQueryVar(name);
304 if (val === null) {
305 val = WebUtil.readSetting(name, defVal);
306 }
307 UI.updateSetting(name, val);
308 return val;
309 },
310
311 // Force a setting to be a certain value
312 forceSetting: function(name, val) {
313 UI.updateSetting(name, val);
314 return val;
315 },
316
317
318 // Show the popup status panel
319 togglePopupStatusPanel: function() {
320 var psp = $D('noVNC_popup_status_panel');
321 if (UI.popupStatusOpen === true) {
322 psp.style.display = "none";
323 UI.popupStatusOpen = false;
324 } else {
325 psp.innerHTML = $D('noVNC_status').innerHTML;
326 psp.style.display = "block";
327 psp.style.left = window.innerWidth/2 -
328 parseInt(window.getComputedStyle(psp, false).width)/2 -30 + "px";
329 UI.popupStatusOpen = true;
330 }
331 },
332
333 // Show the XVP panel
334 toggleXvpPanel: function() {
335 // Close the description panel
336 $D('noVNC_description').style.display = "none";
337 // Close settings if open
338 if (UI.settingsOpen === true) {
339 UI.settingsApply();
340 UI.closeSettingsMenu();
341 }
342 // Close connection settings if open
343 if (UI.connSettingsOpen === true) {
344 UI.toggleConnectPanel();
345 }
346 // Close popup status panel if open
347 if (UI.popupStatusOpen === true) {
348 UI.togglePopupStatusPanel();
349 }
350 // Close clipboard panel if open
351 if (UI.clipboardOpen === true) {
352 UI.toggleClipboardPanel();
353 }
354 // Toggle XVP panel
355 if (UI.xvpOpen === true) {
356 $D('noVNC_xvp').style.display = "none";
357 $D('xvpButton').className = "noVNC_status_button";
358 UI.xvpOpen = false;
359 } else {
360 $D('noVNC_xvp').style.display = "block";
361 $D('xvpButton').className = "noVNC_status_button_selected";
362 UI.xvpOpen = true;
363 }
364 },
365
366 // Show the clipboard panel
367 toggleClipboardPanel: function() {
368 // Close the description panel
369 $D('noVNC_description').style.display = "none";
370 // Close settings if open
371 if (UI.settingsOpen === true) {
372 UI.settingsApply();
373 UI.closeSettingsMenu();
374 }
375 // Close connection settings if open
376 if (UI.connSettingsOpen === true) {
377 UI.toggleConnectPanel();
378 }
379 // Close popup status panel if open
380 if (UI.popupStatusOpen === true) {
381 UI.togglePopupStatusPanel();
382 }
383 // Close XVP panel if open
384 if (UI.xvpOpen === true) {
385 UI.toggleXvpPanel();
386 }
387 // Toggle Clipboard Panel
388 if (UI.clipboardOpen === true) {
389 $D('noVNC_clipboard').style.display = "none";
390 $D('clipboardButton').className = "noVNC_status_button";
391 UI.clipboardOpen = false;
392 } else {
393 $D('noVNC_clipboard').style.display = "block";
394 $D('clipboardButton').className = "noVNC_status_button_selected";
395 UI.clipboardOpen = true;
396 }
397 },
398
399 // Show the connection settings panel/menu
400 toggleConnectPanel: function() {
401 // Close the description panel
402 $D('noVNC_description').style.display = "none";
403 // Close connection settings if open
404 if (UI.settingsOpen === true) {
405 UI.settingsApply();
406 UI.closeSettingsMenu();
407 $D('connectButton').className = "noVNC_status_button";
408 }
409 // Close clipboard panel if open
410 if (UI.clipboardOpen === true) {
411 UI.toggleClipboardPanel();
412 }
413 // Close popup status panel if open
414 if (UI.popupStatusOpen === true) {
415 UI.togglePopupStatusPanel();
416 }
417 // Close XVP panel if open
418 if (UI.xvpOpen === true) {
419 UI.toggleXvpPanel();
420 }
421
422 // Toggle Connection Panel
423 if (UI.connSettingsOpen === true) {
424 $D('noVNC_controls').style.display = "none";
425 $D('connectButton').className = "noVNC_status_button";
426 UI.connSettingsOpen = false;
427 UI.saveSetting('host');
428 UI.saveSetting('port');
429 //UI.saveSetting('password');
430 } else {
431 $D('noVNC_controls').style.display = "block";
432 $D('connectButton').className = "noVNC_status_button_selected";
433 UI.connSettingsOpen = true;
434 $D('noVNC_host').focus();
435 }
436 },
437
438 // Toggle the settings menu:
439 // On open, settings are refreshed from saved cookies.
440 // On close, settings are applied
441 toggleSettingsPanel: function() {
442 // Close the description panel
443 $D('noVNC_description').style.display = "none";
444 if (UI.settingsOpen) {
445 UI.settingsApply();
446 UI.closeSettingsMenu();
447 } else {
448 UI.updateSetting('encrypt');
449 UI.updateSetting('true_color');
450 if (UI.rfb.get_display().get_cursor_uri()) {
451 UI.updateSetting('cursor');
452 } else {
453 UI.updateSetting('cursor', !UI.isTouchDevice);
454 $D('noVNC_cursor').disabled = true;
455 }
456 UI.updateSetting('clip');
f8b399d7 457 UI.updateSetting('resize');
bbbf42bb
SR
458 UI.updateSetting('shared');
459 UI.updateSetting('view_only');
460 UI.updateSetting('path');
461 UI.updateSetting('repeaterID');
462 UI.updateSetting('stylesheet');
463 UI.updateSetting('logging');
464
465 UI.openSettingsMenu();
466 }
467 },
468
469 // Open menu
470 openSettingsMenu: function() {
471 // Close the description panel
472 $D('noVNC_description').style.display = "none";
473 // Close clipboard panel if open
474 if (UI.clipboardOpen === true) {
475 UI.toggleClipboardPanel();
476 }
477 // Close connection settings if open
478 if (UI.connSettingsOpen === true) {
479 UI.toggleConnectPanel();
480 }
481 // Close popup status panel if open
482 if (UI.popupStatusOpen === true) {
483 UI.togglePopupStatusPanel();
484 }
485 // Close XVP panel if open
486 if (UI.xvpOpen === true) {
487 UI.toggleXvpPanel();
488 }
489 $D('noVNC_settings').style.display = "block";
490 $D('settingsButton').className = "noVNC_status_button_selected";
491 UI.settingsOpen = true;
492 },
493
494 // Close menu (without applying settings)
495 closeSettingsMenu: function() {
496 $D('noVNC_settings').style.display = "none";
497 $D('settingsButton').className = "noVNC_status_button";
498 UI.settingsOpen = false;
499 },
500
501 // Save/apply settings when 'Apply' button is pressed
502 settingsApply: function() {
503 //Util.Debug(">> settingsApply");
504 UI.saveSetting('encrypt');
505 UI.saveSetting('true_color');
506 if (UI.rfb.get_display().get_cursor_uri()) {
507 UI.saveSetting('cursor');
508 }
509 UI.saveSetting('clip');
f8b399d7 510 UI.saveSetting('resize');
bbbf42bb
SR
511 UI.saveSetting('shared');
512 UI.saveSetting('view_only');
513 UI.saveSetting('path');
514 UI.saveSetting('repeaterID');
515 UI.saveSetting('stylesheet');
516 UI.saveSetting('logging');
517
518 // Settings with immediate (non-connected related) effect
519 WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
520 WebUtil.init_logging(UI.getSetting('logging'));
521 UI.setViewClip();
522 UI.setViewDrag(UI.rfb.get_viewportDrag());
523 //Util.Debug("<< settingsApply");
524 },
525
526
527
528 setPassword: function() {
529 UI.rfb.sendPassword($D('noVNC_password').value);
530 //Reset connect button.
531 $D('noVNC_connect_button').value = "Connect";
532 $D('noVNC_connect_button').onclick = UI.Connect;
533 //Hide connection panel.
f8ddfc73 534 UI.toggleConnectPanel();
bbbf42bb
SR
535 return false;
536 },
da6dd893 537
bbbf42bb
SR
538 sendCtrlAltDel: function() {
539 UI.rfb.sendCtrlAltDel();
540 },
53fc7392 541
bbbf42bb
SR
542 xvpShutdown: function() {
543 UI.rfb.xvpShutdown();
544 },
01a9eee9 545
bbbf42bb
SR
546 xvpReboot: function() {
547 UI.rfb.xvpReboot();
548 },
53fc7392 549
bbbf42bb
SR
550 xvpReset: function() {
551 UI.rfb.xvpReset();
552 },
01a9eee9 553
bbbf42bb
SR
554 setMouseButton: function(num) {
555 if (typeof num === 'undefined') {
556 // Disable mouse buttons
557 num = -1;
da6dd893 558 }
bbbf42bb
SR
559 if (UI.rfb) {
560 UI.rfb.get_mouse().set_touchButton(num);
561 }
562
563 var blist = [0, 1,2,4];
564 for (var b = 0; b < blist.length; b++) {
565 var button = $D('noVNC_mouse_button' + blist[b]);
566 if (blist[b] === num) {
567 button.style.display = "";
568 } else {
569 button.style.display = "none";
570 }
571 }
572 },
573
574 updateState: function(rfb, state, oldstate, msg) {
575 UI.rfb_state = state;
576 var klass;
577 switch (state) {
578 case 'failed':
579 case 'fatal':
580 klass = "noVNC_status_error";
581 break;
582 case 'normal':
583 klass = "noVNC_status_normal";
584 break;
585 case 'disconnected':
586 $D('noVNC_logo').style.display = "block";
587 /* falls through */
588 case 'loaded':
589 klass = "noVNC_status_normal";
590 break;
591 case 'password':
592 UI.toggleConnectPanel();
593
594 $D('noVNC_connect_button').value = "Send Password";
595 $D('noVNC_connect_button').onclick = UI.setPassword;
596 $D('noVNC_password').focus();
597
598 klass = "noVNC_status_warn";
599 break;
600 default:
601 klass = "noVNC_status_warn";
602 break;
603 }
604
605 if (typeof(msg) !== 'undefined') {
606 $D('noVNC-control-bar').setAttribute("class", klass);
607 $D('noVNC_status').innerHTML = msg;
608 }
609
610 UI.updateVisualState();
611 },
612
613 // Disable/enable controls depending on connection state
614 updateVisualState: function() {
615 var connected = UI.rfb_state === 'normal' ? true : false;
616
617 //Util.Debug(">> updateVisualState");
618 $D('noVNC_encrypt').disabled = connected;
619 $D('noVNC_true_color').disabled = connected;
620 if (UI.rfb && UI.rfb.get_display() &&
621 UI.rfb.get_display().get_cursor_uri()) {
622 $D('noVNC_cursor').disabled = connected;
623 } else {
624 UI.updateSetting('cursor', !UI.isTouchDevice);
625 $D('noVNC_cursor').disabled = true;
626 }
f8b399d7 627 $D('noVNC_clip').disabled = connected || UI.isTouchDevice;
628 $D('noVNC_resize').disabled = connected;
bbbf42bb
SR
629 $D('noVNC_shared').disabled = connected;
630 $D('noVNC_view_only').disabled = connected;
631 $D('noVNC_path').disabled = connected;
632 $D('noVNC_repeaterID').disabled = connected;
633
634 if (connected) {
635 UI.setViewClip();
636 UI.setMouseButton(1);
637 $D('clipboardButton').style.display = "inline";
638 $D('showKeyboard').style.display = "inline";
639 $D('noVNC_extra_keys').style.display = "";
640 $D('sendCtrlAltDelButton').style.display = "inline";
641 } else {
642 UI.setMouseButton();
643 $D('clipboardButton').style.display = "none";
644 $D('showKeyboard').style.display = "none";
645 $D('noVNC_extra_keys').style.display = "none";
646 $D('sendCtrlAltDelButton').style.display = "none";
647 UI.updateXvpVisualState(0);
648 }
649
650 // State change disables viewport dragging.
651 // It is enabled (toggled) by direct click on the button
652 UI.setViewDrag(false);
653
654 switch (UI.rfb_state) {
655 case 'fatal':
656 case 'failed':
657 case 'loaded':
658 case 'disconnected':
659 $D('connectButton').style.display = "";
660 $D('disconnectButton').style.display = "none";
661 break;
662 default:
663 $D('connectButton').style.display = "none";
664 $D('disconnectButton').style.display = "";
665 break;
666 }
667
668 //Util.Debug("<< updateVisualState");
669 },
670
671 // Disable/enable XVP button
672 updateXvpVisualState: function(ver) {
673 if (ver >= 1) {
674 $D('xvpButton').style.display = 'inline';
675 } else {
676 $D('xvpButton').style.display = 'none';
677 // Close XVP panel if open
678 if (UI.xvpOpen === true) {
679 UI.toggleXvpPanel();
680 }
681 }
682 },
683
f8b399d7 684 // This resize can not be done until we know from the first Frame Buffer Update
685 // if it is supported or not.
686 // The resize is needed to make sure the server desktop size is updated to the
687 // corresponding size of the current local window when reconnecting to an
688 // existing session.
689 FBUComplete: function(rfb, fbu) {
690 UI.onresize();
691 UI.rfb.set_onFBUComplete(function() { });
692 },
693
bbbf42bb
SR
694 // Display the desktop name in the document title
695 updateDocumentTitle: function(rfb, name) {
696 document.title = name + " - noVNC";
697 },
698
699 clipReceive: function(rfb, text) {
700 Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "...");
701 $D('noVNC_clipboard_text').value = text;
702 Util.Debug("<< UI.clipReceive");
703 },
704
705 connect: function() {
706 UI.closeSettingsMenu();
c506a481 707 UI.toggleConnectPanel();
f00b1e37 708
bbbf42bb
SR
709 var host = $D('noVNC_host').value;
710 var port = $D('noVNC_port').value;
711 var password = $D('noVNC_password').value;
712 var path = $D('noVNC_path').value;
713 if ((!host) || (!port)) {
714 throw new Error("Must set host and port");
715 }
53fc7392 716
bbbf42bb
SR
717 UI.rfb.set_encrypt(UI.getSetting('encrypt'));
718 UI.rfb.set_true_color(UI.getSetting('true_color'));
719 UI.rfb.set_local_cursor(UI.getSetting('cursor'));
720 UI.rfb.set_shared(UI.getSetting('shared'));
721 UI.rfb.set_view_only(UI.getSetting('view_only'));
722 UI.rfb.set_repeaterID(UI.getSetting('repeaterID'));
53fc7392 723
bbbf42bb 724 UI.rfb.connect(host, port, password, path);
8e0f0088 725
bbbf42bb
SR
726 //Close dialog.
727 setTimeout(UI.setBarPosition, 100);
728 $D('noVNC_logo').style.display = "none";
729 },
5299db1a 730
bbbf42bb
SR
731 disconnect: function() {
732 UI.closeSettingsMenu();
733 UI.rfb.disconnect();
8e0f0088 734
f8b399d7 735 // Restore the callback used for initial resize
736 UI.rfb.set_onFBUComplete(UI.FBUComplete);
737
bbbf42bb
SR
738 $D('noVNC_logo').style.display = "block";
739 UI.connSettingsOpen = false;
740 UI.toggleConnectPanel();
741 },
742
743 displayBlur: function() {
744 UI.rfb.get_keyboard().set_focused(false);
745 UI.rfb.get_mouse().set_focused(false);
746 },
747
748 displayFocus: function() {
749 UI.rfb.get_keyboard().set_focused(true);
750 UI.rfb.get_mouse().set_focused(true);
751 },
752
753 clipClear: function() {
754 $D('noVNC_clipboard_text').value = "";
755 UI.rfb.clipboardPasteFrom("");
756 },
757
758 clipSend: function() {
759 var text = $D('noVNC_clipboard_text').value;
760 Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "...");
761 UI.rfb.clipboardPasteFrom(text);
762 Util.Debug("<< UI.clipSend");
763 },
764
765 // Enable/disable and configure viewport clipping
766 setViewClip: function(clip) {
767 var display;
768 if (UI.rfb) {
769 display = UI.rfb.get_display();
770 } else {
771 return;
772 }
8e0f0088 773
bbbf42bb
SR
774 var cur_clip = display.get_viewport();
775
776 if (typeof(clip) !== 'boolean') {
777 // Use current setting
778 clip = UI.getSetting('clip');
779 }
8e0f0088 780
bbbf42bb
SR
781 if (clip && !cur_clip) {
782 // Turn clipping on
783 UI.updateSetting('clip', true);
784 } else if (!clip && cur_clip) {
785 // Turn clipping off
786 UI.updateSetting('clip', false);
787 display.set_viewport(false);
788 $D('noVNC_canvas').style.position = 'static';
f8b399d7 789 display.viewportChangeSize();
bbbf42bb
SR
790 }
791 if (UI.getSetting('clip')) {
792 // If clipping, update clipping settings
793 $D('noVNC_canvas').style.position = 'absolute';
794 var pos = Util.getPosition($D('noVNC_canvas'));
795 var new_w = window.innerWidth - pos.x;
796 var new_h = window.innerHeight - pos.y;
797 display.set_viewport(true);
f8b399d7 798 display.viewportChangeSize(new_w, new_h);
bbbf42bb
SR
799 }
800 },
801
802 // Toggle/set/unset the viewport drag/move button
803 setViewDrag: function(drag) {
bbbf42bb
SR
804 if (!UI.rfb) { return; }
805
f8b399d7 806 UI.updateViewDragButton();
8e0f0088 807
bbbf42bb
SR
808 if (typeof(drag) === "undefined" ||
809 typeof(drag) === "object") {
810 // If not specified, then toggle
811 drag = !UI.rfb.get_viewportDrag();
812 }
f8b399d7 813 var vmb = $D('noVNC_view_drag_button');
bbbf42bb
SR
814 if (drag) {
815 vmb.className = "noVNC_status_button_selected";
816 UI.rfb.set_viewportDrag(true);
817 } else {
818 vmb.className = "noVNC_status_button";
819 UI.rfb.set_viewportDrag(false);
820 }
821 },
822
f8b399d7 823 updateViewDragButton: function() {
824 var vmb = $D('noVNC_view_drag_button');
825 if (UI.rfb_state === 'normal' &&
826 UI.rfb.get_display().get_viewport() &&
827 UI.rfb.get_display().fbuClip()) {
828 vmb.style.display = "inline";
829 } else {
830 vmb.style.display = "none";
831 }
832 },
833
bbbf42bb
SR
834 // On touch devices, show the OS keyboard
835 showKeyboard: function() {
836 var kbi = $D('keyboardinput');
837 var skb = $D('showKeyboard');
838 var l = kbi.value.length;
839 if(UI.keyboardVisible === false) {
840 kbi.focus();
841 try { kbi.setSelectionRange(l, l); } // Move the caret to the end
842 catch (err) {} // setSelectionRange is undefined in Google Chrome
843 UI.keyboardVisible = true;
844 skb.className = "noVNC_status_button_selected";
845 } else if(UI.keyboardVisible === true) {
846 kbi.blur();
847 skb.className = "noVNC_status_button";
848 UI.keyboardVisible = false;
849 }
850 },
851
852 keepKeyboard: function() {
853 clearTimeout(UI.hideKeyboardTimeout);
854 if(UI.keyboardVisible === true) {
855 $D('keyboardinput').focus();
856 $D('showKeyboard').className = "noVNC_status_button_selected";
857 } else if(UI.keyboardVisible === false) {
858 $D('keyboardinput').blur();
859 $D('showKeyboard').className = "noVNC_status_button";
860 }
861 },
862
863 keyboardinputReset: function() {
864 var kbi = $D('keyboardinput');
865 kbi.value = new Array(UI.defaultKeyboardinputLen).join("_");
866 UI.lastKeyboardinput = kbi.value;
867 },
868
869 // When normal keyboard events are left uncought, use the input events from
870 // the keyboardinput element instead and generate the corresponding key events.
871 // This code is required since some browsers on Android are inconsistent in
872 // sending keyCodes in the normal keyboard events when using on screen keyboards.
873 keyInput: function(event) {
874 var newValue = event.target.value;
875 var oldValue = UI.lastKeyboardinput;
876
877 var newLen;
878 try {
879 // Try to check caret position since whitespace at the end
880 // will not be considered by value.length in some browsers
881 newLen = Math.max(event.target.selectionStart, newValue.length);
882 } catch (err) {
883 // selectionStart is undefined in Google Chrome
884 newLen = newValue.length;
885 }
886 var oldLen = oldValue.length;
887
888 var backspaces;
889 var inputs = newLen - oldLen;
890 if (inputs < 0) {
891 backspaces = -inputs;
892 } else {
893 backspaces = 0;
894 }
8e0f0088 895
bbbf42bb
SR
896 // Compare the old string with the new to account for
897 // text-corrections or other input that modify existing text
898 var i;
899 for (i = 0; i < Math.min(oldLen, newLen); i++) {
900 if (newValue.charAt(i) != oldValue.charAt(i)) {
901 inputs = newLen - i;
902 backspaces = oldLen - i;
903 break;
904 }
905 }
906
907 // Send the key events
908 for (i = 0; i < backspaces; i++) {
909 UI.rfb.sendKey(XK_BackSpace);
910 }
911 for (i = newLen - inputs; i < newLen; i++) {
912 UI.rfb.sendKey(newValue.charCodeAt(i));
913 }
914
915 // Control the text content length in the keyboardinput element
916 if (newLen > 2 * UI.defaultKeyboardinputLen) {
917 UI.keyboardinputReset();
918 } else if (newLen < 1) {
919 // There always have to be some text in the keyboardinput
920 // element with which backspace can interact.
921 UI.keyboardinputReset();
922 // This sometimes causes the keyboard to disappear for a second
923 // but it is required for the android keyboard to recognize that
924 // text has been added to the field
925 event.target.blur();
926 // This has to be ran outside of the input handler in order to work
927 setTimeout(function() { UI.keepKeyboard(); }, 0);
928 } else {
929 UI.lastKeyboardinput = newValue;
930 }
931 },
932
933 keyInputBlur: function() {
934 $D('showKeyboard').className = "noVNC_status_button";
935 //Weird bug in iOS if you change keyboardVisible
936 //here it does not actually occur so next time
937 //you click keyboard icon it doesnt work.
938 UI.hideKeyboardTimeout = setTimeout(function() { UI.setKeyboard(); },100);
939 },
940
941 showExtraKeys: function() {
942 UI.keepKeyboard();
943 if(UI.extraKeysVisible === false) {
944 $D('toggleCtrlButton').style.display = "inline";
945 $D('toggleAltButton').style.display = "inline";
946 $D('sendTabButton').style.display = "inline";
947 $D('sendEscButton').style.display = "inline";
948 $D('showExtraKeysButton').className = "noVNC_status_button_selected";
949 UI.extraKeysVisible = true;
950 } else if(UI.extraKeysVisible === true) {
951 $D('toggleCtrlButton').style.display = "";
952 $D('toggleAltButton').style.display = "";
953 $D('sendTabButton').style.display = "";
954 $D('sendEscButton').style.display = "";
955 $D('showExtraKeysButton').className = "noVNC_status_button";
956 UI.extraKeysVisible = false;
957 }
958 },
959
960 toggleCtrl: function() {
961 UI.keepKeyboard();
962 if(UI.ctrlOn === false) {
963 UI.rfb.sendKey(XK_Control_L, true);
964 $D('toggleCtrlButton').className = "noVNC_status_button_selected";
965 UI.ctrlOn = true;
966 } else if(UI.ctrlOn === true) {
967 UI.rfb.sendKey(XK_Control_L, false);
968 $D('toggleCtrlButton').className = "noVNC_status_button";
969 UI.ctrlOn = false;
970 }
971 },
972
973 toggleAlt: function() {
974 UI.keepKeyboard();
975 if(UI.altOn === false) {
976 UI.rfb.sendKey(XK_Alt_L, true);
977 $D('toggleAltButton').className = "noVNC_status_button_selected";
978 UI.altOn = true;
979 } else if(UI.altOn === true) {
980 UI.rfb.sendKey(XK_Alt_L, false);
981 $D('toggleAltButton').className = "noVNC_status_button";
982 UI.altOn = false;
983 }
984 },
985
986 sendTab: function() {
987 UI.keepKeyboard();
988 UI.rfb.sendKey(XK_Tab);
989 },
990
991 sendEsc: function() {
992 UI.keepKeyboard();
993 UI.rfb.sendKey(XK_Escape);
994 },
995
996 setKeyboard: function() {
997 UI.keyboardVisible = false;
998 },
999
1000 // iOS < Version 5 does not support position fixed. Javascript workaround:
1001 setOnscroll: function() {
1002 window.onscroll = function() {
1003 UI.setBarPosition();
1004 };
1005 },
1006
1007 setResize: function () {
1008 window.onResize = function() {
1009 UI.setBarPosition();
1010 };
1011 },
1012
1013 //Helper to add options to dropdown.
1014 addOption: function(selectbox, text, value) {
1015 var optn = document.createElement("OPTION");
1016 optn.text = text;
1017 optn.value = value;
1018 selectbox.options.add(optn);
1019 },
1020
1021 setBarPosition: function() {
1022 $D('noVNC-control-bar').style.top = (window.pageYOffset) + 'px';
1023 $D('noVNC_mobile_buttons').style.left = (window.pageXOffset) + 'px';
1024
1025 var vncwidth = $D('noVNC_screen').style.offsetWidth;
1026 $D('noVNC-control-bar').style.width = vncwidth + 'px';
1027 }
1028
1029 };
1030})();