]> git.proxmox.com Git - mirror_novnc.git/blame - include/ui.js
Helper function for viewport meta changes.
[mirror_novnc.git] / include / ui.js
CommitLineData
15046f00
JM
1/*
2 * noVNC: HTML5 VNC client
d0c29bb6 3 * Copyright (C) 2011 Joel Martin
5f409eee 4 * Licensed under LGPL-3 (see LICENSE.txt)
15046f00
JM
5 *
6 * See README.md for usage and integration instructions.
7 */
43cf7bd8 8
15046f00 9"use strict";
43cf7bd8 10/*jslint white: false, browser: true */
d890e864 11/*global window, $D, Util, WebUtil, RFB, Display */
15046f00 12
f7ec5b2c 13var UI = {
91308399 14
da6dd893
JM
15settingsOpen : false,
16
f7ec5b2c 17// Render default UI and initialize settings menu
91308399 18load: function(target) {
f7ec5b2c 19 var html = '', i, sheet, sheets, llevels;
91308399 20
f7ec5b2c 21 /* Populate the 'target' DOM element with default UI */
e4671910
JM
22 if (!target) {
23 target = $D('vnc');
24 } else if (typeof target === 'string') {
25 target = $D(target);
26 }
91308399 27
e66f3f89
JM
28 if ((!document.createElement('canvas').getContext) &&
29 window.ActiveXObject) {
30 // Suggest Chrome frame for Internet Explorer users
31 html += '<center><div style="text-align: left; width: 400px">';
32 html += ' You are using a version of Internet Explorer ';
33 html += ' that does not have HTML5 Canvas support. ';
34 html += ' To use noVNC you must use a browser with HTML5 ';
35 html += ' Canvas support or install ';
36 html += ' <a href="http://google.com/chromeframe" target="cframe">';
37 html += ' Google Chrome Frame.</a>';
38 html += '</div></center>';
e4671910 39 target.innerHTML = html;
e66f3f89
JM
40 return;
41 }
42
91308399
JM
43 html += '<div id="VNC_controls">';
44 html += ' <ul>';
45 html += ' <li>Host: <input id="VNC_host"></li>';
46 html += ' <li>Port: <input id="VNC_port"></li>';
47 html += ' <li>Password: <input id="VNC_password"';
48 html += ' type="password"></li>';
91308399
JM
49 html += ' <li><input id="VNC_connect_button" type="button"';
50 html += ' value="Loading" disabled></li>';
51 html += ' </ul>';
52 html += '</div>';
53 html += '<div id="VNC_screen">';
63708ff5
JM
54 html += ' <div id="VNC_status_bar" class="VNC_status_bar" style="margin-top: 0px;">';
55 html += ' <table border=0 width=100%><tr>';
56 html += ' <td><div id="VNC_status">Loading</div></td>';
ad3f7624
JM
57
58 // Mouse button selectors for touch devices
59 html += ' <td width=1%><div class="VNC_buttons_right">';
60 html += ' <nobr><span id="VNC_mouse_buttons" style="display: none;">';
61 html += ' <input type="button" class="VNC_status_button"';
62 html += ' id="VNC_mouse_button1" value="L" onclick="UI.setMouseButton(1);"';
63 html += ' ><input type="button" class="VNC_status_button"';
64 html += ' id="VNC_mouse_button2" value="M" onclick="UI.setMouseButton(2);"';
65 html += ' ><input type="button" class="VNC_status_button"';
66 html += ' id="VNC_mouse_button4" value="R" onclick="UI.setMouseButton(4);">';
7825b9ee 67 html += ' <input type="button" id="showKeyboard" onclick="showkeyboard()" value="Keyboard" />';
ad3f7624
JM
68 html += ' </span></nobr></div></td>';
69
70 // Settings drop-down menu
da6dd893
JM
71 html += ' <td width=1%><div class="VNC_buttons_right">';
72 html += ' <input type=button class="VNC_status_button" value="Settings"';
73 html += ' id="menuButton"';
f7ec5b2c 74 html += ' onclick="UI.clickSettingsMenu();">';
da6dd893 75 html += ' <span id="VNC_settings_menu"';
d890e864
JM
76 html += ' onmouseover="UI.displayBlur();"';
77 html += ' onmouseout="UI.displayFocus();">';
da6dd893
JM
78 html += ' <ul>';
79 html += ' <li><input id="VNC_encrypt"';
208c832b 80 html += ' type="checkbox"> Encrypt</li>';
da6dd893
JM
81 html += ' <li><input id="VNC_true_color"';
82 html += ' type="checkbox" checked> True Color</li>';
83 html += ' <li><input id="VNC_cursor"';
31a837d5 84 html += ' type="checkbox"> Local Cursor</li>';
f1a9971c
JM
85 html += ' <li><input id="VNC_shared"';
86 html += ' type="checkbox"> Shared Mode</li>';
ff36b127
JM
87 html += ' <li><input id="VNC_connectTimeout"';
88 html += ' type="input"> Connect Timeout (s)</li>';
da6dd893
JM
89 html += ' <hr>';
90
91 // Stylesheet selection dropdown
92 html += ' <li><select id="VNC_stylesheet" name="vncStyle">';
93 html += ' <option value="default">default</option>';
8d5d2c82
AM
94 sheet = WebUtil.selectStylesheet();
95 sheets = WebUtil.getStylesheets();
8db09746 96 for (i = 0; i < sheets.length; i += 1) {
da6dd893
JM
97 html += '<option value="' + sheets[i].title + '">' + sheets[i].title + '</option>';
98 }
99 html += ' </select> Style</li>';
100
101 // Logging selection dropdown
102 html += ' <li><select id="VNC_logging" name="vncLogging">';
103 llevels = ['error', 'warn', 'info', 'debug'];
8db09746 104 for (i = 0; i < llevels.length; i += 1) {
da6dd893
JM
105 html += '<option value="' + llevels[i] + '">' + llevels[i] + '</option>';
106 }
107 html += ' </select> Logging</li>';
108
109 html += ' <hr>';
110 html += ' <li><input type="button" id="VNC_apply" value="Apply"';
f7ec5b2c 111 html += ' onclick="UI.settingsApply()"></li>';
da6dd893
JM
112 html += ' </ul>';
113 html += ' </span></div></td>';
ad3f7624
JM
114
115 // CtrlAltDel Button
da6dd893 116 html += ' <td width=1%><div class="VNC_buttons_right">';
ad3f7624 117 html += ' <input type=button class="VNC_status_button" value="CtrlAltDel"';
160fabf6 118 html += ' id="sendCtrlAltDelButton"';
f7ec5b2c 119 html += ' onclick="UI.sendCtrlAltDel();"></div></td>';
ad3f7624 120
63708ff5
JM
121 html += ' </tr></table>';
122 html += ' </div>';
91308399
JM
123 html += ' <canvas id="VNC_canvas" width="640px" height="20px">';
124 html += ' Canvas not supported.';
125 html += ' </canvas>';
7825b9ee 126 html += '<input id="keyboardinput" style="border:none;" type="text" onKeyDown="onKeyDown(event);"/>';
91308399
JM
127 html += '</div>';
128 html += '<br><br>';
129 html += '<div id="VNC_clipboard">';
130 html += ' VNC Clipboard:';
131 html += ' <input id="VNC_clipboard_clear_button"';
132 html += ' type="button" value="Clear"';
f7ec5b2c 133 html += ' onclick="UI.clipClear();">';
91308399
JM
134 html += ' <br>';
135 html += ' <textarea id="VNC_clipboard_text" cols=80 rows=5';
d890e864
JM
136 html += ' onfocus="UI.displayBlur();"';
137 html += ' onblur="UI.displayFocus();"';
f7ec5b2c 138 html += ' onchange="UI.clipSend();"></textarea>';
91308399 139 html += '</div>';
e4671910 140 target.innerHTML = html;
91308399 141
da6dd893 142 // Settings with immediate effects
f7ec5b2c
JM
143 UI.initSetting('logging', 'warn');
144 WebUtil.init_logging(UI.getSetting('logging'));
145 UI.initSetting('stylesheet', 'default');
8db09746 146
8d5d2c82 147 WebUtil.selectStylesheet(null); // call twice to get around webkit bug
f7ec5b2c 148 WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
da6dd893 149
91308399 150 /* Populate the controls if defaults are provided in the URL */
f7ec5b2c
JM
151 UI.initSetting('host', '');
152 UI.initSetting('port', '');
153 UI.initSetting('password', '');
154 UI.initSetting('encrypt', false);
155 UI.initSetting('true_color', true);
156 UI.initSetting('cursor', false);
157 UI.initSetting('shared', true);
158 UI.initSetting('connectTimeout', 2);
159
e4671910 160 UI.rfb = RFB({'target': $D('VNC_canvas'),
d890e864
JM
161 'onUpdateState': UI.updateState,
162 'onClipboard': UI.clipReceive});
8db09746
JM
163
164 // Unfocus clipboard when over the VNC area
e4671910 165 $D('VNC_screen').onmousemove = function () {
d1bd5ec7
JM
166 var keyboard = UI.rfb.get_keyboard();
167 if ((! keyboard) || (! keyboard.get_focused())) {
e4671910 168 $D('VNC_clipboard_text').blur();
3954ae14
JM
169 }
170 };
8db09746 171
ad3f7624
JM
172 // Show mouse selector buttons on touch screen devices
173 if ('ontouchstart' in document.documentElement) {
174 $D('VNC_mouse_buttons').style.display = "inline";
175 UI.setMouseButton();
176 }
177
91308399
JM
178},
179
da6dd893
JM
180// Read form control compatible setting from cookie
181getSetting: function(name) {
e4671910 182 var val, ctrl = $D('VNC_' + name);
8d5d2c82 183 val = WebUtil.readCookie(name);
da6dd893
JM
184 if (ctrl.type === 'checkbox') {
185 if (val.toLowerCase() in {'0':1, 'no':1, 'false':1}) {
186 val = false;
187 } else {
188 val = true;
189 }
190 }
191 return val;
192},
193
194// Update cookie and form control setting. If value is not set, then
195// updates from control to current cookie setting.
196updateSetting: function(name, value) {
e4671910 197 var i, ctrl = $D('VNC_' + name);
da6dd893
JM
198 // Save the cookie for this session
199 if (typeof value !== 'undefined') {
8d5d2c82 200 WebUtil.createCookie(name, value);
da6dd893
JM
201 }
202
203 // Update the settings control
f7ec5b2c 204 value = UI.getSetting(name);
da6dd893
JM
205 if (ctrl.type === 'checkbox') {
206 ctrl.checked = value;
207 } else if (typeof ctrl.options !== 'undefined') {
8db09746 208 for (i = 0; i < ctrl.options.length; i += 1) {
da6dd893
JM
209 if (ctrl.options[i].value === value) {
210 ctrl.selectedIndex = i;
211 break;
212 }
213 }
214 } else {
215 ctrl.value = value;
216 }
217},
218
219// Save control setting to cookie
220saveSetting: function(name) {
e4671910 221 var val, ctrl = $D('VNC_' + name);
da6dd893
JM
222 if (ctrl.type === 'checkbox') {
223 val = ctrl.checked;
224 } else if (typeof ctrl.options !== 'undefined') {
225 val = ctrl.options[ctrl.selectedIndex].value;
226 } else {
227 val = ctrl.value;
228 }
8d5d2c82 229 WebUtil.createCookie(name, val);
8db09746 230 //Util.Debug("Setting saved '" + name + "=" + val + "'");
da6dd893
JM
231 return val;
232},
233
234// Initial page load read/initialization of settings
235initSetting: function(name, defVal) {
a8edf9d8 236 var val;
da6dd893
JM
237
238 // Check Query string followed by cookie
8d5d2c82 239 val = WebUtil.getQueryVar(name);
da6dd893 240 if (val === null) {
8d5d2c82 241 val = WebUtil.readCookie(name, defVal);
da6dd893 242 }
f7ec5b2c 243 UI.updateSetting(name, val);
8db09746 244 //Util.Debug("Setting '" + name + "' initialized to '" + val + "'");
da6dd893
JM
245 return val;
246},
247
248
249// Toggle the settings menu:
250// On open, settings are refreshed from saved cookies.
251// On close, settings are applied
252clickSettingsMenu: function() {
f7ec5b2c
JM
253 if (UI.settingsOpen) {
254 UI.settingsApply();
da6dd893 255
f7ec5b2c 256 UI.closeSettingsMenu();
da6dd893 257 } else {
f7ec5b2c
JM
258 UI.updateSetting('encrypt');
259 UI.updateSetting('true_color');
d890e864 260 if (UI.rfb.get_display().get_cursor_uri()) {
f7ec5b2c 261 UI.updateSetting('cursor');
da6dd893 262 } else {
f7ec5b2c 263 UI.updateSetting('cursor', false);
e4671910 264 $D('VNC_cursor').disabled = true;
da6dd893 265 }
f7ec5b2c
JM
266 UI.updateSetting('shared');
267 UI.updateSetting('connectTimeout');
268 UI.updateSetting('stylesheet');
269 UI.updateSetting('logging');
da6dd893 270
f7ec5b2c 271 UI.openSettingsMenu();
da6dd893
JM
272 }
273},
274
275// Open menu
276openSettingsMenu: function() {
e4671910 277 $D('VNC_settings_menu').style.display = "block";
f7ec5b2c 278 UI.settingsOpen = true;
da6dd893
JM
279},
280
281// Close menu (without applying settings)
282closeSettingsMenu: function() {
e4671910 283 $D('VNC_settings_menu').style.display = "none";
f7ec5b2c 284 UI.settingsOpen = false;
da6dd893
JM
285},
286
287// Disable/enable controls depending on connection state
b5087acc
JM
288settingsDisabled: function(disabled, rfb) {
289 //Util.Debug(">> settingsDisabled");
e4671910
JM
290 $D('VNC_encrypt').disabled = disabled;
291 $D('VNC_true_color').disabled = disabled;
d890e864 292 if (rfb && rfb.get_display() && rfb.get_display().get_cursor_uri()) {
e4671910 293 $D('VNC_cursor').disabled = disabled;
da6dd893 294 } else {
f7ec5b2c 295 UI.updateSetting('cursor', false);
e4671910 296 $D('VNC_cursor').disabled = true;
da6dd893 297 }
e4671910
JM
298 $D('VNC_shared').disabled = disabled;
299 $D('VNC_connectTimeout').disabled = disabled;
b5087acc 300 //Util.Debug("<< settingsDisabled");
da6dd893
JM
301},
302
303// Save/apply settings when 'Apply' button is pressed
304settingsApply: function() {
8db09746 305 //Util.Debug(">> settingsApply");
f7ec5b2c
JM
306 UI.saveSetting('encrypt');
307 UI.saveSetting('true_color');
d890e864 308 if (UI.rfb.get_display().get_cursor_uri()) {
f7ec5b2c 309 UI.saveSetting('cursor');
da6dd893 310 }
f7ec5b2c
JM
311 UI.saveSetting('shared');
312 UI.saveSetting('connectTimeout');
313 UI.saveSetting('stylesheet');
314 UI.saveSetting('logging');
da6dd893
JM
315
316 // Settings with immediate (non-connected related) effect
f7ec5b2c
JM
317 WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
318 WebUtil.init_logging(UI.getSetting('logging'));
da6dd893 319
8db09746 320 //Util.Debug("<< settingsApply");
da6dd893
JM
321},
322
323
324
f00b1e37 325setPassword: function() {
e4671910 326 UI.rfb.sendPassword($D('VNC_password').value);
f00b1e37
JM
327 return false;
328},
329
63708ff5 330sendCtrlAltDel: function() {
f7ec5b2c 331 UI.rfb.sendCtrlAltDel();
63708ff5
JM
332},
333
ad3f7624
JM
334setMouseButton: function(num) {
335 var b, blist = [1,2,4], button,
336 mouse = UI.rfb.get_mouse();
337
338 if (typeof num === 'undefined') {
339 // Show the default
340 num = mouse.get_touchButton();
341 } else if (num === mouse.get_touchButton()) {
342 // Set all buttons off (no clicks)
343 mouse.set_touchButton(0);
344 num = 0;
345 } else {
346 // Turn on one button
347 mouse.set_touchButton(num);
348 }
349
350 for (b = 0; b < blist.length; b++) {
351 button = $D('VNC_mouse_button' + blist[b]);
352 if (blist[b] === num) {
353 button.style.backgroundColor = "black";
354 button.style.color = "lightgray";
355 } else {
356 button.style.backgroundColor = "";
357 button.style.color = "";
358 }
359 }
360
361},
362
8db09746 363updateState: function(rfb, state, oldstate, msg) {
a7a89626 364 var s, sb, c, cad, klass;
e4671910
JM
365 s = $D('VNC_status');
366 sb = $D('VNC_status_bar');
367 c = $D('VNC_connect_button');
368 cad = $D('sendCtrlAltDelButton');
91308399
JM
369 switch (state) {
370 case 'failed':
f00b1e37 371 case 'fatal':
91308399 372 c.disabled = true;
160fabf6 373 cad.disabled = true;
f7ec5b2c 374 UI.settingsDisabled(true, rfb);
91308399
JM
375 klass = "VNC_status_error";
376 break;
377 case 'normal':
378 c.value = "Disconnect";
f7ec5b2c 379 c.onclick = UI.disconnect;
91308399 380 c.disabled = false;
160fabf6 381 cad.disabled = false;
f7ec5b2c 382 UI.settingsDisabled(true, rfb);
91308399
JM
383 klass = "VNC_status_normal";
384 break;
385 case 'disconnected':
f00b1e37 386 case 'loaded':
91308399 387 c.value = "Connect";
f7ec5b2c 388 c.onclick = UI.connect;
91308399
JM
389
390 c.disabled = false;
160fabf6 391 cad.disabled = true;
f7ec5b2c 392 UI.settingsDisabled(false, rfb);
91308399
JM
393 klass = "VNC_status_normal";
394 break;
f00b1e37
JM
395 case 'password':
396 c.value = "Send Password";
f7ec5b2c 397 c.onclick = UI.setPassword;
f00b1e37
JM
398
399 c.disabled = false;
400 cad.disabled = true;
f7ec5b2c 401 UI.settingsDisabled(true, rfb);
f00b1e37
JM
402 klass = "VNC_status_warn";
403 break;
91308399
JM
404 default:
405 c.disabled = true;
160fabf6 406 cad.disabled = true;
f7ec5b2c 407 UI.settingsDisabled(true, rfb);
91308399
JM
408 klass = "VNC_status_warn";
409 break;
410 }
411
412 if (typeof(msg) !== 'undefined') {
413 s.setAttribute("class", klass);
63708ff5 414 sb.setAttribute("class", klass);
91308399
JM
415 s.innerHTML = msg;
416 }
417
418},
419
8db09746 420clipReceive: function(rfb, text) {
f7ec5b2c 421 Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "...");
e4671910 422 $D('VNC_clipboard_text').value = text;
f7ec5b2c 423 Util.Debug("<< UI.clipReceive");
8db09746
JM
424},
425
426
91308399 427connect: function() {
f7ec5b2c 428 var host, port, password;
da6dd893 429
f7ec5b2c 430 UI.closeSettingsMenu();
da6dd893 431
e4671910
JM
432 host = $D('VNC_host').value;
433 port = $D('VNC_port').value;
434 password = $D('VNC_password').value;
91308399 435 if ((!host) || (!port)) {
63708ff5 436 throw("Must set host and port");
91308399
JM
437 }
438
f7ec5b2c
JM
439 UI.rfb.set_encrypt(UI.getSetting('encrypt'));
440 UI.rfb.set_true_color(UI.getSetting('true_color'));
441 UI.rfb.set_local_cursor(UI.getSetting('cursor'));
442 UI.rfb.set_shared(UI.getSetting('shared'));
443 UI.rfb.set_connectTimeout(UI.getSetting('connectTimeout'));
da6dd893 444
f7ec5b2c 445 UI.rfb.connect(host, port, password);
91308399
JM
446},
447
448disconnect: function() {
f7ec5b2c 449 UI.closeSettingsMenu();
da6dd893 450
f7ec5b2c 451 UI.rfb.disconnect();
91308399
JM
452},
453
d890e864 454displayBlur: function() {
d1bd5ec7
JM
455 UI.rfb.get_keyboard().set_focused(false);
456 UI.rfb.get_mouse().set_focused(false);
91308399
JM
457},
458
d890e864 459displayFocus: function() {
d1bd5ec7
JM
460 UI.rfb.get_keyboard().set_focused(true);
461 UI.rfb.get_mouse().set_focused(true);
91308399
JM
462},
463
464clipClear: function() {
e4671910 465 $D('VNC_clipboard_text').value = "";
f7ec5b2c 466 UI.rfb.clipboardPasteFrom("");
91308399
JM
467},
468
469clipSend: function() {
e4671910 470 var text = $D('VNC_clipboard_text').value;
f7ec5b2c
JM
471 Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "...");
472 UI.rfb.clipboardPasteFrom(text);
473 Util.Debug("<< UI.clipSend");
91308399
JM
474}
475
63708ff5 476};
7825b9ee
CG
477
478/*
479 Functions for use of mobile devices native keyboards
480 Added by Chris Gordon
481 http://www.chrisgordon.com.au
482 Date: 23/08/2011
483*/
484
485function showkeyboard(){
486//Get Current Scroll Position of Browser
487var scrollx = (document.all)?document.body.scrollLeft:window.pageXOffset;
488var scrolly = (document.all)?document.body.scrollTop:window.pageYOffset;
489
490//Stop browser zooming on textbox when focus is set.
491zoomDisable();
492
493//Focus on hidden textbox to bring up native keyboard.
494document.getElementById('keyboardinput').focus();
495
496//Set scroll position of browser to the same position it was prior to focus.
497scroll(scrollx,scrolly);
498
499//Renable user zooming.
500zoomEnable();
501}
502
503function zoomDisable(){
504 //Change viewport meta data to disable zooming.
1734b5e4 505 changeViewportMeta("user-scalable=0");
7825b9ee
CG
506}
507
508function zoomEnable(){
509 //Change viewport meta data to enable user zooming.
1734b5e4
CG
510 changeViewportMeta("user-scalable=1");
511}
512
513function changeViewportMeta(newattributes) {
514
515 // First, get the array of meta-tag elements
516 var metatags = document.getElementsByTagName("meta");
517
518 // Update only the Viewport meta tag
519 for (var cnt = 0; cnt < metatags.length; cnt++)
520 {
521
522 var name = metatags[cnt].getAttribute("name");
523 var content = metatags[cnt].getAttribute("content");
524
525 // Update the Viewport meta tag
526 if (metatags[cnt].getAttribute("name") == "viewport")
527 metatags[cnt].setAttribute("content", newattributes);
528 }
7825b9ee 529}