1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2024-11-01 12:04:15 +00:00

Merge pull request #244 from catborise/master

Dockerfile and Requirement Updates
This commit is contained in:
Anatoliy Guskov 2019-06-24 19:59:12 +03:00 committed by GitHub
commit 0ac126ee0d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
56 changed files with 18379 additions and 19848 deletions

View file

@ -1,4 +1,4 @@
FROM phusion/baseimage:0.9.17 FROM phusion/baseimage:0.11
MAINTAINER Jethro Yu <comet.jc@gmail.com> MAINTAINER Jethro Yu <comet.jc@gmail.com>
RUN echo 'APT::Get::Clean=always;' >> /etc/apt/apt.conf.d/99AutomaticClean RUN echo 'APT::Get::Clean=always;' >> /etc/apt/apt.conf.d/99AutomaticClean

View file

@ -1,7 +1,7 @@
Django==1.11.20 Django==1.11.21
websockify==0.8.0 websockify==0.8.0
gunicorn==19.9.0 gunicorn==19.9.0
lxml==4.2.5 lxml==4.2.5
libvirt-python==4.10.0 libvirt-python==5.3.0
pytz pytz
rwlock rwlock

View file

@ -3,7 +3,7 @@
<html> <html>
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<link rel="shortcut icon" href="{% static "img/favicon.ico" %}"> <link rel="shortcut icon" href="{% static "favicon.ico" %}">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{% static "css/bootstrap.min.css" %}"> <link rel="stylesheet" href="{% static "css/bootstrap.min.css" %}">
<link href="{% static "css/webvirtcloud.css" %}" rel="stylesheet"> <link href="{% static "css/webvirtcloud.css" %}" rel="stylesheet">

View file

@ -5,12 +5,9 @@
{% block head %} {% block head %}
<!-- <!--
noVNC example: simple example using default UI noVNC example: simple example using default UI
Copyright (C) 2012 Joel Martin Copyright (C) 2018 The noVNC Authors
Copyright (C) 2016 Samuel Mannehed for Cendio AB
Copyright (C) 2016 Pierre Ossman for Cendio AB
noVNC is licensed under the MPL 2.0 (see LICENSE.txt) noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
This file is licensed under the 2-Clause BSD license (see LICENSE.txt). This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
Connect parameters are provided in query string: Connect parameters are provided in query string:
http://example.com/?host=HOST&port=PORT&encrypt=1 http://example.com/?host=HOST&port=PORT&encrypt=1
or the fragment: or the fragment:
@ -20,10 +17,6 @@
<meta charset="utf-8" /> <meta charset="utf-8" />
<!-- Always force latest IE rendering engine (even in intranet) & Chrome Frame
Remove this if you use the .htaccess -->
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<!-- Icons (see Makefile for what the sizes are for) --> <!-- Icons (see Makefile for what the sizes are for) -->
<link rel="icon" sizes="16x16" type="image/png" href="{% static "js/novnc/app/images/icons/novnc-16x16.png" %}"> <link rel="icon" sizes="16x16" type="image/png" href="{% static "js/novnc/app/images/icons/novnc-16x16.png" %}">
<link rel="icon" sizes="24x24" type="image/png" href="{% static "js/novnc/app/images/icons/novnc-24x24.png" %}"> <link rel="icon" sizes="24x24" type="image/png" href="{% static "js/novnc/app/images/icons/novnc-24x24.png" %}">
@ -140,6 +133,9 @@
<input type="image" alt="Alt" src="{% static "js/novnc/app/images/alt.svg" %}" <input type="image" alt="Alt" src="{% static "js/novnc/app/images/alt.svg" %}"
id="noVNC_toggle_alt_button" class="noVNC_button" id="noVNC_toggle_alt_button" class="noVNC_button"
title="Toggle Alt"/> title="Toggle Alt"/>
<input type="image" alt="Windows" src="{% static "js/novnc/app/images/windows.svg" %}"
id="noVNC_toggle_windows_button" class="noVNC_button"
title="Toggle Windows">
<input type="image" alt="Tab" src="{% static "js/novnc/app/images/tab.svg" %}" <input type="image" alt="Tab" src="{% static "js/novnc/app/images/tab.svg" %}"
id="noVNC_send_tab_button" class="noVNC_button" id="noVNC_send_tab_button" class="noVNC_button"
title="Send Tab"/> title="Send Tab"/>
@ -254,6 +250,10 @@
<label for="noVNC_setting_reconnect_delay">Reconnect Delay (ms):</label> <label for="noVNC_setting_reconnect_delay">Reconnect Delay (ms):</label>
<input id="noVNC_setting_reconnect_delay" type="number" /> <input id="noVNC_setting_reconnect_delay" type="number" />
</li> </li>
<li><hr></li>
<li>
<label><input id="noVNC_setting_show_dot" type="checkbox"> Show Dot when No Cursor</label>
</li>
<li><hr></li> <li><hr></li>
<!-- Logging selection dropdown --> <!-- Logging selection dropdown -->
<li> <li>

View file

