mirror of
https://github.com/retspen/webvirtcloud
synced 2025-01-12 08:25:18 +00:00
update novnc 1.1.0 ->1.2.0
This commit is contained in:
parent
bb935b3713
commit
43f1461e29
53 changed files with 109904 additions and 56529 deletions
|
@ -17,7 +17,11 @@
|
||||||
|
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
|
||||||
<!-- Icons (see Makefile for what the sizes are for) -->
|
<!-- Always force latest IE rendering engine (even in intranet) & Chrome Frame
|
||||||
|
Remove this if you use the .htaccess -->
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
|
||||||
|
<!-- Icons (see app/images/icons/Makefile for what the sizes are for) -->
|
||||||
<link rel="icon" sizes="16x16" type="image/png" href="{% 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" %}">
|
||||||
<link rel="icon" sizes="32x32" type="image/png" href="{% static "js/novnc/app/images/icons/novnc-32x32.png" %}">
|
<link rel="icon" sizes="32x32" type="image/png" href="{% static "js/novnc/app/images/icons/novnc-32x32.png" %}">
|
||||||
|
@ -54,12 +58,8 @@
|
||||||
<!-- Stylesheets -->
|
<!-- Stylesheets -->
|
||||||
<link rel="stylesheet" href="{% static "js/novnc/app/styles/base.css" %}" />
|
<link rel="stylesheet" href="{% static "js/novnc/app/styles/base.css" %}" />
|
||||||
|
|
||||||
<!--
|
|
||||||
<script type='text/javascript' src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
|
||||||
-->
|
|
||||||
|
|
||||||
<!-- this is included as a normal file in order to catch script-loading errors as well -->
|
<!-- this is included as a normal file in order to catch script-loading errors as well -->
|
||||||
<script type="text/javascript" src="{% static "js/novnc/app/error-handler.js" %}"></script>
|
<script src="{% static "js/novnc/app/error-handler.js" %}"></script>
|
||||||
|
|
||||||
<!-- begin scripts -->
|
<!-- begin scripts -->
|
||||||
<!-- promise polyfills promises for IE11 -->
|
<!-- promise polyfills promises for IE11 -->
|
||||||
|
@ -91,7 +91,7 @@
|
||||||
|
|
||||||
<div class="noVNC_scroll">
|
<div class="noVNC_scroll">
|
||||||
|
|
||||||
<h1 class="noVNC_logo" translate="no"><span>no</span><br />VNC</h1>
|
<h1 class="noVNC_logo" translate="no"><span>no</span><br>VNC</h1>
|
||||||
|
|
||||||
<!-- Drag/Pan the viewport -->
|
<!-- Drag/Pan the viewport -->
|
||||||
<input type="image" alt="viewport drag" src="{% static "js/novnc/app/images/drag.svg" %}"
|
<input type="image" alt="viewport drag" src="{% static "js/novnc/app/images/drag.svg" %}"
|
||||||
|
@ -99,20 +99,11 @@
|
||||||
|
|
||||||
<!--noVNC Touch Device only buttons-->
|
<!--noVNC Touch Device only buttons-->
|
||||||
<div id="noVNC_mobile_buttons">
|
<div id="noVNC_mobile_buttons">
|
||||||
<input type="image" alt="No mousebutton" src="{% static "js/novnc/app/images/mouse_none.svg" %}"
|
|
||||||
id="noVNC_mouse_button0" class="noVNC_button" title="Active Mouse Button" />
|
|
||||||
<input type="image" alt="Left mousebutton" src="{% static "js/novnc/app/images/mouse_left.svg" %}"
|
|
||||||
id="noVNC_mouse_button1" class="noVNC_button" title="Active Mouse Button" />
|
|
||||||
<input type="image" alt="Middle mousebutton" src="{% static "js/novnc/app/images/mouse_middle.svg" %}"
|
|
||||||
id="noVNC_mouse_button2" class="noVNC_button" title="Active Mouse Button" />
|
|
||||||
<input type="image" alt="Right mousebutton" src="{% static "js/novnc/app/images/mouse_right.svg" %}"
|
|
||||||
id="noVNC_mouse_button4" class="noVNC_button" title="Active Mouse Button" />
|
|
||||||
<input type="image" alt="Keyboard" src="{% static "js/novnc/app/images/keyboard.svg" %}"
|
<input type="image" alt="Keyboard" src="{% static "js/novnc/app/images/keyboard.svg" %}"
|
||||||
id="noVNC_keyboard_button" class="noVNC_button" value="Keyboard" title="Show Keyboard" />
|
id="noVNC_keyboard_button" class="noVNC_button" title="Show Keyboard">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Extra manual keys -->
|
<!-- Extra manual keys -->
|
||||||
<div id="noVNC_extra_keys">
|
|
||||||
<input type="image" alt="Extra keys" src="{% static "js/novnc/app/images/toggleextrakeys.svg" %}"
|
<input type="image" alt="Extra keys" src="{% static "js/novnc/app/images/toggleextrakeys.svg" %}"
|
||||||
id="noVNC_toggle_extra_keys_button" class="noVNC_button" title="Show Extra Keys" />
|
id="noVNC_toggle_extra_keys_button" class="noVNC_button" title="Show Extra Keys" />
|
||||||
<div class="noVNC_vcenter">
|
<div class="noVNC_vcenter">
|
||||||
|
@ -131,7 +122,6 @@
|
||||||
id="noVNC_send_ctrl_alt_del_button" class="noVNC_button" title="Send Ctrl-Alt-Del" />
|
id="noVNC_send_ctrl_alt_del_button" class="noVNC_button" title="Send Ctrl-Alt-Del" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Shutdown/Reboot -->
|
<!-- Shutdown/Reboot -->
|
||||||
<input type="image" alt="Shutdown/Reboot" src="{% static "js/novnc/app/images/power.svg" %}"
|
<input type="image" alt="Shutdown/Reboot" src="{% static "js/novnc/app/images/power.svg" %}"
|
||||||
|
@ -199,59 +189,53 @@
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class="noVNC_expander">Advanced</div>
|
<div class="noVNC_expander">Advanced</div>
|
||||||
<div>
|
<div><ul>
|
||||||
<ul>
|
<li>
|
||||||
|
<label for="noVNC_setting_quality">Quality:</label>
|
||||||
|
<input id="noVNC_setting_quality" type="range" min="0" max="9" value="6">
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label for="noVNC_setting_compression">Compression level:</label>
|
||||||
|
<input id="noVNC_setting_compression" type="range" min="0" max="9" value="2">
|
||||||
|
</li>
|
||||||
|
<li><hr></li>
|
||||||
<li>
|
<li>
|
||||||
<label for="noVNC_setting_repeaterID">Repeater ID:</label>
|
<label for="noVNC_setting_repeaterID">Repeater ID:</label>
|
||||||
<input id="noVNC_setting_repeaterID" type="input" value="" />
|
<input id="noVNC_setting_repeaterID" type="text" value="">
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<div class="noVNC_expander">WebSocket</div>
|
<div class="noVNC_expander">WebSocket</div>
|
||||||
<div>
|
<div><ul>
|
||||||
<ul>
|
|
||||||
<li>
|
<li>
|
||||||
<label><input id="noVNC_setting_encrypt"
|
<label><input id="noVNC_setting_encrypt" type="checkbox"> Encrypt</label>
|
||||||
type="checkbox" />Encrypt</label>
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<label for="noVNC_setting_host">Host:</label>
|
<label for="noVNC_setting_host">Host:</label>
|
||||||
<input id="noVNC_setting_host" value="{{ ws_host }}" />
|
<input id="noVNC_setting_host">
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<label for="noVNC_setting_port">Port:</label>
|
<label for="noVNC_setting_port">Port:</label>
|
||||||
<input id="noVNC_setting_port" value="{{ ws_port }}"
|
<input id="noVNC_setting_port" type="number">
|
||||||
type="number" />
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<label for="noVNC_setting_path">Path:</label>
|
<label for="noVNC_setting_path">Path:</label>
|
||||||
<!-- <input id="noVNC_setting_path" type="input" value="websockify"/> -->
|
<input id="noVNC_setting_path" type="text" value="websockify">
|
||||||
<input id="noVNC_setting_path" type="input" value="{{ ws_path }}" />
|
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul></div>
|
||||||
</div>
|
|
||||||
</li>
|
</li>
|
||||||
|
<li><hr></li>
|
||||||
<li>
|
<li>
|
||||||
<hr>
|
<label><input id="noVNC_setting_reconnect" type="checkbox"> Automatic Reconnect</label>
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<label><input id="noVNC_setting_reconnect" type="checkbox" />Automatic
|
|
||||||
Reconnect</label>
|
|
||||||
<input id="noVNC_setting_autoconnect" type="checkbox" value="true" hidden />
|
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<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>
|
<li>
|
||||||
<hr>
|
<label><input id="noVNC_setting_show_dot" type="checkbox"> Show Dot when No Cursor</label>
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<label><input id="noVNC_setting_show_dot" type="checkbox">Show Dot when No
|
|
||||||
Cursor</label>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<hr>
|
|
||||||
</li>
|
</li>
|
||||||
|
<li><hr></li>
|
||||||
<!-- Logging selection dropdown -->
|
<!-- Logging selection dropdown -->
|
||||||
<li>
|
<li>
|
||||||
<label>Logging:
|
<label>Logging:
|
||||||
|
@ -259,8 +243,12 @@
|
||||||
</select>
|
</select>
|
||||||
</label>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul></div>
|
||||||
</div>
|
</li>
|
||||||
|
<li class="noVNC_version_separator"><hr></li>
|
||||||
|
<li class="noVNC_version_wrapper">
|
||||||
|
<span>Version:</span>
|
||||||
|
<span class="noVNC_version"></span>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -292,10 +280,14 @@
|
||||||
|
|
||||||
<!-- Password Dialog -->
|
<!-- Password Dialog -->
|
||||||
<div class="noVNC_center noVNC_connect_layer">
|
<div class="noVNC_center noVNC_connect_layer">
|
||||||
<div id="noVNC_password_dlg" class="noVNC_panel">
|
<div id="noVNC_credentials_dlg" class="noVNC_panel">
|
||||||
<form aria-label="noVNC password form">
|
<form aria-label="noVNC password form">
|
||||||
<ul>
|
<ul>
|
||||||
<li>
|
<li id="noVNC_username_block">
|
||||||
|
<label>Username:</label>
|
||||||
|
<input id="noVNC_username_input">
|
||||||
|
</li>
|
||||||
|
<li id="noVNC_password_block">
|
||||||
<label>Password:</label>
|
<label>Password:</label>
|
||||||
{% if perms.instances.passwordless_console %}
|
{% if perms.instances.passwordless_console %}
|
||||||
<input id="noVNC_password_input" type="password" value='{{ console_passwd }}' />
|
<input id="noVNC_password_input" type="password" value='{{ console_passwd }}' />
|
||||||
|
@ -304,7 +296,7 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</li>
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<input id="noVNC_password_button" type="submit" value="Send Password" class="noVNC_submit" />
|
<input id="noVNC_credentials_button" type="submit" value="Send Credentials" class="noVNC_submit" />
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</form>
|
</form>
|
||||||
|
@ -326,8 +318,8 @@
|
||||||
html attributes which attempt to disable text suggestions on the
|
html attributes which attempt to disable text suggestions on the
|
||||||
on-screen keyboard. Let's hope Chrome implements the ime-mode
|
on-screen keyboard. Let's hope Chrome implements the ime-mode
|
||||||
style for example -->
|
style for example -->
|
||||||
<textarea id="noVNC_keyboardinput" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false"
|
<textarea id="noVNC_keyboardinput" autocapitalize="off" autocomplete="off" spellcheck="false"
|
||||||
mozactionhint="Enter" tabindex="-1"></textarea>
|
tabindex="-1"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<audio id="noVNC_bell">
|
<audio id="noVNC_bell">
|
||||||
|
|
|
@ -205,7 +205,6 @@
|
||||||
rfb.addEventListener("disconnect", disconnectedFromServer);
|
rfb.addEventListener("disconnect", disconnectedFromServer);
|
||||||
rfb.addEventListener("credentialsrequired", credentialsAreRequired);
|
rfb.addEventListener("credentialsrequired", credentialsAreRequired);
|
||||||
rfb.addEventListener("desktopname", updateDesktopName);
|
rfb.addEventListener("desktopname", updateDesktopName);
|
||||||
rfb.addEventListener("capabilities", function () { updatePowerButtons(); });
|
|
||||||
|
|
||||||
// Set parameters that can be changed on an active connection
|
// Set parameters that can be changed on an active connection
|
||||||
rfb.scaleViewport = {{ scale }};
|
rfb.scaleViewport = {{ scale }};
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
/*
|
||||||
|
* noVNC: HTML5 VNC client
|
||||||
|
* Copyright (C) 2019 The noVNC Authors
|
||||||
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
|
*
|
||||||
|
* See README.md for usage and integration instructions.
|
||||||
|
*/
|
||||||
|
|
||||||
// NB: this should *not* be included as a module until we have
|
// NB: this should *not* be included as a module until we have
|
||||||
// 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.
|
||||||
|
|
|
@ -15,18 +15,18 @@
|
||||||
inkscape:export-xdpi="90"
|
inkscape:export-xdpi="90"
|
||||||
sodipodi:docname="windows.svg"
|
sodipodi:docname="windows.svg"
|
||||||
inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
|
inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
|
||||||
inkscape:version="0.92.3 (2405546, 2018-03-11)"
|
inkscape:version="0.92.4 (unknown)"
|
||||||
x="0px"
|
x="0px"
|
||||||
y="0px"
|
y="0px"
|
||||||
viewBox="-293 384 25 23"
|
viewBox="-293 384 25 25"
|
||||||
xml:space="preserve"
|
xml:space="preserve"
|
||||||
width="25"
|
width="25"
|
||||||
height="23"><metadata
|
height="25"><metadata
|
||||||
id="metadata21"><rdf:RDF><cc:Work
|
id="metadata21"><rdf:RDF><cc:Work
|
||||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
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
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||||
id="defs19" /><sodipodi:namedview
|
id="defs19" /><sodipodi:namedview
|
||||||
pagecolor="#ffffff"
|
pagecolor="#959595"
|
||||||
bordercolor="#666666"
|
bordercolor="#666666"
|
||||||
borderopacity="1"
|
borderopacity="1"
|
||||||
objecttolerance="10"
|
objecttolerance="10"
|
||||||
|
@ -35,51 +35,31 @@
|
||||||
inkscape:pageopacity="0"
|
inkscape:pageopacity="0"
|
||||||
inkscape:pageshadow="2"
|
inkscape:pageshadow="2"
|
||||||
inkscape:window-width="1920"
|
inkscape:window-width="1920"
|
||||||
inkscape:window-height="1017"
|
inkscape:window-height="1136"
|
||||||
id="namedview17"
|
id="namedview17"
|
||||||
showgrid="false"
|
showgrid="true"
|
||||||
inkscape:pagecheckerboard="true"
|
inkscape:pagecheckerboard="false"
|
||||||
inkscape:zoom="9.44"
|
inkscape:zoom="32"
|
||||||
inkscape:cx="-0.84745763"
|
inkscape:cx="3.926913"
|
||||||
inkscape:cy="12.5"
|
inkscape:cy="13.255959"
|
||||||
inkscape:window-x="2552"
|
inkscape:window-x="1920"
|
||||||
inkscape:window-y="122"
|
inkscape:window-y="27"
|
||||||
inkscape:window-maximized="1"
|
inkscape:window-maximized="1"
|
||||||
inkscape:current-layer="svg2" />
|
inkscape:current-layer="svg2"><inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid818" /></sodipodi:namedview>
|
||||||
<style
|
<style
|
||||||
type="text/css"
|
type="text/css"
|
||||||
id="style2">
|
id="style2">
|
||||||
.st0{fill:#FFFFFF;}
|
.st0{fill:#FFFFFF;}
|
||||||
</style>
|
</style>
|
||||||
<g
|
|
||||||
id="g14"
|
|
||||||
transform="matrix(1.2624869,0,0,1.3601695,73.614445,-144.84322)">
|
|
||||||
<g
|
|
||||||
id="g12">
|
|
||||||
<path
|
<path
|
||||||
class="st0"
|
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
|
||||||
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"
|
d="M 21 4 L 11 5.1757812 L 11 12 L 21 12 L 21 4 z M 10 5.2949219 L 4 6 L 4 12 L 10 12 L 10 5.2949219 z "
|
||||||
id="path4"
|
transform="translate(-293,384)"
|
||||||
inkscape:connector-curvature="0"
|
id="path853" /><path
|
||||||
style="fill:#ffffff" />
|
id="path858"
|
||||||
<path
|
d="m -272,405 -10,-1.17578 V 397 h 10 z M -283,403.70508 -289,403 v -6 h 6 z"
|
||||||
class="st0"
|
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
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"
|
inkscape:connector-curvature="0" /></svg>
|
||||||
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>
|
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 2.4 KiB |
1
static/js/novnc/app/locale/README
Executable file
1
static/js/novnc/app/locale/README
Executable file
|
@ -0,0 +1 @@
|
||||||
|
DO NOT MODIFY THE FILES IN THIS FOLDER, THEY ARE AUTOMATICALLY GENERATED FROM THE PO-FILES.
|
73
static/js/novnc/app/locale/ja.json
Executable file
73
static/js/novnc/app/locale/ja.json
Executable file
|
@ -0,0 +1,73 @@
|
||||||
|
{
|
||||||
|
"Connecting...": "接続しています...",
|
||||||
|
"Disconnecting...": "切断しています...",
|
||||||
|
"Reconnecting...": "再接続しています...",
|
||||||
|
"Internal error": "内部エラー",
|
||||||
|
"Must set host": "ホストを設定する必要があります",
|
||||||
|
"Connected (encrypted) to ": "接続しました (暗号化済み): ",
|
||||||
|
"Connected (unencrypted) to ": "接続しました (暗号化されていません): ",
|
||||||
|
"Something went wrong, connection is closed": "何かが問題で、接続が閉じられました",
|
||||||
|
"Failed to connect to server": "サーバーへの接続に失敗しました",
|
||||||
|
"Disconnected": "切断しました",
|
||||||
|
"New connection has been rejected with reason: ": "新規接続は次の理由で拒否されました: ",
|
||||||
|
"New connection has been rejected": "新規接続は拒否されました",
|
||||||
|
"Password is required": "パスワードが必要です",
|
||||||
|
"noVNC encountered an error:": "noVNC でエラーが発生しました:",
|
||||||
|
"Hide/Show the control bar": "コントロールバーを隠す/表示する",
|
||||||
|
"Move/Drag Viewport": "ビューポートを移動/ドラッグ",
|
||||||
|
"viewport drag": "ビューポートをドラッグ",
|
||||||
|
"Active Mouse Button": "アクティブなマウスボタン",
|
||||||
|
"No mousebutton": "マウスボタンなし",
|
||||||
|
"Left mousebutton": "左マウスボタン",
|
||||||
|
"Middle mousebutton": "中マウスボタン",
|
||||||
|
"Right mousebutton": "右マウスボタン",
|
||||||
|
"Keyboard": "キーボード",
|
||||||
|
"Show Keyboard": "キーボードを表示",
|
||||||
|
"Extra keys": "追加キー",
|
||||||
|
"Show Extra Keys": "追加キーを表示",
|
||||||
|
"Ctrl": "Ctrl",
|
||||||
|
"Toggle Ctrl": "Ctrl キーを切り替え",
|
||||||
|
"Alt": "Alt",
|
||||||
|
"Toggle Alt": "Alt キーを切り替え",
|
||||||
|
"Toggle Windows": "Windows キーを切り替え",
|
||||||
|
"Windows": "Windows",
|
||||||
|
"Send Tab": "Tab キーを送信",
|
||||||
|
"Tab": "Tab",
|
||||||
|
"Esc": "Esc",
|
||||||
|
"Send Escape": "Escape キーを送信",
|
||||||
|
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
|
||||||
|
"Send Ctrl-Alt-Del": "Ctrl-Alt-Del を送信",
|
||||||
|
"Shutdown/Reboot": "シャットダウン/再起動",
|
||||||
|
"Shutdown/Reboot...": "シャットダウン/再起動...",
|
||||||
|
"Power": "電源",
|
||||||
|
"Shutdown": "シャットダウン",
|
||||||
|
"Reboot": "再起動",
|
||||||
|
"Reset": "リセット",
|
||||||
|
"Clipboard": "クリップボード",
|
||||||
|
"Clear": "クリア",
|
||||||
|
"Fullscreen": "全画面表示",
|
||||||
|
"Settings": "設定",
|
||||||
|
"Shared Mode": "共有モード",
|
||||||
|
"View Only": "表示のみ",
|
||||||
|
"Clip to Window": "ウィンドウにクリップ",
|
||||||
|
"Scaling Mode:": "スケーリングモード:",
|
||||||
|
"None": "なし",
|
||||||
|
"Local Scaling": "ローカルスケーリング",
|
||||||
|
"Remote Resizing": "リモートでリサイズ",
|
||||||
|
"Advanced": "高度",
|
||||||
|
"Repeater ID:": "リピーター ID:",
|
||||||
|
"WebSocket": "WebSocket",
|
||||||
|
"Encrypt": "暗号化",
|
||||||
|
"Host:": "ホスト:",
|
||||||
|
"Port:": "ポート:",
|
||||||
|
"Path:": "パス:",
|
||||||
|
"Automatic Reconnect": "自動再接続",
|
||||||
|
"Reconnect Delay (ms):": "再接続する遅延 (ミリ秒):",
|
||||||
|
"Show Dot when No Cursor": "カーソルがないときにドットを表示",
|
||||||
|
"Logging:": "ロギング:",
|
||||||
|
"Disconnect": "切断",
|
||||||
|
"Connect": "接続",
|
||||||
|
"Password:": "パスワード:",
|
||||||
|
"Send Password": "パスワードを送信",
|
||||||
|
"Cancel": "キャンセル"
|
||||||
|
}
|
|
@ -11,16 +11,11 @@
|
||||||
"Disconnected": "Frånkopplad",
|
"Disconnected": "Frånkopplad",
|
||||||
"New connection has been rejected with reason: ": "Ny anslutning har blivit nekad med följande skäl: ",
|
"New connection has been rejected with reason: ": "Ny anslutning har blivit nekad med följande skäl: ",
|
||||||
"New connection has been rejected": "Ny anslutning har blivit nekad",
|
"New connection has been rejected": "Ny anslutning har blivit nekad",
|
||||||
"Password is required": "Lösenord krävs",
|
"Credentials are required": "Användaruppgifter krävs",
|
||||||
"noVNC encountered an error:": "noVNC stötte på ett problem:",
|
"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",
|
||||||
|
"Drag": "Dra",
|
||||||
"Move/Drag Viewport": "Flytta/Dra Vyn",
|
"Move/Drag Viewport": "Flytta/Dra Vyn",
|
||||||
"viewport drag": "dra vy",
|
|
||||||
"Active Mouse Button": "Aktiv musknapp",
|
|
||||||
"No mousebutton": "Ingen musknapp",
|
|
||||||
"Left mousebutton": "Vänster musknapp",
|
|
||||||
"Middle mousebutton": "Mitten-musknapp",
|
|
||||||
"Right mousebutton": "Höger musknapp",
|
|
||||||
"Keyboard": "Tangentbord",
|
"Keyboard": "Tangentbord",
|
||||||
"Show Keyboard": "Visa Tangentbord",
|
"Show Keyboard": "Visa Tangentbord",
|
||||||
"Extra keys": "Extraknappar",
|
"Extra keys": "Extraknappar",
|
||||||
|
@ -55,6 +50,8 @@
|
||||||
"Local Scaling": "Lokal Skalning",
|
"Local Scaling": "Lokal Skalning",
|
||||||
"Remote Resizing": "Ändra Storlek",
|
"Remote Resizing": "Ändra Storlek",
|
||||||
"Advanced": "Avancerat",
|
"Advanced": "Avancerat",
|
||||||
|
"Quality:": "Kvalitet:",
|
||||||
|
"Compression level:": "Kompressionsnivå:",
|
||||||
"Repeater ID:": "Repeater-ID:",
|
"Repeater ID:": "Repeater-ID:",
|
||||||
"WebSocket": "WebSocket",
|
"WebSocket": "WebSocket",
|
||||||
"Encrypt": "Kryptera",
|
"Encrypt": "Kryptera",
|
||||||
|
@ -65,9 +62,11 @@
|
||||||
"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",
|
"Show Dot when No Cursor": "Visa prick när ingen muspekare finns",
|
||||||
"Logging:": "Loggning:",
|
"Logging:": "Loggning:",
|
||||||
|
"Version:": "Version:",
|
||||||
"Disconnect": "Koppla från",
|
"Disconnect": "Koppla från",
|
||||||
"Connect": "Anslut",
|
"Connect": "Anslut",
|
||||||
|
"Username:": "Användarnamn:",
|
||||||
"Password:": "Lösenord:",
|
"Password:": "Lösenord:",
|
||||||
"Send Password": "Skicka lösenord",
|
"Send Credentials": "Skicka Användaruppgifter",
|
||||||
"Cancel": "Avbryt"
|
"Cancel": "Avbryt"
|
||||||
}
|
}
|
|
@ -1,19 +1,19 @@
|
||||||
{
|
{
|
||||||
"Connecting...": "链接中...",
|
"Connecting...": "连接中...",
|
||||||
"Disconnecting...": "正在中断连接...",
|
"Disconnecting...": "正在断开连接...",
|
||||||
"Reconnecting...": "重新链接中...",
|
"Reconnecting...": "重新连接中...",
|
||||||
"Internal error": "内部错误",
|
"Internal error": "内部错误",
|
||||||
"Must set host": "请提供主机名",
|
"Must set host": "请提供主机名",
|
||||||
"Connected (encrypted) to ": "已加密链接到",
|
"Connected (encrypted) to ": "已连接到(加密)",
|
||||||
"Connected (unencrypted) to ": "未加密链接到",
|
"Connected (unencrypted) to ": "已连接到(未加密)",
|
||||||
"Something went wrong, connection is closed": "发生错误,链接已关闭",
|
"Something went wrong, connection is closed": "发生错误,连接已关闭",
|
||||||
"Failed to connect to server": "无法链接到服务器",
|
"Failed to connect to server": "无法连接到服务器",
|
||||||
"Disconnected": "链接已中断",
|
"Disconnected": "已断开连接",
|
||||||
"New connection has been rejected with reason: ": "链接被拒绝,原因:",
|
"New connection has been rejected with reason: ": "连接被拒绝,原因:",
|
||||||
"New connection has been rejected": "链接被拒绝",
|
"New connection has been rejected": "连接被拒绝",
|
||||||
"Password is required": "请提供密码",
|
"Password is required": "请提供密码",
|
||||||
"noVNC encountered an error:": "noVNC 遇到一个错误:",
|
"noVNC encountered an error:": "noVNC 遇到一个错误:",
|
||||||
"Hide/Show the control bar": "显示/隐藏控制列",
|
"Hide/Show the control bar": "显示/隐藏控制栏",
|
||||||
"Move/Drag Viewport": "拖放显示范围",
|
"Move/Drag Viewport": "拖放显示范围",
|
||||||
"viewport drag": "显示范围拖放",
|
"viewport drag": "显示范围拖放",
|
||||||
"Active Mouse Button": "启动鼠标按鍵",
|
"Active Mouse Button": "启动鼠标按鍵",
|
||||||
|
@ -43,10 +43,10 @@
|
||||||
"Reset": "重置",
|
"Reset": "重置",
|
||||||
"Clipboard": "剪贴板",
|
"Clipboard": "剪贴板",
|
||||||
"Clear": "清除",
|
"Clear": "清除",
|
||||||
"Fullscreen": "全屏幕",
|
"Fullscreen": "全屏",
|
||||||
"Settings": "设置",
|
"Settings": "设置",
|
||||||
"Shared Mode": "分享模式",
|
"Shared Mode": "分享模式",
|
||||||
"View Only": "仅检视",
|
"View Only": "仅查看",
|
||||||
"Clip to Window": "限制/裁切窗口大小",
|
"Clip to Window": "限制/裁切窗口大小",
|
||||||
"Scaling Mode:": "缩放模式:",
|
"Scaling Mode:": "缩放模式:",
|
||||||
"None": "无",
|
"None": "无",
|
||||||
|
@ -59,11 +59,11 @@
|
||||||
"Host:": "主机:",
|
"Host:": "主机:",
|
||||||
"Port:": "端口:",
|
"Port:": "端口:",
|
||||||
"Path:": "路径:",
|
"Path:": "路径:",
|
||||||
"Automatic Reconnect": "自动重新链接",
|
"Automatic Reconnect": "自动重新连接",
|
||||||
"Reconnect Delay (ms):": "重新链接间隔 (ms):",
|
"Reconnect Delay (ms):": "重新连接间隔 (ms):",
|
||||||
"Logging:": "日志级别:",
|
"Logging:": "日志级别:",
|
||||||
"Disconnect": "终端链接",
|
"Disconnect": "中断连接",
|
||||||
"Connect": "链接",
|
"Connect": "连接",
|
||||||
"Password:": "密码:",
|
"Password:": "密码:",
|
||||||
"Cancel": "取消"
|
"Cancel": "取消"
|
||||||
}
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC base CSS
|
* noVNC base CSS
|
||||||
* Copyright (C) 2018 The noVNC Authors
|
* Copyright (C) 2019 The noVNC Authors
|
||||||
* noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
|
* 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).
|
||||||
*/
|
*/
|
||||||
|
@ -22,13 +22,12 @@
|
||||||
body {
|
body {
|
||||||
margin:0;
|
margin:0;
|
||||||
padding:0;
|
padding:0;
|
||||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
font-family: Helvetica;
|
||||||
/*Background image with light grey curve.*/
|
/*Background image with light grey curve.*/
|
||||||
background-color:#494949;
|
background-color:#494949;
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,8 +83,20 @@ html {
|
||||||
* ----------------------------------------
|
* ----------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
input[type=input], input[type=password], input[type=number],
|
input:not([type]),
|
||||||
input:not([type]), textarea {
|
input[type=date],
|
||||||
|
input[type=datetime-local],
|
||||||
|
input[type=email],
|
||||||
|
input[type=month],
|
||||||
|
input[type=number],
|
||||||
|
input[type=password],
|
||||||
|
input[type=search],
|
||||||
|
input[type=tel],
|
||||||
|
input[type=text],
|
||||||
|
input[type=time],
|
||||||
|
input[type=url],
|
||||||
|
input[type=week],
|
||||||
|
textarea {
|
||||||
/* Disable default rendering */
|
/* Disable default rendering */
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
-moz-appearance: none;
|
-moz-appearance: none;
|
||||||
|
@ -99,7 +110,11 @@ input:not([type]), textarea {
|
||||||
background: linear-gradient(to top, rgb(255, 255, 255) 80%, rgb(240, 240, 240));
|
background: linear-gradient(to top, rgb(255, 255, 255) 80%, rgb(240, 240, 240));
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=button], input[type=submit], select {
|
input[type=button],
|
||||||
|
input[type=color],
|
||||||
|
input[type=reset],
|
||||||
|
input[type=submit],
|
||||||
|
select {
|
||||||
/* Disable default rendering */
|
/* Disable default rendering */
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
-moz-appearance: none;
|
-moz-appearance: none;
|
||||||
|
@ -117,7 +132,10 @@ input[type=button], input[type=submit], select {
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=button], input[type=submit] {
|
input[type=button],
|
||||||
|
input[type=color],
|
||||||
|
input[type=reset],
|
||||||
|
input[type=submit] {
|
||||||
padding-left: 20px;
|
padding-left: 20px;
|
||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
}
|
}
|
||||||
|
@ -127,35 +145,72 @@ option {
|
||||||
background: white;
|
background: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=input]:focus, input[type=password]:focus,
|
input:not([type]):focus,
|
||||||
input:not([type]):focus, input[type=button]:focus,
|
input[type=button]:focus,
|
||||||
|
input[type=color]:focus,
|
||||||
|
input[type=date]:focus,
|
||||||
|
input[type=datetime-local]:focus,
|
||||||
|
input[type=email]:focus,
|
||||||
|
input[type=month]:focus,
|
||||||
|
input[type=number]:focus,
|
||||||
|
input[type=password]:focus,
|
||||||
|
input[type=reset]:focus,
|
||||||
|
input[type=search]:focus,
|
||||||
input[type=submit]:focus,
|
input[type=submit]:focus,
|
||||||
textarea:focus, select:focus {
|
input[type=tel]:focus,
|
||||||
|
input[type=text]:focus,
|
||||||
|
input[type=time]:focus,
|
||||||
|
input[type=url]:focus,
|
||||||
|
input[type=week]:focus,
|
||||||
|
select:focus,
|
||||||
|
textarea:focus {
|
||||||
box-shadow: 0px 0px 3px rgba(74, 144, 217, 0.5);
|
box-shadow: 0px 0px 3px rgba(74, 144, 217, 0.5);
|
||||||
border-color: rgb(74, 144, 217);
|
border-color: rgb(74, 144, 217);
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=button]::-moz-focus-inner,
|
input[type=button]::-moz-focus-inner,
|
||||||
|
input[type=color]::-moz-focus-inner,
|
||||||
|
input[type=reset]::-moz-focus-inner,
|
||||||
input[type=submit]::-moz-focus-inner {
|
input[type=submit]::-moz-focus-inner {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=input]:disabled, input[type=password]:disabled,
|
input:not([type]):disabled,
|
||||||
input:not([type]):disabled, input[type=button]:disabled,
|
input[type=button]:disabled,
|
||||||
input[type=submit]:disabled, input[type=number]:disabled,
|
input[type=color]:disabled,
|
||||||
textarea:disabled, select:disabled {
|
input[type=date]:disabled,
|
||||||
|
input[type=datetime-local]:disabled,
|
||||||
|
input[type=email]:disabled,
|
||||||
|
input[type=month]:disabled,
|
||||||
|
input[type=number]:disabled,
|
||||||
|
input[type=password]:disabled,
|
||||||
|
input[type=reset]:disabled,
|
||||||
|
input[type=search]:disabled,
|
||||||
|
input[type=submit]:disabled,
|
||||||
|
input[type=tel]:disabled,
|
||||||
|
input[type=text]:disabled,
|
||||||
|
input[type=time]:disabled,
|
||||||
|
input[type=url]:disabled,
|
||||||
|
input[type=week]:disabled,
|
||||||
|
select:disabled,
|
||||||
|
textarea:disabled {
|
||||||
color: rgb(128, 128, 128);
|
color: rgb(128, 128, 128);
|
||||||
background: rgb(240, 240, 240);
|
background: rgb(240, 240, 240);
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type=button]:active, input[type=submit]:active,
|
input[type=button]:active,
|
||||||
|
input[type=color]:active,
|
||||||
|
input[type=reset]:active,
|
||||||
|
input[type=submit]:active,
|
||||||
select:active {
|
select:active {
|
||||||
border-bottom-width: 1px;
|
border-bottom-width: 1px;
|
||||||
margin-top: 3px;
|
margin-top: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
:root:not(.noVNC_touch) input[type=button]:hover:not(:disabled),
|
:root:not(.noVNC_touch) input[type=button]:hover:not(:disabled),
|
||||||
|
:root:not(.noVNC_touch) input[type=color]:hover:not(:disabled),
|
||||||
|
:root:not(.noVNC_touch) input[type=reset]:hover:not(:disabled),
|
||||||
:root:not(.noVNC_touch) input[type=submit]:hover:not(:disabled),
|
:root:not(.noVNC_touch) input[type=submit]:hover:not(:disabled),
|
||||||
:root:not(.noVNC_touch) select:hover:not(:disabled) {
|
:root:not(.noVNC_touch) select:hover:not(:disabled) {
|
||||||
background: linear-gradient(to top, rgb(255, 255, 255), rgb(250, 250, 250));
|
background: linear-gradient(to top, rgb(255, 255, 255), rgb(250, 250, 250));
|
||||||
|
@ -580,7 +635,7 @@ select:active {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Extra manual keys */
|
/* Extra manual keys */
|
||||||
:root:not(.noVNC_connected) #noVNC_extra_keys {
|
:root:not(.noVNC_connected) #noVNC_toggle_extra_keys_button {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -632,6 +687,16 @@ select:active {
|
||||||
width: 100px;
|
width: 100px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Version */
|
||||||
|
|
||||||
|
.noVNC_version_wrapper {
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noVNC_version {
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
/* Connection Controls */
|
/* Connection Controls */
|
||||||
:root:not(.noVNC_connected) #noVNC_disconnect_button {
|
:root:not(.noVNC_connected) #noVNC_disconnect_button {
|
||||||
display: none;
|
display: none;
|
||||||
|
@ -781,19 +846,23 @@ select:active {
|
||||||
* ----------------------------------------
|
* ----------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#noVNC_password_dlg {
|
#noVNC_credentials_dlg {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
transform: translateY(-50px);
|
transform: translateY(-50px);
|
||||||
}
|
}
|
||||||
#noVNC_password_dlg.noVNC_open {
|
#noVNC_credentials_dlg.noVNC_open {
|
||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
#noVNC_password_dlg ul {
|
#noVNC_credentials_dlg ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0px;
|
margin: 0px;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
|
.noVNC_hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ----------------------------------------
|
/* ----------------------------------------
|
||||||
* Main Area
|
* Main Area
|
||||||
|
|
|
@ -60,3 +60,8 @@ html {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: flex-end;
|
justify-content: flex-end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#noVNC_container {
|
||||||
|
flex: 1; /* fill remaining space */
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2018 The noVNC Authors
|
* Copyright (C) 2019 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.
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
import * as Log from '../core/util/logging.js';
|
import * as Log from '../core/util/logging.js';
|
||||||
import _, { l10n } from './localization.js';
|
import _, { l10n } from './localization.js';
|
||||||
import { isTouchDevice, isSafari, isIOS, isAndroid, dragThreshold }
|
import { isTouchDevice, isSafari, hasScrollbarGutter, dragThreshold }
|
||||||
from '../core/util/browser.js';
|
from '../core/util/browser.js';
|
||||||
import { setCapture, getPointerEvent } from '../core/util/events.js';
|
import { setCapture, getPointerEvent } from '../core/util/events.js';
|
||||||
import KeyTable from "../core/input/keysym.js";
|
import KeyTable from "../core/input/keysym.js";
|
||||||
|
@ -17,6 +17,8 @@ import Keyboard from "../core/input/keyboard.js";
|
||||||
import RFB from "../core/rfb.js";
|
import RFB from "../core/rfb.js";
|
||||||
import * as WebUtil from "./webutil.js";
|
import * as WebUtil from "./webutil.js";
|
||||||
|
|
||||||
|
const PAGE_TITLE = "noVNC";
|
||||||
|
|
||||||
const UI = {
|
const UI = {
|
||||||
|
|
||||||
connected: false,
|
connected: false,
|
||||||
|
@ -35,9 +37,9 @@ const UI = {
|
||||||
lastKeyboardinput: null,
|
lastKeyboardinput: null,
|
||||||
defaultKeyboardinputLen: 100,
|
defaultKeyboardinputLen: 100,
|
||||||
|
|
||||||
inhibit_reconnect: true,
|
inhibitReconnect: true,
|
||||||
reconnect_callback: null,
|
reconnectCallback: null,
|
||||||
reconnect_password: null,
|
reconnectPassword: null,
|
||||||
|
|
||||||
prime() {
|
prime() {
|
||||||
return WebUtil.initSettings().then(() => {
|
return WebUtil.initSettings().then(() => {
|
||||||
|
@ -59,6 +61,17 @@ const UI = {
|
||||||
// Translate the DOM
|
// Translate the DOM
|
||||||
l10n.translateDOM();
|
l10n.translateDOM();
|
||||||
|
|
||||||
|
WebUtil.fetchJSON('/static/js/novnc/package.json')
|
||||||
|
.then((packageInfo) => {
|
||||||
|
Array.from(document.getElementsByClassName('noVNC_version')).forEach(el => el.innerText = packageInfo.version);
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
Log.Error("Couldn't fetch package.json: " + err);
|
||||||
|
Array.from(document.getElementsByClassName('noVNC_version_wrapper'))
|
||||||
|
.concat(Array.from(document.getElementsByClassName('noVNC_version_separator')))
|
||||||
|
.forEach(el => el.style.display = 'none');
|
||||||
|
});
|
||||||
|
|
||||||
// Adapt the interface for touch screen devices
|
// Adapt the interface for touch screen devices
|
||||||
if (isTouchDevice) {
|
if (isTouchDevice) {
|
||||||
document.documentElement.classList.add("noVNC_touch");
|
document.documentElement.classList.add("noVNC_touch");
|
||||||
|
@ -148,6 +161,8 @@ const UI = {
|
||||||
UI.initSetting('encrypt', (window.location.protocol === "https:"));
|
UI.initSetting('encrypt', (window.location.protocol === "https:"));
|
||||||
UI.initSetting('view_clip', false);
|
UI.initSetting('view_clip', false);
|
||||||
UI.initSetting('resize', 'off');
|
UI.initSetting('resize', 'off');
|
||||||
|
UI.initSetting('quality', 6);
|
||||||
|
UI.initSetting('compression', 2);
|
||||||
UI.initSetting('shared', true);
|
UI.initSetting('shared', true);
|
||||||
UI.initSetting('view_only', false);
|
UI.initSetting('view_only', false);
|
||||||
UI.initSetting('show_dot', false);
|
UI.initSetting('show_dot', false);
|
||||||
|
@ -219,14 +234,6 @@ const UI = {
|
||||||
},
|
},
|
||||||
|
|
||||||
addTouchSpecificHandlers() {
|
addTouchSpecificHandlers() {
|
||||||
document.getElementById("noVNC_mouse_button0")
|
|
||||||
.addEventListener('click', () => UI.setMouseButton(1));
|
|
||||||
document.getElementById("noVNC_mouse_button1")
|
|
||||||
.addEventListener('click', () => UI.setMouseButton(2));
|
|
||||||
document.getElementById("noVNC_mouse_button2")
|
|
||||||
.addEventListener('click', () => UI.setMouseButton(4));
|
|
||||||
document.getElementById("noVNC_mouse_button4")
|
|
||||||
.addEventListener('click', () => UI.setMouseButton(0));
|
|
||||||
document.getElementById("noVNC_keyboard_button")
|
document.getElementById("noVNC_keyboard_button")
|
||||||
.addEventListener('click', UI.toggleVirtualKeyboard);
|
.addEventListener('click', UI.toggleVirtualKeyboard);
|
||||||
|
|
||||||
|
@ -282,33 +289,6 @@ const UI = {
|
||||||
.addEventListener('click', UI.sendEsc);
|
.addEventListener('click', UI.sendEsc);
|
||||||
document.getElementById("noVNC_send_ctrl_alt_del_button")
|
document.getElementById("noVNC_send_ctrl_alt_del_button")
|
||||||
.addEventListener('click', UI.sendCtrlAltDel);
|
.addEventListener('click', UI.sendCtrlAltDel);
|
||||||
|
|
||||||
document.getElementById('ctrlaltdel')
|
|
||||||
.addEventListener('click', UI.sendCtrlAltDel);
|
|
||||||
document.getElementById('ctrlaltf1')
|
|
||||||
.addEventListener('click', () => UI.sendCtrlAltFN(0));
|
|
||||||
document.getElementById('ctrlaltf2')
|
|
||||||
.addEventListener('click', () => UI.sendCtrlAltFN(1));
|
|
||||||
document.getElementById('ctrlaltf3')
|
|
||||||
.addEventListener('click', () => UI.sendCtrlAltFN(2));
|
|
||||||
document.getElementById('ctrlaltf4')
|
|
||||||
.addEventListener('click', () => UI.sendCtrlAltFN(3));
|
|
||||||
document.getElementById('ctrlaltf5')
|
|
||||||
.addEventListener('click', () => UI.sendCtrlAltFN(4));
|
|
||||||
document.getElementById('ctrlaltf6')
|
|
||||||
.addEventListener('click', () => UI.sendCtrlAltFN(5));
|
|
||||||
document.getElementById('ctrlaltf7')
|
|
||||||
.addEventListener('click', () => UI.sendCtrlAltFN(6));
|
|
||||||
document.getElementById('ctrlaltf8')
|
|
||||||
.addEventListener('click', () => UI.sendCtrlAltFN(7));
|
|
||||||
document.getElementById('ctrlaltf9')
|
|
||||||
.addEventListener('click', () => UI.sendCtrlAltFN(8));
|
|
||||||
document.getElementById('ctrlaltf10')
|
|
||||||
.addEventListener('click', () => UI.sendCtrlAltFN(9));
|
|
||||||
document.getElementById('ctrlaltf11')
|
|
||||||
.addEventListener('click', () => UI.sendCtrlAltFN(10));
|
|
||||||
document.getElementById('ctrlaltf12')
|
|
||||||
.addEventListener('click', () => UI.sendCtrlAltFN(11));
|
|
||||||
},
|
},
|
||||||
|
|
||||||
addMachineHandlers() {
|
addMachineHandlers() {
|
||||||
|
@ -330,8 +310,8 @@ const UI = {
|
||||||
document.getElementById("noVNC_cancel_reconnect_button")
|
document.getElementById("noVNC_cancel_reconnect_button")
|
||||||
.addEventListener('click', UI.cancelReconnect);
|
.addEventListener('click', UI.cancelReconnect);
|
||||||
|
|
||||||
document.getElementById("noVNC_password_button")
|
document.getElementById("noVNC_credentials_button")
|
||||||
.addEventListener('click', UI.setPassword);
|
.addEventListener('click', UI.setCredentials);
|
||||||
},
|
},
|
||||||
|
|
||||||
addClipboardHandlers() {
|
addClipboardHandlers() {
|
||||||
|
@ -361,6 +341,10 @@ const UI = {
|
||||||
UI.addSettingChangeHandler('resize');
|
UI.addSettingChangeHandler('resize');
|
||||||
UI.addSettingChangeHandler('resize', UI.applyResizeMode);
|
UI.addSettingChangeHandler('resize', UI.applyResizeMode);
|
||||||
UI.addSettingChangeHandler('resize', UI.updateViewClip);
|
UI.addSettingChangeHandler('resize', UI.updateViewClip);
|
||||||
|
UI.addSettingChangeHandler('quality');
|
||||||
|
UI.addSettingChangeHandler('quality', UI.updateQuality);
|
||||||
|
UI.addSettingChangeHandler('compression');
|
||||||
|
UI.addSettingChangeHandler('compression', UI.updateCompression);
|
||||||
UI.addSettingChangeHandler('view_clip');
|
UI.addSettingChangeHandler('view_clip');
|
||||||
UI.addSettingChangeHandler('view_clip', UI.updateViewClip);
|
UI.addSettingChangeHandler('view_clip', UI.updateViewClip);
|
||||||
UI.addSettingChangeHandler('shared');
|
UI.addSettingChangeHandler('shared');
|
||||||
|
@ -381,8 +365,6 @@ const UI = {
|
||||||
addFullscreenHandlers() {
|
addFullscreenHandlers() {
|
||||||
document.getElementById("noVNC_fullscreen_button")
|
document.getElementById("noVNC_fullscreen_button")
|
||||||
.addEventListener('click', UI.toggleFullscreen);
|
.addEventListener('click', UI.toggleFullscreen);
|
||||||
document.getElementById("fullscreen_button")
|
|
||||||
.addEventListener('click', UI.toggleFullscreen);
|
|
||||||
|
|
||||||
window.addEventListener('fullscreenchange', UI.updateFullscreenButton);
|
window.addEventListener('fullscreenchange', UI.updateFullscreenButton);
|
||||||
window.addEventListener('mozfullscreenchange', UI.updateFullscreenButton);
|
window.addEventListener('mozfullscreenchange', UI.updateFullscreenButton);
|
||||||
|
@ -404,25 +386,25 @@ const UI = {
|
||||||
document.documentElement.classList.remove("noVNC_disconnecting");
|
document.documentElement.classList.remove("noVNC_disconnecting");
|
||||||
document.documentElement.classList.remove("noVNC_reconnecting");
|
document.documentElement.classList.remove("noVNC_reconnecting");
|
||||||
|
|
||||||
const transition_elem = document.getElementById("noVNC_transition_text");
|
const transitionElem = document.getElementById("noVNC_transition_text");
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case 'init':
|
case 'init':
|
||||||
break;
|
break;
|
||||||
case 'connecting':
|
case 'connecting':
|
||||||
transition_elem.textContent = _("Connecting...");
|
transitionElem.textContent = _("Connecting...");
|
||||||
document.documentElement.classList.add("noVNC_connecting");
|
document.documentElement.classList.add("noVNC_connecting");
|
||||||
break;
|
break;
|
||||||
case 'connected':
|
case 'connected':
|
||||||
document.documentElement.classList.add("noVNC_connected");
|
document.documentElement.classList.add("noVNC_connected");
|
||||||
break;
|
break;
|
||||||
case 'disconnecting':
|
case 'disconnecting':
|
||||||
transition_elem.textContent = _("Disconnecting...");
|
transitionElem.textContent = _("Disconnecting...");
|
||||||
document.documentElement.classList.add("noVNC_disconnecting");
|
document.documentElement.classList.add("noVNC_disconnecting");
|
||||||
break;
|
break;
|
||||||
case 'disconnected':
|
case 'disconnected':
|
||||||
break;
|
break;
|
||||||
case 'reconnecting':
|
case 'reconnecting':
|
||||||
transition_elem.textContent = _("Reconnecting...");
|
transitionElem.textContent = _("Reconnecting...");
|
||||||
document.documentElement.classList.add("noVNC_reconnecting");
|
document.documentElement.classList.add("noVNC_reconnecting");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -440,7 +422,6 @@ const UI = {
|
||||||
UI.disableSetting('port');
|
UI.disableSetting('port');
|
||||||
UI.disableSetting('path');
|
UI.disableSetting('path');
|
||||||
UI.disableSetting('repeaterID');
|
UI.disableSetting('repeaterID');
|
||||||
UI.setMouseButton(1);
|
|
||||||
|
|
||||||
// Hide the controlbar after 2 seconds
|
// Hide the controlbar after 2 seconds
|
||||||
UI.closeControlbarTimeout = setTimeout(UI.closeControlbar, 2000);
|
UI.closeControlbarTimeout = setTimeout(UI.closeControlbar, 2000);
|
||||||
|
@ -455,38 +436,35 @@ const UI = {
|
||||||
UI.keepControlbar();
|
UI.keepControlbar();
|
||||||
}
|
}
|
||||||
|
|
||||||
// State change closes the password dialog
|
// State change closes dialogs as they may not be relevant
|
||||||
document.getElementById('noVNC_password_dlg')
|
// anymore
|
||||||
|
UI.closeAllPanels();
|
||||||
|
document.getElementById('noVNC_credentials_dlg')
|
||||||
.classList.remove('noVNC_open');
|
.classList.remove('noVNC_open');
|
||||||
},
|
},
|
||||||
|
|
||||||
showStatus(text, status_type, time) {
|
showStatus(text, statusType, time) {
|
||||||
const statusElem = document.getElementById('noVNC_status');
|
const statusElem = document.getElementById('noVNC_status');
|
||||||
|
|
||||||
clearTimeout(UI.statusTimeout);
|
if (typeof statusType === 'undefined') {
|
||||||
|
statusType = 'normal';
|
||||||
if (typeof status_type === 'undefined') {
|
|
||||||
status_type = 'normal';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't overwrite more severe visible statuses and never
|
// Don't overwrite more severe visible statuses and never
|
||||||
// errors. Only shows the first error.
|
// errors. Only shows the first error.
|
||||||
let visible_status_type = 'none';
|
|
||||||
if (statusElem.classList.contains("noVNC_open")) {
|
if (statusElem.classList.contains("noVNC_open")) {
|
||||||
if (statusElem.classList.contains("noVNC_status_error")) {
|
if (statusElem.classList.contains("noVNC_status_error")) {
|
||||||
visible_status_type = 'error';
|
|
||||||
} else if (statusElem.classList.contains("noVNC_status_warn")) {
|
|
||||||
visible_status_type = 'warn';
|
|
||||||
} else {
|
|
||||||
visible_status_type = 'normal';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (visible_status_type === 'error' ||
|
|
||||||
(visible_status_type === 'warn' && status_type === 'normal')) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (statusElem.classList.contains("noVNC_status_warn") &&
|
||||||
|
statusType === 'normal') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
switch (status_type) {
|
clearTimeout(UI.statusTimeout);
|
||||||
|
|
||||||
|
switch (statusType) {
|
||||||
case 'error':
|
case 'error':
|
||||||
statusElem.classList.remove("noVNC_status_warn");
|
statusElem.classList.remove("noVNC_status_warn");
|
||||||
statusElem.classList.remove("noVNC_status_normal");
|
statusElem.classList.remove("noVNC_status_normal");
|
||||||
|
@ -516,7 +494,7 @@ const UI = {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error messages do not timeout
|
// Error messages do not timeout
|
||||||
if (status_type !== 'error') {
|
if (statusType !== 'error') {
|
||||||
UI.statusTimeout = window.setTimeout(UI.hideStatus, time);
|
UI.statusTimeout = window.setTimeout(UI.hideStatus, time);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -536,6 +514,13 @@ const UI = {
|
||||||
},
|
},
|
||||||
|
|
||||||
idleControlbar() {
|
idleControlbar() {
|
||||||
|
// Don't fade if a child of the control bar has focus
|
||||||
|
if (document.getElementById('noVNC_control_bar')
|
||||||
|
.contains(document.activeElement) && document.hasFocus()) {
|
||||||
|
UI.activateControlbar();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
document.getElementById('noVNC_control_bar_anchor')
|
document.getElementById('noVNC_control_bar_anchor')
|
||||||
.classList.add("noVNC_idle");
|
.classList.add("noVNC_idle");
|
||||||
},
|
},
|
||||||
|
@ -553,6 +538,7 @@ const UI = {
|
||||||
UI.closeAllPanels();
|
UI.closeAllPanels();
|
||||||
document.getElementById('noVNC_control_bar')
|
document.getElementById('noVNC_control_bar')
|
||||||
.classList.remove("noVNC_open");
|
.classList.remove("noVNC_open");
|
||||||
|
UI.rfb.focus();
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleControlbar() {
|
toggleControlbar() {
|
||||||
|
@ -850,6 +836,8 @@ const UI = {
|
||||||
UI.updateSetting('encrypt');
|
UI.updateSetting('encrypt');
|
||||||
UI.updateSetting('view_clip');
|
UI.updateSetting('view_clip');
|
||||||
UI.updateSetting('resize');
|
UI.updateSetting('resize');
|
||||||
|
UI.updateSetting('quality');
|
||||||
|
UI.updateSetting('compression');
|
||||||
UI.updateSetting('shared');
|
UI.updateSetting('shared');
|
||||||
UI.updateSetting('view_only');
|
UI.updateSetting('view_only');
|
||||||
UI.updateSetting('path');
|
UI.updateSetting('path');
|
||||||
|
@ -1006,7 +994,7 @@ const UI = {
|
||||||
|
|
||||||
if (typeof password === 'undefined') {
|
if (typeof password === 'undefined') {
|
||||||
password = WebUtil.getConfigVar('password');
|
password = WebUtil.getConfigVar('password');
|
||||||
UI.reconnect_password = password;
|
UI.reconnectPassword = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (password === null) {
|
if (password === null) {
|
||||||
|
@ -1021,7 +1009,6 @@ const UI = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UI.closeAllPanels();
|
|
||||||
UI.closeConnectPanel();
|
UI.closeConnectPanel();
|
||||||
|
|
||||||
UI.updateVisualState('connecting');
|
UI.updateVisualState('connecting');
|
||||||
|
@ -1038,7 +1025,6 @@ const UI = {
|
||||||
|
|
||||||
UI.rfb = new RFB(document.getElementById('noVNC_container'), url,
|
UI.rfb = new RFB(document.getElementById('noVNC_container'), url,
|
||||||
{ shared: UI.getSetting('shared'),
|
{ shared: UI.getSetting('shared'),
|
||||||
showDotCursor: UI.getSetting('show_dot'),
|
|
||||||
repeaterID: UI.getSetting('repeaterID'),
|
repeaterID: UI.getSetting('repeaterID'),
|
||||||
credentials: { password: password } });
|
credentials: { password: password } });
|
||||||
UI.rfb.addEventListener("connect", UI.connectFinished);
|
UI.rfb.addEventListener("connect", UI.connectFinished);
|
||||||
|
@ -1052,18 +1038,20 @@ const UI = {
|
||||||
UI.rfb.clipViewport = UI.getSetting('view_clip');
|
UI.rfb.clipViewport = UI.getSetting('view_clip');
|
||||||
UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale';
|
UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale';
|
||||||
UI.rfb.resizeSession = UI.getSetting('resize') === 'remote';
|
UI.rfb.resizeSession = UI.getSetting('resize') === 'remote';
|
||||||
|
UI.rfb.qualityLevel = parseInt(UI.getSetting('quality'));
|
||||||
|
UI.rfb.compressionLevel = parseInt(UI.getSetting('compression'));
|
||||||
|
UI.rfb.showDotCursor = UI.getSetting('show_dot');
|
||||||
|
|
||||||
UI.updateViewOnly(); // requires UI.rfb
|
UI.updateViewOnly(); // requires UI.rfb
|
||||||
},
|
},
|
||||||
|
|
||||||
disconnect() {
|
disconnect() {
|
||||||
UI.closeAllPanels();
|
|
||||||
UI.rfb.disconnect();
|
UI.rfb.disconnect();
|
||||||
|
|
||||||
UI.connected = false;
|
UI.connected = false;
|
||||||
|
|
||||||
// Disable automatic reconnecting
|
// Disable automatic reconnecting
|
||||||
UI.inhibit_reconnect = true;
|
UI.inhibitReconnect = true;
|
||||||
|
|
||||||
UI.updateVisualState('disconnecting');
|
UI.updateVisualState('disconnecting');
|
||||||
|
|
||||||
|
@ -1071,20 +1059,20 @@ const UI = {
|
||||||
},
|
},
|
||||||
|
|
||||||
reconnect() {
|
reconnect() {
|
||||||
UI.reconnect_callback = null;
|
UI.reconnectCallback = null;
|
||||||
|
|
||||||
// if reconnect has been disabled in the meantime, do nothing.
|
// if reconnect has been disabled in the meantime, do nothing.
|
||||||
if (UI.inhibit_reconnect) {
|
if (UI.inhibitReconnect) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UI.connect(null, UI.reconnect_password);
|
UI.connect(null, UI.reconnectPassword);
|
||||||
},
|
},
|
||||||
|
|
||||||
cancelReconnect() {
|
cancelReconnect() {
|
||||||
if (UI.reconnect_callback !== null) {
|
if (UI.reconnectCallback !== null) {
|
||||||
clearTimeout(UI.reconnect_callback);
|
clearTimeout(UI.reconnectCallback);
|
||||||
UI.reconnect_callback = null;
|
UI.reconnectCallback = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
UI.updateVisualState('disconnected');
|
UI.updateVisualState('disconnected');
|
||||||
|
@ -1095,7 +1083,7 @@ const UI = {
|
||||||
|
|
||||||
connectFinished(e) {
|
connectFinished(e) {
|
||||||
UI.connected = true;
|
UI.connected = true;
|
||||||
UI.inhibit_reconnect = false;
|
UI.inhibitReconnect = false;
|
||||||
|
|
||||||
let msg;
|
let msg;
|
||||||
if (UI.getSetting('encrypt')) {
|
if (UI.getSetting('encrypt')) {
|
||||||
|
@ -1129,17 +1117,19 @@ const UI = {
|
||||||
} else {
|
} else {
|
||||||
UI.showStatus(_("Failed to connect to server"), 'error');
|
UI.showStatus(_("Failed to connect to server"), 'error');
|
||||||
}
|
}
|
||||||
} else if (UI.getSetting('reconnect', false) === true && !UI.inhibit_reconnect) {
|
} else if (UI.getSetting('reconnect', false) === true && !UI.inhibitReconnect) {
|
||||||
UI.updateVisualState('reconnecting');
|
UI.updateVisualState('reconnecting');
|
||||||
|
|
||||||
const delay = parseInt(UI.getSetting('reconnect_delay'));
|
const delay = parseInt(UI.getSetting('reconnect_delay'));
|
||||||
UI.reconnect_callback = setTimeout(UI.reconnect, delay);
|
UI.reconnectCallback = setTimeout(UI.reconnect, delay);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
UI.updateVisualState('disconnected');
|
UI.updateVisualState('disconnected');
|
||||||
UI.showStatus(_("Disconnected"), 'normal');
|
UI.showStatus(_("Disconnected"), 'normal');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.title = PAGE_TITLE;
|
||||||
|
|
||||||
UI.openControlbar();
|
UI.openControlbar();
|
||||||
UI.openConnectPanel();
|
UI.openConnectPanel();
|
||||||
},
|
},
|
||||||
|
@ -1166,27 +1156,46 @@ const UI = {
|
||||||
|
|
||||||
credentials(e) {
|
credentials(e) {
|
||||||
// FIXME: handle more types
|
// FIXME: handle more types
|
||||||
document.getElementById('noVNC_password_dlg')
|
|
||||||
|
document.getElementById("noVNC_username_block").classList.remove("noVNC_hidden");
|
||||||
|
document.getElementById("noVNC_password_block").classList.remove("noVNC_hidden");
|
||||||
|
|
||||||
|
let inputFocus = "none";
|
||||||
|
if (e.detail.types.indexOf("username") === -1) {
|
||||||
|
document.getElementById("noVNC_username_block").classList.add("noVNC_hidden");
|
||||||
|
} else {
|
||||||
|
inputFocus = inputFocus === "none" ? "noVNC_username_input" : inputFocus;
|
||||||
|
}
|
||||||
|
if (e.detail.types.indexOf("password") === -1) {
|
||||||
|
document.getElementById("noVNC_password_block").classList.add("noVNC_hidden");
|
||||||
|
} else {
|
||||||
|
inputFocus = inputFocus === "none" ? "noVNC_password_input" : inputFocus;
|
||||||
|
}
|
||||||
|
document.getElementById('noVNC_credentials_dlg')
|
||||||
.classList.add('noVNC_open');
|
.classList.add('noVNC_open');
|
||||||
|
|
||||||
setTimeout(() => document
|
setTimeout(() => document
|
||||||
.getElementById('noVNC_password_input').focus(), 100);
|
.getElementById(inputFocus).focus(), 100);
|
||||||
|
|
||||||
Log.Warn("Server asked for a password");
|
Log.Warn("Server asked for credentials");
|
||||||
UI.showStatus(_("Password is required"), "warning");
|
UI.showStatus(_("Credentials are required"), "warning");
|
||||||
},
|
},
|
||||||
|
|
||||||
setPassword(e) {
|
setCredentials(e) {
|
||||||
// Prevent actually submitting the form
|
// Prevent actually submitting the form
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
const inputElem = document.getElementById('noVNC_password_input');
|
let inputElemUsername = document.getElementById('noVNC_username_input');
|
||||||
const password = inputElem.value;
|
const username = inputElemUsername.value;
|
||||||
|
|
||||||
|
let inputElemPassword = document.getElementById('noVNC_password_input');
|
||||||
|
const password = inputElemPassword.value;
|
||||||
// Clear the input after reading the password
|
// Clear the input after reading the password
|
||||||
inputElem.value = "";
|
inputElemPassword.value = "";
|
||||||
UI.rfb.sendCredentials({ password: password });
|
|
||||||
UI.reconnect_password = password;
|
UI.rfb.sendCredentials({ username: username, password: password });
|
||||||
document.getElementById('noVNC_password_dlg')
|
UI.reconnectPassword = password;
|
||||||
|
document.getElementById('noVNC_credentials_dlg')
|
||||||
.classList.remove('noVNC_open');
|
.classList.remove('noVNC_open');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1269,8 +1278,9 @@ const UI = {
|
||||||
// Can't be clipping if viewport is scaled to fit
|
// Can't be clipping if viewport is scaled to fit
|
||||||
UI.forceSetting('view_clip', false);
|
UI.forceSetting('view_clip', false);
|
||||||
UI.rfb.clipViewport = false;
|
UI.rfb.clipViewport = false;
|
||||||
} else if (isIOS() || isAndroid()) {
|
} else if (!hasScrollbarGutter) {
|
||||||
// iOS and Android usually have shit scrollbars
|
// Some platforms have scrollbars that are difficult
|
||||||
|
// to use in our case, so we always use our own panning
|
||||||
UI.forceSetting('view_clip', true);
|
UI.forceSetting('view_clip', true);
|
||||||
UI.rfb.clipViewport = true;
|
UI.rfb.clipViewport = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -1313,30 +1323,40 @@ const UI = {
|
||||||
viewDragButton.classList.remove("noVNC_selected");
|
viewDragButton.classList.remove("noVNC_selected");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Different behaviour for touch vs non-touch
|
|
||||||
// The button is disabled instead of hidden on touch devices
|
|
||||||
if (isTouchDevice) {
|
|
||||||
viewDragButton.classList.remove("noVNC_hidden");
|
|
||||||
|
|
||||||
if (UI.rfb.clipViewport) {
|
|
||||||
viewDragButton.disabled = false;
|
|
||||||
} else {
|
|
||||||
viewDragButton.disabled = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
viewDragButton.disabled = false;
|
|
||||||
|
|
||||||
if (UI.rfb.clipViewport) {
|
if (UI.rfb.clipViewport) {
|
||||||
viewDragButton.classList.remove("noVNC_hidden");
|
viewDragButton.classList.remove("noVNC_hidden");
|
||||||
} else {
|
} else {
|
||||||
viewDragButton.classList.add("noVNC_hidden");
|
viewDragButton.classList.add("noVNC_hidden");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/* ------^-------
|
/* ------^-------
|
||||||
* /VIEWDRAG
|
* /VIEWDRAG
|
||||||
* ==============
|
* ==============
|
||||||
|
* QUALITY
|
||||||
|
* ------v------*/
|
||||||
|
|
||||||
|
updateQuality() {
|
||||||
|
if (!UI.rfb) return;
|
||||||
|
|
||||||
|
UI.rfb.qualityLevel = parseInt(UI.getSetting('quality'));
|
||||||
|
},
|
||||||
|
|
||||||
|
/* ------^-------
|
||||||
|
* /QUALITY
|
||||||
|
* ==============
|
||||||
|
* COMPRESSION
|
||||||
|
* ------v------*/
|
||||||
|
|
||||||
|
updateCompression() {
|
||||||
|
if (!UI.rfb) return;
|
||||||
|
|
||||||
|
UI.rfb.compressionLevel = parseInt(UI.getSetting('compression'));
|
||||||
|
},
|
||||||
|
|
||||||
|
/* ------^-------
|
||||||
|
* /COMPRESSION
|
||||||
|
* ==============
|
||||||
* KEYBOARD
|
* KEYBOARD
|
||||||
* ------v------*/
|
* ------v------*/
|
||||||
|
|
||||||
|
@ -1531,20 +1551,20 @@ const UI = {
|
||||||
},
|
},
|
||||||
|
|
||||||
sendEsc() {
|
sendEsc() {
|
||||||
UI.rfb.sendKey(KeyTable.XK_Escape, "Escape");
|
UI.sendKey(KeyTable.XK_Escape, "Escape");
|
||||||
},
|
},
|
||||||
|
|
||||||
sendTab() {
|
sendTab() {
|
||||||
UI.rfb.sendKey(KeyTable.XK_Tab);
|
UI.sendKey(KeyTable.XK_Tab, "Tab");
|
||||||
},
|
},
|
||||||
|
|
||||||
toggleCtrl() {
|
toggleCtrl() {
|
||||||
const btn = document.getElementById('noVNC_toggle_ctrl_button');
|
const btn = document.getElementById('noVNC_toggle_ctrl_button');
|
||||||
if (btn.classList.contains("noVNC_selected")) {
|
if (btn.classList.contains("noVNC_selected")) {
|
||||||
UI.rfb.sendKey(KeyTable.XK_Control_L, "ControlLeft", false);
|
UI.sendKey(KeyTable.XK_Control_L, "ControlLeft", false);
|
||||||
btn.classList.remove("noVNC_selected");
|
btn.classList.remove("noVNC_selected");
|
||||||
} else {
|
} else {
|
||||||
UI.rfb.sendKey(KeyTable.XK_Control_L, "ControlLeft", true);
|
UI.sendKey(KeyTable.XK_Control_L, "ControlLeft", true);
|
||||||
btn.classList.add("noVNC_selected");
|
btn.classList.add("noVNC_selected");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1552,10 +1572,10 @@ const UI = {
|
||||||
toggleWindows() {
|
toggleWindows() {
|
||||||
const btn = document.getElementById('noVNC_toggle_windows_button');
|
const btn = document.getElementById('noVNC_toggle_windows_button');
|
||||||
if (btn.classList.contains("noVNC_selected")) {
|
if (btn.classList.contains("noVNC_selected")) {
|
||||||
UI.rfb.sendKey(KeyTable.XK_Super_L, "MetaLeft", false);
|
UI.sendKey(KeyTable.XK_Super_L, "MetaLeft", false);
|
||||||
btn.classList.remove("noVNC_selected");
|
btn.classList.remove("noVNC_selected");
|
||||||
} else {
|
} else {
|
||||||
UI.rfb.sendKey(KeyTable.XK_Super_L, "MetaLeft", true);
|
UI.sendKey(KeyTable.XK_Super_L, "MetaLeft", true);
|
||||||
btn.classList.add("noVNC_selected");
|
btn.classList.add("noVNC_selected");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1563,20 +1583,39 @@ const UI = {
|
||||||
toggleAlt() {
|
toggleAlt() {
|
||||||
const btn = document.getElementById('noVNC_toggle_alt_button');
|
const btn = document.getElementById('noVNC_toggle_alt_button');
|
||||||
if (btn.classList.contains("noVNC_selected")) {
|
if (btn.classList.contains("noVNC_selected")) {
|
||||||
UI.rfb.sendKey(KeyTable.XK_Alt_L, "AltLeft", false);
|
UI.sendKey(KeyTable.XK_Alt_L, "AltLeft", false);
|
||||||
btn.classList.remove("noVNC_selected");
|
btn.classList.remove("noVNC_selected");
|
||||||
} else {
|
} else {
|
||||||
UI.rfb.sendKey(KeyTable.XK_Alt_L, "AltLeft", true);
|
UI.sendKey(KeyTable.XK_Alt_L, "AltLeft", true);
|
||||||
btn.classList.add("noVNC_selected");
|
btn.classList.add("noVNC_selected");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
sendCtrlAltDel() {
|
sendCtrlAltDel() {
|
||||||
UI.rfb.sendCtrlAltDel();
|
UI.rfb.sendCtrlAltDel();
|
||||||
|
// See below
|
||||||
|
UI.rfb.focus();
|
||||||
|
UI.idleControlbar();
|
||||||
},
|
},
|
||||||
|
|
||||||
sendCtrlAltFN: function(f) {
|
sendKey(keysym, code, down) {
|
||||||
UI.rfb.sendCtrlAltFN(f);
|
UI.rfb.sendKey(keysym, code, down);
|
||||||
|
|
||||||
|
// Move focus to the screen in order to be able to use the
|
||||||
|
// keyboard right after these extra keys.
|
||||||
|
// The exception is when a virtual keyboard is used, because
|
||||||
|
// if we focus the screen the virtual keyboard would be closed.
|
||||||
|
// In this case we focus our special virtual keyboard input
|
||||||
|
// element instead.
|
||||||
|
if (document.getElementById('noVNC_keyboard_button')
|
||||||
|
.classList.contains("noVNC_selected")) {
|
||||||
|
document.getElementById('noVNC_keyboardinput').focus();
|
||||||
|
} else {
|
||||||
|
UI.rfb.focus();
|
||||||
|
}
|
||||||
|
// fade out the controlbar to highlight that
|
||||||
|
// the focus has been moved to the screen
|
||||||
|
UI.idleControlbar();
|
||||||
},
|
},
|
||||||
|
|
||||||
/* ------^-------
|
/* ------^-------
|
||||||
|
@ -1585,24 +1624,6 @@ const UI = {
|
||||||
* MISC
|
* MISC
|
||||||
* ------v------*/
|
* ------v------*/
|
||||||
|
|
||||||
setMouseButton(num) {
|
|
||||||
const view_only = UI.rfb.viewOnly;
|
|
||||||
if (UI.rfb && !view_only) {
|
|
||||||
UI.rfb.touchButton = num;
|
|
||||||
}
|
|
||||||
|
|
||||||
const blist = [0, 1, 2, 4];
|
|
||||||
for (let b = 0; b < blist.length; b++) {
|
|
||||||
const button = document.getElementById('noVNC_mouse_button' +
|
|
||||||
blist[b]);
|
|
||||||
if (blist[b] === num && !view_only) {
|
|
||||||
button.classList.remove("noVNC_hidden");
|
|
||||||
} else {
|
|
||||||
button.classList.add("noVNC_hidden");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
updateViewOnly() {
|
updateViewOnly() {
|
||||||
if (!UI.rfb) return;
|
if (!UI.rfb) return;
|
||||||
UI.rfb.viewOnly = UI.getSetting('view_only');
|
UI.rfb.viewOnly = UI.getSetting('view_only');
|
||||||
|
@ -1613,14 +1634,14 @@ const UI = {
|
||||||
.classList.add('noVNC_hidden');
|
.classList.add('noVNC_hidden');
|
||||||
document.getElementById('noVNC_toggle_extra_keys_button')
|
document.getElementById('noVNC_toggle_extra_keys_button')
|
||||||
.classList.add('noVNC_hidden');
|
.classList.add('noVNC_hidden');
|
||||||
document.getElementById('noVNC_mouse_button' + UI.rfb.touchButton)
|
document.getElementById('noVNC_clipboard_button')
|
||||||
.classList.add('noVNC_hidden');
|
.classList.add('noVNC_hidden');
|
||||||
} else {
|
} else {
|
||||||
document.getElementById('noVNC_keyboard_button')
|
document.getElementById('noVNC_keyboard_button')
|
||||||
.classList.remove('noVNC_hidden');
|
.classList.remove('noVNC_hidden');
|
||||||
document.getElementById('noVNC_toggle_extra_keys_button')
|
document.getElementById('noVNC_toggle_extra_keys_button')
|
||||||
.classList.remove('noVNC_hidden');
|
.classList.remove('noVNC_hidden');
|
||||||
document.getElementById('noVNC_mouse_button' + UI.rfb.touchButton)
|
document.getElementById('noVNC_clipboard_button')
|
||||||
.classList.remove('noVNC_hidden');
|
.classList.remove('noVNC_hidden');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1631,13 +1652,13 @@ const UI = {
|
||||||
},
|
},
|
||||||
|
|
||||||
updateLogging() {
|
updateLogging() {
|
||||||
WebUtil.init_logging(UI.getSetting('logging'));
|
WebUtil.initLogging(UI.getSetting('logging'));
|
||||||
},
|
},
|
||||||
|
|
||||||
updateDesktopName(e) {
|
updateDesktopName(e) {
|
||||||
UI.desktopName = e.detail.name;
|
UI.desktopName = e.detail.name;
|
||||||
// Display the desktop name in the document title
|
// Display the desktop name in the document title
|
||||||
document.title = e.detail.name + " - noVNC";
|
document.title = e.detail.name + " - " + PAGE_TITLE;
|
||||||
},
|
},
|
||||||
|
|
||||||
bell(e) {
|
bell(e) {
|
||||||
|
@ -1673,7 +1694,7 @@ const UI = {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set up translations
|
// Set up translations
|
||||||
const LINGUAS = ["cs", "de", "el", "es", "ko", "nl", "pl", "ru", "sv", "tr", "zh_CN", "zh_TW"];
|
const LINGUAS = ["cs", "de", "el", "es", "ja", "ko", "nl", "pl", "ru", "sv", "tr", "zh_CN", "zh_TW"];
|
||||||
l10n.setup(LINGUAS);
|
l10n.setup(LINGUAS);
|
||||||
if (l10n.language === "en" || l10n.dictionary !== undefined) {
|
if (l10n.language === "en" || l10n.dictionary !== undefined) {
|
||||||
UI.prime();
|
UI.prime();
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2018 The noVNC Authors
|
* Copyright (C) 2019 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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { init_logging as main_init_logging } from '../core/util/logging.js';
|
import { initLogging as mainInitLogging } from '../core/util/logging.js';
|
||||||
|
|
||||||
// init log level reading the logging HTTP param
|
// init log level reading the logging HTTP param
|
||||||
export function init_logging(level) {
|
export function initLogging(level) {
|
||||||
"use strict";
|
"use strict";
|
||||||
if (typeof level !== "undefined") {
|
if (typeof level !== "undefined") {
|
||||||
main_init_logging(level);
|
mainInitLogging(level);
|
||||||
} else {
|
} else {
|
||||||
const 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);
|
mainInitLogging(param || undefined);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ export function injectParamIfMissing(path, param, value) {
|
||||||
const elem = document.createElement('a');
|
const elem = document.createElement('a');
|
||||||
elem.href = path;
|
elem.href = path;
|
||||||
|
|
||||||
const param_eq = encodeURIComponent(param) + "=";
|
const paramEq = encodeURIComponent(param) + "=";
|
||||||
let query;
|
let query;
|
||||||
if (elem.search) {
|
if (elem.search) {
|
||||||
query = elem.search.slice(1).split('&');
|
query = elem.search.slice(1).split('&');
|
||||||
|
@ -192,8 +192,8 @@ export function injectParamIfMissing(path, param, value) {
|
||||||
query = [];
|
query = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!query.some(v => v.startsWith(param_eq))) {
|
if (!query.some(v => v.startsWith(paramEq))) {
|
||||||
query.push(param_eq + encodeURIComponent(value));
|
query.push(paramEq + encodeURIComponent(value));
|
||||||
elem.search = "?" + query.join("&");
|
elem.search = "?" + query.join("&");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,12 +57,12 @@ export default {
|
||||||
/* eslint-enable comma-spacing */
|
/* eslint-enable comma-spacing */
|
||||||
|
|
||||||
decode(data, offset = 0) {
|
decode(data, offset = 0) {
|
||||||
let data_length = data.indexOf('=') - offset;
|
let dataLength = data.indexOf('=') - offset;
|
||||||
if (data_length < 0) { data_length = data.length - offset; }
|
if (dataLength < 0) { dataLength = data.length - offset; }
|
||||||
|
|
||||||
/* Every four characters is 3 resulting numbers */
|
/* Every four characters is 3 resulting numbers */
|
||||||
const result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5);
|
const resultLength = (dataLength >> 2) * 3 + Math.floor((dataLength % 4) / 1.5);
|
||||||
const result = new Array(result_length);
|
const result = new Array(resultLength);
|
||||||
|
|
||||||
// Convert one by one.
|
// Convert one by one.
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2019 The noVNC Authors
|
||||||
* Copyright (C) 2018 Samuel Mannehed for Cendio AB
|
|
||||||
* Copyright (C) 2018 Pierre Ossman 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.
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2019 The noVNC Authors
|
||||||
* Copyright (C) 2018 Samuel Mannehed for Cendio AB
|
|
||||||
* Copyright (C) 2018 Pierre Ossman 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.
|
||||||
|
@ -19,10 +17,10 @@ export default class HextileDecoder {
|
||||||
|
|
||||||
decodeRect(x, y, width, height, sock, display, depth) {
|
decodeRect(x, y, width, height, sock, display, depth) {
|
||||||
if (this._tiles === 0) {
|
if (this._tiles === 0) {
|
||||||
this._tiles_x = Math.ceil(width / 16);
|
this._tilesX = Math.ceil(width / 16);
|
||||||
this._tiles_y = Math.ceil(height / 16);
|
this._tilesY = Math.ceil(height / 16);
|
||||||
this._total_tiles = this._tiles_x * this._tiles_y;
|
this._totalTiles = this._tilesX * this._tilesY;
|
||||||
this._tiles = this._total_tiles;
|
this._tiles = this._totalTiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (this._tiles > 0) {
|
while (this._tiles > 0) {
|
||||||
|
@ -41,11 +39,11 @@ export default class HextileDecoder {
|
||||||
subencoding + ")");
|
subencoding + ")");
|
||||||
}
|
}
|
||||||
|
|
||||||
const curr_tile = this._total_tiles - this._tiles;
|
const currTile = this._totalTiles - this._tiles;
|
||||||
const tile_x = curr_tile % this._tiles_x;
|
const tileX = currTile % this._tilesX;
|
||||||
const tile_y = Math.floor(curr_tile / this._tiles_x);
|
const tileY = Math.floor(currTile / this._tilesX);
|
||||||
const tx = x + tile_x * 16;
|
const tx = x + tileX * 16;
|
||||||
const ty = y + tile_y * 16;
|
const ty = y + tileY * 16;
|
||||||
const tw = Math.min(16, (x + width) - tx);
|
const tw = Math.min(16, (x + width) - tx);
|
||||||
const th = Math.min(16, (y + height) - ty);
|
const th = Math.min(16, (y + height) - ty);
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2019 The noVNC Authors
|
||||||
* Copyright (C) 2018 Samuel Mannehed for Cendio AB
|
|
||||||
* Copyright (C) 2018 Pierre Ossman 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.
|
||||||
|
@ -26,15 +24,15 @@ export default class RawDecoder {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const cur_y = y + (height - this._lines);
|
const curY = y + (height - this._lines);
|
||||||
const curr_height = Math.min(this._lines,
|
const currHeight = Math.min(this._lines,
|
||||||
Math.floor(sock.rQlen / bytesPerLine));
|
Math.floor(sock.rQlen / bytesPerLine));
|
||||||
let data = sock.rQ;
|
let data = sock.rQ;
|
||||||
let index = sock.rQi;
|
let index = sock.rQi;
|
||||||
|
|
||||||
// Convert data if needed
|
// Convert data if needed
|
||||||
if (depth == 8) {
|
if (depth == 8) {
|
||||||
const pixels = width * curr_height;
|
const pixels = width * currHeight;
|
||||||
const newdata = new Uint8Array(pixels * 4);
|
const newdata = new Uint8Array(pixels * 4);
|
||||||
for (let i = 0; i < pixels; i++) {
|
for (let i = 0; i < pixels; i++) {
|
||||||
newdata[i * 4 + 0] = ((data[index + i] >> 0) & 0x3) * 255 / 3;
|
newdata[i * 4 + 0] = ((data[index + i] >> 0) & 0x3) * 255 / 3;
|
||||||
|
@ -46,9 +44,9 @@ export default class RawDecoder {
|
||||||
index = 0;
|
index = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
display.blitImage(x, cur_y, width, curr_height, data, index);
|
display.blitImage(x, curY, width, currHeight, data, index);
|
||||||
sock.rQskipBytes(curr_height * bytesPerLine);
|
sock.rQskipBytes(currHeight * bytesPerLine);
|
||||||
this._lines -= curr_height;
|
this._lines -= currHeight;
|
||||||
if (this._lines > 0) {
|
if (this._lines > 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2019 The noVNC Authors
|
||||||
* Copyright (C) 2018 Samuel Mannehed for Cendio AB
|
|
||||||
* Copyright (C) 2018 Pierre Ossman 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.
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2019 The noVNC Authors
|
||||||
* (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
|
* (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)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
|
@ -94,7 +92,7 @@ export default class TightDecoder {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
display.imageRect(x, y, "image/jpeg", data);
|
display.imageRect(x, y, width, height, "image/jpeg", data);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -162,10 +160,9 @@ export default class TightDecoder {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
data = this._zlibs[streamId].inflate(data, true, uncompressedSize);
|
this._zlibs[streamId].setInput(data);
|
||||||
if (data.length != uncompressedSize) {
|
data = this._zlibs[streamId].inflate(uncompressedSize);
|
||||||
throw new Error("Incomplete zlib block");
|
this._zlibs[streamId].setInput(null);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
display.blitRgbImage(x, y, width, height, data, 0, false);
|
display.blitRgbImage(x, y, width, height, data, 0, false);
|
||||||
|
@ -210,10 +207,9 @@ export default class TightDecoder {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
data = this._zlibs[streamId].inflate(data, true, uncompressedSize);
|
this._zlibs[streamId].setInput(data);
|
||||||
if (data.length != uncompressedSize) {
|
data = this._zlibs[streamId].inflate(uncompressedSize);
|
||||||
throw new Error("Incomplete zlib block");
|
this._zlibs[streamId].setInput(null);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert indexed (palette based) image data to RGB
|
// Convert indexed (palette based) image data to RGB
|
||||||
|
|
|
@ -1,8 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2019 The noVNC Authors
|
||||||
* Copyright (C) 2018 Samuel Mannehed for Cendio AB
|
|
||||||
* Copyright (C) 2018 Pierre Ossman 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.
|
||||||
|
@ -18,7 +16,7 @@ export default class TightPNGDecoder extends TightDecoder {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
display.imageRect(x, y, "image/png", data);
|
display.imageRect(x, y, width, height, "image/png", data);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
85
static/js/novnc/core/deflator.js
Executable file
85
static/js/novnc/core/deflator.js
Executable file
|
@ -0,0 +1,85 @@
|
||||||
|
/*
|
||||||
|
* noVNC: HTML5 VNC client
|
||||||
|
* Copyright (C) 2020 The noVNC Authors
|
||||||
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
|
*
|
||||||
|
* See README.md for usage and integration instructions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { deflateInit, deflate } from "../vendor/pako/lib/zlib/deflate.js";
|
||||||
|
import { Z_FULL_FLUSH } from "../vendor/pako/lib/zlib/deflate.js";
|
||||||
|
import ZStream from "../vendor/pako/lib/zlib/zstream.js";
|
||||||
|
|
||||||
|
export default class Deflator {
|
||||||
|
constructor() {
|
||||||
|
this.strm = new ZStream();
|
||||||
|
this.chunkSize = 1024 * 10 * 10;
|
||||||
|
this.outputBuffer = new Uint8Array(this.chunkSize);
|
||||||
|
this.windowBits = 5;
|
||||||
|
|
||||||
|
deflateInit(this.strm, this.windowBits);
|
||||||
|
}
|
||||||
|
|
||||||
|
deflate(inData) {
|
||||||
|
/* eslint-disable camelcase */
|
||||||
|
this.strm.input = inData;
|
||||||
|
this.strm.avail_in = this.strm.input.length;
|
||||||
|
this.strm.next_in = 0;
|
||||||
|
this.strm.output = this.outputBuffer;
|
||||||
|
this.strm.avail_out = this.chunkSize;
|
||||||
|
this.strm.next_out = 0;
|
||||||
|
/* eslint-enable camelcase */
|
||||||
|
|
||||||
|
let lastRet = deflate(this.strm, Z_FULL_FLUSH);
|
||||||
|
let outData = new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
|
||||||
|
|
||||||
|
if (lastRet < 0) {
|
||||||
|
throw new Error("zlib deflate failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.strm.avail_in > 0) {
|
||||||
|
// Read chunks until done
|
||||||
|
|
||||||
|
let chunks = [outData];
|
||||||
|
let totalLen = outData.length;
|
||||||
|
do {
|
||||||
|
/* eslint-disable camelcase */
|
||||||
|
this.strm.output = new Uint8Array(this.chunkSize);
|
||||||
|
this.strm.next_out = 0;
|
||||||
|
this.strm.avail_out = this.chunkSize;
|
||||||
|
/* eslint-enable camelcase */
|
||||||
|
|
||||||
|
lastRet = deflate(this.strm, Z_FULL_FLUSH);
|
||||||
|
|
||||||
|
if (lastRet < 0) {
|
||||||
|
throw new Error("zlib deflate failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
let chunk = new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
|
||||||
|
totalLen += chunk.length;
|
||||||
|
chunks.push(chunk);
|
||||||
|
} while (this.strm.avail_in > 0);
|
||||||
|
|
||||||
|
// Combine chunks into a single data
|
||||||
|
|
||||||
|
let newData = new Uint8Array(totalLen);
|
||||||
|
let offset = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < chunks.length; i++) {
|
||||||
|
newData.set(chunks[i], offset);
|
||||||
|
offset += chunks[i].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
outData = newData;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* eslint-disable camelcase */
|
||||||
|
this.strm.input = null;
|
||||||
|
this.strm.avail_in = 0;
|
||||||
|
this.strm.next_in = 0;
|
||||||
|
/* eslint-enable camelcase */
|
||||||
|
|
||||||
|
return outData;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2018 The noVNC Authors
|
* Copyright (C) 2019 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,24 +9,24 @@
|
||||||
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';
|
import { supportsImageMetadata } from './util/browser.js';
|
||||||
|
import { toSigned32bit } from './util/int.js';
|
||||||
|
|
||||||
export default class Display {
|
export default class Display {
|
||||||
constructor(target) {
|
constructor(target) {
|
||||||
this._drawCtx = null;
|
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._fbWidth = 0;
|
||||||
this._fb_height = 0;
|
this._fbHeight = 0;
|
||||||
|
|
||||||
this._prevDrawStyle = "";
|
this._prevDrawStyle = "";
|
||||||
this._tile = null;
|
this._tile = null;
|
||||||
this._tile16x16 = null;
|
this._tile16x16 = null;
|
||||||
this._tile_x = 0;
|
this._tileX = 0;
|
||||||
this._tile_y = 0;
|
this._tileY = 0;
|
||||||
|
|
||||||
Log.Debug(">> Display.constructor");
|
Log.Debug(">> Display.constructor");
|
||||||
|
|
||||||
|
@ -60,8 +60,6 @@ export default class Display {
|
||||||
|
|
||||||
Log.Debug("User Agent: " + navigator.userAgent);
|
Log.Debug("User Agent: " + navigator.userAgent);
|
||||||
|
|
||||||
this.clear();
|
|
||||||
|
|
||||||
// Check canvas features
|
// Check canvas features
|
||||||
if (!('createImageData' in this._drawCtx)) {
|
if (!('createImageData' in this._drawCtx)) {
|
||||||
throw new Error("Canvas does not support createImageData");
|
throw new Error("Canvas does not support createImageData");
|
||||||
|
@ -74,7 +72,6 @@ export default class Display {
|
||||||
|
|
||||||
this._scale = 1.0;
|
this._scale = 1.0;
|
||||||
this._clipViewport = false;
|
this._clipViewport = false;
|
||||||
this.logo = null;
|
|
||||||
|
|
||||||
// ===== EVENT HANDLERS =====
|
// ===== EVENT HANDLERS =====
|
||||||
|
|
||||||
|
@ -98,11 +95,11 @@ export default class Display {
|
||||||
}
|
}
|
||||||
|
|
||||||
get width() {
|
get width() {
|
||||||
return this._fb_width;
|
return this._fbWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
get height() {
|
get height() {
|
||||||
return this._fb_height;
|
return this._fbHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== PUBLIC METHODS =====
|
// ===== PUBLIC METHODS =====
|
||||||
|
@ -125,15 +122,15 @@ export default class Display {
|
||||||
if (deltaX < 0 && vp.x + deltaX < 0) {
|
if (deltaX < 0 && vp.x + deltaX < 0) {
|
||||||
deltaX = -vp.x;
|
deltaX = -vp.x;
|
||||||
}
|
}
|
||||||
if (vx2 + deltaX >= this._fb_width) {
|
if (vx2 + deltaX >= this._fbWidth) {
|
||||||
deltaX -= vx2 + deltaX - this._fb_width + 1;
|
deltaX -= vx2 + deltaX - this._fbWidth + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vp.y + deltaY < 0) {
|
if (vp.y + deltaY < 0) {
|
||||||
deltaY = -vp.y;
|
deltaY = -vp.y;
|
||||||
}
|
}
|
||||||
if (vy2 + deltaY >= this._fb_height) {
|
if (vy2 + deltaY >= this._fbHeight) {
|
||||||
deltaY -= (vy2 + deltaY - this._fb_height + 1);
|
deltaY -= (vy2 + deltaY - this._fbHeight + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deltaX === 0 && deltaY === 0) {
|
if (deltaX === 0 && deltaY === 0) {
|
||||||
|
@ -156,18 +153,18 @@ export default class Display {
|
||||||
typeof(height) === "undefined") {
|
typeof(height) === "undefined") {
|
||||||
|
|
||||||
Log.Debug("Setting viewport to full display region");
|
Log.Debug("Setting viewport to full display region");
|
||||||
width = this._fb_width;
|
width = this._fbWidth;
|
||||||
height = this._fb_height;
|
height = this._fbHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
width = Math.floor(width);
|
width = Math.floor(width);
|
||||||
height = Math.floor(height);
|
height = Math.floor(height);
|
||||||
|
|
||||||
if (width > this._fb_width) {
|
if (width > this._fbWidth) {
|
||||||
width = this._fb_width;
|
width = this._fbWidth;
|
||||||
}
|
}
|
||||||
if (height > this._fb_height) {
|
if (height > this._fbHeight) {
|
||||||
height = this._fb_height;
|
height = this._fbHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
const vp = this._viewportLoc;
|
const vp = this._viewportLoc;
|
||||||
|
@ -194,21 +191,21 @@ export default class Display {
|
||||||
if (this._scale === 0) {
|
if (this._scale === 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return x / this._scale + this._viewportLoc.x;
|
return toSigned32bit(x / this._scale + this._viewportLoc.x);
|
||||||
}
|
}
|
||||||
|
|
||||||
absY(y) {
|
absY(y) {
|
||||||
if (this._scale === 0) {
|
if (this._scale === 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return y / this._scale + this._viewportLoc.y;
|
return toSigned32bit(y / this._scale + this._viewportLoc.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
resize(width, height) {
|
resize(width, height) {
|
||||||
this._prevDrawStyle = "";
|
this._prevDrawStyle = "";
|
||||||
|
|
||||||
this._fb_width = width;
|
this._fbWidth = width;
|
||||||
this._fb_height = height;
|
this._fbHeight = height;
|
||||||
|
|
||||||
const canvas = this._backbuffer;
|
const canvas = this._backbuffer;
|
||||||
if (canvas.width !== width || canvas.height !== height) {
|
if (canvas.width !== width || canvas.height !== height) {
|
||||||
|
@ -256,9 +253,9 @@ export default class Display {
|
||||||
|
|
||||||
// Update the visible canvas with the contents of the
|
// Update the visible canvas with the contents of the
|
||||||
// rendering canvas
|
// rendering canvas
|
||||||
flip(from_queue) {
|
flip(fromQueue) {
|
||||||
if (this._renderQ.length !== 0 && !from_queue) {
|
if (this._renderQ.length !== 0 && !fromQueue) {
|
||||||
this._renderQ_push({
|
this._renderQPush({
|
||||||
'type': 'flip'
|
'type': 'flip'
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
@ -302,17 +299,6 @@ export default class Display {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
clear() {
|
|
||||||
if (this._logo) {
|
|
||||||
this.resize(this._logo.width, this._logo.height);
|
|
||||||
this.imageRect(0, 0, this._logo.type, this._logo.data);
|
|
||||||
} else {
|
|
||||||
this.resize(240, 20);
|
|
||||||
this._drawCtx.clearRect(0, 0, this._fb_width, this._fb_height);
|
|
||||||
}
|
|
||||||
this.flip();
|
|
||||||
}
|
|
||||||
|
|
||||||
pending() {
|
pending() {
|
||||||
return this._renderQ.length > 0;
|
return this._renderQ.length > 0;
|
||||||
}
|
}
|
||||||
|
@ -325,9 +311,9 @@ export default class Display {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fillRect(x, y, width, height, color, from_queue) {
|
fillRect(x, y, width, height, color, fromQueue) {
|
||||||
if (this._renderQ.length !== 0 && !from_queue) {
|
if (this._renderQ.length !== 0 && !fromQueue) {
|
||||||
this._renderQ_push({
|
this._renderQPush({
|
||||||
'type': 'fill',
|
'type': 'fill',
|
||||||
'x': x,
|
'x': x,
|
||||||
'y': y,
|
'y': y,
|
||||||
|
@ -342,14 +328,14 @@ export default class Display {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
copyImage(old_x, old_y, new_x, new_y, w, h, from_queue) {
|
copyImage(oldX, oldY, newX, newY, w, h, fromQueue) {
|
||||||
if (this._renderQ.length !== 0 && !from_queue) {
|
if (this._renderQ.length !== 0 && !fromQueue) {
|
||||||
this._renderQ_push({
|
this._renderQPush({
|
||||||
'type': 'copy',
|
'type': 'copy',
|
||||||
'old_x': old_x,
|
'oldX': oldX,
|
||||||
'old_y': old_y,
|
'oldY': oldY,
|
||||||
'x': new_x,
|
'x': newX,
|
||||||
'y': new_y,
|
'y': newY,
|
||||||
'width': w,
|
'width': w,
|
||||||
'height': h,
|
'height': h,
|
||||||
});
|
});
|
||||||
|
@ -367,27 +353,35 @@ export default class Display {
|
||||||
this._drawCtx.imageSmoothingEnabled = false;
|
this._drawCtx.imageSmoothingEnabled = false;
|
||||||
|
|
||||||
this._drawCtx.drawImage(this._backbuffer,
|
this._drawCtx.drawImage(this._backbuffer,
|
||||||
old_x, old_y, w, h,
|
oldX, oldY, w, h,
|
||||||
new_x, new_y, w, h);
|
newX, newY, w, h);
|
||||||
this._damage(new_x, new_y, w, h);
|
this._damage(newX, newY, w, h);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
imageRect(x, y, mime, arr) {
|
imageRect(x, y, width, height, mime, arr) {
|
||||||
|
/* The internal logic cannot handle empty images, so bail early */
|
||||||
|
if ((width === 0) || (height === 0)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const img = new Image();
|
const img = new Image();
|
||||||
img.src = "data: " + mime + ";base64," + Base64.encode(arr);
|
img.src = "data: " + mime + ";base64," + Base64.encode(arr);
|
||||||
this._renderQ_push({
|
|
||||||
|
this._renderQPush({
|
||||||
'type': 'img',
|
'type': 'img',
|
||||||
'img': img,
|
'img': img,
|
||||||
'x': x,
|
'x': x,
|
||||||
'y': y
|
'y': y,
|
||||||
|
'width': width,
|
||||||
|
'height': height
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// start updating a tile
|
// start updating a tile
|
||||||
startTile(x, y, width, height, color) {
|
startTile(x, y, width, height, color) {
|
||||||
this._tile_x = x;
|
this._tileX = x;
|
||||||
this._tile_y = y;
|
this._tileY = y;
|
||||||
if (width === 16 && height === 16) {
|
if (width === 16 && height === 16) {
|
||||||
this._tile = this._tile16x16;
|
this._tile = this._tile16x16;
|
||||||
} else {
|
} else {
|
||||||
|
@ -430,21 +424,21 @@ export default class Display {
|
||||||
|
|
||||||
// draw the current tile to the screen
|
// draw the current tile to the screen
|
||||||
finishTile() {
|
finishTile() {
|
||||||
this._drawCtx.putImageData(this._tile, this._tile_x, this._tile_y);
|
this._drawCtx.putImageData(this._tile, this._tileX, this._tileY);
|
||||||
this._damage(this._tile_x, this._tile_y,
|
this._damage(this._tileX, this._tileY,
|
||||||
this._tile.width, this._tile.height);
|
this._tile.width, this._tile.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
blitImage(x, y, width, height, arr, offset, from_queue) {
|
blitImage(x, y, width, height, arr, offset, fromQueue) {
|
||||||
if (this._renderQ.length !== 0 && !from_queue) {
|
if (this._renderQ.length !== 0 && !fromQueue) {
|
||||||
// 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
|
||||||
const new_arr = new Uint8Array(width * height * 4);
|
const newArr = new Uint8Array(width * height * 4);
|
||||||
new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
|
newArr.set(new Uint8Array(arr.buffer, 0, newArr.length));
|
||||||
this._renderQ_push({
|
this._renderQPush({
|
||||||
'type': 'blit',
|
'type': 'blit',
|
||||||
'data': new_arr,
|
'data': newArr,
|
||||||
'x': x,
|
'x': x,
|
||||||
'y': y,
|
'y': y,
|
||||||
'width': width,
|
'width': width,
|
||||||
|
@ -455,16 +449,16 @@ export default class Display {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
blitRgbImage(x, y, width, height, arr, offset, from_queue) {
|
blitRgbImage(x, y, width, height, arr, offset, fromQueue) {
|
||||||
if (this._renderQ.length !== 0 && !from_queue) {
|
if (this._renderQ.length !== 0 && !fromQueue) {
|
||||||
// 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
|
||||||
const new_arr = new Uint8Array(width * height * 3);
|
const newArr = new Uint8Array(width * height * 3);
|
||||||
new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
|
newArr.set(new Uint8Array(arr.buffer, 0, newArr.length));
|
||||||
this._renderQ_push({
|
this._renderQPush({
|
||||||
'type': 'blitRgb',
|
'type': 'blitRgb',
|
||||||
'data': new_arr,
|
'data': newArr,
|
||||||
'x': x,
|
'x': x,
|
||||||
'y': y,
|
'y': y,
|
||||||
'width': width,
|
'width': width,
|
||||||
|
@ -475,16 +469,16 @@ export default class Display {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
blitRgbxImage(x, y, width, height, arr, offset, from_queue) {
|
blitRgbxImage(x, y, width, height, arr, offset, fromQueue) {
|
||||||
if (this._renderQ.length !== 0 && !from_queue) {
|
if (this._renderQ.length !== 0 && !fromQueue) {
|
||||||
// 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
|
||||||
const new_arr = new Uint8Array(width * height * 4);
|
const newArr = new Uint8Array(width * height * 4);
|
||||||
new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
|
newArr.set(new Uint8Array(arr.buffer, 0, newArr.length));
|
||||||
this._renderQ_push({
|
this._renderQPush({
|
||||||
'type': 'blitRgbx',
|
'type': 'blitRgbx',
|
||||||
'data': new_arr,
|
'data': newArr,
|
||||||
'x': x,
|
'x': x,
|
||||||
'y': y,
|
'y': y,
|
||||||
'width': width,
|
'width': width,
|
||||||
|
@ -589,23 +583,23 @@ export default class Display {
|
||||||
this._damage(x, y, img.width, img.height);
|
this._damage(x, y, img.width, img.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
_renderQ_push(action) {
|
_renderQPush(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._scanRenderQ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_resume_renderQ() {
|
_resumeRenderQ() {
|
||||||
// "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._noVNCDisplay._resumeRenderQ);
|
||||||
this._noVNC_display._scan_renderQ();
|
this._noVNCDisplay._scanRenderQ();
|
||||||
}
|
}
|
||||||
|
|
||||||
_scan_renderQ() {
|
_scanRenderQ() {
|
||||||
let ready = true;
|
let ready = true;
|
||||||
while (ready && this._renderQ.length > 0) {
|
while (ready && this._renderQ.length > 0) {
|
||||||
const a = this._renderQ[0];
|
const a = this._renderQ[0];
|
||||||
|
@ -614,7 +608,7 @@ export default class Display {
|
||||||
this.flip(true);
|
this.flip(true);
|
||||||
break;
|
break;
|
||||||
case 'copy':
|
case 'copy':
|
||||||
this.copyImage(a.old_x, a.old_y, a.x, a.y, a.width, a.height, true);
|
this.copyImage(a.oldX, a.oldY, a.x, a.y, a.width, a.height, true);
|
||||||
break;
|
break;
|
||||||
case 'fill':
|
case 'fill':
|
||||||
this.fillRect(a.x, a.y, a.width, a.height, a.color, true);
|
this.fillRect(a.x, a.y, a.width, a.height, a.color, true);
|
||||||
|
@ -629,11 +623,18 @@ export default class Display {
|
||||||
this.blitRgbxImage(a.x, a.y, a.width, a.height, a.data, 0, true);
|
this.blitRgbxImage(a.x, a.y, a.width, a.height, a.data, 0, true);
|
||||||
break;
|
break;
|
||||||
case 'img':
|
case 'img':
|
||||||
if (a.img.complete) {
|
/* IE tends to set "complete" prematurely, so check dimensions */
|
||||||
|
if (a.img.complete && (a.img.width !== 0) && (a.img.height !== 0)) {
|
||||||
|
if (a.img.width !== a.width || a.img.height !== a.height) {
|
||||||
|
Log.Error("Decoded image has incorrect dimensions. Got " +
|
||||||
|
a.img.width + "x" + a.img.height + ". Expected " +
|
||||||
|
a.width + "x" + a.height + ".");
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.drawImage(a.img, a.x, a.y);
|
this.drawImage(a.img, a.x, a.y);
|
||||||
} else {
|
} else {
|
||||||
a.img._noVNC_display = this;
|
a.img._noVNCDisplay = this;
|
||||||
a.img.addEventListener('load', this._resume_renderQ);
|
a.img.addEventListener('load', this._resumeRenderQ);
|
||||||
// We need to wait for this image to 'load'
|
// We need to wait for this image to 'load'
|
||||||
// to keep things in-order
|
// to keep things in-order
|
||||||
ready = false;
|
ready = false;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2018 The noVNC Authors
|
* Copyright (C) 2019 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.
|
||||||
|
@ -20,12 +20,15 @@ export const encodings = {
|
||||||
pseudoEncodingLastRect: -224,
|
pseudoEncodingLastRect: -224,
|
||||||
pseudoEncodingCursor: -239,
|
pseudoEncodingCursor: -239,
|
||||||
pseudoEncodingQEMUExtendedKeyEvent: -258,
|
pseudoEncodingQEMUExtendedKeyEvent: -258,
|
||||||
|
pseudoEncodingDesktopName: -307,
|
||||||
pseudoEncodingExtendedDesktopSize: -308,
|
pseudoEncodingExtendedDesktopSize: -308,
|
||||||
pseudoEncodingXvp: -309,
|
pseudoEncodingXvp: -309,
|
||||||
pseudoEncodingFence: -312,
|
pseudoEncodingFence: -312,
|
||||||
pseudoEncodingContinuousUpdates: -313,
|
pseudoEncodingContinuousUpdates: -313,
|
||||||
pseudoEncodingCompressLevel9: -247,
|
pseudoEncodingCompressLevel9: -247,
|
||||||
pseudoEncodingCompressLevel0: -256,
|
pseudoEncodingCompressLevel0: -256,
|
||||||
|
pseudoEncodingVMwareCursor: 0x574d5664,
|
||||||
|
pseudoEncodingExtendedClipboard: 0xc0a1e5ce
|
||||||
};
|
};
|
||||||
|
|
||||||
export function encodingName(num) {
|
export function encodingName(num) {
|
||||||
|
|
|
@ -1,3 +1,11 @@
|
||||||
|
/*
|
||||||
|
* noVNC: HTML5 VNC client
|
||||||
|
* Copyright (C) 2020 The noVNC Authors
|
||||||
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
|
*
|
||||||
|
* See README.md for usage and integration instructions.
|
||||||
|
*/
|
||||||
|
|
||||||
import { inflateInit, inflate, inflateReset } from "../vendor/pako/lib/zlib/inflate.js";
|
import { 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";
|
||||||
|
|
||||||
|
@ -11,12 +19,22 @@ export default class Inflate {
|
||||||
inflateInit(this.strm, this.windowBits);
|
inflateInit(this.strm, this.windowBits);
|
||||||
}
|
}
|
||||||
|
|
||||||
inflate(data, flush, expected) {
|
setInput(data) {
|
||||||
|
if (!data) {
|
||||||
|
//FIXME: flush remaining data.
|
||||||
|
/* eslint-disable camelcase */
|
||||||
|
this.strm.input = null;
|
||||||
|
this.strm.avail_in = 0;
|
||||||
|
this.strm.next_in = 0;
|
||||||
|
} else {
|
||||||
this.strm.input = data;
|
this.strm.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;
|
||||||
this.strm.next_out = 0;
|
/* eslint-enable camelcase */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inflate(expected) {
|
||||||
// resize our output buffer if it's too small
|
// resize our output buffer if it's too small
|
||||||
// (we could just use multiple chunks, but that would cause an extra
|
// (we could just use multiple chunks, but that would cause an extra
|
||||||
// allocation each time to flatten the chunks)
|
// allocation each time to flatten the chunks)
|
||||||
|
@ -25,9 +43,19 @@ export default class Inflate {
|
||||||
this.strm.output = new Uint8Array(this.chunkSize);
|
this.strm.output = new Uint8Array(this.chunkSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.strm.avail_out = this.chunkSize;
|
/* eslint-disable camelcase */
|
||||||
|
this.strm.next_out = 0;
|
||||||
|
this.strm.avail_out = expected;
|
||||||
|
/* eslint-enable camelcase */
|
||||||
|
|
||||||
inflate(this.strm, flush);
|
let ret = inflate(this.strm, 0); // Flush argument not used.
|
||||||
|
if (ret < 0) {
|
||||||
|
throw new Error("zlib inflate failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.strm.next_out != expected) {
|
||||||
|
throw new Error("Incomplete zlib block");
|
||||||
|
}
|
||||||
|
|
||||||
return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
|
return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,12 +43,10 @@ addStandard("CapsLock", KeyTable.XK_Caps_Lock);
|
||||||
addLeftRight("Control", KeyTable.XK_Control_L, KeyTable.XK_Control_R);
|
addLeftRight("Control", KeyTable.XK_Control_L, KeyTable.XK_Control_R);
|
||||||
// - Fn
|
// - Fn
|
||||||
// - FnLock
|
// - FnLock
|
||||||
addLeftRight("Hyper", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
|
|
||||||
addLeftRight("Meta", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
|
addLeftRight("Meta", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
|
||||||
addStandard("NumLock", KeyTable.XK_Num_Lock);
|
addStandard("NumLock", KeyTable.XK_Num_Lock);
|
||||||
addStandard("ScrollLock", KeyTable.XK_Scroll_Lock);
|
addStandard("ScrollLock", KeyTable.XK_Scroll_Lock);
|
||||||
addLeftRight("Shift", KeyTable.XK_Shift_L, KeyTable.XK_Shift_R);
|
addLeftRight("Shift", KeyTable.XK_Shift_L, KeyTable.XK_Shift_R);
|
||||||
addLeftRight("Super", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
|
|
||||||
// - Symbol
|
// - Symbol
|
||||||
// - SymbolLock
|
// - SymbolLock
|
||||||
|
|
||||||
|
@ -72,6 +70,9 @@ 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);
|
||||||
|
// Browsers send "Clear" for the numpad 5 without NumLock because
|
||||||
|
// Windows uses VK_Clear for that key. But Unix expects KP_Begin for
|
||||||
|
// that scenario.
|
||||||
addNumpad("Clear", KeyTable.XK_Clear, KeyTable.XK_KP_Begin);
|
addNumpad("Clear", KeyTable.XK_Clear, KeyTable.XK_KP_Begin);
|
||||||
addStandard("Copy", KeyTable.XF86XK_Copy);
|
addStandard("Copy", KeyTable.XF86XK_Copy);
|
||||||
// - CrSel
|
// - CrSel
|
||||||
|
@ -194,7 +195,8 @@ addStandard("F35", KeyTable.XK_F35);
|
||||||
addStandard("Close", KeyTable.XF86XK_Close);
|
addStandard("Close", KeyTable.XF86XK_Close);
|
||||||
addStandard("MailForward", KeyTable.XF86XK_MailForward);
|
addStandard("MailForward", KeyTable.XF86XK_MailForward);
|
||||||
addStandard("MailReply", KeyTable.XF86XK_Reply);
|
addStandard("MailReply", KeyTable.XF86XK_Reply);
|
||||||
addStandard("MainSend", KeyTable.XF86XK_Send);
|
addStandard("MailSend", KeyTable.XF86XK_Send);
|
||||||
|
// - MediaClose
|
||||||
addStandard("MediaFastForward", KeyTable.XF86XK_AudioForward);
|
addStandard("MediaFastForward", KeyTable.XF86XK_AudioForward);
|
||||||
addStandard("MediaPause", KeyTable.XF86XK_AudioPause);
|
addStandard("MediaPause", KeyTable.XF86XK_AudioPause);
|
||||||
addStandard("MediaPlay", KeyTable.XF86XK_AudioPlay);
|
addStandard("MediaPlay", KeyTable.XF86XK_AudioPlay);
|
||||||
|
@ -218,11 +220,9 @@ addStandard("SpellCheck", KeyTable.XF86XK_Spell);
|
||||||
|
|
||||||
// - AudioBalanceLeft
|
// - AudioBalanceLeft
|
||||||
// - AudioBalanceRight
|
// - AudioBalanceRight
|
||||||
// - AudioBassDown
|
|
||||||
// - AudioBassBoostDown
|
// - AudioBassBoostDown
|
||||||
// - AudioBassBoostToggle
|
// - AudioBassBoostToggle
|
||||||
// - AudioBassBoostUp
|
// - AudioBassBoostUp
|
||||||
// - AudioBassUp
|
|
||||||
// - AudioFaderFront
|
// - AudioFaderFront
|
||||||
// - AudioFaderRear
|
// - AudioFaderRear
|
||||||
// - AudioSurroundModeNext
|
// - AudioSurroundModeNext
|
||||||
|
@ -243,12 +243,12 @@ addStandard("MicrophoneVolumeMute", KeyTable.XF86XK_AudioMicMute);
|
||||||
|
|
||||||
// 2.14. Application Keys
|
// 2.14. Application Keys
|
||||||
|
|
||||||
addStandard("LaunchCalculator", KeyTable.XF86XK_Calculator);
|
addStandard("LaunchApplication1", KeyTable.XF86XK_MyComputer);
|
||||||
|
addStandard("LaunchApplication2", KeyTable.XF86XK_Calculator);
|
||||||
addStandard("LaunchCalendar", KeyTable.XF86XK_Calendar);
|
addStandard("LaunchCalendar", KeyTable.XF86XK_Calendar);
|
||||||
addStandard("LaunchMail", KeyTable.XF86XK_Mail);
|
addStandard("LaunchMail", KeyTable.XF86XK_Mail);
|
||||||
addStandard("LaunchMediaPlayer", KeyTable.XF86XK_AudioMedia);
|
addStandard("LaunchMediaPlayer", KeyTable.XF86XK_AudioMedia);
|
||||||
addStandard("LaunchMusicPlayer", KeyTable.XF86XK_Music);
|
addStandard("LaunchMusicPlayer", KeyTable.XF86XK_Music);
|
||||||
addStandard("LaunchMyComputer", KeyTable.XF86XK_MyComputer);
|
|
||||||
addStandard("LaunchPhone", KeyTable.XF86XK_Phone);
|
addStandard("LaunchPhone", KeyTable.XF86XK_Phone);
|
||||||
addStandard("LaunchScreenSaver", KeyTable.XF86XK_ScreenSaver);
|
addStandard("LaunchScreenSaver", KeyTable.XF86XK_ScreenSaver);
|
||||||
addStandard("LaunchSpreadsheet", KeyTable.XF86XK_Excel);
|
addStandard("LaunchSpreadsheet", KeyTable.XF86XK_Excel);
|
||||||
|
|
567
static/js/novnc/core/input/gesturehandler.js
Executable file
567
static/js/novnc/core/input/gesturehandler.js
Executable file
|
@ -0,0 +1,567 @@
|
||||||
|
/*
|
||||||
|
* noVNC: HTML5 VNC client
|
||||||
|
* Copyright (C) 2020 The noVNC Authors
|
||||||
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
|
*
|
||||||
|
* See README.md for usage and integration instructions.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
const GH_NOGESTURE = 0;
|
||||||
|
const GH_ONETAP = 1;
|
||||||
|
const GH_TWOTAP = 2;
|
||||||
|
const GH_THREETAP = 4;
|
||||||
|
const GH_DRAG = 8;
|
||||||
|
const GH_LONGPRESS = 16;
|
||||||
|
const GH_TWODRAG = 32;
|
||||||
|
const GH_PINCH = 64;
|
||||||
|
|
||||||
|
const GH_INITSTATE = 127;
|
||||||
|
|
||||||
|
const GH_MOVE_THRESHOLD = 50;
|
||||||
|
const GH_ANGLE_THRESHOLD = 90; // Degrees
|
||||||
|
|
||||||
|
// Timeout when waiting for gestures (ms)
|
||||||
|
const GH_MULTITOUCH_TIMEOUT = 250;
|
||||||
|
|
||||||
|
// Maximum time between press and release for a tap (ms)
|
||||||
|
const GH_TAP_TIMEOUT = 1000;
|
||||||
|
|
||||||
|
// Timeout when waiting for longpress (ms)
|
||||||
|
const GH_LONGPRESS_TIMEOUT = 1000;
|
||||||
|
|
||||||
|
// Timeout when waiting to decide between PINCH and TWODRAG (ms)
|
||||||
|
const GH_TWOTOUCH_TIMEOUT = 50;
|
||||||
|
|
||||||
|
export default class GestureHandler {
|
||||||
|
constructor() {
|
||||||
|
this._target = null;
|
||||||
|
|
||||||
|
this._state = GH_INITSTATE;
|
||||||
|
|
||||||
|
this._tracked = [];
|
||||||
|
this._ignored = [];
|
||||||
|
|
||||||
|
this._waitingRelease = false;
|
||||||
|
this._releaseStart = 0.0;
|
||||||
|
|
||||||
|
this._longpressTimeoutId = null;
|
||||||
|
this._twoTouchTimeoutId = null;
|
||||||
|
|
||||||
|
this._boundEventHandler = this._eventHandler.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
attach(target) {
|
||||||
|
this.detach();
|
||||||
|
|
||||||
|
this._target = target;
|
||||||
|
this._target.addEventListener('touchstart',
|
||||||
|
this._boundEventHandler);
|
||||||
|
this._target.addEventListener('touchmove',
|
||||||
|
this._boundEventHandler);
|
||||||
|
this._target.addEventListener('touchend',
|
||||||
|
this._boundEventHandler);
|
||||||
|
this._target.addEventListener('touchcancel',
|
||||||
|
this._boundEventHandler);
|
||||||
|
}
|
||||||
|
|
||||||
|
detach() {
|
||||||
|
if (!this._target) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._stopLongpressTimeout();
|
||||||
|
this._stopTwoTouchTimeout();
|
||||||
|
|
||||||
|
this._target.removeEventListener('touchstart',
|
||||||
|
this._boundEventHandler);
|
||||||
|
this._target.removeEventListener('touchmove',
|
||||||
|
this._boundEventHandler);
|
||||||
|
this._target.removeEventListener('touchend',
|
||||||
|
this._boundEventHandler);
|
||||||
|
this._target.removeEventListener('touchcancel',
|
||||||
|
this._boundEventHandler);
|
||||||
|
this._target = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_eventHandler(e) {
|
||||||
|
let fn;
|
||||||
|
|
||||||
|
e.stopPropagation();
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
switch (e.type) {
|
||||||
|
case 'touchstart':
|
||||||
|
fn = this._touchStart;
|
||||||
|
break;
|
||||||
|
case 'touchmove':
|
||||||
|
fn = this._touchMove;
|
||||||
|
break;
|
||||||
|
case 'touchend':
|
||||||
|
case 'touchcancel':
|
||||||
|
fn = this._touchEnd;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < e.changedTouches.length; i++) {
|
||||||
|
let touch = e.changedTouches[i];
|
||||||
|
fn.call(this, touch.identifier, touch.clientX, touch.clientY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_touchStart(id, x, y) {
|
||||||
|
// Ignore any new touches if there is already an active gesture,
|
||||||
|
// or we're in a cleanup state
|
||||||
|
if (this._hasDetectedGesture() || (this._state === GH_NOGESTURE)) {
|
||||||
|
this._ignored.push(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Did it take too long between touches that we should no longer
|
||||||
|
// consider this a single gesture?
|
||||||
|
if ((this._tracked.length > 0) &&
|
||||||
|
((Date.now() - this._tracked[0].started) > GH_MULTITOUCH_TIMEOUT)) {
|
||||||
|
this._state = GH_NOGESTURE;
|
||||||
|
this._ignored.push(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're waiting for fingers to release then we should no longer
|
||||||
|
// recognize new touches
|
||||||
|
if (this._waitingRelease) {
|
||||||
|
this._state = GH_NOGESTURE;
|
||||||
|
this._ignored.push(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._tracked.push({
|
||||||
|
id: id,
|
||||||
|
started: Date.now(),
|
||||||
|
active: true,
|
||||||
|
firstX: x,
|
||||||
|
firstY: y,
|
||||||
|
lastX: x,
|
||||||
|
lastY: y,
|
||||||
|
angle: 0
|
||||||
|
});
|
||||||
|
|
||||||
|
switch (this._tracked.length) {
|
||||||
|
case 1:
|
||||||
|
this._startLongpressTimeout();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
this._state &= ~(GH_ONETAP | GH_DRAG | GH_LONGPRESS);
|
||||||
|
this._stopLongpressTimeout();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 3:
|
||||||
|
this._state &= ~(GH_TWOTAP | GH_TWODRAG | GH_PINCH);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
this._state = GH_NOGESTURE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_touchMove(id, x, y) {
|
||||||
|
let touch = this._tracked.find(t => t.id === id);
|
||||||
|
|
||||||
|
// If this is an update for a touch we're not tracking, ignore it
|
||||||
|
if (touch === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the touches last position with the event coordinates
|
||||||
|
touch.lastX = x;
|
||||||
|
touch.lastY = y;
|
||||||
|
|
||||||
|
let deltaX = x - touch.firstX;
|
||||||
|
let deltaY = y - touch.firstY;
|
||||||
|
|
||||||
|
// Update angle when the touch has moved
|
||||||
|
if ((touch.firstX !== touch.lastX) ||
|
||||||
|
(touch.firstY !== touch.lastY)) {
|
||||||
|
touch.angle = Math.atan2(deltaY, deltaX) * 180 / Math.PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._hasDetectedGesture()) {
|
||||||
|
// Ignore moves smaller than the minimum threshold
|
||||||
|
if (Math.hypot(deltaX, deltaY) < GH_MOVE_THRESHOLD) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can't be a tap or long press as we've seen movement
|
||||||
|
this._state &= ~(GH_ONETAP | GH_TWOTAP | GH_THREETAP | GH_LONGPRESS);
|
||||||
|
this._stopLongpressTimeout();
|
||||||
|
|
||||||
|
if (this._tracked.length !== 1) {
|
||||||
|
this._state &= ~(GH_DRAG);
|
||||||
|
}
|
||||||
|
if (this._tracked.length !== 2) {
|
||||||
|
this._state &= ~(GH_TWODRAG | GH_PINCH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to figure out which of our different two touch gestures
|
||||||
|
// this might be
|
||||||
|
if (this._tracked.length === 2) {
|
||||||
|
|
||||||
|
// The other touch is the one where the id doesn't match
|
||||||
|
let prevTouch = this._tracked.find(t => t.id !== id);
|
||||||
|
|
||||||
|
// How far the previous touch point has moved since start
|
||||||
|
let prevDeltaMove = Math.hypot(prevTouch.firstX - prevTouch.lastX,
|
||||||
|
prevTouch.firstY - prevTouch.lastY);
|
||||||
|
|
||||||
|
// We know that the current touch moved far enough,
|
||||||
|
// but unless both touches moved further than their
|
||||||
|
// threshold we don't want to disqualify any gestures
|
||||||
|
if (prevDeltaMove > GH_MOVE_THRESHOLD) {
|
||||||
|
|
||||||
|
// The angle difference between the direction of the touch points
|
||||||
|
let deltaAngle = Math.abs(touch.angle - prevTouch.angle);
|
||||||
|
deltaAngle = Math.abs(((deltaAngle + 180) % 360) - 180);
|
||||||
|
|
||||||
|
// PINCH or TWODRAG can be eliminated depending on the angle
|
||||||
|
if (deltaAngle > GH_ANGLE_THRESHOLD) {
|
||||||
|
this._state &= ~GH_TWODRAG;
|
||||||
|
} else {
|
||||||
|
this._state &= ~GH_PINCH;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._isTwoTouchTimeoutRunning()) {
|
||||||
|
this._stopTwoTouchTimeout();
|
||||||
|
}
|
||||||
|
} else if (!this._isTwoTouchTimeoutRunning()) {
|
||||||
|
// We can't determine the gesture right now, let's
|
||||||
|
// wait and see if more events are on their way
|
||||||
|
this._startTwoTouchTimeout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._hasDetectedGesture()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._pushEvent('gesturestart');
|
||||||
|
}
|
||||||
|
|
||||||
|
this._pushEvent('gesturemove');
|
||||||
|
}
|
||||||
|
|
||||||
|
_touchEnd(id, x, y) {
|
||||||
|
// Check if this is an ignored touch
|
||||||
|
if (this._ignored.indexOf(id) !== -1) {
|
||||||
|
// Remove this touch from ignored
|
||||||
|
this._ignored.splice(this._ignored.indexOf(id), 1);
|
||||||
|
|
||||||
|
// And reset the state if there are no more touches
|
||||||
|
if ((this._ignored.length === 0) &&
|
||||||
|
(this._tracked.length === 0)) {
|
||||||
|
this._state = GH_INITSTATE;
|
||||||
|
this._waitingRelease = false;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We got a touchend before the timer triggered,
|
||||||
|
// this cannot result in a gesture anymore.
|
||||||
|
if (!this._hasDetectedGesture() &&
|
||||||
|
this._isTwoTouchTimeoutRunning()) {
|
||||||
|
this._stopTwoTouchTimeout();
|
||||||
|
this._state = GH_NOGESTURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some gestures don't trigger until a touch is released
|
||||||
|
if (!this._hasDetectedGesture()) {
|
||||||
|
// Can't be a gesture that relies on movement
|
||||||
|
this._state &= ~(GH_DRAG | GH_TWODRAG | GH_PINCH);
|
||||||
|
// Or something that relies on more time
|
||||||
|
this._state &= ~GH_LONGPRESS;
|
||||||
|
this._stopLongpressTimeout();
|
||||||
|
|
||||||
|
if (!this._waitingRelease) {
|
||||||
|
this._releaseStart = Date.now();
|
||||||
|
this._waitingRelease = true;
|
||||||
|
|
||||||
|
// Can't be a tap that requires more touches than we current have
|
||||||
|
switch (this._tracked.length) {
|
||||||
|
case 1:
|
||||||
|
this._state &= ~(GH_TWOTAP | GH_THREETAP);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 2:
|
||||||
|
this._state &= ~(GH_ONETAP | GH_THREETAP);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Waiting for all touches to release? (i.e. some tap)
|
||||||
|
if (this._waitingRelease) {
|
||||||
|
// Were all touches released at roughly the same time?
|
||||||
|
if ((Date.now() - this._releaseStart) > GH_MULTITOUCH_TIMEOUT) {
|
||||||
|
this._state = GH_NOGESTURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Did too long time pass between press and release?
|
||||||
|
if (this._tracked.some(t => (Date.now() - t.started) > GH_TAP_TIMEOUT)) {
|
||||||
|
this._state = GH_NOGESTURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
let touch = this._tracked.find(t => t.id === id);
|
||||||
|
touch.active = false;
|
||||||
|
|
||||||
|
// Are we still waiting for more releases?
|
||||||
|
if (this._hasDetectedGesture()) {
|
||||||
|
this._pushEvent('gesturestart');
|
||||||
|
} else {
|
||||||
|
// Have we reached a dead end?
|
||||||
|
if (this._state !== GH_NOGESTURE) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._hasDetectedGesture()) {
|
||||||
|
this._pushEvent('gestureend');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ignore any remaining touches until they are ended
|
||||||
|
for (let i = 0; i < this._tracked.length; i++) {
|
||||||
|
if (this._tracked[i].active) {
|
||||||
|
this._ignored.push(this._tracked[i].id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._tracked = [];
|
||||||
|
|
||||||
|
this._state = GH_NOGESTURE;
|
||||||
|
|
||||||
|
// Remove this touch from ignored if it's in there
|
||||||
|
if (this._ignored.indexOf(id) !== -1) {
|
||||||
|
this._ignored.splice(this._ignored.indexOf(id), 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We reset the state if ignored is empty
|
||||||
|
if ((this._ignored.length === 0)) {
|
||||||
|
this._state = GH_INITSTATE;
|
||||||
|
this._waitingRelease = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_hasDetectedGesture() {
|
||||||
|
if (this._state === GH_NOGESTURE) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Check to see if the bitmask value is a power of 2
|
||||||
|
// (i.e. only one bit set). If it is, we have a state.
|
||||||
|
if (this._state & (this._state - 1)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For taps we also need to have all touches released
|
||||||
|
// before we've fully detected the gesture
|
||||||
|
if (this._state & (GH_ONETAP | GH_TWOTAP | GH_THREETAP)) {
|
||||||
|
if (this._tracked.some(t => t.active)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_startLongpressTimeout() {
|
||||||
|
this._stopLongpressTimeout();
|
||||||
|
this._longpressTimeoutId = setTimeout(() => this._longpressTimeout(),
|
||||||
|
GH_LONGPRESS_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
_stopLongpressTimeout() {
|
||||||
|
clearTimeout(this._longpressTimeoutId);
|
||||||
|
this._longpressTimeoutId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_longpressTimeout() {
|
||||||
|
if (this._hasDetectedGesture()) {
|
||||||
|
throw new Error("A longpress gesture failed, conflict with a different gesture");
|
||||||
|
}
|
||||||
|
|
||||||
|
this._state = GH_LONGPRESS;
|
||||||
|
this._pushEvent('gesturestart');
|
||||||
|
}
|
||||||
|
|
||||||
|
_startTwoTouchTimeout() {
|
||||||
|
this._stopTwoTouchTimeout();
|
||||||
|
this._twoTouchTimeoutId = setTimeout(() => this._twoTouchTimeout(),
|
||||||
|
GH_TWOTOUCH_TIMEOUT);
|
||||||
|
}
|
||||||
|
|
||||||
|
_stopTwoTouchTimeout() {
|
||||||
|
clearTimeout(this._twoTouchTimeoutId);
|
||||||
|
this._twoTouchTimeoutId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_isTwoTouchTimeoutRunning() {
|
||||||
|
return this._twoTouchTimeoutId !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
_twoTouchTimeout() {
|
||||||
|
if (this._tracked.length === 0) {
|
||||||
|
throw new Error("A pinch or two drag gesture failed, no tracked touches");
|
||||||
|
}
|
||||||
|
|
||||||
|
// How far each touch point has moved since start
|
||||||
|
let avgM = this._getAverageMovement();
|
||||||
|
let avgMoveH = Math.abs(avgM.x);
|
||||||
|
let avgMoveV = Math.abs(avgM.y);
|
||||||
|
|
||||||
|
// The difference in the distance between where
|
||||||
|
// the touch points started and where they are now
|
||||||
|
let avgD = this._getAverageDistance();
|
||||||
|
let deltaTouchDistance = Math.abs(Math.hypot(avgD.first.x, avgD.first.y) -
|
||||||
|
Math.hypot(avgD.last.x, avgD.last.y));
|
||||||
|
|
||||||
|
if ((avgMoveV < deltaTouchDistance) &&
|
||||||
|
(avgMoveH < deltaTouchDistance)) {
|
||||||
|
this._state = GH_PINCH;
|
||||||
|
} else {
|
||||||
|
this._state = GH_TWODRAG;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._pushEvent('gesturestart');
|
||||||
|
this._pushEvent('gesturemove');
|
||||||
|
}
|
||||||
|
|
||||||
|
_pushEvent(type) {
|
||||||
|
let detail = { type: this._stateToGesture(this._state) };
|
||||||
|
|
||||||
|
// For most gesture events the current (average) position is the
|
||||||
|
// most useful
|
||||||
|
let avg = this._getPosition();
|
||||||
|
let pos = avg.last;
|
||||||
|
|
||||||
|
// However we have a slight distance to detect gestures, so for the
|
||||||
|
// first gesture event we want to use the first positions we saw
|
||||||
|
if (type === 'gesturestart') {
|
||||||
|
pos = avg.first;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For these gestures, we always want the event coordinates
|
||||||
|
// to be where the gesture began, not the current touch location.
|
||||||
|
switch (this._state) {
|
||||||
|
case GH_TWODRAG:
|
||||||
|
case GH_PINCH:
|
||||||
|
pos = avg.first;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
detail['clientX'] = pos.x;
|
||||||
|
detail['clientY'] = pos.y;
|
||||||
|
|
||||||
|
// FIXME: other coordinates?
|
||||||
|
|
||||||
|
// Some gestures also have a magnitude
|
||||||
|
if (this._state === GH_PINCH) {
|
||||||
|
let distance = this._getAverageDistance();
|
||||||
|
if (type === 'gesturestart') {
|
||||||
|
detail['magnitudeX'] = distance.first.x;
|
||||||
|
detail['magnitudeY'] = distance.first.y;
|
||||||
|
} else {
|
||||||
|
detail['magnitudeX'] = distance.last.x;
|
||||||
|
detail['magnitudeY'] = distance.last.y;
|
||||||
|
}
|
||||||
|
} else if (this._state === GH_TWODRAG) {
|
||||||
|
if (type === 'gesturestart') {
|
||||||
|
detail['magnitudeX'] = 0.0;
|
||||||
|
detail['magnitudeY'] = 0.0;
|
||||||
|
} else {
|
||||||
|
let movement = this._getAverageMovement();
|
||||||
|
detail['magnitudeX'] = movement.x;
|
||||||
|
detail['magnitudeY'] = movement.y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let gev = new CustomEvent(type, { detail: detail });
|
||||||
|
this._target.dispatchEvent(gev);
|
||||||
|
}
|
||||||
|
|
||||||
|
_stateToGesture(state) {
|
||||||
|
switch (state) {
|
||||||
|
case GH_ONETAP:
|
||||||
|
return 'onetap';
|
||||||
|
case GH_TWOTAP:
|
||||||
|
return 'twotap';
|
||||||
|
case GH_THREETAP:
|
||||||
|
return 'threetap';
|
||||||
|
case GH_DRAG:
|
||||||
|
return 'drag';
|
||||||
|
case GH_LONGPRESS:
|
||||||
|
return 'longpress';
|
||||||
|
case GH_TWODRAG:
|
||||||
|
return 'twodrag';
|
||||||
|
case GH_PINCH:
|
||||||
|
return 'pinch';
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("Unknown gesture state: " + state);
|
||||||
|
}
|
||||||
|
|
||||||
|
_getPosition() {
|
||||||
|
if (this._tracked.length === 0) {
|
||||||
|
throw new Error("Failed to get gesture position, no tracked touches");
|
||||||
|
}
|
||||||
|
|
||||||
|
let size = this._tracked.length;
|
||||||
|
let fx = 0, fy = 0, lx = 0, ly = 0;
|
||||||
|
|
||||||
|
for (let i = 0; i < this._tracked.length; i++) {
|
||||||
|
fx += this._tracked[i].firstX;
|
||||||
|
fy += this._tracked[i].firstY;
|
||||||
|
lx += this._tracked[i].lastX;
|
||||||
|
ly += this._tracked[i].lastY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { first: { x: fx / size,
|
||||||
|
y: fy / size },
|
||||||
|
last: { x: lx / size,
|
||||||
|
y: ly / size } };
|
||||||
|
}
|
||||||
|
|
||||||
|
_getAverageMovement() {
|
||||||
|
if (this._tracked.length === 0) {
|
||||||
|
throw new Error("Failed to get gesture movement, no tracked touches");
|
||||||
|
}
|
||||||
|
|
||||||
|
let totalH, totalV;
|
||||||
|
totalH = totalV = 0;
|
||||||
|
let size = this._tracked.length;
|
||||||
|
|
||||||
|
for (let i = 0; i < this._tracked.length; i++) {
|
||||||
|
totalH += this._tracked[i].lastX - this._tracked[i].firstX;
|
||||||
|
totalV += this._tracked[i].lastY - this._tracked[i].firstY;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { x: totalH / size,
|
||||||
|
y: totalV / size };
|
||||||
|
}
|
||||||
|
|
||||||
|
_getAverageDistance() {
|
||||||
|
if (this._tracked.length === 0) {
|
||||||
|
throw new Error("Failed to get gesture distance, no tracked touches");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Distance between the first and last tracked touches
|
||||||
|
|
||||||
|
let first = this._tracked[0];
|
||||||
|
let last = this._tracked[this._tracked.length - 1];
|
||||||
|
|
||||||
|
let fdx = Math.abs(last.firstX - first.firstX);
|
||||||
|
let fdy = Math.abs(last.firstY - first.firstY);
|
||||||
|
|
||||||
|
let ldx = Math.abs(last.lastX - first.lastX);
|
||||||
|
let ldy = Math.abs(last.lastY - first.lastY);
|
||||||
|
|
||||||
|
return { first: { x: fdx, y: fdy },
|
||||||
|
last: { x: ldx, y: ldy } };
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2018 The noVNC Authors
|
* Copyright (C) 2019 The noVNC Authors
|
||||||
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -118,9 +118,7 @@ export default class Keyboard {
|
||||||
|
|
||||||
// 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
|
||||||
// (iOS omits tracking info on keyup events, which forces us to
|
if (code === 'Unidentified') {
|
||||||
// special treat that platform here)
|
|
||||||
if ((code === 'Unidentified') || browser.isIOS()) {
|
|
||||||
if (keysym) {
|
if (keysym) {
|
||||||
// If it's a virtual keyboard then it should be
|
// If it's a virtual keyboard then it should be
|
||||||
// sufficient to just send press and release right
|
// sufficient to just send press and release right
|
||||||
|
@ -137,7 +135,7 @@ export default class Keyboard {
|
||||||
// keys around a bit to make things more sane for the remote
|
// keys around a bit to make things more sane for the remote
|
||||||
// server. This method is used by RealVNC and TigerVNC (and
|
// server. This method is used by RealVNC and TigerVNC (and
|
||||||
// possibly others).
|
// possibly others).
|
||||||
if (browser.isMac()) {
|
if (browser.isMac() || browser.isIOS()) {
|
||||||
switch (keysym) {
|
switch (keysym) {
|
||||||
case KeyTable.XK_Super_L:
|
case KeyTable.XK_Super_L:
|
||||||
keysym = KeyTable.XK_Alt_L;
|
keysym = KeyTable.XK_Alt_L;
|
||||||
|
@ -164,7 +162,7 @@ export default class Keyboard {
|
||||||
// state change events. That gets extra confusing for CapsLock
|
// state change events. That gets extra confusing for CapsLock
|
||||||
// which toggles on each press, but not on release. So pretend
|
// which toggles on each press, but not on release. So pretend
|
||||||
// it was a quick press and release of the button.
|
// it was a quick press and release of the button.
|
||||||
if (browser.isMac() && (code === 'CapsLock')) {
|
if ((browser.isMac() || browser.isIOS()) && (code === 'CapsLock')) {
|
||||||
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);
|
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);
|
||||||
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);
|
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);
|
||||||
stopEvent(e);
|
stopEvent(e);
|
||||||
|
@ -276,13 +274,28 @@ export default class Keyboard {
|
||||||
}
|
}
|
||||||
|
|
||||||
// See comment in _handleKeyDown()
|
// See comment in _handleKeyDown()
|
||||||
if (browser.isMac() && (code === 'CapsLock')) {
|
if ((browser.isMac() || browser.isIOS()) && (code === 'CapsLock')) {
|
||||||
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);
|
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);
|
||||||
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);
|
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._sendKeyEvent(this._keyDownList[code], code, false);
|
this._sendKeyEvent(this._keyDownList[code], code, false);
|
||||||
|
|
||||||
|
// Windows has a rather nasty bug where it won't send key
|
||||||
|
// release events for a Shift button if the other Shift is still
|
||||||
|
// pressed
|
||||||
|
if (browser.isWindows() && ((code === 'ShiftLeft') ||
|
||||||
|
(code === 'ShiftRight'))) {
|
||||||
|
if ('ShiftRight' in this._keyDownList) {
|
||||||
|
this._sendKeyEvent(this._keyDownList['ShiftRight'],
|
||||||
|
'ShiftRight', false);
|
||||||
|
}
|
||||||
|
if ('ShiftLeft' in this._keyDownList) {
|
||||||
|
this._sendKeyEvent(this._keyDownList['ShiftLeft'],
|
||||||
|
'ShiftLeft', false);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleAltGrTimeout() {
|
_handleAltGrTimeout() {
|
||||||
|
@ -299,8 +312,11 @@ export default class Keyboard {
|
||||||
Log.Debug("<< Keyboard.allKeysUp");
|
Log.Debug("<< Keyboard.allKeysUp");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Firefox Alt workaround, see below
|
// Alt workaround for Firefox on Windows, see below
|
||||||
_checkAlt(e) {
|
_checkAlt(e) {
|
||||||
|
if (e.skipCheckAlt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (e.altKey) {
|
if (e.altKey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -315,6 +331,7 @@ export default class Keyboard {
|
||||||
const event = new KeyboardEvent('keyup',
|
const event = new KeyboardEvent('keyup',
|
||||||
{ key: downList[code],
|
{ key: downList[code],
|
||||||
code: code });
|
code: code });
|
||||||
|
event.skipCheckAlt = true;
|
||||||
target.dispatchEvent(event);
|
target.dispatchEvent(event);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -331,9 +348,10 @@ export default class Keyboard {
|
||||||
// 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
|
// Firefox on Windows has broken handling of Alt, so we need to
|
||||||
// best we can for releases (still doesn't prevent the menu
|
// poll as best we can for releases (still doesn't prevent the
|
||||||
// from popping up though as we can't call preventDefault())
|
// menu from popping up though as we can't call
|
||||||
|
// preventDefault())
|
||||||
if (browser.isWindows() && browser.isFirefox()) {
|
if (browser.isWindows() && browser.isFirefox()) {
|
||||||
const handler = this._eventHandlers.checkalt;
|
const handler = this._eventHandlers.checkalt;
|
||||||
['mousedown', 'mouseup', 'mousemove', 'wheel',
|
['mousedown', 'mouseup', 'mousemove', 'wheel',
|
||||||
|
|
|
@ -1,276 +0,0 @@
|
||||||
/*
|
|
||||||
* noVNC: HTML5 VNC client
|
|
||||||
* Copyright (C) 2018 The noVNC Authors
|
|
||||||
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as Log from '../util/logging.js';
|
|
||||||
import { isTouchDevice } from '../util/browser.js';
|
|
||||||
import { setCapture, stopEvent, getPointerEvent } from '../util/events.js';
|
|
||||||
|
|
||||||
const WHEEL_STEP = 10; // Delta threshold for a mouse wheel step
|
|
||||||
const WHEEL_STEP_TIMEOUT = 50; // ms
|
|
||||||
const WHEEL_LINE_HEIGHT = 19;
|
|
||||||
|
|
||||||
export default class Mouse {
|
|
||||||
constructor(target) {
|
|
||||||
this._target = target || document;
|
|
||||||
|
|
||||||
this._doubleClickTimer = null;
|
|
||||||
this._lastTouchPos = null;
|
|
||||||
|
|
||||||
this._pos = null;
|
|
||||||
this._wheelStepXTimer = null;
|
|
||||||
this._wheelStepYTimer = null;
|
|
||||||
this._accumulatedWheelDeltaX = 0;
|
|
||||||
this._accumulatedWheelDeltaY = 0;
|
|
||||||
|
|
||||||
this._eventHandlers = {
|
|
||||||
'mousedown': this._handleMouseDown.bind(this),
|
|
||||||
'mouseup': this._handleMouseUp.bind(this),
|
|
||||||
'mousemove': this._handleMouseMove.bind(this),
|
|
||||||
'mousewheel': this._handleMouseWheel.bind(this),
|
|
||||||
'mousedisable': this._handleMouseDisable.bind(this)
|
|
||||||
};
|
|
||||||
|
|
||||||
// ===== PROPERTIES =====
|
|
||||||
|
|
||||||
this.touchButton = 1; // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
|
|
||||||
|
|
||||||
// ===== EVENT HANDLERS =====
|
|
||||||
|
|
||||||
this.onmousebutton = () => {}; // Handler for mouse button click/release
|
|
||||||
this.onmousemove = () => {}; // Handler for mouse movement
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== PRIVATE METHODS =====
|
|
||||||
|
|
||||||
_resetDoubleClickTimer() {
|
|
||||||
this._doubleClickTimer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleMouseButton(e, down) {
|
|
||||||
this._updateMousePosition(e);
|
|
||||||
let pos = this._pos;
|
|
||||||
|
|
||||||
let bmask;
|
|
||||||
if (e.touches || e.changedTouches) {
|
|
||||||
// Touch device
|
|
||||||
|
|
||||||
// When two touches occur within 500 ms of each other and are
|
|
||||||
// close enough together a double click is triggered.
|
|
||||||
if (down == 1) {
|
|
||||||
if (this._doubleClickTimer === null) {
|
|
||||||
this._lastTouchPos = pos;
|
|
||||||
} else {
|
|
||||||
clearTimeout(this._doubleClickTimer);
|
|
||||||
|
|
||||||
// When the distance between the two touches is small enough
|
|
||||||
// force the position of the latter touch to the position of
|
|
||||||
// the first.
|
|
||||||
|
|
||||||
const xs = this._lastTouchPos.x - pos.x;
|
|
||||||
const ys = this._lastTouchPos.y - pos.y;
|
|
||||||
const d = Math.sqrt((xs * xs) + (ys * ys));
|
|
||||||
|
|
||||||
// The goal is to trigger on a certain physical width, the
|
|
||||||
// devicePixelRatio brings us a bit closer but is not optimal.
|
|
||||||
const threshold = 20 * (window.devicePixelRatio || 1);
|
|
||||||
if (d < threshold) {
|
|
||||||
pos = this._lastTouchPos;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this._doubleClickTimer = setTimeout(this._resetDoubleClickTimer.bind(this), 500);
|
|
||||||
}
|
|
||||||
bmask = this.touchButton;
|
|
||||||
// If bmask is set
|
|
||||||
} else if (e.which) {
|
|
||||||
/* everything except IE */
|
|
||||||
bmask = 1 << e.button;
|
|
||||||
} else {
|
|
||||||
/* IE including 9 */
|
|
||||||
bmask = (e.button & 0x1) + // Left
|
|
||||||
(e.button & 0x2) * 2 + // Right
|
|
||||||
(e.button & 0x4) / 2; // Middle
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.Debug("onmousebutton " + (down ? "down" : "up") +
|
|
||||||
", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
|
|
||||||
this.onmousebutton(pos.x, pos.y, down, bmask);
|
|
||||||
|
|
||||||
stopEvent(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleMouseDown(e) {
|
|
||||||
// Touch events have implicit capture
|
|
||||||
if (e.type === "mousedown") {
|
|
||||||
setCapture(this._target);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._handleMouseButton(e, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleMouseUp(e) {
|
|
||||||
this._handleMouseButton(e, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mouse wheel events are sent in steps over VNC. This means that the VNC
|
|
||||||
// protocol can't handle a wheel event with specific distance or speed.
|
|
||||||
// Therefor, if we get a lot of small mouse wheel events we combine them.
|
|
||||||
_generateWheelStepX() {
|
|
||||||
|
|
||||||
if (this._accumulatedWheelDeltaX < 0) {
|
|
||||||
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 5);
|
|
||||||
this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 5);
|
|
||||||
} else if (this._accumulatedWheelDeltaX > 0) {
|
|
||||||
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 6);
|
|
||||||
this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 6);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._accumulatedWheelDeltaX = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
_generateWheelStepY() {
|
|
||||||
|
|
||||||
if (this._accumulatedWheelDeltaY < 0) {
|
|
||||||
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 3);
|
|
||||||
this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 3);
|
|
||||||
} else if (this._accumulatedWheelDeltaY > 0) {
|
|
||||||
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 4);
|
|
||||||
this.onmousebutton(this._pos.x, this._pos.y, 0, 1 << 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._accumulatedWheelDeltaY = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
_resetWheelStepTimers() {
|
|
||||||
window.clearTimeout(this._wheelStepXTimer);
|
|
||||||
window.clearTimeout(this._wheelStepYTimer);
|
|
||||||
this._wheelStepXTimer = null;
|
|
||||||
this._wheelStepYTimer = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleMouseWheel(e) {
|
|
||||||
this._resetWheelStepTimers();
|
|
||||||
|
|
||||||
this._updateMousePosition(e);
|
|
||||||
|
|
||||||
let dX = e.deltaX;
|
|
||||||
let dY = e.deltaY;
|
|
||||||
|
|
||||||
// Pixel units unless it's non-zero.
|
|
||||||
// Note that if deltamode is line or page won't matter since we aren't
|
|
||||||
// sending the mouse wheel delta to the server anyway.
|
|
||||||
// The difference between pixel and line can be important however since
|
|
||||||
// we have a threshold that can be smaller than the line height.
|
|
||||||
if (e.deltaMode !== 0) {
|
|
||||||
dX *= WHEEL_LINE_HEIGHT;
|
|
||||||
dY *= WHEEL_LINE_HEIGHT;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._accumulatedWheelDeltaX += dX;
|
|
||||||
this._accumulatedWheelDeltaY += dY;
|
|
||||||
|
|
||||||
// Generate a mouse wheel step event when the accumulated delta
|
|
||||||
// for one of the axes is large enough.
|
|
||||||
// Small delta events that do not pass the threshold get sent
|
|
||||||
// after a timeout.
|
|
||||||
if (Math.abs(this._accumulatedWheelDeltaX) > WHEEL_STEP) {
|
|
||||||
this._generateWheelStepX();
|
|
||||||
} else {
|
|
||||||
this._wheelStepXTimer =
|
|
||||||
window.setTimeout(this._generateWheelStepX.bind(this),
|
|
||||||
WHEEL_STEP_TIMEOUT);
|
|
||||||
}
|
|
||||||
if (Math.abs(this._accumulatedWheelDeltaY) > WHEEL_STEP) {
|
|
||||||
this._generateWheelStepY();
|
|
||||||
} else {
|
|
||||||
this._wheelStepYTimer =
|
|
||||||
window.setTimeout(this._generateWheelStepY.bind(this),
|
|
||||||
WHEEL_STEP_TIMEOUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
stopEvent(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleMouseMove(e) {
|
|
||||||
this._updateMousePosition(e);
|
|
||||||
this.onmousemove(this._pos.x, this._pos.y);
|
|
||||||
stopEvent(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleMouseDisable(e) {
|
|
||||||
/*
|
|
||||||
* Stop propagation if inside canvas area
|
|
||||||
* Note: This is only needed for the 'click' event as it fails
|
|
||||||
* to fire properly for the target element so we have
|
|
||||||
* to listen on the document element instead.
|
|
||||||
*/
|
|
||||||
if (e.target == this._target) {
|
|
||||||
stopEvent(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update coordinates relative to target
|
|
||||||
_updateMousePosition(e) {
|
|
||||||
e = getPointerEvent(e);
|
|
||||||
const bounds = this._target.getBoundingClientRect();
|
|
||||||
let x;
|
|
||||||
let y;
|
|
||||||
// Clip to target bounds
|
|
||||||
if (e.clientX < bounds.left) {
|
|
||||||
x = 0;
|
|
||||||
} else if (e.clientX >= bounds.right) {
|
|
||||||
x = bounds.width - 1;
|
|
||||||
} else {
|
|
||||||
x = e.clientX - bounds.left;
|
|
||||||
}
|
|
||||||
if (e.clientY < bounds.top) {
|
|
||||||
y = 0;
|
|
||||||
} else if (e.clientY >= bounds.bottom) {
|
|
||||||
y = bounds.height - 1;
|
|
||||||
} else {
|
|
||||||
y = e.clientY - bounds.top;
|
|
||||||
}
|
|
||||||
this._pos = {x: x, y: y};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===== PUBLIC METHODS =====
|
|
||||||
|
|
||||||
grab() {
|
|
||||||
if (isTouchDevice) {
|
|
||||||
this._target.addEventListener('touchstart', this._eventHandlers.mousedown);
|
|
||||||
this._target.addEventListener('touchend', this._eventHandlers.mouseup);
|
|
||||||
this._target.addEventListener('touchmove', this._eventHandlers.mousemove);
|
|
||||||
}
|
|
||||||
this._target.addEventListener('mousedown', this._eventHandlers.mousedown);
|
|
||||||
this._target.addEventListener('mouseup', this._eventHandlers.mouseup);
|
|
||||||
this._target.addEventListener('mousemove', this._eventHandlers.mousemove);
|
|
||||||
this._target.addEventListener('wheel', this._eventHandlers.mousewheel);
|
|
||||||
|
|
||||||
/* Prevent middle-click pasting (see above for why we bind to document) */
|
|
||||||
document.addEventListener('click', this._eventHandlers.mousedisable);
|
|
||||||
|
|
||||||
/* preventDefault() on mousedown doesn't stop this event for some
|
|
||||||
reason so we have to explicitly block it */
|
|
||||||
this._target.addEventListener('contextmenu', this._eventHandlers.mousedisable);
|
|
||||||
}
|
|
||||||
|
|
||||||
ungrab() {
|
|
||||||
this._resetWheelStepTimers();
|
|
||||||
|
|
||||||
if (isTouchDevice) {
|
|
||||||
this._target.removeEventListener('touchstart', this._eventHandlers.mousedown);
|
|
||||||
this._target.removeEventListener('touchend', this._eventHandlers.mouseup);
|
|
||||||
this._target.removeEventListener('touchmove', this._eventHandlers.mousemove);
|
|
||||||
}
|
|
||||||
this._target.removeEventListener('mousedown', this._eventHandlers.mousedown);
|
|
||||||
this._target.removeEventListener('mouseup', this._eventHandlers.mouseup);
|
|
||||||
this._target.removeEventListener('mousemove', this._eventHandlers.mousemove);
|
|
||||||
this._target.removeEventListener('wheel', this._eventHandlers.mousewheel);
|
|
||||||
|
|
||||||
document.removeEventListener('click', this._eventHandlers.mousedisable);
|
|
||||||
|
|
||||||
this._target.removeEventListener('contextmenu', this._eventHandlers.mousedisable);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
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";
|
||||||
|
@ -91,6 +92,8 @@ export function getKey(evt) {
|
||||||
// Mozilla isn't fully in sync with the spec yet
|
// Mozilla isn't fully in sync with the spec yet
|
||||||
switch (evt.key) {
|
switch (evt.key) {
|
||||||
case 'OS': return 'Meta';
|
case 'OS': return 'Meta';
|
||||||
|
case 'LaunchMyComputer': return 'LaunchApplication1';
|
||||||
|
case 'LaunchCalculator': return 'LaunchApplication2';
|
||||||
}
|
}
|
||||||
|
|
||||||
// iOS leaks some OS names
|
// iOS leaks some OS names
|
||||||
|
@ -102,9 +105,21 @@ export function getKey(evt) {
|
||||||
case 'UIKeyInputEscape': return 'Escape';
|
case 'UIKeyInputEscape': return 'Escape';
|
||||||
}
|
}
|
||||||
|
|
||||||
// IE and Edge have broken handling of AltGraph so we cannot
|
// Broken behaviour in Chrome
|
||||||
// trust them for printable characters
|
if ((evt.key === '\x00') && (evt.code === 'NumpadDecimal')) {
|
||||||
if ((evt.key.length !== 1) || (!browser.isIE() && !browser.isEdge())) {
|
return 'Delete';
|
||||||
|
}
|
||||||
|
|
||||||
|
// IE and Edge need special handling, but for everyone else we
|
||||||
|
// can trust the value provided
|
||||||
|
if (!browser.isIE() && !browser.isEdge()) {
|
||||||
|
return evt.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IE and Edge have broken handling of AltGraph so we can only
|
||||||
|
// trust them for non-printable characters (and unfortunately
|
||||||
|
// they also specify 'Unidentified' for some problem keys)
|
||||||
|
if ((evt.key.length !== 1) && (evt.key !== 'Unidentified')) {
|
||||||
return evt.key;
|
return evt.key;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,10 +156,39 @@ export function getKeysym(evt) {
|
||||||
location = 2;
|
location = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// And for Clear
|
||||||
|
if ((key === 'Clear') && (location === 3)) {
|
||||||
|
let code = getKeycode(evt);
|
||||||
|
if (code === 'NumLock') {
|
||||||
|
location = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ((location === undefined) || (location > 3)) {
|
if ((location === undefined) || (location > 3)) {
|
||||||
location = 0;
|
location = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The original Meta key now gets confused with the Windows key
|
||||||
|
// https://bugs.chromium.org/p/chromium/issues/detail?id=1020141
|
||||||
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=1232918
|
||||||
|
if (key === 'Meta') {
|
||||||
|
let code = getKeycode(evt);
|
||||||
|
if (code === 'AltLeft') {
|
||||||
|
return KeyTable.XK_Meta_L;
|
||||||
|
} else if (code === 'AltRight') {
|
||||||
|
return KeyTable.XK_Meta_R;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// macOS has Clear instead of NumLock, but the remote system is
|
||||||
|
// probably not macOS, so lying here is probably best...
|
||||||
|
if (key === 'Clear') {
|
||||||
|
let code = getKeycode(evt);
|
||||||
|
if (code === 'NumLock') {
|
||||||
|
return KeyTable.XK_Num_Lock;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return DOMKeyTable[key][location];
|
return DOMKeyTable[key][location];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,9 +1,11 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2018 The noVNC Authors
|
* Copyright (C) 2019 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.
|
||||||
|
*
|
||||||
|
* Browser feature support detection
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import * as Log from './logging.js';
|
import * as Log from './logging.js';
|
||||||
|
@ -31,7 +33,7 @@ try {
|
||||||
const target = document.createElement('canvas');
|
const target = document.createElement('canvas');
|
||||||
target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
|
target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
|
||||||
|
|
||||||
if (target.style.cursor) {
|
if (target.style.cursor.indexOf("url") === 0) {
|
||||||
Log.Info("Data URI scheme cursor supported");
|
Log.Info("Data URI scheme cursor supported");
|
||||||
_supportsCursorURIs = true;
|
_supportsCursorURIs = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -52,6 +54,38 @@ try {
|
||||||
}
|
}
|
||||||
export const supportsImageMetadata = _supportsImageMetadata;
|
export const supportsImageMetadata = _supportsImageMetadata;
|
||||||
|
|
||||||
|
let _hasScrollbarGutter = true;
|
||||||
|
try {
|
||||||
|
// Create invisible container
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.style.visibility = 'hidden';
|
||||||
|
container.style.overflow = 'scroll'; // forcing scrollbars
|
||||||
|
document.body.appendChild(container);
|
||||||
|
|
||||||
|
// Create a div and place it in the container
|
||||||
|
const child = document.createElement('div');
|
||||||
|
container.appendChild(child);
|
||||||
|
|
||||||
|
// Calculate the difference between the container's full width
|
||||||
|
// and the child's width - the difference is the scrollbars
|
||||||
|
const scrollbarWidth = (container.offsetWidth - child.offsetWidth);
|
||||||
|
|
||||||
|
// Clean up
|
||||||
|
container.parentNode.removeChild(container);
|
||||||
|
|
||||||
|
_hasScrollbarGutter = scrollbarWidth != 0;
|
||||||
|
} catch (exc) {
|
||||||
|
Log.Error("Scrollbar test exception: " + exc);
|
||||||
|
}
|
||||||
|
export const hasScrollbarGutter = _hasScrollbarGutter;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The functions for detection of platforms and browsers below are exported
|
||||||
|
* but the use of these should be minimized as much as possible.
|
||||||
|
*
|
||||||
|
* It's better to use feature detection than platform detection.
|
||||||
|
*/
|
||||||
|
|
||||||
export function isMac() {
|
export function isMac() {
|
||||||
return navigator && !!(/mac/i).exec(navigator.platform);
|
return navigator && !!(/mac/i).exec(navigator.platform);
|
||||||
}
|
}
|
||||||
|
@ -67,10 +101,6 @@ 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() {
|
export function isSafari() {
|
||||||
return navigator && (navigator.userAgent.indexOf('Safari') !== -1 &&
|
return navigator && (navigator.userAgent.indexOf('Safari') !== -1 &&
|
||||||
navigator.userAgent.indexOf('Chrome') === -1);
|
navigator.userAgent.indexOf('Chrome') === -1);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2018 The noVNC Authors
|
* Copyright (C) 2019 The noVNC Authors
|
||||||
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -20,7 +20,6 @@ export default class Cursor {
|
||||||
this._canvas.style.pointerEvents = 'none';
|
this._canvas.style.pointerEvents = 'none';
|
||||||
// Can't use "display" because of Firefox bug #1445997
|
// Can't use "display" because of Firefox bug #1445997
|
||||||
this._canvas.style.visibility = 'hidden';
|
this._canvas.style.visibility = 'hidden';
|
||||||
document.body.appendChild(this._canvas);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._position = { x: 0, y: 0 };
|
this._position = { x: 0, y: 0 };
|
||||||
|
@ -31,9 +30,6 @@ export default class Cursor {
|
||||||
'mouseleave': this._handleMouseLeave.bind(this),
|
'mouseleave': this._handleMouseLeave.bind(this),
|
||||||
'mousemove': this._handleMouseMove.bind(this),
|
'mousemove': this._handleMouseMove.bind(this),
|
||||||
'mouseup': this._handleMouseUp.bind(this),
|
'mouseup': this._handleMouseUp.bind(this),
|
||||||
'touchstart': this._handleTouchStart.bind(this),
|
|
||||||
'touchmove': this._handleTouchMove.bind(this),
|
|
||||||
'touchend': this._handleTouchEnd.bind(this),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +41,8 @@ export default class Cursor {
|
||||||
this._target = target;
|
this._target = target;
|
||||||
|
|
||||||
if (useFallback) {
|
if (useFallback) {
|
||||||
|
document.body.appendChild(this._canvas);
|
||||||
|
|
||||||
// FIXME: These don't fire properly except for mouse
|
// FIXME: These don't fire properly except for mouse
|
||||||
/// movement in IE. We want to also capture element
|
/// movement in IE. We want to also capture element
|
||||||
// movement, size changes, visibility, etc.
|
// movement, size changes, visibility, etc.
|
||||||
|
@ -53,17 +51,16 @@ export default class Cursor {
|
||||||
this._target.addEventListener('mouseleave', this._eventHandlers.mouseleave, options);
|
this._target.addEventListener('mouseleave', this._eventHandlers.mouseleave, options);
|
||||||
this._target.addEventListener('mousemove', this._eventHandlers.mousemove, options);
|
this._target.addEventListener('mousemove', this._eventHandlers.mousemove, options);
|
||||||
this._target.addEventListener('mouseup', this._eventHandlers.mouseup, 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();
|
this.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
detach() {
|
detach() {
|
||||||
|
if (!this._target) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (useFallback) {
|
if (useFallback) {
|
||||||
const options = { capture: true, passive: true };
|
const options = { capture: true, passive: true };
|
||||||
this._target.removeEventListener('mouseover', this._eventHandlers.mouseover, options);
|
this._target.removeEventListener('mouseover', this._eventHandlers.mouseover, options);
|
||||||
|
@ -71,9 +68,7 @@ export default class Cursor {
|
||||||
this._target.removeEventListener('mousemove', this._eventHandlers.mousemove, options);
|
this._target.removeEventListener('mousemove', this._eventHandlers.mousemove, options);
|
||||||
this._target.removeEventListener('mouseup', this._eventHandlers.mouseup, options);
|
this._target.removeEventListener('mouseup', this._eventHandlers.mouseup, options);
|
||||||
|
|
||||||
window.removeEventListener('touchstart', this._eventHandlers.touchstart, options);
|
document.body.removeChild(this._canvas);
|
||||||
this._target.removeEventListener('touchmove', this._eventHandlers.touchmove, options);
|
|
||||||
this._target.removeEventListener('touchend', this._eventHandlers.touchend, options);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._target = null;
|
this._target = null;
|
||||||
|
@ -124,6 +119,27 @@ export default class Cursor {
|
||||||
this._hotSpot.y = 0;
|
this._hotSpot.y = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mouse events might be emulated, this allows
|
||||||
|
// moving the cursor in such cases
|
||||||
|
move(clientX, clientY) {
|
||||||
|
if (!useFallback) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// clientX/clientY are relative the _visual viewport_,
|
||||||
|
// but our position is relative the _layout viewport_,
|
||||||
|
// so try to compensate when we can
|
||||||
|
if (window.visualViewport) {
|
||||||
|
this._position.x = clientX + window.visualViewport.offsetLeft;
|
||||||
|
this._position.y = clientY + window.visualViewport.offsetTop;
|
||||||
|
} else {
|
||||||
|
this._position.x = clientX;
|
||||||
|
this._position.y = clientY;
|
||||||
|
}
|
||||||
|
this._updatePosition();
|
||||||
|
let target = document.elementFromPoint(clientX, clientY);
|
||||||
|
this._updateVisibility(target);
|
||||||
|
}
|
||||||
|
|
||||||
_handleMouseOver(event) {
|
_handleMouseOver(event) {
|
||||||
// This event could be because we're entering the target, or
|
// This event could be because we're entering the target, or
|
||||||
// moving around amongst its sub elements. Let the move handler
|
// moving around amongst its sub elements. Let the move handler
|
||||||
|
@ -132,7 +148,8 @@ export default class Cursor {
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleMouseLeave(event) {
|
_handleMouseLeave(event) {
|
||||||
this._hideCursor();
|
// Check if we should show the cursor on the element we are leaving to
|
||||||
|
this._updateVisibility(event.relatedTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
_handleMouseMove(event) {
|
_handleMouseMove(event) {
|
||||||
|
@ -150,27 +167,29 @@ export default class Cursor {
|
||||||
// now and adjust visibility based on that.
|
// now and adjust visibility based on that.
|
||||||
let target = document.elementFromPoint(event.clientX, event.clientY);
|
let target = document.elementFromPoint(event.clientX, event.clientY);
|
||||||
this._updateVisibility(target);
|
this._updateVisibility(target);
|
||||||
|
|
||||||
|
// Captures end with a mouseup but we can't know the event order of
|
||||||
|
// mouseup vs releaseCapture.
|
||||||
|
//
|
||||||
|
// In the cases when releaseCapture comes first, the code above is
|
||||||
|
// enough.
|
||||||
|
//
|
||||||
|
// In the cases when the mouseup comes first, we need wait for the
|
||||||
|
// browser to flush all events and then check again if the cursor
|
||||||
|
// should be visible.
|
||||||
|
if (this._captureIsActive()) {
|
||||||
|
window.setTimeout(() => {
|
||||||
|
// We might have detached at this point
|
||||||
|
if (!this._target) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
// Refresh the target from elementFromPoint since queued events
|
||||||
_handleTouchStart(event) {
|
// might have altered the DOM
|
||||||
// Just as for mouseover, we let the move handler deal with it
|
target = document.elementFromPoint(event.clientX,
|
||||||
this._handleTouchMove(event);
|
event.clientY);
|
||||||
}
|
|
||||||
|
|
||||||
_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);
|
this._updateVisibility(target);
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_showCursor() {
|
_showCursor() {
|
||||||
|
@ -189,6 +208,9 @@ export default class Cursor {
|
||||||
// (i.e. are we over the target, or a child of the target without a
|
// (i.e. are we over the target, or a child of the target without a
|
||||||
// different cursor set)
|
// different cursor set)
|
||||||
_shouldShowCursor(target) {
|
_shouldShowCursor(target) {
|
||||||
|
if (!target) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
// Easy case
|
// Easy case
|
||||||
if (target === this._target) {
|
if (target === this._target) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -207,6 +229,11 @@ export default class Cursor {
|
||||||
}
|
}
|
||||||
|
|
||||||
_updateVisibility(target) {
|
_updateVisibility(target) {
|
||||||
|
// When the cursor target has capture we want to show the cursor.
|
||||||
|
// So, if a capture is active - look at the captured element instead.
|
||||||
|
if (this._captureIsActive()) {
|
||||||
|
target = document.captureElement;
|
||||||
|
}
|
||||||
if (this._shouldShowCursor(target)) {
|
if (this._shouldShowCursor(target)) {
|
||||||
this._showCursor();
|
this._showCursor();
|
||||||
} else {
|
} else {
|
||||||
|
@ -218,4 +245,9 @@ export default class Cursor {
|
||||||
this._canvas.style.left = this._position.x + "px";
|
this._canvas.style.left = this._position.x + "px";
|
||||||
this._canvas.style.top = this._position.y + "px";
|
this._canvas.style.top = this._position.y + "px";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_captureIsActive() {
|
||||||
|
return document.captureElement &&
|
||||||
|
document.documentElement.contains(document.captureElement);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
32
static/js/novnc/core/util/element.js
Executable file
32
static/js/novnc/core/util/element.js
Executable file
|
@ -0,0 +1,32 @@
|
||||||
|
/*
|
||||||
|
* noVNC: HTML5 VNC client
|
||||||
|
* Copyright (C) 2020 The noVNC Authors
|
||||||
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
|
*
|
||||||
|
* See README.md for usage and integration instructions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* HTML element utility functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function clientToElement(x, y, elem) {
|
||||||
|
const bounds = elem.getBoundingClientRect();
|
||||||
|
let pos = { x: 0, y: 0 };
|
||||||
|
// Clip to target bounds
|
||||||
|
if (x < bounds.left) {
|
||||||
|
pos.x = 0;
|
||||||
|
} else if (x >= bounds.right) {
|
||||||
|
pos.x = bounds.width - 1;
|
||||||
|
} else {
|
||||||
|
pos.x = x - bounds.left;
|
||||||
|
}
|
||||||
|
if (y < bounds.top) {
|
||||||
|
pos.y = 0;
|
||||||
|
} else if (y >= bounds.bottom) {
|
||||||
|
pos.y = bounds.height - 1;
|
||||||
|
} else {
|
||||||
|
pos.y = y - bounds.top;
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
|
@ -21,7 +21,8 @@ export function stopEvent(e) {
|
||||||
|
|
||||||
// Emulate Element.setCapture() when not supported
|
// Emulate Element.setCapture() when not supported
|
||||||
let _captureRecursion = false;
|
let _captureRecursion = false;
|
||||||
let _captureElem = null;
|
let _elementForUnflushedEvents = null;
|
||||||
|
document.captureElement = 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;
|
||||||
|
@ -30,7 +31,11 @@ function _captureProxy(e) {
|
||||||
const newEv = new e.constructor(e.type, e);
|
const newEv = new e.constructor(e.type, e);
|
||||||
|
|
||||||
_captureRecursion = true;
|
_captureRecursion = true;
|
||||||
_captureElem.dispatchEvent(newEv);
|
if (document.captureElement) {
|
||||||
|
document.captureElement.dispatchEvent(newEv);
|
||||||
|
} else {
|
||||||
|
_elementForUnflushedEvents.dispatchEvent(newEv);
|
||||||
|
}
|
||||||
_captureRecursion = false;
|
_captureRecursion = false;
|
||||||
|
|
||||||
// Avoid double events
|
// Avoid double events
|
||||||
|
@ -48,58 +53,56 @@ function _captureProxy(e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Follow cursor style of target element
|
// Follow cursor style of target element
|
||||||
function _captureElemChanged() {
|
function _capturedElemChanged() {
|
||||||
const captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
const proxyElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||||
captureElem.style.cursor = window.getComputedStyle(_captureElem).cursor;
|
proxyElem.style.cursor = window.getComputedStyle(document.captureElement).cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
const _captureObserver = new MutationObserver(_captureElemChanged);
|
const _captureObserver = new MutationObserver(_capturedElemChanged);
|
||||||
|
|
||||||
let _captureIndex = 0;
|
export function setCapture(target) {
|
||||||
|
if (target.setCapture) {
|
||||||
|
|
||||||
export function setCapture(elem) {
|
target.setCapture();
|
||||||
if (elem.setCapture) {
|
document.captureElement = target;
|
||||||
|
|
||||||
elem.setCapture();
|
|
||||||
|
|
||||||
// IE releases capture on 'click' events which might not trigger
|
// IE releases capture on 'click' events which might not trigger
|
||||||
elem.addEventListener('mouseup', releaseCapture);
|
target.addEventListener('mouseup', releaseCapture);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Release any existing capture in case this method is
|
// Release any existing capture in case this method is
|
||||||
// called multiple times without coordination
|
// called multiple times without coordination
|
||||||
releaseCapture();
|
releaseCapture();
|
||||||
|
|
||||||
let captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
let proxyElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||||
|
|
||||||
if (captureElem === null) {
|
if (proxyElem === null) {
|
||||||
captureElem = document.createElement("div");
|
proxyElem = document.createElement("div");
|
||||||
captureElem.id = "noVNC_mouse_capture_elem";
|
proxyElem.id = "noVNC_mouse_capture_elem";
|
||||||
captureElem.style.position = "fixed";
|
proxyElem.style.position = "fixed";
|
||||||
captureElem.style.top = "0px";
|
proxyElem.style.top = "0px";
|
||||||
captureElem.style.left = "0px";
|
proxyElem.style.left = "0px";
|
||||||
captureElem.style.width = "100%";
|
proxyElem.style.width = "100%";
|
||||||
captureElem.style.height = "100%";
|
proxyElem.style.height = "100%";
|
||||||
captureElem.style.zIndex = 10000;
|
proxyElem.style.zIndex = 10000;
|
||||||
captureElem.style.display = "none";
|
proxyElem.style.display = "none";
|
||||||
document.body.appendChild(captureElem);
|
document.body.appendChild(proxyElem);
|
||||||
|
|
||||||
// This is to make sure callers don't get confused by having
|
// This is to make sure callers don't get confused by having
|
||||||
// our blocking element as the target
|
// our blocking element as the target
|
||||||
captureElem.addEventListener('contextmenu', _captureProxy);
|
proxyElem.addEventListener('contextmenu', _captureProxy);
|
||||||
|
|
||||||
captureElem.addEventListener('mousemove', _captureProxy);
|
proxyElem.addEventListener('mousemove', _captureProxy);
|
||||||
captureElem.addEventListener('mouseup', _captureProxy);
|
proxyElem.addEventListener('mouseup', _captureProxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
_captureElem = elem;
|
document.captureElement = target;
|
||||||
_captureIndex++;
|
|
||||||
|
|
||||||
// Track cursor and get initial cursor
|
// Track cursor and get initial cursor
|
||||||
_captureObserver.observe(elem, {attributes: true});
|
_captureObserver.observe(target, {attributes: true});
|
||||||
_captureElemChanged();
|
_capturedElemChanged();
|
||||||
|
|
||||||
captureElem.style.display = "";
|
proxyElem.style.display = "";
|
||||||
|
|
||||||
// We listen to events on window in order to keep tracking if it
|
// We listen to events on window in order to keep tracking if it
|
||||||
// happens to leave the viewport
|
// happens to leave the viewport
|
||||||
|
@ -112,26 +115,26 @@ export function releaseCapture() {
|
||||||
if (document.releaseCapture) {
|
if (document.releaseCapture) {
|
||||||
|
|
||||||
document.releaseCapture();
|
document.releaseCapture();
|
||||||
|
document.captureElement = null;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (!_captureElem) {
|
if (!document.captureElement) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// There might be events already queued, so we need to wait for
|
// There might be events already queued. The event proxy needs
|
||||||
// them to flush. E.g. contextmenu in Microsoft Edge
|
// access to the captured element for these queued events.
|
||||||
window.setTimeout((expected) => {
|
// E.g. contextmenu (right-click) in Microsoft Edge
|
||||||
// Only clear it if it's the expected grab (i.e. no one
|
//
|
||||||
// else has initiated a new grab)
|
// Before removing the capturedElem pointer we save it to a
|
||||||
if (_captureIndex === expected) {
|
// temporary variable that the unflushed events can use.
|
||||||
_captureElem = null;
|
_elementForUnflushedEvents = document.captureElement;
|
||||||
}
|
document.captureElement = null;
|
||||||
}, 0, _captureIndex);
|
|
||||||
|
|
||||||
_captureObserver.disconnect();
|
_captureObserver.disconnect();
|
||||||
|
|
||||||
const captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
const proxyElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||||
captureElem.style.display = "none";
|
proxyElem.style.display = "none";
|
||||||
|
|
||||||
window.removeEventListener('mousemove', _captureProxy);
|
window.removeEventListener('mousemove', _captureProxy);
|
||||||
window.removeEventListener('mouseup', _captureProxy);
|
window.removeEventListener('mouseup', _captureProxy);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2018 The noVNC Authors
|
* Copyright (C) 2019 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.
|
||||||
|
|
15
static/js/novnc/core/util/int.js
Executable file
15
static/js/novnc/core/util/int.js
Executable file
|
@ -0,0 +1,15 @@
|
||||||
|
/*
|
||||||
|
* noVNC: HTML5 VNC client
|
||||||
|
* Copyright (C) 2020 The noVNC Authors
|
||||||
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
|
*
|
||||||
|
* See README.md for usage and integration instructions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export function toUnsigned32bit(toConvert) {
|
||||||
|
return toConvert >>> 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function toSigned32bit(toConvert) {
|
||||||
|
return toConvert | 0;
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2018 The noVNC Authors
|
* Copyright (C) 2019 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,18 +10,18 @@
|
||||||
* Logging/debug routines
|
* Logging/debug routines
|
||||||
*/
|
*/
|
||||||
|
|
||||||
let _log_level = 'warn';
|
let _logLevel = 'warn';
|
||||||
|
|
||||||
let Debug = () => {};
|
let Debug = () => {};
|
||||||
let Info = () => {};
|
let Info = () => {};
|
||||||
let Warn = () => {};
|
let Warn = () => {};
|
||||||
let Error = () => {};
|
let Error = () => {};
|
||||||
|
|
||||||
export function init_logging(level) {
|
export function initLogging(level) {
|
||||||
if (typeof level === 'undefined') {
|
if (typeof level === 'undefined') {
|
||||||
level = _log_level;
|
level = _logLevel;
|
||||||
} else {
|
} else {
|
||||||
_log_level = level;
|
_logLevel = level;
|
||||||
}
|
}
|
||||||
|
|
||||||
Debug = Info = Warn = Error = () => {};
|
Debug = Info = Warn = Error = () => {};
|
||||||
|
@ -46,11 +46,11 @@ export function init_logging(level) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function get_logging() {
|
export function getLogging() {
|
||||||
return _log_level;
|
return _logLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
export { Debug, Info, Warn, Error };
|
export { Debug, Info, Warn, Error };
|
||||||
|
|
||||||
// Initialize logging level
|
// Initialize logging level
|
||||||
init_logging();
|
initLogging();
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2018 The noVNC Authors
|
* Copyright (C) 2020 The noVNC Authors
|
||||||
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -52,3 +52,10 @@ if (typeof Object.assign != 'function') {
|
||||||
window.CustomEvent = CustomEvent;
|
window.CustomEvent = CustomEvent;
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
/* Number.isInteger() (taken from MDN) */
|
||||||
|
Number.isInteger = Number.isInteger || function isInteger(value) {
|
||||||
|
return typeof value === 'number' &&
|
||||||
|
isFinite(value) &&
|
||||||
|
Math.floor(value) === value;
|
||||||
|
};
|
||||||
|
|
|
@ -1,14 +1,28 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2018 The noVNC Authors
|
* Copyright (C) 2019 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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
// Decode from UTF-8
|
||||||
* Decode from UTF-8
|
export function decodeUTF8(utf8string, allowLatin1=false) {
|
||||||
*/
|
try {
|
||||||
export function decodeUTF8(utf8string) {
|
|
||||||
return decodeURIComponent(escape(utf8string));
|
return decodeURIComponent(escape(utf8string));
|
||||||
|
} catch (e) {
|
||||||
|
if (e instanceof URIError) {
|
||||||
|
if (allowLatin1) {
|
||||||
|
// If we allow Latin1 we can ignore any decoding fails
|
||||||
|
// and in these cases return the original string
|
||||||
|
return utf8string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Encode to UTF-8
|
||||||
|
export function encodeUTF8(DOMString) {
|
||||||
|
return unescape(encodeURIComponent(DOMString));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
/*
|
/*
|
||||||
* Websock: high-performance binary WebSockets
|
* Websock: high-performance binary WebSockets
|
||||||
* Copyright (C) 2018 The noVNC Authors
|
* Copyright (C) 2019 The noVNC Authors
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
* 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
|
||||||
|
@ -17,6 +17,8 @@ import * as Log from './util/logging.js';
|
||||||
// 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.
|
||||||
|
// Also copyWithin() for TypedArrays is not supported in IE 11 or
|
||||||
|
// Safari 13 (at the moment we want to support Safari 11).
|
||||||
const ENABLE_COPYWITHIN = false;
|
const ENABLE_COPYWITHIN = false;
|
||||||
const MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
|
const MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
|
||||||
|
|
||||||
|
@ -27,7 +29,6 @@ export default class Websock {
|
||||||
this._rQi = 0; // Receive queue index
|
this._rQi = 0; // Receive queue index
|
||||||
this._rQlen = 0; // Next write position in the receive queue
|
this._rQlen = 0; // Next write position in the receive queue
|
||||||
this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
|
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);
|
// called in init: this._rQ = new Uint8Array(this._rQbufferSize);
|
||||||
this._rQ = null; // Receive queue
|
this._rQ = null; // Receive queue
|
||||||
|
|
||||||
|
@ -143,7 +144,7 @@ export default class Websock {
|
||||||
|
|
||||||
flush() {
|
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._encodeMessage());
|
||||||
this._sQlen = 0;
|
this._sQlen = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -154,7 +155,7 @@ export default class Websock {
|
||||||
this.flush();
|
this.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
send_string(str) {
|
sendString(str) {
|
||||||
this.send(str.split('').map(chr => chr.charCodeAt(0)));
|
this.send(str.split('').map(chr => chr.charCodeAt(0)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,13 +168,13 @@ export default class Websock {
|
||||||
this._eventHandlers[evt] = handler;
|
this._eventHandlers[evt] = handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
_allocate_buffers() {
|
_allocateBuffers() {
|
||||||
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() {
|
init() {
|
||||||
this._allocate_buffers();
|
this._allocateBuffers();
|
||||||
this._rQi = 0;
|
this._rQi = 0;
|
||||||
this._websocket = null;
|
this._websocket = null;
|
||||||
}
|
}
|
||||||
|
@ -184,7 +185,7 @@ export default class Websock {
|
||||||
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._recvMessage.bind(this);
|
||||||
this._websocket.onopen = () => {
|
this._websocket.onopen = () => {
|
||||||
Log.Debug('>> WebSock.onopen');
|
Log.Debug('>> WebSock.onopen');
|
||||||
if (this._websocket.protocol) {
|
if (this._websocket.protocol) {
|
||||||
|
@ -219,42 +220,46 @@ export default class Websock {
|
||||||
}
|
}
|
||||||
|
|
||||||
// private methods
|
// private methods
|
||||||
_encode_message() {
|
_encodeMessage() {
|
||||||
// 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(min_fit) {
|
// We want to move all the unread data to the start of the queue,
|
||||||
const resizeNeeded = min_fit || this.rQlen > this._rQbufferSize / 2;
|
// e.g. compacting.
|
||||||
|
// The function also expands the receive que if needed, and for
|
||||||
|
// performance reasons we combine these two actions to avoid
|
||||||
|
// unneccessary copying.
|
||||||
|
_expandCompactRQ(minFit) {
|
||||||
|
// if we're using less than 1/8th of the buffer even with the incoming bytes, compact in place
|
||||||
|
// instead of resizing
|
||||||
|
const requiredBufferSize = (this._rQlen - this._rQi + minFit) * 8;
|
||||||
|
const resizeNeeded = this._rQbufferSize < requiredBufferSize;
|
||||||
|
|
||||||
if (resizeNeeded) {
|
if (resizeNeeded) {
|
||||||
if (!min_fit) {
|
// Make sure we always *at least* double the buffer size, and have at least space for 8x
|
||||||
// just double the size if we need to do compaction
|
// the current amount of data
|
||||||
this._rQbufferSize *= 2;
|
this._rQbufferSize = Math.max(this._rQbufferSize * 2, requiredBufferSize);
|
||||||
} else {
|
|
||||||
// otherwise, make sure we satisy rQlen - rQi + min_fit < rQbufferSize / 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 < min_fit) {
|
if (this._rQbufferSize - this.rQlen < minFit) {
|
||||||
throw new Error("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit");
|
throw new Error("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (resizeNeeded) {
|
if (resizeNeeded) {
|
||||||
const old_rQbuffer = this._rQ.buffer;
|
const oldRQbuffer = this._rQ.buffer;
|
||||||
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(oldRQbuffer, this._rQi, this._rQlen - this._rQi));
|
||||||
} else {
|
} else {
|
||||||
if (ENABLE_COPYWITHIN) {
|
if (ENABLE_COPYWITHIN) {
|
||||||
this._rQ.copyWithin(0, this._rQi);
|
this._rQ.copyWithin(0, this._rQi, this._rQlen);
|
||||||
} else {
|
} else {
|
||||||
this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi));
|
this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi, this._rQlen - this._rQi));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,26 +267,25 @@ export default class Websock {
|
||||||
this._rQi = 0;
|
this._rQi = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_decode_message(data) {
|
// push arraybuffer values onto the end of the receive que
|
||||||
// push arraybuffer values onto the end
|
_DecodeMessage(data) {
|
||||||
const 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._expandCompactRQ(u8.length);
|
||||||
}
|
}
|
||||||
this._rQ.set(u8, this._rQlen);
|
this._rQ.set(u8, this._rQlen);
|
||||||
this._rQlen += u8.length;
|
this._rQlen += u8.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
_recv_message(e) {
|
_recvMessage(e) {
|
||||||
this._decode_message(e.data);
|
this._DecodeMessage(e.data);
|
||||||
if (this.rQlen > 0) {
|
if (this.rQlen > 0) {
|
||||||
this._eventHandlers.message();
|
this._eventHandlers.message();
|
||||||
// Compact the receive queue
|
|
||||||
if (this._rQlen == this._rQi) {
|
if (this._rQlen == this._rQi) {
|
||||||
|
// All data has now been processed, this means we
|
||||||
|
// can reset the receive queue.
|
||||||
this._rQlen = 0;
|
this._rQlen = 0;
|
||||||
this._rQi = 0;
|
this._rQi = 0;
|
||||||
} else if (this._rQlen > this._rQmax) {
|
|
||||||
this._expand_compact_rQ();
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log.Debug("Ignoring empty message");
|
Log.Debug("Ignoring empty message");
|
||||||
|
|
88
static/js/novnc/package.json
Normal file
88
static/js/novnc/package.json
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
{
|
||||||
|
"name": "@novnc/novnc",
|
||||||
|
"version": "1.2.0",
|
||||||
|
"description": "An HTML5 VNC client",
|
||||||
|
"browser": "lib/rfb",
|
||||||
|
"directories": {
|
||||||
|
"lib": "lib",
|
||||||
|
"doc": "docs",
|
||||||
|
"test": "tests"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"lib",
|
||||||
|
"AUTHORS",
|
||||||
|
"VERSION",
|
||||||
|
"docs/API.md",
|
||||||
|
"docs/LIBRARY.md",
|
||||||
|
"docs/LICENSE*",
|
||||||
|
"core",
|
||||||
|
"vendor/pako"
|
||||||
|
],
|
||||||
|
"scripts": {
|
||||||
|
"lint": "eslint app core po/po2js po/xgettext-html tests utils",
|
||||||
|
"test": "karma start karma.conf.js",
|
||||||
|
"prepublish": "node ./utils/use_require.js --as commonjs --clean"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/novnc/noVNC.git"
|
||||||
|
},
|
||||||
|
"author": "Joel Martin <github@martintribe.org> (https://github.com/kanaka)",
|
||||||
|
"contributors": [
|
||||||
|
"Solly Ross <sross@redhat.com> (https://github.com/directxman12)",
|
||||||
|
"Peter Åstrand <astrand@cendio.se> (https://github.com/astrand)",
|
||||||
|
"Samuel Mannehed <samuel@cendio.se> (https://github.com/samhed)",
|
||||||
|
"Pierre Ossman <ossman@cendio.se> (https://github.com/CendioOssman)"
|
||||||
|
],
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/novnc/noVNC/issues"
|
||||||
|
},
|
||||||
|
"homepage": "https://github.com/novnc/noVNC",
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "*",
|
||||||
|
"@babel/plugin-syntax-dynamic-import": "*",
|
||||||
|
"@babel/plugin-transform-modules-amd": "*",
|
||||||
|
"@babel/plugin-transform-modules-commonjs": "*",
|
||||||
|
"@babel/plugin-transform-modules-systemjs": "*",
|
||||||
|
"@babel/plugin-transform-modules-umd": "*",
|
||||||
|
"@babel/preset-env": "*",
|
||||||
|
"@babel/cli": "*",
|
||||||
|
"babel-plugin-import-redirect": "*",
|
||||||
|
"browserify": "*",
|
||||||
|
"babelify": "*",
|
||||||
|
"core-js": "*",
|
||||||
|
"chai": "*",
|
||||||
|
"commander": "*",
|
||||||
|
"es-module-loader": "*",
|
||||||
|
"eslint": "*",
|
||||||
|
"fs-extra": "*",
|
||||||
|
"jsdom": "*",
|
||||||
|
"karma": "*",
|
||||||
|
"karma-mocha": "*",
|
||||||
|
"karma-chrome-launcher": "*",
|
||||||
|
"@chiragrupani/karma-chromium-edge-launcher": "*",
|
||||||
|
"karma-firefox-launcher": "*",
|
||||||
|
"karma-ie-launcher": "*",
|
||||||
|
"karma-mocha-reporter": "*",
|
||||||
|
"karma-safari-launcher": "*",
|
||||||
|
"karma-script-launcher": "*",
|
||||||
|
"karma-sinon-chai": "*",
|
||||||
|
"mocha": "*",
|
||||||
|
"node-getopt": "*",
|
||||||
|
"po2json": "*",
|
||||||
|
"requirejs": "*",
|
||||||
|
"rollup": "*",
|
||||||
|
"rollup-plugin-node-resolve": "*",
|
||||||
|
"sinon": "*",
|
||||||
|
"sinon-chai": "*"
|
||||||
|
},
|
||||||
|
"dependencies": {},
|
||||||
|
"keywords": [
|
||||||
|
"vnc",
|
||||||
|
"rfb",
|
||||||
|
"novnc",
|
||||||
|
"websockify"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ It's based heavily on
|
||||||
https://github.com/ModuleLoader/browser-es-module-loader, but uses
|
https://github.com/ModuleLoader/browser-es-module-loader, but uses
|
||||||
WebWorkers to compile the modules in the background.
|
WebWorkers to compile the modules in the background.
|
||||||
|
|
||||||
To generate, run `rollup -c` in this directory, and then run `browserify
|
To generate, run `npx rollup -c` in this directory, and then run
|
||||||
src/babel-worker.js > dist/babel-worker.js`.
|
`./genworker.js`.
|
||||||
|
|
||||||
LICENSE
|
LICENSE
|
||||||
-------
|
-------
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,7 +1,7 @@
|
||||||
(function (global, factory) {
|
(function (global, factory) {
|
||||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||||
typeof define === 'function' && define.amd ? define(factory) :
|
typeof define === 'function' && define.amd ? define(factory) :
|
||||||
(global.BrowserESModuleLoader = factory());
|
(global = global || self, global.BrowserESModuleLoader = factory());
|
||||||
}(this, (function () { 'use strict';
|
}(this, (function () { 'use strict';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -12,6 +12,7 @@ var isNode = typeof process !== 'undefined' && process.versions && process.versi
|
||||||
var isWindows = typeof process !== 'undefined' && typeof process.platform === 'string' && process.platform.match(/^win/);
|
var isWindows = typeof process !== 'undefined' && typeof process.platform === 'string' && process.platform.match(/^win/);
|
||||||
|
|
||||||
var envGlobal = typeof self !== 'undefined' ? self : global;
|
var envGlobal = typeof self !== 'undefined' ? self : global;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Simple Symbol() shim
|
* Simple Symbol() shim
|
||||||
*/
|
*/
|
||||||
|
@ -22,10 +23,6 @@ function createSymbol (name) {
|
||||||
|
|
||||||
var toStringTag = hasSymbol && Symbol.toStringTag;
|
var toStringTag = hasSymbol && Symbol.toStringTag;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Environment baseURI
|
* Environment baseURI
|
||||||
*/
|
*/
|
||||||
|
@ -95,7 +92,7 @@ function LoaderError__Check_error_message_for_loader_stack (childErr, newMessage
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
var resolvedPromise$1 = Promise.resolve();
|
var resolvedPromise = Promise.resolve();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Simple Array values shim
|
* Simple Array values shim
|
||||||
|
@ -155,7 +152,7 @@ Loader.prototype.import = function (key, parent) {
|
||||||
throw new TypeError('Loader import method must be passed a module key string');
|
throw new TypeError('Loader import method must be passed a module key string');
|
||||||
// custom resolveInstantiate combined hook for better perf
|
// custom resolveInstantiate combined hook for better perf
|
||||||
var loader = this;
|
var loader = this;
|
||||||
return resolvedPromise$1
|
return resolvedPromise
|
||||||
.then(function () {
|
.then(function () {
|
||||||
return loader[RESOLVE_INSTANTIATE](key, parent);
|
return loader[RESOLVE_INSTANTIATE](key, parent);
|
||||||
})
|
})
|
||||||
|
@ -197,7 +194,7 @@ function ensureResolution (resolvedKey) {
|
||||||
|
|
||||||
Loader.prototype.resolve = function (key, parent) {
|
Loader.prototype.resolve = function (key, parent) {
|
||||||
var loader = this;
|
var loader = this;
|
||||||
return resolvedPromise$1
|
return resolvedPromise
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return loader[RESOLVE](key, parent);
|
return loader[RESOLVE](key, parent);
|
||||||
})
|
})
|
||||||
|
@ -318,8 +315,7 @@ function ModuleNamespace (baseObject/*, evaluate*/) {
|
||||||
else { */
|
else { */
|
||||||
Object.keys(baseObject).forEach(extendNamespace, this);
|
Object.keys(baseObject).forEach(extendNamespace, this);
|
||||||
//}
|
//}
|
||||||
}
|
}// 8.4.2
|
||||||
// 8.4.2
|
|
||||||
ModuleNamespace.prototype = Object.create(null);
|
ModuleNamespace.prototype = Object.create(null);
|
||||||
|
|
||||||
if (toStringTag)
|
if (toStringTag)
|
||||||
|
@ -491,7 +487,8 @@ function resolveIfNotPlain (relUrl, parentUrl) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var resolvedPromise = Promise.resolve();
|
var resolvedPromise$1 = Promise.resolve();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Register Loader
|
* Register Loader
|
||||||
*
|
*
|
||||||
|
@ -505,7 +502,7 @@ var resolvedPromise = Promise.resolve();
|
||||||
|
|
||||||
var REGISTER_INTERNAL = createSymbol('register-internal');
|
var REGISTER_INTERNAL = createSymbol('register-internal');
|
||||||
|
|
||||||
function RegisterLoader$1 () {
|
function RegisterLoader () {
|
||||||
Loader.call(this);
|
Loader.call(this);
|
||||||
|
|
||||||
var registryDelete = this.registry.delete;
|
var registryDelete = this.registry.delete;
|
||||||
|
@ -534,17 +531,17 @@ function RegisterLoader$1 () {
|
||||||
this.trace = false;
|
this.trace = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterLoader$1.prototype = Object.create(Loader.prototype);
|
RegisterLoader.prototype = Object.create(Loader.prototype);
|
||||||
RegisterLoader$1.prototype.constructor = RegisterLoader$1;
|
RegisterLoader.prototype.constructor = RegisterLoader;
|
||||||
|
|
||||||
var INSTANTIATE = RegisterLoader$1.instantiate = createSymbol('instantiate');
|
var INSTANTIATE = RegisterLoader.instantiate = createSymbol('instantiate');
|
||||||
|
|
||||||
// default normalize is the WhatWG style normalizer
|
// default normalize is the WhatWG style normalizer
|
||||||
RegisterLoader$1.prototype[RegisterLoader$1.resolve = Loader.resolve] = function (key, parentKey) {
|
RegisterLoader.prototype[RegisterLoader.resolve = Loader.resolve] = function (key, parentKey) {
|
||||||
return resolveIfNotPlain(key, parentKey || baseURI);
|
return resolveIfNotPlain(key, parentKey || baseURI);
|
||||||
};
|
};
|
||||||
|
|
||||||
RegisterLoader$1.prototype[INSTANTIATE] = function (key, processAnonRegister) {};
|
RegisterLoader.prototype[INSTANTIATE] = function (key, processAnonRegister) {};
|
||||||
|
|
||||||
// once evaluated, the linkRecord is set to undefined leaving just the other load record properties
|
// once evaluated, the linkRecord is set to undefined leaving just the other load record properties
|
||||||
// this allows tracking new binding listeners for es modules through importerSetters
|
// this allows tracking new binding listeners for es modules through importerSetters
|
||||||
|
@ -599,7 +596,7 @@ function createLoadRecord (state, key, registration) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterLoader$1.prototype[Loader.resolveInstantiate] = function (key, parentKey) {
|
RegisterLoader.prototype[Loader.resolveInstantiate] = function (key, parentKey) {
|
||||||
var loader = this;
|
var loader = this;
|
||||||
var state = this[REGISTER_INTERNAL];
|
var state = this[REGISTER_INTERNAL];
|
||||||
var registry = this.registry[REGISTRY];
|
var registry = this.registry[REGISTRY];
|
||||||
|
@ -686,7 +683,7 @@ function createProcessAnonRegister (loader, load, state) {
|
||||||
function instantiate (loader, load, link, registry, state) {
|
function instantiate (loader, load, link, registry, state) {
|
||||||
return link.instantiatePromise || (link.instantiatePromise =
|
return link.instantiatePromise || (link.instantiatePromise =
|
||||||
// if there is already an existing registration, skip running instantiate
|
// if there is already an existing registration, skip running instantiate
|
||||||
(load.registration ? resolvedPromise : resolvedPromise.then(function () {
|
(load.registration ? resolvedPromise$1 : resolvedPromise$1.then(function () {
|
||||||
state.lastRegister = undefined;
|
state.lastRegister = undefined;
|
||||||
return loader[INSTANTIATE](load.key, loader[INSTANTIATE].length > 1 && createProcessAnonRegister(loader, load, state));
|
return loader[INSTANTIATE](load.key, loader[INSTANTIATE].length > 1 && createProcessAnonRegister(loader, load, state));
|
||||||
}))
|
}))
|
||||||
|
@ -907,9 +904,9 @@ function deepInstantiateDeps (loader, load, link, registry, state) {
|
||||||
var seen = [];
|
var seen = [];
|
||||||
function addDeps (load, link) {
|
function addDeps (load, link) {
|
||||||
if (!link)
|
if (!link)
|
||||||
return resolvedPromise;
|
return resolvedPromise$1;
|
||||||
if (seen.indexOf(load) !== -1)
|
if (seen.indexOf(load) !== -1)
|
||||||
return resolvedPromise;
|
return resolvedPromise$1;
|
||||||
seen.push(load);
|
seen.push(load);
|
||||||
|
|
||||||
return instantiateDeps(loader, load, link, registry, state)
|
return instantiateDeps(loader, load, link, registry, state)
|
||||||
|
@ -926,14 +923,13 @@ function deepInstantiateDeps (loader, load, link, registry, state) {
|
||||||
return Promise.all(depPromises);
|
return Promise.all(depPromises);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return addDeps(load, link);
|
return addDeps(load, link);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* System.register
|
* System.register
|
||||||
*/
|
*/
|
||||||
RegisterLoader$1.prototype.register = function (key, deps, declare) {
|
RegisterLoader.prototype.register = function (key, deps, declare) {
|
||||||
var state = this[REGISTER_INTERNAL];
|
var state = this[REGISTER_INTERNAL];
|
||||||
|
|
||||||
// anonymous modules get stored as lastAnon
|
// anonymous modules get stored as lastAnon
|
||||||
|
@ -951,7 +947,7 @@ RegisterLoader$1.prototype.register = function (key, deps, declare) {
|
||||||
/*
|
/*
|
||||||
* System.registerDyanmic
|
* System.registerDyanmic
|
||||||
*/
|
*/
|
||||||
RegisterLoader$1.prototype.registerDynamic = function (key, deps, executingRequire, execute) {
|
RegisterLoader.prototype.registerDynamic = function (key, deps, executingRequire, execute) {
|
||||||
var state = this[REGISTER_INTERNAL];
|
var state = this[REGISTER_INTERNAL];
|
||||||
|
|
||||||
// anonymous modules get stored as lastAnon
|
// anonymous modules get stored as lastAnon
|
||||||
|
@ -1279,7 +1275,7 @@ function BrowserESModuleLoader(baseKey) {
|
||||||
if (baseKey)
|
if (baseKey)
|
||||||
this.baseKey = resolveIfNotPlain(baseKey, baseURI) || resolveIfNotPlain('./' + baseKey, baseURI);
|
this.baseKey = resolveIfNotPlain(baseKey, baseURI) || resolveIfNotPlain('./' + baseKey, baseURI);
|
||||||
|
|
||||||
RegisterLoader$1.call(this);
|
RegisterLoader.call(this);
|
||||||
|
|
||||||
var loader = this;
|
var loader = this;
|
||||||
|
|
||||||
|
@ -1293,11 +1289,11 @@ function BrowserESModuleLoader(baseKey) {
|
||||||
prevRegister.apply(this, arguments);
|
prevRegister.apply(this, arguments);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
BrowserESModuleLoader.prototype = Object.create(RegisterLoader$1.prototype);
|
BrowserESModuleLoader.prototype = Object.create(RegisterLoader.prototype);
|
||||||
|
|
||||||
// normalize is never given a relative name like "./x", that part is already handled
|
// normalize is never given a relative name like "./x", that part is already handled
|
||||||
BrowserESModuleLoader.prototype[RegisterLoader$1.resolve] = function(key, parent) {
|
BrowserESModuleLoader.prototype[RegisterLoader.resolve] = function(key, parent) {
|
||||||
var resolved = RegisterLoader$1.prototype[RegisterLoader$1.resolve].call(this, key, parent || this.baseKey) || key;
|
var resolved = RegisterLoader.prototype[RegisterLoader.resolve].call(this, key, parent || this.baseKey) || key;
|
||||||
if (!resolved)
|
if (!resolved)
|
||||||
throw new RangeError('ES module loader does not resolve plain module names, resolving "' + key + '" to ' + parent);
|
throw new RangeError('ES module loader does not resolve plain module names, resolving "' + key + '" to ' + parent);
|
||||||
|
|
||||||
|
@ -1431,8 +1427,7 @@ babelWorker.onmessage = function (evt) {
|
||||||
|
|
||||||
// instantiate just needs to run System.register
|
// instantiate just needs to run System.register
|
||||||
// so we fetch the source, convert into the Babel System module format, then evaluate it
|
// so we fetch the source, convert into the Babel System module format, then evaluate it
|
||||||
BrowserESModuleLoader.prototype[RegisterLoader$1.instantiate] = function(key, processAnonRegister) {
|
BrowserESModuleLoader.prototype[RegisterLoader.instantiate] = function(key, processAnonRegister) {
|
||||||
var loader = this;
|
|
||||||
|
|
||||||
// load as ES with Babel converting into System.register
|
// load as ES with Babel converting into System.register
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
|
|
File diff suppressed because one or more lines are too long
13
static/js/novnc/vendor/browser-es-module-loader/genworker.js
vendored
Executable file
13
static/js/novnc/vendor/browser-es-module-loader/genworker.js
vendored
Executable file
|
@ -0,0 +1,13 @@
|
||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
var fs = require("fs");
|
||||||
|
var browserify = require("browserify");
|
||||||
|
|
||||||
|
browserify("src/babel-worker.js")
|
||||||
|
.transform("babelify", {
|
||||||
|
presets: [ [ "@babel/preset-env", { targets: "ie >= 11" } ] ],
|
||||||
|
global: true,
|
||||||
|
ignore: [ "../../node_modules/core-js" ]
|
||||||
|
})
|
||||||
|
.bundle()
|
||||||
|
.pipe(fs.createWriteStream("dist/babel-worker.js"));
|
|
@ -1,16 +1,15 @@
|
||||||
import nodeResolve from 'rollup-plugin-node-resolve';
|
import nodeResolve from 'rollup-plugin-node-resolve';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
entry: 'src/browser-es-module-loader.js',
|
input: 'src/browser-es-module-loader.js',
|
||||||
dest: 'dist/browser-es-module-loader.js',
|
output: {
|
||||||
|
file: 'dist/browser-es-module-loader.js',
|
||||||
format: 'umd',
|
format: 'umd',
|
||||||
moduleName: 'BrowserESModuleLoader',
|
name: 'BrowserESModuleLoader',
|
||||||
sourceMap: true,
|
sourcemap: true,
|
||||||
|
},
|
||||||
|
|
||||||
plugins: [
|
plugins: [
|
||||||
nodeResolve(),
|
nodeResolve(),
|
||||||
],
|
],
|
||||||
|
|
||||||
// skip rollup warnings (specifically the eval warning)
|
|
||||||
onwarn: function() {}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,12 +1,10 @@
|
||||||
/*import { transform as babelTransform } from 'babel-core';
|
// Polyfills needed for Babel to function
|
||||||
import babelTransformDynamicImport from 'babel-plugin-syntax-dynamic-import';
|
require("core-js");
|
||||||
import babelTransformES2015ModulesSystemJS from 'babel-plugin-transform-es2015-modules-systemjs';*/
|
|
||||||
|
|
||||||
// sadly, due to how rollup works, we can't use es6 imports here
|
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 babelTransformModulesSystemJS = require('@babel/plugin-transform-modules-systemjs');
|
||||||
var babelTransformES2015ModulesSystemJS = require('babel-plugin-transform-es2015-modules-systemjs');
|
var babelPresetEnv = require('@babel/preset-env');
|
||||||
var babelPresetES2015 = require('babel-preset-es2015');
|
|
||||||
|
|
||||||
self.onmessage = function (evt) {
|
self.onmessage = function (evt) {
|
||||||
// transform source with Babel
|
// transform source with Babel
|
||||||
|
@ -17,8 +15,8 @@ self.onmessage = function (evt) {
|
||||||
moduleIds: false,
|
moduleIds: false,
|
||||||
sourceMaps: 'inline',
|
sourceMaps: 'inline',
|
||||||
babelrc: false,
|
babelrc: false,
|
||||||
plugins: [babelTransformDynamicImport, babelTransformES2015ModulesSystemJS],
|
plugins: [babelTransformDynamicImport, babelTransformModulesSystemJS],
|
||||||
presets: [babelPresetES2015],
|
presets: [ [ babelPresetEnv, { targets: 'ie >= 11' } ] ],
|
||||||
});
|
});
|
||||||
|
|
||||||
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});
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import RegisterLoader from 'es-module-loader/core/register-loader.js';
|
import RegisterLoader from 'es-module-loader/core/register-loader.js';
|
||||||
import { InternalModuleNamespace as ModuleNamespace } from 'es-module-loader/core/loader-polyfill.js';
|
|
||||||
|
|
||||||
import { baseURI, global, isBrowser } from 'es-module-loader/core/common.js';
|
import { baseURI, global, isBrowser } from 'es-module-loader/core/common.js';
|
||||||
import { resolveIfNotPlain } from 'es-module-loader/core/resolve.js';
|
import { resolveIfNotPlain } from 'es-module-loader/core/resolve.js';
|
||||||
|
@ -35,7 +34,7 @@ if (typeof document != 'undefined' && document.getElementsByTagName) {
|
||||||
|
|
||||||
// throw so it still shows up in the console
|
// throw so it still shows up in the console
|
||||||
throw err;
|
throw err;
|
||||||
};
|
}
|
||||||
|
|
||||||
var ready = function() {
|
var ready = function() {
|
||||||
document.removeEventListener('DOMContentLoaded', ready, false );
|
document.removeEventListener('DOMContentLoaded', ready, false );
|
||||||
|
@ -63,7 +62,7 @@ if (typeof document != 'undefined' && document.getElementsByTagName) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// simple DOM ready
|
// simple DOM ready
|
||||||
if (document.readyState !== 'loading')
|
if (document.readyState !== 'loading')
|
||||||
|
@ -105,10 +104,10 @@ function xhrFetch(url, resolve, reject) {
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
var load = function(source) {
|
var load = function(source) {
|
||||||
resolve(xhr.responseText);
|
resolve(xhr.responseText);
|
||||||
};
|
}
|
||||||
var error = function() {
|
var error = function() {
|
||||||
reject(new Error('XHR error' + (xhr.status ? ' (' + xhr.status + (xhr.statusText ? ' ' + xhr.statusText : '') + ')' : '') + ' loading ' + url));
|
reject(new Error('XHR error' + (xhr.status ? ' (' + xhr.status + (xhr.statusText ? ' ' + xhr.statusText : '') + ')' : '') + ' loading ' + url));
|
||||||
};
|
}
|
||||||
|
|
||||||
xhr.onreadystatechange = function () {
|
xhr.onreadystatechange = function () {
|
||||||
if (xhr.readyState === 4) {
|
if (xhr.readyState === 4) {
|
||||||
|
@ -235,7 +234,7 @@ BrowserESModuleLoader.prototype[RegisterLoader.instantiate] = function(key, proc
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function(resolve, reject) {
|
||||||
// anonymous module
|
// anonymous module
|
||||||
if (anonSources[key]) {
|
if (anonSources[key]) {
|
||||||
resolve(anonSources[key]);
|
resolve(anonSources[key])
|
||||||
anonSources[key] = undefined;
|
anonSources[key] = undefined;
|
||||||
}
|
}
|
||||||
// otherwise we fetch
|
// otherwise we fetch
|
||||||
|
|
|
@ -4,7 +4,7 @@ export function shrinkBuf (buf, size) {
|
||||||
if (buf.subarray) { return buf.subarray(0, size); }
|
if (buf.subarray) { return buf.subarray(0, size); }
|
||||||
buf.length = size;
|
buf.length = size;
|
||||||
return buf;
|
return buf;
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
export function arraySet (dest, src, src_offs, len, dest_offs) {
|
export function arraySet (dest, src, src_offs, len, dest_offs) {
|
||||||
|
|
60
static/js/novnc/vendor/pako/lib/zlib/deflate.js
vendored
60
static/js/novnc/vendor/pako/lib/zlib/deflate.js
vendored
|
@ -9,51 +9,51 @@ import msg from "./messages.js";
|
||||||
|
|
||||||
|
|
||||||
/* Allowed flush values; see deflate() and inflate() below for details */
|
/* Allowed flush values; see deflate() and inflate() below for details */
|
||||||
var Z_NO_FLUSH = 0;
|
export const Z_NO_FLUSH = 0;
|
||||||
var Z_PARTIAL_FLUSH = 1;
|
export const Z_PARTIAL_FLUSH = 1;
|
||||||
//var Z_SYNC_FLUSH = 2;
|
//export const Z_SYNC_FLUSH = 2;
|
||||||
var Z_FULL_FLUSH = 3;
|
export const Z_FULL_FLUSH = 3;
|
||||||
var Z_FINISH = 4;
|
export const Z_FINISH = 4;
|
||||||
var Z_BLOCK = 5;
|
export const Z_BLOCK = 5;
|
||||||
//var Z_TREES = 6;
|
//export const Z_TREES = 6;
|
||||||
|
|
||||||
|
|
||||||
/* Return codes for the compression/decompression functions. Negative values
|
/* Return codes for the compression/decompression functions. Negative values
|
||||||
* are errors, positive values are used for special but normal events.
|
* are errors, positive values are used for special but normal events.
|
||||||
*/
|
*/
|
||||||
var Z_OK = 0;
|
export const Z_OK = 0;
|
||||||
var Z_STREAM_END = 1;
|
export const Z_STREAM_END = 1;
|
||||||
//var Z_NEED_DICT = 2;
|
//export const Z_NEED_DICT = 2;
|
||||||
//var Z_ERRNO = -1;
|
//export const Z_ERRNO = -1;
|
||||||
var Z_STREAM_ERROR = -2;
|
export const Z_STREAM_ERROR = -2;
|
||||||
var Z_DATA_ERROR = -3;
|
export const Z_DATA_ERROR = -3;
|
||||||
//var Z_MEM_ERROR = -4;
|
//export const Z_MEM_ERROR = -4;
|
||||||
var Z_BUF_ERROR = -5;
|
export const Z_BUF_ERROR = -5;
|
||||||
//var Z_VERSION_ERROR = -6;
|
//export const Z_VERSION_ERROR = -6;
|
||||||
|
|
||||||
|
|
||||||
/* compression levels */
|
/* compression levels */
|
||||||
//var Z_NO_COMPRESSION = 0;
|
//export const Z_NO_COMPRESSION = 0;
|
||||||
//var Z_BEST_SPEED = 1;
|
//export const Z_BEST_SPEED = 1;
|
||||||
//var Z_BEST_COMPRESSION = 9;
|
//export const Z_BEST_COMPRESSION = 9;
|
||||||
var Z_DEFAULT_COMPRESSION = -1;
|
export const Z_DEFAULT_COMPRESSION = -1;
|
||||||
|
|
||||||
|
|
||||||
var Z_FILTERED = 1;
|
export const Z_FILTERED = 1;
|
||||||
var Z_HUFFMAN_ONLY = 2;
|
export const Z_HUFFMAN_ONLY = 2;
|
||||||
var Z_RLE = 3;
|
export const Z_RLE = 3;
|
||||||
var Z_FIXED = 4;
|
export const Z_FIXED = 4;
|
||||||
var Z_DEFAULT_STRATEGY = 0;
|
export const Z_DEFAULT_STRATEGY = 0;
|
||||||
|
|
||||||
/* Possible values of the data_type field (though see inflate()) */
|
/* Possible values of the data_type field (though see inflate()) */
|
||||||
//var Z_BINARY = 0;
|
//export const Z_BINARY = 0;
|
||||||
//var Z_TEXT = 1;
|
//export const Z_TEXT = 1;
|
||||||
//var Z_ASCII = 1; // = Z_TEXT
|
//export const Z_ASCII = 1; // = Z_TEXT
|
||||||
var Z_UNKNOWN = 2;
|
export const Z_UNKNOWN = 2;
|
||||||
|
|
||||||
|
|
||||||
/* The deflate compression method */
|
/* The deflate compression method */
|
||||||
var Z_DEFLATED = 8;
|
export const Z_DEFLATED = 8;
|
||||||
|
|
||||||
/*============================================================================*/
|
/*============================================================================*/
|
||||||
|
|
||||||
|
|
|
@ -277,7 +277,7 @@ export default function inflate_fast(strm, start) {
|
||||||
}
|
}
|
||||||
else if ((op & 64) === 0) { /* 2nd level distance code */
|
else if ((op & 64) === 0) { /* 2nd level distance code */
|
||||||
here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
|
here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
|
||||||
continue;
|
continue dodist;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
strm.msg = 'invalid distance code';
|
strm.msg = 'invalid distance code';
|
||||||
|
@ -290,7 +290,7 @@ export default function inflate_fast(strm, start) {
|
||||||
}
|
}
|
||||||
else if ((op & 64) === 0) { /* 2nd level length code */
|
else if ((op & 64) === 0) { /* 2nd level length code */
|
||||||
here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
|
here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
|
||||||
continue;
|
continue dolen;
|
||||||
}
|
}
|
||||||
else if (op & 32) { /* end-of-block */
|
else if (op & 32) { /* end-of-block */
|
||||||
//Tracevv((stderr, "inflate: end of block\n"));
|
//Tracevv((stderr, "inflate: end of block\n"));
|
||||||
|
@ -320,5 +320,5 @@ export default function inflate_fast(strm, start) {
|
||||||
strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end));
|
strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end));
|
||||||
state.hold = hold;
|
state.hold = hold;
|
||||||
state.bits = bits;
|
state.bits = bits;
|
||||||
|
return;
|
||||||
};
|
};
|
||||||
|
|
34
static/js/novnc/vendor/pako/lib/zlib/inflate.js
vendored
34
static/js/novnc/vendor/pako/lib/zlib/inflate.js
vendored
|
@ -13,30 +13,30 @@ var DISTS = 2;
|
||||||
|
|
||||||
|
|
||||||
/* Allowed flush values; see deflate() and inflate() below for details */
|
/* Allowed flush values; see deflate() and inflate() below for details */
|
||||||
//var Z_NO_FLUSH = 0;
|
//export const Z_NO_FLUSH = 0;
|
||||||
//var Z_PARTIAL_FLUSH = 1;
|
//export const Z_PARTIAL_FLUSH = 1;
|
||||||
//var Z_SYNC_FLUSH = 2;
|
//export const Z_SYNC_FLUSH = 2;
|
||||||
//var Z_FULL_FLUSH = 3;
|
//export const Z_FULL_FLUSH = 3;
|
||||||
var Z_FINISH = 4;
|
export const Z_FINISH = 4;
|
||||||
var Z_BLOCK = 5;
|
export const Z_BLOCK = 5;
|
||||||
var Z_TREES = 6;
|
export const Z_TREES = 6;
|
||||||
|
|
||||||
|
|
||||||
/* Return codes for the compression/decompression functions. Negative values
|
/* Return codes for the compression/decompression functions. Negative values
|
||||||
* are errors, positive values are used for special but normal events.
|
* are errors, positive values are used for special but normal events.
|
||||||
*/
|
*/
|
||||||
var Z_OK = 0;
|
export const Z_OK = 0;
|
||||||
var Z_STREAM_END = 1;
|
export const Z_STREAM_END = 1;
|
||||||
var Z_NEED_DICT = 2;
|
export const Z_NEED_DICT = 2;
|
||||||
//var Z_ERRNO = -1;
|
//export const Z_ERRNO = -1;
|
||||||
var Z_STREAM_ERROR = -2;
|
export const Z_STREAM_ERROR = -2;
|
||||||
var Z_DATA_ERROR = -3;
|
export const Z_DATA_ERROR = -3;
|
||||||
var Z_MEM_ERROR = -4;
|
export const Z_MEM_ERROR = -4;
|
||||||
var Z_BUF_ERROR = -5;
|
export const Z_BUF_ERROR = -5;
|
||||||
//var Z_VERSION_ERROR = -6;
|
//export const Z_VERSION_ERROR = -6;
|
||||||
|
|
||||||
/* The deflate compression method */
|
/* The deflate compression method */
|
||||||
var Z_DEFLATED = 8;
|
export const Z_DEFLATED = 8;
|
||||||
|
|
||||||
|
|
||||||
/* STATES ====================================================================*/
|
/* STATES ====================================================================*/
|
||||||
|
|
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
Loading…
Reference in a new issue