/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
- * Copyright (C) 2016 Samuel Mannehed for Cendio AB
+ * Copyright (C) 2018 Samuel Mannehed for Cendio AB
* Copyright (C) 2016 Pierre Ossman for Cendio AB
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
-/* jslint white: false, browser: true */
-/* global window, document.getElementById, Util, WebUtil, RFB, Display */
-
import * as Log from '../core/util/logging.js';
-import _, { l10n } from '../core/util/localization.js';
-import { isTouchDevice, browserSupportsCursorURIs as cursorURIsSupported } from '../core/util/browsers.js';
+import _, { l10n } from './localization.js';
+import { isTouchDevice, dragThreshold } from '../core/util/browser.js';
import { setCapture, getPointerEvent } from '../core/util/events.js';
import KeyTable from "../core/input/keysym.js";
import keysyms from "../core/input/keysymdef.js";
import Keyboard from "../core/input/keyboard.js";
import RFB from "../core/rfb.js";
-import Display from "../core/display.js";
import * as WebUtil from "./webutil.js";
-var UI = {
+const UI = {
connected: false,
desktopName: "",
- resizeTimeout: null,
statusTimeout: null,
hideKeyboardTimeout: null,
idleControlbarTimeout: null,
reconnect_callback: null,
reconnect_password: null,
- prime: function(callback) {
+ prime(callback) {
if (document.readyState === "interactive" || document.readyState === "complete") {
UI.load(callback);
} else {
// Setup rfb object, load settings from browser storage, then call
// UI.init to setup the UI/menus
- load: function(callback) {
+ load(callback) {
WebUtil.initSettings(UI.start, callback);
},
// Render default UI and initialize settings menu
- start: function(callback) {
+ start(callback) {
// Setup global variables first
UI.isSafari = (navigator.userAgent.indexOf('Safari') !== -1 &&
if (isTouchDevice) {
document.documentElement.classList.add("noVNC_touch");
// Remove the address bar
- setTimeout(function() { window.scrollTo(0, 1); }, 100);
+ setTimeout(() => window.scrollTo(0, 1), 100);
}
// Restore control bar position
UI.initFullscreen();
// Setup event handlers
- UI.addResizeHandlers();
UI.addControlbarHandlers();
UI.addTouchSpecificHandlers();
UI.addExtraKeysHandlers();
- UI.addXvpHandlers();
+ UI.addMachineHandlers();
UI.addConnectionControlHandlers();
UI.addClipboardHandlers();
UI.addSettingsHandlers();
UI.openControlbar();
- UI.updateViewClip();
-
- UI.updateVisualState();
+ UI.updateVisualState('init');
document.documentElement.classList.remove("noVNC_loading");
- var autoconnect = WebUtil.getConfigVar('autoconnect', false);
+ let autoconnect = WebUtil.getConfigVar('autoconnect', false);
if (autoconnect === 'true' || autoconnect == '1') {
autoconnect = true;
UI.connect();
}
},
- initFullscreen: function() {
+ initFullscreen() {
// Only show the button if fullscreen is properly supported
// * Safari doesn't support alphanumerical input while in fullscreen
if (!UI.isSafari &&
}
},
- initSettings: function() {
- var i;
-
+ initSettings() {
// Logging selection dropdown
- var llevels = ['error', 'warn', 'info', 'debug'];
- for (i = 0; i < llevels.length; i += 1) {
- UI.addOption(document.getElementById('noVNC_setting_logging'),llevels[i], llevels[i]);
+ const llevels = ['error', 'warn', 'info', 'debug'];
+ for (let i = 0; i < llevels.length; i += 1) {
+ UI.addOption(document.getElementById('noVNC_setting_logging'), llevels[i], llevels[i]);
}
// Settings with immediate effects
// if port == 80 (or 443) then it won't be present and should be
// set manually
- var port = window.location.port;
+ let port = window.location.port;
if (!port) {
- if (window.location.protocol.substring(0,5) == 'https') {
+ if (window.location.protocol.substring(0, 5) == 'https') {
port = 443;
- }
- else if (window.location.protocol.substring(0,4) == 'http') {
+ } else if (window.location.protocol.substring(0, 4) == 'http') {
port = 80;
}
}
UI.initSetting('host', window.location.hostname);
UI.initSetting('port', port);
UI.initSetting('encrypt', (window.location.protocol === "https:"));
- UI.initSetting('cursor', !isTouchDevice);
UI.initSetting('view_clip', false);
UI.initSetting('resize', 'off');
UI.initSetting('shared', true);
UI.setupSettingLabels();
},
// Adds a link to the label elements on the corresponding input elements
- setupSettingLabels: function() {
- var labels = document.getElementsByTagName('LABEL');
- for (var i = 0; i < labels.length; i++) {
- var htmlFor = labels[i].htmlFor;
+ setupSettingLabels() {
+ const labels = document.getElementsByTagName('LABEL');
+ for (let i = 0; i < labels.length; i++) {
+ const htmlFor = labels[i].htmlFor;
if (htmlFor != '') {
- var elem = document.getElementById(htmlFor);
+ const elem = document.getElementById(htmlFor);
if (elem) elem.label = labels[i];
} else {
// If 'for' isn't set, use the first input element child
- var children = labels[i].children;
- for (var j = 0; j < children.length; j++) {
+ const children = labels[i].children;
+ for (let j = 0; j < children.length; j++) {
if (children[j].form !== undefined) {
children[j].label = labels[i];
break;
}
},
- initRFB: function() {
- try {
- UI.rfb = new RFB({'target': document.getElementById('noVNC_canvas'),
- 'onNotification': UI.notification,
- 'onUpdateState': UI.updateState,
- 'onDisconnected': UI.disconnectFinished,
- 'onCredentialsRequired': UI.credentials,
- 'onXvpInit': UI.updateXvpButton,
- 'onClipboard': UI.clipboardReceive,
- 'onBell': UI.bell,
- 'onFBUComplete': UI.initialResize,
- 'onFBResize': UI.updateSessionSize,
- 'onDesktopName': UI.updateDesktopName});
- return true;
- } catch (exc) {
- var msg = "Unable to create RFB client -- " + exc;
- Log.Error(msg);
- UI.showStatus(msg, 'error');
- return false;
- }
- },
-
/* ------^-------
* /INIT
* ==============
* EVENT HANDLERS
* ------v------*/
- addResizeHandlers: function() {
- window.addEventListener('resize', UI.applyResizeMode);
- window.addEventListener('resize', UI.updateViewClip);
- },
-
- addControlbarHandlers: function() {
+ addControlbarHandlers() {
document.getElementById("noVNC_control_bar")
.addEventListener('mousemove', UI.activateControlbar);
document.getElementById("noVNC_control_bar")
// resize events aren't available for elements
window.addEventListener('resize', UI.updateControlbarHandle);
- var exps = document.getElementsByClassName("noVNC_expander");
- for (var i = 0;i < exps.length;i++) {
+ const exps = document.getElementsByClassName("noVNC_expander");
+ for (let i = 0;i < exps.length;i++) {
exps[i].addEventListener('click', UI.toggleExpander);
}
},
- addTouchSpecificHandlers: function() {
+ addTouchSpecificHandlers() {
document.getElementById("noVNC_mouse_button0")
- .addEventListener('click', function () { UI.setMouseButton(1); });
+ .addEventListener('click', () => UI.setMouseButton(1));
document.getElementById("noVNC_mouse_button1")
- .addEventListener('click', function () { UI.setMouseButton(2); });
+ .addEventListener('click', () => UI.setMouseButton(2));
document.getElementById("noVNC_mouse_button2")
- .addEventListener('click', function () { UI.setMouseButton(4); });
+ .addEventListener('click', () => UI.setMouseButton(4));
document.getElementById("noVNC_mouse_button4")
- .addEventListener('click', function () { UI.setMouseButton(0); });
+ .addEventListener('click', () => UI.setMouseButton(0));
document.getElementById("noVNC_keyboard_button")
.addEventListener('click', UI.toggleVirtualKeyboard);
- UI.touchKeyboard = new Keyboard({target: document.getElementById('noVNC_keyboardinput'),
- onKeyEvent: UI.keyEvent});
+ UI.touchKeyboard = new Keyboard(document.getElementById('noVNC_keyboardinput'));
+ UI.touchKeyboard.onkeyevent = UI.keyEvent;
UI.touchKeyboard.grab();
document.getElementById("noVNC_keyboardinput")
.addEventListener('input', UI.keyInput);
document.getElementById("noVNC_keyboardinput")
.addEventListener('blur', UI.onblurVirtualKeyboard);
document.getElementById("noVNC_keyboardinput")
- .addEventListener('submit', function () { return false; });
+ .addEventListener('submit', () => false);
document.documentElement
.addEventListener('mousedown', UI.keepVirtualKeyboard, true);
- document.documentElement
- .addEventListener('touchstart', UI.keepVirtualKeyboard, true);
document.getElementById("noVNC_control_bar")
.addEventListener('touchstart', UI.activateControlbar);
.addEventListener('touchmove', UI.dragControlbarHandle);
},
- addExtraKeysHandlers: function() {
+ addExtraKeysHandlers() {
document.getElementById("noVNC_toggle_extra_keys_button")
.addEventListener('click', UI.toggleExtraKeys);
document.getElementById("noVNC_toggle_ctrl_button")
.addEventListener('click', UI.sendCtrlAltDel);
},
- addXvpHandlers: function() {
- document.getElementById("noVNC_xvp_shutdown_button")
- .addEventListener('click', function() { UI.rfb.xvpShutdown(); });
- document.getElementById("noVNC_xvp_reboot_button")
- .addEventListener('click', function() { UI.rfb.xvpReboot(); });
- document.getElementById("noVNC_xvp_reset_button")
- .addEventListener('click', function() { UI.rfb.xvpReset(); });
- document.getElementById("noVNC_xvp_button")
- .addEventListener('click', UI.toggleXvpPanel);
+ addMachineHandlers() {
+ document.getElementById("noVNC_shutdown_button")
+ .addEventListener('click', () => UI.rfb.machineShutdown());
+ document.getElementById("noVNC_reboot_button")
+ .addEventListener('click', () => UI.rfb.machineReboot());
+ document.getElementById("noVNC_reset_button")
+ .addEventListener('click', () => UI.rfb.machineReset());
+ document.getElementById("noVNC_power_button")
+ .addEventListener('click', UI.togglePowerPanel);
},
- addConnectionControlHandlers: function() {
+ addConnectionControlHandlers() {
document.getElementById("noVNC_disconnect_button")
.addEventListener('click', UI.disconnect);
document.getElementById("noVNC_connect_button")
.addEventListener('click', UI.setPassword);
},
- addClipboardHandlers: function() {
+ addClipboardHandlers() {
document.getElementById("noVNC_clipboard_button")
.addEventListener('click', UI.toggleClipboardPanel);
document.getElementById("noVNC_clipboard_text")
// Add a call to save settings when the element changes,
// unless the optional parameter changeFunc is used instead.
- addSettingChangeHandler: function(name, changeFunc) {
- var settingElem = document.getElementById("noVNC_setting_" + name);
+ addSettingChangeHandler(name, changeFunc) {
+ const settingElem = document.getElementById("noVNC_setting_" + name);
if (changeFunc === undefined) {
- changeFunc = function () { UI.saveSetting(name); };
+ changeFunc = () => UI.saveSetting(name);
}
settingElem.addEventListener('change', changeFunc);
},
- addSettingsHandlers: function() {
+ addSettingsHandlers() {
document.getElementById("noVNC_settings_button")
.addEventListener('click', UI.toggleSettingsPanel);
UI.addSettingChangeHandler('encrypt');
- UI.addSettingChangeHandler('cursor');
- UI.addSettingChangeHandler('cursor', UI.updateLocalCursor);
UI.addSettingChangeHandler('resize');
- UI.addSettingChangeHandler('resize', UI.enableDisableViewClip);
UI.addSettingChangeHandler('resize', UI.applyResizeMode);
+ UI.addSettingChangeHandler('resize', UI.updateViewClip);
UI.addSettingChangeHandler('view_clip');
UI.addSettingChangeHandler('view_clip', UI.updateViewClip);
UI.addSettingChangeHandler('shared');
UI.addSettingChangeHandler('reconnect_delay');
},
- addFullscreenHandlers: function() {
+ addFullscreenHandlers() {
document.getElementById("noVNC_fullscreen_button")
.addEventListener('click', UI.toggleFullscreen);
* VISUAL
* ------v------*/
- updateState: function(rfb, state, oldstate) {
- var msg;
+ // Disable/enable controls depending on connection state
+ updateVisualState(state) {
document.documentElement.classList.remove("noVNC_connecting");
document.documentElement.classList.remove("noVNC_connected");
document.documentElement.classList.remove("noVNC_disconnecting");
document.documentElement.classList.remove("noVNC_reconnecting");
+ const transition_elem = document.getElementById("noVNC_transition_text");
switch (state) {
+ case 'init':
+ break;
case 'connecting':
- document.getElementById("noVNC_transition_text").textContent = _("Connecting...");
+ transition_elem.textContent = _("Connecting...");
document.documentElement.classList.add("noVNC_connecting");
break;
case 'connected':
- UI.connected = true;
- UI.inhibit_reconnect = false;
document.documentElement.classList.add("noVNC_connected");
- if (rfb && rfb.get_encrypt()) {
- msg = _("Connected (encrypted) to ") + UI.desktopName;
- } else {
- msg = _("Connected (unencrypted) to ") + UI.desktopName;
- }
- UI.showStatus(msg);
- document.getElementById('noVNC_canvas').focus();
break;
case 'disconnecting':
- UI.connected = false;
- document.getElementById("noVNC_transition_text").textContent = _("Disconnecting...");
+ transition_elem.textContent = _("Disconnecting...");
document.documentElement.classList.add("noVNC_disconnecting");
break;
case 'disconnected':
- UI.showStatus(_("Disconnected"));
break;
- default:
- msg = "Invalid UI state";
- Log.Error(msg);
- UI.showStatus(msg, 'error');
+ case 'reconnecting':
+ transition_elem.textContent = _("Reconnecting...");
+ document.documentElement.classList.add("noVNC_reconnecting");
break;
- }
-
- UI.updateVisualState();
- },
-
- // Disable/enable controls depending on connection state
- updateVisualState: function() {
- //Log.Debug(">> updateVisualState");
-
- UI.enableDisableViewClip();
-
- if (cursorURIsSupported() && !isTouchDevice) {
- UI.enableSetting('cursor');
- } else {
- UI.disableSetting('cursor');
+ default:
+ Log.Error("Invalid visual state: " + state);
+ UI.showStatus(_("Internal error"), 'error');
+ return;
}
if (UI.connected) {
+ UI.updateViewClip();
+
UI.disableSetting('encrypt');
UI.disableSetting('shared');
UI.disableSetting('host');
UI.disableSetting('port');
UI.disableSetting('path');
UI.disableSetting('repeaterID');
- UI.updateViewClip();
UI.setMouseButton(1);
// Hide the controlbar after 2 seconds
UI.enableSetting('port');
UI.enableSetting('path');
UI.enableSetting('repeaterID');
- UI.updateXvpButton(0);
+ UI.updatePowerButton();
UI.keepControlbar();
}
- // Hide input related buttons in view only mode
- if (UI.rfb && UI.rfb.get_view_only()) {
- document.getElementById('noVNC_keyboard_button')
- .classList.add('noVNC_hidden');
- document.getElementById('noVNC_toggle_extra_keys_button')
- .classList.add('noVNC_hidden');
- } else {
- document.getElementById('noVNC_keyboard_button')
- .classList.remove('noVNC_hidden');
- document.getElementById('noVNC_toggle_extra_keys_button')
- .classList.remove('noVNC_hidden');
- }
-
- // State change disables viewport dragging.
- // It is enabled (toggled) by direct click on the button
- UI.setViewDrag(false);
-
- // State change also closes the password dialog
+ // State change closes the password dialog
document.getElementById('noVNC_password_dlg')
.classList.remove('noVNC_open');
-
- //Log.Debug("<< updateVisualState");
},
- showStatus: function(text, status_type, time) {
- var statusElem = document.getElementById('noVNC_status');
+ showStatus(text, status_type, time) {
+ const statusElem = document.getElementById('noVNC_status');
clearTimeout(UI.statusTimeout);
status_type = 'normal';
}
- statusElem.classList.remove("noVNC_status_normal");
- statusElem.classList.remove("noVNC_status_warn");
- statusElem.classList.remove("noVNC_status_error");
+ // Don't overwrite more severe visible statuses and never
+ // errors. Only shows the first error.
+ let visible_status_type = 'none';
+ if (statusElem.classList.contains("noVNC_open")) {
+ if (statusElem.classList.contains("noVNC_status_error")) {
+ visible_status_type = 'error';
+ } else if (statusElem.classList.contains("noVNC_status_warn")) {
+ visible_status_type = 'warn';
+ } else {
+ visible_status_type = 'normal';
+ }
+ }
+ if (visible_status_type === 'error' ||
+ (visible_status_type === 'warn' && status_type === 'normal')) {
+ return;
+ }
switch (status_type) {
+ case 'error':
+ statusElem.classList.remove("noVNC_status_warn");
+ statusElem.classList.remove("noVNC_status_normal");
+ statusElem.classList.add("noVNC_status_error");
+ break;
case 'warning':
case 'warn':
+ statusElem.classList.remove("noVNC_status_error");
+ statusElem.classList.remove("noVNC_status_normal");
statusElem.classList.add("noVNC_status_warn");
break;
- case 'error':
- statusElem.classList.add("noVNC_status_error");
- break;
case 'normal':
case 'info':
default:
+ statusElem.classList.remove("noVNC_status_error");
+ statusElem.classList.remove("noVNC_status_warn");
statusElem.classList.add("noVNC_status_normal");
break;
}
}
},
- hideStatus: function() {
+ hideStatus() {
clearTimeout(UI.statusTimeout);
document.getElementById('noVNC_status').classList.remove("noVNC_open");
},
- notification: function (rfb, msg, level, options) {
- UI.showStatus(msg, level);
- },
-
- activateControlbar: function(event) {
+ activateControlbar(event) {
clearTimeout(UI.idleControlbarTimeout);
// We manipulate the anchor instead of the actual control
// bar in order to avoid creating new a stacking group
UI.idleControlbarTimeout = window.setTimeout(UI.idleControlbar, 2000);
},
- idleControlbar: function() {
+ idleControlbar() {
document.getElementById('noVNC_control_bar_anchor')
.classList.add("noVNC_idle");
},
- keepControlbar: function() {
+ keepControlbar() {
clearTimeout(UI.closeControlbarTimeout);
},
- openControlbar: function() {
+ openControlbar() {
document.getElementById('noVNC_control_bar')
.classList.add("noVNC_open");
},
- closeControlbar: function() {
+ closeControlbar() {
UI.closeAllPanels();
document.getElementById('noVNC_control_bar')
.classList.remove("noVNC_open");
},
- toggleControlbar: function() {
+ toggleControlbar() {
if (document.getElementById('noVNC_control_bar')
.classList.contains("noVNC_open")) {
UI.closeControlbar();
}
},
- toggleControlbarSide: function () {
- // Temporarily disable animation to avoid weird movement
- var bar = document.getElementById('noVNC_control_bar');
- bar.style.transitionDuration = '0s';
- bar.addEventListener('transitionend', function () { this.style.transitionDuration = ""; });
+ toggleControlbarSide() {
+ // Temporarily disable animation, if bar is displayed, to avoid weird
+ // movement. The transitionend-event will not fire when display=none.
+ const bar = document.getElementById('noVNC_control_bar');
+ const barDisplayStyle = window.getComputedStyle(bar).display;
+ if (barDisplayStyle !== 'none') {
+ bar.style.transitionDuration = '0s';
+ bar.addEventListener('transitionend', () => bar.style.transitionDuration = '');
+ }
- var anchor = document.getElementById('noVNC_control_bar_anchor');
+ const anchor = document.getElementById('noVNC_control_bar_anchor');
if (anchor.classList.contains("noVNC_right")) {
WebUtil.writeSetting('controlbar_pos', 'left');
anchor.classList.remove("noVNC_right");
UI.controlbarDrag = true;
},
- showControlbarHint: function (show) {
- var hint = document.getElementById('noVNC_control_bar_hint');
+ showControlbarHint(show) {
+ const hint = document.getElementById('noVNC_control_bar_hint');
if (show) {
hint.classList.add("noVNC_active");
} else {
}
},
- dragControlbarHandle: function (e) {
+ dragControlbarHandle(e) {
if (!UI.controlbarGrabbed) return;
- var ptr = getPointerEvent(e);
+ const ptr = getPointerEvent(e);
- var anchor = document.getElementById('noVNC_control_bar_anchor');
+ const anchor = document.getElementById('noVNC_control_bar_anchor');
if (ptr.clientX < (window.innerWidth * 0.1)) {
if (anchor.classList.contains("noVNC_right")) {
UI.toggleControlbarSide();
}
if (!UI.controlbarDrag) {
- // The goal is to trigger on a certain physical width, the
- // devicePixelRatio brings us a bit closer but is not optimal.
- var dragThreshold = 10 * (window.devicePixelRatio || 1);
- var dragDistance = Math.abs(ptr.clientY - UI.controlbarMouseDownClientY);
+ const dragDistance = Math.abs(ptr.clientY - UI.controlbarMouseDownClientY);
if (dragDistance < dragThreshold) return;
UI.controlbarDrag = true;
}
- var eventY = ptr.clientY - UI.controlbarMouseDownOffsetY;
+ const eventY = ptr.clientY - UI.controlbarMouseDownOffsetY;
UI.moveControlbarHandle(eventY);
},
// Move the handle but don't allow any position outside the bounds
- moveControlbarHandle: function (viewportRelativeY) {
- var handle = document.getElementById("noVNC_control_bar_handle");
- var handleHeight = handle.getBoundingClientRect().height;
- var controlbarBounds = document.getElementById("noVNC_control_bar")
+ moveControlbarHandle(viewportRelativeY) {
+ const handle = document.getElementById("noVNC_control_bar_handle");
+ const handleHeight = handle.getBoundingClientRect().height;
+ const controlbarBounds = document.getElementById("noVNC_control_bar")
.getBoundingClientRect();
- var margin = 10;
+ const margin = 10;
// These heights need to be non-zero for the below logic to work
if (handleHeight === 0 || controlbarBounds.height === 0) {
return;
}
- var newY = viewportRelativeY;
+ let newY = viewportRelativeY;
// Check if the coordinates are outside the control bar
if (newY < controlbarBounds.top + margin) {
}
// The transform needs coordinates that are relative to the parent
- var parentRelativeY = newY - controlbarBounds.top;
+ const parentRelativeY = newY - controlbarBounds.top;
handle.style.transform = "translateY(" + parentRelativeY + "px)";
},
- updateControlbarHandle: function () {
+ updateControlbarHandle() {
// Since the control bar is fixed on the viewport and not the page,
// the move function expects coordinates relative the the viewport.
- var handle = document.getElementById("noVNC_control_bar_handle");
- var handleBounds = handle.getBoundingClientRect();
+ const handle = document.getElementById("noVNC_control_bar_handle");
+ const handleBounds = handle.getBoundingClientRect();
UI.moveControlbarHandle(handleBounds.top);
},
- controlbarHandleMouseUp: function(e) {
+ controlbarHandleMouseUp(e) {
if ((e.type == "mouseup") && (e.button != 0)) return;
// mouseup and mousedown on the same place toggles the controlbar
UI.showControlbarHint(false);
},
- controlbarHandleMouseDown: function(e) {
+ controlbarHandleMouseDown(e) {
if ((e.type == "mousedown") && (e.button != 0)) return;
- var ptr = getPointerEvent(e);
+ const ptr = getPointerEvent(e);
- var handle = document.getElementById("noVNC_control_bar_handle");
- var bounds = handle.getBoundingClientRect();
+ const handle = document.getElementById("noVNC_control_bar_handle");
+ const bounds = handle.getBoundingClientRect();
// Touch events have implicit capture
if (e.type === "mousedown") {
UI.activateControlbar();
},
- toggleExpander: function(e) {
+ toggleExpander(e) {
if (this.classList.contains("noVNC_open")) {
this.classList.remove("noVNC_open");
} else {
* ------v------*/
// Initial page load read/initialization of settings
- initSetting: function(name, defVal) {
+ initSetting(name, defVal) {
// Check Query string followed by cookie
- var val = WebUtil.getConfigVar(name);
+ let val = WebUtil.getConfigVar(name);
if (val === null) {
val = WebUtil.readSetting(name, defVal);
}
- UI.updateSetting(name, val);
+ WebUtil.setSetting(name, val);
+ UI.updateSetting(name);
return val;
},
+ // Set the new value, update and disable form control setting
+ forceSetting(name, val) {
+ WebUtil.setSetting(name, val);
+ UI.updateSetting(name);
+ UI.disableSetting(name);
+ },
+
// Update cookie and form control setting. If value is not set, then
// updates from control to current cookie setting.
- updateSetting: function(name, value) {
-
- // Save the cookie for this session
- if (typeof value !== 'undefined') {
- WebUtil.writeSetting(name, value);
- }
+ updateSetting(name) {
// Update the settings control
- value = UI.getSetting(name);
+ let value = UI.getSetting(name);
- var ctrl = document.getElementById('noVNC_setting_' + name);
+ const ctrl = document.getElementById('noVNC_setting_' + name);
if (ctrl.type === 'checkbox') {
ctrl.checked = value;
} else if (typeof ctrl.options !== 'undefined') {
- for (var i = 0; i < ctrl.options.length; i += 1) {
+ for (let i = 0; i < ctrl.options.length; i += 1) {
if (ctrl.options[i].value === value) {
ctrl.selectedIndex = i;
break;
},
// Save control setting to cookie
- saveSetting: function(name) {
- var val, ctrl = document.getElementById('noVNC_setting_' + name);
+ saveSetting(name) {
+ const ctrl = document.getElementById('noVNC_setting_' + name);
+ let val;
if (ctrl.type === 'checkbox') {
val = ctrl.checked;
} else if (typeof ctrl.options !== 'undefined') {
},
// Read form control compatible setting from cookie
- getSetting: function(name) {
- var ctrl = document.getElementById('noVNC_setting_' + name);
- var val = WebUtil.readSetting(name);
+ getSetting(name) {
+ const ctrl = document.getElementById('noVNC_setting_' + name);
+ let val = WebUtil.readSetting(name);
if (typeof val !== 'undefined' && val !== null && ctrl.type === 'checkbox') {
- if (val.toString().toLowerCase() in {'0':1, 'no':1, 'false':1}) {
+ if (val.toString().toLowerCase() in {'0': 1, 'no': 1, 'false': 1}) {
val = false;
} else {
val = true;
// These helpers compensate for the lack of parent-selectors and
// previous-sibling-selectors in CSS which are needed when we want to
// disable the labels that belong to disabled input elements.
- disableSetting: function(name) {
- var ctrl = document.getElementById('noVNC_setting_' + name);
+ disableSetting(name) {
+ const ctrl = document.getElementById('noVNC_setting_' + name);
ctrl.disabled = true;
ctrl.label.classList.add('noVNC_disabled');
},
- enableSetting: function(name) {
- var ctrl = document.getElementById('noVNC_setting_' + name);
+ enableSetting(name) {
+ const ctrl = document.getElementById('noVNC_setting_' + name);
ctrl.disabled = false;
ctrl.label.classList.remove('noVNC_disabled');
},
* PANELS
* ------v------*/
- closeAllPanels: function() {
+ closeAllPanels() {
UI.closeSettingsPanel();
- UI.closeXvpPanel();
+ UI.closePowerPanel();
UI.closeClipboardPanel();
UI.closeExtraKeys();
},
* SETTINGS (panel)
* ------v------*/
- openSettingsPanel: function() {
+ openSettingsPanel() {
UI.closeAllPanels();
UI.openControlbar();
// Refresh UI elements from saved cookies
UI.updateSetting('encrypt');
- if (cursorURIsSupported()) {
- UI.updateSetting('cursor');
- } else {
- UI.updateSetting('cursor', !isTouchDevice);
- UI.disableSetting('cursor');
- }
UI.updateSetting('view_clip');
UI.updateSetting('resize');
UI.updateSetting('shared');
.classList.add("noVNC_selected");
},
- closeSettingsPanel: function() {
+ closeSettingsPanel() {
document.getElementById('noVNC_settings')
.classList.remove("noVNC_open");
document.getElementById('noVNC_settings_button')
.classList.remove("noVNC_selected");
},
- toggleSettingsPanel: function() {
+ toggleSettingsPanel() {
if (document.getElementById('noVNC_settings')
.classList.contains("noVNC_open")) {
UI.closeSettingsPanel();
/* ------^-------
* /SETTINGS
* ==============
- * XVP
+ * POWER
* ------v------*/
- openXvpPanel: function() {
+ openPowerPanel() {
UI.closeAllPanels();
UI.openControlbar();
- document.getElementById('noVNC_xvp')
+ document.getElementById('noVNC_power')
.classList.add("noVNC_open");
- document.getElementById('noVNC_xvp_button')
+ document.getElementById('noVNC_power_button')
.classList.add("noVNC_selected");
},
- closeXvpPanel: function() {
- document.getElementById('noVNC_xvp')
+ closePowerPanel() {
+ document.getElementById('noVNC_power')
.classList.remove("noVNC_open");
- document.getElementById('noVNC_xvp_button')
+ document.getElementById('noVNC_power_button')
.classList.remove("noVNC_selected");
},
- toggleXvpPanel: function() {
- if (document.getElementById('noVNC_xvp')
+ togglePowerPanel() {
+ if (document.getElementById('noVNC_power')
.classList.contains("noVNC_open")) {
- UI.closeXvpPanel();
+ UI.closePowerPanel();
} else {
- UI.openXvpPanel();
+ UI.openPowerPanel();
}
},
- // Disable/enable XVP button
- updateXvpButton: function(ver) {
- if (ver >= 1 && !UI.rfb.get_view_only()) {
- document.getElementById('noVNC_xvp_button')
+ // Disable/enable power button
+ updatePowerButton() {
+ if (UI.connected &&
+ UI.rfb.capabilities.power &&
+ !UI.rfb.viewOnly) {
+ document.getElementById('noVNC_power_button')
.classList.remove("noVNC_hidden");
} else {
- document.getElementById('noVNC_xvp_button')
+ document.getElementById('noVNC_power_button')
.classList.add("noVNC_hidden");
- // Close XVP panel if open
- UI.closeXvpPanel();
+ // Close power panel if open
+ UI.closePowerPanel();
}
},
/* ------^-------
- * /XVP
+ * /POWER
* ==============
* CLIPBOARD
* ------v------*/
- openClipboardPanel: function() {
+ openClipboardPanel() {
UI.closeAllPanels();
UI.openControlbar();
.classList.add("noVNC_selected");
},
- closeClipboardPanel: function() {
+ closeClipboardPanel() {
document.getElementById('noVNC_clipboard')
.classList.remove("noVNC_open");
document.getElementById('noVNC_clipboard_button')
.classList.remove("noVNC_selected");
},
- toggleClipboardPanel: function() {
+ toggleClipboardPanel() {
if (document.getElementById('noVNC_clipboard')
.classList.contains("noVNC_open")) {
UI.closeClipboardPanel();
}
},
- clipboardReceive: function(rfb, text) {
- Log.Debug(">> UI.clipboardReceive: " + text.substr(0,40) + "...");
- document.getElementById('noVNC_clipboard_text').value = text;
+ clipboardReceive(e) {
+ Log.Debug(">> UI.clipboardReceive: " + e.detail.text.substr(0, 40) + "...");
+ document.getElementById('noVNC_clipboard_text').value = e.detail.text;
Log.Debug("<< UI.clipboardReceive");
},
- clipboardClear: function() {
+ clipboardClear() {
document.getElementById('noVNC_clipboard_text').value = "";
UI.rfb.clipboardPasteFrom("");
},
- clipboardSend: function() {
- var text = document.getElementById('noVNC_clipboard_text').value;
- Log.Debug(">> UI.clipboardSend: " + text.substr(0,40) + "...");
+ clipboardSend() {
+ const text = document.getElementById('noVNC_clipboard_text').value;
+ Log.Debug(">> UI.clipboardSend: " + text.substr(0, 40) + "...");
UI.rfb.clipboardPasteFrom(text);
Log.Debug("<< UI.clipboardSend");
},
* CONNECTION
* ------v------*/
- openConnectPanel: function() {
+ openConnectPanel() {
document.getElementById('noVNC_connect_dlg')
.classList.add("noVNC_open");
},
- closeConnectPanel: function() {
+ closeConnectPanel() {
document.getElementById('noVNC_connect_dlg')
.classList.remove("noVNC_open");
},
- connect: function(event, password) {
- var host = UI.getSetting('host');
- var port = UI.getSetting('port');
- var path = UI.getSetting('path');
+ connect(event, password) {
+
+ // Ignore when rfb already exists
+ if (typeof UI.rfb !== 'undefined') {
+ return;
+ }
+
+ const host = UI.getSetting('host');
+ const port = UI.getSetting('port');
+ const path = UI.getSetting('path');
if (typeof password === 'undefined') {
password = WebUtil.getConfigVar('password');
password = undefined;
}
+ UI.hideStatus();
+
if (!host) {
- var msg = _("Must set host");
- Log.Error(msg);
- UI.showStatus(msg, 'error');
+ Log.Error("Can't connect when host is: " + host);
+ UI.showStatus(_("Must set host"), 'error');
return;
}
- if (!UI.initRFB()) return;
-
UI.closeAllPanels();
UI.closeConnectPanel();
- UI.rfb.set_encrypt(UI.getSetting('encrypt'));
- UI.rfb.set_shared(UI.getSetting('shared'));
- UI.rfb.set_repeaterID(UI.getSetting('repeaterID'));
+ UI.updateVisualState('connecting');
- UI.updateLocalCursor();
- UI.updateViewOnly();
+ let url;
- UI.rfb.connect(host, port, { password: password }, path);
+ url = UI.getSetting('encrypt') ? 'wss' : 'ws';
+
+ url += '://' + host;
+ if(port) {
+ url += ':' + port;
+ }
+ url += '/' + path;
+
+ UI.rfb = new RFB(document.getElementById('noVNC_container'), url,
+ { shared: UI.getSetting('shared'),
+ repeaterID: UI.getSetting('repeaterID'),
+ credentials: { password: password } });
+ UI.rfb.addEventListener("connect", UI.connectFinished);
+ UI.rfb.addEventListener("disconnect", UI.disconnectFinished);
+ UI.rfb.addEventListener("credentialsrequired", UI.credentials);
+ UI.rfb.addEventListener("securityfailure", UI.securityFailed);
+ UI.rfb.addEventListener("capabilities", UI.updatePowerButton);
+ UI.rfb.addEventListener("clipboard", UI.clipboardReceive);
+ UI.rfb.addEventListener("bell", UI.bell);
+ UI.rfb.addEventListener("desktopname", UI.updateDesktopName);
+ UI.rfb.clipViewport = UI.getSetting('view_clip');
+ UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale';
+ UI.rfb.resizeSession = UI.getSetting('resize') === 'remote';
+
+ UI.updateViewOnly(); // requires UI.rfb
},
- disconnect: function() {
+ disconnect() {
UI.closeAllPanels();
UI.rfb.disconnect();
+ UI.connected = false;
+
// Disable automatic reconnecting
UI.inhibit_reconnect = true;
- // Restore the callback used for initial resize
- UI.rfb.set_onFBUComplete(UI.initialResize);
+ UI.updateVisualState('disconnecting');
// Don't display the connection settings until we're actually disconnected
},
- reconnect: function() {
+ reconnect() {
UI.reconnect_callback = null;
// if reconnect has been disabled in the meantime, do nothing.
UI.connect(null, UI.reconnect_password);
},
- disconnectFinished: function (rfb, reason) {
- if (typeof reason !== 'undefined') {
- UI.showStatus(reason, 'error');
+ cancelReconnect() {
+ if (UI.reconnect_callback !== null) {
+ clearTimeout(UI.reconnect_callback);
+ UI.reconnect_callback = null;
+ }
+
+ UI.updateVisualState('disconnected');
+
+ UI.openControlbar();
+ UI.openConnectPanel();
+ },
+
+ connectFinished(e) {
+ UI.connected = true;
+ UI.inhibit_reconnect = false;
+
+ let msg;
+ if (UI.getSetting('encrypt')) {
+ msg = _("Connected (encrypted) to ") + UI.desktopName;
+ } else {
+ msg = _("Connected (unencrypted) to ") + UI.desktopName;
+ }
+ UI.showStatus(msg);
+ UI.updateVisualState('connected');
+
+ // Do this last because it can only be used on rendered elements
+ UI.rfb.focus();
+ },
+
+ disconnectFinished(e) {
+ const wasConnected = UI.connected;
+
+ // This variable is ideally set when disconnection starts, but
+ // when the disconnection isn't clean or if it is initiated by
+ // the server, we need to do it here as well since
+ // UI.disconnect() won't be used in those cases.
+ UI.connected = false;
+
+ UI.rfb = undefined;
+
+ if (!e.detail.clean) {
+ UI.updateVisualState('disconnected');
+ if (wasConnected) {
+ UI.showStatus(_("Something went wrong, connection is closed"),
+ 'error');
+ } else {
+ UI.showStatus(_("Failed to connect to server"), 'error');
+ }
} else if (UI.getSetting('reconnect', false) === true && !UI.inhibit_reconnect) {
- document.getElementById("noVNC_transition_text").textContent = _("Reconnecting...");
- document.documentElement.classList.add("noVNC_reconnecting");
+ UI.updateVisualState('reconnecting');
- var delay = parseInt(UI.getSetting('reconnect_delay'));
+ const delay = parseInt(UI.getSetting('reconnect_delay'));
UI.reconnect_callback = setTimeout(UI.reconnect, delay);
return;
+ } else {
+ UI.updateVisualState('disconnected');
+ UI.showStatus(_("Disconnected"), 'normal');
}
UI.openControlbar();
UI.openConnectPanel();
},
- cancelReconnect: function() {
- if (UI.reconnect_callback !== null) {
- clearTimeout(UI.reconnect_callback);
- UI.reconnect_callback = null;
+ securityFailed(e) {
+ let msg = "";
+ // On security failures we might get a string with a reason
+ // directly from the server. Note that we can't control if
+ // this string is translated or not.
+ if ('reason' in e.detail) {
+ msg = _("New connection has been rejected with reason: ") +
+ e.detail.reason;
+ } else {
+ msg = _("New connection has been rejected");
}
-
- document.documentElement.classList.remove("noVNC_reconnecting");
- UI.openControlbar();
- UI.openConnectPanel();
+ UI.showStatus(msg, 'error');
},
/* ------^-------
* PASSWORD
* ------v------*/
- credentials: function(rfb, types) {
+ credentials(e) {
// FIXME: handle more types
document.getElementById('noVNC_password_dlg')
.classList.add('noVNC_open');
- setTimeout(function () {
- document.getElementById('noVNC_password_input').focus();
- }, 100);
+ setTimeout(() => document
+ .getElementById('noVNC_password_input').focus(), 100);
- var msg = _("Password is required");
- Log.Warn(msg);
- UI.showStatus(msg, "warning");
+ Log.Warn("Server asked for a password");
+ UI.showStatus(_("Password is required"), "warning");
},
- setPassword: function(e) {
- var inputElem = document.getElementById('noVNC_password_input');
- var password = inputElem.value;
+ setPassword(e) {
+ // Prevent actually submitting the form
+ e.preventDefault();
+
+ const inputElem = document.getElementById('noVNC_password_input');
+ const password = inputElem.value;
// Clear the input after reading the password
inputElem.value = "";
UI.rfb.sendCredentials({ password: password });
UI.reconnect_password = password;
document.getElementById('noVNC_password_dlg')
.classList.remove('noVNC_open');
- // Prevent actually submitting the form
- e.preventDefault();
},
/* ------^-------
* FULLSCREEN
* ------v------*/
- toggleFullscreen: function() {
+ toggleFullscreen() {
if (document.fullscreenElement || // alternative standard method
document.mozFullScreenElement || // currently working methods
document.webkitFullscreenElement ||
document.body.msRequestFullscreen();
}
}
- UI.enableDisableViewClip();
UI.updateFullscreenButton();
},
- updateFullscreenButton: function() {
+ updateFullscreenButton() {
if (document.fullscreenElement || // alternative standard method
document.mozFullScreenElement || // currently working methods
document.webkitFullscreenElement ||
* ------v------*/
// Apply remote resizing or local scaling
- applyResizeMode: function() {
+ applyResizeMode() {
if (!UI.rfb) return;
- var screen = UI.screenSize();
-
- if (screen && UI.connected) {
-
- var resizeMode = UI.getSetting('resize');
- UI.rfb.set_scale(1);
-
- // Make sure the viewport is adjusted first
- UI.updateViewClip();
-
- if (resizeMode === 'remote') {
-
- // Request changing the resolution of the remote display to
- // the size of the local browser viewport.
-
- // In order to not send multiple requests before the browser-resize
- // is finished we wait 0.5 seconds before sending the request.
- clearTimeout(UI.resizeTimeout);
- UI.resizeTimeout = setTimeout(function(){
- // Request a remote size covering the viewport
- if (UI.rfb.requestDesktopSize(screen.w, screen.h)) {
- Log.Debug('Requested new desktop size: ' +
- screen.w + 'x' + screen.h);
- }
- }, 500);
-
- } else {
- UI.updateScaling();
- }
- }
- },
-
- // Re-calculate local scaling
- updateScaling: function() {
- if (!UI.rfb) return;
-
- var resizeMode = UI.getSetting('resize');
- if (resizeMode !== 'scale' && resizeMode !== 'downscale') {
- return;
- }
-
- var screen = UI.screenSize();
-
- if (!screen || !UI.connected) {
- return;
- }
-
- var downscaleOnly = resizeMode === 'downscale';
- UI.rfb.autoscale(screen.w, screen.h, downscaleOnly);
- UI.fixScrollbars();
- },
-
- // Gets the the size of the available viewport in the browser window
- screenSize: function() {
- var screen = document.getElementById('noVNC_screen');
- return {w: screen.offsetWidth, h: screen.offsetHeight};
- },
-
- // Normally we only apply the current resize mode after a window resize
- // event. This means that when a new connection is opened, there is no
- // resize mode active.
- // We have to wait until the first FBU because this is where the client
- // will find the supported encodings of the server. Some calls later in
- // the chain is dependant on knowing the server-capabilities.
- initialResize: function(rfb, fbu) {
- UI.applyResizeMode();
- // After doing this once, we remove the callback.
- UI.rfb.set_onFBUComplete(function() { });
+ UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale';
+ UI.rfb.resizeSession = UI.getSetting('resize') === 'remote';
},
/* ------^-------
* VIEW CLIPPING
* ------v------*/
- // Set and configure viewport clipping
- setViewClip: function(clip) {
- UI.updateSetting('view_clip', clip);
- UI.updateViewClip();
- },
-
- // Update parameters that depend on the viewport clip setting
- updateViewClip: function() {
+ // Update viewport clipping property for the connection. The normal
+ // case is to get the value from the setting. There are special cases
+ // for when the viewport is scaled or when a touch device is used.
+ updateViewClip() {
if (!UI.rfb) return;
- var cur_clip = UI.rfb.get_viewport();
- var new_clip = UI.getSetting('view_clip');
+ const scaling = UI.getSetting('resize') === 'scale';
- var resizeSetting = UI.getSetting('resize');
- if (resizeSetting === 'downscale' || resizeSetting === 'scale') {
- // Disable viewport clipping if we are scaling
- new_clip = false;
+ if (scaling) {
+ // Can't be clipping if viewport is scaled to fit
+ UI.forceSetting('view_clip', false);
+ UI.rfb.clipViewport = false;
} else if (isTouchDevice) {
// Touch devices usually have shit scrollbars
- new_clip = true;
- }
-
- if (cur_clip !== new_clip) {
- UI.rfb.set_viewport(new_clip);
- }
-
- var size = UI.screenSize();
-
- if (new_clip && size) {
- // When clipping is enabled, the screen is limited to
- // the size of the browser window.
- UI.rfb.viewportChangeSize(size.w, size.h);
- UI.fixScrollbars();
+ UI.forceSetting('view_clip', true);
+ UI.rfb.clipViewport = true;
+ } else {
+ UI.enableSetting('view_clip');
+ UI.rfb.clipViewport = UI.getSetting('view_clip');
}
// Changing the viewport may change the state of
UI.updateViewDrag();
},
- // Handle special cases where viewport clipping is forced on/off or locked
- enableDisableViewClip: function() {
- var resizeSetting = UI.getSetting('resize');
- // Disable clipping if we are scaling, connected or on touch
- if (resizeSetting === 'downscale' || resizeSetting === 'scale' ||
- isTouchDevice) {
- UI.disableSetting('view_clip');
- } else {
- UI.enableSetting('view_clip');
- }
- },
-
/* ------^-------
* /VIEW CLIPPING
* ==============
* VIEWDRAG
* ------v------*/
- toggleViewDrag: function() {
+ toggleViewDrag() {
if (!UI.rfb) return;
- var drag = UI.rfb.get_viewportDrag();
- UI.setViewDrag(!drag);
- },
-
- // Set the view drag mode which moves the viewport on mouse drags
- setViewDrag: function(drag) {
- if (!UI.rfb) return;
-
- UI.rfb.set_viewportDrag(drag);
-
+ UI.rfb.dragViewport = !UI.rfb.dragViewport;
UI.updateViewDrag();
},
- updateViewDrag: function() {
- var clipping = false;
-
+ updateViewDrag() {
if (!UI.connected) return;
- // Check if viewport drag is possible. It is only possible
- // if the remote display is clipping the client display.
- if (UI.rfb.get_viewport() &&
- UI.rfb.clippingDisplay()) {
- clipping = true;
- }
+ const viewDragButton = document.getElementById('noVNC_view_drag_button');
- var viewDragButton = document.getElementById('noVNC_view_drag_button');
-
- if (!clipping &&
- UI.rfb.get_viewportDrag()) {
- // The size of the remote display is the same or smaller
- // than the client display. Make sure viewport drag isn't
- // active when it can't be used.
- UI.rfb.set_viewportDrag(false);
+ if (!UI.rfb.clipViewport && UI.rfb.dragViewport) {
+ // We are no longer clipping the viewport. Make sure
+ // viewport drag isn't active when it can't be used.
+ UI.rfb.dragViewport = false;
}
- if (UI.rfb.get_viewportDrag()) {
+ if (UI.rfb.dragViewport) {
viewDragButton.classList.add("noVNC_selected");
} else {
viewDragButton.classList.remove("noVNC_selected");
if (isTouchDevice) {
viewDragButton.classList.remove("noVNC_hidden");
- if (clipping) {
+ if (UI.rfb.clipViewport) {
viewDragButton.disabled = false;
} else {
viewDragButton.disabled = true;
} else {
viewDragButton.disabled = false;
- if (clipping) {
+ if (UI.rfb.clipViewport) {
viewDragButton.classList.remove("noVNC_hidden");
} else {
viewDragButton.classList.add("noVNC_hidden");
* KEYBOARD
* ------v------*/
- showVirtualKeyboard: function() {
+ showVirtualKeyboard() {
if (!isTouchDevice) return;
- var input = document.getElementById('noVNC_keyboardinput');
+ const input = document.getElementById('noVNC_keyboardinput');
if (document.activeElement == input) return;
input.focus();
try {
- var l = input.value.length;
+ const l = input.value.length;
// Move the caret to the end
input.setSelectionRange(l, l);
- } catch (err) {} // setSelectionRange is undefined in Google Chrome
+ } catch (err) {
+ // setSelectionRange is undefined in Google Chrome
+ }
},
- hideVirtualKeyboard: function() {
+ hideVirtualKeyboard() {
if (!isTouchDevice) return;
- var input = document.getElementById('noVNC_keyboardinput');
+ const input = document.getElementById('noVNC_keyboardinput');
if (document.activeElement != input) return;
input.blur();
},
- toggleVirtualKeyboard: function () {
+ toggleVirtualKeyboard() {
if (document.getElementById('noVNC_keyboard_button')
.classList.contains("noVNC_selected")) {
UI.hideVirtualKeyboard();
}
},
- onfocusVirtualKeyboard: function(event) {
+ onfocusVirtualKeyboard(event) {
document.getElementById('noVNC_keyboard_button')
.classList.add("noVNC_selected");
+ if (UI.rfb) {
+ UI.rfb.focusOnClick = false;
+ }
},
- onblurVirtualKeyboard: function(event) {
+ onblurVirtualKeyboard(event) {
document.getElementById('noVNC_keyboard_button')
.classList.remove("noVNC_selected");
+ if (UI.rfb) {
+ UI.rfb.focusOnClick = true;
+ }
},
- keepVirtualKeyboard: function(event) {
- var input = document.getElementById('noVNC_keyboardinput');
+ keepVirtualKeyboard(event) {
+ const input = document.getElementById('noVNC_keyboardinput');
// Only prevent focus change if the virtual keyboard is active
if (document.activeElement != input) {
}
}
- // The default action of touchstart is to generate other
- // events, which other elements might depend on. So we can't
- // blindly prevent that. Instead restore focus right away.
- if (event.type === "touchstart") {
- setTimeout(input.focus.bind(input));
- } else {
- event.preventDefault();
- }
+ event.preventDefault();
},
- keyboardinputReset: function() {
- var kbi = document.getElementById('noVNC_keyboardinput');
+ keyboardinputReset() {
+ const kbi = document.getElementById('noVNC_keyboardinput');
kbi.value = new Array(UI.defaultKeyboardinputLen).join("_");
UI.lastKeyboardinput = kbi.value;
},
- keyEvent: function (keysym, code, down) {
+ keyEvent(keysym, code, down) {
if (!UI.rfb) return;
UI.rfb.sendKey(keysym, code, down);
// the keyboardinput element instead and generate the corresponding key events.
// This code is required since some browsers on Android are inconsistent in
// sending keyCodes in the normal keyboard events when using on screen keyboards.
- keyInput: function(event) {
+ keyInput(event) {
if (!UI.rfb) return;
- var newValue = event.target.value;
+ const newValue = event.target.value;
if (!UI.lastKeyboardinput) {
UI.keyboardinputReset();
}
- var oldValue = UI.lastKeyboardinput;
+ const oldValue = UI.lastKeyboardinput;
- var newLen;
+ let newLen;
try {
// Try to check caret position since whitespace at the end
// will not be considered by value.length in some browsers
// selectionStart is undefined in Google Chrome
newLen = newValue.length;
}
- var oldLen = oldValue.length;
+ const oldLen = oldValue.length;
- var backspaces;
- var inputs = newLen - oldLen;
- if (inputs < 0) {
- backspaces = -inputs;
- } else {
- backspaces = 0;
- }
+ let inputs = newLen - oldLen;
+ let backspaces = inputs < 0 ? -inputs : 0;
// Compare the old string with the new to account for
// text-corrections or other input that modify existing text
- var i;
- for (i = 0; i < Math.min(oldLen, newLen); i++) {
+ for (let i = 0; i < Math.min(oldLen, newLen); i++) {
if (newValue.charAt(i) != oldValue.charAt(i)) {
inputs = newLen - i;
backspaces = oldLen - i;
}
// Send the key events
- for (i = 0; i < backspaces; i++) {
+ for (let i = 0; i < backspaces; i++) {
UI.rfb.sendKey(KeyTable.XK_BackSpace, "Backspace");
}
- for (i = newLen - inputs; i < newLen; i++) {
+ for (let i = newLen - inputs; i < newLen; i++) {
UI.rfb.sendKey(keysyms.lookup(newValue.charCodeAt(i)));
}
* EXTRA KEYS
* ------v------*/
- openExtraKeys: function() {
+ openExtraKeys() {
UI.closeAllPanels();
UI.openControlbar();
.classList.add("noVNC_selected");
},
- closeExtraKeys: function() {
+ closeExtraKeys() {
document.getElementById('noVNC_modifiers')
.classList.remove("noVNC_open");
document.getElementById('noVNC_toggle_extra_keys_button')
.classList.remove("noVNC_selected");
},
- toggleExtraKeys: function() {
+ toggleExtraKeys() {
if(document.getElementById('noVNC_modifiers')
.classList.contains("noVNC_open")) {
UI.closeExtraKeys();
}
},
- sendEsc: function() {
+ sendEsc() {
UI.rfb.sendKey(KeyTable.XK_Escape, "Escape");
},
- sendTab: function() {
+ sendTab() {
UI.rfb.sendKey(KeyTable.XK_Tab);
},
- toggleCtrl: function() {
- var btn = document.getElementById('noVNC_toggle_ctrl_button');
+ toggleCtrl() {
+ const btn = document.getElementById('noVNC_toggle_ctrl_button');
if (btn.classList.contains("noVNC_selected")) {
UI.rfb.sendKey(KeyTable.XK_Control_L, "ControlLeft", false);
btn.classList.remove("noVNC_selected");
}
},
- toggleAlt: function() {
- var btn = document.getElementById('noVNC_toggle_alt_button');
+ toggleAlt() {
+ const btn = document.getElementById('noVNC_toggle_alt_button');
if (btn.classList.contains("noVNC_selected")) {
UI.rfb.sendKey(KeyTable.XK_Alt_L, "AltLeft", false);
btn.classList.remove("noVNC_selected");
}
},
- sendCtrlAltDel: function() {
+ sendCtrlAltDel() {
UI.rfb.sendCtrlAltDel();
},
* MISC
* ------v------*/
- setMouseButton: function(num) {
- var view_only = UI.rfb.get_view_only();
+ setMouseButton(num) {
+ const view_only = UI.rfb.viewOnly;
if (UI.rfb && !view_only) {
- UI.rfb.set_touchButton(num);
+ UI.rfb.touchButton = num;
}
- var blist = [0, 1,2,4];
- for (var b = 0; b < blist.length; b++) {
- var button = document.getElementById('noVNC_mouse_button' +
+ const blist = [0, 1, 2, 4];
+ for (let b = 0; b < blist.length; b++) {
+ const button = document.getElementById('noVNC_mouse_button' +
blist[b]);
if (blist[b] === num && !view_only) {
button.classList.remove("noVNC_hidden");
}
},
- updateLocalCursor: function() {
+ updateViewOnly() {
if (!UI.rfb) return;
- UI.rfb.set_local_cursor(UI.getSetting('cursor'));
- },
+ UI.rfb.viewOnly = UI.getSetting('view_only');
- updateViewOnly: function() {
- if (!UI.rfb) return;
- UI.rfb.set_view_only(UI.getSetting('view_only'));
+ // Hide input related buttons in view only mode
+ if (UI.rfb.viewOnly) {
+ document.getElementById('noVNC_keyboard_button')
+ .classList.add('noVNC_hidden');
+ document.getElementById('noVNC_toggle_extra_keys_button')
+ .classList.add('noVNC_hidden');
+ } else {
+ document.getElementById('noVNC_keyboard_button')
+ .classList.remove('noVNC_hidden');
+ document.getElementById('noVNC_toggle_extra_keys_button')
+ .classList.remove('noVNC_hidden');
+ }
+ UI.setMouseButton(1); //has it's own logic for hiding/showing
},
- updateLogging: function() {
+ updateLogging() {
WebUtil.init_logging(UI.getSetting('logging'));
},
- updateSessionSize: function(rfb, width, height) {
- UI.updateViewClip();
- UI.updateScaling();
- UI.fixScrollbars();
- },
-
- fixScrollbars: function() {
- // This is a hack because Chrome screws up the calculation
- // for when scrollbars are needed. So to fix it we temporarily
- // toggle them off and on.
- var screen = document.getElementById('noVNC_screen');
- screen.style.overflow = 'hidden';
- // Force Chrome to recalculate the layout by asking for
- // an element's dimensions
- screen.getBoundingClientRect();
- screen.style.overflow = "";
- },
-
- updateDesktopName: function(rfb, name) {
- UI.desktopName = name;
+ updateDesktopName(e) {
+ UI.desktopName = e.detail.name;
// Display the desktop name in the document title
- document.title = name + " - noVNC";
+ document.title = e.detail.name + " - noVNC";
},
- bell: function(rfb) {
+ bell(e) {
if (WebUtil.getConfigVar('bell', 'on') === 'on') {
- var promise = document.getElementById('noVNC_bell').play();
+ const promise = document.getElementById('noVNC_bell').play();
// The standards disagree on the return value here
if (promise) {
- promise.catch(function(e) {
+ promise.catch((e) => {
if (e.name === "NotAllowedError") {
// Ignore when the browser doesn't let us play audio.
// It is common that the browsers require audio to be
},
//Helper to add options to dropdown.
- addOption: function(selectbox, text, value) {
- var optn = document.createElement("OPTION");
+ addOption(selectbox, text, value) {
+ const optn = document.createElement("OPTION");
optn.text = text;
optn.value = value;
selectbox.options.add(optn);
};
// Set up translations
-var LINGUAS = ["de", "el", "nl", "pl", "sv"];
+const LINGUAS = ["de", "el", "es", "nl", "pl", "sv", "tr", "zh_CN", "zh_TW"];
l10n.setup(LINGUAS);
if (l10n.language !== "en" && l10n.dictionary === undefined) {
- WebUtil.fetchJSON('app/locale/' + l10n.language + '.json', function (translations) {
+ WebUtil.fetchJSON('app/locale/' + l10n.language + '.json', (translations) => {
l10n.dictionary = translations;
// wait for translations to load before loading the UI
UI.prime();
- }, function (err) {
- throw err;
+ }, (err) => {
+ Log.Error("Failed to load translations: " + err);
+ UI.prime();
});
} else {
UI.prime();