]>
git.proxmox.com Git - novnc-pve.git/blob - pveui.js
2 * noVNC: HTML5 VNC client
3 * Copyright (C) 2012 Joel Martin
4 * Copyright (C) 2013 Samuel Mannehed for Cendio AB
5 * Licensed under MPL 2.0 (see LICENSE.txt)
7 * This is a modified version of the original 'ui.js' file, used
8 * for the proxmox console.
12 /*jslint white: false, browser: true */
13 /*global window, $D, Util, WebUtil, RFB, Display */
15 // Load supporting scripts
16 window
.onscriptsload = function () { UI
.load(); };
17 window
.onload = function () { UI
.keyboardinputReset(); };
18 Util
.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
19 "keysymdef.js", "keyboard.js", "input.js", "display.js",
20 "jsunzip.js", "rfb.js", "keysym.js"]);
25 pveCommandsOpen
: false,
27 connSettingsOpen
: false,
28 popupStatusOpen
: false,
30 sendKeysVisible
: false,
31 keyboardVisible
: false,
32 hideKeyboardTimeout
: null,
33 lastKeyboardinput
: null,
34 defaultKeyboardinputLen
: 100,
35 extraKeysVisible
: false,
40 consoletype
: undefined,
45 // Setup rfb object, load settings from browser storage, then call
46 // UI.init to setup the UI/menus
47 load: function (callback
) {
48 WebUtil
.initSettings(UI
.pve_start
, callback
);
51 // Proxmox VE related code
53 urlEncode: function(object
) {
54 var i
,value
, params
= [];
57 if (object
.hasOwnProperty(i
)) {
59 if (value
=== undefined) value
= '';
60 params
.push(encodeURIComponent(i
) + '=' + encodeURIComponent(String(value
)));
64 return params
.join('&');
67 API2Request: function(reqOpts
) {
69 reqOpts
.method
= reqOpts
.method
|| 'GET';
71 var xhr
= new XMLHttpRequest();
73 xhr
.onload = function() {
74 var scope
= reqOpts
.scope
|| this;
78 if (xhr
.readyState
=== 4) {
79 var ctype
= xhr
.getResponseHeader('Content-Type');
80 if (xhr
.status
=== 200) {
81 if (ctype
.match(/application\/json;/)) {
82 result
= JSON
.parse(xhr
.responseText
);
84 errmsg
= 'got unexpected content type ' + ctype
;
87 errmsg
= 'Error ' + xhr
.status
+ ': ' + xhr
.statusText
;
90 errmsg
= 'Connection error - server offline?';
93 if (errmsg
!== undefined) {
94 if (reqOpts
.failure
) {
95 reqOpts
.failure
.call(scope
, errmsg
);
98 if (reqOpts
.success
) {
99 reqOpts
.success
.call(scope
, result
);
102 if (reqOpts
.callback
) {
103 reqOpts
.callback
.call(scope
, errmsg
=== undefined);
107 var data
= UI
.urlEncode(reqOpts
.params
|| {});
109 if (reqOpts
.method
=== 'GET') {
110 xhr
.open(reqOpts
.method
, "/api2/json" + reqOpts
.url
+ '?' + data
);
112 xhr
.open(reqOpts
.method
, "/api2/json" + reqOpts
.url
);
114 xhr
.setRequestHeader('Cache-Control', 'no-cache');
115 if (reqOpts
.method
=== 'POST' || reqOpts
.method
=== 'PUT') {
116 xhr
.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
117 xhr
.setRequestHeader('CSRFPreventionToken', PVE
.CSRFPreventionToken
);
119 } else if (reqOpts
.method
=== 'GET') {
122 throw "unknown method";
128 // show msg for 5 seconds
129 pve_show_msg: function(klass
, msg
, permanant
) {
130 var oldklass
= $D('noVNC-control-bar').getAttribute("class");
131 $D('noVNC-control-bar').setAttribute("class", klass
);
132 var oldmsg
= $D('noVNC_status').innerHTML
;
133 $D('noVNC_status').innerHTML
= msg
;
134 if (typeof permanent
!== 'undefined' && permanent
) return;
136 setTimeout(function() {
137 var curmsg
= $D('noVNC_status').innerHTML
;
138 if (curmsg
=== msg
) {
139 $D('noVNC_status').innerHTML
= oldmsg
;
141 var curklass
= $D('noVNC-control-bar').getAttribute("class");
142 if (curklass
=== klass
) {
143 $D('noVNC-control-bar').setAttribute("class", oldklass
);
148 pve_vm_command: function(cmd
, params
, reload
) {
151 if (UI
.consoletype
=== 'kvm') {
152 baseUrl
= '/nodes/' + UI
.nodename
+ '/qemu/' + UI
.vmid
;
153 } else if (UI
.consoletype
=== 'openvz') {
154 baseUrl
= '/nodes/' + UI
.nodename
+ '/openvz/' + UI
.vmid
;
156 throw "unknown VM type";
161 url
: baseUrl
+ "/status/" + cmd
,
163 failure: function(msg
) {
164 UI
.pve_show_msg('noVNC_status_warn', msg
);
166 success: function() {
167 UI
.pve_show_msg('noVNC_status_normall', "VM command '" + cmd
+"' successful");
169 setTimeout(function() {
177 pveCmdStart: function() {
178 if (UI
.pveCommandsOpen
=== true) {
179 UI
.togglePVECommandPanel();
181 UI
.pve_vm_command('start', {}, true);
184 pveCmdShutdown: function() {
185 if (UI
.pveCommandsOpen
=== true) {
186 UI
.togglePVECommandPanel();
188 var msg
= gettext("Do you really want to shutdown VM {0}?");
189 msg
= msg
.replace(/\{0\}/, UI
.vmid
);
191 if (confirm(msg
) === true) {
192 UI
.pve_vm_command('shutdown');
196 pveCmdStop: function() {
197 if (UI
.pveCommandsOpen
=== true) {
198 UI
.togglePVECommandPanel();
201 var msg
= gettext("Do you really want to stop VM {0}?");
202 msg
= msg
.replace(/\{0\}/, UI
.vmid
);
204 if (confirm(msg
) === true) {
205 UI
.pve_vm_command('stop');
209 pveCmdReset: function() {
210 if (UI
.pveCommandsOpen
=== true) {
211 UI
.togglePVECommandPanel();
213 var msg
= gettext("Do you really want to reset VM {0}?");
214 msg
= msg
.replace(/\{0\}/, UI
.vmid
);
216 if (confirm(msg
) === true) {
217 UI
.pve_vm_command('reset');
221 pveCmdSuspend: function() {
222 if (UI
.pveCommandsOpen
=== true) {
223 UI
.togglePVECommandPanel();
225 var msg
= gettext("Do you really want to suspend VM {0}?");
226 msg
= msg
.replace(/\{0\}/, UI
.vmid
);
228 if (confirm(msg
) === true) {
229 UI
.pve_vm_command('suspend');
233 pveCmdResume: function() {
234 if (UI
.pveCommandsOpen
=== true) {
235 UI
.togglePVECommandPanel();
237 UI
.pve_vm_command('resume');
240 pveCmdReload: function() {
241 if (UI
.pveCommandsOpen
=== true) {
242 UI
.togglePVECommandPanel();
247 pveReload: function() {
251 pve_send_key: function(keyname
) {
254 if (UI
.consoletype
=== 'kvm') {
255 baseUrl
= '/nodes/' + UI
.nodename
+ '/qemu/' + UI
.vmid
;
257 throw "send key not implemented";
261 params
: { key
: keyname
},
262 url
: baseUrl
+ '/sendkey',
264 failure: function(msg
) {
265 UI
.pve_show_msg('noVNC_status_warn', msg
);
270 pve_start: function(callback
) {
271 UI
.consoletype
= WebUtil
.getQueryVar('console');
272 UI
.vmid
= WebUtil
.getQueryVar('vmid');
273 UI
.vmname
= WebUtil
.getQueryVar('vmname');
274 UI
.nodename
= WebUtil
.getQueryVar('node');
278 var params
= { websocket
: 1 };
281 // add pve command buttons
282 var cmdpanel
= $D('noVNC_pve_command_menu');
285 text
: gettext('Start'),
286 handler
: UI
.pveCmdStart
,
287 enable
: { kvm
: 1, openvz
: 1 }
290 text
: gettext('Shutdown'),
291 handler
: UI
.pveCmdShutdown
,
292 enable
: { kvm
: 1, openvz
: 1 }
295 text
: gettext('Stop'),
296 handler
: UI
.pveCmdStop
,
297 enable
: { kvm
: 1, openvz
: 1 }
300 text
: gettext('Reset'),
301 handler
: UI
.pveCmdReset
,
305 text
: gettext('Suspend'),
306 handler
: UI
.pveCmdSuspend
,
310 text
: gettext('Resume'),
311 handler
: UI
.pveCmdResume
,
315 text
: gettext('Reload'),
316 handler
: UI
.pveCmdReload
,
320 buttonlist
.forEach(function(btn
) {
321 if (btn
.enable
.any
|| btn
.enable
[UI
.consoletype
]) {
322 var el
= document
.createElement('input');
323 el
.setAttribute('type', 'button');
324 el
.setAttribute('value', btn
.text
);
325 el
.onclick
= btn
.handler
;
326 el
.style
.display
= "block";
327 el
.style
.width
= "100%";
328 el
.style
.minWidth
= "150px";
329 cmdpanel
.appendChild(el
);
330 console
.log("ADD: " + btn
.text
);
334 // add sendKeys buttons
335 var skpanel
= $D('noVNC_send_keys_panel');
339 text
: 'Tab', handler: function() {
340 UI
.pve_send_key('tab');
344 text
: 'Ctrl-Alt-Delete', handler: function() {
345 UI
.pve_send_key('ctrl-alt-delete');
349 text
: 'Ctrl-Alt-Backspace', handler: function() {
350 UI
.pve_send_key('ctrl-alt-backspace');
354 text
: 'Ctrl-Alt-F1', handler: function() {
355 UI
.pve_send_key('ctrl-alt-f1');
359 text
: 'Ctrl-Alt-F2', handler: function() {
360 UI
.pve_send_key('ctrl-alt-f2');
364 text
: 'Ctrl-Alt-F3', handler: function() {
365 UI
.pve_send_key('ctrl-alt-f3');
369 text
: 'Ctrl-Alt-F4', handler: function() {
370 UI
.pve_send_key('ctrl-alt-f4');
374 text
: 'Ctrl-Alt-F5', handler: function() {
375 UI
.pve_send_key('ctrl-alt-f5');
379 text
: 'Ctrl-Alt-F6', handler: function() {
380 UI
.pve_send_key('ctrl-alt-f6');
384 text
: 'Ctrl-Alt-F7', handler: function() {
385 UI
.pve_send_key('ctrl-alt-f7');
389 text
: 'Ctrl-Alt-F8', handler: function() {
390 UI
.pve_send_key('ctrl-alt-f8');
394 text
: 'Ctrl-Alt-F9', handler: function() {
395 UI
.pve_send_key('ctrl-alt-f9');
399 text
: 'Ctrl-Alt-F10', handler: function() {
400 UI
.pve_send_key('ctrl-alt-f10');
404 text
: 'Ctrl-Alt-F11', handler: function() {
405 UI
.pve_send_key('ctrl-alt-f11');
409 text
: 'Ctrl-Alt-F12', handler: function() {
410 UI
.pve_send_key('ctrl-alt-f12');
415 buttonlist
.forEach(function(btn
) {
416 var el
= document
.createElement('input');
417 el
.setAttribute('type', 'button');
418 el
.setAttribute('value', btn
.text
);
419 el
.onclick = function(handler
) {
421 if (UI
.sendKeysVisible
=== true) {
422 UI
.togglePVESendKeysPanel();
428 el
.style
.display
= "block";
429 el
.style
.width
= "100%";
430 el
.style
.minWidth
= "150px";
431 skpanel
.appendChild(el
);
436 if (UI
.consoletype
=== 'kvm') {
437 var baseUrl
= '/nodes/' + UI
.nodename
+ '/qemu/' + UI
.vmid
;
438 url
= baseUrl
+ '/vncproxy';
439 wsurl
= baseUrl
+ '/vncwebsocket';
440 title
= "VM " + UI
.vmid
;
442 title
+= " ('" + UI
.vmname
+ "')";
444 } else if (UI
.consoletype
=== 'openvz') {
445 var baseUrl
= '/nodes/' + UI
.nodename
+ '/openvz/' + UI
.vmid
;
446 url
= baseUrl
+ '/vncproxy';
447 wsurl
= baseUrl
+ '/vncwebsocket';
448 title
= "CT " + UI
.vmid
;
450 title
+= " ('" + UI
.vmname
+ "')";
452 } else if (UI
.consoletype
=== 'shell') {
453 var baseUrl
= '/nodes/' + UI
.nodename
;
454 url
= baseUrl
+ '/vncshell';
455 wsurl
= baseUrl
+ '/vncwebsocket';
456 title
= "node '" + UI
.nodename
+ "'";
457 } else if (UI
.consoletype
=== 'upgrade') {
458 var baseUrl
= '/nodes/' + UI
.nodename
;
459 url
= baseUrl
+ '/vncshell';
460 wsurl
= baseUrl
+ '/vncwebsocket';
462 title
= gettext('System upgrade on node {0}');
463 title
= title
.replace(/\{0\}/, UI
.nodename
);
465 throw "implement me";
468 document
.title
= title
;
470 var start_vnc_viewer = function(param
) {
471 var wsparams
= UI
.urlEncode({
473 vncticket
: param
.ticket
476 UI
.updateSetting('host', window
.location
.hostname
);
477 UI
.updateSetting('port', window
.location
.port
);
478 UI
.updateSetting('password', param
.ticket
);
479 UI
.updateSetting('encrypt', true);
480 UI
.updateSetting('true_color', true);
481 UI
.updateSetting('cursor', !UI
.isTouchDevice
);
482 UI
.updateSetting('shared', true);
483 UI
.updateSetting('view_only', false);
485 UI
.updateSetting('path', 'api2/json' + wsurl
+ "?" + wsparams
);
494 success: function(result
) {
495 start_vnc_viewer(result
.data
);
497 failure: function(msg
) {
498 UI
.pve_show_msg('noVNC_status_error', msg
, 1);
503 lastFBWidth
: undefined,
504 lastFBHeight
: undefined,
505 sizeUpdateTimer
: undefined,
507 updateFBSize: function(rfb
, width
, height
) {
509 // Note1: CSS Canvas size is wrong by a few pixels in Chrome
510 // Note2: window size must be even number for firefox
511 UI
.lastFBWidth
= Math
.floor((width
+ 1)/2)*2;;
512 UI
.lastFBHeight
= Math
.floor((height
+ 6)/2)*2;
514 if (UI
.sizeUpdateTimer
!== undefined) {
515 clearInterval(UI
.sizeUpdateTimer
);
517 if (UI
.getSetting('clip')) return;
519 var update_size = function() {
523 if (window
.innerHeight
) {
524 oh
= window
.innerHeight
;
525 ow
= window
.innerWidth
;
526 } else if (document
.documentElement
&&
527 document
.documentElement
.clientHeight
) {
528 oh
= document
.documentElement
.clientHeight
;
529 ow
= document
.documentElement
.clientWidth
;
530 } else if (document
.body
) {
531 oh
= document
.body
.clientHeight
;
532 ow
= document
.body
.clientWidth
;
534 throw "can't get window size";
537 // see base.css/noVNC_screen_pad
538 var toolbar_height
= 36;
540 var offsetw
= UI
.lastFBWidth
- ow
;
541 var offseth
= UI
.lastFBHeight
+ toolbar_height
- oh
;
542 if (offsetw
!== 0 || offseth
!== 0) {
543 //console.log("try resize by " + offsetw + " " + offseth);
544 window
.resizeBy(offsetw
, offseth
);
549 UI
.sizeUpdateTimer
= setInterval(update_size
, 1000);
556 // Open/close PVE commandand menu
557 togglePVECommandPanel: function() {
558 // Close the description panel
559 $D('noVNC_description').style
.display
= "none";
560 if (UI
.sendKeysVisible
=== true) {
561 UI
.togglePVESendKeysPanel();
563 // Close clipboard panel if open
564 if (UI
.clipboardOpen
=== true) {
565 UI
.toggleClipboardPanel();
567 // Close connection settings if open
568 if (UI
.connSettingsOpen
=== true) {
569 UI
.toggleConnectPanel();
571 // Close popup status panel if open
572 if (UI
.popupStatusOpen
=== true) {
573 UI
.togglePopupStatusPanel();
575 // Close XVP panel if open
576 if (UI
.xvpOpen
=== true) {
579 if (UI
.pveCommandsOpen
) {
580 $D('noVNC_pve_commands').style
.display
= "none";
581 $D('pveCommandsButton').className
= "noVNC_status_button";
582 UI
.pveCommandsOpen
= false;
584 $D('noVNC_pve_commands').style
.display
= "block";
585 $D('pveCommandsButton').className
= "noVNC_status_button_selected";
586 UI
.pveCommandsOpen
= true;
590 // Open/close PVE SendKeys menu
591 togglePVESendKeysPanel: function() {
592 // Close the description panel
593 $D('noVNC_description').style
.display
= "none";
594 if (UI
.pveCommandsOpen
=== true) {
595 UI
.togglePVECommandPanel();
597 // Close clipboard panel if open
598 if (UI
.clipboardOpen
=== true) {
599 UI
.toggleClipboardPanel();
601 // Close connection settings if open
602 if (UI
.connSettingsOpen
=== true) {
603 UI
.toggleConnectPanel();
605 // Close popup status panel if open
606 if (UI
.popupStatusOpen
=== true) {
607 UI
.togglePopupStatusPanel();
609 // Close XVP panel if open
610 if (UI
.xvpOpen
=== true) {
613 if (UI
.sendKeysVisible
) {
614 $D('noVNC_send_keys').style
.display
= "none";
615 $D('showSendKeysButton').className
= "noVNC_status_button";
616 UI
.sendKeysVisible
= false;
618 $D('noVNC_send_keys').style
.display
= "block";
619 $D('showSendKeysButton').className
= "noVNC_status_button_selected";
620 UI
.sendKeysVisible
= true;
624 // Render default UI and initialize settings menu
625 start: function(callback
) {
626 var html
= '', i
, sheet
, sheets
, llevels
, port
, autoconnect
;
628 UI
.isTouchDevice
= 'ontouchstart' in document
.documentElement
;
630 // Stylesheet selection dropdown
631 sheet
= WebUtil
.selectStylesheet();
632 sheets
= WebUtil
.getStylesheets();
633 for (i
= 0; i
< sheets
.length
; i
+= 1) {
634 //UI.addOption($D('noVNC_stylesheet'),sheets[i].title, sheets[i].title);
637 // Logging selection dropdown
638 llevels
= ['error', 'warn', 'info', 'debug'];
639 for (i
= 0; i
< llevels
.length
; i
+= 1) {
640 //UI.addOption($D('noVNC_logging'),llevels[i], llevels[i]);
643 // Settings with immediate effects
644 UI
.initSetting('logging', 'warn');
645 WebUtil
.init_logging(UI
.getSetting('logging'));
647 UI
.initSetting('stylesheet', 'default');
648 WebUtil
.selectStylesheet(null);
649 // call twice to get around webkit bug
650 WebUtil
.selectStylesheet(UI
.getSetting('stylesheet'));
652 UI
.initSetting('repeaterID', '');
654 UI
.rfb
= RFB({'target': $D('noVNC_canvas'),
655 'onUpdateState': UI
.updateState
,
656 'onXvpInit': UI
.updateXvpVisualState
,
657 'onClipboard': UI
.clipReceive
,
658 //'onDesktopName': UI.updateDocumentTitle,
659 'onFBResize': UI
.updateFBSize
});
662 if (autoconnect
=== 'true' || autoconnect
== '1') {
669 UI
.updateVisualState();
671 // Unfocus clipboard when over the VNC area
672 //$D('VNC_screen').onmousemove = function () {
673 // var keyboard = UI.rfb.get_keyboard();
674 // if ((! keyboard) || (! keyboard.get_focused())) {
675 // $D('VNC_clipboard_text').blur();
679 // Show mouse selector buttons on touch screen devices
680 if (UI
.isTouchDevice
) {
681 // Show mobile buttons
682 $D('noVNC_mobile_buttons').style
.display
= "inline";
683 $D('showSendKeysButton').style
.display
= "none";
685 // Remove the address bar
686 setTimeout(function() { window
.scrollTo(0, 1); }, 100);
687 UI
.forceSetting('clip', true);
688 $D('noVNC_clip').disabled
= true;
690 $D('showSendKeysButton').style
.display
= (UI
.consoletype
=== 'kvm') ? "inline" : "none";
691 UI
.initSetting('clip', false);
694 //iOS Safari does not support CSS position:fixed.
695 //This detects iOS devices and enables javascript workaround.
696 if ((navigator
.userAgent
.match(/iPhone/i)) ||
697 (navigator
.userAgent
.match(/iPod/i)) ||
698 (navigator
.userAgent
.match(/iPad/i))) {
704 $D('noVNC_host').focus();
707 Util
.addEvent(window
, 'resize', UI
.setViewClip
);
709 Util
.addEvent(window
, 'beforeunload', function () {
710 if (UI
.rfb_state
=== 'normal') {
711 return "You are currently connected.";
715 // Show description by default when hosted at for kanaka.github.com
716 if (location
.host
=== "kanaka.github.io") {
717 // Open the description dialog
718 $D('noVNC_description').style
.display
= "block";
720 // Show the connect panel on first load unless autoconnecting
721 if (autoconnect
=== UI
.connSettingsOpen
) {
722 UI
.toggleConnectPanel();
726 // Add mouse event click/focus/blur event handlers to the UI
727 UI
.addMouseHandlers();
729 if (typeof callback
=== "function") {
734 addMouseHandlers: function() {
735 // Setup interface handlers that can't be inline
736 $D("noVNC_view_drag_button").onclick
= UI
.setViewDrag
;
737 $D("noVNC_mouse_button0").onclick = function () { UI
.setMouseButton(1); };
738 $D("noVNC_mouse_button1").onclick = function () { UI
.setMouseButton(2); };
739 $D("noVNC_mouse_button2").onclick = function () { UI
.setMouseButton(4); };
740 $D("noVNC_mouse_button4").onclick = function () { UI
.setMouseButton(0); };
741 $D("showKeyboard").onclick
= UI
.showKeyboard
;
743 $D("keyboardinput").oninput
= UI
.keyInput
;
744 $D("keyboardinput").onblur
= UI
.keyInputBlur
;
746 $D("showExtraKeysButton").onclick
= UI
.showExtraKeys
;
747 $D("toggleCtrlButton").onclick
= UI
.toggleCtrl
;
748 $D("toggleAltButton").onclick
= UI
.toggleAlt
;
749 $D("sendTabButton").onclick
= UI
.sendTab
;
750 $D("sendEscButton").onclick
= UI
.sendEsc
;
752 $D("showSendKeysButton").onclick
= UI
.togglePVESendKeysPanel
;
754 $D("sendCtrlAltDelButton").onclick
= UI
.sendCtrlAltDel
;
755 //$D("xvpShutdownButton").onclick = UI.xvpShutdown;
756 //$D("xvpRebootButton").onclick = UI.xvpReboot;
757 //$D("xvpResetButton").onclick = UI.xvpReset;
758 // disable popup, because it does not provide more info?
759 //$D("noVNC_status").onclick = UI.togglePopupStatusPanel;
760 $D("noVNC_popup_status_panel").onclick
= UI
.togglePopupStatusPanel
;
761 //$D("xvpButton").onclick = UI.toggleXvpPanel;
762 $D("clipboardButton").onclick
= UI
.toggleClipboardPanel
;
763 //$D("settingsButton").onclick = UI.toggleSettingsPanel;
764 $D("pveCommandsButton").onclick
= UI
.togglePVECommandPanel
;
765 //$D("connectButton").onclick = UI.toggleConnectPanel;
766 //$D("disconnectButton").onclick = UI.disconnect;
767 //$D("descriptionButton").onclick = UI.toggleConnectPanel;
769 $D("noVNC_clipboard_text").onfocus
= UI
.displayBlur
;
770 $D("noVNC_clipboard_text").onblur
= UI
.displayFocus
;
771 $D("noVNC_clipboard_text").onchange
= UI
.clipSend
;
772 $D("noVNC_clipboard_clear_button").onclick
= UI
.clipClear
;
774 //$D("noVNC_settings_menu").onmouseover = UI.displayBlur;
775 //$D("noVNC_settings_menu").onmouseover = UI.displayFocus;
776 //$D("noVNC_apply").onclick = UI.settingsApply;
778 //$D("noVNC_connect_button").onclick = UI.connect;
781 // Read form control compatible setting from cookie
782 getSetting: function(name
) {
783 var val
, ctrl
= $D('noVNC_' + name
);
784 val
= WebUtil
.readSetting(name
);
785 if (val
!== null && ctrl
.type
=== 'checkbox') {
786 if (val
.toString().toLowerCase() in {'0':1, 'no':1, 'false':1}) {
795 // Update cookie and form control setting. If value is not set, then
796 // updates from control to current cookie setting.
797 updateSetting: function(name
, value
) {
799 var i
, ctrl
= $D('noVNC_' + name
);
800 // Save the cookie for this session
801 if (typeof value
!== 'undefined') {
802 WebUtil
.writeSetting(name
, value
);
805 // Update the settings control
806 value
= UI
.getSetting(name
);
808 if (ctrl
.type
=== 'checkbox') {
809 ctrl
.checked
= value
;
811 } else if (typeof ctrl
.options
!== 'undefined') {
812 for (i
= 0; i
< ctrl
.options
.length
; i
+= 1) {
813 if (ctrl
.options
[i
].value
=== value
) {
814 ctrl
.selectedIndex
= i
;
819 /*Weird IE9 error leads to 'null' appearring
820 in textboxes instead of ''.*/
821 if (value
=== null) {
828 // Save control setting to cookie
829 saveSetting: function(name
) {
830 var val
, ctrl
= $D('noVNC_' + name
);
831 if (ctrl
.type
=== 'checkbox') {
833 } else if (typeof ctrl
.options
!== 'undefined') {
834 val
= ctrl
.options
[ctrl
.selectedIndex
].value
;
838 WebUtil
.writeSetting(name
, val
);
839 //Util.Debug("Setting saved '" + name + "=" + val + "'");
843 // Initial page load read/initialization of settings
844 initSetting: function(name
, defVal
) {
847 // Check Query string followed by cookie
848 val
= WebUtil
.getQueryVar(name
);
850 val
= WebUtil
.readSetting(name
, defVal
);
852 UI
.updateSetting(name
, val
);
853 //Util.Debug("Setting '" + name + "' initialized to '" + val + "'");
857 // Force a setting to be a certain value
858 forceSetting: function(name
, val
) {
859 UI
.updateSetting(name
, val
);
864 // Show the popup status panel
865 togglePopupStatusPanel: function() {
866 var psp
= $D('noVNC_popup_status_panel');
867 if (UI
.popupStatusOpen
=== true) {
868 psp
.style
.display
= "none";
869 UI
.popupStatusOpen
= false;
871 psp
.innerHTML
= $D('noVNC_status').innerHTML
;
872 psp
.style
.display
= "block";
873 psp
.style
.left
= window
.innerWidth
/2 -
874 parseInt(window
.getComputedStyle(psp
, false).width
)/2 -30 + "px";
875 UI
.popupStatusOpen
= true;
879 // Show the XVP panel
880 toggleXvpPanel: function() {
881 // Close the description panel
882 $D('noVNC_description').style
.display
= "none";
883 if (UI
.pveCommandsOpen
=== true) {
884 UI
.togglePVECommandPanel();
886 // Close settings if open
887 if (UI
.settingsOpen
=== true) {
889 UI
.closeSettingsMenu();
891 // Close connection settings if open
892 if (UI
.connSettingsOpen
=== true) {
893 UI
.toggleConnectPanel();
895 // Close popup status panel if open
896 if (UI
.popupStatusOpen
=== true) {
897 UI
.togglePopupStatusPanel();
899 // Close clipboard panel if open
900 if (UI
.clipboardOpen
=== true) {
901 UI
.toggleClipboardPanel();
904 if (UI
.xvpOpen
=== true) {
905 //$D('noVNC_xvp').style.display = "none";
906 //$D('xvpButton').className = "noVNC_status_button";
909 //$D('noVNC_xvp').style.display = "block";
910 //$D('xvpButton').className = "noVNC_status_button_selected";
915 // Show the clipboard panel
916 toggleClipboardPanel: function() {
917 // Close the description panel
918 $D('noVNC_description').style
.display
= "none";
919 if (UI
.pveCommandsOpen
=== true) {
920 UI
.togglePVECommandPanel();
922 if (UI
.sendKeysVisible
=== true) {
923 UI
.togglePVESendKeysPanel();
925 // Close settings if open
926 if (UI
.settingsOpen
=== true) {
928 UI
.closeSettingsMenu();
930 // Close connection settings if open
931 if (UI
.connSettingsOpen
=== true) {
932 UI
.toggleConnectPanel();
934 // Close popup status panel if open
935 if (UI
.popupStatusOpen
=== true) {
936 UI
.togglePopupStatusPanel();
938 // Close XVP panel if open
939 if (UI
.xvpOpen
=== true) {
942 // Toggle Clipboard Panel
943 if (UI
.clipboardOpen
=== true) {
944 $D('noVNC_clipboard').style
.display
= "none";
945 $D('clipboardButton').className
= "noVNC_status_button";
946 UI
.clipboardOpen
= false;
948 $D('noVNC_clipboard').style
.display
= "block";
949 $D('clipboardButton').className
= "noVNC_status_button_selected";
950 UI
.clipboardOpen
= true;
954 // Show the connection settings panel/menu
955 toggleConnectPanel: function() {
956 // Close the description panel
957 $D('noVNC_description').style
.display
= "none";
958 if (UI
.pveCommandsOpen
=== true) {
959 UI
.togglePVECommandPanel();
961 // Close connection settings if open
962 if (UI
.settingsOpen
=== true) {
964 UI
.closeSettingsMenu();
965 //$D('connectButton').className = "noVNC_status_button";
967 // Close clipboard panel if open
968 if (UI
.clipboardOpen
=== true) {
969 UI
.toggleClipboardPanel();
971 // Close popup status panel if open
972 if (UI
.popupStatusOpen
=== true) {
973 UI
.togglePopupStatusPanel();
975 // Close XVP panel if open
976 if (UI
.xvpOpen
=== true) {
980 // Toggle Connection Panel
981 if (UI
.connSettingsOpen
=== true) {
982 $D('noVNC_controls').style
.display
= "none";
983 //$D('connectButton').className = "noVNC_status_button";
984 UI
.connSettingsOpen
= false;
985 UI
.saveSetting('host');
986 UI
.saveSetting('port');
987 //UI.saveSetting('password');
989 $D('noVNC_controls').style
.display
= "block";
990 //$D('connectButton').className = "noVNC_status_button_selected";
991 UI
.connSettingsOpen
= true;
992 $D('noVNC_host').focus();
996 // Toggle the settings menu:
997 // On open, settings are refreshed from saved cookies.
998 // On close, settings are applied
999 toggleSettingsPanel: function() {
1000 // Close the description panel
1001 $D('noVNC_description').style
.display
= "none";
1002 if (UI
.settingsOpen
) {
1004 UI
.closeSettingsMenu();
1006 UI
.updateSetting('encrypt');
1007 UI
.updateSetting('true_color');
1008 if (UI
.rfb
.get_display().get_cursor_uri()) {
1009 UI
.updateSetting('cursor');
1011 UI
.updateSetting('cursor', !UI
.isTouchDevice
);
1012 $D('noVNC_cursor').disabled
= true;
1014 UI
.updateSetting('clip');
1015 UI
.updateSetting('shared');
1016 UI
.updateSetting('view_only');
1017 UI
.updateSetting('path');
1018 UI
.updateSetting('repeaterID');
1019 UI
.updateSetting('stylesheet');
1020 UI
.updateSetting('logging');
1022 UI
.openSettingsMenu();
1027 openSettingsMenu: function() {
1028 // Close the description panel
1029 $D('noVNC_description').style
.display
= "none";
1030 if (UI
.pveCommandsOpen
=== true) {
1031 UI
.togglePVECommandPanel();
1033 // Close clipboard panel if open
1034 if (UI
.clipboardOpen
=== true) {
1035 UI
.toggleClipboardPanel();
1037 // Close connection settings if open
1038 if (UI
.connSettingsOpen
=== true) {
1039 UI
.toggleConnectPanel();
1041 // Close popup status panel if open
1042 if (UI
.popupStatusOpen
=== true) {
1043 UI
.togglePopupStatusPanel();
1045 // Close XVP panel if open
1046 if (UI
.xvpOpen
=== true) {
1047 UI
.toggleXvpPanel();
1049 $D('noVNC_settings').style
.display
= "block";
1050 //$D('settingsButton').className = "noVNC_status_button_selected";
1051 UI
.settingsOpen
= true;
1054 // Close menu (without applying settings)
1055 closeSettingsMenu: function() {
1056 $D('noVNC_settings').style
.display
= "none";
1057 //$D('settingsButton').className = "noVNC_status_button";
1058 UI
.settingsOpen
= false;
1061 // Save/apply settings when 'Apply' button is pressed
1062 settingsApply: function() {
1063 //Util.Debug(">> settingsApply");
1064 UI
.saveSetting('encrypt');
1065 UI
.saveSetting('true_color');
1066 if (UI
.rfb
.get_display().get_cursor_uri()) {
1067 UI
.saveSetting('cursor');
1069 UI
.saveSetting('clip');
1070 UI
.saveSetting('shared');
1071 UI
.saveSetting('view_only');
1072 UI
.saveSetting('path');
1073 UI
.saveSetting('repeaterID');
1074 UI
.saveSetting('stylesheet');
1075 UI
.saveSetting('logging');
1077 // Settings with immediate (non-connected related) effect
1078 WebUtil
.selectStylesheet(UI
.getSetting('stylesheet'));
1079 WebUtil
.init_logging(UI
.getSetting('logging'));
1081 UI
.setViewDrag(UI
.rfb
.get_viewportDrag());
1082 //Util.Debug("<< settingsApply");
1087 setPassword: function() {
1088 UI
.rfb
.sendPassword($D('noVNC_password').value
);
1089 //Reset connect button.
1090 $D('noVNC_connect_button').value
= "Connect";
1091 $D('noVNC_connect_button').onclick
= UI
.Connect
;
1092 //Hide connection panel.
1093 UI
.toggleConnectPanel();
1097 sendCtrlAltDel: function() {
1098 UI
.rfb
.sendCtrlAltDel();
1101 xvpShutdown: function() {
1102 UI
.rfb
.xvpShutdown();
1105 xvpReboot: function() {
1109 xvpReset: function() {
1113 setMouseButton: function(num
) {
1114 var b
, blist
= [0, 1,2,4], button
;
1116 if (typeof num
=== 'undefined') {
1117 // Disable mouse buttons
1121 UI
.rfb
.get_mouse().set_touchButton(num
);
1124 for (b
= 0; b
< blist
.length
; b
++) {
1125 button
= $D('noVNC_mouse_button' + blist
[b
]);
1126 if (blist
[b
] === num
) {
1127 button
.style
.display
= "";
1129 button
.style
.display
= "none";
1131 button.style.backgroundColor = "black";
1132 button.style.color = "lightgray";
1133 button.style.backgroundColor = "";
1134 button.style.color = "";
1140 updateState: function(rfb
, state
, oldstate
, msg
) {
1141 var s
, sb
, c
, d
, cad
, vd
, klass
;
1142 UI
.rfb_state
= state
;
1146 klass
= "noVNC_status_error";
1149 klass
= "noVNC_status_normal";
1151 case 'disconnected':
1152 $D('noVNC_logo').style
.display
= "block";
1155 klass
= "noVNC_status_normal";
1158 UI
.toggleConnectPanel();
1160 $D('noVNC_connect_button').value
= "Send Password";
1161 $D('noVNC_connect_button').onclick
= UI
.setPassword
;
1162 $D('noVNC_password').focus();
1164 klass
= "noVNC_status_warn";
1167 klass
= "noVNC_status_warn";
1171 if (typeof(msg
) !== 'undefined') {
1172 $D('noVNC-control-bar').setAttribute("class", klass
);
1173 $D('noVNC_status').innerHTML
= msg
;
1176 UI
.updateVisualState();
1179 // Disable/enable controls depending on connection state
1180 updateVisualState: function() {
1181 var connected
= UI
.rfb_state
=== 'normal' ? true : false;
1183 //Util.Debug(">> updateVisualState");
1184 $D('noVNC_encrypt').disabled
= connected
;
1185 $D('noVNC_true_color').disabled
= connected
;
1186 if (UI
.rfb
&& UI
.rfb
.get_display() &&
1187 UI
.rfb
.get_display().get_cursor_uri()) {
1188 $D('noVNC_cursor').disabled
= connected
;
1190 UI
.updateSetting('cursor', !UI
.isTouchDevice
);
1191 $D('noVNC_cursor').disabled
= true;
1193 $D('noVNC_shared').disabled
= connected
;
1194 $D('noVNC_view_only').disabled
= connected
;
1195 $D('noVNC_path').disabled
= connected
;
1196 $D('noVNC_repeaterID').disabled
= connected
;
1200 UI
.setMouseButton(1);
1201 $D('clipboardButton').style
.display
= (UI
.consoletype
!== 'kvm') ? "inline" : "none";
1202 $D('showKeyboard').style
.display
= "inline";
1203 $D('noVNC_extra_keys').style
.display
= "";
1205 UI
.setMouseButton();
1206 $D('clipboardButton').style
.display
= "none";
1207 $D('showKeyboard').style
.display
= "none";
1208 $D('noVNC_extra_keys').style
.display
= "none";
1209 UI
.updateXvpVisualState(0);
1212 // State change disables viewport dragging.
1213 // It is enabled (toggled) by direct click on the button
1214 UI
.setViewDrag(false);
1216 switch (UI
.rfb_state
) {
1220 case 'disconnected':
1221 //$D('connectButton').style.display = "";
1222 //$D('disconnectButton').style.display = "none";
1225 //$D('connectButton').style.display = "none";
1226 //$D('disconnectButton').style.display = "";
1230 //Util.Debug("<< updateVisualState");
1233 // Disable/enable XVP button
1234 updateXvpVisualState: function(ver
) {
1237 //$D('xvpButton').style.display = 'inline';
1239 //$D('xvpButton').style.display = 'none';
1240 // Close XVP panel if open
1241 if (UI
.xvpOpen
=== true) {
1242 UI
.toggleXvpPanel();
1248 // Display the desktop name in the document title
1249 updateDocumentTitle: function(rfb
, name
) {
1250 document
.title
= name
+ " - noVNC";
1254 clipReceive: function(rfb
, text
) {
1255 Util
.Debug(">> UI.clipReceive: " + text
.substr(0,40) + "...");
1256 $D('noVNC_clipboard_text').value
= text
;
1257 Util
.Debug("<< UI.clipReceive");
1261 connect: function() {
1262 var host
, port
, password
, path
;
1264 UI
.closeSettingsMenu();
1265 UI
.toggleConnectPanel();
1267 host
= $D('noVNC_host').value
;
1268 port
= $D('noVNC_port').value
;
1269 password
= $D('noVNC_password').value
;
1270 path
= $D('noVNC_path').value
;
1271 if ((!host
) || (!port
)) {
1272 throw("Must set host and port");
1275 UI
.rfb
.set_encrypt(UI
.getSetting('encrypt'));
1276 UI
.rfb
.set_true_color(UI
.getSetting('true_color'));
1277 UI
.rfb
.set_local_cursor(UI
.getSetting('cursor'));
1278 UI
.rfb
.set_shared(UI
.getSetting('shared'));
1279 UI
.rfb
.set_view_only(UI
.getSetting('view_only'));
1280 UI
.rfb
.set_repeaterID(UI
.getSetting('repeaterID'));
1282 UI
.rfb
.connect(host
, port
, password
, path
);
1285 setTimeout(UI
.setBarPosition
, 100);
1286 $D('noVNC_logo').style
.display
= "none";
1289 disconnect: function() {
1290 UI
.closeSettingsMenu();
1291 UI
.rfb
.disconnect();
1293 $D('noVNC_logo').style
.display
= "block";
1294 UI
.connSettingsOpen
= false;
1295 UI
.toggleConnectPanel();
1298 displayBlur: function() {
1299 UI
.rfb
.get_keyboard().set_focused(false);
1300 UI
.rfb
.get_mouse().set_focused(false);
1303 displayFocus: function() {
1304 UI
.rfb
.get_keyboard().set_focused(true);
1305 UI
.rfb
.get_mouse().set_focused(true);
1308 clipClear: function() {
1309 $D('noVNC_clipboard_text').value
= "";
1310 UI
.rfb
.clipboardPasteFrom("");
1313 clipSend: function() {
1314 var text
= $D('noVNC_clipboard_text').value
;
1315 Util
.Debug(">> UI.clipSend: " + text
.substr(0,40) + "...");
1316 UI
.rfb
.clipboardPasteFrom(text
);
1317 Util
.Debug("<< UI.clipSend");
1321 // Enable/disable and configure viewport clipping
1322 setViewClip: function(clip
) {
1323 var display
, cur_clip
, pos
, new_w
, new_h
;
1326 display
= UI
.rfb
.get_display();
1331 cur_clip
= display
.get_viewport();
1333 if (typeof(clip
) !== 'boolean') {
1334 // Use current setting
1335 clip
= UI
.getSetting('clip');
1338 if (clip
&& !cur_clip
) {
1340 UI
.updateSetting('clip', true);
1341 } else if (!clip
&& cur_clip
) {
1342 // Turn clipping off
1343 UI
.updateSetting('clip', false);
1344 display
.set_viewport(false);
1345 $D('noVNC_canvas').style
.position
= 'static';
1346 display
.viewportChange();
1348 if (UI
.getSetting('clip')) {
1349 // If clipping, update clipping settings
1350 $D('noVNC_canvas').style
.position
= 'absolute';
1351 pos
= Util
.getPosition($D('noVNC_canvas'));
1352 new_w
= window
.innerWidth
- pos
.x
;
1353 new_h
= window
.innerHeight
- pos
.y
;
1354 display
.set_viewport(true);
1355 display
.viewportChange(0, 0, new_w
, new_h
);
1359 // Toggle/set/unset the viewport drag/move button
1360 setViewDrag: function(drag
) {
1361 var vmb
= $D('noVNC_view_drag_button');
1362 if (!UI
.rfb
) { return; }
1364 if (UI
.rfb_state
=== 'normal' &&
1365 UI
.rfb
.get_display().get_viewport()) {
1366 vmb
.style
.display
= "inline";
1368 vmb
.style
.display
= "none";
1371 if (typeof(drag
) === "undefined" ||
1372 typeof(drag
) === "object") {
1373 // If not specified, then toggle
1374 drag
= !UI
.rfb
.get_viewportDrag();
1377 vmb
.className
= "noVNC_status_button_selected";
1378 UI
.rfb
.set_viewportDrag(true);
1380 vmb
.className
= "noVNC_status_button";
1381 UI
.rfb
.set_viewportDrag(false);
1385 // On touch devices, show the OS keyboard
1386 showKeyboard: function() {
1388 kbi
= $D('keyboardinput');
1389 skb
= $D('showKeyboard');
1390 l
= kbi
.value
.length
;
1391 if(UI
.keyboardVisible
=== false) {
1393 try { kbi
.setSelectionRange(l
, l
); } // Move the caret to the end
1394 catch (err
) {} // setSelectionRange is undefined in Google Chrome
1395 UI
.keyboardVisible
= true;
1396 skb
.className
= "noVNC_status_button_selected";
1397 } else if(UI
.keyboardVisible
=== true) {
1399 skb
.className
= "noVNC_status_button";
1400 UI
.keyboardVisible
= false;
1404 keepKeyboard: function() {
1405 clearTimeout(UI
.hideKeyboardTimeout
);
1406 if(UI
.keyboardVisible
=== true) {
1407 $D('keyboardinput').focus();
1408 $D('showKeyboard').className
= "noVNC_status_button_selected";
1409 } else if(UI
.keyboardVisible
=== false) {
1410 $D('keyboardinput').blur();
1411 $D('showKeyboard').className
= "noVNC_status_button";
1415 keyboardinputReset: function() {
1416 var kbi
= $D('keyboardinput');
1417 kbi
.value
= Array(UI
.defaultKeyboardinputLen
).join("_");
1418 UI
.lastKeyboardinput
= kbi
.value
;
1421 // When normal keyboard events are left uncought, use the input events from
1422 // the keyboardinput element instead and generate the corresponding key events.
1423 // This code is required since some browsers on Android are inconsistent in
1424 // sending keyCodes in the normal keyboard events when using on screen keyboards.
1425 keyInput: function(event
) {
1426 var newValue
, oldValue
, newLen
, oldLen
;
1427 newValue
= event
.target
.value
;
1428 oldValue
= UI
.lastKeyboardinput
;
1431 // Try to check caret position since whitespace at the end
1432 // will not be considered by value.length in some browsers
1433 newLen
= Math
.max(event
.target
.selectionStart
, newValue
.length
);
1435 // selectionStart is undefined in Google Chrome
1436 newLen
= newValue
.length
;
1438 oldLen
= oldValue
.length
;
1441 var inputs
= newLen
- oldLen
;
1443 backspaces
= -inputs
;
1447 // Compare the old string with the new to account for
1448 // text-corrections or other input that modify existing text
1449 for (var i
= 0; i
< Math
.min(oldLen
, newLen
); i
++) {
1450 if (newValue
.charAt(i
) != oldValue
.charAt(i
)) {
1451 inputs
= newLen
- i
;
1452 backspaces
= oldLen
- i
;
1457 // Send the key events
1458 for (var i
= 0; i
< backspaces
; i
++)
1459 UI
.rfb
.sendKey(XK_BackSpace
);
1460 for (var i
= newLen
- inputs
; i
< newLen
; i
++)
1461 UI
.rfb
.sendKey(newValue
.charCodeAt(i
));
1463 // Control the text content length in the keyboardinput element
1464 if (newLen
> 2 * UI
.defaultKeyboardinputLen
) {
1465 UI
.keyboardinputReset();
1466 } else if (newLen
< 1) {
1467 // There always have to be some text in the keyboardinput
1468 // element with which backspace can interact.
1469 UI
.keyboardinputReset();
1470 // This sometimes causes the keyboard to disappear for a second
1471 // but it is required for the android keyboard to recognize that
1472 // text has been added to the field
1473 event
.target
.blur();
1474 // This has to be ran outside of the input handler in order to work
1475 setTimeout(function() { UI
.keepKeyboard(); }, 0);
1478 UI
.lastKeyboardinput
= newValue
;
1482 keyInputBlur: function() {
1483 $D('showKeyboard').className
= "noVNC_status_button";
1484 //Weird bug in iOS if you change keyboardVisible
1485 //here it does not actually occur so next time
1486 //you click keyboard icon it doesnt work.
1487 UI
.hideKeyboardTimeout
= setTimeout(function() { UI
.setKeyboard(); },100);
1490 showExtraKeys: function() {
1492 if(UI
.extraKeysVisible
=== false) {
1493 $D('toggleCtrlButton').style
.display
= "inline";
1494 $D('toggleAltButton').style
.display
= "inline";
1495 $D('sendTabButton').style
.display
= "inline";
1496 $D('sendEscButton').style
.display
= "inline";
1497 $D('sendCtrlAltDelButton').style
.display
= (UI
.consoletype
=== 'kvm') ? "inline" : "none";
1498 $D('showExtraKeysButton').className
= "noVNC_status_button_selected";
1499 UI
.extraKeysVisible
= true;
1500 } else if(UI
.extraKeysVisible
=== true) {
1501 $D('toggleCtrlButton').style
.display
= "";
1502 $D('toggleAltButton').style
.display
= "";
1503 $D('sendTabButton').style
.display
= "";
1504 $D('sendEscButton').style
.display
= "";
1505 $D('sendCtrlAltDelButton').style
.display
= "";
1506 $D('showExtraKeysButton').className
= "noVNC_status_button";
1507 UI
.extraKeysVisible
= false;
1511 toggleCtrl: function() {
1513 if(UI
.ctrlOn
=== false) {
1514 UI
.rfb
.sendKey(XK_Control_L
, true);
1515 $D('toggleCtrlButton').className
= "noVNC_status_button_selected";
1517 } else if(UI
.ctrlOn
=== true) {
1518 UI
.rfb
.sendKey(XK_Control_L
, false);
1519 $D('toggleCtrlButton').className
= "noVNC_status_button";
1524 toggleAlt: function() {
1526 if(UI
.altOn
=== false) {
1527 UI
.rfb
.sendKey(XK_Alt_L
, true);
1528 $D('toggleAltButton').className
= "noVNC_status_button_selected";
1530 } else if(UI
.altOn
=== true) {
1531 UI
.rfb
.sendKey(XK_Alt_L
, false);
1532 $D('toggleAltButton').className
= "noVNC_status_button";
1537 sendTab: function() {
1539 UI
.rfb
.sendKey(XK_Tab
);
1542 sendEsc: function() {
1544 UI
.rfb
.sendKey(XK_Escape
);
1547 setKeyboard: function() {
1548 UI
.keyboardVisible
= false;
1551 // iOS < Version 5 does not support position fixed. Javascript workaround:
1552 setOnscroll: function() {
1553 window
.onscroll = function() {
1554 UI
.setBarPosition();
1558 setResize: function () {
1559 window
.onResize = function() {
1560 UI
.setBarPosition();
1564 //Helper to add options to dropdown.
1565 addOption: function(selectbox
,text
,value
)
1567 var optn
= document
.createElement("OPTION");
1570 selectbox
.options
.add(optn
);
1573 setBarPosition: function() {
1574 $D('noVNC-control-bar').style
.top
= (window
.pageYOffset
) + 'px';
1575 $D('noVNC_mobile_buttons').style
.left
= (window
.pageXOffset
) + 'px';
1577 var vncwidth
= $D('noVNC_screen').style
.offsetWidth
;
1578 $D('noVNC-control-bar').style
.width
= vncwidth
+ 'px';