/* global window, document.getElementById, Util, WebUtil, RFB, Display */
import * as Log from '../core/util/logging.js';
-import _, { l10n } from '../core/util/localization.js'
+import _, { l10n } from '../core/util/localization.js';
import { isTouchDevice, browserSupportsCursorURIs as cursorURIsSupported } from '../core/util/browsers.js';
import { setCapture, getPointerEvent } from '../core/util/events.js';
import KeyTable from "../core/input/keysym.js";
import Display from "../core/display.js";
import * as WebUtil from "./webutil.js";
-const UI = {
+var UI = {
connected: false,
desktopName: "",
controlbarMouseDownOffsetY: 0,
isSafari: false,
- rememberedClipSetting: null,
lastKeyboardinput: null,
defaultKeyboardinputLen: 100,
document.getElementById("noVNC_status")
.addEventListener('click', UI.hideStatus);
+ // Bootstrap fallback input handler
+ UI.keyboardinputReset();
+
UI.openControlbar();
// Show the connect panel on first load unless autoconnecting
UI.updateVisualState();
- document.getElementById('noVNC_setting_host').focus();
document.documentElement.classList.remove("noVNC_loading");
var autoconnect = WebUtil.getConfigVar('autoconnect', false);
UI.initSetting('host', window.location.hostname);
UI.initSetting('port', port);
UI.initSetting('encrypt', (window.location.protocol === "https:"));
- UI.initSetting('true_color', true);
UI.initSetting('cursor', !isTouchDevice);
- UI.initSetting('clip', false);
+ UI.initSetting('view_clip', false);
UI.initSetting('resize', 'off');
UI.initSetting('shared', true);
UI.initSetting('view_only', false);
document.getElementById("noVNC_control_bar")
.addEventListener('mousedown', UI.activateControlbar);
document.getElementById("noVNC_control_bar")
- .addEventListener('keypress', UI.activateControlbar);
+ .addEventListener('keydown', UI.activateControlbar);
document.getElementById("noVNC_control_bar")
.addEventListener('mousedown', UI.keepControlbar);
document.getElementById("noVNC_control_bar")
- .addEventListener('keypress', UI.keepControlbar);
+ .addEventListener('keydown', UI.keepControlbar);
document.getElementById("noVNC_view_drag_button")
.addEventListener('click', UI.toggleViewDrag);
document.documentElement
.addEventListener('mousedown', UI.keepVirtualKeyboard, true);
+ document.documentElement
+ .addEventListener('touchstart', UI.keepVirtualKeyboard, true);
document.getElementById("noVNC_control_bar")
.addEventListener('touchstart', UI.activateControlbar);
.addEventListener('touchend', UI.controlbarHandleMouseUp);
document.getElementById("noVNC_control_bar_handle")
.addEventListener('touchmove', UI.dragControlbarHandle);
-
- window.addEventListener('load', UI.keyboardinputReset);
},
addExtraKeysHandlers: function() {
addClipboardHandlers: function() {
document.getElementById("noVNC_clipboard_button")
.addEventListener('click', UI.toggleClipboardPanel);
- document.getElementById("noVNC_clipboard_text")
- .addEventListener('focus', UI.displayBlur);
- document.getElementById("noVNC_clipboard_text")
- .addEventListener('blur', UI.displayFocus);
document.getElementById("noVNC_clipboard_text")
.addEventListener('change', UI.clipboardSend);
document.getElementById("noVNC_clipboard_clear_button")
.addEventListener('click', UI.toggleSettingsPanel);
UI.addSettingChangeHandler('encrypt');
- UI.addSettingChangeHandler('true_color');
UI.addSettingChangeHandler('cursor');
UI.addSettingChangeHandler('cursor', UI.updateLocalCursor);
UI.addSettingChangeHandler('resize');
UI.addSettingChangeHandler('resize', UI.enableDisableViewClip);
UI.addSettingChangeHandler('resize', UI.applyResizeMode);
- UI.addSettingChangeHandler('clip');
- UI.addSettingChangeHandler('clip', UI.updateViewClip);
+ UI.addSettingChangeHandler('view_clip');
+ UI.addSettingChangeHandler('view_clip', UI.updateViewClip);
UI.addSettingChangeHandler('shared');
UI.addSettingChangeHandler('view_only');
UI.addSettingChangeHandler('view_only', UI.updateViewOnly);
msg = _("Connected (unencrypted) to ") + UI.desktopName;
}
UI.showStatus(msg);
+ document.getElementById('noVNC_canvas').focus();
break;
case 'disconnecting':
UI.connected = false;
if (UI.connected) {
UI.disableSetting('encrypt');
- UI.disableSetting('true_color');
UI.disableSetting('shared');
UI.disableSetting('host');
UI.disableSetting('port');
UI.closeControlbarTimeout = setTimeout(UI.closeControlbar, 2000);
} else {
UI.enableSetting('encrypt');
- UI.enableSetting('true_color');
UI.enableSetting('shared');
UI.enableSetting('host');
UI.enableSetting('port');
UI.controlbarDrag = true;
},
+ showControlbarHint: function (show) {
+ var hint = document.getElementById('noVNC_control_bar_hint');
+ if (show) {
+ hint.classList.add("noVNC_active");
+ } else {
+ hint.classList.remove("noVNC_active");
+ }
+ },
+
dragControlbarHandle: function (e) {
if (!UI.controlbarGrabbed) return;
UI.activateControlbar();
}
UI.controlbarGrabbed = false;
+ UI.showControlbarHint(false);
},
controlbarHandleMouseDown: function(e) {
var handle = document.getElementById("noVNC_control_bar_handle");
var bounds = handle.getBoundingClientRect();
- setCapture(handle);
+ // Touch events have implicit capture
+ if (e.type === "mousedown") {
+ setCapture(handle);
+ }
+
UI.controlbarGrabbed = true;
UI.controlbarDrag = false;
+ UI.showControlbarHint(true);
+
UI.controlbarMouseDownClientY = ptr.clientY;
UI.controlbarMouseDownOffsetY = ptr.clientY - bounds.top;
e.preventDefault();
// Refresh UI elements from saved cookies
UI.updateSetting('encrypt');
- UI.updateSetting('true_color');
if (cursorURIsSupported()) {
UI.updateSetting('cursor');
} else {
UI.updateSetting('cursor', !isTouchDevice);
UI.disableSetting('cursor');
}
- UI.updateSetting('clip');
+ UI.updateSetting('view_clip');
UI.updateSetting('resize');
UI.updateSetting('shared');
UI.updateSetting('view_only');
password = undefined;
}
- if ((!host) || (!port)) {
- var msg = _("Must set host and port");
+ if (!host) {
+ var msg = _("Must set host");
Log.Error(msg);
UI.showStatus(msg, 'error');
return;
UI.closeConnectPanel();
UI.rfb.set_encrypt(UI.getSetting('encrypt'));
- UI.rfb.set_true_color(UI.getSetting('true_color'));
UI.rfb.set_shared(UI.getSetting('shared'));
UI.rfb.set_repeaterID(UI.getSetting('repeaterID'));
},
setPassword: function(e) {
- var password = document.getElementById('noVNC_password_input').value;
+ var inputElem = document.getElementById('noVNC_password_input');
+ var password = inputElem.value;
+ // Clear the input after reading the password
+ inputElem.value = "";
UI.rfb.sendPassword(password);
UI.reconnect_password = password;
document.getElementById('noVNC_password_dlg')
}
}, 500);
- } else if (resizeMode === 'scale' || resizeMode === 'downscale') {
- var downscaleOnly = resizeMode === 'downscale';
- display.autoscale(screen.w, screen.h, downscaleOnly);
- UI.fixScrollbars();
+ } 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 || !UI.rfb.get_display()) {
+ return;
+ }
+
+ var display = UI.rfb.get_display();
+ var downscaleOnly = resizeMode === 'downscale';
+ display.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');
/* ------^-------
* /RESIZE
* ==============
- * CLIPPING
+ * VIEW CLIPPING
* ------v------*/
// Set and configure viewport clipping
setViewClip: function(clip) {
- UI.updateSetting('clip', clip);
+ UI.updateSetting('view_clip', clip);
UI.updateViewClip();
},
- // Update parameters that depend on the clip setting
+ // Update parameters that depend on the viewport clip setting
updateViewClip: function() {
if (!UI.rfb) return;
var display = UI.rfb.get_display();
var cur_clip = display.get_viewport();
- var new_clip = UI.getSetting('clip');
+ var new_clip = UI.getSetting('view_clip');
var resizeSetting = UI.getSetting('resize');
if (resizeSetting === 'downscale' || resizeSetting === 'scale') {
- // Disable clipping if we are scaling
+ // Disable viewport clipping if we are scaling
new_clip = false;
} else if (isTouchDevice) {
// Touch devices usually have shit scrollbars
UI.updateViewDrag();
},
- // Handle special cases where clipping is forced on/off or locked
+ // 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('clip');
+ UI.disableSetting('view_clip');
} else {
- UI.enableSetting('clip');
+ UI.enableSetting('view_clip');
}
},
/* ------^-------
- * /CLIPPING
+ * /VIEW CLIPPING
* ==============
* VIEWDRAG
* ------v------*/
}
}
- event.preventDefault();
+ // 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();
+ }
},
keyboardinputReset: function() {
// Send the key events
for (i = 0; i < backspaces; i++) {
- UI.rfb.sendKey(KeyTable.XK_BackSpace);
+ UI.rfb.sendKey(KeyTable.XK_BackSpace, "Backspace");
}
for (i = newLen - inputs; i < newLen; i++) {
- UI.rfb.sendKey(keysyms.fromUnicode(newValue.charCodeAt(i)).keysym);
+ UI.rfb.sendKey(keysyms.lookup(newValue.charCodeAt(i)));
}
// Control the text content length in the keyboardinput element
},
sendEsc: function() {
- UI.rfb.sendKey(KeyTable.XK_Escape);
+ UI.rfb.sendKey(KeyTable.XK_Escape, "Escape");
},
sendTab: function() {
toggleCtrl: function() {
var btn = document.getElementById('noVNC_toggle_ctrl_button');
if (btn.classList.contains("noVNC_selected")) {
- UI.rfb.sendKey(KeyTable.XK_Control_L, false);
+ UI.rfb.sendKey(KeyTable.XK_Control_L, "ControlLeft", false);
btn.classList.remove("noVNC_selected");
} else {
- UI.rfb.sendKey(KeyTable.XK_Control_L, true);
+ UI.rfb.sendKey(KeyTable.XK_Control_L, "ControlLeft", true);
btn.classList.add("noVNC_selected");
}
},
toggleAlt: function() {
var btn = document.getElementById('noVNC_toggle_alt_button');
if (btn.classList.contains("noVNC_selected")) {
- UI.rfb.sendKey(KeyTable.XK_Alt_L, false);
+ UI.rfb.sendKey(KeyTable.XK_Alt_L, "AltLeft", false);
btn.classList.remove("noVNC_selected");
} else {
- UI.rfb.sendKey(KeyTable.XK_Alt_L, true);
+ UI.rfb.sendKey(KeyTable.XK_Alt_L, "AltLeft", true);
btn.classList.add("noVNC_selected");
}
},
}
},
- displayBlur: function() {
- if (UI.rfb && !UI.rfb.get_view_only()) {
- UI.rfb.get_keyboard().set_focused(false);
- UI.rfb.get_mouse().set_focused(false);
- }
- },
-
- displayFocus: function() {
- if (UI.rfb && !UI.rfb.get_view_only()) {
- UI.rfb.get_keyboard().set_focused(true);
- UI.rfb.get_mouse().set_focused(true);
- }
- },
-
updateLocalCursor: function() {
if (!UI.rfb) return;
UI.rfb.set_local_cursor(UI.getSetting('cursor'));
updateSessionSize: function(rfb, width, height) {
UI.updateViewClip();
+ UI.updateScaling();
UI.fixScrollbars();
},
bell: function(rfb) {
if (WebUtil.getConfigVar('bell', 'on') === 'on') {
- document.getElementById('noVNC_bell').play();
+ document.getElementById('noVNC_bell').play()
+ .catch(function(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
+ // initiated from a user action.
+ } else {
+ Log.Error("Unable to play bell: " + e);
+ }
+ });
}
},