]> git.proxmox.com Git - mirror_novnc.git/blob - include/ui.js
Helper function for viewport meta changes.
[mirror_novnc.git] / include / ui.js
1 /*
2 * noVNC: HTML5 VNC client
3 * Copyright (C) 2011 Joel Martin
4 * Licensed under LGPL-3 (see LICENSE.txt)
5 *
6 * See README.md for usage and integration instructions.
7 */
8
9 "use strict";
10 /*jslint white: false, browser: true */
11 /*global window, $D, Util, WebUtil, RFB, Display */
12
13 var UI = {
14
15 settingsOpen : false,
16
17 // Render default UI and initialize settings menu
18 load: function(target) {
19 var html = '', i, sheet, sheets, llevels;
20
21 /* Populate the 'target' DOM element with default UI */
22 if (!target) {
23 target = $D('vnc');
24 } else if (typeof target === 'string') {
25 target = $D(target);
26 }
27
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>';
39 target.innerHTML = html;
40 return;
41 }
42
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>';
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">';
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>';
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);">';
67 html += ' <input type="button" id="showKeyboard" onclick="showkeyboard()" value="Keyboard" />';
68 html += ' </span></nobr></div></td>';
69
70 // Settings drop-down menu
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"';
74 html += ' onclick="UI.clickSettingsMenu();">';
75 html += ' <span id="VNC_settings_menu"';
76 html += ' onmouseover="UI.displayBlur();"';
77 html += ' onmouseout="UI.displayFocus();">';
78 html += ' <ul>';
79 html += ' <li><input id="VNC_encrypt"';
80 html += ' type="checkbox"> Encrypt</li>';
81 html += ' <li><input id="VNC_true_color"';
82 html += ' type="checkbox" checked> True Color</li>';
83 html += ' <li><input id="VNC_cursor"';
84 html += ' type="checkbox"> Local Cursor</li>';
85 html += ' <li><input id="VNC_shared"';
86 html += ' type="checkbox"> Shared Mode</li>';
87 html += ' <li><input id="VNC_connectTimeout"';
88 html += ' type="input"> Connect Timeout (s)</li>';
89 html += ' <hr>';
90
91 // Stylesheet selection dropdown
92 html += ' <li><select id="VNC_stylesheet" name="vncStyle">';
93 html += ' <option value="default">default</option>';
94 sheet = WebUtil.selectStylesheet();
95 sheets = WebUtil.getStylesheets();
96 for (i = 0; i < sheets.length; i += 1) {
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'];
104 for (i = 0; i < llevels.length; i += 1) {
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"';
111 html += ' onclick="UI.settingsApply()"></li>';
112 html += ' </ul>';
113 html += ' </span></div></td>';
114
115 // CtrlAltDel Button
116 html += ' <td width=1%><div class="VNC_buttons_right">';
117 html += ' <input type=button class="VNC_status_button" value="CtrlAltDel"';
118 html += ' id="sendCtrlAltDelButton"';
119 html += ' onclick="UI.sendCtrlAltDel();"></div></td>';
120
121 html += ' </tr></table>';
122 html += ' </div>';
123 html += ' <canvas id="VNC_canvas" width="640px" height="20px">';
124 html += ' Canvas not supported.';
125 html += ' </canvas>';
126 html += '<input id="keyboardinput" style="border:none;" type="text" onKeyDown="onKeyDown(event);"/>';
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"';
133 html += ' onclick="UI.clipClear();">';
134 html += ' <br>';
135 html += ' <textarea id="VNC_clipboard_text" cols=80 rows=5';
136 html += ' onfocus="UI.displayBlur();"';
137 html += ' onblur="UI.displayFocus();"';
138 html += ' onchange="UI.clipSend();"></textarea>';
139 html += '</div>';
140 target.innerHTML = html;
141
142 // Settings with immediate effects
143 UI.initSetting('logging', 'warn');
144 WebUtil.init_logging(UI.getSetting('logging'));
145 UI.initSetting('stylesheet', 'default');
146
147 WebUtil.selectStylesheet(null); // call twice to get around webkit bug
148 WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
149
150 /* Populate the controls if defaults are provided in the URL */
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
160 UI.rfb = RFB({'target': $D('VNC_canvas'),
161 'onUpdateState': UI.updateState,
162 'onClipboard': UI.clipReceive});
163
164 // Unfocus clipboard when over the VNC area
165 $D('VNC_screen').onmousemove = function () {
166 var keyboard = UI.rfb.get_keyboard();
167 if ((! keyboard) || (! keyboard.get_focused())) {
168 $D('VNC_clipboard_text').blur();
169 }
170 };
171
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
178 },
179
180 // Read form control compatible setting from cookie
181 getSetting: function(name) {
182 var val, ctrl = $D('VNC_' + name);
183 val = WebUtil.readCookie(name);
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.
196 updateSetting: function(name, value) {
197 var i, ctrl = $D('VNC_' + name);
198 // Save the cookie for this session
199 if (typeof value !== 'undefined') {
200 WebUtil.createCookie(name, value);
201 }
202
203 // Update the settings control
204 value = UI.getSetting(name);
205 if (ctrl.type === 'checkbox') {
206 ctrl.checked = value;
207 } else if (typeof ctrl.options !== 'undefined') {
208 for (i = 0; i < ctrl.options.length; i += 1) {
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
220 saveSetting: function(name) {
221 var val, ctrl = $D('VNC_' + name);
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 }
229 WebUtil.createCookie(name, val);
230 //Util.Debug("Setting saved '" + name + "=" + val + "'");
231 return val;
232 },
233
234 // Initial page load read/initialization of settings
235 initSetting: function(name, defVal) {
236 var val;
237
238 // Check Query string followed by cookie
239 val = WebUtil.getQueryVar(name);
240 if (val === null) {
241 val = WebUtil.readCookie(name, defVal);
242 }
243 UI.updateSetting(name, val);
244 //Util.Debug("Setting '" + name + "' initialized to '" + val + "'");
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
252 clickSettingsMenu: function() {
253 if (UI.settingsOpen) {
254 UI.settingsApply();
255
256 UI.closeSettingsMenu();
257 } else {
258 UI.updateSetting('encrypt');
259 UI.updateSetting('true_color');
260 if (UI.rfb.get_display().get_cursor_uri()) {
261 UI.updateSetting('cursor');
262 } else {
263 UI.updateSetting('cursor', false);
264 $D('VNC_cursor').disabled = true;
265 }
266 UI.updateSetting('shared');
267 UI.updateSetting('connectTimeout');
268 UI.updateSetting('stylesheet');
269 UI.updateSetting('logging');
270
271 UI.openSettingsMenu();
272 }
273 },
274
275 // Open menu
276 openSettingsMenu: function() {
277 $D('VNC_settings_menu').style.display = "block";
278 UI.settingsOpen = true;
279 },
280
281 // Close menu (without applying settings)
282 closeSettingsMenu: function() {
283 $D('VNC_settings_menu').style.display = "none";
284 UI.settingsOpen = false;
285 },
286
287 // Disable/enable controls depending on connection state
288 settingsDisabled: function(disabled, rfb) {
289 //Util.Debug(">> settingsDisabled");
290 $D('VNC_encrypt').disabled = disabled;
291 $D('VNC_true_color').disabled = disabled;
292 if (rfb && rfb.get_display() && rfb.get_display().get_cursor_uri()) {
293 $D('VNC_cursor').disabled = disabled;
294 } else {
295 UI.updateSetting('cursor', false);
296 $D('VNC_cursor').disabled = true;
297 }
298 $D('VNC_shared').disabled = disabled;
299 $D('VNC_connectTimeout').disabled = disabled;
300 //Util.Debug("<< settingsDisabled");
301 },
302
303 // Save/apply settings when 'Apply' button is pressed
304 settingsApply: function() {
305 //Util.Debug(">> settingsApply");
306 UI.saveSetting('encrypt');
307 UI.saveSetting('true_color');
308 if (UI.rfb.get_display().get_cursor_uri()) {
309 UI.saveSetting('cursor');
310 }
311 UI.saveSetting('shared');
312 UI.saveSetting('connectTimeout');
313 UI.saveSetting('stylesheet');
314 UI.saveSetting('logging');
315
316 // Settings with immediate (non-connected related) effect
317 WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
318 WebUtil.init_logging(UI.getSetting('logging'));
319
320 //Util.Debug("<< settingsApply");
321 },
322
323
324
325 setPassword: function() {
326 UI.rfb.sendPassword($D('VNC_password').value);
327 return false;
328 },
329
330 sendCtrlAltDel: function() {
331 UI.rfb.sendCtrlAltDel();
332 },
333
334 setMouseButton: 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
363 updateState: function(rfb, state, oldstate, msg) {
364 var s, sb, c, cad, klass;
365 s = $D('VNC_status');
366 sb = $D('VNC_status_bar');
367 c = $D('VNC_connect_button');
368 cad = $D('sendCtrlAltDelButton');
369 switch (state) {
370 case 'failed':
371 case 'fatal':
372 c.disabled = true;
373 cad.disabled = true;
374 UI.settingsDisabled(true, rfb);
375 klass = "VNC_status_error";
376 break;
377 case 'normal':
378 c.value = "Disconnect";
379 c.onclick = UI.disconnect;
380 c.disabled = false;
381 cad.disabled = false;
382 UI.settingsDisabled(true, rfb);
383 klass = "VNC_status_normal";
384 break;
385 case 'disconnected':
386 case 'loaded':
387 c.value = "Connect";
388 c.onclick = UI.connect;
389
390 c.disabled = false;
391 cad.disabled = true;
392 UI.settingsDisabled(false, rfb);
393 klass = "VNC_status_normal";
394 break;
395 case 'password':
396 c.value = "Send Password";
397 c.onclick = UI.setPassword;
398
399 c.disabled = false;
400 cad.disabled = true;
401 UI.settingsDisabled(true, rfb);
402 klass = "VNC_status_warn";
403 break;
404 default:
405 c.disabled = true;
406 cad.disabled = true;
407 UI.settingsDisabled(true, rfb);
408 klass = "VNC_status_warn";
409 break;
410 }
411
412 if (typeof(msg) !== 'undefined') {
413 s.setAttribute("class", klass);
414 sb.setAttribute("class", klass);
415 s.innerHTML = msg;
416 }
417
418 },
419
420 clipReceive: function(rfb, text) {
421 Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "...");
422 $D('VNC_clipboard_text').value = text;
423 Util.Debug("<< UI.clipReceive");
424 },
425
426
427 connect: function() {
428 var host, port, password;
429
430 UI.closeSettingsMenu();
431
432 host = $D('VNC_host').value;
433 port = $D('VNC_port').value;
434 password = $D('VNC_password').value;
435 if ((!host) || (!port)) {
436 throw("Must set host and port");
437 }
438
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'));
444
445 UI.rfb.connect(host, port, password);
446 },
447
448 disconnect: function() {
449 UI.closeSettingsMenu();
450
451 UI.rfb.disconnect();
452 },
453
454 displayBlur: function() {
455 UI.rfb.get_keyboard().set_focused(false);
456 UI.rfb.get_mouse().set_focused(false);
457 },
458
459 displayFocus: function() {
460 UI.rfb.get_keyboard().set_focused(true);
461 UI.rfb.get_mouse().set_focused(true);
462 },
463
464 clipClear: function() {
465 $D('VNC_clipboard_text').value = "";
466 UI.rfb.clipboardPasteFrom("");
467 },
468
469 clipSend: function() {
470 var text = $D('VNC_clipboard_text').value;
471 Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "...");
472 UI.rfb.clipboardPasteFrom(text);
473 Util.Debug("<< UI.clipSend");
474 }
475
476 };
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
485 function showkeyboard(){
486 //Get Current Scroll Position of Browser
487 var scrollx = (document.all)?document.body.scrollLeft:window.pageXOffset;
488 var scrolly = (document.all)?document.body.scrollTop:window.pageYOffset;
489
490 //Stop browser zooming on textbox when focus is set.
491 zoomDisable();
492
493 //Focus on hidden textbox to bring up native keyboard.
494 document.getElementById('keyboardinput').focus();
495
496 //Set scroll position of browser to the same position it was prior to focus.
497 scroll(scrollx,scrolly);
498
499 //Renable user zooming.
500 zoomEnable();
501 }
502
503 function zoomDisable(){
504 //Change viewport meta data to disable zooming.
505 changeViewportMeta("user-scalable=0");
506 }
507
508 function zoomEnable(){
509 //Change viewport meta data to enable user zooming.
510 changeViewportMeta("user-scalable=1");
511 }
512
513 function 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 }
529 }