Merge main into branch

This commit is contained in:
Rui Reis 2025-03-12 13:35:42 +01:00
commit 08198a6781
20 changed files with 2393 additions and 1495 deletions

View File

@ -66,8 +66,9 @@ profits such as:
RSA-AES, Tight, VeNCrypt Plain, XVP, Apple's Diffie-Hellman,
UltraVNC's MSLogonII
* Supported VNC encodings: raw, copyrect, rre, hextile, tight, tightPNG,
ZRLE, JPEG, Zlib
ZRLE, JPEG, Zlib, H.264
* Supports scaling, clipping and resizing the desktop
* Supports back & forward mouse buttons
* Local cursor rendering
* Clipboard copy/paste with full Unicode support
* Translations
@ -142,7 +143,7 @@ If you want to use certificate files, due to standard snap confinement restricti
#### Running noVNC from snap as a service (daemon)
The snap package also has the capability to run a 'novnc' service which can be
configured to listen on multiple ports connecting to multiple VNC servers
(effectively a service runing multiple instances of novnc).
(effectively a service running multiple instances of novnc).
Instructions (with example values):
List current services (out-of-box this will be blank):

View File

@ -1,13 +1,14 @@
{
"HTTPS is required for full functionality": "すべての機能を使用するにはHTTPS接続が必要です",
"Running without HTTPS is not recommended, crashes or other issues are likely.": "HTTPS接続なしで実行することは推奨されません。クラッシュしたりその他の問題が発生したりする可能性があります。",
"Connecting...": "接続しています...",
"Disconnecting...": "切断しています...",
"Reconnecting...": "再接続しています...",
"Internal error": "内部エラー",
"Must set host": "ホストを設定する必要があります",
"Failed to connect to server: ": "サーバーへの接続に失敗しました: ",
"Connected (encrypted) to ": "接続しました (暗号化済み): ",
"Connected (unencrypted) to ": "接続しました (暗号化されていません): ",
"Something went wrong, connection is closed": "何らかの問題で、接続が閉じられました",
"Something went wrong, connection is closed": "問題が発生したため、接続が閉じられました",
"Failed to connect to server": "サーバーへの接続に失敗しました",
"Disconnected": "切断しました",
"New connection has been rejected with reason: ": "新規接続は次の理由で拒否されました: ",
@ -48,7 +49,7 @@
"Clip to window": "ウィンドウにクリップ",
"Scaling mode:": "スケーリングモード:",
"None": "なし",
"Local scaling": "ローカルスケーリング",
"Local scaling": "ローカルスケーリング",
"Remote resizing": "リモートでリサイズ",
"Advanced": "高度",
"Quality:": "品質:",

View File

@ -31,6 +31,7 @@
:root {
font-family: sans-serif;
line-height: 1.6;
}
body {
@ -53,7 +54,7 @@ html {
}
.noVNC_disabled {
color: rgb(128, 128, 128);
color: var(--novnc-grey);
}
/* ----------------------------------------
@ -174,7 +175,7 @@ html {
font-weight: bold;
color: #fff;
border-radius: 10px;
border-radius: 12px;
box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5);
background: rgba(200,55,55,0.8);
}
@ -241,8 +242,8 @@ html {
transition: 0.5s ease-in-out;
background-color: rgb(110, 132, 163);
border-radius: 0 10px 10px 0;
background-color: var(--novnc-blue);
border-radius: 0 12px 12px 0;
user-select: none;
-webkit-user-select: none;
@ -267,7 +268,7 @@ html {
}
.noVNC_right #noVNC_control_bar {
left: 100%;
border-radius: 10px 0 0 10px;
border-radius: 12px 0 0 12px;
}
.noVNC_right #noVNC_control_bar.noVNC_open {
left: 0;
@ -285,8 +286,8 @@ html {
height: 50px;
z-index: -1;
cursor: pointer;
border-radius: 5px;
background-color: rgb(83, 99, 122);
border-radius: 6px;
background-color: var(--novnc-darkblue);
background-image: url("../images/handle_bg.svg");
background-repeat: no-repeat;
background-position: right;
@ -371,8 +372,8 @@ html {
opacity: 0;
transition: 0.2s ease-in-out;
background: transparent;
box-shadow: 0 0 10px black, inset 0 0 10px 10px rgba(110, 132, 163, 0.8);
border-radius: 10px;
box-shadow: 0 0 10px black, inset 0 0 10px 10px var(--novnc-darkblue);
border-radius: 12px;
transition-delay: 0s;
}
#noVNC_control_bar_hint.noVNC_active {
@ -387,45 +388,21 @@ html {
/* Control bar buttons */
#noVNC_control_bar .noVNC_button {
min-width: unset;
padding: 4px 4px;
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_control_bar .noVNC_button.noVNC_selected {
border-color: rgba(0, 0, 0, 0.8);
background-color: rgba(0, 0, 0, 0.5);
}
#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_control_bar .noVNC_button:not(:disabled):hover {
background-color: rgba(255, 255, 255, 0.2);
}
#noVNC_control_bar .noVNC_button:not(:disabled):active {
padding-top: 5px;
padding-bottom: 3px;
}
#noVNC_control_bar .noVNC_button.noVNC_hidden {
display: none !important;
}
/* 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);
@ -444,7 +421,7 @@ html {
padding: 15px;
background: #fff;
border-radius: 10px;
border-radius: 12px;
color: #000;
border: 2px solid #E0E0E0;
box-shadow: 6px 6px 0px rgba(0, 0, 0, 0.5);
@ -478,7 +455,8 @@ html {
.noVNC_panel hr {
border: none;
border-top: 1px solid rgb(192, 192, 192);
border-top: 1px solid var(--novnc-lightgrey);
width: 100%; /* <hr> inside a flexbox will otherwise be 0px wide */
}
.noVNC_panel label {
@ -486,27 +464,58 @@ html {
white-space: nowrap;
margin: 5px;
}
@media (max-width: 540px) {
/* Allow wrapping on small screens */
.noVNC_panel label {
white-space: unset;
}
}
.noVNC_panel li {
margin: 5px;
}
.noVNC_panel button,
.noVNC_panel select,
.noVNC_panel textarea,
.noVNC_panel input:not([type=checkbox]):not([type=radio]) {
margin-left: 6px;
/* Prevent inputs in panels from being too wide */
max-width: calc(100% - 6px - var(--input-xpadding) * 2);
}
.noVNC_panel .noVNC_heading {
background-color: rgb(110, 132, 163);
border-radius: 5px;
padding: 5px;
background-color: var(--novnc-blue);
border-radius: 6px;
padding: 5px 8px;
/* Compensate for padding in image */
padding-right: 8px;
padding-right: 11px;
display: flex;
align-items: center;
gap: 6px;
color: white;
font-size: 20px;
font-weight: bold;
white-space: nowrap;
}
.noVNC_panel .noVNC_heading img {
vertical-align: bottom;
}
.noVNC_submit {
float: right;
.noVNC_panel form {
display: flex;
flex-direction: column;
gap: 12px
}
.noVNC_panel .button_row {
margin-top: 10px;
display: flex;
gap: 10px;
justify-content: space-between;
}
.noVNC_panel .button_row *:only-child {
margin-left: auto; /* Align single buttons to the right */
}
/* Expanders */
@ -526,8 +535,8 @@ html {
margin: 5px;
margin-left: 10px;
padding: 5px;
background: rgba(0, 0, 0, 0.05);
border-radius: 5px;
background: rgba(0, 0, 0, 0.04);
border-radius: 6px;
}
.noVNC_expander:not(.noVNC_open) ~ * {
display: none;
@ -570,7 +579,7 @@ html {
}
#noVNC_modifiers {
background-color: rgb(92, 92, 92);
background-color: var(--novnc-darkgrey);
border: none;
padding: 10px;
}
@ -725,7 +734,7 @@ html {
font-size: 80px;
text-align: center;
border-radius: 5px;
border-radius: 6px;
}
@media (max-width: 440px) {
#noVNC_connect_dlg {
@ -736,9 +745,9 @@ html {
}
}
#noVNC_connect_dlg div {
padding: 12px;
padding: 18px;
background-color: rgb(110, 132, 163);
background-color: var(--novnc-darkgrey);
border-radius: 12px;
text-align: center;
font-size: 20px;
@ -747,21 +756,17 @@ html {
}
#noVNC_connect_button {
width: 100%;
padding: 5px 30px;
padding: 6px 30px;
cursor: pointer;
border-color: rgb(83, 99, 122);
border-radius: 5px;
background: linear-gradient(to top, rgb(110, 132, 163), rgb(99, 119, 147));
border-color: transparent;
border-radius: 12px;
background-color: var(--novnc-blue);
color: white;
/* This avoids it jumping around when :active */
vertical-align: middle;
}
#noVNC_connect_button:hover {
background: linear-gradient(to top, rgb(110, 132, 163), rgb(105, 125, 155));
display: flex;
justify-content: center;
place-items: center;
gap: 4px;
}
#noVNC_connect_button img {
@ -885,13 +890,13 @@ html {
}
.noVNC_logo {
color:yellow;
color: var(--novnc-yellow);
font-family: 'Orbitron', 'OrbitronTTF', sans-serif;
line-height: 0.9;
text-shadow: 0.1em 0.1em 0 black;
}
.noVNC_logo span{
color:green;
color: var(--novnc-green);
}
#noVNC_bell {

30
app/styles/constants.css Normal file
View File

@ -0,0 +1,30 @@
/*
* noVNC general CSS constant variables
* Copyright (C) 2025 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).
*/
/* ---------- COLORS ----------- */
:root {
--novnc-grey: rgb(128, 128, 128);
--novnc-lightgrey: rgb(192, 192, 192);
--novnc-darkgrey: rgb(92, 92, 92);
/* Transparent to make button colors adapt to the background */
--novnc-buttongrey: rgba(192, 192, 192, 0.5);
--novnc-blue: rgb(110, 132, 163);
--novnc-lightblue: rgb(74, 144, 217);
--novnc-darkblue: rgb(83, 99, 122);
--novnc-green: rgb(0, 128, 0);
--novnc-yellow: rgb(255, 255, 0);
}
/* ------ MISC PROPERTIES ------ */
:root {
--input-xpadding: 1em;
}

View File

