Refactor ES6 module structure/split up Util

This commit restructures many of the ES6 modules, splitting them
up to actual export multiple functions instead of a single object.

It also splits up Util into multiple sub-modules, to make it easier
to maintain.

Finally, localisation is renamed to localization.
This commit is contained in:
Solly Ross 2017-02-04 21:26:00 -05:00
parent 6e744119f8
commit 6d6f0db0da
21 changed files with 5806 additions and 5838 deletions

2
.gitignore vendored
View File

@ -7,3 +7,5 @@ utils/websockify
/build
/lib
recordings
*.swp
*~

View File

@ -5,20 +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/inflator.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

115
app/ui.js
View File

@ -11,20 +11,18 @@
/* jslint white: false, browser: true */
/* global window, document.getElementById, Util, WebUtil, RFB, Display */
import Util from "../core/util.js";
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 WebUtil from "./webutil.js";
import * as WebUtil from "./webutil.js";
var UI;
(function () {
"use strict";
// Fallback for all uncought errors
window.addEventListener('error', function(event) {
// Fallback for all uncought errors
window.addEventListener('error', function(event) {
try {
var msg, div, text;
@ -61,11 +59,9 @@ var UI;
// Don't return true since this would prevent the error
// from being printed to the browser console.
return false;
});
});
var _ = Util.Localisation.get;
UI = {
const UI = {
connected: false,
desktopName: "",
@ -90,6 +86,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) {
@ -106,10 +110,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);
@ -204,7 +208,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);
@ -216,7 +220,6 @@ var UI;
UI.setupSettingLabels();
},
// Adds a link to the label elements on the corresponding input elements
setupSettingLabels: function() {
var labels = document.getElementsByTagName('LABEL');
@ -254,17 +257,17 @@ 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;
}
},
/* ------^-------
* /INIT
* ==============
* EVENT HANDLERS
* ------v------*/
* /INIT
* ==============
* EVENT HANDLERS
* ------v------*/
addResizeHandlers: function() {
window.addEventListener('resize', UI.applyResizeMode);
@ -489,7 +492,7 @@ var UI;
break;
default:
msg = "Invalid UI state";
Util.Error(msg);
Log.Error(msg);
UI.showStatus(msg, 'error');
break;
}
@ -499,11 +502,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');
@ -555,7 +558,7 @@ var UI;
document.getElementById('noVNC_password_dlg')
.classList.remove('noVNC_open');
//Util.Debug("<< updateVisualState");
//Log.Debug("<< updateVisualState");
},
showStatus: function(text, status_type, time) {
@ -669,7 +672,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)) {
@ -766,12 +769,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;
@ -852,7 +855,7 @@ var UI;
val = ctrl.value;
}
WebUtil.writeSetting(name, val);
//Util.Debug("Setting saved '" + name + "=" + val + "'");
//Log.Debug("Setting saved '" + name + "=" + val + "'");
return val;
},
@ -911,10 +914,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');
@ -1027,9 +1030,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() {
@ -1039,9 +1042,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");
},
/* ------^-------
@ -1075,7 +1078,7 @@ var UI;
if ((!host) || (!port)) {
var msg = _("Must set host and port");
Util.Error(msg);
Log.Error(msg);
UI.showStatus(msg, 'error');
return;
}
@ -1165,7 +1168,7 @@ var UI;
if (typeof msg === 'undefined') {
msg = _("Password is required");
}
Util.Warn(msg);
Log.Warn(msg);
UI.showStatus(msg, "warning");
},
@ -1259,7 +1262,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);
@ -1314,7 +1317,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;
}
@ -1342,7 +1345,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');
@ -1401,7 +1404,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) {
@ -1427,7 +1430,7 @@ var UI;
* ------v------*/
showVirtualKeyboard: function() {
if (!Util.isTouchDevice) return;
if (!isTouchDevice) return;
var input = document.getElementById('noVNC_keyboardinput');
@ -1443,7 +1446,7 @@ var UI;
},
hideVirtualKeyboard: function() {
if (!Util.isTouchDevice) return;
if (!isTouchDevice) return;
var input = document.getElementById('noVNC_keyboardinput');
@ -1733,23 +1736,23 @@ var UI;
* /MISC
* ==============
*/
};
};
// Set up translations
var LINGUAS = ["de", "el", "nl", "sv"];
Util.Localisation.setup(LINGUAS);
if (Util.Localisation.language !== "en" && Util.Localisation.dictionary === undefined) {
WebUtil.fetchJSON('app/locale/' + Util.Localisation.language + '.json', function (translations) {
Util.Localisation.dictionary = translations;
// 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;
// wait for translations to load before loading the UI
UI.load();
UI.prime();
}, function (err) {
throw err;
});
} else {
UI.load();
}
})();
} else {
UI.prime();
}
export default UI;

