diff --git a/public/novnc/LICENSE.txt b/public/novnc/LICENSE.txt index ee81d202..37efdcdb 100644 --- a/public/novnc/LICENSE.txt +++ b/public/novnc/LICENSE.txt @@ -1,4 +1,4 @@ -noVNC is Copyright (C) 2019 The noVNC Authors +noVNC is Copyright (C) 2022 The noVNC Authors (./AUTHORS) The noVNC core library files are licensed under the MPL 2.0 (Mozilla diff --git a/public/novnc/app/error-handler.js b/public/novnc/app/error-handler.js index 81a6cba8..67b63720 100644 --- a/public/novnc/app/error-handler.js +++ b/public/novnc/app/error-handler.js @@ -6,61 +6,74 @@ * See README.md for usage and integration instructions. */ -// NB: this should *not* be included as a module until we have -// native support in the browsers, so that our error handler -// can catch script-loading errors. +// Fallback for all uncought errors +function handleError(event, err) { + try { + const msg = document.getElementById('noVNC_fallback_errormsg'); -// No ES6 can be used in this file since it's used for the translation -/* eslint-disable prefer-arrow-callback */ - -(function _scope() { - "use strict"; - - // Fallback for all uncought errors - function handleError(event, err) { - try { - const msg = document.getElementById('noVNC_fallback_errormsg'); - - // Only show the initial error - if (msg.hasChildNodes()) { - return false; - } - - let div = document.createElement("div"); - div.classList.add('noVNC_message'); - div.appendChild(document.createTextNode(event.message)); - msg.appendChild(div); - - if (event.filename) { - div = document.createElement("div"); - div.className = 'noVNC_location'; - let text = event.filename; - if (event.lineno !== undefined) { - text += ":" + event.lineno; - if (event.colno !== undefined) { - text += ":" + event.colno; - } - } - div.appendChild(document.createTextNode(text)); - msg.appendChild(div); - } - - if (err && err.stack) { - div = document.createElement("div"); - div.className = 'noVNC_stack'; - div.appendChild(document.createTextNode(err.stack)); - msg.appendChild(div); - } - - document.getElementById('noVNC_fallback_error') - .classList.add("noVNC_open"); - } catch (exc) { - document.write("noVNC encountered an error."); + // Work around Firefox bug: + // https://bugzilla.mozilla.org/show_bug.cgi?id=1685038 + if (event.message === "ResizeObserver loop completed with undelivered notifications.") { + return false; } - // Don't return true since this would prevent the error - // from being printed to the browser console. - return false; + + // Only show the initial error + if (msg.hasChildNodes()) { + return false; + } + + let div = document.createElement("div"); + div.classList.add('noVNC_message'); + div.appendChild(document.createTextNode(event.message)); + msg.appendChild(div); + + if (event.filename) { + div = document.createElement("div"); + div.className = 'noVNC_location'; + let text = event.filename; + if (event.lineno !== undefined) { + text += ":" + event.lineno; + if (event.colno !== undefined) { + text += ":" + event.colno; + } + } + div.appendChild(document.createTextNode(text)); + msg.appendChild(div); + } + + if (err && err.stack) { + div = document.createElement("div"); + div.className = 'noVNC_stack'; + div.appendChild(document.createTextNode(err.stack)); + msg.appendChild(div); + } + + document.getElementById('noVNC_fallback_error') + .classList.add("noVNC_open"); + + } catch (exc) { + document.write("noVNC encountered an error."); } - window.addEventListener('error', function onerror(evt) { handleError(evt, evt.error); }); - window.addEventListener('unhandledrejection', function onreject(evt) { handleError(evt.reason, evt.reason); }); -})(); + + // Try to disable keyboard interaction, best effort + try { + // Remove focus from the currently focused element in order to + // prevent keyboard interaction from continuing + if (document.activeElement) { document.activeElement.blur(); } + + // Don't let any element be focusable when showing the error + let keyboardFocusable = 'a[href], button, input, textarea, select, details, [tabindex]'; + document.querySelectorAll(keyboardFocusable).forEach((elem) => { + elem.setAttribute("tabindex", "-1"); + }); + } catch (exc) { + // Do nothing + } + + // Don't return true since this would prevent the error + // from being printed to the browser console. + return false; +} + +window.addEventListener('error', evt => handleError(evt, evt.error)); +window.addEventListener('unhandledrejection', evt => handleError(evt.reason, evt.reason)); diff --git a/public/novnc/app/images/icons/Makefile b/public/novnc/app/images/icons/Makefile index be564b43..03eaed07 100644 --- a/public/novnc/app/images/icons/Makefile +++ b/public/novnc/app/images/icons/Makefile @@ -1,42 +1,42 @@ -ICONS := \ - novnc-16x16.png \ - novnc-24x24.png \ - novnc-32x32.png \ - novnc-48x48.png \ - novnc-64x64.png +BROWSER_SIZES := 16 24 32 48 64 +#ANDROID_SIZES := 72 96 144 192 +# FIXME: The ICO is limited to 8 icons due to a Chrome bug: +# https://bugs.chromium.org/p/chromium/issues/detail?id=1381393 +ANDROID_SIZES := 96 144 192 +WEB_ICON_SIZES := $(BROWSER_SIZES) $(ANDROID_SIZES) -ANDROID_LAUNCHER := \ - novnc-48x48.png \ - novnc-72x72.png \ - novnc-96x96.png \ - novnc-144x144.png \ - novnc-192x192.png +#IOS_1X_SIZES := 20 29 40 76 # No such devices exist anymore +IOS_2X_SIZES := 40 58 80 120 152 167 +IOS_3X_SIZES := 60 87 120 180 +ALL_IOS_SIZES := $(IOS_1X_SIZES) $(IOS_2X_SIZES) $(IOS_3X_SIZES) -IPHONE_LAUNCHER := \ - novnc-60x60.png \ - novnc-120x120.png - -IPAD_LAUNCHER := \ - novnc-76x76.png \ - novnc-152x152.png - -ALL_ICONS := $(ICONS) $(ANDROID_LAUNCHER) $(IPHONE_LAUNCHER) $(IPAD_LAUNCHER) +ALL_ICONS := \ + $(ALL_IOS_SIZES:%=novnc-ios-%.png) \ + novnc.ico all: $(ALL_ICONS) -novnc-16x16.png: novnc-icon-sm.svg - convert -density 90 \ - -background transparent "$<" "$@" -novnc-24x24.png: novnc-icon-sm.svg - convert -density 135 \ - -background transparent "$<" "$@" -novnc-32x32.png: novnc-icon-sm.svg - convert -density 180 \ - -background transparent "$<" "$@" +# Our testing shows that the ICO file need to be sorted in largest to +# smallest to get the apporpriate behviour +WEB_ICON_SIZES_REVERSE := $(shell echo $(WEB_ICON_SIZES) | tr ' ' '\n' | sort -nr | tr '\n' ' ') +WEB_BASE_ICONS := $(WEB_ICON_SIZES_REVERSE:%=novnc-%.png) +.INTERMEDIATE: $(WEB_BASE_ICONS) +novnc.ico: $(WEB_BASE_ICONS) + convert $(WEB_BASE_ICONS) "$@" + +# General conversion novnc-%.png: novnc-icon.svg - convert -density $$[`echo $* | cut -d x -f 1` * 90 / 48] \ - -background transparent "$<" "$@" + convert -depth 8 -background transparent \ + -size $*x$* "$(lastword $^)" "$@" + +# iOS icons use their own SVG +novnc-ios-%.png: novnc-ios-icon.svg + convert -depth 8 -background transparent \ + -size $*x$* "$(lastword $^)" "$@" + +# The smallest sizes are generated using a different SVG +novnc-16.png novnc-24.png novnc-32.png: novnc-icon-sm.svg clean: rm -f *.png diff --git a/public/novnc/app/images/icons/novnc-120x120.png b/public/novnc/app/images/icons/novnc-120x120.png deleted file mode 100644 index 40823efb..00000000 Binary files a/public/novnc/app/images/icons/novnc-120x120.png and /dev/null differ diff --git a/public/novnc/app/images/icons/novnc-144x144.png b/public/novnc/app/images/icons/novnc-144x144.png deleted file mode 100644 index eee71f11..00000000 Binary files a/public/novnc/app/images/icons/novnc-144x144.png and /dev/null differ diff --git a/public/novnc/app/images/icons/novnc-152x152.png b/public/novnc/app/images/icons/novnc-152x152.png deleted file mode 100644 index 0694b2de..00000000 Binary files a/public/novnc/app/images/icons/novnc-152x152.png and /dev/null differ diff --git a/public/novnc/app/images/icons/novnc-16x16.png b/public/novnc/app/images/icons/novnc-16x16.png deleted file mode 100644 index 42108f40..00000000 Binary files a/public/novnc/app/images/icons/novnc-16x16.png and /dev/null differ diff --git a/public/novnc/app/images/icons/novnc-192x192.png b/public/novnc/app/images/icons/novnc-192x192.png deleted file mode 100644 index ef9201f4..00000000 Binary files a/public/novnc/app/images/icons/novnc-192x192.png and /dev/null differ diff --git a/public/novnc/app/images/icons/novnc-24x24.png b/public/novnc/app/images/icons/novnc-24x24.png deleted file mode 100644 index 11061359..00000000 Binary files a/public/novnc/app/images/icons/novnc-24x24.png and /dev/null differ diff --git a/public/novnc/app/images/icons/novnc-32x32.png b/public/novnc/app/images/icons/novnc-32x32.png deleted file mode 100644 index ff00dc30..00000000 Binary files a/public/novnc/app/images/icons/novnc-32x32.png and /dev/null differ diff --git a/public/novnc/app/images/icons/novnc-48x48.png b/public/novnc/app/images/icons/novnc-48x48.png deleted file mode 100644 index f24cd6cc..00000000 Binary files a/public/novnc/app/images/icons/novnc-48x48.png and /dev/null differ diff --git a/public/novnc/app/images/icons/novnc-60x60.png b/public/novnc/app/images/icons/novnc-60x60.png deleted file mode 100644 index 06b0d609..00000000 Binary files a/public/novnc/app/images/icons/novnc-60x60.png and /dev/null differ diff --git a/public/novnc/app/images/icons/novnc-64x64.png b/public/novnc/app/images/icons/novnc-64x64.png deleted file mode 100644 index 6d0fb341..00000000 Binary files a/public/novnc/app/images/icons/novnc-64x64.png and /dev/null differ diff --git a/public/novnc/app/images/icons/novnc-72x72.png b/public/novnc/app/images/icons/novnc-72x72.png deleted file mode 100644 index 23163a22..00000000 Binary files a/public/novnc/app/images/icons/novnc-72x72.png and /dev/null differ diff --git a/public/novnc/app/images/icons/novnc-76x76.png b/public/novnc/app/images/icons/novnc-76x76.png deleted file mode 100644 index aef61c48..00000000 Binary files a/public/novnc/app/images/icons/novnc-76x76.png and /dev/null differ diff --git a/public/novnc/app/images/icons/novnc-96x96.png b/public/novnc/app/images/icons/novnc-96x96.png deleted file mode 100644 index 1a77c53f..00000000 Binary files a/public/novnc/app/images/icons/novnc-96x96.png and /dev/null differ diff --git a/public/novnc/app/images/icons/novnc-ios-120.png b/public/novnc/app/images/icons/novnc-ios-120.png new file mode 100644 index 00000000..8da7bab3 Binary files /dev/null and b/public/novnc/app/images/icons/novnc-ios-120.png differ diff --git a/public/novnc/app/images/icons/novnc-ios-152.png b/public/novnc/app/images/icons/novnc-ios-152.png new file mode 100644 index 00000000..60b2bcef Binary files /dev/null and b/public/novnc/app/images/icons/novnc-ios-152.png differ diff --git a/public/novnc/app/images/icons/novnc-ios-167.png b/public/novnc/app/images/icons/novnc-ios-167.png new file mode 100644 index 00000000..98fade2e Binary files /dev/null and b/public/novnc/app/images/icons/novnc-ios-167.png differ diff --git a/public/novnc/app/images/icons/novnc-ios-180.png b/public/novnc/app/images/icons/novnc-ios-180.png new file mode 100644 index 00000000..5d24df70 Binary files /dev/null and b/public/novnc/app/images/icons/novnc-ios-180.png differ diff --git a/public/novnc/app/images/icons/novnc-ios-40.png b/public/novnc/app/images/icons/novnc-ios-40.png new file mode 100644 index 00000000..cf14894d Binary files /dev/null and b/public/novnc/app/images/icons/novnc-ios-40.png differ diff --git a/public/novnc/app/images/icons/novnc-ios-58.png b/public/novnc/app/images/icons/novnc-ios-58.png new file mode 100644 index 00000000..f6dfbebd Binary files /dev/null and b/public/novnc/app/images/icons/novnc-ios-58.png differ diff --git a/public/novnc/app/images/icons/novnc-ios-60.png b/public/novnc/app/images/icons/novnc-ios-60.png new file mode 100644 index 00000000..8cda2953 Binary files /dev/null and b/public/novnc/app/images/icons/novnc-ios-60.png differ diff --git a/public/novnc/app/images/icons/novnc-ios-80.png b/public/novnc/app/images/icons/novnc-ios-80.png new file mode 100644 index 00000000..6c417c47 Binary files /dev/null and b/public/novnc/app/images/icons/novnc-ios-80.png differ diff --git a/public/novnc/app/images/icons/novnc-ios-87.png b/public/novnc/app/images/icons/novnc-ios-87.png new file mode 100644 index 00000000..4377d874 Binary files /dev/null and b/public/novnc/app/images/icons/novnc-ios-87.png differ diff --git a/public/novnc/app/images/icons/novnc-ios-icon.svg b/public/novnc/app/images/icons/novnc-ios-icon.svg new file mode 100644 index 00000000..009452ac --- /dev/null +++ b/public/novnc/app/images/icons/novnc-ios-icon.svg @@ -0,0 +1,183 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/novnc/app/images/icons/novnc.ico b/public/novnc/app/images/icons/novnc.ico new file mode 100644 index 00000000..c3bc58e3 Binary files /dev/null and b/public/novnc/app/images/icons/novnc.ico differ diff --git a/public/novnc/app/locale/fr.json b/public/novnc/app/locale/fr.json index 19e8255b..22531f73 100644 --- a/public/novnc/app/locale/fr.json +++ b/public/novnc/app/locale/fr.json @@ -1,21 +1,22 @@ { + "HTTPS is required for full functionality": "", "Connecting...": "En cours de connexion...", "Disconnecting...": "Déconnexion en cours...", "Reconnecting...": "Reconnexion en cours...", "Internal error": "Erreur interne", "Must set host": "Doit définir l'hôte", - "Connected (encrypted) to ": "Connecté (crypté) à ", - "Connected (unencrypted) to ": "Connecté (non crypté) à ", - "Something went wrong, connection is closed": "Quelque chose est arrivé, la connexion est fermée", + "Connected (encrypted) to ": "Connecté (chiffré) à ", + "Connected (unencrypted) to ": "Connecté (non chiffré) à ", + "Something went wrong, connection is closed": "Quelque chose s'est mal passé, la connexion a été fermée", "Failed to connect to server": "Échec de connexion au serveur", "Disconnected": "Déconnecté", - "New connection has been rejected with reason: ": "Une nouvelle connexion a été rejetée avec raison: ", + "New connection has been rejected with reason: ": "Une nouvelle connexion a été rejetée avec motif : ", "New connection has been rejected": "Une nouvelle connexion a été rejetée", "Credentials are required": "Les identifiants sont requis", - "noVNC encountered an error:": "noVNC a rencontré une erreur:", + "noVNC encountered an error:": "noVNC a rencontré une erreur :", "Hide/Show the control bar": "Masquer/Afficher la barre de contrôle", "Drag": "Faire glisser", - "Move/Drag Viewport": "Déplacer/faire glisser Viewport", + "Move/Drag Viewport": "Déplacer/faire glisser le Viewport", "Keyboard": "Clavier", "Show Keyboard": "Afficher le clavier", "Extra keys": "Touches supplémentaires", @@ -39,34 +40,39 @@ "Reboot": "Redémarrer", "Reset": "Réinitialiser", "Clipboard": "Presse-papiers", - "Clear": "Effacer", - "Fullscreen": "Plein écran", + "Edit clipboard content in the textarea below.": "", "Settings": "Paramètres", "Shared Mode": "Mode partagé", "View Only": "Afficher uniquement", "Clip to Window": "Clip à fenêtre", - "Scaling Mode:": "Mode mise à l'échelle:", + "Scaling Mode:": "Mode mise à l'échelle :", "None": "Aucun", "Local Scaling": "Mise à l'échelle locale", "Remote Resizing": "Redimensionnement à distance", "Advanced": "Avancé", - "Quality:": "Qualité:", - "Compression level:": "Niveau de compression:", - "Repeater ID:": "ID Répéteur:", + "Quality:": "Qualité :", + "Compression level:": "Niveau de compression :", + "Repeater ID:": "ID Répéteur :", "WebSocket": "WebSocket", - "Encrypt": "Crypter", - "Host:": "Hôte:", - "Port:": "Port:", - "Path:": "Chemin:", + "Encrypt": "Chiffrer", + "Host:": "Hôte :", + "Port:": "Port :", + "Path:": "Chemin :", "Automatic Reconnect": "Reconnecter automatiquemen", - "Reconnect Delay (ms):": "Délai de reconnexion (ms):", + "Reconnect Delay (ms):": "Délai de reconnexion (ms) :", "Show Dot when No Cursor": "Afficher le point lorsqu'il n'y a pas de curseur", - "Logging:": "Se connecter:", - "Version:": "Version:", + "Logging:": "Se connecter :", + "Version:": "Version :", "Disconnect": "Déconnecter", "Connect": "Connecter", - "Username:": "Nom d'utilisateur:", - "Password:": "Mot de passe:", + "Server identity": "", + "The server has provided the following identifying information:": "", + "Fingerprint:": "", + "Please verify that the information is correct and press \"Approve\". Otherwise press \"Reject\".": "", + "Approve": "", + "Reject": "", + "Username:": "Nom d'utilisateur :", + "Password:": "Mot de passe :", "Send Credentials": "Envoyer les identifiants", "Cancel": "Annuler" } \ No newline at end of file diff --git a/public/novnc/app/locale/it.json b/public/novnc/app/locale/it.json new file mode 100644 index 00000000..6fd25702 --- /dev/null +++ b/public/novnc/app/locale/it.json @@ -0,0 +1,72 @@ +{ + "Connecting...": "Connessione in corso...", + "Disconnecting...": "Disconnessione...", + "Reconnecting...": "Riconnessione...", + "Internal error": "Errore interno", + "Must set host": "Devi impostare l'host", + "Connected (encrypted) to ": "Connesso (crittografato) a ", + "Connected (unencrypted) to ": "Connesso (non crittografato) a", + "Something went wrong, connection is closed": "Qualcosa è andato storto, la connessione è stata chiusa", + "Failed to connect to server": "Impossibile connettersi al server", + "Disconnected": "Disconnesso", + "New connection has been rejected with reason: ": "La nuova connessione è stata rifiutata con motivo: ", + "New connection has been rejected": "La nuova connessione è stata rifiutata", + "Credentials are required": "Le credenziali sono obbligatorie", + "noVNC encountered an error:": "noVNC ha riscontrato un errore:", + "Hide/Show the control bar": "Nascondi/Mostra la barra di controllo", + "Drag": "", + "Move/Drag Viewport": "", + "Keyboard": "Tastiera", + "Show Keyboard": "Mostra tastiera", + "Extra keys": "Tasti Aggiuntivi", + "Show Extra Keys": "Mostra Tasti Aggiuntivi", + "Ctrl": "Ctrl", + "Toggle Ctrl": "Tieni premuto Ctrl", + "Alt": "Alt", + "Toggle Alt": "Tieni premuto Alt", + "Toggle Windows": "Tieni premuto Windows", + "Windows": "Windows", + "Send Tab": "Invia Tab", + "Tab": "Tab", + "Esc": "Esc", + "Send Escape": "Invia Esc", + "Ctrl+Alt+Del": "Ctrl+Alt+Canc", + "Send Ctrl-Alt-Del": "Invia Ctrl-Alt-Canc", + "Shutdown/Reboot": "Spegnimento/Riavvio", + "Shutdown/Reboot...": "Spegnimento/Riavvio...", + "Power": "Alimentazione", + "Shutdown": "Spegnimento", + "Reboot": "Riavvio", + "Reset": "Reset", + "Clipboard": "Clipboard", + "Clear": "Pulisci", + "Fullscreen": "Schermo intero", + "Settings": "Impostazioni", + "Shared Mode": "Modalità condivisa", + "View Only": "Sola Visualizzazione", + "Clip to Window": "", + "Scaling Mode:": "Modalità di ridimensionamento:", + "None": "Nessuna", + "Local Scaling": "Ridimensionamento Locale", + "Remote Resizing": "Ridimensionamento Remoto", + "Advanced": "Avanzate", + "Quality:": "Qualità:", + "Compression level:": "Livello Compressione:", + "Repeater ID:": "ID Ripetitore:", + "WebSocket": "WebSocket", + "Encrypt": "Crittografa", + "Host:": "Host:", + "Port:": "Porta:", + "Path:": "Percorso:", + "Automatic Reconnect": "Riconnessione Automatica", + "Reconnect Delay (ms):": "Ritardo Riconnessione (ms):", + "Show Dot when No Cursor": "Mostra Punto quando Nessun Cursore", + "Logging:": "", + "Version:": "Versione:", + "Disconnect": "Disconnetti", + "Connect": "Connetti", + "Username:": "Utente:", + "Password:": "Password:", + "Send Credentials": "Invia Credenziale", + "Cancel": "Annulla" +} \ No newline at end of file diff --git a/public/novnc/app/locale/sv.json b/public/novnc/app/locale/sv.json index e46df45b..077ef42c 100644 --- a/public/novnc/app/locale/sv.json +++ b/public/novnc/app/locale/sv.json @@ -1,4 +1,5 @@ { + "HTTPS is required for full functionality": "HTTPS krävs för full funktionalitet", "Connecting...": "Ansluter...", "Disconnecting...": "Kopplar ner...", "Reconnecting...": "Återansluter...", @@ -39,8 +40,8 @@ "Reboot": "Boota om", "Reset": "Återställ", "Clipboard": "Urklipp", - "Clear": "Rensa", - "Fullscreen": "Fullskärm", + "Edit clipboard content in the textarea below.": "Redigera urklippets innehåll i fältet nedan.", + "Full Screen": "Fullskärm", "Settings": "Inställningar", "Shared Mode": "Delat Läge", "View Only": "Endast Visning", @@ -65,6 +66,13 @@ "Version:": "Version:", "Disconnect": "Koppla från", "Connect": "Anslut", + "Server identity": "Server-identitet", + "The server has provided the following identifying information:": "Servern har gett följande identifierande information:", + "Fingerprint:": "Fingeravtryck:", + "Please verify that the information is correct and press \"Approve\". Otherwise press \"Reject\".": "Kontrollera att informationen är korrekt och tryck sedan \"Godkänn\". Tryck annars \"Neka\".", + "Approve": "Godkänn", + "Reject": "Neka", + "Credentials": "Användaruppgifter", "Username:": "Användarnamn:", "Password:": "Lösenord:", "Send Credentials": "Skicka Användaruppgifter", diff --git a/public/novnc/app/localization.js b/public/novnc/app/localization.js index 100901c9..84341da6 100644 --- a/public/novnc/app/localization.js +++ b/public/novnc/app/localization.js @@ -103,13 +103,20 @@ export class Localizer { return items.indexOf(searchElement) !== -1; } + function translateString(str) { + // We assume surrounding whitespace, and whitespace around line + // breaks is just for source formatting + str = str.split("\n").map(s => s.trim()).join(" ").trim(); + return self.get(str); + } + function translateAttribute(elem, attr) { - const str = self.get(elem.getAttribute(attr)); + const str = translateString(elem.getAttribute(attr)); elem.setAttribute(attr, str); } function translateTextNode(node) { - const str = self.get(node.data.trim()); + const str = translateString(node.data); node.data = str; } diff --git a/public/novnc/app/styles/base.css b/public/novnc/app/styles/base.css index fd78b79c..06e736a9 100644 --- a/public/novnc/app/styles/base.css +++ b/public/novnc/app/styles/base.css @@ -19,10 +19,23 @@ * 10000: Max (used for polyfills) */ +/* + * State variables (set on :root): + * + * noVNC_loading: Page is still loading + * noVNC_connecting: Connecting to server + * noVNC_reconnecting: Re-establishing a connection + * noVNC_connected: Connected to server (most common state) + * noVNC_disconnecting: Disconnecting from server + */ + +:root { + font-family: sans-serif; +} + body { margin:0; padding:0; - font-family: Helvetica; /*Background image with light grey curve.*/ background-color:#494949; background-repeat:no-repeat; @@ -78,144 +91,6 @@ html { 50% { box-shadow: 60px 10px 0 rgba(255, 255, 255, 0); width: 10px; } } -/* ---------------------------------------- - * Input Elements - * ---------------------------------------- - */ - -input:not([type]), -input[type=date], -input[type=datetime-local], -input[type=email], -input[type=month], -input[type=number], -input[type=password], -input[type=search], -input[type=tel], -input[type=text], -input[type=time], -input[type=url], -input[type=week], -textarea { - /* Disable default rendering */ - -webkit-appearance: none; - -moz-appearance: none; - background: none; - - margin: 2px; - padding: 2px; - border: 1px solid rgb(192, 192, 192); - border-radius: 5px; - color: black; - background: linear-gradient(to top, rgb(255, 255, 255) 80%, rgb(240, 240, 240)); -} - -input[type=button], -input[type=color], -input[type=reset], -input[type=submit], -select { - /* Disable default rendering */ - -webkit-appearance: none; - -moz-appearance: none; - background: none; - - margin: 2px; - padding: 2px; - border: 1px solid rgb(192, 192, 192); - border-bottom-width: 2px; - border-radius: 5px; - color: black; - background: linear-gradient(to top, rgb(255, 255, 255), rgb(240, 240, 240)); - - /* This avoids it jumping around when :active */ - vertical-align: middle; -} - -input[type=button], -input[type=color], -input[type=reset], -input[type=submit] { - padding-left: 20px; - padding-right: 20px; -} - -option { - color: black; - background: white; -} - -input:not([type]):focus, -input[type=button]:focus, -input[type=color]:focus, -input[type=date]:focus, -input[type=datetime-local]:focus, -input[type=email]:focus, -input[type=month]:focus, -input[type=number]:focus, -input[type=password]:focus, -input[type=reset]:focus, -input[type=search]:focus, -input[type=submit]:focus, -input[type=tel]:focus, -input[type=text]:focus, -input[type=time]:focus, -input[type=url]:focus, -input[type=week]:focus, -select:focus, -textarea:focus { - box-shadow: 0px 0px 3px rgba(74, 144, 217, 0.5); - border-color: rgb(74, 144, 217); - outline: none; -} - -input[type=button]::-moz-focus-inner, -input[type=color]::-moz-focus-inner, -input[type=reset]::-moz-focus-inner, -input[type=submit]::-moz-focus-inner { - border: none; -} - -input:not([type]):disabled, -input[type=button]:disabled, -input[type=color]:disabled, -input[type=date]:disabled, -input[type=datetime-local]:disabled, -input[type=email]:disabled, -input[type=month]:disabled, -input[type=number]:disabled, -input[type=password]:disabled, -input[type=reset]:disabled, -input[type=search]:disabled, -input[type=submit]:disabled, -input[type=tel]:disabled, -input[type=text]:disabled, -input[type=time]:disabled, -input[type=url]:disabled, -input[type=week]:disabled, -select:disabled, -textarea:disabled { - color: rgb(128, 128, 128); - background: rgb(240, 240, 240); -} - -input[type=button]:active, -input[type=color]:active, -input[type=reset]:active, -input[type=submit]:active, -select:active { - border-bottom-width: 1px; - margin-top: 3px; -} - -:root:not(.noVNC_touch) input[type=button]:hover:not(:disabled), -:root:not(.noVNC_touch) input[type=color]:hover:not(:disabled), -:root:not(.noVNC_touch) input[type=reset]:hover:not(:disabled), -:root:not(.noVNC_touch) input[type=submit]:hover:not(:disabled), -:root:not(.noVNC_touch) select:hover:not(:disabled) { - background: linear-gradient(to top, rgb(255, 255, 255), rgb(250, 250, 250)); -} - /* ---------------------------------------- * WebKit centering hacks * ---------------------------------------- @@ -242,13 +117,15 @@ select:active { pointer-events: auto; } .noVNC_vcenter { - display: flex; + display: flex !important; flex-direction: column; justify-content: center; position: fixed; top: 0; left: 0; height: 100%; + margin: 0 !important; + padding: 0 !important; pointer-events: none; } .noVNC_vcenter > * { @@ -272,13 +149,20 @@ select:active { #noVNC_fallback_error { z-index: 1000; visibility: hidden; + /* Put a dark background in front of everything but the error, + and don't let mouse events pass through */ + background: rgba(0, 0, 0, 0.8); + pointer-events: all; } #noVNC_fallback_error.noVNC_open { visibility: visible; } #noVNC_fallback_error > div { - max-width: 90%; + max-width: calc(100vw - 30px - 30px); + max-height: calc(100vh - 30px - 30px); + overflow: auto; + padding: 15px; transition: 0.5s ease-in-out; @@ -317,7 +201,6 @@ select:active { } #noVNC_fallback_error .noVNC_stack { - max-height: 50vh; padding: 10px; margin: 10px; font-size: 0.8em; @@ -361,6 +244,9 @@ select:active { background-color: rgb(110, 132, 163); border-radius: 0 10px 10px 0; + user-select: none; + -webkit-user-select: none; + -webkit-touch-callout: none; /* Disable iOS image long-press popup */ } #noVNC_control_bar.noVNC_open { box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); @@ -433,38 +319,50 @@ select:active { .noVNC_right #noVNC_control_bar.noVNC_open #noVNC_control_bar_handle:after { transform: none; } +/* Larger touch area for the handle, used when a touch screen is available */ #noVNC_control_bar_handle div { position: absolute; right: -35px; top: 0; width: 50px; - height: 50px; -} -:root:not(.noVNC_touch) #noVNC_control_bar_handle div { + height: 100%; display: none; } +@media (any-pointer: coarse) { + #noVNC_control_bar_handle div { + display: initial; + } +} .noVNC_right #noVNC_control_bar_handle div { left: -35px; right: auto; } -#noVNC_control_bar .noVNC_scroll { +#noVNC_control_bar > .noVNC_scroll { max-height: 100vh; /* Chrome is buggy with 100% */ overflow-x: hidden; overflow-y: auto; - padding: 0 10px 0 5px; + padding: 0 10px; } -.noVNC_right #noVNC_control_bar .noVNC_scroll { - padding: 0 5px 0 10px; + +#noVNC_control_bar > .noVNC_scroll > * { + display: block; + margin: 10px auto; } /* Control bar hint */ -#noVNC_control_bar_hint { +#noVNC_hint_anchor { position: fixed; - left: calc(100vw - 50px); + right: -50px; + left: auto; +} +#noVNC_control_bar_anchor.noVNC_right + #noVNC_hint_anchor { + left: -50px; right: auto; - top: 50%; - transform: translateY(-50%) scale(0); +} +#noVNC_control_bar_hint { + position: relative; + transform: scale(0); width: 100px; height: 50%; max-height: 600px; @@ -477,61 +375,65 @@ select:active { border-radius: 10px; transition-delay: 0s; } -#noVNC_control_bar_anchor.noVNC_right #noVNC_control_bar_hint{ - left: auto; - right: calc(100vw - 50px); -} #noVNC_control_bar_hint.noVNC_active { visibility: visible; opacity: 1; transition-delay: 0.2s; - transform: translateY(-50%) scale(1); + transform: scale(1); +} +#noVNC_control_bar_hint.noVNC_notransition { + transition: none !important; } -/* General button style */ -.noVNC_button { - display: block; +/* Control bar buttons */ +#noVNC_control_bar .noVNC_button { padding: 4px 4px; - margin: 10px 0; vertical-align: middle; border:1px solid rgba(255, 255, 255, 0.2); border-radius: 6px; + background-color: transparent; + background-image: unset; /* we don't want the gradiant from input.css */ } -.noVNC_button.noVNC_selected { +#noVNC_control_bar .noVNC_button.noVNC_selected { border-color: rgba(0, 0, 0, 0.8); - background: rgba(0, 0, 0, 0.5); + background-color: rgba(0, 0, 0, 0.5); } -.noVNC_button:disabled { - opacity: 0.4; +#noVNC_control_bar .noVNC_button.noVNC_selected:not(:disabled):hover { + border-color: rgba(0, 0, 0, 0.4); + background-color: rgba(0, 0, 0, 0.2); } -.noVNC_button:focus { - outline: none; +#noVNC_control_bar .noVNC_button:not(:disabled):hover { + background-color: rgba(255, 255, 255, 0.2); } -.noVNC_button:active { +#noVNC_control_bar .noVNC_button:not(:disabled):active { padding-top: 5px; padding-bottom: 3px; } -/* Android browsers don't properly update hover state if touch events - * are intercepted, but focus should be safe to display */ -:root:not(.noVNC_touch) .noVNC_button.noVNC_selected:hover, -.noVNC_button.noVNC_selected:focus { - border-color: rgba(0, 0, 0, 0.4); - background: rgba(0, 0, 0, 0.2); +#noVNC_control_bar .noVNC_button.noVNC_hidden { + display: none !important; } -:root:not(.noVNC_touch) .noVNC_button:hover, -.noVNC_button:focus { - background: rgba(255, 255, 255, 0.2); -} -.noVNC_button.noVNC_hidden { - display: none; + +/* Android browsers don't properly update hover state if touch events are + * intercepted, like they are when clicking on the remote screen. */ +@media (any-pointer: coarse) { + #noVNC_control_bar .noVNC_button:not(:disabled):hover { + background-color: transparent; + } + #noVNC_control_bar .noVNC_button.noVNC_selected:not(:disabled):hover { + border-color: rgba(0, 0, 0, 0.8); + background-color: rgba(0, 0, 0, 0.5); + } } + /* Panels */ .noVNC_panel { transform: translateX(25px); transition: 0.5s ease-in-out; + box-sizing: border-box; /* so max-width don't have to care about padding */ + max-width: calc(100vw - 75px - 25px); /* minus left and right margins */ max-height: 100vh; /* Chrome is buggy with 100% */ overflow-x: hidden; overflow-y: auto; @@ -563,6 +465,17 @@ select:active { transform: translateX(-75px); } +.noVNC_panel > * { + display: block; + margin: 10px auto; +} +.noVNC_panel > *:first-child { + margin-top: 0 !important; +} +.noVNC_panel > *:last-child { + margin-bottom: 0 !important; +} + .noVNC_panel hr { border: none; border-top: 1px solid rgb(192, 192, 192); @@ -571,6 +484,11 @@ select:active { .noVNC_panel label { display: block; white-space: nowrap; + margin: 5px; +} + +.noVNC_panel li { + margin: 5px; } .noVNC_panel .noVNC_heading { @@ -581,7 +499,6 @@ select:active { padding-right: 8px; color: white; font-size: 20px; - margin-bottom: 10px; white-space: nowrap; } .noVNC_panel .noVNC_heading img { @@ -622,6 +539,12 @@ select:active { font-size: 13px; } +.noVNC_logo + hr { + /* Remove all but top border */ + border: none; + border-top: 1px solid rgba(255, 255, 255, 0.2); +} + :root:not(.noVNC_connected) #noVNC_view_drag_button { display: none; } @@ -630,8 +553,15 @@ select:active { :root:not(.noVNC_connected) #noVNC_mobile_buttons { display: none; } -:root:not(.noVNC_touch) #noVNC_mobile_buttons { - display: none; +@media not all and (any-pointer: coarse) { + /* FIXME: The button for the virtual keyboard is the only button in this + group of "mobile buttons". It is bad to assume that no touch + devices have physical keyboards available. Hopefully we can get + a media query for this: + https://github.com/w3c/csswg-drafts/issues/3871 */ + :root.noVNC_connected #noVNC_mobile_buttons { + display: none; + } } /* Extra manual keys */ @@ -642,7 +572,7 @@ select:active { #noVNC_modifiers { background-color: rgb(92, 92, 92); border: none; - padding: 0 10px; + padding: 10px; } /* Shutdown/Reboot */ @@ -663,13 +593,16 @@ select:active { :root:not(.noVNC_connected) #noVNC_clipboard_button { display: none; } -#noVNC_clipboard { - /* Full screen, minus padding and left and right margins */ - max-width: calc(100vw - 2*15px - 75px - 25px); -} #noVNC_clipboard_text { - width: 500px; + width: 360px; + min-width: 150px; + height: 160px; + min-height: 70px; + + box-sizing: border-box; max-width: 100%; + /* minus approximate height of title, height of subtitle, and margin */ + max-height: calc(100vh - 10em - 25px); } /* Settings */ @@ -677,7 +610,6 @@ select:active { } #noVNC_settings ul { list-style: none; - margin: 0px; padding: 0px; } #noVNC_setting_port { @@ -803,36 +735,32 @@ select:active { font-size: calc(25vw - 30px); } } -#noVNC_connect_button { - cursor: pointer; +#noVNC_connect_dlg div { + padding: 12px; - padding: 10px; - - color: white; background-color: rgb(110, 132, 163); border-radius: 12px; - text-align: center; font-size: 20px; box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5); } -#noVNC_connect_button div { - margin: 2px; +#noVNC_connect_button { + width: 100%; padding: 5px 30px; - border: 1px solid rgb(83, 99, 122); - border-bottom-width: 2px; + + cursor: pointer; + + border-color: rgb(83, 99, 122); border-radius: 5px; + background: linear-gradient(to top, rgb(110, 132, 163), rgb(99, 119, 147)); + color: white; /* This avoids it jumping around when :active */ vertical-align: middle; } -#noVNC_connect_button div:active { - border-bottom-width: 1px; - margin-top: 3px; -} -:root:not(.noVNC_touch) #noVNC_connect_button div:hover { +#noVNC_connect_button:hover { background: linear-gradient(to top, rgb(110, 132, 163), rgb(105, 125, 155)); } @@ -841,6 +769,23 @@ select:active { height: 1.3em; } +/* ---------------------------------------- + * Server verification Dialog + * ---------------------------------------- + */ + +#noVNC_verify_server_dlg { + position: relative; + + transform: translateY(-50px); +} +#noVNC_verify_server_dlg.noVNC_open { + transform: translateY(0); +} +#noVNC_fingerprint_block { + margin: 10px; +} + /* ---------------------------------------- * Password Dialog * ---------------------------------------- @@ -854,12 +799,8 @@ select:active { #noVNC_credentials_dlg.noVNC_open { transform: translateY(0); } -#noVNC_credentials_dlg ul { - list-style: none; - margin: 0px; - padding: 0px; -} -.noVNC_hidden { +#noVNC_username_block.noVNC_hidden, +#noVNC_password_block.noVNC_hidden { display: none; } @@ -871,7 +812,11 @@ select:active { /* Transition screen */ #noVNC_transition { - display: none; + transition: 0.5s ease-in-out; + + display: flex; + opacity: 0; + visibility: hidden; position: fixed; top: 0; @@ -892,7 +837,8 @@ select:active { :root.noVNC_connecting #noVNC_transition, :root.noVNC_disconnecting #noVNC_transition, :root.noVNC_reconnecting #noVNC_transition { - display: flex; + opacity: 1; + visibility: visible; } :root:not(.noVNC_reconnecting) #noVNC_cancel_reconnect_button { display: none; @@ -908,6 +854,12 @@ select:active { background-color: #313131; border-bottom-right-radius: 800px 600px; /*border-top-left-radius: 800px 600px;*/ + + /* If selection isn't disabled, long-pressing stuff in the sidebar + can accidentally select the container or the canvas. This can + happen when attempting to move the handle. */ + user-select: none; + -webkit-user-select: none; } #noVNC_keyboardinput { diff --git a/public/novnc/app/styles/input.css b/public/novnc/app/styles/input.css new file mode 100644 index 00000000..eaf083c7 --- /dev/null +++ b/public/novnc/app/styles/input.css @@ -0,0 +1,281 @@ +/* + * noVNC general input element CSS + * Copyright (C) 2022 The noVNC Authors + * noVNC is licensed under the MPL 2.0 (see LICENSE.txt) + * This file is licensed under the 2-Clause BSD license (see LICENSE.txt). + */ + +/* + * Common for all inputs + */ +input, input::file-selector-button, button, select, textarea { + /* Respect standard font settings */ + font: inherit; + + /* Disable default rendering */ + appearance: none; + background: none; + + padding: 5px; + border: 1px solid rgb(192, 192, 192); + border-radius: 5px; + color: black; + --bg-gradient: linear-gradient(to top, rgb(255, 255, 255) 80%, rgb(240, 240, 240)); + background-image: var(--bg-gradient); +} + +/* + * Buttons + */ +input[type=button], +input[type=color], +input[type=image], +input[type=reset], +input[type=submit], +input::file-selector-button, +button, +select { + border-bottom-width: 2px; + + /* This avoids it jumping around when :active */ + vertical-align: middle; + margin-top: 0; + + padding-left: 20px; + padding-right: 20px; + + /* Disable Chrome's touch tap highlight */ + -webkit-tap-highlight-color: transparent; +} + +/* + * Select dropdowns + */ +select { + --select-arrow: url('data:image/svg+xml;utf8, \ + \ + \ + '); + background-image: var(--select-arrow), var(--bg-gradient); + background-position: calc(100% - 7px), left top; + background-repeat: no-repeat; + padding-right: calc(2*7px + 8px); + padding-left: 7px; +} +/* FIXME: :active isn't set when the Clipboard +

+ Edit clipboard content in the textarea below. +

-
- - + title="Full Screen">
+
+ Settings +
-
- +
+
+
+
+
- -
- Connect -
+ +
+ +
+ +
+
+
+ Server identity +
+
+ The server has provided the following identifying information: +
+
+ Fingerprint: + +
+
+ Please verify that the information is correct and press + "Approve". Otherwise press "Reject". +
+
+ + +
+
+
+
-
    -
  • - - -
  • -
  • - - -
  • -
  • - -
  • -
+
+ Credentials +
+
+ + +
+
+ + +
+
+ +