mirror of
https://github.com/retspen/webvirtcloud
synced 2024-12-24 23:25:24 +00:00
Merge pull request #244 from catborise/master
Dockerfile and Requirement Updates
This commit is contained in:
commit
0ac126ee0d
56 changed files with 18379 additions and 19848 deletions
|
@ -1,4 +1,4 @@
|
||||||
FROM phusion/baseimage:0.9.17
|
FROM phusion/baseimage:0.11
|
||||||
MAINTAINER Jethro Yu <comet.jc@gmail.com>
|
MAINTAINER Jethro Yu <comet.jc@gmail.com>
|
||||||
|
|
||||||
RUN echo 'APT::Get::Clean=always;' >> /etc/apt/apt.conf.d/99AutomaticClean
|
RUN echo 'APT::Get::Clean=always;' >> /etc/apt/apt.conf.d/99AutomaticClean
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
Django==1.11.20
|
Django==1.11.21
|
||||||
websockify==0.8.0
|
websockify==0.8.0
|
||||||
gunicorn==19.9.0
|
gunicorn==19.9.0
|
||||||
lxml==4.2.5
|
lxml==4.2.5
|
||||||
libvirt-python==4.10.0
|
libvirt-python==5.3.0
|
||||||
pytz
|
pytz
|
||||||
rwlock
|
rwlock
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<link rel="shortcut icon" href="{% static "img/favicon.ico" %}">
|
<link rel="shortcut icon" href="{% static "favicon.ico" %}">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link rel="stylesheet" href="{% static "css/bootstrap.min.css" %}">
|
<link rel="stylesheet" href="{% static "css/bootstrap.min.css" %}">
|
||||||
<link href="{% static "css/webvirtcloud.css" %}" rel="stylesheet">
|
<link href="{% static "css/webvirtcloud.css" %}" rel="stylesheet">
|
||||||
|
|
|
@ -5,12 +5,9 @@
|
||||||
{% block head %}
|
{% block head %}
|
||||||
<!--
|
<!--
|
||||||
noVNC example: simple example using default UI
|
noVNC example: simple example using default UI
|
||||||
Copyright (C) 2012 Joel Martin
|
Copyright (C) 2018 The noVNC Authors
|
||||||
Copyright (C) 2016 Samuel Mannehed for Cendio AB
|
|
||||||
Copyright (C) 2016 Pierre Ossman for Cendio AB
|
|
||||||
noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
|
noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
|
||||||
This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
|
This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
|
||||||
|
|
||||||
Connect parameters are provided in query string:
|
Connect parameters are provided in query string:
|
||||||
http://example.com/?host=HOST&port=PORT&encrypt=1
|
http://example.com/?host=HOST&port=PORT&encrypt=1
|
||||||
or the fragment:
|
or the fragment:
|
||||||
|
@ -20,10 +17,6 @@
|
||||||
|
|
||||||
<meta charset="utf-8" />
|
<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,chrome=1" />
|
|
||||||
|
|
||||||
<!-- Icons (see Makefile for what the sizes are for) -->
|
<!-- Icons (see Makefile for what the sizes are for) -->
|
||||||
<link rel="icon" sizes="16x16" type="image/png" href="{% static "js/novnc/app/images/icons/novnc-16x16.png" %}">
|
<link rel="icon" sizes="16x16" type="image/png" href="{% static "js/novnc/app/images/icons/novnc-16x16.png" %}">
|
||||||
<link rel="icon" sizes="24x24" type="image/png" href="{% static "js/novnc/app/images/icons/novnc-24x24.png" %}">
|
<link rel="icon" sizes="24x24" type="image/png" href="{% static "js/novnc/app/images/icons/novnc-24x24.png" %}">
|
||||||
|
@ -140,6 +133,9 @@
|
||||||
<input type="image" alt="Alt" src="{% static "js/novnc/app/images/alt.svg" %}"
|
<input type="image" alt="Alt" src="{% static "js/novnc/app/images/alt.svg" %}"
|
||||||
id="noVNC_toggle_alt_button" class="noVNC_button"
|
id="noVNC_toggle_alt_button" class="noVNC_button"
|
||||||
title="Toggle Alt"/>
|
title="Toggle Alt"/>
|
||||||
|
<input type="image" alt="Windows" src="{% static "js/novnc/app/images/windows.svg" %}"
|
||||||
|
id="noVNC_toggle_windows_button" class="noVNC_button"
|
||||||
|
title="Toggle Windows">
|
||||||
<input type="image" alt="Tab" src="{% static "js/novnc/app/images/tab.svg" %}"
|
<input type="image" alt="Tab" src="{% static "js/novnc/app/images/tab.svg" %}"
|
||||||
id="noVNC_send_tab_button" class="noVNC_button"
|
id="noVNC_send_tab_button" class="noVNC_button"
|
||||||
title="Send Tab"/>
|
title="Send Tab"/>
|
||||||
|
@ -254,6 +250,10 @@
|
||||||
<label for="noVNC_setting_reconnect_delay">Reconnect Delay (ms):</label>
|
<label for="noVNC_setting_reconnect_delay">Reconnect Delay (ms):</label>
|
||||||
<input id="noVNC_setting_reconnect_delay" type="number" />
|
<input id="noVNC_setting_reconnect_delay" type="number" />
|
||||||
</li>
|
</li>
|
||||||
|
<li><hr></li>
|
||||||
|
<li>
|
||||||
|
<label><input id="noVNC_setting_show_dot" type="checkbox"> Show Dot when No Cursor</label>
|
||||||
|
</li>
|
||||||
<li><hr></li>
|
<li><hr></li>
|
||||||
<!-- Logging selection dropdown -->
|
<!-- Logging selection dropdown -->
|
||||||
<li>
|
<li>
|
||||||
|
|
|
@ -689,8 +689,6 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function get_cust_vols(compute_id, pool) {
|
function get_cust_vols(compute_id, pool) {
|
||||||
get_vol_url = "/computes/" + compute_id + "/storage/" + pool + "/volumes";
|
get_vol_url = "/computes/" + compute_id + "/storage/" + pool + "/volumes";
|
||||||
$.getJSON(get_vol_url, function (data) {
|
$.getJSON(get_vol_url, function (data) {
|
||||||
|
@ -716,7 +714,7 @@
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function get_disk_bus_choices(compute_id, dev_idx, disk_type){
|
function get_disk_bus_choices(compute_id, dev_idx, disk_type){
|
||||||
get_diskBus_url = "/computes/" + compute_id + "/disk/" + disk_type + "/buses";
|
get_diskBus_url = "/computes/" + compute_id + "/disk/" + disk_type + "/buses";
|
||||||
|
|
||||||
$.getJSON(get_diskBus_url, function (data) {
|
$.getJSON(get_diskBus_url, function (data) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
-r ../conf/requirements.txt
|
-r ../conf/requirements.txt
|
||||||
pep8==1.7.1
|
pep8==1.7.1
|
||||||
pyflakes==2.0.0
|
pyflakes==2.1.1
|
||||||
pylint==1.9.2
|
pylint==1.9.4
|
||||||
|
|
|
@ -84,11 +84,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
{% ifequal status 5 %}
|
<button type="submit" class="btn btn-lg btn-success pull-right" name="add_new_vol">{% trans "Add Volume" %}</button>
|
||||||
<button type="submit" class="btn btn-lg btn-success pull-right" name="add_new_vol">{% trans "Add Volume" %}</button>
|
|
||||||
{% else %}
|
|
||||||
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Add Volume" %}</button>
|
|
||||||
{% endifequal %}
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -142,11 +138,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
{% ifequal status 5 %}
|
<button type="submit" class="btn btn-lg btn-success pull-right" name="add_existing_vol">{% trans "Add Volume" %}</button>
|
||||||
<button type="submit" class="btn btn-lg btn-success pull-right" name="add_existing_vol">{% trans "Add Volume" %}</button>
|
|
||||||
{% else %}
|
|
||||||
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Add Volume" %}</button>
|
|
||||||
{% endifequal %}
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -2,21 +2,23 @@
|
||||||
// native support in the browsers, so that our error handler
|
// native support in the browsers, so that our error handler
|
||||||
// can catch script-loading errors.
|
// can catch script-loading errors.
|
||||||
|
|
||||||
|
// No ES6 can be used in this file since it's used for the translation
|
||||||
|
/* eslint-disable prefer-arrow-callback */
|
||||||
|
|
||||||
(function(){
|
(function _scope() {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// Fallback for all uncought errors
|
// Fallback for all uncought errors
|
||||||
function handleError (event, err) {
|
function handleError(event, err) {
|
||||||
try {
|
try {
|
||||||
var msg = document.getElementById('noVNC_fallback_errormsg');
|
const msg = document.getElementById('noVNC_fallback_errormsg');
|
||||||
|
|
||||||
// Only show the initial error
|
// Only show the initial error
|
||||||
if (msg.hasChildNodes()) {
|
if (msg.hasChildNodes()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var div = document.createElement("div");
|
let div = document.createElement("div");
|
||||||
div.classList.add('noVNC_message');
|
div.classList.add('noVNC_message');
|
||||||
div.appendChild(document.createTextNode(event.message));
|
div.appendChild(document.createTextNode(event.message));
|
||||||
msg.appendChild(div);
|
msg.appendChild(div);
|
||||||
|
@ -24,7 +26,7 @@
|
||||||
if (event.filename) {
|
if (event.filename) {
|
||||||
div = document.createElement("div");
|
div = document.createElement("div");
|
||||||
div.className = 'noVNC_location';
|
div.className = 'noVNC_location';
|
||||||
var text = event.filename;
|
let text = event.filename;
|
||||||
if (event.lineno !== undefined) {
|
if (event.lineno !== undefined) {
|
||||||
text += ":" + event.lineno;
|
text += ":" + event.lineno;
|
||||||
if (event.colno !== undefined) {
|
if (event.colno !== undefined) {
|
||||||
|
@ -35,7 +37,7 @@
|
||||||
msg.appendChild(div);
|
msg.appendChild(div);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (err && (err.stack !== undefined)) {
|
if (err && err.stack) {
|
||||||
div = document.createElement("div");
|
div = document.createElement("div");
|
||||||
div.className = 'noVNC_stack';
|
div.className = 'noVNC_stack';
|
||||||
div.appendChild(document.createTextNode(err.stack));
|
div.appendChild(document.createTextNode(err.stack));
|
||||||
|
@ -51,6 +53,6 @@
|
||||||
// from being printed to the browser console.
|
// from being printed to the browser console.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
window.addEventListener('error', function (evt) { handleError(evt, evt.error); });
|
window.addEventListener('error', function onerror(evt) { handleError(evt, evt.error); });
|
||||||
window.addEventListener('unhandledrejection', function (evt) { handleError(evt.reason, evt.reason); });
|
window.addEventListener('unhandledrejection', function onreject(evt) { handleError(evt.reason, evt.reason); });
|
||||||
})();
|
})();
|
||||||
|
|
85
static/js/novnc/app/images/windows.svg
Executable file
85
static/js/novnc/app/images/windows.svg
Executable file
|
@ -0,0 +1,85 @@
|
||||||
|
<?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>
|
After Width: | Height: | Size: 3.1 KiB |
71
static/js/novnc/app/locale/cs.json
Executable file
71
static/js/novnc/app/locale/cs.json
Executable file
|
@ -0,0 +1,71 @@
|
||||||
|
{
|
||||||
|
"Connecting...": "Připojení...",
|
||||||
|
"Disconnecting...": "Odpojení...",
|
||||||
|
"Reconnecting...": "Obnova připojení...",
|
||||||
|
"Internal error": "Vnitřní chyba",
|
||||||
|
"Must set host": "Hostitel musí být nastavení",
|
||||||
|
"Connected (encrypted) to ": "Připojení (šifrované) k ",
|
||||||
|
"Connected (unencrypted) to ": "Připojení (nešifrované) k ",
|
||||||
|
"Something went wrong, connection is closed": "Něco se pokazilo, odpojeno",
|
||||||
|
"Failed to connect to server": "Chyba připojení k serveru",
|
||||||
|
"Disconnected": "Odpojeno",
|
||||||
|
"New connection has been rejected with reason: ": "Nové připojení bylo odmítnuto s odůvodněním: ",
|
||||||
|
"New connection has been rejected": "Nové připojení bylo odmítnuto",
|
||||||
|
"Password is required": "Je vyžadováno heslo",
|
||||||
|
"noVNC encountered an error:": "noVNC narazilo na chybu:",
|
||||||
|
"Hide/Show the control bar": "Skrýt/zobrazit ovládací panel",
|
||||||
|
"Move/Drag Viewport": "Přesunout/přetáhnout výřez",
|
||||||
|
"viewport drag": "přesun výřezu",
|
||||||
|
"Active Mouse Button": "Aktivní tlačítka myši",
|
||||||
|
"No mousebutton": "Žádné",
|
||||||
|
"Left mousebutton": "Levé tlačítko myši",
|
||||||
|
"Middle mousebutton": "Prostřední tlačítko myši",
|
||||||
|
"Right mousebutton": "Pravé tlačítko myši",
|
||||||
|
"Keyboard": "Klávesnice",
|
||||||
|
"Show Keyboard": "Zobrazit klávesnici",
|
||||||
|
"Extra keys": "Extra klávesy",
|
||||||
|
"Show Extra Keys": "Zobrazit extra klávesy",
|
||||||
|
"Ctrl": "Ctrl",
|
||||||
|
"Toggle Ctrl": "Přepnout Ctrl",
|
||||||
|
"Alt": "Alt",
|
||||||
|
"Toggle Alt": "Přepnout Alt",
|
||||||
|
"Send Tab": "Odeslat tabulátor",
|
||||||
|
"Tab": "Tab",
|
||||||
|
"Esc": "Esc",
|
||||||
|
"Send Escape": "Odeslat Esc",
|
||||||
|
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
|
||||||
|
"Send Ctrl-Alt-Del": "Poslat Ctrl-Alt-Del",
|
||||||
|
"Shutdown/Reboot": "Vypnutí/Restart",
|
||||||
|
"Shutdown/Reboot...": "Vypnutí/Restart...",
|
||||||
|
"Power": "Napájení",
|
||||||
|
"Shutdown": "Vypnout",
|
||||||
|
"Reboot": "Restart",
|
||||||
|
"Reset": "Reset",
|
||||||
|
"Clipboard": "Schránka",
|
||||||
|
"Clear": "Vymazat",
|
||||||
|
"Fullscreen": "Celá obrazovka",
|
||||||
|
"Settings": "Nastavení",
|
||||||
|
"Shared Mode": "Sdílený režim",
|
||||||
|
"View Only": "Pouze prohlížení",
|
||||||
|
"Clip to Window": "Přizpůsobit oknu",
|
||||||
|
"Scaling Mode:": "Přizpůsobení velikosti",
|
||||||
|
"None": "Žádné",
|
||||||
|
"Local Scaling": "Místní",
|
||||||
|
"Remote Resizing": "Vzdálené",
|
||||||
|
"Advanced": "Pokročilé",
|
||||||
|
"Repeater ID:": "ID opakovače",
|
||||||
|
"WebSocket": "WebSocket",
|
||||||
|
"Encrypt": "Šifrování:",
|
||||||
|
"Host:": "Hostitel:",
|
||||||
|
"Port:": "Port:",
|
||||||
|
"Path:": "Cesta",
|
||||||
|
"Automatic Reconnect": "Automatická obnova připojení",
|
||||||
|
"Reconnect Delay (ms):": "Zpoždění připojení (ms)",
|
||||||
|
"Show Dot when No Cursor": "Tečka místo chybějícího kurzoru myši",
|
||||||
|
"Logging:": "Logování:",
|
||||||
|
"Disconnect": "Odpojit",
|
||||||
|
"Connect": "Připojit",
|
||||||
|
"Password:": "Heslo",
|
||||||
|
"Send Password": "Odeslat heslo",
|
||||||
|
"Cancel": "Zrušit"
|
||||||
|
}
|
70
static/js/novnc/app/locale/ko.json
Executable file
70
static/js/novnc/app/locale/ko.json
Executable file
|
@ -0,0 +1,70 @@
|
||||||
|
{
|
||||||
|
"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 켜기/끄기",
|
||||||
|
"Send Tab": "Tab 보내기",
|
||||||
|
"Tab": "Tab",
|
||||||
|
"Esc": "Esc",
|
||||||
|
"Send Escape": "Esc 보내기",
|
||||||
|
"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": "웹소켓",
|
||||||
|
"Encrypt": "암호화",
|
||||||
|
"Host:": "호스트:",
|
||||||
|
"Port:": "포트:",
|
||||||
|
"Path:": "위치:",
|
||||||
|
"Automatic Reconnect": "자동 재연결",
|
||||||
|
"Reconnect Delay (ms):": "재연결 지연 시간 (ms)",
|
||||||
|
"Logging:": "로깅",
|
||||||
|
"Disconnect": "연결 해제",
|
||||||
|
"Connect": "연결",
|
||||||
|
"Password:": "비밀번호:",
|
||||||
|
"Send Password": "비밀번호 전송",
|
||||||
|
"Cancel": "취소"
|
||||||
|
}
|
|
@ -1,13 +1,17 @@
|
||||||
{
|
{
|
||||||
"Connecting...": "Verbinden...",
|
"Connecting...": "Verbinden...",
|
||||||
|
"Disconnecting...": "Verbinding verbreken...",
|
||||||
|
"Reconnecting...": "Opnieuw verbinding maken...",
|
||||||
|
"Internal error": "Interne fout",
|
||||||
|
"Must set host": "Host moeten worden ingesteld",
|
||||||
"Connected (encrypted) to ": "Verbonden (versleuteld) met ",
|
"Connected (encrypted) to ": "Verbonden (versleuteld) met ",
|
||||||
"Connected (unencrypted) to ": "Verbonden (onversleuteld) met ",
|
"Connected (unencrypted) to ": "Verbonden (onversleuteld) met ",
|
||||||
"Disconnecting...": "Verbinding verbreken...",
|
"Something went wrong, connection is closed": "Er iets fout gelopen, verbinding werd verbroken",
|
||||||
|
"Failed to connect to server": "Verbinding maken met server is mislukt",
|
||||||
"Disconnected": "Verbinding verbroken",
|
"Disconnected": "Verbinding verbroken",
|
||||||
"Must set host": "Host moeten worden ingesteld",
|
"New connection has been rejected with reason: ": "Nieuwe verbinding is geweigerd omwille van de volgende reden: ",
|
||||||
"Reconnecting...": "Opnieuw verbinding maken...",
|
"New connection has been rejected": "Nieuwe verbinding is geweigerd",
|
||||||
"Password is required": "Wachtwoord is vereist",
|
"Password is required": "Wachtwoord is vereist",
|
||||||
"Disconnect timeout": "Timeout tijdens verbreken van verbinding",
|
|
||||||
"noVNC encountered an error:": "noVNC heeft een fout bemerkt:",
|
"noVNC encountered an error:": "noVNC heeft een fout bemerkt:",
|
||||||
"Hide/Show the control bar": "Verberg/Toon de bedieningsbalk",
|
"Hide/Show the control bar": "Verberg/Toon de bedieningsbalk",
|
||||||
"Move/Drag Viewport": "Verplaats/Versleep Kijkvenster",
|
"Move/Drag Viewport": "Verplaats/Versleep Kijkvenster",
|
||||||
|
@ -22,9 +26,11 @@
|
||||||
"Extra keys": "Extra toetsen",
|
"Extra keys": "Extra toetsen",
|
||||||
"Show Extra Keys": "Toon Extra Toetsen",
|
"Show Extra Keys": "Toon Extra Toetsen",
|
||||||
"Ctrl": "Ctrl",
|
"Ctrl": "Ctrl",
|
||||||
"Toggle Ctrl": "Ctrl aan/uitzetten",
|
"Toggle Ctrl": "Ctrl omschakelen",
|
||||||
"Alt": "Alt",
|
"Alt": "Alt",
|
||||||
"Toggle Alt": "Alt aan/uitzetten",
|
"Toggle Alt": "Alt omschakelen",
|
||||||
|
"Toggle Windows": "Windows omschakelen",
|
||||||
|
"Windows": "Windows",
|
||||||
"Send Tab": "Tab Sturen",
|
"Send Tab": "Tab Sturen",
|
||||||
"Tab": "Tab",
|
"Tab": "Tab",
|
||||||
"Esc": "Esc",
|
"Esc": "Esc",
|
||||||
|
@ -47,10 +53,8 @@
|
||||||
"Scaling Mode:": "Schaalmodus:",
|
"Scaling Mode:": "Schaalmodus:",
|
||||||
"None": "Geen",
|
"None": "Geen",
|
||||||
"Local Scaling": "Lokaal Schalen",
|
"Local Scaling": "Lokaal Schalen",
|
||||||
"Local Downscaling": "Lokaal Neerschalen",
|
|
||||||
"Remote Resizing": "Op Afstand Formaat Wijzigen",
|
"Remote Resizing": "Op Afstand Formaat Wijzigen",
|
||||||
"Advanced": "Geavanceerd",
|
"Advanced": "Geavanceerd",
|
||||||
"Local Cursor": "Lokale Cursor",
|
|
||||||
"Repeater ID:": "Repeater ID:",
|
"Repeater ID:": "Repeater ID:",
|
||||||
"WebSocket": "WebSocket",
|
"WebSocket": "WebSocket",
|
||||||
"Encrypt": "Versleutelen",
|
"Encrypt": "Versleutelen",
|
||||||
|
@ -59,10 +63,11 @@
|
||||||
"Path:": "Pad:",
|
"Path:": "Pad:",
|
||||||
"Automatic Reconnect": "Automatisch Opnieuw Verbinden",
|
"Automatic Reconnect": "Automatisch Opnieuw Verbinden",
|
||||||
"Reconnect Delay (ms):": "Vertraging voor Opnieuw Verbinden (ms):",
|
"Reconnect Delay (ms):": "Vertraging voor Opnieuw Verbinden (ms):",
|
||||||
|
"Show Dot when No Cursor": "Geef stip weer indien geen cursor",
|
||||||
"Logging:": "Logmeldingen:",
|
"Logging:": "Logmeldingen:",
|
||||||
"Disconnect": "Verbinding verbreken",
|
"Disconnect": "Verbinding verbreken",
|
||||||
"Connect": "Verbinden",
|
"Connect": "Verbinden",
|
||||||
"Password:": "Wachtwoord:",
|
"Password:": "Wachtwoord:",
|
||||||
"Cancel": "Annuleren",
|
"Send Password": "Verzend Wachtwoord:",
|
||||||
"Canvas not supported.": "Canvas wordt niet ondersteund."
|
"Cancel": "Annuleren"
|
||||||
}
|
}
|
73
static/js/novnc/app/locale/ru.json
Executable file
73
static/js/novnc/app/locale/ru.json
Executable file
|
@ -0,0 +1,73 @@
|
||||||
|
{
|
||||||
|
"Connecting...": "Подключение...",
|
||||||
|
"Disconnecting...": "Отключение...",
|
||||||
|
"Reconnecting...": "Переподключение...",
|
||||||
|
"Internal error": "Внутренняя ошибка",
|
||||||
|
"Must set host": "Задайте имя сервера или IP",
|
||||||
|
"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": "Вкладка",
|
||||||
|
"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": "Выход"
|
||||||
|
}
|
|
@ -1,13 +1,17 @@
|
||||||
{
|
{
|
||||||
"Connecting...": "Ansluter...",
|
"Connecting...": "Ansluter...",
|
||||||
|
"Disconnecting...": "Kopplar ner...",
|
||||||
|
"Reconnecting...": "Återansluter...",
|
||||||
|
"Internal error": "Internt fel",
|
||||||
|
"Must set host": "Du måste specifiera en värd",
|
||||||
"Connected (encrypted) to ": "Ansluten (krypterat) till ",
|
"Connected (encrypted) to ": "Ansluten (krypterat) till ",
|
||||||
"Connected (unencrypted) to ": "Ansluten (okrypterat) till ",
|
"Connected (unencrypted) to ": "Ansluten (okrypterat) till ",
|
||||||
"Disconnecting...": "Kopplar ner...",
|
"Something went wrong, connection is closed": "Något gick fel, anslutningen avslutades",
|
||||||
|
"Failed to connect to server": "Misslyckades att ansluta till servern",
|
||||||
"Disconnected": "Frånkopplad",
|
"Disconnected": "Frånkopplad",
|
||||||
"Must set host": "Du måste specifiera en värd",
|
"New connection has been rejected with reason: ": "Ny anslutning har blivit nekad med följande skäl: ",
|
||||||
"Reconnecting...": "Återansluter...",
|
"New connection has been rejected": "Ny anslutning har blivit nekad",
|
||||||
"Password is required": "Lösenord krävs",
|
"Password is required": "Lösenord krävs",
|
||||||
"Disconnect timeout": "Det tog för lång tid att koppla ner",
|
|
||||||
"noVNC encountered an error:": "noVNC stötte på ett problem:",
|
"noVNC encountered an error:": "noVNC stötte på ett problem:",
|
||||||
"Hide/Show the control bar": "Göm/Visa kontrollbaren",
|
"Hide/Show the control bar": "Göm/Visa kontrollbaren",
|
||||||
"Move/Drag Viewport": "Flytta/Dra Vyn",
|
"Move/Drag Viewport": "Flytta/Dra Vyn",
|
||||||
|
@ -25,6 +29,8 @@
|
||||||
"Toggle Ctrl": "Växla Ctrl",
|
"Toggle Ctrl": "Växla Ctrl",
|
||||||
"Alt": "Alt",
|
"Alt": "Alt",
|
||||||
"Toggle Alt": "Växla Alt",
|
"Toggle Alt": "Växla Alt",
|
||||||
|
"Toggle Windows": "Växla Windows",
|
||||||
|
"Windows": "Windows",
|
||||||
"Send Tab": "Skicka Tab",
|
"Send Tab": "Skicka Tab",
|
||||||
"Tab": "Tab",
|
"Tab": "Tab",
|
||||||
"Esc": "Esc",
|
"Esc": "Esc",
|
||||||
|
@ -47,10 +53,8 @@
|
||||||
"Scaling Mode:": "Skalningsläge:",
|
"Scaling Mode:": "Skalningsläge:",
|
||||||
"None": "Ingen",
|
"None": "Ingen",
|
||||||
"Local Scaling": "Lokal Skalning",
|
"Local Scaling": "Lokal Skalning",
|
||||||
"Local Downscaling": "Lokal Nedskalning",
|
|
||||||
"Remote Resizing": "Ändra Storlek",
|
"Remote Resizing": "Ändra Storlek",
|
||||||
"Advanced": "Avancerat",
|
"Advanced": "Avancerat",
|
||||||
"Local Cursor": "Lokal Muspekare",
|
|
||||||
"Repeater ID:": "Repeater-ID:",
|
"Repeater ID:": "Repeater-ID:",
|
||||||
"WebSocket": "WebSocket",
|
"WebSocket": "WebSocket",
|
||||||
"Encrypt": "Kryptera",
|
"Encrypt": "Kryptera",
|
||||||
|
@ -59,10 +63,11 @@
|
||||||
"Path:": "Sökväg:",
|
"Path:": "Sökväg:",
|
||||||
"Automatic Reconnect": "Automatisk Återanslutning",
|
"Automatic Reconnect": "Automatisk Återanslutning",
|
||||||
"Reconnect Delay (ms):": "Fördröjning (ms):",
|
"Reconnect Delay (ms):": "Fördröjning (ms):",
|
||||||
|
"Show Dot when No Cursor": "Visa prick när ingen muspekare finns",
|
||||||
"Logging:": "Loggning:",
|
"Logging:": "Loggning:",
|
||||||
"Disconnect": "Koppla från",
|
"Disconnect": "Koppla från",
|
||||||
"Connect": "Anslut",
|
"Connect": "Anslut",
|
||||||
"Password:": "Lösenord:",
|
"Password:": "Lösenord:",
|
||||||
"Cancel": "Avbryt",
|
"Send Password": "Skicka lösenord",
|
||||||
"Canvas not supported.": "Canvas stöds ej"
|
"Cancel": "Avbryt"
|
||||||
}
|
}
|
69
static/js/novnc/app/locale/zh_CN.json
Executable file
69
static/js/novnc/app/locale/zh_CN.json
Executable file
|
@ -0,0 +1,69 @@
|
||||||
|
{
|
||||||
|
"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",
|
||||||
|
"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):": "重新链接间隔 (ms):",
|
||||||
|
"Logging:": "日志级别:",
|
||||||
|
"Disconnect": "终端链接",
|
||||||
|
"Connect": "链接",
|
||||||
|
"Password:": "密码:",
|
||||||
|
"Cancel": "取消"
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2018 The noVNC Authors
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
|
@ -10,36 +10,35 @@
|
||||||
* Localization Utilities
|
* Localization Utilities
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function Localizer() {
|
export class Localizer {
|
||||||
// Currently configured language
|
constructor() {
|
||||||
this.language = 'en';
|
// Currently configured language
|
||||||
|
this.language = 'en';
|
||||||
|
|
||||||
// Current dictionary of translations
|
// Current dictionary of translations
|
||||||
this.dictionary = undefined;
|
this.dictionary = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
Localizer.prototype = {
|
|
||||||
// Configure suitable language based on user preferences
|
// Configure suitable language based on user preferences
|
||||||
setup: function (supportedLanguages) {
|
setup(supportedLanguages) {
|
||||||
var userLanguages;
|
|
||||||
|
|
||||||
this.language = 'en'; // Default: US English
|
this.language = 'en'; // Default: US English
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Navigator.languages only available in Chrome (32+) and FireFox (32+)
|
* Navigator.languages only available in Chrome (32+) and FireFox (32+)
|
||||||
* Fall back to navigator.language for other browsers
|
* Fall back to navigator.language for other browsers
|
||||||
*/
|
*/
|
||||||
|
let userLanguages;
|
||||||
if (typeof window.navigator.languages == 'object') {
|
if (typeof window.navigator.languages == 'object') {
|
||||||
userLanguages = window.navigator.languages;
|
userLanguages = window.navigator.languages;
|
||||||
} else {
|
} else {
|
||||||
userLanguages = [navigator.language || navigator.userLanguage];
|
userLanguages = [navigator.language || navigator.userLanguage];
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0;i < userLanguages.length;i++) {
|
for (let i = 0;i < userLanguages.length;i++) {
|
||||||
var userLang = userLanguages[i];
|
const userLang = userLanguages[i]
|
||||||
userLang = userLang.toLowerCase();
|
.toLowerCase()
|
||||||
userLang = userLang.replace("_", "-");
|
.replace("_", "-")
|
||||||
userLang = userLang.split("-");
|
.split("-");
|
||||||
|
|
||||||
// Built-in default?
|
// Built-in default?
|
||||||
if ((userLang[0] === 'en') &&
|
if ((userLang[0] === 'en') &&
|
||||||
|
@ -48,66 +47,69 @@ Localizer.prototype = {
|
||||||
}
|
}
|
||||||
|
|
||||||
// First pass: perfect match
|
// First pass: perfect match
|
||||||
for (var j = 0;j < supportedLanguages.length;j++) {
|
for (let j = 0; j < supportedLanguages.length; j++) {
|
||||||
var supLang = supportedLanguages[j];
|
const supLang = supportedLanguages[j]
|
||||||
supLang = supLang.toLowerCase();
|
.toLowerCase()
|
||||||
supLang = supLang.replace("_", "-");
|
.replace("_", "-")
|
||||||
supLang = supLang.split("-");
|
.split("-");
|
||||||
|
|
||||||
if (userLang[0] !== supLang[0])
|
if (userLang[0] !== supLang[0]) {
|
||||||
continue;
|
continue;
|
||||||
if (userLang[1] !== supLang[1])
|
}
|
||||||
|
if (userLang[1] !== supLang[1]) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
this.language = supportedLanguages[j];
|
this.language = supportedLanguages[j];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second pass: fallback
|
// Second pass: fallback
|
||||||
for (var j = 0;j < supportedLanguages.length;j++) {
|
for (let j = 0;j < supportedLanguages.length;j++) {
|
||||||
supLang = supportedLanguages[j];
|
const supLang = supportedLanguages[j]
|
||||||
supLang = supLang.toLowerCase();
|
.toLowerCase()
|
||||||
supLang = supLang.replace("_", "-");
|
.replace("_", "-")
|
||||||
supLang = supLang.split("-");
|
.split("-");
|
||||||
|
|
||||||
if (userLang[0] !== supLang[0])
|
if (userLang[0] !== supLang[0]) {
|
||||||
continue;
|
continue;
|
||||||
if (supLang[1] !== undefined)
|
}
|
||||||
|
if (supLang[1] !== undefined) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
this.language = supportedLanguages[j];
|
this.language = supportedLanguages[j];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// Retrieve localised text
|
// Retrieve localised text
|
||||||
get: function (id) {
|
get(id) {
|
||||||
if (typeof this.dictionary !== 'undefined' && this.dictionary[id]) {
|
if (typeof this.dictionary !== 'undefined' && this.dictionary[id]) {
|
||||||
return this.dictionary[id];
|
return this.dictionary[id];
|
||||||
} else {
|
} else {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// Traverses the DOM and translates relevant fields
|
// Traverses the DOM and translates relevant fields
|
||||||
// See https://html.spec.whatwg.org/multipage/dom.html#attr-translate
|
// See https://html.spec.whatwg.org/multipage/dom.html#attr-translate
|
||||||
translateDOM: function () {
|
translateDOM() {
|
||||||
var self = this;
|
const self = this;
|
||||||
|
|
||||||
function process(elem, enabled) {
|
function process(elem, enabled) {
|
||||||
function isAnyOf(searchElement, items) {
|
function isAnyOf(searchElement, items) {
|
||||||
return items.indexOf(searchElement) !== -1;
|
return items.indexOf(searchElement) !== -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function translateAttribute(elem, attr) {
|
function translateAttribute(elem, attr) {
|
||||||
var str = elem.getAttribute(attr);
|
const str = self.get(elem.getAttribute(attr));
|
||||||
str = self.get(str);
|
|
||||||
elem.setAttribute(attr, str);
|
elem.setAttribute(attr, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
function translateTextNode(node) {
|
function translateTextNode(node) {
|
||||||
var str = node.data.trim();
|
const str = self.get(node.data.trim());
|
||||||
str = self.get(str);
|
|
||||||
node.data = str;
|
node.data = str;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,7 +136,7 @@ Localizer.prototype = {
|
||||||
}
|
}
|
||||||
if (elem.hasAttribute("label") &&
|
if (elem.hasAttribute("label") &&
|
||||||
isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP",
|
isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP",
|
||||||
"OPTION", "TRACK"])) {
|
"OPTION", "TRACK"])) {
|
||||||
translateAttribute(elem, "label");
|
translateAttribute(elem, "label");
|
||||||
}
|
}
|
||||||
// FIXME: Should update "lang"
|
// FIXME: Should update "lang"
|
||||||
|
@ -152,8 +154,8 @@ Localizer.prototype = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0;i < elem.childNodes.length;i++) {
|
for (let i = 0; i < elem.childNodes.length; i++) {
|
||||||
var node = elem.childNodes[i];
|
const node = elem.childNodes[i];
|
||||||
if (node.nodeType === node.ELEMENT_NODE) {
|
if (node.nodeType === node.ELEMENT_NODE) {
|
||||||
process(node, enabled);
|
process(node, enabled);
|
||||||
} else if (node.nodeType === node.TEXT_NODE && enabled) {
|
} else if (node.nodeType === node.TEXT_NODE && enabled) {
|
||||||
|
@ -163,8 +165,8 @@ Localizer.prototype = {
|
||||||
}
|
}
|
||||||
|
|
||||||
process(document.body, true);
|
process(document.body, true);
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export var l10n = new Localizer();
|
export const l10n = new Localizer();
|
||||||
export default l10n.get.bind(l10n);
|
export default l10n.get.bind(l10n);
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC base CSS
|
* noVNC base CSS
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2018 The noVNC Authors
|
||||||
* Copyright (C) 2016 Samuel Mannehed for Cendio AB
|
|
||||||
* Copyright (C) 2016 Pierre Ossman for Cendio AB
|
|
||||||
* noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
|
* noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
|
||||||
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
|
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
|
||||||
*/
|
*/
|
||||||
|
@ -30,6 +28,7 @@ body {
|
||||||
background-repeat:no-repeat;
|
background-repeat:no-repeat;
|
||||||
background-position:right bottom;
|
background-position:right bottom;
|
||||||
height:100%;
|
height:100%;
|
||||||
|
display: flex;
|
||||||
touch-action: none;
|
touch-action: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2018 The noVNC Authors
|
||||||
* Copyright (C) 2013 NTT corp.
|
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
|
@ -10,61 +9,65 @@
|
||||||
import { init_logging as main_init_logging } from '../core/util/logging.js';
|
import { init_logging as main_init_logging } from '../core/util/logging.js';
|
||||||
|
|
||||||
// init log level reading the logging HTTP param
|
// init log level reading the logging HTTP param
|
||||||
export function init_logging (level) {
|
export function init_logging(level) {
|
||||||
"use strict";
|
"use strict";
|
||||||
if (typeof level !== "undefined") {
|
if (typeof level !== "undefined") {
|
||||||
main_init_logging(level);
|
main_init_logging(level);
|
||||||
} else {
|
} else {
|
||||||
var param = document.location.href.match(/logging=([A-Za-z0-9\._\-]*)/);
|
const param = document.location.href.match(/logging=([A-Za-z0-9._-]*)/);
|
||||||
main_init_logging(param || undefined);
|
main_init_logging(param || undefined);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// Read a query string variable
|
// Read a query string variable
|
||||||
export function getQueryVar (name, defVal) {
|
export function getQueryVar(name, defVal) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
|
const re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
|
||||||
match = document.location.href.match(re);
|
match = document.location.href.match(re);
|
||||||
if (typeof defVal === 'undefined') { defVal = null; }
|
if (typeof defVal === 'undefined') { defVal = null; }
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
return decodeURIComponent(match[1]);
|
return decodeURIComponent(match[1]);
|
||||||
} else {
|
|
||||||
return defVal;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
return defVal;
|
||||||
|
}
|
||||||
|
|
||||||
// Read a hash fragment variable
|
// Read a hash fragment variable
|
||||||
export function getHashVar (name, defVal) {
|
export function getHashVar(name, defVal) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var re = new RegExp('.*[&#]' + name + '=([^&]*)'),
|
const re = new RegExp('.*[&#]' + name + '=([^&]*)'),
|
||||||
match = document.location.hash.match(re);
|
match = document.location.hash.match(re);
|
||||||
if (typeof defVal === 'undefined') { defVal = null; }
|
if (typeof defVal === 'undefined') { defVal = null; }
|
||||||
|
|
||||||
if (match) {
|
if (match) {
|
||||||
return decodeURIComponent(match[1]);
|
return decodeURIComponent(match[1]);
|
||||||
} else {
|
|
||||||
return defVal;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
return defVal;
|
||||||
|
}
|
||||||
|
|
||||||
// Read a variable from the fragment or the query string
|
// Read a variable from the fragment or the query string
|
||||||
// Fragment takes precedence
|
// Fragment takes precedence
|
||||||
export function getConfigVar (name, defVal) {
|
export function getConfigVar(name, defVal) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var val = getHashVar(name);
|
const val = getHashVar(name);
|
||||||
|
|
||||||
if (val === null) {
|
if (val === null) {
|
||||||
val = getQueryVar(name, defVal);
|
return getQueryVar(name, defVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
};
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
|
* Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// No days means only for this browser session
|
// No days means only for this browser session
|
||||||
export function createCookie (name, value, days) {
|
export function createCookie(name, value, days) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var date, expires;
|
let date, expires;
|
||||||
if (days) {
|
if (days) {
|
||||||
date = new Date();
|
date = new Date();
|
||||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||||
|
@ -73,115 +76,123 @@ export function createCookie (name, value, days) {
|
||||||
expires = "";
|
expires = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
var secure;
|
let secure;
|
||||||
if (document.location.protocol === "https:") {
|
if (document.location.protocol === "https:") {
|
||||||
secure = "; secure";
|
secure = "; secure";
|
||||||
} else {
|
} else {
|
||||||
secure = "";
|
secure = "";
|
||||||
}
|
}
|
||||||
document.cookie = name + "=" + value + expires + "; path=/" + secure;
|
document.cookie = name + "=" + value + expires + "; path=/" + secure;
|
||||||
};
|
}
|
||||||
|
|
||||||
export function readCookie (name, defaultValue) {
|
export function readCookie(name, defaultValue) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var nameEQ = name + "=",
|
const nameEQ = name + "=";
|
||||||
ca = document.cookie.split(';');
|
const ca = document.cookie.split(';');
|
||||||
|
|
||||||
for (var i = 0; i < ca.length; i += 1) {
|
for (let i = 0; i < ca.length; i += 1) {
|
||||||
var c = ca[i];
|
let c = ca[i];
|
||||||
while (c.charAt(0) === ' ') { c = c.substring(1, c.length); }
|
while (c.charAt(0) === ' ') {
|
||||||
if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length, c.length); }
|
c = c.substring(1, c.length);
|
||||||
|
}
|
||||||
|
if (c.indexOf(nameEQ) === 0) {
|
||||||
|
return c.substring(nameEQ.length, c.length);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return (typeof defaultValue !== 'undefined') ? defaultValue : null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function eraseCookie (name) {
|
return (typeof defaultValue !== 'undefined') ? defaultValue : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function eraseCookie(name) {
|
||||||
"use strict";
|
"use strict";
|
||||||
createCookie(name, "", -1);
|
createCookie(name, "", -1);
|
||||||
};
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Setting handling.
|
* Setting handling.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var settings = {};
|
let settings = {};
|
||||||
|
|
||||||
export function initSettings (callback /*, ...callbackArgs */) {
|
export function initSettings() {
|
||||||
"use strict";
|
if (!window.chrome || !window.chrome.storage) {
|
||||||
var callbackArgs = Array.prototype.slice.call(arguments, 1);
|
settings = {};
|
||||||
if (window.chrome && window.chrome.storage) {
|
return Promise.resolve();
|
||||||
window.chrome.storage.sync.get(function (cfg) {
|
|
||||||
settings = cfg;
|
|
||||||
if (callback) {
|
|
||||||
callback.apply(this, callbackArgs);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// No-op
|
|
||||||
if (callback) {
|
|
||||||
callback.apply(this, callbackArgs);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
return new Promise(resolve => window.chrome.storage.sync.get(resolve))
|
||||||
|
.then((cfg) => { settings = cfg; });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the settings cache, but do not write to permanent storage
|
||||||
|
export function setSetting(name, value) {
|
||||||
|
settings[name] = value;
|
||||||
|
}
|
||||||
|
|
||||||
// No days means only for this browser session
|
// No days means only for this browser session
|
||||||
export function writeSetting (name, value) {
|
export function writeSetting(name, value) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
if (settings[name] === value) return;
|
||||||
|
settings[name] = value;
|
||||||
if (window.chrome && window.chrome.storage) {
|
if (window.chrome && window.chrome.storage) {
|
||||||
if (settings[name] !== value) {
|
window.chrome.storage.sync.set(settings);
|
||||||
settings[name] = value;
|
|
||||||
window.chrome.storage.sync.set(settings);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
localStorage.setItem(name, value);
|
localStorage.setItem(name, value);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export function readSetting (name, defaultValue) {
|
export function readSetting(name, defaultValue) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var value;
|
let value;
|
||||||
if (window.chrome && window.chrome.storage) {
|
if ((name in settings) || (window.chrome && window.chrome.storage)) {
|
||||||
value = settings[name];
|
value = settings[name];
|
||||||
} else {
|
} else {
|
||||||
value = localStorage.getItem(name);
|
value = localStorage.getItem(name);
|
||||||
|
settings[name] = value;
|
||||||
}
|
}
|
||||||
if (typeof value === "undefined") {
|
if (typeof value === "undefined") {
|
||||||
value = null;
|
value = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value === null && typeof defaultValue !== "undefined") {
|
if (value === null && typeof defaultValue !== "undefined") {
|
||||||
return defaultValue;
|
return defaultValue;
|
||||||
} else {
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
export function eraseSetting (name) {
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function eraseSetting(name) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
// Deleting here means that next time the setting is read when using local
|
||||||
|
// storage, it will be pulled from local storage again.
|
||||||
|
// If the setting in local storage is changed (e.g. in another tab)
|
||||||
|
// between this delete and the next read, it could lead to an unexpected
|
||||||
|
// value change.
|
||||||
|
delete settings[name];
|
||||||
if (window.chrome && window.chrome.storage) {
|
if (window.chrome && window.chrome.storage) {
|
||||||
window.chrome.storage.sync.remove(name);
|
window.chrome.storage.sync.remove(name);
|
||||||
delete settings[name];
|
|
||||||
} else {
|
} else {
|
||||||
localStorage.removeItem(name);
|
localStorage.removeItem(name);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export function injectParamIfMissing (path, param, value) {
|
export function injectParamIfMissing(path, param, value) {
|
||||||
// force pretend that we're dealing with a relative path
|
// force pretend that we're dealing with a relative path
|
||||||
// (assume that we wanted an extra if we pass one in)
|
// (assume that we wanted an extra if we pass one in)
|
||||||
path = "/" + path;
|
path = "/" + path;
|
||||||
|
|
||||||
var elem = document.createElement('a');
|
const elem = document.createElement('a');
|
||||||
elem.href = path;
|
elem.href = path;
|
||||||
|
|
||||||
var param_eq = encodeURIComponent(param) + "=";
|
const param_eq = encodeURIComponent(param) + "=";
|
||||||
var query;
|
let query;
|
||||||
if (elem.search) {
|
if (elem.search) {
|
||||||
query = elem.search.slice(1).split('&');
|
query = elem.search.slice(1).split('&');
|
||||||
} else {
|
} else {
|
||||||
query = [];
|
query = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!query.some(function (v) { return v.startsWith(param_eq); })) {
|
if (!query.some(v => v.startsWith(param_eq))) {
|
||||||
query.push(param_eq + encodeURIComponent(value));
|
query.push(param_eq + encodeURIComponent(value));
|
||||||
elem.search = "?" + query.join("&");
|
elem.search = "?" + query.join("&");
|
||||||
}
|
}
|
||||||
|
@ -190,41 +201,39 @@ export function injectParamIfMissing (path, param, value) {
|
||||||
// in the elem.pathname string. Handle that case gracefully.
|
// in the elem.pathname string. Handle that case gracefully.
|
||||||
if (elem.pathname.charAt(0) == "/") {
|
if (elem.pathname.charAt(0) == "/") {
|
||||||
return elem.pathname.slice(1) + elem.search + elem.hash;
|
return elem.pathname.slice(1) + elem.search + elem.hash;
|
||||||
} else {
|
|
||||||
return elem.pathname + elem.search + elem.hash;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
return elem.pathname + elem.search + elem.hash;
|
||||||
|
}
|
||||||
|
|
||||||
// sadly, we can't use the Fetch API until we decide to drop
|
// sadly, we can't use the Fetch API until we decide to drop
|
||||||
// IE11 support or polyfill promises and fetch in IE11.
|
// IE11 support or polyfill promises and fetch in IE11.
|
||||||
// resolve will receive an object on success, while reject
|
// resolve will receive an object on success, while reject
|
||||||
// will receive either an event or an error on failure.
|
// will receive either an event or an error on failure.
|
||||||
export function fetchJSON(path, resolve, reject) {
|
export function fetchJSON(path) {
|
||||||
// NB: IE11 doesn't support JSON as a responseType
|
return new Promise((resolve, reject) => {
|
||||||
var req = new XMLHttpRequest();
|
// NB: IE11 doesn't support JSON as a responseType
|
||||||
req.open('GET', path);
|
const req = new XMLHttpRequest();
|
||||||
|
req.open('GET', path);
|
||||||
|
|
||||||
req.onload = function () {
|
req.onload = () => {
|
||||||
if (req.status === 200) {
|
if (req.status === 200) {
|
||||||
try {
|
let resObj;
|
||||||
var resObj = JSON.parse(req.responseText);
|
try {
|
||||||
} catch (err) {
|
resObj = JSON.parse(req.responseText);
|
||||||
reject(err);
|
} catch (err) {
|
||||||
return;
|
reject(err);
|
||||||
|
}
|
||||||
|
resolve(resObj);
|
||||||
|
} else {
|
||||||
|
reject(new Error("XHR got non-200 status while trying to load '" + path + "': " + req.status));
|
||||||
}
|
}
|
||||||
resolve(resObj);
|
};
|
||||||
} else {
|
|
||||||
reject(new Error("XHR got non-200 status while trying to load '" + path + "': " + req.status));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
req.onerror = function (evt) {
|
req.onerror = evt => reject(new Error("XHR encountered an error while trying to load '" + path + "': " + evt.message));
|
||||||
reject(new Error("XHR encountered an error while trying to load '" + path + "': " + evt.message));
|
|
||||||
};
|
|
||||||
|
|
||||||
req.ontimeout = function (evt) {
|
req.ontimeout = evt => reject(new Error("XHR timed out while trying to load '" + path + "'"));
|
||||||
reject(new Error("XHR timed out while trying to load '" + path + "'"));
|
|
||||||
};
|
|
||||||
|
|
||||||
req.send();
|
req.send();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,45 +8,43 @@ import * as Log from './util/logging.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
/* Convert data (an array of integers) to a Base64 string. */
|
/* Convert data (an array of integers) to a Base64 string. */
|
||||||
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
|
toBase64Table: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
|
||||||
base64Pad : '=',
|
base64Pad: '=',
|
||||||
|
|
||||||
encode: function (data) {
|
encode(data) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var result = '';
|
let result = '';
|
||||||
var toBase64Table = this.toBase64Table;
|
const length = data.length;
|
||||||
var length = data.length;
|
const lengthpad = (length % 3);
|
||||||
var lengthpad = (length % 3);
|
|
||||||
// Convert every three bytes to 4 ascii characters.
|
// Convert every three bytes to 4 ascii characters.
|
||||||
|
|
||||||
for (var i = 0; i < (length - 2); i += 3) {
|
for (let i = 0; i < (length - 2); i += 3) {
|
||||||
result += toBase64Table[data[i] >> 2];
|
result += this.toBase64Table[data[i] >> 2];
|
||||||
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
|
result += this.toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
|
||||||
result += toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
|
result += this.toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
|
||||||
result += toBase64Table[data[i + 2] & 0x3f];
|
result += this.toBase64Table[data[i + 2] & 0x3f];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
|
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
|
||||||
var j = 0;
|
const j = length - lengthpad;
|
||||||
if (lengthpad === 2) {
|
if (lengthpad === 2) {
|
||||||
j = length - lengthpad;
|
result += this.toBase64Table[data[j] >> 2];
|
||||||
result += toBase64Table[data[j] >> 2];
|
result += this.toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)];
|
||||||
result += toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)];
|
result += this.toBase64Table[(data[j + 1] & 0x0f) << 2];
|
||||||
result += toBase64Table[(data[j + 1] & 0x0f) << 2];
|
result += this.toBase64Table[64];
|
||||||
result += toBase64Table[64];
|
|
||||||
} else if (lengthpad === 1) {
|
} else if (lengthpad === 1) {
|
||||||
j = length - lengthpad;
|
result += this.toBase64Table[data[j] >> 2];
|
||||||
result += toBase64Table[data[j] >> 2];
|
result += this.toBase64Table[(data[j] & 0x03) << 4];
|
||||||
result += toBase64Table[(data[j] & 0x03) << 4];
|
result += this.toBase64Table[64];
|
||||||
result += toBase64Table[64];
|
result += this.toBase64Table[64];
|
||||||
result += toBase64Table[64];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
/* Convert Base64 data to a string */
|
/* Convert Base64 data to a string */
|
||||||
toBinaryTable : [
|
/* eslint-disable comma-spacing */
|
||||||
|
toBinaryTable: [
|
||||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
|
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
|
||||||
|
@ -56,27 +54,23 @@ export default {
|
||||||
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
|
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
|
||||||
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
|
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
|
||||||
],
|
],
|
||||||
|
/* eslint-enable comma-spacing */
|
||||||
|
|
||||||
decode: function (data, offset) {
|
decode(data, offset = 0) {
|
||||||
"use strict";
|
let data_length = data.indexOf('=') - offset;
|
||||||
offset = typeof(offset) !== 'undefined' ? offset : 0;
|
|
||||||
var toBinaryTable = this.toBinaryTable;
|
|
||||||
var base64Pad = this.base64Pad;
|
|
||||||
var result, result_length;
|
|
||||||
var leftbits = 0; // number of bits decoded, but yet to be appended
|
|
||||||
var leftdata = 0; // bits decoded, but yet to be appended
|
|
||||||
var data_length = data.indexOf('=') - offset;
|
|
||||||
|
|
||||||
if (data_length < 0) { data_length = data.length - offset; }
|
if (data_length < 0) { data_length = data.length - offset; }
|
||||||
|
|
||||||
/* Every four characters is 3 resulting numbers */
|
/* Every four characters is 3 resulting numbers */
|
||||||
result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5);
|
const result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5);
|
||||||
result = new Array(result_length);
|
const result = new Array(result_length);
|
||||||
|
|
||||||
// Convert one by one.
|
// Convert one by one.
|
||||||
for (var idx = 0, i = offset; i < data.length; i++) {
|
|
||||||
var c = toBinaryTable[data.charCodeAt(i) & 0x7f];
|
let leftbits = 0; // number of bits decoded, but yet to be appended
|
||||||
var padding = (data.charAt(i) === base64Pad);
|
let leftdata = 0; // bits decoded, but yet to be appended
|
||||||
|
for (let idx = 0, i = offset; i < data.length; i++) {
|
||||||
|
const c = this.toBinaryTable[data.charCodeAt(i) & 0x7f];
|
||||||
|
const padding = (data.charAt(i) === this.base64Pad);
|
||||||
// Skip illegal characters and whitespace
|
// Skip illegal characters and whitespace
|
||||||
if (c === -1) {
|
if (c === -1) {
|
||||||
Log.Error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
|
Log.Error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
|
||||||
|
@ -100,7 +94,7 @@ export default {
|
||||||
|
|
||||||
// If there are any bits left, the base64 string was corrupted
|
// If there are any bits left, the base64 string was corrupted
|
||||||
if (leftbits) {
|
if (leftbits) {
|
||||||
err = new Error('Corrupted base64 string');
|
const err = new Error('Corrupted base64 string');
|
||||||
err.name = 'Base64-Error';
|
err.name = 'Base64-Error';
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
24
static/js/novnc/core/decoders/copyrect.js
Executable file
24
static/js/novnc/core/decoders/copyrect.js
Executable file
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
|
*
|
||||||
|
* See README.md for usage and integration instructions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default class CopyRectDecoder {
|
||||||
|
decodeRect(x, y, width, height, sock, display, depth) {
|
||||||
|
if (sock.rQwait("COPYRECT", 4)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let deltaX = sock.rQshift16();
|
||||||
|
let deltaY = sock.rQshift16();
|
||||||
|
display.copyImage(deltaX, deltaY, x, y, width, height);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
139
static/js/novnc/core/decoders/hextile.js
Executable file
139
static/js/novnc/core/decoders/hextile.js
Executable file
|
@ -0,0 +1,139 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
|
*
|
||||||
|
* See README.md for usage and integration instructions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as Log from '../util/logging.js';
|
||||||
|
|
||||||
|
export default class HextileDecoder {
|
||||||
|
constructor() {
|
||||||
|
this._tiles = 0;
|
||||||
|
this._lastsubencoding = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (this._tiles > 0) {
|
||||||
|
let bytes = 1;
|
||||||
|
|
||||||
|
if (sock.rQwait("HEXTILE", bytes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let rQ = sock.rQ;
|
||||||
|
let rQi = sock.rQi;
|
||||||
|
|
||||||
|
let subencoding = rQ[rQi]; // Peek
|
||||||
|
if (subencoding > 30) { // Raw
|
||||||
|
throw new Error("Illegal hextile subencoding (subencoding: " +
|
||||||
|
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 tw = Math.min(16, (x + width) - tx);
|
||||||
|
const th = Math.min(16, (y + height) - ty);
|
||||||
|
|
||||||
|
// Figure out how much we are expecting
|
||||||
|
if (subencoding & 0x01) { // Raw
|
||||||
|
bytes += tw * th * 4;
|
||||||
|
} else {
|
||||||
|
if (subencoding & 0x02) { // Background
|
||||||
|
bytes += 4;
|
||||||
|
}
|
||||||
|
if (subencoding & 0x04) { // Foreground
|
||||||
|
bytes += 4;
|
||||||
|
}
|
||||||
|
if (subencoding & 0x08) { // AnySubrects
|
||||||
|
bytes++; // Since we aren't shifting it off
|
||||||
|
|
||||||
|
if (sock.rQwait("HEXTILE", bytes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let subrects = rQ[rQi + bytes - 1]; // Peek
|
||||||
|
if (subencoding & 0x10) { // SubrectsColoured
|
||||||
|
bytes += subrects * (4 + 2);
|
||||||
|
} else {
|
||||||
|
bytes += subrects * 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sock.rQwait("HEXTILE", bytes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We know the encoding and have a whole tile
|
||||||
|
rQi++;
|
||||||
|
if (subencoding === 0) {
|
||||||
|
if (this._lastsubencoding & 0x01) {
|
||||||
|
// Weird: ignore blanks are RAW
|
||||||
|
Log.Debug(" Ignoring blank after RAW");
|
||||||
|
} else {
|
||||||
|
display.fillRect(tx, ty, tw, th, this._background);
|
||||||
|
}
|
||||||
|
} else if (subencoding & 0x01) { // Raw
|
||||||
|
display.blitImage(tx, ty, tw, th, rQ, rQi);
|
||||||
|
rQi += bytes - 1;
|
||||||
|
} else {
|
||||||
|
if (subencoding & 0x02) { // Background
|
||||||
|
this._background = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
|
||||||
|
rQi += 4;
|
||||||
|
}
|
||||||
|
if (subencoding & 0x04) { // Foreground
|
||||||
|
this._foreground = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
|
||||||
|
rQi += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
display.startTile(tx, ty, tw, th, this._background);
|
||||||
|
if (subencoding & 0x08) { // AnySubrects
|
||||||
|
let subrects = rQ[rQi];
|
||||||
|
rQi++;
|
||||||
|
|
||||||
|
for (let s = 0; s < subrects; s++) {
|
||||||
|
let color;
|
||||||
|
if (subencoding & 0x10) { // SubrectsColoured
|
||||||
|
color = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
|
||||||
|
rQi += 4;
|
||||||
|
} else {
|
||||||
|
color = this._foreground;
|
||||||
|
}
|
||||||
|
const xy = rQ[rQi];
|
||||||
|
rQi++;
|
||||||
|
const sx = (xy >> 4);
|
||||||
|
const sy = (xy & 0x0f);
|
||||||
|
|
||||||
|
const wh = rQ[rQi];
|
||||||
|
rQi++;
|
||||||
|
const sw = (wh >> 4) + 1;
|
||||||
|
const sh = (wh & 0x0f) + 1;
|
||||||
|
|
||||||
|
display.subTile(sx, sy, sw, sh, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
display.finishTile();
|
||||||
|
}
|
||||||
|
sock.rQi = rQi;
|
||||||
|
this._lastsubencoding = subencoding;
|
||||||
|
this._tiles--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
58
static/js/novnc/core/decoders/raw.js
Executable file
58
static/js/novnc/core/decoders/raw.js
Executable file
|
@ -0,0 +1,58 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
|
*
|
||||||
|
* See README.md for usage and integration instructions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default class RawDecoder {
|
||||||
|
constructor() {
|
||||||
|
this._lines = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
decodeRect(x, y, width, height, sock, display, depth) {
|
||||||
|
if (this._lines === 0) {
|
||||||
|
this._lines = height;
|
||||||
|
}
|
||||||
|
|
||||||
|
const pixelSize = depth == 8 ? 1 : 4;
|
||||||
|
const bytesPerLine = width * pixelSize;
|
||||||
|
|
||||||
|
if (sock.rQwait("RAW", bytesPerLine)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const cur_y = y + (height - this._lines);
|
||||||
|
const curr_height = Math.min(this._lines,
|
||||||
|
Math.floor(sock.rQlen / bytesPerLine));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
data = newdata;
|
||||||
|
index = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
display.blitImage(x, cur_y, width, curr_height, data, index);
|
||||||
|
sock.rQskipBytes(curr_height * bytesPerLine);
|
||||||
|
this._lines -= curr_height;
|
||||||
|
if (this._lines > 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
46
static/js/novnc/core/decoders/rre.js
Executable file
46
static/js/novnc/core/decoders/rre.js
Executable file
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
|
*
|
||||||
|
* See README.md for usage and integration instructions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default class RREDecoder {
|
||||||
|
constructor() {
|
||||||
|
this._subrects = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
decodeRect(x, y, width, height, sock, display, depth) {
|
||||||
|
if (this._subrects === 0) {
|
||||||
|
if (sock.rQwait("RRE", 4 + 4)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._subrects = sock.rQshift32();
|
||||||
|
|
||||||
|
let color = sock.rQshiftBytes(4); // Background
|
||||||
|
display.fillRect(x, y, width, height, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (this._subrects > 0) {
|
||||||
|
if (sock.rQwait("RRE", 4 + 8)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let color = sock.rQshiftBytes(4);
|
||||||
|
let sx = sock.rQshift16();
|
||||||
|
let sy = sock.rQshift16();
|
||||||
|
let swidth = sock.rQshift16();
|
||||||
|
let sheight = sock.rQshift16();
|
||||||
|
display.fillRect(x + sx, y + sy, swidth, sheight, color);
|
||||||
|
|
||||||
|
this._subrects--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
319
static/js/novnc/core/decoders/tight.js
Executable file
319
static/js/novnc/core/decoders/tight.js
Executable file
|
@ -0,0 +1,319 @@
|
||||||
|
/*
|
||||||
|
* noVNC: HTML5 VNC client
|
||||||
|
* Copyright (C) 2012 Joel Martin
|
||||||
|
* (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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as Log from '../util/logging.js';
|
||||||
|
import Inflator from "../inflator.js";
|
||||||
|
|
||||||
|
export default class TightDecoder {
|
||||||
|
constructor() {
|
||||||
|
this._ctl = null;
|
||||||
|
this._filter = null;
|
||||||
|
this._numColors = 0;
|
||||||
|
this._palette = new Uint8Array(1024); // 256 * 4 (max palette size * max bytes-per-pixel)
|
||||||
|
this._len = 0;
|
||||||
|
|
||||||
|
this._zlibs = [];
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
this._zlibs[i] = new Inflator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decodeRect(x, y, width, height, sock, display, depth) {
|
||||||
|
if (this._ctl === null) {
|
||||||
|
if (sock.rQwait("TIGHT compression-control", 1)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._ctl = sock.rQshift8();
|
||||||
|
|
||||||
|
// Reset streams if the server requests it
|
||||||
|
for (let i = 0; i < 4; i++) {
|
||||||
|
if ((this._ctl >> i) & 1) {
|
||||||
|
this._zlibs[i].reset();
|
||||||
|
Log.Info("Reset zlib stream " + i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Figure out filter
|
||||||
|
this._ctl = this._ctl >> 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
let ret;
|
||||||
|
|
||||||
|
if (this._ctl === 0x08) {
|
||||||
|
ret = this._fillRect(x, y, width, height,
|
||||||
|
sock, display, depth);
|
||||||
|
} else if (this._ctl === 0x09) {
|
||||||
|
ret = this._jpegRect(x, y, width, height,
|
||||||
|
sock, display, depth);
|
||||||
|
} else if (this._ctl === 0x0A) {
|
||||||
|
ret = this._pngRect(x, y, width, height,
|
||||||
|
sock, display, depth);
|
||||||
|
} else if ((this._ctl & 0x80) == 0) {
|
||||||
|
ret = this._basicRect(this._ctl, x, y, width, height,
|
||||||
|
sock, display, depth);
|
||||||
|
} else {
|
||||||
|
throw new Error("Illegal tight compression received (ctl: " +
|
||||||
|
this._ctl + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
this._ctl = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
_fillRect(x, y, width, height, sock, display, depth) {
|
||||||
|
if (sock.rQwait("TIGHT", 3)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rQi = sock.rQi;
|
||||||
|
const rQ = sock.rQ;
|
||||||
|
|
||||||
|
display.fillRect(x, y, width, height,
|
||||||
|
[rQ[rQi + 2], rQ[rQi + 1], rQ[rQi]], false);
|
||||||
|
sock.rQskipBytes(3);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_jpegRect(x, y, width, height, sock, display, depth) {
|
||||||
|
let data = this._readData(sock);
|
||||||
|
if (data === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
display.imageRect(x, y, "image/jpeg", data);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_pngRect(x, y, width, height, sock, display, depth) {
|
||||||
|
throw new Error("PNG received in standard Tight rect");
|
||||||
|
}
|
||||||
|
|
||||||
|
_basicRect(ctl, x, y, width, height, sock, display, depth) {
|
||||||
|
if (this._filter === null) {
|
||||||
|
if (ctl & 0x4) {
|
||||||
|
if (sock.rQwait("TIGHT", 1)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._filter = sock.rQshift8();
|
||||||
|
} else {
|
||||||
|
// Implicit CopyFilter
|
||||||
|
this._filter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let streamId = ctl & 0x3;
|
||||||
|
|
||||||
|
let ret;
|
||||||
|
|
||||||
|
switch (this._filter) {
|
||||||
|
case 0: // CopyFilter
|
||||||
|
ret = this._copyFilter(streamId, x, y, width, height,
|
||||||
|
sock, display, depth);
|
||||||
|
break;
|
||||||
|
case 1: // PaletteFilter
|
||||||
|
ret = this._paletteFilter(streamId, x, y, width, height,
|
||||||
|
sock, display, depth);
|
||||||
|
break;
|
||||||
|
case 2: // GradientFilter
|
||||||
|
ret = this._gradientFilter(streamId, x, y, width, height,
|
||||||
|
sock, display, depth);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("Illegal tight filter received (ctl: " +
|
||||||
|
this._filter + ")");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
this._filter = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
_copyFilter(streamId, x, y, width, height, sock, display, depth) {
|
||||||
|
const uncompressedSize = width * height * 3;
|
||||||
|
let data;
|
||||||
|
|
||||||
|
if (uncompressedSize < 12) {
|
||||||
|
if (sock.rQwait("TIGHT", uncompressedSize)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = sock.rQshiftBytes(uncompressedSize);
|
||||||
|
} else {
|
||||||
|
data = this._readData(sock);
|
||||||
|
if (data === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = this._zlibs[streamId].inflate(data, true, uncompressedSize);
|
||||||
|
if (data.length != uncompressedSize) {
|
||||||
|
throw new Error("Incomplete zlib block");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
display.blitRgbImage(x, y, width, height, data, 0, false);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_paletteFilter(streamId, x, y, width, height, sock, display, depth) {
|
||||||
|
if (this._numColors === 0) {
|
||||||
|
if (sock.rQwait("TIGHT palette", 1)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const numColors = sock.rQpeek8() + 1;
|
||||||
|
const paletteSize = numColors * 3;
|
||||||
|
|
||||||
|
if (sock.rQwait("TIGHT palette", 1 + paletteSize)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._numColors = numColors;
|
||||||
|
sock.rQskipBytes(1);
|
||||||
|
|
||||||
|
sock.rQshiftTo(this._palette, paletteSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
const bpp = (this._numColors <= 2) ? 1 : 8;
|
||||||
|
const rowSize = Math.floor((width * bpp + 7) / 8);
|
||||||
|
const uncompressedSize = rowSize * height;
|
||||||
|
|
||||||
|
let data;
|
||||||
|
|
||||||
|
if (uncompressedSize < 12) {
|
||||||
|
if (sock.rQwait("TIGHT", uncompressedSize)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = sock.rQshiftBytes(uncompressedSize);
|
||||||
|
} else {
|
||||||
|
data = this._readData(sock);
|
||||||
|
if (data === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = this._zlibs[streamId].inflate(data, true, uncompressedSize);
|
||||||
|
if (data.length != uncompressedSize) {
|
||||||
|
throw new Error("Incomplete zlib block");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert indexed (palette based) image data to RGB
|
||||||
|
if (this._numColors == 2) {
|
||||||
|
this._monoRect(x, y, width, height, data, this._palette, display);
|
||||||
|
} else {
|
||||||
|
this._paletteRect(x, y, width, height, data, this._palette, display);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._numColors = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_monoRect(x, y, width, height, data, palette, display) {
|
||||||
|
// Convert indexed (palette based) image data to RGB
|
||||||
|
// TODO: reduce number of calculations inside loop
|
||||||
|
const dest = this._getScratchBuffer(width * height * 4);
|
||||||
|
const w = Math.floor((width + 7) / 8);
|
||||||
|
const w1 = Math.floor(width / 8);
|
||||||
|
|
||||||
|
for (let y = 0; y < height; y++) {
|
||||||
|
let dp, sp, x;
|
||||||
|
for (x = 0; x < w1; x++) {
|
||||||
|
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 + 1] = palette[sp + 1];
|
||||||
|
dest[dp + 2] = palette[sp + 2];
|
||||||
|
dest[dp + 3] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 + 1] = palette[sp + 1];
|
||||||
|
dest[dp + 2] = palette[sp + 2];
|
||||||
|
dest[dp + 3] = 255;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
display.blitRgbxImage(x, y, width, height, dest, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_paletteRect(x, y, width, height, data, palette, display) {
|
||||||
|
// Convert indexed (palette based) image data to RGB
|
||||||
|
const dest = this._getScratchBuffer(width * height * 4);
|
||||||
|
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 + 1] = palette[sp + 1];
|
||||||
|
dest[i + 2] = palette[sp + 2];
|
||||||
|
dest[i + 3] = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
display.blitRgbxImage(x, y, width, height, dest, 0, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
_gradientFilter(streamId, x, y, width, height, sock, display, depth) {
|
||||||
|
throw new Error("Gradient filter not implemented");
|
||||||
|
}
|
||||||
|
|
||||||
|
_readData(sock) {
|
||||||
|
if (this._len === 0) {
|
||||||
|
if (sock.rQwait("TIGHT", 3)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let byte;
|
||||||
|
|
||||||
|
byte = sock.rQshift8();
|
||||||
|
this._len = byte & 0x7f;
|
||||||
|
if (byte & 0x80) {
|
||||||
|
byte = sock.rQshift8();
|
||||||
|
this._len |= (byte & 0x7f) << 7;
|
||||||
|
if (byte & 0x80) {
|
||||||
|
byte = sock.rQshift8();
|
||||||
|
this._len |= byte << 14;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sock.rQwait("TIGHT", this._len)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = sock.rQshiftBytes(this._len);
|
||||||
|
this._len = 0;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
_getScratchBuffer(size) {
|
||||||
|
if (!this._scratchBuffer || (this._scratchBuffer.length < size)) {
|
||||||
|
this._scratchBuffer = new Uint8Array(size);
|
||||||
|
}
|
||||||
|
return this._scratchBuffer;
|
||||||
|
}
|
||||||
|
}
|
29
static/js/novnc/core/decoders/tightpng.js
Executable file
29
static/js/novnc/core/decoders/tightpng.js
Executable file
|
@ -0,0 +1,29 @@
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
|
*
|
||||||
|
* See README.md for usage and integration instructions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
import TightDecoder from './tight.js';
|
||||||
|
|
||||||
|
export default class TightPNGDecoder extends TightDecoder {
|
||||||
|
_pngRect(x, y, width, height, sock, display, depth) {
|
||||||
|
let data = this._readData(sock);
|
||||||
|
if (data === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
display.imageRect(x, y, "image/png", data);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_basicRect(ctl, x, y, width, height, sock, display, depth) {
|
||||||
|
throw new Error("BasicCompression received in TightPNG rect");
|
||||||
|
}
|
||||||
|
}
|
|
@ -75,84 +75,83 @@
|
||||||
* fine Java utilities: http://www.acme.com/java/
|
* fine Java utilities: http://www.acme.com/java/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default function DES(passwd) {
|
/* eslint-disable comma-spacing */
|
||||||
"use strict";
|
|
||||||
|
|
||||||
// Tables, permutations, S-boxes, etc.
|
// Tables, permutations, S-boxes, etc.
|
||||||
var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,
|
const PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,
|
||||||
25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,
|
25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,
|
||||||
50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],
|
50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],
|
||||||
totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28],
|
totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28];
|
||||||
z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8,
|
|
||||||
keys = [];
|
|
||||||
|
|
||||||
a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e;
|
const z = 0x0;
|
||||||
SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d,
|
let a,b,c,d,e,f;
|
||||||
z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z,
|
a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e;
|
||||||
a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f,
|
const SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d,
|
||||||
c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d];
|
z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z,
|
||||||
a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e;
|
a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f,
|
||||||
SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d,
|
c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d];
|
||||||
a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f,
|
a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e;
|
||||||
z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z,
|
const SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d,
|
||||||
z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e];
|
a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f,
|
||||||
a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e;
|
z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z,
|
||||||
SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f,
|
z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e];
|
||||||
b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z,
|
a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e;
|
||||||
c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d,
|
const SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f,
|
||||||
b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e];
|
b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z,
|
||||||
a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e;
|
c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d,
|
||||||
SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d,
|
b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e];
|
||||||
z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f,
|
a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e;
|
||||||
b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e,
|
const SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d,
|
||||||
c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e];
|
z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f,
|
||||||
a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e;
|
b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e,
|
||||||
SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z,
|
c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e];
|
||||||
a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f,
|
a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e;
|
||||||
z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e,
|
const SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z,
|
||||||
c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d];
|
a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f,
|
||||||
a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e;
|
z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e,
|
||||||
SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f,
|
c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d];
|
||||||
z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z,
|
a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e;
|
||||||
b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z,
|
const SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f,
|
||||||
a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f];
|
z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z,
|
||||||
a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e;
|
b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z,
|
||||||
SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f,
|
a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f];
|
||||||
b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e,
|
a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e;
|
||||||
b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e,
|
const SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f,
|
||||||
z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d];
|
b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e,
|
||||||
a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e;
|
b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e,
|
||||||
SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d,
|
z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d];
|
||||||
c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z,
|
a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e;
|
||||||
a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f,
|
const SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d,
|
||||||
z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e];
|
c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z,
|
||||||
|
a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f,
|
||||||
|
z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e];
|
||||||
|
|
||||||
// Set the key.
|
/* eslint-enable comma-spacing */
|
||||||
function setKeys(keyBlock) {
|
|
||||||
var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [],
|
|
||||||
raw0, raw1, rawi, KnLi;
|
|
||||||
|
|
||||||
for (j = 0, l = 56; j < 56; ++j, l -= 8) {
|
export default class DES {
|
||||||
|
constructor(password) {
|
||||||
|
this.keys = [];
|
||||||
|
|
||||||
|
// Set the key.
|
||||||
|
const pc1m = [], pcr = [], kn = [];
|
||||||
|
|
||||||
|
for (let j = 0, l = 56; j < 56; ++j, l -= 8) {
|
||||||
l += l < -5 ? 65 : l < -3 ? 31 : l < -1 ? 63 : l === 27 ? 35 : 0; // PC1
|
l += l < -5 ? 65 : l < -3 ? 31 : l < -1 ? 63 : l === 27 ? 35 : 0; // PC1
|
||||||
m = l & 0x7;
|
const m = l & 0x7;
|
||||||
pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
|
pc1m[j] = ((password[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < 16; ++i) {
|
for (let i = 0; i < 16; ++i) {
|
||||||
m = i << 1;
|
const m = i << 1;
|
||||||
n = m + 1;
|
const n = m + 1;
|
||||||
kn[m] = kn[n] = 0;
|
kn[m] = kn[n] = 0;
|
||||||
for (o = 28; o < 59; o += 28) {
|
for (let o = 28; o < 59; o += 28) {
|
||||||
for (j = o - 28; j < o; ++j) {
|
for (let j = o - 28; j < o; ++j) {
|
||||||
l = j + totrot[i];
|
const l = j + totrot[i];
|
||||||
if (l < o) {
|
pcr[j] = l < o ? pc1m[l] : pc1m[l - 28];
|
||||||
pcr[j] = pc1m[l];
|
|
||||||
} else {
|
|
||||||
pcr[j] = pc1m[l - 28];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (j = 0; j < 24; ++j) {
|
for (let j = 0; j < 24; ++j) {
|
||||||
if (pcr[PC2[j]] !== 0) {
|
if (pcr[PC2[j]] !== 0) {
|
||||||
kn[m] |= 1 << (23 - j);
|
kn[m] |= 1 << (23 - j);
|
||||||
}
|
}
|
||||||
|
@ -163,26 +162,26 @@ export default function DES(passwd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// cookey
|
// cookey
|
||||||
for (i = 0, rawi = 0, KnLi = 0; i < 16; ++i) {
|
for (let i = 0, rawi = 0, KnLi = 0; i < 16; ++i) {
|
||||||
raw0 = kn[rawi++];
|
const raw0 = kn[rawi++];
|
||||||
raw1 = kn[rawi++];
|
const raw1 = kn[rawi++];
|
||||||
keys[KnLi] = (raw0 & 0x00fc0000) << 6;
|
this.keys[KnLi] = (raw0 & 0x00fc0000) << 6;
|
||||||
keys[KnLi] |= (raw0 & 0x00000fc0) << 10;
|
this.keys[KnLi] |= (raw0 & 0x00000fc0) << 10;
|
||||||
keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
|
this.keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
|
||||||
keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6;
|
this.keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6;
|
||||||
++KnLi;
|
++KnLi;
|
||||||
keys[KnLi] = (raw0 & 0x0003f000) << 12;
|
this.keys[KnLi] = (raw0 & 0x0003f000) << 12;
|
||||||
keys[KnLi] |= (raw0 & 0x0000003f) << 16;
|
this.keys[KnLi] |= (raw0 & 0x0000003f) << 16;
|
||||||
keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;
|
this.keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;
|
||||||
keys[KnLi] |= (raw1 & 0x0000003f);
|
this.keys[KnLi] |= (raw1 & 0x0000003f);
|
||||||
++KnLi;
|
++KnLi;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt 8 bytes of text
|
// Encrypt 8 bytes of text
|
||||||
function enc8(text) {
|
enc8(text) {
|
||||||
var i = 0, b = text.slice(), fval, keysi = 0,
|
const b = text.slice();
|
||||||
l, r, x; // left, right, accumulator
|
let i = 0, l, r, x; // left, right, accumulator
|
||||||
|
|
||||||
// Squash 8 bytes to 2 ints
|
// Squash 8 bytes to 2 ints
|
||||||
l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
|
l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
|
||||||
|
@ -206,26 +205,26 @@ export default function DES(passwd) {
|
||||||
r ^= x;
|
r ^= x;
|
||||||
l = (l << 1) | ((l >>> 31) & 1);
|
l = (l << 1) | ((l >>> 31) & 1);
|
||||||
|
|
||||||
for (i = 0; i < 8; ++i) {
|
for (let i = 0, keysi = 0; i < 8; ++i) {
|
||||||
x = (r << 28) | (r >>> 4);
|
x = (r << 28) | (r >>> 4);
|
||||||
x ^= keys[keysi++];
|
x ^= this.keys[keysi++];
|
||||||
fval = SP7[x & 0x3f];
|
let fval = SP7[x & 0x3f];
|
||||||
fval |= SP5[(x >>> 8) & 0x3f];
|
fval |= SP5[(x >>> 8) & 0x3f];
|
||||||
fval |= SP3[(x >>> 16) & 0x3f];
|
fval |= SP3[(x >>> 16) & 0x3f];
|
||||||
fval |= SP1[(x >>> 24) & 0x3f];
|
fval |= SP1[(x >>> 24) & 0x3f];
|
||||||
x = r ^ keys[keysi++];
|
x = r ^ this.keys[keysi++];
|
||||||
fval |= SP8[x & 0x3f];
|
fval |= SP8[x & 0x3f];
|
||||||
fval |= SP6[(x >>> 8) & 0x3f];
|
fval |= SP6[(x >>> 8) & 0x3f];
|
||||||
fval |= SP4[(x >>> 16) & 0x3f];
|
fval |= SP4[(x >>> 16) & 0x3f];
|
||||||
fval |= SP2[(x >>> 24) & 0x3f];
|
fval |= SP2[(x >>> 24) & 0x3f];
|
||||||
l ^= fval;
|
l ^= fval;
|
||||||
x = (l << 28) | (l >>> 4);
|
x = (l << 28) | (l >>> 4);
|
||||||
x ^= keys[keysi++];
|
x ^= this.keys[keysi++];
|
||||||
fval = SP7[x & 0x3f];
|
fval = SP7[x & 0x3f];
|
||||||
fval |= SP5[(x >>> 8) & 0x3f];
|
fval |= SP5[(x >>> 8) & 0x3f];
|
||||||
fval |= SP3[(x >>> 16) & 0x3f];
|
fval |= SP3[(x >>> 16) & 0x3f];
|
||||||
fval |= SP1[(x >>> 24) & 0x3f];
|
fval |= SP1[(x >>> 24) & 0x3f];
|
||||||
x = l ^ keys[keysi++];
|
x = l ^ this.keys[keysi++];
|
||||||
fval |= SP8[x & 0x0000003f];
|
fval |= SP8[x & 0x0000003f];
|
||||||
fval |= SP6[(x >>> 8) & 0x3f];
|
fval |= SP6[(x >>> 8) & 0x3f];
|
||||||
fval |= SP4[(x >>> 16) & 0x3f];
|
fval |= SP4[(x >>> 16) & 0x3f];
|
||||||
|
@ -261,11 +260,7 @@ export default function DES(passwd) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Encrypt 16 bytes of text using passwd as key
|
// Encrypt 16 bytes of text using passwd as key
|
||||||
function encrypt(t) {
|
encrypt(t) {
|
||||||
return enc8(t.slice(0, 8)).concat(enc8(t.slice(8, 16)));
|
return this.enc8(t.slice(0, 8)).concat(this.enc8(t.slice(8, 16)));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
setKeys(passwd); // Setup keys
|
|
||||||
return {'encrypt': encrypt}; // Public interface
|
|
||||||
|
|
||||||
}; // function DES
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2018 The noVNC Authors
|
||||||
* Copyright (C) 2015 Samuel Mannehed for Cendio AB
|
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
|
@ -9,111 +8,107 @@
|
||||||
|
|
||||||
import * as Log from './util/logging.js';
|
import * as Log from './util/logging.js';
|
||||||
import Base64 from "./base64.js";
|
import Base64 from "./base64.js";
|
||||||
|
import { supportsImageMetadata } from './util/browser.js';
|
||||||
|
|
||||||
export default function Display(target) {
|
export default class Display {
|
||||||
this._drawCtx = null;
|
constructor(target) {
|
||||||
this._c_forceCanvas = false;
|
this._drawCtx = null;
|
||||||
|
this._c_forceCanvas = false;
|
||||||
|
|
||||||
this._renderQ = []; // queue drawing actions for in-oder rendering
|
this._renderQ = []; // queue drawing actions for in-oder rendering
|
||||||
this._flushing = false;
|
this._flushing = false;
|
||||||
|
|
||||||
// the full frame buffer (logical canvas) size
|
// the full frame buffer (logical canvas) size
|
||||||
this._fb_width = 0;
|
this._fb_width = 0;
|
||||||
this._fb_height = 0;
|
this._fb_height = 0;
|
||||||
|
|
||||||
this._prevDrawStyle = "";
|
this._prevDrawStyle = "";
|
||||||
this._tile = null;
|
this._tile = null;
|
||||||
this._tile16x16 = null;
|
this._tile16x16 = null;
|
||||||
this._tile_x = 0;
|
this._tile_x = 0;
|
||||||
this._tile_y = 0;
|
this._tile_y = 0;
|
||||||
|
|
||||||
Log.Debug(">> Display.constructor");
|
Log.Debug(">> Display.constructor");
|
||||||
|
|
||||||
// The visible canvas
|
// The visible canvas
|
||||||
this._target = target;
|
this._target = target;
|
||||||
|
|
||||||
if (!this._target) {
|
if (!this._target) {
|
||||||
throw new Error("Target must be set");
|
throw new Error("Target must be set");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof this._target === 'string') {
|
||||||
|
throw new Error('target must be a DOM element');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._target.getContext) {
|
||||||
|
throw new Error("no getContext method");
|
||||||
|
}
|
||||||
|
|
||||||
|
this._targetCtx = this._target.getContext('2d');
|
||||||
|
|
||||||
|
// the visible canvas viewport (i.e. what actually gets seen)
|
||||||
|
this._viewportLoc = { 'x': 0, 'y': 0, 'w': this._target.width, 'h': this._target.height };
|
||||||
|
|
||||||
|
// The hidden canvas, where we do the actual rendering
|
||||||
|
this._backbuffer = document.createElement('canvas');
|
||||||
|
this._drawCtx = this._backbuffer.getContext('2d');
|
||||||
|
|
||||||
|
this._damageBounds = { left: 0, top: 0,
|
||||||
|
right: this._backbuffer.width,
|
||||||
|
bottom: this._backbuffer.height };
|
||||||
|
|
||||||
|
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 =====
|
||||||
|
|
||||||
|
this.onflush = () => {}; // A flush request has finished
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof this._target === 'string') {
|
|
||||||
throw new Error('target must be a DOM element');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._target.getContext) {
|
|
||||||
throw new Error("no getContext method");
|
|
||||||
}
|
|
||||||
|
|
||||||
this._targetCtx = this._target.getContext('2d');
|
|
||||||
|
|
||||||
// the visible canvas viewport (i.e. what actually gets seen)
|
|
||||||
this._viewportLoc = { 'x': 0, 'y': 0, 'w': this._target.width, 'h': this._target.height };
|
|
||||||
|
|
||||||
// The hidden canvas, where we do the actual rendering
|
|
||||||
this._backbuffer = document.createElement('canvas');
|
|
||||||
this._drawCtx = this._backbuffer.getContext('2d');
|
|
||||||
|
|
||||||
this._damageBounds = { left:0, top:0,
|
|
||||||
right: this._backbuffer.width,
|
|
||||||
bottom: this._backbuffer.height };
|
|
||||||
|
|
||||||
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");
|
|
||||||
};
|
|
||||||
|
|
||||||
var SUPPORTS_IMAGEDATA_CONSTRUCTOR = false;
|
|
||||||
try {
|
|
||||||
new ImageData(new Uint8ClampedArray(4), 1, 1);
|
|
||||||
SUPPORTS_IMAGEDATA_CONSTRUCTOR = true;
|
|
||||||
} catch (ex) {
|
|
||||||
// ignore failure
|
|
||||||
}
|
|
||||||
|
|
||||||
Display.prototype = {
|
|
||||||
// ===== PROPERTIES =====
|
// ===== PROPERTIES =====
|
||||||
|
|
||||||
_scale: 1.0,
|
get scale() { return this._scale; }
|
||||||
get scale() { return this._scale; },
|
|
||||||
set scale(scale) {
|
set scale(scale) {
|
||||||
this._rescale(scale);
|
this._rescale(scale);
|
||||||
},
|
}
|
||||||
|
|
||||||
_clipViewport: false,
|
get clipViewport() { return this._clipViewport; }
|
||||||
get clipViewport() { return this._clipViewport; },
|
|
||||||
set clipViewport(viewport) {
|
set clipViewport(viewport) {
|
||||||
this._clipViewport = viewport;
|
this._clipViewport = viewport;
|
||||||
// May need to readjust the viewport dimensions
|
// May need to readjust the viewport dimensions
|
||||||
var vp = this._viewportLoc;
|
const vp = this._viewportLoc;
|
||||||
this.viewportChangeSize(vp.w, vp.h);
|
this.viewportChangeSize(vp.w, vp.h);
|
||||||
this.viewportChangePos(0, 0);
|
this.viewportChangePos(0, 0);
|
||||||
},
|
}
|
||||||
|
|
||||||
get width() {
|
get width() {
|
||||||
return this._fb_width;
|
return this._fb_width;
|
||||||
},
|
}
|
||||||
|
|
||||||
get height() {
|
get height() {
|
||||||
return this._fb_height;
|
return this._fb_height;
|
||||||
},
|
}
|
||||||
|
|
||||||
logo: null,
|
|
||||||
|
|
||||||
// ===== EVENT HANDLERS =====
|
|
||||||
|
|
||||||
onflush: function () {}, // A flush request has finished
|
|
||||||
|
|
||||||
// ===== PUBLIC METHODS =====
|
// ===== PUBLIC METHODS =====
|
||||||
|
|
||||||
viewportChangePos: function (deltaX, deltaY) {
|
viewportChangePos(deltaX, deltaY) {
|
||||||
var vp = this._viewportLoc;
|
const vp = this._viewportLoc;
|
||||||
deltaX = Math.floor(deltaX);
|
deltaX = Math.floor(deltaX);
|
||||||
deltaY = Math.floor(deltaY);
|
deltaY = Math.floor(deltaY);
|
||||||
|
|
||||||
|
@ -122,8 +117,8 @@ Display.prototype = {
|
||||||
deltaY = -vp.h;
|
deltaY = -vp.h;
|
||||||
}
|
}
|
||||||
|
|
||||||
var vx2 = vp.x + vp.w - 1;
|
const vx2 = vp.x + vp.w - 1;
|
||||||
var vy2 = vp.y + vp.h - 1;
|
const vy2 = vp.y + vp.h - 1;
|
||||||
|
|
||||||
// Position change
|
// Position change
|
||||||
|
|
||||||
|
@ -152,9 +147,9 @@ Display.prototype = {
|
||||||
this._damage(vp.x, vp.y, vp.w, vp.h);
|
this._damage(vp.x, vp.y, vp.w, vp.h);
|
||||||
|
|
||||||
this.flip();
|
this.flip();
|
||||||
},
|
}
|
||||||
|
|
||||||
viewportChangeSize: function(width, height) {
|
viewportChangeSize(width, height) {
|
||||||
|
|
||||||
if (!this._clipViewport ||
|
if (!this._clipViewport ||
|
||||||
typeof(width) === "undefined" ||
|
typeof(width) === "undefined" ||
|
||||||
|
@ -165,6 +160,9 @@ Display.prototype = {
|
||||||
height = this._fb_height;
|
height = this._fb_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
width = Math.floor(width);
|
||||||
|
height = Math.floor(height);
|
||||||
|
|
||||||
if (width > this._fb_width) {
|
if (width > this._fb_width) {
|
||||||
width = this._fb_width;
|
width = this._fb_width;
|
||||||
}
|
}
|
||||||
|
@ -172,12 +170,12 @@ Display.prototype = {
|
||||||
height = this._fb_height;
|
height = this._fb_height;
|
||||||
}
|
}
|
||||||
|
|
||||||
var vp = this._viewportLoc;
|
const vp = this._viewportLoc;
|
||||||
if (vp.w !== width || vp.h !== height) {
|
if (vp.w !== width || vp.h !== height) {
|
||||||
vp.w = width;
|
vp.w = width;
|
||||||
vp.h = height;
|
vp.h = height;
|
||||||
|
|
||||||
var canvas = this._target;
|
const canvas = this._target;
|
||||||
canvas.width = width;
|
canvas.width = width;
|
||||||
canvas.height = height;
|
canvas.height = height;
|
||||||
|
|
||||||
|
@ -190,27 +188,33 @@ Display.prototype = {
|
||||||
// Update the visible size of the target canvas
|
// Update the visible size of the target canvas
|
||||||
this._rescale(this._scale);
|
this._rescale(this._scale);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
absX: function (x) {
|
absX(x) {
|
||||||
|
if (this._scale === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
return x / this._scale + this._viewportLoc.x;
|
return x / this._scale + this._viewportLoc.x;
|
||||||
},
|
}
|
||||||
|
|
||||||
absY: function (y) {
|
absY(y) {
|
||||||
|
if (this._scale === 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
return y / this._scale + this._viewportLoc.y;
|
return y / this._scale + this._viewportLoc.y;
|
||||||
},
|
}
|
||||||
|
|
||||||
resize: function (width, height) {
|
resize(width, height) {
|
||||||
this._prevDrawStyle = "";
|
this._prevDrawStyle = "";
|
||||||
|
|
||||||
this._fb_width = width;
|
this._fb_width = width;
|
||||||
this._fb_height = height;
|
this._fb_height = height;
|
||||||
|
|
||||||
var canvas = this._backbuffer;
|
const canvas = this._backbuffer;
|
||||||
if (canvas.width !== width || canvas.height !== height) {
|
if (canvas.width !== width || canvas.height !== height) {
|
||||||
|
|
||||||
// We have to save the canvas data since changing the size will clear it
|
// We have to save the canvas data since changing the size will clear it
|
||||||
var saveImg = null;
|
let saveImg = null;
|
||||||
if (canvas.width > 0 && canvas.height > 0) {
|
if (canvas.width > 0 && canvas.height > 0) {
|
||||||
saveImg = this._drawCtx.getImageData(0, 0, canvas.width, canvas.height);
|
saveImg = this._drawCtx.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
}
|
}
|
||||||
|
@ -229,13 +233,13 @@ Display.prototype = {
|
||||||
|
|
||||||
// Readjust the viewport as it may be incorrectly sized
|
// Readjust the viewport as it may be incorrectly sized
|
||||||
// and positioned
|
// and positioned
|
||||||
var vp = this._viewportLoc;
|
const vp = this._viewportLoc;
|
||||||
this.viewportChangeSize(vp.w, vp.h);
|
this.viewportChangeSize(vp.w, vp.h);
|
||||||
this.viewportChangePos(0, 0);
|
this.viewportChangePos(0, 0);
|
||||||
},
|
}
|
||||||
|
|
||||||
// Track what parts of the visible canvas that need updating
|
// Track what parts of the visible canvas that need updating
|
||||||
_damage: function(x, y, w, h) {
|
_damage(x, y, w, h) {
|
||||||
if (x < this._damageBounds.left) {
|
if (x < this._damageBounds.left) {
|
||||||
this._damageBounds.left = x;
|
this._damageBounds.left = x;
|
||||||
}
|
}
|
||||||
|
@ -248,25 +252,23 @@ Display.prototype = {
|
||||||
if ((y + h) > this._damageBounds.bottom) {
|
if ((y + h) > this._damageBounds.bottom) {
|
||||||
this._damageBounds.bottom = y + h;
|
this._damageBounds.bottom = y + h;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// Update the visible canvas with the contents of the
|
// Update the visible canvas with the contents of the
|
||||||
// rendering canvas
|
// rendering canvas
|
||||||
flip: function(from_queue) {
|
flip(from_queue) {
|
||||||
if (this._renderQ.length !== 0 && !from_queue) {
|
if (this._renderQ.length !== 0 && !from_queue) {
|
||||||
this._renderQ_push({
|
this._renderQ_push({
|
||||||
'type': 'flip'
|
'type': 'flip'
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
var x, y, vx, vy, w, h;
|
let x = this._damageBounds.left;
|
||||||
|
let y = this._damageBounds.top;
|
||||||
|
let w = this._damageBounds.right - x;
|
||||||
|
let h = this._damageBounds.bottom - y;
|
||||||
|
|
||||||
x = this._damageBounds.left;
|
let vx = x - this._viewportLoc.x;
|
||||||
y = this._damageBounds.top;
|
let vy = y - this._viewportLoc.y;
|
||||||
w = this._damageBounds.right - x;
|
|
||||||
h = this._damageBounds.bottom - y;
|
|
||||||
|
|
||||||
vx = x - this._viewportLoc.x;
|
|
||||||
vy = y - this._viewportLoc.y;
|
|
||||||
|
|
||||||
if (vx < 0) {
|
if (vx < 0) {
|
||||||
w += vx;
|
w += vx;
|
||||||
|
@ -298,9 +300,9 @@ Display.prototype = {
|
||||||
this._damageBounds.left = this._damageBounds.top = 65535;
|
this._damageBounds.left = this._damageBounds.top = 65535;
|
||||||
this._damageBounds.right = this._damageBounds.bottom = 0;
|
this._damageBounds.right = this._damageBounds.bottom = 0;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
clear: function () {
|
clear() {
|
||||||
if (this._logo) {
|
if (this._logo) {
|
||||||
this.resize(this._logo.width, this._logo.height);
|
this.resize(this._logo.width, this._logo.height);
|
||||||
this.imageRect(0, 0, this._logo.type, this._logo.data);
|
this.imageRect(0, 0, this._logo.type, this._logo.data);
|
||||||
|
@ -309,21 +311,21 @@ Display.prototype = {
|
||||||
this._drawCtx.clearRect(0, 0, this._fb_width, this._fb_height);
|
this._drawCtx.clearRect(0, 0, this._fb_width, this._fb_height);
|
||||||
}
|
}
|
||||||
this.flip();
|
this.flip();
|
||||||
},
|
}
|
||||||
|
|
||||||
pending: function() {
|
pending() {
|
||||||
return this._renderQ.length > 0;
|
return this._renderQ.length > 0;
|
||||||
},
|
}
|
||||||
|
|
||||||
flush: function() {
|
flush() {
|
||||||
if (this._renderQ.length === 0) {
|
if (this._renderQ.length === 0) {
|
||||||
this.onflush();
|
this.onflush();
|
||||||
} else {
|
} else {
|
||||||
this._flushing = true;
|
this._flushing = true;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
fillRect: function (x, y, width, height, color, from_queue) {
|
fillRect(x, y, width, height, color, from_queue) {
|
||||||
if (this._renderQ.length !== 0 && !from_queue) {
|
if (this._renderQ.length !== 0 && !from_queue) {
|
||||||
this._renderQ_push({
|
this._renderQ_push({
|
||||||
'type': 'fill',
|
'type': 'fill',
|
||||||
|
@ -338,9 +340,9 @@ Display.prototype = {
|
||||||
this._drawCtx.fillRect(x, y, width, height);
|
this._drawCtx.fillRect(x, y, width, height);
|
||||||
this._damage(x, y, width, height);
|
this._damage(x, y, width, height);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
copyImage: function (old_x, old_y, new_x, new_y, w, h, from_queue) {
|
copyImage(old_x, old_y, new_x, new_y, w, h, from_queue) {
|
||||||
if (this._renderQ.length !== 0 && !from_queue) {
|
if (this._renderQ.length !== 0 && !from_queue) {
|
||||||
this._renderQ_push({
|
this._renderQ_push({
|
||||||
'type': 'copy',
|
'type': 'copy',
|
||||||
|
@ -369,10 +371,10 @@ Display.prototype = {
|
||||||
new_x, new_y, w, h);
|
new_x, new_y, w, h);
|
||||||
this._damage(new_x, new_y, w, h);
|
this._damage(new_x, new_y, w, h);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
imageRect: function(x, y, mime, arr) {
|
imageRect(x, y, mime, arr) {
|
||||||
var img = new Image();
|
const img = new Image();
|
||||||
img.src = "data: " + mime + ";base64," + Base64.encode(arr);
|
img.src = "data: " + mime + ";base64," + Base64.encode(arr);
|
||||||
this._renderQ_push({
|
this._renderQ_push({
|
||||||
'type': 'img',
|
'type': 'img',
|
||||||
|
@ -380,10 +382,10 @@ Display.prototype = {
|
||||||
'x': x,
|
'x': x,
|
||||||
'y': y
|
'y': y
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
|
|
||||||
// start updating a tile
|
// start updating a tile
|
||||||
startTile: function (x, y, width, height, color) {
|
startTile(x, y, width, height, color) {
|
||||||
this._tile_x = x;
|
this._tile_x = x;
|
||||||
this._tile_y = y;
|
this._tile_y = y;
|
||||||
if (width === 16 && height === 16) {
|
if (width === 16 && height === 16) {
|
||||||
|
@ -392,53 +394,53 @@ Display.prototype = {
|
||||||
this._tile = this._drawCtx.createImageData(width, height);
|
this._tile = this._drawCtx.createImageData(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
var red = color[2];
|
const red = color[2];
|
||||||
var green = color[1];
|
const green = color[1];
|
||||||
var blue = color[0];
|
const blue = color[0];
|
||||||
|
|
||||||
var data = this._tile.data;
|
const data = this._tile.data;
|
||||||
for (var i = 0; i < width * height * 4; i += 4) {
|
for (let i = 0; i < width * height * 4; i += 4) {
|
||||||
data[i] = red;
|
data[i] = red;
|
||||||
data[i + 1] = green;
|
data[i + 1] = green;
|
||||||
data[i + 2] = blue;
|
data[i + 2] = blue;
|
||||||
data[i + 3] = 255;
|
data[i + 3] = 255;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// update sub-rectangle of the current tile
|
// update sub-rectangle of the current tile
|
||||||
subTile: function (x, y, w, h, color) {
|
subTile(x, y, w, h, color) {
|
||||||
var red = color[2];
|
const red = color[2];
|
||||||
var green = color[1];
|
const green = color[1];
|
||||||
var blue = color[0];
|
const blue = color[0];
|
||||||
var xend = x + w;
|
const xend = x + w;
|
||||||
var yend = y + h;
|
const yend = y + h;
|
||||||
|
|
||||||
var data = this._tile.data;
|
const data = this._tile.data;
|
||||||
var width = this._tile.width;
|
const width = this._tile.width;
|
||||||
for (var j = y; j < yend; j++) {
|
for (let j = y; j < yend; j++) {
|
||||||
for (var i = x; i < xend; i++) {
|
for (let i = x; i < xend; i++) {
|
||||||
var p = (i + (j * width)) * 4;
|
const p = (i + (j * width)) * 4;
|
||||||
data[p] = red;
|
data[p] = red;
|
||||||
data[p + 1] = green;
|
data[p + 1] = green;
|
||||||
data[p + 2] = blue;
|
data[p + 2] = blue;
|
||||||
data[p + 3] = 255;
|
data[p + 3] = 255;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// draw the current tile to the screen
|
// draw the current tile to the screen
|
||||||
finishTile: function () {
|
finishTile() {
|
||||||
this._drawCtx.putImageData(this._tile, this._tile_x, this._tile_y);
|
this._drawCtx.putImageData(this._tile, this._tile_x, this._tile_y);
|
||||||
this._damage(this._tile_x, this._tile_y,
|
this._damage(this._tile_x, this._tile_y,
|
||||||
this._tile.width, this._tile.height);
|
this._tile.width, this._tile.height);
|
||||||
},
|
}
|
||||||
|
|
||||||
blitImage: function (x, y, width, height, arr, offset, from_queue) {
|
blitImage(x, y, width, height, arr, offset, from_queue) {
|
||||||
if (this._renderQ.length !== 0 && !from_queue) {
|
if (this._renderQ.length !== 0 && !from_queue) {
|
||||||
// NB(directxman12): it's technically more performant here to use preallocated arrays,
|
// 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,
|
// 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
|
// this probably isn't getting called *nearly* as much
|
||||||
var new_arr = new Uint8Array(width * height * 4);
|
const new_arr = new Uint8Array(width * height * 4);
|
||||||
new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
|
new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
|
||||||
this._renderQ_push({
|
this._renderQ_push({
|
||||||
'type': 'blit',
|
'type': 'blit',
|
||||||
|
@ -451,14 +453,14 @@ Display.prototype = {
|
||||||
} else {
|
} else {
|
||||||
this._bgrxImageData(x, y, width, height, arr, offset);
|
this._bgrxImageData(x, y, width, height, arr, offset);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
blitRgbImage: function (x, y , width, height, arr, offset, from_queue) {
|
blitRgbImage(x, y, width, height, arr, offset, from_queue) {
|
||||||
if (this._renderQ.length !== 0 && !from_queue) {
|
if (this._renderQ.length !== 0 && !from_queue) {
|
||||||
// NB(directxman12): it's technically more performant here to use preallocated arrays,
|
// 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,
|
// 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
|
// this probably isn't getting called *nearly* as much
|
||||||
var new_arr = new Uint8Array(width * height * 3);
|
const new_arr = new Uint8Array(width * height * 3);
|
||||||
new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
|
new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
|
||||||
this._renderQ_push({
|
this._renderQ_push({
|
||||||
'type': 'blitRgb',
|
'type': 'blitRgb',
|
||||||
|
@ -471,14 +473,14 @@ Display.prototype = {
|
||||||
} else {
|
} else {
|
||||||
this._rgbImageData(x, y, width, height, arr, offset);
|
this._rgbImageData(x, y, width, height, arr, offset);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
blitRgbxImage: function (x, y, width, height, arr, offset, from_queue) {
|
blitRgbxImage(x, y, width, height, arr, offset, from_queue) {
|
||||||
if (this._renderQ.length !== 0 && !from_queue) {
|
if (this._renderQ.length !== 0 && !from_queue) {
|
||||||
// NB(directxman12): it's technically more performant here to use preallocated arrays,
|
// 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,
|
// 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
|
// this probably isn't getting called *nearly* as much
|
||||||
var new_arr = new Uint8Array(width * height * 4);
|
const new_arr = new Uint8Array(width * height * 4);
|
||||||
new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
|
new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
|
||||||
this._renderQ_push({
|
this._renderQ_push({
|
||||||
'type': 'blitRgbx',
|
'type': 'blitRgbx',
|
||||||
|
@ -491,72 +493,67 @@ Display.prototype = {
|
||||||
} else {
|
} else {
|
||||||
this._rgbxImageData(x, y, width, height, arr, offset);
|
this._rgbxImageData(x, y, width, height, arr, offset);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
drawImage: function (img, x, y) {
|
drawImage(img, x, y) {
|
||||||
this._drawCtx.drawImage(img, x, y);
|
this._drawCtx.drawImage(img, x, y);
|
||||||
this._damage(x, y, img.width, img.height);
|
this._damage(x, y, img.width, img.height);
|
||||||
},
|
}
|
||||||
|
|
||||||
changeCursor: function (pixels, mask, hotx, hoty, w, h) {
|
autoscale(containerWidth, containerHeight) {
|
||||||
Display.changeCursor(this._target, pixels, mask, hotx, hoty, w, h);
|
let scaleRatio;
|
||||||
},
|
|
||||||
|
|
||||||
defaultCursor: function () {
|
if (containerWidth === 0 || containerHeight === 0) {
|
||||||
this._target.style.cursor = "default";
|
scaleRatio = 0;
|
||||||
},
|
|
||||||
|
|
||||||
disableLocalCursor: function () {
|
|
||||||
this._target.style.cursor = "none";
|
|
||||||
},
|
|
||||||
|
|
||||||
autoscale: function (containerWidth, containerHeight) {
|
|
||||||
var vp = this._viewportLoc;
|
|
||||||
var targetAspectRatio = containerWidth / containerHeight;
|
|
||||||
var fbAspectRatio = vp.w / vp.h;
|
|
||||||
|
|
||||||
var scaleRatio;
|
|
||||||
if (fbAspectRatio >= targetAspectRatio) {
|
|
||||||
scaleRatio = containerWidth / vp.w;
|
|
||||||
} else {
|
} else {
|
||||||
scaleRatio = containerHeight / vp.h;
|
|
||||||
|
const vp = this._viewportLoc;
|
||||||
|
const targetAspectRatio = containerWidth / containerHeight;
|
||||||
|
const fbAspectRatio = vp.w / vp.h;
|
||||||
|
|
||||||
|
if (fbAspectRatio >= targetAspectRatio) {
|
||||||
|
scaleRatio = containerWidth / vp.w;
|
||||||
|
} else {
|
||||||
|
scaleRatio = containerHeight / vp.h;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this._rescale(scaleRatio);
|
this._rescale(scaleRatio);
|
||||||
},
|
}
|
||||||
|
|
||||||
// ===== PRIVATE METHODS =====
|
// ===== PRIVATE METHODS =====
|
||||||
|
|
||||||
_rescale: function (factor) {
|
_rescale(factor) {
|
||||||
this._scale = factor;
|
this._scale = factor;
|
||||||
var vp = this._viewportLoc;
|
const vp = this._viewportLoc;
|
||||||
|
|
||||||
// NB(directxman12): If you set the width directly, or set the
|
// NB(directxman12): If you set the width directly, or set the
|
||||||
// style width to a number, the canvas is cleared.
|
// style width to a number, the canvas is cleared.
|
||||||
// However, if you set the style width to a string
|
// However, if you set the style width to a string
|
||||||
// ('NNNpx'), the canvas is scaled without clearing.
|
// ('NNNpx'), the canvas is scaled without clearing.
|
||||||
var width = Math.round(factor * vp.w) + 'px';
|
const width = factor * vp.w + 'px';
|
||||||
var height = Math.round(factor * vp.h) + 'px';
|
const height = factor * vp.h + 'px';
|
||||||
|
|
||||||
if ((this._target.style.width !== width) ||
|
if ((this._target.style.width !== width) ||
|
||||||
(this._target.style.height !== height)) {
|
(this._target.style.height !== height)) {
|
||||||
this._target.style.width = width;
|
this._target.style.width = width;
|
||||||
this._target.style.height = height;
|
this._target.style.height = height;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_setFillColor: function (color) {
|
_setFillColor(color) {
|
||||||
var newStyle = 'rgb(' + color[2] + ',' + color[1] + ',' + color[0] + ')';
|
const newStyle = 'rgb(' + color[2] + ',' + color[1] + ',' + color[0] + ')';
|
||||||
if (newStyle !== this._prevDrawStyle) {
|
if (newStyle !== this._prevDrawStyle) {
|
||||||
this._drawCtx.fillStyle = newStyle;
|
this._drawCtx.fillStyle = newStyle;
|
||||||
this._prevDrawStyle = newStyle;
|
this._prevDrawStyle = newStyle;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_rgbImageData: function (x, y, width, height, arr, offset) {
|
_rgbImageData(x, y, width, height, arr, offset) {
|
||||||
var img = this._drawCtx.createImageData(width, height);
|
const img = this._drawCtx.createImageData(width, height);
|
||||||
var data = img.data;
|
const data = img.data;
|
||||||
for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 3) {
|
for (let i = 0, j = offset; i < width * height * 4; i += 4, j += 3) {
|
||||||
data[i] = arr[j];
|
data[i] = arr[j];
|
||||||
data[i + 1] = arr[j + 1];
|
data[i + 1] = arr[j + 1];
|
||||||
data[i + 2] = arr[j + 2];
|
data[i + 2] = arr[j + 2];
|
||||||
|
@ -564,12 +561,12 @@ Display.prototype = {
|
||||||
}
|
}
|
||||||
this._drawCtx.putImageData(img, x, y);
|
this._drawCtx.putImageData(img, x, y);
|
||||||
this._damage(x, y, img.width, img.height);
|
this._damage(x, y, img.width, img.height);
|
||||||
},
|
}
|
||||||
|
|
||||||
_bgrxImageData: function (x, y, width, height, arr, offset) {
|
_bgrxImageData(x, y, width, height, arr, offset) {
|
||||||
var img = this._drawCtx.createImageData(width, height);
|
const img = this._drawCtx.createImageData(width, height);
|
||||||
var data = img.data;
|
const data = img.data;
|
||||||
for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 4) {
|
for (let i = 0, j = offset; i < width * height * 4; i += 4, j += 4) {
|
||||||
data[i] = arr[j + 2];
|
data[i] = arr[j + 2];
|
||||||
data[i + 1] = arr[j + 1];
|
data[i + 1] = arr[j + 1];
|
||||||
data[i + 2] = arr[j];
|
data[i + 2] = arr[j];
|
||||||
|
@ -577,12 +574,12 @@ Display.prototype = {
|
||||||
}
|
}
|
||||||
this._drawCtx.putImageData(img, x, y);
|
this._drawCtx.putImageData(img, x, y);
|
||||||
this._damage(x, y, img.width, img.height);
|
this._damage(x, y, img.width, img.height);
|
||||||
},
|
}
|
||||||
|
|
||||||
_rgbxImageData: function (x, y, width, height, arr, offset) {
|
_rgbxImageData(x, y, width, height, arr, offset) {
|
||||||
// NB(directxman12): arr must be an Type Array view
|
// NB(directxman12): arr must be an Type Array view
|
||||||
var img;
|
let img;
|
||||||
if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) {
|
if (supportsImageMetadata) {
|
||||||
img = new ImageData(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4), width, height);
|
img = new ImageData(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4), width, height);
|
||||||
} else {
|
} else {
|
||||||
img = this._drawCtx.createImageData(width, height);
|
img = this._drawCtx.createImageData(width, height);
|
||||||
|
@ -590,28 +587,28 @@ Display.prototype = {
|
||||||
}
|
}
|
||||||
this._drawCtx.putImageData(img, x, y);
|
this._drawCtx.putImageData(img, x, y);
|
||||||
this._damage(x, y, img.width, img.height);
|
this._damage(x, y, img.width, img.height);
|
||||||
},
|
}
|
||||||
|
|
||||||
_renderQ_push: function (action) {
|
_renderQ_push(action) {
|
||||||
this._renderQ.push(action);
|
this._renderQ.push(action);
|
||||||
if (this._renderQ.length === 1) {
|
if (this._renderQ.length === 1) {
|
||||||
// If this can be rendered immediately it will be, otherwise
|
// If this can be rendered immediately it will be, otherwise
|
||||||
// the scanner will wait for the relevant event
|
// the scanner will wait for the relevant event
|
||||||
this._scan_renderQ();
|
this._scan_renderQ();
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
_resume_renderQ: function() {
|
_resume_renderQ() {
|
||||||
// "this" is the object that is ready, not the
|
// "this" is the object that is ready, not the
|
||||||
// display object
|
// display object
|
||||||
this.removeEventListener('load', this._noVNC_display._resume_renderQ);
|
this.removeEventListener('load', this._noVNC_display._resume_renderQ);
|
||||||
this._noVNC_display._scan_renderQ();
|
this._noVNC_display._scan_renderQ();
|
||||||
},
|
}
|
||||||
|
|
||||||
_scan_renderQ: function () {
|
_scan_renderQ() {
|
||||||
var ready = true;
|
let ready = true;
|
||||||
while (ready && this._renderQ.length > 0) {
|
while (ready && this._renderQ.length > 0) {
|
||||||
var a = this._renderQ[0];
|
const a = this._renderQ[0];
|
||||||
switch (a.type) {
|
switch (a.type) {
|
||||||
case 'flip':
|
case 'flip':
|
||||||
this.flip(true);
|
this.flip(true);
|
||||||
|
@ -653,46 +650,5 @@ Display.prototype = {
|
||||||
this._flushing = false;
|
this._flushing = false;
|
||||||
this.onflush();
|
this.onflush();
|
||||||
}
|
}
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Class Methods
|
|
||||||
Display.changeCursor = function (target, pixels, mask, hotx, hoty, w, h) {
|
|
||||||
if ((w === 0) || (h === 0)) {
|
|
||||||
target.style.cursor = 'none';
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
var cur = []
|
|
||||||
var y, x;
|
|
||||||
for (y = 0; y < h; y++) {
|
|
||||||
for (x = 0; x < w; x++) {
|
|
||||||
var idx = y * Math.ceil(w / 8) + Math.floor(x / 8);
|
|
||||||
var alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0;
|
|
||||||
idx = ((w * y) + x) * 4;
|
|
||||||
cur.push(pixels[idx + 2]); // red
|
|
||||||
cur.push(pixels[idx + 1]); // green
|
|
||||||
cur.push(pixels[idx]); // blue
|
|
||||||
cur.push(alpha); // alpha
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var canvas = document.createElement('canvas');
|
|
||||||
var ctx = canvas.getContext('2d');
|
|
||||||
|
|
||||||
canvas.width = w;
|
|
||||||
canvas.height = h;
|
|
||||||
|
|
||||||
var img;
|
|
||||||
if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) {
|
|
||||||
img = new ImageData(new Uint8ClampedArray(cur), w, h);
|
|
||||||
} else {
|
|
||||||
img = ctx.createImageData(w, h);
|
|
||||||
img.data.set(new Uint8ClampedArray(cur));
|
|
||||||
}
|
|
||||||
ctx.clearRect(0, 0, w, h);
|
|
||||||
ctx.putImageData(img, 0, 0);
|
|
||||||
|
|
||||||
var url = canvas.toDataURL();
|
|
||||||
target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2017 Pierre Ossman for Cendio AB
|
* Copyright (C) 2018 The noVNC Authors
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export var encodings = {
|
export const encodings = {
|
||||||
encodingRaw: 0,
|
encodingRaw: 0,
|
||||||
encodingCopyRect: 1,
|
encodingCopyRect: 1,
|
||||||
encodingRRE: 2,
|
encodingRRE: 2,
|
||||||
encodingHextile: 5,
|
encodingHextile: 5,
|
||||||
encodingTight: 7,
|
encodingTight: 7,
|
||||||
|
encodingTightPNG: -260,
|
||||||
|
|
||||||
pseudoEncodingQualityLevel9: -23,
|
pseudoEncodingQualityLevel9: -23,
|
||||||
pseudoEncodingQualityLevel0: -32,
|
pseudoEncodingQualityLevel0: -32,
|
||||||
|
@ -19,7 +20,6 @@ export var encodings = {
|
||||||
pseudoEncodingLastRect: -224,
|
pseudoEncodingLastRect: -224,
|
||||||
pseudoEncodingCursor: -239,
|
pseudoEncodingCursor: -239,
|
||||||
pseudoEncodingQEMUExtendedKeyEvent: -258,
|
pseudoEncodingQEMUExtendedKeyEvent: -258,
|
||||||
pseudoEncodingTightPNG: -260,
|
|
||||||
pseudoEncodingExtendedDesktopSize: -308,
|
pseudoEncodingExtendedDesktopSize: -308,
|
||||||
pseudoEncodingXvp: -309,
|
pseudoEncodingXvp: -309,
|
||||||
pseudoEncodingFence: -312,
|
pseudoEncodingFence: -312,
|
||||||
|
@ -35,6 +35,7 @@ export function encodingName(num) {
|
||||||
case encodings.encodingRRE: return "RRE";
|
case encodings.encodingRRE: return "RRE";
|
||||||
case encodings.encodingHextile: return "Hextile";
|
case encodings.encodingHextile: return "Hextile";
|
||||||
case encodings.encodingTight: return "Tight";
|
case encodings.encodingTight: return "Tight";
|
||||||
|
case encodings.encodingTightPNG: return "TightPNG";
|
||||||
default: return "[unknown encoding " + num + "]";
|
default: return "[unknown encoding " + num + "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,17 @@
|
||||||
import { inflateInit, inflate, inflateReset } from "../vendor/pako/lib/zlib/inflate.js";
|
import { inflateInit, inflate, inflateReset } from "../vendor/pako/lib/zlib/inflate.js";
|
||||||
import ZStream from "../vendor/pako/lib/zlib/zstream.js";
|
import ZStream from "../vendor/pako/lib/zlib/zstream.js";
|
||||||
|
|
||||||
Inflate.prototype = {
|
export default class Inflate {
|
||||||
inflate: function (data, flush, expected) {
|
constructor() {
|
||||||
|
this.strm = new ZStream();
|
||||||
|
this.chunkSize = 1024 * 10 * 10;
|
||||||
|
this.strm.output = new Uint8Array(this.chunkSize);
|
||||||
|
this.windowBits = 5;
|
||||||
|
|
||||||
|
inflateInit(this.strm, this.windowBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
inflate(data, flush, expected) {
|
||||||
this.strm.input = data;
|
this.strm.input = data;
|
||||||
this.strm.avail_in = this.strm.input.length;
|
this.strm.avail_in = this.strm.input.length;
|
||||||
this.strm.next_in = 0;
|
this.strm.next_in = 0;
|
||||||
|
@ -21,18 +30,9 @@ Inflate.prototype = {
|
||||||
inflate(this.strm, flush);
|
inflate(this.strm, flush);
|
||||||
|
|
||||||
return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
|
return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
|
||||||
},
|
}
|
||||||
|
|
||||||
reset: function () {
|
reset() {
|
||||||
inflateReset(this.strm);
|
inflateReset(this.strm);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export default function Inflate() {
|
|
||||||
this.strm = new ZStream();
|
|
||||||
this.chunkSize = 1024 * 10 * 10;
|
|
||||||
this.strm.output = new Uint8Array(this.chunkSize);
|
|
||||||
this.windowBits = 5;
|
|
||||||
|
|
||||||
inflateInit(this.strm, this.windowBits);
|
|
||||||
};
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2017 Pierre Ossman for Cendio AB
|
* Copyright (C) 2018 The noVNC Authors
|
||||||
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -13,28 +13,25 @@ import KeyTable from "./keysym.js";
|
||||||
* See https://www.w3.org/TR/uievents-key/ for possible values.
|
* See https://www.w3.org/TR/uievents-key/ for possible values.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var DOMKeyTable = {};
|
const DOMKeyTable = {};
|
||||||
|
|
||||||
function addStandard(key, standard)
|
function addStandard(key, standard) {
|
||||||
{
|
if (standard === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
|
||||||
if (standard === undefined) throw "Undefined keysym for key \"" + key + "\"";
|
if (key in DOMKeyTable) throw new Error("Duplicate entry for key \"" + key + "\"");
|
||||||
if (key in DOMKeyTable) throw "Duplicate entry for key \"" + key + "\"";
|
|
||||||
DOMKeyTable[key] = [standard, standard, standard, standard];
|
DOMKeyTable[key] = [standard, standard, standard, standard];
|
||||||
}
|
}
|
||||||
|
|
||||||
function addLeftRight(key, left, right)
|
function addLeftRight(key, left, right) {
|
||||||
{
|
if (left === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
|
||||||
if (left === undefined) throw "Undefined keysym for key \"" + key + "\"";
|
if (right === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
|
||||||
if (right === undefined) throw "Undefined keysym for key \"" + key + "\"";
|
if (key in DOMKeyTable) throw new Error("Duplicate entry for key \"" + key + "\"");
|
||||||
if (key in DOMKeyTable) throw "Duplicate entry for key \"" + key + "\"";
|
|
||||||
DOMKeyTable[key] = [left, left, right, left];
|
DOMKeyTable[key] = [left, left, right, left];
|
||||||
}
|
}
|
||||||
|
|
||||||
function addNumpad(key, standard, numpad)
|
function addNumpad(key, standard, numpad) {
|
||||||
{
|
if (standard === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
|
||||||
if (standard === undefined) throw "Undefined keysym for key \"" + key + "\"";
|
if (numpad === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
|
||||||
if (numpad === undefined) throw "Undefined keysym for key \"" + key + "\"";
|
if (key in DOMKeyTable) throw new Error("Duplicate entry for key \"" + key + "\"");
|
||||||
if (key in DOMKeyTable) throw "Duplicate entry for key \"" + key + "\"";
|
|
||||||
DOMKeyTable[key] = [standard, standard, standard, numpad];
|
DOMKeyTable[key] = [standard, standard, standard, numpad];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,7 +72,7 @@ addNumpad("PageUp", KeyTable.XK_Prior, KeyTable.XK_KP_Prior);
|
||||||
// 2.5. Editing Keys
|
// 2.5. Editing Keys
|
||||||
|
|
||||||
addStandard("Backspace", KeyTable.XK_BackSpace);
|
addStandard("Backspace", KeyTable.XK_BackSpace);
|
||||||
addStandard("Clear", KeyTable.XK_Clear);
|
addNumpad("Clear", KeyTable.XK_Clear, KeyTable.XK_KP_Begin);
|
||||||
addStandard("Copy", KeyTable.XF86XK_Copy);
|
addStandard("Copy", KeyTable.XF86XK_Copy);
|
||||||
// - CrSel
|
// - CrSel
|
||||||
addStandard("Cut", KeyTable.XF86XK_Cut);
|
addStandard("Cut", KeyTable.XF86XK_Cut);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2017 Pierre Ossman for Cendio AB
|
* Copyright (C) 2018 The noVNC Authors
|
||||||
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -14,6 +14,8 @@
|
||||||
* See https://www.w3.org/TR/uievents-key/ for possible values.
|
* See https://www.w3.org/TR/uievents-key/ for possible values.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/* eslint-disable key-spacing */
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
||||||
// 3.1.1.1. Writing System Keys
|
// 3.1.1.1. Writing System Keys
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2018 The noVNC Authors
|
||||||
* Copyright (C) 2013 Samuel Mannehed for Cendio AB
|
|
||||||
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -15,63 +14,49 @@ import * as browser from "../util/browser.js";
|
||||||
// Keyboard event handler
|
// Keyboard event handler
|
||||||
//
|
//
|
||||||
|
|
||||||
export default function Keyboard(target) {
|
export default class Keyboard {
|
||||||
this._target = target || null;
|
constructor(target) {
|
||||||
|
this._target = target || null;
|
||||||
|
|
||||||
this._keyDownList = {}; // List of depressed keys
|
this._keyDownList = {}; // List of depressed keys
|
||||||
// (even if they are happy)
|
// (even if they are happy)
|
||||||
this._pendingKey = null; // Key waiting for keypress
|
this._pendingKey = null; // Key waiting for keypress
|
||||||
|
this._altGrArmed = false; // Windows AltGr detection
|
||||||
|
|
||||||
// keep these here so we can refer to them later
|
// keep these here so we can refer to them later
|
||||||
this._eventHandlers = {
|
this._eventHandlers = {
|
||||||
'keyup': this._handleKeyUp.bind(this),
|
'keyup': this._handleKeyUp.bind(this),
|
||||||
'keydown': this._handleKeyDown.bind(this),
|
'keydown': this._handleKeyDown.bind(this),
|
||||||
'keypress': this._handleKeyPress.bind(this),
|
'keypress': this._handleKeyPress.bind(this),
|
||||||
'blur': this._allKeysUp.bind(this)
|
'blur': this._allKeysUp.bind(this),
|
||||||
};
|
'checkalt': this._checkAlt.bind(this),
|
||||||
};
|
};
|
||||||
|
|
||||||
Keyboard.prototype = {
|
// ===== EVENT HANDLERS =====
|
||||||
// ===== EVENT HANDLERS =====
|
|
||||||
|
|
||||||
onkeyevent: function () {}, // Handler for key press/release
|
this.onkeyevent = () => {}; // Handler for key press/release
|
||||||
|
}
|
||||||
|
|
||||||
// ===== PRIVATE METHODS =====
|
// ===== PRIVATE METHODS =====
|
||||||
|
|
||||||
_sendKeyEvent: function (keysym, code, down) {
|
_sendKeyEvent(keysym, code, down) {
|
||||||
|
if (down) {
|
||||||
|
this._keyDownList[code] = keysym;
|
||||||
|
} else {
|
||||||
|
// Do we really think this key is down?
|
||||||
|
if (!(code in this._keyDownList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
delete this._keyDownList[code];
|
||||||
|
}
|
||||||
|
|
||||||
Log.Debug("onkeyevent " + (down ? "down" : "up") +
|
Log.Debug("onkeyevent " + (down ? "down" : "up") +
|
||||||
", keysym: " + keysym, ", code: " + code);
|
", keysym: " + keysym, ", code: " + code);
|
||||||
|
|
||||||
// Windows sends CtrlLeft+AltRight when you press
|
|
||||||
// AltGraph, which tends to confuse the hell out of
|
|
||||||
// remote systems. Fake a release of these keys until
|
|
||||||
// there is a way to detect AltGraph properly.
|
|
||||||
var fakeAltGraph = false;
|
|
||||||
if (down && browser.isWindows()) {
|
|
||||||
if ((code !== 'ControlLeft') &&
|
|
||||||
(code !== 'AltRight') &&
|
|
||||||
('ControlLeft' in this._keyDownList) &&
|
|
||||||
('AltRight' in this._keyDownList)) {
|
|
||||||
fakeAltGraph = true;
|
|
||||||
this.onkeyevent(this._keyDownList['AltRight'],
|
|
||||||
'AltRight', false);
|
|
||||||
this.onkeyevent(this._keyDownList['ControlLeft'],
|
|
||||||
'ControlLeft', false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.onkeyevent(keysym, code, down);
|
this.onkeyevent(keysym, code, down);
|
||||||
|
}
|
||||||
|
|
||||||
if (fakeAltGraph) {
|
_getKeyCode(e) {
|
||||||
this.onkeyevent(this._keyDownList['ControlLeft'],
|
const code = KeyboardUtil.getKeycode(e);
|
||||||
'ControlLeft', true);
|
|
||||||
this.onkeyevent(this._keyDownList['AltRight'],
|
|
||||||
'AltRight', true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_getKeyCode: function (e) {
|
|
||||||
var code = KeyboardUtil.getKeycode(e);
|
|
||||||
if (code !== 'Unidentified') {
|
if (code !== 'Unidentified') {
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
|
@ -94,20 +79,42 @@ Keyboard.prototype = {
|
||||||
return e.keyIdentifier;
|
return e.keyIdentifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
var codepoint = parseInt(e.keyIdentifier.substr(2), 16);
|
const codepoint = parseInt(e.keyIdentifier.substr(2), 16);
|
||||||
var char = String.fromCharCode(codepoint);
|
const char = String.fromCharCode(codepoint).toUpperCase();
|
||||||
// Some implementations fail to uppercase the symbols
|
|
||||||
char = char.toUpperCase();
|
|
||||||
|
|
||||||
return 'Platform' + char.charCodeAt();
|
return 'Platform' + char.charCodeAt();
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'Unidentified';
|
return 'Unidentified';
|
||||||
},
|
}
|
||||||
|
|
||||||
_handleKeyDown: function (e) {
|
_handleKeyDown(e) {
|
||||||
var code = this._getKeyCode(e);
|
const code = this._getKeyCode(e);
|
||||||
var keysym = KeyboardUtil.getKeysym(e);
|
let keysym = KeyboardUtil.getKeysym(e);
|
||||||
|
|
||||||
|
// Windows doesn't have a proper AltGr, but handles it using
|
||||||
|
// fake Ctrl+Alt. However the remote end might not be Windows,
|
||||||
|
// so we need to merge those in to a single AltGr event. We
|
||||||
|
// detect this case by seeing the two key events directly after
|
||||||
|
// each other with a very short time between them (<50ms).
|
||||||
|
if (this._altGrArmed) {
|
||||||
|
this._altGrArmed = false;
|
||||||
|
clearTimeout(this._altGrTimeout);
|
||||||
|
|
||||||
|
if ((code === "AltRight") &&
|
||||||
|
((e.timeStamp - this._altGrCtrlTime) < 50)) {
|
||||||
|
// FIXME: We fail to detect this if either Ctrl key is
|
||||||
|
// first manually pressed as Windows then no
|
||||||
|
// longer sends the fake Ctrl down event. It
|
||||||
|
// does however happily send real Ctrl events
|
||||||
|
// even when AltGr is already down. Some
|
||||||
|
// browsers detect this for us though and set the
|
||||||
|
// key to "AltGraph".
|
||||||
|
keysym = KeyTable.XK_ISO_Level3_Shift;
|
||||||
|
} else {
|
||||||
|
this._sendKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We cannot handle keys we cannot track, but we also need
|
// We cannot handle keys we cannot track, but we also need
|
||||||
// to deal with virtual keyboards which omit key info
|
// to deal with virtual keyboards which omit key info
|
||||||
|
@ -132,18 +139,18 @@ Keyboard.prototype = {
|
||||||
// possibly others).
|
// possibly others).
|
||||||
if (browser.isMac()) {
|
if (browser.isMac()) {
|
||||||
switch (keysym) {
|
switch (keysym) {
|
||||||
case KeyTable.XK_Super_L:
|
case KeyTable.XK_Super_L:
|
||||||
keysym = KeyTable.XK_Alt_L;
|
keysym = KeyTable.XK_Alt_L;
|
||||||
break;
|
break;
|
||||||
case KeyTable.XK_Super_R:
|
case KeyTable.XK_Super_R:
|
||||||
keysym = KeyTable.XK_Super_L;
|
keysym = KeyTable.XK_Super_L;
|
||||||
break;
|
break;
|
||||||
case KeyTable.XK_Alt_L:
|
case KeyTable.XK_Alt_L:
|
||||||
keysym = KeyTable.XK_Mode_switch;
|
keysym = KeyTable.XK_Mode_switch;
|
||||||
break;
|
break;
|
||||||
case KeyTable.XK_Alt_R:
|
case KeyTable.XK_Alt_R:
|
||||||
keysym = KeyTable.XK_ISO_Level3_Shift;
|
keysym = KeyTable.XK_ISO_Level3_Shift;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,13 +187,20 @@ Keyboard.prototype = {
|
||||||
this._pendingKey = null;
|
this._pendingKey = null;
|
||||||
stopEvent(e);
|
stopEvent(e);
|
||||||
|
|
||||||
this._keyDownList[code] = keysym;
|
// Possible start of AltGr sequence? (see above)
|
||||||
|
if ((code === "ControlLeft") && browser.isWindows() &&
|
||||||
|
!("ControlLeft" in this._keyDownList)) {
|
||||||
|
this._altGrArmed = true;
|
||||||
|
this._altGrTimeout = setTimeout(this._handleAltGrTimeout.bind(this), 100);
|
||||||
|
this._altGrCtrlTime = e.timeStamp;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this._sendKeyEvent(keysym, code, true);
|
this._sendKeyEvent(keysym, code, true);
|
||||||
},
|
}
|
||||||
|
|
||||||
// Legacy event for browsers without code/key
|
// Legacy event for browsers without code/key
|
||||||
_handleKeyPress: function (e) {
|
_handleKeyPress(e) {
|
||||||
stopEvent(e);
|
stopEvent(e);
|
||||||
|
|
||||||
// Are we expecting a keypress?
|
// Are we expecting a keypress?
|
||||||
|
@ -194,8 +208,8 @@ Keyboard.prototype = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var code = this._getKeyCode(e);
|
let code = this._getKeyCode(e);
|
||||||
var keysym = KeyboardUtil.getKeysym(e);
|
const keysym = KeyboardUtil.getKeysym(e);
|
||||||
|
|
||||||
// The key we were waiting for?
|
// The key we were waiting for?
|
||||||
if ((code !== 'Unidentified') && (code != this._pendingKey)) {
|
if ((code !== 'Unidentified') && (code != this._pendingKey)) {
|
||||||
|
@ -210,19 +224,18 @@ Keyboard.prototype = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._keyDownList[code] = keysym;
|
|
||||||
|
|
||||||
this._sendKeyEvent(keysym, code, true);
|
this._sendKeyEvent(keysym, code, true);
|
||||||
},
|
}
|
||||||
_handleKeyPressTimeout: function (e) {
|
|
||||||
|
_handleKeyPressTimeout(e) {
|
||||||
// Did someone manage to sort out the key already?
|
// Did someone manage to sort out the key already?
|
||||||
if (this._pendingKey === null) {
|
if (this._pendingKey === null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var code, keysym;
|
let keysym;
|
||||||
|
|
||||||
code = this._pendingKey;
|
const code = this._pendingKey;
|
||||||
this._pendingKey = null;
|
this._pendingKey = null;
|
||||||
|
|
||||||
// We have no way of knowing the proper keysym with the
|
// We have no way of knowing the proper keysym with the
|
||||||
|
@ -233,27 +246,34 @@ Keyboard.prototype = {
|
||||||
keysym = e.keyCode;
|
keysym = e.keyCode;
|
||||||
} else if ((e.keyCode >= 0x41) && (e.keyCode <= 0x5a)) {
|
} else if ((e.keyCode >= 0x41) && (e.keyCode <= 0x5a)) {
|
||||||
// Character (A-Z)
|
// Character (A-Z)
|
||||||
var char = String.fromCharCode(e.keyCode);
|
let char = String.fromCharCode(e.keyCode);
|
||||||
// A feeble attempt at the correct case
|
// A feeble attempt at the correct case
|
||||||
if (e.shiftKey)
|
if (e.shiftKey) {
|
||||||
char = char.toUpperCase();
|
char = char.toUpperCase();
|
||||||
else
|
} else {
|
||||||
char = char.toLowerCase();
|
char = char.toLowerCase();
|
||||||
|
}
|
||||||
keysym = char.charCodeAt();
|
keysym = char.charCodeAt();
|
||||||
} else {
|
} else {
|
||||||
// Unknown, give up
|
// Unknown, give up
|
||||||
keysym = 0;
|
keysym = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._keyDownList[code] = keysym;
|
|
||||||
|
|
||||||
this._sendKeyEvent(keysym, code, true);
|
this._sendKeyEvent(keysym, code, true);
|
||||||
},
|
}
|
||||||
|
|
||||||
_handleKeyUp: function (e) {
|
_handleKeyUp(e) {
|
||||||
stopEvent(e);
|
stopEvent(e);
|
||||||
|
|
||||||
var code = this._getKeyCode(e);
|
const code = this._getKeyCode(e);
|
||||||
|
|
||||||
|
// We can't get a release in the middle of an AltGr sequence, so
|
||||||
|
// abort that detection
|
||||||
|
if (this._altGrArmed) {
|
||||||
|
this._altGrArmed = false;
|
||||||
|
clearTimeout(this._altGrTimeout);
|
||||||
|
this._sendKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
|
||||||
|
}
|
||||||
|
|
||||||
// See comment in _handleKeyDown()
|
// See comment in _handleKeyDown()
|
||||||
if (browser.isMac() && (code === 'CapsLock')) {
|
if (browser.isMac() && (code === 'CapsLock')) {
|
||||||
|
@ -262,53 +282,89 @@ Keyboard.prototype = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Do we really think this key is down?
|
this._sendKeyEvent(this._keyDownList[code], code, false);
|
||||||
if (!(code in this._keyDownList)) {
|
}
|
||||||
|
|
||||||
|
_handleAltGrTimeout() {
|
||||||
|
this._altGrArmed = false;
|
||||||
|
clearTimeout(this._altGrTimeout);
|
||||||
|
this._sendKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
_allKeysUp() {
|
||||||
|
Log.Debug(">> Keyboard.allKeysUp");
|
||||||
|
for (let code in this._keyDownList) {
|
||||||
|
this._sendKeyEvent(this._keyDownList[code], code, false);
|
||||||
|
}
|
||||||
|
Log.Debug("<< Keyboard.allKeysUp");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Firefox Alt workaround, see below
|
||||||
|
_checkAlt(e) {
|
||||||
|
if (e.altKey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._sendKeyEvent(this._keyDownList[code], code, false);
|
const target = this._target;
|
||||||
|
const downList = this._keyDownList;
|
||||||
|
['AltLeft', 'AltRight'].forEach((code) => {
|
||||||
|
if (!(code in downList)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
delete this._keyDownList[code];
|
const event = new KeyboardEvent('keyup',
|
||||||
},
|
{ key: downList[code],
|
||||||
|
code: code });
|
||||||
_allKeysUp: function () {
|
target.dispatchEvent(event);
|
||||||
Log.Debug(">> Keyboard.allKeysUp");
|
});
|
||||||
for (var code in this._keyDownList) {
|
}
|
||||||
this._sendKeyEvent(this._keyDownList[code], code, false);
|
|
||||||
};
|
|
||||||
this._keyDownList = {};
|
|
||||||
Log.Debug("<< Keyboard.allKeysUp");
|
|
||||||
},
|
|
||||||
|
|
||||||
// ===== PUBLIC METHODS =====
|
// ===== PUBLIC METHODS =====
|
||||||
|
|
||||||
grab: function () {
|
grab() {
|
||||||
//Log.Debug(">> Keyboard.grab");
|
//Log.Debug(">> Keyboard.grab");
|
||||||
var c = this._target;
|
|
||||||
|
|
||||||
c.addEventListener('keydown', this._eventHandlers.keydown);
|
this._target.addEventListener('keydown', this._eventHandlers.keydown);
|
||||||
c.addEventListener('keyup', this._eventHandlers.keyup);
|
this._target.addEventListener('keyup', this._eventHandlers.keyup);
|
||||||
c.addEventListener('keypress', this._eventHandlers.keypress);
|
this._target.addEventListener('keypress', this._eventHandlers.keypress);
|
||||||
|
|
||||||
// Release (key up) if window loses focus
|
// Release (key up) if window loses focus
|
||||||
window.addEventListener('blur', this._eventHandlers.blur);
|
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())
|
||||||
|
if (browser.isWindows() && browser.isFirefox()) {
|
||||||
|
const handler = this._eventHandlers.checkalt;
|
||||||
|
['mousedown', 'mouseup', 'mousemove', 'wheel',
|
||||||
|
'touchstart', 'touchend', 'touchmove',
|
||||||
|
'keydown', 'keyup'].forEach(type =>
|
||||||
|
document.addEventListener(type, handler,
|
||||||
|
{ capture: true,
|
||||||
|
passive: true }));
|
||||||
|
}
|
||||||
|
|
||||||
//Log.Debug("<< Keyboard.grab");
|
//Log.Debug("<< Keyboard.grab");
|
||||||
},
|
}
|
||||||
|
|
||||||
ungrab: function () {
|
ungrab() {
|
||||||
//Log.Debug(">> Keyboard.ungrab");
|
//Log.Debug(">> Keyboard.ungrab");
|
||||||
var c = this._target;
|
|
||||||
|
|
||||||
c.removeEventListener('keydown', this._eventHandlers.keydown);
|
if (browser.isWindows() && browser.isFirefox()) {
|
||||||
c.removeEventListener('keyup', this._eventHandlers.keyup);
|
const handler = this._eventHandlers.checkalt;
|
||||||
c.removeEventListener('keypress', this._eventHandlers.keypress);
|
['mousedown', 'mouseup', 'mousemove', 'wheel',
|
||||||
|
'touchstart', 'touchend', 'touchmove',
|
||||||
|
'keydown', 'keyup'].forEach(type => document.removeEventListener(type, handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
this._target.removeEventListener('keydown', this._eventHandlers.keydown);
|
||||||
|
this._target.removeEventListener('keyup', this._eventHandlers.keyup);
|
||||||
|
this._target.removeEventListener('keypress', this._eventHandlers.keypress);
|
||||||
window.removeEventListener('blur', this._eventHandlers.blur);
|
window.removeEventListener('blur', this._eventHandlers.blur);
|
||||||
|
|
||||||
// Release (key up) all keys that are in a down state
|
// Release (key up) all keys that are in a down state
|
||||||
this._allKeysUp();
|
this._allKeysUp();
|
||||||
|
|
||||||
//Log.Debug(">> Keyboard.ungrab");
|
//Log.Debug(">> Keyboard.ungrab");
|
||||||
},
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
/* eslint-disable key-spacing */
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
XK_VoidSymbol: 0xffffff, /* Void symbol */
|
XK_VoidSymbol: 0xffffff, /* Void symbol */
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
/* Functions at the bottom */
|
/* Functions at the bottom */
|
||||||
|
|
||||||
var codepoints = {
|
const codepoints = {
|
||||||
0x0100: 0x03c0, // XK_Amacron
|
0x0100: 0x03c0, // XK_Amacron
|
||||||
0x0101: 0x03e0, // XK_amacron
|
0x0101: 0x03e0, // XK_amacron
|
||||||
0x0102: 0x01c3, // XK_Abreve
|
0x0102: 0x01c3, // XK_Abreve
|
||||||
|
@ -670,14 +670,14 @@ var codepoints = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
lookup : function(u) {
|
lookup(u) {
|
||||||
// Latin-1 is one-to-one mapping
|
// Latin-1 is one-to-one mapping
|
||||||
if ((u >= 0x20) && (u <= 0xff)) {
|
if ((u >= 0x20) && (u <= 0xff)) {
|
||||||
return u;
|
return u;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lookup table (fairly random)
|
// Lookup table (fairly random)
|
||||||
var keysym = codepoints[u];
|
const keysym = codepoints[u];
|
||||||
if (keysym !== undefined) {
|
if (keysym !== undefined) {
|
||||||
return keysym;
|
return keysym;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2018 The noVNC Authors
|
||||||
* Copyright (C) 2013 Samuel Mannehed for Cendio AB
|
|
||||||
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -9,52 +8,52 @@ import * as Log from '../util/logging.js';
|
||||||
import { isTouchDevice } from '../util/browser.js';
|
import { isTouchDevice } from '../util/browser.js';
|
||||||
import { setCapture, stopEvent, getPointerEvent } from '../util/events.js';
|
import { setCapture, stopEvent, getPointerEvent } from '../util/events.js';
|
||||||
|
|
||||||
var WHEEL_STEP = 10; // Delta threshold for a mouse wheel step
|
const WHEEL_STEP = 10; // Delta threshold for a mouse wheel step
|
||||||
var WHEEL_STEP_TIMEOUT = 50; // ms
|
const WHEEL_STEP_TIMEOUT = 50; // ms
|
||||||
var WHEEL_LINE_HEIGHT = 19;
|
const WHEEL_LINE_HEIGHT = 19;
|
||||||
|
|
||||||
export default function Mouse(target) {
|
export default class Mouse {
|
||||||
this._target = target || document;
|
constructor(target) {
|
||||||
|
this._target = target || document;
|
||||||
|
|
||||||
this._doubleClickTimer = null;
|
this._doubleClickTimer = null;
|
||||||
this._lastTouchPos = null;
|
this._lastTouchPos = null;
|
||||||
|
|
||||||
this._pos = null;
|
this._pos = null;
|
||||||
this._wheelStepXTimer = null;
|
this._wheelStepXTimer = null;
|
||||||
this._wheelStepYTimer = null;
|
this._wheelStepYTimer = null;
|
||||||
this._accumulatedWheelDeltaX = 0;
|
this._accumulatedWheelDeltaX = 0;
|
||||||
this._accumulatedWheelDeltaY = 0;
|
this._accumulatedWheelDeltaY = 0;
|
||||||
|
|
||||||
this._eventHandlers = {
|
this._eventHandlers = {
|
||||||
'mousedown': this._handleMouseDown.bind(this),
|
'mousedown': this._handleMouseDown.bind(this),
|
||||||
'mouseup': this._handleMouseUp.bind(this),
|
'mouseup': this._handleMouseUp.bind(this),
|
||||||
'mousemove': this._handleMouseMove.bind(this),
|
'mousemove': this._handleMouseMove.bind(this),
|
||||||
'mousewheel': this._handleMouseWheel.bind(this),
|
'mousewheel': this._handleMouseWheel.bind(this),
|
||||||
'mousedisable': this._handleMouseDisable.bind(this)
|
'mousedisable': this._handleMouseDisable.bind(this)
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
Mouse.prototype = {
|
// ===== PROPERTIES =====
|
||||||
// ===== PROPERTIES =====
|
|
||||||
|
|
||||||
touchButton: 1, // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
|
this.touchButton = 1; // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
|
||||||
|
|
||||||
// ===== EVENT HANDLERS =====
|
// ===== EVENT HANDLERS =====
|
||||||
|
|
||||||
onmousebutton: function () {}, // Handler for mouse button click/release
|
this.onmousebutton = () => {}; // Handler for mouse button click/release
|
||||||
onmousemove: function () {}, // Handler for mouse movement
|
this.onmousemove = () => {}; // Handler for mouse movement
|
||||||
|
}
|
||||||
|
|
||||||
// ===== PRIVATE METHODS =====
|
// ===== PRIVATE METHODS =====
|
||||||
|
|
||||||
_resetDoubleClickTimer: function () {
|
_resetDoubleClickTimer() {
|
||||||
this._doubleClickTimer = null;
|
this._doubleClickTimer = null;
|
||||||
},
|
}
|
||||||
|
|
||||||
_handleMouseButton: function (e, down) {
|
_handleMouseButton(e, down) {
|
||||||
this._updateMousePosition(e);
|
this._updateMousePosition(e);
|
||||||
var pos = this._pos;
|
let pos = this._pos;
|
||||||
|
|
||||||
var bmask;
|
let bmask;
|
||||||
if (e.touches || e.changedTouches) {
|
if (e.touches || e.changedTouches) {
|
||||||
// Touch device
|
// Touch device
|
||||||
|
|
||||||
|
@ -70,13 +69,13 @@ Mouse.prototype = {
|
||||||
// force the position of the latter touch to the position of
|
// force the position of the latter touch to the position of
|
||||||
// the first.
|
// the first.
|
||||||
|
|
||||||
var xs = this._lastTouchPos.x - pos.x;
|
const xs = this._lastTouchPos.x - pos.x;
|
||||||
var ys = this._lastTouchPos.y - pos.y;
|
const ys = this._lastTouchPos.y - pos.y;
|
||||||
var d = Math.sqrt((xs * xs) + (ys * ys));
|
const d = Math.sqrt((xs * xs) + (ys * ys));
|
||||||
|
|
||||||
// The goal is to trigger on a certain physical width, the
|
// The goal is to trigger on a certain physical width, the
|
||||||
// devicePixelRatio brings us a bit closer but is not optimal.
|
// devicePixelRatio brings us a bit closer but is not optimal.
|
||||||
var threshold = 20 * (window.devicePixelRatio || 1);
|
const threshold = 20 * (window.devicePixelRatio || 1);
|
||||||
if (d < threshold) {
|
if (d < threshold) {
|
||||||
pos = this._lastTouchPos;
|
pos = this._lastTouchPos;
|
||||||
}
|
}
|
||||||
|
@ -100,25 +99,25 @@ Mouse.prototype = {
|
||||||
this.onmousebutton(pos.x, pos.y, down, bmask);
|
this.onmousebutton(pos.x, pos.y, down, bmask);
|
||||||
|
|
||||||
stopEvent(e);
|
stopEvent(e);
|
||||||
},
|
}
|
||||||
|
|
||||||
_handleMouseDown: function (e) {
|
_handleMouseDown(e) {
|
||||||
// Touch events have implicit capture
|
// Touch events have implicit capture
|
||||||
if (e.type === "mousedown") {
|
if (e.type === "mousedown") {
|
||||||
setCapture(this._target);
|
setCapture(this._target);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._handleMouseButton(e, 1);
|
this._handleMouseButton(e, 1);
|
||||||
},
|
}
|
||||||
|
|
||||||
_handleMouseUp: function (e) {
|
_handleMouseUp(e) {
|
||||||
this._handleMouseButton(e, 0);
|
this._handleMouseButton(e, 0);
|
||||||
},
|
}
|
||||||
|
|
||||||
// Mouse wheel events are sent in steps over VNC. This means that the VNC
|
// 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.
|
// 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.
|
// Therefor, if we get a lot of small mouse wheel events we combine them.
|
||||||
_generateWheelStepX: function () {
|
_generateWheelStepX() {
|
||||||
|
|
||||||
if (this._accumulatedWheelDeltaX < 0) {
|
if (this._accumulatedWheelDeltaX < 0) {
|
||||||
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 5);
|
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 5);
|
||||||
|
@ -129,9 +128,9 @@ Mouse.prototype = {
|
||||||
}
|
}
|
||||||
|
|
||||||
this._accumulatedWheelDeltaX = 0;
|
this._accumulatedWheelDeltaX = 0;
|
||||||
},
|
}
|
||||||
|
|
||||||
_generateWheelStepY: function () {
|
_generateWheelStepY() {
|
||||||
|
|
||||||
if (this._accumulatedWheelDeltaY < 0) {
|
if (this._accumulatedWheelDeltaY < 0) {
|
||||||
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 3);
|
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 3);
|
||||||
|
@ -142,22 +141,22 @@ Mouse.prototype = {
|
||||||
}
|
}
|
||||||
|
|
||||||
this._accumulatedWheelDeltaY = 0;
|
this._accumulatedWheelDeltaY = 0;
|
||||||
},
|
}
|
||||||
|
|
||||||
_resetWheelStepTimers: function () {
|
_resetWheelStepTimers() {
|
||||||
window.clearTimeout(this._wheelStepXTimer);
|
window.clearTimeout(this._wheelStepXTimer);
|
||||||
window.clearTimeout(this._wheelStepYTimer);
|
window.clearTimeout(this._wheelStepYTimer);
|
||||||
this._wheelStepXTimer = null;
|
this._wheelStepXTimer = null;
|
||||||
this._wheelStepYTimer = null;
|
this._wheelStepYTimer = null;
|
||||||
},
|
}
|
||||||
|
|
||||||
_handleMouseWheel: function (e) {
|
_handleMouseWheel(e) {
|
||||||
this._resetWheelStepTimers();
|
this._resetWheelStepTimers();
|
||||||
|
|
||||||
this._updateMousePosition(e);
|
this._updateMousePosition(e);
|
||||||
|
|
||||||
var dX = e.deltaX;
|
let dX = e.deltaX;
|
||||||
var dY = e.deltaY;
|
let dY = e.deltaY;
|
||||||
|
|
||||||
// Pixel units unless it's non-zero.
|
// Pixel units unless it's non-zero.
|
||||||
// Note that if deltamode is line or page won't matter since we aren't
|
// Note that if deltamode is line or page won't matter since we aren't
|
||||||
|
@ -192,15 +191,15 @@ Mouse.prototype = {
|
||||||
}
|
}
|
||||||
|
|
||||||
stopEvent(e);
|
stopEvent(e);
|
||||||
},
|
}
|
||||||
|
|
||||||
_handleMouseMove: function (e) {
|
_handleMouseMove(e) {
|
||||||
this._updateMousePosition(e);
|
this._updateMousePosition(e);
|
||||||
this.onmousemove(this._pos.x, this._pos.y);
|
this.onmousemove(this._pos.x, this._pos.y);
|
||||||
stopEvent(e);
|
stopEvent(e);
|
||||||
},
|
}
|
||||||
|
|
||||||
_handleMouseDisable: function (e) {
|
_handleMouseDisable(e) {
|
||||||
/*
|
/*
|
||||||
* Stop propagation if inside canvas area
|
* Stop propagation if inside canvas area
|
||||||
* Note: This is only needed for the 'click' event as it fails
|
* Note: This is only needed for the 'click' event as it fails
|
||||||
|
@ -210,13 +209,14 @@ Mouse.prototype = {
|
||||||
if (e.target == this._target) {
|
if (e.target == this._target) {
|
||||||
stopEvent(e);
|
stopEvent(e);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// Update coordinates relative to target
|
// Update coordinates relative to target
|
||||||
_updateMousePosition: function(e) {
|
_updateMousePosition(e) {
|
||||||
e = getPointerEvent(e);
|
e = getPointerEvent(e);
|
||||||
var bounds = this._target.getBoundingClientRect();
|
const bounds = this._target.getBoundingClientRect();
|
||||||
var x, y;
|
let x;
|
||||||
|
let y;
|
||||||
// Clip to target bounds
|
// Clip to target bounds
|
||||||
if (e.clientX < bounds.left) {
|
if (e.clientX < bounds.left) {
|
||||||
x = 0;
|
x = 0;
|
||||||
|
@ -232,49 +232,45 @@ Mouse.prototype = {
|
||||||
} else {
|
} else {
|
||||||
y = e.clientY - bounds.top;
|
y = e.clientY - bounds.top;
|
||||||
}
|
}
|
||||||
this._pos = {x:x, y:y};
|
this._pos = {x: x, y: y};
|
||||||
},
|
}
|
||||||
|
|
||||||
// ===== PUBLIC METHODS =====
|
// ===== PUBLIC METHODS =====
|
||||||
|
|
||||||
grab: function () {
|
grab() {
|
||||||
var c = this._target;
|
|
||||||
|
|
||||||
if (isTouchDevice) {
|
if (isTouchDevice) {
|
||||||
c.addEventListener('touchstart', this._eventHandlers.mousedown);
|
this._target.addEventListener('touchstart', this._eventHandlers.mousedown);
|
||||||
c.addEventListener('touchend', this._eventHandlers.mouseup);
|
this._target.addEventListener('touchend', this._eventHandlers.mouseup);
|
||||||
c.addEventListener('touchmove', this._eventHandlers.mousemove);
|
this._target.addEventListener('touchmove', this._eventHandlers.mousemove);
|
||||||
}
|
}
|
||||||
c.addEventListener('mousedown', this._eventHandlers.mousedown);
|
this._target.addEventListener('mousedown', this._eventHandlers.mousedown);
|
||||||
c.addEventListener('mouseup', this._eventHandlers.mouseup);
|
this._target.addEventListener('mouseup', this._eventHandlers.mouseup);
|
||||||
c.addEventListener('mousemove', this._eventHandlers.mousemove);
|
this._target.addEventListener('mousemove', this._eventHandlers.mousemove);
|
||||||
c.addEventListener('wheel', this._eventHandlers.mousewheel);
|
this._target.addEventListener('wheel', this._eventHandlers.mousewheel);
|
||||||
|
|
||||||
/* Prevent middle-click pasting (see above for why we bind to document) */
|
/* Prevent middle-click pasting (see above for why we bind to document) */
|
||||||
document.addEventListener('click', this._eventHandlers.mousedisable);
|
document.addEventListener('click', this._eventHandlers.mousedisable);
|
||||||
|
|
||||||
/* preventDefault() on mousedown doesn't stop this event for some
|
/* preventDefault() on mousedown doesn't stop this event for some
|
||||||
reason so we have to explicitly block it */
|
reason so we have to explicitly block it */
|
||||||
c.addEventListener('contextmenu', this._eventHandlers.mousedisable);
|
this._target.addEventListener('contextmenu', this._eventHandlers.mousedisable);
|
||||||
},
|
}
|
||||||
|
|
||||||
ungrab: function () {
|
|
||||||
var c = this._target;
|
|
||||||
|
|
||||||
|
ungrab() {
|
||||||
this._resetWheelStepTimers();
|
this._resetWheelStepTimers();
|
||||||
|
|
||||||
if (isTouchDevice) {
|
if (isTouchDevice) {
|
||||||
c.removeEventListener('touchstart', this._eventHandlers.mousedown);
|
this._target.removeEventListener('touchstart', this._eventHandlers.mousedown);
|
||||||
c.removeEventListener('touchend', this._eventHandlers.mouseup);
|
this._target.removeEventListener('touchend', this._eventHandlers.mouseup);
|
||||||
c.removeEventListener('touchmove', this._eventHandlers.mousemove);
|
this._target.removeEventListener('touchmove', this._eventHandlers.mousemove);
|
||||||
}
|
}
|
||||||
c.removeEventListener('mousedown', this._eventHandlers.mousedown);
|
this._target.removeEventListener('mousedown', this._eventHandlers.mousedown);
|
||||||
c.removeEventListener('mouseup', this._eventHandlers.mouseup);
|
this._target.removeEventListener('mouseup', this._eventHandlers.mouseup);
|
||||||
c.removeEventListener('mousemove', this._eventHandlers.mousemove);
|
this._target.removeEventListener('mousemove', this._eventHandlers.mousemove);
|
||||||
c.removeEventListener('wheel', this._eventHandlers.mousewheel);
|
this._target.removeEventListener('wheel', this._eventHandlers.mousewheel);
|
||||||
|
|
||||||
document.removeEventListener('click', this._eventHandlers.mousedisable);
|
document.removeEventListener('click', this._eventHandlers.mousedisable);
|
||||||
|
|
||||||
c.removeEventListener('contextmenu', this._eventHandlers.mousedisable);
|
this._target.removeEventListener('contextmenu', this._eventHandlers.mousedisable);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import KeyTable from "./keysym.js";
|
|
||||||
import keysyms from "./keysymdef.js";
|
import keysyms from "./keysymdef.js";
|
||||||
import vkeys from "./vkeys.js";
|
import vkeys from "./vkeys.js";
|
||||||
import fixedkeys from "./fixedkeys.js";
|
import fixedkeys from "./fixedkeys.js";
|
||||||
|
@ -6,7 +5,7 @@ import DOMKeyTable from "./domkeytable.js";
|
||||||
import * as browser from "../util/browser.js";
|
import * as browser from "../util/browser.js";
|
||||||
|
|
||||||
// Get 'KeyboardEvent.code', handling legacy browsers
|
// Get 'KeyboardEvent.code', handling legacy browsers
|
||||||
export function getKeycode(evt){
|
export function getKeycode(evt) {
|
||||||
// Are we getting proper key identifiers?
|
// Are we getting proper key identifiers?
|
||||||
// (unfortunately Firefox and Chrome are crappy here and gives
|
// (unfortunately Firefox and Chrome are crappy here and gives
|
||||||
// us an empty string on some platforms, rather than leaving it
|
// us an empty string on some platforms, rather than leaving it
|
||||||
|
@ -25,7 +24,7 @@ export function getKeycode(evt){
|
||||||
// in the 'keyCode' field for non-printable characters. However
|
// in the 'keyCode' field for non-printable characters. However
|
||||||
// Webkit sets it to the same as charCode in 'keypress' events.
|
// Webkit sets it to the same as charCode in 'keypress' events.
|
||||||
if ((evt.type !== 'keypress') && (evt.keyCode in vkeys)) {
|
if ((evt.type !== 'keypress') && (evt.keyCode in vkeys)) {
|
||||||
var code = vkeys[evt.keyCode];
|
let code = vkeys[evt.keyCode];
|
||||||
|
|
||||||
// macOS has messed up this code for some reason
|
// macOS has messed up this code for some reason
|
||||||
if (browser.isMac() && (code === 'ContextMenu')) {
|
if (browser.isMac() && (code === 'ContextMenu')) {
|
||||||
|
@ -111,7 +110,7 @@ export function getKey(evt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to deduce it based on the physical key
|
// Try to deduce it based on the physical key
|
||||||
var code = getKeycode(evt);
|
const code = getKeycode(evt);
|
||||||
if (code in fixedkeys) {
|
if (code in fixedkeys) {
|
||||||
return fixedkeys[code];
|
return fixedkeys[code];
|
||||||
}
|
}
|
||||||
|
@ -126,8 +125,8 @@ export function getKey(evt) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the most reliable keysym value we can get from a key event
|
// Get the most reliable keysym value we can get from a key event
|
||||||
export function getKeysym(evt){
|
export function getKeysym(evt) {
|
||||||
var key = getKey(evt);
|
const key = getKey(evt);
|
||||||
|
|
||||||
if (key === 'Unidentified') {
|
if (key === 'Unidentified') {
|
||||||
return null;
|
return null;
|
||||||
|
@ -135,7 +134,7 @@ export function getKeysym(evt){
|
||||||
|
|
||||||
// First look up special keys
|
// First look up special keys
|
||||||
if (key in DOMKeyTable) {
|
if (key in DOMKeyTable) {
|
||||||
var location = evt.location;
|
let location = evt.location;
|
||||||
|
|
||||||
// Safari screws up location for the right cmd key
|
// Safari screws up location for the right cmd key
|
||||||
if ((key === 'Meta') && (location === 0)) {
|
if ((key === 'Meta') && (location === 0)) {
|
||||||
|
@ -151,14 +150,12 @@ export function getKeysym(evt){
|
||||||
|
|
||||||
// Now we need to look at the Unicode symbol instead
|
// Now we need to look at the Unicode symbol instead
|
||||||
|
|
||||||
var codepoint;
|
|
||||||
|
|
||||||
// Special key? (FIXME: Should have been caught earlier)
|
// Special key? (FIXME: Should have been caught earlier)
|
||||||
if (key.length !== 1) {
|
if (key.length !== 1) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
codepoint = key.charCodeAt();
|
const codepoint = key.charCodeAt();
|
||||||
if (codepoint) {
|
if (codepoint) {
|
||||||
return keysyms.lookup(codepoint);
|
return keysyms.lookup(codepoint);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2017 Pierre Ossman for Cendio AB
|
* Copyright (C) 2018 The noVNC Authors
|
||||||
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ export default {
|
||||||
0x08: 'Backspace',
|
0x08: 'Backspace',
|
||||||
0x09: 'Tab',
|
0x09: 'Tab',
|
||||||
0x0a: 'NumpadClear',
|
0x0a: 'NumpadClear',
|
||||||
|
0x0c: 'Numpad5', // IE11 sends evt.keyCode: 12 when numlock is off
|
||||||
0x0d: 'Enter',
|
0x0d: 'Enter',
|
||||||
0x10: 'ShiftLeft',
|
0x10: 'ShiftLeft',
|
||||||
0x11: 'ControlLeft',
|
0x11: 'ControlLeft',
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2018 The noVNC Authors
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
|
@ -9,7 +9,7 @@
|
||||||
import * as Log from './logging.js';
|
import * as Log from './logging.js';
|
||||||
|
|
||||||
// Touch detection
|
// Touch detection
|
||||||
export var isTouchDevice = ('ontouchstart' in document.documentElement) ||
|
export let isTouchDevice = ('ontouchstart' in document.documentElement) ||
|
||||||
// requried for Chrome debugger
|
// requried for Chrome debugger
|
||||||
(document.ontouchstart !== undefined) ||
|
(document.ontouchstart !== undefined) ||
|
||||||
// required for MS Surface
|
// required for MS Surface
|
||||||
|
@ -20,42 +20,42 @@ window.addEventListener('touchstart', function onFirstTouch() {
|
||||||
window.removeEventListener('touchstart', onFirstTouch, false);
|
window.removeEventListener('touchstart', onFirstTouch, false);
|
||||||
}, false);
|
}, false);
|
||||||
|
|
||||||
var _cursor_uris_supported = null;
|
|
||||||
|
|
||||||
export function supportsCursorURIs () {
|
// The goal is to find a certain physical width, the devicePixelRatio
|
||||||
if (_cursor_uris_supported === null) {
|
// brings us a bit closer but is not optimal.
|
||||||
try {
|
export let dragThreshold = 10 * (window.devicePixelRatio || 1);
|
||||||
var target = document.createElement('canvas');
|
|
||||||
target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
|
|
||||||
|
|
||||||
if (target.style.cursor) {
|
let _supportsCursorURIs = false;
|
||||||
Log.Info("Data URI scheme cursor supported");
|
|
||||||
_cursor_uris_supported = true;
|
try {
|
||||||
} else {
|
const target = document.createElement('canvas');
|
||||||
Log.Warn("Data URI scheme cursor not supported");
|
target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
|
||||||
_cursor_uris_supported = false;
|
|
||||||
}
|
if (target.style.cursor) {
|
||||||
} catch (exc) {
|
Log.Info("Data URI scheme cursor supported");
|
||||||
Log.Error("Data URI scheme cursor test exception: " + exc);
|
_supportsCursorURIs = true;
|
||||||
_cursor_uris_supported = false;
|
} else {
|
||||||
}
|
Log.Warn("Data URI scheme cursor not supported");
|
||||||
}
|
}
|
||||||
|
} catch (exc) {
|
||||||
|
Log.Error("Data URI scheme cursor test exception: " + exc);
|
||||||
|
}
|
||||||
|
|
||||||
return _cursor_uris_supported;
|
export const supportsCursorURIs = _supportsCursorURIs;
|
||||||
};
|
|
||||||
|
let _supportsImageMetadata = false;
|
||||||
|
try {
|
||||||
|
new ImageData(new Uint8ClampedArray(4), 1, 1);
|
||||||
|
_supportsImageMetadata = true;
|
||||||
|
} catch (ex) {
|
||||||
|
// ignore failure
|
||||||
|
}
|
||||||
|
export const supportsImageMetadata = _supportsImageMetadata;
|
||||||
|
|
||||||
export function isMac() {
|
export function isMac() {
|
||||||
return navigator && !!(/mac/i).exec(navigator.platform);
|
return navigator && !!(/mac/i).exec(navigator.platform);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isIE() {
|
|
||||||
return navigator && !!(/trident/i).exec(navigator.userAgent);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isEdge() {
|
|
||||||
return navigator && !!(/edge/i).exec(navigator.userAgent);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isWindows() {
|
export function isWindows() {
|
||||||
return navigator && !!(/win/i).exec(navigator.platform);
|
return navigator && !!(/win/i).exec(navigator.platform);
|
||||||
}
|
}
|
||||||
|
@ -67,3 +67,24 @@ export function isIOS() {
|
||||||
!!(/ipod/i).exec(navigator.platform));
|
!!(/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);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isIE() {
|
||||||
|
return navigator && !!(/trident/i).exec(navigator.userAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isEdge() {
|
||||||
|
return navigator && !!(/edge/i).exec(navigator.userAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isFirefox() {
|
||||||
|
return navigator && !!(/firefox/i).exec(navigator.userAgent);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
221
static/js/novnc/core/util/cursor.js
Executable file
221
static/js/novnc/core/util/cursor.js
Executable file
|
@ -0,0 +1,221 @@
|
||||||
|
/*
|
||||||
|
* noVNC: HTML5 VNC client
|
||||||
|
* Copyright (C) 2018 The noVNC Authors
|
||||||
|
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { supportsCursorURIs, isTouchDevice } from './browser.js';
|
||||||
|
|
||||||
|
const useFallback = !supportsCursorURIs || isTouchDevice;
|
||||||
|
|
||||||
|
export default class Cursor {
|
||||||
|
constructor() {
|
||||||
|
this._target = null;
|
||||||
|
|
||||||
|
this._canvas = document.createElement('canvas');
|
||||||
|
|
||||||
|
if (useFallback) {
|
||||||
|
this._canvas.style.position = 'fixed';
|
||||||
|
this._canvas.style.zIndex = '65535';
|
||||||
|
this._canvas.style.pointerEvents = 'none';
|
||||||
|
// Can't use "display" because of Firefox bug #1445997
|
||||||
|
this._canvas.style.visibility = 'hidden';
|
||||||
|
document.body.appendChild(this._canvas);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._position = { x: 0, y: 0 };
|
||||||
|
this._hotSpot = { x: 0, y: 0 };
|
||||||
|
|
||||||
|
this._eventHandlers = {
|
||||||
|
'mouseover': this._handleMouseOver.bind(this),
|
||||||
|
'mouseleave': this._handleMouseLeave.bind(this),
|
||||||
|
'mousemove': this._handleMouseMove.bind(this),
|
||||||
|
'mouseup': this._handleMouseUp.bind(this),
|
||||||
|
'touchstart': this._handleTouchStart.bind(this),
|
||||||
|
'touchmove': this._handleTouchMove.bind(this),
|
||||||
|
'touchend': this._handleTouchEnd.bind(this),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
attach(target) {
|
||||||
|
if (this._target) {
|
||||||
|
this.detach();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._target = target;
|
||||||
|
|
||||||
|
if (useFallback) {
|
||||||
|
// FIXME: These don't fire properly except for mouse
|
||||||
|
/// movement in IE. We want to also capture element
|
||||||
|
// movement, size changes, visibility, etc.
|
||||||
|
const options = { capture: true, passive: true };
|
||||||
|
this._target.addEventListener('mouseover', this._eventHandlers.mouseover, options);
|
||||||
|
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 (useFallback) {
|
||||||
|
const options = { capture: true, passive: true };
|
||||||
|
this._target.removeEventListener('mouseover', this._eventHandlers.mouseover, options);
|
||||||
|
this._target.removeEventListener('mouseleave', this._eventHandlers.mouseleave, options);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
this._target = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
change(rgba, hotx, hoty, w, h) {
|
||||||
|
if ((w === 0) || (h === 0)) {
|
||||||
|
this.clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._position.x = this._position.x + this._hotSpot.x - hotx;
|
||||||
|
this._position.y = this._position.y + this._hotSpot.y - hoty;
|
||||||
|
this._hotSpot.x = hotx;
|
||||||
|
this._hotSpot.y = hoty;
|
||||||
|
|
||||||
|
let ctx = this._canvas.getContext('2d');
|
||||||
|
|
||||||
|
this._canvas.width = w;
|
||||||
|
this._canvas.height = h;
|
||||||
|
|
||||||
|
let img;
|
||||||
|
try {
|
||||||
|
// IE doesn't support this
|
||||||
|
img = new ImageData(new Uint8ClampedArray(rgba), w, h);
|
||||||
|
} catch (ex) {
|
||||||
|
img = ctx.createImageData(w, h);
|
||||||
|
img.data.set(new Uint8ClampedArray(rgba));
|
||||||
|
}
|
||||||
|
ctx.clearRect(0, 0, w, h);
|
||||||
|
ctx.putImageData(img, 0, 0);
|
||||||
|
|
||||||
|
if (useFallback) {
|
||||||
|
this._updatePosition();
|
||||||
|
} else {
|
||||||
|
let url = this._canvas.toDataURL();
|
||||||
|
this._target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this._target.style.cursor = 'none';
|
||||||
|
this._canvas.width = 0;
|
||||||
|
this._canvas.height = 0;
|
||||||
|
this._position.x = this._position.x + this._hotSpot.x;
|
||||||
|
this._position.y = this._position.y + this._hotSpot.y;
|
||||||
|
this._hotSpot.x = 0;
|
||||||
|
this._hotSpot.y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleMouseOver(event) {
|
||||||
|
// This event could be because we're entering the target, or
|
||||||
|
// moving around amongst its sub elements. Let the move handler
|
||||||
|
// sort things out.
|
||||||
|
this._handleMouseMove(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleMouseLeave(event) {
|
||||||
|
this._hideCursor();
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleMouseMove(event) {
|
||||||
|
this._updateVisibility(event.target);
|
||||||
|
|
||||||
|
this._position.x = event.clientX - this._hotSpot.x;
|
||||||
|
this._position.y = event.clientY - this._hotSpot.y;
|
||||||
|
|
||||||
|
this._updatePosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleMouseUp(event) {
|
||||||
|
// We might get this event because of a drag operation that
|
||||||
|
// moved outside of the target. Check what's under the cursor
|
||||||
|
// now and adjust visibility based on that.
|
||||||
|
let target = document.elementFromPoint(event.clientX, event.clientY);
|
||||||
|
this._updateVisibility(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
_handleTouchStart(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
_showCursor() {
|
||||||
|
if (this._canvas.style.visibility === 'hidden') {
|
||||||
|
this._canvas.style.visibility = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_hideCursor() {
|
||||||
|
if (this._canvas.style.visibility !== 'hidden') {
|
||||||
|
this._canvas.style.visibility = 'hidden';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Should we currently display the cursor?
|
||||||
|
// (i.e. are we over the target, or a child of the target without a
|
||||||
|
// different cursor set)
|
||||||
|
_shouldShowCursor(target) {
|
||||||
|
// Easy case
|
||||||
|
if (target === this._target) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Other part of the DOM?
|
||||||
|
if (!this._target.contains(target)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Has the child its own cursor?
|
||||||
|
// FIXME: How can we tell that a sub element has an
|
||||||
|
// explicit "cursor: none;"?
|
||||||
|
if (window.getComputedStyle(target).cursor !== 'none') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_updateVisibility(target) {
|
||||||
|
if (this._shouldShowCursor(target)) {
|
||||||
|
this._showCursor();
|
||||||
|
} else {
|
||||||
|
this._hideCursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_updatePosition() {
|
||||||
|
this._canvas.style.left = this._position.x + "px";
|
||||||
|
this._canvas.style.top = this._position.y + "px";
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2018 The noVNC Authors
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
|
@ -10,24 +10,24 @@
|
||||||
* Cross-browser event and position routines
|
* Cross-browser event and position routines
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function getPointerEvent (e) {
|
export function getPointerEvent(e) {
|
||||||
return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e;
|
return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e;
|
||||||
};
|
}
|
||||||
|
|
||||||
export function stopEvent (e) {
|
export function stopEvent(e) {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
};
|
}
|
||||||
|
|
||||||
// Emulate Element.setCapture() when not supported
|
// Emulate Element.setCapture() when not supported
|
||||||
var _captureRecursion = false;
|
let _captureRecursion = false;
|
||||||
var _captureElem = null;
|
let _captureElem = null;
|
||||||
function _captureProxy(e) {
|
function _captureProxy(e) {
|
||||||
// Recursion protection as we'll see our own event
|
// Recursion protection as we'll see our own event
|
||||||
if (_captureRecursion) return;
|
if (_captureRecursion) return;
|
||||||
|
|
||||||
// Clone the event as we cannot dispatch an already dispatched event
|
// Clone the event as we cannot dispatch an already dispatched event
|
||||||
var newEv = new e.constructor(e.type, e);
|
const newEv = new e.constructor(e.type, e);
|
||||||
|
|
||||||
_captureRecursion = true;
|
_captureRecursion = true;
|
||||||
_captureElem.dispatchEvent(newEv);
|
_captureElem.dispatchEvent(newEv);
|
||||||
|
@ -45,18 +45,19 @@ function _captureProxy(e) {
|
||||||
if (e.type === "mouseup") {
|
if (e.type === "mouseup") {
|
||||||
releaseCapture();
|
releaseCapture();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// Follow cursor style of target element
|
// Follow cursor style of target element
|
||||||
function _captureElemChanged() {
|
function _captureElemChanged() {
|
||||||
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
const captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||||
captureElem.style.cursor = window.getComputedStyle(_captureElem).cursor;
|
captureElem.style.cursor = window.getComputedStyle(_captureElem).cursor;
|
||||||
};
|
}
|
||||||
var _captureObserver = new MutationObserver(_captureElemChanged);
|
|
||||||
|
|
||||||
var _captureIndex = 0;
|
const _captureObserver = new MutationObserver(_captureElemChanged);
|
||||||
|
|
||||||
export function setCapture (elem) {
|
let _captureIndex = 0;
|
||||||
|
|
||||||
|
export function setCapture(elem) {
|
||||||
if (elem.setCapture) {
|
if (elem.setCapture) {
|
||||||
|
|
||||||
elem.setCapture();
|
elem.setCapture();
|
||||||
|
@ -69,7 +70,7 @@ export function setCapture (elem) {
|
||||||
// called multiple times without coordination
|
// called multiple times without coordination
|
||||||
releaseCapture();
|
releaseCapture();
|
||||||
|
|
||||||
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
let captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||||
|
|
||||||
if (captureElem === null) {
|
if (captureElem === null) {
|
||||||
captureElem = document.createElement("div");
|
captureElem = document.createElement("div");
|
||||||
|
@ -95,7 +96,7 @@ export function setCapture (elem) {
|
||||||
_captureIndex++;
|
_captureIndex++;
|
||||||
|
|
||||||
// Track cursor and get initial cursor
|
// Track cursor and get initial cursor
|
||||||
_captureObserver.observe(elem, {attributes:true});
|
_captureObserver.observe(elem, {attributes: true});
|
||||||
_captureElemChanged();
|
_captureElemChanged();
|
||||||
|
|
||||||
captureElem.style.display = "";
|
captureElem.style.display = "";
|
||||||
|
@ -105,9 +106,9 @@ export function setCapture (elem) {
|
||||||
window.addEventListener('mousemove', _captureProxy);
|
window.addEventListener('mousemove', _captureProxy);
|
||||||
window.addEventListener('mouseup', _captureProxy);
|
window.addEventListener('mouseup', _captureProxy);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export function releaseCapture () {
|
export function releaseCapture() {
|
||||||
if (document.releaseCapture) {
|
if (document.releaseCapture) {
|
||||||
|
|
||||||
document.releaseCapture();
|
document.releaseCapture();
|
||||||
|
@ -119,7 +120,7 @@ export function releaseCapture () {
|
||||||
|
|
||||||
// There might be events already queued, so we need to wait for
|
// There might be events already queued, so we need to wait for
|
||||||
// them to flush. E.g. contextmenu in Microsoft Edge
|
// them to flush. E.g. contextmenu in Microsoft Edge
|
||||||
window.setTimeout(function(expected) {
|
window.setTimeout((expected) => {
|
||||||
// Only clear it if it's the expected grab (i.e. no one
|
// Only clear it if it's the expected grab (i.e. no one
|
||||||
// else has initiated a new grab)
|
// else has initiated a new grab)
|
||||||
if (_captureIndex === expected) {
|
if (_captureIndex === expected) {
|
||||||
|
@ -129,10 +130,10 @@ export function releaseCapture () {
|
||||||
|
|
||||||
_captureObserver.disconnect();
|
_captureObserver.disconnect();
|
||||||
|
|
||||||
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
const captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||||
captureElem.style.display = "none";
|
captureElem.style.display = "none";
|
||||||
|
|
||||||
window.removeEventListener('mousemove', _captureProxy);
|
window.removeEventListener('mousemove', _captureProxy);
|
||||||
window.removeEventListener('mouseup', _captureProxy);
|
window.removeEventListener('mouseup', _captureProxy);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,40 +1,35 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright 2017 Pierre Ossman for Cendio AB
|
* Copyright (C) 2018 The noVNC Authors
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var EventTargetMixin = {
|
export default class EventTargetMixin {
|
||||||
_listeners: null,
|
constructor() {
|
||||||
|
this._listeners = new Map();
|
||||||
|
}
|
||||||
|
|
||||||
addEventListener: function(type, callback) {
|
addEventListener(type, callback) {
|
||||||
if (!this._listeners) {
|
if (!this._listeners.has(type)) {
|
||||||
this._listeners = new Map();
|
this._listeners.set(type, new Set());
|
||||||
}
|
}
|
||||||
if (!this._listeners.has(type)) {
|
this._listeners.get(type).add(callback);
|
||||||
this._listeners.set(type, new Set());
|
}
|
||||||
}
|
|
||||||
this._listeners.get(type).add(callback);
|
|
||||||
},
|
|
||||||
|
|
||||||
removeEventListener: function(type, callback) {
|
removeEventListener(type, callback) {
|
||||||
if (!this._listeners || !this._listeners.has(type)) {
|
if (this._listeners.has(type)) {
|
||||||
return;
|
this._listeners.get(type).delete(callback);
|
||||||
}
|
}
|
||||||
this._listeners.get(type).delete(callback);
|
}
|
||||||
},
|
|
||||||
|
|
||||||
dispatchEvent: function(event) {
|
dispatchEvent(event) {
|
||||||
if (!this._listeners || !this._listeners.has(event.type)) {
|
if (!this._listeners.has(event.type)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
this._listeners.get(event.type).forEach(function (callback) {
|
this._listeners.get(event.type)
|
||||||
callback.call(this, event);
|
.forEach(callback => callback.call(this, event));
|
||||||
}, this);
|
return !event.defaultPrevented;
|
||||||
return !event.defaultPrevented;
|
}
|
||||||
},
|
}
|
||||||
};
|
|
||||||
|
|
||||||
export default EventTargetMixin;
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2018 The noVNC Authors
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
|
@ -10,22 +10,24 @@
|
||||||
* Logging/debug routines
|
* Logging/debug routines
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var _log_level = 'warn';
|
let _log_level = 'warn';
|
||||||
|
|
||||||
var Debug = function (msg) {};
|
let Debug = () => {};
|
||||||
var Info = function (msg) {};
|
let Info = () => {};
|
||||||
var Warn = function (msg) {};
|
let Warn = () => {};
|
||||||
var Error = function (msg) {};
|
let Error = () => {};
|
||||||
|
|
||||||
export function init_logging (level) {
|
export function init_logging(level) {
|
||||||
if (typeof level === 'undefined') {
|
if (typeof level === 'undefined') {
|
||||||
level = _log_level;
|
level = _log_level;
|
||||||
} else {
|
} else {
|
||||||
_log_level = level;
|
_log_level = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug = Info = Warn = Error = function (msg) {};
|
Debug = Info = Warn = Error = () => {};
|
||||||
|
|
||||||
if (typeof window.console !== "undefined") {
|
if (typeof window.console !== "undefined") {
|
||||||
|
/* eslint-disable no-console, no-fallthrough */
|
||||||
switch (level) {
|
switch (level) {
|
||||||
case 'debug':
|
case 'debug':
|
||||||
Debug = console.debug.bind(window.console);
|
Debug = console.debug.bind(window.console);
|
||||||
|
@ -38,13 +40,16 @@ export function init_logging (level) {
|
||||||
case 'none':
|
case 'none':
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Error("invalid logging type '" + level + "'");
|
throw new window.Error("invalid logging type '" + level + "'");
|
||||||
}
|
}
|
||||||
|
/* eslint-enable no-console, no-fallthrough */
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
export function get_logging () {
|
|
||||||
|
export function get_logging() {
|
||||||
return _log_level;
|
return _log_level;
|
||||||
};
|
}
|
||||||
|
|
||||||
export { Debug, Info, Warn, Error };
|
export { Debug, Info, Warn, Error };
|
||||||
|
|
||||||
// Initialize logging level
|
// Initialize logging level
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright 2017 Pierre Ossman for noVNC
|
* Copyright (C) 2018 The noVNC Authors
|
||||||
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -16,13 +16,13 @@ if (typeof Object.assign != 'function') {
|
||||||
throw new TypeError('Cannot convert undefined or null to object');
|
throw new TypeError('Cannot convert undefined or null to object');
|
||||||
}
|
}
|
||||||
|
|
||||||
var to = Object(target);
|
const to = Object(target);
|
||||||
|
|
||||||
for (var index = 1; index < arguments.length; index++) {
|
for (let index = 1; index < arguments.length; index++) {
|
||||||
var nextSource = arguments[index];
|
const nextSource = arguments[index];
|
||||||
|
|
||||||
if (nextSource != null) { // Skip over if undefined or null
|
if (nextSource != null) { // Skip over if undefined or null
|
||||||
for (var nextKey in nextSource) {
|
for (let nextKey in nextSource) {
|
||||||
// Avoid bugs when hasOwnProperty is shadowed
|
// Avoid bugs when hasOwnProperty is shadowed
|
||||||
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
|
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
|
||||||
to[nextKey] = nextSource[nextKey];
|
to[nextKey] = nextSource[nextKey];
|
||||||
|
@ -38,10 +38,10 @@ if (typeof Object.assign != 'function') {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CustomEvent constructor (taken from MDN) */
|
/* CustomEvent constructor (taken from MDN) */
|
||||||
(function () {
|
(() => {
|
||||||
function CustomEvent ( event, params ) {
|
function CustomEvent(event, params) {
|
||||||
params = params || { bubbles: false, cancelable: false, detail: undefined };
|
params = params || { bubbles: false, cancelable: false, detail: undefined };
|
||||||
var evt = document.createEvent( 'CustomEvent' );
|
const evt = document.createEvent( 'CustomEvent' );
|
||||||
evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
|
evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
|
||||||
return evt;
|
return evt;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2018 The noVNC Authors
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
|
@ -9,7 +9,6 @@
|
||||||
/*
|
/*
|
||||||
* Decode from UTF-8
|
* Decode from UTF-8
|
||||||
*/
|
*/
|
||||||
export function decodeUTF8 (utf8string) {
|
export function decodeUTF8(utf8string) {
|
||||||
"use strict";
|
|
||||||
return decodeURIComponent(escape(utf8string));
|
return decodeURIComponent(escape(utf8string));
|
||||||
};
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Websock: high-performance binary WebSockets
|
* Websock: high-performance binary WebSockets
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2018 The noVNC Authors
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* Websock is similar to the standard WebSocket object but with extra
|
* Websock is similar to the standard WebSocket object but with extra
|
||||||
|
@ -14,143 +14,120 @@
|
||||||
|
|
||||||
import * as Log from './util/logging.js';
|
import * as Log from './util/logging.js';
|
||||||
|
|
||||||
export default function Websock() {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
this._websocket = null; // WebSocket object
|
|
||||||
|
|
||||||
this._rQi = 0; // Receive queue index
|
|
||||||
this._rQlen = 0; // Next write position in the receive queue
|
|
||||||
this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
|
|
||||||
this._rQmax = this._rQbufferSize / 8;
|
|
||||||
// called in init: this._rQ = new Uint8Array(this._rQbufferSize);
|
|
||||||
this._rQ = null; // Receive queue
|
|
||||||
|
|
||||||
this._sQbufferSize = 1024 * 10; // 10 KiB
|
|
||||||
// called in init: this._sQ = new Uint8Array(this._sQbufferSize);
|
|
||||||
this._sQlen = 0;
|
|
||||||
this._sQ = null; // Send queue
|
|
||||||
|
|
||||||
this._eventHandlers = {
|
|
||||||
'message': function () {},
|
|
||||||
'open': function () {},
|
|
||||||
'close': function () {},
|
|
||||||
'error': function () {}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// this has performance issues in some versions Chromium, and
|
// this has performance issues in some versions Chromium, and
|
||||||
// doesn't gain a tremendous amount of performance increase in Firefox
|
// 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.
|
// at the moment. It may be valuable to turn it on in the future.
|
||||||
var ENABLE_COPYWITHIN = false;
|
const ENABLE_COPYWITHIN = false;
|
||||||
|
const MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
|
||||||
|
|
||||||
var MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
|
export default class Websock {
|
||||||
|
constructor() {
|
||||||
|
this._websocket = null; // WebSocket object
|
||||||
|
|
||||||
var typedArrayToString = (function () {
|
this._rQi = 0; // Receive queue index
|
||||||
// This is only for PhantomJS, which doesn't like apply-ing
|
this._rQlen = 0; // Next write position in the receive queue
|
||||||
// with Typed Arrays
|
this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
|
||||||
try {
|
this._rQmax = this._rQbufferSize / 8;
|
||||||
var arr = new Uint8Array([1, 2, 3]);
|
// called in init: this._rQ = new Uint8Array(this._rQbufferSize);
|
||||||
String.fromCharCode.apply(null, arr);
|
this._rQ = null; // Receive queue
|
||||||
return function (a) { return String.fromCharCode.apply(null, a); };
|
|
||||||
} catch (ex) {
|
this._sQbufferSize = 1024 * 10; // 10 KiB
|
||||||
return function (a) {
|
// called in init: this._sQ = new Uint8Array(this._sQbufferSize);
|
||||||
return String.fromCharCode.apply(
|
this._sQlen = 0;
|
||||||
null, Array.prototype.slice.call(a));
|
this._sQ = null; // Send queue
|
||||||
|
|
||||||
|
this._eventHandlers = {
|
||||||
|
message: () => {},
|
||||||
|
open: () => {},
|
||||||
|
close: () => {},
|
||||||
|
error: () => {}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
})();
|
|
||||||
|
|
||||||
Websock.prototype = {
|
|
||||||
// Getters and Setters
|
// Getters and Setters
|
||||||
get_sQ: function () {
|
get sQ() {
|
||||||
return this._sQ;
|
return this._sQ;
|
||||||
},
|
}
|
||||||
|
|
||||||
get_rQ: function () {
|
get rQ() {
|
||||||
return this._rQ;
|
return this._rQ;
|
||||||
},
|
}
|
||||||
|
|
||||||
get_rQi: function () {
|
get rQi() {
|
||||||
return this._rQi;
|
return this._rQi;
|
||||||
},
|
}
|
||||||
|
|
||||||
set_rQi: function (val) {
|
set rQi(val) {
|
||||||
this._rQi = val;
|
this._rQi = val;
|
||||||
},
|
}
|
||||||
|
|
||||||
// Receive Queue
|
// Receive Queue
|
||||||
rQlen: function () {
|
get rQlen() {
|
||||||
return this._rQlen - this._rQi;
|
return this._rQlen - this._rQi;
|
||||||
},
|
}
|
||||||
|
|
||||||
rQpeek8: function () {
|
rQpeek8() {
|
||||||
return this._rQ[this._rQi];
|
return this._rQ[this._rQi];
|
||||||
},
|
}
|
||||||
|
|
||||||
rQshift8: function () {
|
rQskipBytes(bytes) {
|
||||||
return this._rQ[this._rQi++];
|
this._rQi += bytes;
|
||||||
},
|
}
|
||||||
|
|
||||||
rQskip8: function () {
|
rQshift8() {
|
||||||
this._rQi++;
|
return this._rQshift(1);
|
||||||
},
|
}
|
||||||
|
|
||||||
rQskipBytes: function (num) {
|
rQshift16() {
|
||||||
this._rQi += num;
|
return this._rQshift(2);
|
||||||
},
|
}
|
||||||
|
|
||||||
|
rQshift32() {
|
||||||
|
return this._rQshift(4);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO(directxman12): test performance with these vs a DataView
|
// TODO(directxman12): test performance with these vs a DataView
|
||||||
rQshift16: function () {
|
_rQshift(bytes) {
|
||||||
return (this._rQ[this._rQi++] << 8) +
|
let res = 0;
|
||||||
this._rQ[this._rQi++];
|
for (let byte = bytes - 1; byte >= 0; byte--) {
|
||||||
},
|
res += this._rQ[this._rQi++] << (byte * 8);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
rQshift32: function () {
|
rQshiftStr(len) {
|
||||||
return (this._rQ[this._rQi++] << 24) +
|
if (typeof(len) === 'undefined') { len = this.rQlen; }
|
||||||
(this._rQ[this._rQi++] << 16) +
|
let str = "";
|
||||||
(this._rQ[this._rQi++] << 8) +
|
// Handle large arrays in steps to avoid long strings on the stack
|
||||||
this._rQ[this._rQi++];
|
for (let i = 0; i < len; i += 4096) {
|
||||||
},
|
let part = this.rQshiftBytes(Math.min(4096, len - i));
|
||||||
|
str += String.fromCharCode.apply(null, part);
|
||||||
|
}
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
rQshiftStr: function (len) {
|
rQshiftBytes(len) {
|
||||||
if (typeof(len) === 'undefined') { len = this.rQlen(); }
|
if (typeof(len) === 'undefined') { len = this.rQlen; }
|
||||||
var arr = new Uint8Array(this._rQ.buffer, this._rQi, len);
|
|
||||||
this._rQi += len;
|
|
||||||
return typedArrayToString(arr);
|
|
||||||
},
|
|
||||||
|
|
||||||
rQshiftBytes: function (len) {
|
|
||||||
if (typeof(len) === 'undefined') { len = this.rQlen(); }
|
|
||||||
this._rQi += len;
|
this._rQi += len;
|
||||||
return new Uint8Array(this._rQ.buffer, this._rQi - len, len);
|
return new Uint8Array(this._rQ.buffer, this._rQi - len, len);
|
||||||
},
|
}
|
||||||
|
|
||||||
rQshiftTo: function (target, len) {
|
rQshiftTo(target, len) {
|
||||||
if (len === undefined) { len = this.rQlen(); }
|
if (len === undefined) { len = this.rQlen; }
|
||||||
// TODO: make this just use set with views when using a ArrayBuffer to store the rQ
|
// TODO: make this just use set with views when using a ArrayBuffer to store the rQ
|
||||||
target.set(new Uint8Array(this._rQ.buffer, this._rQi, len));
|
target.set(new Uint8Array(this._rQ.buffer, this._rQi, len));
|
||||||
this._rQi += len;
|
this._rQi += len;
|
||||||
},
|
}
|
||||||
|
|
||||||
rQwhole: function () {
|
rQslice(start, end = this.rQlen) {
|
||||||
return new Uint8Array(this._rQ.buffer, 0, this._rQlen);
|
return new Uint8Array(this._rQ.buffer, this._rQi + start, end - start);
|
||||||
},
|
}
|
||||||
|
|
||||||
rQslice: function (start, end) {
|
|
||||||
if (end) {
|
|
||||||
return new Uint8Array(this._rQ.buffer, this._rQi + start, end - start);
|
|
||||||
} else {
|
|
||||||
return new Uint8Array(this._rQ.buffer, this._rQi + start, this._rQlen - this._rQi - start);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Check to see if we must wait for 'num' bytes (default to FBU.bytes)
|
// Check to see if we must wait for 'num' bytes (default to FBU.bytes)
|
||||||
// to be available in the receive queue. Return true if we need to
|
// to be available in the receive queue. Return true if we need to
|
||||||
// wait (and possibly print a debug message), otherwise false.
|
// wait (and possibly print a debug message), otherwise false.
|
||||||
rQwait: function (msg, num, goback) {
|
rQwait(msg, num, goback) {
|
||||||
var rQlen = this._rQlen - this._rQi; // Skip rQlen() function call
|
if (this.rQlen < num) {
|
||||||
if (rQlen < num) {
|
|
||||||
if (goback) {
|
if (goback) {
|
||||||
if (this._rQi < goback) {
|
if (this._rQi < goback) {
|
||||||
throw new Error("rQwait cannot backup " + goback + " bytes");
|
throw new Error("rQwait cannot backup " + goback + " bytes");
|
||||||
|
@ -160,58 +137,55 @@ Websock.prototype = {
|
||||||
return true; // true means need more data
|
return true; // true means need more data
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
}
|
||||||
|
|
||||||
// Send Queue
|
// Send Queue
|
||||||
|
|
||||||
flush: function () {
|
flush() {
|
||||||
if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) {
|
if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) {
|
||||||
this._websocket.send(this._encode_message());
|
this._websocket.send(this._encode_message());
|
||||||
this._sQlen = 0;
|
this._sQlen = 0;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
send: function (arr) {
|
send(arr) {
|
||||||
this._sQ.set(arr, this._sQlen);
|
this._sQ.set(arr, this._sQlen);
|
||||||
this._sQlen += arr.length;
|
this._sQlen += arr.length;
|
||||||
this.flush();
|
this.flush();
|
||||||
},
|
}
|
||||||
|
|
||||||
send_string: function (str) {
|
send_string(str) {
|
||||||
this.send(str.split('').map(function (chr) {
|
this.send(str.split('').map(chr => chr.charCodeAt(0)));
|
||||||
return chr.charCodeAt(0);
|
}
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
// Event Handlers
|
// Event Handlers
|
||||||
off: function (evt) {
|
off(evt) {
|
||||||
this._eventHandlers[evt] = function () {};
|
this._eventHandlers[evt] = () => {};
|
||||||
},
|
}
|
||||||
|
|
||||||
on: function (evt, handler) {
|
on(evt, handler) {
|
||||||
this._eventHandlers[evt] = handler;
|
this._eventHandlers[evt] = handler;
|
||||||
},
|
}
|
||||||
|
|
||||||
_allocate_buffers: function () {
|
_allocate_buffers() {
|
||||||
this._rQ = new Uint8Array(this._rQbufferSize);
|
this._rQ = new Uint8Array(this._rQbufferSize);
|
||||||
this._sQ = new Uint8Array(this._sQbufferSize);
|
this._sQ = new Uint8Array(this._sQbufferSize);
|
||||||
},
|
}
|
||||||
|
|
||||||
init: function () {
|
init() {
|
||||||
this._allocate_buffers();
|
this._allocate_buffers();
|
||||||
this._rQi = 0;
|
this._rQi = 0;
|
||||||
this._websocket = null;
|
this._websocket = null;
|
||||||
},
|
}
|
||||||
|
|
||||||
open: function (uri, protocols) {
|
open(uri, protocols) {
|
||||||
var ws_schema = uri.match(/^([a-z]+):\/\//)[1];
|
|
||||||
this.init();
|
this.init();
|
||||||
|
|
||||||
this._websocket = new WebSocket(uri, protocols);
|
this._websocket = new WebSocket(uri, protocols);
|
||||||
this._websocket.binaryType = 'arraybuffer';
|
this._websocket.binaryType = 'arraybuffer';
|
||||||
|
|
||||||
this._websocket.onmessage = this._recv_message.bind(this);
|
this._websocket.onmessage = this._recv_message.bind(this);
|
||||||
this._websocket.onopen = (function () {
|
this._websocket.onopen = () => {
|
||||||
Log.Debug('>> WebSock.onopen');
|
Log.Debug('>> WebSock.onopen');
|
||||||
if (this._websocket.protocol) {
|
if (this._websocket.protocol) {
|
||||||
Log.Info("Server choose sub-protocol: " + this._websocket.protocol);
|
Log.Info("Server choose sub-protocol: " + this._websocket.protocol);
|
||||||
|
@ -219,20 +193,20 @@ Websock.prototype = {
|
||||||
|
|
||||||
this._eventHandlers.open();
|
this._eventHandlers.open();
|
||||||
Log.Debug("<< WebSock.onopen");
|
Log.Debug("<< WebSock.onopen");
|
||||||
}).bind(this);
|
};
|
||||||
this._websocket.onclose = (function (e) {
|
this._websocket.onclose = (e) => {
|
||||||
Log.Debug(">> WebSock.onclose");
|
Log.Debug(">> WebSock.onclose");
|
||||||
this._eventHandlers.close(e);
|
this._eventHandlers.close(e);
|
||||||
Log.Debug("<< WebSock.onclose");
|
Log.Debug("<< WebSock.onclose");
|
||||||
}).bind(this);
|
};
|
||||||
this._websocket.onerror = (function (e) {
|
this._websocket.onerror = (e) => {
|
||||||
Log.Debug(">> WebSock.onerror: " + e);
|
Log.Debug(">> WebSock.onerror: " + e);
|
||||||
this._eventHandlers.error(e);
|
this._eventHandlers.error(e);
|
||||||
Log.Debug("<< WebSock.onerror: " + e);
|
Log.Debug("<< WebSock.onerror: " + e);
|
||||||
}).bind(this);
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
close: function () {
|
close() {
|
||||||
if (this._websocket) {
|
if (this._websocket) {
|
||||||
if ((this._websocket.readyState === WebSocket.OPEN) ||
|
if ((this._websocket.readyState === WebSocket.OPEN) ||
|
||||||
(this._websocket.readyState === WebSocket.CONNECTING)) {
|
(this._websocket.readyState === WebSocket.CONNECTING)) {
|
||||||
|
@ -240,39 +214,39 @@ Websock.prototype = {
|
||||||
this._websocket.close();
|
this._websocket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
this._websocket.onmessage = function (e) { return; };
|
this._websocket.onmessage = () => {};
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
// private methods
|
// private methods
|
||||||
_encode_message: function () {
|
_encode_message() {
|
||||||
// Put in a binary arraybuffer
|
// Put in a binary arraybuffer
|
||||||
// according to the spec, you can send ArrayBufferViews with the send method
|
// according to the spec, you can send ArrayBufferViews with the send method
|
||||||
return new Uint8Array(this._sQ.buffer, 0, this._sQlen);
|
return new Uint8Array(this._sQ.buffer, 0, this._sQlen);
|
||||||
},
|
}
|
||||||
|
|
||||||
_expand_compact_rQ: function (min_fit) {
|
_expand_compact_rQ(min_fit) {
|
||||||
var resizeNeeded = min_fit || this._rQlen - this._rQi > this._rQbufferSize / 2;
|
const resizeNeeded = min_fit || this.rQlen > this._rQbufferSize / 2;
|
||||||
if (resizeNeeded) {
|
if (resizeNeeded) {
|
||||||
if (!min_fit) {
|
if (!min_fit) {
|
||||||
// just double the size if we need to do compaction
|
// just double the size if we need to do compaction
|
||||||
this._rQbufferSize *= 2;
|
this._rQbufferSize *= 2;
|
||||||
} else {
|
} else {
|
||||||
// otherwise, make sure we satisy rQlen - rQi + min_fit < rQbufferSize / 8
|
// otherwise, make sure we satisy rQlen - rQi + min_fit < rQbufferSize / 8
|
||||||
this._rQbufferSize = (this._rQlen - this._rQi + min_fit) * 8;
|
this._rQbufferSize = (this.rQlen + min_fit) * 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// we don't want to grow unboundedly
|
// we don't want to grow unboundedly
|
||||||
if (this._rQbufferSize > MAX_RQ_GROW_SIZE) {
|
if (this._rQbufferSize > MAX_RQ_GROW_SIZE) {
|
||||||
this._rQbufferSize = MAX_RQ_GROW_SIZE;
|
this._rQbufferSize = MAX_RQ_GROW_SIZE;
|
||||||
if (this._rQbufferSize - this._rQlen - this._rQi < min_fit) {
|
if (this._rQbufferSize - this.rQlen < min_fit) {
|
||||||
throw new Exception("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit");
|
throw new Error("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resizeNeeded) {
|
if (resizeNeeded) {
|
||||||
var old_rQbuffer = this._rQ.buffer;
|
const old_rQbuffer = this._rQ.buffer;
|
||||||
this._rQmax = this._rQbufferSize / 8;
|
this._rQmax = this._rQbufferSize / 8;
|
||||||
this._rQ = new Uint8Array(this._rQbufferSize);
|
this._rQ = new Uint8Array(this._rQbufferSize);
|
||||||
this._rQ.set(new Uint8Array(old_rQbuffer, this._rQi));
|
this._rQ.set(new Uint8Array(old_rQbuffer, this._rQi));
|
||||||
|
@ -286,21 +260,21 @@ Websock.prototype = {
|
||||||
|
|
||||||
this._rQlen = this._rQlen - this._rQi;
|
this._rQlen = this._rQlen - this._rQi;
|
||||||
this._rQi = 0;
|
this._rQi = 0;
|
||||||
},
|
}
|
||||||
|
|
||||||
_decode_message: function (data) {
|
_decode_message(data) {
|
||||||
// push arraybuffer values onto the end
|
// push arraybuffer values onto the end
|
||||||
var u8 = new Uint8Array(data);
|
const u8 = new Uint8Array(data);
|
||||||
if (u8.length > this._rQbufferSize - this._rQlen) {
|
if (u8.length > this._rQbufferSize - this._rQlen) {
|
||||||
this._expand_compact_rQ(u8.length);
|
this._expand_compact_rQ(u8.length);
|
||||||
}
|
}
|
||||||
this._rQ.set(u8, this._rQlen);
|
this._rQ.set(u8, this._rQlen);
|
||||||
this._rQlen += u8.length;
|
this._rQlen += u8.length;
|
||||||
},
|
}
|
||||||
|
|
||||||
_recv_message: function (e) {
|
_recv_message(e) {
|
||||||
this._decode_message(e.data);
|
this._decode_message(e.data);
|
||||||
if (this.rQlen() > 0) {
|
if (this.rQlen > 0) {
|
||||||
this._eventHandlers.message();
|
this._eventHandlers.message();
|
||||||
// Compact the receive queue
|
// Compact the receive queue
|
||||||
if (this._rQlen == this._rQi) {
|
if (this._rQlen == this._rQi) {
|
||||||
|
@ -313,4 +287,4 @@ Websock.prototype = {
|
||||||
Log.Debug("Ignoring empty message");
|
Log.Debug("Ignoring empty message");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
|
@ -6,6 +6,7 @@ import babelTransformES2015ModulesSystemJS from 'babel-plugin-transform-es2015-m
|
||||||
var babelTransform = require('babel-core').transform;
|
var babelTransform = require('babel-core').transform;
|
||||||
var babelTransformDynamicImport = require('babel-plugin-syntax-dynamic-import');
|
var babelTransformDynamicImport = require('babel-plugin-syntax-dynamic-import');
|
||||||
var babelTransformES2015ModulesSystemJS = require('babel-plugin-transform-es2015-modules-systemjs');
|
var babelTransformES2015ModulesSystemJS = require('babel-plugin-transform-es2015-modules-systemjs');
|
||||||
|
var babelPresetES2015 = require('babel-preset-es2015');
|
||||||
|
|
||||||
self.onmessage = function (evt) {
|
self.onmessage = function (evt) {
|
||||||
// transform source with Babel
|
// transform source with Babel
|
||||||
|
@ -17,6 +18,7 @@ self.onmessage = function (evt) {
|
||||||
sourceMaps: 'inline',
|
sourceMaps: 'inline',
|
||||||
babelrc: false,
|
babelrc: false,
|
||||||
plugins: [babelTransformDynamicImport, babelTransformES2015ModulesSystemJS],
|
plugins: [babelTransformDynamicImport, babelTransformES2015ModulesSystemJS],
|
||||||
|
presets: [babelPresetES2015],
|
||||||
});
|
});
|
||||||
|
|
||||||
self.postMessage({key: evt.data.key, code: output.code, source: evt.data.source});
|
self.postMessage({key: evt.data.key, code: output.code, source: evt.data.source});
|
||||||
|
|
|
@ -66,7 +66,7 @@ if (typeof document != 'undefined' && document.getElementsByTagName) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// simple DOM ready
|
// simple DOM ready
|
||||||
if (document.readyState === 'complete')
|
if (document.readyState !== 'loading')
|
||||||
setTimeout(ready);
|
setTimeout(ready);
|
||||||
else
|
else
|
||||||
document.addEventListener('DOMContentLoaded', ready, false);
|
document.addEventListener('DOMContentLoaded', ready, false);
|
||||||
|
@ -140,9 +140,16 @@ var WorkerPool = function (script, size) {
|
||||||
var current = document.currentScript;
|
var current = document.currentScript;
|
||||||
// IE doesn't support currentScript
|
// IE doesn't support currentScript
|
||||||
if (!current) {
|
if (!current) {
|
||||||
// We should be the last loaded script
|
// Find an entry with out basename
|
||||||
var scripts = document.getElementsByTagName('script');
|
var scripts = document.getElementsByTagName('script');
|
||||||
current = scripts[scripts.length - 1];
|
for (var i = 0; i < scripts.length; i++) {
|
||||||
|
if (scripts[i].src.indexOf("browser-es-module-loader.js") !== -1) {
|
||||||
|
current = scripts[i];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!current)
|
||||||
|
throw Error("Could not find own <script> element");
|
||||||
}
|
}
|
||||||
script = current.src.substr(0, current.src.lastIndexOf("/")) + "/" + script;
|
script = current.src.substr(0, current.src.lastIndexOf("/")) + "/" + script;
|
||||||
this._workers = new Array(size);
|
this._workers = new Array(size);
|
||||||
|
|
14043
static/js/novnc/vendor/sinon.js
vendored
14043
static/js/novnc/vendor/sinon.js
vendored
File diff suppressed because one or more lines are too long
|
@ -488,12 +488,11 @@ class wvmInstance(wvmConnect):
|
||||||
<target dev='%s' bus='%s'/>
|
<target dev='%s' bus='%s'/>
|
||||||
</disk>
|
</disk>
|
||||||
""" % (source, target, targetbus)
|
""" % (source, target, targetbus)
|
||||||
|
if self.get_status() == 1:
|
||||||
|
self.instance.attachDeviceFlags(xml_disk, VIR_DOMAIN_AFFECT_LIVE)
|
||||||
|
self.instance.attachDeviceFlags(xml_disk, VIR_DOMAIN_AFFECT_CONFIG)
|
||||||
if self.get_status() == 5:
|
if self.get_status() == 5:
|
||||||
devices = tree.find('devices')
|
self.instance.attachDeviceFlags(xml_disk, VIR_DOMAIN_AFFECT_CONFIG)
|
||||||
elm_disk = ElementTree.fromstring(xml_disk)
|
|
||||||
devices.append(elm_disk)
|
|
||||||
xmldom = ElementTree.tostring(tree)
|
|
||||||
self._defineXML(xmldom)
|
|
||||||
|
|
||||||
def detach_disk(self, dev):
|
def detach_disk(self, dev):
|
||||||
tree = ElementTree.fromstring(self._XMLDesc(0))
|
tree = ElementTree.fromstring(self._XMLDesc(0))
|
||||||
|
|
Loading…
Reference in a new issue