View File

@ -10,31 +10,21 @@
/*jslint bitwise: false, white: false, browser: true, devel: true */
/*global Util, window, document */
import Util from "../core/util.js";
// 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();
};
// 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);
@ -47,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);
@ -61,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;
};
@ -75,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) {
@ -95,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(';');
@ -108,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);
}
@ -137,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);
}
@ -168,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;
@ -212,7 +204,7 @@ WebUtil.injectParamIfMissing = function (path, param, value) {
// 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.
WebUtil.fetchJSON = function (path, resolve, reject) {
export function fetchJSON(path, resolve, reject) {
// NB: IE11 doesn't support JSON as a responseType
const req = new XMLHttpRequest();
req.open('GET', path);
@ -240,6 +232,4 @@ WebUtil.fetchJSON = function (path, resolve, reject) {
};
req.send();
};
export default WebUtil;
}

View File

@ -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 */
export default Base64;

View File

@ -10,10 +10,11 @@
/*jslint browser: true, white: false */
/*global Util, Base64, changeCursor */
import Util from "./util.js";
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";
export default function Display(defaults) {
this._drawCtx = null;
this._c_forceCanvas = false;
@ -31,7 +32,7 @@ export default function Display(defaults) {
this._tile_x = 0;
this._tile_y = 0;
Util.set_defaults(this, defaults, {
set_defaults(this, defaults, {
'true_color': true,
'colourMap': [],
'scale': 1.0,
@ -40,7 +41,7 @@ export default function Display(defaults) {
"onFlush": function () {},
});
Util.Debug(">> Display.constructor");
Log.Debug(">> Display.constructor");
// The visible canvas
if (!this._target) {
@ -68,11 +69,11 @@ export default function Display(defaults) {
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();
@ -84,32 +85,28 @@ export default function Display(defaults) {
}
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 {
var SUPPORTS_IMAGEDATA_CONSTRUCTOR = false;
try {
new ImageData(new Uint8ClampedArray(4), 1, 1);
SUPPORTS_IMAGEDATA_CONSTRUCTOR = true;
} catch (ex) {
} catch (ex) {
// ignore failure
}
}
Display.prototype = {
Display.prototype = {
// Public methods
viewportChangePos: function (deltaX, deltaY) {
var vp = this._viewportLoc;
@ -143,7 +140,7 @@ export default function Display(defaults) {
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;
@ -159,7 +156,7 @@ export default function Display(defaults) {
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;
}
@ -527,7 +524,7 @@ export default function Display(defaults) {
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;
}
@ -740,9 +737,9 @@ export default function Display(defaults) {
this._onFlush();
}
},
};
};
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}
@ -759,10 +756,10 @@ export default function Display(defaults) {
['cursor_uri', 'rw', 'raw'], // Can we render cursor using data URI
['onFlush', 'rw', 'func'], // onFlush(): A flush request has finished
]);
]);
// Class Methods
Display.changeCursor = function (target, pixels, mask, hotx, hoty, w0, h0, cmap) {
// Class Methods
Display.changeCursor = function (target, pixels, mask, hotx, hoty, w0, h0, cmap) {
var w = w0;
var h = h0;
if (h < w) {
@ -867,5 +864,4 @@ export default function Display(defaults) {
var url = 'data:image/x-icon;base64,' + Base64.encode(cur);
target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
};
})();
};

View File

@ -36,4 +36,3 @@ export default function Inflate() {
inflateInit(this.strm, this.windowBits);
};

View File

@ -8,24 +8,21 @@
/*jslint browser: true, white: false */
/*global window, Util */
import Util from "../util.js";
import KeyboardUtil from "./util.js";
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
//
export var Keyboard;
(function () {
"use strict";
//
// 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
});
@ -46,14 +43,14 @@ export var Keyboard;
'keypress': this._handleKeyPress.bind(this),
'blur': this._allKeysUp.bind(this)
};
};
};
Keyboard.prototype = {
Keyboard.prototype = {
// private methods
_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);
}
@ -72,7 +69,7 @@ export var Keyboard;
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
@ -84,7 +81,7 @@ export var Keyboard;
if (this._handler.keypress(e)) {
// Suppress bubbling/default actions
Util.stopEvent(e);
stopEvent(e);
}
},
@ -93,20 +90,20 @@ export var Keyboard;
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);
@ -116,11 +113,11 @@ export var Keyboard;
// 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);
@ -131,33 +128,29 @@ export var Keyboard;
// Release (key up) all keys that are in a down state
this._allKeysUp();
//Util.Debug(">> Keyboard.ungrab");
//Log.Debug(">> Keyboard.ungrab");
},
sync: function (e) {
this._handler.syncModifiers(e);
}
};
};
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
]);
})();
]);
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
@ -170,13 +163,13 @@ export var Mouse;
'mousewheel': this._handleMouseWheel.bind(this),
'mousedisable': this._handleMouseDisable.bind(this)
};
};
};
Mouse.prototype = {
Mouse.prototype = {
// 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
@ -184,7 +177,7 @@ export var Mouse;
},
_releaseMouse: function () {
Util.releaseCapture();
releaseCapture();
this._mouseCaptured = false;
},
@ -243,11 +236,11 @@ export var Mouse;
}
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) {
@ -289,7 +282,7 @@ export var Mouse;
}
}
Util.stopEvent(e);
stopEvent(e);
},
_handleMouseMove: function (e) {
@ -303,7 +296,7 @@ export var Mouse;
if (this._onMouseMove) {
this._onMouseMove(pos.x, pos.y);
}
Util.stopEvent(e);
stopEvent(e);
},
_handleMouseDisable: function (e) {
@ -316,14 +309,13 @@ export var Mouse;
* 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
@ -344,12 +336,11 @@ export var Mouse;
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);
@ -372,7 +363,7 @@ export var Mouse;
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);
@ -388,9 +379,9 @@ export var Mouse;
c.removeEventListener('contextmenu', this._eventHandlers.mousedisable);
}
};
};
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
@ -398,5 +389,6 @@ export var Mouse;
['onMouseButton', 'rw', 'func'], // Handler for mouse button click/release
['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 };

View File

@ -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 */
};
export default KeyTable;

