]> git.proxmox.com Git - mirror_novnc.git/blob - app/ui.js
Remove special password state
[mirror_novnc.git] / app / ui.js
1 /*
2 * noVNC: HTML5 VNC client
3 * Copyright (C) 2012 Joel Martin
4 * Copyright (C) 2016 Samuel Mannehed for Cendio AB
5 * Copyright (C) 2016 Pierre Ossman for Cendio AB
6 * Licensed under MPL 2.0 (see LICENSE.txt)
7 *
8 * See README.md for usage and integration instructions.
9 */
10
11 /* jslint white: false, browser: true */
12 /* global window, document.getElementById, Util, WebUtil, RFB, Display */
13
14 /* [module]
15 * import Util from "../core/util";
16 * import KeyTable from "../core/input/keysym";
17 * import RFB from "../core/rfb";
18 * import Display from "../core/display";
19 * import WebUtil from "./webutil";
20 */
21
22 var UI;
23
24 (function () {
25 "use strict";
26
27 /* [begin skip-as-module] */
28 // Load supporting scripts
29 WebUtil.load_scripts(
30 {'core': ["base64.js", "websock.js", "des.js", "input/keysymdef.js",
31 "input/xtscancodes.js", "input/util.js", "input/devices.js",
32 "display.js", "inflator.js", "rfb.js", "input/keysym.js"]});
33
34 window.onscriptsload = function () { UI.load(); };
35 /* [end skip-as-module] */
36
37 UI = {
38
39 rfb_state: 'loaded',
40
41 resizeTimeout: null,
42 statusTimeout: null,
43 hideKeyboardTimeout: null,
44 idleControlbarTimeout: null,
45 closeControlbarTimeout: null,
46
47 controlbarGrabbed: false,
48 controlbarDrag: false,
49 controlbarMouseDownClientY: 0,
50 controlbarMouseDownOffsetY: 0,
51 keyboardVisible: false,
52
53 isTouchDevice: false,
54 isSafari: false,
55 rememberedClipSetting: null,
56 lastKeyboardinput: null,
57 defaultKeyboardinputLen: 100,
58
59 // Setup rfb object, load settings from browser storage, then call
60 // UI.init to setup the UI/menus
61 load: function(callback) {
62 WebUtil.initSettings(UI.start, callback);
63 },
64
65 // Render default UI and initialize settings menu
66 start: function(callback) {
67
68 // Setup global variables first
69 UI.isTouchDevice = 'ontouchstart' in document.documentElement;
70 UI.isSafari = (navigator.userAgent.indexOf('Safari') !== -1 &&
71 navigator.userAgent.indexOf('Chrome') === -1);
72
73 UI.initSettings();
74
75 // Adapt the interface for touch screen devices
76 if (UI.isTouchDevice) {
77 document.documentElement.classList.add("noVNC_touch");
78 // Remove the address bar
79 setTimeout(function() { window.scrollTo(0, 1); }, 100);
80 UI.forceSetting('clip', true);
81 } else {
82 UI.initSetting('clip', false);
83 }
84
85 // Setup and initialize event handlers
86 UI.setupWindowEvents();
87 UI.setupFullscreen();
88 UI.addControlbarHandlers();
89 UI.addTouchSpecificHandlers();
90 UI.addExtraKeysHandlers();
91 UI.addXvpHandlers();
92 UI.addConnectionControlHandlers();
93 UI.addClipboardHandlers();
94 UI.addSettingsHandlers();
95
96 // Show the connect panel on first load unless autoconnecting
97 if (!autoconnect) {
98 UI.openConnectPanel();
99 }
100
101 UI.updateViewClip();
102
103 UI.updateVisualState();
104
105 document.getElementById('noVNC_setting_host').focus();
106
107 var autoconnect = WebUtil.getConfigVar('autoconnect', false);
108 if (autoconnect === 'true' || autoconnect == '1') {
109 autoconnect = true;
110 UI.connect();
111 } else {
112 autoconnect = false;
113 }
114
115 if (typeof callback === "function") {
116 callback(UI.rfb);
117 }
118 },
119
120 initSettings: function() {
121 // Stylesheet selection dropdown
122 var sheet = WebUtil.selectStylesheet();
123 var sheets = WebUtil.getStylesheets();
124 var i;
125 for (i = 0; i < sheets.length; i += 1) {
126 UI.addOption(document.getElementById('noVNC_setting_stylesheet'),sheets[i].title, sheets[i].title);
127 }
128
129 // Logging selection dropdown
130 var llevels = ['error', 'warn', 'info', 'debug'];
131 for (i = 0; i < llevels.length; i += 1) {
132 UI.addOption(document.getElementById('noVNC_setting_logging'),llevels[i], llevels[i]);
133 }
134
135 // Settings with immediate effects
136 UI.initSetting('logging', 'warn');
137 WebUtil.init_logging(UI.getSetting('logging'));
138
139 UI.initSetting('stylesheet', 'default');
140 WebUtil.selectStylesheet(null);
141 // call twice to get around webkit bug
142 WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
143
144 // if port == 80 (or 443) then it won't be present and should be
145 // set manually
146 var port = window.location.port;
147 if (!port) {
148 if (window.location.protocol.substring(0,5) == 'https') {
149 port = 443;
150 }
151 else if (window.location.protocol.substring(0,4) == 'http') {
152 port = 80;
153 }
154 }
155
156 /* Populate the controls if defaults are provided in the URL */
157 UI.initSetting('host', window.location.hostname);
158 UI.initSetting('port', port);
159 UI.initSetting('password', '');
160 UI.initSetting('encrypt', (window.location.protocol === "https:"));
161 UI.initSetting('true_color', true);
162 UI.initSetting('cursor', !UI.isTouchDevice);
163 UI.initSetting('resize', 'off');
164 UI.initSetting('shared', true);
165 UI.initSetting('view_only', false);
166 UI.initSetting('path', 'websockify');
167 UI.initSetting('repeaterID', '');
168 UI.initSetting('token', '');
169 },
170
171 setupWindowEvents: function() {
172 window.addEventListener( 'resize', function () {
173 UI.applyResizeMode();
174 UI.updateViewClip();
175 UI.updateViewDrag();
176 } );
177
178 document.getElementById("noVNC_status")
179 .addEventListener('click', UI.hideStatus);
180 },
181
182 setupFullscreen: function() {
183 // Only show the button if fullscreen is properly supported
184 // * Safari doesn't support alphanumerical input while in fullscreen
185 if (!UI.isSafari &&
186 (document.documentElement.requestFullscreen ||
187 document.documentElement.mozRequestFullScreen ||
188 document.documentElement.webkitRequestFullscreen ||
189 document.body.msRequestFullscreen)) {
190 document.getElementById('noVNC_fullscreen_button')
191 .classList.remove("noVNC_hidden");
192 UI.addFullscreenHandlers();
193 }
194 },
195
196 addControlbarHandlers: function() {
197 document.getElementById("noVNC_control_bar")
198 .addEventListener('mousemove', UI.activateControlbar);
199 document.getElementById("noVNC_control_bar")
200 .addEventListener('mouseup', UI.activateControlbar);
201 document.getElementById("noVNC_control_bar")
202 .addEventListener('mousedown', UI.activateControlbar);
203 document.getElementById("noVNC_control_bar")
204 .addEventListener('keypress', UI.activateControlbar);
205
206 document.getElementById("noVNC_control_bar")
207 .addEventListener('mousedown', UI.keepControlbar);
208 document.getElementById("noVNC_control_bar")
209 .addEventListener('keypress', UI.keepControlbar);
210
211 document.getElementById("noVNC_view_drag_button")
212 .addEventListener('click', UI.toggleViewDrag);
213
214 document.getElementById("noVNC_control_bar_handle")
215 .addEventListener('mousedown', UI.controlbarHandleMouseDown);
216 document.getElementById("noVNC_control_bar_handle")
217 .addEventListener('mouseup', UI.controlbarHandleMouseUp);
218 document.getElementById("noVNC_control_bar_handle")
219 .addEventListener('mousemove', UI.dragControlbarHandle);
220 // resize events aren't available for elements
221 window.addEventListener('resize', UI.updateControlbarHandle);
222 },
223
224 addTouchSpecificHandlers: function() {
225 document.getElementById("noVNC_mouse_button0")
226 .addEventListener('click', function () { UI.setMouseButton(1); });
227 document.getElementById("noVNC_mouse_button1")
228 .addEventListener('click', function () { UI.setMouseButton(2); });
229 document.getElementById("noVNC_mouse_button2")
230 .addEventListener('click', function () { UI.setMouseButton(4); });
231 document.getElementById("noVNC_mouse_button4")
232 .addEventListener('click', function () { UI.setMouseButton(0); });
233 document.getElementById("noVNC_keyboard_button")
234 .addEventListener('click', UI.toggleVirtualKeyboard);
235
236 document.getElementById("noVNC_keyboardinput")
237 .addEventListener('input', UI.keyInput);
238 document.getElementById("noVNC_keyboardinput")
239 .addEventListener('blur', UI.onblurVirtualKeyboard);
240 document.getElementById("noVNC_keyboardinput")
241 .addEventListener('submit', function () { return false; });
242
243 document.getElementById("noVNC_control_bar")
244 .addEventListener('touchstart', UI.activateControlbar);
245 document.getElementById("noVNC_control_bar")
246 .addEventListener('touchmove', UI.activateControlbar);
247 document.getElementById("noVNC_control_bar")
248 .addEventListener('touchend', UI.activateControlbar);
249 document.getElementById("noVNC_control_bar")
250 .addEventListener('input', UI.activateControlbar);
251
252 document.getElementById("noVNC_control_bar")
253 .addEventListener('touchstart', UI.keepControlbar);
254 document.getElementById("noVNC_control_bar")
255 .addEventListener('input', UI.keepControlbar);
256
257 document.getElementById("noVNC_control_bar_handle")
258 .addEventListener('touchstart', UI.controlbarHandleMouseDown);
259 document.getElementById("noVNC_control_bar_handle")
260 .addEventListener('touchend', UI.controlbarHandleMouseUp);
261 document.getElementById("noVNC_control_bar_handle")
262 .addEventListener('touchmove', UI.dragControlbarHandle);
263
264 window.addEventListener('load', UI.keyboardinputReset);
265 },
266
267 addExtraKeysHandlers: function() {
268 document.getElementById("noVNC_toggle_extra_keys_button")
269 .addEventListener('click', UI.toggleExtraKeys);
270 document.getElementById("noVNC_toggle_ctrl_button")
271 .addEventListener('click', UI.toggleCtrl);
272 document.getElementById("noVNC_toggle_alt_button")
273 .addEventListener('click', UI.toggleAlt);
274 document.getElementById("noVNC_send_tab_button")
275 .addEventListener('click', UI.sendTab);
276 document.getElementById("noVNC_send_esc_button")
277 .addEventListener('click', UI.sendEsc);
278 document.getElementById("noVNC_send_ctrl_alt_del_button")
279 .addEventListener('click', UI.sendCtrlAltDel);
280 },
281
282 addXvpHandlers: function() {
283 document.getElementById("noVNC_xvp_shutdown_button")
284 .addEventListener('click', function() { UI.rfb.xvpShutdown(); });
285 document.getElementById("noVNC_xvp_reboot_button")
286 .addEventListener('click', function() { UI.rfb.xvpReboot(); });
287 document.getElementById("noVNC_xvp_reset_button")
288 .addEventListener('click', function() { UI.rfb.xvpReset(); });
289 document.getElementById("noVNC_xvp_button")
290 .addEventListener('click', UI.toggleXvpPanel);
291 },
292
293 addConnectionControlHandlers: function() {
294 document.getElementById("noVNC_connect_controls_button")
295 .addEventListener('click', UI.toggleConnectPanel);
296 document.getElementById("noVNC_disconnect_button")
297 .addEventListener('click', UI.disconnect);
298 document.getElementById("noVNC_connect_button")
299 .addEventListener('click', UI.connect);
300
301 document.getElementById("noVNC_password_button")
302 .addEventListener('click', UI.setPassword);
303 },
304
305 addClipboardHandlers: function() {
306 document.getElementById("noVNC_clipboard_button")
307 .addEventListener('click', UI.toggleClipboardPanel);
308 document.getElementById("noVNC_clipboard_text")
309 .addEventListener('focus', UI.displayBlur);
310 document.getElementById("noVNC_clipboard_text")
311 .addEventListener('blur', UI.displayFocus);
312 document.getElementById("noVNC_clipboard_text")
313 .addEventListener('change', UI.clipboardSend);
314 document.getElementById("noVNC_clipboard_clear_button")
315 .addEventListener('click', UI.clipboardClear);
316 },
317
318 addSettingsHandlers: function() {
319 document.getElementById("noVNC_settings_button")
320 .addEventListener('click', UI.toggleSettingsPanel);
321 document.getElementById("noVNC_settings_apply")
322 .addEventListener('click', UI.settingsApply);
323
324 document.getElementById("noVNC_setting_resize")
325 .addEventListener('change', UI.enableDisableViewClip);
326 },
327
328 addFullscreenHandlers: function() {
329 document.getElementById("noVNC_fullscreen_button")
330 .addEventListener('click', UI.toggleFullscreen);
331
332 window.addEventListener('fullscreenchange', UI.updateFullscreenButton);
333 window.addEventListener('mozfullscreenchange', UI.updateFullscreenButton);
334 window.addEventListener('webkitfullscreenchange', UI.updateFullscreenButton);
335 window.addEventListener('msfullscreenchange', UI.updateFullscreenButton);
336 },
337
338 initRFB: function() {
339 try {
340 UI.rfb = new RFB({'target': document.getElementById('noVNC_canvas'),
341 'onUpdateState': UI.updateState,
342 'onPasswordRequired': UI.passwordRequired,
343 'onXvpInit': UI.updateXvpButton,
344 'onClipboard': UI.clipboardReceive,
345 'onFBUComplete': UI.initialResize,
346 'onFBResize': UI.updateViewDrag,
347 'onDesktopName': UI.updateDocumentTitle});
348 return true;
349 } catch (exc) {
350 UI.updateState(null, 'fatal', null, 'Unable to create RFB client -- ' + exc);
351 return false;
352 }
353 },
354
355 /* ------^-------
356 * /INIT
357 * ==============
358 * VISUAL
359 * ------v------*/
360
361 updateState: function(rfb, state, oldstate, msg) {
362 UI.rfb_state = state;
363
364 if (typeof(msg) !== 'undefined') {
365 switch (state) {
366 case 'failed':
367 case 'fatal':
368 // zero means no timeout
369 UI.showStatus(msg, 'error', 0);
370 break;
371 case 'normal':
372 /* falls through */
373 case 'disconnected':
374 case 'loaded':
375 UI.showStatus(msg, 'normal');
376 break;
377 default:
378 UI.showStatus(msg, 'warn');
379 break;
380 }
381 }
382
383 UI.updateVisualState();
384 },
385
386 // Disable/enable controls depending on connection state
387 updateVisualState: function() {
388 var connected = UI.rfb && UI.rfb_state === 'normal';
389
390 //Util.Debug(">> updateVisualState");
391 document.getElementById('noVNC_setting_encrypt').disabled = connected;
392 document.getElementById('noVNC_setting_true_color').disabled = connected;
393 if (Util.browserSupportsCursorURIs()) {
394 document.getElementById('noVNC_setting_cursor').disabled = connected;
395 } else {
396 UI.updateSetting('cursor', !UI.isTouchDevice);
397 document.getElementById('noVNC_setting_cursor').disabled = true;
398 }
399
400 UI.enableDisableViewClip();
401 document.getElementById('noVNC_setting_resize').disabled = connected;
402 document.getElementById('noVNC_setting_shared').disabled = connected;
403 document.getElementById('noVNC_setting_view_only').disabled = connected;
404 document.getElementById('noVNC_setting_path').disabled = connected;
405 document.getElementById('noVNC_setting_repeaterID').disabled = connected;
406
407 if (connected) {
408 document.documentElement.classList.add("noVNC_connected");
409 UI.updateViewClip();
410 UI.setMouseButton(1);
411
412 // Hide the controlbar after 2 seconds
413 UI.closeControlbarTimeout = setTimeout(UI.closeControlbar, 2000);
414 } else {
415 document.documentElement.classList.remove("noVNC_connected");
416 UI.updateXvpButton(0);
417 }
418
419 // State change disables viewport dragging.
420 // It is enabled (toggled) by direct click on the button
421 UI.setViewDrag(false);
422
423 // State change also closes the password dialog
424 document.getElementById('noVNC_password_dlg')
425 .classList.remove('noVNC_open');
426
427 switch (UI.rfb_state) {
428 case 'fatal':
429 case 'failed':
430 case 'disconnected':
431 UI.openConnectPanel();
432 break;
433 case 'loaded':
434 break;
435 default:
436 break;
437 }
438
439 //Util.Debug("<< updateVisualState");
440 },
441
442 showStatus: function(text, status_type, time) {
443 var statusElem = document.getElementById('noVNC_status');
444
445 clearTimeout(UI.statusTimeout);
446
447 if (typeof status_type === 'undefined') {
448 status_type = 'normal';
449 }
450
451 statusElem.classList.remove("noVNC_status_normal",
452 "noVNC_status_warn",
453 "noVNC_status_error");
454
455 switch (status_type) {
456 case 'warning':
457 case 'warn':
458 statusElem.classList.add("noVNC_status_warn");
459 break;
460 case 'error':
461 statusElem.classList.add("noVNC_status_error");
462 break;
463 case 'normal':
464 case 'info':
465 default:
466 statusElem.classList.add("noVNC_status_normal");
467 break;
468 }
469
470 statusElem.innerHTML = text;
471 statusElem.classList.add("noVNC_open");
472
473 // If no time was specified, show the status for 1.5 seconds
474 if (typeof time === 'undefined') {
475 time = 1500;
476 }
477
478 // A specified time of zero means no timeout
479 if (time != 0) {
480 UI.statusTimeout = window.setTimeout(UI.hideStatus, time);
481 }
482 },
483
484 hideStatus: function() {
485 clearTimeout(UI.statusTimeout);
486 document.getElementById('noVNC_status').classList.remove("noVNC_open");
487 },
488
489 activateControlbar: function(event) {
490 clearTimeout(UI.idleControlbarTimeout);
491 // We manipulate the anchor instead of the actual control
492 // bar in order to avoid creating new a stacking group
493 document.getElementById('noVNC_control_bar_anchor')
494 .classList.remove("noVNC_idle");
495 UI.idleControlbarTimeout = window.setTimeout(UI.idleControlbar, 2000);
496 },
497
498 idleControlbar: function() {
499 document.getElementById('noVNC_control_bar_anchor')
500 .classList.add("noVNC_idle");
501 },
502
503 keepControlbar: function() {
504 clearTimeout(UI.closeControlbarTimeout);
505 },
506
507 openControlbar: function() {
508 document.getElementById('noVNC_control_bar')
509 .classList.add("noVNC_open");
510 },
511
512 closeControlbar: function() {
513 UI.closeAllPanels();
514 document.getElementById('noVNC_control_bar')
515 .classList.remove("noVNC_open");
516 },
517
518 toggleControlbar: function() {
519 if (document.getElementById('noVNC_control_bar')
520 .classList.contains("noVNC_open")) {
521 UI.closeControlbar();
522 } else {
523 UI.openControlbar();
524 }
525 },
526
527 dragControlbarHandle: function (e) {
528 if (!UI.controlbarGrabbed) return;
529
530 var ptr = Util.getPointerEvent(e);
531
532 if (!UI.controlbarDrag) {
533 // The goal is to trigger on a certain physical width, the
534 // devicePixelRatio brings us a bit closer but is not optimal.
535 var dragThreshold = 10 * (window.devicePixelRatio || 1);
536 var dragDistance = Math.abs(ptr.clientY - UI.controlbarMouseDownClientY);
537
538 if (dragDistance < dragThreshold) return;
539
540 UI.controlbarDrag = true;
541 }
542
543 var eventY = ptr.clientY - UI.controlbarMouseDownOffsetY;
544
545 UI.moveControlbarHandle(eventY);
546
547 e.preventDefault();
548 e.stopPropagation();
549 },
550
551 // Move the handle but don't allow any position outside the bounds
552 moveControlbarHandle: function (posY) {
553 var handle = document.getElementById("noVNC_control_bar_handle");
554 var handleHeight = Util.getPosition(handle).height;
555 var controlbar = document.getElementById("noVNC_control_bar");
556 var controlbarBounds = Util.getPosition(controlbar);
557 var controlbarTop = controlbarBounds.y;
558 var controlbarBottom = controlbarBounds.y + controlbarBounds.height;
559 var margin = 10;
560
561 var viewportY = posY;
562
563 // Refuse coordinates outside the control bar
564 if (viewportY < controlbarTop + margin) {
565 viewportY = controlbarTop + margin;
566 } else if (viewportY > controlbarBottom - handleHeight - margin) {
567 viewportY = controlbarBottom - handleHeight - margin;
568 }
569
570 // Corner case: control bar too small for stable position
571 if (controlbarBounds.height < (handleHeight + margin * 2)) {
572 viewportY = controlbarTop + (controlbarBounds.height - handleHeight) / 2;
573 }
574
575 var relativeY = viewportY - controlbarTop;
576 handle.style.transform = "translateY(" + relativeY + "px)";
577 },
578
579 updateControlbarHandle: function () {
580 var handle = document.getElementById("noVNC_control_bar_handle");
581 var pos = Util.getPosition(handle);
582 UI.moveControlbarHandle(pos.y);
583 },
584
585 controlbarHandleMouseUp: function(e) {
586 if ((e.type == "mouseup") && (e.button != 0)) return;
587
588 // mouseup and mousedown on the same place toggles the controlbar
589 if (UI.controlbarGrabbed && !UI.controlbarDrag) {
590 UI.toggleControlbar();
591 e.preventDefault();
592 e.stopPropagation();
593 }
594 UI.controlbarGrabbed = false;
595 },
596
597 controlbarHandleMouseDown: function(e) {
598 if ((e.type == "mousedown") && (e.button != 0)) return;
599
600 var ptr = Util.getPointerEvent(e);
601
602 var handle = document.getElementById("noVNC_control_bar_handle");
603 var bounds = handle.getBoundingClientRect();
604
605 WebUtil.setCapture(handle);
606 UI.controlbarGrabbed = true;
607 UI.controlbarDrag = false;
608
609 UI.controlbarMouseDownClientY = ptr.clientY;
610 UI.controlbarMouseDownOffsetY = ptr.clientY - bounds.top;
611 e.preventDefault();
612 e.stopPropagation();
613 },
614
615 /* ------^-------
616 * /VISUAL
617 * ==============
618 * SETTINGS
619 * ------v------*/
620
621 // Initial page load read/initialization of settings
622 initSetting: function(name, defVal) {
623 // Check Query string followed by cookie
624 var val = WebUtil.getConfigVar(name);
625 if (val === null) {
626 val = WebUtil.readSetting(name, defVal);
627 }
628 UI.updateSetting(name, val);
629 return val;
630 },
631
632 // Update cookie and form control setting. If value is not set, then
633 // updates from control to current cookie setting.
634 updateSetting: function(name, value) {
635
636 // Save the cookie for this session
637 if (typeof value !== 'undefined') {
638 WebUtil.writeSetting(name, value);
639 }
640
641 // Update the settings control
642 value = UI.getSetting(name);
643
644 var ctrl = document.getElementById('noVNC_setting_' + name);
645 if (ctrl.type === 'checkbox') {
646 ctrl.checked = value;
647
648 } else if (typeof ctrl.options !== 'undefined') {
649 for (var i = 0; i < ctrl.options.length; i += 1) {
650 if (ctrl.options[i].value === value) {
651 ctrl.selectedIndex = i;
652 break;
653 }
654 }
655 } else {
656 /*Weird IE9 error leads to 'null' appearring
657 in textboxes instead of ''.*/
658 if (value === null) {
659 value = "";
660 }
661 ctrl.value = value;
662 }
663 },
664
665 // Save control setting to cookie
666 saveSetting: function(name) {
667 var val, ctrl = document.getElementById('noVNC_setting_' + name);
668 if (ctrl.type === 'checkbox') {
669 val = ctrl.checked;
670 } else if (typeof ctrl.options !== 'undefined') {
671 val = ctrl.options[ctrl.selectedIndex].value;
672 } else {
673 val = ctrl.value;
674 }
675 WebUtil.writeSetting(name, val);
676 //Util.Debug("Setting saved '" + name + "=" + val + "'");
677 return val;
678 },
679
680 // Force a setting to be a certain value
681 forceSetting: function(name, val) {
682 UI.updateSetting(name, val);
683 return val;
684 },
685
686 // Read form control compatible setting from cookie
687 getSetting: function(name) {
688 var ctrl = document.getElementById('noVNC_setting_' + name);
689 var val = WebUtil.readSetting(name);
690 if (typeof val !== 'undefined' && val !== null && ctrl.type === 'checkbox') {
691 if (val.toString().toLowerCase() in {'0':1, 'no':1, 'false':1}) {
692 val = false;
693 } else {
694 val = true;
695 }
696 }
697 return val;
698 },
699
700 // Save/apply settings when 'Apply' button is pressed
701 settingsApply: function() {
702 //Util.Debug(">> settingsApply");
703 UI.saveSetting('encrypt');
704 UI.saveSetting('true_color');
705 if (Util.browserSupportsCursorURIs()) {
706 UI.saveSetting('cursor');
707 }
708
709 UI.saveSetting('resize');
710
711 if (UI.getSetting('resize') === 'downscale' || UI.getSetting('resize') === 'scale') {
712 UI.forceSetting('clip', false);
713 }
714
715 UI.saveSetting('clip');
716 UI.saveSetting('shared');
717 UI.saveSetting('view_only');
718 UI.saveSetting('path');
719 UI.saveSetting('repeaterID');
720 UI.saveSetting('stylesheet');
721 UI.saveSetting('logging');
722
723 // Settings with immediate (non-connected related) effect
724 WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
725 WebUtil.init_logging(UI.getSetting('logging'));
726 UI.updateViewClip();
727 UI.updateViewDrag();
728 //Util.Debug("<< settingsApply");
729 },
730
731 /* ------^-------
732 * /SETTINGS
733 * ==============
734 * PANELS
735 * ------v------*/
736
737 closeAllPanels: function() {
738 UI.closeSettingsPanel();
739 UI.closeXvpPanel();
740 UI.closeClipboardPanel();
741 UI.closeConnectPanel();
742 UI.closeExtraKeys();
743 },
744
745 /* ------^-------
746 * /PANELS
747 * ==============
748 * SETTINGS (panel)
749 * ------v------*/
750
751 openSettingsPanel: function() {
752 UI.closeAllPanels();
753 UI.openControlbar();
754
755 UI.updateSetting('encrypt');
756 UI.updateSetting('true_color');
757 if (Util.browserSupportsCursorURIs()) {
758 UI.updateSetting('cursor');
759 } else {
760 UI.updateSetting('cursor', !UI.isTouchDevice);
761 document.getElementById('noVNC_setting_cursor').disabled = true;
762 }
763 UI.updateSetting('clip');
764 UI.updateSetting('resize');
765 UI.updateSetting('shared');
766 UI.updateSetting('view_only');
767 UI.updateSetting('path');
768 UI.updateSetting('repeaterID');
769 UI.updateSetting('stylesheet');
770 UI.updateSetting('logging');
771
772 document.getElementById('noVNC_settings')
773 .classList.add("noVNC_open");
774 document.getElementById('noVNC_settings_button')
775 .classList.add("noVNC_selected");
776 },
777
778 closeSettingsPanel: function() {
779 document.getElementById('noVNC_settings')
780 .classList.remove("noVNC_open");
781 document.getElementById('noVNC_settings_button')
782 .classList.remove("noVNC_selected");
783 },
784
785 // Toggle the settings menu:
786 // On open, settings are refreshed from saved cookies.
787 // On close, settings are applied
788 toggleSettingsPanel: function() {
789 if (document.getElementById('noVNC_settings')
790 .classList.contains("noVNC_open")) {
791 UI.settingsApply();
792 UI.closeSettingsPanel();
793 } else {
794 UI.openSettingsPanel();
795 }
796 },
797
798 /* ------^-------
799 * /SETTINGS
800 * ==============
801 * XVP
802 * ------v------*/
803
804 openXvpPanel: function() {
805 UI.closeAllPanels();
806 UI.openControlbar();
807
808 document.getElementById('noVNC_xvp')
809 .classList.add("noVNC_open");
810 document.getElementById('noVNC_xvp_button')
811 .classList.add("noVNC_selected");
812 },
813
814 closeXvpPanel: function() {
815 document.getElementById('noVNC_xvp')
816 .classList.remove("noVNC_open");
817 document.getElementById('noVNC_xvp_button')
818 .classList.remove("noVNC_selected");
819 },
820
821 toggleXvpPanel: function() {
822 if (document.getElementById('noVNC_xvp')
823 .classList.contains("noVNC_open")) {
824 UI.closeXvpPanel();
825 } else {
826 UI.openXvpPanel();
827 }
828 },
829
830 // Disable/enable XVP button
831 updateXvpButton: function(ver) {
832 if (ver >= 1) {
833 document.getElementById('noVNC_xvp_button')
834 .classList.remove("noVNC_hidden");
835 } else {
836 document.getElementById('noVNC_xvp_button')
837 .classList.add("noVNC_hidden");
838 // Close XVP panel if open
839 UI.closeXvpPanel();
840 }
841 },
842
843 /* ------^-------
844 * /XVP
845 * ==============
846 * CLIPBOARD
847 * ------v------*/
848
849 openClipboardPanel: function() {
850 UI.closeAllPanels();
851 UI.openControlbar();
852
853 document.getElementById('noVNC_clipboard')
854 .classList.add("noVNC_open");
855 document.getElementById('noVNC_clipboard_button')
856 .classList.add("noVNC_selected");
857 },
858
859 closeClipboardPanel: function() {
860 document.getElementById('noVNC_clipboard')
861 .classList.remove("noVNC_open");
862 document.getElementById('noVNC_clipboard_button')
863 .classList.remove("noVNC_selected");
864 },
865
866 toggleClipboardPanel: function() {
867 if (document.getElementById('noVNC_clipboard')
868 .classList.contains("noVNC_open")) {
869 UI.closeClipboardPanel();
870 } else {
871 UI.openClipboardPanel();
872 }
873 },
874
875 clipboardReceive: function(rfb, text) {
876 Util.Debug(">> UI.clipboardReceive: " + text.substr(0,40) + "...");
877 document.getElementById('noVNC_clipboard_text').value = text;
878 Util.Debug("<< UI.clipboardReceive");
879 },
880
881 clipboardClear: function() {
882 document.getElementById('noVNC_clipboard_text').value = "";
883 UI.rfb.clipboardPasteFrom("");
884 },
885
886 clipboardSend: function() {
887 var text = document.getElementById('noVNC_clipboard_text').value;
888 Util.Debug(">> UI.clipboardSend: " + text.substr(0,40) + "...");
889 UI.rfb.clipboardPasteFrom(text);
890 Util.Debug("<< UI.clipboardSend");
891 },
892
893 /* ------^-------
894 * /CLIPBOARD
895 * ==============
896 * CONNECTION
897 * ------v------*/
898
899 openConnectPanel: function() {
900 UI.closeAllPanels();
901 UI.openControlbar();
902
903 document.getElementById('noVNC_connect_controls')
904 .classList.add("noVNC_open");
905 document.getElementById('noVNC_connect_controls_button')
906 .classList.add("noVNC_selected");
907
908 document.getElementById('noVNC_setting_host').focus();
909 },
910
911 closeConnectPanel: function() {
912 document.getElementById('noVNC_connect_controls')
913 .classList.remove("noVNC_open");
914 document.getElementById('noVNC_connect_controls_button')
915 .classList.remove("noVNC_selected");
916
917 UI.saveSetting('host');
918 UI.saveSetting('port');
919 UI.saveSetting('token');
920 //UI.saveSetting('password');
921 },
922
923 toggleConnectPanel: function() {
924 if (document.getElementById('noVNC_connect_controls')
925 .classList.contains("noVNC_open")) {
926 UI.closeConnectPanel();
927 } else {
928 UI.openConnectPanel();
929 }
930 },
931
932 connect: function() {
933 UI.closeAllPanels();
934
935 var host = document.getElementById('noVNC_setting_host').value;
936 var port = document.getElementById('noVNC_setting_port').value;
937 var password = document.getElementById('noVNC_setting_password').value;
938 var token = document.getElementById('noVNC_setting_token').value;
939 var path = document.getElementById('noVNC_setting_path').value;
940
941 //if token is in path then ignore the new token variable
942 if (token) {
943 path = WebUtil.injectParamIfMissing(path, "token", token);
944 }
945
946 if ((!host) || (!port)) {
947 throw new Error("Must set host and port");
948 }
949
950 if (!UI.initRFB()) return;
951
952 UI.rfb.set_encrypt(UI.getSetting('encrypt'));
953 UI.rfb.set_true_color(UI.getSetting('true_color'));
954 UI.rfb.set_local_cursor(UI.getSetting('cursor'));
955 UI.rfb.set_shared(UI.getSetting('shared'));
956 UI.rfb.set_view_only(UI.getSetting('view_only'));
957 UI.rfb.set_repeaterID(UI.getSetting('repeaterID'));
958
959 UI.rfb.connect(host, port, password, path);
960 },
961
962 disconnect: function() {
963 UI.closeAllPanels();
964 UI.rfb.disconnect();
965
966 // Restore the callback used for initial resize
967 UI.rfb.set_onFBUComplete(UI.initialResize);
968
969 // Don't display the connection settings until we're actually disconnected
970 },
971
972 /* ------^-------
973 * /CONNECTION
974 * ==============
975 * PASSWORD
976 * ------v------*/
977
978 passwordRequired: function(rfb, msg) {
979
980 document.getElementById('noVNC_password_dlg')
981 .classList.add('noVNC_open');
982
983 setTimeout(function () {
984 document.getElementById('noVNC_password_input').focus();
985 }, 100);
986
987 if (typeof msg === 'undefined') {
988 msg = "Password is required";
989 }
990 UI.updateState(null, "warning", null, msg);
991 },
992
993 setPassword: function() {
994 UI.rfb.sendPassword(document.getElementById('noVNC_password_input').value);
995 document.getElementById('noVNC_password_dlg')
996 .classList.remove('noVNC_open');
997 return false;
998 },
999
1000 /* ------^-------
1001 * /PASSWORD
1002 * ==============
1003 * FULLSCREEN
1004 * ------v------*/
1005
1006 toggleFullscreen: function() {
1007 if (document.fullscreenElement || // alternative standard method
1008 document.mozFullScreenElement || // currently working methods
1009 document.webkitFullscreenElement ||
1010 document.msFullscreenElement) {
1011 if (document.exitFullscreen) {
1012 document.exitFullscreen();
1013 } else if (document.mozCancelFullScreen) {
1014 document.mozCancelFullScreen();
1015 } else if (document.webkitExitFullscreen) {
1016 document.webkitExitFullscreen();
1017 } else if (document.msExitFullscreen) {
1018 document.msExitFullscreen();
1019 }
1020 } else {
1021 if (document.documentElement.requestFullscreen) {
1022 document.documentElement.requestFullscreen();
1023 } else if (document.documentElement.mozRequestFullScreen) {
1024 document.documentElement.mozRequestFullScreen();
1025 } else if (document.documentElement.webkitRequestFullscreen) {
1026 document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
1027 } else if (document.body.msRequestFullscreen) {
1028 document.body.msRequestFullscreen();
1029 }
1030 }
1031 UI.enableDisableViewClip();
1032 UI.updateFullscreenButton();
1033 },
1034
1035 updateFullscreenButton: function() {
1036 if (document.fullscreenElement || // alternative standard method
1037 document.mozFullScreenElement || // currently working methods
1038 document.webkitFullscreenElement ||
1039 document.msFullscreenElement ) {
1040 document.getElementById('noVNC_fullscreen_button')
1041 .classList.add("noVNC_selected");
1042 } else {
1043 document.getElementById('noVNC_fullscreen_button')
1044 .classList.remove("noVNC_selected");
1045 }
1046 },
1047
1048 /* ------^-------
1049 * /FULLSCREEN
1050 * ==============
1051 * RESIZE
1052 * ------v------*/
1053
1054 // Apply remote resizing or local scaling
1055 applyResizeMode: function() {
1056 if (!UI.rfb) return;
1057
1058 var screen = UI.screenSize();
1059
1060 if (screen && UI.rfb_state === 'normal' && UI.rfb.get_display()) {
1061
1062 var display = UI.rfb.get_display();
1063 var resizeMode = UI.getSetting('resize');
1064
1065 if (resizeMode === 'remote') {
1066
1067 // Request changing the resolution of the remote display to
1068 // the size of the local browser viewport.
1069
1070 // In order to not send multiple requests before the browser-resize
1071 // is finished we wait 0.5 seconds before sending the request.
1072 clearTimeout(UI.resizeTimeout);
1073 UI.resizeTimeout = setTimeout(function(){
1074
1075 // Limit the viewport to the size of the browser window
1076 display.set_maxWidth(screen.w);
1077 display.set_maxHeight(screen.h);
1078
1079 Util.Debug('Attempting requestDesktopSize(' +
1080 screen.w + ', ' + screen.h + ')');
1081
1082 // Request a remote size covering the viewport
1083 UI.rfb.requestDesktopSize(screen.w, screen.h);
1084 }, 500);
1085
1086 } else if (resizeMode === 'scale' || resizeMode === 'downscale') {
1087 var downscaleOnly = resizeMode === 'downscale';
1088 var scaleRatio = display.autoscale(screen.w, screen.h, downscaleOnly);
1089 UI.rfb.get_mouse().set_scale(scaleRatio);
1090 Util.Debug('Scaling by ' + UI.rfb.get_mouse().get_scale());
1091 }
1092 }
1093 },
1094
1095 // The screen is always the same size as the available viewport
1096 // in the browser window minus the height of the control bar
1097 screenSize: function() {
1098 var screen = document.getElementById('noVNC_screen');
1099
1100 // Hide the scrollbars until the size is calculated
1101 screen.style.overflow = "hidden";
1102
1103 var pos = Util.getPosition(screen);
1104 var w = pos.width;
1105 var h = pos.height;
1106
1107 screen.style.overflow = "visible";
1108
1109 if (isNaN(w) || isNaN(h)) {
1110 return false;
1111 } else {
1112 return {w: w, h: h};
1113 }
1114 },
1115
1116 // Normally we only apply the current resize mode after a window resize
1117 // event. This means that when a new connection is opened, there is no
1118 // resize mode active.
1119 // We have to wait until the first FBU because this is where the client
1120 // will find the supported encodings of the server. Some calls later in
1121 // the chain is dependant on knowing the server-capabilities.
1122 initialResize: function(rfb, fbu) {
1123 UI.applyResizeMode();
1124 // After doing this once, we remove the callback.
1125 UI.rfb.set_onFBUComplete(function() { });
1126 },
1127
1128 /* ------^-------
1129 * /RESIZE
1130 * ==============
1131 * CLIPPING
1132 * ------v------*/
1133
1134 // Set and configure viewport clipping
1135 setViewClip: function(clip) {
1136 UI.updateSetting('clip', clip);
1137 UI.updateViewClip();
1138 },
1139
1140 // Update parameters that depend on the clip setting
1141 updateViewClip: function() {
1142 var display;
1143 if (!UI.rfb) return;
1144
1145 var display = UI.rfb.get_display();
1146 var cur_clip = display.get_viewport();
1147 var new_clip = UI.getSetting('clip');
1148
1149 if (cur_clip !== new_clip) {
1150 display.set_viewport(new_clip);
1151 }
1152
1153 var size = UI.screenSize();
1154
1155 if (new_clip && size) {
1156 // When clipping is enabled, the screen is limited to
1157 // the size of the browser window.
1158 display.set_maxWidth(size.w);
1159 display.set_maxHeight(size.h);
1160
1161 var screen = document.getElementById('noVNC_screen');
1162 var canvas = document.getElementById('noVNC_canvas');
1163
1164 // Hide potential scrollbars that can skew the position
1165 screen.style.overflow = "hidden";
1166
1167 // The x position marks the left margin of the canvas,
1168 // remove the margin from both sides to keep it centered.
1169 var new_w = size.w - (2 * Util.getPosition(canvas).x);
1170
1171 screen.style.overflow = "visible";
1172
1173 display.viewportChangeSize(new_w, size.h);
1174 } else {
1175 // Disable max dimensions
1176 display.set_maxWidth(0);
1177 display.set_maxHeight(0);
1178 display.viewportChangeSize();
1179 }
1180 },
1181
1182 // Handle special cases where clipping is forced on/off or locked
1183 enableDisableViewClip: function() {
1184 var resizeSetting = document.getElementById('noVNC_setting_resize');
1185 var connected = UI.rfb && UI.rfb_state === 'normal';
1186
1187 if (UI.isSafari) {
1188 // Safari auto-hides the scrollbars which makes them
1189 // impossible to use in most cases
1190 UI.setViewClip(true);
1191 document.getElementById('noVNC_setting_clip').disabled = true;
1192 } else if (resizeSetting.value === 'downscale' || resizeSetting.value === 'scale') {
1193 // Disable clipping if we are scaling
1194 UI.setViewClip(false);
1195 document.getElementById('noVNC_setting_clip').disabled = true;
1196 } else if (document.msFullscreenElement) {
1197 // The browser is IE and we are in fullscreen mode.
1198 // - We need to force clipping while in fullscreen since
1199 // scrollbars doesn't work.
1200 UI.showStatus("Forcing clipping mode since scrollbars aren't supported by IE in fullscreen");
1201 UI.rememberedClipSetting = UI.getSetting('clip');
1202 UI.setViewClip(true);
1203 document.getElementById('noVNC_setting_clip').disabled = true;
1204 } else if (document.body.msRequestFullscreen && UI.rememberedClip !== null) {
1205 // Restore view clip to what it was before fullscreen on IE
1206 UI.setViewClip(UI.rememberedClipSetting);
1207 document.getElementById('noVNC_setting_clip').disabled = connected || UI.isTouchDevice;
1208 } else {
1209 document.getElementById('noVNC_setting_clip').disabled = connected || UI.isTouchDevice;
1210 if (UI.isTouchDevice) {
1211 UI.setViewClip(true);
1212 }
1213 }
1214 },
1215
1216 /* ------^-------
1217 * /CLIPPING
1218 * ==============
1219 * VIEWDRAG
1220 * ------v------*/
1221
1222 toggleViewDrag: function() {
1223 if (!UI.rfb) return;
1224
1225 var drag = UI.rfb.get_viewportDrag();
1226 UI.setViewDrag(!drag);
1227 },
1228
1229 // Set the view drag mode which moves the viewport on mouse drags
1230 setViewDrag: function(drag) {
1231 if (!UI.rfb) return;
1232
1233 UI.rfb.set_viewportDrag(drag);
1234
1235 UI.updateViewDrag();
1236 },
1237
1238 updateViewDrag: function() {
1239 var clipping = false;
1240
1241 if (UI.rfb_state !== 'normal') return;
1242
1243 // Check if viewport drag is possible. It is only possible
1244 // if the remote display is clipping the client display.
1245 if (UI.rfb.get_display().get_viewport() &&
1246 UI.rfb.get_display().clippingDisplay()) {
1247 clipping = true;
1248 }
1249
1250 var viewDragButton = document.getElementById('noVNC_view_drag_button');
1251
1252 if (!clipping &&
1253 UI.rfb.get_viewportDrag()) {
1254 // The size of the remote display is the same or smaller
1255 // than the client display. Make sure viewport drag isn't
1256 // active when it can't be used.
1257 UI.rfb.set_viewportDrag(false);
1258 }
1259
1260 if (UI.rfb.get_viewportDrag()) {
1261 viewDragButton.classList.add("noVNC_selected");
1262 } else {
1263 viewDragButton.classList.remove("noVNC_selected");
1264 }
1265
1266 // Different behaviour for touch vs non-touch
1267 // The button is disabled instead of hidden on touch devices
1268 if (UI.isTouchDevice) {
1269 viewDragButton.classList.remove("noVNC_hidden");
1270
1271 if (clipping) {
1272 viewDragButton.disabled = false;
1273 } else {
1274 viewDragButton.disabled = true;
1275 }
1276 } else {
1277 viewDragButton.disabled = false;
1278
1279 if (clipping) {
1280 viewDragButton.classList.remove("noVNC_hidden");
1281 } else {
1282 viewDragButton.classList.add("noVNC_hidden");
1283 }
1284 }
1285 },
1286
1287 /* ------^-------
1288 * /VIEWDRAG
1289 * ==============
1290 * KEYBOARD
1291 * ------v------*/
1292
1293 showVirtualKeyboard: function() {
1294 if (!UI.isTouchDevice) return;
1295
1296 var input = document.getElementById('noVNC_keyboardinput');
1297
1298 if (document.activeElement == input) return;
1299
1300 UI.keyboardVisible = true;
1301 document.getElementById('noVNC_keyboard_button')
1302 .classList.add("noVNC_selected");
1303 input.focus();
1304
1305 try {
1306 var l = input.value.length;
1307 // Move the caret to the end
1308 input.setSelectionRange(l, l);
1309 } catch (err) {} // setSelectionRange is undefined in Google Chrome
1310 },
1311
1312 hideVirtualKeyboard: function() {
1313 if (!UI.isTouchDevice) return;
1314
1315 var input = document.getElementById('noVNC_keyboardinput');
1316
1317 if (document.activeElement != input) return;
1318
1319 input.blur();
1320 },
1321
1322 toggleVirtualKeyboard: function () {
1323 if (UI.keyboardVisible) {
1324 UI.hideVirtualKeyboard();
1325 } else {
1326 UI.showVirtualKeyboard();
1327 }
1328 },
1329
1330 onblurVirtualKeyboard: function() {
1331 //Weird bug in iOS if you change keyboardVisible
1332 //here it does not actually occur so next time
1333 //you click keyboard icon it doesnt work.
1334 UI.hideKeyboardTimeout = setTimeout(function() {
1335 UI.keyboardVisible = false;
1336 document.getElementById('noVNC_keyboard_button')
1337 .classList.remove("noVNC_selected");
1338 },100);
1339 },
1340
1341 keepKeyboard: function() {
1342 clearTimeout(UI.hideKeyboardTimeout);
1343 if(UI.keyboardVisible === true) {
1344 UI.showVirtualKeyboard();
1345 } else if(UI.keyboardVisible === false) {
1346 UI.hideVirtualKeyboard();
1347 }
1348 },
1349
1350 keyboardinputReset: function() {
1351 var kbi = document.getElementById('noVNC_keyboardinput');
1352 kbi.value = new Array(UI.defaultKeyboardinputLen).join("_");
1353 UI.lastKeyboardinput = kbi.value;
1354 },
1355
1356 // When normal keyboard events are left uncought, use the input events from
1357 // the keyboardinput element instead and generate the corresponding key events.
1358 // This code is required since some browsers on Android are inconsistent in
1359 // sending keyCodes in the normal keyboard events when using on screen keyboards.
1360 keyInput: function(event) {
1361
1362 if (!UI.rfb) return;
1363
1364 var newValue = event.target.value;
1365
1366 if (!UI.lastKeyboardinput) {
1367 UI.keyboardinputReset();
1368 }
1369 var oldValue = UI.lastKeyboardinput;
1370
1371 var newLen;
1372 try {
1373 // Try to check caret position since whitespace at the end
1374 // will not be considered by value.length in some browsers
1375 newLen = Math.max(event.target.selectionStart, newValue.length);
1376 } catch (err) {
1377 // selectionStart is undefined in Google Chrome
1378 newLen = newValue.length;
1379 }
1380 var oldLen = oldValue.length;
1381
1382 var backspaces;
1383 var inputs = newLen - oldLen;
1384 if (inputs < 0) {
1385 backspaces = -inputs;
1386 } else {
1387 backspaces = 0;
1388 }
1389
1390 // Compare the old string with the new to account for
1391 // text-corrections or other input that modify existing text
1392 var i;
1393 for (i = 0; i < Math.min(oldLen, newLen); i++) {
1394 if (newValue.charAt(i) != oldValue.charAt(i)) {
1395 inputs = newLen - i;
1396 backspaces = oldLen - i;
1397 break;
1398 }
1399 }
1400
1401 // Send the key events
1402 for (i = 0; i < backspaces; i++) {
1403 UI.rfb.sendKey(KeyTable.XK_BackSpace);
1404 }
1405 for (i = newLen - inputs; i < newLen; i++) {
1406 UI.rfb.sendKey(newValue.charCodeAt(i));
1407 }
1408
1409 // Control the text content length in the keyboardinput element
1410 if (newLen > 2 * UI.defaultKeyboardinputLen) {
1411 UI.keyboardinputReset();
1412 } else if (newLen < 1) {
1413 // There always have to be some text in the keyboardinput
1414 // element with which backspace can interact.
1415 UI.keyboardinputReset();
1416 // This sometimes causes the keyboard to disappear for a second
1417 // but it is required for the android keyboard to recognize that
1418 // text has been added to the field
1419 event.target.blur();
1420 // This has to be ran outside of the input handler in order to work
1421 setTimeout(UI.keepKeyboard, 0);
1422 } else {
1423 UI.lastKeyboardinput = newValue;
1424 }
1425 },
1426
1427 /* ------^-------
1428 * /KEYBOARD
1429 * ==============
1430 * EXTRA KEYS
1431 * ------v------*/
1432
1433 openExtraKeys: function() {
1434 UI.closeAllPanels();
1435 UI.openControlbar();
1436
1437 document.getElementById('noVNC_modifiers')
1438 .classList.add("noVNC_open");
1439 document.getElementById('noVNC_toggle_extra_keys_button')
1440 .classList.add("noVNC_selected");
1441 },
1442
1443 closeExtraKeys: function() {
1444 document.getElementById('noVNC_modifiers')
1445 .classList.remove("noVNC_open");
1446 document.getElementById('noVNC_toggle_extra_keys_button')
1447 .classList.remove("noVNC_selected");
1448 },
1449
1450 toggleExtraKeys: function() {
1451 UI.keepKeyboard();
1452 if(document.getElementById('noVNC_modifiers')
1453 .classList.contains("noVNC_open")) {
1454 UI.closeExtraKeys();
1455 } else {
1456 UI.openExtraKeys();
1457 }
1458 },
1459
1460 sendEsc: function() {
1461 UI.keepKeyboard();
1462 UI.rfb.sendKey(KeyTable.XK_Escape);
1463 },
1464
1465 sendTab: function() {
1466 UI.keepKeyboard();
1467 UI.rfb.sendKey(KeyTable.XK_Tab);
1468 },
1469
1470 toggleCtrl: function() {
1471 UI.keepKeyboard();
1472 var btn = document.getElementById('noVNC_toggle_ctrl_button');
1473 if (btn.classList.contains("noVNC_selected")) {
1474 UI.rfb.sendKey(KeyTable.XK_Control_L, false);
1475 btn.classList.remove("noVNC_selected");
1476 } else {
1477 UI.rfb.sendKey(KeyTable.XK_Control_L, true);
1478 btn.classList.add("noVNC_selected");
1479 }
1480 },
1481
1482 toggleAlt: function() {
1483 UI.keepKeyboard();
1484 var btn = document.getElementById('noVNC_toggle_alt_button');
1485 if (btn.classList.contains("noVNC_selected")) {
1486 UI.rfb.sendKey(KeyTable.XK_Alt_L, false);
1487 btn.classList.remove("noVNC_selected");
1488 } else {
1489 UI.rfb.sendKey(KeyTable.XK_Alt_L, true);
1490 btn.classList.add("noVNC_selected");
1491 }
1492 },
1493
1494 sendCtrlAltDel: function() {
1495 UI.keepKeyboard();
1496 UI.rfb.sendCtrlAltDel();
1497 },
1498
1499 /* ------^-------
1500 * /EXTRA KEYS
1501 * ==============
1502 * MISC
1503 * ------v------*/
1504
1505 setMouseButton: function(num) {
1506 if (UI.rfb) {
1507 UI.rfb.get_mouse().set_touchButton(num);
1508 }
1509
1510 var blist = [0, 1,2,4];
1511 for (var b = 0; b < blist.length; b++) {
1512 var button = document.getElementById('noVNC_mouse_button' + blist[b]);
1513 if (blist[b] === num) {
1514 button.classList.remove("noVNC_hidden");
1515 } else {
1516 button.classList.add("noVNC_hidden");
1517 }
1518 }
1519 },
1520
1521 displayBlur: function() {
1522 if (!UI.rfb) return;
1523
1524 UI.rfb.get_keyboard().set_focused(false);
1525 UI.rfb.get_mouse().set_focused(false);
1526 },
1527
1528 displayFocus: function() {
1529 if (!UI.rfb) return;
1530
1531 UI.rfb.get_keyboard().set_focused(true);
1532 UI.rfb.get_mouse().set_focused(true);
1533 },
1534
1535 // Display the desktop name in the document title
1536 updateDocumentTitle: function(rfb, name) {
1537 document.title = name + " - noVNC";
1538 },
1539
1540 //Helper to add options to dropdown.
1541 addOption: function(selectbox, text, value) {
1542 var optn = document.createElement("OPTION");
1543 optn.text = text;
1544 optn.value = value;
1545 selectbox.options.add(optn);
1546 },
1547
1548 /* ------^-------
1549 * /MISC
1550 * ==============
1551 */
1552 };
1553
1554 /* [module] UI.load(); */
1555 })();
1556
1557 /* [module] export default UI; */