]>
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 * See README.md for usage and integration instructions.
11 /*jslint white: false, browser: true */
12 /*global window, $D, Util, WebUtil, RFB, Display */
14 // Load supporting scripts
15 window
.onscriptsload = function () { UI
.load(); };
16 window
.onload = function () { UI
.keyboardinputReset(); };
17 Util
.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
18 "keysymdef.js", "keyboard.js", "input.js", "display.js",
19 "jsunzip.js", "rfb.js", "keysym.js"]);
24 pveCommandsOpen
: false,
26 connSettingsOpen
: false,
27 popupStatusOpen
: false,
29 sendKeysVisible
: false,
30 keyboardVisible
: false,
31 hideKeyboardTimeout
: null,
32 lastKeyboardinput
: null,
33 defaultKeyboardinputLen
: 100,
34 extraKeysVisible
: false,
39 consoletype
: undefined,
44 // Setup rfb object, load settings from browser storage, then call
45 // UI.init to setup the UI/menus
46 load: function (callback
) {
47 WebUtil
.initSettings(UI
.pve_start
, callback
);
50 // Proxmox VE related code
52 urlEncode: function(object
) {
53 var i
,value
, params
= [];
56 if (object
.hasOwnProperty(i
)) {
58 if (value
=== undefined) value
= '';
59 params
.push(encodeURIComponent(i
) + '=' + encodeURIComponent(String(value
)));
63 return params
.join('&');
66 API2Request: function(reqOpts
) {
68 reqOpts
.method
= reqOpts
.method
|| 'GET';
70 var xhr
= new XMLHttpRequest();
72 xhr
.onload = function() {
73 var scope
= reqOpts
.scope
|| this;
77 if (xhr
.readyState
=== 4) {
78 var ctype
= xhr
.getResponseHeader('Content-Type');
79 if (xhr
.status
=== 200) {
80 if (ctype
.match(/application\/json;/)) {
81 result
= JSON
.parse(xhr
.responseText
);
83 errmsg
= 'got unexpected content type ' + ctype
;
86 errmsg
= 'Error ' + xhr
.status
+ ': ' + xhr
.statusText
;
89 errmsg
= 'Connection error - server offline?';
92 if (errmsg
!== undefined) {
93 if (reqOpts
.failure
) {
94 reqOpts
.failure
.call(scope
, errmsg
);
97 if (reqOpts
.success
) {
98 reqOpts
.success
.call(scope
, result
);
101 if (reqOpts
.callback
) {
102 reqOpts
.callback
.call(scope
, errmsg
=== undefined);
106 var data
= UI
.urlEncode(reqOpts
.params
|| {});
108 if (reqOpts
.method
=== 'GET') {
109 xhr
.open(reqOpts
.method
, "/api2/json" + reqOpts
.url
+ '?' + data
);
111 xhr
.open(reqOpts
.method
, "/api2/json" + reqOpts
.url
);
113 xhr
.setRequestHeader('Cache-Control', 'no-cache');
114 if (reqOpts
.method
=== 'POST' || reqOpts
.method
=== 'PUT') {
115 xhr
.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
116 xhr
.setRequestHeader('CSRFPreventionToken', PVE
.CSRFPreventionToken
);
118 } else if (reqOpts
.method
=== 'GET') {
121 throw "unknown method";
127 // show msg for 5 seconds
128 pve_show_msg: function(klass
, msg
, permanant
) {
129 var oldklass
= $D('noVNC-control-bar').getAttribute("class");
130 $D('noVNC-control-bar').setAttribute("class", klass
);
131 var oldmsg
= $D('noVNC_status').innerHTML
;
132 $D('noVNC_status').innerHTML
= msg
;
133 if (typeof permanent
!== 'undefined' && permanent
) return;
135 setTimeout(function() {
136 var curmsg
= $D('noVNC_status').innerHTML
;
137 if (curmsg
=== msg
) {
138 $D('noVNC_status').innerHTML
= oldmsg
;
140 var curklass
= $D('noVNC-control-bar').getAttribute("class");
141 if (curklass
=== klass
) {
142 $D('noVNC-control-bar').setAttribute("class", oldklass
);
147 pve_vm_command: function(cmd
, params
, reload
) {
150 if (UI
.consoletype
=== 'kvm') {
151 baseUrl
= '/nodes/' + UI
.nodename
+ '/qemu/' + UI
.vmid
;
152 } else if (UI
.consoletype
=== 'openvz') {
153 baseUrl
= '/nodes/' + UI
.nodename
+ '/openvz/' + UI
.vmid
;
155 throw "unknown VM type";
160 url
: baseUrl
+ "/status/" + cmd
,
162 failure: function(msg
) {
163 UI
.pve_show_msg('noVNC_status_warn', msg
);
165 success: function() {
166 UI
.pve_show_msg('noVNC_status_normall', "VM command '" + cmd
+"' successful");
168 setTimeout(function() {
176 pveCmdStart: function() {
177 if (UI
.pveCommandsOpen
=== true) {
178 UI
.togglePVECommandPanel();
180 UI
.pve_vm_command('start', {}, true);
183 pveCmdShutdown: function() {
184 if (UI
.pveCommandsOpen
=== true) {
185 UI
.togglePVECommandPanel();
187 var msg
= gettext("Do you really want to shutdown VM {0}?");
188 msg
= msg
.replace(/\{0\}/, UI
.vmid
);
190 if (confirm(msg
) === true) {
191 UI
.pve_vm_command('shutdown');
195 pveCmdStop: function() {
196 if (UI
.pveCommandsOpen
=== true) {
197 UI
.togglePVECommandPanel();
200 var msg
= gettext("Do you really want to stop VM {0}?");
201 msg
= msg
.replace(/\{0\}/, UI
.vmid
);
203 if (confirm(msg
) === true) {
204 UI
.pve_vm_command('stop');
208 pveCmdReset: function() {
209 if (UI
.pveCommandsOpen
=== true) {
210 UI
.togglePVECommandPanel();
212 var msg
= gettext("Do you really want to reset VM {0}?");
213 msg
= msg
.replace(/\{0\}/, UI
.vmid
);
215 if (confirm(msg
) === true) {
216 UI
.pve_vm_command('reset');
220 pveCmdSuspend: function() {
221 if (UI
.pveCommandsOpen
=== true) {
222 UI
.togglePVECommandPanel();
224 var msg
= gettext("Do you really want to suspend VM {0}?");
225 msg
= msg
.replace(/\{0\}/, UI
.vmid
);
227 if (confirm(msg
) === true) {
228 UI
.pve_vm_command('suspend');
232 pveCmdResume: function() {
233 if (UI
.pveCommandsOpen
=== true) {
234 UI
.togglePVECommandPanel();
236 UI
.pve_vm_command('resume');
239 pveCmdReload: function() {
240 if (UI
.pveCommandsOpen
=== true) {
241 UI
.togglePVECommandPanel();
246 pveReload: function() {
250 pve_send_key: function(keyname
) {
253 if (UI
.consoletype
=== 'kvm') {
254 baseUrl
= '/nodes/' + UI
.nodename
+ '/qemu/' + UI
.vmid
;
256 throw "send key not implemented";
260 params
: { key
: keyname
},
261 url
: baseUrl
+ '/sendkey',
263 failure: function(msg
) {
264 UI
.pve_show_msg('noVNC_status_warn', msg
);
269 pve_start: function(callback
) {
270 UI
.consoletype
= WebUtil
.getQueryVar('console');
271 UI
.vmid
= WebUtil
.getQueryVar('vmid');
272 UI
.vmname
= WebUtil
.getQueryVar('vmname');
273 UI
.nodename
= WebUtil
.getQueryVar('node');
277 var params
= { websocket
: 1 };
280 // add pve command buttons
281 var cmdpanel
= $D('noVNC_pve_command_menu');
284 text
: gettext('Start'),
285 handler
: UI
.pveCmdStart
,
286 enable
: { kvm
: 1, openvz
: 1 }
289 text
: gettext('Shutdown'),
290 handler
: UI
.pveCmdShutdown
,
291 enable
: { kvm
: 1, openvz
: 1 }
294 text
: gettext('Stop'),
295 handler
: UI
.pveCmdStop
,
296 enable
: { kvm
: 1, openvz
: 1 }
299 text
: gettext('Reset'),
300 handler
: UI
.pveCmdReset
,
304 text
: gettext('Suspend'),
305 handler
: UI
.pveCmdSuspend
,
309 text
: gettext('Resume'),
310 handler
: UI
.pveCmdResume
,
314 text
: gettext('Reload'),
315 handler
: UI
.pveCmdReload
,
319 buttonlist
.forEach(function(btn
) {
320 if (btn
.enable
.any
|| btn
.enable
[UI
.consoletype
]) {
321 var el
= document
.createElement('input');
322 el
.setAttribute('type', 'button');
323 el
.setAttribute('value', btn
.text
);
324 el
.onclick
= btn
.handler
;
325 el
.style
.display
= "block";
326 el
.style
.width
= "100%";
327 el
.style
.minWidth
= "150px";
328 cmdpanel
.appendChild(el
);
329 console
.log("ADD: " + btn
.text
);
333 // add sendKeys buttons
334 var skpanel
= $D('noVNC_send_keys_panel');
338 text
: 'Tab', handler: function() {
339 UI
.pve_send_key('tab');
343 text
: 'Ctrl-Alt-Delete', handler: function() {
344 UI
.pve_send_key('ctrl-alt-delete');
348 text
: 'Ctrl-Alt-Backspace', handler: function() {
349 UI
.pve_send_key('ctrl-alt-backspace');
353 text
: 'Ctrl-Alt-F1', handler: function() {
354 UI
.pve_send_key('ctrl-alt-f1');
358 text
: 'Ctrl-Alt-F2', handler: function() {
359 UI
.pve_send_key('ctrl-alt-f2');
363 text
: 'Ctrl-Alt-F3', handler: function() {
364 UI
.pve_send_key('ctrl-alt-f3');
368 text
: 'Ctrl-Alt-F4', handler: function() {
369 UI
.pve_send_key('ctrl-alt-f4');
373 text
: 'Ctrl-Alt-F5', handler: function() {
374 UI
.pve_send_key('ctrl-alt-f5');
378 text
: 'Ctrl-Alt-F6', handler: function() {
379 UI
.pve_send_key('ctrl-alt-f6');
383 text
: 'Ctrl-Alt-F7', handler: function() {
384 UI
.pve_send_key('ctrl-alt-f7');
388 text
: 'Ctrl-Alt-F8', handler: function() {
389 UI
.pve_send_key('ctrl-alt-f8');
393 text
: 'Ctrl-Alt-F9', handler: function() {
394 UI
.pve_send_key('ctrl-alt-f9');
398 text
: 'Ctrl-Alt-F10', handler: function() {
399 UI
.pve_send_key('ctrl-alt-f10');
403 text
: 'Ctrl-Alt-F11', handler: function() {
404 UI
.pve_send_key('ctrl-alt-f11');
408 text
: 'Ctrl-Alt-F12', handler: function() {
409 UI
.pve_send_key('ctrl-alt-f12');
414 buttonlist
.forEach(function(btn
) {
415 var el
= document
.createElement('input');
416 el
.setAttribute('type', 'button');
417 el
.setAttribute('value', btn
.text
);
418 el
.onclick = function(handler
) {
420 if (UI
.sendKeysVisible
=== true) {
421 UI
.togglePVESendKeysPanel();
427 el
.style
.display
= "block";
428 el
.style
.width
= "100%";
429 el
.style
.minWidth
= "150px";
430 skpanel
.appendChild(el
);
435 if (UI
.consoletype
=== 'kvm') {
436 var baseUrl
= '/nodes/' + UI
.nodename
+ '/qemu/' + UI
.vmid
;
437 url
= baseUrl
+ '/vncproxy';
438 wsurl
= baseUrl
+ '/vncwebsocket';
439 title
= "VM " + UI
.vmid
;
441 title
+= " ('" + UI
.vmname
+ "')";
443 } else if (UI
.consoletype
=== 'openvz') {
444 var baseUrl
= '/nodes/' + UI
.nodename
+ '/openvz/' + UI
.vmid
;
445 url
= baseUrl
+ '/vncproxy';
446 wsurl
= baseUrl
+ '/vncwebsocket';
447 title
= "CT " + UI
.vmid
;
449 title
+= " ('" + UI
.vmname
+ "')";
451 } else if (UI
.consoletype
=== 'shell') {
452 var baseUrl
= '/nodes/' + UI
.nodename
;
453 url
= baseUrl
+ '/vncshell';
454 wsurl
= baseUrl
+ '/vncwebsocket';
455 title
= "node '" + UI
.nodename
+ "'";
456 } else if (UI
.consoletype
=== 'upgrade') {
457 var baseUrl
= '/nodes/' + UI
.nodename
;
458 url
= baseUrl
+ '/vncshell';
459 wsurl
= baseUrl
+ '/vncwebsocket';
461 title
= gettext('System upgrade on node {0}');
462 title
= title
.replace(/\{0\}/, UI
.nodename
);
464 throw "implement me";
467 document
.title
= title
;
469 var start_vnc_viewer = function(param
) {
470 var wsparams
= UI
.urlEncode({
472 vncticket
: param
.ticket
475 UI
.updateSetting('host', window
.location
.hostname
);
476 UI
.updateSetting('port', window
.location
.port
);
477 UI
.updateSetting('password', param
.ticket
);
478 UI
.updateSetting('encrypt', true);
479 UI
.updateSetting('true_color', true);
480 UI
.updateSetting('cursor', !UI
.isTouchDevice
);
481 UI
.updateSetting('shared', true);
482 UI
.updateSetting('view_only', false);
484 UI
.updateSetting('path', 'api2/json' + wsurl
+ "?" + wsparams
);
493 success: function(result
) {
494 start_vnc_viewer(result
.data
);
496 failure: function(msg
) {
497 UI
.pve_show_msg('noVNC_status_error', msg
, 1);
502 lastFBWidth
: undefined,
503 lastFBHeight
: undefined,
504 sizeUpdateTimer
: undefined,
506 updateFBSize: function(rfb
, width
, height
) {
508 UI
.lastFBWidth
= width
+ 2;
509 UI
.lastFBHeight
= height
+ 6;
511 if (UI
.sizeUpdateTimer
!== undefined) {
512 clearInterval(UI
.sizeUpdateTimer
);
514 if (UI
.getSetting('clip')) return;
516 var update_size = function() {
520 if (window
.innerHeight
) {
521 oh
= window
.innerHeight
;
522 ow
= window
.innerWidth
;
523 } else if (document
.documentElement
&&
524 document
.documentElement
.clientHeight
) {
525 oh
= document
.documentElement
.clientHeight
;
526 ow
= document
.documentElement
.clientWidth
;
527 } else if (document
.body
) {
528 oh
= document
.body
.clientHeight
;
529 ow
= document
.body
.clientWidth
;
531 throw "can't get window size";
534 // see base.css/noVNC_screen_pad
535 var toolbar_height
= 36;
537 var offsetw
= UI
.lastFBWidth
- ow
;
538 var offseth
= UI
.lastFBHeight
+ toolbar_height
- oh
;
539 if (offsetw
!== 0 || offseth
!== 0) {
540 //console.log("try resize by " + offsetw + " " + offseth);
541 window
.resizeBy(offsetw
, offseth
);
546 UI
.sizeUpdateTimer
= setInterval(update_size
, 1000);
553 // Open/close PVE commandand menu
554 togglePVECommandPanel: function() {
555 // Close the description panel
556 $D('noVNC_description').style
.display
= "none";
557 if (UI
.sendKeysVisible
=== true) {
558 UI
.togglePVESendKeysPanel();
560 // Close clipboard panel if open
561 if (UI
.clipboardOpen
=== true) {
562 UI
.toggleClipboardPanel();
564 // Close connection settings if open
565 if (UI
.connSettingsOpen
=== true) {
566 UI
.toggleConnectPanel();
568 // Close popup status panel if open
569 if (UI
.popupStatusOpen
=== true) {
570 UI
.togglePopupStatusPanel();
572 // Close XVP panel if open
573 if (UI
.xvpOpen
=== true) {
576 if (UI
.pveCommandsOpen
) {
577 $D('noVNC_pve_commands').style
.display
= "none";
578 $D('pveCommandsButton').className
= "noVNC_status_button";
579 UI
.pveCommandsOpen
= false;
581 $D('noVNC_pve_commands').style
.display
= "block";
582 $D('pveCommandsButton').className
= "noVNC_status_button_selected";
583 UI
.pveCommandsOpen
= true;
587 // Open/close PVE SendKeys menu
588 togglePVESendKeysPanel: function() {
589 // Close the description panel
590 $D('noVNC_description').style
.display
= "none";
591 if (UI
.pveCommandsOpen
=== true) {
592 UI
.togglePVECommandPanel();
594 // Close clipboard panel if open
595 if (UI
.clipboardOpen
=== true) {
596 UI
.toggleClipboardPanel();
598 // Close connection settings if open
599 if (UI
.connSettingsOpen
=== true) {
600 UI
.toggleConnectPanel();
602 // Close popup status panel if open
603 if (UI
.popupStatusOpen
=== true) {
604 UI
.togglePopupStatusPanel();
606 // Close XVP panel if open
607 if (UI
.xvpOpen
=== true) {
610 if (UI
.sendKeysVisible
) {
611 $D('noVNC_send_keys').style
.display
= "none";
612 $D('showSendKeysButton').className
= "noVNC_status_button";
613 UI
.sendKeysVisible
= false;
615 $D('noVNC_send_keys').style
.display
= "block";
616 $D('showSendKeysButton').className
= "noVNC_status_button_selected";
617 UI
.sendKeysVisible
= true;
621 // Render default UI and initialize settings menu
622 start: function(callback
) {
623 var html
= '', i
, sheet
, sheets
, llevels
, port
, autoconnect
;
625 UI
.isTouchDevice
= 'ontouchstart' in document
.documentElement
;
627 // Stylesheet selection dropdown
628 sheet
= WebUtil
.selectStylesheet();
629 sheets
= WebUtil
.getStylesheets();
630 for (i
= 0; i
< sheets
.length
; i
+= 1) {
631 //UI.addOption($D('noVNC_stylesheet'),sheets[i].title, sheets[i].title);
634 // Logging selection dropdown
635 llevels
= ['error', 'warn', 'info', 'debug'];
636 for (i
= 0; i
< llevels
.length
; i
+= 1) {
637 //UI.addOption($D('noVNC_logging'),llevels[i], llevels[i]);
640 // Settings with immediate effects
641 UI
.initSetting('logging', 'warn');
642 WebUtil
.init_logging(UI
.getSetting('logging'));
644 UI
.initSetting('stylesheet', 'default');
645 WebUtil
.selectStylesheet(null);
646 // call twice to get around webkit bug
647 WebUtil
.selectStylesheet(UI
.getSetting('stylesheet'));
649 UI
.initSetting('repeaterID', '');
651 UI
.rfb
= RFB({'target': $D('noVNC_canvas'),
652 'onUpdateState': UI
.updateState
,
653 'onXvpInit': UI
.updateXvpVisualState
,
654 'onClipboard': UI
.clipReceive
,
655 //'onDesktopName': UI.updateDocumentTitle,
656 'onFBResize': UI
.updateFBSize
});
659 if (autoconnect
=== 'true' || autoconnect
== '1') {
666 UI
.updateVisualState();
668 // Unfocus clipboard when over the VNC area
669 //$D('VNC_screen').onmousemove = function () {
670 // var keyboard = UI.rfb.get_keyboard();
671 // if ((! keyboard) || (! keyboard.get_focused())) {
672 // $D('VNC_clipboard_text').blur();
676 // Show mouse selector buttons on touch screen devices
677 if (UI
.isTouchDevice
) {
678 // Show mobile buttons
679 $D('noVNC_mobile_buttons').style
.display
= "inline";
680 $D('showSendKeysButton').style
.display
= "none";
682 // Remove the address bar
683 setTimeout(function() { window
.scrollTo(0, 1); }, 100);
684 UI
.forceSetting('clip', true);
685 $D('noVNC_clip').disabled
= true;
687 $D('showSendKeysButton').style
.display
= (UI
.consoletype
=== 'kvm') ? "inline" : "none";
688 UI
.initSetting('clip', false);
691 //iOS Safari does not support CSS position:fixed.
692 //This detects iOS devices and enables javascript workaround.
693 if ((navigator
.userAgent
.match(/iPhone/i)) ||
694 (navigator
.userAgent
.match(/iPod/i)) ||
695 (navigator
.userAgent
.match(/iPad/i))) {
701 $D('noVNC_host').focus();
704 Util
.addEvent(window
, 'resize', UI
.setViewClip
);
706 Util
.addEvent(window
, 'beforeunload', function () {
707 if (UI
.rfb_state
=== 'normal') {
708 return "You are currently connected.";
712 // Show description by default when hosted at for kanaka.github.com
713 if (location
.host
=== "kanaka.github.io") {
714 // Open the description dialog
715 $D('noVNC_description').style
.display
= "block";
717 // Show the connect panel on first load unless autoconnecting
718 if (autoconnect
=== UI
.connSettingsOpen
) {
719 UI
.toggleConnectPanel();
723 // Add mouse event click/focus/blur event handlers to the UI
724 UI
.addMouseHandlers();
726 if (typeof callback
=== "function") {
731 addMouseHandlers: function() {
732 // Setup interface handlers that can't be inline
733 $D("noVNC_view_drag_button").onclick
= UI
.setViewDrag
;
734 $D("noVNC_mouse_button0").onclick = function () { UI
.setMouseButton(1); };
735 $D("noVNC_mouse_button1").onclick = function () { UI
.setMouseButton(2); };
736 $D("noVNC_mouse_button2").onclick = function () { UI
.setMouseButton(4); };
737 $D("noVNC_mouse_button4").onclick = function () { UI
.setMouseButton(0); };
738 $D("showKeyboard").onclick
= UI
.showKeyboard
;
740 $D("keyboardinput").oninput
= UI
.keyInput
;
741 $D("keyboardinput").onblur
= UI
.keyInputBlur
;
743 $D("showExtraKeysButton").onclick
= UI
.showExtraKeys
;
744 $D("toggleCtrlButton").onclick
= UI
.toggleCtrl
;
745 $D("toggleAltButton").onclick
= UI
.toggleAlt
;
746 $D("sendTabButton").onclick
= UI
.sendTab
;
747 $D("sendEscButton").onclick
= UI
.sendEsc
;
749 $D("showSendKeysButton").onclick
= UI
.togglePVESendKeysPanel
;
751 $D("sendCtrlAltDelButton").onclick
= UI
.sendCtrlAltDel
;
752 //$D("xvpShutdownButton").onclick = UI.xvpShutdown;
753 //$D("xvpRebootButton").onclick = UI.xvpReboot;
754 //$D("xvpResetButton").onclick = UI.xvpReset;
755 // disable popup, because it does not provide more info?
756 //$D("noVNC_status").onclick = UI.togglePopupStatusPanel;
757 $D("noVNC_popup_status_panel").onclick
= UI
.togglePopupStatusPanel
;
758 //$D("xvpButton").onclick = UI.toggleXvpPanel;
759 $D("clipboardButton").onclick
= UI
.toggleClipboardPanel
;
760 //$D("settingsButton").onclick = UI.toggleSettingsPanel;
761 $D("pveCommandsButton").onclick
= UI
.togglePVECommandPanel
;
762 //$D("connectButton").onclick = UI.toggleConnectPanel;
763 //$D("disconnectButton").onclick = UI.disconnect;
764 //$D("descriptionButton").onclick = UI.toggleConnectPanel;
766 $D("noVNC_clipboard_text").onfocus
= UI
.displayBlur
;
767 $D("noVNC_clipboard_text").onblur
= UI
.displayFocus
;
768 $D("noVNC_clipboard_text").onchange
= UI
.clipSend
;
769 $D("noVNC_clipboard_clear_button").onclick
= UI
.clipClear
;
771 //$D("noVNC_settings_menu").onmouseover = UI.displayBlur;
772 //$D("noVNC_settings_menu").onmouseover = UI.displayFocus;
773 //$D("noVNC_apply").onclick = UI.settingsApply;
775 //$D("noVNC_connect_button").onclick = UI.connect;
778 // Read form control compatible setting from cookie
779 getSetting: function(name
) {
780 var val
, ctrl
= $D('noVNC_' + name
);
781 val
= WebUtil
.readSetting(name
);
782 if (val
!== null && ctrl
.type
=== 'checkbox') {
783 if (val
.toString().toLowerCase() in {'0':1, 'no':1, 'false':1}) {
792 // Update cookie and form control setting. If value is not set, then
793 // updates from control to current cookie setting.
794 updateSetting: function(name
, value
) {
796 var i
, ctrl
= $D('noVNC_' + name
);
797 // Save the cookie for this session
798 if (typeof value
!== 'undefined') {
799 WebUtil
.writeSetting(name
, value
);
802 // Update the settings control
803 value
= UI
.getSetting(name
);
805 if (ctrl
.type
=== 'checkbox') {
806 ctrl
.checked
= value
;
808 } else if (typeof ctrl
.options
!== 'undefined') {
809 for (i
= 0; i
< ctrl
.options
.length
; i
+= 1) {
810 if (ctrl
.options
[i
].value
=== value
) {
811 ctrl
.selectedIndex
= i
;
816 /*Weird IE9 error leads to 'null' appearring
817 in textboxes instead of ''.*/
818 if (value
=== null) {
825 // Save control setting to cookie
826 saveSetting: function(name
) {
827 var val
, ctrl
= $D('noVNC_' + name
);
828 if (ctrl
.type
=== 'checkbox') {
830 } else if (typeof ctrl
.options
!== 'undefined') {
831 val
= ctrl
.options
[ctrl
.selectedIndex
].value
;
835 WebUtil
.writeSetting(name
, val
);
836 //Util.Debug("Setting saved '" + name + "=" + val + "'");
840 // Initial page load read/initialization of settings
841 initSetting: function(name
, defVal
) {
844 // Check Query string followed by cookie
845 val
= WebUtil
.getQueryVar(name
);
847 val
= WebUtil
.readSetting(name
, defVal
);
849 UI
.updateSetting(name
, val
);
850 //Util.Debug("Setting '" + name + "' initialized to '" + val + "'");
854 // Force a setting to be a certain value
855 forceSetting: function(name
, val
) {
856 UI
.updateSetting(name
, val
);
861 // Show the popup status panel
862 togglePopupStatusPanel: function() {
863 var psp
= $D('noVNC_popup_status_panel');
864 if (UI
.popupStatusOpen
=== true) {
865 psp
.style
.display
= "none";
866 UI
.popupStatusOpen
= false;
868 psp
.innerHTML
= $D('noVNC_status').innerHTML
;
869 psp
.style
.display
= "block";
870 psp
.style
.left
= window
.innerWidth
/2 -
871 parseInt(window
.getComputedStyle(psp
, false).width
)/2 -30 + "px";
872 UI
.popupStatusOpen
= true;
876 // Show the XVP panel
877 toggleXvpPanel: function() {
878 // Close the description panel
879 $D('noVNC_description').style
.display
= "none";
880 if (UI
.pveCommandsOpen
=== true) {
881 UI
.togglePVECommandPanel();
883 // Close settings if open
884 if (UI
.settingsOpen
=== true) {
886 UI
.closeSettingsMenu();
888 // Close connection settings if open
889 if (UI
.connSettingsOpen
=== true) {
890 UI
.toggleConnectPanel();
892 // Close popup status panel if open
893 if (UI
.popupStatusOpen
=== true) {
894 UI
.togglePopupStatusPanel();
896 // Close clipboard panel if open
897 if (UI
.clipboardOpen
=== true) {
898 UI
.toggleClipboardPanel();
901 if (UI
.xvpOpen
=== true) {
902 //$D('noVNC_xvp').style.display = "none";
903 //$D('xvpButton').className = "noVNC_status_button";
906 //$D('noVNC_xvp').style.display = "block";
907 //$D('xvpButton').className = "noVNC_status_button_selected";
912 // Show the clipboard panel
913 toggleClipboardPanel: function() {
914 // Close the description panel
915 $D('noVNC_description').style
.display
= "none";
916 if (UI
.pveCommandsOpen
=== true) {
917 UI
.togglePVECommandPanel();
919 if (UI
.sendKeysVisible
=== true) {
920 UI
.togglePVESendKeysPanel();
922 // Close settings if open
923 if (UI
.settingsOpen
=== true) {
925 UI
.closeSettingsMenu();
927 // Close connection settings if open
928 if (UI
.connSettingsOpen
=== true) {
929 UI
.toggleConnectPanel();
931 // Close popup status panel if open
932 if (UI
.popupStatusOpen
=== true) {
933 UI
.togglePopupStatusPanel();
935 // Close XVP panel if open
936 if (UI
.xvpOpen
=== true) {
939 // Toggle Clipboard Panel
940 if (UI
.clipboardOpen
=== true) {
941 $D('noVNC_clipboard').style
.display
= "none";
942 $D('clipboardButton').className
= "noVNC_status_button";
943 UI
.clipboardOpen
= false;
945 $D('noVNC_clipboard').style
.display
= "block";
946 $D('clipboardButton').className
= "noVNC_status_button_selected";
947 UI
.clipboardOpen
= true;
951 // Show the connection settings panel/menu
952 toggleConnectPanel: function() {
953 // Close the description panel
954 $D('noVNC_description').style
.display
= "none";
955 if (UI
.pveCommandsOpen
=== true) {
956 UI
.togglePVECommandPanel();
958 // Close connection settings if open
959 if (UI
.settingsOpen
=== true) {
961 UI
.closeSettingsMenu();
962 //$D('connectButton').className = "noVNC_status_button";
964 // Close clipboard panel if open
965 if (UI
.clipboardOpen
=== true) {
966 UI
.toggleClipboardPanel();
968 // Close popup status panel if open
969 if (UI
.popupStatusOpen
=== true) {
970 UI
.togglePopupStatusPanel();
972 // Close XVP panel if open
973 if (UI
.xvpOpen
=== true) {
977 // Toggle Connection Panel
978 if (UI
.connSettingsOpen
=== true) {
979 $D('noVNC_controls').style
.display
= "none";
980 //$D('connectButton').className = "noVNC_status_button";
981 UI
.connSettingsOpen
= false;
982 UI
.saveSetting('host');
983 UI
.saveSetting('port');
984 //UI.saveSetting('password');
986 $D('noVNC_controls').style
.display
= "block";
987 //$D('connectButton').className = "noVNC_status_button_selected";
988 UI
.connSettingsOpen
= true;
989 $D('noVNC_host').focus();
993 // Toggle the settings menu:
994 // On open, settings are refreshed from saved cookies.
995 // On close, settings are applied
996 toggleSettingsPanel: function() {
997 // Close the description panel
998 $D('noVNC_description').style
.display
= "none";
999 if (UI
.settingsOpen
) {
1001 UI
.closeSettingsMenu();
1003 UI
.updateSetting('encrypt');
1004 UI
.updateSetting('true_color');
1005 if (UI
.rfb
.get_display().get_cursor_uri()) {
1006 UI
.updateSetting('cursor');
1008 UI
.updateSetting('cursor', !UI
.isTouchDevice
);
1009 $D('noVNC_cursor').disabled
= true;
1011 UI
.updateSetting('clip');
1012 UI
.updateSetting('shared');
1013 UI
.updateSetting('view_only');
1014 UI
.updateSetting('path');
1015 UI
.updateSetting('repeaterID');
1016 UI
.updateSetting('stylesheet');
1017 UI
.updateSetting('logging');
1019 UI
.openSettingsMenu();
1024 openSettingsMenu: function() {
1025 // Close the description panel
1026 $D('noVNC_description').style
.display
= "none";
1027 if (UI
.pveCommandsOpen
=== true) {
1028 UI
.togglePVECommandPanel();
1030 // Close clipboard panel if open
1031 if (UI
.clipboardOpen
=== true) {
1032 UI
.toggleClipboardPanel();
1034 // Close connection settings if open
1035 if (UI
.connSettingsOpen
=== true) {
1036 UI
.toggleConnectPanel();
1038 // Close popup status panel if open
1039 if (UI
.popupStatusOpen
=== true) {
1040 UI
.togglePopupStatusPanel();
1042 // Close XVP panel if open
1043 if (UI
.xvpOpen
=== true) {
1044 UI
.toggleXvpPanel();
1046 $D('noVNC_settings').style
.display
= "block";
1047 //$D('settingsButton').className = "noVNC_status_button_selected";
1048 UI
.settingsOpen
= true;
1051 // Close menu (without applying settings)
1052 closeSettingsMenu: function() {
1053 $D('noVNC_settings').style
.display
= "none";
1054 //$D('settingsButton').className = "noVNC_status_button";
1055 UI
.settingsOpen
= false;
1058 // Save/apply settings when 'Apply' button is pressed
1059 settingsApply: function() {
1060 //Util.Debug(">> settingsApply");
1061 UI
.saveSetting('encrypt');
1062 UI
.saveSetting('true_color');
1063 if (UI
.rfb
.get_display().get_cursor_uri()) {
1064 UI
.saveSetting('cursor');
1066 UI
.saveSetting('clip');
1067 UI
.saveSetting('shared');
1068 UI
.saveSetting('view_only');
1069 UI
.saveSetting('path');
1070 UI
.saveSetting('repeaterID');
1071 UI
.saveSetting('stylesheet');
1072 UI
.saveSetting('logging');
1074 // Settings with immediate (non-connected related) effect
1075 WebUtil
.selectStylesheet(UI
.getSetting('stylesheet'));
1076 WebUtil
.init_logging(UI
.getSetting('logging'));
1078 UI
.setViewDrag(UI
.rfb
.get_viewportDrag());
1079 //Util.Debug("<< settingsApply");
1084 setPassword: function() {
1085 UI
.rfb
.sendPassword($D('noVNC_password').value
);
1086 //Reset connect button.
1087 $D('noVNC_connect_button').value
= "Connect";
1088 $D('noVNC_connect_button').onclick
= UI
.Connect
;
1089 //Hide connection panel.
1090 UI
.toggleConnectPanel();
1094 sendCtrlAltDel: function() {
1095 UI
.rfb
.sendCtrlAltDel();
1098 xvpShutdown: function() {
1099 UI
.rfb
.xvpShutdown();
1102 xvpReboot: function() {
1106 xvpReset: function() {
1110 setMouseButton: function(num
) {
1111 var b
, blist
= [0, 1,2,4], button
;
1113 if (typeof num
=== 'undefined') {
1114 // Disable mouse buttons
1118 UI
.rfb
.get_mouse().set_touchButton(num
);
1121 for (b
= 0; b
< blist
.length
; b
++) {
1122 button
= $D('noVNC_mouse_button' + blist
[b
]);
1123 if (blist
[b
] === num
) {
1124 button
.style
.display
= "";
1126 button
.style
.display
= "none";
1128 button.style.backgroundColor = "black";
1129 button.style.color = "lightgray";
1130 button.style.backgroundColor = "";
1131 button.style.color = "";
1137 updateState: function(rfb
, state
, oldstate
, msg
) {
1138 var s
, sb
, c
, d
, cad
, vd
, klass
;
1139 UI
.rfb_state
= state
;
1143 klass
= "noVNC_status_error";
1146 klass
= "noVNC_status_normal";
1148 case 'disconnected':
1149 $D('noVNC_logo').style
.display
= "block";
1152 klass
= "noVNC_status_normal";
1155 UI
.toggleConnectPanel();
1157 $D('noVNC_connect_button').value
= "Send Password";
1158 $D('noVNC_connect_button').onclick
= UI
.setPassword
;
1159 $D('noVNC_password').focus();
1161 klass
= "noVNC_status_warn";
1164 klass
= "noVNC_status_warn";
1168 if (typeof(msg
) !== 'undefined') {
1169 $D('noVNC-control-bar').setAttribute("class", klass
);
1170 $D('noVNC_status').innerHTML
= msg
;
1173 UI
.updateVisualState();
1176 // Disable/enable controls depending on connection state
1177 updateVisualState: function() {
1178 var connected
= UI
.rfb_state
=== 'normal' ? true : false;
1180 //Util.Debug(">> updateVisualState");
1181 $D('noVNC_encrypt').disabled
= connected
;
1182 $D('noVNC_true_color').disabled
= connected
;
1183 if (UI
.rfb
&& UI
.rfb
.get_display() &&
1184 UI
.rfb
.get_display().get_cursor_uri()) {
1185 $D('noVNC_cursor').disabled
= connected
;
1187 UI
.updateSetting('cursor', !UI
.isTouchDevice
);
1188 $D('noVNC_cursor').disabled
= true;
1190 $D('noVNC_shared').disabled
= connected
;
1191 $D('noVNC_view_only').disabled
= connected
;
1192 $D('noVNC_path').disabled
= connected
;
1193 $D('noVNC_repeaterID').disabled
= connected
;
1197 UI
.setMouseButton(1);
1198 $D('clipboardButton').style
.display
= (UI
.consoletype
!== 'kvm') ? "inline" : "none";
1199 $D('showKeyboard').style
.display
= "inline";
1200 $D('noVNC_extra_keys').style
.display
= "";
1202 UI
.setMouseButton();
1203 $D('clipboardButton').style
.display
= "none";
1204 $D('showKeyboard').style
.display
= "none";
1205 $D('noVNC_extra_keys').style
.display
= "none";
1206 UI
.updateXvpVisualState(0);
1209 // State change disables viewport dragging.
1210 // It is enabled (toggled) by direct click on the button
1211 UI
.setViewDrag(false);
1213 switch (UI
.rfb_state
) {
1217 case 'disconnected':
1218 //$D('connectButton').style.display = "";
1219 //$D('disconnectButton').style.display = "none";
1222 //$D('connectButton').style.display = "none";
1223 //$D('disconnectButton').style.display = "";
1227 //Util.Debug("<< updateVisualState");
1230 // Disable/enable XVP button
1231 updateXvpVisualState: function(ver
) {
1234 //$D('xvpButton').style.display = 'inline';
1236 //$D('xvpButton').style.display = 'none';
1237 // Close XVP panel if open
1238 if (UI
.xvpOpen
=== true) {
1239 UI
.toggleXvpPanel();
1245 // Display the desktop name in the document title
1246 updateDocumentTitle: function(rfb
, name
) {
1247 document
.title
= name
+ " - noVNC";
1251 clipReceive: function(rfb
, text
) {
1252 Util
.Debug(">> UI.clipReceive: " + text
.substr(0,40) + "...");
1253 $D('noVNC_clipboard_text').value
= text
;
1254 Util
.Debug("<< UI.clipReceive");
1258 connect: function() {
1259 var host
, port
, password
, path
;
1261 UI
.closeSettingsMenu();
1262 UI
.toggleConnectPanel();
1264 host
= $D('noVNC_host').value
;
1265 port
= $D('noVNC_port').value
;
1266 password
= $D('noVNC_password').value
;
1267 path
= $D('noVNC_path').value
;
1268 if ((!host
) || (!port
)) {
1269 throw("Must set host and port");
1272 UI
.rfb
.set_encrypt(UI
.getSetting('encrypt'));
1273 UI
.rfb
.set_true_color(UI
.getSetting('true_color'));
1274 UI
.rfb
.set_local_cursor(UI
.getSetting('cursor'));
1275 UI
.rfb
.set_shared(UI
.getSetting('shared'));
1276 UI
.rfb
.set_view_only(UI
.getSetting('view_only'));
1277 UI
.rfb
.set_repeaterID(UI
.getSetting('repeaterID'));
1279 UI
.rfb
.connect(host
, port
, password
, path
);
1282 setTimeout(UI
.setBarPosition
, 100);
1283 $D('noVNC_logo').style
.display
= "none";
1286 disconnect: function() {
1287 UI
.closeSettingsMenu();
1288 UI
.rfb
.disconnect();
1290 $D('noVNC_logo').style
.display
= "block";
1291 UI
.connSettingsOpen
= false;
1292 UI
.toggleConnectPanel();
1295 displayBlur: function() {
1296 UI
.rfb
.get_keyboard().set_focused(false);
1297 UI
.rfb
.get_mouse().set_focused(false);
1300 displayFocus: function() {
1301 UI
.rfb
.get_keyboard().set_focused(true);
1302 UI
.rfb
.get_mouse().set_focused(true);
1305 clipClear: function() {
1306 $D('noVNC_clipboard_text').value
= "";
1307 UI
.rfb
.clipboardPasteFrom("");
1310 clipSend: function() {
1311 var text
= $D('noVNC_clipboard_text').value
;
1312 Util
.Debug(">> UI.clipSend: " + text
.substr(0,40) + "...");
1313 UI
.rfb
.clipboardPasteFrom(text
);
1314 Util
.Debug("<< UI.clipSend");
1318 // Enable/disable and configure viewport clipping
1319 setViewClip: function(clip
) {
1320 var display
, cur_clip
, pos
, new_w
, new_h
;
1323 display
= UI
.rfb
.get_display();
1328 cur_clip
= display
.get_viewport();
1330 if (typeof(clip
) !== 'boolean') {
1331 // Use current setting
1332 clip
= UI
.getSetting('clip');
1335 if (clip
&& !cur_clip
) {
1337 UI
.updateSetting('clip', true);
1338 } else if (!clip
&& cur_clip
) {
1339 // Turn clipping off
1340 UI
.updateSetting('clip', false);
1341 display
.set_viewport(false);
1342 $D('noVNC_canvas').style
.position
= 'static';
1343 display
.viewportChange();
1345 if (UI
.getSetting('clip')) {
1346 // If clipping, update clipping settings
1347 $D('noVNC_canvas').style
.position
= 'absolute';
1348 pos
= Util
.getPosition($D('noVNC_canvas'));
1349 new_w
= window
.innerWidth
- pos
.x
;
1350 new_h
= window
.innerHeight
- pos
.y
;
1351 display
.set_viewport(true);
1352 display
.viewportChange(0, 0, new_w
, new_h
);
1356 // Toggle/set/unset the viewport drag/move button
1357 setViewDrag: function(drag
) {
1358 var vmb
= $D('noVNC_view_drag_button');
1359 if (!UI
.rfb
) { return; }
1361 if (UI
.rfb_state
=== 'normal' &&
1362 UI
.rfb
.get_display().get_viewport()) {
1363 vmb
.style
.display
= "inline";
1365 vmb
.style
.display
= "none";
1368 if (typeof(drag
) === "undefined" ||
1369 typeof(drag
) === "object") {
1370 // If not specified, then toggle
1371 drag
= !UI
.rfb
.get_viewportDrag();
1374 vmb
.className
= "noVNC_status_button_selected";
1375 UI
.rfb
.set_viewportDrag(true);
1377 vmb
.className
= "noVNC_status_button";
1378 UI
.rfb
.set_viewportDrag(false);
1382 // On touch devices, show the OS keyboard
1383 showKeyboard: function() {
1385 kbi
= $D('keyboardinput');
1386 skb
= $D('showKeyboard');
1387 l
= kbi
.value
.length
;
1388 if(UI
.keyboardVisible
=== false) {
1390 try { kbi
.setSelectionRange(l
, l
); } // Move the caret to the end
1391 catch (err
) {} // setSelectionRange is undefined in Google Chrome
1392 UI
.keyboardVisible
= true;
1393 skb
.className
= "noVNC_status_button_selected";
1394 } else if(UI
.keyboardVisible
=== true) {
1396 skb
.className
= "noVNC_status_button";
1397 UI
.keyboardVisible
= false;
1401 keepKeyboard: function() {
1402 clearTimeout(UI
.hideKeyboardTimeout
);
1403 if(UI
.keyboardVisible
=== true) {
1404 $D('keyboardinput').focus();
1405 $D('showKeyboard').className
= "noVNC_status_button_selected";
1406 } else if(UI
.keyboardVisible
=== false) {
1407 $D('keyboardinput').blur();
1408 $D('showKeyboard').className
= "noVNC_status_button";
1412 keyboardinputReset: function() {
1413 var kbi
= $D('keyboardinput');
1414 kbi
.value
= Array(UI
.defaultKeyboardinputLen
).join("_");
1415 UI
.lastKeyboardinput
= kbi
.value
;
1418 // When normal keyboard events are left uncought, use the input events from
1419 // the keyboardinput element instead and generate the corresponding key events.
1420 // This code is required since some browsers on Android are inconsistent in
1421 // sending keyCodes in the normal keyboard events when using on screen keyboards.
1422 keyInput: function(event
) {
1423 var newValue
, oldValue
, newLen
, oldLen
;
1424 newValue
= event
.target
.value
;
1425 oldValue
= UI
.lastKeyboardinput
;
1428 // Try to check caret position since whitespace at the end
1429 // will not be considered by value.length in some browsers
1430 newLen
= Math
.max(event
.target
.selectionStart
, newValue
.length
);
1432 // selectionStart is undefined in Google Chrome
1433 newLen
= newValue
.length
;
1435 oldLen
= oldValue
.length
;
1438 var inputs
= newLen
- oldLen
;
1440 backspaces
= -inputs
;
1444 // Compare the old string with the new to account for
1445 // text-corrections or other input that modify existing text
1446 for (var i
= 0; i
< Math
.min(oldLen
, newLen
); i
++) {
1447 if (newValue
.charAt(i
) != oldValue
.charAt(i
)) {
1448 inputs
= newLen
- i
;
1449 backspaces
= oldLen
- i
;
1454 // Send the key events
1455 for (var i
= 0; i
< backspaces
; i
++)
1456 UI
.rfb
.sendKey(XK_BackSpace
);
1457 for (var i
= newLen
- inputs
; i
< newLen
; i
++)
1458 UI
.rfb
.sendKey(newValue
.charCodeAt(i
));
1460 // Control the text content length in the keyboardinput element
1461 if (newLen
> 2 * UI
.defaultKeyboardinputLen
) {
1462 UI
.keyboardinputReset();
1463 } else if (newLen
< 1) {
1464 // There always have to be some text in the keyboardinput
1465 // element with which backspace can interact.
1466 UI
.keyboardinputReset();
1467 // This sometimes causes the keyboard to disappear for a second
1468 // but it is required for the android keyboard to recognize that
1469 // text has been added to the field
1470 event
.target
.blur();
1471 // This has to be ran outside of the input handler in order to work
1472 setTimeout(function() { UI
.keepKeyboard(); }, 0);
1475 UI
.lastKeyboardinput
= newValue
;
1479 keyInputBlur: function() {
1480 $D('showKeyboard').className
= "noVNC_status_button";
1481 //Weird bug in iOS if you change keyboardVisible
1482 //here it does not actually occur so next time
1483 //you click keyboard icon it doesnt work.
1484 UI
.hideKeyboardTimeout
= setTimeout(function() { UI
.setKeyboard(); },100);
1487 showExtraKeys: function() {
1489 if(UI
.extraKeysVisible
=== false) {
1490 $D('toggleCtrlButton').style
.display
= "inline";
1491 $D('toggleAltButton').style
.display
= "inline";
1492 $D('sendTabButton').style
.display
= "inline";
1493 $D('sendEscButton').style
.display
= "inline";
1494 $D('sendCtrlAltDelButton').style
.display
= (UI
.consoletype
=== 'kvm') ? "inline" : "none";
1495 $D('showExtraKeysButton').className
= "noVNC_status_button_selected";
1496 UI
.extraKeysVisible
= true;
1497 } else if(UI
.extraKeysVisible
=== true) {
1498 $D('toggleCtrlButton').style
.display
= "";
1499 $D('toggleAltButton').style
.display
= "";
1500 $D('sendTabButton').style
.display
= "";
1501 $D('sendEscButton').style
.display
= "";
1502 $D('sendCtrlAltDelButton').style
.display
= "";
1503 $D('showExtraKeysButton').className
= "noVNC_status_button";
1504 UI
.extraKeysVisible
= false;
1508 toggleCtrl: function() {
1510 if(UI
.ctrlOn
=== false) {
1511 UI
.rfb
.sendKey(XK_Control_L
, true);
1512 $D('toggleCtrlButton').className
= "noVNC_status_button_selected";
1514 } else if(UI
.ctrlOn
=== true) {
1515 UI
.rfb
.sendKey(XK_Control_L
, false);
1516 $D('toggleCtrlButton').className
= "noVNC_status_button";
1521 toggleAlt: function() {
1523 if(UI
.altOn
=== false) {
1524 UI
.rfb
.sendKey(XK_Alt_L
, true);
1525 $D('toggleAltButton').className
= "noVNC_status_button_selected";
1527 } else if(UI
.altOn
=== true) {
1528 UI
.rfb
.sendKey(XK_Alt_L
, false);
1529 $D('toggleAltButton').className
= "noVNC_status_button";
1534 sendTab: function() {
1536 UI
.rfb
.sendKey(XK_Tab
);
1539 sendEsc: function() {
1541 UI
.rfb
.sendKey(XK_Escape
);
1544 setKeyboard: function() {
1545 UI
.keyboardVisible
= false;
1548 // iOS < Version 5 does not support position fixed. Javascript workaround:
1549 setOnscroll: function() {
1550 window
.onscroll = function() {
1551 UI
.setBarPosition();
1555 setResize: function () {
1556 window
.onResize = function() {
1557 UI
.setBarPosition();
1561 //Helper to add options to dropdown.
1562 addOption: function(selectbox
,text
,value
)
1564 var optn
= document
.createElement("OPTION");
1567 selectbox
.options
.add(optn
);
1570 setBarPosition: function() {
1571 $D('noVNC-control-bar').style
.top
= (window
.pageYOffset
) + 'px';
1572 $D('noVNC_mobile_buttons').style
.left
= (window
.pageXOffset
) + 'px';
1574 var vncwidth
= $D('noVNC_screen').style
.offsetWidth
;
1575 $D('noVNC-control-bar').style
.width
= vncwidth
+ 'px';