]>
git.proxmox.com Git - mirror_novnc.git/blob - include/ui.js
2 * noVNC: HTML5 VNC client
3 * Copyright (C) 2012 Joel Martin
4 * Licensed under MPL 2.0 (see LICENSE.txt)
6 * See README.md for usage and integration instructions.
10 /*jslint white: false, browser: true */
11 /*global window, $D, Util, WebUtil, RFB, Display */
13 // Load supporting scripts
14 window
.onscriptsload = function () { UI
.load(); };
15 Util
.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
16 "input.js", "display.js", "jsunzip.js", "rfb.js"]);
22 connSettingsOpen
: false,
24 keyboardVisible
: false,
26 // Setup rfb object, load settings from browser storage, then call
27 // UI.init to setup the UI/menus
28 load: function (callback
) {
29 WebUtil
.initSettings(UI
.start
, callback
);
32 // Render default UI and initialize settings menu
33 start: function(callback
) {
34 var html
= '', i
, sheet
, sheets
, llevels
;
36 // Stylesheet selection dropdown
37 sheet
= WebUtil
.selectStylesheet();
38 sheets
= WebUtil
.getStylesheets();
39 for (i
= 0; i
< sheets
.length
; i
+= 1) {
40 UI
.addOption($D('noVNC_stylesheet'),sheets
[i
].title
, sheets
[i
].title
);
43 // Logging selection dropdown
44 llevels
= ['error', 'warn', 'info', 'debug'];
45 for (i
= 0; i
< llevels
.length
; i
+= 1) {
46 UI
.addOption($D('noVNC_logging'),llevels
[i
], llevels
[i
]);
49 // Settings with immediate effects
50 UI
.initSetting('logging', 'warn');
51 WebUtil
.init_logging(UI
.getSetting('logging'));
53 UI
.initSetting('stylesheet', 'default');
54 WebUtil
.selectStylesheet(null);
55 // call twice to get around webkit bug
56 WebUtil
.selectStylesheet(UI
.getSetting('stylesheet'));
58 /* Populate the controls if defaults are provided in the URL */
59 UI
.initSetting('host', window
.location
.hostname
);
60 UI
.initSetting('port', window
.location
.port
);
61 UI
.initSetting('password', '');
62 UI
.initSetting('encrypt', (window
.location
.protocol
=== "https:"));
63 UI
.initSetting('true_color', true);
64 UI
.initSetting('cursor', false);
65 UI
.initSetting('shared', true);
66 UI
.initSetting('view_only', false);
67 UI
.initSetting('connectTimeout', 2);
68 UI
.initSetting('path', 'websockify');
69 UI
.initSetting('repeaterID', '');
71 UI
.rfb
= RFB({'target': $D('noVNC_canvas'),
72 'onUpdateState': UI
.updateState
,
73 'onClipboard': UI
.clipReceive
});
74 UI
.updateVisualState();
76 // Unfocus clipboard when over the VNC area
77 //$D('VNC_screen').onmousemove = function () {
78 // var keyboard = UI.rfb.get_keyboard();
79 // if ((! keyboard) || (! keyboard.get_focused())) {
80 // $D('VNC_clipboard_text').blur();
84 // Show mouse selector buttons on touch screen devices
85 if ('ontouchstart' in document
.documentElement
) {
86 // Show mobile buttons
87 $D('noVNC_mobile_buttons').style
.display
= "inline";
89 // Remove the address bar
90 setTimeout(function() { window
.scrollTo(0, 1); }, 100);
91 UI
.forceSetting('clip', true);
92 $D('noVNC_clip').disabled
= true;
94 UI
.initSetting('clip', false);
97 //iOS Safari does not support CSS position:fixed.
98 //This detects iOS devices and enables javascript workaround.
99 if ((navigator
.userAgent
.match(/iPhone/i)) ||
100 (navigator
.userAgent
.match(/iPod/i)) ||
101 (navigator
.userAgent
.match(/iPad/i))) {
107 $D('noVNC_host').focus();
110 Util
.addEvent(window
, 'resize', UI
.setViewClip
);
112 Util
.addEvent(window
, 'beforeunload', function () {
113 if (UI
.rfb_state
=== 'normal') {
114 return "You are currently connected.";
118 // Show description by default when hosted at for kanaka.github.com
119 if (location
.host
=== "kanaka.github.com") {
120 // Open the description dialog
121 $D('noVNC_description').style
.display
= "block";
123 // Open the connect panel on first load
124 UI
.toggleConnectPanel();
127 // Add mouse event click/focus/blur event handlers to the UI
128 UI
.addMouseHandlers();
130 if (typeof callback
=== "function") {
135 addMouseHandlers: function() {
136 // Setup interface handlers that can't be inline
137 $D("noVNC_view_drag_button").onclick
= UI
.setViewDrag
;
138 $D("noVNC_mouse_button0").onclick = function () { UI
.setMouseButton(1); };
139 $D("noVNC_mouse_button1").onclick = function () { UI
.setMouseButton(2); };
140 $D("noVNC_mouse_button2").onclick = function () { UI
.setMouseButton(4); };
141 $D("noVNC_mouse_button4").onclick = function () { UI
.setMouseButton(0); };
142 $D("showKeyboard").onclick
= UI
.showKeyboard
;
143 //$D("keyboardinput").onkeydown = function (event) { onKeyDown(event); };
144 $D("keyboardinput").onblur
= UI
.keyInputBlur
;
146 $D("sendCtrlAltDelButton").onclick
= UI
.sendCtrlAltDel
;
147 $D("clipboardButton").onclick
= UI
.toggleClipboardPanel
;
148 $D("settingsButton").onclick
= UI
.toggleSettingsPanel
;
149 $D("connectButton").onclick
= UI
.toggleConnectPanel
;
150 $D("disconnectButton").onclick
= UI
.disconnect
;
151 $D("descriptionButton").onclick
= UI
.toggleConnectPanel
;
153 $D("noVNC_clipboard_text").onfocus
= UI
.displayBlur
;
154 $D("noVNC_clipboard_text").onblur
= UI
.displayFocus
;
155 $D("noVNC_clipboard_text").onchange
= UI
.clipSend
;
156 $D("noVNC_clipboard_clear_button").onclick
= UI
.clipClear
;
158 $D("noVNC_settings_menu").onmouseover
= UI
.displayBlur
;
159 $D("noVNC_settings_menu").onmouseover
= UI
.displayFocus
;
160 $D("noVNC_apply").onclick
= UI
.settingsApply
;
162 $D("noVNC_connect_button").onclick
= UI
.connect
;
165 // Read form control compatible setting from cookie
166 getSetting: function(name
) {
167 var val
, ctrl
= $D('noVNC_' + name
);
168 val
= WebUtil
.readSetting(name
);
169 if (val
!== null && ctrl
.type
=== 'checkbox') {
170 if (val
.toString().toLowerCase() in {'0':1, 'no':1, 'false':1}) {
179 // Update cookie and form control setting. If value is not set, then
180 // updates from control to current cookie setting.
181 updateSetting: function(name
, value
) {
183 var i
, ctrl
= $D('noVNC_' + name
);
184 // Save the cookie for this session
185 if (typeof value
!== 'undefined') {
186 WebUtil
.writeSetting(name
, value
);
189 // Update the settings control
190 value
= UI
.getSetting(name
);
192 if (ctrl
.type
=== 'checkbox') {
193 ctrl
.checked
= value
;
195 } else if (typeof ctrl
.options
!== 'undefined') {
196 for (i
= 0; i
< ctrl
.options
.length
; i
+= 1) {
197 if (ctrl
.options
[i
].value
=== value
) {
198 ctrl
.selectedIndex
= i
;
203 /*Weird IE9 error leads to 'null' appearring
204 in textboxes instead of ''.*/
205 if (value
=== null) {
212 // Save control setting to cookie
213 saveSetting: function(name
) {
214 var val
, ctrl
= $D('noVNC_' + name
);
215 if (ctrl
.type
=== 'checkbox') {
217 } else if (typeof ctrl
.options
!== 'undefined') {
218 val
= ctrl
.options
[ctrl
.selectedIndex
].value
;
222 WebUtil
.writeSetting(name
, val
);
223 //Util.Debug("Setting saved '" + name + "=" + val + "'");
227 // Initial page load read/initialization of settings
228 initSetting: function(name
, defVal
) {
231 // Check Query string followed by cookie
232 val
= WebUtil
.getQueryVar(name
);
234 val
= WebUtil
.readSetting(name
, defVal
);
236 UI
.updateSetting(name
, val
);
237 //Util.Debug("Setting '" + name + "' initialized to '" + val + "'");
241 // Force a setting to be a certain value
242 forceSetting: function(name
, val
) {
243 UI
.updateSetting(name
, val
);
248 // Show the clipboard panel
249 toggleClipboardPanel: function() {
250 // Close the description panel
251 $D('noVNC_description').style
.display
= "none";
252 //Close settings if open
253 if (UI
.settingsOpen
=== true) {
255 UI
.closeSettingsMenu();
257 //Close connection settings if open
258 if (UI
.connSettingsOpen
=== true) {
259 UI
.toggleConnectPanel();
261 //Toggle Clipboard Panel
262 if (UI
.clipboardOpen
=== true) {
263 $D('noVNC_clipboard').style
.display
= "none";
264 $D('clipboardButton').className
= "noVNC_status_button";
265 UI
.clipboardOpen
= false;
267 $D('noVNC_clipboard').style
.display
= "block";
268 $D('clipboardButton').className
= "noVNC_status_button_selected";
269 UI
.clipboardOpen
= true;
273 // Show the connection settings panel/menu
274 toggleConnectPanel: function() {
275 // Close the description panel
276 $D('noVNC_description').style
.display
= "none";
277 //Close connection settings if open
278 if (UI
.settingsOpen
=== true) {
280 UI
.closeSettingsMenu();
281 $D('connectButton').className
= "noVNC_status_button";
283 if (UI
.clipboardOpen
=== true) {
284 UI
.toggleClipboardPanel();
287 //Toggle Connection Panel
288 if (UI
.connSettingsOpen
=== true) {
289 $D('noVNC_controls').style
.display
= "none";
290 $D('connectButton').className
= "noVNC_status_button";
291 UI
.connSettingsOpen
= false;
292 UI
.saveSetting('host');
293 UI
.saveSetting('port');
294 //UI.saveSetting('password');
296 $D('noVNC_controls').style
.display
= "block";
297 $D('connectButton').className
= "noVNC_status_button_selected";
298 UI
.connSettingsOpen
= true;
299 $D('noVNC_host').focus();
303 // Toggle the settings menu:
304 // On open, settings are refreshed from saved cookies.
305 // On close, settings are applied
306 toggleSettingsPanel: function() {
307 // Close the description panel
308 $D('noVNC_description').style
.display
= "none";
309 if (UI
.settingsOpen
) {
311 UI
.closeSettingsMenu();
313 UI
.updateSetting('encrypt');
314 UI
.updateSetting('true_color');
315 if (UI
.rfb
.get_display().get_cursor_uri()) {
316 UI
.updateSetting('cursor');
318 UI
.updateSetting('cursor', false);
319 $D('noVNC_cursor').disabled
= true;
321 UI
.updateSetting('clip');
322 UI
.updateSetting('shared');
323 UI
.updateSetting('view_only');
324 UI
.updateSetting('connectTimeout');
325 UI
.updateSetting('path');
326 UI
.updateSetting('repeaterID');
327 UI
.updateSetting('stylesheet');
328 UI
.updateSetting('logging');
330 UI
.openSettingsMenu();
335 openSettingsMenu: function() {
336 // Close the description panel
337 $D('noVNC_description').style
.display
= "none";
338 if (UI
.clipboardOpen
=== true) {
339 UI
.toggleClipboardPanel();
341 //Close connection settings if open
342 if (UI
.connSettingsOpen
=== true) {
343 UI
.toggleConnectPanel();
345 $D('noVNC_settings').style
.display
= "block";
346 $D('settingsButton').className
= "noVNC_status_button_selected";
347 UI
.settingsOpen
= true;
350 // Close menu (without applying settings)
351 closeSettingsMenu: function() {
352 $D('noVNC_settings').style
.display
= "none";
353 $D('settingsButton').className
= "noVNC_status_button";
354 UI
.settingsOpen
= false;
357 // Save/apply settings when 'Apply' button is pressed
358 settingsApply: function() {
359 //Util.Debug(">> settingsApply");
360 UI
.saveSetting('encrypt');
361 UI
.saveSetting('true_color');
362 if (UI
.rfb
.get_display().get_cursor_uri()) {
363 UI
.saveSetting('cursor');
365 UI
.saveSetting('clip');
366 UI
.saveSetting('shared');
367 UI
.saveSetting('view_only');
368 UI
.saveSetting('connectTimeout');
369 UI
.saveSetting('path');
370 UI
.saveSetting('repeaterID');
371 UI
.saveSetting('stylesheet');
372 UI
.saveSetting('logging');
374 // Settings with immediate (non-connected related) effect
375 WebUtil
.selectStylesheet(UI
.getSetting('stylesheet'));
376 WebUtil
.init_logging(UI
.getSetting('logging'));
378 UI
.setViewDrag(UI
.rfb
.get_viewportDrag());
379 //Util.Debug("<< settingsApply");
384 setPassword: function() {
385 UI
.rfb
.sendPassword($D('noVNC_password').value
);
386 //Reset connect button.
387 $D('noVNC_connect_button').value
= "Connect";
388 $D('noVNC_connect_button').onclick
= UI
.Connect
;
389 //Hide connection panel.
390 UI
.toggleConnectPanel();
394 sendCtrlAltDel: function() {
395 UI
.rfb
.sendCtrlAltDel();
398 setMouseButton: function(num
) {
399 var b
, blist
= [0, 1,2,4], button
;
401 if (typeof num
=== 'undefined') {
402 // Disable mouse buttons
406 UI
.rfb
.get_mouse().set_touchButton(num
);
409 for (b
= 0; b
< blist
.length
; b
++) {
410 button
= $D('noVNC_mouse_button' + blist
[b
]);
411 if (blist
[b
] === num
) {
412 button
.style
.display
= "";
414 button
.style
.display
= "none";
416 button.style.backgroundColor = "black";
417 button.style.color = "lightgray";
418 button.style.backgroundColor = "";
419 button.style.color = "";
425 updateState: function(rfb
, state
, oldstate
, msg
) {
426 var s
, sb
, c
, d
, cad
, vd
, klass
;
427 UI
.rfb_state
= state
;
428 s
= $D('noVNC_status');
429 sb
= $D('noVNC_status_bar');
433 klass
= "noVNC_status_error";
436 klass
= "noVNC_status_normal";
439 $D('noVNC_logo').style
.display
= "block";
442 klass
= "noVNC_status_normal";
445 UI
.toggleConnectPanel();
447 $D('noVNC_connect_button').value
= "Send Password";
448 $D('noVNC_connect_button').onclick
= UI
.setPassword
;
449 $D('noVNC_password').focus();
451 klass
= "noVNC_status_warn";
454 klass
= "noVNC_status_warn";
458 if (typeof(msg
) !== 'undefined') {
459 s
.setAttribute("class", klass
);
460 sb
.setAttribute("class", klass
);
464 UI
.updateVisualState();
467 // Disable/enable controls depending on connection state
468 updateVisualState: function() {
469 var connected
= UI
.rfb_state
=== 'normal' ? true : false;
471 //Util.Debug(">> updateVisualState");
472 $D('noVNC_encrypt').disabled
= connected
;
473 $D('noVNC_true_color').disabled
= connected
;
474 if (UI
.rfb
&& UI
.rfb
.get_display() &&
475 UI
.rfb
.get_display().get_cursor_uri()) {
476 $D('noVNC_cursor').disabled
= connected
;
478 UI
.updateSetting('cursor', false);
479 $D('noVNC_cursor').disabled
= true;
481 $D('noVNC_shared').disabled
= connected
;
482 $D('noVNC_view_only').disabled
= connected
;
483 $D('noVNC_connectTimeout').disabled
= connected
;
484 $D('noVNC_path').disabled
= connected
;
485 $D('noVNC_repeaterID').disabled
= connected
;
489 UI
.setMouseButton(1);
490 $D('clipboardButton').style
.display
= "inline";
491 $D('showKeyboard').style
.display
= "inline";
492 $D('sendCtrlAltDelButton').style
.display
= "inline";
495 $D('clipboardButton').style
.display
= "none";
496 $D('showKeyboard').style
.display
= "none";
497 $D('sendCtrlAltDelButton').style
.display
= "none";
499 // State change disables viewport dragging.
500 // It is enabled (toggled) by direct click on the button
501 UI
.setViewDrag(false);
503 switch (UI
.rfb_state
) {
508 $D('connectButton').style
.display
= "";
509 $D('disconnectButton').style
.display
= "none";
512 $D('connectButton').style
.display
= "none";
513 $D('disconnectButton').style
.display
= "";
517 //Util.Debug("<< updateVisualState");
521 clipReceive: function(rfb
, text
) {
522 Util
.Debug(">> UI.clipReceive: " + text
.substr(0,40) + "...");
523 $D('noVNC_clipboard_text').value
= text
;
524 Util
.Debug("<< UI.clipReceive");
528 connect: function() {
529 var host
, port
, password
, path
;
531 UI
.closeSettingsMenu();
532 UI
.toggleConnectPanel();
534 host
= $D('noVNC_host').value
;
535 port
= $D('noVNC_port').value
;
536 password
= $D('noVNC_password').value
;
537 path
= $D('noVNC_path').value
;
538 if ((!host
) || (!port
)) {
539 throw("Must set host and port");
542 UI
.rfb
.set_encrypt(UI
.getSetting('encrypt'));
543 UI
.rfb
.set_true_color(UI
.getSetting('true_color'));
544 UI
.rfb
.set_local_cursor(UI
.getSetting('cursor'));
545 UI
.rfb
.set_shared(UI
.getSetting('shared'));
546 UI
.rfb
.set_view_only(UI
.getSetting('view_only'));
547 UI
.rfb
.set_connectTimeout(UI
.getSetting('connectTimeout'));
548 UI
.rfb
.set_repeaterID(UI
.getSetting('repeaterID'));
550 UI
.rfb
.connect(host
, port
, password
, path
);
553 setTimeout(UI
.setBarPosition
, 100);
554 $D('noVNC_logo').style
.display
= "none";
557 disconnect: function() {
558 UI
.closeSettingsMenu();
561 $D('noVNC_logo').style
.display
= "block";
562 UI
.connSettingsOpen
= false;
563 UI
.toggleConnectPanel();
566 displayBlur: function() {
567 UI
.rfb
.get_keyboard().set_focused(false);
568 UI
.rfb
.get_mouse().set_focused(false);
571 displayFocus: function() {
572 UI
.rfb
.get_keyboard().set_focused(true);
573 UI
.rfb
.get_mouse().set_focused(true);
576 clipClear: function() {
577 $D('noVNC_clipboard_text').value
= "";
578 UI
.rfb
.clipboardPasteFrom("");
581 clipSend: function() {
582 var text
= $D('noVNC_clipboard_text').value
;
583 Util
.Debug(">> UI.clipSend: " + text
.substr(0,40) + "...");
584 UI
.rfb
.clipboardPasteFrom(text
);
585 Util
.Debug("<< UI.clipSend");
589 // Enable/disable and configure viewport clipping
590 setViewClip: function(clip
) {
591 var display
, cur_clip
, pos
, new_w
, new_h
;
594 display
= UI
.rfb
.get_display();
599 cur_clip
= display
.get_viewport();
601 if (typeof(clip
) !== 'boolean') {
602 // Use current setting
603 clip
= UI
.getSetting('clip');
606 if (clip
&& !cur_clip
) {
608 UI
.updateSetting('clip', true);
609 } else if (!clip
&& cur_clip
) {
611 UI
.updateSetting('clip', false);
612 display
.set_viewport(false);
613 $D('noVNC_canvas').style
.position
= 'static';
614 display
.viewportChange();
616 if (UI
.getSetting('clip')) {
617 // If clipping, update clipping settings
618 $D('noVNC_canvas').style
.position
= 'absolute';
619 pos
= Util
.getPosition($D('noVNC_canvas'));
620 new_w
= window
.innerWidth
- pos
.x
;
621 new_h
= window
.innerHeight
- pos
.y
;
622 display
.set_viewport(true);
623 display
.viewportChange(0, 0, new_w
, new_h
);
627 // Toggle/set/unset the viewport drag/move button
628 setViewDrag: function(drag
) {
629 var vmb
= $D('noVNC_view_drag_button');
630 if (!UI
.rfb
) { return; }
632 if (UI
.rfb_state
=== 'normal' &&
633 UI
.rfb
.get_display().get_viewport()) {
634 vmb
.style
.display
= "inline";
636 vmb
.style
.display
= "none";
639 if (typeof(drag
) === "undefined" ||
640 typeof(drag
) === "object") {
641 // If not specified, then toggle
642 drag
= !UI
.rfb
.get_viewportDrag();
645 vmb
.className
= "noVNC_status_button_selected";
646 UI
.rfb
.set_viewportDrag(true);
648 vmb
.className
= "noVNC_status_button";
649 UI
.rfb
.set_viewportDrag(false);
653 // On touch devices, show the OS keyboard
654 showKeyboard: function() {
655 if(UI
.keyboardVisible
=== false) {
656 $D('keyboardinput').focus();
657 UI
.keyboardVisible
= true;
658 $D('showKeyboard').className
= "noVNC_status_button_selected";
659 } else if(UI
.keyboardVisible
=== true) {
660 $D('keyboardinput').blur();
661 $D('showKeyboard').className
= "noVNC_status_button";
662 UI
.keyboardVisible
= false;
666 keyInputBlur: function() {
667 $D('showKeyboard').className
= "noVNC_status_button";
668 //Weird bug in iOS if you change keyboardVisible
669 //here it does not actually occur so next time
670 //you click keyboard icon it doesnt work.
671 setTimeout(function() { UI
.setKeyboard(); },100);
674 setKeyboard: function() {
675 UI
.keyboardVisible
= false;
678 // iOS < Version 5 does not support position fixed. Javascript workaround:
679 setOnscroll: function() {
680 window
.onscroll = function() {
685 setResize: function () {
686 window
.onResize = function() {
691 //Helper to add options to dropdown.
692 addOption: function(selectbox
,text
,value
)
694 var optn
= document
.createElement("OPTION");
697 selectbox
.options
.add(optn
);
700 setBarPosition: function() {
701 $D('noVNC-control-bar').style
.top
= (window
.pageYOffset
) + 'px';
702 $D('noVNC_mobile_buttons').style
.left
= (window
.pageXOffset
) + 'px';
704 var vncwidth
= $D('noVNC_screen').style
.offsetWidth
;
705 $D('noVNC-control-bar').style
.width
= vncwidth
+ 'px';