@ -689,8 +689,6 @@
}); });
}); });
function get_cust_vols(compute_id, pool) { function get_cust_vols(compute_id, pool) {
get_vol_url = "/computes/" + compute_id + "/storage/" + pool + "/volumes"; get_vol_url = "/computes/" + compute_id + "/storage/" + pool + "/volumes";
$.getJSON(get_vol_url, function (data) { $.getJSON(get_vol_url, function (data) {
@ -716,7 +714,7 @@
} }
function get_disk_bus_choices(compute_id, dev_idx, disk_type){ function get_disk_bus_choices(compute_id, dev_idx, disk_type){
get_diskBus_url = "/computes/" + compute_id + "/disk/" + disk_type + "/buses"; get_diskBus_url = "/computes/" + compute_id + "/disk/" + disk_type + "/buses";
$.getJSON(get_diskBus_url, function (data) { $.getJSON(get_diskBus_url, function (data) {

View file

@ -1,4 +1,4 @@
-r ../conf/requirements.txt -r ../conf/requirements.txt
pep8==1.7.1 pep8==1.7.1
pyflakes==2.0.0 pyflakes==2.1.1
pylint==1.9.2 pylint==1.9.4

View file

@ -84,11 +84,7 @@
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
{% ifequal status 5 %} <button type="submit" class="btn btn-lg btn-success pull-right" name="add_new_vol">{% trans "Add Volume" %}</button>
<button type="submit" class="btn btn-lg btn-success pull-right" name="add_new_vol">{% trans "Add Volume" %}</button>
{% else %}
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Add Volume" %}</button>
{% endifequal %}
</div> </div>
</form> </form>
</div> </div>
@ -142,11 +138,7 @@
</div> </div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
{% ifequal status 5 %} <button type="submit" class="btn btn-lg btn-success pull-right" name="add_existing_vol">{% trans "Add Volume" %}</button>
<button type="submit" class="btn btn-lg btn-success pull-right" name="add_existing_vol">{% trans "Add Volume" %}</button>
{% else %}
<button class="btn btn-lg btn-success pull-right disabled">{% trans "Add Volume" %}</button>
{% endifequal %}
</div> </div>
</form> </form>
</div> </div>

View file

@ -2,21 +2,23 @@
// native support in the browsers, so that our error handler // native support in the browsers, so that our error handler
// can catch script-loading errors. // can catch script-loading errors.
// No ES6 can be used in this file since it's used for the translation
/* eslint-disable prefer-arrow-callback */
(function(){ (function _scope() {
"use strict"; "use strict";
// Fallback for all uncought errors // Fallback for all uncought errors
function handleError (event, err) { function handleError(event, err) {
try { try {
var msg = document.getElementById('noVNC_fallback_errormsg'); const msg = document.getElementById('noVNC_fallback_errormsg');
// Only show the initial error // Only show the initial error
if (msg.hasChildNodes()) { if (msg.hasChildNodes()) {
return false; return false;
} }
var div = document.createElement("div"); let div = document.createElement("div");
div.classList.add('noVNC_message'); div.classList.add('noVNC_message');
div.appendChild(document.createTextNode(event.message)); div.appendChild(document.createTextNode(event.message));
msg.appendChild(div); msg.appendChild(div);
@ -24,7 +26,7 @@
if (event.filename) { if (event.filename) {
div = document.createElement("div"); div = document.createElement("div");
div.className = 'noVNC_location'; div.className = 'noVNC_location';
var text = event.filename; let text = event.filename;
if (event.lineno !== undefined) { if (event.lineno !== undefined) {
text += ":" + event.lineno; text += ":" + event.lineno;
if (event.colno !== undefined) { if (event.colno !== undefined) {
@ -35,7 +37,7 @@
msg.appendChild(div); msg.appendChild(div);
} }
if (err && (err.stack !== undefined)) { if (err && err.stack) {
div = document.createElement("div"); div = document.createElement("div");
div.className = 'noVNC_stack'; div.className = 'noVNC_stack';
div.appendChild(document.createTextNode(err.stack)); div.appendChild(document.createTextNode(err.stack));
@ -51,6 +53,6 @@
// from being printed to the browser console. // from being printed to the browser console.
return false; return false;
} }
window.addEventListener('error', function (evt) { handleError(evt, evt.error); }); window.addEventListener('error', function onerror(evt) { handleError(evt, evt.error); });
window.addEventListener('unhandledrejection', function (evt) { handleError(evt.reason, evt.reason); }); window.addEventListener('unhandledrejection', function onreject(evt) { handleError(evt.reason, evt.reason); });
})(); })();

View file

@ -0,0 +1,85 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="svg2"
inkscape:export-ydpi="90"
inkscape:export-xdpi="90"
sodipodi:docname="windows.svg"
inkscape:export-filename="/home/ossman/devel/noVNC/images/drag.png"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
x="0px"
y="0px"
viewBox="-293 384 25 23"
xml:space="preserve"
width="25"
height="23"><metadata
id="metadata21"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs19" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1017"
id="namedview17"
showgrid="false"
inkscape:pagecheckerboard="true"
inkscape:zoom="9.44"
inkscape:cx="-0.84745763"
inkscape:cy="12.5"
inkscape:window-x="2552"
inkscape:window-y="122"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<style
type="text/css"
id="style2">
.st0{fill:#FFFFFF;}
</style>
<g
id="g14"
transform="matrix(1.2624869,0,0,1.3601695,73.614445,-144.84322)">
<g
id="g12">
<path
class="st0"
d="m -277.4,396 c -0.7,0 -1.3,0 -2,0 -0.4,0 -0.5,-0.1 -0.5,-0.5 0,-1 0,-2 0,-3 0,-0.3 0.2,-0.5 0.5,-0.5 1.3,-0.1 2.6,-0.3 3.9,-0.4 0.4,0 0.7,0.1 0.7,0.6 0,1.1 0,2.2 0,3.3 0,0.4 -0.2,0.6 -0.6,0.6 -0.7,-0.1 -1.4,-0.1 -2,-0.1 z"
id="path4"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
<path
class="st0"
d="m -274.9,399.3 c 0,0.6 0,1.1 0,1.7 0,0.4 -0.1,0.6 -0.6,0.6 -1.4,-0.1 -2.8,-0.3 -4.1,-0.4 -0.3,0 -0.4,-0.3 -0.4,-0.5 0,-1 0,-2 0,-3 0,-0.4 0.2,-0.5 0.6,-0.5 1.3,0 2.6,0 3.9,0 0.5,0 0.6,0.2 0.6,0.6 0,0.4 0,0.9 0,1.5 z"
id="path6"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
<path
class="st0"
d="m -283.5,396 c -0.6,0 -1.3,0 -1.9,0 -0.4,0 -0.6,-0.1 -0.6,-0.6 0,-0.8 0,-1.5 0,-2.3 0,-0.4 0.2,-0.6 0.6,-0.7 1.3,-0.1 2.7,-0.3 4,-0.4 0.4,0 0.5,0.1 0.5,0.5 0,1 0,1.9 0,2.9 0,0.4 -0.2,0.5 -0.5,0.5 -0.8,0.1 -1.5,0.1 -2.1,0.1 z"
id="path8"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
<path
class="st0"
d="m -283.5,397 c 0.6,0 1.3,0 1.9,0 0.4,0 0.6,0.1 0.6,0.5 0,1 0,1.9 0,2.9 0,0.4 -0.2,0.5 -0.5,0.5 -1.3,-0.1 -2.7,-0.3 -4,-0.4 -0.4,0 -0.6,-0.2 -0.6,-0.7 0,-0.7 0,-1.5 0,-2.2 0,-0.5 0.2,-0.7 0.7,-0.7 0.6,0.1 1.2,0.1 1.9,0.1 z"
id="path10"
inkscape:connector-curvature="0"
style="fill:#ffffff" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -0,0 +1,71 @@
{
"Connecting...": "Připojení...",
"Disconnecting...": "Odpojení...",
"Reconnecting...": "Obnova připojení...",
"Internal error": "Vnitřní chyba",
"Must set host": "Hostitel musí být nastavení",
"Connected (encrypted) to ": "Připojení (šifrované) k ",
"Connected (unencrypted) to ": "Připojení (nešifrované) k ",
"Something went wrong, connection is closed": "Něco se pokazilo, odpojeno",
"Failed to connect to server": "Chyba připojení k serveru",
"Disconnected": "Odpojeno",
"New connection has been rejected with reason: ": "Nové připojení bylo odmítnuto s odůvodněním: ",
"New connection has been rejected": "Nové připojení bylo odmítnuto",
"Password is required": "Je vyžadováno heslo",
"noVNC encountered an error:": "noVNC narazilo na chybu:",
"Hide/Show the control bar": "Skrýt/zobrazit ovládací panel",
"Move/Drag Viewport": "Přesunout/přetáhnout výřez",
"viewport drag": "přesun výřezu",
"Active Mouse Button": "Aktivní tlačítka myši",
"No mousebutton": "Žádné",
"Left mousebutton": "Levé tlačítko myši",
"Middle mousebutton": "Prostřední tlačítko myši",
"Right mousebutton": "Pravé tlačítko myši",
"Keyboard": "Klávesnice",
"Show Keyboard": "Zobrazit klávesnici",
"Extra keys": "Extra klávesy",
"Show Extra Keys": "Zobrazit extra klávesy",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Přepnout Ctrl",
"Alt": "Alt",
"Toggle Alt": "Přepnout Alt",
"Send Tab": "Odeslat tabulátor",
"Tab": "Tab",
"Esc": "Esc",
"Send Escape": "Odeslat Esc",
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
"Send Ctrl-Alt-Del": "Poslat Ctrl-Alt-Del",
"Shutdown/Reboot": "Vypnutí/Restart",
"Shutdown/Reboot...": "Vypnutí/Restart...",
"Power": "Napájení",
"Shutdown": "Vypnout",
"Reboot": "Restart",
"Reset": "Reset",
"Clipboard": "Schránka",
"Clear": "Vymazat",
"Fullscreen": "Celá obrazovka",
"Settings": "Nastavení",
"Shared Mode": "Sdílený režim",
"View Only": "Pouze prohlížení",
"Clip to Window": "Přizpůsobit oknu",
"Scaling Mode:": "Přizpůsobení velikosti",
"None": "Žádné",
"Local Scaling": "Místní",
"Remote Resizing": "Vzdálené",
"Advanced": "Pokročilé",
"Repeater ID:": "ID opakovače",
"WebSocket": "WebSocket",
"Encrypt": "Šifrování:",
"Host:": "Hostitel:",
"Port:": "Port:",
"Path:": "Cesta",
"Automatic Reconnect": "Automatická obnova připojení",
"Reconnect Delay (ms):": "Zpoždění připojení (ms)",
"Show Dot when No Cursor": "Tečka místo chybějícího kurzoru myši",
"Logging:": "Logování:",
"Disconnect": "Odpojit",
"Connect": "Připojit",
"Password:": "Heslo",
"Send Password": "Odeslat heslo",
"Cancel": "Zrušit"
}

View file

@ -0,0 +1,70 @@
{
"Connecting...": "연결중...",
"Disconnecting...": "연결 해제중...",
"Reconnecting...": "재연결중...",
"Internal error": "내부 오류",
"Must set host": "호스트는 설정되어야 합니다.",
"Connected (encrypted) to ": "다음과 (암호화되어) 연결되었습니다:",
"Connected (unencrypted) to ": "다음과 (암호화 없이) 연결되었습니다:",
"Something went wrong, connection is closed": "무언가 잘못되었습니다, 연결이 닫혔습니다.",
"Failed to connect to server": "서버에 연결하지 못했습니다.",
"Disconnected": "연결이 해제되었습니다.",
"New connection has been rejected with reason: ": "새 연결이 다음 이유로 거부되었습니다:",
"New connection has been rejected": "새 연결이 거부되었습니다.",
"Password is required": "비밀번호가 필요합니다.",
"noVNC encountered an error:": "noVNC에 오류가 발생했습니다:",
"Hide/Show the control bar": "컨트롤 바 숨기기/보이기",
"Move/Drag Viewport": "움직이기/드래그 뷰포트",
"viewport drag": "뷰포트 드래그",
"Active Mouse Button": "마우스 버튼 활성화",
"No mousebutton": "마우스 버튼 없음",
"Left mousebutton": "왼쪽 마우스 버튼",
"Middle mousebutton": "중간 마우스 버튼",
"Right mousebutton": "오른쪽 마우스 버튼",
"Keyboard": "키보드",
"Show Keyboard": "키보드 보이기",
"Extra keys": "기타 키들",
"Show Extra Keys": "기타 키들 보이기",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Ctrl 켜기/끄기",
"Alt": "Alt",
"Toggle Alt": "Alt 켜기/끄기",
"Send Tab": "Tab 보내기",
"Tab": "Tab",
"Esc": "Esc",
"Send Escape": "Esc 보내기",
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
"Send Ctrl-Alt-Del": "Ctrl+Alt+Del 보내기",
"Shutdown/Reboot": "셧다운/리붓",
"Shutdown/Reboot...": "셧다운/리붓...",
"Power": "전원",
"Shutdown": "셧다운",
"Reboot": "리붓",
"Reset": "리셋",
"Clipboard": "클립보드",
"Clear": "지우기",
"Fullscreen": "전체화면",
"Settings": "설정",
"Shared Mode": "공유 모드",
"View Only": "보기 전용",
"Clip to Window": "창에 클립",
"Scaling Mode:": "스케일링 모드:",
"None": "없음",
"Local Scaling": "로컬 스케일링",
"Remote Resizing": "원격 크기 조절",
"Advanced": "고급",
"Repeater ID:": "중계 ID",
"WebSocket": "웹소켓",
"Encrypt": "암호화",
"Host:": "호스트:",
"Port:": "포트:",
"Path:": "위치:",
"Automatic Reconnect": "자동 재연결",
"Reconnect Delay (ms):": "재연결 지연 시간 (ms)",
"Logging:": "로깅",
"Disconnect": "연결 해제",
"Connect": "연결",
"Password:": "비밀번호:",
"Send Password": "비밀번호 전송",
"Cancel": "취소"
}

View file

@ -1,13 +1,17 @@
{ {
"Connecting...": "Verbinden...", "Connecting...": "Verbinden...",
"Disconnecting...": "Verbinding verbreken...",
"Reconnecting...": "Opnieuw verbinding maken...",
"Internal error": "Interne fout",
"Must set host": "Host moeten worden ingesteld",
"Connected (encrypted) to ": "Verbonden (versleuteld) met ", "Connected (encrypted) to ": "Verbonden (versleuteld) met ",
"Connected (unencrypted) to ": "Verbonden (onversleuteld) met ", "Connected (unencrypted) to ": "Verbonden (onversleuteld) met ",
"Disconnecting...": "Verbinding verbreken...", "Something went wrong, connection is closed": "Er iets fout gelopen, verbinding werd verbroken",
"Failed to connect to server": "Verbinding maken met server is mislukt",
"Disconnected": "Verbinding verbroken", "Disconnected": "Verbinding verbroken",
"Must set host": "Host moeten worden ingesteld", "New connection has been rejected with reason: ": "Nieuwe verbinding is geweigerd omwille van de volgende reden: ",
"Reconnecting...": "Opnieuw verbinding maken...", "New connection has been rejected": "Nieuwe verbinding is geweigerd",
"Password is required": "Wachtwoord is vereist", "Password is required": "Wachtwoord is vereist",
"Disconnect timeout": "Timeout tijdens verbreken van verbinding",
"noVNC encountered an error:": "noVNC heeft een fout bemerkt:", "noVNC encountered an error:": "noVNC heeft een fout bemerkt:",
"Hide/Show the control bar": "Verberg/Toon de bedieningsbalk", "Hide/Show the control bar": "Verberg/Toon de bedieningsbalk",
"Move/Drag Viewport": "Verplaats/Versleep Kijkvenster", "Move/Drag Viewport": "Verplaats/Versleep Kijkvenster",
@ -22,9 +26,11 @@
"Extra keys": "Extra toetsen", "Extra keys": "Extra toetsen",
"Show Extra Keys": "Toon Extra Toetsen", "Show Extra Keys": "Toon Extra Toetsen",
"Ctrl": "Ctrl", "Ctrl": "Ctrl",
"Toggle Ctrl": "Ctrl aan/uitzetten", "Toggle Ctrl": "Ctrl omschakelen",
"Alt": "Alt", "Alt": "Alt",
"Toggle Alt": "Alt aan/uitzetten", "Toggle Alt": "Alt omschakelen",
"Toggle Windows": "Windows omschakelen",
"Windows": "Windows",
"Send Tab": "Tab Sturen", "Send Tab": "Tab Sturen",
"Tab": "Tab", "Tab": "Tab",
"Esc": "Esc", "Esc": "Esc",
@ -47,10 +53,8 @@
"Scaling Mode:": "Schaalmodus:", "Scaling Mode:": "Schaalmodus:",
"None": "Geen", "None": "Geen",
"Local Scaling": "Lokaal Schalen", "Local Scaling": "Lokaal Schalen",
"Local Downscaling": "Lokaal Neerschalen",
"Remote Resizing": "Op Afstand Formaat Wijzigen", "Remote Resizing": "Op Afstand Formaat Wijzigen",
"Advanced": "Geavanceerd", "Advanced": "Geavanceerd",
"Local Cursor": "Lokale Cursor",
"Repeater ID:": "Repeater ID:", "Repeater ID:": "Repeater ID:",
"WebSocket": "WebSocket", "WebSocket": "WebSocket",
"Encrypt": "Versleutelen", "Encrypt": "Versleutelen",
@ -59,10 +63,11 @@
"Path:": "Pad:", "Path:": "Pad:",
"Automatic Reconnect": "Automatisch Opnieuw Verbinden", "Automatic Reconnect": "Automatisch Opnieuw Verbinden",
"Reconnect Delay (ms):": "Vertraging voor Opnieuw Verbinden (ms):", "Reconnect Delay (ms):": "Vertraging voor Opnieuw Verbinden (ms):",
"Show Dot when No Cursor": "Geef stip weer indien geen cursor",
"Logging:": "Logmeldingen:", "Logging:": "Logmeldingen:",
"Disconnect": "Verbinding verbreken", "Disconnect": "Verbinding verbreken",
"Connect": "Verbinden", "Connect": "Verbinden",
"Password:": "Wachtwoord:", "Password:": "Wachtwoord:",
"Cancel": "Annuleren", "Send Password": "Verzend Wachtwoord:",
"Canvas not supported.": "Canvas wordt niet ondersteund." "Cancel": "Annuleren"
} }

View file

@ -0,0 +1,73 @@
{
"Connecting...": "Подключение...",
"Disconnecting...": "Отключение...",
"Reconnecting...": "Переподключение...",
"Internal error": "Внутренняя ошибка",
"Must set host": "Задайте имя сервера или IP",
"Connected (encrypted) to ": "Подключено (с шифрованием) к ",
"Connected (unencrypted) to ": "Подключено (без шифрования) к ",
"Something went wrong, connection is closed": "Что-то пошло не так, подключение разорвано",
"Failed to connect to server": "Ошибка подключения к серверу",
"Disconnected": "Отключено",
"New connection has been rejected with reason: ": "Подключиться не удалось: ",
"New connection has been rejected": "Подключиться не удалось",
"Password is required": "Требуется пароль",
"noVNC encountered an error:": "Ошибка noVNC: ",
"Hide/Show the control bar": "Скрыть/Показать контрольную панель",
"Move/Drag Viewport": "Переместить окно",
"viewport drag": "Переместить окно",
"Active Mouse Button": "Активировать кнопки мыши",
"No mousebutton": "Отключить кнопки мыши",
"Left mousebutton": "Левая кнопка мыши",
"Middle mousebutton": "Средняя кнопка мыши",
"Right mousebutton": "Правая кнопка мыши",
"Keyboard": "Клавиатура",
"Show Keyboard": "Показать клавиатуру",
"Extra keys": "Доп. кнопки",
"Show Extra Keys": "Показать дополнительные кнопки",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Передать нажатие Ctrl",
"Alt": "Alt",
"Toggle Alt": "Передать нажатие Alt",
"Toggle Windows": "Переключение вкладок",
"Windows": "Вкладка",
"Send Tab": "Передать нажатие Tab",
"Tab": "Tab",
"Esc": "Esc",
"Send Escape": "Передать нажатие Escape",
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
"Send Ctrl-Alt-Del": "Передать нажатие Ctrl-Alt-Del",
"Shutdown/Reboot": "Выключить/Перезагрузить",
"Shutdown/Reboot...": "Выключить/Перезагрузить...",
"Power": "Питание",
"Shutdown": "Выключить",
"Reboot": "Перезагрузить",
"Reset": "Сброс",
"Clipboard": "Буфер обмена",
"Clear": "Очистить",
"Fullscreen": "Во весь экран",
"Settings": "Настройки",
"Shared Mode": "Общий режим",
"View Only": "Просмотр",
"Clip to Window": "В окно",
"Scaling Mode:": "Масштаб:",
"None": "Нет",
"Local Scaling": "Локльный масштаб",
"Remote Resizing": "Удаленный масштаб",
"Advanced": "Дополнительно",
"Repeater ID:": "Идентификатор ID:",
"WebSocket": "WebSocket",
"Encrypt": "Шифрование",
"Host:": "Сервер:",
"Port:": "Порт:",
"Path:": "Путь:",
"Automatic Reconnect": "Автоматическое переподключение",
"Reconnect Delay (ms):": "Задержка переподключения (мс):",
"Show Dot when No Cursor": "Показать точку вместо курсора",
"Logging:": "Лог:",
"Disconnect": "Отключение",
"Connect": "Подключение",
"Password:": "Пароль:",
"Send Password": "Пароль: ",
"Cancel": "Выход"
}

View file

@ -1,13 +1,17 @@
{ {
"Connecting...": "Ansluter...", "Connecting...": "Ansluter...",
"Disconnecting...": "Kopplar ner...",
"Reconnecting...": "Återansluter...",
"Internal error": "Internt fel",
"Must set host": "Du måste specifiera en värd",
"Connected (encrypted) to ": "Ansluten (krypterat) till ", "Connected (encrypted) to ": "Ansluten (krypterat) till ",
"Connected (unencrypted) to ": "Ansluten (okrypterat) till ", "Connected (unencrypted) to ": "Ansluten (okrypterat) till ",
"Disconnecting...": "Kopplar ner...", "Something went wrong, connection is closed": "Något gick fel, anslutningen avslutades",
"Failed to connect to server": "Misslyckades att ansluta till servern",
"Disconnected": "Frånkopplad", "Disconnected": "Frånkopplad",
"Must set host": "Du måste specifiera en värd", "New connection has been rejected with reason: ": "Ny anslutning har blivit nekad med följande skäl: ",
"Reconnecting...": "Återansluter...", "New connection has been rejected": "Ny anslutning har blivit nekad",
"Password is required": "Lösenord krävs", "Password is required": "Lösenord krävs",
"Disconnect timeout": "Det tog för lång tid att koppla ner",
"noVNC encountered an error:": "noVNC stötte på ett problem:", "noVNC encountered an error:": "noVNC stötte på ett problem:",
"Hide/Show the control bar": "Göm/Visa kontrollbaren", "Hide/Show the control bar": "Göm/Visa kontrollbaren",
"Move/Drag Viewport": "Flytta/Dra Vyn", "Move/Drag Viewport": "Flytta/Dra Vyn",
@ -25,6 +29,8 @@
"Toggle Ctrl": "Växla Ctrl", "Toggle Ctrl": "Växla Ctrl",
"Alt": "Alt", "Alt": "Alt",
"Toggle Alt": "Växla Alt", "Toggle Alt": "Växla Alt",
"Toggle Windows": "Växla Windows",
"Windows": "Windows",
"Send Tab": "Skicka Tab", "Send Tab": "Skicka Tab",
"Tab": "Tab", "Tab": "Tab",
"Esc": "Esc", "Esc": "Esc",
@ -47,10 +53,8 @@
"Scaling Mode:": "Skalningsläge:", "Scaling Mode:": "Skalningsläge:",
"None": "Ingen", "None": "Ingen",
"Local Scaling": "Lokal Skalning", "Local Scaling": "Lokal Skalning",
"Local Downscaling": "Lokal Nedskalning",
"Remote Resizing": "Ändra Storlek", "Remote Resizing": "Ändra Storlek",
"Advanced": "Avancerat", "Advanced": "Avancerat",
"Local Cursor": "Lokal Muspekare",
"Repeater ID:": "Repeater-ID:", "Repeater ID:": "Repeater-ID:",
"WebSocket": "WebSocket", "WebSocket": "WebSocket",
"Encrypt": "Kryptera", "Encrypt": "Kryptera",
@ -59,10 +63,11 @@
"Path:": "Sökväg:", "Path:": "Sökväg:",
"Automatic Reconnect": "Automatisk Återanslutning", "Automatic Reconnect": "Automatisk Återanslutning",
"Reconnect Delay (ms):": "Fördröjning (ms):", "Reconnect Delay (ms):": "Fördröjning (ms):",
"Show Dot when No Cursor": "Visa prick när ingen muspekare finns",
"Logging:": "Loggning:", "Logging:": "Loggning:",
"Disconnect": "Koppla från", "Disconnect": "Koppla från",
"Connect": "Anslut", "Connect": "Anslut",
"Password:": "Lösenord:", "Password:": "Lösenord:",
"Cancel": "Avbryt", "Send Password": "Skicka lösenord",
"Canvas not supported.": "Canvas stöds ej" "Cancel": "Avbryt"
} }

View file

@ -0,0 +1,69 @@
{
"Connecting...": "链接中...",
"Disconnecting...": "正在中断连接...",
"Reconnecting...": "重新链接中...",
"Internal error": "内部错误",
"Must set host": "请提供主机名",
"Connected (encrypted) to ": "已加密链接到",
"Connected (unencrypted) to ": "未加密链接到",
"Something went wrong, connection is closed": "发生错误,链接已关闭",
"Failed to connect to server": "无法链接到服务器",
"Disconnected": "链接已中断",
"New connection has been rejected with reason: ": "链接被拒绝,原因:",
"New connection has been rejected": "链接被拒绝",
"Password is required": "请提供密码",
"noVNC encountered an error:": "noVNC 遇到一个错误:",
"Hide/Show the control bar": "显示/隐藏控制列",
"Move/Drag Viewport": "拖放显示范围",
"viewport drag": "显示范围拖放",
"Active Mouse Button": "启动鼠标按鍵",
"No mousebutton": "禁用鼠标按鍵",
"Left mousebutton": "鼠标左鍵",
"Middle mousebutton": "鼠标中鍵",
"Right mousebutton": "鼠标右鍵",
"Keyboard": "键盘",
"Show Keyboard": "显示键盘",
"Extra keys": "额外按键",
"Show Extra Keys": "显示额外按键",
"Ctrl": "Ctrl",
"Toggle Ctrl": "切换 Ctrl",
"Alt": "Alt",
"Toggle Alt": "切换 Alt",
"Send Tab": "发送 Tab 键",
"Tab": "Tab",
"Esc": "Esc",
"Send Escape": "发送 Escape 键",
"Ctrl+Alt+Del": "Ctrl-Alt-Del",
"Send Ctrl-Alt-Del": "发送 Ctrl-Alt-Del 键",
"Shutdown/Reboot": "关机/重新启动",
"Shutdown/Reboot...": "关机/重新启动...",
"Power": "电源",
"Shutdown": "关机",
"Reboot": "重新启动",
"Reset": "重置",
"Clipboard": "剪贴板",
"Clear": "清除",
"Fullscreen": "全屏幕",
"Settings": "设置",
"Shared Mode": "分享模式",
"View Only": "仅检视",
"Clip to Window": "限制/裁切窗口大小",
"Scaling Mode:": "缩放模式:",
"None": "无",
"Local Scaling": "本地缩放",
"Remote Resizing": "远程调整大小",
"Advanced": "高级",
"Repeater ID:": "中继站 ID",
"WebSocket": "WebSocket",
"Encrypt": "加密",
"Host:": "主机:",
"Port:": "端口:",
"Path:": "路径:",
"Automatic Reconnect": "自动重新链接",
"Reconnect Delay (ms):": "重新链接间隔 (ms)",
"Logging:": "日志级别:",
"Disconnect": "终端链接",
"Connect": "链接",
"Password:": "密码:",
"Cancel": "取消"
}

View file

@ -1,6 +1,6 @@
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin * Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt) * Licensed under MPL 2.0 (see LICENSE.txt)
* *
* See README.md for usage and integration instructions. * See README.md for usage and integration instructions.
@ -10,36 +10,35 @@
* Localization Utilities * Localization Utilities
*/ */
export function Localizer() { export class Localizer {
// Currently configured language constructor() {
this.language = 'en'; // Currently configured language
this.language = 'en';
// Current dictionary of translations // Current dictionary of translations
this.dictionary = undefined; this.dictionary = undefined;
} }
Localizer.prototype = {
// Configure suitable language based on user preferences // Configure suitable language based on user preferences
setup: function (supportedLanguages) { setup(supportedLanguages) {
var userLanguages;
this.language = 'en'; // Default: US English this.language = 'en'; // Default: US English
/* /*
* Navigator.languages only available in Chrome (32+) and FireFox (32+) * Navigator.languages only available in Chrome (32+) and FireFox (32+)
* Fall back to navigator.language for other browsers * Fall back to navigator.language for other browsers
*/ */
let userLanguages;
if (typeof window.navigator.languages == 'object') { if (typeof window.navigator.languages == 'object') {
userLanguages = window.navigator.languages; userLanguages = window.navigator.languages;
} else { } else {
userLanguages = [navigator.language || navigator.userLanguage]; userLanguages = [navigator.language || navigator.userLanguage];
} }
for (var i = 0;i < userLanguages.length;i++) { for (let i = 0;i < userLanguages.length;i++) {
var userLang = userLanguages[i]; const userLang = userLanguages[i]
userLang = userLang.toLowerCase(); .toLowerCase()
userLang = userLang.replace("_", "-"); .replace("_", "-")
userLang = userLang.split("-"); .split("-");
// Built-in default? // Built-in default?
if ((userLang[0] === 'en') && if ((userLang[0] === 'en') &&
@ -48,66 +47,69 @@ Localizer.prototype = {
} }
// First pass: perfect match // First pass: perfect match
for (var j = 0;j < supportedLanguages.length;j++) { for (let j = 0; j < supportedLanguages.length; j++) {
var supLang = supportedLanguages[j]; const supLang = supportedLanguages[j]
supLang = supLang.toLowerCase(); .toLowerCase()
supLang = supLang.replace("_", "-"); .replace("_", "-")
supLang = supLang.split("-"); .split("-");
if (userLang[0] !== supLang[0]) if (userLang[0] !== supLang[0]) {
continue; continue;
if (userLang[1] !== supLang[1]) }
if (userLang[1] !== supLang[1]) {
continue; continue;
}
this.language = supportedLanguages[j]; this.language = supportedLanguages[j];
return; return;
} }
// Second pass: fallback // Second pass: fallback
for (var j = 0;j < supportedLanguages.length;j++) { for (let j = 0;j < supportedLanguages.length;j++) {
supLang = supportedLanguages[j]; const supLang = supportedLanguages[j]
supLang = supLang.toLowerCase(); .toLowerCase()
supLang = supLang.replace("_", "-"); .replace("_", "-")
supLang = supLang.split("-"); .split("-");
if (userLang[0] !== supLang[0]) if (userLang[0] !== supLang[0]) {
continue; continue;
if (supLang[1] !== undefined) }
if (supLang[1] !== undefined) {
continue; continue;
}
this.language = supportedLanguages[j]; this.language = supportedLanguages[j];
return; return;
} }
} }
}, }
// Retrieve localised text // Retrieve localised text
get: function (id) { get(id) {
if (typeof this.dictionary !== 'undefined' && this.dictionary[id]) { if (typeof this.dictionary !== 'undefined' && this.dictionary[id]) {
return this.dictionary[id]; return this.dictionary[id];
} else { } else {
return id; return id;
} }
}, }
// Traverses the DOM and translates relevant fields // Traverses the DOM and translates relevant fields
// See https://html.spec.whatwg.org/multipage/dom.html#attr-translate // See https://html.spec.whatwg.org/multipage/dom.html#attr-translate
translateDOM: function () { translateDOM() {
var self = this; const self = this;
function process(elem, enabled) { function process(elem, enabled) {
function isAnyOf(searchElement, items) { function isAnyOf(searchElement, items) {
return items.indexOf(searchElement) !== -1; return items.indexOf(searchElement) !== -1;
} }
function translateAttribute(elem, attr) { function translateAttribute(elem, attr) {
var str = elem.getAttribute(attr); const str = self.get(elem.getAttribute(attr));
str = self.get(str);
elem.setAttribute(attr, str); elem.setAttribute(attr, str);
} }
function translateTextNode(node) { function translateTextNode(node) {
var str = node.data.trim(); const str = self.get(node.data.trim());
str = self.get(str);
node.data = str; node.data = str;
} }
@ -134,7 +136,7 @@ Localizer.prototype = {
} }
if (elem.hasAttribute("label") && if (elem.hasAttribute("label") &&
isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP", isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP",
"OPTION", "TRACK"])) { "OPTION", "TRACK"])) {
translateAttribute(elem, "label"); translateAttribute(elem, "label");
} }
// FIXME: Should update "lang" // FIXME: Should update "lang"
@ -152,8 +154,8 @@ Localizer.prototype = {
} }
} }
for (var i = 0;i < elem.childNodes.length;i++) { for (let i = 0; i < elem.childNodes.length; i++) {
var node = elem.childNodes[i]; const node = elem.childNodes[i];
if (node.nodeType === node.ELEMENT_NODE) { if (node.nodeType === node.ELEMENT_NODE) {
process(node, enabled); process(node, enabled);
} else if (node.nodeType === node.TEXT_NODE && enabled) { } else if (node.nodeType === node.TEXT_NODE && enabled) {
@ -163,8 +165,8 @@ Localizer.prototype = {
} }
process(document.body, true); process(document.body, true);
}, }
}; }
export var l10n = new Localizer(); export const l10n = new Localizer();
export default l10n.get.bind(l10n); export default l10n.get.bind(l10n);

View file

@ -1,8 +1,6 @@
/* /*
* noVNC base CSS * noVNC base CSS
* Copyright (C) 2012 Joel Martin * Copyright (C) 2018 The noVNC Authors
* Copyright (C) 2016 Samuel Mannehed for Cendio AB
* Copyright (C) 2016 Pierre Ossman for Cendio AB
* noVNC is licensed under the MPL 2.0 (see LICENSE.txt) * noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt). * This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
*/ */
@ -30,6 +28,7 @@ body {
background-repeat:no-repeat; background-repeat:no-repeat;
background-position:right bottom; background-position:right bottom;
height:100%; height:100%;
display: flex;
touch-action: none; touch-action: none;
} }

File diff suppressed because it is too large Load diff

View file

@ -1,7 +1,6 @@
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin * Copyright (C) 2018 The noVNC Authors
* Copyright (C) 2013 NTT corp.
* Licensed under MPL 2.0 (see LICENSE.txt) * Licensed under MPL 2.0 (see LICENSE.txt)
* *
* See README.md for usage and integration instructions. * See README.md for usage and integration instructions.
@ -10,61 +9,65 @@
import { init_logging as main_init_logging } from '../core/util/logging.js'; import { init_logging as main_init_logging } from '../core/util/logging.js';
// init log level reading the logging HTTP param // init log level reading the logging HTTP param
export function init_logging (level) { export function init_logging(level) {
"use strict"; "use strict";
if (typeof level !== "undefined") { if (typeof level !== "undefined") {
main_init_logging(level); main_init_logging(level);
} else { } else {
var param = document.location.href.match(/logging=([A-Za-z0-9\._\-]*)/); const param = document.location.href.match(/logging=([A-Za-z0-9._-]*)/);
main_init_logging(param || undefined); main_init_logging(param || undefined);
} }
}; }
// Read a query string variable // Read a query string variable
export function getQueryVar (name, defVal) { export function getQueryVar(name, defVal) {
"use strict"; "use strict";
var re = new RegExp('.*[?&]' + name + '=([^&#]*)'), const re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
match = document.location.href.match(re); match = document.location.href.match(re);
if (typeof defVal === 'undefined') { defVal = null; } if (typeof defVal === 'undefined') { defVal = null; }
if (match) { if (match) {
return decodeURIComponent(match[1]); return decodeURIComponent(match[1]);
} else {
return defVal;
} }
};
return defVal;
}
// Read a hash fragment variable // Read a hash fragment variable
export function getHashVar (name, defVal) { export function getHashVar(name, defVal) {
"use strict"; "use strict";
var re = new RegExp('.*[&#]' + name + '=([^&]*)'), const re = new RegExp('.*[&#]' + name + '=([^&]*)'),
match = document.location.hash.match(re); match = document.location.hash.match(re);
if (typeof defVal === 'undefined') { defVal = null; } if (typeof defVal === 'undefined') { defVal = null; }
if (match) { if (match) {
return decodeURIComponent(match[1]); return decodeURIComponent(match[1]);
} else {
return defVal;
} }
};
return defVal;
}
// Read a variable from the fragment or the query string // Read a variable from the fragment or the query string
// Fragment takes precedence // Fragment takes precedence
export function getConfigVar (name, defVal) { export function getConfigVar(name, defVal) {
"use strict"; "use strict";
var val = getHashVar(name); const val = getHashVar(name);
if (val === null) { if (val === null) {
val = getQueryVar(name, defVal); return getQueryVar(name, defVal);
} }
return val; return val;
}; }
/* /*
* Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html * Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
*/ */
// No days means only for this browser session // No days means only for this browser session
export function createCookie (name, value, days) { export function createCookie(name, value, days) {
"use strict"; "use strict";
var date, expires; let date, expires;
if (days) { if (days) {
date = new Date(); date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
@ -73,115 +76,123 @@ export function createCookie (name, value, days) {
expires = ""; expires = "";
} }
var secure; let secure;
if (document.location.protocol === "https:") { if (document.location.protocol === "https:") {
secure = "; secure"; secure = "; secure";
} else { } else {
secure = ""; secure = "";
} }
document.cookie = name + "=" + value + expires + "; path=/" + secure; document.cookie = name + "=" + value + expires + "; path=/" + secure;
}; }
export function readCookie (name, defaultValue) { export function readCookie(name, defaultValue) {
"use strict"; "use strict";
var nameEQ = name + "=", const nameEQ = name + "=";
ca = document.cookie.split(';'); const ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i += 1) { for (let i = 0; i < ca.length; i += 1) {
var c = ca[i]; let c = ca[i];
while (c.charAt(0) === ' ') { c = c.substring(1, c.length); } while (c.charAt(0) === ' ') {
if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length, c.length); } c = c.substring(1, c.length);
}
if (c.indexOf(nameEQ) === 0) {
return c.substring(nameEQ.length, c.length);
}
} }
return (typeof defaultValue !== 'undefined') ? defaultValue : null;
};
export function eraseCookie (name) { return (typeof defaultValue !== 'undefined') ? defaultValue : null;
}
export function eraseCookie(name) {
"use strict"; "use strict";
createCookie(name, "", -1); createCookie(name, "", -1);
}; }
/* /*
* Setting handling. * Setting handling.
*/ */
var settings = {}; let settings = {};
export function initSettings (callback /*, ...callbackArgs */) { export function initSettings() {
"use strict"; if (!window.chrome || !window.chrome.storage) {
var callbackArgs = Array.prototype.slice.call(arguments, 1); settings = {};
if (window.chrome && window.chrome.storage) { return Promise.resolve();
window.chrome.storage.sync.get(function (cfg) {
settings = cfg;
if (callback) {
callback.apply(this, callbackArgs);
}
});
} else {
// No-op
if (callback) {
callback.apply(this, callbackArgs);
}
} }
};
return new Promise(resolve => window.chrome.storage.sync.get(resolve))
.then((cfg) => { settings = cfg; });
}
// Update the settings cache, but do not write to permanent storage
export function setSetting(name, value) {
settings[name] = value;
}
// No days means only for this browser session // No days means only for this browser session
export function writeSetting (name, value) { export function writeSetting(name, value) {
"use strict"; "use strict";
if (settings[name] === value) return;
settings[name] = value;
if (window.chrome && window.chrome.storage) { if (window.chrome && window.chrome.storage) {
if (settings[name] !== value) { window.chrome.storage.sync.set(settings);
settings[name] = value;
window.chrome.storage.sync.set(settings);
}
} else { } else {
localStorage.setItem(name, value); localStorage.setItem(name, value);
} }
}; }
export function readSetting (name, defaultValue) { export function readSetting(name, defaultValue) {
"use strict"; "use strict";
var value; let value;
if (window.chrome && window.chrome.storage) { if ((name in settings) || (window.chrome && window.chrome.storage)) {
value = settings[name]; value = settings[name];
} else { } else {
value = localStorage.getItem(name); value = localStorage.getItem(name);
settings[name] = value;
} }
if (typeof value === "undefined") { if (typeof value === "undefined") {
value = null; value = null;
} }
if (value === null && typeof defaultValue !== "undefined") { if (value === null && typeof defaultValue !== "undefined") {
return defaultValue; return defaultValue;
} else {
return value;
} }
};
export function eraseSetting (name) { return value;
}
export function eraseSetting(name) {
"use strict"; "use strict";
// Deleting here means that next time the setting is read when using local
// storage, it will be pulled from local storage again.
// If the setting in local storage is changed (e.g. in another tab)
// between this delete and the next read, it could lead to an unexpected
// value change.
delete settings[name];
if (window.chrome && window.chrome.storage) { if (window.chrome && window.chrome.storage) {
window.chrome.storage.sync.remove(name); window.chrome.storage.sync.remove(name);
delete settings[name];
} else { } else {
localStorage.removeItem(name); localStorage.removeItem(name);
} }
}; }
export function injectParamIfMissing (path, param, value) { export function injectParamIfMissing(path, param, value) {
// force pretend that we're dealing with a relative path // force pretend that we're dealing with a relative path
// (assume that we wanted an extra if we pass one in) // (assume that we wanted an extra if we pass one in)
path = "/" + path; path = "/" + path;
var elem = document.createElement('a'); const elem = document.createElement('a');
elem.href = path; elem.href = path;
var param_eq = encodeURIComponent(param) + "="; const param_eq = encodeURIComponent(param) + "=";
var query; let query;
if (elem.search) { if (elem.search) {
query = elem.search.slice(1).split('&'); query = elem.search.slice(1).split('&');
} else { } else {
query = []; query = [];
} }
if (!query.some(function (v) { return v.startsWith(param_eq); })) { if (!query.some(v => v.startsWith(param_eq))) {
query.push(param_eq + encodeURIComponent(value)); query.push(param_eq + encodeURIComponent(value));
elem.search = "?" + query.join("&"); elem.search = "?" + query.join("&");
} }
@ -190,41 +201,39 @@ export function injectParamIfMissing (path, param, value) {
// in the elem.pathname string. Handle that case gracefully. // in the elem.pathname string. Handle that case gracefully.
if (elem.pathname.charAt(0) == "/") { if (elem.pathname.charAt(0) == "/") {
return elem.pathname.slice(1) + elem.search + elem.hash; return elem.pathname.slice(1) + elem.search + elem.hash;
} else {
return elem.pathname + elem.search + elem.hash;
} }
};
return elem.pathname + elem.search + elem.hash;
}
// sadly, we can't use the Fetch API until we decide to drop // sadly, we can't use the Fetch API until we decide to drop
// IE11 support or polyfill promises and fetch in IE11. // IE11 support or polyfill promises and fetch in IE11.
// resolve will receive an object on success, while reject // resolve will receive an object on success, while reject
// will receive either an event or an error on failure. // will receive either an event or an error on failure.
export function fetchJSON(path, resolve, reject) { export function fetchJSON(path) {
// NB: IE11 doesn't support JSON as a responseType return new Promise((resolve, reject) => {
var req = new XMLHttpRequest(); // NB: IE11 doesn't support JSON as a responseType
req.open('GET', path); const req = new XMLHttpRequest();
req.open('GET', path);
req.onload = function () { req.onload = () => {
if (req.status === 200) { if (req.status === 200) {
try { let resObj;
var resObj = JSON.parse(req.responseText); try {
} catch (err) { resObj = JSON.parse(req.responseText);
reject(err); } catch (err) {
return; reject(err);
}
resolve(resObj);
} else {
reject(new Error("XHR got non-200 status while trying to load '" + path + "': " + req.status));
} }
resolve(resObj); };
} else {
reject(new Error("XHR got non-200 status while trying to load '" + path + "': " + req.status));
}
};
req.onerror = function (evt) { req.onerror = evt => reject(new Error("XHR encountered an error while trying to load '" + path + "': " + evt.message));
reject(new Error("XHR encountered an error while trying to load '" + path + "': " + evt.message));
};
req.ontimeout = function (evt) { req.ontimeout = evt => reject(new Error("XHR timed out while trying to load '" + path + "'"));
reject(new Error("XHR timed out while trying to load '" + path + "'"));
};
req.send(); req.send();
});
} }

View file

@ -8,45 +8,43 @@ import * as Log from './util/logging.js';
export default { export default {
/* Convert data (an array of integers) to a Base64 string. */ /* Convert data (an array of integers) to a Base64 string. */
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''), toBase64Table: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
base64Pad : '=', base64Pad: '=',
encode: function (data) { encode(data) {
"use strict"; "use strict";
var result = ''; let result = '';
var toBase64Table = this.toBase64Table; const length = data.length;
var length = data.length; const lengthpad = (length % 3);
var lengthpad = (length % 3);
// Convert every three bytes to 4 ascii characters. // Convert every three bytes to 4 ascii characters.
for (var i = 0; i < (length - 2); i += 3) { for (let i = 0; i < (length - 2); i += 3) {
result += toBase64Table[data[i] >> 2]; result += this.toBase64Table[data[i] >> 2];
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)]; result += this.toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
result += toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)]; result += this.toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
result += toBase64Table[data[i + 2] & 0x3f]; result += this.toBase64Table[data[i + 2] & 0x3f];
} }
// Convert the remaining 1 or 2 bytes, pad out to 4 characters. // Convert the remaining 1 or 2 bytes, pad out to 4 characters.
var j = 0; const j = length - lengthpad;
if (lengthpad === 2) { if (lengthpad === 2) {
j = length - lengthpad; result += this.toBase64Table[data[j] >> 2];
result += toBase64Table[data[j] >> 2]; result += this.toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)];
result += toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)]; result += this.toBase64Table[(data[j + 1] & 0x0f) << 2];
result += toBase64Table[(data[j + 1] & 0x0f) << 2]; result += this.toBase64Table[64];
result += toBase64Table[64];
} else if (lengthpad === 1) { } else if (lengthpad === 1) {
j = length - lengthpad; result += this.toBase64Table[data[j] >> 2];
result += toBase64Table[data[j] >> 2]; result += this.toBase64Table[(data[j] & 0x03) << 4];
result += toBase64Table[(data[j] & 0x03) << 4]; result += this.toBase64Table[64];
result += toBase64Table[64]; result += this.toBase64Table[64];
result += toBase64Table[64];
} }
return result; return result;
}, },
/* Convert Base64 data to a string */ /* Convert Base64 data to a string */
toBinaryTable : [ /* eslint-disable comma-spacing */
toBinaryTable: [
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
@ -56,27 +54,23 @@ export default {
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
], ],
/* eslint-enable comma-spacing */
decode: function (data, offset) { decode(data, offset = 0) {
"use strict"; let data_length = data.indexOf('=') - offset;
offset = typeof(offset) !== 'undefined' ? offset : 0;
var toBinaryTable = this.toBinaryTable;
var base64Pad = this.base64Pad;
var result, result_length;
var leftbits = 0; // number of bits decoded, but yet to be appended
var leftdata = 0; // bits decoded, but yet to be appended
var data_length = data.indexOf('=') - offset;
if (data_length < 0) { data_length = data.length - offset; } if (data_length < 0) { data_length = data.length - offset; }
/* Every four characters is 3 resulting numbers */ /* Every four characters is 3 resulting numbers */
result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5); const result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5);
result = new Array(result_length); const result = new Array(result_length);
// Convert one by one. // Convert one by one.
for (var idx = 0, i = offset; i < data.length; i++) {
var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; let leftbits = 0; // number of bits decoded, but yet to be appended
var padding = (data.charAt(i) === base64Pad); let leftdata = 0; // bits decoded, but yet to be appended
for (let idx = 0, i = offset; i < data.length; i++) {
const c = this.toBinaryTable[data.charCodeAt(i) & 0x7f];
const padding = (data.charAt(i) === this.base64Pad);
// Skip illegal characters and whitespace // Skip illegal characters and whitespace
if (c === -1) { if (c === -1) {
Log.Error("Illegal character code " + data.charCodeAt(i) + " at position " + i); Log.Error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
@ -100,7 +94,7 @@ export default {
// If there are any bits left, the base64 string was corrupted // If there are any bits left, the base64 string was corrupted
if (leftbits) { if (leftbits) {
err = new Error('Corrupted base64 string'); const err = new Error('Corrupted base64 string');
err.name = 'Base64-Error'; err.name = 'Base64-Error';
throw err; throw err;
} }

View file

@ -0,0 +1,24 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2018 Samuel Mannehed for Cendio AB
* Copyright (C) 2018 Pierre Ossman for Cendio AB
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
export default class CopyRectDecoder {
decodeRect(x, y, width, height, sock, display, depth) {
if (sock.rQwait("COPYRECT", 4)) {
return false;
}
let deltaX = sock.rQshift16();
let deltaY = sock.rQshift16();
display.copyImage(deltaX, deltaY, x, y, width, height);
return true;
}
}

View file

@ -0,0 +1,139 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2018 Samuel Mannehed for Cendio AB
* Copyright (C) 2018 Pierre Ossman for Cendio AB
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
import * as Log from '../util/logging.js';
export default class HextileDecoder {
constructor() {
this._tiles = 0;
this._lastsubencoding = 0;
}
decodeRect(x, y, width, height, sock, display, depth) {
if (this._tiles === 0) {
this._tiles_x = Math.ceil(width / 16);
this._tiles_y = Math.ceil(height / 16);
this._total_tiles = this._tiles_x * this._tiles_y;
this._tiles = this._total_tiles;
}
while (this._tiles > 0) {
let bytes = 1;
if (sock.rQwait("HEXTILE", bytes)) {
return false;
}
let rQ = sock.rQ;
let rQi = sock.rQi;
let subencoding = rQ[rQi]; // Peek
if (subencoding > 30) { // Raw
throw new Error("Illegal hextile subencoding (subencoding: " +
subencoding + ")");
}
const curr_tile = this._total_tiles - this._tiles;
const tile_x = curr_tile % this._tiles_x;
const tile_y = Math.floor(curr_tile / this._tiles_x);
const tx = x + tile_x * 16;
const ty = y + tile_y * 16;
const tw = Math.min(16, (x + width) - tx);
const th = Math.min(16, (y + height) - ty);
// Figure out how much we are expecting
if (subencoding & 0x01) { // Raw
bytes += tw * th * 4;
} else {
if (subencoding & 0x02) { // Background
bytes += 4;
}
if (subencoding & 0x04) { // Foreground
bytes += 4;
}
if (subencoding & 0x08) { // AnySubrects
bytes++; // Since we aren't shifting it off
if (sock.rQwait("HEXTILE", bytes)) {
return false;
}
let subrects = rQ[rQi + bytes - 1]; // Peek
if (subencoding & 0x10) { // SubrectsColoured
bytes += subrects * (4 + 2);
} else {
bytes += subrects * 2;
}
}
}
if (sock.rQwait("HEXTILE", bytes)) {
return false;
}
// We know the encoding and have a whole tile
rQi++;
if (subencoding === 0) {
if (this._lastsubencoding & 0x01) {
// Weird: ignore blanks are RAW
Log.Debug(" Ignoring blank after RAW");
} else {
display.fillRect(tx, ty, tw, th, this._background);
}
} else if (subencoding & 0x01) { // Raw
display.blitImage(tx, ty, tw, th, rQ, rQi);
rQi += bytes - 1;
} else {
if (subencoding & 0x02) { // Background
this._background = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
rQi += 4;
}
if (subencoding & 0x04) { // Foreground
this._foreground = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
rQi += 4;
}
display.startTile(tx, ty, tw, th, this._background);
if (subencoding & 0x08) { // AnySubrects
let subrects = rQ[rQi];
rQi++;
for (let s = 0; s < subrects; s++) {
let color;
if (subencoding & 0x10) { // SubrectsColoured
color = [rQ[rQi], rQ[rQi + 1], rQ[rQi + 2], rQ[rQi + 3]];
rQi += 4;
} else {
color = this._foreground;
}
const xy = rQ[rQi];
rQi++;
const sx = (xy >> 4);
const sy = (xy & 0x0f);
const wh = rQ[rQi];
rQi++;
const sw = (wh >> 4) + 1;
const sh = (wh & 0x0f) + 1;
display.subTile(sx, sy, sw, sh, color);
}
}
display.finishTile();
}
sock.rQi = rQi;
this._lastsubencoding = subencoding;
this._tiles--;
}
return true;
}
}

View file

@ -0,0 +1,58 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2018 Samuel Mannehed for Cendio AB
* Copyright (C) 2018 Pierre Ossman for Cendio AB
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
export default class RawDecoder {
constructor() {
this._lines = 0;
}
decodeRect(x, y, width, height, sock, display, depth) {
if (this._lines === 0) {
this._lines = height;
}
const pixelSize = depth == 8 ? 1 : 4;
const bytesPerLine = width * pixelSize;
if (sock.rQwait("RAW", bytesPerLine)) {
return false;
}
const cur_y = y + (height - this._lines);
const curr_height = Math.min(this._lines,
Math.floor(sock.rQlen / bytesPerLine));
let data = sock.rQ;
let index = sock.rQi;
// Convert data if needed
if (depth == 8) {
const pixels = width * curr_height;
const newdata = new Uint8Array(pixels * 4);
for (let i = 0; i < pixels; i++) {
newdata[i * 4 + 0] = ((data[index + i] >> 0) & 0x3) * 255 / 3;
newdata[i * 4 + 1] = ((data[index + i] >> 2) & 0x3) * 255 / 3;
newdata[i * 4 + 2] = ((data[index + i] >> 4) & 0x3) * 255 / 3;
newdata[i * 4 + 4] = 0;
}
data = newdata;
index = 0;
}
display.blitImage(x, cur_y, width, curr_height, data, index);
sock.rQskipBytes(curr_height * bytesPerLine);
this._lines -= curr_height;
if (this._lines > 0) {
return false;
}
return true;
}
}

View file

@ -0,0 +1,46 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2018 Samuel Mannehed for Cendio AB
* Copyright (C) 2018 Pierre Ossman for Cendio AB
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
export default class RREDecoder {
constructor() {
this._subrects = 0;
}
decodeRect(x, y, width, height, sock, display, depth) {
if (this._subrects === 0) {
if (sock.rQwait("RRE", 4 + 4)) {
return false;
}
this._subrects = sock.rQshift32();
let color = sock.rQshiftBytes(4); // Background
display.fillRect(x, y, width, height, color);
}
while (this._subrects > 0) {
if (sock.rQwait("RRE", 4 + 8)) {
return false;
}
let color = sock.rQshiftBytes(4);
let sx = sock.rQshift16();
let sy = sock.rQshift16();
let swidth = sock.rQshift16();
let sheight = sock.rQshift16();
display.fillRect(x + sx, y + sy, swidth, sheight, color);
this._subrects--;
}
return true;
}
}

View file

@ -0,0 +1,319 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
* Copyright (C) 2018 Samuel Mannehed for Cendio AB
* Copyright (C) 2018 Pierre Ossman for Cendio AB
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
import * as Log from '../util/logging.js';
import Inflator from "../inflator.js";
export default class TightDecoder {
constructor() {
this._ctl = null;
this._filter = null;
this._numColors = 0;
this._palette = new Uint8Array(1024); // 256 * 4 (max palette size * max bytes-per-pixel)
this._len = 0;
this._zlibs = [];
for (let i = 0; i < 4; i++) {
this._zlibs[i] = new Inflator();
}
}
decodeRect(x, y, width, height, sock, display, depth) {
if (this._ctl === null) {
if (sock.rQwait("TIGHT compression-control", 1)) {
return false;
}
this._ctl = sock.rQshift8();
// Reset streams if the server requests it
for (let i = 0; i < 4; i++) {
if ((this._ctl >> i) & 1) {
this._zlibs[i].reset();
Log.Info("Reset zlib stream " + i);
}
}
// Figure out filter
this._ctl = this._ctl >> 4;
}
let ret;
if (this._ctl === 0x08) {
ret = this._fillRect(x, y, width, height,
sock, display, depth);
} else if (this._ctl === 0x09) {
ret = this._jpegRect(x, y, width, height,
sock, display, depth);
} else if (this._ctl === 0x0A) {
ret = this._pngRect(x, y, width, height,
sock, display, depth);
} else if ((this._ctl & 0x80) == 0) {
ret = this._basicRect(this._ctl, x, y, width, height,
sock, display, depth);
} else {
throw new Error("Illegal tight compression received (ctl: " +
this._ctl + ")");
}
if (ret) {
this._ctl = null;
}
return ret;
}
_fillRect(x, y, width, height, sock, display, depth) {
if (sock.rQwait("TIGHT", 3)) {
return false;
}
const rQi = sock.rQi;
const rQ = sock.rQ;
display.fillRect(x, y, width, height,
[rQ[rQi + 2], rQ[rQi + 1], rQ[rQi]], false);
sock.rQskipBytes(3);
return true;
}
_jpegRect(x, y, width, height, sock, display, depth) {
let data = this._readData(sock);
if (data === null) {
return false;
}
display.imageRect(x, y, "image/jpeg", data);
return true;
}
_pngRect(x, y, width, height, sock, display, depth) {
throw new Error("PNG received in standard Tight rect");
}
_basicRect(ctl, x, y, width, height, sock, display, depth) {
if (this._filter === null) {
if (ctl & 0x4) {
if (sock.rQwait("TIGHT", 1)) {
return false;
}
this._filter = sock.rQshift8();
} else {
// Implicit CopyFilter
this._filter = 0;
}
}
let streamId = ctl & 0x3;
let ret;
switch (this._filter) {
case 0: // CopyFilter
ret = this._copyFilter(streamId, x, y, width, height,
sock, display, depth);
break;
case 1: // PaletteFilter
ret = this._paletteFilter(streamId, x, y, width, height,
sock, display, depth);
break;
case 2: // GradientFilter
ret = this._gradientFilter(streamId, x, y, width, height,
sock, display, depth);
break;
default:
throw new Error("Illegal tight filter received (ctl: " +
this._filter + ")");
}
if (ret) {
this._filter = null;
}
return ret;
}
_copyFilter(streamId, x, y, width, height, sock, display, depth) {
const uncompressedSize = width * height * 3;
let data;
if (uncompressedSize < 12) {
if (sock.rQwait("TIGHT", uncompressedSize)) {
return false;
}
data = sock.rQshiftBytes(uncompressedSize);
} else {
data = this._readData(sock);
if (data === null) {
return false;
}
data = this._zlibs[streamId].inflate(data, true, uncompressedSize);
if (data.length != uncompressedSize) {
throw new Error("Incomplete zlib block");
}
}
display.blitRgbImage(x, y, width, height, data, 0, false);
return true;
}
_paletteFilter(streamId, x, y, width, height, sock, display, depth) {
if (this._numColors === 0) {
if (sock.rQwait("TIGHT palette", 1)) {
return false;
}
const numColors = sock.rQpeek8() + 1;
const paletteSize = numColors * 3;
if (sock.rQwait("TIGHT palette", 1 + paletteSize)) {
return false;
}
this._numColors = numColors;
sock.rQskipBytes(1);
sock.rQshiftTo(this._palette, paletteSize);
}
const bpp = (this._numColors <= 2) ? 1 : 8;
const rowSize = Math.floor((width * bpp + 7) / 8);
const uncompressedSize = rowSize * height;
let data;
if (uncompressedSize < 12) {
if (sock.rQwait("TIGHT", uncompressedSize)) {
return false;
}
data = sock.rQshiftBytes(uncompressedSize);
} else {
data = this._readData(sock);
if (data === null) {
return false;
}
data = this._zlibs[streamId].inflate(data, true, uncompressedSize);
if (data.length != uncompressedSize) {
throw new Error("Incomplete zlib block");
}
}
// Convert indexed (palette based) image data to RGB
if (this._numColors == 2) {
this._monoRect(x, y, width, height, data, this._palette, display);
} else {
this._paletteRect(x, y, width, height, data, this._palette, display);
}
this._numColors = 0;
return true;
}
_monoRect(x, y, width, height, data, palette, display) {
// Convert indexed (palette based) image data to RGB
// TODO: reduce number of calculations inside loop
const dest = this._getScratchBuffer(width * height * 4);
const w = Math.floor((width + 7) / 8);
const w1 = Math.floor(width / 8);
for (let y = 0; y < height; y++) {
let dp, sp, x;
for (x = 0; x < w1; x++) {
for (let b = 7; b >= 0; b--) {
dp = (y * width + x * 8 + 7 - b) * 4;
sp = (data[y * w + x] >> b & 1) * 3;
dest[dp] = palette[sp];
dest[dp + 1] = palette[sp + 1];
dest[dp + 2] = palette[sp + 2];
dest[dp + 3] = 255;
}
}
for (let b = 7; b >= 8 - width % 8; b--) {
dp = (y * width + x * 8 + 7 - b) * 4;
sp = (data[y * w + x] >> b & 1) * 3;
dest[dp] = palette[sp];
dest[dp + 1] = palette[sp + 1];
dest[dp + 2] = palette[sp + 2];
dest[dp + 3] = 255;
}
}
display.blitRgbxImage(x, y, width, height, dest, 0, false);
}
_paletteRect(x, y, width, height, data, palette, display) {
// Convert indexed (palette based) image data to RGB
const dest = this._getScratchBuffer(width * height * 4);
const total = width * height * 4;
for (let i = 0, j = 0; i < total; i += 4, j++) {
const sp = data[j] * 3;
dest[i] = palette[sp];
dest[i + 1] = palette[sp + 1];
dest[i + 2] = palette[sp + 2];
dest[i + 3] = 255;
}
display.blitRgbxImage(x, y, width, height, dest, 0, false);
}
_gradientFilter(streamId, x, y, width, height, sock, display, depth) {
throw new Error("Gradient filter not implemented");
}
_readData(sock) {
if (this._len === 0) {
if (sock.rQwait("TIGHT", 3)) {
return null;
}
let byte;
byte = sock.rQshift8();
this._len = byte & 0x7f;
if (byte & 0x80) {
byte = sock.rQshift8();
this._len |= (byte & 0x7f) << 7;
if (byte & 0x80) {
byte = sock.rQshift8();
this._len |= byte << 14;
}
}
}
if (sock.rQwait("TIGHT", this._len)) {
return null;
}
let data = sock.rQshiftBytes(this._len);
this._len = 0;
return data;
}
_getScratchBuffer(size) {
if (!this._scratchBuffer || (this._scratchBuffer.length < size)) {
this._scratchBuffer = new Uint8Array(size);
}
return this._scratchBuffer;
}
}

View file

@ -0,0 +1,29 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2018 Samuel Mannehed for Cendio AB
* Copyright (C) 2018 Pierre Ossman for Cendio AB
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
import TightDecoder from './tight.js';
export default class TightPNGDecoder extends TightDecoder {
_pngRect(x, y, width, height, sock, display, depth) {
let data = this._readData(sock);
if (data === null) {
return false;
}
display.imageRect(x, y, "image/png", data);
return true;
}
_basicRect(ctl, x, y, width, height, sock, display, depth) {
throw new Error("BasicCompression received in TightPNG rect");
}
}

View file

@ -75,84 +75,83 @@
* fine Java utilities: http://www.acme.com/java/ * fine Java utilities: http://www.acme.com/java/
*/ */
export default function DES(passwd) { /* eslint-disable comma-spacing */
"use strict";
// Tables, permutations, S-boxes, etc. // Tables, permutations, S-boxes, etc.
var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3, const PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,
25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39, 25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,
50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ], 50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],
totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28], totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28];
z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8,
keys = [];
a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e; const z = 0x0;
SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d, let a,b,c,d,e,f;
z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z, a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e;
a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f, const SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d,
c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d]; z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z,
a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e; a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f,
SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d, c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d];
a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f, a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e;
z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z, const SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d,
z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e]; a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f,
a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e; z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z,
SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f, z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e];
b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z, a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e;
c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d, const SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f,
b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e]; b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z,
a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e; c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d,
SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d, b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e];
z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f, a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e;
b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e, const SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d,
c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e]; z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f,
a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e; b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e,
SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z, c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e];
a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f, a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e;
z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e, const SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z,
c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d]; a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f,
a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e; z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e,
SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f, c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d];
z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z, a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e;
b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z, const SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f,
a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f]; z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z,
a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e; b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z,
SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f, a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f];
b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e, a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e;
b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e, const SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f,
z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d]; b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e,
a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e; b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e,
SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d, z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d];
c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z, a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e;
a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f, const SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d,
z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e]; c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z,
a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f,
z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e];
// Set the key. /* eslint-enable comma-spacing */
function setKeys(keyBlock) {
var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [],
raw0, raw1, rawi, KnLi;
for (j = 0, l = 56; j < 56; ++j, l -= 8) { export default class DES {
constructor(password) {
this.keys = [];
// Set the key.
const pc1m = [], pcr = [], kn = [];
for (let j = 0, l = 56; j < 56; ++j, l -= 8) {
l += l < -5 ? 65 : l < -3 ? 31 : l < -1 ? 63 : l === 27 ? 35 : 0; // PC1 l += l < -5 ? 65 : l < -3 ? 31 : l < -1 ? 63 : l === 27 ? 35 : 0; // PC1
m = l & 0x7; const m = l & 0x7;
pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0; pc1m[j] = ((password[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
} }
for (i = 0; i < 16; ++i) { for (let i = 0; i < 16; ++i) {
m = i << 1; const m = i << 1;
n = m + 1; const n = m + 1;
kn[m] = kn[n] = 0; kn[m] = kn[n] = 0;
for (o = 28; o < 59; o += 28) { for (let o = 28; o < 59; o += 28) {
for (j = o - 28; j < o; ++j) { for (let j = o - 28; j < o; ++j) {
l = j + totrot[i]; const l = j + totrot[i];
if (l < o) { pcr[j] = l < o ? pc1m[l] : pc1m[l - 28];
pcr[j] = pc1m[l];
} else {
pcr[j] = pc1m[l - 28];
}
} }
} }
for (j = 0; j < 24; ++j) { for (let j = 0; j < 24; ++j) {
if (pcr[PC2[j]] !== 0) { if (pcr[PC2[j]] !== 0) {
kn[m] |= 1 << (23 - j); kn[m] |= 1 << (23 - j);
} }
@ -163,26 +162,26 @@ export default function DES(passwd) {
} }
// cookey // cookey
for (i = 0, rawi = 0, KnLi = 0; i < 16; ++i) { for (let i = 0, rawi = 0, KnLi = 0; i < 16; ++i) {
raw0 = kn[rawi++]; const raw0 = kn[rawi++];
raw1 = kn[rawi++]; const raw1 = kn[rawi++];
keys[KnLi] = (raw0 & 0x00fc0000) << 6; this.keys[KnLi] = (raw0 & 0x00fc0000) << 6;
keys[KnLi] |= (raw0 & 0x00000fc0) << 10; this.keys[KnLi] |= (raw0 & 0x00000fc0) << 10;
keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10; this.keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6; this.keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6;
++KnLi; ++KnLi;
keys[KnLi] = (raw0 & 0x0003f000) << 12; this.keys[KnLi] = (raw0 & 0x0003f000) << 12;
keys[KnLi] |= (raw0 & 0x0000003f) << 16; this.keys[KnLi] |= (raw0 & 0x0000003f) << 16;
keys[KnLi] |= (raw1 & 0x0003f000) >>> 4; this.keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;
keys[KnLi] |= (raw1 & 0x0000003f); this.keys[KnLi] |= (raw1 & 0x0000003f);
++KnLi; ++KnLi;
} }
} }
// Encrypt 8 bytes of text // Encrypt 8 bytes of text
function enc8(text) { enc8(text) {
var i = 0, b = text.slice(), fval, keysi = 0, const b = text.slice();
l, r, x; // left, right, accumulator let i = 0, l, r, x; // left, right, accumulator
// Squash 8 bytes to 2 ints // Squash 8 bytes to 2 ints
l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++]; l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
@ -206,26 +205,26 @@ export default function DES(passwd) {
r ^= x; r ^= x;
l = (l << 1) | ((l >>> 31) & 1); l = (l << 1) | ((l >>> 31) & 1);
for (i = 0; i < 8; ++i) { for (let i = 0, keysi = 0; i < 8; ++i) {
x = (r << 28) | (r >>> 4); x = (r << 28) | (r >>> 4);
x ^= keys[keysi++]; x ^= this.keys[keysi++];
fval = SP7[x & 0x3f]; let fval = SP7[x & 0x3f];
fval |= SP5[(x >>> 8) & 0x3f]; fval |= SP5[(x >>> 8) & 0x3f];
fval |= SP3[(x >>> 16) & 0x3f]; fval |= SP3[(x >>> 16) & 0x3f];
fval |= SP1[(x >>> 24) & 0x3f]; fval |= SP1[(x >>> 24) & 0x3f];
x = r ^ keys[keysi++]; x = r ^ this.keys[keysi++];
fval |= SP8[x & 0x3f]; fval |= SP8[x & 0x3f];
fval |= SP6[(x >>> 8) & 0x3f]; fval |= SP6[(x >>> 8) & 0x3f];
fval |= SP4[(x >>> 16) & 0x3f]; fval |= SP4[(x >>> 16) & 0x3f];
fval |= SP2[(x >>> 24) & 0x3f]; fval |= SP2[(x >>> 24) & 0x3f];
l ^= fval; l ^= fval;
x = (l << 28) | (l >>> 4); x = (l << 28) | (l >>> 4);
x ^= keys[keysi++]; x ^= this.keys[keysi++];
fval = SP7[x & 0x3f]; fval = SP7[x & 0x3f];
fval |= SP5[(x >>> 8) & 0x3f]; fval |= SP5[(x >>> 8) & 0x3f];
fval |= SP3[(x >>> 16) & 0x3f]; fval |= SP3[(x >>> 16) & 0x3f];
fval |= SP1[(x >>> 24) & 0x3f]; fval |= SP1[(x >>> 24) & 0x3f];
x = l ^ keys[keysi++]; x = l ^ this.keys[keysi++];
fval |= SP8[x & 0x0000003f]; fval |= SP8[x & 0x0000003f];
fval |= SP6[(x >>> 8) & 0x3f]; fval |= SP6[(x >>> 8) & 0x3f];
fval |= SP4[(x >>> 16) & 0x3f]; fval |= SP4[(x >>> 16) & 0x3f];
@ -261,11 +260,7 @@ export default function DES(passwd) {
} }
// Encrypt 16 bytes of text using passwd as key // Encrypt 16 bytes of text using passwd as key
function encrypt(t) { encrypt(t) {
return enc8(t.slice(0, 8)).concat(enc8(t.slice(8, 16))); return this.enc8(t.slice(0, 8)).concat(this.enc8(t.slice(8, 16)));
} }
}
setKeys(passwd); // Setup keys
return {'encrypt': encrypt}; // Public interface
}; // function DES

View file

@ -1,7 +1,6 @@
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin * Copyright (C) 2018 The noVNC Authors
* Copyright (C) 2015 Samuel Mannehed for Cendio AB
* Licensed under MPL 2.0 (see LICENSE.txt) * Licensed under MPL 2.0 (see LICENSE.txt)
* *
* See README.md for usage and integration instructions. * See README.md for usage and integration instructions.
@ -9,111 +8,107 @@
import * as Log from './util/logging.js'; import * as Log from './util/logging.js';
import Base64 from "./base64.js"; import Base64 from "./base64.js";
import { supportsImageMetadata } from './util/browser.js';
export default function Display(target) { export default class Display {
this._drawCtx = null; constructor(target) {
this._c_forceCanvas = false; this._drawCtx = null;
this._c_forceCanvas = false;
this._renderQ = []; // queue drawing actions for in-oder rendering this._renderQ = []; // queue drawing actions for in-oder rendering
this._flushing = false; this._flushing = false;
// the full frame buffer (logical canvas) size // the full frame buffer (logical canvas) size
this._fb_width = 0; this._fb_width = 0;
this._fb_height = 0; this._fb_height = 0;
this._prevDrawStyle = ""; this._prevDrawStyle = "";
this._tile = null; this._tile = null;
this._tile16x16 = null; this._tile16x16 = null;
this._tile_x = 0; this._tile_x = 0;
this._tile_y = 0; this._tile_y = 0;
Log.Debug(">> Display.constructor"); Log.Debug(">> Display.constructor");
// The visible canvas // The visible canvas
this._target = target; this._target = target;
if (!this._target) { if (!this._target) {
throw new Error("Target must be set"); throw new Error("Target must be set");
}
if (typeof this._target === 'string') {
throw new Error('target must be a DOM element');
}
if (!this._target.getContext) {
throw new Error("no getContext method");
}
this._targetCtx = this._target.getContext('2d');
// the visible canvas viewport (i.e. what actually gets seen)
this._viewportLoc = { 'x': 0, 'y': 0, 'w': this._target.width, 'h': this._target.height };
// The hidden canvas, where we do the actual rendering
this._backbuffer = document.createElement('canvas');
this._drawCtx = this._backbuffer.getContext('2d');
this._damageBounds = { left: 0, top: 0,
right: this._backbuffer.width,
bottom: this._backbuffer.height };
Log.Debug("User Agent: " + navigator.userAgent);
this.clear();
// Check canvas features
if (!('createImageData' in this._drawCtx)) {
throw new Error("Canvas does not support createImageData");
}
this._tile16x16 = this._drawCtx.createImageData(16, 16);
Log.Debug("<< Display.constructor");
// ===== PROPERTIES =====
this._scale = 1.0;
this._clipViewport = false;
this.logo = null;
// ===== EVENT HANDLERS =====
this.onflush = () => {}; // A flush request has finished
} }
if (typeof this._target === 'string') {
throw new Error('target must be a DOM element');
}
if (!this._target.getContext) {
throw new Error("no getContext method");
}
this._targetCtx = this._target.getContext('2d');
// the visible canvas viewport (i.e. what actually gets seen)
this._viewportLoc = { 'x': 0, 'y': 0, 'w': this._target.width, 'h': this._target.height };
// The hidden canvas, where we do the actual rendering
this._backbuffer = document.createElement('canvas');
this._drawCtx = this._backbuffer.getContext('2d');
this._damageBounds = { left:0, top:0,
right: this._backbuffer.width,
bottom: this._backbuffer.height };
Log.Debug("User Agent: " + navigator.userAgent);
this.clear();
// Check canvas features
if (!('createImageData' in this._drawCtx)) {
throw new Error("Canvas does not support createImageData");
}
this._tile16x16 = this._drawCtx.createImageData(16, 16);
Log.Debug("<< Display.constructor");
};
var SUPPORTS_IMAGEDATA_CONSTRUCTOR = false;
try {
new ImageData(new Uint8ClampedArray(4), 1, 1);
SUPPORTS_IMAGEDATA_CONSTRUCTOR = true;
} catch (ex) {
// ignore failure
}
Display.prototype = {
// ===== PROPERTIES ===== // ===== PROPERTIES =====
_scale: 1.0, get scale() { return this._scale; }
get scale() { return this._scale; },
set scale(scale) { set scale(scale) {
this._rescale(scale); this._rescale(scale);
}, }
_clipViewport: false, get clipViewport() { return this._clipViewport; }
get clipViewport() { return this._clipViewport; },
set clipViewport(viewport) { set clipViewport(viewport) {
this._clipViewport = viewport; this._clipViewport = viewport;
// May need to readjust the viewport dimensions // May need to readjust the viewport dimensions
var vp = this._viewportLoc; const vp = this._viewportLoc;
this.viewportChangeSize(vp.w, vp.h); this.viewportChangeSize(vp.w, vp.h);
this.viewportChangePos(0, 0); this.viewportChangePos(0, 0);
}, }
get width() { get width() {
return this._fb_width; return this._fb_width;
}, }
get height() { get height() {
return this._fb_height; return this._fb_height;
}, }
logo: null,
// ===== EVENT HANDLERS =====
onflush: function () {}, // A flush request has finished
// ===== PUBLIC METHODS ===== // ===== PUBLIC METHODS =====
viewportChangePos: function (deltaX, deltaY) { viewportChangePos(deltaX, deltaY) {
var vp = this._viewportLoc; const vp = this._viewportLoc;
deltaX = Math.floor(deltaX); deltaX = Math.floor(deltaX);
deltaY = Math.floor(deltaY); deltaY = Math.floor(deltaY);
@ -122,8 +117,8 @@ Display.prototype = {
deltaY = -vp.h; deltaY = -vp.h;
} }
var vx2 = vp.x + vp.w - 1; const vx2 = vp.x + vp.w - 1;
var vy2 = vp.y + vp.h - 1; const vy2 = vp.y + vp.h - 1;
// Position change // Position change
@ -152,9 +147,9 @@ Display.prototype = {
this._damage(vp.x, vp.y, vp.w, vp.h); this._damage(vp.x, vp.y, vp.w, vp.h);
this.flip(); this.flip();
}, }
viewportChangeSize: function(width, height) { viewportChangeSize(width, height) {
if (!this._clipViewport || if (!this._clipViewport ||
typeof(width) === "undefined" || typeof(width) === "undefined" ||
@ -165,6 +160,9 @@ Display.prototype = {
height = this._fb_height; height = this._fb_height;
} }
width = Math.floor(width);
height = Math.floor(height);
if (width > this._fb_width) { if (width > this._fb_width) {
width = this._fb_width; width = this._fb_width;
} }
@ -172,12 +170,12 @@ Display.prototype = {
height = this._fb_height; height = this._fb_height;
} }
var vp = this._viewportLoc; const vp = this._viewportLoc;
if (vp.w !== width || vp.h !== height) { if (vp.w !== width || vp.h !== height) {
vp.w = width; vp.w = width;
vp.h = height; vp.h = height;
var canvas = this._target; const canvas = this._target;
canvas.width = width; canvas.width = width;
canvas.height = height; canvas.height = height;
@ -190,27 +188,33 @@ Display.prototype = {
// Update the visible size of the target canvas // Update the visible size of the target canvas
this._rescale(this._scale); this._rescale(this._scale);
} }
}, }
absX: function (x) { absX(x) {
if (this._scale === 0) {
return 0;
}
return x / this._scale + this._viewportLoc.x; return x / this._scale + this._viewportLoc.x;
}, }
absY: function (y) { absY(y) {
if (this._scale === 0) {
return 0;
}
return y / this._scale + this._viewportLoc.y; return y / this._scale + this._viewportLoc.y;
}, }
resize: function (width, height) { resize(width, height) {
this._prevDrawStyle = ""; this._prevDrawStyle = "";
this._fb_width = width; this._fb_width = width;
this._fb_height = height; this._fb_height = height;
var canvas = this._backbuffer; const canvas = this._backbuffer;
if (canvas.width !== width || canvas.height !== height) { if (canvas.width !== width || canvas.height !== height) {
// We have to save the canvas data since changing the size will clear it // We have to save the canvas data since changing the size will clear it
var saveImg = null; let saveImg = null;
if (canvas.width > 0 && canvas.height > 0) { if (canvas.width > 0 && canvas.height > 0) {
saveImg = this._drawCtx.getImageData(0, 0, canvas.width, canvas.height); saveImg = this._drawCtx.getImageData(0, 0, canvas.width, canvas.height);
} }
@ -229,13 +233,13 @@ Display.prototype = {
// Readjust the viewport as it may be incorrectly sized // Readjust the viewport as it may be incorrectly sized
// and positioned // and positioned
var vp = this._viewportLoc; const vp = this._viewportLoc;
this.viewportChangeSize(vp.w, vp.h); this.viewportChangeSize(vp.w, vp.h);
this.viewportChangePos(0, 0); this.viewportChangePos(0, 0);
}, }
// Track what parts of the visible canvas that need updating // Track what parts of the visible canvas that need updating
_damage: function(x, y, w, h) { _damage(x, y, w, h) {
if (x < this._damageBounds.left) { if (x < this._damageBounds.left) {
this._damageBounds.left = x; this._damageBounds.left = x;
} }
@ -248,25 +252,23 @@ Display.prototype = {
if ((y + h) > this._damageBounds.bottom) { if ((y + h) > this._damageBounds.bottom) {
this._damageBounds.bottom = y + h; this._damageBounds.bottom = y + h;
} }
}, }
// Update the visible canvas with the contents of the // Update the visible canvas with the contents of the
// rendering canvas // rendering canvas
flip: function(from_queue) { flip(from_queue) {
if (this._renderQ.length !== 0 && !from_queue) { if (this._renderQ.length !== 0 && !from_queue) {
this._renderQ_push({ this._renderQ_push({
'type': 'flip' 'type': 'flip'
}); });
} else { } else {
var x, y, vx, vy, w, h; let x = this._damageBounds.left;
let y = this._damageBounds.top;
let w = this._damageBounds.right - x;
let h = this._damageBounds.bottom - y;
x = this._damageBounds.left; let vx = x - this._viewportLoc.x;
y = this._damageBounds.top; let vy = y - this._viewportLoc.y;
w = this._damageBounds.right - x;
h = this._damageBounds.bottom - y;
vx = x - this._viewportLoc.x;
vy = y - this._viewportLoc.y;
if (vx < 0) { if (vx < 0) {
w += vx; w += vx;
@ -298,9 +300,9 @@ Display.prototype = {
this._damageBounds.left = this._damageBounds.top = 65535; this._damageBounds.left = this._damageBounds.top = 65535;
this._damageBounds.right = this._damageBounds.bottom = 0; this._damageBounds.right = this._damageBounds.bottom = 0;
} }
}, }
clear: function () { clear() {
if (this._logo) { if (this._logo) {
this.resize(this._logo.width, this._logo.height); this.resize(this._logo.width, this._logo.height);
this.imageRect(0, 0, this._logo.type, this._logo.data); this.imageRect(0, 0, this._logo.type, this._logo.data);
@ -309,21 +311,21 @@ Display.prototype = {
this._drawCtx.clearRect(0, 0, this._fb_width, this._fb_height); this._drawCtx.clearRect(0, 0, this._fb_width, this._fb_height);
} }
this.flip(); this.flip();
}, }
pending: function() { pending() {
return this._renderQ.length > 0; return this._renderQ.length > 0;
}, }
flush: function() { flush() {
if (this._renderQ.length === 0) { if (this._renderQ.length === 0) {
this.onflush(); this.onflush();
} else { } else {
this._flushing = true; this._flushing = true;
} }
}, }
fillRect: function (x, y, width, height, color, from_queue) { fillRect(x, y, width, height, color, from_queue) {
if (this._renderQ.length !== 0 && !from_queue) { if (this._renderQ.length !== 0 && !from_queue) {
this._renderQ_push({ this._renderQ_push({
'type': 'fill', 'type': 'fill',
@ -338,9 +340,9 @@ Display.prototype = {
this._drawCtx.fillRect(x, y, width, height); this._drawCtx.fillRect(x, y, width, height);
this._damage(x, y, width, height); this._damage(x, y, width, height);
} }
}, }
copyImage: function (old_x, old_y, new_x, new_y, w, h, from_queue) { copyImage(old_x, old_y, new_x, new_y, w, h, from_queue) {
if (this._renderQ.length !== 0 && !from_queue) { if (this._renderQ.length !== 0 && !from_queue) {
this._renderQ_push({ this._renderQ_push({
'type': 'copy', 'type': 'copy',
@ -369,10 +371,10 @@ Display.prototype = {
new_x, new_y, w, h); new_x, new_y, w, h);
this._damage(new_x, new_y, w, h); this._damage(new_x, new_y, w, h);
} }
}, }
imageRect: function(x, y, mime, arr) { imageRect(x, y, mime, arr) {
var img = new Image(); const img = new Image();
img.src = "data: " + mime + ";base64," + Base64.encode(arr); img.src = "data: " + mime + ";base64," + Base64.encode(arr);
this._renderQ_push({ this._renderQ_push({
'type': 'img', 'type': 'img',
@ -380,10 +382,10 @@ Display.prototype = {
'x': x, 'x': x,
'y': y 'y': y
}); });
}, }
// start updating a tile // start updating a tile
startTile: function (x, y, width, height, color) { startTile(x, y, width, height, color) {
this._tile_x = x; this._tile_x = x;
this._tile_y = y; this._tile_y = y;
if (width === 16 && height === 16) { if (width === 16 && height === 16) {
@ -392,53 +394,53 @@ Display.prototype = {
this._tile = this._drawCtx.createImageData(width, height); this._tile = this._drawCtx.createImageData(width, height);
} }
var red = color[2]; const red = color[2];
var green = color[1]; const green = color[1];
var blue = color[0]; const blue = color[0];
var data = this._tile.data; const data = this._tile.data;
for (var i = 0; i < width * height * 4; i += 4) { for (let i = 0; i < width * height * 4; i += 4) {
data[i] = red; data[i] = red;
data[i + 1] = green; data[i + 1] = green;
data[i + 2] = blue; data[i + 2] = blue;
data[i + 3] = 255; data[i + 3] = 255;
} }
}, }
// update sub-rectangle of the current tile // update sub-rectangle of the current tile
subTile: function (x, y, w, h, color) { subTile(x, y, w, h, color) {
var red = color[2]; const red = color[2];
var green = color[1]; const green = color[1];
var blue = color[0]; const blue = color[0];
var xend = x + w; const xend = x + w;
var yend = y + h; const yend = y + h;
var data = this._tile.data; const data = this._tile.data;
var width = this._tile.width; const width = this._tile.width;
for (var j = y; j < yend; j++) { for (let j = y; j < yend; j++) {
for (var i = x; i < xend; i++) { for (let i = x; i < xend; i++) {
var p = (i + (j * width)) * 4; const p = (i + (j * width)) * 4;
data[p] = red; data[p] = red;
data[p + 1] = green; data[p + 1] = green;
data[p + 2] = blue; data[p + 2] = blue;
data[p + 3] = 255; data[p + 3] = 255;
} }
} }
}, }
// draw the current tile to the screen // draw the current tile to the screen
finishTile: function () { finishTile() {
this._drawCtx.putImageData(this._tile, this._tile_x, this._tile_y); this._drawCtx.putImageData(this._tile, this._tile_x, this._tile_y);
this._damage(this._tile_x, this._tile_y, this._damage(this._tile_x, this._tile_y,
this._tile.width, this._tile.height); this._tile.width, this._tile.height);
}, }
blitImage: function (x, y, width, height, arr, offset, from_queue) { blitImage(x, y, width, height, arr, offset, from_queue) {
if (this._renderQ.length !== 0 && !from_queue) { if (this._renderQ.length !== 0 && !from_queue) {
// NB(directxman12): it's technically more performant here to use preallocated arrays, // NB(directxman12): it's technically more performant here to use preallocated arrays,
// but it's a lot of extra work for not a lot of payoff -- if we're using the render queue, // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
// this probably isn't getting called *nearly* as much // this probably isn't getting called *nearly* as much
var new_arr = new Uint8Array(width * height * 4); const new_arr = new Uint8Array(width * height * 4);
new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length)); new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
this._renderQ_push({ this._renderQ_push({
'type': 'blit', 'type': 'blit',
@ -451,14 +453,14 @@ Display.prototype = {
} else { } else {
this._bgrxImageData(x, y, width, height, arr, offset); this._bgrxImageData(x, y, width, height, arr, offset);
} }
}, }
blitRgbImage: function (x, y , width, height, arr, offset, from_queue) { blitRgbImage(x, y, width, height, arr, offset, from_queue) {
if (this._renderQ.length !== 0 && !from_queue) { if (this._renderQ.length !== 0 && !from_queue) {
// NB(directxman12): it's technically more performant here to use preallocated arrays, // NB(directxman12): it's technically more performant here to use preallocated arrays,
// but it's a lot of extra work for not a lot of payoff -- if we're using the render queue, // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
// this probably isn't getting called *nearly* as much // this probably isn't getting called *nearly* as much
var new_arr = new Uint8Array(width * height * 3); const new_arr = new Uint8Array(width * height * 3);
new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length)); new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
this._renderQ_push({ this._renderQ_push({
'type': 'blitRgb', 'type': 'blitRgb',
@ -471,14 +473,14 @@ Display.prototype = {
} else { } else {
this._rgbImageData(x, y, width, height, arr, offset); this._rgbImageData(x, y, width, height, arr, offset);
} }
}, }
blitRgbxImage: function (x, y, width, height, arr, offset, from_queue) { blitRgbxImage(x, y, width, height, arr, offset, from_queue) {
if (this._renderQ.length !== 0 && !from_queue) { if (this._renderQ.length !== 0 && !from_queue) {
// NB(directxman12): it's technically more performant here to use preallocated arrays, // NB(directxman12): it's technically more performant here to use preallocated arrays,
// but it's a lot of extra work for not a lot of payoff -- if we're using the render queue, // but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
// this probably isn't getting called *nearly* as much // this probably isn't getting called *nearly* as much
var new_arr = new Uint8Array(width * height * 4); const new_arr = new Uint8Array(width * height * 4);
new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length)); new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
this._renderQ_push({ this._renderQ_push({
'type': 'blitRgbx', 'type': 'blitRgbx',
@ -491,72 +493,67 @@ Display.prototype = {
} else { } else {
this._rgbxImageData(x, y, width, height, arr, offset); this._rgbxImageData(x, y, width, height, arr, offset);
} }
}, }
drawImage: function (img, x, y) { drawImage(img, x, y) {
this._drawCtx.drawImage(img, x, y); this._drawCtx.drawImage(img, x, y);
this._damage(x, y, img.width, img.height); this._damage(x, y, img.width, img.height);
}, }
changeCursor: function (pixels, mask, hotx, hoty, w, h) { autoscale(containerWidth, containerHeight) {
Display.changeCursor(this._target, pixels, mask, hotx, hoty, w, h); let scaleRatio;
},
defaultCursor: function () { if (containerWidth === 0 || containerHeight === 0) {
this._target.style.cursor = "default"; scaleRatio = 0;
},
disableLocalCursor: function () {
this._target.style.cursor = "none";
},
autoscale: function (containerWidth, containerHeight) {
var vp = this._viewportLoc;
var targetAspectRatio = containerWidth / containerHeight;
var fbAspectRatio = vp.w / vp.h;
var scaleRatio;
if (fbAspectRatio >= targetAspectRatio) {
scaleRatio = containerWidth / vp.w;
} else { } else {
scaleRatio = containerHeight / vp.h;
const vp = this._viewportLoc;
const targetAspectRatio = containerWidth / containerHeight;
const fbAspectRatio = vp.w / vp.h;
if (fbAspectRatio >= targetAspectRatio) {
scaleRatio = containerWidth / vp.w;
} else {
scaleRatio = containerHeight / vp.h;
}
} }
this._rescale(scaleRatio); this._rescale(scaleRatio);
}, }
// ===== PRIVATE METHODS ===== // ===== PRIVATE METHODS =====
_rescale: function (factor) { _rescale(factor) {
this._scale = factor; this._scale = factor;
var vp = this._viewportLoc; const vp = this._viewportLoc;
// NB(directxman12): If you set the width directly, or set the // NB(directxman12): If you set the width directly, or set the
// style width to a number, the canvas is cleared. // style width to a number, the canvas is cleared.
// However, if you set the style width to a string // However, if you set the style width to a string
// ('NNNpx'), the canvas is scaled without clearing. // ('NNNpx'), the canvas is scaled without clearing.
var width = Math.round(factor * vp.w) + 'px'; const width = factor * vp.w + 'px';
var height = Math.round(factor * vp.h) + 'px'; const height = factor * vp.h + 'px';
if ((this._target.style.width !== width) || if ((this._target.style.width !== width) ||
(this._target.style.height !== height)) { (this._target.style.height !== height)) {
this._target.style.width = width; this._target.style.width = width;
this._target.style.height = height; this._target.style.height = height;
} }
}, }
_setFillColor: function (color) { _setFillColor(color) {
var newStyle = 'rgb(' + color[2] + ',' + color[1] + ',' + color[0] + ')'; const newStyle = 'rgb(' + color[2] + ',' + color[1] + ',' + color[0] + ')';
if (newStyle !== this._prevDrawStyle) { if (newStyle !== this._prevDrawStyle) {
this._drawCtx.fillStyle = newStyle; this._drawCtx.fillStyle = newStyle;
this._prevDrawStyle = newStyle; this._prevDrawStyle = newStyle;
} }
}, }
_rgbImageData: function (x, y, width, height, arr, offset) { _rgbImageData(x, y, width, height, arr, offset) {
var img = this._drawCtx.createImageData(width, height); const img = this._drawCtx.createImageData(width, height);
var data = img.data; const data = img.data;
for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 3) { for (let i = 0, j = offset; i < width * height * 4; i += 4, j += 3) {
data[i] = arr[j]; data[i] = arr[j];
data[i + 1] = arr[j + 1]; data[i + 1] = arr[j + 1];
data[i + 2] = arr[j + 2]; data[i + 2] = arr[j + 2];
@ -564,12 +561,12 @@ Display.prototype = {
} }
this._drawCtx.putImageData(img, x, y); this._drawCtx.putImageData(img, x, y);
this._damage(x, y, img.width, img.height); this._damage(x, y, img.width, img.height);
}, }
_bgrxImageData: function (x, y, width, height, arr, offset) { _bgrxImageData(x, y, width, height, arr, offset) {
var img = this._drawCtx.createImageData(width, height); const img = this._drawCtx.createImageData(width, height);
var data = img.data; const data = img.data;
for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 4) { for (let i = 0, j = offset; i < width * height * 4; i += 4, j += 4) {
data[i] = arr[j + 2]; data[i] = arr[j + 2];
data[i + 1] = arr[j + 1]; data[i + 1] = arr[j + 1];
data[i + 2] = arr[j]; data[i + 2] = arr[j];
@ -577,12 +574,12 @@ Display.prototype = {
} }
this._drawCtx.putImageData(img, x, y); this._drawCtx.putImageData(img, x, y);
this._damage(x, y, img.width, img.height); this._damage(x, y, img.width, img.height);
}, }
_rgbxImageData: function (x, y, width, height, arr, offset) { _rgbxImageData(x, y, width, height, arr, offset) {
// NB(directxman12): arr must be an Type Array view // NB(directxman12): arr must be an Type Array view
var img; let img;
if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) { if (supportsImageMetadata) {
img = new ImageData(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4), width, height); img = new ImageData(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4), width, height);
} else { } else {
img = this._drawCtx.createImageData(width, height); img = this._drawCtx.createImageData(width, height);
@ -590,28 +587,28 @@ Display.prototype = {
} }
this._drawCtx.putImageData(img, x, y); this._drawCtx.putImageData(img, x, y);
this._damage(x, y, img.width, img.height); this._damage(x, y, img.width, img.height);
}, }
_renderQ_push: function (action) { _renderQ_push(action) {
this._renderQ.push(action); this._renderQ.push(action);
if (this._renderQ.length === 1) { if (this._renderQ.length === 1) {
// If this can be rendered immediately it will be, otherwise // If this can be rendered immediately it will be, otherwise
// the scanner will wait for the relevant event // the scanner will wait for the relevant event
this._scan_renderQ(); this._scan_renderQ();
} }
}, }
_resume_renderQ: function() { _resume_renderQ() {
// "this" is the object that is ready, not the // "this" is the object that is ready, not the
// display object // display object
this.removeEventListener('load', this._noVNC_display._resume_renderQ); this.removeEventListener('load', this._noVNC_display._resume_renderQ);
this._noVNC_display._scan_renderQ(); this._noVNC_display._scan_renderQ();
}, }
_scan_renderQ: function () { _scan_renderQ() {
var ready = true; let ready = true;
while (ready && this._renderQ.length > 0) { while (ready && this._renderQ.length > 0) {
var a = this._renderQ[0]; const a = this._renderQ[0];
switch (a.type) { switch (a.type) {
case 'flip': case 'flip':
this.flip(true); this.flip(true);
@ -653,46 +650,5 @@ Display.prototype = {
this._flushing = false; this._flushing = false;
this.onflush(); this.onflush();
} }
},
};
// Class Methods
Display.changeCursor = function (target, pixels, mask, hotx, hoty, w, h) {
if ((w === 0) || (h === 0)) {
target.style.cursor = 'none';
return;
} }
}
var cur = []
var y, x;
for (y = 0; y < h; y++) {
for (x = 0; x < w; x++) {
var idx = y * Math.ceil(w / 8) + Math.floor(x / 8);
var alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0;
idx = ((w * y) + x) * 4;
cur.push(pixels[idx + 2]); // red
cur.push(pixels[idx + 1]); // green
cur.push(pixels[idx]); // blue
cur.push(alpha); // alpha
}
}
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
canvas.width = w;
canvas.height = h;
var img;
if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) {
img = new ImageData(new Uint8ClampedArray(cur), w, h);
} else {
img = ctx.createImageData(w, h);
img.data.set(new Uint8ClampedArray(cur));
}
ctx.clearRect(0, 0, w, h);
ctx.putImageData(img, 0, 0);
var url = canvas.toDataURL();
target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
};

View file

@ -1,17 +1,18 @@
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright (C) 2017 Pierre Ossman for Cendio AB * Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt) * Licensed under MPL 2.0 (see LICENSE.txt)
* *
* See README.md for usage and integration instructions. * See README.md for usage and integration instructions.
*/ */
export var encodings = { export const encodings = {
encodingRaw: 0, encodingRaw: 0,
encodingCopyRect: 1, encodingCopyRect: 1,
encodingRRE: 2, encodingRRE: 2,
encodingHextile: 5, encodingHextile: 5,
encodingTight: 7, encodingTight: 7,
encodingTightPNG: -260,
pseudoEncodingQualityLevel9: -23, pseudoEncodingQualityLevel9: -23,
pseudoEncodingQualityLevel0: -32, pseudoEncodingQualityLevel0: -32,
@ -19,7 +20,6 @@ export var encodings = {
pseudoEncodingLastRect: -224, pseudoEncodingLastRect: -224,
pseudoEncodingCursor: -239, pseudoEncodingCursor: -239,
pseudoEncodingQEMUExtendedKeyEvent: -258, pseudoEncodingQEMUExtendedKeyEvent: -258,
pseudoEncodingTightPNG: -260,
pseudoEncodingExtendedDesktopSize: -308, pseudoEncodingExtendedDesktopSize: -308,
pseudoEncodingXvp: -309, pseudoEncodingXvp: -309,
pseudoEncodingFence: -312, pseudoEncodingFence: -312,
@ -35,6 +35,7 @@ export function encodingName(num) {
case encodings.encodingRRE: return "RRE"; case encodings.encodingRRE: return "RRE";
case encodings.encodingHextile: return "Hextile"; case encodings.encodingHextile: return "Hextile";
case encodings.encodingTight: return "Tight"; case encodings.encodingTight: return "Tight";
case encodings.encodingTightPNG: return "TightPNG";
default: return "[unknown encoding " + num + "]"; default: return "[unknown encoding " + num + "]";
} }
} }

View file

@ -1,8 +1,17 @@
import { inflateInit, inflate, inflateReset } from "../vendor/pako/lib/zlib/inflate.js"; import { inflateInit, inflate, inflateReset } from "../vendor/pako/lib/zlib/inflate.js";
import ZStream from "../vendor/pako/lib/zlib/zstream.js"; import ZStream from "../vendor/pako/lib/zlib/zstream.js";
Inflate.prototype = { export default class Inflate {
inflate: function (data, flush, expected) { constructor() {
this.strm = new ZStream();
this.chunkSize = 1024 * 10 * 10;
this.strm.output = new Uint8Array(this.chunkSize);
this.windowBits = 5;
inflateInit(this.strm, this.windowBits);
}
inflate(data, flush, expected) {
this.strm.input = data; this.strm.input = data;
this.strm.avail_in = this.strm.input.length; this.strm.avail_in = this.strm.input.length;
this.strm.next_in = 0; this.strm.next_in = 0;
@ -21,18 +30,9 @@ Inflate.prototype = {
inflate(this.strm, flush); inflate(this.strm, flush);
return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out); return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
}, }
reset: function () { reset() {
inflateReset(this.strm); inflateReset(this.strm);
} }
}; }
export default function Inflate() {
this.strm = new ZStream();
this.chunkSize = 1024 * 10 * 10;
this.strm.output = new Uint8Array(this.chunkSize);
this.windowBits = 5;
inflateInit(this.strm, this.windowBits);
};

View file

@ -1,6 +1,6 @@
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright (C) 2017 Pierre Ossman for Cendio AB * Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 or any later version (see LICENSE.txt) * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/ */
@ -13,28 +13,25 @@ import KeyTable from "./keysym.js";
* See https://www.w3.org/TR/uievents-key/ for possible values. * See https://www.w3.org/TR/uievents-key/ for possible values.
*/ */
var DOMKeyTable = {}; const DOMKeyTable = {};
function addStandard(key, standard) function addStandard(key, standard) {
{ if (standard === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
if (standard === undefined) throw "Undefined keysym for key \"" + key + "\""; if (key in DOMKeyTable) throw new Error("Duplicate entry for key \"" + key + "\"");
if (key in DOMKeyTable) throw "Duplicate entry for key \"" + key + "\"";
DOMKeyTable[key] = [standard, standard, standard, standard]; DOMKeyTable[key] = [standard, standard, standard, standard];
} }
function addLeftRight(key, left, right) function addLeftRight(key, left, right) {
{ if (left === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
if (left === undefined) throw "Undefined keysym for key \"" + key + "\""; if (right === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
if (right === undefined) throw "Undefined keysym for key \"" + key + "\""; if (key in DOMKeyTable) throw new Error("Duplicate entry for key \"" + key + "\"");
if (key in DOMKeyTable) throw "Duplicate entry for key \"" + key + "\"";
DOMKeyTable[key] = [left, left, right, left]; DOMKeyTable[key] = [left, left, right, left];
} }
function addNumpad(key, standard, numpad) function addNumpad(key, standard, numpad) {
{ if (standard === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
if (standard === undefined) throw "Undefined keysym for key \"" + key + "\""; if (numpad === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
if (numpad === undefined) throw "Undefined keysym for key \"" + key + "\""; if (key in DOMKeyTable) throw new Error("Duplicate entry for key \"" + key + "\"");
if (key in DOMKeyTable) throw "Duplicate entry for key \"" + key + "\"";
DOMKeyTable[key] = [standard, standard, standard, numpad]; DOMKeyTable[key] = [standard, standard, standard, numpad];
} }
@ -75,7 +72,7 @@ addNumpad("PageUp", KeyTable.XK_Prior, KeyTable.XK_KP_Prior);
// 2.5. Editing Keys // 2.5. Editing Keys
addStandard("Backspace", KeyTable.XK_BackSpace); addStandard("Backspace", KeyTable.XK_BackSpace);
addStandard("Clear", KeyTable.XK_Clear); addNumpad("Clear", KeyTable.XK_Clear, KeyTable.XK_KP_Begin);
addStandard("Copy", KeyTable.XF86XK_Copy); addStandard("Copy", KeyTable.XF86XK_Copy);
// - CrSel // - CrSel
addStandard("Cut", KeyTable.XF86XK_Cut); addStandard("Cut", KeyTable.XF86XK_Cut);

View file

@ -1,6 +1,6 @@
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright (C) 2017 Pierre Ossman for Cendio AB * Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 or any later version (see LICENSE.txt) * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/ */
@ -14,6 +14,8 @@
* See https://www.w3.org/TR/uievents-key/ for possible values. * See https://www.w3.org/TR/uievents-key/ for possible values.
*/ */
/* eslint-disable key-spacing */
export default { export default {
// 3.1.1.1. Writing System Keys // 3.1.1.1. Writing System Keys

View file

@ -1,7 +1,6 @@
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin * Copyright (C) 2018 The noVNC Authors
* Copyright (C) 2013 Samuel Mannehed for Cendio AB
* Licensed under MPL 2.0 or any later version (see LICENSE.txt) * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/ */
@ -15,63 +14,49 @@ import * as browser from "../util/browser.js";
// Keyboard event handler // Keyboard event handler
// //
export default function Keyboard(target) { export default class Keyboard {
this._target = target || null; constructor(target) {
this._target = target || null;
this._keyDownList = {}; // List of depressed keys this._keyDownList = {}; // List of depressed keys
// (even if they are happy) // (even if they are happy)
this._pendingKey = null; // Key waiting for keypress this._pendingKey = null; // Key waiting for keypress
this._altGrArmed = false; // Windows AltGr detection
// keep these here so we can refer to them later // keep these here so we can refer to them later
this._eventHandlers = { this._eventHandlers = {
'keyup': this._handleKeyUp.bind(this), 'keyup': this._handleKeyUp.bind(this),
'keydown': this._handleKeyDown.bind(this), 'keydown': this._handleKeyDown.bind(this),
'keypress': this._handleKeyPress.bind(this), 'keypress': this._handleKeyPress.bind(this),
'blur': this._allKeysUp.bind(this) 'blur': this._allKeysUp.bind(this),
}; 'checkalt': this._checkAlt.bind(this),
}; };
Keyboard.prototype = { // ===== EVENT HANDLERS =====
// ===== EVENT HANDLERS =====
onkeyevent: function () {}, // Handler for key press/release this.onkeyevent = () => {}; // Handler for key press/release
}
// ===== PRIVATE METHODS ===== // ===== PRIVATE METHODS =====
_sendKeyEvent: function (keysym, code, down) { _sendKeyEvent(keysym, code, down) {
if (down) {
this._keyDownList[code] = keysym;
} else {
// Do we really think this key is down?
if (!(code in this._keyDownList)) {
return;
}
delete this._keyDownList[code];
}
Log.Debug("onkeyevent " + (down ? "down" : "up") + Log.Debug("onkeyevent " + (down ? "down" : "up") +
", keysym: " + keysym, ", code: " + code); ", keysym: " + keysym, ", code: " + code);
// Windows sends CtrlLeft+AltRight when you press
// AltGraph, which tends to confuse the hell out of
// remote systems. Fake a release of these keys until
// there is a way to detect AltGraph properly.
var fakeAltGraph = false;
if (down && browser.isWindows()) {
if ((code !== 'ControlLeft') &&
(code !== 'AltRight') &&
('ControlLeft' in this._keyDownList) &&
('AltRight' in this._keyDownList)) {
fakeAltGraph = true;
this.onkeyevent(this._keyDownList['AltRight'],
'AltRight', false);
this.onkeyevent(this._keyDownList['ControlLeft'],
'ControlLeft', false);
}
}
this.onkeyevent(keysym, code, down); this.onkeyevent(keysym, code, down);
}
if (fakeAltGraph) { _getKeyCode(e) {
this.onkeyevent(this._keyDownList['ControlLeft'], const code = KeyboardUtil.getKeycode(e);
'ControlLeft', true);
this.onkeyevent(this._keyDownList['AltRight'],
'AltRight', true);
}
},
_getKeyCode: function (e) {
var code = KeyboardUtil.getKeycode(e);
if (code !== 'Unidentified') { if (code !== 'Unidentified') {
return code; return code;
} }
@ -94,20 +79,42 @@ Keyboard.prototype = {
return e.keyIdentifier; return e.keyIdentifier;
} }
var codepoint = parseInt(e.keyIdentifier.substr(2), 16); const codepoint = parseInt(e.keyIdentifier.substr(2), 16);
var char = String.fromCharCode(codepoint); const char = String.fromCharCode(codepoint).toUpperCase();
// Some implementations fail to uppercase the symbols
char = char.toUpperCase();
return 'Platform' + char.charCodeAt(); return 'Platform' + char.charCodeAt();
} }
return 'Unidentified'; return 'Unidentified';
}, }
_handleKeyDown: function (e) { _handleKeyDown(e) {
var code = this._getKeyCode(e); const code = this._getKeyCode(e);
var keysym = KeyboardUtil.getKeysym(e); let keysym = KeyboardUtil.getKeysym(e);
// Windows doesn't have a proper AltGr, but handles it using
// fake Ctrl+Alt. However the remote end might not be Windows,
// so we need to merge those in to a single AltGr event. We
// detect this case by seeing the two key events directly after
// each other with a very short time between them (<50ms).
if (this._altGrArmed) {
this._altGrArmed = false;
clearTimeout(this._altGrTimeout);
if ((code === "AltRight") &&
((e.timeStamp - this._altGrCtrlTime) < 50)) {
// FIXME: We fail to detect this if either Ctrl key is
// first manually pressed as Windows then no
// longer sends the fake Ctrl down event. It
// does however happily send real Ctrl events
// even when AltGr is already down. Some
// browsers detect this for us though and set the
// key to "AltGraph".
keysym = KeyTable.XK_ISO_Level3_Shift;
} else {
this._sendKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
}
}
// We cannot handle keys we cannot track, but we also need // We cannot handle keys we cannot track, but we also need
// to deal with virtual keyboards which omit key info // to deal with virtual keyboards which omit key info
@ -132,18 +139,18 @@ Keyboard.prototype = {
// possibly others). // possibly others).
if (browser.isMac()) { if (browser.isMac()) {
switch (keysym) { switch (keysym) {
case KeyTable.XK_Super_L: case KeyTable.XK_Super_L:
keysym = KeyTable.XK_Alt_L; keysym = KeyTable.XK_Alt_L;
break; break;
case KeyTable.XK_Super_R: case KeyTable.XK_Super_R:
keysym = KeyTable.XK_Super_L; keysym = KeyTable.XK_Super_L;
break; break;
case KeyTable.XK_Alt_L: case KeyTable.XK_Alt_L:
keysym = KeyTable.XK_Mode_switch; keysym = KeyTable.XK_Mode_switch;
break; break;
case KeyTable.XK_Alt_R: case KeyTable.XK_Alt_R:
keysym = KeyTable.XK_ISO_Level3_Shift; keysym = KeyTable.XK_ISO_Level3_Shift;
break; break;
} }
} }
@ -180,13 +187,20 @@ Keyboard.prototype = {
this._pendingKey = null; this._pendingKey = null;
stopEvent(e); stopEvent(e);
this._keyDownList[code] = keysym; // Possible start of AltGr sequence? (see above)
if ((code === "ControlLeft") && browser.isWindows() &&
!("ControlLeft" in this._keyDownList)) {
this._altGrArmed = true;
this._altGrTimeout = setTimeout(this._handleAltGrTimeout.bind(this), 100);
this._altGrCtrlTime = e.timeStamp;
return;
}
this._sendKeyEvent(keysym, code, true); this._sendKeyEvent(keysym, code, true);
}, }
// Legacy event for browsers without code/key // Legacy event for browsers without code/key
_handleKeyPress: function (e) { _handleKeyPress(e) {
stopEvent(e); stopEvent(e);
// Are we expecting a keypress? // Are we expecting a keypress?
@ -194,8 +208,8 @@ Keyboard.prototype = {
return; return;
} }
var code = this._getKeyCode(e); let code = this._getKeyCode(e);
var keysym = KeyboardUtil.getKeysym(e); const keysym = KeyboardUtil.getKeysym(e);
// The key we were waiting for? // The key we were waiting for?
if ((code !== 'Unidentified') && (code != this._pendingKey)) { if ((code !== 'Unidentified') && (code != this._pendingKey)) {
@ -210,19 +224,18 @@ Keyboard.prototype = {
return; return;
} }
this._keyDownList[code] = keysym;
this._sendKeyEvent(keysym, code, true); this._sendKeyEvent(keysym, code, true);
}, }
_handleKeyPressTimeout: function (e) {
_handleKeyPressTimeout(e) {
// Did someone manage to sort out the key already? // Did someone manage to sort out the key already?
if (this._pendingKey === null) { if (this._pendingKey === null) {
return; return;
} }
var code, keysym; let keysym;
code = this._pendingKey; const code = this._pendingKey;
this._pendingKey = null; this._pendingKey = null;
// We have no way of knowing the proper keysym with the // We have no way of knowing the proper keysym with the
@ -233,27 +246,34 @@ Keyboard.prototype = {
keysym = e.keyCode; keysym = e.keyCode;
} else if ((e.keyCode >= 0x41) && (e.keyCode <= 0x5a)) { } else if ((e.keyCode >= 0x41) && (e.keyCode <= 0x5a)) {
// Character (A-Z) // Character (A-Z)
var char = String.fromCharCode(e.keyCode); let char = String.fromCharCode(e.keyCode);
// A feeble attempt at the correct case // A feeble attempt at the correct case
if (e.shiftKey) if (e.shiftKey) {
char = char.toUpperCase(); char = char.toUpperCase();
else } else {
char = char.toLowerCase(); char = char.toLowerCase();
}
keysym = char.charCodeAt(); keysym = char.charCodeAt();
} else { } else {
// Unknown, give up // Unknown, give up
keysym = 0; keysym = 0;
} }
this._keyDownList[code] = keysym;
this._sendKeyEvent(keysym, code, true); this._sendKeyEvent(keysym, code, true);
}, }
_handleKeyUp: function (e) { _handleKeyUp(e) {
stopEvent(e); stopEvent(e);
var code = this._getKeyCode(e); const code = this._getKeyCode(e);
// We can't get a release in the middle of an AltGr sequence, so
// abort that detection
if (this._altGrArmed) {
this._altGrArmed = false;
clearTimeout(this._altGrTimeout);
this._sendKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
}
// See comment in _handleKeyDown() // See comment in _handleKeyDown()
if (browser.isMac() && (code === 'CapsLock')) { if (browser.isMac() && (code === 'CapsLock')) {
@ -262,53 +282,89 @@ Keyboard.prototype = {
return; return;
} }
// Do we really think this key is down? this._sendKeyEvent(this._keyDownList[code], code, false);
if (!(code in this._keyDownList)) { }
_handleAltGrTimeout() {
this._altGrArmed = false;
clearTimeout(this._altGrTimeout);
this._sendKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
}
_allKeysUp() {
Log.Debug(">> Keyboard.allKeysUp");
for (let code in this._keyDownList) {
this._sendKeyEvent(this._keyDownList[code], code, false);
}
Log.Debug("<< Keyboard.allKeysUp");
}
// Firefox Alt workaround, see below
_checkAlt(e) {
if (e.altKey) {
return; return;
} }
this._sendKeyEvent(this._keyDownList[code], code, false); const target = this._target;
const downList = this._keyDownList;
['AltLeft', 'AltRight'].forEach((code) => {
if (!(code in downList)) {
return;
}
delete this._keyDownList[code]; const event = new KeyboardEvent('keyup',
}, { key: downList[code],
code: code });
_allKeysUp: function () { target.dispatchEvent(event);
Log.Debug(">> Keyboard.allKeysUp"); });
for (var code in this._keyDownList) { }
this._sendKeyEvent(this._keyDownList[code], code, false);
};
this._keyDownList = {};
Log.Debug("<< Keyboard.allKeysUp");
},
// ===== PUBLIC METHODS ===== // ===== PUBLIC METHODS =====
grab: function () { grab() {
//Log.Debug(">> Keyboard.grab"); //Log.Debug(">> Keyboard.grab");
var c = this._target;
c.addEventListener('keydown', this._eventHandlers.keydown); this._target.addEventListener('keydown', this._eventHandlers.keydown);
c.addEventListener('keyup', this._eventHandlers.keyup); this._target.addEventListener('keyup', this._eventHandlers.keyup);
c.addEventListener('keypress', this._eventHandlers.keypress); this._target.addEventListener('keypress', this._eventHandlers.keypress);
// Release (key up) if window loses focus // Release (key up) if window loses focus
window.addEventListener('blur', this._eventHandlers.blur); window.addEventListener('blur', this._eventHandlers.blur);
// Firefox has broken handling of Alt, so we need to poll as
// best we can for releases (still doesn't prevent the menu
// from popping up though as we can't call preventDefault())
if (browser.isWindows() && browser.isFirefox()) {
const handler = this._eventHandlers.checkalt;
['mousedown', 'mouseup', 'mousemove', 'wheel',
'touchstart', 'touchend', 'touchmove',
'keydown', 'keyup'].forEach(type =>
document.addEventListener(type, handler,
{ capture: true,
passive: true }));
}
//Log.Debug("<< Keyboard.grab"); //Log.Debug("<< Keyboard.grab");
}, }
ungrab: function () { ungrab() {
//Log.Debug(">> Keyboard.ungrab"); //Log.Debug(">> Keyboard.ungrab");
var c = this._target;
c.removeEventListener('keydown', this._eventHandlers.keydown); if (browser.isWindows() && browser.isFirefox()) {
c.removeEventListener('keyup', this._eventHandlers.keyup); const handler = this._eventHandlers.checkalt;
c.removeEventListener('keypress', this._eventHandlers.keypress); ['mousedown', 'mouseup', 'mousemove', 'wheel',
'touchstart', 'touchend', 'touchmove',
'keydown', 'keyup'].forEach(type => document.removeEventListener(type, handler));
}
this._target.removeEventListener('keydown', this._eventHandlers.keydown);
this._target.removeEventListener('keyup', this._eventHandlers.keyup);
this._target.removeEventListener('keypress', this._eventHandlers.keypress);
window.removeEventListener('blur', this._eventHandlers.blur); window.removeEventListener('blur', this._eventHandlers.blur);
// Release (key up) all keys that are in a down state // Release (key up) all keys that are in a down state
this._allKeysUp(); this._allKeysUp();
//Log.Debug(">> Keyboard.ungrab"); //Log.Debug(">> Keyboard.ungrab");
}, }
}; }

View file

@ -1,3 +1,5 @@
/* eslint-disable key-spacing */
export default { export default {
XK_VoidSymbol: 0xffffff, /* Void symbol */ XK_VoidSymbol: 0xffffff, /* Void symbol */

View file

@ -7,7 +7,7 @@
/* Functions at the bottom */ /* Functions at the bottom */
var codepoints = { const codepoints = {
0x0100: 0x03c0, // XK_Amacron 0x0100: 0x03c0, // XK_Amacron
0x0101: 0x03e0, // XK_amacron 0x0101: 0x03e0, // XK_amacron
0x0102: 0x01c3, // XK_Abreve 0x0102: 0x01c3, // XK_Abreve
@ -670,14 +670,14 @@ var codepoints = {
}; };
export default { export default {
lookup : function(u) { lookup(u) {
// Latin-1 is one-to-one mapping // Latin-1 is one-to-one mapping
if ((u >= 0x20) && (u <= 0xff)) { if ((u >= 0x20) && (u <= 0xff)) {
return u; return u;
} }
// Lookup table (fairly random) // Lookup table (fairly random)
var keysym = codepoints[u]; const keysym = codepoints[u];
if (keysym !== undefined) { if (keysym !== undefined) {
return keysym; return keysym;
} }

View file

@ -1,7 +1,6 @@
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin * Copyright (C) 2018 The noVNC Authors
* Copyright (C) 2013 Samuel Mannehed for Cendio AB
* Licensed under MPL 2.0 or any later version (see LICENSE.txt) * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/ */
@ -9,52 +8,52 @@ import * as Log from '../util/logging.js';
import { isTouchDevice } from '../util/browser.js'; import { isTouchDevice } from '../util/browser.js';
import { setCapture, stopEvent, getPointerEvent } from '../util/events.js'; import { setCapture, stopEvent, getPointerEvent } from '../util/events.js';
var WHEEL_STEP = 10; // Delta threshold for a mouse wheel step const WHEEL_STEP = 10; // Delta threshold for a mouse wheel step
var WHEEL_STEP_TIMEOUT = 50; // ms const WHEEL_STEP_TIMEOUT = 50; // ms
var WHEEL_LINE_HEIGHT = 19; const WHEEL_LINE_HEIGHT = 19;
export default function Mouse(target) { export default class Mouse {
this._target = target || document; constructor(target) {
this._target = target || document;
this._doubleClickTimer = null; this._doubleClickTimer = null;
this._lastTouchPos = null; this._lastTouchPos = null;
this._pos = null; this._pos = null;
this._wheelStepXTimer = null; this._wheelStepXTimer = null;
this._wheelStepYTimer = null; this._wheelStepYTimer = null;
this._accumulatedWheelDeltaX = 0; this._accumulatedWheelDeltaX = 0;
this._accumulatedWheelDeltaY = 0; this._accumulatedWheelDeltaY = 0;
this._eventHandlers = { this._eventHandlers = {
'mousedown': this._handleMouseDown.bind(this), 'mousedown': this._handleMouseDown.bind(this),
'mouseup': this._handleMouseUp.bind(this), 'mouseup': this._handleMouseUp.bind(this),
'mousemove': this._handleMouseMove.bind(this), 'mousemove': this._handleMouseMove.bind(this),
'mousewheel': this._handleMouseWheel.bind(this), 'mousewheel': this._handleMouseWheel.bind(this),
'mousedisable': this._handleMouseDisable.bind(this) 'mousedisable': this._handleMouseDisable.bind(this)
}; };
};
Mouse.prototype = { // ===== PROPERTIES =====
// ===== PROPERTIES =====
touchButton: 1, // Button mask (1, 2, 4) for touch devices (0 means ignore clicks) this.touchButton = 1; // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
// ===== EVENT HANDLERS ===== // ===== EVENT HANDLERS =====
onmousebutton: function () {}, // Handler for mouse button click/release this.onmousebutton = () => {}; // Handler for mouse button click/release
onmousemove: function () {}, // Handler for mouse movement this.onmousemove = () => {}; // Handler for mouse movement
}
// ===== PRIVATE METHODS ===== // ===== PRIVATE METHODS =====
_resetDoubleClickTimer: function () { _resetDoubleClickTimer() {
this._doubleClickTimer = null; this._doubleClickTimer = null;
}, }
_handleMouseButton: function (e, down) { _handleMouseButton(e, down) {
this._updateMousePosition(e); this._updateMousePosition(e);
var pos = this._pos; let pos = this._pos;
var bmask; let bmask;
if (e.touches || e.changedTouches) { if (e.touches || e.changedTouches) {
// Touch device // Touch device
@ -70,13 +69,13 @@ Mouse.prototype = {
// force the position of the latter touch to the position of // force the position of the latter touch to the position of
// the first. // the first.
var xs = this._lastTouchPos.x - pos.x; const xs = this._lastTouchPos.x - pos.x;
var ys = this._lastTouchPos.y - pos.y; const ys = this._lastTouchPos.y - pos.y;
var d = Math.sqrt((xs * xs) + (ys * ys)); const d = Math.sqrt((xs * xs) + (ys * ys));
// The goal is to trigger on a certain physical width, the // The goal is to trigger on a certain physical width, the
// devicePixelRatio brings us a bit closer but is not optimal. // devicePixelRatio brings us a bit closer but is not optimal.
var threshold = 20 * (window.devicePixelRatio || 1); const threshold = 20 * (window.devicePixelRatio || 1);
if (d < threshold) { if (d < threshold) {
pos = this._lastTouchPos; pos = this._lastTouchPos;
} }
@ -100,25 +99,25 @@ Mouse.prototype = {
this.onmousebutton(pos.x, pos.y, down, bmask); this.onmousebutton(pos.x, pos.y, down, bmask);
stopEvent(e); stopEvent(e);
}, }
_handleMouseDown: function (e) { _handleMouseDown(e) {
// Touch events have implicit capture // Touch events have implicit capture
if (e.type === "mousedown") { if (e.type === "mousedown") {
setCapture(this._target); setCapture(this._target);
} }
this._handleMouseButton(e, 1); this._handleMouseButton(e, 1);
}, }
_handleMouseUp: function (e) { _handleMouseUp(e) {
this._handleMouseButton(e, 0); this._handleMouseButton(e, 0);
}, }
// Mouse wheel events are sent in steps over VNC. This means that the VNC // Mouse wheel events are sent in steps over VNC. This means that the VNC
// protocol can't handle a wheel event with specific distance or speed. // protocol can't handle a wheel event with specific distance or speed.
// Therefor, if we get a lot of small mouse wheel events we combine them. // Therefor, if we get a lot of small mouse wheel events we combine them.
_generateWheelStepX: function () { _generateWheelStepX() {
if (this._accumulatedWheelDeltaX < 0) { if (this._accumulatedWheelDeltaX < 0) {
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 5); this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 5);
@ -129,9 +128,9 @@ Mouse.prototype = {
} }
this._accumulatedWheelDeltaX = 0; this._accumulatedWheelDeltaX = 0;
}, }
_generateWheelStepY: function () { _generateWheelStepY() {
if (this._accumulatedWheelDeltaY < 0) { if (this._accumulatedWheelDeltaY < 0) {
this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 3); this.onmousebutton(this._pos.x, this._pos.y, 1, 1 << 3);
@ -142,22 +141,22 @@ Mouse.prototype = {
} }
this._accumulatedWheelDeltaY = 0; this._accumulatedWheelDeltaY = 0;
}, }
_resetWheelStepTimers: function () { _resetWheelStepTimers() {
window.clearTimeout(this._wheelStepXTimer); window.clearTimeout(this._wheelStepXTimer);
window.clearTimeout(this._wheelStepYTimer); window.clearTimeout(this._wheelStepYTimer);
this._wheelStepXTimer = null; this._wheelStepXTimer = null;
this._wheelStepYTimer = null; this._wheelStepYTimer = null;
}, }
_handleMouseWheel: function (e) { _handleMouseWheel(e) {
this._resetWheelStepTimers(); this._resetWheelStepTimers();
this._updateMousePosition(e); this._updateMousePosition(e);
var dX = e.deltaX; let dX = e.deltaX;
var dY = e.deltaY; let dY = e.deltaY;
// Pixel units unless it's non-zero. // Pixel units unless it's non-zero.
// Note that if deltamode is line or page won't matter since we aren't // Note that if deltamode is line or page won't matter since we aren't
@ -192,15 +191,15 @@ Mouse.prototype = {
} }
stopEvent(e); stopEvent(e);
}, }
_handleMouseMove: function (e) { _handleMouseMove(e) {
this._updateMousePosition(e); this._updateMousePosition(e);
this.onmousemove(this._pos.x, this._pos.y); this.onmousemove(this._pos.x, this._pos.y);
stopEvent(e); stopEvent(e);
}, }
_handleMouseDisable: function (e) { _handleMouseDisable(e) {
/* /*
* Stop propagation if inside canvas area * Stop propagation if inside canvas area
* Note: This is only needed for the 'click' event as it fails * Note: This is only needed for the 'click' event as it fails
@ -210,13 +209,14 @@ Mouse.prototype = {
if (e.target == this._target) { if (e.target == this._target) {
stopEvent(e); stopEvent(e);
} }
}, }
// Update coordinates relative to target // Update coordinates relative to target
_updateMousePosition: function(e) { _updateMousePosition(e) {
e = getPointerEvent(e); e = getPointerEvent(e);
var bounds = this._target.getBoundingClientRect(); const bounds = this._target.getBoundingClientRect();
var x, y; let x;
let y;
// Clip to target bounds // Clip to target bounds
if (e.clientX < bounds.left) { if (e.clientX < bounds.left) {
x = 0; x = 0;
@ -232,49 +232,45 @@ Mouse.prototype = {
} else { } else {
y = e.clientY - bounds.top; y = e.clientY - bounds.top;
} }
this._pos = {x:x, y:y}; this._pos = {x: x, y: y};
}, }
// ===== PUBLIC METHODS ===== // ===== PUBLIC METHODS =====
grab: function () { grab() {
var c = this._target;
if (isTouchDevice) { if (isTouchDevice) {
c.addEventListener('touchstart', this._eventHandlers.mousedown); this._target.addEventListener('touchstart', this._eventHandlers.mousedown);
c.addEventListener('touchend', this._eventHandlers.mouseup); this._target.addEventListener('touchend', this._eventHandlers.mouseup);
c.addEventListener('touchmove', this._eventHandlers.mousemove); this._target.addEventListener('touchmove', this._eventHandlers.mousemove);
} }
c.addEventListener('mousedown', this._eventHandlers.mousedown); this._target.addEventListener('mousedown', this._eventHandlers.mousedown);
c.addEventListener('mouseup', this._eventHandlers.mouseup); this._target.addEventListener('mouseup', this._eventHandlers.mouseup);
c.addEventListener('mousemove', this._eventHandlers.mousemove); this._target.addEventListener('mousemove', this._eventHandlers.mousemove);
c.addEventListener('wheel', this._eventHandlers.mousewheel); this._target.addEventListener('wheel', this._eventHandlers.mousewheel);
/* Prevent middle-click pasting (see above for why we bind to document) */ /* Prevent middle-click pasting (see above for why we bind to document) */
document.addEventListener('click', this._eventHandlers.mousedisable); document.addEventListener('click', this._eventHandlers.mousedisable);
/* preventDefault() on mousedown doesn't stop this event for some /* preventDefault() on mousedown doesn't stop this event for some
reason so we have to explicitly block it */ reason so we have to explicitly block it */
c.addEventListener('contextmenu', this._eventHandlers.mousedisable); this._target.addEventListener('contextmenu', this._eventHandlers.mousedisable);
}, }
ungrab: function () {
var c = this._target;
ungrab() {
this._resetWheelStepTimers(); this._resetWheelStepTimers();
if (isTouchDevice) { if (isTouchDevice) {
c.removeEventListener('touchstart', this._eventHandlers.mousedown); this._target.removeEventListener('touchstart', this._eventHandlers.mousedown);
c.removeEventListener('touchend', this._eventHandlers.mouseup); this._target.removeEventListener('touchend', this._eventHandlers.mouseup);
c.removeEventListener('touchmove', this._eventHandlers.mousemove); this._target.removeEventListener('touchmove', this._eventHandlers.mousemove);
} }
c.removeEventListener('mousedown', this._eventHandlers.mousedown); this._target.removeEventListener('mousedown', this._eventHandlers.mousedown);
c.removeEventListener('mouseup', this._eventHandlers.mouseup); this._target.removeEventListener('mouseup', this._eventHandlers.mouseup);
c.removeEventListener('mousemove', this._eventHandlers.mousemove); this._target.removeEventListener('mousemove', this._eventHandlers.mousemove);
c.removeEventListener('wheel', this._eventHandlers.mousewheel); this._target.removeEventListener('wheel', this._eventHandlers.mousewheel);
document.removeEventListener('click', this._eventHandlers.mousedisable); document.removeEventListener('click', this._eventHandlers.mousedisable);
c.removeEventListener('contextmenu', this._eventHandlers.mousedisable); this._target.removeEventListener('contextmenu', this._eventHandlers.mousedisable);
} }
}; }

View file

@ -1,4 +1,3 @@
import KeyTable from "./keysym.js";
import keysyms from "./keysymdef.js"; import keysyms from "./keysymdef.js";
import vkeys from "./vkeys.js"; import vkeys from "./vkeys.js";
import fixedkeys from "./fixedkeys.js"; import fixedkeys from "./fixedkeys.js";
@ -6,7 +5,7 @@ import DOMKeyTable from "./domkeytable.js";
import * as browser from "../util/browser.js"; import * as browser from "../util/browser.js";
// Get 'KeyboardEvent.code', handling legacy browsers // Get 'KeyboardEvent.code', handling legacy browsers
export function getKeycode(evt){ export function getKeycode(evt) {
// Are we getting proper key identifiers? // Are we getting proper key identifiers?
// (unfortunately Firefox and Chrome are crappy here and gives // (unfortunately Firefox and Chrome are crappy here and gives
// us an empty string on some platforms, rather than leaving it // us an empty string on some platforms, rather than leaving it
@ -25,7 +24,7 @@ export function getKeycode(evt){
// in the 'keyCode' field for non-printable characters. However // in the 'keyCode' field for non-printable characters. However
// Webkit sets it to the same as charCode in 'keypress' events. // Webkit sets it to the same as charCode in 'keypress' events.
if ((evt.type !== 'keypress') && (evt.keyCode in vkeys)) { if ((evt.type !== 'keypress') && (evt.keyCode in vkeys)) {
var code = vkeys[evt.keyCode]; let code = vkeys[evt.keyCode];
// macOS has messed up this code for some reason // macOS has messed up this code for some reason
if (browser.isMac() && (code === 'ContextMenu')) { if (browser.isMac() && (code === 'ContextMenu')) {
@ -111,7 +110,7 @@ export function getKey(evt) {
} }
// Try to deduce it based on the physical key // Try to deduce it based on the physical key
var code = getKeycode(evt); const code = getKeycode(evt);
if (code in fixedkeys) { if (code in fixedkeys) {
return fixedkeys[code]; return fixedkeys[code];
} }
@ -126,8 +125,8 @@ export function getKey(evt) {
} }
// Get the most reliable keysym value we can get from a key event // Get the most reliable keysym value we can get from a key event
export function getKeysym(evt){ export function getKeysym(evt) {
var key = getKey(evt); const key = getKey(evt);
if (key === 'Unidentified') { if (key === 'Unidentified') {
return null; return null;
@ -135,7 +134,7 @@ export function getKeysym(evt){
// First look up special keys // First look up special keys
if (key in DOMKeyTable) { if (key in DOMKeyTable) {
var location = evt.location; let location = evt.location;
// Safari screws up location for the right cmd key // Safari screws up location for the right cmd key
if ((key === 'Meta') && (location === 0)) { if ((key === 'Meta') && (location === 0)) {
@ -151,14 +150,12 @@ export function getKeysym(evt){
// Now we need to look at the Unicode symbol instead // Now we need to look at the Unicode symbol instead
var codepoint;
// Special key? (FIXME: Should have been caught earlier) // Special key? (FIXME: Should have been caught earlier)
if (key.length !== 1) { if (key.length !== 1) {
return null; return null;
} }
codepoint = key.charCodeAt(); const codepoint = key.charCodeAt();
if (codepoint) { if (codepoint) {
return keysyms.lookup(codepoint); return keysyms.lookup(codepoint);
} }

View file

@ -1,6 +1,6 @@
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright (C) 2017 Pierre Ossman for Cendio AB * Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 or any later version (see LICENSE.txt) * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/ */
@ -13,6 +13,7 @@ export default {
0x08: 'Backspace', 0x08: 'Backspace',
0x09: 'Tab', 0x09: 'Tab',
0x0a: 'NumpadClear', 0x0a: 'NumpadClear',
0x0c: 'Numpad5', // IE11 sends evt.keyCode: 12 when numlock is off
0x0d: 'Enter', 0x0d: 'Enter',
0x10: 'ShiftLeft', 0x10: 'ShiftLeft',
0x11: 'ControlLeft', 0x11: 'ControlLeft',

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin * Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt) * Licensed under MPL 2.0 (see LICENSE.txt)
* *
* See README.md for usage and integration instructions. * See README.md for usage and integration instructions.
@ -9,7 +9,7 @@
import * as Log from './logging.js'; import * as Log from './logging.js';
// Touch detection // Touch detection
export var isTouchDevice = ('ontouchstart' in document.documentElement) || export let isTouchDevice = ('ontouchstart' in document.documentElement) ||
// requried for Chrome debugger // requried for Chrome debugger
(document.ontouchstart !== undefined) || (document.ontouchstart !== undefined) ||
// required for MS Surface // required for MS Surface
@ -20,42 +20,42 @@ window.addEventListener('touchstart', function onFirstTouch() {
window.removeEventListener('touchstart', onFirstTouch, false); window.removeEventListener('touchstart', onFirstTouch, false);
}, false); }, false);
var _cursor_uris_supported = null;
export function supportsCursorURIs () { // The goal is to find a certain physical width, the devicePixelRatio
if (_cursor_uris_supported === null) { // brings us a bit closer but is not optimal.
try { export let dragThreshold = 10 * (window.devicePixelRatio || 1);
var target = document.createElement('canvas');
target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
if (target.style.cursor) { let _supportsCursorURIs = false;
Log.Info("Data URI scheme cursor supported");
_cursor_uris_supported = true; try {
} else { const target = document.createElement('canvas');
Log.Warn("Data URI scheme cursor not supported"); target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
_cursor_uris_supported = false;
} if (target.style.cursor) {
} catch (exc) { Log.Info("Data URI scheme cursor supported");
Log.Error("Data URI scheme cursor test exception: " + exc); _supportsCursorURIs = true;
_cursor_uris_supported = false; } else {
} Log.Warn("Data URI scheme cursor not supported");
} }
} catch (exc) {
Log.Error("Data URI scheme cursor test exception: " + exc);
}
return _cursor_uris_supported; export const supportsCursorURIs = _supportsCursorURIs;
};
let _supportsImageMetadata = false;
try {
new ImageData(new Uint8ClampedArray(4), 1, 1);
_supportsImageMetadata = true;
} catch (ex) {
// ignore failure
}
export const supportsImageMetadata = _supportsImageMetadata;
export function isMac() { export function isMac() {
return navigator && !!(/mac/i).exec(navigator.platform); return navigator && !!(/mac/i).exec(navigator.platform);
} }
export function isIE() {
return navigator && !!(/trident/i).exec(navigator.userAgent);
}
export function isEdge() {
return navigator && !!(/edge/i).exec(navigator.userAgent);
}
export function isWindows() { export function isWindows() {
return navigator && !!(/win/i).exec(navigator.platform); return navigator && !!(/win/i).exec(navigator.platform);
} }
@ -67,3 +67,24 @@ export function isIOS() {
!!(/ipod/i).exec(navigator.platform)); !!(/ipod/i).exec(navigator.platform));
} }
export function isAndroid() {
return navigator && !!(/android/i).exec(navigator.userAgent);
}
export function isSafari() {
return navigator && (navigator.userAgent.indexOf('Safari') !== -1 &&
navigator.userAgent.indexOf('Chrome') === -1);
}
export function isIE() {
return navigator && !!(/trident/i).exec(navigator.userAgent);
}
export function isEdge() {
return navigator && !!(/edge/i).exec(navigator.userAgent);
}
export function isFirefox() {
return navigator && !!(/firefox/i).exec(navigator.userAgent);
}

View file

@ -0,0 +1,221 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/
import { supportsCursorURIs, isTouchDevice } from './browser.js';
const useFallback = !supportsCursorURIs || isTouchDevice;
export default class Cursor {
constructor() {
this._target = null;
this._canvas = document.createElement('canvas');
if (useFallback) {
this._canvas.style.position = 'fixed';
this._canvas.style.zIndex = '65535';
this._canvas.style.pointerEvents = 'none';
// Can't use "display" because of Firefox bug #1445997
this._canvas.style.visibility = 'hidden';
document.body.appendChild(this._canvas);
}
this._position = { x: 0, y: 0 };
this._hotSpot = { x: 0, y: 0 };
this._eventHandlers = {
'mouseover': this._handleMouseOver.bind(this),
'mouseleave': this._handleMouseLeave.bind(this),
'mousemove': this._handleMouseMove.bind(this),
'mouseup': this._handleMouseUp.bind(this),
'touchstart': this._handleTouchStart.bind(this),
'touchmove': this._handleTouchMove.bind(this),
'touchend': this._handleTouchEnd.bind(this),
};
}
attach(target) {
if (this._target) {
this.detach();
}
this._target = target;
if (useFallback) {
// FIXME: These don't fire properly except for mouse
/// movement in IE. We want to also capture element
// movement, size changes, visibility, etc.
const options = { capture: true, passive: true };
this._target.addEventListener('mouseover', this._eventHandlers.mouseover, options);
this._target.addEventListener('mouseleave', this._eventHandlers.mouseleave, options);
this._target.addEventListener('mousemove', this._eventHandlers.mousemove, options);
this._target.addEventListener('mouseup', this._eventHandlers.mouseup, options);
// There is no "touchleave" so we monitor touchstart globally
window.addEventListener('touchstart', this._eventHandlers.touchstart, options);
this._target.addEventListener('touchmove', this._eventHandlers.touchmove, options);
this._target.addEventListener('touchend', this._eventHandlers.touchend, options);
}
this.clear();
}
detach() {
if (useFallback) {
const options = { capture: true, passive: true };
this._target.removeEventListener('mouseover', this._eventHandlers.mouseover, options);
this._target.removeEventListener('mouseleave', this._eventHandlers.mouseleave, options);
this._target.removeEventListener('mousemove', this._eventHandlers.mousemove, options);
this._target.removeEventListener('mouseup', this._eventHandlers.mouseup, options);
window.removeEventListener('touchstart', this._eventHandlers.touchstart, options);
this._target.removeEventListener('touchmove', this._eventHandlers.touchmove, options);
this._target.removeEventListener('touchend', this._eventHandlers.touchend, options);
}
this._target = null;
}
change(rgba, hotx, hoty, w, h) {
if ((w === 0) || (h === 0)) {
this.clear();
return;
}
this._position.x = this._position.x + this._hotSpot.x - hotx;
this._position.y = this._position.y + this._hotSpot.y - hoty;
this._hotSpot.x = hotx;
this._hotSpot.y = hoty;
let ctx = this._canvas.getContext('2d');
this._canvas.width = w;
this._canvas.height = h;
let img;
try {
// IE doesn't support this
img = new ImageData(new Uint8ClampedArray(rgba), w, h);
} catch (ex) {
img = ctx.createImageData(w, h);
img.data.set(new Uint8ClampedArray(rgba));
}
ctx.clearRect(0, 0, w, h);
ctx.putImageData(img, 0, 0);
if (useFallback) {
this._updatePosition();
} else {
let url = this._canvas.toDataURL();
this._target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
}
}
clear() {
this._target.style.cursor = 'none';
this._canvas.width = 0;
this._canvas.height = 0;
this._position.x = this._position.x + this._hotSpot.x;
this._position.y = this._position.y + this._hotSpot.y;
this._hotSpot.x = 0;
this._hotSpot.y = 0;
}
_handleMouseOver(event) {
// This event could be because we're entering the target, or
// moving around amongst its sub elements. Let the move handler
// sort things out.
this._handleMouseMove(event);
}
_handleMouseLeave(event) {
this._hideCursor();
}
_handleMouseMove(event) {
this._updateVisibility(event.target);
this._position.x = event.clientX - this._hotSpot.x;
this._position.y = event.clientY - this._hotSpot.y;
this._updatePosition();
}
_handleMouseUp(event) {
// We might get this event because of a drag operation that
// moved outside of the target. Check what's under the cursor
// now and adjust visibility based on that.
let target = document.elementFromPoint(event.clientX, event.clientY);
this._updateVisibility(target);
}
_handleTouchStart(event) {
// Just as for mouseover, we let the move handler deal with it
this._handleTouchMove(event);
}
_handleTouchMove(event) {
this._updateVisibility(event.target);
this._position.x = event.changedTouches[0].clientX - this._hotSpot.x;
this._position.y = event.changedTouches[0].clientY - this._hotSpot.y;
this._updatePosition();
}
_handleTouchEnd(event) {
// Same principle as for mouseup
let target = document.elementFromPoint(event.changedTouches[0].clientX,
event.changedTouches[0].clientY);
this._updateVisibility(target);
}
_showCursor() {
if (this._canvas.style.visibility === 'hidden') {
this._canvas.style.visibility = '';
}
}
_hideCursor() {
if (this._canvas.style.visibility !== 'hidden') {
this._canvas.style.visibility = 'hidden';
}
}
// Should we currently display the cursor?
// (i.e. are we over the target, or a child of the target without a
// different cursor set)
_shouldShowCursor(target) {
// Easy case
if (target === this._target) {
return true;
}
// Other part of the DOM?
if (!this._target.contains(target)) {
return false;
}
// Has the child its own cursor?
// FIXME: How can we tell that a sub element has an
// explicit "cursor: none;"?
if (window.getComputedStyle(target).cursor !== 'none') {
return false;
}
return true;
}
_updateVisibility(target) {
if (this._shouldShowCursor(target)) {
this._showCursor();
} else {
this._hideCursor();
}
}
_updatePosition() {
this._canvas.style.left = this._position.x + "px";
this._canvas.style.top = this._position.y + "px";
}
}

View file

@ -1,6 +1,6 @@
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin * Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt) * Licensed under MPL 2.0 (see LICENSE.txt)
* *
* See README.md for usage and integration instructions. * See README.md for usage and integration instructions.
@ -10,24 +10,24 @@
* Cross-browser event and position routines * Cross-browser event and position routines
*/ */
export function getPointerEvent (e) { export function getPointerEvent(e) {
return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e; return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e;
}; }
export function stopEvent (e) { export function stopEvent(e) {
e.stopPropagation(); e.stopPropagation();
e.preventDefault(); e.preventDefault();
}; }
// Emulate Element.setCapture() when not supported // Emulate Element.setCapture() when not supported
var _captureRecursion = false; let _captureRecursion = false;
var _captureElem = null; let _captureElem = null;
function _captureProxy(e) { function _captureProxy(e) {
// Recursion protection as we'll see our own event // Recursion protection as we'll see our own event
if (_captureRecursion) return; if (_captureRecursion) return;
// Clone the event as we cannot dispatch an already dispatched event // Clone the event as we cannot dispatch an already dispatched event
var newEv = new e.constructor(e.type, e); const newEv = new e.constructor(e.type, e);
_captureRecursion = true; _captureRecursion = true;
_captureElem.dispatchEvent(newEv); _captureElem.dispatchEvent(newEv);
@ -45,18 +45,19 @@ function _captureProxy(e) {
if (e.type === "mouseup") { if (e.type === "mouseup") {
releaseCapture(); releaseCapture();
} }
}; }
// Follow cursor style of target element // Follow cursor style of target element
function _captureElemChanged() { function _captureElemChanged() {
var captureElem = document.getElementById("noVNC_mouse_capture_elem"); const captureElem = document.getElementById("noVNC_mouse_capture_elem");
captureElem.style.cursor = window.getComputedStyle(_captureElem).cursor; captureElem.style.cursor = window.getComputedStyle(_captureElem).cursor;
}; }
var _captureObserver = new MutationObserver(_captureElemChanged);
var _captureIndex = 0; const _captureObserver = new MutationObserver(_captureElemChanged);
export function setCapture (elem) { let _captureIndex = 0;
export function setCapture(elem) {
if (elem.setCapture) { if (elem.setCapture) {
elem.setCapture(); elem.setCapture();
@ -69,7 +70,7 @@ export function setCapture (elem) {
// called multiple times without coordination // called multiple times without coordination
releaseCapture(); releaseCapture();
var captureElem = document.getElementById("noVNC_mouse_capture_elem"); let captureElem = document.getElementById("noVNC_mouse_capture_elem");
if (captureElem === null) { if (captureElem === null) {
captureElem = document.createElement("div"); captureElem = document.createElement("div");
@ -95,7 +96,7 @@ export function setCapture (elem) {
_captureIndex++; _captureIndex++;
// Track cursor and get initial cursor // Track cursor and get initial cursor
_captureObserver.observe(elem, {attributes:true}); _captureObserver.observe(elem, {attributes: true});
_captureElemChanged(); _captureElemChanged();
captureElem.style.display = ""; captureElem.style.display = "";
@ -105,9 +106,9 @@ export function setCapture (elem) {
window.addEventListener('mousemove', _captureProxy); window.addEventListener('mousemove', _captureProxy);
window.addEventListener('mouseup', _captureProxy); window.addEventListener('mouseup', _captureProxy);
} }
}; }
export function releaseCapture () { export function releaseCapture() {
if (document.releaseCapture) { if (document.releaseCapture) {
document.releaseCapture(); document.releaseCapture();
@ -119,7 +120,7 @@ export function releaseCapture () {
// There might be events already queued, so we need to wait for // There might be events already queued, so we need to wait for
// them to flush. E.g. contextmenu in Microsoft Edge // them to flush. E.g. contextmenu in Microsoft Edge
window.setTimeout(function(expected) { window.setTimeout((expected) => {
// Only clear it if it's the expected grab (i.e. no one // Only clear it if it's the expected grab (i.e. no one
// else has initiated a new grab) // else has initiated a new grab)
if (_captureIndex === expected) { if (_captureIndex === expected) {
@ -129,10 +130,10 @@ export function releaseCapture () {
_captureObserver.disconnect(); _captureObserver.disconnect();
var captureElem = document.getElementById("noVNC_mouse_capture_elem"); const captureElem = document.getElementById("noVNC_mouse_capture_elem");
captureElem.style.display = "none"; captureElem.style.display = "none";
window.removeEventListener('mousemove', _captureProxy); window.removeEventListener('mousemove', _captureProxy);
window.removeEventListener('mouseup', _captureProxy); window.removeEventListener('mouseup', _captureProxy);
} }
}; }

View file

@ -1,40 +1,35 @@
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright 2017 Pierre Ossman for Cendio AB * Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt) * Licensed under MPL 2.0 (see LICENSE.txt)
* *
* See README.md for usage and integration instructions. * See README.md for usage and integration instructions.
*/ */
var EventTargetMixin = { export default class EventTargetMixin {
_listeners: null, constructor() {
this._listeners = new Map();
}
addEventListener: function(type, callback) { addEventListener(type, callback) {
if (!this._listeners) { if (!this._listeners.has(type)) {
this._listeners = new Map(); this._listeners.set(type, new Set());
} }
if (!this._listeners.has(type)) { this._listeners.get(type).add(callback);
this._listeners.set(type, new Set()); }
}
this._listeners.get(type).add(callback);
},
removeEventListener: function(type, callback) { removeEventListener(type, callback) {
if (!this._listeners || !this._listeners.has(type)) { if (this._listeners.has(type)) {
return; this._listeners.get(type).delete(callback);
} }
this._listeners.get(type).delete(callback); }
},
dispatchEvent: function(event) { dispatchEvent(event) {
if (!this._listeners || !this._listeners.has(event.type)) { if (!this._listeners.has(event.type)) {
return true; return true;
} }
this._listeners.get(event.type).forEach(function (callback) { this._listeners.get(event.type)
callback.call(this, event); .forEach(callback => callback.call(this, event));
}, this); return !event.defaultPrevented;
return !event.defaultPrevented; }
}, }
};
export default EventTargetMixin;

View file

@ -1,6 +1,6 @@
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin * Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt) * Licensed under MPL 2.0 (see LICENSE.txt)
* *
* See README.md for usage and integration instructions. * See README.md for usage and integration instructions.
@ -10,22 +10,24 @@
* Logging/debug routines * Logging/debug routines
*/ */
var _log_level = 'warn'; let _log_level = 'warn';
var Debug = function (msg) {}; let Debug = () => {};
var Info = function (msg) {}; let Info = () => {};
var Warn = function (msg) {}; let Warn = () => {};
var Error = function (msg) {}; let Error = () => {};
export function init_logging (level) { export function init_logging(level) {
if (typeof level === 'undefined') { if (typeof level === 'undefined') {
level = _log_level; level = _log_level;
} else { } else {
_log_level = level; _log_level = level;
} }
Debug = Info = Warn = Error = function (msg) {}; Debug = Info = Warn = Error = () => {};
if (typeof window.console !== "undefined") { if (typeof window.console !== "undefined") {
/* eslint-disable no-console, no-fallthrough */
switch (level) { switch (level) {
case 'debug': case 'debug':
Debug = console.debug.bind(window.console); Debug = console.debug.bind(window.console);
@ -38,13 +40,16 @@ export function init_logging (level) {
case 'none': case 'none':
break; break;
default: default:
throw new Error("invalid logging type '" + level + "'"); throw new window.Error("invalid logging type '" + level + "'");
} }
/* eslint-enable no-console, no-fallthrough */
} }
}; }
export function get_logging () {
export function get_logging() {
return _log_level; return _log_level;
}; }
export { Debug, Info, Warn, Error }; export { Debug, Info, Warn, Error };
// Initialize logging level // Initialize logging level

View file

@ -1,6 +1,6 @@
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright 2017 Pierre Ossman for noVNC * Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 or any later version (see LICENSE.txt) * Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/ */
@ -16,13 +16,13 @@ if (typeof Object.assign != 'function') {
throw new TypeError('Cannot convert undefined or null to object'); throw new TypeError('Cannot convert undefined or null to object');
} }
var to = Object(target); const to = Object(target);
for (var index = 1; index < arguments.length; index++) { for (let index = 1; index < arguments.length; index++) {
var nextSource = arguments[index]; const nextSource = arguments[index];
if (nextSource != null) { // Skip over if undefined or null if (nextSource != null) { // Skip over if undefined or null
for (var nextKey in nextSource) { for (let nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed // Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey]; to[nextKey] = nextSource[nextKey];
@ -38,10 +38,10 @@ if (typeof Object.assign != 'function') {
} }
/* CustomEvent constructor (taken from MDN) */ /* CustomEvent constructor (taken from MDN) */
(function () { (() => {
function CustomEvent ( event, params ) { function CustomEvent(event, params) {
params = params || { bubbles: false, cancelable: false, detail: undefined }; params = params || { bubbles: false, cancelable: false, detail: undefined };
var evt = document.createEvent( 'CustomEvent' ); const evt = document.createEvent( 'CustomEvent' );
evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail ); evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
return evt; return evt;
} }

View file

@ -1,6 +1,6 @@
/* /*
* noVNC: HTML5 VNC client * noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin * Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt) * Licensed under MPL 2.0 (see LICENSE.txt)
* *
* See README.md for usage and integration instructions. * See README.md for usage and integration instructions.
@ -9,7 +9,6 @@
/* /*
* Decode from UTF-8 * Decode from UTF-8
*/ */
export function decodeUTF8 (utf8string) { export function decodeUTF8(utf8string) {
"use strict";
return decodeURIComponent(escape(utf8string)); return decodeURIComponent(escape(utf8string));
}; }

View file

@ -1,6 +1,6 @@
/* /*
* Websock: high-performance binary WebSockets * Websock: high-performance binary WebSockets
* Copyright (C) 2012 Joel Martin * Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt) * Licensed under MPL 2.0 (see LICENSE.txt)
* *
* Websock is similar to the standard WebSocket object but with extra * Websock is similar to the standard WebSocket object but with extra
@ -14,143 +14,120 @@
import * as Log from './util/logging.js'; import * as Log from './util/logging.js';
export default function Websock() {
"use strict";
this._websocket = null; // WebSocket object
this._rQi = 0; // Receive queue index
this._rQlen = 0; // Next write position in the receive queue
this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
this._rQmax = this._rQbufferSize / 8;
// called in init: this._rQ = new Uint8Array(this._rQbufferSize);
this._rQ = null; // Receive queue
this._sQbufferSize = 1024 * 10; // 10 KiB
// called in init: this._sQ = new Uint8Array(this._sQbufferSize);
this._sQlen = 0;
this._sQ = null; // Send queue
this._eventHandlers = {
'message': function () {},
'open': function () {},
'close': function () {},
'error': function () {}
};
};
// this has performance issues in some versions Chromium, and // this has performance issues in some versions Chromium, and
// doesn't gain a tremendous amount of performance increase in Firefox // doesn't gain a tremendous amount of performance increase in Firefox
// at the moment. It may be valuable to turn it on in the future. // at the moment. It may be valuable to turn it on in the future.
var ENABLE_COPYWITHIN = false; const ENABLE_COPYWITHIN = false;
const MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
var MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB export default class Websock {
constructor() {
this._websocket = null; // WebSocket object
var typedArrayToString = (function () { this._rQi = 0; // Receive queue index
// This is only for PhantomJS, which doesn't like apply-ing this._rQlen = 0; // Next write position in the receive queue
// with Typed Arrays this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
try { this._rQmax = this._rQbufferSize / 8;
var arr = new Uint8Array([1, 2, 3]); // called in init: this._rQ = new Uint8Array(this._rQbufferSize);
String.fromCharCode.apply(null, arr); this._rQ = null; // Receive queue
return function (a) { return String.fromCharCode.apply(null, a); };
} catch (ex) { this._sQbufferSize = 1024 * 10; // 10 KiB
return function (a) { // called in init: this._sQ = new Uint8Array(this._sQbufferSize);
return String.fromCharCode.apply( this._sQlen = 0;
null, Array.prototype.slice.call(a)); this._sQ = null; // Send queue
this._eventHandlers = {
message: () => {},
open: () => {},
close: () => {},
error: () => {}
}; };
} }
})();
Websock.prototype = {
// Getters and Setters // Getters and Setters
get_sQ: function () { get sQ() {
return this._sQ; return this._sQ;
}, }
get_rQ: function () { get rQ() {
return this._rQ; return this._rQ;
}, }
get_rQi: function () { get rQi() {
return this._rQi; return this._rQi;
}, }
set_rQi: function (val) { set rQi(val) {
this._rQi = val; this._rQi = val;
}, }
// Receive Queue // Receive Queue
rQlen: function () { get rQlen() {
return this._rQlen - this._rQi; return this._rQlen - this._rQi;
}, }
rQpeek8: function () { rQpeek8() {
return this._rQ[this._rQi]; return this._rQ[this._rQi];
}, }
rQshift8: function () { rQskipBytes(bytes) {
return this._rQ[this._rQi++]; this._rQi += bytes;
}, }
rQskip8: function () { rQshift8() {
this._rQi++; return this._rQshift(1);
}, }
rQskipBytes: function (num) { rQshift16() {
this._rQi += num; return this._rQshift(2);
}, }
rQshift32() {
return this._rQshift(4);
}
// TODO(directxman12): test performance with these vs a DataView // TODO(directxman12): test performance with these vs a DataView
rQshift16: function () { _rQshift(bytes) {
return (this._rQ[this._rQi++] << 8) + let res = 0;
this._rQ[this._rQi++]; for (let byte = bytes - 1; byte >= 0; byte--) {
}, res += this._rQ[this._rQi++] << (byte * 8);
}
return res;
}
rQshift32: function () { rQshiftStr(len) {
return (this._rQ[this._rQi++] << 24) + if (typeof(len) === 'undefined') { len = this.rQlen; }
(this._rQ[this._rQi++] << 16) + let str = "";
(this._rQ[this._rQi++] << 8) + // Handle large arrays in steps to avoid long strings on the stack
this._rQ[this._rQi++]; for (let i = 0; i < len; i += 4096) {
}, let part = this.rQshiftBytes(Math.min(4096, len - i));
str += String.fromCharCode.apply(null, part);
}
return str;
}
rQshiftStr: function (len) { rQshiftBytes(len) {
if (typeof(len) === 'undefined') { len = this.rQlen(); } if (typeof(len) === 'undefined') { len = this.rQlen; }
var arr = new Uint8Array(this._rQ.buffer, this._rQi, len);
this._rQi += len;
return typedArrayToString(arr);
},
rQshiftBytes: function (len) {
if (typeof(len) === 'undefined') { len = this.rQlen(); }
this._rQi += len; this._rQi += len;
return new Uint8Array(this._rQ.buffer, this._rQi - len, len); return new Uint8Array(this._rQ.buffer, this._rQi - len, len);
}, }
rQshiftTo: function (target, len) { rQshiftTo(target, len) {
if (len === undefined) { len = this.rQlen(); } if (len === undefined) { len = this.rQlen; }
// TODO: make this just use set with views when using a ArrayBuffer to store the rQ // TODO: make this just use set with views when using a ArrayBuffer to store the rQ
target.set(new Uint8Array(this._rQ.buffer, this._rQi, len)); target.set(new Uint8Array(this._rQ.buffer, this._rQi, len));
this._rQi += len; this._rQi += len;
}, }
rQwhole: function () { rQslice(start, end = this.rQlen) {
return new Uint8Array(this._rQ.buffer, 0, this._rQlen); return new Uint8Array(this._rQ.buffer, this._rQi + start, end - start);
}, }
rQslice: function (start, end) {
if (end) {
return new Uint8Array(this._rQ.buffer, this._rQi + start, end - start);
} else {
return new Uint8Array(this._rQ.buffer, this._rQi + start, this._rQlen - this._rQi - start);
}
},
// Check to see if we must wait for 'num' bytes (default to FBU.bytes) // Check to see if we must wait for 'num' bytes (default to FBU.bytes)
// to be available in the receive queue. Return true if we need to // to be available in the receive queue. Return true if we need to
// wait (and possibly print a debug message), otherwise false. // wait (and possibly print a debug message), otherwise false.
rQwait: function (msg, num, goback) { rQwait(msg, num, goback) {
var rQlen = this._rQlen - this._rQi; // Skip rQlen() function call if (this.rQlen < num) {
if (rQlen < num) {
if (goback) { if (goback) {
if (this._rQi < goback) { if (this._rQi < goback) {
throw new Error("rQwait cannot backup " + goback + " bytes"); throw new Error("rQwait cannot backup " + goback + " bytes");
@ -160,58 +137,55 @@ Websock.prototype = {
return true; // true means need more data return true; // true means need more data
} }
return false; return false;
}, }
// Send Queue // Send Queue
flush: function () { flush() {
if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) { if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) {
this._websocket.send(this._encode_message()); this._websocket.send(this._encode_message());
this._sQlen = 0; this._sQlen = 0;
} }
}, }
send: function (arr) { send(arr) {
this._sQ.set(arr, this._sQlen); this._sQ.set(arr, this._sQlen);
this._sQlen += arr.length; this._sQlen += arr.length;
this.flush(); this.flush();
}, }
send_string: function (str) { send_string(str) {
this.send(str.split('').map(function (chr) { this.send(str.split('').map(chr => chr.charCodeAt(0)));
return chr.charCodeAt(0); }
}));
},
// Event Handlers // Event Handlers
off: function (evt) { off(evt) {
this._eventHandlers[evt] = function () {}; this._eventHandlers[evt] = () => {};
}, }
on: function (evt, handler) { on(evt, handler) {
this._eventHandlers[evt] = handler; this._eventHandlers[evt] = handler;
}, }
_allocate_buffers: function () { _allocate_buffers() {
this._rQ = new Uint8Array(this._rQbufferSize); this._rQ = new Uint8Array(this._rQbufferSize);
this._sQ = new Uint8Array(this._sQbufferSize); this._sQ = new Uint8Array(this._sQbufferSize);
}, }
init: function () { init() {
this._allocate_buffers(); this._allocate_buffers();
this._rQi = 0; this._rQi = 0;
this._websocket = null; this._websocket = null;
}, }
open: function (uri, protocols) { open(uri, protocols) {
var ws_schema = uri.match(/^([a-z]+):\/\//)[1];
this.init(); this.init();
this._websocket = new WebSocket(uri, protocols); this._websocket = new WebSocket(uri, protocols);
this._websocket.binaryType = 'arraybuffer'; this._websocket.binaryType = 'arraybuffer';
this._websocket.onmessage = this._recv_message.bind(this); this._websocket.onmessage = this._recv_message.bind(this);
this._websocket.onopen = (function () { this._websocket.onopen = () => {
Log.Debug('>> WebSock.onopen'); Log.Debug('>> WebSock.onopen');
if (this._websocket.protocol) { if (this._websocket.protocol) {
Log.Info("Server choose sub-protocol: " + this._websocket.protocol); Log.Info("Server choose sub-protocol: " + this._websocket.protocol);
@ -219,20 +193,20 @@ Websock.prototype = {
this._eventHandlers.open(); this._eventHandlers.open();
Log.Debug("<< WebSock.onopen"); Log.Debug("<< WebSock.onopen");
}).bind(this); };
this._websocket.onclose = (function (e) { this._websocket.onclose = (e) => {
Log.Debug(">> WebSock.onclose"); Log.Debug(">> WebSock.onclose");
this._eventHandlers.close(e); this._eventHandlers.close(e);
Log.Debug("<< WebSock.onclose"); Log.Debug("<< WebSock.onclose");
}).bind(this); };
this._websocket.onerror = (function (e) { this._websocket.onerror = (e) => {
Log.Debug(">> WebSock.onerror: " + e); Log.Debug(">> WebSock.onerror: " + e);
this._eventHandlers.error(e); this._eventHandlers.error(e);
Log.Debug("<< WebSock.onerror: " + e); Log.Debug("<< WebSock.onerror: " + e);
}).bind(this); };
}, }
close: function () { close() {
if (this._websocket) { if (this._websocket) {
if ((this._websocket.readyState === WebSocket.OPEN) || if ((this._websocket.readyState === WebSocket.OPEN) ||
(this._websocket.readyState === WebSocket.CONNECTING)) { (this._websocket.readyState === WebSocket.CONNECTING)) {
@ -240,39 +214,39 @@ Websock.prototype = {
this._websocket.close(); this._websocket.close();
} }
this._websocket.onmessage = function (e) { return; }; this._websocket.onmessage = () => {};
} }
}, }
// private methods // private methods
_encode_message: function () { _encode_message() {
// Put in a binary arraybuffer // Put in a binary arraybuffer
// according to the spec, you can send ArrayBufferViews with the send method // according to the spec, you can send ArrayBufferViews with the send method
return new Uint8Array(this._sQ.buffer, 0, this._sQlen); return new Uint8Array(this._sQ.buffer, 0, this._sQlen);
}, }
_expand_compact_rQ: function (min_fit) { _expand_compact_rQ(min_fit) {
var resizeNeeded = min_fit || this._rQlen - this._rQi > this._rQbufferSize / 2; const resizeNeeded = min_fit || this.rQlen > this._rQbufferSize / 2;
if (resizeNeeded) { if (resizeNeeded) {
if (!min_fit) { if (!min_fit) {
// just double the size if we need to do compaction // just double the size if we need to do compaction
this._rQbufferSize *= 2; this._rQbufferSize *= 2;
} else { } else {
// otherwise, make sure we satisy rQlen - rQi + min_fit < rQbufferSize / 8 // otherwise, make sure we satisy rQlen - rQi + min_fit < rQbufferSize / 8
this._rQbufferSize = (this._rQlen - this._rQi + min_fit) * 8; this._rQbufferSize = (this.rQlen + min_fit) * 8;
} }
} }
// we don't want to grow unboundedly // we don't want to grow unboundedly
if (this._rQbufferSize > MAX_RQ_GROW_SIZE) { if (this._rQbufferSize > MAX_RQ_GROW_SIZE) {
this._rQbufferSize = MAX_RQ_GROW_SIZE; this._rQbufferSize = MAX_RQ_GROW_SIZE;
if (this._rQbufferSize - this._rQlen - this._rQi < min_fit) { if (this._rQbufferSize - this.rQlen < min_fit) {
throw new Exception("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit"); throw new Error("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit");
} }
} }
if (resizeNeeded) { if (resizeNeeded) {
var old_rQbuffer = this._rQ.buffer; const old_rQbuffer = this._rQ.buffer;
this._rQmax = this._rQbufferSize / 8; this._rQmax = this._rQbufferSize / 8;
this._rQ = new Uint8Array(this._rQbufferSize); this._rQ = new Uint8Array(this._rQbufferSize);
this._rQ.set(new Uint8Array(old_rQbuffer, this._rQi)); this._rQ.set(new Uint8Array(old_rQbuffer, this._rQi));
@ -286,21 +260,21 @@ Websock.prototype = {
this._rQlen = this._rQlen - this._rQi; this._rQlen = this._rQlen - this._rQi;
this._rQi = 0; this._rQi = 0;
}, }
_decode_message: function (data) { _decode_message(data) {
// push arraybuffer values onto the end // push arraybuffer values onto the end
var u8 = new Uint8Array(data); const u8 = new Uint8Array(data);
if (u8.length > this._rQbufferSize - this._rQlen) { if (u8.length > this._rQbufferSize - this._rQlen) {
this._expand_compact_rQ(u8.length); this._expand_compact_rQ(u8.length);
} }
this._rQ.set(u8, this._rQlen); this._rQ.set(u8, this._rQlen);
this._rQlen += u8.length; this._rQlen += u8.length;
}, }
_recv_message: function (e) { _recv_message(e) {
this._decode_message(e.data); this._decode_message(e.data);
if (this.rQlen() > 0) { if (this.rQlen > 0) {
this._eventHandlers.message(); this._eventHandlers.message();
// Compact the receive queue // Compact the receive queue
if (this._rQlen == this._rQi) { if (this._rQlen == this._rQi) {
@ -313,4 +287,4 @@ Websock.prototype = {
Log.Debug("Ignoring empty message"); Log.Debug("Ignoring empty message");
} }
} }
}; }

File diff suppressed because one or more lines are too long

View file

@ -20,6 +20,8 @@ function createSymbol (name) {
return hasSymbol ? Symbol() : '@@' + name; return hasSymbol ? Symbol() : '@@' + name;
} }
var toStringTag = hasSymbol && Symbol.toStringTag;
@ -93,7 +95,7 @@ function LoaderError__Check_error_message_for_loader_stack (childErr, newMessage
return err; return err;
} }
var resolvedPromise = Promise.resolve(); var resolvedPromise$1 = Promise.resolve();
/* /*
* Simple Array values shim * Simple Array values shim
@ -140,7 +142,9 @@ function Loader () {
Loader.prototype.constructor = Loader; Loader.prototype.constructor = Loader;
function ensureInstantiated (module) { function ensureInstantiated (module) {
if (!(module instanceof ModuleNamespace)) if (module === undefined)
return;
if (module instanceof ModuleNamespace === false && module[toStringTag] !== 'module')
throw new TypeError('Module instantiation did not return a valid namespace object.'); throw new TypeError('Module instantiation did not return a valid namespace object.');
return module; return module;
} }
@ -151,7 +155,7 @@ Loader.prototype.import = function (key, parent) {
throw new TypeError('Loader import method must be passed a module key string'); throw new TypeError('Loader import method must be passed a module key string');
// custom resolveInstantiate combined hook for better perf // custom resolveInstantiate combined hook for better perf
var loader = this; var loader = this;
return resolvedPromise return resolvedPromise$1
.then(function () { .then(function () {
return loader[RESOLVE_INSTANTIATE](key, parent); return loader[RESOLVE_INSTANTIATE](key, parent);
}) })
@ -193,7 +197,7 @@ function ensureResolution (resolvedKey) {
Loader.prototype.resolve = function (key, parent) { Loader.prototype.resolve = function (key, parent) {
var loader = this; var loader = this;
return resolvedPromise return resolvedPromise$1
.then(function() { .then(function() {
return loader[RESOLVE](key, parent); return loader[RESOLVE](key, parent);
}) })
@ -261,7 +265,7 @@ Registry.prototype.get = function (key) {
}; };
// 4.4.7 // 4.4.7
Registry.prototype.set = function (key, namespace) { Registry.prototype.set = function (key, namespace) {
if (!(namespace instanceof ModuleNamespace)) if (!(namespace instanceof ModuleNamespace || namespace[toStringTag] === 'module'))
throw new Error('Registry must be set with an instance of Module Namespace'); throw new Error('Registry must be set with an instance of Module Namespace');
this[REGISTRY][key] = namespace; this[REGISTRY][key] = namespace;
return this; return this;
@ -318,8 +322,8 @@ function ModuleNamespace (baseObject/*, evaluate*/) {
// 8.4.2 // 8.4.2
ModuleNamespace.prototype = Object.create(null); ModuleNamespace.prototype = Object.create(null);
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) if (toStringTag)
Object.defineProperty(ModuleNamespace.prototype, Symbol.toStringTag, { Object.defineProperty(ModuleNamespace.prototype, toStringTag, {
value: 'Module' value: 'Module'
}); });
@ -366,8 +370,10 @@ Module.evaluate = function (ns) {
function throwResolveError (relUrl, parentUrl) { function throwResolveError (relUrl, parentUrl) {
throw new RangeError('Unable to resolve "' + relUrl + '" to ' + parentUrl); throw new RangeError('Unable to resolve "' + relUrl + '" to ' + parentUrl);
} }
var backslashRegEx = /\\/g;
function resolveIfNotPlain (relUrl, parentUrl) { function resolveIfNotPlain (relUrl, parentUrl) {
relUrl = relUrl.trim(); if (relUrl[0] === ' ' || relUrl[relUrl.length - 1] === ' ')
relUrl = relUrl.trim();
var parentProtocol = parentUrl && parentUrl.substr(0, parentUrl.indexOf(':') + 1); var parentProtocol = parentUrl && parentUrl.substr(0, parentUrl.indexOf(':') + 1);
var firstChar = relUrl[0]; var firstChar = relUrl[0];
@ -377,12 +383,16 @@ function resolveIfNotPlain (relUrl, parentUrl) {
if (firstChar === '/' && secondChar === '/') { if (firstChar === '/' && secondChar === '/') {
if (!parentProtocol) if (!parentProtocol)
throwResolveError(relUrl, parentUrl); throwResolveError(relUrl, parentUrl);
if (relUrl.indexOf('\\') !== -1)
relUrl = relUrl.replace(backslashRegEx, '/');
return parentProtocol + relUrl; return parentProtocol + relUrl;
} }
// relative-url // relative-url
else if (firstChar === '.' && (secondChar === '/' || secondChar === '.' && (relUrl[2] === '/' || relUrl.length === 2 && (relUrl += '/')) || else if (firstChar === '.' && (secondChar === '/' || secondChar === '.' && (relUrl[2] === '/' || relUrl.length === 2 && (relUrl += '/')) ||
relUrl.length === 1 && (relUrl += '/')) || relUrl.length === 1 && (relUrl += '/')) ||
firstChar === '/') { firstChar === '/') {
if (relUrl.indexOf('\\') !== -1)
relUrl = relUrl.replace(backslashRegEx, '/');
var parentIsPlain = !parentProtocol || parentUrl[parentProtocol.length] !== '/'; var parentIsPlain = !parentProtocol || parentUrl[parentProtocol.length] !== '/';
// read pathname from parent if a URL // read pathname from parent if a URL
@ -475,12 +485,13 @@ function resolveIfNotPlain (relUrl, parentUrl) {
if (isNode) { if (isNode) {
// C:\x becomes file:///c:/x (we don't support C|\x) // C:\x becomes file:///c:/x (we don't support C|\x)
if (relUrl[1] === ':' && relUrl[2] === '\\' && relUrl[0].match(/[a-z]/i)) if (relUrl[1] === ':' && relUrl[2] === '\\' && relUrl[0].match(/[a-z]/i))
return 'file:///' + relUrl.replace(/\\/g, '/'); return 'file:///' + relUrl.replace(backslashRegEx, '/');
} }
return relUrl; return relUrl;
} }
} }
var resolvedPromise = Promise.resolve();
/* /*
* Register Loader * Register Loader
* *
@ -575,6 +586,9 @@ function createLoadRecord (state, key, registration) {
// will be the array of dependency load record or a module namespace // will be the array of dependency load record or a module namespace
dependencyInstantiations: undefined, dependencyInstantiations: undefined,
// top-level await!
evaluatePromise: undefined,
// NB optimization and way of ensuring module objects in setters // NB optimization and way of ensuring module objects in setters
// indicates setters which should run pre-execution of that dependency // indicates setters which should run pre-execution of that dependency
// setters is then just for completely executed module objects // setters is then just for completely executed module objects
@ -592,7 +606,7 @@ RegisterLoader$1.prototype[Loader.resolveInstantiate] = function (key, parentKey
return resolveInstantiate(loader, key, parentKey, registry, state) return resolveInstantiate(loader, key, parentKey, registry, state)
.then(function (instantiated) { .then(function (instantiated) {
if (instantiated instanceof ModuleNamespace) if (instantiated instanceof ModuleNamespace || instantiated[toStringTag] === 'module')
return instantiated; return instantiated;
// resolveInstantiate always returns a load record with a link record and no module value // resolveInstantiate always returns a load record with a link record and no module value
@ -607,7 +621,7 @@ RegisterLoader$1.prototype[Loader.resolveInstantiate] = function (key, parentKey
return deepInstantiateDeps(loader, instantiated, link, registry, state) return deepInstantiateDeps(loader, instantiated, link, registry, state)
.then(function () { .then(function () {
return ensureEvaluate(loader, instantiated, link, registry, state, undefined); return ensureEvaluate(loader, instantiated, link, registry, state);
}); });
}); });
}; };
@ -672,14 +686,14 @@ function createProcessAnonRegister (loader, load, state) {
function instantiate (loader, load, link, registry, state) { function instantiate (loader, load, link, registry, state) {
return link.instantiatePromise || (link.instantiatePromise = return link.instantiatePromise || (link.instantiatePromise =
// if there is already an existing registration, skip running instantiate // if there is already an existing registration, skip running instantiate
(load.registration ? Promise.resolve() : Promise.resolve().then(function () { (load.registration ? resolvedPromise : resolvedPromise.then(function () {
state.lastRegister = undefined; state.lastRegister = undefined;
return loader[INSTANTIATE](load.key, loader[INSTANTIATE].length > 1 && createProcessAnonRegister(loader, load, state)); return loader[INSTANTIATE](load.key, loader[INSTANTIATE].length > 1 && createProcessAnonRegister(loader, load, state));
})) }))
.then(function (instantiation) { .then(function (instantiation) {
// direct module return from instantiate -> we're done // direct module return from instantiate -> we're done
if (instantiation !== undefined) { if (instantiation !== undefined) {
if (!(instantiation instanceof ModuleNamespace)) if (!(instantiation instanceof ModuleNamespace || instantiation[toStringTag] === 'module'))
throw new TypeError('Instantiate did not return a valid Module object.'); throw new TypeError('Instantiate did not return a valid Module object.');
delete state.records[load.key]; delete state.records[load.key];
@ -826,7 +840,7 @@ function registerDeclarative (loader, load, link, declare) {
return value; return value;
}, new ContextualLoader(loader, load.key)); }, new ContextualLoader(loader, load.key));
link.setters = declared.setters; link.setters = declared.setters || [];
link.execute = declared.execute; link.execute = declared.execute;
if (declared.exports) { if (declared.exports) {
link.moduleObj = moduleObj = declared.exports; link.moduleObj = moduleObj = declared.exports;
@ -854,7 +868,7 @@ function instantiateDeps (loader, load, link, registry, state) {
if (setter) { if (setter) {
var instantiation = dependencyInstantiations[i]; var instantiation = dependencyInstantiations[i];
if (instantiation instanceof ModuleNamespace) { if (instantiation instanceof ModuleNamespace || instantiation[toStringTag] === 'module') {
setter(instantiation); setter(instantiation);
} }
else { else {
@ -890,37 +904,30 @@ function instantiateDeps (loader, load, link, registry, state) {
} }
function deepInstantiateDeps (loader, load, link, registry, state) { function deepInstantiateDeps (loader, load, link, registry, state) {
return new Promise(function (resolve, reject) { var seen = [];
var seen = []; function addDeps (load, link) {
var loadCnt = 0; if (!link)
function queueLoad (load) { return resolvedPromise;
var link = load.linkRecord; if (seen.indexOf(load) !== -1)
if (!link) return resolvedPromise;
return; seen.push(load);
if (seen.indexOf(load) !== -1) return instantiateDeps(loader, load, link, registry, state)
return; .then(function () {
seen.push(load); var depPromises;
for (var i = 0; i < link.dependencies.length; i++) {
loadCnt++; var depLoad = link.dependencyInstantiations[i];
instantiateDeps(loader, load, link, registry, state) if (!(depLoad instanceof ModuleNamespace || depLoad[toStringTag] === 'module')) {
.then(processLoad, reject); depPromises = depPromises || [];
} depPromises.push(addDeps(depLoad, depLoad.linkRecord));
function processLoad (load) {
loadCnt--;
var link = load.linkRecord;
if (link) {
for (var i = 0; i < link.dependencies.length; i++) {
var depLoad = link.dependencyInstantiations[i];
if (!(depLoad instanceof ModuleNamespace))
queueLoad(depLoad);
} }
} }
if (loadCnt === 0) if (depPromises)
resolve(); return Promise.all(depPromises);
} });
queueLoad(load); }
});
return addDeps(load, link);
} }
/* /*
@ -981,23 +988,22 @@ ContextualLoader.prototype.import = function (key) {
return this.loader.resolve(key, this.key); return this.loader.resolve(key, this.key);
};*/ };*/
// this is the execution function bound to the Module namespace record function ensureEvaluate (loader, load, link, registry, state) {
function ensureEvaluate (loader, load, link, registry, state, seen) {
if (load.module) if (load.module)
return load.module; return load.module;
if (load.evalError) if (load.evalError)
throw load.evalError; throw load.evalError;
if (link.evaluatePromise)
return link.evaluatePromise;
if (seen && seen.indexOf(load) !== -1) if (link.setters) {
return load.linkRecord.moduleObj; var evaluatePromise = doEvaluateDeclarative(loader, load, link, registry, state, [load]);
if (evaluatePromise)
// for ES loads we always run ensureEvaluate on top-level, so empty seen is passed regardless return evaluatePromise;
// for dynamic loads, we pass seen if also dynamic }
var err = doEvaluate(loader, load, link, registry, state, link.setters ? [] : seen || []); else {
if (err) doEvaluateDynamic(loader, load, link, registry, state, [load]);
throw err; }
return load.module; return load.module;
} }
@ -1009,10 +1015,23 @@ function makeDynamicRequire (loader, key, dependencies, dependencyInstantiations
var depLoad = dependencyInstantiations[i]; var depLoad = dependencyInstantiations[i];
var module; var module;
if (depLoad instanceof ModuleNamespace) if (depLoad instanceof ModuleNamespace || depLoad[toStringTag] === 'module') {
module = depLoad; module = depLoad;
else }
module = ensureEvaluate(loader, depLoad, depLoad.linkRecord, registry, state, seen); else {
if (depLoad.evalError)
throw depLoad.evalError;
if (depLoad.module === undefined && seen.indexOf(depLoad) === -1 && !depLoad.linkRecord.evaluatePromise) {
if (depLoad.linkRecord.setters) {
doEvaluateDeclarative(loader, depLoad, depLoad.linkRecord, registry, state, [depLoad]);
}
else {
seen.push(depLoad);
doEvaluateDynamic(loader, depLoad, depLoad.linkRecord, registry, state, seen);
}
}
module = depLoad.module || depLoad.linkRecord.moduleObj;
}
return '__useDefault' in module ? module.__useDefault : module; return '__useDefault' in module ? module.__useDefault : module;
} }
@ -1021,133 +1040,173 @@ function makeDynamicRequire (loader, key, dependencies, dependencyInstantiations
}; };
} }
// ensures the given es load is evaluated 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 // returns the error if any
function doEvaluate (loader, load, link, registry, state, seen) { function doEvaluateDeclarative (loader, load, link, registry, state, seen) {
seen.push(load); var depLoad, depLink;
var depLoadPromises;
for (var i = 0; i < link.dependencies.length; i++) {
var depLoad = link.dependencyInstantiations[i];
if (depLoad instanceof ModuleNamespace || depLoad[toStringTag] === 'module')
continue;
var err; // custom Module returned from instantiate
depLink = depLoad.linkRecord;
// es modules evaluate dependencies first if (depLink) {
// non es modules explicitly call moduleEvaluate through require if (depLoad.evalError) {
if (link.setters) { evalError(load, depLoad.evalError);
var depLoad, depLink;
for (var i = 0; i < link.dependencies.length; i++) {
depLoad = link.dependencyInstantiations[i];
if (depLoad instanceof ModuleNamespace)
continue;
// custom Module returned from instantiate
depLink = depLoad.linkRecord;
if (depLink && seen.indexOf(depLoad) === -1) {
if (depLoad.evalError)
err = depLoad.evalError;
else
// dynamic / declarative boundaries clear the "seen" list
// we just let cross format circular throw as would happen in real implementations
err = doEvaluate(loader, depLoad, depLink, registry, state, depLink.setters ? seen : []);
} }
else if (depLink.setters) {
if (err) { if (seen.indexOf(depLoad) === -1) {
load.linkRecord = undefined; seen.push(depLoad);
load.evalError = LoaderError__Check_error_message_for_loader_stack(err, 'Evaluating ' + load.key); try {
return load.evalError; var depLoadPromise = doEvaluateDeclarative(loader, depLoad, depLink, registry, state, seen);
}
catch (e) {
evalError(load, e);
}
if (depLoadPromise) {
depLoadPromises = depLoadPromises || [];
depLoadPromises.push(depLoadPromise.catch(function (err) {
evalError(load, err);
}));
}
}
}
else {
try {
doEvaluateDynamic(loader, depLoad, depLink, registry, state, [depLoad]);
}
catch (e) {
evalError(load, e);
}
} }
} }
} }
// link.execute won't exist for Module returns from instantiate on top-level load if (depLoadPromises)
return link.evaluatePromise = Promise.all(depLoadPromises)
.then(function () {
if (link.execute) {
// ES System.register execute
// "this" is null in ES
try {
var execPromise = link.execute.call(nullContext);
}
catch (e) {
evalError(load, e);
}
if (execPromise)
return execPromise.catch(function (e) {
evalError(load, e);
})
.then(function () {
load.linkRecord = undefined;
return registry[load.key] = load.module = new ModuleNamespace(link.moduleObj);
});
}
// dispose link record
load.linkRecord = undefined;
registry[load.key] = load.module = new ModuleNamespace(link.moduleObj);
});
if (link.execute) { if (link.execute) {
// ES System.register execute // ES System.register execute
// "this" is null in ES // "this" is null in ES
if (link.setters) { try {
err = declarativeExecute(link.execute); var execPromise = link.execute.call(nullContext);
} }
// System.registerDynamic execute catch (e) {
// "this" is "exports" in CJS evalError(load, e);
else { }
var module = { id: load.key }; if (execPromise)
var moduleObj = link.moduleObj; return link.evaluatePromise = execPromise.catch(function (e) {
Object.defineProperty(module, 'exports', { evalError(load, e);
configurable: true, })
set: function (exports) { .then(function () {
moduleObj.default = moduleObj.__useDefault = exports; load.linkRecord = undefined;
}, return registry[load.key] = load.module = new ModuleNamespace(link.moduleObj);
get: function () {
return moduleObj.__useDefault;
}
}); });
var require = makeDynamicRequire(loader, load.key, link.dependencies, link.dependencyInstantiations, registry, state, seen);
// evaluate deps first
if (!link.executingRequire)
for (var i = 0; i < link.dependencies.length; i++)
require(link.dependencies[i]);
err = dynamicExecute(link.execute, require, moduleObj.default, module);
// pick up defineProperty calls to module.exports when we can
if (module.exports !== moduleObj.__useDefault)
moduleObj.default = moduleObj.__useDefault = module.exports;
var moduleDefault = moduleObj.default;
// __esModule flag extension support via lifting
if (moduleDefault && moduleDefault.__esModule) {
for (var p in moduleDefault) {
if (Object.hasOwnProperty.call(moduleDefault, p))
moduleObj[p] = moduleDefault[p];
}
}
}
} }
// dispose link record // dispose link record
load.linkRecord = undefined; load.linkRecord = undefined;
if (err)
return load.evalError = LoaderError__Check_error_message_for_loader_stack(err, 'Evaluating ' + load.key);
registry[load.key] = load.module = new ModuleNamespace(link.moduleObj); registry[load.key] = load.module = new ModuleNamespace(link.moduleObj);
// if not an esm module, run importer setters and clear them
// this allows dynamic modules to update themselves into es modules
// as soon as execution has completed
if (!link.setters) {
if (load.importerSetters)
for (var i = 0; i < load.importerSetters.length; i++)
load.importerSetters[i](load.module);
load.importerSetters = undefined;
}
} }
// {} is the closest we can get to call(undefined) // non es modules explicitly call moduleEvaluate through require
var nullContext = {}; function doEvaluateDynamic (loader, load, link, registry, state, seen) {
if (Object.freeze) // System.registerDynamic execute
Object.freeze(nullContext); // "this" is "exports" in CJS
var module = { id: load.key };
var moduleObj = link.moduleObj;
Object.defineProperty(module, 'exports', {
configurable: true,
set: function (exports) {
moduleObj.default = moduleObj.__useDefault = exports;
},
get: function () {
return moduleObj.__useDefault;
}
});
function declarativeExecute (execute) { var require = makeDynamicRequire(loader, load.key, link.dependencies, link.dependencyInstantiations, registry, state, seen);
try {
execute.call(nullContext); // evaluate deps first
} if (!link.executingRequire)
catch (e) { for (var i = 0; i < link.dependencies.length; i++)
return e; require(link.dependencies[i]);
}
}
function dynamicExecute (execute, require, exports, module) {
try { try {
var output = execute.call(envGlobal, require, exports, module); var output = link.execute.call(envGlobal, require, moduleObj.default, module);
if (output !== undefined) if (output !== undefined)
module.exports = output; module.exports = output;
} }
catch (e) { catch (e) {
return e; evalError(load, e);
} }
load.linkRecord = undefined;
// pick up defineProperty calls to module.exports when we can
if (module.exports !== moduleObj.__useDefault)
moduleObj.default = moduleObj.__useDefault = module.exports;
var moduleDefault = moduleObj.default;
// __esModule flag extension support via lifting
if (moduleDefault && moduleDefault.__esModule) {
for (var p in moduleDefault) {
if (Object.hasOwnProperty.call(moduleDefault, p))
moduleObj[p] = moduleDefault[p];
}
}
registry[load.key] = load.module = new ModuleNamespace(link.moduleObj);
// run importer setters and clear them
// this allows dynamic modules to update themselves into es modules
// as soon as execution has completed
if (load.importerSetters)
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)
Object.freeze(nullContext);
var loader; var loader;
// <script type="module"> support // <script type="module"> support
@ -1210,7 +1269,7 @@ if (typeof document != 'undefined' && document.getElementsByTagName) {
}; };
// simple DOM ready // simple DOM ready
if (document.readyState === 'complete') if (document.readyState !== 'loading')
setTimeout(ready); setTimeout(ready);
else else
document.addEventListener('DOMContentLoaded', ready, false); document.addEventListener('DOMContentLoaded', ready, false);
@ -1284,9 +1343,16 @@ var WorkerPool = function (script, size) {
var current = document.currentScript; var current = document.currentScript;
// IE doesn't support currentScript // IE doesn't support currentScript
if (!current) { if (!current) {
// We should be the last loaded script // Find an entry with out basename
var scripts = document.getElementsByTagName('script'); var scripts = document.getElementsByTagName('script');
current = scripts[scripts.length - 1]; for (var i = 0; i < scripts.length; i++) {
if (scripts[i].src.indexOf("browser-es-module-loader.js") !== -1) {
current = scripts[i];
break;
}
}
if (!current)
throw Error("Could not find own <script> element");
} }
script = current.src.substr(0, current.src.lastIndexOf("/")) + "/" + script; script = current.src.substr(0, current.src.lastIndexOf("/")) + "/" + script;
this._workers = new Array(size); this._workers = new Array(size);

File diff suppressed because one or more lines are too long

View file

@ -6,6 +6,7 @@ import babelTransformES2015ModulesSystemJS from 'babel-plugin-transform-es2015-m
var babelTransform = require('babel-core').transform; var babelTransform = require('babel-core').transform;
var babelTransformDynamicImport = require('babel-plugin-syntax-dynamic-import'); var babelTransformDynamicImport = require('babel-plugin-syntax-dynamic-import');
var babelTransformES2015ModulesSystemJS = require('babel-plugin-transform-es2015-modules-systemjs'); var babelTransformES2015ModulesSystemJS = require('babel-plugin-transform-es2015-modules-systemjs');
var babelPresetES2015 = require('babel-preset-es2015');
self.onmessage = function (evt) { self.onmessage = function (evt) {
// transform source with Babel // transform source with Babel
@ -17,6 +18,7 @@ self.onmessage = function (evt) {
sourceMaps: 'inline', sourceMaps: 'inline',
babelrc: false, babelrc: false,
plugins: [babelTransformDynamicImport, babelTransformES2015ModulesSystemJS], plugins: [babelTransformDynamicImport, babelTransformES2015ModulesSystemJS],
presets: [babelPresetES2015],
}); });
self.postMessage({key: evt.data.key, code: output.code, source: evt.data.source}); self.postMessage({key: evt.data.key, code: output.code, source: evt.data.source});

View file

@ -66,7 +66,7 @@ if (typeof document != 'undefined' && document.getElementsByTagName) {
} }
// simple DOM ready // simple DOM ready
if (document.readyState === 'complete') if (document.readyState !== 'loading')
setTimeout(ready); setTimeout(ready);
else else
document.addEventListener('DOMContentLoaded', ready, false); document.addEventListener('DOMContentLoaded', ready, false);
@ -140,9 +140,16 @@ var WorkerPool = function (script, size) {
var current = document.currentScript; var current = document.currentScript;
// IE doesn't support currentScript // IE doesn't support currentScript
if (!current) { if (!current) {
// We should be the last loaded script // Find an entry with out basename
var scripts = document.getElementsByTagName('script'); var scripts = document.getElementsByTagName('script');
current = scripts[scripts.length - 1]; for (var i = 0; i < scripts.length; i++) {
if (scripts[i].src.indexOf("browser-es-module-loader.js") !== -1) {
current = scripts[i];
break;
}
}
if (!current)
throw Error("Could not find own <script> element");
} }
script = current.src.substr(0, current.src.lastIndexOf("/")) + "/" + script; script = current.src.substr(0, current.src.lastIndexOf("/")) + "/" + script;
this._workers = new Array(size); this._workers = new Array(size);

File diff suppressed because one or more lines are too long

View file

@ -488,12 +488,11 @@ class wvmInstance(wvmConnect):
<target dev='%s' bus='%s'/> <target dev='%s' bus='%s'/>
</disk> </disk>
""" % (source, target, targetbus) """ % (source, target, targetbus)
if self.get_status() == 1:
self.instance.attachDeviceFlags(xml_disk, VIR_DOMAIN_AFFECT_LIVE)
self.instance.attachDeviceFlags(xml_disk, VIR_DOMAIN_AFFECT_CONFIG)
if self.get_status() == 5: if self.get_status() == 5:
devices = tree.find('devices') self.instance.attachDeviceFlags(xml_disk, VIR_DOMAIN_AFFECT_CONFIG)
elm_disk = ElementTree.fromstring(xml_disk)
devices.append(elm_disk)
xmldom = ElementTree.tostring(tree)
self._defineXML(xmldom)
def detach_disk(self, dev): def detach_disk(self, dev):
tree = ElementTree.fromstring(self._XMLDesc(0)) tree = ElementTree.fromstring(self._XMLDesc(0))