mirror of
https://github.com/retspen/webvirtcloud
synced 2024-12-24 15:15:22 +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" />
|
||||
|
||||
<!-- 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="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" %}">
|
||||
|
@ -54,12 +58,8 @@
|
|||
<!-- Stylesheets -->
|
||||
<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 -->
|
||||
<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 -->
|
||||
<!-- promise polyfills promises for IE11 -->
|
||||
|
@ -91,7 +91,7 @@
|
|||
|
||||
<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 -->
|
||||
<input type="image" alt="viewport drag" src="{% static "js/novnc/app/images/drag.svg" %}"
|
||||
|
@ -99,20 +99,11 @@
|
|||
|
||||
<!--noVNC Touch Device only 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" %}"
|
||||
id="noVNC_keyboard_button" class="noVNC_button" value="Keyboard" title="Show Keyboard" />
|
||||
id="noVNC_keyboard_button" class="noVNC_button" title="Show Keyboard">
|
||||
</div>
|
||||
|
||||
<!-- Extra manual keys -->
|
||||
<div id="noVNC_extra_keys">
|
||||
<input type="image" alt="Extra keys" src="{% static "js/novnc/app/images/toggleextrakeys.svg" %}"
|
||||
id="noVNC_toggle_extra_keys_button" class="noVNC_button" title="Show Extra Keys" />
|
||||
<div class="noVNC_vcenter">
|
||||
|
@ -131,7 +122,6 @@
|
|||
id="noVNC_send_ctrl_alt_del_button" class="noVNC_button" title="Send Ctrl-Alt-Del" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Shutdown/Reboot -->
|
||||
<input type="image" alt="Shutdown/Reboot" src="{% static "js/novnc/app/images/power.svg" %}"
|
||||
|
@ -199,59 +189,53 @@
|
|||
</li>
|
||||
<li>
|
||||
<div class="noVNC_expander">Advanced</div>
|
||||
<div>
|
||||
<ul>
|
||||
<div><ul>
|
||||
<li>
|
||||
<label for="noVNC_setting_quality">Quality:</label>
|
||||
<input id="noVNC_setting_quality" type="range" min="0" max="9" value="6">
|
||||
</li>
|
||||
<li>
|
||||
<label for="noVNC_setting_compression">Compression level:</label>
|
||||
<input id="noVNC_setting_compression" type="range" min="0" max="9" value="2">
|
||||
</li>
|
||||
<li><hr></li>
|
||||
<li>
|
||||
<label for="noVNC_setting_repeaterID">Repeater ID:</label>
|
||||
<input id="noVNC_setting_repeaterID" type="input" value="" />
|
||||
<input id="noVNC_setting_repeaterID" type="text" value="">
|
||||
</li>
|
||||
<li>
|
||||
<div class="noVNC_expander">WebSocket</div>
|
||||
<div>
|
||||
<ul>
|
||||
<div><ul>
|
||||
<li>
|
||||
<label><input id="noVNC_setting_encrypt"
|
||||
type="checkbox" />Encrypt</label>
|
||||
<label><input id="noVNC_setting_encrypt" type="checkbox"> Encrypt</label>
|
||||
</li>
|
||||
<li>
|
||||
<label for="noVNC_setting_host">Host:</label>
|
||||
<input id="noVNC_setting_host" value="{{ ws_host }}" />
|
||||
<input id="noVNC_setting_host">
|
||||
</li>
|
||||
<li>
|
||||
<label for="noVNC_setting_port">Port:</label>
|
||||
<input id="noVNC_setting_port" value="{{ ws_port }}"
|
||||
type="number" />
|
||||
<input id="noVNC_setting_port" type="number">
|
||||
</li>
|
||||
<li>
|
||||
<label for="noVNC_setting_path">Path:</label>
|
||||
<!-- <input id="noVNC_setting_path" type="input" value="websockify"/> -->
|
||||
<input id="noVNC_setting_path" type="input" value="{{ ws_path }}" />
|
||||
<input id="noVNC_setting_path" type="text" value="websockify">
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</ul></div>
|
||||
</li>
|
||||
<li><hr></li>
|
||||
<li>
|
||||
<hr>
|
||||
</li>
|
||||
<li>
|
||||
<label><input id="noVNC_setting_reconnect" type="checkbox" />Automatic
|
||||
Reconnect</label>
|
||||
<input id="noVNC_setting_autoconnect" type="checkbox" value="true" hidden />
|
||||
<label><input id="noVNC_setting_reconnect" type="checkbox"> Automatic Reconnect</label>
|
||||
</li>
|
||||
<li>
|
||||
<label for="noVNC_setting_reconnect_delay">Reconnect Delay (ms):</label>
|
||||
<input id="noVNC_setting_reconnect_delay" type="number" />
|
||||
<input id="noVNC_setting_reconnect_delay" type="number">
|
||||
</li>
|
||||
<li><hr></li>
|
||||
<li>
|
||||
<hr>
|
||||
</li>
|
||||
<li>
|
||||
<label><input id="noVNC_setting_show_dot" type="checkbox">Show Dot when No
|
||||
Cursor</label>
|
||||
</li>
|
||||
<li>
|
||||
<hr>
|
||||
<label><input id="noVNC_setting_show_dot" type="checkbox"> Show Dot when No Cursor</label>
|
||||
</li>
|
||||
<li><hr></li>
|
||||
<!-- Logging selection dropdown -->
|
||||
<li>
|
||||
<label>Logging:
|
||||
|
@ -259,8 +243,12 @@
|
|||
</select>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</ul></div>
|
||||
</li>
|
||||
<li class="noVNC_version_separator"><hr></li>
|
||||
<li class="noVNC_version_wrapper">
|
||||
<span>Version:</span>
|
||||
<span class="noVNC_version"></span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -292,10 +280,14 @@
|
|||
|
||||
<!-- Password Dialog -->
|
||||
<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">
|
||||
<ul>
|
||||
<li>
|
||||
<li id="noVNC_username_block">
|
||||
<label>Username:</label>
|
||||
<input id="noVNC_username_input">
|
||||
</li>
|
||||
<li id="noVNC_password_block">
|
||||
<label>Password:</label>
|
||||
{% if perms.instances.passwordless_console %}
|
||||
<input id="noVNC_password_input" type="password" value='{{ console_passwd }}' />
|
||||
|
@ -304,7 +296,7 @@
|
|||
{% endif %}
|
||||
</li>
|
||||
<li>
|
||||
<input id="noVNC_password_button" type="submit" value="Send Password" class="noVNC_submit" />
|
||||
<input id="noVNC_credentials_button" type="submit" value="Send Credentials" class="noVNC_submit" />
|
||||
</li>
|
||||
</ul>
|
||||
</form>
|
||||
|
@ -326,8 +318,8 @@
|
|||
html attributes which attempt to disable text suggestions on the
|
||||
on-screen keyboard. Let's hope Chrome implements the ime-mode
|
||||
style for example -->
|
||||
<textarea id="noVNC_keyboardinput" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false"
|
||||
mozactionhint="Enter" tabindex="-1"></textarea>
|
||||
<textarea id="noVNC_keyboardinput" autocapitalize="off" autocomplete="off" spellcheck="false"
|
||||
tabindex="-1"></textarea>
|
||||
</div>
|
||||
|
||||
<audio id="noVNC_bell">
|
||||
|
|
|
@ -205,7 +205,6 @@
|
|||
rfb.addEventListener("disconnect", disconnectedFromServer);
|
||||
rfb.addEventListener("credentialsrequired", credentialsAreRequired);
|
||||
rfb.addEventListener("desktopname", updateDesktopName);
|
||||
rfb.addEventListener("capabilities", function () { updatePowerButtons(); });
|
||||
|
||||
// Set parameters that can be changed on an active connection
|
||||
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
|
||||
// native support in the browsers, so that our error handler
|
||||
// can catch script-loading errors.
|
||||
|
|
|
@ -15,18 +15,18 @@
|
|||
inkscape:export-xdpi="90"
|
||||
sodipodi:docname="windows.svg"
|
||||
inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
|
||||
inkscape:version="0.92.3 (2405546, 2018-03-11)"
|
||||
inkscape:version="0.92.4 (unknown)"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="-293 384 25 23"
|
||||
viewBox="-293 384 25 25"
|
||||
xml:space="preserve"
|
||||
width="25"
|
||||
height="23"><metadata
|
||||
height="25"><metadata
|
||||
id="metadata21"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs19" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
pagecolor="#959595"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
|
@ -35,51 +35,31 @@
|
|||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-height="1136"
|
||||
id="namedview17"
|
||||
showgrid="false"
|
||||
inkscape:pagecheckerboard="true"
|
||||
inkscape:zoom="9.44"
|
||||
inkscape:cx="-0.84745763"
|
||||
inkscape:cy="12.5"
|
||||
inkscape:window-x="2552"
|
||||
inkscape:window-y="122"
|
||||
showgrid="true"
|
||||
inkscape:pagecheckerboard="false"
|
||||
inkscape:zoom="32"
|
||||
inkscape:cx="3.926913"
|
||||
inkscape:cy="13.255959"
|
||||
inkscape:window-x="1920"
|
||||
inkscape:window-y="27"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg2" />
|
||||
inkscape:current-layer="svg2"><inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid818" /></sodipodi:namedview>
|
||||
<style
|
||||
type="text/css"
|
||||
id="style2">
|
||||
.st0{fill:#FFFFFF;}
|
||||
</style>
|
||||
<g
|
||||
id="g14"
|
||||
transform="matrix(1.2624869,0,0,1.3601695,73.614445,-144.84322)">
|
||||
<g
|
||||
id="g12">
|
||||
<path
|
||||
class="st0"
|
||||
d="m -277.4,396 c -0.7,0 -1.3,0 -2,0 -0.4,0 -0.5,-0.1 -0.5,-0.5 0,-1 0,-2 0,-3 0,-0.3 0.2,-0.5 0.5,-0.5 1.3,-0.1 2.6,-0.3 3.9,-0.4 0.4,0 0.7,0.1 0.7,0.6 0,1.1 0,2.2 0,3.3 0,0.4 -0.2,0.6 -0.6,0.6 -0.7,-0.1 -1.4,-0.1 -2,-0.1 z"
|
||||
id="path4"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#ffffff" />
|
||||
<path
|
||||
class="st0"
|
||||
d="m -274.9,399.3 c 0,0.6 0,1.1 0,1.7 0,0.4 -0.1,0.6 -0.6,0.6 -1.4,-0.1 -2.8,-0.3 -4.1,-0.4 -0.3,0 -0.4,-0.3 -0.4,-0.5 0,-1 0,-2 0,-3 0,-0.4 0.2,-0.5 0.6,-0.5 1.3,0 2.6,0 3.9,0 0.5,0 0.6,0.2 0.6,0.6 0,0.4 0,0.9 0,1.5 z"
|
||||
id="path6"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#ffffff" />
|
||||
<path
|
||||
class="st0"
|
||||
d="m -283.5,396 c -0.6,0 -1.3,0 -1.9,0 -0.4,0 -0.6,-0.1 -0.6,-0.6 0,-0.8 0,-1.5 0,-2.3 0,-0.4 0.2,-0.6 0.6,-0.7 1.3,-0.1 2.7,-0.3 4,-0.4 0.4,0 0.5,0.1 0.5,0.5 0,1 0,1.9 0,2.9 0,0.4 -0.2,0.5 -0.5,0.5 -0.8,0.1 -1.5,0.1 -2.1,0.1 z"
|
||||
id="path8"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#ffffff" />
|
||||
<path
|
||||
class="st0"
|
||||
d="m -283.5,397 c 0.6,0 1.3,0 1.9,0 0.4,0 0.6,0.1 0.6,0.5 0,1 0,1.9 0,2.9 0,0.4 -0.2,0.5 -0.5,0.5 -1.3,-0.1 -2.7,-0.3 -4,-0.4 -0.4,0 -0.6,-0.2 -0.6,-0.7 0,-0.7 0,-1.5 0,-2.2 0,-0.5 0.2,-0.7 0.7,-0.7 0.6,0.1 1.2,0.1 1.9,0.1 z"
|
||||
id="path10"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#ffffff" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
<path
|
||||
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 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 "
|
||||
transform="translate(-293,384)"
|
||||
id="path853" /><path
|
||||
id="path858"
|
||||
d="m -272,405 -10,-1.17578 V 397 h 10 z M -283,403.70508 -289,403 v -6 h 6 z"
|
||||
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
inkscape:connector-curvature="0" /></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",
|
||||
"New connection has been rejected with reason: ": "Ny anslutning har blivit nekad med följande skäl: ",
|
||||
"New connection has been rejected": "Ny anslutning har blivit nekad",
|
||||
"Password is required": "Lösenord krävs",
|
||||
"Credentials are required": "Användaruppgifter krävs",
|
||||
"noVNC encountered an error:": "noVNC stötte på ett problem:",
|
||||
"Hide/Show the control bar": "Göm/Visa kontrollbaren",
|
||||
"Drag": "Dra",
|
||||
"Move/Drag Viewport": "Flytta/Dra Vyn",
|
||||
"viewport drag": "dra vy",
|
||||
"Active Mouse Button": "Aktiv musknapp",
|
||||
"No mousebutton": "Ingen musknapp",
|
||||
"Left mousebutton": "Vänster musknapp",
|
||||
"Middle mousebutton": "Mitten-musknapp",
|
||||
"Right mousebutton": "Höger musknapp",
|
||||
"Keyboard": "Tangentbord",
|
||||
"Show Keyboard": "Visa Tangentbord",
|
||||
"Extra keys": "Extraknappar",
|
||||
|
@ -55,6 +50,8 @@
|
|||
"Local Scaling": "Lokal Skalning",
|
||||
"Remote Resizing": "Ändra Storlek",
|
||||
"Advanced": "Avancerat",
|
||||
"Quality:": "Kvalitet:",
|
||||
"Compression level:": "Kompressionsnivå:",
|
||||
"Repeater ID:": "Repeater-ID:",
|
||||
"WebSocket": "WebSocket",
|
||||
"Encrypt": "Kryptera",
|
||||
|
@ -65,9 +62,11 @@
|
|||
"Reconnect Delay (ms):": "Fördröjning (ms):",
|
||||
"Show Dot when No Cursor": "Visa prick när ingen muspekare finns",
|
||||
"Logging:": "Loggning:",
|
||||
"Version:": "Version:",
|
||||
"Disconnect": "Koppla från",
|
||||
"Connect": "Anslut",
|
||||
"Username:": "Användarnamn:",
|
||||
"Password:": "Lösenord:",
|
||||
"Send Password": "Skicka lösenord",
|
||||
"Send Credentials": "Skicka Användaruppgifter",
|
||||
"Cancel": "Avbryt"
|
||||
}
|
|
@ -1,19 +1,19 @@
|
|||
{
|
||||
"Connecting...": "链接中...",
|
||||
"Disconnecting...": "正在中断连接...",
|
||||
"Reconnecting...": "重新链接中...",
|
||||
"Connecting...": "连接中...",
|
||||
"Disconnecting...": "正在断开连接...",
|
||||
"Reconnecting...": "重新连接中...",
|
||||
"Internal error": "内部错误",
|
||||
"Must set host": "请提供主机名",
|
||||
"Connected (encrypted) to ": "已加密链接到",
|
||||
"Connected (unencrypted) to ": "未加密链接到",
|
||||
"Something went wrong, connection is closed": "发生错误,链接已关闭",
|
||||
"Failed to connect to server": "无法链接到服务器",
|
||||
"Disconnected": "链接已中断",
|
||||
"New connection has been rejected with reason: ": "链接被拒绝,原因:",
|
||||
"New connection has been rejected": "链接被拒绝",
|
||||
"Connected (encrypted) to ": "已连接到(加密)",
|
||||
"Connected (unencrypted) to ": "已连接到(未加密)",
|
||||
"Something went wrong, connection is closed": "发生错误,连接已关闭",
|
||||
"Failed to connect to server": "无法连接到服务器",
|
||||
"Disconnected": "已断开连接",
|
||||
"New connection has been rejected with reason: ": "连接被拒绝,原因:",
|
||||
"New connection has been rejected": "连接被拒绝",
|
||||
"Password is required": "请提供密码",
|
||||
"noVNC encountered an error:": "noVNC 遇到一个错误:",
|
||||
"Hide/Show the control bar": "显示/隐藏控制列",
|
||||
"Hide/Show the control bar": "显示/隐藏控制栏",
|
||||
"Move/Drag Viewport": "拖放显示范围",
|
||||
"viewport drag": "显示范围拖放",
|
||||
"Active Mouse Button": "启动鼠标按鍵",
|
||||
|
@ -43,10 +43,10 @@
|
|||
"Reset": "重置",
|
||||
"Clipboard": "剪贴板",
|
||||
"Clear": "清除",
|
||||
"Fullscreen": "全屏幕",
|
||||
"Fullscreen": "全屏",
|
||||
"Settings": "设置",
|
||||
"Shared Mode": "分享模式",
|
||||
"View Only": "仅检视",
|
||||
"View Only": "仅查看",
|
||||
"Clip to Window": "限制/裁切窗口大小",
|
||||
"Scaling Mode:": "缩放模式:",
|
||||
"None": "无",
|
||||
|
@ -59,11 +59,11 @@
|
|||
"Host:": "主机:",
|
||||
"Port:": "端口:",
|
||||
"Path:": "路径:",
|
||||
"Automatic Reconnect": "自动重新链接",
|
||||
"Reconnect Delay (ms):": "重新链接间隔 (ms):",
|
||||
"Automatic Reconnect": "自动重新连接",
|
||||
"Reconnect Delay (ms):": "重新连接间隔 (ms):",
|
||||
"Logging:": "日志级别:",
|
||||
"Disconnect": "终端链接",
|
||||
"Connect": "链接",
|
||||
"Disconnect": "中断连接",
|
||||
"Connect": "连接",
|
||||
"Password:": "密码:",
|
||||
"Cancel": "取消"
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* noVNC base CSS
|
||||
* Copyright (C) 2018 The noVNC Authors
|
||||
* Copyright (C) 2019 The noVNC Authors
|
||||
* noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
|
||||
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
|
||||
*/
|
||||
|
@ -22,13 +22,12 @@
|
|||
body {
|
||||
margin:0;
|
||||
padding:0;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-family: Helvetica;
|
||||
/*Background image with light grey curve.*/
|
||||
background-color:#494949;
|
||||
background-repeat:no-repeat;
|
||||
background-position:right bottom;
|
||||
height:100%;
|
||||
display: flex;
|
||||
touch-action: none;
|
||||
}
|
||||
|
||||
|
@ -84,8 +83,20 @@ html {
|
|||
* ----------------------------------------
|
||||
*/
|
||||
|
||||
input[type=input], input[type=password], input[type=number],
|
||||
input:not([type]), textarea {
|
||||
input:not([type]),
|
||||
input[type=date],
|
||||
input[type=datetime-local],
|
||||
input[type=email],
|
||||
input[type=month],
|
||||
input[type=number],
|
||||
input[type=password],
|
||||
input[type=search],
|
||||
input[type=tel],
|
||||
input[type=text],
|
||||
input[type=time],
|
||||
input[type=url],
|
||||
input[type=week],
|
||||
textarea {
|
||||
/* Disable default rendering */
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
|
@ -99,7 +110,11 @@ input:not([type]), textarea {
|
|||
background: linear-gradient(to top, rgb(255, 255, 255) 80%, rgb(240, 240, 240));
|
||||
}
|
||||
|
||||
input[type=button], input[type=submit], select {
|
||||
input[type=button],
|
||||
input[type=color],
|
||||
input[type=reset],
|
||||
input[type=submit],
|
||||
select {
|
||||
/* Disable default rendering */
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
|
@ -117,7 +132,10 @@ input[type=button], input[type=submit], select {
|
|||
vertical-align: middle;
|
||||
}
|
||||
|
||||
input[type=button], input[type=submit] {
|
||||
input[type=button],
|
||||
input[type=color],
|
||||
input[type=reset],
|
||||
input[type=submit] {
|
||||
padding-left: 20px;
|
||||
padding-right: 20px;
|
||||
}
|
||||
|
@ -127,35 +145,72 @@ option {
|
|||
background: white;
|
||||
}
|
||||
|
||||
input[type=input]:focus, input[type=password]:focus,
|
||||
input:not([type]):focus, input[type=button]:focus,
|
||||
input:not([type]):focus,
|
||||
input[type=button]:focus,
|
||||
input[type=color]:focus,
|
||||
input[type=date]:focus,
|
||||
input[type=datetime-local]:focus,
|
||||
input[type=email]:focus,
|
||||
input[type=month]:focus,
|
||||
input[type=number]:focus,
|
||||
input[type=password]:focus,
|
||||
input[type=reset]:focus,
|
||||
input[type=search]:focus,
|
||||
input[type=submit]:focus,
|
||||
textarea:focus, select:focus {
|
||||
input[type=tel]:focus,
|
||||
input[type=text]:focus,
|
||||
input[type=time]:focus,
|
||||
input[type=url]:focus,
|
||||
input[type=week]:focus,
|
||||
select:focus,
|
||||
textarea:focus {
|
||||
box-shadow: 0px 0px 3px rgba(74, 144, 217, 0.5);
|
||||
border-color: rgb(74, 144, 217);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input[type=button]::-moz-focus-inner,
|
||||
input[type=color]::-moz-focus-inner,
|
||||
input[type=reset]::-moz-focus-inner,
|
||||
input[type=submit]::-moz-focus-inner {
|
||||
border: none;
|
||||
}
|
||||
|
||||
input[type=input]:disabled, input[type=password]:disabled,
|
||||
input:not([type]):disabled, input[type=button]:disabled,
|
||||
input[type=submit]:disabled, input[type=number]:disabled,
|
||||
textarea:disabled, select:disabled {
|
||||
input:not([type]):disabled,
|
||||
input[type=button]:disabled,
|
||||
input[type=color]:disabled,
|
||||
input[type=date]:disabled,
|
||||
input[type=datetime-local]:disabled,
|
||||
input[type=email]:disabled,
|
||||
input[type=month]:disabled,
|
||||
input[type=number]:disabled,
|
||||
input[type=password]:disabled,
|
||||
input[type=reset]:disabled,
|
||||
input[type=search]:disabled,
|
||||
input[type=submit]:disabled,
|
||||
input[type=tel]:disabled,
|
||||
input[type=text]:disabled,
|
||||
input[type=time]:disabled,
|
||||
input[type=url]:disabled,
|
||||
input[type=week]:disabled,
|
||||
select:disabled,
|
||||
textarea:disabled {
|
||||
color: rgb(128, 128, 128);
|
||||
background: rgb(240, 240, 240);
|
||||
}
|
||||
|
||||
input[type=button]:active, input[type=submit]:active,
|
||||
input[type=button]:active,
|
||||
input[type=color]:active,
|
||||
input[type=reset]:active,
|
||||
input[type=submit]:active,
|
||||
select:active {
|
||||
border-bottom-width: 1px;
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
:root:not(.noVNC_touch) input[type=button]:hover:not(:disabled),
|
||||
:root:not(.noVNC_touch) input[type=color]:hover:not(:disabled),
|
||||
:root:not(.noVNC_touch) input[type=reset]:hover:not(:disabled),
|
||||
:root:not(.noVNC_touch) input[type=submit]:hover:not(:disabled),
|
||||
:root:not(.noVNC_touch) select:hover:not(:disabled) {
|
||||
background: linear-gradient(to top, rgb(255, 255, 255), rgb(250, 250, 250));
|
||||
|
@ -580,7 +635,7 @@ select:active {
|
|||
}
|
||||
|
||||
/* Extra manual keys */
|
||||
:root:not(.noVNC_connected) #noVNC_extra_keys {
|
||||
:root:not(.noVNC_connected) #noVNC_toggle_extra_keys_button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
@ -632,6 +687,16 @@ select:active {
|
|||
width: 100px;
|
||||
}
|
||||
|
||||
/* Version */
|
||||
|
||||
.noVNC_version_wrapper {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
.noVNC_version {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
/* Connection Controls */
|
||||
:root:not(.noVNC_connected) #noVNC_disconnect_button {
|
||||
display: none;
|
||||
|
@ -781,19 +846,23 @@ select:active {
|
|||
* ----------------------------------------
|
||||
*/
|
||||
|
||||
#noVNC_password_dlg {
|
||||
#noVNC_credentials_dlg {
|
||||
position: relative;
|
||||
|
||||
transform: translateY(-50px);
|
||||
}
|
||||
#noVNC_password_dlg.noVNC_open {
|
||||
#noVNC_credentials_dlg.noVNC_open {
|
||||
transform: translateY(0);
|
||||
}
|
||||
#noVNC_password_dlg ul {
|
||||
#noVNC_credentials_dlg ul {
|
||||
list-style: none;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
.noVNC_hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
/* ----------------------------------------
|
||||
* Main Area
|
||||
|
|
|
@ -60,3 +60,8 @@ html {
|
|||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
#noVNC_container {
|
||||
flex: 1; /* fill remaining space */
|
||||
overflow: hidden;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2018 The noVNC Authors
|
||||
* Copyright (C) 2019 The noVNC Authors
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
|
@ -8,7 +8,7 @@
|
|||
|
||||
import * as Log from '../core/util/logging.js';
|
||||
import _, { l10n } from './localization.js';
|
||||
import { isTouchDevice, isSafari, isIOS, isAndroid, dragThreshold }
|
||||
import { isTouchDevice, isSafari, hasScrollbarGutter, dragThreshold }
|
||||
from '../core/util/browser.js';
|
||||
import { setCapture, getPointerEvent } from '../core/util/events.js';
|
||||
import KeyTable from "../core/input/keysym.js";
|
||||
|
@ -17,6 +17,8 @@ import Keyboard from "../core/input/keyboard.js";
|
|||
import RFB from "../core/rfb.js";
|
||||
import * as WebUtil from "./webutil.js";
|
||||
|
||||
const PAGE_TITLE = "noVNC";
|
||||
|
||||
const UI = {
|
||||
|
||||
connected: false,
|
||||
|
@ -35,9 +37,9 @@ const UI = {
|
|||
lastKeyboardinput: null,
|
||||
defaultKeyboardinputLen: 100,
|
||||
|
||||
inhibit_reconnect: true,
|
||||
reconnect_callback: null,
|
||||
reconnect_password: null,
|
||||
inhibitReconnect: true,
|
||||
reconnectCallback: null,
|
||||
reconnectPassword: null,
|
||||
|
||||
prime() {
|
||||
return WebUtil.initSettings().then(() => {
|
||||
|
@ -59,6 +61,17 @@ const UI = {
|
|||
// Translate the DOM
|
||||
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
|
||||
if (isTouchDevice) {
|
||||
document.documentElement.classList.add("noVNC_touch");
|
||||
|
@ -148,6 +161,8 @@ const UI = {
|
|||
UI.initSetting('encrypt', (window.location.protocol === "https:"));
|
||||
UI.initSetting('view_clip', false);
|
||||
UI.initSetting('resize', 'off');
|
||||
UI.initSetting('quality', 6);
|
||||
UI.initSetting('compression', 2);
|
||||
UI.initSetting('shared', true);
|
||||
UI.initSetting('view_only', false);
|
||||
UI.initSetting('show_dot', false);
|
||||
|
@ -219,14 +234,6 @@ const UI = {
|
|||
},
|
||||
|
||||
addTouchSpecificHandlers() {
|
||||
document.getElementById("noVNC_mouse_button0")
|
||||
.addEventListener('click', () => UI.setMouseButton(1));
|
||||
document.getElementById("noVNC_mouse_button1")
|
||||
.addEventListener('click', () => UI.setMouseButton(2));
|
||||
document.getElementById("noVNC_mouse_button2")
|
||||
.addEventListener('click', () => UI.setMouseButton(4));
|
||||
document.getElementById("noVNC_mouse_button4")
|
||||
.addEventListener('click', () => UI.setMouseButton(0));
|
||||
document.getElementById("noVNC_keyboard_button")
|
||||
.addEventListener('click', UI.toggleVirtualKeyboard);
|
||||
|
||||
|
@ -282,33 +289,6 @@ const UI = {
|
|||
.addEventListener('click', UI.sendEsc);
|
||||
document.getElementById("noVNC_send_ctrl_alt_del_button")
|
||||
.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() {
|
||||
|
@ -330,8 +310,8 @@ const UI = {
|
|||
document.getElementById("noVNC_cancel_reconnect_button")
|
||||
.addEventListener('click', UI.cancelReconnect);
|
||||
|
||||
document.getElementById("noVNC_password_button")
|
||||
.addEventListener('click', UI.setPassword);
|
||||
document.getElementById("noVNC_credentials_button")
|
||||
.addEventListener('click', UI.setCredentials);
|
||||
},
|
||||
|
||||
addClipboardHandlers() {
|
||||
|
@ -361,6 +341,10 @@ const UI = {
|
|||
UI.addSettingChangeHandler('resize');
|
||||
UI.addSettingChangeHandler('resize', UI.applyResizeMode);
|
||||
UI.addSettingChangeHandler('resize', UI.updateViewClip);
|
||||
UI.addSettingChangeHandler('quality');
|
||||
UI.addSettingChangeHandler('quality', UI.updateQuality);
|
||||
UI.addSettingChangeHandler('compression');
|
||||
UI.addSettingChangeHandler('compression', UI.updateCompression);
|
||||
UI.addSettingChangeHandler('view_clip');
|
||||
UI.addSettingChangeHandler('view_clip', UI.updateViewClip);
|
||||
UI.addSettingChangeHandler('shared');
|
||||
|
@ -381,8 +365,6 @@ const UI = {
|
|||
addFullscreenHandlers() {
|
||||
document.getElementById("noVNC_fullscreen_button")
|
||||
.addEventListener('click', UI.toggleFullscreen);
|
||||
document.getElementById("fullscreen_button")
|
||||
.addEventListener('click', UI.toggleFullscreen);
|
||||
|
||||
window.addEventListener('fullscreenchange', UI.updateFullscreenButton);
|
||||
window.addEventListener('mozfullscreenchange', UI.updateFullscreenButton);
|
||||
|
@ -404,25 +386,25 @@ const UI = {
|
|||
document.documentElement.classList.remove("noVNC_disconnecting");
|
||||
document.documentElement.classList.remove("noVNC_reconnecting");
|
||||
|
||||
const transition_elem = document.getElementById("noVNC_transition_text");
|
||||
const transitionElem = document.getElementById("noVNC_transition_text");
|
||||
switch (state) {
|
||||
case 'init':
|
||||
break;
|
||||
case 'connecting':
|
||||
transition_elem.textContent = _("Connecting...");
|
||||
transitionElem.textContent = _("Connecting...");
|
||||
document.documentElement.classList.add("noVNC_connecting");
|
||||
break;
|
||||
case 'connected':
|
||||
document.documentElement.classList.add("noVNC_connected");
|
||||
break;
|
||||
case 'disconnecting':
|
||||
transition_elem.textContent = _("Disconnecting...");
|
||||
transitionElem.textContent = _("Disconnecting...");
|
||||
document.documentElement.classList.add("noVNC_disconnecting");
|
||||
break;
|
||||
case 'disconnected':
|
||||
break;
|
||||
case 'reconnecting':
|
||||
transition_elem.textContent = _("Reconnecting...");
|
||||
transitionElem.textContent = _("Reconnecting...");
|
||||
document.documentElement.classList.add("noVNC_reconnecting");
|
||||
break;
|
||||
default:
|
||||
|
@ -440,7 +422,6 @@ const UI = {
|
|||
UI.disableSetting('port');
|
||||
UI.disableSetting('path');
|
||||
UI.disableSetting('repeaterID');
|
||||
UI.setMouseButton(1);
|
||||
|
||||
// Hide the controlbar after 2 seconds
|
||||
UI.closeControlbarTimeout = setTimeout(UI.closeControlbar, 2000);
|
||||
|
@ -455,38 +436,35 @@ const UI = {
|
|||
UI.keepControlbar();
|
||||
}
|
||||
|
||||
// State change closes the password dialog
|
||||
document.getElementById('noVNC_password_dlg')
|
||||
// State change closes dialogs as they may not be relevant
|
||||
// anymore
|
||||
UI.closeAllPanels();
|
||||
document.getElementById('noVNC_credentials_dlg')
|
||||
.classList.remove('noVNC_open');
|
||||
},
|
||||
|
||||
showStatus(text, status_type, time) {
|
||||
showStatus(text, statusType, time) {
|
||||
const statusElem = document.getElementById('noVNC_status');
|
||||
|
||||
clearTimeout(UI.statusTimeout);
|
||||
|
||||
if (typeof status_type === 'undefined') {
|
||||
status_type = 'normal';
|
||||
if (typeof statusType === 'undefined') {
|
||||
statusType = 'normal';
|
||||
}
|
||||
|
||||
// Don't overwrite more severe visible statuses and never
|
||||
// errors. Only shows the first error.
|
||||
let visible_status_type = 'none';
|
||||
if (statusElem.classList.contains("noVNC_open")) {
|
||||
if (statusElem.classList.contains("noVNC_status_error")) {
|
||||
visible_status_type = 'error';
|
||||
} else if (statusElem.classList.contains("noVNC_status_warn")) {
|
||||
visible_status_type = 'warn';
|
||||
} else {
|
||||
visible_status_type = 'normal';
|
||||
}
|
||||
}
|
||||
if (visible_status_type === 'error' ||
|
||||
(visible_status_type === 'warn' && status_type === 'normal')) {
|
||||
return;
|
||||
}
|
||||
if (statusElem.classList.contains("noVNC_status_warn") &&
|
||||
statusType === 'normal') {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
switch (status_type) {
|
||||
clearTimeout(UI.statusTimeout);
|
||||
|
||||
switch (statusType) {
|
||||
case 'error':
|
||||
statusElem.classList.remove("noVNC_status_warn");
|
||||
statusElem.classList.remove("noVNC_status_normal");
|
||||
|
@ -516,7 +494,7 @@ const UI = {
|
|||
}
|
||||
|
||||
// Error messages do not timeout
|
||||
if (status_type !== 'error') {
|
||||
if (statusType !== 'error') {
|
||||
UI.statusTimeout = window.setTimeout(UI.hideStatus, time);
|
||||
}
|
||||
},
|
||||
|
@ -536,6 +514,13 @@ const UI = {
|
|||
},
|
||||
|
||||
idleControlbar() {
|
||||
// Don't fade if a child of the control bar has focus
|
||||
if (document.getElementById('noVNC_control_bar')
|
||||
.contains(document.activeElement) && document.hasFocus()) {
|
||||
UI.activateControlbar();
|
||||
return;
|
||||
}
|
||||
|
||||
document.getElementById('noVNC_control_bar_anchor')
|
||||
.classList.add("noVNC_idle");
|
||||
},
|
||||
|
@ -553,6 +538,7 @@ const UI = {
|
|||
UI.closeAllPanels();
|
||||
document.getElementById('noVNC_control_bar')
|
||||
.classList.remove("noVNC_open");
|
||||
UI.rfb.focus();
|
||||
},
|
||||
|
||||
toggleControlbar() {
|
||||
|
@ -850,6 +836,8 @@ const UI = {
|
|||
UI.updateSetting('encrypt');
|
||||
UI.updateSetting('view_clip');
|
||||
UI.updateSetting('resize');
|
||||
UI.updateSetting('quality');
|
||||
UI.updateSetting('compression');
|
||||
UI.updateSetting('shared');
|
||||
UI.updateSetting('view_only');
|
||||
UI.updateSetting('path');
|
||||
|
@ -1006,7 +994,7 @@ const UI = {
|
|||
|
||||
if (typeof password === 'undefined') {
|
||||
password = WebUtil.getConfigVar('password');
|
||||
UI.reconnect_password = password;
|
||||
UI.reconnectPassword = password;
|
||||
}
|
||||
|
||||
if (password === null) {
|
||||
|
@ -1021,7 +1009,6 @@ const UI = {
|
|||
return;
|
||||
}
|
||||
|
||||
UI.closeAllPanels();
|
||||
UI.closeConnectPanel();
|
||||
|
||||
UI.updateVisualState('connecting');
|
||||
|
@ -1038,7 +1025,6 @@ const UI = {
|
|||
|
||||
UI.rfb = new RFB(document.getElementById('noVNC_container'), url,
|
||||
{ shared: UI.getSetting('shared'),
|
||||
showDotCursor: UI.getSetting('show_dot'),
|
||||
repeaterID: UI.getSetting('repeaterID'),
|
||||
credentials: { password: password } });
|
||||
UI.rfb.addEventListener("connect", UI.connectFinished);
|
||||
|
@ -1052,18 +1038,20 @@ const UI = {
|
|||
UI.rfb.clipViewport = UI.getSetting('view_clip');
|
||||
UI.rfb.scaleViewport = UI.getSetting('resize') === 'scale';
|
||||
UI.rfb.resizeSession = UI.getSetting('resize') === 'remote';
|
||||
UI.rfb.qualityLevel = parseInt(UI.getSetting('quality'));
|
||||
UI.rfb.compressionLevel = parseInt(UI.getSetting('compression'));
|
||||
UI.rfb.showDotCursor = UI.getSetting('show_dot');
|
||||
|
||||
UI.updateViewOnly(); // requires UI.rfb
|
||||
},
|
||||
|
||||
disconnect() {
|
||||
UI.closeAllPanels();
|
||||
UI.rfb.disconnect();
|
||||
|
||||
UI.connected = false;
|
||||
|
||||
// Disable automatic reconnecting
|
||||
UI.inhibit_reconnect = true;
|
||||
UI.inhibitReconnect = true;
|
||||
|
||||
UI.updateVisualState('disconnecting');
|
||||
|
||||
|
@ -1071,20 +1059,20 @@ const UI = {
|
|||
},
|
||||
|
||||
reconnect() {
|
||||
UI.reconnect_callback = null;
|
||||
UI.reconnectCallback = null;
|
||||
|
||||
// if reconnect has been disabled in the meantime, do nothing.
|
||||
if (UI.inhibit_reconnect) {
|
||||
if (UI.inhibitReconnect) {
|
||||
return;
|
||||
}
|
||||
|
||||
UI.connect(null, UI.reconnect_password);
|
||||
UI.connect(null, UI.reconnectPassword);
|
||||
},
|
||||
|
||||
cancelReconnect() {
|
||||
if (UI.reconnect_callback !== null) {
|
||||
clearTimeout(UI.reconnect_callback);
|
||||
UI.reconnect_callback = null;
|
||||
if (UI.reconnectCallback !== null) {
|
||||
clearTimeout(UI.reconnectCallback);
|
||||
UI.reconnectCallback = null;
|
||||
}
|
||||
|
||||
UI.updateVisualState('disconnected');
|
||||
|
@ -1095,7 +1083,7 @@ const UI = {
|
|||
|
||||
connectFinished(e) {
|
||||
UI.connected = true;
|
||||
UI.inhibit_reconnect = false;
|
||||
UI.inhibitReconnect = false;
|
||||
|
||||
let msg;
|
||||
if (UI.getSetting('encrypt')) {
|
||||
|
@ -1129,17 +1117,19 @@ const UI = {
|
|||
} else {
|
||||
UI.showStatus(_("Failed to connect to server"), 'error');
|
||||
}
|
||||
} else if (UI.getSetting('reconnect', false) === true && !UI.inhibit_reconnect) {
|
||||
} else if (UI.getSetting('reconnect', false) === true && !UI.inhibitReconnect) {
|
||||
UI.updateVisualState('reconnecting');
|
||||
|
||||
const delay = parseInt(UI.getSetting('reconnect_delay'));
|
||||
UI.reconnect_callback = setTimeout(UI.reconnect, delay);
|
||||
UI.reconnectCallback = setTimeout(UI.reconnect, delay);
|
||||
return;
|
||||
} else {
|
||||
UI.updateVisualState('disconnected');
|
||||
UI.showStatus(_("Disconnected"), 'normal');
|
||||
}
|
||||
|
||||
document.title = PAGE_TITLE;
|
||||
|
||||
UI.openControlbar();
|
||||
UI.openConnectPanel();
|
||||
},
|
||||
|
@ -1166,27 +1156,46 @@ const UI = {
|
|||
|
||||
credentials(e) {
|
||||
// FIXME: handle more types
|
||||
document.getElementById('noVNC_password_dlg')
|
||||
|
||||
document.getElementById("noVNC_username_block").classList.remove("noVNC_hidden");
|
||||
document.getElementById("noVNC_password_block").classList.remove("noVNC_hidden");
|
||||
|
||||
let inputFocus = "none";
|
||||
if (e.detail.types.indexOf("username") === -1) {
|
||||
document.getElementById("noVNC_username_block").classList.add("noVNC_hidden");
|
||||
} else {
|
||||
inputFocus = inputFocus === "none" ? "noVNC_username_input" : inputFocus;
|
||||
}
|
||||
if (e.detail.types.indexOf("password") === -1) {
|
||||
document.getElementById("noVNC_password_block").classList.add("noVNC_hidden");
|
||||
} else {
|
||||
inputFocus = inputFocus === "none" ? "noVNC_password_input" : inputFocus;
|
||||
}
|
||||
document.getElementById('noVNC_credentials_dlg')
|
||||
.classList.add('noVNC_open');
|
||||
|
||||
setTimeout(() => document
|
||||
.getElementById('noVNC_password_input').focus(), 100);
|
||||
.getElementById(inputFocus).focus(), 100);
|
||||
|
||||
Log.Warn("Server asked for a password");
|
||||
UI.showStatus(_("Password is required"), "warning");
|
||||
Log.Warn("Server asked for credentials");
|
||||
UI.showStatus(_("Credentials are required"), "warning");
|
||||
},
|
||||
|
||||
setPassword(e) {
|
||||
setCredentials(e) {
|
||||
// Prevent actually submitting the form
|
||||
e.preventDefault();
|
||||
|
||||
const inputElem = document.getElementById('noVNC_password_input');
|
||||
const password = inputElem.value;
|
||||
let inputElemUsername = document.getElementById('noVNC_username_input');
|
||||
const username = inputElemUsername.value;
|
||||
|
||||
let inputElemPassword = document.getElementById('noVNC_password_input');
|
||||
const password = inputElemPassword.value;
|
||||
// Clear the input after reading the password
|
||||
inputElem.value = "";
|
||||
UI.rfb.sendCredentials({ password: password });
|
||||
UI.reconnect_password = password;
|
||||
document.getElementById('noVNC_password_dlg')
|
||||
inputElemPassword.value = "";
|
||||
|
||||
UI.rfb.sendCredentials({ username: username, password: password });
|
||||
UI.reconnectPassword = password;
|
||||
document.getElementById('noVNC_credentials_dlg')
|
||||
.classList.remove('noVNC_open');
|
||||
},
|
||||
|
||||
|
@ -1269,8 +1278,9 @@ const UI = {
|
|||
// Can't be clipping if viewport is scaled to fit
|
||||
UI.forceSetting('view_clip', false);
|
||||
UI.rfb.clipViewport = false;
|
||||
} else if (isIOS() || isAndroid()) {
|
||||
// iOS and Android usually have shit scrollbars
|
||||
} else if (!hasScrollbarGutter) {
|
||||
// Some platforms have scrollbars that are difficult
|
||||
// to use in our case, so we always use our own panning
|
||||
UI.forceSetting('view_clip', true);
|
||||
UI.rfb.clipViewport = true;
|
||||
} else {
|
||||
|
@ -1313,30 +1323,40 @@ const UI = {
|
|||
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) {
|
||||
viewDragButton.classList.remove("noVNC_hidden");
|
||||
} else {
|
||||
viewDragButton.classList.add("noVNC_hidden");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/* ------^-------
|
||||
* /VIEWDRAG
|
||||
* ==============
|
||||
* QUALITY
|
||||
* ------v------*/
|
||||
|
||||
updateQuality() {
|
||||
if (!UI.rfb) return;
|
||||
|
||||
UI.rfb.qualityLevel = parseInt(UI.getSetting('quality'));
|
||||
},
|
||||
|
||||
/* ------^-------
|
||||
* /QUALITY
|
||||
* ==============
|
||||
* COMPRESSION
|
||||
* ------v------*/
|
||||
|
||||
updateCompression() {
|
||||
if (!UI.rfb) return;
|
||||
|
||||
UI.rfb.compressionLevel = parseInt(UI.getSetting('compression'));
|
||||
},
|
||||
|
||||
/* ------^-------
|
||||
* /COMPRESSION
|
||||
* ==============
|
||||
* KEYBOARD
|
||||
* ------v------*/
|
||||
|
||||
|
@ -1531,20 +1551,20 @@ const UI = {
|
|||
},
|
||||
|
||||
sendEsc() {
|
||||
UI.rfb.sendKey(KeyTable.XK_Escape, "Escape");
|
||||
UI.sendKey(KeyTable.XK_Escape, "Escape");
|
||||
},
|
||||
|
||||
sendTab() {
|
||||
UI.rfb.sendKey(KeyTable.XK_Tab);
|
||||
UI.sendKey(KeyTable.XK_Tab, "Tab");
|
||||
},
|
||||
|
||||
toggleCtrl() {
|
||||
const btn = document.getElementById('noVNC_toggle_ctrl_button');
|
||||
if (btn.classList.contains("noVNC_selected")) {
|
||||
UI.rfb.sendKey(KeyTable.XK_Control_L, "ControlLeft", false);
|
||||
UI.sendKey(KeyTable.XK_Control_L, "ControlLeft", false);
|
||||
btn.classList.remove("noVNC_selected");
|
||||
} else {
|
||||
UI.rfb.sendKey(KeyTable.XK_Control_L, "ControlLeft", true);
|
||||
UI.sendKey(KeyTable.XK_Control_L, "ControlLeft", true);
|
||||
btn.classList.add("noVNC_selected");
|
||||
}
|
||||
},
|
||||
|
@ -1552,10 +1572,10 @@ const UI = {
|
|||
toggleWindows() {
|
||||
const btn = document.getElementById('noVNC_toggle_windows_button');
|
||||
if (btn.classList.contains("noVNC_selected")) {
|
||||
UI.rfb.sendKey(KeyTable.XK_Super_L, "MetaLeft", false);
|
||||
UI.sendKey(KeyTable.XK_Super_L, "MetaLeft", false);
|
||||
btn.classList.remove("noVNC_selected");
|
||||
} else {
|
||||
UI.rfb.sendKey(KeyTable.XK_Super_L, "MetaLeft", true);
|
||||
UI.sendKey(KeyTable.XK_Super_L, "MetaLeft", true);
|
||||
btn.classList.add("noVNC_selected");
|
||||
}
|
||||
},
|
||||
|
@ -1563,20 +1583,39 @@ const UI = {
|
|||
toggleAlt() {
|
||||
const btn = document.getElementById('noVNC_toggle_alt_button');
|
||||
if (btn.classList.contains("noVNC_selected")) {
|
||||
UI.rfb.sendKey(KeyTable.XK_Alt_L, "AltLeft", false);
|
||||
UI.sendKey(KeyTable.XK_Alt_L, "AltLeft", false);
|
||||
btn.classList.remove("noVNC_selected");
|
||||
} else {
|
||||
UI.rfb.sendKey(KeyTable.XK_Alt_L, "AltLeft", true);
|
||||
UI.sendKey(KeyTable.XK_Alt_L, "AltLeft", true);
|
||||
btn.classList.add("noVNC_selected");
|
||||
}
|
||||
},
|
||||
|
||||
sendCtrlAltDel() {
|
||||
UI.rfb.sendCtrlAltDel();
|
||||
// See below
|
||||
UI.rfb.focus();
|
||||
UI.idleControlbar();
|
||||
},
|
||||
|
||||
sendCtrlAltFN: function(f) {
|
||||
UI.rfb.sendCtrlAltFN(f);
|
||||
sendKey(keysym, code, down) {
|
||||
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
|
||||
* ------v------*/
|
||||
|
||||
setMouseButton(num) {
|
||||
const view_only = UI.rfb.viewOnly;
|
||||
if (UI.rfb && !view_only) {
|
||||
UI.rfb.touchButton = num;
|
||||
}
|
||||
|
||||
const blist = [0, 1, 2, 4];
|
||||
for (let b = 0; b < blist.length; b++) {
|
||||
const button = document.getElementById('noVNC_mouse_button' +
|
||||
blist[b]);
|
||||
if (blist[b] === num && !view_only) {
|
||||
button.classList.remove("noVNC_hidden");
|
||||
} else {
|
||||
button.classList.add("noVNC_hidden");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
updateViewOnly() {
|
||||
if (!UI.rfb) return;
|
||||
UI.rfb.viewOnly = UI.getSetting('view_only');
|
||||
|
@ -1613,14 +1634,14 @@ const UI = {
|
|||
.classList.add('noVNC_hidden');
|
||||
document.getElementById('noVNC_toggle_extra_keys_button')
|
||||
.classList.add('noVNC_hidden');
|
||||
document.getElementById('noVNC_mouse_button' + UI.rfb.touchButton)
|
||||
document.getElementById('noVNC_clipboard_button')
|
||||
.classList.add('noVNC_hidden');
|
||||
} else {
|
||||
document.getElementById('noVNC_keyboard_button')
|
||||
.classList.remove('noVNC_hidden');
|
||||
document.getElementById('noVNC_toggle_extra_keys_button')
|
||||
.classList.remove('noVNC_hidden');
|
||||
document.getElementById('noVNC_mouse_button' + UI.rfb.touchButton)
|
||||
document.getElementById('noVNC_clipboard_button')
|
||||
.classList.remove('noVNC_hidden');
|
||||
}
|
||||
},
|
||||
|
@ -1631,13 +1652,13 @@ const UI = {
|
|||
},
|
||||
|
||||
updateLogging() {
|
||||
WebUtil.init_logging(UI.getSetting('logging'));
|
||||
WebUtil.initLogging(UI.getSetting('logging'));
|
||||
},
|
||||
|
||||
updateDesktopName(e) {
|
||||
UI.desktopName = e.detail.name;
|
||||
// Display the desktop name in the document title
|
||||
document.title = e.detail.name + " - noVNC";
|
||||
document.title = e.detail.name + " - " + PAGE_TITLE;
|
||||
},
|
||||
|
||||
bell(e) {
|
||||
|
@ -1673,7 +1694,7 @@ const UI = {
|
|||
};
|
||||
|
||||
// Set up translations
|
||||
const LINGUAS = ["cs", "de", "el", "es", "ko", "nl", "pl", "ru", "sv", "tr", "zh_CN", "zh_TW"];
|
||||
const LINGUAS = ["cs", "de", "el", "es", "ja", "ko", "nl", "pl", "ru", "sv", "tr", "zh_CN", "zh_TW"];
|
||||
l10n.setup(LINGUAS);
|
||||
if (l10n.language === "en" || l10n.dictionary !== undefined) {
|
||||
UI.prime();
|
||||
|
|
|
@ -1,21 +1,21 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2018 The noVNC Authors
|
||||
* Copyright (C) 2019 The noVNC Authors
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
import { init_logging as main_init_logging } from '../core/util/logging.js';
|
||||
import { initLogging as mainInitLogging } from '../core/util/logging.js';
|
||||
|
||||
// init log level reading the logging HTTP param
|
||||
export function init_logging(level) {
|
||||
export function initLogging(level) {
|
||||
"use strict";
|
||||
if (typeof level !== "undefined") {
|
||||
main_init_logging(level);
|
||||
mainInitLogging(level);
|
||||
} else {
|
||||
const param = document.location.href.match(/logging=([A-Za-z0-9._-]*)/);
|
||||
main_init_logging(param || undefined);
|
||||
mainInitLogging(param || undefined);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -184,7 +184,7 @@ export function injectParamIfMissing(path, param, value) {
|
|||
const elem = document.createElement('a');
|
||||
elem.href = path;
|
||||
|
||||
const param_eq = encodeURIComponent(param) + "=";
|
||||
const paramEq = encodeURIComponent(param) + "=";
|
||||
let query;
|
||||
if (elem.search) {
|
||||
query = elem.search.slice(1).split('&');
|
||||
|
@ -192,8 +192,8 @@ export function injectParamIfMissing(path, param, value) {
|
|||
query = [];
|
||||
}
|
||||
|
||||
if (!query.some(v => v.startsWith(param_eq))) {
|
||||
query.push(param_eq + encodeURIComponent(value));
|
||||
if (!query.some(v => v.startsWith(paramEq))) {
|
||||
query.push(paramEq + encodeURIComponent(value));
|
||||
elem.search = "?" + query.join("&");
|
||||
}
|
||||
|
||||
|
|
|
@ -57,12 +57,12 @@ export default {
|
|||
/* eslint-enable comma-spacing */
|
||||
|
||||
decode(data, offset = 0) {
|
||||
let data_length = data.indexOf('=') - offset;
|
||||
if (data_length < 0) { data_length = data.length - offset; }
|
||||
let dataLength = data.indexOf('=') - offset;
|
||||
if (dataLength < 0) { dataLength = data.length - offset; }
|
||||
|
||||
/* Every four characters is 3 resulting numbers */
|
||||
const result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5);
|
||||
const result = new Array(result_length);
|
||||
const resultLength = (dataLength >> 2) * 3 + Math.floor((dataLength % 4) / 1.5);
|
||||
const result = new Array(resultLength);
|
||||
|
||||
// Convert one by one.
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Copyright (C) 2018 Samuel Mannehed for Cendio AB
|
||||
* Copyright (C) 2018 Pierre Ossman for Cendio AB
|
||||
* Copyright (C) 2019 The noVNC Authors
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Copyright (C) 2018 Samuel Mannehed for Cendio AB
|
||||
* Copyright (C) 2018 Pierre Ossman for Cendio AB
|
||||
* Copyright (C) 2019 The noVNC Authors
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
|
@ -19,10 +17,10 @@ export default class HextileDecoder {
|
|||
|
||||
decodeRect(x, y, width, height, sock, display, depth) {
|
||||
if (this._tiles === 0) {
|
||||
this._tiles_x = Math.ceil(width / 16);
|
||||
this._tiles_y = Math.ceil(height / 16);
|
||||
this._total_tiles = this._tiles_x * this._tiles_y;
|
||||
this._tiles = this._total_tiles;
|
||||
this._tilesX = Math.ceil(width / 16);
|
||||
this._tilesY = Math.ceil(height / 16);
|
||||
this._totalTiles = this._tilesX * this._tilesY;
|
||||
this._tiles = this._totalTiles;
|
||||
}
|
||||
|
||||
while (this._tiles > 0) {
|
||||
|
@ -41,11 +39,11 @@ export default class HextileDecoder {
|
|||
subencoding + ")");
|
||||
}
|
||||
|
||||
const curr_tile = this._total_tiles - this._tiles;
|
||||
const tile_x = curr_tile % this._tiles_x;
|
||||
const tile_y = Math.floor(curr_tile / this._tiles_x);
|
||||
const tx = x + tile_x * 16;
|
||||
const ty = y + tile_y * 16;
|
||||
const currTile = this._totalTiles - this._tiles;
|
||||
const tileX = currTile % this._tilesX;
|
||||
const tileY = Math.floor(currTile / this._tilesX);
|
||||
const tx = x + tileX * 16;
|
||||
const ty = y + tileY * 16;
|
||||
const tw = Math.min(16, (x + width) - tx);
|
||||
const th = Math.min(16, (y + height) - ty);
|
||||
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Copyright (C) 2018 Samuel Mannehed for Cendio AB
|
||||
* Copyright (C) 2018 Pierre Ossman for Cendio AB
|
||||
* Copyright (C) 2019 The noVNC Authors
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
|
@ -26,15 +24,15 @@ export default class RawDecoder {
|
|||
return false;
|
||||
}
|
||||
|
||||
const cur_y = y + (height - this._lines);
|
||||
const curr_height = Math.min(this._lines,
|
||||
const curY = y + (height - this._lines);
|
||||
const currHeight = Math.min(this._lines,
|
||||
Math.floor(sock.rQlen / bytesPerLine));
|
||||
let data = sock.rQ;
|
||||
let index = sock.rQi;
|
||||
|
||||
// Convert data if needed
|
||||
if (depth == 8) {
|
||||
const pixels = width * curr_height;
|
||||
const pixels = width * currHeight;
|
||||
const newdata = new Uint8Array(pixels * 4);
|
||||
for (let i = 0; i < pixels; i++) {
|
||||
newdata[i * 4 + 0] = ((data[index + i] >> 0) & 0x3) * 255 / 3;
|
||||
|
@ -46,9 +44,9 @@ export default class RawDecoder {
|
|||
index = 0;
|
||||
}
|
||||
|
||||
display.blitImage(x, cur_y, width, curr_height, data, index);
|
||||
sock.rQskipBytes(curr_height * bytesPerLine);
|
||||
this._lines -= curr_height;
|
||||
display.blitImage(x, curY, width, currHeight, data, index);
|
||||
sock.rQskipBytes(currHeight * bytesPerLine);
|
||||
this._lines -= currHeight;
|
||||
if (this._lines > 0) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Copyright (C) 2018 Samuel Mannehed for Cendio AB
|
||||
* Copyright (C) 2018 Pierre Ossman for Cendio AB
|
||||
* Copyright (C) 2019 The noVNC Authors
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Copyright (C) 2019 The noVNC Authors
|
||||
* (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
|
||||
* Copyright (C) 2018 Samuel Mannehed for Cendio AB
|
||||
* Copyright (C) 2018 Pierre Ossman for Cendio AB
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
|
@ -94,7 +92,7 @@ export default class TightDecoder {
|
|||
return false;
|
||||
}
|
||||
|
||||
display.imageRect(x, y, "image/jpeg", data);
|
||||
display.imageRect(x, y, width, height, "image/jpeg", data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -162,10 +160,9 @@ export default class TightDecoder {
|
|||
return false;
|
||||
}
|
||||
|
||||
data = this._zlibs[streamId].inflate(data, true, uncompressedSize);
|
||||
if (data.length != uncompressedSize) {
|
||||
throw new Error("Incomplete zlib block");
|
||||
}
|
||||
this._zlibs[streamId].setInput(data);
|
||||
data = this._zlibs[streamId].inflate(uncompressedSize);
|
||||
this._zlibs[streamId].setInput(null);
|
||||
}
|
||||
|
||||
display.blitRgbImage(x, y, width, height, data, 0, false);
|
||||
|
@ -210,10 +207,9 @@ export default class TightDecoder {
|
|||
return false;
|
||||
}
|
||||
|
||||
data = this._zlibs[streamId].inflate(data, true, uncompressedSize);
|
||||
if (data.length != uncompressedSize) {
|
||||
throw new Error("Incomplete zlib block");
|
||||
}
|
||||
this._zlibs[streamId].setInput(data);
|
||||
data = this._zlibs[streamId].inflate(uncompressedSize);
|
||||
this._zlibs[streamId].setInput(null);
|
||||
}
|
||||
|
||||
// Convert indexed (palette based) image data to RGB
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Copyright (C) 2018 Samuel Mannehed for Cendio AB
|
||||
* Copyright (C) 2018 Pierre Ossman for Cendio AB
|
||||
* Copyright (C) 2019 The noVNC Authors
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
|
@ -18,7 +16,7 @@ export default class TightPNGDecoder extends TightDecoder {
|
|||
return false;
|
||||
}
|
||||
|
||||
display.imageRect(x, y, "image/png", data);
|
||||
display.imageRect(x, y, width, height, "image/png", data);
|
||||
|
||||
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
|
||||
* Copyright (C) 2018 The noVNC Authors
|
||||
* Copyright (C) 2019 The noVNC Authors
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
|
@ -9,24 +9,24 @@
|
|||
import * as Log from './util/logging.js';
|
||||
import Base64 from "./base64.js";
|
||||
import { supportsImageMetadata } from './util/browser.js';
|
||||
import { toSigned32bit } from './util/int.js';
|
||||
|
||||
export default class Display {
|
||||
constructor(target) {
|
||||
this._drawCtx = null;
|
||||
this._c_forceCanvas = false;
|
||||
|
||||
this._renderQ = []; // queue drawing actions for in-oder rendering
|
||||
this._flushing = false;
|
||||
|
||||
// the full frame buffer (logical canvas) size
|
||||
this._fb_width = 0;
|
||||
this._fb_height = 0;
|
||||
this._fbWidth = 0;
|
||||
this._fbHeight = 0;
|
||||
|
||||
this._prevDrawStyle = "";
|
||||
this._tile = null;
|
||||
this._tile16x16 = null;
|
||||
this._tile_x = 0;
|
||||
this._tile_y = 0;
|
||||
this._tileX = 0;
|
||||
this._tileY = 0;
|
||||
|
||||
Log.Debug(">> Display.constructor");
|
||||
|
||||
|
@ -60,8 +60,6 @@ export default class Display {
|
|||
|
||||
Log.Debug("User Agent: " + navigator.userAgent);
|
||||
|
||||
this.clear();
|
||||
|
||||
// Check canvas features
|
||||
if (!('createImageData' in this._drawCtx)) {
|
||||
throw new Error("Canvas does not support createImageData");
|
||||
|
@ -74,7 +72,6 @@ export default class Display {
|
|||
|
||||
this._scale = 1.0;
|
||||
this._clipViewport = false;
|
||||
this.logo = null;
|
||||
|
||||
// ===== EVENT HANDLERS =====
|
||||
|
||||
|
@ -98,11 +95,11 @@ export default class Display {
|
|||
}
|
||||
|
||||
get width() {
|
||||
return this._fb_width;
|
||||
return this._fbWidth;
|
||||
}
|
||||
|
||||
get height() {
|
||||
return this._fb_height;
|
||||
return this._fbHeight;
|
||||
}
|
||||
|
||||
// ===== PUBLIC METHODS =====
|
||||
|
@ -125,15 +122,15 @@ export default class Display {
|
|||
if (deltaX < 0 && vp.x + deltaX < 0) {
|
||||
deltaX = -vp.x;
|
||||
}
|
||||
if (vx2 + deltaX >= this._fb_width) {
|
||||
deltaX -= vx2 + deltaX - this._fb_width + 1;
|
||||
if (vx2 + deltaX >= this._fbWidth) {
|
||||
deltaX -= vx2 + deltaX - this._fbWidth + 1;
|
||||
}
|
||||
|
||||
if (vp.y + deltaY < 0) {
|
||||
deltaY = -vp.y;
|
||||
}
|
||||
if (vy2 + deltaY >= this._fb_height) {
|
||||
deltaY -= (vy2 + deltaY - this._fb_height + 1);
|
||||
if (vy2 + deltaY >= this._fbHeight) {
|
||||
deltaY -= (vy2 + deltaY - this._fbHeight + 1);
|
||||
}
|
||||
|
||||
if (deltaX === 0 && deltaY === 0) {
|
||||
|
@ -156,18 +153,18 @@ export default class Display {
|
|||
typeof(height) === "undefined") {
|
||||
|
||||
Log.Debug("Setting viewport to full display region");
|
||||
width = this._fb_width;
|
||||
height = this._fb_height;
|
||||
width = this._fbWidth;
|
||||
height = this._fbHeight;
|
||||
}
|
||||
|
||||
width = Math.floor(width);
|
||||
height = Math.floor(height);
|
||||
|
||||
if (width > this._fb_width) {
|
||||
width = this._fb_width;
|
||||
if (width > this._fbWidth) {
|
||||
width = this._fbWidth;
|
||||
}
|
||||
if (height > this._fb_height) {
|
||||
height = this._fb_height;
|
||||
if (height > this._fbHeight) {
|
||||
height = this._fbHeight;
|
||||
}
|
||||
|
||||
const vp = this._viewportLoc;
|
||||
|
@ -194,21 +191,21 @@ export default class Display {
|
|||
if (this._scale === 0) {
|
||||
return 0;
|
||||
}
|
||||
return x / this._scale + this._viewportLoc.x;
|
||||
return toSigned32bit(x / this._scale + this._viewportLoc.x);
|
||||
}
|
||||
|
||||
absY(y) {
|
||||
if (this._scale === 0) {
|
||||
return 0;
|
||||
}
|
||||
return y / this._scale + this._viewportLoc.y;
|
||||
return toSigned32bit(y / this._scale + this._viewportLoc.y);
|
||||
}
|
||||
|
||||
resize(width, height) {
|
||||
this._prevDrawStyle = "";
|
||||
|
||||
this._fb_width = width;
|
||||
this._fb_height = height;
|
||||
this._fbWidth = width;
|
||||
this._fbHeight = height;
|
||||
|
||||
const canvas = this._backbuffer;
|
||||
if (canvas.width !== width || canvas.height !== height) {
|
||||
|
@ -256,9 +253,9 @@ export default class Display {
|
|||
|
||||
// Update the visible canvas with the contents of the
|
||||
// rendering canvas
|
||||
flip(from_queue) {
|
||||
if (this._renderQ.length !== 0 && !from_queue) {
|
||||
this._renderQ_push({
|
||||
flip(fromQueue) {
|
||||
if (this._renderQ.length !== 0 && !fromQueue) {
|
||||
this._renderQPush({
|
||||
'type': 'flip'
|
||||
});
|
||||
} else {
|
||||
|
@ -302,17 +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() {
|
||||
return this._renderQ.length > 0;
|
||||
}
|
||||
|
@ -325,9 +311,9 @@ export default class Display {
|
|||
}
|
||||
}
|
||||
|
||||
fillRect(x, y, width, height, color, from_queue) {
|
||||
if (this._renderQ.length !== 0 && !from_queue) {
|
||||
this._renderQ_push({
|
||||
fillRect(x, y, width, height, color, fromQueue) {
|
||||
if (this._renderQ.length !== 0 && !fromQueue) {
|
||||
this._renderQPush({
|
||||
'type': 'fill',
|
||||
'x': x,
|
||||
'y': y,
|
||||
|
@ -342,14 +328,14 @@ export default class Display {
|
|||
}
|
||||
}
|
||||
|
||||
copyImage(old_x, old_y, new_x, new_y, w, h, from_queue) {
|
||||
if (this._renderQ.length !== 0 && !from_queue) {
|
||||
this._renderQ_push({
|
||||
copyImage(oldX, oldY, newX, newY, w, h, fromQueue) {
|
||||
if (this._renderQ.length !== 0 && !fromQueue) {
|
||||
this._renderQPush({
|
||||
'type': 'copy',
|
||||
'old_x': old_x,
|
||||
'old_y': old_y,
|
||||
'x': new_x,
|
||||
'y': new_y,
|
||||
'oldX': oldX,
|
||||
'oldY': oldY,
|
||||
'x': newX,
|
||||
'y': newY,
|
||||
'width': w,
|
||||
'height': h,
|
||||
});
|
||||
|
@ -367,27 +353,35 @@ export default class Display {
|
|||
this._drawCtx.imageSmoothingEnabled = false;
|
||||
|
||||
this._drawCtx.drawImage(this._backbuffer,
|
||||
old_x, old_y, w, h,
|
||||
new_x, new_y, w, h);
|
||||
this._damage(new_x, new_y, w, h);
|
||||
oldX, oldY, w, h,
|
||||
newX, newY, w, h);
|
||||
this._damage(newX, newY, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
imageRect(x, y, mime, arr) {
|
||||
imageRect(x, y, width, height, mime, arr) {
|
||||
/* The internal logic cannot handle empty images, so bail early */
|
||||
if ((width === 0) || (height === 0)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const img = new Image();
|
||||
img.src = "data: " + mime + ";base64," + Base64.encode(arr);
|
||||
this._renderQ_push({
|
||||
|
||||
this._renderQPush({
|
||||
'type': 'img',
|
||||
'img': img,
|
||||
'x': x,
|
||||
'y': y
|
||||
'y': y,
|
||||
'width': width,
|
||||
'height': height
|
||||
});
|
||||
}
|
||||
|
||||
// start updating a tile
|
||||
startTile(x, y, width, height, color) {
|
||||
this._tile_x = x;
|
||||
this._tile_y = y;
|
||||
this._tileX = x;
|
||||
this._tileY = y;
|
||||
if (width === 16 && height === 16) {
|
||||
this._tile = this._tile16x16;
|
||||
} else {
|
||||
|
@ -430,21 +424,21 @@ export default class Display {
|
|||
|
||||
// draw the current tile to the screen
|
||||
finishTile() {
|
||||
this._drawCtx.putImageData(this._tile, this._tile_x, this._tile_y);
|
||||
this._damage(this._tile_x, this._tile_y,
|
||||
this._drawCtx.putImageData(this._tile, this._tileX, this._tileY);
|
||||
this._damage(this._tileX, this._tileY,
|
||||
this._tile.width, this._tile.height);
|
||||
}
|
||||
|
||||
blitImage(x, y, width, height, arr, offset, from_queue) {
|
||||
if (this._renderQ.length !== 0 && !from_queue) {
|
||||
blitImage(x, y, width, height, arr, offset, fromQueue) {
|
||||
if (this._renderQ.length !== 0 && !fromQueue) {
|
||||
// NB(directxman12): it's technically more performant here to use preallocated arrays,
|
||||
// but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
|
||||
// this probably isn't getting called *nearly* as much
|
||||
const new_arr = new Uint8Array(width * height * 4);
|
||||
new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
|
||||
this._renderQ_push({
|
||||
const newArr = new Uint8Array(width * height * 4);
|
||||
newArr.set(new Uint8Array(arr.buffer, 0, newArr.length));
|
||||
this._renderQPush({
|
||||
'type': 'blit',
|
||||
'data': new_arr,
|
||||
'data': newArr,
|
||||
'x': x,
|
||||
'y': y,
|
||||
'width': width,
|
||||
|
@ -455,16 +449,16 @@ export default class Display {
|
|||
}
|
||||
}
|
||||
|
||||
blitRgbImage(x, y, width, height, arr, offset, from_queue) {
|
||||
if (this._renderQ.length !== 0 && !from_queue) {
|
||||
blitRgbImage(x, y, width, height, arr, offset, fromQueue) {
|
||||
if (this._renderQ.length !== 0 && !fromQueue) {
|
||||
// NB(directxman12): it's technically more performant here to use preallocated arrays,
|
||||
// but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
|
||||
// this probably isn't getting called *nearly* as much
|
||||
const new_arr = new Uint8Array(width * height * 3);
|
||||
new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
|
||||
this._renderQ_push({
|
||||
const newArr = new Uint8Array(width * height * 3);
|
||||
newArr.set(new Uint8Array(arr.buffer, 0, newArr.length));
|
||||
this._renderQPush({
|
||||
'type': 'blitRgb',
|
||||
'data': new_arr,
|
||||
'data': newArr,
|
||||
'x': x,
|
||||
'y': y,
|
||||
'width': width,
|
||||
|
@ -475,16 +469,16 @@ export default class Display {
|
|||
}
|
||||
}
|
||||
|
||||
blitRgbxImage(x, y, width, height, arr, offset, from_queue) {
|
||||
if (this._renderQ.length !== 0 && !from_queue) {
|
||||
blitRgbxImage(x, y, width, height, arr, offset, fromQueue) {
|
||||
if (this._renderQ.length !== 0 && !fromQueue) {
|
||||
// NB(directxman12): it's technically more performant here to use preallocated arrays,
|
||||
// but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
|
||||
// this probably isn't getting called *nearly* as much
|
||||
const new_arr = new Uint8Array(width * height * 4);
|
||||
new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
|
||||
this._renderQ_push({
|
||||
const newArr = new Uint8Array(width * height * 4);
|
||||
newArr.set(new Uint8Array(arr.buffer, 0, newArr.length));
|
||||
this._renderQPush({
|
||||
'type': 'blitRgbx',
|
||||
'data': new_arr,
|
||||
'data': newArr,
|
||||
'x': x,
|
||||
'y': y,
|
||||
'width': width,
|
||||
|
@ -589,23 +583,23 @@ export default class Display {
|
|||
this._damage(x, y, img.width, img.height);
|
||||
}
|
||||
|
||||
_renderQ_push(action) {
|
||||
_renderQPush(action) {
|
||||
this._renderQ.push(action);
|
||||
if (this._renderQ.length === 1) {
|
||||
// If this can be rendered immediately it will be, otherwise
|
||||
// the scanner will wait for the relevant event
|
||||
this._scan_renderQ();
|
||||
this._scanRenderQ();
|
||||
}
|
||||
}
|
||||
|
||||
_resume_renderQ() {
|
||||
_resumeRenderQ() {
|
||||
// "this" is the object that is ready, not the
|
||||
// display object
|
||||
this.removeEventListener('load', this._noVNC_display._resume_renderQ);
|
||||
this._noVNC_display._scan_renderQ();
|
||||
this.removeEventListener('load', this._noVNCDisplay._resumeRenderQ);
|
||||
this._noVNCDisplay._scanRenderQ();
|
||||
}
|
||||
|
||||
_scan_renderQ() {
|
||||
_scanRenderQ() {
|
||||
let ready = true;
|
||||
while (ready && this._renderQ.length > 0) {
|
||||
const a = this._renderQ[0];
|
||||
|
@ -614,7 +608,7 @@ export default class Display {
|
|||
this.flip(true);
|
||||
break;
|
||||
case 'copy':
|
||||
this.copyImage(a.old_x, a.old_y, a.x, a.y, a.width, a.height, true);
|
||||
this.copyImage(a.oldX, a.oldY, a.x, a.y, a.width, a.height, true);
|
||||
break;
|
||||
case 'fill':
|
||||
this.fillRect(a.x, a.y, a.width, a.height, a.color, true);
|
||||
|
@ -629,11 +623,18 @@ export default class Display {
|
|||
this.blitRgbxImage(a.x, a.y, a.width, a.height, a.data, 0, true);
|
||||
break;
|
||||
case 'img':
|
||||
if (a.img.complete) {
|
||||
/* IE tends to set "complete" prematurely, so check dimensions */
|
||||
if (a.img.complete && (a.img.width !== 0) && (a.img.height !== 0)) {
|
||||
if (a.img.width !== a.width || a.img.height !== a.height) {
|
||||
Log.Error("Decoded image has incorrect dimensions. Got " +
|
||||
a.img.width + "x" + a.img.height + ". Expected " +
|
||||
a.width + "x" + a.height + ".");
|
||||
return;
|
||||
}
|
||||
this.drawImage(a.img, a.x, a.y);
|
||||
} else {
|
||||
a.img._noVNC_display = this;
|
||||
a.img.addEventListener('load', this._resume_renderQ);
|
||||
a.img._noVNCDisplay = this;
|
||||
a.img.addEventListener('load', this._resumeRenderQ);
|
||||
// We need to wait for this image to 'load'
|
||||
// to keep things in-order
|
||||
ready = false;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2018 The noVNC Authors
|
||||
* Copyright (C) 2019 The noVNC Authors
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
|
@ -20,12 +20,15 @@ export const encodings = {
|
|||
pseudoEncodingLastRect: -224,
|
||||
pseudoEncodingCursor: -239,
|
||||
pseudoEncodingQEMUExtendedKeyEvent: -258,
|
||||
pseudoEncodingDesktopName: -307,
|
||||
pseudoEncodingExtendedDesktopSize: -308,
|
||||
pseudoEncodingXvp: -309,
|
||||
pseudoEncodingFence: -312,
|
||||
pseudoEncodingContinuousUpdates: -313,
|
||||
pseudoEncodingCompressLevel9: -247,
|
||||
pseudoEncodingCompressLevel0: -256,
|
||||
pseudoEncodingVMwareCursor: 0x574d5664,
|
||||
pseudoEncodingExtendedClipboard: 0xc0a1e5ce
|
||||
};
|
||||
|
||||
export function encodingName(num) {
|
||||
|
|
|
@ -1,3 +1,11 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2020 The noVNC Authors
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
import { inflateInit, inflate, inflateReset } from "../vendor/pako/lib/zlib/inflate.js";
|
||||
import ZStream from "../vendor/pako/lib/zlib/zstream.js";
|
||||
|
||||
|
@ -11,12 +19,22 @@ export default class Inflate {
|
|||
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.avail_in = this.strm.input.length;
|
||||
this.strm.next_in = 0;
|
||||
this.strm.next_out = 0;
|
||||
/* eslint-enable camelcase */
|
||||
}
|
||||
}
|
||||
|
||||
inflate(expected) {
|
||||
// resize our output buffer if it's too small
|
||||
// (we could just use multiple chunks, but that would cause an extra
|
||||
// allocation each time to flatten the chunks)
|
||||
|
@ -25,9 +43,19 @@ export default class Inflate {
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -43,12 +43,10 @@ addStandard("CapsLock", KeyTable.XK_Caps_Lock);
|
|||
addLeftRight("Control", KeyTable.XK_Control_L, KeyTable.XK_Control_R);
|
||||
// - Fn
|
||||
// - FnLock
|
||||
addLeftRight("Hyper", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
|
||||
addLeftRight("Meta", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
|
||||
addStandard("NumLock", KeyTable.XK_Num_Lock);
|
||||
addStandard("ScrollLock", KeyTable.XK_Scroll_Lock);
|
||||
addLeftRight("Shift", KeyTable.XK_Shift_L, KeyTable.XK_Shift_R);
|
||||
addLeftRight("Super", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
|
||||
// - Symbol
|
||||
// - SymbolLock
|
||||
|
||||
|
@ -72,6 +70,9 @@ addNumpad("PageUp", KeyTable.XK_Prior, KeyTable.XK_KP_Prior);
|
|||
// 2.5. Editing Keys
|
||||
|
||||
addStandard("Backspace", KeyTable.XK_BackSpace);
|
||||
// Browsers send "Clear" for the numpad 5 without NumLock because
|
||||
// Windows uses VK_Clear for that key. But Unix expects KP_Begin for
|
||||
// that scenario.
|
||||
addNumpad("Clear", KeyTable.XK_Clear, KeyTable.XK_KP_Begin);
|
||||
addStandard("Copy", KeyTable.XF86XK_Copy);
|
||||
// - CrSel
|
||||
|
@ -194,7 +195,8 @@ addStandard("F35", KeyTable.XK_F35);
|
|||
addStandard("Close", KeyTable.XF86XK_Close);
|
||||
addStandard("MailForward", KeyTable.XF86XK_MailForward);
|
||||
addStandard("MailReply", KeyTable.XF86XK_Reply);
|
||||
addStandard("MainSend", KeyTable.XF86XK_Send);
|
||||
addStandard("MailSend", KeyTable.XF86XK_Send);
|
||||
// - MediaClose
|
||||
addStandard("MediaFastForward", KeyTable.XF86XK_AudioForward);
|
||||
addStandard("MediaPause", KeyTable.XF86XK_AudioPause);
|
||||
addStandard("MediaPlay", KeyTable.XF86XK_AudioPlay);
|
||||
|
@ -218,11 +220,9 @@ addStandard("SpellCheck", KeyTable.XF86XK_Spell);
|
|||
|
||||
// - AudioBalanceLeft
|
||||
// - AudioBalanceRight
|
||||
// - AudioBassDown
|
||||
// - AudioBassBoostDown
|
||||
// - AudioBassBoostToggle
|
||||
// - AudioBassBoostUp
|
||||
// - AudioBassUp
|
||||
// - AudioFaderFront
|
||||
// - AudioFaderRear
|
||||
// - AudioSurroundModeNext
|
||||
|
@ -243,12 +243,12 @@ addStandard("MicrophoneVolumeMute", KeyTable.XF86XK_AudioMicMute);
|
|||
|
||||
// 2.14. Application Keys
|
||||
|
||||
addStandard("LaunchCalculator", KeyTable.XF86XK_Calculator);
|
||||
addStandard("LaunchApplication1", KeyTable.XF86XK_MyComputer);
|
||||
addStandard("LaunchApplication2", KeyTable.XF86XK_Calculator);
|
||||
addStandard("LaunchCalendar", KeyTable.XF86XK_Calendar);
|
||||
addStandard("LaunchMail", KeyTable.XF86XK_Mail);
|
||||
addStandard("LaunchMediaPlayer", KeyTable.XF86XK_AudioMedia);
|
||||
addStandard("LaunchMusicPlayer", KeyTable.XF86XK_Music);
|
||||
addStandard("LaunchMyComputer", KeyTable.XF86XK_MyComputer);
|
||||
addStandard("LaunchPhone", KeyTable.XF86XK_Phone);
|
||||
addStandard("LaunchScreenSaver", KeyTable.XF86XK_ScreenSaver);
|
||||
addStandard("LaunchSpreadsheet", KeyTable.XF86XK_Excel);
|
||||
|
|
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
|
||||
* Copyright (C) 2018 The noVNC Authors
|
||||
* Copyright (C) 2019 The noVNC Authors
|
||||
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||
*/
|
||||
|
||||
|
@ -118,9 +118,7 @@ export default class Keyboard {
|
|||
|
||||
// We cannot handle keys we cannot track, but we also need
|
||||
// to deal with virtual keyboards which omit key info
|
||||
// (iOS omits tracking info on keyup events, which forces us to
|
||||
// special treat that platform here)
|
||||
if ((code === 'Unidentified') || browser.isIOS()) {
|
||||
if (code === 'Unidentified') {
|
||||
if (keysym) {
|
||||
// If it's a virtual keyboard then it should be
|
||||
// sufficient to just send press and release right
|
||||
|
@ -137,7 +135,7 @@ export default class Keyboard {
|
|||
// keys around a bit to make things more sane for the remote
|
||||
// server. This method is used by RealVNC and TigerVNC (and
|
||||
// possibly others).
|
||||
if (browser.isMac()) {
|
||||
if (browser.isMac() || browser.isIOS()) {
|
||||
switch (keysym) {
|
||||
case KeyTable.XK_Super_L:
|
||||
keysym = KeyTable.XK_Alt_L;
|
||||
|
@ -164,7 +162,7 @@ export default class Keyboard {
|
|||
// state change events. That gets extra confusing for CapsLock
|
||||
// which toggles on each press, but not on release. So pretend
|
||||
// it was a quick press and release of the button.
|
||||
if (browser.isMac() && (code === 'CapsLock')) {
|
||||
if ((browser.isMac() || browser.isIOS()) && (code === 'CapsLock')) {
|
||||
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);
|
||||
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);
|
||||
stopEvent(e);
|
||||
|
@ -276,13 +274,28 @@ export default class Keyboard {
|
|||
}
|
||||
|
||||
// See comment in _handleKeyDown()
|
||||
if (browser.isMac() && (code === 'CapsLock')) {
|
||||
if ((browser.isMac() || browser.isIOS()) && (code === 'CapsLock')) {
|
||||
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);
|
||||
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);
|
||||
return;
|
||||
}
|
||||
|
||||
this._sendKeyEvent(this._keyDownList[code], code, false);
|
||||
|
||||
// Windows has a rather nasty bug where it won't send key
|
||||
// release events for a Shift button if the other Shift is still
|
||||
// pressed
|
||||
if (browser.isWindows() && ((code === 'ShiftLeft') ||
|
||||
(code === 'ShiftRight'))) {
|
||||
if ('ShiftRight' in this._keyDownList) {
|
||||
this._sendKeyEvent(this._keyDownList['ShiftRight'],
|
||||
'ShiftRight', false);
|
||||
}
|
||||
if ('ShiftLeft' in this._keyDownList) {
|
||||
this._sendKeyEvent(this._keyDownList['ShiftLeft'],
|
||||
'ShiftLeft', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_handleAltGrTimeout() {
|
||||
|
@ -299,8 +312,11 @@ export default class Keyboard {
|
|||
Log.Debug("<< Keyboard.allKeysUp");
|
||||
}
|
||||
|
||||
// Firefox Alt workaround, see below
|
||||
// Alt workaround for Firefox on Windows, see below
|
||||
_checkAlt(e) {
|
||||
if (e.skipCheckAlt) {
|
||||
return;
|
||||
}
|
||||
if (e.altKey) {
|
||||
return;
|
||||
}
|
||||
|
@ -315,6 +331,7 @@ export default class Keyboard {
|
|||
const event = new KeyboardEvent('keyup',
|
||||
{ key: downList[code],
|
||||
code: code });
|
||||
event.skipCheckAlt = true;
|
||||
target.dispatchEvent(event);
|
||||
});
|
||||
}
|
||||
|
@ -331,9 +348,10 @@ export default class Keyboard {
|
|||
// Release (key up) if window loses focus
|
||||
window.addEventListener('blur', this._eventHandlers.blur);
|
||||
|
||||
// Firefox has broken handling of Alt, so we need to poll as
|
||||
// best we can for releases (still doesn't prevent the menu
|
||||
// from popping up though as we can't call preventDefault())
|
||||
// Firefox on Windows has broken handling of Alt, so we need to
|
||||
// poll as best we can for releases (still doesn't prevent the
|
||||
// menu from popping up though as we can't call
|
||||
// preventDefault())
|
||||
if (browser.isWindows() && browser.isFirefox()) {
|
||||
const handler = this._eventHandlers.checkalt;
|
||||
['mousedown', 'mouseup', 'mousemove', 'wheel',
|
||||
|
|
|
@ -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 vkeys from "./vkeys.js";
|
||||
import fixedkeys from "./fixedkeys.js";
|
||||
|
@ -91,6 +92,8 @@ export function getKey(evt) {
|
|||
// Mozilla isn't fully in sync with the spec yet
|
||||
switch (evt.key) {
|
||||
case 'OS': return 'Meta';
|
||||
case 'LaunchMyComputer': return 'LaunchApplication1';
|
||||
case 'LaunchCalculator': return 'LaunchApplication2';
|
||||
}
|
||||
|
||||
// iOS leaks some OS names
|
||||
|
@ -102,9 +105,21 @@ export function getKey(evt) {
|
|||
case 'UIKeyInputEscape': return 'Escape';
|
||||
}
|
||||
|
||||
// IE and Edge have broken handling of AltGraph so we cannot
|
||||
// trust them for printable characters
|
||||
if ((evt.key.length !== 1) || (!browser.isIE() && !browser.isEdge())) {
|
||||
// Broken behaviour in Chrome
|
||||
if ((evt.key === '\x00') && (evt.code === 'NumpadDecimal')) {
|
||||
return 'Delete';
|
||||
}
|
||||
|
||||
// IE and Edge need special handling, but for everyone else we
|
||||
// can trust the value provided
|
||||
if (!browser.isIE() && !browser.isEdge()) {
|
||||
return evt.key;
|
||||
}
|
||||
|
||||
// IE and Edge have broken handling of AltGraph so we can only
|
||||
// trust them for non-printable characters (and unfortunately
|
||||
// they also specify 'Unidentified' for some problem keys)
|
||||
if ((evt.key.length !== 1) && (evt.key !== 'Unidentified')) {
|
||||
return evt.key;
|
||||
}
|
||||
}
|
||||
|
@ -141,10 +156,39 @@ export function getKeysym(evt) {
|
|||
location = 2;
|
||||
}
|
||||
|
||||
// And for Clear
|
||||
if ((key === 'Clear') && (location === 3)) {
|
||||
let code = getKeycode(evt);
|
||||
if (code === 'NumLock') {
|
||||
location = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if ((location === undefined) || (location > 3)) {
|
||||
location = 0;
|
||||
}
|
||||
|
||||
// The original Meta key now gets confused with the Windows key
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=1020141
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1232918
|
||||
if (key === 'Meta') {
|
||||
let code = getKeycode(evt);
|
||||
if (code === 'AltLeft') {
|
||||
return KeyTable.XK_Meta_L;
|
||||
} else if (code === 'AltRight') {
|
||||
return KeyTable.XK_Meta_R;
|
||||
}
|
||||
}
|
||||
|
||||
// macOS has Clear instead of NumLock, but the remote system is
|
||||
// probably not macOS, so lying here is probably best...
|
||||
if (key === 'Clear') {
|
||||
let code = getKeycode(evt);
|
||||
if (code === 'NumLock') {
|
||||
return KeyTable.XK_Num_Lock;
|
||||
}
|
||||
}
|
||||
|
||||
return DOMKeyTable[key][location];
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,9 +1,11 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2018 The noVNC Authors
|
||||
* Copyright (C) 2019 The noVNC Authors
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*
|
||||
* Browser feature support detection
|
||||
*/
|
||||
|
||||
import * as Log from './logging.js';
|
||||
|
@ -31,7 +33,7 @@ try {
|
|||
const target = document.createElement('canvas');
|
||||
target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
|
||||
|
||||
if (target.style.cursor) {
|
||||
if (target.style.cursor.indexOf("url") === 0) {
|
||||
Log.Info("Data URI scheme cursor supported");
|
||||
_supportsCursorURIs = true;
|
||||
} else {
|
||||
|
@ -52,6 +54,38 @@ try {
|
|||
}
|
||||
export const supportsImageMetadata = _supportsImageMetadata;
|
||||
|
||||
let _hasScrollbarGutter = true;
|
||||
try {
|
||||
// Create invisible container
|
||||
const container = document.createElement('div');
|
||||
container.style.visibility = 'hidden';
|
||||
container.style.overflow = 'scroll'; // forcing scrollbars
|
||||
document.body.appendChild(container);
|
||||
|
||||
// Create a div and place it in the container
|
||||
const child = document.createElement('div');
|
||||
container.appendChild(child);
|
||||
|
||||
// Calculate the difference between the container's full width
|
||||
// and the child's width - the difference is the scrollbars
|
||||
const scrollbarWidth = (container.offsetWidth - child.offsetWidth);
|
||||
|
||||
// Clean up
|
||||
container.parentNode.removeChild(container);
|
||||
|
||||
_hasScrollbarGutter = scrollbarWidth != 0;
|
||||
} catch (exc) {
|
||||
Log.Error("Scrollbar test exception: " + exc);
|
||||
}
|
||||
export const hasScrollbarGutter = _hasScrollbarGutter;
|
||||
|
||||
/*
|
||||
* The functions for detection of platforms and browsers below are exported
|
||||
* but the use of these should be minimized as much as possible.
|
||||
*
|
||||
* It's better to use feature detection than platform detection.
|
||||
*/
|
||||
|
||||
export function isMac() {
|
||||
return navigator && !!(/mac/i).exec(navigator.platform);
|
||||
}
|
||||
|
@ -67,10 +101,6 @@ export function isIOS() {
|
|||
!!(/ipod/i).exec(navigator.platform));
|
||||
}
|
||||
|
||||
export function isAndroid() {
|
||||
return navigator && !!(/android/i).exec(navigator.userAgent);
|
||||
}
|
||||
|
||||
export function isSafari() {
|
||||
return navigator && (navigator.userAgent.indexOf('Safari') !== -1 &&
|
||||
navigator.userAgent.indexOf('Chrome') === -1);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2018 The noVNC Authors
|
||||
* Copyright (C) 2019 The noVNC Authors
|
||||
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||
*/
|
||||
|
||||
|
@ -20,7 +20,6 @@ export default class Cursor {
|
|||
this._canvas.style.pointerEvents = 'none';
|
||||
// Can't use "display" because of Firefox bug #1445997
|
||||
this._canvas.style.visibility = 'hidden';
|
||||
document.body.appendChild(this._canvas);
|
||||
}
|
||||
|
||||
this._position = { x: 0, y: 0 };
|
||||
|
@ -31,9 +30,6 @@ export default class Cursor {
|
|||
'mouseleave': this._handleMouseLeave.bind(this),
|
||||
'mousemove': this._handleMouseMove.bind(this),
|
||||
'mouseup': this._handleMouseUp.bind(this),
|
||||
'touchstart': this._handleTouchStart.bind(this),
|
||||
'touchmove': this._handleTouchMove.bind(this),
|
||||
'touchend': this._handleTouchEnd.bind(this),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -45,6 +41,8 @@ export default class Cursor {
|
|||
this._target = target;
|
||||
|
||||
if (useFallback) {
|
||||
document.body.appendChild(this._canvas);
|
||||
|
||||
// FIXME: These don't fire properly except for mouse
|
||||
/// movement in IE. We want to also capture element
|
||||
// movement, size changes, visibility, etc.
|
||||
|
@ -53,17 +51,16 @@ export default class Cursor {
|
|||
this._target.addEventListener('mouseleave', this._eventHandlers.mouseleave, options);
|
||||
this._target.addEventListener('mousemove', this._eventHandlers.mousemove, options);
|
||||
this._target.addEventListener('mouseup', this._eventHandlers.mouseup, options);
|
||||
|
||||
// There is no "touchleave" so we monitor touchstart globally
|
||||
window.addEventListener('touchstart', this._eventHandlers.touchstart, options);
|
||||
this._target.addEventListener('touchmove', this._eventHandlers.touchmove, options);
|
||||
this._target.addEventListener('touchend', this._eventHandlers.touchend, options);
|
||||
}
|
||||
|
||||
this.clear();
|
||||
}
|
||||
|
||||
detach() {
|
||||
if (!this._target) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (useFallback) {
|
||||
const options = { capture: true, passive: true };
|
||||
this._target.removeEventListener('mouseover', this._eventHandlers.mouseover, options);
|
||||
|
@ -71,9 +68,7 @@ export default class Cursor {
|
|||
this._target.removeEventListener('mousemove', this._eventHandlers.mousemove, options);
|
||||
this._target.removeEventListener('mouseup', this._eventHandlers.mouseup, options);
|
||||
|
||||
window.removeEventListener('touchstart', this._eventHandlers.touchstart, options);
|
||||
this._target.removeEventListener('touchmove', this._eventHandlers.touchmove, options);
|
||||
this._target.removeEventListener('touchend', this._eventHandlers.touchend, options);
|
||||
document.body.removeChild(this._canvas);
|
||||
}
|
||||
|
||||
this._target = null;
|
||||
|
@ -124,6 +119,27 @@ export default class Cursor {
|
|||
this._hotSpot.y = 0;
|
||||
}
|
||||
|
||||
// Mouse events might be emulated, this allows
|
||||
// moving the cursor in such cases
|
||||
move(clientX, clientY) {
|
||||
if (!useFallback) {
|
||||
return;
|
||||
}
|
||||
// clientX/clientY are relative the _visual viewport_,
|
||||
// but our position is relative the _layout viewport_,
|
||||
// so try to compensate when we can
|
||||
if (window.visualViewport) {
|
||||
this._position.x = clientX + window.visualViewport.offsetLeft;
|
||||
this._position.y = clientY + window.visualViewport.offsetTop;
|
||||
} else {
|
||||
this._position.x = clientX;
|
||||
this._position.y = clientY;
|
||||
}
|
||||
this._updatePosition();
|
||||
let target = document.elementFromPoint(clientX, clientY);
|
||||
this._updateVisibility(target);
|
||||
}
|
||||
|
||||
_handleMouseOver(event) {
|
||||
// This event could be because we're entering the target, or
|
||||
// moving around amongst its sub elements. Let the move handler
|
||||
|
@ -132,7 +148,8 @@ export default class Cursor {
|
|||
}
|
||||
|
||||
_handleMouseLeave(event) {
|
||||
this._hideCursor();
|
||||
// Check if we should show the cursor on the element we are leaving to
|
||||
this._updateVisibility(event.relatedTarget);
|
||||
}
|
||||
|
||||
_handleMouseMove(event) {
|
||||
|
@ -150,27 +167,29 @@ export default class Cursor {
|
|||
// now and adjust visibility based on that.
|
||||
let target = document.elementFromPoint(event.clientX, event.clientY);
|
||||
this._updateVisibility(target);
|
||||
|
||||
// Captures end with a mouseup but we can't know the event order of
|
||||
// mouseup vs releaseCapture.
|
||||
//
|
||||
// In the cases when releaseCapture comes first, the code above is
|
||||
// enough.
|
||||
//
|
||||
// In the cases when the mouseup comes first, we need wait for the
|
||||
// browser to flush all events and then check again if the cursor
|
||||
// should be visible.
|
||||
if (this._captureIsActive()) {
|
||||
window.setTimeout(() => {
|
||||
// We might have detached at this point
|
||||
if (!this._target) {
|
||||
return;
|
||||
}
|
||||
|
||||
_handleTouchStart(event) {
|
||||
// Just as for mouseover, we let the move handler deal with it
|
||||
this._handleTouchMove(event);
|
||||
}
|
||||
|
||||
_handleTouchMove(event) {
|
||||
this._updateVisibility(event.target);
|
||||
|
||||
this._position.x = event.changedTouches[0].clientX - this._hotSpot.x;
|
||||
this._position.y = event.changedTouches[0].clientY - this._hotSpot.y;
|
||||
|
||||
this._updatePosition();
|
||||
}
|
||||
|
||||
_handleTouchEnd(event) {
|
||||
// Same principle as for mouseup
|
||||
let target = document.elementFromPoint(event.changedTouches[0].clientX,
|
||||
event.changedTouches[0].clientY);
|
||||
// Refresh the target from elementFromPoint since queued events
|
||||
// might have altered the DOM
|
||||
target = document.elementFromPoint(event.clientX,
|
||||
event.clientY);
|
||||
this._updateVisibility(target);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
_showCursor() {
|
||||
|
@ -189,6 +208,9 @@ export default class Cursor {
|
|||
// (i.e. are we over the target, or a child of the target without a
|
||||
// different cursor set)
|
||||
_shouldShowCursor(target) {
|
||||
if (!target) {
|
||||
return false;
|
||||
}
|
||||
// Easy case
|
||||
if (target === this._target) {
|
||||
return true;
|
||||
|
@ -207,6 +229,11 @@ export default class Cursor {
|
|||
}
|
||||
|
||||
_updateVisibility(target) {
|
||||
// When the cursor target has capture we want to show the cursor.
|
||||
// So, if a capture is active - look at the captured element instead.
|
||||
if (this._captureIsActive()) {
|
||||
target = document.captureElement;
|
||||
}
|
||||
if (this._shouldShowCursor(target)) {
|
||||
this._showCursor();
|
||||
} else {
|
||||
|
@ -218,4 +245,9 @@ export default class Cursor {
|
|||
this._canvas.style.left = this._position.x + "px";
|
||||
this._canvas.style.top = this._position.y + "px";
|
||||
}
|
||||
|
||||
_captureIsActive() {
|
||||
return document.captureElement &&
|
||||
document.documentElement.contains(document.captureElement);
|
||||
}
|
||||
}
|
||||
|
|
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
|
||||
let _captureRecursion = false;
|
||||
let _captureElem = null;
|
||||
let _elementForUnflushedEvents = null;
|
||||
document.captureElement = null;
|
||||
function _captureProxy(e) {
|
||||
// Recursion protection as we'll see our own event
|
||||
if (_captureRecursion) return;
|
||||
|
@ -30,7 +31,11 @@ function _captureProxy(e) {
|
|||
const newEv = new e.constructor(e.type, e);
|
||||
|
||||
_captureRecursion = true;
|
||||
_captureElem.dispatchEvent(newEv);
|
||||
if (document.captureElement) {
|
||||
document.captureElement.dispatchEvent(newEv);
|
||||
} else {
|
||||
_elementForUnflushedEvents.dispatchEvent(newEv);
|
||||
}
|
||||
_captureRecursion = false;
|
||||
|
||||
// Avoid double events
|
||||
|
@ -48,58 +53,56 @@ function _captureProxy(e) {
|
|||
}
|
||||
|
||||
// Follow cursor style of target element
|
||||
function _captureElemChanged() {
|
||||
const captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||
captureElem.style.cursor = window.getComputedStyle(_captureElem).cursor;
|
||||
function _capturedElemChanged() {
|
||||
const proxyElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||
proxyElem.style.cursor = window.getComputedStyle(document.captureElement).cursor;
|
||||
}
|
||||
|
||||
const _captureObserver = new MutationObserver(_captureElemChanged);
|
||||
const _captureObserver = new MutationObserver(_capturedElemChanged);
|
||||
|
||||
let _captureIndex = 0;
|
||||
export function setCapture(target) {
|
||||
if (target.setCapture) {
|
||||
|
||||
export function setCapture(elem) {
|
||||
if (elem.setCapture) {
|
||||
|
||||
elem.setCapture();
|
||||
target.setCapture();
|
||||
document.captureElement = target;
|
||||
|
||||
// IE releases capture on 'click' events which might not trigger
|
||||
elem.addEventListener('mouseup', releaseCapture);
|
||||
target.addEventListener('mouseup', releaseCapture);
|
||||
|
||||
} else {
|
||||
// Release any existing capture in case this method is
|
||||
// called multiple times without coordination
|
||||
releaseCapture();
|
||||
|
||||
let captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||
let proxyElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||
|
||||
if (captureElem === null) {
|
||||
captureElem = document.createElement("div");
|
||||
captureElem.id = "noVNC_mouse_capture_elem";
|
||||
captureElem.style.position = "fixed";
|
||||
captureElem.style.top = "0px";
|
||||
captureElem.style.left = "0px";
|
||||
captureElem.style.width = "100%";
|
||||
captureElem.style.height = "100%";
|
||||
captureElem.style.zIndex = 10000;
|
||||
captureElem.style.display = "none";
|
||||
document.body.appendChild(captureElem);
|
||||
if (proxyElem === null) {
|
||||
proxyElem = document.createElement("div");
|
||||
proxyElem.id = "noVNC_mouse_capture_elem";
|
||||
proxyElem.style.position = "fixed";
|
||||
proxyElem.style.top = "0px";
|
||||
proxyElem.style.left = "0px";
|
||||
proxyElem.style.width = "100%";
|
||||
proxyElem.style.height = "100%";
|
||||
proxyElem.style.zIndex = 10000;
|
||||
proxyElem.style.display = "none";
|
||||
document.body.appendChild(proxyElem);
|
||||
|
||||
// This is to make sure callers don't get confused by having
|
||||
// our blocking element as the target
|
||||
captureElem.addEventListener('contextmenu', _captureProxy);
|
||||
proxyElem.addEventListener('contextmenu', _captureProxy);
|
||||
|
||||
captureElem.addEventListener('mousemove', _captureProxy);
|
||||
captureElem.addEventListener('mouseup', _captureProxy);
|
||||
proxyElem.addEventListener('mousemove', _captureProxy);
|
||||
proxyElem.addEventListener('mouseup', _captureProxy);
|
||||
}
|
||||
|
||||
_captureElem = elem;
|
||||
_captureIndex++;
|
||||
document.captureElement = target;
|
||||
|
||||
// Track cursor and get initial cursor
|
||||
_captureObserver.observe(elem, {attributes: true});
|
||||
_captureElemChanged();
|
||||
_captureObserver.observe(target, {attributes: true});
|
||||
_capturedElemChanged();
|
||||
|
||||
captureElem.style.display = "";
|
||||
proxyElem.style.display = "";
|
||||
|
||||
// We listen to events on window in order to keep tracking if it
|
||||
// happens to leave the viewport
|
||||
|
@ -112,26 +115,26 @@ export function releaseCapture() {
|
|||
if (document.releaseCapture) {
|
||||
|
||||
document.releaseCapture();
|
||||
document.captureElement = null;
|
||||
|
||||
} else {
|
||||
if (!_captureElem) {
|
||||
if (!document.captureElement) {
|
||||
return;
|
||||
}
|
||||
|
||||
// There might be events already queued, so we need to wait for
|
||||
// them to flush. E.g. contextmenu in Microsoft Edge
|
||||
window.setTimeout((expected) => {
|
||||
// Only clear it if it's the expected grab (i.e. no one
|
||||
// else has initiated a new grab)
|
||||
if (_captureIndex === expected) {
|
||||
_captureElem = null;
|
||||
}
|
||||
}, 0, _captureIndex);
|
||||
// There might be events already queued. The event proxy needs
|
||||
// access to the captured element for these queued events.
|
||||
// E.g. contextmenu (right-click) in Microsoft Edge
|
||||
//
|
||||
// Before removing the capturedElem pointer we save it to a
|
||||
// temporary variable that the unflushed events can use.
|
||||
_elementForUnflushedEvents = document.captureElement;
|
||||
document.captureElement = null;
|
||||
|
||||
_captureObserver.disconnect();
|
||||
|
||||
const captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||
captureElem.style.display = "none";
|
||||
const proxyElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||
proxyElem.style.display = "none";
|
||||
|
||||
window.removeEventListener('mousemove', _captureProxy);
|
||||
window.removeEventListener('mouseup', _captureProxy);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2018 The noVNC Authors
|
||||
* Copyright (C) 2019 The noVNC Authors
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
|
|
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
|
||||
* Copyright (C) 2018 The noVNC Authors
|
||||
* Copyright (C) 2019 The noVNC Authors
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
|
@ -10,18 +10,18 @@
|
|||
* Logging/debug routines
|
||||
*/
|
||||
|
||||
let _log_level = 'warn';
|
||||
let _logLevel = 'warn';
|
||||
|
||||
let Debug = () => {};
|
||||
let Info = () => {};
|
||||
let Warn = () => {};
|
||||
let Error = () => {};
|
||||
|
||||
export function init_logging(level) {
|
||||
export function initLogging(level) {
|
||||
if (typeof level === 'undefined') {
|
||||
level = _log_level;
|
||||
level = _logLevel;
|
||||
} else {
|
||||
_log_level = level;
|
||||
_logLevel = level;
|
||||
}
|
||||
|
||||
Debug = Info = Warn = Error = () => {};
|
||||
|
@ -46,11 +46,11 @@ export function init_logging(level) {
|
|||
}
|
||||
}
|
||||
|
||||
export function get_logging() {
|
||||
return _log_level;
|
||||
export function getLogging() {
|
||||
return _logLevel;
|
||||
}
|
||||
|
||||
export { Debug, Info, Warn, Error };
|
||||
|
||||
// Initialize logging level
|
||||
init_logging();
|
||||
initLogging();
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2018 The noVNC Authors
|
||||
* Copyright (C) 2020 The noVNC Authors
|
||||
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||
*/
|
||||
|
||||
|
@ -52,3 +52,10 @@ if (typeof Object.assign != 'function') {
|
|||
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
|
||||
* Copyright (C) 2018 The noVNC Authors
|
||||
* Copyright (C) 2019 The noVNC Authors
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Decode from UTF-8
|
||||
*/
|
||||
export function decodeUTF8(utf8string) {
|
||||
// Decode from UTF-8
|
||||
export function decodeUTF8(utf8string, allowLatin1=false) {
|
||||
try {
|
||||
return decodeURIComponent(escape(utf8string));
|
||||
} catch (e) {
|
||||
if (e instanceof URIError) {
|
||||
if (allowLatin1) {
|
||||
// If we allow Latin1 we can ignore any decoding fails
|
||||
// and in these cases return the original string
|
||||
return utf8string;
|
||||
}
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
// Encode to UTF-8
|
||||
export function encodeUTF8(DOMString) {
|
||||
return unescape(encodeURIComponent(DOMString));
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Websock: high-performance binary WebSockets
|
||||
* Copyright (C) 2018 The noVNC Authors
|
||||
* Copyright (C) 2019 The noVNC Authors
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* Websock is similar to the standard WebSocket object but with extra
|
||||
|
@ -17,6 +17,8 @@ import * as Log from './util/logging.js';
|
|||
// this has performance issues in some versions Chromium, and
|
||||
// doesn't gain a tremendous amount of performance increase in Firefox
|
||||
// at the moment. It may be valuable to turn it on in the future.
|
||||
// Also copyWithin() for TypedArrays is not supported in IE 11 or
|
||||
// Safari 13 (at the moment we want to support Safari 11).
|
||||
const ENABLE_COPYWITHIN = false;
|
||||
const MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
|
||||
|
||||
|
@ -27,7 +29,6 @@ export default class Websock {
|
|||
this._rQi = 0; // Receive queue index
|
||||
this._rQlen = 0; // Next write position in the receive queue
|
||||
this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
|
||||
this._rQmax = this._rQbufferSize / 8;
|
||||
// called in init: this._rQ = new Uint8Array(this._rQbufferSize);
|
||||
this._rQ = null; // Receive queue
|
||||
|
||||
|
@ -143,7 +144,7 @@ export default class Websock {
|
|||
|
||||
flush() {
|
||||
if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) {
|
||||
this._websocket.send(this._encode_message());
|
||||
this._websocket.send(this._encodeMessage());
|
||||
this._sQlen = 0;
|
||||
}
|
||||
}
|
||||
|
@ -154,7 +155,7 @@ export default class Websock {
|
|||
this.flush();
|
||||
}
|
||||
|
||||
send_string(str) {
|
||||
sendString(str) {
|
||||
this.send(str.split('').map(chr => chr.charCodeAt(0)));
|
||||
}
|
||||
|
||||
|
@ -167,13 +168,13 @@ export default class Websock {
|
|||
this._eventHandlers[evt] = handler;
|
||||
}
|
||||
|
||||
_allocate_buffers() {
|
||||
_allocateBuffers() {
|
||||
this._rQ = new Uint8Array(this._rQbufferSize);
|
||||
this._sQ = new Uint8Array(this._sQbufferSize);
|
||||
}
|
||||
|
||||
init() {
|
||||
this._allocate_buffers();
|
||||
this._allocateBuffers();
|
||||
this._rQi = 0;
|
||||
this._websocket = null;
|
||||
}
|
||||
|
@ -184,7 +185,7 @@ export default class Websock {
|
|||
this._websocket = new WebSocket(uri, protocols);
|
||||
this._websocket.binaryType = 'arraybuffer';
|
||||
|
||||
this._websocket.onmessage = this._recv_message.bind(this);
|
||||
this._websocket.onmessage = this._recvMessage.bind(this);
|
||||
this._websocket.onopen = () => {
|
||||
Log.Debug('>> WebSock.onopen');
|
||||
if (this._websocket.protocol) {
|
||||
|
@ -219,42 +220,46 @@ export default class Websock {
|
|||
}
|
||||
|
||||
// private methods
|
||||
_encode_message() {
|
||||
_encodeMessage() {
|
||||
// Put in a binary arraybuffer
|
||||
// according to the spec, you can send ArrayBufferViews with the send method
|
||||
return new Uint8Array(this._sQ.buffer, 0, this._sQlen);
|
||||
}
|
||||
|
||||
_expand_compact_rQ(min_fit) {
|
||||
const resizeNeeded = min_fit || this.rQlen > this._rQbufferSize / 2;
|
||||
// We want to move all the unread data to the start of the queue,
|
||||
// e.g. compacting.
|
||||
// The function also expands the receive que if needed, and for
|
||||
// performance reasons we combine these two actions to avoid
|
||||
// unneccessary copying.
|
||||
_expandCompactRQ(minFit) {
|
||||
// if we're using less than 1/8th of the buffer even with the incoming bytes, compact in place
|
||||
// instead of resizing
|
||||
const requiredBufferSize = (this._rQlen - this._rQi + minFit) * 8;
|
||||
const resizeNeeded = this._rQbufferSize < requiredBufferSize;
|
||||
|
||||
if (resizeNeeded) {
|
||||
if (!min_fit) {
|
||||
// just double the size if we need to do compaction
|
||||
this._rQbufferSize *= 2;
|
||||
} else {
|
||||
// otherwise, make sure we satisy rQlen - rQi + min_fit < rQbufferSize / 8
|
||||
this._rQbufferSize = (this.rQlen + min_fit) * 8;
|
||||
}
|
||||
// Make sure we always *at least* double the buffer size, and have at least space for 8x
|
||||
// the current amount of data
|
||||
this._rQbufferSize = Math.max(this._rQbufferSize * 2, requiredBufferSize);
|
||||
}
|
||||
|
||||
// we don't want to grow unboundedly
|
||||
if (this._rQbufferSize > MAX_RQ_GROW_SIZE) {
|
||||
this._rQbufferSize = MAX_RQ_GROW_SIZE;
|
||||
if (this._rQbufferSize - this.rQlen < min_fit) {
|
||||
if (this._rQbufferSize - this.rQlen < minFit) {
|
||||
throw new Error("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit");
|
||||
}
|
||||
}
|
||||
|
||||
if (resizeNeeded) {
|
||||
const old_rQbuffer = this._rQ.buffer;
|
||||
this._rQmax = this._rQbufferSize / 8;
|
||||
const oldRQbuffer = this._rQ.buffer;
|
||||
this._rQ = new Uint8Array(this._rQbufferSize);
|
||||
this._rQ.set(new Uint8Array(old_rQbuffer, this._rQi));
|
||||
this._rQ.set(new Uint8Array(oldRQbuffer, this._rQi, this._rQlen - this._rQi));
|
||||
} else {
|
||||
if (ENABLE_COPYWITHIN) {
|
||||
this._rQ.copyWithin(0, this._rQi);
|
||||
this._rQ.copyWithin(0, this._rQi, this._rQlen);
|
||||
} else {
|
||||
this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi));
|
||||
this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi, this._rQlen - this._rQi));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,26 +267,25 @@ export default class Websock {
|
|||
this._rQi = 0;
|
||||
}
|
||||
|
||||
_decode_message(data) {
|
||||
// push arraybuffer values onto the end
|
||||
// push arraybuffer values onto the end of the receive que
|
||||
_DecodeMessage(data) {
|
||||
const u8 = new Uint8Array(data);
|
||||
if (u8.length > this._rQbufferSize - this._rQlen) {
|
||||
this._expand_compact_rQ(u8.length);
|
||||
this._expandCompactRQ(u8.length);
|
||||
}
|
||||
this._rQ.set(u8, this._rQlen);
|
||||
this._rQlen += u8.length;
|
||||
}
|
||||
|
||||
_recv_message(e) {
|
||||
this._decode_message(e.data);
|
||||
_recvMessage(e) {
|
||||
this._DecodeMessage(e.data);
|
||||
if (this.rQlen > 0) {
|
||||
this._eventHandlers.message();
|
||||
// Compact the receive queue
|
||||
if (this._rQlen == this._rQi) {
|
||||
// All data has now been processed, this means we
|
||||
// can reset the receive queue.
|
||||
this._rQlen = 0;
|
||||
this._rQi = 0;
|
||||
} else if (this._rQlen > this._rQmax) {
|
||||
this._expand_compact_rQ();
|
||||
}
|
||||
} else {
|
||||
Log.Debug("Ignoring empty message");
|
||||
|
|
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
|
||||
WebWorkers to compile the modules in the background.
|
||||
|
||||
To generate, run `rollup -c` in this directory, and then run `browserify
|
||||
src/babel-worker.js > dist/babel-worker.js`.
|
||||
To generate, run `npx rollup -c` in this directory, and then run
|
||||
`./genworker.js`.
|
||||
|
||||
LICENSE
|
||||
-------
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,74 +1,71 @@
|
|||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||
typeof define === 'function' && define.amd ? define(factory) :
|
||||
(global.BrowserESModuleLoader = factory());
|
||||
(global = global || self, global.BrowserESModuleLoader = factory());
|
||||
}(this, (function () { 'use strict';
|
||||
|
||||
/*
|
||||
/*
|
||||
* Environment
|
||||
*/
|
||||
var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
|
||||
var isNode = typeof process !== 'undefined' && process.versions && process.versions.node;
|
||||
var isWindows = typeof process !== 'undefined' && typeof process.platform === 'string' && process.platform.match(/^win/);
|
||||
var isBrowser = typeof window !== 'undefined' && typeof document !== 'undefined';
|
||||
var isNode = typeof process !== 'undefined' && process.versions && process.versions.node;
|
||||
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
|
||||
*/
|
||||
var hasSymbol = typeof Symbol !== 'undefined';
|
||||
function createSymbol (name) {
|
||||
var hasSymbol = typeof Symbol !== 'undefined';
|
||||
function createSymbol (name) {
|
||||
return hasSymbol ? Symbol() : '@@' + name;
|
||||
}
|
||||
}
|
||||
|
||||
var toStringTag = hasSymbol && Symbol.toStringTag;
|
||||
var toStringTag = hasSymbol && Symbol.toStringTag;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
/*
|
||||
* Environment baseURI
|
||||
*/
|
||||
var baseURI;
|
||||
var baseURI;
|
||||
|
||||
// environent baseURI detection
|
||||
if (typeof document != 'undefined' && document.getElementsByTagName) {
|
||||
// environent baseURI detection
|
||||
if (typeof document != 'undefined' && document.getElementsByTagName) {
|
||||
baseURI = document.baseURI;
|
||||
|
||||
if (!baseURI) {
|
||||
var bases = document.getElementsByTagName('base');
|
||||
baseURI = bases[0] && bases[0].href || window.location.href;
|
||||
}
|
||||
}
|
||||
else if (typeof location != 'undefined') {
|
||||
}
|
||||
else if (typeof location != 'undefined') {
|
||||
baseURI = location.href;
|
||||
}
|
||||
}
|
||||
|
||||
// sanitize out the hash and querystring
|
||||
if (baseURI) {
|
||||
// sanitize out the hash and querystring
|
||||
if (baseURI) {
|
||||
baseURI = baseURI.split('#')[0].split('?')[0];
|
||||
var slashIndex = baseURI.lastIndexOf('/');
|
||||
if (slashIndex !== -1)
|
||||
baseURI = baseURI.substr(0, slashIndex + 1);
|
||||
}
|
||||
else if (typeof process !== 'undefined' && process.cwd) {
|
||||
}
|
||||
else if (typeof process !== 'undefined' && process.cwd) {
|
||||
baseURI = 'file://' + (isWindows ? '/' : '') + process.cwd();
|
||||
if (isWindows)
|
||||
baseURI = baseURI.replace(/\\/g, '/');
|
||||
}
|
||||
else {
|
||||
}
|
||||
else {
|
||||
throw new TypeError('No environment baseURI');
|
||||
}
|
||||
}
|
||||
|
||||
// ensure baseURI has trailing "/"
|
||||
if (baseURI[baseURI.length - 1] !== '/')
|
||||
// ensure baseURI has trailing "/"
|
||||
if (baseURI[baseURI.length - 1] !== '/')
|
||||
baseURI += '/';
|
||||
|
||||
/*
|
||||
/*
|
||||
* LoaderError with chaining for loader stacks
|
||||
*/
|
||||
var errArgs = new Error(0, '_').fileName == '_';
|
||||
function LoaderError__Check_error_message_for_loader_stack (childErr, newMessage) {
|
||||
var errArgs = new Error(0, '_').fileName == '_';
|
||||
function LoaderError__Check_error_message_for_loader_stack (childErr, newMessage) {
|
||||
// Convert file:/// URLs to paths in Node
|
||||
if (!isBrowser)
|
||||
newMessage = newMessage.replace(isWindows ? /file:\/\/\//g : /file:\/\//g, '');
|
||||
|
@ -93,14 +90,14 @@ function LoaderError__Check_error_message_for_loader_stack (childErr, newMessage
|
|||
err.originalErr = childErr.originalErr || childErr;
|
||||
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
var resolvedPromise$1 = Promise.resolve();
|
||||
var resolvedPromise = Promise.resolve();
|
||||
|
||||
/*
|
||||
/*
|
||||
* Simple Array values shim
|
||||
*/
|
||||
function arrayValues (arr) {
|
||||
function arrayValues (arr) {
|
||||
if (arr.values)
|
||||
return arr.values();
|
||||
|
||||
|
@ -127,35 +124,35 @@ function arrayValues (arr) {
|
|||
};
|
||||
};
|
||||
return iterable;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* 3. Reflect.Loader
|
||||
*
|
||||
* We skip the entire native internal pipeline, just providing the bare API
|
||||
*/
|
||||
// 3.1.1
|
||||
function Loader () {
|
||||
// 3.1.1
|
||||
function Loader () {
|
||||
this.registry = new Registry();
|
||||
}
|
||||
// 3.3.1
|
||||
Loader.prototype.constructor = Loader;
|
||||
}
|
||||
// 3.3.1
|
||||
Loader.prototype.constructor = Loader;
|
||||
|
||||
function ensureInstantiated (module) {
|
||||
function ensureInstantiated (module) {
|
||||
if (module === undefined)
|
||||
return;
|
||||
if (module instanceof ModuleNamespace === false && module[toStringTag] !== 'module')
|
||||
throw new TypeError('Module instantiation did not return a valid namespace object.');
|
||||
return module;
|
||||
}
|
||||
}
|
||||
|
||||
// 3.3.2
|
||||
Loader.prototype.import = function (key, parent) {
|
||||
// 3.3.2
|
||||
Loader.prototype.import = function (key, parent) {
|
||||
if (typeof key !== 'string')
|
||||
throw new TypeError('Loader import method must be passed a module key string');
|
||||
// custom resolveInstantiate combined hook for better perf
|
||||
var loader = this;
|
||||
return resolvedPromise$1
|
||||
return resolvedPromise
|
||||
.then(function () {
|
||||
return loader[RESOLVE_INSTANTIATE](key, parent);
|
||||
})
|
||||
|
@ -164,11 +161,11 @@ Loader.prototype.import = function (key, parent) {
|
|||
.catch(function (err) {
|
||||
throw LoaderError__Check_error_message_for_loader_stack(err, 'Loading ' + key + (parent ? ' from ' + parent : ''));
|
||||
});
|
||||
};
|
||||
// 3.3.3
|
||||
var RESOLVE = Loader.resolve = createSymbol('resolve');
|
||||
};
|
||||
// 3.3.3
|
||||
var RESOLVE = Loader.resolve = createSymbol('resolve');
|
||||
|
||||
/*
|
||||
/*
|
||||
* Combined resolve / instantiate hook
|
||||
*
|
||||
* Not in current reduced spec, but necessary to separate RESOLVE from RESOLVE + INSTANTIATE as described
|
||||
|
@ -177,27 +174,27 @@ var RESOLVE = Loader.resolve = createSymbol('resolve');
|
|||
* We implement RESOLVE_INSTANTIATE as a single hook instead of a separate INSTANTIATE in order to avoid
|
||||
* the need for double registry lookups as a performance optimization.
|
||||
*/
|
||||
var RESOLVE_INSTANTIATE = Loader.resolveInstantiate = createSymbol('resolveInstantiate');
|
||||
var RESOLVE_INSTANTIATE = Loader.resolveInstantiate = createSymbol('resolveInstantiate');
|
||||
|
||||
// default resolveInstantiate is just to call resolve and then get from the registry
|
||||
// this provides compatibility for the resolveInstantiate optimization
|
||||
Loader.prototype[RESOLVE_INSTANTIATE] = function (key, parent) {
|
||||
// default resolveInstantiate is just to call resolve and then get from the registry
|
||||
// this provides compatibility for the resolveInstantiate optimization
|
||||
Loader.prototype[RESOLVE_INSTANTIATE] = function (key, parent) {
|
||||
var loader = this;
|
||||
return loader.resolve(key, parent)
|
||||
.then(function (resolved) {
|
||||
return loader.registry.get(resolved);
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
function ensureResolution (resolvedKey) {
|
||||
function ensureResolution (resolvedKey) {
|
||||
if (resolvedKey === undefined)
|
||||
throw new RangeError('No resolution found.');
|
||||
return resolvedKey;
|
||||
}
|
||||
}
|
||||
|
||||
Loader.prototype.resolve = function (key, parent) {
|
||||
Loader.prototype.resolve = function (key, parent) {
|
||||
var loader = this;
|
||||
return resolvedPromise$1
|
||||
return resolvedPromise
|
||||
.then(function() {
|
||||
return loader[RESOLVE](key, parent);
|
||||
})
|
||||
|
@ -205,20 +202,20 @@ Loader.prototype.resolve = function (key, parent) {
|
|||
.catch(function (err) {
|
||||
throw LoaderError__Check_error_message_for_loader_stack(err, 'Resolving ' + key + (parent ? ' to ' + parent : ''));
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
// 3.3.4 (import without evaluate)
|
||||
// this is not documented because the use of deferred evaluation as in Module.evaluate is not
|
||||
// documented, as it is not considered a stable feature to be encouraged
|
||||
// Loader.prototype.load may well be deprecated if this stays disabled
|
||||
/* Loader.prototype.load = function (key, parent) {
|
||||
// 3.3.4 (import without evaluate)
|
||||
// this is not documented because the use of deferred evaluation as in Module.evaluate is not
|
||||
// documented, as it is not considered a stable feature to be encouraged
|
||||
// Loader.prototype.load may well be deprecated if this stays disabled
|
||||
/* Loader.prototype.load = function (key, parent) {
|
||||
return Promise.resolve(this[RESOLVE_INSTANTIATE](key, parent || this.key))
|
||||
.catch(function (err) {
|
||||
throw addToError(err, 'Loading ' + key + (parent ? ' from ' + parent : ''));
|
||||
});
|
||||
}; */
|
||||
}; */
|
||||
|
||||
/*
|
||||
/*
|
||||
* 4. Registry
|
||||
*
|
||||
* Instead of structuring through a Map, just use a dictionary object
|
||||
|
@ -227,13 +224,13 @@ Loader.prototype.resolve = function (key, parent) {
|
|||
* Registry has been adjusted to use Namespace objects over ModuleStatus objects
|
||||
* as part of simplifying loader API implementation
|
||||
*/
|
||||
var iteratorSupport = typeof Symbol !== 'undefined' && Symbol.iterator;
|
||||
var REGISTRY = createSymbol('registry');
|
||||
function Registry() {
|
||||
var iteratorSupport = typeof Symbol !== 'undefined' && Symbol.iterator;
|
||||
var REGISTRY = createSymbol('registry');
|
||||
function Registry() {
|
||||
this[REGISTRY] = {};
|
||||
}
|
||||
// 4.4.1
|
||||
if (iteratorSupport) {
|
||||
}
|
||||
// 4.4.1
|
||||
if (iteratorSupport) {
|
||||
// 4.4.2
|
||||
Registry.prototype[Symbol.iterator] = function () {
|
||||
return this.entries()[Symbol.iterator]();
|
||||
|
@ -246,52 +243,52 @@ if (iteratorSupport) {
|
|||
return [key, registry[key]];
|
||||
}));
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 4.4.4
|
||||
Registry.prototype.keys = function () {
|
||||
// 4.4.4
|
||||
Registry.prototype.keys = function () {
|
||||
return arrayValues(Object.keys(this[REGISTRY]));
|
||||
};
|
||||
// 4.4.5
|
||||
Registry.prototype.values = function () {
|
||||
};
|
||||
// 4.4.5
|
||||
Registry.prototype.values = function () {
|
||||
var registry = this[REGISTRY];
|
||||
return arrayValues(Object.keys(registry).map(function (key) {
|
||||
return registry[key];
|
||||
}));
|
||||
};
|
||||
// 4.4.6
|
||||
Registry.prototype.get = function (key) {
|
||||
};
|
||||
// 4.4.6
|
||||
Registry.prototype.get = function (key) {
|
||||
return this[REGISTRY][key];
|
||||
};
|
||||
// 4.4.7
|
||||
Registry.prototype.set = function (key, namespace) {
|
||||
};
|
||||
// 4.4.7
|
||||
Registry.prototype.set = function (key, namespace) {
|
||||
if (!(namespace instanceof ModuleNamespace || namespace[toStringTag] === 'module'))
|
||||
throw new Error('Registry must be set with an instance of Module Namespace');
|
||||
this[REGISTRY][key] = namespace;
|
||||
return this;
|
||||
};
|
||||
// 4.4.8
|
||||
Registry.prototype.has = function (key) {
|
||||
};
|
||||
// 4.4.8
|
||||
Registry.prototype.has = function (key) {
|
||||
return Object.hasOwnProperty.call(this[REGISTRY], key);
|
||||
};
|
||||
// 4.4.9
|
||||
Registry.prototype.delete = function (key) {
|
||||
};
|
||||
// 4.4.9
|
||||
Registry.prototype.delete = function (key) {
|
||||
if (Object.hasOwnProperty.call(this[REGISTRY], key)) {
|
||||
delete this[REGISTRY][key];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
/*
|
||||
* Simple ModuleNamespace Exotic object based on a baseObject
|
||||
* We export this for allowing a fast-path for module namespace creation over Module descriptors
|
||||
*/
|
||||
// var EVALUATE = createSymbol('evaluate');
|
||||
var BASE_OBJECT = createSymbol('baseObject');
|
||||
// var EVALUATE = createSymbol('evaluate');
|
||||
var BASE_OBJECT = createSymbol('baseObject');
|
||||
|
||||
// 8.3.1 Reflect.Module
|
||||
/*
|
||||
// 8.3.1 Reflect.Module
|
||||
/*
|
||||
* Best-effort simplified non-spec implementation based on
|
||||
* a baseObject referenced via getters.
|
||||
*
|
||||
|
@ -302,7 +299,7 @@ var BASE_OBJECT = createSymbol('baseObject');
|
|||
* Optional evaluation function provides experimental Module.evaluate
|
||||
* support for non-executed modules in registry.
|
||||
*/
|
||||
function ModuleNamespace (baseObject/*, evaluate*/) {
|
||||
function ModuleNamespace (baseObject/*, evaluate*/) {
|
||||
Object.defineProperty(this, BASE_OBJECT, {
|
||||
value: baseObject
|
||||
});
|
||||
|
@ -318,35 +315,34 @@ function ModuleNamespace (baseObject/*, evaluate*/) {
|
|||
else { */
|
||||
Object.keys(baseObject).forEach(extendNamespace, this);
|
||||
//}
|
||||
}
|
||||
// 8.4.2
|
||||
ModuleNamespace.prototype = Object.create(null);
|
||||
}// 8.4.2
|
||||
ModuleNamespace.prototype = Object.create(null);
|
||||
|
||||
if (toStringTag)
|
||||
if (toStringTag)
|
||||
Object.defineProperty(ModuleNamespace.prototype, toStringTag, {
|
||||
value: 'Module'
|
||||
});
|
||||
|
||||
function extendNamespace (key) {
|
||||
function extendNamespace (key) {
|
||||
Object.defineProperty(this, key, {
|
||||
enumerable: true,
|
||||
get: function () {
|
||||
return this[BASE_OBJECT][key];
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/* function doEvaluate (evaluate, context) {
|
||||
/* function doEvaluate (evaluate, context) {
|
||||
try {
|
||||
evaluate.call(context);
|
||||
}
|
||||
catch (e) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 8.4.1 Module.evaluate... not documented or used because this is potentially unstable
|
||||
Module.evaluate = function (ns) {
|
||||
// 8.4.1 Module.evaluate... not documented or used because this is potentially unstable
|
||||
Module.evaluate = function (ns) {
|
||||
var evaluate = ns[EVALUATE];
|
||||
if (evaluate) {
|
||||
ns[EVALUATE] = undefined;
|
||||
|
@ -362,16 +358,16 @@ Module.evaluate = function (ns) {
|
|||
}
|
||||
// make chainable
|
||||
return ns;
|
||||
}; */
|
||||
}; */
|
||||
|
||||
/*
|
||||
/*
|
||||
* Optimized URL normalization assuming a syntax-valid URL parent
|
||||
*/
|
||||
function throwResolveError (relUrl, parentUrl) {
|
||||
function throwResolveError (relUrl, parentUrl) {
|
||||
throw new RangeError('Unable to resolve "' + relUrl + '" to ' + parentUrl);
|
||||
}
|
||||
var backslashRegEx = /\\/g;
|
||||
function resolveIfNotPlain (relUrl, parentUrl) {
|
||||
}
|
||||
var backslashRegEx = /\\/g;
|
||||
function resolveIfNotPlain (relUrl, parentUrl) {
|
||||
if (relUrl[0] === ' ' || relUrl[relUrl.length - 1] === ' ')
|
||||
relUrl = relUrl.trim();
|
||||
var parentProtocol = parentUrl && parentUrl.substr(0, parentUrl.indexOf(':') + 1);
|
||||
|
@ -489,10 +485,11 @@ function resolveIfNotPlain (relUrl, parentUrl) {
|
|||
}
|
||||
return relUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var resolvedPromise = Promise.resolve();
|
||||
/*
|
||||
var resolvedPromise$1 = Promise.resolve();
|
||||
|
||||
/*
|
||||
* Register Loader
|
||||
*
|
||||
* Builds directly on top of loader polyfill to provide:
|
||||
|
@ -503,9 +500,9 @@ var resolvedPromise = Promise.resolve();
|
|||
* - build tracing support by providing a .trace=true and .loads object format
|
||||
*/
|
||||
|
||||
var REGISTER_INTERNAL = createSymbol('register-internal');
|
||||
var REGISTER_INTERNAL = createSymbol('register-internal');
|
||||
|
||||
function RegisterLoader$1 () {
|
||||
function RegisterLoader () {
|
||||
Loader.call(this);
|
||||
|
||||
var registryDelete = this.registry.delete;
|
||||
|
@ -532,24 +529,24 @@ function RegisterLoader$1 () {
|
|||
|
||||
// tracing
|
||||
this.trace = false;
|
||||
}
|
||||
}
|
||||
|
||||
RegisterLoader$1.prototype = Object.create(Loader.prototype);
|
||||
RegisterLoader$1.prototype.constructor = RegisterLoader$1;
|
||||
RegisterLoader.prototype = Object.create(Loader.prototype);
|
||||
RegisterLoader.prototype.constructor = RegisterLoader;
|
||||
|
||||
var INSTANTIATE = RegisterLoader$1.instantiate = createSymbol('instantiate');
|
||||
var INSTANTIATE = RegisterLoader.instantiate = createSymbol('instantiate');
|
||||
|
||||
// default normalize is the WhatWG style normalizer
|
||||
RegisterLoader$1.prototype[RegisterLoader$1.resolve = Loader.resolve] = function (key, parentKey) {
|
||||
// default normalize is the WhatWG style normalizer
|
||||
RegisterLoader.prototype[RegisterLoader.resolve = Loader.resolve] = function (key, parentKey) {
|
||||
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
|
||||
// this allows tracking new binding listeners for es modules through importerSetters
|
||||
// for dynamic modules, the load record is removed entirely.
|
||||
function createLoadRecord (state, key, registration) {
|
||||
// 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
|
||||
// for dynamic modules, the load record is removed entirely.
|
||||
function createLoadRecord (state, key, registration) {
|
||||
return state.records[key] = {
|
||||
key: key,
|
||||
|
||||
|
@ -597,9 +594,9 @@ function createLoadRecord (state, key, registration) {
|
|||
// hoisted: undefined
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
RegisterLoader$1.prototype[Loader.resolveInstantiate] = function (key, parentKey) {
|
||||
RegisterLoader.prototype[Loader.resolveInstantiate] = function (key, parentKey) {
|
||||
var loader = this;
|
||||
var state = this[REGISTER_INTERNAL];
|
||||
var registry = this.registry[REGISTRY];
|
||||
|
@ -624,9 +621,9 @@ RegisterLoader$1.prototype[Loader.resolveInstantiate] = function (key, parentKey
|
|||
return ensureEvaluate(loader, instantiated, link, registry, state);
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
function resolveInstantiate (loader, key, parentKey, registry, state) {
|
||||
function resolveInstantiate (loader, key, parentKey, registry, state) {
|
||||
// normalization shortpath for already-normalized key
|
||||
// could add a plain name filter, but doesn't yet seem necessary for perf
|
||||
var module = registry[key];
|
||||
|
@ -667,9 +664,9 @@ function resolveInstantiate (loader, key, parentKey, registry, state) {
|
|||
|
||||
return instantiate(loader, load, link, registry, state);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function createProcessAnonRegister (loader, load, state) {
|
||||
function createProcessAnonRegister (loader, load, state) {
|
||||
return function () {
|
||||
var lastRegister = state.lastRegister;
|
||||
|
||||
|
@ -681,12 +678,12 @@ function createProcessAnonRegister (loader, load, state) {
|
|||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function instantiate (loader, load, link, registry, state) {
|
||||
function instantiate (loader, load, link, registry, state) {
|
||||
return link.instantiatePromise || (link.instantiatePromise =
|
||||
// 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;
|
||||
return loader[INSTANTIATE](load.key, loader[INSTANTIATE].length > 1 && createProcessAnonRegister(loader, load, state));
|
||||
}))
|
||||
|
@ -733,10 +730,10 @@ function instantiate (loader, load, link, registry, state) {
|
|||
load.linkRecord = undefined;
|
||||
throw load.loadError = load.loadError || LoaderError__Check_error_message_for_loader_stack(err, 'Instantiating ' + load.key);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// like resolveInstantiate, but returning load records for linking
|
||||
function resolveInstantiateDep (loader, key, parentKey, registry, state, traceDepMap) {
|
||||
// like resolveInstantiate, but returning load records for linking
|
||||
function resolveInstantiateDep (loader, key, parentKey, registry, state, traceDepMap) {
|
||||
// normalization shortpaths for already-normalized key
|
||||
// DISABLED to prioritise consistent resolver calls
|
||||
// could add a plain name filter, but doesn't yet seem necessary for perf
|
||||
|
@ -789,9 +786,9 @@ function resolveInstantiateDep (loader, key, parentKey, registry, state, traceDe
|
|||
|
||||
return instantiate(loader, load, link, registry, state);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function traceLoad (loader, load, link) {
|
||||
function traceLoad (loader, load, link) {
|
||||
loader.loads = loader.loads || {};
|
||||
loader.loads[load.key] = {
|
||||
key: load.key,
|
||||
|
@ -799,16 +796,16 @@ function traceLoad (loader, load, link) {
|
|||
dynamicDeps: [],
|
||||
depMap: link.depMap || {}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* Convert a CJS module.exports into a valid object for new Module:
|
||||
*
|
||||
* new Module(getEsModule(module.exports))
|
||||
*
|
||||
* Sets the default value to the module, while also reading off named exports carefully.
|
||||
*/
|
||||
function registerDeclarative (loader, load, link, declare) {
|
||||
function registerDeclarative (loader, load, link, declare) {
|
||||
var moduleObj = link.moduleObj;
|
||||
var importerSetters = load.importerSetters;
|
||||
|
||||
|
@ -846,9 +843,9 @@ function registerDeclarative (loader, load, link, declare) {
|
|||
link.moduleObj = moduleObj = declared.exports;
|
||||
definedExports = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function instantiateDeps (loader, load, link, registry, state) {
|
||||
function instantiateDeps (loader, load, link, registry, state) {
|
||||
if (link.depsInstantiatePromise)
|
||||
return link.depsInstantiatePromise;
|
||||
|
||||
|
@ -901,15 +898,15 @@ function instantiateDeps (loader, load, link, registry, state) {
|
|||
depsInstantiatePromise.catch(function () {});
|
||||
|
||||
return link.depsInstantiatePromise = depsInstantiatePromise;
|
||||
}
|
||||
}
|
||||
|
||||
function deepInstantiateDeps (loader, load, link, registry, state) {
|
||||
function deepInstantiateDeps (loader, load, link, registry, state) {
|
||||
var seen = [];
|
||||
function addDeps (load, link) {
|
||||
if (!link)
|
||||
return resolvedPromise;
|
||||
return resolvedPromise$1;
|
||||
if (seen.indexOf(load) !== -1)
|
||||
return resolvedPromise;
|
||||
return resolvedPromise$1;
|
||||
seen.push(load);
|
||||
|
||||
return instantiateDeps(loader, load, link, registry, state)
|
||||
|
@ -926,14 +923,13 @@ function deepInstantiateDeps (loader, load, link, registry, state) {
|
|||
return Promise.all(depPromises);
|
||||
});
|
||||
}
|
||||
|
||||
return addDeps(load, link);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
/*
|
||||
* System.register
|
||||
*/
|
||||
RegisterLoader$1.prototype.register = function (key, deps, declare) {
|
||||
RegisterLoader.prototype.register = function (key, deps, declare) {
|
||||
var state = this[REGISTER_INTERNAL];
|
||||
|
||||
// anonymous modules get stored as lastAnon
|
||||
|
@ -946,12 +942,12 @@ RegisterLoader$1.prototype.register = function (key, deps, declare) {
|
|||
var load = state.records[key] || createLoadRecord(state, key, undefined);
|
||||
load.registration = [deps, declare, undefined];
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
/*
|
||||
* System.registerDyanmic
|
||||
*/
|
||||
RegisterLoader$1.prototype.registerDynamic = function (key, deps, executingRequire, execute) {
|
||||
RegisterLoader.prototype.registerDynamic = function (key, deps, executingRequire, execute) {
|
||||
var state = this[REGISTER_INTERNAL];
|
||||
|
||||
// anonymous modules get stored as lastAnon
|
||||
|
@ -964,31 +960,31 @@ RegisterLoader$1.prototype.registerDynamic = function (key, deps, executingRequi
|
|||
var load = state.records[key] || createLoadRecord(state, key, undefined);
|
||||
load.registration = [deps, executingRequire, execute];
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// ContextualLoader class
|
||||
// backwards-compatible with previous System.register context argument by exposing .id, .key
|
||||
function ContextualLoader (loader, key) {
|
||||
// ContextualLoader class
|
||||
// backwards-compatible with previous System.register context argument by exposing .id, .key
|
||||
function ContextualLoader (loader, key) {
|
||||
this.loader = loader;
|
||||
this.key = this.id = key;
|
||||
this.meta = {
|
||||
url: key
|
||||
// scriptElement: null
|
||||
};
|
||||
}
|
||||
/*ContextualLoader.prototype.constructor = function () {
|
||||
}
|
||||
/*ContextualLoader.prototype.constructor = function () {
|
||||
throw new TypeError('Cannot subclass the contextual loader only Reflect.Loader.');
|
||||
};*/
|
||||
ContextualLoader.prototype.import = function (key) {
|
||||
};*/
|
||||
ContextualLoader.prototype.import = function (key) {
|
||||
if (this.loader.trace)
|
||||
this.loader.loads[this.key].dynamicDeps.push(key);
|
||||
return this.loader.import(key, this.key);
|
||||
};
|
||||
/*ContextualLoader.prototype.resolve = function (key) {
|
||||
};
|
||||
/*ContextualLoader.prototype.resolve = function (key) {
|
||||
return this.loader.resolve(key, this.key);
|
||||
};*/
|
||||
};*/
|
||||
|
||||
function ensureEvaluate (loader, load, link, registry, state) {
|
||||
function ensureEvaluate (loader, load, link, registry, state) {
|
||||
if (load.module)
|
||||
return load.module;
|
||||
if (load.evalError)
|
||||
|
@ -1005,9 +1001,9 @@ function ensureEvaluate (loader, load, link, registry, state) {
|
|||
doEvaluateDynamic(loader, load, link, registry, state, [load]);
|
||||
}
|
||||
return load.module;
|
||||
}
|
||||
}
|
||||
|
||||
function makeDynamicRequire (loader, key, dependencies, dependencyInstantiations, registry, state, seen) {
|
||||
function makeDynamicRequire (loader, key, dependencies, dependencyInstantiations, registry, state, seen) {
|
||||
// we can only require from already-known dependencies
|
||||
return function (name) {
|
||||
for (var i = 0; i < dependencies.length; i++) {
|
||||
|
@ -1038,19 +1034,19 @@ function makeDynamicRequire (loader, key, dependencies, dependencyInstantiations
|
|||
}
|
||||
throw new Error('Module ' + name + ' not declared as a System.registerDynamic dependency of ' + key);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
function evalError (load, err) {
|
||||
function evalError (load, err) {
|
||||
load.linkRecord = undefined;
|
||||
var evalError = LoaderError__Check_error_message_for_loader_stack(err, 'Evaluating ' + load.key);
|
||||
if (load.evalError === undefined)
|
||||
load.evalError = evalError;
|
||||
throw evalError;
|
||||
}
|
||||
}
|
||||
|
||||
// es modules evaluate dependencies first
|
||||
// returns the error if any
|
||||
function doEvaluateDeclarative (loader, load, link, registry, state, seen) {
|
||||
// es modules evaluate dependencies first
|
||||
// returns the error if any
|
||||
function doEvaluateDeclarative (loader, load, link, registry, state, seen) {
|
||||
var depLoad, depLink;
|
||||
var depLoadPromises;
|
||||
for (var i = 0; i < link.dependencies.length; i++) {
|
||||
|
@ -1141,10 +1137,10 @@ function doEvaluateDeclarative (loader, load, link, registry, state, seen) {
|
|||
// dispose link record
|
||||
load.linkRecord = undefined;
|
||||
registry[load.key] = load.module = new ModuleNamespace(link.moduleObj);
|
||||
}
|
||||
}
|
||||
|
||||
// non es modules explicitly call moduleEvaluate through require
|
||||
function doEvaluateDynamic (loader, load, link, registry, state, seen) {
|
||||
// non es modules explicitly call moduleEvaluate through require
|
||||
function doEvaluateDynamic (loader, load, link, registry, state, seen) {
|
||||
// System.registerDynamic execute
|
||||
// "this" is "exports" in CJS
|
||||
var module = { id: load.key };
|
||||
|
@ -1200,18 +1196,18 @@ function doEvaluateDynamic (loader, load, link, registry, state, seen) {
|
|||
for (var i = 0; i < load.importerSetters.length; i++)
|
||||
load.importerSetters[i](load.module);
|
||||
load.importerSetters = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// the closest we can get to call(undefined)
|
||||
var nullContext = Object.create(null);
|
||||
if (Object.freeze)
|
||||
// the closest we can get to call(undefined)
|
||||
var nullContext = Object.create(null);
|
||||
if (Object.freeze)
|
||||
Object.freeze(nullContext);
|
||||
|
||||
var loader;
|
||||
var loader;
|
||||
|
||||
// <script type="module"> support
|
||||
var anonSources = {};
|
||||
if (typeof document != 'undefined' && document.getElementsByTagName) {
|
||||
// <script type="module"> support
|
||||
var anonSources = {};
|
||||
if (typeof document != 'undefined' && document.getElementsByTagName) {
|
||||
var handleError = function(err) {
|
||||
// dispatch an error event so that we can display in errors in browsers
|
||||
// that don't yet support unhandledrejection
|
||||
|
@ -1273,13 +1269,13 @@ if (typeof document != 'undefined' && document.getElementsByTagName) {
|
|||
setTimeout(ready);
|
||||
else
|
||||
document.addEventListener('DOMContentLoaded', ready, false);
|
||||
}
|
||||
}
|
||||
|
||||
function BrowserESModuleLoader(baseKey) {
|
||||
function BrowserESModuleLoader(baseKey) {
|
||||
if (baseKey)
|
||||
this.baseKey = resolveIfNotPlain(baseKey, baseURI) || resolveIfNotPlain('./' + baseKey, baseURI);
|
||||
|
||||
RegisterLoader$1.call(this);
|
||||
RegisterLoader.call(this);
|
||||
|
||||
var loader = this;
|
||||
|
||||
|
@ -1292,19 +1288,19 @@ function BrowserESModuleLoader(baseKey) {
|
|||
if (prevRegister)
|
||||
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
|
||||
BrowserESModuleLoader.prototype[RegisterLoader$1.resolve] = function(key, parent) {
|
||||
var resolved = RegisterLoader$1.prototype[RegisterLoader$1.resolve].call(this, key, parent || this.baseKey) || key;
|
||||
// normalize is never given a relative name like "./x", that part is already handled
|
||||
BrowserESModuleLoader.prototype[RegisterLoader.resolve] = function(key, parent) {
|
||||
var resolved = RegisterLoader.prototype[RegisterLoader.resolve].call(this, key, parent || this.baseKey) || key;
|
||||
if (!resolved)
|
||||
throw new RangeError('ES module loader does not resolve plain module names, resolving "' + key + '" to ' + parent);
|
||||
|
||||
return resolved;
|
||||
};
|
||||
};
|
||||
|
||||
function xhrFetch(url, resolve, reject) {
|
||||
function xhrFetch(url, resolve, reject) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
var load = function(source) {
|
||||
resolve(xhr.responseText);
|
||||
|
@ -1337,9 +1333,9 @@ function xhrFetch(url, resolve, reject) {
|
|||
};
|
||||
xhr.open("GET", url, true);
|
||||
xhr.send(null);
|
||||
}
|
||||
}
|
||||
|
||||
var WorkerPool = function (script, size) {
|
||||
var WorkerPool = function (script, size) {
|
||||
var current = document.currentScript;
|
||||
// IE doesn't support currentScript
|
||||
if (!current) {
|
||||
|
@ -1371,8 +1367,8 @@ var WorkerPool = function (script, size) {
|
|||
}
|
||||
|
||||
this._checkJobs();
|
||||
};
|
||||
WorkerPool.prototype = {
|
||||
};
|
||||
WorkerPool.prototype = {
|
||||
postMessage: function (msg) {
|
||||
if (this._stopTimeout !== undefined) {
|
||||
clearTimeout(this._stopTimeout);
|
||||
|
@ -1419,20 +1415,19 @@ WorkerPool.prototype = {
|
|||
wrkr.terminate();
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var promiseMap = new Map();
|
||||
var babelWorker = new WorkerPool('babel-worker.js', 3);
|
||||
babelWorker.onmessage = function (evt) {
|
||||
var promiseMap = new Map();
|
||||
var babelWorker = new WorkerPool('babel-worker.js', 3);
|
||||
babelWorker.onmessage = function (evt) {
|
||||
var promFuncs = promiseMap.get(evt.data.key);
|
||||
promFuncs.resolve(evt.data);
|
||||
promiseMap.delete(evt.data.key);
|
||||
};
|
||||
};
|
||||
|
||||
// instantiate just needs to run System.register
|
||||
// so we fetch the source, convert into the Babel System module format, then evaluate it
|
||||
BrowserESModuleLoader.prototype[RegisterLoader$1.instantiate] = function(key, processAnonRegister) {
|
||||
var loader = this;
|
||||
// instantiate just needs to run System.register
|
||||
// so we fetch the source, convert into the Babel System module format, then evaluate it
|
||||
BrowserESModuleLoader.prototype[RegisterLoader.instantiate] = function(key, processAnonRegister) {
|
||||
|
||||
// load as ES with Babel converting into System.register
|
||||
return new Promise(function(resolve, reject) {
|
||||
|
@ -1474,13 +1469,13 @@ BrowserESModuleLoader.prototype[RegisterLoader$1.instantiate] = function(key, pr
|
|||
(0, eval)(data.code + '\n//# sourceURL=' + data.key + '!transpiled');
|
||||
processAnonRegister();
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
// create a default loader instance in the browser
|
||||
if (isBrowser)
|
||||
// create a default loader instance in the browser
|
||||
if (isBrowser)
|
||||
loader = new BrowserESModuleLoader();
|
||||
|
||||
return BrowserESModuleLoader;
|
||||
return BrowserESModuleLoader;
|
||||
|
||||
})));
|
||||
//# sourceMappingURL=browser-es-module-loader.js.map
|
||||
|
|
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';
|
||||
|
||||
export default {
|
||||
entry: 'src/browser-es-module-loader.js',
|
||||
dest: 'dist/browser-es-module-loader.js',
|
||||
input: 'src/browser-es-module-loader.js',
|
||||
output: {
|
||||
file: 'dist/browser-es-module-loader.js',
|
||||
format: 'umd',
|
||||
moduleName: 'BrowserESModuleLoader',
|
||||
sourceMap: true,
|
||||
name: 'BrowserESModuleLoader',
|
||||
sourcemap: true,
|
||||
},
|
||||
|
||||
plugins: [
|
||||
nodeResolve(),
|
||||
],
|
||||
|
||||
// skip rollup warnings (specifically the eval warning)
|
||||
onwarn: function() {}
|
||||
};
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
/*import { transform as babelTransform } from 'babel-core';
|
||||
import babelTransformDynamicImport from 'babel-plugin-syntax-dynamic-import';
|
||||
import babelTransformES2015ModulesSystemJS from 'babel-plugin-transform-es2015-modules-systemjs';*/
|
||||
// Polyfills needed for Babel to function
|
||||
require("core-js");
|
||||
|
||||
// sadly, due to how rollup works, we can't use es6 imports here
|
||||
var babelTransform = require('babel-core').transform;
|
||||
var babelTransformDynamicImport = require('babel-plugin-syntax-dynamic-import');
|
||||
var babelTransformES2015ModulesSystemJS = require('babel-plugin-transform-es2015-modules-systemjs');
|
||||
var babelPresetES2015 = require('babel-preset-es2015');
|
||||
var babelTransform = require('@babel/core').transform;
|
||||
var babelTransformDynamicImport = require('@babel/plugin-syntax-dynamic-import');
|
||||
var babelTransformModulesSystemJS = require('@babel/plugin-transform-modules-systemjs');
|
||||
var babelPresetEnv = require('@babel/preset-env');
|
||||
|
||||
self.onmessage = function (evt) {
|
||||
// transform source with Babel
|
||||
|
@ -17,8 +15,8 @@ self.onmessage = function (evt) {
|
|||
moduleIds: false,
|
||||
sourceMaps: 'inline',
|
||||
babelrc: false,
|
||||
plugins: [babelTransformDynamicImport, babelTransformES2015ModulesSystemJS],
|
||||
presets: [babelPresetES2015],
|
||||
plugins: [babelTransformDynamicImport, babelTransformModulesSystemJS],
|
||||
presets: [ [ babelPresetEnv, { targets: 'ie >= 11' } ] ],
|
||||
});
|
||||
|
||||
self.postMessage({key: evt.data.key, code: output.code, source: evt.data.source});
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import RegisterLoader from 'es-module-loader/core/register-loader.js';
|
||||
import { InternalModuleNamespace as ModuleNamespace } from 'es-module-loader/core/loader-polyfill.js';
|
||||
|
||||
import { baseURI, global, isBrowser } from 'es-module-loader/core/common.js';
|
||||
import { resolveIfNotPlain } from 'es-module-loader/core/resolve.js';
|
||||
|
@ -35,7 +34,7 @@ if (typeof document != 'undefined' && document.getElementsByTagName) {
|
|||
|
||||
// throw so it still shows up in the console
|
||||
throw err;
|
||||
};
|
||||
}
|
||||
|
||||
var ready = function() {
|
||||
document.removeEventListener('DOMContentLoaded', ready, false );
|
||||
|
@ -63,7 +62,7 @@ if (typeof document != 'undefined' && document.getElementsByTagName) {
|
|||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// simple DOM ready
|
||||
if (document.readyState !== 'loading')
|
||||
|
@ -105,10 +104,10 @@ function xhrFetch(url, resolve, reject) {
|
|||
var xhr = new XMLHttpRequest();
|
||||
var load = function(source) {
|
||||
resolve(xhr.responseText);
|
||||
};
|
||||
}
|
||||
var error = function() {
|
||||
reject(new Error('XHR error' + (xhr.status ? ' (' + xhr.status + (xhr.statusText ? ' ' + xhr.statusText : '') + ')' : '') + ' loading ' + url));
|
||||
};
|
||||
}
|
||||
|
||||
xhr.onreadystatechange = function () {
|
||||
if (xhr.readyState === 4) {
|
||||
|
@ -235,7 +234,7 @@ BrowserESModuleLoader.prototype[RegisterLoader.instantiate] = function(key, proc
|
|||
return new Promise(function(resolve, reject) {
|
||||
// anonymous module
|
||||
if (anonSources[key]) {
|
||||
resolve(anonSources[key]);
|
||||
resolve(anonSources[key])
|
||||
anonSources[key] = undefined;
|
||||
}
|
||||
// otherwise we fetch
|
||||
|
|
|
@ -4,7 +4,7 @@ export function shrinkBuf (buf, size) {
|
|||
if (buf.subarray) { return buf.subarray(0, size); }
|
||||
buf.length = size;
|
||||
return buf;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
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 */
|
||||
var Z_NO_FLUSH = 0;
|
||||
var Z_PARTIAL_FLUSH = 1;
|
||||
//var Z_SYNC_FLUSH = 2;
|
||||
var Z_FULL_FLUSH = 3;
|
||||
var Z_FINISH = 4;
|
||||
var Z_BLOCK = 5;
|
||||
//var Z_TREES = 6;
|
||||
export const Z_NO_FLUSH = 0;
|
||||
export const Z_PARTIAL_FLUSH = 1;
|
||||
//export const Z_SYNC_FLUSH = 2;
|
||||
export const Z_FULL_FLUSH = 3;
|
||||
export const Z_FINISH = 4;
|
||||
export const Z_BLOCK = 5;
|
||||
//export const Z_TREES = 6;
|
||||
|
||||
|
||||
/* Return codes for the compression/decompression functions. Negative values
|
||||
* are errors, positive values are used for special but normal events.
|
||||
*/
|
||||
var Z_OK = 0;
|
||||
var Z_STREAM_END = 1;
|
||||
//var Z_NEED_DICT = 2;
|
||||
//var Z_ERRNO = -1;
|
||||
var Z_STREAM_ERROR = -2;
|
||||
var Z_DATA_ERROR = -3;
|
||||
//var Z_MEM_ERROR = -4;
|
||||
var Z_BUF_ERROR = -5;
|
||||
//var Z_VERSION_ERROR = -6;
|
||||
export const Z_OK = 0;
|
||||
export const Z_STREAM_END = 1;
|
||||
//export const Z_NEED_DICT = 2;
|
||||
//export const Z_ERRNO = -1;
|
||||
export const Z_STREAM_ERROR = -2;
|
||||
export const Z_DATA_ERROR = -3;
|
||||
//export const Z_MEM_ERROR = -4;
|
||||
export const Z_BUF_ERROR = -5;
|
||||
//export const Z_VERSION_ERROR = -6;
|
||||
|
||||
|
||||
/* compression levels */
|
||||
//var Z_NO_COMPRESSION = 0;
|
||||
//var Z_BEST_SPEED = 1;
|
||||
//var Z_BEST_COMPRESSION = 9;
|
||||
var Z_DEFAULT_COMPRESSION = -1;
|
||||
//export const Z_NO_COMPRESSION = 0;
|
||||
//export const Z_BEST_SPEED = 1;
|
||||
//export const Z_BEST_COMPRESSION = 9;
|
||||
export const Z_DEFAULT_COMPRESSION = -1;
|
||||
|
||||
|
||||
var Z_FILTERED = 1;
|
||||
var Z_HUFFMAN_ONLY = 2;
|
||||
var Z_RLE = 3;
|
||||
var Z_FIXED = 4;
|
||||
var Z_DEFAULT_STRATEGY = 0;
|
||||
export const Z_FILTERED = 1;
|
||||
export const Z_HUFFMAN_ONLY = 2;
|
||||
export const Z_RLE = 3;
|
||||
export const Z_FIXED = 4;
|
||||
export const Z_DEFAULT_STRATEGY = 0;
|
||||
|
||||
/* Possible values of the data_type field (though see inflate()) */
|
||||
//var Z_BINARY = 0;
|
||||
//var Z_TEXT = 1;
|
||||
//var Z_ASCII = 1; // = Z_TEXT
|
||||
var Z_UNKNOWN = 2;
|
||||
//export const Z_BINARY = 0;
|
||||
//export const Z_TEXT = 1;
|
||||
//export const Z_ASCII = 1; // = Z_TEXT
|
||||
export const Z_UNKNOWN = 2;
|
||||
|
||||
|
||||
/* The deflate compression method */
|
||||
var Z_DEFLATED = 8;
|
||||
export const Z_DEFLATED = 8;
|
||||
|
||||
/*============================================================================*/
|
||||
|
||||
|
|
|
@ -277,7 +277,7 @@ export default function inflate_fast(strm, start) {
|
|||
}
|
||||
else if ((op & 64) === 0) { /* 2nd level distance code */
|
||||
here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
|
||||
continue;
|
||||
continue dodist;
|
||||
}
|
||||
else {
|
||||
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 */
|
||||
here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
|
||||
continue;
|
||||
continue dolen;
|
||||
}
|
||||
else if (op & 32) { /* end-of-block */
|
||||
//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));
|
||||
state.hold = hold;
|
||||
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 */
|
||||
//var Z_NO_FLUSH = 0;
|
||||
//var Z_PARTIAL_FLUSH = 1;
|
||||
//var Z_SYNC_FLUSH = 2;
|
||||
//var Z_FULL_FLUSH = 3;
|
||||
var Z_FINISH = 4;
|
||||
var Z_BLOCK = 5;
|
||||
var Z_TREES = 6;
|
||||
//export const Z_NO_FLUSH = 0;
|
||||
//export const Z_PARTIAL_FLUSH = 1;
|
||||
//export const Z_SYNC_FLUSH = 2;
|
||||
//export const Z_FULL_FLUSH = 3;
|
||||
export const Z_FINISH = 4;
|
||||
export const Z_BLOCK = 5;
|
||||
export const Z_TREES = 6;
|
||||
|
||||
|
||||
/* Return codes for the compression/decompression functions. Negative values
|
||||
* are errors, positive values are used for special but normal events.
|
||||
*/
|
||||
var Z_OK = 0;
|
||||
var Z_STREAM_END = 1;
|
||||
var Z_NEED_DICT = 2;
|
||||
//var Z_ERRNO = -1;
|
||||
var Z_STREAM_ERROR = -2;
|
||||
var Z_DATA_ERROR = -3;
|
||||
var Z_MEM_ERROR = -4;
|
||||
var Z_BUF_ERROR = -5;
|
||||
//var Z_VERSION_ERROR = -6;
|
||||
export const Z_OK = 0;
|
||||
export const Z_STREAM_END = 1;
|
||||
export const Z_NEED_DICT = 2;
|
||||
//export const Z_ERRNO = -1;
|
||||
export const Z_STREAM_ERROR = -2;
|
||||
export const Z_DATA_ERROR = -3;
|
||||
export const Z_MEM_ERROR = -4;
|
||||
export const Z_BUF_ERROR = -5;
|
||||
//export const Z_VERSION_ERROR = -6;
|
||||
|
||||
/* The deflate compression method */
|
||||
var Z_DEFLATED = 8;
|
||||
export const Z_DEFLATED = 8;
|
||||
|
||||
|
||||
/* STATES ====================================================================*/
|
||||
|
|
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