commit
0bd3813949
51
app/ui.js
51
app/ui.js
|
@ -30,9 +30,11 @@ window.updateSetting = (name, value) => {
|
|||
}
|
||||
}
|
||||
|
||||
import "core-js/stable";
|
||||
import "regenerator-runtime/runtime";
|
||||
import * as Log from '../core/util/logging.js';
|
||||
import _, { l10n } from './localization.js';
|
||||
import { isTouchDevice, isSafari, hasScrollbarGutter, dragThreshold }
|
||||
import { isTouchDevice, isSafari, hasScrollbarGutter, dragThreshold, supportsBinaryClipboard, isFirefox }
|
||||
from '../core/util/browser.js';
|
||||
import { setCapture, getPointerEvent } from '../core/util/events.js';
|
||||
import KeyTable from "../core/input/keysym.js";
|
||||
|
@ -1100,20 +1102,12 @@ const UI = {
|
|||
},
|
||||
|
||||
clipboardReceive(e) {
|
||||
if (UI.rfb.clipboardDown && UI.rfb.clipboardSeamless ) {
|
||||
if (UI.rfb.clipboardDown) {
|
||||
var curvalue = document.getElementById('noVNC_clipboard_text').value;
|
||||
if (curvalue != e.detail.text) {
|
||||
Log.Debug(">> UI.clipboardReceive: " + e.detail.text.substr(0, 40) + "...");
|
||||
document.getElementById('noVNC_clipboard_text').value = e.detail.text;
|
||||
Log.Debug("<< UI.clipboardReceive");
|
||||
if (navigator.clipboard && navigator.clipboard.writeText){
|
||||
navigator.clipboard.writeText(e.detail.text)
|
||||
.then(function () {
|
||||
//UI.popupMessage("Selection Copied");
|
||||
}, function () {
|
||||
console.error("Failed to write system clipboard (trying to copy from NoVNC clipboard)")
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1142,26 +1136,22 @@ const UI = {
|
|||
UI.copyFromLocalClipboard();
|
||||
},
|
||||
copyFromLocalClipboard: function copyFromLocalClipboard() {
|
||||
if (!document.hasFocus()) {
|
||||
Log.Debug("window does not have focus");
|
||||
return;
|
||||
}
|
||||
if (UI.rfb && UI.rfb.clipboardUp && UI.rfb.clipboardSeamless) {
|
||||
UI.readClipboard(function (text) {
|
||||
var maximumBufferSize = 10000;
|
||||
var clipVal = document.getElementById('noVNC_clipboard_text').value;
|
||||
|
||||
if (clipVal != text) {
|
||||
document.getElementById('noVNC_clipboard_text').value = text; // The websocket has a maximum buffer array size
|
||||
|
||||
if (text.length > maximumBufferSize) {
|
||||
UI.popupMessage("Clipboard contents too large. Data truncated", 2000);
|
||||
UI.rfb.clipboardPasteFrom(text.slice(0, maximumBufferSize));
|
||||
} else {
|
||||
//UI.popupMessage("Copied from Local Clipboard");
|
||||
UI.rfb.clipboardPasteFrom(text);
|
||||
if (UI.rfb.clipboardBinary) {
|
||||
navigator.clipboard.read().then((data) => {
|
||||
if (UI.rfb) {
|
||||
UI.rfb.clipboardPasteDataFrom(data);
|
||||
}
|
||||
} // Reset flag to prevent checking too often
|
||||
|
||||
|
||||
UI.needToCheckClipboardChange = false;
|
||||
});
|
||||
UI.needToCheckClipboardChange = false;
|
||||
}, (err) => {
|
||||
Log.Debug("No data in clipboard");
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1304,6 +1294,13 @@ const UI = {
|
|||
UI.rfb.clipboardUp = UI.getSetting('clipboard_up');
|
||||
UI.rfb.clipboardDown = UI.getSetting('clipboard_down');
|
||||
UI.rfb.clipboardSeamless = UI.getSetting('clipboard_seamless');
|
||||
UI.rfb.clipboardBinary = supportsBinaryClipboard() && UI.rfb.clipboardSeamless;
|
||||
|
||||
//Only explicitly request permission to clipboard on browsers that support binary clipboard access
|
||||
if (supportsBinaryClipboard()) {
|
||||
// explicitly request permission to the clipboard
|
||||
navigator.permissions.query({ name: "clipboard-read" }).then((result) => { Log.Debug('binary clipboard enabled') });
|
||||
}
|
||||
// KASM-960 workaround, disable seamless on Safari
|
||||
if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent))
|
||||
{
|
||||
|
|
263
core/rfb.js
263
core/rfb.js
|
@ -10,6 +10,7 @@
|
|||
import { toUnsigned32bit, toSigned32bit } from './util/int.js';
|
||||
import * as Log from './util/logging.js';
|
||||
import { encodeUTF8, decodeUTF8 } from './util/strings.js';
|
||||
import { hashUInt8Array } from './util/int.js';
|
||||
import { dragThreshold, supportsCursorURIs, isTouchDevice, isWindows, isMac } from './util/browser.js';
|
||||
import { clientToElement } from './util/element.js';
|
||||
import { setCapture } from './util/events.js';
|
||||
|
@ -140,6 +141,7 @@ export default class RFB extends EventTargetMixin {
|
|||
this._frameRate = 30;
|
||||
this._maxVideoResolutionX = 960;
|
||||
this._maxVideoResolutionY = 540;
|
||||
this._clipboardBinary = true;
|
||||
|
||||
this._trackFrameStats = false;
|
||||
|
||||
|
@ -332,10 +334,14 @@ export default class RFB extends EventTargetMixin {
|
|||
|
||||
this._qualityLevel = 6;
|
||||
this._compressionLevel = 2;
|
||||
this._clipHash = 0;
|
||||
}
|
||||
|
||||
// ===== PROPERTIES =====
|
||||
|
||||
get clipboardBinary() { return this._clipboardMode; }
|
||||
set clipboardBinary(val) { this._clipboardMode = val; }
|
||||
|
||||
get videoQuality() { return this._videoQuality; }
|
||||
set videoQuality(quality) { this._videoQuality = quality; }
|
||||
|
||||
|
@ -763,21 +769,94 @@ export default class RFB extends EventTargetMixin {
|
|||
|
||||
clipboardPasteFrom(text) {
|
||||
if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; }
|
||||
if (!(typeof text === 'string' && text.length > 0)) { return; }
|
||||
|
||||
this.sentEventsCounter+=1;
|
||||
if (this._clipboardServerCapabilitiesFormats[extendedClipboardFormatText] &&
|
||||
this._clipboardServerCapabilitiesActions[extendedClipboardActionNotify]) {
|
||||
|
||||
this._clipboardText = text;
|
||||
RFB.messages.extendedClipboardNotify(this._sock, [extendedClipboardFormatText]);
|
||||
} else {
|
||||
let data = new Uint8Array(text.length);
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
// FIXME: text can have values outside of Latin1/Uint8
|
||||
data[i] = text.charCodeAt(i);
|
||||
}
|
||||
|
||||
RFB.messages.clientCutText(this._sock, data);
|
||||
let data = new Uint8Array(text.length);
|
||||
for (let i = 0; i < text.length; i++) {
|
||||
data[i] = text.charCodeAt(i);
|
||||
}
|
||||
|
||||
let h = hashUInt8Array(data);
|
||||
if (h === this._clipHash) {
|
||||
Log.Debug('No clipboard changes');
|
||||
return;
|
||||
} else {
|
||||
this._clipHash = h;
|
||||
}
|
||||
|
||||
let dataset = [];
|
||||
let mimes = [ 'text/plain' ];
|
||||
dataset.push(data);
|
||||
|
||||
RFB.messages.sendBinaryClipboard(this._sock, dataset, mimes);
|
||||
}
|
||||
|
||||
async clipboardPasteDataFrom(clipdata) {
|
||||
if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; }
|
||||
this.sentEventsCounter+=1;
|
||||
|
||||
let dataset = [];
|
||||
let mimes = [];
|
||||
let h = 0;
|
||||
for (let i = 0; i < clipdata.length; i++) {
|
||||
for (let ti = 0; ti < clipdata[i].types.length; ti++) {
|
||||
let mime = clipdata[i].types[ti];
|
||||
|
||||
switch (mime) {
|
||||
case 'image/png':
|
||||
case 'text/plain':
|
||||
case 'text/html':
|
||||
let blob = await clipdata[i].getType(mime);
|
||||
if (!blob) {
|
||||
continue;
|
||||
}
|
||||
let buff = await blob.arrayBuffer();
|
||||
let data = new Uint8Array(buff);
|
||||
|
||||
if (!h) {
|
||||
h = hashUInt8Array(data);
|
||||
if (h === this._clipHash) {
|
||||
Log.Debug('No clipboard changes');
|
||||
return;
|
||||
} else {
|
||||
this._clipHash = h;
|
||||
}
|
||||
}
|
||||
|
||||
if (mimes.includes(mime)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
mimes.push(mime);
|
||||
dataset.push(data);
|
||||
Log.Debug('Sending mime type: ' + mime);
|
||||
break;
|
||||
default:
|
||||
Log.Info('skipping clip send mime type: ' + mime)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//if png is present and text/plain is not, remove other variations of images to save bandwidth
|
||||
//if png is present with text/plain, then remove png. Word will put in a png of copied text
|
||||
if (mimes.includes('image/png') && !mimes.includes('text/plain')) {
|
||||
let i = mimes.indexOf('image/png');
|
||||
mimes = mimes.slice(i, i+1);
|
||||
dataset = dataset.slice(i, i+1);
|
||||
} else if (mimes.includes('image/png') && mimes.includes('text/plain')) {
|
||||
let i = mimes.indexOf('image/png');
|
||||
mimes.splice(i, 1);
|
||||
dataset.splice(i, 1);
|
||||
}
|
||||
|
||||
|
||||
if (dataset.length > 0) {
|
||||
RFB.messages.sendBinaryClipboard(this._sock, dataset, mimes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
requestBottleneckStats() {
|
||||
|
@ -980,7 +1059,7 @@ export default class RFB extends EventTargetMixin {
|
|||
try {
|
||||
if (x > 1280 && limited && this.videoQuality == 1) {
|
||||
var ratio = y / x;
|
||||
console.log(ratio);
|
||||
Log.Debug(ratio);
|
||||
x = 1280;
|
||||
y = x * ratio;
|
||||
}
|
||||
|
@ -989,7 +1068,7 @@ export default class RFB extends EventTargetMixin {
|
|||
y = 720;
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
Log.Debug(err);
|
||||
}
|
||||
|
||||
return { w: x,
|
||||
|
@ -2383,6 +2462,100 @@ export default class RFB extends EventTargetMixin {
|
|||
return true;
|
||||
}
|
||||
|
||||
_handleBinaryClipboard() {
|
||||
Log.Debug("HandleBinaryClipboard");
|
||||
|
||||
if (this._sock.rQwait("Binary Clipboard header", 2, 1)) { return false; }
|
||||
|
||||
let num = this._sock.rQshift8(); // how many different mime types
|
||||
let mimes = [];
|
||||
let clipItemData = {};
|
||||
let buffByteLen = 2;
|
||||
let textdata = '';
|
||||
Log.Info(num + ' Clipboard items recieved.');
|
||||
Log.Debug('Started clipbooard processing with Client sockjs buffer size ' + this._sock.rQlen);
|
||||
|
||||
|
||||
|
||||
for (let i = 0; i < num; i++) {
|
||||
if (this._sock.rQwait("Binary Clipboard mimelen", 1, buffByteLen)) { return false; }
|
||||
buffByteLen++;
|
||||
let mimelen = this._sock.rQshift8();
|
||||
|
||||
if (this._sock.rQwait("Binary Clipboard mime", Math.abs(mimelen), buffByteLen)) { return false; }
|
||||
buffByteLen+=mimelen;
|
||||
let mime = this._sock.rQshiftStr(mimelen);
|
||||
|
||||
if (this._sock.rQwait("Binary Clipboard data len", 4, buffByteLen)) { return false; }
|
||||
buffByteLen+=4;
|
||||
let len = this._sock.rQshift32();
|
||||
|
||||
if (this._sock.rQwait("Binary Clipboard data", Math.abs(len), buffByteLen)) { return false; }
|
||||
let data = this._sock.rQshiftBytes(len);
|
||||
buffByteLen+=len;
|
||||
|
||||
switch(mime) {
|
||||
case "image/png":
|
||||
case "text/html":
|
||||
case "text/plain":
|
||||
mimes.push(mime);
|
||||
|
||||
if (mime == "text/plain") {
|
||||
|
||||
//textdata = new TextDecoder().decode(data);
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
textdata+=String.fromCharCode(data[i]);
|
||||
}
|
||||
|
||||
if ((textdata.length > 0) && "\0" === textdata.charAt(textdata.length - 1)) {
|
||||
textdata = textdata.slice(0, -1);
|
||||
}
|
||||
|
||||
Log.Debug("Plain text clipboard recieved and placed in text element, size: " + textdata.length);
|
||||
this.dispatchEvent(new CustomEvent(
|
||||
"clipboard",
|
||||
{ detail: { text: textdata } })
|
||||
);
|
||||
}
|
||||
|
||||
if (!this.clipboardBinary) { continue; }
|
||||
|
||||
Log.Info("Processed binary clipboard of MIME " + mime + " of length " + len);
|
||||
|
||||
clipItemData[mime] = new Blob([data], { type: mime });
|
||||
break;
|
||||
default:
|
||||
Log.Debug('Mime type skipped: ' + mime);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Log.Debug('Finished processing binary clipboard with client sockjs buffer size ' + this._sock.rQlen);
|
||||
|
||||
if (Object.keys(clipItemData).length > 0) {
|
||||
if (this.clipboardBinary) {
|
||||
this._clipHash = 0;
|
||||
navigator.clipboard.write([new ClipboardItem(clipItemData)]).then(
|
||||
function() {},
|
||||
function(err) {
|
||||
Log.Error("Error writing to client clipboard: " + err);
|
||||
// Lets try writeText
|
||||
if (textdata.length > 0) {
|
||||
navigator.clipboard.writeText(textdata).then(
|
||||
function() {},
|
||||
function(err2) {
|
||||
Log.Error("Error writing text to client clipboard: " + err2);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
_handle_server_stats_msg() {
|
||||
this._sock.rQskipBytes(3); // Padding
|
||||
const length = this._sock.rQshift32();
|
||||
|
@ -2390,8 +2563,8 @@ export default class RFB extends EventTargetMixin {
|
|||
|
||||
const text = this._sock.rQshiftStr(length);
|
||||
|
||||
console.log("Received KASM bottleneck stats:");
|
||||
console.log(text);
|
||||
Log.Debug("Received KASM bottleneck stats:");
|
||||
Log.Debug(text);
|
||||
this.dispatchEvent(new CustomEvent(
|
||||
"bottleneck_stats",
|
||||
{ detail: { text: text } }));
|
||||
|
@ -2523,6 +2696,9 @@ export default class RFB extends EventTargetMixin {
|
|||
this._trackFrameStats = true;
|
||||
return true;
|
||||
|
||||
case 180: // KASM binary clipboard
|
||||
return this._handleBinaryClipboard();
|
||||
|
||||
case 248: // ServerFence
|
||||
return this._handleServerFenceMsg();
|
||||
|
||||
|
@ -3184,6 +3360,61 @@ RFB.messages = {
|
|||
|
||||
},
|
||||
|
||||
sendBinaryClipboard(sock, dataset, mimes) {
|
||||
|
||||
|
||||
const buff = sock._sQ;
|
||||
let offset = sock._sQlen;
|
||||
|
||||
buff[offset] = 180; // msg-type
|
||||
buff[offset + 1] = dataset.length; // how many mime types
|
||||
sock._sQlen += 2;
|
||||
offset += 2;
|
||||
|
||||
for (let i=0; i < dataset.length; i++) {
|
||||
let mime = mimes[i];
|
||||
let data = dataset[i];
|
||||
|
||||
buff[offset++] = mime.length;
|
||||
|
||||
for (let i = 0; i < mime.length; i++) {
|
||||
buff[offset++] = mime.charCodeAt(i); // change to [] if not a string
|
||||
}
|
||||
|
||||
let length = data.length;
|
||||
|
||||
Log.Info('Clipboard data sent mime type ' + mime + ' len ' + length);
|
||||
|
||||
buff[offset++] = length >> 24;
|
||||
buff[offset++] = length >> 16;
|
||||
buff[offset++] = length >> 8;
|
||||
buff[offset++] = length;
|
||||
|
||||
sock._sQlen += 1 + mime.length + 4;
|
||||
|
||||
// We have to keep track of from where in the data we begin creating the
|
||||
// buffer for the flush in the next iteration.
|
||||
let dataOffset = 0;
|
||||
|
||||
let remaining = data.length;
|
||||
while (remaining > 0) {
|
||||
|
||||
let flushSize = Math.min(remaining, (sock._sQbufferSize - sock._sQlen));
|
||||
for (let i = 0; i < flushSize; i++) {
|
||||
buff[sock._sQlen + i] = data[dataOffset + i];
|
||||
}
|
||||
|
||||
sock._sQlen += flushSize;
|
||||
sock.flush();
|
||||
|
||||
remaining -= flushSize;
|
||||
dataOffset += flushSize;
|
||||
}
|
||||
|
||||
offset = sock._sQlen;
|
||||
}
|
||||
},
|
||||
|
||||
setDesktopSize(sock, width, height, id, flags) {
|
||||
const buff = sock._sQ;
|
||||
const offset = sock._sQlen;
|
||||
|
|
|
@ -101,3 +101,9 @@ export function isFirefox() {
|
|||
return navigator && !!(/firefox/i).exec(navigator.userAgent);
|
||||
}
|
||||
|
||||
export function supportsBinaryClipboard() {
|
||||
//Safari does support the clipbaord API but has a lot of security restrictions
|
||||
if (isSafari()) { return false; }
|
||||
return (navigator.clipboard && typeof navigator.clipboard.read === "function");
|
||||
}
|
||||
|
||||
|
|
|
@ -13,3 +13,14 @@ export function toUnsigned32bit(toConvert) {
|
|||
export function toSigned32bit(toConvert) {
|
||||
return toConvert | 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fast hashing function with low entropy, not for security uses.
|
||||
*/
|
||||
export function hashUInt8Array(data) {
|
||||
let h;
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
h = Math.imul(31, h) + data[i] | 0;
|
||||
}
|
||||
return h;
|
||||
}
|
|
@ -22,7 +22,7 @@
|
|||
"chai": "*",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"commander": "*",
|
||||
"core-js": "*",
|
||||
"core-js": "^3.18.3",
|
||||
"css-loader": "^5.0.1",
|
||||
"css-minimizer-webpack-plugin": "^1.1.5",
|
||||
"es-module-loader": "*",
|
||||
|
@ -48,6 +48,7 @@
|
|||
"po2json": "*",
|
||||
"postcss-loader": "^4.1.0",
|
||||
"preload-webpack-plugin": "^3.0.0-beta.4",
|
||||
"regenerator-runtime": "^0.13.9",
|
||||
"requirejs": "*",
|
||||
"rollup": "*",
|
||||
"rollup-plugin-node-resolve": "*",
|
||||
|
@ -4010,9 +4011,9 @@
|
|||
}
|
||||
},
|
||||
"node_modules/core-js": {
|
||||
"version": "3.16.2",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.2.tgz",
|
||||
"integrity": "sha512-P0KPukO6OjMpjBtHSceAZEWlDD1M2Cpzpg6dBbrjFqFhBHe/BwhxaP820xKOjRn/lZRQirrCusIpLS/n2sgXLQ==",
|
||||
"version": "3.18.3",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.18.3.tgz",
|
||||
"integrity": "sha512-tReEhtMReZaPFVw7dajMx0vlsz3oOb8ajgPoHVYGxr8ErnZ6PcYEvvmjGmXlfpnxpkYSdOQttjB+MvVbCGfvLw==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"funding": {
|
||||
|
@ -18310,7 +18311,8 @@
|
|||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
|
||||
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"acorn-node": {
|
||||
"version": "1.8.2",
|
||||
|
@ -18364,13 +18366,15 @@
|
|||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
|
||||
"integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"ajv-keywords": {
|
||||
"version": "3.5.2",
|
||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"alphanum-sort": {
|
||||
"version": "1.0.2",
|
||||
|
@ -18684,7 +18688,8 @@
|
|||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/babelify/-/babelify-10.0.0.tgz",
|
||||
"integrity": "sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"babylon": {
|
||||
"version": "6.18.0",
|
||||
|
@ -19806,9 +19811,9 @@
|
|||
"dev": true
|
||||
},
|
||||
"core-js": {
|
||||
"version": "3.16.2",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.2.tgz",
|
||||
"integrity": "sha512-P0KPukO6OjMpjBtHSceAZEWlDD1M2Cpzpg6dBbrjFqFhBHe/BwhxaP820xKOjRn/lZRQirrCusIpLS/n2sgXLQ==",
|
||||
"version": "3.18.3",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.18.3.tgz",
|
||||
"integrity": "sha512-tReEhtMReZaPFVw7dajMx0vlsz3oOb8ajgPoHVYGxr8ErnZ6PcYEvvmjGmXlfpnxpkYSdOQttjB+MvVbCGfvLw==",
|
||||
"dev": true
|
||||
},
|
||||
"core-js-compat": {
|
||||
|
@ -20819,7 +20824,8 @@
|
|||
"version": "7.4.6",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
|
||||
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -22436,7 +22442,8 @@
|
|||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
|
||||
"integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"ieee754": {
|
||||
"version": "1.2.1",
|
||||
|
@ -23347,19 +23354,22 @@
|
|||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/karma-safari-launcher/-/karma-safari-launcher-1.0.0.tgz",
|
||||
"integrity": "sha1-lpgqLMR9BmquccVTursoMZEVos4=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"karma-script-launcher": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/karma-script-launcher/-/karma-script-launcher-1.0.0.tgz",
|
||||
"integrity": "sha1-zQF8TeXvCeWp2nkydhdhCN1LVC0=",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"karma-sinon-chai": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/karma-sinon-chai/-/karma-sinon-chai-2.0.2.tgz",
|
||||
"integrity": "sha512-SDgh6V0CUd+7ruL1d3yG6lFzmJNGRNQuEuCYXLaorruNP9nwQfA7hpsp4clx4CbOo5Gsajh3qUOT7CrVStUKMw==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"kew": {
|
||||
"version": "0.7.0",
|
||||
|
@ -25647,7 +25657,8 @@
|
|||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
|
||||
"integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"postcss-modules-local-by-default": {
|
||||
"version": "4.0.0",
|
||||
|
@ -26312,7 +26323,8 @@
|
|||
"version": "3.0.0-beta.4",
|
||||
"resolved": "https://registry.npmjs.org/preload-webpack-plugin/-/preload-webpack-plugin-3.0.0-beta.4.tgz",
|
||||
"integrity": "sha512-6hhh0AswCbp/U4EPVN4fbK2wiDkXhmgjjgEYEmXa21UYwjYzCIgh3ZRMXM21ZPLfbQGpdFuSL3zFslU+edjpwg==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"prelude-ls": {
|
||||
"version": "1.2.1",
|
||||
|
@ -27246,7 +27258,8 @@
|
|||
"version": "2.14.0",
|
||||
"resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-2.14.0.tgz",
|
||||
"integrity": "sha512-9stIF1utB0ywNHNT7RgiXbdmen8QDCRsrTjw+G9TgKt1Yexjiv8TOWZ6WHsTPz57Yky3DIswZvEqX8fpuHNDtQ==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"slash": {
|
||||
"version": "2.0.0",
|
||||
|
@ -29645,7 +29658,8 @@
|
|||
"version": "8.2.0",
|
||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.0.tgz",
|
||||
"integrity": "sha512-uYhVJ/m9oXwEI04iIVmgLmugh2qrZihkywG9y5FfZV2ATeLIzHf93qs+tUNqlttbQK957/VX3mtwAS+UfIwA4g==",
|
||||
"dev": true
|
||||
"dev": true,
|
||||
"requires": {}
|
||||
},
|
||||
"xml-name-validator": {
|
||||
"version": "3.0.0",
|
||||
|
|
19
package.json
19
package.json
|
@ -30,29 +30,27 @@
|
|||
"url": "git+https://github.com/kasmtech/noVNC.git"
|
||||
},
|
||||
"author": "Kasm Technologies (https://www.kasmweb.com)",
|
||||
"contributors": [
|
||||
],
|
||||
"contributors": [],
|
||||
"license": "MPL-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/kasmtech/noVNC/issues"
|
||||
},
|
||||
"homepage": "https://github.com/kasmtech/noVNC",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.12.10",
|
||||
"@babel/preset-env": "^7.12.11",
|
||||
"@babel/cli": "*",
|
||||
"@babel/core": "*",
|
||||
"babel-loader": "^8.2.2",
|
||||
"@babel/plugin-syntax-dynamic-import": "*",
|
||||
"@babel/plugin-transform-modules-commonjs": "*",
|
||||
"@babel/preset-env": "*",
|
||||
"@babel/cli": "*",
|
||||
"@chiragrupani/karma-chromium-edge-launcher": "*",
|
||||
"babel-loader": "^8.2.2",
|
||||
"babel-plugin-import-redirect": "*",
|
||||
"browserify": "*",
|
||||
"babelify": "*",
|
||||
"core-js": "*",
|
||||
"browserify": "*",
|
||||
"chai": "*",
|
||||
"clean-webpack-plugin": "^3.0.0",
|
||||
"commander": "*",
|
||||
"core-js": "^3.18.3",
|
||||
"css-loader": "^5.0.1",
|
||||
"css-minimizer-webpack-plugin": "^1.1.5",
|
||||
"es-module-loader": "*",
|
||||
|
@ -64,11 +62,10 @@
|
|||
"html-webpack-plugin": "^4.5.0",
|
||||
"jsdom": "*",
|
||||
"karma": "*",
|
||||
"karma-mocha": "*",
|
||||
"karma-chrome-launcher": "*",
|
||||
"@chiragrupani/karma-chromium-edge-launcher": "*",
|
||||
"karma-firefox-launcher": "*",
|
||||
"karma-ie-launcher": "*",
|
||||
"karma-mocha": "*",
|
||||
"karma-mocha-reporter": "*",
|
||||
"karma-safari-launcher": "*",
|
||||
"karma-script-launcher": "*",
|
||||
|
@ -79,6 +76,7 @@
|
|||
"po2json": "*",
|
||||
"postcss-loader": "^4.1.0",
|
||||
"preload-webpack-plugin": "^3.0.0-beta.4",
|
||||
"regenerator-runtime": "^0.13.9",
|
||||
"requirejs": "*",
|
||||
"rollup": "*",
|
||||
"rollup-plugin-node-resolve": "*",
|
||||
|
@ -90,7 +88,6 @@
|
|||
"webpack": "^4.29.6",
|
||||
"webpack-cli": "^3.2.3"
|
||||
},
|
||||
"dependencies": {},
|
||||
"keywords": [
|
||||
"vnc",
|
||||
"rfb",
|
||||
|
|
Loading…
Reference in New Issue