]> git.proxmox.com Git - mirror_novnc.git/blobdiff - app/ui.js
Enforce object key spacing
[mirror_novnc.git] / app / ui.js
index 559a80b72f262318480d00065d4d5900c15a4a21..64a35369dc1ae1ba1dbd726e4f65186feb46ccb7 100644 (file)
--- a/app/ui.js
+++ b/app/ui.js
@@ -1,7 +1,7 @@
 /*
  * noVNC: HTML5 VNC client
  * Copyright (C) 2012 Joel Martin
- * Copyright (C) 2016 Samuel Mannehed for Cendio AB
+ * Copyright (C) 2018 Samuel Mannehed for Cendio AB
  * Copyright (C) 2016 Pierre Ossman for Cendio AB
  * Licensed under MPL 2.0 (see LICENSE.txt)
  *
 
 import * as Log from '../core/util/logging.js';
 import _, { l10n } from './localization.js';
-import { isTouchDevice } from '../core/util/browser.js';
+import { isTouchDevice, dragThreshold } from '../core/util/browser.js';
 import { setCapture, getPointerEvent } from '../core/util/events.js';
 import KeyTable from "../core/input/keysym.js";
 import keysyms from "../core/input/keysymdef.js";
 import Keyboard from "../core/input/keyboard.js";
 import RFB from "../core/rfb.js";
-import Display from "../core/display.js";
 import * as WebUtil from "./webutil.js";
 
-var UI = {
+const UI = {
 
     connected: false,
     desktopName: "",
@@ -42,7 +41,7 @@ var UI = {
     reconnect_callback: null,
     reconnect_password: null,
 
-    prime: function(callback) {
+    prime(callback) {
         if (document.readyState === "interactive" || document.readyState === "complete") {
             UI.load(callback);
         } else {
@@ -52,12 +51,12 @@ var 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 &&
@@ -72,7 +71,7 @@ var UI = {
         if (isTouchDevice) {
             document.documentElement.classList.add("noVNC_touch");
             // Remove the address bar
-            setTimeout(function() { window.scrollTo(0, 1); }, 100);
+            setTimeout(() => window.scrollTo(0, 1), 100);
         }
 
         // Restore control bar position
@@ -102,7 +101,7 @@ var UI = {
 
         document.documentElement.classList.remove("noVNC_loading");
 
-        var autoconnect = WebUtil.getConfigVar('autoconnect', false);
+        let autoconnect = WebUtil.getConfigVar('autoconnect', false);
         if (autoconnect === 'true' || autoconnect == '1') {
             autoconnect = true;
             UI.connect();
@@ -117,7 +116,7 @@ var 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 &&
@@ -131,13 +130,11 @@ var UI = {
         }
     },
 
-    initSettings: function() {
-        var i;
-
+    initSettings() {
         // Logging selection dropdown
-        var llevels = ['error', 'warn', 'info', 'debug'];
-        for (i = 0; i < llevels.length; i += 1) {
-            UI.addOption(document.getElementById('noVNC_setting_logging'),llevels[i], llevels[i]);
+        const llevels = ['error', 'warn', 'info', 'debug'];
+        for (let i = 0; i < llevels.length; i += 1) {
+            UI.addOption(document.getElementById('noVNC_setting_logging'), llevels[i], llevels[i]);
         }
 
         // Settings with immediate effects
@@ -146,12 +143,11 @@ var UI = {
 
         // if port == 80 (or 443) then it won't be present and should be
         // set manually
-        var port = window.location.port;
+        let port = window.location.port;
         if (!port) {
-            if (window.location.protocol.substring(0,5) == 'https') {
+            if (window.location.protocol.substring(0, 5) == 'https') {
                 port = 443;
-            }
-            else if (window.location.protocol.substring(0,4) == 'http') {
+            } else if (window.location.protocol.substring(0, 4) == 'http') {
                 port = 80;
             }
         }
@@ -172,17 +168,17 @@ var UI = {
         UI.setupSettingLabels();
     },
     // Adds a link to the label elements on the corresponding input elements
-    setupSettingLabels: function() {
-        var labels = document.getElementsByTagName('LABEL');
-        for (var i = 0; i < labels.length; i++) {
-            var htmlFor = labels[i].htmlFor;
+    setupSettingLabels() {
+        const labels = document.getElementsByTagName('LABEL');
+        for (let i = 0; i < labels.length; i++) {
+            const htmlFor = labels[i].htmlFor;
             if (htmlFor != '') {
-                var elem = document.getElementById(htmlFor);
+                const elem = document.getElementById(htmlFor);
                 if (elem) elem.label = labels[i];
             } else {
                 // If 'for' isn't set, use the first input element child
-                var children = labels[i].children;
-                for (var j = 0; j < children.length; j++) {
+                const children = labels[i].children;
+                for (let j = 0; j < children.length; j++) {
                     if (children[j].form !== undefined) {
                         children[j].label = labels[i];
                         break;
@@ -198,7 +194,7 @@ var UI = {
 * EVENT HANDLERS
 * ------v------*/
 
-    addControlbarHandlers: function() {
+    addControlbarHandlers() {
         document.getElementById("noVNC_control_bar")
             .addEventListener('mousemove', UI.activateControlbar);
         document.getElementById("noVNC_control_bar")
@@ -225,21 +221,21 @@ var UI = {
         // resize events aren't available for elements
         window.addEventListener('resize', UI.updateControlbarHandle);
 
-        var exps = document.getElementsByClassName("noVNC_expander");
-        for (var i = 0;i < exps.length;i++) {
+        const exps = document.getElementsByClassName("noVNC_expander");
+        for (let i = 0;i < exps.length;i++) {
             exps[i].addEventListener('click', UI.toggleExpander);
         }
     },
 
-    addTouchSpecificHandlers: function() {
+    addTouchSpecificHandlers() {
         document.getElementById("noVNC_mouse_button0")
-            .addEventListener('click', function () { UI.setMouseButton(1); });
+            .addEventListener('click', () => UI.setMouseButton(1));
         document.getElementById("noVNC_mouse_button1")
-            .addEventListener('click', function () { UI.setMouseButton(2); });
+            .addEventListener('click', () => UI.setMouseButton(2));
         document.getElementById("noVNC_mouse_button2")
-            .addEventListener('click', function () { UI.setMouseButton(4); });
+            .addEventListener('click', () => UI.setMouseButton(4));
         document.getElementById("noVNC_mouse_button4")
-            .addEventListener('click', function () { UI.setMouseButton(0); });
+            .addEventListener('click', () => UI.setMouseButton(0));
         document.getElementById("noVNC_keyboard_button")
             .addEventListener('click', UI.toggleVirtualKeyboard);
 
@@ -253,7 +249,7 @@ var UI = {
         document.getElementById("noVNC_keyboardinput")
             .addEventListener('blur', UI.onblurVirtualKeyboard);
         document.getElementById("noVNC_keyboardinput")
-            .addEventListener('submit', function () { return false; });
+            .addEventListener('submit', () => false);
 
         document.documentElement
             .addEventListener('mousedown', UI.keepVirtualKeyboard, true);
@@ -280,7 +276,7 @@ var 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")
@@ -295,18 +291,18 @@ var UI = {
             .addEventListener('click', UI.sendCtrlAltDel);
     },
 
-    addMachineHandlers: function() {
+    addMachineHandlers() {
         document.getElementById("noVNC_shutdown_button")
-            .addEventListener('click', function() { UI.rfb.machineShutdown(); });
+            .addEventListener('click', () => UI.rfb.machineShutdown());
         document.getElementById("noVNC_reboot_button")
-            .addEventListener('click', function() { UI.rfb.machineReboot(); });
+            .addEventListener('click', () => UI.rfb.machineReboot());
         document.getElementById("noVNC_reset_button")
-            .addEventListener('click', function() { UI.rfb.machineReset(); });
+            .addEventListener('click', () => UI.rfb.machineReset());
         document.getElementById("noVNC_power_button")
             .addEventListener('click', UI.togglePowerPanel);
     },
 
-    addConnectionControlHandlers: function() {
+    addConnectionControlHandlers() {
         document.getElementById("noVNC_disconnect_button")
             .addEventListener('click', UI.disconnect);
         document.getElementById("noVNC_connect_button")
@@ -318,7 +314,7 @@ var UI = {
             .addEventListener('click', UI.setPassword);
     },
 
-    addClipboardHandlers: function() {
+    addClipboardHandlers() {
         document.getElementById("noVNC_clipboard_button")
             .addEventListener('click', UI.toggleClipboardPanel);
         document.getElementById("noVNC_clipboard_text")
@@ -329,22 +325,22 @@ var UI = {
 
     // Add a call to save settings when the element changes,
     // unless the optional parameter changeFunc is used instead.
-    addSettingChangeHandler: function(name, changeFunc) {
-        var settingElem = document.getElementById("noVNC_setting_" + name);
+    addSettingChangeHandler(name, changeFunc) {
+        const settingElem = document.getElementById("noVNC_setting_" + name);
         if (changeFunc === undefined) {
-            changeFunc = function () { UI.saveSetting(name); };
+            changeFunc = () => UI.saveSetting(name);
         }
         settingElem.addEventListener('change', changeFunc);
     },
 
-    addSettingsHandlers: function() {
+    addSettingsHandlers() {
         document.getElementById("noVNC_settings_button")
             .addEventListener('click', UI.toggleSettingsPanel);
 
         UI.addSettingChangeHandler('encrypt');
         UI.addSettingChangeHandler('resize');
-        UI.addSettingChangeHandler('resize', UI.enableDisableViewClip);
         UI.addSettingChangeHandler('resize', UI.applyResizeMode);
+        UI.addSettingChangeHandler('resize', UI.updateViewClip);
         UI.addSettingChangeHandler('view_clip');
         UI.addSettingChangeHandler('view_clip', UI.updateViewClip);
         UI.addSettingChangeHandler('shared');
@@ -360,7 +356,7 @@ var UI = {
         UI.addSettingChangeHandler('reconnect_delay');
     },
 
-    addFullscreenHandlers: function() {
+    addFullscreenHandlers() {
         document.getElementById("noVNC_fullscreen_button")
             .addEventListener('click', UI.toggleFullscreen);
 
@@ -377,14 +373,14 @@ var 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");
         document.documentElement.classList.remove("noVNC_disconnecting");
         document.documentElement.classList.remove("noVNC_reconnecting");
 
-        let transition_elem = document.getElementById("noVNC_transition_text");
+        const transition_elem = document.getElementById("noVNC_transition_text");
         switch (state) {
             case 'init':
                 break;
@@ -411,9 +407,9 @@ var UI = {
                 return;
         }
 
-        UI.enableDisableViewClip();
-
         if (UI.connected) {
+            UI.updateViewClip();
+
             UI.disableSetting('encrypt');
             UI.disableSetting('shared');
             UI.disableSetting('host');
@@ -435,17 +431,13 @@ var UI = {
             UI.keepControlbar();
         }
 
-        // State change disables viewport dragging.
-        // It is enabled (toggled) by direct click on the button
-        UI.setViewDrag(false);
-
-        // State change also closes the password dialog
+        // State change closes the password dialog
         document.getElementById('noVNC_password_dlg')
             .classList.remove('noVNC_open');
     },
 
-    showStatus: function(text, status_type, time) {
-        var statusElem = document.getElementById('noVNC_status');
+    showStatus(text, status_type, time) {
+        const statusElem = document.getElementById('noVNC_status');
 
         clearTimeout(UI.statusTimeout);
 
@@ -505,12 +497,12 @@ var 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
@@ -519,27 +511,27 @@ var 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();
@@ -548,13 +540,17 @@ var UI = {
         }
     },
 
-    toggleControlbarSide: function () {
-        // Temporarily disable animation to avoid weird movement
-        var bar = document.getElementById('noVNC_control_bar');
-        bar.style.transitionDuration = '0s';
-        bar.addEventListener('transitionend', function () { this.style.transitionDuration = ""; });
+    toggleControlbarSide() {
+        // Temporarily disable animation, if bar is displayed, to avoid weird
+        // movement. The transitionend-event will not fire when display=none.
+        const bar = document.getElementById('noVNC_control_bar');
+        const barDisplayStyle = window.getComputedStyle(bar).display;
+        if (barDisplayStyle !== 'none') {
+            bar.style.transitionDuration = '0s';
+            bar.addEventListener('transitionend', () => bar.style.transitionDuration = '');
+        }
 
-        var anchor = document.getElementById('noVNC_control_bar_anchor');
+        const anchor = document.getElementById('noVNC_control_bar_anchor');
         if (anchor.classList.contains("noVNC_right")) {
             WebUtil.writeSetting('controlbar_pos', 'left');
             anchor.classList.remove("noVNC_right");
@@ -567,8 +563,8 @@ var UI = {
         UI.controlbarDrag = true;
     },
 
-    showControlbarHint: function (show) {
-        var hint = document.getElementById('noVNC_control_bar_hint');
+    showControlbarHint(show) {
+        const hint = document.getElementById('noVNC_control_bar_hint');
         if (show) {
             hint.classList.add("noVNC_active");
         } else {
@@ -576,12 +572,12 @@ var UI = {
         }
     },
 
-    dragControlbarHandle: function (e) {
+    dragControlbarHandle(e) {
         if (!UI.controlbarGrabbed) return;
 
-        var ptr = getPointerEvent(e);
+        const ptr = getPointerEvent(e);
 
-        var anchor = document.getElementById('noVNC_control_bar_anchor');
+        const anchor = document.getElementById('noVNC_control_bar_anchor');
         if (ptr.clientX < (window.innerWidth * 0.1)) {
             if (anchor.classList.contains("noVNC_right")) {
                 UI.toggleControlbarSide();
@@ -593,17 +589,14 @@ var UI = {
         }
 
         if (!UI.controlbarDrag) {
-            // The goal is to trigger on a certain physical width, the
-            // devicePixelRatio brings us a bit closer but is not optimal.
-            var dragThreshold = 10 * (window.devicePixelRatio || 1);
-            var dragDistance = Math.abs(ptr.clientY - UI.controlbarMouseDownClientY);
+            const dragDistance = Math.abs(ptr.clientY - UI.controlbarMouseDownClientY);
 
             if (dragDistance < dragThreshold) return;
 
             UI.controlbarDrag = true;
         }
 
-        var eventY = ptr.clientY - UI.controlbarMouseDownOffsetY;
+        const eventY = ptr.clientY - UI.controlbarMouseDownOffsetY;
 
         UI.moveControlbarHandle(eventY);
 
@@ -614,19 +607,19 @@ var UI = {
     },
 
     // Move the handle but don't allow any position outside the bounds
-    moveControlbarHandle: function (viewportRelativeY) {
-        var handle = document.getElementById("noVNC_control_bar_handle");
-        var handleHeight = handle.getBoundingClientRect().height;
-        var controlbarBounds = document.getElementById("noVNC_control_bar")
+    moveControlbarHandle(viewportRelativeY) {
+        const handle = document.getElementById("noVNC_control_bar_handle");
+        const handleHeight = handle.getBoundingClientRect().height;
+        const controlbarBounds = document.getElementById("noVNC_control_bar")
             .getBoundingClientRect();
-        var margin = 10;
+        const margin = 10;
 
         // These heights need to be non-zero for the below logic to work
         if (handleHeight === 0 || controlbarBounds.height === 0) {
             return;
         }
 
-        var newY = viewportRelativeY;
+        let newY = viewportRelativeY;
 
         // Check if the coordinates are outside the control bar
         if (newY < controlbarBounds.top + margin) {
@@ -647,19 +640,19 @@ var UI = {
         }
 
         // The transform needs coordinates that are relative to the parent
-        var parentRelativeY = newY - controlbarBounds.top;
+        const parentRelativeY = newY - controlbarBounds.top;
         handle.style.transform = "translateY(" + parentRelativeY + "px)";
     },
 
-    updateControlbarHandle: function () {
+    updateControlbarHandle() {
         // Since the control bar is fixed on the viewport and not the page,
         // the move function expects coordinates relative the the viewport.
-        var handle = document.getElementById("noVNC_control_bar_handle");
-        var handleBounds = handle.getBoundingClientRect();
+        const handle = document.getElementById("noVNC_control_bar_handle");
+        const handleBounds = handle.getBoundingClientRect();
         UI.moveControlbarHandle(handleBounds.top);
     },
 
-    controlbarHandleMouseUp: function(e) {
+    controlbarHandleMouseUp(e) {
         if ((e.type == "mouseup") && (e.button != 0)) return;
 
         // mouseup and mousedown on the same place toggles the controlbar
@@ -674,13 +667,13 @@ var UI = {
         UI.showControlbarHint(false);
     },
 
-    controlbarHandleMouseDown: function(e) {
+    controlbarHandleMouseDown(e) {
         if ((e.type == "mousedown") && (e.button != 0)) return;
 
-        var ptr = getPointerEvent(e);
+        const ptr = getPointerEvent(e);
 
-        var handle = document.getElementById("noVNC_control_bar_handle");
-        var bounds = handle.getBoundingClientRect();
+        const handle = document.getElementById("noVNC_control_bar_handle");
+        const bounds = handle.getBoundingClientRect();
 
         // Touch events have implicit capture
         if (e.type === "mousedown") {
@@ -700,7 +693,7 @@ var UI = {
         UI.activateControlbar();
     },
 
-    toggleExpander: function(e) {
+    toggleExpander(e) {
         if (this.classList.contains("noVNC_open")) {
             this.classList.remove("noVNC_open");
         } else {
@@ -715,34 +708,37 @@ var UI = {
  * ------v------*/
 
     // Initial page load read/initialization of settings
-    initSetting: function(name, defVal) {
+    initSetting(name, defVal) {
         // Check Query string followed by cookie
-        var val = WebUtil.getConfigVar(name);
+        let val = WebUtil.getConfigVar(name);
         if (val === null) {
             val = WebUtil.readSetting(name, defVal);
         }
-        UI.updateSetting(name, val);
+        WebUtil.setSetting(name, val);
+        UI.updateSetting(name);
         return val;
     },
 
+    // Set the new value, update and disable form control setting
+    forceSetting(name, val) {
+        WebUtil.setSetting(name, val);
+        UI.updateSetting(name);
+        UI.disableSetting(name);
+    },
+
     // Update cookie and form control setting. If value is not set, then
     // updates from control to current cookie setting.
-    updateSetting: function(name, value) {
-
-        // Save the cookie for this session
-        if (typeof value !== 'undefined') {
-            WebUtil.writeSetting(name, value);
-        }
+    updateSetting(name) {
 
         // Update the settings control
-        value = UI.getSetting(name);
+        let value = UI.getSetting(name);
 
-        var ctrl = document.getElementById('noVNC_setting_' + name);
+        const ctrl = document.getElementById('noVNC_setting_' + name);
         if (ctrl.type === 'checkbox') {
             ctrl.checked = value;
 
         } else if (typeof ctrl.options !== 'undefined') {
-            for (var i = 0; i < ctrl.options.length; i += 1) {
+            for (let i = 0; i < ctrl.options.length; i += 1) {
                 if (ctrl.options[i].value === value) {
                     ctrl.selectedIndex = i;
                     break;
@@ -759,8 +755,9 @@ var UI = {
     },
 
     // Save control setting to cookie
-    saveSetting: function(name) {
-        var val, ctrl = document.getElementById('noVNC_setting_' + name);
+    saveSetting(name) {
+        const ctrl = document.getElementById('noVNC_setting_' + name);
+        let val;
         if (ctrl.type === 'checkbox') {
             val = ctrl.checked;
         } else if (typeof ctrl.options !== 'undefined') {
@@ -774,11 +771,11 @@ var UI = {
     },
 
     // Read form control compatible setting from cookie
-    getSetting: function(name) {
-        var ctrl = document.getElementById('noVNC_setting_' + name);
-        var val = WebUtil.readSetting(name);
+    getSetting(name) {
+        const ctrl = document.getElementById('noVNC_setting_' + name);
+        let val = WebUtil.readSetting(name);
         if (typeof val !== 'undefined' && val !== null && ctrl.type === 'checkbox') {
-            if (val.toString().toLowerCase() in {'0':1, 'no':1, 'false':1}) {
+            if (val.toString().toLowerCase() in {'0': 1, 'no': 1, 'false': 1}) {
                 val = false;
             } else {
                 val = true;
@@ -790,14 +787,14 @@ var 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) {
-        var ctrl = document.getElementById('noVNC_setting_' + name);
+    disableSetting(name) {
+        const ctrl = document.getElementById('noVNC_setting_' + name);
         ctrl.disabled = true;
         ctrl.label.classList.add('noVNC_disabled');
     },
 
-    enableSetting: function(name) {
-        var ctrl = document.getElementById('noVNC_setting_' + name);
+    enableSetting(name) {
+        const ctrl = document.getElementById('noVNC_setting_' + name);
         ctrl.disabled = false;
         ctrl.label.classList.remove('noVNC_disabled');
     },
@@ -808,7 +805,7 @@ var UI = {
  *    PANELS
  * ------v------*/
 
-    closeAllPanels: function() {
+    closeAllPanels() {
         UI.closeSettingsPanel();
         UI.closePowerPanel();
         UI.closeClipboardPanel();
@@ -821,7 +818,7 @@ var UI = {
  * SETTINGS (panel)
  * ------v------*/
 
-    openSettingsPanel: function() {
+    openSettingsPanel() {
         UI.closeAllPanels();
         UI.openControlbar();
 
@@ -843,14 +840,14 @@ var 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();
@@ -865,7 +862,7 @@ var UI = {
  *     POWER
  * ------v------*/
 
-    openPowerPanel: function() {
+    openPowerPanel() {
         UI.closeAllPanels();
         UI.openControlbar();
 
@@ -875,14 +872,14 @@ var 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();
@@ -892,7 +889,7 @@ var UI = {
     },
 
     // Disable/enable power button
-    updatePowerButton: function() {
+    updatePowerButton() {
         if (UI.connected &&
             UI.rfb.capabilities.power &&
             !UI.rfb.viewOnly) {
@@ -912,7 +909,7 @@ var UI = {
  *   CLIPBOARD
  * ------v------*/
 
-    openClipboardPanel: function() {
+    openClipboardPanel() {
         UI.closeAllPanels();
         UI.openControlbar();
 
@@ -922,14 +919,14 @@ var 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();
@@ -938,20 +935,20 @@ var UI = {
         }
     },
 
-    clipboardReceive: function(e) {
-        Log.Debug(">> UI.clipboardReceive: " + e.detail.text.substr(0,40) + "...");
+    clipboardReceive(e) {
+        Log.Debug(">> UI.clipboardReceive: " + e.detail.text.substr(0, 40) + "...");
         document.getElementById('noVNC_clipboard_text').value = e.detail.text;
         Log.Debug("<< UI.clipboardReceive");
     },
 
-    clipboardClear: function() {
+    clipboardClear() {
         document.getElementById('noVNC_clipboard_text').value = "";
         UI.rfb.clipboardPasteFrom("");
     },
 
-    clipboardSend: function() {
-        var text = document.getElementById('noVNC_clipboard_text').value;
-        Log.Debug(">> UI.clipboardSend: " + text.substr(0,40) + "...");
+    clipboardSend() {
+        const text = document.getElementById('noVNC_clipboard_text').value;
+        Log.Debug(">> UI.clipboardSend: " + text.substr(0, 40) + "...");
         UI.rfb.clipboardPasteFrom(text);
         Log.Debug("<< UI.clipboardSend");
     },
@@ -962,26 +959,26 @@ var 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') {
             return;
         }
 
-        var host = UI.getSetting('host');
-        var port = UI.getSetting('port');
-        var path = UI.getSetting('path');
+        const host = UI.getSetting('host');
+        const port = UI.getSetting('port');
+        const path = UI.getSetting('path');
 
         if (typeof password === 'undefined') {
             password = WebUtil.getConfigVar('password');
@@ -1005,7 +1002,7 @@ var UI = {
 
         UI.updateVisualState('connecting');
 
-        var url;
+        let url;
 
         url = UI.getSetting('encrypt') ? 'wss' : 'ws';
 
@@ -1023,7 +1020,7 @@ var UI = {
         UI.rfb.addEventListener("disconnect", UI.disconnectFinished);
         UI.rfb.addEventListener("credentialsrequired", UI.credentials);
         UI.rfb.addEventListener("securityfailure", UI.securityFailed);
-        UI.rfb.addEventListener("capabilities", function () { UI.updatePowerButton(); });
+        UI.rfb.addEventListener("capabilities", UI.updatePowerButton);
         UI.rfb.addEventListener("clipboard", UI.clipboardReceive);
         UI.rfb.addEventListener("bell", UI.bell);
         UI.rfb.addEventListener("desktopname", UI.updateDesktopName);
@@ -1034,7 +1031,7 @@ var UI = {
         UI.updateViewOnly(); // requires UI.rfb
     },
 
-    disconnect: function() {
+    disconnect() {
         UI.closeAllPanels();
         UI.rfb.disconnect();
 
@@ -1048,7 +1045,7 @@ var 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.
@@ -1059,7 +1056,7 @@ var UI = {
         UI.connect(null, UI.reconnect_password);
     },
 
-    cancelReconnect: function() {
+    cancelReconnect() {
         if (UI.reconnect_callback !== null) {
             clearTimeout(UI.reconnect_callback);
             UI.reconnect_callback = null;
@@ -1071,7 +1068,7 @@ var UI = {
         UI.openConnectPanel();
     },
 
-    connectFinished: function (e) {
+    connectFinished(e) {
         UI.connected = true;
         UI.inhibit_reconnect = false;
 
@@ -1088,8 +1085,8 @@ var UI = {
         UI.rfb.focus();
     },
 
-    disconnectFinished: function (e) {
-        let wasConnected = UI.connected;
+    disconnectFinished(e) {
+        const wasConnected = UI.connected;
 
         // This variable is ideally set when disconnection starts, but
         // when the disconnection isn't clean or if it is initiated by
@@ -1110,7 +1107,7 @@ var UI = {
         } else if (UI.getSetting('reconnect', false) === true && !UI.inhibit_reconnect) {
             UI.updateVisualState('reconnecting');
 
-            var delay = parseInt(UI.getSetting('reconnect_delay'));
+            const delay = parseInt(UI.getSetting('reconnect_delay'));
             UI.reconnect_callback = setTimeout(UI.reconnect, delay);
             return;
         } else {
@@ -1122,7 +1119,7 @@ var 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
@@ -1142,25 +1139,24 @@ var UI = {
  *   PASSWORD
  * ------v------*/
 
-    credentials: function(e) {
+    credentials(e) {
         // FIXME: handle more types
         document.getElementById('noVNC_password_dlg')
             .classList.add('noVNC_open');
 
-        setTimeout(function () {
-                document.getElementById('noVNC_password_input').focus();
-            }, 100);
+        setTimeout(() => document
+            .getElementById('noVNC_password_input').focus(), 100);
 
         Log.Warn("Server asked for a password");
         UI.showStatus(_("Password is required"), "warning");
     },
 
-    setPassword: function(e) {
+    setPassword(e) {
         // Prevent actually submitting the form
         e.preventDefault();
 
-        var inputElem = document.getElementById('noVNC_password_input');
-        var password = inputElem.value;
+        const inputElem = document.getElementById('noVNC_password_input');
+        const password = inputElem.value;
         // Clear the input after reading the password
         inputElem.value = "";
         UI.rfb.sendCredentials({ password: password });
@@ -1175,7 +1171,7 @@ var UI = {
  *   FULLSCREEN
  * ------v------*/
 
-    toggleFullscreen: function() {
+    toggleFullscreen() {
         if (document.fullscreenElement || // alternative standard method
             document.mozFullScreenElement || // currently working methods
             document.webkitFullscreenElement ||
@@ -1200,11 +1196,10 @@ var UI = {
                 document.body.msRequestFullscreen();
             }
         }
-        UI.enableDisableViewClip();
         UI.updateFullscreenButton();
     },
 
-    updateFullscreenButton: function() {
+    updateFullscreenButton() {
         if (document.fullscreenElement || // alternative standard method
             document.mozFullScreenElement || // currently working methods
             document.webkitFullscreenElement ||
@@ -1224,7 +1219,7 @@ var UI = {
  * ------v------*/
 
     // Apply remote resizing or local scaling
-    applyResizeMode: function() {
+    applyResizeMode() {
         if (!UI.rfb) return;
 
         UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale';
@@ -1237,20 +1232,25 @@ var UI = {
  * VIEW CLIPPING
  * ------v------*/
 
-    // Update parameters that depend on the viewport clip setting
-    updateViewClip: function() {
+    // Update viewport clipping property for the connection. The normal
+    // case is to get the value from the setting. There are special cases
+    // for when the viewport is scaled or when a touch device is used.
+    updateViewClip() {
         if (!UI.rfb) return;
 
-        var cur_clip = UI.rfb.clipViewport;
-        var new_clip = UI.getSetting('view_clip');
+        const scaling = UI.getSetting('resize') === 'scale';
 
-        if (isTouchDevice) {
+        if (scaling) {
+            // Can't be clipping if viewport is scaled to fit
+            UI.forceSetting('view_clip', false);
+            UI.rfb.clipViewport  = false;
+        } else if (isTouchDevice) {
             // Touch devices usually have shit scrollbars
-            new_clip = true;
-        }
-
-        if (cur_clip !== new_clip) {
-            UI.rfb.clipViewport = new_clip;
+            UI.forceSetting('view_clip', true);
+            UI.rfb.clipViewport = true;
+        } else {
+            UI.enableSetting('view_clip');
+            UI.rfb.clipViewport = UI.getSetting('view_clip');
         }
 
         // Changing the viewport may change the state of
@@ -1258,44 +1258,23 @@ var UI = {
         UI.updateViewDrag();
     },
 
-    // Handle special cases where viewport clipping is forced on/off or locked
-    enableDisableViewClip: function() {
-        var resizeSetting = UI.getSetting('resize');
-        // Disable clipping if we are scaling, connected or on touch
-        if (resizeSetting === 'scale' ||
-            isTouchDevice) {
-            UI.disableSetting('view_clip');
-        } else {
-            UI.enableSetting('view_clip');
-        }
-    },
-
 /* ------^-------
  * /VIEW CLIPPING
  * ==============
  *    VIEWDRAG
  * ------v------*/
 
-    toggleViewDrag: function() {
-        if (!UI.rfb) return;
-
-        var drag = UI.rfb.dragViewport;
-        UI.setViewDrag(!drag);
-     },
-
-    // Set the view drag mode which moves the viewport on mouse drags
-    setViewDrag: function(drag) {
+    toggleViewDrag() {
         if (!UI.rfb) return;
 
-        UI.rfb.dragViewport = drag;
-
+        UI.rfb.dragViewport = !UI.rfb.dragViewport;
         UI.updateViewDrag();
     },
 
-    updateViewDrag: function() {
+    updateViewDrag() {
         if (!UI.connected) return;
 
-        var viewDragButton = document.getElementById('noVNC_view_drag_button');
+        const viewDragButton = document.getElementById('noVNC_view_drag_button');
 
         if (!UI.rfb.clipViewport && UI.rfb.dragViewport) {
             // We are no longer clipping the viewport. Make sure
@@ -1336,33 +1315,35 @@ var UI = {
  *    KEYBOARD
  * ------v------*/
 
-    showVirtualKeyboard: function() {
+    showVirtualKeyboard() {
         if (!isTouchDevice) return;
 
-        var input = document.getElementById('noVNC_keyboardinput');
+        const input = document.getElementById('noVNC_keyboardinput');
 
         if (document.activeElement == input) return;
 
         input.focus();
 
         try {
-            var l = input.value.length;
+            const l = input.value.length;
             // Move the caret to the end
             input.setSelectionRange(l, l);
-        } catch (err) {} // setSelectionRange is undefined in Google Chrome
+        } catch (err) {
+            // setSelectionRange is undefined in Google Chrome 
+        }
     },
 
-    hideVirtualKeyboard: function() {
+    hideVirtualKeyboard() {
         if (!isTouchDevice) return;
 
-        var input = document.getElementById('noVNC_keyboardinput');
+        const input = document.getElementById('noVNC_keyboardinput');
 
         if (document.activeElement != input) return;
 
         input.blur();
     },
 
-    toggleVirtualKeyboard: function () {
+    toggleVirtualKeyboard() {
         if (document.getElementById('noVNC_keyboard_button')
             .classList.contains("noVNC_selected")) {
             UI.hideVirtualKeyboard();
@@ -1371,7 +1352,7 @@ var UI = {
         }
     },
 
-    onfocusVirtualKeyboard: function(event) {
+    onfocusVirtualKeyboard(event) {
         document.getElementById('noVNC_keyboard_button')
             .classList.add("noVNC_selected");
         if (UI.rfb) {
@@ -1379,7 +1360,7 @@ var UI = {
         }
     },
 
-    onblurVirtualKeyboard: function(event) {
+    onblurVirtualKeyboard(event) {
         document.getElementById('noVNC_keyboard_button')
             .classList.remove("noVNC_selected");
         if (UI.rfb) {
@@ -1387,8 +1368,8 @@ var UI = {
         }
     },
 
-    keepVirtualKeyboard: function(event) {
-        var input = document.getElementById('noVNC_keyboardinput');
+    keepVirtualKeyboard(event) {
+        const input = document.getElementById('noVNC_keyboardinput');
 
         // Only prevent focus change if the virtual keyboard is active
         if (document.activeElement != input) {
@@ -1415,13 +1396,13 @@ var UI = {
         event.preventDefault();
     },
 
-    keyboardinputReset: function() {
-        var kbi = document.getElementById('noVNC_keyboardinput');
+    keyboardinputReset() {
+        const kbi = document.getElementById('noVNC_keyboardinput');
         kbi.value = new Array(UI.defaultKeyboardinputLen).join("_");
         UI.lastKeyboardinput = kbi.value;
     },
 
-    keyEvent: function (keysym, code, down) {
+    keyEvent(keysym, code, down) {
         if (!UI.rfb) return;
 
         UI.rfb.sendKey(keysym, code, down);
@@ -1431,18 +1412,18 @@ var 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;
 
-        var newValue = event.target.value;
+        const newValue = event.target.value;
 
         if (!UI.lastKeyboardinput) {
             UI.keyboardinputReset();
         }
-        var oldValue = UI.lastKeyboardinput;
+        const oldValue = UI.lastKeyboardinput;
 
-        var newLen;
+        let newLen;
         try {
             // Try to check caret position since whitespace at the end
             // will not be considered by value.length in some browsers
@@ -1451,20 +1432,14 @@ var UI = {
             // selectionStart is undefined in Google Chrome
             newLen = newValue.length;
         }
-        var oldLen = oldValue.length;
+        const oldLen = oldValue.length;
 
-        var backspaces;
-        var inputs = newLen - oldLen;
-        if (inputs < 0) {
-            backspaces = -inputs;
-        } else {
-            backspaces = 0;
-        }
+        let inputs = newLen - oldLen;
+        let backspaces = inputs < 0 ? -inputs : 0;
 
         // Compare the old string with the new to account for
         // text-corrections or other input that modify existing text
-        var i;
-        for (i = 0; i < Math.min(oldLen, newLen); i++) {
+        for (let i = 0; i < Math.min(oldLen, newLen); i++) {
             if (newValue.charAt(i) != oldValue.charAt(i)) {
                 inputs = newLen - i;
                 backspaces = oldLen - i;
@@ -1473,10 +1448,10 @@ var UI = {
         }
 
         // Send the key events
-        for (i = 0; i < backspaces; i++) {
+        for (let i = 0; i < backspaces; i++) {
             UI.rfb.sendKey(KeyTable.XK_BackSpace, "Backspace");
         }
-        for (i = newLen - inputs; i < newLen; i++) {
+        for (let i = newLen - inputs; i < newLen; i++) {
             UI.rfb.sendKey(keysyms.lookup(newValue.charCodeAt(i)));
         }
 
@@ -1504,7 +1479,7 @@ var UI = {
  *   EXTRA KEYS
  * ------v------*/
 
-    openExtraKeys: function() {
+    openExtraKeys() {
         UI.closeAllPanels();
         UI.openControlbar();
 
@@ -1514,14 +1489,14 @@ var 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();
@@ -1530,16 +1505,16 @@ var UI = {
         }
     },
 
-    sendEsc: function() {
+    sendEsc() {
         UI.rfb.sendKey(KeyTable.XK_Escape, "Escape");
     },
 
-    sendTab: function() {
+    sendTab() {
         UI.rfb.sendKey(KeyTable.XK_Tab);
     },
 
-    toggleCtrl: function() {
-        var btn = document.getElementById('noVNC_toggle_ctrl_button');
+    toggleCtrl() {
+        const btn = document.getElementById('noVNC_toggle_ctrl_button');
         if (btn.classList.contains("noVNC_selected")) {
             UI.rfb.sendKey(KeyTable.XK_Control_L, "ControlLeft", false);
             btn.classList.remove("noVNC_selected");
@@ -1549,8 +1524,8 @@ var UI = {
         }
     },
 
-    toggleAlt: function() {
-        var btn = document.getElementById('noVNC_toggle_alt_button');
+    toggleAlt() {
+        const btn = document.getElementById('noVNC_toggle_alt_button');
         if (btn.classList.contains("noVNC_selected")) {
             UI.rfb.sendKey(KeyTable.XK_Alt_L, "AltLeft", false);
             btn.classList.remove("noVNC_selected");
@@ -1560,7 +1535,7 @@ var UI = {
         }
     },
 
-    sendCtrlAltDel: function() {
+    sendCtrlAltDel() {
         UI.rfb.sendCtrlAltDel();
     },
 
@@ -1570,15 +1545,15 @@ var UI = {
  *     MISC
  * ------v------*/
 
-    setMouseButton: function(num) {
-        var view_only = UI.rfb.viewOnly;
+    setMouseButton(num) {
+        const view_only = UI.rfb.viewOnly;
         if (UI.rfb && !view_only) {
             UI.rfb.touchButton = num;
         }
 
-        var blist = [0, 1,2,4];
-        for (var b = 0; b < blist.length; b++) {
-            var button = document.getElementById('noVNC_mouse_button' +
+        const blist = [0, 1, 2, 4];
+        for (let b = 0; b < blist.length; b++) {
+            const button = document.getElementById('noVNC_mouse_button' +
                                                  blist[b]);
             if (blist[b] === num && !view_only) {
                 button.classList.remove("noVNC_hidden");
@@ -1588,7 +1563,7 @@ var UI = {
         }
     },
 
-    updateViewOnly: function() {
+    updateViewOnly() {
         if (!UI.rfb) return;
         UI.rfb.viewOnly = UI.getSetting('view_only');
 
@@ -1607,22 +1582,22 @@ var 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') {
-            var promise = document.getElementById('noVNC_bell').play();
+            const promise = document.getElementById('noVNC_bell').play();
             // The standards disagree on the return value here
             if (promise) {
-                promise.catch(function(e) {
+                promise.catch((e) => {
                     if (e.name === "NotAllowedError") {
                         // Ignore when the browser doesn't let us play audio.
                         // It is common that the browsers require audio to be
@@ -1636,8 +1611,8 @@ var UI = {
     },
 
     //Helper to add options to dropdown.
-    addOption: function(selectbox, text, value) {
-        var optn = document.createElement("OPTION");
+    addOption(selectbox, text, value) {
+        const optn = document.createElement("OPTION");
         optn.text = text;
         optn.value = value;
         selectbox.options.add(optn);
@@ -1650,16 +1625,17 @@ var UI = {
 };
 
 // Set up translations
-var LINGUAS = ["de", "el", "es", "nl", "pl", "sv", "tr", "zh"];
+const LINGUAS = ["de", "el", "es", "nl", "pl", "sv", "tr", "zh_CN", "zh_TW"];
 l10n.setup(LINGUAS);
 if (l10n.language !== "en" && l10n.dictionary === undefined) {
-    WebUtil.fetchJSON('app/locale/' + l10n.language + '.json', function (translations) {
+    WebUtil.fetchJSON('app/locale/' + l10n.language + '.json', (translations) => {
         l10n.dictionary = translations;
 
         // wait for translations to load before loading the UI
         UI.prime();
-    }, function (err) {
-        throw err;
+    }, (err) => {
+        Log.Error("Failed to load translations: " + err);
+        UI.prime();
     });
 } else {
     UI.prime();