File diff suppressed because one or more lines are too long

View File

@ -1,13 +1,7 @@
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 = {
@ -21,20 +15,20 @@ var KeyboardUtil = {};
var sub = substitutions[cp];
return sub ? sub : cp;
}
}
function isMac() {
function isMac() {
return navigator && !!(/mac/i).exec(navigator.platform);
}
function isWindows() {
}
function isWindows() {
return navigator && !!(/win/i).exec(navigator.platform);
}
function isLinux() {
}
function isLinux() {
return navigator && !!(/linux/i).exec(navigator.platform);
}
}
// Return true if a modifier which is not the specified char modifier (and is not shift) is down
function hasShortcutModifier(charModifier, currentModifiers) {
// Return true if a modifier which is not the specified char modifier (and is not shift) is down
export function hasShortcutModifier(charModifier, currentModifiers) {
var mods = {};
for (var key in currentModifiers) {
if (parseInt(key) !== KeyTable.XK_Shift_L) {
@ -54,10 +48,10 @@ var KeyboardUtil = {};
else {
return sum > 0;
}
}
}
// Return true if the specified char modifier is currently down
function hasCharModifier(charModifier, currentModifiers) {
// Return true if the specified char modifier is currently down
export function hasCharModifier(charModifier, currentModifiers) {
if (charModifier.length === 0) { return false; }
for (var i = 0; i < charModifier.length; ++i) {
@ -66,11 +60,11 @@ var KeyboardUtil = {};
}
}
return true;
}
}
// Helper object tracking modifier key state
// and generates fake key events to compensate if it gets out of sync
function ModifierSync(charModifier) {
// Helper object tracking modifier key state
// and generates fake key events to compensate if it gets out of sync
export function ModifierSync(charModifier) {
if (!charModifier) {
if (isMac()) {
// on Mac, Option (AKA Alt) is used as a char modifier
@ -152,11 +146,11 @@ var KeyboardUtil = {};
// if a char modifier is down, return the keys it consists of, otherwise return null
activeCharModifier: function() { return hasCharModifier(charModifier, state) ? charModifier : null; }
};
}
}
// Get a key ID from a keyboard event
// May be a string or an integer depending on the available properties
function getKey(evt){
// Get a key ID from a keyboard event
// May be a string or an integer depending on the available properties
export function getKey(evt){
if ('keyCode' in evt && 'key' in evt) {
return evt.key + ':' + evt.keyCode;
}
@ -166,11 +160,11 @@ var KeyboardUtil = {};
else {
return evt.key;
}
}
}
// 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){
// 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
export function getKeysym(evt){
var codepoint;
if (evt.char && evt.char.length === 1) {
codepoint = evt.char.charCodeAt();
@ -196,11 +190,11 @@ var KeyboardUtil = {};
return keysyms.lookup(keysymFromKeyCode(evt.which, evt.shiftKey));
}
return null;
}
}
// Given a keycode, try to predict which keysym it might be.
// If the keycode is unknown, null is returned.
function keysymFromKeyCode(keycode, shiftPressed) {
// Given a keycode, try to predict which keysym it might be.
// If the keycode is unknown, null is returned.
export function keysymFromKeyCode(keycode, shiftPressed) {
if (typeof(keycode) !== 'number') {
return null;
}
@ -231,11 +225,11 @@ var KeyboardUtil = {};
}
return nonCharacterKey({keyCode: keycode});
}
}
// 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) {
// if the key is a known non-character key (any key which doesn't generate character data)
// return its keysym value. Otherwise return null
export function nonCharacterKey(evt) {
// evt.key not implemented yet
if (!evt.keyCode) { return null; }
var keycode = evt.keyCode;
@ -275,19 +269,9 @@ var KeyboardUtil = {};
case 93 : return KeyTable.XK_Menu; // also: Windows-Menu, Command on Mac
default: return null;
}
}
}
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) {
@ -333,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;
@ -357,7 +341,7 @@ KeyboardUtil.QEMUKeyEventDecoder = function(modifierState, next) {
};
};
KeyboardUtil.TrackQEMUKeyState = function(next) {
export function TrackQEMUKeyState (next) {
"use strict";
var state = [];
@ -425,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) {
@ -434,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;
}
@ -454,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();
@ -512,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;
@ -569,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 = [];
@ -653,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) {
@ -674,5 +658,3 @@ KeyboardUtil.EscapeModifiers = function(next) {
/* jshint shadow: false */
};
};
export default KeyboardUtil;

