Merge a06f2cf408 into fc5b83c08f
This commit is contained in:
commit
a002e3e6e2
|
|
@ -791,7 +791,6 @@ html {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
}
|
}
|
||||||
#noVNC_settings button,
|
|
||||||
#noVNC_settings select,
|
#noVNC_settings select,
|
||||||
#noVNC_settings textarea,
|
#noVNC_settings textarea,
|
||||||
#noVNC_settings input:not([type=checkbox]):not([type=radio]) {
|
#noVNC_settings input:not([type=checkbox]):not([type=radio]) {
|
||||||
|
|
@ -799,6 +798,10 @@ html {
|
||||||
/* Prevent inputs in settings from being too wide */
|
/* Prevent inputs in settings from being too wide */
|
||||||
max-width: calc(100% - 6px - var(--input-xpadding) * 2);
|
max-width: calc(100% - 6px - var(--input-xpadding) * 2);
|
||||||
}
|
}
|
||||||
|
#noVNC_settings button:not(#noVNC_ignore_keys_help_button) {
|
||||||
|
margin-left: 6px;
|
||||||
|
max-width: calc(100% - 6px - var(--input-xpadding) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
#noVNC_setting_port {
|
#noVNC_setting_port {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
|
|
@ -822,6 +825,83 @@ html {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Help tooltips */
|
||||||
|
.noVNC_setting_with_help {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noVNC_setting_with_help label {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_settings .noVNC_tooltip_list {
|
||||||
|
margin: 0.4rem 0 0 0;
|
||||||
|
padding-left: 0.8rem;
|
||||||
|
list-style: disc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.noVNC_tooltip_list li {
|
||||||
|
margin: 0.15rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_setting_ignore_keys::placeholder {
|
||||||
|
color: var(--novnc-grey);
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_setting_ignore_keys.noVNC_invalid,
|
||||||
|
#noVNC_setting_ignore_keys.noVNC_invalid:focus {
|
||||||
|
border-color: var(--novnc-red);
|
||||||
|
box-shadow: 0 0 2px rgba(var(--novnc-red-rgb), 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_ignore_keys_help_button {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
|
||||||
|
width: 1.6rem;
|
||||||
|
height: 1.6rem;
|
||||||
|
min-width: 1.6rem;
|
||||||
|
max-width: 1.6rem;
|
||||||
|
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
border-radius: 50%;
|
||||||
|
background: transparent;
|
||||||
|
border: 1px solid rgba(0,0,0,0.25);
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_ignore_keys_tooltip {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1000;
|
||||||
|
left: 1rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
margin: 0;
|
||||||
|
border-radius: 0.4rem;
|
||||||
|
background: rgba(20, 20, 20, 0.96);
|
||||||
|
color: #fff;
|
||||||
|
box-shadow: 0 0.4rem 1rem rgba(0, 0, 0, 0.35);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
line-height: .9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
#noVNC_ignore_keys_tooltip.noVNC_open {
|
||||||
|
display: flex !important;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
/* ----------------------------------------
|
/* ----------------------------------------
|
||||||
* Status dialog
|
* Status dialog
|
||||||
* ----------------------------------------
|
* ----------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,8 @@
|
||||||
|
|
||||||
--novnc-green: rgb(0, 128, 0);
|
--novnc-green: rgb(0, 128, 0);
|
||||||
--novnc-yellow: rgb(255, 255, 0);
|
--novnc-yellow: rgb(255, 255, 0);
|
||||||
|
--novnc-red-rgb: 229, 57, 53;
|
||||||
|
--novnc-red: rgb(var(--novnc-red-rgb));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------ MISC PROPERTIES ------ */
|
/* ------ MISC PROPERTIES ------ */
|
||||||
|
|
|
||||||
229
app/ui.js
229
app/ui.js
|
|
@ -129,6 +129,7 @@ const UI = {
|
||||||
UI.addConnectionControlHandlers();
|
UI.addConnectionControlHandlers();
|
||||||
UI.addClipboardHandlers();
|
UI.addClipboardHandlers();
|
||||||
UI.addSettingsHandlers();
|
UI.addSettingsHandlers();
|
||||||
|
UI.initIgnoreKeysTooltip();
|
||||||
document.getElementById("noVNC_status")
|
document.getElementById("noVNC_status")
|
||||||
.addEventListener('click', UI.hideStatus);
|
.addEventListener('click', UI.hideStatus);
|
||||||
|
|
||||||
|
|
@ -196,6 +197,7 @@ const UI = {
|
||||||
UI.initSetting('reconnect', false);
|
UI.initSetting('reconnect', false);
|
||||||
UI.initSetting('reconnect_delay', 5000);
|
UI.initSetting('reconnect_delay', 5000);
|
||||||
UI.initSetting('keep_device_awake', false);
|
UI.initSetting('keep_device_awake', false);
|
||||||
|
UI.initSetting('ignore_keys', '');
|
||||||
},
|
},
|
||||||
// Adds a link to the label elements on the corresponding input elements
|
// Adds a link to the label elements on the corresponding input elements
|
||||||
setupSettingLabels() {
|
setupSettingLabels() {
|
||||||
|
|
@ -388,6 +390,13 @@ const UI = {
|
||||||
UI.addSettingChangeHandler('logging', UI.updateLogging);
|
UI.addSettingChangeHandler('logging', UI.updateLogging);
|
||||||
UI.addSettingChangeHandler('reconnect');
|
UI.addSettingChangeHandler('reconnect');
|
||||||
UI.addSettingChangeHandler('reconnect_delay');
|
UI.addSettingChangeHandler('reconnect_delay');
|
||||||
|
UI.addSettingChangeHandler('ignore_keys');
|
||||||
|
const input = document.getElementById('noVNC_setting_ignore_keys');
|
||||||
|
if (input && !input.dataset.validationBound) {
|
||||||
|
input.addEventListener('input', UI.validateIgnoreKeysInput);
|
||||||
|
input.addEventListener('blur', UI.validateIgnoreKeysInput);
|
||||||
|
input.dataset.validationBound = 'true';
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
addFullscreenHandlers() {
|
addFullscreenHandlers() {
|
||||||
|
|
@ -875,13 +884,28 @@ const UI = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
ctrl.value = value;
|
ctrl.value = value ?? '';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Save control setting to cookie
|
// Update cookie and form control setting. If value is not set, then
|
||||||
saveSetting(name) {
|
// updates from control to current cookie setting.
|
||||||
const ctrl = document.getElementById('noVNC_setting_' + name);
|
saveSetting(nameOrEvent) {
|
||||||
|
let ctrl = null;
|
||||||
|
|
||||||
|
if (typeof nameOrEvent === 'string') {
|
||||||
|
ctrl = document.getElementById('noVNC_setting_' + nameOrEvent);
|
||||||
|
} else if (nameOrEvent && nameOrEvent.target) {
|
||||||
|
ctrl = nameOrEvent.target;
|
||||||
|
} else if (this instanceof Element) {
|
||||||
|
ctrl = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ctrl) {
|
||||||
|
Log.Warn("saveSetting called without valid control");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
let val;
|
let val;
|
||||||
if (ctrl.type === 'checkbox') {
|
if (ctrl.type === 'checkbox') {
|
||||||
val = ctrl.checked;
|
val = ctrl.checked;
|
||||||
|
|
@ -890,6 +914,17 @@ const UI = {
|
||||||
} else {
|
} else {
|
||||||
val = ctrl.value;
|
val = ctrl.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let name = ctrl.id;
|
||||||
|
if (name && name.startsWith('noVNC_setting_')) {
|
||||||
|
name = name.slice('noVNC_setting_'.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!name) {
|
||||||
|
Log.Warn("saveSetting could not determine setting name");
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
WebUtil.writeSetting(name, val);
|
WebUtil.writeSetting(name, val);
|
||||||
//Log.Debug("Setting saved '" + name + "=" + val + "'");
|
//Log.Debug("Setting saved '" + name + "=" + val + "'");
|
||||||
return val;
|
return val;
|
||||||
|
|
@ -969,6 +1004,7 @@ const UI = {
|
||||||
UI.updateSetting('logging');
|
UI.updateSetting('logging');
|
||||||
UI.updateSetting('reconnect');
|
UI.updateSetting('reconnect');
|
||||||
UI.updateSetting('reconnect_delay');
|
UI.updateSetting('reconnect_delay');
|
||||||
|
UI.updateSetting('ignore_keys');
|
||||||
|
|
||||||
document.getElementById('noVNC_settings')
|
document.getElementById('noVNC_settings')
|
||||||
.classList.add("noVNC_open");
|
.classList.add("noVNC_open");
|
||||||
|
|
@ -1166,6 +1202,8 @@ const UI = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UI.wrapRfbSendKey();
|
||||||
|
|
||||||
UI.rfb.addEventListener("connect", UI.connectFinished);
|
UI.rfb.addEventListener("connect", UI.connectFinished);
|
||||||
UI.rfb.addEventListener("disconnect", UI.disconnectFinished);
|
UI.rfb.addEventListener("disconnect", UI.disconnectFinished);
|
||||||
UI.rfb.addEventListener("serververification", UI.serverVerify);
|
UI.rfb.addEventListener("serververification", UI.serverVerify);
|
||||||
|
|
@ -1666,6 +1704,25 @@ const UI = {
|
||||||
UI.rfb.sendKey(keysym, code, down);
|
UI.rfb.sendKey(keysym, code, down);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
wrapRfbSendKey() {
|
||||||
|
if (!UI.rfb || !UI.rfb.sendKey || UI.rfb._ignoreKeysWrapped) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const originalSendKey = UI.rfb.sendKey.bind(UI.rfb);
|
||||||
|
|
||||||
|
UI.rfb.sendKey = function (keysym, code, down) {
|
||||||
|
if (UI.shouldIgnoreKey(code)) {
|
||||||
|
Log.Debug("Key ignored: " + code);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return originalSendKey(keysym, code, down);
|
||||||
|
};
|
||||||
|
|
||||||
|
UI.rfb._ignoreKeysWrapped = true;
|
||||||
|
},
|
||||||
|
|
||||||
// When normal keyboard events are left uncought, use the input events from
|
// When normal keyboard events are left uncought, use the input events from
|
||||||
// the keyboardinput element instead and generate the corresponding key events.
|
// the keyboardinput element instead and generate the corresponding key events.
|
||||||
// This code is required since some browsers on Android are inconsistent in
|
// This code is required since some browsers on Android are inconsistent in
|
||||||
|
|
@ -1937,6 +1994,170 @@ const UI = {
|
||||||
selectbox.options.add(optn);
|
selectbox.options.add(optn);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
supportedIgnoreKeys: [
|
||||||
|
{ label: 'Escape', aliases: ['esc', 'escape'] },
|
||||||
|
{ label: 'Tab', aliases: ['tab'] },
|
||||||
|
{ label: 'Enter', aliases: ['enter', 'return'] },
|
||||||
|
{ label: 'Delete', aliases: ['del', 'delete'] },
|
||||||
|
{ label: 'Backspace', aliases: ['bs', 'backspace'] },
|
||||||
|
{ label: 'ControlLeft', aliases: ['ctrl', 'ctl', 'controlleft'] },
|
||||||
|
{ label: 'AltLeft', aliases: ['alt', 'altleft'] },
|
||||||
|
{ label: 'MetaLeft', aliases: ['win', 'cmd', 'super', 'metaleft'] },
|
||||||
|
],
|
||||||
|
|
||||||
|
buildIgnoreKeysTooltipText() {
|
||||||
|
return `<ul class="noVNC_tooltip_list">` +
|
||||||
|
UI.supportedIgnoreKeys
|
||||||
|
.map(({ label, aliases }) => {
|
||||||
|
if (!aliases.length) {
|
||||||
|
return `<li>${label}</li>`;
|
||||||
|
}
|
||||||
|
return `<li>${label} (${aliases.join(', ')})</li>`;
|
||||||
|
})
|
||||||
|
.join('') +
|
||||||
|
`</ul>`;
|
||||||
|
},
|
||||||
|
|
||||||
|
buildIgnoreKeysPlaceholder() {
|
||||||
|
return UI.supportedIgnoreKeys
|
||||||
|
.map(({ aliases, label }) => aliases[0] || label)
|
||||||
|
.slice(0, 4)
|
||||||
|
.join(', ') + '...';
|
||||||
|
},
|
||||||
|
|
||||||
|
initIgnoreKeysTooltip() {
|
||||||
|
const input = document.getElementById('noVNC_setting_ignore_keys');
|
||||||
|
if (!input) return;
|
||||||
|
|
||||||
|
const tooltip = document.getElementById('noVNC_ignore_keys_tooltip');
|
||||||
|
const button = document.getElementById('noVNC_ignore_keys_help_button');
|
||||||
|
|
||||||
|
if (!tooltip || !button) return;
|
||||||
|
|
||||||
|
if (!input.placeholder) {
|
||||||
|
input.placeholder = UI.buildIgnoreKeysPlaceholder();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tooltip.dataset.examplesAppended) {
|
||||||
|
tooltip.innerHTML += UI.buildIgnoreKeysTooltipText();
|
||||||
|
tooltip.dataset.examplesAppended = 'true';
|
||||||
|
}
|
||||||
|
|
||||||
|
const show = () => {
|
||||||
|
tooltip.classList.add('noVNC_open');
|
||||||
|
button.setAttribute('aria-expanded', 'true');
|
||||||
|
};
|
||||||
|
|
||||||
|
const hide = () => {
|
||||||
|
tooltip.classList.remove('noVNC_open');
|
||||||
|
button.setAttribute('aria-expanded', 'false');
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggle = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
|
||||||
|
if (tooltip.classList.contains('noVNC_open')) {
|
||||||
|
hide();
|
||||||
|
} else {
|
||||||
|
show();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!button.dataset.tooltipBound) {
|
||||||
|
button.addEventListener('mouseenter', show);
|
||||||
|
button.addEventListener('mouseleave', hide);
|
||||||
|
button.addEventListener('focus', show);
|
||||||
|
button.addEventListener('blur', hide);
|
||||||
|
button.addEventListener('click', toggle);
|
||||||
|
|
||||||
|
tooltip.addEventListener('mouseenter', show);
|
||||||
|
tooltip.addEventListener('mouseleave', hide);
|
||||||
|
|
||||||
|
document.addEventListener('click', (event) => {
|
||||||
|
if (!button.contains(event.target) && !tooltip.contains(event.target)) {
|
||||||
|
hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
button.dataset.tooltipBound = 'true';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
parseIgnoredKeys() {
|
||||||
|
const raw = UI.getSetting('ignore_keys');
|
||||||
|
if (!raw) return new Set();
|
||||||
|
|
||||||
|
return new Set(
|
||||||
|
raw.split(',')
|
||||||
|
.map(k => k.trim().toLowerCase())
|
||||||
|
.filter(Boolean)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
normalizeIgnoreKey(value) {
|
||||||
|
const normalized = (value || '').trim().toLowerCase();
|
||||||
|
if (!normalized) return '';
|
||||||
|
|
||||||
|
for (const { label, aliases } of UI.supportedIgnoreKeys) {
|
||||||
|
const canonical = label.toLowerCase();
|
||||||
|
|
||||||
|
if (canonical === normalized) {
|
||||||
|
return canonical;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const alias of aliases) {
|
||||||
|
if (alias.toLowerCase() === normalized) {
|
||||||
|
return canonical;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized;
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldIgnoreKey: (code) => {
|
||||||
|
const ignored = UI.parseIgnoredKeys();
|
||||||
|
if (ignored.size === 0) return false;
|
||||||
|
|
||||||
|
const codeCanonical = UI.normalizeIgnoreKey(code);
|
||||||
|
if (!codeCanonical) return false;
|
||||||
|
|
||||||
|
for (const key of ignored) {
|
||||||
|
if (UI.normalizeIgnoreKey(key) === codeCanonical) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
validateIgnoreKeysInput() {
|
||||||
|
const input = document.getElementById('noVNC_setting_ignore_keys');
|
||||||
|
if (!input) return true;
|
||||||
|
|
||||||
|
const tokens = input.value
|
||||||
|
.split(',')
|
||||||
|
.map(k => k.trim())
|
||||||
|
.filter(Boolean);
|
||||||
|
|
||||||
|
if (tokens.length === 0) {
|
||||||
|
input.classList.remove('noVNC_invalid');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const validSet = new Set(
|
||||||
|
UI.supportedIgnoreKeys.map(k => k.label.toLowerCase())
|
||||||
|
);
|
||||||
|
|
||||||
|
const isValid = tokens.every((k) => {
|
||||||
|
const normalized = UI.normalizeIgnoreKey(k);
|
||||||
|
return validSet.has(normalized);
|
||||||
|
});
|
||||||
|
|
||||||
|
input.classList.toggle('noVNC_invalid', !isValid);
|
||||||
|
return isValid;
|
||||||
|
},
|
||||||
|
|
||||||
/* ------^-------
|
/* ------^-------
|
||||||
* /MISC
|
* /MISC
|
||||||
* ==============
|
* ==============
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ module.exports = (config) => {
|
||||||
{ pattern: 'node_modules/sinon-chai/**', included: false },
|
{ pattern: 'node_modules/sinon-chai/**', included: false },
|
||||||
// modules to test
|
// modules to test
|
||||||
{ pattern: 'app/localization.js', included: false, type: 'module' },
|
{ pattern: 'app/localization.js', included: false, type: 'module' },
|
||||||
|
{ pattern: 'app/ui.js', included: false, type: 'module' },
|
||||||
{ pattern: 'app/wakelock.js', included: false, type: 'module' },
|
{ pattern: 'app/wakelock.js', included: false, type: 'module' },
|
||||||
{ pattern: 'app/webutil.js', included: false, type: 'module' },
|
{ pattern: 'app/webutil.js', included: false, type: 'module' },
|
||||||
{ pattern: 'core/**/*.js', included: false, type: 'module' },
|
{ pattern: 'core/**/*.js', included: false, type: 'module' },
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
import UI from '../app/ui.js';
|
||||||
|
import * as WebUtil from '../app/webutil.js';
|
||||||
|
|
||||||
|
describe('UI', function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
describe('Ignore Keys Feature', function () {
|
||||||
|
let originalSupportedIgnoreKeys;
|
||||||
|
|
||||||
|
beforeEach(async function () {
|
||||||
|
await WebUtil.initSettings();
|
||||||
|
|
||||||
|
// Save original reference
|
||||||
|
originalSupportedIgnoreKeys = UI.supportedIgnoreKeys;
|
||||||
|
|
||||||
|
// Clone + remove one key (MetaLeft) for testing
|
||||||
|
UI.supportedIgnoreKeys = UI.supportedIgnoreKeys.filter(
|
||||||
|
k => k.label !== 'MetaLeft'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function () {
|
||||||
|
UI.rfb = null;
|
||||||
|
|
||||||
|
// Restore original list
|
||||||
|
UI.supportedIgnoreKeys = originalSupportedIgnoreKeys;
|
||||||
|
|
||||||
|
WebUtil.eraseSetting('ignore_keys');
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('shouldIgnoreKey()', function () {
|
||||||
|
it('returns false for removed keys', function () {
|
||||||
|
WebUtil.setSetting('ignore_keys', 'cmd,win');
|
||||||
|
|
||||||
|
expect(UI.shouldIgnoreKey('MetaLeft')).to.be.false;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('still works for remaining keys', function () {
|
||||||
|
WebUtil.setSetting('ignore_keys', 'esc,ctrl');
|
||||||
|
|
||||||
|
expect(UI.shouldIgnoreKey('Escape')).to.be.true;
|
||||||
|
expect(UI.shouldIgnoreKey('ControlLeft')).to.be.true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
15
vnc.html
15
vnc.html
|
|
@ -218,6 +218,21 @@
|
||||||
</label>
|
</label>
|
||||||
</li>
|
</li>
|
||||||
<li><hr></li>
|
<li><hr></li>
|
||||||
|
<li class="noVNC_setting">
|
||||||
|
<div class="noVNC_setting_with_help">
|
||||||
|
<label for="noVNC_setting_ignore_keys">Ignored Keys:</label>
|
||||||
|
|
||||||
|
<button id="noVNC_ignore_keys_help_button" type="button">?</button>
|
||||||
|
|
||||||
|
<div id="noVNC_ignore_keys_tooltip"
|
||||||
|
class="noVNC_tooltip">
|
||||||
|
Comma-separated list<br/>of keys to ignore.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input id="noVNC_setting_ignore_keys" type="text">
|
||||||
|
</li>
|
||||||
|
<li><hr></li>
|
||||||
<li>
|
<li>
|
||||||
<label>
|
<label>
|
||||||
<input id="noVNC_setting_view_clip" type="checkbox"
|
<input id="noVNC_setting_view_clip" type="checkbox"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue