import * as Log from '../core/util/logging.js';
import _, { l10n } from './localization.js';
-import { isTouchDevice, isSafari, isIOS, isAndroid, dragThreshold }
+import { isTouchDevice, isSafari, hasScrollbarGutter, dragThreshold }
from '../core/util/browser.js';
import { setCapture, getPointerEvent } from '../core/util/events.js';
import KeyTable from "../core/input/keysym.js";
lastKeyboardinput: null,
defaultKeyboardinputLen: 100,
- inhibit_reconnect: true,
- reconnect_callback: null,
- reconnect_password: null,
+ inhibitReconnect: true,
+ reconnectCallback: null,
+ reconnectPassword: null,
prime() {
return WebUtil.initSettings().then(() => {
// Translate the DOM
l10n.translateDOM();
- WebUtil.fetchJSON('../package.json')
+ fetch('./package.json')
+ .then((response) => {
+ if (!response.ok) {
+ throw Error("" + response.status + " " + response.statusText);
+ }
+ return response.json();
+ })
.then((packageInfo) => {
Array.from(document.getElementsByClassName('noVNC_version')).forEach(el => el.innerText = packageInfo.version);
})
UI.initSetting('encrypt', (window.location.protocol === "https:"));
UI.initSetting('view_clip', false);
UI.initSetting('resize', 'off');
+ UI.initSetting('quality', 6);
+ UI.initSetting('compression', 2);
UI.initSetting('shared', true);
UI.initSetting('view_only', false);
UI.initSetting('show_dot', false);
},
addTouchSpecificHandlers() {
- document.getElementById("noVNC_mouse_button0")
- .addEventListener('click', () => UI.setMouseButton(1));
- document.getElementById("noVNC_mouse_button1")
- .addEventListener('click', () => UI.setMouseButton(2));
- document.getElementById("noVNC_mouse_button2")
- .addEventListener('click', () => UI.setMouseButton(4));
- document.getElementById("noVNC_mouse_button4")
- .addEventListener('click', () => UI.setMouseButton(0));
document.getElementById("noVNC_keyboard_button")
.addEventListener('click', UI.toggleVirtualKeyboard);
document.getElementById("noVNC_cancel_reconnect_button")
.addEventListener('click', UI.cancelReconnect);
- document.getElementById("noVNC_password_button")
- .addEventListener('click', UI.setPassword);
+ document.getElementById("noVNC_credentials_button")
+ .addEventListener('click', UI.setCredentials);
},
addClipboardHandlers() {
UI.addSettingChangeHandler('resize');
UI.addSettingChangeHandler('resize', UI.applyResizeMode);
UI.addSettingChangeHandler('resize', UI.updateViewClip);
+ UI.addSettingChangeHandler('quality');
+ UI.addSettingChangeHandler('quality', UI.updateQuality);
+ UI.addSettingChangeHandler('compression');
+ UI.addSettingChangeHandler('compression', UI.updateCompression);
UI.addSettingChangeHandler('view_clip');
UI.addSettingChangeHandler('view_clip', UI.updateViewClip);
UI.addSettingChangeHandler('shared');
document.documentElement.classList.remove("noVNC_disconnecting");
document.documentElement.classList.remove("noVNC_reconnecting");
- const transition_elem = document.getElementById("noVNC_transition_text");
+ const transitionElem = document.getElementById("noVNC_transition_text");
switch (state) {
case 'init':
break;
case 'connecting':
- transition_elem.textContent = _("Connecting...");
+ transitionElem.textContent = _("Connecting...");
document.documentElement.classList.add("noVNC_connecting");
break;
case 'connected':
document.documentElement.classList.add("noVNC_connected");
break;
case 'disconnecting':
- transition_elem.textContent = _("Disconnecting...");
+ transitionElem.textContent = _("Disconnecting...");
document.documentElement.classList.add("noVNC_disconnecting");
break;
case 'disconnected':
break;
case 'reconnecting':
- transition_elem.textContent = _("Reconnecting...");
+ transitionElem.textContent = _("Reconnecting...");
document.documentElement.classList.add("noVNC_reconnecting");
break;
default:
UI.disableSetting('port');
UI.disableSetting('path');
UI.disableSetting('repeaterID');
- UI.setMouseButton(1);
// Hide the controlbar after 2 seconds
UI.closeControlbarTimeout = setTimeout(UI.closeControlbar, 2000);
UI.keepControlbar();
}
- // State change closes the password dialog
- document.getElementById('noVNC_password_dlg')
+ // State change closes dialogs as they may not be relevant
+ // anymore
+ UI.closeAllPanels();
+ document.getElementById('noVNC_credentials_dlg')
.classList.remove('noVNC_open');
},
- showStatus(text, status_type, time) {
+ showStatus(text, statusType, time) {
const statusElem = document.getElementById('noVNC_status');
- clearTimeout(UI.statusTimeout);
-
- if (typeof status_type === 'undefined') {
- status_type = 'normal';
+ if (typeof statusType === 'undefined') {
+ statusType = 'normal';
}
// 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';
+ return;
+ }
+ if (statusElem.classList.contains("noVNC_status_warn") &&
+ statusType === 'normal') {
+ return;
}
- }
- if (visible_status_type === 'error' ||
- (visible_status_type === 'warn' && status_type === 'normal')) {
- return;
}
- switch (status_type) {
+ clearTimeout(UI.statusTimeout);
+
+ switch (statusType) {
case 'error':
statusElem.classList.remove("noVNC_status_warn");
statusElem.classList.remove("noVNC_status_normal");
}
// Error messages do not timeout
- if (status_type !== 'error') {
+ if (statusType !== 'error') {
UI.statusTimeout = window.setTimeout(UI.hideStatus, time);
}
},
},
idleControlbar() {
+ // Don't fade if a child of the control bar has focus
+ if (document.getElementById('noVNC_control_bar')
+ .contains(document.activeElement) && document.hasFocus()) {
+ UI.activateControlbar();
+ return;
+ }
+
document.getElementById('noVNC_control_bar_anchor')
.classList.add("noVNC_idle");
},
UI.closeAllPanels();
document.getElementById('noVNC_control_bar')
.classList.remove("noVNC_open");
+ UI.rfb.focus();
},
toggleControlbar() {
}
}
} else {
- /*Weird IE9 error leads to 'null' appearring
- in textboxes instead of ''.*/
- if (value === null) {
- value = "";
- }
ctrl.value = value;
}
},
UI.updateSetting('encrypt');
UI.updateSetting('view_clip');
UI.updateSetting('resize');
+ UI.updateSetting('quality');
+ UI.updateSetting('compression');
UI.updateSetting('shared');
UI.updateSetting('view_only');
UI.updateSetting('path');
if (typeof password === 'undefined') {
password = WebUtil.getConfigVar('password');
- UI.reconnect_password = password;
+ UI.reconnectPassword = password;
}
if (password === null) {
return;
}
- UI.closeAllPanels();
UI.closeConnectPanel();
UI.updateVisualState('connecting');
UI.rfb.clipViewport = UI.getSetting('view_clip');
UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale';
UI.rfb.resizeSession = UI.getSetting('resize') === 'remote';
+ UI.rfb.qualityLevel = parseInt(UI.getSetting('quality'));
+ UI.rfb.compressionLevel = parseInt(UI.getSetting('compression'));
UI.rfb.showDotCursor = UI.getSetting('show_dot');
UI.updateViewOnly(); // requires UI.rfb
},
disconnect() {
- UI.closeAllPanels();
UI.rfb.disconnect();
UI.connected = false;
// Disable automatic reconnecting
- UI.inhibit_reconnect = true;
+ UI.inhibitReconnect = true;
UI.updateVisualState('disconnecting');
},
reconnect() {
- UI.reconnect_callback = null;
+ UI.reconnectCallback = null;
// if reconnect has been disabled in the meantime, do nothing.
- if (UI.inhibit_reconnect) {
+ if (UI.inhibitReconnect) {
return;
}
- UI.connect(null, UI.reconnect_password);
+ UI.connect(null, UI.reconnectPassword);
},
cancelReconnect() {
- if (UI.reconnect_callback !== null) {
- clearTimeout(UI.reconnect_callback);
- UI.reconnect_callback = null;
+ if (UI.reconnectCallback !== null) {
+ clearTimeout(UI.reconnectCallback);
+ UI.reconnectCallback = null;
}
UI.updateVisualState('disconnected');
connectFinished(e) {
UI.connected = true;
- UI.inhibit_reconnect = false;
+ UI.inhibitReconnect = false;
let msg;
if (UI.getSetting('encrypt')) {
} else {
UI.showStatus(_("Failed to connect to server"), 'error');
}
- } else if (UI.getSetting('reconnect', false) === true && !UI.inhibit_reconnect) {
+ } else if (UI.getSetting('reconnect', false) === true && !UI.inhibitReconnect) {
UI.updateVisualState('reconnecting');
const delay = parseInt(UI.getSetting('reconnect_delay'));
- UI.reconnect_callback = setTimeout(UI.reconnect, delay);
+ UI.reconnectCallback = setTimeout(UI.reconnect, delay);
return;
} else {
UI.updateVisualState('disconnected');
credentials(e) {
// FIXME: handle more types
- document.getElementById('noVNC_password_dlg')
+
+ document.getElementById("noVNC_username_block").classList.remove("noVNC_hidden");
+ document.getElementById("noVNC_password_block").classList.remove("noVNC_hidden");
+
+ let inputFocus = "none";
+ if (e.detail.types.indexOf("username") === -1) {
+ document.getElementById("noVNC_username_block").classList.add("noVNC_hidden");
+ } else {
+ inputFocus = inputFocus === "none" ? "noVNC_username_input" : inputFocus;
+ }
+ if (e.detail.types.indexOf("password") === -1) {
+ document.getElementById("noVNC_password_block").classList.add("noVNC_hidden");
+ } else {
+ inputFocus = inputFocus === "none" ? "noVNC_password_input" : inputFocus;
+ }
+ document.getElementById('noVNC_credentials_dlg')
.classList.add('noVNC_open');
setTimeout(() => document
- .getElementById('noVNC_password_input').focus(), 100);
+ .getElementById(inputFocus).focus(), 100);
- Log.Warn("Server asked for a password");
- UI.showStatus(_("Password is required"), "warning");
+ Log.Warn("Server asked for credentials");
+ UI.showStatus(_("Credentials are required"), "warning");
},
- setPassword(e) {
+ setCredentials(e) {
// Prevent actually submitting the form
e.preventDefault();
- const inputElem = document.getElementById('noVNC_password_input');
- const password = inputElem.value;
+ let inputElemUsername = document.getElementById('noVNC_username_input');
+ const username = inputElemUsername.value;
+
+ let inputElemPassword = document.getElementById('noVNC_password_input');
+ const password = inputElemPassword.value;
// Clear the input after reading the password
- inputElem.value = "";
- UI.rfb.sendCredentials({ password: password });
- UI.reconnect_password = password;
- document.getElementById('noVNC_password_dlg')
+ inputElemPassword.value = "";
+
+ UI.rfb.sendCredentials({ username: username, password: password });
+ UI.reconnectPassword = password;
+ document.getElementById('noVNC_credentials_dlg')
.classList.remove('noVNC_open');
},
// Can't be clipping if viewport is scaled to fit
UI.forceSetting('view_clip', false);
UI.rfb.clipViewport = false;
- } else if (isIOS() || isAndroid()) {
- // iOS and Android usually have shit scrollbars
+ } else if (!hasScrollbarGutter) {
+ // Some platforms have scrollbars that are difficult
+ // to use in our case, so we always use our own panning
UI.forceSetting('view_clip', true);
UI.rfb.clipViewport = true;
} else {
/* ------^-------
* /VIEWDRAG
* ==============
+ * QUALITY
+ * ------v------*/
+
+ updateQuality() {
+ if (!UI.rfb) return;
+
+ UI.rfb.qualityLevel = parseInt(UI.getSetting('quality'));
+ },
+
+/* ------^-------
+ * /QUALITY
+ * ==============
+ * COMPRESSION
+ * ------v------*/
+
+ updateCompression() {
+ if (!UI.rfb) return;
+
+ UI.rfb.compressionLevel = parseInt(UI.getSetting('compression'));
+ },
+
+/* ------^-------
+ * /COMPRESSION
+ * ==============
* KEYBOARD
* ------v------*/
sendKey(keysym, code, down) {
UI.rfb.sendKey(keysym, code, down);
- // move focus to the screen in order to be able to
- // use the keyboard right after these extra keys
- UI.rfb.focus();
+
+ // Move focus to the screen in order to be able to use the
+ // keyboard right after these extra keys.
+ // The exception is when a virtual keyboard is used, because
+ // if we focus the screen the virtual keyboard would be closed.
+ // In this case we focus our special virtual keyboard input
+ // element instead.
+ if (document.getElementById('noVNC_keyboard_button')
+ .classList.contains("noVNC_selected")) {
+ document.getElementById('noVNC_keyboardinput').focus();
+ } else {
+ UI.rfb.focus();
+ }
// fade out the controlbar to highlight that
// the focus has been moved to the screen
UI.idleControlbar();
* MISC
* ------v------*/
- setMouseButton(num) {
- const view_only = UI.rfb.viewOnly;
- if (UI.rfb && !view_only) {
- UI.rfb.touchButton = num;
- }
-
- 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");
- } else {
- button.classList.add("noVNC_hidden");
- }
- }
- },
-
updateViewOnly() {
if (!UI.rfb) return;
UI.rfb.viewOnly = UI.getSetting('view_only');
.classList.add('noVNC_hidden');
document.getElementById('noVNC_toggle_extra_keys_button')
.classList.add('noVNC_hidden');
- document.getElementById('noVNC_mouse_button' + UI.rfb.touchButton)
+ document.getElementById('noVNC_clipboard_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');
- document.getElementById('noVNC_mouse_button' + UI.rfb.touchButton)
+ document.getElementById('noVNC_clipboard_button')
.classList.remove('noVNC_hidden');
}
},
},
updateLogging() {
- WebUtil.init_logging(UI.getSetting('logging'));
+ WebUtil.initLogging(UI.getSetting('logging'));
},
updateDesktopName(e) {
};
// Set up translations
-const LINGUAS = ["cs", "de", "el", "es", "ja", "ko", "nl", "pl", "ru", "sv", "tr", "zh_CN", "zh_TW"];
+const LINGUAS = ["cs", "de", "el", "es", "fr", "ja", "ko", "nl", "pl", "pt_BR", "ru", "sv", "tr", "zh_CN", "zh_TW"];
l10n.setup(LINGUAS);
if (l10n.language === "en" || l10n.dictionary !== undefined) {
UI.prime();
} else {
- WebUtil.fetchJSON('app/locale/' + l10n.language + '.json')
+ fetch('app/locale/' + l10n.language + '.json')
+ .then((response) => {
+ if (!response.ok) {
+ throw Error("" + response.status + " " + response.statusText);
+ }
+ return response.json();
+ })
.then((translations) => { l10n.dictionary = translations; })
.catch(err => Log.Error("Failed to load translations: " + err))
.then(UI.prime);