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 * as Log from '../core/util/logging.js';
|
||||||
import _, { l10n } from './localization.js';
|
import _, { l10n } from './localization.js';
|
||||||
import { isTouchDevice, isSafari, hasScrollbarGutter, dragThreshold }
|
import { isTouchDevice, isSafari, hasScrollbarGutter, dragThreshold, supportsBinaryClipboard, isFirefox }
|
||||||
from '../core/util/browser.js';
|
from '../core/util/browser.js';
|
||||||
import { setCapture, getPointerEvent } from '../core/util/events.js';
|
import { setCapture, getPointerEvent } from '../core/util/events.js';
|
||||||
import KeyTable from "../core/input/keysym.js";
|
import KeyTable from "../core/input/keysym.js";
|
||||||
|
@ -1100,20 +1102,12 @@ const UI = {
|
||||||
},
|
},
|
||||||
|
|
||||||
clipboardReceive(e) {
|
clipboardReceive(e) {
|
||||||
if (UI.rfb.clipboardDown && UI.rfb.clipboardSeamless ) {
|
if (UI.rfb.clipboardDown) {
|
||||||
var curvalue = document.getElementById('noVNC_clipboard_text').value;
|
var curvalue = document.getElementById('noVNC_clipboard_text').value;
|
||||||
if (curvalue != e.detail.text) {
|
if (curvalue != e.detail.text) {
|
||||||
Log.Debug(">> UI.clipboardReceive: " + e.detail.text.substr(0, 40) + "...");
|
Log.Debug(">> UI.clipboardReceive: " + e.detail.text.substr(0, 40) + "...");
|
||||||
document.getElementById('noVNC_clipboard_text').value = e.detail.text;
|
document.getElementById('noVNC_clipboard_text').value = e.detail.text;
|
||||||
Log.Debug("<< UI.clipboardReceive");
|
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();
|
UI.copyFromLocalClipboard();
|
||||||
},
|
},
|
||||||
copyFromLocalClipboard: function copyFromLocalClipboard() {
|
copyFromLocalClipboard: function copyFromLocalClipboard() {
|
||||||
|
if (!document.hasFocus()) {
|
||||||
|
Log.Debug("window does not have focus");
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (UI.rfb && UI.rfb.clipboardUp && UI.rfb.clipboardSeamless) {
|
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) {
|
if (UI.rfb.clipboardBinary) {
|
||||||
document.getElementById('noVNC_clipboard_text').value = text; // The websocket has a maximum buffer array size
|
navigator.clipboard.read().then((data) => {
|
||||||
|
if (UI.rfb) {
|
||||||
if (text.length > maximumBufferSize) {
|
UI.rfb.clipboardPasteDataFrom(data);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
} // Reset flag to prevent checking too often
|
UI.needToCheckClipboardChange = false;
|
||||||
|
}, (err) => {
|
||||||
|
Log.Debug("No data in clipboard");
|
||||||
UI.needToCheckClipboardChange = false;
|
});
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1304,6 +1294,13 @@ const UI = {
|
||||||
UI.rfb.clipboardUp = UI.getSetting('clipboard_up');
|
UI.rfb.clipboardUp = UI.getSetting('clipboard_up');
|
||||||
UI.rfb.clipboardDown = UI.getSetting('clipboard_down');
|
UI.rfb.clipboardDown = UI.getSetting('clipboard_down');
|
||||||
UI.rfb.clipboardSeamless = UI.getSetting('clipboard_seamless');
|
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
|
// KASM-960 workaround, disable seamless on Safari
|
||||||
if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent))
|
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 { toUnsigned32bit, toSigned32bit } from './util/int.js';
|
||||||
import * as Log from './util/logging.js';
|
import * as Log from './util/logging.js';
|
||||||
import { encodeUTF8, decodeUTF8 } from './util/strings.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 { dragThreshold, supportsCursorURIs, isTouchDevice, isWindows, isMac } from './util/browser.js';
|
||||||
import { clientToElement } from './util/element.js';
|
import { clientToElement } from './util/element.js';
|
||||||
import { setCapture } from './util/events.js';
|
import { setCapture } from './util/events.js';
|
||||||
|
@ -140,6 +141,7 @@ export default class RFB extends EventTargetMixin {
|
||||||
this._frameRate = 30;
|
this._frameRate = 30;
|
||||||
this._maxVideoResolutionX = 960;
|
this._maxVideoResolutionX = 960;
|
||||||
this._maxVideoResolutionY = 540;
|
this._maxVideoResolutionY = 540;
|
||||||
|
this._clipboardBinary = true;
|
||||||
|
|
||||||
this._trackFrameStats = false;
|
this._trackFrameStats = false;
|
||||||
|
|
||||||
|
@ -332,10 +334,14 @@ export default class RFB extends EventTargetMixin {
|
||||||
|
|
||||||
this._qualityLevel = 6;
|
this._qualityLevel = 6;
|
||||||
this._compressionLevel = 2;
|
this._compressionLevel = 2;
|
||||||
|
this._clipHash = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== PROPERTIES =====
|
// ===== PROPERTIES =====
|
||||||
|
|
||||||
|
get clipboardBinary() { return this._clipboardMode; }
|
||||||
|
set clipboardBinary(val) { this._clipboardMode = val; }
|
||||||
|
|
||||||
get videoQuality() { return this._videoQuality; }
|
get videoQuality() { return this._videoQuality; }
|
||||||
set videoQuality(quality) { this._videoQuality = quality; }
|
set videoQuality(quality) { this._videoQuality = quality; }
|
||||||
|
|
||||||
|
@ -763,21 +769,94 @@ export default class RFB extends EventTargetMixin {
|
||||||
|
|
||||||
clipboardPasteFrom(text) {
|
clipboardPasteFrom(text) {
|
||||||
if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; }
|
if (this._rfbConnectionState !== 'connected' || this._viewOnly) { return; }
|
||||||
|
if (!(typeof text === 'string' && text.length > 0)) { return; }
|
||||||
|
|
||||||
this.sentEventsCounter+=1;
|
this.sentEventsCounter+=1;
|
||||||
if (this._clipboardServerCapabilitiesFormats[extendedClipboardFormatText] &&
|
|
||||||
this._clipboardServerCapabilitiesActions[extendedClipboardActionNotify]) {
|
|
||||||
|
|
||||||
this._clipboardText = text;
|
let data = new Uint8Array(text.length);
|
||||||
RFB.messages.extendedClipboardNotify(this._sock, [extendedClipboardFormatText]);
|
for (let i = 0; i < text.length; i++) {
|
||||||
} else {
|
data[i] = text.charCodeAt(i);
|
||||||
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 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() {
|
requestBottleneckStats() {
|
||||||
|
@ -980,7 +1059,7 @@ export default class RFB extends EventTargetMixin {
|
||||||
try {
|
try {
|
||||||
if (x > 1280 && limited && this.videoQuality == 1) {
|
if (x > 1280 && limited && this.videoQuality == 1) {
|
||||||
var ratio = y / x;
|
var ratio = y / x;
|
||||||
console.log(ratio);
|
Log.Debug(ratio);
|
||||||
x = 1280;
|
x = 1280;
|
||||||
y = x * ratio;
|
y = x * ratio;
|
||||||
}
|
}
|
||||||
|
@ -989,7 +1068,7 @@ export default class RFB extends EventTargetMixin {
|
||||||
y = 720;
|
y = 720;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
Log.Debug(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
return { w: x,
|
return { w: x,
|
||||||
|
@ -2383,6 +2462,100 @@ export default class RFB extends EventTargetMixin {
|
||||||
return true;
|
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() {
|
_handle_server_stats_msg() {
|
||||||
this._sock.rQskipBytes(3); // Padding
|
this._sock.rQskipBytes(3); // Padding
|
||||||
const length = this._sock.rQshift32();
|
const length = this._sock.rQshift32();
|
||||||
|
@ -2390,8 +2563,8 @@ export default class RFB extends EventTargetMixin {
|
||||||
|
|
||||||
const text = this._sock.rQshiftStr(length);
|
const text = this._sock.rQshiftStr(length);
|
||||||
|
|
||||||
console.log("Received KASM bottleneck stats:");
|
Log.Debug("Received KASM bottleneck stats:");
|
||||||
console.log(text);
|
Log.Debug(text);
|
||||||
this.dispatchEvent(new CustomEvent(
|
this.dispatchEvent(new CustomEvent(
|
||||||
"bottleneck_stats",
|
"bottleneck_stats",
|
||||||
{ detail: { text: text } }));
|
{ detail: { text: text } }));
|
||||||
|
@ -2523,6 +2696,9 @@ export default class RFB extends EventTargetMixin {
|
||||||
this._trackFrameStats = true;
|
this._trackFrameStats = true;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case 180: // KASM binary clipboard
|
||||||
|
return this._handleBinaryClipboard();
|
||||||
|
|
||||||
case 248: // ServerFence
|
case 248: // ServerFence
|
||||||
return this._handleServerFenceMsg();
|
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) {
|
setDesktopSize(sock, width, height, id, flags) {
|
||||||
const buff = sock._sQ;
|
const buff = sock._sQ;
|
||||||
const offset = sock._sQlen;
|
const offset = sock._sQlen;
|
||||||
|
|
|
@ -101,3 +101,9 @@ export function isFirefox() {
|
||||||
return navigator && !!(/firefox/i).exec(navigator.userAgent);
|
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) {
|
export function toSigned32bit(toConvert) {
|
||||||
return toConvert | 0;
|
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": "*",
|
"chai": "*",
|
||||||
"clean-webpack-plugin": "^3.0.0",
|
"clean-webpack-plugin": "^3.0.0",
|
||||||
"commander": "*",
|
"commander": "*",
|
||||||
"core-js": "*",
|
"core-js": "^3.18.3",
|
||||||
"css-loader": "^5.0.1",
|
"css-loader": "^5.0.1",
|
||||||
"css-minimizer-webpack-plugin": "^1.1.5",
|
"css-minimizer-webpack-plugin": "^1.1.5",
|
||||||
"es-module-loader": "*",
|
"es-module-loader": "*",
|
||||||
|
@ -48,6 +48,7 @@
|
||||||
"po2json": "*",
|
"po2json": "*",
|
||||||
"postcss-loader": "^4.1.0",
|
"postcss-loader": "^4.1.0",
|
||||||
"preload-webpack-plugin": "^3.0.0-beta.4",
|
"preload-webpack-plugin": "^3.0.0-beta.4",
|
||||||
|
"regenerator-runtime": "^0.13.9",
|
||||||
"requirejs": "*",
|
"requirejs": "*",
|
||||||
"rollup": "*",
|
"rollup": "*",
|
||||||
"rollup-plugin-node-resolve": "*",
|
"rollup-plugin-node-resolve": "*",
|
||||||
|
@ -4010,9 +4011,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/core-js": {
|
"node_modules/core-js": {
|
||||||
"version": "3.16.2",
|
"version": "3.18.3",
|
||||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.2.tgz",
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.18.3.tgz",
|
||||||
"integrity": "sha512-P0KPukO6OjMpjBtHSceAZEWlDD1M2Cpzpg6dBbrjFqFhBHe/BwhxaP820xKOjRn/lZRQirrCusIpLS/n2sgXLQ==",
|
"integrity": "sha512-tReEhtMReZaPFVw7dajMx0vlsz3oOb8ajgPoHVYGxr8ErnZ6PcYEvvmjGmXlfpnxpkYSdOQttjB+MvVbCGfvLw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"funding": {
|
"funding": {
|
||||||
|
@ -18310,7 +18311,8 @@
|
||||||
"version": "5.3.2",
|
"version": "5.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
|
||||||
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
|
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
},
|
},
|
||||||
"acorn-node": {
|
"acorn-node": {
|
||||||
"version": "1.8.2",
|
"version": "1.8.2",
|
||||||
|
@ -18364,13 +18366,15 @@
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz",
|
||||||
"integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
|
"integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
},
|
},
|
||||||
"ajv-keywords": {
|
"ajv-keywords": {
|
||||||
"version": "3.5.2",
|
"version": "3.5.2",
|
||||||
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
|
||||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
},
|
},
|
||||||
"alphanum-sort": {
|
"alphanum-sort": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
|
@ -18684,7 +18688,8 @@
|
||||||
"version": "10.0.0",
|
"version": "10.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/babelify/-/babelify-10.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/babelify/-/babelify-10.0.0.tgz",
|
||||||
"integrity": "sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg==",
|
"integrity": "sha512-X40FaxyH7t3X+JFAKvb1H9wooWKLRCi8pg3m8poqtdZaIng+bjzp9RvKQCvRjF9isHiPkXspbbXT/zwXLtwgwg==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
},
|
},
|
||||||
"babylon": {
|
"babylon": {
|
||||||
"version": "6.18.0",
|
"version": "6.18.0",
|
||||||
|
@ -19806,9 +19811,9 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"core-js": {
|
"core-js": {
|
||||||
"version": "3.16.2",
|
"version": "3.18.3",
|
||||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.16.2.tgz",
|
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.18.3.tgz",
|
||||||
"integrity": "sha512-P0KPukO6OjMpjBtHSceAZEWlDD1M2Cpzpg6dBbrjFqFhBHe/BwhxaP820xKOjRn/lZRQirrCusIpLS/n2sgXLQ==",
|
"integrity": "sha512-tReEhtMReZaPFVw7dajMx0vlsz3oOb8ajgPoHVYGxr8ErnZ6PcYEvvmjGmXlfpnxpkYSdOQttjB+MvVbCGfvLw==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"core-js-compat": {
|
"core-js-compat": {
|
||||||
|
@ -20819,7 +20824,8 @@
|
||||||
"version": "7.4.6",
|
"version": "7.4.6",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz",
|
||||||
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
|
"integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -22436,7 +22442,8 @@
|
||||||
"version": "5.1.0",
|
"version": "5.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz",
|
||||||
"integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
|
"integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
},
|
},
|
||||||
"ieee754": {
|
"ieee754": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
|
@ -23347,19 +23354,22 @@
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/karma-safari-launcher/-/karma-safari-launcher-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/karma-safari-launcher/-/karma-safari-launcher-1.0.0.tgz",
|
||||||
"integrity": "sha1-lpgqLMR9BmquccVTursoMZEVos4=",
|
"integrity": "sha1-lpgqLMR9BmquccVTursoMZEVos4=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
},
|
},
|
||||||
"karma-script-launcher": {
|
"karma-script-launcher": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/karma-script-launcher/-/karma-script-launcher-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/karma-script-launcher/-/karma-script-launcher-1.0.0.tgz",
|
||||||
"integrity": "sha1-zQF8TeXvCeWp2nkydhdhCN1LVC0=",
|
"integrity": "sha1-zQF8TeXvCeWp2nkydhdhCN1LVC0=",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
},
|
},
|
||||||
"karma-sinon-chai": {
|
"karma-sinon-chai": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/karma-sinon-chai/-/karma-sinon-chai-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/karma-sinon-chai/-/karma-sinon-chai-2.0.2.tgz",
|
||||||
"integrity": "sha512-SDgh6V0CUd+7ruL1d3yG6lFzmJNGRNQuEuCYXLaorruNP9nwQfA7hpsp4clx4CbOo5Gsajh3qUOT7CrVStUKMw==",
|
"integrity": "sha512-SDgh6V0CUd+7ruL1d3yG6lFzmJNGRNQuEuCYXLaorruNP9nwQfA7hpsp4clx4CbOo5Gsajh3qUOT7CrVStUKMw==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
},
|
},
|
||||||
"kew": {
|
"kew": {
|
||||||
"version": "0.7.0",
|
"version": "0.7.0",
|
||||||
|
@ -25647,7 +25657,8 @@
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz",
|
||||||
"integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
|
"integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
},
|
},
|
||||||
"postcss-modules-local-by-default": {
|
"postcss-modules-local-by-default": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
|
@ -26312,7 +26323,8 @@
|
||||||
"version": "3.0.0-beta.4",
|
"version": "3.0.0-beta.4",
|
||||||
"resolved": "https://registry.npmjs.org/preload-webpack-plugin/-/preload-webpack-plugin-3.0.0-beta.4.tgz",
|
"resolved": "https://registry.npmjs.org/preload-webpack-plugin/-/preload-webpack-plugin-3.0.0-beta.4.tgz",
|
||||||
"integrity": "sha512-6hhh0AswCbp/U4EPVN4fbK2wiDkXhmgjjgEYEmXa21UYwjYzCIgh3ZRMXM21ZPLfbQGpdFuSL3zFslU+edjpwg==",
|
"integrity": "sha512-6hhh0AswCbp/U4EPVN4fbK2wiDkXhmgjjgEYEmXa21UYwjYzCIgh3ZRMXM21ZPLfbQGpdFuSL3zFslU+edjpwg==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
},
|
},
|
||||||
"prelude-ls": {
|
"prelude-ls": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
|
@ -27246,7 +27258,8 @@
|
||||||
"version": "2.14.0",
|
"version": "2.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-2.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/sinon-chai/-/sinon-chai-2.14.0.tgz",
|
||||||
"integrity": "sha512-9stIF1utB0ywNHNT7RgiXbdmen8QDCRsrTjw+G9TgKt1Yexjiv8TOWZ6WHsTPz57Yky3DIswZvEqX8fpuHNDtQ==",
|
"integrity": "sha512-9stIF1utB0ywNHNT7RgiXbdmen8QDCRsrTjw+G9TgKt1Yexjiv8TOWZ6WHsTPz57Yky3DIswZvEqX8fpuHNDtQ==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
},
|
},
|
||||||
"slash": {
|
"slash": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
|
@ -29645,7 +29658,8 @@
|
||||||
"version": "8.2.0",
|
"version": "8.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-8.2.0.tgz",
|
||||||
"integrity": "sha512-uYhVJ/m9oXwEI04iIVmgLmugh2qrZihkywG9y5FfZV2ATeLIzHf93qs+tUNqlttbQK957/VX3mtwAS+UfIwA4g==",
|
"integrity": "sha512-uYhVJ/m9oXwEI04iIVmgLmugh2qrZihkywG9y5FfZV2ATeLIzHf93qs+tUNqlttbQK957/VX3mtwAS+UfIwA4g==",
|
||||||
"dev": true
|
"dev": true,
|
||||||
|
"requires": {}
|
||||||
},
|
},
|
||||||
"xml-name-validator": {
|
"xml-name-validator": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
|
|
19
package.json
19
package.json
|
@ -30,29 +30,27 @@
|
||||||
"url": "git+https://github.com/kasmtech/noVNC.git"
|
"url": "git+https://github.com/kasmtech/noVNC.git"
|
||||||
},
|
},
|
||||||
"author": "Kasm Technologies (https://www.kasmweb.com)",
|
"author": "Kasm Technologies (https://www.kasmweb.com)",
|
||||||
"contributors": [
|
"contributors": [],
|
||||||
],
|
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/kasmtech/noVNC/issues"
|
"url": "https://github.com/kasmtech/noVNC/issues"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/kasmtech/noVNC",
|
"homepage": "https://github.com/kasmtech/noVNC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.12.10",
|
"@babel/cli": "*",
|
||||||
"@babel/preset-env": "^7.12.11",
|
|
||||||
"@babel/core": "*",
|
"@babel/core": "*",
|
||||||
"babel-loader": "^8.2.2",
|
|
||||||
"@babel/plugin-syntax-dynamic-import": "*",
|
"@babel/plugin-syntax-dynamic-import": "*",
|
||||||
"@babel/plugin-transform-modules-commonjs": "*",
|
"@babel/plugin-transform-modules-commonjs": "*",
|
||||||
"@babel/preset-env": "*",
|
"@babel/preset-env": "*",
|
||||||
"@babel/cli": "*",
|
"@chiragrupani/karma-chromium-edge-launcher": "*",
|
||||||
|
"babel-loader": "^8.2.2",
|
||||||
"babel-plugin-import-redirect": "*",
|
"babel-plugin-import-redirect": "*",
|
||||||
"browserify": "*",
|
|
||||||
"babelify": "*",
|
"babelify": "*",
|
||||||
"core-js": "*",
|
"browserify": "*",
|
||||||
"chai": "*",
|
"chai": "*",
|
||||||
"clean-webpack-plugin": "^3.0.0",
|
"clean-webpack-plugin": "^3.0.0",
|
||||||
"commander": "*",
|
"commander": "*",
|
||||||
|
"core-js": "^3.18.3",
|
||||||
"css-loader": "^5.0.1",
|
"css-loader": "^5.0.1",
|
||||||
"css-minimizer-webpack-plugin": "^1.1.5",
|
"css-minimizer-webpack-plugin": "^1.1.5",
|
||||||
"es-module-loader": "*",
|
"es-module-loader": "*",
|
||||||
|
@ -64,11 +62,10 @@
|
||||||
"html-webpack-plugin": "^4.5.0",
|
"html-webpack-plugin": "^4.5.0",
|
||||||
"jsdom": "*",
|
"jsdom": "*",
|
||||||
"karma": "*",
|
"karma": "*",
|
||||||
"karma-mocha": "*",
|
|
||||||
"karma-chrome-launcher": "*",
|
"karma-chrome-launcher": "*",
|
||||||
"@chiragrupani/karma-chromium-edge-launcher": "*",
|
|
||||||
"karma-firefox-launcher": "*",
|
"karma-firefox-launcher": "*",
|
||||||
"karma-ie-launcher": "*",
|
"karma-ie-launcher": "*",
|
||||||
|
"karma-mocha": "*",
|
||||||
"karma-mocha-reporter": "*",
|
"karma-mocha-reporter": "*",
|
||||||
"karma-safari-launcher": "*",
|
"karma-safari-launcher": "*",
|
||||||
"karma-script-launcher": "*",
|
"karma-script-launcher": "*",
|
||||||
|
@ -79,6 +76,7 @@
|
||||||
"po2json": "*",
|
"po2json": "*",
|
||||||
"postcss-loader": "^4.1.0",
|
"postcss-loader": "^4.1.0",
|
||||||
"preload-webpack-plugin": "^3.0.0-beta.4",
|
"preload-webpack-plugin": "^3.0.0-beta.4",
|
||||||
|
"regenerator-runtime": "^0.13.9",
|
||||||
"requirejs": "*",
|
"requirejs": "*",
|
||||||
"rollup": "*",
|
"rollup": "*",
|
||||||
"rollup-plugin-node-resolve": "*",
|
"rollup-plugin-node-resolve": "*",
|
||||||
|
@ -90,7 +88,6 @@
|
||||||
"webpack": "^4.29.6",
|
"webpack": "^4.29.6",
|
||||||
"webpack-cli": "^3.2.3"
|
"webpack-cli": "^3.2.3"
|
||||||
},
|
},
|
||||||
"dependencies": {},
|
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"vnc",
|
"vnc",
|
||||||
"rfb",
|
"rfb",
|
||||||
|
|
Loading…
Reference in New Issue