Upgrade noVNC to git commit 9f557f5

This commit is contained in:
Eric Schultz
2019-12-04 02:46:24 -06:00
parent 55bbb218af
commit 80b2a0dbc0
54 changed files with 114101 additions and 49550 deletions

View File

@@ -1,22 +1,32 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
// NB: this should *not* be included as a module until we have
// native support in the browsers, so that our error handler
// can catch script-loading errors.
// 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";
// Fallback for all uncought errors
function handleError (event, err) {
function handleError(event, err) {
try {
var msg = document.getElementById('noVNC_fallback_errormsg');
const msg = document.getElementById('noVNC_fallback_errormsg');
// Only show the initial error
if (msg.hasChildNodes()) {
return false;
}
var div = document.createElement("div");
let div = document.createElement("div");
div.classList.add('noVNC_message');
div.appendChild(document.createTextNode(event.message));
msg.appendChild(div);
@@ -24,7 +34,7 @@
if (event.filename) {
div = document.createElement("div");
div.className = 'noVNC_location';
var text = event.filename;
let text = event.filename;
if (event.lineno !== undefined) {
text += ":" + event.lineno;
if (event.colno !== undefined) {
@@ -35,7 +45,7 @@
msg.appendChild(div);
}
if (err && (err.stack !== undefined)) {
if (err && err.stack) {
div = document.createElement("div");
div.className = 'noVNC_stack';
div.appendChild(document.createTextNode(err.stack));
@@ -51,6 +61,6 @@
// from being printed to the browser console.
return false;
}
window.addEventListener('error', function (evt) { handleError(evt, evt.error); });
window.addEventListener('unhandledrejection', function (evt) { handleError(evt.reason, evt.reason); });
window.addEventListener('error', function onerror(evt) { handleError(evt, evt.error); });
window.addEventListener('unhandledrejection', function onreject(evt) { handleError(evt.reason, evt.reason); });
})();

View File

@@ -0,0 +1,42 @@
ICONS := \
novnc-16x16.png \
novnc-24x24.png \
novnc-32x32.png \
novnc-48x48.png \
novnc-64x64.png
ANDROID_LAUNCHER := \
novnc-48x48.png \
novnc-72x72.png \
novnc-96x96.png \
novnc-144x144.png \
novnc-192x192.png
IPHONE_LAUNCHER := \
novnc-60x60.png \
novnc-120x120.png
IPAD_LAUNCHER := \
novnc-76x76.png \
novnc-152x152.png
ALL_ICONS := $(ICONS) $(ANDROID_LAUNCHER) $(IPHONE_LAUNCHER) $(IPAD_LAUNCHER)
all: $(ALL_ICONS)
novnc-16x16.png: novnc-icon-sm.svg
convert -density 90 \
-background transparent "$<" "$@"
novnc-24x24.png: novnc-icon-sm.svg
convert -density 135 \
-background transparent "$<" "$@"
novnc-32x32.png: novnc-icon-sm.svg
convert -density 180 \
-background transparent "$<" "$@"
novnc-%.png: novnc-icon.svg
convert -density $$[`echo $* | cut -d x -f 1` * 90 / 48] \
-background transparent "$<" "$@"
clean:
rm -f *.png

View File

@@ -0,0 +1,65 @@
<?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.4 (unknown)"
x="0px"
y="0px"
viewBox="-293 384 25 25"
xml:space="preserve"
width="25"
height="25"><metadata
id="metadata21"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs19" /><sodipodi:namedview
pagecolor="#959595"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1136"
id="namedview17"
showgrid="true"
inkscape:pagecheckerboard="false"
inkscape:zoom="32"
inkscape:cx="3.926913"
inkscape:cy="13.255959"
inkscape:window-x="1920"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="svg2"><inkscape:grid
type="xygrid"
id="grid818" /></sodipodi:namedview>
<style
type="text/css"
id="style2">
.st0{fill:#FFFFFF;}
</style>
<path
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1;fill-opacity:1"
d="M 21 4 L 11 5.1757812 L 11 12 L 21 12 L 21 4 z M 10 5.2949219 L 4 6 L 4 12 L 10 12 L 10 5.2949219 z "
transform="translate(-293,384)"
id="path853" /><path
id="path858"
d="m -272,405 -10,-1.17578 V 397 h 10 z M -283,403.70508 -289,403 v -6 h 6 z"
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
inkscape:connector-curvature="0" /></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

View File

@@ -0,0 +1 @@
DO NOT MODIFY THE FILES IN THIS FOLDER, THEY ARE AUTOMATICALLY GENERATED FROM THE PO-FILES.

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,73 @@
{
"Connecting...": "接続しています...",
"Disconnecting...": "切断しています...",
"Reconnecting...": "再接続しています...",
"Internal error": "内部エラー",
"Must set host": "ホストを設定する必要があります",
"Connected (encrypted) to ": "接続しました (暗号化済み): ",
"Connected (unencrypted) to ": "接続しました (暗号化されていません): ",
"Something went wrong, connection is closed": "何かが問題で、接続が閉じられました",
"Failed to connect to server": "サーバーへの接続に失敗しました",
"Disconnected": "切断しました",
"New connection has been rejected with reason: ": "新規接続は次の理由で拒否されました: ",
"New connection has been rejected": "新規接続は拒否されました",
"Password is required": "パスワードが必要です",
"noVNC encountered an error:": "noVNC でエラーが発生しました:",
"Hide/Show the control bar": "コントロールバーを隠す/表示する",
"Move/Drag Viewport": "ビューポートを移動/ドラッグ",
"viewport drag": "ビューポートをドラッグ",
"Active Mouse Button": "アクティブなマウスボタン",
"No mousebutton": "マウスボタンなし",
"Left mousebutton": "左マウスボタン",
"Middle mousebutton": "中マウスボタン",
"Right mousebutton": "右マウスボタン",
"Keyboard": "キーボード",
"Show Keyboard": "キーボードを表示",
"Extra keys": "追加キー",
"Show Extra Keys": "追加キーを表示",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Ctrl キーを切り替え",
"Alt": "Alt",
"Toggle Alt": "Alt キーを切り替え",
"Toggle Windows": "Windows キーを切り替え",
"Windows": "Windows",
"Send Tab": "Tab キーを送信",
"Tab": "Tab",
"Esc": "Esc",
"Send Escape": "Escape キーを送信",
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
"Send Ctrl-Alt-Del": "Ctrl-Alt-Del を送信",
"Shutdown/Reboot": "シャットダウン/再起動",
"Shutdown/Reboot...": "シャットダウン/再起動...",
"Power": "電源",
"Shutdown": "シャットダウン",
"Reboot": "再起動",
"Reset": "リセット",
"Clipboard": "クリップボード",
"Clear": "クリア",
"Fullscreen": "全画面表示",
"Settings": "設定",
"Shared Mode": "共有モード",
"View Only": "表示のみ",
"Clip to Window": "ウィンドウにクリップ",
"Scaling Mode:": "スケーリングモード:",
"None": "なし",
"Local Scaling": "ローカルスケーリング",
"Remote Resizing": "リモートでリサイズ",
"Advanced": "高度",
"Repeater ID:": "リピーター ID:",
"WebSocket": "WebSocket",
"Encrypt": "暗号化",
"Host:": "ホスト:",
"Port:": "ポート:",
"Path:": "パス:",
"Automatic Reconnect": "自動再接続",
"Reconnect Delay (ms):": "再接続する遅延 (ミリ秒):",
"Show Dot when No Cursor": "カーソルがないときにドットを表示",
"Logging:": "ロギング:",
"Disconnect": "切断",
"Connect": "接続",
"Password:": "パスワード:",
"Send Password": "パスワードを送信",
"Cancel": "キャンセル"
}

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...",
"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 (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",
"Must set host": "Host moeten worden ingesteld",
"Reconnecting...": "Opnieuw verbinding maken...",
"New connection has been rejected with reason: ": "Nieuwe verbinding is geweigerd omwille van de volgende reden: ",
"New connection has been rejected": "Nieuwe verbinding is geweigerd",
"Password is required": "Wachtwoord is vereist",
"Disconnect timeout": "Timeout tijdens verbreken van verbinding",
"noVNC encountered an error:": "noVNC heeft een fout bemerkt:",
"Hide/Show the control bar": "Verberg/Toon de bedieningsbalk",
"Move/Drag Viewport": "Verplaats/Versleep Kijkvenster",
@@ -22,9 +26,11 @@
"Extra keys": "Extra toetsen",
"Show Extra Keys": "Toon Extra Toetsen",
"Ctrl": "Ctrl",
"Toggle Ctrl": "Ctrl aan/uitzetten",
"Toggle Ctrl": "Ctrl omschakelen",
"Alt": "Alt",
"Toggle Alt": "Alt aan/uitzetten",
"Toggle Alt": "Alt omschakelen",
"Toggle Windows": "Windows omschakelen",
"Windows": "Windows",
"Send Tab": "Tab Sturen",
"Tab": "Tab",
"Esc": "Esc",
@@ -47,10 +53,8 @@
"Scaling Mode:": "Schaalmodus:",
"None": "Geen",
"Local Scaling": "Lokaal Schalen",
"Local Downscaling": "Lokaal Neerschalen",
"Remote Resizing": "Op Afstand Formaat Wijzigen",
"Advanced": "Geavanceerd",
"Local Cursor": "Lokale Cursor",
"Repeater ID:": "Repeater ID:",
"WebSocket": "WebSocket",
"Encrypt": "Versleutelen",
@@ -59,10 +63,11 @@
"Path:": "Pad:",
"Automatic Reconnect": "Automatisch Opnieuw Verbinden",
"Reconnect Delay (ms):": "Vertraging voor Opnieuw Verbinden (ms):",
"Show Dot when No Cursor": "Geef stip weer indien geen cursor",
"Logging:": "Logmeldingen:",
"Disconnect": "Verbinding verbreken",
"Connect": "Verbinden",
"Password:": "Wachtwoord:",
"Cancel": "Annuleren",
"Canvas not supported.": "Canvas wordt niet ondersteund."
"Send Password": "Verzend Wachtwoord:",
"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...",
"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 (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",
"Must set host": "Du måste specifiera en värd",
"Reconnecting...": "Återansluter...",
"New connection has been rejected with reason: ": "Ny anslutning har blivit nekad med följande skäl: ",
"New connection has been rejected": "Ny anslutning har blivit nekad",
"Password is required": "Lösenord krävs",
"Disconnect timeout": "Det tog för lång tid att koppla ner",
"noVNC encountered an error:": "noVNC stötte på ett problem:",
"Hide/Show the control bar": "Göm/Visa kontrollbaren",
"Move/Drag Viewport": "Flytta/Dra Vyn",
@@ -25,6 +29,8 @@
"Toggle Ctrl": "Växla Ctrl",
"Alt": "Alt",
"Toggle Alt": "Växla Alt",
"Toggle Windows": "Växla Windows",
"Windows": "Windows",
"Send Tab": "Skicka Tab",
"Tab": "Tab",
"Esc": "Esc",
@@ -47,10 +53,8 @@
"Scaling Mode:": "Skalningsläge:",
"None": "Ingen",
"Local Scaling": "Lokal Skalning",
"Local Downscaling": "Lokal Nedskalning",
"Remote Resizing": "Ändra Storlek",
"Advanced": "Avancerat",
"Local Cursor": "Lokal Muspekare",
"Repeater ID:": "Repeater-ID:",
"WebSocket": "WebSocket",
"Encrypt": "Kryptera",
@@ -59,10 +63,11 @@
"Path:": "Sökväg:",
"Automatic Reconnect": "Automatisk Återanslutning",
"Reconnect Delay (ms):": "Fördröjning (ms):",
"Show Dot when No Cursor": "Visa prick när ingen muspekare finns",
"Logging:": "Loggning:",
"Disconnect": "Koppla från",
"Connect": "Anslut",
"Password:": "Lösenord:",
"Cancel": "Avbryt",
"Canvas not supported.": "Canvas stöds ej"
"Send Password": "Skicka lösenord",
"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
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
@@ -10,36 +10,35 @@
* Localization Utilities
*/
export function Localizer() {
// Currently configured language
this.language = 'en';
export class Localizer {
constructor() {
// Currently configured language
this.language = 'en';
// Current dictionary of translations
this.dictionary = undefined;
}
// Current dictionary of translations
this.dictionary = undefined;
}
Localizer.prototype = {
// Configure suitable language based on user preferences
setup: function (supportedLanguages) {
var userLanguages;
setup(supportedLanguages) {
this.language = 'en'; // Default: US English
/*
* Navigator.languages only available in Chrome (32+) and FireFox (32+)
* Fall back to navigator.language for other browsers
*/
let userLanguages;
if (typeof window.navigator.languages == 'object') {
userLanguages = window.navigator.languages;
} else {
userLanguages = [navigator.language || navigator.userLanguage];
}
for (var i = 0;i < userLanguages.length;i++) {
var userLang = userLanguages[i];
userLang = userLang.toLowerCase();
userLang = userLang.replace("_", "-");
userLang = userLang.split("-");
for (let i = 0;i < userLanguages.length;i++) {
const userLang = userLanguages[i]
.toLowerCase()
.replace("_", "-")
.split("-");
// Built-in default?
if ((userLang[0] === 'en') &&
@@ -48,66 +47,69 @@ Localizer.prototype = {
}
// First pass: perfect match
for (var j = 0;j < supportedLanguages.length;j++) {
var supLang = supportedLanguages[j];
supLang = supLang.toLowerCase();
supLang = supLang.replace("_", "-");
supLang = supLang.split("-");
for (let j = 0; j < supportedLanguages.length; j++) {
const supLang = supportedLanguages[j]
.toLowerCase()
.replace("_", "-")
.split("-");
if (userLang[0] !== supLang[0])
if (userLang[0] !== supLang[0]) {
continue;
if (userLang[1] !== supLang[1])
}
if (userLang[1] !== supLang[1]) {
continue;
}
this.language = supportedLanguages[j];
return;
}
// Second pass: fallback
for (var j = 0;j < supportedLanguages.length;j++) {
supLang = supportedLanguages[j];
supLang = supLang.toLowerCase();
supLang = supLang.replace("_", "-");
supLang = supLang.split("-");
for (let j = 0;j < supportedLanguages.length;j++) {
const supLang = supportedLanguages[j]
.toLowerCase()
.replace("_", "-")
.split("-");
if (userLang[0] !== supLang[0])
if (userLang[0] !== supLang[0]) {
continue;
if (supLang[1] !== undefined)
}
if (supLang[1] !== undefined) {
continue;
}
this.language = supportedLanguages[j];
return;
}
}
},
}
// Retrieve localised text
get: function (id) {
get(id) {
if (typeof this.dictionary !== 'undefined' && this.dictionary[id]) {
return this.dictionary[id];
} else {
return id;
}
},
}
// Traverses the DOM and translates relevant fields
// See https://html.spec.whatwg.org/multipage/dom.html#attr-translate
translateDOM: function () {
var self = this;
translateDOM() {
const self = this;
function process(elem, enabled) {
function isAnyOf(searchElement, items) {
return items.indexOf(searchElement) !== -1;
}
function translateAttribute(elem, attr) {
var str = elem.getAttribute(attr);
str = self.get(str);
const str = self.get(elem.getAttribute(attr));
elem.setAttribute(attr, str);
}
function translateTextNode(node) {
var str = node.data.trim();
str = self.get(str);
const str = self.get(node.data.trim());
node.data = str;
}
@@ -134,7 +136,7 @@ Localizer.prototype = {
}
if (elem.hasAttribute("label") &&
isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP",
"OPTION", "TRACK"])) {
"OPTION", "TRACK"])) {
translateAttribute(elem, "label");
}
// FIXME: Should update "lang"
@@ -152,8 +154,8 @@ Localizer.prototype = {
}
}
for (var i = 0;i < elem.childNodes.length;i++) {
var node = elem.childNodes[i];
for (let i = 0; i < elem.childNodes.length; i++) {
const node = elem.childNodes[i];
if (node.nodeType === node.ELEMENT_NODE) {
process(node, enabled);
} else if (node.nodeType === node.TEXT_NODE && enabled) {
@@ -163,8 +165,8 @@ Localizer.prototype = {
}
process(document.body, true);
},
};
}
}
export var l10n = new Localizer();
export const l10n = new Localizer();
export default l10n.get.bind(l10n);

View File

@@ -1,8 +1,6 @@
/*
* noVNC base CSS
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2016 Samuel Mannehed for Cendio AB
* Copyright (C) 2016 Pierre Ossman for Cendio AB
* Copyright (C) 2019 The noVNC Authors
* noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
*/
@@ -633,6 +631,16 @@ select:active {
width: 100px;
}
/* Version */
.noVNC_version_wrapper {
font-size: small;
}
.noVNC_version {
margin-left: 1rem;
}
/* Connection Controls */
:root:not(.noVNC_connected) #noVNC_disconnect_button {
display: none;

View File

@@ -1,63 +0,0 @@
/*
* noVNC auto CSS
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2017 Samuel Mannehed for Cendio AB
* noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
*/
body {
margin:0;
background-color:#313131;
border-bottom-right-radius: 800px 600px;
height:100%;
display: flex;
flex-direction: column;
}
html {
background-color:#494949;
height:100%;
}
#noVNC_status_bar {
width: 100%;
display:flex;
justify-content: space-between;
}
#noVNC_status {
color: #fff;
font: bold 12px Helvetica;
margin: auto;
}
.noVNC_status_normal {
background: linear-gradient(#b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%);
}
.noVNC_status_error {
background: linear-gradient(#c83737 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%);
}
.noVNC_status_warn {
background: linear-gradient(#b4b41e 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%);
}
.noNVC_shown {
display: inline;
}
.noVNC_hidden {
display: none;
}
#noVNC_left_dummy_elem {
flex: 1;
}
#noVNC_buttons {
padding: 1px;
flex: 1;
display: flex;
justify-content: flex-end;
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,6 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2013 NTT corp.
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
@@ -10,61 +9,65 @@
import { init_logging as main_init_logging } from '../core/util/logging.js';
// init log level reading the logging HTTP param
export function init_logging (level) {
export function init_logging(level) {
"use strict";
if (typeof level !== "undefined") {
main_init_logging(level);
} 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);
}
};
}
// Read a query string variable
export function getQueryVar (name, defVal) {
export function getQueryVar(name, defVal) {
"use strict";
var re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
const re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
match = document.location.href.match(re);
if (typeof defVal === 'undefined') { defVal = null; }
if (match) {
return decodeURIComponent(match[1]);
} else {
return defVal;
}
};
return defVal;
}
// Read a hash fragment variable
export function getHashVar (name, defVal) {
export function getHashVar(name, defVal) {
"use strict";
var re = new RegExp('.*[&#]' + name + '=([^&]*)'),
const re = new RegExp('.*[&#]' + name + '=([^&]*)'),
match = document.location.hash.match(re);
if (typeof defVal === 'undefined') { defVal = null; }
if (match) {
return decodeURIComponent(match[1]);
} else {
return defVal;
}
};
return defVal;
}
// Read a variable from the fragment or the query string
// Fragment takes precedence
export function getConfigVar (name, defVal) {
export function getConfigVar(name, defVal) {
"use strict";
var val = getHashVar(name);
const val = getHashVar(name);
if (val === null) {
val = getQueryVar(name, defVal);
return getQueryVar(name, defVal);
}
return val;
};
}
/*
* Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
*/
// No days means only for this browser session
export function createCookie (name, value, days) {
export function createCookie(name, value, days) {
"use strict";
var date, expires;
let date, expires;
if (days) {
date = new Date();
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
@@ -73,115 +76,123 @@ export function createCookie (name, value, days) {
expires = "";
}
var secure;
let secure;
if (document.location.protocol === "https:") {
secure = "; secure";
} else {
secure = "";
}
document.cookie = name + "=" + value + expires + "; path=/" + secure;
};
}
export function readCookie (name, defaultValue) {
export function readCookie(name, defaultValue) {
"use strict";
var nameEQ = name + "=",
ca = document.cookie.split(';');
const nameEQ = name + "=";
const ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i += 1) {
var c = ca[i];
while (c.charAt(0) === ' ') { c = c.substring(1, c.length); }
if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length, c.length); }
for (let i = 0; i < ca.length; i += 1) {
let c = ca[i];
while (c.charAt(0) === ' ') {
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";
createCookie(name, "", -1);
};
}
/*
* Setting handling.
*/
var settings = {};
let settings = {};
export function initSettings (callback /*, ...callbackArgs */) {
"use strict";
var callbackArgs = Array.prototype.slice.call(arguments, 1);
if (window.chrome && window.chrome.storage) {
window.chrome.storage.sync.get(function (cfg) {
settings = cfg;
if (callback) {
callback.apply(this, callbackArgs);
}
});
} else {
// No-op
if (callback) {
callback.apply(this, callbackArgs);
}
export function initSettings() {
if (!window.chrome || !window.chrome.storage) {
settings = {};
return Promise.resolve();
}
};
return new Promise(resolve => window.chrome.storage.sync.get(resolve))
.then((cfg) => { settings = cfg; });
}
// 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
export function writeSetting (name, value) {
export function writeSetting(name, value) {
"use strict";
if (settings[name] === value) return;
settings[name] = value;
if (window.chrome && window.chrome.storage) {
if (settings[name] !== value) {
settings[name] = value;
window.chrome.storage.sync.set(settings);
}
window.chrome.storage.sync.set(settings);
} else {
localStorage.setItem(name, value);
}
};
}
export function readSetting (name, defaultValue) {
export function readSetting(name, defaultValue) {
"use strict";
var value;
if (window.chrome && window.chrome.storage) {
let value;
if ((name in settings) || (window.chrome && window.chrome.storage)) {
value = settings[name];
} else {
value = localStorage.getItem(name);
settings[name] = value;
}
if (typeof value === "undefined") {
value = null;
}
if (value === null && typeof defaultValue !== "undefined") {
return defaultValue;
} else {
return value;
}
};
export function eraseSetting (name) {
return value;
}
export function eraseSetting(name) {
"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) {
window.chrome.storage.sync.remove(name);
delete settings[name];
} else {
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
// (assume that we wanted an extra if we pass one in)
path = "/" + path;
var elem = document.createElement('a');
const elem = document.createElement('a');
elem.href = path;
var param_eq = encodeURIComponent(param) + "=";
var query;
const param_eq = encodeURIComponent(param) + "=";
let query;
if (elem.search) {
query = elem.search.slice(1).split('&');
} else {
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));
elem.search = "?" + query.join("&");
}
@@ -190,41 +201,39 @@ export function injectParamIfMissing (path, param, value) {
// in the elem.pathname string. Handle that case gracefully.
if (elem.pathname.charAt(0) == "/") {
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
// IE11 support or polyfill promises and fetch in IE11.
// resolve will receive an object on success, while reject
// will receive either an event or an error on failure.
export function fetchJSON(path, resolve, reject) {
// NB: IE11 doesn't support JSON as a responseType
var req = new XMLHttpRequest();
req.open('GET', path);
export function fetchJSON(path) {
return new Promise((resolve, reject) => {
// NB: IE11 doesn't support JSON as a responseType
const req = new XMLHttpRequest();
req.open('GET', path);
req.onload = function () {
if (req.status === 200) {
try {
var resObj = JSON.parse(req.responseText);
} catch (err) {
reject(err);
return;
req.onload = () => {
if (req.status === 200) {
let resObj;
try {
resObj = JSON.parse(req.responseText);
} catch (err) {
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) {
reject(new Error("XHR encountered an error while trying to load '" + path + "': " + evt.message));
};
req.onerror = evt => reject(new Error("XHR encountered an error while trying to load '" + path + "': " + evt.message));
req.ontimeout = function (evt) {
reject(new Error("XHR timed out while trying to load '" + path + "'"));
};
req.ontimeout = evt => 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 {
/* Convert data (an array of integers) to a Base64 string. */
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
base64Pad : '=',
toBase64Table: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
base64Pad: '=',
encode: function (data) {
encode(data) {
"use strict";
var result = '';
var toBase64Table = this.toBase64Table;
var length = data.length;
var lengthpad = (length % 3);
let result = '';
const length = data.length;
const lengthpad = (length % 3);
// Convert every three bytes to 4 ascii characters.
for (var i = 0; i < (length - 2); i += 3) {
result += toBase64Table[data[i] >> 2];
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
result += toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
result += toBase64Table[data[i + 2] & 0x3f];
for (let i = 0; i < (length - 2); i += 3) {
result += this.toBase64Table[data[i] >> 2];
result += this.toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
result += this.toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
result += this.toBase64Table[data[i + 2] & 0x3f];
}
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
var j = 0;
const j = length - lengthpad;
if (lengthpad === 2) {
j = length - lengthpad;
result += toBase64Table[data[j] >> 2];
result += toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)];
result += toBase64Table[(data[j + 1] & 0x0f) << 2];
result += toBase64Table[64];
result += this.toBase64Table[data[j] >> 2];
result += this.toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)];
result += this.toBase64Table[(data[j + 1] & 0x0f) << 2];
result += this.toBase64Table[64];
} else if (lengthpad === 1) {
j = length - lengthpad;
result += toBase64Table[data[j] >> 2];
result += toBase64Table[(data[j] & 0x03) << 4];
result += toBase64Table[64];
result += toBase64Table[64];
result += this.toBase64Table[data[j] >> 2];
result += this.toBase64Table[(data[j] & 0x03) << 4];
result += this.toBase64Table[64];
result += this.toBase64Table[64];
}
return result;
},
/* 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,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,
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
],
/* eslint-enable comma-spacing */
decode: function (data, offset) {
"use strict";
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;
decode(data, offset = 0) {
let data_length = data.indexOf('=') - offset;
if (data_length < 0) { data_length = data.length - offset; }
/* Every four characters is 3 resulting numbers */
result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5);
result = new Array(result_length);
const result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5);
const result = new Array(result_length);
// Convert one by one.
for (var idx = 0, i = offset; i < data.length; i++) {
var c = toBinaryTable[data.charCodeAt(i) & 0x7f];
var padding = (data.charAt(i) === base64Pad);
let leftbits = 0; // number of bits decoded, but yet to be appended
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
if (c === -1) {
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 (leftbits) {
err = new Error('Corrupted base64 string');
const err = new Error('Corrupted base64 string');
err.name = 'Base64-Error';
throw err;
}

View File

@@ -0,0 +1,22 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
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,137 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
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,56 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
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,44 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
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,317 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
* 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,27 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*
*/
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/
*/
export default function DES(passwd) {
"use strict";
/* eslint-disable comma-spacing */
// Tables, permutations, S-boxes, etc.
var 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,
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],
z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8,
keys = [];
// Tables, permutations, S-boxes, etc.
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,
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];
a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e;
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,
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|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,
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=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e;
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,
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,
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,
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=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e;
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|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,
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,
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];
a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e;
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,
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,
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,
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=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|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,
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,
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,
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=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|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,
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,
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,
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];
a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e;
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,
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,
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,
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];
a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|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,
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];
const z = 0x0;
let a,b,c,d,e,f;
a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e;
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,
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|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,
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=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e;
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,
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,
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,
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=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e;
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|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,
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,
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];
a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|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,
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,
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,
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=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|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,
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,
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,
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=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e;
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,
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,
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,
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];
a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|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,
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,
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,
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];
a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e;
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,
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.
function setKeys(keyBlock) {
var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [],
raw0, raw1, rawi, KnLi;
/* eslint-enable comma-spacing */
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
m = l & 0x7;
pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
const m = l & 0x7;
pc1m[j] = ((password[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
}
for (i = 0; i < 16; ++i) {
m = i << 1;
n = m + 1;
for (let i = 0; i < 16; ++i) {
const m = i << 1;
const n = m + 1;
kn[m] = kn[n] = 0;
for (o = 28; o < 59; o += 28) {
for (j = o - 28; j < o; ++j) {
l = j + totrot[i];
if (l < o) {
pcr[j] = pc1m[l];
} else {
pcr[j] = pc1m[l - 28];
}
for (let o = 28; o < 59; o += 28) {
for (let j = o - 28; j < o; ++j) {
const l = j + totrot[i];
pcr[j] = l < o ? pc1m[l] : pc1m[l - 28];
}
}
for (j = 0; j < 24; ++j) {
for (let j = 0; j < 24; ++j) {
if (pcr[PC2[j]] !== 0) {
kn[m] |= 1 << (23 - j);
}
@@ -163,26 +162,26 @@ export default function DES(passwd) {
}
// cookey
for (i = 0, rawi = 0, KnLi = 0; i < 16; ++i) {
raw0 = kn[rawi++];
raw1 = kn[rawi++];
keys[KnLi] = (raw0 & 0x00fc0000) << 6;
keys[KnLi] |= (raw0 & 0x00000fc0) << 10;
keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6;
for (let i = 0, rawi = 0, KnLi = 0; i < 16; ++i) {
const raw0 = kn[rawi++];
const raw1 = kn[rawi++];
this.keys[KnLi] = (raw0 & 0x00fc0000) << 6;
this.keys[KnLi] |= (raw0 & 0x00000fc0) << 10;
this.keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
this.keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6;
++KnLi;
keys[KnLi] = (raw0 & 0x0003f000) << 12;
keys[KnLi] |= (raw0 & 0x0000003f) << 16;
keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;
keys[KnLi] |= (raw1 & 0x0000003f);
this.keys[KnLi] = (raw0 & 0x0003f000) << 12;
this.keys[KnLi] |= (raw0 & 0x0000003f) << 16;
this.keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;
this.keys[KnLi] |= (raw1 & 0x0000003f);
++KnLi;
}
}
// Encrypt 8 bytes of text
function enc8(text) {
var i = 0, b = text.slice(), fval, keysi = 0,
l, r, x; // left, right, accumulator
enc8(text) {
const b = text.slice();
let i = 0, l, r, x; // left, right, accumulator
// Squash 8 bytes to 2 ints
l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
@@ -206,26 +205,26 @@ export default function DES(passwd) {
r ^= x;
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 ^= keys[keysi++];
fval = SP7[x & 0x3f];
x ^= this.keys[keysi++];
let fval = SP7[x & 0x3f];
fval |= SP5[(x >>> 8) & 0x3f];
fval |= SP3[(x >>> 16) & 0x3f];
fval |= SP1[(x >>> 24) & 0x3f];
x = r ^ keys[keysi++];
x = r ^ this.keys[keysi++];
fval |= SP8[x & 0x3f];
fval |= SP6[(x >>> 8) & 0x3f];
fval |= SP4[(x >>> 16) & 0x3f];
fval |= SP2[(x >>> 24) & 0x3f];
l ^= fval;
x = (l << 28) | (l >>> 4);
x ^= keys[keysi++];
x ^= this.keys[keysi++];
fval = SP7[x & 0x3f];
fval |= SP5[(x >>> 8) & 0x3f];
fval |= SP3[(x >>> 16) & 0x3f];
fval |= SP1[(x >>> 24) & 0x3f];
x = l ^ keys[keysi++];
x = l ^ this.keys[keysi++];
fval |= SP8[x & 0x0000003f];
fval |= SP6[(x >>> 8) & 0x3f];
fval |= SP4[(x >>> 16) & 0x3f];
@@ -261,11 +260,7 @@ export default function DES(passwd) {
}
// Encrypt 16 bytes of text using passwd as key
function encrypt(t) {
return enc8(t.slice(0, 8)).concat(enc8(t.slice(8, 16)));
encrypt(t) {
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
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2015 Samuel Mannehed for Cendio AB
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
@@ -9,111 +8,104 @@
import * as Log from './util/logging.js';
import Base64 from "./base64.js";
import { supportsImageMetadata } from './util/browser.js';
export default function Display(target) {
this._drawCtx = null;
this._c_forceCanvas = false;
export default class Display {
constructor(target) {
this._drawCtx = null;
this._c_forceCanvas = false;
this._renderQ = []; // queue drawing actions for in-oder rendering
this._flushing = false;
this._renderQ = []; // queue drawing actions for in-oder rendering
this._flushing = false;
// the full frame buffer (logical canvas) size
this._fb_width = 0;
this._fb_height = 0;
// the full frame buffer (logical canvas) size
this._fb_width = 0;
this._fb_height = 0;
this._prevDrawStyle = "";
this._tile = null;
this._tile16x16 = null;
this._tile_x = 0;
this._tile_y = 0;
this._prevDrawStyle = "";
this._tile = null;
this._tile16x16 = null;
this._tile_x = 0;
this._tile_y = 0;
Log.Debug(">> Display.constructor");
Log.Debug(">> Display.constructor");
// The visible canvas
this._target = target;
// The visible canvas
this._target = target;
if (!this._target) {
throw new Error("Target must be set");
if (!this._target) {
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);
// 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;
// ===== 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 =====
_scale: 1.0,
get scale() { return this._scale; },
get scale() { return this._scale; }
set scale(scale) {
this._rescale(scale);
},
}
_clipViewport: false,
get clipViewport() { return this._clipViewport; },
get clipViewport() { return this._clipViewport; }
set clipViewport(viewport) {
this._clipViewport = viewport;
// May need to readjust the viewport dimensions
var vp = this._viewportLoc;
const vp = this._viewportLoc;
this.viewportChangeSize(vp.w, vp.h);
this.viewportChangePos(0, 0);
},
}
get width() {
return this._fb_width;
},
}
get height() {
return this._fb_height;
},
logo: null,
// ===== EVENT HANDLERS =====
onflush: function () {}, // A flush request has finished
}
// ===== PUBLIC METHODS =====
viewportChangePos: function (deltaX, deltaY) {
var vp = this._viewportLoc;
viewportChangePos(deltaX, deltaY) {
const vp = this._viewportLoc;
deltaX = Math.floor(deltaX);
deltaY = Math.floor(deltaY);
@@ -122,8 +114,8 @@ Display.prototype = {
deltaY = -vp.h;
}
var vx2 = vp.x + vp.w - 1;
var vy2 = vp.y + vp.h - 1;
const vx2 = vp.x + vp.w - 1;
const vy2 = vp.y + vp.h - 1;
// Position change
@@ -152,9 +144,9 @@ Display.prototype = {
this._damage(vp.x, vp.y, vp.w, vp.h);
this.flip();
},
}
viewportChangeSize: function(width, height) {
viewportChangeSize(width, height) {
if (!this._clipViewport ||
typeof(width) === "undefined" ||
@@ -165,6 +157,9 @@ Display.prototype = {
height = this._fb_height;
}
width = Math.floor(width);
height = Math.floor(height);
if (width > this._fb_width) {
width = this._fb_width;
}
@@ -172,12 +167,12 @@ Display.prototype = {
height = this._fb_height;
}
var vp = this._viewportLoc;
const vp = this._viewportLoc;
if (vp.w !== width || vp.h !== height) {
vp.w = width;
vp.h = height;
var canvas = this._target;
const canvas = this._target;
canvas.width = width;
canvas.height = height;
@@ -190,27 +185,33 @@ Display.prototype = {
// Update the visible size of the target canvas
this._rescale(this._scale);
}
},
}
absX: function (x) {
absX(x) {
if (this._scale === 0) {
return 0;
}
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;
},
}
resize: function (width, height) {
resize(width, height) {
this._prevDrawStyle = "";
this._fb_width = width;
this._fb_height = height;
var canvas = this._backbuffer;
const canvas = this._backbuffer;
if (canvas.width !== width || canvas.height !== height) {
// 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) {
saveImg = this._drawCtx.getImageData(0, 0, canvas.width, canvas.height);
}
@@ -229,13 +230,13 @@ Display.prototype = {
// Readjust the viewport as it may be incorrectly sized
// and positioned
var vp = this._viewportLoc;
const vp = this._viewportLoc;
this.viewportChangeSize(vp.w, vp.h);
this.viewportChangePos(0, 0);
},
}
// 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) {
this._damageBounds.left = x;
}
@@ -248,25 +249,23 @@ Display.prototype = {
if ((y + h) > this._damageBounds.bottom) {
this._damageBounds.bottom = y + h;
}
},
}
// Update the visible canvas with the contents of the
// rendering canvas
flip: function(from_queue) {
flip(from_queue) {
if (this._renderQ.length !== 0 && !from_queue) {
this._renderQ_push({
'type': 'flip'
});
} 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;
y = this._damageBounds.top;
w = this._damageBounds.right - x;
h = this._damageBounds.bottom - y;
vx = x - this._viewportLoc.x;
vy = y - this._viewportLoc.y;
let vx = x - this._viewportLoc.x;
let vy = y - this._viewportLoc.y;
if (vx < 0) {
w += vx;
@@ -298,32 +297,21 @@ Display.prototype = {
this._damageBounds.left = this._damageBounds.top = 65535;
this._damageBounds.right = this._damageBounds.bottom = 0;
}
},
}
clear: function () {
if (this._logo) {
this.resize(this._logo.width, this._logo.height);
this.imageRect(0, 0, this._logo.type, this._logo.data);
} else {
this.resize(240, 20);
this._drawCtx.clearRect(0, 0, this._fb_width, this._fb_height);
}
this.flip();
},
pending: function() {
pending() {
return this._renderQ.length > 0;
},
}
flush: function() {
flush() {
if (this._renderQ.length === 0) {
this.onflush();
} else {
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) {
this._renderQ_push({
'type': 'fill',
@@ -338,9 +326,9 @@ Display.prototype = {
this._drawCtx.fillRect(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) {
this._renderQ_push({
'type': 'copy',
@@ -369,10 +357,10 @@ Display.prototype = {
new_x, new_y, w, h);
this._damage(new_x, new_y, w, h);
}
},
}
imageRect: function(x, y, mime, arr) {
var img = new Image();
imageRect(x, y, mime, arr) {
const img = new Image();
img.src = "data: " + mime + ";base64," + Base64.encode(arr);
this._renderQ_push({
'type': 'img',
@@ -380,10 +368,10 @@ Display.prototype = {
'x': x,
'y': y
});
},
}
// start updating a tile
startTile: function (x, y, width, height, color) {
startTile(x, y, width, height, color) {
this._tile_x = x;
this._tile_y = y;
if (width === 16 && height === 16) {
@@ -392,53 +380,53 @@ Display.prototype = {
this._tile = this._drawCtx.createImageData(width, height);
}
var red = color[2];
var green = color[1];
var blue = color[0];
const red = color[2];
const green = color[1];
const blue = color[0];
var data = this._tile.data;
for (var i = 0; i < width * height * 4; i += 4) {
const data = this._tile.data;
for (let i = 0; i < width * height * 4; i += 4) {
data[i] = red;
data[i + 1] = green;
data[i + 2] = blue;
data[i + 3] = 255;
}
},
}
// update sub-rectangle of the current tile
subTile: function (x, y, w, h, color) {
var red = color[2];
var green = color[1];
var blue = color[0];
var xend = x + w;
var yend = y + h;
subTile(x, y, w, h, color) {
const red = color[2];
const green = color[1];
const blue = color[0];
const xend = x + w;
const yend = y + h;
var data = this._tile.data;
var width = this._tile.width;
for (var j = y; j < yend; j++) {
for (var i = x; i < xend; i++) {
var p = (i + (j * width)) * 4;
const data = this._tile.data;
const width = this._tile.width;
for (let j = y; j < yend; j++) {
for (let i = x; i < xend; i++) {
const p = (i + (j * width)) * 4;
data[p] = red;
data[p + 1] = green;
data[p + 2] = blue;
data[p + 3] = 255;
}
}
},
}
// draw the current tile to the screen
finishTile: function () {
finishTile() {
this._drawCtx.putImageData(this._tile, this._tile_x, this._tile_y);
this._damage(this._tile_x, this._tile_y,
this._tile.width, this._tile.height);
},
}
blitImage: 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) {
// NB(directxman12): it's technically more performant here to use preallocated arrays,
// but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
// this probably isn't getting called *nearly* as much
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));
this._renderQ_push({
'type': 'blit',
@@ -451,14 +439,14 @@ Display.prototype = {
} else {
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) {
// NB(directxman12): it's technically more performant here to use preallocated arrays,
// but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
// this probably isn't getting called *nearly* as much
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));
this._renderQ_push({
'type': 'blitRgb',
@@ -471,14 +459,14 @@ Display.prototype = {
} else {
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) {
// NB(directxman12): it's technically more performant here to use preallocated arrays,
// but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
// this probably isn't getting called *nearly* as much
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));
this._renderQ_push({
'type': 'blitRgbx',
@@ -491,72 +479,67 @@ Display.prototype = {
} else {
this._rgbxImageData(x, y, width, height, arr, offset);
}
},
}
drawImage: function (img, x, y) {
drawImage(img, x, y) {
this._drawCtx.drawImage(img, x, y);
this._damage(x, y, img.width, img.height);
},
}
changeCursor: function (pixels, mask, hotx, hoty, w, h) {
Display.changeCursor(this._target, pixels, mask, hotx, hoty, w, h);
},
autoscale(containerWidth, containerHeight) {
let scaleRatio;
defaultCursor: function () {
this._target.style.cursor = "default";
},
if (containerWidth === 0 || containerHeight === 0) {
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 {
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);
},
}
// ===== PRIVATE METHODS =====
_rescale: function (factor) {
_rescale(factor) {
this._scale = factor;
var vp = this._viewportLoc;
const vp = this._viewportLoc;
// NB(directxman12): If you set the width directly, or set the
// style width to a number, the canvas is cleared.
// However, if you set the style width to a string
// ('NNNpx'), the canvas is scaled without clearing.
var width = Math.round(factor * vp.w) + 'px';
var height = Math.round(factor * vp.h) + 'px';
const width = factor * vp.w + 'px';
const height = factor * vp.h + 'px';
if ((this._target.style.width !== width) ||
(this._target.style.height !== height)) {
this._target.style.width = width;
this._target.style.height = height;
}
},
}
_setFillColor: function (color) {
var newStyle = 'rgb(' + color[2] + ',' + color[1] + ',' + color[0] + ')';
_setFillColor(color) {
const newStyle = 'rgb(' + color[2] + ',' + color[1] + ',' + color[0] + ')';
if (newStyle !== this._prevDrawStyle) {
this._drawCtx.fillStyle = newStyle;
this._prevDrawStyle = newStyle;
}
},
}
_rgbImageData: function (x, y, width, height, arr, offset) {
var img = this._drawCtx.createImageData(width, height);
var data = img.data;
for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 3) {
_rgbImageData(x, y, width, height, arr, offset) {
const img = this._drawCtx.createImageData(width, height);
const data = img.data;
for (let i = 0, j = offset; i < width * height * 4; i += 4, j += 3) {
data[i] = arr[j];
data[i + 1] = arr[j + 1];
data[i + 2] = arr[j + 2];
@@ -564,12 +547,12 @@ Display.prototype = {
}
this._drawCtx.putImageData(img, x, y);
this._damage(x, y, img.width, img.height);
},
}
_bgrxImageData: function (x, y, width, height, arr, offset) {
var img = this._drawCtx.createImageData(width, height);
var data = img.data;
for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 4) {
_bgrxImageData(x, y, width, height, arr, offset) {
const img = this._drawCtx.createImageData(width, height);
const data = img.data;
for (let i = 0, j = offset; i < width * height * 4; i += 4, j += 4) {
data[i] = arr[j + 2];
data[i + 1] = arr[j + 1];
data[i + 2] = arr[j];
@@ -577,12 +560,12 @@ Display.prototype = {
}
this._drawCtx.putImageData(img, x, y);
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
var img;
if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) {
let img;
if (supportsImageMetadata) {
img = new ImageData(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4), width, height);
} else {
img = this._drawCtx.createImageData(width, height);
@@ -590,28 +573,28 @@ Display.prototype = {
}
this._drawCtx.putImageData(img, x, y);
this._damage(x, y, img.width, img.height);
},
}
_renderQ_push: function (action) {
_renderQ_push(action) {
this._renderQ.push(action);
if (this._renderQ.length === 1) {
// If this can be rendered immediately it will be, otherwise
// the scanner will wait for the relevant event
this._scan_renderQ();
}
},
}
_resume_renderQ: function() {
_resume_renderQ() {
// "this" is the object that is ready, not the
// display object
this.removeEventListener('load', this._noVNC_display._resume_renderQ);
this._noVNC_display._scan_renderQ();
},
}
_scan_renderQ: function () {
var ready = true;
_scan_renderQ() {
let ready = true;
while (ready && this._renderQ.length > 0) {
var a = this._renderQ[0];
const a = this._renderQ[0];
switch (a.type) {
case 'flip':
this.flip(true);
@@ -653,46 +636,5 @@ Display.prototype = {
this._flushing = false;
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
* Copyright (C) 2017 Pierre Ossman for Cendio AB
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
export var encodings = {
export const encodings = {
encodingRaw: 0,
encodingCopyRect: 1,
encodingRRE: 2,
encodingHextile: 5,
encodingTight: 7,
encodingTightPNG: -260,
pseudoEncodingQualityLevel9: -23,
pseudoEncodingQualityLevel0: -32,
@@ -19,13 +20,14 @@ export var encodings = {
pseudoEncodingLastRect: -224,
pseudoEncodingCursor: -239,
pseudoEncodingQEMUExtendedKeyEvent: -258,
pseudoEncodingTightPNG: -260,
pseudoEncodingDesktopName: -307,
pseudoEncodingExtendedDesktopSize: -308,
pseudoEncodingXvp: -309,
pseudoEncodingFence: -312,
pseudoEncodingContinuousUpdates: -313,
pseudoEncodingCompressLevel9: -247,
pseudoEncodingCompressLevel0: -256,
pseudoEncodingVMwareCursor: 0x574d5664
};
export function encodingName(num) {
@@ -35,6 +37,7 @@ export function encodingName(num) {
case encodings.encodingRRE: return "RRE";
case encodings.encodingHextile: return "Hextile";
case encodings.encodingTight: return "Tight";
case encodings.encodingTightPNG: return "TightPNG";
default: return "[unknown encoding " + num + "]";
}
}

View File

@@ -1,8 +1,17 @@
import { inflateInit, inflate, inflateReset } from "../vendor/pako/lib/zlib/inflate.js";
import ZStream from "../vendor/pako/lib/zlib/zstream.js";
Inflate.prototype = {
inflate: function (data, flush, expected) {
export default class Inflate {
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.avail_in = this.strm.input.length;
this.strm.next_in = 0;
@@ -21,18 +30,9 @@ Inflate.prototype = {
inflate(this.strm, flush);
return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
},
}
reset: function () {
reset() {
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
* 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)
*/
@@ -13,28 +13,25 @@ import KeyTable from "./keysym.js";
* See https://www.w3.org/TR/uievents-key/ for possible values.
*/
var DOMKeyTable = {};
const DOMKeyTable = {};
function addStandard(key, standard)
{
if (standard === undefined) throw "Undefined keysym for key \"" + key + "\"";
if (key in DOMKeyTable) throw "Duplicate entry for key \"" + key + "\"";
function addStandard(key, standard) {
if (standard === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
if (key in DOMKeyTable) throw new Error("Duplicate entry for key \"" + key + "\"");
DOMKeyTable[key] = [standard, standard, standard, standard];
}
function addLeftRight(key, left, right)
{
if (left === undefined) throw "Undefined keysym for key \"" + key + "\"";
if (right === undefined) throw "Undefined keysym for key \"" + key + "\"";
if (key in DOMKeyTable) throw "Duplicate entry for key \"" + key + "\"";
function addLeftRight(key, left, right) {
if (left === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
if (right === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
if (key in DOMKeyTable) throw new Error("Duplicate entry for key \"" + key + "\"");
DOMKeyTable[key] = [left, left, right, left];
}
function addNumpad(key, standard, numpad)
{
if (standard === undefined) throw "Undefined keysym for key \"" + key + "\"";
if (numpad === undefined) throw "Undefined keysym for key \"" + key + "\"";
if (key in DOMKeyTable) throw "Duplicate entry for key \"" + key + "\"";
function addNumpad(key, standard, numpad) {
if (standard === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
if (numpad === undefined) throw new Error("Undefined keysym for key \"" + key + "\"");
if (key in DOMKeyTable) throw new Error("Duplicate entry for key \"" + key + "\"");
DOMKeyTable[key] = [standard, standard, standard, numpad];
}
@@ -46,12 +43,10 @@ addStandard("CapsLock", KeyTable.XK_Caps_Lock);
addLeftRight("Control", KeyTable.XK_Control_L, KeyTable.XK_Control_R);
// - Fn
// - FnLock
addLeftRight("Hyper", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
addLeftRight("Meta", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
addStandard("NumLock", KeyTable.XK_Num_Lock);
addStandard("ScrollLock", KeyTable.XK_Scroll_Lock);
addLeftRight("Shift", KeyTable.XK_Shift_L, KeyTable.XK_Shift_R);
addLeftRight("Super", KeyTable.XK_Super_L, KeyTable.XK_Super_R);
// - Symbol
// - SymbolLock
@@ -75,7 +70,10 @@ addNumpad("PageUp", KeyTable.XK_Prior, KeyTable.XK_KP_Prior);
// 2.5. Editing Keys
addStandard("Backspace", KeyTable.XK_BackSpace);
addStandard("Clear", KeyTable.XK_Clear);
// Browsers send "Clear" for the numpad 5 without NumLock because
// Windows uses VK_Clear for that key. But Unix expects KP_Begin for
// that scenario.
addNumpad("Clear", KeyTable.XK_Clear, KeyTable.XK_KP_Begin);
addStandard("Copy", KeyTable.XF86XK_Copy);
// - CrSel
addStandard("Cut", KeyTable.XF86XK_Cut);
@@ -197,7 +195,8 @@ addStandard("F35", KeyTable.XK_F35);
addStandard("Close", KeyTable.XF86XK_Close);
addStandard("MailForward", KeyTable.XF86XK_MailForward);
addStandard("MailReply", KeyTable.XF86XK_Reply);
addStandard("MainSend", KeyTable.XF86XK_Send);
addStandard("MailSend", KeyTable.XF86XK_Send);
// - MediaClose
addStandard("MediaFastForward", KeyTable.XF86XK_AudioForward);
addStandard("MediaPause", KeyTable.XF86XK_AudioPause);
addStandard("MediaPlay", KeyTable.XF86XK_AudioPlay);
@@ -221,11 +220,9 @@ addStandard("SpellCheck", KeyTable.XF86XK_Spell);
// - AudioBalanceLeft
// - AudioBalanceRight
// - AudioBassDown
// - AudioBassBoostDown
// - AudioBassBoostToggle
// - AudioBassBoostUp
// - AudioBassUp
// - AudioFaderFront
// - AudioFaderRear
// - AudioSurroundModeNext
@@ -246,12 +243,12 @@ addStandard("MicrophoneVolumeMute", KeyTable.XF86XK_AudioMicMute);
// 2.14. Application Keys
addStandard("LaunchCalculator", KeyTable.XF86XK_Calculator);
addStandard("LaunchApplication1", KeyTable.XF86XK_MyComputer);
addStandard("LaunchApplication2", KeyTable.XF86XK_Calculator);
addStandard("LaunchCalendar", KeyTable.XF86XK_Calendar);
addStandard("LaunchMail", KeyTable.XF86XK_Mail);
addStandard("LaunchMediaPlayer", KeyTable.XF86XK_AudioMedia);
addStandard("LaunchMusicPlayer", KeyTable.XF86XK_Music);
addStandard("LaunchMyComputer", KeyTable.XF86XK_MyComputer);
addStandard("LaunchPhone", KeyTable.XF86XK_Phone);
addStandard("LaunchScreenSaver", KeyTable.XF86XK_ScreenSaver);
addStandard("LaunchSpreadsheet", KeyTable.XF86XK_Excel);

View File

@@ -1,6 +1,6 @@
/*
* 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)
*/
@@ -14,6 +14,8 @@
* See https://www.w3.org/TR/uievents-key/ for possible values.
*/
/* eslint-disable key-spacing */
export default {
// 3.1.1.1. Writing System Keys

View File

@@ -1,7 +1,6 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2013 Samuel Mannehed for Cendio AB
* Copyright (C) 2019 The noVNC Authors
* 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
//
export default function Keyboard(target) {
this._target = target || null;
export default class Keyboard {
constructor(target) {
this._target = target || null;
this._keyDownList = {}; // List of depressed keys
// (even if they are happy)
this._pendingKey = null; // Key waiting for keypress
this._keyDownList = {}; // List of depressed keys
// (even if they are happy)
this._pendingKey = null; // Key waiting for keypress
this._altGrArmed = false; // Windows AltGr detection
// keep these here so we can refer to them later
this._eventHandlers = {
'keyup': this._handleKeyUp.bind(this),
'keydown': this._handleKeyDown.bind(this),
'keypress': this._handleKeyPress.bind(this),
'blur': this._allKeysUp.bind(this)
};
};
// keep these here so we can refer to them later
this._eventHandlers = {
'keyup': this._handleKeyUp.bind(this),
'keydown': this._handleKeyDown.bind(this),
'keypress': this._handleKeyPress.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 =====
_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") +
", 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);
}
if (fakeAltGraph) {
this.onkeyevent(this._keyDownList['ControlLeft'],
'ControlLeft', true);
this.onkeyevent(this._keyDownList['AltRight'],
'AltRight', true);
}
},
_getKeyCode: function (e) {
var code = KeyboardUtil.getKeycode(e);
_getKeyCode(e) {
const code = KeyboardUtil.getKeycode(e);
if (code !== 'Unidentified') {
return code;
}
@@ -94,26 +79,46 @@ Keyboard.prototype = {
return e.keyIdentifier;
}
var codepoint = parseInt(e.keyIdentifier.substr(2), 16);
var char = String.fromCharCode(codepoint);
// Some implementations fail to uppercase the symbols
char = char.toUpperCase();
const codepoint = parseInt(e.keyIdentifier.substr(2), 16);
const char = String.fromCharCode(codepoint).toUpperCase();
return 'Platform' + char.charCodeAt();
}
return 'Unidentified';
},
}
_handleKeyDown: function (e) {
var code = this._getKeyCode(e);
var keysym = KeyboardUtil.getKeysym(e);
_handleKeyDown(e) {
const code = this._getKeyCode(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
// to deal with virtual keyboards which omit key info
// (iOS omits tracking info on keyup events, which forces us to
// special treat that platform here)
if ((code === 'Unidentified') || browser.isIOS()) {
if (code === 'Unidentified') {
if (keysym) {
// If it's a virtual keyboard then it should be
// sufficient to just send press and release right
@@ -130,20 +135,20 @@ Keyboard.prototype = {
// keys around a bit to make things more sane for the remote
// server. This method is used by RealVNC and TigerVNC (and
// possibly others).
if (browser.isMac()) {
if (browser.isMac() || browser.isIOS()) {
switch (keysym) {
case KeyTable.XK_Super_L:
keysym = KeyTable.XK_Alt_L;
break;
case KeyTable.XK_Super_R:
keysym = KeyTable.XK_Super_L;
break;
case KeyTable.XK_Alt_L:
keysym = KeyTable.XK_Mode_switch;
break;
case KeyTable.XK_Alt_R:
keysym = KeyTable.XK_ISO_Level3_Shift;
break;
case KeyTable.XK_Super_L:
keysym = KeyTable.XK_Alt_L;
break;
case KeyTable.XK_Super_R:
keysym = KeyTable.XK_Super_L;
break;
case KeyTable.XK_Alt_L:
keysym = KeyTable.XK_Mode_switch;
break;
case KeyTable.XK_Alt_R:
keysym = KeyTable.XK_ISO_Level3_Shift;
break;
}
}
@@ -157,7 +162,7 @@ Keyboard.prototype = {
// state change events. That gets extra confusing for CapsLock
// which toggles on each press, but not on release. So pretend
// it was a quick press and release of the button.
if (browser.isMac() && (code === 'CapsLock')) {
if ((browser.isMac() || browser.isIOS()) && (code === 'CapsLock')) {
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);
stopEvent(e);
@@ -180,13 +185,20 @@ Keyboard.prototype = {
this._pendingKey = null;
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);
},
}
// Legacy event for browsers without code/key
_handleKeyPress: function (e) {
_handleKeyPress(e) {
stopEvent(e);
// Are we expecting a keypress?
@@ -194,8 +206,8 @@ Keyboard.prototype = {
return;
}
var code = this._getKeyCode(e);
var keysym = KeyboardUtil.getKeysym(e);
let code = this._getKeyCode(e);
const keysym = KeyboardUtil.getKeysym(e);
// The key we were waiting for?
if ((code !== 'Unidentified') && (code != this._pendingKey)) {
@@ -210,19 +222,18 @@ Keyboard.prototype = {
return;
}
this._keyDownList[code] = keysym;
this._sendKeyEvent(keysym, code, true);
},
_handleKeyPressTimeout: function (e) {
}
_handleKeyPressTimeout(e) {
// Did someone manage to sort out the key already?
if (this._pendingKey === null) {
return;
}
var code, keysym;
let keysym;
code = this._pendingKey;
const code = this._pendingKey;
this._pendingKey = null;
// We have no way of knowing the proper keysym with the
@@ -233,82 +244,145 @@ Keyboard.prototype = {
keysym = e.keyCode;
} else if ((e.keyCode >= 0x41) && (e.keyCode <= 0x5a)) {
// Character (A-Z)
var char = String.fromCharCode(e.keyCode);
let char = String.fromCharCode(e.keyCode);
// A feeble attempt at the correct case
if (e.shiftKey)
if (e.shiftKey) {
char = char.toUpperCase();
else
} else {
char = char.toLowerCase();
}
keysym = char.charCodeAt();
} else {
// Unknown, give up
keysym = 0;
}
this._keyDownList[code] = keysym;
this._sendKeyEvent(keysym, code, true);
},
}
_handleKeyUp: function (e) {
_handleKeyUp(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()
if (browser.isMac() && (code === 'CapsLock')) {
if ((browser.isMac() || browser.isIOS()) && (code === 'CapsLock')) {
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', true);
this._sendKeyEvent(KeyTable.XK_Caps_Lock, 'CapsLock', false);
return;
}
// Do we really think this key is down?
if (!(code in this._keyDownList)) {
this._sendKeyEvent(this._keyDownList[code], code, false);
// Windows has a rather nasty bug where it won't send key
// release events for a Shift button if the other Shift is still
// pressed
if (browser.isWindows() && ((code === 'ShiftLeft') ||
(code === 'ShiftRight'))) {
if ('ShiftRight' in this._keyDownList) {
this._sendKeyEvent(this._keyDownList['ShiftRight'],
'ShiftRight', false);
}
if ('ShiftLeft' in this._keyDownList) {
this._sendKeyEvent(this._keyDownList['ShiftLeft'],
'ShiftLeft', false);
}
}
}
_handleAltGrTimeout() {
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");
}
// Alt workaround for Firefox on Windows, see below
_checkAlt(e) {
if (e.skipCheckAlt) {
return;
}
if (e.altKey) {
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];
},
_allKeysUp: function () {
Log.Debug(">> Keyboard.allKeysUp");
for (var code in this._keyDownList) {
this._sendKeyEvent(this._keyDownList[code], code, false);
};
this._keyDownList = {};
Log.Debug("<< Keyboard.allKeysUp");
},
const event = new KeyboardEvent('keyup',
{ key: downList[code],
code: code });
event.skipCheckAlt = true;
target.dispatchEvent(event);
});
}
// ===== PUBLIC METHODS =====
grab: function () {
grab() {
//Log.Debug(">> Keyboard.grab");
var c = this._target;
c.addEventListener('keydown', this._eventHandlers.keydown);
c.addEventListener('keyup', this._eventHandlers.keyup);
c.addEventListener('keypress', this._eventHandlers.keypress);
this._target.addEventListener('keydown', this._eventHandlers.keydown);
this._target.addEventListener('keyup', this._eventHandlers.keyup);
this._target.addEventListener('keypress', this._eventHandlers.keypress);
// Release (key up) if window loses focus
window.addEventListener('blur', this._eventHandlers.blur);
// Firefox on Windows has broken handling of Alt, so we need to
// poll as best we can for releases (still doesn't prevent the
// menu from popping up though as we can't call
// preventDefault())
if (browser.isWindows() && browser.isFirefox()) {
const handler = this._eventHandlers.checkalt;
['mousedown', 'mouseup', 'mousemove', 'wheel',
'touchstart', 'touchend', 'touchmove',
'keydown', 'keyup'].forEach(type =>
document.addEventListener(type, handler,
{ capture: true,
passive: true }));
}
//Log.Debug("<< Keyboard.grab");
},
}
ungrab: function () {
ungrab() {
//Log.Debug(">> Keyboard.ungrab");
var c = this._target;
c.removeEventListener('keydown', this._eventHandlers.keydown);
c.removeEventListener('keyup', this._eventHandlers.keyup);
c.removeEventListener('keypress', this._eventHandlers.keypress);
if (browser.isWindows() && browser.isFirefox()) {
const handler = this._eventHandlers.checkalt;
['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);
// Release (key up) all keys that are in a down state
this._allKeysUp();
//Log.Debug(">> Keyboard.ungrab");
},
};
}
}

View File

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

View File

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

View File

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

View File

@@ -6,7 +6,7 @@ import DOMKeyTable from "./domkeytable.js";
import * as browser from "../util/browser.js";
// Get 'KeyboardEvent.code', handling legacy browsers
export function getKeycode(evt){
export function getKeycode(evt) {
// Are we getting proper key identifiers?
// (unfortunately Firefox and Chrome are crappy here and gives
// us an empty string on some platforms, rather than leaving it
@@ -25,7 +25,7 @@ export function getKeycode(evt){
// in the 'keyCode' field for non-printable characters. However
// Webkit sets it to the same as charCode in 'keypress' events.
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
if (browser.isMac() && (code === 'ContextMenu')) {
@@ -92,6 +92,8 @@ export function getKey(evt) {
// Mozilla isn't fully in sync with the spec yet
switch (evt.key) {
case 'OS': return 'Meta';
case 'LaunchMyComputer': return 'LaunchApplication1';
case 'LaunchCalculator': return 'LaunchApplication2';
}
// iOS leaks some OS names
@@ -103,15 +105,27 @@ export function getKey(evt) {
case 'UIKeyInputEscape': return 'Escape';
}
// IE and Edge have broken handling of AltGraph so we cannot
// trust them for printable characters
if ((evt.key.length !== 1) || (!browser.isIE() && !browser.isEdge())) {
// Broken behaviour in Chrome
if ((evt.key === '\x00') && (evt.code === 'NumpadDecimal')) {
return 'Delete';
}
// IE and Edge need special handling, but for everyone else we
// can trust the value provided
if (!browser.isIE() && !browser.isEdge()) {
return evt.key;
}
// IE and Edge have broken handling of AltGraph so we can only
// trust them for non-printable characters (and unfortunately
// they also specify 'Unidentified' for some problem keys)
if ((evt.key.length !== 1) && (evt.key !== 'Unidentified')) {
return evt.key;
}
}
// Try to deduce it based on the physical key
var code = getKeycode(evt);
const code = getKeycode(evt);
if (code in fixedkeys) {
return fixedkeys[code];
}
@@ -126,8 +140,8 @@ export function getKey(evt) {
}
// Get the most reliable keysym value we can get from a key event
export function getKeysym(evt){
var key = getKey(evt);
export function getKeysym(evt) {
const key = getKey(evt);
if (key === 'Unidentified') {
return null;
@@ -135,30 +149,57 @@ export function getKeysym(evt){
// First look up special keys
if (key in DOMKeyTable) {
var location = evt.location;
let location = evt.location;
// Safari screws up location for the right cmd key
if ((key === 'Meta') && (location === 0)) {
location = 2;
}
// And for Clear
if ((key === 'Clear') && (location === 3)) {
let code = getKeycode(evt);
if (code === 'NumLock') {
location = 0;
}
}
if ((location === undefined) || (location > 3)) {
location = 0;
}
// The original Meta key now gets confused with the Windows key
// https://bugs.chromium.org/p/chromium/issues/detail?id=1020141
// https://bugzilla.mozilla.org/show_bug.cgi?id=1232918
if (key === 'Meta') {
let code = getKeycode(evt);
if (code === 'AltLeft') {
return KeyTable.XK_Meta_L;
} else if (code === 'AltRight') {
return KeyTable.XK_Meta_R;
}
}
// macOS has Clear instead of NumLock, but the remote system is
// probably not macOS, so lying here is probably best...
if (key === 'Clear') {
let code = getKeycode(evt);
if (code === 'NumLock') {
return KeyTable.XK_Num_Lock;
}
}
return DOMKeyTable[key][location];
}
// Now we need to look at the Unicode symbol instead
var codepoint;
// Special key? (FIXME: Should have been caught earlier)
if (key.length !== 1) {
return null;
}
codepoint = key.charCodeAt();
const codepoint = key.charCodeAt();
if (codepoint) {
return keysyms.lookup(codepoint);
}

View File

@@ -1,6 +1,6 @@
/*
* 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)
*/
@@ -13,6 +13,7 @@ export default {
0x08: 'Backspace',
0x09: 'Tab',
0x0a: 'NumpadClear',
0x0c: 'Numpad5', // IE11 sends evt.keyCode: 12 when numlock is off
0x0d: 'Enter',
0x10: 'ShiftLeft',
0x11: 'ControlLeft',

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
@@ -9,7 +9,7 @@
import * as Log from './logging.js';
// Touch detection
export var isTouchDevice = ('ontouchstart' in document.documentElement) ||
export let isTouchDevice = ('ontouchstart' in document.documentElement) ||
// requried for Chrome debugger
(document.ontouchstart !== undefined) ||
// required for MS Surface
@@ -20,42 +20,42 @@ window.addEventListener('touchstart', function onFirstTouch() {
window.removeEventListener('touchstart', onFirstTouch, false);
}, false);
var _cursor_uris_supported = null;
export function supportsCursorURIs () {
if (_cursor_uris_supported === null) {
try {
var target = document.createElement('canvas');
target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
// The goal is to find a certain physical width, the devicePixelRatio
// brings us a bit closer but is not optimal.
export let dragThreshold = 10 * (window.devicePixelRatio || 1);
if (target.style.cursor) {
Log.Info("Data URI scheme cursor supported");
_cursor_uris_supported = true;
} else {
Log.Warn("Data URI scheme cursor not supported");
_cursor_uris_supported = false;
}
} catch (exc) {
Log.Error("Data URI scheme cursor test exception: " + exc);
_cursor_uris_supported = false;
}
let _supportsCursorURIs = false;
try {
const target = document.createElement('canvas');
target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
if (target.style.cursor.indexOf("url") === 0) {
Log.Info("Data URI scheme cursor supported");
_supportsCursorURIs = true;
} 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() {
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() {
return navigator && !!(/win/i).exec(navigator.platform);
}
@@ -67,3 +67,24 @@ export function isIOS() {
!!(/ipod/i).exec(navigator.platform));
}
export function isAndroid() {
return navigator && !!(/android/i).exec(navigator.userAgent);
}
export function isSafari() {
return navigator && (navigator.userAgent.indexOf('Safari') !== -1 &&
navigator.userAgent.indexOf('Chrome') === -1);
}
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,258 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2019 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 (!this._target) {
return;
}
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) {
// Check if we should show the cursor on the element we are leaving to
this._updateVisibility(event.relatedTarget);
}
_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);
// Captures end with a mouseup but we can't know the event order of
// mouseup vs releaseCapture.
//
// In the cases when releaseCapture comes first, the code above is
// enough.
//
// In the cases when the mouseup comes first, we need wait for the
// browser to flush all events and then check again if the cursor
// should be visible.
if (this._captureIsActive()) {
window.setTimeout(() => {
// Refresh the target from elementFromPoint since queued events
// might have altered the DOM
target = document.elementFromPoint(event.clientX,
event.clientY);
this._updateVisibility(target);
}, 0);
}
}
_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) {
if (!target) {
return false;
}
// 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) {
// When the cursor target has capture we want to show the cursor.
// So, if a capture is active - look at the captured element instead.
if (this._captureIsActive()) {
target = document.captureElement;
}
if (this._shouldShowCursor(target)) {
this._showCursor();
} else {
this._hideCursor();
}
}
_updatePosition() {
this._canvas.style.left = this._position.x + "px";
this._canvas.style.top = this._position.y + "px";
}
_captureIsActive() {
return document.captureElement &&
document.documentElement.contains(document.captureElement);
}
}

View File

@@ -1,6 +1,6 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2018 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
@@ -10,27 +10,32 @@
* 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;
};
}
export function stopEvent (e) {
export function stopEvent(e) {
e.stopPropagation();
e.preventDefault();
};
}
// Emulate Element.setCapture() when not supported
var _captureRecursion = false;
var _captureElem = null;
let _captureRecursion = false;
let _elementForUnflushedEvents = null;
document.captureElement = null;
function _captureProxy(e) {
// Recursion protection as we'll see our own event
if (_captureRecursion) return;
// 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;
_captureElem.dispatchEvent(newEv);
if (document.captureElement) {
document.captureElement.dispatchEvent(newEv);
} else {
_elementForUnflushedEvents.dispatchEvent(newEv);
}
_captureRecursion = false;
// Avoid double events
@@ -45,94 +50,93 @@ function _captureProxy(e) {
if (e.type === "mouseup") {
releaseCapture();
}
};
}
// Follow cursor style of target element
function _captureElemChanged() {
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
captureElem.style.cursor = window.getComputedStyle(_captureElem).cursor;
};
var _captureObserver = new MutationObserver(_captureElemChanged);
function _capturedElemChanged() {
const proxyElem = document.getElementById("noVNC_mouse_capture_elem");
proxyElem.style.cursor = window.getComputedStyle(document.captureElement).cursor;
}
var _captureIndex = 0;
const _captureObserver = new MutationObserver(_capturedElemChanged);
export function setCapture (elem) {
if (elem.setCapture) {
export function setCapture(target) {
if (target.setCapture) {
elem.setCapture();
target.setCapture();
document.captureElement = target;
// IE releases capture on 'click' events which might not trigger
elem.addEventListener('mouseup', releaseCapture);
target.addEventListener('mouseup', releaseCapture);
} else {
// Release any existing capture in case this method is
// called multiple times without coordination
releaseCapture();
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
let proxyElem = document.getElementById("noVNC_mouse_capture_elem");
if (captureElem === null) {
captureElem = document.createElement("div");
captureElem.id = "noVNC_mouse_capture_elem";
captureElem.style.position = "fixed";
captureElem.style.top = "0px";
captureElem.style.left = "0px";
captureElem.style.width = "100%";
captureElem.style.height = "100%";
captureElem.style.zIndex = 10000;
captureElem.style.display = "none";
document.body.appendChild(captureElem);
if (proxyElem === null) {
proxyElem = document.createElement("div");
proxyElem.id = "noVNC_mouse_capture_elem";
proxyElem.style.position = "fixed";
proxyElem.style.top = "0px";
proxyElem.style.left = "0px";
proxyElem.style.width = "100%";
proxyElem.style.height = "100%";
proxyElem.style.zIndex = 10000;
proxyElem.style.display = "none";
document.body.appendChild(proxyElem);
// This is to make sure callers don't get confused by having
// our blocking element as the target
captureElem.addEventListener('contextmenu', _captureProxy);
proxyElem.addEventListener('contextmenu', _captureProxy);
captureElem.addEventListener('mousemove', _captureProxy);
captureElem.addEventListener('mouseup', _captureProxy);
proxyElem.addEventListener('mousemove', _captureProxy);
proxyElem.addEventListener('mouseup', _captureProxy);
}
_captureElem = elem;
_captureIndex++;
document.captureElement = target;
// Track cursor and get initial cursor
_captureObserver.observe(elem, {attributes:true});
_captureElemChanged();
_captureObserver.observe(target, {attributes: true});
_capturedElemChanged();
captureElem.style.display = "";
proxyElem.style.display = "";
// We listen to events on window in order to keep tracking if it
// happens to leave the viewport
window.addEventListener('mousemove', _captureProxy);
window.addEventListener('mouseup', _captureProxy);
}
};
}
export function releaseCapture () {
export function releaseCapture() {
if (document.releaseCapture) {
document.releaseCapture();
document.captureElement = null;
} else {
if (!_captureElem) {
if (!document.captureElement) {
return;
}
// There might be events already queued, so we need to wait for
// them to flush. E.g. contextmenu in Microsoft Edge
window.setTimeout(function(expected) {
// Only clear it if it's the expected grab (i.e. no one
// else has initiated a new grab)
if (_captureIndex === expected) {
_captureElem = null;
}
}, 0, _captureIndex);
// There might be events already queued. The event proxy needs
// access to the captured element for these queued events.
// E.g. contextmenu (right-click) in Microsoft Edge
//
// Before removing the capturedElem pointer we save it to a
// temporary variable that the unflushed events can use.
_elementForUnflushedEvents = document.captureElement;
document.captureElement = null;
_captureObserver.disconnect();
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
captureElem.style.display = "none";
const proxyElem = document.getElementById("noVNC_mouse_capture_elem");
proxyElem.style.display = "none";
window.removeEventListener('mousemove', _captureProxy);
window.removeEventListener('mouseup', _captureProxy);
}
};
}

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
/*
* 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)
*/
@@ -16,13 +16,13 @@ if (typeof Object.assign != 'function') {
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++) {
var nextSource = arguments[index];
for (let index = 1; index < arguments.length; index++) {
const nextSource = arguments[index];
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
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
@@ -38,10 +38,10 @@ if (typeof Object.assign != 'function') {
}
/* CustomEvent constructor (taken from MDN) */
(function () {
function CustomEvent ( event, params ) {
(() => {
function CustomEvent(event, params) {
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 );
return evt;
}

View File

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

View File

@@ -1,6 +1,6 @@
/*
* Websock: high-performance binary WebSockets
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2019 The noVNC Authors
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* Websock is similar to the standard WebSocket object but with extra
@@ -14,143 +14,121 @@
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
// 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.
var ENABLE_COPYWITHIN = false;
// Also copyWithin() for TypedArrays is not supported in IE 11 or
// Safari 13 (at the moment we want to support Safari 11).
const ENABLE_COPYWITHIN = false;
const MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
var MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
export default class Websock {
constructor() {
this._websocket = null; // WebSocket object
var typedArrayToString = (function () {
// This is only for PhantomJS, which doesn't like apply-ing
// with Typed Arrays
try {
var arr = new Uint8Array([1, 2, 3]);
String.fromCharCode.apply(null, arr);
return function (a) { return String.fromCharCode.apply(null, a); };
} catch (ex) {
return function (a) {
return String.fromCharCode.apply(
null, Array.prototype.slice.call(a));
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)
// 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: () => {},
open: () => {},
close: () => {},
error: () => {}
};
}
})();
Websock.prototype = {
// Getters and Setters
get_sQ: function () {
get sQ() {
return this._sQ;
},
}
get_rQ: function () {
get rQ() {
return this._rQ;
},
}
get_rQi: function () {
get rQi() {
return this._rQi;
},
}
set_rQi: function (val) {
set rQi(val) {
this._rQi = val;
},
}
// Receive Queue
rQlen: function () {
get rQlen() {
return this._rQlen - this._rQi;
},
}
rQpeek8: function () {
rQpeek8() {
return this._rQ[this._rQi];
},
}
rQshift8: function () {
return this._rQ[this._rQi++];
},
rQskipBytes(bytes) {
this._rQi += bytes;
}
rQskip8: function () {
this._rQi++;
},
rQshift8() {
return this._rQshift(1);
}
rQskipBytes: function (num) {
this._rQi += num;
},
rQshift16() {
return this._rQshift(2);
}
rQshift32() {
return this._rQshift(4);
}
// TODO(directxman12): test performance with these vs a DataView
rQshift16: function () {
return (this._rQ[this._rQi++] << 8) +
this._rQ[this._rQi++];
},
_rQshift(bytes) {
let res = 0;
for (let byte = bytes - 1; byte >= 0; byte--) {
res += this._rQ[this._rQi++] << (byte * 8);
}
return res;
}
rQshift32: function () {
return (this._rQ[this._rQi++] << 24) +
(this._rQ[this._rQi++] << 16) +
(this._rQ[this._rQi++] << 8) +
this._rQ[this._rQi++];
},
rQshiftStr(len) {
if (typeof(len) === 'undefined') { len = this.rQlen; }
let str = "";
// Handle large arrays in steps to avoid long strings on the stack
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) {
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(); }
rQshiftBytes(len) {
if (typeof(len) === 'undefined') { len = this.rQlen; }
this._rQi += len;
return new Uint8Array(this._rQ.buffer, this._rQi - len, len);
},
}
rQshiftTo: function (target, len) {
if (len === undefined) { len = this.rQlen(); }
rQshiftTo(target, len) {
if (len === undefined) { len = this.rQlen; }
// 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));
this._rQi += len;
},
}
rQwhole: function () {
return new Uint8Array(this._rQ.buffer, 0, this._rQlen);
},
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);
}
},
rQslice(start, end = this.rQlen) {
return new Uint8Array(this._rQ.buffer, this._rQi + start, end - start);
}
// 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
// wait (and possibly print a debug message), otherwise false.
rQwait: function (msg, num, goback) {
var rQlen = this._rQlen - this._rQi; // Skip rQlen() function call
if (rQlen < num) {
rQwait(msg, num, goback) {
if (this.rQlen < num) {
if (goback) {
if (this._rQi < goback) {
throw new Error("rQwait cannot backup " + goback + " bytes");
@@ -160,58 +138,55 @@ Websock.prototype = {
return true; // true means need more data
}
return false;
},
}
// Send Queue
flush: function () {
flush() {
if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) {
this._websocket.send(this._encode_message());
this._sQlen = 0;
}
},
}
send: function (arr) {
send(arr) {
this._sQ.set(arr, this._sQlen);
this._sQlen += arr.length;
this.flush();
},
}
send_string: function (str) {
this.send(str.split('').map(function (chr) {
return chr.charCodeAt(0);
}));
},
send_string(str) {
this.send(str.split('').map(chr => chr.charCodeAt(0)));
}
// Event Handlers
off: function (evt) {
this._eventHandlers[evt] = function () {};
},
off(evt) {
this._eventHandlers[evt] = () => {};
}
on: function (evt, handler) {
on(evt, handler) {
this._eventHandlers[evt] = handler;
},
}
_allocate_buffers: function () {
_allocate_buffers() {
this._rQ = new Uint8Array(this._rQbufferSize);
this._sQ = new Uint8Array(this._sQbufferSize);
},
}
init: function () {
init() {
this._allocate_buffers();
this._rQi = 0;
this._websocket = null;
},
}
open: function (uri, protocols) {
var ws_schema = uri.match(/^([a-z]+):\/\//)[1];
open(uri, protocols) {
this.init();
this._websocket = new WebSocket(uri, protocols);
this._websocket.binaryType = 'arraybuffer';
this._websocket.onmessage = this._recv_message.bind(this);
this._websocket.onopen = (function () {
this._websocket.onopen = () => {
Log.Debug('>> WebSock.onopen');
if (this._websocket.protocol) {
Log.Info("Server choose sub-protocol: " + this._websocket.protocol);
@@ -219,20 +194,20 @@ Websock.prototype = {
this._eventHandlers.open();
Log.Debug("<< WebSock.onopen");
}).bind(this);
this._websocket.onclose = (function (e) {
};
this._websocket.onclose = (e) => {
Log.Debug(">> WebSock.onclose");
this._eventHandlers.close(e);
Log.Debug("<< WebSock.onclose");
}).bind(this);
this._websocket.onerror = (function (e) {
};
this._websocket.onerror = (e) => {
Log.Debug(">> WebSock.onerror: " + e);
this._eventHandlers.error(e);
Log.Debug("<< WebSock.onerror: " + e);
}).bind(this);
},
};
}
close: function () {
close() {
if (this._websocket) {
if ((this._websocket.readyState === WebSocket.OPEN) ||
(this._websocket.readyState === WebSocket.CONNECTING)) {
@@ -240,77 +215,80 @@ Websock.prototype = {
this._websocket.close();
}
this._websocket.onmessage = function (e) { return; };
this._websocket.onmessage = () => {};
}
},
}
// private methods
_encode_message: function () {
_encode_message() {
// Put in a binary arraybuffer
// according to the spec, you can send ArrayBufferViews with the send method
return new Uint8Array(this._sQ.buffer, 0, this._sQlen);
},
}
// We want to move all the unread data to the start of the queue,
// e.g. compacting.
// The function also expands the receive que if needed, and for
// performance reasons we combine these two actions to avoid
// unneccessary copying.
_expand_compact_rQ(min_fit) {
// if we're using less than 1/8th of the buffer even with the incoming bytes, compact in place
// instead of resizing
const required_buffer_size = (this._rQlen - this._rQi + min_fit) * 8;
const resizeNeeded = this._rQbufferSize < required_buffer_size;
_expand_compact_rQ: function (min_fit) {
var resizeNeeded = min_fit || this._rQlen - this._rQi > this._rQbufferSize / 2;
if (resizeNeeded) {
if (!min_fit) {
// just double the size if we need to do compaction
this._rQbufferSize *= 2;
} else {
// otherwise, make sure we satisy rQlen - rQi + min_fit < rQbufferSize / 8
this._rQbufferSize = (this._rQlen - this._rQi + min_fit) * 8;
}
// Make sure we always *at least* double the buffer size, and have at least space for 8x
// the current amount of data
this._rQbufferSize = Math.max(this._rQbufferSize * 2, required_buffer_size);
}
// we don't want to grow unboundedly
if (this._rQbufferSize > MAX_RQ_GROW_SIZE) {
this._rQbufferSize = MAX_RQ_GROW_SIZE;
if (this._rQbufferSize - this._rQlen - this._rQi < min_fit) {
throw new Exception("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit");
if (this._rQbufferSize - this.rQlen < min_fit) {
throw new Error("Receive Queue buffer exceeded " + MAX_RQ_GROW_SIZE + " bytes, and the new message could not fit");
}
}
if (resizeNeeded) {
var old_rQbuffer = this._rQ.buffer;
this._rQmax = this._rQbufferSize / 8;
const old_rQbuffer = this._rQ.buffer;
this._rQ = new Uint8Array(this._rQbufferSize);
this._rQ.set(new Uint8Array(old_rQbuffer, this._rQi));
this._rQ.set(new Uint8Array(old_rQbuffer, this._rQi, this._rQlen - this._rQi));
} else {
if (ENABLE_COPYWITHIN) {
this._rQ.copyWithin(0, this._rQi);
this._rQ.copyWithin(0, this._rQi, this._rQlen);
} else {
this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi));
this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi, this._rQlen - this._rQi));
}
}
this._rQlen = this._rQlen - this._rQi;
this._rQi = 0;
},
}
_decode_message: function (data) {
// push arraybuffer values onto the end
var u8 = new Uint8Array(data);
// push arraybuffer values onto the end of the receive que
_decode_message(data) {
const u8 = new Uint8Array(data);
if (u8.length > this._rQbufferSize - this._rQlen) {
this._expand_compact_rQ(u8.length);
}
this._rQ.set(u8, this._rQlen);
this._rQlen += u8.length;
},
}
_recv_message: function (e) {
_recv_message(e) {
this._decode_message(e.data);
if (this.rQlen() > 0) {
if (this.rQlen > 0) {
this._eventHandlers.message();
// Compact the receive queue
if (this._rQlen == this._rQi) {
// All data has now been processed, this means we
// can reset the receive queue.
this._rQlen = 0;
this._rQi = 0;
} else if (this._rQlen > this._rQmax) {
this._expand_compact_rQ();
}
} else {
Log.Debug("Ignoring empty message");
}
}
};
}

View File

@@ -0,0 +1,82 @@
{
"name": "@novnc/novnc",
"version": "1.1.0",
"description": "An HTML5 VNC client",
"browser": "lib/rfb",
"directories": {
"lib": "lib",
"doc": "docs",
"test": "tests"
},
"files": [
"lib",
"AUTHORS",
"VERSION",
"docs/API.md",
"docs/LIBRARY.md",
"docs/LICENSE*",
"core",
"vendor/pako"
],
"scripts": {
"lint": "eslint app core po/po2js po/xgettext-html tests utils",
"test": "karma start karma.conf.js",
"prepublish": "node ./utils/use_require.js --as commonjs --clean"
},
"repository": {
"type": "git",
"url": "git+https://github.com/novnc/noVNC.git"
},
"author": "Joel Martin <github@martintribe.org> (https://github.com/kanaka)",
"contributors": [
"Solly Ross <sross@redhat.com> (https://github.com/directxman12)",
"Peter Åstrand <astrand@cendio.se> (https://github.com/astrand)",
"Samuel Mannehed <samuel@cendio.se> (https://github.com/samhed)",
"Pierre Ossman <ossman@cendio.se> (https://github.com/CendioOssman)"
],
"license": "MPL-2.0",
"bugs": {
"url": "https://github.com/novnc/noVNC/issues"
},
"homepage": "https://github.com/novnc/noVNC",
"devDependencies": {
"@babel/core": "*",
"@babel/plugin-syntax-dynamic-import": "*",
"@babel/plugin-transform-modules-amd": "*",
"@babel/plugin-transform-modules-commonjs": "*",
"@babel/plugin-transform-modules-systemjs": "*",
"@babel/plugin-transform-modules-umd": "*",
"@babel/preset-env": "*",
"@babel/cli": "*",
"babel-plugin-import-redirect": "*",
"browserify": "*",
"babelify": "*",
"core-js": "*",
"chai": "*",
"commander": "*",
"es-module-loader": "*",
"eslint": "*",
"fs-extra": "*",
"jsdom": "*",
"karma": "*",
"karma-mocha": "*",
"karma-mocha-reporter": "*",
"karma-sauce-launcher": "*",
"karma-sinon-chai": "*",
"mocha": "*",
"node-getopt": "*",
"po2json": "*",
"requirejs": "*",
"rollup": "*",
"rollup-plugin-node-resolve": "*",
"sinon": "*",
"sinon-chai": "*"
},
"dependencies": {},
"keywords": [
"vnc",
"rfb",
"novnc",
"websockify"
]
}

View File

@@ -6,8 +6,8 @@ It's based heavily on
https://github.com/ModuleLoader/browser-es-module-loader, but uses
WebWorkers to compile the modules in the background.
To generate, run `rollup -c` in this directory, and then run `browserify
src/babel-worker.js > dist/babel-worker.js`.
To generate, run `npx rollup -c` in this directory, and then run
`./genworker.js`.
LICENSE
-------

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,13 @@
#!/usr/bin/env node
var fs = require("fs");
var browserify = require("browserify");
browserify("src/babel-worker.js")
.transform("babelify", {
presets: [ [ "@babel/preset-env", { targets: "ie >= 11" } ] ],
global: true,
ignore: [ "../../node_modules/core-js" ]
})
.bundle()
.pipe(fs.createWriteStream("dist/babel-worker.js"));

View File

@@ -1,16 +1,15 @@
import nodeResolve from 'rollup-plugin-node-resolve';
export default {
entry: 'src/browser-es-module-loader.js',
dest: 'dist/browser-es-module-loader.js',
format: 'umd',
moduleName: 'BrowserESModuleLoader',
sourceMap: true,
input: 'src/browser-es-module-loader.js',
output: {
file: 'dist/browser-es-module-loader.js',
format: 'umd',
name: 'BrowserESModuleLoader',
sourcemap: true,
},
plugins: [
nodeResolve(),
],
// skip rollup warnings (specifically the eval warning)
onwarn: function() {}
};

File diff suppressed because one or more lines are too long

View File

@@ -1,12 +1,10 @@
<!DOCTYPE html>
<html class="noVNC_loading">
<html lang="en" class="noVNC_loading">
<head>
<!--
noVNC example: simple example using default UI
Copyright (C) 2012 Joel Martin
Copyright (C) 2016 Samuel Mannehed for Cendio AB
Copyright (C) 2016 Pierre Ossman for Cendio AB
Copyright (C) 2019 The noVNC Authors
noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
@@ -17,7 +15,7 @@
-->
<title>noVNC</title>
<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 -->
@@ -44,9 +42,9 @@
<link rel="icon" sizes="16x16" type="image/png" href="novnc/app/images/icons/novnc-16x16.png">
<!-- Apple iOS Safari settings -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<!-- Home Screen Icons (favourites and bookmarks use the normal icons) -->
<link rel="apple-touch-icon" sizes="60x60" type="image/png" href="novnc/app/images/icons/novnc-60x60.png">
<link rel="apple-touch-icon" sizes="76x76" type="image/png" href="novnc/app/images/icons/novnc-76x76.png">
@@ -54,12 +52,7 @@
<link rel="apple-touch-icon" sizes="152x152" type="image/png" href="novnc/app/images/icons/novnc-152x152.png">
<!-- Stylesheets -->
<link rel="stylesheet" href="novnc/app/styles/base.css" />
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
<link rel="stylesheet" href="novnc/app/styles/base.css">
<!-- this is included as a normal file in order to catch script-loading errors as well -->
<script type="text/javascript" src="novnc/app/error-handler.js"></script>
@@ -68,17 +61,7 @@
<!-- promise polyfills promises for IE11 -->
<script src="novnc/vendor/promise.js"></script>
<!-- ES2015/ES6 modules polyfill -->
<script type="module">
window._noVNC_has_module_support = true;
</script>
<script>
window.addEventListener("load", function() {
if (window._noVNC_has_module_support) return;
var loader = document.createElement("script");
loader.src = "novnc/vendor/browser-es-module-loader/dist/browser-es-module-loader.js";
document.head.appendChild(loader);
});
</script>
<script nomodule src="novnc/vendor/browser-es-module-loader/dist/browser-es-module-loader.js"></script>
<!-- actual script modules -->
<script type="module" crossorigin="anonymous" src="novnc/app/ui.js"></script>
<!-- end scripts -->
@@ -102,54 +85,56 @@
<div class="noVNC_scroll">
<h1 class="noVNC_logo" translate="no"><span>no</span><br />VNC</h1>
<h1 class="noVNC_logo" translate="no"><span>no</span><br>VNC</h1>
<!-- Drag/Pan the viewport -->
<input type="image" alt="viewport drag" src="novnc/app/images/drag.svg"
<input type="image" alt="Drag" src="novnc/app/images/drag.svg"
id="noVNC_view_drag_button" class="noVNC_button noVNC_hidden"
title="Move/Drag Viewport" />
title="Move/Drag Viewport">
<!--noVNC Touch Device only buttons-->
<div id="noVNC_mobile_buttons">
<input type="image" alt="No mousebutton" src="novnc/app/images/mouse_none.svg"
id="noVNC_mouse_button0" class="noVNC_button"
title="Active Mouse Button"/>
title="Active Mouse Button">
<input type="image" alt="Left mousebutton" src="novnc/app/images/mouse_left.svg"
id="noVNC_mouse_button1" class="noVNC_button"
title="Active Mouse Button"/>
title="Active Mouse Button">
<input type="image" alt="Middle mousebutton" src="novnc/app/images/mouse_middle.svg"
id="noVNC_mouse_button2" class="noVNC_button"
title="Active Mouse Button"/>
title="Active Mouse Button">
<input type="image" alt="Right mousebutton" src="novnc/app/images/mouse_right.svg"
id="noVNC_mouse_button4" class="noVNC_button"
title="Active Mouse Button"/>
title="Active Mouse Button">
<input type="image" alt="Keyboard" src="novnc/app/images/keyboard.svg"
id="noVNC_keyboard_button" class="noVNC_button"
value="Keyboard" title="Show Keyboard" />
id="noVNC_keyboard_button" class="noVNC_button" title="Show Keyboard">
</div>
<!-- Extra manual keys -->
<div id="noVNC_extra_keys">
<input type="image" alt="Extra keys" src="novnc/app/images/toggleextrakeys.svg"
id="noVNC_toggle_extra_keys_button" class="noVNC_button"
title="Show Extra Keys"/>
title="Show Extra Keys">
<div class="noVNC_vcenter">
<div id="noVNC_modifiers" class="noVNC_panel">
<input type="image" alt="Ctrl" src="novnc/app/images/ctrl.svg"
id="noVNC_toggle_ctrl_button" class="noVNC_button"
title="Toggle Ctrl"/>
title="Toggle Ctrl">
<input type="image" alt="Alt" src="novnc/app/images/alt.svg"
id="noVNC_toggle_alt_button" class="noVNC_button"
title="Toggle Alt"/>
title="Toggle Alt">
<input type="image" alt="Windows" src="novnc/app/images/windows.svg"
id="noVNC_toggle_windows_button" class="noVNC_button"
title="Toggle Windows">
<input type="image" alt="Tab" src="novnc/app/images/tab.svg"
id="noVNC_send_tab_button" class="noVNC_button"
title="Send Tab"/>
title="Send Tab">
<input type="image" alt="Esc" src="novnc/app/images/esc.svg"
id="noVNC_send_esc_button" class="noVNC_button"
title="Send Escape"/>
title="Send Escape">
<input type="image" alt="Ctrl+Alt+Del" src="novnc/app/images/ctrlaltdel.svg"
id="noVNC_send_ctrl_alt_del_button" class="noVNC_button"
title="Send Ctrl-Alt-Del" />
title="Send Ctrl-Alt-Del">
</div>
</div>
</div>
@@ -157,43 +142,43 @@
<!-- Shutdown/Reboot -->
<input type="image" alt="Shutdown/Reboot" src="novnc/app/images/power.svg"
id="noVNC_power_button" class="noVNC_button"
title="Shutdown/Reboot..." />
title="Shutdown/Reboot...">
<div class="noVNC_vcenter">
<div id="noVNC_power" class="noVNC_panel">
<div class="noVNC_heading">
<img src="novnc/app/images/power.svg"> Power
<img alt="" src="novnc/app/images/power.svg"> Power
</div>
<input type="button" id="noVNC_shutdown_button" value="Shutdown" />
<input type="button" id="noVNC_reboot_button" value="Reboot" />
<input type="button" id="noVNC_reset_button" value="Reset" />
<input type="button" id="noVNC_shutdown_button" value="Shutdown">
<input type="button" id="noVNC_reboot_button" value="Reboot">
<input type="button" id="noVNC_reset_button" value="Reset">
</div>
</div>
<!-- Clipboard -->
<input type="image" alt="Clipboard" src="novnc/app/images/clipboard.svg"
id="noVNC_clipboard_button" class="noVNC_button"
title="Clipboard" />
title="Clipboard">
<div class="noVNC_vcenter">
<div id="noVNC_clipboard" class="noVNC_panel">
<div class="noVNC_heading">
<img src="novnc/app/images/clipboard.svg"> Clipboard
<img alt="" src="novnc/app/images/clipboard.svg"> Clipboard
</div>
<textarea id="noVNC_clipboard_text" rows=5></textarea>
<br />
<br>
<input id="noVNC_clipboard_clear_button" type="button"
value="Clear" class="noVNC_submit" />
value="Clear" class="noVNC_submit">
</div>
</div>
<!-- Toggle fullscreen -->
<input type="image" alt="Fullscreen" src="novnc/app/images/fullscreen.svg"
id="noVNC_fullscreen_button" class="noVNC_button noVNC_hidden"
title="Fullscreen" />
title="Fullscreen">
<!-- Settings -->
<input type="image" alt="Settings" src="novnc/app/images/settings.svg"
id="noVNC_settings_button" class="noVNC_button"
title="Settings" />
title="Settings">
<div class="noVNC_vcenter">
<div id="noVNC_settings" class="noVNC_panel">
<ul>
@@ -201,14 +186,14 @@
<img src="novnc/app/images/settings.svg"> Settings
</li>
<li>
<label><input id="noVNC_setting_shared" type="checkbox" /> Shared Mode</label>
<label><input id="noVNC_setting_shared" type="checkbox"> Shared Mode</label>
</li>
<li>
<label><input id="noVNC_setting_view_only" type="checkbox" /> View Only</label>
<label><input id="noVNC_setting_view_only" type="checkbox"> View Only</label>
</li>
<li><hr></li>
<li>
<label><input id="noVNC_setting_view_clip" type="checkbox" /> Clip to Window</label>
<label><input id="noVNC_setting_view_clip" type="checkbox"> Clip to Window</label>
</li>
<li>
<label for="noVNC_setting_resize">Scaling Mode:</label>
@@ -224,35 +209,39 @@
<div><ul>
<li>
<label for="noVNC_setting_repeaterID">Repeater ID:</label>
<input id="noVNC_setting_repeaterID" type="input" value="" />
<input id="noVNC_setting_repeaterID" type="text" value="">
</li>
<li>
<div class="noVNC_expander">WebSocket</div>
<div><ul>
<li>
<label><input id="noVNC_setting_encrypt" type="checkbox" /> Encrypt</label>
<label><input id="noVNC_setting_encrypt" type="checkbox"> Encrypt</label>
</li>
<li>
<label for="noVNC_setting_host">Host:</label>
<input id="noVNC_setting_host" />
<input id="noVNC_setting_host">
</li>
<li>
<label for="noVNC_setting_port">Port:</label>
<input id="noVNC_setting_port" type="number" />
<input id="noVNC_setting_port" type="number">
</li>
<li>
<label for="noVNC_setting_path">Path:</label>
<input id="noVNC_setting_path" type="input" value="websockify" />
<input id="noVNC_setting_path" type="text" value="websockify">
</li>
</ul></div>
</li>
<li><hr></li>
<li>
<label><input id="noVNC_setting_reconnect" type="checkbox" /> Automatic Reconnect</label>
<label><input id="noVNC_setting_reconnect" type="checkbox"> Automatic Reconnect</label>
</li>
<li>
<label for="noVNC_setting_reconnect_delay">Reconnect Delay (ms):</label>
<input id="noVNC_setting_reconnect_delay" type="number" />
<input id="noVNC_setting_reconnect_delay" type="number">
</li>
<li><hr></li>
<li>
<label><input id="noVNC_setting_show_dot" type="checkbox"> Show Dot when No Cursor</label>
</li>
<li><hr></li>
<!-- Logging selection dropdown -->
@@ -264,6 +253,11 @@
</li>
</ul></div>
</li>
<li class="noVNC_version_separator"><hr></li>
<li class="noVNC_version_wrapper">
<span>Version:</span>
<span class="noVNC_version"></span>
</li>
</ul>
</div>
</div>
@@ -271,7 +265,7 @@
<!-- Connection Controls -->
<input type="image" alt="Disconnect" src="novnc/app/images/disconnect.svg"
id="noVNC_disconnect_button" class="noVNC_button"
title="Disconnect" />
title="Disconnect">
</div>
</div>
@@ -288,7 +282,7 @@
<div id="noVNC_connect_dlg">
<div class="noVNC_logo" translate="no"><span>no</span>VNC</div>
<div id="noVNC_connect_button"><div>
<img src="novnc/app/images/connect.svg"> Connect
<img alt="" src="novnc/app/images/connect.svg"> Connect
</div></div>
</div>
</div>
@@ -299,10 +293,10 @@
<ul>
<li>
<label>Password:</label>
<input id="noVNC_password_input" type="password" />
<input id="noVNC_password_input" type="password">
</li>
<li>
<input id="noVNC_password_button" type="submit" value="Send Password" class="noVNC_submit" />
<input id="noVNC_password_button" type="submit" value="Send Password" class="noVNC_submit">
</li>
</ul>
</form></div>
@@ -312,7 +306,7 @@
<div id="noVNC_transition">
<div id="noVNC_transition_text"></div>
<div>
<input type="button" id="noVNC_cancel_reconnect_button" value="Cancel" class="noVNC_submit" />
<input type="button" id="noVNC_cancel_reconnect_button" value="Cancel" class="noVNC_submit">
</div>
<div class="noVNC_spinner"></div>
</div>
@@ -324,8 +318,7 @@
on-screen keyboard. Let's hope Chrome implements the ime-mode
style for example -->
<textarea id="noVNC_keyboardinput" autocapitalize="off"
autocorrect="off" autocomplete="off" spellcheck="false"
mozactionhint="Enter" tabindex="-1"></textarea>
autocomplete="off" spellcheck="false" tabindex="-1"></textarea>
</div>
<audio id="noVNC_bell">