From: Juanjo Diaz Date: Thu, 5 Jul 2018 19:31:56 +0000 (+0200) Subject: Use ES6 classes X-Git-Tag: v1.1.0~61 X-Git-Url: https://git.proxmox.com/?p=mirror_novnc.git;a=commitdiff_plain;h=0e4808bf6f3165f7daa1282af445f6ea7e60de55 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: () => { ... } }` --- diff --git a/.eslintrc b/.eslintrc index aef4c0d..ea4d655 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,15 +1,17 @@ { - "env": { - "browser": true, - "es6": true - }, - "parserOptions": { - "sourceType": "module" - }, - "extends": "eslint:recommended", - "rules": { - "no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": true }], - "no-constant-condition": ["error", { "checkLoops": false }], - "no-var": "error" - } + "env": { + "browser": true, + "es6": true + }, + "parserOptions": { + "sourceType": "module" + }, + "extends": "eslint:recommended", + "rules": { + "no-unused-vars": ["error", { "vars": "all", "args": "none", "ignoreRestSiblings": true }], + "no-constant-condition": ["error", { "checkLoops": false }], + "no-var": "error", + "no-useless-constructor": "error", + "object-shorthand": ["error", "methods", { "avoidQuotes": true }], + } } diff --git a/app/localization.js b/app/localization.js index 11144f7..6bc8d55 100644 --- a/app/localization.js +++ b/app/localization.js @@ -10,17 +10,17 @@ * Localization Utilities */ -export function Localizer() { - // Currently configured language - this.language = 'en'; +export class Localizer { + constructor() { + // Currently configured language + this.language = 'en'; - // Current dictionary of translations - this.dictionary = undefined; -} + // 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); diff --git a/app/ui.js b/app/ui.js index f792d01..a8537fb 100644 --- a/app/ui.js +++ b/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; diff --git a/core/base64.js b/core/base64.js index d57d986..895aa46 100644 --- a/core/base64.js +++ b/core/base64.js @@ -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; diff --git a/core/display.js b/core/display.js index 057b12f..4955ce2 100644 --- a/core/display.js +++ b/core/display.js @@ -10,109 +10,112 @@ import * as Log from './util/logging.js'; import Base64 from "./base64.js"; -export default function Display(target) { - this._drawCtx = null; - this._c_forceCanvas = false; +let SUPPORTS_IMAGEDATA_CONSTRUCTOR = false; +try { + new ImageData(new Uint8ClampedArray(4), 1, 1); + SUPPORTS_IMAGEDATA_CONSTRUCTOR = true; +} catch (ex) { + // ignore failure +} - this._renderQ = []; // queue drawing actions for in-oder rendering - this._flushing = false; +export default class Display { + constructor(target) { + this._drawCtx = null; + this._c_forceCanvas = false; - // the full frame buffer (logical canvas) size - this._fb_width = 0; - this._fb_height = 0; + this._renderQ = []; // queue drawing actions for in-oder rendering + this._flushing = false; - this._prevDrawStyle = ""; - this._tile = null; - this._tile16x16 = null; - this._tile_x = 0; - this._tile_y = 0; + // the full frame buffer (logical canvas) size + this._fb_width = 0; + this._fb_height = 0; - Log.Debug(">> Display.constructor"); + this._prevDrawStyle = ""; + this._tile = null; + this._tile16x16 = null; + this._tile_x = 0; + this._tile_y = 0; - // The visible canvas - this._target = target; + Log.Debug(">> Display.constructor"); - if (!this._target) { - throw new Error("Target must be set"); - } + // The visible canvas + this._target = target; - if (typeof this._target === 'string') { - throw new Error('target must be a DOM element'); - } + if (!this._target) { + throw new Error("Target must be set"); + } - if (!this._target.getContext) { - throw new Error("no getContext method"); - } + if (typeof this._target === 'string') { + throw new Error('target must be a DOM element'); + } - this._targetCtx = this._target.getContext('2d'); + if (!this._target.getContext) { + throw new Error("no getContext method"); + } - // the visible canvas viewport (i.e. what actually gets seen) - this._viewportLoc = { 'x': 0, 'y': 0, 'w': this._target.width, 'h': this._target.height }; + this._targetCtx = this._target.getContext('2d'); - // The hidden canvas, where we do the actual rendering - this._backbuffer = document.createElement('canvas'); - this._drawCtx = this._backbuffer.getContext('2d'); + // the visible canvas viewport (i.e. what actually gets seen) + this._viewportLoc = { 'x': 0, 'y': 0, 'w': this._target.width, 'h': this._target.height }; - this._damageBounds = { left:0, top:0, - right: this._backbuffer.width, - bottom: this._backbuffer.height }; + // The hidden canvas, where we do the actual rendering + this._backbuffer = document.createElement('canvas'); + this._drawCtx = this._backbuffer.getContext('2d'); - Log.Debug("User Agent: " + navigator.userAgent); + this._damageBounds = { left:0, top:0, + right: this._backbuffer.width, + bottom: this._backbuffer.height }; - this.clear(); + Log.Debug("User Agent: " + navigator.userAgent); - // Check canvas features - if (!('createImageData' in this._drawCtx)) { - throw new Error("Canvas does not support createImageData"); - } + this.clear(); - this._tile16x16 = this._drawCtx.createImageData(16, 16); - Log.Debug("<< Display.constructor"); -} + // Check canvas features + if (!('createImageData' in this._drawCtx)) { + throw new Error("Canvas does not support createImageData"); + } -let SUPPORTS_IMAGEDATA_CONSTRUCTOR = false; -try { - new ImageData(new Uint8ClampedArray(4), 1, 1); - SUPPORTS_IMAGEDATA_CONSTRUCTOR = true; -} catch (ex) { - // ignore failure -} + this._tile16x16 = this._drawCtx.createImageData(16, 16); + Log.Debug("<< Display.constructor"); + + // ===== PROPERTIES ===== + + this._scale = 1.0; + this._clipViewport = false; + this.logo = null; + + // ===== EVENT HANDLERS ===== + + this.onflush = () => {}; // A flush request has finished + } -Display.prototype = { // ===== PROPERTIES ===== - _scale: 1.0, - get scale() { return this._scale; }, + 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(); } - }, -}; + } +} diff --git a/core/inflator.js b/core/inflator.js index 48fb719..0eab8fe 100644 --- a/core/inflator.js +++ b/core/inflator.js @@ -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); } diff --git a/core/input/keyboard.js b/core/input/keyboard.js index d64298a..5b46748 100644 --- a/core/input/keyboard.js +++ b/core/input/keyboard.js @@ -15,32 +15,32 @@ import * as browser from "../util/browser.js"; // Keyboard event handler // -export default function Keyboard(target) { - this._target = target || null; - - this._keyDownList = {}; // List of depressed keys - // (even if they are happy) - this._pendingKey = null; // Key waiting for keypress - this._altGrArmed = false; // Windows AltGr detection - - // keep these here so we can refer to them later - this._eventHandlers = { - 'keyup': this._handleKeyUp.bind(this), - 'keydown': this._handleKeyDown.bind(this), - 'keypress': this._handleKeyPress.bind(this), - 'blur': this._allKeysUp.bind(this), - 'checkalt': this._checkAlt.bind(this), - }; -} +export default class Keyboard { + constructor(target) { + this._target = target || null; + + this._keyDownList = {}; // List of depressed keys + // (even if they are happy) + this._pendingKey = null; // Key waiting for keypress + this._altGrArmed = false; // Windows AltGr detection + + // keep these here so we can refer to them later + this._eventHandlers = { + 'keyup': this._handleKeyUp.bind(this), + 'keydown': this._handleKeyDown.bind(this), + 'keypress': this._handleKeyPress.bind(this), + 'blur': this._allKeysUp.bind(this), + 'checkalt': this._checkAlt.bind(this), + }; -Keyboard.prototype = { - // ===== EVENT HANDLERS ===== + // ===== 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); @@ -341,13 +342,13 @@ Keyboard.prototype = { document.addEventListener(type, handler, { capture: true, passive: true }); - }); + }); } //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"); - }, -}; + } +} diff --git a/core/input/keysymdef.js b/core/input/keysymdef.js index 5969b5d..951caca 100644 --- a/core/input/keysymdef.js +++ b/core/input/keysymdef.js @@ -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; diff --git a/core/input/mouse.js b/core/input/mouse.js index 17c8cd5..4a8710e 100644 --- a/core/input/mouse.js +++ b/core/input/mouse.js @@ -13,44 +13,44 @@ 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) { - this._target = target || document; - - this._doubleClickTimer = null; - this._lastTouchPos = null; - - this._pos = null; - this._wheelStepXTimer = null; - this._wheelStepYTimer = null; - this._accumulatedWheelDeltaX = 0; - this._accumulatedWheelDeltaY = 0; - - this._eventHandlers = { - 'mousedown': this._handleMouseDown.bind(this), - 'mouseup': this._handleMouseUp.bind(this), - 'mousemove': this._handleMouseMove.bind(this), - 'mousewheel': this._handleMouseWheel.bind(this), - 'mousedisable': this._handleMouseDisable.bind(this) - }; -} +export default class Mouse { + constructor(target) { + this._target = target || document; + + this._doubleClickTimer = null; + this._lastTouchPos = null; + + this._pos = null; + this._wheelStepXTimer = null; + this._wheelStepYTimer = null; + this._accumulatedWheelDeltaX = 0; + this._accumulatedWheelDeltaY = 0; + + this._eventHandlers = { + 'mousedown': this._handleMouseDown.bind(this), + 'mouseup': this._handleMouseUp.bind(this), + 'mousemove': this._handleMouseMove.bind(this), + 'mousewheel': this._handleMouseWheel.bind(this), + 'mousedisable': this._handleMouseDisable.bind(this) + }; -Mouse.prototype = { - // ===== PROPERTIES ===== + // ===== 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 ===== + // ===== 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); } -}; +} diff --git a/core/rfb.js b/core/rfb.js index a94542f..ed765e8 100644 --- a/core/rfb.js +++ b/core/rfb.js @@ -28,238 +28,247 @@ 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) { - if (!target) { - throw Error("Must specify target"); - } - if (!url) { - throw Error("Must specify URL"); - } +export default class RFB extends EventTargetMixin { + constructor(target, url, options) { + if (!target) { + throw Error("Must specify target"); + } + if (!url) { + throw Error("Must specify URL"); + } + + super(); + + this._target = target; + this._url = url; + + // Connection details + options = options || {}; + this._rfb_credentials = options.credentials || {}; + this._shared = 'shared' in options ? !!options.shared : true; + this._repeaterID = options.repeaterID || ''; + + // Internal state + this._rfb_connection_state = ''; + this._rfb_init_state = ''; + this._rfb_auth_scheme = ''; + this._rfb_clean_disconnect = true; + + // Server capabilities + this._rfb_version = 0; + this._rfb_max_version = 3.8; + this._rfb_tightvnc = false; + this._rfb_xvp_ver = 0; + + this._fb_width = 0; + this._fb_height = 0; + + this._fb_name = ""; + + this._capabilities = { power: false }; + + this._supportsFence = false; + + this._supportsContinuousUpdates = false; + this._enabledContinuousUpdates = false; + + this._supportsSetDesktopSize = false; + this._screen_id = 0; + this._screen_flags = 0; + + this._qemuExtKeyEventSupported = false; + + // Internal objects + this._sock = null; // Websock object + this._display = null; // Display object + this._flushing = false; // Display flushing state + this._keyboard = null; // Keyboard input handler object + this._mouse = null; // Mouse input handler object + + // Timers + this._disconnTimer = null; // disconnection timer + this._resizeTimeout = null; // resize rate limiting + + // Decoder states and stats + this._encHandlers = {}; + this._encStats = {}; + + this._FBU = { + rects: 0, + subrects: 0, // RRE and HEXTILE + lines: 0, // RAW + tiles: 0, // HEXTILE + bytes: 0, + x: 0, + y: 0, + width: 0, + height: 0, + encoding: 0, + subencoding: -1, + background: null, + zlibs: [] // TIGHT zlib streams + }; - this._target = target; - this._url = url; - - // Connection details - options = options || {}; - this._rfb_credentials = options.credentials || {}; - this._shared = 'shared' in options ? !!options.shared : true; - this._repeaterID = options.repeaterID || ''; - - // Internal state - this._rfb_connection_state = ''; - this._rfb_init_state = ''; - this._rfb_auth_scheme = ''; - this._rfb_clean_disconnect = true; - - // Server capabilities - this._rfb_version = 0; - this._rfb_max_version = 3.8; - this._rfb_tightvnc = false; - this._rfb_xvp_ver = 0; - - this._fb_width = 0; - this._fb_height = 0; - - this._fb_name = ""; - - this._capabilities = { power: false }; - - this._supportsFence = false; - - this._supportsContinuousUpdates = false; - this._enabledContinuousUpdates = false; - - this._supportsSetDesktopSize = false; - this._screen_id = 0; - this._screen_flags = 0; - - this._qemuExtKeyEventSupported = false; - - // Internal objects - this._sock = null; // Websock object - this._display = null; // Display object - this._flushing = false; // Display flushing state - this._keyboard = null; // Keyboard input handler object - this._mouse = null; // Mouse input handler object - - // Timers - this._disconnTimer = null; // disconnection timer - this._resizeTimeout = null; // resize rate limiting - - // Decoder states and stats - this._encHandlers = {}; - this._encStats = {}; - - this._FBU = { - rects: 0, - subrects: 0, // RRE and HEXTILE - lines: 0, // RAW - tiles: 0, // HEXTILE - bytes: 0, - x: 0, - y: 0, - width: 0, - height: 0, - encoding: 0, - subencoding: -1, - background: null, - zlibs: [] // TIGHT zlib streams - }; - for (let i = 0; i < 4; i++) { - this._FBU.zlibs[i] = new Inflator(); - } + for (let i = 0; i < 4; i++) { + this._FBU.zlibs[i] = new Inflator(); + } + + this._destBuff = null; + this._paletteBuff = new Uint8Array(1024); // 256 * 4 (max palette size * max bytes-per-pixel) - this._destBuff = null; - this._paletteBuff = new Uint8Array(1024); // 256 * 4 (max palette size * max bytes-per-pixel) - - this._rre_chunk_sz = 100; - - this._timing = { - last_fbu: 0, - fbu_total: 0, - fbu_total_cnt: 0, - full_fbu_total: 0, - full_fbu_cnt: 0, - - fbu_rt_start: 0, - fbu_rt_total: 0, - fbu_rt_cnt: 0, - pixels: 0 - }; - - // Mouse state - this._mouse_buttonMask = 0; - this._mouse_arr = []; - this._viewportDragging = false; - this._viewportDragPos = {}; - this._viewportHasMoved = false; - - // Bound event handlers - this._eventHandlers = { - focusCanvas: this._focusCanvas.bind(this), - windowResize: this._windowResize.bind(this), - }; - - // main setup - Log.Debug(">> RFB.constructor"); - - // Create DOM elements - this._screen = document.createElement('div'); - this._screen.style.display = 'flex'; - this._screen.style.width = '100%'; - this._screen.style.height = '100%'; - this._screen.style.overflow = 'auto'; - this._screen.style.backgroundColor = 'rgb(40, 40, 40)'; - this._canvas = document.createElement('canvas'); - this._canvas.style.margin = 'auto'; - // Some browsers add an outline on focus - this._canvas.style.outline = 'none'; - // IE miscalculates width without this :( - this._canvas.style.flexShrink = '0'; - this._canvas.width = 0; - this._canvas.height = 0; - this._canvas.tabIndex = -1; - this._screen.appendChild(this._canvas); + this._rre_chunk_sz = 100; + + this._timing = { + last_fbu: 0, + fbu_total: 0, + fbu_total_cnt: 0, + full_fbu_total: 0, + full_fbu_cnt: 0, + + fbu_rt_start: 0, + fbu_rt_total: 0, + fbu_rt_cnt: 0, + pixels: 0 + }; + + // Mouse state + this._mouse_buttonMask = 0; + this._mouse_arr = []; + this._viewportDragging = false; + this._viewportDragPos = {}; + this._viewportHasMoved = false; + + // Bound event handlers + this._eventHandlers = { + focusCanvas: this._focusCanvas.bind(this), + windowResize: this._windowResize.bind(this), + }; + + // main setup + Log.Debug(">> RFB.constructor"); + + // Create DOM elements + this._screen = document.createElement('div'); + this._screen.style.display = 'flex'; + this._screen.style.width = '100%'; + this._screen.style.height = '100%'; + this._screen.style.overflow = 'auto'; + this._screen.style.backgroundColor = 'rgb(40, 40, 40)'; + this._canvas = document.createElement('canvas'); + this._canvas.style.margin = 'auto'; + // Some browsers add an outline on focus + this._canvas.style.outline = 'none'; + // IE miscalculates width without this :( + this._canvas.style.flexShrink = '0'; + this._canvas.width = 0; + this._canvas.height = 0; + this._canvas.tabIndex = -1; + this._screen.appendChild(this._canvas); this._cursor = new Cursor(); - // populate encHandlers with bound versions - this._encHandlers[encodings.encodingRaw] = RFB.encodingHandlers.RAW.bind(this); - this._encHandlers[encodings.encodingCopyRect] = RFB.encodingHandlers.COPYRECT.bind(this); - this._encHandlers[encodings.encodingRRE] = RFB.encodingHandlers.RRE.bind(this); - this._encHandlers[encodings.encodingHextile] = RFB.encodingHandlers.HEXTILE.bind(this); - this._encHandlers[encodings.encodingTight] = RFB.encodingHandlers.TIGHT.bind(this, false); - this._encHandlers[encodings.encodingTightPNG] = RFB.encodingHandlers.TIGHT.bind(this, true); - - this._encHandlers[encodings.pseudoEncodingDesktopSize] = RFB.encodingHandlers.DesktopSize.bind(this); - this._encHandlers[encodings.pseudoEncodingLastRect] = RFB.encodingHandlers.last_rect.bind(this); - this._encHandlers[encodings.pseudoEncodingCursor] = RFB.encodingHandlers.Cursor.bind(this); - this._encHandlers[encodings.pseudoEncodingQEMUExtendedKeyEvent] = RFB.encodingHandlers.QEMUExtendedKeyEvent.bind(this); - this._encHandlers[encodings.pseudoEncodingExtendedDesktopSize] = RFB.encodingHandlers.ExtendedDesktopSize.bind(this); - - // NB: nothing that needs explicit teardown should be done - // before this point, since this can throw an exception - try { - this._display = new Display(this._canvas); - } catch (exc) { - Log.Error("Display exception: " + exc); - throw exc; - } - this._display.onflush = this._onFlush.bind(this); - this._display.clear(); - - this._keyboard = new Keyboard(this._canvas); - this._keyboard.onkeyevent = this._handleKeyEvent.bind(this); - - this._mouse = new Mouse(this._canvas); - this._mouse.onmousebutton = this._handleMouseButton.bind(this); - this._mouse.onmousemove = this._handleMouseMove.bind(this); - - this._sock = new Websock(); - this._sock.on('message', this._handle_message.bind(this)); - this._sock.on('open', function () { - if ((this._rfb_connection_state === 'connecting') && - (this._rfb_init_state === '')) { - this._rfb_init_state = 'ProtocolVersion'; - Log.Debug("Starting VNC handshake"); - } else { - this._fail("Unexpected server connection while " + - this._rfb_connection_state); - } - }.bind(this)); - this._sock.on('close', function (e) { - Log.Debug("WebSocket on-close event"); - let msg = ""; - if (e.code) { - msg = "(code: " + e.code; - if (e.reason) { - msg += ", reason: " + e.reason; + // populate encHandlers with bound versions + this._encHandlers[encodings.encodingRaw] = RFB.encodingHandlers.RAW.bind(this); + this._encHandlers[encodings.encodingCopyRect] = RFB.encodingHandlers.COPYRECT.bind(this); + this._encHandlers[encodings.encodingRRE] = RFB.encodingHandlers.RRE.bind(this); + this._encHandlers[encodings.encodingHextile] = RFB.encodingHandlers.HEXTILE.bind(this); + this._encHandlers[encodings.encodingTight] = RFB.encodingHandlers.TIGHT.bind(this, false); + this._encHandlers[encodings.encodingTightPNG] = RFB.encodingHandlers.TIGHT.bind(this, true); + + this._encHandlers[encodings.pseudoEncodingDesktopSize] = RFB.encodingHandlers.DesktopSize.bind(this); + this._encHandlers[encodings.pseudoEncodingLastRect] = RFB.encodingHandlers.last_rect.bind(this); + this._encHandlers[encodings.pseudoEncodingCursor] = RFB.encodingHandlers.Cursor.bind(this); + this._encHandlers[encodings.pseudoEncodingQEMUExtendedKeyEvent] = RFB.encodingHandlers.QEMUExtendedKeyEvent.bind(this); + this._encHandlers[encodings.pseudoEncodingExtendedDesktopSize] = RFB.encodingHandlers.ExtendedDesktopSize.bind(this); + + // NB: nothing that needs explicit teardown should be done + // before this point, since this can throw an exception + try { + this._display = new Display(this._canvas); + } catch (exc) { + Log.Error("Display exception: " + exc); + throw exc; + } + this._display.onflush = this._onFlush.bind(this); + this._display.clear(); + + this._keyboard = new Keyboard(this._canvas); + this._keyboard.onkeyevent = this._handleKeyEvent.bind(this); + + this._mouse = new Mouse(this._canvas); + this._mouse.onmousebutton = this._handleMouseButton.bind(this); + this._mouse.onmousemove = this._handleMouseMove.bind(this); + + this._sock = new Websock(); + this._sock.on('message', this._handle_message.bind(this)); + this._sock.on('open', function () { + if ((this._rfb_connection_state === 'connecting') && + (this._rfb_init_state === '')) { + this._rfb_init_state = 'ProtocolVersion'; + Log.Debug("Starting VNC handshake"); + } else { + this._fail("Unexpected server connection while " + + this._rfb_connection_state); } - msg += ")"; - } - switch (this._rfb_connection_state) { - case 'connecting': - this._fail("Connection closed " + msg); - break; - case 'connected': - // Handle disconnects that were initiated server-side - this._updateConnectionState('disconnecting'); - this._updateConnectionState('disconnected'); - break; - case 'disconnecting': - // Normal disconnection path - this._updateConnectionState('disconnected'); - break; - case 'disconnected': - this._fail("Unexpected server disconnect " + - "when already disconnected " + msg); - break; - default: - this._fail("Unexpected server disconnect before connecting " + - msg); - break; - } - this._sock.off('close'); - }.bind(this)); - this._sock.on('error', function (e) { - Log.Warn("WebSocket on-error event"); - }); + }.bind(this)); + this._sock.on('close', function (e) { + Log.Debug("WebSocket on-close event"); + let msg = ""; + if (e.code) { + msg = "(code: " + e.code; + if (e.reason) { + msg += ", reason: " + e.reason; + } + msg += ")"; + } + switch (this._rfb_connection_state) { + case 'connecting': + this._fail("Connection closed " + msg); + break; + case 'connected': + // Handle disconnects that were initiated server-side + this._updateConnectionState('disconnecting'); + this._updateConnectionState('disconnected'); + break; + case 'disconnecting': + // Normal disconnection path + this._updateConnectionState('disconnected'); + break; + case 'disconnected': + this._fail("Unexpected server disconnect " + + "when already disconnected " + msg); + break; + default: + this._fail("Unexpected server disconnect before connecting " + + msg); + break; + } + this._sock.off('close'); + }.bind(this)); + this._sock.on('error', function (e) { + Log.Warn("WebSocket on-error event"); + }); - // Slight delay of the actual connection so that the caller has - // time to set up callbacks - setTimeout(this._updateConnectionState.bind(this, 'connecting')); + // Slight delay of the actual connection so that the caller has + // time to set up callbacks + setTimeout(this._updateConnectionState.bind(this, 'connecting')); - Log.Debug("<< RFB.constructor"); -} + Log.Debug("<< RFB.constructor"); -RFB.prototype = { - // ===== PROPERTIES ===== + // ===== 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 } })); - }, + { 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,17 +964,17 @@ 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) { this.dispatchEvent(new CustomEvent( "credentialsrequired", { detail: { types: ["username", "password", "target"] } })); - return false; + return false; } const xvp_auth_str = String.fromCharCode(this._rfb_credentials.username.length) + @@ -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 } - }, + } } diff --git a/core/util/cursor.js b/core/util/cursor.js index da72723..18aa7be 100644 --- a/core/util/cursor.js +++ b/core/util/cursor.js @@ -8,36 +8,36 @@ import { supportsCursorURIs, isTouchDevice } from './browser.js'; const useFallback = !supportsCursorURIs() || isTouchDevice; -function Cursor(container) { - this._target = null; - - this._canvas = document.createElement('canvas'); - - if (useFallback) { - this._canvas.style.position = 'fixed'; - this._canvas.style.zIndex = '65535'; - this._canvas.style.pointerEvents = 'none'; - // Can't use "display" because of Firefox bug #1445997 - this._canvas.style.visibility = 'hidden'; - document.body.appendChild(this._canvas); - } - - this._position = { x: 0, y: 0 }; - this._hotSpot = { x: 0, y: 0 }; - - this._eventHandlers = { - 'mouseover': this._handleMouseOver.bind(this), - 'mouseleave': this._handleMouseLeave.bind(this), - 'mousemove': this._handleMouseMove.bind(this), - 'mouseup': this._handleMouseUp.bind(this), - 'touchstart': this._handleTouchStart.bind(this), - 'touchmove': this._handleTouchMove.bind(this), - 'touchend': this._handleTouchEnd.bind(this), - }; -} +export default class Cursor { + constructor(container) { + this._target = null; + + this._canvas = document.createElement('canvas'); + + if (useFallback) { + this._canvas.style.position = 'fixed'; + this._canvas.style.zIndex = '65535'; + this._canvas.style.pointerEvents = 'none'; + // Can't use "display" because of Firefox bug #1445997 + this._canvas.style.visibility = 'hidden'; + document.body.appendChild(this._canvas); + } -Cursor.prototype = { - attach: function (target) { + this._position = { x: 0, y: 0 }; + this._hotSpot = { x: 0, y: 0 }; + + this._eventHandlers = { + 'mouseover': this._handleMouseOver.bind(this), + 'mouseleave': this._handleMouseLeave.bind(this), + 'mousemove': this._handleMouseMove.bind(this), + 'mouseup': this._handleMouseUp.bind(this), + 'touchstart': this._handleTouchStart.bind(this), + 'touchmove': this._handleTouchMove.bind(this), + 'touchend': this._handleTouchEnd.bind(this), + }; + } + + 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; + } +} diff --git a/core/util/eventtarget.js b/core/util/eventtarget.js index 2472319..7e56cd5 100644 --- a/core/util/eventtarget.js +++ b/core/util/eventtarget.js @@ -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; + } +} diff --git a/core/util/strings.js b/core/util/strings.js index d04ac00..b3de547 100644 --- a/core/util/strings.js +++ b/core/util/strings.js @@ -10,6 +10,5 @@ * Decode from UTF-8 */ export function decodeUTF8 (utf8string) { - "use strict"; return decodeURIComponent(escape(utf8string)); } diff --git a/core/websock.js b/core/websock.js index fc77dd0..ce27c5b 100644 --- a/core/websock.js +++ b/core/websock.js @@ -14,91 +14,88 @@ import * as Log from './util/logging.js'; -export default function Websock() { - "use strict"; - - this._websocket = null; // WebSocket object - - this._rQi = 0; // Receive queue index - this._rQlen = 0; // Next write position in the receive queue - this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB) - this._rQmax = this._rQbufferSize / 8; - // called in init: this._rQ = new Uint8Array(this._rQbufferSize); - this._rQ = null; // Receive queue - - this._sQbufferSize = 1024 * 10; // 10 KiB - // called in init: this._sQ = new Uint8Array(this._sQbufferSize); - this._sQlen = 0; - this._sQ = null; // Send queue - - this._eventHandlers = { - 'message': function () {}, - 'open': function () {}, - 'close': function () {}, - 'error': function () {} - }; -} - // 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 = { +export default class Websock { + constructor() { + this._websocket = null; // WebSocket object + + this._rQi = 0; // Receive queue index + this._rQlen = 0; // Next write position in the receive queue + this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB) + this._rQmax = this._rQbufferSize / 8; + // called in init: this._rQ = new Uint8Array(this._rQbufferSize); + this._rQ = null; // Receive queue + + this._sQbufferSize = 1024 * 10; // 10 KiB + // called in init: this._sQ = new Uint8Array(this._sQbufferSize); + this._sQlen = 0; + this._sQ = null; // Send queue + + this._eventHandlers = { + message: () => {}, + open: () => {}, + close: () => {}, + error: () => {} + }; + } + // 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"); } } -}; +} diff --git a/tests/.eslintrc b/tests/.eslintrc index 800dcf9..b52ebaf 100644 --- a/tests/.eslintrc +++ b/tests/.eslintrc @@ -1,9 +1,9 @@ { - "env": { - "node": true, - "mocha": true - }, - "globals": { - "chai": true - } + "env": { + "node": true, + "mocha": true + }, + "globals": { + "chai": true + } } \ No newline at end of file diff --git a/tests/fake.websocket.js b/tests/fake.websocket.js index 2e28494..e0290ed 100644 --- a/tests/fake.websocket.js +++ b/tests/fake.websocket.js @@ -12,34 +12,34 @@ function make_event(name, props) { return evt; } -export default function FakeWebSocket (uri, protocols) { - this.url = uri; - this.binaryType = "arraybuffer"; - this.extensions = ""; +export default class FakeWebSocket { + constructor(uri, protocols) { + this.url = uri; + this.binaryType = "arraybuffer"; + this.extensions = ""; - if (!protocols || typeof protocols === 'string') { - this.protocol = protocols; - } else { - this.protocol = protocols[0]; - } + if (!protocols || typeof protocols === 'string') { + this.protocol = protocols; + } else { + this.protocol = protocols[0]; + } - this._send_queue = new Uint8Array(20000); + this._send_queue = new Uint8Array(20000); - this.readyState = FakeWebSocket.CONNECTING; - this.bufferedAmount = 0; + this.readyState = FakeWebSocket.CONNECTING; + this.bufferedAmount = 0; - this.__is_fake = true; -} + 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; diff --git a/tests/playback-ui.js b/tests/playback-ui.js index 1565b8f..d2d0869 100644 --- a/tests/playback-ui.js +++ b/tests/playback-ui.js @@ -53,26 +53,26 @@ function enableUI() { encoding = VNC_frame_encoding; } -function IterationPlayer (iterations, frames, encoding) { - this._iterations = iterations; +class IterationPlayer { + constructor(iterations, frames, encoding) { + this._iterations = iterations; - this._iteration = undefined; - this._player = undefined; + this._iteration = undefined; + this._player = undefined; - this._start_time = undefined; + this._start_time = undefined; - this._frames = frames; - this._encoding = encoding; + this._frames = frames; + this._encoding = encoding; - this._state = 'running'; + this._state = 'running'; 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"; diff --git a/tests/playback.js b/tests/playback.js index cbeb20f..6b422d8 100644 --- a/tests/playback.js +++ b/tests/playback.js @@ -44,37 +44,37 @@ if (window.setImmediate === undefined) { window.addEventListener("message", _onMessage); } -export default function RecordingPlayer (frames, encoding, disconnected) { - this._frames = frames; - this._encoding = encoding; +export default class RecordingPlayer { + constructor(frames, encoding, disconnected) { + this._frames = frames; + this._encoding = encoding; - this._disconnected = disconnected; + this._disconnected = disconnected; - if (this._encoding === undefined) { + if (this._encoding === undefined) { const frame = this._frames[0]; const start = frame.indexOf('{', 1) + 1; - if (frame.slice(start).startsWith('UkZC')) { - this._encoding = 'base64'; - } else { - this._encoding = 'binary'; + if (frame.slice(start).startsWith('UkZC')) { + this._encoding = 'base64'; + } else { + this._encoding = 'binary'; + } } - } - this._rfb = undefined; - this._frame_length = this._frames.length; + this._rfb = undefined; + this._frame_length = this._frames.length; - this._frame_index = 0; - this._start_time = undefined; - this._realtime = true; - this._trafficManagement = true; + this._frame_index = 0; + this._start_time = undefined; + this._realtime = true; + this._trafficManagement = true; - this._running = false; + 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); } -}; +} diff --git a/tests/test.rfb.js b/tests/test.rfb.js index 0ebad9c..cb72f4c 100644 --- a/tests/test.rfb.js +++ b/tests/test.rfb.js @@ -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); }); diff --git a/tests/test.webutil.js b/tests/test.webutil.js index 90c4bd3..ae28974 100644 --- a/tests/test.webutil.js +++ b/tests/test.webutil.js @@ -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() {} } } };