Merge pull request #1 from bitctrl/feature/clipboard_updated_notification
Update notification when clipboard is updated
This commit is contained in:
commit
af842c737d
|
|
@ -9,6 +9,9 @@ on:
|
||||||
jobs:
|
jobs:
|
||||||
npm:
|
npm:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
contents: read
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- run: |
|
- run: |
|
||||||
|
|
@ -18,6 +21,9 @@ jobs:
|
||||||
if: github.event_name != 'release'
|
if: github.event_name != 'release'
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
|
# Node 24 is needed to get npm > 11.5.1, which is a requirement for
|
||||||
|
# OIDC auth.
|
||||||
|
node-version: 24
|
||||||
# Needs to be explicitly specified for auth to work
|
# Needs to be explicitly specified for auth to work
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: 'https://registry.npmjs.org'
|
||||||
- run: npm install
|
- run: npm install
|
||||||
|
|
@ -26,22 +32,16 @@ jobs:
|
||||||
name: npm
|
name: npm
|
||||||
path: lib
|
path: lib
|
||||||
- run: npm publish --access public
|
- run: npm publish --access public
|
||||||
env:
|
|
||||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
|
||||||
if: |
|
if: |
|
||||||
github.repository == 'novnc/noVNC' &&
|
github.repository == 'novnc/noVNC' &&
|
||||||
github.event_name == 'release' &&
|
github.event_name == 'release' &&
|
||||||
!github.event.release.prerelease
|
!github.event.release.prerelease
|
||||||
- run: npm publish --access public --tag beta
|
- run: npm publish --access public --tag beta
|
||||||
env:
|
|
||||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
|
||||||
if: |
|
if: |
|
||||||
github.repository == 'novnc/noVNC' &&
|
github.repository == 'novnc/noVNC' &&
|
||||||
github.event_name == 'release' &&
|
github.event_name == 'release' &&
|
||||||
github.event.release.prerelease
|
github.event.release.prerelease
|
||||||
- run: npm publish --access public --tag dev
|
- run: npm publish --access public --tag dev
|
||||||
env:
|
|
||||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
|
||||||
if: |
|
if: |
|
||||||
github.repository == 'novnc/noVNC' &&
|
github.repository == 'novnc/noVNC' &&
|
||||||
github.event_name == 'push' &&
|
github.event_name == 'push' &&
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
{
|
||||||
|
"Running without HTTPS is not recommended, crashes or other issues are likely.": "HTTPS nélkül futtatni nem ajánlott, összeomlások vagy más problémák várhatók.",
|
||||||
|
"Connecting...": "Kapcsolódás...",
|
||||||
|
"Disconnecting...": "Kapcsolat bontása...",
|
||||||
|
"Reconnecting...": "Újrakapcsolódás...",
|
||||||
|
"Internal error": "Belső hiba",
|
||||||
|
"Failed to connect to server: ": "Nem sikerült csatlakozni a szerverhez: ",
|
||||||
|
"Connected (encrypted) to ": "Kapcsolódva (titkosítva) ehhez: ",
|
||||||
|
"Connected (unencrypted) to ": "Kapcsolódva (titkosítatlanul) ehhez: ",
|
||||||
|
"Something went wrong, connection is closed": "Valami hiba történt, a kapcsolat lezárult",
|
||||||
|
"Failed to connect to server": "Nem sikerült csatlakozni a szerverhez",
|
||||||
|
"Disconnected": "Kapcsolat bontva",
|
||||||
|
"New connection has been rejected with reason: ": "Az új kapcsolat elutasítva, indok: ",
|
||||||
|
"New connection has been rejected": "Az új kapcsolat elutasítva",
|
||||||
|
"Credentials are required": "Hitelesítő adatok szükségesek",
|
||||||
|
"noVNC encountered an error:": "A noVNC hibát észlelt:",
|
||||||
|
"Hide/Show the control bar": "Vezérlősáv elrejtése/megjelenítése",
|
||||||
|
"Drag": "Húzás",
|
||||||
|
"Move/Drag viewport": "Nézet mozgatása/húzása",
|
||||||
|
"Keyboard": "Billentyűzet",
|
||||||
|
"Show keyboard": "Billentyűzet megjelenítése",
|
||||||
|
"Extra keys": "Extra billentyűk",
|
||||||
|
"Show extra keys": "Extra billentyűk megjelenítése",
|
||||||
|
"Ctrl": "Ctrl",
|
||||||
|
"Toggle Ctrl": "Ctrl lenyomása/felengedése",
|
||||||
|
"Alt": "Alt",
|
||||||
|
"Toggle Alt": "Alt lenyomása/felengedése",
|
||||||
|
"Toggle Windows": "Windows lenyomása/felengedése",
|
||||||
|
"Windows": "Windows",
|
||||||
|
"Send Tab": "Tab küldése",
|
||||||
|
"Tab": "Tab",
|
||||||
|
"Esc": "Esc",
|
||||||
|
"Send Escape": "Escape küldése",
|
||||||
|
"Ctrl+Alt+Del": "Ctrl+Alt+Del",
|
||||||
|
"Send Ctrl-Alt-Del": "Ctrl-Alt-Del küldése",
|
||||||
|
"Shutdown/Reboot": "Leállítás/Újraindítás",
|
||||||
|
"Shutdown/Reboot...": "Leállítás/Újraindítás...",
|
||||||
|
"Power": "Bekapcsolás",
|
||||||
|
"Shutdown": "Leállítás",
|
||||||
|
"Reboot": "Újraindítás",
|
||||||
|
"Reset": "Reset",
|
||||||
|
"Clipboard": "Vágólap",
|
||||||
|
"Edit clipboard content in the textarea below.": "Itt tudod módosítani a vágólap tartalmát.",
|
||||||
|
"Full screen": "Teljes képernyő",
|
||||||
|
"Settings": "Beállítások",
|
||||||
|
"Shared mode": "Megosztott mód",
|
||||||
|
"View only": "Csak megtekintés",
|
||||||
|
"Clip to window": "Ablakhoz igazítás",
|
||||||
|
"Scaling mode:": "Méretezési mód:",
|
||||||
|
"None": "Nincs",
|
||||||
|
"Local scaling": "Helyi méretezés",
|
||||||
|
"Remote resizing": "Távoli átméretezés",
|
||||||
|
"Advanced": "Speciális",
|
||||||
|
"Quality:": "Minőség:",
|
||||||
|
"Compression level:": "Tömörítési szint:",
|
||||||
|
"Repeater ID:": "Ismétlő azonosító:",
|
||||||
|
"WebSocket": "WebSocket",
|
||||||
|
"Encrypt": "Titkosítás",
|
||||||
|
"Host:": "Hoszt:",
|
||||||
|
"Port:": "Port:",
|
||||||
|
"Path:": "Útvonal:",
|
||||||
|
"Automatic reconnect": "Automatikus újracsatlakozás",
|
||||||
|
"Reconnect delay (ms):": "Újracsatlakozás késleltetése (ms):",
|
||||||
|
"Show dot when no cursor": "Kurzor hiányában pont mutatása",
|
||||||
|
"Logging:": "Naplózás:",
|
||||||
|
"Version:": "Verzió:",
|
||||||
|
"Disconnect": "Kapcsolat bontása",
|
||||||
|
"Connect": "Csatlakozás",
|
||||||
|
"Server identity": "Szerver azonosító",
|
||||||
|
"The server has provided the following identifying information:": "A szerver a következő azonosító információt adta meg:",
|
||||||
|
"Fingerprint:": "Ujjlenyomat:",
|
||||||
|
"Please verify that the information is correct and press \"Approve\". Otherwise press \"Reject\".": "Ellenőrizze, hogy az információ helyes-e és nyomja meg a \"Jóváhagyás\" gombot. Ellenkező esetben nyomja meg az \"Elutasítás\" gombot.",
|
||||||
|
"Approve": "Jóváhagyás",
|
||||||
|
"Reject": "Elutasítás",
|
||||||
|
"Credentials": "Hitelesítő adatok",
|
||||||
|
"Username:": "Felhasználónév:",
|
||||||
|
"Password:": "Jelszó:",
|
||||||
|
"Send credentials": "Hitelesítő adatok küldése",
|
||||||
|
"Cancel": "Mégse"
|
||||||
|
}
|
||||||
|
|
@ -902,30 +902,6 @@ html {
|
||||||
#noVNC_bell {
|
#noVNC_bell {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
/* ----------------------------------------
|
|
||||||
*
|
|
||||||
* Styles for Showing the Clipboard updated
|
|
||||||
*/
|
|
||||||
.notification {
|
|
||||||
position: fixed;
|
|
||||||
top: 20px;
|
|
||||||
right: 20px;
|
|
||||||
background-color: grey;
|
|
||||||
color: white;
|
|
||||||
padding: 10px 20px;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.2);
|
|
||||||
font-family: sans-serif;
|
|
||||||
font-size: 16px;
|
|
||||||
opacity: 1;
|
|
||||||
transition: opacity 0.5s ease;
|
|
||||||
z-index: 9999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-out {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* ----------------------------------------
|
/* ----------------------------------------
|
||||||
* Media sizing
|
* Media sizing
|
||||||
|
|
|
||||||
85
app/ui.js
85
app/ui.js
|
|
@ -9,7 +9,7 @@
|
||||||
import * as Log from '../core/util/logging.js';
|
import * as Log from '../core/util/logging.js';
|
||||||
import _, { l10n } from './localization.js';
|
import _, { l10n } from './localization.js';
|
||||||
import { isTouchDevice, isMac, isIOS, isAndroid, isChromeOS, isSafari,
|
import { isTouchDevice, isMac, isIOS, isAndroid, isChromeOS, isSafari,
|
||||||
hasScrollbarGutter, dragThreshold }
|
hasScrollbarGutter, dragThreshold, browserAsyncClipboardSupport }
|
||||||
from '../core/util/browser.js';
|
from '../core/util/browser.js';
|
||||||
import { setCapture, getPointerEvent } from '../core/util/events.js';
|
import { setCapture, getPointerEvent } from '../core/util/events.js';
|
||||||
import KeyTable from "../core/input/keysym.js";
|
import KeyTable from "../core/input/keysym.js";
|
||||||
|
|
@ -20,7 +20,7 @@ import * as WebUtil from "./webutil.js";
|
||||||
|
|
||||||
const PAGE_TITLE = "noVNC";
|
const PAGE_TITLE = "noVNC";
|
||||||
|
|
||||||
const LINGUAS = ["cs", "de", "el", "es", "fr", "it", "ja", "ko", "nl", "pl", "pt_BR", "ru", "sv", "tr", "zh_CN", "zh_TW"];
|
const LINGUAS = ["cs", "de", "el", "es", "fr", "hr", "hu", "it", "ja", "ko", "nl", "pl", "pt_BR", "ru", "sv", "tr", "zh_CN", "zh_TW"];
|
||||||
|
|
||||||
const UI = {
|
const UI = {
|
||||||
|
|
||||||
|
|
@ -185,6 +185,7 @@ const UI = {
|
||||||
UI.initSetting('bell', 'on');
|
UI.initSetting('bell', 'on');
|
||||||
UI.initSetting('view_only', false);
|
UI.initSetting('view_only', false);
|
||||||
UI.initSetting('show_dot', false);
|
UI.initSetting('show_dot', false);
|
||||||
|
UI.initSetting('notify_clipboard_received', false);
|
||||||
UI.initSetting('path', 'websockify');
|
UI.initSetting('path', 'websockify');
|
||||||
UI.initSetting('repeaterID', '');
|
UI.initSetting('repeaterID', '');
|
||||||
UI.initSetting('reconnect', false);
|
UI.initSetting('reconnect', false);
|
||||||
|
|
@ -371,6 +372,8 @@ const UI = {
|
||||||
UI.addSettingChangeHandler('view_only', UI.updateViewOnly);
|
UI.addSettingChangeHandler('view_only', UI.updateViewOnly);
|
||||||
UI.addSettingChangeHandler('show_dot');
|
UI.addSettingChangeHandler('show_dot');
|
||||||
UI.addSettingChangeHandler('show_dot', UI.updateShowDotCursor);
|
UI.addSettingChangeHandler('show_dot', UI.updateShowDotCursor);
|
||||||
|
UI.addSettingChangeHandler('notify_clipboard_received');
|
||||||
|
UI.addSettingChangeHandler('notify_clipboard_received', UI.updateNotifyClipboardReceived);
|
||||||
UI.addSettingChangeHandler('host');
|
UI.addSettingChangeHandler('host');
|
||||||
UI.addSettingChangeHandler('port');
|
UI.addSettingChangeHandler('port');
|
||||||
UI.addSettingChangeHandler('path');
|
UI.addSettingChangeHandler('path');
|
||||||
|
|
@ -892,6 +895,7 @@ const UI = {
|
||||||
UI.updateSetting('logging');
|
UI.updateSetting('logging');
|
||||||
UI.updateSetting('reconnect');
|
UI.updateSetting('reconnect');
|
||||||
UI.updateSetting('reconnect_delay');
|
UI.updateSetting('reconnect_delay');
|
||||||
|
UI.updateSetting('notify_clipboard_received');
|
||||||
|
|
||||||
document.getElementById('noVNC_settings')
|
document.getElementById('noVNC_settings')
|
||||||
.classList.add("noVNC_open");
|
.classList.add("noVNC_open");
|
||||||
|
|
@ -993,25 +997,17 @@ const UI = {
|
||||||
UI.openClipboardPanel();
|
UI.openClipboardPanel();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
showCopiedNotification() {
|
|
||||||
// This is responsible for showing the notification when text is updated in the clipboard.
|
|
||||||
const notificationBox = document.createElement("div");
|
|
||||||
notificationBox.className = "notification";
|
|
||||||
notificationBox.innerText = "Clipboard updated";
|
|
||||||
document.body.appendChild(notificationBox);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
notifyClipboardReceived() {
|
||||||
notificationBox.classList.add("fade-out");
|
// When enabled with setting 'notify_clipboard_received', shows
|
||||||
setTimeout(() => {
|
// notification when a 'clipboard-received'-event is fired by
|
||||||
notificationBox.remove();
|
// the RFB instance.
|
||||||
}, 500); // Wait for fade-out transition
|
UI.showStatus(_('Clipboard updated'), 3000);
|
||||||
}, 3000); // Display for 3 seconds
|
|
||||||
},
|
},
|
||||||
|
|
||||||
clipboardReceive(e) {
|
clipboardReceive(e) {
|
||||||
Log.Debug(">> UI.clipboardReceive: " + e.detail.text.substr(0, 40) + "...");
|
Log.Debug(">> UI.clipboardReceive: " + e.detail.text.substr(0, 40) + "...");
|
||||||
document.getElementById('noVNC_clipboard_text').value = e.detail.text;
|
document.getElementById('noVNC_clipboard_text').value = e.detail.text;
|
||||||
UI.showCopiedNotification();
|
|
||||||
Log.Debug("<< UI.clipboardReceive");
|
Log.Debug("<< UI.clipboardReceive");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -1118,6 +1114,8 @@ const UI = {
|
||||||
UI.rfb.showDotCursor = UI.getSetting('show_dot');
|
UI.rfb.showDotCursor = UI.getSetting('show_dot');
|
||||||
|
|
||||||
UI.updateViewOnly(); // requires UI.rfb
|
UI.updateViewOnly(); // requires UI.rfb
|
||||||
|
UI.updateClipboard();
|
||||||
|
UI.updateNotifyClipboardReceived();
|
||||||
},
|
},
|
||||||
|
|
||||||
disconnect() {
|
disconnect() {
|
||||||
|
|
@ -1169,6 +1167,8 @@ const UI = {
|
||||||
UI.showStatus(msg);
|
UI.showStatus(msg);
|
||||||
UI.updateVisualState('connected');
|
UI.updateVisualState('connected');
|
||||||
|
|
||||||
|
UI.updateBeforeUnload();
|
||||||
|
|
||||||
// Do this last because it can only be used on rendered elements
|
// Do this last because it can only be used on rendered elements
|
||||||
UI.rfb.focus();
|
UI.rfb.focus();
|
||||||
},
|
},
|
||||||
|
|
@ -1205,6 +1205,8 @@ const UI = {
|
||||||
UI.showStatus(_("Disconnected"), 'normal');
|
UI.showStatus(_("Disconnected"), 'normal');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UI.updateBeforeUnload();
|
||||||
|
|
||||||
document.title = PAGE_TITLE;
|
document.title = PAGE_TITLE;
|
||||||
|
|
||||||
UI.openControlbar();
|
UI.openControlbar();
|
||||||
|
|
@ -1225,6 +1227,24 @@ const UI = {
|
||||||
UI.showStatus(msg, 'error');
|
UI.showStatus(msg, 'error');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
handleBeforeUnload(e) {
|
||||||
|
// Trigger a "Leave site?" warning prompt before closing the
|
||||||
|
// page. Modern browsers (Oct 2025) accept either (or both)
|
||||||
|
// preventDefault() or a nonempty returnValue, though the latter is
|
||||||
|
// considered legacy. The custom string is ignored by modern browsers,
|
||||||
|
// which display a native message, but older browsers will show it.
|
||||||
|
e.preventDefault();
|
||||||
|
e.returnValue = _("Are you sure you want to disconnect the session?");
|
||||||
|
},
|
||||||
|
|
||||||
|
updateBeforeUnload() {
|
||||||
|
// Remove first to avoid adding duplicates
|
||||||
|
window.removeEventListener("beforeunload", UI.handleBeforeUnload);
|
||||||
|
if (!UI.rfb?.viewOnly && UI.connected) {
|
||||||
|
window.addEventListener("beforeunload", UI.handleBeforeUnload);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/* ------^-------
|
/* ------^-------
|
||||||
* /CONNECTION
|
* /CONNECTION
|
||||||
* ==============
|
* ==============
|
||||||
|
|
@ -1751,6 +1771,8 @@ const UI = {
|
||||||
if (!UI.rfb) return;
|
if (!UI.rfb) return;
|
||||||
UI.rfb.viewOnly = UI.getSetting('view_only');
|
UI.rfb.viewOnly = UI.getSetting('view_only');
|
||||||
|
|
||||||
|
UI.updateBeforeUnload();
|
||||||
|
|
||||||
// Hide input related buttons in view only mode
|
// Hide input related buttons in view only mode
|
||||||
if (UI.rfb.viewOnly) {
|
if (UI.rfb.viewOnly) {
|
||||||
document.getElementById('noVNC_keyboard_button')
|
document.getElementById('noVNC_keyboard_button')
|
||||||
|
|
@ -1769,11 +1791,44 @@ const UI = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateClipboard() {
|
||||||
|
browserAsyncClipboardSupport()
|
||||||
|
.then((support) => {
|
||||||
|
if (support === 'unsupported') {
|
||||||
|
// Use fallback clipboard panel
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (support === 'denied' || support === 'available') {
|
||||||
|
UI.closeClipboardPanel();
|
||||||
|
document.getElementById('noVNC_clipboard_button')
|
||||||
|
.classList.add('noVNC_hidden');
|
||||||
|
document.getElementById('noVNC_clipboard_button')
|
||||||
|
.removeEventListener('click', UI.toggleClipboardPanel);
|
||||||
|
document.getElementById('noVNC_clipboard_text')
|
||||||
|
.removeEventListener('change', UI.clipboardSend);
|
||||||
|
if (UI.rfb) {
|
||||||
|
UI.rfb.removeEventListener('clipboard', UI.clipboardReceive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
// Treat as unsupported
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
updateShowDotCursor() {
|
updateShowDotCursor() {
|
||||||
if (!UI.rfb) return;
|
if (!UI.rfb) return;
|
||||||
UI.rfb.showDotCursor = UI.getSetting('show_dot');
|
UI.rfb.showDotCursor = UI.getSetting('show_dot');
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateNotifyClipboardReceived() {
|
||||||
|
if (!UI.rfb) return;
|
||||||
|
UI.rfb.removeEventListener('clipboardreceived', UI.notifyClipboardReceived);
|
||||||
|
if (UI.getSetting('notify_clipboard_received')) {
|
||||||
|
UI.rfb.addEventListener('clipboardreceived', UI.notifyClipboardReceived);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
updateLogging() {
|
updateLogging() {
|
||||||
WebUtil.initLogging(UI.getSetting('logging'));
|
WebUtil.initLogging(UI.getSetting('logging'));
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,72 @@
|
||||||
|
/*
|
||||||
|
* noVNC: HTML5 VNC client
|
||||||
|
* Copyright (c) 2025 The noVNC authors
|
||||||
|
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as Log from './util/logging.js';
|
||||||
|
import { browserAsyncClipboardSupport } from './util/browser.js';
|
||||||
|
|
||||||
|
export default class AsyncClipboard {
|
||||||
|
constructor(target) {
|
||||||
|
this._target = target || null;
|
||||||
|
|
||||||
|
this._isAvailable = null;
|
||||||
|
|
||||||
|
this._eventHandlers = {
|
||||||
|
'focus': this._handleFocus.bind(this),
|
||||||
|
};
|
||||||
|
|
||||||
|
// ===== EVENT HANDLERS =====
|
||||||
|
|
||||||
|
this.onpaste = () => {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== PRIVATE METHODS =====
|
||||||
|
|
||||||
|
async _ensureAvailable() {
|
||||||
|
if (this._isAvailable !== null) return this._isAvailable;
|
||||||
|
try {
|
||||||
|
const status = await browserAsyncClipboardSupport();
|
||||||
|
this._isAvailable = (status === 'available');
|
||||||
|
} catch {
|
||||||
|
this._isAvailable = false;
|
||||||
|
}
|
||||||
|
return this._isAvailable;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _handleFocus(event) {
|
||||||
|
if (!(await this._ensureAvailable())) return;
|
||||||
|
try {
|
||||||
|
const text = await navigator.clipboard.readText();
|
||||||
|
this.onpaste(text);
|
||||||
|
} catch (error) {
|
||||||
|
Log.Error("Clipboard read failed: ", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== PUBLIC METHODS =====
|
||||||
|
|
||||||
|
writeClipboard(text) {
|
||||||
|
// Can lazily check cached availability
|
||||||
|
if (!this._isAvailable) return false;
|
||||||
|
navigator.clipboard.writeText(text)
|
||||||
|
.catch(error => Log.Error("Clipboard write failed: ", error));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
grab() {
|
||||||
|
if (!this._target) return;
|
||||||
|
this._ensureAvailable()
|
||||||
|
.then((isAvailable) => {
|
||||||
|
if (isAvailable) {
|
||||||
|
this._target.addEventListener('focus', this._eventHandlers.focus);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
ungrab() {
|
||||||
|
if (!this._target) return;
|
||||||
|
this._target.removeEventListener('focus', this._eventHandlers.focus);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -119,18 +119,33 @@ export default class JPEGDecoder {
|
||||||
let extra = 0;
|
let extra = 0;
|
||||||
if (type === 0xDA) {
|
if (type === 0xDA) {
|
||||||
// start of scan
|
// start of scan
|
||||||
extra += 2;
|
if (sock.rQwait("JPEG", length-2 + 2, 4)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let len = sock.rQlen();
|
||||||
|
let data = sock.rQpeekBytes(len, false);
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (sock.rQwait("JPEG", length-2+extra, 4)) {
|
let idx = data.indexOf(0xFF, length-2+extra);
|
||||||
|
if (idx === -1) {
|
||||||
|
sock.rQwait("JPEG", Infinity, 4);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
let data = sock.rQpeekBytes(length-2+extra, false);
|
|
||||||
if (data.at(-2) === 0xFF && data.at(-1) !== 0x00 &&
|
if (idx === len-1) {
|
||||||
!(data.at(-1) >= 0xD0 && data.at(-1) <= 0xD7)) {
|
sock.rQwait("JPEG", Infinity, 4);
|
||||||
extra -= 2;
|
return null;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
extra++;
|
|
||||||
|
if (data.at(idx+1) === 0x00 ||
|
||||||
|
(data.at(idx+1) >= 0xD0 && data.at(idx+1) <= 0xD7)) {
|
||||||
|
extra = idx+2 - (length-2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
extra = idx - (length-2);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -521,6 +521,9 @@ export default class Display {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.drawImage(a.img, a.x, a.y);
|
this.drawImage(a.img, a.x, a.y);
|
||||||
|
// This helps the browser free the memory right
|
||||||
|
// away, rather than ballooning
|
||||||
|
a.img.src = "";
|
||||||
} else {
|
} else {
|
||||||
a.img._noVNCDisplay = this;
|
a.img._noVNCDisplay = this;
|
||||||
a.img.addEventListener('load', this._resumeRenderQ);
|
a.img.addEventListener('load', this._resumeRenderQ);
|
||||||
|
|
|
||||||
31
core/rfb.js
31
core/rfb.js
|
|
@ -15,6 +15,7 @@ import { clientToElement } from './util/element.js';
|
||||||
import { setCapture } from './util/events.js';
|
import { setCapture } from './util/events.js';
|
||||||
import EventTargetMixin from './util/eventtarget.js';
|
import EventTargetMixin from './util/eventtarget.js';
|
||||||
import Display from "./display.js";
|
import Display from "./display.js";
|
||||||
|
import AsyncClipboard from "./clipboard.js";
|
||||||
import Inflator from "./inflator.js";
|
import Inflator from "./inflator.js";
|
||||||
import Deflator from "./deflator.js";
|
import Deflator from "./deflator.js";
|
||||||
import Keyboard from "./input/keyboard.js";
|
import Keyboard from "./input/keyboard.js";
|
||||||
|
|
@ -164,6 +165,7 @@ export default class RFB extends EventTargetMixin {
|
||||||
this._sock = null; // Websock object
|
this._sock = null; // Websock object
|
||||||
this._display = null; // Display object
|
this._display = null; // Display object
|
||||||
this._flushing = false; // Display flushing state
|
this._flushing = false; // Display flushing state
|
||||||
|
this._asyncClipboard = null; // Async clipboard object
|
||||||
this._keyboard = null; // Keyboard input handler object
|
this._keyboard = null; // Keyboard input handler object
|
||||||
this._gestures = null; // Gesture input handler object
|
this._gestures = null; // Gesture input handler object
|
||||||
this._resizeObserver = null; // Resize observer object
|
this._resizeObserver = null; // Resize observer object
|
||||||
|
|
@ -266,6 +268,9 @@ export default class RFB extends EventTargetMixin {
|
||||||
throw exc;
|
throw exc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this._asyncClipboard = new AsyncClipboard(this._canvas);
|
||||||
|
this._asyncClipboard.onpaste = this.clipboardPasteFrom.bind(this);
|
||||||
|
|
||||||
this._keyboard = new Keyboard(this._canvas);
|
this._keyboard = new Keyboard(this._canvas);
|
||||||
this._keyboard.onkeyevent = this._handleKeyEvent.bind(this);
|
this._keyboard.onkeyevent = this._handleKeyEvent.bind(this);
|
||||||
this._remoteCapsLock = null; // Null indicates unknown or irrelevant
|
this._remoteCapsLock = null; // Null indicates unknown or irrelevant
|
||||||
|
|
@ -315,8 +320,10 @@ export default class RFB extends EventTargetMixin {
|
||||||
this._rfbConnectionState === "connected") {
|
this._rfbConnectionState === "connected") {
|
||||||
if (viewOnly) {
|
if (viewOnly) {
|
||||||
this._keyboard.ungrab();
|
this._keyboard.ungrab();
|
||||||
|
this._asyncClipboard.ungrab();
|
||||||
} else {
|
} else {
|
||||||
this._keyboard.grab();
|
this._keyboard.grab();
|
||||||
|
this._asyncClipboard.grab();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2208,7 +2215,10 @@ export default class RFB extends EventTargetMixin {
|
||||||
this._setDesktopName(name);
|
this._setDesktopName(name);
|
||||||
this._resize(width, height);
|
this._resize(width, height);
|
||||||
|
|
||||||
if (!this._viewOnly) { this._keyboard.grab(); }
|
if (!this._viewOnly) {
|
||||||
|
this._keyboard.grab();
|
||||||
|
this._asyncClipboard.grab();
|
||||||
|
}
|
||||||
|
|
||||||
this._fbDepth = 24;
|
this._fbDepth = 24;
|
||||||
|
|
||||||
|
|
@ -2323,6 +2333,17 @@ export default class RFB extends EventTargetMixin {
|
||||||
return this._fail("Unexpected SetColorMapEntries message");
|
return this._fail("Unexpected SetColorMapEntries message");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_writeClipboard(text) {
|
||||||
|
if (this._viewOnly) return;
|
||||||
|
if (!(this._asyncClipboard.writeClipboard(text))) {
|
||||||
|
// Fallback clipboard
|
||||||
|
this.dispatchEvent(
|
||||||
|
new CustomEvent("clipboard", {detail: {text: text}})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.dispatchEvent(new CustomEvent("clipboardreceived"));
|
||||||
|
}
|
||||||
|
|
||||||
_handleServerCutText() {
|
_handleServerCutText() {
|
||||||
Log.Debug("ServerCutText");
|
Log.Debug("ServerCutText");
|
||||||
|
|
||||||
|
|
@ -2342,9 +2363,7 @@ export default class RFB extends EventTargetMixin {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.dispatchEvent(new CustomEvent(
|
this._writeClipboard(text);
|
||||||
"clipboard",
|
|
||||||
{ detail: { text: text } }));
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
//Extended msg.
|
//Extended msg.
|
||||||
|
|
@ -2480,9 +2499,7 @@ export default class RFB extends EventTargetMixin {
|
||||||
|
|
||||||
textData = textData.replaceAll("\r\n", "\n");
|
textData = textData.replaceAll("\r\n", "\n");
|
||||||
|
|
||||||
this.dispatchEvent(new CustomEvent(
|
this._writeClipboard(textData);
|
||||||
"clipboard",
|
|
||||||
{ detail: { text: textData } }));
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return this._fail("Unexpected action in extended clipboard message: " + actions);
|
return this._fail("Unexpected action in extended clipboard message: " + actions);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,39 @@
|
||||||
import * as Log from './logging.js';
|
import * as Log from './logging.js';
|
||||||
import Base64 from '../base64.js';
|
import Base64 from '../base64.js';
|
||||||
|
|
||||||
|
// Async clipboard detection
|
||||||
|
|
||||||
|
/* Evaluates if there is browser support for the async clipboard API and
|
||||||
|
* relevant clipboard permissions. Returns 'unsupported' if permission states
|
||||||
|
* cannot be resolved. On the other hand, detecting 'granted' or 'prompt'
|
||||||
|
* permission states for both read and write indicates full API support with no
|
||||||
|
* imposed native browser paste prompt. Conversely, detecting 'denied' indicates
|
||||||
|
* the user elected to disable clipboard.
|
||||||
|
*/
|
||||||
|
export async function browserAsyncClipboardSupport() {
|
||||||
|
if (!(navigator?.permissions?.query &&
|
||||||
|
navigator?.clipboard?.writeText &&
|
||||||
|
navigator?.clipboard?.readText)) {
|
||||||
|
return 'unsupported';
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const writePerm = await navigator.permissions.query(
|
||||||
|
{name: "clipboard-write", allowWithoutGesture: true});
|
||||||
|
const readPerm = await navigator.permissions.query(
|
||||||
|
{name: "clipboard-read", allowWithoutGesture: false});
|
||||||
|
if (writePerm.state === "denied" || readPerm.state === "denied") {
|
||||||
|
return 'denied';
|
||||||
|
}
|
||||||
|
if ((writePerm.state === "granted" || writePerm.state === "prompt") &&
|
||||||
|
(readPerm.state === "granted" || readPerm.state === "prompt")) {
|
||||||
|
return 'available';
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
return 'unsupported';
|
||||||
|
}
|
||||||
|
return 'unsupported';
|
||||||
|
}
|
||||||
|
|
||||||
// Touch detection
|
// Touch detection
|
||||||
export let isTouchDevice = ('ontouchstart' in document.documentElement) ||
|
export let isTouchDevice = ('ontouchstart' in document.documentElement) ||
|
||||||
// required for Chrome debugger
|
// required for Chrome debugger
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,10 @@ export default class Websock {
|
||||||
return res >>> 0;
|
return res >>> 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rQlen() {
|
||||||
|
return this._rQlen - this._rQi;
|
||||||
|
}
|
||||||
|
|
||||||
rQshiftStr(len) {
|
rQshiftStr(len) {
|
||||||
let str = "";
|
let str = "";
|
||||||
// Handle large arrays in steps to avoid long strings on the stack
|
// Handle large arrays in steps to avoid long strings on the stack
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ keysym values.
|
||||||
* __Display__ (core/display.js): Efficient 2D rendering abstraction
|
* __Display__ (core/display.js): Efficient 2D rendering abstraction
|
||||||
layered on the HTML5 canvas element.
|
layered on the HTML5 canvas element.
|
||||||
|
|
||||||
|
* __Clipboard__ (core/clipboard.js): Clipboard event handler.
|
||||||
|
|
||||||
* __Websock__ (core/websock.js): Websock client from websockify
|
* __Websock__ (core/websock.js): Websock client from websockify
|
||||||
with transparent binary data support.
|
with transparent binary data support.
|
||||||
[Websock API](https://github.com/novnc/websockify-js/wiki/websock.js) wiki page.
|
[Websock API](https://github.com/novnc/websockify-js/wiki/websock.js) wiki page.
|
||||||
|
|
@ -25,10 +27,10 @@ with transparent binary data support.
|
||||||
|
|
||||||
## 1.2 Callbacks
|
## 1.2 Callbacks
|
||||||
|
|
||||||
For the Mouse, Keyboard and Display objects the callback functions are
|
For the Mouse, Keyboard, Display, and Clipboard objects, the callback
|
||||||
assigned to configuration attributes, just as for the RFB object. The
|
functions are assigned to configuration attributes, just as for the RFB
|
||||||
WebSock module has a method named 'on' that takes two parameters: the
|
object. The WebSock module has a method named 'on' that takes two
|
||||||
callback event name, and the callback function.
|
parameters: the callback event name, and the callback function.
|
||||||
|
|
||||||
## 2. Modules
|
## 2. Modules
|
||||||
|
|
||||||
|
|
@ -81,3 +83,23 @@ None
|
||||||
| blitImage | (x, y, width, height, arr, offset, from_queue) | Blit pixels (of R,G,B,A) to the display
|
| blitImage | (x, y, width, height, arr, offset, from_queue) | Blit pixels (of R,G,B,A) to the display
|
||||||
| drawImage | (img, x, y) | Draw image and track damage
|
| drawImage | (img, x, y) | Draw image and track damage
|
||||||
| autoscale | (containerWidth, containerHeight) | Scale the display
|
| autoscale | (containerWidth, containerHeight) | Scale the display
|
||||||
|
|
||||||
|
## 2.3 Clipboard module
|
||||||
|
|
||||||
|
### 2.3.1 Configuration attributes
|
||||||
|
|
||||||
|
None
|
||||||
|
|
||||||
|
### 2.3.2 Methods
|
||||||
|
|
||||||
|
| name | parameters | description
|
||||||
|
| ------------------ | ----------------- | ------------
|
||||||
|
| writeClipboard | (text) | An async write text to clipboard
|
||||||
|
| grab | () | Begin capturing clipboard events
|
||||||
|
| ungrab | () | Stop capturing clipboard events
|
||||||
|
|
||||||
|
### 2.3.3 Callbacks
|
||||||
|
|
||||||
|
| name | parameters | description
|
||||||
|
| ------- | ---------- | ------------
|
||||||
|
| onpaste | (text) | Called following a target focus event and an async clipboard read
|
||||||
|
|
|
||||||
11
docs/API.md
11
docs/API.md
|
|
@ -77,11 +77,6 @@ protocol stream.
|
||||||
if the remote session is smaller than its container, or handled
|
if the remote session is smaller than its container, or handled
|
||||||
according to `clipViewport` if it is larger. Disabled by default.
|
according to `clipViewport` if it is larger. Disabled by default.
|
||||||
|
|
||||||
`showDotCursor`
|
|
||||||
- Is a `boolean` indicating whether a dot cursor should be shown
|
|
||||||
instead of a zero-sized or fully-transparent cursor if the server
|
|
||||||
sets such invisible cursor. Disabled by default.
|
|
||||||
|
|
||||||
`viewOnly`
|
`viewOnly`
|
||||||
- Is a `boolean` indicating if any events (e.g. key presses or mouse
|
- Is a `boolean` indicating if any events (e.g. key presses or mouse
|
||||||
movement) should be prevented from being sent to the server.
|
movement) should be prevented from being sent to the server.
|
||||||
|
|
@ -101,6 +96,12 @@ protocol stream.
|
||||||
- The `clipboard` event is fired when clipboard data is received from
|
- The `clipboard` event is fired when clipboard data is received from
|
||||||
the server.
|
the server.
|
||||||
|
|
||||||
|
['clipboardreceived'](#clipboardreceived)
|
||||||
|
- The `clipboardreceived` event is fired after the `clipboard` event
|
||||||
|
or after the clipboard has been updated through the
|
||||||
|
[Clipboard module](API-internal.md#11-module-list). The copied text
|
||||||
|
has already been written to the system clipboard or the clipboard panel.
|
||||||
|
|
||||||
[`clippingviewport`](#clippingviewport)
|
[`clippingviewport`](#clippingviewport)
|
||||||
- The `clippingviewport` event is fired when `RFB.clippingViewport` is
|
- The `clippingviewport` event is fired when `RFB.clippingViewport` is
|
||||||
updated.
|
updated.
|
||||||
|
|
|
||||||
|
|
@ -89,9 +89,6 @@ Currently, the following options are available:
|
||||||
|
|
||||||
* `compression` - The session compression level. Can be `0` to `9`.
|
* `compression` - The session compression level. Can be `0` to `9`.
|
||||||
|
|
||||||
* `show_dot` - If a dot cursor should be shown when the remote server provides
|
|
||||||
no local cursor, or provides a fully-transparent (invisible) cursor.
|
|
||||||
|
|
||||||
* `logging` - The console log level. Can be one of `error`, `warn`, `info` or
|
* `logging` - The console log level. Can be one of `error`, `warn`, `info` or
|
||||||
`debug`.
|
`debug`.
|
||||||
|
|
||||||
|
|
|
||||||
14
package.json
14
package.json
|
|
@ -2,24 +2,20 @@
|
||||||
"name": "@novnc/novnc",
|
"name": "@novnc/novnc",
|
||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"description": "An HTML5 VNC client",
|
"description": "An HTML5 VNC client",
|
||||||
"browser": "lib/rfb",
|
"type": "module",
|
||||||
"directories": {
|
|
||||||
"lib": "lib",
|
|
||||||
"doc": "docs",
|
|
||||||
"test": "tests"
|
|
||||||
},
|
|
||||||
"files": [
|
"files": [
|
||||||
"lib",
|
"core",
|
||||||
|
"vendor",
|
||||||
"AUTHORS",
|
"AUTHORS",
|
||||||
"VERSION",
|
"VERSION",
|
||||||
"docs/API.md",
|
"docs/API.md",
|
||||||
"docs/LIBRARY.md",
|
"docs/LIBRARY.md",
|
||||||
"docs/LICENSE*"
|
"docs/LICENSE*"
|
||||||
],
|
],
|
||||||
|
"exports": "./core/rfb.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "eslint app core po/po2js po/xgettext-html tests utils",
|
"lint": "eslint app core po/po2js po/xgettext-html tests utils",
|
||||||
"test": "karma start karma.conf.js",
|
"test": "karma start karma.conf.cjs"
|
||||||
"prepublish": "node ./utils/convert.js --clean"
|
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ all:
|
||||||
.PHONY: update-po update-js update-pot
|
.PHONY: update-po update-js update-pot
|
||||||
.PHONY: FORCE
|
.PHONY: FORCE
|
||||||
|
|
||||||
LINGUAS := cs de el es fr it ja ko nl pl pt_BR ru sv tr zh_CN zh_TW
|
LINGUAS := cs de el es fr hr hu it ja ko nl pl pt_BR ru sv tr zh_CN zh_TW
|
||||||
|
|
||||||
VERSION := $(shell grep '"version"' ../package.json | cut -d '"' -f 4)
|
VERSION := $(shell grep '"version"' ../package.json | cut -d '"' -f 4)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,338 @@
|
||||||
|
# Croatian translations for noVNC package
|
||||||
|
# Hrvatski prijevod za noVNC paket
|
||||||
|
# Copyright (C) 2025 The noVNC authors
|
||||||
|
# This file is distributed under the same license as the noVNC package.
|
||||||
|
# Milo Ivir <mail@milotype.de>, 2025.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: noVNC 1.6.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
|
||||||
|
"POT-Creation-Date: 2025-02-14 10:14+0100\n"
|
||||||
|
"PO-Revision-Date: 2025-08-25 18:24+0200\n"
|
||||||
|
"Last-Translator: Milo Ivir <mail@mivirtype.de>\n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"Language: hr\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"X-Generator: Poedit 3.7\n"
|
||||||
|
|
||||||
|
#: ../app/ui.js:84
|
||||||
|
msgid ""
|
||||||
|
"Running without HTTPS is not recommended, crashes or other issues are likely."
|
||||||
|
msgstr ""
|
||||||
|
"Pokretanje bez HTTPS-a se ne preporučuje, vjerojatno će se dogoditi prekidi "
|
||||||
|
"rada ili drugi problemi."
|
||||||
|
|
||||||
|
#: ../app/ui.js:413
|
||||||
|
msgid "Connecting..."
|
||||||
|
msgstr "Povezivanje …"
|
||||||
|
|
||||||
|
#: ../app/ui.js:420
|
||||||
|
msgid "Disconnecting..."
|
||||||
|
msgstr "Odspajanje …"
|
||||||
|
|
||||||
|
#: ../app/ui.js:426
|
||||||
|
msgid "Reconnecting..."
|
||||||
|
msgstr "Ponovno povezivanje …"
|
||||||
|
|
||||||
|
#: ../app/ui.js:431
|
||||||
|
msgid "Internal error"
|
||||||
|
msgstr "Interna greška"
|
||||||
|
|
||||||
|
#: ../app/ui.js:1079
|
||||||
|
msgid "Failed to connect to server: "
|
||||||
|
msgstr "Povezivanje sa serverom nije uspjelo: "
|
||||||
|
|
||||||
|
#: ../app/ui.js:1145
|
||||||
|
msgid "Connected (encrypted) to "
|
||||||
|
msgstr "Povezano (šifrirano) na "
|
||||||
|
|
||||||
|
#: ../app/ui.js:1147
|
||||||
|
msgid "Connected (unencrypted) to "
|
||||||
|
msgstr "Povezano (nešifrirano) na "
|
||||||
|
|
||||||
|
#: ../app/ui.js:1170
|
||||||
|
msgid "Something went wrong, connection is closed"
|
||||||
|
msgstr "Nešto nije u redu, veza je zatvorena"
|
||||||
|
|
||||||
|
#: ../app/ui.js:1173
|
||||||
|
msgid "Failed to connect to server"
|
||||||
|
msgstr "Povezivanje sa serverom nije uspjelo"
|
||||||
|
|
||||||
|
#: ../app/ui.js:1185
|
||||||
|
msgid "Disconnected"
|
||||||
|
msgstr "Odspojeno"
|
||||||
|
|
||||||
|
#: ../app/ui.js:1200
|
||||||
|
msgid "New connection has been rejected with reason: "
|
||||||
|
msgstr "Nova veza je odbijena s razlogom: "
|
||||||
|
|
||||||
|
#: ../app/ui.js:1203
|
||||||
|
msgid "New connection has been rejected"
|
||||||
|
msgstr "Nova veza je odbijena"
|
||||||
|
|
||||||
|
#: ../app/ui.js:1269
|
||||||
|
msgid "Credentials are required"
|
||||||
|
msgstr "Podaci za prijavu su obavezni"
|
||||||
|
|
||||||
|
#: ../vnc.html:106
|
||||||
|
msgid "noVNC encountered an error:"
|
||||||
|
msgstr "noVNC je naišao na grešku:"
|
||||||
|
|
||||||
|
#: ../vnc.html:116
|
||||||
|
msgid "Hide/Show the control bar"
|
||||||
|
msgstr "Sakrij/Prikaži traku kontrola"
|
||||||
|
|
||||||
|
#: ../vnc.html:125
|
||||||
|
msgid "Drag"
|
||||||
|
msgstr "Povuci"
|
||||||
|
|
||||||
|
#: ../vnc.html:125
|
||||||
|
msgid "Move/Drag viewport"
|
||||||
|
msgstr "Pomakni/Povuci vidljivo područje"
|
||||||
|
|
||||||
|
#: ../vnc.html:131
|
||||||
|
msgid "Keyboard"
|
||||||
|
msgstr "Tipkovnica"
|
||||||
|
|
||||||
|
#: ../vnc.html:131
|
||||||
|
msgid "Show keyboard"
|
||||||
|
msgstr "Prikaži tipkovnicu"
|
||||||
|
|
||||||
|
#: ../vnc.html:136
|
||||||
|
msgid "Extra keys"
|
||||||
|
msgstr "Dodatne tipke"
|
||||||
|
|
||||||
|
#: ../vnc.html:136
|
||||||
|
msgid "Show extra keys"
|
||||||
|
msgstr "Prikaži dodatne tipke"
|
||||||
|
|
||||||
|
#: ../vnc.html:141
|
||||||
|
msgid "Ctrl"
|
||||||
|
msgstr "Ctrl"
|
||||||
|
|
||||||
|
#: ../vnc.html:141
|
||||||
|
msgid "Toggle Ctrl"
|
||||||
|
msgstr "Uključi/Isključi Ctrl"
|
||||||
|
|
||||||
|
#: ../vnc.html:144
|
||||||
|
msgid "Alt"
|
||||||
|
msgstr "Alt"
|
||||||
|
|
||||||
|
#: ../vnc.html:144
|
||||||
|
msgid "Toggle Alt"
|
||||||
|
msgstr "Uključi/Isključi Alt"
|
||||||
|
|
||||||
|
#: ../vnc.html:147
|
||||||
|
msgid "Toggle Windows"
|
||||||
|
msgstr "Uključi/Isključi Windows"
|
||||||
|
|
||||||
|
#: ../vnc.html:147
|
||||||
|
msgid "Windows"
|
||||||
|
msgstr "Windows"
|
||||||
|
|
||||||
|
#: ../vnc.html:150
|
||||||
|
msgid "Send Tab"
|
||||||
|
msgstr "Pošalji tabulator"
|
||||||
|
|
||||||
|
#: ../vnc.html:150
|
||||||
|
msgid "Tab"
|
||||||
|
msgstr "Tabulator"
|
||||||
|
|
||||||
|
#: ../vnc.html:153
|
||||||
|
msgid "Esc"
|
||||||
|
msgstr "Esc"
|
||||||
|
|
||||||
|
#: ../vnc.html:153
|
||||||
|
msgid "Send Escape"
|
||||||
|
msgstr "Pošalji Escape"
|
||||||
|
|
||||||
|
#: ../vnc.html:156
|
||||||
|
msgid "Ctrl+Alt+Del"
|
||||||
|
msgstr "Ctrl + Alt + Del"
|
||||||
|
|
||||||
|
#: ../vnc.html:156
|
||||||
|
msgid "Send Ctrl-Alt-Del"
|
||||||
|
msgstr "Pošalji Ctrl+Alt+Del"
|
||||||
|
|
||||||
|
#: ../vnc.html:163
|
||||||
|
msgid "Shutdown/Reboot"
|
||||||
|
msgstr "Isključi/Ponovo pokreni"
|
||||||
|
|
||||||
|
#: ../vnc.html:163
|
||||||
|
msgid "Shutdown/Reboot..."
|
||||||
|
msgstr "Isključi/Ponovo pokreni …"
|
||||||
|
|
||||||
|
#: ../vnc.html:169
|
||||||
|
msgid "Power"
|
||||||
|
msgstr "Napajanje"
|
||||||
|
|
||||||
|
#: ../vnc.html:171
|
||||||
|
msgid "Shutdown"
|
||||||
|
msgstr "Isključi"
|
||||||
|
|
||||||
|
#: ../vnc.html:172
|
||||||
|
msgid "Reboot"
|
||||||
|
msgstr "Ponovo pokreni"
|
||||||
|
|
||||||
|
#: ../vnc.html:173
|
||||||
|
msgid "Reset"
|
||||||
|
msgstr "Resetiraj"
|
||||||
|
|
||||||
|
#: ../vnc.html:178 ../vnc.html:184
|
||||||
|
msgid "Clipboard"
|
||||||
|
msgstr "Međuspremnik"
|
||||||
|
|
||||||
|
#: ../vnc.html:186
|
||||||
|
msgid "Edit clipboard content in the textarea below."
|
||||||
|
msgstr "Uredi sadržaj međuspremnika u donjem području teksta."
|
||||||
|
|
||||||
|
#: ../vnc.html:194
|
||||||
|
msgid "Full screen"
|
||||||
|
msgstr "Cjeloekranski prikaz"
|
||||||
|
|
||||||
|
#: ../vnc.html:199 ../vnc.html:205
|
||||||
|
msgid "Settings"
|
||||||
|
msgstr "Postavke"
|
||||||
|
|
||||||
|
#: ../vnc.html:211
|
||||||
|
msgid "Shared mode"
|
||||||
|
msgstr "Dijeljeni modus"
|
||||||
|
|
||||||
|
#: ../vnc.html:218
|
||||||
|
msgid "View only"
|
||||||
|
msgstr "Samo prikaz"
|
||||||
|
|
||||||
|
#: ../vnc.html:226
|
||||||
|
msgid "Clip to window"
|
||||||
|
msgstr "Isijeci na veličinu prozora"
|
||||||
|
|
||||||
|
#: ../vnc.html:231
|
||||||
|
msgid "Scaling mode:"
|
||||||
|
msgstr "Modus skaliranja:"
|
||||||
|
|
||||||
|
#: ../vnc.html:233
|
||||||
|
msgid "None"
|
||||||
|
msgstr "Bez"
|
||||||
|
|
||||||
|
#: ../vnc.html:234
|
||||||
|
msgid "Local scaling"
|
||||||
|
msgstr "Lokalno skaliranje"
|
||||||
|
|
||||||
|
#: ../vnc.html:235
|
||||||
|
msgid "Remote resizing"
|
||||||
|
msgstr "Daljinsko mijenjanje veličine"
|
||||||
|
|
||||||
|
#: ../vnc.html:240
|
||||||
|
msgid "Advanced"
|
||||||
|
msgstr "Napredno"
|
||||||
|
|
||||||
|
#: ../vnc.html:243
|
||||||
|
msgid "Quality:"
|
||||||
|
msgstr "Kvaliteta:"
|
||||||
|
|
||||||
|
#: ../vnc.html:247
|
||||||
|
msgid "Compression level:"
|
||||||
|
msgstr "Razina kompresije:"
|
||||||
|
|
||||||
|
#: ../vnc.html:252
|
||||||
|
msgid "Repeater ID:"
|
||||||
|
msgstr "ID repetitora:"
|
||||||
|
|
||||||
|
#: ../vnc.html:256
|
||||||
|
msgid "WebSocket"
|
||||||
|
msgstr "WebSocket"
|
||||||
|
|
||||||
|
#: ../vnc.html:261
|
||||||
|
msgid "Encrypt"
|
||||||
|
msgstr "Šifriraj"
|
||||||
|
|
||||||
|
#: ../vnc.html:266
|
||||||
|
msgid "Host:"
|
||||||
|
msgstr "Host:"
|
||||||
|
|
||||||
|
#: ../vnc.html:270
|
||||||
|
msgid "Port:"
|
||||||
|
msgstr "Priključak:"
|
||||||
|
|
||||||
|
#: ../vnc.html:274
|
||||||
|
msgid "Path:"
|
||||||
|
msgstr "Putanja:"
|
||||||
|
|
||||||
|
#: ../vnc.html:283
|
||||||
|
msgid "Automatic reconnect"
|
||||||
|
msgstr "Automatsko ponovno povezivanje"
|
||||||
|
|
||||||
|
#: ../vnc.html:288
|
||||||
|
msgid "Reconnect delay (ms):"
|
||||||
|
msgstr "Kašnjenje ponovnog povezivanja (ms):"
|
||||||
|
|
||||||
|
#: ../vnc.html:295
|
||||||
|
msgid "Show dot when no cursor"
|
||||||
|
msgstr "Prikaži točku kada nema pokazivača"
|
||||||
|
|
||||||
|
#: ../vnc.html:302
|
||||||
|
msgid "Logging:"
|
||||||
|
msgstr "Zapisivanje:"
|
||||||
|
|
||||||
|
#: ../vnc.html:311
|
||||||
|
msgid "Version:"
|
||||||
|
msgstr "Verzija:"
|
||||||
|
|
||||||
|
#: ../vnc.html:319
|
||||||
|
msgid "Disconnect"
|
||||||
|
msgstr "Odspoji"
|
||||||
|
|
||||||
|
#: ../vnc.html:342
|
||||||
|
msgid "Connect"
|
||||||
|
msgstr "Poveži"
|
||||||
|
|
||||||
|
#: ../vnc.html:351
|
||||||
|
msgid "Server identity"
|
||||||
|
msgstr "Identitet servera"
|
||||||
|
|
||||||
|
#: ../vnc.html:354
|
||||||
|
msgid "The server has provided the following identifying information:"
|
||||||
|
msgstr "Server je pružio sljedeće identifikacijske podatke:"
|
||||||
|
|
||||||
|
#: ../vnc.html:357
|
||||||
|
msgid "Fingerprint:"
|
||||||
|
msgstr "Otisak:"
|
||||||
|
|
||||||
|
#: ../vnc.html:361
|
||||||
|
msgid ""
|
||||||
|
"Please verify that the information is correct and press \"Approve\". "
|
||||||
|
"Otherwise press \"Reject\"."
|
||||||
|
msgstr ""
|
||||||
|
"Provjeri jesu li podaci točni i pritisni „Odobri“. U suprotnom pritisni "
|
||||||
|
"„Odbaci“."
|
||||||
|
|
||||||
|
#: ../vnc.html:366
|
||||||
|
msgid "Approve"
|
||||||
|
msgstr "Odobri"
|
||||||
|
|
||||||
|
#: ../vnc.html:367
|
||||||
|
msgid "Reject"
|
||||||
|
msgstr "Odbij"
|
||||||
|
|
||||||
|
#: ../vnc.html:375
|
||||||
|
msgid "Credentials"
|
||||||
|
msgstr "Podaci za prijavu"
|
||||||
|
|
||||||
|
#: ../vnc.html:379
|
||||||
|
msgid "Username:"
|
||||||
|
msgstr "Korisničko ime:"
|
||||||
|
|
||||||
|
#: ../vnc.html:383
|
||||||
|
msgid "Password:"
|
||||||
|
msgstr "Lozinka:"
|
||||||
|
|
||||||
|
#: ../vnc.html:387
|
||||||
|
msgid "Send credentials"
|
||||||
|
msgstr "Pošalji podatke za prijavu"
|
||||||
|
|
||||||
|
#: ../vnc.html:396
|
||||||
|
msgid "Cancel"
|
||||||
|
msgstr "Odustani"
|
||||||
|
|
@ -0,0 +1,332 @@
|
||||||
|
# Hungarian translations for noVNC package.
|
||||||
|
# Copyright (C) 2025 The noVNC authors
|
||||||
|
# This file is distributed under the same license as the noVNC package.
|
||||||
|
# Daniel Felso <danielfelso@protonmail.com>, 2025.
|
||||||
|
#
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: noVNC 1.6.0\n"
|
||||||
|
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
|
||||||
|
"POT-Creation-Date: 2025-02-14 10:14+0100\n"
|
||||||
|
"PO-Revision-Date: 2025-10-06 14:38+0200\n"
|
||||||
|
"Last-Translator: Daniel Felso <danielfelso@protonmail.com>\n"
|
||||||
|
"Language-Team: \n"
|
||||||
|
"Language: hu\n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
#: ../app/ui.js:84
|
||||||
|
msgid ""
|
||||||
|
"Running without HTTPS is not recommended, crashes or other issues are likely."
|
||||||
|
msgstr "HTTPS nélkül futtatni nem ajánlott, összeomlások vagy más problémák várhatók."
|
||||||
|
|
||||||
|
#: ../app/ui.js:413
|
||||||
|
msgid "Connecting..."
|
||||||
|
msgstr "Kapcsolódás..."
|
||||||
|
|
||||||
|
#: ../app/ui.js:420
|
||||||
|
msgid "Disconnecting..."
|
||||||
|
msgstr "Kapcsolat bontása..."
|
||||||
|
|
||||||
|
#: ../app/ui.js:426
|
||||||
|
msgid "Reconnecting..."
|
||||||
|
msgstr "Újrakapcsolódás..."
|
||||||
|
|
||||||
|
#: ../app/ui.js:431
|
||||||
|
msgid "Internal error"
|
||||||
|
msgstr "Belső hiba"
|
||||||
|
|
||||||
|
#: ../app/ui.js:1079
|
||||||
|
msgid "Failed to connect to server: "
|
||||||
|
msgstr "Nem sikerült csatlakozni a szerverhez: "
|
||||||
|
|
||||||
|
#: ../app/ui.js:1145
|
||||||
|
msgid "Connected (encrypted) to "
|
||||||
|
msgstr "Kapcsolódva (titkosítva) ehhez: "
|
||||||
|
|
||||||
|
#: ../app/ui.js:1147
|
||||||
|
msgid "Connected (unencrypted) to "
|
||||||
|
msgstr "Kapcsolódva (titkosítatlanul) ehhez: "
|
||||||
|
|
||||||
|
#: ../app/ui.js:1170
|
||||||
|
msgid "Something went wrong, connection is closed"
|
||||||
|
msgstr "Valami hiba történt, a kapcsolat lezárult"
|
||||||
|
|
||||||
|
#: ../app/ui.js:1173
|
||||||
|
msgid "Failed to connect to server"
|
||||||
|
msgstr "Nem sikerült csatlakozni a szerverhez"
|
||||||
|
|
||||||
|
#: ../app/ui.js:1185
|
||||||
|
msgid "Disconnected"
|
||||||
|
msgstr "Kapcsolat bontva"
|
||||||
|
|
||||||
|
#: ../app/ui.js:1200
|
||||||
|
msgid "New connection has been rejected with reason: "
|
||||||
|
msgstr "Az új kapcsolat elutasítva, indok: "
|
||||||
|
|
||||||
|
#: ../app/ui.js:1203
|
||||||
|
msgid "New connection has been rejected"
|
||||||
|
msgstr "Az új kapcsolat elutasítva"
|
||||||
|
|
||||||
|
#: ../app/ui.js:1269
|
||||||
|
msgid "Credentials are required"
|
||||||
|
msgstr "Hitelesítő adatok szükségesek"
|
||||||
|
|
||||||
|
#: ../vnc.html:106
|
||||||
|
msgid "noVNC encountered an error:"
|
||||||
|
msgstr "A noVNC hibát észlelt:"
|
||||||
|
|
||||||
|
#: ../vnc.html:116
|
||||||
|
msgid "Hide/Show the control bar"
|
||||||
|
msgstr "Vezérlősáv elrejtése/megjelenítése"
|
||||||
|
|
||||||
|
#: ../vnc.html:125
|
||||||
|
msgid "Drag"
|
||||||
|
msgstr "Húzás"
|
||||||
|
|
||||||
|
#: ../vnc.html:125
|
||||||
|
msgid "Move/Drag viewport"
|
||||||
|
msgstr "Nézet mozgatása/húzása"
|
||||||
|
|
||||||
|
#: ../vnc.html:131
|
||||||
|
msgid "Keyboard"
|
||||||
|
msgstr "Billentyűzet"
|
||||||
|
|
||||||
|
#: ../vnc.html:131
|
||||||
|
msgid "Show keyboard"
|
||||||
|
msgstr "Billentyűzet megjelenítése"
|
||||||
|
|
||||||
|
#: ../vnc.html:136
|
||||||
|
msgid "Extra keys"
|
||||||
|
msgstr "Extra billentyűk"
|
||||||
|
|
||||||
|
#: ../vnc.html:136
|
||||||
|
msgid "Show extra keys"
|
||||||
|
msgstr "Extra billentyűk megjelenítése"
|
||||||
|
|
||||||
|
#: ../vnc.html:141
|
||||||
|
msgid "Ctrl"
|
||||||
|
msgstr "Ctrl"
|
||||||
|
|
||||||
|
#: ../vnc.html:141
|
||||||
|
msgid "Toggle Ctrl"
|
||||||
|
msgstr "Ctrl lenyomása/felengedése"
|
||||||
|
|
||||||
|
#: ../vnc.html:144
|
||||||
|
msgid "Alt"
|
||||||
|
msgstr "Alt"
|
||||||
|
|
||||||
|
#: ../vnc.html:144
|
||||||
|
msgid "Toggle Alt"
|
||||||
|
msgstr "Alt lenyomása/felengedése"
|
||||||
|
|
||||||
|
#: ../vnc.html:147
|
||||||
|
msgid "Toggle Windows"
|
||||||
|
msgstr "Windows lenyomása/felengedése"
|
||||||
|
|
||||||
|
#: ../vnc.html:147
|
||||||
|
msgid "Windows"
|
||||||
|
msgstr "Windows"
|
||||||
|
|
||||||
|
#: ../vnc.html:150
|
||||||
|
msgid "Send Tab"
|
||||||
|
msgstr "Tab küldése"
|
||||||
|
|
||||||
|
#: ../vnc.html:150
|
||||||
|
msgid "Tab"
|
||||||
|
msgstr "Tab"
|
||||||
|
|
||||||
|
#: ../vnc.html:153
|
||||||
|
msgid "Esc"
|
||||||
|
msgstr "Esc"
|
||||||
|
|
||||||
|
#: ../vnc.html:153
|
||||||
|
msgid "Send Escape"
|
||||||
|
msgstr "Escape küldése"
|
||||||
|
|
||||||
|
#: ../vnc.html:156
|
||||||
|
msgid "Ctrl+Alt+Del"
|
||||||
|
msgstr "Ctrl+Alt+Del"
|
||||||
|
|
||||||
|
#: ../vnc.html:156
|
||||||
|
msgid "Send Ctrl-Alt-Del"
|
||||||
|
msgstr "Ctrl-Alt-Del küldése"
|
||||||
|
|
||||||
|
#: ../vnc.html:163
|
||||||
|
msgid "Shutdown/Reboot"
|
||||||
|
msgstr "Leállítás/Újraindítás"
|
||||||
|
|
||||||
|
#: ../vnc.html:163
|
||||||
|
msgid "Shutdown/Reboot..."
|
||||||
|
msgstr "Leállítás/Újraindítás..."
|
||||||
|
|
||||||
|
#: ../vnc.html:169
|
||||||
|
msgid "Power"
|
||||||
|
msgstr "Bekapcsolás"
|
||||||
|
|
||||||
|
#: ../vnc.html:171
|
||||||
|
msgid "Shutdown"
|
||||||
|
msgstr "Leállítás"
|
||||||
|
|
||||||
|
#: ../vnc.html:172
|
||||||
|
msgid "Reboot"
|
||||||
|
msgstr "Újraindítás"
|
||||||
|
|
||||||
|
#: ../vnc.html:173
|
||||||
|
msgid "Reset"
|
||||||
|
msgstr "Reset"
|
||||||
|
|
||||||
|
#: ../vnc.html:178 ../vnc.html:184
|
||||||
|
msgid "Clipboard"
|
||||||
|
msgstr "Vágólap"
|
||||||
|
|
||||||
|
#: ../vnc.html:186
|
||||||
|
msgid "Edit clipboard content in the textarea below."
|
||||||
|
msgstr "Itt tudod módosítani a vágólap tartalmát."
|
||||||
|
|
||||||
|
#: ../vnc.html:194
|
||||||
|
msgid "Full screen"
|
||||||
|
msgstr "Teljes képernyő"
|
||||||
|
|
||||||
|
#: ../vnc.html:199 ../vnc.html:205
|
||||||
|
msgid "Settings"
|
||||||
|
msgstr "Beállítások"
|
||||||
|
|
||||||
|
#: ../vnc.html:211
|
||||||
|
msgid "Shared mode"
|
||||||
|
msgstr "Megosztott mód"
|
||||||
|
|
||||||
|
#: ../vnc.html:218
|
||||||
|
msgid "View only"
|
||||||
|
msgstr "Csak megtekintés"
|
||||||
|
|
||||||
|
#: ../vnc.html:226
|
||||||
|
msgid "Clip to window"
|
||||||
|
msgstr "Ablakhoz igazítás"
|
||||||
|
|
||||||
|
#: ../vnc.html:231
|
||||||
|
msgid "Scaling mode:"
|
||||||
|
msgstr "Méretezési mód:"
|
||||||
|
|
||||||
|
#: ../vnc.html:233
|
||||||
|
msgid "None"
|
||||||
|
msgstr "Nincs"
|
||||||
|
|
||||||
|
#: ../vnc.html:234
|
||||||
|
msgid "Local scaling"
|
||||||
|
msgstr "Helyi méretezés"
|
||||||
|
|
||||||
|
#: ../vnc.html:235
|
||||||
|
msgid "Remote resizing"
|
||||||
|
msgstr "Távoli átméretezés"
|
||||||
|
|
||||||
|
#: ../vnc.html:240
|
||||||
|
msgid "Advanced"
|
||||||
|
msgstr "Speciális"
|
||||||
|
|
||||||
|
#: ../vnc.html:243
|
||||||
|
msgid "Quality:"
|
||||||
|
msgstr "Minőség:"
|
||||||
|
|
||||||
|
#: ../vnc.html:247
|
||||||
|
msgid "Compression level:"
|
||||||
|
msgstr "Tömörítési szint:"
|
||||||
|
|
||||||
|
#: ../vnc.html:252
|
||||||
|
msgid "Repeater ID:"
|
||||||
|
msgstr "Ismétlő azonosító:"
|
||||||
|
|
||||||
|
#: ../vnc.html:256
|
||||||
|
msgid "WebSocket"
|
||||||
|
msgstr "WebSocket"
|
||||||
|
|
||||||
|
#: ../vnc.html:261
|
||||||
|
msgid "Encrypt"
|
||||||
|
msgstr "Titkosítás"
|
||||||
|
|
||||||
|
#: ../vnc.html:266
|
||||||
|
msgid "Host:"
|
||||||
|
msgstr "Hoszt:"
|
||||||
|
|
||||||
|
#: ../vnc.html:270
|
||||||
|
msgid "Port:"
|
||||||
|
msgstr "Port:"
|
||||||
|
|
||||||
|
#: ../vnc.html:274
|
||||||
|
msgid "Path:"
|
||||||
|
msgstr "Útvonal:"
|
||||||
|
|
||||||
|
#: ../vnc.html:283
|
||||||
|
msgid "Automatic reconnect"
|
||||||
|
msgstr "Automatikus újracsatlakozás"
|
||||||
|
|
||||||
|
#: ../vnc.html:288
|
||||||
|
msgid "Reconnect delay (ms):"
|
||||||
|
msgstr "Újracsatlakozás késleltetése (ms):"
|
||||||
|
|
||||||
|
#: ../vnc.html:295
|
||||||
|
msgid "Show dot when no cursor"
|
||||||
|
msgstr "Kurzor hiányában pont mutatása"
|
||||||
|
|
||||||
|
#: ../vnc.html:302
|
||||||
|
msgid "Logging:"
|
||||||
|
msgstr "Naplózás:"
|
||||||
|
|
||||||
|
#: ../vnc.html:311
|
||||||
|
msgid "Version:"
|
||||||
|
msgstr "Verzió:"
|
||||||
|
|
||||||
|
#: ../vnc.html:319
|
||||||
|
msgid "Disconnect"
|
||||||
|
msgstr "Kapcsolat bontása"
|
||||||
|
|
||||||
|
#: ../vnc.html:342
|
||||||
|
msgid "Connect"
|
||||||
|
msgstr "Csatlakozás"
|
||||||
|
|
||||||
|
#: ../vnc.html:351
|
||||||
|
msgid "Server identity"
|
||||||
|
msgstr "Szerver azonosító"
|
||||||
|
|
||||||
|
#: ../vnc.html:354
|
||||||
|
msgid "The server has provided the following identifying information:"
|
||||||
|
msgstr "A szerver a következő azonosító információt adta meg:"
|
||||||
|
|
||||||
|
#: ../vnc.html:357
|
||||||
|
msgid "Fingerprint:"
|
||||||
|
msgstr "Ujjlenyomat:"
|
||||||
|
|
||||||
|
#: ../vnc.html:361
|
||||||
|
msgid ""
|
||||||
|
"Please verify that the information is correct and press \"Approve\". "
|
||||||
|
"Otherwise press \"Reject\"."
|
||||||
|
msgstr "Ellenőrizze, hogy az információ helyes-e és nyomja meg a \"Jóváhagyás\" gombot. Ellenkező esetben nyomja meg az \"Elutasítás\" gombot."
|
||||||
|
|
||||||
|
#: ../vnc.html:366
|
||||||
|
msgid "Approve"
|
||||||
|
msgstr "Jóváhagyás"
|
||||||
|
|
||||||
|
#: ../vnc.html:367
|
||||||
|
msgid "Reject"
|
||||||
|
msgstr "Elutasítás"
|
||||||
|
|
||||||
|
#: ../vnc.html:375
|
||||||
|
msgid "Credentials"
|
||||||
|
msgstr "Hitelesítő adatok"
|
||||||
|
|
||||||
|
#: ../vnc.html:379
|
||||||
|
msgid "Username:"
|
||||||
|
msgstr "Felhasználónév:"
|
||||||
|
|
||||||
|
#: ../vnc.html:383
|
||||||
|
msgid "Password:"
|
||||||
|
msgstr "Jelszó:"
|
||||||
|
|
||||||
|
#: ../vnc.html:387
|
||||||
|
msgid "Send credentials"
|
||||||
|
msgstr "Hitelesítő adatok küldése"
|
||||||
|
|
||||||
|
#: ../vnc.html:396
|
||||||
|
msgid "Cancel"
|
||||||
|
msgstr "Mégse"
|
||||||
24
po/noVNC.pot
24
po/noVNC.pot
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: noVNC 1.6.0\n"
|
"Project-Id-Version: noVNC 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
|
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
|
||||||
"POT-Creation-Date: 2025-02-14 10:14+0100\n"
|
"POT-Creation-Date: 2025-10-31 09:17+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
|
@ -38,39 +38,43 @@ msgstr ""
|
||||||
msgid "Internal error"
|
msgid "Internal error"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ../app/ui.js:1079
|
#: ../app/ui.js:1084
|
||||||
msgid "Failed to connect to server: "
|
msgid "Failed to connect to server: "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ../app/ui.js:1145
|
#: ../app/ui.js:1151
|
||||||
msgid "Connected (encrypted) to "
|
msgid "Connected (encrypted) to "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ../app/ui.js:1147
|
#: ../app/ui.js:1153
|
||||||
msgid "Connected (unencrypted) to "
|
msgid "Connected (unencrypted) to "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ../app/ui.js:1170
|
#: ../app/ui.js:1178
|
||||||
msgid "Something went wrong, connection is closed"
|
msgid "Something went wrong, connection is closed"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ../app/ui.js:1173
|
#: ../app/ui.js:1181
|
||||||
msgid "Failed to connect to server"
|
msgid "Failed to connect to server"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ../app/ui.js:1185
|
#: ../app/ui.js:1193
|
||||||
msgid "Disconnected"
|
msgid "Disconnected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ../app/ui.js:1200
|
#: ../app/ui.js:1210
|
||||||
msgid "New connection has been rejected with reason: "
|
msgid "New connection has been rejected with reason: "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ../app/ui.js:1203
|
#: ../app/ui.js:1213
|
||||||
msgid "New connection has been rejected"
|
msgid "New connection has been rejected"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: ../app/ui.js:1269
|
#: ../app/ui.js:1225
|
||||||
|
msgid "Are you sure you want to disconnect the session?"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: ../app/ui.js:1297
|
||||||
msgid "Credentials are required"
|
msgid "Credentials are required"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|
|
||||||
6
po/po2js
6
po/po2js
|
|
@ -17,9 +17,9 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { program } = require('commander');
|
import { program } from 'commander';
|
||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const pofile = require("pofile");
|
import pofile from "pofile";
|
||||||
|
|
||||||
program
|
program
|
||||||
.argument('<input>')
|
.argument('<input>')
|
||||||
|
|
|
||||||
700
po/sv.po
700
po/sv.po
|
|
@ -1,348 +1,352 @@
|
||||||
# Swedish translations for noVNC package
|
# Swedish translations for noVNC package
|
||||||
# Svenska översättningar för paketet noVNC.
|
# Svenska översättningar för paketet noVNC.
|
||||||
# Copyright (C) 2025 The noVNC authors
|
# Copyright (C) 2025 The noVNC authors
|
||||||
# This file is distributed under the same license as the noVNC package.
|
# This file is distributed under the same license as the noVNC package.
|
||||||
# Samuel Mannehed <samuel@cendio.se>, 2020.
|
# Samuel Mannehed <samuel@cendio.se>, 2020.
|
||||||
#
|
#
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: noVNC 1.6.0\n"
|
"Project-Id-Version: noVNC 1.6.0\n"
|
||||||
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
|
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
|
||||||
"POT-Creation-Date: 2025-02-14 10:14+0100\n"
|
"POT-Creation-Date: 2025-10-31 09:17+0100\n"
|
||||||
"PO-Revision-Date: 2025-02-14 10:29+0100\n"
|
"PO-Revision-Date: 2025-10-31 10:48+0100\n"
|
||||||
"Last-Translator: Alexander Zeijlon <aleze@cendio.com>\n"
|
"Last-Translator: Alexander Zeijlon <aleze@cendio.com>\n"
|
||||||
"Language-Team: none\n"
|
"Language-Team: none\n"
|
||||||
"Language: sv\n"
|
"Language: sv\n"
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
"X-Generator: Poedit 3.5\n"
|
"X-Generator: Poedit 3.7\n"
|
||||||
|
|
||||||
#: ../app/ui.js:84
|
#: ../app/ui.js:84
|
||||||
msgid ""
|
msgid ""
|
||||||
"Running without HTTPS is not recommended, crashes or other issues are likely."
|
"Running without HTTPS is not recommended, crashes or other issues are likely."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Det är ej rekommenderat att köra utan HTTPS, krascher och andra problem är "
|
"Det är ej rekommenderat att köra utan HTTPS, krascher och andra problem är "
|
||||||
"troliga."
|
"troliga."
|
||||||
|
|
||||||
#: ../app/ui.js:413
|
#: ../app/ui.js:413
|
||||||
msgid "Connecting..."
|
msgid "Connecting..."
|
||||||
msgstr "Ansluter..."
|
msgstr "Ansluter..."
|
||||||
|
|
||||||
#: ../app/ui.js:420
|
#: ../app/ui.js:420
|
||||||
msgid "Disconnecting..."
|
msgid "Disconnecting..."
|
||||||
msgstr "Kopplar ner..."
|
msgstr "Kopplar ifrån..."
|
||||||
|
|
||||||
#: ../app/ui.js:426
|
#: ../app/ui.js:426
|
||||||
msgid "Reconnecting..."
|
msgid "Reconnecting..."
|
||||||
msgstr "Återansluter..."
|
msgstr "Återansluter..."
|
||||||
|
|
||||||
#: ../app/ui.js:431
|
#: ../app/ui.js:431
|
||||||
msgid "Internal error"
|
msgid "Internal error"
|
||||||
msgstr "Internt fel"
|
msgstr "Internt fel"
|
||||||
|
|
||||||
#: ../app/ui.js:1079
|
#: ../app/ui.js:1084
|
||||||
msgid "Failed to connect to server: "
|
msgid "Failed to connect to server: "
|
||||||
msgstr "Misslyckades att ansluta till servern: "
|
msgstr "Misslyckades att ansluta till servern: "
|
||||||
|
|
||||||
#: ../app/ui.js:1145
|
#: ../app/ui.js:1151
|
||||||
msgid "Connected (encrypted) to "
|
msgid "Connected (encrypted) to "
|
||||||
msgstr "Ansluten (krypterat) till "
|
msgstr "Ansluten (krypterat) till "
|
||||||
|
|
||||||
#: ../app/ui.js:1147
|
#: ../app/ui.js:1153
|
||||||
msgid "Connected (unencrypted) to "
|
msgid "Connected (unencrypted) to "
|
||||||
msgstr "Ansluten (okrypterat) till "
|
msgstr "Ansluten (okrypterat) till "
|
||||||
|
|
||||||
#: ../app/ui.js:1170
|
#: ../app/ui.js:1178
|
||||||
msgid "Something went wrong, connection is closed"
|
msgid "Something went wrong, connection is closed"
|
||||||
msgstr "Något gick fel, anslutningen avslutades"
|
msgstr "Något gick fel, anslutningen avslutades"
|
||||||
|
|
||||||
#: ../app/ui.js:1173
|
#: ../app/ui.js:1181
|
||||||
msgid "Failed to connect to server"
|
msgid "Failed to connect to server"
|
||||||
msgstr "Misslyckades att ansluta till servern"
|
msgstr "Misslyckades att ansluta till servern"
|
||||||
|
|
||||||
#: ../app/ui.js:1185
|
#: ../app/ui.js:1193
|
||||||
msgid "Disconnected"
|
msgid "Disconnected"
|
||||||
msgstr "Frånkopplad"
|
msgstr "Frånkopplad"
|
||||||
|
|
||||||
#: ../app/ui.js:1200
|
#: ../app/ui.js:1210
|
||||||
msgid "New connection has been rejected with reason: "
|
msgid "New connection has been rejected with reason: "
|
||||||
msgstr "Ny anslutning har blivit nekad med följande skäl: "
|
msgstr "Ny anslutning har blivit nekad med följande skäl: "
|
||||||
|
|
||||||
#: ../app/ui.js:1203
|
#: ../app/ui.js:1213
|
||||||
msgid "New connection has been rejected"
|
msgid "New connection has been rejected"
|
||||||
msgstr "Ny anslutning har blivit nekad"
|
msgstr "Ny anslutning har blivit nekad"
|
||||||
|
|
||||||
#: ../app/ui.js:1269
|
#: ../app/ui.js:1225
|
||||||
msgid "Credentials are required"
|
msgid "Are you sure you want to disconnect the session?"
|
||||||
msgstr "Användaruppgifter krävs"
|
msgstr "Är du säker på att du vill koppla ifrån sessionen?"
|
||||||
|
|
||||||
#: ../vnc.html:106
|
#: ../app/ui.js:1297
|
||||||
msgid "noVNC encountered an error:"
|
msgid "Credentials are required"
|
||||||
msgstr "noVNC stötte på ett problem:"
|
msgstr "Användaruppgifter krävs"
|
||||||
|
|
||||||
#: ../vnc.html:116
|
#: ../vnc.html:106
|
||||||
msgid "Hide/Show the control bar"
|
msgid "noVNC encountered an error:"
|
||||||
msgstr "Göm/Visa kontrollbaren"
|
msgstr "noVNC stötte på ett problem:"
|
||||||
|
|
||||||
#: ../vnc.html:125
|
#: ../vnc.html:116
|
||||||
msgid "Drag"
|
msgid "Hide/Show the control bar"
|
||||||
msgstr "Dra"
|
msgstr "Göm/Visa kontrollbaren"
|
||||||
|
|
||||||
#: ../vnc.html:125
|
#: ../vnc.html:125
|
||||||
msgid "Move/Drag viewport"
|
msgid "Drag"
|
||||||
msgstr "Flytta/Dra vyn"
|
msgstr "Dra"
|
||||||
|
|
||||||
#: ../vnc.html:131
|
#: ../vnc.html:125
|
||||||
msgid "Keyboard"
|
msgid "Move/Drag viewport"
|
||||||
msgstr "Tangentbord"
|
msgstr "Flytta/Dra vyn"
|
||||||
|
|
||||||
#: ../vnc.html:131
|
#: ../vnc.html:131
|
||||||
msgid "Show keyboard"
|
msgid "Keyboard"
|
||||||
msgstr "Visa tangentbord"
|
msgstr "Tangentbord"
|
||||||
|
|
||||||
#: ../vnc.html:136
|
#: ../vnc.html:131
|
||||||
msgid "Extra keys"
|
msgid "Show keyboard"
|
||||||
msgstr "Extraknappar"
|
msgstr "Visa tangentbord"
|
||||||
|
|
||||||
#: ../vnc.html:136
|
#: ../vnc.html:136
|
||||||
msgid "Show extra keys"
|
msgid "Extra keys"
|
||||||
msgstr "Visa extraknappar"
|
msgstr "Extraknappar"
|
||||||
|
|
||||||
#: ../vnc.html:141
|
#: ../vnc.html:136
|
||||||
msgid "Ctrl"
|
msgid "Show extra keys"
|
||||||
msgstr "Ctrl"
|
msgstr "Visa extraknappar"
|
||||||
|
|
||||||
#: ../vnc.html:141
|
#: ../vnc.html:141
|
||||||
msgid "Toggle Ctrl"
|
msgid "Ctrl"
|
||||||
msgstr "Växla Ctrl"
|
msgstr "Ctrl"
|
||||||
|
|
||||||
#: ../vnc.html:144
|
#: ../vnc.html:141
|
||||||
msgid "Alt"
|
msgid "Toggle Ctrl"
|
||||||
msgstr "Alt"
|
msgstr "Växla Ctrl"
|
||||||
|
|
||||||
#: ../vnc.html:144
|
#: ../vnc.html:144
|
||||||
msgid "Toggle Alt"
|
msgid "Alt"
|
||||||
msgstr "Växla Alt"
|
msgstr "Alt"
|
||||||
|
|
||||||
#: ../vnc.html:147
|
#: ../vnc.html:144
|
||||||
msgid "Toggle Windows"
|
msgid "Toggle Alt"
|
||||||
msgstr "Växla Windows"
|
msgstr "Växla Alt"
|
||||||
|
|
||||||
#: ../vnc.html:147
|
#: ../vnc.html:147
|
||||||
msgid "Windows"
|
msgid "Toggle Windows"
|
||||||
msgstr "Windows"
|
msgstr "Växla Windows"
|
||||||
|
|
||||||
#: ../vnc.html:150
|
#: ../vnc.html:147
|
||||||
msgid "Send Tab"
|
msgid "Windows"
|
||||||
msgstr "Skicka Tab"
|
msgstr "Windows"
|
||||||
|
|
||||||
#: ../vnc.html:150
|
#: ../vnc.html:150
|
||||||
msgid "Tab"
|
msgid "Send Tab"
|
||||||
msgstr "Tab"
|
msgstr "Skicka Tab"
|
||||||
|
|
||||||
#: ../vnc.html:153
|
#: ../vnc.html:150
|
||||||
msgid "Esc"
|
msgid "Tab"
|
||||||
msgstr "Esc"
|
msgstr "Tab"
|
||||||
|
|
||||||
#: ../vnc.html:153
|
#: ../vnc.html:153
|
||||||
msgid "Send Escape"
|
msgid "Esc"
|
||||||
msgstr "Skicka Escape"
|
msgstr "Esc"
|
||||||
|
|
||||||
#: ../vnc.html:156
|
#: ../vnc.html:153
|
||||||
msgid "Ctrl+Alt+Del"
|
msgid "Send Escape"
|
||||||
msgstr "Ctrl+Alt+Del"
|
msgstr "Skicka Escape"
|
||||||
|
|
||||||
#: ../vnc.html:156
|
#: ../vnc.html:156
|
||||||
msgid "Send Ctrl-Alt-Del"
|
msgid "Ctrl+Alt+Del"
|
||||||
msgstr "Skicka Ctrl-Alt-Del"
|
msgstr "Ctrl+Alt+Del"
|
||||||
|
|
||||||
#: ../vnc.html:163
|
#: ../vnc.html:156
|
||||||
msgid "Shutdown/Reboot"
|
msgid "Send Ctrl-Alt-Del"
|
||||||
msgstr "Stäng av/Boota om"
|
msgstr "Skicka Ctrl-Alt-Del"
|
||||||
|
|
||||||
#: ../vnc.html:163
|
#: ../vnc.html:163
|
||||||
msgid "Shutdown/Reboot..."
|
msgid "Shutdown/Reboot"
|
||||||
msgstr "Stäng av/Boota om..."
|
msgstr "Stäng av/Starta om"
|
||||||
|
|
||||||
#: ../vnc.html:169
|
#: ../vnc.html:163
|
||||||
msgid "Power"
|
msgid "Shutdown/Reboot..."
|
||||||
msgstr "Ström"
|
msgstr "Stäng av/Starta om..."
|
||||||
|
|
||||||
#: ../vnc.html:171
|
#: ../vnc.html:169
|
||||||
msgid "Shutdown"
|
msgid "Power"
|
||||||
msgstr "Stäng av"
|
msgstr "Ström"
|
||||||
|
|
||||||
#: ../vnc.html:172
|
#: ../vnc.html:171
|
||||||
msgid "Reboot"
|
msgid "Shutdown"
|
||||||
msgstr "Boota om"
|
msgstr "Stäng av"
|
||||||
|
|
||||||
#: ../vnc.html:173
|
#: ../vnc.html:172
|
||||||
msgid "Reset"
|
msgid "Reboot"
|
||||||
msgstr "Återställ"
|
msgstr "Starta om"
|
||||||
|
|
||||||
#: ../vnc.html:178 ../vnc.html:184
|
#: ../vnc.html:173
|
||||||
msgid "Clipboard"
|
msgid "Reset"
|
||||||
msgstr "Urklipp"
|
msgstr "Återställ"
|
||||||
|
|
||||||
#: ../vnc.html:186
|
#: ../vnc.html:178 ../vnc.html:184
|
||||||
msgid "Edit clipboard content in the textarea below."
|
msgid "Clipboard"
|
||||||
msgstr "Redigera urklippets innehåll i fältet nedan."
|
msgstr "Urklipp"
|
||||||
|
|
||||||
#: ../vnc.html:194
|
#: ../vnc.html:186
|
||||||
msgid "Full screen"
|
msgid "Edit clipboard content in the textarea below."
|
||||||
msgstr "Fullskärm"
|
msgstr "Redigera urklippets innehåll i fältet nedan."
|
||||||
|
|
||||||
#: ../vnc.html:199 ../vnc.html:205
|
#: ../vnc.html:194
|
||||||
msgid "Settings"
|
msgid "Full screen"
|
||||||
msgstr "Inställningar"
|
msgstr "Fullskärm"
|
||||||
|
|
||||||
#: ../vnc.html:211
|
#: ../vnc.html:199 ../vnc.html:205
|
||||||
msgid "Shared mode"
|
msgid "Settings"
|
||||||
msgstr "Delat läge"
|
msgstr "Inställningar"
|
||||||
|
|
||||||
#: ../vnc.html:218
|
#: ../vnc.html:211
|
||||||
msgid "View only"
|
msgid "Shared mode"
|
||||||
msgstr "Endast visning"
|
msgstr "Delat läge"
|
||||||
|
|
||||||
#: ../vnc.html:226
|
#: ../vnc.html:218
|
||||||
msgid "Clip to window"
|
msgid "View only"
|
||||||
msgstr "Begränsa till fönster"
|
msgstr "Endast visning"
|
||||||
|
|
||||||
#: ../vnc.html:231
|
#: ../vnc.html:226
|
||||||
msgid "Scaling mode:"
|
msgid "Clip to window"
|
||||||
msgstr "Skalningsläge:"
|
msgstr "Begränsa till fönster"
|
||||||
|
|
||||||
#: ../vnc.html:233
|
#: ../vnc.html:231
|
||||||
msgid "None"
|
msgid "Scaling mode:"
|
||||||
msgstr "Ingen"
|
msgstr "Skalningsläge:"
|
||||||
|
|
||||||
#: ../vnc.html:234
|
#: ../vnc.html:233
|
||||||
msgid "Local scaling"
|
msgid "None"
|
||||||
msgstr "Lokal skalning"
|
msgstr "Ingen"
|
||||||
|
|
||||||
#: ../vnc.html:235
|
#: ../vnc.html:234
|
||||||
msgid "Remote resizing"
|
msgid "Local scaling"
|
||||||
msgstr "Ändra storlek"
|
msgstr "Lokal skalning"
|
||||||
|
|
||||||
#: ../vnc.html:240
|
#: ../vnc.html:235
|
||||||
msgid "Advanced"
|
msgid "Remote resizing"
|
||||||
msgstr "Avancerat"
|
msgstr "Ändra storlek"
|
||||||
|
|
||||||
#: ../vnc.html:243
|
#: ../vnc.html:240
|
||||||
msgid "Quality:"
|
msgid "Advanced"
|
||||||
msgstr "Kvalitet:"
|
msgstr "Avancerat"
|
||||||
|
|
||||||
#: ../vnc.html:247
|
#: ../vnc.html:243
|
||||||
msgid "Compression level:"
|
msgid "Quality:"
|
||||||
msgstr "Kompressionsnivå:"
|
msgstr "Kvalitet:"
|
||||||
|
|
||||||
#: ../vnc.html:252
|
#: ../vnc.html:247
|
||||||
msgid "Repeater ID:"
|
msgid "Compression level:"
|
||||||
msgstr "Repeater-ID:"
|
msgstr "Kompressionsnivå:"
|
||||||
|
|
||||||
#: ../vnc.html:256
|
#: ../vnc.html:252
|
||||||
msgid "WebSocket"
|
msgid "Repeater ID:"
|
||||||
msgstr "WebSocket"
|
msgstr "Repeater-ID:"
|
||||||
|
|
||||||
#: ../vnc.html:261
|
#: ../vnc.html:256
|
||||||
msgid "Encrypt"
|
msgid "WebSocket"
|
||||||
msgstr "Kryptera"
|
msgstr "WebSocket"
|
||||||
|
|
||||||
#: ../vnc.html:266
|
#: ../vnc.html:261
|
||||||
msgid "Host:"
|
msgid "Encrypt"
|
||||||
msgstr "Värd:"
|
msgstr "Kryptera"
|
||||||
|
|
||||||
#: ../vnc.html:270
|
#: ../vnc.html:266
|
||||||
msgid "Port:"
|
msgid "Host:"
|
||||||
msgstr "Port:"
|
msgstr "Värd:"
|
||||||
|
|
||||||
#: ../vnc.html:274
|
#: ../vnc.html:270
|
||||||
msgid "Path:"
|
msgid "Port:"
|
||||||
msgstr "Sökväg:"
|
msgstr "Port:"
|
||||||
|
|
||||||
#: ../vnc.html:283
|
#: ../vnc.html:274
|
||||||
msgid "Automatic reconnect"
|
msgid "Path:"
|
||||||
msgstr "Automatisk återanslutning"
|
msgstr "Sökväg:"
|
||||||
|
|
||||||
#: ../vnc.html:288
|
#: ../vnc.html:283
|
||||||
msgid "Reconnect delay (ms):"
|
msgid "Automatic reconnect"
|
||||||
msgstr "Fördröjning (ms):"
|
msgstr "Automatisk återanslutning"
|
||||||
|
|
||||||
#: ../vnc.html:295
|
#: ../vnc.html:288
|
||||||
msgid "Show dot when no cursor"
|
msgid "Reconnect delay (ms):"
|
||||||
msgstr "Visa prick när ingen muspekare finns"
|
msgstr "Fördröjning (ms):"
|
||||||
|
|
||||||
#: ../vnc.html:302
|
#: ../vnc.html:295
|
||||||
msgid "Logging:"
|
msgid "Show dot when no cursor"
|
||||||
msgstr "Loggning:"
|
msgstr "Visa prick när ingen muspekare finns"
|
||||||
|
|
||||||
#: ../vnc.html:311
|
#: ../vnc.html:302
|
||||||
msgid "Version:"
|
msgid "Logging:"
|
||||||
msgstr "Version:"
|
msgstr "Loggning:"
|
||||||
|
|
||||||
#: ../vnc.html:319
|
#: ../vnc.html:311
|
||||||
msgid "Disconnect"
|
msgid "Version:"
|
||||||
msgstr "Koppla från"
|
msgstr "Version:"
|
||||||
|
|
||||||
#: ../vnc.html:342
|
#: ../vnc.html:319
|
||||||
msgid "Connect"
|
msgid "Disconnect"
|
||||||
msgstr "Anslut"
|
msgstr "Koppla ifrån"
|
||||||
|
|
||||||
#: ../vnc.html:351
|
#: ../vnc.html:342
|
||||||
msgid "Server identity"
|
msgid "Connect"
|
||||||
msgstr "Server-identitet"
|
msgstr "Anslut"
|
||||||
|
|
||||||
#: ../vnc.html:354
|
#: ../vnc.html:351
|
||||||
msgid "The server has provided the following identifying information:"
|
msgid "Server identity"
|
||||||
msgstr "Servern har gett följande identifierande information:"
|
msgstr "Serveridentitet"
|
||||||
|
|
||||||
#: ../vnc.html:357
|
#: ../vnc.html:354
|
||||||
msgid "Fingerprint:"
|
msgid "The server has provided the following identifying information:"
|
||||||
msgstr "Fingeravtryck:"
|
msgstr "Servern har gett följande identifierande information:"
|
||||||
|
|
||||||
#: ../vnc.html:361
|
#: ../vnc.html:357
|
||||||
msgid ""
|
msgid "Fingerprint:"
|
||||||
"Please verify that the information is correct and press \"Approve\". "
|
msgstr "Fingeravtryck:"
|
||||||
"Otherwise press \"Reject\"."
|
|
||||||
msgstr ""
|
#: ../vnc.html:361
|
||||||
"Kontrollera att informationen är korrekt och tryck sedan \"Godkänn\". Tryck "
|
msgid ""
|
||||||
"annars \"Neka\"."
|
"Please verify that the information is correct and press \"Approve\". "
|
||||||
|
"Otherwise press \"Reject\"."
|
||||||
#: ../vnc.html:366
|
msgstr ""
|
||||||
msgid "Approve"
|
"Kontrollera att informationen är korrekt och tryck sedan \"Godkänn\". Tryck "
|
||||||
msgstr "Godkänn"
|
"annars \"Avvisa\"."
|
||||||
|
|
||||||
#: ../vnc.html:367
|
#: ../vnc.html:366
|
||||||
msgid "Reject"
|
msgid "Approve"
|
||||||
msgstr "Neka"
|
msgstr "Godkänn"
|
||||||
|
|
||||||
#: ../vnc.html:375
|
#: ../vnc.html:367
|
||||||
msgid "Credentials"
|
msgid "Reject"
|
||||||
msgstr "Användaruppgifter"
|
msgstr "Avvisa"
|
||||||
|
|
||||||
#: ../vnc.html:379
|
#: ../vnc.html:375
|
||||||
msgid "Username:"
|
msgid "Credentials"
|
||||||
msgstr "Användarnamn:"
|
msgstr "Användaruppgifter"
|
||||||
|
|
||||||
#: ../vnc.html:383
|
#: ../vnc.html:379
|
||||||
msgid "Password:"
|
msgid "Username:"
|
||||||
msgstr "Lösenord:"
|
msgstr "Användarnamn:"
|
||||||
|
|
||||||
#: ../vnc.html:387
|
#: ../vnc.html:383
|
||||||
msgid "Send credentials"
|
msgid "Password:"
|
||||||
msgstr "Skicka användaruppgifter"
|
msgstr "Lösenord:"
|
||||||
|
|
||||||
#: ../vnc.html:396
|
#: ../vnc.html:387
|
||||||
msgid "Cancel"
|
msgid "Send credentials"
|
||||||
msgstr "Avbryt"
|
msgstr "Skicka användaruppgifter"
|
||||||
|
|
||||||
#~ msgid "Must set host"
|
#: ../vnc.html:396
|
||||||
#~ msgstr "Du måste specifiera en värd"
|
msgid "Cancel"
|
||||||
|
msgstr "Avbryt"
|
||||||
#~ msgid "HTTPS is required for full functionality"
|
|
||||||
#~ msgstr "HTTPS krävs för full funktionalitet"
|
#~ msgid "Must set host"
|
||||||
|
#~ msgstr "Du måste specifiera en värd"
|
||||||
#~ msgid "Clear"
|
|
||||||
#~ msgstr "Rensa"
|
#~ msgid "HTTPS is required for full functionality"
|
||||||
|
#~ msgstr "HTTPS krävs för full funktionalitet"
|
||||||
|
|
||||||
|
#~ msgid "Clear"
|
||||||
|
#~ msgstr "Rensa"
|
||||||
|
|
|
||||||
|
|
@ -5,9 +5,9 @@
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { program } = require('commander');
|
import { program } from 'commander';
|
||||||
const jsdom = require("jsdom");
|
import jsdom from 'jsdom';
|
||||||
const fs = require("fs");
|
import fs from 'fs';
|
||||||
|
|
||||||
program
|
program
|
||||||
.argument('<INPUT...>')
|
.argument('<INPUT...>')
|
||||||
|
|
@ -106,7 +106,7 @@ let output = "";
|
||||||
|
|
||||||
for (let str in strings) {
|
for (let str in strings) {
|
||||||
output += "#:";
|
output += "#:";
|
||||||
for (location in strings[str]) {
|
for (let location in strings[str]) {
|
||||||
output += " " + location;
|
output += " " + location;
|
||||||
}
|
}
|
||||||
output += "\n";
|
output += "\n";
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import * as chai from '../node_modules/chai/chai.js';
|
import * as chai from '../node_modules/chai/index.js';
|
||||||
import sinon from '../node_modules/sinon/pkg/sinon-esm.js';
|
import sinon from '../node_modules/sinon/pkg/sinon-esm.js';
|
||||||
import sinonChai from '../node_modules/sinon-chai/lib/sinon-chai.js';
|
import sinonChai from '../node_modules/sinon-chai/lib/sinon-chai.js';
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,74 @@
|
||||||
import { isMac, isWindows, isIOS, isAndroid, isChromeOS,
|
import { isMac, isWindows, isIOS, isAndroid, isChromeOS,
|
||||||
isSafari, isFirefox, isChrome, isChromium, isOpera, isEdge,
|
isSafari, isFirefox, isChrome, isChromium, isOpera, isEdge,
|
||||||
isGecko, isWebKit, isBlink } from '../core/util/browser.js';
|
isGecko, isWebKit, isBlink,
|
||||||
|
browserAsyncClipboardSupport } from '../core/util/browser.js';
|
||||||
|
|
||||||
|
describe('Async clipboard', function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
sinon.stub(navigator, "clipboard").value({
|
||||||
|
writeText: sinon.stub(),
|
||||||
|
readText: sinon.stub(),
|
||||||
|
});
|
||||||
|
sinon.stub(navigator, "permissions").value({
|
||||||
|
query: sinon.stub().resolves({ state: "granted" })
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
sinon.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("queries permissions with correct parameters", async function () {
|
||||||
|
const queryStub = navigator.permissions.query;
|
||||||
|
await browserAsyncClipboardSupport();
|
||||||
|
expect(queryStub.firstCall).to.have.been.calledWithExactly({
|
||||||
|
name: "clipboard-write",
|
||||||
|
allowWithoutGesture: true
|
||||||
|
});
|
||||||
|
expect(queryStub.secondCall).to.have.been.calledWithExactly({
|
||||||
|
name: "clipboard-read",
|
||||||
|
allowWithoutGesture: false
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is available when API present and permissions granted", async function () {
|
||||||
|
navigator.permissions.query.resolves({ state: "granted" });
|
||||||
|
const result = await browserAsyncClipboardSupport();
|
||||||
|
expect(result).to.equal('available');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is available when API present and permissions yield 'prompt'", async function () {
|
||||||
|
navigator.permissions.query.resolves({ state: "prompt" });
|
||||||
|
const result = await browserAsyncClipboardSupport();
|
||||||
|
expect(result).to.equal('available');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is unavailable when permissions denied", async function () {
|
||||||
|
navigator.permissions.query.resolves({ state: "denied" });
|
||||||
|
const result = await browserAsyncClipboardSupport();
|
||||||
|
expect(result).to.equal('denied');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is unavailable when permissions API fails", async function () {
|
||||||
|
navigator.permissions.query.rejects(new Error("fail"));
|
||||||
|
const result = await browserAsyncClipboardSupport();
|
||||||
|
expect(result).to.equal('unsupported');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is unavailable when write text API missing", async function () {
|
||||||
|
navigator.clipboard.writeText = undefined;
|
||||||
|
const result = await browserAsyncClipboardSupport();
|
||||||
|
expect(result).to.equal('unsupported');
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is unavailable when read text API missing", async function () {
|
||||||
|
navigator.clipboard.readText = undefined;
|
||||||
|
const result = await browserAsyncClipboardSupport();
|
||||||
|
expect(result).to.equal('unsupported');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('OS detection', function () {
|
describe('OS detection', function () {
|
||||||
let origNavigator;
|
let origNavigator;
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,121 @@
|
||||||
|
import AsyncClipboard from '../core/clipboard.js';
|
||||||
|
|
||||||
|
describe('Async Clipboard', function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
let targetMock;
|
||||||
|
let clipboard;
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
|
sinon.stub(navigator, "clipboard").value({
|
||||||
|
writeText: sinon.stub().resolves(),
|
||||||
|
readText: sinon.stub().resolves(),
|
||||||
|
});
|
||||||
|
|
||||||
|
sinon.stub(navigator, "permissions").value({
|
||||||
|
query: sinon.stub(),
|
||||||
|
});
|
||||||
|
|
||||||
|
targetMock = document.createElement("canvas");
|
||||||
|
clipboard = new AsyncClipboard(targetMock);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
sinon.restore();
|
||||||
|
targetMock = null;
|
||||||
|
clipboard = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
function stubClipboardPermissions(state) {
|
||||||
|
navigator.permissions.query
|
||||||
|
.withArgs({ name: 'clipboard-write', allowWithoutGesture: true })
|
||||||
|
.resolves({ state: state });
|
||||||
|
navigator.permissions.query
|
||||||
|
.withArgs({ name: 'clipboard-read', allowWithoutGesture: false })
|
||||||
|
.resolves({ state: state });
|
||||||
|
}
|
||||||
|
|
||||||
|
function nextTick() {
|
||||||
|
return new Promise(resolve => setTimeout(resolve, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
it('grab() adds listener if permissions granted', async function () {
|
||||||
|
stubClipboardPermissions('granted');
|
||||||
|
|
||||||
|
const addListenerSpy = sinon.spy(targetMock, 'addEventListener');
|
||||||
|
clipboard.grab();
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
expect(addListenerSpy.calledWith('focus')).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('grab() does not add listener if permissions denied', async function () {
|
||||||
|
stubClipboardPermissions('denied');
|
||||||
|
|
||||||
|
const addListenerSpy = sinon.spy(targetMock, 'addEventListener');
|
||||||
|
clipboard.grab();
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
expect(addListenerSpy.calledWith('focus')).to.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('focus event triggers onpaste() if permissions granted', async function () {
|
||||||
|
stubClipboardPermissions('granted');
|
||||||
|
|
||||||
|
const text = 'hello clipboard world';
|
||||||
|
navigator.clipboard.readText.resolves(text);
|
||||||
|
|
||||||
|
const spyPromise = new Promise(resolve => clipboard.onpaste = resolve);
|
||||||
|
|
||||||
|
clipboard.grab();
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
targetMock.dispatchEvent(new Event('focus'));
|
||||||
|
|
||||||
|
const res = await spyPromise;
|
||||||
|
expect(res).to.equal(text);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('focus event does not trigger onpaste() if permissions denied', async function () {
|
||||||
|
stubClipboardPermissions('denied');
|
||||||
|
|
||||||
|
const text = 'should not read';
|
||||||
|
navigator.clipboard.readText.resolves(text);
|
||||||
|
|
||||||
|
clipboard.onpaste = sinon.spy();
|
||||||
|
|
||||||
|
clipboard.grab();
|
||||||
|
|
||||||
|
await nextTick();
|
||||||
|
|
||||||
|
targetMock.dispatchEvent(new Event('focus'));
|
||||||
|
|
||||||
|
expect(clipboard.onpaste.called).to.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('writeClipboard() calls navigator.clipboard.writeText() if permissions granted', async function () {
|
||||||
|
stubClipboardPermissions('granted');
|
||||||
|
clipboard._isAvailable = true;
|
||||||
|
|
||||||
|
const text = 'writing to clipboard';
|
||||||
|
const result = clipboard.writeClipboard(text);
|
||||||
|
|
||||||
|
expect(navigator.clipboard.writeText.calledWith(text)).to.be.true;
|
||||||
|
expect(result).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('writeClipboard() does not call navigator.clipboard.writeText() if permissions denied', async function () {
|
||||||
|
stubClipboardPermissions('denied');
|
||||||
|
clipboard._isAvailable = false;
|
||||||
|
|
||||||
|
const text = 'should not write';
|
||||||
|
const result = clipboard.writeClipboard(text);
|
||||||
|
|
||||||
|
expect(navigator.clipboard.writeText.called).to.be.false;
|
||||||
|
expect(result).to.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -384,10 +384,11 @@ describe('Display/Canvas helper', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should draw an image from an image object on type "img" (if complete)', function () {
|
it('should draw an image from an image object on type "img" (if complete)', function () {
|
||||||
|
const img = { complete: true };
|
||||||
display.drawImage = sinon.spy();
|
display.drawImage = sinon.spy();
|
||||||
display._renderQPush({ type: 'img', x: 3, y: 4, img: { complete: true } });
|
display._renderQPush({ type: 'img', x: 3, y: 4, img: img });
|
||||||
expect(display.drawImage).to.have.been.calledOnce;
|
expect(display.drawImage).to.have.been.calledOnce;
|
||||||
expect(display.drawImage).to.have.been.calledWith({ complete: true }, 3, 4);
|
expect(display.drawImage).to.have.been.calledWith(img, 3, 4);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -3467,17 +3467,48 @@ describe('Remote Frame Buffer protocol client', function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Normal clipboard handling receive', function () {
|
describe('Normal clipboard handling receive', function () {
|
||||||
it('should fire the clipboard callback with the retrieved text on ServerCutText', function () {
|
it('should not dispatch a clipboard event following successful async write clipboard', async function () {
|
||||||
|
client._viewOnly = false;
|
||||||
|
client._asyncClipboard = {
|
||||||
|
writeClipboard: sinon.stub().returns(true),
|
||||||
|
};
|
||||||
const expectedStr = 'cheese!';
|
const expectedStr = 'cheese!';
|
||||||
const data = [3, 0, 0, 0];
|
const data = [3, 0, 0, 0];
|
||||||
push32(data, expectedStr.length);
|
push32(data, expectedStr.length);
|
||||||
for (let i = 0; i < expectedStr.length; i++) { data.push(expectedStr.charCodeAt(i)); }
|
for (let i = 0; i < expectedStr.length; i++) { data.push(expectedStr.charCodeAt(i)); }
|
||||||
const spy = sinon.spy();
|
|
||||||
client.addEventListener("clipboard", spy);
|
const dispatchEventSpy = sinon.spy(client, 'dispatchEvent');
|
||||||
|
|
||||||
client._sock._websocket._receiveData(new Uint8Array(data));
|
client._sock._websocket._receiveData(new Uint8Array(data));
|
||||||
expect(spy).to.have.been.calledOnce;
|
|
||||||
expect(spy.args[0][0].detail.text).to.equal(expectedStr);
|
expect(client._asyncClipboard.writeClipboard.calledOnceWith(
|
||||||
|
expectedStr
|
||||||
|
)).to.be.true;
|
||||||
|
expect(dispatchEventSpy.calledWith(
|
||||||
|
new CustomEvent("clipboard", {detail: {expectedStr: expectedStr}})
|
||||||
|
)).to.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should dispatch a clipboard event following unsuccessful async write clipboard', async function () {
|
||||||
|
client._viewOnly = false;
|
||||||
|
client._asyncClipboard = {
|
||||||
|
writeClipboard: sinon.stub().returns(false),
|
||||||
|
};
|
||||||
|
const expectedStr = 'cheese!';
|
||||||
|
const data = [3, 0, 0, 0];
|
||||||
|
push32(data, expectedStr.length);
|
||||||
|
for (let i = 0; i < expectedStr.length; i++) { data.push(expectedStr.charCodeAt(i)); }
|
||||||
|
|
||||||
|
const dispatchEventSpy = sinon.spy(client, 'dispatchEvent');
|
||||||
|
|
||||||
|
client._sock._websocket._receiveData(new Uint8Array(data));
|
||||||
|
|
||||||
|
expect(client._asyncClipboard.writeClipboard.calledOnceWith(
|
||||||
|
expectedStr
|
||||||
|
)).to.be.true;
|
||||||
|
expect(dispatchEventSpy.calledOnceWith(
|
||||||
|
new CustomEvent("clipboard", {detail: {expectedStr: expectedStr}})
|
||||||
|
)).to.be.true;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -3530,8 +3561,71 @@ describe('Remote Frame Buffer protocol client', function () {
|
||||||
client._sock._websocket._receiveData(new Uint8Array(data));
|
client._sock._websocket._receiveData(new Uint8Array(data));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not dispatch a clipboard event following successful async write clipboard', async function () {
|
||||||
|
client._viewOnly = false;
|
||||||
|
client._asyncClipboard = {
|
||||||
|
writeClipboard: sinon.stub().returns(true),
|
||||||
|
};
|
||||||
|
let expectedData = "Schnitzel";
|
||||||
|
let data = [3, 0, 0, 0];
|
||||||
|
const flags = [0x10, 0x00, 0x00, 0x01];
|
||||||
|
|
||||||
|
let text = encodeUTF8("Schnitzel");
|
||||||
|
let deflatedText = deflateWithSize(text);
|
||||||
|
|
||||||
|
// How much data we are sending.
|
||||||
|
push32(data, toUnsigned32bit(-(4 + deflatedText.length)));
|
||||||
|
|
||||||
|
data = data.concat(flags);
|
||||||
|
data = data.concat(Array.from(deflatedText));
|
||||||
|
|
||||||
|
const dispatchEventSpy = sinon.spy(client, 'dispatchEvent');
|
||||||
|
|
||||||
|
client._sock._websocket._receiveData(new Uint8Array(data));
|
||||||
|
|
||||||
|
expect(client._asyncClipboard.writeClipboard.calledOnceWith(
|
||||||
|
expectedData
|
||||||
|
)).to.be.true;
|
||||||
|
expect(dispatchEventSpy.calledOnceWith(
|
||||||
|
new CustomEvent("clipboard", {detail: {expectedData: expectedData}})
|
||||||
|
)).to.be.false;
|
||||||
|
});
|
||||||
|
it('should dispatch a clipboard event following unsuccessful async write clipboard', async function () {
|
||||||
|
client._viewOnly = false;
|
||||||
|
client._asyncClipboard = {
|
||||||
|
writeClipboard: sinon.stub().returns(false),
|
||||||
|
};
|
||||||
|
let expectedData = "Potatoes";
|
||||||
|
let data = [3, 0, 0, 0];
|
||||||
|
const flags = [0x10, 0x00, 0x00, 0x01];
|
||||||
|
|
||||||
|
let text = encodeUTF8("Potatoes");
|
||||||
|
let deflatedText = deflateWithSize(text);
|
||||||
|
|
||||||
|
// How much data we are sending.
|
||||||
|
push32(data, toUnsigned32bit(-(4 + deflatedText.length)));
|
||||||
|
|
||||||
|
data = data.concat(flags);
|
||||||
|
data = data.concat(Array.from(deflatedText));
|
||||||
|
|
||||||
|
const dispatchEventSpy = sinon.spy(client, 'dispatchEvent');
|
||||||
|
|
||||||
|
client._sock._websocket._receiveData(new Uint8Array(data));
|
||||||
|
|
||||||
|
expect(client._asyncClipboard.writeClipboard.calledOnceWith(
|
||||||
|
expectedData
|
||||||
|
)).to.be.true;
|
||||||
|
expect(dispatchEventSpy.calledOnceWith(
|
||||||
|
new CustomEvent("clipboard", {detail: {expectedData: expectedData}})
|
||||||
|
)).to.be.true;
|
||||||
|
});
|
||||||
|
|
||||||
describe('Handle Provide', function () {
|
describe('Handle Provide', function () {
|
||||||
it('should update clipboard with correct Unicode data from a Provide message', function () {
|
it('should update clipboard with correct Unicode data from a Provide message', async function () {
|
||||||
|
client._viewOnly = false;
|
||||||
|
client._asyncClipboard = {
|
||||||
|
writeClipboard: sinon.stub().returns(false),
|
||||||
|
};
|
||||||
let expectedData = "Aå漢字!";
|
let expectedData = "Aå漢字!";
|
||||||
let data = [3, 0, 0, 0];
|
let data = [3, 0, 0, 0];
|
||||||
const flags = [0x10, 0x00, 0x00, 0x01];
|
const flags = [0x10, 0x00, 0x00, 0x01];
|
||||||
|
|
@ -3545,16 +3639,23 @@ describe('Remote Frame Buffer protocol client', function () {
|
||||||
data = data.concat(flags);
|
data = data.concat(flags);
|
||||||
data = data.concat(Array.from(deflatedText));
|
data = data.concat(Array.from(deflatedText));
|
||||||
|
|
||||||
const spy = sinon.spy();
|
const dispatchEventSpy = sinon.spy(client, 'dispatchEvent');
|
||||||
client.addEventListener("clipboard", spy);
|
|
||||||
|
|
||||||
client._sock._websocket._receiveData(new Uint8Array(data));
|
client._sock._websocket._receiveData(new Uint8Array(data));
|
||||||
expect(spy).to.have.been.calledOnce;
|
|
||||||
expect(spy.args[0][0].detail.text).to.equal(expectedData);
|
expect(client._asyncClipboard.writeClipboard.calledOnceWith(
|
||||||
client.removeEventListener("clipboard", spy);
|
expectedData
|
||||||
|
)).to.be.true;
|
||||||
|
expect(dispatchEventSpy.calledOnceWith(
|
||||||
|
new CustomEvent("clipboard", {detail: {expectedData: expectedData}})
|
||||||
|
)).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should update clipboard with correct escape characters from a Provide message ', function () {
|
it('should update clipboard with correct escape characters from a Provide message ', async function () {
|
||||||
|
client._viewOnly = false;
|
||||||
|
client._asyncClipboard = {
|
||||||
|
writeClipboard: sinon.stub().returns(false),
|
||||||
|
};
|
||||||
let expectedData = "Oh\nmy\n!";
|
let expectedData = "Oh\nmy\n!";
|
||||||
let data = [3, 0, 0, 0];
|
let data = [3, 0, 0, 0];
|
||||||
const flags = [0x10, 0x00, 0x00, 0x01];
|
const flags = [0x10, 0x00, 0x00, 0x01];
|
||||||
|
|
@ -3569,16 +3670,23 @@ describe('Remote Frame Buffer protocol client', function () {
|
||||||
data = data.concat(flags);
|
data = data.concat(flags);
|
||||||
data = data.concat(Array.from(deflatedText));
|
data = data.concat(Array.from(deflatedText));
|
||||||
|
|
||||||
const spy = sinon.spy();
|
const dispatchEventSpy = sinon.spy(client, 'dispatchEvent');
|
||||||
client.addEventListener("clipboard", spy);
|
|
||||||
|
|
||||||
client._sock._websocket._receiveData(new Uint8Array(data));
|
client._sock._websocket._receiveData(new Uint8Array(data));
|
||||||
expect(spy).to.have.been.calledOnce;
|
|
||||||
expect(spy.args[0][0].detail.text).to.equal(expectedData);
|
expect(client._asyncClipboard.writeClipboard.calledOnceWith(
|
||||||
client.removeEventListener("clipboard", spy);
|
expectedData
|
||||||
|
)).to.be.true;
|
||||||
|
expect(dispatchEventSpy.calledOnceWith(
|
||||||
|
new CustomEvent("clipboard", {detail: {expectedData: expectedData}})
|
||||||
|
)).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to handle large Provide messages', function () {
|
it('should be able to handle large Provide messages', async function () {
|
||||||
|
client._viewOnly = false;
|
||||||
|
client._asyncClipboard = {
|
||||||
|
writeClipboard: sinon.stub().returns(false),
|
||||||
|
};
|
||||||
let expectedData = "hello".repeat(100000);
|
let expectedData = "hello".repeat(100000);
|
||||||
let data = [3, 0, 0, 0];
|
let data = [3, 0, 0, 0];
|
||||||
const flags = [0x10, 0x00, 0x00, 0x01];
|
const flags = [0x10, 0x00, 0x00, 0x01];
|
||||||
|
|
@ -3593,13 +3701,16 @@ describe('Remote Frame Buffer protocol client', function () {
|
||||||
data = data.concat(flags);
|
data = data.concat(flags);
|
||||||
data = data.concat(Array.from(deflatedText));
|
data = data.concat(Array.from(deflatedText));
|
||||||
|
|
||||||
const spy = sinon.spy();
|
const dispatchEventSpy = sinon.spy(client, 'dispatchEvent');
|
||||||
client.addEventListener("clipboard", spy);
|
|
||||||
|
|
||||||
client._sock._websocket._receiveData(new Uint8Array(data));
|
client._sock._websocket._receiveData(new Uint8Array(data));
|
||||||
expect(spy).to.have.been.calledOnce;
|
|
||||||
expect(spy.args[0][0].detail.text).to.equal(expectedData);
|
expect(client._asyncClipboard.writeClipboard.calledOnceWith(
|
||||||
client.removeEventListener("clipboard", spy);
|
expectedData
|
||||||
|
)).to.be.true;
|
||||||
|
expect(dispatchEventSpy.calledOnceWith(
|
||||||
|
new CustomEvent("clipboard", {detail: {expectedData: expectedData}})
|
||||||
|
)).to.be.true;
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,20 @@ describe('Websock', function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('rQlen())', function () {
|
||||||
|
it('should return the number of buffered bytes in the receive queue', function () {
|
||||||
|
websock._receiveData(new Uint8Array([0xab, 0xcd, 0x12, 0x34,
|
||||||
|
0x88, 0xee, 0x11, 0x33]));
|
||||||
|
expect(sock.rQlen()).to.equal(8);
|
||||||
|
sock.rQshift8();
|
||||||
|
expect(sock.rQlen()).to.equal(7);
|
||||||
|
sock.rQshift16();
|
||||||
|
expect(sock.rQlen()).to.equal(5);
|
||||||
|
sock.rQshift32();
|
||||||
|
expect(sock.rQlen()).to.equal(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('rQshiftStr', function () {
|
describe('rQshiftStr', function () {
|
||||||
it('should shift the given number of bytes off of the receive queue and return a string', function () {
|
it('should shift the given number of bytes off of the receive queue and return a string', function () {
|
||||||
websock._receiveData(new Uint8Array([0xab, 0xcd, 0x12, 0x34,
|
websock._receiveData(new Uint8Array([0xab, 0xcd, 0x12, 0x34,
|
||||||
|
|
|
||||||
140
utils/convert.js
140
utils/convert.js
|
|
@ -1,140 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
const path = require('path');
|
|
||||||
const { program } = require('commander');
|
|
||||||
const fs = require('fs');
|
|
||||||
const fse = require('fs-extra');
|
|
||||||
const babel = require('@babel/core');
|
|
||||||
|
|
||||||
program
|
|
||||||
.option('-m, --with-source-maps [type]', 'output source maps when not generating a bundled app (type may be empty for external source maps, inline for inline source maps, or both) ')
|
|
||||||
.option('--clean', 'clear the lib folder before building')
|
|
||||||
.parse(process.argv);
|
|
||||||
|
|
||||||
// the various important paths
|
|
||||||
const paths = {
|
|
||||||
main: path.resolve(__dirname, '..'),
|
|
||||||
core: path.resolve(__dirname, '..', 'core'),
|
|
||||||
vendor: path.resolve(__dirname, '..', 'vendor'),
|
|
||||||
libDirBase: path.resolve(__dirname, '..', 'lib'),
|
|
||||||
};
|
|
||||||
|
|
||||||
// util.promisify requires Node.js 8.x, so we have our own
|
|
||||||
function promisify(original) {
|
|
||||||
return function promiseWrap() {
|
|
||||||
const args = Array.prototype.slice.call(arguments);
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
original.apply(this, args.concat((err, value) => {
|
|
||||||
if (err) return reject(err);
|
|
||||||
resolve(value);
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const writeFile = promisify(fs.writeFile);
|
|
||||||
|
|
||||||
const readdir = promisify(fs.readdir);
|
|
||||||
const lstat = promisify(fs.lstat);
|
|
||||||
|
|
||||||
const ensureDir = promisify(fse.ensureDir);
|
|
||||||
|
|
||||||
const babelTransformFile = promisify(babel.transformFile);
|
|
||||||
|
|
||||||
// walkDir *recursively* walks directories trees,
|
|
||||||
// calling the callback for all normal files found.
|
|
||||||
function walkDir(basePath, cb, filter) {
|
|
||||||
return readdir(basePath)
|
|
||||||
.then((files) => {
|
|
||||||
const paths = files.map(filename => path.join(basePath, filename));
|
|
||||||
return Promise.all(paths.map(filepath => lstat(filepath)
|
|
||||||
.then((stats) => {
|
|
||||||
if (filter !== undefined && !filter(filepath, stats)) return;
|
|
||||||
|
|
||||||
if (stats.isSymbolicLink()) return;
|
|
||||||
if (stats.isFile()) return cb(filepath);
|
|
||||||
if (stats.isDirectory()) return walkDir(filepath, cb, filter);
|
|
||||||
})));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeLibFiles(sourceMaps) {
|
|
||||||
// NB: we need to make a copy of babelOpts, since babel sets some defaults on it
|
|
||||||
const babelOpts = () => ({
|
|
||||||
plugins: [],
|
|
||||||
presets: [
|
|
||||||
[ '@babel/preset-env',
|
|
||||||
{ modules: 'commonjs' } ]
|
|
||||||
],
|
|
||||||
ast: false,
|
|
||||||
sourceMaps: sourceMaps,
|
|
||||||
});
|
|
||||||
|
|
||||||
fse.ensureDirSync(paths.libDirBase);
|
|
||||||
|
|
||||||
const outFiles = [];
|
|
||||||
|
|
||||||
const handleDir = (vendorRewrite, inPathBase, filename) => Promise.resolve()
|
|
||||||
.then(() => {
|
|
||||||
const outPath = path.join(paths.libDirBase, path.relative(inPathBase, filename));
|
|
||||||
|
|
||||||
if (path.extname(filename) !== '.js') {
|
|
||||||
return; // skip non-javascript files
|
|
||||||
}
|
|
||||||
return Promise.resolve()
|
|
||||||
.then(() => ensureDir(path.dirname(outPath)))
|
|
||||||
.then(() => {
|
|
||||||
const opts = babelOpts();
|
|
||||||
// Adjust for the fact that we move the core files relative
|
|
||||||
// to the vendor directory
|
|
||||||
if (vendorRewrite) {
|
|
||||||
opts.plugins.push(["import-redirect",
|
|
||||||
{"root": paths.libDirBase,
|
|
||||||
"redirect": { "vendor/(.+)": "./vendor/$1"}}]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return babelTransformFile(filename, opts)
|
|
||||||
.then((res) => {
|
|
||||||
console.log(`Writing ${outPath}`);
|
|
||||||
const {map} = res;
|
|
||||||
let {code} = res;
|
|
||||||
if (sourceMaps === true) {
|
|
||||||
// append URL for external source map
|
|
||||||
code += `\n//# sourceMappingURL=${path.basename(outPath)}.map\n`;
|
|
||||||
}
|
|
||||||
outFiles.push(`${outPath}`);
|
|
||||||
return writeFile(outPath, code)
|
|
||||||
.then(() => {
|
|
||||||
if (sourceMaps === true || sourceMaps === 'both') {
|
|
||||||
console.log(` and ${outPath}.map`);
|
|
||||||
outFiles.push(`${outPath}.map`);
|
|
||||||
return writeFile(`${outPath}.map`, JSON.stringify(map));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
Promise.resolve()
|
|
||||||
.then(() => {
|
|
||||||
const handler = handleDir.bind(null, false, paths.main);
|
|
||||||
return walkDir(paths.vendor, handler);
|
|
||||||
})
|
|
||||||
.then(() => {
|
|
||||||
const handler = handleDir.bind(null, true, paths.core);
|
|
||||||
return walkDir(paths.core, handler);
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error(`Failure converting modules: ${err}`);
|
|
||||||
process.exit(1);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let options = program.opts();
|
|
||||||
|
|
||||||
if (options.clean) {
|
|
||||||
console.log(`Removing ${paths.libDirBase}`);
|
|
||||||
fse.removeSync(paths.libDirBase);
|
|
||||||
}
|
|
||||||
|
|
||||||
makeLibFiles(options.withSourceMaps);
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
|
|
||||||
let showHelp = process.argv.length === 2;
|
let showHelp = process.argv.length === 2;
|
||||||
let filename;
|
let filename;
|
||||||
|
|
|
||||||
|
|
@ -181,10 +181,8 @@ if [[ -d ${HERE}/websockify ]]; then
|
||||||
|
|
||||||
echo "Using local websockify at $WEBSOCKIFY"
|
echo "Using local websockify at $WEBSOCKIFY"
|
||||||
else
|
else
|
||||||
WEBSOCKIFY_FROMSYSTEM=$(which websockify 2>/dev/null)
|
WEBSOCKIFY_FROMSYSTEM=$(type -P websockify 2>/dev/null)
|
||||||
WEBSOCKIFY_FROMSNAP=${HERE}/../usr/bin/python2-websockify
|
|
||||||
[ -f $WEBSOCKIFY_FROMSYSTEM ] && WEBSOCKIFY=$WEBSOCKIFY_FROMSYSTEM
|
[ -f $WEBSOCKIFY_FROMSYSTEM ] && WEBSOCKIFY=$WEBSOCKIFY_FROMSYSTEM
|
||||||
[ -f $WEBSOCKIFY_FROMSNAP ] && WEBSOCKIFY=$WEBSOCKIFY_FROMSNAP
|
|
||||||
|
|
||||||
if [ ! -f "$WEBSOCKIFY" ]; then
|
if [ ! -f "$WEBSOCKIFY" ]; then
|
||||||
echo "No installed websockify, attempting to clone websockify..."
|
echo "No installed websockify, attempting to clone websockify..."
|
||||||
|
|
|
||||||
7
vnc.html
7
vnc.html
|
|
@ -296,6 +296,13 @@
|
||||||
Show dot when no cursor
|
Show dot when no cursor
|
||||||
</label>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<label>
|
||||||
|
<input id="noVNC_setting_notify_clipboard_received" type="checkbox"
|
||||||
|
class="toggle">
|
||||||
|
Show clipboard notification
|
||||||
|
</label>
|
||||||
|
</li>
|
||||||
<li><hr></li>
|
<li><hr></li>
|
||||||
<!-- Logging selection dropdown -->
|
<!-- Logging selection dropdown -->
|
||||||
<li>
|
<li>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue