Merge pull request #774 from novnc/refactor/es6-module-loader
[Refactor] ES6 Modules
This commit is contained in:
commit
dd7068984d
|
@ -3,7 +3,9 @@
|
|||
tests/data_*.js
|
||||
utils/rebind.so
|
||||
utils/websockify
|
||||
node_modules
|
||||
build
|
||||
lib
|
||||
/node_modules
|
||||
/build
|
||||
/lib
|
||||
recordings
|
||||
*.swp
|
||||
*~
|
||||
|
|
|
@ -7,7 +7,6 @@ node_js:
|
|||
- '6.1'
|
||||
env:
|
||||
matrix:
|
||||
- TEST_BROWSER_NAME=PhantomJS
|
||||
- TEST_BROWSER_NAME=chrome TEST_BROWSER_OS='Windows 10,Linux,OS X 10.11'
|
||||
- TEST_BROWSER_NAME=firefox TEST_BROWSER_OS='Windows 10,Linux,OS X 10.11'
|
||||
- TEST_BROWSER_NAME='internet explorer' TEST_BROWSER_OS='Windows 10'
|
||||
|
|
|
@ -35,20 +35,8 @@ Contributing Guidelines
|
|||
Running the unit tests
|
||||
----------------------
|
||||
|
||||
There are two ways to run the unit tests. For both ways, you should first run
|
||||
`npm install` (not as root).
|
||||
|
||||
The first way to run the tests is to run `npm test`. This will run all the
|
||||
tests in the headless PhantomJS browser (which uses WebKit).
|
||||
|
||||
The second way to run the tests is using the `tests/run_from_console.js` file.
|
||||
This way is a bit more flexible, and can provide more information about what
|
||||
went wrong. To run all the tests, simply run `tests/run_from_console.js`.
|
||||
To run a specific test file, you can use the `-t path/to/test/file.js` option.
|
||||
If you wish to simply generate the HTML for the test, use the `-g` option, and
|
||||
the path to the temporary HTML file will be written to standard out. To open
|
||||
this file in your default browser automatically, pass the `-o` option as well.
|
||||
More information can be found by passing the `--help` or `-h` option.
|
||||
|
||||
We use Karma to run our tests. You can launch karma manually, or simply
|
||||
run `npm test`. The Karma debug page will display the tests in normal
|
||||
mocha form, if you need it.
|
||||
|
||||
Thanks, and happy coding!
|
||||
|
|
26
LICENSE.txt
26
LICENSE.txt
|
@ -5,19 +5,9 @@ Public License 2.0). The noVNC core library is composed of the
|
|||
Javascript code necessary for full noVNC operation. This includes (but
|
||||
is not limited to):
|
||||
|
||||
core/base64.js
|
||||
core/des.js
|
||||
core/display.js
|
||||
core/input/devices.js
|
||||
core/input/keysym.js
|
||||
core/logo.js
|
||||
core/playback.js
|
||||
core/rfb.js
|
||||
app/ui.js
|
||||
core/util.js
|
||||
core/websock.js
|
||||
app/webutil.js
|
||||
core/input/xtscancodes.js
|
||||
core/**/*.js
|
||||
app/*.js
|
||||
test/playback.js
|
||||
|
||||
The HTML, CSS, font and images files that included with the noVNC
|
||||
source distibution (or repository) are not considered part of the
|
||||
|
@ -49,8 +39,11 @@ licenses (all MPL 2.0 compatible):
|
|||
|
||||
core/des.js : Various BSD style licenses
|
||||
|
||||
utils/inflator.mod.js
|
||||
include/inflator.js : MIT (for pako)
|
||||
vendor/pako/ : MIT
|
||||
|
||||
vendor/browser-es-module-loader: MIT
|
||||
|
||||
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
|
||||
|
@ -66,7 +59,7 @@ The following license texts are included:
|
|||
docs/LICENSE.BSD-2-Clause (Simplified BSD / FreeBSD)
|
||||
docs/LICENSE.zlib
|
||||
docs/LICENSE.Apache-2.0
|
||||
docs/LICENSE.pako
|
||||
vendor/pako/LICENSE (MIT)
|
||||
|
||||
Or alternatively the license texts may be found here:
|
||||
|
||||
|
@ -77,3 +70,4 @@ Or alternatively the license texts may be found here:
|
|||
http://en.wikipedia.org/wiki/BSD_licenses
|
||||
http://www.gzip.org/zlib/zlib_license.html
|
||||
http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
https://opensource.org/licenses/MIT
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
// NB: this should *not* be included as a module until we have
|
||||
// native support in the browsers, so that our error handler
|
||||
// can catch script-loading errors.
|
||||
|
||||
|
||||
(function(){
|
||||
"use strict";
|
||||
|
||||
function convertNewlines(msg, parentElem) {
|
||||
const lines = msg.split("\n");
|
||||
lines.forEach(function (line) {
|
||||
parentElem.appendChild(document.createElement("br"));
|
||||
parentElem.appendChild(document.createTextNode(line));
|
||||
});
|
||||
parentElem.removeChild(parentElem.firstChild);
|
||||
return parentElem;
|
||||
}
|
||||
|
||||
// Fallback for all uncought errors
|
||||
function handleError (event, err) {
|
||||
try {
|
||||
const msg = document.getElementById('noVNC_fallback_errormsg');
|
||||
|
||||
// Only show the initial error
|
||||
if (msg.hasChildNodes()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var div = document.createElement("div");
|
||||
div.classList.add('noVNC_message');
|
||||
convertNewlines(event.message, div);
|
||||
msg.appendChild(div);
|
||||
|
||||
if (event.filename !== undefined && event.lineno !== undefined && event.colno !== undefined) {
|
||||
div = document.createElement("div");
|
||||
div.className = 'noVNC_location';
|
||||
const text = event.filename + ":" + event.lineno + ":" + event.colno;
|
||||
div.appendChild(document.createTextNode(text));
|
||||
msg.appendChild(div);
|
||||
}
|
||||
|
||||
if ((err !== undefined) &&
|
||||
(err.stack !== undefined)) {
|
||||
div = document.createElement("div");
|
||||
div.className = 'noVNC_stack';
|
||||
div.appendChild(document.createTextNode(err.stack));
|
||||
msg.appendChild(div);
|
||||
}
|
||||
|
||||
document.getElementById('noVNC_fallback_error')
|
||||
.classList.add("noVNC_open");
|
||||
} catch (exc) {
|
||||
document.write("noVNC encountered an error.");
|
||||
}
|
||||
// Don't return true since this would prevent the error
|
||||
// from being printed to the browser console.
|
||||
return false;
|
||||
}
|
||||
window.addEventListener('error', function (evt) { handleError(evt, evt.error); });
|
||||
window.addEventListener('unhandledrejection', function (evt) { handleError(evt.reason, evt.reason); });
|
||||
})();
|
|
@ -1,11 +1,4 @@
|
|||
/*
|
||||
* Translations for de
|
||||
*
|
||||
* This file was autotomatically generated from de.po
|
||||
* DO NOT EDIT!
|
||||
*/
|
||||
|
||||
Language = {
|
||||
{
|
||||
"Connecting...": "Verbunden...",
|
||||
"Connected (encrypted) to ": "Verbunden mit (verschlüsselt) ",
|
||||
"Connected (unencrypted) to ": "Verbunden mit (unverschlüsselt) ",
|
||||
|
@ -14,5 +7,5 @@ Language = {
|
|||
"Must set host and port": "Richten Sie Host und Port ein",
|
||||
"Password is required": "Passwort ist erforderlich",
|
||||
"Forcing clipping mode since scrollbars aren't supported by IE in fullscreen": "'Clipping-Modus' aktiviert, Scrollbalken in 'IE-Vollbildmodus' werden nicht unterstützt",
|
||||
"Disconnect timeout": "Timeout beim trennen",
|
||||
};
|
||||
"Disconnect timeout": "Timeout beim trennen"
|
||||
}
|
|
@ -1,11 +1,4 @@
|
|||
/*
|
||||
* Translations for el
|
||||
*
|
||||
* This file was autotomatically generated from el.po
|
||||
* DO NOT EDIT!
|
||||
*/
|
||||
|
||||
Language = {
|
||||
{
|
||||
"Connecting...": "Συνδέεται...",
|
||||
"Connected (encrypted) to ": "Συνδέθηκε (κρυπτογραφημένα) με το ",
|
||||
"Connected (unencrypted) to ": "Συνδέθηκε (μη κρυπτογραφημένα) με το ",
|
||||
|
@ -15,7 +8,7 @@ Language = {
|
|||
"Password is required": "Απαιτείται ο κωδικός πρόσβασης",
|
||||
"Forcing clipping mode since scrollbars aren't supported by IE in fullscreen": "Εφαρμογή λειτουργίας αποκοπής αφού δεν υποστηρίζονται οι λωρίδες κύλισης σε πλήρη οθόνη στον IE",
|
||||
"Disconnect timeout": "Παρέλευση χρονικού ορίου αποσύνδεσης",
|
||||
"noVNC encountered an error:": "το noVNC αντιμετώπισε ένα σφάλμα",
|
||||
"noVNC encountered an error:": "το noVNC αντιμετώπισε ένα σφάλμα:",
|
||||
"Hide/Show the control bar": "Απόκρυψη/Εμφάνιση γραμμής ελέγχου",
|
||||
"Move/Drag Viewport": "Μετακίνηση/Σύρσιμο Θεατού πεδίου",
|
||||
"viewport drag": "σύρσιμο θεατού πεδίου",
|
||||
|
@ -65,10 +58,13 @@ Language = {
|
|||
"default": "προεπιλεγμένο",
|
||||
"Logging:": "Καταγραφή:",
|
||||
"Apply": "Εφαρμογή",
|
||||
"Connect": "Σύνδεση",
|
||||
"Disconnect": "Αποσύνδεση",
|
||||
"Connection": "Σύνδεση",
|
||||
"Host:": "Όνομα διακομιστή:",
|
||||
"Port:": "Πόρτα διακομιστή:",
|
||||
"Password:": "Κωδικός Πρόσβασης:",
|
||||
"Token:": "Διακριτικό:",
|
||||
"Send Password": "Αποστολή Κωδικού Πρόσβασης",
|
||||
"Canvas not supported.": "Δεν υποστηρίζεται το στοιχείο Canvas",
|
||||
};
|
||||
"Canvas not supported.": "Δεν υποστηρίζεται το στοιχείο Canvas"
|
||||
}
|
|
@ -1,11 +1,4 @@
|
|||
/*
|
||||
* Translations for nl
|
||||
*
|
||||
* This file was autotomatically generated from nl.po
|
||||
* DO NOT EDIT!
|
||||
*/
|
||||
|
||||
Language = {
|
||||
{
|
||||
"Connecting...": "Verbinden...",
|
||||
"Connected (encrypted) to ": "Verbonden (versleuteld) met ",
|
||||
"Connected (unencrypted) to ": "Verbonden (onversleuteld) met ",
|
||||
|
@ -14,5 +7,5 @@ Language = {
|
|||
"Must set host and port": "Host en poort moeten worden ingesteld",
|
||||
"Password is required": "Wachtwoord is vereist",
|
||||
"Forcing clipping mode since scrollbars aren't supported by IE in fullscreen": "''Clipping mode' ingeschakeld, omdat schuifbalken in volledige-scherm-modus in IE niet worden ondersteund",
|
||||
"Disconnect timeout": "Timeout tijdens verbreken van verbinding",
|
||||
};
|
||||
"Disconnect timeout": "Timeout tijdens verbreken van verbinding"
|
||||
}
|
|
@ -1,11 +1,4 @@
|
|||
/*
|
||||
* Translations for sv
|
||||
*
|
||||
* This file was autotomatically generated from sv.po
|
||||
* DO NOT EDIT!
|
||||
*/
|
||||
|
||||
Language = {
|
||||
{
|
||||
"Connecting...": "Ansluter...",
|
||||
"Connected (encrypted) to ": "Ansluten (krypterat) till ",
|
||||
"Connected (unencrypted) to ": "Ansluten (okrypterat) till ",
|
||||
|
@ -73,5 +66,5 @@ Language = {
|
|||
"Password:": "Lösenord:",
|
||||
"Token:": "Token:",
|
||||
"Send Password": "Skicka Lösenord",
|
||||
"Canvas not supported.": "Canvas stöds ej",
|
||||
};
|
||||
"Canvas not supported.": "Canvas stöds ej"
|
||||
}
|
|
@ -247,6 +247,11 @@ select:active {
|
|||
font-weight: normal;
|
||||
}
|
||||
|
||||
#noVNC_fallback_errormsg .noVNC_message {
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#noVNC_fallback_error .noVNC_location {
|
||||
font-style: italic;
|
||||
font-size: 0.8em;
|
||||
|
@ -774,6 +779,7 @@ select:active {
|
|||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
:root.noVNC_loading #noVNC_transition,
|
||||
:root.noVNC_connecting #noVNC_transition,
|
||||
:root.noVNC_disconnecting #noVNC_transition,
|
||||
:root.noVNC_reconnecting #noVNC_transition {
|
||||
|
|
167
app/ui.js
167
app/ui.js
|
@ -11,81 +11,17 @@
|
|||
/* jslint white: false, browser: true */
|
||||
/* global window, document.getElementById, Util, WebUtil, RFB, Display */
|
||||
|
||||
/* [module]
|
||||
* import Util from "../core/util";
|
||||
* import KeyTable from "../core/input/keysym";
|
||||
* import keysyms from "./keysymdef";
|
||||
* import RFB from "../core/rfb";
|
||||
* import Display from "../core/display";
|
||||
* import WebUtil from "./webutil";
|
||||
*/
|
||||
import * as Log from '../core/util/logging.js';
|
||||
import _, { l10n } from '../core/util/localization.js'
|
||||
import { isTouchDevice, browserSupportsCursorURIs as cursorURIsSupported } from '../core/util/browsers.js';
|
||||
import { setCapture, getPointerEvent } from '../core/util/events.js';
|
||||
import KeyTable from "../core/input/keysym.js";
|
||||
import keysyms from "../core/input/keysymdef.js";
|
||||
import RFB from "../core/rfb.js";
|
||||
import Display from "../core/display.js";
|
||||
import * as WebUtil from "./webutil.js";
|
||||
|
||||
var UI;
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
// Fallback for all uncought errors
|
||||
window.addEventListener('error', function(event) {
|
||||
try {
|
||||
var msg, div, text;
|
||||
|
||||
msg = document.getElementById('noVNC_fallback_errormsg');
|
||||
|
||||
// Only show the initial error
|
||||
if (msg.hasChildNodes()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
div = document.createElement("div");
|
||||
div.appendChild(document.createTextNode(event.message));
|
||||
msg.appendChild(div);
|
||||
|
||||
div = document.createElement("div");
|
||||
div.className = 'noVNC_location';
|
||||
text = event.filename + ":" + event.lineno + ":" + event.colno;
|
||||
div.appendChild(document.createTextNode(text));
|
||||
msg.appendChild(div);
|
||||
|
||||
if ((event.error !== undefined) &&
|
||||
(event.error.stack !== undefined)) {
|
||||
div = document.createElement("div");
|
||||
div.className = 'noVNC_stack';
|
||||
div.appendChild(document.createTextNode(event.error.stack));
|
||||
msg.appendChild(div);
|
||||
}
|
||||
|
||||
document.getElementById('noVNC_fallback_error')
|
||||
.classList.add("noVNC_open");
|
||||
} catch (exc) {
|
||||
document.write("noVNC encountered an error.");
|
||||
}
|
||||
// Don't return true since this would prevent the error
|
||||
// from being printed to the browser console.
|
||||
return false;
|
||||
});
|
||||
|
||||
// Set up translations
|
||||
var LINGUAS = ["de", "el", "nl", "sv"];
|
||||
Util.Localisation.setup(LINGUAS);
|
||||
if (Util.Localisation.language !== "en") {
|
||||
WebUtil.load_scripts(
|
||||
{'app': ["locale/" + Util.Localisation.language + ".js"]});
|
||||
}
|
||||
|
||||
/* [begin skip-as-module] */
|
||||
// Load supporting scripts
|
||||
WebUtil.load_scripts(
|
||||
{'core': ["base64.js", "websock.js", "des.js", "input/keysymdef.js",
|
||||
"input/xtscancodes.js", "input/util.js", "input/devices.js",
|
||||
"display.js", "inflator.js", "rfb.js", "input/keysym.js"]});
|
||||
|
||||
window.onscriptsload = function () { UI.load(); };
|
||||
/* [end skip-as-module] */
|
||||
|
||||
var _ = Util.Localisation.get;
|
||||
|
||||
UI = {
|
||||
const UI = {
|
||||
|
||||
connected: false,
|
||||
desktopName: "",
|
||||
|
@ -110,6 +46,14 @@ var UI;
|
|||
reconnect_callback: null,
|
||||
reconnect_password: null,
|
||||
|
||||
prime: function(callback) {
|
||||
if (document.readyState === "interactive" || document.readyState === "complete") {
|
||||
UI.load(callback);
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', UI.load.bind(UI, callback));
|
||||
}
|
||||
},
|
||||
|
||||
// Setup rfb object, load settings from browser storage, then call
|
||||
// UI.init to setup the UI/menus
|
||||
load: function(callback) {
|
||||
|
@ -126,10 +70,10 @@ var UI;
|
|||
UI.initSettings();
|
||||
|
||||
// Translate the DOM
|
||||
Util.Localisation.translateDOM();
|
||||
l10n.translateDOM();
|
||||
|
||||
// Adapt the interface for touch screen devices
|
||||
if (Util.isTouchDevice) {
|
||||
if (isTouchDevice) {
|
||||
document.documentElement.classList.add("noVNC_touch");
|
||||
// Remove the address bar
|
||||
setTimeout(function() { window.scrollTo(0, 1); }, 100);
|
||||
|
@ -166,6 +110,7 @@ var UI;
|
|||
UI.updateVisualState();
|
||||
|
||||
document.getElementById('noVNC_setting_host').focus();
|
||||
document.documentElement.classList.remove("noVNC_loading");
|
||||
|
||||
var autoconnect = WebUtil.getConfigVar('autoconnect', false);
|
||||
if (autoconnect === 'true' || autoconnect == '1') {
|
||||
|
@ -224,7 +169,7 @@ var UI;
|
|||
UI.initSetting('port', port);
|
||||
UI.initSetting('encrypt', (window.location.protocol === "https:"));
|
||||
UI.initSetting('true_color', true);
|
||||
UI.initSetting('cursor', !Util.isTouchDevice);
|
||||
UI.initSetting('cursor', !isTouchDevice);
|
||||
UI.initSetting('clip', false);
|
||||
UI.initSetting('resize', 'off');
|
||||
UI.initSetting('shared', true);
|
||||
|
@ -236,7 +181,6 @@ var UI;
|
|||
|
||||
UI.setupSettingLabels();
|
||||
},
|
||||
|
||||
// Adds a link to the label elements on the corresponding input elements
|
||||
setupSettingLabels: function() {
|
||||
var labels = document.getElementsByTagName('LABEL');
|
||||
|
@ -274,7 +218,7 @@ var UI;
|
|||
return true;
|
||||
} catch (exc) {
|
||||
var msg = "Unable to create RFB client -- " + exc;
|
||||
Util.Error(msg);
|
||||
Log.Error(msg);
|
||||
UI.showStatus(msg, 'error');
|
||||
return false;
|
||||
}
|
||||
|
@ -509,7 +453,7 @@ var UI;
|
|||
break;
|
||||
default:
|
||||
msg = "Invalid UI state";
|
||||
Util.Error(msg);
|
||||
Log.Error(msg);
|
||||
UI.showStatus(msg, 'error');
|
||||
break;
|
||||
}
|
||||
|
@ -519,11 +463,11 @@ var UI;
|
|||
|
||||
// Disable/enable controls depending on connection state
|
||||
updateVisualState: function() {
|
||||
//Util.Debug(">> updateVisualState");
|
||||
//Log.Debug(">> updateVisualState");
|
||||
|
||||
UI.enableDisableViewClip();
|
||||
|
||||
if (Util.browserSupportsCursorURIs() && !Util.isTouchDevice) {
|
||||
if (cursorURIsSupported() && !isTouchDevice) {
|
||||
UI.enableSetting('cursor');
|
||||
} else {
|
||||
UI.disableSetting('cursor');
|
||||
|
@ -575,7 +519,7 @@ var UI;
|
|||
document.getElementById('noVNC_password_dlg')
|
||||
.classList.remove('noVNC_open');
|
||||
|
||||
//Util.Debug("<< updateVisualState");
|
||||
//Log.Debug("<< updateVisualState");
|
||||
},
|
||||
|
||||
showStatus: function(text, status_type, time) {
|
||||
|
@ -689,7 +633,7 @@ var UI;
|
|||
dragControlbarHandle: function (e) {
|
||||
if (!UI.controlbarGrabbed) return;
|
||||
|
||||
var ptr = Util.getPointerEvent(e);
|
||||
var ptr = getPointerEvent(e);
|
||||
|
||||
var anchor = document.getElementById('noVNC_control_bar_anchor');
|
||||
if (ptr.clientX < (window.innerWidth * 0.1)) {
|
||||
|
@ -786,12 +730,12 @@ var UI;
|
|||
controlbarHandleMouseDown: function(e) {
|
||||
if ((e.type == "mousedown") && (e.button != 0)) return;
|
||||
|
||||
var ptr = Util.getPointerEvent(e);
|
||||
var ptr = getPointerEvent(e);
|
||||
|
||||
var handle = document.getElementById("noVNC_control_bar_handle");
|
||||
var bounds = handle.getBoundingClientRect();
|
||||
|
||||
Util.setCapture(handle);
|
||||
setCapture(handle);
|
||||
UI.controlbarGrabbed = true;
|
||||
UI.controlbarDrag = false;
|
||||
|
||||
|
@ -872,7 +816,7 @@ var UI;
|
|||
val = ctrl.value;
|
||||
}
|
||||
WebUtil.writeSetting(name, val);
|
||||
//Util.Debug("Setting saved '" + name + "=" + val + "'");
|
||||
//Log.Debug("Setting saved '" + name + "=" + val + "'");
|
||||
return val;
|
||||
},
|
||||
|
||||
|
@ -931,10 +875,10 @@ var UI;
|
|||
// Refresh UI elements from saved cookies
|
||||
UI.updateSetting('encrypt');
|
||||
UI.updateSetting('true_color');
|
||||
if (Util.browserSupportsCursorURIs()) {
|
||||
if (cursorURIsSupported()) {
|
||||
UI.updateSetting('cursor');
|
||||
} else {
|
||||
UI.updateSetting('cursor', !Util.isTouchDevice);
|
||||
UI.updateSetting('cursor', !isTouchDevice);
|
||||
UI.disableSetting('cursor');
|
||||
}
|
||||
UI.updateSetting('clip');
|
||||
|
@ -1047,9 +991,9 @@ var UI;
|
|||
},
|
||||
|
||||
clipboardReceive: function(rfb, text) {
|
||||
Util.Debug(">> UI.clipboardReceive: " + text.substr(0,40) + "...");
|
||||
Log.Debug(">> UI.clipboardReceive: " + text.substr(0,40) + "...");
|
||||
document.getElementById('noVNC_clipboard_text').value = text;
|
||||
Util.Debug("<< UI.clipboardReceive");
|
||||
Log.Debug("<< UI.clipboardReceive");
|
||||
},
|
||||
|
||||
clipboardClear: function() {
|
||||
|
@ -1059,9 +1003,9 @@ var UI;
|
|||
|
||||
clipboardSend: function() {
|
||||
var text = document.getElementById('noVNC_clipboard_text').value;
|
||||
Util.Debug(">> UI.clipboardSend: " + text.substr(0,40) + "...");
|
||||
Log.Debug(">> UI.clipboardSend: " + text.substr(0,40) + "...");
|
||||
UI.rfb.clipboardPasteFrom(text);
|
||||
Util.Debug("<< UI.clipboardSend");
|
||||
Log.Debug("<< UI.clipboardSend");
|
||||
},
|
||||
|
||||
/* ------^-------
|
||||
|
@ -1095,7 +1039,7 @@ var UI;
|
|||
|
||||
if ((!host) || (!port)) {
|
||||
var msg = _("Must set host and port");
|
||||
Util.Error(msg);
|
||||
Log.Error(msg);
|
||||
UI.showStatus(msg, 'error');
|
||||
return;
|
||||
}
|
||||
|
@ -1185,7 +1129,7 @@ var UI;
|
|||
if (typeof msg === 'undefined') {
|
||||
msg = _("Password is required");
|
||||
}
|
||||
Util.Warn(msg);
|
||||
Log.Warn(msg);
|
||||
UI.showStatus(msg, "warning");
|
||||
},
|
||||
|
||||
|
@ -1279,7 +1223,7 @@ var UI;
|
|||
UI.resizeTimeout = setTimeout(function(){
|
||||
// Request a remote size covering the viewport
|
||||
if (UI.rfb.requestDesktopSize(screen.w, screen.h)) {
|
||||
Util.Debug('Requested new desktop size: ' +
|
||||
Log.Debug('Requested new desktop size: ' +
|
||||
screen.w + 'x' + screen.h);
|
||||
}
|
||||
}, 500);
|
||||
|
@ -1334,7 +1278,7 @@ var UI;
|
|||
if (resizeSetting === 'downscale' || resizeSetting === 'scale') {
|
||||
// Disable clipping if we are scaling
|
||||
new_clip = false;
|
||||
} else if (Util.isTouchDevice) {
|
||||
} else if (isTouchDevice) {
|
||||
// Touch devices usually have shit scrollbars
|
||||
new_clip = true;
|
||||
}
|
||||
|
@ -1362,7 +1306,7 @@ var UI;
|
|||
var resizeSetting = UI.getSetting('resize');
|
||||
// Disable clipping if we are scaling, connected or on touch
|
||||
if (resizeSetting === 'downscale' || resizeSetting === 'scale' ||
|
||||
Util.isTouchDevice) {
|
||||
isTouchDevice) {
|
||||
UI.disableSetting('clip');
|
||||
} else {
|
||||
UI.enableSetting('clip');
|
||||
|
@ -1421,7 +1365,7 @@ var UI;
|
|||
|
||||
// Different behaviour for touch vs non-touch
|
||||
// The button is disabled instead of hidden on touch devices
|
||||
if (Util.isTouchDevice) {
|
||||
if (isTouchDevice) {
|
||||
viewDragButton.classList.remove("noVNC_hidden");
|
||||
|
||||
if (clipping) {
|
||||
|
@ -1447,7 +1391,7 @@ var UI;
|
|||
* ------v------*/
|
||||
|
||||
showVirtualKeyboard: function() {
|
||||
if (!Util.isTouchDevice) return;
|
||||
if (!isTouchDevice) return;
|
||||
|
||||
var input = document.getElementById('noVNC_keyboardinput');
|
||||
|
||||
|
@ -1463,7 +1407,7 @@ var UI;
|
|||
},
|
||||
|
||||
hideVirtualKeyboard: function() {
|
||||
if (!Util.isTouchDevice) return;
|
||||
if (!isTouchDevice) return;
|
||||
|
||||
var input = document.getElementById('noVNC_keyboardinput');
|
||||
|
||||
|
@ -1701,10 +1645,12 @@ var UI;
|
|||
},
|
||||
|
||||
updateLocalCursor: function() {
|
||||
if (!UI.rfb) return;
|
||||
UI.rfb.set_local_cursor(UI.getSetting('cursor'));
|
||||
},
|
||||
|
||||
updateViewOnly: function() {
|
||||
if (!UI.rfb) return;
|
||||
UI.rfb.set_view_only(UI.getSetting('view_only'));
|
||||
},
|
||||
|
||||
|
@ -1755,7 +1701,20 @@ var UI;
|
|||
*/
|
||||
};
|
||||
|
||||
/* [module] UI.load(); */
|
||||
})();
|
||||
// Set up translations
|
||||
var LINGUAS = ["de", "el", "nl", "sv"];
|
||||
l10n.setup(LINGUAS);
|
||||
if (l10n.language !== "en" && l10n.dictionary === undefined) {
|
||||
WebUtil.fetchJSON('app/locale/' + l10n.language + '.json', function (translations) {
|
||||
l10n.dictionary = translations;
|
||||
|
||||
/* [module] export default UI; */
|
||||
// wait for translations to load before loading the UI
|
||||
UI.prime();
|
||||
}, function (err) {
|
||||
throw err;
|
||||
});
|
||||
} else {
|
||||
UI.prime();
|
||||
}
|
||||
|
||||
export default UI;
|
||||
|
|
184
app/webutil.js
184
app/webutil.js
|
@ -10,62 +10,21 @@
|
|||
/*jslint bitwise: false, white: false, browser: true, devel: true */
|
||||
/*global Util, window, document */
|
||||
|
||||
/* [module]
|
||||
* import Util from "../core/util";
|
||||
*/
|
||||
|
||||
// Globals defined here
|
||||
var WebUtil = {};
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* Namespaced in WebUtil
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
import { init_logging as main_init_logging } from '../core/util/logging.js';
|
||||
|
||||
// init log level reading the logging HTTP param
|
||||
WebUtil.init_logging = function (level) {
|
||||
export function init_logging (level) {
|
||||
"use strict";
|
||||
if (typeof level !== "undefined") {
|
||||
Util._log_level = level;
|
||||
main_init_logging(level);
|
||||
} else {
|
||||
var param = document.location.href.match(/logging=([A-Za-z0-9\._\-]*)/);
|
||||
Util._log_level = (param || ['', Util._log_level])[1];
|
||||
main_init_logging(param || undefined);
|
||||
}
|
||||
Util.init_logging();
|
||||
};
|
||||
|
||||
|
||||
WebUtil.dirObj = function (obj, depth, parent) {
|
||||
"use strict";
|
||||
if (! depth) { depth = 2; }
|
||||
if (! parent) { parent = ""; }
|
||||
|
||||
// Print the properties of the passed-in object
|
||||
var msg = "";
|
||||
for (var i in obj) {
|
||||
if ((depth > 1) && (typeof obj[i] === "object")) {
|
||||
// Recurse attributes that are objects
|
||||
msg += WebUtil.dirObj(obj[i], depth - 1, parent + "." + i);
|
||||
} else {
|
||||
//val = new String(obj[i]).replace("\n", " ");
|
||||
var val = "";
|
||||
if (typeof(obj[i]) === "undefined") {
|
||||
val = "undefined";
|
||||
} else {
|
||||
val = obj[i].toString().replace("\n", " ");
|
||||
}
|
||||
if (val.length > 30) {
|
||||
val = val.substr(0, 30) + "...";
|
||||
}
|
||||
msg += parent + "." + i + ": " + val + "\n";
|
||||
}
|
||||
}
|
||||
return msg;
|
||||
};
|
||||
|
||||
// Read a query string variable
|
||||
WebUtil.getQueryVar = function (name, defVal) {
|
||||
export function getQueryVar (name, defVal) {
|
||||
"use strict";
|
||||
var re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
|
||||
match = document.location.href.match(re);
|
||||
|
@ -78,7 +37,7 @@ WebUtil.getQueryVar = function (name, defVal) {
|
|||
};
|
||||
|
||||
// Read a hash fragment variable
|
||||
WebUtil.getHashVar = function (name, defVal) {
|
||||
export function getHashVar (name, defVal) {
|
||||
"use strict";
|
||||
var re = new RegExp('.*[&#]' + name + '=([^&]*)'),
|
||||
match = document.location.hash.match(re);
|
||||
|
@ -92,11 +51,11 @@ WebUtil.getHashVar = function (name, defVal) {
|
|||
|
||||
// Read a variable from the fragment or the query string
|
||||
// Fragment takes precedence
|
||||
WebUtil.getConfigVar = function (name, defVal) {
|
||||
export function getConfigVar (name, defVal) {
|
||||
"use strict";
|
||||
var val = WebUtil.getHashVar(name);
|
||||
var val = getHashVar(name);
|
||||
if (val === null) {
|
||||
val = WebUtil.getQueryVar(name, defVal);
|
||||
val = getQueryVar(name, defVal);
|
||||
}
|
||||
return val;
|
||||
};
|
||||
|
@ -106,7 +65,7 @@ WebUtil.getConfigVar = function (name, defVal) {
|
|||
*/
|
||||
|
||||
// No days means only for this browser session
|
||||
WebUtil.createCookie = function (name, value, days) {
|
||||
export function createCookie (name, value, days) {
|
||||
"use strict";
|
||||
var date, expires;
|
||||
if (days) {
|
||||
|
@ -126,7 +85,7 @@ WebUtil.createCookie = function (name, value, days) {
|
|||
document.cookie = name + "=" + value + expires + "; path=/" + secure;
|
||||
};
|
||||
|
||||
WebUtil.readCookie = function (name, defaultValue) {
|
||||
export function readCookie (name, defaultValue) {
|
||||
"use strict";
|
||||
var nameEQ = name + "=",
|
||||
ca = document.cookie.split(';');
|
||||
|
@ -139,22 +98,24 @@ WebUtil.readCookie = function (name, defaultValue) {
|
|||
return (typeof defaultValue !== 'undefined') ? defaultValue : null;
|
||||
};
|
||||
|
||||
WebUtil.eraseCookie = function (name) {
|
||||
export function eraseCookie (name) {
|
||||
"use strict";
|
||||
WebUtil.createCookie(name, "", -1);
|
||||
createCookie(name, "", -1);
|
||||
};
|
||||
|
||||
/*
|
||||
* Setting handling.
|
||||
*/
|
||||
|
||||
WebUtil.initSettings = function (callback /*, ...callbackArgs */) {
|
||||
var settings = {};
|
||||
|
||||
export function initSettings (callback /*, ...callbackArgs */) {
|
||||
"use strict";
|
||||
var callbackArgs = Array.prototype.slice.call(arguments, 1);
|
||||
if (window.chrome && window.chrome.storage) {
|
||||
window.chrome.storage.sync.get(function (cfg) {
|
||||
WebUtil.settings = cfg;
|
||||
console.log(WebUtil.settings);
|
||||
settings = cfg;
|
||||
console.log(settings);
|
||||
if (callback) {
|
||||
callback.apply(this, callbackArgs);
|
||||
}
|
||||
|
@ -168,24 +129,24 @@ WebUtil.initSettings = function (callback /*, ...callbackArgs */) {
|
|||
};
|
||||
|
||||
// No days means only for this browser session
|
||||
WebUtil.writeSetting = function (name, value) {
|
||||
export function writeSetting (name, value) {
|
||||
"use strict";
|
||||
if (window.chrome && window.chrome.storage) {
|
||||
//console.log("writeSetting:", name, value);
|
||||
if (WebUtil.settings[name] !== value) {
|
||||
WebUtil.settings[name] = value;
|
||||
window.chrome.storage.sync.set(WebUtil.settings);
|
||||
if (settings[name] !== value) {
|
||||
settings[name] = value;
|
||||
window.chrome.storage.sync.set(settings);
|
||||
}
|
||||
} else {
|
||||
localStorage.setItem(name, value);
|
||||
}
|
||||
};
|
||||
|
||||
WebUtil.readSetting = function (name, defaultValue) {
|
||||
export function readSetting (name, defaultValue) {
|
||||
"use strict";
|
||||
var value;
|
||||
if (window.chrome && window.chrome.storage) {
|
||||
value = WebUtil.settings[name];
|
||||
value = settings[name];
|
||||
} else {
|
||||
value = localStorage.getItem(name);
|
||||
}
|
||||
|
@ -199,17 +160,17 @@ WebUtil.readSetting = function (name, defaultValue) {
|
|||
}
|
||||
};
|
||||
|
||||
WebUtil.eraseSetting = function (name) {
|
||||
export function eraseSetting (name) {
|
||||
"use strict";
|
||||
if (window.chrome && window.chrome.storage) {
|
||||
window.chrome.storage.sync.remove(name);
|
||||
delete WebUtil.settings[name];
|
||||
delete settings[name];
|
||||
} else {
|
||||
localStorage.removeItem(name);
|
||||
}
|
||||
};
|
||||
|
||||
WebUtil.injectParamIfMissing = function (path, param, value) {
|
||||
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;
|
||||
|
@ -239,73 +200,36 @@ WebUtil.injectParamIfMissing = function (path, param, value) {
|
|||
}
|
||||
};
|
||||
|
||||
// Dynamically load scripts without using document.write()
|
||||
// Reference: http://unixpapa.com/js/dyna.html
|
||||
//
|
||||
// Handles the case where load_scripts is invoked from a script that
|
||||
// itself is loaded via load_scripts. Once all scripts are loaded the
|
||||
// window.onscriptsloaded handler is called (if set).
|
||||
WebUtil.get_include_uri = function (root_dir) {
|
||||
return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI + root_dir + '/' : root_dir + '/';
|
||||
};
|
||||
WebUtil._loading_scripts = [];
|
||||
WebUtil._pending_scripts = [];
|
||||
WebUtil.load_scripts = function (files_by_dir) {
|
||||
"use strict";
|
||||
var head = document.getElementsByTagName('head')[0], script,
|
||||
ls = WebUtil._loading_scripts, ps = WebUtil._pending_scripts;
|
||||
// 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, resolve, reject) {
|
||||
// NB: IE11 doesn't support JSON as a responseType
|
||||
const req = new XMLHttpRequest();
|
||||
req.open('GET', path);
|
||||
|
||||
var loadFunc = function (e) {
|
||||
while (ls.length > 0 && (ls[0].readyState === 'loaded' ||
|
||||
ls[0].readyState === 'complete')) {
|
||||
// For IE, append the script to trigger execution
|
||||
var s = ls.shift();
|
||||
//console.log("loaded script: " + s.src);
|
||||
head.appendChild(s);
|
||||
req.onload = function () {
|
||||
if (req.status === 200) {
|
||||
try {
|
||||
var resObj = JSON.parse(req.responseText);
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
if (!this.readyState ||
|
||||
(Util.Engine.presto && this.readyState === 'loaded') ||
|
||||
this.readyState === 'complete') {
|
||||
if (ps.indexOf(this) >= 0) {
|
||||
this.onload = this.onreadystatechange = null;
|
||||
//console.log("completed script: " + this.src);
|
||||
ps.splice(ps.indexOf(this), 1);
|
||||
|
||||
// Call window.onscriptsload after last script loads
|
||||
if (ps.length === 0 && window.onscriptsload) {
|
||||
window.onscriptsload();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var root_dirs = Object.keys(files_by_dir);
|
||||
|
||||
for (var d = 0; d < root_dirs.length; d++) {
|
||||
var root_dir = root_dirs[d];
|
||||
var files = files_by_dir[root_dir];
|
||||
|
||||
for (var f = 0; f < files.length; f++) {
|
||||
script = document.createElement('script');
|
||||
script.type = 'text/javascript';
|
||||
script.src = WebUtil.get_include_uri(root_dir) + files[f];
|
||||
//console.log("loading script: " + script.src);
|
||||
script.onload = script.onreadystatechange = loadFunc;
|
||||
// In-order script execution tricks
|
||||
if (Util.Engine.trident) {
|
||||
// For IE wait until readyState is 'loaded' before
|
||||
// appending it which will trigger execution
|
||||
// http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
|
||||
ls.push(script);
|
||||
resolve(resObj);
|
||||
} else {
|
||||
// For webkit and firefox set async=false and append now
|
||||
// https://developer.mozilla.org/en-US/docs/HTML/Element/script
|
||||
script.async = false;
|
||||
head.appendChild(script);
|
||||
}
|
||||
ps.push(script);
|
||||
}
|
||||
reject(new Error("XHR got non-200 status while trying to load '" + path + "': " + req.status));
|
||||
}
|
||||
};
|
||||
|
||||
/* [module] export default WebUtil; */
|
||||
req.onerror = function (evt) {
|
||||
reject(new Error("XHR encountered an error while trying to load '" + path + "': " + evt.message));
|
||||
};
|
||||
|
||||
req.ontimeout = function (evt) {
|
||||
reject(new Error("XHR timed out while trying to load '" + path + "'"));
|
||||
};
|
||||
|
||||
req.send();
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
/*jslint white: false */
|
||||
/*global console */
|
||||
|
||||
var Base64 = {
|
||||
export default {
|
||||
/* Convert data (an array of integers) to a Base64 string. */
|
||||
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
|
||||
base64Pad : '=',
|
||||
|
@ -15,7 +15,7 @@ var Base64 = {
|
|||
encode: function (data) {
|
||||
"use strict";
|
||||
var result = '';
|
||||
var toBase64Table = Base64.toBase64Table;
|
||||
var toBase64Table = this.toBase64Table;
|
||||
var length = data.length;
|
||||
var lengthpad = (length % 3);
|
||||
// Convert every three bytes to 4 ascii characters.
|
||||
|
@ -63,8 +63,8 @@ var Base64 = {
|
|||
decode: function (data, offset) {
|
||||
"use strict";
|
||||
offset = typeof(offset) !== 'undefined' ? offset : 0;
|
||||
var toBinaryTable = Base64.toBinaryTable;
|
||||
var base64Pad = Base64.base64Pad;
|
||||
var toBinaryTable = this.toBinaryTable;
|
||||
var base64Pad = this.base64Pad;
|
||||
var result, result_length;
|
||||
var leftbits = 0; // number of bits decoded, but yet to be appended
|
||||
var leftdata = 0; // bits decoded, but yet to be appended
|
||||
|
@ -111,5 +111,3 @@ var Base64 = {
|
|||
return result;
|
||||
}
|
||||
}; /* End of Base64 namespace */
|
||||
|
||||
/* [module] export default Base64; */
|
||||
|
|
|
@ -77,7 +77,7 @@
|
|||
|
||||
/* jslint white: false */
|
||||
|
||||
/* [module] export default */ function DES(passwd) {
|
||||
export default function DES(passwd) {
|
||||
"use strict";
|
||||
|
||||
// Tables, permutations, S-boxes, etc.
|
||||
|
|
|
@ -10,12 +10,12 @@
|
|||
/*jslint browser: true, white: false */
|
||||
/*global Util, Base64, changeCursor */
|
||||
|
||||
/* [module]
|
||||
* import Util from "./util";
|
||||
* import Base64 from "./base64";
|
||||
*/
|
||||
import { Engine, browserSupportsCursorURIs as cursorURIsSupported } from './util/browsers.js';
|
||||
import { set_defaults, make_properties } from './util/properties.js';
|
||||
import * as Log from './util/logging.js';
|
||||
import Base64 from "./base64.js";
|
||||
|
||||
/* [module] export default */ function Display(defaults) {
|
||||
export default function Display(defaults) {
|
||||
this._drawCtx = null;
|
||||
this._c_forceCanvas = false;
|
||||
|
||||
|
@ -32,7 +32,7 @@
|
|||
this._tile_x = 0;
|
||||
this._tile_y = 0;
|
||||
|
||||
Util.set_defaults(this, defaults, {
|
||||
set_defaults(this, defaults, {
|
||||
'true_color': true,
|
||||
'colourMap': [],
|
||||
'scale': 1.0,
|
||||
|
@ -41,7 +41,7 @@
|
|||
"onFlush": function () {},
|
||||
});
|
||||
|
||||
Util.Debug(">> Display.constructor");
|
||||
Log.Debug(">> Display.constructor");
|
||||
|
||||
// The visible canvas
|
||||
if (!this._target) {
|
||||
|
@ -69,11 +69,11 @@
|
|||
right: this._backbuffer.width,
|
||||
bottom: this._backbuffer.height };
|
||||
|
||||
Util.Debug("User Agent: " + navigator.userAgent);
|
||||
if (Util.Engine.gecko) { Util.Debug("Browser: gecko " + Util.Engine.gecko); }
|
||||
if (Util.Engine.webkit) { Util.Debug("Browser: webkit " + Util.Engine.webkit); }
|
||||
if (Util.Engine.trident) { Util.Debug("Browser: trident " + Util.Engine.trident); }
|
||||
if (Util.Engine.presto) { Util.Debug("Browser: presto " + Util.Engine.presto); }
|
||||
Log.Debug("User Agent: " + navigator.userAgent);
|
||||
if (Engine.gecko) { Log.Debug("Browser: gecko " + Engine.gecko); }
|
||||
if (Engine.webkit) { Log.Debug("Browser: webkit " + Engine.webkit); }
|
||||
if (Engine.trident) { Log.Debug("Browser: trident " + Engine.trident); }
|
||||
if (Engine.presto) { Log.Debug("Browser: presto " + Engine.presto); }
|
||||
|
||||
this.clear();
|
||||
|
||||
|
@ -85,22 +85,19 @@
|
|||
}
|
||||
|
||||
if (this._prefer_js === null) {
|
||||
Util.Info("Prefering javascript operations");
|
||||
Log.Info("Prefering javascript operations");
|
||||
this._prefer_js = true;
|
||||
}
|
||||
|
||||
// Determine browser support for setting the cursor via data URI scheme
|
||||
if (this._cursor_uri || this._cursor_uri === null ||
|
||||
this._cursor_uri === undefined) {
|
||||
this._cursor_uri = Util.browserSupportsCursorURIs();
|
||||
this._cursor_uri = cursorURIsSupported();
|
||||
}
|
||||
|
||||
Util.Debug("<< Display.constructor");
|
||||
Log.Debug("<< Display.constructor");
|
||||
};
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var SUPPORTS_IMAGEDATA_CONSTRUCTOR = false;
|
||||
try {
|
||||
new ImageData(new Uint8ClampedArray(4), 1, 1);
|
||||
|
@ -109,7 +106,6 @@
|
|||
// ignore failure
|
||||
}
|
||||
|
||||
|
||||
Display.prototype = {
|
||||
// Public methods
|
||||
viewportChangePos: function (deltaX, deltaY) {
|
||||
|
@ -144,7 +140,7 @@
|
|||
if (deltaX === 0 && deltaY === 0) {
|
||||
return;
|
||||
}
|
||||
Util.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY);
|
||||
Log.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY);
|
||||
|
||||
vp.x += deltaX;
|
||||
vp.y += deltaY;
|
||||
|
@ -160,7 +156,7 @@
|
|||
typeof(width) === "undefined" ||
|
||||
typeof(height) === "undefined") {
|
||||
|
||||
Util.Debug("Setting viewport to full display region");
|
||||
Log.Debug("Setting viewport to full display region");
|
||||
width = this._fb_width;
|
||||
height = this._fb_height;
|
||||
}
|
||||
|
@ -528,7 +524,7 @@
|
|||
|
||||
changeCursor: function (pixels, mask, hotx, hoty, w, h) {
|
||||
if (this._cursor_uri === false) {
|
||||
Util.Warn("changeCursor called but no cursor data URI support");
|
||||
Log.Warn("changeCursor called but no cursor data URI support");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -743,7 +739,7 @@
|
|||
},
|
||||
};
|
||||
|
||||
Util.make_properties(Display, [
|
||||
make_properties(Display, [
|
||||
['target', 'wo', 'dom'], // Canvas element for rendering
|
||||
['context', 'ro', 'raw'], // Canvas 2D context for rendering (read-only)
|
||||
['logo', 'rw', 'raw'], // Logo to display when cleared: {"width": w, "height": h, "type": mime-type, "data": data}
|
||||
|
@ -869,4 +865,3 @@
|
|||
var url = 'data:image/x-icon;base64,' + Base64.encode(cur);
|
||||
target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
|
||||
};
|
||||
})();
|
||||
|
|
2435
core/inflator.js
2435
core/inflator.js
File diff suppressed because it is too large
Load Diff
|
@ -1,40 +0,0 @@
|
|||
var zlib = require('pako/lib/zlib/inflate.js');
|
||||
var ZStream = require('pako/lib/zlib/zstream.js');
|
||||
|
||||
function Inflate() {
|
||||
this.strm = new ZStream();
|
||||
this.chunkSize = 1024 * 10 * 10;
|
||||
this.strm.output = new Uint8Array(this.chunkSize);
|
||||
this.windowBits = 5;
|
||||
|
||||
zlib.inflateInit(this.strm, this.windowBits);
|
||||
};
|
||||
|
||||
Inflate.prototype = {
|
||||
inflate: function (data, flush, expected) {
|
||||
this.strm.input = data;
|
||||
this.strm.avail_in = this.strm.input.length;
|
||||
this.strm.next_in = 0;
|
||||
this.strm.next_out = 0;
|
||||
|
||||
// resize our output buffer if it's too small
|
||||
// (we could just use multiple chunks, but that would cause an extra
|
||||
// allocation each time to flatten the chunks)
|
||||
if (expected > this.chunkSize) {
|
||||
this.chunkSize = expected;
|
||||
this.strm.output = new Uint8Array(this.chunkSize);
|
||||
}
|
||||
|
||||
this.strm.avail_out = this.chunkSize;
|
||||
|
||||
zlib.inflate(this.strm, flush);
|
||||
|
||||
return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
|
||||
},
|
||||
|
||||
reset: function () {
|
||||
zlib.inflateReset(this.strm);
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = { Inflate: Inflate };
|
|
@ -8,25 +8,21 @@
|
|||
/*jslint browser: true, white: false */
|
||||
/*global window, Util */
|
||||
|
||||
/* [module]
|
||||
* import Util from "../util";
|
||||
* import KeyboardUtil from "./util";
|
||||
*/
|
||||
|
||||
/* [module] export */ var Keyboard;
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
import * as Log from '../util/logging.js';
|
||||
import { isTouchDevice } from '../util/browsers.js'
|
||||
import { setCapture, releaseCapture, stopEvent, getPointerEvent } from '../util/events.js';
|
||||
import { set_defaults, make_properties } from '../util/properties.js';
|
||||
import * as KeyboardUtil from "./util.js";
|
||||
|
||||
//
|
||||
// Keyboard event handler
|
||||
//
|
||||
|
||||
Keyboard = function (defaults) {
|
||||
const Keyboard = function (defaults) {
|
||||
this._keyDownList = []; // List of depressed keys
|
||||
// (even if they are happy)
|
||||
|
||||
Util.set_defaults(this, defaults, {
|
||||
set_defaults(this, defaults, {
|
||||
'target': document,
|
||||
'focused': true
|
||||
});
|
||||
|
@ -54,7 +50,7 @@
|
|||
|
||||
_handleRfbEvent: function (e) {
|
||||
if (this._onKeyPress) {
|
||||
Util.Debug("onKeyPress " + (e.type == 'keydown' ? "down" : "up") +
|
||||
Log.Debug("onKeyPress " + (e.type == 'keydown' ? "down" : "up") +
|
||||
", keysym: " + e.keysym.keysym + "(" + e.keysym.keyname + ")");
|
||||
this._onKeyPress(e);
|
||||
}
|
||||
|
@ -73,7 +69,7 @@
|
|||
|
||||
if (this._handler.keydown(e)) {
|
||||
// Suppress bubbling/default actions
|
||||
Util.stopEvent(e);
|
||||
stopEvent(e);
|
||||
} else {
|
||||
// Allow the event to bubble and become a keyPress event which
|
||||
// will have the character code translated
|
||||
|
@ -85,7 +81,7 @@
|
|||
|
||||
if (this._handler.keypress(e)) {
|
||||
// Suppress bubbling/default actions
|
||||
Util.stopEvent(e);
|
||||
stopEvent(e);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -94,20 +90,20 @@
|
|||
|
||||
if (this._handler.keyup(e)) {
|
||||
// Suppress bubbling/default actions
|
||||
Util.stopEvent(e);
|
||||
stopEvent(e);
|
||||
}
|
||||
},
|
||||
|
||||
_allKeysUp: function () {
|
||||
Util.Debug(">> Keyboard.allKeysUp");
|
||||
Log.Debug(">> Keyboard.allKeysUp");
|
||||
this._handler.releaseAll();
|
||||
Util.Debug("<< Keyboard.allKeysUp");
|
||||
Log.Debug("<< Keyboard.allKeysUp");
|
||||
},
|
||||
|
||||
// Public methods
|
||||
|
||||
grab: function () {
|
||||
//Util.Debug(">> Keyboard.grab");
|
||||
//Log.Debug(">> Keyboard.grab");
|
||||
var c = this._target;
|
||||
|
||||
c.addEventListener('keydown', this._eventHandlers.keydown);
|
||||
|
@ -117,11 +113,11 @@
|
|||
// Release (key up) if window loses focus
|
||||
window.addEventListener('blur', this._eventHandlers.blur);
|
||||
|
||||
//Util.Debug("<< Keyboard.grab");
|
||||
//Log.Debug("<< Keyboard.grab");
|
||||
},
|
||||
|
||||
ungrab: function () {
|
||||
//Util.Debug(">> Keyboard.ungrab");
|
||||
//Log.Debug(">> Keyboard.ungrab");
|
||||
var c = this._target;
|
||||
|
||||
c.removeEventListener('keydown', this._eventHandlers.keydown);
|
||||
|
@ -132,7 +128,7 @@
|
|||
// Release (key up) all keys that are in a down state
|
||||
this._allKeysUp();
|
||||
|
||||
//Util.Debug(">> Keyboard.ungrab");
|
||||
//Log.Debug(">> Keyboard.ungrab");
|
||||
},
|
||||
|
||||
sync: function (e) {
|
||||
|
@ -140,25 +136,21 @@
|
|||
}
|
||||
};
|
||||
|
||||
Util.make_properties(Keyboard, [
|
||||
make_properties(Keyboard, [
|
||||
['target', 'wo', 'dom'], // DOM element that captures keyboard input
|
||||
['focused', 'rw', 'bool'], // Capture and send key events
|
||||
|
||||
['onKeyPress', 'rw', 'func'] // Handler for key press/release
|
||||
]);
|
||||
})();
|
||||
|
||||
/* [module] export */ var Mouse;
|
||||
|
||||
(function () {
|
||||
Mouse = function (defaults) {
|
||||
const Mouse = function (defaults) {
|
||||
this._mouseCaptured = false;
|
||||
|
||||
this._doubleClickTimer = null;
|
||||
this._lastTouchPos = null;
|
||||
|
||||
// Configuration attributes
|
||||
Util.set_defaults(this, defaults, {
|
||||
set_defaults(this, defaults, {
|
||||
'target': document,
|
||||
'focused': true,
|
||||
'touchButton': 1
|
||||
|
@ -177,7 +169,7 @@
|
|||
// private methods
|
||||
_captureMouse: function () {
|
||||
// capturing the mouse ensures we get the mouseup event
|
||||
Util.setCapture(this._target);
|
||||
setCapture(this._target);
|
||||
|
||||
// some browsers give us mouseup events regardless,
|
||||
// so if we never captured the mouse, we can disregard the event
|
||||
|
@ -185,7 +177,7 @@
|
|||
},
|
||||
|
||||
_releaseMouse: function () {
|
||||
Util.releaseCapture();
|
||||
releaseCapture();
|
||||
this._mouseCaptured = false;
|
||||
},
|
||||
|
||||
|
@ -244,11 +236,11 @@
|
|||
}
|
||||
|
||||
if (this._onMouseButton) {
|
||||
Util.Debug("onMouseButton " + (down ? "down" : "up") +
|
||||
Log.Debug("onMouseButton " + (down ? "down" : "up") +
|
||||
", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
|
||||
this._onMouseButton(pos.x, pos.y, down, bmask);
|
||||
}
|
||||
Util.stopEvent(e);
|
||||
stopEvent(e);
|
||||
},
|
||||
|
||||
_handleMouseDown: function (e) {
|
||||
|
@ -290,7 +282,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
Util.stopEvent(e);
|
||||
stopEvent(e);
|
||||
},
|
||||
|
||||
_handleMouseMove: function (e) {
|
||||
|
@ -304,7 +296,7 @@
|
|||
if (this._onMouseMove) {
|
||||
this._onMouseMove(pos.x, pos.y);
|
||||
}
|
||||
Util.stopEvent(e);
|
||||
stopEvent(e);
|
||||
},
|
||||
|
||||
_handleMouseDisable: function (e) {
|
||||
|
@ -317,14 +309,13 @@
|
|||
* to listen on the document element instead.
|
||||
*/
|
||||
if (e.target == this._target) {
|
||||
//Util.Debug("mouse event disabled");
|
||||
Util.stopEvent(e);
|
||||
stopEvent(e);
|
||||
}
|
||||
},
|
||||
|
||||
// Return coordinates relative to target
|
||||
_getMousePosition: function(e) {
|
||||
e = Util.getPointerEvent(e);
|
||||
e = getPointerEvent(e);
|
||||
var bounds = this._target.getBoundingClientRect();
|
||||
var x, y;
|
||||
// Clip to target bounds
|
||||
|
@ -345,12 +336,11 @@
|
|||
return {x:x, y:y};
|
||||
},
|
||||
|
||||
|
||||
// Public methods
|
||||
grab: function () {
|
||||
var c = this._target;
|
||||
|
||||
if (Util.isTouchDevice) {
|
||||
if (isTouchDevice) {
|
||||
c.addEventListener('touchstart', this._eventHandlers.mousedown);
|
||||
window.addEventListener('touchend', this._eventHandlers.mouseup);
|
||||
c.addEventListener('touchend', this._eventHandlers.mouseup);
|
||||
|
@ -373,7 +363,7 @@
|
|||
ungrab: function () {
|
||||
var c = this._target;
|
||||
|
||||
if (Util.isTouchDevice) {
|
||||
if (isTouchDevice) {
|
||||
c.removeEventListener('touchstart', this._eventHandlers.mousedown);
|
||||
window.removeEventListener('touchend', this._eventHandlers.mouseup);
|
||||
c.removeEventListener('touchend', this._eventHandlers.mouseup);
|
||||
|
@ -391,7 +381,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
Util.make_properties(Mouse, [
|
||||
make_properties(Mouse, [
|
||||
['target', 'ro', 'dom'], // DOM element that captures mouse input
|
||||
['notify', 'ro', 'func'], // Function to call to notify whenever a mouse event is received
|
||||
['focused', 'rw', 'bool'], // Capture and send mouse clicks/movement
|
||||
|
@ -400,4 +390,5 @@
|
|||
['onMouseMove', 'rw', 'func'], // Handler for mouse movement
|
||||
['touchButton', 'rw', 'int'] // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
|
||||
]);
|
||||
})();
|
||||
|
||||
export { Keyboard, Mouse };
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
var KeyTable = {
|
||||
export default {
|
||||
XK_VoidSymbol: 0xffffff, /* Void symbol */
|
||||
|
||||
XK_BackSpace: 0xff08, /* Back space, back char */
|
||||
|
@ -378,5 +378,3 @@ var KeyTable = {
|
|||
XK_thorn: 0x00fe, /* U+00FE LATIN SMALL LETTER THORN */
|
||||
XK_ydiaeresis: 0x00ff, /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */
|
||||
};
|
||||
|
||||
/* [module] export default KeyTable; */
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,14 +1,7 @@
|
|||
/* [module]
|
||||
* import KeyTable from "./keysym";
|
||||
* import keysyms from "./keysymdef";
|
||||
*/
|
||||
import KeyTable from "./keysym.js";
|
||||
import keysyms from "./keysymdef.js";
|
||||
|
||||
var KeyboardUtil = {};
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
function substituteCodepoint(cp) {
|
||||
export function substituteCodepoint(cp) {
|
||||
// Any Unicode code points which do not have corresponding keysym entries
|
||||
// can be swapped out for another code point by adding them to this table
|
||||
var substitutions = {
|
||||
|
@ -35,7 +28,7 @@ var KeyboardUtil = {};
|
|||
}
|
||||
|
||||
// Return true if a modifier which is not the specified char modifier (and is not shift) is down
|
||||
function hasShortcutModifier(charModifier, currentModifiers) {
|
||||
export function hasShortcutModifier(charModifier, currentModifiers) {
|
||||
var mods = {};
|
||||
for (var key in currentModifiers) {
|
||||
if (parseInt(key) !== KeyTable.XK_Shift_L) {
|
||||
|
@ -58,7 +51,7 @@ var KeyboardUtil = {};
|
|||
}
|
||||
|
||||
// Return true if the specified char modifier is currently down
|
||||
function hasCharModifier(charModifier, currentModifiers) {
|
||||
export function hasCharModifier(charModifier, currentModifiers) {
|
||||
if (charModifier.length === 0) { return false; }
|
||||
|
||||
for (var i = 0; i < charModifier.length; ++i) {
|
||||
|
@ -71,7 +64,7 @@ var KeyboardUtil = {};
|
|||
|
||||
// Helper object tracking modifier key state
|
||||
// and generates fake key events to compensate if it gets out of sync
|
||||
function ModifierSync(charModifier) {
|
||||
export function ModifierSync(charModifier) {
|
||||
if (!charModifier) {
|
||||
if (isMac()) {
|
||||
// on Mac, Option (AKA Alt) is used as a char modifier
|
||||
|
@ -157,7 +150,7 @@ var KeyboardUtil = {};
|
|||
|
||||
// Get a key ID from a keyboard event
|
||||
// May be a string or an integer depending on the available properties
|
||||
function getKey(evt){
|
||||
export function getKey(evt){
|
||||
if ('keyCode' in evt && 'key' in evt) {
|
||||
return evt.key + ':' + evt.keyCode;
|
||||
}
|
||||
|
@ -171,7 +164,7 @@ var KeyboardUtil = {};
|
|||
|
||||
// Get the most reliable keysym value we can get from a key event
|
||||
// if char/charCode is available, prefer those, otherwise fall back to key/keyCode/which
|
||||
function getKeysym(evt){
|
||||
export function getKeysym(evt){
|
||||
var codepoint;
|
||||
if (evt.char && evt.char.length === 1) {
|
||||
codepoint = evt.char.charCodeAt();
|
||||
|
@ -201,7 +194,7 @@ var KeyboardUtil = {};
|
|||
|
||||
// Given a keycode, try to predict which keysym it might be.
|
||||
// If the keycode is unknown, null is returned.
|
||||
function keysymFromKeyCode(keycode, shiftPressed) {
|
||||
export function keysymFromKeyCode(keycode, shiftPressed) {
|
||||
if (typeof(keycode) !== 'number') {
|
||||
return null;
|
||||
}
|
||||
|
@ -236,7 +229,7 @@ var KeyboardUtil = {};
|
|||
|
||||
// if the key is a known non-character key (any key which doesn't generate character data)
|
||||
// return its keysym value. Otherwise return null
|
||||
function nonCharacterKey(evt) {
|
||||
export function nonCharacterKey(evt) {
|
||||
// evt.key not implemented yet
|
||||
if (!evt.keyCode) { return null; }
|
||||
var keycode = evt.keyCode;
|
||||
|
@ -278,17 +271,7 @@ var KeyboardUtil = {};
|
|||
}
|
||||
}
|
||||
|
||||
KeyboardUtil.hasShortcutModifier = hasShortcutModifier;
|
||||
KeyboardUtil.hasCharModifier = hasCharModifier;
|
||||
KeyboardUtil.ModifierSync = ModifierSync;
|
||||
KeyboardUtil.getKey = getKey;
|
||||
KeyboardUtil.getKeysym = getKeysym;
|
||||
KeyboardUtil.keysymFromKeyCode = keysymFromKeyCode;
|
||||
KeyboardUtil.nonCharacterKey = nonCharacterKey;
|
||||
KeyboardUtil.substituteCodepoint = substituteCodepoint;
|
||||
})();
|
||||
|
||||
KeyboardUtil.QEMUKeyEventDecoder = function(modifierState, next) {
|
||||
export function QEMUKeyEventDecoder (modifierState, next) {
|
||||
"use strict";
|
||||
|
||||
function sendAll(evts) {
|
||||
|
@ -334,7 +317,7 @@ KeyboardUtil.QEMUKeyEventDecoder = function(modifierState, next) {
|
|||
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
|
||||
var isShift = evt.keyCode === 0x10 || evt.key === 'Shift';
|
||||
|
||||
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!KeyboardUtil.nonCharacterKey(evt));
|
||||
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!nonCharacterKey(evt));
|
||||
|
||||
next(result);
|
||||
return suppress;
|
||||
|
@ -358,7 +341,7 @@ KeyboardUtil.QEMUKeyEventDecoder = function(modifierState, next) {
|
|||
};
|
||||
};
|
||||
|
||||
KeyboardUtil.TrackQEMUKeyState = function(next) {
|
||||
export function TrackQEMUKeyState (next) {
|
||||
"use strict";
|
||||
var state = [];
|
||||
|
||||
|
@ -426,7 +409,7 @@ KeyboardUtil.TrackQEMUKeyState = function(next) {
|
|||
// - marks each event with an 'escape' property if a modifier was down which should be "escaped"
|
||||
// - generates a "stall" event in cases where it might be necessary to wait and see if a keypress event follows a keydown
|
||||
// This information is collected into an object which is passed to the next() function. (one call per event)
|
||||
KeyboardUtil.KeyEventDecoder = function(modifierState, next) {
|
||||
export function KeyEventDecoder (modifierState, next) {
|
||||
"use strict";
|
||||
function sendAll(evts) {
|
||||
for (var i = 0; i < evts.length; ++i) {
|
||||
|
@ -435,18 +418,18 @@ KeyboardUtil.KeyEventDecoder = function(modifierState, next) {
|
|||
}
|
||||
function process(evt, type) {
|
||||
var result = {type: type};
|
||||
var keyId = KeyboardUtil.getKey(evt);
|
||||
var keyId = getKey(evt);
|
||||
if (keyId) {
|
||||
result.keyId = keyId;
|
||||
}
|
||||
|
||||
var keysym = KeyboardUtil.getKeysym(evt);
|
||||
var keysym = getKeysym(evt);
|
||||
|
||||
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
|
||||
// Is this a case where we have to decide on the keysym right away, rather than waiting for the keypress?
|
||||
// "special" keys like enter, tab or backspace don't send keypress events,
|
||||
// and some browsers don't send keypresses at all if a modifier is down
|
||||
if (keysym && (type !== 'keydown' || KeyboardUtil.nonCharacterKey(evt) || hasModifier)) {
|
||||
if (keysym && (type !== 'keydown' || nonCharacterKey(evt) || hasModifier)) {
|
||||
result.keysym = keysym;
|
||||
}
|
||||
|
||||
|
@ -455,11 +438,11 @@ KeyboardUtil.KeyEventDecoder = function(modifierState, next) {
|
|||
// Should we prevent the browser from handling the event?
|
||||
// Doing so on a keydown (in most browsers) prevents keypress from being generated
|
||||
// so only do that if we have to.
|
||||
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!KeyboardUtil.nonCharacterKey(evt));
|
||||
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!nonCharacterKey(evt));
|
||||
|
||||
// If a char modifier is down on a keydown, we need to insert a stall,
|
||||
// so VerifyCharModifier knows to wait and see if a keypress is comnig
|
||||
var stall = type === 'keydown' && modifierState.activeCharModifier() && !KeyboardUtil.nonCharacterKey(evt);
|
||||
var stall = type === 'keydown' && modifierState.activeCharModifier() && !nonCharacterKey(evt);
|
||||
|
||||
// if a char modifier is pressed, get the keys it consists of (on Windows, AltGr is equivalent to Ctrl+Alt)
|
||||
var active = modifierState.activeCharModifier();
|
||||
|
@ -513,7 +496,7 @@ KeyboardUtil.KeyEventDecoder = function(modifierState, next) {
|
|||
// so when used with the '2' key, Ctrl-Alt counts as a char modifier (and should be escaped), but when used with 'D', it does not.
|
||||
// The only way we can distinguish these cases is to wait and see if a keypress event arrives
|
||||
// When we receive a "stall" event, wait a few ms before processing the next keydown. If a keypress has also arrived, merge the two
|
||||
KeyboardUtil.VerifyCharModifier = function(next) {
|
||||
export function VerifyCharModifier (next) {
|
||||
"use strict";
|
||||
var queue = [];
|
||||
var timer = null;
|
||||
|
@ -570,7 +553,7 @@ KeyboardUtil.VerifyCharModifier = function(next) {
|
|||
// in some cases, a single key may produce multiple keysyms, so the corresponding keyup event must release all of these chars
|
||||
// key repeat events should be merged into a single entry.
|
||||
// Because we can't always identify which entry a keydown or keyup event corresponds to, we sometimes have to guess
|
||||
KeyboardUtil.TrackKeyState = function(next) {
|
||||
export function TrackKeyState (next) {
|
||||
"use strict";
|
||||
var state = [];
|
||||
|
||||
|
@ -654,7 +637,7 @@ KeyboardUtil.TrackKeyState = function(next) {
|
|||
|
||||
// Handles "escaping" of modifiers: if a char modifier is used to produce a keysym (such as AltGr-2 to generate an @),
|
||||
// then the modifier must be "undone" before sending the @, and "redone" afterwards.
|
||||
KeyboardUtil.EscapeModifiers = function(next) {
|
||||
export function EscapeModifiers (next) {
|
||||
"use strict";
|
||||
return function(evt) {
|
||||
if (evt.type !== 'keydown' || evt.escape === undefined) {
|
||||
|
@ -675,5 +658,3 @@ KeyboardUtil.EscapeModifiers = function(next) {
|
|||
/* jshint shadow: false */
|
||||
};
|
||||
};
|
||||
|
||||
/* [module] export default KeyboardUtil; */
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
var XtScancode = {
|
||||
export default {
|
||||
"Escape": 0x0001,
|
||||
"Digit1": 0x0002,
|
||||
"Digit2": 0x0003,
|
||||
|
@ -147,5 +147,3 @@ var XtScancode = {
|
|||
"LaunchMail": 0xE06C,
|
||||
"MediaSelect": 0xE06D,
|
||||
};
|
||||
|
||||
/* [module] export default XtScancode */
|
||||
|
|
212
core/rfb.js
212
core/rfb.js
|
@ -10,21 +10,23 @@
|
|||
* (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
|
||||
*/
|
||||
|
||||
/* [module]
|
||||
* import Util from "./util";
|
||||
* import Display from "./display";
|
||||
* import { Keyboard, Mouse } from "./input/devices"
|
||||
* import Websock from "./websock"
|
||||
* import Base64 from "./base64";
|
||||
* import DES from "./des";
|
||||
* import KeyTable from "./input/keysym";
|
||||
* import XtScancode from "./input/xtscancodes";
|
||||
* import Inflator from "./inflator.mod";
|
||||
*/
|
||||
import * as Log from './util/logging.js';
|
||||
import _ from './util/localization.js';
|
||||
import { decodeUTF8 } from './util/strings.js';
|
||||
import { set_defaults, make_properties } from './util/properties.js';
|
||||
import Display from "./display.js";
|
||||
import { Keyboard, Mouse } from "./input/devices.js";
|
||||
import Websock from "./websock.js";
|
||||
import Base64 from "./base64.js";
|
||||
import DES from "./des.js";
|
||||
import KeyTable from "./input/keysym.js";
|
||||
import XtScancode from "./input/xtscancodes.js";
|
||||
import Inflator from "./inflator.js";
|
||||
|
||||
/*jslint white: false, browser: true */
|
||||
/*global window, Util, Display, Keyboard, Mouse, Websock, Websock_native, Base64, DES, KeyTable, Inflator, XtScancode */
|
||||
|
||||
/* [module] export default */ function RFB(defaults) {
|
||||
export default function RFB(defaults) {
|
||||
"use strict";
|
||||
if (!defaults) {
|
||||
defaults = {};
|
||||
|
@ -144,7 +146,7 @@
|
|||
this._qemuExtKeyEventSupported = false;
|
||||
|
||||
// set the default value on user-facing properties
|
||||
Util.set_defaults(this, defaults, {
|
||||
set_defaults(this, defaults, {
|
||||
'target': 'null', // VNC display rendering Canvas object
|
||||
'focusContainer': document, // DOM element that captures keyboard input
|
||||
'encrypt': false, // Use TLS/SSL/wss encryption
|
||||
|
@ -173,7 +175,7 @@
|
|||
});
|
||||
|
||||
// main setup
|
||||
Util.Debug(">> RFB.constructor");
|
||||
Log.Debug(">> RFB.constructor");
|
||||
|
||||
// populate encHandlers with bound versions
|
||||
Object.keys(RFB.encodingHandlers).forEach(function (encName) {
|
||||
|
@ -193,7 +195,7 @@
|
|||
this._display = new Display({target: this._target,
|
||||
onFlush: this._onFlush.bind(this)});
|
||||
} catch (exc) {
|
||||
Util.Error("Display exception: " + exc);
|
||||
Log.Error("Display exception: " + exc);
|
||||
throw exc;
|
||||
}
|
||||
|
||||
|
@ -211,13 +213,13 @@
|
|||
if ((this._rfb_connection_state === 'connecting') &&
|
||||
(this._rfb_init_state === '')) {
|
||||
this._rfb_init_state = 'ProtocolVersion';
|
||||
Util.Debug("Starting VNC handshake");
|
||||
Log.Debug("Starting VNC handshake");
|
||||
} else {
|
||||
this._fail("Unexpected server connection");
|
||||
}
|
||||
}.bind(this));
|
||||
this._sock.on('close', function (e) {
|
||||
Util.Warn("WebSocket on-close event");
|
||||
Log.Warn("WebSocket on-close event");
|
||||
var msg = "";
|
||||
if (e.code) {
|
||||
msg = " (code: " + e.code;
|
||||
|
@ -250,21 +252,18 @@
|
|||
this._sock.off('close');
|
||||
}.bind(this));
|
||||
this._sock.on('error', function (e) {
|
||||
Util.Warn("WebSocket on-error event");
|
||||
Log.Warn("WebSocket on-error event");
|
||||
});
|
||||
|
||||
this._init_vars();
|
||||
this._cleanup();
|
||||
|
||||
var rmode = this._display.get_render_mode();
|
||||
Util.Info("Using native WebSockets, render mode: " + rmode);
|
||||
Log.Info("Using native WebSockets, render mode: " + rmode);
|
||||
|
||||
Util.Debug("<< RFB.constructor");
|
||||
Log.Debug("<< RFB.constructor");
|
||||
};
|
||||
|
||||
(function() {
|
||||
var _ = Util.Localisation.get;
|
||||
|
||||
RFB.prototype = {
|
||||
// Public methods
|
||||
connect: function (host, port, password, path) {
|
||||
|
@ -297,7 +296,7 @@
|
|||
|
||||
sendCtrlAltDel: function () {
|
||||
if (this._rfb_connection_state !== 'connected' || this._view_only) { return false; }
|
||||
Util.Info("Sending Ctrl-Alt-Del");
|
||||
Log.Info("Sending Ctrl-Alt-Del");
|
||||
|
||||
RFB.messages.keyEvent(this._sock, KeyTable.XK_Control_L, 1);
|
||||
RFB.messages.keyEvent(this._sock, KeyTable.XK_Alt_L, 1);
|
||||
|
@ -310,7 +309,7 @@
|
|||
|
||||
xvpOp: function (ver, op) {
|
||||
if (this._rfb_xvp_ver < ver) { return false; }
|
||||
Util.Info("Sending XVP operation " + op + " (version " + ver + ")");
|
||||
Log.Info("Sending XVP operation " + op + " (version " + ver + ")");
|
||||
this._sock.send_string("\xFA\x00" + String.fromCharCode(ver) + String.fromCharCode(op));
|
||||
return true;
|
||||
},
|
||||
|
@ -332,10 +331,10 @@
|
|||
sendKey: function (keysym, down) {
|
||||
if (this._rfb_connection_state !== 'connected' || this._view_only) { return false; }
|
||||
if (typeof down !== 'undefined') {
|
||||
Util.Info("Sending keysym (" + (down ? "down" : "up") + "): " + keysym);
|
||||
Log.Info("Sending keysym (" + (down ? "down" : "up") + "): " + keysym);
|
||||
RFB.messages.keyEvent(this._sock, keysym, down ? 1 : 0);
|
||||
} else {
|
||||
Util.Info("Sending keysym (down + up): " + keysym);
|
||||
Log.Info("Sending keysym (down + up): " + keysym);
|
||||
RFB.messages.keyEvent(this._sock, keysym, 1);
|
||||
RFB.messages.keyEvent(this._sock, keysym, 0);
|
||||
}
|
||||
|
@ -343,9 +342,7 @@
|
|||
},
|
||||
|
||||
clipboardPasteFrom: function (text) {
|
||||
if (this._rfb_connection_state !== 'connected' || this._view_only) {
|
||||
return;
|
||||
}
|
||||
if (this._rfb_connection_state !== 'connected' || this._view_only) { return; }
|
||||
RFB.messages.clientCutText(this._sock, text);
|
||||
},
|
||||
|
||||
|
@ -371,7 +368,7 @@
|
|||
// Private methods
|
||||
|
||||
_connect: function () {
|
||||
Util.Debug(">> RFB.connect");
|
||||
Log.Debug(">> RFB.connect");
|
||||
this._init_vars();
|
||||
|
||||
var uri;
|
||||
|
@ -382,7 +379,7 @@
|
|||
}
|
||||
|
||||
uri += '://' + this._rfb_host + ':' + this._rfb_port + '/' + this._rfb_path;
|
||||
Util.Info("connecting to " + uri);
|
||||
Log.Info("connecting to " + uri);
|
||||
|
||||
try {
|
||||
// WebSocket.onopen transitions to the RFB init states
|
||||
|
@ -395,15 +392,15 @@
|
|||
}
|
||||
}
|
||||
|
||||
Util.Debug("<< RFB.connect");
|
||||
Log.Debug("<< RFB.connect");
|
||||
},
|
||||
|
||||
_disconnect: function () {
|
||||
Util.Debug(">> RFB.disconnect");
|
||||
Log.Debug(">> RFB.disconnect");
|
||||
this._cleanup();
|
||||
this._sock.close();
|
||||
this._print_stats();
|
||||
Util.Debug("<< RFB.disconnect");
|
||||
Log.Debug("<< RFB.disconnect");
|
||||
},
|
||||
|
||||
_init_vars: function () {
|
||||
|
@ -424,24 +421,24 @@
|
|||
}
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
this._FBU.zlibs[i] = new Inflator.Inflate();
|
||||
this._FBU.zlibs[i] = new Inflator();
|
||||
}
|
||||
},
|
||||
|
||||
_print_stats: function () {
|
||||
Util.Info("Encoding stats for this connection:");
|
||||
Log.Info("Encoding stats for this connection:");
|
||||
var i, s;
|
||||
for (i = 0; i < this._encodings.length; i++) {
|
||||
s = this._encStats[this._encodings[i][1]];
|
||||
if (s[0] + s[1] > 0) {
|
||||
Util.Info(" " + this._encodings[i][0] + ": " + s[0] + " rects");
|
||||
Log.Info(" " + this._encodings[i][0] + ": " + s[0] + " rects");
|
||||
}
|
||||
}
|
||||
|
||||
Util.Info("Encoding stats since page load:");
|
||||
Log.Info("Encoding stats since page load:");
|
||||
for (i = 0; i < this._encodings.length; i++) {
|
||||
s = this._encStats[this._encodings[i][1]];
|
||||
Util.Info(" " + this._encodings[i][0] + ": " + s[1] + " rects");
|
||||
Log.Info(" " + this._encodings[i][0] + ": " + s[1] + " rects");
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -449,7 +446,7 @@
|
|||
if (!this._view_only) { this._keyboard.ungrab(); }
|
||||
if (!this._view_only) { this._mouse.ungrab(); }
|
||||
this._display.defaultCursor();
|
||||
if (Util.get_logging() !== 'debug') {
|
||||
if (Log.get_logging() !== 'debug') {
|
||||
// Show noVNC logo on load and when disconnected, unless in
|
||||
// debug mode
|
||||
this._display.clear();
|
||||
|
@ -467,13 +464,13 @@
|
|||
var oldstate = this._rfb_connection_state;
|
||||
|
||||
if (state === oldstate) {
|
||||
Util.Debug("Already in state '" + state + "', ignoring");
|
||||
Log.Debug("Already in state '" + state + "', ignoring");
|
||||
return;
|
||||
}
|
||||
|
||||
// The 'disconnected' state is permanent for each RFB object
|
||||
if (oldstate === 'disconnected') {
|
||||
Util.Error("Tried changing state of a disconnected RFB object");
|
||||
Log.Error("Tried changing state of a disconnected RFB object");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -481,7 +478,7 @@
|
|||
switch (state) {
|
||||
case 'connected':
|
||||
if (oldstate !== 'connecting') {
|
||||
Util.Error("Bad transition to connected state, " +
|
||||
Log.Error("Bad transition to connected state, " +
|
||||
"previous connection state: " + oldstate);
|
||||
return;
|
||||
}
|
||||
|
@ -489,7 +486,7 @@
|
|||
|
||||
case 'disconnected':
|
||||
if (oldstate !== 'disconnecting') {
|
||||
Util.Error("Bad transition to disconnected state, " +
|
||||
Log.Error("Bad transition to disconnected state, " +
|
||||
"previous connection state: " + oldstate);
|
||||
return;
|
||||
}
|
||||
|
@ -497,7 +494,7 @@
|
|||
|
||||
case 'connecting':
|
||||
if (oldstate !== '') {
|
||||
Util.Error("Bad transition to connecting state, " +
|
||||
Log.Error("Bad transition to connecting state, " +
|
||||
"previous connection state: " + oldstate);
|
||||
return;
|
||||
}
|
||||
|
@ -505,14 +502,14 @@
|
|||
|
||||
case 'disconnecting':
|
||||
if (oldstate !== 'connected' && oldstate !== 'connecting') {
|
||||
Util.Error("Bad transition to disconnecting state, " +
|
||||
Log.Error("Bad transition to disconnecting state, " +
|
||||
"previous connection state: " + oldstate);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Util.Error("Unknown connection state: " + state);
|
||||
Log.Error("Unknown connection state: " + state);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -522,10 +519,10 @@
|
|||
this._onUpdateState(this, state, oldstate);
|
||||
|
||||
var smsg = "New state '" + state + "', was '" + oldstate + "'.";
|
||||
Util.Debug(smsg);
|
||||
Log.Debug(smsg);
|
||||
|
||||
if (this._disconnTimer && state !== 'disconnecting') {
|
||||
Util.Debug("Clearing disconnect timer");
|
||||
Log.Debug("Clearing disconnect timer");
|
||||
clearTimeout(this._disconnTimer);
|
||||
this._disconnTimer = null;
|
||||
|
||||
|
@ -572,16 +569,16 @@
|
|||
}
|
||||
switch (this._rfb_connection_state) {
|
||||
case 'disconnecting':
|
||||
Util.Error("Failed when disconnecting: " + fullmsg);
|
||||
Log.Error("Failed when disconnecting: " + fullmsg);
|
||||
break;
|
||||
case 'connected':
|
||||
Util.Error("Failed while connected: " + fullmsg);
|
||||
Log.Error("Failed while connected: " + fullmsg);
|
||||
break;
|
||||
case 'connecting':
|
||||
Util.Error("Failed when connecting: " + fullmsg);
|
||||
Log.Error("Failed when connecting: " + fullmsg);
|
||||
break;
|
||||
default:
|
||||
Util.Error("RFB failure: " + fullmsg);
|
||||
Log.Error("RFB failure: " + fullmsg);
|
||||
break;
|
||||
}
|
||||
this._rfb_disconnect_reason = msg; //This is sent to the UI
|
||||
|
@ -606,10 +603,10 @@
|
|||
case 'normal':
|
||||
case 'warn':
|
||||
case 'error':
|
||||
Util.Debug("Notification[" + level + "]:" + msg);
|
||||
Log.Debug("Notification[" + level + "]:" + msg);
|
||||
break;
|
||||
default:
|
||||
Util.Error("Invalid notification level: " + level);
|
||||
Log.Error("Invalid notification level: " + level);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -622,13 +619,13 @@
|
|||
|
||||
_handle_message: function () {
|
||||
if (this._sock.rQlen() === 0) {
|
||||
Util.Warn("handle_message called on an empty receive queue");
|
||||
Log.Warn("handle_message called on an empty receive queue");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this._rfb_connection_state) {
|
||||
case 'disconnected':
|
||||
Util.Error("Got data while disconnected");
|
||||
Log.Error("Got data while disconnected");
|
||||
break;
|
||||
case 'connected':
|
||||
while (true) {
|
||||
|
@ -659,7 +656,7 @@
|
|||
var keysym = keyevent.keysym;
|
||||
RFB.messages.QEMUExtendedKeyEvent(this._sock, keysym, down, scancode);
|
||||
} else {
|
||||
Util.Error('Unable to find a xt scancode for code = ' + keyevent.code);
|
||||
Log.Error('Unable to find a xt scancode for code = ' + keyevent.code);
|
||||
}
|
||||
} else {
|
||||
keysym = keyevent.keysym.keysym;
|
||||
|
@ -735,7 +732,7 @@
|
|||
}
|
||||
|
||||
var sversion = this._sock.rQshiftStr(12).substr(4, 7);
|
||||
Util.Info("Server ProtocolVersion: " + sversion);
|
||||
Log.Info("Server ProtocolVersion: " + sversion);
|
||||
var is_repeater = 0;
|
||||
switch (sversion) {
|
||||
case "000.000": // UltraVNC repeater
|
||||
|
@ -776,12 +773,23 @@
|
|||
var cversion = "00" + parseInt(this._rfb_version, 10) +
|
||||
".00" + ((this._rfb_version * 10) % 10);
|
||||
this._sock.send_string("RFB " + cversion + "\n");
|
||||
Util.Debug('Sent ProtocolVersion: ' + cversion);
|
||||
Log.Debug('Sent ProtocolVersion: ' + cversion);
|
||||
|
||||
this._rfb_init_state = 'Security';
|
||||
},
|
||||
|
||||
_negotiate_security: function () {
|
||||
// Polyfill since IE and PhantomJS doesn't have
|
||||
// TypedArray.includes()
|
||||
function includes(item, array) {
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
if (array[i] === item) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._rfb_version >= 3.7) {
|
||||
// Server sends supported list, client decides
|
||||
var num_types = this._sock.rQshift8();
|
||||
|
@ -795,18 +803,7 @@
|
|||
}
|
||||
|
||||
var types = this._sock.rQshiftBytes(num_types);
|
||||
Util.Debug("Server security types: " + types);
|
||||
|
||||
// Polyfill since IE and PhantomJS doesn't have
|
||||
// TypedArray.includes()
|
||||
function includes(item, array) {
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
if (array[i] === item) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
Log.Debug("Server security types: " + types);
|
||||
|
||||
// Look for each auth in preferred order
|
||||
this._rfb_auth_scheme = 0;
|
||||
|
@ -831,7 +828,7 @@
|
|||
}
|
||||
|
||||
this._rfb_init_state = 'Authentication';
|
||||
Util.Debug('Authenticating using scheme: ' + this._rfb_auth_scheme);
|
||||
Log.Debug('Authenticating using scheme: ' + this._rfb_auth_scheme);
|
||||
|
||||
return this._init_msg(); // jump to authentication
|
||||
},
|
||||
|
@ -1000,7 +997,7 @@
|
|||
switch (this._sock.rQshift32()) {
|
||||
case 0: // OK
|
||||
this._rfb_init_state = 'ClientInitialisation';
|
||||
Util.Debug('Authentication OK');
|
||||
Log.Debug('Authentication OK');
|
||||
return this._init_msg();
|
||||
case 1: // failed
|
||||
if (this._rfb_version >= 3.8) {
|
||||
|
@ -1048,7 +1045,7 @@
|
|||
/* Connection name/title */
|
||||
var name_length = this._sock.rQshift32();
|
||||
if (this._sock.rQwait('server init name', name_length, 24)) { return false; }
|
||||
this._fb_name = Util.decodeUTF8(this._sock.rQshiftStr(name_length));
|
||||
this._fb_name = decodeUTF8(this._sock.rQshiftStr(name_length));
|
||||
|
||||
if (this._rfb_tightvnc) {
|
||||
if (this._sock.rQwait('TightVNC extended server init header', 8, 24 + name_length)) { return false; }
|
||||
|
@ -1076,7 +1073,7 @@
|
|||
|
||||
// NB(directxman12): these are down here so that we don't run them multiple times
|
||||
// if we backtrack
|
||||
Util.Info("Screen: " + this._fb_width + "x" + this._fb_height +
|
||||
Log.Info("Screen: " + this._fb_width + "x" + this._fb_height +
|
||||
", bpp: " + bpp + ", depth: " + depth +
|
||||
", big_endian: " + big_endian +
|
||||
", true_color: " + true_color +
|
||||
|
@ -1088,22 +1085,22 @@
|
|||
", blue_shift: " + blue_shift);
|
||||
|
||||
if (big_endian !== 0) {
|
||||
Util.Warn("Server native endian is not little endian");
|
||||
Log.Warn("Server native endian is not little endian");
|
||||
}
|
||||
|
||||
if (red_shift !== 16) {
|
||||
Util.Warn("Server native red-shift is not 16");
|
||||
Log.Warn("Server native red-shift is not 16");
|
||||
}
|
||||
|
||||
if (blue_shift !== 0) {
|
||||
Util.Warn("Server native blue-shift is not 0");
|
||||
Log.Warn("Server native blue-shift is not 0");
|
||||
}
|
||||
|
||||
// we're past the point where we could backtrack, so it's safe to call this
|
||||
this._onDesktopName(this, this._fb_name);
|
||||
|
||||
if (this._true_color && this._fb_name === "Intel(r) AMT KVM") {
|
||||
Util.Warn("Intel AMT KVM only supports 8/16 bit depths. Disabling true color");
|
||||
Log.Warn("Intel AMT KVM only supports 8/16 bit depths. Disabling true color");
|
||||
this._true_color = false;
|
||||
}
|
||||
|
||||
|
@ -1170,7 +1167,7 @@
|
|||
},
|
||||
|
||||
_handle_set_colour_map_msg: function () {
|
||||
Util.Debug("SetColorMapEntries");
|
||||
Log.Debug("SetColorMapEntries");
|
||||
this._sock.rQskip8(); // Padding
|
||||
|
||||
var first_colour = this._sock.rQshift16();
|
||||
|
@ -1183,15 +1180,14 @@
|
|||
var blue = parseInt(this._sock.rQshift16() / 256, 10);
|
||||
this._display.set_colourMap([blue, green, red], first_colour + c);
|
||||
}
|
||||
Util.Debug("colourMap: " + this._display.get_colourMap());
|
||||
Util.Info("Registered " + num_colours + " colourMap entries");
|
||||
Log.Debug("colourMap: " + this._display.get_colourMap());
|
||||
Log.Info("Registered " + num_colours + " colourMap entries");
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_handle_server_cut_text: function () {
|
||||
Util.Debug("ServerCutText");
|
||||
if (this._view_only) { return true; }
|
||||
Log.Debug("ServerCutText");
|
||||
|
||||
if (this._sock.rQwait("ServerCutText header", 7, 1)) { return false; }
|
||||
this._sock.rQskipBytes(3); // Padding
|
||||
|
@ -1199,6 +1195,9 @@
|
|||
if (this._sock.rQwait("ServerCutText", length, 8)) { return false; }
|
||||
|
||||
var text = this._sock.rQshiftStr(length);
|
||||
|
||||
if (this._view_only) { return true; }
|
||||
|
||||
this._onClipboard(this, text);
|
||||
|
||||
return true;
|
||||
|
@ -1213,7 +1212,7 @@
|
|||
if (this._sock.rQwait("ServerFence payload", length, 9)) { return false; }
|
||||
|
||||
if (length > 64) {
|
||||
Util.Warn("Bad payload length (" + length + ") in fence response");
|
||||
Log.Warn("Bad payload length (" + length + ") in fence response");
|
||||
length = 64;
|
||||
}
|
||||
|
||||
|
@ -1255,12 +1254,12 @@
|
|||
|
||||
switch (xvp_msg) {
|
||||
case 0: // XVP_FAIL
|
||||
Util.Error("Operation Failed");
|
||||
Log.Error("Operation Failed");
|
||||
this._notification("XVP Operation Failed", 'error');
|
||||
break;
|
||||
case 1: // XVP_INIT
|
||||
this._rfb_xvp_ver = xvp_ver;
|
||||
Util.Info("XVP extensions enabled (version " + this._rfb_xvp_ver + ")");
|
||||
Log.Info("XVP extensions enabled (version " + this._rfb_xvp_ver + ")");
|
||||
this._onXvpInit(this._rfb_xvp_ver);
|
||||
break;
|
||||
default:
|
||||
|
@ -1294,7 +1293,7 @@
|
|||
return this._handle_set_colour_map_msg();
|
||||
|
||||
case 2: // Bell
|
||||
Util.Debug("Bell");
|
||||
Log.Debug("Bell");
|
||||
this._onBell(this);
|
||||
return true;
|
||||
|
||||
|
@ -1308,7 +1307,7 @@
|
|||
if (first) {
|
||||
this._enabledContinuousUpdates = true;
|
||||
this._updateContinuousUpdates();
|
||||
Util.Info("Enabling continuous updates.");
|
||||
Log.Info("Enabling continuous updates.");
|
||||
} else {
|
||||
// FIXME: We need to send a framebufferupdaterequest here
|
||||
// if we add support for turning off continuous updates
|
||||
|
@ -1323,7 +1322,7 @@
|
|||
|
||||
default:
|
||||
this._fail("Unexpected server message", "Type:" + msg_type);
|
||||
Util.Debug("sock.rQslice(0, 30): " + this._sock.rQslice(0, 30));
|
||||
Log.Debug("sock.rQslice(0, 30): " + this._sock.rQslice(0, 30));
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
@ -1348,7 +1347,7 @@
|
|||
this._timing.cur_fbu = 0;
|
||||
if (this._timing.fbu_rt_start > 0) {
|
||||
now = (new Date()).getTime();
|
||||
Util.Info("First FBU latency: " + (now - this._timing.fbu_rt_start));
|
||||
Log.Info("First FBU latency: " + (now - this._timing.fbu_rt_start));
|
||||
}
|
||||
|
||||
// Make sure the previous frame is fully rendered first
|
||||
|
@ -1408,7 +1407,7 @@
|
|||
this._timing.fbu_rt_start > 0) {
|
||||
this._timing.full_fbu_total += this._timing.cur_fbu;
|
||||
this._timing.full_fbu_cnt++;
|
||||
Util.Info("Timing of full FBU, curr: " +
|
||||
Log.Info("Timing of full FBU, curr: " +
|
||||
this._timing.cur_fbu + ", total: " +
|
||||
this._timing.full_fbu_total + ", cnt: " +
|
||||
this._timing.full_fbu_cnt + ", avg: " +
|
||||
|
@ -1419,7 +1418,7 @@
|
|||
var fbu_rt_diff = now - this._timing.fbu_rt_start;
|
||||
this._timing.fbu_rt_total += fbu_rt_diff;
|
||||
this._timing.fbu_rt_cnt++;
|
||||
Util.Info("full FBU round-trip, cur: " +
|
||||
Log.Info("full FBU round-trip, cur: " +
|
||||
fbu_rt_diff + ", total: " +
|
||||
this._timing.fbu_rt_total + ", cnt: " +
|
||||
this._timing.fbu_rt_cnt + ", avg: " +
|
||||
|
@ -1450,7 +1449,7 @@
|
|||
}
|
||||
};
|
||||
|
||||
Util.make_properties(RFB, [
|
||||
make_properties(RFB, [
|
||||
['target', 'wo', 'dom'], // VNC display rendering Canvas object
|
||||
['focusContainer', 'wo', 'dom'], // DOM element that captures keyboard input
|
||||
['encrypt', 'rw', 'bool'], // Use TLS/SSL/wss encryption
|
||||
|
@ -1486,7 +1485,7 @@
|
|||
if (this._display.get_cursor_uri()) {
|
||||
this._local_cursor = true;
|
||||
} else {
|
||||
Util.Warn("Browser does not support local cursor");
|
||||
Log.Warn("Browser does not support local cursor");
|
||||
this._display.disableLocalCursor();
|
||||
}
|
||||
}
|
||||
|
@ -1749,10 +1748,10 @@
|
|||
var i, j = offset + 4, cnt = 0;
|
||||
for (i = 0; i < encodings.length; i++) {
|
||||
if (encodings[i][0] === "Cursor" && !local_cursor) {
|
||||
Util.Debug("Skipping Cursor pseudo-encoding");
|
||||
Log.Debug("Skipping Cursor pseudo-encoding");
|
||||
} else if (encodings[i][0] === "TIGHT" && !true_color) {
|
||||
// TODO: remove this when we have tight+non-true-color
|
||||
Util.Warn("Skipping tight as it is only supported with true color");
|
||||
Log.Warn("Skipping tight as it is only supported with true color");
|
||||
} else {
|
||||
var enc = encodings[i][1];
|
||||
buff[j] = enc >> 24;
|
||||
|
@ -1937,7 +1936,7 @@
|
|||
if (this._FBU.subencoding === 0) {
|
||||
if (this._FBU.lastsubencoding & 0x01) {
|
||||
// Weird: ignore blanks are RAW
|
||||
Util.Debug(" Ignoring blank after RAW");
|
||||
Log.Debug(" Ignoring blank after RAW");
|
||||
} else {
|
||||
this._display.fillRect(x, y, w, h, this._FBU.background);
|
||||
}
|
||||
|
@ -2049,14 +2048,14 @@
|
|||
for (var i = 0; i < 4; i++) {
|
||||
if ((resetStreams >> i) & 1) {
|
||||
this._FBU.zlibs[i].reset();
|
||||
Util.Info("Reset zlib stream " + i);
|
||||
Log.Info("Reset zlib stream " + i);
|
||||
}
|
||||
}
|
||||
|
||||
//var uncompressed = this._FBU.zlibs[streamId].uncompress(data, 0);
|
||||
var uncompressed = this._FBU.zlibs[streamId].inflate(data, true, expected);
|
||||
/*if (uncompressed.status !== 0) {
|
||||
Util.Error("Invalid data in zlib stream");
|
||||
Log.Error("Invalid data in zlib stream");
|
||||
}*/
|
||||
|
||||
//return uncompressed.data;
|
||||
|
@ -2432,7 +2431,7 @@
|
|||
},
|
||||
|
||||
Cursor: function () {
|
||||
Util.Debug(">> set_cursor");
|
||||
Log.Debug(">> set_cursor");
|
||||
var x = this._FBU.x; // hotspot-x
|
||||
var y = this._FBU.y; // hotspot-y
|
||||
var w = this._FBU.width;
|
||||
|
@ -2451,7 +2450,7 @@
|
|||
this._FBU.bytes = 0;
|
||||
this._FBU.rects--;
|
||||
|
||||
Util.Debug("<< set_cursor");
|
||||
Log.Debug("<< set_cursor");
|
||||
return true;
|
||||
},
|
||||
|
||||
|
@ -2466,11 +2465,10 @@
|
|||
},
|
||||
|
||||
JPEG_quality_lo: function () {
|
||||
Util.Error("Server sent jpeg_quality pseudo-encoding");
|
||||
Log.Error("Server sent jpeg_quality pseudo-encoding");
|
||||
},
|
||||
|
||||
compress_lo: function () {
|
||||
Util.Error("Server sent compress level pseudo-encoding");
|
||||
Log.Error("Server sent compress level pseudo-encoding");
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
|
621
core/util.js
621
core/util.js
|
@ -1,621 +0,0 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
/* jshint white: false, nonstandard: true */
|
||||
/*global window, console, document, navigator, ActiveXObject, INCLUDE_URI */
|
||||
|
||||
var Util = {};
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* Namespaced in Util
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
* Logging/debug routines
|
||||
*/
|
||||
|
||||
Util._log_level = 'warn';
|
||||
Util.init_logging = function (level) {
|
||||
"use strict";
|
||||
if (typeof level === 'undefined') {
|
||||
level = Util._log_level;
|
||||
} else {
|
||||
Util._log_level = level;
|
||||
}
|
||||
|
||||
Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {};
|
||||
if (typeof window.console !== "undefined") {
|
||||
/* jshint -W086 */
|
||||
switch (level) {
|
||||
case 'debug':
|
||||
Util.Debug = console.debug.bind(window.console);
|
||||
case 'info':
|
||||
Util.Info = console.info.bind(window.console);
|
||||
case 'warn':
|
||||
Util.Warn = console.warn.bind(window.console);
|
||||
case 'error':
|
||||
Util.Error = console.error.bind(window.console);
|
||||
case 'none':
|
||||
break;
|
||||
default:
|
||||
throw new Error("invalid logging type '" + level + "'");
|
||||
}
|
||||
/* jshint +W086 */
|
||||
}
|
||||
};
|
||||
Util.get_logging = function () {
|
||||
return Util._log_level;
|
||||
};
|
||||
// Initialize logging level
|
||||
Util.init_logging();
|
||||
|
||||
Util.make_property = function (proto, name, mode, type) {
|
||||
"use strict";
|
||||
|
||||
var getter;
|
||||
if (type === 'arr') {
|
||||
getter = function (idx) {
|
||||
if (typeof idx !== 'undefined') {
|
||||
return this['_' + name][idx];
|
||||
} else {
|
||||
return this['_' + name];
|
||||
}
|
||||
};
|
||||
} else {
|
||||
getter = function () {
|
||||
return this['_' + name];
|
||||
};
|
||||
}
|
||||
|
||||
var make_setter = function (process_val) {
|
||||
if (process_val) {
|
||||
return function (val, idx) {
|
||||
if (typeof idx !== 'undefined') {
|
||||
this['_' + name][idx] = process_val(val);
|
||||
} else {
|
||||
this['_' + name] = process_val(val);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return function (val, idx) {
|
||||
if (typeof idx !== 'undefined') {
|
||||
this['_' + name][idx] = val;
|
||||
} else {
|
||||
this['_' + name] = val;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
var setter;
|
||||
if (type === 'bool') {
|
||||
setter = make_setter(function (val) {
|
||||
if (!val || (val in {'0': 1, 'no': 1, 'false': 1})) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} else if (type === 'int') {
|
||||
setter = make_setter(function (val) { return parseInt(val, 10); });
|
||||
} else if (type === 'float') {
|
||||
setter = make_setter(parseFloat);
|
||||
} else if (type === 'str') {
|
||||
setter = make_setter(String);
|
||||
} else if (type === 'func') {
|
||||
setter = make_setter(function (val) {
|
||||
if (!val) {
|
||||
return function () {};
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
});
|
||||
} else if (type === 'arr' || type === 'dom' || type == 'raw') {
|
||||
setter = make_setter();
|
||||
} else {
|
||||
throw new Error('Unknown property type ' + type); // some sanity checking
|
||||
}
|
||||
|
||||
// set the getter
|
||||
if (typeof proto['get_' + name] === 'undefined') {
|
||||
proto['get_' + name] = getter;
|
||||
}
|
||||
|
||||
// set the setter if needed
|
||||
if (typeof proto['set_' + name] === 'undefined') {
|
||||
if (mode === 'rw') {
|
||||
proto['set_' + name] = setter;
|
||||
} else if (mode === 'wo') {
|
||||
proto['set_' + name] = function (val, idx) {
|
||||
if (typeof this['_' + name] !== 'undefined') {
|
||||
throw new Error(name + " can only be set once");
|
||||
}
|
||||
setter.call(this, val, idx);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// make a special setter that we can use in set defaults
|
||||
proto['_raw_set_' + name] = function (val, idx) {
|
||||
setter.call(this, val, idx);
|
||||
//delete this['_init_set_' + name]; // remove it after use
|
||||
};
|
||||
};
|
||||
|
||||
Util.make_properties = function (constructor, arr) {
|
||||
"use strict";
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
Util.make_property(constructor.prototype, arr[i][0], arr[i][1], arr[i][2]);
|
||||
}
|
||||
};
|
||||
|
||||
Util.set_defaults = function (obj, conf, defaults) {
|
||||
var defaults_keys = Object.keys(defaults);
|
||||
var conf_keys = Object.keys(conf);
|
||||
var keys_obj = {};
|
||||
var i;
|
||||
for (i = 0; i < defaults_keys.length; i++) { keys_obj[defaults_keys[i]] = 1; }
|
||||
for (i = 0; i < conf_keys.length; i++) { keys_obj[conf_keys[i]] = 1; }
|
||||
var keys = Object.keys(keys_obj);
|
||||
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
var setter = obj['_raw_set_' + keys[i]];
|
||||
if (!setter) {
|
||||
Util.Warn('Invalid property ' + keys[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keys[i] in conf) {
|
||||
setter.call(obj, conf[keys[i]]);
|
||||
} else {
|
||||
setter.call(obj, defaults[keys[i]]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Decode from UTF-8
|
||||
*/
|
||||
Util.decodeUTF8 = function (utf8string) {
|
||||
"use strict";
|
||||
return decodeURIComponent(escape(utf8string));
|
||||
};
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Cross-browser routines
|
||||
*/
|
||||
|
||||
Util.getPointerEvent = function (e) {
|
||||
return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e;
|
||||
};
|
||||
|
||||
Util.stopEvent = function (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
// Touch detection
|
||||
Util.isTouchDevice = ('ontouchstart' in document.documentElement) ||
|
||||
// requried for Chrome debugger
|
||||
(document.ontouchstart !== undefined) ||
|
||||
// required for MS Surface
|
||||
(navigator.maxTouchPoints > 0) ||
|
||||
(navigator.msMaxTouchPoints > 0);
|
||||
window.addEventListener('touchstart', function onFirstTouch() {
|
||||
Util.isTouchDevice = true;
|
||||
window.removeEventListener('touchstart', onFirstTouch, false);
|
||||
}, false);
|
||||
|
||||
Util._cursor_uris_supported = null;
|
||||
|
||||
Util.browserSupportsCursorURIs = function () {
|
||||
if (Util._cursor_uris_supported === null) {
|
||||
try {
|
||||
var target = document.createElement('canvas');
|
||||
target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
|
||||
|
||||
if (target.style.cursor) {
|
||||
Util.Info("Data URI scheme cursor supported");
|
||||
Util._cursor_uris_supported = true;
|
||||
} else {
|
||||
Util.Warn("Data URI scheme cursor not supported");
|
||||
Util._cursor_uris_supported = false;
|
||||
}
|
||||
} catch (exc) {
|
||||
Util.Error("Data URI scheme cursor test exception: " + exc);
|
||||
Util._cursor_uris_supported = false;
|
||||
}
|
||||
}
|
||||
|
||||
return Util._cursor_uris_supported;
|
||||
};
|
||||
|
||||
// Set browser engine versions. Based on mootools.
|
||||
Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)};
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
// 'presto': (function () { return (!window.opera) ? false : true; }()),
|
||||
var detectPresto = function () {
|
||||
return !!window.opera;
|
||||
};
|
||||
|
||||
// 'trident': (function () { return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4);
|
||||
var detectTrident = function () {
|
||||
if (!window.ActiveXObject) {
|
||||
return false;
|
||||
} else {
|
||||
if (window.XMLHttpRequest) {
|
||||
return (document.querySelectorAll) ? 6 : 5;
|
||||
} else {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 'webkit': (function () { try { return (navigator.taintEnabled) ? false : ((Util.Features.xpath) ? ((Util.Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()),
|
||||
var detectInitialWebkit = function () {
|
||||
try {
|
||||
if (navigator.taintEnabled) {
|
||||
return false;
|
||||
} else {
|
||||
if (Util.Features.xpath) {
|
||||
return (Util.Features.query) ? 525 : 420;
|
||||
} else {
|
||||
return 419;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
var detectActualWebkit = function (initial_ver) {
|
||||
var re = /WebKit\/([0-9\.]*) /;
|
||||
var str_ver = (navigator.userAgent.match(re) || ['', initial_ver])[1];
|
||||
return parseFloat(str_ver, 10);
|
||||
};
|
||||
|
||||
// 'gecko': (function () { return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19ssName) ? 19 : 18 : 18); }())
|
||||
var detectGecko = function () {
|
||||
/* jshint -W041 */
|
||||
if (!document.getBoxObjectFor && window.mozInnerScreenX == null) {
|
||||
return false;
|
||||
} else {
|
||||
return (document.getElementsByClassName) ? 19 : 18;
|
||||
}
|
||||
/* jshint +W041 */
|
||||
};
|
||||
|
||||
Util.Engine = {
|
||||
// Version detection break in Opera 11.60 (errors on arguments.callee.caller reference)
|
||||
//'presto': (function() {
|
||||
// return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
|
||||
'presto': detectPresto(),
|
||||
'trident': detectTrident(),
|
||||
'webkit': detectInitialWebkit(),
|
||||
'gecko': detectGecko()
|
||||
};
|
||||
|
||||
if (Util.Engine.webkit) {
|
||||
// Extract actual webkit version if available
|
||||
Util.Engine.webkit = detectActualWebkit(Util.Engine.webkit);
|
||||
}
|
||||
})();
|
||||
|
||||
Util.Flash = (function () {
|
||||
"use strict";
|
||||
var v, version;
|
||||
try {
|
||||
v = navigator.plugins['Shockwave Flash'].description;
|
||||
} catch (err1) {
|
||||
try {
|
||||
v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
|
||||
} catch (err2) {
|
||||
v = '0 r0';
|
||||
}
|
||||
}
|
||||
version = v.match(/\d+/g);
|
||||
return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
|
||||
}());
|
||||
|
||||
|
||||
Util.Localisation = {
|
||||
// Currently configured language
|
||||
language: 'en',
|
||||
|
||||
// Configure suitable language based on user preferences
|
||||
setup: function (supportedLanguages) {
|
||||
var userLanguages;
|
||||
|
||||
Util.Localisation.language = 'en'; // Default: US English
|
||||
|
||||
/*
|
||||
* Navigator.languages only available in Chrome (32+) and FireFox (32+)
|
||||
* Fall back to navigator.language for other browsers
|
||||
*/
|
||||
if (typeof window.navigator.languages == 'object') {
|
||||
userLanguages = window.navigator.languages;
|
||||
} else {
|
||||
userLanguages = [navigator.language || navigator.userLanguage];
|
||||
}
|
||||
|
||||
for (var i = 0;i < userLanguages.length;i++) {
|
||||
var userLang = userLanguages[i];
|
||||
userLang = userLang.toLowerCase();
|
||||
userLang = userLang.replace("_", "-");
|
||||
userLang = userLang.split("-");
|
||||
|
||||
// Built-in default?
|
||||
if ((userLang[0] === 'en') &&
|
||||
((userLang[1] === undefined) || (userLang[1] === 'us'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// First pass: perfect match
|
||||
for (var j = 0;j < supportedLanguages.length;j++) {
|
||||
var supLang = supportedLanguages[j];
|
||||
supLang = supLang.toLowerCase();
|
||||
supLang = supLang.replace("_", "-");
|
||||
supLang = supLang.split("-");
|
||||
|
||||
if (userLang[0] !== supLang[0])
|
||||
continue;
|
||||
if (userLang[1] !== supLang[1])
|
||||
continue;
|
||||
|
||||
Util.Localisation.language = supportedLanguages[j];
|
||||
return;
|
||||
}
|
||||
|
||||
// Second pass: fallback
|
||||
for (var j = 0;j < supportedLanguages.length;j++) {
|
||||
supLang = supportedLanguages[j];
|
||||
supLang = supLang.toLowerCase();
|
||||
supLang = supLang.replace("_", "-");
|
||||
supLang = supLang.split("-");
|
||||
|
||||
if (userLang[0] !== supLang[0])
|
||||
continue;
|
||||
if (supLang[1] !== undefined)
|
||||
continue;
|
||||
|
||||
Util.Localisation.language = supportedLanguages[j];
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Retrieve localised text
|
||||
get: function (id) {
|
||||
if (typeof Language !== 'undefined' && Language[id]) {
|
||||
return Language[id];
|
||||
} else {
|
||||
return id;
|
||||
}
|
||||
},
|
||||
|
||||
// Traverses the DOM and translates relevant fields
|
||||
// See https://html.spec.whatwg.org/multipage/dom.html#attr-translate
|
||||
translateDOM: function () {
|
||||
function process(elem, enabled) {
|
||||
function isAnyOf(searchElement, items) {
|
||||
return items.indexOf(searchElement) !== -1;
|
||||
}
|
||||
|
||||
function translateAttribute(elem, attr) {
|
||||
var str = elem.getAttribute(attr);
|
||||
str = Util.Localisation.get(str);
|
||||
elem.setAttribute(attr, str);
|
||||
}
|
||||
|
||||
function translateTextNode(node) {
|
||||
var str = node.data.trim();
|
||||
str = Util.Localisation.get(str);
|
||||
node.data = str;
|
||||
}
|
||||
|
||||
if (elem.hasAttribute("translate")) {
|
||||
if (isAnyOf(elem.getAttribute("translate"), ["", "yes"])) {
|
||||
enabled = true;
|
||||
} else if (isAnyOf(elem.getAttribute("translate"), ["no"])) {
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
if (elem.hasAttribute("abbr") &&
|
||||
elem.tagName === "TH") {
|
||||
translateAttribute(elem, "abbr");
|
||||
}
|
||||
if (elem.hasAttribute("alt") &&
|
||||
isAnyOf(elem.tagName, ["AREA", "IMG", "INPUT"])) {
|
||||
translateAttribute(elem, "alt");
|
||||
}
|
||||
if (elem.hasAttribute("download") &&
|
||||
isAnyOf(elem.tagName, ["A", "AREA"])) {
|
||||
translateAttribute(elem, "download");
|
||||
}
|
||||
if (elem.hasAttribute("label") &&
|
||||
isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP",
|
||||
"OPTION", "TRACK"])) {
|
||||
translateAttribute(elem, "label");
|
||||
}
|
||||
// FIXME: Should update "lang"
|
||||
if (elem.hasAttribute("placeholder") &&
|
||||
isAnyOf(elem.tagName, ["INPUT", "TEXTAREA"])) {
|
||||
translateAttribute(elem, "placeholder");
|
||||
}
|
||||
if (elem.hasAttribute("title")) {
|
||||
translateAttribute(elem, "title");
|
||||
}
|
||||
if (elem.hasAttribute("value") &&
|
||||
elem.tagName === "INPUT" &&
|
||||
isAnyOf(elem.getAttribute("type"), ["reset", "button"])) {
|
||||
translateAttribute(elem, "value");
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0;i < elem.childNodes.length;i++) {
|
||||
node = elem.childNodes[i];
|
||||
if (node.nodeType === node.ELEMENT_NODE) {
|
||||
process(node, enabled);
|
||||
} else if (node.nodeType === node.TEXT_NODE && enabled) {
|
||||
translateTextNode(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process(document.body, true);
|
||||
},
|
||||
};
|
||||
|
||||
// Emulate Element.setCapture() when not supported
|
||||
|
||||
Util._captureRecursion = false;
|
||||
Util._captureProxy = function (e) {
|
||||
// Recursion protection as we'll see our own event
|
||||
if (Util._captureRecursion) return;
|
||||
|
||||
// Clone the event as we cannot dispatch an already dispatched event
|
||||
var newEv = new e.constructor(e.type, e);
|
||||
|
||||
Util._captureRecursion = true;
|
||||
Util._captureElem.dispatchEvent(newEv);
|
||||
Util._captureRecursion = false;
|
||||
|
||||
// Avoid double events
|
||||
e.stopPropagation();
|
||||
|
||||
// Respect the wishes of the redirected event handlers
|
||||
if (newEv.defaultPrevented) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
// Implicitly release the capture on button release
|
||||
if ((e.type === "mouseup") || (e.type === "touchend")) {
|
||||
Util.releaseCapture();
|
||||
}
|
||||
};
|
||||
|
||||
// Follow cursor style of target element
|
||||
Util._captureElemChanged = function() {
|
||||
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||
captureElem.style.cursor = window.getComputedStyle(Util._captureElem).cursor;
|
||||
};
|
||||
Util._captureObserver = new MutationObserver(Util._captureElemChanged);
|
||||
|
||||
Util._captureIndex = 0;
|
||||
|
||||
Util.setCapture = function (elem) {
|
||||
if (elem.setCapture) {
|
||||
|
||||
elem.setCapture();
|
||||
|
||||
// IE releases capture on 'click' events which might not trigger
|
||||
elem.addEventListener('mouseup', Util.releaseCapture);
|
||||
elem.addEventListener('touchend', Util.releaseCapture);
|
||||
|
||||
} else {
|
||||
// Release any existing capture in case this method is
|
||||
// called multiple times without coordination
|
||||
Util.releaseCapture();
|
||||
|
||||
// Safari on iOS 9 has a broken constructor for TouchEvent.
|
||||
// We are fine in this case however, since Safari seems to
|
||||
// have some sort of implicit setCapture magic anyway.
|
||||
if (window.TouchEvent !== undefined) {
|
||||
try {
|
||||
new TouchEvent("touchstart");
|
||||
} catch (TypeError) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||
|
||||
if (captureElem === null) {
|
||||
captureElem = document.createElement("div");
|
||||
captureElem.id = "noVNC_mouse_capture_elem";
|
||||
captureElem.style.position = "fixed";
|
||||
captureElem.style.top = "0px";
|
||||
captureElem.style.left = "0px";
|
||||
captureElem.style.width = "100%";
|
||||
captureElem.style.height = "100%";
|
||||
captureElem.style.zIndex = 10000;
|
||||
captureElem.style.display = "none";
|
||||
document.body.appendChild(captureElem);
|
||||
|
||||
// This is to make sure callers don't get confused by having
|
||||
// our blocking element as the target
|
||||
captureElem.addEventListener('contextmenu', Util._captureProxy);
|
||||
|
||||
captureElem.addEventListener('mousemove', Util._captureProxy);
|
||||
captureElem.addEventListener('mouseup', Util._captureProxy);
|
||||
|
||||
captureElem.addEventListener('touchmove', Util._captureProxy);
|
||||
captureElem.addEventListener('touchend', Util._captureProxy);
|
||||
}
|
||||
|
||||
Util._captureElem = elem;
|
||||
Util._captureIndex++;
|
||||
|
||||
// Track cursor and get initial cursor
|
||||
Util._captureObserver.observe(elem, {attributes:true});
|
||||
Util._captureElemChanged();
|
||||
|
||||
captureElem.style.display = null;
|
||||
|
||||
// We listen to events on window in order to keep tracking if it
|
||||
// happens to leave the viewport
|
||||
window.addEventListener('mousemove', Util._captureProxy);
|
||||
window.addEventListener('mouseup', Util._captureProxy);
|
||||
|
||||
window.addEventListener('touchmove', Util._captureProxy);
|
||||
window.addEventListener('touchend', Util._captureProxy);
|
||||
}
|
||||
};
|
||||
|
||||
Util.releaseCapture = function () {
|
||||
if (document.releaseCapture) {
|
||||
|
||||
document.releaseCapture();
|
||||
|
||||
} else {
|
||||
if (!Util._captureElem) {
|
||||
return;
|
||||
}
|
||||
|
||||
// There might be events already queued, so we need to wait for
|
||||
// them to flush. E.g. contextmenu in Microsoft Edge
|
||||
window.setTimeout(function(expected) {
|
||||
// Only clear it if it's the expected grab (i.e. no one
|
||||
// else has initiated a new grab)
|
||||
if (Util._captureIndex === expected) {
|
||||
Util._captureElem = null;
|
||||
}
|
||||
}, 0, Util._captureIndex);
|
||||
|
||||
Util._captureObserver.disconnect();
|
||||
|
||||
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||
captureElem.style.display = "none";
|
||||
|
||||
window.removeEventListener('mousemove', Util._captureProxy);
|
||||
window.removeEventListener('mouseup', Util._captureProxy);
|
||||
|
||||
window.removeEventListener('touchmove', Util._captureProxy);
|
||||
window.removeEventListener('touchend', Util._captureProxy);
|
||||
}
|
||||
};
|
||||
|
||||
/* [module] export default Util; */
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
import * as Log from './logging.js';
|
||||
|
||||
// Set browser engine versions. Based on mootools.
|
||||
const Features = {xpath: !!(document.evaluate), query: !!(document.querySelector)};
|
||||
|
||||
// 'presto': (function () { return (!window.opera) ? false : true; }()),
|
||||
var detectPresto = function () {
|
||||
return !!window.opera;
|
||||
};
|
||||
|
||||
// 'trident': (function () { return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4);
|
||||
var detectTrident = function () {
|
||||
if (!window.ActiveXObject) {
|
||||
return false;
|
||||
} else {
|
||||
if (window.XMLHttpRequest) {
|
||||
return (document.querySelectorAll) ? 6 : 5;
|
||||
} else {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 'webkit': (function () { try { return (navigator.taintEnabled) ? false : ((Features.xpath) ? ((Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()),
|
||||
var detectInitialWebkit = function () {
|
||||
try {
|
||||
if (navigator.taintEnabled) {
|
||||
return false;
|
||||
} else {
|
||||
if (Features.xpath) {
|
||||
return (Features.query) ? 525 : 420;
|
||||
} else {
|
||||
return 419;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
var detectActualWebkit = function (initial_ver) {
|
||||
var re = /WebKit\/([0-9\.]*) /;
|
||||
var str_ver = (navigator.userAgent.match(re) || ['', initial_ver])[1];
|
||||
return parseFloat(str_ver, 10);
|
||||
};
|
||||
|
||||
// 'gecko': (function () { return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19ssName) ? 19 : 18 : 18); }())
|
||||
var detectGecko = function () {
|
||||
/* jshint -W041 */
|
||||
if (!document.getBoxObjectFor && window.mozInnerScreenX == null) {
|
||||
return false;
|
||||
} else {
|
||||
return (document.getElementsByClassName) ? 19 : 18;
|
||||
}
|
||||
/* jshint +W041 */
|
||||
};
|
||||
|
||||
const isWebkitInitial = detectInitialWebkit();
|
||||
|
||||
export const Engine = {
|
||||
// Version detection break in Opera 11.60 (errors on arguments.callee.caller reference)
|
||||
//'presto': (function() {
|
||||
// return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
|
||||
'presto': detectPresto(),
|
||||
'trident': detectTrident(),
|
||||
'webkit': isWebkitInitial ? detectActualWebkit(isWebkitInitial) : false,
|
||||
'gecko': detectGecko()
|
||||
};
|
||||
|
||||
// Touch detection
|
||||
export var isTouchDevice = ('ontouchstart' in document.documentElement) ||
|
||||
// requried for Chrome debugger
|
||||
(document.ontouchstart !== undefined) ||
|
||||
// required for MS Surface
|
||||
(navigator.maxTouchPoints > 0) ||
|
||||
(navigator.msMaxTouchPoints > 0);
|
||||
window.addEventListener('touchstart', function onFirstTouch() {
|
||||
isTouchDevice = true;
|
||||
window.removeEventListener('touchstart', onFirstTouch, false);
|
||||
}, false);
|
||||
|
||||
var _cursor_uris_supported = null;
|
||||
|
||||
export function browserSupportsCursorURIs () {
|
||||
if (_cursor_uris_supported === null) {
|
||||
try {
|
||||
var target = document.createElement('canvas');
|
||||
target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
|
||||
|
||||
if (target.style.cursor) {
|
||||
Log.Info("Data URI scheme cursor supported");
|
||||
_cursor_uris_supported = true;
|
||||
} else {
|
||||
Log.Warn("Data URI scheme cursor not supported");
|
||||
_cursor_uris_supported = false;
|
||||
}
|
||||
} catch (exc) {
|
||||
Log.Error("Data URI scheme cursor test exception: " + exc);
|
||||
_cursor_uris_supported = false;
|
||||
}
|
||||
}
|
||||
|
||||
return _cursor_uris_supported;
|
||||
};
|
||||
|
||||
export function _forceCursorURIs(enabled) {
|
||||
if (enabled === undefined || enabled) {
|
||||
_cursor_uris_supported = true;
|
||||
} else {
|
||||
_cursor_uris_supported = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cross-browser event and position routines
|
||||
*/
|
||||
|
||||
import * as Log from './logging.js';
|
||||
|
||||
export function getPointerEvent (e) {
|
||||
return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e;
|
||||
};
|
||||
|
||||
export function stopEvent (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
// Emulate Element.setCapture() when not supported
|
||||
var _captureRecursion = false;
|
||||
var _captureElem = null;
|
||||
const _captureProxy = function (e) {
|
||||
// Recursion protection as we'll see our own event
|
||||
if (_captureRecursion) return;
|
||||
|
||||
// Clone the event as we cannot dispatch an already dispatched event
|
||||
var newEv = new e.constructor(e.type, e);
|
||||
|
||||
_captureRecursion = true;
|
||||
_captureElem.dispatchEvent(newEv);
|
||||
_captureRecursion = false;
|
||||
|
||||
// Avoid double events
|
||||
e.stopPropagation();
|
||||
|
||||
// Respect the wishes of the redirected event handlers
|
||||
if (newEv.defaultPrevented) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
// Implicitly release the capture on button release
|
||||
if ((e.type === "mouseup") || (e.type === "touchend")) {
|
||||
releaseCapture();
|
||||
}
|
||||
};
|
||||
|
||||
// Follow cursor style of target element
|
||||
const _captureElemChanged = function() {
|
||||
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||
captureElem.style.cursor = window.getComputedStyle(_captureElem).cursor;
|
||||
};
|
||||
const _captureObserver = new MutationObserver(_captureElemChanged);
|
||||
|
||||
var _captureIndex = 0;
|
||||
|
||||
export function setCapture (elem) {
|
||||
if (elem.setCapture) {
|
||||
|
||||
elem.setCapture();
|
||||
|
||||
// IE releases capture on 'click' events which might not trigger
|
||||
elem.addEventListener('mouseup', releaseCapture);
|
||||
elem.addEventListener('touchend', releaseCapture);
|
||||
|
||||
} else {
|
||||
// Release any existing capture in case this method is
|
||||
// called multiple times without coordination
|
||||
releaseCapture();
|
||||
|
||||
// Safari on iOS 9 has a broken constructor for TouchEvent.
|
||||
// We are fine in this case however, since Safari seems to
|
||||
// have some sort of implicit setCapture magic anyway.
|
||||
if (window.TouchEvent !== undefined) {
|
||||
try {
|
||||
new TouchEvent("touchstart");
|
||||
} catch (TypeError) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||
|
||||
if (captureElem === null) {
|
||||
captureElem = document.createElement("div");
|
||||
captureElem.id = "noVNC_mouse_capture_elem";
|
||||
captureElem.style.position = "fixed";
|
||||
captureElem.style.top = "0px";
|
||||
captureElem.style.left = "0px";
|
||||
captureElem.style.width = "100%";
|
||||
captureElem.style.height = "100%";
|
||||
captureElem.style.zIndex = 10000;
|
||||
captureElem.style.display = "none";
|
||||
document.body.appendChild(captureElem);
|
||||
|
||||
// This is to make sure callers don't get confused by having
|
||||
// our blocking element as the target
|
||||
captureElem.addEventListener('contextmenu', _captureProxy);
|
||||
|
||||
captureElem.addEventListener('mousemove', _captureProxy);
|
||||
captureElem.addEventListener('mouseup', _captureProxy);
|
||||
|
||||
captureElem.addEventListener('touchmove', _captureProxy);
|
||||
captureElem.addEventListener('touchend', _captureProxy);
|
||||
}
|
||||
|
||||
_captureElem = elem;
|
||||
_captureIndex++;
|
||||
|
||||
// Track cursor and get initial cursor
|
||||
_captureObserver.observe(elem, {attributes:true});
|
||||
_captureElemChanged();
|
||||
|
||||
captureElem.style.display = null;
|
||||
|
||||
// We listen to events on window in order to keep tracking if it
|
||||
// happens to leave the viewport
|
||||
window.addEventListener('mousemove', _captureProxy);
|
||||
window.addEventListener('mouseup', _captureProxy);
|
||||
|
||||
window.addEventListener('touchmove', _captureProxy);
|
||||
window.addEventListener('touchend', _captureProxy);
|
||||
}
|
||||
};
|
||||
|
||||
export function releaseCapture () {
|
||||
if (document.releaseCapture) {
|
||||
|
||||
document.releaseCapture();
|
||||
|
||||
} else {
|
||||
if (!_captureElem) {
|
||||
return;
|
||||
}
|
||||
|
||||
// There might be events already queued, so we need to wait for
|
||||
// them to flush. E.g. contextmenu in Microsoft Edge
|
||||
window.setTimeout(function(expected) {
|
||||
// Only clear it if it's the expected grab (i.e. no one
|
||||
// else has initiated a new grab)
|
||||
if (_captureIndex === expected) {
|
||||
_captureElem = null;
|
||||
}
|
||||
}, 0, _captureIndex);
|
||||
|
||||
_captureObserver.disconnect();
|
||||
|
||||
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||
captureElem.style.display = "none";
|
||||
|
||||
window.removeEventListener('mousemove', _captureProxy);
|
||||
window.removeEventListener('mouseup', _captureProxy);
|
||||
|
||||
window.removeEventListener('touchmove', _captureProxy);
|
||||
window.removeEventListener('touchend', _captureProxy);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Localization Utilities
|
||||
*/
|
||||
|
||||
export function Localizer() {
|
||||
// Currently configured language
|
||||
this.language = 'en';
|
||||
|
||||
// Current dictionary of translations
|
||||
this.dictionary = undefined;
|
||||
}
|
||||
|
||||
Localizer.prototype = {
|
||||
// Configure suitable language based on user preferences
|
||||
setup: function (supportedLanguages) {
|
||||
var userLanguages;
|
||||
|
||||
this.language = 'en'; // Default: US English
|
||||
|
||||
/*
|
||||
* Navigator.languages only available in Chrome (32+) and FireFox (32+)
|
||||
* Fall back to navigator.language for other browsers
|
||||
*/
|
||||
if (typeof window.navigator.languages == 'object') {
|
||||
userLanguages = window.navigator.languages;
|
||||
} else {
|
||||
userLanguages = [navigator.language || navigator.userLanguage];
|
||||
}
|
||||
|
||||
for (var i = 0;i < userLanguages.length;i++) {
|
||||
var userLang = userLanguages[i];
|
||||
userLang = userLang.toLowerCase();
|
||||
userLang = userLang.replace("_", "-");
|
||||
userLang = userLang.split("-");
|
||||
|
||||
// Built-in default?
|
||||
if ((userLang[0] === 'en') &&
|
||||
((userLang[1] === undefined) || (userLang[1] === 'us'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// First pass: perfect match
|
||||
for (var j = 0;j < supportedLanguages.length;j++) {
|
||||
var supLang = supportedLanguages[j];
|
||||
supLang = supLang.toLowerCase();
|
||||
supLang = supLang.replace("_", "-");
|
||||
supLang = supLang.split("-");
|
||||
|
||||
if (userLang[0] !== supLang[0])
|
||||
continue;
|
||||
if (userLang[1] !== supLang[1])
|
||||
continue;
|
||||
|
||||
this.language = supportedLanguages[j];
|
||||
return;
|
||||
}
|
||||
|
||||
// Second pass: fallback
|
||||
for (var j = 0;j < supportedLanguages.length;j++) {
|
||||
supLang = supportedLanguages[j];
|
||||
supLang = supLang.toLowerCase();
|
||||
supLang = supLang.replace("_", "-");
|
||||
supLang = supLang.split("-");
|
||||
|
||||
if (userLang[0] !== supLang[0])
|
||||
continue;
|
||||
if (supLang[1] !== undefined)
|
||||
continue;
|
||||
|
||||
this.language = supportedLanguages[j];
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Retrieve localised text
|
||||
get: function (id) {
|
||||
if (typeof this.dictionary !== 'undefined' && this.dictionary[id]) {
|
||||
return this.dictionary[id];
|
||||
} else {
|
||||
return id;
|
||||
}
|
||||
},
|
||||
|
||||
// Traverses the DOM and translates relevant fields
|
||||
// See https://html.spec.whatwg.org/multipage/dom.html#attr-translate
|
||||
translateDOM: function () {
|
||||
var self = this;
|
||||
function process(elem, enabled) {
|
||||
function isAnyOf(searchElement, items) {
|
||||
return items.indexOf(searchElement) !== -1;
|
||||
}
|
||||
|
||||
function translateAttribute(elem, attr) {
|
||||
var str = elem.getAttribute(attr);
|
||||
str = self.get(str);
|
||||
elem.setAttribute(attr, str);
|
||||
}
|
||||
|
||||
function translateTextNode(node) {
|
||||
var str = node.data.trim();
|
||||
str = self.get(str);
|
||||
node.data = str;
|
||||
}
|
||||
|
||||
if (elem.hasAttribute("translate")) {
|
||||
if (isAnyOf(elem.getAttribute("translate"), ["", "yes"])) {
|
||||
enabled = true;
|
||||
} else if (isAnyOf(elem.getAttribute("translate"), ["no"])) {
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
if (elem.hasAttribute("abbr") &&
|
||||
elem.tagName === "TH") {
|
||||
translateAttribute(elem, "abbr");
|
||||
}
|
||||
if (elem.hasAttribute("alt") &&
|
||||
isAnyOf(elem.tagName, ["AREA", "IMG", "INPUT"])) {
|
||||
translateAttribute(elem, "alt");
|
||||
}
|
||||
if (elem.hasAttribute("download") &&
|
||||
isAnyOf(elem.tagName, ["A", "AREA"])) {
|
||||
translateAttribute(elem, "download");
|
||||
}
|
||||
if (elem.hasAttribute("label") &&
|
||||
isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP",
|
||||
"OPTION", "TRACK"])) {
|
||||
translateAttribute(elem, "label");
|
||||
}
|
||||
// FIXME: Should update "lang"
|
||||
if (elem.hasAttribute("placeholder") &&
|
||||
isAnyOf(elem.tagName, ["INPUT", "TEXTAREA"])) {
|
||||
translateAttribute(elem, "placeholder");
|
||||
}
|
||||
if (elem.hasAttribute("title")) {
|
||||
translateAttribute(elem, "title");
|
||||
}
|
||||
if (elem.hasAttribute("value") &&
|
||||
elem.tagName === "INPUT" &&
|
||||
isAnyOf(elem.getAttribute("type"), ["reset", "button"])) {
|
||||
translateAttribute(elem, "value");
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0;i < elem.childNodes.length;i++) {
|
||||
let node = elem.childNodes[i];
|
||||
if (node.nodeType === node.ELEMENT_NODE) {
|
||||
process(node, enabled);
|
||||
} else if (node.nodeType === node.TEXT_NODE && enabled) {
|
||||
translateTextNode(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process(document.body, true);
|
||||
},
|
||||
}
|
||||
|
||||
export const l10n = new Localizer();
|
||||
export default l10n.get.bind(l10n);
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Logging/debug routines
|
||||
*/
|
||||
|
||||
var _log_level = 'warn';
|
||||
|
||||
var Debug = function (msg) {};
|
||||
var Info = function (msg) {};
|
||||
var Warn = function (msg) {};
|
||||
var Error = function (msg) {};
|
||||
|
||||
export function init_logging (level) {
|
||||
if (typeof level === 'undefined') {
|
||||
level = _log_level;
|
||||
} else {
|
||||
_log_level = level;
|
||||
}
|
||||
|
||||
Debug = Info = Warn = Error = function (msg) {};
|
||||
if (typeof window.console !== "undefined") {
|
||||
/* jshint -W086 */
|
||||
switch (level) {
|
||||
case 'debug':
|
||||
Debug = console.debug.bind(window.console);
|
||||
case 'info':
|
||||
Info = console.info.bind(window.console);
|
||||
case 'warn':
|
||||
Warn = console.warn.bind(window.console);
|
||||
case 'error':
|
||||
Error = console.error.bind(window.console);
|
||||
case 'none':
|
||||
break;
|
||||
default:
|
||||
throw new Error("invalid logging type '" + level + "'");
|
||||
}
|
||||
/* jshint +W086 */
|
||||
}
|
||||
};
|
||||
export function get_logging () {
|
||||
return _log_level;
|
||||
};
|
||||
export { Debug, Info, Warn, Error };
|
||||
|
||||
// Initialize logging level
|
||||
init_logging();
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Getter/Setter Creation Utilities
|
||||
*/
|
||||
|
||||
import * as Log from './logging.js';
|
||||
|
||||
function make_property (proto, name, mode, type) {
|
||||
"use strict";
|
||||
|
||||
var getter;
|
||||
if (type === 'arr') {
|
||||
getter = function (idx) {
|
||||
if (typeof idx !== 'undefined') {
|
||||
return this['_' + name][idx];
|
||||
} else {
|
||||
return this['_' + name];
|
||||
}
|
||||
};
|
||||
} else {
|
||||
getter = function () {
|
||||
return this['_' + name];
|
||||
};
|
||||
}
|
||||
|
||||
var make_setter = function (process_val) {
|
||||
if (process_val) {
|
||||
return function (val, idx) {
|
||||
if (typeof idx !== 'undefined') {
|
||||
this['_' + name][idx] = process_val(val);
|
||||
} else {
|
||||
this['_' + name] = process_val(val);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return function (val, idx) {
|
||||
if (typeof idx !== 'undefined') {
|
||||
this['_' + name][idx] = val;
|
||||
} else {
|
||||
this['_' + name] = val;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
var setter;
|
||||
if (type === 'bool') {
|
||||
setter = make_setter(function (val) {
|
||||
if (!val || (val in {'0': 1, 'no': 1, 'false': 1})) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} else if (type === 'int') {
|
||||
setter = make_setter(function (val) { return parseInt(val, 10); });
|
||||
} else if (type === 'float') {
|
||||
setter = make_setter(parseFloat);
|
||||
} else if (type === 'str') {
|
||||
setter = make_setter(String);
|
||||
} else if (type === 'func') {
|
||||
setter = make_setter(function (val) {
|
||||
if (!val) {
|
||||
return function () {};
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
});
|
||||
} else if (type === 'arr' || type === 'dom' || type == 'raw') {
|
||||
setter = make_setter();
|
||||
} else {
|
||||
throw new Error('Unknown property type ' + type); // some sanity checking
|
||||
}
|
||||
|
||||
// set the getter
|
||||
if (typeof proto['get_' + name] === 'undefined') {
|
||||
proto['get_' + name] = getter;
|
||||
}
|
||||
|
||||
// set the setter if needed
|
||||
if (typeof proto['set_' + name] === 'undefined') {
|
||||
if (mode === 'rw') {
|
||||
proto['set_' + name] = setter;
|
||||
} else if (mode === 'wo') {
|
||||
proto['set_' + name] = function (val, idx) {
|
||||
if (typeof this['_' + name] !== 'undefined') {
|
||||
throw new Error(name + " can only be set once");
|
||||
}
|
||||
setter.call(this, val, idx);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// make a special setter that we can use in set defaults
|
||||
proto['_raw_set_' + name] = function (val, idx) {
|
||||
setter.call(this, val, idx);
|
||||
//delete this['_init_set_' + name]; // remove it after use
|
||||
};
|
||||
};
|
||||
|
||||
export function make_properties (constructor, arr) {
|
||||
"use strict";
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
make_property(constructor.prototype, arr[i][0], arr[i][1], arr[i][2]);
|
||||
}
|
||||
};
|
||||
|
||||
export function set_defaults (obj, conf, defaults) {
|
||||
var defaults_keys = Object.keys(defaults);
|
||||
var conf_keys = Object.keys(conf);
|
||||
var keys_obj = {};
|
||||
var i;
|
||||
for (i = 0; i < defaults_keys.length; i++) { keys_obj[defaults_keys[i]] = 1; }
|
||||
for (i = 0; i < conf_keys.length; i++) { keys_obj[conf_keys[i]] = 1; }
|
||||
var keys = Object.keys(keys_obj);
|
||||
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
var setter = obj['_raw_set_' + keys[i]];
|
||||
if (!setter) {
|
||||
Log.Warn('Invalid property ' + keys[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keys[i] in conf) {
|
||||
setter.call(obj, conf[keys[i]]);
|
||||
} else {
|
||||
setter.call(obj, defaults[keys[i]]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Decode from UTF-8
|
||||
*/
|
||||
export function decodeUTF8 (utf8string) {
|
||||
"use strict";
|
||||
return decodeURIComponent(escape(utf8string));
|
||||
};
|
|
@ -12,14 +12,12 @@
|
|||
* read binary data off of the receive queue.
|
||||
*/
|
||||
|
||||
/* [module]
|
||||
* import Util from "./util";
|
||||
*/
|
||||
import * as Log from './util/logging.js';
|
||||
|
||||
/*jslint browser: true, bitwise: true */
|
||||
/*global Util*/
|
||||
|
||||
/* [module] export default */ function Websock() {
|
||||
export default function Websock() {
|
||||
"use strict";
|
||||
|
||||
this._websocket = null; // WebSocket object
|
||||
|
@ -44,8 +42,6 @@
|
|||
};
|
||||
};
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
// 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.
|
||||
|
@ -173,7 +169,7 @@
|
|||
|
||||
flush: function () {
|
||||
if (this._websocket.bufferedAmount !== 0) {
|
||||
Util.Debug("bufferedAmount: " + this._websocket.bufferedAmount);
|
||||
Log.Debug("bufferedAmount: " + this._websocket.bufferedAmount);
|
||||
}
|
||||
|
||||
if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) {
|
||||
|
@ -223,23 +219,23 @@
|
|||
|
||||
this._websocket.onmessage = this._recv_message.bind(this);
|
||||
this._websocket.onopen = (function () {
|
||||
Util.Debug('>> WebSock.onopen');
|
||||
Log.Debug('>> WebSock.onopen');
|
||||
if (this._websocket.protocol) {
|
||||
Util.Info("Server choose sub-protocol: " + this._websocket.protocol);
|
||||
Log.Info("Server choose sub-protocol: " + this._websocket.protocol);
|
||||
}
|
||||
|
||||
this._eventHandlers.open();
|
||||
Util.Debug("<< WebSock.onopen");
|
||||
Log.Debug("<< WebSock.onopen");
|
||||
}).bind(this);
|
||||
this._websocket.onclose = (function (e) {
|
||||
Util.Debug(">> WebSock.onclose");
|
||||
Log.Debug(">> WebSock.onclose");
|
||||
this._eventHandlers.close(e);
|
||||
Util.Debug("<< WebSock.onclose");
|
||||
Log.Debug("<< WebSock.onclose");
|
||||
}).bind(this);
|
||||
this._websocket.onerror = (function (e) {
|
||||
Util.Debug(">> WebSock.onerror: " + e);
|
||||
Log.Debug(">> WebSock.onerror: " + e);
|
||||
this._eventHandlers.error(e);
|
||||
Util.Debug("<< WebSock.onerror: " + e);
|
||||
Log.Debug("<< WebSock.onerror: " + e);
|
||||
}).bind(this);
|
||||
},
|
||||
|
||||
|
@ -247,7 +243,7 @@
|
|||
if (this._websocket) {
|
||||
if ((this._websocket.readyState === WebSocket.OPEN) ||
|
||||
(this._websocket.readyState === WebSocket.CONNECTING)) {
|
||||
Util.Info("Closing WebSocket connection");
|
||||
Log.Info("Closing WebSocket connection");
|
||||
this._websocket.close();
|
||||
}
|
||||
|
||||
|
@ -322,7 +318,7 @@
|
|||
this._expand_compact_rQ();
|
||||
}
|
||||
} else {
|
||||
Util.Debug("Ignoring empty message");
|
||||
Log.Debug("Ignoring empty message");
|
||||
}
|
||||
} catch (exc) {
|
||||
var exception_str = "";
|
||||
|
@ -340,9 +336,9 @@
|
|||
}
|
||||
|
||||
if (exception_str.length > 0) {
|
||||
Util.Error("recv_message, caught exception: " + exception_str);
|
||||
Log.Error("recv_message, caught exception: " + exception_str);
|
||||
} else {
|
||||
Util.Error("recv_message, caught exception: " + exc);
|
||||
Log.Error("recv_message, caught exception: " + exc);
|
||||
}
|
||||
|
||||
if (typeof exc.name !== 'undefined') {
|
||||
|
@ -353,4 +349,3 @@
|
|||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -1,56 +1,6 @@
|
|||
// Karma configuration
|
||||
|
||||
module.exports = function(config) {
|
||||
/*var customLaunchers = {
|
||||
sl_chrome_win7: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'chrome',
|
||||
platform: 'Windows 7'
|
||||
},
|
||||
|
||||
sl_firefox30_linux: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: '30',
|
||||
platform: 'Linux'
|
||||
},
|
||||
|
||||
sl_firefox26_linux: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'firefox',
|
||||
version: 26,
|
||||
platform: 'Linux'
|
||||
},
|
||||
|
||||
sl_windows7_ie10: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
platform: 'Windows 7',
|
||||
version: '10'
|
||||
},
|
||||
|
||||
sl_windows81_ie11: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'internet explorer',
|
||||
platform: 'Windows 8.1',
|
||||
version: '11'
|
||||
},
|
||||
|
||||
sl_osxmavericks_safari7: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.9',
|
||||
version: '7'
|
||||
},
|
||||
|
||||
sl_osxmtnlion_safari6: {
|
||||
base: 'SauceLabs',
|
||||
browserName: 'safari',
|
||||
platform: 'OS X 10.8',
|
||||
version: '6'
|
||||
}
|
||||
};*/
|
||||
|
||||
var customLaunchers = {};
|
||||
var browsers = [];
|
||||
var useSauce = false;
|
||||
|
@ -93,7 +43,8 @@ module.exports = function(config) {
|
|||
browsers = Object.keys(customLaunchers);
|
||||
} else {
|
||||
useSauce = false;
|
||||
browsers = ['PhantomJS'];
|
||||
//browsers = ['PhantomJS'];
|
||||
browsers = [];
|
||||
}
|
||||
|
||||
var my_conf = {
|
||||
|
@ -103,39 +54,30 @@ module.exports = function(config) {
|
|||
|
||||
// frameworks to use
|
||||
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
|
||||
frameworks: ['mocha', 'sinon', 'chai', 'sinon-chai'],
|
||||
|
||||
frameworks: ['requirejs', 'mocha', 'chai'],
|
||||
|
||||
// list of files / patterns to load in the browser (loaded in order)
|
||||
files: [
|
||||
'tests/fake.*.js',
|
||||
'tests/assertions.js',
|
||||
'core/util.js', // load first to avoid issues, since methods are called immediately
|
||||
//'../core/*.js',
|
||||
'core/base64.js',
|
||||
'core/input/keysym.js',
|
||||
'core/input/keysymdef.js',
|
||||
'core/input/xtscancodes.js',
|
||||
'core/input/util.js',
|
||||
'core/input/devices.js',
|
||||
'core/websock.js',
|
||||
'core/rfb.js',
|
||||
'core/des.js',
|
||||
'core/display.js',
|
||||
'core/inflator.js',
|
||||
'tests/test.*.js'
|
||||
{ pattern: 'vendor/sinon.js', included: false },
|
||||
{ pattern: 'node_modules/sinon-chai/lib/sinon-chai.js', included: false },
|
||||
{ pattern: 'core/**/*.js', included: false },
|
||||
{ pattern: 'vendor/pako/**/*.js', included: false },
|
||||
{ pattern: 'tests/test.*.js', included: false },
|
||||
{ pattern: 'tests/fake.*.js', included: false },
|
||||
{ pattern: 'tests/assertions.js', included: false },
|
||||
'tests/karma-test-main.js',
|
||||
],
|
||||
|
||||
client: {
|
||||
mocha: {
|
||||
// replace Karma debug page with mocha display
|
||||
'reporter': 'html',
|
||||
'ui': 'bdd'
|
||||
}
|
||||
},
|
||||
|
||||
// list of files to exclude
|
||||
exclude: [
|
||||
'../tests/playback.js',
|
||||
'../app/ui.js'
|
||||
],
|
||||
|
||||
customLaunchers: customLaunchers,
|
||||
|
@ -147,14 +89,24 @@ module.exports = function(config) {
|
|||
// preprocess matching files before serving them to the browser
|
||||
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
|
||||
preprocessors: {
|
||||
|
||||
'core/**/*.js': ['babel'],
|
||||
'tests/test.*.js': ['babel'],
|
||||
'tests/fake.*.js': ['babel'],
|
||||
'tests/assertions.js': ['babel'],
|
||||
'vendor/pako/**/*.js': ['babel'],
|
||||
},
|
||||
|
||||
babelPreprocessor: {
|
||||
options: {
|
||||
plugins: ['transform-es2015-modules-amd', 'syntax-dynamic-import'],
|
||||
sourceMap: 'inline',
|
||||
},
|
||||
},
|
||||
|
||||
// test results reporter to use
|
||||
// possible values: 'dots', 'progress'
|
||||
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
|
||||
reporters: ['mocha', 'saucelabs'],
|
||||
reporters: ['mocha'],
|
||||
|
||||
|
||||
// web server port
|
||||
|
@ -186,6 +138,7 @@ module.exports = function(config) {
|
|||
};
|
||||
|
||||
if (useSauce) {
|
||||
my_conf.reporters.push('saucelabs');
|
||||
my_conf.captureTimeout = 0; // use SL timeout
|
||||
my_conf.sauceLabs = {
|
||||
testName: 'noVNC Tests (all)',
|
||||
|
|
30
package.json
30
package.json
|
@ -9,8 +9,7 @@
|
|||
},
|
||||
"scripts": {
|
||||
"test": "PATH=$PATH:node_modules/karma/bin karma start karma.conf.js",
|
||||
"prepublish": "node ./utils/use_require.js --as-require",
|
||||
"build-es6": "node ./utils/use_require.js"
|
||||
"prepublish": "node ./utils/use_require.js --as commonjs"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -28,36 +27,33 @@
|
|||
},
|
||||
"homepage": "https://github.com/kanaka/noVNC",
|
||||
"devDependencies": {
|
||||
"ansi": "^0.3.1",
|
||||
"babel-core": "^6.22.1",
|
||||
"babel-plugin-add-module-exports": "^0.2.1",
|
||||
"babel-plugin-syntax-dynamic-import": "^6.18.0",
|
||||
"babel-plugin-transform-es2015-modules-amd": "^6.22.0",
|
||||
"babel-plugin-transform-es2015-modules-commonjs": "^6.18.0",
|
||||
"babel-plugin-transform-es2015-modules-systemjs": "^6.22.0",
|
||||
"babel-plugin-transform-es2015-modules-umd": "^6.22.0",
|
||||
"babelify": "^7.3.0",
|
||||
"browserify": "^13.1.0",
|
||||
"casperjs": "^1.1.3",
|
||||
"chai": "^3.5.0",
|
||||
"commander": "^2.9.0",
|
||||
"es-module-loader": "^2.1.0",
|
||||
"fs-extra": "^1.0.0",
|
||||
"jsdom": "*",
|
||||
"karma": "^1.3.0",
|
||||
"karma-babel-preprocessor": "^6.0.1",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-mocha": "^1.3.0",
|
||||
"karma-mocha-reporter": "^2.2.0",
|
||||
"karma-phantomjs-launcher": "^1.0.2",
|
||||
"karma-requirejs": "^1.1.0",
|
||||
"karma-sauce-launcher": "^1.0.0",
|
||||
"karma-sinon": "^1.0.5",
|
||||
"karma-sinon-chai-latest": "^0.1.0",
|
||||
"mocha": "^3.1.2",
|
||||
"node-getopt": "*",
|
||||
"open": "^0.0.5",
|
||||
"phantomjs-prebuilt": "^2.1.13",
|
||||
"po2json": "*",
|
||||
"sinon": "^1.17.6",
|
||||
"sinon-chai": "^2.8.0",
|
||||
"spooky": "^0.2.5",
|
||||
"temp": "^0.8.3",
|
||||
"through2": "^2.0.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"pako": "^1.0.3"
|
||||
"requirejs": "^2.3.2",
|
||||
"rollup": "^0.41.4",
|
||||
"rollup-plugin-node-resolve": "^2.0.0",
|
||||
"sinon-chai": "^2.8.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,14 +6,14 @@ LINGUAS := de el nl sv
|
|||
VERSION := $(shell grep '"version"' ../package.json | cut -d '"' -f 4)
|
||||
|
||||
POFILES := $(addsuffix .po,$(LINGUAS))
|
||||
JSFILES := $(addprefix ../app/locale/,$(addsuffix .js,$(LINGUAS)))
|
||||
JSONFILES := $(addprefix ../app/locale/,$(addsuffix .json,$(LINGUAS)))
|
||||
|
||||
update-po: $(POFILES)
|
||||
update-js: $(JSFILES)
|
||||
update-js: $(JSONFILES)
|
||||
|
||||
%.po: noVNC.pot
|
||||
msgmerge --update --lang=$* $@ $<
|
||||
../app/locale/%.js: %.po
|
||||
../app/locale/%.json: %.po
|
||||
./po2js $< $@
|
||||
|
||||
update-pot:
|
||||
|
|
25
po/po2js
25
po/po2js
|
@ -32,25 +32,12 @@ if (opt.argv.length != 2) {
|
|||
|
||||
var data = po2json.parseFileSync(opt.argv[0]);
|
||||
|
||||
var output =
|
||||
"/*\n" +
|
||||
" * Translations for " + data[""]["language"] + "\n" +
|
||||
" *\n" +
|
||||
" * This file was autotomatically generated from " + opt.argv[0] + "\n" +
|
||||
" * DO NOT EDIT!\n" +
|
||||
" */\n" +
|
||||
"\n" +
|
||||
"Language = {\n";
|
||||
var bodyPart = Object.keys(data).filter((msgid) => msgid !== "").map((msgid) => {
|
||||
if (msgid === "") return;
|
||||
var msgstr = data[msgid][1];
|
||||
return " " + JSON.stringify(msgid) + ": " + JSON.stringify(msgstr);
|
||||
}).join(",\n");
|
||||
|
||||
for (msgid in data) {
|
||||
if (msgid === "")
|
||||
continue;
|
||||
|
||||
msgstr = data[msgid][1];
|
||||
output += " " + JSON.stringify(msgid) + ": " +
|
||||
JSON.stringify(msgstr) + ",\n";
|
||||
}
|
||||
|
||||
output += "};\n";
|
||||
var output = "{\n" + bodyPart + "\n}";
|
||||
|
||||
fs.writeFileSync(opt.argv[1], output);
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
var FakeWebSocket;
|
||||
|
||||
(function () {
|
||||
// PhantomJS can't create Event objects directly, so we need to use this
|
||||
function make_event(name, props) {
|
||||
var evt = document.createEvent('Event');
|
||||
|
@ -13,7 +10,7 @@ var FakeWebSocket;
|
|||
return evt;
|
||||
}
|
||||
|
||||
FakeWebSocket = function (uri, protocols) {
|
||||
export default function FakeWebSocket (uri, protocols) {
|
||||
this.url = uri;
|
||||
this.binaryType = "arraybuffer";
|
||||
this.extensions = "";
|
||||
|
@ -88,4 +85,3 @@ var FakeWebSocket;
|
|||
WebSocket = WebSocket.__real_version;
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
var TEST_REGEXP = /test\..*\.js/;
|
||||
var allTestFiles = [];
|
||||
|
||||
Object.keys(window.__karma__.files).forEach(function (file) {
|
||||
if (TEST_REGEXP.test(file)) {
|
||||
// TODO: normalize?
|
||||
allTestFiles.push(file);
|
||||
}
|
||||
});
|
||||
|
||||
require.config({
|
||||
baseUrl: '/base',
|
||||
deps: allTestFiles,
|
||||
callback: window.__karma__.start,
|
||||
paths: {
|
||||
'sinon': 'vendor/sinon',
|
||||
},
|
||||
});
|
|
@ -0,0 +1,174 @@
|
|||
import * as WebUtil from '../app/webutil.js';
|
||||
import RecordingPlayer from './playback.js';
|
||||
|
||||
var frames = null;
|
||||
var encoding = null;
|
||||
|
||||
function message(str) {
|
||||
console.log(str);
|
||||
var cell = document.getElementById('messages');
|
||||
cell.textContent += str + "\n";
|
||||
cell.scrollTop = cell.scrollHeight;
|
||||
}
|
||||
|
||||
function loadFile() {
|
||||
const fname = WebUtil.getQueryVar('data', null);
|
||||
|
||||
if (!fname) {
|
||||
return Promise.reject("Must specify data=FOO in query string.");
|
||||
}
|
||||
|
||||
message("Loading " + fname);
|
||||
return import(`../recordings/${fname}#nocache`);
|
||||
}
|
||||
|
||||
function enableUI(recording) {
|
||||
var iterations = WebUtil.getQueryVar('iterations', 3);
|
||||
document.getElementById('iterations').value = iterations;
|
||||
|
||||
var mode = WebUtil.getQueryVar('mode', 3);
|
||||
if (mode === 'realtime') {
|
||||
document.getElementById('mode2').checked = true;
|
||||
} else {
|
||||
document.getElementById('mode1').checked = true;
|
||||
}
|
||||
|
||||
message("VNC_frame_data.length: " + recording.VNC_frame_data.length);
|
||||
|
||||
const startButton = document.getElementById('startButton');
|
||||
startButton.disabled = false
|
||||
startButton.addEventListener('click', start);
|
||||
|
||||
frames = recording.VNC_frame_data;
|
||||
encoding = recording.VNC_frame_encoding;
|
||||
}
|
||||
|
||||
const notification = function (rfb, mesg, level, options) {
|
||||
document.getElementById('VNC_status').textContent = mesg;
|
||||
}
|
||||
|
||||
function IterationPlayer (iterations, frames, encoding) {
|
||||
this._iterations = iterations;
|
||||
|
||||
this._iteration = undefined;
|
||||
this._player = undefined;
|
||||
|
||||
this._start_time = undefined;
|
||||
|
||||
this._frames = frames;
|
||||
this._encoding = encoding;
|
||||
|
||||
this._state = 'running';
|
||||
|
||||
this.onfinish = function() {};
|
||||
this.oniterationfinish = function() {};
|
||||
this.rfbdisconnected = function() {};
|
||||
this.rfbnotification = function() {};
|
||||
}
|
||||
|
||||
IterationPlayer.prototype = {
|
||||
start: function (mode) {
|
||||
this._iteration = 0;
|
||||
this._start_time = (new Date()).getTime();
|
||||
|
||||
this._realtime = mode.startsWith('realtime');
|
||||
this._trafficMgmt = !mode.endsWith('-no-mgmt');
|
||||
|
||||
this._nextIteration();
|
||||
},
|
||||
|
||||
_nextIteration: function () {
|
||||
const player = new RecordingPlayer(this._frames, this._encoding, this._disconnected.bind(this), this._notification.bind(this));
|
||||
player.onfinish = this._iterationFinish.bind(this);
|
||||
|
||||
if (this._state !== 'running') { return; }
|
||||
|
||||
this._iteration++;
|
||||
if (this._iteration > this._iterations) {
|
||||
this._finish();
|
||||
return;
|
||||
}
|
||||
|
||||
player.run(this._realtime, this._trafficMgmt);
|
||||
},
|
||||
|
||||
_finish: function () {
|
||||
const endTime = (new Date()).getTime();
|
||||
const totalDuration = endTime - this._start_time;
|
||||
|
||||
const evt = new Event('finish');
|
||||
evt.duration = totalDuration;
|
||||
evt.iterations = this._iterations;
|
||||
this.onfinish(evt);
|
||||
},
|
||||
|
||||
_iterationFinish: function (duration) {
|
||||
const evt = new Event('iterationfinish');
|
||||
evt.duration = duration;
|
||||
evt.number = this._iteration;
|
||||
this.oniterationfinish(evt);
|
||||
|
||||
this._nextIteration();
|
||||
},
|
||||
|
||||
_disconnected: function (rfb, reason, frame) {
|
||||
if (reason) {
|
||||
this._state = 'failed';
|
||||
}
|
||||
|
||||
var evt = new Event('rfbdisconnected');
|
||||
evt.reason = reason;
|
||||
evt.frame = frame;
|
||||
|
||||
this.onrfbdisconnected(evt);
|
||||
},
|
||||
|
||||
_notification: function (rfb, msg, level, options) {
|
||||
var evt = new Event('rfbnotification');
|
||||
evt.message = msg;
|
||||
evt.level = level;
|
||||
evt.options = options;
|
||||
|
||||
this.onrfbnotification(evt);
|
||||
},
|
||||
};
|
||||
|
||||
function start() {
|
||||
document.getElementById('startButton').value = "Running";
|
||||
document.getElementById('startButton').disabled = true;
|
||||
|
||||
const iterations = document.getElementById('iterations').value;
|
||||
|
||||
var mode;
|
||||
|
||||
if (document.getElementById('mode1').checked) {
|
||||
message(`Starting performance playback (fullspeed) [${iterations} iteration(s)]`);
|
||||
mode = 'perftest';
|
||||
} else {
|
||||
message(`Starting realtime playback [${iterations} iteration(s)]`);
|
||||
mode = 'realtime';
|
||||
}
|
||||
|
||||
const player = new IterationPlayer(iterations, frames, encoding);
|
||||
player.oniterationfinish = function (evt) {
|
||||
message(`Iteration ${evt.number} took ${evt.duration}ms`);
|
||||
};
|
||||
player.onrfbdisconnected = function (evt) {
|
||||
if (evt.reason) {
|
||||
message(`noVNC sent disconnected during iteration ${evt.iteration} frame ${evt.frame}`);
|
||||
}
|
||||
};
|
||||
player.onrfbnotification = function (evt) {
|
||||
document.getElementById('VNC_status').textContent = evt.message;
|
||||
};
|
||||
player.onfinish = function (evt) {
|
||||
const iterTime = parseInt(evt.duration / evt.iterations, 10);
|
||||
message(`${evt.iterations} iterations took ${evt.duration}ms (average ${iterTime}ms / iteration)`);
|
||||
|
||||
document.getElementById('startButton').disabled = false;
|
||||
document.getElementById('startButton').value = "Start";
|
||||
};
|
||||
player.start(mode);
|
||||
}
|
||||
|
||||
loadFile().then(enableUI).catch(message);
|
|
@ -4,29 +4,17 @@
|
|||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
/*jslint browser: true, white: false */
|
||||
/*global Util, VNC_frame_data, finish */
|
||||
|
||||
var rfb, mode, test_state, frame_idx, frame_length,
|
||||
iteration, iterations, istart_time, encoding,
|
||||
|
||||
// Pre-declarations for jslint
|
||||
send_array, next_iteration, end_iteration, queue_next_packet,
|
||||
do_packet, enable_test_mode;
|
||||
|
||||
// Override send_array
|
||||
send_array = function (arr) {
|
||||
// Stub out send_array
|
||||
};
|
||||
import RFB from '../core/rfb.js';
|
||||
import * as Log from '../core/util/logging.js';
|
||||
import Base64 from '../core/base64.js';
|
||||
|
||||
// Immediate polyfill
|
||||
if (window.setImmediate === undefined) {
|
||||
if (setImmediate === undefined) {
|
||||
var _immediateIdCounter = 1;
|
||||
var _immediateFuncs = {};
|
||||
|
||||
window.setImmediate = function (func) {
|
||||
var index = Util._immediateIdCounter++;
|
||||
var setImmediate = function (func) {
|
||||
var index = _immediateIdCounter++;
|
||||
_immediateFuncs[index] = func;
|
||||
window.postMessage("noVNC immediate trigger:" + index, "*");
|
||||
return index;
|
||||
|
@ -56,12 +44,67 @@ if (window.setImmediate === undefined) {
|
|||
window.addEventListener("message", _onMessage);
|
||||
}
|
||||
|
||||
enable_test_mode = function () {
|
||||
rfb._sock.send = send_array;
|
||||
rfb._sock.close = function () {};
|
||||
rfb._sock.flush = function () {};
|
||||
rfb._checkEvents = function () {};
|
||||
rfb.connect = function (host, port, password, path) {
|
||||
export default function RecordingPlayer (frames, encoding, disconnected, notification) {
|
||||
this._frames = frames;
|
||||
this._encoding = encoding;
|
||||
|
||||
this._disconnected = disconnected;
|
||||
this._notification = notification;
|
||||
|
||||
if (this._encoding === undefined) {
|
||||
let frame = this._frames[0];
|
||||
let start = frame.indexOf('{', 1) + 1;
|
||||
if (frame.slice(start).startsWith('UkZC')) {
|
||||
this._encoding = 'base64';
|
||||
} else {
|
||||
this._encoding = 'binary';
|
||||
}
|
||||
}
|
||||
|
||||
this._rfb = undefined;
|
||||
this._frame_length = this._frames.length;
|
||||
|
||||
this._frame_index = 0;
|
||||
this._start_time = undefined;
|
||||
this._realtime = true;
|
||||
this._trafficManagement = true;
|
||||
|
||||
this._running = false;
|
||||
|
||||
this.onfinish = function () {};
|
||||
}
|
||||
|
||||
RecordingPlayer.prototype = {
|
||||
run: function (realtime, trafficManagement) {
|
||||
// initialize a new RFB
|
||||
this._rfb = new RFB({'target': document.getElementById('VNC_canvas'),
|
||||
'view_only': true,
|
||||
'onDisconnected': this._handleDisconnect.bind(this),
|
||||
'onNotification': this._notification});
|
||||
this._enablePlaybackMode();
|
||||
|
||||
// reset the frame index and timer
|
||||
this._frame_index = 0;
|
||||
this._start_time = (new Date()).getTime();
|
||||
|
||||
this._realtime = realtime;
|
||||
this._trafficManagement = (trafficManagement === undefined) ? !realtime : trafficManagement;
|
||||
|
||||
this._running = true;
|
||||
|
||||
// launch the tests
|
||||
this._rfb.connect('test', 0, 'bogus');
|
||||
|
||||
this._queueNextPacket();
|
||||
},
|
||||
|
||||
// _enablePlaybackMode mocks out things not required for running playback
|
||||
_enablePlaybackMode: function () {
|
||||
this._rfb._sock.send = function (arr) {};
|
||||
this._rfb._sock.close = function () {};
|
||||
this._rfb._sock.flush = function () {};
|
||||
this._rfb._checkEvents = function () {};
|
||||
this._rfb.connect = function (host, port, password, path) {
|
||||
this._rfb_host = host;
|
||||
this._rfb_port = port;
|
||||
this._rfb_password = (password !== undefined) ? password : "";
|
||||
|
@ -70,129 +113,91 @@ enable_test_mode = function () {
|
|||
this._rfb_connection_state = 'connecting';
|
||||
this._rfb_init_state = 'ProtocolVersion';
|
||||
};
|
||||
};
|
||||
},
|
||||
|
||||
next_iteration = function () {
|
||||
rfb = new RFB({'target': document.getElementById('VNC_canvas'),
|
||||
'view_only': true,
|
||||
'onDisconnected': disconnected,
|
||||
'onNotification': notification});
|
||||
enable_test_mode();
|
||||
_queueNextPacket: function () {
|
||||
if (!this._running) { return; }
|
||||
|
||||
// Missing in older recordings
|
||||
if (typeof VNC_frame_encoding === 'undefined') {
|
||||
var frame = VNC_frame_data[0];
|
||||
var start = frame.indexOf('{', 1) + 1;
|
||||
if (frame.slice(start).startsWith('UkZC')) {
|
||||
encoding = 'base64';
|
||||
} else {
|
||||
encoding = 'binary';
|
||||
}
|
||||
} else {
|
||||
encoding = VNC_frame_encoding;
|
||||
}
|
||||
var frame = this._frames[this._frame_index];
|
||||
|
||||
if (iteration === 0) {
|
||||
frame_length = VNC_frame_data.length;
|
||||
test_state = 'running';
|
||||
}
|
||||
|
||||
if (test_state !== 'running') { return; }
|
||||
|
||||
iteration += 1;
|
||||
if (iteration > iterations) {
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
|
||||
frame_idx = 0;
|
||||
istart_time = (new Date()).getTime();
|
||||
rfb.connect('test', 0, "bogus");
|
||||
|
||||
queue_next_packet();
|
||||
|
||||
};
|
||||
|
||||
end_iteration = function () {
|
||||
if (rfb._display.pending()) {
|
||||
rfb._display.set_onFlush(function () {
|
||||
if (rfb._flushing) {
|
||||
rfb._onFlush();
|
||||
}
|
||||
end_iteration();
|
||||
});
|
||||
rfb._display.flush();
|
||||
} else {
|
||||
next_iteration();
|
||||
}
|
||||
};
|
||||
|
||||
queue_next_packet = function () {
|
||||
var frame, foffset, toffset, delay;
|
||||
if (test_state !== 'running') { return; }
|
||||
|
||||
frame = VNC_frame_data[frame_idx];
|
||||
while ((frame_idx < frame_length) && (frame.charAt(0) === "}")) {
|
||||
//Util.Debug("Send frame " + frame_idx);
|
||||
frame_idx += 1;
|
||||
frame = VNC_frame_data[frame_idx];
|
||||
// skip send frames
|
||||
while (this._frame_index < this._frame_length && frame.charAt(0) === "}") {
|
||||
this._frame_index++;
|
||||
frame = this._frames[this._frame_index];
|
||||
}
|
||||
|
||||
if (frame === 'EOF') {
|
||||
Util.Debug("Finished, found EOF");
|
||||
end_iteration();
|
||||
return;
|
||||
}
|
||||
if (frame_idx >= frame_length) {
|
||||
Util.Debug("Finished, no more frames");
|
||||
end_iteration();
|
||||
Log.Debug('Finished, found EOF');
|
||||
this._finish();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mode === 'realtime') {
|
||||
foffset = frame.slice(1, frame.indexOf('{', 1));
|
||||
toffset = (new Date()).getTime() - istart_time;
|
||||
delay = foffset - toffset;
|
||||
if (delay < 1) {
|
||||
delay = 1;
|
||||
if (this._frame_index >= this._frame_length) {
|
||||
Log.Debug('Finished, no more frames');
|
||||
this._finish();
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(do_packet, delay);
|
||||
if (this._realtime) {
|
||||
let foffset = frame.slice(1, frame.indexOf('{', 1));
|
||||
let toffset = (new Date()).getTime() - this._start_time;
|
||||
let delay = foffset - toffset;
|
||||
if (delay < 1) delay = 1;
|
||||
|
||||
setTimeout(this._doPacket.bind(this), delay);
|
||||
} else {
|
||||
window.setImmediate(do_packet);
|
||||
setImmediate(this._doPacket.bind(this));
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
var bytes_processed = 0;
|
||||
|
||||
do_packet = function () {
|
||||
// Avoid having an excessive queue buildup
|
||||
if (rfb._flushing && (mode !== 'realtime')) {
|
||||
rfb._display.set_onFlush(function () {
|
||||
rfb._display.set_onFlush(rfb._onFlush.bind(rfb));
|
||||
rfb._onFlush();
|
||||
do_packet();
|
||||
_doPacket: function () {
|
||||
// Avoid having excessive queue buildup in non-realtime mode
|
||||
if (!this._trafficManagement && this._rfb._flushing) {
|
||||
let player = this;
|
||||
this._rfb.display.set_onFlush(function () {
|
||||
this._rfb._display.set_onFlush(this._rfb._onFlush.bind(this._rfb));
|
||||
this._rfb._onFlush();
|
||||
player._doPacket();
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
//Util.Debug("Processing frame: " + frame_idx);
|
||||
var frame = VNC_frame_data[frame_idx],
|
||||
start = frame.indexOf('{', 1) + 1;
|
||||
var u8;
|
||||
if (encoding === 'base64') {
|
||||
u8 = Base64.decode(frame.slice(start));
|
||||
const frame = this._frames[this._frame_index];
|
||||
var start = frame.indexOf('{', 1) + 1;
|
||||
if (this._encoding === 'base64') {
|
||||
var u8 = Base64.decode(frame.slice(start));
|
||||
start = 0;
|
||||
} else {
|
||||
u8 = new Uint8Array(frame.length - start);
|
||||
for (var i = 0; i < frame.length - start; i++) {
|
||||
var u8 = new Uint8Array(frame.length - start);
|
||||
for (let i = 0; i < frame.length - start; i++) {
|
||||
u8[i] = frame.charCodeAt(start + i);
|
||||
}
|
||||
}
|
||||
bytes_processed += u8.length;
|
||||
rfb._sock._recv_message({'data' : u8});
|
||||
frame_idx += 1;
|
||||
|
||||
queue_next_packet();
|
||||
this._rfb._sock._recv_message({'data': u8});
|
||||
this._frame_index++;
|
||||
|
||||
this._queueNextPacket();
|
||||
},
|
||||
|
||||
_finish() {
|
||||
if (this._rfb._display.pending()) {
|
||||
var player = this;
|
||||
this._rfb._display.set_onFlush(function () {
|
||||
if (player._rfb._flushing) {
|
||||
player._rfb._onFlush();
|
||||
}
|
||||
player._finish();
|
||||
});
|
||||
this._rfb._display.flush();
|
||||
} else {
|
||||
this._running = false;
|
||||
this.onfinish((new Date()).getTime() - this._start_time);
|
||||
}
|
||||
},
|
||||
|
||||
_handleDisconnect(rfb, reason) {
|
||||
this._running = false;
|
||||
this._disconnected(rfb, reason, this._frame_index);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -1,114 +0,0 @@
|
|||
var Spooky = require('spooky');
|
||||
var path = require('path');
|
||||
|
||||
var phantom_path = require('phantomjs-prebuilt').path;
|
||||
var casper_path = path.resolve(__dirname, '../node_modules/casperjs/bin/casperjs');
|
||||
process.env.PHANTOMJS_EXECUTABLE = phantom_path;
|
||||
var casper_opts = {
|
||||
child: {
|
||||
transport: 'http',
|
||||
command: casper_path
|
||||
},
|
||||
casper: {
|
||||
logLevel: 'debug',
|
||||
verbose: true
|
||||
}
|
||||
};
|
||||
|
||||
var provide_emitter = function(file_paths, debug_port) {
|
||||
if (debug_port) {
|
||||
casper_opts.child['remote-debugger-port'] = debug_port;
|
||||
var debug_url = ('https://localhost:' + debug_port +
|
||||
'/webkit/inspector/inspector.html?page=');
|
||||
console.info('[remote-debugger] Navigate to ' + debug_url + '1 and ' +
|
||||
'run `__run();` in the console to continue loading.' +
|
||||
'\n[remote-debugger] Navigate to ' + debug_url + '2 to ' +
|
||||
'view the actual page source.\n' +
|
||||
'[remote-debugger] Use the `debugger;` statement to ' +
|
||||
'trigger an initial breakpoint.');
|
||||
}
|
||||
|
||||
var spooky = new Spooky(casper_opts, function(err) {
|
||||
if (err) {
|
||||
if (err.stack) console.warn(err.stack);
|
||||
else console.warn(err);
|
||||
return;
|
||||
}
|
||||
spooky.start('about:blank');
|
||||
|
||||
file_paths.forEach(function(file_path, path_ind) {
|
||||
spooky.thenOpen('file://'+file_path);
|
||||
spooky.waitFor(function() {
|
||||
return this.getGlobal('__mocha_done') === true;
|
||||
},
|
||||
[{ path_ind: path_ind }, function() {
|
||||
var res_json = {
|
||||
file_ind: path_ind
|
||||
};
|
||||
|
||||
res_json.num_tests = this.evaluate(function() { return document.querySelectorAll('li.test').length; });
|
||||
res_json.num_passes = this.evaluate(function() { return document.querySelectorAll('li.test.pass').length; });
|
||||
res_json.num_fails = this.evaluate(function() { return document.querySelectorAll('li.test.fail').length; });
|
||||
res_json.num_slow = this.evaluate(function() { return document.querySelectorAll('li.test.pass:not(.fast):not(.pending)').length; });
|
||||
res_json.num_skipped = this.evaluate(function () { return document.querySelectorAll('li.test.pending').length; });
|
||||
res_json.duration = this.evaluate(function() { return document.querySelector('li.duration em').textContent; });
|
||||
|
||||
res_json.suites = this.evaluate(function() {
|
||||
var traverse_node = function(elem) {
|
||||
var res;
|
||||
if (elem.classList.contains('suite')) {
|
||||
res = {
|
||||
type: 'suite',
|
||||
name: elem.querySelector('h1').textContent,
|
||||
has_subfailures: elem.querySelectorAll('li.test.fail').length > 0,
|
||||
};
|
||||
|
||||
var child_elems = elem.querySelector('ul').children;
|
||||
res.children = Array.prototype.map.call(child_elems, traverse_node);
|
||||
return res;
|
||||
}
|
||||
else {
|
||||
var h2_content = elem.querySelector('h2').childNodes;
|
||||
res = {
|
||||
type: 'test',
|
||||
text: h2_content[0].textContent,
|
||||
};
|
||||
|
||||
if (elem.classList.contains('pass')) {
|
||||
res.pass = true;
|
||||
if (elem.classList.contains('pending')) {
|
||||
res.slow = false;
|
||||
res.skipped = true;
|
||||
}
|
||||
else {
|
||||
res.slow = !elem.classList.contains('fast');
|
||||
res.skipped = false;
|
||||
res.duration = h2_content[1].textContent;
|
||||
}
|
||||
}
|
||||
else {
|
||||
res.error = elem.querySelector('pre.error').textContent;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
var top_suites = document.querySelectorAll('#mocha-report > li.suite');
|
||||
return Array.prototype.map.call(top_suites, traverse_node);
|
||||
});
|
||||
|
||||
res_json.replay = this.evaluate(function() { return document.querySelector('a.replay').textContent; });
|
||||
|
||||
this.emit('test_ready', res_json);
|
||||
}]);
|
||||
});
|
||||
spooky.run();
|
||||
});
|
||||
|
||||
return spooky;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
provide_emitter: provide_emitter,
|
||||
name: 'SpookyJS (CapserJS on PhantomJS)'
|
||||
};
|
|
@ -1,360 +0,0 @@
|
|||
#!/usr/bin/env node
|
||||
var ansi = require('ansi');
|
||||
var program = require('commander');
|
||||
var path = require('path');
|
||||
var fs = require('fs');
|
||||
|
||||
var make_list = function(val) {
|
||||
return val.split(',');
|
||||
};
|
||||
|
||||
program
|
||||
.option('-t, --tests <testlist>', 'Run the specified html-file-based test(s). \'testlist\' should be a comma-separated list', make_list, [])
|
||||
.option('-a, --print-all', 'Print all tests, not just the failures')
|
||||
.option('--disable-color', 'Explicitly disable color')
|
||||
.option('-c, --color', 'Explicitly enable color (default is to use color when not outputting to a pipe)')
|
||||
.option('-i, --auto-inject <includefiles>', 'Treat the test list as a set of mocha JS files, and automatically generate HTML files with which to test test. \'includefiles\' should be a comma-separated list of paths to javascript files to include in each of the generated HTML files', make_list, null)
|
||||
.option('-p, --provider <name>', 'Use the given provider (defaults to "casper"). Currently, may be "casper" or "zombie"', 'casper')
|
||||
.option('-g, --generate-html', 'Instead of running the tests, just return the path to the generated HTML file, then wait for user interaction to exit (should be used with .js tests).')
|
||||
.option('-o, --open-in-browser', 'Open the generated HTML files in a web browser using the "open" module (must be used with the "-g"/"--generate-html" option).')
|
||||
.option('--output-html', 'Instead of running the tests, just output the generated HTML source to STDOUT (should be used with .js tests)')
|
||||
.option('-d, --debug', 'Show debug output (the "console" event) from the provider')
|
||||
.option('-r, --relative', 'Use relative paths in the generated HTML file')
|
||||
.option('--debugger <port>', 'Enable the remote debugger for CasperJS')
|
||||
.parse(process.argv);
|
||||
|
||||
if (program.tests.length === 0) {
|
||||
program.tests = fs.readdirSync(__dirname).filter(function(f) { return (/^test\.(\w|\.|-)+\.js$/).test(f); });
|
||||
program.tests = program.tests.map(function (f) { return path.resolve(__dirname, f); }); // add full paths in
|
||||
console.log('using files %s', program.tests);
|
||||
}
|
||||
|
||||
var file_paths = [];
|
||||
|
||||
var all_js = program.tests.reduce(function(a,e) { return a && e.slice(-3) == '.js'; }, true);
|
||||
|
||||
var get_path = function (/* arguments */) {
|
||||
if (program.relative) {
|
||||
return path.join.apply(null, arguments);
|
||||
} else {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
args.unshift(__dirname, '..');
|
||||
return path.resolve.apply(null, args);
|
||||
}
|
||||
};
|
||||
|
||||
var get_path_cwd = function (/* arguments */) {
|
||||
if (program.relative) {
|
||||
var part_path = path.join.apply(null, arguments);
|
||||
return path.relative(path.join(__dirname, '..'), path.resolve(process.cwd(), part_path));
|
||||
} else {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
args.unshift(process.cwd());
|
||||
return path.resolve.apply(null, args);
|
||||
}
|
||||
};
|
||||
|
||||
if (all_js && !program.autoInject) {
|
||||
var all_modules = {};
|
||||
|
||||
// uses the first instance of the string 'requires local modules: '
|
||||
program.tests.forEach(function (testname) {
|
||||
var full_path = path.resolve(process.cwd(), testname);
|
||||
var content = fs.readFileSync(full_path).toString();
|
||||
var ind = content.indexOf('requires local modules: ');
|
||||
if (ind > -1) {
|
||||
ind += 'requires local modules: '.length;
|
||||
var eol = content.indexOf('\n', ind);
|
||||
var modules = content.slice(ind, eol).split(/,\s*/);
|
||||
modules.forEach(function (mod) {
|
||||
all_modules[get_path('core/', mod) + '.js'] = 1;
|
||||
});
|
||||
}
|
||||
|
||||
var fakes_ind = content.indexOf('requires test modules: ');
|
||||
if (fakes_ind > -1) {
|
||||
fakes_ind += 'requires test modules: '.length;
|
||||
var fakes_eol = content.indexOf('\n', fakes_ind);
|
||||
var fakes_modules = content.slice(fakes_ind, fakes_eol).split(/,\s*/);
|
||||
fakes_modules.forEach(function (mod) {
|
||||
all_modules[get_path('tests/', mod) + '.js'] = 1;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
program.autoInject = Object.keys(all_modules);
|
||||
}
|
||||
|
||||
if (program.autoInject) {
|
||||
var temp = require('temp');
|
||||
temp.track();
|
||||
|
||||
var template = {
|
||||
header: "<html>\n<head>\n<meta charset='utf-8' />\n<link rel='stylesheet' href='" + get_path('node_modules/mocha/mocha.css') + "'/>\n</head>\n<body><div id='mocha'></div>",
|
||||
script_tag: function(p) { return "<script src='" + p + "'></script>"; },
|
||||
footer: "<script>\nmocha.checkLeaks();\nmocha.globals(['navigator', 'create', 'ClientUtils', '__utils__', 'requestAnimationFrame', 'WebSocket']);\nmocha.run(function () { window.__mocha_done = true; });\n</script>\n</body>\n</html>"
|
||||
};
|
||||
|
||||
template.header += "\n" + template.script_tag(get_path('node_modules/chai/chai.js'));
|
||||
template.header += "\n" + template.script_tag(get_path('node_modules/mocha/mocha.js'));
|
||||
template.header += "\n" + template.script_tag(get_path('node_modules/sinon/pkg/sinon.js'));
|
||||
template.header += "\n" + template.script_tag(get_path('node_modules/sinon-chai/lib/sinon-chai.js'));
|
||||
template.header += "\n<script>mocha.setup('bdd');</script>";
|
||||
|
||||
|
||||
template.header = program.autoInject.reduce(function(acc, sn) {
|
||||
return acc + "\n" + template.script_tag(get_path_cwd(sn));
|
||||
}, template.header);
|
||||
|
||||
file_paths = program.tests.map(function(jsn, ind) {
|
||||
var templ = template.header;
|
||||
templ += "\n";
|
||||
templ += template.script_tag(get_path_cwd(jsn));
|
||||
templ += template.footer;
|
||||
|
||||
var tempfile = temp.openSync({ prefix: 'novnc-zombie-inject-', suffix: '-file_num-'+ind+'.html' });
|
||||
fs.writeSync(tempfile.fd, templ);
|
||||
fs.closeSync(tempfile.fd);
|
||||
return tempfile.path;
|
||||
});
|
||||
|
||||
}
|
||||
else {
|
||||
file_paths = program.tests.map(function(fn) {
|
||||
return path.resolve(process.cwd(), fn);
|
||||
});
|
||||
}
|
||||
|
||||
var use_ansi = false;
|
||||
if (program.color) use_ansi = true;
|
||||
else if (program.disableColor) use_ansi = false;
|
||||
else if (process.stdout.isTTY) use_ansi = true;
|
||||
|
||||
var cursor = ansi(process.stdout, { enabled: use_ansi });
|
||||
|
||||
if (program.outputHtml) {
|
||||
file_paths.forEach(function(path, path_ind) {
|
||||
fs.readFile(path, function(err, data) {
|
||||
if (err) {
|
||||
console.warn(error.stack);
|
||||
return;
|
||||
}
|
||||
|
||||
if (use_ansi) {
|
||||
cursor
|
||||
.bold()
|
||||
.write(program.tests[path_ind])
|
||||
.reset()
|
||||
.write("\n")
|
||||
.write(Array(program.tests[path_ind].length+1).join('='))
|
||||
.write("\n\n");
|
||||
}
|
||||
|
||||
cursor
|
||||
.write(data)
|
||||
.write("\n\n");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (program.generateHtml) {
|
||||
var open_browser;
|
||||
if (program.openInBrowser) {
|
||||
open_browser = require('open');
|
||||
}
|
||||
|
||||
file_paths.forEach(function(path, path_ind) {
|
||||
cursor
|
||||
.bold()
|
||||
.write(program.tests[path_ind])
|
||||
.write(": ")
|
||||
.reset()
|
||||
.write(path)
|
||||
.write("\n");
|
||||
|
||||
if (program.openInBrowser) {
|
||||
open_browser(path);
|
||||
}
|
||||
});
|
||||
console.log('');
|
||||
}
|
||||
|
||||
if (program.generateHtml) {
|
||||
process.stdin.resume(); // pause until C-c
|
||||
process.on('SIGINT', function() {
|
||||
process.stdin.pause(); // exit
|
||||
});
|
||||
}
|
||||
|
||||
if (!program.outputHtml && !program.generateHtml) {
|
||||
var failure_count = 0;
|
||||
|
||||
var prov = require(path.resolve(__dirname, 'run_from_console.'+program.provider+'.js'));
|
||||
|
||||
cursor
|
||||
.write("Running tests ")
|
||||
.bold()
|
||||
.write(program.tests.join(', '))
|
||||
.reset()
|
||||
.grey()
|
||||
.write(' using provider '+prov.name)
|
||||
.reset()
|
||||
.write("\n");
|
||||
//console.log("Running tests %s using provider %s", program.tests.join(', '), prov.name);
|
||||
|
||||
var provider = prov.provide_emitter(file_paths, program.debugger);
|
||||
provider.on('test_ready', function(test_json) {
|
||||
console.log('');
|
||||
|
||||
filename = program.tests[test_json.file_ind];
|
||||
|
||||
cursor.bold();
|
||||
console.log('Results for %s:', filename);
|
||||
console.log(Array('Results for :'.length+filename.length+1).join('='));
|
||||
cursor.reset();
|
||||
|
||||
console.log('');
|
||||
|
||||
cursor
|
||||
.write(''+test_json.num_tests+' tests run, ')
|
||||
.green()
|
||||
.write(''+test_json.num_passes+' passed');
|
||||
if (test_json.num_slow > 0) {
|
||||
cursor
|
||||
.reset()
|
||||
.write(' (');
|
||||
cursor
|
||||
.yellow()
|
||||
.write(''+test_json.num_slow+' slow')
|
||||
.reset()
|
||||
.write(')');
|
||||
}
|
||||
cursor
|
||||
.reset()
|
||||
.write(', ');
|
||||
cursor
|
||||
.red()
|
||||
.write(''+test_json.num_fails+' failed');
|
||||
if (test_json.num_skipped > 0) {
|
||||
cursor
|
||||
.reset()
|
||||
.write(', ')
|
||||
.grey()
|
||||
.write(''+test_json.num_skipped+' skipped');
|
||||
}
|
||||
cursor
|
||||
.reset()
|
||||
.write(' -- duration: '+test_json.duration+"s\n");
|
||||
|
||||
console.log('');
|
||||
|
||||
if (test_json.num_fails > 0 || program.printAll) {
|
||||
var extract_error_lines = function (err) {
|
||||
// the split is to avoid a weird thing where in PhantomJS where we get a stack trace too
|
||||
var err_lines = err.split('\n');
|
||||
if (err_lines.length == 1) {
|
||||
return err_lines[0];
|
||||
} else {
|
||||
var ind;
|
||||
for (ind = 0; ind < err_lines.length; ind++) {
|
||||
var at_ind = err_lines[ind].trim().indexOf('at ');
|
||||
if (at_ind === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return err_lines.slice(0, ind).join('\n');
|
||||
}
|
||||
};
|
||||
|
||||
var traverse_tree = function(indentation, node) {
|
||||
if (node.type == 'suite') {
|
||||
if (!node.has_subfailures && !program.printAll) return;
|
||||
|
||||
if (indentation === 0) {
|
||||
cursor.bold();
|
||||
console.log(node.name);
|
||||
console.log(Array(node.name.length+1).join('-'));
|
||||
cursor.reset();
|
||||
}
|
||||
else {
|
||||
cursor
|
||||
.write(Array(indentation+3).join('#'))
|
||||
.bold()
|
||||
.write(' '+node.name+' ')
|
||||
.reset()
|
||||
.write(Array(indentation+3).join('#'))
|
||||
.write("\n");
|
||||
}
|
||||
|
||||
console.log('');
|
||||
|
||||
for (var i = 0; i < node.children.length; i++) {
|
||||
traverse_tree(indentation+1, node.children[i]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!node.pass) {
|
||||
cursor.magenta();
|
||||
console.log('- failed: '+node.text+test_json.replay);
|
||||
cursor.red();
|
||||
console.log(' '+extract_error_lines(node.error));
|
||||
cursor.reset();
|
||||
console.log('');
|
||||
}
|
||||
else if (program.printAll) {
|
||||
if (node.skipped) {
|
||||
cursor
|
||||
.grey()
|
||||
.write('- skipped: '+node.text);
|
||||
}
|
||||
else {
|
||||
if (node.slow) cursor.yellow();
|
||||
else cursor.green();
|
||||
|
||||
cursor
|
||||
.write('- pass: '+node.text)
|
||||
.grey()
|
||||
.write(' ('+node.duration+') ');
|
||||
}
|
||||
/*if (node.slow) cursor.yellow();
|
||||
else cursor.green();*/
|
||||
cursor
|
||||
//.write(test_json.replay)
|
||||
.reset()
|
||||
.write("\n");
|
||||
console.log('');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
for (var i = 0; i < test_json.suites.length; i++) {
|
||||
traverse_tree(0, test_json.suites[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (test_json.num_fails === 0) {
|
||||
cursor.fg.green();
|
||||
console.log('all tests passed :-)');
|
||||
cursor.reset();
|
||||
}
|
||||
});
|
||||
|
||||
if (program.debug) {
|
||||
provider.on('console', function(line) {
|
||||
// log to stderr
|
||||
console.error(line);
|
||||
});
|
||||
}
|
||||
|
||||
provider.on('error', function(line) {
|
||||
// log to stderr
|
||||
console.error('ERROR: ' + line);
|
||||
});
|
||||
|
||||
/*gprom.finally(function(ph) {
|
||||
ph.exit();
|
||||
// exit with a status code that actually gives information
|
||||
if (program.exitWithFailureCount) process.exit(failure_count);
|
||||
});*/
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
var Browser = require('zombie');
|
||||
var path = require('path');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var Q = require('q');
|
||||
|
||||
var provide_emitter = function(file_paths) {
|
||||
var emitter = new EventEmitter();
|
||||
|
||||
file_paths.reduce(function(prom, file_path, path_ind) {
|
||||
return prom.then(function(browser) {
|
||||
browser.visit('file://'+file_path, function() {
|
||||
if (browser.error) throw new Error(browser.errors);
|
||||
|
||||
var res_json = {};
|
||||
res_json.file_ind = path_ind;
|
||||
|
||||
res_json.num_tests = browser.querySelectorAll('li.test').length;
|
||||
res_json.num_fails = browser.querySelectorAll('li.test.fail').length;
|
||||
res_json.num_passes = browser.querySelectorAll('li.test.pass').length;
|
||||
res_json.num_slow = browser.querySelectorAll('li.test.pass:not(.fast)').length;
|
||||
res_json.num_skipped = browser.querySelectorAll('li.test.pending').length;
|
||||
res_json.duration = browser.text('li.duration em');
|
||||
|
||||
var traverse_node = function(elem) {
|
||||
var classList = elem.className.split(' ');
|
||||
var res;
|
||||
if (classList.indexOf('suite') > -1) {
|
||||
res = {
|
||||
type: 'suite',
|
||||
name: elem.querySelector('h1').textContent,
|
||||
has_subfailures: elem.querySelectorAll('li.test.fail').length > 0
|
||||
};
|
||||
|
||||
var child_elems = elem.querySelector('ul').children;
|
||||
res.children = Array.prototype.map.call(child_elems, traverse_node);
|
||||
return res;
|
||||
}
|
||||
else {
|
||||
var h2_content = elem.querySelector('h2').childNodes;
|
||||
res = {
|
||||
type: 'test',
|
||||
text: h2_content[0].textContent
|
||||
};
|
||||
|
||||
if (classList.indexOf('pass') > -1) {
|
||||
res.pass = true;
|
||||
if (classList.indexOf('pending') > -1) {
|
||||
res.slow = false;
|
||||
res.skipped = true;
|
||||
}
|
||||
else {
|
||||
res.slow = classList.indexOf('fast') < 0;
|
||||
res.skipped = false;
|
||||
res.duration = h2_content[1].textContent;
|
||||
}
|
||||
}
|
||||
else {
|
||||
res.error = elem.querySelector('pre.error').textContent;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
var top_suites = browser.querySelectorAll('#mocha-report > li.suite');
|
||||
res_json.suites = Array.prototype.map.call(top_suites, traverse_node);
|
||||
res_json.replay = browser.querySelector('a.replay').textContent;
|
||||
|
||||
emitter.emit('test_ready', res_json);
|
||||
});
|
||||
|
||||
return new Browser();
|
||||
});
|
||||
}, Q(new Browser()));
|
||||
|
||||
return emitter;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
provide_emitter: provide_emitter,
|
||||
name: 'ZombieJS'
|
||||
};
|
|
@ -1,7 +1,8 @@
|
|||
// requires local modules: base64
|
||||
var assert = chai.assert;
|
||||
var expect = chai.expect;
|
||||
|
||||
import Base64 from '../core/base64.js';
|
||||
|
||||
describe('Base64 Tools', function() {
|
||||
"use strict";
|
||||
|
||||
|
|
|
@ -1,8 +1,15 @@
|
|||
// requires local modules: util, base64, display
|
||||
// requires test modules: assertions
|
||||
/* jshint expr: true */
|
||||
var expect = chai.expect;
|
||||
|
||||
import Base64 from '../core/base64.js';
|
||||
import Display from '../core/display.js';
|
||||
import { _forceCursorURIs, browserSupportsCursorURIs } from '../core/util/browsers.js';
|
||||
|
||||
import './assertions.js';
|
||||
import 'sinon';
|
||||
import sinonChai from '../node_modules/sinon-chai/lib/sinon-chai.js'
|
||||
chai.use(sinonChai);
|
||||
|
||||
describe('Display/Canvas Helper', function () {
|
||||
var checked_data = [
|
||||
0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
|
||||
|
@ -34,31 +41,23 @@ describe('Display/Canvas Helper', function () {
|
|||
}
|
||||
|
||||
describe('checking for cursor uri support', function () {
|
||||
beforeEach(function () {
|
||||
this._old_browser_supports_cursor_uris = Util.browserSupportsCursorURIs;
|
||||
});
|
||||
|
||||
it('should disable cursor URIs if there is no support', function () {
|
||||
Util.browserSupportsCursorURIs = function () { return false; };
|
||||
_forceCursorURIs(false);
|
||||
var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false });
|
||||
expect(display._cursor_uri).to.be.false;
|
||||
});
|
||||
|
||||
it('should enable cursor URIs if there is support', function () {
|
||||
Util.browserSupportsCursorURIs = function () { return true; };
|
||||
_forceCursorURIs(true);
|
||||
var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false });
|
||||
expect(display._cursor_uri).to.be.true;
|
||||
});
|
||||
|
||||
it('respect the cursor_uri option if there is support', function () {
|
||||
Util.browserSupportsCursorURIs = function () { return false; };
|
||||
_forceCursorURIs(false);
|
||||
var display = new Display({ target: document.createElement('canvas'), prefer_js: true, viewport: false, cursor_uri: false });
|
||||
expect(display._cursor_uri).to.be.false;
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
Util.browserSupportsCursorURIs = this._old_browser_supports_cursor_uris;
|
||||
});
|
||||
});
|
||||
|
||||
describe('viewport handling', function () {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
// requires local modules: input/keysym, input/keysymdef, input/util
|
||||
|
||||
var assert = chai.assert;
|
||||
var assert = chai.assert;
|
||||
var expect = chai.expect;
|
||||
|
||||
import keysyms from '../core/input/keysymdef.js';
|
||||
import * as KeyboardUtil from "../core/input/util.js";
|
||||
|
||||
describe('Helpers', function() {
|
||||
"use strict";
|
||||
describe('keysymFromKeyCode', function() {
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
// requires local modules: input/devices, input/util, input/keysymdef, input/keysym
|
||||
var assert = chai.assert;
|
||||
var expect = chai.expect;
|
||||
|
||||
import keysyms from '../core/input/keysymdef.js';
|
||||
import * as KeyboardUtil from '../core/input/util.js';
|
||||
|
||||
/* jshint newcap: false, expr: true */
|
||||
describe('Key Event Pipeline Stages', function() {
|
||||
"use strict";
|
||||
|
|
|
@ -1,9 +1,16 @@
|
|||
// requires local modules: util, websock, rfb, input/util, input/keysym, input/keysymdef, input/devices, inflator, des, display
|
||||
// requires test modules: fake.websocket, assertions
|
||||
/* jshint expr: true */
|
||||
var assert = chai.assert;
|
||||
var expect = chai.expect;
|
||||
|
||||
import RFB from '../core/rfb.js';
|
||||
import Websock from '../core/websock.js';
|
||||
|
||||
import FakeWebSocket from './fake.websocket.js';
|
||||
import './assertions';
|
||||
import 'sinon';
|
||||
import sinonChai from '../node_modules/sinon-chai/lib/sinon-chai.js'
|
||||
chai.use(sinonChai);
|
||||
|
||||
function make_rfb (extra_opts) {
|
||||
if (!extra_opts) {
|
||||
extra_opts = {};
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
// requires local modules: util
|
||||
/* jshint expr: true */
|
||||
|
||||
var assert = chai.assert;
|
||||
var expect = chai.expect;
|
||||
|
||||
import * as Log from '../core/util/logging.js';
|
||||
import l10nGet, { l10n } from '../core/util/localization.js';
|
||||
|
||||
import 'sinon';
|
||||
import sinonChai from '../node_modules/sinon-chai/lib/sinon-chai.js'
|
||||
chai.use(sinonChai);
|
||||
|
||||
describe('Utils', function() {
|
||||
"use strict";
|
||||
|
||||
|
@ -25,34 +31,34 @@ describe('Utils', function() {
|
|||
});
|
||||
|
||||
it('should use noop for levels lower than the min level', function () {
|
||||
Util.init_logging('warn');
|
||||
Util.Debug('hi');
|
||||
Util.Info('hello');
|
||||
Log.init_logging('warn');
|
||||
Log.Debug('hi');
|
||||
Log.Info('hello');
|
||||
expect(console.log).to.not.have.been.called;
|
||||
});
|
||||
|
||||
it('should use console.debug for Debug', function () {
|
||||
Util.init_logging('debug');
|
||||
Util.Debug('dbg');
|
||||
Log.init_logging('debug');
|
||||
Log.Debug('dbg');
|
||||
expect(console.debug).to.have.been.calledWith('dbg');
|
||||
});
|
||||
|
||||
it('should use console.info for Info', function () {
|
||||
Util.init_logging('debug');
|
||||
Util.Info('inf');
|
||||
Log.init_logging('debug');
|
||||
Log.Info('inf');
|
||||
expect(console.info).to.have.been.calledWith('inf');
|
||||
});
|
||||
|
||||
it('should use console.warn for Warn', function () {
|
||||
Util.init_logging('warn');
|
||||
Util.Warn('wrn');
|
||||
Log.init_logging('warn');
|
||||
Log.Warn('wrn');
|
||||
expect(console.warn).to.have.been.called;
|
||||
expect(console.warn).to.have.been.calledWith('wrn');
|
||||
});
|
||||
|
||||
it('should use console.error for Error', function () {
|
||||
Util.init_logging('error');
|
||||
Util.Error('err');
|
||||
Log.init_logging('error');
|
||||
Log.Error('err');
|
||||
expect(console.error).to.have.been.called;
|
||||
expect(console.error).to.have.been.calledWith('err');
|
||||
});
|
||||
|
@ -85,42 +91,42 @@ describe('Utils', function() {
|
|||
});
|
||||
|
||||
it('should use English by default', function() {
|
||||
expect(Util.Localisation.language).to.equal('en');
|
||||
expect(l10n.language).to.equal('en');
|
||||
});
|
||||
it('should use English if no user language matches', function() {
|
||||
window.navigator.languages = ["nl", "de"];
|
||||
Util.Localisation.setup(["es", "fr"]);
|
||||
expect(Util.Localisation.language).to.equal('en');
|
||||
l10n.setup(["es", "fr"]);
|
||||
expect(l10n.language).to.equal('en');
|
||||
});
|
||||
it('should use the most preferred user language', function() {
|
||||
window.navigator.languages = ["nl", "de", "fr"];
|
||||
Util.Localisation.setup(["es", "fr", "de"]);
|
||||
expect(Util.Localisation.language).to.equal('de');
|
||||
l10n.setup(["es", "fr", "de"]);
|
||||
expect(l10n.language).to.equal('de');
|
||||
});
|
||||
it('should prefer sub-languages languages', function() {
|
||||
window.navigator.languages = ["pt-BR"];
|
||||
Util.Localisation.setup(["pt", "pt-BR"]);
|
||||
expect(Util.Localisation.language).to.equal('pt-BR');
|
||||
l10n.setup(["pt", "pt-BR"]);
|
||||
expect(l10n.language).to.equal('pt-BR');
|
||||
});
|
||||
it('should fall back to language "parents"', function() {
|
||||
window.navigator.languages = ["pt-BR"];
|
||||
Util.Localisation.setup(["fr", "pt", "de"]);
|
||||
expect(Util.Localisation.language).to.equal('pt');
|
||||
l10n.setup(["fr", "pt", "de"]);
|
||||
expect(l10n.language).to.equal('pt');
|
||||
});
|
||||
it('should not use specific language when user asks for a generic language', function() {
|
||||
window.navigator.languages = ["pt", "de"];
|
||||
Util.Localisation.setup(["fr", "pt-BR", "de"]);
|
||||
expect(Util.Localisation.language).to.equal('de');
|
||||
l10n.setup(["fr", "pt-BR", "de"]);
|
||||
expect(l10n.language).to.equal('de');
|
||||
});
|
||||
it('should handle underscore as a separator', function() {
|
||||
window.navigator.languages = ["pt-BR"];
|
||||
Util.Localisation.setup(["pt_BR"]);
|
||||
expect(Util.Localisation.language).to.equal('pt_BR');
|
||||
l10n.setup(["pt_BR"]);
|
||||
expect(l10n.language).to.equal('pt_BR');
|
||||
});
|
||||
it('should handle difference in case', function() {
|
||||
window.navigator.languages = ["pt-br"];
|
||||
Util.Localisation.setup(["pt-BR"]);
|
||||
expect(Util.Localisation.language).to.equal('pt-BR');
|
||||
l10n.setup(["pt-BR"]);
|
||||
expect(l10n.language).to.equal('pt-BR');
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,9 +1,15 @@
|
|||
// requires local modules: websock, util
|
||||
// requires test modules: fake.websocket, assertions
|
||||
/* jshint expr: true */
|
||||
var assert = chai.assert;
|
||||
var expect = chai.expect;
|
||||
|
||||
import Websock from '../core/websock.js';
|
||||
import FakeWebSocket from './fake.websocket.js';
|
||||
|
||||
import './assertions';
|
||||
import 'sinon';
|
||||
import sinonChai from '../node_modules/sinon-chai/lib/sinon-chai.js'
|
||||
chai.use(sinonChai);
|
||||
|
||||
describe('Websock', function() {
|
||||
"use strict";
|
||||
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
<html>
|
||||
<head>
|
||||
<title>VNC Playback</title>
|
||||
<script src="/vendor/browser-es-module-loader/dist/browser-es-module-loader.js"></script>
|
||||
<script type="module" src="./playback.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
@ -9,8 +11,7 @@
|
|||
Perftest:<input type='radio' id='mode1' name='mode' checked>
|
||||
Realtime:<input type='radio' id='mode2' name='mode'>
|
||||
|
||||
<input id='startButton' type='button' value='Start' style='width:100px'
|
||||
onclick="start();" disabled>
|
||||
<input id='startButton' type='button' value='Start' style='width:100px' disabled>
|
||||
|
||||
<br><br>
|
||||
|
||||
|
@ -37,98 +38,5 @@
|
|||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
||||
-->
|
||||
|
||||
<script type="text/javascript">
|
||||
var INCLUDE_URI= "../";
|
||||
</script>
|
||||
<script src="../core/util.js"></script>
|
||||
<script src="../app/webutil.js"></script>
|
||||
|
||||
<script>
|
||||
var fname, start_time;
|
||||
|
||||
function message(str) {
|
||||
console.log(str);
|
||||
var cell = document.getElementById('messages');
|
||||
cell.textContent += str + "\n";
|
||||
cell.scrollTop = cell.scrollHeight;
|
||||
}
|
||||
|
||||
fname = WebUtil.getQueryVar('data', null);
|
||||
if (fname) {
|
||||
message("Loading " + fname);
|
||||
// Load supporting scripts
|
||||
WebUtil.load_scripts({
|
||||
'core': ["base64.js", "websock.js", "des.js", "input/keysym.js",
|
||||
"input/keysymdef.js", "input/xtscancodes.js", "input/util.js",
|
||||
"input/devices.js", "display.js", "rfb.js", "inflator.js"],
|
||||
'tests': ["playback.js"],
|
||||
'recordings': [fname]});
|
||||
|
||||
} else {
|
||||
message("Must specify data=FOO in query string.");
|
||||
}
|
||||
|
||||
disconnected = function (rfb, reason) {
|
||||
if (reason) {
|
||||
message("noVNC sent '" + state + "' state during iteration " + iteration + " frame " + frame_idx);
|
||||
test_state = 'failed';
|
||||
}
|
||||
}
|
||||
|
||||
notification = function (rfb, mesg, level, options) {
|
||||
document.getElementById('VNC_status').textContent = mesg;
|
||||
}
|
||||
|
||||
function start() {
|
||||
document.getElementById('startButton').value = "Running";
|
||||
document.getElementById('startButton').disabled = true;
|
||||
|
||||
iterations = document.getElementById('iterations').value;
|
||||
iteration = 0;
|
||||
start_time = (new Date()).getTime();
|
||||
|
||||
if (document.getElementById('mode1').checked) {
|
||||
message("Starting performance playback (fullspeed) [" + iterations + " iteration(s)]");
|
||||
mode = 'perftest';
|
||||
} else {
|
||||
message("Starting realtime playback [" + iterations + " iteration(s)]");
|
||||
mode = 'realtime';
|
||||
}
|
||||
|
||||
//recv_message = rfb.testMode(send_array, VNC_frame_encoding);
|
||||
|
||||
next_iteration();
|
||||
}
|
||||
|
||||
function finish() {
|
||||
// Finished with all iterations
|
||||
var total_time, end_time = (new Date()).getTime();
|
||||
total_time = end_time - start_time;
|
||||
|
||||
iter_time = parseInt(total_time / iterations, 10);
|
||||
message(iterations + " iterations took " + total_time + "ms, " +
|
||||
iter_time + "ms per iteration");
|
||||
// Shut-off event interception
|
||||
rfb.get_mouse().ungrab();
|
||||
rfb.get_keyboard().ungrab();
|
||||
document.getElementById('startButton').disabled = false;
|
||||
document.getElementById('startButton').value = "Start";
|
||||
|
||||
}
|
||||
|
||||
window.onscriptsload = function () {
|
||||
iterations = WebUtil.getQueryVar('iterations', 3);
|
||||
document.getElementById('iterations').value = iterations;
|
||||
mode = WebUtil.getQueryVar('mode', 3);
|
||||
if (mode === 'realtime') {
|
||||
document.getElementById('mode2').checked = true;
|
||||
} else {
|
||||
document.getElementById('mode1').checked = true;
|
||||
}
|
||||
if (fname) {
|
||||
message("VNC_frame_data.length: " + VNC_frame_data.length);
|
||||
}
|
||||
document.getElementById('startButton').disabled = false;
|
||||
}
|
||||
</script>
|
||||
<script type="module" src="./playback-ui.js">
|
||||
</html>
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
var through = require('through2');
|
||||
|
||||
var singleLineRe = /\/\* \[module\] ((.(?!\*\/))+) \*\//g;
|
||||
var multiLineRe = /\/\* \[module\]\n(( * .+\n)+) \*\//g;
|
||||
|
||||
var skipAsModule = /\/\* \[begin skip-as-module\] \*\/(.|\n)+\/\* \[end skip-as-module\] \*\//g;
|
||||
|
||||
module.exports = function (file) {
|
||||
var stream = through(function (buf, enc, next) {
|
||||
var bufStr = buf.toString('utf8');
|
||||
bufStr = bufStr.replace(singleLineRe, "$1");
|
||||
bufStr = bufStr.replace(multiLineRe, function (match, mainLines) {
|
||||
return mainLines.split(" * ").join("");
|
||||
});
|
||||
|
||||
bufStr = bufStr.replace(skipAsModule, "");
|
||||
|
||||
this.push(bufStr);
|
||||
next();
|
||||
});
|
||||
|
||||
stream._is_make_module = true;
|
||||
|
||||
return stream;
|
||||
};
|
|
@ -4,118 +4,170 @@ var path = require('path');
|
|||
var program = require('commander');
|
||||
var fs = require('fs');
|
||||
var fse = require('fs-extra');
|
||||
var browserify = require('browserify');
|
||||
|
||||
var make_modules_transform = require('./make-module-transform');
|
||||
var babelify = require("babelify");
|
||||
|
||||
const SUPPORTED_FORMATS = new Set(['amd', 'commonjs', 'systemjs', 'umd']);
|
||||
|
||||
program
|
||||
.option('-b, --browserify', 'create a browserify bundled app')
|
||||
.option('--as-require', 'output files using "require" instead of ES6 import and export')
|
||||
.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')
|
||||
.parse(process.argv);
|
||||
|
||||
// the various important paths
|
||||
var core_path = path.resolve(__dirname, '..', 'core');
|
||||
var app_path = path.resolve(__dirname, '..', 'app');
|
||||
var out_dir_base = path.resolve(__dirname, '..', 'build');
|
||||
var lib_dir_base = path.resolve(__dirname, '..', 'lib');
|
||||
|
||||
var make_browserify = function (src_files, opts) {
|
||||
// change to the root noVNC directory
|
||||
process.chdir(path.resolve(__dirname, '..'));
|
||||
|
||||
var b = browserify(src_files, opts);
|
||||
|
||||
// register the transforms
|
||||
b.transform(make_modules_transform);
|
||||
b.transform(babelify,
|
||||
{ plugins: ["add-module-exports", "transform-es2015-modules-commonjs"] });
|
||||
|
||||
return b;
|
||||
const paths = {
|
||||
main: path.resolve(__dirname, '..'),
|
||||
core: path.resolve(__dirname, '..', 'core'),
|
||||
app: path.resolve(__dirname, '..', 'app'),
|
||||
vendor: path.resolve(__dirname, '..', 'vendor'),
|
||||
out_dir_base: path.resolve(__dirname, '..', 'build'),
|
||||
lib_dir_base: path.resolve(__dirname, '..', 'lib'),
|
||||
};
|
||||
|
||||
var make_full_app = function () {
|
||||
// make sure the output directory exists
|
||||
fse.ensureDir(out_dir_base);
|
||||
const no_copy_files = 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.vendor, 'promise.js'),
|
||||
]);
|
||||
|
||||
// actually bundle the files into a browserified bundled
|
||||
var ui_file = path.join(app_path, 'ui.js');
|
||||
var b = make_browserify(ui_file, {});
|
||||
var app_file = path.join(out_dir_base, 'app.js');
|
||||
b.bundle().pipe(fs.createWriteStream(app_file));
|
||||
const no_transform_files = new Set([
|
||||
// don't transform this -- we want it imported as-is to properly catch loading errors
|
||||
path.join(paths.app, 'error-handler.js'),
|
||||
]);
|
||||
|
||||
// copy over app-related resources (images, styles, etc)
|
||||
var src_dir_app = path.join(__dirname, '..', 'app');
|
||||
fs.readdir(src_dir_app, function (err, files) {
|
||||
if (err) { throw err; }
|
||||
no_copy_files.forEach((file) => no_transform_files.add(file));
|
||||
|
||||
files.forEach(function (src_file) {
|
||||
var src_file_path = path.resolve(src_dir_app, src_file);
|
||||
var out_file_path = path.resolve(out_dir_base, src_file);
|
||||
var ext = path.extname(src_file);
|
||||
if (ext === '.js' || ext === '.html') return;
|
||||
fse.copy(src_file_path, out_file_path, function (err) {
|
||||
if (err) { throw err; }
|
||||
console.log("Copied file(s) from " + src_file_path + " to " + out_file_path);
|
||||
// walkDir *recursively* walks directories trees,
|
||||
// calling the callback for all normal files found.
|
||||
var walkDir = function (base_path, cb, filter) {
|
||||
fs.readdir(base_path, (err, files) => {
|
||||
if (err) throw err;
|
||||
|
||||
files.map((filename) => path.join(base_path, filename)).forEach((filepath) => {
|
||||
fs.lstat(filepath, (err, stats) => {
|
||||
if (err) throw err;
|
||||
|
||||
if (filter !== undefined && !filter(filepath, stats)) return;
|
||||
|
||||
if (stats.isSymbolicLink()) return;
|
||||
if (stats.isFile()) cb(filepath);
|
||||
if (stats.isDirectory()) walkDir(filepath, cb, filter);
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var transform_html = function (new_script) {
|
||||
// write out the modified vnc.html file that works with the bundle
|
||||
var src_html_path = path.resolve(__dirname, '..', 'vnc.html');
|
||||
var out_html_path = path.resolve(out_dir_base, 'vnc.html');
|
||||
fs.readFile(src_html_path, function (err, contents_raw) {
|
||||
var out_html_path = path.resolve(paths.out_dir_base, 'vnc.html');
|
||||
fs.readFile(src_html_path, (err, contents_raw) => {
|
||||
if (err) { throw err; }
|
||||
|
||||
var contents = contents_raw.toString();
|
||||
contents = contents.replace(/="app\//g, '="');
|
||||
|
||||
var start_marker = '<!-- begin scripts -->\n';
|
||||
var end_marker = '<!-- end scripts -->';
|
||||
var start_ind = contents.indexOf(start_marker) + start_marker.length;
|
||||
var end_ind = contents.indexOf(end_marker, start_ind);
|
||||
|
||||
contents = contents.slice(0, start_ind) + '<script src="app.js"></script>\n' + contents.slice(end_ind);
|
||||
contents = contents.slice(0, start_ind) + `${new_script}\n` + contents.slice(end_ind);
|
||||
|
||||
console.log(`Writing ${out_html_path}`);
|
||||
fs.writeFile(out_html_path, contents, function (err) {
|
||||
if (err) { throw err; }
|
||||
console.log("Wrote " + out_html_path);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
var make_lib_files = function (use_require) {
|
||||
// make sure the output directory exists
|
||||
fse.ensureDir(lib_dir_base);
|
||||
|
||||
var through = require('through2');
|
||||
|
||||
var deps = {};
|
||||
var rfb_file = path.join(core_path, 'rfb.js');
|
||||
var b = make_browserify(rfb_file, {});
|
||||
b.on('transform', function (tr, file) {
|
||||
if (tr._is_make_module) {
|
||||
var new_path = path.join(lib_dir_base, path.relative(core_path, file));
|
||||
fse.ensureDir(path.dirname(new_path));
|
||||
console.log("Writing " + new_path)
|
||||
var fileStream = fs.createWriteStream(new_path);
|
||||
|
||||
if (use_require) {
|
||||
var babelificate = babelify(file,
|
||||
{ plugins: ["add-module-exports", "transform-es2015-modules-commonjs"] });
|
||||
tr.pipe(babelificate);
|
||||
tr = babelificate;
|
||||
}
|
||||
tr.pipe(fileStream);
|
||||
|
||||
var make_lib_files = function (import_format, source_maps, with_app_dir) {
|
||||
if (!import_format) {
|
||||
throw new Error("you must specify an import format to generate compiled noVNC libraries");
|
||||
} else if (!SUPPORTED_FORMATS.has(import_format)) {
|
||||
throw new Error(`unsupported output format "${import_format}" for import/export -- only ${Array.from(SUPPORTED_FORMATS)} are supported`);
|
||||
}
|
||||
|
||||
// NB: we need to make a copy of babel_opts, since babel sets some defaults on it
|
||||
const babel_opts = () => ({
|
||||
plugins: [`transform-es2015-modules-${import_format}`],
|
||||
ast: false,
|
||||
sourceMaps: source_maps,
|
||||
});
|
||||
const babel = require('babel-core');
|
||||
|
||||
b.bundle();
|
||||
};
|
||||
|
||||
if (program.browserify) {
|
||||
make_full_app();
|
||||
var in_path;
|
||||
if (with_app_dir) {
|
||||
var out_path_base = paths.out_dir_base;
|
||||
in_path = paths.main;
|
||||
} else {
|
||||
make_lib_files(program.asRequire);
|
||||
var out_path_base = paths.lib_dir_base;
|
||||
}
|
||||
|
||||
fse.ensureDirSync(out_path_base);
|
||||
|
||||
const helpers = require('./use_require_helpers');
|
||||
const helper = helpers[import_format];
|
||||
|
||||
var handleDir = (js_only, in_path_base, filename) => {
|
||||
if (no_copy_files.has(filename)) return;
|
||||
|
||||
const out_path = path.join(out_path_base, path.relative(in_path_base, filename));
|
||||
if(path.extname(filename) !== '.js') {
|
||||
if (!js_only) {
|
||||
console.log(`Writing ${out_path}`);
|
||||
fse.copy(filename, out_path, (err) => { if (err) throw err; });
|
||||
}
|
||||
return; // skip non-javascript files
|
||||
}
|
||||
|
||||
fse.ensureDir(path.dirname(out_path), () => {
|
||||
if (no_transform_files.has(filename)) {
|
||||
console.log(`Writing ${out_path}`);
|
||||
fse.copy(filename, out_path, (err) => { if (err) throw err; });
|
||||
return;
|
||||
}
|
||||
|
||||
const opts = babel_opts();
|
||||
if (helper && helpers.optionsOverride) {
|
||||
helper.optionsOverride(opts);
|
||||
}
|
||||
|
||||
babel.transformFile(filename, babel_opts(), (err, res) => {
|
||||
console.log(`Writing ${out_path}`);
|
||||
if (err) throw err;
|
||||
var {code, map, ast} = res;
|
||||
if (source_maps === true) {
|
||||
// append URL for external source map
|
||||
code += `\n//# sourceMappingURL=${path.basename(out_path)}.map\n`;
|
||||
}
|
||||
fs.writeFile(out_path, code, (err) => { if (err) throw err; });
|
||||
if (source_maps === true || source_maps === 'both') {
|
||||
console.log(` and ${out_path}.map`);
|
||||
fs.writeFile(`${out_path}.map`, JSON.stringify(map), (err) => { if (err) throw err; });
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (with_app_dir && helper && helper.noCopyOverride) {
|
||||
helper.noCopyOverride(paths, no_copy_files);
|
||||
}
|
||||
|
||||
walkDir(paths.core, handleDir.bind(null, true, in_path || paths.core), (filename, stats) => !no_copy_files.has(filename));
|
||||
walkDir(paths.vendor, handleDir.bind(null, true, in_path || paths.main), (filename, stats) => !no_copy_files.has(filename));
|
||||
|
||||
if (with_app_dir) {
|
||||
walkDir(paths.app, handleDir.bind(null, false, in_path || paths.app), (filename, stats) => !no_copy_files.has(filename));
|
||||
|
||||
const out_app_path = path.join(out_path_base, 'app.js');
|
||||
if (helper && helper.appWriter) {
|
||||
console.log(`Writing ${out_app_path}`);
|
||||
let out_script = helper.appWriter(out_path_base, out_app_path);
|
||||
transform_html(out_script);
|
||||
} else {
|
||||
console.error(`Unable to generate app for the ${import_format} format!`);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
make_lib_files(program.as, program.withSourceMaps, program.withApp);
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
// writes helpers require for vnc.html (they should output app.js)
|
||||
var fs = require('fs');
|
||||
var fse = require('fs-extra');
|
||||
var path = require('path');
|
||||
|
||||
module.exports = {
|
||||
'amd': {
|
||||
appWriter: (base_out_path, out_path) => {
|
||||
// setup for requirejs
|
||||
fs.writeFile(out_path, 'requirejs(["app/ui"], function (ui) {});', (err) => { if (err) throw err; });
|
||||
console.log(`Please place RequireJS in ${path.join(base_out_path, 'require.js')}`);
|
||||
return `<script src="require.js" data-main="${path.relative(base_out_path, out_path)}"></script>`;
|
||||
},
|
||||
noCopyOverride: () => {},
|
||||
},
|
||||
'commonjs': {
|
||||
optionsOverride: (opts) => {
|
||||
// CommonJS supports properly shifting the default export to work as normal
|
||||
opts.plugins.unshift("add-module-exports");
|
||||
},
|
||||
appWriter: (base_out_path, out_path) => {
|
||||
var browserify = require('browserify');
|
||||
var b = browserify(path.join(base_out_path, 'app/ui.js'), {});
|
||||
b.bundle().pipe(fs.createWriteStream(out_path));
|
||||
return `<script src="${path.relative(base_out_path, out_path)}"></script>`;
|
||||
},
|
||||
noCopyOverride: () => {},
|
||||
},
|
||||
'systemjs': {
|
||||
appWriter: (base_out_path, out_path) => {
|
||||
fs.writeFile(out_path, 'SystemJS.import("./app/ui.js");', (err) => { if (err) throw err; });
|
||||
console.log(`Please place SystemJS in ${path.join(base_out_path, 'system-production.js')}`);
|
||||
return `<script src="vendor/promise.js"></script>
|
||||
<script src="system-production.js"></script>\n<script src="${path.relative(base_out_path, out_path)}"></script>`;
|
||||
},
|
||||
noCopyOverride: (paths, no_copy_files) => {
|
||||
no_copy_files.delete(path.join(paths.vendor, 'promise.js'));
|
||||
},
|
||||
},
|
||||
'umd': {
|
||||
optionsOverride: (opts) => {
|
||||
// umd supports properly shifting the default export to work as normal
|
||||
opts.plugins.unshift("add-module-exports");
|
||||
},
|
||||
},
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
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 `rollup -c` in this directory, and then run `browserify
|
||||
src/babel-worker.js > dist/babel-worker.js`.
|
||||
|
||||
LICENSE
|
||||
-------
|
||||
|
||||
MIT
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,15 @@
|
|||
import nodeResolve from 'rollup-plugin-node-resolve';
|
||||
|
||||
export default {
|
||||
entry: 'src/browser-es-module-loader.js',
|
||||
dest: 'dist/browser-es-module-loader.js',
|
||||
format: 'umd',
|
||||
moduleName: 'BrowserESModuleLoader',
|
||||
|
||||
plugins: [
|
||||
nodeResolve(),
|
||||
],
|
||||
|
||||
// skip rollup warnings (specifically the eval warning)
|
||||
onwarn: function() {}
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
/*import { transform as babelTransform } from 'babel-core';
|
||||
import babelTransformDynamicImport from 'babel-plugin-syntax-dynamic-import';
|
||||
import babelTransformES2015ModulesSystemJS from 'babel-plugin-transform-es2015-modules-systemjs';*/
|
||||
|
||||
// sadly, due to how rollup works, we can't use es6 imports here
|
||||
var babelTransform = require('babel-core').transform;
|
||||
var babelTransformDynamicImport = require('babel-plugin-syntax-dynamic-import');
|
||||
var babelTransformES2015ModulesSystemJS = require('babel-plugin-transform-es2015-modules-systemjs');
|
||||
|
||||
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, babelTransformES2015ModulesSystemJS],
|
||||
});
|
||||
|
||||
self.postMessage({key: evt.data.key, code: output.code, source: evt.data.source});
|
||||
};
|
|
@ -0,0 +1,247 @@
|
|||
import RegisterLoader from 'es-module-loader/core/register-loader.js';
|
||||
import { InternalModuleNamespace as ModuleNamespace } from 'es-module-loader/core/loader-polyfill.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) {
|
||||
function ready() {
|
||||
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(function(err) {
|
||||
// dispatch an error event so that we can display in errors in browsers
|
||||
// that don't yet support unhandledrejection
|
||||
try {
|
||||
var evt = new Event('error');
|
||||
} catch (_eventError) {
|
||||
var evt = document.createEvent('Event');
|
||||
evt.initEvent('error', true, true);
|
||||
}
|
||||
evt.message = err.message;
|
||||
evt.error = err;
|
||||
window.dispatchEvent(evt);
|
||||
|
||||
// throw so it still shows up in the console
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
// anonymous modules supported via a custom naming scheme and registry
|
||||
else {
|
||||
var uri = './<anon' + ++anonCnt + '>';
|
||||
if (script.id !== ""){
|
||||
uri = "./" + script.id;
|
||||
}
|
||||
|
||||
var anonName = resolveIfNotPlain(uri, baseURI);
|
||||
anonSources[anonName] = script.innerHTML;
|
||||
loader.import(anonName).catch(function(err) {
|
||||
// dispatch an error event so that we can display in errors in browsers
|
||||
// that don't yet support unhandledrejection
|
||||
try {
|
||||
var evt = new Event('error');
|
||||
} catch (_eventError) {
|
||||
var evt = document.createEvent('Event');
|
||||
evt.initEvent('error', true, true);
|
||||
}
|
||||
evt.message = err.message;
|
||||
evt.error = err;
|
||||
window.dispatchEvent(evt);
|
||||
|
||||
// throw so it still shows up in the console
|
||||
throw err;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// simple DOM ready
|
||||
if (document.readyState === 'complete')
|
||||
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();
|
||||
function load(source) {
|
||||
resolve(xhr.responseText);
|
||||
}
|
||||
function error() {
|
||||
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) {
|
||||
this._workers = new Array(size);
|
||||
this._ind = 0;
|
||||
this._size = size;
|
||||
this._jobs = 0;
|
||||
this.onmessage = undefined;
|
||||
this._stopTimeout = undefined;
|
||||
for (let i = 0; i < size; i++) {
|
||||
let wrkr = new Worker(script);
|
||||
wrkr._count = 0;
|
||||
wrkr._ind = i;
|
||||
wrkr.onmessage = this._onmessage.bind(this, wrkr);
|
||||
this._workers[i] = wrkr;
|
||||
}
|
||||
|
||||
this._checkJobs();
|
||||
};
|
||||
WorkerPool.prototype = {
|
||||
postMessage: function (msg) {
|
||||
if (this._stopTimeout !== undefined) {
|
||||
clearTimeout(this._stopTimeout);
|
||||
this._stopTimeout = undefined;
|
||||
}
|
||||
let 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();
|
||||
},
|
||||
|
||||
_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('/vendor/browser-es-module-loader/dist/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
|
||||
const cacheEntryTrans = localStorage.getItem(key+'!transpiled');
|
||||
if (cacheEntryTrans) {
|
||||
const cacheEntryRaw = localStorage.getItem(key+'!raw');
|
||||
// TODO: store a hash instead
|
||||
if (cacheEntryRaw === source) {
|
||||
return Promise.resolve({key: key, code: cacheEntryTrans, source: 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
|
||||
if (data.key.slice(-8) !== '#nocache') {
|
||||
localStorage.setItem(key+'!raw', data.source);
|
||||
localStorage.setItem(data.key+'!transpiled', data.code);
|
||||
}
|
||||
(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;
|
|
@ -0,0 +1,21 @@
|
|||
(The MIT License)
|
||||
|
||||
Copyright (C) 2014-2016 by Vitaly Puzrin
|
||||
|
||||
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.
|
|
@ -0,0 +1,6 @@
|
|||
This is an ES6-modules-compatible version of
|
||||
https://github.com/nodeca/pako, based on pako version 1.0.3.
|
||||
|
||||
It's more-or-less a direct translation of the original, with unused parts
|
||||
removed, and the dynamic support for non-typed arrays removed (since ES6
|
||||
modules don't work well with dynamic exports).
|
|
@ -0,0 +1,45 @@
|
|||
// reduce buffer size, avoiding mem copy
|
||||
export function shrinkBuf (buf, size) {
|
||||
if (buf.length === size) { return buf; }
|
||||
if (buf.subarray) { return buf.subarray(0, size); }
|
||||
buf.length = size;
|
||||
return buf;
|
||||
};
|
||||
|
||||
|
||||
export function arraySet (dest, src, src_offs, len, dest_offs) {
|
||||
if (src.subarray && dest.subarray) {
|
||||
dest.set(src.subarray(src_offs, src_offs + len), dest_offs);
|
||||
return;
|
||||
}
|
||||
// Fallback to ordinary array
|
||||
for (var i = 0; i < len; i++) {
|
||||
dest[dest_offs + i] = src[src_offs + i];
|
||||
}
|
||||
}
|
||||
|
||||
// Join array of chunks to single array.
|
||||
export function flattenChunks (chunks) {
|
||||
var i, l, len, pos, chunk, result;
|
||||
|
||||
// calculate data length
|
||||
len = 0;
|
||||
for (i = 0, l = chunks.length; i < l; i++) {
|
||||
len += chunks[i].length;
|
||||
}
|
||||
|
||||
// join chunks
|
||||
result = new Uint8Array(len);
|
||||
pos = 0;
|
||||
for (i = 0, l = chunks.length; i < l; i++) {
|
||||
chunk = chunks[i];
|
||||
result.set(chunk, pos);
|
||||
pos += chunk.length;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export const Buf8 = Uint8Array;
|
||||
export const Buf16 = Uint16Array;
|
||||
export const Buf32 = Int32Array;
|
|
@ -0,0 +1,27 @@
|
|||
// Note: adler32 takes 12% for level 0 and 2% for level 6.
|
||||
// It doesn't worth to make additional optimizationa as in original.
|
||||
// Small size is preferable.
|
||||
|
||||
export default function adler32(adler, buf, len, pos) {
|
||||
var s1 = (adler & 0xffff) |0,
|
||||
s2 = ((adler >>> 16) & 0xffff) |0,
|
||||
n = 0;
|
||||
|
||||
while (len !== 0) {
|
||||
// Set limit ~ twice less than 5552, to keep
|
||||
// s2 in 31-bits, because we force signed ints.
|
||||
// in other case %= will fail.
|
||||
n = len > 2000 ? 2000 : len;
|
||||
len -= n;
|
||||
|
||||
do {
|
||||
s1 = (s1 + buf[pos++]) |0;
|
||||
s2 = (s2 + s1) |0;
|
||||
} while (--n);
|
||||
|
||||
s1 %= 65521;
|
||||
s2 %= 65521;
|
||||
}
|
||||
|
||||
return (s1 | (s2 << 16)) |0;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
export default {
|
||||
|
||||
/* Allowed flush values; see deflate() and inflate() below for details */
|
||||
Z_NO_FLUSH: 0,
|
||||
Z_PARTIAL_FLUSH: 1,
|
||||
Z_SYNC_FLUSH: 2,
|
||||
Z_FULL_FLUSH: 3,
|
||||
Z_FINISH: 4,
|
||||
Z_BLOCK: 5,
|
||||
Z_TREES: 6,
|
||||
|
||||
/* Return codes for the compression/decompression functions. Negative values
|
||||
* are errors, positive values are used for special but normal events.
|
||||
*/
|
||||
Z_OK: 0,
|
||||
Z_STREAM_END: 1,
|
||||
Z_NEED_DICT: 2,
|
||||
Z_ERRNO: -1,
|
||||
Z_STREAM_ERROR: -2,
|
||||
Z_DATA_ERROR: -3,
|
||||
//Z_MEM_ERROR: -4,
|
||||
Z_BUF_ERROR: -5,
|
||||
//Z_VERSION_ERROR: -6,
|
||||
|
||||
/* compression levels */
|
||||
Z_NO_COMPRESSION: 0,
|
||||
Z_BEST_SPEED: 1,
|
||||
Z_BEST_COMPRESSION: 9,
|
||||
Z_DEFAULT_COMPRESSION: -1,
|
||||
|
||||
|
||||
Z_FILTERED: 1,
|
||||
Z_HUFFMAN_ONLY: 2,
|
||||
Z_RLE: 3,
|
||||
Z_FIXED: 4,
|
||||
Z_DEFAULT_STRATEGY: 0,
|
||||
|
||||
/* Possible values of the data_type field (though see inflate()) */
|
||||
Z_BINARY: 0,
|
||||
Z_TEXT: 1,
|
||||
//Z_ASCII: 1, // = Z_TEXT (deprecated)
|
||||
Z_UNKNOWN: 2,
|
||||
|
||||
/* The deflate compression method */
|
||||
Z_DEFLATED: 8
|
||||
//Z_NULL: null // Use -1 or null inline, depending on var type
|
||||
};
|
|
@ -0,0 +1,36 @@
|
|||
// Note: we can't get significant speed boost here.
|
||||
// So write code to minimize size - no pregenerated tables
|
||||
// and array tools dependencies.
|
||||
|
||||
|
||||
// Use ordinary array, since untyped makes no boost here
|
||||
export default function makeTable() {
|
||||
var c, table = [];
|
||||
|
||||
for (var n = 0; n < 256; n++) {
|
||||
c = n;
|
||||
for (var k = 0; k < 8; k++) {
|
||||
c = ((c & 1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
|
||||
}
|
||||
table[n] = c;
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
// Create table on load. Just 255 signed longs. Not a problem.
|
||||
var crcTable = makeTable();
|
||||
|
||||
|
||||
function crc32(crc, buf, len, pos) {
|
||||
var t = crcTable,
|
||||
end = pos + len;
|
||||
|
||||
crc ^= -1;
|
||||
|
||||
for (var i = pos; i < end; i++) {
|
||||
crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF];
|
||||
}
|
||||
|
||||
return (crc ^ (-1)); // >>> 0;
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,35 @@
|
|||
export default function GZheader() {
|
||||
/* true if compressed data believed to be text */
|
||||
this.text = 0;
|
||||
/* modification time */
|
||||
this.time = 0;
|
||||
/* extra flags (not used when writing a gzip file) */
|
||||
this.xflags = 0;
|
||||
/* operating system */
|
||||
this.os = 0;
|
||||
/* pointer to extra field or Z_NULL if none */
|
||||
this.extra = null;
|
||||
/* extra field length (valid if extra != Z_NULL) */
|
||||
this.extra_len = 0; // Actually, we don't need it in JS,
|
||||
// but leave for few code modifications
|
||||
|
||||
//
|
||||
// Setup limits is not necessary because in js we should not preallocate memory
|
||||
// for inflate use constant limit in 65536 bytes
|
||||
//
|
||||
|
||||
/* space at extra (only when reading header) */
|
||||
// this.extra_max = 0;
|
||||
/* pointer to zero-terminated file name or Z_NULL */
|
||||
this.name = '';
|
||||
/* space at name (only when reading header) */
|
||||
// this.name_max = 0;
|
||||
/* pointer to zero-terminated comment or Z_NULL */
|
||||
this.comment = '';
|
||||
/* space at comment (only when reading header) */
|
||||
// this.comm_max = 0;
|
||||
/* true if there was or will be a header crc */
|
||||
this.hcrc = 0;
|
||||
/* true when done reading gzip header (not used when writing a gzip file) */
|
||||
this.done = false;
|
||||
}
|
|
@ -0,0 +1,324 @@
|
|||
// See state defs from inflate.js
|
||||
var BAD = 30; /* got a data error -- remain here until reset */
|
||||
var TYPE = 12; /* i: waiting for type bits, including last-flag bit */
|
||||
|
||||
/*
|
||||
Decode literal, length, and distance codes and write out the resulting
|
||||
literal and match bytes until either not enough input or output is
|
||||
available, an end-of-block is encountered, or a data error is encountered.
|
||||
When large enough input and output buffers are supplied to inflate(), for
|
||||
example, a 16K input buffer and a 64K output buffer, more than 95% of the
|
||||
inflate execution time is spent in this routine.
|
||||
|
||||
Entry assumptions:
|
||||
|
||||
state.mode === LEN
|
||||
strm.avail_in >= 6
|
||||
strm.avail_out >= 258
|
||||
start >= strm.avail_out
|
||||
state.bits < 8
|
||||
|
||||
On return, state.mode is one of:
|
||||
|
||||
LEN -- ran out of enough output space or enough available input
|
||||
TYPE -- reached end of block code, inflate() to interpret next block
|
||||
BAD -- error in block data
|
||||
|
||||
Notes:
|
||||
|
||||
- The maximum input bits used by a length/distance pair is 15 bits for the
|
||||
length code, 5 bits for the length extra, 15 bits for the distance code,
|
||||
and 13 bits for the distance extra. This totals 48 bits, or six bytes.
|
||||
Therefore if strm.avail_in >= 6, then there is enough input to avoid
|
||||
checking for available input while decoding.
|
||||
|
||||
- The maximum bytes that a single length/distance pair can output is 258
|
||||
bytes, which is the maximum length that can be coded. inflate_fast()
|
||||
requires strm.avail_out >= 258 for each loop to avoid checking for
|
||||
output space.
|
||||
*/
|
||||
export default function inflate_fast(strm, start) {
|
||||
var state;
|
||||
var _in; /* local strm.input */
|
||||
var last; /* have enough input while in < last */
|
||||
var _out; /* local strm.output */
|
||||
var beg; /* inflate()'s initial strm.output */
|
||||
var end; /* while out < end, enough space available */
|
||||
//#ifdef INFLATE_STRICT
|
||||
var dmax; /* maximum distance from zlib header */
|
||||
//#endif
|
||||
var wsize; /* window size or zero if not using window */
|
||||
var whave; /* valid bytes in the window */
|
||||
var wnext; /* window write index */
|
||||
// Use `s_window` instead `window`, avoid conflict with instrumentation tools
|
||||
var s_window; /* allocated sliding window, if wsize != 0 */
|
||||
var hold; /* local strm.hold */
|
||||
var bits; /* local strm.bits */
|
||||
var lcode; /* local strm.lencode */
|
||||
var dcode; /* local strm.distcode */
|
||||
var lmask; /* mask for first level of length codes */
|
||||
var dmask; /* mask for first level of distance codes */
|
||||
var here; /* retrieved table entry */
|
||||
var op; /* code bits, operation, extra bits, or */
|
||||
/* window position, window bytes to copy */
|
||||
var len; /* match length, unused bytes */
|
||||
var dist; /* match distance */
|
||||
var from; /* where to copy match from */
|
||||
var from_source;
|
||||
|
||||
|
||||
var input, output; // JS specific, because we have no pointers
|
||||
|
||||
/* copy state to local variables */
|
||||
state = strm.state;
|
||||
//here = state.here;
|
||||
_in = strm.next_in;
|
||||
input = strm.input;
|
||||
last = _in + (strm.avail_in - 5);
|
||||
_out = strm.next_out;
|
||||
output = strm.output;
|
||||
beg = _out - (start - strm.avail_out);
|
||||
end = _out + (strm.avail_out - 257);
|
||||
//#ifdef INFLATE_STRICT
|
||||
dmax = state.dmax;
|
||||
//#endif
|
||||
wsize = state.wsize;
|
||||
whave = state.whave;
|
||||
wnext = state.wnext;
|
||||
s_window = state.window;
|
||||
hold = state.hold;
|
||||
bits = state.bits;
|
||||
lcode = state.lencode;
|
||||
dcode = state.distcode;
|
||||
lmask = (1 << state.lenbits) - 1;
|
||||
dmask = (1 << state.distbits) - 1;
|
||||
|
||||
|
||||
/* decode literals and length/distances until end-of-block or not enough
|
||||
input data or output space */
|
||||
|
||||
top:
|
||||
do {
|
||||
if (bits < 15) {
|
||||
hold += input[_in++] << bits;
|
||||
bits += 8;
|
||||
hold += input[_in++] << bits;
|
||||
bits += 8;
|
||||
}
|
||||
|
||||
here = lcode[hold & lmask];
|
||||
|
||||
dolen:
|
||||
for (;;) { // Goto emulation
|
||||
op = here >>> 24/*here.bits*/;
|
||||
hold >>>= op;
|
||||
bits -= op;
|
||||
op = (here >>> 16) & 0xff/*here.op*/;
|
||||
if (op === 0) { /* literal */
|
||||
//Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
|
||||
// "inflate: literal '%c'\n" :
|
||||
// "inflate: literal 0x%02x\n", here.val));
|
||||
output[_out++] = here & 0xffff/*here.val*/;
|
||||
}
|
||||
else if (op & 16) { /* length base */
|
||||
len = here & 0xffff/*here.val*/;
|
||||
op &= 15; /* number of extra bits */
|
||||
if (op) {
|
||||
if (bits < op) {
|
||||
hold += input[_in++] << bits;
|
||||
bits += 8;
|
||||
}
|
||||
len += hold & ((1 << op) - 1);
|
||||
hold >>>= op;
|
||||
bits -= op;
|
||||
}
|
||||
//Tracevv((stderr, "inflate: length %u\n", len));
|
||||
if (bits < 15) {
|
||||
hold += input[_in++] << bits;
|
||||
bits += 8;
|
||||
hold += input[_in++] << bits;
|
||||
bits += 8;
|
||||
}
|
||||
here = dcode[hold & dmask];
|
||||
|
||||
dodist:
|
||||
for (;;) { // goto emulation
|
||||
op = here >>> 24/*here.bits*/;
|
||||
hold >>>= op;
|
||||
bits -= op;
|
||||
op = (here >>> 16) & 0xff/*here.op*/;
|
||||
|
||||
if (op & 16) { /* distance base */
|
||||
dist = here & 0xffff/*here.val*/;
|
||||
op &= 15; /* number of extra bits */
|
||||
if (bits < op) {
|
||||
hold += input[_in++] << bits;
|
||||
bits += 8;
|
||||
if (bits < op) {
|
||||
hold += input[_in++] << bits;
|
||||
bits += 8;
|
||||
}
|
||||
}
|
||||
dist += hold & ((1 << op) - 1);
|
||||
//#ifdef INFLATE_STRICT
|
||||
if (dist > dmax) {
|
||||
strm.msg = 'invalid distance too far back';
|
||||
state.mode = BAD;
|
||||
break top;
|
||||
}
|
||||
//#endif
|
||||
hold >>>= op;
|
||||
bits -= op;
|
||||
//Tracevv((stderr, "inflate: distance %u\n", dist));
|
||||
op = _out - beg; /* max distance in output */
|
||||
if (dist > op) { /* see if copy from window */
|
||||
op = dist - op; /* distance back in window */
|
||||
if (op > whave) {
|
||||
if (state.sane) {
|
||||
strm.msg = 'invalid distance too far back';
|
||||
state.mode = BAD;
|
||||
break top;
|
||||
}
|
||||
|
||||
// (!) This block is disabled in zlib defailts,
|
||||
// don't enable it for binary compatibility
|
||||
//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
|
||||
// if (len <= op - whave) {
|
||||
// do {
|
||||
// output[_out++] = 0;
|
||||
// } while (--len);
|
||||
// continue top;
|
||||
// }
|
||||
// len -= op - whave;
|
||||
// do {
|
||||
// output[_out++] = 0;
|
||||
// } while (--op > whave);
|
||||
// if (op === 0) {
|
||||
// from = _out - dist;
|
||||
// do {
|
||||
// output[_out++] = output[from++];
|
||||
// } while (--len);
|
||||
// continue top;
|
||||
// }
|
||||
//#endif
|
||||
}
|
||||
from = 0; // window index
|
||||
from_source = s_window;
|
||||
if (wnext === 0) { /* very common case */
|
||||
from += wsize - op;
|
||||
if (op < len) { /* some from window */
|
||||
len -= op;
|
||||
do {
|
||||
output[_out++] = s_window[from++];
|
||||
} while (--op);
|
||||
from = _out - dist; /* rest from output */
|
||||
from_source = output;
|
||||
}
|
||||
}
|
||||
else if (wnext < op) { /* wrap around window */
|
||||
from += wsize + wnext - op;
|
||||
op -= wnext;
|
||||
if (op < len) { /* some from end of window */
|
||||
len -= op;
|
||||
do {
|
||||
output[_out++] = s_window[from++];
|
||||
} while (--op);
|
||||
from = 0;
|
||||
if (wnext < len) { /* some from start of window */
|
||||
op = wnext;
|
||||
len -= op;
|
||||
do {
|
||||
output[_out++] = s_window[from++];
|
||||
} while (--op);
|
||||
from = _out - dist; /* rest from output */
|
||||
from_source = output;
|
||||
}
|
||||
}
|
||||
}
|
||||
else { /* contiguous in window */
|
||||
from += wnext - op;
|
||||
if (op < len) { /* some from window */
|
||||
len -= op;
|
||||
do {
|
||||
output[_out++] = s_window[from++];
|
||||
} while (--op);
|
||||
from = _out - dist; /* rest from output */
|
||||
from_source = output;
|
||||
}
|
||||
}
|
||||
while (len > 2) {
|
||||
output[_out++] = from_source[from++];
|
||||
output[_out++] = from_source[from++];
|
||||
output[_out++] = from_source[from++];
|
||||
len -= 3;
|
||||
}
|
||||
if (len) {
|
||||
output[_out++] = from_source[from++];
|
||||
if (len > 1) {
|
||||
output[_out++] = from_source[from++];
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
from = _out - dist; /* copy direct from output */
|
||||
do { /* minimum length is three */
|
||||
output[_out++] = output[from++];
|
||||
output[_out++] = output[from++];
|
||||
output[_out++] = output[from++];
|
||||
len -= 3;
|
||||
} while (len > 2);
|
||||
if (len) {
|
||||
output[_out++] = output[from++];
|
||||
if (len > 1) {
|
||||
output[_out++] = output[from++];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ((op & 64) === 0) { /* 2nd level distance code */
|
||||
here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
|
||||
continue dodist;
|
||||
}
|
||||
else {
|
||||
strm.msg = 'invalid distance code';
|
||||
state.mode = BAD;
|
||||
break top;
|
||||
}
|
||||
|
||||
break; // need to emulate goto via "continue"
|
||||
}
|
||||
}
|
||||
else if ((op & 64) === 0) { /* 2nd level length code */
|
||||
here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
|
||||
continue dolen;
|
||||
}
|
||||
else if (op & 32) { /* end-of-block */
|
||||
//Tracevv((stderr, "inflate: end of block\n"));
|
||||
state.mode = TYPE;
|
||||
break top;
|
||||
}
|
||||
else {
|
||||
strm.msg = 'invalid literal/length code';
|
||||
state.mode = BAD;
|
||||
break top;
|
||||
}
|
||||
|
||||
break; // need to emulate goto via "continue"
|
||||
}
|
||||
} while (_in < last && _out < end);
|
||||
|
||||
/* return unused bytes (on entry, bits < 8, so in won't go too far back) */
|
||||
len = bits >> 3;
|
||||
_in -= len;
|
||||
bits -= len << 3;
|
||||
hold &= (1 << bits) - 1;
|
||||
|
||||
/* update state and return */
|
||||
strm.next_in = _in;
|
||||
strm.next_out = _out;
|
||||
strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last));
|
||||
strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end));
|
||||
state.hold = hold;
|
||||
state.bits = bits;
|
||||
return;
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,322 @@
|
|||
import * as utils from "../utils/common.js";
|
||||
|
||||
var MAXBITS = 15;
|
||||
var ENOUGH_LENS = 852;
|
||||
var ENOUGH_DISTS = 592;
|
||||
//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);
|
||||
|
||||
var CODES = 0;
|
||||
var LENS = 1;
|
||||
var DISTS = 2;
|
||||
|
||||
var lbase = [ /* Length codes 257..285 base */
|
||||
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
|
||||
35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0
|
||||
];
|
||||
|
||||
var lext = [ /* Length codes 257..285 extra */
|
||||
16, 16, 16, 16, 16, 16, 16, 16, 17, 17, 17, 17, 18, 18, 18, 18,
|
||||
19, 19, 19, 19, 20, 20, 20, 20, 21, 21, 21, 21, 16, 72, 78
|
||||
];
|
||||
|
||||
var dbase = [ /* Distance codes 0..29 base */
|
||||
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
|
||||
257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
|
||||
8193, 12289, 16385, 24577, 0, 0
|
||||
];
|
||||
|
||||
var dext = [ /* Distance codes 0..29 extra */
|
||||
16, 16, 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22,
|
||||
23, 23, 24, 24, 25, 25, 26, 26, 27, 27,
|
||||
28, 28, 29, 29, 64, 64
|
||||
];
|
||||
|
||||
export default function inflate_table(type, lens, lens_index, codes, table, table_index, work, opts)
|
||||
{
|
||||
var bits = opts.bits;
|
||||
//here = opts.here; /* table entry for duplication */
|
||||
|
||||
var len = 0; /* a code's length in bits */
|
||||
var sym = 0; /* index of code symbols */
|
||||
var min = 0, max = 0; /* minimum and maximum code lengths */
|
||||
var root = 0; /* number of index bits for root table */
|
||||
var curr = 0; /* number of index bits for current table */
|
||||
var drop = 0; /* code bits to drop for sub-table */
|
||||
var left = 0; /* number of prefix codes available */
|
||||
var used = 0; /* code entries in table used */
|
||||
var huff = 0; /* Huffman code */
|
||||
var incr; /* for incrementing code, index */
|
||||
var fill; /* index for replicating entries */
|
||||
var low; /* low bits for current root entry */
|
||||
var mask; /* mask for low root bits */
|
||||
var next; /* next available space in table */
|
||||
var base = null; /* base value table to use */
|
||||
var base_index = 0;
|
||||
// var shoextra; /* extra bits table to use */
|
||||
var end; /* use base and extra for symbol > end */
|
||||
var count = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1]; /* number of codes of each length */
|
||||
var offs = new utils.Buf16(MAXBITS + 1); //[MAXBITS+1]; /* offsets in table for each length */
|
||||
var extra = null;
|
||||
var extra_index = 0;
|
||||
|
||||
var here_bits, here_op, here_val;
|
||||
|
||||
/*
|
||||
Process a set of code lengths to create a canonical Huffman code. The
|
||||
code lengths are lens[0..codes-1]. Each length corresponds to the
|
||||
symbols 0..codes-1. The Huffman code is generated by first sorting the
|
||||
symbols by length from short to long, and retaining the symbol order
|
||||
for codes with equal lengths. Then the code starts with all zero bits
|
||||
for the first code of the shortest length, and the codes are integer
|
||||
increments for the same length, and zeros are appended as the length
|
||||
increases. For the deflate format, these bits are stored backwards
|
||||
from their more natural integer increment ordering, and so when the
|
||||
decoding tables are built in the large loop below, the integer codes
|
||||
are incremented backwards.
|
||||
|
||||
This routine assumes, but does not check, that all of the entries in
|
||||
lens[] are in the range 0..MAXBITS. The caller must assure this.
|
||||
1..MAXBITS is interpreted as that code length. zero means that that
|
||||
symbol does not occur in this code.
|
||||
|
||||
The codes are sorted by computing a count of codes for each length,
|
||||
creating from that a table of starting indices for each length in the
|
||||
sorted table, and then entering the symbols in order in the sorted
|
||||
table. The sorted table is work[], with that space being provided by
|
||||
the caller.
|
||||
|
||||
The length counts are used for other purposes as well, i.e. finding
|
||||
the minimum and maximum length codes, determining if there are any
|
||||
codes at all, checking for a valid set of lengths, and looking ahead
|
||||
at length counts to determine sub-table sizes when building the
|
||||
decoding tables.
|
||||
*/
|
||||
|
||||
/* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
|
||||
for (len = 0; len <= MAXBITS; len++) {
|
||||
count[len] = 0;
|
||||
}
|
||||
for (sym = 0; sym < codes; sym++) {
|
||||
count[lens[lens_index + sym]]++;
|
||||
}
|
||||
|
||||
/* bound code lengths, force root to be within code lengths */
|
||||
root = bits;
|
||||
for (max = MAXBITS; max >= 1; max--) {
|
||||
if (count[max] !== 0) { break; }
|
||||
}
|
||||
if (root > max) {
|
||||
root = max;
|
||||
}
|
||||
if (max === 0) { /* no symbols to code at all */
|
||||
//table.op[opts.table_index] = 64; //here.op = (var char)64; /* invalid code marker */
|
||||
//table.bits[opts.table_index] = 1; //here.bits = (var char)1;
|
||||
//table.val[opts.table_index++] = 0; //here.val = (var short)0;
|
||||
table[table_index++] = (1 << 24) | (64 << 16) | 0;
|
||||
|
||||
|
||||
//table.op[opts.table_index] = 64;
|
||||
//table.bits[opts.table_index] = 1;
|
||||
//table.val[opts.table_index++] = 0;
|
||||
table[table_index++] = (1 << 24) | (64 << 16) | 0;
|
||||
|
||||
opts.bits = 1;
|
||||
return 0; /* no symbols, but wait for decoding to report error */
|
||||
}
|
||||
for (min = 1; min < max; min++) {
|
||||
if (count[min] !== 0) { break; }
|
||||
}
|
||||
if (root < min) {
|
||||
root = min;
|
||||
}
|
||||
|
||||
/* check for an over-subscribed or incomplete set of lengths */
|
||||
left = 1;
|
||||
for (len = 1; len <= MAXBITS; len++) {
|
||||
left <<= 1;
|
||||
left -= count[len];
|
||||
if (left < 0) {
|
||||
return -1;
|
||||
} /* over-subscribed */
|
||||
}
|
||||
if (left > 0 && (type === CODES || max !== 1)) {
|
||||
return -1; /* incomplete set */
|
||||
}
|
||||
|
||||
/* generate offsets into symbol table for each length for sorting */
|
||||
offs[1] = 0;
|
||||
for (len = 1; len < MAXBITS; len++) {
|
||||
offs[len + 1] = offs[len] + count[len];
|
||||
}
|
||||
|
||||
/* sort symbols by length, by symbol order within each length */
|
||||
for (sym = 0; sym < codes; sym++) {
|
||||
if (lens[lens_index + sym] !== 0) {
|
||||
work[offs[lens[lens_index + sym]]++] = sym;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Create and fill in decoding tables. In this loop, the table being
|
||||
filled is at next and has curr index bits. The code being used is huff
|
||||
with length len. That code is converted to an index by dropping drop
|
||||
bits off of the bottom. For codes where len is less than drop + curr,
|
||||
those top drop + curr - len bits are incremented through all values to
|
||||
fill the table with replicated entries.
|
||||
|
||||
root is the number of index bits for the root table. When len exceeds
|
||||
root, sub-tables are created pointed to by the root entry with an index
|
||||
of the low root bits of huff. This is saved in low to check for when a
|
||||
new sub-table should be started. drop is zero when the root table is
|
||||
being filled, and drop is root when sub-tables are being filled.
|
||||
|
||||
When a new sub-table is needed, it is necessary to look ahead in the
|
||||
code lengths to determine what size sub-table is needed. The length
|
||||
counts are used for this, and so count[] is decremented as codes are
|
||||
entered in the tables.
|
||||
|
||||
used keeps track of how many table entries have been allocated from the
|
||||
provided *table space. It is checked for LENS and DIST tables against
|
||||
the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in
|
||||
the initial root table size constants. See the comments in inftrees.h
|
||||
for more information.
|
||||
|
||||
sym increments through all symbols, and the loop terminates when
|
||||
all codes of length max, i.e. all codes, have been processed. This
|
||||
routine permits incomplete codes, so another loop after this one fills
|
||||
in the rest of the decoding tables with invalid code markers.
|
||||
*/
|
||||
|
||||
/* set up for code type */
|
||||
// poor man optimization - use if-else instead of switch,
|
||||
// to avoid deopts in old v8
|
||||
if (type === CODES) {
|
||||
base = extra = work; /* dummy value--not used */
|
||||
end = 19;
|
||||
|
||||
} else if (type === LENS) {
|
||||
base = lbase;
|
||||
base_index -= 257;
|
||||
extra = lext;
|
||||
extra_index -= 257;
|
||||
end = 256;
|
||||
|
||||
} else { /* DISTS */
|
||||
base = dbase;
|
||||
extra = dext;
|
||||
end = -1;
|
||||
}
|
||||
|
||||
/* initialize opts for loop */
|
||||
huff = 0; /* starting code */
|
||||
sym = 0; /* starting code symbol */
|
||||
len = min; /* starting code length */
|
||||
next = table_index; /* current table to fill in */
|
||||
curr = root; /* current table index bits */
|
||||
drop = 0; /* current bits to drop from code for index */
|
||||
low = -1; /* trigger new sub-table when len > root */
|
||||
used = 1 << root; /* use root table entries */
|
||||
mask = used - 1; /* mask for comparing low */
|
||||
|
||||
/* check available table space */
|
||||
if ((type === LENS && used > ENOUGH_LENS) ||
|
||||
(type === DISTS && used > ENOUGH_DISTS)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* process all codes and make table entries */
|
||||
for (;;) {
|
||||
/* create table entry */
|
||||
here_bits = len - drop;
|
||||
if (work[sym] < end) {
|
||||
here_op = 0;
|
||||
here_val = work[sym];
|
||||
}
|
||||
else if (work[sym] > end) {
|
||||
here_op = extra[extra_index + work[sym]];
|
||||
here_val = base[base_index + work[sym]];
|
||||
}
|
||||
else {
|
||||
here_op = 32 + 64; /* end of block */
|
||||
here_val = 0;
|
||||
}
|
||||
|
||||
/* replicate for those indices with low len bits equal to huff */
|
||||
incr = 1 << (len - drop);
|
||||
fill = 1 << curr;
|
||||
min = fill; /* save offset to next table */
|
||||
do {
|
||||
fill -= incr;
|
||||
table[next + (huff >> drop) + fill] = (here_bits << 24) | (here_op << 16) | here_val |0;
|
||||
} while (fill !== 0);
|
||||
|
||||
/* backwards increment the len-bit code huff */
|
||||
incr = 1 << (len - 1);
|
||||
while (huff & incr) {
|
||||
incr >>= 1;
|
||||
}
|
||||
if (incr !== 0) {
|
||||
huff &= incr - 1;
|
||||
huff += incr;
|
||||
} else {
|
||||
huff = 0;
|
||||
}
|
||||
|
||||
/* go to next symbol, update count, len */
|
||||
sym++;
|
||||
if (--count[len] === 0) {
|
||||
if (len === max) { break; }
|
||||
len = lens[lens_index + work[sym]];
|
||||
}
|
||||
|
||||
/* create new sub-table if needed */
|
||||
if (len > root && (huff & mask) !== low) {
|
||||
/* if first time, transition to sub-tables */
|
||||
if (drop === 0) {
|
||||
drop = root;
|
||||
}
|
||||
|
||||
/* increment past last table */
|
||||
next += min; /* here min is 1 << curr */
|
||||
|
||||
/* determine length of next table */
|
||||
curr = len - drop;
|
||||
left = 1 << curr;
|
||||
while (curr + drop < max) {
|
||||
left -= count[curr + drop];
|
||||
if (left <= 0) { break; }
|
||||
curr++;
|
||||
left <<= 1;
|
||||
}
|
||||
|
||||
/* check for enough space */
|
||||
used += 1 << curr;
|
||||
if ((type === LENS && used > ENOUGH_LENS) ||
|
||||
(type === DISTS && used > ENOUGH_DISTS)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* point entry in root table to sub-table */
|
||||
low = huff & mask;
|
||||
/*table.op[low] = curr;
|
||||
table.bits[low] = root;
|
||||
table.val[low] = next - opts.table_index;*/
|
||||
table[low] = (root << 24) | (curr << 16) | (next - table_index) |0;
|
||||
}
|
||||
}
|
||||
|
||||
/* fill in remaining table entry if code is incomplete (guaranteed to have
|
||||
at most one remaining entry, since if the code is incomplete, the
|
||||
maximum code length that was allowed to get this far is one bit) */
|
||||
if (huff !== 0) {
|
||||
//table.op[next + huff] = 64; /* invalid code marker */
|
||||
//table.bits[next + huff] = len - drop;
|
||||
//table.val[next + huff] = 0;
|
||||
table[next + huff] = ((len - drop) << 24) | (64 << 16) |0;
|
||||
}
|
||||
|
||||
/* set return parameters */
|
||||
//opts.table_index += used;
|
||||
opts.bits = root;
|
||||
return 0;
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
export default {
|
||||
2: 'need dictionary', /* Z_NEED_DICT 2 */
|
||||
1: 'stream end', /* Z_STREAM_END 1 */
|
||||
0: '', /* Z_OK 0 */
|
||||
'-1': 'file error', /* Z_ERRNO (-1) */
|
||||
'-2': 'stream error', /* Z_STREAM_ERROR (-2) */
|
||||
'-3': 'data error', /* Z_DATA_ERROR (-3) */
|
||||
'-4': 'insufficient memory', /* Z_MEM_ERROR (-4) */
|
||||
'-5': 'buffer error', /* Z_BUF_ERROR (-5) */
|
||||
'-6': 'incompatible version' /* Z_VERSION_ERROR (-6) */
|
||||
};
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,24 @@
|
|||
export default function ZStream() {
|
||||
/* next input byte */
|
||||
this.input = null; // JS specific, because we have no pointers
|
||||
this.next_in = 0;
|
||||
/* number of bytes available at input */
|
||||
this.avail_in = 0;
|
||||
/* total number of input bytes read so far */
|
||||
this.total_in = 0;
|
||||
/* next output byte should be put there */
|
||||
this.output = null; // JS specific, because we have no pointers
|
||||
this.next_out = 0;
|
||||
/* remaining free space at output */
|
||||
this.avail_out = 0;
|
||||
/* total number of bytes output so far */
|
||||
this.total_out = 0;
|
||||
/* last error message, NULL if no error */
|
||||
this.msg = ''/*Z_NULL*/;
|
||||
/* not visible by applications */
|
||||
this.state = null;
|
||||
/* best guess about the data type: binary or text */
|
||||
this.data_type = 2/*Z_UNKNOWN*/;
|
||||
/* adler32 value of the uncompressed data */
|
||||
this.adler = 0;
|
||||
}
|
|
@ -0,0 +1,255 @@
|
|||
/* 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);
|
File diff suppressed because it is too large
Load Diff
18
vnc.html
18
vnc.html
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<html class="noVNC_loading">
|
||||
<head>
|
||||
|
||||
<!--
|
||||
|
@ -59,6 +59,15 @@
|
|||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
||||
-->
|
||||
|
||||
<!-- this is included as a normal file in order to catch script-loading errors as well -->
|
||||
<script type="text/javascript" src="app/error-handler.js"></script>
|
||||
|
||||
<!-- begin scripts -->
|
||||
<!-- promise polyfills promises for IE11 -->
|
||||
<script src="vendor/promise.js"></script>
|
||||
<script src="vendor/browser-es-module-loader/dist/browser-es-module-loader.js"></script>
|
||||
<script type="module" src="app/ui.js"></script>
|
||||
<!-- end scripts -->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -320,12 +329,5 @@
|
|||
<source src="app/sounds/bell.oga" type="audio/ogg">
|
||||
<source src="app/sounds/bell.mp3" type="audio/mpeg">
|
||||
</audio>
|
||||
|
||||
<!-- begin scripts -->
|
||||
<script src="core/util.js"></script>
|
||||
<script src="app/webutil.js"></script>
|
||||
<script src="app/ui.js"></script>
|
||||
<!-- end scripts -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
136
vnc_auto.html
136
vnc_auto.html
|
@ -22,18 +22,33 @@
|
|||
Remove this if you use the .htaccess -->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
|
||||
<!-- Icons (see Makefile for what the sizes are for) -->
|
||||
<link rel="icon" sizes="16x16" type="image/png" href="app/images/icons/novnc-16x16.png">
|
||||
<link rel="icon" sizes="24x24" type="image/png" href="app/images/icons/novnc-24x24.png">
|
||||
<link rel="icon" sizes="32x32" type="image/png" href="app/images/icons/novnc-32x32.png">
|
||||
<link rel="icon" sizes="48x48" type="image/png" href="app/images/icons/novnc-48x48.png">
|
||||
<link rel="icon" sizes="60x60" type="image/png" href="app/images/icons/novnc-60x60.png">
|
||||
<link rel="icon" sizes="64x64" type="image/png" href="app/images/icons/novnc-64x64.png">
|
||||
<link rel="icon" sizes="72x72" type="image/png" href="app/images/icons/novnc-72x72.png">
|
||||
<link rel="icon" sizes="76x76" type="image/png" href="app/images/icons/novnc-76x76.png">
|
||||
<link rel="icon" sizes="96x96" type="image/png" href="app/images/icons/novnc-96x96.png">
|
||||
<link rel="icon" sizes="120x120" type="image/png" href="app/images/icons/novnc-120x120.png">
|
||||
<link rel="icon" sizes="144x144" type="image/png" href="app/images/icons/novnc-144x144.png">
|
||||
<link rel="icon" sizes="152x152" type="image/png" href="app/images/icons/novnc-152x152.png">
|
||||
<link rel="icon" sizes="192x192" type="image/png" href="app/images/icons/novnc-192x192.png">
|
||||
<link rel="icon" sizes="any" type="image/svg+xml" href="app/images/icons/novnc-icon.svg">
|
||||
<!-- Repeated last so that legacy handling will pick this -->
|
||||
<link rel="icon" sizes="16x16" type="image/png" href="app/images/icons/novnc-16x16.png">
|
||||
|
||||
<!-- Apple iOS Safari settings -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<!-- App Start Icon -->
|
||||
<link rel="apple-touch-startup-image" href="app/images/screen_320x460.png" />
|
||||
<!-- For iOS devices set the icon to use if user bookmarks app on their homescreen -->
|
||||
<link rel="apple-touch-icon" href="app/images/screen_57x57.png">
|
||||
<!--
|
||||
<link rel="apple-touch-icon-precomposed" href="app/images/screen_57x57.png" />
|
||||
-->
|
||||
|
||||
<!-- Home Screen Icons (favourites and bookmarks use the normal icons) -->
|
||||
<link rel="apple-touch-icon" sizes="60x60" type="image/png" href="app/images/icons/novnc-60x60.png">
|
||||
<link rel="apple-touch-icon" sizes="76x76" type="image/png" href="app/images/icons/novnc-76x76.png">
|
||||
<link rel="apple-touch-icon" sizes="120x120" type="image/png" href="app/images/icons/novnc-120x120.png">
|
||||
<link rel="apple-touch-icon" sizes="152x152" type="image/png" href="app/images/icons/novnc-152x152.png">
|
||||
|
||||
<!-- Stylesheets -->
|
||||
<link rel="stylesheet" href="app/styles/auto.css">
|
||||
|
@ -42,52 +57,19 @@
|
|||
<script type='text/javascript'
|
||||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
||||
-->
|
||||
<script src="core/util.js"></script>
|
||||
<script src="app/webutil.js"></script>
|
||||
</head>
|
||||
|
||||
<body style="margin: 0px;">
|
||||
<div id="noVNC_container">
|
||||
<div id="noVNC_status_bar" class="noVNC_status_bar" style="margin-top: 0px;">
|
||||
<table border=0 width="100%"><tr>
|
||||
<td><div id="noVNC_status" style="position: relative; height: auto;">
|
||||
Loading
|
||||
</div></td>
|
||||
<td width="1%"><div id="noVNC_buttons">
|
||||
<input type=button value="Send CtrlAltDel"
|
||||
id="sendCtrlAltDelButton">
|
||||
<span id="noVNC_xvp_buttons">
|
||||
<input type=button value="Shutdown"
|
||||
id="xvpShutdownButton">
|
||||
<input type=button value="Reboot"
|
||||
id="xvpRebootButton">
|
||||
<input type=button value="Reset"
|
||||
id="xvpResetButton">
|
||||
</span>
|
||||
</div></td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<canvas id="noVNC_canvas" width="640px" height="20px">
|
||||
Canvas not supported.
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
/*jslint white: false */
|
||||
/*global window, $, Util, RFB, */
|
||||
"use strict";
|
||||
|
||||
<!-- promise polyfills promises for IE11 -->
|
||||
<script src="vendor/promise.js"></script>
|
||||
<script src="vendor/browser-es-module-loader/dist/browser-es-module-loader.js"></script>
|
||||
<script type="module">
|
||||
// Load supporting scripts
|
||||
WebUtil.load_scripts({
|
||||
'core': ["base64.js", "websock.js", "des.js", "input/keysymdef.js",
|
||||
"input/xtscancodes.js", "input/util.js", "input/devices.js",
|
||||
"display.js", "inflator.js", "rfb.js", "input/keysym.js"]});
|
||||
import * as WebUtil from './app/webutil.js';
|
||||
import RFB from './core/rfb.js';
|
||||
|
||||
var rfb;
|
||||
var resizeTimeout;
|
||||
var desktopName;
|
||||
|
||||
|
||||
function UIresize() {
|
||||
if (WebUtil.getConfigVar('resize', false)) {
|
||||
var innerW = window.innerWidth;
|
||||
|
@ -109,15 +91,17 @@
|
|||
msg = 'Password Required: ';
|
||||
}
|
||||
var html;
|
||||
html = '<form onsubmit="return setPassword();"';
|
||||
html += ' style="margin-bottom: 0px">';
|
||||
html += '<label></label>'
|
||||
html += '<input type=password size=10 id="password_input" class="noVNC_status">';
|
||||
html += '<\/form>';
|
||||
|
||||
var form = document.createElement('form');
|
||||
form.style = 'margin-bottom: 0px';
|
||||
form.innerHTML = '<label></label>'
|
||||
form.innerHTML += '<input type=password size=10 id="password_input" class="noVNC_status">';
|
||||
form.onsubmit = setPassword;
|
||||
|
||||
// bypass status() because it sets text content
|
||||
document.getElementById('noVNC_status_bar').setAttribute("class", "noVNC_status_warn");
|
||||
document.getElementById('noVNC_status').innerHTML = html;
|
||||
document.getElementById('noVNC_status').innerHTML = '';
|
||||
document.getElementById('noVNC_status').appendChild(form);
|
||||
document.getElementById('noVNC_status').querySelector('label').textContent = msg;
|
||||
}
|
||||
function setPassword() {
|
||||
|
@ -215,9 +199,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
window.onscriptsload = function () {
|
||||
var host, port, password, path, token;
|
||||
|
||||
document.getElementById('sendCtrlAltDelButton').style.display = "inline";
|
||||
document.getElementById('sendCtrlAltDelButton').onclick = sendCtrlAltDel;
|
||||
document.getElementById('xvpShutdownButton').onclick = xvpShutdown;
|
||||
|
@ -227,8 +208,8 @@
|
|||
WebUtil.init_logging(WebUtil.getConfigVar('logging', 'warn'));
|
||||
document.title = unescape(WebUtil.getConfigVar('title', 'noVNC'));
|
||||
// By default, use the host and port of server that served this file
|
||||
host = WebUtil.getConfigVar('host', window.location.hostname);
|
||||
port = WebUtil.getConfigVar('port', window.location.port);
|
||||
var host = WebUtil.getConfigVar('host', window.location.hostname);
|
||||
var port = WebUtil.getConfigVar('port', window.location.port);
|
||||
|
||||
// if port == 80 (or 443) then it won't be present and should be
|
||||
// set manually
|
||||
|
@ -241,23 +222,23 @@
|
|||
}
|
||||
}
|
||||
|
||||
password = WebUtil.getConfigVar('password', '');
|
||||
path = WebUtil.getConfigVar('path', 'websockify');
|
||||
var password = WebUtil.getConfigVar('password', '');
|
||||
var path = WebUtil.getConfigVar('path', 'websockify');
|
||||
|
||||
// If a token variable is passed in, set the parameter in a cookie.
|
||||
// This is used by nova-novncproxy.
|
||||
token = WebUtil.getConfigVar('token', null);
|
||||
var token = WebUtil.getConfigVar('token', null);
|
||||
if (token) {
|
||||
|
||||
// if token is already present in the path we should use it
|
||||
path = WebUtil.injectParamIfMissing(path, "token", token);
|
||||
|
||||
WebUtil.createCookie('token', token, 1)
|
||||
}
|
||||
|
||||
(function() {
|
||||
|
||||
if ((!host) || (!port)) {
|
||||
status('Must specify host and port in URL', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -282,8 +263,35 @@
|
|||
}
|
||||
|
||||
rfb.connect(host, port, password, path);
|
||||
};
|
||||
})();
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body style="margin: 0px;">
|
||||
<div id="noVNC_container">
|
||||
<div id="noVNC_status_bar" class="noVNC_status_bar" style="margin-top: 0px;">
|
||||
<table border=0 width="100%"><tr>
|
||||
<td><div id="noVNC_status" style="position: relative; height: auto;">
|
||||
Loading
|
||||
</div></td>
|
||||
<td width="1%"><div id="noVNC_buttons">
|
||||
<input type=button value="Send CtrlAltDel"
|
||||
id="sendCtrlAltDelButton">
|
||||
<span id="noVNC_xvp_buttons">
|
||||
<input type=button value="Shutdown"
|
||||
id="xvpShutdownButton">
|
||||
<input type=button value="Reboot"
|
||||
id="xvpRebootButton">
|
||||
<input type=button value="Reset"
|
||||
id="xvpResetButton">
|
||||
</span>
|
||||
</div></td>
|
||||
</tr></table>
|
||||
</div>
|
||||
<canvas id="noVNC_canvas" width="640px" height="20px">
|
||||
Canvas not supported.
|
||||
</canvas>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
Loading…
Reference in New Issue