@ -1,32 +1,170 @@
/*
* noVNC general input element CSS
* Copyright (C) 2022 The noVNC authors
* Copyright (C) 2025 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 {
/* ------- SHARED BETWEEN INPUT ELEMENTS -------- */
input,
textarea,
button,
select,
input::file-selector-button {
padding: 0.5em var(--input-xpadding);
border-radius: 6px;
appearance: none;
text-overflow: ellipsis;
/* 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);
line-height: 1.6;
}
input:disabled,
textarea:disabled,
button:disabled,
select:disabled,
label[disabled] {
opacity: 0.4;
}
/*
* Buttons
*/
input:focus-visible,
textarea:focus-visible,
button:focus-visible,
select:focus-visible,
input:focus-visible::file-selector-button {
outline: 2px solid var(--novnc-lightblue);
outline-offset: 1px;
}
/* ------- TEXT INPUT -------- */
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 {
border: 1px solid var(--novnc-lightgrey);
/* Account for borders on text inputs, buttons dont have borders */
padding: calc(0.5em - 1px) var(--input-xpadding);
}
input:not([type]):focus-visible,
input[type=date]:focus-visible,
input[type=datetime-local]:focus-visible,
input[type=email]:focus-visible,
input[type=month]:focus-visible,
input[type=number]:focus-visible,
input[type=password]:focus-visible,
input[type=search]:focus-visible,
input[type=tel]:focus-visible,
input[type=text]:focus-visible,
input[type=time]:focus-visible,
input[type=url]:focus-visible,
input[type=week]:focus-visible,
textarea:focus-visible {
outline-offset: -1px;
}
textarea {
margin: unset; /* Remove Firefox's built in margin */
/* Prevent layout from shifting when scrollbars show */
scrollbar-gutter: stable;
/* Make textareas show at minimum one line. This does not work when
using box-sizing border-box, in which case, vertical padding and
border width needs to be taken into account. */
min-height: 1lh;
vertical-align: baseline; /* Firefox gives "text-bottom" by default */
}
/* ------- NUMBER PICKERS ------- */
/* We can't style the number spinner buttons:
https://github.com/w3c/csswg-drafts/issues/8777 */
input[type=number]::-webkit-inner-spin-button,
input[type=number]::-webkit-outer-spin-button {
/* Get rid of increase/decrease buttons in WebKit */
appearance: none;
}
input[type=number] {
/* Get rid of increase/decrease buttons in Firefox */
appearance: textfield;
}
/* ------- BUTTON ACTIVATIONS -------- */
/* A color overlay that depends on the activation level. The level can then be
set for different states on an element, for example hover and click on a
<button>. */
input, button, select, option,
input::file-selector-button,
.button-activations {
--button-activation-level: 0;
/* Note that CSS variables aren't functions, beware when inheriting */
--button-activation-alpha: calc(0.08 * var(--button-activation-level));
/* FIXME: We want the image() function instead of the linear-gradient()
function below. But it's not supported in the browsers yet. */
--button-activation-overlay:
linear-gradient(rgba(0, 0, 0, var(--button-activation-alpha))
100%, transparent);
--button-activation-overlay-light:
linear-gradient(rgba(255, 255, 255, calc(0.23 * var(--button-activation-level)))
100%, transparent);
}
.button-activations {
background-image: var(--button-activation-overlay);
/* Disable Chrome's touch tap highlight to avoid conflicts with overlay */
-webkit-tap-highlight-color: transparent;
}
/* When we want the light overlay on activations instead.
This is best used on elements with darker backgrounds. */
.button-activations.light-overlay {
background-image: var(--button-activation-overlay-light);
/* Can't use the normal blend mode since that gives washed out colors. */
/* FIXME: For elements with these activation overlays we'd like only
the luminosity to change. The proprty "background-blend-mode" set
to "luminosity" sounds good, but it doesn't work as intended,
see: https://bugzilla.mozilla.org/show_bug.cgi?id=1806417 */
background-blend-mode: overlay;
}
input:hover, button:hover, select:hover, option:hover,
input::file-selector-button:hover,
.button-activations:hover {
--button-activation-level: 1;
}
/* Unfortunately we have to disable the :hover effect on touch devices,
otherwise the style lingers after tapping the button. */
@media (any-pointer: coarse) {
input:hover, button:hover, select:hover, option:hover,
input::file-selector-button:hover,
.button-activations:hover {
--button-activation-level: 0;
}
}
input:active, button:active, select:active, option:active,
input::file-selector-button:active,
.button-activations:active {
--button-activation-level: 2;
}
input:disabled, button:disabled, select:disabled, select:disabled option,
input:disabled::file-selector-button,
.button-activations:disabled {
--button-activation-level: 0;
}
/* ------- BUTTONS -------- */
input[type=button],
input[type=color],
input[type=image],
@ -35,227 +173,16 @@ 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;
min-width: 8em;
border: none;
color: black;
font-weight: bold;
background-color: var(--novnc-buttongrey);
background-image: var(--button-activation-overlay);
cursor: pointer;
/* Disable Chrome's touch tap highlight */
-webkit-tap-highlight-color: transparent;
}
/*
* Select dropdowns
*/
select {
--select-arrow: url('data:image/svg+xml;utf8, \
<svg width="8" height="6" version="1.1" viewBox="0 0 8 6" \
xmlns="http://www.w3.org/2000/svg"> \
<path d="m6.5 1.5 -2.5 3 -2.5 -3 5 0" stroke-width="3" \
stroke="rgb(31,31,31)" fill="none" \
stroke-linecap="round" stroke-linejoin="round" /> \
</svg>');
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 <select> is opened in Firefox:
https://bugzilla.mozilla.org/show_bug.cgi?id=1805406 */
select:active {
/* Rotated arrow */
background-image: url('data:image/svg+xml;utf8, \
<svg width="8" height="6" version="1.1" viewBox="0 0 8 6" \
xmlns="http://www.w3.org/2000/svg" transform="rotate(180)" > \
<path d="m6.5 1.5 -2.5 3 -2.5 -3 5 0" stroke-width="3" \
stroke="rgb(31,31,31)" fill="none" \
stroke-linecap="round" stroke-linejoin="round" /> \
</svg>'), var(--bg-gradient);
}
option {
color: black;
background: white;
}
/*
* Checkboxes
*/
input[type=checkbox] {
display: inline-flex;
justify-content: center;
align-items: center;
background-color: white;
background-image: unset;
border: 1px solid dimgrey;
border-radius: 3px;
width: 13px;
height: 13px;
padding: 0;
margin-right: 6px;
vertical-align: bottom;
transition: 0.2s background-color linear;
}
input[type=checkbox]:checked {
background-color: rgb(110, 132, 163);
border-color: rgb(110, 132, 163);
}
input[type=checkbox]:checked::after {
content: "";
display: block; /* width & height doesn't work on inline elements */
width: 3px;
height: 7px;
border: 1px solid white;
border-width: 0 2px 2px 0;
transform: rotate(40deg) translateY(-1px);
}
/*
* Radiobuttons
*/
input[type=radio] {
border-radius: 50%;
border: 1px solid dimgrey;
width: 12px;
height: 12px;
padding: 0;
margin-right: 6px;
transition: 0.2s border linear;
}
input[type=radio]:checked {
border: 6px solid rgb(110, 132, 163);
}
/*
* Range sliders
*/
input[type=range] {
border: unset;
border-radius: 3px;
height: 20px;
padding: 0;
background: transparent;
}
/* -webkit-slider.. & -moz-range.. cant be in selector lists:
https://bugs.chromium.org/p/chromium/issues/detail?id=1154623 */
input[type=range]::-webkit-slider-runnable-track {
background-color: rgb(110, 132, 163);
height: 6px;
border-radius: 3px;
}
input[type=range]::-moz-range-track {
background-color: rgb(110, 132, 163);
height: 6px;
border-radius: 3px;
}
input[type=range]::-webkit-slider-thumb {
appearance: none;
width: 18px;
height: 20px;
border-radius: 5px;
background-color: white;
border: 1px solid dimgray;
margin-top: -7px;
}
input[type=range]::-moz-range-thumb {
appearance: none;
width: 18px;
height: 20px;
border-radius: 5px;
background-color: white;
border: 1px solid dimgray;
margin-top: -7px;
}
/*
* File choosers
*/
input[type=file] {
background-image: none;
border: none;
}
input::file-selector-button {
margin-right: 6px;
}
/*
* Hover
*/
input[type=button]:hover,
input[type=color]:hover,
input[type=image]:hover,
input[type=reset]:hover,
input[type=submit]:hover,
input::file-selector-button:hover,
button:hover {
background-image: linear-gradient(to top, rgb(255, 255, 255), rgb(250, 250, 250));
}
select:hover {
background-image: var(--select-arrow),
linear-gradient(to top, rgb(255, 255, 255), rgb(250, 250, 250));
background-position: calc(100% - 7px), left top;
background-repeat: no-repeat;
}
@media (any-pointer: coarse) {
/* We don't want a hover style after touch input */
input[type=button]:hover,
input[type=color]:hover,
input[type=image]:hover,
input[type=reset]:hover,
input[type=submit]:hover,
input::file-selector-button:hover,
button:hover {
background-image: var(--bg-gradient);
}
select:hover {
background-image: var(--select-arrow), var(--bg-gradient);
}
}
/*
* Active (clicked)
*/
input[type=button]:active,
input[type=color]:active,
input[type=image]:active,
input[type=reset]:active,
input[type=submit]:active,
input::file-selector-button:active,
button:active,
select:active {
border-bottom-width: 1px;
margin-top: 1px;
}
/*
* Focus (tab)
*/
input:focus-visible,
input:focus-visible::file-selector-button,
button:focus-visible,
select:focus-visible,
textarea:focus-visible {
outline: 2px solid rgb(74, 144, 217);
outline-offset: 1px;
}
input[type=file]:focus-visible {
outline: none; /* We outline the button instead of the entire element */
}
/*
* Disabled
*/
input:disabled,
input:disabled::file-selector-button,
button:disabled,
select:disabled,
textarea:disabled {
opacity: 0.4;
}
input[type=button]:disabled,
input[type=color]:disabled,
input[type=image]:disabled,
@ -264,18 +191,438 @@ input[type=submit]:disabled,
input:disabled::file-selector-button,
button:disabled,
select:disabled {
background-image: var(--bg-gradient);
border-bottom-width: 2px;
margin-top: 0;
}
input[type=file]:disabled {
background-image: none;
}
select:disabled {
background-image: var(--select-arrow), var(--bg-gradient);
}
input[type=image]:disabled {
/* See Firefox bug:
https://bugzilla.mozilla.org/show_bug.cgi?id=1798304 */
cursor: default;
}
input[type=button],
input[type=color],
input[type=reset],
input[type=submit] {
/* Workaround for text-overflow bugs in Firefox and Chromium:
https://bugzilla.mozilla.org/show_bug.cgi?id=1800077
https://bugs.chromium.org/p/chromium/issues/detail?id=1383144 */
overflow: clip;
}
/* ------- COLOR PICKERS ------- */
input[type=color] {
min-width: unset;
box-sizing: content-box;
width: 1.4em;
height: 1.4em;
}
input[type=color]::-webkit-color-swatch-wrapper {
padding: 0;
}
/* -webkit-color-swatch & -moz-color-swatch cant be in a selector list:
https://bugs.chromium.org/p/chromium/issues/detail?id=1154623 */
input[type=color]::-webkit-color-swatch {
border: none;
border-radius: 6px;
}
input[type=color]::-moz-color-swatch {
border: none;
border-radius: 6px;
}
/* -- SHARED BETWEEN CHECKBOXES, RADIOBUTTONS AND THE TOGGLE CLASS -- */
input[type=radio],
input[type=checkbox] {
display: inline-flex;
justify-content: center;
align-items: center;
background-color: var(--novnc-buttongrey);
background-image: var(--button-activation-overlay);
/* Disable Chrome's touch tap highlight to avoid conflicts with overlay */
-webkit-tap-highlight-color: transparent;
width: 16px;
--checkradio-height: 16px;
height: var(--checkradio-height);
padding: 0;
margin: 0 6px 0 0;
/* Don't have transitions for outline in order to be consistent
with other elements */
transition: all 0.2s, outline-color 0s, outline-offset 0s;
/* A transparent outline in order to work around a graphical clipping issue
in WebKit. See bug: https://bugs.webkit.org/show_bug.cgi?id=256003 */
outline: 1px solid transparent;
position: relative; /* Since ::before & ::after are absolute positioned */
/* We want to align with the middle of capital letters, this requires
a workaround. The default behavior is to align the bottom of the element
on top of the text baseline, this is too far up.
We want to push the element down half the difference in height between
it and a capital X. In our font, the height of a capital "X" is 0.698em.
*/
vertical-align: calc(0px - (var(--checkradio-height) - 0.698em) / 2);
/* FIXME: Could write 1cap instead of 0.698em, but it's only supported in
Firefox as of 2023 */
/* FIXME: We probably want to use round() here, see bug 8148 */
}
input[type=radio]:focus-visible,
input[type=checkbox]:focus-visible {
outline-color: var(--novnc-lightblue);
}
input[type=checkbox]::before,
input[type=checkbox]:not(.toggle)::after,
input[type=radio]::before,
input[type=radio]::after {
content: "";
display: block; /* width & height doesn't work on inline elements */
transition: inherit;
/* Let's prevent the pseudo-elements from taking up layout space so that
the ::before and ::after pseudo-elements can be in the same place. This
is also required for vertical-align: baseline to work like we want it to
on radio/checkboxes. If the pseudo-elements take up layout space, the
baseline of text inside them will be used instead. */
position: absolute;
}
input[type=checkbox]:not(.toggle)::after,
input[type=radio]::after {
width: 10px;
height: 2px;
background-color: transparent;
border-radius: 2px;
}
/* ------- CHECKBOXES ------- */
input[type=checkbox]:not(.toggle) {
border-radius: 4px;
}
input[type=checkbox]:not(.toggle):checked,
input[type=checkbox]:not(.toggle):indeterminate {
background-color: var(--novnc-blue);
background-image: var(--button-activation-overlay-light);
background-blend-mode: overlay;
}
input[type=checkbox]:not(.toggle)::before {
width: 25%;
height: 55%;
border-style: solid;
border-color: transparent;
border-width: 0 2px 2px 0;
border-radius: 1px;
transform: translateY(-1px) rotate(35deg);
}
input[type=checkbox]:not(.toggle):checked::before {
border-color: white;
}
input[type=checkbox]:not(.toggle):indeterminate::after {
background-color: white;
}
/* ------- RADIO BUTTONS ------- */
input[type=radio] {
border-radius: 50%;
border: 1px solid transparent; /* To ensure a smooth transition */
}
input[type=radio]:checked {
border: 4px solid var(--novnc-blue);
background-color: white;
/* button-activation-overlay should be removed from the radio
element to not interfere with button-activation-overlay-light
that is set on the ::before element. */
background-image: none;
}
input[type=radio]::before {
width: inherit;
height: inherit;
border-radius: inherit;
/* We can achieve the highlight overlay effect on border colors by
setting button-activation-overlay-light on an element that stays
on top (z-axis) of the element with a border. */
background-image: var(--button-activation-overlay-light);
mix-blend-mode: overlay;
opacity: 0;
}
input[type=radio]:checked::before {
opacity: 1;
}
input[type=radio]:indeterminate::after {
background-color: black;
}
/* ------- TOGGLE SWITCHES ------- */
/* These are meant to be used instead of checkboxes in some cases. If all of
the following critera are true you should use a toggle switch:
* The choice is a simple ON/OFF or ENABLE/DISABLE
* The choice doesn't give the feeling of "I agree" or "I confirm"
* There are not multiple related & grouped options
*/
input[type=checkbox].toggle {
display: inline-block;
--checkradio-height: 18px; /* Height value used in calc, see above */
width: 31px;
cursor: pointer;
user-select: none;
-webkit-user-select: none;
border-radius: 9px;
}
input[type=checkbox].toggle:disabled {
cursor: default;
}
input[type=checkbox].toggle:indeterminate {
background-color: var(--novnc-buttongrey);
background-image: var(--button-activation-overlay);
}
input[type=checkbox].toggle:checked {
background-color: var(--novnc-blue);
background-image: var(--button-activation-overlay-light);
background-blend-mode: overlay;
}
input[type=checkbox].toggle::before {
--circle-diameter: 10px;
--circle-offset: 4px;
width: var(--circle-diameter);
height: var(--circle-diameter);
top: var(--circle-offset);
left: var(--circle-offset);
background: white;
border-radius: 6px;
}
input[type=checkbox].toggle:checked::before {
left: calc(100% - var(--circle-offset) - var(--circle-diameter));
}
input[type=checkbox].toggle:indeterminate::before {
left: calc(50% - var(--circle-diameter) / 2);
}
/* ------- RANGE SLIDERS ------- */
input[type=range] {
border: unset;
border-radius: 8px;
height: 15px;
padding: 0;
background: transparent;
/* Needed to get properly rounded corners on -moz-range-progress
when the thumb is all the way to the right. Without overflow
hidden, the pointy edges of the progress track shows to the
right of the thumb. */
overflow: hidden;
}
@supports selector(::-webkit-slider-thumb) {
input[type=range] {
/* Needs a fixed width to match clip-path */
width: 125px;
/* overflow: hidden is not ideal for hiding the left part of the box
shadow of -webkit-slider-thumb since it doesn't match the smaller
border-radius of the progress track. The below clip-path has two
circular sides to make the ends of the track have correctly rounded
corners. The clip path shape looks something like this:
+-------------------------------+
/---| |---\
| |
\---| |---/
+-------------------------------+
The larger middle part of the clip path is made to have room for the
thumb. By using margins on the track, we prevent the thumb from
touching the ends of the track.
*/
clip-path: path(' \
M 4.5 3 \
L 4.5 0 \
L 120.5 0 \
L 120.5 3 \
A 1 1 0 0 1 120.5 12 \
L 120.5 15 \
L 4.5 15 \
L 4.5 12 \
A 1 1 0 0 1 4.5 3 \
');
}
}
input[type=range]:hover {
cursor: grab;
}
input[type=range]:active {
cursor: grabbing;
}
input[type=range]:disabled {
cursor: default;
}
input[type=range]:focus-visible {
clip-path: none; /* Otherwise it hides the outline */
}
/* -webkit-slider.. & -moz-range.. cant be in selector lists:
https://bugs.chromium.org/p/chromium/issues/detail?id=1154623 */
input[type=range]::-webkit-slider-runnable-track {
background-color: var(--novnc-buttongrey);
height: 7px;
border-radius: 4px;
margin: 0 3px;
}
input[type=range]::-moz-range-track {
background-color: var(--novnc-buttongrey);
height: 7px;
border-radius: 4px;
}
input[type=range]::-moz-range-progress {
background-color: var(--novnc-blue);
height: 9px;
/* Needs rounded corners only on the left side. Otherwise the rounding of
the progress track starts before the thumb, when the thumb is close to
the left edge. */
border-radius: 5px 0 0 5px;
}
input[type=range]::-webkit-slider-thumb {
appearance: none;
width: 15px;
height: 15px;
border-radius: 50%;
background-color: white;
background-image: var(--button-activation-overlay);
/* Disable Chrome's touch tap highlight to avoid conflicts with overlay */
-webkit-tap-highlight-color: transparent;
border: 3px solid var(--novnc-blue);
margin-top: -4px; /* (track height / 2) - (thumb height /2) */
/* Since there is no way to style the left part of the range track in
webkit, we add a large shadow (1000px wide) to the left of the thumb and
then crop it with a clip-path shaped like this:
___
+-------------------/ \
| progress |Thumb|
+-------------------\ ___ /
The large left part of the shadow is clipped by another clip-path on on
the main range input element. */
/* FIXME: We can remove the box shadow workaround when this is standardized:
https://github.com/w3c/csswg-drafts/issues/4410 */
box-shadow: calc(-100vw - 8px) 0 0 100vw var(--novnc-blue);
clip-path: path(' \
M -1000 3 \
L 3 3 \
L 15 7.5 \
A 1 1 0 0 1 0 7.5 \
A 1 1 0 0 1 15 7.5 \
L 3 12 \
L -1000 12 Z \
');
}
input[type=range]::-moz-range-thumb {
appearance: none;
width: 15px;
height: 15px;
border-radius: 50%;
box-sizing: border-box;
background-color: white;
background-image: var(--button-activation-overlay);
border: 3px solid var(--novnc-blue);
margin-top: -7px;
}
/* ------- FILE CHOOSERS ------- */
input[type=file] {
background-image: none;
border: none;
}
input::file-selector-button {
margin-right: 6px;
}
input[type=file]:focus-visible {
outline: none; /* We outline the button instead of the entire element */
}
/* ------- SELECT BUTTONS ------- */
select {
--select-arrow: url('data:image/svg+xml;utf8, \
<svg width="11" height="6" version="1.1" viewBox="0 0 11 6" \
xmlns="http://www.w3.org/2000/svg"> \
<path d="m10.5.5-5 5-5-5" fill="none" \
stroke="black" stroke-width="1.5" \
stroke-linecap="round" stroke-linejoin="round"/> \
</svg>');
/* FIXME: A bug in Firefox, requires a workaround for the background:
https://bugzilla.mozilla.org/show_bug.cgi?id=1810958 */
/* The dropdown list will show the select element's background above and
below the options in Firefox. We want the entire dropdown to be white. */
background-color: white;
/* However, we don't want the select element to actually show a white
background, so let's place a gradient above it with the color we want. */
--grey-background: linear-gradient(var(--novnc-buttongrey) 100%,
transparent);
background-image:
var(--select-arrow),
var(--button-activation-overlay),
var(--grey-background);
background-position: calc(100% - var(--input-xpadding)), left top, left top;
background-repeat: no-repeat;
padding-right: calc(2*var(--input-xpadding) + 11px);
overflow: auto;
}
/* FIXME: :active isn't set when the <select> is opened in Firefox:
https://bugzilla.mozilla.org/show_bug.cgi?id=1805406 */
select:active {
/* Rotated arrow */
background-image: url('data:image/svg+xml;utf8, \
<svg width="11" height="6" version="1.1" viewBox="0 0 11 6" \
xmlns="http://www.w3.org/2000/svg" transform="rotate(180)"> \
<path d="m10.5.5-5 5-5-5" fill="none" \
stroke="black" stroke-width="1.5" \
stroke-linecap="round" stroke-linejoin="round"/> \
</svg>'),
var(--button-activation-overlay),
var(--grey-background);
}
select:disabled {
background-image:
var(--select-arrow),
var(--grey-background);
}
/* Note that styling for <option> doesn't work in all browsers
since its often drawn directly by the OS. We are generally very
limited in what we can change here. */
option {
/* Prevent Chrome from inheriting background-color from the <select> */
background-color: white;
color: black;
font-weight: normal;
background-image: var(--button-activation-overlay);
}
option:checked {
background-color: var(--novnc-lightgrey);
}
/* Change the look when the <select> isn't used as a dropdown. When "size"
or "multiple" are set, these elements behaves more like lists. */
select[size]:not([size="1"]), select[multiple] {
background-color: white;
background-image: unset; /* Don't show the arrow and other gradients */
border: 1px solid var(--novnc-lightgrey);
padding: 0;
font-weight: normal; /* Without this, options get bold font in WebKit. */
/* As an exception to the "list"-look, multi-selects in Chrome on Android,
and Safari on iOS, are unfortunately designed to be shown as a single
line. We can mitigate this inconsistency by at least fixing the height
here. By setting a min-height that matches other input elements, it
doesn't look too much out of place:
(1px border * 2) + (6.5px padding * 2) + 24px line-height = 39px */
min-height: 39px;
}
select[size]:not([size="1"]):focus-visible,
select[multiple]:focus-visible {
/* Text input style focus-visible highlight */
outline-offset: -1px;
}
select[size]:not([size="1"]) option, select[multiple] option {
overflow: hidden;
text-overflow: ellipsis;
padding: 4px var(--input-xpadding);
}

View File

@ -31,6 +31,7 @@ export const encodings = {
pseudoEncodingXvp: -309,
pseudoEncodingFence: -312,
pseudoEncodingContinuousUpdates: -313,
pseudoEncodingExtendedMouseButtons: -316,
pseudoEncodingCompressLevel9: -247,
pseudoEncodingCompressLevel0: -256,
pseudoEncodingVMwareCursor: 0x574d5664,

View File

@ -151,9 +151,13 @@ export default class RFB extends EventTargetMixin {
this._supportsSetDesktopSize = false;
this._screenID = 0;
this._screenFlags = 0;
this._pendingRemoteResize = false;
this._lastResize = 0;
this._qemuExtKeyEventSupported = false;
this._extendedPointerEventSupported = false;
this._clipboardText = null;
this._clipboardServerCapabilitiesActions = {};
this._clipboardServerCapabilitiesFormats = {};
@ -743,6 +747,7 @@ export default class RFB extends EventTargetMixin {
currentHeight == this._expectedClientHeight;
}
// Handle browser window resizes
_handleResize() {
// Don't change anything if the client size is already as expected
if (this._clientHasExpectedSize()) {
@ -753,17 +758,12 @@ export default class RFB extends EventTargetMixin {
window.requestAnimationFrame(() => {
this._updateClip();
this._updateScale();
this._saveExpectedClientSize();
});
if (this._resizeSession) {
// Request changing the resolution of the remote display to
// the size of the local browser viewport.
// In order to not send multiple requests before the browser-resize
// is finished we wait 0.5 seconds before sending the request.
clearTimeout(this._resizeTimeout);
this._resizeTimeout = setTimeout(this._requestRemoteResize.bind(this), 500);
}
this._requestRemoteResize();
}
// Update state of clipping in Display object, and make sure the
@ -813,16 +813,39 @@ export default class RFB extends EventTargetMixin {
// Requests a change of remote desktop size. This message is an extension
// and may only be sent if we have received an ExtendedDesktopSize message
_requestRemoteResize() {
clearTimeout(this._resizeTimeout);
this._resizeTimeout = null;
if (!this._resizeSession || this._viewOnly ||
!this._supportsSetDesktopSize) {
if (!this._resizeSession) {
return;
}
if (this._viewOnly) {
return;
}
if (!this._supportsSetDesktopSize) {
return;
}
// Rate limit to one pending resize at a time
if (this._pendingRemoteResize) {
return;
}
// And no more than once every 100ms
if ((Date.now() - this._lastResize) < 100) {
clearTimeout(this._resizeTimeout);
this._resizeTimeout = setTimeout(this._requestRemoteResize.bind(this),
100 - (Date.now() - this._lastResize));
return;
}
this._resizeTimeout = null;
const size = this._screenSize();
// Do we actually change anything?
if (size.w === this._fbWidth && size.h === this._fbHeight) {
return;
}
this._pendingRemoteResize = true;
this._lastResize = Date.now();
RFB.messages.setDesktopSize(this._sock,
Math.floor(size.w), Math.floor(size.h),
this._screenID, this._screenFlags);
@ -1054,6 +1077,36 @@ export default class RFB extends EventTargetMixin {
this.sendKey(keysym, code, down);
}
static _convertButtonMask(buttons) {
/* The bits in MouseEvent.buttons property correspond
* to the following mouse buttons:
* 0: Left
* 1: Right
* 2: Middle
* 3: Back
* 4: Forward
*
* These bits needs to be converted to what they are defined as
* in the RFB protocol.
*/
const buttonMaskMap = {
0: 1 << 0, // Left
1: 1 << 2, // Right
2: 1 << 1, // Middle
3: 1 << 7, // Back
4: 1 << 8, // Forward
};
let bmask = 0;
for (let i = 0; i < 5; i++) {
if (buttons & (1 << i)) {
bmask |= buttonMaskMap[i];
}
}
return bmask;
}
_handleMouse(ev) {
/*
* We don't check connection status or viewOnly here as the
@ -1083,80 +1136,75 @@ export default class RFB extends EventTargetMixin {
let pos = clientToElement(ev.clientX, ev.clientY,
this._canvas);
let bmask = RFB._convertButtonMask(ev.buttons);
let down = ev.type == 'mousedown';
switch (ev.type) {
case 'mousedown':
setCapture(this._canvas);
this._handleMouseButton(pos.x, pos.y,
true, 1 << ev.button);
break;
case 'mouseup':
this._handleMouseButton(pos.x, pos.y,
false, 1 << ev.button);
break;
case 'mousemove':
this._handleMouseMove(pos.x, pos.y);
break;
}
}
_handleMouseButton(x, y, down, bmask) {
if (this.dragViewport) {
if (down && !this._viewportDragging) {
this._viewportDragging = true;
this._viewportDragPos = {'x': x, 'y': y};
this._viewportDragPos = {'x': pos.x, 'y': pos.y};
this._viewportHasMoved = false;
// Skip sending mouse events
return;
this._flushMouseMoveTimer(pos.x, pos.y);
// Skip sending mouse events, instead save the current
// mouse mask so we can send it later.
this._mouseButtonMask = bmask;
break;
} else {
this._viewportDragging = false;
// If we actually performed a drag then we are done
// here and should not send any mouse events
if (this._viewportHasMoved) {
return;
this._mouseButtonMask = bmask;
break;
}
// Otherwise we treat this as a mouse click event.
// Send the button down event here, as the button up
// event is sent at the end of this function.
this._sendMouse(x, y, bmask);
// Send the previously saved button mask, followed
// by the current button mask at the end of this
// function.
this._sendMouse(pos.x, pos.y, this._mouseButtonMask);
}
}
// Flush waiting move event first
if (this._mouseMoveTimer !== null) {
clearTimeout(this._mouseMoveTimer);
this._mouseMoveTimer = null;
this._sendMouse(x, y, this._mouseButtonMask);
}
if (down) {
this._mouseButtonMask |= bmask;
} else {
this._mouseButtonMask &= ~bmask;
setCapture(this._canvas);
}
this._sendMouse(x, y, this._mouseButtonMask);
}
_handleMouseMove(x, y) {
this._handleMouseButton(pos.x, pos.y, bmask);
break;
case 'mousemove':
if (this._viewportDragging) {
const deltaX = this._viewportDragPos.x - x;
const deltaY = this._viewportDragPos.y - y;
const deltaX = this._viewportDragPos.x - pos.x;
const deltaY = this._viewportDragPos.y - pos.y;
if (this._viewportHasMoved || (Math.abs(deltaX) > dragThreshold ||
Math.abs(deltaY) > dragThreshold)) {
this._viewportHasMoved = true;
this._viewportDragPos = {'x': x, 'y': y};
this._viewportDragPos = {'x': pos.x, 'y': pos.y};
this._display.viewportChangePos(deltaX, deltaY);
}
// Skip sending mouse events
return;
break;
}
this._handleMouseMove(pos.x, pos.y);
break;
}
}
_handleMouseButton(x, y, bmask) {
// Flush waiting move event first
this._flushMouseMoveTimer(x, y);
this._mouseButtonMask = bmask;
this._sendMouse(x, y, this._mouseButtonMask);
}
_handleMouseMove(x, y) {
this._mousePos = { 'x': x, 'y': y };
// Limit many mouse move events to one every MOUSE_MOVE_DELAY ms
@ -1186,9 +1234,21 @@ export default class RFB extends EventTargetMixin {
if (this._rfbConnectionState !== 'connected') { return; }
if (this._viewOnly) { return; } // View only, skip mouse events
// Highest bit in mask is never sent to the server
if (mask & 0x8000) {
throw new Error("Illegal mouse button mask (mask: " + mask + ")");
}
let extendedMouseButtons = mask & 0x7f80;
if (this._extendedPointerEventSupported && extendedMouseButtons) {
RFB.messages.extendedPointerEvent(this._sock, this._display.absX(x),
this._display.absY(y), mask);
} else {
RFB.messages.pointerEvent(this._sock, this._display.absX(x),
this._display.absY(y), mask);
}
}
_handleWheel(ev) {
if (this._rfbConnectionState !== 'connected') { return; }
@ -1200,6 +1260,7 @@ export default class RFB extends EventTargetMixin {
let pos = clientToElement(ev.clientX, ev.clientY,
this._canvas);
let bmask = RFB._convertButtonMask(ev.buttons);
let dX = ev.deltaX;
let dY = ev.deltaY;
@ -1219,26 +1280,27 @@ export default class RFB extends EventTargetMixin {
this._accumulatedWheelDeltaX += dX;
this._accumulatedWheelDeltaY += dY;
// Generate a mouse wheel step event when the accumulated delta
// for one of the axes is large enough.
if (Math.abs(this._accumulatedWheelDeltaX) >= WHEEL_STEP) {
if (this._accumulatedWheelDeltaX < 0) {
this._handleMouseButton(pos.x, pos.y, true, 1 << 5);
this._handleMouseButton(pos.x, pos.y, false, 1 << 5);
this._handleMouseButton(pos.x, pos.y, bmask | 1 << 5);
this._handleMouseButton(pos.x, pos.y, bmask);
} else if (this._accumulatedWheelDeltaX > 0) {
this._handleMouseButton(pos.x, pos.y, true, 1 << 6);
this._handleMouseButton(pos.x, pos.y, false, 1 << 6);
this._handleMouseButton(pos.x, pos.y, bmask | 1 << 6);
this._handleMouseButton(pos.x, pos.y, bmask);
}
this._accumulatedWheelDeltaX = 0;
}
if (Math.abs(this._accumulatedWheelDeltaY) >= WHEEL_STEP) {
if (this._accumulatedWheelDeltaY < 0) {
this._handleMouseButton(pos.x, pos.y, true, 1 << 3);
this._handleMouseButton(pos.x, pos.y, false, 1 << 3);
this._handleMouseButton(pos.x, pos.y, bmask | 1 << 3);
this._handleMouseButton(pos.x, pos.y, bmask);
} else if (this._accumulatedWheelDeltaY > 0) {
this._handleMouseButton(pos.x, pos.y, true, 1 << 4);
this._handleMouseButton(pos.x, pos.y, false, 1 << 4);
this._handleMouseButton(pos.x, pos.y, bmask | 1 << 4);
this._handleMouseButton(pos.x, pos.y, bmask);
}
this._accumulatedWheelDeltaY = 0;
@ -1277,8 +1339,8 @@ export default class RFB extends EventTargetMixin {
this._gestureLastTapTime = Date.now();
this._fakeMouseMove(this._gestureFirstDoubleTapEv, pos.x, pos.y);
this._handleMouseButton(pos.x, pos.y, true, bmask);
this._handleMouseButton(pos.x, pos.y, false, bmask);
this._handleMouseButton(pos.x, pos.y, bmask);
this._handleMouseButton(pos.x, pos.y, 0x0);
}
_handleGesture(ev) {
@ -1299,14 +1361,27 @@ export default class RFB extends EventTargetMixin {
this._handleTapEvent(ev, 0x2);
break;
case 'drag':
if (this.dragViewport) {
this._viewportHasMoved = false;
this._viewportDragging = true;
this._viewportDragPos = {'x': pos.x, 'y': pos.y};
} else {
this._fakeMouseMove(ev, pos.x, pos.y);
this._handleMouseButton(pos.x, pos.y, true, 0x1);
this._handleMouseButton(pos.x, pos.y, 0x1);
}
break;
case 'longpress':
if (this.dragViewport) {
// If dragViewport is true, we need to wait to see
// if we have dragged outside the threshold before
// sending any events to the server.
this._viewportHasMoved = false;
this._viewportDragPos = {'x': pos.x, 'y': pos.y};
} else {
this._fakeMouseMove(ev, pos.x, pos.y);
this._handleMouseButton(pos.x, pos.y, true, 0x4);
this._handleMouseButton(pos.x, pos.y, 0x4);
}
break;
case 'twodrag':
this._gestureLastMagnitudeX = ev.detail.magnitudeX;
this._gestureLastMagnitudeY = ev.detail.magnitudeY;
@ -1328,7 +1403,21 @@ export default class RFB extends EventTargetMixin {
break;
case 'drag':
case 'longpress':
if (this.dragViewport) {
this._viewportDragging = true;
const deltaX = this._viewportDragPos.x - pos.x;
const deltaY = this._viewportDragPos.y - pos.y;
if (this._viewportHasMoved || (Math.abs(deltaX) > dragThreshold ||
Math.abs(deltaY) > dragThreshold)) {
this._viewportHasMoved = true;
this._viewportDragPos = {'x': pos.x, 'y': pos.y};
this._display.viewportChangePos(deltaX, deltaY);
}
} else {
this._fakeMouseMove(ev, pos.x, pos.y);
}
break;
case 'twodrag':
// Always scroll in the same position.
@ -1336,23 +1425,23 @@ export default class RFB extends EventTargetMixin {
// every update.
this._fakeMouseMove(ev, pos.x, pos.y);
while ((ev.detail.magnitudeY - this._gestureLastMagnitudeY) > GESTURE_SCRLSENS) {
this._handleMouseButton(pos.x, pos.y, true, 0x8);
this._handleMouseButton(pos.x, pos.y, false, 0x8);
this._handleMouseButton(pos.x, pos.y, 0x8);
this._handleMouseButton(pos.x, pos.y, 0x0);
this._gestureLastMagnitudeY += GESTURE_SCRLSENS;
}
while ((ev.detail.magnitudeY - this._gestureLastMagnitudeY) < -GESTURE_SCRLSENS) {
this._handleMouseButton(pos.x, pos.y, true, 0x10);
this._handleMouseButton(pos.x, pos.y, false, 0x10);
this._handleMouseButton(pos.x, pos.y, 0x10);
this._handleMouseButton(pos.x, pos.y, 0x0);
this._gestureLastMagnitudeY -= GESTURE_SCRLSENS;
}
while ((ev.detail.magnitudeX - this._gestureLastMagnitudeX) > GESTURE_SCRLSENS) {
this._handleMouseButton(pos.x, pos.y, true, 0x20);
this._handleMouseButton(pos.x, pos.y, false, 0x20);
this._handleMouseButton(pos.x, pos.y, 0x20);
this._handleMouseButton(pos.x, pos.y, 0x0);
this._gestureLastMagnitudeX += GESTURE_SCRLSENS;
}
while ((ev.detail.magnitudeX - this._gestureLastMagnitudeX) < -GESTURE_SCRLSENS) {
this._handleMouseButton(pos.x, pos.y, true, 0x40);
this._handleMouseButton(pos.x, pos.y, false, 0x40);
this._handleMouseButton(pos.x, pos.y, 0x40);
this._handleMouseButton(pos.x, pos.y, 0x0);
this._gestureLastMagnitudeX -= GESTURE_SCRLSENS;
}
break;
@ -1365,13 +1454,13 @@ export default class RFB extends EventTargetMixin {
if (Math.abs(magnitude - this._gestureLastMagnitudeX) > GESTURE_ZOOMSENS) {
this._handleKeyEvent(KeyTable.XK_Control_L, "ControlLeft", true);
while ((magnitude - this._gestureLastMagnitudeX) > GESTURE_ZOOMSENS) {
this._handleMouseButton(pos.x, pos.y, true, 0x8);
this._handleMouseButton(pos.x, pos.y, false, 0x8);
this._handleMouseButton(pos.x, pos.y, 0x8);
this._handleMouseButton(pos.x, pos.y, 0x0);
this._gestureLastMagnitudeX += GESTURE_ZOOMSENS;
}
while ((magnitude - this._gestureLastMagnitudeX) < -GESTURE_ZOOMSENS) {
this._handleMouseButton(pos.x, pos.y, true, 0x10);
this._handleMouseButton(pos.x, pos.y, false, 0x10);
this._handleMouseButton(pos.x, pos.y, 0x10);
this._handleMouseButton(pos.x, pos.y, 0x0);
this._gestureLastMagnitudeX -= GESTURE_ZOOMSENS;
}
}
@ -1389,12 +1478,32 @@ export default class RFB extends EventTargetMixin {
case 'twodrag':
break;
case 'drag':
if (this.dragViewport) {
this._viewportDragging = false;
} else {
this._fakeMouseMove(ev, pos.x, pos.y);
this._handleMouseButton(pos.x, pos.y, false, 0x1);
this._handleMouseButton(pos.x, pos.y, 0x0);
}
break;
case 'longpress':
if (this._viewportHasMoved) {
// We don't want to send any events if we have moved
// our viewport
break;
}
if (this.dragViewport && !this._viewportHasMoved) {
this._fakeMouseMove(ev, pos.x, pos.y);
this._handleMouseButton(pos.x, pos.y, false, 0x4);
// If dragViewport is true, we need to wait to see
// if we have dragged outside the threshold before
// sending any events to the server.
this._handleMouseButton(pos.x, pos.y, 0x4);
this._handleMouseButton(pos.x, pos.y, 0x0);
this._viewportDragging = false;
} else {
this._fakeMouseMove(ev, pos.x, pos.y);
this._handleMouseButton(pos.x, pos.y, 0x0);
}
break;
}
break;
@ -1479,6 +1588,14 @@ export default class RFB extends EventTargetMixin {
this._sock.flush();
}
_flushMouseMoveTimer(x, y) {
if (this._mouseMoveTimer !== null) {
clearTimeout(this._mouseMoveTimer);
this._mouseMoveTimer = null;
this._sendMouse(x, y, this._mouseButtonMask);
}
}
// Message handlers
_negotiateProtocolVersion() {
@ -2247,6 +2364,7 @@ export default class RFB extends EventTargetMixin {
encs.push(encodings.pseudoEncodingContinuousUpdates);
encs.push(encodings.pseudoEncodingDesktopName);
encs.push(encodings.pseudoEncodingExtendedClipboard);
encs.push(encodings.pseudoEncodingExtendedMouseButtons);
if (this._gesturesMode === 'ultravnc') {
encs.push(encodings.pseudoEncodingGii);
@ -2707,6 +2825,10 @@ export default class RFB extends EventTargetMixin {
case encodings.pseudoEncodingExtendedDesktopSize:
return this._handleExtendedDesktopSize();
case encodings.pseudoEncodingExtendedMouseButtons:
this._extendedPointerEventSupported = true;
return true;
case encodings.pseudoEncodingQEMULedEvent:
return this._handleLedEvent();
@ -2942,6 +3064,10 @@ export default class RFB extends EventTargetMixin {
* 2 - another client requested the resize
*/
if (this._FBU.x === 1) {
this._pendingRemoteResize = false;
}
// We need to handle errors when we requested the resize.
if (this._FBU.x === 1 && this._FBU.y !== 0) {
let msg = "";
@ -2974,6 +3100,12 @@ export default class RFB extends EventTargetMixin {
this._requestRemoteResize();
}
if (this._FBU.x === 1 && this._FBU.y === 0) {
// We might have resized again whilst waiting for the
// previous request, so check if we are in sync
this._requestRemoteResize();
}
return true;
}
@ -3003,6 +3135,7 @@ export default class RFB extends EventTargetMixin {
this._fbWidth, this._fbHeight);
}
// Handle resize-messages from the server
_resize(width, height) {
this._fbWidth = width;
this._fbHeight = height;
@ -3125,6 +3258,10 @@ RFB.messages = {
pointerEvent(sock, x, y, mask) {
sock.sQpush8(5); // msg-type
// Marker bit must be set to 0, otherwise the server might
// confuse the marker bit with the highest bit in a normal
// PointerEvent message.
mask = mask & 0x7f;
sock.sQpush8(mask);
sock.sQpush16(x);
@ -3133,6 +3270,27 @@ RFB.messages = {
sock.flush();
},
extendedPointerEvent(sock, x, y, mask) {
sock.sQpush8(5); // msg-type
let higherBits = (mask >> 7) & 0xff;
// Bits 2-7 are reserved
if (higherBits & 0xfc) {
throw new Error("Invalid mouse button mask: " + mask);
}
let lowerBits = mask & 0x7f;
lowerBits |= 0x80; // Set marker bit to 1
sock.sQpush8(lowerBits);
sock.sQpush16(x);
sock.sQpush16(y);
sock.sQpush8(higherBits);
sock.flush();
},
// Used to build Notify and Request data.
_buildExtendedClipboardFlags(actions, formats) {
let data = new Uint8Array(4);

View File

@ -13,7 +13,7 @@ import Base64 from '../base64.js';
// Touch detection
export let isTouchDevice = ('ontouchstart' in document.documentElement) ||
// requried for Chrome debugger
// required for Chrome debugger
(document.ontouchstart !== undefined) ||
// required for MS Surface
(navigator.maxTouchPoints > 0) ||

View File

@ -1,6 +1,6 @@
{
"name": "@novnc/novnc",
"version": "1.5.0",
"version": "1.6.0-beta",
"description": "An HTML5 VNC client",
"browser": "lib/rfb",
"directories": {
@ -56,8 +56,7 @@
"karma-safari-launcher": "latest",
"karma-script-launcher": "latest",
"mocha": "latest",
"node-getopt": "latest",
"po2json": "latest",
"pofile": "latest",
"sinon": "latest",
"sinon-chai": "latest"
},

199
po/ja.po
View File

@ -1,15 +1,15 @@
# Japanese translations for noVNC package
# noVNC パッケージに対する日訳
# Copyright (C) 2019 The noVNC authors
# Copyright (C) 2019-2024 The noVNC authors
# This file is distributed under the same license as the noVNC package.
# nnn1590 <nnn1590@nnn1590.org>, 2019-2020.
# nnn1590 <nnn1590@nnn1590.org>, 2019-2024.
#
msgid ""
msgstr ""
"Project-Id-Version: noVNC 1.1.0\n"
"Project-Id-Version: noVNC 1.5.0\n"
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
"POT-Creation-Date: 2022-12-27 15:24+0100\n"
"PO-Revision-Date: 2023-03-21 12:42+0900\n"
"POT-Creation-Date: 2024-06-03 14:10+0200\n"
"PO-Revision-Date: 2024-12-14 15:22+0900\n"
"Last-Translator: nnn1590 <nnn1590@nnn1590.org>\n"
"Language-Team: Japanese\n"
"Language: ja\n"
@ -20,8 +20,11 @@ msgstr ""
"X-Generator: Poedit 2.3\n"
#: ../app/ui.js:69
msgid "HTTPS is required for full functionality"
msgstr "すべての機能を使用するにはHTTPS接続が必要です"
msgid ""
"Running without HTTPS is not recommended, crashes or other issues are likely."
msgstr ""
"HTTPS接続なしで実行することは推奨されません。クラッシュしたりその他の問題が発"
"生したりする可能性があります。"
#: ../app/ui.js:410
msgid "Connecting..."
@ -43,321 +46,297 @@ msgstr "内部エラー"
msgid "Must set host"
msgstr "ホストを設定する必要があります"
#: ../app/ui.js:1110
#: ../app/ui.js:1052
msgid "Failed to connect to server: "
msgstr "サーバーへの接続に失敗しました: "
#: ../app/ui.js:1118
msgid "Connected (encrypted) to "
msgstr "接続しました (暗号化済み): "
#: ../app/ui.js:1112
#: ../app/ui.js:1120
msgid "Connected (unencrypted) to "
msgstr "接続しました (暗号化されていません): "
#: ../app/ui.js:1135
#: ../app/ui.js:1143
msgid "Something went wrong, connection is closed"
msgstr "何らかの問題で、接続が閉じられました"
msgstr "問題が発生したため、接続が閉じられました"
#: ../app/ui.js:1138
#: ../app/ui.js:1146
msgid "Failed to connect to server"
msgstr "サーバーへの接続に失敗しました"
#: ../app/ui.js:1150
#: ../app/ui.js:1158
msgid "Disconnected"
msgstr "切断しました"
#: ../app/ui.js:1165
#: ../app/ui.js:1173
msgid "New connection has been rejected with reason: "
msgstr "新規接続は次の理由で拒否されました: "
#: ../app/ui.js:1168
#: ../app/ui.js:1176
msgid "New connection has been rejected"
msgstr "新規接続は拒否されました"
#: ../app/ui.js:1234
#: ../app/ui.js:1242
msgid "Credentials are required"
msgstr "資格情報が必要です"
#: ../vnc.html:57
#: ../vnc.html:55
msgid "noVNC encountered an error:"
msgstr "noVNC でエラーが発生しました:"
#: ../vnc.html:67
#: ../vnc.html:65
msgid "Hide/Show the control bar"
msgstr "コントロールバーを隠す/表示する"
#: ../vnc.html:76
#: ../vnc.html:74
msgid "Drag"
msgstr "ドラッグ"
#: ../vnc.html:76
#: ../vnc.html:74
msgid "Move/Drag viewport"
msgstr "ビューポートを移動/ドラッグ"
#: ../vnc.html:82
#: ../vnc.html:80
msgid "Keyboard"
msgstr "キーボード"
#: ../vnc.html:82
#: ../vnc.html:80
msgid "Show keyboard"
msgstr "キーボードを表示"
#: ../vnc.html:87
#: ../vnc.html:85
msgid "Extra keys"
msgstr "追加キー"
#: ../vnc.html:87
#: ../vnc.html:85
msgid "Show extra keys"
msgstr "追加キーを表示"
#: ../vnc.html:92
#: ../vnc.html:90
msgid "Ctrl"
msgstr "Ctrl"
#: ../vnc.html:92
#: ../vnc.html:90
msgid "Toggle Ctrl"
msgstr "Ctrl キーをトグル"
#: ../vnc.html:95
#: ../vnc.html:93
msgid "Alt"
msgstr "Alt"
#: ../vnc.html:95
#: ../vnc.html:93
msgid "Toggle Alt"
msgstr "Alt キーをトグル"
#: ../vnc.html:98
#: ../vnc.html:96
msgid "Toggle Windows"
msgstr "Windows キーをトグル"
#: ../vnc.html:98
#: ../vnc.html:96
msgid "Windows"
msgstr "Windows"
#: ../vnc.html:101
#: ../vnc.html:99
msgid "Send Tab"
msgstr "Tab キーを送信"
#: ../vnc.html:101
#: ../vnc.html:99
msgid "Tab"
msgstr "Tab"
#: ../vnc.html:104
#: ../vnc.html:102
msgid "Esc"
msgstr "Esc"
#: ../vnc.html:104
#: ../vnc.html:102
msgid "Send Escape"
msgstr "Escape キーを送信"
#: ../vnc.html:107
#: ../vnc.html:105
msgid "Ctrl+Alt+Del"
msgstr "Ctrl+Alt+Del"
#: ../vnc.html:107
#: ../vnc.html:105
msgid "Send Ctrl-Alt-Del"
msgstr "Ctrl-Alt-Del を送信"
#: ../vnc.html:114
#: ../vnc.html:112
msgid "Shutdown/Reboot"
msgstr "シャットダウン/再起動"
#: ../vnc.html:114
#: ../vnc.html:112
msgid "Shutdown/Reboot..."
msgstr "シャットダウン/再起動..."
#: ../vnc.html:120
#: ../vnc.html:118
msgid "Power"
msgstr "電源"
#: ../vnc.html:122
#: ../vnc.html:120
msgid "Shutdown"
msgstr "シャットダウン"
#: ../vnc.html:123
#: ../vnc.html:121
msgid "Reboot"
msgstr "再起動"
#: ../vnc.html:124
#: ../vnc.html:122
msgid "Reset"
msgstr "リセット"
#: ../vnc.html:129 ../vnc.html:135
#: ../vnc.html:127 ../vnc.html:133
msgid "Clipboard"
msgstr "クリップボード"
#: ../vnc.html:137
#: ../vnc.html:135
msgid "Edit clipboard content in the textarea below."
msgstr "以下の入力欄からクリップボードの内容を編集できます。"
#: ../vnc.html:145
#: ../vnc.html:143
msgid "Full screen"
msgstr "全画面表示"
#: ../vnc.html:150 ../vnc.html:156
#: ../vnc.html:148 ../vnc.html:154
msgid "Settings"
msgstr "設定"
#: ../vnc.html:160
#: ../vnc.html:158
msgid "Shared mode"
msgstr "共有モード"
#: ../vnc.html:163
#: ../vnc.html:161
msgid "View only"
msgstr "表示専用"
#: ../vnc.html:167
#: ../vnc.html:165
msgid "Clip to window"
msgstr "ウィンドウにクリップ"
#: ../vnc.html:170
#: ../vnc.html:168
msgid "Scaling mode:"
msgstr "スケーリングモード:"
#: ../vnc.html:172
#: ../vnc.html:170
msgid "None"
msgstr "なし"
#: ../vnc.html:173
#: ../vnc.html:171
msgid "Local scaling"
msgstr "ローカルスケーリング"
msgstr "ローカルスケーリング"
#: ../vnc.html:174
#: ../vnc.html:172
msgid "Remote resizing"
msgstr "リモートでリサイズ"
#: ../vnc.html:179
#: ../vnc.html:177
msgid "Advanced"
msgstr "高度"
#: ../vnc.html:182
#: ../vnc.html:180
msgid "Quality:"
msgstr "品質:"
#: ../vnc.html:186
#: ../vnc.html:184
msgid "Compression level:"
msgstr "圧縮レベル:"
#: ../vnc.html:191
#: ../vnc.html:189
msgid "Repeater ID:"
msgstr "リピーター ID:"
#: ../vnc.html:195
#: ../vnc.html:193
msgid "WebSocket"
msgstr "WebSocket"
#: ../vnc.html:198
#: ../vnc.html:196
msgid "Encrypt"
msgstr "暗号化"
#: ../vnc.html:201
#: ../vnc.html:199
msgid "Host:"
msgstr "ホスト:"
#: ../vnc.html:205
#: ../vnc.html:203
msgid "Port:"
msgstr "ポート:"
#: ../vnc.html:209
#: ../vnc.html:207
msgid "Path:"
msgstr "パス:"
#: ../vnc.html:216
#: ../vnc.html:214
msgid "Automatic reconnect"
msgstr "自動再接続"
#: ../vnc.html:219
#: ../vnc.html:217
msgid "Reconnect delay (ms):"
msgstr "再接続する遅延 (ミリ秒):"
#: ../vnc.html:224
#: ../vnc.html:222
msgid "Show dot when no cursor"
msgstr "カーソルがないときにドットを表示する"
#: ../vnc.html:229
#: ../vnc.html:227
msgid "Logging:"
msgstr "ロギング:"
#: ../vnc.html:238
#: ../vnc.html:236
msgid "Version:"
msgstr "バージョン:"
#: ../vnc.html:246
#: ../vnc.html:244
msgid "Disconnect"
msgstr "切断"
#: ../vnc.html:269
#: ../vnc.html:267
msgid "Connect"
msgstr "接続"
#: ../vnc.html:278
#: ../vnc.html:276
msgid "Server identity"
msgstr "サーバーの識別情報"
#: ../vnc.html:281
#: ../vnc.html:279
msgid "The server has provided the following identifying information:"
msgstr "サーバーは以下の識別情報を提供しています:"
#: ../vnc.html:285
#: ../vnc.html:283
msgid "Fingerprint:"
msgstr "フィンガープリント:"
#: ../vnc.html:288
#: ../vnc.html:286
msgid ""
"Please verify that the information is correct and press \"Approve\". "
"Otherwise press \"Reject\"."
msgstr ""
"この情報が正しい場合は「承認」を、そうでない場合は「拒否」を押してく"
"ださい。"
"この情報が正しい場合は「承認」を、そうでない場合は「拒否」を押してください。"
#: ../vnc.html:293
#: ../vnc.html:291
msgid "Approve"
msgstr "承認"
#: ../vnc.html:294
#: ../vnc.html:292
msgid "Reject"
msgstr "拒否"
#: ../vnc.html:302
#: ../vnc.html:300
msgid "Credentials"
msgstr "資格情報"
#: ../vnc.html:306
#: ../vnc.html:304
msgid "Username:"
msgstr "ユーザー名:"
#: ../vnc.html:310
#: ../vnc.html:308
msgid "Password:"
msgstr "パスワード:"
#: ../vnc.html:314
#: ../vnc.html:312
msgid "Send credentials"
msgstr "資格情報を送信"
#: ../vnc.html:323
#: ../vnc.html:321
msgid "Cancel"
msgstr "キャンセル"
#~ msgid "Clear"
#~ msgstr "クリア"
#~ msgid "Password is required"
#~ msgstr "パスワードが必要です"
#~ msgid "viewport drag"
#~ msgstr "ビューポートをドラッグ"
#~ msgid "Active Mouse Button"
#~ msgstr "アクティブなマウスボタン"
#~ msgid "No mousebutton"
#~ msgstr "マウスボタンなし"
#~ msgid "Left mousebutton"
#~ msgstr "左マウスボタン"
#~ msgid "Middle mousebutton"
#~ msgstr "中マウスボタン"
#~ msgid "Right mousebutton"
#~ msgstr "右マウスボタン"
#~ msgid "Send Password"
#~ msgstr "パスワードを送信"

View File

@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: noVNC 1.5.0\n"
"Project-Id-Version: noVNC 1.6.0\n"
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
"POT-Creation-Date: 2024-06-03 14:10+0200\n"
"POT-Creation-Date: 2025-02-14 10:14+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@ -17,321 +17,317 @@ msgstr ""
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"
#: ../app/ui.js:69
#: ../app/ui.js:84
msgid ""
"Running without HTTPS is not recommended, crashes or other issues are likely."
msgstr ""
#: ../app/ui.js:410
#: ../app/ui.js:413
msgid "Connecting..."
msgstr ""
#: ../app/ui.js:417
#: ../app/ui.js:420
msgid "Disconnecting..."
msgstr ""
#: ../app/ui.js:423
#: ../app/ui.js:426
msgid "Reconnecting..."
msgstr ""
#: ../app/ui.js:428
#: ../app/ui.js:431
msgid "Internal error"
msgstr ""
#: ../app/ui.js:1026
msgid "Must set host"
msgstr ""
#: ../app/ui.js:1052
#: ../app/ui.js:1079
msgid "Failed to connect to server: "
msgstr ""
#: ../app/ui.js:1118
#: ../app/ui.js:1145
msgid "Connected (encrypted) to "
msgstr ""
#: ../app/ui.js:1120
#: ../app/ui.js:1147
msgid "Connected (unencrypted) to "
msgstr ""
#: ../app/ui.js:1143
#: ../app/ui.js:1170
msgid "Something went wrong, connection is closed"
msgstr ""
#: ../app/ui.js:1146
#: ../app/ui.js:1173
msgid "Failed to connect to server"
msgstr ""
#: ../app/ui.js:1158
#: ../app/ui.js:1185
msgid "Disconnected"
msgstr ""
#: ../app/ui.js:1173
#: ../app/ui.js:1200
msgid "New connection has been rejected with reason: "
msgstr ""
#: ../app/ui.js:1176
#: ../app/ui.js:1203
msgid "New connection has been rejected"
msgstr ""
#: ../app/ui.js:1242
#: ../app/ui.js:1269
msgid "Credentials are required"
msgstr ""
#: ../vnc.html:55
#: ../vnc.html:106
msgid "noVNC encountered an error:"
msgstr ""
#: ../vnc.html:65
#: ../vnc.html:116
msgid "Hide/Show the control bar"
msgstr ""
#: ../vnc.html:74
#: ../vnc.html:125
msgid "Drag"
msgstr ""
#: ../vnc.html:74
#: ../vnc.html:125
msgid "Move/Drag viewport"
msgstr ""
#: ../vnc.html:80
#: ../vnc.html:131
msgid "Keyboard"
msgstr ""
#: ../vnc.html:80
#: ../vnc.html:131
msgid "Show keyboard"
msgstr ""
#: ../vnc.html:85
#: ../vnc.html:136
msgid "Extra keys"
msgstr ""
#: ../vnc.html:85
#: ../vnc.html:136
msgid "Show extra keys"
msgstr ""
#: ../vnc.html:90
#: ../vnc.html:141
msgid "Ctrl"
msgstr ""
#: ../vnc.html:90
#: ../vnc.html:141
msgid "Toggle Ctrl"
msgstr ""
#: ../vnc.html:93
#: ../vnc.html:144
msgid "Alt"
msgstr ""
#: ../vnc.html:93
#: ../vnc.html:144
msgid "Toggle Alt"
msgstr ""
#: ../vnc.html:96
#: ../vnc.html:147
msgid "Toggle Windows"
msgstr ""
#: ../vnc.html:96
#: ../vnc.html:147
msgid "Windows"
msgstr ""
#: ../vnc.html:99
#: ../vnc.html:150
msgid "Send Tab"
msgstr ""
#: ../vnc.html:99
#: ../vnc.html:150
msgid "Tab"
msgstr ""
#: ../vnc.html:102
#: ../vnc.html:153
msgid "Esc"
msgstr ""
#: ../vnc.html:102
#: ../vnc.html:153
msgid "Send Escape"
msgstr ""
#: ../vnc.html:105
#: ../vnc.html:156
msgid "Ctrl+Alt+Del"
msgstr ""
#: ../vnc.html:105
#: ../vnc.html:156
msgid "Send Ctrl-Alt-Del"
msgstr ""
#: ../vnc.html:112
#: ../vnc.html:163
msgid "Shutdown/Reboot"
msgstr ""
#: ../vnc.html:112
#: ../vnc.html:163
msgid "Shutdown/Reboot..."
msgstr ""
#: ../vnc.html:118
#: ../vnc.html:169
msgid "Power"
msgstr ""
#: ../vnc.html:120
#: ../vnc.html:171
msgid "Shutdown"
msgstr ""
#: ../vnc.html:121
#: ../vnc.html:172
msgid "Reboot"
msgstr ""
#: ../vnc.html:122
#: ../vnc.html:173
msgid "Reset"
msgstr ""
#: ../vnc.html:127 ../vnc.html:133
#: ../vnc.html:178 ../vnc.html:184
msgid "Clipboard"
msgstr ""
#: ../vnc.html:135
#: ../vnc.html:186
msgid "Edit clipboard content in the textarea below."
msgstr ""
#: ../vnc.html:143
#: ../vnc.html:194
msgid "Full screen"
msgstr ""
#: ../vnc.html:148 ../vnc.html:154
#: ../vnc.html:199 ../vnc.html:205
msgid "Settings"
msgstr ""
#: ../vnc.html:158
#: ../vnc.html:211
msgid "Shared mode"
msgstr ""
#: ../vnc.html:161
#: ../vnc.html:218
msgid "View only"
msgstr ""
#: ../vnc.html:165
#: ../vnc.html:226
msgid "Clip to window"
msgstr ""
#: ../vnc.html:168
#: ../vnc.html:231
msgid "Scaling mode:"
msgstr ""
#: ../vnc.html:170
#: ../vnc.html:233
msgid "None"
msgstr ""
#: ../vnc.html:171
#: ../vnc.html:234
msgid "Local scaling"
msgstr ""
#: ../vnc.html:172
#: ../vnc.html:235
msgid "Remote resizing"
msgstr ""
#: ../vnc.html:177
#: ../vnc.html:240
msgid "Advanced"
msgstr ""
#: ../vnc.html:180
#: ../vnc.html:243
msgid "Quality:"
msgstr ""
#: ../vnc.html:184
#: ../vnc.html:247
msgid "Compression level:"
msgstr ""
#: ../vnc.html:189
#: ../vnc.html:252
msgid "Repeater ID:"
msgstr ""
#: ../vnc.html:193
#: ../vnc.html:256
msgid "WebSocket"
msgstr ""
#: ../vnc.html:196
#: ../vnc.html:261
msgid "Encrypt"
msgstr ""
#: ../vnc.html:199
#: ../vnc.html:266
msgid "Host:"
msgstr ""
#: ../vnc.html:203
#: ../vnc.html:270
msgid "Port:"
msgstr ""
#: ../vnc.html:207
#: ../vnc.html:274
msgid "Path:"
msgstr ""
#: ../vnc.html:214
#: ../vnc.html:283
msgid "Automatic reconnect"
msgstr ""
#: ../vnc.html:217
#: ../vnc.html:288
msgid "Reconnect delay (ms):"
msgstr ""
#: ../vnc.html:222
#: ../vnc.html:295
msgid "Show dot when no cursor"
msgstr ""
#: ../vnc.html:227
#: ../vnc.html:302
msgid "Logging:"
msgstr ""
#: ../vnc.html:236
#: ../vnc.html:311
msgid "Version:"
msgstr ""
#: ../vnc.html:244
#: ../vnc.html:319
msgid "Disconnect"
msgstr ""
#: ../vnc.html:267
#: ../vnc.html:342
msgid "Connect"
msgstr ""
#: ../vnc.html:276
#: ../vnc.html:351
msgid "Server identity"
msgstr ""
#: ../vnc.html:279
#: ../vnc.html:354
msgid "The server has provided the following identifying information:"
msgstr ""
#: ../vnc.html:283
#: ../vnc.html:357
msgid "Fingerprint:"
msgstr ""
#: ../vnc.html:286
#: ../vnc.html:361
msgid ""
"Please verify that the information is correct and press \"Approve\". "
"Otherwise press \"Reject\"."
msgstr ""
#: ../vnc.html:291
#: ../vnc.html:366
msgid "Approve"
msgstr ""
#: ../vnc.html:292
#: ../vnc.html:367
msgid "Reject"
msgstr ""
#: ../vnc.html:300
#: ../vnc.html:375
msgid "Credentials"
msgstr ""
#: ../vnc.html:304
#: ../vnc.html:379
msgid "Username:"
msgstr ""
#: ../vnc.html:308
#: ../vnc.html:383
msgid "Password:"
msgstr ""
#: ../vnc.html:312
#: ../vnc.html:387
msgid "Send credentials"
msgstr ""
#: ../vnc.html:321
#: ../vnc.html:396
msgid "Cancel"
msgstr ""

View File

@ -17,29 +17,24 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
const getopt = require('node-getopt');
const { program } = require('commander');
const fs = require('fs');
const po2json = require("po2json");
const pofile = require("pofile");
const opt = getopt.create([
['h', 'help', 'display this help'],
]).bindHelp().parseSystem();
program
.argument('<input>')
.argument('<output>')
.parse(process.argv);
if (opt.argv.length != 2) {
console.error("Incorrect number of arguments given");
process.exit(1);
}
let data = fs.readFileSync(program.args[0], "utf8");
let po = pofile.parse(data);
const data = po2json.parseFileSync(opt.argv[0]);
const bodyPart = Object.keys(data)
.filter(msgid => msgid !== "")
.filter(msgid => data[msgid][1] !== "")
.map((msgid) => {
const msgstr = data[msgid][1];
return " " + JSON.stringify(msgid) + ": " + JSON.stringify(msgstr);
}).join(",\n");
const bodyPart = po.items
.filter(item => item.msgid !== "")
.filter(item => item.msgstr[0] !== "")
.map(item => " " + JSON.stringify(item.msgid) + ": " + JSON.stringify(item.msgstr[0]))
.join(",\n");
const output = "{\n" + bodyPart + "\n}";
fs.writeFileSync(opt.argv[1], output);
fs.writeFileSync(program.args[1], output);

215
po/sv.po
View File

@ -1,312 +1,308 @@
# Swedish translations for noVNC package
# Svenska översättningar för paketet noVNC.
# Copyright (C) 2020 The noVNC authors
# Copyright (C) 2025 The noVNC authors
# This file is distributed under the same license as the noVNC package.
# Samuel Mannehed <samuel@cendio.se>, 2020.
#
msgid ""
msgstr ""
"Project-Id-Version: noVNC 1.3.0\n"
"Project-Id-Version: noVNC 1.6.0\n"
"Report-Msgid-Bugs-To: novnc@googlegroups.com\n"
"POT-Creation-Date: 2024-06-03 14:10+0200\n"
"PO-Revision-Date: 2024-06-18 13:52+0200\n"
"Last-Translator: Pierre Ossman <ossman@cendio.se>\n"
"POT-Creation-Date: 2025-02-14 10:14+0100\n"
"PO-Revision-Date: 2025-02-14 10:29+0100\n"
"Last-Translator: Alexander Zeijlon <aleze@cendio.com>\n"
"Language-Team: none\n"
"Language: sv\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"X-Generator: Poedit 3.4.4\n"
"X-Generator: Poedit 3.5\n"
#: ../app/ui.js:69
#: ../app/ui.js:84
msgid ""
"Running without HTTPS is not recommended, crashes or other issues are likely."
msgstr ""
"Det är ej rekommenderat att köra utan HTTPS, krascher och andra problem är "
"troliga."
#: ../app/ui.js:410
#: ../app/ui.js:413
msgid "Connecting..."
msgstr "Ansluter..."
#: ../app/ui.js:417
#: ../app/ui.js:420
msgid "Disconnecting..."
msgstr "Kopplar ner..."
#: ../app/ui.js:423
#: ../app/ui.js:426
msgid "Reconnecting..."
msgstr "Återansluter..."
#: ../app/ui.js:428
#: ../app/ui.js:431
msgid "Internal error"
msgstr "Internt fel"
#: ../app/ui.js:1026
msgid "Must set host"
msgstr "Du måste specifiera en värd"
#: ../app/ui.js:1052
#: ../app/ui.js:1079
msgid "Failed to connect to server: "
msgstr "Misslyckades att ansluta till servern: "
#: ../app/ui.js:1118
#: ../app/ui.js:1145
msgid "Connected (encrypted) to "
msgstr "Ansluten (krypterat) till "
#: ../app/ui.js:1120
#: ../app/ui.js:1147
msgid "Connected (unencrypted) to "
msgstr "Ansluten (okrypterat) till "
#: ../app/ui.js:1143
#: ../app/ui.js:1170
msgid "Something went wrong, connection is closed"
msgstr "Något gick fel, anslutningen avslutades"
#: ../app/ui.js:1146
#: ../app/ui.js:1173
msgid "Failed to connect to server"
msgstr "Misslyckades att ansluta till servern"
#: ../app/ui.js:1158
#: ../app/ui.js:1185
msgid "Disconnected"
msgstr "Frånkopplad"
#: ../app/ui.js:1173
#: ../app/ui.js:1200
msgid "New connection has been rejected with reason: "
msgstr "Ny anslutning har blivit nekad med följande skäl: "
#: ../app/ui.js:1176
#: ../app/ui.js:1203
msgid "New connection has been rejected"
msgstr "Ny anslutning har blivit nekad"
#: ../app/ui.js:1242
#: ../app/ui.js:1269
msgid "Credentials are required"
msgstr "Användaruppgifter krävs"
#: ../vnc.html:55
#: ../vnc.html:106
msgid "noVNC encountered an error:"
msgstr "noVNC stötte på ett problem:"
#: ../vnc.html:65
#: ../vnc.html:116
msgid "Hide/Show the control bar"
msgstr "Göm/Visa kontrollbaren"
#: ../vnc.html:74
#: ../vnc.html:125
msgid "Drag"
msgstr "Dra"
#: ../vnc.html:74
msgid "Move/Drag Viewport"
msgstr "Flytta/Dra Vyn"
#: ../vnc.html:125
msgid "Move/Drag viewport"
msgstr "Flytta/Dra vyn"
#: ../vnc.html:80
#: ../vnc.html:131
msgid "Keyboard"
msgstr "Tangentbord"
#: ../vnc.html:80
msgid "Show Keyboard"
msgstr "Visa Tangentbord"
#: ../vnc.html:131
msgid "Show keyboard"
msgstr "Visa tangentbord"
#: ../vnc.html:85
#: ../vnc.html:136
msgid "Extra keys"
msgstr "Extraknappar"
#: ../vnc.html:85
msgid "Show Extra Keys"
msgstr "Visa Extraknappar"
#: ../vnc.html:136
msgid "Show extra keys"
msgstr "Visa extraknappar"
#: ../vnc.html:90
#: ../vnc.html:141
msgid "Ctrl"
msgstr "Ctrl"
#: ../vnc.html:90
#: ../vnc.html:141
msgid "Toggle Ctrl"
msgstr "Växla Ctrl"
#: ../vnc.html:93
#: ../vnc.html:144
msgid "Alt"
msgstr "Alt"
#: ../vnc.html:93
#: ../vnc.html:144
msgid "Toggle Alt"
msgstr "Växla Alt"
#: ../vnc.html:96
#: ../vnc.html:147
msgid "Toggle Windows"
msgstr "Växla Windows"
#: ../vnc.html:96
#: ../vnc.html:147
msgid "Windows"
msgstr "Windows"
#: ../vnc.html:99
#: ../vnc.html:150
msgid "Send Tab"
msgstr "Skicka Tab"
#: ../vnc.html:99
#: ../vnc.html:150
msgid "Tab"
msgstr "Tab"
#: ../vnc.html:102
#: ../vnc.html:153
msgid "Esc"
msgstr "Esc"
#: ../vnc.html:102
#: ../vnc.html:153
msgid "Send Escape"
msgstr "Skicka Escape"
#: ../vnc.html:105
#: ../vnc.html:156
msgid "Ctrl+Alt+Del"
msgstr "Ctrl+Alt+Del"
#: ../vnc.html:105
#: ../vnc.html:156
msgid "Send Ctrl-Alt-Del"
msgstr "Skicka Ctrl-Alt-Del"
#: ../vnc.html:112
#: ../vnc.html:163
msgid "Shutdown/Reboot"
msgstr "Stäng av/Boota om"
#: ../vnc.html:112
#: ../vnc.html:163
msgid "Shutdown/Reboot..."
msgstr "Stäng av/Boota om..."
#: ../vnc.html:118
#: ../vnc.html:169
msgid "Power"
msgstr "Ström"
#: ../vnc.html:120
#: ../vnc.html:171
msgid "Shutdown"
msgstr "Stäng av"
#: ../vnc.html:121
#: ../vnc.html:172
msgid "Reboot"
msgstr "Boota om"
#: ../vnc.html:122
#: ../vnc.html:173
msgid "Reset"
msgstr "Återställ"
#: ../vnc.html:127 ../vnc.html:133
#: ../vnc.html:178 ../vnc.html:184
msgid "Clipboard"
msgstr "Urklipp"
#: ../vnc.html:135
#: ../vnc.html:186
msgid "Edit clipboard content in the textarea below."
msgstr "Redigera urklippets innehåll i fältet nedan."
#: ../vnc.html:143
#: ../vnc.html:194
msgid "Full screen"
msgstr "Fullskärm"
#: ../vnc.html:148 ../vnc.html:154
#: ../vnc.html:199 ../vnc.html:205
msgid "Settings"
msgstr "Inställningar"
#: ../vnc.html:158
msgid "Shared Mode"
msgstr "Delat Läge"
#: ../vnc.html:211
msgid "Shared mode"
msgstr "Delat läge"
#: ../vnc.html:161
msgid "View Only"
msgstr "Endast Visning"
#: ../vnc.html:218
msgid "View only"
msgstr "Endast visning"
#: ../vnc.html:165
msgid "Clip to Window"
msgstr "Begränsa till Fönster"
#: ../vnc.html:226
msgid "Clip to window"
msgstr "Begränsa till fönster"
#: ../vnc.html:168
#: ../vnc.html:231
msgid "Scaling mode:"
msgstr "Skalningsläge:"
#: ../vnc.html:170
#: ../vnc.html:233
msgid "None"
msgstr "Ingen"
#: ../vnc.html:171
msgid "Local Scaling"
msgstr "Lokal Skalning"
#: ../vnc.html:234
msgid "Local scaling"
msgstr "Lokal skalning"
#: ../vnc.html:172
msgid "Remote Resizing"
msgstr "Ändra Storlek"
#: ../vnc.html:235
msgid "Remote resizing"
msgstr "Ändra storlek"
#: ../vnc.html:177
#: ../vnc.html:240
msgid "Advanced"
msgstr "Avancerat"
#: ../vnc.html:180
#: ../vnc.html:243
msgid "Quality:"
msgstr "Kvalitet:"
#: ../vnc.html:184
#: ../vnc.html:247
msgid "Compression level:"
msgstr "Kompressionsnivå:"
#: ../vnc.html:189
#: ../vnc.html:252
msgid "Repeater ID:"
msgstr "Repeater-ID:"
#: ../vnc.html:193
#: ../vnc.html:256
msgid "WebSocket"
msgstr "WebSocket"
#: ../vnc.html:196
#: ../vnc.html:261
msgid "Encrypt"
msgstr "Kryptera"
#: ../vnc.html:199
#: ../vnc.html:266
msgid "Host:"
msgstr "Värd:"
#: ../vnc.html:203
#: ../vnc.html:270
msgid "Port:"
msgstr "Port:"
#: ../vnc.html:207
#: ../vnc.html:274
msgid "Path:"
msgstr "Sökväg:"
#: ../vnc.html:214
msgid "Automatic Reconnect"
msgstr "Automatisk Återanslutning"
#: ../vnc.html:283
msgid "Automatic reconnect"
msgstr "Automatisk återanslutning"
#: ../vnc.html:217
#: ../vnc.html:288
msgid "Reconnect delay (ms):"
msgstr "Fördröjning (ms):"
#: ../vnc.html:222
#: ../vnc.html:295
msgid "Show dot when no cursor"
msgstr "Visa prick när ingen muspekare finns"
#: ../vnc.html:227
#: ../vnc.html:302
msgid "Logging:"
msgstr "Loggning:"
#: ../vnc.html:236
#: ../vnc.html:311
msgid "Version:"
msgstr "Version:"
#: ../vnc.html:244
#: ../vnc.html:319
msgid "Disconnect"
msgstr "Koppla från"
#: ../vnc.html:267
#: ../vnc.html:342
msgid "Connect"
msgstr "Anslut"
#: ../vnc.html:276
#: ../vnc.html:351
msgid "Server identity"
msgstr "Server-identitet"
#: ../vnc.html:279
#: ../vnc.html:354
msgid "The server has provided the following identifying information:"
msgstr "Servern har gett följande identifierande information:"
#: ../vnc.html:283
#: ../vnc.html:357
msgid "Fingerprint:"
msgstr "Fingeravtryck:"
#: ../vnc.html:286
#: ../vnc.html:361
msgid ""
"Please verify that the information is correct and press \"Approve\". "
"Otherwise press \"Reject\"."
@ -314,34 +310,37 @@ msgstr ""
"Kontrollera att informationen är korrekt och tryck sedan \"Godkänn\". Tryck "
"annars \"Neka\"."
#: ../vnc.html:291
#: ../vnc.html:366
msgid "Approve"
msgstr "Godkänn"
#: ../vnc.html:292
#: ../vnc.html:367
msgid "Reject"
msgstr "Neka"
#: ../vnc.html:300
#: ../vnc.html:375
msgid "Credentials"
msgstr "Användaruppgifter"
#: ../vnc.html:304
#: ../vnc.html:379
msgid "Username:"
msgstr "Användarnamn:"
#: ../vnc.html:308
#: ../vnc.html:383
msgid "Password:"
msgstr "Lösenord:"
#: ../vnc.html:312
msgid "Send Credentials"
msgstr "Skicka Användaruppgifter"
#: ../vnc.html:387
msgid "Send credentials"
msgstr "Skicka användaruppgifter"
#: ../vnc.html:321
#: ../vnc.html:396
msgid "Cancel"
msgstr "Avbryt"
#~ msgid "Must set host"
#~ msgstr "Du måste specifiera en värd"
#~ msgid "HTTPS is required for full functionality"
#~ msgstr "HTTPS krävs för full funktionalitet"

View File

@ -5,14 +5,14 @@
* Licensed under MPL 2.0 (see LICENSE.txt)
*/
const getopt = require('node-getopt');
const { program } = require('commander');
const jsdom = require("jsdom");
const fs = require("fs");
const opt = getopt.create([
['o', 'output=FILE', 'write output to specified file'],
['h', 'help', 'display this help'],
]).bindHelp().parseSystem();
program
.argument('<INPUT...>')
.requiredOption('-o, --output <FILE>', 'write output to specified file')
.parse(process.argv);
const strings = {};
@ -87,8 +87,8 @@ function process(elem, locator, enabled) {
}
}
for (let i = 0; i < opt.argv.length; i++) {
const fn = opt.argv[i];
for (let i = 0; i < program.args.length; i++) {
const fn = program.args[i];
const file = fs.readFileSync(fn, "utf8");
const dom = new jsdom.JSDOM(file, { includeNodeLocations: true });
const body = dom.window.document.body;
@ -116,4 +116,4 @@ for (let str in strings) {
output += "\n";
}
fs.writeFileSync(opt.options.output, output);
fs.writeFileSync(program.opts().output, output);

View File

@ -11,7 +11,7 @@
# "vnc": "localhost:5902"
#}
#}
snapctl get services | jq -c '.[]' | while read service; do # for each service the user sepcified..
snapctl get services | jq -c '.[]' | while read service; do # for each service the user specified..
# get the important data for the service (listen port, VNC host:port)
listen_port="$(echo $service | jq --raw-output '.listen')"
vnc_host_port="$(echo $service | jq --raw-output '.vnc')" # --raw-output removes any quotation marks from the output

View File

@ -42,7 +42,7 @@ parts:
- jq
websockify:
source: https://github.com/novnc/websockify/archive/v0.12.0.tar.gz
source: https://github.com/novnc/websockify/archive/v0.13.0.tar.gz
plugin: python
stage-packages:
- python3-numpy

View File

@ -525,6 +525,37 @@ describe('Key event handling', function () {
expect(kbd.onkeyevent).to.not.have.been.called;
});
it('should generate AltGraph for quick Ctrl+AltGraph sequence', function () {
const kbd = new Keyboard(document);
kbd.onkeyevent = sinon.spy();
kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1, timeStamp: Date.now()}));
this.clock.tick(20);
kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'AltGraph', location: 2, timeStamp: Date.now()}));
expect(kbd.onkeyevent).to.have.been.calledOnce;
expect(kbd.onkeyevent).to.have.been.calledWith(0xfe03, 'AltRight', true);
// Check that the timer is properly dead
kbd.onkeyevent.resetHistory();
this.clock.tick(100);
expect(kbd.onkeyevent).to.not.have.been.called;
});
it('should generate Ctrl, AltGraph for slow Ctrl+AltGraph sequence', function () {
const kbd = new Keyboard(document);
kbd.onkeyevent = sinon.spy();
kbd._handleKeyDown(keyevent('keydown', {code: 'ControlLeft', key: 'Control', location: 1, timeStamp: Date.now()}));
this.clock.tick(60);
kbd._handleKeyDown(keyevent('keydown', {code: 'AltRight', key: 'AltGraph', location: 2, timeStamp: Date.now()}));
expect(kbd.onkeyevent).to.have.been.calledTwice;
expect(kbd.onkeyevent.firstCall).to.have.been.calledWith(0xffe3, "ControlLeft", true);
expect(kbd.onkeyevent.secondCall).to.have.been.calledWith(0xfe03, "AltRight", true);
// Check that the timer is properly dead
kbd.onkeyevent.resetHistory();
this.clock.tick(100);
expect(kbd.onkeyevent).to.not.have.been.called;
});
it('should pass through single Alt', function () {
const kbd = new Keyboard(document);
kbd.onkeyevent = sinon.spy();

File diff suppressed because it is too large Load Diff

View File

@ -28,16 +28,63 @@ for fn in "$@"; do
curl --silent \
--header "Content-Type: ${type}; charset=utf-8" \
--data-binary @${fn} \
https://validator.w3.org/nu/?out=text > $OUT
cat $OUT
echo
"https://validator.w3.org/nu/?out=gnu&level=error&asciiquotes=yes" \
> $OUT
# We don't fail the check for warnings as some warnings are
# not relevant for us, and we don't currently have a way to
# ignore just those
if grep -q -s -E "^Error:" $OUT; then
RET=1
while read -r line; do
echo
line_info=$(echo $line | cut -d ":" -f 2)
start_info=$(echo $line_info | cut -d "-" -f 1)
end_info=$(echo $line_info | cut -d "-" -f 2)
line_start=$(echo $start_info | cut -d "." -f 1)
col_start=$(echo $start_info | cut -d "." -f 2)
line_end=$(echo $end_info | cut -d "." -f 1)
col_end=$(echo $end_info | cut -d "." -f 2)
error=$(echo $line | cut -d ":" -f 4-)
case $error in
*"\"scrollbar-gutter\": Property \"scrollbar-gutter\" doesn't exist.")
# FIXME: https://github.com/validator/validator/issues/1788
echo "Ignoring below error on line ${line_start}," \
"the scrollbar-gutter property actually exist and is widely" \
"supported:"
echo $error
continue
;;
*"\"clip-path\": \"path("*)
# FIXME: https://github.com/validator/validator/issues/1786
echo "Ignoring below error on line ${line_start}," \
"the path() function is valid for clip-path and is" \
"widely supported:"
echo $error
continue
;;
*"Parse Error.")
# FIXME: https://github.com/validator/validator/issues/1786
lineofselector=$(grep -n "@supports selector(" $fn | cut -d ":" -f 1)
linediff=$((lineofselector-line_start))
# Only ignore if parse error is within 50 lines of "selector()"
if [ ${linediff#-} -lt 50 ]; then
echo "Ignoring below error on line ${line_start}," \
"the @supports selector() function should not give a parse" \
"error:"
echo $error
continue
fi
;;
esac
echo "ERROR between line ${line_start} (col ${col_start})" \
"and line ${line_end} (col ${col_end}):"
echo $error
RET=1
done < "$OUT"
done
rm $OUT

View File

@ -37,6 +37,7 @@
<link rel="apple-touch-icon" sizes="180x180" type="image/png" href="app/images/icons/novnc-ios-180.png">
<!-- Stylesheets -->
<link rel="stylesheet" href="app/styles/constants.css">
<link rel="stylesheet" href="app/styles/base.css">
<link rel="stylesheet" href="app/styles/input.css">
@ -205,14 +206,26 @@
</div>
<ul>
<li>
<label><input id="noVNC_setting_shared" type="checkbox"> Shared mode</label>
<label>
<input id="noVNC_setting_shared" type="checkbox"
class="toggle">
Shared mode
</label>
</li>
<li>
<label><input id="noVNC_setting_view_only" type="checkbox"> View only</label>
<label>
<input id="noVNC_setting_view_only" type="checkbox"
class="toggle">
View only
</label>
</li>
<li><hr></li>
<li>
<label><input id="noVNC_setting_view_clip" type="checkbox"> Clip to window</label>
<label>
<input id="noVNC_setting_view_clip" type="checkbox"
class="toggle">
Clip to window
</label>
</li>
<li>
<label for="noVNC_setting_resize">Scaling mode:</label>
@ -243,7 +256,11 @@
<div class="noVNC_expander">WebSocket</div>
<div><ul>
<li>
<label><input id="noVNC_setting_encrypt" type="checkbox"> Encrypt</label>
<label>
<input id="noVNC_setting_encrypt" type="checkbox"
class="toggle">
Encrypt
</label>
</li>
<li>
<label for="noVNC_setting_host">Host:</label>
@ -261,7 +278,11 @@
</li>
<li><hr></li>
<li>
<label><input id="noVNC_setting_reconnect" type="checkbox"> Automatic reconnect</label>
<label>
<input id="noVNC_setting_reconnect" type="checkbox"
class="toggle">
Automatic reconnect
</label>
</li>
<li>
<label for="noVNC_setting_reconnect_delay">Reconnect delay (ms):</label>
@ -269,7 +290,11 @@
</li>
<li><hr></li>
<li>
<label><input id="noVNC_setting_show_dot" type="checkbox"> Show dot when no cursor</label>
<label>
<input id="noVNC_setting_show_dot" type="checkbox"
class="toggle">
Show dot when no cursor
</label>
<label for="noVNC_setting_gestures_mode">Gestures mode:</label>
<select id="noVNC_setting_gestures_mode" name="vncGestures">
<option value="novnc">Default (noVNC)</option>
@ -335,16 +360,16 @@
The server has provided the following identifying information:
</div>
<div id="noVNC_fingerprint_block">
<b>Fingerprint:</b>
Fingerprint:
<span id="noVNC_fingerprint"></span>
</div>
<div>
Please verify that the information is correct and press
"Approve". Otherwise press "Reject".
</div>
<div>
<input id="noVNC_approve_server_button" type="submit" value="Approve" class="noVNC_submit">
<input id="noVNC_reject_server_button" type="button" value="Reject" class="noVNC_submit">
<div class="button_row">
<input id="noVNC_approve_server_button" type="submit" value="Approve">
<input id="noVNC_reject_server_button" type="button" value="Reject">
</div>
</form></div>
</div>
@ -363,8 +388,8 @@
<label for="noVNC_password_input">Password:</label>
<input id="noVNC_password_input" type="password">
</div>
<div>
<input id="noVNC_credentials_button" type="submit" value="Send credentials" class="noVNC_submit">
<div class="button_row">
<input id="noVNC_credentials_button" type="submit" value="Send credentials">
</div>
</form></div>
</div>
@ -373,7 +398,7 @@
<div id="noVNC_transition">
<div id="noVNC_transition_text"></div>
<div>
<input type="button" id="noVNC_cancel_reconnect_button" value="Cancel" class="noVNC_submit">
<input type="button" id="noVNC_cancel_reconnect_button" value="Cancel">
</div>
<div class="noVNC_spinner"></div>
</div>