View File

@ -1,4 +1,4 @@
var XtScancode = {
export default {
"Escape": 0x0001,
"Digit1": 0x0002,
"Digit2": 0x0003,
@ -147,5 +147,3 @@ var XtScancode = {
"LaunchMail": 0xE06C,
"MediaSelect": 0xE06D,
};
export default XtScancode

View File

@ -10,7 +10,10 @@
* (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
*/
import Util from "./util.js";
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";
@ -143,7 +146,7 @@ export default function RFB(defaults) {
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
@ -172,7 +175,7 @@ export default function RFB(defaults) {
});
// main setup
Util.Debug(">> RFB.constructor");
Log.Debug(">> RFB.constructor");
// populate encHandlers with bound versions
Object.keys(RFB.encodingHandlers).forEach(function (encName) {
@ -192,7 +195,7 @@ export default function RFB(defaults) {
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;
}
@ -210,13 +213,13 @@ export default function RFB(defaults) {
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;
@ -249,22 +252,19 @@ export default function RFB(defaults) {
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 = {
RFB.prototype = {
// Public methods
connect: function (host, port, password, path) {
this._rfb_host = host;
@ -296,7 +296,7 @@ export default function RFB(defaults) {
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);
@ -309,7 +309,7 @@ export default function RFB(defaults) {
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;
},
@ -331,10 +331,10 @@ export default function RFB(defaults) {
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);
}
@ -342,9 +342,7 @@ export default function RFB(defaults) {
},
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);
},
@ -370,7 +368,7 @@ export default function RFB(defaults) {
// Private methods
_connect: function () {
Util.Debug(">> RFB.connect");
Log.Debug(">> RFB.connect");
this._init_vars();
var uri;
@ -381,7 +379,7 @@ export default function RFB(defaults) {
}
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
@ -394,15 +392,15 @@ export default function RFB(defaults) {
}
}
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 () {
@ -428,19 +426,19 @@ export default function RFB(defaults) {
},
_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");
}
},
@ -448,7 +446,7 @@ export default function RFB(defaults) {
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();
@ -466,13 +464,13 @@ export default function RFB(defaults) {
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;
}
@ -480,7 +478,7 @@ export default function RFB(defaults) {
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;
}
@ -488,7 +486,7 @@ export default function RFB(defaults) {
case 'disconnected':
if (oldstate !== 'disconnecting') {
Util.Error("Bad transition to disconnected state, " +
Log.Error("Bad transition to disconnected state, " +
"previous connection state: " + oldstate);
return;
}
@ -496,7 +494,7 @@ export default function RFB(defaults) {
case 'connecting':
if (oldstate !== '') {
Util.Error("Bad transition to connecting state, " +
Log.Error("Bad transition to connecting state, " +
"previous connection state: " + oldstate);
return;
}
@ -504,14 +502,14 @@ export default function RFB(defaults) {
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;
}
@ -521,10 +519,10 @@ export default function RFB(defaults) {
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;
@ -571,16 +569,16 @@ export default function RFB(defaults) {
}
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
@ -605,10 +603,10 @@ export default function RFB(defaults) {
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;
}
@ -621,13 +619,13 @@ export default function RFB(defaults) {
_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) {
@ -658,7 +656,7 @@ export default function RFB(defaults) {
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;
@ -734,7 +732,7 @@ export default function RFB(defaults) {
}
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
@ -775,12 +773,23 @@ export default function RFB(defaults) {
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();
@ -794,18 +803,7 @@ export default function RFB(defaults) {
}
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;
@ -830,7 +828,7 @@ export default function RFB(defaults) {
}
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
},
@ -999,7 +997,7 @@ export default function RFB(defaults) {
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) {
@ -1047,7 +1045,7 @@ export default function RFB(defaults) {
/* 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; }
@ -1075,7 +1073,7 @@ export default function RFB(defaults) {
// 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 +
@ -1087,22 +1085,22 @@ export default function RFB(defaults) {
", 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;
}
@ -1169,7 +1167,7 @@ export default function RFB(defaults) {
},
_handle_set_colour_map_msg: function () {
Util.Debug("SetColorMapEntries");
Log.Debug("SetColorMapEntries");
this._sock.rQskip8(); // Padding
var first_colour = this._sock.rQshift16();
@ -1182,14 +1180,14 @@ export default function RFB(defaults) {
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");
Log.Debug("ServerCutText");
if (this._view_only) { return true; }
if (this._sock.rQwait("ServerCutText header", 7, 1)) { return false; }
@ -1212,7 +1210,7 @@ export default function RFB(defaults) {
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;
}
@ -1254,12 +1252,12 @@ export default function RFB(defaults) {
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:
@ -1293,7 +1291,7 @@ export default function RFB(defaults) {
return this._handle_set_colour_map_msg();
case 2: // Bell
Util.Debug("Bell");
Log.Debug("Bell");
this._onBell(this);
return true;
@ -1307,7 +1305,7 @@ export default function RFB(defaults) {
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
@ -1322,7 +1320,7 @@ export default function RFB(defaults) {
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;
}
},
@ -1347,7 +1345,7 @@ export default function RFB(defaults) {
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
@ -1407,7 +1405,7 @@ export default function RFB(defaults) {
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: " +
@ -1418,7 +1416,7 @@ export default function RFB(defaults) {
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: " +
@ -1447,9 +1445,9 @@ export default function RFB(defaults) {
RFB.messages.enableContinuousUpdates(this._sock, true, 0, 0,
this._fb_width, this._fb_height);
}
};
};
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
@ -1475,9 +1473,9 @@ export default function RFB(defaults) {
['onFBResize', 'rw', 'func'], // onFBResize(rfb, width, height): frame buffer resized
['onDesktopName', 'rw', 'func'], // onDesktopName(rfb, name): desktop name received
['onXvpInit', 'rw', 'func'] // onXvpInit(version): XVP extensions active for this connection
]);
]);
RFB.prototype.set_local_cursor = function (cursor) {
RFB.prototype.set_local_cursor = function (cursor) {
if (!cursor || (cursor in {'0': 1, 'no': 1, 'false': 1})) {
this._local_cursor = false;
this._display.disableLocalCursor(); //Only show server-side cursor
@ -1485,7 +1483,7 @@ export default function RFB(defaults) {
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();
}
}
@ -1495,9 +1493,9 @@ export default function RFB(defaults) {
RFB.messages.clientEncodings(this._sock, this._encodings, cursor,
this._true_color);
}
};
};
RFB.prototype.set_view_only = function (view_only) {
RFB.prototype.set_view_only = function (view_only) {
this._view_only = view_only;
if (this._rfb_connection_state === "connecting" ||
@ -1510,14 +1508,14 @@ export default function RFB(defaults) {
this._mouse.grab();
}
}
};
};
RFB.prototype.get_display = function () { return this._display; };
RFB.prototype.get_keyboard = function () { return this._keyboard; };
RFB.prototype.get_mouse = function () { return this._mouse; };
RFB.prototype.get_display = function () { return this._display; };
RFB.prototype.get_keyboard = function () { return this._keyboard; };
RFB.prototype.get_mouse = function () { return this._mouse; };
// Class Methods
RFB.messages = {
// Class Methods
RFB.messages = {
keyEvent: function (sock, keysym, down) {
var buff = sock._sQ;
var offset = sock._sQlen;
@ -1748,10 +1746,10 @@ export default function RFB(defaults) {
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;
@ -1796,17 +1794,17 @@ export default function RFB(defaults) {
sock._sQlen += 10;
sock.flush();
}
};
};
RFB.genDES = function (password, challenge) {
RFB.genDES = function (password, challenge) {
var passwd = [];
for (var i = 0; i < password.length; i++) {
passwd.push(password.charCodeAt(i));
}
return (new DES(passwd)).encrypt(challenge);
};
};
RFB.encodingHandlers = {
RFB.encodingHandlers = {
RAW: function () {
if (this._FBU.lines === 0) {
this._FBU.lines = this._FBU.height;
@ -1936,7 +1934,7 @@ export default function RFB(defaults) {
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);
}
@ -2048,14 +2046,14 @@ export default function RFB(defaults) {
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;
@ -2431,7 +2429,7 @@ export default function RFB(defaults) {
},
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;
@ -2450,7 +2448,7 @@ export default function RFB(defaults) {
this._FBU.bytes = 0;
this._FBU.rects--;
Util.Debug("<< set_cursor");
Log.Debug("<< set_cursor");
return true;
},
@ -2465,11 +2463,10 @@ export default function RFB(defaults) {
},
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");
}
};
})();
};

View File

@ -1,624 +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',
// Current dictionary of translations
dictionary: undefined,
// 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 Util.Localisation.dictionary !== 'undefined' && Util.Localisation.dictionary[id]) {
return Util.Localisation.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 () {
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++) {
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);
},
};
// 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);
}
};
export default Util;

120
core/util/browsers.js Normal file
View File

@ -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;
}
}

161
core/util/events.js Normal file
View File

@ -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);
}
};

170
core/util/localization.js Normal file
View File

@ -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);

53
core/util/logging.js Normal file
View File

@ -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();

138
core/util/properties.js Normal file
View File

@ -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]]);
}
}
};

15
core/util/strings.js Normal file
View File

@ -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));
};

View File

@ -12,8 +12,7 @@
* read binary data off of the receive queue.
*/
import Util from "./util.js";
import * as Log from './util/logging.js';
/*jslint browser: true, bitwise: true */
/*global Util*/
@ -43,16 +42,14 @@ export default function Websock() {
};
};
(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.
var ENABLE_COPYWITHIN = false;
// 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.
var ENABLE_COPYWITHIN = false;
var MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
var MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
var typedArrayToString = (function () {
var typedArrayToString = (function () {
// This is only for PhantomJS, which doesn't like apply-ing
// with Typed Arrays
try {
@ -65,9 +62,9 @@ export default function Websock() {
null, Array.prototype.slice.call(a));
};
}
})();
})();
Websock.prototype = {
Websock.prototype = {
// Getters and Setters
get_sQ: function () {
return this._sQ;
@ -172,7 +169,7 @@ export default function Websock() {
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) {
@ -222,23 +219,23 @@ export default function Websock() {
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);
},
@ -246,7 +243,7 @@ export default function Websock() {
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();
}
@ -321,7 +318,7 @@ export default function Websock() {
this._expand_compact_rQ();
}
} else {
Util.Debug("Ignoring empty message");
Log.Debug("Ignoring empty message");
}
} catch (exc) {
var exception_str = "";
@ -339,9 +336,9 @@ export default function Websock() {
}
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') {
@ -351,5 +348,4 @@ export default function Websock() {
}
}
}
};
})();
};