systemvm: update novnc v1.2.0 (#4323)

Update noVNC v1.2.0, add support for clipboard, explicit button toolbar and resize screensize
diff --git a/server/src/main/java/com/cloud/servlet/ConsoleProxyServlet.java b/server/src/main/java/com/cloud/servlet/ConsoleProxyServlet.java
index ed73625..b735be8 100644
--- a/server/src/main/java/com/cloud/servlet/ConsoleProxyServlet.java
+++ b/server/src/main/java/com/cloud/servlet/ConsoleProxyServlet.java
@@ -484,7 +484,7 @@
         if (param.getHypervHost() != null || !ConsoleProxyManager.NoVncConsoleDefault.value()) {
             sb.append("/ajax?token=" + encryptor.encryptObject(ConsoleProxyClientParam.class, param));
         } else {
-            sb.append("/resource/noVNC/vnc_lite.html?port=" + ConsoleProxyManager.DEFAULT_NOVNC_PORT + "&token="
+            sb.append("/resource/noVNC/vnc.html?port=" + ConsoleProxyManager.DEFAULT_NOVNC_PORT + "&token="
                 + encryptor.encryptObject(ConsoleProxyClientParam.class, param));
         }
 
diff --git a/systemvm/agent/noVNC/.eslintignore b/systemvm/agent/noVNC/.eslintignore
deleted file mode 100644
index d381628..0000000
--- a/systemvm/agent/noVNC/.eslintignore
+++ /dev/null
@@ -1 +0,0 @@
-**/xtscancodes.js
diff --git a/systemvm/agent/noVNC/.eslintrc b/systemvm/agent/noVNC/.eslintrc
deleted file mode 100644
index 900a718..0000000
--- a/systemvm/agent/noVNC/.eslintrc
+++ /dev/null
@@ -1,48 +0,0 @@
-{
-    "env": {
-        "browser": true,
-        "es6": true
-    },
-    "parserOptions": {
-        "sourceType": "module"
-    },
-    "extends": "eslint:recommended",
-    "rules": {
-        // Unsafe or confusing stuff that we forbid
-
-        "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 }],
-        "prefer-arrow-callback": "error",
-        "arrow-body-style": ["error", "as-needed", { "requireReturnForObjectLiteral": false } ],
-        "arrow-parens": ["error", "as-needed", { "requireForBlockBody": true }],
-        "arrow-spacing": ["error"],
-        "no-confusing-arrow": ["error", { "allowParens": true }],
-
-        // Enforced coding style
-
-        "brace-style": ["error", "1tbs", { "allowSingleLine": true }],
-        "indent": ["error", 4, { "SwitchCase": 1,
-                                 "CallExpression": { "arguments": "first" },
-                                 "ArrayExpression": "first",
-                                 "ObjectExpression": "first",
-                                 "ignoreComments": true }],
-        "comma-spacing": ["error"],
-        "comma-style": ["error"],
-        "curly": ["error", "multi-line"],
-        "func-call-spacing": ["error"],
-        "func-names": ["error"],
-        "func-style": ["error", "declaration", { "allowArrowFunctions": true }],
-        "key-spacing": ["error"],
-        "keyword-spacing": ["error"],
-        "no-trailing-spaces": ["error"],
-        "semi": ["error"],
-        "space-before-blocks": ["error"],
-        "space-before-function-paren": ["error", { "anonymous": "always",
-                                                   "named": "never",
-                                                   "asyncArrow": "always" }],
-        "switch-colon-spacing": ["error"],
-    }
-}
diff --git a/systemvm/agent/noVNC/.github/ISSUE_TEMPLATE/bug_report.md b/systemvm/agent/noVNC/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index 94ac6f8..0000000
--- a/systemvm/agent/noVNC/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,34 +0,0 @@
----
-name: Bug report
-about: Create a report to help us improve
-
----
-
-**Describe the bug**
-A clear and concise description of what the bug is.
-
-**To Reproduce**
-Steps to reproduce the behavior:
-1. Go to '...'
-2. Click on '....'
-3. Scroll down to '....'
-4. See error
-
-**Expected behavior**
-A clear and concise description of what you expected to happen.
-
-**Screenshots**
-If applicable, add screenshots to help explain your problem.
-
-**Client (please complete the following information):**
- - OS: [e.g. iOS]
- - Browser: [e.g. chrome, safari]
- - Browser version: [e.g. 22]
-
-**Server (please complete the following information):**
- - noVNC version: [e.g. 1.0.0 or git commit id]
- - VNC server: [e.g. QEMU, TigerVNC]
- - WebSocket proxy: [e.g. websockify]
-
-**Additional context**
-Add any other context about the problem here.
diff --git a/systemvm/agent/noVNC/.github/ISSUE_TEMPLATE/feature_request.md b/systemvm/agent/noVNC/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index 066b2d9..0000000
--- a/systemvm/agent/noVNC/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,17 +0,0 @@
----
-name: Feature request
-about: Suggest an idea for this project
-
----
-
-**Is your feature request related to a problem? Please describe.**
-A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
-
-**Describe the solution you'd like**
-A clear and concise description of what you want to happen.
-
-**Describe alternatives you've considered**
-A clear and concise description of any alternative solutions or features you've considered.
-
-**Additional context**
-Add any other context or screenshots about the feature request here.
diff --git a/systemvm/agent/noVNC/.gitignore b/systemvm/agent/noVNC/.gitignore
deleted file mode 100644
index c178dba..0000000
--- a/systemvm/agent/noVNC/.gitignore
+++ /dev/null
@@ -1,12 +0,0 @@
-*.pyc
-*.o
-tests/data_*.js
-utils/rebind.so
-utils/websockify
-/node_modules
-/build
-/lib
-recordings
-*.swp
-*~
-noVNC-*.tgz
diff --git a/systemvm/agent/noVNC/.gitmodules b/systemvm/agent/noVNC/.gitmodules
deleted file mode 100644
index e69de29..0000000
--- a/systemvm/agent/noVNC/.gitmodules
+++ /dev/null
diff --git a/systemvm/agent/noVNC/.travis.yml b/systemvm/agent/noVNC/.travis.yml
deleted file mode 100644
index 78b521a..0000000
--- a/systemvm/agent/noVNC/.travis.yml
+++ /dev/null
@@ -1,58 +0,0 @@
-language: node_js
-sudo: false
-cache:
-  directories:
-  - node_modules
-node_js:
- - 6
-env:
-  matrix:
-  - TEST_BROWSER_NAME=chrome TEST_BROWSER_OS='Windows 10'
-# FIXME Skip tests in Linux since Sauce Labs browser versions are ancient.
-#  - TEST_BROWSER_NAME=chrome TEST_BROWSER_OS='Linux'
-  - TEST_BROWSER_NAME=chrome TEST_BROWSER_OS='OS X 10.11'
-  - TEST_BROWSER_NAME=firefox TEST_BROWSER_OS='Windows 10'
-#  - TEST_BROWSER_NAME=firefox TEST_BROWSER_OS='Linux'
-  - TEST_BROWSER_NAME=firefox TEST_BROWSER_OS='OS X 10.11'
-  - TEST_BROWSER_NAME='internet explorer' TEST_BROWSER_OS='Windows 10'
-  - TEST_BROWSER_NAME='internet explorer' TEST_BROWSER_OS='Windows 7'
-  - TEST_BROWSER_NAME=microsoftedge TEST_BROWSER_OS='Windows 10'
-  - TEST_BROWSER_NAME=safari TEST_BROWSER_OS='OS X 10.13'
-before_script: npm install -g karma-cli
-addons:
-  sauce_connect:
-    username: "directxman12"
-  jwt:
-    secure: "d3ekMYslpn6R4f0ajtRMt9SUFmNGDiItHpqaXC5T4KI0KMEsxgvEOfJot5PiFFJWg1DSpJZH6oaW2UxGZ3duJLZrXIEd/JePY8a6NtT35BNgiDPgcp+eu2Bu3rhrSNg7/HEsD1ma+JeUTnv18Ai5oMFfCCQJx2J6osIxyl/ZVxA="
-stages:
-- lint
-- test
-- name: deploy
-  if: tag is PRESENT
-jobs:
-  include:
-  - stage: lint
-    env:
-    addons:
-    before_script:
-    script: npm run lint
-  -
-    env:
-    addons:
-    before_script:
-    script: git ls-tree --name-only -r HEAD | grep -E "[.](html|css)$" | xargs ./utils/validate
-  - stage: deploy
-    env:
-    addons:
-    script: skip
-    before_script: skip
-    deploy:
-      provider: npm
-      email: ossman@cendio.se
-      api_key:
-        secure: "Qq2Mi9xQawO2zlAigzshzMu2QMHvu1IaN9l0ZIivE99wHJj7eS5f4miJ9wB+/mWRRgb3E8uj9ZRV24+Oc36drlBTU9sz+lHhH0uFMfAIseceK64wZV9sLAZm472fmPp2xdUeTCCqPaRy7g1XBqiJ0LyZvEFLsRijqcLjPBF+b8w="
-      on:
-        tags: true
-        repo: novnc/noVNC
-
-
diff --git a/systemvm/agent/noVNC/AUTHORS b/systemvm/agent/noVNC/AUTHORS
deleted file mode 100644
index dec0e89..0000000
--- a/systemvm/agent/noVNC/AUTHORS
+++ /dev/null
@@ -1,13 +0,0 @@
-maintainers:
-- Joel Martin (@kanaka)
-- Solly Ross (@directxman12)
-- Samuel Mannehed for Cendio AB (@samhed)
-- Pierre Ossman for Cendio AB (@CendioOssman)
-maintainersEmeritus:
-- @astrand 
-contributors:
-# There are a bunch of people that should be here.
-# If you want to be on this list, feel free send a PR
-# to add yourself.
-- jalf <git@jalf.dk>
-- NTT corp.
diff --git a/systemvm/agent/noVNC/LICENSE.txt b/systemvm/agent/noVNC/LICENSE.txt
deleted file mode 100644
index 20f3eb0..0000000
--- a/systemvm/agent/noVNC/LICENSE.txt
+++ /dev/null
@@ -1,68 +0,0 @@
-noVNC is Copyright (C) 2018 The noVNC Authors
-(./AUTHORS)
-
-The noVNC core library files are licensed under the MPL 2.0 (Mozilla
-Public License 2.0). The noVNC core library is composed of the
-Javascript code necessary for full noVNC operation. This includes (but
-is not limited to):
-
-    core/**/*.js
-    app/*.js
-    test/playback.js
-
-The HTML, CSS, font and images files that included with the noVNC
-source distibution (or repository) are not considered part of the
-noVNC core library and are licensed under more permissive licenses.
-The intent is to allow easy integration of noVNC into existing web
-sites and web applications.
-
-The HTML, CSS, font and image files are licensed as follows:
-
-    *.html                     : 2-Clause BSD license
-
-    app/styles/*.css           : 2-Clause BSD license
-
-    app/styles/Orbitron*       : SIL Open Font License 1.1
-                                 (Copyright 2009 Matt McInerney)
-
-    app/images/                : Creative Commons Attribution-ShareAlike
-                                 http://creativecommons.org/licenses/by-sa/3.0/
-
-Some portions of noVNC are copyright to their individual authors.
-Please refer to the individual source files and/or to the noVNC commit
-history: https://github.com/novnc/noVNC/commits/master
-
-The are several files and projects that have been incorporated into
-the noVNC core library. Here is a list of those files and the original
-licenses (all MPL 2.0 compatible):
-
-    core/base64.js          : MPL 2.0
-
-    core/des.js             : Various BSD style licenses
-
-    vendor/pako/            : MIT
-
-    vendor/browser-es-module-loader/src/ : MIT
-
-    vendor/browser-es-module-loader/dist/ : Various BSD style licenses
-
-    vendor/promise.js       : MIT
-
-Any other files not mentioned above are typically marked with
-a copyright/license header at the top of the file. The default noVNC
-license is MPL-2.0.
-
-The following license texts are included:
-
-    docs/LICENSE.MPL-2.0
-    docs/LICENSE.OFL-1.1
-    docs/LICENSE.BSD-3-Clause (New BSD)
-    docs/LICENSE.BSD-2-Clause (Simplified BSD / FreeBSD)
-    vendor/pako/LICENSE (MIT)
-
-Or alternatively the license texts may be found here:
-
-    http://www.mozilla.org/MPL/2.0/
-    http://scripts.sil.org/OFL
-    http://en.wikipedia.org/wiki/BSD_licenses
-    https://opensource.org/licenses/MIT
diff --git a/systemvm/agent/noVNC/README.md b/systemvm/agent/noVNC/README.md
deleted file mode 100644
index 566b8e4..0000000
--- a/systemvm/agent/noVNC/README.md
+++ /dev/null
@@ -1,152 +0,0 @@
-## noVNC: HTML VNC Client Library and Application
-
-[![Build Status](https://travis-ci.org/novnc/noVNC.svg?branch=master)](https://travis-ci.org/novnc/noVNC)
-
-### Description
-
-noVNC is both a HTML VNC client JavaScript library and an application built on
-top of that library. noVNC runs well in any modern browser including mobile
-browsers (iOS and Android).
-
-Many companies, projects and products have integrated noVNC including
-[OpenStack](http://www.openstack.org),
-[OpenNebula](http://opennebula.org/),
-[LibVNCServer](http://libvncserver.sourceforge.net), and
-[ThinLinc](https://cendio.com/thinlinc). See
-[the Projects and Companies wiki page](https://github.com/novnc/noVNC/wiki/Projects-and-companies-using-noVNC)
-for a more complete list with additional info and links.
-
-### Table of Contents
-
-- [News/help/contact](#newshelpcontact)
-- [Features](#features)
-- [Screenshots](#screenshots)
-- [Browser Requirements](#browser-requirements)
-- [Server Requirements](#server-requirements)
-- [Quick Start](#quick-start)
-- [Integration and Deployment](#integration-and-deployment)
-- [Authors/Contributors](#authorscontributors)
-
-### News/help/contact
-
-The project website is found at [novnc.com](http://novnc.com).
-Notable commits, announcements and news are posted to
-[@noVNC](http://www.twitter.com/noVNC).
-
-If you are a noVNC developer/integrator/user (or want to be) please join the
-[noVNC discussion group](https://groups.google.com/forum/?fromgroups#!forum/novnc).
-
-Bugs and feature requests can be submitted via
-[github issues](https://github.com/novnc/noVNC/issues). If you have questions
-about using noVNC then please first use the
-[discussion group](https://groups.google.com/forum/?fromgroups#!forum/novnc).
-We also have a [wiki](https://github.com/novnc/noVNC/wiki/) with lots of
-helpful information.
-
-If you are looking for a place to start contributing to noVNC, a good place to
-start would be the issues that are marked as
-["patchwelcome"](https://github.com/novnc/noVNC/issues?labels=patchwelcome).
-Please check our
-[contribution guide](https://github.com/novnc/noVNC/wiki/Contributing) though.
-
-If you want to show appreciation for noVNC you could donate to a great non-
-profits such as:
-[Compassion International](http://www.compassion.com/),
-[SIL](http://www.sil.org),
-[Habitat for Humanity](http://www.habitat.org),
-[Electronic Frontier Foundation](https://www.eff.org/),
-[Against Malaria Foundation](http://www.againstmalaria.com/),
-[Nothing But Nets](http://www.nothingbutnets.net/), etc.
-Please tweet [@noVNC](http://www.twitter.com/noVNC) if you do.
-
-
-### Features
-
-* Supports all modern browsers including mobile (iOS, Android)
-* Supported VNC encodings: raw, copyrect, rre, hextile, tight, tightPNG
-* Supports scaling, clipping and resizing the desktop
-* Local cursor rendering
-* Clipboard copy/paste
-* Translations
-* Licensed mainly under the [MPL 2.0](http://www.mozilla.org/MPL/2.0/), see
-  [the license document](LICENSE.txt) for details
-
-### Screenshots
-
-Running in Firefox before and after connecting:
-
-<img src="http://novnc.com/img/noVNC-1-login.png" width=400>&nbsp;
-<img src="http://novnc.com/img/noVNC-3-connected.png" width=400>
-
-See more screenshots
-[here](http://novnc.com/screenshots.html).
-
-
-### Browser Requirements
-
-noVNC uses many modern web technologies so a formal requirement list is
-not available. However these are the minimum versions we are currently
-aware of:
-
-* Chrome 49, Firefox 44, Safari 10, Opera 36, IE 11, Edge 12
-
-
-### Server Requirements
-
-noVNC follows the standard VNC protocol, but unlike other VNC clients it does
-require WebSockets support. Many servers include support (e.g.
-[x11vnc/libvncserver](http://libvncserver.sourceforge.net/),
-[QEMU](http://www.qemu.org/), and
-[MobileVNC](http://www.smartlab.at/mobilevnc/)), but for the others you need to
-use a WebSockets to TCP socket proxy. noVNC has a sister project
-[websockify](https://github.com/novnc/websockify) that provides a simple such
-proxy.
-
-
-### Quick Start
-
-* Use the launch script to automatically download and start websockify, which
-  includes a mini-webserver and the WebSockets proxy. The `--vnc` option is
-  used to specify the location of a running VNC server:
-
-    `./utils/launch.sh --vnc localhost:5901`
-
-* Point your browser to the cut-and-paste URL that is output by the launch
-  script. Hit the Connect button, enter a password if the VNC server has one
-  configured, and enjoy!
-
-
-### Integration and Deployment
-
-Please see our other documents for how to integrate noVNC in your own software,
-or deploying the noVNC application in production environments:
-
-* [Embedding](docs/EMBEDDING.md) - For the noVNC application
-* [Library](docs/LIBRARY.md) - For the noVNC JavaScript library
-
-
-### Authors/Contributors
-
-See [AUTHORS](AUTHORS) for a (full-ish) list of authors.  If you're not on
-that list and you think you should be, feel free to send a PR to fix that.
-
-* Core team:
-    * [Joel Martin](https://github.com/kanaka)
-    * [Samuel Mannehed](https://github.com/samhed) (Cendio)
-    * [Peter Åstrand](https://github.com/astrand) (Cendio)
-    * [Solly Ross](https://github.com/DirectXMan12) (Red Hat / OpenStack)
-    * [Pierre Ossman](https://github.com/CendioOssman) (Cendio)
-
-* Notable contributions:
-    * UI and Icons : Pierre Ossman, Chris Gordon
-    * Original Logo : Michael Sersen
-    * tight encoding : Michael Tinglof (Mercuri.ca)
-
-* Included libraries:
-    * base64 : Martijn Pieters (Digital Creations 2), Samuel Sieb (sieb.net)
-    * DES : Dave Zimmerman (Widget Workshop), Jef Poskanzer (ACME Labs)
-    * Pako : Vitaly Puzrin (https://github.com/nodeca/pako)
-
-Do you want to be on this list? Check out our
-[contribution guide](https://github.com/novnc/noVNC/wiki/Contributing) and
-start hacking!
diff --git a/systemvm/agent/noVNC/VERSION b/systemvm/agent/noVNC/VERSION
deleted file mode 100644
index 9084fa2..0000000
--- a/systemvm/agent/noVNC/VERSION
+++ /dev/null
@@ -1 +0,0 @@
-1.1.0
diff --git a/systemvm/agent/noVNC/app/error-handler.js b/systemvm/agent/noVNC/app/error-handler.js
index 8e29416..81a6cba 100644
--- a/systemvm/agent/noVNC/app/error-handler.js
+++ b/systemvm/agent/noVNC/app/error-handler.js
@@ -1,3 +1,11 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2019 The noVNC Authors
+ * Licensed under MPL 2.0 (see LICENSE.txt)
+ *
+ * See README.md for usage and integration instructions.
+ */
+
 // NB: this should *not* be included as a module until we have
 // native support in the browsers, so that our error handler
 // can catch script-loading errors.
diff --git a/systemvm/agent/noVNC/app/images/alt.png b/systemvm/agent/noVNC/app/images/alt.png
new file mode 100644
index 0000000..2d5e35e
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/alt.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/alt.svg b/systemvm/agent/noVNC/app/images/alt.svg
deleted file mode 100644
index e5bb461..0000000
--- a/systemvm/agent/noVNC/app/images/alt.svg
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="alt.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="16"
-     inkscape:cx="18.205425"
-     inkscape:cy="17.531398"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="true"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="true"
-     inkscape:snap-global="true">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <g
-       style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:48px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'Sans Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="text5290">
-      <path
-         d="m 9.9560547,1042.3329 -2.9394531,0 -0.4638672,1.3281 -1.8896485,0 2.7001953,-7.29 2.241211,0 2.7001958,7.29 -1.889649,0 -0.4589843,-1.3281 z m -2.4707031,-1.3526 1.9970703,0 -0.9960938,-2.9003 -1.0009765,2.9003 z"
-         style="font-size:10px;fill:#ffffff;fill-opacity:1"
-         id="path5340" />
-      <path
-         d="m 13.188477,1036.0634 1.748046,0 0,7.5976 -1.748046,0 0,-7.5976 z"
-         style="font-size:10px;fill:#ffffff;fill-opacity:1"
-         id="path5342" />
-      <path
-         d="m 18.535156,1036.6395 0,1.5528 1.801758,0 0,1.25 -1.801758,0 0,2.3193 q 0,0.3809 0.151367,0.5176 0.151368,0.1318 0.600586,0.1318 l 0.898438,0 0,1.25 -1.499024,0 q -1.035156,0 -1.469726,-0.4297 -0.429688,-0.4345 -0.429688,-1.4697 l 0,-2.3193 -0.86914,0 0,-1.25 0.86914,0 0,-1.5528 1.748047,0 z"
-         style="font-size:10px;fill:#ffffff;fill-opacity:1"
-         id="path5344" />
-    </g>
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/clipboard.png b/systemvm/agent/noVNC/app/images/clipboard.png
new file mode 100644
index 0000000..d7fe507
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/clipboard.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/clipboard.svg b/systemvm/agent/noVNC/app/images/clipboard.svg
deleted file mode 100644
index 79af275..0000000
--- a/systemvm/agent/noVNC/app/images/clipboard.svg
+++ /dev/null
@@ -1,106 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="clipboard.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="1"
-     inkscape:cx="15.366606"
-     inkscape:cy="16.42981"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="true"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="true"
-     inkscape:snap-global="true">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <path
-       style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       d="M 9,6 6,6 C 5.4459889,6 5,6.4459889 5,7 l 0,13 c 0,0.554011 0.4459889,1 1,1 l 13,0 c 0.554011,0 1,-0.445989 1,-1 L 20,7 C 20,6.4459889 19.554011,6 19,6 l -3,0"
-       transform="translate(0,1027.3622)"
-       id="rect6083"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cssssssssc" />
-    <rect
-       style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       id="rect6085"
-       width="7"
-       height="4"
-       x="9"
-       y="1031.3622"
-       ry="1.00002" />
-    <path
-       style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.50196081"
-       d="m 8.5071212,1038.8622 7.9999998,0"
-       id="path6087"
-       inkscape:connector-curvature="0" />
-    <path
-       style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.50196081"
-       d="m 8.5071212,1041.8622 3.9999998,0"
-       id="path6089"
-       inkscape:connector-curvature="0" />
-    <path
-       style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.50196081"
-       d="m 8.5071212,1044.8622 5.9999998,0"
-       id="path6091"
-       inkscape:connector-curvature="0" />
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/connect.png b/systemvm/agent/noVNC/app/images/connect.png
new file mode 100644
index 0000000..abdbe42
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/connect.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/connect.svg b/systemvm/agent/noVNC/app/images/connect.svg
deleted file mode 100644
index 56cde41..0000000
--- a/systemvm/agent/noVNC/app/images/connect.svg
+++ /dev/null
@@ -1,96 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="connect.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="1"
-     inkscape:cx="37.14834"
-     inkscape:cy="1.9525926"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="true"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="true">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <g
-       id="g5103"
-       transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,-729.15757,315.8823)">
-      <path
-         sodipodi:nodetypes="cssssc"
-         inkscape:connector-curvature="0"
-         id="rect5096"
-         d="m 11,1040.3622 -5,0 c -1.108,0 -2,-0.892 -2,-2 l 0,-4 c 0,-1.108 0.892,-2 2,-2 l 5,0"
-         style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
-      <path
-         style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-         d="m 14,1032.3622 5,0 c 1.108,0 2,0.892 2,2 l 0,4 c 0,1.108 -0.892,2 -2,2 l -5,0"
-         id="path5099"
-         inkscape:connector-curvature="0"
-         sodipodi:nodetypes="cssssc" />
-      <path
-         inkscape:connector-curvature="0"
-         id="path5101"
-         d="m 9,1036.3622 7,0"
-         style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
-    </g>
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/ctrl.png b/systemvm/agent/noVNC/app/images/ctrl.png
new file mode 100644
index 0000000..fbc9e13
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/ctrl.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/ctrl.svg b/systemvm/agent/noVNC/app/images/ctrl.svg
deleted file mode 100644
index 856e939..0000000
--- a/systemvm/agent/noVNC/app/images/ctrl.svg
+++ /dev/null
@@ -1,96 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="ctrl.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="16"
-     inkscape:cx="18.205425"
-     inkscape:cy="17.531398"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="true"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="true"
-     inkscape:snap-global="true">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <g
-       style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:48px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'Sans Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="text5290">
-      <path
-         d="m 9.1210938,1043.1898 q -0.5175782,0.2686 -1.0791016,0.4053 -0.5615235,0.1367 -1.171875,0.1367 -1.8212891,0 -2.8857422,-1.0156 -1.0644531,-1.0205 -1.0644531,-2.7637 0,-1.748 1.0644531,-2.7637 1.0644531,-1.0205 2.8857422,-1.0205 0.6103515,0 1.171875,0.1368 0.5615234,0.1367 1.0791016,0.4052 l 0,1.5088 q -0.522461,-0.3564 -1.0302735,-0.5224 -0.5078125,-0.1661 -1.0693359,-0.1661 -1.0058594,0 -1.5820313,0.6446 -0.5761719,0.6445 -0.5761719,1.7773 0,1.1279 0.5761719,1.7725 0.5761719,0.6445 1.5820313,0.6445 0.5615234,0 1.0693359,-0.166 0.5078125,-0.166 1.0302735,-0.5225 l 0,1.5088 z"
-         style="font-size:10px;fill:#ffffff;fill-opacity:1"
-         id="path5370" />
-      <path
-         d="m 12.514648,1036.5687 0,1.5528 1.801758,0 0,1.25 -1.801758,0 0,2.3193 q 0,0.3809 0.151368,0.5176 0.151367,0.1318 0.600586,0.1318 l 0.898437,0 0,1.25 -1.499023,0 q -1.035157,0 -1.469727,-0.4297 -0.429687,-0.4345 -0.429687,-1.4697 l 0,-2.3193 -0.8691411,0 0,-1.25 0.8691411,0 0,-1.5528 1.748046,0 z"
-         style="font-size:10px;fill:#ffffff;fill-opacity:1"
-         id="path5372" />
-      <path
-         d="m 19.453125,1039.6107 q -0.229492,-0.1074 -0.458984,-0.1562 -0.22461,-0.054 -0.454102,-0.054 -0.673828,0 -1.040039,0.4345 -0.361328,0.4297 -0.361328,1.2354 l 0,2.5195 -1.748047,0 0,-5.4687 1.748047,0 0,0.8984 q 0.336914,-0.5371 0.771484,-0.7813 0.439453,-0.249 1.049805,-0.249 0.08789,0 0.19043,0.01 0.102539,0 0.297851,0.029 l 0.0049,1.582 z"
-         style="font-size:10px;fill:#ffffff;fill-opacity:1"
-         id="path5374" />
-      <path
-         d="m 20.332031,1035.9926 1.748047,0 0,7.5976 -1.748047,0 0,-7.5976 z"
-         style="font-size:10px;fill:#ffffff;fill-opacity:1"
-         id="path5376" />
-    </g>
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/ctrlaltdel.png b/systemvm/agent/noVNC/app/images/ctrlaltdel.png
new file mode 100644
index 0000000..dd04978
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/ctrlaltdel.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/ctrlaltdel.svg b/systemvm/agent/noVNC/app/images/ctrlaltdel.svg
deleted file mode 100644
index d7744ea..0000000
--- a/systemvm/agent/noVNC/app/images/ctrlaltdel.svg
+++ /dev/null
@@ -1,100 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="ctrlaltdel.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="8"
-     inkscape:cx="11.135667"
-     inkscape:cy="16.407428"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="true"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="true"
-     inkscape:snap-global="true">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <rect
-       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       id="rect5253"
-       width="5"
-       height="5.0000172"
-       x="16"
-       y="1031.3622"
-       ry="1.0000174" />
-    <rect
-       y="1043.3622"
-       x="4"
-       height="5.0000172"
-       width="5"
-       id="rect5255"
-       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       ry="1.0000174" />
-    <rect
-       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       id="rect5257"
-       width="5"
-       height="5.0000172"
-       x="13"
-       y="1043.3622"
-       ry="1.0000174" />
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/disconnect.png b/systemvm/agent/noVNC/app/images/disconnect.png
new file mode 100644
index 0000000..97eb1a9
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/disconnect.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/disconnect.svg b/systemvm/agent/noVNC/app/images/disconnect.svg
deleted file mode 100644
index 6be7d18..0000000
--- a/systemvm/agent/noVNC/app/images/disconnect.svg
+++ /dev/null
@@ -1,94 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="disconnect.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="16"
-     inkscape:cx="25.05707"
-     inkscape:cy="11.594858"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="true"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="true"
-     inkscape:snap-global="false">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <g
-       id="g5171"
-       transform="translate(-24.062499,-6.15775e-4)">
-      <path
-         id="path5110"
-         transform="translate(0,1027.3622)"
-         d="m 39.744141,3.4960938 c -0.769923,0 -1.539607,0.2915468 -2.121094,0.8730468 l -2.566406,2.5664063 1.414062,1.4140625 2.566406,-2.5664063 c 0.403974,-0.404 1.010089,-0.404 1.414063,0 l 2.828125,2.828125 c 0.40398,0.4039 0.403907,1.0101621 0,1.4140629 l -2.566406,2.566406 1.414062,1.414062 2.566406,-2.566406 c 1.163041,-1.1629 1.162968,-3.0791874 0,-4.2421874 L 41.865234,4.3691406 C 41.283747,3.7876406 40.514063,3.4960937 39.744141,3.4960938 Z M 39.017578,9.015625 a 1.0001,1.0001 0 0 0 -0.6875,0.3027344 l -0.445312,0.4453125 1.414062,1.4140621 0.445313,-0.445312 A 1.0001,1.0001 0 0 0 39.017578,9.015625 Z m -6.363281,0.7070312 a 1.0001,1.0001 0 0 0 -0.6875,0.3027348 L 28.431641,13.5625 c -1.163042,1.163 -1.16297,3.079187 0,4.242188 l 2.828125,2.828124 c 1.162974,1.163101 3.079213,1.163101 4.242187,0 l 3.535156,-3.535156 a 1.0001,1.0001 0 1 0 -1.414062,-1.414062 l -3.535156,3.535156 c -0.403974,0.404 -1.010089,0.404 -1.414063,0 l -2.828125,-2.828125 c -0.403981,-0.404 -0.403908,-1.010162 0,-1.414063 l 3.535156,-3.537109 A 1.0001,1.0001 0 0 0 32.654297,9.7226562 Z m 3.109375,2.1621098 -2.382813,2.384765 a 1.0001,1.0001 0 1 0 1.414063,1.414063 l 2.382812,-2.384766 -1.414062,-1.414062 z"
-         style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-         inkscape:connector-curvature="0" />
-      <rect
-         transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,0,0)"
-         y="752.29541"
-         x="-712.31262"
-         height="18.000017"
-         width="3"
-         id="rect5116"
-         style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
-    </g>
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/drag.png b/systemvm/agent/noVNC/app/images/drag.png
new file mode 100644
index 0000000..f006202
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/drag.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/drag.svg b/systemvm/agent/noVNC/app/images/drag.svg
deleted file mode 100644
index 139caf9..0000000
--- a/systemvm/agent/noVNC/app/images/drag.svg
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="drag.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="22.627417"
-     inkscape:cx="9.8789407"
-     inkscape:cy="9.5008608"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="true"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="false"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <path
-       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       d="m 7.039733,1049.3037 c -0.4309106,-0.1233 -0.7932634,-0.4631 -0.9705434,-0.9103 -0.04922,-0.1241 -0.057118,-0.2988 -0.071321,-1.5771 l -0.015972,-1.4375 -0.328125,-0.082 c -0.7668138,-0.1927 -1.1897046,-0.4275 -1.7031253,-0.9457 -0.4586773,-0.4629 -0.6804297,-0.8433 -0.867034,-1.4875 -0.067215,-0.232 -0.068001,-0.2642 -0.078682,-3.2188 -0.012078,-3.341 -0.020337,-3.2012 0.2099452,-3.5555 0.2246623,-0.3458 0.5798271,-0.5892 0.9667343,-0.6626 0.092506,-0.017 0.531898,-0.032 0.9764271,-0.032 l 0.8082347,0 1.157e-4,1.336 c 1.125e-4,1.2779 0.00281,1.3403 0.062214,1.4378 0.091785,0.1505 0.2357707,0.226 0.4314082,0.2261 0.285389,2e-4 0.454884,-0.1352 0.5058962,-0.4042 0.019355,-0.102 0.031616,-0.982 0.031616,-2.269 0,-1.9756 0.00357,-2.1138 0.059205,-2.2926 0.1645475,-0.5287 0.6307616,-0.9246 1.19078,-1.0113 0.8000572,-0.1238 1.5711277,0.4446 1.6860387,1.2429 0.01732,0.1203 0.03177,0.8248 0.03211,1.5657 6.19e-4,1.3449 7.22e-4,1.347 0.07093,1.4499 0.108355,0.1587 0.255268,0.2248 0.46917,0.2108 0.204069,-0.013 0.316116,-0.08 0.413642,-0.2453 0.06028,-0.1024 0.06307,-0.1778 0.07862,-2.1218 0.01462,-1.8283 0.02124,-2.0285 0.07121,-2.1549 0.260673,-0.659 0.934894,-1.0527 1.621129,-0.9465 0.640523,0.099 1.152269,0.6104 1.243187,1.2421 0.01827,0.1269 0.03175,0.9943 0.03211,2.0657 l 6.19e-4,1.8469 0.07031,0.103 c 0.108355,0.1587 0.255267,0.2248 0.46917,0.2108 0.204069,-0.013 0.316115,-0.08 0.413642,-0.2453 0.05951,-0.1011 0.06329,-0.1786 0.07907,-1.6218 0.01469,-1.3438 0.02277,-1.5314 0.07121,-1.6549 0.257975,-0.6576 0.934425,-1.0527 1.620676,-0.9465 0.640522,0.099 1.152269,0.6104 1.243186,1.2421 0.0186,0.1292 0.03179,1.0759 0.03222,2.3125 7.15e-4,2.0335 0.0025,2.0966 0.06283,2.1956 0.09178,0.1505 0.235771,0.226 0.431409,0.2261 0.285388,2e-4 0.454884,-0.1352 0.505897,-0.4042 0.01874,-0.099 0.03161,-0.8192 0.03161,-1.769 0,-1.4848 0.0043,-1.6163 0.0592,-1.7926 0.164548,-0.5287 0.630762,-0.9246 1.19078,-1.0113 0.800057,-0.1238 1.571128,0.4446 1.686039,1.2429 0.04318,0.2999 0.04372,9.1764 5.78e-4,9.4531 -0.04431,0.2841 -0.217814,0.6241 -0.420069,0.8232 -0.320102,0.315 -0.63307,0.4268 -1.194973,0.4268 l -0.35281,0 -2.51e-4,1.2734 c -1.25e-4,0.7046 -0.01439,1.3642 -0.03191,1.4766 -0.06665,0.4274 -0.372966,0.8704 -0.740031,1.0702 -0.349999,0.1905 0.01748,0.18 -6.242199,0.1776 -5.3622439,0 -5.7320152,-0.01 -5.9121592,-0.057 l 1.4e-5,0 z"
-       id="path4379"
-       inkscape:connector-curvature="0" />
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/error.png b/systemvm/agent/noVNC/app/images/error.png
new file mode 100644
index 0000000..04e78e1
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/error.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/error.svg b/systemvm/agent/noVNC/app/images/error.svg
deleted file mode 100644
index 8356d3f..0000000
--- a/systemvm/agent/noVNC/app/images/error.svg
+++ /dev/null
@@ -1,81 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="error.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="1"
-     inkscape:cx="14.00357"
-     inkscape:cy="12.443398"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="true"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="true"
-     inkscape:snap-global="true">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <path
-       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       d="M 7 3 C 4.7839905 3 3 4.7839905 3 7 L 3 18 C 3 20.21601 4.7839905 22 7 22 L 18 22 C 20.21601 22 22 20.21601 22 18 L 22 7 C 22 4.7839905 20.21601 3 18 3 L 7 3 z M 7.6992188 6 A 1.6916875 1.6924297 0 0 1 8.9121094 6.5117188 L 12.5 10.101562 L 16.087891 6.5117188 A 1.6916875 1.6924297 0 0 1 17.251953 6 A 1.6916875 1.6924297 0 0 1 18.480469 8.90625 L 14.892578 12.496094 L 18.480469 16.085938 A 1.6916875 1.6924297 0 1 1 16.087891 18.478516 L 12.5 14.888672 L 8.9121094 18.478516 A 1.6916875 1.6924297 0 1 1 6.5214844 16.085938 L 10.109375 12.496094 L 6.5214844 8.90625 A 1.6916875 1.6924297 0 0 1 7.6992188 6 z "
-       transform="translate(0,1027.3622)"
-       id="rect4135" />
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/esc.png b/systemvm/agent/noVNC/app/images/esc.png
new file mode 100644
index 0000000..e1b1bfa
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/esc.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/esc.svg b/systemvm/agent/noVNC/app/images/esc.svg
deleted file mode 100644
index 830152b..0000000
--- a/systemvm/agent/noVNC/app/images/esc.svg
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="esc.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="16"
-     inkscape:cx="18.205425"
-     inkscape:cy="17.531398"
-     inkscape:document-units="px"
-     inkscape:current-layer="text5290"
-     showgrid="false"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="true"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="true"
-     inkscape:snap-global="true">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <g
-       style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:48px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'Sans Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="text5290">
-      <path
-         d="m 3.9331055,1036.1464 5.0732422,0 0,1.4209 -3.1933594,0 0,1.3574 3.0029297,0 0,1.4209 -3.0029297,0 0,1.6699 3.3007812,0 0,1.4209 -5.180664,0 0,-7.29 z"
-         style="font-size:10px;fill:#ffffff;fill-opacity:1"
-         id="path5314" />
-      <path
-         d="m 14.963379,1038.1385 0,1.3282 q -0.561524,-0.2344 -1.083984,-0.3516 -0.522461,-0.1172 -0.986329,-0.1172 -0.498046,0 -0.742187,0.127 -0.239258,0.122 -0.239258,0.3808 0,0.21 0.180664,0.3223 0.185547,0.1123 0.65918,0.166 l 0.307617,0.044 q 1.342773,0.1709 1.806641,0.5615 0.463867,0.3906 0.463867,1.2256 0,0.874 -0.644531,1.3134 -0.644532,0.4395 -1.923829,0.4395 -0.541992,0 -1.123046,-0.088 -0.576172,-0.083 -1.186524,-0.2539 l 0,-1.3281 q 0.522461,0.2539 1.069336,0.3808 0.551758,0.127 1.118164,0.127 0.512695,0 0.771485,-0.1416 0.258789,-0.1416 0.258789,-0.4199 0,-0.2344 -0.180664,-0.3467 -0.175782,-0.1172 -0.708008,-0.1807 l -0.307617,-0.039 q -1.166993,-0.1465 -1.635743,-0.542 -0.46875,-0.3955 -0.46875,-1.2012 0,-0.8691 0.595703,-1.2891 0.595704,-0.4199 1.826172,-0.4199 0.483399,0 1.015625,0.073 0.532227,0.073 1.157227,0.2294 z"
-         style="font-size:10px;fill:#ffffff;fill-opacity:1"
-         id="path5316" />
-      <path
-         d="m 21.066895,1038.1385 0,1.4258 q -0.356446,-0.2441 -0.717774,-0.3613 -0.356445,-0.1172 -0.742187,-0.1172 -0.732422,0 -1.142579,0.4297 -0.405273,0.4248 -0.405273,1.1914 0,0.7666 0.405273,1.1963 0.410157,0.4248 1.142579,0.4248 0.410156,0 0.776367,-0.1221 0.371094,-0.122 0.683594,-0.3613 l 0,1.4307 q -0.410157,0.1513 -0.834961,0.2246 -0.419922,0.078 -0.844727,0.078 -1.479492,0 -2.314453,-0.7568 -0.834961,-0.7618 -0.834961,-2.1143 0,-1.3525 0.834961,-2.1094 0.834961,-0.7617 2.314453,-0.7617 0.429688,0 0.844727,0.078 0.419921,0.073 0.834961,0.2246 z"
-         style="font-size:10px;fill:#ffffff;fill-opacity:1"
-         id="path5318" />
-    </g>
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/expander.png b/systemvm/agent/noVNC/app/images/expander.png
new file mode 100644
index 0000000..2793721
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/expander.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/expander.svg b/systemvm/agent/noVNC/app/images/expander.svg
deleted file mode 100644
index e163535..0000000
--- a/systemvm/agent/noVNC/app/images/expander.svg
+++ /dev/null
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="9"
-   height="10"
-   viewBox="0 0 9 10"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="expander.svg">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#ffffff"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0.0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="45.254834"
-     inkscape:cx="9.8737281"
-     inkscape:cy="6.4583132"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="true"
-     units="px"
-     inkscape:snap-object-midpoints="false"
-     inkscape:object-nodes="true"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="0"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1042.3622)">
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="M 2.0800781,1042.3633 A 2.0002,2.0002 0 0 0 0,1044.3613 l 0,6 a 2.0002,2.0002 0 0 0 3.0292969,1.7168 l 5,-3 a 2.0002,2.0002 0 0 0 0,-3.4316 l -5,-3 a 2.0002,2.0002 0 0 0 -0.9492188,-0.2832 z"
-       id="path4138"
-       inkscape:connector-curvature="0" />
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/fullscreen.png b/systemvm/agent/noVNC/app/images/fullscreen.png
new file mode 100644
index 0000000..a7f2634
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/fullscreen.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/fullscreen.svg b/systemvm/agent/noVNC/app/images/fullscreen.svg
deleted file mode 100644
index 29bd05d..0000000
--- a/systemvm/agent/noVNC/app/images/fullscreen.svg
+++ /dev/null
@@ -1,93 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="fullscreen.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="1"
-     inkscape:cx="16.400723"
-     inkscape:cy="15.083758"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="false"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="false">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <rect
-       style="opacity:1;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       id="rect5006"
-       width="17"
-       height="17.000017"
-       x="4"
-       y="1031.3622"
-       ry="3.0000174" />
-    <path
-       style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1"
-       d="m 7.5,1044.8622 4,0 -1.5,-1.5 1.5,-1.5 -1,-1 -1.5,1.5 -1.5,-1.5 0,4 z"
-       id="path5017"
-       inkscape:connector-curvature="0" />
-    <path
-       inkscape:connector-curvature="0"
-       id="path5025"
-       d="m 17.5,1034.8622 -4,0 1.5,1.5 -1.5,1.5 1,1 1.5,-1.5 1.5,1.5 0,-4 z"
-       style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/handle.png b/systemvm/agent/noVNC/app/images/handle.png
new file mode 100644
index 0000000..cf0e5d5
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/handle.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/handle.svg b/systemvm/agent/noVNC/app/images/handle.svg
deleted file mode 100644
index 4a7a126..0000000
--- a/systemvm/agent/noVNC/app/images/handle.svg
+++ /dev/null
@@ -1,82 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="5"
-   height="6"
-   viewBox="0 0 5 6"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="handle.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="32"
-     inkscape:cx="1.3551778"
-     inkscape:cy="8.7800329"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="true"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="false"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="true"
-     inkscape:snap-global="true">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1046.3622)">
-    <path
-       style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       d="m 4.0000803,1049.3622 -3,-2 0,4 z"
-       id="path4247"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccc" />
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/handle_bg.png b/systemvm/agent/noVNC/app/images/handle_bg.png
new file mode 100644
index 0000000..efb0357
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/handle_bg.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/handle_bg.svg b/systemvm/agent/noVNC/app/images/handle_bg.svg
deleted file mode 100644
index 7579c42..0000000
--- a/systemvm/agent/noVNC/app/images/handle_bg.svg
+++ /dev/null
@@ -1,172 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="15"
-   height="50"
-   viewBox="0 0 15 50"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="handle_bg.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="16"
-     inkscape:cx="-10.001409"
-     inkscape:cy="24.512566"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="true"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="false"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="true"
-     inkscape:snap-global="true">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1002.3622)">
-    <rect
-       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       id="rect4249"
-       width="1"
-       height="1.0000174"
-       x="9.5"
-       y="1008.8622"
-       ry="1.7382812e-05" />
-    <rect
-       ry="1.7382812e-05"
-       y="1013.8622"
-       x="9.5"
-       height="1.0000174"
-       width="1"
-       id="rect4255"
-       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
-    <rect
-       ry="1.7382812e-05"
-       y="1008.8622"
-       x="4.5"
-       height="1.0000174"
-       width="1"
-       id="rect4261"
-       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
-    <rect
-       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       id="rect4263"
-       width="1"
-       height="1.0000174"
-       x="4.5"
-       y="1013.8622"
-       ry="1.7382812e-05" />
-    <rect
-       ry="1.7382812e-05"
-       y="1039.8622"
-       x="9.5"
-       height="1.0000174"
-       width="1"
-       id="rect4265"
-       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
-    <rect
-       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       id="rect4267"
-       width="1"
-       height="1.0000174"
-       x="9.5"
-       y="1044.8622"
-       ry="1.7382812e-05" />
-    <rect
-       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       id="rect4269"
-       width="1"
-       height="1.0000174"
-       x="4.5"
-       y="1039.8622"
-       ry="1.7382812e-05" />
-    <rect
-       ry="1.7382812e-05"
-       y="1044.8622"
-       x="4.5"
-       height="1.0000174"
-       width="1"
-       id="rect4271"
-       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
-    <rect
-       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       id="rect4273"
-       width="1"
-       height="1.0000174"
-       x="9.5"
-       y="1018.8622"
-       ry="1.7382812e-05" />
-    <rect
-       ry="1.7382812e-05"
-       y="1018.8622"
-       x="4.5"
-       height="1.0000174"
-       width="1"
-       id="rect4275"
-       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
-    <rect
-       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       id="rect4277"
-       width="1"
-       height="1.0000174"
-       x="9.5"
-       y="1034.8622"
-       ry="1.7382812e-05" />
-    <rect
-       ry="1.7382812e-05"
-       y="1034.8622"
-       x="4.5"
-       height="1.0000174"
-       width="1"
-       id="rect4279"
-       style="opacity:0.25;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1" />
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/info.png b/systemvm/agent/noVNC/app/images/info.png
new file mode 100644
index 0000000..21e3589
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/info.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/info.svg b/systemvm/agent/noVNC/app/images/info.svg
deleted file mode 100644
index 557b772..0000000
--- a/systemvm/agent/noVNC/app/images/info.svg
+++ /dev/null
@@ -1,81 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="info.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="1"
-     inkscape:cx="15.720838"
-     inkscape:cy="8.9111233"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="false"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="true"
-     inkscape:snap-global="true">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <path
-       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       d="M 12.5 3 A 9.5 9.4999914 0 0 0 3 12.5 A 9.5 9.4999914 0 0 0 12.5 22 A 9.5 9.4999914 0 0 0 22 12.5 A 9.5 9.4999914 0 0 0 12.5 3 z M 12.5 5 A 1.5 1.5000087 0 0 1 14 6.5 A 1.5 1.5000087 0 0 1 12.5 8 A 1.5 1.5000087 0 0 1 11 6.5 A 1.5 1.5000087 0 0 1 12.5 5 z M 10.521484 8.9785156 L 12.521484 8.9785156 A 1.50015 1.50015 0 0 1 14.021484 10.478516 L 14.021484 15.972656 A 1.50015 1.50015 0 0 1 14.498047 18.894531 C 14.498047 18.894531 13.74301 19.228309 12.789062 18.912109 C 12.312092 18.754109 11.776235 18.366625 11.458984 17.828125 C 11.141734 17.289525 11.021484 16.668469 11.021484 15.980469 L 11.021484 11.980469 L 10.521484 11.980469 A 1.50015 1.50015 0 1 1 10.521484 8.9804688 L 10.521484 8.9785156 z "
-       transform="translate(0,1027.3622)"
-       id="path4136" />
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/keyboard.png b/systemvm/agent/noVNC/app/images/keyboard.png
new file mode 100644
index 0000000..fe8056b
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/keyboard.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/keyboard.svg b/systemvm/agent/noVNC/app/images/keyboard.svg
deleted file mode 100644
index 137b350..0000000
--- a/systemvm/agent/noVNC/app/images/keyboard.svg
+++ /dev/null
@@ -1,88 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="keyboard.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/keyboard.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#717171"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="1"
-     inkscape:cx="31.285341"
-     inkscape:cy="8.8028469"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:snap-bbox-midpoints="false"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:object-paths="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-midpoints="true"
-     inkscape:snap-smooth-nodes="true">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title />
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="M 7,3 C 4.8012876,3 3,4.8013 3,7 3,11.166667 3,15.333333 3,19.5 3,20.8764 4.1236413,22 5.5,22 l 14,0 C 20.876358,22 22,20.8764 22,19.5 22,15.333333 22,11.166667 22,7 22,4.8013 20.198712,3 18,3 Z m 0,2 11,0 c 1.125307,0 2,0.8747 2,2 L 20,12 5,12 5,7 C 5,5.8747 5.8746931,5 7,5 Z M 6.5,14 C 6.777,14 7,14.223 7,14.5 7,14.777 6.777,15 6.5,15 6.223,15 6,14.777 6,14.5 6,14.223 6.223,14 6.5,14 Z m 2,0 C 8.777,14 9,14.223 9,14.5 9,14.777 8.777,15 8.5,15 8.223,15 8,14.777 8,14.5 8,14.223 8.223,14 8.5,14 Z m 2,0 C 10.777,14 11,14.223 11,14.5 11,14.777 10.777,15 10.5,15 10.223,15 10,14.777 10,14.5 10,14.223 10.223,14 10.5,14 Z m 2,0 C 12.777,14 13,14.223 13,14.5 13,14.777 12.777,15 12.5,15 12.223,15 12,14.777 12,14.5 12,14.223 12.223,14 12.5,14 Z m 2,0 C 14.777,14 15,14.223 15,14.5 15,14.777 14.777,15 14.5,15 14.223,15 14,14.777 14,14.5 14,14.223 14.223,14 14.5,14 Z m 2,0 C 16.777,14 17,14.223 17,14.5 17,14.777 16.777,15 16.5,15 16.223,15 16,14.777 16,14.5 16,14.223 16.223,14 16.5,14 Z m 2,0 C 18.777,14 19,14.223 19,14.5 19,14.777 18.777,15 18.5,15 18.223,15 18,14.777 18,14.5 18,14.223 18.223,14 18.5,14 Z m -13,2 C 5.777,16 6,16.223 6,16.5 6,16.777 5.777,17 5.5,17 5.223,17 5,16.777 5,16.5 5,16.223 5.223,16 5.5,16 Z m 2,0 C 7.777,16 8,16.223 8,16.5 8,16.777 7.777,17 7.5,17 7.223,17 7,16.777 7,16.5 7,16.223 7.223,16 7.5,16 Z m 2,0 C 9.777,16 10,16.223 10,16.5 10,16.777 9.777,17 9.5,17 9.223,17 9,16.777 9,16.5 9,16.223 9.223,16 9.5,16 Z m 2,0 C 11.777,16 12,16.223 12,16.5 12,16.777 11.777,17 11.5,17 11.223,17 11,16.777 11,16.5 11,16.223 11.223,16 11.5,16 Z m 2,0 C 13.777,16 14,16.223 14,16.5 14,16.777 13.777,17 13.5,17 13.223,17 13,16.777 13,16.5 13,16.223 13.223,16 13.5,16 Z m 2,0 C 15.777,16 16,16.223 16,16.5 16,16.777 15.777,17 15.5,17 15.223,17 15,16.777 15,16.5 15,16.223 15.223,16 15.5,16 Z m 2,0 C 17.777,16 18,16.223 18,16.5 18,16.777 17.777,17 17.5,17 17.223,17 17,16.777 17,16.5 17,16.223 17.223,16 17.5,16 Z m 2,0 C 19.777,16 20,16.223 20,16.5 20,16.777 19.777,17 19.5,17 19.223,17 19,16.777 19,16.5 19,16.223 19.223,16 19.5,16 Z M 6,18 c 0.554,0 1,0.446 1,1 0,0.554 -0.446,1 -1,1 -0.554,0 -1,-0.446 -1,-1 0,-0.554 0.446,-1 1,-1 z m 2.8261719,0 7.3476561,0 C 16.631643,18 17,18.368372 17,18.826172 l 0,0.347656 C 17,19.631628 16.631643,20 16.173828,20 L 8.8261719,20 C 8.3683573,20 8,19.631628 8,19.173828 L 8,18.826172 C 8,18.368372 8.3683573,18 8.8261719,18 Z m 10.1113281,0 0.125,0 C 19.581551,18 20,18.4184 20,18.9375 l 0,0.125 C 20,19.5816 19.581551,20 19.0625,20 l -0.125,0 C 18.418449,20 18,19.5816 18,19.0625 l 0,-0.125 C 18,18.4184 18.418449,18 18.9375,18 Z"
-       transform="translate(0,1027.3622)"
-       id="rect4160"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="sccssccsssssccssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssssss" />
-    <path
-       style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:1"
-       d="m 12.499929,1033.8622 -2,2 1.500071,0 0,2 1,0 0,-2 1.499929,0 z"
-       id="path4150"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cccccccc" />
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/mouse_left.svg b/systemvm/agent/noVNC/app/images/mouse_left.svg
deleted file mode 100644
index ce4cca4..0000000
--- a/systemvm/agent/noVNC/app/images/mouse_left.svg
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="mouse_left.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="11.313708"
-     inkscape:cx="15.551515"
-     inkscape:cy="12.205592"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="true"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="true"
-     inkscape:snap-global="true">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0068f6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 8,1030.3622 c -2.1987124,0 -4,1.8013 -4,4 l 0,2 5,0 0,-2 c 0,-1.4738 1.090393,-2.7071 2.5,-2.9492 l 0,-1.0508 -3.5,0 z"
-       id="path6219" />
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 13.5,1030.3622 0,1.0508 c 1.409607,0.2421 2.5,1.4754 2.5,2.9492 l 0,2 5,0 0,-2 c 0,-2.1987 -1.801288,-4 -4,-4 l -3.5,0 z"
-       id="path6217" />
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 12,1033.3622 c -0.571311,0 -1,0.4287 -1,1 l 0,5 c 0,0.5713 0.428689,1 1,1 l 1,0 c 0.571311,0 1,-0.4287 1,-1 l 0,-5 c 0,-0.5713 -0.428689,-1 -1,-1 l -1,0 z"
-       id="path6215" />
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 4,1038.3622 0,3.5 c 0,4.1377 3.362302,7.5 7.5,7.5 l 2,0 c 4.137698,0 7.5,-3.3623 7.5,-7.5 l 0,-3.5 -5,0 0,1 c 0,1.6447 -1.355293,3 -3,3 l -1,0 c -1.644707,0 -3,-1.3553 -3,-3 l 0,-1 -5,0 z"
-       id="rect6178" />
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/mouse_middle.svg b/systemvm/agent/noVNC/app/images/mouse_middle.svg
deleted file mode 100644
index 6603425..0000000
--- a/systemvm/agent/noVNC/app/images/mouse_middle.svg
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="mouse_middle.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="11.313708"
-     inkscape:cx="15.551515"
-     inkscape:cy="12.205592"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="true"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="true"
-     inkscape:snap-global="true">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 8,1030.3622 c -2.1987124,0 -4,1.8013 -4,4 l 0,2 5,0 0,-2 c 0,-1.4738 1.090393,-2.7071 2.5,-2.9492 l 0,-1.0508 -3.5,0 z"
-       id="path6219" />
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 13.5,1030.3622 0,1.0508 c 1.409607,0.2421 2.5,1.4754 2.5,2.9492 l 0,2 5,0 0,-2 c 0,-2.1987 -1.801288,-4 -4,-4 l -3.5,0 z"
-       id="path6217" />
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0068f6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 12,1033.3622 c -0.571311,0 -1,0.4287 -1,1 l 0,5 c 0,0.5713 0.428689,1 1,1 l 1,0 c 0.571311,0 1,-0.4287 1,-1 l 0,-5 c 0,-0.5713 -0.428689,-1 -1,-1 l -1,0 z"
-       id="path6215" />
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 4,1038.3622 0,3.5 c 0,4.1377 3.362302,7.5 7.5,7.5 l 2,0 c 4.137698,0 7.5,-3.3623 7.5,-7.5 l 0,-3.5 -5,0 0,1 c 0,1.6447 -1.355293,3 -3,3 l -1,0 c -1.644707,0 -3,-1.3553 -3,-3 l 0,-1 -5,0 z"
-       id="rect6178" />
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/mouse_none.svg b/systemvm/agent/noVNC/app/images/mouse_none.svg
deleted file mode 100644
index 3e0f838..0000000
--- a/systemvm/agent/noVNC/app/images/mouse_none.svg
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="mouse_none.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="16"
-     inkscape:cx="23.160825"
-     inkscape:cy="13.208262"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="true"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="true"
-     inkscape:snap-global="true">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 8,1030.3622 c -2.1987124,0 -4,1.8013 -4,4 l 0,2 5,0 0,-2 c 0,-1.4738 1.090393,-2.7071 2.5,-2.9492 l 0,-1.0508 -3.5,0 z"
-       id="path6219" />
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 13.5,1030.3622 0,1.0508 c 1.409607,0.2421 2.5,1.4754 2.5,2.9492 l 0,2 5,0 0,-2 c 0,-2.1987 -1.801288,-4 -4,-4 l -3.5,0 z"
-       id="path6217" />
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 12,1033.3622 c -0.571311,0 -1,0.4287 -1,1 l 0,5 c 0,0.5713 0.428689,1 1,1 l 1,0 c 0.571311,0 1,-0.4287 1,-1 l 0,-5 c 0,-0.5713 -0.428689,-1 -1,-1 l -1,0 z"
-       id="path6215" />
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 4,1038.3622 0,3.5 c 0,4.1377 3.362302,7.5 7.5,7.5 l 2,0 c 4.137698,0 7.5,-3.3623 7.5,-7.5 l 0,-3.5 -5,0 0,1 c 0,1.6447 -1.355293,3 -3,3 l -1,0 c -1.644707,0 -3,-1.3553 -3,-3 l 0,-1 -5,0 z"
-       id="rect6178" />
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/mouse_right.svg b/systemvm/agent/noVNC/app/images/mouse_right.svg
deleted file mode 100644
index f4bad76..0000000
--- a/systemvm/agent/noVNC/app/images/mouse_right.svg
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="mouse_right.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="11.313708"
-     inkscape:cx="15.551515"
-     inkscape:cy="12.205592"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="true"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="true"
-     inkscape:snap-global="true">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 8,1030.3622 c -2.1987124,0 -4,1.8013 -4,4 l 0,2 5,0 0,-2 c 0,-1.4738 1.090393,-2.7071 2.5,-2.9492 l 0,-1.0508 -3.5,0 z"
-       id="path6219" />
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#0068f6;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 13.5,1030.3622 0,1.0508 c 1.409607,0.2421 2.5,1.4754 2.5,2.9492 l 0,2 5,0 0,-2 c 0,-2.1987 -1.801288,-4 -4,-4 l -3.5,0 z"
-       id="path6217" />
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 12,1033.3622 c -0.571311,0 -1,0.4287 -1,1 l 0,5 c 0,0.5713 0.428689,1 1,1 l 1,0 c 0.571311,0 1,-0.4287 1,-1 l 0,-5 c 0,-0.5713 -0.428689,-1 -1,-1 l -1,0 z"
-       id="path6215" />
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 4,1038.3622 0,3.5 c 0,4.1377 3.362302,7.5 7.5,7.5 l 2,0 c 4.137698,0 7.5,-3.3623 7.5,-7.5 l 0,-3.5 -5,0 0,1 c 0,1.6447 -1.355293,3 -3,3 l -1,0 c -1.644707,0 -3,-1.3553 -3,-3 l 0,-1 -5,0 z"
-       id="rect6178" />
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/power.png b/systemvm/agent/noVNC/app/images/power.png
new file mode 100644
index 0000000..7f87135
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/power.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/power.svg b/systemvm/agent/noVNC/app/images/power.svg
deleted file mode 100644
index 4925d3e..0000000
--- a/systemvm/agent/noVNC/app/images/power.svg
+++ /dev/null
@@ -1,87 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="power.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="1"
-     inkscape:cx="9.3159849"
-     inkscape:cy="13.436208"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="true"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="true"
-     inkscape:snap-global="true">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="M 9 6.8183594 C 6.3418164 8.1213032 4.5 10.849161 4.5 14 C 4.5 18.4065 8.0935666 22 12.5 22 C 16.906433 22 20.5 18.4065 20.5 14 C 20.5 10.849161 18.658184 8.1213032 16 6.8183594 L 16 9.125 C 17.514327 10.211757 18.5 11.984508 18.5 14 C 18.5 17.3256 15.825553 20 12.5 20 C 9.1744469 20 6.5 17.3256 6.5 14 C 6.5 11.984508 7.4856727 10.211757 9 9.125 L 9 6.8183594 z "
-       transform="translate(0,1027.3622)"
-       id="path6140" />
-    <path
-       style="fill:none;fill-rule:evenodd;stroke:#ffffff;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
-       d="m 12.5,1031.8836 0,6.4786"
-       id="path6142"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="cc" />
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/settings.png b/systemvm/agent/noVNC/app/images/settings.png
new file mode 100644
index 0000000..8d0c0d4
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/settings.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/settings.svg b/systemvm/agent/noVNC/app/images/settings.svg
deleted file mode 100644
index dbb2e80..0000000
--- a/systemvm/agent/noVNC/app/images/settings.svg
+++ /dev/null
@@ -1,76 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="settings.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="22.627417"
-     inkscape:cx="14.69683"
-     inkscape:cy="8.8039511"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="true"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="false"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <path
-       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       d="M 11 3 L 11 5.1601562 A 7.5 7.5 0 0 0 8.3671875 6.2460938 L 6.84375 4.7226562 L 4.7226562 6.84375 L 6.2480469 8.3691406 A 7.5 7.5 0 0 0 5.1523438 11 L 3 11 L 3 14 L 5.1601562 14 A 7.5 7.5 0 0 0 6.2460938 16.632812 L 4.7226562 18.15625 L 6.84375 20.277344 L 8.3691406 18.751953 A 7.5 7.5 0 0 0 11 19.847656 L 11 22 L 14 22 L 14 19.839844 A 7.5 7.5 0 0 0 16.632812 18.753906 L 18.15625 20.277344 L 20.277344 18.15625 L 18.751953 16.630859 A 7.5 7.5 0 0 0 19.847656 14 L 22 14 L 22 11 L 19.839844 11 A 7.5 7.5 0 0 0 18.753906 8.3671875 L 20.277344 6.84375 L 18.15625 4.7226562 L 16.630859 6.2480469 A 7.5 7.5 0 0 0 14 5.1523438 L 14 3 L 11 3 z M 12.5 10 A 2.5 2.5 0 0 1 15 12.5 A 2.5 2.5 0 0 1 12.5 15 A 2.5 2.5 0 0 1 10 12.5 A 2.5 2.5 0 0 1 12.5 10 z "
-       transform="translate(0,1027.3622)"
-       id="rect4967" />
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/tab.png b/systemvm/agent/noVNC/app/images/tab.png
new file mode 100644
index 0000000..6adb781
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/tab.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/tab.svg b/systemvm/agent/noVNC/app/images/tab.svg
deleted file mode 100644
index 1ccb322..0000000
--- a/systemvm/agent/noVNC/app/images/tab.svg
+++ /dev/null
@@ -1,86 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="tab.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="16"
-     inkscape:cx="11.67335"
-     inkscape:cy="17.881696"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="true"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="true"
-     inkscape:snap-global="true">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <path
-       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       d="m 3,1031.3622 0,8 2,0 0,-4 0,-4 -2,0 z m 2,4 4,4 0,-3 13,0 0,-2 -13,0 0,-3 -4,4 z"
-       id="rect5194"
-       inkscape:connector-curvature="0" />
-    <path
-       id="path5211"
-       d="m 22,1048.3622 0,-8 -2,0 0,4 0,4 2,0 z m -2,-4 -4,-4 0,3 -13,0 0,2 13,0 0,3 4,-4 z"
-       style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
-       inkscape:connector-curvature="0" />
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/toggleextrakeys.png b/systemvm/agent/noVNC/app/images/toggleextrakeys.png
new file mode 100644
index 0000000..0f80f6d
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/toggleextrakeys.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/toggleextrakeys.svg b/systemvm/agent/noVNC/app/images/toggleextrakeys.svg
deleted file mode 100644
index b578c0d..0000000
--- a/systemvm/agent/noVNC/app/images/toggleextrakeys.svg
+++ /dev/null
@@ -1,90 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="extrakeys.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="1"
-     inkscape:cx="15.234555"
-     inkscape:cy="9.9710826"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="false"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="false">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="m 8,1031.3622 c -2.1987124,0 -4,1.8013 -4,4 l 0,8.9996 c 0,2.1987 1.8012876,4 4,4 l 9,0 c 2.198712,0 4,-1.8013 4,-4 l 0,-8.9996 c 0,-2.1987 -1.801288,-4 -4,-4 z m 0,2 9,0 c 1.125307,0 2,0.8747 2,2 l 0,7.0005 c 0,1.1253 -0.874693,2 -2,2 l -9,0 c -1.1253069,0 -2,-0.8747 -2,-2 l 0,-7.0005 c 0,-1.1253 0.8746931,-2 2,-2 z"
-       id="rect5006"
-       inkscape:connector-curvature="0"
-       sodipodi:nodetypes="ssssssssssssssssss" />
-    <g
-       style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:10px;line-height:125%;font-family:'DejaVu Sans';-inkscape-font-specification:'Sans Bold';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
-       id="text4167"
-       transform="matrix(0.96021948,0,0,0.96021948,0.18921715,41.80659)">
-      <path
-         d="m 14.292969,1040.6791 -2.939453,0 -0.463868,1.3281 -1.889648,0 2.700195,-7.29 2.241211,0 2.700196,7.29 -1.889649,0 -0.458984,-1.3281 z m -2.470703,-1.3526 1.99707,0 -0.996094,-2.9004 -1.000976,2.9004 z"
-         id="path4172"
-         inkscape:connector-curvature="0" />
-    </g>
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/warning.png b/systemvm/agent/noVNC/app/images/warning.png
new file mode 100644
index 0000000..e9dd550
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/warning.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/warning.svg b/systemvm/agent/noVNC/app/images/warning.svg
deleted file mode 100644
index 7114f9b..0000000
--- a/systemvm/agent/noVNC/app/images/warning.svg
+++ /dev/null
@@ -1,81 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Created with Inkscape (http://www.inkscape.org/) -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   width="25"
-   height="25"
-   viewBox="0 0 25 25"
-   id="svg2"
-   version="1.1"
-   inkscape:version="0.91 r13725"
-   sodipodi:docname="warning.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:export-xdpi="90"
-   inkscape:export-ydpi="90">
-  <defs
-     id="defs4" />
-  <sodipodi:namedview
-     id="base"
-     pagecolor="#959595"
-     bordercolor="#666666"
-     borderopacity="1.0"
-     inkscape:pageopacity="0"
-     inkscape:pageshadow="2"
-     inkscape:zoom="1"
-     inkscape:cx="16.457343"
-     inkscape:cy="12.179552"
-     inkscape:document-units="px"
-     inkscape:current-layer="layer1"
-     showgrid="false"
-     units="px"
-     inkscape:snap-bbox="true"
-     inkscape:bbox-paths="true"
-     inkscape:bbox-nodes="true"
-     inkscape:snap-bbox-edge-midpoints="true"
-     inkscape:object-paths="true"
-     showguides="false"
-     inkscape:window-width="1920"
-     inkscape:window-height="1136"
-     inkscape:window-x="1920"
-     inkscape:window-y="27"
-     inkscape:window-maximized="1"
-     inkscape:snap-smooth-nodes="true"
-     inkscape:object-nodes="true"
-     inkscape:snap-intersection-paths="true"
-     inkscape:snap-nodes="true"
-     inkscape:snap-global="true">
-    <inkscape:grid
-       type="xygrid"
-       id="grid4136" />
-  </sodipodi:namedview>
-  <metadata
-     id="metadata7">
-    <rdf:RDF>
-      <cc:Work
-         rdf:about="">
-        <dc:format>image/svg+xml</dc:format>
-        <dc:type
-           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
-        <dc:title></dc:title>
-      </cc:Work>
-    </rdf:RDF>
-  </metadata>
-  <g
-     inkscape:label="Layer 1"
-     inkscape:groupmode="layer"
-     id="layer1"
-     transform="translate(0,-1027.3622)">
-    <path
-       style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:4;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
-       d="M 12.513672 3.0019531 C 11.751609 2.9919531 11.052563 3.4242687 10.710938 4.1054688 L 3.2109375 19.105469 C 2.5461937 20.435369 3.5132277 21.9999 5 22 L 20 22 C 21.486772 21.9999 22.453806 20.435369 21.789062 19.105469 L 14.289062 4.1054688 C 13.951849 3.4330688 13.265888 3.0066531 12.513672 3.0019531 z M 12.478516 6.9804688 A 1.50015 1.50015 0 0 1 14 8.5 L 14 14.5 A 1.50015 1.50015 0 1 1 11 14.5 L 11 8.5 A 1.50015 1.50015 0 0 1 12.478516 6.9804688 z M 12.5 17 A 1.5 1.5 0 0 1 14 18.5 A 1.5 1.5 0 0 1 12.5 20 A 1.5 1.5 0 0 1 11 18.5 A 1.5 1.5 0 0 1 12.5 17 z "
-       transform="translate(0,1027.3622)"
-       id="path4208" />
-  </g>
-</svg>
diff --git a/systemvm/agent/noVNC/app/images/windows.png b/systemvm/agent/noVNC/app/images/windows.png
new file mode 100644
index 0000000..036e1ae
--- /dev/null
+++ b/systemvm/agent/noVNC/app/images/windows.png
Binary files differ
diff --git a/systemvm/agent/noVNC/app/images/windows.svg b/systemvm/agent/noVNC/app/images/windows.svg
deleted file mode 100644
index 270405c..0000000
--- a/systemvm/agent/noVNC/app/images/windows.svg
+++ /dev/null
@@ -1,85 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
-
-<svg
-   xmlns:dc="http://purl.org/dc/elements/1.1/"
-   xmlns:cc="http://creativecommons.org/ns#"
-   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
-   xmlns:svg="http://www.w3.org/2000/svg"
-   xmlns="http://www.w3.org/2000/svg"
-   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
-   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
-   version="1.1"
-   id="svg2"
-   inkscape:export-ydpi="90"
-   inkscape:export-xdpi="90"
-   sodipodi:docname="windows.svg"
-   inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
-   inkscape:version="0.92.3 (2405546, 2018-03-11)"
-   x="0px"
-   y="0px"
-   viewBox="-293 384 25 23"
-   xml:space="preserve"
-   width="25"
-   height="23"><metadata
-   id="metadata21"><rdf:RDF><cc:Work
-       rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
-         rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
-   id="defs19" /><sodipodi:namedview
-   pagecolor="#ffffff"
-   bordercolor="#666666"
-   borderopacity="1"
-   objecttolerance="10"
-   gridtolerance="10"
-   guidetolerance="10"
-   inkscape:pageopacity="0"
-   inkscape:pageshadow="2"
-   inkscape:window-width="1920"
-   inkscape:window-height="1017"
-   id="namedview17"
-   showgrid="false"
-   inkscape:pagecheckerboard="true"
-   inkscape:zoom="9.44"
-   inkscape:cx="-0.84745763"
-   inkscape:cy="12.5"
-   inkscape:window-x="2552"
-   inkscape:window-y="122"
-   inkscape:window-maximized="1"
-   inkscape:current-layer="svg2" />
-<style
-   type="text/css"
-   id="style2">
-	.st0{fill:#FFFFFF;}
-</style>
-<g
-   id="g14"
-   transform="matrix(1.2624869,0,0,1.3601695,73.614445,-144.84322)">
-	<g
-   id="g12">
-		<path
-   class="st0"
-   d="m -277.4,396 c -0.7,0 -1.3,0 -2,0 -0.4,0 -0.5,-0.1 -0.5,-0.5 0,-1 0,-2 0,-3 0,-0.3 0.2,-0.5 0.5,-0.5 1.3,-0.1 2.6,-0.3 3.9,-0.4 0.4,0 0.7,0.1 0.7,0.6 0,1.1 0,2.2 0,3.3 0,0.4 -0.2,0.6 -0.6,0.6 -0.7,-0.1 -1.4,-0.1 -2,-0.1 z"
-   id="path4"
-   inkscape:connector-curvature="0"
-   style="fill:#ffffff" />
-		<path
-   class="st0"
-   d="m -274.9,399.3 c 0,0.6 0,1.1 0,1.7 0,0.4 -0.1,0.6 -0.6,0.6 -1.4,-0.1 -2.8,-0.3 -4.1,-0.4 -0.3,0 -0.4,-0.3 -0.4,-0.5 0,-1 0,-2 0,-3 0,-0.4 0.2,-0.5 0.6,-0.5 1.3,0 2.6,0 3.9,0 0.5,0 0.6,0.2 0.6,0.6 0,0.4 0,0.9 0,1.5 z"
-   id="path6"
-   inkscape:connector-curvature="0"
-   style="fill:#ffffff" />
-		<path
-   class="st0"
-   d="m -283.5,396 c -0.6,0 -1.3,0 -1.9,0 -0.4,0 -0.6,-0.1 -0.6,-0.6 0,-0.8 0,-1.5 0,-2.3 0,-0.4 0.2,-0.6 0.6,-0.7 1.3,-0.1 2.7,-0.3 4,-0.4 0.4,0 0.5,0.1 0.5,0.5 0,1 0,1.9 0,2.9 0,0.4 -0.2,0.5 -0.5,0.5 -0.8,0.1 -1.5,0.1 -2.1,0.1 z"
-   id="path8"
-   inkscape:connector-curvature="0"
-   style="fill:#ffffff" />
-		<path
-   class="st0"
-   d="m -283.5,397 c 0.6,0 1.3,0 1.9,0 0.4,0 0.6,0.1 0.6,0.5 0,1 0,1.9 0,2.9 0,0.4 -0.2,0.5 -0.5,0.5 -1.3,-0.1 -2.7,-0.3 -4,-0.4 -0.4,0 -0.6,-0.2 -0.6,-0.7 0,-0.7 0,-1.5 0,-2.2 0,-0.5 0.2,-0.7 0.7,-0.7 0.6,0.1 1.2,0.1 1.9,0.1 z"
-   id="path10"
-   inkscape:connector-curvature="0"
-   style="fill:#ffffff" />
-	</g>
-</g>
-</svg>
\ No newline at end of file
diff --git a/systemvm/agent/noVNC/app/locale/README b/systemvm/agent/noVNC/app/locale/README
new file mode 100644
index 0000000..ca4f548
--- /dev/null
+++ b/systemvm/agent/noVNC/app/locale/README
@@ -0,0 +1 @@
+DO NOT MODIFY THE FILES IN THIS FOLDER, THEY ARE AUTOMATICALLY GENERATED FROM THE PO-FILES.
diff --git a/systemvm/agent/noVNC/app/locale/ja.json b/systemvm/agent/noVNC/app/locale/ja.json
new file mode 100644
index 0000000..e5fe340
--- /dev/null
+++ b/systemvm/agent/noVNC/app/locale/ja.json
@@ -0,0 +1,73 @@
+{
+    "Connecting...": "接続しています...",
+    "Disconnecting...": "切断しています...",
+    "Reconnecting...": "再接続しています...",
+    "Internal error": "内部エラー",
+    "Must set host": "ホストを設定する必要があります",
+    "Connected (encrypted) to ": "接続しました (暗号化済み): ",
+    "Connected (unencrypted) to ": "接続しました (暗号化されていません): ",
+    "Something went wrong, connection is closed": "何かが問題で、接続が閉じられました",
+    "Failed to connect to server": "サーバーへの接続に失敗しました",
+    "Disconnected": "切断しました",
+    "New connection has been rejected with reason: ": "新規接続は次の理由で拒否されました: ",
+    "New connection has been rejected": "新規接続は拒否されました",
+    "Password is required": "パスワードが必要です",
+    "noVNC encountered an error:": "noVNC でエラーが発生しました:",
+    "Hide/Show the control bar": "コントロールバーを隠す/表示する",
+    "Move/Drag Viewport": "ビューポートを移動/ドラッグ",
+    "viewport drag": "ビューポートをドラッグ",
+    "Active Mouse Button": "アクティブなマウスボタン",
+    "No mousebutton": "マウスボタンなし",
+    "Left mousebutton": "左マウスボタン",
+    "Middle mousebutton": "中マウスボタン",
+    "Right mousebutton": "右マウスボタン",
+    "Keyboard": "キーボード",
+    "Show Keyboard": "キーボードを表示",
+    "Extra keys": "追加キー",
+    "Show Extra Keys": "追加キーを表示",
+    "Ctrl": "Ctrl",
+    "Toggle Ctrl": "Ctrl キーを切り替え",
+    "Alt": "Alt",
+    "Toggle Alt": "Alt キーを切り替え",
+    "Toggle Windows": "Windows キーを切り替え",
+    "Windows": "Windows",
+    "Send Tab": "Tab キーを送信",
+    "Tab": "Tab",
+    "Esc": "Esc",
+    "Send Escape": "Escape キーを送信",
+    "Ctrl+Alt+Del": "Ctrl+Alt+Del",
+    "Send Ctrl-Alt-Del": "Ctrl-Alt-Del を送信",
+    "Shutdown/Reboot": "シャットダウン/再起動",
+    "Shutdown/Reboot...": "シャットダウン/再起動...",
+    "Power": "電源",
+    "Shutdown": "シャットダウン",
+    "Reboot": "再起動",
+    "Reset": "リセット",
+    "Clipboard": "クリップボード",
+    "Clear": "クリア",
+    "Fullscreen": "全画面表示",
+    "Settings": "設定",
+    "Shared Mode": "共有モード",
+    "View Only": "表示のみ",
+    "Clip to Window": "ウィンドウにクリップ",
+    "Scaling Mode:": "スケーリングモード:",
+    "None": "なし",
+    "Local Scaling": "ローカルスケーリング",
+    "Remote Resizing": "リモートでリサイズ",
+    "Advanced": "高度",
+    "Repeater ID:": "リピーター ID:",
+    "WebSocket": "WebSocket",
+    "Encrypt": "暗号化",
+    "Host:": "ホスト:",
+    "Port:": "ポート:",
+    "Path:": "パス:",
+    "Automatic Reconnect": "自動再接続",
+    "Reconnect Delay (ms):": "再接続する遅延 (ミリ秒):",
+    "Show Dot when No Cursor": "カーソルがないときにドットを表示",
+    "Logging:": "ロギング:",
+    "Disconnect": "切断",
+    "Connect": "接続",
+    "Password:": "パスワード:",
+    "Send Password": "パスワードを送信",
+    "Cancel": "キャンセル"
+}
\ No newline at end of file
diff --git a/systemvm/agent/noVNC/app/locale/sv.json b/systemvm/agent/noVNC/app/locale/sv.json
index d49ea54..e46df45 100644
--- a/systemvm/agent/noVNC/app/locale/sv.json
+++ b/systemvm/agent/noVNC/app/locale/sv.json
@@ -11,16 +11,11 @@
     "Disconnected": "Frånkopplad",
     "New connection has been rejected with reason: ": "Ny anslutning har blivit nekad med följande skäl: ",
     "New connection has been rejected": "Ny anslutning har blivit nekad",
-    "Password is required": "Lösenord krävs",
+    "Credentials are required": "Användaruppgifter krävs",
     "noVNC encountered an error:": "noVNC stötte på ett problem:",
     "Hide/Show the control bar": "Göm/Visa kontrollbaren",
+    "Drag": "Dra",
     "Move/Drag Viewport": "Flytta/Dra Vyn",
-    "viewport drag": "dra vy",
-    "Active Mouse Button": "Aktiv musknapp",
-    "No mousebutton": "Ingen musknapp",
-    "Left mousebutton": "Vänster musknapp",
-    "Middle mousebutton": "Mitten-musknapp",
-    "Right mousebutton": "Höger musknapp",
     "Keyboard": "Tangentbord",
     "Show Keyboard": "Visa Tangentbord",
     "Extra keys": "Extraknappar",
@@ -55,6 +50,8 @@
     "Local Scaling": "Lokal Skalning",
     "Remote Resizing": "Ändra Storlek",
     "Advanced": "Avancerat",
+    "Quality:": "Kvalitet:",
+    "Compression level:": "Kompressionsnivå:",
     "Repeater ID:": "Repeater-ID:",
     "WebSocket": "WebSocket",
     "Encrypt": "Kryptera",
@@ -65,9 +62,11 @@
     "Reconnect Delay (ms):": "Fördröjning (ms):",
     "Show Dot when No Cursor": "Visa prick när ingen muspekare finns",
     "Logging:": "Loggning:",
+    "Version:": "Version:",
     "Disconnect": "Koppla från",
     "Connect": "Anslut",
+    "Username:": "Användarnamn:",
     "Password:": "Lösenord:",
-    "Send Password": "Skicka lösenord",
+    "Send Credentials": "Skicka Användaruppgifter",
     "Cancel": "Avbryt"
 }
\ No newline at end of file
diff --git a/systemvm/agent/noVNC/app/locale/zh_CN.json b/systemvm/agent/noVNC/app/locale/zh_CN.json
index b669956..f0aea9a 100644
--- a/systemvm/agent/noVNC/app/locale/zh_CN.json
+++ b/systemvm/agent/noVNC/app/locale/zh_CN.json
@@ -1,19 +1,19 @@
 {
-    "Connecting...": "链接中...",
-    "Disconnecting...": "正在中断连接...",
-    "Reconnecting...": "重新链接中...",
+    "Connecting...": "连接中...",
+    "Disconnecting...": "正在断开连接...",
+    "Reconnecting...": "重新连接中...",
     "Internal error": "内部错误",
     "Must set host": "请提供主机名",
-    "Connected (encrypted) to ": "已加密链接到",
-    "Connected (unencrypted) to ": "未加密链接到",
-    "Something went wrong, connection is closed": "发生错误,链接已关闭",
-    "Failed to connect to server": "无法链接到服务器",
-    "Disconnected": "链接已中断",
-    "New connection has been rejected with reason: ": "链接被拒绝,原因:",
-    "New connection has been rejected": "链接被拒绝",
+    "Connected (encrypted) to ": "已连接到(加密)",
+    "Connected (unencrypted) to ": "已连接到(未加密)",
+    "Something went wrong, connection is closed": "发生错误,连接已关闭",
+    "Failed to connect to server": "无法连接到服务器",
+    "Disconnected": "已断开连接",
+    "New connection has been rejected with reason: ": "连接被拒绝,原因:",
+    "New connection has been rejected": "连接被拒绝",
     "Password is required": "请提供密码",
     "noVNC encountered an error:": "noVNC 遇到一个错误:",
-    "Hide/Show the control bar": "显示/隐藏控制列",
+    "Hide/Show the control bar": "显示/隐藏控制栏",
     "Move/Drag Viewport": "拖放显示范围",
     "viewport drag": "显示范围拖放",
     "Active Mouse Button": "启动鼠标按鍵",
@@ -43,10 +43,10 @@
     "Reset": "重置",
     "Clipboard": "剪贴板",
     "Clear": "清除",
-    "Fullscreen": "全屏幕",
+    "Fullscreen": "全屏",
     "Settings": "设置",
     "Shared Mode": "分享模式",
-    "View Only": "仅检视",
+    "View Only": "仅查看",
     "Clip to Window": "限制/裁切窗口大小",
     "Scaling Mode:": "缩放模式:",
     "None": "无",
@@ -59,11 +59,11 @@
     "Host:": "主机:",
     "Port:": "端口:",
     "Path:": "路径:",
-    "Automatic Reconnect": "自动重新链接",
-    "Reconnect Delay (ms):": "重新链接间隔 (ms):",
+    "Automatic Reconnect": "自动重新连接",
+    "Reconnect Delay (ms):": "重新连接间隔 (ms):",
     "Logging:": "日志级别:",
-    "Disconnect": "终端链接",
-    "Connect": "链接",
+    "Disconnect": "中断连接",
+    "Connect": "连接",
     "Password:": "密码:",
     "Cancel": "取消"
 }
\ No newline at end of file
diff --git a/systemvm/agent/noVNC/app/styles/base.css b/systemvm/agent/noVNC/app/styles/base.css
index 3ca9894..fd78b79 100644
--- a/systemvm/agent/noVNC/app/styles/base.css
+++ b/systemvm/agent/noVNC/app/styles/base.css
@@ -1,6 +1,6 @@
 /*
  * noVNC base CSS
- * Copyright (C) 2018 The noVNC Authors
+ * Copyright (C) 2019 The noVNC Authors
  * noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
  * This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
  */
@@ -83,8 +83,20 @@
  * ----------------------------------------
  */
 
-input[type=input], input[type=password], input[type=number],
-input:not([type]), textarea {
+input:not([type]),
+input[type=date],
+input[type=datetime-local],
+input[type=email],
+input[type=month],
+input[type=number],
+input[type=password],
+input[type=search],
+input[type=tel],
+input[type=text],
+input[type=time],
+input[type=url],
+input[type=week],
+textarea {
   /* Disable default rendering */
   -webkit-appearance: none;
   -moz-appearance: none;
@@ -98,7 +110,11 @@
   background: linear-gradient(to top, rgb(255, 255, 255) 80%, rgb(240, 240, 240));
 }
 
-input[type=button], input[type=submit], select {
+input[type=button],
+input[type=color],
+input[type=reset],
+input[type=submit],
+select {
   /* Disable default rendering */
   -webkit-appearance: none;
   -moz-appearance: none;
@@ -116,7 +132,10 @@
   vertical-align: middle;
 }
 
-input[type=button], input[type=submit] {
+input[type=button],
+input[type=color],
+input[type=reset],
+input[type=submit] {
   padding-left: 20px;
   padding-right: 20px;
 }
@@ -126,35 +145,72 @@
   background: white;
 }
 
-input[type=input]:focus, input[type=password]:focus,
-input:not([type]):focus, input[type=button]:focus,
+input:not([type]):focus,
+input[type=button]:focus,
+input[type=color]:focus,
+input[type=date]:focus,
+input[type=datetime-local]:focus,
+input[type=email]:focus,
+input[type=month]:focus,
+input[type=number]:focus,
+input[type=password]:focus,
+input[type=reset]:focus,
+input[type=search]:focus,
 input[type=submit]:focus,
-textarea:focus, select:focus {
+input[type=tel]:focus,
+input[type=text]:focus,
+input[type=time]:focus,
+input[type=url]:focus,
+input[type=week]:focus,
+select:focus,
+textarea:focus {
   box-shadow: 0px 0px 3px rgba(74, 144, 217, 0.5);
   border-color: rgb(74, 144, 217);
   outline: none;
 }
 
 input[type=button]::-moz-focus-inner,
+input[type=color]::-moz-focus-inner,
+input[type=reset]::-moz-focus-inner,
 input[type=submit]::-moz-focus-inner {
   border: none;
 }
 
-input[type=input]:disabled, input[type=password]:disabled,
-input:not([type]):disabled, input[type=button]:disabled,
-input[type=submit]:disabled, input[type=number]:disabled,
-textarea:disabled, select:disabled {
+input:not([type]):disabled,
+input[type=button]:disabled,
+input[type=color]:disabled,
+input[type=date]:disabled,
+input[type=datetime-local]:disabled,
+input[type=email]:disabled,
+input[type=month]:disabled,
+input[type=number]:disabled,
+input[type=password]:disabled,
+input[type=reset]:disabled,
+input[type=search]:disabled,
+input[type=submit]:disabled,
+input[type=tel]:disabled,
+input[type=text]:disabled,
+input[type=time]:disabled,
+input[type=url]:disabled,
+input[type=week]:disabled,
+select:disabled,
+textarea:disabled {
   color: rgb(128, 128, 128);
   background: rgb(240, 240, 240);
 }
 
-input[type=button]:active, input[type=submit]:active,
+input[type=button]:active,
+input[type=color]:active,
+input[type=reset]:active,
+input[type=submit]:active,
 select:active {
   border-bottom-width: 1px;
   margin-top: 3px;
 }
 
 :root:not(.noVNC_touch) input[type=button]:hover:not(:disabled),
+:root:not(.noVNC_touch) input[type=color]:hover:not(:disabled),
+:root:not(.noVNC_touch) input[type=reset]:hover:not(:disabled),
 :root:not(.noVNC_touch) input[type=submit]:hover:not(:disabled),
 :root:not(.noVNC_touch) select:hover:not(:disabled) {
   background: linear-gradient(to top, rgb(255, 255, 255), rgb(250, 250, 250));
@@ -579,7 +635,7 @@
 }
 
 /* Extra manual keys */
-:root:not(.noVNC_connected) #noVNC_extra_keys {
+:root:not(.noVNC_connected) #noVNC_toggle_extra_keys_button {
   display: none;
 }
 
@@ -631,6 +687,16 @@
   width: 100px;
 }
 
+/* Version */
+
+.noVNC_version_wrapper {
+  font-size: small;
+}
+
+.noVNC_version {
+  margin-left: 1rem;
+}
+
 /* Connection Controls */
 :root:not(.noVNC_connected) #noVNC_disconnect_button {
   display: none;
@@ -780,19 +846,23 @@
  * ----------------------------------------
  */
 
-#noVNC_password_dlg {
+#noVNC_credentials_dlg {
   position: relative;
 
   transform: translateY(-50px);
 }
-#noVNC_password_dlg.noVNC_open {
+#noVNC_credentials_dlg.noVNC_open {
   transform: translateY(0);
 }
-#noVNC_password_dlg ul {
+#noVNC_credentials_dlg ul {
   list-style: none;
   margin: 0px;
   padding: 0px;
 }
+.noVNC_hidden {
+  display: none;
+}
+
 
 /* ----------------------------------------
  * Main Area
diff --git a/systemvm/agent/noVNC/app/ui.js b/systemvm/agent/noVNC/app/ui.js
index 13d1c01..9158c33 100644
--- a/systemvm/agent/noVNC/app/ui.js
+++ b/systemvm/agent/noVNC/app/ui.js
@@ -1,6 +1,6 @@
 /*
  * noVNC: HTML5 VNC client
- * Copyright (C) 2018 The noVNC Authors
+ * Copyright (C) 2019 The noVNC Authors
  * Licensed under MPL 2.0 (see LICENSE.txt)
  *
  * See README.md for usage and integration instructions.
@@ -8,7 +8,7 @@
 
 import * as Log from '../core/util/logging.js';
 import _, { l10n } from './localization.js';
-import { isTouchDevice, isSafari, isIOS, isAndroid, dragThreshold }
+import { isTouchDevice, isSafari, hasScrollbarGutter, dragThreshold }
     from '../core/util/browser.js';
 import { setCapture, getPointerEvent } from '../core/util/events.js';
 import KeyTable from "../core/input/keysym.js";
@@ -17,6 +17,8 @@
 import RFB from "../core/rfb.js";
 import * as WebUtil from "./webutil.js";
 
+const PAGE_TITLE = "noVNC";
+
 const UI = {
 
     connected: false,
@@ -35,9 +37,11 @@
     lastKeyboardinput: null,
     defaultKeyboardinputLen: 100,
 
-    inhibit_reconnect: true,
-    reconnect_callback: null,
-    reconnect_password: null,
+    inhibitReconnect: true,
+    reconnectCallback: null,
+    reconnectPassword: null,
+
+    fullScreen: false,
 
     prime() {
         return WebUtil.initSettings().then(() => {
@@ -59,6 +63,17 @@
         // Translate the DOM
         l10n.translateDOM();
 
+        WebUtil.fetchJSON('./package.json')
+            .then((packageInfo) => {
+                Array.from(document.getElementsByClassName('noVNC_version')).forEach(el => el.innerText = packageInfo.version);
+            })
+            .catch((err) => {
+                Log.Error("Couldn't fetch package.json: " + err);
+                Array.from(document.getElementsByClassName('noVNC_version_wrapper'))
+                    .concat(Array.from(document.getElementsByClassName('noVNC_version_separator')))
+                    .forEach(el => el.style.display = 'none');
+            });
+
         // Adapt the interface for touch screen devices
         if (isTouchDevice) {
             document.documentElement.classList.add("noVNC_touch");
@@ -145,10 +160,13 @@
         /* Populate the controls if defaults are provided in the URL */
         UI.initSetting('host', window.location.hostname);
         UI.initSetting('port', port);
+        UI.initSetting('token', window.location.token);
         UI.initSetting('encrypt', (window.location.protocol === "https:"));
         UI.initSetting('view_clip', false);
         UI.initSetting('resize', 'off');
-        UI.initSetting('shared', false);
+        UI.initSetting('quality', 6);
+        UI.initSetting('compression', 2);
+        UI.initSetting('shared', true);
         UI.initSetting('view_only', false);
         UI.initSetting('show_dot', false);
         UI.initSetting('path', 'websockify');
@@ -219,14 +237,6 @@
     },
 
     addTouchSpecificHandlers() {
-        document.getElementById("noVNC_mouse_button0")
-            .addEventListener('click', () => UI.setMouseButton(1));
-        document.getElementById("noVNC_mouse_button1")
-            .addEventListener('click', () => UI.setMouseButton(2));
-        document.getElementById("noVNC_mouse_button2")
-            .addEventListener('click', () => UI.setMouseButton(4));
-        document.getElementById("noVNC_mouse_button4")
-            .addEventListener('click', () => UI.setMouseButton(0));
         document.getElementById("noVNC_keyboard_button")
             .addEventListener('click', UI.toggleVirtualKeyboard);
 
@@ -303,17 +313,17 @@
         document.getElementById("noVNC_cancel_reconnect_button")
             .addEventListener('click', UI.cancelReconnect);
 
-        document.getElementById("noVNC_password_button")
-            .addEventListener('click', UI.setPassword);
+        document.getElementById("noVNC_credentials_button")
+            .addEventListener('click', UI.setCredentials);
     },
 
     addClipboardHandlers() {
         document.getElementById("noVNC_clipboard_button")
             .addEventListener('click', UI.toggleClipboardPanel);
-        document.getElementById("noVNC_clipboard_text")
-            .addEventListener('change', UI.clipboardSend);
         document.getElementById("noVNC_clipboard_clear_button")
             .addEventListener('click', UI.clipboardClear);
+        document.getElementById("noVNC_clipboard_send_button")
+            .addEventListener('click', UI.clipboardSend);
     },
 
     // Add a call to save settings when the element changes,
@@ -334,6 +344,10 @@
         UI.addSettingChangeHandler('resize');
         UI.addSettingChangeHandler('resize', UI.applyResizeMode);
         UI.addSettingChangeHandler('resize', UI.updateViewClip);
+        UI.addSettingChangeHandler('quality');
+        UI.addSettingChangeHandler('quality', UI.updateQuality);
+        UI.addSettingChangeHandler('compression');
+        UI.addSettingChangeHandler('compression', UI.updateCompression);
         UI.addSettingChangeHandler('view_clip');
         UI.addSettingChangeHandler('view_clip', UI.updateViewClip);
         UI.addSettingChangeHandler('shared');
@@ -375,25 +389,25 @@
         document.documentElement.classList.remove("noVNC_disconnecting");
         document.documentElement.classList.remove("noVNC_reconnecting");
 
-        const transition_elem = document.getElementById("noVNC_transition_text");
+        const transitionElem = document.getElementById("noVNC_transition_text");
         switch (state) {
             case 'init':
                 break;
             case 'connecting':
-                transition_elem.textContent = _("Connecting...");
+                transitionElem.textContent = _("Connecting...");
                 document.documentElement.classList.add("noVNC_connecting");
                 break;
             case 'connected':
                 document.documentElement.classList.add("noVNC_connected");
                 break;
             case 'disconnecting':
-                transition_elem.textContent = _("Disconnecting...");
+                transitionElem.textContent = _("Disconnecting...");
                 document.documentElement.classList.add("noVNC_disconnecting");
                 break;
             case 'disconnected':
                 break;
             case 'reconnecting':
-                transition_elem.textContent = _("Reconnecting...");
+                transitionElem.textContent = _("Reconnecting...");
                 document.documentElement.classList.add("noVNC_reconnecting");
                 break;
             default:
@@ -411,7 +425,6 @@
             UI.disableSetting('port');
             UI.disableSetting('path');
             UI.disableSetting('repeaterID');
-            UI.setMouseButton(1);
 
             // Hide the controlbar after 2 seconds
             UI.closeControlbarTimeout = setTimeout(UI.closeControlbar, 2000);
@@ -426,38 +439,35 @@
             UI.keepControlbar();
         }
 
-        // State change closes the password dialog
-        document.getElementById('noVNC_password_dlg')
+        // State change closes dialogs as they may not be relevant
+        // anymore
+        UI.closeAllPanels();
+        document.getElementById('noVNC_credentials_dlg')
             .classList.remove('noVNC_open');
     },
 
-    showStatus(text, status_type, time) {
+    showStatus(text, statusType, time) {
         const statusElem = document.getElementById('noVNC_status');
 
-        clearTimeout(UI.statusTimeout);
-
-        if (typeof status_type === 'undefined') {
-            status_type = 'normal';
+        if (typeof statusType === 'undefined') {
+            statusType = 'normal';
         }
 
         // Don't overwrite more severe visible statuses and never
         // errors. Only shows the first error.
-        let visible_status_type = 'none';
         if (statusElem.classList.contains("noVNC_open")) {
             if (statusElem.classList.contains("noVNC_status_error")) {
-                visible_status_type = 'error';
-            } else if (statusElem.classList.contains("noVNC_status_warn")) {
-                visible_status_type = 'warn';
-            } else {
-                visible_status_type = 'normal';
+                return;
+            }
+            if (statusElem.classList.contains("noVNC_status_warn") &&
+                statusType === 'normal') {
+                return;
             }
         }
-        if (visible_status_type === 'error' ||
-            (visible_status_type === 'warn' && status_type === 'normal')) {
-            return;
-        }
 
-        switch (status_type) {
+        clearTimeout(UI.statusTimeout);
+
+        switch (statusType) {
             case 'error':
                 statusElem.classList.remove("noVNC_status_warn");
                 statusElem.classList.remove("noVNC_status_normal");
@@ -487,7 +497,7 @@
         }
 
         // Error messages do not timeout
-        if (status_type !== 'error') {
+        if (statusType !== 'error') {
             UI.statusTimeout = window.setTimeout(UI.hideStatus, time);
         }
     },
@@ -507,6 +517,13 @@
     },
 
     idleControlbar() {
+        // Don't fade if a child of the control bar has focus
+        if (document.getElementById('noVNC_control_bar')
+            .contains(document.activeElement) && document.hasFocus()) {
+            UI.activateControlbar();
+            return;
+        }
+
         document.getElementById('noVNC_control_bar_anchor')
             .classList.add("noVNC_idle");
     },
@@ -524,6 +541,7 @@
         UI.closeAllPanels();
         document.getElementById('noVNC_control_bar')
             .classList.remove("noVNC_open");
+        UI.rfb.focus();
     },
 
     toggleControlbar() {
@@ -821,6 +839,8 @@
         UI.updateSetting('encrypt');
         UI.updateSetting('view_clip');
         UI.updateSetting('resize');
+        UI.updateSetting('quality');
+        UI.updateSetting('compression');
         UI.updateSetting('shared');
         UI.updateSetting('view_only');
         UI.updateSetting('path');
@@ -927,6 +947,8 @@
             UI.closeClipboardPanel();
         } else {
             UI.openClipboardPanel();
+            setTimeout(() => document
+                .getElementById('noVNC_clipboard_text').focus(), 100);
         }
     },
 
@@ -938,14 +960,13 @@
 
     clipboardClear() {
         document.getElementById('noVNC_clipboard_text').value = "";
-        UI.rfb.clipboardPasteFrom("");
     },
 
     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");
+        UI.rfb.sendText(text);
+        UI.closeClipboardPanel();
+        UI.focusOnConsole();
     },
 
 /* ------^-------
@@ -974,10 +995,11 @@
         const host = UI.getSetting('host');
         const port = UI.getSetting('port');
         const path = UI.getSetting('path');
+        const token = UI.getSetting('token')
 
         if (typeof password === 'undefined') {
             password = WebUtil.getConfigVar('password');
-            UI.reconnect_password = password;
+            UI.reconnectPassword = password;
         }
 
         if (password === null) {
@@ -992,7 +1014,6 @@
             return;
         }
 
-        UI.closeAllPanels();
         UI.closeConnectPanel();
 
         UI.updateVisualState('connecting');
@@ -1006,16 +1027,10 @@
             url += ':' + port;
         }
         url += '/' + path;
-
-        var urlParams = new URLSearchParams(window.location.search);
-        var param = urlParams.get('token');
-        if (param) {
-            url += "?token=" + param
-        }
+        url += '?token=' + token;
 
         UI.rfb = new RFB(document.getElementById('noVNC_container'), url,
                          { shared: UI.getSetting('shared'),
-                           showDotCursor: UI.getSetting('show_dot'),
                            repeaterID: UI.getSetting('repeaterID'),
                            credentials: { password: password } });
         UI.rfb.addEventListener("connect", UI.connectFinished);
@@ -1029,18 +1044,20 @@
         UI.rfb.clipViewport = UI.getSetting('view_clip');
         UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale';
         UI.rfb.resizeSession = UI.getSetting('resize') === 'remote';
+        UI.rfb.qualityLevel = parseInt(UI.getSetting('quality'));
+        UI.rfb.compressionLevel = parseInt(UI.getSetting('compression'));
+        UI.rfb.showDotCursor = UI.getSetting('show_dot');
 
         UI.updateViewOnly(); // requires UI.rfb
     },
 
     disconnect() {
-        UI.closeAllPanels();
         UI.rfb.disconnect();
 
         UI.connected = false;
 
         // Disable automatic reconnecting
-        UI.inhibit_reconnect = true;
+        UI.inhibitReconnect = true;
 
         UI.updateVisualState('disconnecting');
 
@@ -1048,20 +1065,20 @@
     },
 
     reconnect() {
-        UI.reconnect_callback = null;
+        UI.reconnectCallback = null;
 
         // if reconnect has been disabled in the meantime, do nothing.
-        if (UI.inhibit_reconnect) {
+        if (UI.inhibitReconnect) {
             return;
         }
 
-        UI.connect(null, UI.reconnect_password);
+        UI.connect(null, UI.reconnectPassword);
     },
 
     cancelReconnect() {
-        if (UI.reconnect_callback !== null) {
-            clearTimeout(UI.reconnect_callback);
-            UI.reconnect_callback = null;
+        if (UI.reconnectCallback !== null) {
+            clearTimeout(UI.reconnectCallback);
+            UI.reconnectCallback = null;
         }
 
         UI.updateVisualState('disconnected');
@@ -1072,13 +1089,13 @@
 
     connectFinished(e) {
         UI.connected = true;
-        UI.inhibit_reconnect = false;
+        UI.inhibitReconnect = false;
 
         let msg;
         if (UI.getSetting('encrypt')) {
-            msg = _("Connected (encrypted) to ") + UI.desktopName;
+            msg = _("Connected");
         } else {
-            msg = _("Connected (unencrypted) to ") + UI.desktopName;
+            msg = _("Connected")
         }
         UI.showStatus(msg);
         UI.updateVisualState('connected');
@@ -1106,17 +1123,19 @@
             } else {
                 UI.showStatus(_("Failed to connect to server"), 'error');
             }
-        } else if (UI.getSetting('reconnect', false) === true && !UI.inhibit_reconnect) {
+        } else if (UI.getSetting('reconnect', false) === true && !UI.inhibitReconnect) {
             UI.updateVisualState('reconnecting');
 
             const delay = parseInt(UI.getSetting('reconnect_delay'));
-            UI.reconnect_callback = setTimeout(UI.reconnect, delay);
+            UI.reconnectCallback = setTimeout(UI.reconnect, delay);
             return;
         } else {
             UI.updateVisualState('disconnected');
             UI.showStatus(_("Disconnected"), 'normal');
         }
 
+        document.title = PAGE_TITLE;
+
         UI.openControlbar();
         UI.openConnectPanel();
     },
@@ -1143,27 +1162,46 @@
 
     credentials(e) {
         // FIXME: handle more types
-        document.getElementById('noVNC_password_dlg')
+
+        document.getElementById("noVNC_username_block").classList.remove("noVNC_hidden");
+        document.getElementById("noVNC_password_block").classList.remove("noVNC_hidden");
+
+        let inputFocus = "none";
+        if (e.detail.types.indexOf("username") === -1) {
+            document.getElementById("noVNC_username_block").classList.add("noVNC_hidden");
+        } else {
+            inputFocus = inputFocus === "none" ? "noVNC_username_input" : inputFocus;
+        }
+        if (e.detail.types.indexOf("password") === -1) {
+            document.getElementById("noVNC_password_block").classList.add("noVNC_hidden");
+        } else {
+            inputFocus = inputFocus === "none" ? "noVNC_password_input" : inputFocus;
+        }
+        document.getElementById('noVNC_credentials_dlg')
             .classList.add('noVNC_open');
 
         setTimeout(() => document
-            .getElementById('noVNC_password_input').focus(), 100);
+            .getElementById(inputFocus).focus(), 100);
 
-        Log.Warn("Server asked for a password");
-        UI.showStatus(_("Password is required"), "warning");
+        Log.Warn("Server asked for credentials");
+        UI.showStatus(_("Credentials are required"), "warning");
     },
 
-    setPassword(e) {
+    setCredentials(e) {
         // Prevent actually submitting the form
         e.preventDefault();
 
-        const inputElem = document.getElementById('noVNC_password_input');
-        const password = inputElem.value;
+        let inputElemUsername = document.getElementById('noVNC_username_input');
+        const username = inputElemUsername.value;
+
+        let inputElemPassword = document.getElementById('noVNC_password_input');
+        const password = inputElemPassword.value;
         // Clear the input after reading the password
-        inputElem.value = "";
-        UI.rfb.sendCredentials({ password: password });
-        UI.reconnect_password = password;
-        document.getElementById('noVNC_password_dlg')
+        inputElemPassword.value = "";
+
+        UI.rfb.sendCredentials({ username: username, password: password });
+        UI.reconnectPassword = password;
+        document.getElementById('noVNC_credentials_dlg')
             .classList.remove('noVNC_open');
     },
 
@@ -1174,38 +1212,14 @@
  * ------v------*/
 
     toggleFullscreen() {
-        if (document.fullscreenElement || // alternative standard method
-            document.mozFullScreenElement || // currently working methods
-            document.webkitFullscreenElement ||
-            document.msFullscreenElement) {
-            if (document.exitFullscreen) {
-                document.exitFullscreen();
-            } else if (document.mozCancelFullScreen) {
-                document.mozCancelFullScreen();
-            } else if (document.webkitExitFullscreen) {
-                document.webkitExitFullscreen();
-            } else if (document.msExitFullscreen) {
-                document.msExitFullscreen();
-            }
-        } else {
-            if (document.documentElement.requestFullscreen) {
-                document.documentElement.requestFullscreen();
-            } else if (document.documentElement.mozRequestFullScreen) {
-                document.documentElement.mozRequestFullScreen();
-            } else if (document.documentElement.webkitRequestFullscreen) {
-                document.documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
-            } else if (document.body.msRequestFullscreen) {
-                document.body.msRequestFullscreen();
-            }
-        }
-        UI.updateFullscreenButton();
+        this.fullScreen = !this.fullScreen
+        UI.rfb.scaleViewport = this.fullScreen
+        UI.updateFullscreenButton(this.fullScreen);
+        UI.focusOnConsole();
     },
 
-    updateFullscreenButton() {
-        if (document.fullscreenElement || // alternative standard method
-            document.mozFullScreenElement || // currently working methods
-            document.webkitFullscreenElement ||
-            document.msFullscreenElement ) {
+    updateFullscreenButton(fullScreen) {
+        if (fullScreen) {
             document.getElementById('noVNC_fullscreen_button')
                 .classList.add("noVNC_selected");
         } else {
@@ -1246,8 +1260,9 @@
             // Can't be clipping if viewport is scaled to fit
             UI.forceSetting('view_clip', false);
             UI.rfb.clipViewport  = false;
-        } else if (isIOS() || isAndroid()) {
-            // iOS and Android usually have shit scrollbars
+        } else if (!hasScrollbarGutter) {
+            // Some platforms have scrollbars that are difficult
+            // to use in our case, so we always use our own panning
             UI.forceSetting('view_clip', true);
             UI.rfb.clipViewport = true;
         } else {
@@ -1290,30 +1305,40 @@
             viewDragButton.classList.remove("noVNC_selected");
         }
 
-        // Different behaviour for touch vs non-touch
-        // The button is disabled instead of hidden on touch devices
-        if (isTouchDevice) {
+        if (UI.rfb.clipViewport) {
             viewDragButton.classList.remove("noVNC_hidden");
-
-            if (UI.rfb.clipViewport) {
-                viewDragButton.disabled = false;
-            } else {
-                viewDragButton.disabled = true;
-            }
         } else {
-            viewDragButton.disabled = false;
-
-            if (UI.rfb.clipViewport) {
-                viewDragButton.classList.remove("noVNC_hidden");
-            } else {
-                viewDragButton.classList.add("noVNC_hidden");
-            }
+            viewDragButton.classList.add("noVNC_hidden");
         }
     },
 
 /* ------^-------
  *   /VIEWDRAG
  * ==============
+ *    QUALITY
+ * ------v------*/
+
+    updateQuality() {
+        if (!UI.rfb) return;
+
+        UI.rfb.qualityLevel = parseInt(UI.getSetting('quality'));
+    },
+
+/* ------^-------
+ *   /QUALITY
+ * ==============
+ *  COMPRESSION
+ * ------v------*/
+
+    updateCompression() {
+        if (!UI.rfb) return;
+
+        UI.rfb.compressionLevel = parseInt(UI.getSetting('compression'));
+    },
+
+/* ------^-------
+ *  /COMPRESSION
+ * ==============
  *    KEYBOARD
  * ------v------*/
 
@@ -1508,20 +1533,20 @@
     },
 
     sendEsc() {
-        UI.rfb.sendKey(KeyTable.XK_Escape, "Escape");
+        UI.sendKey(KeyTable.XK_Escape, "Escape");
     },
 
     sendTab() {
-        UI.rfb.sendKey(KeyTable.XK_Tab);
+        UI.sendKey(KeyTable.XK_Tab, "Tab");
     },
 
     toggleCtrl() {
         const btn = document.getElementById('noVNC_toggle_ctrl_button');
         if (btn.classList.contains("noVNC_selected")) {
-            UI.rfb.sendKey(KeyTable.XK_Control_L, "ControlLeft", false);
+            UI.sendKey(KeyTable.XK_Control_L, "ControlLeft", false);
             btn.classList.remove("noVNC_selected");
         } else {
-            UI.rfb.sendKey(KeyTable.XK_Control_L, "ControlLeft", true);
+            UI.sendKey(KeyTable.XK_Control_L, "ControlLeft", true);
             btn.classList.add("noVNC_selected");
         }
     },
@@ -1529,10 +1554,10 @@
     toggleWindows() {
         const btn = document.getElementById('noVNC_toggle_windows_button');
         if (btn.classList.contains("noVNC_selected")) {
-            UI.rfb.sendKey(KeyTable.XK_Super_L, "MetaLeft", false);
+            UI.sendKey(KeyTable.XK_Super_L, "MetaLeft", false);
             btn.classList.remove("noVNC_selected");
         } else {
-            UI.rfb.sendKey(KeyTable.XK_Super_L, "MetaLeft", true);
+            UI.sendKey(KeyTable.XK_Super_L, "MetaLeft", true);
             btn.classList.add("noVNC_selected");
         }
     },
@@ -1540,16 +1565,42 @@
     toggleAlt() {
         const btn = document.getElementById('noVNC_toggle_alt_button');
         if (btn.classList.contains("noVNC_selected")) {
-            UI.rfb.sendKey(KeyTable.XK_Alt_L, "AltLeft", false);
+            UI.sendKey(KeyTable.XK_Alt_L, "AltLeft", false);
             btn.classList.remove("noVNC_selected");
         } else {
-            UI.rfb.sendKey(KeyTable.XK_Alt_L, "AltLeft", true);
+            UI.sendKey(KeyTable.XK_Alt_L, "AltLeft", true);
             btn.classList.add("noVNC_selected");
         }
     },
 
     sendCtrlAltDel() {
         UI.rfb.sendCtrlAltDel();
+        // See below
+        UI.rfb.focus();
+        UI.idleControlbar();
+    },
+
+    // Move focus to the screen in order to be able to use the
+    // keyboard right after these extra keys.
+    // The exception is when a virtual keyboard is used, because
+    // if we focus the screen the virtual keyboard would be closed.
+    // In this case we focus our special virtual keyboard input
+    // element instead.
+    focusOnConsole() {
+        if (document.getElementById('noVNC_keyboard_button')
+            .classList.contains("noVNC_selected")) {
+            document.getElementById('noVNC_keyboardinput').focus();
+        } else {
+            UI.rfb.focus();
+        }
+    },
+
+    sendKey(keysym, code, down) {
+        UI.rfb.sendKey(keysym, code, down);
+        UI.focusOnConsole()
+        // fade out the controlbar to highlight that
+        // the focus has been moved to the screen
+        UI.idleControlbar();
     },
 
 /* ------^-------
@@ -1558,24 +1609,6 @@
  *     MISC
  * ------v------*/
 
-    setMouseButton(num) {
-        const view_only = UI.rfb.viewOnly;
-        if (UI.rfb && !view_only) {
-            UI.rfb.touchButton = num;
-        }
-
-        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");
-            } else {
-                button.classList.add("noVNC_hidden");
-            }
-        }
-    },
-
     updateViewOnly() {
         if (!UI.rfb) return;
         UI.rfb.viewOnly = UI.getSetting('view_only');
@@ -1586,14 +1619,14 @@
                 .classList.add('noVNC_hidden');
             document.getElementById('noVNC_toggle_extra_keys_button')
                 .classList.add('noVNC_hidden');
-            document.getElementById('noVNC_mouse_button' + UI.rfb.touchButton)
+            document.getElementById('noVNC_clipboard_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');
-            document.getElementById('noVNC_mouse_button' + UI.rfb.touchButton)
+            document.getElementById('noVNC_clipboard_button')
                 .classList.remove('noVNC_hidden');
         }
     },
@@ -1604,13 +1637,13 @@
     },
 
     updateLogging() {
-        WebUtil.init_logging(UI.getSetting('logging'));
+        WebUtil.initLogging(UI.getSetting('logging'));
     },
 
     updateDesktopName(e) {
         UI.desktopName = e.detail.name;
         // Display the desktop name in the document title
-        document.title = e.detail.name + " - noVNC";
+        document.title = e.detail.name + " - " + PAGE_TITLE;
     },
 
     bell(e) {
@@ -1646,7 +1679,7 @@
 };
 
 // Set up translations
-const LINGUAS = ["cs", "de", "el", "es", "ko", "nl", "pl", "ru", "sv", "tr", "zh_CN", "zh_TW"];
+const LINGUAS = ["cs", "de", "el", "es", "ja", "ko", "nl", "pl", "ru", "sv", "tr", "zh_CN", "zh_TW"];
 l10n.setup(LINGUAS);
 if (l10n.language === "en" || l10n.dictionary !== undefined) {
     UI.prime();
diff --git a/systemvm/agent/noVNC/app/webutil.js b/systemvm/agent/noVNC/app/webutil.js
index 98e1d9e..568f0e2 100644
--- a/systemvm/agent/noVNC/app/webutil.js
+++ b/systemvm/agent/noVNC/app/webutil.js
@@ -1,21 +1,21 @@
 /*
  * noVNC: HTML5 VNC client
- * Copyright (C) 2018 The noVNC Authors
+ * Copyright (C) 2019 The noVNC Authors
  * Licensed under MPL 2.0 (see LICENSE.txt)
  *
  * See README.md for usage and integration instructions.
  */
 
-import { init_logging as main_init_logging } from '../core/util/logging.js';
+import { initLogging as mainInitLogging } from '../core/util/logging.js';
 
 // init log level reading the logging HTTP param
-export function init_logging(level) {
+export function initLogging(level) {
     "use strict";
     if (typeof level !== "undefined") {
-        main_init_logging(level);
+        mainInitLogging(level);
     } else {
         const param = document.location.href.match(/logging=([A-Za-z0-9._-]*)/);
-        main_init_logging(param || undefined);
+        mainInitLogging(param || undefined);
     }
 }
 
@@ -115,13 +115,8 @@
 let settings = {};
 
 export function initSettings() {
-    if (!window.chrome || !window.chrome.storage) {
-        settings = {};
-        return Promise.resolve();
-    }
-
-    return new Promise(resolve => window.chrome.storage.sync.get(resolve))
-        .then((cfg) => { settings = cfg; });
+    settings = {};
+    return Promise.resolve();
 }
 
 // Update the settings cache, but do not write to permanent storage
@@ -134,22 +129,13 @@
     "use strict";
     if (settings[name] === value) return;
     settings[name] = value;
-    if (window.chrome && window.chrome.storage) {
-        window.chrome.storage.sync.set(settings);
-    } else {
-        localStorage.setItem(name, value);
-    }
 }
 
 export function readSetting(name, defaultValue) {
     "use strict";
     let value;
-    if ((name in settings) || (window.chrome && window.chrome.storage)) {
-        value = settings[name];
-    } else {
-        value = localStorage.getItem(name);
-        settings[name] = value;
-    }
+    value = settings[name];
+
     if (typeof value === "undefined") {
         value = null;
     }
@@ -169,11 +155,6 @@
     // between this delete and the next read, it could lead to an unexpected
     // value change.
     delete settings[name];
-    if (window.chrome && window.chrome.storage) {
-        window.chrome.storage.sync.remove(name);
-    } else {
-        localStorage.removeItem(name);
-    }
 }
 
 export function injectParamIfMissing(path, param, value) {
@@ -184,7 +165,7 @@
     const elem = document.createElement('a');
     elem.href = path;
 
-    const param_eq = encodeURIComponent(param) + "=";
+    const paramEq = encodeURIComponent(param) + "=";
     let query;
     if (elem.search) {
         query = elem.search.slice(1).split('&');
@@ -192,8 +173,8 @@
         query = [];
     }
 
-    if (!query.some(v => v.startsWith(param_eq))) {
-        query.push(param_eq + encodeURIComponent(value));
+    if (!query.some(v => v.startsWith(paramEq))) {
+        query.push(paramEq + encodeURIComponent(value));
         elem.search = "?" + query.join("&");
     }
 
diff --git a/systemvm/agent/noVNC/core/base64.js b/systemvm/agent/noVNC/core/base64.js
index 88e7454..db572c2 100644
--- a/systemvm/agent/noVNC/core/base64.js
+++ b/systemvm/agent/noVNC/core/base64.js
@@ -57,12 +57,12 @@
     /* eslint-enable comma-spacing */
 
     decode(data, offset = 0) {
-        let data_length = data.indexOf('=') - offset;
-        if (data_length < 0) { data_length = data.length - offset; }
+        let dataLength = data.indexOf('=') - offset;
+        if (dataLength < 0) { dataLength = data.length - offset; }
 
         /* Every four characters is 3 resulting numbers */
-        const result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5);
-        const result = new Array(result_length);
+        const resultLength = (dataLength >> 2) * 3 + Math.floor((dataLength % 4) / 1.5);
+        const result = new Array(resultLength);
 
         // Convert one by one.
 
diff --git a/systemvm/agent/noVNC/core/decoders/copyrect.js b/systemvm/agent/noVNC/core/decoders/copyrect.js
index a78ded7..9e6391a 100644
--- a/systemvm/agent/noVNC/core/decoders/copyrect.js
+++ b/systemvm/agent/noVNC/core/decoders/copyrect.js
@@ -1,8 +1,6 @@
 /*
  * noVNC: HTML5 VNC client
- * Copyright (C) 2012 Joel Martin
- * Copyright (C) 2018 Samuel Mannehed for Cendio AB
- * Copyright (C) 2018 Pierre Ossman for Cendio AB
+ * Copyright (C) 2019 The noVNC Authors
  * Licensed under MPL 2.0 (see LICENSE.txt)
  *
  * See README.md for usage and integration instructions.
@@ -17,6 +15,11 @@
 
         let deltaX = sock.rQshift16();
         let deltaY = sock.rQshift16();
+
+        if ((width === 0) || (height === 0)) {
+            return true;
+        }
+
         display.copyImage(deltaX, deltaY, x, y, width, height);
 
         return true;
diff --git a/systemvm/agent/noVNC/core/decoders/hextile.js b/systemvm/agent/noVNC/core/decoders/hextile.js
index aa76d2f..ac21eff 100644
--- a/systemvm/agent/noVNC/core/decoders/hextile.js
+++ b/systemvm/agent/noVNC/core/decoders/hextile.js
@@ -1,8 +1,6 @@
 /*
  * noVNC: HTML5 VNC client
- * Copyright (C) 2012 Joel Martin
- * Copyright (C) 2018 Samuel Mannehed for Cendio AB
- * Copyright (C) 2018 Pierre Ossman for Cendio AB
+ * Copyright (C) 2019 The noVNC Authors
  * Licensed under MPL 2.0 (see LICENSE.txt)
  *
  * See README.md for usage and integration instructions.
@@ -15,14 +13,15 @@
     constructor() {
         this._tiles = 0;
         this._lastsubencoding = 0;
+        this._tileBuffer = new Uint8Array(16 * 16 * 4);
     }
 
     decodeRect(x, y, width, height, sock, display, depth) {
         if (this._tiles === 0) {
-            this._tiles_x = Math.ceil(width / 16);
-            this._tiles_y = Math.ceil(height / 16);
-            this._total_tiles = this._tiles_x * this._tiles_y;
-            this._tiles = this._total_tiles;
+            this._tilesX = Math.ceil(width / 16);
+            this._tilesY = Math.ceil(height / 16);
+            this._totalTiles = this._tilesX * this._tilesY;
+            this._tiles = this._totalTiles;
         }
 
         while (this._tiles > 0) {
@@ -41,11 +40,11 @@
                             subencoding + ")");
             }
 
-            const curr_tile = this._total_tiles - this._tiles;
-            const tile_x = curr_tile % this._tiles_x;
-            const tile_y = Math.floor(curr_tile / this._tiles_x);
-            const tx = x + tile_x * 16;
-            const ty = y + tile_y * 16;
+            const currTile = this._totalTiles - this._tiles;
+            const tileX = currTile % this._tilesX;
+            const tileY = Math.floor(currTile / this._tilesX);
+            const tx = x + tileX * 16;
+            const ty = y + tileY * 16;
             const tw = Math.min(16, (x + width) - tx);
             const th = Math.min(16, (y + height) - ty);
 
@@ -89,6 +88,11 @@
                     display.fillRect(tx, ty, tw, th, this._background);
                 }
             } else if (subencoding & 0x01) {  // Raw
+                let pixels = tw * th;
+                // Max sure the image is fully opaque
+                for (let i = 0;i <  pixels;i++) {
+                    rQ[rQi + i * 4 + 3] = 255;
+                }
                 display.blitImage(tx, ty, tw, th, rQ, rQi);
                 rQi += bytes - 1;
             } else {
@@ -101,7 +105,7 @@
                     rQi += 4;
                 }
 
-                display.startTile(tx, ty, tw, th, this._background);
+                this._startTile(tx, ty, tw, th, this._background);
                 if (subencoding & 0x08) {  // AnySubrects
                     let subrects = rQ[rQi];
                     rQi++;
@@ -124,10 +128,10 @@
                         const sw = (wh >> 4) + 1;
                         const sh = (wh & 0x0f) + 1;
 
-                        display.subTile(sx, sy, sw, sh, color);
+                        this._subTile(sx, sy, sw, sh, color);
                     }
                 }
-                display.finishTile();
+                this._finishTile(display);
             }
             sock.rQi = rQi;
             this._lastsubencoding = subencoding;
@@ -136,4 +140,52 @@
 
         return true;
     }
+
+    // start updating a tile
+    _startTile(x, y, width, height, color) {
+        this._tileX = x;
+        this._tileY = y;
+        this._tileW = width;
+        this._tileH = height;
+
+        const red = color[0];
+        const green = color[1];
+        const blue = color[2];
+
+        const data = this._tileBuffer;
+        for (let i = 0; i < width * height * 4; i += 4) {
+            data[i]     = red;
+            data[i + 1] = green;
+            data[i + 2] = blue;
+            data[i + 3] = 255;
+        }
+    }
+
+    // update sub-rectangle of the current tile
+    _subTile(x, y, w, h, color) {
+        const red = color[0];
+        const green = color[1];
+        const blue = color[2];
+        const xend = x + w;
+        const yend = y + h;
+
+        const data = this._tileBuffer;
+        const width = this._tileW;
+        for (let j = y; j < yend; j++) {
+            for (let i = x; i < xend; i++) {
+                const p = (i + (j * width)) * 4;
+                data[p]     = red;
+                data[p + 1] = green;
+                data[p + 2] = blue;
+                data[p + 3] = 255;
+            }
+        }
+    }
+
+    // draw the current tile to the screen
+    _finishTile(display) {
+        display.blitImage(this._tileX, this._tileY,
+                          this._tileW, this._tileH,
+                          this._tileBuffer, 0);
+    }
 }
diff --git a/systemvm/agent/noVNC/core/decoders/raw.js b/systemvm/agent/noVNC/core/decoders/raw.js
index f676e0d..e8ea178 100644
--- a/systemvm/agent/noVNC/core/decoders/raw.js
+++ b/systemvm/agent/noVNC/core/decoders/raw.js
@@ -1,8 +1,6 @@
 /*
  * noVNC: HTML5 VNC client
- * Copyright (C) 2012 Joel Martin
- * Copyright (C) 2018 Samuel Mannehed for Cendio AB
- * Copyright (C) 2018 Pierre Ossman for Cendio AB
+ * Copyright (C) 2019 The noVNC Authors
  * Licensed under MPL 2.0 (see LICENSE.txt)
  *
  * See README.md for usage and integration instructions.
@@ -15,6 +13,10 @@
     }
 
     decodeRect(x, y, width, height, sock, display, depth) {
+        if ((width === 0) || (height === 0)) {
+            return true;
+        }
+
         if (this._lines === 0) {
             this._lines = height;
         }
@@ -26,29 +28,35 @@
             return false;
         }
 
-        const cur_y = y + (height - this._lines);
-        const curr_height = Math.min(this._lines,
-                                     Math.floor(sock.rQlen / bytesPerLine));
+        const curY = y + (height - this._lines);
+        const currHeight = Math.min(this._lines,
+                                    Math.floor(sock.rQlen / bytesPerLine));
+        const pixels = width * currHeight;
+
         let data = sock.rQ;
         let index = sock.rQi;
 
         // Convert data if needed
         if (depth == 8) {
-            const pixels = width * curr_height;
             const newdata = new Uint8Array(pixels * 4);
             for (let i = 0; i < pixels; i++) {
                 newdata[i * 4 + 0] = ((data[index + i] >> 0) & 0x3) * 255 / 3;
                 newdata[i * 4 + 1] = ((data[index + i] >> 2) & 0x3) * 255 / 3;
                 newdata[i * 4 + 2] = ((data[index + i] >> 4) & 0x3) * 255 / 3;
-                newdata[i * 4 + 4] = 0;
+                newdata[i * 4 + 3] = 255;
             }
             data = newdata;
             index = 0;
         }
 
-        display.blitImage(x, cur_y, width, curr_height, data, index);
-        sock.rQskipBytes(curr_height * bytesPerLine);
-        this._lines -= curr_height;
+        // Max sure the image is fully opaque
+        for (let i = 0; i < pixels; i++) {
+            data[i * 4 + 3] = 255;
+        }
+
+        display.blitImage(x, curY, width, currHeight, data, index);
+        sock.rQskipBytes(currHeight * bytesPerLine);
+        this._lines -= currHeight;
         if (this._lines > 0) {
             return false;
         }
diff --git a/systemvm/agent/noVNC/core/decoders/rre.js b/systemvm/agent/noVNC/core/decoders/rre.js
index 57414a0..6219369 100644
--- a/systemvm/agent/noVNC/core/decoders/rre.js
+++ b/systemvm/agent/noVNC/core/decoders/rre.js
@@ -1,8 +1,6 @@
 /*
  * noVNC: HTML5 VNC client
- * Copyright (C) 2012 Joel Martin
- * Copyright (C) 2018 Samuel Mannehed for Cendio AB
- * Copyright (C) 2018 Pierre Ossman for Cendio AB
+ * Copyright (C) 2019 The noVNC Authors
  * Licensed under MPL 2.0 (see LICENSE.txt)
  *
  * See README.md for usage and integration instructions.
diff --git a/systemvm/agent/noVNC/core/decoders/tight.js b/systemvm/agent/noVNC/core/decoders/tight.js
index bcda04c..7952707 100644
--- a/systemvm/agent/noVNC/core/decoders/tight.js
+++ b/systemvm/agent/noVNC/core/decoders/tight.js
@@ -1,9 +1,7 @@
 /*
  * noVNC: HTML5 VNC client
- * Copyright (C) 2012 Joel Martin
+ * Copyright (C) 2019 The noVNC Authors
  * (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
- * Copyright (C) 2018 Samuel Mannehed for Cendio AB
- * Copyright (C) 2018 Pierre Ossman for Cendio AB
  * Licensed under MPL 2.0 (see LICENSE.txt)
  *
  * See README.md for usage and integration instructions.
@@ -58,7 +56,7 @@
         } else if (this._ctl === 0x0A) {
             ret = this._pngRect(x, y, width, height,
                                 sock, display, depth);
-        } else if ((this._ctl & 0x80) == 0) {
+        } else if ((this._ctl & 0x08) == 0) {
             ret = this._basicRect(this._ctl, x, y, width, height,
                                   sock, display, depth);
         } else {
@@ -82,7 +80,7 @@
         const rQ = sock.rQ;
 
         display.fillRect(x, y, width, height,
-                         [rQ[rQi + 2], rQ[rQi + 1], rQ[rQi]], false);
+                         [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2]], false);
         sock.rQskipBytes(3);
 
         return true;
@@ -94,7 +92,7 @@
             return false;
         }
 
-        display.imageRect(x, y, "image/jpeg", data);
+        display.imageRect(x, y, width, height, "image/jpeg", data);
 
         return true;
     }
@@ -150,6 +148,10 @@
         const uncompressedSize = width * height * 3;
         let data;
 
+        if (uncompressedSize === 0) {
+            return true;
+        }
+
         if (uncompressedSize < 12) {
             if (sock.rQwait("TIGHT", uncompressedSize)) {
                 return false;
@@ -162,13 +164,20 @@
                 return false;
             }
 
-            data = this._zlibs[streamId].inflate(data, true, uncompressedSize);
-            if (data.length != uncompressedSize) {
-                throw new Error("Incomplete zlib block");
-            }
+            this._zlibs[streamId].setInput(data);
+            data = this._zlibs[streamId].inflate(uncompressedSize);
+            this._zlibs[streamId].setInput(null);
         }
 
-        display.blitRgbImage(x, y, width, height, data, 0, false);
+        let rgbx = new Uint8Array(width * height * 4);
+        for (let i = 0, j = 0; i < width * height * 4; i += 4, j += 3) {
+            rgbx[i]     = data[j];
+            rgbx[i + 1] = data[j + 1];
+            rgbx[i + 2] = data[j + 2];
+            rgbx[i + 3] = 255;  // Alpha
+        }
+
+        display.blitImage(x, y, width, height, rgbx, 0, false);
 
         return true;
     }
@@ -198,6 +207,10 @@
 
         let data;
 
+        if (uncompressedSize === 0) {
+            return true;
+        }
+
         if (uncompressedSize < 12) {
             if (sock.rQwait("TIGHT", uncompressedSize)) {
                 return false;
@@ -210,10 +223,9 @@
                 return false;
             }
 
-            data = this._zlibs[streamId].inflate(data, true, uncompressedSize);
-            if (data.length != uncompressedSize) {
-                throw new Error("Incomplete zlib block");
-            }
+            this._zlibs[streamId].setInput(data);
+            data = this._zlibs[streamId].inflate(uncompressedSize);
+            this._zlibs[streamId].setInput(null);
         }
 
         // Convert indexed (palette based) image data to RGB
@@ -241,7 +253,7 @@
                 for (let b = 7; b >= 0; b--) {
                     dp = (y * width + x * 8 + 7 - b) * 4;
                     sp = (data[y * w + x] >> b & 1) * 3;
-                    dest[dp] = palette[sp];
+                    dest[dp]     = palette[sp];
                     dest[dp + 1] = palette[sp + 1];
                     dest[dp + 2] = palette[sp + 2];
                     dest[dp + 3] = 255;
@@ -251,14 +263,14 @@
             for (let b = 7; b >= 8 - width % 8; b--) {
                 dp = (y * width + x * 8 + 7 - b) * 4;
                 sp = (data[y * w + x] >> b & 1) * 3;
-                dest[dp] = palette[sp];
+                dest[dp]     = palette[sp];
                 dest[dp + 1] = palette[sp + 1];
                 dest[dp + 2] = palette[sp + 2];
                 dest[dp + 3] = 255;
             }
         }
 
-        display.blitRgbxImage(x, y, width, height, dest, 0, false);
+        display.blitImage(x, y, width, height, dest, 0, false);
     }
 
     _paletteRect(x, y, width, height, data, palette, display) {
@@ -267,13 +279,13 @@
         const total = width * height * 4;
         for (let i = 0, j = 0; i < total; i += 4, j++) {
             const sp = data[j] * 3;
-            dest[i] = palette[sp];
+            dest[i]     = palette[sp];
             dest[i + 1] = palette[sp + 1];
             dest[i + 2] = palette[sp + 2];
             dest[i + 3] = 255;
         }
 
-        display.blitRgbxImage(x, y, width, height, dest, 0, false);
+        display.blitImage(x, y, width, height, dest, 0, false);
     }
 
     _gradientFilter(streamId, x, y, width, height, sock, display, depth) {
diff --git a/systemvm/agent/noVNC/core/decoders/tightpng.js b/systemvm/agent/noVNC/core/decoders/tightpng.js
index 7bbde3a..82f492d 100644
--- a/systemvm/agent/noVNC/core/decoders/tightpng.js
+++ b/systemvm/agent/noVNC/core/decoders/tightpng.js
@@ -1,8 +1,6 @@
 /*
  * noVNC: HTML5 VNC client
- * Copyright (C) 2012 Joel Martin
- * Copyright (C) 2018 Samuel Mannehed for Cendio AB
- * Copyright (C) 2018 Pierre Ossman for Cendio AB
+ * Copyright (C) 2019 The noVNC Authors
  * Licensed under MPL 2.0 (see LICENSE.txt)
  *
  * See README.md for usage and integration instructions.
@@ -18,7 +16,7 @@
             return false;
         }
 
-        display.imageRect(x, y, "image/png", data);
+        display.imageRect(x, y, width, height, "image/png", data);
 
         return true;
     }
diff --git a/systemvm/agent/noVNC/core/deflator.js b/systemvm/agent/noVNC/core/deflator.js
new file mode 100644
index 0000000..fe2a8f7
--- /dev/null
+++ b/systemvm/agent/noVNC/core/deflator.js
@@ -0,0 +1,85 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2020 The noVNC Authors
+ * Licensed under MPL 2.0 (see LICENSE.txt)
+ *
+ * See README.md for usage and integration instructions.
+ */
+
+import { deflateInit, deflate } from "../vendor/pako/lib/zlib/deflate.js";
+import { Z_FULL_FLUSH } from "../vendor/pako/lib/zlib/deflate.js";
+import ZStream from "../vendor/pako/lib/zlib/zstream.js";
+
+export default class Deflator {
+    constructor() {
+        this.strm = new ZStream();
+        this.chunkSize = 1024 * 10 * 10;
+        this.outputBuffer = new Uint8Array(this.chunkSize);
+        this.windowBits = 5;
+
+        deflateInit(this.strm, this.windowBits);
+    }
+
+    deflate(inData) {
+        /* eslint-disable camelcase */
+        this.strm.input = inData;
+        this.strm.avail_in = this.strm.input.length;
+        this.strm.next_in = 0;
+        this.strm.output = this.outputBuffer;
+        this.strm.avail_out = this.chunkSize;
+        this.strm.next_out = 0;
+        /* eslint-enable camelcase */
+
+        let lastRet = deflate(this.strm, Z_FULL_FLUSH);
+        let outData = new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
+
+        if (lastRet < 0) {
+            throw new Error("zlib deflate failed");
+        }
+
+        if (this.strm.avail_in > 0) {
+            // Read chunks until done
+
+            let chunks = [outData];
+            let totalLen = outData.length;
+            do {
+                /* eslint-disable camelcase */
+                this.strm.output = new Uint8Array(this.chunkSize);
+                this.strm.next_out = 0;
+                this.strm.avail_out = this.chunkSize;
+                /* eslint-enable camelcase */
+
+                lastRet = deflate(this.strm, Z_FULL_FLUSH);
+
+                if (lastRet < 0) {
+                    throw new Error("zlib deflate failed");
+                }
+
+                let chunk = new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
+                totalLen += chunk.length;
+                chunks.push(chunk);
+            } while (this.strm.avail_in > 0);
+
+            // Combine chunks into a single data
+
+            let newData = new Uint8Array(totalLen);
+            let offset = 0;
+
+            for (let i = 0; i < chunks.length; i++) {
+                newData.set(chunks[i], offset);
+                offset += chunks[i].length;
+            }
+
+            outData = newData;
+        }
+
+        /* eslint-disable camelcase */
+        this.strm.input = null;
+        this.strm.avail_in = 0;
+        this.strm.next_in = 0;
+        /* eslint-enable camelcase */
+
+        return outData;
+    }
+
+}
diff --git a/systemvm/agent/noVNC/core/display.js b/systemvm/agent/noVNC/core/display.js
index 1528384..8eaa800 100644
--- a/systemvm/agent/noVNC/core/display.js
+++ b/systemvm/agent/noVNC/core/display.js
@@ -1,6 +1,6 @@
 /*
  * noVNC: HTML5 VNC client
- * Copyright (C) 2018 The noVNC Authors
+ * Copyright (C) 2019 The noVNC Authors
  * Licensed under MPL 2.0 (see LICENSE.txt)
  *
  * See README.md for usage and integration instructions.
@@ -9,24 +9,20 @@
 import * as Log from './util/logging.js';
 import Base64 from "./base64.js";
 import { supportsImageMetadata } from './util/browser.js';
+import { toSigned32bit } from './util/int.js';
 
 export default class Display {
     constructor(target) {
         this._drawCtx = null;
-        this._c_forceCanvas = false;
 
         this._renderQ = [];  // queue drawing actions for in-oder rendering
         this._flushing = false;
 
         // the full frame buffer (logical canvas) size
-        this._fb_width = 0;
-        this._fb_height = 0;
+        this._fbWidth = 0;
+        this._fbHeight = 0;
 
         this._prevDrawStyle = "";
-        this._tile = null;
-        this._tile16x16 = null;
-        this._tile_x = 0;
-        this._tile_y = 0;
 
         Log.Debug(">> Display.constructor");
 
@@ -60,21 +56,17 @@
 
         Log.Debug("User Agent: " + navigator.userAgent);
 
-        this.clear();
-
         // Check canvas features
         if (!('createImageData' in this._drawCtx)) {
             throw new Error("Canvas does not support createImageData");
         }
 
-        this._tile16x16 = this._drawCtx.createImageData(16, 16);
         Log.Debug("<< Display.constructor");
 
         // ===== PROPERTIES =====
 
         this._scale = 1.0;
         this._clipViewport = false;
-        this.logo = null;
 
         // ===== EVENT HANDLERS =====
 
@@ -98,11 +90,11 @@
     }
 
     get width() {
-        return this._fb_width;
+        return this._fbWidth;
     }
 
     get height() {
-        return this._fb_height;
+        return this._fbHeight;
     }
 
     // ===== PUBLIC METHODS =====
@@ -125,15 +117,15 @@
         if (deltaX < 0 && vp.x + deltaX < 0) {
             deltaX = -vp.x;
         }
-        if (vx2 + deltaX >= this._fb_width) {
-            deltaX -= vx2 + deltaX - this._fb_width + 1;
+        if (vx2 + deltaX >= this._fbWidth) {
+            deltaX -= vx2 + deltaX - this._fbWidth + 1;
         }
 
         if (vp.y + deltaY < 0) {
             deltaY = -vp.y;
         }
-        if (vy2 + deltaY >= this._fb_height) {
-            deltaY -= (vy2 + deltaY - this._fb_height + 1);
+        if (vy2 + deltaY >= this._fbHeight) {
+            deltaY -= (vy2 + deltaY - this._fbHeight + 1);
         }
 
         if (deltaX === 0 && deltaY === 0) {
@@ -156,18 +148,18 @@
             typeof(height) === "undefined") {
 
             Log.Debug("Setting viewport to full display region");
-            width = this._fb_width;
-            height = this._fb_height;
+            width = this._fbWidth;
+            height = this._fbHeight;
         }
 
         width = Math.floor(width);
         height = Math.floor(height);
 
-        if (width > this._fb_width) {
-            width = this._fb_width;
+        if (width > this._fbWidth) {
+            width = this._fbWidth;
         }
-        if (height > this._fb_height) {
-            height = this._fb_height;
+        if (height > this._fbHeight) {
+            height = this._fbHeight;
         }
 
         const vp = this._viewportLoc;
@@ -194,21 +186,21 @@
         if (this._scale === 0) {
             return 0;
         }
-        return x / this._scale + this._viewportLoc.x;
+        return toSigned32bit(x / this._scale + this._viewportLoc.x);
     }
 
     absY(y) {
         if (this._scale === 0) {
             return 0;
         }
-        return y / this._scale + this._viewportLoc.y;
+        return toSigned32bit(y / this._scale + this._viewportLoc.y);
     }
 
     resize(width, height) {
         this._prevDrawStyle = "";
 
-        this._fb_width = width;
-        this._fb_height = height;
+        this._fbWidth = width;
+        this._fbHeight = height;
 
         const canvas = this._backbuffer;
         if (canvas.width !== width || canvas.height !== height) {
@@ -256,9 +248,9 @@
 
     // Update the visible canvas with the contents of the
     // rendering canvas
-    flip(from_queue) {
-        if (this._renderQ.length !== 0 && !from_queue) {
-            this._renderQ_push({
+    flip(fromQueue) {
+        if (this._renderQ.length !== 0 && !fromQueue) {
+            this._renderQPush({
                 'type': 'flip'
             });
         } else {
@@ -302,17 +294,6 @@
         }
     }
 
-    clear() {
-        if (this._logo) {
-            this.resize(this._logo.width, this._logo.height);
-            this.imageRect(0, 0, this._logo.type, this._logo.data);
-        } else {
-            this.resize(240, 20);
-            this._drawCtx.clearRect(0, 0, this._fb_width, this._fb_height);
-        }
-        this.flip();
-    }
-
     pending() {
         return this._renderQ.length > 0;
     }
@@ -325,9 +306,9 @@
         }
     }
 
-    fillRect(x, y, width, height, color, from_queue) {
-        if (this._renderQ.length !== 0 && !from_queue) {
-            this._renderQ_push({
+    fillRect(x, y, width, height, color, fromQueue) {
+        if (this._renderQ.length !== 0 && !fromQueue) {
+            this._renderQPush({
                 'type': 'fill',
                 'x': x,
                 'y': y,
@@ -342,14 +323,14 @@
         }
     }
 
-    copyImage(old_x, old_y, new_x, new_y, w, h, from_queue) {
-        if (this._renderQ.length !== 0 && !from_queue) {
-            this._renderQ_push({
+    copyImage(oldX, oldY, newX, newY, w, h, fromQueue) {
+        if (this._renderQ.length !== 0 && !fromQueue) {
+            this._renderQPush({
                 'type': 'copy',
-                'old_x': old_x,
-                'old_y': old_y,
-                'x': new_x,
-                'y': new_y,
+                'oldX': oldX,
+                'oldY': oldY,
+                'x': newX,
+                'y': newY,
                 'width': w,
                 'height': h,
             });
@@ -367,131 +348,60 @@
             this._drawCtx.imageSmoothingEnabled = false;
 
             this._drawCtx.drawImage(this._backbuffer,
-                                    old_x, old_y, w, h,
-                                    new_x, new_y, w, h);
-            this._damage(new_x, new_y, w, h);
+                                    oldX, oldY, w, h,
+                                    newX, newY, w, h);
+            this._damage(newX, newY, w, h);
         }
     }
 
-    imageRect(x, y, mime, arr) {
+    imageRect(x, y, width, height, mime, arr) {
+        /* The internal logic cannot handle empty images, so bail early */
+        if ((width === 0) || (height === 0)) {
+            return;
+        }
+
         const img = new Image();
         img.src = "data: " + mime + ";base64," + Base64.encode(arr);
-        this._renderQ_push({
+
+        this._renderQPush({
             'type': 'img',
             'img': img,
             'x': x,
-            'y': y
+            'y': y,
+            'width': width,
+            'height': height
         });
     }
 
-    // start updating a tile
-    startTile(x, y, width, height, color) {
-        this._tile_x = x;
-        this._tile_y = y;
-        if (width === 16 && height === 16) {
-            this._tile = this._tile16x16;
-        } else {
-            this._tile = this._drawCtx.createImageData(width, height);
-        }
-
-        const red = color[2];
-        const green = color[1];
-        const blue = color[0];
-
-        const data = this._tile.data;
-        for (let i = 0; i < width * height * 4; i += 4) {
-            data[i] = red;
-            data[i + 1] = green;
-            data[i + 2] = blue;
-            data[i + 3] = 255;
-        }
-    }
-
-    // update sub-rectangle of the current tile
-    subTile(x, y, w, h, color) {
-        const red = color[2];
-        const green = color[1];
-        const blue = color[0];
-        const xend = x + w;
-        const yend = y + h;
-
-        const data = this._tile.data;
-        const width = this._tile.width;
-        for (let j = y; j < yend; j++) {
-            for (let i = x; i < xend; i++) {
-                const p = (i + (j * width)) * 4;
-                data[p] = red;
-                data[p + 1] = green;
-                data[p + 2] = blue;
-                data[p + 3] = 255;
-            }
-        }
-    }
-
-    // draw the current tile to the screen
-    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(x, y, width, height, arr, offset, from_queue) {
-        if (this._renderQ.length !== 0 && !from_queue) {
+    blitImage(x, y, width, height, arr, offset, fromQueue) {
+        if (this._renderQ.length !== 0 && !fromQueue) {
             // 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,
             // this probably isn't getting called *nearly* as much
-            const new_arr = new Uint8Array(width * height * 4);
-            new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
-            this._renderQ_push({
+            const newArr = new Uint8Array(width * height * 4);
+            newArr.set(new Uint8Array(arr.buffer, 0, newArr.length));
+            this._renderQPush({
                 'type': 'blit',
-                'data': new_arr,
+                'data': newArr,
                 'x': x,
                 'y': y,
                 'width': width,
                 'height': height,
             });
         } else {
-            this._bgrxImageData(x, y, width, height, arr, offset);
-        }
-    }
-
-    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,
-            // this probably isn't getting called *nearly* as much
-            const new_arr = new Uint8Array(width * height * 3);
-            new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
-            this._renderQ_push({
-                'type': 'blitRgb',
-                'data': new_arr,
-                'x': x,
-                'y': y,
-                'width': width,
-                'height': height,
-            });
-        } else {
-            this._rgbImageData(x, y, width, height, arr, offset);
-        }
-    }
-
-    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,
-            // this probably isn't getting called *nearly* as much
-            const new_arr = new Uint8Array(width * height * 4);
-            new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
-            this._renderQ_push({
-                'type': 'blitRgbx',
-                'data': new_arr,
-                'x': x,
-                'y': y,
-                'width': width,
-                'height': height,
-            });
-        } else {
-            this._rgbxImageData(x, y, width, height, arr, offset);
+            // NB(directxman12): arr must be an Type Array view
+            let data = new Uint8ClampedArray(arr.buffer,
+                                             arr.byteOffset + offset,
+                                             width * height * 4);
+            let img;
+            if (supportsImageMetadata) {
+                img = new ImageData(data, width, height);
+            } else {
+                img = this._drawCtx.createImageData(width, height);
+                img.data.set(data);
+            }
+            this._drawCtx.putImageData(img, x, y);
+            this._damage(x, y, width, height);
         }
     }
 
@@ -543,69 +453,30 @@
     }
 
     _setFillColor(color) {
-        const newStyle = 'rgb(' + color[2] + ',' + color[1] + ',' + color[0] + ')';
+        const newStyle = 'rgb(' + color[0] + ',' + color[1] + ',' + color[2] + ')';
         if (newStyle !== this._prevDrawStyle) {
             this._drawCtx.fillStyle = newStyle;
             this._prevDrawStyle = newStyle;
         }
     }
 
-    _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) {
-            data[i]     = arr[j];
-            data[i + 1] = arr[j + 1];
-            data[i + 2] = arr[j + 2];
-            data[i + 3] = 255;  // Alpha
-        }
-        this._drawCtx.putImageData(img, x, y);
-        this._damage(x, y, img.width, img.height);
-    }
-
-    _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) {
-            data[i]     = arr[j + 2];
-            data[i + 1] = arr[j + 1];
-            data[i + 2] = arr[j];
-            data[i + 3] = 255;  // Alpha
-        }
-        this._drawCtx.putImageData(img, x, y);
-        this._damage(x, y, img.width, img.height);
-    }
-
-    _rgbxImageData(x, y, width, height, arr, offset) {
-        // NB(directxman12): arr must be an Type Array view
-        let img;
-        if (supportsImageMetadata) {
-            img = new ImageData(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4), width, height);
-        } else {
-            img = this._drawCtx.createImageData(width, height);
-            img.data.set(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4));
-        }
-        this._drawCtx.putImageData(img, x, y);
-        this._damage(x, y, img.width, img.height);
-    }
-
-    _renderQ_push(action) {
+    _renderQPush(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();
+            this._scanRenderQ();
         }
     }
 
-    _resume_renderQ() {
+    _resumeRenderQ() {
         // "this" is the object that is ready, not the
         // display object
-        this.removeEventListener('load', this._noVNC_display._resume_renderQ);
-        this._noVNC_display._scan_renderQ();
+        this.removeEventListener('load', this._noVNCDisplay._resumeRenderQ);
+        this._noVNCDisplay._scanRenderQ();
     }
 
-    _scan_renderQ() {
+    _scanRenderQ() {
         let ready = true;
         while (ready && this._renderQ.length > 0) {
             const a = this._renderQ[0];
@@ -614,7 +485,7 @@
                     this.flip(true);
                     break;
                 case 'copy':
-                    this.copyImage(a.old_x, a.old_y, a.x, a.y, a.width, a.height, true);
+                    this.copyImage(a.oldX, a.oldY, a.x, a.y, a.width, a.height, true);
                     break;
                 case 'fill':
                     this.fillRect(a.x, a.y, a.width, a.height, a.color, true);
@@ -622,18 +493,19 @@
                 case 'blit':
                     this.blitImage(a.x, a.y, a.width, a.height, a.data, 0, true);
                     break;
-                case 'blitRgb':
-                    this.blitRgbImage(a.x, a.y, a.width, a.height, a.data, 0, true);
-                    break;
-                case 'blitRgbx':
-                    this.blitRgbxImage(a.x, a.y, a.width, a.height, a.data, 0, true);
-                    break;
                 case 'img':
-                    if (a.img.complete) {
+                    /* IE tends to set "complete" prematurely, so check dimensions */
+                    if (a.img.complete && (a.img.width !== 0) && (a.img.height !== 0)) {
+                        if (a.img.width !== a.width || a.img.height !== a.height) {
+                            Log.Error("Decoded image has incorrect dimensions. Got " +
+                                      a.img.width + "x" + a.img.height + ". Expected " +
+                                      a.width + "x" + a.height + ".");
+                            return;
+                        }
                         this.drawImage(a.img, a.x, a.y);
                     } else {
-                        a.img._noVNC_display = this;
-                        a.img.addEventListener('load', this._resume_renderQ);
+                        a.img._noVNCDisplay = this;
+                        a.img.addEventListener('load', this._resumeRenderQ);
                         // We need to wait for this image to 'load'
                         // to keep things in-order
                         ready = false;
diff --git a/systemvm/agent/noVNC/core/encodings.js b/systemvm/agent/noVNC/core/encodings.js
index 9fd38d5..51c0992 100644
--- a/systemvm/agent/noVNC/core/encodings.js
+++ b/systemvm/agent/noVNC/core/encodings.js
@@ -1,6 +1,6 @@
 /*
  * noVNC: HTML5 VNC client
- * Copyright (C) 2018 The noVNC Authors
+ * Copyright (C) 2019 The noVNC Authors
  * Licensed under MPL 2.0 (see LICENSE.txt)
  *
  * See README.md for usage and integration instructions.
@@ -20,12 +20,15 @@
     pseudoEncodingLastRect: -224,
     pseudoEncodingCursor: -239,
     pseudoEncodingQEMUExtendedKeyEvent: -258,
+    pseudoEncodingDesktopName: -307,
     pseudoEncodingExtendedDesktopSize: -308,
     pseudoEncodingXvp: -309,
     pseudoEncodingFence: -312,
     pseudoEncodingContinuousUpdates: -313,
     pseudoEncodingCompressLevel9: -247,
     pseudoEncodingCompressLevel0: -256,
+    pseudoEncodingVMwareCursor: 0x574d5664,
+    pseudoEncodingExtendedClipboard: 0xc0a1e5ce
 };
 
 export function encodingName(num) {
diff --git a/systemvm/agent/noVNC/core/inflator.js b/systemvm/agent/noVNC/core/inflator.js
index 0eab8fe..4b33760 100644
--- a/systemvm/agent/noVNC/core/inflator.js
+++ b/systemvm/agent/noVNC/core/inflator.js
@@ -1,3 +1,11 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2020 The noVNC Authors
+ * Licensed under MPL 2.0 (see LICENSE.txt)
+ *
+ * See README.md for usage and integration instructions.
+ */
+
 import { inflateInit, inflate, inflateReset } from "../vendor/pako/lib/zlib/inflate.js";
 import ZStream from "../vendor/pako/lib/zlib/zstream.js";
 
@@ -11,12 +19,22 @@
         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;
-        this.strm.next_out = 0;
+    setInput(data) {
+        if (!data) {
+            //FIXME: flush remaining data.
+            /* eslint-disable camelcase */
+            this.strm.input = null;
+            this.strm.avail_in = 0;
+            this.strm.next_in = 0;
+        } else {
+            this.strm.input = data;
+            this.strm.avail_in = this.strm.input.length;
+            this.strm.next_in = 0;
+            /* eslint-enable camelcase */
+        }
+    }
 
+    inflate(expected) {
         // resize our output buffer if it's too small
         // (we could just use multiple chunks, but that would cause an extra
         // allocation each time to flatten the chunks)
@@ -25,9 +43,19 @@
             this.strm.output = new Uint8Array(this.chunkSize);
         }
 
-        this.strm.avail_out = this.chunkSize;
+        /* eslint-disable camelcase */
+        this.strm.next_out = 0;
+        this.strm.avail_out = expected;
+        /* eslint-enable camelcase */
 
-        inflate(this.strm, flush);
+        let ret = inflate(this.strm, 0); // Flush argument not used.
+        if (ret < 0) {
+            throw new Error("zlib inflate failed");
+        }
+
+        if (this.strm.next_out != expected) {
+            throw new Error("Incomplete zlib block");
+        }
 
         return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
     }
diff --git a/systemvm/agent/noVNC/core/input/domkeytable.js b/systemvm/agent/noVNC/core/input/domkeytable.js
index 60ae3f9..b84ad45 100644
--- a/systemvm/agent/noVNC/core/input/domkeytable.js
+++ b/systemvm/agent/noVNC/core/input/domkeytable.js
@@ -43,12 +43,10 @@
 addLeftRight("Control", KeyTable.XK_Control_L, KeyTable.XK_Control_R);
 // - Fn
 // - FnLock
-addLeftRight("Hyper", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
 addLeftRight("Meta", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
 addStandard("NumLock", KeyTable.XK_Num_Lock);
 addStandard("ScrollLock", KeyTable.XK_Scroll_Lock);
 addLeftRight("Shift", KeyTable.XK_Shift_L, KeyTable.XK_Shift_R);
-addLeftRight("Super", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
 // - Symbol
 // - SymbolLock
 
@@ -72,6 +70,9 @@
 // 2.5. Editing Keys
 
 addStandard("Backspace", KeyTable.XK_BackSpace);
+// Browsers send "Clear" for the numpad 5 without NumLock because
+// Windows uses VK_Clear for that key. But Unix expects KP_Begin for
+// that scenario.
 addNumpad("Clear", KeyTable.XK_Clear, KeyTable.XK_KP_Begin);
 addStandard("Copy", KeyTable.XF86XK_Copy);
 // - CrSel
@@ -194,7 +195,8 @@
 addStandard("Close", KeyTable.XF86XK_Close);
 addStandard("MailForward", KeyTable.XF86XK_MailForward);
 addStandard("MailReply", KeyTable.XF86XK_Reply);
-addStandard("MainSend", KeyTable.XF86XK_Send);
+addStandard("MailSend", KeyTable.XF86XK_Send);
+// - MediaClose
 addStandard("MediaFastForward", KeyTable.XF86XK_AudioForward);
 addStandard("MediaPause", KeyTable.XF86XK_AudioPause);
 addStandard("MediaPlay", KeyTable.XF86XK_AudioPlay);
@@ -218,11 +220,9 @@
 
 // - AudioBalanceLeft
 // - AudioBalanceRight
-// - AudioBassDown
 // - AudioBassBoostDown
 // - AudioBassBoostToggle
 // - AudioBassBoostUp
-// - AudioBassUp
 // - AudioFaderFront
 // - AudioFaderRear
 // - AudioSurroundModeNext
@@ -243,12 +243,12 @@
 
 // 2.14. Application Keys
 
-addStandard("LaunchCalculator", KeyTable.XF86XK_Calculator);
+addStandard("LaunchApplication1", KeyTable.XF86XK_MyComputer);
+addStandard("LaunchApplication2", KeyTable.XF86XK_Calculator);
 addStandard("LaunchCalendar", KeyTable.XF86XK_Calendar);
 addStandard("LaunchMail", KeyTable.XF86XK_Mail);
 addStandard("LaunchMediaPlayer", KeyTable.XF86XK_AudioMedia);
 addStandard("LaunchMusicPlayer", KeyTable.XF86XK_Music);
-addStandard("LaunchMyComputer", KeyTable.XF86XK_MyComputer);
 addStandard("LaunchPhone", KeyTable.XF86XK_Phone);
 addStandard("LaunchScreenSaver", KeyTable.XF86XK_ScreenSaver);
 addStandard("LaunchSpreadsheet", KeyTable.XF86XK_Excel);
diff --git a/systemvm/agent/noVNC/core/input/gesturehandler.js b/systemvm/agent/noVNC/core/input/gesturehandler.js
new file mode 100644
index 0000000..6fa72d2
--- /dev/null
+++ b/systemvm/agent/noVNC/core/input/gesturehandler.js
@@ -0,0 +1,567 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2020 The noVNC Authors
+ * Licensed under MPL 2.0 (see LICENSE.txt)
+ *
+ * See README.md for usage and integration instructions.
+ *
+ */
+
+const GH_NOGESTURE = 0;
+const GH_ONETAP    = 1;
+const GH_TWOTAP    = 2;
+const GH_THREETAP  = 4;
+const GH_DRAG      = 8;
+const GH_LONGPRESS = 16;
+const GH_TWODRAG   = 32;
+const GH_PINCH     = 64;
+
+const GH_INITSTATE = 127;
+
+const GH_MOVE_THRESHOLD = 50;
+const GH_ANGLE_THRESHOLD = 90; // Degrees
+
+// Timeout when waiting for gestures (ms)
+const GH_MULTITOUCH_TIMEOUT = 250;
+
+// Maximum time between press and release for a tap (ms)
+const GH_TAP_TIMEOUT = 1000;
+
+// Timeout when waiting for longpress (ms)
+const GH_LONGPRESS_TIMEOUT = 1000;
+
+// Timeout when waiting to decide between PINCH and TWODRAG (ms)
+const GH_TWOTOUCH_TIMEOUT = 50;
+
+export default class GestureHandler {
+    constructor() {
+        this._target = null;
+
+        this._state = GH_INITSTATE;
+
+        this._tracked = [];
+        this._ignored = [];
+
+        this._waitingRelease = false;
+        this._releaseStart = 0.0;
+
+        this._longpressTimeoutId = null;
+        this._twoTouchTimeoutId = null;
+
+        this._boundEventHandler = this._eventHandler.bind(this);
+    }
+
+    attach(target) {
+        this.detach();
+
+        this._target = target;
+        this._target.addEventListener('touchstart',
+                                      this._boundEventHandler);
+        this._target.addEventListener('touchmove',
+                                      this._boundEventHandler);
+        this._target.addEventListener('touchend',
+                                      this._boundEventHandler);
+        this._target.addEventListener('touchcancel',
+                                      this._boundEventHandler);
+    }
+
+    detach() {
+        if (!this._target) {
+            return;
+        }
+
+        this._stopLongpressTimeout();
+        this._stopTwoTouchTimeout();
+
+        this._target.removeEventListener('touchstart',
+                                         this._boundEventHandler);
+        this._target.removeEventListener('touchmove',
+                                         this._boundEventHandler);
+        this._target.removeEventListener('touchend',
+                                         this._boundEventHandler);
+        this._target.removeEventListener('touchcancel',
+                                         this._boundEventHandler);
+        this._target = null;
+    }
+
+    _eventHandler(e) {
+        let fn;
+
+        e.stopPropagation();
+        e.preventDefault();
+
+        switch (e.type) {
+            case 'touchstart':
+                fn = this._touchStart;
+                break;
+            case 'touchmove':
+                fn = this._touchMove;
+                break;
+            case 'touchend':
+            case 'touchcancel':
+                fn = this._touchEnd;
+                break;
+        }
+
+        for (let i = 0; i < e.changedTouches.length; i++) {
+            let touch = e.changedTouches[i];
+            fn.call(this, touch.identifier, touch.clientX, touch.clientY);
+        }
+    }
+
+    _touchStart(id, x, y) {
+        // Ignore any new touches if there is already an active gesture,
+        // or we're in a cleanup state
+        if (this._hasDetectedGesture() || (this._state === GH_NOGESTURE)) {
+            this._ignored.push(id);
+            return;
+        }
+
+        // Did it take too long between touches that we should no longer
+        // consider this a single gesture?
+        if ((this._tracked.length > 0) &&
+            ((Date.now() - this._tracked[0].started) > GH_MULTITOUCH_TIMEOUT)) {
+            this._state = GH_NOGESTURE;
+            this._ignored.push(id);
+            return;
+        }
+
+        // If we're waiting for fingers to release then we should no longer
+        // recognize new touches
+        if (this._waitingRelease) {
+            this._state = GH_NOGESTURE;
+            this._ignored.push(id);
+            return;
+        }
+
+        this._tracked.push({
+            id: id,
+            started: Date.now(),
+            active: true,
+            firstX: x,
+            firstY: y,
+            lastX: x,
+            lastY: y,
+            angle: 0
+        });
+
+        switch (this._tracked.length) {
+            case 1:
+                this._startLongpressTimeout();
+                break;
+
+            case 2:
+                this._state &= ~(GH_ONETAP | GH_DRAG | GH_LONGPRESS);
+                this._stopLongpressTimeout();
+                break;
+
+            case 3:
+                this._state &= ~(GH_TWOTAP | GH_TWODRAG | GH_PINCH);
+                break;
+
+            default:
+                this._state = GH_NOGESTURE;
+        }
+    }
+
+    _touchMove(id, x, y) {
+        let touch = this._tracked.find(t => t.id === id);
+
+        // If this is an update for a touch we're not tracking, ignore it
+        if (touch === undefined) {
+            return;
+        }
+
+        // Update the touches last position with the event coordinates
+        touch.lastX = x;
+        touch.lastY = y;
+
+        let deltaX = x - touch.firstX;
+        let deltaY = y - touch.firstY;
+
+        // Update angle when the touch has moved
+        if ((touch.firstX !== touch.lastX) ||
+            (touch.firstY !== touch.lastY)) {
+            touch.angle = Math.atan2(deltaY, deltaX) * 180 / Math.PI;
+        }
+
+        if (!this._hasDetectedGesture()) {
+            // Ignore moves smaller than the minimum threshold
+            if (Math.hypot(deltaX, deltaY) < GH_MOVE_THRESHOLD) {
+                return;
+            }
+
+            // Can't be a tap or long press as we've seen movement
+            this._state &= ~(GH_ONETAP | GH_TWOTAP | GH_THREETAP | GH_LONGPRESS);
+            this._stopLongpressTimeout();
+
+            if (this._tracked.length !== 1) {
+                this._state &= ~(GH_DRAG);
+            }
+            if (this._tracked.length !== 2) {
+                this._state &= ~(GH_TWODRAG | GH_PINCH);
+            }
+
+            // We need to figure out which of our different two touch gestures
+            // this might be
+            if (this._tracked.length === 2) {
+
+                // The other touch is the one where the id doesn't match
+                let prevTouch = this._tracked.find(t => t.id !== id);
+
+                // How far the previous touch point has moved since start
+                let prevDeltaMove = Math.hypot(prevTouch.firstX - prevTouch.lastX,
+                                               prevTouch.firstY - prevTouch.lastY);
+
+                // We know that the current touch moved far enough,
+                // but unless both touches moved further than their
+                // threshold we don't want to disqualify any gestures
+                if (prevDeltaMove > GH_MOVE_THRESHOLD) {
+
+                    // The angle difference between the direction of the touch points
+                    let deltaAngle = Math.abs(touch.angle - prevTouch.angle);
+                    deltaAngle = Math.abs(((deltaAngle + 180) % 360) - 180);
+
+                    // PINCH or TWODRAG can be eliminated depending on the angle
+                    if (deltaAngle > GH_ANGLE_THRESHOLD) {
+                        this._state &= ~GH_TWODRAG;
+                    } else {
+                        this._state &= ~GH_PINCH;
+                    }
+
+                    if (this._isTwoTouchTimeoutRunning()) {
+                        this._stopTwoTouchTimeout();
+                    }
+                } else if (!this._isTwoTouchTimeoutRunning()) {
+                    // We can't determine the gesture right now, let's
+                    // wait and see if more events are on their way
+                    this._startTwoTouchTimeout();
+                }
+            }
+
+            if (!this._hasDetectedGesture()) {
+                return;
+            }
+
+            this._pushEvent('gesturestart');
+        }
+
+        this._pushEvent('gesturemove');
+    }
+
+    _touchEnd(id, x, y) {
+        // Check if this is an ignored touch
+        if (this._ignored.indexOf(id) !== -1) {
+            // Remove this touch from ignored
+            this._ignored.splice(this._ignored.indexOf(id), 1);
+
+            // And reset the state if there are no more touches
+            if ((this._ignored.length === 0) &&
+                (this._tracked.length === 0)) {
+                this._state = GH_INITSTATE;
+                this._waitingRelease = false;
+            }
+            return;
+        }
+
+        // We got a touchend before the timer triggered,
+        // this cannot result in a gesture anymore.
+        if (!this._hasDetectedGesture() &&
+            this._isTwoTouchTimeoutRunning()) {
+            this._stopTwoTouchTimeout();
+            this._state = GH_NOGESTURE;
+        }
+
+        // Some gestures don't trigger until a touch is released
+        if (!this._hasDetectedGesture()) {
+            // Can't be a gesture that relies on movement
+            this._state &= ~(GH_DRAG | GH_TWODRAG | GH_PINCH);
+            // Or something that relies on more time
+            this._state &= ~GH_LONGPRESS;
+            this._stopLongpressTimeout();
+
+            if (!this._waitingRelease) {
+                this._releaseStart = Date.now();
+                this._waitingRelease = true;
+
+                // Can't be a tap that requires more touches than we current have
+                switch (this._tracked.length) {
+                    case 1:
+                        this._state &= ~(GH_TWOTAP | GH_THREETAP);
+                        break;
+
+                    case 2:
+                        this._state &= ~(GH_ONETAP | GH_THREETAP);
+                        break;
+                }
+            }
+        }
+
+        // Waiting for all touches to release? (i.e. some tap)
+        if (this._waitingRelease) {
+            // Were all touches released at roughly the same time?
+            if ((Date.now() - this._releaseStart) > GH_MULTITOUCH_TIMEOUT) {
+                this._state = GH_NOGESTURE;
+            }
+
+            // Did too long time pass between press and release?
+            if (this._tracked.some(t => (Date.now() - t.started) > GH_TAP_TIMEOUT)) {
+                this._state = GH_NOGESTURE;
+            }
+
+            let touch = this._tracked.find(t => t.id === id);
+            touch.active = false;
+
+            // Are we still waiting for more releases?
+            if (this._hasDetectedGesture()) {
+                this._pushEvent('gesturestart');
+            } else {
+                // Have we reached a dead end?
+                if (this._state !== GH_NOGESTURE) {
+                    return;
+                }
+            }
+        }
+
+        if (this._hasDetectedGesture()) {
+            this._pushEvent('gestureend');
+        }
+
+        // Ignore any remaining touches until they are ended
+        for (let i = 0; i < this._tracked.length; i++) {
+            if (this._tracked[i].active) {
+                this._ignored.push(this._tracked[i].id);
+            }
+        }
+        this._tracked = [];
+
+        this._state = GH_NOGESTURE;
+
+        // Remove this touch from ignored if it's in there
+        if (this._ignored.indexOf(id) !== -1) {
+            this._ignored.splice(this._ignored.indexOf(id), 1);
+        }
+
+        // We reset the state if ignored is empty
+        if ((this._ignored.length === 0)) {
+            this._state = GH_INITSTATE;
+            this._waitingRelease = false;
+        }
+    }
+
+    _hasDetectedGesture() {
+        if (this._state === GH_NOGESTURE) {
+            return false;
+        }
+        // Check to see if the bitmask value is a power of 2
+        // (i.e. only one bit set). If it is, we have a state.
+        if (this._state & (this._state - 1)) {
+            return false;
+        }
+
+        // For taps we also need to have all touches released
+        // before we've fully detected the gesture
+        if (this._state & (GH_ONETAP | GH_TWOTAP | GH_THREETAP)) {
+            if (this._tracked.some(t => t.active)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    _startLongpressTimeout() {
+        this._stopLongpressTimeout();
+        this._longpressTimeoutId = setTimeout(() => this._longpressTimeout(),
+                                              GH_LONGPRESS_TIMEOUT);
+    }
+
+    _stopLongpressTimeout() {
+        clearTimeout(this._longpressTimeoutId);
+        this._longpressTimeoutId = null;
+    }
+
+    _longpressTimeout() {
+        if (this._hasDetectedGesture()) {
+            throw new Error("A longpress gesture failed, conflict with a different gesture");
+        }
+
+        this._state = GH_LONGPRESS;
+        this._pushEvent('gesturestart');
+    }
+
+    _startTwoTouchTimeout() {
+        this._stopTwoTouchTimeout();
+        this._twoTouchTimeoutId = setTimeout(() => this._twoTouchTimeout(),
+                                             GH_TWOTOUCH_TIMEOUT);
+    }
+
+    _stopTwoTouchTimeout() {
+        clearTimeout(this._twoTouchTimeoutId);
+        this._twoTouchTimeoutId = null;
+    }
+
+    _isTwoTouchTimeoutRunning() {
+        return this._twoTouchTimeoutId !== null;
+    }
+
+    _twoTouchTimeout() {
+        if (this._tracked.length === 0) {
+            throw new Error("A pinch or two drag gesture failed, no tracked touches");
+        }
+
+        // How far each touch point has moved since start
+        let avgM = this._getAverageMovement();
+        let avgMoveH = Math.abs(avgM.x);
+        let avgMoveV = Math.abs(avgM.y);
+
+        // The difference in the distance between where
+        // the touch points started and where they are now
+        let avgD = this._getAverageDistance();
+        let deltaTouchDistance = Math.abs(Math.hypot(avgD.first.x, avgD.first.y) -
+                                          Math.hypot(avgD.last.x, avgD.last.y));
+
+        if ((avgMoveV < deltaTouchDistance) &&
+            (avgMoveH < deltaTouchDistance)) {
+            this._state = GH_PINCH;
+        } else {
+            this._state = GH_TWODRAG;
+        }
+
+        this._pushEvent('gesturestart');
+        this._pushEvent('gesturemove');
+    }
+
+    _pushEvent(type) {
+        let detail = { type: this._stateToGesture(this._state) };
+
+        // For most gesture events the current (average) position is the
+        // most useful
+        let avg = this._getPosition();
+        let pos = avg.last;
+
+        // However we have a slight distance to detect gestures, so for the
+        // first gesture event we want to use the first positions we saw
+        if (type === 'gesturestart') {
+            pos = avg.first;
+        }
+
+        // For these gestures, we always want the event coordinates
+        // to be where the gesture began, not the current touch location.
+        switch (this._state) {
+            case GH_TWODRAG:
+            case GH_PINCH:
+                pos = avg.first;
+                break;
+        }
+
+        detail['clientX'] = pos.x;
+        detail['clientY'] = pos.y;
+
+        // FIXME: other coordinates?
+
+        // Some gestures also have a magnitude
+        if (this._state === GH_PINCH) {
+            let distance = this._getAverageDistance();
+            if (type === 'gesturestart') {
+                detail['magnitudeX'] = distance.first.x;
+                detail['magnitudeY'] = distance.first.y;
+            } else {
+                detail['magnitudeX'] = distance.last.x;
+                detail['magnitudeY'] = distance.last.y;
+            }
+        } else if (this._state === GH_TWODRAG) {
+            if (type === 'gesturestart') {
+                detail['magnitudeX'] = 0.0;
+                detail['magnitudeY'] = 0.0;
+            } else {
+                let movement = this._getAverageMovement();
+                detail['magnitudeX'] = movement.x;
+                detail['magnitudeY'] = movement.y;
+            }
+        }
+
+        let gev = new CustomEvent(type, { detail: detail });
+        this._target.dispatchEvent(gev);
+    }
+
+    _stateToGesture(state) {
+        switch (state) {
+            case GH_ONETAP:
+                return 'onetap';
+            case GH_TWOTAP:
+                return 'twotap';
+            case GH_THREETAP:
+                return 'threetap';
+            case GH_DRAG:
+                return 'drag';
+            case GH_LONGPRESS:
+                return 'longpress';
+            case GH_TWODRAG:
+                return 'twodrag';
+            case GH_PINCH:
+                return 'pinch';
+        }
+
+        throw new Error("Unknown gesture state: " + state);
+    }
+
+    _getPosition() {
+        if (this._tracked.length === 0) {
+            throw new Error("Failed to get gesture position, no tracked touches");
+        }
+
+        let size = this._tracked.length;
+        let fx = 0, fy = 0, lx = 0, ly = 0;
+
+        for (let i = 0; i < this._tracked.length; i++) {
+            fx += this._tracked[i].firstX;
+            fy += this._tracked[i].firstY;
+            lx += this._tracked[i].lastX;
+            ly += this._tracked[i].lastY;
+        }
+
+        return { first: { x: fx / size,
+                          y: fy / size },
+                 last: { x: lx / size,
+                         y: ly / size } };
+    }
+
+    _getAverageMovement() {
+        if (this._tracked.length === 0) {
+            throw new Error("Failed to get gesture movement, no tracked touches");
+        }
+
+        let totalH, totalV;
+        totalH = totalV = 0;
+        let size = this._tracked.length;
+
+        for (let i = 0; i < this._tracked.length; i++) {
+            totalH += this._tracked[i].lastX - this._tracked[i].firstX;
+            totalV += this._tracked[i].lastY - this._tracked[i].firstY;
+        }
+
+        return { x: totalH / size,
+                 y: totalV / size };
+    }
+
+    _getAverageDistance() {
+        if (this._tracked.length === 0) {
+            throw new Error("Failed to get gesture distance, no tracked touches");
+        }
+
+        // Distance between the first and last tracked touches
+
+        let first = this._tracked[0];
+        let last = this._tracked[this._tracked.length - 1];
+
+        let fdx = Math.abs(last.firstX - first.firstX);
+        let fdy = Math.abs(last.firstY - first.firstY);
+
+        let ldx = Math.abs(last.lastX - first.lastX);
+        let ldy = Math.abs(last.lastY - first.lastY);
+
+        return { first: { x: fdx, y: fdy },
+                 last: { x: ldx, y: ldy } };
+    }
+}
diff --git a/systemvm/agent/noVNC/core/input/keyboard.js b/systemvm/agent/noVNC/core/input/keyboard.js
index 9dbc8d6..9e6af2a 100644
--- a/systemvm/agent/noVNC/core/input/keyboard.js
+++ b/systemvm/agent/noVNC/core/input/keyboard.js
@@ -1,6 +1,6 @@
 /*
  * noVNC: HTML5 VNC client
- * Copyright (C) 2018 The noVNC Authors
+ * Copyright (C) 2019 The noVNC Authors
  * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
  */
 
@@ -118,9 +118,7 @@
 
         // We cannot handle keys we cannot track, but we also need
         // to deal with virtual keyboards which omit key info
-        // (iOS omits tracking info on keyup events, which forces us to
-        // special treat that platform here)
-        if ((code === 'Unidentified') || browser.isIOS()) {
+        if (code === 'Unidentified') {
             if (keysym) {
                 // If it's a virtual keyboard then it should be
                 // sufficient to just send press and release right
@@ -137,7 +135,7 @@
         // keys around a bit to make things more sane for the remote
         // server. This method is used by RealVNC and TigerVNC (and
         // possibly others).
-        if (browser.isMac()) {
+        if (browser.isMac() || browser.isIOS()) {
             switch (keysym) {
                 case KeyTable.XK_Super_L:
                     keysym = KeyTable.XK_Alt_L;
@@ -164,7 +162,7 @@
         // state change events. That gets extra confusing for CapsLock
         // which toggles on each press, but not on release. So pretend
         // it was a quick press and release of the button.
-        if (browser.isMac() && (code === 'CapsLock')) {
+        if ((browser.isMac() || browser.isIOS()) && (code === 'CapsLock')) {
             this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);
             this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);
             stopEvent(e);
@@ -276,13 +274,28 @@
         }
 
         // See comment in _handleKeyDown()
-        if (browser.isMac() && (code === 'CapsLock')) {
+        if ((browser.isMac() || browser.isIOS()) && (code === 'CapsLock')) {
             this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);
             this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);
             return;
         }
 
         this._sendKeyEvent(this._keyDownList[code], code, false);
+
+        // Windows has a rather nasty bug where it won't send key
+        // release events for a Shift button if the other Shift is still
+        // pressed
+        if (browser.isWindows() && ((code === 'ShiftLeft') ||
+                                    (code === 'ShiftRight'))) {
+            if ('ShiftRight' in this._keyDownList) {
+                this._sendKeyEvent(this._keyDownList['ShiftRight'],
+                                   'ShiftRight', false);
+            }
+            if ('ShiftLeft' in this._keyDownList) {
+                this._sendKeyEvent(this._keyDownList['ShiftLeft'],
+                                   'ShiftLeft', false);
+            }
+        }
     }
 
     _handleAltGrTimeout() {
@@ -299,8 +312,11 @@
         Log.Debug("<< Keyboard.allKeysUp");
     }
 
-    // Firefox Alt workaround, see below
+    // Alt workaround for Firefox on Windows, see below
     _checkAlt(e) {
+        if (e.skipCheckAlt) {
+            return;
+        }
         if (e.altKey) {
             return;
         }
@@ -315,6 +331,7 @@
             const event = new KeyboardEvent('keyup',
                                             { key: downList[code],
                                               code: code });
+            event.skipCheckAlt = true;
             target.dispatchEvent(event);
         });
     }
@@ -331,9 +348,10 @@
         // Release (key up) if window loses focus
         window.addEventListener('blur', this._eventHandlers.blur);
 
-        // Firefox has broken handling of Alt, so we need to poll as
-        // best we can for releases (still doesn't prevent the menu
-        // from popping up though as we can't call preventDefault())
+        // Firefox on Windows has broken handling of Alt, so we need to
+        // poll as best we can for releases (still doesn't prevent the
+        // menu from popping up though as we can't call
+        // preventDefault())
         if (browser.isWindows() && browser.isFirefox()) {
             const handler = this._eventHandlers.checkalt;
             ['mousedown', 'mouseup', 'mousemove', 'wheel',
diff --git a/systemvm/agent/noVNC/core/input/mouse.js b/systemvm/agent/noVNC/core/input/mouse.js
deleted file mode 100644
index 58a2982..0000000
--- a/systemvm/agent/noVNC/core/input/mouse.js
+++ /dev/null
@@ -1,276 +0,0 @@
-/*
- * noVNC: HTML5 VNC client
- * Copyright (C) 2018 The noVNC Authors
- * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
- */
-
-import * as Log from '../util/logging.js';
-import { isTouchDevice } from '../util/browser.js';
-import { setCapture, stopEvent, getPointerEvent } from '../util/events.js';
-
-const WHEEL_STEP = 10; // Delta threshold for a mouse wheel step
-const WHEEL_STEP_TIMEOUT = 50; // ms
-const WHEEL_LINE_HEIGHT = 19;
-
-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)
-        };
-
-        // ===== PROPERTIES =====
-
-        this.touchButton = 1;                 // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
-
-        // ===== EVENT HANDLERS =====
-
-        this.onmousebutton = () => {}; // Handler for mouse button click/release
-        this.onmousemove = () => {}; // Handler for mouse movement
-    }
-
-    // ===== PRIVATE METHODS =====
-
-    _resetDoubleClickTimer() {
-        this._doubleClickTimer = null;
-    }
-
-    _handleMouseButton(e, down) {
-        this._updateMousePosition(e);
-        let pos = this._pos;
-
-        let bmask;
-        if (e.touches || e.changedTouches) {
-            // Touch device
-
-            // When two touches occur within 500 ms of each other and are
-            // close enough together a double click is triggered.
-            if (down == 1) {
-                if (this._doubleClickTimer === null) {
-                    this._lastTouchPos = pos;
-                } else {
-                    clearTimeout(this._doubleClickTimer);
-
-                    // When the distance between the two touches is small enough
-                    // force the position of the latter touch to the position of
-                    // the first.
-
-                    const xs = this._lastTouchPos.x - pos.x;
-                    const ys = this._lastTouchPos.y - pos.y;
-                    const d = Math.sqrt((xs * xs) + (ys * ys));
-
-                    // The goal is to trigger on a certain physical width, the
-                    // devicePixelRatio brings us a bit closer but is not optimal.
-                    const threshold = 20 * (window.devicePixelRatio || 1);
-                    if (d < threshold) {
-                        pos = this._lastTouchPos;
-                    }
-                }
-                this._doubleClickTimer = setTimeout(this._resetDoubleClickTimer.bind(this), 500);
-            }
-            bmask = this.touchButton;
-            // If bmask is set
-        } else if (e.which) {
-            /* everything except IE */
-            bmask = 1 << e.button;
-        } else {
-            /* IE including 9 */
-            bmask = (e.button & 0x1) +      // Left
-                    (e.button & 0x2) * 2 +  // Right
-                    (e.button & 0x4) / 2;   // Middle
-        }
-
-        Log.Debug("onmousebutton " + (down ? "down" : "up") +
-                  ", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
-        this.onmousebutton(pos.x, pos.y, down, bmask);
-
-        stopEvent(e);
-    }
-
-    _handleMouseDown(e) {
-        // Touch events have implicit capture
-        if (e.type === "mousedown") {
-            setCapture(this._target);
-        }
-
-        this._handleMouseButton(e, 1);
-    }
-
-    _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() {
-
-        if (this._accumulatedWheelDeltaX < 0) {
-            this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 5);
-            this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 5);
-        } else if (this._accumulatedWheelDeltaX > 0) {
-            this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 6);
-            this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 6);
-        }
-
-        this._accumulatedWheelDeltaX = 0;
-    }
-
-    _generateWheelStepY() {
-
-        if (this._accumulatedWheelDeltaY < 0) {
-            this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 3);
-            this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 3);
-        } else if (this._accumulatedWheelDeltaY > 0) {
-            this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 4);
-            this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 4);
-        }
-
-        this._accumulatedWheelDeltaY = 0;
-    }
-
-    _resetWheelStepTimers() {
-        window.clearTimeout(this._wheelStepXTimer);
-        window.clearTimeout(this._wheelStepYTimer);
-        this._wheelStepXTimer = null;
-        this._wheelStepYTimer = null;
-    }
-
-    _handleMouseWheel(e) {
-        this._resetWheelStepTimers();
-
-        this._updateMousePosition(e);
-
-        let dX = e.deltaX;
-        let dY = e.deltaY;
-
-        // Pixel units unless it's non-zero.
-        // Note that if deltamode is line or page won't matter since we aren't
-        // sending the mouse wheel delta to the server anyway.
-        // The difference between pixel and line can be important however since
-        // we have a threshold that can be smaller than the line height.
-        if (e.deltaMode !== 0) {
-            dX *= WHEEL_LINE_HEIGHT;
-            dY *= WHEEL_LINE_HEIGHT;
-        }
-
-        this._accumulatedWheelDeltaX += dX;
-        this._accumulatedWheelDeltaY += dY;
-
-        // Generate a mouse wheel step event when the accumulated delta
-        // for one of the axes is large enough.
-        // Small delta events that do not pass the threshold get sent
-        // after a timeout.
-        if (Math.abs(this._accumulatedWheelDeltaX) > WHEEL_STEP) {
-            this._generateWheelStepX();
-        } else {
-            this._wheelStepXTimer =
-                window.setTimeout(this._generateWheelStepX.bind(this),
-                                  WHEEL_STEP_TIMEOUT);
-        }
-        if (Math.abs(this._accumulatedWheelDeltaY) > WHEEL_STEP) {
-            this._generateWheelStepY();
-        } else {
-            this._wheelStepYTimer =
-                window.setTimeout(this._generateWheelStepY.bind(this),
-                                  WHEEL_STEP_TIMEOUT);
-        }
-
-        stopEvent(e);
-    }
-
-    _handleMouseMove(e) {
-        this._updateMousePosition(e);
-        this.onmousemove(this._pos.x, this._pos.y);
-        stopEvent(e);
-    }
-
-    _handleMouseDisable(e) {
-        /*
-         * Stop propagation if inside canvas area
-         * Note: This is only needed for the 'click' event as it fails
-         *       to fire properly for the target element so we have
-         *       to listen on the document element instead.
-         */
-        if (e.target == this._target) {
-            stopEvent(e);
-        }
-    }
-
-    // Update coordinates relative to target
-    _updateMousePosition(e) {
-        e = getPointerEvent(e);
-        const bounds = this._target.getBoundingClientRect();
-        let x;
-        let y;
-        // Clip to target bounds
-        if (e.clientX < bounds.left) {
-            x = 0;
-        } else if (e.clientX >= bounds.right) {
-            x = bounds.width - 1;
-        } else {
-            x = e.clientX - bounds.left;
-        }
-        if (e.clientY < bounds.top) {
-            y = 0;
-        } else if (e.clientY >= bounds.bottom) {
-            y = bounds.height - 1;
-        } else {
-            y = e.clientY - bounds.top;
-        }
-        this._pos = {x: x, y: y};
-    }
-
-    // ===== PUBLIC METHODS =====
-
-    grab() {
-        if (isTouchDevice) {
-            this._target.addEventListener('touchstart', this._eventHandlers.mousedown);
-            this._target.addEventListener('touchend', this._eventHandlers.mouseup);
-            this._target.addEventListener('touchmove', this._eventHandlers.mousemove);
-        }
-        this._target.addEventListener('mousedown', this._eventHandlers.mousedown);
-        this._target.addEventListener('mouseup', this._eventHandlers.mouseup);
-        this._target.addEventListener('mousemove', this._eventHandlers.mousemove);
-        this._target.addEventListener('wheel', this._eventHandlers.mousewheel);
-
-        /* Prevent middle-click pasting (see above for why we bind to document) */
-        document.addEventListener('click', this._eventHandlers.mousedisable);
-
-        /* preventDefault() on mousedown doesn't stop this event for some
-           reason so we have to explicitly block it */
-        this._target.addEventListener('contextmenu', this._eventHandlers.mousedisable);
-    }
-
-    ungrab() {
-        this._resetWheelStepTimers();
-
-        if (isTouchDevice) {
-            this._target.removeEventListener('touchstart', this._eventHandlers.mousedown);
-            this._target.removeEventListener('touchend', this._eventHandlers.mouseup);
-            this._target.removeEventListener('touchmove', this._eventHandlers.mousemove);
-        }
-        this._target.removeEventListener('mousedown', this._eventHandlers.mousedown);
-        this._target.removeEventListener('mouseup', this._eventHandlers.mouseup);
-        this._target.removeEventListener('mousemove', this._eventHandlers.mousemove);
-        this._target.removeEventListener('wheel', this._eventHandlers.mousewheel);
-
-        document.removeEventListener('click', this._eventHandlers.mousedisable);
-
-        this._target.removeEventListener('contextmenu', this._eventHandlers.mousedisable);
-    }
-}
diff --git a/systemvm/agent/noVNC/core/input/uskeysym.js b/systemvm/agent/noVNC/core/input/uskeysym.js
new file mode 100644
index 0000000..97c5ae4
--- /dev/null
+++ b/systemvm/agent/noVNC/core/input/uskeysym.js
@@ -0,0 +1,57 @@
+export default {
+    '1': 0x0031, /* U+0031 DIGIT ONE */
+    '2': 0x0032, /* U+0032 DIGIT TWO */
+    '3': 0x0033, /* U+0033 DIGIT THREE */
+    '4': 0x0034, /* U+0034 DIGIT FOUR */
+    '5': 0x0035, /* U+0035 DIGIT FIVE */
+    '6': 0x0036, /* U+0036 DIGIT SIX */
+    '7': 0x0037, /* U+0037 DIGIT SEVEN */
+    '8': 0x0038, /* U+0038 DIGIT EIGHT */
+    '9': 0x0039, /* U+0039 DIGIT NINE */
+    '0': 0x0030, /* U+0030 DIGIT ZERO */
+
+    'a': 0x0061, /* U+0061 LATIN SMALL LETTER A */
+    'b': 0x0062, /* U+0062 LATIN SMALL LETTER B */
+    'c': 0x0063, /* U+0063 LATIN SMALL LETTER C */
+    'd': 0x0064, /* U+0064 LATIN SMALL LETTER D */
+    'e': 0x0065, /* U+0065 LATIN SMALL LETTER E */
+    'f': 0x0066, /* U+0066 LATIN SMALL LETTER F */
+    'g': 0x0067, /* U+0067 LATIN SMALL LETTER G */
+    'h': 0x0068, /* U+0068 LATIN SMALL LETTER H */
+    'i': 0x0069, /* U+0069 LATIN SMALL LETTER I */
+    'j': 0x006a, /* U+006A LATIN SMALL LETTER J */
+    'k': 0x006b, /* U+006B LATIN SMALL LETTER K */
+    'l': 0x006c, /* U+006C LATIN SMALL LETTER L */
+    'm': 0x006d, /* U+006D LATIN SMALL LETTER M */
+    'n': 0x006e, /* U+006E LATIN SMALL LETTER N */
+    'o': 0x006f, /* U+006F LATIN SMALL LETTER O */
+    'p': 0x0070, /* U+0070 LATIN SMALL LETTER P */
+    'q': 0x0071, /* U+0071 LATIN SMALL LETTER Q */
+    'r': 0x0072, /* U+0072 LATIN SMALL LETTER R */
+    's': 0x0073, /* U+0073 LATIN SMALL LETTER S */
+    't': 0x0074, /* U+0074 LATIN SMALL LETTER T */
+    'u': 0x0075, /* U+0075 LATIN SMALL LETTER U */
+    'v': 0x0076, /* U+0076 LATIN SMALL LETTER V */
+    'w': 0x0077, /* U+0077 LATIN SMALL LETTER W */
+    'x': 0x0078, /* U+0078 LATIN SMALL LETTER X */
+    'y': 0x0079, /* U+0079 LATIN SMALL LETTER Y */
+    'z': 0x007a, /* U+007A LATIN SMALL LETTER Z */
+
+    '`': 0x0060, /* U+0060 GRAVE ACCENT */
+    '-': 0x002d, /* U+002D HYPHEN-MINUS */
+    '=': 0x003d, /* U+003D EQUALS SIGN */
+
+    '[': 0x005b, /* U+005B LEFT SQUARE BRACKET */
+    ']': 0x005d, /* U+005D RIGHT SQUARE BRACKET */
+    '\\': 0x005c, /* U+005C REVERSE SOLIDUS */
+
+    ';': 0x003b, /* U+003B SEMICOLON */
+    '\'': 0x0027, /* U+0027 APOSTROPHE */
+
+    ',': 0x002c, /* U+002C COMMA */
+    '.': 0x002e, /* U+002E FULL STOP */
+    '/': 0x002f, /* U+002F SOLIDUS */
+
+    ' ': 0x0020, /* U+0020 SPACE */
+    '\n': 0xff0d
+}
\ No newline at end of file
diff --git a/systemvm/agent/noVNC/core/input/util.js b/systemvm/agent/noVNC/core/input/util.js
index f177ef5..1b98040 100644
--- a/systemvm/agent/noVNC/core/input/util.js
+++ b/systemvm/agent/noVNC/core/input/util.js
@@ -1,3 +1,4 @@
+import KeyTable from "./keysym.js";
 import keysyms from "./keysymdef.js";
 import vkeys from "./vkeys.js";
 import fixedkeys from "./fixedkeys.js";
@@ -91,6 +92,8 @@
         // Mozilla isn't fully in sync with the spec yet
         switch (evt.key) {
             case 'OS': return 'Meta';
+            case 'LaunchMyComputer': return 'LaunchApplication1';
+            case 'LaunchCalculator': return 'LaunchApplication2';
         }
 
         // iOS leaks some OS names
@@ -102,9 +105,21 @@
             case 'UIKeyInputEscape': return 'Escape';
         }
 
-        // IE and Edge have broken handling of AltGraph so we cannot
-        // trust them for printable characters
-        if ((evt.key.length !== 1) || (!browser.isIE() && !browser.isEdge())) {
+        // Broken behaviour in Chrome
+        if ((evt.key === '\x00') && (evt.code === 'NumpadDecimal')) {
+            return 'Delete';
+        }
+
+        // IE and Edge need special handling, but for everyone else we
+        // can trust the value provided
+        if (!browser.isIE() && !browser.isEdge()) {
+            return evt.key;
+        }
+
+        // IE and Edge have broken handling of AltGraph so we can only
+        // trust them for non-printable characters (and unfortunately
+        // they also specify 'Unidentified' for some problem keys)
+        if ((evt.key.length !== 1) && (evt.key !== 'Unidentified')) {
             return evt.key;
         }
     }
@@ -141,10 +156,39 @@
             location = 2;
         }
 
+        // And for Clear
+        if ((key === 'Clear') && (location === 3)) {
+            let code = getKeycode(evt);
+            if (code === 'NumLock') {
+                location = 0;
+            }
+        }
+
         if ((location === undefined) || (location > 3)) {
             location = 0;
         }
 
+        // The original Meta key now gets confused with the Windows key
+        // https://bugs.chromium.org/p/chromium/issues/detail?id=1020141
+        // https://bugzilla.mozilla.org/show_bug.cgi?id=1232918
+        if (key === 'Meta') {
+            let code = getKeycode(evt);
+            if (code === 'AltLeft') {
+                return KeyTable.XK_Meta_L;
+            } else if (code === 'AltRight') {
+                return KeyTable.XK_Meta_R;
+            }
+        }
+
+        // macOS has Clear instead of NumLock, but the remote system is
+        // probably not macOS, so lying here is probably best...
+        if (key === 'Clear') {
+            let code = getKeycode(evt);
+            if (code === 'NumLock') {
+                return KeyTable.XK_Num_Lock;
+            }
+        }
+
         return DOMKeyTable[key][location];
     }
 
diff --git a/systemvm/agent/noVNC/core/rfb.js b/systemvm/agent/noVNC/core/rfb.js
index e40df66..eda1597 100644
--- a/systemvm/agent/noVNC/core/rfb.js
+++ b/systemvm/agent/noVNC/core/rfb.js
@@ -1,23 +1,29 @@
 /*
  * noVNC: HTML5 VNC client
- * Copyright (C) 2018 The noVNC Authors
+ * Copyright (C) 2020 The noVNC Authors
  * Licensed under MPL 2.0 (see LICENSE.txt)
  *
  * See README.md for usage and integration instructions.
  *
  */
 
+import { toUnsigned32bit, toSigned32bit } from './util/int.js';
 import * as Log from './util/logging.js';
-import { decodeUTF8 } from './util/strings.js';
+import { encodeUTF8, decodeUTF8 } from './util/strings.js';
 import { dragThreshold } from './util/browser.js';
+import { clientToElement } from './util/element.js';
+import { setCapture } from './util/events.js';
 import EventTargetMixin from './util/eventtarget.js';
 import Display from "./display.js";
+import Inflator from "./inflator.js";
+import Deflator from "./deflator.js";
 import Keyboard from "./input/keyboard.js";
-import Mouse from "./input/mouse.js";
+import GestureHandler from "./input/gesturehandler.js";
 import Cursor from "./util/cursor.js";
 import Websock from "./websock.js";
 import DES from "./des.js";
 import KeyTable from "./input/keysym.js";
+import USKeyTable from "./input/uskeysym.js";
 import XtScancode from "./input/xtscancodes.js";
 import { encodings } from "./encodings.js";
 import "./util/polyfill.js";
@@ -33,6 +39,36 @@
 const DISCONNECT_TIMEOUT = 3;
 const DEFAULT_BACKGROUND = 'rgb(40, 40, 40)';
 
+// Minimum wait (ms) between two mouse moves
+const MOUSE_MOVE_DELAY = 17;
+
+// Wheel thresholds
+const WHEEL_STEP = 50; // Pixels needed for one step
+const WHEEL_LINE_HEIGHT = 19; // Assumed pixels for one line step
+
+// Gesture thresholds
+const GESTURE_ZOOMSENS = 75;
+const GESTURE_SCRLSENS = 50;
+const DOUBLE_TAP_TIMEOUT = 1000;
+const DOUBLE_TAP_THRESHOLD = 50;
+
+// Extended clipboard pseudo-encoding formats
+const extendedClipboardFormatText   = 1;
+/*eslint-disable no-unused-vars */
+const extendedClipboardFormatRtf    = 1 << 1;
+const extendedClipboardFormatHtml   = 1 << 2;
+const extendedClipboardFormatDib    = 1 << 3;
+const extendedClipboardFormatFiles  = 1 << 4;
+/*eslint-enable */
+
+// Extended clipboard pseudo-encoding actions
+const extendedClipboardActionCaps    = 1 << 24;
+const extendedClipboardActionRequest = 1 << 25;
+const extendedClipboardActionPeek    = 1 << 26;
+const extendedClipboardActionNotify  = 1 << 27;
+const extendedClipboardActionProvide = 1 << 28;
+
+
 export default class RFB extends EventTargetMixin {
     constructor(target, url, options) {
         if (!target) {
@@ -49,27 +85,28 @@
 
         // Connection details
         options = options || {};
-        this._rfb_credentials = options.credentials || {};
-        this._shared = false;
+        this._rfbCredentials = options.credentials || {};
+        this._shared = 'shared' in options ? !!options.shared : true;
         this._repeaterID = options.repeaterID || '';
-        this._showDotCursor = options.showDotCursor || false;
+        this._wsProtocols = ['binary'];
 
         // Internal state
-        this._rfb_connection_state = '';
-        this._rfb_init_state = '';
-        this._rfb_auth_scheme = -1;
-        this._rfb_clean_disconnect = true;
+        this._rfbConnectionState = '';
+        this._rfbInitState = '';
+        this._rfbAuthScheme = -1;
+        this._rfbCleanDisconnect = true;
 
         // Server capabilities
-        this._rfb_version = 0;
-        this._rfb_max_version = 3.8;
-        this._rfb_tightvnc = false;
-        this._rfb_xvp_ver = 0;
+        this._rfbVersion = 0;
+        this._rfbMaxVersion = 3.8;
+        this._rfbTightVNC = false;
+        this._rfbVeNCryptState = 0;
+        this._rfbXvpVer = 0;
 
-        this._fb_width = 0;
-        this._fb_height = 0;
+        this._fbWidth = 0;
+        this._fbHeight = 0;
 
-        this._fb_name = "";
+        this._fbName = "";
 
         this._capabilities = { power: false };
 
@@ -79,21 +116,26 @@
         this._enabledContinuousUpdates = false;
 
         this._supportsSetDesktopSize = false;
-        this._screen_id = 0;
-        this._screen_flags = 0;
+        this._screenID = 0;
+        this._screenFlags = 0;
 
         this._qemuExtKeyEventSupported = false;
 
+        this._clipboardText = null;
+        this._clipboardServerCapabilitiesActions = {};
+        this._clipboardServerCapabilitiesFormats = {};
+
         // 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
+        this._gestures = null;          // Gesture input handler object
 
         // Timers
         this._disconnTimer = null;      // disconnection timer
         this._resizeTimeout = null;     // resize rate limiting
+        this._mouseMoveTimer = null;
 
         // Decoder states
         this._decoders = {};
@@ -108,16 +150,28 @@
         };
 
         // Mouse state
-        this._mouse_buttonMask = 0;
-        this._mouse_arr = [];
+        this._mousePos = {};
+        this._mouseButtonMask = 0;
+        this._mouseLastMoveTime = 0;
         this._viewportDragging = false;
         this._viewportDragPos = {};
         this._viewportHasMoved = false;
+        this._accumulatedWheelDeltaX = 0;
+        this._accumulatedWheelDeltaY = 0;
+
+        // Gesture state
+        this._gestureLastTapTime = null;
+        this._gestureFirstDoubleTapEv = null;
+        this._gestureLastMagnitudeX = 0;
+        this._gestureLastMagnitudeY = 0;
 
         // Bound event handlers
         this._eventHandlers = {
             focusCanvas: this._focusCanvas.bind(this),
             windowResize: this._windowResize.bind(this),
+            handleMouse: this._handleMouse.bind(this),
+            handleWheel: this._handleWheel.bind(this),
+            handleGesture: this._handleGesture.bind(this),
         };
 
         // main setup
@@ -172,27 +226,24 @@
             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._gestures = new GestureHandler();
 
         this._sock = new Websock();
         this._sock.on('message', () => {
-            this._handle_message();
+            this._handleMessage();
         });
         this._sock.on('open', () => {
-            if ((this._rfb_connection_state === 'connecting') &&
-                (this._rfb_init_state === '')) {
-                this._rfb_init_state = 'ProtocolVersion';
+            if ((this._rfbConnectionState === 'connecting') &&
+                (this._rfbInitState === '')) {
+                this._rfbInitState = 'ProtocolVersion';
                 Log.Debug("Starting VNC handshake");
             } else {
                 this._fail("Unexpected server connection while " +
-                           this._rfb_connection_state);
+                           this._rfbConnectionState);
             }
         });
         this._sock.on('close', (e) => {
@@ -205,7 +256,7 @@
                 }
                 msg += ")";
             }
-            switch (this._rfb_connection_state) {
+            switch (this._rfbConnectionState) {
                 case 'connecting':
                     this._fail("Connection closed " + msg);
                     break;
@@ -246,6 +297,15 @@
         this._clipViewport = false;
         this._scaleViewport = false;
         this._resizeSession = false;
+
+        this._showDotCursor = false;
+        if (options.showDotCursor !== undefined) {
+            Log.Warn("Specifying showDotCursor as a RFB constructor argument is deprecated");
+            this._showDotCursor = options.showDotCursor;
+        }
+
+        this._qualityLevel = 6;
+        this._compressionLevel = 2;
     }
 
     // ===== PROPERTIES =====
@@ -254,22 +314,20 @@
     set viewOnly(viewOnly) {
         this._viewOnly = viewOnly;
 
-        if (this._rfb_connection_state === "connecting" ||
-            this._rfb_connection_state === "connected") {
+        if (this._rfbConnectionState === "connecting" ||
+            this._rfbConnectionState === "connected") {
             if (viewOnly) {
                 this._keyboard.ungrab();
-                this._mouse.ungrab();
             } else {
                 this._keyboard.grab();
-                this._mouse.grab();
             }
         }
     }
 
     get capabilities() { return this._capabilities; }
 
-    get touchButton() { return this._mouse.touchButton; }
-    set touchButton(button) { this._mouse.touchButton = button; }
+    get touchButton() { return 0; }
+    set touchButton(button) { Log.Warn("Using old API!"); }
 
     get clipViewport() { return this._clipViewport; }
     set clipViewport(viewport) {
@@ -308,6 +366,46 @@
     get background() { return this._screen.style.background; }
     set background(cssValue) { this._screen.style.background = cssValue; }
 
+    get qualityLevel() {
+        return this._qualityLevel;
+    }
+    set qualityLevel(qualityLevel) {
+        if (!Number.isInteger(qualityLevel) || qualityLevel < 0 || qualityLevel > 9) {
+            Log.Error("qualityLevel must be an integer between 0 and 9");
+            return;
+        }
+
+        if (this._qualityLevel === qualityLevel) {
+            return;
+        }
+
+        this._qualityLevel = qualityLevel;
+
+        if (this._rfbConnectionState === 'connected') {
+            this._sendEncodings();
+        }
+    }
+
+    get compressionLevel() {
+        return this._compressionLevel;
+    }
+    set compressionLevel(compressionLevel) {
+        if (!Number.isInteger(compressionLevel) || compressionLevel < 0 || compressionLevel > 9) {
+            Log.Error("compressionLevel must be an integer between 0 and 9");
+            return;
+        }
+
+        if (this._compressionLevel === compressionLevel) {
+            return;
+        }
+
+        this._compressionLevel = compressionLevel;
+
+        if (this._rfbConnectionState === 'connected') {
+            this._sendEncodings();
+        }
+    }
+
     // ===== PUBLIC METHODS =====
 
     disconnect() {
@@ -318,12 +416,29 @@
     }
 
     sendCredentials(creds) {
-        this._rfb_credentials = creds;
-        setTimeout(this._init_msg.bind(this), 0);
+        this._rfbCredentials = creds;
+        setTimeout(this._initMsg.bind(this), 0);
+    }
+
+    sendText(text) {
+        for (var i = 0; i < text.length; i++) {
+            const character = text.charAt(i);
+            var charCode = USKeyTable[character] || false;
+            if (charCode) {
+                this.sendKey(charCode, character, true);
+                this.sendKey(charCode, character, false);
+            } else {
+                charCode = text.charCodeAt(i)
+                this.sendKey(KeyTable.XK_Shift_L, "ShiftLeft", true);
+                this.sendKey(charCode, character, true);
+                this.sendKey(charCode, character, false);
+                this.sendKey(KeyTable.XK_Shift_L, "ShiftLeft", false);
+            }
+        }
     }
 
     sendCtrlAltDel() {
-        if (this._rfb_connection_state !== 'connected' || this._viewOnly) { return; }
+        if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; }
         Log.Info("Sending Ctrl-Alt-Del");
 
         this.sendKey(KeyTable.XK_Control_L, "ControlLeft", true);
@@ -359,7 +474,7 @@
     // Send a key press. If 'down' is not specified then send a down key
     // followed by an up key.
     sendKey(keysym, code, down) {
-        if (this._rfb_connection_state !== 'connected' || this._viewOnly) { return; }
+        if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; }
 
         if (down === undefined) {
             this.sendKey(keysym, code, true);
@@ -394,8 +509,22 @@
     }
 
     clipboardPasteFrom(text) {
-        if (this._rfb_connection_state !== 'connected' || this._viewOnly) { return; }
-        RFB.messages.clientCutText(this._sock, text);
+        if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; }
+
+        if (this._clipboardServerCapabilitiesFormats[extendedClipboardFormatText] &&
+            this._clipboardServerCapabilitiesActions[extendedClipboardActionNotify]) {
+
+            this._clipboardText = text;
+            RFB.messages.extendedClipboardNotify(this._sock, [extendedClipboardFormatText]);
+        } else {
+            let data = new Uint8Array(text.length);
+            for (let i = 0; i < text.length; i++) {
+                // FIXME: text can have values outside of Latin1/Uint8
+                data[i] = text.charCodeAt(i);
+            }
+
+            RFB.messages.clientCutText(this._sock, data);
+        }
     }
 
     // ===== PRIVATE METHODS =====
@@ -407,7 +536,7 @@
 
         try {
             // WebSocket.onopen transitions to the RFB init states
-            this._sock.open(this._url, ['binary']);
+            this._sock.open(this._url, this._wsProtocols);
         } catch (e) {
             if (e.name === 'SyntaxError') {
                 this._fail("Invalid host or port (" + e + ")");
@@ -419,6 +548,8 @@
         // Make our elements part of the page
         this._target.appendChild(this._screen);
 
+        this._gestures.attach(this._canvas);
+
         this._cursor.attach(this._canvas);
         this._refreshCursor();
 
@@ -430,17 +561,44 @@
         this._canvas.addEventListener("mousedown", this._eventHandlers.focusCanvas);
         this._canvas.addEventListener("touchstart", this._eventHandlers.focusCanvas);
 
+        // Mouse events
+        this._canvas.addEventListener('mousedown', this._eventHandlers.handleMouse);
+        this._canvas.addEventListener('mouseup', this._eventHandlers.handleMouse);
+        this._canvas.addEventListener('mousemove', this._eventHandlers.handleMouse);
+        // Prevent middle-click pasting (see handler for why we bind to document)
+        this._canvas.addEventListener('click', this._eventHandlers.handleMouse);
+        // preventDefault() on mousedown doesn't stop this event for some
+        // reason so we have to explicitly block it
+        this._canvas.addEventListener('contextmenu', this._eventHandlers.handleMouse);
+
+        // Wheel events
+        this._canvas.addEventListener("wheel", this._eventHandlers.handleWheel);
+
+        // Gesture events
+        this._canvas.addEventListener("gesturestart", this._eventHandlers.handleGesture);
+        this._canvas.addEventListener("gesturemove", this._eventHandlers.handleGesture);
+        this._canvas.addEventListener("gestureend", this._eventHandlers.handleGesture);
+
         Log.Debug("<< RFB.connect");
     }
 
     _disconnect() {
         Log.Debug(">> RFB.disconnect");
         this._cursor.detach();
+        this._canvas.removeEventListener("gesturestart", this._eventHandlers.handleGesture);
+        this._canvas.removeEventListener("gesturemove", this._eventHandlers.handleGesture);
+        this._canvas.removeEventListener("gestureend", this._eventHandlers.handleGesture);
+        this._canvas.removeEventListener("wheel", this._eventHandlers.handleWheel);
+        this._canvas.removeEventListener('mousedown', this._eventHandlers.handleMouse);
+        this._canvas.removeEventListener('mouseup', this._eventHandlers.handleMouse);
+        this._canvas.removeEventListener('mousemove', this._eventHandlers.handleMouse);
+        this._canvas.removeEventListener('click', this._eventHandlers.handleMouse);
+        this._canvas.removeEventListener('contextmenu', this._eventHandlers.handleMouse);
         this._canvas.removeEventListener("mousedown", this._eventHandlers.focusCanvas);
         this._canvas.removeEventListener("touchstart", this._eventHandlers.focusCanvas);
         window.removeEventListener('resize', this._eventHandlers.windowResize);
         this._keyboard.ungrab();
-        this._mouse.ungrab();
+        this._gestures.detach();
         this._sock.close();
         try {
             this._target.removeChild(this._screen);
@@ -453,15 +611,11 @@
             }
         }
         clearTimeout(this._resizeTimeout);
+        clearTimeout(this._mouseMoveTimer);
         Log.Debug("<< RFB.disconnect");
     }
 
     _focusCanvas(event) {
-        // Respect earlier handlers' request to not do side-effects
-        if (event.defaultPrevented) {
-            return;
-        }
-
         if (!this.focusOnClick) {
             return;
         }
@@ -469,6 +623,13 @@
         this.focus();
     }
 
+    _setDesktopName(name) {
+        this._fbName = name;
+        this.dispatchEvent(new CustomEvent(
+            "desktopname",
+            { detail: { name: this._fbName } }));
+    }
+
     _windowResize(event) {
         // If the window resized then our screen element might have
         // as well. Update the viewport dimensions.
@@ -491,19 +652,19 @@
     // Update state of clipping in Display object, and make sure the
     // configured viewport matches the current screen size
     _updateClip() {
-        const cur_clip = this._display.clipViewport;
-        let new_clip = this._clipViewport;
+        const curClip = this._display.clipViewport;
+        let newClip = this._clipViewport;
 
         if (this._scaleViewport) {
             // Disable viewport clipping if we are scaling
-            new_clip = false;
+            newClip = false;
         }
 
-        if (cur_clip !== new_clip) {
-            this._display.clipViewport = new_clip;
+        if (curClip !== newClip) {
+            this._display.clipViewport = newClip;
         }
 
-        if (new_clip) {
+        if (newClip) {
             // When clipping is enabled, the screen is limited to
             // the size of the container.
             const size = this._screenSize();
@@ -536,7 +697,7 @@
         const size = this._screenSize();
         RFB.messages.setDesktopSize(this._sock,
                                     Math.floor(size.w), Math.floor(size.h),
-                                    this._screen_id, this._screen_flags);
+                                    this._screenID, this._screenFlags);
 
         Log.Debug('Requested new desktop size: ' +
                    size.w + 'x' + size.h);
@@ -568,7 +729,7 @@
      *   disconnected - permanent state
      */
     _updateConnectionState(state) {
-        const oldstate = this._rfb_connection_state;
+        const oldstate = this._rfbConnectionState;
 
         if (state === oldstate) {
             Log.Debug("Already in state '" + state + "', ignoring");
@@ -622,7 +783,7 @@
 
         // State change actions
 
-        this._rfb_connection_state = state;
+        this._rfbConnectionState = state;
 
         Log.Debug("New state '" + state + "', was '" + oldstate + "'.");
 
@@ -656,7 +817,7 @@
             case 'disconnected':
                 this.dispatchEvent(new CustomEvent(
                     "disconnect", { detail:
-                                    { clean: this._rfb_clean_disconnect } }));
+                                    { clean: this._rfbCleanDisconnect } }));
                 break;
         }
     }
@@ -667,7 +828,7 @@
      * should be logged but not sent to the user interface.
      */
     _fail(details) {
-        switch (this._rfb_connection_state) {
+        switch (this._rfbConnectionState) {
             case 'disconnecting':
                 Log.Error("Failed when disconnecting: " + details);
                 break;
@@ -681,7 +842,7 @@
                 Log.Error("RFB failure: " + details);
                 break;
         }
-        this._rfb_clean_disconnect = false; //This is sent to the UI
+        this._rfbCleanDisconnect = false; //This is sent to the UI
 
         // Transition to disconnected without waiting for socket to close
         this._updateConnectionState('disconnecting');
@@ -696,13 +857,13 @@
                                            { detail: { capabilities: this._capabilities } }));
     }
 
-    _handle_message() {
+    _handleMessage() {
         if (this._sock.rQlen === 0) {
-            Log.Warn("handle_message called on an empty receive queue");
+            Log.Warn("handleMessage called on an empty receive queue");
             return;
         }
 
-        switch (this._rfb_connection_state) {
+        switch (this._rfbConnectionState) {
             case 'disconnected':
                 Log.Error("Got data while disconnected");
                 break;
@@ -711,7 +872,7 @@
                     if (this._flushing) {
                         break;
                     }
-                    if (!this._normal_msg()) {
+                    if (!this._normalMsg()) {
                         break;
                     }
                     if (this._sock.rQlen === 0) {
@@ -720,7 +881,7 @@
                 }
                 break;
             default:
-                this._init_msg();
+                this._initMsg();
                 break;
         }
     }
@@ -729,13 +890,52 @@
         this.sendKey(keysym, code, down);
     }
 
-    _handleMouseButton(x, y, down, bmask) {
-        if (down) {
-            this._mouse_buttonMask |= bmask;
-        } else {
-            this._mouse_buttonMask &= ~bmask;
+    _handleMouse(ev) {
+        /*
+         * We don't check connection status or viewOnly here as the
+         * mouse events might be used to control the viewport
+         */
+
+        if (ev.type === 'click') {
+            /*
+             * Note: This is only needed for the 'click' event as it fails
+             *       to fire properly for the target element so we have
+             *       to listen on the document element instead.
+             */
+            if (ev.target !== this._canvas) {
+                return;
+            }
         }
 
+        // FIXME: if we're in view-only and not dragging,
+        //        should we stop events?
+        ev.stopPropagation();
+        ev.preventDefault();
+
+        if ((ev.type === 'click') || (ev.type === 'contextmenu')) {
+            return;
+        }
+
+        let pos = clientToElement(ev.clientX, ev.clientY,
+                                  this._canvas);
+
+        switch (ev.type) {
+            case 'mousedown':
+                setCapture(this._canvas);
+                this._handleMouseButton(pos.x, pos.y,
+                                        true, 1 << ev.button);
+                break;
+            case 'mouseup':
+                this._handleMouseButton(pos.x, pos.y,
+                                        false, 1 << ev.button);
+                break;
+            case 'mousemove':
+                this._handleMouseMove(pos.x, pos.y);
+                break;
+        }
+    }
+
+    _handleMouseButton(x, y, down, bmask) {
         if (this.dragViewport) {
             if (down && !this._viewportDragging) {
                 this._viewportDragging = true;
@@ -756,17 +956,24 @@
                 // Otherwise we treat this as a mouse click event.
                 // Send the button down event here, as the button up
                 // event is sent at the end of this function.
-                RFB.messages.pointerEvent(this._sock,
-                                          this._display.absX(x),
-                                          this._display.absY(y),
-                                          bmask);
+                this._sendMouse(x, y, bmask);
             }
         }
 
-        if (this._viewOnly) { return; } // View only, skip mouse events
+        // Flush waiting move event first
+        if (this._mouseMoveTimer !== null) {
+            clearTimeout(this._mouseMoveTimer);
+            this._mouseMoveTimer = null;
+            this._sendMouse(x, y, this._mouseButtonMask);
+        }
 
-        if (this._rfb_connection_state !== 'connected') { return; }
-        RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask);
+        if (down) {
+            this._mouseButtonMask |= bmask;
+        } else {
+            this._mouseButtonMask &= ~bmask;
+        }
+
+        this._sendMouse(x, y, this._mouseButtonMask);
     }
 
     _handleMouseMove(x, y) {
@@ -786,66 +993,304 @@
             return;
         }
 
+        this._mousePos = { 'x': x, 'y': y };
+
+        // Limit many mouse move events to one every MOUSE_MOVE_DELAY ms
+        if (this._mouseMoveTimer == null) {
+
+            const timeSinceLastMove = Date.now() - this._mouseLastMoveTime;
+            if (timeSinceLastMove > MOUSE_MOVE_DELAY) {
+                this._sendMouse(x, y, this._mouseButtonMask);
+                this._mouseLastMoveTime = Date.now();
+            } else {
+                // Too soon since the latest move, wait the remaining time
+                this._mouseMoveTimer = setTimeout(() => {
+                    this._handleDelayedMouseMove();
+                }, MOUSE_MOVE_DELAY - timeSinceLastMove);
+            }
+        }
+    }
+
+    _handleDelayedMouseMove() {
+        this._mouseMoveTimer = null;
+        this._sendMouse(this._mousePos.x, this._mousePos.y,
+                        this._mouseButtonMask);
+        this._mouseLastMoveTime = Date.now();
+    }
+
+    _sendMouse(x, y, mask) {
+        if (this._rfbConnectionState !== 'connected') { return; }
         if (this._viewOnly) { return; } // View only, skip mouse events
 
-        if (this._rfb_connection_state !== 'connected') { return; }
-        RFB.messages.pointerEvent(this._sock, this._display.absX(x), this._display.absY(y), this._mouse_buttonMask);
+        RFB.messages.pointerEvent(this._sock, this._display.absX(x),
+                                  this._display.absY(y), mask);
+    }
+
+    _handleWheel(ev) {
+        if (this._rfbConnectionState !== 'connected') { return; }
+        if (this._viewOnly) { return; } // View only, skip mouse events
+
+        ev.stopPropagation();
+        ev.preventDefault();
+
+        let pos = clientToElement(ev.clientX, ev.clientY,
+                                  this._canvas);
+
+        let dX = ev.deltaX;
+        let dY = ev.deltaY;
+
+        // Pixel units unless it's non-zero.
+        // Note that if deltamode is line or page won't matter since we aren't
+        // sending the mouse wheel delta to the server anyway.
+        // The difference between pixel and line can be important however since
+        // we have a threshold that can be smaller than the line height.
+        if (ev.deltaMode !== 0) {
+            dX *= WHEEL_LINE_HEIGHT;
+            dY *= WHEEL_LINE_HEIGHT;
+        }
+
+        // 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.
+        this._accumulatedWheelDeltaX += dX;
+        this._accumulatedWheelDeltaY += dY;
+
+        // Generate a mouse wheel step event when the accumulated delta
+        // for one of the axes is large enough.
+        if (Math.abs(this._accumulatedWheelDeltaX) >= WHEEL_STEP) {
+            if (this._accumulatedWheelDeltaX < 0) {
+                this._handleMouseButton(pos.x, pos.y, true, 1 << 5);
+                this._handleMouseButton(pos.x, pos.y, false, 1 << 5);
+            } else if (this._accumulatedWheelDeltaX > 0) {
+                this._handleMouseButton(pos.x, pos.y, true, 1 << 6);
+                this._handleMouseButton(pos.x, pos.y, false, 1 << 6);
+            }
+
+            this._accumulatedWheelDeltaX = 0;
+        }
+        if (Math.abs(this._accumulatedWheelDeltaY) >= WHEEL_STEP) {
+            if (this._accumulatedWheelDeltaY < 0) {
+                this._handleMouseButton(pos.x, pos.y, true, 1 << 3);
+                this._handleMouseButton(pos.x, pos.y, false, 1 << 3);
+            } else if (this._accumulatedWheelDeltaY > 0) {
+                this._handleMouseButton(pos.x, pos.y, true, 1 << 4);
+                this._handleMouseButton(pos.x, pos.y, false, 1 << 4);
+            }
+
+            this._accumulatedWheelDeltaY = 0;
+        }
+    }
+
+    _fakeMouseMove(ev, elementX, elementY) {
+        this._handleMouseMove(elementX, elementY);
+        this._cursor.move(ev.detail.clientX, ev.detail.clientY);
+    }
+
+    _handleTapEvent(ev, bmask) {
+        let pos = clientToElement(ev.detail.clientX, ev.detail.clientY,
+                                  this._canvas);
+
+        // If the user quickly taps multiple times we assume they meant to
+        // hit the same spot, so slightly adjust coordinates
+
+        if ((this._gestureLastTapTime !== null) &&
+            ((Date.now() - this._gestureLastTapTime) < DOUBLE_TAP_TIMEOUT) &&
+            (this._gestureFirstDoubleTapEv.detail.type === ev.detail.type)) {
+            let dx = this._gestureFirstDoubleTapEv.detail.clientX - ev.detail.clientX;
+            let dy = this._gestureFirstDoubleTapEv.detail.clientY - ev.detail.clientY;
+            let distance = Math.hypot(dx, dy);
+
+            if (distance < DOUBLE_TAP_THRESHOLD) {
+                pos = clientToElement(this._gestureFirstDoubleTapEv.detail.clientX,
+                                      this._gestureFirstDoubleTapEv.detail.clientY,
+                                      this._canvas);
+            } else {
+                this._gestureFirstDoubleTapEv = ev;
+            }
+        } else {
+            this._gestureFirstDoubleTapEv = ev;
+        }
+        this._gestureLastTapTime = Date.now();
+
+        this._fakeMouseMove(this._gestureFirstDoubleTapEv, pos.x, pos.y);
+        this._handleMouseButton(pos.x, pos.y, true, bmask);
+        this._handleMouseButton(pos.x, pos.y, false, bmask);
+    }
+
+    _handleGesture(ev) {
+        let magnitude;
+
+        let pos = clientToElement(ev.detail.clientX, ev.detail.clientY,
+                                  this._canvas);
+        switch (ev.type) {
+            case 'gesturestart':
+                switch (ev.detail.type) {
+                    case 'onetap':
+                        this._handleTapEvent(ev, 0x1);
+                        break;
+                    case 'twotap':
+                        this._handleTapEvent(ev, 0x4);
+                        break;
+                    case 'threetap':
+                        this._handleTapEvent(ev, 0x2);
+                        break;
+                    case 'drag':
+                        this._fakeMouseMove(ev, pos.x, pos.y);
+                        this._handleMouseButton(pos.x, pos.y, true, 0x1);
+                        break;
+                    case 'longpress':
+                        this._fakeMouseMove(ev, pos.x, pos.y);
+                        this._handleMouseButton(pos.x, pos.y, true, 0x4);
+                        break;
+
+                    case 'twodrag':
+                        this._gestureLastMagnitudeX = ev.detail.magnitudeX;
+                        this._gestureLastMagnitudeY = ev.detail.magnitudeY;
+                        this._fakeMouseMove(ev, pos.x, pos.y);
+                        break;
+                    case 'pinch':
+                        this._gestureLastMagnitudeX = Math.hypot(ev.detail.magnitudeX,
+                                                                 ev.detail.magnitudeY);
+                        this._fakeMouseMove(ev, pos.x, pos.y);
+                        break;
+                }
+                break;
+
+            case 'gesturemove':
+                switch (ev.detail.type) {
+                    case 'onetap':
+                    case 'twotap':
+                    case 'threetap':
+                        break;
+                    case 'drag':
+                    case 'longpress':
+                        this._fakeMouseMove(ev, pos.x, pos.y);
+                        break;
+                    case 'twodrag':
+                        // Always scroll in the same position.
+                        // We don't know if the mouse was moved so we need to move it
+                        // every update.
+                        this._fakeMouseMove(ev, pos.x, pos.y);
+                        while ((ev.detail.magnitudeY - this._gestureLastMagnitudeY) > GESTURE_SCRLSENS) {
+                            this._handleMouseButton(pos.x, pos.y, true, 0x8);
+                            this._handleMouseButton(pos.x, pos.y, false, 0x8);
+                            this._gestureLastMagnitudeY += GESTURE_SCRLSENS;
+                        }
+                        while ((ev.detail.magnitudeY - this._gestureLastMagnitudeY) < -GESTURE_SCRLSENS) {
+                            this._handleMouseButton(pos.x, pos.y, true, 0x10);
+                            this._handleMouseButton(pos.x, pos.y, false, 0x10);
+                            this._gestureLastMagnitudeY -= GESTURE_SCRLSENS;
+                        }
+                        while ((ev.detail.magnitudeX - this._gestureLastMagnitudeX) > GESTURE_SCRLSENS) {
+                            this._handleMouseButton(pos.x, pos.y, true, 0x20);
+                            this._handleMouseButton(pos.x, pos.y, false, 0x20);
+                            this._gestureLastMagnitudeX += GESTURE_SCRLSENS;
+                        }
+                        while ((ev.detail.magnitudeX - this._gestureLastMagnitudeX) < -GESTURE_SCRLSENS) {
+                            this._handleMouseButton(pos.x, pos.y, true, 0x40);
+                            this._handleMouseButton(pos.x, pos.y, false, 0x40);
+                            this._gestureLastMagnitudeX -= GESTURE_SCRLSENS;
+                        }
+                        break;
+                    case 'pinch':
+                        // Always scroll in the same position.
+                        // We don't know if the mouse was moved so we need to move it
+                        // every update.
+                        this._fakeMouseMove(ev, pos.x, pos.y);
+                        magnitude = Math.hypot(ev.detail.magnitudeX, ev.detail.magnitudeY);
+                        if (Math.abs(magnitude - this._gestureLastMagnitudeX) > GESTURE_ZOOMSENS) {
+                            this._handleKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
+                            while ((magnitude - this._gestureLastMagnitudeX) > GESTURE_ZOOMSENS) {
+                                this._handleMouseButton(pos.x, pos.y, true, 0x8);
+                                this._handleMouseButton(pos.x, pos.y, false, 0x8);
+                                this._gestureLastMagnitudeX += GESTURE_ZOOMSENS;
+                            }
+                            while ((magnitude -  this._gestureLastMagnitudeX) < -GESTURE_ZOOMSENS) {
+                                this._handleMouseButton(pos.x, pos.y, true, 0x10);
+                                this._handleMouseButton(pos.x, pos.y, false, 0x10);
+                                this._gestureLastMagnitudeX -= GESTURE_ZOOMSENS;
+                            }
+                        }
+                        this._handleKeyEvent(KeyTable.XK_Control_L, "ControlLeft", false);
+                        break;
+                }
+                break;
+
+            case 'gestureend':
+                switch (ev.detail.type) {
+                    case 'onetap':
+                    case 'twotap':
+                    case 'threetap':
+                    case 'pinch':
+                    case 'twodrag':
+                        break;
+                    case 'drag':
+                        this._fakeMouseMove(ev, pos.x, pos.y);
+                        this._handleMouseButton(pos.x, pos.y, false, 0x1);
+                        break;
+                    case 'longpress':
+                        this._fakeMouseMove(ev, pos.x, pos.y);
+                        this._handleMouseButton(pos.x, pos.y, false, 0x4);
+                        break;
+                }
+                break;
+        }
     }
 
     // Message Handlers
 
-    _negotiate_protocol_version() {
+    _negotiateProtocolVersion() {
         if (this._sock.rQwait("version", 12)) {
             return false;
         }
 
         const sversion = this._sock.rQshiftStr(12).substr(4, 7);
         Log.Info("Server ProtocolVersion: " + sversion);
-        let is_repeater = 0;
+        let isRepeater = 0;
         switch (sversion) {
             case "000.000":  // UltraVNC repeater
-                is_repeater = 1;
+                isRepeater = 1;
                 break;
             case "003.003":
             case "003.006":  // UltraVNC
             case "003.889":  // Apple Remote Desktop
-                this._rfb_version = 3.3;
+                this._rfbVersion = 3.3;
                 break;
             case "003.007":
-                this._rfb_version = 3.7;
+                this._rfbVersion = 3.7;
                 break;
             case "003.008":
             case "004.000":  // Intel AMT KVM
             case "004.001":  // RealVNC 4.6
             case "005.000":  // RealVNC 5.3
-                this._rfb_version = 3.8;
+                this._rfbVersion = 3.8;
                 break;
             default:
                 return this._fail("Invalid server version " + sversion);
         }
 
-        if (is_repeater) {
+        if (isRepeater) {
             let repeaterID = "ID:" + this._repeaterID;
             while (repeaterID.length < 250) {
                 repeaterID += "\0";
             }
-            this._sock.send_string(repeaterID);
+            this._sock.sendString(repeaterID);
             return true;
         }
 
-        if (this._rfb_version > this._rfb_max_version) {
-            this._rfb_version = this._rfb_max_version;
+        if (this._rfbVersion > this._rfbMaxVersion) {
+            this._rfbVersion = this._rfbMaxVersion;
         }
 
-        const cversion = "00" + parseInt(this._rfb_version, 10) +
-                       ".00" + ((this._rfb_version * 10) % 10);
-        this._sock.send_string("RFB " + cversion + "\n");
+        const cversion = "00" + parseInt(this._rfbVersion, 10) +
+                       ".00" + ((this._rfbVersion * 10) % 10);
+        this._sock.sendString("RFB " + cversion + "\n");
         Log.Debug('Sent ProtocolVersion: ' + cversion);
 
-        this._rfb_init_state = 'Security';
+        this._rfbInitState = 'Security';
     }
 
-    _negotiate_security() {
+    _negotiateSecurity() {
         // Polyfill since IE and PhantomJS doesn't have
         // TypedArray.includes()
         function includes(item, array) {
@@ -857,55 +1302,57 @@
             return false;
         }
 
-        if (this._rfb_version >= 3.7) {
+        if (this._rfbVersion >= 3.7) {
             // Server sends supported list, client decides
-            const num_types = this._sock.rQshift8();
-            if (this._sock.rQwait("security type", num_types, 1)) { return false; }
+            const numTypes = this._sock.rQshift8();
+            if (this._sock.rQwait("security type", numTypes, 1)) { return false; }
 
-            if (num_types === 0) {
-                this._rfb_init_state = "SecurityReason";
-                this._security_context = "no security types";
-                this._security_status = 1;
-                return this._init_msg();
+            if (numTypes === 0) {
+                this._rfbInitState = "SecurityReason";
+                this._securityContext = "no security types";
+                this._securityStatus = 1;
+                return this._initMsg();
             }
 
-            const types = this._sock.rQshiftBytes(num_types);
+            const types = this._sock.rQshiftBytes(numTypes);
             Log.Debug("Server security types: " + types);
 
             // Look for each auth in preferred order
             if (includes(1, types)) {
-                this._rfb_auth_scheme = 1; // None
+                this._rfbAuthScheme = 1; // None
             } else if (includes(22, types)) {
-                this._rfb_auth_scheme = 22; // XVP
+                this._rfbAuthScheme = 22; // XVP
             } else if (includes(16, types)) {
-                this._rfb_auth_scheme = 16; // Tight
+                this._rfbAuthScheme = 16; // Tight
             } else if (includes(2, types)) {
-                this._rfb_auth_scheme = 2; // VNC Auth
+                this._rfbAuthScheme = 2; // VNC Auth
+            } else if (includes(19, types)) {
+                this._rfbAuthScheme = 19; // VeNCrypt Auth
             } else {
                 return this._fail("Unsupported security types (types: " + types + ")");
             }
 
-            this._sock.send([this._rfb_auth_scheme]);
+            this._sock.send([this._rfbAuthScheme]);
         } else {
             // Server decides
             if (this._sock.rQwait("security scheme", 4)) { return false; }
-            this._rfb_auth_scheme = this._sock.rQshift32();
+            this._rfbAuthScheme = this._sock.rQshift32();
 
-            if (this._rfb_auth_scheme == 0) {
-                this._rfb_init_state = "SecurityReason";
-                this._security_context = "authentication scheme";
-                this._security_status = 1;
-                return this._init_msg();
+            if (this._rfbAuthScheme == 0) {
+                this._rfbInitState = "SecurityReason";
+                this._securityContext = "authentication scheme";
+                this._securityStatus = 1;
+                return this._initMsg();
             }
         }
 
-        this._rfb_init_state = 'Authentication';
-        Log.Debug('Authenticating using scheme: ' + this._rfb_auth_scheme);
+        this._rfbInitState = 'Authentication';
+        Log.Debug('Authenticating using scheme: ' + this._rfbAuthScheme);
 
-        return this._init_msg(); // jump to authentication
+        return this._initMsg(); // jump to authentication
     }
 
-    _handle_security_reason() {
+    _handleSecurityReason() {
         if (this._sock.rQwait("reason length", 4)) {
             return false;
         }
@@ -920,46 +1367,134 @@
         if (reason !== "") {
             this.dispatchEvent(new CustomEvent(
                 "securityfailure",
-                { detail: { status: this._security_status,
+                { detail: { status: this._securityStatus,
                             reason: reason } }));
 
             return this._fail("Security negotiation failed on " +
-                              this._security_context +
+                              this._securityContext +
                               " (reason: " + reason + ")");
         } else {
             this.dispatchEvent(new CustomEvent(
                 "securityfailure",
-                { detail: { status: this._security_status } }));
+                { detail: { status: this._securityStatus } }));
 
             return this._fail("Security negotiation failed on " +
-                              this._security_context);
+                              this._securityContext);
         }
     }
 
     // authentication
-    _negotiate_xvp_auth() {
-        if (!this._rfb_credentials.username ||
-            !this._rfb_credentials.password ||
-            !this._rfb_credentials.target) {
+    _negotiateXvpAuth() {
+        if (this._rfbCredentials.username === undefined ||
+            this._rfbCredentials.password === undefined ||
+            this._rfbCredentials.target === undefined) {
             this.dispatchEvent(new CustomEvent(
                 "credentialsrequired",
                 { detail: { types: ["username", "password", "target"] } }));
             return false;
         }
 
-        const xvp_auth_str = String.fromCharCode(this._rfb_credentials.username.length) +
-                           String.fromCharCode(this._rfb_credentials.target.length) +
-                           this._rfb_credentials.username +
-                           this._rfb_credentials.target;
-        this._sock.send_string(xvp_auth_str);
-        this._rfb_auth_scheme = 2;
-        return this._negotiate_authentication();
+        const xvpAuthStr = String.fromCharCode(this._rfbCredentials.username.length) +
+                           String.fromCharCode(this._rfbCredentials.target.length) +
+                           this._rfbCredentials.username +
+                           this._rfbCredentials.target;
+        this._sock.sendString(xvpAuthStr);
+        this._rfbAuthScheme = 2;
+        return this._negotiateAuthentication();
     }
 
-    _negotiate_std_vnc_auth() {
+    // VeNCrypt authentication, currently only supports version 0.2 and only Plain subtype
+    _negotiateVeNCryptAuth() {
+
+        // waiting for VeNCrypt version
+        if (this._rfbVeNCryptState == 0) {
+            if (this._sock.rQwait("vencrypt version", 2)) { return false; }
+
+            const major = this._sock.rQshift8();
+            const minor = this._sock.rQshift8();
+
+            if (!(major == 0 && minor == 2)) {
+                return this._fail("Unsupported VeNCrypt version " + major + "." + minor);
+            }
+
+            this._sock.send([0, 2]);
+            this._rfbVeNCryptState = 1;
+        }
+
+        // waiting for ACK
+        if (this._rfbVeNCryptState == 1) {
+            if (this._sock.rQwait("vencrypt ack", 1)) { return false; }
+
+            const res = this._sock.rQshift8();
+
+            if (res != 0) {
+                return this._fail("VeNCrypt failure " + res);
+            }
+
+            this._rfbVeNCryptState = 2;
+        }
+        // must fall through here (i.e. no "else if"), beacause we may have already received
+        // the subtypes length and won't be called again
+
+        if (this._rfbVeNCryptState == 2) { // waiting for subtypes length
+            if (this._sock.rQwait("vencrypt subtypes length", 1)) { return false; }
+
+            const subtypesLength = this._sock.rQshift8();
+            if (subtypesLength < 1) {
+                return this._fail("VeNCrypt subtypes empty");
+            }
+
+            this._rfbVeNCryptSubtypesLength = subtypesLength;
+            this._rfbVeNCryptState = 3;
+        }
+
+        // waiting for subtypes list
+        if (this._rfbVeNCryptState == 3) {
+            if (this._sock.rQwait("vencrypt subtypes", 4 * this._rfbVeNCryptSubtypesLength)) { return false; }
+
+            const subtypes = [];
+            for (let i = 0; i < this._rfbVeNCryptSubtypesLength; i++) {
+                subtypes.push(this._sock.rQshift32());
+            }
+
+            // 256 = Plain subtype
+            if (subtypes.indexOf(256) != -1) {
+                // 0x100 = 256
+                this._sock.send([0, 0, 1, 0]);
+                this._rfbVeNCryptState = 4;
+            } else {
+                return this._fail("VeNCrypt Plain subtype not offered by server");
+            }
+        }
+
+        // negotiated Plain subtype, server waits for password
+        if (this._rfbVeNCryptState == 4) {
+            if (!this._rfbCredentials.username ||
+                !this._rfbCredentials.password) {
+                this.dispatchEvent(new CustomEvent(
+                    "credentialsrequired",
+                    { detail: { types: ["username", "password"] } }));
+                return false;
+            }
+
+            const user = encodeUTF8(this._rfbCredentials.username);
+            const pass = encodeUTF8(this._rfbCredentials.password);
+
+            // XXX we assume lengths are <= 255 (should not be an issue in the real world)
+            this._sock.send([0, 0, 0, user.length]);
+            this._sock.send([0, 0, 0, pass.length]);
+            this._sock.sendString(user);
+            this._sock.sendString(pass);
+
+            this._rfbInitState = "SecurityResult";
+            return true;
+        }
+    }
+
+    _negotiateStdVNCAuth() {
         if (this._sock.rQwait("auth challenge", 16)) { return false; }
 
-        if (!this._rfb_credentials.password) {
+        if (this._rfbCredentials.password === undefined) {
             this.dispatchEvent(new CustomEvent(
                 "credentialsrequired",
                 { detail: { types: ["password"] } }));
@@ -968,23 +1503,40 @@
 
         // TODO(directxman12): make genDES not require an Array
         const challenge = Array.prototype.slice.call(this._sock.rQshiftBytes(16));
-        const response = RFB.genDES(this._rfb_credentials.password, challenge);
+        const response = RFB.genDES(this._rfbCredentials.password, challenge);
         this._sock.send(response);
-        this._rfb_init_state = "SecurityResult";
+        this._rfbInitState = "SecurityResult";
         return true;
     }
 
-    _negotiate_tight_tunnels(numTunnels) {
+    _negotiateTightUnixAuth() {
+        if (this._rfbCredentials.username === undefined ||
+            this._rfbCredentials.password === undefined) {
+            this.dispatchEvent(new CustomEvent(
+                "credentialsrequired",
+                { detail: { types: ["username", "password"] } }));
+            return false;
+        }
+
+        this._sock.send([0, 0, 0, this._rfbCredentials.username.length]);
+        this._sock.send([0, 0, 0, this._rfbCredentials.password.length]);
+        this._sock.sendString(this._rfbCredentials.username);
+        this._sock.sendString(this._rfbCredentials.password);
+        this._rfbInitState = "SecurityResult";
+        return true;
+    }
+
+    _negotiateTightTunnels(numTunnels) {
         const clientSupportedTunnelTypes = {
             0: { vendor: 'TGHT', signature: 'NOTUNNEL' }
         };
         const serverSupportedTunnelTypes = {};
         // receive tunnel capabilities
         for (let i = 0; i < numTunnels; i++) {
-            const cap_code = this._sock.rQshift32();
-            const cap_vendor = this._sock.rQshiftStr(4);
-            const cap_signature = this._sock.rQshiftStr(8);
-            serverSupportedTunnelTypes[cap_code] = { vendor: cap_vendor, signature: cap_signature };
+            const capCode = this._sock.rQshift32();
+            const capVendor = this._sock.rQshiftStr(4);
+            const capSignature = this._sock.rQshiftStr(8);
+            serverSupportedTunnelTypes[capCode] = { vendor: capVendor, signature: capSignature };
         }
 
         Log.Debug("Server Tight tunnel types: " + serverSupportedTunnelTypes);
@@ -1015,16 +1567,16 @@
         }
     }
 
-    _negotiate_tight_auth() {
-        if (!this._rfb_tightvnc) {  // first pass, do the tunnel negotiation
+    _negotiateTightAuth() {
+        if (!this._rfbTightVNC) {  // first pass, do the tunnel negotiation
             if (this._sock.rQwait("num tunnels", 4)) { return false; }
             const numTunnels = this._sock.rQshift32();
             if (numTunnels > 0 && this._sock.rQwait("tunnel capabilities", 16 * numTunnels, 4)) { return false; }
 
-            this._rfb_tightvnc = true;
+            this._rfbTightVNC = true;
 
             if (numTunnels > 0) {
-                this._negotiate_tight_tunnels(numTunnels);
+                this._negotiateTightTunnels(numTunnels);
                 return false;  // wait until we receive the sub auth to continue
             }
         }
@@ -1033,7 +1585,7 @@
         if (this._sock.rQwait("sub auth count", 4)) { return false; }
         const subAuthCount = this._sock.rQshift32();
         if (subAuthCount === 0) {  // empty sub-auth list received means 'no auth' subtype selected
-            this._rfb_init_state = 'SecurityResult';
+            this._rfbInitState = 'SecurityResult';
             return true;
         }
 
@@ -1041,7 +1593,8 @@
 
         const clientSupportedTypes = {
             'STDVNOAUTH__': 1,
-            'STDVVNCAUTH_': 2
+            'STDVVNCAUTH_': 2,
+            'TGHTULGNAUTH': 129
         };
 
         const serverSupportedTypes = [];
@@ -1061,11 +1614,14 @@
 
                 switch (authType) {
                     case 'STDVNOAUTH__':  // no auth
-                        this._rfb_init_state = 'SecurityResult';
+                        this._rfbInitState = 'SecurityResult';
                         return true;
                     case 'STDVVNCAUTH_': // VNC auth
-                        this._rfb_auth_scheme = 2;
-                        return this._init_msg();
+                        this._rfbAuthScheme = 2;
+                        return this._initMsg();
+                    case 'TGHTULGNAUTH': // UNIX auth
+                        this._rfbAuthScheme = 129;
+                        return this._initMsg();
                     default:
                         return this._fail("Unsupported tiny auth scheme " +
                                           "(scheme: " + authType + ")");
@@ -1076,46 +1632,52 @@
         return this._fail("No supported sub-auth types!");
     }
 
-    _negotiate_authentication() {
-        switch (this._rfb_auth_scheme) {
+    _negotiateAuthentication() {
+        switch (this._rfbAuthScheme) {
             case 1:  // no auth
-                if (this._rfb_version >= 3.8) {
-                    this._rfb_init_state = 'SecurityResult';
+                if (this._rfbVersion >= 3.8) {
+                    this._rfbInitState = 'SecurityResult';
                     return true;
                 }
-                this._rfb_init_state = 'ClientInitialisation';
-                return this._init_msg();
+                this._rfbInitState = 'ClientInitialisation';
+                return this._initMsg();
 
             case 22:  // XVP auth
-                return this._negotiate_xvp_auth();
+                return this._negotiateXvpAuth();
 
             case 2:  // VNC authentication
-                return this._negotiate_std_vnc_auth();
+                return this._negotiateStdVNCAuth();
 
             case 16:  // TightVNC Security Type
-                return this._negotiate_tight_auth();
+                return this._negotiateTightAuth();
+
+            case 19:  // VeNCrypt Security Type
+                return this._negotiateVeNCryptAuth();
+
+            case 129:  // TightVNC UNIX Security Type
+                return this._negotiateTightUnixAuth();
 
             default:
                 return this._fail("Unsupported auth scheme (scheme: " +
-                                  this._rfb_auth_scheme + ")");
+                                  this._rfbAuthScheme + ")");
         }
     }
 
-    _handle_security_result() {
+    _handleSecurityResult() {
         if (this._sock.rQwait('VNC auth response ', 4)) { return false; }
 
         const status = this._sock.rQshift32();
 
         if (status === 0) { // OK
-            this._rfb_init_state = 'ClientInitialisation';
+            this._rfbInitState = 'ClientInitialisation';
             Log.Debug('Authentication OK');
-            return this._init_msg();
+            return this._initMsg();
         } else {
-            if (this._rfb_version >= 3.8) {
-                this._rfb_init_state = "SecurityReason";
-                this._security_context = "security result";
-                this._security_status = status;
-                return this._init_msg();
+            if (this._rfbVersion >= 3.8) {
+                this._rfbInitState = "SecurityReason";
+                this._securityContext = "security result";
+                this._securityStatus = status;
+                return this._initMsg();
             } else {
                 this.dispatchEvent(new CustomEvent(
                     "securityfailure",
@@ -1126,7 +1688,7 @@
         }
     }
 
-    _negotiate_server_init() {
+    _negotiateServerInit() {
         if (this._sock.rQwait("server initialization", 24)) { return false; }
 
         /* Screen size */
@@ -1136,27 +1698,28 @@
         /* PIXEL_FORMAT */
         const bpp         = this._sock.rQshift8();
         const depth       = this._sock.rQshift8();
-        const big_endian  = this._sock.rQshift8();
-        const true_color  = this._sock.rQshift8();
+        const bigEndian  = this._sock.rQshift8();
+        const trueColor  = this._sock.rQshift8();
 
-        const red_max     = this._sock.rQshift16();
-        const green_max   = this._sock.rQshift16();
-        const blue_max    = this._sock.rQshift16();
-        const red_shift   = this._sock.rQshift8();
-        const green_shift = this._sock.rQshift8();
-        const blue_shift  = this._sock.rQshift8();
+        const redMax     = this._sock.rQshift16();
+        const greenMax   = this._sock.rQshift16();
+        const blueMax    = this._sock.rQshift16();
+        const redShift   = this._sock.rQshift8();
+        const greenShift = this._sock.rQshift8();
+        const blueShift  = this._sock.rQshift8();
         this._sock.rQskipBytes(3);  // padding
 
         // NB(directxman12): we don't want to call any callbacks or print messages until
         //                   *after* we're past the point where we could backtrack
 
         /* Connection name/title */
-        const name_length = this._sock.rQshift32();
-        if (this._sock.rQwait('server init name', name_length, 24)) { return false; }
-        this._fb_name = decodeUTF8(this._sock.rQshiftStr(name_length));
+        const nameLength = this._sock.rQshift32();
+        if (this._sock.rQwait('server init name', nameLength, 24)) { return false; }
+        let name = this._sock.rQshiftStr(nameLength);
+        name = decodeUTF8(name, true);
 
-        if (this._rfb_tightvnc) {
-            if (this._sock.rQwait('TightVNC extended server init header', 8, 24 + name_length)) { return false; }
+        if (this._rfbTightVNC) {
+            if (this._sock.rQwait('TightVNC extended server init header', 8, 24 + nameLength)) { return false; }
             // In TightVNC mode, ServerInit message is extended
             const numServerMessages = this._sock.rQshift16();
             const numClientMessages = this._sock.rQshift16();
@@ -1164,7 +1727,7 @@
             this._sock.rQskipBytes(2);  // padding
 
             const totalMessagesLength = (numServerMessages + numClientMessages + numEncodings) * 16;
-            if (this._sock.rQwait('TightVNC extended server init header', totalMessagesLength, 32 + name_length)) { return false; }
+            if (this._sock.rQwait('TightVNC extended server init header', totalMessagesLength, 32 + nameLength)) { return false; }
 
             // we don't actually do anything with the capability information that TIGHT sends,
             // so we just skip the all of this.
@@ -1183,42 +1746,31 @@
         //                   if we backtrack
         Log.Info("Screen: " + width + "x" + height +
                   ", bpp: " + bpp + ", depth: " + depth +
-                  ", big_endian: " + big_endian +
-                  ", true_color: " + true_color +
-                  ", red_max: " + red_max +
-                  ", green_max: " + green_max +
-                  ", blue_max: " + blue_max +
-                  ", red_shift: " + red_shift +
-                  ", green_shift: " + green_shift +
-                  ", blue_shift: " + blue_shift);
+                  ", bigEndian: " + bigEndian +
+                  ", trueColor: " + trueColor +
+                  ", redMax: " + redMax +
+                  ", greenMax: " + greenMax +
+                  ", blueMax: " + blueMax +
+                  ", redShift: " + redShift +
+                  ", greenShift: " + greenShift +
+                  ", blueShift: " + blueShift);
 
-        if (big_endian !== 0) {
-            Log.Warn("Server native endian is not little endian");
-        }
-
-        if (red_shift !== 16) {
-            Log.Warn("Server native red-shift is not 16");
-        }
-
-        if (blue_shift !== 0) {
-            Log.Warn("Server native blue-shift is not 0");
-        }
-
+        // we're past the point where we could backtrack, so it's safe to call this
+        this._setDesktopName(name);
         this._resize(width, height);
 
         if (!this._viewOnly) { this._keyboard.grab(); }
-        if (!this._viewOnly) { this._mouse.grab(); }
 
-        this._fb_depth = 24;
+        this._fbDepth = 24;
 
-        if (this._fb_name === "Intel(r) AMT KVM") {
+        if (this._fbName === "Intel(r) AMT KVM") {
             Log.Warn("Intel AMT KVM only supports 8/16 bit depths. Using low color mode.");
-            this._fb_depth = 8;
+            this._fbDepth = 8;
         }
 
-        RFB.messages.pixelFormat(this._sock, this._fb_depth, true);
+        RFB.messages.pixelFormat(this._sock, this._fbDepth, true);
         this._sendEncodings();
-        RFB.messages.fbUpdateRequest(this._sock, false, 0, 0, this._fb_width, this._fb_height);
+        RFB.messages.fbUpdateRequest(this._sock, false, 0, 0, this._fbWidth, this._fbHeight);
 
         this._updateConnectionState('connected');
         return true;
@@ -1230,7 +1782,7 @@
         // In preference order
         encs.push(encodings.encodingCopyRect);
         // Only supported with full depth support
-        if (this._fb_depth == 24) {
+        if (this._fbDepth == 24) {
             encs.push(encodings.encodingTight);
             encs.push(encodings.encodingTightPNG);
             encs.push(encodings.encodingHextile);
@@ -1239,8 +1791,8 @@
         encs.push(encodings.encodingRaw);
 
         // Psuedo-encoding settings
-        encs.push(encodings.pseudoEncodingQualityLevel0 + 6);
-        encs.push(encodings.pseudoEncodingCompressLevel0 + 2);
+        encs.push(encodings.pseudoEncodingQualityLevel0 + this._qualityLevel);
+        encs.push(encodings.pseudoEncodingCompressLevel0 + this._compressionLevel);
 
         encs.push(encodings.pseudoEncodingDesktopSize);
         encs.push(encodings.pseudoEncodingLastRect);
@@ -1249,8 +1801,11 @@
         encs.push(encodings.pseudoEncodingXvp);
         encs.push(encodings.pseudoEncodingFence);
         encs.push(encodings.pseudoEncodingContinuousUpdates);
+        encs.push(encodings.pseudoEncodingDesktopName);
+        encs.push(encodings.pseudoEncodingExtendedClipboard);
 
-        if (this._fb_depth == 24) {
+        if (this._fbDepth == 24) {
+            encs.push(encodings.pseudoEncodingVMwareCursor);
             encs.push(encodings.pseudoEncodingCursor);
         }
 
@@ -1265,63 +1820,212 @@
      *   ClientInitialization - not triggered by server message
      *   ServerInitialization
      */
-    _init_msg() {
-        switch (this._rfb_init_state) {
+    _initMsg() {
+        switch (this._rfbInitState) {
             case 'ProtocolVersion':
-                return this._negotiate_protocol_version();
+                return this._negotiateProtocolVersion();
 
             case 'Security':
-                return this._negotiate_security();
+                return this._negotiateSecurity();
 
             case 'Authentication':
-                return this._negotiate_authentication();
+                return this._negotiateAuthentication();
 
             case 'SecurityResult':
-                return this._handle_security_result();
+                return this._handleSecurityResult();
 
             case 'SecurityReason':
-                return this._handle_security_reason();
+                return this._handleSecurityReason();
 
             case 'ClientInitialisation':
-                this._sock.send([0]); // ClientInitialisation for exclusive access
-                this._rfb_init_state = 'ServerInitialisation';
+                this._sock.send([this._shared ? 1 : 0]); // ClientInitialisation
+                this._rfbInitState = 'ServerInitialisation';
                 return true;
 
             case 'ServerInitialisation':
-                return this._negotiate_server_init();
+                return this._negotiateServerInit();
 
             default:
                 return this._fail("Unknown init state (state: " +
-                                  this._rfb_init_state + ")");
+                                  this._rfbInitState + ")");
         }
     }
 
-    _handle_set_colour_map_msg() {
+    _handleSetColourMapMsg() {
         Log.Debug("SetColorMapEntries");
 
         return this._fail("Unexpected SetColorMapEntries message");
     }
 
-    _handle_server_cut_text() {
+    _handleServerCutText() {
         Log.Debug("ServerCutText");
 
         if (this._sock.rQwait("ServerCutText header", 7, 1)) { return false; }
+
         this._sock.rQskipBytes(3);  // Padding
-        const length = this._sock.rQshift32();
-        if (this._sock.rQwait("ServerCutText", length, 8)) { return false; }
 
-        const text = this._sock.rQshiftStr(length);
+        let length = this._sock.rQshift32();
+        length = toSigned32bit(length);
 
-        if (this._viewOnly) { return true; }
+        if (this._sock.rQwait("ServerCutText content", Math.abs(length), 8)) { return false; }
 
-        this.dispatchEvent(new CustomEvent(
-            "clipboard",
-            { detail: { text: text } }));
+        if (length >= 0) {
+            //Standard msg
+            const text = this._sock.rQshiftStr(length);
+            if (this._viewOnly) {
+                return true;
+            }
 
+            this.dispatchEvent(new CustomEvent(
+                "clipboard",
+                { detail: { text: text } }));
+
+        } else {
+            //Extended msg.
+            length = Math.abs(length);
+            const flags = this._sock.rQshift32();
+            let formats = flags & 0x0000FFFF;
+            let actions = flags & 0xFF000000;
+
+            let isCaps = (!!(actions & extendedClipboardActionCaps));
+            if (isCaps) {
+                this._clipboardServerCapabilitiesFormats = {};
+                this._clipboardServerCapabilitiesActions = {};
+
+                // Update our server capabilities for Formats
+                for (let i = 0; i <= 15; i++) {
+                    let index = 1 << i;
+
+                    // Check if format flag is set.
+                    if ((formats & index)) {
+                        this._clipboardServerCapabilitiesFormats[index] = true;
+                        // We don't send unsolicited clipboard, so we
+                        // ignore the size
+                        this._sock.rQshift32();
+                    }
+                }
+
+                // Update our server capabilities for Actions
+                for (let i = 24; i <= 31; i++) {
+                    let index = 1 << i;
+                    this._clipboardServerCapabilitiesActions[index] = !!(actions & index);
+                }
+
+                /*  Caps handling done, send caps with the clients
+                    capabilities set as a response */
+                let clientActions = [
+                    extendedClipboardActionCaps,
+                    extendedClipboardActionRequest,
+                    extendedClipboardActionPeek,
+                    extendedClipboardActionNotify,
+                    extendedClipboardActionProvide
+                ];
+                RFB.messages.extendedClipboardCaps(this._sock, clientActions, {extendedClipboardFormatText: 0});
+
+            } else if (actions === extendedClipboardActionRequest) {
+                if (this._viewOnly) {
+                    return true;
+                }
+
+                // Check if server has told us it can handle Provide and there is clipboard data to send.
+                if (this._clipboardText != null &&
+                    this._clipboardServerCapabilitiesActions[extendedClipboardActionProvide]) {
+
+                    if (formats & extendedClipboardFormatText) {
+                        RFB.messages.extendedClipboardProvide(this._sock, [extendedClipboardFormatText], [this._clipboardText]);
+                    }
+                }
+
+            } else if (actions === extendedClipboardActionPeek) {
+                if (this._viewOnly) {
+                    return true;
+                }
+
+                if (this._clipboardServerCapabilitiesActions[extendedClipboardActionNotify]) {
+
+                    if (this._clipboardText != null) {
+                        RFB.messages.extendedClipboardNotify(this._sock, [extendedClipboardFormatText]);
+                    } else {
+                        RFB.messages.extendedClipboardNotify(this._sock, []);
+                    }
+                }
+
+            } else if (actions === extendedClipboardActionNotify) {
+                if (this._viewOnly) {
+                    return true;
+                }
+
+                if (this._clipboardServerCapabilitiesActions[extendedClipboardActionRequest]) {
+
+                    if (formats & extendedClipboardFormatText) {
+                        RFB.messages.extendedClipboardRequest(this._sock, [extendedClipboardFormatText]);
+                    }
+                }
+
+            } else if (actions === extendedClipboardActionProvide) {
+                if (this._viewOnly) {
+                    return true;
+                }
+
+                if (!(formats & extendedClipboardFormatText)) {
+                    return true;
+                }
+                // Ignore what we had in our clipboard client side.
+                this._clipboardText = null;
+
+                // FIXME: Should probably verify that this data was actually requested
+                let zlibStream = this._sock.rQshiftBytes(length - 4);
+                let streamInflator = new Inflator();
+                let textData = null;
+
+                streamInflator.setInput(zlibStream);
+                for (let i = 0; i <= 15; i++) {
+                    let format = 1 << i;
+
+                    if (formats & format) {
+
+                        let size = 0x00;
+                        let sizeArray = streamInflator.inflate(4);
+
+                        size |= (sizeArray[0] << 24);
+                        size |= (sizeArray[1] << 16);
+                        size |= (sizeArray[2] << 8);
+                        size |= (sizeArray[3]);
+                        let chunk = streamInflator.inflate(size);
+
+                        if (format === extendedClipboardFormatText) {
+                            textData = chunk;
+                        }
+                    }
+                }
+                streamInflator.setInput(null);
+
+                if (textData !== null) {
+                    let tmpText = "";
+                    for (let i = 0; i < textData.length; i++) {
+                        tmpText += String.fromCharCode(textData[i]);
+                    }
+                    textData = tmpText;
+
+                    textData = decodeUTF8(textData);
+                    if ((textData.length > 0) && "\0" === textData.charAt(textData.length - 1)) {
+                        textData = textData.slice(0, -1);
+                    }
+
+                    textData = textData.replace("\r\n", "\n");
+
+                    this.dispatchEvent(new CustomEvent(
+                        "clipboard",
+                        { detail: { text: textData } }));
+                }
+            } else {
+                return this._fail("Unexpected action in extended clipboard message: " + actions);
+            }
+        }
         return true;
     }
 
-    _handle_server_fence_msg() {
+    _handleServerFenceMsg() {
         if (this._sock.rQwait("ServerFence header", 8, 1)) { return false; }
         this._sock.rQskipBytes(3); // Padding
         let flags = this._sock.rQshift32();
@@ -1363,49 +2067,49 @@
         return true;
     }
 
-    _handle_xvp_msg() {
+    _handleXvpMsg() {
         if (this._sock.rQwait("XVP version and message", 3, 1)) { return false; }
         this._sock.rQskipBytes(1);  // Padding
-        const xvp_ver = this._sock.rQshift8();
-        const xvp_msg = this._sock.rQshift8();
+        const xvpVer = this._sock.rQshift8();
+        const xvpMsg = this._sock.rQshift8();
 
-        switch (xvp_msg) {
+        switch (xvpMsg) {
             case 0:  // XVP_FAIL
                 Log.Error("XVP Operation Failed");
                 break;
             case 1:  // XVP_INIT
-                this._rfb_xvp_ver = xvp_ver;
-                Log.Info("XVP extensions enabled (version " + this._rfb_xvp_ver + ")");
+                this._rfbXvpVer = xvpVer;
+                Log.Info("XVP extensions enabled (version " + this._rfbXvpVer + ")");
                 this._setCapability("power", true);
                 break;
             default:
-                this._fail("Illegal server XVP message (msg: " + xvp_msg + ")");
+                this._fail("Illegal server XVP message (msg: " + xvpMsg + ")");
                 break;
         }
 
         return true;
     }
 
-    _normal_msg() {
-        let msg_type;
+    _normalMsg() {
+        let msgType;
         if (this._FBU.rects > 0) {
-            msg_type = 0;
+            msgType = 0;
         } else {
-            msg_type = this._sock.rQshift8();
+            msgType = this._sock.rQshift8();
         }
 
         let first, ret;
-        switch (msg_type) {
+        switch (msgType) {
             case 0:  // FramebufferUpdate
                 ret = this._framebufferUpdate();
                 if (ret && !this._enabledContinuousUpdates) {
                     RFB.messages.fbUpdateRequest(this._sock, true, 0, 0,
-                                                 this._fb_width, this._fb_height);
+                                                 this._fbWidth, this._fbHeight);
                 }
                 return ret;
 
             case 1:  // SetColorMapEntries
-                return this._handle_set_colour_map_msg();
+                return this._handleSetColourMapMsg();
 
             case 2:  // Bell
                 Log.Debug("Bell");
@@ -1415,7 +2119,7 @@
                 return true;
 
             case 3:  // ServerCutText
-                return this._handle_server_cut_text();
+                return this._handleServerCutText();
 
             case 150: // EndOfContinuousUpdates
                 first = !this._supportsContinuousUpdates;
@@ -1432,13 +2136,13 @@
                 return true;
 
             case 248: // ServerFence
-                return this._handle_server_fence_msg();
+                return this._handleServerFenceMsg();
 
             case 250:  // XVP
-                return this._handle_xvp_msg();
+                return this._handleXvpMsg();
 
             default:
-                this._fail("Unexpected server message (type " + msg_type + ")");
+                this._fail("Unexpected server message (type " + msgType + ")");
                 Log.Debug("sock.rQslice(0, 30): " + this._sock.rQslice(0, 30));
                 return true;
         }
@@ -1448,7 +2152,7 @@
         this._flushing = false;
         // Resume processing
         if (this._sock.rQlen > 0) {
-            this._handle_message();
+            this._handleMessage();
         }
     }
 
@@ -1500,6 +2204,9 @@
                 this._FBU.rects = 1; // Will be decreased when we return
                 return true;
 
+            case encodings.pseudoEncodingVMwareCursor:
+                return this._handleVMwareCursor();
+
             case encodings.pseudoEncodingCursor:
                 return this._handleCursor();
 
@@ -1515,6 +2222,9 @@
                 }
                 return true;
 
+            case encodings.pseudoEncodingDesktopName:
+                return this._handleDesktopName();
+
             case encodings.pseudoEncodingDesktopSize:
                 this._resize(this._FBU.width, this._FBU.height);
                 return true;
@@ -1527,6 +2237,122 @@
         }
     }
 
+    _handleVMwareCursor() {
+        const hotx = this._FBU.x;  // hotspot-x
+        const hoty = this._FBU.y;  // hotspot-y
+        const w = this._FBU.width;
+        const h = this._FBU.height;
+        if (this._sock.rQwait("VMware cursor encoding", 1)) {
+            return false;
+        }
+
+        const cursorType = this._sock.rQshift8();
+
+        this._sock.rQshift8(); //Padding
+
+        let rgba;
+        const bytesPerPixel = 4;
+
+        //Classic cursor
+        if (cursorType == 0) {
+            //Used to filter away unimportant bits.
+            //OR is used for correct conversion in js.
+            const PIXEL_MASK = 0xffffff00 | 0;
+            rgba = new Array(w * h * bytesPerPixel);
+
+            if (this._sock.rQwait("VMware cursor classic encoding",
+                                  (w * h * bytesPerPixel) * 2, 2)) {
+                return false;
+            }
+
+            let andMask = new Array(w * h);
+            for (let pixel = 0; pixel < (w * h); pixel++) {
+                andMask[pixel] = this._sock.rQshift32();
+            }
+
+            let xorMask = new Array(w * h);
+            for (let pixel = 0; pixel < (w * h); pixel++) {
+                xorMask[pixel] = this._sock.rQshift32();
+            }
+
+            for (let pixel = 0; pixel < (w * h); pixel++) {
+                if (andMask[pixel] == 0) {
+                    //Fully opaque pixel
+                    let bgr = xorMask[pixel];
+                    let r   = bgr >> 8  & 0xff;
+                    let g   = bgr >> 16 & 0xff;
+                    let b   = bgr >> 24 & 0xff;
+
+                    rgba[(pixel * bytesPerPixel)     ] = r;    //r
+                    rgba[(pixel * bytesPerPixel) + 1 ] = g;    //g
+                    rgba[(pixel * bytesPerPixel) + 2 ] = b;    //b
+                    rgba[(pixel * bytesPerPixel) + 3 ] = 0xff; //a
+
+                } else if ((andMask[pixel] & PIXEL_MASK) ==
+                           PIXEL_MASK) {
+                    //Only screen value matters, no mouse colouring
+                    if (xorMask[pixel] == 0) {
+                        //Transparent pixel
+                        rgba[(pixel * bytesPerPixel)     ] = 0x00;
+                        rgba[(pixel * bytesPerPixel) + 1 ] = 0x00;
+                        rgba[(pixel * bytesPerPixel) + 2 ] = 0x00;
+                        rgba[(pixel * bytesPerPixel) + 3 ] = 0x00;
+
+                    } else if ((xorMask[pixel] & PIXEL_MASK) ==
+                               PIXEL_MASK) {
+                        //Inverted pixel, not supported in browsers.
+                        //Fully opaque instead.
+                        rgba[(pixel * bytesPerPixel)     ] = 0x00;
+                        rgba[(pixel * bytesPerPixel) + 1 ] = 0x00;
+                        rgba[(pixel * bytesPerPixel) + 2 ] = 0x00;
+                        rgba[(pixel * bytesPerPixel) + 3 ] = 0xff;
+
+                    } else {
+                        //Unhandled xorMask
+                        rgba[(pixel * bytesPerPixel)     ] = 0x00;
+                        rgba[(pixel * bytesPerPixel) + 1 ] = 0x00;
+                        rgba[(pixel * bytesPerPixel) + 2 ] = 0x00;
+                        rgba[(pixel * bytesPerPixel) + 3 ] = 0xff;
+                    }
+
+                } else {
+                    //Unhandled andMask
+                    rgba[(pixel * bytesPerPixel)     ] = 0x00;
+                    rgba[(pixel * bytesPerPixel) + 1 ] = 0x00;
+                    rgba[(pixel * bytesPerPixel) + 2 ] = 0x00;
+                    rgba[(pixel * bytesPerPixel) + 3 ] = 0xff;
+                }
+            }
+
+        //Alpha cursor.
+        } else if (cursorType == 1) {
+            if (this._sock.rQwait("VMware cursor alpha encoding",
+                                  (w * h * 4), 2)) {
+                return false;
+            }
+
+            rgba = new Array(w * h * bytesPerPixel);
+
+            for (let pixel = 0; pixel < (w * h); pixel++) {
+                let data = this._sock.rQshift32();
+
+                rgba[(pixel * 4)     ] = data >> 24 & 0xff; //r
+                rgba[(pixel * 4) + 1 ] = data >> 16 & 0xff; //g
+                rgba[(pixel * 4) + 2 ] = data >> 8 & 0xff;  //b
+                rgba[(pixel * 4) + 3 ] = data & 0xff;       //a
+            }
+
+        } else {
+            Log.Warn("The given cursor type is not supported: "
+                      + cursorType + " given.");
+            return false;
+        }
+
+        this._updateCursor(rgba, hotx, hoty, w, h);
+
+        return true;
+    }
+
     _handleCursor() {
         const hotx = this._FBU.x;  // hotspot-x
         const hoty = this._FBU.y;  // hotspot-y
@@ -1546,16 +2372,16 @@
         const mask = this._sock.rQshiftBytes(masklength);
         let rgba = new Uint8Array(w * h * 4);
 
-        let pix_idx = 0;
+        let pixIdx = 0;
         for (let y = 0; y < h; y++) {
             for (let x = 0; x < w; x++) {
-                let mask_idx = y * Math.ceil(w / 8) + Math.floor(x / 8);
-                let alpha = (mask[mask_idx] << (x % 8)) & 0x80 ? 255 : 0;
-                rgba[pix_idx    ] = pixels[pix_idx + 2];
-                rgba[pix_idx + 1] = pixels[pix_idx + 1];
-                rgba[pix_idx + 2] = pixels[pix_idx];
-                rgba[pix_idx + 3] = alpha;
-                pix_idx += 4;
+                let maskIdx = y * Math.ceil(w / 8) + Math.floor(x / 8);
+                let alpha = (mask[maskIdx] << (x % 8)) & 0x80 ? 255 : 0;
+                rgba[pixIdx    ] = pixels[pixIdx + 2];
+                rgba[pixIdx + 1] = pixels[pixIdx + 1];
+                rgba[pixIdx + 2] = pixels[pixIdx];
+                rgba[pixIdx + 3] = alpha;
+                pixIdx += 4;
             }
         }
 
@@ -1564,14 +2390,33 @@
         return true;
     }
 
+    _handleDesktopName() {
+        if (this._sock.rQwait("DesktopName", 4)) {
+            return false;
+        }
+
+        let length = this._sock.rQshift32();
+
+        if (this._sock.rQwait("DesktopName", length, 4)) {
+            return false;
+        }
+
+        let name = this._sock.rQshiftStr(length);
+        name = decodeUTF8(name, true);
+
+        this._setDesktopName(name);
+
+        return true;
+    }
+
     _handleExtendedDesktopSize() {
         if (this._sock.rQwait("ExtendedDesktopSize", 4)) {
             return false;
         }
 
-        const number_of_screens = this._sock.rQpeek8();
+        const numberOfScreens = this._sock.rQpeek8();
 
-        let bytes = 4 + (number_of_screens * 16);
+        let bytes = 4 + (numberOfScreens * 16);
         if (this._sock.rQwait("ExtendedDesktopSize", bytes)) {
             return false;
         }
@@ -1590,15 +2435,15 @@
         this._sock.rQskipBytes(1);  // number-of-screens
         this._sock.rQskipBytes(3);  // padding
 
-        for (let i = 0; i < number_of_screens; i += 1) {
+        for (let i = 0; i < numberOfScreens; i += 1) {
             // Save the id and flags of the first screen
             if (i === 0) {
-                this._screen_id = this._sock.rQshiftBytes(4);    // id
+                this._screenID = this._sock.rQshiftBytes(4);    // id
                 this._sock.rQskipBytes(2);                       // x-position
                 this._sock.rQskipBytes(2);                       // y-position
                 this._sock.rQskipBytes(2);                       // width
                 this._sock.rQskipBytes(2);                       // height
-                this._screen_flags = this._sock.rQshiftBytes(4); // flags
+                this._screenFlags = this._sock.rQshiftBytes(4); // flags
             } else {
                 this._sock.rQskipBytes(16);
             }
@@ -1651,7 +2496,7 @@
             return decoder.decodeRect(this._FBU.x, this._FBU.y,
                                       this._FBU.width, this._FBU.height,
                                       this._sock, this._display,
-                                      this._fb_depth);
+                                      this._fbDepth);
         } catch (err) {
             this._fail("Error decoding rect: " + err);
             return false;
@@ -1662,14 +2507,14 @@
         if (!this._enabledContinuousUpdates) { return; }
 
         RFB.messages.enableContinuousUpdates(this._sock, true, 0, 0,
-                                             this._fb_width, this._fb_height);
+                                             this._fbWidth, this._fbHeight);
     }
 
     _resize(width, height) {
-        this._fb_width = width;
-        this._fb_height = height;
+        this._fbWidth = width;
+        this._fbHeight = height;
 
-        this._display.resize(this._fb_width, this._fb_height);
+        this._display.resize(this._fbWidth, this._fbHeight);
 
         // Adjust the visible viewport based on the new dimensions
         this._updateClip();
@@ -1679,7 +2524,7 @@
     }
 
     _xvpOp(ver, op) {
-        if (this._rfb_xvp_ver < ver) { return; }
+        if (this._rfbXvpVer < ver) { return; }
         Log.Info("Sending XVP operation " + op + " (version " + ver + ")");
         RFB.messages.xvpOp(this._sock, ver, op);
     }
@@ -1715,6 +2560,10 @@
     }
 
     _refreshCursor() {
+        if (this._rfbConnectionState !== "connecting" &&
+            this._rfbConnectionState !== "connected") {
+            return;
+        }
         const image = this._shouldShowDotCursor() ? RFB.cursors.dot : this._cursorImage;
         this._cursor.change(image.rgbaPixels,
                             image.hotx, image.hoty,
@@ -1750,13 +2599,13 @@
     },
 
     QEMUExtendedKeyEvent(sock, keysym, down, keycode) {
-        function getRFBkeycode(xt_scancode) {
+        function getRFBkeycode(xtScanCode) {
             const upperByte = (keycode >> 8);
             const lowerByte = (keycode & 0x00ff);
             if (upperByte === 0xe0 && lowerByte < 0x7f) {
                 return lowerByte | 0x80;
             }
-            return xt_scancode;
+            return xtScanCode;
         }
 
         const buff = sock._sQ;
@@ -1802,8 +2651,102 @@
         sock.flush();
     },
 
-    // TODO(directxman12): make this unicode compatible?
-    clientCutText(sock, text) {
+    // Used to build Notify and Request data.
+    _buildExtendedClipboardFlags(actions, formats) {
+        let data = new Uint8Array(4);
+        let formatFlag = 0x00000000;
+        let actionFlag = 0x00000000;
+
+        for (let i = 0; i < actions.length; i++) {
+            actionFlag |= actions[i];
+        }
+
+        for (let i = 0; i < formats.length; i++) {
+            formatFlag |= formats[i];
+        }
+
+        data[0] = actionFlag >> 24; // Actions
+        data[1] = 0x00;             // Reserved
+        data[2] = 0x00;             // Reserved
+        data[3] = formatFlag;       // Formats
+
+        return data;
+    },
+
+    extendedClipboardProvide(sock, formats, inData) {
+        // Deflate incomming data and their sizes
+        let deflator = new Deflator();
+        let dataToDeflate = [];
+
+        for (let i = 0; i < formats.length; i++) {
+            // We only support the format Text at this time
+            if (formats[i] != extendedClipboardFormatText) {
+                throw new Error("Unsupported extended clipboard format for Provide message.");
+            }
+
+            // Change lone \r or \n into \r\n as defined in rfbproto
+            inData[i] = inData[i].replace(/\r\n|\r|\n/gm, "\r\n");
+
+            // Check if it already has \0
+            let text = encodeUTF8(inData[i] + "\0");
+
+            dataToDeflate.push( (text.length >> 24) & 0xFF,
+                                (text.length >> 16) & 0xFF,
+                                (text.length >>  8) & 0xFF,
+                                (text.length & 0xFF));
+
+            for (let j = 0; j < text.length; j++) {
+                dataToDeflate.push(text.charCodeAt(j));
+            }
+        }
+
+        let deflatedData = deflator.deflate(new Uint8Array(dataToDeflate));
+
+        // Build data  to send
+        let data = new Uint8Array(4 + deflatedData.length);
+        data.set(RFB.messages._buildExtendedClipboardFlags([extendedClipboardActionProvide],
+                                                           formats));
+        data.set(deflatedData, 4);
+
+        RFB.messages.clientCutText(sock, data, true);
+    },
+
+    extendedClipboardNotify(sock, formats) {
+        let flags = RFB.messages._buildExtendedClipboardFlags([extendedClipboardActionNotify],
+                                                              formats);
+        RFB.messages.clientCutText(sock, flags, true);
+    },
+
+    extendedClipboardRequest(sock, formats) {
+        let flags = RFB.messages._buildExtendedClipboardFlags([extendedClipboardActionRequest],
+                                                              formats);
+        RFB.messages.clientCutText(sock, flags, true);
+    },
+
+    extendedClipboardCaps(sock, actions, formats) {
+        let formatKeys = Object.keys(formats);
+        let data  = new Uint8Array(4 + (4 * formatKeys.length));
+
+        formatKeys.map(x => parseInt(x));
+        formatKeys.sort((a, b) =>  a - b);
+
+        data.set(RFB.messages._buildExtendedClipboardFlags(actions, []));
+
+        let loopOffset = 4;
+        for (let i = 0; i < formatKeys.length; i++) {
+            data[loopOffset]     = formats[formatKeys[i]] >> 24;
+            data[loopOffset + 1] = formats[formatKeys[i]] >> 16;
+            data[loopOffset + 2] = formats[formatKeys[i]] >> 8;
+            data[loopOffset + 3] = formats[formatKeys[i]] >> 0;
+
+            loopOffset += 4;
+            data[3] |= (1 << formatKeys[i]); // Update our format flags
+        }
+
+        RFB.messages.clientCutText(sock, data, true);
+    },
+
+    clientCutText(sock, data, extended = false) {
         const buff = sock._sQ;
         const offset = sock._sQlen;
 
@@ -1813,7 +2756,12 @@
         buff[offset + 2] = 0; // padding
         buff[offset + 3] = 0; // padding
 
-        let length = text.length;
+        let length;
+        if (extended) {
+            length = toUnsigned32bit(-data.length);
+        } else {
+            length = data.length;
+        }
 
         buff[offset + 4] = length >> 24;
         buff[offset + 5] = length >> 16;
@@ -1822,24 +2770,25 @@
 
         sock._sQlen += 8;
 
-        // We have to keep track of from where in the text we begin creating the
+        // We have to keep track of from where in the data we begin creating the
         // buffer for the flush in the next iteration.
-        let textOffset = 0;
+        let dataOffset = 0;
 
-        let remaining = length;
+        let remaining = data.length;
         while (remaining > 0) {
 
             let flushSize = Math.min(remaining, (sock._sQbufferSize - sock._sQlen));
             for (let i = 0; i < flushSize; i++) {
-                buff[sock._sQlen + i] =  text.charCodeAt(textOffset + i);
+                buff[sock._sQlen + i] = data[dataOffset + i];
             }
 
             sock._sQlen += flushSize;
             sock.flush();
 
             remaining -= flushSize;
-            textOffset += flushSize;
+            dataOffset += flushSize;
         }
+
     },
 
     setDesktopSize(sock, width, height, id, flags) {
@@ -1925,7 +2874,7 @@
         sock.flush();
     },
 
-    pixelFormat(sock, depth, true_color) {
+    pixelFormat(sock, depth, trueColor) {
         const buff = sock._sQ;
         const offset = sock._sQlen;
 
@@ -1950,7 +2899,7 @@
         buff[offset + 4] = bpp;                 // bits-per-pixel
         buff[offset + 5] = depth;               // depth
         buff[offset + 6] = 0;                   // little-endian
-        buff[offset + 7] = true_color ? 1 : 0;  // true-color
+        buff[offset + 7] = trueColor ? 1 : 0;  // true-color
 
         buff[offset + 8] = 0;    // red-max
         buff[offset + 9] = (1 << bits) - 1;  // red-max
@@ -1961,9 +2910,9 @@
         buff[offset + 12] = 0;   // blue-max
         buff[offset + 13] = (1 << bits) - 1; // blue-max
 
-        buff[offset + 14] = bits * 2; // red-shift
+        buff[offset + 14] = bits * 0; // red-shift
         buff[offset + 15] = bits * 1; // green-shift
-        buff[offset + 16] = bits * 0; // blue-shift
+        buff[offset + 16] = bits * 2; // blue-shift
 
         buff[offset + 17] = 0;   // padding
         buff[offset + 18] = 0;   // padding
diff --git a/systemvm/agent/noVNC/core/util/browser.js b/systemvm/agent/noVNC/core/util/browser.js
index 8996cfe..1554801 100644
--- a/systemvm/agent/noVNC/core/util/browser.js
+++ b/systemvm/agent/noVNC/core/util/browser.js
@@ -1,9 +1,11 @@
 /*
  * noVNC: HTML5 VNC client
- * Copyright (C) 2018 The noVNC Authors
+ * Copyright (C) 2019 The noVNC Authors
  * Licensed under MPL 2.0 (see LICENSE.txt)
  *
  * See README.md for usage and integration instructions.
+ *
+ * Browser feature support detection
  */
 
 import * as Log from './logging.js';
@@ -31,7 +33,7 @@
     const target = document.createElement('canvas');
     target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
 
-    if (target.style.cursor) {
+    if (target.style.cursor.indexOf("url") === 0) {
         Log.Info("Data URI scheme cursor supported");
         _supportsCursorURIs = true;
     } else {
@@ -52,6 +54,38 @@
 }
 export const supportsImageMetadata = _supportsImageMetadata;
 
+let _hasScrollbarGutter = true;
+try {
+    // Create invisible container
+    const container = document.createElement('div');
+    container.style.visibility = 'hidden';
+    container.style.overflow = 'scroll'; // forcing scrollbars
+    document.body.appendChild(container);
+
+    // Create a div and place it in the container
+    const child = document.createElement('div');
+    container.appendChild(child);
+
+    // Calculate the difference between the container's full width
+    // and the child's width - the difference is the scrollbars
+    const scrollbarWidth = (container.offsetWidth - child.offsetWidth);
+
+    // Clean up
+    container.parentNode.removeChild(container);
+
+    _hasScrollbarGutter = scrollbarWidth != 0;
+} catch (exc) {
+    Log.Error("Scrollbar test exception: " + exc);
+}
+export const hasScrollbarGutter = _hasScrollbarGutter;
+
+/*
+ * The functions for detection of platforms and browsers below are exported
+ * but the use of these should be minimized as much as possible.
+ *
+ * It's better to use feature detection than platform detection.
+ */
+
 export function isMac() {
     return navigator && !!(/mac/i).exec(navigator.platform);
 }
@@ -67,10 +101,6 @@
             !!(/ipod/i).exec(navigator.platform));
 }
 
-export function isAndroid() {
-    return navigator && !!(/android/i).exec(navigator.userAgent);
-}
-
 export function isSafari() {
     return navigator && (navigator.userAgent.indexOf('Safari') !== -1 &&
                          navigator.userAgent.indexOf('Chrome') === -1);
diff --git a/systemvm/agent/noVNC/core/util/cursor.js b/systemvm/agent/noVNC/core/util/cursor.js
index 0d0b754..4db1dab 100644
--- a/systemvm/agent/noVNC/core/util/cursor.js
+++ b/systemvm/agent/noVNC/core/util/cursor.js
@@ -1,6 +1,6 @@
 /*
  * noVNC: HTML5 VNC client
- * Copyright (C) 2018 The noVNC Authors
+ * Copyright (C) 2019 The noVNC Authors
  * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
  */
 
@@ -20,7 +20,6 @@
             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 };
@@ -31,9 +30,6 @@
             '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),
         };
     }
 
@@ -45,6 +41,8 @@
         this._target = target;
 
         if (useFallback) {
+            document.body.appendChild(this._canvas);
+
             // FIXME: These don't fire properly except for mouse
             ///       movement in IE. We want to also capture element
             //        movement, size changes, visibility, etc.
@@ -53,17 +51,16 @@
             this._target.addEventListener('mouseleave', this._eventHandlers.mouseleave, options);
             this._target.addEventListener('mousemove', this._eventHandlers.mousemove, options);
             this._target.addEventListener('mouseup', this._eventHandlers.mouseup, options);
-
-            // There is no "touchleave" so we monitor touchstart globally
-            window.addEventListener('touchstart', this._eventHandlers.touchstart, options);
-            this._target.addEventListener('touchmove', this._eventHandlers.touchmove, options);
-            this._target.addEventListener('touchend', this._eventHandlers.touchend, options);
         }
 
         this.clear();
     }
 
     detach() {
+        if (!this._target) {
+            return;
+        }
+
         if (useFallback) {
             const options = { capture: true, passive: true };
             this._target.removeEventListener('mouseover', this._eventHandlers.mouseover, options);
@@ -71,9 +68,7 @@
             this._target.removeEventListener('mousemove', this._eventHandlers.mousemove, options);
             this._target.removeEventListener('mouseup', this._eventHandlers.mouseup, options);
 
-            window.removeEventListener('touchstart', this._eventHandlers.touchstart, options);
-            this._target.removeEventListener('touchmove', this._eventHandlers.touchmove, options);
-            this._target.removeEventListener('touchend', this._eventHandlers.touchend, options);
+            document.body.removeChild(this._canvas);
         }
 
         this._target = null;
@@ -124,6 +119,27 @@
         this._hotSpot.y = 0;
     }
 
+    // Mouse events might be emulated, this allows
+    // moving the cursor in such cases
+    move(clientX, clientY) {
+        if (!useFallback) {
+            return;
+        }
+        // clientX/clientY are relative the _visual viewport_,
+        // but our position is relative the _layout viewport_,
+        // so try to compensate when we can
+        if (window.visualViewport) {
+            this._position.x = clientX + window.visualViewport.offsetLeft;
+            this._position.y = clientY + window.visualViewport.offsetTop;
+        } else {
+            this._position.x = clientX;
+            this._position.y = clientY;
+        }
+        this._updatePosition();
+        let target = document.elementFromPoint(clientX, clientY);
+        this._updateVisibility(target);
+    }
+
     _handleMouseOver(event) {
         // This event could be because we're entering the target, or
         // moving around amongst its sub elements. Let the move handler
@@ -132,7 +148,8 @@
     }
 
     _handleMouseLeave(event) {
-        this._hideCursor();
+        // Check if we should show the cursor on the element we are leaving to
+        this._updateVisibility(event.relatedTarget);
     }
 
     _handleMouseMove(event) {
@@ -150,27 +167,29 @@
         // now and adjust visibility based on that.
         let target = document.elementFromPoint(event.clientX, event.clientY);
         this._updateVisibility(target);
-    }
 
-    _handleTouchStart(event) {
-        // Just as for mouseover, we let the move handler deal with it
-        this._handleTouchMove(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(event) {
-        // Same principle as for mouseup
-        let target = document.elementFromPoint(event.changedTouches[0].clientX,
-                                               event.changedTouches[0].clientY);
-        this._updateVisibility(target);
+        // Captures end with a mouseup but we can't know the event order of
+        // mouseup vs releaseCapture.
+        //
+        // In the cases when releaseCapture comes first, the code above is
+        // enough.
+        //
+        // In the cases when the mouseup comes first, we need wait for the
+        // browser to flush all events and then check again if the cursor
+        // should be visible.
+        if (this._captureIsActive()) {
+            window.setTimeout(() => {
+                // We might have detached at this point
+                if (!this._target) {
+                    return;
+                }
+                // Refresh the target from elementFromPoint since queued events
+                // might have altered the DOM
+                target = document.elementFromPoint(event.clientX,
+                                                   event.clientY);
+                this._updateVisibility(target);
+            }, 0);
+        }
     }
 
     _showCursor() {
@@ -189,6 +208,9 @@
     // (i.e. are we over the target, or a child of the target without a
     // different cursor set)
     _shouldShowCursor(target) {
+        if (!target) {
+            return false;
+        }
         // Easy case
         if (target === this._target) {
             return true;
@@ -207,6 +229,11 @@
     }
 
     _updateVisibility(target) {
+        // When the cursor target has capture we want to show the cursor.
+        // So, if a capture is active - look at the captured element instead.
+        if (this._captureIsActive()) {
+            target = document.captureElement;
+        }
         if (this._shouldShowCursor(target)) {
             this._showCursor();
         } else {
@@ -218,4 +245,9 @@
         this._canvas.style.left = this._position.x + "px";
         this._canvas.style.top = this._position.y + "px";
     }
+
+    _captureIsActive() {
+        return document.captureElement &&
+            document.documentElement.contains(document.captureElement);
+    }
 }
diff --git a/systemvm/agent/noVNC/core/util/element.js b/systemvm/agent/noVNC/core/util/element.js
new file mode 100644
index 0000000..466a745
--- /dev/null
+++ b/systemvm/agent/noVNC/core/util/element.js
@@ -0,0 +1,32 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2020 The noVNC Authors
+ * Licensed under MPL 2.0 (see LICENSE.txt)
+ *
+ * See README.md for usage and integration instructions.
+ */
+
+/*
+ * HTML element utility functions
+ */
+
+export function clientToElement(x, y, elem) {
+    const bounds = elem.getBoundingClientRect();
+    let pos = { x: 0, y: 0 };
+    // Clip to target bounds
+    if (x < bounds.left) {
+        pos.x = 0;
+    } else if (x >= bounds.right) {
+        pos.x = bounds.width - 1;
+    } else {
+        pos.x = x - bounds.left;
+    }
+    if (y < bounds.top) {
+        pos.y = 0;
+    } else if (y >= bounds.bottom) {
+        pos.y = bounds.height - 1;
+    } else {
+        pos.y = y - bounds.top;
+    }
+    return pos;
+}
diff --git a/systemvm/agent/noVNC/core/util/events.js b/systemvm/agent/noVNC/core/util/events.js
index f122279..39eefd4 100644
--- a/systemvm/agent/noVNC/core/util/events.js
+++ b/systemvm/agent/noVNC/core/util/events.js
@@ -21,7 +21,8 @@
 
 // Emulate Element.setCapture() when not supported
 let _captureRecursion = false;
-let _captureElem = null;
+let _elementForUnflushedEvents = null;
+document.captureElement = null;
 function _captureProxy(e) {
     // Recursion protection as we'll see our own event
     if (_captureRecursion) return;
@@ -30,7 +31,11 @@
     const newEv = new e.constructor(e.type, e);
 
     _captureRecursion = true;
-    _captureElem.dispatchEvent(newEv);
+    if (document.captureElement) {
+        document.captureElement.dispatchEvent(newEv);
+    } else {
+        _elementForUnflushedEvents.dispatchEvent(newEv);
+    }
     _captureRecursion = false;
 
     // Avoid double events
@@ -48,58 +53,56 @@
 }
 
 // Follow cursor style of target element
-function _captureElemChanged() {
-    const captureElem = document.getElementById("noVNC_mouse_capture_elem");
-    captureElem.style.cursor = window.getComputedStyle(_captureElem).cursor;
+function _capturedElemChanged() {
+    const proxyElem = document.getElementById("noVNC_mouse_capture_elem");
+    proxyElem.style.cursor = window.getComputedStyle(document.captureElement).cursor;
 }
 
-const _captureObserver = new MutationObserver(_captureElemChanged);
+const _captureObserver = new MutationObserver(_capturedElemChanged);
 
-let _captureIndex = 0;
+export function setCapture(target) {
+    if (target.setCapture) {
 
-export function setCapture(elem) {
-    if (elem.setCapture) {
-
-        elem.setCapture();
+        target.setCapture();
+        document.captureElement = target;
 
         // IE releases capture on 'click' events which might not trigger
-        elem.addEventListener('mouseup', releaseCapture);
+        target.addEventListener('mouseup', releaseCapture);
 
     } else {
         // Release any existing capture in case this method is
         // called multiple times without coordination
         releaseCapture();
 
-        let captureElem = document.getElementById("noVNC_mouse_capture_elem");
+        let proxyElem = document.getElementById("noVNC_mouse_capture_elem");
 
-        if (captureElem === null) {
-            captureElem = document.createElement("div");
-            captureElem.id = "noVNC_mouse_capture_elem";
-            captureElem.style.position = "fixed";
-            captureElem.style.top = "0px";
-            captureElem.style.left = "0px";
-            captureElem.style.width = "100%";
-            captureElem.style.height = "100%";
-            captureElem.style.zIndex = 10000;
-            captureElem.style.display = "none";
-            document.body.appendChild(captureElem);
+        if (proxyElem === null) {
+            proxyElem = document.createElement("div");
+            proxyElem.id = "noVNC_mouse_capture_elem";
+            proxyElem.style.position = "fixed";
+            proxyElem.style.top = "0px";
+            proxyElem.style.left = "0px";
+            proxyElem.style.width = "100%";
+            proxyElem.style.height = "100%";
+            proxyElem.style.zIndex = 10000;
+            proxyElem.style.display = "none";
+            document.body.appendChild(proxyElem);
 
             // This is to make sure callers don't get confused by having
             // our blocking element as the target
-            captureElem.addEventListener('contextmenu', _captureProxy);
+            proxyElem.addEventListener('contextmenu', _captureProxy);
 
-            captureElem.addEventListener('mousemove', _captureProxy);
-            captureElem.addEventListener('mouseup', _captureProxy);
+            proxyElem.addEventListener('mousemove', _captureProxy);
+            proxyElem.addEventListener('mouseup', _captureProxy);
         }
 
-        _captureElem = elem;
-        _captureIndex++;
+        document.captureElement = target;
 
         // Track cursor and get initial cursor
-        _captureObserver.observe(elem, {attributes: true});
-        _captureElemChanged();
+        _captureObserver.observe(target, {attributes: true});
+        _capturedElemChanged();
 
-        captureElem.style.display = "";
+        proxyElem.style.display = "";
 
         // We listen to events on window in order to keep tracking if it
         // happens to leave the viewport
@@ -112,26 +115,26 @@
     if (document.releaseCapture) {
 
         document.releaseCapture();
+        document.captureElement = null;
 
     } else {
-        if (!_captureElem) {
+        if (!document.captureElement) {
             return;
         }
 
-        // There might be events already queued, so we need to wait for
-        // them to flush. E.g. contextmenu in Microsoft Edge
-        window.setTimeout((expected) => {
-            // Only clear it if it's the expected grab (i.e. no one
-            // else has initiated a new grab)
-            if (_captureIndex === expected) {
-                _captureElem = null;
-            }
-        }, 0, _captureIndex);
+        // There might be events already queued. The event proxy needs
+        // access to the captured element for these queued events.
+        // E.g. contextmenu (right-click) in Microsoft Edge
+        //
+        // Before removing the capturedElem pointer we save it to a
+        // temporary variable that the unflushed events can use.
+        _elementForUnflushedEvents = document.captureElement;
+        document.captureElement = null;
 
         _captureObserver.disconnect();
 
-        const captureElem = document.getElementById("noVNC_mouse_capture_elem");
-        captureElem.style.display = "none";
+        const proxyElem = document.getElementById("noVNC_mouse_capture_elem");
+        proxyElem.style.display = "none";
 
         window.removeEventListener('mousemove', _captureProxy);
         window.removeEventListener('mouseup', _captureProxy);
diff --git a/systemvm/agent/noVNC/core/util/eventtarget.js b/systemvm/agent/noVNC/core/util/eventtarget.js
index f54ca9b..a21aa54 100644
--- a/systemvm/agent/noVNC/core/util/eventtarget.js
+++ b/systemvm/agent/noVNC/core/util/eventtarget.js
@@ -1,6 +1,6 @@
 /*
  * noVNC: HTML5 VNC client
- * Copyright (C) 2018 The noVNC Authors
+ * Copyright (C) 2019 The noVNC Authors
  * Licensed under MPL 2.0 (see LICENSE.txt)
  *
  * See README.md for usage and integration instructions.
diff --git a/systemvm/agent/noVNC/core/util/int.js b/systemvm/agent/noVNC/core/util/int.js
new file mode 100644
index 0000000..001f40f
--- /dev/null
+++ b/systemvm/agent/noVNC/core/util/int.js
@@ -0,0 +1,15 @@
+/*
+ * noVNC: HTML5 VNC client
+ * Copyright (C) 2020 The noVNC Authors
+ * Licensed under MPL 2.0 (see LICENSE.txt)
+ *
+ * See README.md for usage and integration instructions.
+ */
+
+export function toUnsigned32bit(toConvert) {
+    return toConvert >>> 0;
+}
+
+export function toSigned32bit(toConvert) {
+    return toConvert | 0;
+}
diff --git a/systemvm/agent/noVNC/core/util/logging.js b/systemvm/agent/noVNC/core/util/logging.js
index 4c8943d..fe449e9 100644
--- a/systemvm/agent/noVNC/core/util/logging.js
+++ b/systemvm/agent/noVNC/core/util/logging.js
@@ -1,6 +1,6 @@
 /*
  * noVNC: HTML5 VNC client
- * Copyright (C) 2018 The noVNC Authors
+ * Copyright (C) 2019 The noVNC Authors
  * Licensed under MPL 2.0 (see LICENSE.txt)
  *
  * See README.md for usage and integration instructions.
@@ -10,18 +10,18 @@
  * Logging/debug routines
  */
 
-let _log_level = 'warn';
+let _logLevel = 'warn';
 
 let Debug = () => {};
 let Info = () => {};
 let Warn = () => {};
 let Error = () => {};
 
-export function init_logging(level) {
+export function initLogging(level) {
     if (typeof level === 'undefined') {
-        level = _log_level;
+        level = _logLevel;
     } else {
-        _log_level = level;
+        _logLevel = level;
     }
 
     Debug = Info = Warn = Error = () => {};
@@ -46,11 +46,11 @@
     }
 }
 
-export function get_logging() {
-    return _log_level;
+export function getLogging() {
+    return _logLevel;
 }
 
 export { Debug, Info, Warn, Error };
 
 // Initialize logging level
-init_logging();
+initLogging();
diff --git a/systemvm/agent/noVNC/core/util/polyfill.js b/systemvm/agent/noVNC/core/util/polyfill.js
index 648ceeb..0e458c8 100644
--- a/systemvm/agent/noVNC/core/util/polyfill.js
+++ b/systemvm/agent/noVNC/core/util/polyfill.js
@@ -1,6 +1,6 @@
 /*
  * noVNC: HTML5 VNC client
- * Copyright (C) 2018 The noVNC Authors
+ * Copyright (C) 2020 The noVNC Authors
  * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
  */
 
@@ -52,3 +52,10 @@
         window.CustomEvent = CustomEvent;
     }
 })();
+
+/* Number.isInteger() (taken from MDN) */
+Number.isInteger = Number.isInteger || function isInteger(value) {
+    return typeof value === 'number' &&
+      isFinite(value) &&
+      Math.floor(value) === value;
+};
diff --git a/systemvm/agent/noVNC/core/util/strings.js b/systemvm/agent/noVNC/core/util/strings.js
index 61f4f23..3dd4b29 100644
--- a/systemvm/agent/noVNC/core/util/strings.js
+++ b/systemvm/agent/noVNC/core/util/strings.js
@@ -1,14 +1,28 @@
 /*
  * noVNC: HTML5 VNC client
- * Copyright (C) 2018 The noVNC Authors
+ * Copyright (C) 2019 The noVNC Authors
  * Licensed under MPL 2.0 (see LICENSE.txt)
  *
  * See README.md for usage and integration instructions.
  */
 
-/*
- * Decode from UTF-8
- */
-export function decodeUTF8(utf8string) {
-    return decodeURIComponent(escape(utf8string));
+// Decode from UTF-8
+export function decodeUTF8(utf8string, allowLatin1=false) {
+    try {
+        return decodeURIComponent(escape(utf8string));
+    } catch (e) {
+        if (e instanceof URIError) {
+            if (allowLatin1) {
+                // If we allow Latin1 we can ignore any decoding fails
+                // and in these cases return the original string
+                return utf8string;
+            }
+        }
+        throw e;
+    }
+}
+
+// Encode to UTF-8
+export function encodeUTF8(DOMString) {
+    return unescape(encodeURIComponent(DOMString));
 }
diff --git a/systemvm/agent/noVNC/core/websock.js b/systemvm/agent/noVNC/core/websock.js
index 51b9a66..3156aed 100644
--- a/systemvm/agent/noVNC/core/websock.js
+++ b/systemvm/agent/noVNC/core/websock.js
@@ -1,6 +1,6 @@
 /*
  * Websock: high-performance binary WebSockets
- * Copyright (C) 2018 The noVNC Authors
+ * Copyright (C) 2019 The noVNC Authors
  * Licensed under MPL 2.0 (see LICENSE.txt)
  *
  * Websock is similar to the standard WebSocket object but with extra
@@ -17,6 +17,8 @@
 // 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.
+// Also copyWithin() for TypedArrays is not supported in IE 11 or
+// Safari 13 (at the moment we want to support Safari 11).
 const ENABLE_COPYWITHIN = false;
 const MAX_RQ_GROW_SIZE = 40 * 1024 * 1024;  // 40 MiB
 
@@ -27,7 +29,6 @@
         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
 
@@ -143,7 +144,7 @@
 
     flush() {
         if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) {
-            this._websocket.send(this._encode_message());
+            this._websocket.send(this._encodeMessage());
             this._sQlen = 0;
         }
     }
@@ -154,7 +155,7 @@
         this.flush();
     }
 
-    send_string(str) {
+    sendString(str) {
         this.send(str.split('').map(chr => chr.charCodeAt(0)));
     }
 
@@ -167,13 +168,13 @@
         this._eventHandlers[evt] = handler;
     }
 
-    _allocate_buffers() {
+    _allocateBuffers() {
         this._rQ = new Uint8Array(this._rQbufferSize);
         this._sQ = new Uint8Array(this._sQbufferSize);
     }
 
     init() {
-        this._allocate_buffers();
+        this._allocateBuffers();
         this._rQi = 0;
         this._websocket = null;
     }
@@ -184,7 +185,7 @@
         this._websocket = new WebSocket(uri, protocols);
         this._websocket.binaryType = 'arraybuffer';
 
-        this._websocket.onmessage = this._recv_message.bind(this);
+        this._websocket.onmessage = this._recvMessage.bind(this);
         this._websocket.onopen = () => {
             Log.Debug('>> WebSock.onopen');
             if (this._websocket.protocol) {
@@ -219,42 +220,46 @@
     }
 
     // private methods
-    _encode_message() {
+    _encodeMessage() {
         // 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(min_fit) {
-        const resizeNeeded = min_fit || this.rQlen > this._rQbufferSize / 2;
+    // We want to move all the unread data to the start of the queue,
+    // e.g. compacting.
+    // The function also expands the receive que if needed, and for
+    // performance reasons we combine these two actions to avoid
+    // unneccessary copying.
+    _expandCompactRQ(minFit) {
+        // if we're using less than 1/8th of the buffer even with the incoming bytes, compact in place
+        // instead of resizing
+        const requiredBufferSize =  (this._rQlen - this._rQi + minFit) * 8;
+        const resizeNeeded = this._rQbufferSize < requiredBufferSize;
+
         if (resizeNeeded) {
-            if (!min_fit) {
-                // just double the size if we need to do compaction
-                this._rQbufferSize *= 2;
-            } else {
-                // otherwise, make sure we satisy rQlen - rQi + min_fit < rQbufferSize / 8
-                this._rQbufferSize = (this.rQlen + min_fit) * 8;
-            }
+            // Make sure we always *at least* double the buffer size, and have at least space for 8x
+            // the current amount of data
+            this._rQbufferSize = Math.max(this._rQbufferSize * 2, requiredBufferSize);
         }
 
         // we don't want to grow unboundedly
         if (this._rQbufferSize > MAX_RQ_GROW_SIZE) {
             this._rQbufferSize = MAX_RQ_GROW_SIZE;
-            if (this._rQbufferSize - this.rQlen < min_fit) {
+            if (this._rQbufferSize - this.rQlen < minFit) {
                 throw new Error("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit");
             }
         }
 
         if (resizeNeeded) {
-            const old_rQbuffer = this._rQ.buffer;
-            this._rQmax = this._rQbufferSize / 8;
+            const oldRQbuffer = this._rQ.buffer;
             this._rQ = new Uint8Array(this._rQbufferSize);
-            this._rQ.set(new Uint8Array(old_rQbuffer, this._rQi));
+            this._rQ.set(new Uint8Array(oldRQbuffer, this._rQi, this._rQlen - this._rQi));
         } else {
             if (ENABLE_COPYWITHIN) {
-                this._rQ.copyWithin(0, this._rQi);
+                this._rQ.copyWithin(0, this._rQi, this._rQlen);
             } else {
-                this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi));
+                this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi, this._rQlen - this._rQi));
             }
         }
 
@@ -262,26 +267,25 @@
         this._rQi = 0;
     }
 
-    _decode_message(data) {
-        // push arraybuffer values onto the end
+    // push arraybuffer values onto the end of the receive que
+    _DecodeMessage(data) {
         const u8 = new Uint8Array(data);
         if (u8.length > this._rQbufferSize - this._rQlen) {
-            this._expand_compact_rQ(u8.length);
+            this._expandCompactRQ(u8.length);
         }
         this._rQ.set(u8, this._rQlen);
         this._rQlen += u8.length;
     }
 
-    _recv_message(e) {
-        this._decode_message(e.data);
+    _recvMessage(e) {
+        this._DecodeMessage(e.data);
         if (this.rQlen > 0) {
             this._eventHandlers.message();
-            // Compact the receive queue
             if (this._rQlen == this._rQi) {
+                // All data has now been processed, this means we
+                // can reset the receive queue.
                 this._rQlen = 0;
                 this._rQi = 0;
-            } else if (this._rQlen > this._rQmax) {
-                this._expand_compact_rQ();
             }
         } else {
             Log.Debug("Ignoring empty message");
diff --git a/systemvm/agent/noVNC/docs/API-internal.md b/systemvm/agent/noVNC/docs/API-internal.md
deleted file mode 100644
index 0b29afb..0000000
--- a/systemvm/agent/noVNC/docs/API-internal.md
+++ /dev/null
@@ -1,122 +0,0 @@
-# 1. Internal Modules
-
-The noVNC client is composed of several internal modules that handle
-rendering, input, networking, etc. Each of the modules is designed to
-be cross-browser and independent from each other.
-
-Note however that the API of these modules is not guaranteed to be
-stable, and this documentation is not maintained as well as the
-official external API.
-
-
-## 1.1 Module List
-
-* __Mouse__ (core/input/mouse.js): Mouse input event handler with
-limited touch support.
-
-* __Keyboard__ (core/input/keyboard.js): Keyboard input event handler with
-non-US keyboard support. Translates keyDown and keyUp events to X11
-keysym values.
-
-* __Display__ (core/display.js): Efficient 2D rendering abstraction
-layered on the HTML5 canvas element.
-
-* __Websock__ (core/websock.js): Websock client from websockify
-with transparent binary data support.
-[Websock API](https://github.com/novnc/websockify/wiki/websock.js) wiki page.
-
-
-## 1.2 Callbacks
-
-For the Mouse, Keyboard and Display objects the callback functions are
-assigned to configuration attributes, just as for the RFB object. The
-WebSock module has a method named 'on' that takes two parameters: the
-callback event name, and the callback function.
-
-## 2. Modules
-
-## 2.1 Mouse Module
-
-### 2.1.1 Configuration Attributes
-
-| name        | type | mode | default  | description
-| ----------- | ---- | ---- | -------- | ------------
-| touchButton | int  | RW   | 1        | Button mask (1, 2, 4) for which click to send on touch devices. 0 means ignore clicks.
-
-### 2.1.2 Methods
-
-| name   | parameters | description
-| ------ | ---------- | ------------
-| grab   | ()         | Begin capturing mouse events
-| ungrab | ()         | Stop capturing mouse events
-
-### 2.1.2 Callbacks
-
-| name          | parameters          | description
-| ------------- | ------------------- | ------------
-| onmousebutton | (x, y, down, bmask) | Handler for mouse button click/release
-| onmousemove   | (x, y)              | Handler for mouse movement
-
-
-## 2.2 Keyboard Module
-
-### 2.2.1 Configuration Attributes
-
-None
-
-### 2.2.2 Methods
-
-| name   | parameters | description
-| ------ | ---------- | ------------
-| grab   | ()         | Begin capturing keyboard events
-| ungrab | ()         | Stop capturing keyboard events
-
-### 2.2.3 Callbacks
-
-| name       | parameters           | description
-| ---------- | -------------------- | ------------
-| onkeypress | (keysym, code, down) | Handler for key press/release
-
-
-## 2.3 Display Module
-
-### 2.3.1 Configuration Attributes
-
-| name         | type  | mode | default | description
-| ------------ | ----- | ---- | ------- | ------------
-| logo         | raw   | RW   |         | Logo to display when cleared: {"width": width, "height": height, "type": mime-type, "data": data}
-| scale        | float | RW   | 1.0     | Display area scale factor 0.0 - 1.0
-| clipViewport | bool  | RW   | false   | Use viewport clipping
-| width        | int   | RO   |         | Display area width
-| height       | int   | RO   |         | Display area height
-
-### 2.3.2 Methods
-
-| name               | parameters                                              | description
-| ------------------ | ------------------------------------------------------- | ------------
-| viewportChangePos  | (deltaX, deltaY)                                        | Move the viewport relative to the current location
-| viewportChangeSize | (width, height)                                         | Change size of the viewport
-| absX               | (x)                                                     | Return X relative to the remote display
-| absY               | (y)                                                     | Return Y relative to the remote display
-| resize             | (width, height)                                         | Set width and height
-| flip               | (from_queue)                                            | Update the visible canvas with the contents of the rendering canvas
-| clear              | ()                                                      | Clear the display (show logo if set)
-| pending            | ()                                                      | Check if there are waiting items in the render queue
-| flush              | ()                                                      | Resume processing the render queue unless it's empty
-| fillRect           | (x, y, width, height, color, from_queue)                | Draw a filled in rectangle
-| copyImage          | (old_x, old_y, new_x, new_y, width, height, from_queue) | Copy a rectangular area
-| imageRect          | (x, y, mime, arr)                                       | Draw a rectangle with an image
-| startTile          | (x, y, width, height, color)                            | Begin updating a tile
-| subTile            | (tile, x, y, w, h, color)                               | Update a sub-rectangle within the given tile
-| finishTile         | ()                                                      | Draw the current tile to the display
-| blitImage          | (x, y, width, height, arr, offset, from_queue)          | Blit pixels (of R,G,B,A) to the display
-| blitRgbImage       | (x, y, width, height, arr, offset, from_queue)          | Blit RGB encoded image to display
-| blitRgbxImage      | (x, y, width, height, arr, offset, from_queue)          | Blit RGBX encoded image to display
-| drawImage          | (img, x, y)                                             | Draw image and track damage
-| autoscale          | (containerWidth, containerHeight)                       | Scale the display
-
-### 2.3.3 Callbacks
-
-| name    | parameters | description
-| ------- | ---------- | ------------
-| onflush | ()         | A display flush has been requested and we are now ready to resume FBU processing
diff --git a/systemvm/agent/noVNC/docs/API.md b/systemvm/agent/noVNC/docs/API.md
deleted file mode 100644
index d587429..0000000
--- a/systemvm/agent/noVNC/docs/API.md
+++ /dev/null
@@ -1,375 +0,0 @@
-# noVNC API
-
-The interface of the noVNC client consists of a single RFB object that
-is instantiated once per connection.
-
-## RFB
-
-The `RFB` object represents a single connection to a VNC server. It
-communicates using a WebSocket that must provide a standard RFB
-protocol stream.
-
-### Constructor
-
-[`RFB()`](#rfb-1)
-  - Creates and returns a new `RFB` object.
-
-### Properties
-
-`viewOnly`
-  - Is a `boolean` indicating if any events (e.g. key presses or mouse
-    movement) should be prevented from being sent to the server.
-    Disabled by default.
-
-`focusOnClick`
-  - Is a `boolean` indicating if keyboard focus should automatically be
-    moved to the remote session when a `mousedown` or `touchstart`
-    event is received.
-
-`touchButton`
-  - Is a `long` controlling the button mask that should be simulated
-    when a touch event is recieved. Uses the same values as
-    [`MouseEvent.button`](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button).
-    Is set to `1` by default.
-
-`clipViewport`
-  - Is a `boolean` indicating if the remote session should be clipped
-    to its container. When disabled scrollbars will be shown to handle
-    the resulting overflow. Disabled by default.
-
-`dragViewport`
-  - Is a `boolean` indicating if mouse events should control the
-    relative position of a clipped remote session. Only relevant if
-    `clipViewport` is enabled. Disabled by default.
-
-`scaleViewport`
-  - Is a `boolean` indicating if the remote session should be scaled
-    locally so it fits its container. When disabled it will be centered
-    if the remote session is smaller than its container, or handled
-    according to `clipViewport` if it is larger. Disabled by default.
-
-`resizeSession`
-  - Is a `boolean` indicating if a request to resize the remote session
-    should be sent whenever the container changes dimensions. Disabled
-    by default.
-
-`showDotCursor`
-  - Is a `boolean` indicating whether a dot cursor should be shown
-    instead of a zero-sized or fully-transparent cursor if the server
-    sets such invisible cursor. Disabled by default.
-
-`background`
-  - Is a valid CSS [background](https://developer.mozilla.org/en-US/docs/Web/CSS/background)
-    style value indicating which background style should be applied
-    to the element containing the remote session screen. The default value is `rgb(40, 40, 40)`
-    (solid gray color).
-
-`capabilities` *Read only*
-  - Is an `Object` indicating which optional extensions are available
-    on the server. Some methods may only be called if the corresponding
-    capability is set. The following capabilities are defined:
-
-    | name     | type      | description
-    | -------- | --------- | -----------
-    | `power`  | `boolean` | Machine power control is available
-
-### Events
-
-[`connect`](#connect)
-  - The `connect` event is fired when the `RFB` object has completed
-    the connection and handshaking with the server.
-
-[`disconnect`](#disconnected)
-  - The `disconnect` event is fired when the `RFB` object disconnects.
-
-[`credentialsrequired`](#credentialsrequired)
-  - The `credentialsrequired` event is fired when more credentials must
-    be given to continue.
-
-[`securityfailure`](#securityfailure)
-  - The `securityfailure` event is fired when the security negotiation
-    with the server fails.
-
-[`clipboard`](#clipboard)
-  - The `clipboard` event is fired when clipboard data is received from
-    the server.
-
-[`bell`](#bell)
-  - The `bell` event is fired when a audible bell request is received
-    from the server.
-
-[`desktopname`](#desktopname)
-  - The `desktopname` event is fired when the remote desktop name
-    changes.
-
-[`capabilities`](#capabilities)
-  - The `capabilities` event is fired when `RFB.capabilities` is
-    updated.
-
-### Methods
-
-[`RFB.disconnect()`](#rfbdisconnect)
-  - Disconnect from the server.
-
-[`RFB.sendCredentials()`](#rfbsendcredentials)
-  - Send credentials to server. Should be called after the
-    [`credentialsrequired`](#credentialsrequired) event has fired.
-
-[`RFB.sendKey()`](#rfbsendKey)
-  - Send a key event.
-
-[`RFB.sendCtrlAltDel()`](#rfbsendctrlaltdel)
-  - Send Ctrl-Alt-Del key sequence.
-
-[`RFB.focus()`](#rfbfocus)
-  - Move keyboard focus to the remote session.
-
-[`RFB.blur()`](#rfbblur)
-  - Remove keyboard focus from the remote session.
-
-[`RFB.machineShutdown()`](#rfbmachineshutdown)
-  - Request a shutdown of the remote machine.
-
-[`RFB.machineReboot()`](#rfbmachinereboot)
-  - Request a reboot of the remote machine.
-
-[`RFB.machineReset()`](#rfbmachinereset)
-  - Request a reset of the remote machine.
-
-[`RFB.clipboardPasteFrom()`](#rfbclipboardPasteFrom)
-  - Send clipboard contents to server.
-
-### Details
-
-#### RFB()
-
-The `RFB()` constructor returns a new `RFB` object and initiates a new
-connection to a specified VNC server.
-
-##### Syntax
-
-    let rfb = new RFB( target, url [, options] );
-
-###### Parameters
-
-**`target`**
-  - A block [`HTMLElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement)
-    that specifies where the `RFB` object should attach itself. The
-    existing contents of the `HTMLElement` will be untouched, but new
-    elements will be added during the lifetime of the `RFB` object.
-
-**`url`**
-  - A `DOMString` specifying the VNC server to connect to. This must be
-    a valid WebSocket URL.
-
-**`options`** *Optional*
-  - An `Object` specifying extra details about how the connection
-    should be made.
-
-    Possible options:
-
-    `shared`
-      - A `boolean` indicating if the remote server should be shared or
-        if any other connected clients should be disconnected. Enabled
-        by default.
-
-    `credentials`
-      - An `Object` specifying the credentials to provide to the server
-        when authenticating. The following credentials are possible:
-
-        | name         | type        | description
-        | ------------ | ----------- | -----------
-        | `"username"` | `DOMString` | The user that authenticates
-        | `"password"` | `DOMString` | Password for the user
-        | `"target"`   | `DOMString` | Target machine or session
-
-    `repeaterID`
-      - A `DOMString` specifying the ID to provide to any VNC repeater
-        encountered.
-
-#### connect
-
-The `connect` event is fired after all the handshaking with the server
-is completed and the connection is fully established. After this event
-the `RFB` object is ready to recieve graphics updates and to send input.
-
-#### disconnect
-
-The `disconnect` event is fired when the connection has been
-terminated. The `detail` property is an `Object` that contains the
-property `clean`. `clean` is a `boolean` indicating if the termination
-was clean or not. In the event of an unexpected termination or an error
-`clean` will be set to false.
-
-#### credentialsrequired
-
-The `credentialsrequired` event is fired when the server requests more
-credentials than were specified to [`RFB()`](#rfb-1). The `detail`
-property is an `Object` containing the property `types` which is an
-`Array` of `DOMString` listing the credentials that are required.
-
-#### securityfailure
-
-The `securityfailure` event is fired when the handshaking process with
-the server fails during the security negotiation step. The `detail`
-property is an `Object` containing the following properties:
-
-| Property | Type        | Description
-| -------- | ----------- | -----------
-| `status` | `long`      | The failure status code
-| `reason` | `DOMString` | The **optional** reason for the failure
-
-The property `status` corresponds to the
-[SecurityResult](https://github.com/rfbproto/rfbproto/blob/master/rfbproto.rst#securityresult)
-status code in cases of failure. A status of zero will not be sent in
-this event since that indicates a successful security handshaking
-process. The optional property `reason` is provided by the server and
-thus the language of the string is not known. However most servers will
-probably send English strings. The server can choose to not send a
-reason and in these cases the `reason` property will be omitted.
-
-#### clipboard
-
-The `clipboard` event is fired when the server has sent clipboard data.
-The `detail` property is an `Object` containing the property `text`
-which is a `DOMString` with the clipboard data.
-
-#### bell
-
-The `bell` event is fired when the server has requested an audible
-bell.
-
-#### desktopname
-
-The `desktopname` event is fired when the name of the remote desktop
-changes. The `detail` property is an `Object` with the property `name`
-which is a `DOMString` specifying the new name.
-
-#### capabilities
-
-The `capabilities` event is fired whenever an entry is added or removed
-from `RFB.capabilities`. The `detail` property is an `Object` with the
-property `capabilities` containing the new value of `RFB.capabilities`.
-
-#### RFB.disconnect()
-
-The `RFB.disconnect()` method is used to disconnect from the currently
-connected server.
-
-##### Syntax
-
-    RFB.disconnect( );
-
-#### RFB.sendCredentials()
-
-The `RFB.sendCredentials()` method is used to provide the missing
-credentials after a `credentialsrequired` event has been fired.
-
-##### Syntax
-
-    RFB.sendCredentials( credentials );
-
-###### Parameters
-
-**`credentials`**
-  - An `Object` specifying the credentials to provide to the server
-    when authenticating. See [`RFB()`](#rfb-1) for details.
-
-#### RFB.sendKey()
-
-The `RFB.sendKey()` method is used to send a key event to the server.
-
-##### Syntax
-
-    RFB.sendKey( keysym, code [, down] );
-
-###### Parameters
-
-**`keysym`**
-  - A `long` specifying the RFB keysym to send. Can be `0` if a valid
-    **`code`** is specified.
-
-**`code`**
-  - A `DOMString` specifying the physical key to send. Valid values are
-    those that can be specified to
-    [`KeyboardEvent.code`](https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/code).
-    If the physical key cannot be determined then `null` shall be
-    specified.
-
-**`down`** *Optional*
-  - A `boolean` specifying if a press or a release event should be
-    sent. If omitted then both a press and release event are sent.
-
-#### RFB.sendCtrlAltDel()
-
-The `RFB.sendCtrlAltDel()` method is used to send the key sequence
-*left Control*, *left Alt*, *Delete*. This is a convenience wrapper
-around [`RFB.sendKey()`](#rfbsendkey).
-
-##### Syntax
-
-    RFB.sendCtrlAltDel( );
-
-#### RFB.focus()
-
-The `RFB.focus()` method sets the keyboard focus on the remote session.
-Keyboard events will be sent to the remote server after this point.
-
-##### Syntax
-
-    RFB.focus( );
-
-#### RFB.blur()
-
-The `RFB.blur()` method remove keyboard focus on the remote session.
-Keyboard events will no longer be sent to the remote server after this
-point.
-
-##### Syntax
-
-    RFB.blur( );
-
-#### RFB.machineShutdown()
-
-The `RFB.machineShutdown()` method is used to request to shut down the
-remote machine. The capability `power` must be set for this method to
-have any effect.
-
-##### Syntax
-
-    RFB.machineShutdown( );
-
-#### RFB.machineReboot()
-
-The `RFB.machineReboot()` method is used to request a clean reboot of
-the remote machine. The capability `power` must be set for this method
-to have any effect.
-
-##### Syntax
-
-    RFB.machineReboot( );
-
-#### RFB.machineReset()
-
-The `RFB.machineReset()` method is used to request a forced reset of
-the remote machine. The capability `power` must be set for this method
-to have any effect.
-
-##### Syntax
-
-    RFB.machineReset( );
-
-#### RFB.clipboardPasteFrom()
-
-The `RFB.clipboardPasteFrom()` method is used to send clipboard data
-to the remote server.
-
-##### Syntax
-
-    RFB.clipboardPasteFrom( text );
-
-###### Parameters
-
-**`text`**
-  - A `DOMString` specifying the clipboard data to send. Currently only
-  characters from ISO 8859-1 are supported.
diff --git a/systemvm/agent/noVNC/docs/EMBEDDING.md b/systemvm/agent/noVNC/docs/EMBEDDING.md
deleted file mode 100644
index 5399b48..0000000
--- a/systemvm/agent/noVNC/docs/EMBEDDING.md
+++ /dev/null
@@ -1,119 +0,0 @@
-# Embedding and Deploying noVNC Application
-
-This document describes how to embed and deploy the noVNC application, which
-includes settings and a full user interface. If you are looking for
-documentation on how to use the core noVNC library in your own application,
-then please see our [library documentation](LIBRARY.md).
-
-## Files
-
-The noVNC application consists of the following files and directories:
-
-* `vnc.html` - The main page for the application and where users should go. It
-  is possible to rename this file.
-
-* `app/` - Support files for the application. Contains code, images, styles and
-  translations.
-
-* `core/` - The core noVNC library.
-
-* `vendor/` - Third party support libraries used by the application and the
-  core library.
-
-The most basic deployment consists of simply serving these files from a web
-server and setting up a WebSocket proxy to the VNC server.
-
-## Parameters
-
-The noVNC application can be controlled by including certain settings in the
-query string. Currently the following options are available:
-
-* `autoconnect` - Automatically connect as soon as the page has finished
-  loading.
-
-* `reconnect` - If noVNC should automatically reconnect if the connection is
-  dropped.
-
-* `reconnect_delay` - How long to wait in milliseconds before attempting to
-  reconnect.
-
-* `host` - The WebSocket host to connect to.
-
-* `port` - The WebSocket port to connect to.
-
-* `encrypt` - If TLS should be used for the WebSocket connection.
-
-* `path` - The WebSocket path to use.
-
-* `password` - The password sent to the server, if required.
-
-* `repeaterID` - The repeater ID to use if a VNC repeater is detected.
-
-* `shared` - If other VNC clients should be disconnected when noVNC connects.
-
-* `bell` - If the keyboard bell should be enabled or not.
-
-* `view_only` - If the remote session should be in non-interactive mode.
-
-* `view_clip` - If the remote session should be clipped or use scrollbars if
-  it cannot fit in the browser.
-
-* `resize` - How to resize the remote session if it is not the same size as
-  the browser window. Can be one of `off`, `scale` and `remote`.
-
-* `show_dot` - If a dot cursor should be shown when the remote server provides
-  no local cursor, or provides a fully-transparent (invisible) cursor.
-
-* `logging` - The console log level. Can be one of `error`, `warn`, `info` or
-  `debug`.
-
-## Pre-conversion of Modules
-
-noVNC is written using ECMAScript 6 modules. Many of the major browsers support
-these modules natively, but not all. By default the noVNC application includes
-a script that can convert these modules to an older format as they are being
-loaded. However this process can be slow and severely increases the load time
-for the application.
-
-It is possible to perform this conversion ahead of time, avoiding the extra
-load times. To do this please follow these steps:
-
- 1. Install Node.js
- 2. Run `npm install` in the noVNC directory
- 3. Run `./utils/use_require.js --with-app --as commonjs`
-
-This will produce a `build/` directory that includes everything needed to run
-the noVNC application.
-
-## HTTP Serving Considerations
-### Browser Cache Issue
-
-If you serve noVNC files using a web server that provides an ETag header, and
-include any options in the query string, a nasty browser cache issue can bite
-you on upgrade, resulting in a red error box. The issue is caused by a mismatch
-between the new vnc.html (which is reloaded because the user has used it with
-new query string after the upgrade) and the old javascript files (that the
-browser reuses from its cache). To avoid this issue, the browser must be told
-to always revalidate cached files using conditional requests. The correct
-semantics are achieved via the (confusingly named) `Cache-Control: no-cache`
-header that needs to be provided in the web server responses.
-
-### Example Server Configurations
-
-Apache:
-
-```
-    # In the main configuration file
-    # (Debian/Ubuntu users: use "a2enmod headers" instead)
-    LoadModule headers_module modules/mod_headers.so
-
-    # In the <Directory> or <Location> block related to noVNC
-    Header set Cache-Control "no-cache"
-```
-
-Nginx:
-
-```
-    # In the location block related to noVNC
-    add_header Cache-Control no-cache;
-```
diff --git a/systemvm/agent/noVNC/docs/LIBRARY.md b/systemvm/agent/noVNC/docs/LIBRARY.md
deleted file mode 100644
index 63f55e8..0000000
--- a/systemvm/agent/noVNC/docs/LIBRARY.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# Using the noVNC JavaScript library
-
-This document describes how to make use of the noVNC JavaScript library for
-integration in your own VNC client application. If you wish to embed the more
-complete noVNC application with its included user interface then please see
-our [embedding documentation](EMBEDDING.md).
-
-## API
-
-The API of noVNC consists of a single object called `RFB`. The formal
-documentation for that object can be found in our [API documentation](API.md).
-
-## Example
-
-noVNC includes a small example application called `vnc_lite.html`. This does
-not make use of all the features of noVNC, but is a good start to see how to
-do things.
-
-## Conversion of Modules
-
-noVNC is written using ECMAScript 6 modules. Many of the major browsers support
-these modules natively, but not all. They are also not supported by Node.js. To
-use noVNC in these places the library must first be converted.
-
-Fortunately noVNC includes a script to handle this conversion. Please follow
-the following steps:
-
- 1. Install Node.js
- 2. Run `npm install` in the noVNC directory
- 3. Run `./utils/use_require.js --as <module format>`
-
-Several module formats are available. Please run
-`./utils/use_require.js --help` to see them all.
-
-The result of the conversion is available in the `lib/` directory.
diff --git a/systemvm/agent/noVNC/docs/LICENSE.BSD-2-Clause b/systemvm/agent/noVNC/docs/LICENSE.BSD-2-Clause
deleted file mode 100644
index 9d66ec9..0000000
--- a/systemvm/agent/noVNC/docs/LICENSE.BSD-2-Clause
+++ /dev/null
@@ -1,22 +0,0 @@
-Copyright (c) <year>, <copyright holder>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-    * Redistributions of source code must retain the above copyright
-      notice, this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above copyright
-      notice, this list of conditions and the following disclaimer in the
-      documentation and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/systemvm/agent/noVNC/docs/LICENSE.BSD-3-Clause b/systemvm/agent/noVNC/docs/LICENSE.BSD-3-Clause
deleted file mode 100644
index e160466..0000000
--- a/systemvm/agent/noVNC/docs/LICENSE.BSD-3-Clause
+++ /dev/null
@@ -1,24 +0,0 @@
-Copyright (c) <year>, <copyright holder>
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-    * Redistributions of source code must retain the above copyright
-      notice, this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above copyright
-      notice, this list of conditions and the following disclaimer in the
-      documentation and/or other materials provided with the distribution.
-    * Neither the name of the <organization> nor the
-      names of its contributors may be used to endorse or promote products
-      derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/systemvm/agent/noVNC/docs/LICENSE.MPL-2.0 b/systemvm/agent/noVNC/docs/LICENSE.MPL-2.0
deleted file mode 100644
index 14e2f77..0000000
--- a/systemvm/agent/noVNC/docs/LICENSE.MPL-2.0
+++ /dev/null
@@ -1,373 +0,0 @@
-Mozilla Public License Version 2.0
-==================================
-
-1. Definitions
---------------
-
-1.1. "Contributor"
-    means each individual or legal entity that creates, contributes to
-    the creation of, or owns Covered Software.
-
-1.2. "Contributor Version"
-    means the combination of the Contributions of others (if any) used
-    by a Contributor and that particular Contributor's Contribution.
-
-1.3. "Contribution"
-    means Covered Software of a particular Contributor.
-
-1.4. "Covered Software"
-    means Source Code Form to which the initial Contributor has attached
-    the notice in Exhibit A, the Executable Form of such Source Code
-    Form, and Modifications of such Source Code Form, in each case
-    including portions thereof.
-
-1.5. "Incompatible With Secondary Licenses"
-    means
-
-    (a) that the initial Contributor has attached the notice described
-        in Exhibit B to the Covered Software; or
-
-    (b) that the Covered Software was made available under the terms of
-        version 1.1 or earlier of the License, but not also under the
-        terms of a Secondary License.
-
-1.6. "Executable Form"
-    means any form of the work other than Source Code Form.
-
-1.7. "Larger Work"
-    means a work that combines Covered Software with other material, in 
-    a separate file or files, that is not Covered Software.
-
-1.8. "License"
-    means this document.
-
-1.9. "Licensable"
-    means having the right to grant, to the maximum extent possible,
-    whether at the time of the initial grant or subsequently, any and
-    all of the rights conveyed by this License.
-
-1.10. "Modifications"
-    means any of the following:
-
-    (a) any file in Source Code Form that results from an addition to,
-        deletion from, or modification of the contents of Covered
-        Software; or
-
-    (b) any new file in Source Code Form that contains any Covered
-        Software.
-
-1.11. "Patent Claims" of a Contributor
-    means any patent claim(s), including without limitation, method,
-    process, and apparatus claims, in any patent Licensable by such
-    Contributor that would be infringed, but for the grant of the
-    License, by the making, using, selling, offering for sale, having
-    made, import, or transfer of either its Contributions or its
-    Contributor Version.
-
-1.12. "Secondary License"
-    means either the GNU General Public License, Version 2.0, the GNU
-    Lesser General Public License, Version 2.1, the GNU Affero General
-    Public License, Version 3.0, or any later versions of those
-    licenses.
-
-1.13. "Source Code Form"
-    means the form of the work preferred for making modifications.
-
-1.14. "You" (or "Your")
-    means an individual or a legal entity exercising rights under this
-    License. For legal entities, "You" includes any entity that
-    controls, is controlled by, or is under common control with You. For
-    purposes of this definition, "control" means (a) the power, direct
-    or indirect, to cause the direction or management of such entity,
-    whether by contract or otherwise, or (b) ownership of more than
-    fifty percent (50%) of the outstanding shares or beneficial
-    ownership of such entity.
-
-2. License Grants and Conditions
---------------------------------
-
-2.1. Grants
-
-Each Contributor hereby grants You a world-wide, royalty-free,
-non-exclusive license:
-
-(a) under intellectual property rights (other than patent or trademark)
-    Licensable by such Contributor to use, reproduce, make available,
-    modify, display, perform, distribute, and otherwise exploit its
-    Contributions, either on an unmodified basis, with Modifications, or
-    as part of a Larger Work; and
-
-(b) under Patent Claims of such Contributor to make, use, sell, offer
-    for sale, have made, import, and otherwise transfer either its
-    Contributions or its Contributor Version.
-
-2.2. Effective Date
-
-The licenses granted in Section 2.1 with respect to any Contribution
-become effective for each Contribution on the date the Contributor first
-distributes such Contribution.
-
-2.3. Limitations on Grant Scope
-
-The licenses granted in this Section 2 are the only rights granted under
-this License. No additional rights or licenses will be implied from the
-distribution or licensing of Covered Software under this License.
-Notwithstanding Section 2.1(b) above, no patent license is granted by a
-Contributor:
-
-(a) for any code that a Contributor has removed from Covered Software;
-    or
-
-(b) for infringements caused by: (i) Your and any other third party's
-    modifications of Covered Software, or (ii) the combination of its
-    Contributions with other software (except as part of its Contributor
-    Version); or
-
-(c) under Patent Claims infringed by Covered Software in the absence of
-    its Contributions.
-
-This License does not grant any rights in the trademarks, service marks,
-or logos of any Contributor (except as may be necessary to comply with
-the notice requirements in Section 3.4).
-
-2.4. Subsequent Licenses
-
-No Contributor makes additional grants as a result of Your choice to
-distribute the Covered Software under a subsequent version of this
-License (see Section 10.2) or under the terms of a Secondary License (if
-permitted under the terms of Section 3.3).
-
-2.5. Representation
-
-Each Contributor represents that the Contributor believes its
-Contributions are its original creation(s) or it has sufficient rights
-to grant the rights to its Contributions conveyed by this License.
-
-2.6. Fair Use
-
-This License is not intended to limit any rights You have under
-applicable copyright doctrines of fair use, fair dealing, or other
-equivalents.
-
-2.7. Conditions
-
-Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
-in Section 2.1.
-
-3. Responsibilities
--------------------
-
-3.1. Distribution of Source Form
-
-All distribution of Covered Software in Source Code Form, including any
-Modifications that You create or to which You contribute, must be under
-the terms of this License. You must inform recipients that the Source
-Code Form of the Covered Software is governed by the terms of this
-License, and how they can obtain a copy of this License. You may not
-attempt to alter or restrict the recipients' rights in the Source Code
-Form.
-
-3.2. Distribution of Executable Form
-
-If You distribute Covered Software in Executable Form then:
-
-(a) such Covered Software must also be made available in Source Code
-    Form, as described in Section 3.1, and You must inform recipients of
-    the Executable Form how they can obtain a copy of such Source Code
-    Form by reasonable means in a timely manner, at a charge no more
-    than the cost of distribution to the recipient; and
-
-(b) You may distribute such Executable Form under the terms of this
-    License, or sublicense it under different terms, provided that the
-    license for the Executable Form does not attempt to limit or alter
-    the recipients' rights in the Source Code Form under this License.
-
-3.3. Distribution of a Larger Work
-
-You may create and distribute a Larger Work under terms of Your choice,
-provided that You also comply with the requirements of this License for
-the Covered Software. If the Larger Work is a combination of Covered
-Software with a work governed by one or more Secondary Licenses, and the
-Covered Software is not Incompatible With Secondary Licenses, this
-License permits You to additionally distribute such Covered Software
-under the terms of such Secondary License(s), so that the recipient of
-the Larger Work may, at their option, further distribute the Covered
-Software under the terms of either this License or such Secondary
-License(s).
-
-3.4. Notices
-
-You may not remove or alter the substance of any license notices
-(including copyright notices, patent notices, disclaimers of warranty,
-or limitations of liability) contained within the Source Code Form of
-the Covered Software, except that You may alter any license notices to
-the extent required to remedy known factual inaccuracies.
-
-3.5. Application of Additional Terms
-
-You may choose to offer, and to charge a fee for, warranty, support,
-indemnity or liability obligations to one or more recipients of Covered
-Software. However, You may do so only on Your own behalf, and not on
-behalf of any Contributor. You must make it absolutely clear that any
-such warranty, support, indemnity, or liability obligation is offered by
-You alone, and You hereby agree to indemnify every Contributor for any
-liability incurred by such Contributor as a result of warranty, support,
-indemnity or liability terms You offer. You may include additional
-disclaimers of warranty and limitations of liability specific to any
-jurisdiction.
-
-4. Inability to Comply Due to Statute or Regulation
----------------------------------------------------
-
-If it is impossible for You to comply with any of the terms of this
-License with respect to some or all of the Covered Software due to
-statute, judicial order, or regulation then You must: (a) comply with
-the terms of this License to the maximum extent possible; and (b)
-describe the limitations and the code they affect. Such description must
-be placed in a text file included with all distributions of the Covered
-Software under this License. Except to the extent prohibited by statute
-or regulation, such description must be sufficiently detailed for a
-recipient of ordinary skill to be able to understand it.
-
-5. Termination
---------------
-
-5.1. The rights granted under this License will terminate automatically
-if You fail to comply with any of its terms. However, if You become
-compliant, then the rights granted under this License from a particular
-Contributor are reinstated (a) provisionally, unless and until such
-Contributor explicitly and finally terminates Your grants, and (b) on an
-ongoing basis, if such Contributor fails to notify You of the
-non-compliance by some reasonable means prior to 60 days after You have
-come back into compliance. Moreover, Your grants from a particular
-Contributor are reinstated on an ongoing basis if such Contributor
-notifies You of the non-compliance by some reasonable means, this is the
-first time You have received notice of non-compliance with this License
-from such Contributor, and You become compliant prior to 30 days after
-Your receipt of the notice.
-
-5.2. If You initiate litigation against any entity by asserting a patent
-infringement claim (excluding declaratory judgment actions,
-counter-claims, and cross-claims) alleging that a Contributor Version
-directly or indirectly infringes any patent, then the rights granted to
-You by any and all Contributors for the Covered Software under Section
-2.1 of this License shall terminate.
-
-5.3. In the event of termination under Sections 5.1 or 5.2 above, all
-end user license agreements (excluding distributors and resellers) which
-have been validly granted by You or Your distributors under this License
-prior to termination shall survive termination.
-
-************************************************************************
-*                                                                      *
-*  6. Disclaimer of Warranty                                           *
-*  -------------------------                                           *
-*                                                                      *
-*  Covered Software is provided under this License on an "as is"       *
-*  basis, without warranty of any kind, either expressed, implied, or  *
-*  statutory, including, without limitation, warranties that the       *
-*  Covered Software is free of defects, merchantable, fit for a        *
-*  particular purpose or non-infringing. The entire risk as to the     *
-*  quality and performance of the Covered Software is with You.        *
-*  Should any Covered Software prove defective in any respect, You     *
-*  (not any Contributor) assume the cost of any necessary servicing,   *
-*  repair, or correction. This disclaimer of warranty constitutes an   *
-*  essential part of this License. No use of any Covered Software is   *
-*  authorized under this License except under this disclaimer.         *
-*                                                                      *
-************************************************************************
-
-************************************************************************
-*                                                                      *
-*  7. Limitation of Liability                                          *
-*  --------------------------                                          *
-*                                                                      *
-*  Under no circumstances and under no legal theory, whether tort      *
-*  (including negligence), contract, or otherwise, shall any           *
-*  Contributor, or anyone who distributes Covered Software as          *
-*  permitted above, be liable to You for any direct, indirect,         *
-*  special, incidental, or consequential damages of any character      *
-*  including, without limitation, damages for lost profits, loss of    *
-*  goodwill, work stoppage, computer failure or malfunction, or any    *
-*  and all other commercial damages or losses, even if such party      *
-*  shall have been informed of the possibility of such damages. This   *
-*  limitation of liability shall not apply to liability for death or   *
-*  personal injury resulting from such party's negligence to the       *
-*  extent applicable law prohibits such limitation. Some               *
-*  jurisdictions do not allow the exclusion or limitation of           *
-*  incidental or consequential damages, so this exclusion and          *
-*  limitation may not apply to You.                                    *
-*                                                                      *
-************************************************************************
-
-8. Litigation
--------------
-
-Any litigation relating to this License may be brought only in the
-courts of a jurisdiction where the defendant maintains its principal
-place of business and such litigation shall be governed by laws of that
-jurisdiction, without reference to its conflict-of-law provisions.
-Nothing in this Section shall prevent a party's ability to bring
-cross-claims or counter-claims.
-
-9. Miscellaneous
-----------------
-
-This License represents the complete agreement concerning the subject
-matter hereof. If any provision of this License is held to be
-unenforceable, such provision shall be reformed only to the extent
-necessary to make it enforceable. Any law or regulation which provides
-that the language of a contract shall be construed against the drafter
-shall not be used to construe this License against a Contributor.
-
-10. Versions of the License
----------------------------
-
-10.1. New Versions
-
-Mozilla Foundation is the license steward. Except as provided in Section
-10.3, no one other than the license steward has the right to modify or
-publish new versions of this License. Each version will be given a
-distinguishing version number.
-
-10.2. Effect of New Versions
-
-You may distribute the Covered Software under the terms of the version
-of the License under which You originally received the Covered Software,
-or under the terms of any subsequent version published by the license
-steward.
-
-10.3. Modified Versions
-
-If you create software not governed by this License, and you want to
-create a new license for such software, you may create and use a
-modified version of this License if you rename the license and remove
-any references to the name of the license steward (except to note that
-such modified license differs from this License).
-
-10.4. Distributing Source Code Form that is Incompatible With Secondary
-Licenses
-
-If You choose to distribute Source Code Form that is Incompatible With
-Secondary Licenses under the terms of this version of the License, the
-notice described in Exhibit B of this License must be attached.
-
-Exhibit A - Source Code Form License Notice
--------------------------------------------
-
-  This Source Code Form is subject to the terms of the Mozilla Public
-  License, v. 2.0. If a copy of the MPL was not distributed with this
-  file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-If it is not possible or desirable to put the notice in a particular
-file, then You may include the notice in a location (such as a LICENSE
-file in a relevant directory) where a recipient would be likely to look
-for such a notice.
-
-You may add additional accurate notices of copyright ownership.
-
-Exhibit B - "Incompatible With Secondary Licenses" Notice
----------------------------------------------------------
-
-  This Source Code Form is "Incompatible With Secondary Licenses", as
-  defined by the Mozilla Public License, v. 2.0.
diff --git a/systemvm/agent/noVNC/docs/LICENSE.OFL-1.1 b/systemvm/agent/noVNC/docs/LICENSE.OFL-1.1
deleted file mode 100644
index 77b1731..0000000
--- a/systemvm/agent/noVNC/docs/LICENSE.OFL-1.1
+++ /dev/null
@@ -1,91 +0,0 @@
-This Font Software is licensed under the SIL Open Font License, Version 1.1.
-This license is copied below, and is also available with a FAQ at:
-http://scripts.sil.org/OFL
-
-
------------------------------------------------------------
-SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
------------------------------------------------------------
-
-PREAMBLE
-The goals of the Open Font License (OFL) are to stimulate worldwide
-development of collaborative font projects, to support the font creation
-efforts of academic and linguistic communities, and to provide a free and
-open framework in which fonts may be shared and improved in partnership
-with others.
-
-The OFL allows the licensed fonts to be used, studied, modified and
-redistributed freely as long as they are not sold by themselves. The
-fonts, including any derivative works, can be bundled, embedded, 
-redistributed and/or sold with any software provided that any reserved
-names are not used by derivative works. The fonts and derivatives,
-however, cannot be released under any other type of license. The
-requirement for fonts to remain under this license does not apply
-to any document created using the fonts or their derivatives.
-
-DEFINITIONS
-"Font Software" refers to the set of files released by the Copyright
-Holder(s) under this license and clearly marked as such. This may
-include source files, build scripts and documentation.
-
-"Reserved Font Name" refers to any names specified as such after the
-copyright statement(s).
-
-"Original Version" refers to the collection of Font Software components as
-distributed by the Copyright Holder(s).
-
-"Modified Version" refers to any derivative made by adding to, deleting,
-or substituting -- in part or in whole -- any of the components of the
-Original Version, by changing formats or by porting the Font Software to a
-new environment.
-
-"Author" refers to any designer, engineer, programmer, technical
-writer or other person who contributed to the Font Software.
-
-PERMISSION & CONDITIONS
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of the Font Software, to use, study, copy, merge, embed, modify,
-redistribute, and sell modified and unmodified copies of the Font
-Software, subject to the following conditions:
-
-1) Neither the Font Software nor any of its individual components,
-in Original or Modified Versions, may be sold by itself.
-
-2) Original or Modified Versions of the Font Software may be bundled,
-redistributed and/or sold with any software, provided that each copy
-contains the above copyright notice and this license. These can be
-included either as stand-alone text files, human-readable headers or
-in the appropriate machine-readable metadata fields within text or
-binary files as long as those fields can be easily viewed by the user.
-
-3) No Modified Version of the Font Software may use the Reserved Font
-Name(s) unless explicit written permission is granted by the corresponding
-Copyright Holder. This restriction only applies to the primary font name as
-presented to the users.
-
-4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
-Software shall not be used to promote, endorse or advertise any
-Modified Version, except to acknowledge the contribution(s) of the
-Copyright Holder(s) and the Author(s) or with their explicit written
-permission.
-
-5) The Font Software, modified or unmodified, in part or in whole,
-must be distributed entirely under this license, and must not be
-distributed under any other license. The requirement for fonts to
-remain under this license does not apply to any document created
-using the Font Software.
-
-TERMINATION
-This license becomes null and void if any of the above conditions are
-not met.
-
-DISCLAIMER
-THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
-OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
-COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
-DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
-OTHER DEALINGS IN THE FONT SOFTWARE.
diff --git a/systemvm/agent/noVNC/docs/flash_policy.txt b/systemvm/agent/noVNC/docs/flash_policy.txt
deleted file mode 100644
index df325c0..0000000
--- a/systemvm/agent/noVNC/docs/flash_policy.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-Manual setup:
-
-DATA="echo \'<cross-domain-policy><allow-access-from domain=\\\"*\\\" to-ports=\\\"*\\\" /></cross-domain-policy>\'"
-/usr/bin/socat -T 1 TCP-L:843,reuseaddr,fork,crlf SYSTEM:"$DATA"
diff --git a/systemvm/agent/noVNC/docs/links b/systemvm/agent/noVNC/docs/links
deleted file mode 100644
index 31544ce..0000000
--- a/systemvm/agent/noVNC/docs/links
+++ /dev/null
@@ -1,76 +0,0 @@
-New tight PNG protocol:
-    http://wiki.qemu.org/VNC_Tight_PNG
-    http://xf.iksaif.net/blog/index.php?post/2010/06/14/QEMU:-Tight-PNG-and-some-profiling
-
-RFB protocol and extensions:
-    http://tigervnc.org/cgi-bin/rfbproto
-
-Canvas Browser Compatibility:
-    http://philip.html5.org/tests/canvas/suite/tests/results.html
-
-WebSockets API standard:
-    http://www.whatwg.org/specs/web-apps/current-work/complete.html#websocket
-    http://dev.w3.org/html5/websockets/
-    http://www.ietf.org/id/draft-ietf-hybi-thewebsocketprotocol-00.txt
-
-Browser Keyboard Events detailed:
-    http://unixpapa.com/js/key.html
-
-ActionScript (Flash) WebSocket implementation:
-    http://github.com/gimite/web-socket-js
-
-ActionScript (Flash) crypto/TLS library:
-    http://code.google.com/p/as3crypto
-    http://github.com/lyokato/as3crypto_patched
-
-TLS Protocol:
-    http://en.wikipedia.org/wiki/Transport_Layer_Security
-
-Generate self-signed certificate:
-    http://docs.python.org/dev/library/ssl.html#certificates
-
-Cursor appearance/style (for Cursor pseudo-encoding):
-    http://en.wikipedia.org/wiki/ICO_(file_format)
-    http://www.daubnet.com/en/file-format-cur
-    https://developer.mozilla.org/en/Using_URL_values_for_the_cursor_property
-    http://www.fileformat.info/format/bmp/egff.htm
-
-Icon/Cursor file format:
-    http://msdn.microsoft.com/en-us/library/ms997538
-    http://msdn.microsoft.com/en-us/library/aa921550.aspx
-    http://msdn.microsoft.com/en-us/library/aa930622.aspx
-
-
-RDP Protocol specification:
-    http://msdn.microsoft.com/en-us/library/cc240445(v=PROT.10).aspx
-
-
-Related projects:
-    
-    guacamole: http://guacamole.sourceforge.net/
-
-        - Web client, but Java servlet does pre-processing
-
-    jsvnc: http://code.google.com/p/jsvnc/
-
-        - No releases
-
-    webvnc: http://code.google.com/p/webvnc/
-
-        - Jetty web server gateway, no updates since April 2008.
-
-    RealVNC Java applet: http://www.realvnc.com/support/javavncviewer.html
-
-        - Java applet
-
-    Flashlight-VNC: http://www.wizhelp.com/flashlight-vnc/
-
-        - Adobe Flash implementation
-
-    FVNC: http://osflash.org/fvnc
-
-        - Adbove Flash implementation
-
-    CanVNC: http://canvnc.sourceforge.net/
-
-        - HTML client with REST to VNC python proxy. Mostly vapor.
diff --git a/systemvm/agent/noVNC/docs/notes b/systemvm/agent/noVNC/docs/notes
deleted file mode 100644
index dfef0bd..0000000
--- a/systemvm/agent/noVNC/docs/notes
+++ /dev/null
@@ -1,5 +0,0 @@
-Rebuilding inflator.js
-
-- Download pako from npm
-- Install browserify using npm
-- browserify core/inflator.mod.js -o core/inflator.js -s Inflator
diff --git a/systemvm/agent/noVNC/docs/rfb_notes b/systemvm/agent/noVNC/docs/rfb_notes
deleted file mode 100644
index 643e16c..0000000
--- a/systemvm/agent/noVNC/docs/rfb_notes
+++ /dev/null
@@ -1,147 +0,0 @@
-5.1.1 ProtocolVersion: 12, 12 bytes
-
-    - Sent by server, max supported
-        12 ascii - "RFB 003.008\n"
-    - Response by client, version to use
-        12 ascii - "RFB 003.003\n"
-
-5.1.2 Authentication: >=4, [16, 4] bytes
-
-    - Sent by server
-        CARD32 - authentication-scheme
-                0 - connection failed
-                    CARD32 - length
-                    length - reason
-                1 - no authentication
-
-                2 - VNC authentication
-                    16 CARD8 - challenge (random bytes)
-
-    - Response by client (if VNC authentication)
-        16 CARD8 - client encrypts the challenge with DES, using user
-                   password as key, sends resulting 16 byte response
-
-    - Response by server (if VNC authentication) 
-        CARD32 - 0 - OK
-                 1 - failed
-                 2 - too-many
-
-5.1.3 ClientInitialisation: 1 byte
-    - Sent by client
-        CARD8 - shared-flag, 0 exclusive, non-zero shared
-
-5.1.4 ServerInitialisation: >=24 bytes
-    - Sent by server
-        CARD16 - framebuffer-width
-        CARD16 - framebuffer-height
-        16 byte PIXEL_FORMAT - server-pixel-format
-            CARD8 - bits-per-pixel
-            CARD8 - depth
-            CARD8 - big-endian-flag, non-zero is big endian
-            CARD8 - true-color-flag, non-zero then next 6 apply
-            CARD16 - red-max
-            CARD16 - green-max
-            CARD16 - blue-max
-            CARD8 - red-shift
-            CARD8 - green-shift
-            CARD8 - blue-shift
-            3 bytes - padding
-        CARD32 - name-length
-
-        CARD8[length] - name-string
-
-
-
-Client to Server Messages:
-
-5.2.1 SetPixelFormat: 20 bytes
-    CARD8: 0 - message-type
-    ...
-
-5.2.2 FixColourMapEntries: >=6 bytes
-    CARD8: 1 - message-type
-    ...
-
-5.2.3 SetEncodings: >=8 bytes
-    CARD8: 2 - message-type
-    CARD8    - padding
-    CARD16   - numer-of-encodings
-
-    CARD32   - encoding-type in preference order
-        0 - raw
-        1 - copy-rectangle
-        2 - RRE
-        4 - CoRRE
-        5 - hextile
-
-5.2.4 FramebufferUpdateRequest (10 bytes)
-    CARD8: 3 - message-type
-    CARD8    - incremental (0 for full-update, non-zero for incremental)
-    CARD16   - x-position
-    CARD16   - y-position
-    CARD16   - width
-    CARD16   - height
-
-
-5.2.5 KeyEvent: 8 bytes
-    CARD8: 4 - message-type
-    CARD8    - down-flag
-    2 bytes  - padding
-    CARD32   - key (X-Windows keysym values)
-
-5.2.6 PointerEvent: 6 bytes
-    CARD8: 5 - message-type
-    CARD8    - button-mask
-    CARD16   - x-position
-    CARD16   - y-position
-
-5.2.7 ClientCutText: >=9 bytes
-    CARD8: 6 - message-type
-    ...
-
-
-Server to Client Messages:
-
-5.3.1 FramebufferUpdate
-    CARD8: 0 - message-type
-    1 byte   - padding
-    CARD16   - number-of-rectangles
-
-    CARD16   - x-position
-    CARD16   - y-position
-    CARD16   - width
-    CARD16   - height
-    CARD16   - encoding-type:
-        0 - raw
-        1 - copy rectangle
-        2 - RRE
-        4 - CoRRE
-        5 - hextile
-
-        raw:
-            - width x height pixel values
-
-        copy rectangle: 
-            CARD16 - src-x-position
-            CARD16 - src-y-position
-
-        RRE:
-            CARD32  - N number-of-subrectangles
-            Nxd bytes - background-pixel-value (d bits-per-pixel)
-
-        ...
-
-5.3.2 SetColourMapEntries (no support)
-    CARD8: 1 - message-type
-    ...
-
-5.3.3 Bell
-    CARD8: 2 - message-type
-
-5.3.4 ServerCutText
-    CARD8: 3 - message-type
-
-
-
-
-    
diff --git a/systemvm/agent/noVNC/docs/rfbproto-3.3.pdf b/systemvm/agent/noVNC/docs/rfbproto-3.3.pdf
deleted file mode 100644
index 56b8764..0000000
--- a/systemvm/agent/noVNC/docs/rfbproto-3.3.pdf
+++ /dev/null
Binary files differ
diff --git a/systemvm/agent/noVNC/docs/rfbproto-3.7.pdf b/systemvm/agent/noVNC/docs/rfbproto-3.7.pdf
deleted file mode 100644
index 1ef5462..0000000
--- a/systemvm/agent/noVNC/docs/rfbproto-3.7.pdf
+++ /dev/null
Binary files differ
diff --git a/systemvm/agent/noVNC/docs/rfbproto-3.8.pdf b/systemvm/agent/noVNC/docs/rfbproto-3.8.pdf
deleted file mode 100644
index 8f0730f..0000000
--- a/systemvm/agent/noVNC/docs/rfbproto-3.8.pdf
+++ /dev/null
Binary files differ
diff --git a/systemvm/agent/noVNC/karma.conf.js b/systemvm/agent/noVNC/karma.conf.js
deleted file mode 100644
index 5cbd7a5..0000000
--- a/systemvm/agent/noVNC/karma.conf.js
+++ /dev/null
@@ -1,134 +0,0 @@
-// Karma configuration
-
-module.exports = (config) => {
-  const customLaunchers = {};
-  let browsers = [];
-  let useSauce = false;
-
-  // use Sauce when running on Travis
-  if (process.env.TRAVIS_JOB_NUMBER) {
-    useSauce = true;
-  } 
-
-  if (useSauce && process.env.TEST_BROWSER_NAME && process.env.TEST_BROWSER_NAME != 'PhantomJS') {
-    const names = process.env.TEST_BROWSER_NAME.split(',');
-    const platforms = process.env.TEST_BROWSER_OS.split(',');
-    const versions = process.env.TEST_BROWSER_VERSION
-      ? process.env.TEST_BROWSER_VERSION.split(',')
-      : [null];
-
-    for (let i = 0; i < names.length; i++) {
-      for (let j = 0; j < platforms.length; j++) {
-        for (let k = 0; k < versions.length; k++) {
-          let launcher_name = 'sl_' + platforms[j].replace(/[^a-zA-Z0-9]/g, '') + '_' + names[i];
-          if (versions[k]) {
-            launcher_name += '_' + versions[k];
-          }
-
-          customLaunchers[launcher_name] = {
-            base: 'SauceLabs',
-            browserName: names[i],
-            platform: platforms[j],
-          };
-
-          if (versions[i]) {
-            customLaunchers[launcher_name].version = versions[k];
-          }
-        }
-      }
-    }
-
-    browsers = Object.keys(customLaunchers);
-  } else {
-    useSauce = false;
-    //browsers = ['PhantomJS'];
-    browsers = [];
-  }
-
-  const my_conf = {
-
-    // base path that will be used to resolve all patterns (eg. files, exclude)
-    basePath: '',
-
-    // frameworks to use
-    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
-    frameworks: ['mocha', 'sinon-chai'],
-
-    // list of files / patterns to load in the browser (loaded in order)
-    files: [
-      { pattern: 'app/localization.js', included: false },
-      { pattern: 'app/webutil.js', included: false },
-      { pattern: 'core/**/*.js', included: false },
-      { pattern: 'vendor/pako/**/*.js', included: false },
-      { pattern: 'vendor/browser-es-module-loader/dist/*.js*', included: false },
-      { pattern: 'tests/test.*.js', included: false },
-      { pattern: 'tests/fake.*.js', included: false },
-      { pattern: 'tests/assertions.js', included: false },
-      'vendor/promise.js',
-      'tests/karma-test-main.js',
-    ],
-
-    client: {
-      mocha: {
-        // replace Karma debug page with mocha display
-        'reporter': 'html',
-        'ui': 'bdd'
-      }
-    },
-
-    // list of files to exclude
-    exclude: [
-    ],
-
-    customLaunchers: customLaunchers,
-
-    // start these browsers
-    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
-    browsers: browsers,
-
-    // test results reporter to use
-    // possible values: 'dots', 'progress'
-    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
-    reporters: ['mocha'],
-
-
-    // web server port
-    port: 9876,
-
-
-    // enable / disable colors in the output (reporters and logs)
-    colors: true,
-
-
-    // level of logging
-    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
-    logLevel: config.LOG_INFO,
-
-
-    // enable / disable watching file and executing tests whenever any file changes
-    autoWatch: false,
-
-    // Continuous Integration mode
-    // if true, Karma captures browsers, runs the tests and exits
-    singleRun: true,
-
-    // Increase timeout in case connection is slow/we run more browsers than possible
-    // (we currently get 3 for free, and we try to run 7, so it can take a while)
-    captureTimeout: 240000,
-
-    // similarly to above
-    browserNoActivityTimeout: 100000,
-  };
-
-  if (useSauce) {
-    my_conf.reporters.push('saucelabs');
-    my_conf.captureTimeout = 0; // use SL timeout
-    my_conf.sauceLabs = {
-      testName: 'noVNC Tests (all)',
-      startConnect: false,
-      tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER
-    };
-  }
-
-  config.set(my_conf);
-};
diff --git a/systemvm/agent/noVNC/package.json b/systemvm/agent/noVNC/package.json
index 2d84a5f..8fc04e5 100644
--- a/systemvm/agent/noVNC/package.json
+++ b/systemvm/agent/noVNC/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@novnc/novnc",
-  "version": "1.1.0",
+  "version": "1.2.0",
   "description": "An HTML5 VNC client",
   "browser": "lib/rfb",
   "directories": {
@@ -19,7 +19,7 @@
     "vendor/pako"
   ],
   "scripts": {
-    "lint": "eslint app core po tests utils",
+    "lint": "eslint app core po/po2js po/xgettext-html tests utils",
     "test": "karma start karma.conf.js",
     "prepublish": "node ./utils/use_require.js --as commonjs --clean"
   },
@@ -40,36 +40,42 @@
   },
   "homepage": "https://github.com/novnc/noVNC",
   "devDependencies": {
-    "babel-core": "^6.22.1",
-    "babel-plugin-add-module-exports": "^0.2.1",
+    "@babel/core": "*",
+    "@babel/plugin-syntax-dynamic-import": "*",
+    "@babel/plugin-transform-modules-amd": "*",
+    "@babel/plugin-transform-modules-commonjs": "*",
+    "@babel/plugin-transform-modules-systemjs": "*",
+    "@babel/plugin-transform-modules-umd": "*",
+    "@babel/preset-env": "*",
+    "@babel/cli": "*",
     "babel-plugin-import-redirect": "*",
-    "babel-plugin-syntax-dynamic-import": "^6.18.0",
-    "babel-plugin-transform-es2015-modules-amd": "^6.22.0",
-    "babel-plugin-transform-es2015-modules-commonjs": "^6.18.0",
-    "babel-plugin-transform-es2015-modules-systemjs": "^6.22.0",
-    "babel-plugin-transform-es2015-modules-umd": "^6.22.0",
-    "babel-preset-es2015": "^6.24.1",
-    "babelify": "^7.3.0",
-    "browserify": "^13.1.0",
-    "chai": "^3.5.0",
-    "commander": "^2.9.0",
-    "es-module-loader": "^2.1.0",
-    "eslint": "^4.16.0",
-    "fs-extra": "^1.0.0",
+    "browserify": "*",
+    "babelify": "*",
+    "core-js": "*",
+    "chai": "*",
+    "commander": "*",
+    "es-module-loader": "*",
+    "eslint": "*",
+    "fs-extra": "*",
     "jsdom": "*",
-    "karma": "^1.3.0",
-    "karma-mocha": "^1.3.0",
-    "karma-mocha-reporter": "^2.2.0",
-    "karma-sauce-launcher": "^1.0.0",
-    "karma-sinon-chai": "^2.0.0",
-    "mocha": "^3.1.2",
+    "karma": "*",
+    "karma-mocha": "*",
+    "karma-chrome-launcher": "*",
+    "@chiragrupani/karma-chromium-edge-launcher": "*",
+    "karma-firefox-launcher": "*",
+    "karma-ie-launcher": "*",
+    "karma-mocha-reporter": "*",
+    "karma-safari-launcher": "*",
+    "karma-script-launcher": "*",
+    "karma-sinon-chai": "*",
+    "mocha": "*",
     "node-getopt": "*",
     "po2json": "*",
-    "requirejs": "^2.3.2",
-    "rollup": "^0.41.4",
-    "rollup-plugin-node-resolve": "^2.0.0",
-    "sinon": "^4.0.0",
-    "sinon-chai": "^2.8.0"
+    "requirejs": "*",
+    "rollup": "*",
+    "rollup-plugin-node-resolve": "*",
+    "sinon": "*",
+    "sinon-chai": "*"
   },
   "dependencies": {},
   "keywords": [
diff --git a/systemvm/agent/noVNC/po/Makefile b/systemvm/agent/noVNC/po/Makefile
deleted file mode 100644
index 6dbd830..0000000
--- a/systemvm/agent/noVNC/po/Makefile
+++ /dev/null
@@ -1,35 +0,0 @@
-all:
-.PHONY: update-po update-js update-pot
-
-LINGUAS := cs de el es ko nl pl ru sv tr zh_CN zh_TW
-
-VERSION := $(shell grep '"version"' ../package.json | cut -d '"' -f 4)
-
-POFILES := $(addsuffix .po,$(LINGUAS))
-JSONFILES := $(addprefix ../app/locale/,$(addsuffix .json,$(LINGUAS)))
-
-update-po: $(POFILES)
-update-js: $(JSONFILES)
-
-%.po: noVNC.pot
-	msgmerge --update --lang=$* $@ $<
-../app/locale/%.json: %.po
-	./po2js $< $@
-
-update-pot:
-	xgettext --output=noVNC.js.pot \
-		--copyright-holder="The noVNC Authors" \
-		--package-name="noVNC" \
-		--package-version="$(VERSION)" \
-		--msgid-bugs-address="novnc@googlegroups.com" \
-		--add-comments=TRANSLATORS: \
-		--from-code=UTF-8 \
-		--sort-by-file \
-		../app/*.js \
-		../core/*.js \
-		../core/input/*.js
-	./xgettext-html --output=noVNC.html.pot \
-		../vnc.html
-	msgcat --output-file=noVNC.pot \
-		--sort-by-file noVNC.js.pot noVNC.html.pot
-	rm -f noVNC.js.pot noVNC.html.pot
diff --git a/systemvm/agent/noVNC/po/cs.po b/systemvm/agent/noVNC/po/cs.po
deleted file mode 100644
index 2b1efd8..0000000
--- a/systemvm/agent/noVNC/po/cs.po
+++ /dev/null
@@ -1,294 +0,0 @@
-# Czech translations for noVNC package.
-# Copyright (C) 2018 The noVNC Authors
-# This file is distributed under the same license as the noVNC package.
-# Petr <petr@kle.cz>, 2018.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: noVNC 1.0.0-testing.2\n"
-"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
-"POT-Creation-Date: 2018-10-19 12:00+0200\n"
-"PO-Revision-Date: 2018-10-19 12:00+0200\n"
-"Last-Translator: Petr <petr@kle.cz>\n"
-"Language-Team: Czech\n"
-"Language: cs\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n"
-
-#: ../app/ui.js:389
-msgid "Connecting..."
-msgstr "Připojení..."
-
-#: ../app/ui.js:396
-msgid "Disconnecting..."
-msgstr "Odpojení..."
-
-#: ../app/ui.js:402
-msgid "Reconnecting..."
-msgstr "Obnova připojení..."
-
-#: ../app/ui.js:407
-msgid "Internal error"
-msgstr "Vnitřní chyba"
-
-#: ../app/ui.js:997
-msgid "Must set host"
-msgstr "Hostitel musí být nastavení"
-
-#: ../app/ui.js:1079
-msgid "Connected (encrypted) to "
-msgstr "Připojení (šifrované) k "
-
-#: ../app/ui.js:1081
-msgid "Connected (unencrypted) to "
-msgstr "Připojení (nešifrované) k "
-
-#: ../app/ui.js:1104
-msgid "Something went wrong, connection is closed"
-msgstr "Něco se pokazilo, odpojeno"
-
-#: ../app/ui.js:1107
-msgid "Failed to connect to server"
-msgstr "Chyba připojení k serveru"
-
-#: ../app/ui.js:1117
-msgid "Disconnected"
-msgstr "Odpojeno"
-
-#: ../app/ui.js:1130
-msgid "New connection has been rejected with reason: "
-msgstr "Nové připojení bylo odmítnuto s odůvodněním: "
-
-#: ../app/ui.js:1133
-msgid "New connection has been rejected"
-msgstr "Nové připojení bylo odmítnuto"
-
-#: ../app/ui.js:1153
-msgid "Password is required"
-msgstr "Je vyžadováno heslo"
-
-#: ../vnc.html:84
-msgid "noVNC encountered an error:"
-msgstr "noVNC narazilo na chybu:"
-
-#: ../vnc.html:94
-msgid "Hide/Show the control bar"
-msgstr "Skrýt/zobrazit ovládací panel"
-
-#: ../vnc.html:101
-msgid "Move/Drag Viewport"
-msgstr "Přesunout/přetáhnout výřez"
-
-#: ../vnc.html:101
-msgid "viewport drag"
-msgstr "přesun výřezu"
-
-#: ../vnc.html:107 ../vnc.html:110 ../vnc.html:113 ../vnc.html:116
-msgid "Active Mouse Button"
-msgstr "Aktivní tlačítka myši"
-
-#: ../vnc.html:107
-msgid "No mousebutton"
-msgstr "Žádné"
-
-#: ../vnc.html:110
-msgid "Left mousebutton"
-msgstr "Levé tlačítko myši"
-
-#: ../vnc.html:113
-msgid "Middle mousebutton"
-msgstr "Prostřední tlačítko myši"
-
-#: ../vnc.html:116
-msgid "Right mousebutton"
-msgstr "Pravé tlačítko myši"
-
-#: ../vnc.html:119
-msgid "Keyboard"
-msgstr "Klávesnice"
-
-#: ../vnc.html:119
-msgid "Show Keyboard"
-msgstr "Zobrazit klávesnici"
-
-#: ../vnc.html:126
-msgid "Extra keys"
-msgstr "Extra klávesy"
-
-#: ../vnc.html:126
-msgid "Show Extra Keys"
-msgstr "Zobrazit extra klávesy"
-
-#: ../vnc.html:131
-msgid "Ctrl"
-msgstr "Ctrl"
-
-#: ../vnc.html:131
-msgid "Toggle Ctrl"
-msgstr "Přepnout Ctrl"
-
-#: ../vnc.html:134
-msgid "Alt"
-msgstr "Alt"
-
-#: ../vnc.html:134
-msgid "Toggle Alt"
-msgstr "Přepnout Alt"
-
-#: ../vnc.html:137
-msgid "Send Tab"
-msgstr "Odeslat tabulátor"
-
-#: ../vnc.html:137
-msgid "Tab"
-msgstr "Tab"
-
-#: ../vnc.html:140
-msgid "Esc"
-msgstr "Esc"
-
-#: ../vnc.html:140
-msgid "Send Escape"
-msgstr "Odeslat Esc"
-
-#: ../vnc.html:143
-msgid "Ctrl+Alt+Del"
-msgstr "Ctrl+Alt+Del"
-
-#: ../vnc.html:143
-msgid "Send Ctrl-Alt-Del"
-msgstr "Poslat Ctrl-Alt-Del"
-
-#: ../vnc.html:151
-msgid "Shutdown/Reboot"
-msgstr "Vypnutí/Restart"
-
-#: ../vnc.html:151
-msgid "Shutdown/Reboot..."
-msgstr "Vypnutí/Restart..."
-
-#: ../vnc.html:157
-msgid "Power"
-msgstr "Napájení"
-
-#: ../vnc.html:159
-msgid "Shutdown"
-msgstr "Vypnout"
-
-#: ../vnc.html:160
-msgid "Reboot"
-msgstr "Restart"
-
-#: ../vnc.html:161
-msgid "Reset"
-msgstr "Reset"
-
-#: ../vnc.html:166 ../vnc.html:172
-msgid "Clipboard"
-msgstr "Schránka"
-
-#: ../vnc.html:176
-msgid "Clear"
-msgstr "Vymazat"
-
-#: ../vnc.html:182
-msgid "Fullscreen"
-msgstr "Celá obrazovka"
-
-#: ../vnc.html:187 ../vnc.html:194
-msgid "Settings"
-msgstr "Nastavení"
-
-#: ../vnc.html:197
-msgid "Shared Mode"
-msgstr "Sdílený režim"
-
-#: ../vnc.html:200
-msgid "View Only"
-msgstr "Pouze prohlížení"
-
-#: ../vnc.html:204
-msgid "Clip to Window"
-msgstr "Přizpůsobit oknu"
-
-#: ../vnc.html:207
-msgid "Scaling Mode:"
-msgstr "Přizpůsobení velikosti"
-
-#: ../vnc.html:209
-msgid "None"
-msgstr "Žádné"
-
-#: ../vnc.html:210
-msgid "Local Scaling"
-msgstr "Místní"
-
-#: ../vnc.html:211
-msgid "Remote Resizing"
-msgstr "Vzdálené"
-
-#: ../vnc.html:216
-msgid "Advanced"
-msgstr "Pokročilé"
-
-#: ../vnc.html:219
-msgid "Repeater ID:"
-msgstr "ID opakovače"
-
-#: ../vnc.html:223
-msgid "WebSocket"
-msgstr "WebSocket"
-
-#: ../vnc.html:226
-msgid "Encrypt"
-msgstr "Šifrování:"
-
-#: ../vnc.html:229
-msgid "Host:"
-msgstr "Hostitel:"
-
-#: ../vnc.html:233
-msgid "Port:"
-msgstr "Port:"
-
-#: ../vnc.html:237
-msgid "Path:"
-msgstr "Cesta"
-
-#: ../vnc.html:244
-msgid "Automatic Reconnect"
-msgstr "Automatická obnova připojení"
-
-#: ../vnc.html:247
-msgid "Reconnect Delay (ms):"
-msgstr "Zpoždění připojení (ms)"
-
-#: ../vnc.html:252
-msgid "Show Dot when No Cursor"
-msgstr "Tečka místo chybějícího kurzoru myši"
-
-#: ../vnc.html:257
-msgid "Logging:"
-msgstr "Logování:"
-
-#: ../vnc.html:269
-msgid "Disconnect"
-msgstr "Odpojit"
-
-#: ../vnc.html:288
-msgid "Connect"
-msgstr "Připojit"
-
-#: ../vnc.html:298
-msgid "Password:"
-msgstr "Heslo"
-
-#: ../vnc.html:302
-msgid "Send Password"
-msgstr "Odeslat heslo"
-
-#: ../vnc.html:312
-msgid "Cancel"
-msgstr "Zrušit"
diff --git a/systemvm/agent/noVNC/po/de.po b/systemvm/agent/noVNC/po/de.po
deleted file mode 100644
index 0c3fa0d..0000000
--- a/systemvm/agent/noVNC/po/de.po
+++ /dev/null
@@ -1,303 +0,0 @@
-# German translations for noVNC package
-# German translation for noVNC.
-# Copyright (C) 2018 The noVNC Authors
-# This file is distributed under the same license as the noVNC package.
-# Loek Janssen <loekjanssen@gmail.com>, 2016.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: noVNC 0.6.1\n"
-"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
-"POT-Creation-Date: 2017-11-24 07:16+0000\n"
-"PO-Revision-Date: 2017-11-24 08:20+0100\n"
-"Last-Translator: Dominik Csapak <d.csapak@proxmox.com>\n"
-"Language-Team: none\n"
-"Language: de\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Poedit 1.8.11\n"
-
-#: ../app/ui.js:404
-msgid "Connecting..."
-msgstr "Verbinden..."
-
-#: ../app/ui.js:411
-msgid "Disconnecting..."
-msgstr "Verbindung trennen..."
-
-#: ../app/ui.js:417
-msgid "Reconnecting..."
-msgstr "Verbindung wiederherstellen..."
-
-#: ../app/ui.js:422
-msgid "Internal error"
-msgstr "Interner Fehler"
-
-#: ../app/ui.js:1019
-msgid "Must set host"
-msgstr "Richten Sie den Server ein"
-
-#: ../app/ui.js:1099
-msgid "Connected (encrypted) to "
-msgstr "Verbunden mit (verschlüsselt) "
-
-#: ../app/ui.js:1101
-msgid "Connected (unencrypted) to "
-msgstr "Verbunden mit (unverschlüsselt) "
-
-#: ../app/ui.js:1119
-msgid "Something went wrong, connection is closed"
-msgstr "Etwas lief schief, Verbindung wurde getrennt"
-
-#: ../app/ui.js:1129
-msgid "Disconnected"
-msgstr "Verbindung zum Server getrennt"
-
-#: ../app/ui.js:1142
-msgid "New connection has been rejected with reason: "
-msgstr "Verbindung wurde aus folgendem Grund abgelehnt: "
-
-#: ../app/ui.js:1145
-msgid "New connection has been rejected"
-msgstr "Verbindung wurde abgelehnt"
-
-#: ../app/ui.js:1166
-msgid "Password is required"
-msgstr "Passwort ist erforderlich"
-
-#: ../vnc.html:89
-msgid "noVNC encountered an error:"
-msgstr "Ein Fehler ist aufgetreten:"
-
-#: ../vnc.html:99
-msgid "Hide/Show the control bar"
-msgstr "Kontrollleiste verstecken/anzeigen"
-
-#: ../vnc.html:106
-msgid "Move/Drag Viewport"
-msgstr "Ansichtsfenster verschieben/ziehen"
-
-#: ../vnc.html:106
-msgid "viewport drag"
-msgstr "Ansichtsfenster ziehen"
-
-#: ../vnc.html:112 ../vnc.html:115 ../vnc.html:118 ../vnc.html:121
-msgid "Active Mouse Button"
-msgstr "Aktive Maustaste"
-
-#: ../vnc.html:112
-msgid "No mousebutton"
-msgstr "Keine Maustaste"
-
-#: ../vnc.html:115
-msgid "Left mousebutton"
-msgstr "Linke Maustaste"
-
-#: ../vnc.html:118
-msgid "Middle mousebutton"
-msgstr "Mittlere Maustaste"
-
-#: ../vnc.html:121
-msgid "Right mousebutton"
-msgstr "Rechte Maustaste"
-
-#: ../vnc.html:124
-msgid "Keyboard"
-msgstr "Tastatur"
-
-#: ../vnc.html:124
-msgid "Show Keyboard"
-msgstr "Tastatur anzeigen"
-
-#: ../vnc.html:131
-msgid "Extra keys"
-msgstr "Zusatztasten"
-
-#: ../vnc.html:131
-msgid "Show Extra Keys"
-msgstr "Zusatztasten anzeigen"
-
-#: ../vnc.html:136
-msgid "Ctrl"
-msgstr "Strg"
-
-#: ../vnc.html:136
-msgid "Toggle Ctrl"
-msgstr "Strg umschalten"
-
-#: ../vnc.html:139
-msgid "Alt"
-msgstr "Alt"
-
-#: ../vnc.html:139
-msgid "Toggle Alt"
-msgstr "Alt umschalten"
-
-#: ../vnc.html:142
-msgid "Send Tab"
-msgstr "Tab senden"
-
-#: ../vnc.html:142
-msgid "Tab"
-msgstr "Tab"
-
-#: ../vnc.html:145
-msgid "Esc"
-msgstr "Esc"
-
-#: ../vnc.html:145
-msgid "Send Escape"
-msgstr "Escape senden"
-
-#: ../vnc.html:148
-msgid "Ctrl+Alt+Del"
-msgstr "Strg+Alt+Entf"
-
-#: ../vnc.html:148
-msgid "Send Ctrl-Alt-Del"
-msgstr "Strg+Alt+Entf senden"
-
-#: ../vnc.html:156
-msgid "Shutdown/Reboot"
-msgstr "Herunterfahren/Neustarten"
-
-#: ../vnc.html:156
-msgid "Shutdown/Reboot..."
-msgstr "Herunterfahren/Neustarten..."
-
-#: ../vnc.html:162
-msgid "Power"
-msgstr "Energie"
-
-#: ../vnc.html:164
-msgid "Shutdown"
-msgstr "Herunterfahren"
-
-#: ../vnc.html:165
-msgid "Reboot"
-msgstr "Neustarten"
-
-#: ../vnc.html:166
-msgid "Reset"
-msgstr "Zurücksetzen"
-
-#: ../vnc.html:171 ../vnc.html:177
-msgid "Clipboard"
-msgstr "Zwischenablage"
-
-#: ../vnc.html:181
-msgid "Clear"
-msgstr "Löschen"
-
-#: ../vnc.html:187
-msgid "Fullscreen"
-msgstr "Vollbild"
-
-#: ../vnc.html:192 ../vnc.html:199
-msgid "Settings"
-msgstr "Einstellungen"
-
-#: ../vnc.html:202
-msgid "Shared Mode"
-msgstr "Geteilter Modus"
-
-#: ../vnc.html:205
-msgid "View Only"
-msgstr "Nur betrachten"
-
-#: ../vnc.html:209
-msgid "Clip to Window"
-msgstr "Auf Fenster begrenzen"
-
-#: ../vnc.html:212
-msgid "Scaling Mode:"
-msgstr "Skalierungsmodus:"
-
-#: ../vnc.html:214
-msgid "None"
-msgstr "Keiner"
-
-#: ../vnc.html:215
-msgid "Local Scaling"
-msgstr "Lokales skalieren"
-
-#: ../vnc.html:216
-msgid "Remote Resizing"
-msgstr "Serverseitiges skalieren"
-
-#: ../vnc.html:221
-msgid "Advanced"
-msgstr "Erweitert"
-
-#: ../vnc.html:224
-msgid "Repeater ID:"
-msgstr "Repeater ID:"
-
-#: ../vnc.html:228
-msgid "WebSocket"
-msgstr "WebSocket"
-
-#: ../vnc.html:231
-msgid "Encrypt"
-msgstr "Verschlüsselt"
-
-#: ../vnc.html:234
-msgid "Host:"
-msgstr "Server:"
-
-#: ../vnc.html:238
-msgid "Port:"
-msgstr "Port:"
-
-#: ../vnc.html:242
-msgid "Path:"
-msgstr "Pfad:"
-
-#: ../vnc.html:249
-msgid "Automatic Reconnect"
-msgstr "Automatisch wiederverbinden"
-
-#: ../vnc.html:252
-msgid "Reconnect Delay (ms):"
-msgstr "Wiederverbindungsverzögerung (ms):"
-
-#: ../vnc.html:258
-msgid "Logging:"
-msgstr "Protokollierung:"
-
-#: ../vnc.html:270
-msgid "Disconnect"
-msgstr "Verbindung trennen"
-
-#: ../vnc.html:289
-msgid "Connect"
-msgstr "Verbinden"
-
-#: ../vnc.html:299
-msgid "Password:"
-msgstr "Passwort:"
-
-#: ../vnc.html:313
-msgid "Cancel"
-msgstr "Abbrechen"
-
-#: ../vnc.html:329
-msgid "Canvas not supported."
-msgstr "Canvas nicht unterstützt."
-
-#~ msgid "Disconnect timeout"
-#~ msgstr "Zeitüberschreitung beim Trennen"
-
-#~ msgid "Local Downscaling"
-#~ msgstr "Lokales herunterskalieren"
-
-#~ msgid "Local Cursor"
-#~ msgstr "Lokaler Mauszeiger"
-
-#~ msgid "Forcing clipping mode since scrollbars aren't supported by IE in fullscreen"
-#~ msgstr "'Clipping-Modus' aktiviert, Scrollbalken in 'IE-Vollbildmodus' werden nicht unterstützt"
-
-#~ msgid "True Color"
-#~ msgstr "True Color"
diff --git a/systemvm/agent/noVNC/po/el.po b/systemvm/agent/noVNC/po/el.po
deleted file mode 100644
index 5213ae5..0000000
--- a/systemvm/agent/noVNC/po/el.po
+++ /dev/null
@@ -1,323 +0,0 @@
-# Greek translations for noVNC package.
-# Copyright (C) 2018 The noVNC Authors
-# This file is distributed under the same license as the noVNC package.
-# Giannis Kosmas <kosmasgiannis@gmail.com>, 2016.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: noVNC 0.6.1\n"
-"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
-"POT-Creation-Date: 2017-11-17 21:40+0200\n"
-"PO-Revision-Date: 2017-10-11 16:16+0200\n"
-"Last-Translator: Giannis Kosmas <kosmasgiannis@gmail.com>\n"
-"Language-Team: none\n"
-"Language: el\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#: ../app/ui.js:404
-msgid "Connecting..."
-msgstr "Συνδέεται..."
-
-#: ../app/ui.js:411
-msgid "Disconnecting..."
-msgstr "Aποσυνδέεται..."
-
-#: ../app/ui.js:417
-msgid "Reconnecting..."
-msgstr "Επανασυνδέεται..."
-
-#: ../app/ui.js:422
-msgid "Internal error"
-msgstr "Εσωτερικό σφάλμα"
-
-#: ../app/ui.js:1019
-msgid "Must set host"
-msgstr "Πρέπει να οριστεί ο διακομιστής"
-
-#: ../app/ui.js:1099
-msgid "Connected (encrypted) to "
-msgstr "Συνδέθηκε (κρυπτογραφημένα) με το "
-
-#: ../app/ui.js:1101
-msgid "Connected (unencrypted) to "
-msgstr "Συνδέθηκε (μη κρυπτογραφημένα) με το "
-
-#: ../app/ui.js:1119
-msgid "Something went wrong, connection is closed"
-msgstr "Κάτι πήγε στραβά, η σύνδεση διακόπηκε"
-
-#: ../app/ui.js:1129
-msgid "Disconnected"
-msgstr "Αποσυνδέθηκε"
-
-#: ../app/ui.js:1142
-msgid "New connection has been rejected with reason: "
-msgstr "Η νέα σύνδεση απορρίφθηκε διότι: "
-
-#: ../app/ui.js:1145
-msgid "New connection has been rejected"
-msgstr "Η νέα σύνδεση απορρίφθηκε "
-
-#: ../app/ui.js:1166
-msgid "Password is required"
-msgstr "Απαιτείται ο κωδικός πρόσβασης"
-
-#: ../vnc.html:89
-msgid "noVNC encountered an error:"
-msgstr "το noVNC αντιμετώπισε ένα σφάλμα:"
-
-#: ../vnc.html:99
-msgid "Hide/Show the control bar"
-msgstr "Απόκρυψη/Εμφάνιση γραμμής ελέγχου"
-
-#: ../vnc.html:106
-msgid "Move/Drag Viewport"
-msgstr "Μετακίνηση/Σύρσιμο Θεατού πεδίου"
-
-#: ../vnc.html:106
-msgid "viewport drag"
-msgstr "σύρσιμο θεατού πεδίου"
-
-#: ../vnc.html:112 ../vnc.html:115 ../vnc.html:118 ../vnc.html:121
-msgid "Active Mouse Button"
-msgstr "Ενεργό Πλήκτρο Ποντικιού"
-
-#: ../vnc.html:112
-msgid "No mousebutton"
-msgstr "Χωρίς Πλήκτρο Ποντικιού"
-
-#: ../vnc.html:115
-msgid "Left mousebutton"
-msgstr "Αριστερό Πλήκτρο Ποντικιού"
-
-#: ../vnc.html:118
-msgid "Middle mousebutton"
-msgstr "Μεσαίο Πλήκτρο Ποντικιού"
-
-#: ../vnc.html:121
-msgid "Right mousebutton"
-msgstr "Δεξί Πλήκτρο Ποντικιού"
-
-#: ../vnc.html:124
-msgid "Keyboard"
-msgstr "Πληκτρολόγιο"
-
-#: ../vnc.html:124
-msgid "Show Keyboard"
-msgstr "Εμφάνιση Πληκτρολογίου"
-
-#: ../vnc.html:131
-msgid "Extra keys"
-msgstr "Επιπλέον πλήκτρα"
-
-#: ../vnc.html:131
-msgid "Show Extra Keys"
-msgstr "Εμφάνιση Επιπλέον Πλήκτρων"
-
-#: ../vnc.html:136
-msgid "Ctrl"
-msgstr "Ctrl"
-
-#: ../vnc.html:136
-msgid "Toggle Ctrl"
-msgstr "Εναλλαγή Ctrl"
-
-#: ../vnc.html:139
-msgid "Alt"
-msgstr "Alt"
-
-#: ../vnc.html:139
-msgid "Toggle Alt"
-msgstr "Εναλλαγή Alt"
-
-#: ../vnc.html:142
-msgid "Send Tab"
-msgstr "Αποστολή Tab"
-
-#: ../vnc.html:142
-msgid "Tab"
-msgstr "Tab"
-
-#: ../vnc.html:145
-msgid "Esc"
-msgstr "Esc"
-
-#: ../vnc.html:145
-msgid "Send Escape"
-msgstr "Αποστολή Escape"
-
-#: ../vnc.html:148
-msgid "Ctrl+Alt+Del"
-msgstr "Ctrl+Alt+Del"
-
-#: ../vnc.html:148
-msgid "Send Ctrl-Alt-Del"
-msgstr "Αποστολή Ctrl-Alt-Del"
-
-#: ../vnc.html:156
-msgid "Shutdown/Reboot"
-msgstr "Κλείσιμο/Επανεκκίνηση"
-
-#: ../vnc.html:156
-msgid "Shutdown/Reboot..."
-msgstr "Κλείσιμο/Επανεκκίνηση..."
-
-#: ../vnc.html:162
-msgid "Power"
-msgstr "Απενεργοποίηση"
-
-#: ../vnc.html:164
-msgid "Shutdown"
-msgstr "Κλείσιμο"
-
-#: ../vnc.html:165
-msgid "Reboot"
-msgstr "Επανεκκίνηση"
-
-#: ../vnc.html:166
-msgid "Reset"
-msgstr "Επαναφορά"
-
-#: ../vnc.html:171 ../vnc.html:177
-msgid "Clipboard"
-msgstr "Πρόχειρο"
-
-#: ../vnc.html:181
-msgid "Clear"
-msgstr "Καθάρισμα"
-
-#: ../vnc.html:187
-msgid "Fullscreen"
-msgstr "Πλήρης Οθόνη"
-
-#: ../vnc.html:192 ../vnc.html:199
-msgid "Settings"
-msgstr "Ρυθμίσεις"
-
-#: ../vnc.html:202
-msgid "Shared Mode"
-msgstr "Κοινόχρηστη Λειτουργία"
-
-#: ../vnc.html:205
-msgid "View Only"
-msgstr "Μόνο Θέαση"
-
-#: ../vnc.html:209
-msgid "Clip to Window"
-msgstr "Αποκοπή στο όριο του Παράθυρου"
-
-#: ../vnc.html:212
-msgid "Scaling Mode:"
-msgstr "Λειτουργία Κλιμάκωσης:"
-
-#: ../vnc.html:214
-msgid "None"
-msgstr "Καμία"
-
-#: ../vnc.html:215
-msgid "Local Scaling"
-msgstr "Τοπική Κλιμάκωση"
-
-#: ../vnc.html:216
-msgid "Remote Resizing"
-msgstr "Απομακρυσμένη Αλλαγή μεγέθους"
-
-#: ../vnc.html:221
-msgid "Advanced"
-msgstr "Για προχωρημένους"
-
-#: ../vnc.html:224
-msgid "Repeater ID:"
-msgstr "Repeater ID:"
-
-#: ../vnc.html:228
-msgid "WebSocket"
-msgstr "WebSocket"
-
-#: ../vnc.html:231
-msgid "Encrypt"
-msgstr "Κρυπτογράφηση"
-
-#: ../vnc.html:234
-msgid "Host:"
-msgstr "Όνομα διακομιστή:"
-
-#: ../vnc.html:238
-msgid "Port:"
-msgstr "Πόρτα διακομιστή:"
-
-#: ../vnc.html:242
-msgid "Path:"
-msgstr "Διαδρομή:"
-
-#: ../vnc.html:249
-msgid "Automatic Reconnect"
-msgstr "Αυτόματη επανασύνδεση"
-
-#: ../vnc.html:252
-msgid "Reconnect Delay (ms):"
-msgstr "Καθυστέρηση επανασύνδεσης (ms):"
-
-#: ../vnc.html:258
-msgid "Logging:"
-msgstr "Καταγραφή:"
-
-#: ../vnc.html:270
-msgid "Disconnect"
-msgstr "Αποσύνδεση"
-
-#: ../vnc.html:289
-msgid "Connect"
-msgstr "Σύνδεση"
-
-#: ../vnc.html:299
-msgid "Password:"
-msgstr "Κωδικός Πρόσβασης:"
-
-#: ../vnc.html:313
-msgid "Cancel"
-msgstr "Ακύρωση"
-
-#: ../vnc.html:329
-msgid "Canvas not supported."
-msgstr "Δεν υποστηρίζεται το στοιχείο Canvas"
-
-#~ msgid "Disconnect timeout"
-#~ msgstr "Παρέλευση χρονικού ορίου αποσύνδεσης"
-
-#~ msgid "Local Downscaling"
-#~ msgstr "Τοπική Συρρίκνωση"
-
-#~ msgid "Local Cursor"
-#~ msgstr "Τοπικός Δρομέας"
-
-#~ msgid ""
-#~ "Forcing clipping mode since scrollbars aren't supported by IE in "
-#~ "fullscreen"
-#~ msgstr ""
-#~ "Εφαρμογή λειτουργίας αποκοπής αφού δεν υποστηρίζονται οι λωρίδες κύλισης "
-#~ "σε πλήρη οθόνη στον IE"
-
-#~ msgid "True Color"
-#~ msgstr "Πραγματικά Χρώματα"
-
-#~ msgid "Style:"
-#~ msgstr "Στυλ:"
-
-#~ msgid "default"
-#~ msgstr "προεπιλεγμένο"
-
-#~ msgid "Apply"
-#~ msgstr "Εφαρμογή"
-
-#~ msgid "Connection"
-#~ msgstr "Σύνδεση"
-
-#~ msgid "Token:"
-#~ msgstr "Διακριτικό:"
-
-#~ msgid "Send Password"
-#~ msgstr "Αποστολή Κωδικού Πρόσβασης"
diff --git a/systemvm/agent/noVNC/po/es.po b/systemvm/agent/noVNC/po/es.po
deleted file mode 100644
index e15655f..0000000
--- a/systemvm/agent/noVNC/po/es.po
+++ /dev/null
@@ -1,283 +0,0 @@
-# Spanish translations for noVNC package
-# Traducciones al español para el paquete noVNC.
-# Copyright (C) 2018 The noVNC Authors
-# This file is distributed under the same license as the noVNC package.
-# Juanjo Diaz <juanjo.diazmo@gmail.com>, 2018.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: noVNC 1.0.0-testing.2\n"
-"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
-"POT-Creation-Date: 2017-10-06 10:07+0200\n"
-"PO-Revision-Date: 2018-01-30 19:14-0800\n"
-"Last-Translator: Juanjo Diaz <juanjo.diazmo@gmail.com>\n"
-"Language-Team: Spanish\n"
-"Language: es\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#: ../app/ui.js:430
-msgid "Connecting..."
-msgstr "Conectando..."
-
-#: ../app/ui.js:438
-msgid "Connected (encrypted) to "
-msgstr "Conectado (con encriptación) a"
-
-#: ../app/ui.js:440
-msgid "Connected (unencrypted) to "
-msgstr "Conectado (sin encriptación) a"
-
-#: ../app/ui.js:446
-msgid "Disconnecting..."
-msgstr "Desconectando..."
-
-#: ../app/ui.js:450
-msgid "Disconnected"
-msgstr "Desconectado"
-
-#: ../app/ui.js:1052 ../core/rfb.js:248
-msgid "Must set host"
-msgstr "Debes configurar el host"
-
-#: ../app/ui.js:1101
-msgid "Reconnecting..."
-msgstr "Reconectando..."
-
-#: ../app/ui.js:1140
-msgid "Password is required"
-msgstr "Contraseña es obligatoria"
-
-#: ../core/rfb.js:548
-msgid "Disconnect timeout"
-msgstr "Tiempo de desconexión agotado"
-
-#: ../vnc.html:89
-msgid "noVNC encountered an error:"
-msgstr "noVNC ha encontrado un error:"
-
-#: ../vnc.html:99
-msgid "Hide/Show the control bar"
-msgstr "Ocultar/Mostrar la barra de control"
-
-#: ../vnc.html:106
-msgid "Move/Drag Viewport"
-msgstr "Mover/Arrastrar la ventana"
-
-#: ../vnc.html:106
-msgid "viewport drag"
-msgstr "Arrastrar la ventana"
-
-#: ../vnc.html:112 ../vnc.html:115 ../vnc.html:118 ../vnc.html:121
-msgid "Active Mouse Button"
-msgstr "Botón activo del ratón"
-
-#: ../vnc.html:112
-msgid "No mousebutton"
-msgstr "Ningún botón del ratón"
-
-#: ../vnc.html:115
-msgid "Left mousebutton"
-msgstr "Botón izquierdo del ratón"
-
-#: ../vnc.html:118
-msgid "Middle mousebutton"
-msgstr "Botón central del ratón"
-
-#: ../vnc.html:121
-msgid "Right mousebutton"
-msgstr "Botón derecho del ratón"
-
-#: ../vnc.html:124
-msgid "Keyboard"
-msgstr "Teclado"
-
-#: ../vnc.html:124
-msgid "Show Keyboard"
-msgstr "Mostrar teclado"
-
-#: ../vnc.html:131
-msgid "Extra keys"
-msgstr "Teclas adicionales"
-
-#: ../vnc.html:131
-msgid "Show Extra Keys"
-msgstr "Mostrar Teclas Adicionales"
-
-#: ../vnc.html:136
-msgid "Ctrl"
-msgstr "Ctrl"
-
-#: ../vnc.html:136
-msgid "Toggle Ctrl"
-msgstr "Pulsar/Soltar Ctrl"
-
-#: ../vnc.html:139
-msgid "Alt"
-msgstr "Alt"
-
-#: ../vnc.html:139
-msgid "Toggle Alt"
-msgstr "Pulsar/Soltar Alt"
-
-#: ../vnc.html:142
-msgid "Send Tab"
-msgstr "Enviar Tabulación"
-
-#: ../vnc.html:142
-msgid "Tab"
-msgstr "Tabulación"
-
-#: ../vnc.html:145
-msgid "Esc"
-msgstr "Esc"
-
-#: ../vnc.html:145
-msgid "Send Escape"
-msgstr "Enviar Escape"
-
-#: ../vnc.html:148
-msgid "Ctrl+Alt+Del"
-msgstr "Ctrl+Alt+Del"
-
-#: ../vnc.html:148
-msgid "Send Ctrl-Alt-Del"
-msgstr "Enviar Ctrl+Alt+Del"
-
-#: ../vnc.html:156
-msgid "Shutdown/Reboot"
-msgstr "Apagar/Reiniciar"
-
-#: ../vnc.html:156
-msgid "Shutdown/Reboot..."
-msgstr "Apagar/Reiniciar..."
-
-#: ../vnc.html:162
-msgid "Power"
-msgstr "Encender"
-
-#: ../vnc.html:164
-msgid "Shutdown"
-msgstr "Apagar"
-
-#: ../vnc.html:165
-msgid "Reboot"
-msgstr "Reiniciar"
-
-#: ../vnc.html:166
-msgid "Reset"
-msgstr "Restablecer"
-
-#: ../vnc.html:171 ../vnc.html:177
-msgid "Clipboard"
-msgstr "Portapapeles"
-
-#: ../vnc.html:181
-msgid "Clear"
-msgstr "Vaciar"
-
-#: ../vnc.html:187
-msgid "Fullscreen"
-msgstr "Pantalla Completa"
-
-#: ../vnc.html:192 ../vnc.html:199
-msgid "Settings"
-msgstr "Configuraciones"
-
-#: ../vnc.html:202
-msgid "Shared Mode"
-msgstr "Modo Compartido"
-
-#: ../vnc.html:205
-msgid "View Only"
-msgstr "Solo visualización"
-
-#: ../vnc.html:209
-msgid "Clip to Window"
-msgstr "Recortar al tamaño de la ventana"
-
-#: ../vnc.html:212
-msgid "Scaling Mode:"
-msgstr "Modo de escalado:"
-
-#: ../vnc.html:214
-msgid "None"
-msgstr "Ninguno"
-
-#: ../vnc.html:215
-msgid "Local Scaling"
-msgstr "Escalado Local"
-
-#: ../vnc.html:216
-msgid "Local Downscaling"
-msgstr "Reducción de escala local"
-
-#: ../vnc.html:217
-msgid "Remote Resizing"
-msgstr "Cambio de tamaño remoto"
-
-#: ../vnc.html:222
-msgid "Advanced"
-msgstr "Avanzado"
-
-#: ../vnc.html:225
-msgid "Local Cursor"
-msgstr "Cursor Local"
-
-#: ../vnc.html:229
-msgid "Repeater ID:"
-msgstr "ID del Repetidor"
-
-#: ../vnc.html:233
-msgid "WebSocket"
-msgstr "WebSocket"
-
-#: ../vnc.html:236
-msgid "Encrypt"
-msgstr ""
-
-#: ../vnc.html:239
-msgid "Host:"
-msgstr "Host"
-
-#: ../vnc.html:243
-msgid "Port:"
-msgstr "Puesto"
-
-#: ../vnc.html:247
-msgid "Path:"
-msgstr "Ruta"
-
-#: ../vnc.html:254
-msgid "Automatic Reconnect"
-msgstr "Reconexión automática"
-
-#: ../vnc.html:257
-msgid "Reconnect Delay (ms):"
-msgstr "Retraso en la reconexión (ms)"
-
-#: ../vnc.html:263
-msgid "Logging:"
-msgstr "Logging"
-
-#: ../vnc.html:275
-msgid "Disconnect"
-msgstr "Desconectar"
-
-#: ../vnc.html:294
-msgid "Connect"
-msgstr "Conectar"
-
-#: ../vnc.html:304
-msgid "Password:"
-msgstr "Contraseña"
-
-#: ../vnc.html:318
-msgid "Cancel"
-msgstr "Cancelar"
-
-#: ../vnc.html:334
-msgid "Canvas not supported."
-msgstr "Canvas no está soportado"
diff --git a/systemvm/agent/noVNC/po/ko.po b/systemvm/agent/noVNC/po/ko.po
deleted file mode 100644
index 87ae106..0000000
--- a/systemvm/agent/noVNC/po/ko.po
+++ /dev/null
@@ -1,290 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) 2018 The noVNC Authors
-# This file is distributed under the same license as the noVNC package.
-# Baw Appie <pp121324@gmail.com>, 2018.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: noVNC 1.0.0-testing.2\n"
-"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
-"POT-Creation-Date: 2018-01-31 16:29+0100\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: Baw Appie <pp121324@gmail.com>\n"
-"Language-Team: Korean\n"
-"Language: ko\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: ../app/ui.js:395
-msgid "Connecting..."
-msgstr "연결중..."
-
-#: ../app/ui.js:402
-msgid "Disconnecting..."
-msgstr "연결 해제중..."
-
-#: ../app/ui.js:408
-msgid "Reconnecting..."
-msgstr "재연결중..."
-
-#: ../app/ui.js:413
-msgid "Internal error"
-msgstr "내부 오류"
-
-#: ../app/ui.js:1002
-msgid "Must set host"
-msgstr "호스트는 설정되어야 합니다."
-
-#: ../app/ui.js:1083
-msgid "Connected (encrypted) to "
-msgstr "다음과 (암호화되어) 연결되었습니다:"
-
-#: ../app/ui.js:1085
-msgid "Connected (unencrypted) to "
-msgstr "다음과 (암호화 없이) 연결되었습니다:"
-
-#: ../app/ui.js:1108
-msgid "Something went wrong, connection is closed"
-msgstr "무언가 잘못되었습니다, 연결이 닫혔습니다."
-
-#: ../app/ui.js:1111
-msgid "Failed to connect to server"
-msgstr "서버에 연결하지 못했습니다."
-
-#: ../app/ui.js:1121
-msgid "Disconnected"
-msgstr "연결이 해제되었습니다."
-
-#: ../app/ui.js:1134
-msgid "New connection has been rejected with reason: "
-msgstr "새 연결이 다음 이유로 거부되었습니다:"
-
-#: ../app/ui.js:1137
-msgid "New connection has been rejected"
-msgstr "새 연결이 거부되었습니다."
-
-#: ../app/ui.js:1158
-msgid "Password is required"
-msgstr "비밀번호가 필요합니다."
-
-#: ../vnc.html:91
-msgid "noVNC encountered an error:"
-msgstr "noVNC에 오류가 발생했습니다:"
-
-#: ../vnc.html:101
-msgid "Hide/Show the control bar"
-msgstr "컨트롤 바 숨기기/보이기"
-
-#: ../vnc.html:108
-msgid "Move/Drag Viewport"
-msgstr "움직이기/드래그 뷰포트"
-
-#: ../vnc.html:108
-msgid "viewport drag"
-msgstr "뷰포트 드래그"
-
-#: ../vnc.html:114 ../vnc.html:117 ../vnc.html:120 ../vnc.html:123
-msgid "Active Mouse Button"
-msgstr "마우스 버튼 활성화"
-
-#: ../vnc.html:114
-msgid "No mousebutton"
-msgstr "마우스 버튼 없음"
-
-#: ../vnc.html:117
-msgid "Left mousebutton"
-msgstr "왼쪽 마우스 버튼"
-
-#: ../vnc.html:120
-msgid "Middle mousebutton"
-msgstr "중간 마우스 버튼"
-
-#: ../vnc.html:123
-msgid "Right mousebutton"
-msgstr "오른쪽 마우스 버튼"
-
-#: ../vnc.html:126
-msgid "Keyboard"
-msgstr "키보드"
-
-#: ../vnc.html:126
-msgid "Show Keyboard"
-msgstr "키보드 보이기"
-
-#: ../vnc.html:133
-msgid "Extra keys"
-msgstr "기타 키들"
-
-#: ../vnc.html:133
-msgid "Show Extra Keys"
-msgstr "기타 키들 보이기"
-
-#: ../vnc.html:138
-msgid "Ctrl"
-msgstr "Ctrl"
-
-#: ../vnc.html:138
-msgid "Toggle Ctrl"
-msgstr "Ctrl 켜기/끄기"
-
-#: ../vnc.html:141
-msgid "Alt"
-msgstr "Alt"
-
-#: ../vnc.html:141
-msgid "Toggle Alt"
-msgstr "Alt 켜기/끄기"
-
-#: ../vnc.html:144
-msgid "Send Tab"
-msgstr "Tab 보내기"
-
-#: ../vnc.html:144
-msgid "Tab"
-msgstr "Tab"
-
-#: ../vnc.html:147
-msgid "Esc"
-msgstr "Esc"
-
-#: ../vnc.html:147
-msgid "Send Escape"
-msgstr "Esc 보내기"
-
-#: ../vnc.html:150
-msgid "Ctrl+Alt+Del"
-msgstr "Ctrl+Alt+Del"
-
-#: ../vnc.html:150
-msgid "Send Ctrl-Alt-Del"
-msgstr "Ctrl+Alt+Del 보내기"
-
-#: ../vnc.html:158
-msgid "Shutdown/Reboot"
-msgstr "셧다운/리붓"
-
-#: ../vnc.html:158
-msgid "Shutdown/Reboot..."
-msgstr "셧다운/리붓..."
-
-#: ../vnc.html:164
-msgid "Power"
-msgstr "전원"
-
-#: ../vnc.html:166
-msgid "Shutdown"
-msgstr "셧다운"
-
-#: ../vnc.html:167
-msgid "Reboot"
-msgstr "리붓"
-
-#: ../vnc.html:168
-msgid "Reset"
-msgstr "리셋"
-
-#: ../vnc.html:173 ../vnc.html:179
-msgid "Clipboard"
-msgstr "클립보드"
-
-#: ../vnc.html:183
-msgid "Clear"
-msgstr "지우기"
-
-#: ../vnc.html:189
-msgid "Fullscreen"
-msgstr "전체화면"
-
-#: ../vnc.html:194 ../vnc.html:201
-msgid "Settings"
-msgstr "설정"
-
-#: ../vnc.html:204
-msgid "Shared Mode"
-msgstr "공유 모드"
-
-#: ../vnc.html:207
-msgid "View Only"
-msgstr "보기 전용"
-
-#: ../vnc.html:211
-msgid "Clip to Window"
-msgstr "창에 클립"
-
-#: ../vnc.html:214
-msgid "Scaling Mode:"
-msgstr "스케일링 모드:"
-
-#: ../vnc.html:216
-msgid "None"
-msgstr "없음"
-
-#: ../vnc.html:217
-msgid "Local Scaling"
-msgstr "로컬 스케일링"
-
-#: ../vnc.html:218
-msgid "Remote Resizing"
-msgstr "원격 크기 조절"
-
-#: ../vnc.html:223
-msgid "Advanced"
-msgstr "고급"
-
-#: ../vnc.html:226
-msgid "Repeater ID:"
-msgstr "중계 ID"
-
-#: ../vnc.html:230
-msgid "WebSocket"
-msgstr "웹소켓"
-
-#: ../vnc.html:233
-msgid "Encrypt"
-msgstr "암호화"
-
-#: ../vnc.html:236
-msgid "Host:"
-msgstr "호스트:"
-
-#: ../vnc.html:240
-msgid "Port:"
-msgstr "포트:"
-
-#: ../vnc.html:244
-msgid "Path:"
-msgstr "위치:"
-
-#: ../vnc.html:251
-msgid "Automatic Reconnect"
-msgstr "자동 재연결"
-
-#: ../vnc.html:254
-msgid "Reconnect Delay (ms):"
-msgstr "재연결 지연 시간 (ms)"
-
-#: ../vnc.html:260
-msgid "Logging:"
-msgstr "로깅"
-
-#: ../vnc.html:272
-msgid "Disconnect"
-msgstr "연결 해제"
-
-#: ../vnc.html:291
-msgid "Connect"
-msgstr "연결"
-
-#: ../vnc.html:301
-msgid "Password:"
-msgstr "비밀번호:"
-
-#: ../vnc.html:305
-msgid "Send Password"
-msgstr "비밀번호 전송"
-
-#: ../vnc.html:315
-msgid "Cancel"
-msgstr "취소"
diff --git a/systemvm/agent/noVNC/po/nl.po b/systemvm/agent/noVNC/po/nl.po
deleted file mode 100644
index 343204a..0000000
--- a/systemvm/agent/noVNC/po/nl.po
+++ /dev/null
@@ -1,322 +0,0 @@
-# Dutch translations for noVNC package
-# Nederlandse vertalingen voor het pakket noVNC.
-# Copyright (C) 2018 The noVNC Authors
-# This file is distributed under the same license as the noVNC package.
-# Loek Janssen <loekjanssen@gmail.com>, 2016.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: noVNC 1.1.0\n"
-"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
-"POT-Creation-Date: 2019-04-09 11:06+0100\n"
-"PO-Revision-Date: 2019-04-09 17:17+0100\n"
-"Last-Translator: Arend Lapere <arend.lapere@gmail.com>\n"
-"Language-Team: none\n"
-"Language: nl\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#: ../app/ui.js:383
-msgid "Connecting..."
-msgstr "Verbinden..."
-
-#: ../app/ui.js:390
-msgid "Disconnecting..."
-msgstr "Verbinding verbreken..."
-
-#: ../app/ui.js:396
-msgid "Reconnecting..."
-msgstr "Opnieuw verbinding maken..."
-
-#: ../app/ui.js:401
-msgid "Internal error"
-msgstr "Interne fout"
-
-#: ../app/ui.js:991
-msgid "Must set host"
-msgstr "Host moeten worden ingesteld"
-
-#: ../app/ui.js:1073
-msgid "Connected (encrypted) to "
-msgstr "Verbonden (versleuteld) met "
-
-#: ../app/ui.js:1075
-msgid "Connected (unencrypted) to "
-msgstr "Verbonden (onversleuteld) met "
-
-#: ../app/ui.js:1098
-msgid "Something went wrong, connection is closed"
-msgstr "Er iets fout gelopen, verbinding werd verbroken"
-
-#: ../app/ui.js:1101
-msgid "Failed to connect to server"
-msgstr "Verbinding maken met server is mislukt"
-
-#: ../app/ui.js:1111
-msgid "Disconnected"
-msgstr "Verbinding verbroken"
-
-#: ../app/ui.js:1124
-msgid "New connection has been rejected with reason: "
-msgstr "Nieuwe verbinding is geweigerd omwille van de volgende reden: "
-
-#: ../app/ui.js:1127
-msgid "New connection has been rejected"
-msgstr "Nieuwe verbinding is geweigerd"
-
-#: ../app/ui.js:1147
-msgid "Password is required"
-msgstr "Wachtwoord is vereist"
-
-#: ../vnc.html:80
-msgid "noVNC encountered an error:"
-msgstr "noVNC heeft een fout bemerkt:"
-
-#: ../vnc.html:90
-msgid "Hide/Show the control bar"
-msgstr "Verberg/Toon de bedieningsbalk"
-
-#: ../vnc.html:97
-msgid "Move/Drag Viewport"
-msgstr "Verplaats/Versleep Kijkvenster"
-
-#: ../vnc.html:97
-msgid "viewport drag"
-msgstr "kijkvenster slepen"
-
-#: ../vnc.html:103 ../vnc.html:106 ../vnc.html:109 ../vnc.html:112
-msgid "Active Mouse Button"
-msgstr "Actieve Muisknop"
-
-#: ../vnc.html:103
-msgid "No mousebutton"
-msgstr "Geen muisknop"
-
-#: ../vnc.html:106
-msgid "Left mousebutton"
-msgstr "Linker muisknop"
-
-#: ../vnc.html:109
-msgid "Middle mousebutton"
-msgstr "Middelste muisknop"
-
-#: ../vnc.html:112
-msgid "Right mousebutton"
-msgstr "Rechter muisknop"
-
-#: ../vnc.html:115
-msgid "Keyboard"
-msgstr "Toetsenbord"
-
-#: ../vnc.html:115
-msgid "Show Keyboard"
-msgstr "Toon Toetsenbord"
-
-#: ../vnc.html:121
-msgid "Extra keys"
-msgstr "Extra toetsen"
-
-#: ../vnc.html:121
-msgid "Show Extra Keys"
-msgstr "Toon Extra Toetsen"
-
-#: ../vnc.html:126
-msgid "Ctrl"
-msgstr "Ctrl"
-
-#: ../vnc.html:126
-msgid "Toggle Ctrl"
-msgstr "Ctrl omschakelen"
-
-#: ../vnc.html:129
-msgid "Alt"
-msgstr "Alt"
-
-#: ../vnc.html:129
-msgid "Toggle Alt"
-msgstr "Alt omschakelen"
-
-#: ../vnc.html:132
-msgid "Toggle Windows"
-msgstr "Windows omschakelen"
-
-#: ../vnc.html:132
-msgid "Windows"
-msgstr "Windows"
-
-#: ../vnc.html:135
-msgid "Send Tab"
-msgstr "Tab Sturen"
-
-#: ../vnc.html:135
-msgid "Tab"
-msgstr "Tab"
-
-#: ../vnc.html:138
-msgid "Esc"
-msgstr "Esc"
-
-#: ../vnc.html:138
-msgid "Send Escape"
-msgstr "Escape Sturen"
-
-#: ../vnc.html:141
-msgid "Ctrl+Alt+Del"
-msgstr "Ctrl-Alt-Del"
-
-#: ../vnc.html:141
-msgid "Send Ctrl-Alt-Del"
-msgstr "Ctrl-Alt-Del Sturen"
-
-#: ../vnc.html:149
-msgid "Shutdown/Reboot"
-msgstr "Uitschakelen/Herstarten"
-
-#: ../vnc.html:149
-msgid "Shutdown/Reboot..."
-msgstr "Uitschakelen/Herstarten..."
-
-#: ../vnc.html:155
-msgid "Power"
-msgstr "Systeem"
-
-#: ../vnc.html:157
-msgid "Shutdown"
-msgstr "Uitschakelen"
-
-#: ../vnc.html:158
-msgid "Reboot"
-msgstr "Herstarten"
-
-#: ../vnc.html:159
-msgid "Reset"
-msgstr "Resetten"
-
-#: ../vnc.html:164 ../vnc.html:170
-msgid "Clipboard"
-msgstr "Klembord"
-
-#: ../vnc.html:174
-msgid "Clear"
-msgstr "Wissen"
-
-#: ../vnc.html:180
-msgid "Fullscreen"
-msgstr "Volledig Scherm"
-
-#: ../vnc.html:185 ../vnc.html:192
-msgid "Settings"
-msgstr "Instellingen"
-
-#: ../vnc.html:195
-msgid "Shared Mode"
-msgstr "Gedeelde Modus"
-
-#: ../vnc.html:198
-msgid "View Only"
-msgstr "Alleen Kijken"
-
-#: ../vnc.html:202
-msgid "Clip to Window"
-msgstr "Randen buiten venster afsnijden"
-
-#: ../vnc.html:205
-msgid "Scaling Mode:"
-msgstr "Schaalmodus:"
-
-#: ../vnc.html:207
-msgid "None"
-msgstr "Geen"
-
-#: ../vnc.html:208
-msgid "Local Scaling"
-msgstr "Lokaal Schalen"
-
-#: ../vnc.html:209
-msgid "Remote Resizing"
-msgstr "Op Afstand Formaat Wijzigen"
-
-#: ../vnc.html:214
-msgid "Advanced"
-msgstr "Geavanceerd"
-
-#: ../vnc.html:217
-msgid "Repeater ID:"
-msgstr "Repeater ID:"
-
-#: ../vnc.html:221
-msgid "WebSocket"
-msgstr "WebSocket"
-
-#: ../vnc.html:224
-msgid "Encrypt"
-msgstr "Versleutelen"
-
-#: ../vnc.html:227
-msgid "Host:"
-msgstr "Host:"
-
-#: ../vnc.html:231
-msgid "Port:"
-msgstr "Poort:"
-
-#: ../vnc.html:235
-msgid "Path:"
-msgstr "Pad:"
-
-#: ../vnc.html:242
-msgid "Automatic Reconnect"
-msgstr "Automatisch Opnieuw Verbinden"
-
-#: ../vnc.html:245
-msgid "Reconnect Delay (ms):"
-msgstr "Vertraging voor Opnieuw Verbinden (ms):"
-
-#: ../vnc.html:250
-msgid "Show Dot when No Cursor"
-msgstr "Geef stip weer indien geen cursor"
-
-#: ../vnc.html:255
-msgid "Logging:"
-msgstr "Logmeldingen:"
-
-#: ../vnc.html:267
-msgid "Disconnect"
-msgstr "Verbinding verbreken"
-
-#: ../vnc.html:286
-msgid "Connect"
-msgstr "Verbinden"
-
-#: ../vnc.html:296
-msgid "Password:"
-msgstr "Wachtwoord:"
-
-#: ../vnc.html:300
-msgid "Send Password"
-msgstr "Verzend Wachtwoord:"
-
-#: ../vnc.html:310
-msgid "Cancel"
-msgstr "Annuleren"
-
-#~ msgid "Disconnect timeout"
-#~ msgstr "Timeout tijdens verbreken van verbinding"
-
-#~ msgid "Local Downscaling"
-#~ msgstr "Lokaal Neerschalen"
-
-#~ msgid "Local Cursor"
-#~ msgstr "Lokale Cursor"
-
-#~ msgid "Canvas not supported."
-#~ msgstr "Canvas wordt niet ondersteund."
-
-#~ msgid ""
-#~ "Forcing clipping mode since scrollbars aren't supported by IE in "
-#~ "fullscreen"
-#~ msgstr ""
-#~ "''Clipping mode' ingeschakeld, omdat schuifbalken in volledige-scherm-"
-#~ "modus in IE niet worden ondersteund"
diff --git a/systemvm/agent/noVNC/po/noVNC.pot b/systemvm/agent/noVNC/po/noVNC.pot
deleted file mode 100644
index 200be01..0000000
--- a/systemvm/agent/noVNC/po/noVNC.pot
+++ /dev/null
@@ -1,302 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR The noVNC Authors
-# This file is distributed under the same license as the noVNC package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: noVNC 1.1.0\n"
-"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
-"POT-Creation-Date: 2019-01-16 11:06+0100\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
-"Language-Team: LANGUAGE <LL@li.org>\n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=CHARSET\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: ../app/ui.js:387
-msgid "Connecting..."
-msgstr ""
-
-#: ../app/ui.js:394
-msgid "Disconnecting..."
-msgstr ""
-
-#: ../app/ui.js:400
-msgid "Reconnecting..."
-msgstr ""
-
-#: ../app/ui.js:405
-msgid "Internal error"
-msgstr ""
-
-#: ../app/ui.js:995
-msgid "Must set host"
-msgstr ""
-
-#: ../app/ui.js:1077
-msgid "Connected (encrypted) to "
-msgstr ""
-
-#: ../app/ui.js:1079
-msgid "Connected (unencrypted) to "
-msgstr ""
-
-#: ../app/ui.js:1102
-msgid "Something went wrong, connection is closed"
-msgstr ""
-
-#: ../app/ui.js:1105
-msgid "Failed to connect to server"
-msgstr ""
-
-#: ../app/ui.js:1115
-msgid "Disconnected"
-msgstr ""
-
-#: ../app/ui.js:1128
-msgid "New connection has been rejected with reason: "
-msgstr ""
-
-#: ../app/ui.js:1131
-msgid "New connection has been rejected"
-msgstr ""
-
-#: ../app/ui.js:1151
-msgid "Password is required"
-msgstr ""
-
-#: ../vnc.html:84
-msgid "noVNC encountered an error:"
-msgstr ""
-
-#: ../vnc.html:94
-msgid "Hide/Show the control bar"
-msgstr ""
-
-#: ../vnc.html:101
-msgid "Move/Drag Viewport"
-msgstr ""
-
-#: ../vnc.html:101
-msgid "viewport drag"
-msgstr ""
-
-#: ../vnc.html:107 ../vnc.html:110 ../vnc.html:113 ../vnc.html:116
-msgid "Active Mouse Button"
-msgstr ""
-
-#: ../vnc.html:107
-msgid "No mousebutton"
-msgstr ""
-
-#: ../vnc.html:110
-msgid "Left mousebutton"
-msgstr ""
-
-#: ../vnc.html:113
-msgid "Middle mousebutton"
-msgstr ""
-
-#: ../vnc.html:116
-msgid "Right mousebutton"
-msgstr ""
-
-#: ../vnc.html:119
-msgid "Keyboard"
-msgstr ""
-
-#: ../vnc.html:119
-msgid "Show Keyboard"
-msgstr ""
-
-#: ../vnc.html:126
-msgid "Extra keys"
-msgstr ""
-
-#: ../vnc.html:126
-msgid "Show Extra Keys"
-msgstr ""
-
-#: ../vnc.html:131
-msgid "Ctrl"
-msgstr ""
-
-#: ../vnc.html:131
-msgid "Toggle Ctrl"
-msgstr ""
-
-#: ../vnc.html:134
-msgid "Alt"
-msgstr ""
-
-#: ../vnc.html:134
-msgid "Toggle Alt"
-msgstr ""
-
-#: ../vnc.html:137
-msgid "Toggle Windows"
-msgstr ""
-
-#: ../vnc.html:137
-msgid "Windows"
-msgstr ""
-
-#: ../vnc.html:140
-msgid "Send Tab"
-msgstr ""
-
-#: ../vnc.html:140
-msgid "Tab"
-msgstr ""
-
-#: ../vnc.html:143
-msgid "Esc"
-msgstr ""
-
-#: ../vnc.html:143
-msgid "Send Escape"
-msgstr ""
-
-#: ../vnc.html:146
-msgid "Ctrl+Alt+Del"
-msgstr ""
-
-#: ../vnc.html:146
-msgid "Send Ctrl-Alt-Del"
-msgstr ""
-
-#: ../vnc.html:154
-msgid "Shutdown/Reboot"
-msgstr ""
-
-#: ../vnc.html:154
-msgid "Shutdown/Reboot..."
-msgstr ""
-
-#: ../vnc.html:160
-msgid "Power"
-msgstr ""
-
-#: ../vnc.html:162
-msgid "Shutdown"
-msgstr ""
-
-#: ../vnc.html:163
-msgid "Reboot"
-msgstr ""
-
-#: ../vnc.html:164
-msgid "Reset"
-msgstr ""
-
-#: ../vnc.html:169 ../vnc.html:175
-msgid "Clipboard"
-msgstr ""
-
-#: ../vnc.html:179
-msgid "Clear"
-msgstr ""
-
-#: ../vnc.html:185
-msgid "Fullscreen"
-msgstr ""
-
-#: ../vnc.html:190 ../vnc.html:197
-msgid "Settings"
-msgstr ""
-
-#: ../vnc.html:200
-msgid "Shared Mode"
-msgstr ""
-
-#: ../vnc.html:203
-msgid "View Only"
-msgstr ""
-
-#: ../vnc.html:207
-msgid "Clip to Window"
-msgstr ""
-
-#: ../vnc.html:210
-msgid "Scaling Mode:"
-msgstr ""
-
-#: ../vnc.html:212
-msgid "None"
-msgstr ""
-
-#: ../vnc.html:213
-msgid "Local Scaling"
-msgstr ""
-
-#: ../vnc.html:214
-msgid "Remote Resizing"
-msgstr ""
-
-#: ../vnc.html:219
-msgid "Advanced"
-msgstr ""
-
-#: ../vnc.html:222
-msgid "Repeater ID:"
-msgstr ""
-
-#: ../vnc.html:226
-msgid "WebSocket"
-msgstr ""
-
-#: ../vnc.html:229
-msgid "Encrypt"
-msgstr ""
-
-#: ../vnc.html:232
-msgid "Host:"
-msgstr ""
-
-#: ../vnc.html:236
-msgid "Port:"
-msgstr ""
-
-#: ../vnc.html:240
-msgid "Path:"
-msgstr ""
-
-#: ../vnc.html:247
-msgid "Automatic Reconnect"
-msgstr ""
-
-#: ../vnc.html:250
-msgid "Reconnect Delay (ms):"
-msgstr ""
-
-#: ../vnc.html:255
-msgid "Show Dot when No Cursor"
-msgstr ""
-
-#: ../vnc.html:260
-msgid "Logging:"
-msgstr ""
-
-#: ../vnc.html:272
-msgid "Disconnect"
-msgstr ""
-
-#: ../vnc.html:291
-msgid "Connect"
-msgstr ""
-
-#: ../vnc.html:301
-msgid "Password:"
-msgstr ""
-
-#: ../vnc.html:305
-msgid "Send Password"
-msgstr ""
-
-#: ../vnc.html:315
-msgid "Cancel"
-msgstr ""
diff --git a/systemvm/agent/noVNC/po/pl.po b/systemvm/agent/noVNC/po/pl.po
deleted file mode 100644
index 5acfdc4..0000000
--- a/systemvm/agent/noVNC/po/pl.po
+++ /dev/null
@@ -1,325 +0,0 @@
-# Polish translations for noVNC package.
-# Copyright (C) 2018 The noVNC Authors
-# This file is distributed under the same license as the noVNC package.
-# Mariusz Jamro <mariusz.jamro@gmail.com>, 2017.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: noVNC 0.6.1\n"
-"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
-"POT-Creation-Date: 2017-11-21 19:53+0100\n"
-"PO-Revision-Date: 2017-11-21 19:54+0100\n"
-"Last-Translator: Mariusz Jamro <mariusz.jamro@gmail.com>\n"
-"Language-Team: Polish\n"
-"Language: pl\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 "
-"|| n%100>=20) ? 1 : 2);\n"
-"X-Generator: Poedit 2.0.1\n"
-
-#: ../app/ui.js:404
-msgid "Connecting..."
-msgstr "Łączenie..."
-
-#: ../app/ui.js:411
-msgid "Disconnecting..."
-msgstr "Rozłączanie..."
-
-#: ../app/ui.js:417
-msgid "Reconnecting..."
-msgstr "Łączenie..."
-
-#: ../app/ui.js:422
-msgid "Internal error"
-msgstr "Błąd wewnętrzny"
-
-#: ../app/ui.js:1019
-msgid "Must set host"
-msgstr "Host i port są wymagane"
-
-#: ../app/ui.js:1099
-msgid "Connected (encrypted) to "
-msgstr "Połączenie (szyfrowane) z "
-
-#: ../app/ui.js:1101
-msgid "Connected (unencrypted) to "
-msgstr "Połączenie (nieszyfrowane) z "
-
-#: ../app/ui.js:1119
-msgid "Something went wrong, connection is closed"
-msgstr "Coś poszło źle, połączenie zostało zamknięte"
-
-#: ../app/ui.js:1129
-msgid "Disconnected"
-msgstr "Rozłączony"
-
-#: ../app/ui.js:1142
-msgid "New connection has been rejected with reason: "
-msgstr "Nowe połączenie zostało odrzucone z powodu: "
-
-#: ../app/ui.js:1145
-msgid "New connection has been rejected"
-msgstr "Nowe połączenie zostało odrzucone"
-
-#: ../app/ui.js:1166
-msgid "Password is required"
-msgstr "Hasło jest wymagane"
-
-#: ../vnc.html:89
-msgid "noVNC encountered an error:"
-msgstr "noVNC napotkało błąd:"
-
-#: ../vnc.html:99
-msgid "Hide/Show the control bar"
-msgstr "Pokaż/Ukryj pasek ustawień"
-
-#: ../vnc.html:106
-msgid "Move/Drag Viewport"
-msgstr "Ruszaj/Przeciągaj Viewport"
-
-#: ../vnc.html:106
-msgid "viewport drag"
-msgstr "przeciągnij viewport"
-
-#: ../vnc.html:112 ../vnc.html:115 ../vnc.html:118 ../vnc.html:121
-msgid "Active Mouse Button"
-msgstr "Aktywny Przycisk Myszy"
-
-#: ../vnc.html:112
-msgid "No mousebutton"
-msgstr "Brak przycisku myszy"
-
-#: ../vnc.html:115
-msgid "Left mousebutton"
-msgstr "Lewy przycisk myszy"
-
-#: ../vnc.html:118
-msgid "Middle mousebutton"
-msgstr "Środkowy przycisk myszy"
-
-#: ../vnc.html:121
-msgid "Right mousebutton"
-msgstr "Prawy przycisk myszy"
-
-#: ../vnc.html:124
-msgid "Keyboard"
-msgstr "Klawiatura"
-
-#: ../vnc.html:124
-msgid "Show Keyboard"
-msgstr "Pokaż klawiaturę"
-
-#: ../vnc.html:131
-msgid "Extra keys"
-msgstr "Przyciski dodatkowe"
-
-#: ../vnc.html:131
-msgid "Show Extra Keys"
-msgstr "Pokaż przyciski dodatkowe"
-
-#: ../vnc.html:136
-msgid "Ctrl"
-msgstr "Ctrl"
-
-#: ../vnc.html:136
-msgid "Toggle Ctrl"
-msgstr "Przełącz Ctrl"
-
-#: ../vnc.html:139
-msgid "Alt"
-msgstr "Alt"
-
-#: ../vnc.html:139
-msgid "Toggle Alt"
-msgstr "Przełącz Alt"
-
-#: ../vnc.html:142
-msgid "Send Tab"
-msgstr "Wyślij Tab"
-
-#: ../vnc.html:142
-msgid "Tab"
-msgstr "Tab"
-
-#: ../vnc.html:145
-msgid "Esc"
-msgstr "Esc"
-
-#: ../vnc.html:145
-msgid "Send Escape"
-msgstr "Wyślij Escape"
-
-#: ../vnc.html:148
-msgid "Ctrl+Alt+Del"
-msgstr "Ctrl+Alt+Del"
-
-#: ../vnc.html:148
-msgid "Send Ctrl-Alt-Del"
-msgstr "Wyślij Ctrl-Alt-Del"
-
-#: ../vnc.html:156
-msgid "Shutdown/Reboot"
-msgstr "Wyłącz/Uruchom ponownie"
-
-#: ../vnc.html:156
-msgid "Shutdown/Reboot..."
-msgstr "Wyłącz/Uruchom ponownie..."
-
-#: ../vnc.html:162
-msgid "Power"
-msgstr "Włączony"
-
-#: ../vnc.html:164
-msgid "Shutdown"
-msgstr "Wyłącz"
-
-#: ../vnc.html:165
-msgid "Reboot"
-msgstr "Uruchom ponownie"
-
-#: ../vnc.html:166
-msgid "Reset"
-msgstr "Resetuj"
-
-#: ../vnc.html:171 ../vnc.html:177
-msgid "Clipboard"
-msgstr "Schowek"
-
-#: ../vnc.html:181
-msgid "Clear"
-msgstr "Wyczyść"
-
-#: ../vnc.html:187
-msgid "Fullscreen"
-msgstr "Pełny ekran"
-
-#: ../vnc.html:192 ../vnc.html:199
-msgid "Settings"
-msgstr "Ustawienia"
-
-#: ../vnc.html:202
-msgid "Shared Mode"
-msgstr "Tryb Współdzielenia"
-
-#: ../vnc.html:205
-msgid "View Only"
-msgstr "Tylko Podgląd"
-
-#: ../vnc.html:209
-msgid "Clip to Window"
-msgstr "Przytnij do Okna"
-
-#: ../vnc.html:212
-msgid "Scaling Mode:"
-msgstr "Tryb Skalowania:"
-
-#: ../vnc.html:214
-msgid "None"
-msgstr "Brak"
-
-#: ../vnc.html:215
-msgid "Local Scaling"
-msgstr "Skalowanie lokalne"
-
-#: ../vnc.html:216
-msgid "Remote Resizing"
-msgstr "Skalowanie zdalne"
-
-#: ../vnc.html:221
-msgid "Advanced"
-msgstr "Zaawansowane"
-
-#: ../vnc.html:224
-msgid "Repeater ID:"
-msgstr "ID Repeatera:"
-
-#: ../vnc.html:228
-msgid "WebSocket"
-msgstr "WebSocket"
-
-#: ../vnc.html:231
-msgid "Encrypt"
-msgstr "Szyfrowanie"
-
-#: ../vnc.html:234
-msgid "Host:"
-msgstr "Host:"
-
-#: ../vnc.html:238
-msgid "Port:"
-msgstr "Port:"
-
-#: ../vnc.html:242
-msgid "Path:"
-msgstr "Ścieżka:"
-
-#: ../vnc.html:249
-msgid "Automatic Reconnect"
-msgstr "Automatycznie wznawiaj połączenie"
-
-#: ../vnc.html:252
-msgid "Reconnect Delay (ms):"
-msgstr "Opóźnienie wznawiania (ms):"
-
-#: ../vnc.html:258
-msgid "Logging:"
-msgstr "Poziom logowania:"
-
-#: ../vnc.html:270
-msgid "Disconnect"
-msgstr "Rozłącz"
-
-#: ../vnc.html:289
-msgid "Connect"
-msgstr "Połącz"
-
-#: ../vnc.html:299
-msgid "Password:"
-msgstr "Hasło:"
-
-#: ../vnc.html:313
-msgid "Cancel"
-msgstr "Anuluj"
-
-#: ../vnc.html:329
-msgid "Canvas not supported."
-msgstr "Element Canvas nie jest wspierany."
-
-#~ msgid "Disconnect timeout"
-#~ msgstr "Timeout rozłączenia"
-
-#~ msgid "Local Downscaling"
-#~ msgstr "Downscaling lokalny"
-
-#~ msgid "Local Cursor"
-#~ msgstr "Lokalny kursor"
-
-#~ msgid ""
-#~ "Forcing clipping mode since scrollbars aren't supported by IE in "
-#~ "fullscreen"
-#~ msgstr ""
-#~ "Wymuszam clipping mode ponieważ paski przewijania nie są wspierane przez "
-#~ "IE w trybie pełnoekranowym"
-
-#~ msgid "True Color"
-#~ msgstr "True Color"
-
-#~ msgid "Style:"
-#~ msgstr "Styl:"
-
-#~ msgid "default"
-#~ msgstr "domyślny"
-
-#~ msgid "Apply"
-#~ msgstr "Zapisz"
-
-#~ msgid "Connection"
-#~ msgstr "Połączenie"
-
-#~ msgid "Token:"
-#~ msgstr "Token:"
-
-#~ msgid "Send Password"
-#~ msgstr "Wyślij Hasło"
diff --git a/systemvm/agent/noVNC/po/po2js b/systemvm/agent/noVNC/po/po2js
deleted file mode 100755
index 03c1490..0000000
--- a/systemvm/agent/noVNC/po/po2js
+++ /dev/null
@@ -1,43 +0,0 @@
-#!/usr/bin/env node
-/*
- * ps2js: gettext .po to noVNC .js converter
- * Copyright (C) 2018 The noVNC Authors
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-const getopt = require('node-getopt');
-const fs = require('fs');
-const po2json = require("po2json");
-
-const opt = getopt.create([
-  ['h' , 'help'                , 'display this help'],
-]).bindHelp().parseSystem();
-
-if (opt.argv.length != 2) {
-  console.error("Incorrect number of arguments given");
-  process.exit(1);
-}
-
-const data = po2json.parseFileSync(opt.argv[0]);
-
-const bodyPart = Object.keys(data).filter((msgid) => msgid !== "").map((msgid) => {
-    if (msgid === "") return;
-    const msgstr = data[msgid][1];
-    return "    " + JSON.stringify(msgid) + ": " + JSON.stringify(msgstr);
-}).join(",\n");
-
-const output = "{\n" + bodyPart + "\n}";
-
-fs.writeFileSync(opt.argv[1], output);
diff --git a/systemvm/agent/noVNC/po/ru.po b/systemvm/agent/noVNC/po/ru.po
deleted file mode 100644
index fb5d087..0000000
--- a/systemvm/agent/noVNC/po/ru.po
+++ /dev/null
@@ -1,306 +0,0 @@
-# Russian translations for noVNC package
-# Русский перевод для пакета noVNC.
-# Copyright (C) 2019 Dmitriy Shweew
-# This file is distributed under the same license as the noVNC package.
-# Dmitriy Shweew <shweew@it-advisor.ru>, 2019.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: noVNC 1.1.0\n"
-"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
-"POT-Creation-Date: 2019-02-26 14:53+0400\n"
-"PO-Revision-Date: 2019-02-17 17:29+0400\n"
-"Last-Translator: Dmitriy Shweew <shweew@it-advisor.ru>\n"
-"Language-Team: Russian\n"
-"Language: ru\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
-"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
-"X-Generator: Poedit 2.2.1\n"
-"X-Poedit-Flags-xgettext: --add-comments\n"
-
-#: ../app/ui.js:387
-msgid "Connecting..."
-msgstr "Подключение..."
-
-#: ../app/ui.js:394
-msgid "Disconnecting..."
-msgstr "Отключение..."
-
-#: ../app/ui.js:400
-msgid "Reconnecting..."
-msgstr "Переподключение..."
-
-#: ../app/ui.js:405
-msgid "Internal error"
-msgstr "Внутренняя ошибка"
-
-#: ../app/ui.js:995
-msgid "Must set host"
-msgstr "Задайте имя сервера или IP"
-
-#: ../app/ui.js:1077
-msgid "Connected (encrypted) to "
-msgstr "Подключено (с шифрованием) к "
-
-#: ../app/ui.js:1079
-msgid "Connected (unencrypted) to "
-msgstr "Подключено (без шифрования) к "
-
-#: ../app/ui.js:1102
-msgid "Something went wrong, connection is closed"
-msgstr "Что-то пошло не так, подключение разорвано"
-
-#: ../app/ui.js:1105
-msgid "Failed to connect to server"
-msgstr "Ошибка подключения к серверу"
-
-#: ../app/ui.js:1115
-msgid "Disconnected"
-msgstr "Отключено"
-
-#: ../app/ui.js:1128
-msgid "New connection has been rejected with reason: "
-msgstr "Подключиться не удалось: "
-
-#: ../app/ui.js:1131
-msgid "New connection has been rejected"
-msgstr "Подключиться не удалось"
-
-#: ../app/ui.js:1151
-msgid "Password is required"
-msgstr "Требуется пароль"
-
-#: ../vnc.html:84
-msgid "noVNC encountered an error:"
-msgstr "Ошибка noVNC: "
-
-#: ../vnc.html:94
-msgid "Hide/Show the control bar"
-msgstr "Скрыть/Показать контрольную панель"
-
-#: ../vnc.html:101
-msgid "Move/Drag Viewport"
-msgstr "Переместить окно"
-
-#: ../vnc.html:101
-msgid "viewport drag"
-msgstr "Переместить окно"
-
-#: ../vnc.html:107 ../vnc.html:110 ../vnc.html:113 ../vnc.html:116
-msgid "Active Mouse Button"
-msgstr "Активировать кнопки мыши"
-
-#: ../vnc.html:107
-msgid "No mousebutton"
-msgstr "Отключить кнопки мыши"
-
-#: ../vnc.html:110
-msgid "Left mousebutton"
-msgstr "Левая кнопка мыши"
-
-#: ../vnc.html:113
-msgid "Middle mousebutton"
-msgstr "Средняя  кнопка мыши"
-
-#: ../vnc.html:116
-msgid "Right mousebutton"
-msgstr "Правая кнопка мыши"
-
-#: ../vnc.html:119
-msgid "Keyboard"
-msgstr "Клавиатура"
-
-#: ../vnc.html:119
-msgid "Show Keyboard"
-msgstr "Показать клавиатуру"
-
-#: ../vnc.html:126
-msgid "Extra keys"
-msgstr "Доп. кнопки"
-
-#: ../vnc.html:126
-msgid "Show Extra Keys"
-msgstr "Показать дополнительные кнопки"
-
-#: ../vnc.html:131
-msgid "Ctrl"
-msgstr "Ctrl"
-
-#: ../vnc.html:131
-msgid "Toggle Ctrl"
-msgstr "Передать нажатие Ctrl"
-
-#: ../vnc.html:134
-msgid "Alt"
-msgstr "Alt"
-
-#: ../vnc.html:134
-msgid "Toggle Alt"
-msgstr "Передать нажатие Alt"
-
-#: ../vnc.html:137
-msgid "Toggle Windows"
-msgstr "Переключение вкладок"
-
-#: ../vnc.html:137
-msgid "Windows"
-msgstr "Вкладка"
-
-#: ../vnc.html:140
-msgid "Send Tab"
-msgstr "Передать нажатие Tab"
-
-#: ../vnc.html:140
-msgid "Tab"
-msgstr "Tab"
-
-#: ../vnc.html:143
-msgid "Esc"
-msgstr "Esc"
-
-#: ../vnc.html:143
-msgid "Send Escape"
-msgstr "Передать нажатие Escape"
-
-#: ../vnc.html:146
-msgid "Ctrl+Alt+Del"
-msgstr "Ctrl+Alt+Del"
-
-#: ../vnc.html:146
-msgid "Send Ctrl-Alt-Del"
-msgstr "Передать нажатие Ctrl-Alt-Del"
-
-#: ../vnc.html:154
-msgid "Shutdown/Reboot"
-msgstr "Выключить/Перезагрузить"
-
-#: ../vnc.html:154
-msgid "Shutdown/Reboot..."
-msgstr "Выключить/Перезагрузить..."
-
-#: ../vnc.html:160
-msgid "Power"
-msgstr "Питание"
-
-#: ../vnc.html:162
-msgid "Shutdown"
-msgstr "Выключить"
-
-#: ../vnc.html:163
-msgid "Reboot"
-msgstr "Перезагрузить"
-
-#: ../vnc.html:164
-msgid "Reset"
-msgstr "Сброс"
-
-#: ../vnc.html:169 ../vnc.html:175
-msgid "Clipboard"
-msgstr "Буфер обмена"
-
-#: ../vnc.html:179
-msgid "Clear"
-msgstr "Очистить"
-
-#: ../vnc.html:185
-msgid "Fullscreen"
-msgstr "Во весь экран"
-
-#: ../vnc.html:190 ../vnc.html:197
-msgid "Settings"
-msgstr "Настройки"
-
-#: ../vnc.html:200
-msgid "Shared Mode"
-msgstr "Общий режим"
-
-#: ../vnc.html:203
-msgid "View Only"
-msgstr "Просмотр"
-
-#: ../vnc.html:207
-msgid "Clip to Window"
-msgstr "В окно"
-
-#: ../vnc.html:210
-msgid "Scaling Mode:"
-msgstr "Масштаб:"
-
-#: ../vnc.html:212
-msgid "None"
-msgstr "Нет"
-
-#: ../vnc.html:213
-msgid "Local Scaling"
-msgstr "Локльный масштаб"
-
-#: ../vnc.html:214
-msgid "Remote Resizing"
-msgstr "Удаленный масштаб"
-
-#: ../vnc.html:219
-msgid "Advanced"
-msgstr "Дополнительно"
-
-#: ../vnc.html:222
-msgid "Repeater ID:"
-msgstr "Идентификатор ID:"
-
-#: ../vnc.html:226
-msgid "WebSocket"
-msgstr "WebSocket"
-
-#: ../vnc.html:229
-msgid "Encrypt"
-msgstr "Шифрование"
-
-#: ../vnc.html:232
-msgid "Host:"
-msgstr "Сервер:"
-
-#: ../vnc.html:236
-msgid "Port:"
-msgstr "Порт:"
-
-#: ../vnc.html:240
-msgid "Path:"
-msgstr "Путь:"
-
-#: ../vnc.html:247
-msgid "Automatic Reconnect"
-msgstr "Автоматическое переподключение"
-
-#: ../vnc.html:250
-msgid "Reconnect Delay (ms):"
-msgstr "Задержка переподключения (мс):"
-
-#: ../vnc.html:255
-msgid "Show Dot when No Cursor"
-msgstr "Показать точку вместо курсора"
-
-#: ../vnc.html:260
-msgid "Logging:"
-msgstr "Лог:"
-
-#: ../vnc.html:272
-msgid "Disconnect"
-msgstr "Отключение"
-
-#: ../vnc.html:291
-msgid "Connect"
-msgstr "Подключение"
-
-#: ../vnc.html:301
-msgid "Password:"
-msgstr "Пароль:"
-
-#: ../vnc.html:305
-msgid "Send Password"
-msgstr "Пароль: "
-
-#: ../vnc.html:315
-msgid "Cancel"
-msgstr "Выход"
diff --git a/systemvm/agent/noVNC/po/sv.po b/systemvm/agent/noVNC/po/sv.po
deleted file mode 100644
index f795566..0000000
--- a/systemvm/agent/noVNC/po/sv.po
+++ /dev/null
@@ -1,316 +0,0 @@
-# Swedish translations for noVNC package
-# Svenska översättningar för paket noVNC.
-# Copyright (C) 2018 The noVNC Authors
-# This file is distributed under the same license as the noVNC package.
-# Samuel Mannehed <samuel@cendio.se>, 2019.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: noVNC 1.1.0\n"
-"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
-"POT-Creation-Date: 2019-01-16 11:06+0100\n"
-"PO-Revision-Date: 2019-04-08 10:18+0200\n"
-"Last-Translator: Samuel Mannehed <samuel@cendio.se>\n"
-"Language-Team: none\n"
-"Language: sv\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-"X-Generator: Poedit 2.0.3\n"
-
-#: ../app/ui.js:387
-msgid "Connecting..."
-msgstr "Ansluter..."
-
-#: ../app/ui.js:394
-msgid "Disconnecting..."
-msgstr "Kopplar ner..."
-
-#: ../app/ui.js:400
-msgid "Reconnecting..."
-msgstr "Återansluter..."
-
-#: ../app/ui.js:405
-msgid "Internal error"
-msgstr "Internt fel"
-
-#: ../app/ui.js:995
-msgid "Must set host"
-msgstr "Du måste specifiera en värd"
-
-#: ../app/ui.js:1077
-msgid "Connected (encrypted) to "
-msgstr "Ansluten (krypterat) till "
-
-#: ../app/ui.js:1079
-msgid "Connected (unencrypted) to "
-msgstr "Ansluten (okrypterat) till "
-
-#: ../app/ui.js:1102
-msgid "Something went wrong, connection is closed"
-msgstr "Något gick fel, anslutningen avslutades"
-
-#: ../app/ui.js:1105
-msgid "Failed to connect to server"
-msgstr "Misslyckades att ansluta till servern"
-
-#: ../app/ui.js:1115
-msgid "Disconnected"
-msgstr "Frånkopplad"
-
-#: ../app/ui.js:1128
-msgid "New connection has been rejected with reason: "
-msgstr "Ny anslutning har blivit nekad med följande skäl: "
-
-#: ../app/ui.js:1131
-msgid "New connection has been rejected"
-msgstr "Ny anslutning har blivit nekad"
-
-#: ../app/ui.js:1151
-msgid "Password is required"
-msgstr "Lösenord krävs"
-
-#: ../vnc.html:84
-msgid "noVNC encountered an error:"
-msgstr "noVNC stötte på ett problem:"
-
-#: ../vnc.html:94
-msgid "Hide/Show the control bar"
-msgstr "Göm/Visa kontrollbaren"
-
-#: ../vnc.html:101
-msgid "Move/Drag Viewport"
-msgstr "Flytta/Dra Vyn"
-
-#: ../vnc.html:101
-msgid "viewport drag"
-msgstr "dra vy"
-
-#: ../vnc.html:107 ../vnc.html:110 ../vnc.html:113 ../vnc.html:116
-msgid "Active Mouse Button"
-msgstr "Aktiv musknapp"
-
-#: ../vnc.html:107
-msgid "No mousebutton"
-msgstr "Ingen musknapp"
-
-#: ../vnc.html:110
-msgid "Left mousebutton"
-msgstr "Vänster musknapp"
-
-#: ../vnc.html:113
-msgid "Middle mousebutton"
-msgstr "Mitten-musknapp"
-
-#: ../vnc.html:116
-msgid "Right mousebutton"
-msgstr "Höger musknapp"
-
-#: ../vnc.html:119
-msgid "Keyboard"
-msgstr "Tangentbord"
-
-#: ../vnc.html:119
-msgid "Show Keyboard"
-msgstr "Visa Tangentbord"
-
-#: ../vnc.html:126
-msgid "Extra keys"
-msgstr "Extraknappar"
-
-#: ../vnc.html:126
-msgid "Show Extra Keys"
-msgstr "Visa Extraknappar"
-
-#: ../vnc.html:131
-msgid "Ctrl"
-msgstr "Ctrl"
-
-#: ../vnc.html:131
-msgid "Toggle Ctrl"
-msgstr "Växla Ctrl"
-
-#: ../vnc.html:134
-msgid "Alt"
-msgstr "Alt"
-
-#: ../vnc.html:134
-msgid "Toggle Alt"
-msgstr "Växla Alt"
-
-#: ../vnc.html:137
-msgid "Toggle Windows"
-msgstr "Växla Windows"
-
-#: ../vnc.html:137
-msgid "Windows"
-msgstr "Windows"
-
-#: ../vnc.html:140
-msgid "Send Tab"
-msgstr "Skicka Tab"
-
-#: ../vnc.html:140
-msgid "Tab"
-msgstr "Tab"
-
-#: ../vnc.html:143
-msgid "Esc"
-msgstr "Esc"
-
-#: ../vnc.html:143
-msgid "Send Escape"
-msgstr "Skicka Escape"
-
-#: ../vnc.html:146
-msgid "Ctrl+Alt+Del"
-msgstr "Ctrl+Alt+Del"
-
-#: ../vnc.html:146
-msgid "Send Ctrl-Alt-Del"
-msgstr "Skicka Ctrl-Alt-Del"
-
-#: ../vnc.html:154
-msgid "Shutdown/Reboot"
-msgstr "Stäng av/Boota om"
-
-#: ../vnc.html:154
-msgid "Shutdown/Reboot..."
-msgstr "Stäng av/Boota om..."
-
-#: ../vnc.html:160
-msgid "Power"
-msgstr "Ström"
-
-#: ../vnc.html:162
-msgid "Shutdown"
-msgstr "Stäng av"
-
-#: ../vnc.html:163
-msgid "Reboot"
-msgstr "Boota om"
-
-#: ../vnc.html:164
-msgid "Reset"
-msgstr "Återställ"
-
-#: ../vnc.html:169 ../vnc.html:175
-msgid "Clipboard"
-msgstr "Urklipp"
-
-#: ../vnc.html:179
-msgid "Clear"
-msgstr "Rensa"
-
-#: ../vnc.html:185
-msgid "Fullscreen"
-msgstr "Fullskärm"
-
-#: ../vnc.html:190 ../vnc.html:197
-msgid "Settings"
-msgstr "Inställningar"
-
-#: ../vnc.html:200
-msgid "Shared Mode"
-msgstr "Delat Läge"
-
-#: ../vnc.html:203
-msgid "View Only"
-msgstr "Endast Visning"
-
-#: ../vnc.html:207
-msgid "Clip to Window"
-msgstr "Begränsa till Fönster"
-
-#: ../vnc.html:210
-msgid "Scaling Mode:"
-msgstr "Skalningsläge:"
-
-#: ../vnc.html:212
-msgid "None"
-msgstr "Ingen"
-
-#: ../vnc.html:213
-msgid "Local Scaling"
-msgstr "Lokal Skalning"
-
-#: ../vnc.html:214
-msgid "Remote Resizing"
-msgstr "Ändra Storlek"
-
-#: ../vnc.html:219
-msgid "Advanced"
-msgstr "Avancerat"
-
-#: ../vnc.html:222
-msgid "Repeater ID:"
-msgstr "Repeater-ID:"
-
-#: ../vnc.html:226
-msgid "WebSocket"
-msgstr "WebSocket"
-
-#: ../vnc.html:229
-msgid "Encrypt"
-msgstr "Kryptera"
-
-#: ../vnc.html:232
-msgid "Host:"
-msgstr "Värd:"
-
-#: ../vnc.html:236
-msgid "Port:"
-msgstr "Port:"
-
-#: ../vnc.html:240
-msgid "Path:"
-msgstr "Sökväg:"
-
-#: ../vnc.html:247
-msgid "Automatic Reconnect"
-msgstr "Automatisk Återanslutning"
-
-#: ../vnc.html:250
-msgid "Reconnect Delay (ms):"
-msgstr "Fördröjning (ms):"
-
-#: ../vnc.html:255
-msgid "Show Dot when No Cursor"
-msgstr "Visa prick när ingen muspekare finns"
-
-#: ../vnc.html:260
-msgid "Logging:"
-msgstr "Loggning:"
-
-#: ../vnc.html:272
-msgid "Disconnect"
-msgstr "Koppla från"
-
-#: ../vnc.html:291
-msgid "Connect"
-msgstr "Anslut"
-
-#: ../vnc.html:301
-msgid "Password:"
-msgstr "Lösenord:"
-
-#: ../vnc.html:305
-msgid "Send Password"
-msgstr "Skicka lösenord"
-
-#: ../vnc.html:315
-msgid "Cancel"
-msgstr "Avbryt"
-
-#~ msgid "Disconnect timeout"
-#~ msgstr "Det tog för lång tid att koppla ner"
-
-#~ msgid "Local Downscaling"
-#~ msgstr "Lokal Nedskalning"
-
-#~ msgid "Local Cursor"
-#~ msgstr "Lokal Muspekare"
-
-#~ msgid "Canvas not supported."
-#~ msgstr "Canvas stöds ej"
diff --git a/systemvm/agent/noVNC/po/tr.po b/systemvm/agent/noVNC/po/tr.po
deleted file mode 100644
index 8b5c181..0000000
--- a/systemvm/agent/noVNC/po/tr.po
+++ /dev/null
@@ -1,288 +0,0 @@
-# Turkish translations for noVNC package
-# Turkish translation for noVNC.
-# Copyright (C) 2018 The noVNC Authors
-# This file is distributed under the same license as the noVNC package.
-# Ömer ÇAKMAK <farukomercakmak@gmail.com>, 2018.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: noVNC 0.6.1\n"
-"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
-"POT-Creation-Date: 2017-11-24 07:16+0000\n"
-"PO-Revision-Date: 2018-01-05 19:07+0300\n"
-"Last-Translator: Ömer ÇAKMAK <farukomercakmak@gmail.com>\n"
-"Language-Team: Türkçe <gnome-turk@gnome.org>\n"
-"Language: tr\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-"X-Generator: Gtranslator 2.91.7\n"
-
-#: ../app/ui.js:404
-msgid "Connecting..."
-msgstr "Bağlanıyor..."
-
-#: ../app/ui.js:411
-msgid "Disconnecting..."
-msgstr "Bağlantı kesiliyor..."
-
-#: ../app/ui.js:417
-msgid "Reconnecting..."
-msgstr "Yeniden bağlantı kuruluyor..."
-
-#: ../app/ui.js:422
-msgid "Internal error"
-msgstr "İç hata"
-
-#: ../app/ui.js:1019
-msgid "Must set host"
-msgstr "Sunucuyu kur"
-
-#: ../app/ui.js:1099
-msgid "Connected (encrypted) to "
-msgstr "Bağlı (şifrelenmiş)"
-
-#: ../app/ui.js:1101
-msgid "Connected (unencrypted) to "
-msgstr "Bağlandı (şifrelenmemiş)"
-
-#: ../app/ui.js:1119
-msgid "Something went wrong, connection is closed"
-msgstr "Bir şeyler ters gitti, bağlantı kesildi"
-
-#: ../app/ui.js:1129
-msgid "Disconnected"
-msgstr "Bağlantı kesildi"
-
-#: ../app/ui.js:1142
-msgid "New connection has been rejected with reason: "
-msgstr "Bağlantı aşağıdaki nedenlerden dolayı reddedildi: "
-
-#: ../app/ui.js:1145
-msgid "New connection has been rejected"
-msgstr "Bağlantı reddedildi"
-
-#: ../app/ui.js:1166
-msgid "Password is required"
-msgstr "Şifre gerekli"
-
-#: ../vnc.html:89
-msgid "noVNC encountered an error:"
-msgstr "Bir hata oluştu:"
-
-#: ../vnc.html:99
-msgid "Hide/Show the control bar"
-msgstr "Denetim masasını Gizle/Göster"
-
-#: ../vnc.html:106
-msgid "Move/Drag Viewport"
-msgstr "Görünümü Taşı/Sürükle"
-
-#: ../vnc.html:106
-msgid "viewport drag"
-msgstr "Görüntü penceresini sürükle"
-
-#: ../vnc.html:112 ../vnc.html:115 ../vnc.html:118 ../vnc.html:121
-msgid "Active Mouse Button"
-msgstr "Aktif Fare Düğmesi"
-
-#: ../vnc.html:112
-msgid "No mousebutton"
-msgstr "Fare düğmesi yok"
-
-#: ../vnc.html:115
-msgid "Left mousebutton"
-msgstr "Farenin sol düğmesi"
-
-#: ../vnc.html:118
-msgid "Middle mousebutton"
-msgstr "Farenin orta düğmesi"
-
-#: ../vnc.html:121
-msgid "Right mousebutton"
-msgstr "Farenin sağ düğmesi"
-
-#: ../vnc.html:124
-msgid "Keyboard"
-msgstr "Klavye"
-
-#: ../vnc.html:124
-msgid "Show Keyboard"
-msgstr "Klavye Düzenini Göster"
-
-#: ../vnc.html:131
-msgid "Extra keys"
-msgstr "Ekstra tuşlar"
-
-#: ../vnc.html:131
-msgid "Show Extra Keys"
-msgstr "Ekstra tuşları göster"
-
-#: ../vnc.html:136
-msgid "Ctrl"
-msgstr "Ctrl"
-
-#: ../vnc.html:136
-msgid "Toggle Ctrl"
-msgstr "Ctrl Değiştir "
-
-#: ../vnc.html:139
-msgid "Alt"
-msgstr "Alt"
-
-#: ../vnc.html:139
-msgid "Toggle Alt"
-msgstr "Alt Değiştir"
-
-#: ../vnc.html:142
-msgid "Send Tab"
-msgstr "Sekme Gönder"
-
-#: ../vnc.html:142
-msgid "Tab"
-msgstr "Sekme"
-
-#: ../vnc.html:145
-msgid "Esc"
-msgstr "Esc"
-
-#: ../vnc.html:145
-msgid "Send Escape"
-msgstr "Boşluk Gönder"
-
-#: ../vnc.html:148
-msgid "Ctrl+Alt+Del"
-msgstr "Ctrl + Alt + Del"
-
-#: ../vnc.html:148
-msgid "Send Ctrl-Alt-Del"
-msgstr "Ctrl-Alt-Del Gönder"
-
-#: ../vnc.html:156
-msgid "Shutdown/Reboot"
-msgstr "Kapat/Yeniden Başlat"
-
-#: ../vnc.html:156
-msgid "Shutdown/Reboot..."
-msgstr "Kapat/Yeniden Başlat..."
-
-#: ../vnc.html:162
-msgid "Power"
-msgstr "Güç"
-
-#: ../vnc.html:164
-msgid "Shutdown"
-msgstr "Kapat"
-
-#: ../vnc.html:165
-msgid "Reboot"
-msgstr "Yeniden Başlat"
-
-#: ../vnc.html:166
-msgid "Reset"
-msgstr "Sıfırla"
-
-#: ../vnc.html:171 ../vnc.html:177
-msgid "Clipboard"
-msgstr "Pano"
-
-#: ../vnc.html:181
-msgid "Clear"
-msgstr "Temizle"
-
-#: ../vnc.html:187
-msgid "Fullscreen"
-msgstr "Tam Ekran"
-
-#: ../vnc.html:192 ../vnc.html:199
-msgid "Settings"
-msgstr "Ayarlar"
-
-#: ../vnc.html:202
-msgid "Shared Mode"
-msgstr "Paylaşım Modu"
-
-#: ../vnc.html:205
-msgid "View Only"
-msgstr "Sadece Görüntüle"
-
-#: ../vnc.html:209
-msgid "Clip to Window"
-msgstr "Pencereye Tıkla"
-
-#: ../vnc.html:212
-msgid "Scaling Mode:"
-msgstr "Ölçekleme Modu:"
-
-#: ../vnc.html:214
-msgid "None"
-msgstr "Bilinmeyen"
-
-#: ../vnc.html:215
-msgid "Local Scaling"
-msgstr "Yerel Ölçeklendirme"
-
-#: ../vnc.html:216
-msgid "Remote Resizing"
-msgstr "Uzaktan Yeniden Boyutlandırma"
-
-#: ../vnc.html:221
-msgid "Advanced"
-msgstr "Gelişmiş"
-
-#: ../vnc.html:224
-msgid "Repeater ID:"
-msgstr "Tekralayıcı ID:"
-
-#: ../vnc.html:228
-msgid "WebSocket"
-msgstr "WebSocket"
-
-#: ../vnc.html:231
-msgid "Encrypt"
-msgstr "Şifrele"
-
-#: ../vnc.html:234
-msgid "Host:"
-msgstr "Ana makine:"
-
-#: ../vnc.html:238
-msgid "Port:"
-msgstr "Port:"
-
-#: ../vnc.html:242
-msgid "Path:"
-msgstr "Yol:"
-
-#: ../vnc.html:249
-msgid "Automatic Reconnect"
-msgstr "Otomatik Yeniden Bağlan"
-
-#: ../vnc.html:252
-msgid "Reconnect Delay (ms):"
-msgstr "Yeniden Bağlanma Süreci (ms):"
-
-#: ../vnc.html:258
-msgid "Logging:"
-msgstr "Giriş yapılıyor:"
-
-#: ../vnc.html:270
-msgid "Disconnect"
-msgstr "Bağlantıyı Kes"
-
-#: ../vnc.html:289
-msgid "Connect"
-msgstr "Bağlan"
-
-#: ../vnc.html:299
-msgid "Password:"
-msgstr "Parola:"
-
-#: ../vnc.html:313
-msgid "Cancel"
-msgstr "Vazgeç"
-
-#: ../vnc.html:329
-msgid "Canvas not supported."
-msgstr "Tuval desteklenmiyor."
diff --git a/systemvm/agent/noVNC/po/xgettext-html b/systemvm/agent/noVNC/po/xgettext-html
deleted file mode 100755
index 547f568..0000000
--- a/systemvm/agent/noVNC/po/xgettext-html
+++ /dev/null
@@ -1,115 +0,0 @@
-#!/usr/bin/env node
-/*
- * xgettext-html: HTML gettext parser
- * Copyright (C) 2018 The noVNC Authors
- * Licensed under MPL 2.0 (see LICENSE.txt)
- */
-
-const getopt = require('node-getopt');
-const jsdom = require("jsdom");
-const fs = require("fs");
-
-const opt = getopt.create([
-    ['o' , 'output=FILE'      , 'write output to specified file'],
-    ['h' , 'help'             , 'display this help'],
-]).bindHelp().parseSystem();
-
-const strings = {};
-
-function addString(str, location) {
-    if (str.length == 0) {
-        return;
-    }
-
-    if (strings[str] === undefined) {
-        strings[str] = {}
-    }
-    strings[str][location] = null;
-}
-
-// See https://html.spec.whatwg.org/multipage/dom.html#attr-translate
-function process(elem, locator, enabled) {
-    function isAnyOf(searchElement, items) {
-        return items.indexOf(searchElement) !== -1;
-    }
-
-    if (elem.hasAttribute("translate")) {
-        if (isAnyOf(elem.getAttribute("translate"), ["", "yes"])) {
-            enabled = true;
-        } else if (isAnyOf(elem.getAttribute("translate"), ["no"])) {
-            enabled = false;
-        }
-    }
-
-    if (enabled) {
-        if (elem.hasAttribute("abbr") &&
-            elem.tagName === "TH") {
-            addString(elem.getAttribute("abbr"), locator(elem));
-        }
-        if (elem.hasAttribute("alt") &&
-            isAnyOf(elem.tagName, ["AREA", "IMG", "INPUT"])) {
-            addString(elem.getAttribute("alt"), locator(elem));
-        }
-        if (elem.hasAttribute("download") &&
-            isAnyOf(elem.tagName, ["A", "AREA"])) {
-            addString(elem.getAttribute("download"), locator(elem));
-        }
-        if (elem.hasAttribute("label") &&
-            isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP",
-                                   "OPTION", "TRACK"])) {
-            addString(elem.getAttribute("label"), locator(elem));
-        }
-        if (elem.hasAttribute("placeholder") &&
-            isAnyOf(elem.tagName in ["INPUT", "TEXTAREA"])) {
-            addString(elem.getAttribute("placeholder"), locator(elem));
-        }
-        if (elem.hasAttribute("title")) {
-            addString(elem.getAttribute("title"), locator(elem));
-        }
-        if (elem.hasAttribute("value") &&
-            elem.tagName === "INPUT" &&
-            isAnyOf(elem.getAttribute("type"), ["reset", "button", "submit"])) {
-            addString(elem.getAttribute("value"), locator(elem));
-        }
-    }
-
-    for (let i = 0; i < elem.childNodes.length; i++) {
-        node = elem.childNodes[i];
-        if (node.nodeType === node.ELEMENT_NODE) {
-            process(node, locator, enabled);
-        } else if (node.nodeType === node.TEXT_NODE && enabled) {
-            addString(node.data.trim(), locator(node));
-        }
-    }
-}
-
-for (let i = 0; i < opt.argv.length; i++) {
-    const fn = opt.argv[i];
-    const file = fs.readFileSync(fn, "utf8");
-    const dom = new jsdom.JSDOM(file, { includeNodeLocations: true });
-    const body = dom.window.document.body;
-
-    function locator(elem) {
-        const offset = dom.nodeLocation(elem).startOffset;
-        const line = file.slice(0, offset).split("\n").length;
-        return fn + ":" + line;
-    }
-
-    process(body, locator, true);
-}
-
-let output = "";
-
-for (str in strings) {
-    output += "#:";
-    for (location in strings[str]) {
-        output += " " + location;
-    }
-    output += "\n";
-
-    output += "msgid " + JSON.stringify(str) + "\n";
-    output += "msgstr \"\"\n";
-    output += "\n";
-}
-
-fs.writeFileSync(opt.options.output, output);
diff --git a/systemvm/agent/noVNC/po/zh_CN.po b/systemvm/agent/noVNC/po/zh_CN.po
deleted file mode 100644
index 78bfb95..0000000
--- a/systemvm/agent/noVNC/po/zh_CN.po
+++ /dev/null
@@ -1,284 +0,0 @@
-# Simplified Chinese translations for noVNC package.
-# Copyright (C) 2018 The noVNC Authors
-# This file is distributed under the same license as the noVNC package.
-# Peter Dave Hello <hsu@peterdavehello.org>, 2018.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: noVNC 1.0.0-testing.2\n"
-"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
-"POT-Creation-Date: 2018-01-10 00:53+0800\n"
-"PO-Revision-Date: 2018-04-06 21:33+0800\n"
-"Last-Translator: CUI Wei <ghostplant@qq.com>\n"
-"Language: zh_CN\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: ../app/ui.js:395
-msgid "Connecting..."
-msgstr "链接中..."
-
-#: ../app/ui.js:402
-msgid "Disconnecting..."
-msgstr "正在中断连接..."
-
-#: ../app/ui.js:408
-msgid "Reconnecting..."
-msgstr "重新链接中..."
-
-#: ../app/ui.js:413
-msgid "Internal error"
-msgstr "内部错误"
-
-#: ../app/ui.js:1015
-msgid "Must set host"
-msgstr "请提供主机名"
-
-#: ../app/ui.js:1097
-msgid "Connected (encrypted) to "
-msgstr "已加密链接到"
-
-#: ../app/ui.js:1099
-msgid "Connected (unencrypted) to "
-msgstr "未加密链接到"
-
-#: ../app/ui.js:1120
-msgid "Something went wrong, connection is closed"
-msgstr "发生错误,链接已关闭"
-
-#: ../app/ui.js:1123
-msgid "Failed to connect to server"
-msgstr "无法链接到服务器"
-
-#: ../app/ui.js:1133
-msgid "Disconnected"
-msgstr "链接已中断"
-
-#: ../app/ui.js:1146
-msgid "New connection has been rejected with reason: "
-msgstr "链接被拒绝,原因:"
-
-#: ../app/ui.js:1149
-msgid "New connection has been rejected"
-msgstr "链接被拒绝"
-
-#: ../app/ui.js:1170
-msgid "Password is required"
-msgstr "请提供密码"
-
-#: ../vnc.html:89
-msgid "noVNC encountered an error:"
-msgstr "noVNC 遇到一个错误:"
-
-#: ../vnc.html:99
-msgid "Hide/Show the control bar"
-msgstr "显示/隐藏控制列"
-
-#: ../vnc.html:106
-msgid "Move/Drag Viewport"
-msgstr "拖放显示范围"
-
-#: ../vnc.html:106
-msgid "viewport drag"
-msgstr "显示范围拖放"
-
-#: ../vnc.html:112 ../vnc.html:115 ../vnc.html:118 ../vnc.html:121
-msgid "Active Mouse Button"
-msgstr "启动鼠标按鍵"
-
-#: ../vnc.html:112
-msgid "No mousebutton"
-msgstr "禁用鼠标按鍵"
-
-#: ../vnc.html:115
-msgid "Left mousebutton"
-msgstr "鼠标左鍵"
-
-#: ../vnc.html:118
-msgid "Middle mousebutton"
-msgstr "鼠标中鍵"
-
-#: ../vnc.html:121
-msgid "Right mousebutton"
-msgstr "鼠标右鍵"
-
-#: ../vnc.html:124
-msgid "Keyboard"
-msgstr "键盘"
-
-#: ../vnc.html:124
-msgid "Show Keyboard"
-msgstr "显示键盘"
-
-#: ../vnc.html:131
-msgid "Extra keys"
-msgstr "额外按键"
-
-#: ../vnc.html:131
-msgid "Show Extra Keys"
-msgstr "显示额外按键"
-
-#: ../vnc.html:136
-msgid "Ctrl"
-msgstr "Ctrl"
-
-#: ../vnc.html:136
-msgid "Toggle Ctrl"
-msgstr "切换 Ctrl"
-
-#: ../vnc.html:139
-msgid "Alt"
-msgstr "Alt"
-
-#: ../vnc.html:139
-msgid "Toggle Alt"
-msgstr "切换 Alt"
-
-#: ../vnc.html:142
-msgid "Send Tab"
-msgstr "发送 Tab 键"
-
-#: ../vnc.html:142
-msgid "Tab"
-msgstr "Tab"
-
-#: ../vnc.html:145
-msgid "Esc"
-msgstr "Esc"
-
-#: ../vnc.html:145
-msgid "Send Escape"
-msgstr "发送 Escape 键"
-
-#: ../vnc.html:148
-msgid "Ctrl+Alt+Del"
-msgstr "Ctrl-Alt-Del"
-
-#: ../vnc.html:148
-msgid "Send Ctrl-Alt-Del"
-msgstr "发送 Ctrl-Alt-Del 键"
-
-#: ../vnc.html:156
-msgid "Shutdown/Reboot"
-msgstr "关机/重新启动"
-
-#: ../vnc.html:156
-msgid "Shutdown/Reboot..."
-msgstr "关机/重新启动..."
-
-#: ../vnc.html:162
-msgid "Power"
-msgstr "电源"
-
-#: ../vnc.html:164
-msgid "Shutdown"
-msgstr "关机"
-
-#: ../vnc.html:165
-msgid "Reboot"
-msgstr "重新启动"
-
-#: ../vnc.html:166
-msgid "Reset"
-msgstr "重置"
-
-#: ../vnc.html:171 ../vnc.html:177
-msgid "Clipboard"
-msgstr "剪贴板"
-
-#: ../vnc.html:181
-msgid "Clear"
-msgstr "清除"
-
-#: ../vnc.html:187
-msgid "Fullscreen"
-msgstr "全屏幕"
-
-#: ../vnc.html:192 ../vnc.html:199
-msgid "Settings"
-msgstr "设置"
-
-#: ../vnc.html:202
-msgid "Shared Mode"
-msgstr "分享模式"
-
-#: ../vnc.html:205
-msgid "View Only"
-msgstr "仅检视"
-
-#: ../vnc.html:209
-msgid "Clip to Window"
-msgstr "限制/裁切窗口大小"
-
-#: ../vnc.html:212
-msgid "Scaling Mode:"
-msgstr "缩放模式:"
-
-#: ../vnc.html:214
-msgid "None"
-msgstr "无"
-
-#: ../vnc.html:215
-msgid "Local Scaling"
-msgstr "本地缩放"
-
-#: ../vnc.html:216
-msgid "Remote Resizing"
-msgstr "远程调整大小"
-
-#: ../vnc.html:221
-msgid "Advanced"
-msgstr "高级"
-
-#: ../vnc.html:224
-msgid "Repeater ID:"
-msgstr "中继站 ID"
-
-#: ../vnc.html:228
-msgid "WebSocket"
-msgstr "WebSocket"
-
-#: ../vnc.html:231
-msgid "Encrypt"
-msgstr "加密"
-
-#: ../vnc.html:234
-msgid "Host:"
-msgstr "主机:"
-
-#: ../vnc.html:238
-msgid "Port:"
-msgstr "端口:"
-
-#: ../vnc.html:242
-msgid "Path:"
-msgstr "路径:"
-
-#: ../vnc.html:249
-msgid "Automatic Reconnect"
-msgstr "自动重新链接"
-
-#: ../vnc.html:252
-msgid "Reconnect Delay (ms):"
-msgstr "重新链接间隔 (ms):"
-
-#: ../vnc.html:258
-msgid "Logging:"
-msgstr "日志级别:"
-
-#: ../vnc.html:270
-msgid "Disconnect"
-msgstr "终端链接"
-
-#: ../vnc.html:289
-msgid "Connect"
-msgstr "链接"
-
-#: ../vnc.html:299
-msgid "Password:"
-msgstr "密码:"
-
-#: ../vnc.html:313
-msgid "Cancel"
-msgstr "取消"
diff --git a/systemvm/agent/noVNC/po/zh_TW.po b/systemvm/agent/noVNC/po/zh_TW.po
deleted file mode 100644
index 9ddf550..0000000
--- a/systemvm/agent/noVNC/po/zh_TW.po
+++ /dev/null
@@ -1,285 +0,0 @@
-# Traditional Chinese translations for noVNC package.
-# Copyright (C) 2018 The noVNC Authors
-# This file is distributed under the same license as the noVNC package.
-# Peter Dave Hello <hsu@peterdavehello.org>, 2018.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: noVNC 1.0.0-testing.2\n"
-"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
-"POT-Creation-Date: 2018-01-10 00:53+0800\n"
-"PO-Revision-Date: 2018-01-10 01:33+0800\n"
-"Last-Translator: Peter Dave Hello <hsu@peterdavehello.org>\n"
-"Language-Team: Peter Dave Hello <hsu@peterdavehello.org>\n"
-"Language: zh\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: ../app/ui.js:395
-msgid "Connecting..."
-msgstr "連線中..."
-
-#: ../app/ui.js:402
-msgid "Disconnecting..."
-msgstr "正在中斷連線..."
-
-#: ../app/ui.js:408
-msgid "Reconnecting..."
-msgstr "重新連線中..."
-
-#: ../app/ui.js:413
-msgid "Internal error"
-msgstr "內部錯誤"
-
-#: ../app/ui.js:1015
-msgid "Must set host"
-msgstr "請提供主機資訊"
-
-#: ../app/ui.js:1097
-msgid "Connected (encrypted) to "
-msgstr "已加密連線到"
-
-#: ../app/ui.js:1099
-msgid "Connected (unencrypted) to "
-msgstr "未加密連線到"
-
-#: ../app/ui.js:1120
-msgid "Something went wrong, connection is closed"
-msgstr "發生錯誤,連線已關閉"
-
-#: ../app/ui.js:1123
-msgid "Failed to connect to server"
-msgstr "無法連線到伺服器"
-
-#: ../app/ui.js:1133
-msgid "Disconnected"
-msgstr "連線已中斷"
-
-#: ../app/ui.js:1146
-msgid "New connection has been rejected with reason: "
-msgstr "連線被拒絕,原因:"
-
-#: ../app/ui.js:1149
-msgid "New connection has been rejected"
-msgstr "連線被拒絕"
-
-#: ../app/ui.js:1170
-msgid "Password is required"
-msgstr "請提供密碼"
-
-#: ../vnc.html:89
-msgid "noVNC encountered an error:"
-msgstr "noVNC 遇到一個錯誤:"
-
-#: ../vnc.html:99
-msgid "Hide/Show the control bar"
-msgstr "顯示/隱藏控制列"
-
-#: ../vnc.html:106
-msgid "Move/Drag Viewport"
-msgstr "拖放顯示範圍"
-
-#: ../vnc.html:106
-msgid "viewport drag"
-msgstr "顯示範圍拖放"
-
-#: ../vnc.html:112 ../vnc.html:115 ../vnc.html:118 ../vnc.html:121
-msgid "Active Mouse Button"
-msgstr "啟用滑鼠按鍵"
-
-#: ../vnc.html:112
-msgid "No mousebutton"
-msgstr "無滑鼠按鍵"
-
-#: ../vnc.html:115
-msgid "Left mousebutton"
-msgstr "滑鼠左鍵"
-
-#: ../vnc.html:118
-msgid "Middle mousebutton"
-msgstr "滑鼠中鍵"
-
-#: ../vnc.html:121
-msgid "Right mousebutton"
-msgstr "滑鼠右鍵"
-
-#: ../vnc.html:124
-msgid "Keyboard"
-msgstr "鍵盤"
-
-#: ../vnc.html:124
-msgid "Show Keyboard"
-msgstr "顯示鍵盤"
-
-#: ../vnc.html:131
-msgid "Extra keys"
-msgstr "額外按鍵"
-
-#: ../vnc.html:131
-msgid "Show Extra Keys"
-msgstr "顯示額外按鍵"
-
-#: ../vnc.html:136
-msgid "Ctrl"
-msgstr "Ctrl"
-
-#: ../vnc.html:136
-msgid "Toggle Ctrl"
-msgstr "切換 Ctrl"
-
-#: ../vnc.html:139
-msgid "Alt"
-msgstr "Alt"
-
-#: ../vnc.html:139
-msgid "Toggle Alt"
-msgstr "切換 Alt"
-
-#: ../vnc.html:142
-msgid "Send Tab"
-msgstr "送出 Tab 鍵"
-
-#: ../vnc.html:142
-msgid "Tab"
-msgstr "Tab"
-
-#: ../vnc.html:145
-msgid "Esc"
-msgstr "Esc"
-
-#: ../vnc.html:145
-msgid "Send Escape"
-msgstr "送出 Escape 鍵"
-
-#: ../vnc.html:148
-msgid "Ctrl+Alt+Del"
-msgstr "Ctrl-Alt-Del"
-
-#: ../vnc.html:148
-msgid "Send Ctrl-Alt-Del"
-msgstr "送出 Ctrl-Alt-Del 快捷鍵"
-
-#: ../vnc.html:156
-msgid "Shutdown/Reboot"
-msgstr "關機/重新啟動"
-
-#: ../vnc.html:156
-msgid "Shutdown/Reboot..."
-msgstr "關機/重新啟動..."
-
-#: ../vnc.html:162
-msgid "Power"
-msgstr "電源"
-
-#: ../vnc.html:164
-msgid "Shutdown"
-msgstr "關機"
-
-#: ../vnc.html:165
-msgid "Reboot"
-msgstr "重新啟動"
-
-#: ../vnc.html:166
-msgid "Reset"
-msgstr "重設"
-
-#: ../vnc.html:171 ../vnc.html:177
-msgid "Clipboard"
-msgstr "剪貼簿"
-
-#: ../vnc.html:181
-msgid "Clear"
-msgstr "清除"
-
-#: ../vnc.html:187
-msgid "Fullscreen"
-msgstr "全螢幕"
-
-#: ../vnc.html:192 ../vnc.html:199
-msgid "Settings"
-msgstr "設定"
-
-#: ../vnc.html:202
-msgid "Shared Mode"
-msgstr "分享模式"
-
-#: ../vnc.html:205
-msgid "View Only"
-msgstr "僅檢視"
-
-#: ../vnc.html:209
-msgid "Clip to Window"
-msgstr "限制/裁切視窗大小"
-
-#: ../vnc.html:212
-msgid "Scaling Mode:"
-msgstr "縮放模式:"
-
-#: ../vnc.html:214
-msgid "None"
-msgstr "無"
-
-#: ../vnc.html:215
-msgid "Local Scaling"
-msgstr "本機縮放"
-
-#: ../vnc.html:216
-msgid "Remote Resizing"
-msgstr "遠端調整大小"
-
-#: ../vnc.html:221
-msgid "Advanced"
-msgstr "進階"
-
-#: ../vnc.html:224
-msgid "Repeater ID:"
-msgstr "中繼站 ID"
-
-#: ../vnc.html:228
-msgid "WebSocket"
-msgstr "WebSocket"
-
-#: ../vnc.html:231
-msgid "Encrypt"
-msgstr "加密"
-
-#: ../vnc.html:234
-msgid "Host:"
-msgstr "主機:"
-
-#: ../vnc.html:238
-msgid "Port:"
-msgstr "連接埠:"
-
-#: ../vnc.html:242
-msgid "Path:"
-msgstr "路徑:"
-
-#: ../vnc.html:249
-msgid "Automatic Reconnect"
-msgstr "自動重新連線"
-
-#: ../vnc.html:252
-msgid "Reconnect Delay (ms):"
-msgstr "重新連線間隔 (ms):"
-
-#: ../vnc.html:258
-msgid "Logging:"
-msgstr "日誌級別:"
-
-#: ../vnc.html:270
-msgid "Disconnect"
-msgstr "中斷連線"
-
-#: ../vnc.html:289
-msgid "Connect"
-msgstr "連線"
-
-#: ../vnc.html:299
-msgid "Password:"
-msgstr "密碼:"
-
-#: ../vnc.html:313
-msgid "Cancel"
-msgstr "取消"
diff --git a/systemvm/agent/noVNC/tests/.eslintrc b/systemvm/agent/noVNC/tests/.eslintrc
deleted file mode 100644
index 545fa2e..0000000
--- a/systemvm/agent/noVNC/tests/.eslintrc
+++ /dev/null
@@ -1,15 +0,0 @@
-{
-    "env": {
-        "node": true,
-        "mocha": true
-    },
-    "globals": {
-        "chai": false,
-        "sinon": false
-    },
-    "rules": {
-        "prefer-arrow-callback": 0,
-        // Too many anonymous callbacks
-        "func-names": "off",
-    }
-}
diff --git a/systemvm/agent/noVNC/tests/assertions.js b/systemvm/agent/noVNC/tests/assertions.js
deleted file mode 100644
index 07a5c29..0000000
--- a/systemvm/agent/noVNC/tests/assertions.js
+++ /dev/null
@@ -1,101 +0,0 @@
-// noVNC specific assertions
-chai.use(function (_chai, utils) {
-    _chai.Assertion.addMethod('displayed', function (target_data) {
-        const obj = this._obj;
-        const ctx = obj._target.getContext('2d');
-        const data_cl = ctx.getImageData(0, 0, obj._target.width, obj._target.height).data;
-        // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray, so work around that
-        const data = new Uint8Array(data_cl);
-        const len = data_cl.length;
-        new chai.Assertion(len).to.be.equal(target_data.length, "unexpected display size");
-        let same = true;
-        for (let i = 0; i < len; i++) {
-            if (data[i] != target_data[i]) {
-                same = false;
-                break;
-            }
-        }
-        if (!same) {
-            // eslint-disable-next-line no-console
-            console.log("expected data: %o, actual data: %o", target_data, data);
-        }
-        this.assert(same,
-                    "expected #{this} to have displayed the image #{exp}, but instead it displayed #{act}",
-                    "expected #{this} not to have displayed the image #{act}",
-                    target_data,
-                    data);
-    });
-
-    _chai.Assertion.addMethod('sent', function (target_data) {
-        const obj = this._obj;
-        obj.inspect = () => {
-            const res = { _websocket: obj._websocket, rQi: obj._rQi, _rQ: new Uint8Array(obj._rQ.buffer, 0, obj._rQlen),
-                          _sQ: new Uint8Array(obj._sQ.buffer, 0, obj._sQlen) };
-            res.prototype = obj;
-            return res;
-        };
-        const data = obj._websocket._get_sent_data();
-        let same = true;
-        if (data.length != target_data.length) {
-            same = false;
-        } else {
-            for (let i = 0; i < data.length; i++) {
-                if (data[i] != target_data[i]) {
-                    same = false;
-                    break;
-                }
-            }
-        }
-        if (!same) {
-            // eslint-disable-next-line no-console
-            console.log("expected data: %o, actual data: %o", target_data, data);
-        }
-        this.assert(same,
-                    "expected #{this} to have sent the data #{exp}, but it actually sent #{act}",
-                    "expected #{this} not to have sent the data #{act}",
-                    Array.prototype.slice.call(target_data),
-                    Array.prototype.slice.call(data));
-    });
-
-    _chai.Assertion.addProperty('array', function () {
-        utils.flag(this, 'array', true);
-    });
-
-    _chai.Assertion.overwriteMethod('equal', function (_super) {
-        return function assertArrayEqual(target) {
-            if (utils.flag(this, 'array')) {
-                const obj = this._obj;
-
-                let same = true;
-
-                if (utils.flag(this, 'deep')) {
-                    for (let i = 0; i < obj.length; i++) {
-                        if (!utils.eql(obj[i], target[i])) {
-                            same = false;
-                            break;
-                        }
-                    }
-
-                    this.assert(same,
-                                "expected #{this} to have elements deeply equal to #{exp}",
-                                "expected #{this} not to have elements deeply equal to #{exp}",
-                                Array.prototype.slice.call(target));
-                } else {
-                    for (let i = 0; i < obj.length; i++) {
-                        if (obj[i] != target[i]) {
-                            same = false;
-                            break;
-                        }
-                    }
-
-                    this.assert(same,
-                                "expected #{this} to have elements equal to #{exp}",
-                                "expected #{this} not to have elements equal to #{exp}",
-                                Array.prototype.slice.call(target));
-                }
-            } else {
-                _super.apply(this, arguments);
-            }
-        };
-    });
-});
diff --git a/systemvm/agent/noVNC/tests/fake.websocket.js b/systemvm/agent/noVNC/tests/fake.websocket.js
deleted file mode 100644
index 68ab3f8..0000000
--- a/systemvm/agent/noVNC/tests/fake.websocket.js
+++ /dev/null
@@ -1,96 +0,0 @@
-import Base64 from '../core/base64.js';
-
-// PhantomJS can't create Event objects directly, so we need to use this
-function make_event(name, props) {
-    const evt = document.createEvent('Event');
-    evt.initEvent(name, true, true);
-    if (props) {
-        for (let prop in props) {
-            evt[prop] = props[prop];
-        }
-    }
-    return evt;
-}
-
-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];
-        }
-
-        this._send_queue = new Uint8Array(20000);
-
-        this.readyState = FakeWebSocket.CONNECTING;
-        this.bufferedAmount = 0;
-
-        this.__is_fake = true;
-    }
-
-    close(code, reason) {
-        this.readyState = FakeWebSocket.CLOSED;
-        if (this.onclose) {
-            this.onclose(make_event("close", { 'code': code, 'reason': reason, 'wasClean': true }));
-        }
-    }
-
-    send(data) {
-        if (this.protocol == 'base64') {
-            data = Base64.decode(data);
-        } else {
-            data = new Uint8Array(data);
-        }
-        this._send_queue.set(data, this.bufferedAmount);
-        this.bufferedAmount += data.length;
-    }
-
-    _get_sent_data() {
-        const res = new Uint8Array(this._send_queue.buffer, 0, this.bufferedAmount);
-        this.bufferedAmount = 0;
-        return res;
-    }
-
-    _open() {
-        this.readyState = FakeWebSocket.OPEN;
-        if (this.onopen) {
-            this.onopen(make_event('open'));
-        }
-    }
-
-    _receive_data(data) {
-        // Break apart the data to expose bugs where we assume data is
-        // neatly packaged
-        for (let i = 0;i < data.length;i++) {
-            let buf = data.subarray(i, i+1);
-            this.onmessage(make_event("message", { 'data': buf }));
-        }
-    }
-}
-
-FakeWebSocket.OPEN = WebSocket.OPEN;
-FakeWebSocket.CONNECTING = WebSocket.CONNECTING;
-FakeWebSocket.CLOSING = WebSocket.CLOSING;
-FakeWebSocket.CLOSED = WebSocket.CLOSED;
-
-FakeWebSocket.__is_fake = true;
-
-FakeWebSocket.replace = () => {
-    if (!WebSocket.__is_fake) {
-        const real_version = WebSocket;
-        // eslint-disable-next-line no-global-assign
-        WebSocket = FakeWebSocket;
-        FakeWebSocket.__real_version = real_version;
-    }
-};
-
-FakeWebSocket.restore = () => {
-    if (WebSocket.__is_fake) {
-        // eslint-disable-next-line no-global-assign
-        WebSocket = WebSocket.__real_version;
-    }
-};
diff --git a/systemvm/agent/noVNC/tests/karma-test-main.js b/systemvm/agent/noVNC/tests/karma-test-main.js
deleted file mode 100644
index 2843666..0000000
--- a/systemvm/agent/noVNC/tests/karma-test-main.js
+++ /dev/null
@@ -1,48 +0,0 @@
-const TEST_REGEXP = /test\..*\.js/;
-const allTestFiles = [];
-const extraFiles = ['/base/tests/assertions.js'];
-
-Object.keys(window.__karma__.files).forEach(function (file) {
-    if (TEST_REGEXP.test(file)) {
-        // TODO: normalize?
-        allTestFiles.push(file);
-    }
-});
-
-// Stub out mocha's start function so we can run it once we're done loading
-mocha.origRun = mocha.run;
-mocha.run = function () {};
-
-let script;
-
-// Script to import all our tests
-script = document.createElement("script");
-script.type = "module";
-script.text = "";
-let allModules = allTestFiles.concat(extraFiles);
-allModules.forEach(function (file) {
-    script.text += "import \"" + file + "\";\n";
-});
-script.text += "\nmocha.origRun();\n";
-document.body.appendChild(script);
-
-// Fallback code for browsers that don't support modules (IE)
-script = document.createElement("script");
-script.type = "module";
-script.text = "window._noVNC_has_module_support = true;\n";
-document.body.appendChild(script);
-
-function fallback() {
-    if (!window._noVNC_has_module_support) {
-        /* eslint-disable no-console */
-        if (console) {
-            console.log("No module support detected. Loading fallback...");
-        }
-        /* eslint-enable no-console */
-        let loader = document.createElement("script");
-        loader.src = "base/vendor/browser-es-module-loader/dist/browser-es-module-loader.js";
-        document.body.appendChild(loader);
-    }
-}
-
-setTimeout(fallback, 500);
diff --git a/systemvm/agent/noVNC/tests/playback-ui.js b/systemvm/agent/noVNC/tests/playback-ui.js
deleted file mode 100644
index 65c715a..0000000
--- a/systemvm/agent/noVNC/tests/playback-ui.js
+++ /dev/null
@@ -1,210 +0,0 @@
-/* global VNC_frame_data, VNC_frame_encoding */
-
-import * as WebUtil from '../app/webutil.js';
-import RecordingPlayer from './playback.js';
-import Base64 from '../core/base64.js';
-
-let frames = null;
-
-function message(str) {
-    const cell = document.getElementById('messages');
-    cell.textContent += str + "\n";
-    cell.scrollTop = cell.scrollHeight;
-}
-
-function loadFile() {
-    const fname = WebUtil.getQueryVar('data', null);
-
-    if (!fname) {
-        return Promise.reject("Must specify data=FOO in query string.");
-    }
-
-    message("Loading " + fname + "...");
-
-    return new Promise((resolve, reject) => {
-        const script = document.createElement("script");
-        script.onload = resolve;
-        script.onerror = reject;
-        document.body.appendChild(script);
-        script.src = "../recordings/" + fname;
-    });
-}
-
-function enableUI() {
-    const iterations = WebUtil.getQueryVar('iterations', 3);
-    document.getElementById('iterations').value = iterations;
-
-    const mode = WebUtil.getQueryVar('mode', 3);
-    if (mode === 'realtime') {
-        document.getElementById('mode2').checked = true;
-    } else {
-        document.getElementById('mode1').checked = true;
-    }
-
-    message("Loaded " + VNC_frame_data.length + " frames");
-
-    const startButton = document.getElementById('startButton');
-    startButton.disabled = false;
-    startButton.addEventListener('click', start);
-
-    message("Converting...");
-
-    frames = VNC_frame_data;
-
-    let encoding;
-    // Only present in older recordings
-    if (window.VNC_frame_encoding) {
-        encoding = VNC_frame_encoding;
-    } else {
-        let frame = frames[0];
-        let start = frame.indexOf('{', 1) + 1;
-        if (frame.slice(start, start+4) === 'UkZC') {
-            encoding = 'base64';
-        } else {
-            encoding = 'binary';
-        }
-    }
-
-    for (let i = 0;i < frames.length;i++) {
-        let frame = frames[i];
-
-        if (frame === "EOF") {
-            frames.splice(i);
-            break;
-        }
-
-        let dataIdx = frame.indexOf('{', 1) + 1;
-
-        let time = parseInt(frame.slice(1, dataIdx - 1));
-
-        let u8;
-        if (encoding === 'base64') {
-            u8 = Base64.decode(frame.slice(dataIdx));
-        } else {
-            u8 = new Uint8Array(frame.length - dataIdx);
-            for (let j = 0; j < frame.length - dataIdx; j++) {
-                u8[j] = frame.charCodeAt(dataIdx + j);
-            }
-        }
-
-        frames[i] = { fromClient: frame[0] === '}',
-                      timestamp: time,
-                      data: u8 };
-    }
-
-    message("Ready");
-}
-
-class IterationPlayer {
-    constructor(iterations, frames) {
-        this._iterations = iterations;
-
-        this._iteration = undefined;
-        this._player = undefined;
-
-        this._start_time = undefined;
-
-        this._frames = frames;
-
-        this._state = 'running';
-
-        this.onfinish = () => {};
-        this.oniterationfinish = () => {};
-        this.rfbdisconnected = () => {};
-    }
-
-    start(realtime) {
-        this._iteration = 0;
-        this._start_time = (new Date()).getTime();
-
-        this._realtime = realtime;
-
-        this._nextIteration();
-    }
-
-    _nextIteration() {
-        const player = new RecordingPlayer(this._frames, this._disconnected.bind(this));
-        player.onfinish = this._iterationFinish.bind(this);
-
-        if (this._state !== 'running') { return; }
-
-        this._iteration++;
-        if (this._iteration > this._iterations) {
-            this._finish();
-            return;
-        }
-
-        player.run(this._realtime, false);
-    }
-
-    _finish() {
-        const endTime = (new Date()).getTime();
-        const totalDuration = endTime - this._start_time;
-
-        const evt = new CustomEvent('finish',
-                                    { detail:
-                                      { duration: totalDuration,
-                                        iterations: this._iterations } } );
-        this.onfinish(evt);
-    }
-
-    _iterationFinish(duration) {
-        const evt = new CustomEvent('iterationfinish',
-                                    { detail:
-                                      { duration: duration,
-                                        number: this._iteration } } );
-        this.oniterationfinish(evt);
-
-        this._nextIteration();
-    }
-
-    _disconnected(clean, frame) {
-        if (!clean) {
-            this._state = 'failed';
-        }
-
-        const evt = new CustomEvent('rfbdisconnected',
-                                    { detail:
-                                      { clean: clean,
-                                        frame: frame,
-                                        iteration: this._iteration } } );
-        this.onrfbdisconnected(evt);
-    }
-}
-
-function start() {
-    document.getElementById('startButton').value = "Running";
-    document.getElementById('startButton').disabled = true;
-
-    const iterations = document.getElementById('iterations').value;
-
-    let realtime;
-
-    if (document.getElementById('mode1').checked) {
-        message(`Starting performance playback (fullspeed) [${iterations} iteration(s)]`);
-        realtime = false;
-    } else {
-        message(`Starting realtime playback [${iterations} iteration(s)]`);
-        realtime = true;
-    }
-
-    const player = new IterationPlayer(iterations, frames);
-    player.oniterationfinish = (evt) => {
-        message(`Iteration ${evt.detail.number} took ${evt.detail.duration}ms`);
-    };
-    player.onrfbdisconnected = (evt) => {
-        if (!evt.detail.clean) {
-            message(`noVNC sent disconnected during iteration ${evt.detail.iteration} frame ${evt.detail.frame}`);
-        }
-    };
-    player.onfinish = (evt) => {
-        const iterTime = parseInt(evt.detail.duration / evt.detail.iterations, 10);
-        message(`${evt.detail.iterations} iterations took ${evt.detail.duration}ms (average ${iterTime}ms / iteration)`);
-
-        document.getElementById('startButton').disabled = false;
-        document.getElementById('startButton').value = "Start";
-    };
-    player.start(realtime);
-}
-
-loadFile().then(enableUI).catch(e => message("Error loading recording: " + e));
diff --git a/systemvm/agent/noVNC/tests/playback.js b/systemvm/agent/noVNC/tests/playback.js
deleted file mode 100644
index 5bd8103..0000000
--- a/systemvm/agent/noVNC/tests/playback.js
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * noVNC: HTML5 VNC client
- * Copyright (C) 2018 The noVNC Authors
- * Licensed under MPL 2.0 (see LICENSE.txt)
- */
-
-import RFB from '../core/rfb.js';
-import * as Log from '../core/util/logging.js';
-
-// Immediate polyfill
-if (window.setImmediate === undefined) {
-    let _immediateIdCounter = 1;
-    const _immediateFuncs = {};
-
-    window.setImmediate = (func) => {
-        const index = _immediateIdCounter++;
-        _immediateFuncs[index] = func;
-        window.postMessage("noVNC immediate trigger:" + index, "*");
-        return index;
-    };
-
-    window.clearImmediate = (id) => {
-        _immediateFuncs[id];
-    };
-
-    window.addEventListener("message", (event) => {
-        if ((typeof event.data !== "string") ||
-            (event.data.indexOf("noVNC immediate trigger:") !== 0)) {
-            return;
-        }
-
-        const index = event.data.slice("noVNC immediate trigger:".length);
-
-        const callback = _immediateFuncs[index];
-        if (callback === undefined) {
-            return;
-        }
-
-        delete _immediateFuncs[index];
-
-        callback();
-    });
-}
-
-export default class RecordingPlayer {
-    constructor(frames, disconnected) {
-        this._frames = frames;
-
-        this._disconnected = disconnected;
-
-        this._rfb = undefined;
-        this._frame_length = this._frames.length;
-
-        this._frame_index = 0;
-        this._start_time = undefined;
-        this._realtime = true;
-        this._trafficManagement = true;
-
-        this._running = false;
-
-        this.onfinish = () => {};
-    }
-
-    run(realtime, trafficManagement) {
-        // initialize a new RFB
-        this._rfb = new RFB(document.getElementById('VNC_screen'), 'wss://test');
-        this._rfb.viewOnly = true;
-        this._rfb.addEventListener("disconnect",
-                                   this._handleDisconnect.bind(this));
-        this._rfb.addEventListener("credentialsrequired",
-                                   this._handleCredentials.bind(this));
-        this._enablePlaybackMode();
-
-        // reset the frame index and timer
-        this._frame_index = 0;
-        this._start_time = (new Date()).getTime();
-
-        this._realtime = realtime;
-        this._trafficManagement = (trafficManagement === undefined) ? !realtime : trafficManagement;
-
-        this._running = true;
-    }
-
-    // _enablePlaybackMode mocks out things not required for running playback
-    _enablePlaybackMode() {
-        const self = this;
-        this._rfb._sock.send = () => {};
-        this._rfb._sock.close = () => {};
-        this._rfb._sock.flush = () => {};
-        this._rfb._sock.open = function () {
-            this.init();
-            this._eventHandlers.open();
-            self._queueNextPacket();
-        };
-    }
-
-    _queueNextPacket() {
-        if (!this._running) { return; }
-
-        let frame = this._frames[this._frame_index];
-
-        // skip send frames
-        while (this._frame_index < this._frame_length && frame.fromClient) {
-            this._frame_index++;
-            frame = this._frames[this._frame_index];
-        }
-
-        if (this._frame_index >= this._frame_length) {
-            Log.Debug('Finished, no more frames');
-            this._finish();
-            return;
-        }
-
-        if (this._realtime) {
-            const toffset = (new Date()).getTime() - this._start_time;
-            let delay = frame.timestamp - toffset;
-            if (delay < 1) delay = 1;
-
-            setTimeout(this._doPacket.bind(this), delay);
-        } else {
-            setImmediate(this._doPacket.bind(this));
-        }
-    }
-
-    _doPacket() {
-        // Avoid having excessive queue buildup in non-realtime mode
-        if (this._trafficManagement && this._rfb._flushing) {
-            const orig = this._rfb._display.onflush;
-            this._rfb._display.onflush = () => {
-                this._rfb._display.onflush = orig;
-                this._rfb._onFlush();
-                this._doPacket();
-            };
-            return;
-        }
-
-        const frame = this._frames[this._frame_index];
-
-        this._rfb._sock._recv_message({'data': frame.data});
-        this._frame_index++;
-
-        this._queueNextPacket();
-    }
-
-    _finish() {
-        if (this._rfb._display.pending()) {
-            this._rfb._display.onflush = () => {
-                if (this._rfb._flushing) {
-                    this._rfb._onFlush();
-                }
-                this._finish();
-            };
-            this._rfb._display.flush();
-        } else {
-            this._running = false;
-            this._rfb._sock._eventHandlers.close({code: 1000, reason: ""});
-            delete this._rfb;
-            this.onfinish((new Date()).getTime() - this._start_time);
-        }
-    }
-
-    _handleDisconnect(evt) {
-        this._running = false;
-        this._disconnected(evt.detail.clean, this._frame_index);
-    }
-
-    _handleCredentials(evt) {
-        this._rfb.sendCredentials({"username": "Foo",
-                                   "password": "Bar",
-                                   "target": "Baz"});
-    }
-}
diff --git a/systemvm/agent/noVNC/tests/test.base64.js b/systemvm/agent/noVNC/tests/test.base64.js
deleted file mode 100644
index 04bd207..0000000
--- a/systemvm/agent/noVNC/tests/test.base64.js
+++ /dev/null
@@ -1,33 +0,0 @@
-const expect = chai.expect;
-
-import Base64 from '../core/base64.js';
-
-describe('Base64 Tools', function () {
-    "use strict";
-
-    const BIN_ARR = new Array(256);
-    for (let i = 0; i < 256; i++) {
-        BIN_ARR[i] = i;
-    }
-
-    const B64_STR = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==";
-
-
-    describe('encode', function () {
-        it('should encode a binary string into Base64', function () {
-            const encoded = Base64.encode(BIN_ARR);
-            expect(encoded).to.equal(B64_STR);
-        });
-    });
-
-    describe('decode', function () {
-        it('should decode a Base64 string into a normal string', function () {
-            const decoded = Base64.decode(B64_STR);
-            expect(decoded).to.deep.equal(BIN_ARR);
-        });
-
-        it('should throw an error if we have extra characters at the end of the string', function () {
-            expect(() => Base64.decode(B64_STR+'abcdef')).to.throw(Error);
-        });
-    });
-});
diff --git a/systemvm/agent/noVNC/tests/test.display.js b/systemvm/agent/noVNC/tests/test.display.js
deleted file mode 100644
index b359550..0000000
--- a/systemvm/agent/noVNC/tests/test.display.js
+++ /dev/null
@@ -1,486 +0,0 @@
-const expect = chai.expect;
-
-import Base64 from '../core/base64.js';
-import Display from '../core/display.js';
-
-describe('Display/Canvas Helper', function () {
-    const checked_data = new Uint8Array([
-        0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
-        0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
-        0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
-        0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
-    ]);
-
-    const basic_data = new Uint8Array([0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0xff, 0xff, 0xff, 255]);
-
-    function make_image_canvas(input_data) {
-        const canvas = document.createElement('canvas');
-        canvas.width = 4;
-        canvas.height = 4;
-        const ctx = canvas.getContext('2d');
-        const data = ctx.createImageData(4, 4);
-        for (let i = 0; i < checked_data.length; i++) { data.data[i] = input_data[i]; }
-        ctx.putImageData(data, 0, 0);
-        return canvas;
-    }
-
-    function make_image_png(input_data) {
-        const canvas = make_image_canvas(input_data);
-        const url = canvas.toDataURL();
-        const data = url.split(",")[1];
-        return Base64.decode(data);
-    }
-
-    describe('viewport handling', function () {
-        let display;
-        beforeEach(function () {
-            display = new Display(document.createElement('canvas'));
-            display.clipViewport = true;
-            display.resize(5, 5);
-            display.viewportChangeSize(3, 3);
-            display.viewportChangePos(1, 1);
-        });
-
-        it('should take viewport location into consideration when drawing images', function () {
-            display.resize(4, 4);
-            display.viewportChangeSize(2, 2);
-            display.drawImage(make_image_canvas(basic_data), 1, 1);
-            display.flip();
-
-            const expected = new Uint8Array(16);
-            for (let i = 0; i < 8; i++) { expected[i] = basic_data[i]; }
-            for (let i = 8; i < 16; i++) { expected[i] = 0; }
-            expect(display).to.have.displayed(expected);
-        });
-
-        it('should resize the target canvas when resizing the viewport', function () {
-            display.viewportChangeSize(2, 2);
-            expect(display._target.width).to.equal(2);
-            expect(display._target.height).to.equal(2);
-        });
-
-        it('should move the viewport if necessary', function () {
-            display.viewportChangeSize(5, 5);
-            expect(display.absX(0)).to.equal(0);
-            expect(display.absY(0)).to.equal(0);
-            expect(display._target.width).to.equal(5);
-            expect(display._target.height).to.equal(5);
-        });
-
-        it('should limit the viewport to the framebuffer size', function () {
-            display.viewportChangeSize(6, 6);
-            expect(display._target.width).to.equal(5);
-            expect(display._target.height).to.equal(5);
-        });
-
-        it('should redraw when moving the viewport', function () {
-            display.flip = sinon.spy();
-            display.viewportChangePos(-1, 1);
-            expect(display.flip).to.have.been.calledOnce;
-        });
-
-        it('should redraw when resizing the viewport', function () {
-            display.flip = sinon.spy();
-            display.viewportChangeSize(2, 2);
-            expect(display.flip).to.have.been.calledOnce;
-        });
-
-        it('should show the entire framebuffer when disabling the viewport', function () {
-            display.clipViewport = false;
-            expect(display.absX(0)).to.equal(0);
-            expect(display.absY(0)).to.equal(0);
-            expect(display._target.width).to.equal(5);
-            expect(display._target.height).to.equal(5);
-        });
-
-        it('should ignore viewport changes when the viewport is disabled', function () {
-            display.clipViewport = false;
-            display.viewportChangeSize(2, 2);
-            display.viewportChangePos(1, 1);
-            expect(display.absX(0)).to.equal(0);
-            expect(display.absY(0)).to.equal(0);
-            expect(display._target.width).to.equal(5);
-            expect(display._target.height).to.equal(5);
-        });
-
-        it('should show the entire framebuffer just after enabling the viewport', function () {
-            display.clipViewport = false;
-            display.clipViewport = true;
-            expect(display.absX(0)).to.equal(0);
-            expect(display.absY(0)).to.equal(0);
-            expect(display._target.width).to.equal(5);
-            expect(display._target.height).to.equal(5);
-        });
-    });
-
-    describe('resizing', function () {
-        let display;
-        beforeEach(function () {
-            display = new Display(document.createElement('canvas'));
-            display.clipViewport = false;
-            display.resize(4, 4);
-        });
-
-        it('should change the size of the logical canvas', function () {
-            display.resize(5, 7);
-            expect(display._fb_width).to.equal(5);
-            expect(display._fb_height).to.equal(7);
-        });
-
-        it('should keep the framebuffer data', function () {
-            display.fillRect(0, 0, 4, 4, [0, 0, 0xff]);
-            display.resize(2, 2);
-            display.flip();
-            const expected = [];
-            for (let i = 0; i < 4 * 2*2; i += 4) {
-                expected[i] = 0xff;
-                expected[i+1] = expected[i+2] = 0;
-                expected[i+3] = 0xff;
-            }
-            expect(display).to.have.displayed(new Uint8Array(expected));
-        });
-
-        describe('viewport', function () {
-            beforeEach(function () {
-                display.clipViewport = true;
-                display.viewportChangeSize(3, 3);
-                display.viewportChangePos(1, 1);
-            });
-
-            it('should keep the viewport position and size if possible', function () {
-                display.resize(6, 6);
-                expect(display.absX(0)).to.equal(1);
-                expect(display.absY(0)).to.equal(1);
-                expect(display._target.width).to.equal(3);
-                expect(display._target.height).to.equal(3);
-            });
-
-            it('should move the viewport if necessary', function () {
-                display.resize(3, 3);
-                expect(display.absX(0)).to.equal(0);
-                expect(display.absY(0)).to.equal(0);
-                expect(display._target.width).to.equal(3);
-                expect(display._target.height).to.equal(3);
-            });
-
-            it('should shrink the viewport if necessary', function () {
-                display.resize(2, 2);
-                expect(display.absX(0)).to.equal(0);
-                expect(display.absY(0)).to.equal(0);
-                expect(display._target.width).to.equal(2);
-                expect(display._target.height).to.equal(2);
-            });
-        });
-    });
-
-    describe('rescaling', function () {
-        let display;
-        let canvas;
-
-        beforeEach(function () {
-            canvas = document.createElement('canvas');
-            display = new Display(canvas);
-            display.clipViewport = true;
-            display.resize(4, 4);
-            display.viewportChangeSize(3, 3);
-            display.viewportChangePos(1, 1);
-            document.body.appendChild(canvas);
-        });
-
-        afterEach(function () {
-            document.body.removeChild(canvas);
-        });
-
-        it('should not change the bitmap size of the canvas', function () {
-            display.scale = 2.0;
-            expect(canvas.width).to.equal(3);
-            expect(canvas.height).to.equal(3);
-        });
-
-        it('should change the effective rendered size of the canvas', function () {
-            display.scale = 2.0;
-            expect(canvas.clientWidth).to.equal(6);
-            expect(canvas.clientHeight).to.equal(6);
-        });
-
-        it('should not change when resizing', function () {
-            display.scale = 2.0;
-            display.resize(5, 5);
-            expect(display.scale).to.equal(2.0);
-            expect(canvas.width).to.equal(3);
-            expect(canvas.height).to.equal(3);
-            expect(canvas.clientWidth).to.equal(6);
-            expect(canvas.clientHeight).to.equal(6);
-        });
-    });
-
-    describe('autoscaling', function () {
-        let display;
-        let canvas;
-
-        beforeEach(function () {
-            canvas = document.createElement('canvas');
-            display = new Display(canvas);
-            display.clipViewport = true;
-            display.resize(4, 3);
-            document.body.appendChild(canvas);
-        });
-
-        afterEach(function () {
-            document.body.removeChild(canvas);
-        });
-
-        it('should preserve aspect ratio while autoscaling', function () {
-            display.autoscale(16, 9);
-            expect(canvas.clientWidth / canvas.clientHeight).to.equal(4 / 3);
-        });
-
-        it('should use width to determine scale when the current aspect ratio is wider than the target', function () {
-            display.autoscale(9, 16);
-            expect(display.absX(9)).to.equal(4);
-            expect(display.absY(18)).to.equal(8);
-            expect(canvas.clientWidth).to.equal(9);
-            expect(canvas.clientHeight).to.equal(7); // round 9 / (4 / 3)
-        });
-
-        it('should use height to determine scale when the current aspect ratio is taller than the target', function () {
-            display.autoscale(16, 9);
-            expect(display.absX(9)).to.equal(3);
-            expect(display.absY(18)).to.equal(6);
-            expect(canvas.clientWidth).to.equal(12);  // 16 * (4 / 3)
-            expect(canvas.clientHeight).to.equal(9);
-
-        });
-
-        it('should not change the bitmap size of the canvas', function () {
-            display.autoscale(16, 9);
-            expect(canvas.width).to.equal(4);
-            expect(canvas.height).to.equal(3);
-        });
-    });
-
-    describe('drawing', function () {
-
-        // TODO(directxman12): improve the tests for each of the drawing functions to cover more than just the
-        //                     basic cases
-        let display;
-        beforeEach(function () {
-            display = new Display(document.createElement('canvas'));
-            display.resize(4, 4);
-        });
-
-        it('should clear the screen on #clear without a logo set', function () {
-            display.fillRect(0, 0, 4, 4, [0x00, 0x00, 0xff]);
-            display._logo = null;
-            display.clear();
-            display.resize(4, 4);
-            const empty = [];
-            for (let i = 0; i < 4 * display._fb_width * display._fb_height; i++) { empty[i] = 0; }
-            expect(display).to.have.displayed(new Uint8Array(empty));
-        });
-
-        it('should draw the logo on #clear with a logo set', function (done) {
-            display._logo = { width: 4, height: 4, type: "image/png", data: make_image_png(checked_data) };
-            display.clear();
-            display.onflush = () => {
-                expect(display).to.have.displayed(checked_data);
-                expect(display._fb_width).to.equal(4);
-                expect(display._fb_height).to.equal(4);
-                done();
-            };
-            display.flush();
-        });
-
-        it('should not draw directly on the target canvas', function () {
-            display.fillRect(0, 0, 4, 4, [0, 0, 0xff]);
-            display.flip();
-            display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
-            const expected = [];
-            for (let i = 0; i < 4 * display._fb_width * display._fb_height; i += 4) {
-                expected[i] = 0xff;
-                expected[i+1] = expected[i+2] = 0;
-                expected[i+3] = 0xff;
-            }
-            expect(display).to.have.displayed(new Uint8Array(expected));
-        });
-
-        it('should support filling a rectangle with particular color via #fillRect', function () {
-            display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
-            display.fillRect(0, 0, 2, 2, [0xff, 0, 0]);
-            display.fillRect(2, 2, 2, 2, [0xff, 0, 0]);
-            display.flip();
-            expect(display).to.have.displayed(checked_data);
-        });
-
-        it('should support copying an portion of the canvas via #copyImage', function () {
-            display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
-            display.fillRect(0, 0, 2, 2, [0xff, 0, 0x00]);
-            display.copyImage(0, 0, 2, 2, 2, 2);
-            display.flip();
-            expect(display).to.have.displayed(checked_data);
-        });
-
-        it('should support drawing images via #imageRect', function (done) {
-            display.imageRect(0, 0, "image/png", make_image_png(checked_data));
-            display.flip();
-            display.onflush = () => {
-                expect(display).to.have.displayed(checked_data);
-                done();
-            };
-            display.flush();
-        });
-
-        it('should support drawing tile data with a background color and sub tiles', function () {
-            display.startTile(0, 0, 4, 4, [0, 0xff, 0]);
-            display.subTile(0, 0, 2, 2, [0xff, 0, 0]);
-            display.subTile(2, 2, 2, 2, [0xff, 0, 0]);
-            display.finishTile();
-            display.flip();
-            expect(display).to.have.displayed(checked_data);
-        });
-
-        // We have a special cache for 16x16 tiles that we need to test
-        it('should support drawing a 16x16 tile', function () {
-            const large_checked_data = new Uint8Array(16*16*4);
-            display.resize(16, 16);
-
-            for (let y = 0;y < 16;y++) {
-                for (let x = 0;x < 16;x++) {
-                    let pixel;
-                    if ((x < 4) && (y < 4)) {
-                        // NB: of course IE11 doesn't support #slice on ArrayBufferViews...
-                        pixel = Array.prototype.slice.call(checked_data, (y*4+x)*4, (y*4+x+1)*4);
-                    } else {
-                        pixel = [0, 0xff, 0, 255];
-                    }
-                    large_checked_data.set(pixel, (y*16+x)*4);
-                }
-            }
-
-            display.startTile(0, 0, 16, 16, [0, 0xff, 0]);
-            display.subTile(0, 0, 2, 2, [0xff, 0, 0]);
-            display.subTile(2, 2, 2, 2, [0xff, 0, 0]);
-            display.finishTile();
-            display.flip();
-            expect(display).to.have.displayed(large_checked_data);
-        });
-
-        it('should support drawing BGRX blit images with true color via #blitImage', function () {
-            const data = [];
-            for (let i = 0; i < 16; i++) {
-                data[i * 4] = checked_data[i * 4 + 2];
-                data[i * 4 + 1] = checked_data[i * 4 + 1];
-                data[i * 4 + 2] = checked_data[i * 4];
-                data[i * 4 + 3] = checked_data[i * 4 + 3];
-            }
-            display.blitImage(0, 0, 4, 4, data, 0);
-            display.flip();
-            expect(display).to.have.displayed(checked_data);
-        });
-
-        it('should support drawing RGB blit images with true color via #blitRgbImage', function () {
-            const data = [];
-            for (let i = 0; i < 16; i++) {
-                data[i * 3] = checked_data[i * 4];
-                data[i * 3 + 1] = checked_data[i * 4 + 1];
-                data[i * 3 + 2] = checked_data[i * 4 + 2];
-            }
-            display.blitRgbImage(0, 0, 4, 4, data, 0);
-            display.flip();
-            expect(display).to.have.displayed(checked_data);
-        });
-
-        it('should support drawing an image object via #drawImage', function () {
-            const img = make_image_canvas(checked_data);
-            display.drawImage(img, 0, 0);
-            display.flip();
-            expect(display).to.have.displayed(checked_data);
-        });
-    });
-
-    describe('the render queue processor', function () {
-        let display;
-        beforeEach(function () {
-            display = new Display(document.createElement('canvas'));
-            display.resize(4, 4);
-            sinon.spy(display, '_scan_renderQ');
-        });
-
-        afterEach(function () {
-            window.requestAnimationFrame = this.old_requestAnimationFrame;
-        });
-
-        it('should try to process an item when it is pushed on, if nothing else is on the queue', function () {
-            display._renderQ_push({ type: 'noop' });  // does nothing
-            expect(display._scan_renderQ).to.have.been.calledOnce;
-        });
-
-        it('should not try to process an item when it is pushed on if we are waiting for other items', function () {
-            display._renderQ.length = 2;
-            display._renderQ_push({ type: 'noop' });
-            expect(display._scan_renderQ).to.not.have.been.called;
-        });
-
-        it('should wait until an image is loaded to attempt to draw it and the rest of the queue', function () {
-            const img = { complete: false, addEventListener: sinon.spy() };
-            display._renderQ = [{ type: 'img', x: 3, y: 4, img: img },
-                                { type: 'fill', x: 1, y: 2, width: 3, height: 4, color: 5 }];
-            display.drawImage = sinon.spy();
-            display.fillRect = sinon.spy();
-
-            display._scan_renderQ();
-            expect(display.drawImage).to.not.have.been.called;
-            expect(display.fillRect).to.not.have.been.called;
-            expect(img.addEventListener).to.have.been.calledOnce;
-
-            display._renderQ[0].img.complete = true;
-            display._scan_renderQ();
-            expect(display.drawImage).to.have.been.calledOnce;
-            expect(display.fillRect).to.have.been.calledOnce;
-            expect(img.addEventListener).to.have.been.calledOnce;
-        });
-
-        it('should call callback when queue is flushed', function () {
-            display.onflush = sinon.spy();
-            display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);
-            expect(display.onflush).to.not.have.been.called;
-            display.flush();
-            expect(display.onflush).to.have.been.calledOnce;
-        });
-
-        it('should draw a blit image on type "blit"', function () {
-            display.blitImage = sinon.spy();
-            display._renderQ_push({ type: 'blit', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] });
-            expect(display.blitImage).to.have.been.calledOnce;
-            expect(display.blitImage).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
-        });
-
-        it('should draw a blit RGB image on type "blitRgb"', function () {
-            display.blitRgbImage = sinon.spy();
-            display._renderQ_push({ type: 'blitRgb', x: 3, y: 4, width: 5, height: 6, data: [7, 8, 9] });
-            expect(display.blitRgbImage).to.have.been.calledOnce;
-            expect(display.blitRgbImage).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9], 0);
-        });
-
-        it('should copy a region on type "copy"', function () {
-            display.copyImage = sinon.spy();
-            display._renderQ_push({ type: 'copy', x: 3, y: 4, width: 5, height: 6, old_x: 7, old_y: 8 });
-            expect(display.copyImage).to.have.been.calledOnce;
-            expect(display.copyImage).to.have.been.calledWith(7, 8, 3, 4, 5, 6);
-        });
-
-        it('should fill a rect with a given color on type "fill"', function () {
-            display.fillRect = sinon.spy();
-            display._renderQ_push({ type: 'fill', x: 3, y: 4, width: 5, height: 6, color: [7, 8, 9]});
-            expect(display.fillRect).to.have.been.calledOnce;
-            expect(display.fillRect).to.have.been.calledWith(3, 4, 5, 6, [7, 8, 9]);
-        });
-
-        it('should draw an image from an image object on type "img" (if complete)', function () {
-            display.drawImage = sinon.spy();
-            display._renderQ_push({ type: 'img', x: 3, y: 4, img: { complete: true } });
-            expect(display.drawImage).to.have.been.calledOnce;
-            expect(display.drawImage).to.have.been.calledWith({ complete: true }, 3, 4);
-        });
-    });
-});
diff --git a/systemvm/agent/noVNC/tests/test.helper.js b/systemvm/agent/noVNC/tests/test.helper.js
deleted file mode 100644
index d44bab0..0000000
--- a/systemvm/agent/noVNC/tests/test.helper.js
+++ /dev/null
@@ -1,223 +0,0 @@
-const expect = chai.expect;
-
-import keysyms from '../core/input/keysymdef.js';
-import * as KeyboardUtil from "../core/input/util.js";
-import * as browser from '../core/util/browser.js';
-
-describe('Helpers', function () {
-    "use strict";
-
-    describe('keysyms.lookup', function () {
-        it('should map ASCII characters to keysyms', function () {
-            expect(keysyms.lookup('a'.charCodeAt())).to.be.equal(0x61);
-            expect(keysyms.lookup('A'.charCodeAt())).to.be.equal(0x41);
-        });
-        it('should map Latin-1 characters to keysyms', function () {
-            expect(keysyms.lookup('ø'.charCodeAt())).to.be.equal(0xf8);
-
-            expect(keysyms.lookup('é'.charCodeAt())).to.be.equal(0xe9);
-        });
-        it('should map characters that are in Windows-1252 but not in Latin-1 to keysyms', function () {
-            expect(keysyms.lookup('Š'.charCodeAt())).to.be.equal(0x01a9);
-        });
-        it('should map characters which aren\'t in Latin1 *or* Windows-1252 to keysyms', function () {
-            expect(keysyms.lookup('ũ'.charCodeAt())).to.be.equal(0x03fd);
-        });
-        it('should map unknown codepoints to the Unicode range', function () {
-            expect(keysyms.lookup('\n'.charCodeAt())).to.be.equal(0x100000a);
-            expect(keysyms.lookup('\u262D'.charCodeAt())).to.be.equal(0x100262d);
-        });
-        // This requires very recent versions of most browsers... skipping for now
-        it.skip('should map UCS-4 codepoints to the Unicode range', function () {
-            //expect(keysyms.lookup('\u{1F686}'.codePointAt())).to.be.equal(0x101f686);
-        });
-    });
-
-    describe('getKeycode', function () {
-        it('should pass through proper code', function () {
-            expect(KeyboardUtil.getKeycode({code: 'Semicolon'})).to.be.equal('Semicolon');
-        });
-        it('should map legacy values', function () {
-            expect(KeyboardUtil.getKeycode({code: ''})).to.be.equal('Unidentified');
-            expect(KeyboardUtil.getKeycode({code: 'OSLeft'})).to.be.equal('MetaLeft');
-        });
-        it('should map keyCode to code when possible', function () {
-            expect(KeyboardUtil.getKeycode({keyCode: 0x14})).to.be.equal('CapsLock');
-            expect(KeyboardUtil.getKeycode({keyCode: 0x5b})).to.be.equal('MetaLeft');
-            expect(KeyboardUtil.getKeycode({keyCode: 0x35})).to.be.equal('Digit5');
-            expect(KeyboardUtil.getKeycode({keyCode: 0x65})).to.be.equal('Numpad5');
-        });
-        it('should map keyCode left/right side', function () {
-            expect(KeyboardUtil.getKeycode({keyCode: 0x10, location: 1})).to.be.equal('ShiftLeft');
-            expect(KeyboardUtil.getKeycode({keyCode: 0x10, location: 2})).to.be.equal('ShiftRight');
-            expect(KeyboardUtil.getKeycode({keyCode: 0x11, location: 1})).to.be.equal('ControlLeft');
-            expect(KeyboardUtil.getKeycode({keyCode: 0x11, location: 2})).to.be.equal('ControlRight');
-        });
-        it('should map keyCode on numpad', function () {
-            expect(KeyboardUtil.getKeycode({keyCode: 0x0d, location: 0})).to.be.equal('Enter');
-            expect(KeyboardUtil.getKeycode({keyCode: 0x0d, location: 3})).to.be.equal('NumpadEnter');
-            expect(KeyboardUtil.getKeycode({keyCode: 0x23, location: 0})).to.be.equal('End');
-            expect(KeyboardUtil.getKeycode({keyCode: 0x23, location: 3})).to.be.equal('Numpad1');
-        });
-        it('should return Unidentified when it cannot map the keyCode', function () {
-            expect(KeyboardUtil.getKeycode({keycode: 0x42})).to.be.equal('Unidentified');
-        });
-
-        describe('Fix Meta on macOS', function () {
-            let origNavigator;
-            beforeEach(function () {
-                // window.navigator is a protected read-only property in many
-                // environments, so we need to redefine it whilst running these
-                // tests.
-                origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
-                if (origNavigator === undefined) {
-                    // Object.getOwnPropertyDescriptor() doesn't work
-                    // properly in any version of IE
-                    this.skip();
-                }
-
-                Object.defineProperty(window, "navigator", {value: {}});
-                if (window.navigator.platform !== undefined) {
-                    // Object.defineProperty() doesn't work properly in old
-                    // versions of Chrome
-                    this.skip();
-                }
-
-                window.navigator.platform = "Mac x86_64";
-            });
-            afterEach(function () {
-                Object.defineProperty(window, "navigator", origNavigator);
-            });
-
-            it('should respect ContextMenu on modern browser', function () {
-                expect(KeyboardUtil.getKeycode({code: 'ContextMenu', keyCode: 0x5d})).to.be.equal('ContextMenu');
-            });
-            it('should translate legacy ContextMenu to MetaRight', function () {
-                expect(KeyboardUtil.getKeycode({keyCode: 0x5d})).to.be.equal('MetaRight');
-            });
-        });
-    });
-
-    describe('getKey', function () {
-        it('should prefer key', function () {
-            if (browser.isIE() || browser.isEdge()) this.skip();
-            expect(KeyboardUtil.getKey({key: 'a', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.be.equal('a');
-        });
-        it('should map legacy values', function () {
-            expect(KeyboardUtil.getKey({key: 'Spacebar'})).to.be.equal(' ');
-            expect(KeyboardUtil.getKey({key: 'Left'})).to.be.equal('ArrowLeft');
-            expect(KeyboardUtil.getKey({key: 'OS'})).to.be.equal('Meta');
-            expect(KeyboardUtil.getKey({key: 'Win'})).to.be.equal('Meta');
-            expect(KeyboardUtil.getKey({key: 'UIKeyInputLeftArrow'})).to.be.equal('ArrowLeft');
-        });
-        it('should use code if no key', function () {
-            expect(KeyboardUtil.getKey({code: 'NumpadBackspace'})).to.be.equal('Backspace');
-        });
-        it('should not use code fallback for character keys', function () {
-            expect(KeyboardUtil.getKey({code: 'KeyA'})).to.be.equal('Unidentified');
-            expect(KeyboardUtil.getKey({code: 'Digit1'})).to.be.equal('Unidentified');
-            expect(KeyboardUtil.getKey({code: 'Period'})).to.be.equal('Unidentified');
-            expect(KeyboardUtil.getKey({code: 'Numpad1'})).to.be.equal('Unidentified');
-        });
-        it('should use charCode if no key', function () {
-            expect(KeyboardUtil.getKey({charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.be.equal('Š');
-        });
-        it('should return Unidentified when it cannot map the key', function () {
-            expect(KeyboardUtil.getKey({keycode: 0x42})).to.be.equal('Unidentified');
-        });
-
-        describe('Broken key AltGraph on IE/Edge', function () {
-            let origNavigator;
-            beforeEach(function () {
-                // window.navigator is a protected read-only property in many
-                // environments, so we need to redefine it whilst running these
-                // tests.
-                origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
-                if (origNavigator === undefined) {
-                    // Object.getOwnPropertyDescriptor() doesn't work
-                    // properly in any version of IE
-                    this.skip();
-                }
-
-                Object.defineProperty(window, "navigator", {value: {}});
-                if (window.navigator.platform !== undefined) {
-                    // Object.defineProperty() doesn't work properly in old
-                    // versions of Chrome
-                    this.skip();
-                }
-            });
-            afterEach(function () {
-                Object.defineProperty(window, "navigator", origNavigator);
-            });
-
-            it('should ignore printable character key on IE', function () {
-                window.navigator.userAgent = "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko";
-                expect(KeyboardUtil.getKey({key: 'a'})).to.be.equal('Unidentified');
-            });
-            it('should ignore printable character key on Edge', function () {
-                window.navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393";
-                expect(KeyboardUtil.getKey({key: 'a'})).to.be.equal('Unidentified');
-            });
-            it('should allow non-printable character key on IE', function () {
-                window.navigator.userAgent = "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko";
-                expect(KeyboardUtil.getKey({key: 'Shift'})).to.be.equal('Shift');
-            });
-            it('should allow non-printable character key on Edge', function () {
-                window.navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393";
-                expect(KeyboardUtil.getKey({key: 'Shift'})).to.be.equal('Shift');
-            });
-        });
-    });
-
-    describe('getKeysym', function () {
-        describe('Non-character keys', function () {
-            it('should recognize the right keys', function () {
-                expect(KeyboardUtil.getKeysym({key: 'Enter'})).to.be.equal(0xFF0D);
-                expect(KeyboardUtil.getKeysym({key: 'Backspace'})).to.be.equal(0xFF08);
-                expect(KeyboardUtil.getKeysym({key: 'Tab'})).to.be.equal(0xFF09);
-                expect(KeyboardUtil.getKeysym({key: 'Shift'})).to.be.equal(0xFFE1);
-                expect(KeyboardUtil.getKeysym({key: 'Control'})).to.be.equal(0xFFE3);
-                expect(KeyboardUtil.getKeysym({key: 'Alt'})).to.be.equal(0xFFE9);
-                expect(KeyboardUtil.getKeysym({key: 'Meta'})).to.be.equal(0xFFEB);
-                expect(KeyboardUtil.getKeysym({key: 'Escape'})).to.be.equal(0xFF1B);
-                expect(KeyboardUtil.getKeysym({key: 'ArrowUp'})).to.be.equal(0xFF52);
-            });
-            it('should map left/right side', function () {
-                expect(KeyboardUtil.getKeysym({key: 'Shift', location: 1})).to.be.equal(0xFFE1);
-                expect(KeyboardUtil.getKeysym({key: 'Shift', location: 2})).to.be.equal(0xFFE2);
-                expect(KeyboardUtil.getKeysym({key: 'Control', location: 1})).to.be.equal(0xFFE3);
-                expect(KeyboardUtil.getKeysym({key: 'Control', location: 2})).to.be.equal(0xFFE4);
-            });
-            it('should handle AltGraph', function () {
-                expect(KeyboardUtil.getKeysym({code: 'AltRight', key: 'Alt', location: 2})).to.be.equal(0xFFEA);
-                expect(KeyboardUtil.getKeysym({code: 'AltRight', key: 'AltGraph', location: 2})).to.be.equal(0xFE03);
-            });
-            it('should return null for unknown keys', function () {
-                expect(KeyboardUtil.getKeysym({key: 'Semicolon'})).to.be.null;
-                expect(KeyboardUtil.getKeysym({key: 'BracketRight'})).to.be.null;
-            });
-            it('should handle remappings', function () {
-                expect(KeyboardUtil.getKeysym({code: 'ControlLeft', key: 'Tab'})).to.be.equal(0xFF09);
-            });
-        });
-
-        describe('Numpad', function () {
-            it('should handle Numpad numbers', function () {
-                if (browser.isIE() || browser.isEdge()) this.skip();
-                expect(KeyboardUtil.getKeysym({code: 'Digit5', key: '5', location: 0})).to.be.equal(0x0035);
-                expect(KeyboardUtil.getKeysym({code: 'Numpad5', key: '5', location: 3})).to.be.equal(0xFFB5);
-            });
-            it('should handle Numpad non-character keys', function () {
-                expect(KeyboardUtil.getKeysym({code: 'Home', key: 'Home', location: 0})).to.be.equal(0xFF50);
-                expect(KeyboardUtil.getKeysym({code: 'Numpad5', key: 'Home', location: 3})).to.be.equal(0xFF95);
-                expect(KeyboardUtil.getKeysym({code: 'Delete', key: 'Delete', location: 0})).to.be.equal(0xFFFF);
-                expect(KeyboardUtil.getKeysym({code: 'NumpadDecimal', key: 'Delete', location: 3})).to.be.equal(0xFF9F);
-            });
-            it('should handle Numpad Decimal key', function () {
-                if (browser.isIE() || browser.isEdge()) this.skip();
-                expect(KeyboardUtil.getKeysym({code: 'NumpadDecimal', key: '.', location: 3})).to.be.equal(0xFFAE);
-                expect(KeyboardUtil.getKeysym({code: 'NumpadDecimal', key: ',', location: 3})).to.be.equal(0xFFAC);
-            });
-        });
-    });
-});
diff --git a/systemvm/agent/noVNC/tests/test.keyboard.js b/systemvm/agent/noVNC/tests/test.keyboard.js
deleted file mode 100644
index 77fe3f6..0000000
--- a/systemvm/agent/noVNC/tests/test.keyboard.js
+++ /dev/null
@@ -1,510 +0,0 @@
-const expect = chai.expect;
-
-import Keyboard from '../core/input/keyboard.js';
-import * as browser from '../core/util/browser.js';
-
-describe('Key Event Handling', function () {
-    "use strict";
-
-    // The real KeyboardEvent constructor might not work everywhere we
-    // want to run these tests
-    function keyevent(typeArg, KeyboardEventInit) {
-        const e = { type: typeArg };
-        for (let key in KeyboardEventInit) {
-            e[key] = KeyboardEventInit[key];
-        }
-        e.stopPropagation = sinon.spy();
-        e.preventDefault = sinon.spy();
-        return e;
-    }
-
-    describe('Decode Keyboard Events', function () {
-        it('should decode keydown events', function (done) {
-            if (browser.isIE() || browser.isEdge()) this.skip();
-            const kbd = new Keyboard(document);
-            kbd.onkeyevent = (keysym, code, down) => {
-                expect(keysym).to.be.equal(0x61);
-                expect(code).to.be.equal('KeyA');
-                expect(down).to.be.equal(true);
-                done();
-            };
-            kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
-        });
-        it('should decode keyup events', function (done) {
-            if (browser.isIE() || browser.isEdge()) this.skip();
-            let calls = 0;
-            const kbd = new Keyboard(document);
-            kbd.onkeyevent = (keysym, code, down) => {
-                expect(keysym).to.be.equal(0x61);
-                expect(code).to.be.equal('KeyA');
-                if (calls++ === 1) {
-                    expect(down).to.be.equal(false);
-                    done();
-                }
-            };
-            kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
-            kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'}));
-        });
-
-        describe('Legacy keypress Events', function () {
-            it('should wait for keypress when needed', function () {
-                const kbd = new Keyboard(document);
-                kbd.onkeyevent = sinon.spy();
-                kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
-                expect(kbd.onkeyevent).to.not.have.been.called;
-            });
-            it('should decode keypress events', function (done) {
-                const kbd = new Keyboard(document);
-                kbd.onkeyevent = (keysym, code, down) => {
-                    expect(keysym).to.be.equal(0x61);
-                    expect(code).to.be.equal('KeyA');
-                    expect(down).to.be.equal(true);
-                    done();
-                };
-                kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
-                kbd._handleKeyPress(keyevent('keypress', {code: 'KeyA', charCode: 0x61}));
-            });
-            it('should ignore keypress with different code', function () {
-                const kbd = new Keyboard(document);
-                kbd.onkeyevent = sinon.spy();
-                kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
-                kbd._handleKeyPress(keyevent('keypress', {code: 'KeyB', charCode: 0x61}));
-                expect(kbd.onkeyevent).to.not.have.been.called;
-            });
-            it('should handle keypress with missing code', function (done) {
-                const kbd = new Keyboard(document);
-                kbd.onkeyevent = (keysym, code, down) => {
-                    expect(keysym).to.be.equal(0x61);
-                    expect(code).to.be.equal('KeyA');
-                    expect(down).to.be.equal(true);
-                    done();
-                };
-                kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
-                kbd._handleKeyPress(keyevent('keypress', {charCode: 0x61}));
-            });
-            it('should guess key if no keypress and numeric key', function (done) {
-                const kbd = new Keyboard(document);
-                kbd.onkeyevent = (keysym, code, down) => {
-                    expect(keysym).to.be.equal(0x32);
-                    expect(code).to.be.equal('Digit2');
-                    expect(down).to.be.equal(true);
-                    done();
-                };
-                kbd._handleKeyDown(keyevent('keydown', {code: 'Digit2', keyCode: 0x32}));
-            });
-            it('should guess key if no keypress and alpha key', function (done) {
-                const kbd = new Keyboard(document);
-                kbd.onkeyevent = (keysym, code, down) => {
-                    expect(keysym).to.be.equal(0x61);
-                    expect(code).to.be.equal('KeyA');
-                    expect(down).to.be.equal(true);
-                    done();
-                };
-                kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41, shiftKey: false}));
-            });
-            it('should guess key if no keypress and alpha key (with shift)', function (done) {
-                const kbd = new Keyboard(document);
-                kbd.onkeyevent = (keysym, code, down) => {
-                    expect(keysym).to.be.equal(0x41);
-                    expect(code).to.be.equal('KeyA');
-                    expect(down).to.be.equal(true);
-                    done();
-                };
-                kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41, shiftKey: true}));
-            });
-            it('should not guess key if no keypress and unknown key', function (done) {
-                const kbd = new Keyboard(document);
-                kbd.onkeyevent = (keysym, code, down) => {
-                    expect(keysym).to.be.equal(0);
-                    expect(code).to.be.equal('KeyA');
-                    expect(down).to.be.equal(true);
-                    done();
-                };
-                kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x09}));
-            });
-        });
-
-        describe('suppress the right events at the right time', function () {
-            beforeEach(function () {
-                if (browser.isIE() || browser.isEdge()) this.skip();
-            });
-            it('should suppress anything with a valid key', function () {
-                const kbd = new Keyboard(document, {});
-                const evt1 = keyevent('keydown', {code: 'KeyA', key: 'a'});
-                kbd._handleKeyDown(evt1);
-                expect(evt1.preventDefault).to.have.been.called;
-                const evt2 = keyevent('keyup', {code: 'KeyA', key: 'a'});
-                kbd._handleKeyUp(evt2);
-                expect(evt2.preventDefault).to.have.been.called;
-            });
-            it('should not suppress keys without key', function () {
-                const kbd = new Keyboard(document, {});
-                const evt = keyevent('keydown', {code: 'KeyA', keyCode: 0x41});
-                kbd._handleKeyDown(evt);
-                expect(evt.preventDefault).to.not.have.been.called;
-            });
-            it('should suppress the following keypress event', function () {
-                const kbd = new Keyboard(document, {});
-                const evt1 = keyevent('keydown', {code: 'KeyA', keyCode: 0x41});
-                kbd._handleKeyDown(evt1);
-                const evt2 = keyevent('keypress', {code: 'KeyA', charCode: 0x41});
-                kbd._handleKeyPress(evt2);
-                expect(evt2.preventDefault).to.have.been.called;
-            });
-        });
-    });
-
-    describe('Fake keyup', function () {
-        it('should fake keyup events for virtual keyboards', function (done) {
-            if (browser.isIE() || browser.isEdge()) this.skip();
-            let count = 0;
-            const kbd = new Keyboard(document);
-            kbd.onkeyevent = (keysym, code, down) => {
-                switch (count++) {
-                    case 0:
-                        expect(keysym).to.be.equal(0x61);
-                        expect(code).to.be.equal('Unidentified');
-                        expect(down).to.be.equal(true);
-                        break;
-                    case 1:
-                        expect(keysym).to.be.equal(0x61);
-                        expect(code).to.be.equal('Unidentified');
-                        expect(down).to.be.equal(false);
-                        done();
-                }
-            };
-            kbd._handleKeyDown(keyevent('keydown', {code: 'Unidentified', key: 'a'}));
-        });
-
-        describe('iOS', function () {
-            let origNavigator;
-            beforeEach(function () {
-                // window.navigator is a protected read-only property in many
-                // environments, so we need to redefine it whilst running these
-                // tests.
-                origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
-                if (origNavigator === undefined) {
-                    // Object.getOwnPropertyDescriptor() doesn't work
-                    // properly in any version of IE
-                    this.skip();
-                }
-
-                Object.defineProperty(window, "navigator", {value: {}});
-                if (window.navigator.platform !== undefined) {
-                    // Object.defineProperty() doesn't work properly in old
-                    // versions of Chrome
-                    this.skip();
-                }
-
-                window.navigator.platform = "iPhone 9.0";
-            });
-            afterEach(function () {
-                Object.defineProperty(window, "navigator", origNavigator);
-            });
-
-            it('should fake keyup events on iOS', function (done) {
-                if (browser.isIE() || browser.isEdge()) this.skip();
-                let count = 0;
-                const kbd = new Keyboard(document);
-                kbd.onkeyevent = (keysym, code, down) => {
-                    switch (count++) {
-                        case 0:
-                            expect(keysym).to.be.equal(0x61);
-                            expect(code).to.be.equal('KeyA');
-                            expect(down).to.be.equal(true);
-                            break;
-                        case 1:
-                            expect(keysym).to.be.equal(0x61);
-                            expect(code).to.be.equal('KeyA');
-                            expect(down).to.be.equal(false);
-                            done();
-                    }
-                };
-                kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
-            });
-        });
-    });
-
-    describe('Track Key State', function () {
-        beforeEach(function () {
-            if (browser.isIE() || browser.isEdge()) this.skip();
-        });
-        it('should send release using the same keysym as the press', function (done) {
-            const kbd = new Keyboard(document);
-            kbd.onkeyevent = (keysym, code, down) => {
-                expect(keysym).to.be.equal(0x61);
-                expect(code).to.be.equal('KeyA');
-                if (!down) {
-                    done();
-                }
-            };
-            kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
-            kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'b'}));
-        });
-        it('should send the same keysym for multiple presses', function () {
-            let count = 0;
-            const kbd = new Keyboard(document);
-            kbd.onkeyevent = (keysym, code, down) => {
-                expect(keysym).to.be.equal(0x61);
-                expect(code).to.be.equal('KeyA');
-                expect(down).to.be.equal(true);
-                count++;
-            };
-            kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
-            kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'b'}));
-            expect(count).to.be.equal(2);
-        });
-        it('should do nothing on keyup events if no keys are down', function () {
-            const kbd = new Keyboard(document);
-            kbd.onkeyevent = sinon.spy();
-            kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'}));
-            expect(kbd.onkeyevent).to.not.have.been.called;
-        });
-
-        describe('Legacy Events', function () {
-            it('should track keys using keyCode if no code', function (done) {
-                const kbd = new Keyboard(document);
-                kbd.onkeyevent = (keysym, code, down) => {
-                    expect(keysym).to.be.equal(0x61);
-                    expect(code).to.be.equal('Platform65');
-                    if (!down) {
-                        done();
-                    }
-                };
-                kbd._handleKeyDown(keyevent('keydown', {keyCode: 65, key: 'a'}));
-                kbd._handleKeyUp(keyevent('keyup', {keyCode: 65, key: 'b'}));
-            });
-            it('should ignore compositing code', function () {
-                const kbd = new Keyboard(document);
-                kbd.onkeyevent = (keysym, code, down) => {
-                    expect(keysym).to.be.equal(0x61);
-                    expect(code).to.be.equal('Unidentified');
-                };
-                kbd._handleKeyDown(keyevent('keydown', {keyCode: 229, key: 'a'}));
-            });
-            it('should track keys using keyIdentifier if no code', function (done) {
-                const kbd = new Keyboard(document);
-                kbd.onkeyevent = (keysym, code, down) => {
-                    expect(keysym).to.be.equal(0x61);
-                    expect(code).to.be.equal('Platform65');
-                    if (!down) {
-                        done();
-                    }
-                };
-                kbd._handleKeyDown(keyevent('keydown', {keyIdentifier: 'U+0041', key: 'a'}));
-                kbd._handleKeyUp(keyevent('keyup', {keyIdentifier: 'U+0041', key: 'b'}));
-            });
-        });
-    });
-
-    describe('Shuffle modifiers on macOS', function () {
-        let origNavigator;
-        beforeEach(function () {
-            // window.navigator is a protected read-only property in many
-            // environments, so we need to redefine it whilst running these
-            // tests.
-            origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
-            if (origNavigator === undefined) {
-                // Object.getOwnPropertyDescriptor() doesn't work
-                // properly in any version of IE
-                this.skip();
-            }
-
-            Object.defineProperty(window, "navigator", {value: {}});
-            if (window.navigator.platform !== undefined) {
-                // Object.defineProperty() doesn't work properly in old
-                // versions of Chrome
-                this.skip();
-            }
-
-            window.navigator.platform = "Mac x86_64";
-        });
-        afterEach(function () {
-            Object.defineProperty(window, "navigator", origNavigator);
-        });
-
-        it('should change Alt to AltGraph', function () {
-            let count = 0;
-            const kbd = new Keyboard(document);
-            kbd.onkeyevent = (keysym, code, down) => {
-                switch (count++) {
-                    case 0:
-                        expect(keysym).to.be.equal(0xFF7E);
-                        expect(code).to.be.equal('AltLeft');
-                        break;
-                    case 1:
-                        expect(keysym).to.be.equal(0xFE03);
-                        expect(code).to.be.equal('AltRight');
-                        break;
-                }
-            };
-            kbd._handleKeyDown(keyevent('keydown', {code: 'AltLeft', key: 'Alt', location: 1}));
-            kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2}));
-            expect(count).to.be.equal(2);
-        });
-        it('should change left Super to Alt', function (done) {
-            const kbd = new Keyboard(document);
-            kbd.onkeyevent = (keysym, code, down) => {
-                expect(keysym).to.be.equal(0xFFE9);
-                expect(code).to.be.equal('MetaLeft');
-                done();
-            };
-            kbd._handleKeyDown(keyevent('keydown', {code: 'MetaLeft', key: 'Meta', location: 1}));
-        });
-        it('should change right Super to left Super', function (done) {
-            const kbd = new Keyboard(document);
-            kbd.onkeyevent = (keysym, code, down) => {
-                expect(keysym).to.be.equal(0xFFEB);
-                expect(code).to.be.equal('MetaRight');
-                done();
-            };
-            kbd._handleKeyDown(keyevent('keydown', {code: 'MetaRight', key: 'Meta', location: 2}));
-        });
-    });
-
-    describe('Escape AltGraph on Windows', function () {
-        let origNavigator;
-        beforeEach(function () {
-            // window.navigator is a protected read-only property in many
-            // environments, so we need to redefine it whilst running these
-            // tests.
-            origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
-            if (origNavigator === undefined) {
-                // Object.getOwnPropertyDescriptor() doesn't work
-                // properly in any version of IE
-                this.skip();
-            }
-
-            Object.defineProperty(window, "navigator", {value: {}});
-            if (window.navigator.platform !== undefined) {
-                // Object.defineProperty() doesn't work properly in old
-                // versions of Chrome
-                this.skip();
-            }
-
-            window.navigator.platform = "Windows x86_64";
-
-            this.clock = sinon.useFakeTimers();
-        });
-        afterEach(function () {
-            Object.defineProperty(window, "navigator", origNavigator);
-            this.clock.restore();
-        });
-
-        it('should supress ControlLeft until it knows if it is AltGr', function () {
-            const kbd = new Keyboard(document);
-            kbd.onkeyevent = sinon.spy();
-            kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
-            expect(kbd.onkeyevent).to.not.have.been.called;
-        });
-
-        it('should not trigger on repeating ControlLeft', function () {
-            const kbd = new Keyboard(document);
-            kbd.onkeyevent = sinon.spy();
-            kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
-            kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
-            expect(kbd.onkeyevent).to.have.been.calledTwice;
-            expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true);
-            expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xffe3, "ControlLeft", true);
-        });
-
-        it('should not supress ControlRight', function () {
-            const kbd = new Keyboard(document);
-            kbd.onkeyevent = sinon.spy();
-            kbd._handleKeyDown(keyevent('keydown', {code: 'ControlRight', key: 'Control', location: 2}));
-            expect(kbd.onkeyevent).to.have.been.calledOnce;
-            expect(kbd.onkeyevent).to.have.been.calledWith(0xffe4, "ControlRight", true);
-        });
-
-        it('should release ControlLeft after 100 ms', function () {
-            const kbd = new Keyboard(document);
-            kbd.onkeyevent = sinon.spy();
-            kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
-            expect(kbd.onkeyevent).to.not.have.been.called;
-            this.clock.tick(100);
-            expect(kbd.onkeyevent).to.have.been.calledOnce;
-            expect(kbd.onkeyevent).to.have.been.calledWith(0xffe3, "ControlLeft", true);
-        });
-
-        it('should release ControlLeft on other key press', function () {
-            const kbd = new Keyboard(document);
-            kbd.onkeyevent = sinon.spy();
-            kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
-            expect(kbd.onkeyevent).to.not.have.been.called;
-            kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
-            expect(kbd.onkeyevent).to.have.been.calledTwice;
-            expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true);
-            expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0x61, "KeyA", true);
-
-            // Check that the timer is properly dead
-            kbd.onkeyevent.reset();
-            this.clock.tick(100);
-            expect(kbd.onkeyevent).to.not.have.been.called;
-        });
-
-        it('should release ControlLeft on other key release', function () {
-            const kbd = new Keyboard(document);
-            kbd.onkeyevent = sinon.spy();
-            kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
-            kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1}));
-            expect(kbd.onkeyevent).to.have.been.calledOnce;
-            expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0x61, "KeyA", true);
-            kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'}));
-            expect(kbd.onkeyevent).to.have.been.calledThrice;
-            expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xffe3, "ControlLeft", true);
-            expect(kbd.onkeyevent.thirdCall).to.have.been.calledWith(0x61, "KeyA", false);
-
-            // Check that the timer is properly dead
-            kbd.onkeyevent.reset();
-            this.clock.tick(100);
-            expect(kbd.onkeyevent).to.not.have.been.called;
-        });
-
-        it('should generate AltGraph for quick Ctrl+Alt sequence', function () {
-            const kbd = new Keyboard(document);
-            kbd.onkeyevent = sinon.spy();
-            kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1, timeStamp: Date.now()}));
-            this.clock.tick(20);
-            kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2, timeStamp: Date.now()}));
-            expect(kbd.onkeyevent).to.have.been.calledOnce;
-            expect(kbd.onkeyevent).to.have.been.calledWith(0xfe03, 'AltRight', true);
-
-            // Check that the timer is properly dead
-            kbd.onkeyevent.reset();
-            this.clock.tick(100);
-            expect(kbd.onkeyevent).to.not.have.been.called;
-        });
-
-        it('should generate Ctrl, Alt for slow Ctrl+Alt sequence', function () {
-            const kbd = new Keyboard(document);
-            kbd.onkeyevent = sinon.spy();
-            kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1, timeStamp: Date.now()}));
-            this.clock.tick(60);
-            kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2, timeStamp: Date.now()}));
-            expect(kbd.onkeyevent).to.have.been.calledTwice;
-            expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true);
-            expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xffea, "AltRight", true);
-
-            // Check that the timer is properly dead
-            kbd.onkeyevent.reset();
-            this.clock.tick(100);
-            expect(kbd.onkeyevent).to.not.have.been.called;
-        });
-
-        it('should pass through single Alt', function () {
-            const kbd = new Keyboard(document);
-            kbd.onkeyevent = sinon.spy();
-            kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'Alt', location: 2}));
-            expect(kbd.onkeyevent).to.have.been.calledOnce;
-            expect(kbd.onkeyevent).to.have.been.calledWith(0xffea, 'AltRight', true);
-        });
-
-        it('should pass through single AltGr', function () {
-            const kbd = new Keyboard(document);
-            kbd.onkeyevent = sinon.spy();
-            kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'AltGraph', location: 2}));
-            expect(kbd.onkeyevent).to.have.been.calledOnce;
-            expect(kbd.onkeyevent).to.have.been.calledWith(0xfe03, 'AltRight', true);
-        });
-    });
-});
diff --git a/systemvm/agent/noVNC/tests/test.localization.js b/systemvm/agent/noVNC/tests/test.localization.js
deleted file mode 100644
index 9570c17..0000000
--- a/systemvm/agent/noVNC/tests/test.localization.js
+++ /dev/null
@@ -1,72 +0,0 @@
-const expect = chai.expect;
-import { l10n } from '../app/localization.js';
-
-describe('Localization', function () {
-    "use strict";
-
-    describe('language selection', function () {
-        let origNavigator;
-        beforeEach(function () {
-            // window.navigator is a protected read-only property in many
-            // environments, so we need to redefine it whilst running these
-            // tests.
-            origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
-            if (origNavigator === undefined) {
-                // Object.getOwnPropertyDescriptor() doesn't work
-                // properly in any version of IE
-                this.skip();
-            }
-
-            Object.defineProperty(window, "navigator", {value: {}});
-            if (window.navigator.languages !== undefined) {
-                // Object.defineProperty() doesn't work properly in old
-                // versions of Chrome
-                this.skip();
-            }
-
-            window.navigator.languages = [];
-        });
-        afterEach(function () {
-            Object.defineProperty(window, "navigator", origNavigator);
-        });
-
-        it('should use English by default', function () {
-            expect(l10n.language).to.equal('en');
-        });
-        it('should use English if no user language matches', function () {
-            window.navigator.languages = ["nl", "de"];
-            l10n.setup(["es", "fr"]);
-            expect(l10n.language).to.equal('en');
-        });
-        it('should use the most preferred user language', function () {
-            window.navigator.languages = ["nl", "de", "fr"];
-            l10n.setup(["es", "fr", "de"]);
-            expect(l10n.language).to.equal('de');
-        });
-        it('should prefer sub-languages languages', function () {
-            window.navigator.languages = ["pt-BR"];
-            l10n.setup(["pt", "pt-BR"]);
-            expect(l10n.language).to.equal('pt-BR');
-        });
-        it('should fall back to language "parents"', function () {
-            window.navigator.languages = ["pt-BR"];
-            l10n.setup(["fr", "pt", "de"]);
-            expect(l10n.language).to.equal('pt');
-        });
-        it('should not use specific language when user asks for a generic language', function () {
-            window.navigator.languages = ["pt", "de"];
-            l10n.setup(["fr", "pt-BR", "de"]);
-            expect(l10n.language).to.equal('de');
-        });
-        it('should handle underscore as a separator', function () {
-            window.navigator.languages = ["pt-BR"];
-            l10n.setup(["pt_BR"]);
-            expect(l10n.language).to.equal('pt_BR');
-        });
-        it('should handle difference in case', function () {
-            window.navigator.languages = ["pt-br"];
-            l10n.setup(["pt-BR"]);
-            expect(l10n.language).to.equal('pt-BR');
-        });
-    });
-});
diff --git a/systemvm/agent/noVNC/tests/test.mouse.js b/systemvm/agent/noVNC/tests/test.mouse.js
deleted file mode 100644
index 78c74f1..0000000
--- a/systemvm/agent/noVNC/tests/test.mouse.js
+++ /dev/null
@@ -1,304 +0,0 @@
-const expect = chai.expect;
-
-import Mouse from '../core/input/mouse.js';
-
-describe('Mouse Event Handling', function () {
-    "use strict";
-
-    let target;
-
-    beforeEach(function () {
-        // For these tests we can assume that the canvas is 100x100
-        // located at coordinates 10x10
-        target = document.createElement('canvas');
-        target.style.position = "absolute";
-        target.style.top = "10px";
-        target.style.left = "10px";
-        target.style.width = "100px";
-        target.style.height = "100px";
-        document.body.appendChild(target);
-    });
-    afterEach(function () {
-        document.body.removeChild(target);
-        target = null;
-    });
-
-    // The real constructors might not work everywhere we
-    // want to run these tests
-    const mouseevent = (typeArg, MouseEventInit) => {
-        const e = { type: typeArg };
-        for (let key in MouseEventInit) {
-            e[key] = MouseEventInit[key];
-        }
-        e.stopPropagation = sinon.spy();
-        e.preventDefault = sinon.spy();
-        return e;
-    };
-    const touchevent = mouseevent;
-
-    describe('Decode Mouse Events', function () {
-        it('should decode mousedown events', function (done) {
-            const mouse = new Mouse(target);
-            mouse.onmousebutton = (x, y, down, bmask) => {
-                expect(bmask).to.be.equal(0x01);
-                expect(down).to.be.equal(1);
-                done();
-            };
-            mouse._handleMouseDown(mouseevent('mousedown', { button: '0x01' }));
-        });
-        it('should decode mouseup events', function (done) {
-            let calls = 0;
-            const mouse = new Mouse(target);
-            mouse.onmousebutton = (x, y, down, bmask) => {
-                expect(bmask).to.be.equal(0x01);
-                if (calls++ === 1) {
-                    expect(down).to.not.be.equal(1);
-                    done();
-                }
-            };
-            mouse._handleMouseDown(mouseevent('mousedown', { button: '0x01' }));
-            mouse._handleMouseUp(mouseevent('mouseup', { button: '0x01' }));
-        });
-        it('should decode mousemove events', function (done) {
-            const mouse = new Mouse(target);
-            mouse.onmousemove = (x, y) => {
-                // Note that target relative coordinates are sent
-                expect(x).to.be.equal(40);
-                expect(y).to.be.equal(10);
-                done();
-            };
-            mouse._handleMouseMove(mouseevent('mousemove',
-                                              { clientX: 50, clientY: 20 }));
-        });
-        it('should decode mousewheel events', function (done) {
-            let calls = 0;
-            const mouse = new Mouse(target);
-            mouse.onmousebutton = (x, y, down, bmask) => {
-                calls++;
-                expect(bmask).to.be.equal(1<<6);
-                if (calls === 1) {
-                    expect(down).to.be.equal(1);
-                } else if (calls === 2) {
-                    expect(down).to.not.be.equal(1);
-                    done();
-                }
-            };
-            mouse._handleMouseWheel(mouseevent('mousewheel',
-                                               { deltaX: 50, deltaY: 0,
-                                                 deltaMode: 0}));
-        });
-    });
-
-    describe('Double-click for Touch', function () {
-
-        beforeEach(function () { this.clock = sinon.useFakeTimers(); });
-        afterEach(function () { this.clock.restore(); });
-
-        it('should use same pos for 2nd tap if close enough', function (done) {
-            let calls = 0;
-            const mouse = new Mouse(target);
-            mouse.onmousebutton = (x, y, down, bmask) => {
-                calls++;
-                if (calls === 1) {
-                    expect(down).to.be.equal(1);
-                    expect(x).to.be.equal(68);
-                    expect(y).to.be.equal(36);
-                } else if (calls === 3) {
-                    expect(down).to.be.equal(1);
-                    expect(x).to.be.equal(68);
-                    expect(y).to.be.equal(36);
-                    done();
-                }
-            };
-            // touch events are sent in an array of events
-            // with one item for each touch point
-            mouse._handleMouseDown(touchevent(
-                'touchstart', { touches: [{ clientX: 78, clientY: 46 }]}));
-            this.clock.tick(10);
-            mouse._handleMouseUp(touchevent(
-                'touchend', { touches: [{ clientX: 79, clientY: 45 }]}));
-            this.clock.tick(200);
-            mouse._handleMouseDown(touchevent(
-                'touchstart', { touches: [{ clientX: 67, clientY: 35 }]}));
-            this.clock.tick(10);
-            mouse._handleMouseUp(touchevent(
-                'touchend', { touches: [{ clientX: 66, clientY: 36 }]}));
-        });
-
-        it('should not modify 2nd tap pos if far apart', function (done) {
-            let calls = 0;
-            const mouse = new Mouse(target);
-            mouse.onmousebutton = (x, y, down, bmask) => {
-                calls++;
-                if (calls === 1) {
-                    expect(down).to.be.equal(1);
-                    expect(x).to.be.equal(68);
-                    expect(y).to.be.equal(36);
-                } else if (calls === 3) {
-                    expect(down).to.be.equal(1);
-                    expect(x).to.not.be.equal(68);
-                    expect(y).to.not.be.equal(36);
-                    done();
-                }
-            };
-            mouse._handleMouseDown(touchevent(
-                'touchstart', { touches: [{ clientX: 78, clientY: 46 }]}));
-            this.clock.tick(10);
-            mouse._handleMouseUp(touchevent(
-                'touchend', { touches: [{ clientX: 79, clientY: 45 }]}));
-            this.clock.tick(200);
-            mouse._handleMouseDown(touchevent(
-                'touchstart', { touches: [{ clientX: 57, clientY: 35 }]}));
-            this.clock.tick(10);
-            mouse._handleMouseUp(touchevent(
-                'touchend', { touches: [{ clientX: 56, clientY: 36 }]}));
-        });
-
-        it('should not modify 2nd tap pos if not soon enough', function (done) {
-            let calls = 0;
-            const mouse = new Mouse(target);
-            mouse.onmousebutton = (x, y, down, bmask) => {
-                calls++;
-                if (calls === 1) {
-                    expect(down).to.be.equal(1);
-                    expect(x).to.be.equal(68);
-                    expect(y).to.be.equal(36);
-                } else if (calls === 3) {
-                    expect(down).to.be.equal(1);
-                    expect(x).to.not.be.equal(68);
-                    expect(y).to.not.be.equal(36);
-                    done();
-                }
-            };
-            mouse._handleMouseDown(touchevent(
-                'touchstart', { touches: [{ clientX: 78, clientY: 46 }]}));
-            this.clock.tick(10);
-            mouse._handleMouseUp(touchevent(
-                'touchend', { touches: [{ clientX: 79, clientY: 45 }]}));
-            this.clock.tick(500);
-            mouse._handleMouseDown(touchevent(
-                'touchstart', { touches: [{ clientX: 67, clientY: 35 }]}));
-            this.clock.tick(10);
-            mouse._handleMouseUp(touchevent(
-                'touchend', { touches: [{ clientX: 66, clientY: 36 }]}));
-        });
-
-        it('should not modify 2nd tap pos if not touch', function (done) {
-            let calls = 0;
-            const mouse = new Mouse(target);
-            mouse.onmousebutton = (x, y, down, bmask) => {
-                calls++;
-                if (calls === 1) {
-                    expect(down).to.be.equal(1);
-                    expect(x).to.be.equal(68);
-                    expect(y).to.be.equal(36);
-                } else if (calls === 3) {
-                    expect(down).to.be.equal(1);
-                    expect(x).to.not.be.equal(68);
-                    expect(y).to.not.be.equal(36);
-                    done();
-                }
-            };
-            mouse._handleMouseDown(mouseevent(
-                'mousedown', { button: '0x01', clientX: 78, clientY: 46 }));
-            this.clock.tick(10);
-            mouse._handleMouseUp(mouseevent(
-                'mouseup', { button: '0x01', clientX: 79, clientY: 45 }));
-            this.clock.tick(200);
-            mouse._handleMouseDown(mouseevent(
-                'mousedown', { button: '0x01', clientX: 67, clientY: 35 }));
-            this.clock.tick(10);
-            mouse._handleMouseUp(mouseevent(
-                'mouseup', { button: '0x01', clientX: 66, clientY: 36 }));
-        });
-
-    });
-
-    describe('Accumulate mouse wheel events with small delta', function () {
-
-        beforeEach(function () { this.clock = sinon.useFakeTimers(); });
-        afterEach(function () { this.clock.restore(); });
-
-        it('should accumulate wheel events if small enough', function () {
-            const mouse = new Mouse(target);
-            mouse.onmousebutton = sinon.spy();
-
-            mouse._handleMouseWheel(mouseevent(
-                'mousewheel', { clientX: 18, clientY: 40,
-                                deltaX: 4, deltaY: 0, deltaMode: 0 }));
-            this.clock.tick(10);
-            mouse._handleMouseWheel(mouseevent(
-                'mousewheel', { clientX: 18, clientY: 40,
-                                deltaX: 4, deltaY: 0, deltaMode: 0 }));
-
-            // threshold is 10
-            expect(mouse._accumulatedWheelDeltaX).to.be.equal(8);
-
-            this.clock.tick(10);
-            mouse._handleMouseWheel(mouseevent(
-                'mousewheel', { clientX: 18, clientY: 40,
-                                deltaX: 4, deltaY: 0, deltaMode: 0 }));
-
-            expect(mouse.onmousebutton).to.have.callCount(2); // mouse down and up
-
-            this.clock.tick(10);
-            mouse._handleMouseWheel(mouseevent(
-                'mousewheel', { clientX: 18, clientY: 40,
-                                deltaX: 4, deltaY: 9, deltaMode: 0 }));
-
-            expect(mouse._accumulatedWheelDeltaX).to.be.equal(4);
-            expect(mouse._accumulatedWheelDeltaY).to.be.equal(9);
-
-            expect(mouse.onmousebutton).to.have.callCount(2); // still
-        });
-
-        it('should not accumulate large wheel events', function () {
-            const mouse = new Mouse(target);
-            mouse.onmousebutton = sinon.spy();
-
-            mouse._handleMouseWheel(mouseevent(
-                'mousewheel', { clientX: 18, clientY: 40,
-                                deltaX: 11, deltaY: 0, deltaMode: 0 }));
-            this.clock.tick(10);
-            mouse._handleMouseWheel(mouseevent(
-                'mousewheel', { clientX: 18, clientY: 40,
-                                deltaX: 0, deltaY: 70, deltaMode: 0 }));
-            this.clock.tick(10);
-            mouse._handleMouseWheel(mouseevent(
-                'mousewheel', { clientX: 18, clientY: 40,
-                                deltaX: 400, deltaY: 400, deltaMode: 0 }));
-
-            expect(mouse.onmousebutton).to.have.callCount(8); // mouse down and up
-        });
-
-        it('should send even small wheel events after a timeout', function () {
-            const mouse = new Mouse(target);
-            mouse.onmousebutton = sinon.spy();
-
-            mouse._handleMouseWheel(mouseevent(
-                'mousewheel', { clientX: 18, clientY: 40,
-                                deltaX: 1, deltaY: 0, deltaMode: 0 }));
-            this.clock.tick(51); // timeout on 50 ms
-
-            expect(mouse.onmousebutton).to.have.callCount(2); // mouse down and up
-        });
-
-        it('should account for non-zero deltaMode', function () {
-            const mouse = new Mouse(target);
-            mouse.onmousebutton = sinon.spy();
-
-            mouse._handleMouseWheel(mouseevent(
-                'mousewheel', { clientX: 18, clientY: 40,
-                                deltaX: 0, deltaY: 2, deltaMode: 1 }));
-
-            this.clock.tick(10);
-
-            mouse._handleMouseWheel(mouseevent(
-                'mousewheel', { clientX: 18, clientY: 40,
-                                deltaX: 1, deltaY: 0, deltaMode: 2 }));
-
-            expect(mouse.onmousebutton).to.have.callCount(4); // mouse down and up
-        });
-    });
-
-});
diff --git a/systemvm/agent/noVNC/tests/test.rfb.js b/systemvm/agent/noVNC/tests/test.rfb.js
deleted file mode 100644
index 99c9c90..0000000
--- a/systemvm/agent/noVNC/tests/test.rfb.js
+++ /dev/null
@@ -1,2389 +0,0 @@
-const expect = chai.expect;
-
-import RFB from '../core/rfb.js';
-import Websock from '../core/websock.js';
-import { encodings } from '../core/encodings.js';
-
-import FakeWebSocket from './fake.websocket.js';
-
-/* UIEvent constructor polyfill for IE */
-(() => {
-    if (typeof window.UIEvent === "function") return;
-
-    function UIEvent( event, params ) {
-        params = params || { bubbles: false, cancelable: false, view: window, detail: undefined };
-        const evt = document.createEvent( 'UIEvent' );
-        evt.initUIEvent( event, params.bubbles, params.cancelable, params.view, params.detail );
-        return evt;
-    }
-
-    UIEvent.prototype = window.UIEvent.prototype;
-
-    window.UIEvent = UIEvent;
-})();
-
-function push8(arr, num) {
-    "use strict";
-    arr.push(num & 0xFF);
-}
-
-function push16(arr, num) {
-    "use strict";
-    arr.push((num >> 8) & 0xFF,
-             num & 0xFF);
-}
-
-function push32(arr, num) {
-    "use strict";
-    arr.push((num >> 24) & 0xFF,
-             (num >> 16) & 0xFF,
-             (num >>  8) & 0xFF,
-             num & 0xFF);
-}
-
-describe('Remote Frame Buffer Protocol Client', function () {
-    let clock;
-    let raf;
-
-    before(FakeWebSocket.replace);
-    after(FakeWebSocket.restore);
-
-    before(function () {
-        this.clock = clock = sinon.useFakeTimers();
-        // sinon doesn't support this yet
-        raf = window.requestAnimationFrame;
-        window.requestAnimationFrame = setTimeout;
-        // Use a single set of buffers instead of reallocating to
-        // speed up tests
-        const sock = new Websock();
-        const _sQ = new Uint8Array(sock._sQbufferSize);
-        const rQ = new Uint8Array(sock._rQbufferSize);
-
-        Websock.prototype._old_allocate_buffers = Websock.prototype._allocate_buffers;
-        Websock.prototype._allocate_buffers = function () {
-            this._sQ = _sQ;
-            this._rQ = rQ;
-        };
-
-    });
-
-    after(function () {
-        Websock.prototype._allocate_buffers = Websock.prototype._old_allocate_buffers;
-        this.clock.restore();
-        window.requestAnimationFrame = raf;
-    });
-
-    let container;
-    let rfbs;
-
-    beforeEach(function () {
-        // Create a container element for all RFB objects to attach to
-        container = document.createElement('div');
-        container.style.width = "100%";
-        container.style.height = "100%";
-        document.body.appendChild(container);
-
-        // And track all created RFB objects
-        rfbs = [];
-    });
-    afterEach(function () {
-        // Make sure every created RFB object is properly cleaned up
-        // or they might affect subsequent tests
-        rfbs.forEach(function (rfb) {
-            rfb.disconnect();
-            expect(rfb._disconnect).to.have.been.called;
-        });
-        rfbs = [];
-
-        document.body.removeChild(container);
-        container = null;
-    });
-
-    function make_rfb(url, options) {
-        url = url || 'wss://host:8675';
-        const rfb = new RFB(container, url, options);
-        clock.tick();
-        rfb._sock._websocket._open();
-        rfb._rfb_connection_state = 'connected';
-        sinon.spy(rfb, "_disconnect");
-        rfbs.push(rfb);
-        return rfb;
-    }
-
-    describe('Connecting/Disconnecting', function () {
-        describe('#RFB', function () {
-            it('should set the current state to "connecting"', function () {
-                const client = new RFB(document.createElement('div'), 'wss://host:8675');
-                client._rfb_connection_state = '';
-                this.clock.tick();
-                expect(client._rfb_connection_state).to.equal('connecting');
-            });
-
-            it('should actually connect to the websocket', function () {
-                const client = new RFB(document.createElement('div'), 'ws://HOST:8675/PATH');
-                sinon.spy(client._sock, 'open');
-                this.clock.tick();
-                expect(client._sock.open).to.have.been.calledOnce;
-                expect(client._sock.open).to.have.been.calledWith('ws://HOST:8675/PATH');
-            });
-        });
-
-        describe('#disconnect', function () {
-            let client;
-            beforeEach(function () {
-                client = make_rfb();
-            });
-
-            it('should go to state "disconnecting" before "disconnected"', function () {
-                sinon.spy(client, '_updateConnectionState');
-                client.disconnect();
-                expect(client._updateConnectionState).to.have.been.calledTwice;
-                expect(client._updateConnectionState.getCall(0).args[0])
-                    .to.equal('disconnecting');
-                expect(client._updateConnectionState.getCall(1).args[0])
-                    .to.equal('disconnected');
-                expect(client._rfb_connection_state).to.equal('disconnected');
-            });
-
-            it('should unregister error event handler', function () {
-                sinon.spy(client._sock, 'off');
-                client.disconnect();
-                expect(client._sock.off).to.have.been.calledWith('error');
-            });
-
-            it('should unregister message event handler', function () {
-                sinon.spy(client._sock, 'off');
-                client.disconnect();
-                expect(client._sock.off).to.have.been.calledWith('message');
-            });
-
-            it('should unregister open event handler', function () {
-                sinon.spy(client._sock, 'off');
-                client.disconnect();
-                expect(client._sock.off).to.have.been.calledWith('open');
-            });
-        });
-
-        describe('#sendCredentials', function () {
-            let client;
-            beforeEach(function () {
-                client = make_rfb();
-                client._rfb_connection_state = 'connecting';
-            });
-
-            it('should set the rfb credentials properly"', function () {
-                client.sendCredentials({ password: 'pass' });
-                expect(client._rfb_credentials).to.deep.equal({ password: 'pass' });
-            });
-
-            it('should call init_msg "soon"', function () {
-                client._init_msg = sinon.spy();
-                client.sendCredentials({ password: 'pass' });
-                this.clock.tick(5);
-                expect(client._init_msg).to.have.been.calledOnce;
-            });
-        });
-    });
-
-    describe('Public API Basic Behavior', function () {
-        let client;
-        beforeEach(function () {
-            client = make_rfb();
-        });
-
-        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: () => {}};
-                RFB.messages.keyEvent(expected, 0xFFE3, 1);
-                RFB.messages.keyEvent(expected, 0xFFE9, 1);
-                RFB.messages.keyEvent(expected, 0xFFFF, 1);
-                RFB.messages.keyEvent(expected, 0xFFFF, 0);
-                RFB.messages.keyEvent(expected, 0xFFE9, 0);
-                RFB.messages.keyEvent(expected, 0xFFE3, 0);
-
-                client.sendCtrlAltDel();
-                expect(client._sock).to.have.sent(expected._sQ);
-            });
-
-            it('should not send the keys if we are not in a normal state', function () {
-                sinon.spy(client._sock, 'flush');
-                client._rfb_connection_state = "connecting";
-                client.sendCtrlAltDel();
-                expect(client._sock.flush).to.not.have.been.called;
-            });
-
-            it('should not send the keys if we are set as view_only', function () {
-                sinon.spy(client._sock, 'flush');
-                client._viewOnly = true;
-                client.sendCtrlAltDel();
-                expect(client._sock.flush).to.not.have.been.called;
-            });
-        });
-
-        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: () => {}};
-                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: () => {}};
-                RFB.messages.keyEvent(expected, 123, 1);
-                RFB.messages.keyEvent(expected, 123, 0);
-                client.sendKey(123, 'Key123');
-                expect(client._sock).to.have.sent(expected._sQ);
-            });
-
-            it('should not send the key if we are not in a normal state', function () {
-                sinon.spy(client._sock, 'flush');
-                client._rfb_connection_state = "connecting";
-                client.sendKey(123, 'Key123');
-                expect(client._sock.flush).to.not.have.been.called;
-            });
-
-            it('should not send the key if we are set as view_only', function () {
-                sinon.spy(client._sock, 'flush');
-                client._viewOnly = true;
-                client.sendKey(123, 'Key123');
-                expect(client._sock.flush).to.not.have.been.called;
-            });
-
-            it('should send QEMU extended events if supported', function () {
-                client._qemuExtKeyEventSupported = true;
-                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);
-            });
-
-            it('should not send QEMU extended events if unknown key code', function () {
-                client._qemuExtKeyEventSupported = true;
-                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);
-            });
-        });
-
-        describe('#focus', function () {
-            it('should move focus to canvas object', function () {
-                client._canvas.focus = sinon.spy();
-                client.focus();
-                expect(client._canvas.focus).to.have.been.called.once;
-            });
-        });
-
-        describe('#blur', function () {
-            it('should remove focus from canvas object', function () {
-                client._canvas.blur = sinon.spy();
-                client.blur();
-                expect(client._canvas.blur).to.have.been.called.once;
-            });
-        });
-
-        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: () => {}};
-                RFB.messages.clientCutText(expected, 'abc');
-                client.clipboardPasteFrom('abc');
-                expect(client._sock).to.have.sent(expected._sQ);
-            });
-
-            it('should flush multiple times for large clipboards', function () {
-                sinon.spy(client._sock, 'flush');
-                let long_text = "";
-                for (let i = 0; i < client._sock._sQbufferSize + 100; i++) {
-                    long_text += 'a';
-                }
-                client.clipboardPasteFrom(long_text);
-                expect(client._sock.flush).to.have.been.calledTwice;
-            });
-
-            it('should not send the text if we are not in a normal state', function () {
-                sinon.spy(client._sock, 'flush');
-                client._rfb_connection_state = "connecting";
-                client.clipboardPasteFrom('abc');
-                expect(client._sock.flush).to.not.have.been.called;
-            });
-        });
-
-        describe("XVP operations", function () {
-            beforeEach(function () {
-                client._rfb_xvp_ver = 1;
-            });
-
-            it('should send the shutdown signal on #machineShutdown', function () {
-                client.machineShutdown();
-                expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x02]));
-            });
-
-            it('should send the reboot signal on #machineReboot', function () {
-                client.machineReboot();
-                expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x03]));
-            });
-
-            it('should send the reset signal on #machineReset', function () {
-                client.machineReset();
-                expect(client._sock).to.have.sent(new Uint8Array([0xFA, 0x00, 0x01, 0x04]));
-            });
-
-            it('should not send XVP operations with higher versions than we support', function () {
-                sinon.spy(client._sock, 'flush');
-                client._xvpOp(2, 7);
-                expect(client._sock.flush).to.not.have.been.called;
-            });
-        });
-    });
-
-    describe('Clipping', function () {
-        let client;
-        beforeEach(function () {
-            client = make_rfb();
-            container.style.width = '70px';
-            container.style.height = '80px';
-            client.clipViewport = true;
-        });
-
-        it('should update display clip state when changing the property', function () {
-            const spy = sinon.spy(client._display, "clipViewport", ["set"]);
-
-            client.clipViewport = false;
-            expect(spy.set).to.have.been.calledOnce;
-            expect(spy.set).to.have.been.calledWith(false);
-            spy.set.reset();
-
-            client.clipViewport = true;
-            expect(spy.set).to.have.been.calledOnce;
-            expect(spy.set).to.have.been.calledWith(true);
-        });
-
-        it('should update the viewport when the container size changes', function () {
-            sinon.spy(client._display, "viewportChangeSize");
-
-            container.style.width = '40px';
-            container.style.height = '50px';
-            const event = new UIEvent('resize');
-            window.dispatchEvent(event);
-            clock.tick();
-
-            expect(client._display.viewportChangeSize).to.have.been.calledOnce;
-            expect(client._display.viewportChangeSize).to.have.been.calledWith(40, 50);
-        });
-
-        it('should update the viewport when the remote session resizes', function () {
-            // Simple ExtendedDesktopSize FBU message
-            const incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-                               0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
-                               0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                               0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
-                               0x00, 0x00, 0x00, 0x00 ];
-
-            sinon.spy(client._display, "viewportChangeSize");
-
-            client._sock._websocket._receive_data(new Uint8Array(incoming));
-
-            // FIXME: Display implicitly calls viewportChangeSize() when
-            //        resizing the framebuffer, hence calledTwice.
-            expect(client._display.viewportChangeSize).to.have.been.calledTwice;
-            expect(client._display.viewportChangeSize).to.have.been.calledWith(70, 80);
-        });
-
-        it('should not update the viewport if not clipping', function () {
-            client.clipViewport = false;
-            sinon.spy(client._display, "viewportChangeSize");
-
-            container.style.width = '40px';
-            container.style.height = '50px';
-            const event = new UIEvent('resize');
-            window.dispatchEvent(event);
-            clock.tick();
-
-            expect(client._display.viewportChangeSize).to.not.have.been.called;
-        });
-
-        it('should not update the viewport if scaling', function () {
-            client.scaleViewport = true;
-            sinon.spy(client._display, "viewportChangeSize");
-
-            container.style.width = '40px';
-            container.style.height = '50px';
-            const event = new UIEvent('resize');
-            window.dispatchEvent(event);
-            clock.tick();
-
-            expect(client._display.viewportChangeSize).to.not.have.been.called;
-        });
-
-        describe('Dragging', function () {
-            beforeEach(function () {
-                client.dragViewport = true;
-                sinon.spy(RFB.messages, "pointerEvent");
-            });
-
-            afterEach(function () {
-                RFB.messages.pointerEvent.restore();
-            });
-
-            it('should not send button messages when initiating viewport dragging', function () {
-                client._handleMouseButton(13, 9, 0x001);
-                expect(RFB.messages.pointerEvent).to.not.have.been.called;
-            });
-
-            it('should send button messages when release without movement', function () {
-                // Just up and down
-                client._handleMouseButton(13, 9, 0x001);
-                client._handleMouseButton(13, 9, 0x000);
-                expect(RFB.messages.pointerEvent).to.have.been.calledTwice;
-
-                RFB.messages.pointerEvent.reset();
-
-                // Small movement
-                client._handleMouseButton(13, 9, 0x001);
-                client._handleMouseMove(15, 14);
-                client._handleMouseButton(15, 14, 0x000);
-                expect(RFB.messages.pointerEvent).to.have.been.calledTwice;
-            });
-
-            it('should send button message directly when drag is disabled', function () {
-                client.dragViewport = false;
-                client._handleMouseButton(13, 9, 0x001);
-                expect(RFB.messages.pointerEvent).to.have.been.calledOnce;
-            });
-
-            it('should be initiate viewport dragging on sufficient movement', function () {
-                sinon.spy(client._display, "viewportChangePos");
-
-                // Too small movement
-
-                client._handleMouseButton(13, 9, 0x001);
-                client._handleMouseMove(18, 9);
-
-                expect(RFB.messages.pointerEvent).to.not.have.been.called;
-                expect(client._display.viewportChangePos).to.not.have.been.called;
-
-                // Sufficient movement
-
-                client._handleMouseMove(43, 9);
-
-                expect(RFB.messages.pointerEvent).to.not.have.been.called;
-                expect(client._display.viewportChangePos).to.have.been.calledOnce;
-                expect(client._display.viewportChangePos).to.have.been.calledWith(-30, 0);
-
-                client._display.viewportChangePos.reset();
-
-                // Now a small movement should move right away
-
-                client._handleMouseMove(43, 14);
-
-                expect(RFB.messages.pointerEvent).to.not.have.been.called;
-                expect(client._display.viewportChangePos).to.have.been.calledOnce;
-                expect(client._display.viewportChangePos).to.have.been.calledWith(0, -5);
-            });
-
-            it('should not send button messages when dragging ends', function () {
-                // First the movement
-
-                client._handleMouseButton(13, 9, 0x001);
-                client._handleMouseMove(43, 9);
-                client._handleMouseButton(43, 9, 0x000);
-
-                expect(RFB.messages.pointerEvent).to.not.have.been.called;
-            });
-
-            it('should terminate viewport dragging on a button up event', function () {
-                // First the dragging movement
-
-                client._handleMouseButton(13, 9, 0x001);
-                client._handleMouseMove(43, 9);
-                client._handleMouseButton(43, 9, 0x000);
-
-                // Another movement now should not move the viewport
-
-                sinon.spy(client._display, "viewportChangePos");
-
-                client._handleMouseMove(43, 59);
-
-                expect(client._display.viewportChangePos).to.not.have.been.called;
-            });
-        });
-    });
-
-    describe('Scaling', function () {
-        let client;
-        beforeEach(function () {
-            client = make_rfb();
-            container.style.width = '70px';
-            container.style.height = '80px';
-            client.scaleViewport = true;
-        });
-
-        it('should update display scale factor when changing the property', function () {
-            const spy = sinon.spy(client._display, "scale", ["set"]);
-            sinon.spy(client._display, "autoscale");
-
-            client.scaleViewport = false;
-            expect(spy.set).to.have.been.calledOnce;
-            expect(spy.set).to.have.been.calledWith(1.0);
-            expect(client._display.autoscale).to.not.have.been.called;
-
-            client.scaleViewport = true;
-            expect(client._display.autoscale).to.have.been.calledOnce;
-            expect(client._display.autoscale).to.have.been.calledWith(70, 80);
-        });
-
-        it('should update the clipping setting when changing the property', function () {
-            client.clipViewport = true;
-
-            const spy = sinon.spy(client._display, "clipViewport", ["set"]);
-
-            client.scaleViewport = false;
-            expect(spy.set).to.have.been.calledOnce;
-            expect(spy.set).to.have.been.calledWith(true);
-
-            spy.set.reset();
-
-            client.scaleViewport = true;
-            expect(spy.set).to.have.been.calledOnce;
-            expect(spy.set).to.have.been.calledWith(false);
-        });
-
-        it('should update the scaling when the container size changes', function () {
-            sinon.spy(client._display, "autoscale");
-
-            container.style.width = '40px';
-            container.style.height = '50px';
-            const event = new UIEvent('resize');
-            window.dispatchEvent(event);
-            clock.tick();
-
-            expect(client._display.autoscale).to.have.been.calledOnce;
-            expect(client._display.autoscale).to.have.been.calledWith(40, 50);
-        });
-
-        it('should update the scaling when the remote session resizes', function () {
-            // Simple ExtendedDesktopSize FBU message
-            const incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-                               0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0xfe, 0xcc,
-                               0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                               0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff,
-                               0x00, 0x00, 0x00, 0x00 ];
-
-            sinon.spy(client._display, "autoscale");
-
-            client._sock._websocket._receive_data(new Uint8Array(incoming));
-
-            expect(client._display.autoscale).to.have.been.calledOnce;
-            expect(client._display.autoscale).to.have.been.calledWith(70, 80);
-        });
-
-        it('should not update the display scale factor if not scaling', function () {
-            client.scaleViewport = false;
-
-            sinon.spy(client._display, "autoscale");
-
-            container.style.width = '40px';
-            container.style.height = '50px';
-            const event = new UIEvent('resize');
-            window.dispatchEvent(event);
-            clock.tick();
-
-            expect(client._display.autoscale).to.not.have.been.called;
-        });
-    });
-
-    describe('Remote resize', function () {
-        let client;
-        beforeEach(function () {
-            client = make_rfb();
-            client._supportsSetDesktopSize = true;
-            client.resizeSession = true;
-            container.style.width = '70px';
-            container.style.height = '80px';
-            sinon.spy(RFB.messages, "setDesktopSize");
-        });
-
-        afterEach(function () {
-            RFB.messages.setDesktopSize.restore();
-        });
-
-        it('should only request a resize when turned on', function () {
-            client.resizeSession = false;
-            expect(RFB.messages.setDesktopSize).to.not.have.been.called;
-            client.resizeSession = true;
-            expect(RFB.messages.setDesktopSize).to.have.been.calledOnce;
-        });
-
-        it('should request a resize when initially connecting', function () {
-            // Simple ExtendedDesktopSize FBU message
-            const incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-                               0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
-                               0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                               0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
-                               0x00, 0x00, 0x00, 0x00 ];
-
-            // First message should trigger a resize
-
-            client._supportsSetDesktopSize = false;
-
-            client._sock._websocket._receive_data(new Uint8Array(incoming));
-
-            expect(RFB.messages.setDesktopSize).to.have.been.calledOnce;
-            expect(RFB.messages.setDesktopSize).to.have.been.calledWith(sinon.match.object, 70, 80, 0, 0);
-
-            RFB.messages.setDesktopSize.reset();
-
-            // Second message should not trigger a resize
-
-            client._sock._websocket._receive_data(new Uint8Array(incoming));
-
-            expect(RFB.messages.setDesktopSize).to.not.have.been.called;
-        });
-
-        it('should request a resize when the container resizes', function () {
-            container.style.width = '40px';
-            container.style.height = '50px';
-            const event = new UIEvent('resize');
-            window.dispatchEvent(event);
-            clock.tick(1000);
-
-            expect(RFB.messages.setDesktopSize).to.have.been.calledOnce;
-            expect(RFB.messages.setDesktopSize).to.have.been.calledWith(sinon.match.object, 40, 50, 0, 0);
-        });
-
-        it('should not resize until the container size is stable', function () {
-            container.style.width = '20px';
-            container.style.height = '30px';
-            const event1 = new UIEvent('resize');
-            window.dispatchEvent(event1);
-            clock.tick(400);
-
-            expect(RFB.messages.setDesktopSize).to.not.have.been.called;
-
-            container.style.width = '40px';
-            container.style.height = '50px';
-            const event2 = new UIEvent('resize');
-            window.dispatchEvent(event2);
-            clock.tick(400);
-
-            expect(RFB.messages.setDesktopSize).to.not.have.been.called;
-
-            clock.tick(200);
-
-            expect(RFB.messages.setDesktopSize).to.have.been.calledOnce;
-            expect(RFB.messages.setDesktopSize).to.have.been.calledWith(sinon.match.object, 40, 50, 0, 0);
-        });
-
-        it('should not resize when resize is disabled', function () {
-            client._resizeSession = false;
-
-            container.style.width = '40px';
-            container.style.height = '50px';
-            const event = new UIEvent('resize');
-            window.dispatchEvent(event);
-            clock.tick(1000);
-
-            expect(RFB.messages.setDesktopSize).to.not.have.been.called;
-        });
-
-        it('should not resize when resize is not supported', function () {
-            client._supportsSetDesktopSize = false;
-
-            container.style.width = '40px';
-            container.style.height = '50px';
-            const event = new UIEvent('resize');
-            window.dispatchEvent(event);
-            clock.tick(1000);
-
-            expect(RFB.messages.setDesktopSize).to.not.have.been.called;
-        });
-
-        it('should not resize when in view only mode', function () {
-            client._viewOnly = true;
-
-            container.style.width = '40px';
-            container.style.height = '50px';
-            const event = new UIEvent('resize');
-            window.dispatchEvent(event);
-            clock.tick(1000);
-
-            expect(RFB.messages.setDesktopSize).to.not.have.been.called;
-        });
-
-        it('should not try to override a server resize', function () {
-            // Simple ExtendedDesktopSize FBU message
-            const incoming = [ 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
-                               0x00, 0x04, 0x00, 0x04, 0xff, 0xff, 0xfe, 0xcc,
-                               0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-                               0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x04,
-                               0x00, 0x00, 0x00, 0x00 ];
-
-            client._sock._websocket._receive_data(new Uint8Array(incoming));
-
-            expect(RFB.messages.setDesktopSize).to.not.have.been.called;
-        });
-    });
-
-    describe('Misc Internals', function () {
-        describe('#_updateConnectionState', function () {
-            let client;
-            beforeEach(function () {
-                client = make_rfb();
-            });
-
-            it('should clear the disconnect timer if the state is not "disconnecting"', function () {
-                const spy = sinon.spy();
-                client._disconnTimer = setTimeout(spy, 50);
-                client._rfb_connection_state = 'connecting';
-                client._updateConnectionState('connected');
-                this.clock.tick(51);
-                expect(spy).to.not.have.been.called;
-                expect(client._disconnTimer).to.be.null;
-            });
-
-            it('should set the rfb_connection_state', function () {
-                client._rfb_connection_state = 'connecting';
-                client._updateConnectionState('connected');
-                expect(client._rfb_connection_state).to.equal('connected');
-            });
-
-            it('should not change the state when we are disconnected', function () {
-                client.disconnect();
-                expect(client._rfb_connection_state).to.equal('disconnected');
-                client._updateConnectionState('connecting');
-                expect(client._rfb_connection_state).to.not.equal('connecting');
-            });
-
-            it('should ignore state changes to the same state', function () {
-                const connectSpy = sinon.spy();
-                client.addEventListener("connect", connectSpy);
-
-                expect(client._rfb_connection_state).to.equal('connected');
-                client._updateConnectionState('connected');
-                expect(connectSpy).to.not.have.been.called;
-
-                client.disconnect();
-
-                const disconnectSpy = sinon.spy();
-                client.addEventListener("disconnect", disconnectSpy);
-
-                expect(client._rfb_connection_state).to.equal('disconnected');
-                client._updateConnectionState('disconnected');
-                expect(disconnectSpy).to.not.have.been.called;
-            });
-
-            it('should ignore illegal state changes', function () {
-                const spy = sinon.spy();
-                client.addEventListener("disconnect", spy);
-                client._updateConnectionState('disconnected');
-                expect(client._rfb_connection_state).to.not.equal('disconnected');
-                expect(spy).to.not.have.been.called;
-            });
-        });
-
-        describe('#_fail', function () {
-            let client;
-            beforeEach(function () {
-                client = make_rfb();
-            });
-
-            it('should close the WebSocket connection', function () {
-                sinon.spy(client._sock, 'close');
-                client._fail();
-                expect(client._sock.close).to.have.been.calledOnce;
-            });
-
-            it('should transition to disconnected', function () {
-                sinon.spy(client, '_updateConnectionState');
-                client._fail();
-                this.clock.tick(2000);
-                expect(client._updateConnectionState).to.have.been.called;
-                expect(client._rfb_connection_state).to.equal('disconnected');
-            });
-
-            it('should set clean_disconnect variable', function () {
-                client._rfb_clean_disconnect = true;
-                client._rfb_connection_state = 'connected';
-                client._fail();
-                expect(client._rfb_clean_disconnect).to.be.false;
-            });
-
-            it('should result in disconnect event with clean set to false', function () {
-                client._rfb_connection_state = 'connected';
-                const spy = sinon.spy();
-                client.addEventListener("disconnect", spy);
-                client._fail();
-                this.clock.tick(2000);
-                expect(spy).to.have.been.calledOnce;
-                expect(spy.args[0][0].detail.clean).to.be.false;
-            });
-
-        });
-    });
-
-    describe('Connection States', function () {
-        describe('connecting', function () {
-            it('should open the websocket connection', function () {
-                const client = new RFB(document.createElement('div'),
-                                       'ws://HOST:8675/PATH');
-                sinon.spy(client._sock, 'open');
-                this.clock.tick();
-                expect(client._sock.open).to.have.been.calledOnce;
-            });
-        });
-
-        describe('connected', function () {
-            let client;
-            beforeEach(function () {
-                client = make_rfb();
-            });
-
-            it('should result in a connect event if state becomes connected', function () {
-                const spy = sinon.spy();
-                client.addEventListener("connect", spy);
-                client._rfb_connection_state = 'connecting';
-                client._updateConnectionState('connected');
-                expect(spy).to.have.been.calledOnce;
-            });
-
-            it('should not result in a connect event if the state is not "connected"', function () {
-                const spy = sinon.spy();
-                client.addEventListener("connect", spy);
-                client._sock._websocket.open = () => {};  // explicitly don't call onopen
-                client._updateConnectionState('connecting');
-                expect(spy).to.not.have.been.called;
-            });
-        });
-
-        describe('disconnecting', function () {
-            let client;
-            beforeEach(function () {
-                client = make_rfb();
-            });
-
-            it('should force disconnect if we do not call Websock.onclose within the disconnection timeout', function () {
-                sinon.spy(client, '_updateConnectionState');
-                client._sock._websocket.close = () => {};  // explicitly don't call onclose
-                client._updateConnectionState('disconnecting');
-                this.clock.tick(3 * 1000);
-                expect(client._updateConnectionState).to.have.been.calledTwice;
-                expect(client._rfb_disconnect_reason).to.not.equal("");
-                expect(client._rfb_connection_state).to.equal("disconnected");
-            });
-
-            it('should not fail if Websock.onclose gets called within the disconnection timeout', function () {
-                client._updateConnectionState('disconnecting');
-                this.clock.tick(3 * 1000 / 2);
-                client._sock._websocket.close();
-                this.clock.tick(3 * 1000 / 2 + 1);
-                expect(client._rfb_connection_state).to.equal('disconnected');
-            });
-
-            it('should close the WebSocket connection', function () {
-                sinon.spy(client._sock, 'close');
-                client._updateConnectionState('disconnecting');
-                expect(client._sock.close).to.have.been.calledOnce;
-            });
-
-            it('should not result in a disconnect event', function () {
-                const spy = sinon.spy();
-                client.addEventListener("disconnect", spy);
-                client._sock._websocket.close = () => {};  // explicitly don't call onclose
-                client._updateConnectionState('disconnecting');
-                expect(spy).to.not.have.been.called;
-            });
-        });
-
-        describe('disconnected', function () {
-            let client;
-            beforeEach(function () {
-                client = new RFB(document.createElement('div'), 'ws://HOST:8675/PATH');
-            });
-
-            it('should result in a disconnect event if state becomes "disconnected"', function () {
-                const spy = sinon.spy();
-                client.addEventListener("disconnect", spy);
-                client._rfb_connection_state = 'disconnecting';
-                client._updateConnectionState('disconnected');
-                expect(spy).to.have.been.calledOnce;
-                expect(spy.args[0][0].detail.clean).to.be.true;
-            });
-
-            it('should result in a disconnect event without msg when no reason given', function () {
-                const spy = sinon.spy();
-                client.addEventListener("disconnect", spy);
-                client._rfb_connection_state = 'disconnecting';
-                client._rfb_disconnect_reason = "";
-                client._updateConnectionState('disconnected');
-                expect(spy).to.have.been.calledOnce;
-                expect(spy.args[0].length).to.equal(1);
-            });
-        });
-    });
-
-    describe('Protocol Initialization States', function () {
-        let client;
-        beforeEach(function () {
-            client = make_rfb();
-            client._rfb_connection_state = 'connecting';
-        });
-
-        describe('ProtocolVersion', function () {
-            function send_ver(ver, client) {
-                const arr = new Uint8Array(12);
-                for (let i = 0; i < ver.length; i++) {
-                    arr[i+4] = ver.charCodeAt(i);
-                }
-                arr[0] = 'R'; arr[1] = 'F'; arr[2] = 'B'; arr[3] = ' ';
-                arr[11] = '\n';
-                client._sock._websocket._receive_data(arr);
-            }
-
-            describe('version parsing', function () {
-                it('should interpret version 003.003 as version 3.3', function () {
-                    send_ver('003.003', client);
-                    expect(client._rfb_version).to.equal(3.3);
-                });
-
-                it('should interpret version 003.006 as version 3.3', function () {
-                    send_ver('003.006', client);
-                    expect(client._rfb_version).to.equal(3.3);
-                });
-
-                it('should interpret version 003.889 as version 3.3', function () {
-                    send_ver('003.889', client);
-                    expect(client._rfb_version).to.equal(3.3);
-                });
-
-                it('should interpret version 003.007 as version 3.7', function () {
-                    send_ver('003.007', client);
-                    expect(client._rfb_version).to.equal(3.7);
-                });
-
-                it('should interpret version 003.008 as version 3.8', function () {
-                    send_ver('003.008', client);
-                    expect(client._rfb_version).to.equal(3.8);
-                });
-
-                it('should interpret version 004.000 as version 3.8', function () {
-                    send_ver('004.000', client);
-                    expect(client._rfb_version).to.equal(3.8);
-                });
-
-                it('should interpret version 004.001 as version 3.8', function () {
-                    send_ver('004.001', client);
-                    expect(client._rfb_version).to.equal(3.8);
-                });
-
-                it('should interpret version 005.000 as version 3.8', function () {
-                    send_ver('005.000', client);
-                    expect(client._rfb_version).to.equal(3.8);
-                });
-
-                it('should fail on an invalid version', function () {
-                    sinon.spy(client, "_fail");
-                    send_ver('002.000', client);
-                    expect(client._fail).to.have.been.calledOnce;
-                });
-            });
-
-            it('should send back the interpreted version', function () {
-                send_ver('004.000', client);
-
-                const expected_str = 'RFB 003.008\n';
-                const expected = [];
-                for (let i = 0; i < expected_str.length; i++) {
-                    expected[i] = expected_str.charCodeAt(i);
-                }
-
-                expect(client._sock).to.have.sent(new Uint8Array(expected));
-            });
-
-            it('should transition to the Security state on successful negotiation', function () {
-                send_ver('003.008', client);
-                expect(client._rfb_init_state).to.equal('Security');
-            });
-
-            describe('Repeater', function () {
-                beforeEach(function () {
-                    client = make_rfb('wss://host:8675', { repeaterID: "12345" });
-                    client._rfb_connection_state = 'connecting';
-                });
-
-                it('should interpret version 000.000 as a repeater', function () {
-                    send_ver('000.000', client);
-                    expect(client._rfb_version).to.equal(0);
-
-                    const sent_data = client._sock._websocket._get_sent_data();
-                    expect(new Uint8Array(sent_data.buffer, 0, 9)).to.array.equal(new Uint8Array([73, 68, 58, 49, 50, 51, 52, 53, 0]));
-                    expect(sent_data).to.have.length(250);
-                });
-
-                it('should handle two step repeater negotiation', function () {
-                    send_ver('000.000', client);
-                    send_ver('003.008', client);
-                    expect(client._rfb_version).to.equal(3.8);
-                });
-            });
-        });
-
-        describe('Security', function () {
-            beforeEach(function () {
-                client._rfb_init_state = 'Security';
-            });
-
-            it('should simply receive the auth scheme when for versions < 3.7', function () {
-                client._rfb_version = 3.6;
-                const auth_scheme_raw = [1, 2, 3, 4];
-                const auth_scheme = (auth_scheme_raw[0] << 24) + (auth_scheme_raw[1] << 16) +
-                                  (auth_scheme_raw[2] << 8) + auth_scheme_raw[3];
-                client._sock._websocket._receive_data(new Uint8Array(auth_scheme_raw));
-                expect(client._rfb_auth_scheme).to.equal(auth_scheme);
-            });
-
-            it('should prefer no authentication is possible', function () {
-                client._rfb_version = 3.7;
-                const auth_schemes = [2, 1, 3];
-                client._sock._websocket._receive_data(new Uint8Array(auth_schemes));
-                expect(client._rfb_auth_scheme).to.equal(1);
-                expect(client._sock).to.have.sent(new Uint8Array([1, 1]));
-            });
-
-            it('should choose for the most prefered scheme possible for versions >= 3.7', function () {
-                client._rfb_version = 3.7;
-                const auth_schemes = [2, 22, 16];
-                client._sock._websocket._receive_data(new Uint8Array(auth_schemes));
-                expect(client._rfb_auth_scheme).to.equal(22);
-                expect(client._sock).to.have.sent(new Uint8Array([22]));
-            });
-
-            it('should fail if there are no supported schemes for versions >= 3.7', function () {
-                sinon.spy(client, "_fail");
-                client._rfb_version = 3.7;
-                const auth_schemes = [1, 32];
-                client._sock._websocket._receive_data(new Uint8Array(auth_schemes));
-                expect(client._fail).to.have.been.calledOnce;
-            });
-
-            it('should fail with the appropriate message if no types are sent for versions >= 3.7', function () {
-                client._rfb_version = 3.7;
-                const failure_data = [0, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
-                sinon.spy(client, '_fail');
-                client._sock._websocket._receive_data(new Uint8Array(failure_data));
-
-                expect(client._fail).to.have.been.calledOnce;
-                expect(client._fail).to.have.been.calledWith(
-                    'Security negotiation failed on no security types (reason: whoops)');
-            });
-
-            it('should transition to the Authentication state and continue on successful negotiation', function () {
-                client._rfb_version = 3.7;
-                const auth_schemes = [1, 1];
-                client._negotiate_authentication = sinon.spy();
-                client._sock._websocket._receive_data(new Uint8Array(auth_schemes));
-                expect(client._rfb_init_state).to.equal('Authentication');
-                expect(client._negotiate_authentication).to.have.been.calledOnce;
-            });
-        });
-
-        describe('Authentication', function () {
-            beforeEach(function () {
-                client._rfb_init_state = 'Security';
-            });
-
-            function send_security(type, cl) {
-                cl._sock._websocket._receive_data(new Uint8Array([1, type]));
-            }
-
-            it('should fail on auth scheme 0 (pre 3.7) with the given message', function () {
-                client._rfb_version = 3.6;
-                const err_msg = "Whoopsies";
-                const data = [0, 0, 0, 0];
-                const err_len = err_msg.length;
-                push32(data, err_len);
-                for (let i = 0; i < err_len; i++) {
-                    data.push(err_msg.charCodeAt(i));
-                }
-
-                sinon.spy(client, '_fail');
-                client._sock._websocket._receive_data(new Uint8Array(data));
-                expect(client._fail).to.have.been.calledWith(
-                    'Security negotiation failed on authentication scheme (reason: Whoopsies)');
-            });
-
-            it('should transition straight to SecurityResult on "no auth" (1) for versions >= 3.8', function () {
-                client._rfb_version = 3.8;
-                send_security(1, client);
-                expect(client._rfb_init_state).to.equal('SecurityResult');
-            });
-
-            it('should transition straight to ServerInitialisation on "no auth" for versions < 3.8', function () {
-                client._rfb_version = 3.7;
-                send_security(1, client);
-                expect(client._rfb_init_state).to.equal('ServerInitialisation');
-            });
-
-            it('should fail on an unknown auth scheme', function () {
-                sinon.spy(client, "_fail");
-                client._rfb_version = 3.8;
-                send_security(57, client);
-                expect(client._fail).to.have.been.calledOnce;
-            });
-
-            describe('VNC Authentication (type 2) Handler', function () {
-                beforeEach(function () {
-                    client._rfb_init_state = 'Security';
-                    client._rfb_version = 3.8;
-                });
-
-                it('should fire the credentialsrequired event if missing a password', function () {
-                    const spy = sinon.spy();
-                    client.addEventListener("credentialsrequired", spy);
-                    send_security(2, client);
-
-                    const challenge = [];
-                    for (let i = 0; i < 16; i++) { challenge[i] = i; }
-                    client._sock._websocket._receive_data(new Uint8Array(challenge));
-
-                    expect(client._rfb_credentials).to.be.empty;
-                    expect(spy).to.have.been.calledOnce;
-                    expect(spy.args[0][0].detail.types).to.have.members(["password"]);
-                });
-
-                it('should encrypt the password with DES and then send it back', function () {
-                    client._rfb_credentials = { password: 'passwd' };
-                    send_security(2, client);
-                    client._sock._websocket._get_sent_data(); // skip the choice of auth reply
-
-                    const challenge = [];
-                    for (let i = 0; i < 16; i++) { challenge[i] = i; }
-                    client._sock._websocket._receive_data(new Uint8Array(challenge));
-
-                    const des_pass = RFB.genDES('passwd', challenge);
-                    expect(client._sock).to.have.sent(new Uint8Array(des_pass));
-                });
-
-                it('should transition to SecurityResult immediately after sending the password', function () {
-                    client._rfb_credentials = { password: 'passwd' };
-                    send_security(2, client);
-
-                    const challenge = [];
-                    for (let i = 0; i < 16; i++) { challenge[i] = i; }
-                    client._sock._websocket._receive_data(new Uint8Array(challenge));
-
-                    expect(client._rfb_init_state).to.equal('SecurityResult');
-                });
-            });
-
-            describe('XVP Authentication (type 22) Handler', function () {
-                beforeEach(function () {
-                    client._rfb_init_state = 'Security';
-                    client._rfb_version = 3.8;
-                });
-
-                it('should fall through to standard VNC authentication upon completion', function () {
-                    client._rfb_credentials = { username: 'user',
-                                                target: 'target',
-                                                password: 'password' };
-                    client._negotiate_std_vnc_auth = sinon.spy();
-                    send_security(22, client);
-                    expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
-                });
-
-                it('should fire the credentialsrequired event if all credentials are missing', function () {
-                    const spy = sinon.spy();
-                    client.addEventListener("credentialsrequired", spy);
-                    client._rfb_credentials = {};
-                    send_security(22, client);
-
-                    expect(client._rfb_credentials).to.be.empty;
-                    expect(spy).to.have.been.calledOnce;
-                    expect(spy.args[0][0].detail.types).to.have.members(["username", "password", "target"]);
-                });
-
-                it('should fire the credentialsrequired event if some credentials are missing', function () {
-                    const spy = sinon.spy();
-                    client.addEventListener("credentialsrequired", spy);
-                    client._rfb_credentials = { username: 'user',
-                                                target: 'target' };
-                    send_security(22, client);
-
-                    expect(spy).to.have.been.calledOnce;
-                    expect(spy.args[0][0].detail.types).to.have.members(["username", "password", "target"]);
-                });
-
-                it('should send user and target separately', function () {
-                    client._rfb_credentials = { username: 'user',
-                                                target: 'target',
-                                                password: 'password' };
-                    client._negotiate_std_vnc_auth = sinon.spy();
-
-                    send_security(22, client);
-
-                    const expected = [22, 4, 6]; // auth selection, len user, len target
-                    for (let i = 0; i < 10; i++) { expected[i+3] = 'usertarget'.charCodeAt(i); }
-
-                    expect(client._sock).to.have.sent(new Uint8Array(expected));
-                });
-            });
-
-            describe('TightVNC Authentication (type 16) Handler', function () {
-                beforeEach(function () {
-                    client._rfb_init_state = 'Security';
-                    client._rfb_version = 3.8;
-                    send_security(16, client);
-                    client._sock._websocket._get_sent_data();  // skip the security reply
-                });
-
-                function send_num_str_pairs(pairs, client) {
-                    const data = [];
-                    push32(data, pairs.length);
-
-                    for (let i = 0; i < pairs.length; i++) {
-                        push32(data, pairs[i][0]);
-                        for (let j = 0; j < 4; j++) {
-                            data.push(pairs[i][1].charCodeAt(j));
-                        }
-                        for (let j = 0; j < 8; j++) {
-                            data.push(pairs[i][2].charCodeAt(j));
-                        }
-                    }
-
-                    client._sock._websocket._receive_data(new Uint8Array(data));
-                }
-
-                it('should skip tunnel negotiation if no tunnels are requested', function () {
-                    client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0]));
-                    expect(client._rfb_tightvnc).to.be.true;
-                });
-
-                it('should fail if no supported tunnels are listed', function () {
-                    sinon.spy(client, "_fail");
-                    send_num_str_pairs([[123, 'OTHR', 'SOMETHNG']], client);
-                    expect(client._fail).to.have.been.calledOnce;
-                });
-
-                it('should choose the notunnel tunnel type', function () {
-                    send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL'], [123, 'OTHR', 'SOMETHNG']], client);
-                    expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 0]));
-                });
-
-                it('should choose the notunnel tunnel type for Siemens devices', function () {
-                    send_num_str_pairs([[1, 'SICR', 'SCHANNEL'], [2, 'SICR', 'SCHANLPW']], client);
-                    expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 0]));
-                });
-
-                it('should continue to sub-auth negotiation after tunnel negotiation', function () {
-                    send_num_str_pairs([[0, 'TGHT', 'NOTUNNEL']], client);
-                    client._sock._websocket._get_sent_data();  // skip the tunnel choice here
-                    send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client);
-                    expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 1]));
-                    expect(client._rfb_init_state).to.equal('SecurityResult');
-                });
-
-                /*it('should attempt to use VNC auth over no auth when possible', function () {
-                    client._rfb_tightvnc = true;
-                    client._negotiate_std_vnc_auth = sinon.spy();
-                    send_num_str_pairs([[1, 'STDV', 'NOAUTH__'], [2, 'STDV', 'VNCAUTH_']], client);
-                    expect(client._sock).to.have.sent([0, 0, 0, 1]);
-                    expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
-                    expect(client._rfb_auth_scheme).to.equal(2);
-                });*/ // while this would make sense, the original code doesn't actually do this
-
-                it('should accept the "no auth" auth type and transition to SecurityResult', function () {
-                    client._rfb_tightvnc = true;
-                    send_num_str_pairs([[1, 'STDV', 'NOAUTH__']], client);
-                    expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 1]));
-                    expect(client._rfb_init_state).to.equal('SecurityResult');
-                });
-
-                it('should accept VNC authentication and transition to that', function () {
-                    client._rfb_tightvnc = true;
-                    client._negotiate_std_vnc_auth = sinon.spy();
-                    send_num_str_pairs([[2, 'STDV', 'VNCAUTH__']], client);
-                    expect(client._sock).to.have.sent(new Uint8Array([0, 0, 0, 2]));
-                    expect(client._negotiate_std_vnc_auth).to.have.been.calledOnce;
-                    expect(client._rfb_auth_scheme).to.equal(2);
-                });
-
-                it('should fail if there are no supported auth types', function () {
-                    sinon.spy(client, "_fail");
-                    client._rfb_tightvnc = true;
-                    send_num_str_pairs([[23, 'stdv', 'badval__']], client);
-                    expect(client._fail).to.have.been.calledOnce;
-                });
-            });
-        });
-
-        describe('SecurityResult', function () {
-            beforeEach(function () {
-                client._rfb_init_state = 'SecurityResult';
-            });
-
-            it('should fall through to ServerInitialisation on a response code of 0', function () {
-                client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0]));
-                expect(client._rfb_init_state).to.equal('ServerInitialisation');
-            });
-
-            it('should fail on an error code of 1 with the given message for versions >= 3.8', function () {
-                client._rfb_version = 3.8;
-                sinon.spy(client, '_fail');
-                const failure_data = [0, 0, 0, 1, 0, 0, 0, 6, 119, 104, 111, 111, 112, 115];
-                client._sock._websocket._receive_data(new Uint8Array(failure_data));
-                expect(client._fail).to.have.been.calledWith(
-                    'Security negotiation failed on security result (reason: whoops)');
-            });
-
-            it('should fail on an error code of 1 with a standard message for version < 3.8', function () {
-                sinon.spy(client, '_fail');
-                client._rfb_version = 3.7;
-                client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 1]));
-                expect(client._fail).to.have.been.calledWith(
-                    'Security handshake failed');
-            });
-
-            it('should result in securityfailure event when receiving a non zero status', function () {
-                const spy = sinon.spy();
-                client.addEventListener("securityfailure", spy);
-                client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 2]));
-                expect(spy).to.have.been.calledOnce;
-                expect(spy.args[0][0].detail.status).to.equal(2);
-            });
-
-            it('should include reason when provided in securityfailure event', function () {
-                client._rfb_version = 3.8;
-                const spy = sinon.spy();
-                client.addEventListener("securityfailure", spy);
-                const failure_data = [0, 0, 0, 1, 0, 0, 0, 12, 115, 117, 99, 104,
-                                      32, 102, 97, 105, 108, 117, 114, 101];
-                client._sock._websocket._receive_data(new Uint8Array(failure_data));
-                expect(spy.args[0][0].detail.status).to.equal(1);
-                expect(spy.args[0][0].detail.reason).to.equal('such failure');
-            });
-
-            it('should not include reason when length is zero in securityfailure event', function () {
-                client._rfb_version = 3.9;
-                const spy = sinon.spy();
-                client.addEventListener("securityfailure", spy);
-                const failure_data = [0, 0, 0, 1, 0, 0, 0, 0];
-                client._sock._websocket._receive_data(new Uint8Array(failure_data));
-                expect(spy.args[0][0].detail.status).to.equal(1);
-                expect('reason' in spy.args[0][0].detail).to.be.false;
-            });
-
-            it('should not include reason in securityfailure event for version < 3.8', function () {
-                client._rfb_version = 3.6;
-                const spy = sinon.spy();
-                client.addEventListener("securityfailure", spy);
-                client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 2]));
-                expect(spy.args[0][0].detail.status).to.equal(2);
-                expect('reason' in spy.args[0][0].detail).to.be.false;
-            });
-        });
-
-        describe('ClientInitialisation', function () {
-            it('should transition to the ServerInitialisation state', function () {
-                const client = make_rfb();
-                client._rfb_connection_state = 'connecting';
-                client._rfb_init_state = 'SecurityResult';
-                client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0]));
-                expect(client._rfb_init_state).to.equal('ServerInitialisation');
-            });
-
-            it('should send 1 if we are in shared mode', function () {
-                const client = make_rfb('wss://host:8675', { shared: true });
-                client._rfb_connection_state = 'connecting';
-                client._rfb_init_state = 'SecurityResult';
-                client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0]));
-                expect(client._sock).to.have.sent(new Uint8Array([1]));
-            });
-
-            it('should send 0 if we are not in shared mode', function () {
-                const client = make_rfb('wss://host:8675', { shared: false });
-                client._rfb_connection_state = 'connecting';
-                client._rfb_init_state = 'SecurityResult';
-                client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 0]));
-                expect(client._sock).to.have.sent(new Uint8Array([0]));
-            });
-        });
-
-        describe('ServerInitialisation', function () {
-            beforeEach(function () {
-                client._rfb_init_state = 'ServerInitialisation';
-            });
-
-            function send_server_init(opts, client) {
-                const full_opts = { width: 10, height: 12, bpp: 24, depth: 24, big_endian: 0,
-                                    true_color: 1, red_max: 255, green_max: 255, blue_max: 255,
-                                    red_shift: 16, green_shift: 8, blue_shift: 0, name: 'a name' };
-                for (let opt in opts) {
-                    full_opts[opt] = opts[opt];
-                }
-                const data = [];
-
-                push16(data, full_opts.width);
-                push16(data, full_opts.height);
-
-                data.push(full_opts.bpp);
-                data.push(full_opts.depth);
-                data.push(full_opts.big_endian);
-                data.push(full_opts.true_color);
-
-                push16(data, full_opts.red_max);
-                push16(data, full_opts.green_max);
-                push16(data, full_opts.blue_max);
-                push8(data, full_opts.red_shift);
-                push8(data, full_opts.green_shift);
-                push8(data, full_opts.blue_shift);
-
-                // padding
-                push8(data, 0);
-                push8(data, 0);
-                push8(data, 0);
-
-                client._sock._websocket._receive_data(new Uint8Array(data));
-
-                const name_data = [];
-                push32(name_data, full_opts.name.length);
-                for (let i = 0; i < full_opts.name.length; i++) {
-                    name_data.push(full_opts.name.charCodeAt(i));
-                }
-                client._sock._websocket._receive_data(new Uint8Array(name_data));
-            }
-
-            it('should set the framebuffer width and height', function () {
-                send_server_init({ width: 32, height: 84 }, client);
-                expect(client._fb_width).to.equal(32);
-                expect(client._fb_height).to.equal(84);
-            });
-
-            // NB(sross): we just warn, not fail, for endian-ness and shifts, so we don't test them
-
-            it('should set the framebuffer name and call the callback', function () {
-                const spy = sinon.spy();
-                client.addEventListener("desktopname", spy);
-                send_server_init({ name: 'some name' }, client);
-
-                expect(client._fb_name).to.equal('some name');
-                expect(spy).to.have.been.calledOnce;
-                expect(spy.args[0][0].detail.name).to.equal('some name');
-            });
-
-            it('should handle the extended init message of the tight encoding', function () {
-                // NB(sross): we don't actually do anything with it, so just test that we can
-                //            read it w/o throwing an error
-                client._rfb_tightvnc = true;
-                send_server_init({}, client);
-
-                const tight_data = [];
-                push16(tight_data, 1);
-                push16(tight_data, 2);
-                push16(tight_data, 3);
-                push16(tight_data, 0);
-                for (let i = 0; i < 16 + 32 + 48; i++) {
-                    tight_data.push(i);
-                }
-                client._sock._websocket._receive_data(new Uint8Array(tight_data));
-
-                expect(client._rfb_connection_state).to.equal('connected');
-            });
-
-            it('should resize the display', function () {
-                sinon.spy(client._display, 'resize');
-                send_server_init({ width: 27, height: 32 }, client);
-
-                expect(client._display.resize).to.have.been.calledOnce;
-                expect(client._display.resize).to.have.been.calledWith(27, 32);
-            });
-
-            it('should grab the mouse and keyboard', function () {
-                sinon.spy(client._keyboard, 'grab');
-                sinon.spy(client._mouse, 'grab');
-                send_server_init({}, client);
-                expect(client._keyboard.grab).to.have.been.calledOnce;
-                expect(client._mouse.grab).to.have.been.calledOnce;
-            });
-
-            describe('Initial Update Request', function () {
-                beforeEach(function () {
-                    sinon.spy(RFB.messages, "pixelFormat");
-                    sinon.spy(RFB.messages, "clientEncodings");
-                    sinon.spy(RFB.messages, "fbUpdateRequest");
-                });
-
-                afterEach(function () {
-                    RFB.messages.pixelFormat.restore();
-                    RFB.messages.clientEncodings.restore();
-                    RFB.messages.fbUpdateRequest.restore();
-                });
-
-                // TODO(directxman12): test the various options in this configuration matrix
-                it('should reply with the pixel format, client encodings, and initial update request', function () {
-                    send_server_init({ width: 27, height: 32 }, client);
-
-                    expect(RFB.messages.pixelFormat).to.have.been.calledOnce;
-                    expect(RFB.messages.pixelFormat).to.have.been.calledWith(client._sock, 24, true);
-                    expect(RFB.messages.pixelFormat).to.have.been.calledBefore(RFB.messages.clientEncodings);
-                    expect(RFB.messages.clientEncodings).to.have.been.calledOnce;
-                    expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.include(encodings.encodingTight);
-                    expect(RFB.messages.clientEncodings).to.have.been.calledBefore(RFB.messages.fbUpdateRequest);
-                    expect(RFB.messages.fbUpdateRequest).to.have.been.calledOnce;
-                    expect(RFB.messages.fbUpdateRequest).to.have.been.calledWith(client._sock, false, 0, 0, 27, 32);
-                });
-
-                it('should reply with restricted settings for Intel AMT servers', function () {
-                    send_server_init({ width: 27, height: 32, name: "Intel(r) AMT KVM"}, client);
-
-                    expect(RFB.messages.pixelFormat).to.have.been.calledOnce;
-                    expect(RFB.messages.pixelFormat).to.have.been.calledWith(client._sock, 8, true);
-                    expect(RFB.messages.pixelFormat).to.have.been.calledBefore(RFB.messages.clientEncodings);
-                    expect(RFB.messages.clientEncodings).to.have.been.calledOnce;
-                    expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.not.include(encodings.encodingTight);
-                    expect(RFB.messages.clientEncodings.getCall(0).args[1]).to.not.include(encodings.encodingHextile);
-                    expect(RFB.messages.clientEncodings).to.have.been.calledBefore(RFB.messages.fbUpdateRequest);
-                    expect(RFB.messages.fbUpdateRequest).to.have.been.calledOnce;
-                    expect(RFB.messages.fbUpdateRequest).to.have.been.calledWith(client._sock, false, 0, 0, 27, 32);
-                });
-            });
-
-            it('should transition to the "connected" state', function () {
-                send_server_init({}, client);
-                expect(client._rfb_connection_state).to.equal('connected');
-            });
-        });
-    });
-
-    describe('Protocol Message Processing After Completing Initialization', function () {
-        let client;
-
-        beforeEach(function () {
-            client = make_rfb();
-            client._fb_name = 'some device';
-            client._fb_width = 640;
-            client._fb_height = 20;
-        });
-
-        describe('Framebuffer Update Handling', function () {
-            const target_data_arr = [
-                0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
-                0x00, 0xff, 0x00, 255, 0xff, 0x00, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
-                0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255,
-                0xee, 0x00, 0xff, 255, 0x00, 0xee, 0xff, 255, 0xaa, 0xee, 0xff, 255, 0xab, 0xee, 0xff, 255
-            ];
-            let target_data;
-
-            const target_data_check_arr = [
-                0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
-                0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
-                0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
-                0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
-            ];
-            let target_data_check;
-
-            before(function () {
-                // NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray
-                target_data = new Uint8Array(target_data_arr);
-                target_data_check = new Uint8Array(target_data_check_arr);
-            });
-
-            function send_fbu_msg(rect_info, rect_data, client, rect_cnt) {
-                let data = [];
-
-                if (!rect_cnt || rect_cnt > -1) {
-                    // header
-                    data.push(0);  // msg type
-                    data.push(0);  // padding
-                    push16(data, rect_cnt || rect_data.length);
-                }
-
-                for (let i = 0; i < rect_data.length; i++) {
-                    if (rect_info[i]) {
-                        push16(data, rect_info[i].x);
-                        push16(data, rect_info[i].y);
-                        push16(data, rect_info[i].width);
-                        push16(data, rect_info[i].height);
-                        push32(data, rect_info[i].encoding);
-                    }
-                    data = data.concat(rect_data[i]);
-                }
-
-                client._sock._websocket._receive_data(new Uint8Array(data));
-            }
-
-            it('should send an update request if there is sufficient data', function () {
-                const expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: () => {}};
-                RFB.messages.fbUpdateRequest(expected_msg, true, 0, 0, 640, 20);
-
-                client._framebufferUpdate = () => true;
-                client._sock._websocket._receive_data(new Uint8Array([0]));
-
-                expect(client._sock).to.have.sent(expected_msg._sQ);
-            });
-
-            it('should not send an update request if we need more data', function () {
-                client._sock._websocket._receive_data(new Uint8Array([0]));
-                expect(client._sock._websocket._get_sent_data()).to.have.length(0);
-            });
-
-            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: () => {}};
-                RFB.messages.fbUpdateRequest(expected_msg, true, 0, 0, 640, 20);
-
-                // just enough to set FBU.rects
-                client._sock._websocket._receive_data(new Uint8Array([0, 0, 0, 3]));
-                expect(client._sock._websocket._get_sent_data()).to.have.length(0);
-
-                client._framebufferUpdate = function () { this._sock.rQskipBytes(1); return true; };  // we magically have enough data
-                // 247 should *not* be used as the message type here
-                client._sock._websocket._receive_data(new Uint8Array([247]));
-                expect(client._sock).to.have.sent(expected_msg._sQ);
-            });
-
-            it('should not send a request in continuous updates mode', function () {
-                client._enabledContinuousUpdates = true;
-                client._framebufferUpdate = () => true;
-                client._sock._websocket._receive_data(new Uint8Array([0]));
-
-                expect(client._sock._websocket._get_sent_data()).to.have.length(0);
-            });
-
-            it('should fail on an unsupported encoding', function () {
-                sinon.spy(client, "_fail");
-                const rect_info = { x: 8, y: 11, width: 27, height: 32, encoding: 234 };
-                send_fbu_msg([rect_info], [[]], client);
-                expect(client._fail).to.have.been.calledOnce;
-            });
-
-            it('should be able to pause and resume receiving rects if not enought data', function () {
-                // seed some initial data to copy
-                client._fb_width = 4;
-                client._fb_height = 4;
-                client._display.resize(4, 4);
-                client._display.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr.slice(0, 32)), 0);
-
-                const info = [{ x: 0, y: 2, width: 2, height: 2, encoding: 0x01},
-                              { x: 2, y: 2, width: 2, height: 2, encoding: 0x01}];
-                // data says [{ old_x: 2, old_y: 0 }, { old_x: 0, old_y: 0 }]
-                const rects = [[0, 2, 0, 0], [0, 0, 0, 0]];
-                send_fbu_msg([info[0]], [rects[0]], client, 2);
-                send_fbu_msg([info[1]], [rects[1]], client, -1);
-                expect(client._display).to.have.displayed(target_data_check);
-            });
-
-            describe('Message Encoding Handlers', function () {
-                beforeEach(function () {
-                    // a really small frame
-                    client._fb_width = 4;
-                    client._fb_height = 4;
-                    client._fb_depth = 24;
-                    client._display.resize(4, 4);
-                });
-
-                it('should handle the RAW encoding', function () {
-                    const info = [{ x: 0, y: 0, width: 2, height: 2, encoding: 0x00 },
-                                  { x: 2, y: 0, width: 2, height: 2, encoding: 0x00 },
-                                  { x: 0, y: 2, width: 4, height: 1, encoding: 0x00 },
-                                  { x: 0, y: 3, width: 4, height: 1, encoding: 0x00 }];
-                    // data is in bgrx
-                    const rects = [
-                        [0x00, 0x00, 0xff, 0, 0x00, 0xff, 0x00, 0, 0x00, 0xff, 0x00, 0, 0x00, 0x00, 0xff, 0],
-                        [0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0, 0xff, 0x00, 0x00, 0],
-                        [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0],
-                        [0xff, 0x00, 0xee, 0, 0xff, 0xee, 0x00, 0, 0xff, 0xee, 0xaa, 0, 0xff, 0xee, 0xab, 0]];
-                    send_fbu_msg(info, rects, client);
-                    expect(client._display).to.have.displayed(target_data);
-                });
-
-                it('should handle the RAW encoding in low colour mode', function () {
-                    const info = [{ x: 0, y: 0, width: 2, height: 2, encoding: 0x00 },
-                                  { x: 2, y: 0, width: 2, height: 2, encoding: 0x00 },
-                                  { x: 0, y: 2, width: 4, height: 1, encoding: 0x00 },
-                                  { x: 0, y: 3, width: 4, height: 1, encoding: 0x00 }];
-                    const rects = [
-                        [0x03, 0x03, 0x03, 0x03],
-                        [0x0c, 0x0c, 0x0c, 0x0c],
-                        [0x0c, 0x0c, 0x03, 0x03],
-                        [0x0c, 0x0c, 0x03, 0x03]];
-                    client._fb_depth = 8;
-                    send_fbu_msg(info, rects, client);
-                    expect(client._display).to.have.displayed(target_data_check);
-                });
-
-                it('should handle the COPYRECT encoding', function () {
-                    // seed some initial data to copy
-                    client._display.blitRgbxImage(0, 0, 4, 2, new Uint8Array(target_data_check_arr.slice(0, 32)), 0);
-
-                    const info = [{ x: 0, y: 2, width: 2, height: 2, encoding: 0x01},
-                                  { x: 2, y: 2, width: 2, height: 2, encoding: 0x01}];
-                    // data says [{ old_x: 0, old_y: 0 }, { old_x: 0, old_y: 0 }]
-                    const rects = [[0, 2, 0, 0], [0, 0, 0, 0]];
-                    send_fbu_msg(info, rects, client);
-                    expect(client._display).to.have.displayed(target_data_check);
-                });
-
-                // TODO(directxman12): for encodings with subrects, test resuming on partial send?
-                // TODO(directxman12): test rre_chunk_sz (related to above about subrects)?
-
-                it('should handle the RRE encoding', function () {
-                    const info = [{ x: 0, y: 0, width: 4, height: 4, encoding: 0x02 }];
-                    const rect = [];
-                    push32(rect, 2); // 2 subrects
-                    push32(rect, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
-                    rect.push(0xff); // becomes ff0000ff --> #0000FF color
-                    rect.push(0x00);
-                    rect.push(0x00);
-                    rect.push(0xff);
-                    push16(rect, 0); // x: 0
-                    push16(rect, 0); // y: 0
-                    push16(rect, 2); // width: 2
-                    push16(rect, 2); // height: 2
-                    rect.push(0xff); // becomes ff0000ff --> #0000FF color
-                    rect.push(0x00);
-                    rect.push(0x00);
-                    rect.push(0xff);
-                    push16(rect, 2); // x: 2
-                    push16(rect, 2); // y: 2
-                    push16(rect, 2); // width: 2
-                    push16(rect, 2); // height: 2
-
-                    send_fbu_msg(info, [rect], client);
-                    expect(client._display).to.have.displayed(target_data_check);
-                });
-
-                describe('the HEXTILE encoding handler', function () {
-                    it('should handle a tile with fg, bg specified, normal subrects', function () {
-                        const info = [{ x: 0, y: 0, width: 4, height: 4, encoding: 0x05 }];
-                        const rect = [];
-                        rect.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
-                        push32(rect, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
-                        rect.push(0xff); // becomes ff0000ff --> #0000FF fg color
-                        rect.push(0x00);
-                        rect.push(0x00);
-                        rect.push(0xff);
-                        rect.push(2); // 2 subrects
-                        rect.push(0); // x: 0, y: 0
-                        rect.push(1 | (1 << 4)); // width: 2, height: 2
-                        rect.push(2 | (2 << 4)); // x: 2, y: 2
-                        rect.push(1 | (1 << 4)); // width: 2, height: 2
-                        send_fbu_msg(info, [rect], client);
-                        expect(client._display).to.have.displayed(target_data_check);
-                    });
-
-                    it('should handle a raw tile', function () {
-                        const info = [{ x: 0, y: 0, width: 4, height: 4, encoding: 0x05 }];
-                        const rect = [];
-                        rect.push(0x01); // raw
-                        for (let i = 0; i < target_data.length; i += 4) {
-                            rect.push(target_data[i + 2]);
-                            rect.push(target_data[i + 1]);
-                            rect.push(target_data[i]);
-                            rect.push(target_data[i + 3]);
-                        }
-                        send_fbu_msg(info, [rect], client);
-                        expect(client._display).to.have.displayed(target_data);
-                    });
-
-                    it('should handle a tile with only bg specified (solid bg)', function () {
-                        const info = [{ x: 0, y: 0, width: 4, height: 4, encoding: 0x05 }];
-                        const rect = [];
-                        rect.push(0x02);
-                        push32(rect, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
-                        send_fbu_msg(info, [rect], client);
-
-                        const expected = [];
-                        for (let i = 0; i < 16; i++) { push32(expected, 0xff00ff); }
-                        expect(client._display).to.have.displayed(new Uint8Array(expected));
-                    });
-
-                    it('should handle a tile with only bg specified and an empty frame afterwards', function () {
-                        // set the width so we can have two tiles
-                        client._fb_width = 8;
-                        client._display.resize(8, 4);
-
-                        const info = [{ x: 0, y: 0, width: 32, height: 4, encoding: 0x05 }];
-
-                        const rect = [];
-
-                        // send a bg frame
-                        rect.push(0x02);
-                        push32(rect, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
-
-                        // send an empty frame
-                        rect.push(0x00);
-
-                        send_fbu_msg(info, [rect], client);
-
-                        const expected = [];
-                        for (let i = 0; i < 16; i++) { push32(expected, 0xff00ff); }     // rect 1: solid
-                        for (let i = 0; i < 16; i++) { push32(expected, 0xff00ff); }    // rect 2: same bkground color
-                        expect(client._display).to.have.displayed(new Uint8Array(expected));
-                    });
-
-                    it('should handle a tile with bg and coloured subrects', function () {
-                        const info = [{ x: 0, y: 0, width: 4, height: 4, encoding: 0x05 }];
-                        const rect = [];
-                        rect.push(0x02 | 0x08 | 0x10); // bg spec, anysubrects, colouredsubrects
-                        push32(rect, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
-                        rect.push(2); // 2 subrects
-                        rect.push(0xff); // becomes ff0000ff --> #0000FF fg color
-                        rect.push(0x00);
-                        rect.push(0x00);
-                        rect.push(0xff);
-                        rect.push(0); // x: 0, y: 0
-                        rect.push(1 | (1 << 4)); // width: 2, height: 2
-                        rect.push(0xff); // becomes ff0000ff --> #0000FF fg color
-                        rect.push(0x00);
-                        rect.push(0x00);
-                        rect.push(0xff);
-                        rect.push(2 | (2 << 4)); // x: 2, y: 2
-                        rect.push(1 | (1 << 4)); // width: 2, height: 2
-                        send_fbu_msg(info, [rect], client);
-                        expect(client._display).to.have.displayed(target_data_check);
-                    });
-
-                    it('should carry over fg and bg colors from the previous tile if not specified', function () {
-                        client._fb_width = 4;
-                        client._fb_height = 17;
-                        client._display.resize(4, 17);
-
-                        const info = [{ x: 0, y: 0, width: 4, height: 17, encoding: 0x05}];
-                        const rect = [];
-                        rect.push(0x02 | 0x04 | 0x08); // bg spec, fg spec, anysubrects
-                        push32(rect, 0xff00ff); // becomes 00ff00ff --> #00FF00 bg color
-                        rect.push(0xff); // becomes ff0000ff --> #0000FF fg color
-                        rect.push(0x00);
-                        rect.push(0x00);
-                        rect.push(0xff);
-                        rect.push(8); // 8 subrects
-                        for (let i = 0; i < 4; i++) {
-                            rect.push((0 << 4) | (i * 4)); // x: 0, y: i*4
-                            rect.push(1 | (1 << 4)); // width: 2, height: 2
-                            rect.push((2 << 4) | (i * 4 + 2)); // x: 2, y: i * 4 + 2
-                            rect.push(1 | (1 << 4)); // width: 2, height: 2
-                        }
-                        rect.push(0x08); // anysubrects
-                        rect.push(1); // 1 subrect
-                        rect.push(0); // x: 0, y: 0
-                        rect.push(1 | (1 << 4)); // width: 2, height: 2
-                        send_fbu_msg(info, [rect], client);
-
-                        let expected = [];
-                        for (let i = 0; i < 4; i++) { expected = expected.concat(target_data_check_arr); }
-                        expected = expected.concat(target_data_check_arr.slice(0, 16));
-                        expect(client._display).to.have.displayed(new Uint8Array(expected));
-                    });
-
-                    it('should fail on an invalid subencoding', function () {
-                        sinon.spy(client, "_fail");
-                        const info = [{ x: 0, y: 0, width: 4, height: 4, encoding: 0x05 }];
-                        const rects = [[45]];  // an invalid subencoding
-                        send_fbu_msg(info, rects, client);
-                        expect(client._fail).to.have.been.calledOnce;
-                    });
-                });
-
-                it.skip('should handle the TIGHT encoding', function () {
-                    // TODO(directxman12): test this
-                });
-
-                it.skip('should handle the TIGHT_PNG encoding', function () {
-                    // TODO(directxman12): test this
-                });
-
-                it('should handle the DesktopSize pseduo-encoding', function () {
-                    sinon.spy(client._display, 'resize');
-                    send_fbu_msg([{ x: 0, y: 0, width: 20, height: 50, encoding: -223 }], [[]], client);
-
-                    expect(client._fb_width).to.equal(20);
-                    expect(client._fb_height).to.equal(50);
-
-                    expect(client._display.resize).to.have.been.calledOnce;
-                    expect(client._display.resize).to.have.been.calledWith(20, 50);
-                });
-
-                describe('the ExtendedDesktopSize pseudo-encoding handler', function () {
-                    beforeEach(function () {
-                        // a really small frame
-                        client._fb_width = 4;
-                        client._fb_height = 4;
-                        client._display.resize(4, 4);
-                        sinon.spy(client._display, 'resize');
-                    });
-
-                    function make_screen_data(nr_of_screens) {
-                        const data = [];
-                        push8(data, nr_of_screens);   // number-of-screens
-                        push8(data, 0);               // padding
-                        push16(data, 0);              // padding
-                        for (let i=0; i<nr_of_screens; i += 1) {
-                            push32(data, 0);  // id
-                            push16(data, 0);  // x-position
-                            push16(data, 0);  // y-position
-                            push16(data, 20); // width
-                            push16(data, 50); // height
-                            push32(data, 0);  // flags
-                        }
-                        return data;
-                    }
-
-                    it('should handle a resize requested by this client', function () {
-                        const reason_for_change = 1; // requested by this client
-                        const status_code       = 0; // No error
-
-                        send_fbu_msg([{ x: reason_for_change, y: status_code,
-                                        width: 20, height: 50, encoding: -308 }],
-                                     make_screen_data(1), client);
-
-                        expect(client._fb_width).to.equal(20);
-                        expect(client._fb_height).to.equal(50);
-
-                        expect(client._display.resize).to.have.been.calledOnce;
-                        expect(client._display.resize).to.have.been.calledWith(20, 50);
-                    });
-
-                    it('should handle a resize requested by another client', function () {
-                        const reason_for_change = 2; // requested by another client
-                        const status_code       = 0; // No error
-
-                        send_fbu_msg([{ x: reason_for_change, y: status_code,
-                                        width: 20, height: 50, encoding: -308 }],
-                                     make_screen_data(1), client);
-
-                        expect(client._fb_width).to.equal(20);
-                        expect(client._fb_height).to.equal(50);
-
-                        expect(client._display.resize).to.have.been.calledOnce;
-                        expect(client._display.resize).to.have.been.calledWith(20, 50);
-                    });
-
-                    it('should be able to recieve requests which contain data for multiple screens', function () {
-                        const reason_for_change = 2; // requested by another client
-                        const status_code       = 0; // No error
-
-                        send_fbu_msg([{ x: reason_for_change, y: status_code,
-                                        width: 60, height: 50, encoding: -308 }],
-                                     make_screen_data(3), client);
-
-                        expect(client._fb_width).to.equal(60);
-                        expect(client._fb_height).to.equal(50);
-
-                        expect(client._display.resize).to.have.been.calledOnce;
-                        expect(client._display.resize).to.have.been.calledWith(60, 50);
-                    });
-
-                    it('should not handle a failed request', function () {
-                        const reason_for_change = 1; // requested by this client
-                        const status_code       = 1; // Resize is administratively prohibited
-
-                        send_fbu_msg([{ x: reason_for_change, y: status_code,
-                                        width: 20, height: 50, encoding: -308 }],
-                                     make_screen_data(1), client);
-
-                        expect(client._fb_width).to.equal(4);
-                        expect(client._fb_height).to.equal(4);
-
-                        expect(client._display.resize).to.not.have.been.called;
-                    });
-                });
-
-                describe('the Cursor pseudo-encoding handler', function () {
-                    beforeEach(function () {
-                        sinon.spy(client._cursor, 'change');
-                    });
-
-                    it('should handle a standard cursor', function () {
-                        const info = { x: 5, y: 7,
-                                       width: 4, height: 4,
-                                       encoding: -239};
-                        let rect = [];
-                        let expected = [];
-
-                        for (let i = 0;i < info.width*info.height;i++) {
-                            push32(rect, 0x11223300);
-                        }
-                        push32(rect, 0xa0a0a0a0);
-
-                        for (let i = 0;i < info.width*info.height/2;i++) {
-                            push32(expected, 0x332211ff);
-                            push32(expected, 0x33221100);
-                        }
-                        expected = new Uint8Array(expected);
-
-                        send_fbu_msg([info], [rect], client);
-
-                        expect(client._cursor.change).to.have.been.calledOnce;
-                        expect(client._cursor.change).to.have.been.calledWith(expected, 5, 7, 4, 4);
-                    });
-
-                    it('should handle an empty cursor', function () {
-                        const info = { x: 0, y: 0,
-                                       width: 0, height: 0,
-                                       encoding: -239};
-                        const rect = [];
-
-                        send_fbu_msg([info], [rect], client);
-
-                        expect(client._cursor.change).to.have.been.calledOnce;
-                        expect(client._cursor.change).to.have.been.calledWith(new Uint8Array, 0, 0, 0, 0);
-                    });
-
-                    it('should handle a transparent cursor', function () {
-                        const info = { x: 5, y: 7,
-                                       width: 4, height: 4,
-                                       encoding: -239};
-                        let rect = [];
-                        let expected = [];
-
-                        for (let i = 0;i < info.width*info.height;i++) {
-                            push32(rect, 0x11223300);
-                        }
-                        push32(rect, 0x00000000);
-
-                        for (let i = 0;i < info.width*info.height;i++) {
-                            push32(expected, 0x33221100);
-                        }
-                        expected = new Uint8Array(expected);
-
-                        send_fbu_msg([info], [rect], client);
-
-                        expect(client._cursor.change).to.have.been.calledOnce;
-                        expect(client._cursor.change).to.have.been.calledWith(expected, 5, 7, 4, 4);
-                    });
-
-                    describe('dot for empty cursor', function () {
-                        beforeEach(function () {
-                            client.showDotCursor = true;
-                            // Was called when we enabled dot cursor
-                            client._cursor.change.reset();
-                        });
-
-                        it('should show a standard cursor', function () {
-                            const info = { x: 5, y: 7,
-                                           width: 4, height: 4,
-                                           encoding: -239};
-                            let rect = [];
-                            let expected = [];
-
-                            for (let i = 0;i < info.width*info.height;i++) {
-                                push32(rect, 0x11223300);
-                            }
-                            push32(rect, 0xa0a0a0a0);
-
-                            for (let i = 0;i < info.width*info.height/2;i++) {
-                                push32(expected, 0x332211ff);
-                                push32(expected, 0x33221100);
-                            }
-                            expected = new Uint8Array(expected);
-
-                            send_fbu_msg([info], [rect], client);
-
-                            expect(client._cursor.change).to.have.been.calledOnce;
-                            expect(client._cursor.change).to.have.been.calledWith(expected, 5, 7, 4, 4);
-                        });
-
-                        it('should handle an empty cursor', function () {
-                            const info = { x: 0, y: 0,
-                                           width: 0, height: 0,
-                                           encoding: -239};
-                            const rect = [];
-                            const dot = RFB.cursors.dot;
-
-                            send_fbu_msg([info], [rect], client);
-
-                            expect(client._cursor.change).to.have.been.calledOnce;
-                            expect(client._cursor.change).to.have.been.calledWith(dot.rgbaPixels,
-                                                                                  dot.hotx,
-                                                                                  dot.hoty,
-                                                                                  dot.w,
-                                                                                  dot.h);
-                        });
-
-                        it('should handle a transparent cursor', function () {
-                            const info = { x: 5, y: 7,
-                                           width: 4, height: 4,
-                                           encoding: -239};
-                            let rect = [];
-                            const dot = RFB.cursors.dot;
-
-                            for (let i = 0;i < info.width*info.height;i++) {
-                                push32(rect, 0x11223300);
-                            }
-                            push32(rect, 0x00000000);
-
-                            send_fbu_msg([info], [rect], client);
-
-                            expect(client._cursor.change).to.have.been.calledOnce;
-                            expect(client._cursor.change).to.have.been.calledWith(dot.rgbaPixels,
-                                                                                  dot.hotx,
-                                                                                  dot.hoty,
-                                                                                  dot.w,
-                                                                                  dot.h);
-                        });
-                    });
-                });
-
-                it('should handle the last_rect pseudo-encoding', function () {
-                    send_fbu_msg([{ x: 0, y: 0, width: 0, height: 0, encoding: -224}], [[]], client, 100);
-                    expect(client._FBU.rects).to.equal(0);
-                });
-            });
-        });
-
-        describe('XVP Message Handling', function () {
-            it('should set the XVP version and fire the callback with the version on XVP_INIT', function () {
-                const spy = sinon.spy();
-                client.addEventListener("capabilities", spy);
-                client._sock._websocket._receive_data(new Uint8Array([250, 0, 10, 1]));
-                expect(client._rfb_xvp_ver).to.equal(10);
-                expect(spy).to.have.been.calledOnce;
-                expect(spy.args[0][0].detail.capabilities.power).to.be.true;
-                expect(client.capabilities.power).to.be.true;
-            });
-
-            it('should fail on unknown XVP message types', function () {
-                sinon.spy(client, "_fail");
-                client._sock._websocket._receive_data(new Uint8Array([250, 0, 10, 237]));
-                expect(client._fail).to.have.been.calledOnce;
-            });
-        });
-
-        it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
-            const expected_str = 'cheese!';
-            const data = [3, 0, 0, 0];
-            push32(data, expected_str.length);
-            for (let i = 0; i < expected_str.length; i++) { data.push(expected_str.charCodeAt(i)); }
-            const spy = sinon.spy();
-            client.addEventListener("clipboard", spy);
-
-            client._sock._websocket._receive_data(new Uint8Array(data));
-            expect(spy).to.have.been.calledOnce;
-            expect(spy.args[0][0].detail.text).to.equal(expected_str);
-        });
-
-        it('should fire the bell callback on Bell', function () {
-            const spy = sinon.spy();
-            client.addEventListener("bell", spy);
-            client._sock._websocket._receive_data(new Uint8Array([2]));
-            expect(spy).to.have.been.calledOnce;
-        });
-
-        it('should respond correctly to ServerFence', 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";
-
-            // ClientFence and ServerFence are identical in structure
-            RFB.messages.clientFence(expected_msg, (1<<0) | (1<<1), payload);
-            RFB.messages.clientFence(incoming_msg, 0xffffffff, payload);
-
-            client._sock._websocket._receive_data(incoming_msg._sQ);
-
-            expect(client._sock).to.have.sent(expected_msg._sQ);
-
-            expected_msg._sQlen = 0;
-            incoming_msg._sQlen = 0;
-
-            RFB.messages.clientFence(expected_msg, (1<<0), payload);
-            RFB.messages.clientFence(incoming_msg, (1<<0) | (1<<31), payload);
-
-            client._sock._websocket._receive_data(incoming_msg._sQ);
-
-            expect(client._sock).to.have.sent(expected_msg._sQ);
-        });
-
-        it('should enable continuous updates on first EndOfContinousUpdates', function () {
-            const expected_msg = {_sQ: new Uint8Array(10), _sQlen: 0, flush: () => {}};
-
-            RFB.messages.enableContinuousUpdates(expected_msg, true, 0, 0, 640, 20);
-
-            expect(client._enabledContinuousUpdates).to.be.false;
-
-            client._sock._websocket._receive_data(new Uint8Array([150]));
-
-            expect(client._enabledContinuousUpdates).to.be.true;
-            expect(client._sock).to.have.sent(expected_msg._sQ);
-        });
-
-        it('should disable continuous updates on subsequent EndOfContinousUpdates', function () {
-            client._enabledContinuousUpdates = true;
-            client._supportsContinuousUpdates = true;
-
-            client._sock._websocket._receive_data(new Uint8Array([150]));
-
-            expect(client._enabledContinuousUpdates).to.be.false;
-        });
-
-        it('should update continuous updates on resize', 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);
-
-            expect(client._sock._websocket._get_sent_data()).to.have.length(0);
-
-            client._enabledContinuousUpdates = true;
-
-            client._resize(90, 700);
-
-            expect(client._sock).to.have.sent(expected_msg._sQ);
-        });
-
-        it('should fail on an unknown message type', function () {
-            sinon.spy(client, "_fail");
-            client._sock._websocket._receive_data(new Uint8Array([87]));
-            expect(client._fail).to.have.been.calledOnce;
-        });
-    });
-
-    describe('Asynchronous Events', function () {
-        let client;
-        beforeEach(function () {
-            client = make_rfb();
-        });
-
-        describe('Mouse event handlers', function () {
-            it('should not send button messages in view-only mode', function () {
-                client._viewOnly = true;
-                sinon.spy(client._sock, 'flush');
-                client._handleMouseButton(0, 0, 1, 0x001);
-                expect(client._sock.flush).to.not.have.been.called;
-            });
-
-            it('should not send movement messages in view-only mode', function () {
-                client._viewOnly = true;
-                sinon.spy(client._sock, 'flush');
-                client._handleMouseMove(0, 0);
-                expect(client._sock.flush).to.not.have.been.called;
-            });
-
-            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: () => {}};
-                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: () => {}};
-                RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x001);
-                expect(client._sock).to.have.sent(pointer_msg._sQ);
-            });
-
-            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: () => {}};
-                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: () => {}};
-                RFB.messages.pointerEvent(pointer_msg, 10, 12, 0x000);
-                expect(client._sock).to.have.sent(pointer_msg._sQ);
-            });
-
-            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: () => {}};
-                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);
-            });
-        });
-
-        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: () => {}};
-                RFB.messages.keyEvent(key_msg, 0x41, 1);
-                expect(client._sock).to.have.sent(key_msg._sQ);
-            });
-
-            it('should not send messages in view-only mode', function () {
-                client._viewOnly = true;
-                sinon.spy(client._sock, 'flush');
-                client._handleKeyEvent('a', 'KeyA', true);
-                expect(client._sock.flush).to.not.have.been.called;
-            });
-        });
-
-        describe('WebSocket event handlers', function () {
-            // message events
-            it('should do nothing if we receive an empty message and have nothing in the queue', function () {
-                client._normal_msg = sinon.spy();
-                client._sock._websocket._receive_data(new Uint8Array([]));
-                expect(client._normal_msg).to.not.have.been.called;
-            });
-
-            it('should handle a message in the connected state as a normal message', function () {
-                client._normal_msg = sinon.spy();
-                client._sock._websocket._receive_data(new Uint8Array([1, 2, 3]));
-                expect(client._normal_msg).to.have.been.called;
-            });
-
-            it('should handle a message in any non-disconnected/failed state like an init message', function () {
-                client._rfb_connection_state = 'connecting';
-                client._rfb_init_state = 'ProtocolVersion';
-                client._init_msg = sinon.spy();
-                client._sock._websocket._receive_data(new Uint8Array([1, 2, 3]));
-                expect(client._init_msg).to.have.been.called;
-            });
-
-            it('should process all normal messages directly', function () {
-                const spy = sinon.spy();
-                client.addEventListener("bell", spy);
-                client._sock._websocket._receive_data(new Uint8Array([0x02, 0x02]));
-                expect(spy).to.have.been.calledTwice;
-            });
-
-            // open events
-            it('should update the state to ProtocolVersion on open (if the state is "connecting")', function () {
-                client = new RFB(document.createElement('div'), 'wss://host:8675');
-                this.clock.tick();
-                client._sock._websocket._open();
-                expect(client._rfb_init_state).to.equal('ProtocolVersion');
-            });
-
-            it('should fail if we are not currently ready to connect and we get an "open" event', function () {
-                sinon.spy(client, "_fail");
-                client._rfb_connection_state = 'connected';
-                client._sock._websocket._open();
-                expect(client._fail).to.have.been.calledOnce;
-            });
-
-            // close events
-            it('should transition to "disconnected" from "disconnecting" on a close event', function () {
-                const real = client._sock._websocket.close;
-                client._sock._websocket.close = () => {};
-                client.disconnect();
-                expect(client._rfb_connection_state).to.equal('disconnecting');
-                client._sock._websocket.close = real;
-                client._sock._websocket.close();
-                expect(client._rfb_connection_state).to.equal('disconnected');
-            });
-
-            it('should fail if we get a close event while connecting', function () {
-                sinon.spy(client, "_fail");
-                client._rfb_connection_state = 'connecting';
-                client._sock._websocket.close();
-                expect(client._fail).to.have.been.calledOnce;
-            });
-
-            it('should unregister close event handler', function () {
-                sinon.spy(client._sock, 'off');
-                client.disconnect();
-                client._sock._websocket.close();
-                expect(client._sock.off).to.have.been.calledWith('close');
-            });
-
-            // error events do nothing
-        });
-    });
-});
diff --git a/systemvm/agent/noVNC/tests/test.util.js b/systemvm/agent/noVNC/tests/test.util.js
deleted file mode 100644
index 201acc8..0000000
--- a/systemvm/agent/noVNC/tests/test.util.js
+++ /dev/null
@@ -1,69 +0,0 @@
-/* eslint-disable no-console */
-const expect = chai.expect;
-
-import * as Log from '../core/util/logging.js';
-
-describe('Utils', function () {
-    "use strict";
-
-    describe('logging functions', function () {
-        beforeEach(function () {
-            sinon.spy(console, 'log');
-            sinon.spy(console, 'debug');
-            sinon.spy(console, 'warn');
-            sinon.spy(console, 'error');
-            sinon.spy(console, 'info');
-        });
-
-        afterEach(function () {
-            console.log.restore();
-            console.debug.restore();
-            console.warn.restore();
-            console.error.restore();
-            console.info.restore();
-            Log.init_logging();
-        });
-
-        it('should use noop for levels lower than the min level', function () {
-            Log.init_logging('warn');
-            Log.Debug('hi');
-            Log.Info('hello');
-            expect(console.log).to.not.have.been.called;
-        });
-
-        it('should use console.debug for Debug', function () {
-            Log.init_logging('debug');
-            Log.Debug('dbg');
-            expect(console.debug).to.have.been.calledWith('dbg');
-        });
-
-        it('should use console.info for Info', function () {
-            Log.init_logging('debug');
-            Log.Info('inf');
-            expect(console.info).to.have.been.calledWith('inf');
-        });
-
-        it('should use console.warn for Warn', function () {
-            Log.init_logging('warn');
-            Log.Warn('wrn');
-            expect(console.warn).to.have.been.called;
-            expect(console.warn).to.have.been.calledWith('wrn');
-        });
-
-        it('should use console.error for Error', function () {
-            Log.init_logging('error');
-            Log.Error('err');
-            expect(console.error).to.have.been.called;
-            expect(console.error).to.have.been.calledWith('err');
-        });
-    });
-
-    // TODO(directxman12): test the conf_default and conf_defaults methods
-    // TODO(directxman12): test decodeUTF8
-    // TODO(directxman12): test the event methods (addEvent, removeEvent, stopEvent)
-    // TODO(directxman12): figure out a good way to test getPosition and getEventPosition
-    // TODO(directxman12): figure out how to test the browser detection functions properly
-    //                     (we can't really test them against the browsers, except for Gecko
-    //                     via PhantomJS, the default test driver)
-});
-/* eslint-enable no-console */
diff --git a/systemvm/agent/noVNC/tests/test.websock.js b/systemvm/agent/noVNC/tests/test.websock.js
deleted file mode 100644
index 30e19e9..0000000
--- a/systemvm/agent/noVNC/tests/test.websock.js
+++ /dev/null
@@ -1,441 +0,0 @@
-const expect = chai.expect;
-
-import Websock from '../core/websock.js';
-import FakeWebSocket from './fake.websocket.js';
-
-describe('Websock', function () {
-    "use strict";
-
-    describe('Queue methods', function () {
-        let sock;
-        const RQ_TEMPLATE = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]);
-
-        beforeEach(function () {
-            sock = new Websock();
-            // skip init
-            sock._allocate_buffers();
-            sock._rQ.set(RQ_TEMPLATE);
-            sock._rQlen = RQ_TEMPLATE.length;
-        });
-        describe('rQlen', function () {
-            it('should return the length of the receive queue', function () {
-                sock.rQi = 0;
-
-                expect(sock.rQlen).to.equal(RQ_TEMPLATE.length);
-            });
-
-            it("should return the proper length if we read some from the receive queue", function () {
-                sock.rQi = 1;
-
-                expect(sock.rQlen).to.equal(RQ_TEMPLATE.length - 1);
-            });
-        });
-
-        describe('rQpeek8', function () {
-            it('should peek at the next byte without poping it off the queue', function () {
-                const bef_len = sock.rQlen;
-                const peek = sock.rQpeek8();
-                expect(sock.rQpeek8()).to.equal(peek);
-                expect(sock.rQlen).to.equal(bef_len);
-            });
-        });
-
-        describe('rQshift8()', function () {
-            it('should pop a single byte from the receive queue', function () {
-                const peek = sock.rQpeek8();
-                const bef_len = sock.rQlen;
-                expect(sock.rQshift8()).to.equal(peek);
-                expect(sock.rQlen).to.equal(bef_len - 1);
-            });
-        });
-
-        describe('rQshift16()', function () {
-            it('should pop two bytes from the receive queue and return a single number', function () {
-                const bef_len = sock.rQlen;
-                const expected = (RQ_TEMPLATE[0] << 8) + RQ_TEMPLATE[1];
-                expect(sock.rQshift16()).to.equal(expected);
-                expect(sock.rQlen).to.equal(bef_len - 2);
-            });
-        });
-
-        describe('rQshift32()', function () {
-            it('should pop four bytes from the receive queue and return a single number', function () {
-                const bef_len = sock.rQlen;
-                const expected = (RQ_TEMPLATE[0] << 24) +
-                               (RQ_TEMPLATE[1] << 16) +
-                               (RQ_TEMPLATE[2] << 8) +
-                               RQ_TEMPLATE[3];
-                expect(sock.rQshift32()).to.equal(expected);
-                expect(sock.rQlen).to.equal(bef_len - 4);
-            });
-        });
-
-        describe('rQshiftStr', function () {
-            it('should shift the given number of bytes off of the receive queue and return a string', function () {
-                const bef_len = sock.rQlen;
-                const bef_rQi = sock.rQi;
-                const shifted = sock.rQshiftStr(3);
-                expect(shifted).to.be.a('string');
-                expect(shifted).to.equal(String.fromCharCode.apply(null, Array.prototype.slice.call(new Uint8Array(RQ_TEMPLATE.buffer, bef_rQi, 3))));
-                expect(sock.rQlen).to.equal(bef_len - 3);
-            });
-
-            it('should shift the entire rest of the queue off if no length is given', function () {
-                sock.rQshiftStr();
-                expect(sock.rQlen).to.equal(0);
-            });
-
-            it('should be able to handle very large strings', function () {
-                const BIG_LEN = 500000;
-                const RQ_BIG = new Uint8Array(BIG_LEN);
-                let expected = "";
-                let letterCode = 'a'.charCodeAt(0);
-                for (let i = 0; i < BIG_LEN; i++) {
-                    RQ_BIG[i] = letterCode;
-                    expected += String.fromCharCode(letterCode);
-
-                    if (letterCode < 'z'.charCodeAt(0)) {
-                        letterCode++;
-                    } else {
-                        letterCode = 'a'.charCodeAt(0);
-                    }
-                }
-                sock._rQ.set(RQ_BIG);
-                sock._rQlen = RQ_BIG.length;
-
-                const shifted = sock.rQshiftStr();
-
-                expect(shifted).to.be.equal(expected);
-                expect(sock.rQlen).to.equal(0);
-            });
-        });
-
-        describe('rQshiftBytes', function () {
-            it('should shift the given number of bytes of the receive queue and return an array', function () {
-                const bef_len = sock.rQlen;
-                const bef_rQi = sock.rQi;
-                const shifted = sock.rQshiftBytes(3);
-                expect(shifted).to.be.an.instanceof(Uint8Array);
-                expect(shifted).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, bef_rQi, 3));
-                expect(sock.rQlen).to.equal(bef_len - 3);
-            });
-
-            it('should shift the entire rest of the queue off if no length is given', function () {
-                sock.rQshiftBytes();
-                expect(sock.rQlen).to.equal(0);
-            });
-        });
-
-        describe('rQslice', function () {
-            beforeEach(function () {
-                sock.rQi = 0;
-            });
-
-            it('should not modify the receive queue', function () {
-                const bef_len = sock.rQlen;
-                sock.rQslice(0, 2);
-                expect(sock.rQlen).to.equal(bef_len);
-            });
-
-            it('should return an array containing the given slice of the receive queue', function () {
-                const sl = sock.rQslice(0, 2);
-                expect(sl).to.be.an.instanceof(Uint8Array);
-                expect(sl).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 0, 2));
-            });
-
-            it('should use the rest of the receive queue if no end is given', function () {
-                const sl = sock.rQslice(1);
-                expect(sl).to.have.length(RQ_TEMPLATE.length - 1);
-                expect(sl).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 1));
-            });
-
-            it('should take the current rQi in to account', function () {
-                sock.rQi = 1;
-                expect(sock.rQslice(0, 2)).to.array.equal(new Uint8Array(RQ_TEMPLATE.buffer, 1, 2));
-            });
-        });
-
-        describe('rQwait', function () {
-            beforeEach(function () {
-                sock.rQi = 0;
-            });
-
-            it('should return true if there are not enough bytes in the receive queue', function () {
-                expect(sock.rQwait('hi', RQ_TEMPLATE.length + 1)).to.be.true;
-            });
-
-            it('should return false if there are enough bytes in the receive queue', function () {
-                expect(sock.rQwait('hi', RQ_TEMPLATE.length)).to.be.false;
-            });
-
-            it('should return true and reduce rQi by "goback" if there are not enough bytes', function () {
-                sock.rQi = 5;
-                expect(sock.rQwait('hi', RQ_TEMPLATE.length, 4)).to.be.true;
-                expect(sock.rQi).to.equal(1);
-            });
-
-            it('should raise an error if we try to go back more than possible', function () {
-                sock.rQi = 5;
-                expect(() => sock.rQwait('hi', RQ_TEMPLATE.length, 6)).to.throw(Error);
-            });
-
-            it('should not reduce rQi if there are enough bytes', function () {
-                sock.rQi = 5;
-                sock.rQwait('hi', 1, 6);
-                expect(sock.rQi).to.equal(5);
-            });
-        });
-
-        describe('flush', function () {
-            beforeEach(function () {
-                sock._websocket = {
-                    send: sinon.spy()
-                };
-            });
-
-            it('should actually send on the websocket', function () {
-                sock._websocket.bufferedAmount = 8;
-                sock._websocket.readyState = WebSocket.OPEN;
-                sock._sQ = new Uint8Array([1, 2, 3]);
-                sock._sQlen = 3;
-                const encoded = sock._encode_message();
-
-                sock.flush();
-                expect(sock._websocket.send).to.have.been.calledOnce;
-                expect(sock._websocket.send).to.have.been.calledWith(encoded);
-            });
-
-            it('should not call send if we do not have anything queued up', function () {
-                sock._sQlen = 0;
-                sock._websocket.bufferedAmount = 8;
-
-                sock.flush();
-
-                expect(sock._websocket.send).not.to.have.been.called;
-            });
-        });
-
-        describe('send', function () {
-            beforeEach(function () {
-                sock.flush = sinon.spy();
-            });
-
-            it('should add to the send queue', function () {
-                sock.send([1, 2, 3]);
-                const sq = sock.sQ;
-                expect(new Uint8Array(sq.buffer, sock._sQlen - 3, 3)).to.array.equal(new Uint8Array([1, 2, 3]));
-            });
-
-            it('should call flush', function () {
-                sock.send([1, 2, 3]);
-                expect(sock.flush).to.have.been.calledOnce;
-            });
-        });
-
-        describe('send_string', function () {
-            beforeEach(function () {
-                sock.send = sinon.spy();
-            });
-
-            it('should call send after converting the string to an array', function () {
-                sock.send_string("\x01\x02\x03");
-                expect(sock.send).to.have.been.calledWith([1, 2, 3]);
-            });
-        });
-    });
-
-    describe('lifecycle methods', function () {
-        let old_WS;
-        before(function () {
-            old_WS = WebSocket;
-        });
-
-        let sock;
-        beforeEach(function () {
-            sock = new Websock();
-            // eslint-disable-next-line no-global-assign
-            WebSocket = sinon.spy();
-            WebSocket.OPEN = old_WS.OPEN;
-            WebSocket.CONNECTING = old_WS.CONNECTING;
-            WebSocket.CLOSING = old_WS.CLOSING;
-            WebSocket.CLOSED = old_WS.CLOSED;
-
-            WebSocket.prototype.binaryType = 'arraybuffer';
-        });
-
-        describe('opening', function () {
-            it('should pick the correct protocols if none are given', function () {
-
-            });
-
-            it('should open the actual websocket', function () {
-                sock.open('ws://localhost:8675', 'binary');
-                expect(WebSocket).to.have.been.calledWith('ws://localhost:8675', 'binary');
-            });
-
-            // it('should initialize the event handlers')?
-        });
-
-        describe('closing', function () {
-            beforeEach(function () {
-                sock.open('ws://');
-                sock._websocket.close = sinon.spy();
-            });
-
-            it('should close the actual websocket if it is open', function () {
-                sock._websocket.readyState = WebSocket.OPEN;
-                sock.close();
-                expect(sock._websocket.close).to.have.been.calledOnce;
-            });
-
-            it('should close the actual websocket if it is connecting', function () {
-                sock._websocket.readyState = WebSocket.CONNECTING;
-                sock.close();
-                expect(sock._websocket.close).to.have.been.calledOnce;
-            });
-
-            it('should not try to close the actual websocket if closing', function () {
-                sock._websocket.readyState = WebSocket.CLOSING;
-                sock.close();
-                expect(sock._websocket.close).not.to.have.been.called;
-            });
-
-            it('should not try to close the actual websocket if closed', function () {
-                sock._websocket.readyState = WebSocket.CLOSED;
-                sock.close();
-                expect(sock._websocket.close).not.to.have.been.called;
-            });
-
-            it('should reset onmessage to not call _recv_message', function () {
-                sinon.spy(sock, '_recv_message');
-                sock.close();
-                sock._websocket.onmessage(null);
-                try {
-                    expect(sock._recv_message).not.to.have.been.called;
-                } finally {
-                    sock._recv_message.restore();
-                }
-            });
-        });
-
-        describe('event handlers', function () {
-            beforeEach(function () {
-                sock._recv_message = sinon.spy();
-                sock.on('open', sinon.spy());
-                sock.on('close', sinon.spy());
-                sock.on('error', sinon.spy());
-                sock.open('ws://');
-            });
-
-            it('should call _recv_message on a message', function () {
-                sock._websocket.onmessage(null);
-                expect(sock._recv_message).to.have.been.calledOnce;
-            });
-
-            it('should call the open event handler on opening', function () {
-                sock._websocket.onopen();
-                expect(sock._eventHandlers.open).to.have.been.calledOnce;
-            });
-
-            it('should call the close event handler on closing', function () {
-                sock._websocket.onclose();
-                expect(sock._eventHandlers.close).to.have.been.calledOnce;
-            });
-
-            it('should call the error event handler on error', function () {
-                sock._websocket.onerror();
-                expect(sock._eventHandlers.error).to.have.been.calledOnce;
-            });
-        });
-
-        after(function () {
-            // eslint-disable-next-line no-global-assign
-            WebSocket = old_WS;
-        });
-    });
-
-    describe('WebSocket Receiving', function () {
-        let sock;
-        beforeEach(function () {
-            sock = new Websock();
-            sock._allocate_buffers();
-        });
-
-        it('should support adding binary Uint8Array data to the receive queue', function () {
-            const msg = { data: new Uint8Array([1, 2, 3]) };
-            sock._mode = 'binary';
-            sock._recv_message(msg);
-            expect(sock.rQshiftStr(3)).to.equal('\x01\x02\x03');
-        });
-
-        it('should call the message event handler if present', function () {
-            sock._eventHandlers.message = sinon.spy();
-            const msg = { data: new Uint8Array([1, 2, 3]).buffer };
-            sock._mode = 'binary';
-            sock._recv_message(msg);
-            expect(sock._eventHandlers.message).to.have.been.calledOnce;
-        });
-
-        it('should not call the message event handler if there is nothing in the receive queue', function () {
-            sock._eventHandlers.message = sinon.spy();
-            const msg = { data: new Uint8Array([]).buffer };
-            sock._mode = 'binary';
-            sock._recv_message(msg);
-            expect(sock._eventHandlers.message).not.to.have.been.called;
-        });
-
-        it('should compact the receive queue', function () {
-            // NB(sross): while this is an internal implementation detail, it's important to
-            //            test, otherwise the receive queue could become very large very quickly
-            sock._rQ = new Uint8Array([0, 1, 2, 3, 4, 5, 0, 0, 0, 0]);
-            sock._rQlen = 6;
-            sock.rQi = 6;
-            sock._rQmax = 3;
-            const msg = { data: new Uint8Array([1, 2, 3]).buffer };
-            sock._mode = 'binary';
-            sock._recv_message(msg);
-            expect(sock._rQlen).to.equal(3);
-            expect(sock.rQi).to.equal(0);
-        });
-
-        it('should automatically resize the receive queue if the incoming message is too large', function () {
-            sock._rQ = new Uint8Array(20);
-            sock._rQlen = 0;
-            sock.rQi = 0;
-            sock._rQbufferSize = 20;
-            sock._rQmax = 2;
-            const msg = { data: new Uint8Array(30).buffer };
-            sock._mode = 'binary';
-            sock._recv_message(msg);
-            expect(sock._rQlen).to.equal(30);
-            expect(sock.rQi).to.equal(0);
-            expect(sock._rQ.length).to.equal(240);  // keep the invariant that rQbufferSize / 8 >= rQlen
-        });
-    });
-
-    describe('Data encoding', function () {
-        before(function () { FakeWebSocket.replace(); });
-        after(function () { FakeWebSocket.restore(); });
-
-        describe('as binary data', function () {
-            let sock;
-            beforeEach(function () {
-                sock = new Websock();
-                sock.open('ws://', 'binary');
-                sock._websocket._open();
-            });
-
-            it('should only send the send queue up to the send queue length', function () {
-                sock._sQ = new Uint8Array([1, 2, 3, 4, 5]);
-                sock._sQlen = 3;
-                const res = sock._encode_message();
-                expect(res).to.array.equal(new Uint8Array([1, 2, 3]));
-            });
-
-            it('should properly pass the encoded data off to the actual WebSocket', function () {
-                sock.send([1, 2, 3]);
-                expect(sock._websocket._get_sent_data()).to.array.equal(new Uint8Array([1, 2, 3]));
-            });
-        });
-    });
-});
diff --git a/systemvm/agent/noVNC/tests/test.webutil.js b/systemvm/agent/noVNC/tests/test.webutil.js
deleted file mode 100644
index 72e1942..0000000
--- a/systemvm/agent/noVNC/tests/test.webutil.js
+++ /dev/null
@@ -1,184 +0,0 @@
-/* jshint expr: true */
-
-const expect = chai.expect;
-
-import * as WebUtil from '../app/webutil.js';
-
-describe('WebUtil', function () {
-    "use strict";
-
-    describe('settings', function () {
-
-        describe('localStorage', function () {
-            let chrome = window.chrome;
-            before(function () {
-                chrome = window.chrome;
-                window.chrome = null;
-            });
-            after(function () {
-                window.chrome = chrome;
-            });
-
-            let origLocalStorage;
-            beforeEach(function () {
-                origLocalStorage = Object.getOwnPropertyDescriptor(window, "localStorage");
-                if (origLocalStorage === undefined) {
-                    // Object.getOwnPropertyDescriptor() doesn't work
-                    // properly in any version of IE
-                    this.skip();
-                }
-
-                Object.defineProperty(window, "localStorage", {value: {}});
-                if (window.localStorage.setItem !== undefined) {
-                    // Object.defineProperty() doesn't work properly in old
-                    // versions of Chrome
-                    this.skip();
-                }
-
-                window.localStorage.setItem = sinon.stub();
-                window.localStorage.getItem = sinon.stub();
-                window.localStorage.removeItem = sinon.stub();
-
-                return WebUtil.initSettings();
-            });
-            afterEach(function () {
-                Object.defineProperty(window, "localStorage", origLocalStorage);
-            });
-
-            describe('writeSetting', function () {
-                it('should save the setting value to local storage', function () {
-                    WebUtil.writeSetting('test', 'value');
-                    expect(window.localStorage.setItem).to.have.been.calledWithExactly('test', 'value');
-                    expect(WebUtil.readSetting('test')).to.equal('value');
-                });
-            });
-
-            describe('setSetting', function () {
-                it('should update the setting but not save to local storage', function () {
-                    WebUtil.setSetting('test', 'value');
-                    expect(window.localStorage.setItem).to.not.have.been.called;
-                    expect(WebUtil.readSetting('test')).to.equal('value');
-                });
-            });
-
-            describe('readSetting', function () {
-                it('should read the setting value from local storage', function () {
-                    localStorage.getItem.returns('value');
-                    expect(WebUtil.readSetting('test')).to.equal('value');
-                });
-
-                it('should return the default value when not in local storage', function () {
-                    expect(WebUtil.readSetting('test', 'default')).to.equal('default');
-                });
-
-                it('should return the cached value even if local storage changed', function () {
-                    localStorage.getItem.returns('value');
-                    expect(WebUtil.readSetting('test')).to.equal('value');
-                    localStorage.getItem.returns('something else');
-                    expect(WebUtil.readSetting('test')).to.equal('value');
-                });
-
-                it('should cache the value even if it is not initially in local storage', function () {
-                    expect(WebUtil.readSetting('test')).to.be.null;
-                    localStorage.getItem.returns('value');
-                    expect(WebUtil.readSetting('test')).to.be.null;
-                });
-
-                it('should return the default value always if the first read was not in local storage', function () {
-                    expect(WebUtil.readSetting('test', 'default')).to.equal('default');
-                    localStorage.getItem.returns('value');
-                    expect(WebUtil.readSetting('test', 'another default')).to.equal('another default');
-                });
-
-                it('should return the last local written value', function () {
-                    localStorage.getItem.returns('value');
-                    expect(WebUtil.readSetting('test')).to.equal('value');
-                    WebUtil.writeSetting('test', 'something else');
-                    expect(WebUtil.readSetting('test')).to.equal('something else');
-                });
-            });
-
-            // this doesn't appear to be used anywhere
-            describe('eraseSetting', function () {
-                it('should remove the setting from local storage', function () {
-                    WebUtil.eraseSetting('test');
-                    expect(window.localStorage.removeItem).to.have.been.calledWithExactly('test');
-                });
-            });
-        });
-
-        describe('chrome.storage', function () {
-            let chrome = window.chrome;
-            let settings = {};
-            before(function () {
-                chrome = window.chrome;
-                window.chrome = {
-                    storage: {
-                        sync: {
-                            get(cb) { cb(settings); },
-                            set() {},
-                            remove() {}
-                        }
-                    }
-                };
-            });
-            after(function () {
-                window.chrome = chrome;
-            });
-
-            const csSandbox = sinon.createSandbox();
-
-            beforeEach(function () {
-                settings = {};
-                csSandbox.spy(window.chrome.storage.sync, 'set');
-                csSandbox.spy(window.chrome.storage.sync, 'remove');
-                return WebUtil.initSettings();
-            });
-            afterEach(function () {
-                csSandbox.restore();
-            });
-
-            describe('writeSetting', function () {
-                it('should save the setting value to chrome storage', function () {
-                    WebUtil.writeSetting('test', 'value');
-                    expect(window.chrome.storage.sync.set).to.have.been.calledWithExactly(sinon.match({ test: 'value' }));
-                    expect(WebUtil.readSetting('test')).to.equal('value');
-                });
-            });
-
-            describe('setSetting', function () {
-                it('should update the setting but not save to chrome storage', function () {
-                    WebUtil.setSetting('test', 'value');
-                    expect(window.chrome.storage.sync.set).to.not.have.been.called;
-                    expect(WebUtil.readSetting('test')).to.equal('value');
-                });
-            });
-
-            describe('readSetting', function () {
-                it('should read the setting value from chrome storage', function () {
-                    settings.test = 'value';
-                    expect(WebUtil.readSetting('test')).to.equal('value');
-                });
-
-                it('should return the default value when not in chrome storage', function () {
-                    expect(WebUtil.readSetting('test', 'default')).to.equal('default');
-                });
-
-                it('should return the last local written value', function () {
-                    settings.test = 'value';
-                    expect(WebUtil.readSetting('test')).to.equal('value');
-                    WebUtil.writeSetting('test', 'something else');
-                    expect(WebUtil.readSetting('test')).to.equal('something else');
-                });
-            });
-
-            // this doesn't appear to be used anywhere
-            describe('eraseSetting', function () {
-                it('should remove the setting from chrome storage', function () {
-                    WebUtil.eraseSetting('test');
-                    expect(window.chrome.storage.sync.remove).to.have.been.calledWithExactly('test');
-                });
-            });
-        });
-    });
-});
diff --git a/systemvm/agent/noVNC/tests/vnc_playback.html b/systemvm/agent/noVNC/tests/vnc_playback.html
deleted file mode 100644
index 4fd7465..0000000
--- a/systemvm/agent/noVNC/tests/vnc_playback.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-    <head>
-        <title>VNC Playback</title>
-        <!-- promise polyfills promises for IE11 -->
-        <script src="../vendor/promise.js"></script>
-        <!-- ES2015/ES6 modules polyfill -->
-        <script type="module">
-            window._noVNC_has_module_support = true;
-        </script>
-        <script>
-            window.addEventListener("load", function() {
-                if (window._noVNC_has_module_support) return;
-                var loader = document.createElement("script");
-                loader.src = "../vendor/browser-es-module-loader/dist/browser-es-module-loader.js";
-                document.head.appendChild(loader);
-            });
-        </script>
-        <!-- actual script modules -->
-        <script type="module" src="./playback-ui.js"></script>
-    </head>
-    <body>
-
-        Iterations: <input id='iterations'>&nbsp;
-        Perftest:<input type='radio' id='mode1' name='mode' checked>&nbsp;
-        Realtime:<input type='radio' id='mode2' name='mode'>&nbsp;&nbsp;
-
-        <input id='startButton' type='button' value='Start' disabled>&nbsp;
-
-        <br><br>
-
-        Results:<br>
-        <textarea id="messages" cols=80 rows=25></textarea>
-
-        <br><br>
-
-        <div id="VNC_screen">
-            <div id="VNC_status">Loading</div>
-        </div>
-
-        <script type="module" src="./playback-ui.js"></script>
-    </body>
-</html>
diff --git a/systemvm/agent/noVNC/utils/.eslintrc b/systemvm/agent/noVNC/utils/.eslintrc
deleted file mode 100644
index b7dc129..0000000
--- a/systemvm/agent/noVNC/utils/.eslintrc
+++ /dev/null
@@ -1,8 +0,0 @@
-{
-  "env": {
-    "node": true
-  },
-  "rules": {
-  	"no-console": 0
-  }
-}
\ No newline at end of file
diff --git a/systemvm/agent/noVNC/utils/README.md b/systemvm/agent/noVNC/utils/README.md
deleted file mode 100644
index 32582e6..0000000
--- a/systemvm/agent/noVNC/utils/README.md
+++ /dev/null
@@ -1,14 +0,0 @@
-## WebSockets Proxy/Bridge
-
-Websockify has been forked out into its own project.  `launch.sh` wil
-automatically download it here if it is not already present and not
-installed as system-wide.
-
-For more detailed description and usage information please refer to
-the [websockify README](https://github.com/novnc/websockify/blob/master/README.md).
-
-The other versions of websockify (C, Node.js) and the associated test
-programs have been moved to
-[websockify](https://github.com/novnc/websockify).  Websockify was
-formerly named wsproxy.
-
diff --git a/systemvm/agent/noVNC/utils/b64-to-binary.pl b/systemvm/agent/noVNC/utils/b64-to-binary.pl
deleted file mode 100755
index 280e28c..0000000
--- a/systemvm/agent/noVNC/utils/b64-to-binary.pl
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/usr/bin/env perl
-use MIME::Base64;
-
-for (<>) {
-    unless (/^'([{}])(\d+)\1(.+?)',$/) {
-        print;
-        next;
-    }
-
-    my ($dir, $amt, $b64) = ($1, $2, $3);
-
-    my $decoded = MIME::Base64::decode($b64) or die "Could not base64-decode line `$_`";
-
-    my $decoded_escaped = join "", map { "\\x$_" } unpack("(H2)*", $decoded);
-
-    print "'${dir}${amt}${dir}${decoded_escaped}',\n";
-}
diff --git a/systemvm/agent/noVNC/utils/genkeysymdef.js b/systemvm/agent/noVNC/utils/genkeysymdef.js
deleted file mode 100755
index d21773f..0000000
--- a/systemvm/agent/noVNC/utils/genkeysymdef.js
+++ /dev/null
@@ -1,127 +0,0 @@
-#!/usr/bin/env node
-/*
- * genkeysymdef: X11 keysymdef.h to JavaScript converter
- * Copyright (C) 2018 The noVNC Authors
- * Licensed under MPL 2.0 (see LICENSE.txt)
- */
-
-"use strict";
-
-const fs = require('fs');
-
-let show_help = process.argv.length === 2;
-let filename;
-
-for (let i = 2; i < process.argv.length; ++i) {
-    switch (process.argv[i]) {
-        case "--help":
-        case "-h":
-            show_help = true;
-            break;
-        case "--file":
-        case "-f":
-        default:
-            filename = process.argv[i];
-    }
-}
-
-if (!filename) {
-    show_help = true;
-    console.log("Error: No filename specified\n");
-}
-
-if (show_help) {
-    console.log("Parses a *nix keysymdef.h to generate Unicode code point mappings");
-    console.log("Usage: node parse.js [options] filename:");
-    console.log("  -h [ --help ]                 Produce this help message");
-    console.log("  filename                      The keysymdef.h file to parse");
-    process.exit(0);
-}
-
-const buf = fs.readFileSync(filename);
-const str = buf.toString('utf8');
-
-const re = /^#define XK_([a-zA-Z_0-9]+)\s+0x([0-9a-fA-F]+)\s*(\/\*\s*(.*)\s*\*\/)?\s*$/m;
-
-const arr = str.split('\n');
-
-const codepoints = {};
-
-for (let i = 0; i < arr.length; ++i) {
-    const result = re.exec(arr[i]);
-    if (result) {
-        const keyname = result[1];
-        const keysym = parseInt(result[2], 16);
-        const remainder = result[3];
-
-        const unicodeRes = /U\+([0-9a-fA-F]+)/.exec(remainder);
-        if (unicodeRes) {
-            const unicode = parseInt(unicodeRes[1], 16);
-            // The first entry is the preferred one
-            if (!codepoints[unicode]) {
-                codepoints[unicode] = { keysym: keysym, name: keyname };
-            }
-        }
-    }
-}
-
-let out =
-"/*\n" +
-" * Mapping from Unicode codepoints to X11/RFB keysyms\n" +
-" *\n" +
-" * This file was automatically generated from keysymdef.h\n" +
-" * DO NOT EDIT!\n" +
-" */\n" +
-"\n" +
-"/* Functions at the bottom */\n" +
-"\n" +
-"const codepoints = {\n";
-
-function toHex(num) {
-    let s = num.toString(16);
-    if (s.length < 4) {
-        s = ("0000" + s).slice(-4);
-    }
-    return "0x" + s;
-}
-
-for (let codepoint in codepoints) {
-    codepoint = parseInt(codepoint);
-
-    // Latin-1?
-    if ((codepoint >= 0x20) && (codepoint <= 0xff)) {
-        continue;
-    }
-
-    // Handled by the general Unicode mapping?
-    if ((codepoint | 0x01000000) === codepoints[codepoint].keysym) {
-        continue;
-    }
-
-    out += "    " + toHex(codepoint) + ": " +
-           toHex(codepoints[codepoint].keysym) +
-           ", // XK_" + codepoints[codepoint].name + "\n";
-}
-
-out +=
-"};\n" +
-"\n" +
-"export default {\n" +
-"    lookup(u) {\n" +
-"        // Latin-1 is one-to-one mapping\n" +
-"        if ((u >= 0x20) && (u <= 0xff)) {\n" +
-"            return u;\n" +
-"        }\n" +
-"\n" +
-"        // Lookup table (fairly random)\n" +
-"        const keysym = codepoints[u];\n" +
-"        if (keysym !== undefined) {\n" +
-"            return keysym;\n" +
-"        }\n" +
-"\n" +
-"        // General mapping as final fallback\n" +
-"        return 0x01000000 | u;\n" +
-"    },\n" +
-"};";
-
-console.log(out);
diff --git a/systemvm/agent/noVNC/utils/img2js.py b/systemvm/agent/noVNC/utils/img2js.py
deleted file mode 100755
index ceab6bf..0000000
--- a/systemvm/agent/noVNC/utils/img2js.py
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/usr/bin/env python
-
-#
-# Convert image to Javascript compatible base64 Data URI
-# Copyright (C) 2018 The noVNC Authors
-# Licensed under MPL 2.0 (see docs/LICENSE.MPL-2.0)
-#
-
-import sys, base64
-
-try:
-    from PIL import Image
-except:
-    print "python PIL module required (python-imaging package)"
-    sys.exit(1)
-
-
-if len(sys.argv) < 3:
-    print "Usage: %s IMAGE JS_VARIABLE" % sys.argv[0]
-    sys.exit(1)
-
-fname = sys.argv[1]
-var   = sys.argv[2]
-
-ext = fname.lower().split('.')[-1]
-if   ext == "png":            mime = "image/png"
-elif ext in ["jpg", "jpeg"]:  mime = "image/jpeg"
-elif ext == "gif":            mime = "image/gif"
-else:
-    print "Only PNG, JPEG and GIF images are supported"
-    sys.exit(1)
-uri = "data:%s;base64," % mime
-
-im = Image.open(fname)
-w, h = im.size
-
-raw = open(fname).read()
-
-print '%s = {"width": %s, "height": %s, "data": "%s%s"};' % (
-        var, w, h, uri, base64.b64encode(raw))
diff --git a/systemvm/agent/noVNC/utils/json2graph.py b/systemvm/agent/noVNC/utils/json2graph.py
deleted file mode 100755
index bdaeecc..0000000
--- a/systemvm/agent/noVNC/utils/json2graph.py
+++ /dev/null
@@ -1,206 +0,0 @@
-#!/usr/bin/env python
-
-'''
-Use matplotlib to generate performance charts
-Copyright (C) 2018 The noVNC Authors
-Licensed under MPL-2.0 (see docs/LICENSE.MPL-2.0)
-'''
-
-# a bar plot with errorbars
-import sys, json
-import numpy as np
-import matplotlib.pyplot as plt
-from matplotlib.font_manager import FontProperties
-
-def usage():
-    print "%s json_file level1 level2 level3 [legend_height]\n\n" % sys.argv[0]
-    print "Description:\n"
-    print "level1, level2, and level3 are one each of the following:\n";
-    print "  select=ITEM - select only ITEM at this level";
-    print "  bar         - each item on this level becomes a graph bar";
-    print "  group       - items on this level become groups of bars";
-    print "\n";
-    print "json_file is a file containing json data in the following format:\n"
-    print '  {';
-    print '    "conf": {';
-    print '      "order_l1": [';
-    print '        "level1_label1",';
-    print '        "level1_label2",';
-    print '        ...';
-    print '      ],';
-    print '      "order_l2": [';
-    print '        "level2_label1",';
-    print '        "level2_label2",';
-    print '        ...';
-    print '      ],';
-    print '      "order_l3": [';
-    print '        "level3_label1",';
-    print '        "level3_label2",';
-    print '        ...';
-    print '      ]';
-    print '    },';
-    print '    "stats": {';
-    print '      "level1_label1": {';
-    print '        "level2_label1": {';
-    print '          "level3_label1": [val1, val2, val3],';
-    print '          "level3_label2": [val1, val2, val3],';
-    print '          ...';
-    print '        },';
-    print '        "level2_label2": {';
-    print '        ...';
-    print '        },';
-    print '      },';
-    print '      "level1_label2": {';
-    print '        ...';
-    print '      },';
-    print '      ...';
-    print '    },';
-    print '  }';
-    sys.exit(2)
-
-def error(msg):
-    print msg
-    sys.exit(1)
-
-
-#colors = ['#ff0000', '#0863e9', '#00f200', '#ffa100',
-#          '#800000', '#805100', '#013075', '#007900']
-colors = ['#ff0000', '#00ff00', '#0000ff',
-          '#dddd00', '#dd00dd', '#00dddd',
-          '#dd6622', '#dd2266', '#66dd22',
-          '#8844dd', '#44dd88', '#4488dd']
-
-if len(sys.argv) < 5:
-    usage()
-
-filename = sys.argv[1]
-L1 = sys.argv[2]
-L2 = sys.argv[3]
-L3 = sys.argv[4]
-if len(sys.argv) > 5:
-    legendHeight = float(sys.argv[5])
-else:
-    legendHeight = 0.75
-
-# Load the JSON data from the file
-data = json.loads(file(filename).read())
-conf = data['conf']
-stats = data['stats']
-
-# Sanity check data hierarchy
-if len(conf['order_l1']) != len(stats.keys()):
-    error("conf.order_l1 does not match stats level 1")
-for l1 in stats.keys():
-    if len(conf['order_l2']) != len(stats[l1].keys()):
-        error("conf.order_l2 does not match stats level 2 for %s" % l1)
-    if conf['order_l1'].count(l1) < 1:
-        error("%s not found in conf.order_l1" % l1)
-    for l2 in stats[l1].keys():
-        if len(conf['order_l3']) != len(stats[l1][l2].keys()):
-            error("conf.order_l3 does not match stats level 3")
-        if conf['order_l2'].count(l2) < 1:
-            error("%s not found in conf.order_l2" % l2)
-        for l3 in stats[l1][l2].keys():
-            if conf['order_l3'].count(l3) < 1:
-                error("%s not found in conf.order_l3" % l3)
-
-#
-# Generate the data based on the level specifications
-#
-bar_labels = None
-group_labels = None
-bar_vals = []
-bar_sdvs = []
-if L3.startswith("select="):
-    select_label = l3 = L3.split("=")[1]
-    bar_labels = conf['order_l1']
-    group_labels = conf['order_l2']
-    bar_vals = [[0]*len(group_labels) for i in bar_labels]
-    bar_sdvs = [[0]*len(group_labels) for i in bar_labels]
-    for b in range(len(bar_labels)):
-        l1 = bar_labels[b]
-        for g in range(len(group_labels)):
-            l2 = group_labels[g]
-            bar_vals[b][g] = np.mean(stats[l1][l2][l3])
-            bar_sdvs[b][g] = np.std(stats[l1][l2][l3])
-elif L2.startswith("select="):
-    select_label = l2 = L2.split("=")[1]
-    bar_labels = conf['order_l1']
-    group_labels = conf['order_l3']
-    bar_vals = [[0]*len(group_labels) for i in bar_labels]
-    bar_sdvs = [[0]*len(group_labels) for i in bar_labels]
-    for b in range(len(bar_labels)):
-        l1 = bar_labels[b]
-        for g in range(len(group_labels)):
-            l3 = group_labels[g]
-            bar_vals[b][g] = np.mean(stats[l1][l2][l3])
-            bar_sdvs[b][g] = np.std(stats[l1][l2][l3])
-elif L1.startswith("select="):
-    select_label = l1 = L1.split("=")[1]
-    bar_labels = conf['order_l2']
-    group_labels = conf['order_l3']
-    bar_vals = [[0]*len(group_labels) for i in bar_labels]
-    bar_sdvs = [[0]*len(group_labels) for i in bar_labels]
-    for b in range(len(bar_labels)):
-        l2 = bar_labels[b]
-        for g in range(len(group_labels)):
-            l3 = group_labels[g]
-            bar_vals[b][g] = np.mean(stats[l1][l2][l3])
-            bar_sdvs[b][g] = np.std(stats[l1][l2][l3])
-else:
-    usage()
-
-# If group is before bar then flip (zip) the data
-if [L1, L2, L3].index("group") < [L1, L2, L3].index("bar"):
-    bar_labels, group_labels = group_labels, bar_labels
-    bar_vals = zip(*bar_vals)
-    bar_sdvs = zip(*bar_sdvs)
-
-print "bar_vals:", bar_vals
-
-#
-# Now render the bar graph
-#
-ind = np.arange(len(group_labels))  # the x locations for the groups
-width = 0.8 * (1.0/len(bar_labels)) # the width of the bars
-
-fig = plt.figure(figsize=(10,6), dpi=80)
-plot = fig.add_subplot(1, 1, 1)
-
-rects = []
-for i in range(len(bar_vals)):
-    rects.append(plot.bar(ind+width*i, bar_vals[i], width, color=colors[i],
-                          yerr=bar_sdvs[i], align='center'))
-
-# add some
-plot.set_ylabel('Milliseconds (less is better)')
-plot.set_title("Javascript array test: %s" % select_label)
-plot.set_xticks(ind+width)
-plot.set_xticklabels( group_labels )
-
-fontP = FontProperties()
-fontP.set_size('small')
-plot.legend( [r[0] for r in rects], bar_labels, prop=fontP,
-            loc = 'center right', bbox_to_anchor = (1.0, legendHeight))
-
-def autolabel(rects):
-    # attach some text labels
-    for rect in rects:
-        height = rect.get_height()
-        if np.isnan(height):
-            height = 0.0
-        plot.text(rect.get_x()+rect.get_width()/2., height+20, '%d'%int(height),
-                ha='center', va='bottom', size='7')
-
-for rect in rects:
-    autolabel(rect)
-
-# Adjust axis sizes
-axis = list(plot.axis())
-axis[0] = -width          # Make sure left side has enough for bar
-#axis[1] = axis[1] * 1.20  # Add 20% to the right to make sure it fits
-axis[2] = 0               # Make y-axis start at 0
-axis[3] = axis[3] * 1.10  # Add 10% to the top
-plot.axis(axis)
-
-plt.show()
diff --git a/systemvm/agent/noVNC/utils/launch.sh b/systemvm/agent/noVNC/utils/launch.sh
deleted file mode 100755
index 162607e..0000000
--- a/systemvm/agent/noVNC/utils/launch.sh
+++ /dev/null
@@ -1,169 +0,0 @@
-#!/usr/bin/env bash
-
-# Copyright (C) 2018 The noVNC Authors
-# Licensed under MPL 2.0 or any later version (see LICENSE.txt)
-
-usage() {
-    if [ "$*" ]; then
-        echo "$*"
-        echo
-    fi
-    echo "Usage: ${NAME} [--listen PORT] [--vnc VNC_HOST:PORT] [--cert CERT] [--ssl-only]"
-    echo
-    echo "Starts the WebSockets proxy and a mini-webserver and "
-    echo "provides a cut-and-paste URL to go to."
-    echo
-    echo "    --listen PORT         Port for proxy/webserver to listen on"
-    echo "                          Default: 6080"
-    echo "    --vnc VNC_HOST:PORT   VNC server host:port proxy target"
-    echo "                          Default: localhost:5900"
-    echo "    --cert CERT           Path to combined cert/key file"
-    echo "                          Default: self.pem"
-    echo "    --web WEB             Path to web files (e.g. vnc.html)"
-    echo "                          Default: ./"
-    echo "    --ssl-only            Disable non-https connections."
-    echo "                                    "
-    echo "    --record FILE         Record traffic to FILE.session.js"
-    echo "                                    "
-    exit 2
-}
-
-NAME="$(basename $0)"
-REAL_NAME="$(readlink -f $0)"
-HERE="$(cd "$(dirname "$REAL_NAME")" && pwd)"
-PORT="6080"
-VNC_DEST="localhost:5900"
-CERT=""
-WEB=""
-proxy_pid=""
-SSLONLY=""
-RECORD_ARG=""
-
-die() {
-    echo "$*"
-    exit 1
-}
-
-cleanup() {
-    trap - TERM QUIT INT EXIT
-    trap "true" CHLD   # Ignore cleanup messages
-    echo
-    if [ -n "${proxy_pid}" ]; then
-        echo "Terminating WebSockets proxy (${proxy_pid})"
-        kill ${proxy_pid}
-    fi
-}
-
-# Process Arguments
-
-# Arguments that only apply to chrooter itself
-while [ "$*" ]; do
-    param=$1; shift; OPTARG=$1
-    case $param in
-    --listen)  PORT="${OPTARG}"; shift            ;;
-    --vnc)     VNC_DEST="${OPTARG}"; shift        ;;
-    --cert)    CERT="${OPTARG}"; shift            ;;
-    --web)     WEB="${OPTARG}"; shift            ;;
-    --ssl-only) SSLONLY="--ssl-only"             ;;
-    --record) RECORD_ARG="--record ${OPTARG}"; shift ;;
-    -h|--help) usage                              ;;
-    -*) usage "Unknown chrooter option: ${param}" ;;
-    *) break                                      ;;
-    esac
-done
-
-# Sanity checks
-if bash -c "exec 7<>/dev/tcp/localhost/${PORT}" &> /dev/null; then
-    exec 7<&-
-    exec 7>&-
-    die "Port ${PORT} in use. Try --listen PORT"
-else
-    exec 7<&-
-    exec 7>&-
-fi
-
-trap "cleanup" TERM QUIT INT EXIT
-
-# Find vnc.html
-if [ -n "${WEB}" ]; then
-    if [ ! -e "${WEB}/vnc.html" ]; then
-        die "Could not find ${WEB}/vnc.html"
-    fi
-elif [ -e "$(pwd)/vnc.html" ]; then
-    WEB=$(pwd)
-elif [ -e "${HERE}/../vnc.html" ]; then
-    WEB=${HERE}/../
-elif [ -e "${HERE}/vnc.html" ]; then
-    WEB=${HERE}
-elif [ -e "${HERE}/../share/novnc/vnc.html" ]; then
-    WEB=${HERE}/../share/novnc/
-else
-    die "Could not find vnc.html"
-fi
-
-# Find self.pem
-if [ -n "${CERT}" ]; then
-    if [ ! -e "${CERT}" ]; then
-        die "Could not find ${CERT}"
-    fi
-elif [ -e "$(pwd)/self.pem" ]; then
-    CERT="$(pwd)/self.pem"
-elif [ -e "${HERE}/../self.pem" ]; then
-    CERT="${HERE}/../self.pem"
-elif [ -e "${HERE}/self.pem" ]; then
-    CERT="${HERE}/self.pem"
-else
-    echo "Warning: could not find self.pem"
-fi
-
-# try to find websockify (prefer local, try global, then download local)
-if [[ -e ${HERE}/websockify ]]; then
-    WEBSOCKIFY=${HERE}/websockify/run
-
-    if [[ ! -x $WEBSOCKIFY ]]; then
-        echo "The path ${HERE}/websockify exists, but $WEBSOCKIFY either does not exist or is not executable."
-        echo "If you intended to use an installed websockify package, please remove ${HERE}/websockify."
-        exit 1
-    fi
-
-    echo "Using local websockify at $WEBSOCKIFY"
-else
-    WEBSOCKIFY=$(which websockify 2>/dev/null)
-
-    if [[ $? -ne 0 ]]; then
-        echo "No installed websockify, attempting to clone websockify..."
-        WEBSOCKIFY=${HERE}/websockify/run
-        git clone https://github.com/novnc/websockify ${HERE}/websockify
-
-        if [[ ! -e $WEBSOCKIFY ]]; then
-            echo "Unable to locate ${HERE}/websockify/run after downloading"
-            exit 1
-        fi
-
-        echo "Using local websockify at $WEBSOCKIFY"
-    else
-        echo "Using installed websockify at $WEBSOCKIFY"
-    fi
-fi
-
-echo "Starting webserver and WebSockets proxy on port ${PORT}"
-#${HERE}/websockify --web ${WEB} ${CERT:+--cert ${CERT}} ${PORT} ${VNC_DEST} &
-${WEBSOCKIFY} ${SSLONLY} --web ${WEB} ${CERT:+--cert ${CERT}} ${PORT} ${VNC_DEST} ${RECORD_ARG} &
-proxy_pid="$!"
-sleep 1
-if ! ps -p ${proxy_pid} >/dev/null; then
-    proxy_pid=
-    echo "Failed to start WebSockets proxy"
-    exit 1
-fi
-
-echo -e "\n\nNavigate to this URL:\n"
-if [ "x$SSLONLY" == "x" ]; then
-    echo -e "    http://$(hostname):${PORT}/vnc.html?host=$(hostname)&port=${PORT}\n"
-else
-    echo -e "    https://$(hostname):${PORT}/vnc.html?host=$(hostname)&port=${PORT}\n"
-fi
-
-echo -e "Press Ctrl-C to exit\n\n"
-
-wait ${proxy_pid}
diff --git a/systemvm/agent/noVNC/utils/u2x11 b/systemvm/agent/noVNC/utils/u2x11
deleted file mode 100755
index fd3e4ba..0000000
--- a/systemvm/agent/noVNC/utils/u2x11
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env bash
-#
-# Convert "U+..." commented entries in /usr/include/X11/keysymdef.h
-# into JavaScript for use by noVNC.  Note this is likely to produce
-# a few duplicate properties with clashing values, that will need
-# resolving manually.
-#
-# Colin Dean <colin@xvpsource.org>
-#
-
-regex="^#define[ \t]+XK_[A-Za-z0-9_]+[ \t]+0x([0-9a-fA-F]+)[ \t]+\/\*[ \t]+U\+([0-9a-fA-F]+)[ \t]+[^*]+.[ \t]+\*\/[ \t]*$"
-echo "unicodeTable = {"
-while read line; do
-    if echo "${line}" | egrep -qs "${regex}"; then
-
-        x11=$(echo "${line}" | sed -r "s/${regex}/\1/")
-        vnc=$(echo "${line}" | sed -r "s/${regex}/\2/")
-	
-	if echo "${vnc}" | egrep -qs "^00[2-9A-F][0-9A-F]$"; then
-	    : # skip ISO Latin-1 (U+0020 to U+00FF) as 1-to-1 mapping
-	else
-	    # note 1-to-1 is possible (e.g. for Euro symbol, U+20AC)
-	    echo "    0x${vnc} : 0x${x11},"
-	fi
-    fi
-done < /usr/include/X11/keysymdef.h | uniq
-echo "};"
-
diff --git a/systemvm/agent/noVNC/utils/use_require.js b/systemvm/agent/noVNC/utils/use_require.js
deleted file mode 100755
index 2487927..0000000
--- a/systemvm/agent/noVNC/utils/use_require.js
+++ /dev/null
@@ -1,313 +0,0 @@
-#!/usr/bin/env node
-
-const path = require('path');
-const program = require('commander');
-const fs = require('fs');
-const fse = require('fs-extra');
-const babel = require('babel-core');
-
-const SUPPORTED_FORMATS = new Set(['amd', 'commonjs', 'systemjs', 'umd']);
-
-program
-    .option('--as [format]', `output files using various import formats instead of ES6 import and export.  Supports ${Array.from(SUPPORTED_FORMATS)}.`)
-    .option('-m, --with-source-maps [type]', 'output source maps when not generating a bundled app (type may be empty for external source maps, inline for inline source maps, or both) ')
-    .option('--with-app', 'process app files as well as core files')
-    .option('--only-legacy', 'only output legacy files (no ES6 modules) for the app')
-    .option('--clean', 'clear the lib folder before building')
-    .parse(process.argv);
-
-// the various important paths
-const paths = {
-    main: path.resolve(__dirname, '..'),
-    core: path.resolve(__dirname, '..', 'core'),
-    app: path.resolve(__dirname, '..', 'app'),
-    vendor: path.resolve(__dirname, '..', 'vendor'),
-    out_dir_base: path.resolve(__dirname, '..', 'build'),
-    lib_dir_base: path.resolve(__dirname, '..', 'lib'),
-};
-
-const no_copy_files = new Set([
-    // skip these -- they don't belong in the processed application
-    path.join(paths.vendor, 'sinon.js'),
-    path.join(paths.vendor, 'browser-es-module-loader'),
-    path.join(paths.vendor, 'promise.js'),
-    path.join(paths.app, 'images', 'icons', 'Makefile'),
-]);
-
-const no_transform_files = new Set([
-    // don't transform this -- we want it imported as-is to properly catch loading errors
-    path.join(paths.app, 'error-handler.js'),
-]);
-
-no_copy_files.forEach(file => no_transform_files.add(file));
-
-// util.promisify requires Node.js 8.x, so we have our own
-function promisify(original) {
-    return function promise_wrap() {
-        const args = Array.prototype.slice.call(arguments);
-        return new Promise((resolve, reject) => {
-            original.apply(this, args.concat((err, value) => {
-                if (err) return reject(err);
-                resolve(value);
-            }));
-        });
-    };
-}
-
-const readFile = promisify(fs.readFile);
-const writeFile = promisify(fs.writeFile);
-
-const readdir = promisify(fs.readdir);
-const lstat = promisify(fs.lstat);
-
-const copy = promisify(fse.copy);
-const unlink = promisify(fse.unlink);
-const ensureDir = promisify(fse.ensureDir);
-const rmdir = promisify(fse.rmdir);
-
-const babelTransformFile = promisify(babel.transformFile);
-
-// walkDir *recursively* walks directories trees,
-// calling the callback for all normal files found.
-function walkDir(base_path, cb, filter) {
-    return readdir(base_path)
-        .then((files) => {
-            const paths = files.map(filename => path.join(base_path, filename));
-            return Promise.all(paths.map(filepath => lstat(filepath)
-                .then((stats) => {
-                    if (filter !== undefined && !filter(filepath, stats)) return;
-
-                    if (stats.isSymbolicLink()) return;
-                    if (stats.isFile()) return cb(filepath);
-                    if (stats.isDirectory()) return walkDir(filepath, cb, filter);
-                })));
-        });
-}
-
-function transform_html(legacy_scripts, only_legacy) {
-    // write out the modified vnc.html file that works with the bundle
-    const src_html_path = path.resolve(__dirname, '..', 'vnc.html');
-    const out_html_path = path.resolve(paths.out_dir_base, 'vnc.html');
-    return readFile(src_html_path)
-        .then((contents_raw) => {
-            let contents = contents_raw.toString();
-
-            const start_marker = '<!-- begin scripts -->\n';
-            const end_marker = '<!-- end scripts -->';
-            const start_ind = contents.indexOf(start_marker) + start_marker.length;
-            const end_ind = contents.indexOf(end_marker, start_ind);
-
-            let new_script = '';
-
-            if (only_legacy) {
-            // Only legacy version, so include things directly
-                for (let i = 0;i < legacy_scripts.length;i++) {
-                    new_script += `    <script src="${legacy_scripts[i]}"></script>\n`;
-                }
-            } else {
-            // Otherwise detect if it's a modern browser and select
-            // variant accordingly
-                new_script += `\
-    <script type="module">\n\
-        window._noVNC_has_module_support = true;\n\
-    </script>\n\
-    <script>\n\
-        window.addEventListener("load", function() {\n\
-            if (window._noVNC_has_module_support) return;\n\
-            let legacy_scripts = ${JSON.stringify(legacy_scripts)};\n\
-            for (let i = 0;i < legacy_scripts.length;i++) {\n\
-                let script = document.createElement("script");\n\
-                script.src = legacy_scripts[i];\n\
-                script.async = false;\n\
-                document.head.appendChild(script);\n\
-            }\n\
-        });\n\
-    </script>\n`;
-
-            // Original, ES6 modules
-                new_script += '    <script type="module" crossorigin="anonymous" src="app/ui.js"></script>\n';
-            }
-
-            contents = contents.slice(0, start_ind) + `${new_script}\n` + contents.slice(end_ind);
-
-            return contents;
-        })
-        .then((contents) => {
-            console.log(`Writing ${out_html_path}`);
-            return writeFile(out_html_path, contents);
-        });
-}
-
-function make_lib_files(import_format, source_maps, with_app_dir, only_legacy) {
-    if (!import_format) {
-        throw new Error("you must specify an import format to generate compiled noVNC libraries");
-    } else if (!SUPPORTED_FORMATS.has(import_format)) {
-        throw new Error(`unsupported output format "${import_format}" for import/export -- only ${Array.from(SUPPORTED_FORMATS)} are supported`);
-    }
-
-    // NB: we need to make a copy of babel_opts, since babel sets some defaults on it
-    const babel_opts = () => ({
-        plugins: [`transform-es2015-modules-${import_format}`],
-        presets: ['es2015'],
-        ast: false,
-        sourceMaps: source_maps,
-    });
-
-    // No point in duplicate files without the app, so force only converted files
-    if (!with_app_dir) {
-        only_legacy = true;
-    }
-
-    let in_path;
-    let out_path_base;
-    if (with_app_dir) {
-        out_path_base = paths.out_dir_base;
-        in_path = paths.main;
-    } else {
-        out_path_base = paths.lib_dir_base;
-    }
-    const legacy_path_base = only_legacy ? out_path_base : path.join(out_path_base, 'legacy');
-
-    fse.ensureDirSync(out_path_base);
-
-    const helpers = require('./use_require_helpers');
-    const helper = helpers[import_format];
-
-    const outFiles = [];
-
-    const handleDir = (js_only, vendor_rewrite, in_path_base, filename) => Promise.resolve()
-        .then(() => {
-            if (no_copy_files.has(filename)) return;
-
-            const out_path = path.join(out_path_base, path.relative(in_path_base, filename));
-            const legacy_path = path.join(legacy_path_base, path.relative(in_path_base, filename));
-
-            if (path.extname(filename) !== '.js') {
-                if (!js_only) {
-                    console.log(`Writing ${out_path}`);
-                    return copy(filename, out_path);
-                }
-                return;  // skip non-javascript files
-            }
-
-            return Promise.resolve()
-                .then(() => {
-                    if (only_legacy && !no_transform_files.has(filename)) {
-                        return;
-                    }
-                    return ensureDir(path.dirname(out_path))
-                        .then(() => {
-                            console.log(`Writing ${out_path}`);
-                            return copy(filename, out_path);
-                        });
-                })
-                .then(() => ensureDir(path.dirname(legacy_path)))
-                .then(() => {
-                    if (no_transform_files.has(filename)) {
-                        return;
-                    }
-
-                    const opts = babel_opts();
-                    if (helper && helpers.optionsOverride) {
-                        helper.optionsOverride(opts);
-                    }
-            // Adjust for the fact that we move the core files relative
-            // to the vendor directory
-                    if (vendor_rewrite) {
-                        opts.plugins.push(["import-redirect",
-                                           {"root": legacy_path_base,
-                                            "redirect": { "vendor/(.+)": "./vendor/$1"}}]);
-                    }
-
-                    return babelTransformFile(filename, opts)
-                        .then((res) => {
-                            console.log(`Writing ${legacy_path}`);
-                            const {map} = res;
-                            let {code} = res;
-                            if (source_maps === true) {
-                    // append URL for external source map
-                                code += `\n//# sourceMappingURL=${path.basename(legacy_path)}.map\n`;
-                            }
-                            outFiles.push(`${legacy_path}`);
-                            return writeFile(legacy_path, code)
-                                .then(() => {
-                                    if (source_maps === true || source_maps === 'both') {
-                                        console.log(`  and ${legacy_path}.map`);
-                                        outFiles.push(`${legacy_path}.map`);
-                                        return writeFile(`${legacy_path}.map`, JSON.stringify(map));
-                                    }
-                                });
-                        });
-                });
-        });
-
-    if (with_app_dir && helper && helper.noCopyOverride) {
-        helper.noCopyOverride(paths, no_copy_files);
-    }
-
-    Promise.resolve()
-        .then(() => {
-            const handler = handleDir.bind(null, true, false, in_path || paths.main);
-            const filter = (filename, stats) => !no_copy_files.has(filename);
-            return walkDir(paths.vendor, handler, filter);
-        })
-        .then(() => {
-            const handler = handleDir.bind(null, true, !in_path, in_path || paths.core);
-            const filter = (filename, stats) => !no_copy_files.has(filename);
-            return walkDir(paths.core, handler, filter);
-        })
-        .then(() => {
-            if (!with_app_dir) return;
-            const handler = handleDir.bind(null, false, false, in_path);
-            const filter = (filename, stats) => !no_copy_files.has(filename);
-            return walkDir(paths.app, handler, filter);
-        })
-        .then(() => {
-            if (!with_app_dir) return;
-
-            if (!helper || !helper.appWriter) {
-                throw new Error(`Unable to generate app for the ${import_format} format!`);
-            }
-
-            const out_app_path = path.join(legacy_path_base, 'app.js');
-            console.log(`Writing ${out_app_path}`);
-            return helper.appWriter(out_path_base, legacy_path_base, out_app_path)
-                .then((extra_scripts) => {
-                    const rel_app_path = path.relative(out_path_base, out_app_path);
-                    const legacy_scripts = extra_scripts.concat([rel_app_path]);
-                    transform_html(legacy_scripts, only_legacy);
-                })
-                .then(() => {
-                    if (!helper.removeModules) return;
-                    console.log(`Cleaning up temporary files...`);
-                    return Promise.all(outFiles.map((filepath) => {
-                        unlink(filepath)
-                            .then(() => {
-                    // Try to clean up any empty directories if this
-                    // was the last file in there
-                                const rmdir_r = dir =>
-                                    rmdir(dir)
-                                        .then(() => rmdir_r(path.dirname(dir)))
-                                        .catch(() => {
-                            // Assume the error was ENOTEMPTY and ignore it
-                                        });
-                                return rmdir_r(path.dirname(filepath));
-                            });
-                    }));
-                });
-        })
-        .catch((err) => {
-            console.error(`Failure converting modules: ${err}`);
-            process.exit(1);
-        });
-}
-
-if (program.clean) {
-    console.log(`Removing ${paths.lib_dir_base}`);
-    fse.removeSync(paths.lib_dir_base);
-
-    console.log(`Removing ${paths.out_dir_base}`);
-    fse.removeSync(paths.out_dir_base);
-}
-
-make_lib_files(program.as, program.withSourceMaps, program.withApp, program.onlyLegacy);
diff --git a/systemvm/agent/noVNC/utils/use_require_helpers.js b/systemvm/agent/noVNC/utils/use_require_helpers.js
deleted file mode 100644
index a4f99c7..0000000
--- a/systemvm/agent/noVNC/utils/use_require_helpers.js
+++ /dev/null
@@ -1,76 +0,0 @@
-// writes helpers require for vnc.html (they should output app.js)
-const fs = require('fs');
-const path = require('path');
-
-// util.promisify requires Node.js 8.x, so we have our own
-function promisify(original) {
-    return function promise_wrap() {
-        const args = Array.prototype.slice.call(arguments);
-        return new Promise((resolve, reject) => {
-            original.apply(this, args.concat((err, value) => {
-                if (err) return reject(err);
-                resolve(value);
-            }));
-        });
-    };
-}
-
-const writeFile = promisify(fs.writeFile);
-
-module.exports = {
-    'amd': {
-        appWriter: (base_out_path, script_base_path, out_path) => {
-            // setup for requirejs
-            const ui_path = path.relative(base_out_path,
-                                          path.join(script_base_path, 'app', 'ui'));
-            return writeFile(out_path, `requirejs(["${ui_path}"], (ui) => {});`)
-                .then(() => {
-                    console.log(`Please place RequireJS in ${path.join(script_base_path, 'require.js')}`);
-                    const require_path = path.relative(base_out_path,
-                                                       path.join(script_base_path, 'require.js'));
-                    return [ require_path ];
-                });
-        },
-        noCopyOverride: () => {},
-    },
-    'commonjs': {
-        optionsOverride: (opts) => {
-            // CommonJS supports properly shifting the default export to work as normal
-            opts.plugins.unshift("add-module-exports");
-        },
-        appWriter: (base_out_path, script_base_path, out_path) => {
-            const browserify = require('browserify');
-            const b = browserify(path.join(script_base_path, 'app/ui.js'), {});
-            return promisify(b.bundle).call(b)
-                .then(buf => writeFile(out_path, buf))
-                .then(() => []);
-        },
-        noCopyOverride: () => {},
-        removeModules: true,
-    },
-    'systemjs': {
-        appWriter: (base_out_path, script_base_path, out_path) => {
-            const ui_path = path.relative(base_out_path,
-                                          path.join(script_base_path, 'app', 'ui.js'));
-            return writeFile(out_path, `SystemJS.import("${ui_path}");`)
-                .then(() => {
-                    console.log(`Please place SystemJS in ${path.join(script_base_path, 'system-production.js')}`);
-                // FIXME: Should probably be in the legacy directory
-                    const promise_path = path.relative(base_out_path,
-                                                       path.join(base_out_path, 'vendor', 'promise.js'));
-                    const systemjs_path = path.relative(base_out_path,
-                                                        path.join(script_base_path, 'system-production.js'));
-                    return [ promise_path, systemjs_path ];
-                });
-        },
-        noCopyOverride: (paths, no_copy_files) => {
-            no_copy_files.delete(path.join(paths.vendor, 'promise.js'));
-        },
-    },
-    'umd': {
-        optionsOverride: (opts) => {
-            // umd supports properly shifting the default export to work as normal
-            opts.plugins.unshift("add-module-exports");
-        },
-    },
-};
diff --git a/systemvm/agent/noVNC/utils/validate b/systemvm/agent/noVNC/utils/validate
deleted file mode 100755
index a6b5507..0000000
--- a/systemvm/agent/noVNC/utils/validate
+++ /dev/null
@@ -1,45 +0,0 @@
-#!/bin/bash
-
-set -e
-
-RET=0
-
-OUT=`mktemp`
-
-for fn in "$@"; do
-	echo "Validating $fn..."
-	echo
-
-	case $fn in
-		*.html)
-			type="text/html"
-			;;
-		*.css)
-			type="text/css"
-			;;
-		*)
-			echo "Unknown format!"
-			echo
-			RET=1
-			continue
-			;;
-	esac
-
-	curl --silent \
-		--header "Content-Type: ${type}; charset=utf-8" \
-		--data-binary @${fn} \
-		https://validator.w3.org/nu/?out=text > $OUT
-	cat $OUT
-	echo
-
-	# We don't fail the check for warnings as some warnings are
-	# not relevant for us, and we don't currently have a way to
-	# ignore just those
-	if grep -q -s -E "^Error:" $OUT; then
-		RET=1
-	fi
-done
-
-rm $OUT
-
-exit $RET
diff --git a/systemvm/agent/noVNC/vendor/browser-es-module-loader/README.md b/systemvm/agent/noVNC/vendor/browser-es-module-loader/README.md
index c26867f..a50cc37 100644
--- a/systemvm/agent/noVNC/vendor/browser-es-module-loader/README.md
+++ b/systemvm/agent/noVNC/vendor/browser-es-module-loader/README.md
@@ -6,8 +6,8 @@
 https://github.com/ModuleLoader/browser-es-module-loader, but uses
 WebWorkers to compile the modules in the background.
 
-To generate, run `rollup -c` in this directory, and then run `browserify
-src/babel-worker.js > dist/babel-worker.js`.
+To generate, run `npx rollup -c` in this directory, and then run
+`./genworker.js`.
 
 LICENSE
 -------
diff --git a/systemvm/agent/noVNC/vendor/browser-es-module-loader/genworker.js b/systemvm/agent/noVNC/vendor/browser-es-module-loader/genworker.js
new file mode 100755
index 0000000..dbf5d2f
--- /dev/null
+++ b/systemvm/agent/noVNC/vendor/browser-es-module-loader/genworker.js
@@ -0,0 +1,13 @@
+#!/usr/bin/env node
+
+var fs = require("fs");
+var browserify = require("browserify");
+
+browserify("src/babel-worker.js")
+  .transform("babelify", {
+    presets: [ [ "@babel/preset-env", { targets: "ie >= 11" } ] ],
+    global: true,
+    ignore: [ "../../node_modules/core-js" ]
+  })
+  .bundle()
+  .pipe(fs.createWriteStream("dist/babel-worker.js"));
diff --git a/systemvm/agent/noVNC/vendor/browser-es-module-loader/rollup.config.js b/systemvm/agent/noVNC/vendor/browser-es-module-loader/rollup.config.js
index 4bf4a5f..33a4a24 100644
--- a/systemvm/agent/noVNC/vendor/browser-es-module-loader/rollup.config.js
+++ b/systemvm/agent/noVNC/vendor/browser-es-module-loader/rollup.config.js
@@ -1,16 +1,15 @@
 import nodeResolve from 'rollup-plugin-node-resolve';
 
 export default {
-  entry: 'src/browser-es-module-loader.js',
-  dest: 'dist/browser-es-module-loader.js',
-  format: 'umd',
-  moduleName: 'BrowserESModuleLoader',
-  sourceMap: true,
+  input: 'src/browser-es-module-loader.js',
+  output: {
+    file: 'dist/browser-es-module-loader.js',
+    format: 'umd',
+    name: 'BrowserESModuleLoader',
+    sourcemap: true,
+  },
 
   plugins: [
     nodeResolve(),
   ],
-
-  // skip rollup warnings (specifically the eval warning)
-  onwarn: function() {}
 };
diff --git a/systemvm/agent/noVNC/vendor/browser-es-module-loader/src/babel-worker.js b/systemvm/agent/noVNC/vendor/browser-es-module-loader/src/babel-worker.js
index 007bd68..19e23bf 100644
--- a/systemvm/agent/noVNC/vendor/browser-es-module-loader/src/babel-worker.js
+++ b/systemvm/agent/noVNC/vendor/browser-es-module-loader/src/babel-worker.js
@@ -1,12 +1,10 @@
-/*import { transform as babelTransform } from 'babel-core';
-import babelTransformDynamicImport from 'babel-plugin-syntax-dynamic-import';
-import babelTransformES2015ModulesSystemJS from 'babel-plugin-transform-es2015-modules-systemjs';*/
+// Polyfills needed for Babel to function
+require("core-js");
 
-// sadly, due to how rollup works, we can't use es6 imports here
-var babelTransform = require('babel-core').transform;
-var babelTransformDynamicImport = require('babel-plugin-syntax-dynamic-import');
-var babelTransformES2015ModulesSystemJS = require('babel-plugin-transform-es2015-modules-systemjs');
-var babelPresetES2015 = require('babel-preset-es2015');
+var babelTransform = require('@babel/core').transform;
+var babelTransformDynamicImport = require('@babel/plugin-syntax-dynamic-import');
+var babelTransformModulesSystemJS = require('@babel/plugin-transform-modules-systemjs');
+var babelPresetEnv = require('@babel/preset-env');
 
 self.onmessage = function (evt) {
     // transform source with Babel
@@ -17,8 +15,8 @@
       moduleIds: false,
       sourceMaps: 'inline',
       babelrc: false,
-      plugins: [babelTransformDynamicImport, babelTransformES2015ModulesSystemJS],
-      presets: [babelPresetES2015],
+      plugins: [babelTransformDynamicImport, babelTransformModulesSystemJS],
+      presets: [ [ babelPresetEnv, { targets: 'ie >= 11' } ] ],
     });
 
     self.postMessage({key: evt.data.key, code: output.code, source: evt.data.source});
diff --git a/systemvm/agent/noVNC/vendor/browser-es-module-loader/src/browser-es-module-loader.js b/systemvm/agent/noVNC/vendor/browser-es-module-loader/src/browser-es-module-loader.js
index efae617..9e50b8b 100644
--- a/systemvm/agent/noVNC/vendor/browser-es-module-loader/src/browser-es-module-loader.js
+++ b/systemvm/agent/noVNC/vendor/browser-es-module-loader/src/browser-es-module-loader.js
@@ -1,5 +1,4 @@
 import RegisterLoader from 'es-module-loader/core/register-loader.js';
-import { InternalModuleNamespace as ModuleNamespace } from 'es-module-loader/core/loader-polyfill.js';
 
 import { baseURI, global, isBrowser } from 'es-module-loader/core/common.js';
 import { resolveIfNotPlain } from 'es-module-loader/core/resolve.js';
diff --git a/systemvm/agent/noVNC/vendor/pako/lib/zlib/deflate.js b/systemvm/agent/noVNC/vendor/pako/lib/zlib/deflate.js
index c51915e..c3a5ba4 100644
--- a/systemvm/agent/noVNC/vendor/pako/lib/zlib/deflate.js
+++ b/systemvm/agent/noVNC/vendor/pako/lib/zlib/deflate.js
@@ -9,51 +9,51 @@
 
 
 /* Allowed flush values; see deflate() and inflate() below for details */
-var Z_NO_FLUSH      = 0;
-var Z_PARTIAL_FLUSH = 1;
-//var Z_SYNC_FLUSH    = 2;
-var Z_FULL_FLUSH    = 3;
-var Z_FINISH        = 4;
-var Z_BLOCK         = 5;
-//var Z_TREES         = 6;
+export const Z_NO_FLUSH      = 0;
+export const Z_PARTIAL_FLUSH = 1;
+//export const Z_SYNC_FLUSH    = 2;
+export const Z_FULL_FLUSH    = 3;
+export const Z_FINISH        = 4;
+export const Z_BLOCK         = 5;
+//export const Z_TREES         = 6;
 
 
 /* Return codes for the compression/decompression functions. Negative values
  * are errors, positive values are used for special but normal events.
  */
-var Z_OK            = 0;
-var Z_STREAM_END    = 1;
-//var Z_NEED_DICT     = 2;
-//var Z_ERRNO         = -1;
-var Z_STREAM_ERROR  = -2;
-var Z_DATA_ERROR    = -3;
-//var Z_MEM_ERROR     = -4;
-var Z_BUF_ERROR     = -5;
-//var Z_VERSION_ERROR = -6;
+export const Z_OK            = 0;
+export const Z_STREAM_END    = 1;
+//export const Z_NEED_DICT     = 2;
+//export const Z_ERRNO         = -1;
+export const Z_STREAM_ERROR  = -2;
+export const Z_DATA_ERROR    = -3;
+//export const Z_MEM_ERROR     = -4;
+export const Z_BUF_ERROR     = -5;
+//export const Z_VERSION_ERROR = -6;
 
 
 /* compression levels */
-//var Z_NO_COMPRESSION      = 0;
-//var Z_BEST_SPEED          = 1;
-//var Z_BEST_COMPRESSION    = 9;
-var Z_DEFAULT_COMPRESSION = -1;
+//export const Z_NO_COMPRESSION      = 0;
+//export const Z_BEST_SPEED          = 1;
+//export const Z_BEST_COMPRESSION    = 9;
+export const Z_DEFAULT_COMPRESSION = -1;
 
 
-var Z_FILTERED            = 1;
-var Z_HUFFMAN_ONLY        = 2;
-var Z_RLE                 = 3;
-var Z_FIXED               = 4;
-var Z_DEFAULT_STRATEGY    = 0;
+export const Z_FILTERED            = 1;
+export const Z_HUFFMAN_ONLY        = 2;
+export const Z_RLE                 = 3;
+export const Z_FIXED               = 4;
+export const Z_DEFAULT_STRATEGY    = 0;
 
 /* Possible values of the data_type field (though see inflate()) */
-//var Z_BINARY              = 0;
-//var Z_TEXT                = 1;
-//var Z_ASCII               = 1; // = Z_TEXT
-var Z_UNKNOWN             = 2;
+//export const Z_BINARY              = 0;
+//export const Z_TEXT                = 1;
+//export const Z_ASCII               = 1; // = Z_TEXT
+export const Z_UNKNOWN             = 2;
 
 
 /* The deflate compression method */
-var Z_DEFLATED  = 8;
+export const Z_DEFLATED  = 8;
 
 /*============================================================================*/
 
diff --git a/systemvm/agent/noVNC/vendor/pako/lib/zlib/inflate.js b/systemvm/agent/noVNC/vendor/pako/lib/zlib/inflate.js
index b79b396..1d2063b 100644
--- a/systemvm/agent/noVNC/vendor/pako/lib/zlib/inflate.js
+++ b/systemvm/agent/noVNC/vendor/pako/lib/zlib/inflate.js
@@ -13,30 +13,30 @@
 
 
 /* Allowed flush values; see deflate() and inflate() below for details */
-//var Z_NO_FLUSH      = 0;
-//var Z_PARTIAL_FLUSH = 1;
-//var Z_SYNC_FLUSH    = 2;
-//var Z_FULL_FLUSH    = 3;
-var Z_FINISH        = 4;
-var Z_BLOCK         = 5;
-var Z_TREES         = 6;
+//export const Z_NO_FLUSH      = 0;
+//export const Z_PARTIAL_FLUSH = 1;
+//export const Z_SYNC_FLUSH    = 2;
+//export const Z_FULL_FLUSH    = 3;
+export const Z_FINISH        = 4;
+export const Z_BLOCK         = 5;
+export const Z_TREES         = 6;
 
 
 /* Return codes for the compression/decompression functions. Negative values
  * are errors, positive values are used for special but normal events.
  */
-var Z_OK            = 0;
-var Z_STREAM_END    = 1;
-var Z_NEED_DICT     = 2;
-//var Z_ERRNO         = -1;
-var Z_STREAM_ERROR  = -2;
-var Z_DATA_ERROR    = -3;
-var Z_MEM_ERROR     = -4;
-var Z_BUF_ERROR     = -5;
-//var Z_VERSION_ERROR = -6;
+export const Z_OK            = 0;
+export const Z_STREAM_END    = 1;
+export const Z_NEED_DICT     = 2;
+//export const Z_ERRNO         = -1;
+export const Z_STREAM_ERROR  = -2;
+export const Z_DATA_ERROR    = -3;
+export const Z_MEM_ERROR     = -4;
+export const Z_BUF_ERROR     = -5;
+//export const Z_VERSION_ERROR = -6;
 
 /* The deflate compression method */
-var Z_DEFLATED  = 8;
+export const Z_DEFLATED  = 8;
 
 
 /* STATES ====================================================================*/
diff --git a/systemvm/agent/noVNC/vnc.html b/systemvm/agent/noVNC/vnc.html
index 212321b..a244a7d 100644
--- a/systemvm/agent/noVNC/vnc.html
+++ b/systemvm/agent/noVNC/vnc.html
@@ -4,7 +4,7 @@
 
     <!--
     noVNC example: simple example using default UI
-    Copyright (C) 2018 The noVNC Authors
+    Copyright (C) 2019 The noVNC Authors
     noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
     This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
 
@@ -17,6 +17,10 @@
 
     <meta charset="utf-8">
 
+    <!-- Always force latest IE rendering engine (even in intranet) & Chrome Frame
+                Remove this if you use the .htaccess -->
+    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+
     <!-- Icons (see app/images/icons/Makefile for what the sizes are for) -->
     <link rel="icon" sizes="16x16" type="image/png" href="app/images/icons/novnc-16x16.png">
     <link rel="icon" sizes="24x24" type="image/png" href="app/images/icons/novnc-24x24.png">
@@ -57,23 +61,13 @@
     <!-- promise polyfills promises for IE11 -->
     <script src="vendor/promise.js"></script>
     <!-- ES2015/ES6 modules polyfill -->
-    <script type="module">
-        window._noVNC_has_module_support = true;
-    </script>
-    <script>
-        window.addEventListener("load", function() {
-            if (window._noVNC_has_module_support) return;
-            var loader = document.createElement("script");
-            loader.src = "vendor/browser-es-module-loader/dist/browser-es-module-loader.js";
-            document.head.appendChild(loader);
-        });
-    </script>
+    <script nomodule src="vendor/browser-es-module-loader/dist/browser-es-module-loader.js"></script>
     <!-- actual script modules -->
     <script type="module" crossorigin="anonymous" src="app/ui.js"></script>
     <!-- end scripts -->
 </head>
 
-<body>
+<body id="body">
 
     <div id="noVNC_fallback_error" class="noVNC_center">
         <div>
@@ -91,68 +85,54 @@
 
             <div class="noVNC_scroll">
 
-            <h1 class="noVNC_logo" translate="no"><span>no</span><br>VNC</h1>
+            <h1 class="noVNC_logo" translate="no" style="display: none;"><span>no</span><br>VNC</h1>
 
             <!-- Drag/Pan the viewport -->
-            <input type="image" alt="viewport drag" src="app/images/drag.svg"
+            <input type="image" alt="Drag" src="app/images/drag.png"
                 id="noVNC_view_drag_button" class="noVNC_button noVNC_hidden"
                 title="Move/Drag Viewport">
 
             <!--noVNC Touch Device only buttons-->
-            <div id="noVNC_mobile_buttons">
-                <input type="image" alt="No mousebutton" src="app/images/mouse_none.svg"
-                    id="noVNC_mouse_button0" class="noVNC_button"
-                    title="Active Mouse Button">
-                <input type="image" alt="Left mousebutton" src="app/images/mouse_left.svg"
-                    id="noVNC_mouse_button1" class="noVNC_button"
-                    title="Active Mouse Button">
-                <input type="image" alt="Middle mousebutton" src="app/images/mouse_middle.svg"
-                    id="noVNC_mouse_button2" class="noVNC_button"
-                    title="Active Mouse Button">
-                <input type="image" alt="Right mousebutton" src="app/images/mouse_right.svg"
-                    id="noVNC_mouse_button4" class="noVNC_button"
-                    title="Active Mouse Button">
-                <input type="image" alt="Keyboard" src="app/images/keyboard.svg"
+            <div id="noVNC_mobile_buttons" style="display: none;">
+                <input type="image" alt="Keyboard" src="app/images/keyboard.png"
                     id="noVNC_keyboard_button" class="noVNC_button" title="Show Keyboard">
             </div>
 
             <!-- Extra manual keys -->
-            <div id="noVNC_extra_keys">
-                <input type="image" alt="Extra keys" src="app/images/toggleextrakeys.svg"
-                    id="noVNC_toggle_extra_keys_button" class="noVNC_button"
-                    title="Show Extra Keys">
-                <div class="noVNC_vcenter">
-                <div id="noVNC_modifiers" class="noVNC_panel">
-                    <input type="image" alt="Ctrl" src="app/images/ctrl.svg"
-                        id="noVNC_toggle_ctrl_button" class="noVNC_button"
-                        title="Toggle Ctrl">
-                    <input type="image" alt="Alt" src="app/images/alt.svg"
-                        id="noVNC_toggle_alt_button" class="noVNC_button"
-                        title="Toggle Alt">
-                    <input type="image" alt="Windows" src="app/images/windows.svg"
-                        id="noVNC_toggle_windows_button" class="noVNC_button"
-                        title="Toggle Windows">
-                    <input type="image" alt="Tab" src="app/images/tab.svg"
-                        id="noVNC_send_tab_button" class="noVNC_button"
-                        title="Send Tab">
-                    <input type="image" alt="Esc" src="app/images/esc.svg"
-                        id="noVNC_send_esc_button" class="noVNC_button"
-                        title="Send Escape">
-                    <input type="image" alt="Ctrl+Alt+Del" src="app/images/ctrlaltdel.svg"
-                        id="noVNC_send_ctrl_alt_del_button" class="noVNC_button"
-                        title="Send Ctrl-Alt-Del">
-                </div>
-                </div>
+            <input type="image" alt="Extra keys" src="app/images/toggleextrakeys.png"
+                id="noVNC_toggle_extra_keys_button" class="noVNC_button"
+                title="Show Extra Keys">
+            <div class="noVNC_vcenter">
+            <div id="noVNC_modifiers" class="noVNC_panel">
+                <input type="image" alt="Ctrl" src="app/images/ctrl.png"
+                    id="noVNC_toggle_ctrl_button" class="noVNC_button"
+                    title="Toggle Ctrl">
+                <input type="image" alt="Alt" src="app/images/alt.png"
+                    id="noVNC_toggle_alt_button" class="noVNC_button"
+                    title="Toggle Alt">
+                <input type="image" alt="Windows" src="app/images/windows.png"
+                    id="noVNC_toggle_windows_button" class="noVNC_button"
+                    title="Toggle Windows">
+                <input type="image" alt="Tab" src="app/images/tab.png"
+                    id="noVNC_send_tab_button" class="noVNC_button"
+                    title="Send Tab">
+                <input type="image" alt="Esc" src="app/images/esc.png"
+                    id="noVNC_send_esc_button" class="noVNC_button"
+                    title="Send Escape">
+                <input type="image" alt="Ctrl+Alt+Del" src="app/images/ctrlaltdel.png"
+                    id="noVNC_send_ctrl_alt_del_button" class="noVNC_button"
+                    title="Send Ctrl-Alt-Del">
+            </div>
             </div>
 
             <!-- Shutdown/Reboot -->
-            <input type="image" alt="Shutdown/Reboot" src="app/images/power.svg"
+            <input type="image" alt="Shutdown/Reboot" src="app/images/power.png"
                 id="noVNC_power_button" class="noVNC_button"
                 title="Shutdown/Reboot...">
             <div class="noVNC_vcenter">
             <div id="noVNC_power" class="noVNC_panel">
                 <div class="noVNC_heading">
-                    <img alt="" src="app/images/power.svg"> Power
+                    <img alt="" src="app/images/power.png"> Power
                 </div>
                 <input type="button" id="noVNC_shutdown_button" value="Shutdown">
                 <input type="button" id="noVNC_reboot_button" value="Reboot">
@@ -161,110 +141,132 @@
             </div>
 
             <!-- Clipboard -->
-            <input type="image" alt="Clipboard" src="app/images/clipboard.svg"
+            <input type="image" alt="Clipboard" src="app/images/clipboard.png"
                 id="noVNC_clipboard_button" class="noVNC_button"
                 title="Clipboard">
             <div class="noVNC_vcenter">
             <div id="noVNC_clipboard" class="noVNC_panel">
                 <div class="noVNC_heading">
-                    <img alt="" src="app/images/clipboard.svg"> Clipboard
+                    <img alt="" src="app/images/clipboard.png"> Clipboard
                 </div>
                 <textarea id="noVNC_clipboard_text" rows=5></textarea>
                 <br>
+                <input id="noVNC_clipboard_send_button" type="button"
+                    value="Send" style="color: white; background: rgb(75, 93, 118);" class="noVNC_submit">
                 <input id="noVNC_clipboard_clear_button" type="button"
                     value="Clear" class="noVNC_submit">
             </div>
             </div>
 
             <!-- Toggle fullscreen -->
-            <input type="image" alt="Fullscreen" src="app/images/fullscreen.svg"
+            <input type="image" alt="Fullscreen" src="app/images/fullscreen.png"
                 id="noVNC_fullscreen_button" class="noVNC_button noVNC_hidden"
                 title="Fullscreen">
 
             <!-- Settings -->
-            <input type="image" alt="Settings" src="app/images/settings.svg"
-                id="noVNC_settings_button" class="noVNC_button"
-                title="Settings">
-            <div class="noVNC_vcenter">
-            <div id="noVNC_settings" class="noVNC_panel">
-                <ul>
-                    <li class="noVNC_heading">
-                        <img alt="" src="app/images/settings.svg"> Settings
-                    </li>
-                    <li>
-                        <label><input id="noVNC_setting_shared" type="checkbox"> Shared Mode</label>
-                    </li>
-                    <li>
-                        <label><input id="noVNC_setting_view_only" type="checkbox"> View Only</label>
-                    </li>
-                    <li><hr></li>
-                    <li>
-                        <label><input id="noVNC_setting_view_clip" type="checkbox"> Clip to Window</label>
-                    </li>
-                    <li>
-                        <label for="noVNC_setting_resize">Scaling Mode:</label>
-                        <select id="noVNC_setting_resize" name="vncResize">
-                            <option value="off">None</option>
-                            <option value="scale">Local Scaling</option>
-                            <option value="remote">Remote Resizing</option>
-                        </select>
-                    </li>
-                    <li><hr></li>
-                    <li>
-                        <div class="noVNC_expander">Advanced</div>
-                        <div><ul>
-                            <li>
-                                <label for="noVNC_setting_repeaterID">Repeater ID:</label>
-                                <input id="noVNC_setting_repeaterID" type="text" value="">
-                            </li>
-                            <li>
-                                <div class="noVNC_expander">WebSocket</div>
-                                <div><ul>
-                                    <li>
-                                        <label><input id="noVNC_setting_encrypt" type="checkbox"> Encrypt</label>
-                                    </li>
-                                    <li>
-                                        <label for="noVNC_setting_host">Host:</label>
-                                        <input id="noVNC_setting_host">
-                                    </li>
-                                    <li>
-                                        <label for="noVNC_setting_port">Port:</label>
-                                        <input id="noVNC_setting_port" type="number">
-                                    </li>
-                                    <li>
-                                        <label for="noVNC_setting_path">Path:</label>
-                                        <input id="noVNC_setting_path" type="text" value="websockify">
-                                    </li>
-                                </ul></div>
-                            </li>
-                            <li><hr></li>
-                            <li>
-                                <label><input id="noVNC_setting_reconnect" type="checkbox"> Automatic Reconnect</label>
-                            </li>
-                            <li>
-                                <label for="noVNC_setting_reconnect_delay">Reconnect Delay (ms):</label>
-                                <input id="noVNC_setting_reconnect_delay" type="number">
-                            </li>
-                            <li><hr></li>
-                            <li>
-                                <label><input id="noVNC_setting_show_dot" type="checkbox"> Show Dot when No Cursor</label>
-                            </li>
-                            <li><hr></li>
-                            <!-- Logging selection dropdown -->
-                            <li>
-                                <label>Logging:
-                                    <select id="noVNC_setting_logging" name="vncLogging">
-                                    </select>
-                                </label>
-                            </li>
-                        </ul></div>
-                    </li>
-                </ul>
-            </div>
-            </div>
+            <span style="display: none;">
+                <input type="image" alt="Settings" src="app/images/settings.png"
+                    id="noVNC_settings_button" class="noVNC_button"
+                    title="Settings">
+                <div class="noVNC_vcenter">
+                <div id="noVNC_settings" class="noVNC_panel">
+                    <ul>
+                        <li class="noVNC_heading">
+                            <img alt="" src="app/images/settings.png"> Settings
+                        </li>
+                        <li>
+                            <label><input id="noVNC_setting_shared" type="checkbox"> Shared Mode</label>
+                        </li>
+                        <li>
+                            <label><input id="noVNC_setting_view_only" type="checkbox"> View Only</label>
+                        </li>
+                        <li><hr></li>
+                        <li>
+                            <label><input id="noVNC_setting_view_clip" type="checkbox"> Clip to Window</label>
+                        </li>
+                        <li>
+                            <label for="noVNC_setting_resize">Scaling Mode:</label>
+                            <select id="noVNC_setting_resize" name="vncResize">
+                                <option value="off">None</option>
+                                <option value="scale">Local Scaling</option>
+                                <option value="remote">Remote Resizing</option>
+                            </select>
+                        </li>
+                        <li><hr></li>
+                        <li>
+                            <div class="noVNC_expander">Advanced</div>
+                            <div><ul>
+                                <li>
+                                    <label for="noVNC_setting_quality">Quality:</label>
+                                    <input id="noVNC_setting_quality" type="range" min="0" max="9" value="6">
+                                </li>
+                                <li>
+                                    <label for="noVNC_setting_compression">Compression level:</label>
+                                    <input id="noVNC_setting_compression" type="range" min="0" max="9" value="2">
+                                </li>
+                                <li><hr></li>
+                                <li>
+                                    <label for="noVNC_setting_repeaterID">Repeater ID:</label>
+                                    <input id="noVNC_setting_repeaterID" type="text" value="">
+                                </li>
+                                <li>
+                                    <div class="noVNC_expander">WebSocket</div>
+                                    <div><ul>
+                                        <li>
+                                            <label><input id="noVNC_setting_encrypt" type="checkbox"> Encrypt</label>
+                                        </li>
+                                        <li>
+                                            <label for="noVNC_setting_host">Host:</label>
+                                            <input id="noVNC_setting_host">
+                                        </li>
+                                        <li>
+                                            <label for="noVNC_setting_port">Port:</label>
+                                            <input id="noVNC_setting_port" type="number">
+                                        </li>
+                                        <li>
+                                            <label for="noVNC_setting_path">Path:</label>
+                                            <input id="noVNC_setting_path" type="text" value="websockify">
+                                        </li>
+                                        <li>
+                                            <label for="noVNC_setting_token">Token:</label>
+                                            <input id="noVNC_setting_token" type="text">
+                                        </li>
+                                    </ul></div>
+                                </li>
+                                <li><hr></li>
+                                <li>
+                                    <label><input id="noVNC_setting_reconnect" type="checkbox"> Automatic Reconnect</label>
+                                </li>
+                                <li>
+                                    <label for="noVNC_setting_reconnect_delay">Reconnect Delay (ms):</label>
+                                    <input id="noVNC_setting_reconnect_delay" type="number">
+                                </li>
+                                <li><hr></li>
+                                <li>
+                                    <label><input id="noVNC_setting_show_dot" type="checkbox"> Show Dot when No Cursor</label>
+                                </li>
+                                <li><hr></li>
+                                <!-- Logging selection dropdown -->
+                                <li>
+                                    <label>Logging:
+                                        <select id="noVNC_setting_logging" name="vncLogging">
+                                        </select>
+                                    </label>
+                                </li>
+                            </ul></div>
+                        </li>
+                        <li class="noVNC_version_separator"><hr></li>
+                        <li class="noVNC_version_wrapper">
+                            <span>Version:</span>
+                            <span class="noVNC_version"></span>
+                        </li>
+                    </ul>
+                </div>
+                </div>
+            </span>
 
             <!-- Connection Controls -->
-            <input type="image" alt="Disconnect" src="app/images/disconnect.svg"
+            <input style="display: none;" type="image" alt="Disconnect" src="app/images/disconnect.png"
                 id="noVNC_disconnect_button" class="noVNC_button"
                 title="Disconnect">
 
@@ -279,25 +281,29 @@
     <div id="noVNC_status"></div>
 
     <!-- Connect button -->
-    <div class="noVNC_center">
+    <div class="noVNC_center" style="display: none;">
         <div id="noVNC_connect_dlg">
             <div class="noVNC_logo" translate="no"><span>no</span>VNC</div>
             <div id="noVNC_connect_button"><div>
-                <img alt="" src="app/images/connect.svg"> Connect
+                <img alt="" src="app/images/connect.png"> Connect
             </div></div>
         </div>
     </div>
 
     <!-- Password Dialog -->
     <div class="noVNC_center noVNC_connect_layer">
-    <div id="noVNC_password_dlg" class="noVNC_panel"><form>
+    <div id="noVNC_credentials_dlg" class="noVNC_panel"><form>
         <ul>
-            <li>
+            <li id="noVNC_username_block">
+                <label>Username:</label>
+                <input id="noVNC_username_input">
+            </li>
+            <li id="noVNC_password_block">
                 <label>Password:</label>
                 <input id="noVNC_password_input" type="password">
             </li>
             <li>
-                <input id="noVNC_password_button" type="submit" value="Send Password" class="noVNC_submit">
+                <input id="noVNC_credentials_button" type="submit" value="Send Credentials" class="noVNC_submit">
             </li>
         </ul>
     </form></div>
@@ -327,4 +333,9 @@
         <source src="app/sounds/bell.mp3" type="audio/mpeg">
     </audio>
  </body>
+ <script type="application/javascript">
+    window.onload = function() {
+        document.getElementById("noVNC_connect_button").click();
+    }
+ </script>
 </html>
diff --git a/systemvm/agent/noVNC/vnc_lite.html b/systemvm/agent/noVNC/vnc_lite.html
index 12ac1d5..0be2b53 100644
--- a/systemvm/agent/noVNC/vnc_lite.html
+++ b/systemvm/agent/noVNC/vnc_lite.html
@@ -7,7 +7,7 @@
 
     This is a self-contained file which doesn't import WebUtil or external CSS.
 
-    Copyright (C) 2018 The noVNC Authors
+    Copyright (C) 2019 The noVNC Authors
     noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
     This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
 
@@ -18,6 +18,10 @@
 
     <meta charset="utf-8">
 
+    <!-- Always force latest IE rendering engine (even in intranet) &
+                Chrome Frame. Remove this if you use the .htaccess -->
+    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
+
     <style>
 
         body {
@@ -68,18 +72,7 @@
     <script src="vendor/promise.js"></script>
 
     <!-- ES2015/ES6 modules polyfill -->
-    <script type="module">
-        window._noVNC_has_module_support = true;
-    </script>
-    <script>
-        window.addEventListener("load", function() {
-            if (window._noVNC_has_module_support) return;
-            const loader = document.createElement("script");
-            loader.src = "vendor/browser-es-module-loader/dist/" +
-                "browser-es-module-loader.js";
-            document.head.appendChild(loader);
-        });
-    </script>
+    <script nomodule src="vendor/browser-es-module-loader/dist/browser-es-module-loader.js"></script>
 
     <!-- actual script modules -->
     <script type="module" crossorigin="anonymous">