This commit is contained in:
Pierre Ossman 2020-12-07 14:30:05 +01:00
commit dc9da4a042
41 changed files with 93 additions and 124603 deletions

View File

@ -17,8 +17,6 @@ jobs:
browser: Safari
- os: windows-latest
browser: EdgeHeadless
- os: windows-latest
browser: IE
fail-fast: false
runs-on: ${{ matrix.os }}
steps:

View File

@ -42,12 +42,6 @@ licenses (all MPL 2.0 compatible):
vendor/pako/ : MIT
vendor/browser-es-module-loader/src/ : MIT
vendor/browser-es-module-loader/dist/ : Various BSD style licenses
vendor/promise.js : MIT
Any other files not mentioned above are typically marked with
a copyright/license header at the top of the file. The default noVNC
license is MPL-2.0.

View File

@ -91,7 +91,7 @@ noVNC uses many modern web technologies so a formal requirement list is
not available. However these are the minimum versions we are currently
aware of:
* Chrome 49, Firefox 44, Safari 11, Opera 36, IE 11, Edge 12
* Chrome 49, Firefox 44, Safari 11, Opera 36, Edge 79
### Server Requirements

View File

@ -61,7 +61,13 @@ const UI = {
// Translate the DOM
l10n.translateDOM();
WebUtil.fetchJSON('./package.json')
fetch('./package.json')
.then((response) => {
if (!response.ok) {
throw Error("" + response.status + " " + response.statusText);
}
return response.json();
})
.then((packageInfo) => {
Array.from(document.getElementsByClassName('noVNC_version')).forEach(el => el.innerText = packageInfo.version);
})
@ -755,11 +761,6 @@ const UI = {
}
}
} else {
/*Weird IE9 error leads to 'null' appearring
in textboxes instead of ''.*/
if (value === null) {
value = "";
}
ctrl.value = value;
}
},
@ -1699,7 +1700,13 @@ l10n.setup(LINGUAS);
if (l10n.language === "en" || l10n.dictionary !== undefined) {
UI.prime();
} else {
WebUtil.fetchJSON('app/locale/' + l10n.language + '.json')
fetch('app/locale/' + l10n.language + '.json')
.then((response) => {
if (!response.ok) {
throw Error("" + response.status + " " + response.statusText);
}
return response.json();
})
.then((translations) => { l10n.dictionary = translations; })
.catch(err => Log.Error("Failed to load translations: " + err))
.then(UI.prime);

View File

@ -175,65 +175,3 @@ export function eraseSetting(name) {
localStorage.removeItem(name);
}
}
export function injectParamIfMissing(path, param, value) {
// force pretend that we're dealing with a relative path
// (assume that we wanted an extra if we pass one in)
path = "/" + path;
const elem = document.createElement('a');
elem.href = path;
const paramEq = encodeURIComponent(param) + "=";
let query;
if (elem.search) {
query = elem.search.slice(1).split('&');
} else {
query = [];
}
if (!query.some(v => v.startsWith(paramEq))) {
query.push(paramEq + encodeURIComponent(value));
elem.search = "?" + query.join("&");
}
// some browsers (e.g. IE11) may occasionally omit the leading slash
// in the elem.pathname string. Handle that case gracefully.
if (elem.pathname.charAt(0) == "/") {
return elem.pathname.slice(1) + elem.search + elem.hash;
}
return elem.pathname + elem.search + elem.hash;
}
// sadly, we can't use the Fetch API until we decide to drop
// IE11 support or polyfill promises and fetch in IE11.
// resolve will receive an object on success, while reject
// will receive either an event or an error on failure.
export function fetchJSON(path) {
return new Promise((resolve, reject) => {
// NB: IE11 doesn't support JSON as a responseType
const req = new XMLHttpRequest();
req.open('GET', path);
req.onload = () => {
if (req.status === 200) {
let resObj;
try {
resObj = JSON.parse(req.responseText);
} catch (err) {
reject(err);
}
resolve(resObj);
} else {
reject(new Error("XHR got non-200 status while trying to load '" + path + "': " + req.status));
}
};
req.onerror = evt => reject(new Error("XHR encountered an error while trying to load '" + path + "': " + evt.message));
req.ontimeout = evt => reject(new Error("XHR timed out while trying to load '" + path + "'"));
req.send();
});
}

View File

@ -8,7 +8,6 @@
import * as Log from './util/logging.js';
import Base64 from "./base64.js";
import { supportsImageMetadata } from './util/browser.js';
import { toSigned32bit } from './util/int.js';
export default class Display {
@ -56,11 +55,6 @@ export default class Display {
Log.Debug("User Agent: " + navigator.userAgent);
// Check canvas features
if (!('createImageData' in this._drawCtx)) {
throw new Error("Canvas does not support createImageData");
}
Log.Debug("<< Display.constructor");
// ===== PROPERTIES =====
@ -393,13 +387,7 @@ export default class Display {
let data = new Uint8ClampedArray(arr.buffer,
arr.byteOffset + offset,
width * height * 4);
let img;
if (supportsImageMetadata) {
img = new ImageData(data, width, height);
} else {
img = this._drawCtx.createImageData(width, height);
img.data.set(data);
}
let img = new ImageData(data, width, height);
this._drawCtx.putImageData(img, x, y);
this._damage(x, y, width, height);
}
@ -494,8 +482,7 @@ export default class Display {
this.blitImage(a.x, a.y, a.width, a.height, a.data, 0, true);
break;
case 'img':
/* IE tends to set "complete" prematurely, so check dimensions */
if (a.img.complete && (a.img.width !== 0) && (a.img.height !== 0)) {
if (a.img.complete) {
if (a.img.width !== a.width || a.img.height !== a.height) {
Log.Error("Decoded image has incorrect dimensions. Got " +
a.img.width + "x" + a.img.height + ". Expected " +

View File

@ -20,14 +20,12 @@ export default class Keyboard {
this._keyDownList = {}; // List of depressed keys
// (even if they are happy)
this._pendingKey = null; // Key waiting for keypress
this._altGrArmed = false; // Windows AltGr detection
// keep these here so we can refer to them later
this._eventHandlers = {
'keyup': this._handleKeyUp.bind(this),
'keydown': this._handleKeyDown.bind(this),
'keypress': this._handleKeyPress.bind(this),
'blur': this._allKeysUp.bind(this),
};
@ -61,9 +59,7 @@ export default class Keyboard {
}
// Unstable, but we don't have anything else to go on
// (don't use it for 'keypress' events thought since
// WebKit sets it to the same as charCode)
if (e.keyCode && (e.type !== 'keypress')) {
if (e.keyCode) {
// 229 is used for composition events
if (e.keyCode !== 229) {
return 'Platform' + e.keyCode;
@ -168,20 +164,6 @@ export default class Keyboard {
return;
}
// If this is a legacy browser then we'll need to wait for
// a keypress event as well
// (IE and Edge has a broken KeyboardEvent.key, so we can't
// just check for the presence of that field)
if (!keysym && (!e.key || browser.isIE() || browser.isEdge())) {
this._pendingKey = code;
// However we might not get a keypress event if the key
// is non-printable, which needs some special fallback
// handling
setTimeout(this._handleKeyPressTimeout.bind(this), 10, e);
return;
}
this._pendingKey = null;
stopEvent(e);
// Possible start of AltGr sequence? (see above)
@ -196,69 +178,6 @@ export default class Keyboard {
this._sendKeyEvent(keysym, code, true);
}
// Legacy event for browsers without code/key
_handleKeyPress(e) {
stopEvent(e);
// Are we expecting a keypress?
if (this._pendingKey === null) {
return;
}
let code = this._getKeyCode(e);
const keysym = KeyboardUtil.getKeysym(e);
// The key we were waiting for?
if ((code !== 'Unidentified') && (code != this._pendingKey)) {
return;
}
code = this._pendingKey;
this._pendingKey = null;
if (!keysym) {
Log.Info('keypress with no keysym:', e);
return;
}
this._sendKeyEvent(keysym, code, true);
}
_handleKeyPressTimeout(e) {
// Did someone manage to sort out the key already?
if (this._pendingKey === null) {
return;
}
let keysym;
const code = this._pendingKey;
this._pendingKey = null;
// We have no way of knowing the proper keysym with the
// information given, but the following are true for most
// layouts
if ((e.keyCode >= 0x30) && (e.keyCode <= 0x39)) {
// Digit
keysym = e.keyCode;
} else if ((e.keyCode >= 0x41) && (e.keyCode <= 0x5a)) {
// Character (A-Z)
let char = String.fromCharCode(e.keyCode);
// A feeble attempt at the correct case
if (e.shiftKey) {
char = char.toUpperCase();
} else {
char = char.toLowerCase();
}
keysym = char.charCodeAt();
} else {
// Unknown, give up
keysym = 0;
}
this._sendKeyEvent(keysym, code, true);
}
_handleKeyUp(e) {
stopEvent(e);
@ -318,7 +237,6 @@ export default class Keyboard {
this._target.addEventListener('keydown', this._eventHandlers.keydown);
this._target.addEventListener('keyup', this._eventHandlers.keyup);
this._target.addEventListener('keypress', this._eventHandlers.keypress);
// Release (key up) if window loses focus
window.addEventListener('blur', this._eventHandlers.blur);
@ -331,7 +249,6 @@ export default class Keyboard {
this._target.removeEventListener('keydown', this._eventHandlers.keydown);
this._target.removeEventListener('keyup', this._eventHandlers.keyup);
this._target.removeEventListener('keypress', this._eventHandlers.keypress);
window.removeEventListener('blur', this._eventHandlers.blur);
// Release (key up) all keys that are in a down state

View File

@ -22,9 +22,8 @@ export function getKeycode(evt) {
}
// The de-facto standard is to use Windows Virtual-Key codes
// in the 'keyCode' field for non-printable characters. However
// Webkit sets it to the same as charCode in 'keypress' events.
if ((evt.type !== 'keypress') && (evt.keyCode in vkeys)) {
// in the 'keyCode' field for non-printable characters
if (evt.keyCode in vkeys) {
let code = vkeys[evt.keyCode];
// macOS has messed up this code for some reason
@ -69,26 +68,6 @@ export function getKeycode(evt) {
export function getKey(evt) {
// Are we getting a proper key value?
if (evt.key !== undefined) {
// IE and Edge use some ancient version of the spec
// https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8860571/
switch (evt.key) {
case 'Spacebar': return ' ';
case 'Esc': return 'Escape';
case 'Scroll': return 'ScrollLock';
case 'Win': return 'Meta';
case 'Apps': return 'ContextMenu';
case 'Up': return 'ArrowUp';
case 'Left': return 'ArrowLeft';
case 'Right': return 'ArrowRight';
case 'Down': return 'ArrowDown';
case 'Del': return 'Delete';
case 'Divide': return '/';
case 'Multiply': return '*';
case 'Subtract': return '-';
case 'Add': return '+';
case 'Decimal': return evt.char;
}
// Mozilla isn't fully in sync with the spec yet
switch (evt.key) {
case 'OS': return 'Meta';
@ -110,18 +89,7 @@ export function getKey(evt) {
return 'Delete';
}
// IE and Edge need special handling, but for everyone else we
// can trust the value provided
if (!browser.isIE() && !browser.isEdge()) {
return evt.key;
}
// IE and Edge have broken handling of AltGraph so we can only
// trust them for non-printable characters (and unfortunately
// they also specify 'Unidentified' for some problem keys)
if ((evt.key.length !== 1) && (evt.key !== 'Unidentified')) {
return evt.key;
}
return evt.key;
}
// Try to deduce it based on the physical key

View File

@ -13,7 +13,6 @@ export default {
0x08: 'Backspace',
0x09: 'Tab',
0x0a: 'NumpadClear',
0x0c: 'Numpad5', // IE11 sends evt.keyCode: 12 when numlock is off
0x0d: 'Enter',
0x10: 'ShiftLeft',
0x11: 'ControlLeft',

View File

@ -25,7 +25,6 @@ import DES from "./des.js";
import KeyTable from "./input/keysym.js";
import XtScancode from "./input/xtscancodes.js";
import { encodings } from "./encodings.js";
import "./util/polyfill.js";
import RawDecoder from "./decoders/raw.js";
import CopyRectDecoder from "./decoders/copyrect.js";
@ -187,8 +186,6 @@ export default class RFB extends EventTargetMixin {
this._canvas.style.margin = 'auto';
// Some browsers add an outline on focus
this._canvas.style.outline = 'none';
// IE miscalculates width without this :(
this._canvas.style.flexShrink = '0';
this._canvas.width = 0;
this._canvas.height = 0;
this._canvas.tabIndex = -1;
@ -1263,17 +1260,6 @@ export default class RFB extends EventTargetMixin {
}
_negotiateSecurity() {
// Polyfill since IE and PhantomJS doesn't have
// TypedArray.includes()
function includes(item, array) {
for (let i = 0; i < array.length; i++) {
if (array[i] === item) {
return true;
}
}
return false;
}
if (this._rfbVersion >= 3.7) {
// Server sends supported list, client decides
const numTypes = this._sock.rQshift8();
@ -1290,15 +1276,15 @@ export default class RFB extends EventTargetMixin {
Log.Debug("Server security types: " + types);
// Look for each auth in preferred order
if (includes(1, types)) {
if (types.includes(1)) {
this._rfbAuthScheme = 1; // None
} else if (includes(22, types)) {
} else if (types.includes(22)) {
this._rfbAuthScheme = 22; // XVP
} else if (includes(16, types)) {
} else if (types.includes(16)) {
this._rfbAuthScheme = 16; // Tight
} else if (includes(2, types)) {
} else if (types.includes(2)) {
this._rfbAuthScheme = 2; // VNC Auth
} else if (includes(19, types)) {
} else if (types.includes(19)) {
this._rfbAuthScheme = 19; // VeNCrypt Auth
} else {
return this._fail("Unsupported security types (types: " + types + ")");
@ -2183,15 +2169,7 @@ export default class RFB extends EventTargetMixin {
return this._handleCursor();
case encodings.pseudoEncodingQEMUExtendedKeyEvent:
// Old Safari doesn't support creating keyboard events
try {
const keyboardEvent = document.createEvent("keyboardEvent");
if (keyboardEvent.code !== undefined) {
this._qemuExtKeyEventSupported = true;
}
} catch (err) {
// Do nothing
}
this._qemuExtKeyEventSupported = true;
return true;
case encodings.pseudoEncodingDesktopName:

View File

@ -45,15 +45,6 @@ try {
export const supportsCursorURIs = _supportsCursorURIs;
let _supportsImageMetadata = false;
try {
new ImageData(new Uint8ClampedArray(4), 1, 1);
_supportsImageMetadata = true;
} catch (ex) {
// ignore failure
}
export const supportsImageMetadata = _supportsImageMetadata;
let _hasScrollbarGutter = true;
try {
// Create invisible container
@ -106,14 +97,6 @@ export function isSafari() {
navigator.userAgent.indexOf('Chrome') === -1);
}
export function isIE() {
return navigator && !!(/trident/i).exec(navigator.userAgent);
}
export function isEdge() {
return navigator && !!(/edge/i).exec(navigator.userAgent);
}
export function isFirefox() {
return navigator && !!(/firefox/i).exec(navigator.userAgent);
}

View File

@ -43,9 +43,6 @@ export default class Cursor {
if (useFallback) {
document.body.appendChild(this._canvas);
// FIXME: These don't fire properly except for mouse
/// movement in IE. We want to also capture element
// movement, size changes, visibility, etc.
const options = { capture: true, passive: true };
this._target.addEventListener('mouseover', this._eventHandlers.mouseover, options);
this._target.addEventListener('mouseleave', this._eventHandlers.mouseleave, options);
@ -90,14 +87,7 @@ export default class Cursor {
this._canvas.width = w;
this._canvas.height = h;
let img;
try {
// IE doesn't support this
img = new ImageData(new Uint8ClampedArray(rgba), w, h);
} catch (ex) {
img = ctx.createImageData(w, h);
img.data.set(new Uint8ClampedArray(rgba));
}
let img = new ImageData(new Uint8ClampedArray(rgba), w, h);
ctx.clearRect(0, 0, w, h);
ctx.putImageData(img, 0, 0);

View File

@ -65,10 +65,6 @@ export function setCapture(target) {
target.setCapture();
document.captureElement = target;
// IE releases capture on 'click' events which might not trigger
target.addEventListener('mouseup', releaseCapture);
} else {
// Release any existing capture in case this method is
// called multiple times without coordination

View File

@ -1,61 +0,0 @@
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2020 The noVNC Authors
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
*/
/* Polyfills to provide new APIs in old browsers */
/* Object.assign() (taken from MDN) */
if (typeof Object.assign != 'function') {
// Must be writable: true, enumerable: false, configurable: true
Object.defineProperty(Object, "assign", {
value: function assign(target, varArgs) { // .length of function is 2
'use strict';
if (target == null) { // TypeError if undefined or null
throw new TypeError('Cannot convert undefined or null to object');
}
const to = Object(target);
for (let index = 1; index < arguments.length; index++) {
const nextSource = arguments[index];
if (nextSource != null) { // Skip over if undefined or null
for (let nextKey in nextSource) {
// Avoid bugs when hasOwnProperty is shadowed
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
},
writable: true,
configurable: true
});
}
/* CustomEvent constructor (taken from MDN) */
(() => {
function CustomEvent(event, params) {
params = params || { bubbles: false, cancelable: false, detail: undefined };
const evt = document.createEvent( 'CustomEvent' );
evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
return evt;
}
CustomEvent.prototype = window.Event.prototype;
if (typeof window.CustomEvent !== "function") {
window.CustomEvent = CustomEvent;
}
})();
/* Number.isInteger() (taken from MDN) */
Number.isInteger = Number.isInteger || function isInteger(value) {
return typeof value === 'number' &&
isFinite(value) &&
Math.floor(value) === value;
};

View File

@ -17,9 +17,6 @@ import * as Log from './util/logging.js';
// this has performance issues in some versions Chromium, and
// doesn't gain a tremendous amount of performance increase in Firefox
// at the moment. It may be valuable to turn it on in the future.
// Also copyWithin() for TypedArrays is not supported in IE 11 or
// Safari 13 (at the moment we want to support Safari 11).
const ENABLE_COPYWITHIN = false;
const MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
export default class Websock {
@ -256,11 +253,7 @@ export default class Websock {
this._rQ = new Uint8Array(this._rQbufferSize);
this._rQ.set(new Uint8Array(oldRQbuffer, this._rQi, this._rQlen - this._rQi));
} else {
if (ENABLE_COPYWITHIN) {
this._rQ.copyWithin(0, this._rQi, this._rQlen);
} else {
this._rQ.set(new Uint8Array(this._rQ.buffer, this._rQi, this._rQlen - this._rQi));
}
this._rQ.copyWithin(0, this._rQi, this._rQlen);
}
this._rQlen = this._rQlen - this._rQi;

View File

@ -31,16 +31,13 @@ module.exports = (config) => {
// list of files / patterns to load in the browser (loaded in order)
files: [
{ pattern: 'app/localization.js', included: false },
{ pattern: 'app/webutil.js', included: false },
{ pattern: 'core/**/*.js', included: false },
{ pattern: 'vendor/pako/**/*.js', included: false },
{ pattern: 'vendor/browser-es-module-loader/dist/*.js*', included: false },
{ pattern: 'tests/test.*.js', included: false },
{ pattern: 'tests/fake.*.js', included: false },
{ pattern: 'tests/assertions.js', included: false },
'vendor/promise.js',
'tests/karma-test-main.js',
{ pattern: 'app/localization.js', included: false, type: 'module' },
{ pattern: 'app/webutil.js', included: false, type: 'module' },
{ pattern: 'core/**/*.js', included: false, type: 'module' },
{ pattern: 'vendor/pako/**/*.js', included: false, type: 'module' },
{ pattern: 'tests/test.*.js', type: 'module' },
{ pattern: 'tests/fake.*.js', included: false, type: 'module' },
{ pattern: 'tests/assertions.js', type: 'module' },
],
client: {

View File

@ -21,7 +21,7 @@
"scripts": {
"lint": "eslint app core po/po2js po/xgettext-html tests utils",
"test": "karma start karma.conf.js",
"prepublish": "node ./utils/use_require.js --as commonjs --clean"
"prepublish": "node ./utils/use_require.js --clean"
},
"repository": {
"type": "git",
@ -42,10 +42,7 @@
"devDependencies": {
"@babel/core": "*",
"@babel/plugin-syntax-dynamic-import": "*",
"@babel/plugin-transform-modules-amd": "*",
"@babel/plugin-transform-modules-commonjs": "*",
"@babel/plugin-transform-modules-systemjs": "*",
"@babel/plugin-transform-modules-umd": "*",
"@babel/preset-env": "*",
"@babel/cli": "*",
"babel-plugin-import-redirect": "*",

View File

@ -6,10 +6,8 @@ chai.use(function (_chai, utils) {
_chai.Assertion.addMethod('displayed', function (targetData, cmp=_equal) {
const obj = this._obj;
const ctx = obj._target.getContext('2d');
const dataCl = ctx.getImageData(0, 0, obj._target.width, obj._target.height).data;
// NB(directxman12): PhantomJS 1.x doesn't implement Uint8ClampedArray, so work around that
const data = new Uint8Array(dataCl);
const len = dataCl.length;
const data = ctx.getImageData(0, 0, obj._target.width, obj._target.height).data;
const len = data.length;
new chai.Assertion(len).to.be.equal(targetData.length, "unexpected display size");
let same = true;
for (let i = 0; i < len; i++) {

View File

@ -1,17 +1,5 @@
import Base64 from '../core/base64.js';
// PhantomJS can't create Event objects directly, so we need to use this
function makeEvent(name, props) {
const evt = document.createEvent('Event');
evt.initEvent(name, true, true);
if (props) {
for (let prop in props) {
evt[prop] = props[prop];
}
}
return evt;
}
export default class FakeWebSocket {
constructor(uri, protocols) {
this.url = uri;
@ -35,7 +23,7 @@ export default class FakeWebSocket {
close(code, reason) {
this.readyState = FakeWebSocket.CLOSED;
if (this.onclose) {
this.onclose(makeEvent("close", { 'code': code, 'reason': reason, 'wasClean': true }));
this.onclose(new CloseEvent("close", { 'code': code, 'reason': reason, 'wasClean': true }));
}
}
@ -58,7 +46,7 @@ export default class FakeWebSocket {
_open() {
this.readyState = FakeWebSocket.OPEN;
if (this.onopen) {
this.onopen(makeEvent('open'));
this.onopen(new Event('open'));
}
}
@ -67,7 +55,7 @@ export default class FakeWebSocket {
// neatly packaged
for (let i = 0;i < data.length;i++) {
let buf = data.subarray(i, i+1);
this.onmessage(makeEvent("message", { 'data': buf }));
this.onmessage(new MessageEvent("message", { 'data': buf }));
}
}
}

View File

@ -1,48 +0,0 @@
const TEST_REGEXP = /test\..*\.js/;
const allTestFiles = [];
const extraFiles = ['/base/tests/assertions.js'];
Object.keys(window.__karma__.files).forEach(function (file) {
if (TEST_REGEXP.test(file)) {
// TODO: normalize?
allTestFiles.push(file);
}
});
// Stub out mocha's start function so we can run it once we're done loading
mocha.origRun = mocha.run;
mocha.run = function () {};
let script;
// Script to import all our tests
script = document.createElement("script");
script.type = "module";
script.text = "";
let allModules = allTestFiles.concat(extraFiles);
allModules.forEach(function (file) {
script.text += "import \"" + file + "\";\n";
});
script.text += "\nmocha.origRun();\n";
document.body.appendChild(script);
// Fallback code for browsers that don't support modules (IE)
script = document.createElement("script");
script.type = "module";
script.text = "window._noVNC_has_module_support = true;\n";
document.body.appendChild(script);
function fallback() {
if (!window._noVNC_has_module_support) {
/* eslint-disable no-console */
if (console) {
console.log("No module support detected. Loading fallback...");
}
/* eslint-enable no-console */
let loader = document.createElement("script");
loader.src = "base/vendor/browser-es-module-loader/dist/browser-es-module-loader.js";
document.body.appendChild(loader);
}
}
setTimeout(fallback, 500);

View File

@ -4,28 +4,27 @@ import Base64 from '../core/base64.js';
import Display from '../core/display.js';
describe('Display/Canvas Helper', function () {
const checkedData = new Uint8Array([
const checkedData = new Uint8ClampedArray([
0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255,
0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255
]);
const basicData = new Uint8Array([0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0xff, 0xff, 0xff, 255]);
const basicData = new Uint8ClampedArray([0xff, 0x00, 0x00, 255, 0x00, 0xff, 0x00, 255, 0x00, 0x00, 0xff, 255, 0xff, 0xff, 0xff, 255]);
function makeImageCanvas(inputData) {
function makeImageCanvas(inputData, width, height) {
const canvas = document.createElement('canvas');
canvas.width = 4;
canvas.height = 4;
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
const data = ctx.createImageData(4, 4);
for (let i = 0; i < checkedData.length; i++) { data.data[i] = inputData[i]; }
const data = new ImageData(inputData, width, height);
ctx.putImageData(data, 0, 0);
return canvas;
}
function makeImagePng(inputData) {
const canvas = makeImageCanvas(inputData);
function makeImagePng(inputData, width, height) {
const canvas = makeImageCanvas(inputData, width, height);
const url = canvas.toDataURL();
const data = url.split(",")[1];
return Base64.decode(data);
@ -44,7 +43,7 @@ describe('Display/Canvas Helper', function () {
it('should take viewport location into consideration when drawing images', function () {
display.resize(4, 4);
display.viewportChangeSize(2, 2);
display.drawImage(makeImageCanvas(basicData), 1, 1);
display.drawImage(makeImageCanvas(basicData, 4, 1), 1, 1);
display.flip();
const expected = new Uint8Array(16);
@ -300,7 +299,7 @@ describe('Display/Canvas Helper', function () {
});
it('should support drawing images via #imageRect', function (done) {
display.imageRect(0, 0, 4, 4, "image/png", makeImagePng(checkedData));
display.imageRect(0, 0, 4, 4, "image/png", makeImagePng(checkedData, 4, 4));
display.flip();
display.onflush = () => {
expect(display).to.have.displayed(checkedData);
@ -316,7 +315,7 @@ describe('Display/Canvas Helper', function () {
});
it('should support drawing an image object via #drawImage', function () {
const img = makeImageCanvas(checkedData);
const img = makeImageCanvas(checkedData, 4, 4);
display.drawImage(img, 0, 0);
display.flip();
expect(display).to.have.displayed(checkedData);
@ -361,27 +360,6 @@ describe('Display/Canvas Helper', function () {
expect(img.addEventListener).to.have.been.calledOnce;
});
it('should wait if an image is incorrectly loaded', function () {
const img = { complete: true, width: 0, height: 0, addEventListener: sinon.spy() };
display._renderQ = [{ type: 'img', x: 3, y: 4, width: 4, height: 4, img: img },
{ type: 'fill', x: 1, y: 2, width: 3, height: 4, color: 5 }];
display.drawImage = sinon.spy();
display.fillRect = sinon.spy();
display._scanRenderQ();
expect(display.drawImage).to.not.have.been.called;
expect(display.fillRect).to.not.have.been.called;
expect(img.addEventListener).to.have.been.calledOnce;
display._renderQ[0].img.complete = true;
display._renderQ[0].img.width = 4;
display._renderQ[0].img.height = 4;
display._scanRenderQ();
expect(display.drawImage).to.have.been.calledOnce;
expect(display.fillRect).to.have.been.calledOnce;
expect(img.addEventListener).to.have.been.calledOnce;
});
it('should call callback when queue is flushed', function () {
display.onflush = sinon.spy();
display.fillRect(0, 0, 4, 4, [0, 0xff, 0]);

View File

@ -3,7 +3,6 @@ const expect = chai.expect;
import EventTargetMixin from '../core/util/eventtarget.js';
import GestureHandler from '../core/input/gesturehandler.js';
import * as browser from '../core/util/browser.js';
class DummyTarget extends EventTargetMixin {
}
@ -23,12 +22,6 @@ describe('Gesture handler', function () {
});
beforeEach(function () {
// Touch events and gestures are not supported on IE
if (browser.isIE()) {
this.skip();
return;
}
target = new DummyTarget();
gestures = sinon.spy();
target.addEventListener('gesturestart', gestures);

View File

@ -2,7 +2,6 @@
import keysyms from '../core/input/keysymdef.js';
import * as KeyboardUtil from "../core/input/util.js";
import * as browser from '../core/util/browser.js';
describe('Helpers', function () {
"use strict";
@ -70,11 +69,6 @@ describe('Helpers', function () {
// environments, so we need to redefine it whilst running these
// tests.
origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
if (origNavigator === undefined) {
// Object.getOwnPropertyDescriptor() doesn't work
// properly in any version of IE
this.skip();
}
Object.defineProperty(window, "navigator", {value: {}});
if (window.navigator.platform !== undefined) {
@ -102,14 +96,10 @@ describe('Helpers', function () {
describe('getKey', function () {
it('should prefer key', function () {
if (browser.isIE() || browser.isEdge()) this.skip();
expect(KeyboardUtil.getKey({key: 'a', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.be.equal('a');
});
it('should map legacy values', function () {
expect(KeyboardUtil.getKey({key: 'Spacebar'})).to.be.equal(' ');
expect(KeyboardUtil.getKey({key: 'Left'})).to.be.equal('ArrowLeft');
expect(KeyboardUtil.getKey({key: 'OS'})).to.be.equal('Meta');
expect(KeyboardUtil.getKey({key: 'Win'})).to.be.equal('Meta');
expect(KeyboardUtil.getKey({key: 'UIKeyInputLeftArrow'})).to.be.equal('ArrowLeft');
});
it('should handle broken Delete', function () {
@ -130,60 +120,6 @@ describe('Helpers', function () {
it('should return Unidentified when it cannot map the key', function () {
expect(KeyboardUtil.getKey({keycode: 0x42})).to.be.equal('Unidentified');
});
describe('Broken key AltGraph on IE/Edge', function () {
let origNavigator;
beforeEach(function () {
// window.navigator is a protected read-only property in many
// environments, so we need to redefine it whilst running these
// tests.
origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
if (origNavigator === undefined) {
// Object.getOwnPropertyDescriptor() doesn't work
// properly in any version of IE
this.skip();
}
Object.defineProperty(window, "navigator", {value: {}});
if (window.navigator.platform !== undefined) {
// Object.defineProperty() doesn't work properly in old
// versions of Chrome
this.skip();
}
});
afterEach(function () {
if (origNavigator !== undefined) {
Object.defineProperty(window, "navigator", origNavigator);
}
});
it('should ignore printable character key on IE', function () {
window.navigator.userAgent = "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko";
expect(KeyboardUtil.getKey({key: 'a'})).to.be.equal('Unidentified');
});
it('should ignore printable character key on Edge', function () {
window.navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393";
expect(KeyboardUtil.getKey({key: 'a'})).to.be.equal('Unidentified');
});
it('should allow non-printable character key on IE', function () {
window.navigator.userAgent = "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko";
expect(KeyboardUtil.getKey({key: 'Shift'})).to.be.equal('Shift');
});
it('should allow non-printable character key on Edge', function () {
window.navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393";
expect(KeyboardUtil.getKey({key: 'Shift'})).to.be.equal('Shift');
});
it('should allow printable character key with charCode on IE', function () {
window.navigator.userAgent = "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko";
expect(KeyboardUtil.getKey({key: 'a', charCode: 0x61})).to.be.equal('a');
expect(KeyboardUtil.getKey({key: 'Unidentified', charCode: 0x61})).to.be.equal('a');
});
it('should allow printable character key with charCode on Edge', function () {
window.navigator.userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.79 Safari/537.36 Edge/14.14393";
expect(KeyboardUtil.getKey({key: 'a', charCode: 0x61})).to.be.equal('a');
expect(KeyboardUtil.getKey({key: 'Unidentified', charCode: 0x61})).to.be.equal('a');
});
});
});
describe('getKeysym', function () {
@ -236,7 +172,6 @@ describe('Helpers', function () {
describe('Numpad', function () {
it('should handle Numpad numbers', function () {
if (browser.isIE() || browser.isEdge()) this.skip();
expect(KeyboardUtil.getKeysym({code: 'Digit5', key: '5', location: 0})).to.be.equal(0x0035);
expect(KeyboardUtil.getKeysym({code: 'Numpad5', key: '5', location: 3})).to.be.equal(0xFFB5);
});
@ -247,7 +182,6 @@ describe('Helpers', function () {
expect(KeyboardUtil.getKeysym({code: 'NumpadDecimal', key: 'Delete', location: 3})).to.be.equal(0xFF9F);
});
it('should handle Numpad Decimal key', function () {
if (browser.isIE() || browser.isEdge()) this.skip();
expect(KeyboardUtil.getKeysym({code: 'NumpadDecimal', key: '.', location: 3})).to.be.equal(0xFFAE);
expect(KeyboardUtil.getKeysym({code: 'NumpadDecimal', key: ',', location: 3})).to.be.equal(0xFFAC);
});

View File

@ -1,7 +1,6 @@
const expect = chai.expect;
import Keyboard from '../core/input/keyboard.js';
import * as browser from '../core/util/browser.js';
describe('Key Event Handling', function () {
"use strict";
@ -20,7 +19,6 @@ describe('Key Event Handling', function () {
describe('Decode Keyboard Events', function () {
it('should decode keydown events', function (done) {
if (browser.isIE() || browser.isEdge()) this.skip();
const kbd = new Keyboard(document);
kbd.onkeyevent = (keysym, code, down) => {
expect(keysym).to.be.equal(0x61);
@ -31,7 +29,6 @@ describe('Key Event Handling', function () {
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
});
it('should decode keyup events', function (done) {
if (browser.isIE() || browser.isEdge()) this.skip();
let calls = 0;
const kbd = new Keyboard(document);
kbd.onkeyevent = (keysym, code, down) => {
@ -45,118 +42,10 @@ describe('Key Event Handling', function () {
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', key: 'a'}));
kbd._handleKeyUp(keyevent('keyup', {code: 'KeyA', key: 'a'}));
});
describe('Legacy keypress Events', function () {
it('should wait for keypress when needed', function () {
const kbd = new Keyboard(document);
kbd.onkeyevent = sinon.spy();
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
expect(kbd.onkeyevent).to.not.have.been.called;
});
it('should decode keypress events', function (done) {
const kbd = new Keyboard(document);
kbd.onkeyevent = (keysym, code, down) => {
expect(keysym).to.be.equal(0x61);
expect(code).to.be.equal('KeyA');
expect(down).to.be.equal(true);
done();
};
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
kbd._handleKeyPress(keyevent('keypress', {code: 'KeyA', charCode: 0x61}));
});
it('should ignore keypress with different code', function () {
const kbd = new Keyboard(document);
kbd.onkeyevent = sinon.spy();
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
kbd._handleKeyPress(keyevent('keypress', {code: 'KeyB', charCode: 0x61}));
expect(kbd.onkeyevent).to.not.have.been.called;
});
it('should handle keypress with missing code', function (done) {
const kbd = new Keyboard(document);
kbd.onkeyevent = (keysym, code, down) => {
expect(keysym).to.be.equal(0x61);
expect(code).to.be.equal('KeyA');
expect(down).to.be.equal(true);
done();
};
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41}));
kbd._handleKeyPress(keyevent('keypress', {charCode: 0x61}));
});
it('should guess key if no keypress and numeric key', function (done) {
const kbd = new Keyboard(document);
kbd.onkeyevent = (keysym, code, down) => {
expect(keysym).to.be.equal(0x32);
expect(code).to.be.equal('Digit2');
expect(down).to.be.equal(true);
done();
};
kbd._handleKeyDown(keyevent('keydown', {code: 'Digit2', keyCode: 0x32}));
});
it('should guess key if no keypress and alpha key', function (done) {
const kbd = new Keyboard(document);
kbd.onkeyevent = (keysym, code, down) => {
expect(keysym).to.be.equal(0x61);
expect(code).to.be.equal('KeyA');
expect(down).to.be.equal(true);
done();
};
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41, shiftKey: false}));
});
it('should guess key if no keypress and alpha key (with shift)', function (done) {
const kbd = new Keyboard(document);
kbd.onkeyevent = (keysym, code, down) => {
expect(keysym).to.be.equal(0x41);
expect(code).to.be.equal('KeyA');
expect(down).to.be.equal(true);
done();
};
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x41, shiftKey: true}));
});
it('should not guess key if no keypress and unknown key', function (done) {
const kbd = new Keyboard(document);
kbd.onkeyevent = (keysym, code, down) => {
expect(keysym).to.be.equal(0);
expect(code).to.be.equal('KeyA');
expect(down).to.be.equal(true);
done();
};
kbd._handleKeyDown(keyevent('keydown', {code: 'KeyA', keyCode: 0x09}));
});
});
describe('suppress the right events at the right time', function () {
beforeEach(function () {
if (browser.isIE() || browser.isEdge()) this.skip();
});
it('should suppress anything with a valid key', function () {
const kbd = new Keyboard(document, {});
const evt1 = keyevent('keydown', {code: 'KeyA', key: 'a'});
kbd._handleKeyDown(evt1);
expect(evt1.preventDefault).to.have.been.called;
const evt2 = keyevent('keyup', {code: 'KeyA', key: 'a'});
kbd._handleKeyUp(evt2);
expect(evt2.preventDefault).to.have.been.called;
});
it('should not suppress keys without key', function () {
const kbd = new Keyboard(document, {});
const evt = keyevent('keydown', {code: 'KeyA', keyCode: 0x41});
kbd._handleKeyDown(evt);
expect(evt.preventDefault).to.not.have.been.called;
});
it('should suppress the following keypress event', function () {
const kbd = new Keyboard(document, {});
const evt1 = keyevent('keydown', {code: 'KeyA', keyCode: 0x41});
kbd._handleKeyDown(evt1);
const evt2 = keyevent('keypress', {code: 'KeyA', charCode: 0x41});
kbd._handleKeyPress(evt2);
expect(evt2.preventDefault).to.have.been.called;
});
});
});
describe('Fake keyup', function () {
it('should fake keyup events for virtual keyboards', function (done) {
if (browser.isIE() || browser.isEdge()) this.skip();
let count = 0;
const kbd = new Keyboard(document);
kbd.onkeyevent = (keysym, code, down) => {
@ -178,9 +67,6 @@ describe('Key Event Handling', function () {
});
describe('Track Key State', function () {
beforeEach(function () {
if (browser.isIE() || browser.isEdge()) this.skip();
});
it('should send release using the same keysym as the press', function (done) {
const kbd = new Keyboard(document);
kbd.onkeyevent = (keysym, code, down) => {
@ -256,11 +142,6 @@ describe('Key Event Handling', function () {
// environments, so we need to redefine it whilst running these
// tests.
origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
if (origNavigator === undefined) {
// Object.getOwnPropertyDescriptor() doesn't work
// properly in any version of IE
this.skip();
}
Object.defineProperty(window, "navigator", {value: {}});
if (window.navigator.platform !== undefined) {
@ -323,11 +204,6 @@ describe('Key Event Handling', function () {
// environments, so we need to redefine it whilst running these
// tests.
origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
if (origNavigator === undefined) {
// Object.getOwnPropertyDescriptor() doesn't work
// properly in any version of IE
this.skip();
}
Object.defineProperty(window, "navigator", {value: {}});
if (window.navigator.platform !== undefined) {
@ -399,11 +275,6 @@ describe('Key Event Handling', function () {
// environments, so we need to redefine it whilst running these
// tests.
origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
if (origNavigator === undefined) {
// Object.getOwnPropertyDescriptor() doesn't work
// properly in any version of IE
this.skip();
}
Object.defineProperty(window, "navigator", {value: {}});
if (window.navigator.platform !== undefined) {
@ -549,11 +420,6 @@ describe('Key Event Handling', function () {
// environments, so we need to redefine it whilst running these
// tests.
origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
if (origNavigator === undefined) {
// Object.getOwnPropertyDescriptor() doesn't work
// properly in any version of IE
this.skip();
}
Object.defineProperty(window, "navigator", {value: {}});
if (window.navigator.platform !== undefined) {

View File

@ -11,11 +11,6 @@ describe('Localization', function () {
// environments, so we need to redefine it whilst running these
// tests.
origNavigator = Object.getOwnPropertyDescriptor(window, "navigator");
if (origNavigator === undefined) {
// Object.getOwnPropertyDescriptor() doesn't work
// properly in any version of IE
this.skip();
}
Object.defineProperty(window, "navigator", {value: {}});
if (window.navigator.languages !== undefined) {

View File

@ -8,26 +8,9 @@ import { encodings } from '../core/encodings.js';
import { toUnsigned32bit } from '../core/util/int.js';
import { encodeUTF8 } from '../core/util/strings.js';
import KeyTable from '../core/input/keysym.js';
import * as browser from '../core/util/browser.js';
import FakeWebSocket from './fake.websocket.js';
/* UIEvent constructor polyfill for IE */
(() => {
if (typeof window.UIEvent === "function") return;
function UIEvent( event, params ) {
params = params || { bubbles: false, cancelable: false, view: window, detail: undefined };
const evt = document.createEvent( 'UIEvent' );
evt.initUIEvent( event, params.bubbles, params.cancelable, params.view, params.detail );
return evt;
}
UIEvent.prototype = window.UIEvent.prototype;
window.UIEvent = UIEvent;
})();
function push8(arr, num) {
"use strict";
arr.push(num & 0xFF);
@ -2277,12 +2260,7 @@ describe('Remote Frame Buffer Protocol Client', function () {
});
it('should be able to handle large Provide messages', function () {
// repeat() is not supported in IE so a loop is needed instead
let expectedData = "hello";
for (let i = 1; i <= 100000; i++) {
expectedData += "hello";
}
let expectedData = "hello".repeat(100000);
let data = [3, 0, 0, 0];
const flags = [0x10, 0x00, 0x00, 0x01];
@ -2528,23 +2506,11 @@ describe('Remote Frame Buffer Protocol Client', function () {
let pos = elementToClient(x, y);
let ev;
try {
ev = new MouseEvent('mousemove',
{ 'screenX': pos.x + window.screenX,
'screenY': pos.y + window.screenY,
'clientX': pos.x,
'clientY': pos.y });
} catch (e) {
ev = document.createEvent('MouseEvent');
ev.initMouseEvent('mousemove',
true, true, window, 0,
pos.x + window.screenX,
pos.y + window.screenY,
pos.x, pos.y,
false, false, false, false,
0, null);
}
ev = new MouseEvent('mousemove',
{ 'screenX': pos.x + window.screenX,
'screenY': pos.y + window.screenY,
'clientX': pos.x,
'clientY': pos.y });
client._canvas.dispatchEvent(ev);
}
@ -2552,25 +2518,13 @@ describe('Remote Frame Buffer Protocol Client', function () {
let pos = elementToClient(x, y);
let ev;
try {
ev = new MouseEvent(down ? 'mousedown' : 'mouseup',
{ 'screenX': pos.x + window.screenX,
'screenY': pos.y + window.screenY,
'clientX': pos.x,
'clientY': pos.y,
'button': button,
'buttons': 1 << button });
} catch (e) {
ev = document.createEvent('MouseEvent');
ev.initMouseEvent(down ? 'mousedown' : 'mouseup',
true, true, window, 0,
pos.x + window.screenX,
pos.y + window.screenY,
pos.x, pos.y,
false, false, false, false,
button, null);
}
ev = new MouseEvent(down ? 'mousedown' : 'mouseup',
{ 'screenX': pos.x + window.screenX,
'screenY': pos.y + window.screenY,
'clientX': pos.x,
'clientY': pos.y,
'button': button,
'buttons': 1 << button });
client._canvas.dispatchEvent(ev);
}
@ -2805,25 +2759,14 @@ describe('Remote Frame Buffer Protocol Client', function () {
let pos = elementToClient(x, y);
let ev;
try {
ev = new WheelEvent('wheel',
{ 'screenX': pos.x + window.screenX,
'screenY': pos.y + window.screenY,
'clientX': pos.x,
'clientY': pos.y,
'deltaX': dx,
'deltaY': dy,
'deltaMode': mode });
} catch (e) {
ev = document.createEvent('WheelEvent');
ev.initWheelEvent('wheel', true, true, window, 0,
pos.x + window.screenX,
pos.y + window.screenY,
pos.x, pos.y,
0, null, "",
dx, dy, 0, mode);
}
ev = new WheelEvent('wheel',
{ 'screenX': pos.x + window.screenX,
'screenY': pos.y + window.screenY,
'clientX': pos.x,
'clientY': pos.y,
'deltaX': dx,
'deltaY': dy,
'deltaMode': mode });
client._canvas.dispatchEvent(ev);
}
@ -2938,14 +2881,6 @@ describe('Remote Frame Buffer Protocol Client', function () {
});
describe('Gesture event handlers', function () {
beforeEach(function () {
// Touch events and gestures are not supported on IE
if (browser.isIE()) {
this.skip();
return;
}
});
function gestureStart(gestureType, x, y,
magnitudeX = 0, magnitudeY = 0) {
let pos = elementToClient(x, y);

View File

@ -22,11 +22,6 @@ describe('WebUtil', function () {
let origLocalStorage;
beforeEach(function () {
origLocalStorage = Object.getOwnPropertyDescriptor(window, "localStorage");
if (origLocalStorage === undefined) {
// Object.getOwnPropertyDescriptor() doesn't work
// properly in any version of IE
this.skip();
}
Object.defineProperty(window, "localStorage", {value: {}});
if (window.localStorage.setItem !== undefined) {

View File

@ -2,21 +2,6 @@
<html lang="en">
<head>
<title>VNC Playback</title>
<!-- promise polyfills promises for IE11 -->
<script src="../vendor/promise.js"></script>
<!-- ES2015/ES6 modules polyfill -->
<script type="module">
window._noVNC_has_module_support = true;
</script>
<script>
window.addEventListener("load", function() {
if (window._noVNC_has_module_support) return;
var loader = document.createElement("script");
loader.src = "../vendor/browser-es-module-loader/dist/browser-es-module-loader.js";
document.head.appendChild(loader);
});
</script>
<!-- actual script modules -->
<script type="module" src="./playback-ui.js"></script>
</head>
<body>
@ -37,7 +22,5 @@
<div id="VNC_screen">
<div id="VNC_status">Loading</div>
</div>
<script type="module" src="./playback-ui.js"></script>
</body>
</html>

View File

@ -6,13 +6,8 @@ const fs = require('fs');
const fse = require('fs-extra');
const babel = require('@babel/core');
const SUPPORTED_FORMATS = new Set(['amd', 'commonjs', 'systemjs', 'umd']);
program
.option('--as [format]', `output files using various import formats instead of ES6 import and export. Supports ${Array.from(SUPPORTED_FORMATS)}.`)
.option('-m, --with-source-maps [type]', 'output source maps when not generating a bundled app (type may be empty for external source maps, inline for inline source maps, or both) ')
.option('--with-app', 'process app files as well as core files')
.option('--only-legacy', 'only output legacy files (no ES6 modules) for the app')
.option('--clean', 'clear the lib folder before building')
.parse(process.argv);
@ -20,30 +15,10 @@ program
const paths = {
main: path.resolve(__dirname, '..'),
core: path.resolve(__dirname, '..', 'core'),
app: path.resolve(__dirname, '..', 'app'),
vendor: path.resolve(__dirname, '..', 'vendor'),
outDirBase: path.resolve(__dirname, '..', 'build'),
libDirBase: path.resolve(__dirname, '..', 'lib'),
};
const noCopyFiles = new Set([
// skip these -- they don't belong in the processed application
path.join(paths.vendor, 'sinon.js'),
path.join(paths.vendor, 'browser-es-module-loader'),
path.join(paths.app, 'images', 'icons', 'Makefile'),
]);
const onlyLegacyScripts = new Set([
path.join(paths.vendor, 'promise.js'),
]);
const noTransformFiles = new Set([
// don't transform this -- we want it imported as-is to properly catch loading errors
path.join(paths.app, 'error-handler.js'),
]);
noCopyFiles.forEach(file => noTransformFiles.add(file));
// util.promisify requires Node.js 8.x, so we have our own
function promisify(original) {
return function promiseWrap() {
@ -57,16 +32,12 @@ function promisify(original) {
};
}
const readFile = promisify(fs.readFile);
const writeFile = promisify(fs.writeFile);
const readdir = promisify(fs.readdir);
const lstat = promisify(fs.lstat);
const copy = promisify(fse.copy);
const unlink = promisify(fse.unlink);
const ensureDir = promisify(fse.ensureDir);
const rmdir = promisify(fse.rmdir);
const babelTransformFile = promisify(babel.transformFile);
@ -87,157 +58,57 @@ function walkDir(basePath, cb, filter) {
});
}
function transformHtml(legacyScripts, onlyLegacy) {
// write out the modified vnc.html file that works with the bundle
const srcHtmlPath = path.resolve(__dirname, '..', 'vnc.html');
const outHtmlPath = path.resolve(paths.outDirBase, 'vnc.html');
return readFile(srcHtmlPath)
.then((contentsRaw) => {
let contents = contentsRaw.toString();
const startMarker = '<!-- begin scripts -->\n';
const endMarker = '<!-- end scripts -->';
const startInd = contents.indexOf(startMarker) + startMarker.length;
const endInd = contents.indexOf(endMarker, startInd);
let newScript = '';
if (onlyLegacy) {
// Only legacy version, so include things directly
for (let i = 0;i < legacyScripts.length;i++) {
newScript += ` <script src="${legacyScripts[i]}"></script>\n`;
}
} else {
// Otherwise include both modules and legacy fallbacks
newScript += ' <script type="module" crossorigin="anonymous" src="app/ui.js"></script>\n';
for (let i = 0;i < legacyScripts.length;i++) {
newScript += ` <script nomodule src="${legacyScripts[i]}"></script>\n`;
}
}
contents = contents.slice(0, startInd) + `${newScript}\n` + contents.slice(endInd);
return contents;
})
.then((contents) => {
console.log(`Writing ${outHtmlPath}`);
return writeFile(outHtmlPath, contents);
});
}
function makeLibFiles(importFormat, sourceMaps, withAppDir, onlyLegacy) {
if (!importFormat) {
throw new Error("you must specify an import format to generate compiled noVNC libraries");
} else if (!SUPPORTED_FORMATS.has(importFormat)) {
throw new Error(`unsupported output format "${importFormat}" for import/export -- only ${Array.from(SUPPORTED_FORMATS)} are supported`);
}
function makeLibFiles(sourceMaps) {
// NB: we need to make a copy of babelOpts, since babel sets some defaults on it
const babelOpts = () => ({
plugins: [],
presets: [
[ '@babel/preset-env',
{ targets: 'ie >= 11',
modules: importFormat } ]
{ modules: 'commonjs' } ]
],
ast: false,
sourceMaps: sourceMaps,
});
// No point in duplicate files without the app, so force only converted files
if (!withAppDir) {
onlyLegacy = true;
}
let inPath;
let outPathBase;
if (withAppDir) {
outPathBase = paths.outDirBase;
inPath = paths.main;
} else {
outPathBase = paths.libDirBase;
}
const legacyPathBase = onlyLegacy ? outPathBase : path.join(outPathBase, 'legacy');
fse.ensureDirSync(outPathBase);
const helpers = require('./use_require_helpers');
const helper = helpers[importFormat];
fse.ensureDirSync(paths.libDirBase);
const outFiles = [];
const legacyFiles = [];
const handleDir = (jsOnly, vendorRewrite, inPathBase, filename) => Promise.resolve()
const handleDir = (vendorRewrite, inPathBase, filename) => Promise.resolve()
.then(() => {
const outPath = path.join(outPathBase, path.relative(inPathBase, filename));
const legacyPath = path.join(legacyPathBase, path.relative(inPathBase, filename));
const outPath = path.join(paths.libDirBase, path.relative(inPathBase, filename));
if (path.extname(filename) !== '.js') {
if (!jsOnly) {
console.log(`Writing ${outPath}`);
return copy(filename, outPath);
}
return; // skip non-javascript files
}
if (noTransformFiles.has(filename)) {
return ensureDir(path.dirname(outPath))
.then(() => {
console.log(`Writing ${outPath}`);
return copy(filename, outPath);
});
}
if (onlyLegacyScripts.has(filename)) {
legacyFiles.push(legacyPath);
return ensureDir(path.dirname(legacyPath))
.then(() => {
console.log(`Writing ${legacyPath}`);
return copy(filename, legacyPath);
});
}
return Promise.resolve()
.then(() => {
if (onlyLegacy) {
return;
}
return ensureDir(path.dirname(outPath))
.then(() => {
console.log(`Writing ${outPath}`);
return copy(filename, outPath);
});
})
.then(() => ensureDir(path.dirname(legacyPath)))
.then(() => ensureDir(path.dirname(outPath)))
.then(() => {
const opts = babelOpts();
if (helper && helpers.optionsOverride) {
helper.optionsOverride(opts);
}
// Adjust for the fact that we move the core files relative
// to the vendor directory
if (vendorRewrite) {
opts.plugins.push(["import-redirect",
{"root": legacyPathBase,
{"root": paths.libDirBase,
"redirect": { "vendor/(.+)": "./vendor/$1"}}]);
}
return babelTransformFile(filename, opts)
.then((res) => {
console.log(`Writing ${legacyPath}`);
console.log(`Writing ${outPath}`);
const {map} = res;
let {code} = res;
if (sourceMaps === true) {
// append URL for external source map
code += `\n//# sourceMappingURL=${path.basename(legacyPath)}.map\n`;
code += `\n//# sourceMappingURL=${path.basename(outPath)}.map\n`;
}
outFiles.push(`${legacyPath}`);
return writeFile(legacyPath, code)
outFiles.push(`${outPath}`);
return writeFile(outPath, code)
.then(() => {
if (sourceMaps === true || sourceMaps === 'both') {
console.log(` and ${legacyPath}.map`);
outFiles.push(`${legacyPath}.map`);
return writeFile(`${legacyPath}.map`, JSON.stringify(map));
console.log(` and ${outPath}.map`);
outFiles.push(`${outPath}.map`);
return writeFile(`${outPath}.map`, JSON.stringify(map));
}
});
});
@ -246,64 +117,12 @@ function makeLibFiles(importFormat, sourceMaps, withAppDir, onlyLegacy) {
Promise.resolve()
.then(() => {
const handler = handleDir.bind(null, true, false, inPath || paths.main);
const filter = (filename, stats) => !noCopyFiles.has(filename);
return walkDir(paths.vendor, handler, filter);
const handler = handleDir.bind(null, false, paths.main);
return walkDir(paths.vendor, handler);
})
.then(() => {
const handler = handleDir.bind(null, true, !inPath, inPath || paths.core);
const filter = (filename, stats) => !noCopyFiles.has(filename);
return walkDir(paths.core, handler, filter);
})
.then(() => {
if (!withAppDir) return;
const handler = handleDir.bind(null, false, false, inPath);
const filter = (filename, stats) => !noCopyFiles.has(filename);
return walkDir(paths.app, handler, filter);
})
.then(() => {
if (!withAppDir) return;
if (!helper || !helper.appWriter) {
throw new Error(`Unable to generate app for the ${importFormat} format!`);
}
const outAppPath = path.join(legacyPathBase, 'app.js');
console.log(`Writing ${outAppPath}`);
return helper.appWriter(outPathBase, legacyPathBase, outAppPath)
.then((extraScripts) => {
let legacyScripts = [];
legacyFiles.forEach((file) => {
let relFilePath = path.relative(outPathBase, file);
legacyScripts.push(relFilePath);
});
legacyScripts = legacyScripts.concat(extraScripts);
let relAppPath = path.relative(outPathBase, outAppPath);
legacyScripts.push(relAppPath);
transformHtml(legacyScripts, onlyLegacy);
})
.then(() => {
if (!helper.removeModules) return;
console.log(`Cleaning up temporary files...`);
return Promise.all(outFiles.map((filepath) => {
unlink(filepath)
.then(() => {
// Try to clean up any empty directories if
// this was the last file in there
const rmdirR = dir =>
rmdir(dir)
.then(() => rmdirR(path.dirname(dir)))
.catch(() => {
// Assume the error was ENOTEMPTY and ignore it
});
return rmdirR(path.dirname(filepath));
});
}));
});
const handler = handleDir.bind(null, true, paths.core);
return walkDir(paths.core, handler);
})
.catch((err) => {
console.error(`Failure converting modules: ${err}`);
@ -314,9 +133,6 @@ function makeLibFiles(importFormat, sourceMaps, withAppDir, onlyLegacy) {
if (program.clean) {
console.log(`Removing ${paths.libDirBase}`);
fse.removeSync(paths.libDirBase);
console.log(`Removing ${paths.outDirBase}`);
fse.removeSync(paths.outDirBase);
}
makeLibFiles(program.as, program.withSourceMaps, program.withApp, program.onlyLegacy);
makeLibFiles(program.withSourceMaps);

View File

@ -1,60 +0,0 @@
// writes helpers require for vnc.html (they should output app.js)
const fs = require('fs');
const path = require('path');
// util.promisify requires Node.js 8.x, so we have our own
function promisify(original) {
return function promiseWrap() {
const args = Array.prototype.slice.call(arguments);
return new Promise((resolve, reject) => {
original.apply(this, args.concat((err, value) => {
if (err) return reject(err);
resolve(value);
}));
});
};
}
const writeFile = promisify(fs.writeFile);
module.exports = {
'amd': {
appWriter: (baseOutPath, scriptBasePath, outPath) => {
// setup for requirejs
const uiPath = path.relative(baseOutPath,
path.join(scriptBasePath, 'app', 'ui'));
return writeFile(outPath, `requirejs(["${uiPath}"], function (ui) {});`)
.then(() => {
console.log(`Please place RequireJS in ${path.join(scriptBasePath, 'require.js')}`);
const requirePath = path.relative(baseOutPath,
path.join(scriptBasePath, 'require.js'));
return [ requirePath ];
});
},
},
'commonjs': {
appWriter: (baseOutPath, scriptBasePath, outPath) => {
const browserify = require('browserify');
const b = browserify(path.join(scriptBasePath, 'app/ui.js'), {});
return promisify(b.bundle).call(b)
.then(buf => writeFile(outPath, buf))
.then(() => []);
},
removeModules: true,
},
'systemjs': {
appWriter: (baseOutPath, scriptBasePath, outPath) => {
const uiPath = path.relative(baseOutPath,
path.join(scriptBasePath, 'app', 'ui.js'));
return writeFile(outPath, `SystemJS.import("${uiPath}");`)
.then(() => {
console.log(`Please place SystemJS in ${path.join(scriptBasePath, 'system-production.js')}`);
const systemjsPath = path.relative(baseOutPath,
path.join(scriptBasePath, 'system-production.js'));
return [ systemjsPath ];
});
},
},
'umd': {
},
};

View File

@ -1,15 +0,0 @@
Custom Browser ES Module Loader
===============================
This is a module loader using babel and the ES Module Loader polyfill.
It's based heavily on
https://github.com/ModuleLoader/browser-es-module-loader, but uses
WebWorkers to compile the modules in the background.
To generate, run `npx rollup -c` in this directory, and then run
`./genworker.js`.
LICENSE
-------
MIT

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,13 +0,0 @@
#!/usr/bin/env node
var fs = require("fs");
var browserify = require("browserify");
browserify("src/babel-worker.js")
.transform("babelify", {
presets: [ [ "@babel/preset-env", { targets: "ie >= 11" } ] ],
global: true,
ignore: [ "../../node_modules/core-js" ]
})
.bundle()
.pipe(fs.createWriteStream("dist/babel-worker.js"));

View File

@ -1,15 +0,0 @@
import nodeResolve from 'rollup-plugin-node-resolve';
export default {
input: 'src/browser-es-module-loader.js',
output: {
file: 'dist/browser-es-module-loader.js',
format: 'umd',
name: 'BrowserESModuleLoader',
sourcemap: true,
},
plugins: [
nodeResolve(),
],
};

View File

@ -1,23 +0,0 @@
// Polyfills needed for Babel to function
require("core-js");
var babelTransform = require('@babel/core').transform;
var babelTransformDynamicImport = require('@babel/plugin-syntax-dynamic-import');
var babelTransformModulesSystemJS = require('@babel/plugin-transform-modules-systemjs');
var babelPresetEnv = require('@babel/preset-env');
self.onmessage = function (evt) {
// transform source with Babel
var output = babelTransform(evt.data.source, {
compact: false,
filename: evt.data.key + '!transpiled',
sourceFileName: evt.data.key,
moduleIds: false,
sourceMaps: 'inline',
babelrc: false,
plugins: [babelTransformDynamicImport, babelTransformModulesSystemJS],
presets: [ [ babelPresetEnv, { targets: 'ie >= 11' } ] ],
});
self.postMessage({key: evt.data.key, code: output.code, source: evt.data.source});
};

View File

@ -1,279 +0,0 @@
import RegisterLoader from 'es-module-loader/core/register-loader.js';
import { baseURI, global, isBrowser } from 'es-module-loader/core/common.js';
import { resolveIfNotPlain } from 'es-module-loader/core/resolve.js';
var loader;
// <script type="module"> support
var anonSources = {};
if (typeof document != 'undefined' && document.getElementsByTagName) {
var handleError = function(err) {
// dispatch an error event so that we can display in errors in browsers
// that don't yet support unhandledrejection
if (window.onunhandledrejection === undefined) {
try {
var evt = new Event('error');
} catch (_eventError) {
var evt = document.createEvent('Event');
evt.initEvent('error', true, true);
}
evt.message = err.message;
if (err.fileName) {
evt.filename = err.fileName;
evt.lineno = err.lineNumber;
evt.colno = err.columnNumber;
} else if (err.sourceURL) {
evt.filename = err.sourceURL;
evt.lineno = err.line;
evt.colno = err.column;
}
evt.error = err;
window.dispatchEvent(evt);
}
// throw so it still shows up in the console
throw err;
}
var ready = function() {
document.removeEventListener('DOMContentLoaded', ready, false );
var anonCnt = 0;
var scripts = document.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
var script = scripts[i];
if (script.type == 'module' && !script.loaded) {
script.loaded = true;
if (script.src) {
loader.import(script.src).catch(handleError);
}
// anonymous modules supported via a custom naming scheme and registry
else {
var uri = './<anon' + ++anonCnt + '>.js';
if (script.id !== ""){
uri = "./" + script.id;
}
var anonName = resolveIfNotPlain(uri, baseURI);
anonSources[anonName] = script.innerHTML;
loader.import(anonName).catch(handleError);
}
}
}
}
// simple DOM ready
if (document.readyState !== 'loading')
setTimeout(ready);
else
document.addEventListener('DOMContentLoaded', ready, false);
}
function BrowserESModuleLoader(baseKey) {
if (baseKey)
this.baseKey = resolveIfNotPlain(baseKey, baseURI) || resolveIfNotPlain('./' + baseKey, baseURI);
RegisterLoader.call(this);
var loader = this;
// ensure System.register is available
global.System = global.System || {};
if (typeof global.System.register == 'function')
var prevRegister = global.System.register;
global.System.register = function() {
loader.register.apply(loader, arguments);
if (prevRegister)
prevRegister.apply(this, arguments);
};
}
BrowserESModuleLoader.prototype = Object.create(RegisterLoader.prototype);
// normalize is never given a relative name like "./x", that part is already handled
BrowserESModuleLoader.prototype[RegisterLoader.resolve] = function(key, parent) {
var resolved = RegisterLoader.prototype[RegisterLoader.resolve].call(this, key, parent || this.baseKey) || key;
if (!resolved)
throw new RangeError('ES module loader does not resolve plain module names, resolving "' + key + '" to ' + parent);
return resolved;
};
function xhrFetch(url, resolve, reject) {
var xhr = new XMLHttpRequest();
var load = function(source) {
resolve(xhr.responseText);
}
var error = function() {
reject(new Error('XHR error' + (xhr.status ? ' (' + xhr.status + (xhr.statusText ? ' ' + xhr.statusText : '') + ')' : '') + ' loading ' + url));
}
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
// in Chrome on file:/// URLs, status is 0
if (xhr.status == 0) {
if (xhr.responseText) {
load();
}
else {
// when responseText is empty, wait for load or error event
// to inform if it is a 404 or empty file
xhr.addEventListener('error', error);
xhr.addEventListener('load', load);
}
}
else if (xhr.status === 200) {
load();
}
else {
error();
}
}
};
xhr.open("GET", url, true);
xhr.send(null);
}
var WorkerPool = function (script, size) {
var current = document.currentScript;
// IE doesn't support currentScript
if (!current) {
// Find an entry with out basename
var scripts = document.getElementsByTagName('script');
for (var i = 0; i < scripts.length; i++) {
if (scripts[i].src.indexOf("browser-es-module-loader.js") !== -1) {
current = scripts[i];
break;
}
}
if (!current)
throw Error("Could not find own <script> element");
}
script = current.src.substr(0, current.src.lastIndexOf("/")) + "/" + script;
this._workers = new Array(size);
this._ind = 0;
this._size = size;
this._jobs = 0;
this.onmessage = undefined;
this._stopTimeout = undefined;
for (var i = 0; i < size; i++) {
var wrkr = new Worker(script);
wrkr._count = 0;
wrkr._ind = i;
wrkr.onmessage = this._onmessage.bind(this, wrkr);
wrkr.onerror = this._onerror.bind(this);
this._workers[i] = wrkr;
}
this._checkJobs();
};
WorkerPool.prototype = {
postMessage: function (msg) {
if (this._stopTimeout !== undefined) {
clearTimeout(this._stopTimeout);
this._stopTimeout = undefined;
}
var wrkr = this._workers[this._ind % this._size];
wrkr._count++;
this._jobs++;
wrkr.postMessage(msg);
this._ind++;
},
_onmessage: function (wrkr, evt) {
wrkr._count--;
this._jobs--;
this.onmessage(evt, wrkr);
this._checkJobs();
},
_onerror: function(err) {
try {
var evt = new Event('error');
} catch (_eventError) {
var evt = document.createEvent('Event');
evt.initEvent('error', true, true);
}
evt.message = err.message;
evt.filename = err.filename;
evt.lineno = err.lineno;
evt.colno = err.colno;
evt.error = err.error;
window.dispatchEvent(evt);
},
_checkJobs: function () {
if (this._jobs === 0 && this._stopTimeout === undefined) {
// wait for 2s of inactivity before stopping (that should be enough for local loading)
this._stopTimeout = setTimeout(this._stop.bind(this), 2000);
}
},
_stop: function () {
this._workers.forEach(function(wrkr) {
wrkr.terminate();
});
}
};
var promiseMap = new Map();
var babelWorker = new WorkerPool('babel-worker.js', 3);
babelWorker.onmessage = function (evt) {
var promFuncs = promiseMap.get(evt.data.key);
promFuncs.resolve(evt.data);
promiseMap.delete(evt.data.key);
};
// instantiate just needs to run System.register
// so we fetch the source, convert into the Babel System module format, then evaluate it
BrowserESModuleLoader.prototype[RegisterLoader.instantiate] = function(key, processAnonRegister) {
var loader = this;
// load as ES with Babel converting into System.register
return new Promise(function(resolve, reject) {
// anonymous module
if (anonSources[key]) {
resolve(anonSources[key])
anonSources[key] = undefined;
}
// otherwise we fetch
else {
xhrFetch(key, resolve, reject);
}
})
.then(function(source) {
// check our cache first
var cacheEntry = localStorage.getItem(key);
if (cacheEntry) {
cacheEntry = JSON.parse(cacheEntry);
// TODO: store a hash instead
if (cacheEntry.source === source) {
return Promise.resolve({key: key, code: cacheEntry.code, source: cacheEntry.source});
}
}
return new Promise(function (resolve, reject) {
promiseMap.set(key, {resolve: resolve, reject: reject});
babelWorker.postMessage({key: key, source: source});
});
}).then(function (data) {
// evaluate without require, exports and module variables
// we leave module in for now to allow module.require access
try {
var cacheEntry = JSON.stringify({source: data.source, code: data.code});
localStorage.setItem(key, cacheEntry);
} catch (e) {
if (window.console) {
window.console.warn('Unable to cache transpiled version of ' + key + ': ' + e);
}
}
(0, eval)(data.code + '\n//# sourceURL=' + data.key + '!transpiled');
processAnonRegister();
});
};
// create a default loader instance in the browser
if (isBrowser)
loader = new BrowserESModuleLoader();
export default BrowserESModuleLoader;

255
vendor/promise.js vendored
View File

@ -1,255 +0,0 @@
/* Copyright (c) 2014 Taylor Hakes
* Copyright (c) 2014 Forbes Lindesay
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
(function (root) {
// Store setTimeout reference so promise-polyfill will be unaffected by
// other code modifying setTimeout (like sinon.useFakeTimers())
var setTimeoutFunc = setTimeout;
function noop() {}
// Polyfill for Function.prototype.bind
function bind(fn, thisArg) {
return function () {
fn.apply(thisArg, arguments);
};
}
function Promise(fn) {
if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new');
if (typeof fn !== 'function') throw new TypeError('not a function');
this._state = 0;
this._handled = false;
this._value = undefined;
this._deferreds = [];
doResolve(fn, this);
}
function handle(self, deferred) {
while (self._state === 3) {
self = self._value;
}
if (self._state === 0) {
self._deferreds.push(deferred);
return;
}
self._handled = true;
Promise._immediateFn(function () {
var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
if (cb === null) {
(self._state === 1 ? resolve : reject)(deferred.promise, self._value);
return;
}
var ret;
try {
ret = cb(self._value);
} catch (e) {
reject(deferred.promise, e);
return;
}
resolve(deferred.promise, ret);
});
}
function resolve(self, newValue) {
try {
// Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.');
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
var then = newValue.then;
if (newValue instanceof Promise) {
self._state = 3;
self._value = newValue;
finale(self);
return;
} else if (typeof then === 'function') {
doResolve(bind(then, newValue), self);
return;
}
}
self._state = 1;
self._value = newValue;
finale(self);
} catch (e) {
reject(self, e);
}
}
function reject(self, newValue) {
self._state = 2;
self._value = newValue;
finale(self);
}
function finale(self) {
if (self._state === 2 && self._deferreds.length === 0) {
Promise._immediateFn(function() {
if (!self._handled) {
Promise._unhandledRejectionFn(self._value);
}
});
}
for (var i = 0, len = self._deferreds.length; i < len; i++) {
handle(self, self._deferreds[i]);
}
self._deferreds = null;
}
function Handler(onFulfilled, onRejected, promise) {
this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
this.onRejected = typeof onRejected === 'function' ? onRejected : null;
this.promise = promise;
}
/**
* Take a potentially misbehaving resolver function and make sure
* onFulfilled and onRejected are only called once.
*
* Makes no guarantees about asynchrony.
*/
function doResolve(fn, self) {
var done = false;
try {
fn(function (value) {
if (done) return;
done = true;
resolve(self, value);
}, function (reason) {
if (done) return;
done = true;
reject(self, reason);
});
} catch (ex) {
if (done) return;
done = true;
reject(self, ex);
}
}
Promise.prototype['catch'] = function (onRejected) {
return this.then(null, onRejected);
};
Promise.prototype.then = function (onFulfilled, onRejected) {
var prom = new (this.constructor)(noop);
handle(this, new Handler(onFulfilled, onRejected, prom));
return prom;
};
Promise.all = function (arr) {
var args = Array.prototype.slice.call(arr);
return new Promise(function (resolve, reject) {
if (args.length === 0) return resolve([]);
var remaining = args.length;
function res(i, val) {
try {
if (val && (typeof val === 'object' || typeof val === 'function')) {
var then = val.then;
if (typeof then === 'function') {
then.call(val, function (val) {
res(i, val);
}, reject);
return;
}
}
args[i] = val;
if (--remaining === 0) {
resolve(args);
}
} catch (ex) {
reject(ex);
}
}
for (var i = 0; i < args.length; i++) {
res(i, args[i]);
}
});
};
Promise.resolve = function (value) {
if (value && typeof value === 'object' && value.constructor === Promise) {
return value;
}
return new Promise(function (resolve) {
resolve(value);
});
};
Promise.reject = function (value) {
return new Promise(function (resolve, reject) {
reject(value);
});
};
Promise.race = function (values) {
return new Promise(function (resolve, reject) {
for (var i = 0, len = values.length; i < len; i++) {
values[i].then(resolve, reject);
}
});
};
// Use polyfill for setImmediate for performance gains
Promise._immediateFn = (typeof setImmediate === 'function' && function (fn) { setImmediate(fn); }) ||
function (fn) {
setTimeoutFunc(fn, 0);
};
Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) {
if (typeof console !== 'undefined' && console) {
console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
}
};
/**
* Set the immediate function to execute callbacks
* @param fn {function} Function to execute
* @deprecated
*/
Promise._setImmediateFn = function _setImmediateFn(fn) {
Promise._immediateFn = fn;
};
/**
* Change the function to execute on unhandled rejection
* @param {function} fn Function to execute on unhandled rejection
* @deprecated
*/
Promise._setUnhandledRejectionFn = function _setUnhandledRejectionFn(fn) {
Promise._unhandledRejectionFn = fn;
};
if (typeof module !== 'undefined' && module.exports) {
module.exports = Promise;
} else if (!root.Promise) {
root.Promise = Promise;
}
})(this);

View File

@ -16,10 +16,6 @@
<title>noVNC</title>
<meta charset="utf-8">
<!-- Always force latest IE rendering engine (even in intranet) & Chrome Frame
Remove this if you use the .htaccess -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<!-- Icons (see app/images/icons/Makefile for what the sizes are for) -->
<link rel="icon" sizes="16x16" type="image/png" href="app/images/icons/novnc-16x16.png">
@ -54,17 +50,8 @@
<!-- Stylesheets -->
<link rel="stylesheet" href="app/styles/base.css">
<!-- this is included as a normal file in order to catch script-loading errors as well -->
<script src="app/error-handler.js"></script>
<!-- begin scripts -->
<!-- promise polyfills promises for IE11 -->
<script src="vendor/promise.js"></script>
<!-- ES2015/ES6 modules polyfill -->
<script nomodule src="vendor/browser-es-module-loader/dist/browser-es-module-loader.js"></script>
<!-- actual script modules -->
<script type="module" crossorigin="anonymous" src="app/ui.js"></script>
<!-- end scripts -->
</head>
<body>

View File

@ -18,10 +18,6 @@
<meta charset="utf-8">
<!-- Always force latest IE rendering engine (even in intranet) &
Chrome Frame. Remove this if you use the .htaccess -->
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<style>
body {
@ -61,13 +57,6 @@
</style>
<!-- Promise polyfill for IE11 -->
<script src="vendor/promise.js"></script>
<!-- ES2015/ES6 modules polyfill -->
<script nomodule src="vendor/browser-es-module-loader/dist/browser-es-module-loader.js"></script>
<!-- actual script modules -->
<script type="module" crossorigin="anonymous">
// RFB holds the API to connect and communicate with a VNC server
import RFB from './core/rfb.js';