Use ES6 classes
Always use the shorthand notation if the function is a method of an object or class `{ foo() { ... } }` or `class bar { foo() { ... } }` unless it's a callback in which case you a fat arrow function should be used `{ cb: () => { ... } }`
This commit is contained in:
parent
67fefcf184
commit
0e4808bf6f
|
@ -10,6 +10,8 @@
|
|||
"rules": {
|
||||
"no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": true }],
|
||||
"no-constant-condition": ["error", { "checkLoops": false }],
|
||||
"no-var": "error"
|
||||
"no-var": "error",
|
||||
"no-useless-constructor": "error",
|
||||
"object-shorthand": ["error", "methods", { "avoidQuotes": true }],
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,17 +10,17 @@
|
|||
* Localization Utilities
|
||||
*/
|
||||
|
||||
export function Localizer() {
|
||||
export class Localizer {
|
||||
constructor() {
|
||||
// Currently configured language
|
||||
this.language = 'en';
|
||||
|
||||
// Current dictionary of translations
|
||||
this.dictionary = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
Localizer.prototype = {
|
||||
// Configure suitable language based on user preferences
|
||||
setup: function (supportedLanguages) {
|
||||
setup(supportedLanguages) {
|
||||
this.language = 'en'; // Default: US English
|
||||
|
||||
/*
|
||||
|
@ -78,21 +78,22 @@ Localizer.prototype = {
|
|||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Retrieve localised text
|
||||
get: function (id) {
|
||||
get(id) {
|
||||
if (typeof this.dictionary !== 'undefined' && this.dictionary[id]) {
|
||||
return this.dictionary[id];
|
||||
} else {
|
||||
return id;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Traverses the DOM and translates relevant fields
|
||||
// See https://html.spec.whatwg.org/multipage/dom.html#attr-translate
|
||||
translateDOM: function () {
|
||||
translateDOM() {
|
||||
const self = this;
|
||||
|
||||
function process(elem, enabled) {
|
||||
function isAnyOf(searchElement, items) {
|
||||
return items.indexOf(searchElement) !== -1;
|
||||
|
@ -160,8 +161,8 @@ Localizer.prototype = {
|
|||
}
|
||||
|
||||
process(document.body, true);
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export const l10n = new Localizer();
|
||||
export default l10n.get.bind(l10n);
|
||||
|
|
190
app/ui.js
190
app/ui.js
|
@ -41,7 +41,7 @@ const UI = {
|
|||
reconnect_callback: null,
|
||||
reconnect_password: null,
|
||||
|
||||
prime: function(callback) {
|
||||
prime(callback) {
|
||||
if (document.readyState === "interactive" || document.readyState === "complete") {
|
||||
UI.load(callback);
|
||||
} else {
|
||||
|
@ -51,12 +51,12 @@ const UI = {
|
|||
|
||||
// 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 &&
|
||||
|
@ -116,7 +116,7 @@ const UI = {
|
|||
}
|
||||
},
|
||||
|
||||
initFullscreen: function() {
|
||||
initFullscreen() {
|
||||
// Only show the button if fullscreen is properly supported
|
||||
// * Safari doesn't support alphanumerical input while in fullscreen
|
||||
if (!UI.isSafari &&
|
||||
|
@ -130,7 +130,7 @@ const UI = {
|
|||
}
|
||||
},
|
||||
|
||||
initSettings: function() {
|
||||
initSettings() {
|
||||
// Logging selection dropdown
|
||||
const llevels = ['error', 'warn', 'info', 'debug'];
|
||||
for (let i = 0; i < llevels.length; i += 1) {
|
||||
|
@ -169,7 +169,7 @@ const UI = {
|
|||
UI.setupSettingLabels();
|
||||
},
|
||||
// Adds a link to the label elements on the corresponding input elements
|
||||
setupSettingLabels: function() {
|
||||
setupSettingLabels() {
|
||||
const labels = document.getElementsByTagName('LABEL');
|
||||
for (let i = 0; i < labels.length; i++) {
|
||||
const htmlFor = labels[i].htmlFor;
|
||||
|
@ -195,7 +195,7 @@ const UI = {
|
|||
* EVENT HANDLERS
|
||||
* ------v------*/
|
||||
|
||||
addControlbarHandlers: function() {
|
||||
addControlbarHandlers() {
|
||||
document.getElementById("noVNC_control_bar")
|
||||
.addEventListener('mousemove', UI.activateControlbar);
|
||||
document.getElementById("noVNC_control_bar")
|
||||
|
@ -228,7 +228,7 @@ const UI = {
|
|||
}
|
||||
},
|
||||
|
||||
addTouchSpecificHandlers: function() {
|
||||
addTouchSpecificHandlers() {
|
||||
document.getElementById("noVNC_mouse_button0")
|
||||
.addEventListener('click', function () { UI.setMouseButton(1); });
|
||||
document.getElementById("noVNC_mouse_button1")
|
||||
|
@ -277,7 +277,7 @@ const UI = {
|
|||
.addEventListener('touchmove', UI.dragControlbarHandle);
|
||||
},
|
||||
|
||||
addExtraKeysHandlers: function() {
|
||||
addExtraKeysHandlers() {
|
||||
document.getElementById("noVNC_toggle_extra_keys_button")
|
||||
.addEventListener('click', UI.toggleExtraKeys);
|
||||
document.getElementById("noVNC_toggle_ctrl_button")
|
||||
|
@ -292,7 +292,7 @@ const UI = {
|
|||
.addEventListener('click', UI.sendCtrlAltDel);
|
||||
},
|
||||
|
||||
addMachineHandlers: function() {
|
||||
addMachineHandlers() {
|
||||
document.getElementById("noVNC_shutdown_button")
|
||||
.addEventListener('click', function() { UI.rfb.machineShutdown(); });
|
||||
document.getElementById("noVNC_reboot_button")
|
||||
|
@ -303,7 +303,7 @@ const UI = {
|
|||
.addEventListener('click', UI.togglePowerPanel);
|
||||
},
|
||||
|
||||
addConnectionControlHandlers: function() {
|
||||
addConnectionControlHandlers() {
|
||||
document.getElementById("noVNC_disconnect_button")
|
||||
.addEventListener('click', UI.disconnect);
|
||||
document.getElementById("noVNC_connect_button")
|
||||
|
@ -315,7 +315,7 @@ const UI = {
|
|||
.addEventListener('click', UI.setPassword);
|
||||
},
|
||||
|
||||
addClipboardHandlers: function() {
|
||||
addClipboardHandlers() {
|
||||
document.getElementById("noVNC_clipboard_button")
|
||||
.addEventListener('click', UI.toggleClipboardPanel);
|
||||
document.getElementById("noVNC_clipboard_text")
|
||||
|
@ -326,7 +326,7 @@ const UI = {
|
|||
|
||||
// Add a call to save settings when the element changes,
|
||||
// unless the optional parameter changeFunc is used instead.
|
||||
addSettingChangeHandler: function(name, changeFunc) {
|
||||
addSettingChangeHandler(name, changeFunc) {
|
||||
const settingElem = document.getElementById("noVNC_setting_" + name);
|
||||
if (changeFunc === undefined) {
|
||||
changeFunc = function () { UI.saveSetting(name); };
|
||||
|
@ -334,7 +334,7 @@ const UI = {
|
|||
settingElem.addEventListener('change', changeFunc);
|
||||
},
|
||||
|
||||
addSettingsHandlers: function() {
|
||||
addSettingsHandlers() {
|
||||
document.getElementById("noVNC_settings_button")
|
||||
.addEventListener('click', UI.toggleSettingsPanel);
|
||||
|
||||
|
@ -357,7 +357,7 @@ const UI = {
|
|||
UI.addSettingChangeHandler('reconnect_delay');
|
||||
},
|
||||
|
||||
addFullscreenHandlers: function() {
|
||||
addFullscreenHandlers() {
|
||||
document.getElementById("noVNC_fullscreen_button")
|
||||
.addEventListener('click', UI.toggleFullscreen);
|
||||
|
||||
|
@ -374,7 +374,7 @@ const UI = {
|
|||
* ------v------*/
|
||||
|
||||
// Disable/enable controls depending on connection state
|
||||
updateVisualState: function(state) {
|
||||
updateVisualState(state) {
|
||||
|
||||
document.documentElement.classList.remove("noVNC_connecting");
|
||||
document.documentElement.classList.remove("noVNC_connected");
|
||||
|
@ -441,7 +441,7 @@ const UI = {
|
|||
.classList.remove('noVNC_open');
|
||||
},
|
||||
|
||||
showStatus: function(text, status_type, time) {
|
||||
showStatus(text, status_type, time) {
|
||||
const statusElem = document.getElementById('noVNC_status');
|
||||
|
||||
clearTimeout(UI.statusTimeout);
|
||||
|
@ -502,12 +502,12 @@ const UI = {
|
|||
}
|
||||
},
|
||||
|
||||
hideStatus: function() {
|
||||
hideStatus() {
|
||||
clearTimeout(UI.statusTimeout);
|
||||
document.getElementById('noVNC_status').classList.remove("noVNC_open");
|
||||
},
|
||||
|
||||
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
|
||||
|
@ -516,27 +516,27 @@ const UI = {
|
|||
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();
|
||||
|
@ -545,7 +545,7 @@ const UI = {
|
|||
}
|
||||
},
|
||||
|
||||
toggleControlbarSide: function () {
|
||||
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');
|
||||
|
@ -569,7 +569,7 @@ const UI = {
|
|||
UI.controlbarDrag = true;
|
||||
},
|
||||
|
||||
showControlbarHint: function (show) {
|
||||
showControlbarHint(show) {
|
||||
const hint = document.getElementById('noVNC_control_bar_hint');
|
||||
if (show) {
|
||||
hint.classList.add("noVNC_active");
|
||||
|
@ -578,7 +578,7 @@ const UI = {
|
|||
}
|
||||
},
|
||||
|
||||
dragControlbarHandle: function (e) {
|
||||
dragControlbarHandle(e) {
|
||||
if (!UI.controlbarGrabbed) return;
|
||||
|
||||
const ptr = getPointerEvent(e);
|
||||
|
@ -616,7 +616,7 @@ const UI = {
|
|||
},
|
||||
|
||||
// Move the handle but don't allow any position outside the bounds
|
||||
moveControlbarHandle: function (viewportRelativeY) {
|
||||
moveControlbarHandle(viewportRelativeY) {
|
||||
const handle = document.getElementById("noVNC_control_bar_handle");
|
||||
const handleHeight = handle.getBoundingClientRect().height;
|
||||
const controlbarBounds = document.getElementById("noVNC_control_bar")
|
||||
|
@ -653,7 +653,7 @@ const UI = {
|
|||
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.
|
||||
const handle = document.getElementById("noVNC_control_bar_handle");
|
||||
|
@ -661,7 +661,7 @@ const UI = {
|
|||
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
|
||||
|
@ -676,7 +676,7 @@ const UI = {
|
|||
UI.showControlbarHint(false);
|
||||
},
|
||||
|
||||
controlbarHandleMouseDown: function(e) {
|
||||
controlbarHandleMouseDown(e) {
|
||||
if ((e.type == "mousedown") && (e.button != 0)) return;
|
||||
|
||||
const ptr = getPointerEvent(e);
|
||||
|
@ -702,7 +702,7 @@ const UI = {
|
|||
UI.activateControlbar();
|
||||
},
|
||||
|
||||
toggleExpander: function(e) {
|
||||
toggleExpander(e) {
|
||||
if (this.classList.contains("noVNC_open")) {
|
||||
this.classList.remove("noVNC_open");
|
||||
} else {
|
||||
|
@ -717,7 +717,7 @@ const UI = {
|
|||
* ------v------*/
|
||||
|
||||
// Initial page load read/initialization of settings
|
||||
initSetting: function(name, defVal) {
|
||||
initSetting(name, defVal) {
|
||||
// Check Query string followed by cookie
|
||||
let val = WebUtil.getConfigVar(name);
|
||||
if (val === null) {
|
||||
|
@ -729,7 +729,7 @@ const UI = {
|
|||
},
|
||||
|
||||
// Set the new value, update and disable form control setting
|
||||
forceSetting: function(name, val) {
|
||||
forceSetting(name, val) {
|
||||
WebUtil.setSetting(name, val);
|
||||
UI.updateSetting(name);
|
||||
UI.disableSetting(name);
|
||||
|
@ -737,7 +737,7 @@ const UI = {
|
|||
|
||||
// Update cookie and form control setting. If value is not set, then
|
||||
// updates from control to current cookie setting.
|
||||
updateSetting: function(name) {
|
||||
updateSetting(name) {
|
||||
|
||||
// Update the settings control
|
||||
let value = UI.getSetting(name);
|
||||
|
@ -764,7 +764,7 @@ const UI = {
|
|||
},
|
||||
|
||||
// Save control setting to cookie
|
||||
saveSetting: function(name) {
|
||||
saveSetting(name) {
|
||||
const ctrl = document.getElementById('noVNC_setting_' + name);
|
||||
let val;
|
||||
if (ctrl.type === 'checkbox') {
|
||||
|
@ -780,7 +780,7 @@ const UI = {
|
|||
},
|
||||
|
||||
// Read form control compatible setting from cookie
|
||||
getSetting: function(name) {
|
||||
getSetting(name) {
|
||||
const ctrl = document.getElementById('noVNC_setting_' + name);
|
||||
let val = WebUtil.readSetting(name);
|
||||
if (typeof val !== 'undefined' && val !== null && ctrl.type === 'checkbox') {
|
||||
|
@ -796,13 +796,13 @@ const UI = {
|
|||
// 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) {
|
||||
disableSetting(name) {
|
||||
const ctrl = document.getElementById('noVNC_setting_' + name);
|
||||
ctrl.disabled = true;
|
||||
ctrl.label.classList.add('noVNC_disabled');
|
||||
},
|
||||
|
||||
enableSetting: function(name) {
|
||||
enableSetting(name) {
|
||||
const ctrl = document.getElementById('noVNC_setting_' + name);
|
||||
ctrl.disabled = false;
|
||||
ctrl.label.classList.remove('noVNC_disabled');
|
||||
|
@ -814,7 +814,7 @@ const UI = {
|
|||
* PANELS
|
||||
* ------v------*/
|
||||
|
||||
closeAllPanels: function() {
|
||||
closeAllPanels() {
|
||||
UI.closeSettingsPanel();
|
||||
UI.closePowerPanel();
|
||||
UI.closeClipboardPanel();
|
||||
|
@ -827,7 +827,7 @@ const UI = {
|
|||
* SETTINGS (panel)
|
||||
* ------v------*/
|
||||
|
||||
openSettingsPanel: function() {
|
||||
openSettingsPanel() {
|
||||
UI.closeAllPanels();
|
||||
UI.openControlbar();
|
||||
|
||||
|
@ -849,14 +849,14 @@ const UI = {
|
|||
.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();
|
||||
|
@ -871,7 +871,7 @@ const UI = {
|
|||
* POWER
|
||||
* ------v------*/
|
||||
|
||||
openPowerPanel: function() {
|
||||
openPowerPanel() {
|
||||
UI.closeAllPanels();
|
||||
UI.openControlbar();
|
||||
|
||||
|
@ -881,14 +881,14 @@ const UI = {
|
|||
.classList.add("noVNC_selected");
|
||||
},
|
||||
|
||||
closePowerPanel: function() {
|
||||
closePowerPanel() {
|
||||
document.getElementById('noVNC_power')
|
||||
.classList.remove("noVNC_open");
|
||||
document.getElementById('noVNC_power_button')
|
||||
.classList.remove("noVNC_selected");
|
||||
},
|
||||
|
||||
togglePowerPanel: function() {
|
||||
togglePowerPanel() {
|
||||
if (document.getElementById('noVNC_power')
|
||||
.classList.contains("noVNC_open")) {
|
||||
UI.closePowerPanel();
|
||||
|
@ -898,7 +898,7 @@ const UI = {
|
|||
},
|
||||
|
||||
// Disable/enable power button
|
||||
updatePowerButton: function() {
|
||||
updatePowerButton() {
|
||||
if (UI.connected &&
|
||||
UI.rfb.capabilities.power &&
|
||||
!UI.rfb.viewOnly) {
|
||||
|
@ -918,7 +918,7 @@ const UI = {
|
|||
* CLIPBOARD
|
||||
* ------v------*/
|
||||
|
||||
openClipboardPanel: function() {
|
||||
openClipboardPanel() {
|
||||
UI.closeAllPanels();
|
||||
UI.openControlbar();
|
||||
|
||||
|
@ -928,14 +928,14 @@ const UI = {
|
|||
.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();
|
||||
|
@ -944,18 +944,18 @@ const UI = {
|
|||
}
|
||||
},
|
||||
|
||||
clipboardReceive: function(e) {
|
||||
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() {
|
||||
clipboardSend() {
|
||||
const text = document.getElementById('noVNC_clipboard_text').value;
|
||||
Log.Debug(">> UI.clipboardSend: " + text.substr(0,40) + "...");
|
||||
UI.rfb.clipboardPasteFrom(text);
|
||||
|
@ -968,17 +968,17 @@ const UI = {
|
|||
* 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) {
|
||||
connect(event, password) {
|
||||
|
||||
// Ignore when rfb already exists
|
||||
if (typeof UI.rfb !== 'undefined') {
|
||||
|
@ -1040,7 +1040,7 @@ const UI = {
|
|||
UI.updateViewOnly(); // requires UI.rfb
|
||||
},
|
||||
|
||||
disconnect: function() {
|
||||
disconnect() {
|
||||
UI.closeAllPanels();
|
||||
UI.rfb.disconnect();
|
||||
|
||||
|
@ -1054,7 +1054,7 @@ const UI = {
|
|||
// 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.
|
||||
|
@ -1065,7 +1065,7 @@ const UI = {
|
|||
UI.connect(null, UI.reconnect_password);
|
||||
},
|
||||
|
||||
cancelReconnect: function() {
|
||||
cancelReconnect() {
|
||||
if (UI.reconnect_callback !== null) {
|
||||
clearTimeout(UI.reconnect_callback);
|
||||
UI.reconnect_callback = null;
|
||||
|
@ -1077,7 +1077,7 @@ const UI = {
|
|||
UI.openConnectPanel();
|
||||
},
|
||||
|
||||
connectFinished: function (e) {
|
||||
connectFinished(e) {
|
||||
UI.connected = true;
|
||||
UI.inhibit_reconnect = false;
|
||||
|
||||
|
@ -1094,7 +1094,7 @@ const UI = {
|
|||
UI.rfb.focus();
|
||||
},
|
||||
|
||||
disconnectFinished: function (e) {
|
||||
disconnectFinished(e) {
|
||||
const wasConnected = UI.connected;
|
||||
|
||||
// This variable is ideally set when disconnection starts, but
|
||||
|
@ -1128,7 +1128,7 @@ const UI = {
|
|||
UI.openConnectPanel();
|
||||
},
|
||||
|
||||
securityFailed: function (e) {
|
||||
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
|
||||
|
@ -1148,7 +1148,7 @@ const UI = {
|
|||
* PASSWORD
|
||||
* ------v------*/
|
||||
|
||||
credentials: function(e) {
|
||||
credentials(e) {
|
||||
// FIXME: handle more types
|
||||
document.getElementById('noVNC_password_dlg')
|
||||
.classList.add('noVNC_open');
|
||||
|
@ -1161,7 +1161,7 @@ const UI = {
|
|||
UI.showStatus(_("Password is required"), "warning");
|
||||
},
|
||||
|
||||
setPassword: function(e) {
|
||||
setPassword(e) {
|
||||
// Prevent actually submitting the form
|
||||
e.preventDefault();
|
||||
|
||||
|
@ -1181,7 +1181,7 @@ const UI = {
|
|||
* FULLSCREEN
|
||||
* ------v------*/
|
||||
|
||||
toggleFullscreen: function() {
|
||||
toggleFullscreen() {
|
||||
if (document.fullscreenElement || // alternative standard method
|
||||
document.mozFullScreenElement || // currently working methods
|
||||
document.webkitFullscreenElement ||
|
||||
|
@ -1210,7 +1210,7 @@ const UI = {
|
|||
UI.updateFullscreenButton();
|
||||
},
|
||||
|
||||
updateFullscreenButton: function() {
|
||||
updateFullscreenButton() {
|
||||
if (document.fullscreenElement || // alternative standard method
|
||||
document.mozFullScreenElement || // currently working methods
|
||||
document.webkitFullscreenElement ||
|
||||
|
@ -1230,7 +1230,7 @@ const UI = {
|
|||
* ------v------*/
|
||||
|
||||
// Apply remote resizing or local scaling
|
||||
applyResizeMode: function() {
|
||||
applyResizeMode() {
|
||||
if (!UI.rfb) return;
|
||||
|
||||
UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale';
|
||||
|
@ -1244,7 +1244,7 @@ const UI = {
|
|||
* ------v------*/
|
||||
|
||||
// Update parameters that depend on the viewport clip setting
|
||||
updateViewClip: function() {
|
||||
updateViewClip() {
|
||||
if (!UI.rfb) return;
|
||||
|
||||
const cur_clip = UI.rfb.clipViewport;
|
||||
|
@ -1265,7 +1265,7 @@ const UI = {
|
|||
},
|
||||
|
||||
// Handle special cases where viewport clipping is locked
|
||||
enableDisableViewClip: function() {
|
||||
enableDisableViewClip() {
|
||||
const resizeSetting = UI.getSetting('resize');
|
||||
if (isTouchDevice) {
|
||||
UI.forceSetting('view_clip', true);
|
||||
|
@ -1282,7 +1282,7 @@ const UI = {
|
|||
* VIEWDRAG
|
||||
* ------v------*/
|
||||
|
||||
toggleViewDrag: function() {
|
||||
toggleViewDrag() {
|
||||
if (!UI.rfb) return;
|
||||
|
||||
const drag = UI.rfb.dragViewport;
|
||||
|
@ -1290,7 +1290,7 @@ const UI = {
|
|||
},
|
||||
|
||||
// Set the view drag mode which moves the viewport on mouse drags
|
||||
setViewDrag: function(drag) {
|
||||
setViewDrag(drag) {
|
||||
if (!UI.rfb) return;
|
||||
|
||||
UI.rfb.dragViewport = drag;
|
||||
|
@ -1298,7 +1298,7 @@ const UI = {
|
|||
UI.updateViewDrag();
|
||||
},
|
||||
|
||||
updateViewDrag: function() {
|
||||
updateViewDrag() {
|
||||
if (!UI.connected) return;
|
||||
|
||||
const viewDragButton = document.getElementById('noVNC_view_drag_button');
|
||||
|
@ -1342,7 +1342,7 @@ const UI = {
|
|||
* KEYBOARD
|
||||
* ------v------*/
|
||||
|
||||
showVirtualKeyboard: function() {
|
||||
showVirtualKeyboard() {
|
||||
if (!isTouchDevice) return;
|
||||
|
||||
const input = document.getElementById('noVNC_keyboardinput');
|
||||
|
@ -1360,7 +1360,7 @@ const UI = {
|
|||
}
|
||||
},
|
||||
|
||||
hideVirtualKeyboard: function() {
|
||||
hideVirtualKeyboard() {
|
||||
if (!isTouchDevice) return;
|
||||
|
||||
const input = document.getElementById('noVNC_keyboardinput');
|
||||
|
@ -1370,7 +1370,7 @@ const UI = {
|
|||
input.blur();
|
||||
},
|
||||
|
||||
toggleVirtualKeyboard: function () {
|
||||
toggleVirtualKeyboard() {
|
||||
if (document.getElementById('noVNC_keyboard_button')
|
||||
.classList.contains("noVNC_selected")) {
|
||||
UI.hideVirtualKeyboard();
|
||||
|
@ -1379,7 +1379,7 @@ const UI = {
|
|||
}
|
||||
},
|
||||
|
||||
onfocusVirtualKeyboard: function(event) {
|
||||
onfocusVirtualKeyboard(event) {
|
||||
document.getElementById('noVNC_keyboard_button')
|
||||
.classList.add("noVNC_selected");
|
||||
if (UI.rfb) {
|
||||
|
@ -1387,7 +1387,7 @@ const UI = {
|
|||
}
|
||||
},
|
||||
|
||||
onblurVirtualKeyboard: function(event) {
|
||||
onblurVirtualKeyboard(event) {
|
||||
document.getElementById('noVNC_keyboard_button')
|
||||
.classList.remove("noVNC_selected");
|
||||
if (UI.rfb) {
|
||||
|
@ -1395,7 +1395,7 @@ const UI = {
|
|||
}
|
||||
},
|
||||
|
||||
keepVirtualKeyboard: function(event) {
|
||||
keepVirtualKeyboard(event) {
|
||||
const input = document.getElementById('noVNC_keyboardinput');
|
||||
|
||||
// Only prevent focus change if the virtual keyboard is active
|
||||
|
@ -1423,13 +1423,13 @@ const UI = {
|
|||
event.preventDefault();
|
||||
},
|
||||
|
||||
keyboardinputReset: function() {
|
||||
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);
|
||||
|
@ -1439,7 +1439,7 @@ const UI = {
|
|||
// 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;
|
||||
|
||||
|
@ -1506,7 +1506,7 @@ const UI = {
|
|||
* EXTRA KEYS
|
||||
* ------v------*/
|
||||
|
||||
openExtraKeys: function() {
|
||||
openExtraKeys() {
|
||||
UI.closeAllPanels();
|
||||
UI.openControlbar();
|
||||
|
||||
|
@ -1516,14 +1516,14 @@ const UI = {
|
|||
.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();
|
||||
|
@ -1532,15 +1532,15 @@ const UI = {
|
|||
}
|
||||
},
|
||||
|
||||
sendEsc: function() {
|
||||
sendEsc() {
|
||||
UI.rfb.sendKey(KeyTable.XK_Escape, "Escape");
|
||||
},
|
||||
|
||||
sendTab: function() {
|
||||
sendTab() {
|
||||
UI.rfb.sendKey(KeyTable.XK_Tab);
|
||||
},
|
||||
|
||||
toggleCtrl: function() {
|
||||
toggleCtrl() {
|
||||
const btn = document.getElementById('noVNC_toggle_ctrl_button');
|
||||
if (btn.classList.contains("noVNC_selected")) {
|
||||
UI.rfb.sendKey(KeyTable.XK_Control_L, "ControlLeft", false);
|
||||
|
@ -1551,7 +1551,7 @@ const UI = {
|
|||
}
|
||||
},
|
||||
|
||||
toggleAlt: function() {
|
||||
toggleAlt() {
|
||||
const btn = document.getElementById('noVNC_toggle_alt_button');
|
||||
if (btn.classList.contains("noVNC_selected")) {
|
||||
UI.rfb.sendKey(KeyTable.XK_Alt_L, "AltLeft", false);
|
||||
|
@ -1562,7 +1562,7 @@ const UI = {
|
|||
}
|
||||
},
|
||||
|
||||
sendCtrlAltDel: function() {
|
||||
sendCtrlAltDel() {
|
||||
UI.rfb.sendCtrlAltDel();
|
||||
},
|
||||
|
||||
|
@ -1572,7 +1572,7 @@ const UI = {
|
|||
* MISC
|
||||
* ------v------*/
|
||||
|
||||
setMouseButton: function(num) {
|
||||
setMouseButton(num) {
|
||||
const view_only = UI.rfb.viewOnly;
|
||||
if (UI.rfb && !view_only) {
|
||||
UI.rfb.touchButton = num;
|
||||
|
@ -1590,7 +1590,7 @@ const UI = {
|
|||
}
|
||||
},
|
||||
|
||||
updateViewOnly: function() {
|
||||
updateViewOnly() {
|
||||
if (!UI.rfb) return;
|
||||
UI.rfb.viewOnly = UI.getSetting('view_only');
|
||||
|
||||
|
@ -1609,17 +1609,17 @@ const UI = {
|
|||
UI.setMouseButton(1); //has it's own logic for hiding/showing
|
||||
},
|
||||
|
||||
updateLogging: function() {
|
||||
updateLogging() {
|
||||
WebUtil.init_logging(UI.getSetting('logging'));
|
||||
},
|
||||
|
||||
updateDesktopName: function(e) {
|
||||
updateDesktopName(e) {
|
||||
UI.desktopName = e.detail.name;
|
||||
// Display the desktop name in the document title
|
||||
document.title = e.detail.name + " - noVNC";
|
||||
},
|
||||
|
||||
bell: function(e) {
|
||||
bell(e) {
|
||||
if (WebUtil.getConfigVar('bell', 'on') === 'on') {
|
||||
const promise = document.getElementById('noVNC_bell').play();
|
||||
// The standards disagree on the return value here
|
||||
|
@ -1638,7 +1638,7 @@ const UI = {
|
|||
},
|
||||
|
||||
//Helper to add options to dropdown.
|
||||
addOption: function(selectbox, text, value) {
|
||||
addOption(selectbox, text, value) {
|
||||
const optn = document.createElement("OPTION");
|
||||
optn.text = text;
|
||||
optn.value = value;
|
||||
|
|
|
@ -11,7 +11,7 @@ export default {
|
|||
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
|
||||
base64Pad : '=',
|
||||
|
||||
encode: function (data) {
|
||||
encode(data) {
|
||||
"use strict";
|
||||
let result = '';
|
||||
const length = data.length;
|
||||
|
@ -54,8 +54,7 @@ export default {
|
|||
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
|
||||
],
|
||||
|
||||
decode: function (data, offset) {
|
||||
"use strict";
|
||||
decode(data, offset) {
|
||||
offset = typeof(offset) !== 'undefined' ? offset : 0;
|
||||
|
||||
let data_length = data.indexOf('=') - offset;
|
||||
|
|
171
core/display.js
171
core/display.js
|
@ -10,7 +10,16 @@
|
|||
import * as Log from './util/logging.js';
|
||||
import Base64 from "./base64.js";
|
||||
|
||||
export default function Display(target) {
|
||||
let SUPPORTS_IMAGEDATA_CONSTRUCTOR = false;
|
||||
try {
|
||||
new ImageData(new Uint8ClampedArray(4), 1, 1);
|
||||
SUPPORTS_IMAGEDATA_CONSTRUCTOR = true;
|
||||
} catch (ex) {
|
||||
// ignore failure
|
||||
}
|
||||
|
||||
export default class Display {
|
||||
constructor(target) {
|
||||
this._drawCtx = null;
|
||||
this._c_forceCanvas = false;
|
||||
|
||||
|
@ -68,51 +77,45 @@ export default function Display(target) {
|
|||
|
||||
this._tile16x16 = this._drawCtx.createImageData(16, 16);
|
||||
Log.Debug("<< Display.constructor");
|
||||
}
|
||||
|
||||
let SUPPORTS_IMAGEDATA_CONSTRUCTOR = false;
|
||||
try {
|
||||
new ImageData(new Uint8ClampedArray(4), 1, 1);
|
||||
SUPPORTS_IMAGEDATA_CONSTRUCTOR = true;
|
||||
} catch (ex) {
|
||||
// ignore failure
|
||||
}
|
||||
|
||||
Display.prototype = {
|
||||
// ===== PROPERTIES =====
|
||||
|
||||
_scale: 1.0,
|
||||
get scale() { return this._scale; },
|
||||
this._scale = 1.0;
|
||||
this._clipViewport = false;
|
||||
this.logo = null;
|
||||
|
||||
// ===== EVENT HANDLERS =====
|
||||
|
||||
this.onflush = () => {}; // A flush request has finished
|
||||
}
|
||||
|
||||
// ===== PROPERTIES =====
|
||||
|
||||
get scale() { return this._scale; }
|
||||
set scale(scale) {
|
||||
this._rescale(scale);
|
||||
},
|
||||
}
|
||||
|
||||
_clipViewport: false,
|
||||
get clipViewport() { return this._clipViewport; },
|
||||
get clipViewport() { return this._clipViewport; }
|
||||
set clipViewport(viewport) {
|
||||
this._clipViewport = viewport;
|
||||
// May need to readjust the viewport dimensions
|
||||
const vp = this._viewportLoc;
|
||||
this.viewportChangeSize(vp.w, vp.h);
|
||||
this.viewportChangePos(0, 0);
|
||||
},
|
||||
}
|
||||
|
||||
get width() {
|
||||
return this._fb_width;
|
||||
},
|
||||
}
|
||||
|
||||
get height() {
|
||||
return this._fb_height;
|
||||
},
|
||||
|
||||
logo: null,
|
||||
|
||||
// ===== EVENT HANDLERS =====
|
||||
|
||||
onflush: function () {}, // A flush request has finished
|
||||
}
|
||||
|
||||
// ===== PUBLIC METHODS =====
|
||||
|
||||
viewportChangePos: function (deltaX, deltaY) {
|
||||
viewportChangePos(deltaX, deltaY) {
|
||||
const vp = this._viewportLoc;
|
||||
deltaX = Math.floor(deltaX);
|
||||
deltaY = Math.floor(deltaY);
|
||||
|
@ -152,9 +155,9 @@ Display.prototype = {
|
|||
this._damage(vp.x, vp.y, vp.w, vp.h);
|
||||
|
||||
this.flip();
|
||||
},
|
||||
}
|
||||
|
||||
viewportChangeSize: function(width, height) {
|
||||
viewportChangeSize(width, height) {
|
||||
|
||||
if (!this._clipViewport ||
|
||||
typeof(width) === "undefined" ||
|
||||
|
@ -190,17 +193,17 @@ Display.prototype = {
|
|||
// Update the visible size of the target canvas
|
||||
this._rescale(this._scale);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
absX: function (x) {
|
||||
absX(x) {
|
||||
return x / this._scale + this._viewportLoc.x;
|
||||
},
|
||||
}
|
||||
|
||||
absY: function (y) {
|
||||
absY(y) {
|
||||
return y / this._scale + this._viewportLoc.y;
|
||||
},
|
||||
}
|
||||
|
||||
resize: function (width, height) {
|
||||
resize(width, height) {
|
||||
this._prevDrawStyle = "";
|
||||
|
||||
this._fb_width = width;
|
||||
|
@ -232,10 +235,10 @@ Display.prototype = {
|
|||
const vp = this._viewportLoc;
|
||||
this.viewportChangeSize(vp.w, vp.h);
|
||||
this.viewportChangePos(0, 0);
|
||||
},
|
||||
}
|
||||
|
||||
// Track what parts of the visible canvas that need updating
|
||||
_damage: function(x, y, w, h) {
|
||||
_damage(x, y, w, h) {
|
||||
if (x < this._damageBounds.left) {
|
||||
this._damageBounds.left = x;
|
||||
}
|
||||
|
@ -248,11 +251,11 @@ Display.prototype = {
|
|||
if ((y + h) > this._damageBounds.bottom) {
|
||||
this._damageBounds.bottom = y + h;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Update the visible canvas with the contents of the
|
||||
// rendering canvas
|
||||
flip: function(from_queue) {
|
||||
flip(from_queue) {
|
||||
if (this._renderQ.length !== 0 && !from_queue) {
|
||||
this._renderQ_push({
|
||||
'type': 'flip'
|
||||
|
@ -296,9 +299,9 @@ Display.prototype = {
|
|||
this._damageBounds.left = this._damageBounds.top = 65535;
|
||||
this._damageBounds.right = this._damageBounds.bottom = 0;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
clear: function () {
|
||||
clear() {
|
||||
if (this._logo) {
|
||||
this.resize(this._logo.width, this._logo.height);
|
||||
this.imageRect(0, 0, this._logo.type, this._logo.data);
|
||||
|
@ -307,21 +310,21 @@ Display.prototype = {
|
|||
this._drawCtx.clearRect(0, 0, this._fb_width, this._fb_height);
|
||||
}
|
||||
this.flip();
|
||||
},
|
||||
}
|
||||
|
||||
pending: function() {
|
||||
pending() {
|
||||
return this._renderQ.length > 0;
|
||||
},
|
||||
}
|
||||
|
||||
flush: function() {
|
||||
flush() {
|
||||
if (this._renderQ.length === 0) {
|
||||
this.onflush();
|
||||
} else {
|
||||
this._flushing = true;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
fillRect: function (x, y, width, height, color, from_queue) {
|
||||
fillRect(x, y, width, height, color, from_queue) {
|
||||
if (this._renderQ.length !== 0 && !from_queue) {
|
||||
this._renderQ_push({
|
||||
'type': 'fill',
|
||||
|
@ -336,9 +339,9 @@ Display.prototype = {
|
|||
this._drawCtx.fillRect(x, y, width, height);
|
||||
this._damage(x, y, width, height);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
copyImage: function (old_x, old_y, new_x, new_y, w, h, from_queue) {
|
||||
copyImage(old_x, old_y, new_x, new_y, w, h, from_queue) {
|
||||
if (this._renderQ.length !== 0 && !from_queue) {
|
||||
this._renderQ_push({
|
||||
'type': 'copy',
|
||||
|
@ -367,9 +370,9 @@ Display.prototype = {
|
|||
new_x, new_y, w, h);
|
||||
this._damage(new_x, new_y, w, h);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
imageRect: function(x, y, mime, arr) {
|
||||
imageRect(x, y, mime, arr) {
|
||||
const img = new Image();
|
||||
img.src = "data: " + mime + ";base64," + Base64.encode(arr);
|
||||
this._renderQ_push({
|
||||
|
@ -378,10 +381,10 @@ Display.prototype = {
|
|||
'x': x,
|
||||
'y': y
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
// start updating a tile
|
||||
startTile: function (x, y, width, height, color) {
|
||||
startTile(x, y, width, height, color) {
|
||||
this._tile_x = x;
|
||||
this._tile_y = y;
|
||||
if (width === 16 && height === 16) {
|
||||
|
@ -401,10 +404,10 @@ Display.prototype = {
|
|||
data[i + 2] = blue;
|
||||
data[i + 3] = 255;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// update sub-rectangle of the current tile
|
||||
subTile: function (x, y, w, h, color) {
|
||||
subTile(x, y, w, h, color) {
|
||||
const red = color[2];
|
||||
const green = color[1];
|
||||
const blue = color[0];
|
||||
|
@ -422,16 +425,16 @@ Display.prototype = {
|
|||
data[p + 3] = 255;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// draw the current tile to the screen
|
||||
finishTile: function () {
|
||||
finishTile() {
|
||||
this._drawCtx.putImageData(this._tile, this._tile_x, this._tile_y);
|
||||
this._damage(this._tile_x, this._tile_y,
|
||||
this._tile.width, this._tile.height);
|
||||
},
|
||||
}
|
||||
|
||||
blitImage: function (x, y, width, height, arr, offset, from_queue) {
|
||||
blitImage(x, y, width, height, arr, offset, from_queue) {
|
||||
if (this._renderQ.length !== 0 && !from_queue) {
|
||||
// NB(directxman12): it's technically more performant here to use preallocated arrays,
|
||||
// but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
|
||||
|
@ -449,9 +452,9 @@ Display.prototype = {
|
|||
} else {
|
||||
this._bgrxImageData(x, y, width, height, arr, offset);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
blitRgbImage: function (x, y , width, height, arr, offset, from_queue) {
|
||||
blitRgbImage(x, y , width, height, arr, offset, from_queue) {
|
||||
if (this._renderQ.length !== 0 && !from_queue) {
|
||||
// NB(directxman12): it's technically more performant here to use preallocated arrays,
|
||||
// but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
|
||||
|
@ -469,9 +472,9 @@ Display.prototype = {
|
|||
} else {
|
||||
this._rgbImageData(x, y, width, height, arr, offset);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
blitRgbxImage: function (x, y, width, height, arr, offset, from_queue) {
|
||||
blitRgbxImage(x, y, width, height, arr, offset, from_queue) {
|
||||
if (this._renderQ.length !== 0 && !from_queue) {
|
||||
// NB(directxman12): it's technically more performant here to use preallocated arrays,
|
||||
// but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
|
||||
|
@ -489,14 +492,14 @@ Display.prototype = {
|
|||
} else {
|
||||
this._rgbxImageData(x, y, width, height, arr, offset);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
drawImage: function (img, x, y) {
|
||||
drawImage(img, x, y) {
|
||||
this._drawCtx.drawImage(img, x, y);
|
||||
this._damage(x, y, img.width, img.height);
|
||||
},
|
||||
}
|
||||
|
||||
autoscale: function (containerWidth, containerHeight) {
|
||||
autoscale(containerWidth, containerHeight) {
|
||||
const vp = this._viewportLoc;
|
||||
const targetAspectRatio = containerWidth / containerHeight;
|
||||
const fbAspectRatio = vp.w / vp.h;
|
||||
|
@ -509,11 +512,11 @@ Display.prototype = {
|
|||
}
|
||||
|
||||
this._rescale(scaleRatio);
|
||||
},
|
||||
}
|
||||
|
||||
// ===== PRIVATE METHODS =====
|
||||
|
||||
_rescale: function (factor) {
|
||||
_rescale(factor) {
|
||||
this._scale = factor;
|
||||
const vp = this._viewportLoc;
|
||||
|
||||
|
@ -529,17 +532,17 @@ Display.prototype = {
|
|||
this._target.style.width = width;
|
||||
this._target.style.height = height;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_setFillColor: function (color) {
|
||||
_setFillColor(color) {
|
||||
const newStyle = 'rgb(' + color[2] + ',' + color[1] + ',' + color[0] + ')';
|
||||
if (newStyle !== this._prevDrawStyle) {
|
||||
this._drawCtx.fillStyle = newStyle;
|
||||
this._prevDrawStyle = newStyle;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_rgbImageData: function (x, y, width, height, arr, offset) {
|
||||
_rgbImageData(x, y, width, height, arr, offset) {
|
||||
const img = this._drawCtx.createImageData(width, height);
|
||||
const data = img.data;
|
||||
for (let i = 0, j = offset; i < width * height * 4; i += 4, j += 3) {
|
||||
|
@ -550,9 +553,9 @@ Display.prototype = {
|
|||
}
|
||||
this._drawCtx.putImageData(img, x, y);
|
||||
this._damage(x, y, img.width, img.height);
|
||||
},
|
||||
}
|
||||
|
||||
_bgrxImageData: function (x, y, width, height, arr, offset) {
|
||||
_bgrxImageData(x, y, width, height, arr, offset) {
|
||||
const img = this._drawCtx.createImageData(width, height);
|
||||
const data = img.data;
|
||||
for (let i = 0, j = offset; i < width * height * 4; i += 4, j += 4) {
|
||||
|
@ -563,9 +566,9 @@ Display.prototype = {
|
|||
}
|
||||
this._drawCtx.putImageData(img, x, y);
|
||||
this._damage(x, y, img.width, img.height);
|
||||
},
|
||||
}
|
||||
|
||||
_rgbxImageData: function (x, y, width, height, arr, offset) {
|
||||
_rgbxImageData(x, y, width, height, arr, offset) {
|
||||
// NB(directxman12): arr must be an Type Array view
|
||||
let img;
|
||||
if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) {
|
||||
|
@ -576,25 +579,25 @@ Display.prototype = {
|
|||
}
|
||||
this._drawCtx.putImageData(img, x, y);
|
||||
this._damage(x, y, img.width, img.height);
|
||||
},
|
||||
}
|
||||
|
||||
_renderQ_push: function (action) {
|
||||
_renderQ_push(action) {
|
||||
this._renderQ.push(action);
|
||||
if (this._renderQ.length === 1) {
|
||||
// If this can be rendered immediately it will be, otherwise
|
||||
// the scanner will wait for the relevant event
|
||||
this._scan_renderQ();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_resume_renderQ: function() {
|
||||
_resume_renderQ() {
|
||||
// "this" is the object that is ready, not the
|
||||
// display object
|
||||
this.removeEventListener('load', this._noVNC_display._resume_renderQ);
|
||||
this._noVNC_display._scan_renderQ();
|
||||
},
|
||||
}
|
||||
|
||||
_scan_renderQ: function () {
|
||||
_scan_renderQ() {
|
||||
let ready = true;
|
||||
while (ready && this._renderQ.length > 0) {
|
||||
const a = this._renderQ[0];
|
||||
|
@ -639,5 +642,5 @@ Display.prototype = {
|
|||
this._flushing = false;
|
||||
this.onflush();
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,17 @@
|
|||
import { inflateInit, inflate, inflateReset } from "../vendor/pako/lib/zlib/inflate.js";
|
||||
import ZStream from "../vendor/pako/lib/zlib/zstream.js";
|
||||
|
||||
Inflate.prototype = {
|
||||
inflate: function (data, flush, expected) {
|
||||
export default class Inflate {
|
||||
constructor() {
|
||||
this.strm = new ZStream();
|
||||
this.chunkSize = 1024 * 10 * 10;
|
||||
this.strm.output = new Uint8Array(this.chunkSize);
|
||||
this.windowBits = 5;
|
||||
|
||||
inflateInit(this.strm, this.windowBits);
|
||||
}
|
||||
|
||||
inflate(data, flush, expected) {
|
||||
this.strm.input = data;
|
||||
this.strm.avail_in = this.strm.input.length;
|
||||
this.strm.next_in = 0;
|
||||
|
@ -21,18 +30,9 @@ Inflate.prototype = {
|
|||
inflate(this.strm, flush);
|
||||
|
||||
return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
|
||||
},
|
||||
}
|
||||
|
||||
reset: function () {
|
||||
reset() {
|
||||
inflateReset(this.strm);
|
||||
}
|
||||
};
|
||||
|
||||
export default function Inflate() {
|
||||
this.strm = new ZStream();
|
||||
this.chunkSize = 1024 * 10 * 10;
|
||||
this.strm.output = new Uint8Array(this.chunkSize);
|
||||
this.windowBits = 5;
|
||||
|
||||
inflateInit(this.strm, this.windowBits);
|
||||
}
|
||||
|
|
|
@ -15,7 +15,8 @@ import * as browser from "../util/browser.js";
|
|||
// Keyboard event handler
|
||||
//
|
||||
|
||||
export default function Keyboard(target) {
|
||||
export default class Keyboard {
|
||||
constructor(target) {
|
||||
this._target = target || null;
|
||||
|
||||
this._keyDownList = {}; // List of depressed keys
|
||||
|
@ -31,16 +32,15 @@ export default function Keyboard(target) {
|
|||
'blur': this._allKeysUp.bind(this),
|
||||
'checkalt': this._checkAlt.bind(this),
|
||||
};
|
||||
}
|
||||
|
||||
Keyboard.prototype = {
|
||||
// ===== EVENT HANDLERS =====
|
||||
|
||||
onkeyevent: function () {}, // Handler for key press/release
|
||||
this.onkeyevent = () => {}; // Handler for key press/release
|
||||
}
|
||||
|
||||
// ===== PRIVATE METHODS =====
|
||||
|
||||
_sendKeyEvent: function (keysym, code, down) {
|
||||
_sendKeyEvent(keysym, code, down) {
|
||||
if (down) {
|
||||
this._keyDownList[code] = keysym;
|
||||
} else {
|
||||
|
@ -54,9 +54,9 @@ Keyboard.prototype = {
|
|||
Log.Debug("onkeyevent " + (down ? "down" : "up") +
|
||||
", keysym: " + keysym, ", code: " + code);
|
||||
this.onkeyevent(keysym, code, down);
|
||||
},
|
||||
}
|
||||
|
||||
_getKeyCode: function (e) {
|
||||
_getKeyCode(e) {
|
||||
const code = KeyboardUtil.getKeycode(e);
|
||||
if (code !== 'Unidentified') {
|
||||
return code;
|
||||
|
@ -87,9 +87,9 @@ Keyboard.prototype = {
|
|||
}
|
||||
|
||||
return 'Unidentified';
|
||||
},
|
||||
}
|
||||
|
||||
_handleKeyDown: function (e) {
|
||||
_handleKeyDown(e) {
|
||||
const code = this._getKeyCode(e);
|
||||
let keysym = KeyboardUtil.getKeysym(e);
|
||||
|
||||
|
@ -198,10 +198,10 @@ Keyboard.prototype = {
|
|||
}
|
||||
|
||||
this._sendKeyEvent(keysym, code, true);
|
||||
},
|
||||
}
|
||||
|
||||
// Legacy event for browsers without code/key
|
||||
_handleKeyPress: function (e) {
|
||||
_handleKeyPress(e) {
|
||||
stopEvent(e);
|
||||
|
||||
// Are we expecting a keypress?
|
||||
|
@ -226,8 +226,9 @@ Keyboard.prototype = {
|
|||
}
|
||||
|
||||
this._sendKeyEvent(keysym, code, true);
|
||||
},
|
||||
_handleKeyPressTimeout: function (e) {
|
||||
}
|
||||
|
||||
_handleKeyPressTimeout(e) {
|
||||
// Did someone manage to sort out the key already?
|
||||
if (this._pendingKey === null) {
|
||||
return;
|
||||
|
@ -259,9 +260,9 @@ Keyboard.prototype = {
|
|||
}
|
||||
|
||||
this._sendKeyEvent(keysym, code, true);
|
||||
},
|
||||
}
|
||||
|
||||
_handleKeyUp: function (e) {
|
||||
_handleKeyUp(e) {
|
||||
stopEvent(e);
|
||||
|
||||
const code = this._getKeyCode(e);
|
||||
|
@ -282,24 +283,24 @@ Keyboard.prototype = {
|
|||
}
|
||||
|
||||
this._sendKeyEvent(this._keyDownList[code], code, false);
|
||||
},
|
||||
}
|
||||
|
||||
_handleAltGrTimeout: function () {
|
||||
_handleAltGrTimeout() {
|
||||
this._altGrArmed = false;
|
||||
clearTimeout(this._altGrTimeout);
|
||||
this._sendKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
|
||||
},
|
||||
}
|
||||
|
||||
_allKeysUp: function () {
|
||||
_allKeysUp() {
|
||||
Log.Debug(">> Keyboard.allKeysUp");
|
||||
for (let code in this._keyDownList) {
|
||||
this._sendKeyEvent(this._keyDownList[code], code, false);
|
||||
}
|
||||
Log.Debug("<< Keyboard.allKeysUp");
|
||||
},
|
||||
}
|
||||
|
||||
// Firefox Alt workaround, see below
|
||||
_checkAlt: function (e) {
|
||||
_checkAlt(e) {
|
||||
if (e.altKey) {
|
||||
return;
|
||||
}
|
||||
|
@ -316,11 +317,11 @@ Keyboard.prototype = {
|
|||
code: code });
|
||||
target.dispatchEvent(event);
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
// ===== PUBLIC METHODS =====
|
||||
|
||||
grab: function () {
|
||||
grab() {
|
||||
//Log.Debug(">> Keyboard.grab");
|
||||
|
||||
this._target.addEventListener('keydown', this._eventHandlers.keydown);
|
||||
|
@ -345,9 +346,9 @@ Keyboard.prototype = {
|
|||
}
|
||||
|
||||
//Log.Debug("<< Keyboard.grab");
|
||||
},
|
||||
}
|
||||
|
||||
ungrab: function () {
|
||||
ungrab() {
|
||||
//Log.Debug(">> Keyboard.ungrab");
|
||||
|
||||
if (browser.isWindows() && browser.isFirefox()) {
|
||||
|
@ -368,5 +369,5 @@ Keyboard.prototype = {
|
|||
this._allKeysUp();
|
||||
|
||||
//Log.Debug(">> Keyboard.ungrab");
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -670,7 +670,7 @@ const codepoints = {
|
|||
};
|
||||
|
||||
export default {
|
||||
lookup : function(u) {
|
||||
lookup(u) {
|
||||
// Latin-1 is one-to-one mapping
|
||||
if ((u >= 0x20) && (u <= 0xff)) {
|
||||
return u;
|
||||
|
|
|
@ -13,7 +13,8 @@ const WHEEL_STEP = 10; // Delta threshold for a mouse wheel step
|
|||
const WHEEL_STEP_TIMEOUT = 50; // ms
|
||||
const WHEEL_LINE_HEIGHT = 19;
|
||||
|
||||
export default function Mouse(target) {
|
||||
export default class Mouse {
|
||||
constructor(target) {
|
||||
this._target = target || document;
|
||||
|
||||
this._doubleClickTimer = null;
|
||||
|
@ -32,25 +33,24 @@ export default function Mouse(target) {
|
|||
'mousewheel': this._handleMouseWheel.bind(this),
|
||||
'mousedisable': this._handleMouseDisable.bind(this)
|
||||
};
|
||||
}
|
||||
|
||||
Mouse.prototype = {
|
||||
// ===== PROPERTIES =====
|
||||
|
||||
touchButton: 1, // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
|
||||
this.touchButton = 1; // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
|
||||
|
||||
// ===== EVENT HANDLERS =====
|
||||
|
||||
onmousebutton: function () {}, // Handler for mouse button click/release
|
||||
onmousemove: function () {}, // Handler for mouse movement
|
||||
this.onmousebutton = () => {}; // Handler for mouse button click/release
|
||||
this.onmousemove = () => {}; // Handler for mouse movement
|
||||
}
|
||||
|
||||
// ===== PRIVATE METHODS =====
|
||||
|
||||
_resetDoubleClickTimer: function () {
|
||||
_resetDoubleClickTimer() {
|
||||
this._doubleClickTimer = null;
|
||||
},
|
||||
}
|
||||
|
||||
_handleMouseButton: function (e, down) {
|
||||
_handleMouseButton(e, down) {
|
||||
this._updateMousePosition(e);
|
||||
let pos = this._pos;
|
||||
|
||||
|
@ -100,25 +100,25 @@ Mouse.prototype = {
|
|||
this.onmousebutton(pos.x, pos.y, down, bmask);
|
||||
|
||||
stopEvent(e);
|
||||
},
|
||||
}
|
||||
|
||||
_handleMouseDown: function (e) {
|
||||
_handleMouseDown(e) {
|
||||
// Touch events have implicit capture
|
||||
if (e.type === "mousedown") {
|
||||
setCapture(this._target);
|
||||
}
|
||||
|
||||
this._handleMouseButton(e, 1);
|
||||
},
|
||||
}
|
||||
|
||||
_handleMouseUp: function (e) {
|
||||
_handleMouseUp(e) {
|
||||
this._handleMouseButton(e, 0);
|
||||
},
|
||||
}
|
||||
|
||||
// Mouse wheel events are sent in steps over VNC. This means that the VNC
|
||||
// protocol can't handle a wheel event with specific distance or speed.
|
||||
// Therefor, if we get a lot of small mouse wheel events we combine them.
|
||||
_generateWheelStepX: function () {
|
||||
_generateWheelStepX() {
|
||||
|
||||
if (this._accumulatedWheelDeltaX < 0) {
|
||||
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 5);
|
||||
|
@ -129,9 +129,9 @@ Mouse.prototype = {
|
|||
}
|
||||
|
||||
this._accumulatedWheelDeltaX = 0;
|
||||
},
|
||||
}
|
||||
|
||||
_generateWheelStepY: function () {
|
||||
_generateWheelStepY() {
|
||||
|
||||
if (this._accumulatedWheelDeltaY < 0) {
|
||||
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 3);
|
||||
|
@ -142,16 +142,16 @@ Mouse.prototype = {
|
|||
}
|
||||
|
||||
this._accumulatedWheelDeltaY = 0;
|
||||
},
|
||||
}
|
||||
|
||||
_resetWheelStepTimers: function () {
|
||||
_resetWheelStepTimers() {
|
||||
window.clearTimeout(this._wheelStepXTimer);
|
||||
window.clearTimeout(this._wheelStepYTimer);
|
||||
this._wheelStepXTimer = null;
|
||||
this._wheelStepYTimer = null;
|
||||
},
|
||||
}
|
||||
|
||||
_handleMouseWheel: function (e) {
|
||||
_handleMouseWheel(e) {
|
||||
this._resetWheelStepTimers();
|
||||
|
||||
this._updateMousePosition(e);
|
||||
|
@ -192,15 +192,15 @@ Mouse.prototype = {
|
|||
}
|
||||
|
||||
stopEvent(e);
|
||||
},
|
||||
}
|
||||
|
||||
_handleMouseMove: function (e) {
|
||||
_handleMouseMove(e) {
|
||||
this._updateMousePosition(e);
|
||||
this.onmousemove(this._pos.x, this._pos.y);
|
||||
stopEvent(e);
|
||||
},
|
||||
}
|
||||
|
||||
_handleMouseDisable: function (e) {
|
||||
_handleMouseDisable(e) {
|
||||
/*
|
||||
* Stop propagation if inside canvas area
|
||||
* Note: This is only needed for the 'click' event as it fails
|
||||
|
@ -210,10 +210,10 @@ Mouse.prototype = {
|
|||
if (e.target == this._target) {
|
||||
stopEvent(e);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Update coordinates relative to target
|
||||
_updateMousePosition: function(e) {
|
||||
_updateMousePosition(e) {
|
||||
e = getPointerEvent(e);
|
||||
const bounds = this._target.getBoundingClientRect();
|
||||
let x;
|
||||
|
@ -234,11 +234,11 @@ Mouse.prototype = {
|
|||
y = e.clientY - bounds.top;
|
||||
}
|
||||
this._pos = {x:x, y:y};
|
||||
},
|
||||
}
|
||||
|
||||
// ===== PUBLIC METHODS =====
|
||||
|
||||
grab: function () {
|
||||
grab() {
|
||||
const c = this._target;
|
||||
|
||||
if (isTouchDevice) {
|
||||
|
@ -257,9 +257,9 @@ Mouse.prototype = {
|
|||
/* preventDefault() on mousedown doesn't stop this event for some
|
||||
reason so we have to explicitly block it */
|
||||
c.addEventListener('contextmenu', this._eventHandlers.mousedisable);
|
||||
},
|
||||
}
|
||||
|
||||
ungrab: function () {
|
||||
ungrab() {
|
||||
const c = this._target;
|
||||
|
||||
this._resetWheelStepTimers();
|
||||
|
@ -278,4 +278,4 @@ Mouse.prototype = {
|
|||
|
||||
c.removeEventListener('contextmenu', this._eventHandlers.mousedisable);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
309
core/rfb.js
309
core/rfb.js
|
@ -28,7 +28,8 @@ import "./util/polyfill.js";
|
|||
// How many seconds to wait for a disconnect to finish
|
||||
const DISCONNECT_TIMEOUT = 3;
|
||||
|
||||
export default function RFB(target, url, options) {
|
||||
export default class RFB extends EventTargetMixin {
|
||||
constructor(target, url, options) {
|
||||
if (!target) {
|
||||
throw Error("Must specify target");
|
||||
}
|
||||
|
@ -36,6 +37,8 @@ export default function RFB(target, url, options) {
|
|||
throw Error("Must specify URL");
|
||||
}
|
||||
|
||||
super();
|
||||
|
||||
this._target = target;
|
||||
this._url = url;
|
||||
|
||||
|
@ -105,6 +108,7 @@ export default function RFB(target, url, options) {
|
|||
background: null,
|
||||
zlibs: [] // TIGHT zlib streams
|
||||
};
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
this._FBU.zlibs[i] = new Inflator();
|
||||
}
|
||||
|
@ -250,16 +254,21 @@ export default function RFB(target, url, options) {
|
|||
setTimeout(this._updateConnectionState.bind(this, 'connecting'));
|
||||
|
||||
Log.Debug("<< RFB.constructor");
|
||||
}
|
||||
|
||||
RFB.prototype = {
|
||||
// ===== PROPERTIES =====
|
||||
|
||||
dragViewport: false,
|
||||
focusOnClick: true,
|
||||
this.dragViewport = false;
|
||||
this.focusOnClick = true;
|
||||
|
||||
_viewOnly: false,
|
||||
get viewOnly() { return this._viewOnly; },
|
||||
this._viewOnly = false;
|
||||
this._clipViewport = false;
|
||||
this._scaleViewport = false;
|
||||
this._resizeSession = false;
|
||||
}
|
||||
|
||||
// ===== PROPERTIES =====
|
||||
|
||||
get viewOnly() { return this._viewOnly; }
|
||||
set viewOnly(viewOnly) {
|
||||
this._viewOnly = viewOnly;
|
||||
|
||||
|
@ -273,22 +282,20 @@ RFB.prototype = {
|
|||
this._mouse.grab();
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
get capabilities() { return this._capabilities; },
|
||||
get capabilities() { return this._capabilities; }
|
||||
|
||||
get touchButton() { return this._mouse.touchButton; },
|
||||
set touchButton(button) { this._mouse.touchButton = button; },
|
||||
get touchButton() { return this._mouse.touchButton; }
|
||||
set touchButton(button) { this._mouse.touchButton = button; }
|
||||
|
||||
_clipViewport: false,
|
||||
get clipViewport() { return this._clipViewport; },
|
||||
get clipViewport() { return this._clipViewport; }
|
||||
set clipViewport(viewport) {
|
||||
this._clipViewport = viewport;
|
||||
this._updateClip();
|
||||
},
|
||||
}
|
||||
|
||||
_scaleViewport: false,
|
||||
get scaleViewport() { return this._scaleViewport; },
|
||||
get scaleViewport() { return this._scaleViewport; }
|
||||
set scaleViewport(scale) {
|
||||
this._scaleViewport = scale;
|
||||
// Scaling trumps clipping, so we may need to adjust
|
||||
|
@ -300,32 +307,31 @@ RFB.prototype = {
|
|||
if (!scale && this._clipViewport) {
|
||||
this._updateClip();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_resizeSession: false,
|
||||
get resizeSession() { return this._resizeSession; },
|
||||
get resizeSession() { return this._resizeSession; }
|
||||
set resizeSession(resize) {
|
||||
this._resizeSession = resize;
|
||||
if (resize) {
|
||||
this._requestRemoteResize();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// ===== PUBLIC METHODS =====
|
||||
|
||||
disconnect: function () {
|
||||
disconnect() {
|
||||
this._updateConnectionState('disconnecting');
|
||||
this._sock.off('error');
|
||||
this._sock.off('message');
|
||||
this._sock.off('open');
|
||||
},
|
||||
}
|
||||
|
||||
sendCredentials: function (creds) {
|
||||
sendCredentials(creds) {
|
||||
this._rfb_credentials = creds;
|
||||
setTimeout(this._init_msg.bind(this), 0);
|
||||
},
|
||||
}
|
||||
|
||||
sendCtrlAltDel: function () {
|
||||
sendCtrlAltDel() {
|
||||
if (this._rfb_connection_state !== 'connected' || this._viewOnly) { return; }
|
||||
Log.Info("Sending Ctrl-Alt-Del");
|
||||
|
||||
|
@ -335,23 +341,23 @@ RFB.prototype = {
|
|||
this.sendKey(KeyTable.XK_Delete, "Delete", false);
|
||||
this.sendKey(KeyTable.XK_Alt_L, "AltLeft", false);
|
||||
this.sendKey(KeyTable.XK_Control_L, "ControlLeft", false);
|
||||
},
|
||||
}
|
||||
|
||||
machineShutdown: function () {
|
||||
machineShutdown() {
|
||||
this._xvpOp(1, 2);
|
||||
},
|
||||
}
|
||||
|
||||
machineReboot: function () {
|
||||
machineReboot() {
|
||||
this._xvpOp(1, 3);
|
||||
},
|
||||
}
|
||||
|
||||
machineReset: function () {
|
||||
machineReset() {
|
||||
this._xvpOp(1, 4);
|
||||
},
|
||||
}
|
||||
|
||||
// Send a key press. If 'down' is not specified then send a down key
|
||||
// followed by an up key.
|
||||
sendKey: function (keysym, code, down) {
|
||||
sendKey(keysym, code, down) {
|
||||
if (this._rfb_connection_state !== 'connected' || this._viewOnly) { return; }
|
||||
|
||||
if (down === undefined) {
|
||||
|
@ -376,24 +382,24 @@ RFB.prototype = {
|
|||
Log.Info("Sending keysym (" + (down ? "down" : "up") + "): " + keysym);
|
||||
RFB.messages.keyEvent(this._sock, keysym, down ? 1 : 0);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
focus: function () {
|
||||
focus() {
|
||||
this._canvas.focus();
|
||||
},
|
||||
}
|
||||
|
||||
blur: function () {
|
||||
blur() {
|
||||
this._canvas.blur();
|
||||
},
|
||||
}
|
||||
|
||||
clipboardPasteFrom: function (text) {
|
||||
clipboardPasteFrom(text) {
|
||||
if (this._rfb_connection_state !== 'connected' || this._viewOnly) { return; }
|
||||
RFB.messages.clientCutText(this._sock, text);
|
||||
},
|
||||
}
|
||||
|
||||
// ===== PRIVATE METHODS =====
|
||||
|
||||
_connect: function () {
|
||||
_connect() {
|
||||
Log.Debug(">> RFB.connect");
|
||||
|
||||
Log.Info("connecting to " + this._url);
|
||||
|
@ -423,9 +429,9 @@ RFB.prototype = {
|
|||
this._canvas.addEventListener("touchstart", this._eventHandlers.focusCanvas);
|
||||
|
||||
Log.Debug("<< RFB.connect");
|
||||
},
|
||||
}
|
||||
|
||||
_disconnect: function () {
|
||||
_disconnect() {
|
||||
Log.Debug(">> RFB.disconnect");
|
||||
this._cursor.detach();
|
||||
this._canvas.removeEventListener("mousedown", this._eventHandlers.focusCanvas);
|
||||
|
@ -447,9 +453,9 @@ RFB.prototype = {
|
|||
}
|
||||
clearTimeout(this._resizeTimeout);
|
||||
Log.Debug("<< RFB.disconnect");
|
||||
},
|
||||
}
|
||||
|
||||
_print_stats: function () {
|
||||
_print_stats() {
|
||||
const stats = this._encStats;
|
||||
|
||||
Log.Info("Encoding stats for this connection:");
|
||||
|
@ -465,9 +471,9 @@ RFB.prototype = {
|
|||
const s = stats[key];
|
||||
Log.Info(" " + encodingName(key) + ": " + s[1] + " rects");
|
||||
});
|
||||
},
|
||||
}
|
||||
|
||||
_focusCanvas: function(event) {
|
||||
_focusCanvas(event) {
|
||||
// Respect earlier handlers' request to not do side-effects
|
||||
if (event.defaultPrevented) {
|
||||
return;
|
||||
|
@ -478,9 +484,9 @@ RFB.prototype = {
|
|||
}
|
||||
|
||||
this.focus();
|
||||
},
|
||||
}
|
||||
|
||||
_windowResize: function (event) {
|
||||
_windowResize(event) {
|
||||
// If the window resized then our screen element might have
|
||||
// as well. Update the viewport dimensions.
|
||||
window.requestAnimationFrame(function () {
|
||||
|
@ -497,11 +503,11 @@ RFB.prototype = {
|
|||
clearTimeout(this._resizeTimeout);
|
||||
this._resizeTimeout = setTimeout(this._requestRemoteResize.bind(this), 500);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Update state of clipping in Display object, and make sure the
|
||||
// configured viewport matches the current screen size
|
||||
_updateClip: function () {
|
||||
_updateClip() {
|
||||
const cur_clip = this._display.clipViewport;
|
||||
let new_clip = this._clipViewport;
|
||||
|
||||
|
@ -521,9 +527,9 @@ RFB.prototype = {
|
|||
this._display.viewportChangeSize(size.w, size.h);
|
||||
this._fixScrollbars();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_updateScale: function () {
|
||||
_updateScale() {
|
||||
if (!this._scaleViewport) {
|
||||
this._display.scale = 1.0;
|
||||
} else {
|
||||
|
@ -531,11 +537,11 @@ RFB.prototype = {
|
|||
this._display.autoscale(size.w, size.h);
|
||||
}
|
||||
this._fixScrollbars();
|
||||
},
|
||||
}
|
||||
|
||||
// Requests a change of remote desktop size. This message is an extension
|
||||
// and may only be sent if we have received an ExtendedDesktopSize message
|
||||
_requestRemoteResize: function () {
|
||||
_requestRemoteResize() {
|
||||
clearTimeout(this._resizeTimeout);
|
||||
this._resizeTimeout = null;
|
||||
|
||||
|
@ -550,15 +556,15 @@ RFB.prototype = {
|
|||
|
||||
Log.Debug('Requested new desktop size: ' +
|
||||
size.w + 'x' + size.h);
|
||||
},
|
||||
}
|
||||
|
||||
// Gets the the size of the available screen
|
||||
_screenSize: function () {
|
||||
_screenSize() {
|
||||
return { w: this._screen.offsetWidth,
|
||||
h: this._screen.offsetHeight };
|
||||
},
|
||||
}
|
||||
|
||||
_fixScrollbars: function () {
|
||||
_fixScrollbars() {
|
||||
// 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.
|
||||
|
@ -568,7 +574,7 @@ RFB.prototype = {
|
|||
// an element's dimensions
|
||||
this._screen.getBoundingClientRect();
|
||||
this._screen.style.overflow = orig;
|
||||
},
|
||||
}
|
||||
|
||||
/*
|
||||
* Connection states:
|
||||
|
@ -577,7 +583,7 @@ RFB.prototype = {
|
|||
* disconnecting
|
||||
* disconnected - permanent state
|
||||
*/
|
||||
_updateConnectionState: function (state) {
|
||||
_updateConnectionState(state) {
|
||||
const oldstate = this._rfb_connection_state;
|
||||
|
||||
if (state === oldstate) {
|
||||
|
@ -670,14 +676,14 @@ RFB.prototype = {
|
|||
{ clean: this._rfb_clean_disconnect } }));
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/* Print errors and disconnect
|
||||
*
|
||||
* The parameter 'details' is used for information that
|
||||
* should be logged but not sent to the user interface.
|
||||
*/
|
||||
_fail: function (details) {
|
||||
_fail(details) {
|
||||
switch (this._rfb_connection_state) {
|
||||
case 'disconnecting':
|
||||
Log.Error("Failed when disconnecting: " + details);
|
||||
|
@ -699,15 +705,15 @@ RFB.prototype = {
|
|||
this._updateConnectionState('disconnected');
|
||||
|
||||
return false;
|
||||
},
|
||||
}
|
||||
|
||||
_setCapability: function (cap, val) {
|
||||
_setCapability(cap, val) {
|
||||
this._capabilities[cap] = val;
|
||||
this.dispatchEvent(new CustomEvent("capabilities",
|
||||
{ detail: { capabilities: this._capabilities } }));
|
||||
},
|
||||
}
|
||||
|
||||
_handle_message: function () {
|
||||
_handle_message() {
|
||||
if (this._sock.rQlen() === 0) {
|
||||
Log.Warn("handle_message called on an empty receive queue");
|
||||
return;
|
||||
|
@ -734,13 +740,13 @@ RFB.prototype = {
|
|||
this._init_msg();
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_handleKeyEvent: function (keysym, code, down) {
|
||||
_handleKeyEvent(keysym, code, down) {
|
||||
this.sendKey(keysym, code, down);
|
||||
},
|
||||
}
|
||||
|
||||
_handleMouseButton: function (x, y, down, bmask) {
|
||||
_handleMouseButton(x, y, down, bmask) {
|
||||
if (down) {
|
||||
this._mouse_buttonMask |= bmask;
|
||||
} else {
|
||||
|
@ -778,9 +784,9 @@ RFB.prototype = {
|
|||
|
||||
if (this._rfb_connection_state !== 'connected') { return; }
|
||||
RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask);
|
||||
},
|
||||
}
|
||||
|
||||
_handleMouseMove: function (x, y) {
|
||||
_handleMouseMove(x, y) {
|
||||
if (this._viewportDragging) {
|
||||
const deltaX = this._viewportDragPos.x - x;
|
||||
const deltaY = this._viewportDragPos.y - y;
|
||||
|
@ -805,11 +811,11 @@ RFB.prototype = {
|
|||
|
||||
if (this._rfb_connection_state !== 'connected') { return; }
|
||||
RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask);
|
||||
},
|
||||
}
|
||||
|
||||
// Message Handlers
|
||||
|
||||
_negotiate_protocol_version: function () {
|
||||
_negotiate_protocol_version() {
|
||||
if (this._sock.rQlen() < 12) {
|
||||
return this._fail("Received incomplete protocol version.");
|
||||
}
|
||||
|
@ -858,9 +864,9 @@ RFB.prototype = {
|
|||
Log.Debug('Sent ProtocolVersion: ' + cversion);
|
||||
|
||||
this._rfb_init_state = 'Security';
|
||||
},
|
||||
}
|
||||
|
||||
_negotiate_security: function () {
|
||||
_negotiate_security() {
|
||||
// Polyfill since IE and PhantomJS doesn't have
|
||||
// TypedArray.includes()
|
||||
function includes(item, array) {
|
||||
|
@ -909,7 +915,7 @@ RFB.prototype = {
|
|||
Log.Debug('Authenticating using scheme: ' + this._rfb_auth_scheme);
|
||||
|
||||
return this._init_msg(); // jump to authentication
|
||||
},
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the security failure reason if sent from the server and
|
||||
|
@ -921,7 +927,7 @@ RFB.prototype = {
|
|||
* - The optional parameter security_result_status can be used to
|
||||
* add a custom status code to the event.
|
||||
*/
|
||||
_handle_security_failure: function (context, security_result_status) {
|
||||
_handle_security_failure(context, security_result_status) {
|
||||
|
||||
if (typeof context === 'undefined') {
|
||||
context = "";
|
||||
|
@ -958,10 +964,10 @@ RFB.prototype = {
|
|||
|
||||
return this._fail("Security negotiation failed" + context);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// authentication
|
||||
_negotiate_xvp_auth: function () {
|
||||
_negotiate_xvp_auth() {
|
||||
if (!this._rfb_credentials.username ||
|
||||
!this._rfb_credentials.password ||
|
||||
!this._rfb_credentials.target) {
|
||||
|
@ -978,9 +984,9 @@ RFB.prototype = {
|
|||
this._sock.send_string(xvp_auth_str);
|
||||
this._rfb_auth_scheme = 2;
|
||||
return this._negotiate_authentication();
|
||||
},
|
||||
}
|
||||
|
||||
_negotiate_std_vnc_auth: function () {
|
||||
_negotiate_std_vnc_auth() {
|
||||
if (this._sock.rQwait("auth challenge", 16)) { return false; }
|
||||
|
||||
if (!this._rfb_credentials.password) {
|
||||
|
@ -996,9 +1002,9 @@ RFB.prototype = {
|
|||
this._sock.send(response);
|
||||
this._rfb_init_state = "SecurityResult";
|
||||
return true;
|
||||
},
|
||||
}
|
||||
|
||||
_negotiate_tight_tunnels: function (numTunnels) {
|
||||
_negotiate_tight_tunnels(numTunnels) {
|
||||
const clientSupportedTunnelTypes = {
|
||||
0: { vendor: 'TGHT', signature: 'NOTUNNEL' }
|
||||
};
|
||||
|
@ -1037,9 +1043,9 @@ RFB.prototype = {
|
|||
return this._fail("Server wanted tunnels, but doesn't support " +
|
||||
"the notunnel type");
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_negotiate_tight_auth: function () {
|
||||
_negotiate_tight_auth() {
|
||||
if (!this._rfb_tightvnc) { // first pass, do the tunnel negotiation
|
||||
if (this._sock.rQwait("num tunnels", 4)) { return false; }
|
||||
const numTunnels = this._sock.rQshift32();
|
||||
|
@ -1098,9 +1104,9 @@ RFB.prototype = {
|
|||
}
|
||||
|
||||
return this._fail("No supported sub-auth types!");
|
||||
},
|
||||
}
|
||||
|
||||
_negotiate_authentication: function () {
|
||||
_negotiate_authentication() {
|
||||
switch (this._rfb_auth_scheme) {
|
||||
case 0: // connection failed
|
||||
return this._handle_security_failure("authentication scheme");
|
||||
|
@ -1126,9 +1132,9 @@ RFB.prototype = {
|
|||
return this._fail("Unsupported auth scheme (scheme: " +
|
||||
this._rfb_auth_scheme + ")");
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_handle_security_result: function () {
|
||||
_handle_security_result() {
|
||||
if (this._sock.rQwait('VNC auth response ', 4)) { return false; }
|
||||
|
||||
const status = this._sock.rQshift32();
|
||||
|
@ -1148,9 +1154,9 @@ RFB.prototype = {
|
|||
return this._fail("Security handshake failed");
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_negotiate_server_init: function () {
|
||||
_negotiate_server_init() {
|
||||
if (this._sock.rQwait("server initialization", 24)) { return false; }
|
||||
|
||||
/* Screen size */
|
||||
|
@ -1254,9 +1260,9 @@ RFB.prototype = {
|
|||
|
||||
this._updateConnectionState('connected');
|
||||
return true;
|
||||
},
|
||||
}
|
||||
|
||||
_sendEncodings: function () {
|
||||
_sendEncodings() {
|
||||
const encs = [];
|
||||
|
||||
// In preference order
|
||||
|
@ -1287,7 +1293,7 @@ RFB.prototype = {
|
|||
}
|
||||
|
||||
RFB.messages.clientEncodings(this._sock, encs);
|
||||
},
|
||||
}
|
||||
|
||||
/* RFB protocol initialization states:
|
||||
* ProtocolVersion
|
||||
|
@ -1297,7 +1303,7 @@ RFB.prototype = {
|
|||
* ClientInitialization - not triggered by server message
|
||||
* ServerInitialization
|
||||
*/
|
||||
_init_msg: function () {
|
||||
_init_msg() {
|
||||
switch (this._rfb_init_state) {
|
||||
case 'ProtocolVersion':
|
||||
return this._negotiate_protocol_version();
|
||||
|
@ -1323,15 +1329,15 @@ RFB.prototype = {
|
|||
return this._fail("Unknown init state (state: " +
|
||||
this._rfb_init_state + ")");
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_handle_set_colour_map_msg: function () {
|
||||
_handle_set_colour_map_msg() {
|
||||
Log.Debug("SetColorMapEntries");
|
||||
|
||||
return this._fail("Unexpected SetColorMapEntries message");
|
||||
},
|
||||
}
|
||||
|
||||
_handle_server_cut_text: function () {
|
||||
_handle_server_cut_text() {
|
||||
Log.Debug("ServerCutText");
|
||||
|
||||
if (this._sock.rQwait("ServerCutText header", 7, 1)) { return false; }
|
||||
|
@ -1348,9 +1354,9 @@ RFB.prototype = {
|
|||
{ detail: { text: text } }));
|
||||
|
||||
return true;
|
||||
},
|
||||
}
|
||||
|
||||
_handle_server_fence_msg: function() {
|
||||
_handle_server_fence_msg() {
|
||||
if (this._sock.rQwait("ServerFence header", 8, 1)) { return false; }
|
||||
this._sock.rQskipBytes(3); // Padding
|
||||
let flags = this._sock.rQshift32();
|
||||
|
@ -1390,9 +1396,9 @@ RFB.prototype = {
|
|||
RFB.messages.clientFence(this._sock, flags, payload);
|
||||
|
||||
return true;
|
||||
},
|
||||
}
|
||||
|
||||
_handle_xvp_msg: function () {
|
||||
_handle_xvp_msg() {
|
||||
if (this._sock.rQwait("XVP version and message", 3, 1)) { return false; }
|
||||
this._sock.rQskip8(); // Padding
|
||||
const xvp_ver = this._sock.rQshift8();
|
||||
|
@ -1413,9 +1419,9 @@ RFB.prototype = {
|
|||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
}
|
||||
|
||||
_normal_msg: function () {
|
||||
_normal_msg() {
|
||||
let msg_type;
|
||||
if (this._FBU.rects > 0) {
|
||||
msg_type = 0;
|
||||
|
@ -1471,17 +1477,17 @@ RFB.prototype = {
|
|||
Log.Debug("sock.rQslice(0, 30): " + this._sock.rQslice(0, 30));
|
||||
return true;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_onFlush: function() {
|
||||
_onFlush() {
|
||||
this._flushing = false;
|
||||
// Resume processing
|
||||
if (this._sock.rQlen() > 0) {
|
||||
this._handle_message();
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_framebufferUpdate: function () {
|
||||
_framebufferUpdate() {
|
||||
if (this._FBU.rects === 0) {
|
||||
if (this._sock.rQwait("FBU header", 3, 1)) { return false; }
|
||||
this._sock.rQskip8(); // Padding
|
||||
|
@ -1572,16 +1578,16 @@ RFB.prototype = {
|
|||
this._display.flip();
|
||||
|
||||
return true; // We finished this FBU
|
||||
},
|
||||
}
|
||||
|
||||
_updateContinuousUpdates: function() {
|
||||
_updateContinuousUpdates() {
|
||||
if (!this._enabledContinuousUpdates) { return; }
|
||||
|
||||
RFB.messages.enableContinuousUpdates(this._sock, true, 0, 0,
|
||||
this._fb_width, this._fb_height);
|
||||
},
|
||||
}
|
||||
|
||||
_resize: function(width, height) {
|
||||
_resize(width, height) {
|
||||
this._fb_width = width;
|
||||
this._fb_height = height;
|
||||
|
||||
|
@ -1595,20 +1601,26 @@ RFB.prototype = {
|
|||
|
||||
this._timing.fbu_rt_start = (new Date()).getTime();
|
||||
this._updateContinuousUpdates();
|
||||
},
|
||||
}
|
||||
|
||||
_xvpOp: function (ver, op) {
|
||||
_xvpOp(ver, op) {
|
||||
if (this._rfb_xvp_ver < ver) { return; }
|
||||
Log.Info("Sending XVP operation " + op + " (version " + ver + ")");
|
||||
RFB.messages.xvpOp(this._sock, ver, op);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Object.assign(RFB.prototype, EventTargetMixin);
|
||||
static genDES(password, challenge) {
|
||||
const passwd = [];
|
||||
for (let i = 0; i < password.length; i++) {
|
||||
passwd.push(password.charCodeAt(i));
|
||||
}
|
||||
return (new DES(passwd)).encrypt(challenge);
|
||||
}
|
||||
}
|
||||
|
||||
// Class Methods
|
||||
RFB.messages = {
|
||||
keyEvent: function (sock, keysym, down) {
|
||||
keyEvent(sock, keysym, down) {
|
||||
const buff = sock._sQ;
|
||||
const offset = sock._sQlen;
|
||||
|
||||
|
@ -1627,7 +1639,7 @@ RFB.messages = {
|
|||
sock.flush();
|
||||
},
|
||||
|
||||
QEMUExtendedKeyEvent: function (sock, keysym, down, keycode) {
|
||||
QEMUExtendedKeyEvent(sock, keysym, down, keycode) {
|
||||
function getRFBkeycode(xt_scancode) {
|
||||
const upperByte = (keycode >> 8);
|
||||
const lowerByte = (keycode & 0x00ff);
|
||||
|
@ -1662,7 +1674,7 @@ RFB.messages = {
|
|||
sock.flush();
|
||||
},
|
||||
|
||||
pointerEvent: function (sock, x, y, mask) {
|
||||
pointerEvent(sock, x, y, mask) {
|
||||
const buff = sock._sQ;
|
||||
const offset = sock._sQlen;
|
||||
|
||||
|
@ -1681,7 +1693,7 @@ RFB.messages = {
|
|||
},
|
||||
|
||||
// TODO(directxman12): make this unicode compatible?
|
||||
clientCutText: function (sock, text) {
|
||||
clientCutText(sock, text) {
|
||||
const buff = sock._sQ;
|
||||
const offset = sock._sQlen;
|
||||
|
||||
|
@ -1691,7 +1703,7 @@ RFB.messages = {
|
|||
buff[offset + 2] = 0; // padding
|
||||
buff[offset + 3] = 0; // padding
|
||||
|
||||
const length = text.length;
|
||||
let length = text.length;
|
||||
|
||||
buff[offset + 4] = length >> 24;
|
||||
buff[offset + 5] = length >> 16;
|
||||
|
@ -1707,7 +1719,7 @@ RFB.messages = {
|
|||
let remaining = length;
|
||||
while (remaining > 0) {
|
||||
|
||||
const flushSize = Math.min(remaining, (sock._sQbufferSize - sock._sQlen));
|
||||
let flushSize = Math.min(remaining, (sock._sQbufferSize - sock._sQlen));
|
||||
if (flushSize <= 0) {
|
||||
this._fail("Clipboard contents could not be sent");
|
||||
break;
|
||||
|
@ -1725,7 +1737,7 @@ RFB.messages = {
|
|||
}
|
||||
},
|
||||
|
||||
setDesktopSize: function (sock, width, height, id, flags) {
|
||||
setDesktopSize(sock, width, height, id, flags) {
|
||||
const buff = sock._sQ;
|
||||
const offset = sock._sQlen;
|
||||
|
||||
|
@ -1761,7 +1773,7 @@ RFB.messages = {
|
|||
sock.flush();
|
||||
},
|
||||
|
||||
clientFence: function (sock, flags, payload) {
|
||||
clientFence(sock, flags, payload) {
|
||||
const buff = sock._sQ;
|
||||
const offset = sock._sQlen;
|
||||
|
||||
|
@ -1788,7 +1800,7 @@ RFB.messages = {
|
|||
sock.flush();
|
||||
},
|
||||
|
||||
enableContinuousUpdates: function (sock, enable, x, y, width, height) {
|
||||
enableContinuousUpdates(sock, enable, x, y, width, height) {
|
||||
const buff = sock._sQ;
|
||||
const offset = sock._sQlen;
|
||||
|
||||
|
@ -1808,7 +1820,7 @@ RFB.messages = {
|
|||
sock.flush();
|
||||
},
|
||||
|
||||
pixelFormat: function (sock, depth, true_color) {
|
||||
pixelFormat(sock, depth, true_color) {
|
||||
const buff = sock._sQ;
|
||||
const offset = sock._sQlen;
|
||||
|
||||
|
@ -1856,7 +1868,7 @@ RFB.messages = {
|
|||
sock.flush();
|
||||
},
|
||||
|
||||
clientEncodings: function (sock, encodings) {
|
||||
clientEncodings(sock, encodings) {
|
||||
const buff = sock._sQ;
|
||||
const offset = sock._sQlen;
|
||||
|
||||
|
@ -1881,7 +1893,7 @@ RFB.messages = {
|
|||
sock.flush();
|
||||
},
|
||||
|
||||
fbUpdateRequest: function (sock, incremental, x, y, w, h) {
|
||||
fbUpdateRequest(sock, incremental, x, y, w, h) {
|
||||
const buff = sock._sQ;
|
||||
const offset = sock._sQlen;
|
||||
|
||||
|
@ -1907,7 +1919,7 @@ RFB.messages = {
|
|||
sock.flush();
|
||||
},
|
||||
|
||||
xvpOp: function (sock, ver, op) {
|
||||
xvpOp(sock, ver, op) {
|
||||
const buff = sock._sQ;
|
||||
const offset = sock._sQlen;
|
||||
|
||||
|
@ -1919,19 +1931,12 @@ RFB.messages = {
|
|||
|
||||
sock._sQlen += 4;
|
||||
sock.flush();
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
RFB.genDES = function (password, challenge) {
|
||||
const passwd = [];
|
||||
for (let i = 0; i < password.length; i++) {
|
||||
passwd.push(password.charCodeAt(i));
|
||||
}
|
||||
return (new DES(passwd)).encrypt(challenge);
|
||||
};
|
||||
|
||||
RFB.encodingHandlers = {
|
||||
RAW: function () {
|
||||
RAW() {
|
||||
if (this._FBU.lines === 0) {
|
||||
this._FBU.lines = this._FBU.height;
|
||||
}
|
||||
|
@ -1971,7 +1976,7 @@ RFB.encodingHandlers = {
|
|||
return true;
|
||||
},
|
||||
|
||||
COPYRECT: function () {
|
||||
COPYRECT() {
|
||||
this._FBU.bytes = 4;
|
||||
if (this._sock.rQwait("COPYRECT", 4)) { return false; }
|
||||
this._display.copyImage(this._sock.rQshift16(), this._sock.rQshift16(),
|
||||
|
@ -1983,7 +1988,7 @@ RFB.encodingHandlers = {
|
|||
return true;
|
||||
},
|
||||
|
||||
RRE: function () {
|
||||
RRE() {
|
||||
let color;
|
||||
if (this._FBU.subrects === 0) {
|
||||
this._FBU.bytes = 4 + 4;
|
||||
|
@ -2014,7 +2019,7 @@ RFB.encodingHandlers = {
|
|||
return true;
|
||||
},
|
||||
|
||||
HEXTILE: function () {
|
||||
HEXTILE() {
|
||||
const rQ = this._sock.get_rQ();
|
||||
let rQi = this._sock.get_rQi();
|
||||
|
||||
|
@ -2132,7 +2137,7 @@ RFB.encodingHandlers = {
|
|||
return true;
|
||||
},
|
||||
|
||||
TIGHT: function (isTightPNG) {
|
||||
TIGHT(isTightPNG) {
|
||||
this._FBU.bytes = 1; // compression-control byte
|
||||
if (this._sock.rQwait("TIGHT compression-control", this._FBU.bytes)) { return false; }
|
||||
|
||||
|
@ -2436,12 +2441,12 @@ RFB.encodingHandlers = {
|
|||
return true;
|
||||
},
|
||||
|
||||
last_rect: function () {
|
||||
last_rect() {
|
||||
this._FBU.rects = 0;
|
||||
return true;
|
||||
},
|
||||
|
||||
ExtendedDesktopSize: function () {
|
||||
ExtendedDesktopSize() {
|
||||
this._FBU.bytes = 1;
|
||||
if (this._sock.rQwait("ExtendedDesktopSize", this._FBU.bytes)) { return false; }
|
||||
|
||||
|
@ -2515,14 +2520,14 @@ RFB.encodingHandlers = {
|
|||
return true;
|
||||
},
|
||||
|
||||
DesktopSize: function () {
|
||||
DesktopSize() {
|
||||
this._resize(this._FBU.width, this._FBU.height);
|
||||
this._FBU.bytes = 0;
|
||||
this._FBU.rects -= 1;
|
||||
return true;
|
||||
},
|
||||
|
||||
Cursor: function () {
|
||||
Cursor() {
|
||||
Log.Debug(">> set_cursor");
|
||||
const x = this._FBU.x; // hotspot-x
|
||||
const y = this._FBU.y; // hotspot-y
|
||||
|
@ -2546,7 +2551,7 @@ RFB.encodingHandlers = {
|
|||
return true;
|
||||
},
|
||||
|
||||
QEMUExtendedKeyEvent: function () {
|
||||
QEMUExtendedKeyEvent() {
|
||||
this._FBU.rects--;
|
||||
|
||||
// Old Safari doesn't support creating keyboard events
|
||||
|
@ -2558,5 +2563,5 @@ RFB.encodingHandlers = {
|
|||
} catch (err) {
|
||||
// Do nothing
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@ import { supportsCursorURIs, isTouchDevice } from './browser.js';
|
|||
|
||||
const useFallback = !supportsCursorURIs() || isTouchDevice;
|
||||
|
||||
function Cursor(container) {
|
||||
export default class Cursor {
|
||||
constructor(container) {
|
||||
this._target = null;
|
||||
|
||||
this._canvas = document.createElement('canvas');
|
||||
|
@ -34,10 +35,9 @@ function Cursor(container) {
|
|||
'touchmove': this._handleTouchMove.bind(this),
|
||||
'touchend': this._handleTouchEnd.bind(this),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Cursor.prototype = {
|
||||
attach: function (target) {
|
||||
attach(target) {
|
||||
if (this._target) {
|
||||
this.detach();
|
||||
}
|
||||
|
@ -61,9 +61,9 @@ Cursor.prototype = {
|
|||
}
|
||||
|
||||
this.clear();
|
||||
},
|
||||
}
|
||||
|
||||
detach: function () {
|
||||
detach() {
|
||||
if (useFallback) {
|
||||
const options = { capture: true, passive: true };
|
||||
this._target.removeEventListener('mouseover', this._eventHandlers.mouseover, options);
|
||||
|
@ -77,9 +77,9 @@ Cursor.prototype = {
|
|||
}
|
||||
|
||||
this._target = null;
|
||||
},
|
||||
}
|
||||
|
||||
change: function (pixels, mask, hotx, hoty, w, h) {
|
||||
change(pixels, mask, hotx, hoty, w, h) {
|
||||
if ((w === 0) || (h === 0)) {
|
||||
this.clear();
|
||||
return;
|
||||
|
@ -125,9 +125,9 @@ Cursor.prototype = {
|
|||
let url = this._canvas.toDataURL();
|
||||
this._target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
clear: function () {
|
||||
clear() {
|
||||
this._target.style.cursor = 'none';
|
||||
this._canvas.width = 0;
|
||||
this._canvas.height = 0;
|
||||
|
@ -135,71 +135,71 @@ Cursor.prototype = {
|
|||
this._position.y = this._position.y + this._hotSpot.y;
|
||||
this._hotSpot.x = 0;
|
||||
this._hotSpot.y = 0;
|
||||
},
|
||||
}
|
||||
|
||||
_handleMouseOver: function (event) {
|
||||
_handleMouseOver(event) {
|
||||
// This event could be because we're entering the target, or
|
||||
// moving around amongst its sub elements. Let the move handler
|
||||
// sort things out.
|
||||
this._handleMouseMove(event);
|
||||
},
|
||||
}
|
||||
|
||||
_handleMouseLeave: function (event) {
|
||||
_handleMouseLeave(event) {
|
||||
this._hideCursor();
|
||||
},
|
||||
}
|
||||
|
||||
_handleMouseMove: function (event) {
|
||||
_handleMouseMove(event) {
|
||||
this._updateVisibility(event.target);
|
||||
|
||||
this._position.x = event.clientX - this._hotSpot.x;
|
||||
this._position.y = event.clientY - this._hotSpot.y;
|
||||
|
||||
this._updatePosition();
|
||||
},
|
||||
}
|
||||
|
||||
_handleMouseUp: function (event) {
|
||||
_handleMouseUp(event) {
|
||||
// We might get this event because of a drag operation that
|
||||
// moved outside of the target. Check what's under the cursor
|
||||
// now and adjust visibility based on that.
|
||||
let target = document.elementFromPoint(event.clientX, event.clientY);
|
||||
this._updateVisibility(target);
|
||||
},
|
||||
}
|
||||
|
||||
_handleTouchStart: function (event) {
|
||||
_handleTouchStart(event) {
|
||||
// Just as for mouseover, we let the move handler deal with it
|
||||
this._handleTouchMove(event);
|
||||
},
|
||||
}
|
||||
|
||||
_handleTouchMove: function (event) {
|
||||
_handleTouchMove(event) {
|
||||
this._updateVisibility(event.target);
|
||||
|
||||
this._position.x = event.changedTouches[0].clientX - this._hotSpot.x;
|
||||
this._position.y = event.changedTouches[0].clientY - this._hotSpot.y;
|
||||
|
||||
this._updatePosition();
|
||||
},
|
||||
}
|
||||
|
||||
_handleTouchEnd: function (event) {
|
||||
_handleTouchEnd(event) {
|
||||
// Same principle as for mouseup
|
||||
let target = document.elementFromPoint(event.changedTouches[0].clientX,
|
||||
event.changedTouches[0].clientY);
|
||||
this._updateVisibility(target);
|
||||
},
|
||||
}
|
||||
|
||||
_showCursor: function () {
|
||||
_showCursor() {
|
||||
if (this._canvas.style.visibility === 'hidden')
|
||||
this._canvas.style.visibility = '';
|
||||
},
|
||||
}
|
||||
|
||||
_hideCursor: function () {
|
||||
_hideCursor() {
|
||||
if (this._canvas.style.visibility !== 'hidden')
|
||||
this._canvas.style.visibility = 'hidden';
|
||||
},
|
||||
}
|
||||
|
||||
// Should we currently display the cursor?
|
||||
// (i.e. are we over the target, or a child of the target without a
|
||||
// different cursor set)
|
||||
_shouldShowCursor: function (target) {
|
||||
_shouldShowCursor(target) {
|
||||
// Easy case
|
||||
if (target === this._target)
|
||||
return true;
|
||||
|
@ -212,19 +212,17 @@ Cursor.prototype = {
|
|||
if (window.getComputedStyle(target).cursor !== 'none')
|
||||
return false;
|
||||
return true;
|
||||
},
|
||||
}
|
||||
|
||||
_updateVisibility: function (target) {
|
||||
_updateVisibility(target) {
|
||||
if (this._shouldShowCursor(target))
|
||||
this._showCursor();
|
||||
else
|
||||
this._hideCursor();
|
||||
},
|
||||
}
|
||||
|
||||
_updatePosition: function () {
|
||||
_updatePosition() {
|
||||
this._canvas.style.left = this._position.x + "px";
|
||||
this._canvas.style.top = this._position.y + "px";
|
||||
},
|
||||
};
|
||||
|
||||
export default Cursor;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,10 +6,12 @@
|
|||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
const EventTargetMixin = {
|
||||
_listeners: null,
|
||||
export default class EventTargetMixin {
|
||||
constructor() {
|
||||
this._listeners = null;
|
||||
}
|
||||
|
||||
addEventListener: function(type, callback) {
|
||||
addEventListener(type, callback) {
|
||||
if (!this._listeners) {
|
||||
this._listeners = new Map();
|
||||
}
|
||||
|
@ -17,16 +19,16 @@ const EventTargetMixin = {
|
|||
this._listeners.set(type, new Set());
|
||||
}
|
||||
this._listeners.get(type).add(callback);
|
||||
},
|
||||
}
|
||||
|
||||
removeEventListener: function(type, callback) {
|
||||
removeEventListener(type, callback) {
|
||||
if (!this._listeners || !this._listeners.has(type)) {
|
||||
return;
|
||||
}
|
||||
this._listeners.get(type).delete(callback);
|
||||
},
|
||||
}
|
||||
|
||||
dispatchEvent: function(event) {
|
||||
dispatchEvent(event) {
|
||||
if (!this._listeners || !this._listeners.has(event.type)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -34,7 +36,5 @@ const EventTargetMixin = {
|
|||
callback.call(this, event);
|
||||
}, this);
|
||||
return !event.defaultPrevented;
|
||||
},
|
||||
};
|
||||
|
||||
export default EventTargetMixin;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,5 @@
|
|||
* Decode from UTF-8
|
||||
*/
|
||||
export function decodeUTF8 (utf8string) {
|
||||
"use strict";
|
||||
return decodeURIComponent(escape(utf8string));
|
||||
}
|
||||
|
|
147
core/websock.js
147
core/websock.js
|
@ -14,9 +14,14 @@
|
|||
|
||||
import * as Log from './util/logging.js';
|
||||
|
||||
export default function Websock() {
|
||||
"use strict";
|
||||
// this has performance issues in some versions Chromium, and
|
||||
// doesn't gain a tremendous amount of performance increase in Firefox
|
||||
// at the moment. It may be valuable to turn it on in the future.
|
||||
const ENABLE_COPYWITHIN = false;
|
||||
const MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
|
||||
|
||||
export default class Websock {
|
||||
constructor() {
|
||||
this._websocket = null; // WebSocket object
|
||||
|
||||
this._rQi = 0; // Receive queue index
|
||||
|
@ -32,73 +37,65 @@ export default function Websock() {
|
|||
this._sQ = null; // Send queue
|
||||
|
||||
this._eventHandlers = {
|
||||
'message': function () {},
|
||||
'open': function () {},
|
||||
'close': function () {},
|
||||
'error': function () {}
|
||||
message: () => {},
|
||||
open: () => {},
|
||||
close: () => {},
|
||||
error: () => {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// this has performance issues in some versions Chromium, and
|
||||
// doesn't gain a tremendous amount of performance increase in Firefox
|
||||
// at the moment. It may be valuable to turn it on in the future.
|
||||
const ENABLE_COPYWITHIN = false;
|
||||
|
||||
const MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
|
||||
|
||||
Websock.prototype = {
|
||||
// Getters and Setters
|
||||
get_sQ: function () {
|
||||
get_sQ() {
|
||||
return this._sQ;
|
||||
},
|
||||
}
|
||||
|
||||
get_rQ: function () {
|
||||
get_rQ() {
|
||||
return this._rQ;
|
||||
},
|
||||
}
|
||||
|
||||
get_rQi: function () {
|
||||
get_rQi() {
|
||||
return this._rQi;
|
||||
},
|
||||
}
|
||||
|
||||
set_rQi: function (val) {
|
||||
set_rQi(val) {
|
||||
this._rQi = val;
|
||||
},
|
||||
}
|
||||
|
||||
// Receive Queue
|
||||
rQlen: function () {
|
||||
rQlen() {
|
||||
return this._rQlen - this._rQi;
|
||||
},
|
||||
}
|
||||
|
||||
rQpeek8: function () {
|
||||
rQpeek8() {
|
||||
return this._rQ[this._rQi];
|
||||
},
|
||||
}
|
||||
|
||||
rQshift8: function () {
|
||||
rQshift8() {
|
||||
return this._rQ[this._rQi++];
|
||||
},
|
||||
}
|
||||
|
||||
rQskip8: function () {
|
||||
rQskip8() {
|
||||
this._rQi++;
|
||||
},
|
||||
}
|
||||
|
||||
rQskipBytes: function (num) {
|
||||
rQskipBytes(num) {
|
||||
this._rQi += num;
|
||||
},
|
||||
}
|
||||
|
||||
// TODO(directxman12): test performance with these vs a DataView
|
||||
rQshift16: function () {
|
||||
rQshift16() {
|
||||
return (this._rQ[this._rQi++] << 8) +
|
||||
this._rQ[this._rQi++];
|
||||
},
|
||||
}
|
||||
|
||||
rQshift32: function () {
|
||||
rQshift32() {
|
||||
return (this._rQ[this._rQi++] << 24) +
|
||||
(this._rQ[this._rQi++] << 16) +
|
||||
(this._rQ[this._rQi++] << 8) +
|
||||
this._rQ[this._rQi++];
|
||||
},
|
||||
}
|
||||
|
||||
rQshiftStr: function (len) {
|
||||
rQshiftStr(len) {
|
||||
if (typeof(len) === 'undefined') { len = this.rQlen(); }
|
||||
let str = "";
|
||||
// Handle large arrays in steps to avoid long strings on the stack
|
||||
|
@ -107,37 +104,37 @@ Websock.prototype = {
|
|||
str += String.fromCharCode.apply(null, part);
|
||||
}
|
||||
return str;
|
||||
},
|
||||
}
|
||||
|
||||
rQshiftBytes: function (len) {
|
||||
rQshiftBytes(len) {
|
||||
if (typeof(len) === 'undefined') { len = this.rQlen(); }
|
||||
this._rQi += len;
|
||||
return new Uint8Array(this._rQ.buffer, this._rQi - len, len);
|
||||
},
|
||||
}
|
||||
|
||||
rQshiftTo: function (target, len) {
|
||||
rQshiftTo(target, len) {
|
||||
if (len === undefined) { len = this.rQlen(); }
|
||||
// TODO: make this just use set with views when using a ArrayBuffer to store the rQ
|
||||
target.set(new Uint8Array(this._rQ.buffer, this._rQi, len));
|
||||
this._rQi += len;
|
||||
},
|
||||
}
|
||||
|
||||
rQwhole: function () {
|
||||
rQwhole() {
|
||||
return new Uint8Array(this._rQ.buffer, 0, this._rQlen);
|
||||
},
|
||||
}
|
||||
|
||||
rQslice: function (start, end) {
|
||||
rQslice(start, end) {
|
||||
if (end) {
|
||||
return new Uint8Array(this._rQ.buffer, this._rQi + start, end - start);
|
||||
} else {
|
||||
return new Uint8Array(this._rQ.buffer, this._rQi + start, this._rQlen - this._rQi - start);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// Check to see if we must wait for 'num' bytes (default to FBU.bytes)
|
||||
// to be available in the receive queue. Return true if we need to
|
||||
// wait (and possibly print a debug message), otherwise false.
|
||||
rQwait: function (msg, num, goback) {
|
||||
rQwait(msg, num, goback) {
|
||||
const rQlen = this._rQlen - this._rQi; // Skip rQlen() function call
|
||||
if (rQlen < num) {
|
||||
if (goback) {
|
||||
|
@ -149,50 +146,50 @@ Websock.prototype = {
|
|||
return true; // true means need more data
|
||||
}
|
||||
return false;
|
||||
},
|
||||
}
|
||||
|
||||
// Send Queue
|
||||
|
||||
flush: function () {
|
||||
flush() {
|
||||
if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) {
|
||||
this._websocket.send(this._encode_message());
|
||||
this._sQlen = 0;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
send: function (arr) {
|
||||
send(arr) {
|
||||
this._sQ.set(arr, this._sQlen);
|
||||
this._sQlen += arr.length;
|
||||
this.flush();
|
||||
},
|
||||
}
|
||||
|
||||
send_string: function (str) {
|
||||
send_string(str) {
|
||||
this.send(str.split('').map(function (chr) {
|
||||
return chr.charCodeAt(0);
|
||||
}));
|
||||
},
|
||||
}
|
||||
|
||||
// Event Handlers
|
||||
off: function (evt) {
|
||||
off(evt) {
|
||||
this._eventHandlers[evt] = function () {};
|
||||
},
|
||||
}
|
||||
|
||||
on: function (evt, handler) {
|
||||
on(evt, handler) {
|
||||
this._eventHandlers[evt] = handler;
|
||||
},
|
||||
}
|
||||
|
||||
_allocate_buffers: function () {
|
||||
_allocate_buffers() {
|
||||
this._rQ = new Uint8Array(this._rQbufferSize);
|
||||
this._sQ = new Uint8Array(this._sQbufferSize);
|
||||
},
|
||||
}
|
||||
|
||||
init: function () {
|
||||
init() {
|
||||
this._allocate_buffers();
|
||||
this._rQi = 0;
|
||||
this._websocket = null;
|
||||
},
|
||||
}
|
||||
|
||||
open: function (uri, protocols) {
|
||||
open(uri, protocols) {
|
||||
this.init();
|
||||
|
||||
this._websocket = new WebSocket(uri, protocols);
|
||||
|
@ -218,9 +215,9 @@ Websock.prototype = {
|
|||
this._eventHandlers.error(e);
|
||||
Log.Debug("<< WebSock.onerror: " + e);
|
||||
}).bind(this);
|
||||
},
|
||||
}
|
||||
|
||||
close: function () {
|
||||
close() {
|
||||
if (this._websocket) {
|
||||
if ((this._websocket.readyState === WebSocket.OPEN) ||
|
||||
(this._websocket.readyState === WebSocket.CONNECTING)) {
|
||||
|
@ -230,16 +227,16 @@ Websock.prototype = {
|
|||
|
||||
this._websocket.onmessage = function (e) { return; };
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// private methods
|
||||
_encode_message: function () {
|
||||
_encode_message() {
|
||||
// Put in a binary arraybuffer
|
||||
// according to the spec, you can send ArrayBufferViews with the send method
|
||||
return new Uint8Array(this._sQ.buffer, 0, this._sQlen);
|
||||
},
|
||||
}
|
||||
|
||||
_expand_compact_rQ: function (min_fit) {
|
||||
_expand_compact_rQ(min_fit) {
|
||||
const resizeNeeded = min_fit || this._rQlen - this._rQi > this._rQbufferSize / 2;
|
||||
if (resizeNeeded) {
|
||||
if (!min_fit) {
|
||||
|
@ -274,9 +271,9 @@ Websock.prototype = {
|
|||
|
||||
this._rQlen = this._rQlen - this._rQi;
|
||||
this._rQi = 0;
|
||||
},
|
||||
}
|
||||
|
||||
_decode_message: function (data) {
|
||||
_decode_message(data) {
|
||||
// push arraybuffer values onto the end
|
||||
const u8 = new Uint8Array(data);
|
||||
if (u8.length > this._rQbufferSize - this._rQlen) {
|
||||
|
@ -284,9 +281,9 @@ Websock.prototype = {
|
|||
}
|
||||
this._rQ.set(u8, this._rQlen);
|
||||
this._rQlen += u8.length;
|
||||
},
|
||||
}
|
||||
|
||||
_recv_message: function (e) {
|
||||
_recv_message(e) {
|
||||
this._decode_message(e.data);
|
||||
if (this.rQlen() > 0) {
|
||||
this._eventHandlers.message();
|
||||
|
@ -301,4 +298,4 @@ Websock.prototype = {
|
|||
Log.Debug("Ignoring empty message");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -12,7 +12,8 @@ function make_event(name, props) {
|
|||
return evt;
|
||||
}
|
||||
|
||||
export default function FakeWebSocket (uri, protocols) {
|
||||
export default class FakeWebSocket {
|
||||
constructor(uri, protocols) {
|
||||
this.url = uri;
|
||||
this.binaryType = "arraybuffer";
|
||||
this.extensions = "";
|
||||
|
@ -29,17 +30,16 @@ export default function FakeWebSocket (uri, protocols) {
|
|||
this.bufferedAmount = 0;
|
||||
|
||||
this.__is_fake = true;
|
||||
}
|
||||
}
|
||||
|
||||
FakeWebSocket.prototype = {
|
||||
close: function (code, reason) {
|
||||
close(code, reason) {
|
||||
this.readyState = FakeWebSocket.CLOSED;
|
||||
if (this.onclose) {
|
||||
this.onclose(make_event("close", { 'code': code, 'reason': reason, 'wasClean': true }));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
send: function (data) {
|
||||
send(data) {
|
||||
if (this.protocol == 'base64') {
|
||||
data = Base64.decode(data);
|
||||
} else {
|
||||
|
@ -47,25 +47,25 @@ FakeWebSocket.prototype = {
|
|||
}
|
||||
this._send_queue.set(data, this.bufferedAmount);
|
||||
this.bufferedAmount += data.length;
|
||||
},
|
||||
}
|
||||
|
||||
_get_sent_data: function () {
|
||||
_get_sent_data() {
|
||||
const res = new Uint8Array(this._send_queue.buffer, 0, this.bufferedAmount);
|
||||
this.bufferedAmount = 0;
|
||||
return res;
|
||||
},
|
||||
}
|
||||
|
||||
_open: function (data) {
|
||||
_open() {
|
||||
this.readyState = FakeWebSocket.OPEN;
|
||||
if (this.onopen) {
|
||||
this.onopen(make_event('open'));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_receive_data: function (data) {
|
||||
_receive_data(data) {
|
||||
this.onmessage(make_event("message", { 'data': data }));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
FakeWebSocket.OPEN = WebSocket.OPEN;
|
||||
FakeWebSocket.CONNECTING = WebSocket.CONNECTING;
|
||||
|
|
|
@ -53,7 +53,8 @@ function enableUI() {
|
|||
encoding = VNC_frame_encoding;
|
||||
}
|
||||
|
||||
function IterationPlayer (iterations, frames, encoding) {
|
||||
class IterationPlayer {
|
||||
constructor(iterations, frames, encoding) {
|
||||
this._iterations = iterations;
|
||||
|
||||
this._iteration = undefined;
|
||||
|
@ -69,10 +70,9 @@ function IterationPlayer (iterations, frames, encoding) {
|
|||
this.onfinish = function() {};
|
||||
this.oniterationfinish = function() {};
|
||||
this.rfbdisconnected = function() {};
|
||||
}
|
||||
}
|
||||
|
||||
IterationPlayer.prototype = {
|
||||
start: function (mode) {
|
||||
start(mode) {
|
||||
this._iteration = 0;
|
||||
this._start_time = (new Date()).getTime();
|
||||
|
||||
|
@ -80,9 +80,9 @@ IterationPlayer.prototype = {
|
|||
this._trafficMgmt = !mode.endsWith('-no-mgmt');
|
||||
|
||||
this._nextIteration();
|
||||
},
|
||||
}
|
||||
|
||||
_nextIteration: function () {
|
||||
_nextIteration() {
|
||||
const player = new RecordingPlayer(this._frames, this._encoding, this._disconnected.bind(this));
|
||||
player.onfinish = this._iterationFinish.bind(this);
|
||||
|
||||
|
@ -95,9 +95,9 @@ IterationPlayer.prototype = {
|
|||
}
|
||||
|
||||
player.run(this._realtime, this._trafficMgmt);
|
||||
},
|
||||
}
|
||||
|
||||
_finish: function () {
|
||||
_finish() {
|
||||
const endTime = (new Date()).getTime();
|
||||
const totalDuration = endTime - this._start_time;
|
||||
|
||||
|
@ -105,18 +105,18 @@ IterationPlayer.prototype = {
|
|||
evt.duration = totalDuration;
|
||||
evt.iterations = this._iterations;
|
||||
this.onfinish(evt);
|
||||
},
|
||||
}
|
||||
|
||||
_iterationFinish: function (duration) {
|
||||
_iterationFinish(duration) {
|
||||
const evt = new Event('iterationfinish');
|
||||
evt.duration = duration;
|
||||
evt.number = this._iteration;
|
||||
this.oniterationfinish(evt);
|
||||
|
||||
this._nextIteration();
|
||||
},
|
||||
}
|
||||
|
||||
_disconnected: function (clean, frame) {
|
||||
_disconnected(clean, frame) {
|
||||
if (!clean) {
|
||||
this._state = 'failed';
|
||||
}
|
||||
|
@ -127,8 +127,8 @@ IterationPlayer.prototype = {
|
|||
evt.iteration = this._iteration;
|
||||
|
||||
this.onrfbdisconnected(evt);
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function start() {
|
||||
document.getElementById('startButton').value = "Running";
|
||||
|
|
|
@ -44,7 +44,8 @@ if (window.setImmediate === undefined) {
|
|||
window.addEventListener("message", _onMessage);
|
||||
}
|
||||
|
||||
export default function RecordingPlayer (frames, encoding, disconnected) {
|
||||
export default class RecordingPlayer {
|
||||
constructor(frames, encoding, disconnected) {
|
||||
this._frames = frames;
|
||||
this._encoding = encoding;
|
||||
|
||||
|
@ -71,10 +72,9 @@ export default function RecordingPlayer (frames, encoding, disconnected) {
|
|||
this._running = false;
|
||||
|
||||
this.onfinish = function () {};
|
||||
}
|
||||
}
|
||||
|
||||
RecordingPlayer.prototype = {
|
||||
run: function (realtime, trafficManagement) {
|
||||
run(realtime, trafficManagement) {
|
||||
// initialize a new RFB
|
||||
this._rfb = new RFB(document.getElementById('VNC_screen'), 'wss://test');
|
||||
this._rfb.viewOnly = true;
|
||||
|
@ -92,10 +92,10 @@ RecordingPlayer.prototype = {
|
|||
this._running = true;
|
||||
|
||||
this._queueNextPacket();
|
||||
},
|
||||
}
|
||||
|
||||
// _enablePlaybackMode mocks out things not required for running playback
|
||||
_enablePlaybackMode: function () {
|
||||
_enablePlaybackMode() {
|
||||
this._rfb._sock.send = function (arr) {};
|
||||
this._rfb._sock.close = function () {};
|
||||
this._rfb._sock.flush = function () {};
|
||||
|
@ -103,9 +103,9 @@ RecordingPlayer.prototype = {
|
|||
this.init();
|
||||
this._eventHandlers.open();
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
_queueNextPacket: function () {
|
||||
_queueNextPacket() {
|
||||
if (!this._running) { return; }
|
||||
|
||||
let frame = this._frames[this._frame_index];
|
||||
|
@ -138,9 +138,9 @@ RecordingPlayer.prototype = {
|
|||
} else {
|
||||
setImmediate(this._doPacket.bind(this));
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_doPacket: function () {
|
||||
_doPacket() {
|
||||
// Avoid having excessive queue buildup in non-realtime mode
|
||||
if (this._trafficManagement && this._rfb._flushing) {
|
||||
const player = this;
|
||||
|
@ -170,7 +170,7 @@ RecordingPlayer.prototype = {
|
|||
this._frame_index++;
|
||||
|
||||
this._queueNextPacket();
|
||||
},
|
||||
}
|
||||
|
||||
_finish() {
|
||||
if (this._rfb._display.pending()) {
|
||||
|
@ -188,10 +188,10 @@ RecordingPlayer.prototype = {
|
|||
delete this._rfb;
|
||||
this.onfinish((new Date()).getTime() - this._start_time);
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
_handleDisconnect(evt) {
|
||||
this._running = false;
|
||||
this._disconnected(evt.detail.clean, this._frame_index);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -194,7 +194,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
|
|||
|
||||
describe('#sendCtrlAlDel', function () {
|
||||
it('should sent ctrl[down]-alt[down]-del[down] then del[up]-alt[up]-ctrl[up]', function () {
|
||||
const expected = {_sQ: new Uint8Array(48), _sQlen: 0, flush: function () {}};
|
||||
const expected = {_sQ: new Uint8Array(48), _sQlen: 0, flush: () => {}};
|
||||
RFB.messages.keyEvent(expected, 0xFFE3, 1);
|
||||
RFB.messages.keyEvent(expected, 0xFFE9, 1);
|
||||
RFB.messages.keyEvent(expected, 0xFFFF, 1);
|
||||
|
@ -223,14 +223,14 @@ describe('Remote Frame Buffer Protocol Client', function() {
|
|||
|
||||
describe('#sendKey', function () {
|
||||
it('should send a single key with the given code and state (down = true)', function () {
|
||||
const expected = {_sQ: new Uint8Array(8), _sQlen: 0, flush: function () {}};
|
||||
const expected = {_sQ: new Uint8Array(8), _sQlen: 0, flush: () => {}};
|
||||
RFB.messages.keyEvent(expected, 123, 1);
|
||||
client.sendKey(123, 'Key123', true);
|
||||
expect(client._sock).to.have.sent(expected._sQ);
|
||||
});
|
||||
|
||||
it('should send both a down and up event if the state is not specified', function () {
|
||||
const expected = {_sQ: new Uint8Array(16), _sQlen: 0, flush: function () {}};
|
||||
const expected = {_sQ: new Uint8Array(16), _sQlen: 0, flush: () => {}};
|
||||
RFB.messages.keyEvent(expected, 123, 1);
|
||||
RFB.messages.keyEvent(expected, 123, 0);
|
||||
client.sendKey(123, 'Key123');
|
||||
|
@ -253,7 +253,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
|
|||
|
||||
it('should send QEMU extended events if supported', function () {
|
||||
client._qemuExtKeyEventSupported = true;
|
||||
const expected = {_sQ: new Uint8Array(12), _sQlen: 0, flush: function () {}};
|
||||
const expected = {_sQ: new Uint8Array(12), _sQlen: 0, flush: () => {}};
|
||||
RFB.messages.QEMUExtendedKeyEvent(expected, 0x20, true, 0x0039);
|
||||
client.sendKey(0x20, 'Space', true);
|
||||
expect(client._sock).to.have.sent(expected._sQ);
|
||||
|
@ -261,7 +261,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
|
|||
|
||||
it('should not send QEMU extended events if unknown key code', function () {
|
||||
client._qemuExtKeyEventSupported = true;
|
||||
const expected = {_sQ: new Uint8Array(8), _sQlen: 0, flush: function () {}};
|
||||
const expected = {_sQ: new Uint8Array(8), _sQlen: 0, flush: () => {}};
|
||||
RFB.messages.keyEvent(expected, 123, 1);
|
||||
client.sendKey(123, 'FooBar', true);
|
||||
expect(client._sock).to.have.sent(expected._sQ);
|
||||
|
@ -287,7 +287,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
|
|||
describe('#clipboardPasteFrom', function () {
|
||||
it('should send the given text in a paste event', function () {
|
||||
const expected = {_sQ: new Uint8Array(11), _sQlen: 0,
|
||||
_sQbufferSize: 11, flush: function () {}};
|
||||
_sQbufferSize: 11, flush: () => {}};
|
||||
RFB.messages.clientCutText(expected, 'abc');
|
||||
client.clipboardPasteFrom('abc');
|
||||
expect(client._sock).to.have.sent(expected._sQ);
|
||||
|
@ -1615,7 +1615,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
|
|||
}
|
||||
|
||||
it('should send an update request if there is sufficient data', function () {
|
||||
const expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: function() {}};
|
||||
const expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: () => {}};
|
||||
RFB.messages.fbUpdateRequest(expected_msg, true, 0, 0, 640, 20);
|
||||
|
||||
client._framebufferUpdate = function () { return true; };
|
||||
|
@ -1630,7 +1630,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
|
|||
});
|
||||
|
||||
it('should resume receiving an update if we previously did not have enough data', function () {
|
||||
const expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: function() {}};
|
||||
const expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: () => {}};
|
||||
RFB.messages.fbUpdateRequest(expected_msg, true, 0, 0, 640, 20);
|
||||
|
||||
// just enough to set FBU.rects
|
||||
|
@ -2040,8 +2040,8 @@ describe('Remote Frame Buffer Protocol Client', function() {
|
|||
});
|
||||
|
||||
it('should respond correctly to ServerFence', function () {
|
||||
const expected_msg = {_sQ: new Uint8Array(16), _sQlen: 0, flush: function() {}};
|
||||
const incoming_msg = {_sQ: new Uint8Array(16), _sQlen: 0, flush: function() {}};
|
||||
const expected_msg = {_sQ: new Uint8Array(16), _sQlen: 0, flush: () => {}};
|
||||
const incoming_msg = {_sQ: new Uint8Array(16), _sQlen: 0, flush: () => {}};
|
||||
|
||||
const payload = "foo\x00ab9";
|
||||
|
||||
|
@ -2065,7 +2065,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
|
|||
});
|
||||
|
||||
it('should enable continuous updates on first EndOfContinousUpdates', function () {
|
||||
const expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: function() {}};
|
||||
const expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: () => {}};
|
||||
|
||||
RFB.messages.enableContinuousUpdates(expected_msg, true, 0, 0, 640, 20);
|
||||
|
||||
|
@ -2087,7 +2087,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
|
|||
});
|
||||
|
||||
it('should update continuous updates on resize', function () {
|
||||
const expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: function() {}};
|
||||
const expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: () => {}};
|
||||
RFB.messages.enableContinuousUpdates(expected_msg, true, 0, 0, 90, 700);
|
||||
|
||||
client._resize(450, 160);
|
||||
|
@ -2131,14 +2131,14 @@ describe('Remote Frame Buffer Protocol Client', function() {
|
|||
|
||||
it('should send a pointer event on mouse button presses', function () {
|
||||
client._handleMouseButton(10, 12, 1, 0x001);
|
||||
const pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0, flush: function () {}};
|
||||
const pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0, flush: () => {}};
|
||||
RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x001);
|
||||
expect(client._sock).to.have.sent(pointer_msg._sQ);
|
||||
});
|
||||
|
||||
it('should send a mask of 1 on mousedown', function () {
|
||||
client._handleMouseButton(10, 12, 1, 0x001);
|
||||
const pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0, flush: function () {}};
|
||||
const pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0, flush: () => {}};
|
||||
RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x001);
|
||||
expect(client._sock).to.have.sent(pointer_msg._sQ);
|
||||
});
|
||||
|
@ -2146,14 +2146,14 @@ describe('Remote Frame Buffer Protocol Client', function() {
|
|||
it('should send a mask of 0 on mouseup', function () {
|
||||
client._mouse_buttonMask = 0x001;
|
||||
client._handleMouseButton(10, 12, 0, 0x001);
|
||||
const pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0, flush: function () {}};
|
||||
const pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0, flush: () => {}};
|
||||
RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x000);
|
||||
expect(client._sock).to.have.sent(pointer_msg._sQ);
|
||||
});
|
||||
|
||||
it('should send a pointer event on mouse movement', function () {
|
||||
client._handleMouseMove(10, 12);
|
||||
const pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0, flush: function () {}};
|
||||
const pointer_msg = {_sQ: new Uint8Array(6), _sQlen: 0, flush: () => {}};
|
||||
RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x000);
|
||||
expect(client._sock).to.have.sent(pointer_msg._sQ);
|
||||
});
|
||||
|
@ -2161,7 +2161,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
|
|||
it('should set the button mask so that future mouse movements use it', function () {
|
||||
client._handleMouseButton(10, 12, 1, 0x010);
|
||||
client._handleMouseMove(13, 9);
|
||||
const pointer_msg = {_sQ: new Uint8Array(12), _sQlen: 0, flush: function () {}};
|
||||
const pointer_msg = {_sQ: new Uint8Array(12), _sQlen: 0, flush: () => {}};
|
||||
RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x010);
|
||||
RFB.messages.pointerEvent(pointer_msg, 13, 9, 0x010);
|
||||
expect(client._sock).to.have.sent(pointer_msg._sQ);
|
||||
|
@ -2171,7 +2171,7 @@ describe('Remote Frame Buffer Protocol Client', function() {
|
|||
describe('Keyboard Event Handlers', function () {
|
||||
it('should send a key message on a key press', function () {
|
||||
client._handleKeyEvent(0x41, 'KeyA', true);
|
||||
const key_msg = {_sQ: new Uint8Array(8), _sQlen: 0, flush: function () {}};
|
||||
const key_msg = {_sQ: new Uint8Array(8), _sQlen: 0, flush: () => {}};
|
||||
RFB.messages.keyEvent(key_msg, 0x41, 1);
|
||||
expect(client._sock).to.have.sent(key_msg._sQ);
|
||||
});
|
||||
|
|
|
@ -117,9 +117,9 @@ describe('WebUtil', function() {
|
|||
window.chrome = {
|
||||
storage: {
|
||||
sync: {
|
||||
get: function(cb){ cb(settings); },
|
||||
set: function(){},
|
||||
remove: function() {}
|
||||
get(cb){ cb(settings); },
|
||||
set(){},
|
||||
remove() {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue