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:
parent
6e744119f8
commit
6d6f0db0da
|
@ -7,3 +7,5 @@ utils/websockify
|
|||
/build
|
||||
/lib
|
||||
recordings
|
||||
*.swp
|
||||
*~
|
||||
|
|
17
LICENSE.txt
17
LICENSE.txt
|
@ -5,20 +5,9 @@ Public License 2.0). The noVNC core library is composed of the
|
|||
Javascript code necessary for full noVNC operation. This includes (but
|
||||
is not limited to):
|
||||
|
||||
core/base64.js
|
||||
core/des.js
|
||||
core/display.js
|
||||
core/input/devices.js
|
||||
core/input/keysym.js
|
||||
core/logo.js
|
||||
core/playback.js
|
||||
core/rfb.js
|
||||
app/ui.js
|
||||
core/util.js
|
||||
core/websock.js
|
||||
app/webutil.js
|
||||
core/input/xtscancodes.js
|
||||
core/inflator.js
|
||||
core/**/*.js
|
||||
app/*.js
|
||||
test/playback.js
|
||||
|
||||
The HTML, CSS, font and images files that included with the noVNC
|
||||
source distibution (or repository) are not considered part of the
|
||||
|
|
115
app/ui.js
115
app/ui.js
|
@ -11,20 +11,18 @@
|
|||
/* jslint white: false, browser: true */
|
||||
/* global window, document.getElementById, Util, WebUtil, RFB, Display */
|
||||
|
||||
import Util from "../core/util.js";
|
||||
import * as Log from '../core/util/logging.js';
|
||||
import _, { l10n } from '../core/util/localization.js'
|
||||
import { isTouchDevice, browserSupportsCursorURIs as cursorURIsSupported } from '../core/util/browsers.js';
|
||||
import { setCapture, getPointerEvent } from '../core/util/events.js';
|
||||
import KeyTable from "../core/input/keysym.js";
|
||||
import keysyms from "../core/input/keysymdef.js";
|
||||
import RFB from "../core/rfb.js";
|
||||
import Display from "../core/display.js";
|
||||
import WebUtil from "./webutil.js";
|
||||
import * as WebUtil from "./webutil.js";
|
||||
|
||||
var UI;
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
// Fallback for all uncought errors
|
||||
window.addEventListener('error', function(event) {
|
||||
// Fallback for all uncought errors
|
||||
window.addEventListener('error', function(event) {
|
||||
try {
|
||||
var msg, div, text;
|
||||
|
||||
|
@ -61,11 +59,9 @@ var UI;
|
|||
// Don't return true since this would prevent the error
|
||||
// from being printed to the browser console.
|
||||
return false;
|
||||
});
|
||||
});
|
||||
|
||||
var _ = Util.Localisation.get;
|
||||
|
||||
UI = {
|
||||
const UI = {
|
||||
|
||||
connected: false,
|
||||
desktopName: "",
|
||||
|
@ -90,6 +86,14 @@ var UI;
|
|||
reconnect_callback: null,
|
||||
reconnect_password: null,
|
||||
|
||||
prime: function(callback) {
|
||||
if (document.readyState === "interactive" || document.readyState === "complete") {
|
||||
UI.load(callback);
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', UI.load.bind(UI, callback));
|
||||
}
|
||||
},
|
||||
|
||||
// Setup rfb object, load settings from browser storage, then call
|
||||
// UI.init to setup the UI/menus
|
||||
load: function(callback) {
|
||||
|
@ -106,10 +110,10 @@ var UI;
|
|||
UI.initSettings();
|
||||
|
||||
// Translate the DOM
|
||||
Util.Localisation.translateDOM();
|
||||
l10n.translateDOM();
|
||||
|
||||
// Adapt the interface for touch screen devices
|
||||
if (Util.isTouchDevice) {
|
||||
if (isTouchDevice) {
|
||||
document.documentElement.classList.add("noVNC_touch");
|
||||
// Remove the address bar
|
||||
setTimeout(function() { window.scrollTo(0, 1); }, 100);
|
||||
|
@ -204,7 +208,7 @@ var UI;
|
|||
UI.initSetting('port', port);
|
||||
UI.initSetting('encrypt', (window.location.protocol === "https:"));
|
||||
UI.initSetting('true_color', true);
|
||||
UI.initSetting('cursor', !Util.isTouchDevice);
|
||||
UI.initSetting('cursor', !isTouchDevice);
|
||||
UI.initSetting('clip', false);
|
||||
UI.initSetting('resize', 'off');
|
||||
UI.initSetting('shared', true);
|
||||
|
@ -216,7 +220,6 @@ var UI;
|
|||
|
||||
UI.setupSettingLabels();
|
||||
},
|
||||
|
||||
// Adds a link to the label elements on the corresponding input elements
|
||||
setupSettingLabels: function() {
|
||||
var labels = document.getElementsByTagName('LABEL');
|
||||
|
@ -254,17 +257,17 @@ var UI;
|
|||
return true;
|
||||
} catch (exc) {
|
||||
var msg = "Unable to create RFB client -- " + exc;
|
||||
Util.Error(msg);
|
||||
Log.Error(msg);
|
||||
UI.showStatus(msg, 'error');
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/* ------^-------
|
||||
* /INIT
|
||||
* ==============
|
||||
* EVENT HANDLERS
|
||||
* ------v------*/
|
||||
* /INIT
|
||||
* ==============
|
||||
* EVENT HANDLERS
|
||||
* ------v------*/
|
||||
|
||||
addResizeHandlers: function() {
|
||||
window.addEventListener('resize', UI.applyResizeMode);
|
||||
|
@ -489,7 +492,7 @@ var UI;
|
|||
break;
|
||||
default:
|
||||
msg = "Invalid UI state";
|
||||
Util.Error(msg);
|
||||
Log.Error(msg);
|
||||
UI.showStatus(msg, 'error');
|
||||
break;
|
||||
}
|
||||
|
@ -499,11 +502,11 @@ var UI;
|
|||
|
||||
// Disable/enable controls depending on connection state
|
||||
updateVisualState: function() {
|
||||
//Util.Debug(">> updateVisualState");
|
||||
//Log.Debug(">> updateVisualState");
|
||||
|
||||
UI.enableDisableViewClip();
|
||||
|
||||
if (Util.browserSupportsCursorURIs() && !Util.isTouchDevice) {
|
||||
if (cursorURIsSupported() && !isTouchDevice) {
|
||||
UI.enableSetting('cursor');
|
||||
} else {
|
||||
UI.disableSetting('cursor');
|
||||
|
@ -555,7 +558,7 @@ var UI;
|
|||
document.getElementById('noVNC_password_dlg')
|
||||
.classList.remove('noVNC_open');
|
||||
|
||||
//Util.Debug("<< updateVisualState");
|
||||
//Log.Debug("<< updateVisualState");
|
||||
},
|
||||
|
||||
showStatus: function(text, status_type, time) {
|
||||
|
@ -669,7 +672,7 @@ var UI;
|
|||
dragControlbarHandle: function (e) {
|
||||
if (!UI.controlbarGrabbed) return;
|
||||
|
||||
var ptr = Util.getPointerEvent(e);
|
||||
var ptr = getPointerEvent(e);
|
||||
|
||||
var anchor = document.getElementById('noVNC_control_bar_anchor');
|
||||
if (ptr.clientX < (window.innerWidth * 0.1)) {
|
||||
|
@ -766,12 +769,12 @@ var UI;
|
|||
controlbarHandleMouseDown: function(e) {
|
||||
if ((e.type == "mousedown") && (e.button != 0)) return;
|
||||
|
||||
var ptr = Util.getPointerEvent(e);
|
||||
var ptr = getPointerEvent(e);
|
||||
|
||||
var handle = document.getElementById("noVNC_control_bar_handle");
|
||||
var bounds = handle.getBoundingClientRect();
|
||||
|
||||
Util.setCapture(handle);
|
||||
setCapture(handle);
|
||||
UI.controlbarGrabbed = true;
|
||||
UI.controlbarDrag = false;
|
||||
|
||||
|
@ -852,7 +855,7 @@ var UI;
|
|||
val = ctrl.value;
|
||||
}
|
||||
WebUtil.writeSetting(name, val);
|
||||
//Util.Debug("Setting saved '" + name + "=" + val + "'");
|
||||
//Log.Debug("Setting saved '" + name + "=" + val + "'");
|
||||
return val;
|
||||
},
|
||||
|
||||
|
@ -911,10 +914,10 @@ var UI;
|
|||
// Refresh UI elements from saved cookies
|
||||
UI.updateSetting('encrypt');
|
||||
UI.updateSetting('true_color');
|
||||
if (Util.browserSupportsCursorURIs()) {
|
||||
if (cursorURIsSupported()) {
|
||||
UI.updateSetting('cursor');
|
||||
} else {
|
||||
UI.updateSetting('cursor', !Util.isTouchDevice);
|
||||
UI.updateSetting('cursor', !isTouchDevice);
|
||||
UI.disableSetting('cursor');
|
||||
}
|
||||
UI.updateSetting('clip');
|
||||
|
@ -1027,9 +1030,9 @@ var UI;
|
|||
},
|
||||
|
||||
clipboardReceive: function(rfb, text) {
|
||||
Util.Debug(">> UI.clipboardReceive: " + text.substr(0,40) + "...");
|
||||
Log.Debug(">> UI.clipboardReceive: " + text.substr(0,40) + "...");
|
||||
document.getElementById('noVNC_clipboard_text').value = text;
|
||||
Util.Debug("<< UI.clipboardReceive");
|
||||
Log.Debug("<< UI.clipboardReceive");
|
||||
},
|
||||
|
||||
clipboardClear: function() {
|
||||
|
@ -1039,9 +1042,9 @@ var UI;
|
|||
|
||||
clipboardSend: function() {
|
||||
var text = document.getElementById('noVNC_clipboard_text').value;
|
||||
Util.Debug(">> UI.clipboardSend: " + text.substr(0,40) + "...");
|
||||
Log.Debug(">> UI.clipboardSend: " + text.substr(0,40) + "...");
|
||||
UI.rfb.clipboardPasteFrom(text);
|
||||
Util.Debug("<< UI.clipboardSend");
|
||||
Log.Debug("<< UI.clipboardSend");
|
||||
},
|
||||
|
||||
/* ------^-------
|
||||
|
@ -1075,7 +1078,7 @@ var UI;
|
|||
|
||||
if ((!host) || (!port)) {
|
||||
var msg = _("Must set host and port");
|
||||
Util.Error(msg);
|
||||
Log.Error(msg);
|
||||
UI.showStatus(msg, 'error');
|
||||
return;
|
||||
}
|
||||
|
@ -1165,7 +1168,7 @@ var UI;
|
|||
if (typeof msg === 'undefined') {
|
||||
msg = _("Password is required");
|
||||
}
|
||||
Util.Warn(msg);
|
||||
Log.Warn(msg);
|
||||
UI.showStatus(msg, "warning");
|
||||
},
|
||||
|
||||
|
@ -1259,7 +1262,7 @@ var UI;
|
|||
UI.resizeTimeout = setTimeout(function(){
|
||||
// Request a remote size covering the viewport
|
||||
if (UI.rfb.requestDesktopSize(screen.w, screen.h)) {
|
||||
Util.Debug('Requested new desktop size: ' +
|
||||
Log.Debug('Requested new desktop size: ' +
|
||||
screen.w + 'x' + screen.h);
|
||||
}
|
||||
}, 500);
|
||||
|
@ -1314,7 +1317,7 @@ var UI;
|
|||
if (resizeSetting === 'downscale' || resizeSetting === 'scale') {
|
||||
// Disable clipping if we are scaling
|
||||
new_clip = false;
|
||||
} else if (Util.isTouchDevice) {
|
||||
} else if (isTouchDevice) {
|
||||
// Touch devices usually have shit scrollbars
|
||||
new_clip = true;
|
||||
}
|
||||
|
@ -1342,7 +1345,7 @@ var UI;
|
|||
var resizeSetting = UI.getSetting('resize');
|
||||
// Disable clipping if we are scaling, connected or on touch
|
||||
if (resizeSetting === 'downscale' || resizeSetting === 'scale' ||
|
||||
Util.isTouchDevice) {
|
||||
isTouchDevice) {
|
||||
UI.disableSetting('clip');
|
||||
} else {
|
||||
UI.enableSetting('clip');
|
||||
|
@ -1401,7 +1404,7 @@ var UI;
|
|||
|
||||
// Different behaviour for touch vs non-touch
|
||||
// The button is disabled instead of hidden on touch devices
|
||||
if (Util.isTouchDevice) {
|
||||
if (isTouchDevice) {
|
||||
viewDragButton.classList.remove("noVNC_hidden");
|
||||
|
||||
if (clipping) {
|
||||
|
@ -1427,7 +1430,7 @@ var UI;
|
|||
* ------v------*/
|
||||
|
||||
showVirtualKeyboard: function() {
|
||||
if (!Util.isTouchDevice) return;
|
||||
if (!isTouchDevice) return;
|
||||
|
||||
var input = document.getElementById('noVNC_keyboardinput');
|
||||
|
||||
|
@ -1443,7 +1446,7 @@ var UI;
|
|||
},
|
||||
|
||||
hideVirtualKeyboard: function() {
|
||||
if (!Util.isTouchDevice) return;
|
||||
if (!isTouchDevice) return;
|
||||
|
||||
var input = document.getElementById('noVNC_keyboardinput');
|
||||
|
||||
|
@ -1733,23 +1736,23 @@ var UI;
|
|||
* /MISC
|
||||
* ==============
|
||||
*/
|
||||
};
|
||||
};
|
||||
|
||||
// Set up translations
|
||||
var LINGUAS = ["de", "el", "nl", "sv"];
|
||||
Util.Localisation.setup(LINGUAS);
|
||||
if (Util.Localisation.language !== "en" && Util.Localisation.dictionary === undefined) {
|
||||
WebUtil.fetchJSON('app/locale/' + Util.Localisation.language + '.json', function (translations) {
|
||||
Util.Localisation.dictionary = translations;
|
||||
// Set up translations
|
||||
var LINGUAS = ["de", "el", "nl", "sv"];
|
||||
l10n.setup(LINGUAS);
|
||||
if (l10n.language !== "en" && l10n.dictionary === undefined) {
|
||||
WebUtil.fetchJSON('app/locale/' + l10n.language + '.json', function (translations) {
|
||||
l10n.dictionary = translations;
|
||||
|
||||
// wait for translations to load before loading the UI
|
||||
UI.load();
|
||||
UI.prime();
|
||||
}, function (err) {
|
||||
throw err;
|
||||
});
|
||||
} else {
|
||||
UI.load();
|
||||
}
|
||||
})();
|
||||
} else {
|
||||
UI.prime();
|
||||
}
|
||||
|
||||
|
||||
export default UI;
|
||||
|
|
|
@ -10,31 +10,21 @@
|
|||
/*jslint bitwise: false, white: false, browser: true, devel: true */
|
||||
/*global Util, window, document */
|
||||
|
||||
import Util from "../core/util.js";
|
||||
|
||||
// Globals defined here
|
||||
var WebUtil = {};
|
||||
|
||||
/*
|
||||
* ------------------------------------------------------
|
||||
* Namespaced in WebUtil
|
||||
* ------------------------------------------------------
|
||||
*/
|
||||
import { init_logging as main_init_logging } from '../core/util/logging.js';
|
||||
|
||||
// init log level reading the logging HTTP param
|
||||
WebUtil.init_logging = function (level) {
|
||||
export function init_logging (level) {
|
||||
"use strict";
|
||||
if (typeof level !== "undefined") {
|
||||
Util._log_level = level;
|
||||
main_init_logging(level);
|
||||
} else {
|
||||
var param = document.location.href.match(/logging=([A-Za-z0-9\._\-]*)/);
|
||||
Util._log_level = (param || ['', Util._log_level])[1];
|
||||
main_init_logging(param || undefined);
|
||||
}
|
||||
Util.init_logging();
|
||||
};
|
||||
|
||||
// Read a query string variable
|
||||
WebUtil.getQueryVar = function (name, defVal) {
|
||||
export function getQueryVar (name, defVal) {
|
||||
"use strict";
|
||||
var re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
|
||||
match = document.location.href.match(re);
|
||||
|
@ -47,7 +37,7 @@ WebUtil.getQueryVar = function (name, defVal) {
|
|||
};
|
||||
|
||||
// Read a hash fragment variable
|
||||
WebUtil.getHashVar = function (name, defVal) {
|
||||
export function getHashVar (name, defVal) {
|
||||
"use strict";
|
||||
var re = new RegExp('.*[&#]' + name + '=([^&]*)'),
|
||||
match = document.location.hash.match(re);
|
||||
|
@ -61,11 +51,11 @@ WebUtil.getHashVar = function (name, defVal) {
|
|||
|
||||
// Read a variable from the fragment or the query string
|
||||
// Fragment takes precedence
|
||||
WebUtil.getConfigVar = function (name, defVal) {
|
||||
export function getConfigVar (name, defVal) {
|
||||
"use strict";
|
||||
var val = WebUtil.getHashVar(name);
|
||||
var val = getHashVar(name);
|
||||
if (val === null) {
|
||||
val = WebUtil.getQueryVar(name, defVal);
|
||||
val = getQueryVar(name, defVal);
|
||||
}
|
||||
return val;
|
||||
};
|
||||
|
@ -75,7 +65,7 @@ WebUtil.getConfigVar = function (name, defVal) {
|
|||
*/
|
||||
|
||||
// No days means only for this browser session
|
||||
WebUtil.createCookie = function (name, value, days) {
|
||||
export function createCookie (name, value, days) {
|
||||
"use strict";
|
||||
var date, expires;
|
||||
if (days) {
|
||||
|
@ -95,7 +85,7 @@ WebUtil.createCookie = function (name, value, days) {
|
|||
document.cookie = name + "=" + value + expires + "; path=/" + secure;
|
||||
};
|
||||
|
||||
WebUtil.readCookie = function (name, defaultValue) {
|
||||
export function readCookie (name, defaultValue) {
|
||||
"use strict";
|
||||
var nameEQ = name + "=",
|
||||
ca = document.cookie.split(';');
|
||||
|
@ -108,22 +98,24 @@ WebUtil.readCookie = function (name, defaultValue) {
|
|||
return (typeof defaultValue !== 'undefined') ? defaultValue : null;
|
||||
};
|
||||
|
||||
WebUtil.eraseCookie = function (name) {
|
||||
export function eraseCookie (name) {
|
||||
"use strict";
|
||||
WebUtil.createCookie(name, "", -1);
|
||||
createCookie(name, "", -1);
|
||||
};
|
||||
|
||||
/*
|
||||
* Setting handling.
|
||||
*/
|
||||
|
||||
WebUtil.initSettings = function (callback /*, ...callbackArgs */) {
|
||||
var settings = {};
|
||||
|
||||
export function initSettings (callback /*, ...callbackArgs */) {
|
||||
"use strict";
|
||||
var callbackArgs = Array.prototype.slice.call(arguments, 1);
|
||||
if (window.chrome && window.chrome.storage) {
|
||||
window.chrome.storage.sync.get(function (cfg) {
|
||||
WebUtil.settings = cfg;
|
||||
console.log(WebUtil.settings);
|
||||
settings = cfg;
|
||||
console.log(settings);
|
||||
if (callback) {
|
||||
callback.apply(this, callbackArgs);
|
||||
}
|
||||
|
@ -137,24 +129,24 @@ WebUtil.initSettings = function (callback /*, ...callbackArgs */) {
|
|||
};
|
||||
|
||||
// No days means only for this browser session
|
||||
WebUtil.writeSetting = function (name, value) {
|
||||
export function writeSetting (name, value) {
|
||||
"use strict";
|
||||
if (window.chrome && window.chrome.storage) {
|
||||
//console.log("writeSetting:", name, value);
|
||||
if (WebUtil.settings[name] !== value) {
|
||||
WebUtil.settings[name] = value;
|
||||
window.chrome.storage.sync.set(WebUtil.settings);
|
||||
if (settings[name] !== value) {
|
||||
settings[name] = value;
|
||||
window.chrome.storage.sync.set(settings);
|
||||
}
|
||||
} else {
|
||||
localStorage.setItem(name, value);
|
||||
}
|
||||
};
|
||||
|
||||
WebUtil.readSetting = function (name, defaultValue) {
|
||||
export function readSetting (name, defaultValue) {
|
||||
"use strict";
|
||||
var value;
|
||||
if (window.chrome && window.chrome.storage) {
|
||||
value = WebUtil.settings[name];
|
||||
value = settings[name];
|
||||
} else {
|
||||
value = localStorage.getItem(name);
|
||||
}
|
||||
|
@ -168,17 +160,17 @@ WebUtil.readSetting = function (name, defaultValue) {
|
|||
}
|
||||
};
|
||||
|
||||
WebUtil.eraseSetting = function (name) {
|
||||
export function eraseSetting (name) {
|
||||
"use strict";
|
||||
if (window.chrome && window.chrome.storage) {
|
||||
window.chrome.storage.sync.remove(name);
|
||||
delete WebUtil.settings[name];
|
||||
delete settings[name];
|
||||
} else {
|
||||
localStorage.removeItem(name);
|
||||
}
|
||||
};
|
||||
|
||||
WebUtil.injectParamIfMissing = function (path, param, value) {
|
||||
export function injectParamIfMissing (path, param, value) {
|
||||
// force pretend that we're dealing with a relative path
|
||||
// (assume that we wanted an extra if we pass one in)
|
||||
path = "/" + path;
|
||||
|
@ -212,7 +204,7 @@ WebUtil.injectParamIfMissing = function (path, param, value) {
|
|||
// IE11 support or polyfill promises and fetch in IE11.
|
||||
// resolve will receive an object on success, while reject
|
||||
// will receive either an event or an error on failure.
|
||||
WebUtil.fetchJSON = function (path, resolve, reject) {
|
||||
export function fetchJSON(path, resolve, reject) {
|
||||
// NB: IE11 doesn't support JSON as a responseType
|
||||
const req = new XMLHttpRequest();
|
||||
req.open('GET', path);
|
||||
|
@ -240,6 +232,4 @@ WebUtil.fetchJSON = function (path, resolve, reject) {
|
|||
};
|
||||
|
||||
req.send();
|
||||
};
|
||||
|
||||
export default WebUtil;
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
/*jslint white: false */
|
||||
/*global console */
|
||||
|
||||
var Base64 = {
|
||||
export default {
|
||||
/* Convert data (an array of integers) to a Base64 string. */
|
||||
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
|
||||
base64Pad : '=',
|
||||
|
@ -15,7 +15,7 @@ var Base64 = {
|
|||
encode: function (data) {
|
||||
"use strict";
|
||||
var result = '';
|
||||
var toBase64Table = Base64.toBase64Table;
|
||||
var toBase64Table = this.toBase64Table;
|
||||
var length = data.length;
|
||||
var lengthpad = (length % 3);
|
||||
// Convert every three bytes to 4 ascii characters.
|
||||
|
@ -63,8 +63,8 @@ var Base64 = {
|
|||
decode: function (data, offset) {
|
||||
"use strict";
|
||||
offset = typeof(offset) !== 'undefined' ? offset : 0;
|
||||
var toBinaryTable = Base64.toBinaryTable;
|
||||
var base64Pad = Base64.base64Pad;
|
||||
var toBinaryTable = this.toBinaryTable;
|
||||
var base64Pad = this.base64Pad;
|
||||
var result, result_length;
|
||||
var leftbits = 0; // number of bits decoded, but yet to be appended
|
||||
var leftdata = 0; // bits decoded, but yet to be appended
|
||||
|
@ -111,5 +111,3 @@ var Base64 = {
|
|||
return result;
|
||||
}
|
||||
}; /* End of Base64 namespace */
|
||||
|
||||
export default Base64;
|
||||
|
|
|
@ -10,10 +10,11 @@
|
|||
/*jslint browser: true, white: false */
|
||||
/*global Util, Base64, changeCursor */
|
||||
|
||||
import Util from "./util.js";
|
||||
import { Engine, browserSupportsCursorURIs as cursorURIsSupported } from './util/browsers.js';
|
||||
import { set_defaults, make_properties } from './util/properties.js';
|
||||
import * as Log from './util/logging.js';
|
||||
import Base64 from "./base64.js";
|
||||
|
||||
|
||||
export default function Display(defaults) {
|
||||
this._drawCtx = null;
|
||||
this._c_forceCanvas = false;
|
||||
|
@ -31,7 +32,7 @@ export default function Display(defaults) {
|
|||
this._tile_x = 0;
|
||||
this._tile_y = 0;
|
||||
|
||||
Util.set_defaults(this, defaults, {
|
||||
set_defaults(this, defaults, {
|
||||
'true_color': true,
|
||||
'colourMap': [],
|
||||
'scale': 1.0,
|
||||
|
@ -40,7 +41,7 @@ export default function Display(defaults) {
|
|||
"onFlush": function () {},
|
||||
});
|
||||
|
||||
Util.Debug(">> Display.constructor");
|
||||
Log.Debug(">> Display.constructor");
|
||||
|
||||
// The visible canvas
|
||||
if (!this._target) {
|
||||
|
@ -68,11 +69,11 @@ export default function Display(defaults) {
|
|||
right: this._backbuffer.width,
|
||||
bottom: this._backbuffer.height };
|
||||
|
||||
Util.Debug("User Agent: " + navigator.userAgent);
|
||||
if (Util.Engine.gecko) { Util.Debug("Browser: gecko " + Util.Engine.gecko); }
|
||||
if (Util.Engine.webkit) { Util.Debug("Browser: webkit " + Util.Engine.webkit); }
|
||||
if (Util.Engine.trident) { Util.Debug("Browser: trident " + Util.Engine.trident); }
|
||||
if (Util.Engine.presto) { Util.Debug("Browser: presto " + Util.Engine.presto); }
|
||||
Log.Debug("User Agent: " + navigator.userAgent);
|
||||
if (Engine.gecko) { Log.Debug("Browser: gecko " + Engine.gecko); }
|
||||
if (Engine.webkit) { Log.Debug("Browser: webkit " + Engine.webkit); }
|
||||
if (Engine.trident) { Log.Debug("Browser: trident " + Engine.trident); }
|
||||
if (Engine.presto) { Log.Debug("Browser: presto " + Engine.presto); }
|
||||
|
||||
this.clear();
|
||||
|
||||
|
@ -84,32 +85,28 @@ export default function Display(defaults) {
|
|||
}
|
||||
|
||||
if (this._prefer_js === null) {
|
||||
Util.Info("Prefering javascript operations");
|
||||
Log.Info("Prefering javascript operations");
|
||||
this._prefer_js = true;
|
||||
}
|
||||
|
||||
// Determine browser support for setting the cursor via data URI scheme
|
||||
if (this._cursor_uri || this._cursor_uri === null ||
|
||||
this._cursor_uri === undefined) {
|
||||
this._cursor_uri = Util.browserSupportsCursorURIs();
|
||||
this._cursor_uri = cursorURIsSupported();
|
||||
}
|
||||
|
||||
Util.Debug("<< Display.constructor");
|
||||
Log.Debug("<< Display.constructor");
|
||||
};
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var SUPPORTS_IMAGEDATA_CONSTRUCTOR = false;
|
||||
try {
|
||||
var SUPPORTS_IMAGEDATA_CONSTRUCTOR = false;
|
||||
try {
|
||||
new ImageData(new Uint8ClampedArray(4), 1, 1);
|
||||
SUPPORTS_IMAGEDATA_CONSTRUCTOR = true;
|
||||
} catch (ex) {
|
||||
} catch (ex) {
|
||||
// ignore failure
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Display.prototype = {
|
||||
Display.prototype = {
|
||||
// Public methods
|
||||
viewportChangePos: function (deltaX, deltaY) {
|
||||
var vp = this._viewportLoc;
|
||||
|
@ -143,7 +140,7 @@ export default function Display(defaults) {
|
|||
if (deltaX === 0 && deltaY === 0) {
|
||||
return;
|
||||
}
|
||||
Util.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY);
|
||||
Log.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY);
|
||||
|
||||
vp.x += deltaX;
|
||||
vp.y += deltaY;
|
||||
|
@ -159,7 +156,7 @@ export default function Display(defaults) {
|
|||
typeof(width) === "undefined" ||
|
||||
typeof(height) === "undefined") {
|
||||
|
||||
Util.Debug("Setting viewport to full display region");
|
||||
Log.Debug("Setting viewport to full display region");
|
||||
width = this._fb_width;
|
||||
height = this._fb_height;
|
||||
}
|
||||
|
@ -527,7 +524,7 @@ export default function Display(defaults) {
|
|||
|
||||
changeCursor: function (pixels, mask, hotx, hoty, w, h) {
|
||||
if (this._cursor_uri === false) {
|
||||
Util.Warn("changeCursor called but no cursor data URI support");
|
||||
Log.Warn("changeCursor called but no cursor data URI support");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -740,9 +737,9 @@ export default function Display(defaults) {
|
|||
this._onFlush();
|
||||
}
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
Util.make_properties(Display, [
|
||||
make_properties(Display, [
|
||||
['target', 'wo', 'dom'], // Canvas element for rendering
|
||||
['context', 'ro', 'raw'], // Canvas 2D context for rendering (read-only)
|
||||
['logo', 'rw', 'raw'], // Logo to display when cleared: {"width": w, "height": h, "type": mime-type, "data": data}
|
||||
|
@ -759,10 +756,10 @@ export default function Display(defaults) {
|
|||
['cursor_uri', 'rw', 'raw'], // Can we render cursor using data URI
|
||||
|
||||
['onFlush', 'rw', 'func'], // onFlush(): A flush request has finished
|
||||
]);
|
||||
]);
|
||||
|
||||
// Class Methods
|
||||
Display.changeCursor = function (target, pixels, mask, hotx, hoty, w0, h0, cmap) {
|
||||
// Class Methods
|
||||
Display.changeCursor = function (target, pixels, mask, hotx, hoty, w0, h0, cmap) {
|
||||
var w = w0;
|
||||
var h = h0;
|
||||
if (h < w) {
|
||||
|
@ -867,5 +864,4 @@ export default function Display(defaults) {
|
|||
|
||||
var url = 'data:image/x-icon;base64,' + Base64.encode(cur);
|
||||
target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
|
||||
};
|
||||
})();
|
||||
};
|
||||
|
|
|
@ -36,4 +36,3 @@ export default function Inflate() {
|
|||
|
||||
inflateInit(this.strm, this.windowBits);
|
||||
};
|
||||
|
||||
|
|
|
@ -8,24 +8,21 @@
|
|||
/*jslint browser: true, white: false */
|
||||
/*global window, Util */
|
||||
|
||||
import Util from "../util.js";
|
||||
import KeyboardUtil from "./util.js";
|
||||
import * as Log from '../util/logging.js';
|
||||
import { isTouchDevice } from '../util/browsers.js'
|
||||
import { setCapture, releaseCapture, stopEvent, getPointerEvent } from '../util/events.js';
|
||||
import { set_defaults, make_properties } from '../util/properties.js';
|
||||
import * as KeyboardUtil from "./util.js";
|
||||
|
||||
//
|
||||
// Keyboard event handler
|
||||
//
|
||||
|
||||
export var Keyboard;
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
//
|
||||
// Keyboard event handler
|
||||
//
|
||||
|
||||
Keyboard = function (defaults) {
|
||||
const Keyboard = function (defaults) {
|
||||
this._keyDownList = []; // List of depressed keys
|
||||
// (even if they are happy)
|
||||
|
||||
Util.set_defaults(this, defaults, {
|
||||
set_defaults(this, defaults, {
|
||||
'target': document,
|
||||
'focused': true
|
||||
});
|
||||
|
@ -46,14 +43,14 @@ export var Keyboard;
|
|||
'keypress': this._handleKeyPress.bind(this),
|
||||
'blur': this._allKeysUp.bind(this)
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Keyboard.prototype = {
|
||||
Keyboard.prototype = {
|
||||
// private methods
|
||||
|
||||
_handleRfbEvent: function (e) {
|
||||
if (this._onKeyPress) {
|
||||
Util.Debug("onKeyPress " + (e.type == 'keydown' ? "down" : "up") +
|
||||
Log.Debug("onKeyPress " + (e.type == 'keydown' ? "down" : "up") +
|
||||
", keysym: " + e.keysym.keysym + "(" + e.keysym.keyname + ")");
|
||||
this._onKeyPress(e);
|
||||
}
|
||||
|
@ -72,7 +69,7 @@ export var Keyboard;
|
|||
|
||||
if (this._handler.keydown(e)) {
|
||||
// Suppress bubbling/default actions
|
||||
Util.stopEvent(e);
|
||||
stopEvent(e);
|
||||
} else {
|
||||
// Allow the event to bubble and become a keyPress event which
|
||||
// will have the character code translated
|
||||
|
@ -84,7 +81,7 @@ export var Keyboard;
|
|||
|
||||
if (this._handler.keypress(e)) {
|
||||
// Suppress bubbling/default actions
|
||||
Util.stopEvent(e);
|
||||
stopEvent(e);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -93,20 +90,20 @@ export var Keyboard;
|
|||
|
||||
if (this._handler.keyup(e)) {
|
||||
// Suppress bubbling/default actions
|
||||
Util.stopEvent(e);
|
||||
stopEvent(e);
|
||||
}
|
||||
},
|
||||
|
||||
_allKeysUp: function () {
|
||||
Util.Debug(">> Keyboard.allKeysUp");
|
||||
Log.Debug(">> Keyboard.allKeysUp");
|
||||
this._handler.releaseAll();
|
||||
Util.Debug("<< Keyboard.allKeysUp");
|
||||
Log.Debug("<< Keyboard.allKeysUp");
|
||||
},
|
||||
|
||||
// Public methods
|
||||
|
||||
grab: function () {
|
||||
//Util.Debug(">> Keyboard.grab");
|
||||
//Log.Debug(">> Keyboard.grab");
|
||||
var c = this._target;
|
||||
|
||||
c.addEventListener('keydown', this._eventHandlers.keydown);
|
||||
|
@ -116,11 +113,11 @@ export var Keyboard;
|
|||
// Release (key up) if window loses focus
|
||||
window.addEventListener('blur', this._eventHandlers.blur);
|
||||
|
||||
//Util.Debug("<< Keyboard.grab");
|
||||
//Log.Debug("<< Keyboard.grab");
|
||||
},
|
||||
|
||||
ungrab: function () {
|
||||
//Util.Debug(">> Keyboard.ungrab");
|
||||
//Log.Debug(">> Keyboard.ungrab");
|
||||
var c = this._target;
|
||||
|
||||
c.removeEventListener('keydown', this._eventHandlers.keydown);
|
||||
|
@ -131,33 +128,29 @@ export var Keyboard;
|
|||
// Release (key up) all keys that are in a down state
|
||||
this._allKeysUp();
|
||||
|
||||
//Util.Debug(">> Keyboard.ungrab");
|
||||
//Log.Debug(">> Keyboard.ungrab");
|
||||
},
|
||||
|
||||
sync: function (e) {
|
||||
this._handler.syncModifiers(e);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Util.make_properties(Keyboard, [
|
||||
make_properties(Keyboard, [
|
||||
['target', 'wo', 'dom'], // DOM element that captures keyboard input
|
||||
['focused', 'rw', 'bool'], // Capture and send key events
|
||||
|
||||
['onKeyPress', 'rw', 'func'] // Handler for key press/release
|
||||
]);
|
||||
})();
|
||||
]);
|
||||
|
||||
export var Mouse;
|
||||
|
||||
(function () {
|
||||
Mouse = function (defaults) {
|
||||
const Mouse = function (defaults) {
|
||||
this._mouseCaptured = false;
|
||||
|
||||
this._doubleClickTimer = null;
|
||||
this._lastTouchPos = null;
|
||||
|
||||
// Configuration attributes
|
||||
Util.set_defaults(this, defaults, {
|
||||
set_defaults(this, defaults, {
|
||||
'target': document,
|
||||
'focused': true,
|
||||
'touchButton': 1
|
||||
|
@ -170,13 +163,13 @@ export var Mouse;
|
|||
'mousewheel': this._handleMouseWheel.bind(this),
|
||||
'mousedisable': this._handleMouseDisable.bind(this)
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Mouse.prototype = {
|
||||
Mouse.prototype = {
|
||||
// private methods
|
||||
_captureMouse: function () {
|
||||
// capturing the mouse ensures we get the mouseup event
|
||||
Util.setCapture(this._target);
|
||||
setCapture(this._target);
|
||||
|
||||
// some browsers give us mouseup events regardless,
|
||||
// so if we never captured the mouse, we can disregard the event
|
||||
|
@ -184,7 +177,7 @@ export var Mouse;
|
|||
},
|
||||
|
||||
_releaseMouse: function () {
|
||||
Util.releaseCapture();
|
||||
releaseCapture();
|
||||
this._mouseCaptured = false;
|
||||
},
|
||||
|
||||
|
@ -243,11 +236,11 @@ export var Mouse;
|
|||
}
|
||||
|
||||
if (this._onMouseButton) {
|
||||
Util.Debug("onMouseButton " + (down ? "down" : "up") +
|
||||
Log.Debug("onMouseButton " + (down ? "down" : "up") +
|
||||
", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
|
||||
this._onMouseButton(pos.x, pos.y, down, bmask);
|
||||
}
|
||||
Util.stopEvent(e);
|
||||
stopEvent(e);
|
||||
},
|
||||
|
||||
_handleMouseDown: function (e) {
|
||||
|
@ -289,7 +282,7 @@ export var Mouse;
|
|||
}
|
||||
}
|
||||
|
||||
Util.stopEvent(e);
|
||||
stopEvent(e);
|
||||
},
|
||||
|
||||
_handleMouseMove: function (e) {
|
||||
|
@ -303,7 +296,7 @@ export var Mouse;
|
|||
if (this._onMouseMove) {
|
||||
this._onMouseMove(pos.x, pos.y);
|
||||
}
|
||||
Util.stopEvent(e);
|
||||
stopEvent(e);
|
||||
},
|
||||
|
||||
_handleMouseDisable: function (e) {
|
||||
|
@ -316,14 +309,13 @@ export var Mouse;
|
|||
* to listen on the document element instead.
|
||||
*/
|
||||
if (e.target == this._target) {
|
||||
//Util.Debug("mouse event disabled");
|
||||
Util.stopEvent(e);
|
||||
stopEvent(e);
|
||||
}
|
||||
},
|
||||
|
||||
// Return coordinates relative to target
|
||||
_getMousePosition: function(e) {
|
||||
e = Util.getPointerEvent(e);
|
||||
e = getPointerEvent(e);
|
||||
var bounds = this._target.getBoundingClientRect();
|
||||
var x, y;
|
||||
// Clip to target bounds
|
||||
|
@ -344,12 +336,11 @@ export var Mouse;
|
|||
return {x:x, y:y};
|
||||
},
|
||||
|
||||
|
||||
// Public methods
|
||||
grab: function () {
|
||||
var c = this._target;
|
||||
|
||||
if (Util.isTouchDevice) {
|
||||
if (isTouchDevice) {
|
||||
c.addEventListener('touchstart', this._eventHandlers.mousedown);
|
||||
window.addEventListener('touchend', this._eventHandlers.mouseup);
|
||||
c.addEventListener('touchend', this._eventHandlers.mouseup);
|
||||
|
@ -372,7 +363,7 @@ export var Mouse;
|
|||
ungrab: function () {
|
||||
var c = this._target;
|
||||
|
||||
if (Util.isTouchDevice) {
|
||||
if (isTouchDevice) {
|
||||
c.removeEventListener('touchstart', this._eventHandlers.mousedown);
|
||||
window.removeEventListener('touchend', this._eventHandlers.mouseup);
|
||||
c.removeEventListener('touchend', this._eventHandlers.mouseup);
|
||||
|
@ -388,9 +379,9 @@ export var Mouse;
|
|||
|
||||
c.removeEventListener('contextmenu', this._eventHandlers.mousedisable);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Util.make_properties(Mouse, [
|
||||
make_properties(Mouse, [
|
||||
['target', 'ro', 'dom'], // DOM element that captures mouse input
|
||||
['notify', 'ro', 'func'], // Function to call to notify whenever a mouse event is received
|
||||
['focused', 'rw', 'bool'], // Capture and send mouse clicks/movement
|
||||
|
@ -398,5 +389,6 @@ export var Mouse;
|
|||
['onMouseButton', 'rw', 'func'], // Handler for mouse button click/release
|
||||
['onMouseMove', 'rw', 'func'], // Handler for mouse movement
|
||||
['touchButton', 'rw', 'int'] // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
|
||||
]);
|
||||
})();
|
||||
]);
|
||||
|
||||
export { Keyboard, Mouse };
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
var KeyTable = {
|
||||
export default {
|
||||
XK_VoidSymbol: 0xffffff, /* Void symbol */
|
||||
|
||||
XK_BackSpace: 0xff08, /* Back space, back char */
|
||||
|
@ -378,5 +378,3 @@ var KeyTable = {
|
|||
XK_thorn: 0x00fe, /* U+00FE LATIN SMALL LETTER THORN */
|
||||
XK_ydiaeresis: 0x00ff, /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */
|
||||
};
|
||||
|
||||
export default KeyTable;
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,13 +1,7 @@
|
|||
import KeyTable from "./keysym.js";
|
||||
import keysyms from "./keysymdef.js";
|
||||
|
||||
|
||||
var KeyboardUtil = {};
|
||||
|
||||
(function() {
|
||||
"use strict";
|
||||
|
||||
function substituteCodepoint(cp) {
|
||||
export function substituteCodepoint(cp) {
|
||||
// Any Unicode code points which do not have corresponding keysym entries
|
||||
// can be swapped out for another code point by adding them to this table
|
||||
var substitutions = {
|
||||
|
@ -21,20 +15,20 @@ var KeyboardUtil = {};
|
|||
|
||||
var sub = substitutions[cp];
|
||||
return sub ? sub : cp;
|
||||
}
|
||||
}
|
||||
|
||||
function isMac() {
|
||||
function isMac() {
|
||||
return navigator && !!(/mac/i).exec(navigator.platform);
|
||||
}
|
||||
function isWindows() {
|
||||
}
|
||||
function isWindows() {
|
||||
return navigator && !!(/win/i).exec(navigator.platform);
|
||||
}
|
||||
function isLinux() {
|
||||
}
|
||||
function isLinux() {
|
||||
return navigator && !!(/linux/i).exec(navigator.platform);
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if a modifier which is not the specified char modifier (and is not shift) is down
|
||||
function hasShortcutModifier(charModifier, currentModifiers) {
|
||||
// Return true if a modifier which is not the specified char modifier (and is not shift) is down
|
||||
export function hasShortcutModifier(charModifier, currentModifiers) {
|
||||
var mods = {};
|
||||
for (var key in currentModifiers) {
|
||||
if (parseInt(key) !== KeyTable.XK_Shift_L) {
|
||||
|
@ -54,10 +48,10 @@ var KeyboardUtil = {};
|
|||
else {
|
||||
return sum > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if the specified char modifier is currently down
|
||||
function hasCharModifier(charModifier, currentModifiers) {
|
||||
// Return true if the specified char modifier is currently down
|
||||
export function hasCharModifier(charModifier, currentModifiers) {
|
||||
if (charModifier.length === 0) { return false; }
|
||||
|
||||
for (var i = 0; i < charModifier.length; ++i) {
|
||||
|
@ -66,11 +60,11 @@ var KeyboardUtil = {};
|
|||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Helper object tracking modifier key state
|
||||
// and generates fake key events to compensate if it gets out of sync
|
||||
function ModifierSync(charModifier) {
|
||||
// Helper object tracking modifier key state
|
||||
// and generates fake key events to compensate if it gets out of sync
|
||||
export function ModifierSync(charModifier) {
|
||||
if (!charModifier) {
|
||||
if (isMac()) {
|
||||
// on Mac, Option (AKA Alt) is used as a char modifier
|
||||
|
@ -152,11 +146,11 @@ var KeyboardUtil = {};
|
|||
// if a char modifier is down, return the keys it consists of, otherwise return null
|
||||
activeCharModifier: function() { return hasCharModifier(charModifier, state) ? charModifier : null; }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Get a key ID from a keyboard event
|
||||
// May be a string or an integer depending on the available properties
|
||||
function getKey(evt){
|
||||
// Get a key ID from a keyboard event
|
||||
// May be a string or an integer depending on the available properties
|
||||
export function getKey(evt){
|
||||
if ('keyCode' in evt && 'key' in evt) {
|
||||
return evt.key + ':' + evt.keyCode;
|
||||
}
|
||||
|
@ -166,11 +160,11 @@ var KeyboardUtil = {};
|
|||
else {
|
||||
return evt.key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the most reliable keysym value we can get from a key event
|
||||
// if char/charCode is available, prefer those, otherwise fall back to key/keyCode/which
|
||||
function getKeysym(evt){
|
||||
// Get the most reliable keysym value we can get from a key event
|
||||
// if char/charCode is available, prefer those, otherwise fall back to key/keyCode/which
|
||||
export function getKeysym(evt){
|
||||
var codepoint;
|
||||
if (evt.char && evt.char.length === 1) {
|
||||
codepoint = evt.char.charCodeAt();
|
||||
|
@ -196,11 +190,11 @@ var KeyboardUtil = {};
|
|||
return keysyms.lookup(keysymFromKeyCode(evt.which, evt.shiftKey));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Given a keycode, try to predict which keysym it might be.
|
||||
// If the keycode is unknown, null is returned.
|
||||
function keysymFromKeyCode(keycode, shiftPressed) {
|
||||
// Given a keycode, try to predict which keysym it might be.
|
||||
// If the keycode is unknown, null is returned.
|
||||
export function keysymFromKeyCode(keycode, shiftPressed) {
|
||||
if (typeof(keycode) !== 'number') {
|
||||
return null;
|
||||
}
|
||||
|
@ -231,11 +225,11 @@ var KeyboardUtil = {};
|
|||
}
|
||||
|
||||
return nonCharacterKey({keyCode: keycode});
|
||||
}
|
||||
}
|
||||
|
||||
// if the key is a known non-character key (any key which doesn't generate character data)
|
||||
// return its keysym value. Otherwise return null
|
||||
function nonCharacterKey(evt) {
|
||||
// if the key is a known non-character key (any key which doesn't generate character data)
|
||||
// return its keysym value. Otherwise return null
|
||||
export function nonCharacterKey(evt) {
|
||||
// evt.key not implemented yet
|
||||
if (!evt.keyCode) { return null; }
|
||||
var keycode = evt.keyCode;
|
||||
|
@ -275,19 +269,9 @@ var KeyboardUtil = {};
|
|||
case 93 : return KeyTable.XK_Menu; // also: Windows-Menu, Command on Mac
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KeyboardUtil.hasShortcutModifier = hasShortcutModifier;
|
||||
KeyboardUtil.hasCharModifier = hasCharModifier;
|
||||
KeyboardUtil.ModifierSync = ModifierSync;
|
||||
KeyboardUtil.getKey = getKey;
|
||||
KeyboardUtil.getKeysym = getKeysym;
|
||||
KeyboardUtil.keysymFromKeyCode = keysymFromKeyCode;
|
||||
KeyboardUtil.nonCharacterKey = nonCharacterKey;
|
||||
KeyboardUtil.substituteCodepoint = substituteCodepoint;
|
||||
})();
|
||||
|
||||
KeyboardUtil.QEMUKeyEventDecoder = function(modifierState, next) {
|
||||
export function QEMUKeyEventDecoder (modifierState, next) {
|
||||
"use strict";
|
||||
|
||||
function sendAll(evts) {
|
||||
|
@ -333,7 +317,7 @@ KeyboardUtil.QEMUKeyEventDecoder = function(modifierState, next) {
|
|||
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
|
||||
var isShift = evt.keyCode === 0x10 || evt.key === 'Shift';
|
||||
|
||||
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!KeyboardUtil.nonCharacterKey(evt));
|
||||
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!nonCharacterKey(evt));
|
||||
|
||||
next(result);
|
||||
return suppress;
|
||||
|
@ -357,7 +341,7 @@ KeyboardUtil.QEMUKeyEventDecoder = function(modifierState, next) {
|
|||
};
|
||||
};
|
||||
|
||||
KeyboardUtil.TrackQEMUKeyState = function(next) {
|
||||
export function TrackQEMUKeyState (next) {
|
||||
"use strict";
|
||||
var state = [];
|
||||
|
||||
|
@ -425,7 +409,7 @@ KeyboardUtil.TrackQEMUKeyState = function(next) {
|
|||
// - marks each event with an 'escape' property if a modifier was down which should be "escaped"
|
||||
// - generates a "stall" event in cases where it might be necessary to wait and see if a keypress event follows a keydown
|
||||
// This information is collected into an object which is passed to the next() function. (one call per event)
|
||||
KeyboardUtil.KeyEventDecoder = function(modifierState, next) {
|
||||
export function KeyEventDecoder (modifierState, next) {
|
||||
"use strict";
|
||||
function sendAll(evts) {
|
||||
for (var i = 0; i < evts.length; ++i) {
|
||||
|
@ -434,18 +418,18 @@ KeyboardUtil.KeyEventDecoder = function(modifierState, next) {
|
|||
}
|
||||
function process(evt, type) {
|
||||
var result = {type: type};
|
||||
var keyId = KeyboardUtil.getKey(evt);
|
||||
var keyId = getKey(evt);
|
||||
if (keyId) {
|
||||
result.keyId = keyId;
|
||||
}
|
||||
|
||||
var keysym = KeyboardUtil.getKeysym(evt);
|
||||
var keysym = getKeysym(evt);
|
||||
|
||||
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
|
||||
// Is this a case where we have to decide on the keysym right away, rather than waiting for the keypress?
|
||||
// "special" keys like enter, tab or backspace don't send keypress events,
|
||||
// and some browsers don't send keypresses at all if a modifier is down
|
||||
if (keysym && (type !== 'keydown' || KeyboardUtil.nonCharacterKey(evt) || hasModifier)) {
|
||||
if (keysym && (type !== 'keydown' || nonCharacterKey(evt) || hasModifier)) {
|
||||
result.keysym = keysym;
|
||||
}
|
||||
|
||||
|
@ -454,11 +438,11 @@ KeyboardUtil.KeyEventDecoder = function(modifierState, next) {
|
|||
// Should we prevent the browser from handling the event?
|
||||
// Doing so on a keydown (in most browsers) prevents keypress from being generated
|
||||
// so only do that if we have to.
|
||||
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!KeyboardUtil.nonCharacterKey(evt));
|
||||
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!nonCharacterKey(evt));
|
||||
|
||||
// If a char modifier is down on a keydown, we need to insert a stall,
|
||||
// so VerifyCharModifier knows to wait and see if a keypress is comnig
|
||||
var stall = type === 'keydown' && modifierState.activeCharModifier() && !KeyboardUtil.nonCharacterKey(evt);
|
||||
var stall = type === 'keydown' && modifierState.activeCharModifier() && !nonCharacterKey(evt);
|
||||
|
||||
// if a char modifier is pressed, get the keys it consists of (on Windows, AltGr is equivalent to Ctrl+Alt)
|
||||
var active = modifierState.activeCharModifier();
|
||||
|
@ -512,7 +496,7 @@ KeyboardUtil.KeyEventDecoder = function(modifierState, next) {
|
|||
// so when used with the '2' key, Ctrl-Alt counts as a char modifier (and should be escaped), but when used with 'D', it does not.
|
||||
// The only way we can distinguish these cases is to wait and see if a keypress event arrives
|
||||
// When we receive a "stall" event, wait a few ms before processing the next keydown. If a keypress has also arrived, merge the two
|
||||
KeyboardUtil.VerifyCharModifier = function(next) {
|
||||
export function VerifyCharModifier (next) {
|
||||
"use strict";
|
||||
var queue = [];
|
||||
var timer = null;
|
||||
|
@ -569,7 +553,7 @@ KeyboardUtil.VerifyCharModifier = function(next) {
|
|||
// in some cases, a single key may produce multiple keysyms, so the corresponding keyup event must release all of these chars
|
||||
// key repeat events should be merged into a single entry.
|
||||
// Because we can't always identify which entry a keydown or keyup event corresponds to, we sometimes have to guess
|
||||
KeyboardUtil.TrackKeyState = function(next) {
|
||||
export function TrackKeyState (next) {
|
||||
"use strict";
|
||||
var state = [];
|
||||
|
||||
|
@ -653,7 +637,7 @@ KeyboardUtil.TrackKeyState = function(next) {
|
|||
|
||||
// Handles "escaping" of modifiers: if a char modifier is used to produce a keysym (such as AltGr-2 to generate an @),
|
||||
// then the modifier must be "undone" before sending the @, and "redone" afterwards.
|
||||
KeyboardUtil.EscapeModifiers = function(next) {
|
||||
export function EscapeModifiers (next) {
|
||||
"use strict";
|
||||
return function(evt) {
|
||||
if (evt.type !== 'keydown' || evt.escape === undefined) {
|
||||
|
@ -674,5 +658,3 @@ KeyboardUtil.EscapeModifiers = function(next) {
|
|||
/* jshint shadow: false */
|
||||
};
|
||||
};
|
||||
|
||||
export default KeyboardUtil;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
var XtScancode = {
|
||||
export default {
|
||||
"Escape": 0x0001,
|
||||
"Digit1": 0x0002,
|
||||
"Digit2": 0x0003,
|
||||
|
@ -147,5 +147,3 @@ var XtScancode = {
|
|||
"LaunchMail": 0xE06C,
|
||||
"MediaSelect": 0xE06D,
|
||||
};
|
||||
|
||||
export default XtScancode
|
||||
|
|
219
core/rfb.js
219
core/rfb.js
|
@ -10,7 +10,10 @@
|
|||
* (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
|
||||
*/
|
||||
|
||||
import Util from "./util.js";
|
||||
import * as Log from './util/logging.js';
|
||||
import _ from './util/localization.js';
|
||||
import { decodeUTF8 } from './util/strings.js';
|
||||
import { set_defaults, make_properties } from './util/properties.js';
|
||||
import Display from "./display.js";
|
||||
import { Keyboard, Mouse } from "./input/devices.js";
|
||||
import Websock from "./websock.js";
|
||||
|
@ -143,7 +146,7 @@ export default function RFB(defaults) {
|
|||
this._qemuExtKeyEventSupported = false;
|
||||
|
||||
// set the default value on user-facing properties
|
||||
Util.set_defaults(this, defaults, {
|
||||
set_defaults(this, defaults, {
|
||||
'target': 'null', // VNC display rendering Canvas object
|
||||
'focusContainer': document, // DOM element that captures keyboard input
|
||||
'encrypt': false, // Use TLS/SSL/wss encryption
|
||||
|
@ -172,7 +175,7 @@ export default function RFB(defaults) {
|
|||
});
|
||||
|
||||
// main setup
|
||||
Util.Debug(">> RFB.constructor");
|
||||
Log.Debug(">> RFB.constructor");
|
||||
|
||||
// populate encHandlers with bound versions
|
||||
Object.keys(RFB.encodingHandlers).forEach(function (encName) {
|
||||
|
@ -192,7 +195,7 @@ export default function RFB(defaults) {
|
|||
this._display = new Display({target: this._target,
|
||||
onFlush: this._onFlush.bind(this)});
|
||||
} catch (exc) {
|
||||
Util.Error("Display exception: " + exc);
|
||||
Log.Error("Display exception: " + exc);
|
||||
throw exc;
|
||||
}
|
||||
|
||||
|
@ -210,13 +213,13 @@ export default function RFB(defaults) {
|
|||
if ((this._rfb_connection_state === 'connecting') &&
|
||||
(this._rfb_init_state === '')) {
|
||||
this._rfb_init_state = 'ProtocolVersion';
|
||||
Util.Debug("Starting VNC handshake");
|
||||
Log.Debug("Starting VNC handshake");
|
||||
} else {
|
||||
this._fail("Unexpected server connection");
|
||||
}
|
||||
}.bind(this));
|
||||
this._sock.on('close', function (e) {
|
||||
Util.Warn("WebSocket on-close event");
|
||||
Log.Warn("WebSocket on-close event");
|
||||
var msg = "";
|
||||
if (e.code) {
|
||||
msg = " (code: " + e.code;
|
||||
|
@ -249,22 +252,19 @@ export default function RFB(defaults) {
|
|||
this._sock.off('close');
|
||||
}.bind(this));
|
||||
this._sock.on('error', function (e) {
|
||||
Util.Warn("WebSocket on-error event");
|
||||
Log.Warn("WebSocket on-error event");
|
||||
});
|
||||
|
||||
this._init_vars();
|
||||
this._cleanup();
|
||||
|
||||
var rmode = this._display.get_render_mode();
|
||||
Util.Info("Using native WebSockets, render mode: " + rmode);
|
||||
Log.Info("Using native WebSockets, render mode: " + rmode);
|
||||
|
||||
Util.Debug("<< RFB.constructor");
|
||||
Log.Debug("<< RFB.constructor");
|
||||
};
|
||||
|
||||
(function() {
|
||||
var _ = Util.Localisation.get;
|
||||
|
||||
RFB.prototype = {
|
||||
RFB.prototype = {
|
||||
// Public methods
|
||||
connect: function (host, port, password, path) {
|
||||
this._rfb_host = host;
|
||||
|
@ -296,7 +296,7 @@ export default function RFB(defaults) {
|
|||
|
||||
sendCtrlAltDel: function () {
|
||||
if (this._rfb_connection_state !== 'connected' || this._view_only) { return false; }
|
||||
Util.Info("Sending Ctrl-Alt-Del");
|
||||
Log.Info("Sending Ctrl-Alt-Del");
|
||||
|
||||
RFB.messages.keyEvent(this._sock, KeyTable.XK_Control_L, 1);
|
||||
RFB.messages.keyEvent(this._sock, KeyTable.XK_Alt_L, 1);
|
||||
|
@ -309,7 +309,7 @@ export default function RFB(defaults) {
|
|||
|
||||
xvpOp: function (ver, op) {
|
||||
if (this._rfb_xvp_ver < ver) { return false; }
|
||||
Util.Info("Sending XVP operation " + op + " (version " + ver + ")");
|
||||
Log.Info("Sending XVP operation " + op + " (version " + ver + ")");
|
||||
this._sock.send_string("\xFA\x00" + String.fromCharCode(ver) + String.fromCharCode(op));
|
||||
return true;
|
||||
},
|
||||
|
@ -331,10 +331,10 @@ export default function RFB(defaults) {
|
|||
sendKey: function (keysym, down) {
|
||||
if (this._rfb_connection_state !== 'connected' || this._view_only) { return false; }
|
||||
if (typeof down !== 'undefined') {
|
||||
Util.Info("Sending keysym (" + (down ? "down" : "up") + "): " + keysym);
|
||||
Log.Info("Sending keysym (" + (down ? "down" : "up") + "): " + keysym);
|
||||
RFB.messages.keyEvent(this._sock, keysym, down ? 1 : 0);
|
||||
} else {
|
||||
Util.Info("Sending keysym (down + up): " + keysym);
|
||||
Log.Info("Sending keysym (down + up): " + keysym);
|
||||
RFB.messages.keyEvent(this._sock, keysym, 1);
|
||||
RFB.messages.keyEvent(this._sock, keysym, 0);
|
||||
}
|
||||
|
@ -342,9 +342,7 @@ export default function RFB(defaults) {
|
|||
},
|
||||
|
||||
clipboardPasteFrom: function (text) {
|
||||
if (this._rfb_connection_state !== 'connected' || this._view_only) {
|
||||
return;
|
||||
}
|
||||
if (this._rfb_connection_state !== 'connected' || this._view_only) { return; }
|
||||
RFB.messages.clientCutText(this._sock, text);
|
||||
},
|
||||
|
||||
|
@ -370,7 +368,7 @@ export default function RFB(defaults) {
|
|||
// Private methods
|
||||
|
||||
_connect: function () {
|
||||
Util.Debug(">> RFB.connect");
|
||||
Log.Debug(">> RFB.connect");
|
||||
this._init_vars();
|
||||
|
||||
var uri;
|
||||
|
@ -381,7 +379,7 @@ export default function RFB(defaults) {
|
|||
}
|
||||
|
||||
uri += '://' + this._rfb_host + ':' + this._rfb_port + '/' + this._rfb_path;
|
||||
Util.Info("connecting to " + uri);
|
||||
Log.Info("connecting to " + uri);
|
||||
|
||||
try {
|
||||
// WebSocket.onopen transitions to the RFB init states
|
||||
|
@ -394,15 +392,15 @@ export default function RFB(defaults) {
|
|||
}
|
||||
}
|
||||
|
||||
Util.Debug("<< RFB.connect");
|
||||
Log.Debug("<< RFB.connect");
|
||||
},
|
||||
|
||||
_disconnect: function () {
|
||||
Util.Debug(">> RFB.disconnect");
|
||||
Log.Debug(">> RFB.disconnect");
|
||||
this._cleanup();
|
||||
this._sock.close();
|
||||
this._print_stats();
|
||||
Util.Debug("<< RFB.disconnect");
|
||||
Log.Debug("<< RFB.disconnect");
|
||||
},
|
||||
|
||||
_init_vars: function () {
|
||||
|
@ -428,19 +426,19 @@ export default function RFB(defaults) {
|
|||
},
|
||||
|
||||
_print_stats: function () {
|
||||
Util.Info("Encoding stats for this connection:");
|
||||
Log.Info("Encoding stats for this connection:");
|
||||
var i, s;
|
||||
for (i = 0; i < this._encodings.length; i++) {
|
||||
s = this._encStats[this._encodings[i][1]];
|
||||
if (s[0] + s[1] > 0) {
|
||||
Util.Info(" " + this._encodings[i][0] + ": " + s[0] + " rects");
|
||||
Log.Info(" " + this._encodings[i][0] + ": " + s[0] + " rects");
|
||||
}
|
||||
}
|
||||
|
||||
Util.Info("Encoding stats since page load:");
|
||||
Log.Info("Encoding stats since page load:");
|
||||
for (i = 0; i < this._encodings.length; i++) {
|
||||
s = this._encStats[this._encodings[i][1]];
|
||||
Util.Info(" " + this._encodings[i][0] + ": " + s[1] + " rects");
|
||||
Log.Info(" " + this._encodings[i][0] + ": " + s[1] + " rects");
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -448,7 +446,7 @@ export default function RFB(defaults) {
|
|||
if (!this._view_only) { this._keyboard.ungrab(); }
|
||||
if (!this._view_only) { this._mouse.ungrab(); }
|
||||
this._display.defaultCursor();
|
||||
if (Util.get_logging() !== 'debug') {
|
||||
if (Log.get_logging() !== 'debug') {
|
||||
// Show noVNC logo on load and when disconnected, unless in
|
||||
// debug mode
|
||||
this._display.clear();
|
||||
|
@ -466,13 +464,13 @@ export default function RFB(defaults) {
|
|||
var oldstate = this._rfb_connection_state;
|
||||
|
||||
if (state === oldstate) {
|
||||
Util.Debug("Already in state '" + state + "', ignoring");
|
||||
Log.Debug("Already in state '" + state + "', ignoring");
|
||||
return;
|
||||
}
|
||||
|
||||
// The 'disconnected' state is permanent for each RFB object
|
||||
if (oldstate === 'disconnected') {
|
||||
Util.Error("Tried changing state of a disconnected RFB object");
|
||||
Log.Error("Tried changing state of a disconnected RFB object");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -480,7 +478,7 @@ export default function RFB(defaults) {
|
|||
switch (state) {
|
||||
case 'connected':
|
||||
if (oldstate !== 'connecting') {
|
||||
Util.Error("Bad transition to connected state, " +
|
||||
Log.Error("Bad transition to connected state, " +
|
||||
"previous connection state: " + oldstate);
|
||||
return;
|
||||
}
|
||||
|
@ -488,7 +486,7 @@ export default function RFB(defaults) {
|
|||
|
||||
case 'disconnected':
|
||||
if (oldstate !== 'disconnecting') {
|
||||
Util.Error("Bad transition to disconnected state, " +
|
||||
Log.Error("Bad transition to disconnected state, " +
|
||||
"previous connection state: " + oldstate);
|
||||
return;
|
||||
}
|
||||
|
@ -496,7 +494,7 @@ export default function RFB(defaults) {
|
|||
|
||||
case 'connecting':
|
||||
if (oldstate !== '') {
|
||||
Util.Error("Bad transition to connecting state, " +
|
||||
Log.Error("Bad transition to connecting state, " +
|
||||
"previous connection state: " + oldstate);
|
||||
return;
|
||||
}
|
||||
|
@ -504,14 +502,14 @@ export default function RFB(defaults) {
|
|||
|
||||
case 'disconnecting':
|
||||
if (oldstate !== 'connected' && oldstate !== 'connecting') {
|
||||
Util.Error("Bad transition to disconnecting state, " +
|
||||
Log.Error("Bad transition to disconnecting state, " +
|
||||
"previous connection state: " + oldstate);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
Util.Error("Unknown connection state: " + state);
|
||||
Log.Error("Unknown connection state: " + state);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -521,10 +519,10 @@ export default function RFB(defaults) {
|
|||
this._onUpdateState(this, state, oldstate);
|
||||
|
||||
var smsg = "New state '" + state + "', was '" + oldstate + "'.";
|
||||
Util.Debug(smsg);
|
||||
Log.Debug(smsg);
|
||||
|
||||
if (this._disconnTimer && state !== 'disconnecting') {
|
||||
Util.Debug("Clearing disconnect timer");
|
||||
Log.Debug("Clearing disconnect timer");
|
||||
clearTimeout(this._disconnTimer);
|
||||
this._disconnTimer = null;
|
||||
|
||||
|
@ -571,16 +569,16 @@ export default function RFB(defaults) {
|
|||
}
|
||||
switch (this._rfb_connection_state) {
|
||||
case 'disconnecting':
|
||||
Util.Error("Failed when disconnecting: " + fullmsg);
|
||||
Log.Error("Failed when disconnecting: " + fullmsg);
|
||||
break;
|
||||
case 'connected':
|
||||
Util.Error("Failed while connected: " + fullmsg);
|
||||
Log.Error("Failed while connected: " + fullmsg);
|
||||
break;
|
||||
case 'connecting':
|
||||
Util.Error("Failed when connecting: " + fullmsg);
|
||||
Log.Error("Failed when connecting: " + fullmsg);
|
||||
break;
|
||||
default:
|
||||
Util.Error("RFB failure: " + fullmsg);
|
||||
Log.Error("RFB failure: " + fullmsg);
|
||||
break;
|
||||
}
|
||||
this._rfb_disconnect_reason = msg; //This is sent to the UI
|
||||
|
@ -605,10 +603,10 @@ export default function RFB(defaults) {
|
|||
case 'normal':
|
||||
case 'warn':
|
||||
case 'error':
|
||||
Util.Debug("Notification[" + level + "]:" + msg);
|
||||
Log.Debug("Notification[" + level + "]:" + msg);
|
||||
break;
|
||||
default:
|
||||
Util.Error("Invalid notification level: " + level);
|
||||
Log.Error("Invalid notification level: " + level);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -621,13 +619,13 @@ export default function RFB(defaults) {
|
|||
|
||||
_handle_message: function () {
|
||||
if (this._sock.rQlen() === 0) {
|
||||
Util.Warn("handle_message called on an empty receive queue");
|
||||
Log.Warn("handle_message called on an empty receive queue");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (this._rfb_connection_state) {
|
||||
case 'disconnected':
|
||||
Util.Error("Got data while disconnected");
|
||||
Log.Error("Got data while disconnected");
|
||||
break;
|
||||
case 'connected':
|
||||
while (true) {
|
||||
|
@ -658,7 +656,7 @@ export default function RFB(defaults) {
|
|||
var keysym = keyevent.keysym;
|
||||
RFB.messages.QEMUExtendedKeyEvent(this._sock, keysym, down, scancode);
|
||||
} else {
|
||||
Util.Error('Unable to find a xt scancode for code = ' + keyevent.code);
|
||||
Log.Error('Unable to find a xt scancode for code = ' + keyevent.code);
|
||||
}
|
||||
} else {
|
||||
keysym = keyevent.keysym.keysym;
|
||||
|
@ -734,7 +732,7 @@ export default function RFB(defaults) {
|
|||
}
|
||||
|
||||
var sversion = this._sock.rQshiftStr(12).substr(4, 7);
|
||||
Util.Info("Server ProtocolVersion: " + sversion);
|
||||
Log.Info("Server ProtocolVersion: " + sversion);
|
||||
var is_repeater = 0;
|
||||
switch (sversion) {
|
||||
case "000.000": // UltraVNC repeater
|
||||
|
@ -775,12 +773,23 @@ export default function RFB(defaults) {
|
|||
var cversion = "00" + parseInt(this._rfb_version, 10) +
|
||||
".00" + ((this._rfb_version * 10) % 10);
|
||||
this._sock.send_string("RFB " + cversion + "\n");
|
||||
Util.Debug('Sent ProtocolVersion: ' + cversion);
|
||||
Log.Debug('Sent ProtocolVersion: ' + cversion);
|
||||
|
||||
this._rfb_init_state = 'Security';
|
||||
},
|
||||
|
||||
_negotiate_security: function () {
|
||||
// Polyfill since IE and PhantomJS doesn't have
|
||||
// TypedArray.includes()
|
||||
function includes(item, array) {
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
if (array[i] === item) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this._rfb_version >= 3.7) {
|
||||
// Server sends supported list, client decides
|
||||
var num_types = this._sock.rQshift8();
|
||||
|
@ -794,18 +803,7 @@ export default function RFB(defaults) {
|
|||
}
|
||||
|
||||
var types = this._sock.rQshiftBytes(num_types);
|
||||
Util.Debug("Server security types: " + types);
|
||||
|
||||
// Polyfill since IE and PhantomJS doesn't have
|
||||
// TypedArray.includes()
|
||||
function includes(item, array) {
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
if (array[i] === item) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
Log.Debug("Server security types: " + types);
|
||||
|
||||
// Look for each auth in preferred order
|
||||
this._rfb_auth_scheme = 0;
|
||||
|
@ -830,7 +828,7 @@ export default function RFB(defaults) {
|
|||
}
|
||||
|
||||
this._rfb_init_state = 'Authentication';
|
||||
Util.Debug('Authenticating using scheme: ' + this._rfb_auth_scheme);
|
||||
Log.Debug('Authenticating using scheme: ' + this._rfb_auth_scheme);
|
||||
|
||||
return this._init_msg(); // jump to authentication
|
||||
},
|
||||
|
@ -999,7 +997,7 @@ export default function RFB(defaults) {
|
|||
switch (this._sock.rQshift32()) {
|
||||
case 0: // OK
|
||||
this._rfb_init_state = 'ClientInitialisation';
|
||||
Util.Debug('Authentication OK');
|
||||
Log.Debug('Authentication OK');
|
||||
return this._init_msg();
|
||||
case 1: // failed
|
||||
if (this._rfb_version >= 3.8) {
|
||||
|
@ -1047,7 +1045,7 @@ export default function RFB(defaults) {
|
|||
/* Connection name/title */
|
||||
var name_length = this._sock.rQshift32();
|
||||
if (this._sock.rQwait('server init name', name_length, 24)) { return false; }
|
||||
this._fb_name = Util.decodeUTF8(this._sock.rQshiftStr(name_length));
|
||||
this._fb_name = decodeUTF8(this._sock.rQshiftStr(name_length));
|
||||
|
||||
if (this._rfb_tightvnc) {
|
||||
if (this._sock.rQwait('TightVNC extended server init header', 8, 24 + name_length)) { return false; }
|
||||
|
@ -1075,7 +1073,7 @@ export default function RFB(defaults) {
|
|||
|
||||
// NB(directxman12): these are down here so that we don't run them multiple times
|
||||
// if we backtrack
|
||||
Util.Info("Screen: " + this._fb_width + "x" + this._fb_height +
|
||||
Log.Info("Screen: " + this._fb_width + "x" + this._fb_height +
|
||||
", bpp: " + bpp + ", depth: " + depth +
|
||||
", big_endian: " + big_endian +
|
||||
", true_color: " + true_color +
|
||||
|
@ -1087,22 +1085,22 @@ export default function RFB(defaults) {
|
|||
", blue_shift: " + blue_shift);
|
||||
|
||||
if (big_endian !== 0) {
|
||||
Util.Warn("Server native endian is not little endian");
|
||||
Log.Warn("Server native endian is not little endian");
|
||||
}
|
||||
|
||||
if (red_shift !== 16) {
|
||||
Util.Warn("Server native red-shift is not 16");
|
||||
Log.Warn("Server native red-shift is not 16");
|
||||
}
|
||||
|
||||
if (blue_shift !== 0) {
|
||||
Util.Warn("Server native blue-shift is not 0");
|
||||
Log.Warn("Server native blue-shift is not 0");
|
||||
}
|
||||
|
||||
// we're past the point where we could backtrack, so it's safe to call this
|
||||
this._onDesktopName(this, this._fb_name);
|
||||
|
||||
if (this._true_color && this._fb_name === "Intel(r) AMT KVM") {
|
||||
Util.Warn("Intel AMT KVM only supports 8/16 bit depths. Disabling true color");
|
||||
Log.Warn("Intel AMT KVM only supports 8/16 bit depths. Disabling true color");
|
||||
this._true_color = false;
|
||||
}
|
||||
|
||||
|
@ -1169,7 +1167,7 @@ export default function RFB(defaults) {
|
|||
},
|
||||
|
||||
_handle_set_colour_map_msg: function () {
|
||||
Util.Debug("SetColorMapEntries");
|
||||
Log.Debug("SetColorMapEntries");
|
||||
this._sock.rQskip8(); // Padding
|
||||
|
||||
var first_colour = this._sock.rQshift16();
|
||||
|
@ -1182,14 +1180,14 @@ export default function RFB(defaults) {
|
|||
var blue = parseInt(this._sock.rQshift16() / 256, 10);
|
||||
this._display.set_colourMap([blue, green, red], first_colour + c);
|
||||
}
|
||||
Util.Debug("colourMap: " + this._display.get_colourMap());
|
||||
Util.Info("Registered " + num_colours + " colourMap entries");
|
||||
Log.Debug("colourMap: " + this._display.get_colourMap());
|
||||
Log.Info("Registered " + num_colours + " colourMap entries");
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
_handle_server_cut_text: function () {
|
||||
Util.Debug("ServerCutText");
|
||||
Log.Debug("ServerCutText");
|
||||
if (this._view_only) { return true; }
|
||||
|
||||
if (this._sock.rQwait("ServerCutText header", 7, 1)) { return false; }
|
||||
|
@ -1212,7 +1210,7 @@ export default function RFB(defaults) {
|
|||
if (this._sock.rQwait("ServerFence payload", length, 9)) { return false; }
|
||||
|
||||
if (length > 64) {
|
||||
Util.Warn("Bad payload length (" + length + ") in fence response");
|
||||
Log.Warn("Bad payload length (" + length + ") in fence response");
|
||||
length = 64;
|
||||
}
|
||||
|
||||
|
@ -1254,12 +1252,12 @@ export default function RFB(defaults) {
|
|||
|
||||
switch (xvp_msg) {
|
||||
case 0: // XVP_FAIL
|
||||
Util.Error("Operation Failed");
|
||||
Log.Error("Operation Failed");
|
||||
this._notification("XVP Operation Failed", 'error');
|
||||
break;
|
||||
case 1: // XVP_INIT
|
||||
this._rfb_xvp_ver = xvp_ver;
|
||||
Util.Info("XVP extensions enabled (version " + this._rfb_xvp_ver + ")");
|
||||
Log.Info("XVP extensions enabled (version " + this._rfb_xvp_ver + ")");
|
||||
this._onXvpInit(this._rfb_xvp_ver);
|
||||
break;
|
||||
default:
|
||||
|
@ -1293,7 +1291,7 @@ export default function RFB(defaults) {
|
|||
return this._handle_set_colour_map_msg();
|
||||
|
||||
case 2: // Bell
|
||||
Util.Debug("Bell");
|
||||
Log.Debug("Bell");
|
||||
this._onBell(this);
|
||||
return true;
|
||||
|
||||
|
@ -1307,7 +1305,7 @@ export default function RFB(defaults) {
|
|||
if (first) {
|
||||
this._enabledContinuousUpdates = true;
|
||||
this._updateContinuousUpdates();
|
||||
Util.Info("Enabling continuous updates.");
|
||||
Log.Info("Enabling continuous updates.");
|
||||
} else {
|
||||
// FIXME: We need to send a framebufferupdaterequest here
|
||||
// if we add support for turning off continuous updates
|
||||
|
@ -1322,7 +1320,7 @@ export default function RFB(defaults) {
|
|||
|
||||
default:
|
||||
this._fail("Unexpected server message", "Type:" + msg_type);
|
||||
Util.Debug("sock.rQslice(0, 30): " + this._sock.rQslice(0, 30));
|
||||
Log.Debug("sock.rQslice(0, 30): " + this._sock.rQslice(0, 30));
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
@ -1347,7 +1345,7 @@ export default function RFB(defaults) {
|
|||
this._timing.cur_fbu = 0;
|
||||
if (this._timing.fbu_rt_start > 0) {
|
||||
now = (new Date()).getTime();
|
||||
Util.Info("First FBU latency: " + (now - this._timing.fbu_rt_start));
|
||||
Log.Info("First FBU latency: " + (now - this._timing.fbu_rt_start));
|
||||
}
|
||||
|
||||
// Make sure the previous frame is fully rendered first
|
||||
|
@ -1407,7 +1405,7 @@ export default function RFB(defaults) {
|
|||
this._timing.fbu_rt_start > 0) {
|
||||
this._timing.full_fbu_total += this._timing.cur_fbu;
|
||||
this._timing.full_fbu_cnt++;
|
||||
Util.Info("Timing of full FBU, curr: " +
|
||||
Log.Info("Timing of full FBU, curr: " +
|
||||
this._timing.cur_fbu + ", total: " +
|
||||
this._timing.full_fbu_total + ", cnt: " +
|
||||
this._timing.full_fbu_cnt + ", avg: " +
|
||||
|
@ -1418,7 +1416,7 @@ export default function RFB(defaults) {
|
|||
var fbu_rt_diff = now - this._timing.fbu_rt_start;
|
||||
this._timing.fbu_rt_total += fbu_rt_diff;
|
||||
this._timing.fbu_rt_cnt++;
|
||||
Util.Info("full FBU round-trip, cur: " +
|
||||
Log.Info("full FBU round-trip, cur: " +
|
||||
fbu_rt_diff + ", total: " +
|
||||
this._timing.fbu_rt_total + ", cnt: " +
|
||||
this._timing.fbu_rt_cnt + ", avg: " +
|
||||
|
@ -1447,9 +1445,9 @@ export default function RFB(defaults) {
|
|||
RFB.messages.enableContinuousUpdates(this._sock, true, 0, 0,
|
||||
this._fb_width, this._fb_height);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Util.make_properties(RFB, [
|
||||
make_properties(RFB, [
|
||||
['target', 'wo', 'dom'], // VNC display rendering Canvas object
|
||||
['focusContainer', 'wo', 'dom'], // DOM element that captures keyboard input
|
||||
['encrypt', 'rw', 'bool'], // Use TLS/SSL/wss encryption
|
||||
|
@ -1475,9 +1473,9 @@ export default function RFB(defaults) {
|
|||
['onFBResize', 'rw', 'func'], // onFBResize(rfb, width, height): frame buffer resized
|
||||
['onDesktopName', 'rw', 'func'], // onDesktopName(rfb, name): desktop name received
|
||||
['onXvpInit', 'rw', 'func'] // onXvpInit(version): XVP extensions active for this connection
|
||||
]);
|
||||
]);
|
||||
|
||||
RFB.prototype.set_local_cursor = function (cursor) {
|
||||
RFB.prototype.set_local_cursor = function (cursor) {
|
||||
if (!cursor || (cursor in {'0': 1, 'no': 1, 'false': 1})) {
|
||||
this._local_cursor = false;
|
||||
this._display.disableLocalCursor(); //Only show server-side cursor
|
||||
|
@ -1485,7 +1483,7 @@ export default function RFB(defaults) {
|
|||
if (this._display.get_cursor_uri()) {
|
||||
this._local_cursor = true;
|
||||
} else {
|
||||
Util.Warn("Browser does not support local cursor");
|
||||
Log.Warn("Browser does not support local cursor");
|
||||
this._display.disableLocalCursor();
|
||||
}
|
||||
}
|
||||
|
@ -1495,9 +1493,9 @@ export default function RFB(defaults) {
|
|||
RFB.messages.clientEncodings(this._sock, this._encodings, cursor,
|
||||
this._true_color);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
RFB.prototype.set_view_only = function (view_only) {
|
||||
RFB.prototype.set_view_only = function (view_only) {
|
||||
this._view_only = view_only;
|
||||
|
||||
if (this._rfb_connection_state === "connecting" ||
|
||||
|
@ -1510,14 +1508,14 @@ export default function RFB(defaults) {
|
|||
this._mouse.grab();
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
RFB.prototype.get_display = function () { return this._display; };
|
||||
RFB.prototype.get_keyboard = function () { return this._keyboard; };
|
||||
RFB.prototype.get_mouse = function () { return this._mouse; };
|
||||
RFB.prototype.get_display = function () { return this._display; };
|
||||
RFB.prototype.get_keyboard = function () { return this._keyboard; };
|
||||
RFB.prototype.get_mouse = function () { return this._mouse; };
|
||||
|
||||
// Class Methods
|
||||
RFB.messages = {
|
||||
// Class Methods
|
||||
RFB.messages = {
|
||||
keyEvent: function (sock, keysym, down) {
|
||||
var buff = sock._sQ;
|
||||
var offset = sock._sQlen;
|
||||
|
@ -1748,10 +1746,10 @@ export default function RFB(defaults) {
|
|||
var i, j = offset + 4, cnt = 0;
|
||||
for (i = 0; i < encodings.length; i++) {
|
||||
if (encodings[i][0] === "Cursor" && !local_cursor) {
|
||||
Util.Debug("Skipping Cursor pseudo-encoding");
|
||||
Log.Debug("Skipping Cursor pseudo-encoding");
|
||||
} else if (encodings[i][0] === "TIGHT" && !true_color) {
|
||||
// TODO: remove this when we have tight+non-true-color
|
||||
Util.Warn("Skipping tight as it is only supported with true color");
|
||||
Log.Warn("Skipping tight as it is only supported with true color");
|
||||
} else {
|
||||
var enc = encodings[i][1];
|
||||
buff[j] = enc >> 24;
|
||||
|
@ -1796,17 +1794,17 @@ export default function RFB(defaults) {
|
|||
sock._sQlen += 10;
|
||||
sock.flush();
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
RFB.genDES = function (password, challenge) {
|
||||
RFB.genDES = function (password, challenge) {
|
||||
var passwd = [];
|
||||
for (var i = 0; i < password.length; i++) {
|
||||
passwd.push(password.charCodeAt(i));
|
||||
}
|
||||
return (new DES(passwd)).encrypt(challenge);
|
||||
};
|
||||
};
|
||||
|
||||
RFB.encodingHandlers = {
|
||||
RFB.encodingHandlers = {
|
||||
RAW: function () {
|
||||
if (this._FBU.lines === 0) {
|
||||
this._FBU.lines = this._FBU.height;
|
||||
|
@ -1936,7 +1934,7 @@ export default function RFB(defaults) {
|
|||
if (this._FBU.subencoding === 0) {
|
||||
if (this._FBU.lastsubencoding & 0x01) {
|
||||
// Weird: ignore blanks are RAW
|
||||
Util.Debug(" Ignoring blank after RAW");
|
||||
Log.Debug(" Ignoring blank after RAW");
|
||||
} else {
|
||||
this._display.fillRect(x, y, w, h, this._FBU.background);
|
||||
}
|
||||
|
@ -2048,14 +2046,14 @@ export default function RFB(defaults) {
|
|||
for (var i = 0; i < 4; i++) {
|
||||
if ((resetStreams >> i) & 1) {
|
||||
this._FBU.zlibs[i].reset();
|
||||
Util.Info("Reset zlib stream " + i);
|
||||
Log.Info("Reset zlib stream " + i);
|
||||
}
|
||||
}
|
||||
|
||||
//var uncompressed = this._FBU.zlibs[streamId].uncompress(data, 0);
|
||||
var uncompressed = this._FBU.zlibs[streamId].inflate(data, true, expected);
|
||||
/*if (uncompressed.status !== 0) {
|
||||
Util.Error("Invalid data in zlib stream");
|
||||
Log.Error("Invalid data in zlib stream");
|
||||
}*/
|
||||
|
||||
//return uncompressed.data;
|
||||
|
@ -2431,7 +2429,7 @@ export default function RFB(defaults) {
|
|||
},
|
||||
|
||||
Cursor: function () {
|
||||
Util.Debug(">> set_cursor");
|
||||
Log.Debug(">> set_cursor");
|
||||
var x = this._FBU.x; // hotspot-x
|
||||
var y = this._FBU.y; // hotspot-y
|
||||
var w = this._FBU.width;
|
||||
|
@ -2450,7 +2448,7 @@ export default function RFB(defaults) {
|
|||
this._FBU.bytes = 0;
|
||||
this._FBU.rects--;
|
||||
|
||||
Util.Debug("<< set_cursor");
|
||||
Log.Debug("<< set_cursor");
|
||||
return true;
|
||||
},
|
||||
|
||||
|
@ -2465,11 +2463,10 @@ export default function RFB(defaults) {
|
|||
},
|
||||
|
||||
JPEG_quality_lo: function () {
|
||||
Util.Error("Server sent jpeg_quality pseudo-encoding");
|
||||
Log.Error("Server sent jpeg_quality pseudo-encoding");
|
||||
},
|
||||
|
||||
compress_lo: function () {
|
||||
Util.Error("Server sent compress level pseudo-encoding");
|
||||
Log.Error("Server sent compress level pseudo-encoding");
|
||||
}
|
||||
};
|
||||
})();
|
||||
};
|
||||
|
|
624
core/util.js
624
core/util.js
|
@ -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;
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
import * as Log from './logging.js';
|
||||
|
||||
// Set browser engine versions. Based on mootools.
|
||||
const Features = {xpath: !!(document.evaluate), query: !!(document.querySelector)};
|
||||
|
||||
// 'presto': (function () { return (!window.opera) ? false : true; }()),
|
||||
var detectPresto = function () {
|
||||
return !!window.opera;
|
||||
};
|
||||
|
||||
// 'trident': (function () { return (!window.ActiveXObject) ? false : ((window.XMLHttpRequest) ? ((document.querySelectorAll) ? 6 : 5) : 4);
|
||||
var detectTrident = function () {
|
||||
if (!window.ActiveXObject) {
|
||||
return false;
|
||||
} else {
|
||||
if (window.XMLHttpRequest) {
|
||||
return (document.querySelectorAll) ? 6 : 5;
|
||||
} else {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 'webkit': (function () { try { return (navigator.taintEnabled) ? false : ((Features.xpath) ? ((Features.query) ? 525 : 420) : 419); } catch (e) { return false; } }()),
|
||||
var detectInitialWebkit = function () {
|
||||
try {
|
||||
if (navigator.taintEnabled) {
|
||||
return false;
|
||||
} else {
|
||||
if (Features.xpath) {
|
||||
return (Features.query) ? 525 : 420;
|
||||
} else {
|
||||
return 419;
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
var detectActualWebkit = function (initial_ver) {
|
||||
var re = /WebKit\/([0-9\.]*) /;
|
||||
var str_ver = (navigator.userAgent.match(re) || ['', initial_ver])[1];
|
||||
return parseFloat(str_ver, 10);
|
||||
};
|
||||
|
||||
// 'gecko': (function () { return (!document.getBoxObjectFor && window.mozInnerScreenX == null) ? false : ((document.getElementsByClassName) ? 19ssName) ? 19 : 18 : 18); }())
|
||||
var detectGecko = function () {
|
||||
/* jshint -W041 */
|
||||
if (!document.getBoxObjectFor && window.mozInnerScreenX == null) {
|
||||
return false;
|
||||
} else {
|
||||
return (document.getElementsByClassName) ? 19 : 18;
|
||||
}
|
||||
/* jshint +W041 */
|
||||
};
|
||||
|
||||
const isWebkitInitial = detectInitialWebkit();
|
||||
|
||||
export const Engine = {
|
||||
// Version detection break in Opera 11.60 (errors on arguments.callee.caller reference)
|
||||
//'presto': (function() {
|
||||
// return (!window.opera) ? false : ((arguments.callee.caller) ? 960 : ((document.getElementsByClassName) ? 950 : 925)); }()),
|
||||
'presto': detectPresto(),
|
||||
'trident': detectTrident(),
|
||||
'webkit': isWebkitInitial ? detectActualWebkit(isWebkitInitial) : false,
|
||||
'gecko': detectGecko()
|
||||
};
|
||||
|
||||
// Touch detection
|
||||
export var isTouchDevice = ('ontouchstart' in document.documentElement) ||
|
||||
// requried for Chrome debugger
|
||||
(document.ontouchstart !== undefined) ||
|
||||
// required for MS Surface
|
||||
(navigator.maxTouchPoints > 0) ||
|
||||
(navigator.msMaxTouchPoints > 0);
|
||||
window.addEventListener('touchstart', function onFirstTouch() {
|
||||
isTouchDevice = true;
|
||||
window.removeEventListener('touchstart', onFirstTouch, false);
|
||||
}, false);
|
||||
|
||||
var _cursor_uris_supported = null;
|
||||
|
||||
export function browserSupportsCursorURIs () {
|
||||
if (_cursor_uris_supported === null) {
|
||||
try {
|
||||
var target = document.createElement('canvas');
|
||||
target.style.cursor = 'url("data:image/x-icon;base64,AAACAAEACAgAAAIAAgA4AQAAFgAAACgAAAAIAAAAEAAAAAEAIAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAD/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAA==") 2 2, default';
|
||||
|
||||
if (target.style.cursor) {
|
||||
Log.Info("Data URI scheme cursor supported");
|
||||
_cursor_uris_supported = true;
|
||||
} else {
|
||||
Log.Warn("Data URI scheme cursor not supported");
|
||||
_cursor_uris_supported = false;
|
||||
}
|
||||
} catch (exc) {
|
||||
Log.Error("Data URI scheme cursor test exception: " + exc);
|
||||
_cursor_uris_supported = false;
|
||||
}
|
||||
}
|
||||
|
||||
return _cursor_uris_supported;
|
||||
};
|
||||
|
||||
export function _forceCursorURIs(enabled) {
|
||||
if (enabled === undefined || enabled) {
|
||||
_cursor_uris_supported = true;
|
||||
} else {
|
||||
_cursor_uris_supported = false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Cross-browser event and position routines
|
||||
*/
|
||||
|
||||
import * as Log from './logging.js';
|
||||
|
||||
export function getPointerEvent (e) {
|
||||
return e.changedTouches ? e.changedTouches[0] : e.touches ? e.touches[0] : e;
|
||||
};
|
||||
|
||||
export function stopEvent (e) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
// Emulate Element.setCapture() when not supported
|
||||
var _captureRecursion = false;
|
||||
var _captureElem = null;
|
||||
const _captureProxy = function (e) {
|
||||
// Recursion protection as we'll see our own event
|
||||
if (_captureRecursion) return;
|
||||
|
||||
// Clone the event as we cannot dispatch an already dispatched event
|
||||
var newEv = new e.constructor(e.type, e);
|
||||
|
||||
_captureRecursion = true;
|
||||
_captureElem.dispatchEvent(newEv);
|
||||
_captureRecursion = false;
|
||||
|
||||
// Avoid double events
|
||||
e.stopPropagation();
|
||||
|
||||
// Respect the wishes of the redirected event handlers
|
||||
if (newEv.defaultPrevented) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
// Implicitly release the capture on button release
|
||||
if ((e.type === "mouseup") || (e.type === "touchend")) {
|
||||
releaseCapture();
|
||||
}
|
||||
};
|
||||
|
||||
// Follow cursor style of target element
|
||||
const _captureElemChanged = function() {
|
||||
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||
captureElem.style.cursor = window.getComputedStyle(_captureElem).cursor;
|
||||
};
|
||||
const _captureObserver = new MutationObserver(_captureElemChanged);
|
||||
|
||||
var _captureIndex = 0;
|
||||
|
||||
export function setCapture (elem) {
|
||||
if (elem.setCapture) {
|
||||
|
||||
elem.setCapture();
|
||||
|
||||
// IE releases capture on 'click' events which might not trigger
|
||||
elem.addEventListener('mouseup', releaseCapture);
|
||||
elem.addEventListener('touchend', releaseCapture);
|
||||
|
||||
} else {
|
||||
// Release any existing capture in case this method is
|
||||
// called multiple times without coordination
|
||||
releaseCapture();
|
||||
|
||||
// Safari on iOS 9 has a broken constructor for TouchEvent.
|
||||
// We are fine in this case however, since Safari seems to
|
||||
// have some sort of implicit setCapture magic anyway.
|
||||
if (window.TouchEvent !== undefined) {
|
||||
try {
|
||||
new TouchEvent("touchstart");
|
||||
} catch (TypeError) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||
|
||||
if (captureElem === null) {
|
||||
captureElem = document.createElement("div");
|
||||
captureElem.id = "noVNC_mouse_capture_elem";
|
||||
captureElem.style.position = "fixed";
|
||||
captureElem.style.top = "0px";
|
||||
captureElem.style.left = "0px";
|
||||
captureElem.style.width = "100%";
|
||||
captureElem.style.height = "100%";
|
||||
captureElem.style.zIndex = 10000;
|
||||
captureElem.style.display = "none";
|
||||
document.body.appendChild(captureElem);
|
||||
|
||||
// This is to make sure callers don't get confused by having
|
||||
// our blocking element as the target
|
||||
captureElem.addEventListener('contextmenu', _captureProxy);
|
||||
|
||||
captureElem.addEventListener('mousemove', _captureProxy);
|
||||
captureElem.addEventListener('mouseup', _captureProxy);
|
||||
|
||||
captureElem.addEventListener('touchmove', _captureProxy);
|
||||
captureElem.addEventListener('touchend', _captureProxy);
|
||||
}
|
||||
|
||||
_captureElem = elem;
|
||||
_captureIndex++;
|
||||
|
||||
// Track cursor and get initial cursor
|
||||
_captureObserver.observe(elem, {attributes:true});
|
||||
_captureElemChanged();
|
||||
|
||||
captureElem.style.display = null;
|
||||
|
||||
// We listen to events on window in order to keep tracking if it
|
||||
// happens to leave the viewport
|
||||
window.addEventListener('mousemove', _captureProxy);
|
||||
window.addEventListener('mouseup', _captureProxy);
|
||||
|
||||
window.addEventListener('touchmove', _captureProxy);
|
||||
window.addEventListener('touchend', _captureProxy);
|
||||
}
|
||||
};
|
||||
|
||||
export function releaseCapture () {
|
||||
if (document.releaseCapture) {
|
||||
|
||||
document.releaseCapture();
|
||||
|
||||
} else {
|
||||
if (!_captureElem) {
|
||||
return;
|
||||
}
|
||||
|
||||
// There might be events already queued, so we need to wait for
|
||||
// them to flush. E.g. contextmenu in Microsoft Edge
|
||||
window.setTimeout(function(expected) {
|
||||
// Only clear it if it's the expected grab (i.e. no one
|
||||
// else has initiated a new grab)
|
||||
if (_captureIndex === expected) {
|
||||
_captureElem = null;
|
||||
}
|
||||
}, 0, _captureIndex);
|
||||
|
||||
_captureObserver.disconnect();
|
||||
|
||||
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||
captureElem.style.display = "none";
|
||||
|
||||
window.removeEventListener('mousemove', _captureProxy);
|
||||
window.removeEventListener('mouseup', _captureProxy);
|
||||
|
||||
window.removeEventListener('touchmove', _captureProxy);
|
||||
window.removeEventListener('touchend', _captureProxy);
|
||||
}
|
||||
};
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Localization Utilities
|
||||
*/
|
||||
|
||||
export function Localizer() {
|
||||
// Currently configured language
|
||||
this.language = 'en';
|
||||
|
||||
// Current dictionary of translations
|
||||
this.dictionary = undefined;
|
||||
}
|
||||
|
||||
Localizer.prototype = {
|
||||
// Configure suitable language based on user preferences
|
||||
setup: function (supportedLanguages) {
|
||||
var userLanguages;
|
||||
|
||||
this.language = 'en'; // Default: US English
|
||||
|
||||
/*
|
||||
* Navigator.languages only available in Chrome (32+) and FireFox (32+)
|
||||
* Fall back to navigator.language for other browsers
|
||||
*/
|
||||
if (typeof window.navigator.languages == 'object') {
|
||||
userLanguages = window.navigator.languages;
|
||||
} else {
|
||||
userLanguages = [navigator.language || navigator.userLanguage];
|
||||
}
|
||||
|
||||
for (var i = 0;i < userLanguages.length;i++) {
|
||||
var userLang = userLanguages[i];
|
||||
userLang = userLang.toLowerCase();
|
||||
userLang = userLang.replace("_", "-");
|
||||
userLang = userLang.split("-");
|
||||
|
||||
// Built-in default?
|
||||
if ((userLang[0] === 'en') &&
|
||||
((userLang[1] === undefined) || (userLang[1] === 'us'))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// First pass: perfect match
|
||||
for (var j = 0;j < supportedLanguages.length;j++) {
|
||||
var supLang = supportedLanguages[j];
|
||||
supLang = supLang.toLowerCase();
|
||||
supLang = supLang.replace("_", "-");
|
||||
supLang = supLang.split("-");
|
||||
|
||||
if (userLang[0] !== supLang[0])
|
||||
continue;
|
||||
if (userLang[1] !== supLang[1])
|
||||
continue;
|
||||
|
||||
this.language = supportedLanguages[j];
|
||||
return;
|
||||
}
|
||||
|
||||
// Second pass: fallback
|
||||
for (var j = 0;j < supportedLanguages.length;j++) {
|
||||
supLang = supportedLanguages[j];
|
||||
supLang = supLang.toLowerCase();
|
||||
supLang = supLang.replace("_", "-");
|
||||
supLang = supLang.split("-");
|
||||
|
||||
if (userLang[0] !== supLang[0])
|
||||
continue;
|
||||
if (supLang[1] !== undefined)
|
||||
continue;
|
||||
|
||||
this.language = supportedLanguages[j];
|
||||
return;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Retrieve localised text
|
||||
get: function (id) {
|
||||
if (typeof this.dictionary !== 'undefined' && this.dictionary[id]) {
|
||||
return this.dictionary[id];
|
||||
} else {
|
||||
return id;
|
||||
}
|
||||
},
|
||||
|
||||
// Traverses the DOM and translates relevant fields
|
||||
// See https://html.spec.whatwg.org/multipage/dom.html#attr-translate
|
||||
translateDOM: function () {
|
||||
var self = this;
|
||||
function process(elem, enabled) {
|
||||
function isAnyOf(searchElement, items) {
|
||||
return items.indexOf(searchElement) !== -1;
|
||||
}
|
||||
|
||||
function translateAttribute(elem, attr) {
|
||||
var str = elem.getAttribute(attr);
|
||||
str = self.get(str);
|
||||
elem.setAttribute(attr, str);
|
||||
}
|
||||
|
||||
function translateTextNode(node) {
|
||||
var str = node.data.trim();
|
||||
str = self.get(str);
|
||||
node.data = str;
|
||||
}
|
||||
|
||||
if (elem.hasAttribute("translate")) {
|
||||
if (isAnyOf(elem.getAttribute("translate"), ["", "yes"])) {
|
||||
enabled = true;
|
||||
} else if (isAnyOf(elem.getAttribute("translate"), ["no"])) {
|
||||
enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
if (elem.hasAttribute("abbr") &&
|
||||
elem.tagName === "TH") {
|
||||
translateAttribute(elem, "abbr");
|
||||
}
|
||||
if (elem.hasAttribute("alt") &&
|
||||
isAnyOf(elem.tagName, ["AREA", "IMG", "INPUT"])) {
|
||||
translateAttribute(elem, "alt");
|
||||
}
|
||||
if (elem.hasAttribute("download") &&
|
||||
isAnyOf(elem.tagName, ["A", "AREA"])) {
|
||||
translateAttribute(elem, "download");
|
||||
}
|
||||
if (elem.hasAttribute("label") &&
|
||||
isAnyOf(elem.tagName, ["MENUITEM", "MENU", "OPTGROUP",
|
||||
"OPTION", "TRACK"])) {
|
||||
translateAttribute(elem, "label");
|
||||
}
|
||||
// FIXME: Should update "lang"
|
||||
if (elem.hasAttribute("placeholder") &&
|
||||
isAnyOf(elem.tagName, ["INPUT", "TEXTAREA"])) {
|
||||
translateAttribute(elem, "placeholder");
|
||||
}
|
||||
if (elem.hasAttribute("title")) {
|
||||
translateAttribute(elem, "title");
|
||||
}
|
||||
if (elem.hasAttribute("value") &&
|
||||
elem.tagName === "INPUT" &&
|
||||
isAnyOf(elem.getAttribute("type"), ["reset", "button"])) {
|
||||
translateAttribute(elem, "value");
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0;i < elem.childNodes.length;i++) {
|
||||
let node = elem.childNodes[i];
|
||||
if (node.nodeType === node.ELEMENT_NODE) {
|
||||
process(node, enabled);
|
||||
} else if (node.nodeType === node.TEXT_NODE && enabled) {
|
||||
translateTextNode(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process(document.body, true);
|
||||
},
|
||||
}
|
||||
|
||||
export const l10n = new Localizer();
|
||||
export default l10n.get.bind(l10n);
|
|
@ -0,0 +1,53 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Logging/debug routines
|
||||
*/
|
||||
|
||||
var _log_level = 'warn';
|
||||
|
||||
var Debug = function (msg) {};
|
||||
var Info = function (msg) {};
|
||||
var Warn = function (msg) {};
|
||||
var Error = function (msg) {};
|
||||
|
||||
export function init_logging (level) {
|
||||
if (typeof level === 'undefined') {
|
||||
level = _log_level;
|
||||
} else {
|
||||
_log_level = level;
|
||||
}
|
||||
|
||||
Debug = Info = Warn = Error = function (msg) {};
|
||||
if (typeof window.console !== "undefined") {
|
||||
/* jshint -W086 */
|
||||
switch (level) {
|
||||
case 'debug':
|
||||
Debug = console.debug.bind(window.console);
|
||||
case 'info':
|
||||
Info = console.info.bind(window.console);
|
||||
case 'warn':
|
||||
Warn = console.warn.bind(window.console);
|
||||
case 'error':
|
||||
Error = console.error.bind(window.console);
|
||||
case 'none':
|
||||
break;
|
||||
default:
|
||||
throw new Error("invalid logging type '" + level + "'");
|
||||
}
|
||||
/* jshint +W086 */
|
||||
}
|
||||
};
|
||||
export function get_logging () {
|
||||
return _log_level;
|
||||
};
|
||||
export { Debug, Info, Warn, Error };
|
||||
|
||||
// Initialize logging level
|
||||
init_logging();
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Getter/Setter Creation Utilities
|
||||
*/
|
||||
|
||||
import * as Log from './logging.js';
|
||||
|
||||
function make_property (proto, name, mode, type) {
|
||||
"use strict";
|
||||
|
||||
var getter;
|
||||
if (type === 'arr') {
|
||||
getter = function (idx) {
|
||||
if (typeof idx !== 'undefined') {
|
||||
return this['_' + name][idx];
|
||||
} else {
|
||||
return this['_' + name];
|
||||
}
|
||||
};
|
||||
} else {
|
||||
getter = function () {
|
||||
return this['_' + name];
|
||||
};
|
||||
}
|
||||
|
||||
var make_setter = function (process_val) {
|
||||
if (process_val) {
|
||||
return function (val, idx) {
|
||||
if (typeof idx !== 'undefined') {
|
||||
this['_' + name][idx] = process_val(val);
|
||||
} else {
|
||||
this['_' + name] = process_val(val);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return function (val, idx) {
|
||||
if (typeof idx !== 'undefined') {
|
||||
this['_' + name][idx] = val;
|
||||
} else {
|
||||
this['_' + name] = val;
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
var setter;
|
||||
if (type === 'bool') {
|
||||
setter = make_setter(function (val) {
|
||||
if (!val || (val in {'0': 1, 'no': 1, 'false': 1})) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
} else if (type === 'int') {
|
||||
setter = make_setter(function (val) { return parseInt(val, 10); });
|
||||
} else if (type === 'float') {
|
||||
setter = make_setter(parseFloat);
|
||||
} else if (type === 'str') {
|
||||
setter = make_setter(String);
|
||||
} else if (type === 'func') {
|
||||
setter = make_setter(function (val) {
|
||||
if (!val) {
|
||||
return function () {};
|
||||
} else {
|
||||
return val;
|
||||
}
|
||||
});
|
||||
} else if (type === 'arr' || type === 'dom' || type == 'raw') {
|
||||
setter = make_setter();
|
||||
} else {
|
||||
throw new Error('Unknown property type ' + type); // some sanity checking
|
||||
}
|
||||
|
||||
// set the getter
|
||||
if (typeof proto['get_' + name] === 'undefined') {
|
||||
proto['get_' + name] = getter;
|
||||
}
|
||||
|
||||
// set the setter if needed
|
||||
if (typeof proto['set_' + name] === 'undefined') {
|
||||
if (mode === 'rw') {
|
||||
proto['set_' + name] = setter;
|
||||
} else if (mode === 'wo') {
|
||||
proto['set_' + name] = function (val, idx) {
|
||||
if (typeof this['_' + name] !== 'undefined') {
|
||||
throw new Error(name + " can only be set once");
|
||||
}
|
||||
setter.call(this, val, idx);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// make a special setter that we can use in set defaults
|
||||
proto['_raw_set_' + name] = function (val, idx) {
|
||||
setter.call(this, val, idx);
|
||||
//delete this['_init_set_' + name]; // remove it after use
|
||||
};
|
||||
};
|
||||
|
||||
export function make_properties (constructor, arr) {
|
||||
"use strict";
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
make_property(constructor.prototype, arr[i][0], arr[i][1], arr[i][2]);
|
||||
}
|
||||
};
|
||||
|
||||
export function set_defaults (obj, conf, defaults) {
|
||||
var defaults_keys = Object.keys(defaults);
|
||||
var conf_keys = Object.keys(conf);
|
||||
var keys_obj = {};
|
||||
var i;
|
||||
for (i = 0; i < defaults_keys.length; i++) { keys_obj[defaults_keys[i]] = 1; }
|
||||
for (i = 0; i < conf_keys.length; i++) { keys_obj[conf_keys[i]] = 1; }
|
||||
var keys = Object.keys(keys_obj);
|
||||
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
var setter = obj['_raw_set_' + keys[i]];
|
||||
if (!setter) {
|
||||
Log.Warn('Invalid property ' + keys[i]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (keys[i] in conf) {
|
||||
setter.call(obj, conf[keys[i]]);
|
||||
} else {
|
||||
setter.call(obj, defaults[keys[i]]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* noVNC: HTML5 VNC client
|
||||
* Copyright (C) 2012 Joel Martin
|
||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||
*
|
||||
* See README.md for usage and integration instructions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Decode from UTF-8
|
||||
*/
|
||||
export function decodeUTF8 (utf8string) {
|
||||
"use strict";
|
||||
return decodeURIComponent(escape(utf8string));
|
||||
};
|
|
@ -12,8 +12,7 @@
|
|||
* read binary data off of the receive queue.
|
||||
*/
|
||||
|
||||
import Util from "./util.js";
|
||||
|
||||
import * as Log from './util/logging.js';
|
||||
|
||||
/*jslint browser: true, bitwise: true */
|
||||
/*global Util*/
|
||||
|
@ -43,16 +42,14 @@ export default function Websock() {
|
|||
};
|
||||
};
|
||||
|
||||
(function () {
|
||||
"use strict";
|
||||
// this has performance issues in some versions Chromium, and
|
||||
// doesn't gain a tremendous amount of performance increase in Firefox
|
||||
// at the moment. It may be valuable to turn it on in the future.
|
||||
var ENABLE_COPYWITHIN = false;
|
||||
// this has performance issues in some versions Chromium, and
|
||||
// doesn't gain a tremendous amount of performance increase in Firefox
|
||||
// at the moment. It may be valuable to turn it on in the future.
|
||||
var ENABLE_COPYWITHIN = false;
|
||||
|
||||
var MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
|
||||
var MAX_RQ_GROW_SIZE = 40 * 1024 * 1024; // 40 MiB
|
||||
|
||||
var typedArrayToString = (function () {
|
||||
var typedArrayToString = (function () {
|
||||
// This is only for PhantomJS, which doesn't like apply-ing
|
||||
// with Typed Arrays
|
||||
try {
|
||||
|
@ -65,9 +62,9 @@ export default function Websock() {
|
|||
null, Array.prototype.slice.call(a));
|
||||
};
|
||||
}
|
||||
})();
|
||||
})();
|
||||
|
||||
Websock.prototype = {
|
||||
Websock.prototype = {
|
||||
// Getters and Setters
|
||||
get_sQ: function () {
|
||||
return this._sQ;
|
||||
|
@ -172,7 +169,7 @@ export default function Websock() {
|
|||
|
||||
flush: function () {
|
||||
if (this._websocket.bufferedAmount !== 0) {
|
||||
Util.Debug("bufferedAmount: " + this._websocket.bufferedAmount);
|
||||
Log.Debug("bufferedAmount: " + this._websocket.bufferedAmount);
|
||||
}
|
||||
|
||||
if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) {
|
||||
|
@ -222,23 +219,23 @@ export default function Websock() {
|
|||
|
||||
this._websocket.onmessage = this._recv_message.bind(this);
|
||||
this._websocket.onopen = (function () {
|
||||
Util.Debug('>> WebSock.onopen');
|
||||
Log.Debug('>> WebSock.onopen');
|
||||
if (this._websocket.protocol) {
|
||||
Util.Info("Server choose sub-protocol: " + this._websocket.protocol);
|
||||
Log.Info("Server choose sub-protocol: " + this._websocket.protocol);
|
||||
}
|
||||
|
||||
this._eventHandlers.open();
|
||||
Util.Debug("<< WebSock.onopen");
|
||||
Log.Debug("<< WebSock.onopen");
|
||||
}).bind(this);
|
||||
this._websocket.onclose = (function (e) {
|
||||
Util.Debug(">> WebSock.onclose");
|
||||
Log.Debug(">> WebSock.onclose");
|
||||
this._eventHandlers.close(e);
|
||||
Util.Debug("<< WebSock.onclose");
|
||||
Log.Debug("<< WebSock.onclose");
|
||||
}).bind(this);
|
||||
this._websocket.onerror = (function (e) {
|
||||
Util.Debug(">> WebSock.onerror: " + e);
|
||||
Log.Debug(">> WebSock.onerror: " + e);
|
||||
this._eventHandlers.error(e);
|
||||
Util.Debug("<< WebSock.onerror: " + e);
|
||||
Log.Debug("<< WebSock.onerror: " + e);
|
||||
}).bind(this);
|
||||
},
|
||||
|
||||
|
@ -246,7 +243,7 @@ export default function Websock() {
|
|||
if (this._websocket) {
|
||||
if ((this._websocket.readyState === WebSocket.OPEN) ||
|
||||
(this._websocket.readyState === WebSocket.CONNECTING)) {
|
||||
Util.Info("Closing WebSocket connection");
|
||||
Log.Info("Closing WebSocket connection");
|
||||
this._websocket.close();
|
||||
}
|
||||
|
||||
|
@ -321,7 +318,7 @@ export default function Websock() {
|
|||
this._expand_compact_rQ();
|
||||
}
|
||||
} else {
|
||||
Util.Debug("Ignoring empty message");
|
||||
Log.Debug("Ignoring empty message");
|
||||
}
|
||||
} catch (exc) {
|
||||
var exception_str = "";
|
||||
|
@ -339,9 +336,9 @@ export default function Websock() {
|
|||
}
|
||||
|
||||
if (exception_str.length > 0) {
|
||||
Util.Error("recv_message, caught exception: " + exception_str);
|
||||
Log.Error("recv_message, caught exception: " + exception_str);
|
||||
} else {
|
||||
Util.Error("recv_message, caught exception: " + exc);
|
||||
Log.Error("recv_message, caught exception: " + exc);
|
||||
}
|
||||
|
||||
if (typeof exc.name !== 'undefined') {
|
||||
|
@ -351,5 +348,4 @@ export default function Websock() {
|
|||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
})();
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue