]> git.proxmox.com Git - mirror_novnc.git/blobdiff - app/ui.js
Remove alternative style sheets
[mirror_novnc.git] / app / ui.js
index 50e2400b4112ce8fdf75d79b0f8175fb325de655..9ff24068124d41c93faa8f288e20fc72a51d2cd2 100644 (file)
--- a/app/ui.js
+++ b/app/ui.js
@@ -14,6 +14,7 @@
 /* [module]
  * import Util from "../core/util";
  * import KeyTable from "../core/input/keysym";
+ * import keysyms from "./keysymdef";
  * import RFB from "../core/rfb";
  * import Display from "../core/display";
  * import WebUtil from "./webutil";
@@ -24,6 +25,46 @@ var UI;
 (function () {
     "use strict";
 
+    // Fallback for all uncought errors
+    window.addEventListener('error', function(event) {
+        try {
+            var msg = "";
+
+            msg += "<div>";
+            msg += event.message;
+            msg += "</div>";
+
+            msg += " <div class=\"noVNC_location\">";
+            msg += event.filename;
+            msg += ":" + event.lineno + ":" + event.colno;
+            msg += "</div>";
+
+            if ((event.error !== undefined) &&
+                (event.error.stack !== undefined)) {
+                msg += "<div class=\"noVNC_stack\">";
+                msg += event.error.stack;
+                msg += "</div>";
+            }
+
+            document.getElementById('noVNC_fallback_error')
+                .classList.add("noVNC_open");
+            document.getElementById('noVNC_fallback_errormsg').innerHTML = msg;
+        } catch (exc) {
+            document.write("noVNC encountered an error.");
+        }
+        // Don't return true since this would prevent the error
+        // from being printed to the browser console.
+        return false;
+    });
+
+    // Set up translations
+    var LINGUAS = ["de", "el", "nl", "sv"];
+    Util.Localisation.setup(LINGUAS);
+    if (Util.Localisation.language !== "en") {
+        WebUtil.load_scripts(
+            {'app': ["locale/" + Util.Localisation.language + ".js"]});
+    }
+
     /* [begin skip-as-module] */
     // Load supporting scripts
     WebUtil.load_scripts(
@@ -34,6 +75,8 @@ var UI;
     window.onscriptsload = function () { UI.load(); };
     /* [end skip-as-module] */
 
+    var _ = Util.Localisation.get;
+
     UI = {
 
         connected: false,
@@ -49,9 +92,7 @@ var UI;
         controlbarDrag: false,
         controlbarMouseDownClientY: 0,
         controlbarMouseDownOffsetY: 0,
-        keyboardVisible: false,
 
-        isTouchDevice: false,
         isSafari: false,
         rememberedClipSetting: null,
         lastKeyboardinput: null,
@@ -67,14 +108,16 @@ var UI;
         start: function(callback) {
 
             // Setup global variables first
-            UI.isTouchDevice = 'ontouchstart' in document.documentElement;
             UI.isSafari = (navigator.userAgent.indexOf('Safari') !== -1 &&
                            navigator.userAgent.indexOf('Chrome') === -1);
 
             UI.initSettings();
 
+            // Translate the DOM
+            Util.Localisation.translateDOM();
+
             // Adapt the interface for touch screen devices
-            if (UI.isTouchDevice) {
+            if (Util.isTouchDevice) {
                 document.documentElement.classList.add("noVNC_touch");
                 // Remove the address bar
                 setTimeout(function() { window.scrollTo(0, 1); }, 100);
@@ -83,6 +126,11 @@ var UI;
                 UI.initSetting('clip', false);
             }
 
+            // Restore control bar position
+            if (WebUtil.readSetting('controlbar_pos') === 'right') {
+                UI.toggleControlbarSide();
+            }
+
             // Setup and initialize event handlers
             UI.setupWindowEvents();
             UI.setupFullscreen();
@@ -119,13 +167,7 @@ var UI;
         },
 
         initSettings: function() {
-            // Stylesheet selection dropdown
-            var sheet = WebUtil.selectStylesheet();
-            var sheets = WebUtil.getStylesheets();
             var i;
-            for (i = 0; i < sheets.length; i += 1) {
-                UI.addOption(document.getElementById('noVNC_setting_stylesheet'),sheets[i].title, sheets[i].title);
-            }
 
             // Logging selection dropdown
             var llevels = ['error', 'warn', 'info', 'debug'];
@@ -137,11 +179,6 @@ var UI;
             UI.initSetting('logging', 'warn');
             WebUtil.init_logging(UI.getSetting('logging'));
 
-            UI.initSetting('stylesheet', 'default');
-            WebUtil.selectStylesheet(null);
-            // call twice to get around webkit bug
-            WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
-
             // if port == 80 (or 443) then it won't be present and should be
             // set manually
             var port = window.location.port;
@@ -160,21 +197,18 @@ var UI;
             UI.initSetting('password', '');
             UI.initSetting('encrypt', (window.location.protocol === "https:"));
             UI.initSetting('true_color', true);
-            UI.initSetting('cursor', !UI.isTouchDevice);
+            UI.initSetting('cursor', !Util.isTouchDevice);
             UI.initSetting('resize', 'off');
             UI.initSetting('shared', true);
             UI.initSetting('view_only', false);
             UI.initSetting('path', 'websockify');
             UI.initSetting('repeaterID', '');
-            UI.initSetting('token', '');
         },
 
         setupWindowEvents: function() {
-            window.addEventListener( 'resize', function () {
-                UI.applyResizeMode();
-                UI.updateViewClip();
-                UI.updateViewDrag();
-            } );
+            window.addEventListener('resize', UI.applyResizeMode);
+            window.addEventListener('resize', UI.updateViewClip);
+            window.addEventListener('resize', UI.updateViewDrag);
 
             document.getElementById("noVNC_status")
                 .addEventListener('click', UI.hideStatus);
@@ -236,11 +270,16 @@ var UI;
 
             document.getElementById("noVNC_keyboardinput")
                 .addEventListener('input', UI.keyInput);
+            document.getElementById("noVNC_keyboardinput")
+                .addEventListener('focus', UI.onfocusVirtualKeyboard);
             document.getElementById("noVNC_keyboardinput")
                 .addEventListener('blur', UI.onblurVirtualKeyboard);
             document.getElementById("noVNC_keyboardinput")
                 .addEventListener('submit', function () { return false; });
 
+            document.documentElement
+                .addEventListener('mousedown', UI.keepVirtualKeyboard, true);
+
             document.getElementById("noVNC_control_bar")
                 .addEventListener('touchstart', UI.activateControlbar);
             document.getElementById("noVNC_control_bar")
@@ -347,11 +386,11 @@ var UI;
                                   'onClipboard': UI.clipboardReceive,
                                   'onBell': UI.bell,
                                   'onFBUComplete': UI.initialResize,
-                                  'onFBResize': UI.updateViewDrag,
+                                  'onFBResize': UI.updateSessionSize,
                                   'onDesktopName': UI.updateDesktopName});
                 return true;
             } catch (exc) {
-                var msg = 'Unable to create RFB client -- ' + exc;
+                var msg = "Unable to create RFB client -- " + exc;
                 Util.Error(msg);
                 UI.showStatus(msg, 'error');
                 return false;
@@ -365,29 +404,39 @@ var UI;
  * ------v------*/
 
         updateState: function(rfb, state, oldstate) {
+            var msg;
+
+            document.documentElement.classList.remove("noVNC_connecting");
+            document.documentElement.classList.remove("noVNC_connected");
+            document.documentElement.classList.remove("noVNC_disconnecting");
+
             switch (state) {
                 case 'connecting':
-                    UI.showStatus("Connecting");
+                    document.getElementById("noVNC_transition_text").innerHTML = _("Connecting...");
+                    document.documentElement.classList.add("noVNC_connecting");
                     break;
                 case 'connected':
                     UI.connected = true;
+                    document.documentElement.classList.add("noVNC_connected");
                     if (rfb && rfb.get_encrypt()) {
-                        UI.showStatus("Connected (encrypted) to " +
-                                      UI.desktopName);
+                        msg = _("Connected (encrypted) to ") + UI.desktopName;
                     } else {
-                        UI.showStatus("Connected (unencrypted) to " +
-                                      UI.desktopName);
+                        msg = _("Connected (unencrypted) to ") + UI.desktopName;
                     }
+                    UI.showStatus(msg);
                     break;
                 case 'disconnecting':
-                    UI.showStatus("Disconnecting");
+                    UI.connected = false;
+                    document.getElementById("noVNC_transition_text").innerHTML = _("Disconnecting...");
+                    document.documentElement.classList.add("noVNC_disconnecting");
                     break;
                 case 'disconnected':
-                    UI.connected = false;
-                    UI.showStatus("Disconnected");
+                    UI.showStatus(_("Disconnected"));
                     break;
                 default:
-                    UI.showStatus("Invalid state", 'error');
+                    msg = "Invalid UI state";
+                    Util.Error(msg);
+                    UI.showStatus(msg, 'error');
                     break;
             }
 
@@ -402,7 +451,7 @@ var UI;
             if (Util.browserSupportsCursorURIs()) {
                 document.getElementById('noVNC_setting_cursor').disabled = UI.connected;
             } else {
-                UI.updateSetting('cursor', !UI.isTouchDevice);
+                UI.updateSetting('cursor', !Util.isTouchDevice);
                 document.getElementById('noVNC_setting_cursor').disabled = true;
             }
 
@@ -414,18 +463,29 @@ var UI;
             document.getElementById('noVNC_setting_repeaterID').disabled = UI.connected;
 
             if (UI.connected) {
-                document.documentElement.classList.add("noVNC_connected");
                 UI.updateViewClip();
                 UI.setMouseButton(1);
 
                 // Hide the controlbar after 2 seconds
                 UI.closeControlbarTimeout = setTimeout(UI.closeControlbar, 2000);
             } else {
-                document.documentElement.classList.remove("noVNC_connected");
                 UI.updateXvpButton(0);
                 UI.keepControlbar();
             }
 
+            // Hide input related buttons in view only mode
+            if (UI.rfb && UI.rfb.get_view_only()) {
+                document.getElementById('noVNC_keyboard_button')
+                    .classList.add('noVNC_hidden');
+                document.getElementById('noVNC_toggle_extra_keys_button')
+                    .classList.add('noVNC_hidden');
+            } else {
+                document.getElementById('noVNC_keyboard_button')
+                    .classList.remove('noVNC_hidden');
+                document.getElementById('noVNC_toggle_extra_keys_button')
+                    .classList.remove('noVNC_hidden');
+            }
+
             // State change disables viewport dragging.
             // It is enabled (toggled) by direct click on the button
             UI.setViewDrag(false);
@@ -526,11 +586,41 @@ 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 = ""; });
+
+            var anchor = document.getElementById('noVNC_control_bar_anchor');
+            if (anchor.classList.contains("noVNC_right")) {
+                WebUtil.writeSetting('controlbar_pos', 'left');
+                anchor.classList.remove("noVNC_right");
+            } else {
+                WebUtil.writeSetting('controlbar_pos', 'right');
+                anchor.classList.add("noVNC_right");
+            }
+
+            // Consider this a movement of the handle
+            UI.controlbarDrag = true;
+        },
+
         dragControlbarHandle: function (e) {
             if (!UI.controlbarGrabbed) return;
 
             var ptr = Util.getPointerEvent(e);
 
+            var anchor = document.getElementById('noVNC_control_bar_anchor');
+            if (ptr.clientX < (window.innerWidth * 0.1)) {
+                if (anchor.classList.contains("noVNC_right")) {
+                    UI.toggleControlbarSide();
+                }
+            } else if (ptr.clientX > (window.innerWidth * 0.9)) {
+                if (!anchor.classList.contains("noVNC_right")) {
+                    UI.toggleControlbarSide();
+                }
+            }
+
             if (!UI.controlbarDrag) {
                 // The goal is to trigger on a certain physical width, the
                 // devicePixelRatio brings us a bit closer but is not optimal.
@@ -548,40 +638,54 @@ var UI;
 
             e.preventDefault();
             e.stopPropagation();
+            UI.keepControlbar();
+            UI.activateControlbar();
         },
 
         // Move the handle but don't allow any position outside the bounds
-        moveControlbarHandle: function (posY) {
+        moveControlbarHandle: function (viewportRelativeY) {
             var handle = document.getElementById("noVNC_control_bar_handle");
-            var handleHeight = Util.getPosition(handle).height;
-            var controlbar = document.getElementById("noVNC_control_bar");
-            var controlbarBounds = Util.getPosition(controlbar);
-            var controlbarTop = controlbarBounds.y;
-            var controlbarBottom = controlbarBounds.y + controlbarBounds.height;
+            var handleHeight = handle.getBoundingClientRect().height;
+            var controlbarBounds = document.getElementById("noVNC_control_bar")
+                .getBoundingClientRect();
             var margin = 10;
 
-            var viewportY = posY;
+            // These heights need to be non-zero for the below logic to work
+            if (handleHeight === 0 || controlbarBounds.height === 0) {
+                return;
+            }
+
+            var newY = viewportRelativeY;
+
+            // Check if the coordinates are outside the control bar
+            if (newY < controlbarBounds.top + margin) {
+                // Force coordinates to be below the top of the control bar
+                newY = controlbarBounds.top + margin;
 
-            // Refuse coordinates outside the control bar
-            if (viewportY < controlbarTop + margin) {
-                viewportY = controlbarTop + margin;
-            } else if (viewportY > controlbarBottom - handleHeight - margin) {
-                viewportY = controlbarBottom - handleHeight - margin;
+            } else if (newY > controlbarBounds.top +
+                       controlbarBounds.height - handleHeight - margin) {
+                // Force coordinates to be above the bottom of the control bar
+                newY = controlbarBounds.top +
+                    controlbarBounds.height - handleHeight - margin;
             }
 
             // Corner case: control bar too small for stable position
             if (controlbarBounds.height < (handleHeight + margin * 2)) {
-                viewportY = controlbarTop + (controlbarBounds.height - handleHeight) / 2;
+                newY = controlbarBounds.top +
+                    (controlbarBounds.height - handleHeight) / 2;
             }
 
-            var relativeY = viewportY - controlbarTop;
-            handle.style.transform = "translateY(" + relativeY + "px)";
+            // The transform needs coordinates that are relative to the parent
+            var parentRelativeY = newY - controlbarBounds.top;
+            handle.style.transform = "translateY(" + parentRelativeY + "px)";
         },
 
         updateControlbarHandle: function () {
+            // 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 pos = Util.getPosition(handle);
-            UI.moveControlbarHandle(pos.y);
+            var handleBounds = handle.getBoundingClientRect();
+            UI.moveControlbarHandle(handleBounds.top);
         },
 
         controlbarHandleMouseUp: function(e) {
@@ -592,6 +696,8 @@ var UI;
                 UI.toggleControlbar();
                 e.preventDefault();
                 e.stopPropagation();
+                UI.keepControlbar();
+                UI.activateControlbar();
             }
             UI.controlbarGrabbed = false;
         },
@@ -612,6 +718,8 @@ var UI;
             UI.controlbarMouseDownOffsetY = ptr.clientY - bounds.top;
             e.preventDefault();
             e.stopPropagation();
+            UI.keepControlbar();
+            UI.activateControlbar();
         },
 
 /* ------^-------
@@ -719,11 +827,9 @@ var UI;
             UI.saveSetting('view_only');
             UI.saveSetting('path');
             UI.saveSetting('repeaterID');
-            UI.saveSetting('stylesheet');
             UI.saveSetting('logging');
 
             // Settings with immediate (non-connected related) effect
-            WebUtil.selectStylesheet(UI.getSetting('stylesheet'));
             WebUtil.init_logging(UI.getSetting('logging'));
             UI.updateViewClip();
             UI.updateViewDrag();
@@ -759,7 +865,7 @@ var UI;
             if (Util.browserSupportsCursorURIs()) {
                 UI.updateSetting('cursor');
             } else {
-                UI.updateSetting('cursor', !UI.isTouchDevice);
+                UI.updateSetting('cursor', !Util.isTouchDevice);
                 document.getElementById('noVNC_setting_cursor').disabled = true;
             }
             UI.updateSetting('clip');
@@ -768,7 +874,6 @@ var UI;
             UI.updateSetting('view_only');
             UI.updateSetting('path');
             UI.updateSetting('repeaterID');
-            UI.updateSetting('stylesheet');
             UI.updateSetting('logging');
 
             document.getElementById('noVNC_settings')
@@ -831,7 +936,7 @@ var UI;
 
         // Disable/enable XVP button
         updateXvpButton: function(ver) {
-            if (ver >= 1) {
+            if (ver >= 1 && !UI.rfb.get_view_only()) {
                 document.getElementById('noVNC_xvp_button')
                     .classList.remove("noVNC_hidden");
             } else {
@@ -918,7 +1023,6 @@ var UI;
 
             UI.saveSetting('host');
             UI.saveSetting('port');
-            UI.saveSetting('token');
             //UI.saveSetting('password');
         },
 
@@ -935,16 +1039,12 @@ var UI;
             var host = document.getElementById('noVNC_setting_host').value;
             var port = document.getElementById('noVNC_setting_port').value;
             var password = document.getElementById('noVNC_setting_password').value;
-            var token = document.getElementById('noVNC_setting_token').value;
             var path = document.getElementById('noVNC_setting_path').value;
 
-            //if token is in path then ignore the new token variable
-            if (token) {
-                path = WebUtil.injectParamIfMissing(path, "token", token);
-            }
-
             if ((!host) || (!port)) {
-                UI.showStatus("Must set host and port", 'error');
+                var msg = _("Must set host and port");
+                Util.Error(msg);
+                UI.showStatus(msg, 'error');
                 return;
             }
 
@@ -995,8 +1095,9 @@ var UI;
                 }, 100);
 
             if (typeof msg === 'undefined') {
-                msg = "Password is required";
+                msg = _("Password is required");
             }
+            Util.Warn(msg);
             UI.showStatus(msg, "warning");
         },
 
@@ -1081,11 +1182,6 @@ var UI;
                     // is finished we wait 0.5 seconds before sending the request.
                     clearTimeout(UI.resizeTimeout);
                     UI.resizeTimeout = setTimeout(function(){
-
-                        // Limit the viewport to the size of the browser window
-                        display.set_maxWidth(screen.w);
-                        display.set_maxHeight(screen.h);
-
                         // Request a remote size covering the viewport
                         if (UI.rfb.requestDesktopSize(screen.w, screen.h)) {
                             Util.Debug('Requested new desktop size: ' +
@@ -1105,8 +1201,7 @@ var UI;
             }
         },
 
-        // The screen is always the same size as the available viewport
-        // in the browser window minus the height of the control bar
+        // Gets the the size of the available viewport in the browser window
         screenSize: function() {
             var screen = document.getElementById('noVNC_screen');
 
@@ -1167,27 +1262,7 @@ var UI;
             if (new_clip && size) {
                 // When clipping is enabled, the screen is limited to
                 // the size of the browser window.
-                display.set_maxWidth(size.w);
-                display.set_maxHeight(size.h);
-
-                var screen = document.getElementById('noVNC_screen');
-                var canvas = document.getElementById('noVNC_canvas');
-
-                // Hide potential scrollbars that can skew the position
-                screen.style.overflow = "hidden";
-
-                // The x position marks the left margin of the canvas,
-                // remove the margin from both sides to keep it centered.
-                var new_w = size.w - (2 * Util.getPosition(canvas).x);
-
-                screen.style.overflow = "visible";
-
-                display.viewportChangeSize(new_w, size.h);
-            } else {
-                // Disable max dimensions
-                display.set_maxWidth(0);
-                display.set_maxHeight(0);
-                display.viewportChangeSize();
+                display.viewportChangeSize(size.w, size.h);
             }
         },
 
@@ -1208,19 +1283,24 @@ var UI;
                 // The browser is IE and we are in fullscreen mode.
                 // - We need to force clipping while in fullscreen since
                 //   scrollbars doesn't work.
-                UI.showStatus("Forcing clipping mode since scrollbars aren't supported by IE in fullscreen");
+                var msg = _("Forcing clipping mode since " +
+                            "scrollbars aren't supported " +
+                            "by IE in fullscreen");
+                Util.Debug(msg);
+                UI.showStatus(msg);
                 UI.rememberedClipSetting = UI.getSetting('clip');
                 UI.setViewClip(true);
                 document.getElementById('noVNC_setting_clip').disabled = true;
-            } else if (document.body.msRequestFullscreen && UI.rememberedClip !== null) {
+            } else if (document.body.msRequestFullscreen &&
+                       UI.rememberedClipSetting !== null) {
                 // Restore view clip to what it was before fullscreen on IE
                 UI.setViewClip(UI.rememberedClipSetting);
                 document.getElementById('noVNC_setting_clip').disabled =
-                    UI.connected || UI.isTouchDevice;
+                    UI.connected || Util.isTouchDevice;
             } else {
                 document.getElementById('noVNC_setting_clip').disabled =
-                    UI.connected || UI.isTouchDevice;
-                if (UI.isTouchDevice) {
+                    UI.connected || Util.isTouchDevice;
+                if (Util.isTouchDevice) {
                     UI.setViewClip(true);
                 }
             }
@@ -1278,7 +1358,7 @@ var UI;
 
             // Different behaviour for touch vs non-touch
             // The button is disabled instead of hidden on touch devices
-            if (UI.isTouchDevice) {
+            if (Util.isTouchDevice) {
                 viewDragButton.classList.remove("noVNC_hidden");
 
                 if (clipping) {
@@ -1304,15 +1384,12 @@ var UI;
  * ------v------*/
 
         showVirtualKeyboard: function() {
-            if (!UI.isTouchDevice) return;
+            if (!Util.isTouchDevice) return;
 
             var input = document.getElementById('noVNC_keyboardinput');
 
             if (document.activeElement == input) return;
 
-            UI.keyboardVisible = true;
-            document.getElementById('noVNC_keyboard_button')
-                .classList.add("noVNC_selected");
             input.focus();
 
             try {
@@ -1323,7 +1400,7 @@ var UI;
         },
 
         hideVirtualKeyboard: function() {
-            if (!UI.isTouchDevice) return;
+            if (!Util.isTouchDevice) return;
 
             var input = document.getElementById('noVNC_keyboardinput');
 
@@ -1333,31 +1410,44 @@ var UI;
         },
 
         toggleVirtualKeyboard: function () {
-            if (UI.keyboardVisible) {
+            if (document.getElementById('noVNC_keyboard_button')
+                .classList.contains("noVNC_selected")) {
                 UI.hideVirtualKeyboard();
             } else {
                 UI.showVirtualKeyboard();
             }
         },
 
-        onblurVirtualKeyboard: function() {
-            //Weird bug in iOS if you change keyboardVisible
-            //here it does not actually occur so next time
-            //you click keyboard icon it doesnt work.
-            UI.hideKeyboardTimeout = setTimeout(function() {
-                UI.keyboardVisible = false;
-                document.getElementById('noVNC_keyboard_button')
-                       .classList.remove("noVNC_selected");
-            },100);
+        onfocusVirtualKeyboard: function(event) {
+            document.getElementById('noVNC_keyboard_button')
+                .classList.add("noVNC_selected");
         },
 
-        keepKeyboard: function() {
-            clearTimeout(UI.hideKeyboardTimeout);
-            if(UI.keyboardVisible === true) {
-                UI.showVirtualKeyboard();
-            } else if(UI.keyboardVisible === false) {
-                UI.hideVirtualKeyboard();
+        onblurVirtualKeyboard: function(event) {
+            document.getElementById('noVNC_keyboard_button')
+                .classList.remove("noVNC_selected");
+        },
+
+        keepVirtualKeyboard: function(event) {
+            var input = document.getElementById('noVNC_keyboardinput');
+
+            // Only prevent focus change if the virtual keyboard is active
+            if (document.activeElement != input) {
+                return;
+            }
+
+            // Allow clicking on links
+            if (event.target.tagName === "A") {
+                return;
             }
+
+            // And form elements, except standard noVNC buttons
+            if ((event.target.form !== undefined) &&
+                !event.target.classList.contains("noVNC_button")) {
+                return;
+            }
+
+            event.preventDefault();
         },
 
         keyboardinputReset: function() {
@@ -1416,7 +1506,7 @@ var UI;
                 UI.rfb.sendKey(KeyTable.XK_BackSpace);
             }
             for (i = newLen - inputs; i < newLen; i++) {
-                UI.rfb.sendKey(newValue.charCodeAt(i));
+                UI.rfb.sendKey(keysyms.fromUnicode(newValue.charCodeAt(i)).keysym);
             }
 
             // Control the text content length in the keyboardinput element
@@ -1431,7 +1521,7 @@ var UI;
                 // text has been added to the field
                 event.target.blur();
                 // This has to be ran outside of the input handler in order to work
-                setTimeout(UI.keepKeyboard, 0);
+                setTimeout(event.target.focus.bind(event.target), 0);
             } else {
                 UI.lastKeyboardinput = newValue;
             }
@@ -1461,7 +1551,6 @@ var UI;
         },
 
         toggleExtraKeys: function() {
-            UI.keepKeyboard();
             if(document.getElementById('noVNC_modifiers')
                 .classList.contains("noVNC_open")) {
                 UI.closeExtraKeys();
@@ -1471,17 +1560,14 @@ var UI;
         },
 
         sendEsc: function() {
-            UI.keepKeyboard();
             UI.rfb.sendKey(KeyTable.XK_Escape);
         },
 
         sendTab: function() {
-            UI.keepKeyboard();
             UI.rfb.sendKey(KeyTable.XK_Tab);
         },
 
         toggleCtrl: function() {
-            UI.keepKeyboard();
             var btn = document.getElementById('noVNC_toggle_ctrl_button');
             if (btn.classList.contains("noVNC_selected")) {
                 UI.rfb.sendKey(KeyTable.XK_Control_L, false);
@@ -1493,7 +1579,6 @@ var UI;
         },
 
         toggleAlt: function() {
-            UI.keepKeyboard();
             var btn = document.getElementById('noVNC_toggle_alt_button');
             if (btn.classList.contains("noVNC_selected")) {
                 UI.rfb.sendKey(KeyTable.XK_Alt_L, false);
@@ -1505,7 +1590,6 @@ var UI;
         },
 
         sendCtrlAltDel: function() {
-            UI.keepKeyboard();
             UI.rfb.sendCtrlAltDel();
         },
 
@@ -1516,14 +1600,16 @@ var UI;
  * ------v------*/
 
         setMouseButton: function(num) {
-            if (UI.rfb && !UI.rfb.get_view_only()) {
+            var view_only = UI.rfb.get_view_only();
+            if (UI.rfb && !view_only) {
                 UI.rfb.get_mouse().set_touchButton(num);
             }
 
             var blist = [0, 1,2,4];
             for (var b = 0; b < blist.length; b++) {
-                var button = document.getElementById('noVNC_mouse_button' + blist[b]);
-                if (blist[b] === num) {
+                var button = document.getElementById('noVNC_mouse_button' +
+                                                     blist[b]);
+                if (blist[b] === num && !view_only) {
                     button.classList.remove("noVNC_hidden");
                 } else {
                     button.classList.add("noVNC_hidden");
@@ -1545,6 +1631,11 @@ var UI;
             }
         },
 
+        updateSessionSize: function(rfb, width, height) {
+            UI.updateViewClip();
+            UI.updateViewDrag();
+        },
+
         updateDesktopName: function(rfb, name) {
             UI.desktopName = name;
             // Display the desktop name in the document title