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 /build
/lib /lib
recordings 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 Javascript code necessary for full noVNC operation. This includes (but
is not limited to): is not limited to):
core/base64.js core/**/*.js
core/des.js app/*.js
core/display.js test/playback.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
The HTML, CSS, font and images files that included with the noVNC The HTML, CSS, font and images files that included with the noVNC
source distibution (or repository) are not considered part of the 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 */ /* jslint white: false, browser: true */
/* global window, document.getElementById, Util, WebUtil, RFB, Display */ /* 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 KeyTable from "../core/input/keysym.js";
import keysyms from "../core/input/keysymdef.js"; import keysyms from "../core/input/keysymdef.js";
import RFB from "../core/rfb.js"; import RFB from "../core/rfb.js";
import Display from "../core/display.js"; import Display from "../core/display.js";
import WebUtil from "./webutil.js"; import * as WebUtil from "./webutil.js";
var UI; // Fallback for all uncought errors
window.addEventListener('error', function(event) {
(function () {
"use strict";
// Fallback for all uncought errors
window.addEventListener('error', function(event) {
try { try {
var msg, div, text; var msg, div, text;
@ -61,11 +59,9 @@ var UI;
// Don't return true since this would prevent the error // Don't return true since this would prevent the error
// from being printed to the browser console. // from being printed to the browser console.
return false; return false;
}); });
var _ = Util.Localisation.get; const UI = {
UI = {
connected: false, connected: false,
desktopName: "", desktopName: "",
@ -90,6 +86,14 @@ var UI;
reconnect_callback: null, reconnect_callback: null,
reconnect_password: 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 // Setup rfb object, load settings from browser storage, then call
// UI.init to setup the UI/menus // UI.init to setup the UI/menus
load: function(callback) { load: function(callback) {
@ -106,10 +110,10 @@ var UI;
UI.initSettings(); UI.initSettings();
// Translate the DOM // Translate the DOM
Util.Localisation.translateDOM(); l10n.translateDOM();
// Adapt the interface for touch screen devices // Adapt the interface for touch screen devices
if (Util.isTouchDevice) { if (isTouchDevice) {
document.documentElement.classList.add("noVNC_touch"); document.documentElement.classList.add("noVNC_touch");
// Remove the address bar // Remove the address bar
setTimeout(function() { window.scrollTo(0, 1); }, 100); setTimeout(function() { window.scrollTo(0, 1); }, 100);
@ -204,7 +208,7 @@ var UI;
UI.initSetting('port', port); UI.initSetting('port', port);
UI.initSetting('encrypt', (window.location.protocol === "https:")); UI.initSetting('encrypt', (window.location.protocol === "https:"));
UI.initSetting('true_color', true); UI.initSetting('true_color', true);
UI.initSetting('cursor', !Util.isTouchDevice); UI.initSetting('cursor', !isTouchDevice);
UI.initSetting('clip', false); UI.initSetting('clip', false);
UI.initSetting('resize', 'off'); UI.initSetting('resize', 'off');
UI.initSetting('shared', true); UI.initSetting('shared', true);
@ -216,7 +220,6 @@ var UI;
UI.setupSettingLabels(); UI.setupSettingLabels();
}, },
// Adds a link to the label elements on the corresponding input elements // Adds a link to the label elements on the corresponding input elements
setupSettingLabels: function() { setupSettingLabels: function() {
var labels = document.getElementsByTagName('LABEL'); var labels = document.getElementsByTagName('LABEL');
@ -254,17 +257,17 @@ var UI;
return true; return true;
} catch (exc) { } catch (exc) {
var msg = "Unable to create RFB client -- " + exc; var msg = "Unable to create RFB client -- " + exc;
Util.Error(msg); Log.Error(msg);
UI.showStatus(msg, 'error'); UI.showStatus(msg, 'error');
return false; return false;
} }
}, },
/* ------^------- /* ------^-------
* /INIT * /INIT
* ============== * ==============
* EVENT HANDLERS * EVENT HANDLERS
* ------v------*/ * ------v------*/
addResizeHandlers: function() { addResizeHandlers: function() {
window.addEventListener('resize', UI.applyResizeMode); window.addEventListener('resize', UI.applyResizeMode);
@ -489,7 +492,7 @@ var UI;
break; break;
default: default:
msg = "Invalid UI state"; msg = "Invalid UI state";
Util.Error(msg); Log.Error(msg);
UI.showStatus(msg, 'error'); UI.showStatus(msg, 'error');
break; break;
} }
@ -499,11 +502,11 @@ var UI;
// Disable/enable controls depending on connection state // Disable/enable controls depending on connection state
updateVisualState: function() { updateVisualState: function() {
//Util.Debug(">> updateVisualState"); //Log.Debug(">> updateVisualState");
UI.enableDisableViewClip(); UI.enableDisableViewClip();
if (Util.browserSupportsCursorURIs() && !Util.isTouchDevice) { if (cursorURIsSupported() && !isTouchDevice) {
UI.enableSetting('cursor'); UI.enableSetting('cursor');
} else { } else {
UI.disableSetting('cursor'); UI.disableSetting('cursor');
@ -555,7 +558,7 @@ var UI;
document.getElementById('noVNC_password_dlg') document.getElementById('noVNC_password_dlg')
.classList.remove('noVNC_open'); .classList.remove('noVNC_open');
//Util.Debug("<< updateVisualState"); //Log.Debug("<< updateVisualState");
}, },
showStatus: function(text, status_type, time) { showStatus: function(text, status_type, time) {
@ -669,7 +672,7 @@ var UI;
dragControlbarHandle: function (e) { dragControlbarHandle: function (e) {
if (!UI.controlbarGrabbed) return; if (!UI.controlbarGrabbed) return;
var ptr = Util.getPointerEvent(e); var ptr = getPointerEvent(e);
var anchor = document.getElementById('noVNC_control_bar_anchor'); var anchor = document.getElementById('noVNC_control_bar_anchor');
if (ptr.clientX < (window.innerWidth * 0.1)) { if (ptr.clientX < (window.innerWidth * 0.1)) {
@ -766,12 +769,12 @@ var UI;
controlbarHandleMouseDown: function(e) { controlbarHandleMouseDown: function(e) {
if ((e.type == "mousedown") && (e.button != 0)) return; 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 handle = document.getElementById("noVNC_control_bar_handle");
var bounds = handle.getBoundingClientRect(); var bounds = handle.getBoundingClientRect();
Util.setCapture(handle); setCapture(handle);
UI.controlbarGrabbed = true; UI.controlbarGrabbed = true;
UI.controlbarDrag = false; UI.controlbarDrag = false;
@ -852,7 +855,7 @@ var UI;
val = ctrl.value; val = ctrl.value;
} }
WebUtil.writeSetting(name, val); WebUtil.writeSetting(name, val);
//Util.Debug("Setting saved '" + name + "=" + val + "'"); //Log.Debug("Setting saved '" + name + "=" + val + "'");
return val; return val;
}, },
@ -911,10 +914,10 @@ var UI;
// Refresh UI elements from saved cookies // Refresh UI elements from saved cookies
UI.updateSetting('encrypt'); UI.updateSetting('encrypt');
UI.updateSetting('true_color'); UI.updateSetting('true_color');
if (Util.browserSupportsCursorURIs()) { if (cursorURIsSupported()) {
UI.updateSetting('cursor'); UI.updateSetting('cursor');
} else { } else {
UI.updateSetting('cursor', !Util.isTouchDevice); UI.updateSetting('cursor', !isTouchDevice);
UI.disableSetting('cursor'); UI.disableSetting('cursor');
} }
UI.updateSetting('clip'); UI.updateSetting('clip');
@ -1027,9 +1030,9 @@ var UI;
}, },
clipboardReceive: function(rfb, text) { 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; document.getElementById('noVNC_clipboard_text').value = text;
Util.Debug("<< UI.clipboardReceive"); Log.Debug("<< UI.clipboardReceive");
}, },
clipboardClear: function() { clipboardClear: function() {
@ -1039,9 +1042,9 @@ var UI;
clipboardSend: function() { clipboardSend: function() {
var text = document.getElementById('noVNC_clipboard_text').value; 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); UI.rfb.clipboardPasteFrom(text);
Util.Debug("<< UI.clipboardSend"); Log.Debug("<< UI.clipboardSend");
}, },
/* ------^------- /* ------^-------
@ -1075,7 +1078,7 @@ var UI;
if ((!host) || (!port)) { if ((!host) || (!port)) {
var msg = _("Must set host and port"); var msg = _("Must set host and port");
Util.Error(msg); Log.Error(msg);
UI.showStatus(msg, 'error'); UI.showStatus(msg, 'error');
return; return;
} }
@ -1165,7 +1168,7 @@ var UI;
if (typeof msg === 'undefined') { if (typeof msg === 'undefined') {
msg = _("Password is required"); msg = _("Password is required");
} }
Util.Warn(msg); Log.Warn(msg);
UI.showStatus(msg, "warning"); UI.showStatus(msg, "warning");
}, },
@ -1259,7 +1262,7 @@ var UI;
UI.resizeTimeout = setTimeout(function(){ UI.resizeTimeout = setTimeout(function(){
// Request a remote size covering the viewport // Request a remote size covering the viewport
if (UI.rfb.requestDesktopSize(screen.w, screen.h)) { 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); screen.w + 'x' + screen.h);
} }
}, 500); }, 500);
@ -1314,7 +1317,7 @@ var UI;
if (resizeSetting === 'downscale' || resizeSetting === 'scale') { if (resizeSetting === 'downscale' || resizeSetting === 'scale') {
// Disable clipping if we are scaling // Disable clipping if we are scaling
new_clip = false; new_clip = false;
} else if (Util.isTouchDevice) { } else if (isTouchDevice) {
// Touch devices usually have shit scrollbars // Touch devices usually have shit scrollbars
new_clip = true; new_clip = true;
} }
@ -1342,7 +1345,7 @@ var UI;
var resizeSetting = UI.getSetting('resize'); var resizeSetting = UI.getSetting('resize');
// Disable clipping if we are scaling, connected or on touch // Disable clipping if we are scaling, connected or on touch
if (resizeSetting === 'downscale' || resizeSetting === 'scale' || if (resizeSetting === 'downscale' || resizeSetting === 'scale' ||
Util.isTouchDevice) { isTouchDevice) {
UI.disableSetting('clip'); UI.disableSetting('clip');
} else { } else {
UI.enableSetting('clip'); UI.enableSetting('clip');
@ -1401,7 +1404,7 @@ var UI;
// Different behaviour for touch vs non-touch // Different behaviour for touch vs non-touch
// The button is disabled instead of hidden on touch devices // The button is disabled instead of hidden on touch devices
if (Util.isTouchDevice) { if (isTouchDevice) {
viewDragButton.classList.remove("noVNC_hidden"); viewDragButton.classList.remove("noVNC_hidden");
if (clipping) { if (clipping) {
@ -1427,7 +1430,7 @@ var UI;
* ------v------*/ * ------v------*/
showVirtualKeyboard: function() { showVirtualKeyboard: function() {
if (!Util.isTouchDevice) return; if (!isTouchDevice) return;
var input = document.getElementById('noVNC_keyboardinput'); var input = document.getElementById('noVNC_keyboardinput');
@ -1443,7 +1446,7 @@ var UI;
}, },
hideVirtualKeyboard: function() { hideVirtualKeyboard: function() {
if (!Util.isTouchDevice) return; if (!isTouchDevice) return;
var input = document.getElementById('noVNC_keyboardinput'); var input = document.getElementById('noVNC_keyboardinput');
@ -1733,23 +1736,23 @@ var UI;
* /MISC * /MISC
* ============== * ==============
*/ */
}; };
// Set up translations // Set up translations
var LINGUAS = ["de", "el", "nl", "sv"]; var LINGUAS = ["de", "el", "nl", "sv"];
Util.Localisation.setup(LINGUAS); l10n.setup(LINGUAS);
if (Util.Localisation.language !== "en" && Util.Localisation.dictionary === undefined) { if (l10n.language !== "en" && l10n.dictionary === undefined) {
WebUtil.fetchJSON('app/locale/' + Util.Localisation.language + '.json', function (translations) { WebUtil.fetchJSON('app/locale/' + l10n.language + '.json', function (translations) {
Util.Localisation.dictionary = translations; l10n.dictionary = translations;
// wait for translations to load before loading the UI // wait for translations to load before loading the UI
UI.load(); UI.prime();
}, function (err) { }, function (err) {
throw err; throw err;
}); });
} else { } else {
UI.load(); UI.prime();
} }
})();
export default UI; export default UI;

View File

@ -10,31 +10,21 @@
/*jslint bitwise: false, white: false, browser: true, devel: true */ /*jslint bitwise: false, white: false, browser: true, devel: true */
/*global Util, window, document */ /*global Util, window, document */
import Util from "../core/util.js"; import { init_logging as main_init_logging } from '../core/util/logging.js';
// Globals defined here
var WebUtil = {};
/*
* ------------------------------------------------------
* Namespaced in WebUtil
* ------------------------------------------------------
*/
// init log level reading the logging HTTP param // init log level reading the logging HTTP param
WebUtil.init_logging = function (level) { export function init_logging (level) {
"use strict"; "use strict";
if (typeof level !== "undefined") { if (typeof level !== "undefined") {
Util._log_level = level; main_init_logging(level);
} else { } else {
var param = document.location.href.match(/logging=([A-Za-z0-9\._\-]*)/); 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 // Read a query string variable
WebUtil.getQueryVar = function (name, defVal) { export function getQueryVar (name, defVal) {
"use strict"; "use strict";
var re = new RegExp('.*[?&]' + name + '=([^&#]*)'), var re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
match = document.location.href.match(re); match = document.location.href.match(re);
@ -47,7 +37,7 @@ WebUtil.getQueryVar = function (name, defVal) {
}; };
// Read a hash fragment variable // Read a hash fragment variable
WebUtil.getHashVar = function (name, defVal) { export function getHashVar (name, defVal) {
"use strict"; "use strict";
var re = new RegExp('.*[&#]' + name + '=([^&]*)'), var re = new RegExp('.*[&#]' + name + '=([^&]*)'),
match = document.location.hash.match(re); 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 // Read a variable from the fragment or the query string
// Fragment takes precedence // Fragment takes precedence
WebUtil.getConfigVar = function (name, defVal) { export function getConfigVar (name, defVal) {
"use strict"; "use strict";
var val = WebUtil.getHashVar(name); var val = getHashVar(name);
if (val === null) { if (val === null) {
val = WebUtil.getQueryVar(name, defVal); val = getQueryVar(name, defVal);
} }
return val; return val;
}; };
@ -75,7 +65,7 @@ WebUtil.getConfigVar = function (name, defVal) {
*/ */
// No days means only for this browser session // No days means only for this browser session
WebUtil.createCookie = function (name, value, days) { export function createCookie (name, value, days) {
"use strict"; "use strict";
var date, expires; var date, expires;
if (days) { if (days) {
@ -95,7 +85,7 @@ WebUtil.createCookie = function (name, value, days) {
document.cookie = name + "=" + value + expires + "; path=/" + secure; document.cookie = name + "=" + value + expires + "; path=/" + secure;
}; };
WebUtil.readCookie = function (name, defaultValue) { export function readCookie (name, defaultValue) {
"use strict"; "use strict";
var nameEQ = name + "=", var nameEQ = name + "=",
ca = document.cookie.split(';'); ca = document.cookie.split(';');
@ -108,22 +98,24 @@ WebUtil.readCookie = function (name, defaultValue) {
return (typeof defaultValue !== 'undefined') ? defaultValue : null; return (typeof defaultValue !== 'undefined') ? defaultValue : null;
}; };
WebUtil.eraseCookie = function (name) { export function eraseCookie (name) {
"use strict"; "use strict";
WebUtil.createCookie(name, "", -1); createCookie(name, "", -1);
}; };
/* /*
* Setting handling. * Setting handling.
*/ */
WebUtil.initSettings = function (callback /*, ...callbackArgs */) { var settings = {};
export function initSettings (callback /*, ...callbackArgs */) {
"use strict"; "use strict";
var callbackArgs = Array.prototype.slice.call(arguments, 1); var callbackArgs = Array.prototype.slice.call(arguments, 1);
if (window.chrome && window.chrome.storage) { if (window.chrome && window.chrome.storage) {
window.chrome.storage.sync.get(function (cfg) { window.chrome.storage.sync.get(function (cfg) {
WebUtil.settings = cfg; settings = cfg;
console.log(WebUtil.settings); console.log(settings);
if (callback) { if (callback) {
callback.apply(this, callbackArgs); callback.apply(this, callbackArgs);
} }
@ -137,24 +129,24 @@ WebUtil.initSettings = function (callback /*, ...callbackArgs */) {
}; };
// No days means only for this browser session // No days means only for this browser session
WebUtil.writeSetting = function (name, value) { export function writeSetting (name, value) {
"use strict"; "use strict";
if (window.chrome && window.chrome.storage) { if (window.chrome && window.chrome.storage) {
//console.log("writeSetting:", name, value); //console.log("writeSetting:", name, value);
if (WebUtil.settings[name] !== value) { if (settings[name] !== value) {
WebUtil.settings[name] = value; settings[name] = value;
window.chrome.storage.sync.set(WebUtil.settings); window.chrome.storage.sync.set(settings);
} }
} else { } else {
localStorage.setItem(name, value); localStorage.setItem(name, value);
} }
}; };
WebUtil.readSetting = function (name, defaultValue) { export function readSetting (name, defaultValue) {
"use strict"; "use strict";
var value; var value;
if (window.chrome && window.chrome.storage) { if (window.chrome && window.chrome.storage) {
value = WebUtil.settings[name]; value = settings[name];
} else { } else {
value = localStorage.getItem(name); value = localStorage.getItem(name);
} }
@ -168,17 +160,17 @@ WebUtil.readSetting = function (name, defaultValue) {
} }
}; };
WebUtil.eraseSetting = function (name) { export function eraseSetting (name) {
"use strict"; "use strict";
if (window.chrome && window.chrome.storage) { if (window.chrome && window.chrome.storage) {
window.chrome.storage.sync.remove(name); window.chrome.storage.sync.remove(name);
delete WebUtil.settings[name]; delete settings[name];
} else { } else {
localStorage.removeItem(name); 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 // force pretend that we're dealing with a relative path
// (assume that we wanted an extra if we pass one in) // (assume that we wanted an extra if we pass one in)
path = "/" + path; path = "/" + path;
@ -212,7 +204,7 @@ WebUtil.injectParamIfMissing = function (path, param, value) {
// IE11 support or polyfill promises and fetch in IE11. // IE11 support or polyfill promises and fetch in IE11.
// resolve will receive an object on success, while reject // resolve will receive an object on success, while reject
// will receive either an event or an error on failure. // 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 // NB: IE11 doesn't support JSON as a responseType
const req = new XMLHttpRequest(); const req = new XMLHttpRequest();
req.open('GET', path); req.open('GET', path);
@ -240,6 +232,4 @@ WebUtil.fetchJSON = function (path, resolve, reject) {
}; };
req.send(); req.send();
}; }
export default WebUtil;

View File

@ -7,7 +7,7 @@
/*jslint white: false */ /*jslint white: false */
/*global console */ /*global console */
var Base64 = { export default {
/* Convert data (an array of integers) to a Base64 string. */ /* Convert data (an array of integers) to a Base64 string. */
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''), toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
base64Pad : '=', base64Pad : '=',
@ -15,7 +15,7 @@ var Base64 = {
encode: function (data) { encode: function (data) {
"use strict"; "use strict";
var result = ''; var result = '';
var toBase64Table = Base64.toBase64Table; var toBase64Table = this.toBase64Table;
var length = data.length; var length = data.length;
var lengthpad = (length % 3); var lengthpad = (length % 3);
// Convert every three bytes to 4 ascii characters. // Convert every three bytes to 4 ascii characters.
@ -63,8 +63,8 @@ var Base64 = {
decode: function (data, offset) { decode: function (data, offset) {
"use strict"; "use strict";
offset = typeof(offset) !== 'undefined' ? offset : 0; offset = typeof(offset) !== 'undefined' ? offset : 0;
var toBinaryTable = Base64.toBinaryTable; var toBinaryTable = this.toBinaryTable;
var base64Pad = Base64.base64Pad; var base64Pad = this.base64Pad;
var result, result_length; var result, result_length;
var leftbits = 0; // number of bits decoded, but yet to be appended var leftbits = 0; // number of bits decoded, but yet to be appended
var leftdata = 0; // 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; return result;
} }
}; /* End of Base64 namespace */ }; /* End of Base64 namespace */
export default Base64;

View File

@ -10,10 +10,11 @@
/*jslint browser: true, white: false */ /*jslint browser: true, white: false */
/*global Util, Base64, changeCursor */ /*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"; import Base64 from "./base64.js";
export default function Display(defaults) { export default function Display(defaults) {
this._drawCtx = null; this._drawCtx = null;
this._c_forceCanvas = false; this._c_forceCanvas = false;
@ -31,7 +32,7 @@ export default function Display(defaults) {
this._tile_x = 0; this._tile_x = 0;
this._tile_y = 0; this._tile_y = 0;
Util.set_defaults(this, defaults, { set_defaults(this, defaults, {
'true_color': true, 'true_color': true,
'colourMap': [], 'colourMap': [],
'scale': 1.0, 'scale': 1.0,
@ -40,7 +41,7 @@ export default function Display(defaults) {
"onFlush": function () {}, "onFlush": function () {},
}); });
Util.Debug(">> Display.constructor"); Log.Debug(">> Display.constructor");
// The visible canvas // The visible canvas
if (!this._target) { if (!this._target) {
@ -68,11 +69,11 @@ export default function Display(defaults) {
right: this._backbuffer.width, right: this._backbuffer.width,
bottom: this._backbuffer.height }; bottom: this._backbuffer.height };
Util.Debug("User Agent: " + navigator.userAgent); Log.Debug("User Agent: " + navigator.userAgent);
if (Util.Engine.gecko) { Util.Debug("Browser: gecko " + Util.Engine.gecko); } if (Engine.gecko) { Log.Debug("Browser: gecko " + Engine.gecko); }
if (Util.Engine.webkit) { Util.Debug("Browser: webkit " + Util.Engine.webkit); } if (Engine.webkit) { Log.Debug("Browser: webkit " + Engine.webkit); }
if (Util.Engine.trident) { Util.Debug("Browser: trident " + Util.Engine.trident); } if (Engine.trident) { Log.Debug("Browser: trident " + Engine.trident); }
if (Util.Engine.presto) { Util.Debug("Browser: presto " + Util.Engine.presto); } if (Engine.presto) { Log.Debug("Browser: presto " + Engine.presto); }
this.clear(); this.clear();
@ -84,32 +85,28 @@ export default function Display(defaults) {
} }
if (this._prefer_js === null) { if (this._prefer_js === null) {
Util.Info("Prefering javascript operations"); Log.Info("Prefering javascript operations");
this._prefer_js = true; this._prefer_js = true;
} }
// Determine browser support for setting the cursor via data URI scheme // Determine browser support for setting the cursor via data URI scheme
if (this._cursor_uri || this._cursor_uri === null || if (this._cursor_uri || this._cursor_uri === null ||
this._cursor_uri === undefined) { this._cursor_uri === undefined) {
this._cursor_uri = Util.browserSupportsCursorURIs(); this._cursor_uri = cursorURIsSupported();
} }
Util.Debug("<< Display.constructor"); Log.Debug("<< Display.constructor");
}; };
(function () { var SUPPORTS_IMAGEDATA_CONSTRUCTOR = false;
"use strict"; try {
var SUPPORTS_IMAGEDATA_CONSTRUCTOR = false;
try {
new ImageData(new Uint8ClampedArray(4), 1, 1); new ImageData(new Uint8ClampedArray(4), 1, 1);
SUPPORTS_IMAGEDATA_CONSTRUCTOR = true; SUPPORTS_IMAGEDATA_CONSTRUCTOR = true;
} catch (ex) { } catch (ex) {
// ignore failure // ignore failure
} }
Display.prototype = {
Display.prototype = {
// Public methods // Public methods
viewportChangePos: function (deltaX, deltaY) { viewportChangePos: function (deltaX, deltaY) {
var vp = this._viewportLoc; var vp = this._viewportLoc;
@ -143,7 +140,7 @@ export default function Display(defaults) {
if (deltaX === 0 && deltaY === 0) { if (deltaX === 0 && deltaY === 0) {
return; return;
} }
Util.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY); Log.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY);
vp.x += deltaX; vp.x += deltaX;
vp.y += deltaY; vp.y += deltaY;
@ -159,7 +156,7 @@ export default function Display(defaults) {
typeof(width) === "undefined" || typeof(width) === "undefined" ||
typeof(height) === "undefined") { typeof(height) === "undefined") {
Util.Debug("Setting viewport to full display region"); Log.Debug("Setting viewport to full display region");
width = this._fb_width; width = this._fb_width;
height = this._fb_height; height = this._fb_height;
} }
@ -527,7 +524,7 @@ export default function Display(defaults) {
changeCursor: function (pixels, mask, hotx, hoty, w, h) { changeCursor: function (pixels, mask, hotx, hoty, w, h) {
if (this._cursor_uri === false) { 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; return;
} }
@ -740,9 +737,9 @@ export default function Display(defaults) {
this._onFlush(); this._onFlush();
} }
}, },
}; };
Util.make_properties(Display, [ make_properties(Display, [
['target', 'wo', 'dom'], // Canvas element for rendering ['target', 'wo', 'dom'], // Canvas element for rendering
['context', 'ro', 'raw'], // Canvas 2D context for rendering (read-only) ['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} ['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 ['cursor_uri', 'rw', 'raw'], // Can we render cursor using data URI
['onFlush', 'rw', 'func'], // onFlush(): A flush request has finished ['onFlush', 'rw', 'func'], // onFlush(): A flush request has finished
]); ]);
// Class Methods // Class Methods
Display.changeCursor = function (target, pixels, mask, hotx, hoty, w0, h0, cmap) { Display.changeCursor = function (target, pixels, mask, hotx, hoty, w0, h0, cmap) {
var w = w0; var w = w0;
var h = h0; var h = h0;
if (h < w) { if (h < w) {
@ -867,5 +864,4 @@ export default function Display(defaults) {
var url = 'data:image/x-icon;base64,' + Base64.encode(cur); var url = 'data:image/x-icon;base64,' + Base64.encode(cur);
target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default'; target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
}; };
})();

View File

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

View File

@ -8,24 +8,21 @@
/*jslint browser: true, white: false */ /*jslint browser: true, white: false */
/*global window, Util */ /*global window, Util */
import Util from "../util.js"; import * as Log from '../util/logging.js';
import KeyboardUtil from "./util.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; const Keyboard = function (defaults) {
(function () {
"use strict";
//
// Keyboard event handler
//
Keyboard = function (defaults) {
this._keyDownList = []; // List of depressed keys this._keyDownList = []; // List of depressed keys
// (even if they are happy) // (even if they are happy)
Util.set_defaults(this, defaults, { set_defaults(this, defaults, {
'target': document, 'target': document,
'focused': true 'focused': true
}); });
@ -46,14 +43,14 @@ export var Keyboard;
'keypress': this._handleKeyPress.bind(this), 'keypress': this._handleKeyPress.bind(this),
'blur': this._allKeysUp.bind(this) 'blur': this._allKeysUp.bind(this)
}; };
}; };
Keyboard.prototype = { Keyboard.prototype = {
// private methods // private methods
_handleRfbEvent: function (e) { _handleRfbEvent: function (e) {
if (this._onKeyPress) { 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 + ")"); ", keysym: " + e.keysym.keysym + "(" + e.keysym.keyname + ")");
this._onKeyPress(e); this._onKeyPress(e);
} }
@ -72,7 +69,7 @@ export var Keyboard;
if (this._handler.keydown(e)) { if (this._handler.keydown(e)) {
// Suppress bubbling/default actions // Suppress bubbling/default actions
Util.stopEvent(e); stopEvent(e);
} else { } else {
// Allow the event to bubble and become a keyPress event which // Allow the event to bubble and become a keyPress event which
// will have the character code translated // will have the character code translated
@ -84,7 +81,7 @@ export var Keyboard;
if (this._handler.keypress(e)) { if (this._handler.keypress(e)) {
// Suppress bubbling/default actions // Suppress bubbling/default actions
Util.stopEvent(e); stopEvent(e);
} }
}, },
@ -93,20 +90,20 @@ export var Keyboard;
if (this._handler.keyup(e)) { if (this._handler.keyup(e)) {
// Suppress bubbling/default actions // Suppress bubbling/default actions
Util.stopEvent(e); stopEvent(e);
} }
}, },
_allKeysUp: function () { _allKeysUp: function () {
Util.Debug(">> Keyboard.allKeysUp"); Log.Debug(">> Keyboard.allKeysUp");
this._handler.releaseAll(); this._handler.releaseAll();
Util.Debug("<< Keyboard.allKeysUp"); Log.Debug("<< Keyboard.allKeysUp");
}, },
// Public methods // Public methods
grab: function () { grab: function () {
//Util.Debug(">> Keyboard.grab"); //Log.Debug(">> Keyboard.grab");
var c = this._target; var c = this._target;
c.addEventListener('keydown', this._eventHandlers.keydown); c.addEventListener('keydown', this._eventHandlers.keydown);
@ -116,11 +113,11 @@ export var Keyboard;
// Release (key up) if window loses focus // Release (key up) if window loses focus
window.addEventListener('blur', this._eventHandlers.blur); window.addEventListener('blur', this._eventHandlers.blur);
//Util.Debug("<< Keyboard.grab"); //Log.Debug("<< Keyboard.grab");
}, },
ungrab: function () { ungrab: function () {
//Util.Debug(">> Keyboard.ungrab"); //Log.Debug(">> Keyboard.ungrab");
var c = this._target; var c = this._target;
c.removeEventListener('keydown', this._eventHandlers.keydown); c.removeEventListener('keydown', this._eventHandlers.keydown);
@ -131,33 +128,29 @@ export var Keyboard;
// Release (key up) all keys that are in a down state // Release (key up) all keys that are in a down state
this._allKeysUp(); this._allKeysUp();
//Util.Debug(">> Keyboard.ungrab"); //Log.Debug(">> Keyboard.ungrab");
}, },
sync: function (e) { sync: function (e) {
this._handler.syncModifiers(e); this._handler.syncModifiers(e);
} }
}; };
Util.make_properties(Keyboard, [ make_properties(Keyboard, [
['target', 'wo', 'dom'], // DOM element that captures keyboard input ['target', 'wo', 'dom'], // DOM element that captures keyboard input
['focused', 'rw', 'bool'], // Capture and send key events ['focused', 'rw', 'bool'], // Capture and send key events
['onKeyPress', 'rw', 'func'] // Handler for key press/release ['onKeyPress', 'rw', 'func'] // Handler for key press/release
]); ]);
})();
export var Mouse; const Mouse = function (defaults) {
(function () {
Mouse = function (defaults) {
this._mouseCaptured = false; this._mouseCaptured = false;
this._doubleClickTimer = null; this._doubleClickTimer = null;
this._lastTouchPos = null; this._lastTouchPos = null;
// Configuration attributes // Configuration attributes
Util.set_defaults(this, defaults, { set_defaults(this, defaults, {
'target': document, 'target': document,
'focused': true, 'focused': true,
'touchButton': 1 'touchButton': 1
@ -170,13 +163,13 @@ export var Mouse;
'mousewheel': this._handleMouseWheel.bind(this), 'mousewheel': this._handleMouseWheel.bind(this),
'mousedisable': this._handleMouseDisable.bind(this) 'mousedisable': this._handleMouseDisable.bind(this)
}; };
}; };
Mouse.prototype = { Mouse.prototype = {
// private methods // private methods
_captureMouse: function () { _captureMouse: function () {
// capturing the mouse ensures we get the mouseup event // capturing the mouse ensures we get the mouseup event
Util.setCapture(this._target); setCapture(this._target);
// some browsers give us mouseup events regardless, // some browsers give us mouseup events regardless,
// so if we never captured the mouse, we can disregard the event // so if we never captured the mouse, we can disregard the event
@ -184,7 +177,7 @@ export var Mouse;
}, },
_releaseMouse: function () { _releaseMouse: function () {
Util.releaseCapture(); releaseCapture();
this._mouseCaptured = false; this._mouseCaptured = false;
}, },
@ -243,11 +236,11 @@ export var Mouse;
} }
if (this._onMouseButton) { if (this._onMouseButton) {
Util.Debug("onMouseButton " + (down ? "down" : "up") + Log.Debug("onMouseButton " + (down ? "down" : "up") +
", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask); ", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
this._onMouseButton(pos.x, pos.y, down, bmask); this._onMouseButton(pos.x, pos.y, down, bmask);
} }
Util.stopEvent(e); stopEvent(e);
}, },
_handleMouseDown: function (e) { _handleMouseDown: function (e) {
@ -289,7 +282,7 @@ export var Mouse;
} }
} }
Util.stopEvent(e); stopEvent(e);
}, },
_handleMouseMove: function (e) { _handleMouseMove: function (e) {
@ -303,7 +296,7 @@ export var Mouse;
if (this._onMouseMove) { if (this._onMouseMove) {
this._onMouseMove(pos.x, pos.y); this._onMouseMove(pos.x, pos.y);
} }
Util.stopEvent(e); stopEvent(e);
}, },
_handleMouseDisable: function (e) { _handleMouseDisable: function (e) {
@ -316,14 +309,13 @@ export var Mouse;
* to listen on the document element instead. * to listen on the document element instead.
*/ */
if (e.target == this._target) { if (e.target == this._target) {
//Util.Debug("mouse event disabled"); stopEvent(e);
Util.stopEvent(e);
} }
}, },
// Return coordinates relative to target // Return coordinates relative to target
_getMousePosition: function(e) { _getMousePosition: function(e) {
e = Util.getPointerEvent(e); e = getPointerEvent(e);
var bounds = this._target.getBoundingClientRect(); var bounds = this._target.getBoundingClientRect();
var x, y; var x, y;
// Clip to target bounds // Clip to target bounds
@ -344,12 +336,11 @@ export var Mouse;
return {x:x, y:y}; return {x:x, y:y};
}, },
// Public methods // Public methods
grab: function () { grab: function () {
var c = this._target; var c = this._target;
if (Util.isTouchDevice) { if (isTouchDevice) {
c.addEventListener('touchstart', this._eventHandlers.mousedown); c.addEventListener('touchstart', this._eventHandlers.mousedown);
window.addEventListener('touchend', this._eventHandlers.mouseup); window.addEventListener('touchend', this._eventHandlers.mouseup);
c.addEventListener('touchend', this._eventHandlers.mouseup); c.addEventListener('touchend', this._eventHandlers.mouseup);
@ -372,7 +363,7 @@ export var Mouse;
ungrab: function () { ungrab: function () {
var c = this._target; var c = this._target;
if (Util.isTouchDevice) { if (isTouchDevice) {
c.removeEventListener('touchstart', this._eventHandlers.mousedown); c.removeEventListener('touchstart', this._eventHandlers.mousedown);
window.removeEventListener('touchend', this._eventHandlers.mouseup); window.removeEventListener('touchend', this._eventHandlers.mouseup);
c.removeEventListener('touchend', this._eventHandlers.mouseup); c.removeEventListener('touchend', this._eventHandlers.mouseup);
@ -388,9 +379,9 @@ export var Mouse;
c.removeEventListener('contextmenu', this._eventHandlers.mousedisable); c.removeEventListener('contextmenu', this._eventHandlers.mousedisable);
} }
}; };
Util.make_properties(Mouse, [ make_properties(Mouse, [
['target', 'ro', 'dom'], // DOM element that captures mouse input ['target', 'ro', 'dom'], // DOM element that captures mouse input
['notify', 'ro', 'func'], // Function to call to notify whenever a mouse event is received ['notify', 'ro', 'func'], // Function to call to notify whenever a mouse event is received
['focused', 'rw', 'bool'], // Capture and send mouse clicks/movement ['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 ['onMouseButton', 'rw', 'func'], // Handler for mouse button click/release
['onMouseMove', 'rw', 'func'], // Handler for mouse movement ['onMouseMove', 'rw', 'func'], // Handler for mouse movement
['touchButton', 'rw', 'int'] // Button mask (1, 2, 4) for touch devices (0 means ignore clicks) ['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_VoidSymbol: 0xffffff, /* Void symbol */
XK_BackSpace: 0xff08, /* Back space, back char */ XK_BackSpace: 0xff08, /* Back space, back char */
@ -378,5 +378,3 @@ var KeyTable = {
XK_thorn: 0x00fe, /* U+00FE LATIN SMALL LETTER THORN */ XK_thorn: 0x00fe, /* U+00FE LATIN SMALL LETTER THORN */
XK_ydiaeresis: 0x00ff, /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */ 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 KeyTable from "./keysym.js";
import keysyms from "./keysymdef.js"; import keysyms from "./keysymdef.js";
export function substituteCodepoint(cp) {
var KeyboardUtil = {};
(function() {
"use strict";
function substituteCodepoint(cp) {
// Any Unicode code points which do not have corresponding keysym entries // 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 // can be swapped out for another code point by adding them to this table
var substitutions = { var substitutions = {
@ -21,20 +15,20 @@ var KeyboardUtil = {};
var sub = substitutions[cp]; var sub = substitutions[cp];
return sub ? sub : cp; return sub ? sub : cp;
} }
function isMac() { function isMac() {
return navigator && !!(/mac/i).exec(navigator.platform); return navigator && !!(/mac/i).exec(navigator.platform);
} }
function isWindows() { function isWindows() {
return navigator && !!(/win/i).exec(navigator.platform); return navigator && !!(/win/i).exec(navigator.platform);
} }
function isLinux() { function isLinux() {
return navigator && !!(/linux/i).exec(navigator.platform); 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 // Return true if a modifier which is not the specified char modifier (and is not shift) is down
function hasShortcutModifier(charModifier, currentModifiers) { export function hasShortcutModifier(charModifier, currentModifiers) {
var mods = {}; var mods = {};
for (var key in currentModifiers) { for (var key in currentModifiers) {
if (parseInt(key) !== KeyTable.XK_Shift_L) { if (parseInt(key) !== KeyTable.XK_Shift_L) {
@ -54,10 +48,10 @@ var KeyboardUtil = {};
else { else {
return sum > 0; return sum > 0;
} }
} }
// Return true if the specified char modifier is currently down // Return true if the specified char modifier is currently down
function hasCharModifier(charModifier, currentModifiers) { export function hasCharModifier(charModifier, currentModifiers) {
if (charModifier.length === 0) { return false; } if (charModifier.length === 0) { return false; }
for (var i = 0; i < charModifier.length; ++i) { for (var i = 0; i < charModifier.length; ++i) {
@ -66,11 +60,11 @@ var KeyboardUtil = {};
} }
} }
return true; return true;
} }
// Helper object tracking modifier key state // Helper object tracking modifier key state
// and generates fake key events to compensate if it gets out of sync // and generates fake key events to compensate if it gets out of sync
function ModifierSync(charModifier) { export function ModifierSync(charModifier) {
if (!charModifier) { if (!charModifier) {
if (isMac()) { if (isMac()) {
// on Mac, Option (AKA Alt) is used as a char modifier // 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 // if a char modifier is down, return the keys it consists of, otherwise return null
activeCharModifier: function() { return hasCharModifier(charModifier, state) ? charModifier : null; } activeCharModifier: function() { return hasCharModifier(charModifier, state) ? charModifier : null; }
}; };
} }
// Get a key ID from a keyboard event // Get a key ID from a keyboard event
// May be a string or an integer depending on the available properties // May be a string or an integer depending on the available properties
function getKey(evt){ export function getKey(evt){
if ('keyCode' in evt && 'key' in evt) { if ('keyCode' in evt && 'key' in evt) {
return evt.key + ':' + evt.keyCode; return evt.key + ':' + evt.keyCode;
} }
@ -166,11 +160,11 @@ var KeyboardUtil = {};
else { else {
return evt.key; return evt.key;
} }
} }
// Get the most reliable keysym value we can get from a key event // 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 // if char/charCode is available, prefer those, otherwise fall back to key/keyCode/which
function getKeysym(evt){ export function getKeysym(evt){
var codepoint; var codepoint;
if (evt.char && evt.char.length === 1) { if (evt.char && evt.char.length === 1) {
codepoint = evt.char.charCodeAt(); codepoint = evt.char.charCodeAt();
@ -196,11 +190,11 @@ var KeyboardUtil = {};
return keysyms.lookup(keysymFromKeyCode(evt.which, evt.shiftKey)); return keysyms.lookup(keysymFromKeyCode(evt.which, evt.shiftKey));
} }
return null; return null;
} }
// Given a keycode, try to predict which keysym it might be. // Given a keycode, try to predict which keysym it might be.
// If the keycode is unknown, null is returned. // If the keycode is unknown, null is returned.
function keysymFromKeyCode(keycode, shiftPressed) { export function keysymFromKeyCode(keycode, shiftPressed) {
if (typeof(keycode) !== 'number') { if (typeof(keycode) !== 'number') {
return null; return null;
} }
@ -231,11 +225,11 @@ var KeyboardUtil = {};
} }
return nonCharacterKey({keyCode: keycode}); return nonCharacterKey({keyCode: keycode});
} }
// if the key is a known non-character key (any key which doesn't generate character data) // if the key is a known non-character key (any key which doesn't generate character data)
// return its keysym value. Otherwise return null // return its keysym value. Otherwise return null
function nonCharacterKey(evt) { export function nonCharacterKey(evt) {
// evt.key not implemented yet // evt.key not implemented yet
if (!evt.keyCode) { return null; } if (!evt.keyCode) { return null; }
var keycode = evt.keyCode; var keycode = evt.keyCode;
@ -275,19 +269,9 @@ var KeyboardUtil = {};
case 93 : return KeyTable.XK_Menu; // also: Windows-Menu, Command on Mac case 93 : return KeyTable.XK_Menu; // also: Windows-Menu, Command on Mac
default: return null; default: return null;
} }
} }
KeyboardUtil.hasShortcutModifier = hasShortcutModifier; export function QEMUKeyEventDecoder (modifierState, next) {
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) {
"use strict"; "use strict";
function sendAll(evts) { function sendAll(evts) {
@ -333,7 +317,7 @@ KeyboardUtil.QEMUKeyEventDecoder = function(modifierState, next) {
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier(); var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
var isShift = evt.keyCode === 0x10 || evt.key === 'Shift'; 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); next(result);
return suppress; return suppress;
@ -357,7 +341,7 @@ KeyboardUtil.QEMUKeyEventDecoder = function(modifierState, next) {
}; };
}; };
KeyboardUtil.TrackQEMUKeyState = function(next) { export function TrackQEMUKeyState (next) {
"use strict"; "use strict";
var state = []; 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" // - 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 // - 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) // 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"; "use strict";
function sendAll(evts) { function sendAll(evts) {
for (var i = 0; i < evts.length; ++i) { for (var i = 0; i < evts.length; ++i) {
@ -434,18 +418,18 @@ KeyboardUtil.KeyEventDecoder = function(modifierState, next) {
} }
function process(evt, type) { function process(evt, type) {
var result = {type: type}; var result = {type: type};
var keyId = KeyboardUtil.getKey(evt); var keyId = getKey(evt);
if (keyId) { if (keyId) {
result.keyId = keyId; result.keyId = keyId;
} }
var keysym = KeyboardUtil.getKeysym(evt); var keysym = getKeysym(evt);
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier(); 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? // 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, // "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 // 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; result.keysym = keysym;
} }
@ -454,11 +438,11 @@ KeyboardUtil.KeyEventDecoder = function(modifierState, next) {
// Should we prevent the browser from handling the event? // Should we prevent the browser from handling the event?
// Doing so on a keydown (in most browsers) prevents keypress from being generated // Doing so on a keydown (in most browsers) prevents keypress from being generated
// so only do that if we have to. // 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, // 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 // 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) // if a char modifier is pressed, get the keys it consists of (on Windows, AltGr is equivalent to Ctrl+Alt)
var active = modifierState.activeCharModifier(); 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. // 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 // 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 // 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"; "use strict";
var queue = []; var queue = [];
var timer = null; 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 // 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. // 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 // 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"; "use strict";
var state = []; 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 @), // 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. // then the modifier must be "undone" before sending the @, and "redone" afterwards.
KeyboardUtil.EscapeModifiers = function(next) { export function EscapeModifiers (next) {
"use strict"; "use strict";
return function(evt) { return function(evt) {
if (evt.type !== 'keydown' || evt.escape === undefined) { if (evt.type !== 'keydown' || evt.escape === undefined) {
@ -674,5 +658,3 @@ KeyboardUtil.EscapeModifiers = function(next) {
/* jshint shadow: false */ /* jshint shadow: false */
}; };
}; };
export default KeyboardUtil;

View File

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

View File

@ -10,7 +10,10 @@
* (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca) * (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 Display from "./display.js";
import { Keyboard, Mouse } from "./input/devices.js"; import { Keyboard, Mouse } from "./input/devices.js";
import Websock from "./websock.js"; import Websock from "./websock.js";
@ -143,7 +146,7 @@ export default function RFB(defaults) {
this._qemuExtKeyEventSupported = false; this._qemuExtKeyEventSupported = false;
// set the default value on user-facing properties // set the default value on user-facing properties
Util.set_defaults(this, defaults, { set_defaults(this, defaults, {
'target': 'null', // VNC display rendering Canvas object 'target': 'null', // VNC display rendering Canvas object
'focusContainer': document, // DOM element that captures keyboard input 'focusContainer': document, // DOM element that captures keyboard input
'encrypt': false, // Use TLS/SSL/wss encryption 'encrypt': false, // Use TLS/SSL/wss encryption
@ -172,7 +175,7 @@ export default function RFB(defaults) {
}); });
// main setup // main setup
Util.Debug(">> RFB.constructor"); Log.Debug(">> RFB.constructor");
// populate encHandlers with bound versions // populate encHandlers with bound versions
Object.keys(RFB.encodingHandlers).forEach(function (encName) { Object.keys(RFB.encodingHandlers).forEach(function (encName) {
@ -192,7 +195,7 @@ export default function RFB(defaults) {
this._display = new Display({target: this._target, this._display = new Display({target: this._target,
onFlush: this._onFlush.bind(this)}); onFlush: this._onFlush.bind(this)});
} catch (exc) { } catch (exc) {
Util.Error("Display exception: " + exc); Log.Error("Display exception: " + exc);
throw exc; throw exc;
} }
@ -210,13 +213,13 @@ export default function RFB(defaults) {
if ((this._rfb_connection_state === 'connecting') && if ((this._rfb_connection_state === 'connecting') &&
(this._rfb_init_state === '')) { (this._rfb_init_state === '')) {
this._rfb_init_state = 'ProtocolVersion'; this._rfb_init_state = 'ProtocolVersion';
Util.Debug("Starting VNC handshake"); Log.Debug("Starting VNC handshake");
} else { } else {
this._fail("Unexpected server connection"); this._fail("Unexpected server connection");
} }
}.bind(this)); }.bind(this));
this._sock.on('close', function (e) { this._sock.on('close', function (e) {
Util.Warn("WebSocket on-close event"); Log.Warn("WebSocket on-close event");
var msg = ""; var msg = "";
if (e.code) { if (e.code) {
msg = " (code: " + e.code; msg = " (code: " + e.code;
@ -249,22 +252,19 @@ export default function RFB(defaults) {
this._sock.off('close'); this._sock.off('close');
}.bind(this)); }.bind(this));
this._sock.on('error', function (e) { this._sock.on('error', function (e) {
Util.Warn("WebSocket on-error event"); Log.Warn("WebSocket on-error event");
}); });
this._init_vars(); this._init_vars();
this._cleanup(); this._cleanup();
var rmode = this._display.get_render_mode(); 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() { RFB.prototype = {
var _ = Util.Localisation.get;
RFB.prototype = {
// Public methods // Public methods
connect: function (host, port, password, path) { connect: function (host, port, password, path) {
this._rfb_host = host; this._rfb_host = host;
@ -296,7 +296,7 @@ export default function RFB(defaults) {
sendCtrlAltDel: function () { sendCtrlAltDel: function () {
if (this._rfb_connection_state !== 'connected' || this._view_only) { return false; } 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_Control_L, 1);
RFB.messages.keyEvent(this._sock, KeyTable.XK_Alt_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) { xvpOp: function (ver, op) {
if (this._rfb_xvp_ver < ver) { return false; } 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)); this._sock.send_string("\xFA\x00" + String.fromCharCode(ver) + String.fromCharCode(op));
return true; return true;
}, },
@ -331,10 +331,10 @@ export default function RFB(defaults) {
sendKey: function (keysym, down) { sendKey: function (keysym, down) {
if (this._rfb_connection_state !== 'connected' || this._view_only) { return false; } if (this._rfb_connection_state !== 'connected' || this._view_only) { return false; }
if (typeof down !== 'undefined') { 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); RFB.messages.keyEvent(this._sock, keysym, down ? 1 : 0);
} else { } 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, 1);
RFB.messages.keyEvent(this._sock, keysym, 0); RFB.messages.keyEvent(this._sock, keysym, 0);
} }
@ -342,9 +342,7 @@ export default function RFB(defaults) {
}, },
clipboardPasteFrom: function (text) { clipboardPasteFrom: function (text) {
if (this._rfb_connection_state !== 'connected' || this._view_only) { if (this._rfb_connection_state !== 'connected' || this._view_only) { return; }
return;
}
RFB.messages.clientCutText(this._sock, text); RFB.messages.clientCutText(this._sock, text);
}, },
@ -370,7 +368,7 @@ export default function RFB(defaults) {
// Private methods // Private methods
_connect: function () { _connect: function () {
Util.Debug(">> RFB.connect"); Log.Debug(">> RFB.connect");
this._init_vars(); this._init_vars();
var uri; var uri;
@ -381,7 +379,7 @@ export default function RFB(defaults) {
} }
uri += '://' + this._rfb_host + ':' + this._rfb_port + '/' + this._rfb_path; uri += '://' + this._rfb_host + ':' + this._rfb_port + '/' + this._rfb_path;
Util.Info("connecting to " + uri); Log.Info("connecting to " + uri);
try { try {
// WebSocket.onopen transitions to the RFB init states // 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 () { _disconnect: function () {
Util.Debug(">> RFB.disconnect"); Log.Debug(">> RFB.disconnect");
this._cleanup(); this._cleanup();
this._sock.close(); this._sock.close();
this._print_stats(); this._print_stats();
Util.Debug("<< RFB.disconnect"); Log.Debug("<< RFB.disconnect");
}, },
_init_vars: function () { _init_vars: function () {
@ -428,19 +426,19 @@ export default function RFB(defaults) {
}, },
_print_stats: function () { _print_stats: function () {
Util.Info("Encoding stats for this connection:"); Log.Info("Encoding stats for this connection:");
var i, s; var i, s;
for (i = 0; i < this._encodings.length; i++) { for (i = 0; i < this._encodings.length; i++) {
s = this._encStats[this._encodings[i][1]]; s = this._encStats[this._encodings[i][1]];
if (s[0] + s[1] > 0) { 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++) { for (i = 0; i < this._encodings.length; i++) {
s = this._encStats[this._encodings[i][1]]; 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._keyboard.ungrab(); }
if (!this._view_only) { this._mouse.ungrab(); } if (!this._view_only) { this._mouse.ungrab(); }
this._display.defaultCursor(); this._display.defaultCursor();
if (Util.get_logging() !== 'debug') { if (Log.get_logging() !== 'debug') {
// Show noVNC logo on load and when disconnected, unless in // Show noVNC logo on load and when disconnected, unless in
// debug mode // debug mode
this._display.clear(); this._display.clear();
@ -466,13 +464,13 @@ export default function RFB(defaults) {
var oldstate = this._rfb_connection_state; var oldstate = this._rfb_connection_state;
if (state === oldstate) { if (state === oldstate) {
Util.Debug("Already in state '" + state + "', ignoring"); Log.Debug("Already in state '" + state + "', ignoring");
return; return;
} }
// The 'disconnected' state is permanent for each RFB object // The 'disconnected' state is permanent for each RFB object
if (oldstate === 'disconnected') { if (oldstate === 'disconnected') {
Util.Error("Tried changing state of a disconnected RFB object"); Log.Error("Tried changing state of a disconnected RFB object");
return; return;
} }
@ -480,7 +478,7 @@ export default function RFB(defaults) {
switch (state) { switch (state) {
case 'connected': case 'connected':
if (oldstate !== 'connecting') { if (oldstate !== 'connecting') {
Util.Error("Bad transition to connected state, " + Log.Error("Bad transition to connected state, " +
"previous connection state: " + oldstate); "previous connection state: " + oldstate);
return; return;
} }
@ -488,7 +486,7 @@ export default function RFB(defaults) {
case 'disconnected': case 'disconnected':
if (oldstate !== 'disconnecting') { if (oldstate !== 'disconnecting') {
Util.Error("Bad transition to disconnected state, " + Log.Error("Bad transition to disconnected state, " +
"previous connection state: " + oldstate); "previous connection state: " + oldstate);
return; return;
} }
@ -496,7 +494,7 @@ export default function RFB(defaults) {
case 'connecting': case 'connecting':
if (oldstate !== '') { if (oldstate !== '') {
Util.Error("Bad transition to connecting state, " + Log.Error("Bad transition to connecting state, " +
"previous connection state: " + oldstate); "previous connection state: " + oldstate);
return; return;
} }
@ -504,14 +502,14 @@ export default function RFB(defaults) {
case 'disconnecting': case 'disconnecting':
if (oldstate !== 'connected' && oldstate !== 'connecting') { if (oldstate !== 'connected' && oldstate !== 'connecting') {
Util.Error("Bad transition to disconnecting state, " + Log.Error("Bad transition to disconnecting state, " +
"previous connection state: " + oldstate); "previous connection state: " + oldstate);
return; return;
} }
break; break;
default: default:
Util.Error("Unknown connection state: " + state); Log.Error("Unknown connection state: " + state);
return; return;
} }
@ -521,10 +519,10 @@ export default function RFB(defaults) {
this._onUpdateState(this, state, oldstate); this._onUpdateState(this, state, oldstate);
var smsg = "New state '" + state + "', was '" + oldstate + "'."; var smsg = "New state '" + state + "', was '" + oldstate + "'.";
Util.Debug(smsg); Log.Debug(smsg);
if (this._disconnTimer && state !== 'disconnecting') { if (this._disconnTimer && state !== 'disconnecting') {
Util.Debug("Clearing disconnect timer"); Log.Debug("Clearing disconnect timer");
clearTimeout(this._disconnTimer); clearTimeout(this._disconnTimer);
this._disconnTimer = null; this._disconnTimer = null;
@ -571,16 +569,16 @@ export default function RFB(defaults) {
} }
switch (this._rfb_connection_state) { switch (this._rfb_connection_state) {
case 'disconnecting': case 'disconnecting':
Util.Error("Failed when disconnecting: " + fullmsg); Log.Error("Failed when disconnecting: " + fullmsg);
break; break;
case 'connected': case 'connected':
Util.Error("Failed while connected: " + fullmsg); Log.Error("Failed while connected: " + fullmsg);
break; break;
case 'connecting': case 'connecting':
Util.Error("Failed when connecting: " + fullmsg); Log.Error("Failed when connecting: " + fullmsg);
break; break;
default: default:
Util.Error("RFB failure: " + fullmsg); Log.Error("RFB failure: " + fullmsg);
break; break;
} }
this._rfb_disconnect_reason = msg; //This is sent to the UI this._rfb_disconnect_reason = msg; //This is sent to the UI
@ -605,10 +603,10 @@ export default function RFB(defaults) {
case 'normal': case 'normal':
case 'warn': case 'warn':
case 'error': case 'error':
Util.Debug("Notification[" + level + "]:" + msg); Log.Debug("Notification[" + level + "]:" + msg);
break; break;
default: default:
Util.Error("Invalid notification level: " + level); Log.Error("Invalid notification level: " + level);
return; return;
} }
@ -621,13 +619,13 @@ export default function RFB(defaults) {
_handle_message: function () { _handle_message: function () {
if (this._sock.rQlen() === 0) { 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; return;
} }
switch (this._rfb_connection_state) { switch (this._rfb_connection_state) {
case 'disconnected': case 'disconnected':
Util.Error("Got data while disconnected"); Log.Error("Got data while disconnected");
break; break;
case 'connected': case 'connected':
while (true) { while (true) {
@ -658,7 +656,7 @@ export default function RFB(defaults) {
var keysym = keyevent.keysym; var keysym = keyevent.keysym;
RFB.messages.QEMUExtendedKeyEvent(this._sock, keysym, down, scancode); RFB.messages.QEMUExtendedKeyEvent(this._sock, keysym, down, scancode);
} else { } 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 { } else {
keysym = keyevent.keysym.keysym; keysym = keyevent.keysym.keysym;
@ -734,7 +732,7 @@ export default function RFB(defaults) {
} }
var sversion = this._sock.rQshiftStr(12).substr(4, 7); var sversion = this._sock.rQshiftStr(12).substr(4, 7);
Util.Info("Server ProtocolVersion: " + sversion); Log.Info("Server ProtocolVersion: " + sversion);
var is_repeater = 0; var is_repeater = 0;
switch (sversion) { switch (sversion) {
case "000.000": // UltraVNC repeater case "000.000": // UltraVNC repeater
@ -775,12 +773,23 @@ export default function RFB(defaults) {
var cversion = "00" + parseInt(this._rfb_version, 10) + var cversion = "00" + parseInt(this._rfb_version, 10) +
".00" + ((this._rfb_version * 10) % 10); ".00" + ((this._rfb_version * 10) % 10);
this._sock.send_string("RFB " + cversion + "\n"); this._sock.send_string("RFB " + cversion + "\n");
Util.Debug('Sent ProtocolVersion: ' + cversion); Log.Debug('Sent ProtocolVersion: ' + cversion);
this._rfb_init_state = 'Security'; this._rfb_init_state = 'Security';
}, },
_negotiate_security: function () { _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) { if (this._rfb_version >= 3.7) {
// Server sends supported list, client decides // Server sends supported list, client decides
var num_types = this._sock.rQshift8(); var num_types = this._sock.rQshift8();
@ -794,18 +803,7 @@ export default function RFB(defaults) {
} }
var types = this._sock.rQshiftBytes(num_types); var types = this._sock.rQshiftBytes(num_types);
Util.Debug("Server security types: " + types); Log.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;
}
// Look for each auth in preferred order // Look for each auth in preferred order
this._rfb_auth_scheme = 0; this._rfb_auth_scheme = 0;
@ -830,7 +828,7 @@ export default function RFB(defaults) {
} }
this._rfb_init_state = 'Authentication'; 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 return this._init_msg(); // jump to authentication
}, },
@ -999,7 +997,7 @@ export default function RFB(defaults) {
switch (this._sock.rQshift32()) { switch (this._sock.rQshift32()) {
case 0: // OK case 0: // OK
this._rfb_init_state = 'ClientInitialisation'; this._rfb_init_state = 'ClientInitialisation';
Util.Debug('Authentication OK'); Log.Debug('Authentication OK');
return this._init_msg(); return this._init_msg();
case 1: // failed case 1: // failed
if (this._rfb_version >= 3.8) { if (this._rfb_version >= 3.8) {
@ -1047,7 +1045,7 @@ export default function RFB(defaults) {
/* Connection name/title */ /* Connection name/title */
var name_length = this._sock.rQshift32(); var name_length = this._sock.rQshift32();
if (this._sock.rQwait('server init name', name_length, 24)) { return false; } 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._rfb_tightvnc) {
if (this._sock.rQwait('TightVNC extended server init header', 8, 24 + name_length)) { return false; } 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 // NB(directxman12): these are down here so that we don't run them multiple times
// if we backtrack // 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 + ", bpp: " + bpp + ", depth: " + depth +
", big_endian: " + big_endian + ", big_endian: " + big_endian +
", true_color: " + true_color + ", true_color: " + true_color +
@ -1087,22 +1085,22 @@ export default function RFB(defaults) {
", blue_shift: " + blue_shift); ", blue_shift: " + blue_shift);
if (big_endian !== 0) { 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) { 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) { 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 // we're past the point where we could backtrack, so it's safe to call this
this._onDesktopName(this, this._fb_name); this._onDesktopName(this, this._fb_name);
if (this._true_color && this._fb_name === "Intel(r) AMT KVM") { 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; this._true_color = false;
} }
@ -1169,7 +1167,7 @@ export default function RFB(defaults) {
}, },
_handle_set_colour_map_msg: function () { _handle_set_colour_map_msg: function () {
Util.Debug("SetColorMapEntries"); Log.Debug("SetColorMapEntries");
this._sock.rQskip8(); // Padding this._sock.rQskip8(); // Padding
var first_colour = this._sock.rQshift16(); var first_colour = this._sock.rQshift16();
@ -1182,14 +1180,14 @@ export default function RFB(defaults) {
var blue = parseInt(this._sock.rQshift16() / 256, 10); var blue = parseInt(this._sock.rQshift16() / 256, 10);
this._display.set_colourMap([blue, green, red], first_colour + c); this._display.set_colourMap([blue, green, red], first_colour + c);
} }
Util.Debug("colourMap: " + this._display.get_colourMap()); Log.Debug("colourMap: " + this._display.get_colourMap());
Util.Info("Registered " + num_colours + " colourMap entries"); Log.Info("Registered " + num_colours + " colourMap entries");
return true; return true;
}, },
_handle_server_cut_text: function () { _handle_server_cut_text: function () {
Util.Debug("ServerCutText"); Log.Debug("ServerCutText");
if (this._view_only) { return true; } if (this._view_only) { return true; }
if (this._sock.rQwait("ServerCutText header", 7, 1)) { return false; } 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 (this._sock.rQwait("ServerFence payload", length, 9)) { return false; }
if (length > 64) { if (length > 64) {
Util.Warn("Bad payload length (" + length + ") in fence response"); Log.Warn("Bad payload length (" + length + ") in fence response");
length = 64; length = 64;
} }
@ -1254,12 +1252,12 @@ export default function RFB(defaults) {
switch (xvp_msg) { switch (xvp_msg) {
case 0: // XVP_FAIL case 0: // XVP_FAIL
Util.Error("Operation Failed"); Log.Error("Operation Failed");
this._notification("XVP Operation Failed", 'error'); this._notification("XVP Operation Failed", 'error');
break; break;
case 1: // XVP_INIT case 1: // XVP_INIT
this._rfb_xvp_ver = xvp_ver; 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); this._onXvpInit(this._rfb_xvp_ver);
break; break;
default: default:
@ -1293,7 +1291,7 @@ export default function RFB(defaults) {
return this._handle_set_colour_map_msg(); return this._handle_set_colour_map_msg();
case 2: // Bell case 2: // Bell
Util.Debug("Bell"); Log.Debug("Bell");
this._onBell(this); this._onBell(this);
return true; return true;
@ -1307,7 +1305,7 @@ export default function RFB(defaults) {
if (first) { if (first) {
this._enabledContinuousUpdates = true; this._enabledContinuousUpdates = true;
this._updateContinuousUpdates(); this._updateContinuousUpdates();
Util.Info("Enabling continuous updates."); Log.Info("Enabling continuous updates.");
} else { } else {
// FIXME: We need to send a framebufferupdaterequest here // FIXME: We need to send a framebufferupdaterequest here
// if we add support for turning off continuous updates // if we add support for turning off continuous updates
@ -1322,7 +1320,7 @@ export default function RFB(defaults) {
default: default:
this._fail("Unexpected server message", "Type:" + msg_type); 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; return true;
} }
}, },
@ -1347,7 +1345,7 @@ export default function RFB(defaults) {
this._timing.cur_fbu = 0; this._timing.cur_fbu = 0;
if (this._timing.fbu_rt_start > 0) { if (this._timing.fbu_rt_start > 0) {
now = (new Date()).getTime(); 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 // 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.fbu_rt_start > 0) {
this._timing.full_fbu_total += this._timing.cur_fbu; this._timing.full_fbu_total += this._timing.cur_fbu;
this._timing.full_fbu_cnt++; 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.cur_fbu + ", total: " +
this._timing.full_fbu_total + ", cnt: " + this._timing.full_fbu_total + ", cnt: " +
this._timing.full_fbu_cnt + ", avg: " + 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; var fbu_rt_diff = now - this._timing.fbu_rt_start;
this._timing.fbu_rt_total += fbu_rt_diff; this._timing.fbu_rt_total += fbu_rt_diff;
this._timing.fbu_rt_cnt++; this._timing.fbu_rt_cnt++;
Util.Info("full FBU round-trip, cur: " + Log.Info("full FBU round-trip, cur: " +
fbu_rt_diff + ", total: " + fbu_rt_diff + ", total: " +
this._timing.fbu_rt_total + ", cnt: " + this._timing.fbu_rt_total + ", cnt: " +
this._timing.fbu_rt_cnt + ", avg: " + this._timing.fbu_rt_cnt + ", avg: " +
@ -1447,9 +1445,9 @@ export default function RFB(defaults) {
RFB.messages.enableContinuousUpdates(this._sock, true, 0, 0, RFB.messages.enableContinuousUpdates(this._sock, true, 0, 0,
this._fb_width, this._fb_height); this._fb_width, this._fb_height);
} }
}; };
Util.make_properties(RFB, [ make_properties(RFB, [
['target', 'wo', 'dom'], // VNC display rendering Canvas object ['target', 'wo', 'dom'], // VNC display rendering Canvas object
['focusContainer', 'wo', 'dom'], // DOM element that captures keyboard input ['focusContainer', 'wo', 'dom'], // DOM element that captures keyboard input
['encrypt', 'rw', 'bool'], // Use TLS/SSL/wss encryption ['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 ['onFBResize', 'rw', 'func'], // onFBResize(rfb, width, height): frame buffer resized
['onDesktopName', 'rw', 'func'], // onDesktopName(rfb, name): desktop name received ['onDesktopName', 'rw', 'func'], // onDesktopName(rfb, name): desktop name received
['onXvpInit', 'rw', 'func'] // onXvpInit(version): XVP extensions active for this connection ['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})) { if (!cursor || (cursor in {'0': 1, 'no': 1, 'false': 1})) {
this._local_cursor = false; this._local_cursor = false;
this._display.disableLocalCursor(); //Only show server-side cursor this._display.disableLocalCursor(); //Only show server-side cursor
@ -1485,7 +1483,7 @@ export default function RFB(defaults) {
if (this._display.get_cursor_uri()) { if (this._display.get_cursor_uri()) {
this._local_cursor = true; this._local_cursor = true;
} else { } else {
Util.Warn("Browser does not support local cursor"); Log.Warn("Browser does not support local cursor");
this._display.disableLocalCursor(); this._display.disableLocalCursor();
} }
} }
@ -1495,9 +1493,9 @@ export default function RFB(defaults) {
RFB.messages.clientEncodings(this._sock, this._encodings, cursor, RFB.messages.clientEncodings(this._sock, this._encodings, cursor,
this._true_color); this._true_color);
} }
}; };
RFB.prototype.set_view_only = function (view_only) { RFB.prototype.set_view_only = function (view_only) {
this._view_only = view_only; this._view_only = view_only;
if (this._rfb_connection_state === "connecting" || if (this._rfb_connection_state === "connecting" ||
@ -1510,14 +1508,14 @@ export default function RFB(defaults) {
this._mouse.grab(); this._mouse.grab();
} }
} }
}; };
RFB.prototype.get_display = function () { return this._display; }; RFB.prototype.get_display = function () { return this._display; };
RFB.prototype.get_keyboard = function () { return this._keyboard; }; RFB.prototype.get_keyboard = function () { return this._keyboard; };
RFB.prototype.get_mouse = function () { return this._mouse; }; RFB.prototype.get_mouse = function () { return this._mouse; };
// Class Methods // Class Methods
RFB.messages = { RFB.messages = {
keyEvent: function (sock, keysym, down) { keyEvent: function (sock, keysym, down) {
var buff = sock._sQ; var buff = sock._sQ;
var offset = sock._sQlen; var offset = sock._sQlen;
@ -1748,10 +1746,10 @@ export default function RFB(defaults) {
var i, j = offset + 4, cnt = 0; var i, j = offset + 4, cnt = 0;
for (i = 0; i < encodings.length; i++) { for (i = 0; i < encodings.length; i++) {
if (encodings[i][0] === "Cursor" && !local_cursor) { 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) { } else if (encodings[i][0] === "TIGHT" && !true_color) {
// TODO: remove this when we have tight+non-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 { } else {
var enc = encodings[i][1]; var enc = encodings[i][1];
buff[j] = enc >> 24; buff[j] = enc >> 24;
@ -1796,17 +1794,17 @@ export default function RFB(defaults) {
sock._sQlen += 10; sock._sQlen += 10;
sock.flush(); sock.flush();
} }
}; };
RFB.genDES = function (password, challenge) { RFB.genDES = function (password, challenge) {
var passwd = []; var passwd = [];
for (var i = 0; i < password.length; i++) { for (var i = 0; i < password.length; i++) {
passwd.push(password.charCodeAt(i)); passwd.push(password.charCodeAt(i));
} }
return (new DES(passwd)).encrypt(challenge); return (new DES(passwd)).encrypt(challenge);
}; };
RFB.encodingHandlers = { RFB.encodingHandlers = {
RAW: function () { RAW: function () {
if (this._FBU.lines === 0) { if (this._FBU.lines === 0) {
this._FBU.lines = this._FBU.height; this._FBU.lines = this._FBU.height;
@ -1936,7 +1934,7 @@ export default function RFB(defaults) {
if (this._FBU.subencoding === 0) { if (this._FBU.subencoding === 0) {
if (this._FBU.lastsubencoding & 0x01) { if (this._FBU.lastsubencoding & 0x01) {
// Weird: ignore blanks are RAW // Weird: ignore blanks are RAW
Util.Debug(" Ignoring blank after RAW"); Log.Debug(" Ignoring blank after RAW");
} else { } else {
this._display.fillRect(x, y, w, h, this._FBU.background); 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++) { for (var i = 0; i < 4; i++) {
if ((resetStreams >> i) & 1) { if ((resetStreams >> i) & 1) {
this._FBU.zlibs[i].reset(); 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].uncompress(data, 0);
var uncompressed = this._FBU.zlibs[streamId].inflate(data, true, expected); var uncompressed = this._FBU.zlibs[streamId].inflate(data, true, expected);
/*if (uncompressed.status !== 0) { /*if (uncompressed.status !== 0) {
Util.Error("Invalid data in zlib stream"); Log.Error("Invalid data in zlib stream");
}*/ }*/
//return uncompressed.data; //return uncompressed.data;
@ -2431,7 +2429,7 @@ export default function RFB(defaults) {
}, },
Cursor: function () { Cursor: function () {
Util.Debug(">> set_cursor"); Log.Debug(">> set_cursor");
var x = this._FBU.x; // hotspot-x var x = this._FBU.x; // hotspot-x
var y = this._FBU.y; // hotspot-y var y = this._FBU.y; // hotspot-y
var w = this._FBU.width; var w = this._FBU.width;
@ -2450,7 +2448,7 @@ export default function RFB(defaults) {
this._FBU.bytes = 0; this._FBU.bytes = 0;
this._FBU.rects--; this._FBU.rects--;
Util.Debug("<< set_cursor"); Log.Debug("<< set_cursor");
return true; return true;
}, },
@ -2465,11 +2463,10 @@ export default function RFB(defaults) {
}, },
JPEG_quality_lo: function () { JPEG_quality_lo: function () {
Util.Error("Server sent jpeg_quality pseudo-encoding"); Log.Error("Server sent jpeg_quality pseudo-encoding");
}, },
compress_lo: function () { 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. * 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 */ /*jslint browser: true, bitwise: true */
/*global Util*/ /*global Util*/
@ -43,16 +42,14 @@ export default function Websock() {
}; };
}; };
(function () { // this has performance issues in some versions Chromium, and
"use strict"; // doesn't gain a tremendous amount of performance increase in Firefox
// this has performance issues in some versions Chromium, and // at the moment. It may be valuable to turn it on in the future.
// doesn't gain a tremendous amount of performance increase in Firefox var ENABLE_COPYWITHIN = false;
// 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 // This is only for PhantomJS, which doesn't like apply-ing
// with Typed Arrays // with Typed Arrays
try { try {
@ -65,9 +62,9 @@ export default function Websock() {
null, Array.prototype.slice.call(a)); null, Array.prototype.slice.call(a));
}; };
} }
})(); })();
Websock.prototype = { Websock.prototype = {
// Getters and Setters // Getters and Setters
get_sQ: function () { get_sQ: function () {
return this._sQ; return this._sQ;
@ -172,7 +169,7 @@ export default function Websock() {
flush: function () { flush: function () {
if (this._websocket.bufferedAmount !== 0) { 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) { 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.onmessage = this._recv_message.bind(this);
this._websocket.onopen = (function () { this._websocket.onopen = (function () {
Util.Debug('>> WebSock.onopen'); Log.Debug('>> WebSock.onopen');
if (this._websocket.protocol) { 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(); this._eventHandlers.open();
Util.Debug("<< WebSock.onopen"); Log.Debug("<< WebSock.onopen");
}).bind(this); }).bind(this);
this._websocket.onclose = (function (e) { this._websocket.onclose = (function (e) {
Util.Debug(">> WebSock.onclose"); Log.Debug(">> WebSock.onclose");
this._eventHandlers.close(e); this._eventHandlers.close(e);
Util.Debug("<< WebSock.onclose"); Log.Debug("<< WebSock.onclose");
}).bind(this); }).bind(this);
this._websocket.onerror = (function (e) { this._websocket.onerror = (function (e) {
Util.Debug(">> WebSock.onerror: " + e); Log.Debug(">> WebSock.onerror: " + e);
this._eventHandlers.error(e); this._eventHandlers.error(e);
Util.Debug("<< WebSock.onerror: " + e); Log.Debug("<< WebSock.onerror: " + e);
}).bind(this); }).bind(this);
}, },
@ -246,7 +243,7 @@ export default function Websock() {
if (this._websocket) { if (this._websocket) {
if ((this._websocket.readyState === WebSocket.OPEN) || if ((this._websocket.readyState === WebSocket.OPEN) ||
(this._websocket.readyState === WebSocket.CONNECTING)) { (this._websocket.readyState === WebSocket.CONNECTING)) {
Util.Info("Closing WebSocket connection"); Log.Info("Closing WebSocket connection");
this._websocket.close(); this._websocket.close();
} }
@ -321,7 +318,7 @@ export default function Websock() {
this._expand_compact_rQ(); this._expand_compact_rQ();
} }
} else { } else {
Util.Debug("Ignoring empty message"); Log.Debug("Ignoring empty message");
} }
} catch (exc) { } catch (exc) {
var exception_str = ""; var exception_str = "";
@ -339,9 +336,9 @@ export default function Websock() {
} }
if (exception_str.length > 0) { if (exception_str.length > 0) {
Util.Error("recv_message, caught exception: " + exception_str); Log.Error("recv_message, caught exception: " + exception_str);
} else { } else {
Util.Error("recv_message, caught exception: " + exc); Log.Error("recv_message, caught exception: " + exc);
} }
if (typeof exc.name !== 'undefined') { if (typeof exc.name !== 'undefined') {
@ -351,5 +348,4 @@ export default function Websock() {
} }
} }
} }
}; };
})();