]>
git.proxmox.com Git - mirror_novnc.git/blob - include/ui.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 Util
.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
17 "input.js", "display.js", "jsunzip.js", "rfb.js"]);
23 connSettingsOpen
: false,
24 popupStatusOpen
: false,
26 keyboardVisible
: false,
28 // Setup rfb object, load settings from browser storage, then call
29 // UI.init to setup the UI/menus
30 load: function (callback
) {
31 WebUtil
.initSettings(UI
.start
, callback
);
34 // Render default UI and initialize settings menu
35 start: function(callback
) {
36 var html
= '', i
, sheet
, sheets
, llevels
, port
;
38 // Stylesheet selection dropdown
39 sheet
= WebUtil
.selectStylesheet();
40 sheets
= WebUtil
.getStylesheets();
41 for (i
= 0; i
< sheets
.length
; i
+= 1) {
42 UI
.addOption($D('noVNC_stylesheet'),sheets
[i
].title
, sheets
[i
].title
);
45 // Logging selection dropdown
46 llevels
= ['error', 'warn', 'info', 'debug'];
47 for (i
= 0; i
< llevels
.length
; i
+= 1) {
48 UI
.addOption($D('noVNC_logging'),llevels
[i
], llevels
[i
]);
51 // Settings with immediate effects
52 UI
.initSetting('logging', 'warn');
53 WebUtil
.init_logging(UI
.getSetting('logging'));
55 UI
.initSetting('stylesheet', 'default');
56 WebUtil
.selectStylesheet(null);
57 // call twice to get around webkit bug
58 WebUtil
.selectStylesheet(UI
.getSetting('stylesheet'));
60 // if port == 80 (or 443) then it won't be present and should be
62 port
= window
.location
.port
;
64 if (window
.location
.protocol
.substring(0,5) == 'https') {
67 else if (window
.location
.protocol
.substring(0,4) == 'http') {
72 /* Populate the controls if defaults are provided in the URL */
73 UI
.initSetting('host', window
.location
.hostname
);
74 UI
.initSetting('port', port
);
75 UI
.initSetting('password', '');
76 UI
.initSetting('encrypt', (window
.location
.protocol
=== "https:"));
77 UI
.initSetting('true_color', true);
78 UI
.initSetting('cursor', false);
79 UI
.initSetting('shared', true);
80 UI
.initSetting('view_only', false);
81 UI
.initSetting('connectTimeout', 2);
82 UI
.initSetting('path', 'websockify');
83 UI
.initSetting('repeaterID', '');
85 UI
.rfb
= RFB({'target': $D('noVNC_canvas'),
86 'onUpdateState': UI
.updateState
,
87 'onClipboard': UI
.clipReceive
,
88 'onDesktopName': UI
.updateDocumentTitle
});
89 UI
.updateVisualState();
91 // Unfocus clipboard when over the VNC area
92 //$D('VNC_screen').onmousemove = function () {
93 // var keyboard = UI.rfb.get_keyboard();
94 // if ((! keyboard) || (! keyboard.get_focused())) {
95 // $D('VNC_clipboard_text').blur();
99 // Show mouse selector buttons on touch screen devices
100 if ('ontouchstart' in document
.documentElement
) {
101 // Show mobile buttons
102 $D('noVNC_mobile_buttons').style
.display
= "inline";
104 // Remove the address bar
105 setTimeout(function() { window
.scrollTo(0, 1); }, 100);
106 UI
.forceSetting('clip', true);
107 $D('noVNC_clip').disabled
= true;
109 UI
.initSetting('clip', false);
112 //iOS Safari does not support CSS position:fixed.
113 //This detects iOS devices and enables javascript workaround.
114 if ((navigator
.userAgent
.match(/iPhone/i)) ||
115 (navigator
.userAgent
.match(/iPod/i)) ||
116 (navigator
.userAgent
.match(/iPad/i))) {
122 $D('noVNC_host').focus();
125 Util
.addEvent(window
, 'resize', UI
.setViewClip
);
127 Util
.addEvent(window
, 'beforeunload', function () {
128 if (UI
.rfb_state
=== 'normal') {
129 return "You are currently connected.";
133 // Show description by default when hosted at for kanaka.github.com
134 if (location
.host
=== "kanaka.github.com") {
135 // Open the description dialog
136 $D('noVNC_description').style
.display
= "block";
138 // Open the connect panel on first load
139 UI
.toggleConnectPanel();
142 // Add mouse event click/focus/blur event handlers to the UI
143 UI
.addMouseHandlers();
145 if (typeof callback
=== "function") {
150 addMouseHandlers: function() {
151 // Setup interface handlers that can't be inline
152 $D("noVNC_view_drag_button").onclick
= UI
.setViewDrag
;
153 $D("noVNC_mouse_button0").onclick = function () { UI
.setMouseButton(1); };
154 $D("noVNC_mouse_button1").onclick = function () { UI
.setMouseButton(2); };
155 $D("noVNC_mouse_button2").onclick = function () { UI
.setMouseButton(4); };
156 $D("noVNC_mouse_button4").onclick = function () { UI
.setMouseButton(0); };
157 $D("showKeyboard").onclick
= UI
.showKeyboard
;
158 //$D("keyboardinput").onkeydown = function (event) { onKeyDown(event); };
159 $D("keyboardinput").onblur
= UI
.keyInputBlur
;
161 $D("sendCtrlAltDelButton").onclick
= UI
.sendCtrlAltDel
;
162 $D("noVNC_status").onclick
= UI
.togglePopupStatusPanel
;
163 $D("noVNC_popup_status_panel").onclick
= UI
.togglePopupStatusPanel
;
164 $D("clipboardButton").onclick
= UI
.toggleClipboardPanel
;
165 $D("settingsButton").onclick
= UI
.toggleSettingsPanel
;
166 $D("connectButton").onclick
= UI
.toggleConnectPanel
;
167 $D("disconnectButton").onclick
= UI
.disconnect
;
168 $D("descriptionButton").onclick
= UI
.toggleConnectPanel
;
170 $D("noVNC_clipboard_text").onfocus
= UI
.displayBlur
;
171 $D("noVNC_clipboard_text").onblur
= UI
.displayFocus
;
172 $D("noVNC_clipboard_text").onchange
= UI
.clipSend
;
173 $D("noVNC_clipboard_clear_button").onclick
= UI
.clipClear
;
175 $D("noVNC_settings_menu").onmouseover
= UI
.displayBlur
;
176 $D("noVNC_settings_menu").onmouseover
= UI
.displayFocus
;
177 $D("noVNC_apply").onclick
= UI
.settingsApply
;
179 $D("noVNC_connect_button").onclick
= UI
.connect
;
182 // Read form control compatible setting from cookie
183 getSetting: function(name
) {
184 var val
, ctrl
= $D('noVNC_' + name
);
185 val
= WebUtil
.readSetting(name
);
186 if (val
!== null && ctrl
.type
=== 'checkbox') {
187 if (val
.toString().toLowerCase() in {'0':1, 'no':1, 'false':1}) {
196 // Update cookie and form control setting. If value is not set, then
197 // updates from control to current cookie setting.
198 updateSetting: function(name
, value
) {
200 var i
, ctrl
= $D('noVNC_' + name
);
201 // Save the cookie for this session
202 if (typeof value
!== 'undefined') {
203 WebUtil
.writeSetting(name
, value
);
206 // Update the settings control
207 value
= UI
.getSetting(name
);
209 if (ctrl
.type
=== 'checkbox') {
210 ctrl
.checked
= value
;
212 } else if (typeof ctrl
.options
!== 'undefined') {
213 for (i
= 0; i
< ctrl
.options
.length
; i
+= 1) {
214 if (ctrl
.options
[i
].value
=== value
) {
215 ctrl
.selectedIndex
= i
;
220 /*Weird IE9 error leads to 'null' appearring
221 in textboxes instead of ''.*/
222 if (value
=== null) {
229 // Save control setting to cookie
230 saveSetting: function(name
) {
231 var val
, ctrl
= $D('noVNC_' + name
);
232 if (ctrl
.type
=== 'checkbox') {
234 } else if (typeof ctrl
.options
!== 'undefined') {
235 val
= ctrl
.options
[ctrl
.selectedIndex
].value
;
239 WebUtil
.writeSetting(name
, val
);
240 //Util.Debug("Setting saved '" + name + "=" + val + "'");
244 // Initial page load read/initialization of settings
245 initSetting: function(name
, defVal
) {
248 // Check Query string followed by cookie
249 val
= WebUtil
.getQueryVar(name
);
251 val
= WebUtil
.readSetting(name
, defVal
);
253 UI
.updateSetting(name
, val
);
254 //Util.Debug("Setting '" + name + "' initialized to '" + val + "'");
258 // Force a setting to be a certain value
259 forceSetting: function(name
, val
) {
260 UI
.updateSetting(name
, val
);
265 // Show the popup status panel
266 togglePopupStatusPanel: function() {
267 var psp
= $D('noVNC_popup_status_panel');
268 if (UI
.popupStatusOpen
=== true) {
269 psp
.style
.display
= "none";
270 UI
.popupStatusOpen
= false;
272 psp
.innerHTML
= $D('noVNC_status').innerHTML
;
273 psp
.style
.display
= "block";
274 psp
.style
.left
= window
.innerWidth
/2 -
275 parseInt(window
.getComputedStyle(psp
, false).width
)/2 -30 + "px";
276 UI
.popupStatusOpen
= true;
280 // Show the clipboard panel
281 toggleClipboardPanel: function() {
282 // Close the description panel
283 $D('noVNC_description').style
.display
= "none";
284 // Close settings if open
285 if (UI
.settingsOpen
=== true) {
287 UI
.closeSettingsMenu();
289 // Close connection settings if open
290 if (UI
.connSettingsOpen
=== true) {
291 UI
.toggleConnectPanel();
293 // Close popup status panel if open
294 if (UI
.popupStatusOpen
=== true) {
295 UI
.togglePopupStatusPanel();
297 // Toggle Clipboard Panel
298 if (UI
.clipboardOpen
=== true) {
299 $D('noVNC_clipboard').style
.display
= "none";
300 $D('clipboardButton').className
= "noVNC_status_button";
301 UI
.clipboardOpen
= false;
303 $D('noVNC_clipboard').style
.display
= "block";
304 $D('clipboardButton').className
= "noVNC_status_button_selected";
305 UI
.clipboardOpen
= true;
309 // Show the connection settings panel/menu
310 toggleConnectPanel: function() {
311 // Close the description panel
312 $D('noVNC_description').style
.display
= "none";
313 // Close connection settings if open
314 if (UI
.settingsOpen
=== true) {
316 UI
.closeSettingsMenu();
317 $D('connectButton').className
= "noVNC_status_button";
319 // Close clipboard panel if open
320 if (UI
.clipboardOpen
=== true) {
321 UI
.toggleClipboardPanel();
323 // Close popup status panel if open
324 if (UI
.popupStatusOpen
=== true) {
325 UI
.togglePopupStatusPanel();
328 // Toggle Connection Panel
329 if (UI
.connSettingsOpen
=== true) {
330 $D('noVNC_controls').style
.display
= "none";
331 $D('connectButton').className
= "noVNC_status_button";
332 UI
.connSettingsOpen
= false;
333 UI
.saveSetting('host');
334 UI
.saveSetting('port');
335 //UI.saveSetting('password');
337 $D('noVNC_controls').style
.display
= "block";
338 $D('connectButton').className
= "noVNC_status_button_selected";
339 UI
.connSettingsOpen
= true;
340 $D('noVNC_host').focus();
344 // Toggle the settings menu:
345 // On open, settings are refreshed from saved cookies.
346 // On close, settings are applied
347 toggleSettingsPanel: function() {
348 // Close the description panel
349 $D('noVNC_description').style
.display
= "none";
350 if (UI
.settingsOpen
) {
352 UI
.closeSettingsMenu();
354 UI
.updateSetting('encrypt');
355 UI
.updateSetting('true_color');
356 if (UI
.rfb
.get_display().get_cursor_uri()) {
357 UI
.updateSetting('cursor');
359 UI
.updateSetting('cursor', false);
360 $D('noVNC_cursor').disabled
= true;
362 UI
.updateSetting('clip');
363 UI
.updateSetting('shared');
364 UI
.updateSetting('view_only');
365 UI
.updateSetting('connectTimeout');
366 UI
.updateSetting('path');
367 UI
.updateSetting('repeaterID');
368 UI
.updateSetting('stylesheet');
369 UI
.updateSetting('logging');
371 UI
.openSettingsMenu();
376 openSettingsMenu: function() {
377 // Close the description panel
378 $D('noVNC_description').style
.display
= "none";
379 // Close clipboard panel if open
380 if (UI
.clipboardOpen
=== true) {
381 UI
.toggleClipboardPanel();
383 // Close connection settings if open
384 if (UI
.connSettingsOpen
=== true) {
385 UI
.toggleConnectPanel();
387 // Close popup status panel if open
388 if (UI
.popupStatusOpen
=== true) {
389 UI
.togglePopupStatusPanel();
391 $D('noVNC_settings').style
.display
= "block";
392 $D('settingsButton').className
= "noVNC_status_button_selected";
393 UI
.settingsOpen
= true;
396 // Close menu (without applying settings)
397 closeSettingsMenu: function() {
398 $D('noVNC_settings').style
.display
= "none";
399 $D('settingsButton').className
= "noVNC_status_button";
400 UI
.settingsOpen
= false;
403 // Save/apply settings when 'Apply' button is pressed
404 settingsApply: function() {
405 //Util.Debug(">> settingsApply");
406 UI
.saveSetting('encrypt');
407 UI
.saveSetting('true_color');
408 if (UI
.rfb
.get_display().get_cursor_uri()) {
409 UI
.saveSetting('cursor');
411 UI
.saveSetting('clip');
412 UI
.saveSetting('shared');
413 UI
.saveSetting('view_only');
414 UI
.saveSetting('connectTimeout');
415 UI
.saveSetting('path');
416 UI
.saveSetting('repeaterID');
417 UI
.saveSetting('stylesheet');
418 UI
.saveSetting('logging');
420 // Settings with immediate (non-connected related) effect
421 WebUtil
.selectStylesheet(UI
.getSetting('stylesheet'));
422 WebUtil
.init_logging(UI
.getSetting('logging'));
424 UI
.setViewDrag(UI
.rfb
.get_viewportDrag());
425 //Util.Debug("<< settingsApply");
430 setPassword: function() {
431 UI
.rfb
.sendPassword($D('noVNC_password').value
);
432 //Reset connect button.
433 $D('noVNC_connect_button').value
= "Connect";
434 $D('noVNC_connect_button').onclick
= UI
.Connect
;
435 //Hide connection panel.
436 UI
.toggleConnectPanel();
440 sendCtrlAltDel: function() {
441 UI
.rfb
.sendCtrlAltDel();
444 setMouseButton: function(num
) {
445 var b
, blist
= [0, 1,2,4], button
;
447 if (typeof num
=== 'undefined') {
448 // Disable mouse buttons
452 UI
.rfb
.get_mouse().set_touchButton(num
);
455 for (b
= 0; b
< blist
.length
; b
++) {
456 button
= $D('noVNC_mouse_button' + blist
[b
]);
457 if (blist
[b
] === num
) {
458 button
.style
.display
= "";
460 button
.style
.display
= "none";
462 button.style.backgroundColor = "black";
463 button.style.color = "lightgray";
464 button.style.backgroundColor = "";
465 button.style.color = "";
471 updateState: function(rfb
, state
, oldstate
, msg
) {
472 var s
, sb
, c
, d
, cad
, vd
, klass
;
473 UI
.rfb_state
= state
;
477 klass
= "noVNC_status_error";
480 klass
= "noVNC_status_normal";
483 $D('noVNC_logo').style
.display
= "block";
486 klass
= "noVNC_status_normal";
489 UI
.toggleConnectPanel();
491 $D('noVNC_connect_button').value
= "Send Password";
492 $D('noVNC_connect_button').onclick
= UI
.setPassword
;
493 $D('noVNC_password').focus();
495 klass
= "noVNC_status_warn";
498 klass
= "noVNC_status_warn";
502 if (typeof(msg
) !== 'undefined') {
503 $D('noVNC-control-bar').setAttribute("class", klass
);
504 $D('noVNC_status').innerHTML
= msg
;
507 UI
.updateVisualState();
510 // Disable/enable controls depending on connection state
511 updateVisualState: function() {
512 var connected
= UI
.rfb_state
=== 'normal' ? true : false;
514 //Util.Debug(">> updateVisualState");
515 $D('noVNC_encrypt').disabled
= connected
;
516 $D('noVNC_true_color').disabled
= connected
;
517 if (UI
.rfb
&& UI
.rfb
.get_display() &&
518 UI
.rfb
.get_display().get_cursor_uri()) {
519 $D('noVNC_cursor').disabled
= connected
;
521 UI
.updateSetting('cursor', false);
522 $D('noVNC_cursor').disabled
= true;
524 $D('noVNC_shared').disabled
= connected
;
525 $D('noVNC_view_only').disabled
= connected
;
526 $D('noVNC_connectTimeout').disabled
= connected
;
527 $D('noVNC_path').disabled
= connected
;
528 $D('noVNC_repeaterID').disabled
= connected
;
532 UI
.setMouseButton(1);
533 $D('clipboardButton').style
.display
= "inline";
534 $D('showKeyboard').style
.display
= "inline";
535 $D('sendCtrlAltDelButton').style
.display
= "inline";
538 $D('clipboardButton').style
.display
= "none";
539 $D('showKeyboard').style
.display
= "none";
540 $D('sendCtrlAltDelButton').style
.display
= "none";
542 // State change disables viewport dragging.
543 // It is enabled (toggled) by direct click on the button
544 UI
.setViewDrag(false);
546 switch (UI
.rfb_state
) {
551 $D('connectButton').style
.display
= "";
552 $D('disconnectButton').style
.display
= "none";
555 $D('connectButton').style
.display
= "none";
556 $D('disconnectButton').style
.display
= "";
560 //Util.Debug("<< updateVisualState");
564 // Display the desktop name in the document title
565 updateDocumentTitle: function(rfb
, name
) {
566 document
.title
= name
+ " - noVNC";
570 clipReceive: function(rfb
, text
) {
571 Util
.Debug(">> UI.clipReceive: " + text
.substr(0,40) + "...");
572 $D('noVNC_clipboard_text').value
= text
;
573 Util
.Debug("<< UI.clipReceive");
577 connect: function() {
578 var host
, port
, password
, path
;
580 UI
.closeSettingsMenu();
581 UI
.toggleConnectPanel();
583 host
= $D('noVNC_host').value
;
584 port
= $D('noVNC_port').value
;
585 password
= $D('noVNC_password').value
;
586 path
= $D('noVNC_path').value
;
587 if ((!host
) || (!port
)) {
588 throw("Must set host and port");
591 UI
.rfb
.set_encrypt(UI
.getSetting('encrypt'));
592 UI
.rfb
.set_true_color(UI
.getSetting('true_color'));
593 UI
.rfb
.set_local_cursor(UI
.getSetting('cursor'));
594 UI
.rfb
.set_shared(UI
.getSetting('shared'));
595 UI
.rfb
.set_view_only(UI
.getSetting('view_only'));
596 UI
.rfb
.set_connectTimeout(UI
.getSetting('connectTimeout'));
597 UI
.rfb
.set_repeaterID(UI
.getSetting('repeaterID'));
599 UI
.rfb
.connect(host
, port
, password
, path
);
602 setTimeout(UI
.setBarPosition
, 100);
603 $D('noVNC_logo').style
.display
= "none";
606 disconnect: function() {
607 UI
.closeSettingsMenu();
610 $D('noVNC_logo').style
.display
= "block";
611 UI
.connSettingsOpen
= false;
612 UI
.toggleConnectPanel();
615 displayBlur: function() {
616 UI
.rfb
.get_keyboard().set_focused(false);
617 UI
.rfb
.get_mouse().set_focused(false);
620 displayFocus: function() {
621 UI
.rfb
.get_keyboard().set_focused(true);
622 UI
.rfb
.get_mouse().set_focused(true);
625 clipClear: function() {
626 $D('noVNC_clipboard_text').value
= "";
627 UI
.rfb
.clipboardPasteFrom("");
630 clipSend: function() {
631 var text
= $D('noVNC_clipboard_text').value
;
632 Util
.Debug(">> UI.clipSend: " + text
.substr(0,40) + "...");
633 UI
.rfb
.clipboardPasteFrom(text
);
634 Util
.Debug("<< UI.clipSend");
638 // Enable/disable and configure viewport clipping
639 setViewClip: function(clip
) {
640 var display
, cur_clip
, pos
, new_w
, new_h
;
643 display
= UI
.rfb
.get_display();
648 cur_clip
= display
.get_viewport();
650 if (typeof(clip
) !== 'boolean') {
651 // Use current setting
652 clip
= UI
.getSetting('clip');
655 if (clip
&& !cur_clip
) {
657 UI
.updateSetting('clip', true);
658 } else if (!clip
&& cur_clip
) {
660 UI
.updateSetting('clip', false);
661 display
.set_viewport(false);
662 $D('noVNC_canvas').style
.position
= 'static';
663 display
.viewportChange();
665 if (UI
.getSetting('clip')) {
666 // If clipping, update clipping settings
667 $D('noVNC_canvas').style
.position
= 'absolute';
668 pos
= Util
.getPosition($D('noVNC_canvas'));
669 new_w
= window
.innerWidth
- pos
.x
;
670 new_h
= window
.innerHeight
- pos
.y
;
671 display
.set_viewport(true);
672 display
.viewportChange(0, 0, new_w
, new_h
);
676 // Toggle/set/unset the viewport drag/move button
677 setViewDrag: function(drag
) {
678 var vmb
= $D('noVNC_view_drag_button');
679 if (!UI
.rfb
) { return; }
681 if (UI
.rfb_state
=== 'normal' &&
682 UI
.rfb
.get_display().get_viewport()) {
683 vmb
.style
.display
= "inline";
685 vmb
.style
.display
= "none";
688 if (typeof(drag
) === "undefined" ||
689 typeof(drag
) === "object") {
690 // If not specified, then toggle
691 drag
= !UI
.rfb
.get_viewportDrag();
694 vmb
.className
= "noVNC_status_button_selected";
695 UI
.rfb
.set_viewportDrag(true);
697 vmb
.className
= "noVNC_status_button";
698 UI
.rfb
.set_viewportDrag(false);
702 // On touch devices, show the OS keyboard
703 showKeyboard: function() {
704 if(UI
.keyboardVisible
=== false) {
705 $D('keyboardinput').focus();
706 UI
.keyboardVisible
= true;
707 $D('showKeyboard').className
= "noVNC_status_button_selected";
708 } else if(UI
.keyboardVisible
=== true) {
709 $D('keyboardinput').blur();
710 $D('showKeyboard').className
= "noVNC_status_button";
711 UI
.keyboardVisible
= false;
715 keyInputBlur: function() {
716 $D('showKeyboard').className
= "noVNC_status_button";
717 //Weird bug in iOS if you change keyboardVisible
718 //here it does not actually occur so next time
719 //you click keyboard icon it doesnt work.
720 setTimeout(function() { UI
.setKeyboard(); },100);
723 setKeyboard: function() {
724 UI
.keyboardVisible
= false;
727 // iOS < Version 5 does not support position fixed. Javascript workaround:
728 setOnscroll: function() {
729 window
.onscroll = function() {
734 setResize: function () {
735 window
.onResize = function() {
740 //Helper to add options to dropdown.
741 addOption: function(selectbox
,text
,value
)
743 var optn
= document
.createElement("OPTION");
746 selectbox
.options
.add(optn
);
749 setBarPosition: function() {
750 $D('noVNC-control-bar').style
.top
= (window
.pageYOffset
) + 'px';
751 $D('noVNC_mobile_buttons').style
.left
= (window
.pageXOffset
) + 'px';
753 var vncwidth
= $D('noVNC_screen').style
.offsetWidth
;
754 $D('noVNC-control-bar').style
.width
= vncwidth
+ 'px';