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
|
/build
|
||||||
/lib
|
/lib
|
||||||
recordings
|
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
|
Javascript code necessary for full noVNC operation. This includes (but
|
||||||
is not limited to):
|
is not limited to):
|
||||||
|
|
||||||
core/base64.js
|
core/**/*.js
|
||||||
core/des.js
|
app/*.js
|
||||||
core/display.js
|
test/playback.js
|
||||||
core/input/devices.js
|
|
||||||
core/input/keysym.js
|
|
||||||
core/logo.js
|
|
||||||
core/playback.js
|
|
||||||
core/rfb.js
|
|
||||||
app/ui.js
|
|
||||||
core/util.js
|
|
||||||
core/websock.js
|
|
||||||
app/webutil.js
|
|
||||||
core/input/xtscancodes.js
|
|
||||||
core/inflator.js
|
|
||||||
|
|
||||||
The HTML, CSS, font and images files that included with the noVNC
|
The HTML, CSS, font and images files that included with the noVNC
|
||||||
source distibution (or repository) are not considered part of the
|
source distibution (or repository) are not considered part of the
|
||||||
|
|
91
app/ui.js
91
app/ui.js
|
@ -11,17 +11,15 @@
|
||||||
/* jslint white: false, browser: true */
|
/* jslint white: false, browser: true */
|
||||||
/* global window, document.getElementById, Util, WebUtil, RFB, Display */
|
/* global window, document.getElementById, Util, WebUtil, RFB, Display */
|
||||||
|
|
||||||
import Util from "../core/util.js";
|
import * as Log from '../core/util/logging.js';
|
||||||
|
import _, { l10n } from '../core/util/localization.js'
|
||||||
|
import { isTouchDevice, browserSupportsCursorURIs as cursorURIsSupported } from '../core/util/browsers.js';
|
||||||
|
import { setCapture, getPointerEvent } from '../core/util/events.js';
|
||||||
import KeyTable from "../core/input/keysym.js";
|
import KeyTable from "../core/input/keysym.js";
|
||||||
import keysyms from "../core/input/keysymdef.js";
|
import keysyms from "../core/input/keysymdef.js";
|
||||||
import RFB from "../core/rfb.js";
|
import RFB from "../core/rfb.js";
|
||||||
import Display from "../core/display.js";
|
import Display from "../core/display.js";
|
||||||
import WebUtil from "./webutil.js";
|
import * as WebUtil from "./webutil.js";
|
||||||
|
|
||||||
var UI;
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
// Fallback for all uncought errors
|
// Fallback for all uncought errors
|
||||||
window.addEventListener('error', function(event) {
|
window.addEventListener('error', function(event) {
|
||||||
|
@ -63,9 +61,7 @@ var UI;
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
var _ = Util.Localisation.get;
|
const UI = {
|
||||||
|
|
||||||
UI = {
|
|
||||||
|
|
||||||
connected: false,
|
connected: false,
|
||||||
desktopName: "",
|
desktopName: "",
|
||||||
|
@ -90,6 +86,14 @@ var UI;
|
||||||
reconnect_callback: null,
|
reconnect_callback: null,
|
||||||
reconnect_password: null,
|
reconnect_password: null,
|
||||||
|
|
||||||
|
prime: function(callback) {
|
||||||
|
if (document.readyState === "interactive" || document.readyState === "complete") {
|
||||||
|
UI.load(callback);
|
||||||
|
} else {
|
||||||
|
document.addEventListener('DOMContentLoaded', UI.load.bind(UI, callback));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Setup rfb object, load settings from browser storage, then call
|
// Setup rfb object, load settings from browser storage, then call
|
||||||
// UI.init to setup the UI/menus
|
// UI.init to setup the UI/menus
|
||||||
load: function(callback) {
|
load: function(callback) {
|
||||||
|
@ -106,10 +110,10 @@ var UI;
|
||||||
UI.initSettings();
|
UI.initSettings();
|
||||||
|
|
||||||
// Translate the DOM
|
// Translate the DOM
|
||||||
Util.Localisation.translateDOM();
|
l10n.translateDOM();
|
||||||
|
|
||||||
// Adapt the interface for touch screen devices
|
// Adapt the interface for touch screen devices
|
||||||
if (Util.isTouchDevice) {
|
if (isTouchDevice) {
|
||||||
document.documentElement.classList.add("noVNC_touch");
|
document.documentElement.classList.add("noVNC_touch");
|
||||||
// Remove the address bar
|
// Remove the address bar
|
||||||
setTimeout(function() { window.scrollTo(0, 1); }, 100);
|
setTimeout(function() { window.scrollTo(0, 1); }, 100);
|
||||||
|
@ -204,7 +208,7 @@ var UI;
|
||||||
UI.initSetting('port', port);
|
UI.initSetting('port', port);
|
||||||
UI.initSetting('encrypt', (window.location.protocol === "https:"));
|
UI.initSetting('encrypt', (window.location.protocol === "https:"));
|
||||||
UI.initSetting('true_color', true);
|
UI.initSetting('true_color', true);
|
||||||
UI.initSetting('cursor', !Util.isTouchDevice);
|
UI.initSetting('cursor', !isTouchDevice);
|
||||||
UI.initSetting('clip', false);
|
UI.initSetting('clip', false);
|
||||||
UI.initSetting('resize', 'off');
|
UI.initSetting('resize', 'off');
|
||||||
UI.initSetting('shared', true);
|
UI.initSetting('shared', true);
|
||||||
|
@ -216,7 +220,6 @@ var UI;
|
||||||
|
|
||||||
UI.setupSettingLabels();
|
UI.setupSettingLabels();
|
||||||
},
|
},
|
||||||
|
|
||||||
// Adds a link to the label elements on the corresponding input elements
|
// Adds a link to the label elements on the corresponding input elements
|
||||||
setupSettingLabels: function() {
|
setupSettingLabels: function() {
|
||||||
var labels = document.getElementsByTagName('LABEL');
|
var labels = document.getElementsByTagName('LABEL');
|
||||||
|
@ -254,7 +257,7 @@ var UI;
|
||||||
return true;
|
return true;
|
||||||
} catch (exc) {
|
} catch (exc) {
|
||||||
var msg = "Unable to create RFB client -- " + exc;
|
var msg = "Unable to create RFB client -- " + exc;
|
||||||
Util.Error(msg);
|
Log.Error(msg);
|
||||||
UI.showStatus(msg, 'error');
|
UI.showStatus(msg, 'error');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -489,7 +492,7 @@ var UI;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
msg = "Invalid UI state";
|
msg = "Invalid UI state";
|
||||||
Util.Error(msg);
|
Log.Error(msg);
|
||||||
UI.showStatus(msg, 'error');
|
UI.showStatus(msg, 'error');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -499,11 +502,11 @@ var UI;
|
||||||
|
|
||||||
// Disable/enable controls depending on connection state
|
// Disable/enable controls depending on connection state
|
||||||
updateVisualState: function() {
|
updateVisualState: function() {
|
||||||
//Util.Debug(">> updateVisualState");
|
//Log.Debug(">> updateVisualState");
|
||||||
|
|
||||||
UI.enableDisableViewClip();
|
UI.enableDisableViewClip();
|
||||||
|
|
||||||
if (Util.browserSupportsCursorURIs() && !Util.isTouchDevice) {
|
if (cursorURIsSupported() && !isTouchDevice) {
|
||||||
UI.enableSetting('cursor');
|
UI.enableSetting('cursor');
|
||||||
} else {
|
} else {
|
||||||
UI.disableSetting('cursor');
|
UI.disableSetting('cursor');
|
||||||
|
@ -555,7 +558,7 @@ var UI;
|
||||||
document.getElementById('noVNC_password_dlg')
|
document.getElementById('noVNC_password_dlg')
|
||||||
.classList.remove('noVNC_open');
|
.classList.remove('noVNC_open');
|
||||||
|
|
||||||
//Util.Debug("<< updateVisualState");
|
//Log.Debug("<< updateVisualState");
|
||||||
},
|
},
|
||||||
|
|
||||||
showStatus: function(text, status_type, time) {
|
showStatus: function(text, status_type, time) {
|
||||||
|
@ -669,7 +672,7 @@ var UI;
|
||||||
dragControlbarHandle: function (e) {
|
dragControlbarHandle: function (e) {
|
||||||
if (!UI.controlbarGrabbed) return;
|
if (!UI.controlbarGrabbed) return;
|
||||||
|
|
||||||
var ptr = Util.getPointerEvent(e);
|
var ptr = getPointerEvent(e);
|
||||||
|
|
||||||
var anchor = document.getElementById('noVNC_control_bar_anchor');
|
var anchor = document.getElementById('noVNC_control_bar_anchor');
|
||||||
if (ptr.clientX < (window.innerWidth * 0.1)) {
|
if (ptr.clientX < (window.innerWidth * 0.1)) {
|
||||||
|
@ -766,12 +769,12 @@ var UI;
|
||||||
controlbarHandleMouseDown: function(e) {
|
controlbarHandleMouseDown: function(e) {
|
||||||
if ((e.type == "mousedown") && (e.button != 0)) return;
|
if ((e.type == "mousedown") && (e.button != 0)) return;
|
||||||
|
|
||||||
var ptr = Util.getPointerEvent(e);
|
var ptr = getPointerEvent(e);
|
||||||
|
|
||||||
var handle = document.getElementById("noVNC_control_bar_handle");
|
var handle = document.getElementById("noVNC_control_bar_handle");
|
||||||
var bounds = handle.getBoundingClientRect();
|
var bounds = handle.getBoundingClientRect();
|
||||||
|
|
||||||
Util.setCapture(handle);
|
setCapture(handle);
|
||||||
UI.controlbarGrabbed = true;
|
UI.controlbarGrabbed = true;
|
||||||
UI.controlbarDrag = false;
|
UI.controlbarDrag = false;
|
||||||
|
|
||||||
|
@ -852,7 +855,7 @@ var UI;
|
||||||
val = ctrl.value;
|
val = ctrl.value;
|
||||||
}
|
}
|
||||||
WebUtil.writeSetting(name, val);
|
WebUtil.writeSetting(name, val);
|
||||||
//Util.Debug("Setting saved '" + name + "=" + val + "'");
|
//Log.Debug("Setting saved '" + name + "=" + val + "'");
|
||||||
return val;
|
return val;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -911,10 +914,10 @@ var UI;
|
||||||
// Refresh UI elements from saved cookies
|
// Refresh UI elements from saved cookies
|
||||||
UI.updateSetting('encrypt');
|
UI.updateSetting('encrypt');
|
||||||
UI.updateSetting('true_color');
|
UI.updateSetting('true_color');
|
||||||
if (Util.browserSupportsCursorURIs()) {
|
if (cursorURIsSupported()) {
|
||||||
UI.updateSetting('cursor');
|
UI.updateSetting('cursor');
|
||||||
} else {
|
} else {
|
||||||
UI.updateSetting('cursor', !Util.isTouchDevice);
|
UI.updateSetting('cursor', !isTouchDevice);
|
||||||
UI.disableSetting('cursor');
|
UI.disableSetting('cursor');
|
||||||
}
|
}
|
||||||
UI.updateSetting('clip');
|
UI.updateSetting('clip');
|
||||||
|
@ -1027,9 +1030,9 @@ var UI;
|
||||||
},
|
},
|
||||||
|
|
||||||
clipboardReceive: function(rfb, text) {
|
clipboardReceive: function(rfb, text) {
|
||||||
Util.Debug(">> UI.clipboardReceive: " + text.substr(0,40) + "...");
|
Log.Debug(">> UI.clipboardReceive: " + text.substr(0,40) + "...");
|
||||||
document.getElementById('noVNC_clipboard_text').value = text;
|
document.getElementById('noVNC_clipboard_text').value = text;
|
||||||
Util.Debug("<< UI.clipboardReceive");
|
Log.Debug("<< UI.clipboardReceive");
|
||||||
},
|
},
|
||||||
|
|
||||||
clipboardClear: function() {
|
clipboardClear: function() {
|
||||||
|
@ -1039,9 +1042,9 @@ var UI;
|
||||||
|
|
||||||
clipboardSend: function() {
|
clipboardSend: function() {
|
||||||
var text = document.getElementById('noVNC_clipboard_text').value;
|
var text = document.getElementById('noVNC_clipboard_text').value;
|
||||||
Util.Debug(">> UI.clipboardSend: " + text.substr(0,40) + "...");
|
Log.Debug(">> UI.clipboardSend: " + text.substr(0,40) + "...");
|
||||||
UI.rfb.clipboardPasteFrom(text);
|
UI.rfb.clipboardPasteFrom(text);
|
||||||
Util.Debug("<< UI.clipboardSend");
|
Log.Debug("<< UI.clipboardSend");
|
||||||
},
|
},
|
||||||
|
|
||||||
/* ------^-------
|
/* ------^-------
|
||||||
|
@ -1075,7 +1078,7 @@ var UI;
|
||||||
|
|
||||||
if ((!host) || (!port)) {
|
if ((!host) || (!port)) {
|
||||||
var msg = _("Must set host and port");
|
var msg = _("Must set host and port");
|
||||||
Util.Error(msg);
|
Log.Error(msg);
|
||||||
UI.showStatus(msg, 'error');
|
UI.showStatus(msg, 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1165,7 +1168,7 @@ var UI;
|
||||||
if (typeof msg === 'undefined') {
|
if (typeof msg === 'undefined') {
|
||||||
msg = _("Password is required");
|
msg = _("Password is required");
|
||||||
}
|
}
|
||||||
Util.Warn(msg);
|
Log.Warn(msg);
|
||||||
UI.showStatus(msg, "warning");
|
UI.showStatus(msg, "warning");
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1259,7 +1262,7 @@ var UI;
|
||||||
UI.resizeTimeout = setTimeout(function(){
|
UI.resizeTimeout = setTimeout(function(){
|
||||||
// Request a remote size covering the viewport
|
// Request a remote size covering the viewport
|
||||||
if (UI.rfb.requestDesktopSize(screen.w, screen.h)) {
|
if (UI.rfb.requestDesktopSize(screen.w, screen.h)) {
|
||||||
Util.Debug('Requested new desktop size: ' +
|
Log.Debug('Requested new desktop size: ' +
|
||||||
screen.w + 'x' + screen.h);
|
screen.w + 'x' + screen.h);
|
||||||
}
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
|
@ -1314,7 +1317,7 @@ var UI;
|
||||||
if (resizeSetting === 'downscale' || resizeSetting === 'scale') {
|
if (resizeSetting === 'downscale' || resizeSetting === 'scale') {
|
||||||
// Disable clipping if we are scaling
|
// Disable clipping if we are scaling
|
||||||
new_clip = false;
|
new_clip = false;
|
||||||
} else if (Util.isTouchDevice) {
|
} else if (isTouchDevice) {
|
||||||
// Touch devices usually have shit scrollbars
|
// Touch devices usually have shit scrollbars
|
||||||
new_clip = true;
|
new_clip = true;
|
||||||
}
|
}
|
||||||
|
@ -1342,7 +1345,7 @@ var UI;
|
||||||
var resizeSetting = UI.getSetting('resize');
|
var resizeSetting = UI.getSetting('resize');
|
||||||
// Disable clipping if we are scaling, connected or on touch
|
// Disable clipping if we are scaling, connected or on touch
|
||||||
if (resizeSetting === 'downscale' || resizeSetting === 'scale' ||
|
if (resizeSetting === 'downscale' || resizeSetting === 'scale' ||
|
||||||
Util.isTouchDevice) {
|
isTouchDevice) {
|
||||||
UI.disableSetting('clip');
|
UI.disableSetting('clip');
|
||||||
} else {
|
} else {
|
||||||
UI.enableSetting('clip');
|
UI.enableSetting('clip');
|
||||||
|
@ -1401,7 +1404,7 @@ var UI;
|
||||||
|
|
||||||
// Different behaviour for touch vs non-touch
|
// Different behaviour for touch vs non-touch
|
||||||
// The button is disabled instead of hidden on touch devices
|
// The button is disabled instead of hidden on touch devices
|
||||||
if (Util.isTouchDevice) {
|
if (isTouchDevice) {
|
||||||
viewDragButton.classList.remove("noVNC_hidden");
|
viewDragButton.classList.remove("noVNC_hidden");
|
||||||
|
|
||||||
if (clipping) {
|
if (clipping) {
|
||||||
|
@ -1427,7 +1430,7 @@ var UI;
|
||||||
* ------v------*/
|
* ------v------*/
|
||||||
|
|
||||||
showVirtualKeyboard: function() {
|
showVirtualKeyboard: function() {
|
||||||
if (!Util.isTouchDevice) return;
|
if (!isTouchDevice) return;
|
||||||
|
|
||||||
var input = document.getElementById('noVNC_keyboardinput');
|
var input = document.getElementById('noVNC_keyboardinput');
|
||||||
|
|
||||||
|
@ -1443,7 +1446,7 @@ var UI;
|
||||||
},
|
},
|
||||||
|
|
||||||
hideVirtualKeyboard: function() {
|
hideVirtualKeyboard: function() {
|
||||||
if (!Util.isTouchDevice) return;
|
if (!isTouchDevice) return;
|
||||||
|
|
||||||
var input = document.getElementById('noVNC_keyboardinput');
|
var input = document.getElementById('noVNC_keyboardinput');
|
||||||
|
|
||||||
|
@ -1737,19 +1740,19 @@ var UI;
|
||||||
|
|
||||||
// Set up translations
|
// Set up translations
|
||||||
var LINGUAS = ["de", "el", "nl", "sv"];
|
var LINGUAS = ["de", "el", "nl", "sv"];
|
||||||
Util.Localisation.setup(LINGUAS);
|
l10n.setup(LINGUAS);
|
||||||
if (Util.Localisation.language !== "en" && Util.Localisation.dictionary === undefined) {
|
if (l10n.language !== "en" && l10n.dictionary === undefined) {
|
||||||
WebUtil.fetchJSON('app/locale/' + Util.Localisation.language + '.json', function (translations) {
|
WebUtil.fetchJSON('app/locale/' + l10n.language + '.json', function (translations) {
|
||||||
Util.Localisation.dictionary = translations;
|
l10n.dictionary = translations;
|
||||||
|
|
||||||
// wait for translations to load before loading the UI
|
// wait for translations to load before loading the UI
|
||||||
UI.load();
|
UI.prime();
|
||||||
}, function (err) {
|
}, function (err) {
|
||||||
throw err;
|
throw err;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
UI.load();
|
UI.prime();
|
||||||
}
|
}
|
||||||
})();
|
|
||||||
|
|
||||||
export default UI;
|
export default UI;
|
||||||
|
|
|
@ -10,31 +10,21 @@
|
||||||
/*jslint bitwise: false, white: false, browser: true, devel: true */
|
/*jslint bitwise: false, white: false, browser: true, devel: true */
|
||||||
/*global Util, window, document */
|
/*global Util, window, document */
|
||||||
|
|
||||||
import Util from "../core/util.js";
|
import { init_logging as main_init_logging } from '../core/util/logging.js';
|
||||||
|
|
||||||
// Globals defined here
|
|
||||||
var WebUtil = {};
|
|
||||||
|
|
||||||
/*
|
|
||||||
* ------------------------------------------------------
|
|
||||||
* Namespaced in WebUtil
|
|
||||||
* ------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
// init log level reading the logging HTTP param
|
// init log level reading the logging HTTP param
|
||||||
WebUtil.init_logging = function (level) {
|
export function init_logging (level) {
|
||||||
"use strict";
|
"use strict";
|
||||||
if (typeof level !== "undefined") {
|
if (typeof level !== "undefined") {
|
||||||
Util._log_level = level;
|
main_init_logging(level);
|
||||||
} else {
|
} else {
|
||||||
var param = document.location.href.match(/logging=([A-Za-z0-9\._\-]*)/);
|
var param = document.location.href.match(/logging=([A-Za-z0-9\._\-]*)/);
|
||||||
Util._log_level = (param || ['', Util._log_level])[1];
|
main_init_logging(param || undefined);
|
||||||
}
|
}
|
||||||
Util.init_logging();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Read a query string variable
|
// Read a query string variable
|
||||||
WebUtil.getQueryVar = function (name, defVal) {
|
export function getQueryVar (name, defVal) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
|
var re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
|
||||||
match = document.location.href.match(re);
|
match = document.location.href.match(re);
|
||||||
|
@ -47,7 +37,7 @@ WebUtil.getQueryVar = function (name, defVal) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// Read a hash fragment variable
|
// Read a hash fragment variable
|
||||||
WebUtil.getHashVar = function (name, defVal) {
|
export function getHashVar (name, defVal) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var re = new RegExp('.*[&#]' + name + '=([^&]*)'),
|
var re = new RegExp('.*[&#]' + name + '=([^&]*)'),
|
||||||
match = document.location.hash.match(re);
|
match = document.location.hash.match(re);
|
||||||
|
@ -61,11 +51,11 @@ WebUtil.getHashVar = function (name, defVal) {
|
||||||
|
|
||||||
// Read a variable from the fragment or the query string
|
// Read a variable from the fragment or the query string
|
||||||
// Fragment takes precedence
|
// Fragment takes precedence
|
||||||
WebUtil.getConfigVar = function (name, defVal) {
|
export function getConfigVar (name, defVal) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var val = WebUtil.getHashVar(name);
|
var val = getHashVar(name);
|
||||||
if (val === null) {
|
if (val === null) {
|
||||||
val = WebUtil.getQueryVar(name, defVal);
|
val = getQueryVar(name, defVal);
|
||||||
}
|
}
|
||||||
return val;
|
return val;
|
||||||
};
|
};
|
||||||
|
@ -75,7 +65,7 @@ WebUtil.getConfigVar = function (name, defVal) {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
// No days means only for this browser session
|
// No days means only for this browser session
|
||||||
WebUtil.createCookie = function (name, value, days) {
|
export function createCookie (name, value, days) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var date, expires;
|
var date, expires;
|
||||||
if (days) {
|
if (days) {
|
||||||
|
@ -95,7 +85,7 @@ WebUtil.createCookie = function (name, value, days) {
|
||||||
document.cookie = name + "=" + value + expires + "; path=/" + secure;
|
document.cookie = name + "=" + value + expires + "; path=/" + secure;
|
||||||
};
|
};
|
||||||
|
|
||||||
WebUtil.readCookie = function (name, defaultValue) {
|
export function readCookie (name, defaultValue) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var nameEQ = name + "=",
|
var nameEQ = name + "=",
|
||||||
ca = document.cookie.split(';');
|
ca = document.cookie.split(';');
|
||||||
|
@ -108,22 +98,24 @@ WebUtil.readCookie = function (name, defaultValue) {
|
||||||
return (typeof defaultValue !== 'undefined') ? defaultValue : null;
|
return (typeof defaultValue !== 'undefined') ? defaultValue : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
WebUtil.eraseCookie = function (name) {
|
export function eraseCookie (name) {
|
||||||
"use strict";
|
"use strict";
|
||||||
WebUtil.createCookie(name, "", -1);
|
createCookie(name, "", -1);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Setting handling.
|
* Setting handling.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
WebUtil.initSettings = function (callback /*, ...callbackArgs */) {
|
var settings = {};
|
||||||
|
|
||||||
|
export function initSettings (callback /*, ...callbackArgs */) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var callbackArgs = Array.prototype.slice.call(arguments, 1);
|
var callbackArgs = Array.prototype.slice.call(arguments, 1);
|
||||||
if (window.chrome && window.chrome.storage) {
|
if (window.chrome && window.chrome.storage) {
|
||||||
window.chrome.storage.sync.get(function (cfg) {
|
window.chrome.storage.sync.get(function (cfg) {
|
||||||
WebUtil.settings = cfg;
|
settings = cfg;
|
||||||
console.log(WebUtil.settings);
|
console.log(settings);
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback.apply(this, callbackArgs);
|
callback.apply(this, callbackArgs);
|
||||||
}
|
}
|
||||||
|
@ -137,24 +129,24 @@ WebUtil.initSettings = function (callback /*, ...callbackArgs */) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// No days means only for this browser session
|
// No days means only for this browser session
|
||||||
WebUtil.writeSetting = function (name, value) {
|
export function writeSetting (name, value) {
|
||||||
"use strict";
|
"use strict";
|
||||||
if (window.chrome && window.chrome.storage) {
|
if (window.chrome && window.chrome.storage) {
|
||||||
//console.log("writeSetting:", name, value);
|
//console.log("writeSetting:", name, value);
|
||||||
if (WebUtil.settings[name] !== value) {
|
if (settings[name] !== value) {
|
||||||
WebUtil.settings[name] = value;
|
settings[name] = value;
|
||||||
window.chrome.storage.sync.set(WebUtil.settings);
|
window.chrome.storage.sync.set(settings);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
localStorage.setItem(name, value);
|
localStorage.setItem(name, value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
WebUtil.readSetting = function (name, defaultValue) {
|
export function readSetting (name, defaultValue) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var value;
|
var value;
|
||||||
if (window.chrome && window.chrome.storage) {
|
if (window.chrome && window.chrome.storage) {
|
||||||
value = WebUtil.settings[name];
|
value = settings[name];
|
||||||
} else {
|
} else {
|
||||||
value = localStorage.getItem(name);
|
value = localStorage.getItem(name);
|
||||||
}
|
}
|
||||||
|
@ -168,17 +160,17 @@ WebUtil.readSetting = function (name, defaultValue) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
WebUtil.eraseSetting = function (name) {
|
export function eraseSetting (name) {
|
||||||
"use strict";
|
"use strict";
|
||||||
if (window.chrome && window.chrome.storage) {
|
if (window.chrome && window.chrome.storage) {
|
||||||
window.chrome.storage.sync.remove(name);
|
window.chrome.storage.sync.remove(name);
|
||||||
delete WebUtil.settings[name];
|
delete settings[name];
|
||||||
} else {
|
} else {
|
||||||
localStorage.removeItem(name);
|
localStorage.removeItem(name);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
WebUtil.injectParamIfMissing = function (path, param, value) {
|
export function injectParamIfMissing (path, param, value) {
|
||||||
// force pretend that we're dealing with a relative path
|
// force pretend that we're dealing with a relative path
|
||||||
// (assume that we wanted an extra if we pass one in)
|
// (assume that we wanted an extra if we pass one in)
|
||||||
path = "/" + path;
|
path = "/" + path;
|
||||||
|
@ -212,7 +204,7 @@ WebUtil.injectParamIfMissing = function (path, param, value) {
|
||||||
// IE11 support or polyfill promises and fetch in IE11.
|
// IE11 support or polyfill promises and fetch in IE11.
|
||||||
// resolve will receive an object on success, while reject
|
// resolve will receive an object on success, while reject
|
||||||
// will receive either an event or an error on failure.
|
// will receive either an event or an error on failure.
|
||||||
WebUtil.fetchJSON = function (path, resolve, reject) {
|
export function fetchJSON(path, resolve, reject) {
|
||||||
// NB: IE11 doesn't support JSON as a responseType
|
// NB: IE11 doesn't support JSON as a responseType
|
||||||
const req = new XMLHttpRequest();
|
const req = new XMLHttpRequest();
|
||||||
req.open('GET', path);
|
req.open('GET', path);
|
||||||
|
@ -240,6 +232,4 @@ WebUtil.fetchJSON = function (path, resolve, reject) {
|
||||||
};
|
};
|
||||||
|
|
||||||
req.send();
|
req.send();
|
||||||
};
|
}
|
||||||
|
|
||||||
export default WebUtil;
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
/*jslint white: false */
|
/*jslint white: false */
|
||||||
/*global console */
|
/*global console */
|
||||||
|
|
||||||
var Base64 = {
|
export default {
|
||||||
/* Convert data (an array of integers) to a Base64 string. */
|
/* Convert data (an array of integers) to a Base64 string. */
|
||||||
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
|
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
|
||||||
base64Pad : '=',
|
base64Pad : '=',
|
||||||
|
@ -15,7 +15,7 @@ var Base64 = {
|
||||||
encode: function (data) {
|
encode: function (data) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var result = '';
|
var result = '';
|
||||||
var toBase64Table = Base64.toBase64Table;
|
var toBase64Table = this.toBase64Table;
|
||||||
var length = data.length;
|
var length = data.length;
|
||||||
var lengthpad = (length % 3);
|
var lengthpad = (length % 3);
|
||||||
// Convert every three bytes to 4 ascii characters.
|
// Convert every three bytes to 4 ascii characters.
|
||||||
|
@ -63,8 +63,8 @@ var Base64 = {
|
||||||
decode: function (data, offset) {
|
decode: function (data, offset) {
|
||||||
"use strict";
|
"use strict";
|
||||||
offset = typeof(offset) !== 'undefined' ? offset : 0;
|
offset = typeof(offset) !== 'undefined' ? offset : 0;
|
||||||
var toBinaryTable = Base64.toBinaryTable;
|
var toBinaryTable = this.toBinaryTable;
|
||||||
var base64Pad = Base64.base64Pad;
|
var base64Pad = this.base64Pad;
|
||||||
var result, result_length;
|
var result, result_length;
|
||||||
var leftbits = 0; // number of bits decoded, but yet to be appended
|
var leftbits = 0; // number of bits decoded, but yet to be appended
|
||||||
var leftdata = 0; // bits decoded, but yet to be appended
|
var leftdata = 0; // bits decoded, but yet to be appended
|
||||||
|
@ -111,5 +111,3 @@ var Base64 = {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}; /* End of Base64 namespace */
|
}; /* End of Base64 namespace */
|
||||||
|
|
||||||
export default Base64;
|
|
||||||
|
|
|
@ -10,10 +10,11 @@
|
||||||
/*jslint browser: true, white: false */
|
/*jslint browser: true, white: false */
|
||||||
/*global Util, Base64, changeCursor */
|
/*global Util, Base64, changeCursor */
|
||||||
|
|
||||||
import Util from "./util.js";
|
import { Engine, browserSupportsCursorURIs as cursorURIsSupported } from './util/browsers.js';
|
||||||
|
import { set_defaults, make_properties } from './util/properties.js';
|
||||||
|
import * as Log from './util/logging.js';
|
||||||
import Base64 from "./base64.js";
|
import Base64 from "./base64.js";
|
||||||
|
|
||||||
|
|
||||||
export default function Display(defaults) {
|
export default function Display(defaults) {
|
||||||
this._drawCtx = null;
|
this._drawCtx = null;
|
||||||
this._c_forceCanvas = false;
|
this._c_forceCanvas = false;
|
||||||
|
@ -31,7 +32,7 @@ export default function Display(defaults) {
|
||||||
this._tile_x = 0;
|
this._tile_x = 0;
|
||||||
this._tile_y = 0;
|
this._tile_y = 0;
|
||||||
|
|
||||||
Util.set_defaults(this, defaults, {
|
set_defaults(this, defaults, {
|
||||||
'true_color': true,
|
'true_color': true,
|
||||||
'colourMap': [],
|
'colourMap': [],
|
||||||
'scale': 1.0,
|
'scale': 1.0,
|
||||||
|
@ -40,7 +41,7 @@ export default function Display(defaults) {
|
||||||
"onFlush": function () {},
|
"onFlush": function () {},
|
||||||
});
|
});
|
||||||
|
|
||||||
Util.Debug(">> Display.constructor");
|
Log.Debug(">> Display.constructor");
|
||||||
|
|
||||||
// The visible canvas
|
// The visible canvas
|
||||||
if (!this._target) {
|
if (!this._target) {
|
||||||
|
@ -68,11 +69,11 @@ export default function Display(defaults) {
|
||||||
right: this._backbuffer.width,
|
right: this._backbuffer.width,
|
||||||
bottom: this._backbuffer.height };
|
bottom: this._backbuffer.height };
|
||||||
|
|
||||||
Util.Debug("User Agent: " + navigator.userAgent);
|
Log.Debug("User Agent: " + navigator.userAgent);
|
||||||
if (Util.Engine.gecko) { Util.Debug("Browser: gecko " + Util.Engine.gecko); }
|
if (Engine.gecko) { Log.Debug("Browser: gecko " + Engine.gecko); }
|
||||||
if (Util.Engine.webkit) { Util.Debug("Browser: webkit " + Util.Engine.webkit); }
|
if (Engine.webkit) { Log.Debug("Browser: webkit " + Engine.webkit); }
|
||||||
if (Util.Engine.trident) { Util.Debug("Browser: trident " + Util.Engine.trident); }
|
if (Engine.trident) { Log.Debug("Browser: trident " + Engine.trident); }
|
||||||
if (Util.Engine.presto) { Util.Debug("Browser: presto " + Util.Engine.presto); }
|
if (Engine.presto) { Log.Debug("Browser: presto " + Engine.presto); }
|
||||||
|
|
||||||
this.clear();
|
this.clear();
|
||||||
|
|
||||||
|
@ -84,22 +85,19 @@ export default function Display(defaults) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._prefer_js === null) {
|
if (this._prefer_js === null) {
|
||||||
Util.Info("Prefering javascript operations");
|
Log.Info("Prefering javascript operations");
|
||||||
this._prefer_js = true;
|
this._prefer_js = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine browser support for setting the cursor via data URI scheme
|
// Determine browser support for setting the cursor via data URI scheme
|
||||||
if (this._cursor_uri || this._cursor_uri === null ||
|
if (this._cursor_uri || this._cursor_uri === null ||
|
||||||
this._cursor_uri === undefined) {
|
this._cursor_uri === undefined) {
|
||||||
this._cursor_uri = Util.browserSupportsCursorURIs();
|
this._cursor_uri = cursorURIsSupported();
|
||||||
}
|
}
|
||||||
|
|
||||||
Util.Debug("<< Display.constructor");
|
Log.Debug("<< Display.constructor");
|
||||||
};
|
};
|
||||||
|
|
||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
var SUPPORTS_IMAGEDATA_CONSTRUCTOR = false;
|
var SUPPORTS_IMAGEDATA_CONSTRUCTOR = false;
|
||||||
try {
|
try {
|
||||||
new ImageData(new Uint8ClampedArray(4), 1, 1);
|
new ImageData(new Uint8ClampedArray(4), 1, 1);
|
||||||
|
@ -108,7 +106,6 @@ export default function Display(defaults) {
|
||||||
// ignore failure
|
// ignore failure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Display.prototype = {
|
Display.prototype = {
|
||||||
// Public methods
|
// Public methods
|
||||||
viewportChangePos: function (deltaX, deltaY) {
|
viewportChangePos: function (deltaX, deltaY) {
|
||||||
|
@ -143,7 +140,7 @@ export default function Display(defaults) {
|
||||||
if (deltaX === 0 && deltaY === 0) {
|
if (deltaX === 0 && deltaY === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Util.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY);
|
Log.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY);
|
||||||
|
|
||||||
vp.x += deltaX;
|
vp.x += deltaX;
|
||||||
vp.y += deltaY;
|
vp.y += deltaY;
|
||||||
|
@ -159,7 +156,7 @@ export default function Display(defaults) {
|
||||||
typeof(width) === "undefined" ||
|
typeof(width) === "undefined" ||
|
||||||
typeof(height) === "undefined") {
|
typeof(height) === "undefined") {
|
||||||
|
|
||||||
Util.Debug("Setting viewport to full display region");
|
Log.Debug("Setting viewport to full display region");
|
||||||
width = this._fb_width;
|
width = this._fb_width;
|
||||||
height = this._fb_height;
|
height = this._fb_height;
|
||||||
}
|
}
|
||||||
|
@ -527,7 +524,7 @@ export default function Display(defaults) {
|
||||||
|
|
||||||
changeCursor: function (pixels, mask, hotx, hoty, w, h) {
|
changeCursor: function (pixels, mask, hotx, hoty, w, h) {
|
||||||
if (this._cursor_uri === false) {
|
if (this._cursor_uri === false) {
|
||||||
Util.Warn("changeCursor called but no cursor data URI support");
|
Log.Warn("changeCursor called but no cursor data URI support");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -742,7 +739,7 @@ export default function Display(defaults) {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
Util.make_properties(Display, [
|
make_properties(Display, [
|
||||||
['target', 'wo', 'dom'], // Canvas element for rendering
|
['target', 'wo', 'dom'], // Canvas element for rendering
|
||||||
['context', 'ro', 'raw'], // Canvas 2D context for rendering (read-only)
|
['context', 'ro', 'raw'], // Canvas 2D context for rendering (read-only)
|
||||||
['logo', 'rw', 'raw'], // Logo to display when cleared: {"width": w, "height": h, "type": mime-type, "data": data}
|
['logo', 'rw', 'raw'], // Logo to display when cleared: {"width": w, "height": h, "type": mime-type, "data": data}
|
||||||
|
@ -868,4 +865,3 @@ export default function Display(defaults) {
|
||||||
var url = 'data:image/x-icon;base64,' + Base64.encode(cur);
|
var url = 'data:image/x-icon;base64,' + Base64.encode(cur);
|
||||||
target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
|
target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
|
||||||
};
|
};
|
||||||
})();
|
|
||||||
|
|
|
@ -36,4 +36,3 @@ export default function Inflate() {
|
||||||
|
|
||||||
inflateInit(this.strm, this.windowBits);
|
inflateInit(this.strm, this.windowBits);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,24 +8,21 @@
|
||||||
/*jslint browser: true, white: false */
|
/*jslint browser: true, white: false */
|
||||||
/*global window, Util */
|
/*global window, Util */
|
||||||
|
|
||||||
import Util from "../util.js";
|
import * as Log from '../util/logging.js';
|
||||||
import KeyboardUtil from "./util.js";
|
import { isTouchDevice } from '../util/browsers.js'
|
||||||
|
import { setCapture, releaseCapture, stopEvent, getPointerEvent } from '../util/events.js';
|
||||||
|
import { set_defaults, make_properties } from '../util/properties.js';
|
||||||
export var Keyboard;
|
import * as KeyboardUtil from "./util.js";
|
||||||
|
|
||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Keyboard event handler
|
// Keyboard event handler
|
||||||
//
|
//
|
||||||
|
|
||||||
Keyboard = function (defaults) {
|
const Keyboard = function (defaults) {
|
||||||
this._keyDownList = []; // List of depressed keys
|
this._keyDownList = []; // List of depressed keys
|
||||||
// (even if they are happy)
|
// (even if they are happy)
|
||||||
|
|
||||||
Util.set_defaults(this, defaults, {
|
set_defaults(this, defaults, {
|
||||||
'target': document,
|
'target': document,
|
||||||
'focused': true
|
'focused': true
|
||||||
});
|
});
|
||||||
|
@ -53,7 +50,7 @@ export var Keyboard;
|
||||||
|
|
||||||
_handleRfbEvent: function (e) {
|
_handleRfbEvent: function (e) {
|
||||||
if (this._onKeyPress) {
|
if (this._onKeyPress) {
|
||||||
Util.Debug("onKeyPress " + (e.type == 'keydown' ? "down" : "up") +
|
Log.Debug("onKeyPress " + (e.type == 'keydown' ? "down" : "up") +
|
||||||
", keysym: " + e.keysym.keysym + "(" + e.keysym.keyname + ")");
|
", keysym: " + e.keysym.keysym + "(" + e.keysym.keyname + ")");
|
||||||
this._onKeyPress(e);
|
this._onKeyPress(e);
|
||||||
}
|
}
|
||||||
|
@ -72,7 +69,7 @@ export var Keyboard;
|
||||||
|
|
||||||
if (this._handler.keydown(e)) {
|
if (this._handler.keydown(e)) {
|
||||||
// Suppress bubbling/default actions
|
// Suppress bubbling/default actions
|
||||||
Util.stopEvent(e);
|
stopEvent(e);
|
||||||
} else {
|
} else {
|
||||||
// Allow the event to bubble and become a keyPress event which
|
// Allow the event to bubble and become a keyPress event which
|
||||||
// will have the character code translated
|
// will have the character code translated
|
||||||
|
@ -84,7 +81,7 @@ export var Keyboard;
|
||||||
|
|
||||||
if (this._handler.keypress(e)) {
|
if (this._handler.keypress(e)) {
|
||||||
// Suppress bubbling/default actions
|
// Suppress bubbling/default actions
|
||||||
Util.stopEvent(e);
|
stopEvent(e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -93,20 +90,20 @@ export var Keyboard;
|
||||||
|
|
||||||
if (this._handler.keyup(e)) {
|
if (this._handler.keyup(e)) {
|
||||||
// Suppress bubbling/default actions
|
// Suppress bubbling/default actions
|
||||||
Util.stopEvent(e);
|
stopEvent(e);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_allKeysUp: function () {
|
_allKeysUp: function () {
|
||||||
Util.Debug(">> Keyboard.allKeysUp");
|
Log.Debug(">> Keyboard.allKeysUp");
|
||||||
this._handler.releaseAll();
|
this._handler.releaseAll();
|
||||||
Util.Debug("<< Keyboard.allKeysUp");
|
Log.Debug("<< Keyboard.allKeysUp");
|
||||||
},
|
},
|
||||||
|
|
||||||
// Public methods
|
// Public methods
|
||||||
|
|
||||||
grab: function () {
|
grab: function () {
|
||||||
//Util.Debug(">> Keyboard.grab");
|
//Log.Debug(">> Keyboard.grab");
|
||||||
var c = this._target;
|
var c = this._target;
|
||||||
|
|
||||||
c.addEventListener('keydown', this._eventHandlers.keydown);
|
c.addEventListener('keydown', this._eventHandlers.keydown);
|
||||||
|
@ -116,11 +113,11 @@ export var Keyboard;
|
||||||
// Release (key up) if window loses focus
|
// Release (key up) if window loses focus
|
||||||
window.addEventListener('blur', this._eventHandlers.blur);
|
window.addEventListener('blur', this._eventHandlers.blur);
|
||||||
|
|
||||||
//Util.Debug("<< Keyboard.grab");
|
//Log.Debug("<< Keyboard.grab");
|
||||||
},
|
},
|
||||||
|
|
||||||
ungrab: function () {
|
ungrab: function () {
|
||||||
//Util.Debug(">> Keyboard.ungrab");
|
//Log.Debug(">> Keyboard.ungrab");
|
||||||
var c = this._target;
|
var c = this._target;
|
||||||
|
|
||||||
c.removeEventListener('keydown', this._eventHandlers.keydown);
|
c.removeEventListener('keydown', this._eventHandlers.keydown);
|
||||||
|
@ -131,7 +128,7 @@ export var Keyboard;
|
||||||
// Release (key up) all keys that are in a down state
|
// Release (key up) all keys that are in a down state
|
||||||
this._allKeysUp();
|
this._allKeysUp();
|
||||||
|
|
||||||
//Util.Debug(">> Keyboard.ungrab");
|
//Log.Debug(">> Keyboard.ungrab");
|
||||||
},
|
},
|
||||||
|
|
||||||
sync: function (e) {
|
sync: function (e) {
|
||||||
|
@ -139,25 +136,21 @@ export var Keyboard;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Util.make_properties(Keyboard, [
|
make_properties(Keyboard, [
|
||||||
['target', 'wo', 'dom'], // DOM element that captures keyboard input
|
['target', 'wo', 'dom'], // DOM element that captures keyboard input
|
||||||
['focused', 'rw', 'bool'], // Capture and send key events
|
['focused', 'rw', 'bool'], // Capture and send key events
|
||||||
|
|
||||||
['onKeyPress', 'rw', 'func'] // Handler for key press/release
|
['onKeyPress', 'rw', 'func'] // Handler for key press/release
|
||||||
]);
|
]);
|
||||||
})();
|
|
||||||
|
|
||||||
export var Mouse;
|
const Mouse = function (defaults) {
|
||||||
|
|
||||||
(function () {
|
|
||||||
Mouse = function (defaults) {
|
|
||||||
this._mouseCaptured = false;
|
this._mouseCaptured = false;
|
||||||
|
|
||||||
this._doubleClickTimer = null;
|
this._doubleClickTimer = null;
|
||||||
this._lastTouchPos = null;
|
this._lastTouchPos = null;
|
||||||
|
|
||||||
// Configuration attributes
|
// Configuration attributes
|
||||||
Util.set_defaults(this, defaults, {
|
set_defaults(this, defaults, {
|
||||||
'target': document,
|
'target': document,
|
||||||
'focused': true,
|
'focused': true,
|
||||||
'touchButton': 1
|
'touchButton': 1
|
||||||
|
@ -176,7 +169,7 @@ export var Mouse;
|
||||||
// private methods
|
// private methods
|
||||||
_captureMouse: function () {
|
_captureMouse: function () {
|
||||||
// capturing the mouse ensures we get the mouseup event
|
// capturing the mouse ensures we get the mouseup event
|
||||||
Util.setCapture(this._target);
|
setCapture(this._target);
|
||||||
|
|
||||||
// some browsers give us mouseup events regardless,
|
// some browsers give us mouseup events regardless,
|
||||||
// so if we never captured the mouse, we can disregard the event
|
// so if we never captured the mouse, we can disregard the event
|
||||||
|
@ -184,7 +177,7 @@ export var Mouse;
|
||||||
},
|
},
|
||||||
|
|
||||||
_releaseMouse: function () {
|
_releaseMouse: function () {
|
||||||
Util.releaseCapture();
|
releaseCapture();
|
||||||
this._mouseCaptured = false;
|
this._mouseCaptured = false;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -243,11 +236,11 @@ export var Mouse;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._onMouseButton) {
|
if (this._onMouseButton) {
|
||||||
Util.Debug("onMouseButton " + (down ? "down" : "up") +
|
Log.Debug("onMouseButton " + (down ? "down" : "up") +
|
||||||
", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
|
", x: " + pos.x + ", y: " + pos.y + ", bmask: " + bmask);
|
||||||
this._onMouseButton(pos.x, pos.y, down, bmask);
|
this._onMouseButton(pos.x, pos.y, down, bmask);
|
||||||
}
|
}
|
||||||
Util.stopEvent(e);
|
stopEvent(e);
|
||||||
},
|
},
|
||||||
|
|
||||||
_handleMouseDown: function (e) {
|
_handleMouseDown: function (e) {
|
||||||
|
@ -289,7 +282,7 @@ export var Mouse;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Util.stopEvent(e);
|
stopEvent(e);
|
||||||
},
|
},
|
||||||
|
|
||||||
_handleMouseMove: function (e) {
|
_handleMouseMove: function (e) {
|
||||||
|
@ -303,7 +296,7 @@ export var Mouse;
|
||||||
if (this._onMouseMove) {
|
if (this._onMouseMove) {
|
||||||
this._onMouseMove(pos.x, pos.y);
|
this._onMouseMove(pos.x, pos.y);
|
||||||
}
|
}
|
||||||
Util.stopEvent(e);
|
stopEvent(e);
|
||||||
},
|
},
|
||||||
|
|
||||||
_handleMouseDisable: function (e) {
|
_handleMouseDisable: function (e) {
|
||||||
|
@ -316,14 +309,13 @@ export var Mouse;
|
||||||
* to listen on the document element instead.
|
* to listen on the document element instead.
|
||||||
*/
|
*/
|
||||||
if (e.target == this._target) {
|
if (e.target == this._target) {
|
||||||
//Util.Debug("mouse event disabled");
|
stopEvent(e);
|
||||||
Util.stopEvent(e);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// Return coordinates relative to target
|
// Return coordinates relative to target
|
||||||
_getMousePosition: function(e) {
|
_getMousePosition: function(e) {
|
||||||
e = Util.getPointerEvent(e);
|
e = getPointerEvent(e);
|
||||||
var bounds = this._target.getBoundingClientRect();
|
var bounds = this._target.getBoundingClientRect();
|
||||||
var x, y;
|
var x, y;
|
||||||
// Clip to target bounds
|
// Clip to target bounds
|
||||||
|
@ -344,12 +336,11 @@ export var Mouse;
|
||||||
return {x:x, y:y};
|
return {x:x, y:y};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
// Public methods
|
// Public methods
|
||||||
grab: function () {
|
grab: function () {
|
||||||
var c = this._target;
|
var c = this._target;
|
||||||
|
|
||||||
if (Util.isTouchDevice) {
|
if (isTouchDevice) {
|
||||||
c.addEventListener('touchstart', this._eventHandlers.mousedown);
|
c.addEventListener('touchstart', this._eventHandlers.mousedown);
|
||||||
window.addEventListener('touchend', this._eventHandlers.mouseup);
|
window.addEventListener('touchend', this._eventHandlers.mouseup);
|
||||||
c.addEventListener('touchend', this._eventHandlers.mouseup);
|
c.addEventListener('touchend', this._eventHandlers.mouseup);
|
||||||
|
@ -372,7 +363,7 @@ export var Mouse;
|
||||||
ungrab: function () {
|
ungrab: function () {
|
||||||
var c = this._target;
|
var c = this._target;
|
||||||
|
|
||||||
if (Util.isTouchDevice) {
|
if (isTouchDevice) {
|
||||||
c.removeEventListener('touchstart', this._eventHandlers.mousedown);
|
c.removeEventListener('touchstart', this._eventHandlers.mousedown);
|
||||||
window.removeEventListener('touchend', this._eventHandlers.mouseup);
|
window.removeEventListener('touchend', this._eventHandlers.mouseup);
|
||||||
c.removeEventListener('touchend', this._eventHandlers.mouseup);
|
c.removeEventListener('touchend', this._eventHandlers.mouseup);
|
||||||
|
@ -390,7 +381,7 @@ export var Mouse;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Util.make_properties(Mouse, [
|
make_properties(Mouse, [
|
||||||
['target', 'ro', 'dom'], // DOM element that captures mouse input
|
['target', 'ro', 'dom'], // DOM element that captures mouse input
|
||||||
['notify', 'ro', 'func'], // Function to call to notify whenever a mouse event is received
|
['notify', 'ro', 'func'], // Function to call to notify whenever a mouse event is received
|
||||||
['focused', 'rw', 'bool'], // Capture and send mouse clicks/movement
|
['focused', 'rw', 'bool'], // Capture and send mouse clicks/movement
|
||||||
|
@ -399,4 +390,5 @@ export var Mouse;
|
||||||
['onMouseMove', 'rw', 'func'], // Handler for mouse movement
|
['onMouseMove', 'rw', 'func'], // Handler for mouse movement
|
||||||
['touchButton', 'rw', 'int'] // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
|
['touchButton', 'rw', 'int'] // Button mask (1, 2, 4) for touch devices (0 means ignore clicks)
|
||||||
]);
|
]);
|
||||||
})();
|
|
||||||
|
export { Keyboard, Mouse };
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
var KeyTable = {
|
export default {
|
||||||
XK_VoidSymbol: 0xffffff, /* Void symbol */
|
XK_VoidSymbol: 0xffffff, /* Void symbol */
|
||||||
|
|
||||||
XK_BackSpace: 0xff08, /* Back space, back char */
|
XK_BackSpace: 0xff08, /* Back space, back char */
|
||||||
|
@ -378,5 +378,3 @@ var KeyTable = {
|
||||||
XK_thorn: 0x00fe, /* U+00FE LATIN SMALL LETTER THORN */
|
XK_thorn: 0x00fe, /* U+00FE LATIN SMALL LETTER THORN */
|
||||||
XK_ydiaeresis: 0x00ff, /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */
|
XK_ydiaeresis: 0x00ff, /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */
|
||||||
};
|
};
|
||||||
|
|
||||||
export default KeyTable;
|
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,13 +1,7 @@
|
||||||
import KeyTable from "./keysym.js";
|
import KeyTable from "./keysym.js";
|
||||||
import keysyms from "./keysymdef.js";
|
import keysyms from "./keysymdef.js";
|
||||||
|
|
||||||
|
export function substituteCodepoint(cp) {
|
||||||
var KeyboardUtil = {};
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
function substituteCodepoint(cp) {
|
|
||||||
// Any Unicode code points which do not have corresponding keysym entries
|
// Any Unicode code points which do not have corresponding keysym entries
|
||||||
// can be swapped out for another code point by adding them to this table
|
// can be swapped out for another code point by adding them to this table
|
||||||
var substitutions = {
|
var substitutions = {
|
||||||
|
@ -34,7 +28,7 @@ var KeyboardUtil = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return true if a modifier which is not the specified char modifier (and is not shift) is down
|
// Return true if a modifier which is not the specified char modifier (and is not shift) is down
|
||||||
function hasShortcutModifier(charModifier, currentModifiers) {
|
export function hasShortcutModifier(charModifier, currentModifiers) {
|
||||||
var mods = {};
|
var mods = {};
|
||||||
for (var key in currentModifiers) {
|
for (var key in currentModifiers) {
|
||||||
if (parseInt(key) !== KeyTable.XK_Shift_L) {
|
if (parseInt(key) !== KeyTable.XK_Shift_L) {
|
||||||
|
@ -57,7 +51,7 @@ var KeyboardUtil = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return true if the specified char modifier is currently down
|
// Return true if the specified char modifier is currently down
|
||||||
function hasCharModifier(charModifier, currentModifiers) {
|
export function hasCharModifier(charModifier, currentModifiers) {
|
||||||
if (charModifier.length === 0) { return false; }
|
if (charModifier.length === 0) { return false; }
|
||||||
|
|
||||||
for (var i = 0; i < charModifier.length; ++i) {
|
for (var i = 0; i < charModifier.length; ++i) {
|
||||||
|
@ -70,7 +64,7 @@ var KeyboardUtil = {};
|
||||||
|
|
||||||
// Helper object tracking modifier key state
|
// Helper object tracking modifier key state
|
||||||
// and generates fake key events to compensate if it gets out of sync
|
// and generates fake key events to compensate if it gets out of sync
|
||||||
function ModifierSync(charModifier) {
|
export function ModifierSync(charModifier) {
|
||||||
if (!charModifier) {
|
if (!charModifier) {
|
||||||
if (isMac()) {
|
if (isMac()) {
|
||||||
// on Mac, Option (AKA Alt) is used as a char modifier
|
// on Mac, Option (AKA Alt) is used as a char modifier
|
||||||
|
@ -156,7 +150,7 @@ var KeyboardUtil = {};
|
||||||
|
|
||||||
// Get a key ID from a keyboard event
|
// Get a key ID from a keyboard event
|
||||||
// May be a string or an integer depending on the available properties
|
// May be a string or an integer depending on the available properties
|
||||||
function getKey(evt){
|
export function getKey(evt){
|
||||||
if ('keyCode' in evt && 'key' in evt) {
|
if ('keyCode' in evt && 'key' in evt) {
|
||||||
return evt.key + ':' + evt.keyCode;
|
return evt.key + ':' + evt.keyCode;
|
||||||
}
|
}
|
||||||
|
@ -170,7 +164,7 @@ var KeyboardUtil = {};
|
||||||
|
|
||||||
// Get the most reliable keysym value we can get from a key event
|
// Get the most reliable keysym value we can get from a key event
|
||||||
// if char/charCode is available, prefer those, otherwise fall back to key/keyCode/which
|
// if char/charCode is available, prefer those, otherwise fall back to key/keyCode/which
|
||||||
function getKeysym(evt){
|
export function getKeysym(evt){
|
||||||
var codepoint;
|
var codepoint;
|
||||||
if (evt.char && evt.char.length === 1) {
|
if (evt.char && evt.char.length === 1) {
|
||||||
codepoint = evt.char.charCodeAt();
|
codepoint = evt.char.charCodeAt();
|
||||||
|
@ -200,7 +194,7 @@ var KeyboardUtil = {};
|
||||||
|
|
||||||
// Given a keycode, try to predict which keysym it might be.
|
// Given a keycode, try to predict which keysym it might be.
|
||||||
// If the keycode is unknown, null is returned.
|
// If the keycode is unknown, null is returned.
|
||||||
function keysymFromKeyCode(keycode, shiftPressed) {
|
export function keysymFromKeyCode(keycode, shiftPressed) {
|
||||||
if (typeof(keycode) !== 'number') {
|
if (typeof(keycode) !== 'number') {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -235,7 +229,7 @@ var KeyboardUtil = {};
|
||||||
|
|
||||||
// if the key is a known non-character key (any key which doesn't generate character data)
|
// if the key is a known non-character key (any key which doesn't generate character data)
|
||||||
// return its keysym value. Otherwise return null
|
// return its keysym value. Otherwise return null
|
||||||
function nonCharacterKey(evt) {
|
export function nonCharacterKey(evt) {
|
||||||
// evt.key not implemented yet
|
// evt.key not implemented yet
|
||||||
if (!evt.keyCode) { return null; }
|
if (!evt.keyCode) { return null; }
|
||||||
var keycode = evt.keyCode;
|
var keycode = evt.keyCode;
|
||||||
|
@ -277,17 +271,7 @@ var KeyboardUtil = {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyboardUtil.hasShortcutModifier = hasShortcutModifier;
|
export function QEMUKeyEventDecoder (modifierState, next) {
|
||||||
KeyboardUtil.hasCharModifier = hasCharModifier;
|
|
||||||
KeyboardUtil.ModifierSync = ModifierSync;
|
|
||||||
KeyboardUtil.getKey = getKey;
|
|
||||||
KeyboardUtil.getKeysym = getKeysym;
|
|
||||||
KeyboardUtil.keysymFromKeyCode = keysymFromKeyCode;
|
|
||||||
KeyboardUtil.nonCharacterKey = nonCharacterKey;
|
|
||||||
KeyboardUtil.substituteCodepoint = substituteCodepoint;
|
|
||||||
})();
|
|
||||||
|
|
||||||
KeyboardUtil.QEMUKeyEventDecoder = function(modifierState, next) {
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function sendAll(evts) {
|
function sendAll(evts) {
|
||||||
|
@ -333,7 +317,7 @@ KeyboardUtil.QEMUKeyEventDecoder = function(modifierState, next) {
|
||||||
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
|
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
|
||||||
var isShift = evt.keyCode === 0x10 || evt.key === 'Shift';
|
var isShift = evt.keyCode === 0x10 || evt.key === 'Shift';
|
||||||
|
|
||||||
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!KeyboardUtil.nonCharacterKey(evt));
|
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!nonCharacterKey(evt));
|
||||||
|
|
||||||
next(result);
|
next(result);
|
||||||
return suppress;
|
return suppress;
|
||||||
|
@ -357,7 +341,7 @@ KeyboardUtil.QEMUKeyEventDecoder = function(modifierState, next) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
KeyboardUtil.TrackQEMUKeyState = function(next) {
|
export function TrackQEMUKeyState (next) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var state = [];
|
var state = [];
|
||||||
|
|
||||||
|
@ -425,7 +409,7 @@ KeyboardUtil.TrackQEMUKeyState = function(next) {
|
||||||
// - marks each event with an 'escape' property if a modifier was down which should be "escaped"
|
// - marks each event with an 'escape' property if a modifier was down which should be "escaped"
|
||||||
// - generates a "stall" event in cases where it might be necessary to wait and see if a keypress event follows a keydown
|
// - generates a "stall" event in cases where it might be necessary to wait and see if a keypress event follows a keydown
|
||||||
// This information is collected into an object which is passed to the next() function. (one call per event)
|
// This information is collected into an object which is passed to the next() function. (one call per event)
|
||||||
KeyboardUtil.KeyEventDecoder = function(modifierState, next) {
|
export function KeyEventDecoder (modifierState, next) {
|
||||||
"use strict";
|
"use strict";
|
||||||
function sendAll(evts) {
|
function sendAll(evts) {
|
||||||
for (var i = 0; i < evts.length; ++i) {
|
for (var i = 0; i < evts.length; ++i) {
|
||||||
|
@ -434,18 +418,18 @@ KeyboardUtil.KeyEventDecoder = function(modifierState, next) {
|
||||||
}
|
}
|
||||||
function process(evt, type) {
|
function process(evt, type) {
|
||||||
var result = {type: type};
|
var result = {type: type};
|
||||||
var keyId = KeyboardUtil.getKey(evt);
|
var keyId = getKey(evt);
|
||||||
if (keyId) {
|
if (keyId) {
|
||||||
result.keyId = keyId;
|
result.keyId = keyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
var keysym = KeyboardUtil.getKeysym(evt);
|
var keysym = getKeysym(evt);
|
||||||
|
|
||||||
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
|
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
|
||||||
// Is this a case where we have to decide on the keysym right away, rather than waiting for the keypress?
|
// Is this a case where we have to decide on the keysym right away, rather than waiting for the keypress?
|
||||||
// "special" keys like enter, tab or backspace don't send keypress events,
|
// "special" keys like enter, tab or backspace don't send keypress events,
|
||||||
// and some browsers don't send keypresses at all if a modifier is down
|
// and some browsers don't send keypresses at all if a modifier is down
|
||||||
if (keysym && (type !== 'keydown' || KeyboardUtil.nonCharacterKey(evt) || hasModifier)) {
|
if (keysym && (type !== 'keydown' || nonCharacterKey(evt) || hasModifier)) {
|
||||||
result.keysym = keysym;
|
result.keysym = keysym;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -454,11 +438,11 @@ KeyboardUtil.KeyEventDecoder = function(modifierState, next) {
|
||||||
// Should we prevent the browser from handling the event?
|
// Should we prevent the browser from handling the event?
|
||||||
// Doing so on a keydown (in most browsers) prevents keypress from being generated
|
// Doing so on a keydown (in most browsers) prevents keypress from being generated
|
||||||
// so only do that if we have to.
|
// so only do that if we have to.
|
||||||
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!KeyboardUtil.nonCharacterKey(evt));
|
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!nonCharacterKey(evt));
|
||||||
|
|
||||||
// If a char modifier is down on a keydown, we need to insert a stall,
|
// If a char modifier is down on a keydown, we need to insert a stall,
|
||||||
// so VerifyCharModifier knows to wait and see if a keypress is comnig
|
// so VerifyCharModifier knows to wait and see if a keypress is comnig
|
||||||
var stall = type === 'keydown' && modifierState.activeCharModifier() && !KeyboardUtil.nonCharacterKey(evt);
|
var stall = type === 'keydown' && modifierState.activeCharModifier() && !nonCharacterKey(evt);
|
||||||
|
|
||||||
// if a char modifier is pressed, get the keys it consists of (on Windows, AltGr is equivalent to Ctrl+Alt)
|
// if a char modifier is pressed, get the keys it consists of (on Windows, AltGr is equivalent to Ctrl+Alt)
|
||||||
var active = modifierState.activeCharModifier();
|
var active = modifierState.activeCharModifier();
|
||||||
|
@ -512,7 +496,7 @@ KeyboardUtil.KeyEventDecoder = function(modifierState, next) {
|
||||||
// so when used with the '2' key, Ctrl-Alt counts as a char modifier (and should be escaped), but when used with 'D', it does not.
|
// so when used with the '2' key, Ctrl-Alt counts as a char modifier (and should be escaped), but when used with 'D', it does not.
|
||||||
// The only way we can distinguish these cases is to wait and see if a keypress event arrives
|
// The only way we can distinguish these cases is to wait and see if a keypress event arrives
|
||||||
// When we receive a "stall" event, wait a few ms before processing the next keydown. If a keypress has also arrived, merge the two
|
// When we receive a "stall" event, wait a few ms before processing the next keydown. If a keypress has also arrived, merge the two
|
||||||
KeyboardUtil.VerifyCharModifier = function(next) {
|
export function VerifyCharModifier (next) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var queue = [];
|
var queue = [];
|
||||||
var timer = null;
|
var timer = null;
|
||||||
|
@ -569,7 +553,7 @@ KeyboardUtil.VerifyCharModifier = function(next) {
|
||||||
// in some cases, a single key may produce multiple keysyms, so the corresponding keyup event must release all of these chars
|
// in some cases, a single key may produce multiple keysyms, so the corresponding keyup event must release all of these chars
|
||||||
// key repeat events should be merged into a single entry.
|
// key repeat events should be merged into a single entry.
|
||||||
// Because we can't always identify which entry a keydown or keyup event corresponds to, we sometimes have to guess
|
// Because we can't always identify which entry a keydown or keyup event corresponds to, we sometimes have to guess
|
||||||
KeyboardUtil.TrackKeyState = function(next) {
|
export function TrackKeyState (next) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var state = [];
|
var state = [];
|
||||||
|
|
||||||
|
@ -653,7 +637,7 @@ KeyboardUtil.TrackKeyState = function(next) {
|
||||||
|
|
||||||
// Handles "escaping" of modifiers: if a char modifier is used to produce a keysym (such as AltGr-2 to generate an @),
|
// Handles "escaping" of modifiers: if a char modifier is used to produce a keysym (such as AltGr-2 to generate an @),
|
||||||
// then the modifier must be "undone" before sending the @, and "redone" afterwards.
|
// then the modifier must be "undone" before sending the @, and "redone" afterwards.
|
||||||
KeyboardUtil.EscapeModifiers = function(next) {
|
export function EscapeModifiers (next) {
|
||||||
"use strict";
|
"use strict";
|
||||||
return function(evt) {
|
return function(evt) {
|
||||||
if (evt.type !== 'keydown' || evt.escape === undefined) {
|
if (evt.type !== 'keydown' || evt.escape === undefined) {
|
||||||
|
@ -674,5 +658,3 @@ KeyboardUtil.EscapeModifiers = function(next) {
|
||||||
/* jshint shadow: false */
|
/* jshint shadow: false */
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default KeyboardUtil;
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
var XtScancode = {
|
export default {
|
||||||
"Escape": 0x0001,
|
"Escape": 0x0001,
|
||||||
"Digit1": 0x0002,
|
"Digit1": 0x0002,
|
||||||
"Digit2": 0x0003,
|
"Digit2": 0x0003,
|
||||||
|
@ -147,5 +147,3 @@ var XtScancode = {
|
||||||
"LaunchMail": 0xE06C,
|
"LaunchMail": 0xE06C,
|
||||||
"MediaSelect": 0xE06D,
|
"MediaSelect": 0xE06D,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default XtScancode
|
|
||||||
|
|
185
core/rfb.js
185
core/rfb.js
|
@ -10,7 +10,10 @@
|
||||||
* (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
|
* (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Util from "./util.js";
|
import * as Log from './util/logging.js';
|
||||||
|
import _ from './util/localization.js';
|
||||||
|
import { decodeUTF8 } from './util/strings.js';
|
||||||
|
import { set_defaults, make_properties } from './util/properties.js';
|
||||||
import Display from "./display.js";
|
import Display from "./display.js";
|
||||||
import { Keyboard, Mouse } from "./input/devices.js";
|
import { Keyboard, Mouse } from "./input/devices.js";
|
||||||
import Websock from "./websock.js";
|
import Websock from "./websock.js";
|
||||||
|
@ -143,7 +146,7 @@ export default function RFB(defaults) {
|
||||||
this._qemuExtKeyEventSupported = false;
|
this._qemuExtKeyEventSupported = false;
|
||||||
|
|
||||||
// set the default value on user-facing properties
|
// set the default value on user-facing properties
|
||||||
Util.set_defaults(this, defaults, {
|
set_defaults(this, defaults, {
|
||||||
'target': 'null', // VNC display rendering Canvas object
|
'target': 'null', // VNC display rendering Canvas object
|
||||||
'focusContainer': document, // DOM element that captures keyboard input
|
'focusContainer': document, // DOM element that captures keyboard input
|
||||||
'encrypt': false, // Use TLS/SSL/wss encryption
|
'encrypt': false, // Use TLS/SSL/wss encryption
|
||||||
|
@ -172,7 +175,7 @@ export default function RFB(defaults) {
|
||||||
});
|
});
|
||||||
|
|
||||||
// main setup
|
// main setup
|
||||||
Util.Debug(">> RFB.constructor");
|
Log.Debug(">> RFB.constructor");
|
||||||
|
|
||||||
// populate encHandlers with bound versions
|
// populate encHandlers with bound versions
|
||||||
Object.keys(RFB.encodingHandlers).forEach(function (encName) {
|
Object.keys(RFB.encodingHandlers).forEach(function (encName) {
|
||||||
|
@ -192,7 +195,7 @@ export default function RFB(defaults) {
|
||||||
this._display = new Display({target: this._target,
|
this._display = new Display({target: this._target,
|
||||||
onFlush: this._onFlush.bind(this)});
|
onFlush: this._onFlush.bind(this)});
|
||||||
} catch (exc) {
|
} catch (exc) {
|
||||||
Util.Error("Display exception: " + exc);
|
Log.Error("Display exception: " + exc);
|
||||||
throw exc;
|
throw exc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -210,13 +213,13 @@ export default function RFB(defaults) {
|
||||||
if ((this._rfb_connection_state === 'connecting') &&
|
if ((this._rfb_connection_state === 'connecting') &&
|
||||||
(this._rfb_init_state === '')) {
|
(this._rfb_init_state === '')) {
|
||||||
this._rfb_init_state = 'ProtocolVersion';
|
this._rfb_init_state = 'ProtocolVersion';
|
||||||
Util.Debug("Starting VNC handshake");
|
Log.Debug("Starting VNC handshake");
|
||||||
} else {
|
} else {
|
||||||
this._fail("Unexpected server connection");
|
this._fail("Unexpected server connection");
|
||||||
}
|
}
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
this._sock.on('close', function (e) {
|
this._sock.on('close', function (e) {
|
||||||
Util.Warn("WebSocket on-close event");
|
Log.Warn("WebSocket on-close event");
|
||||||
var msg = "";
|
var msg = "";
|
||||||
if (e.code) {
|
if (e.code) {
|
||||||
msg = " (code: " + e.code;
|
msg = " (code: " + e.code;
|
||||||
|
@ -249,21 +252,18 @@ export default function RFB(defaults) {
|
||||||
this._sock.off('close');
|
this._sock.off('close');
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
this._sock.on('error', function (e) {
|
this._sock.on('error', function (e) {
|
||||||
Util.Warn("WebSocket on-error event");
|
Log.Warn("WebSocket on-error event");
|
||||||
});
|
});
|
||||||
|
|
||||||
this._init_vars();
|
this._init_vars();
|
||||||
this._cleanup();
|
this._cleanup();
|
||||||
|
|
||||||
var rmode = this._display.get_render_mode();
|
var rmode = this._display.get_render_mode();
|
||||||
Util.Info("Using native WebSockets, render mode: " + rmode);
|
Log.Info("Using native WebSockets, render mode: " + rmode);
|
||||||
|
|
||||||
Util.Debug("<< RFB.constructor");
|
Log.Debug("<< RFB.constructor");
|
||||||
};
|
};
|
||||||
|
|
||||||
(function() {
|
|
||||||
var _ = Util.Localisation.get;
|
|
||||||
|
|
||||||
RFB.prototype = {
|
RFB.prototype = {
|
||||||
// Public methods
|
// Public methods
|
||||||
connect: function (host, port, password, path) {
|
connect: function (host, port, password, path) {
|
||||||
|
@ -296,7 +296,7 @@ export default function RFB(defaults) {
|
||||||
|
|
||||||
sendCtrlAltDel: function () {
|
sendCtrlAltDel: function () {
|
||||||
if (this._rfb_connection_state !== 'connected' || this._view_only) { return false; }
|
if (this._rfb_connection_state !== 'connected' || this._view_only) { return false; }
|
||||||
Util.Info("Sending Ctrl-Alt-Del");
|
Log.Info("Sending Ctrl-Alt-Del");
|
||||||
|
|
||||||
RFB.messages.keyEvent(this._sock, KeyTable.XK_Control_L, 1);
|
RFB.messages.keyEvent(this._sock, KeyTable.XK_Control_L, 1);
|
||||||
RFB.messages.keyEvent(this._sock, KeyTable.XK_Alt_L, 1);
|
RFB.messages.keyEvent(this._sock, KeyTable.XK_Alt_L, 1);
|
||||||
|
@ -309,7 +309,7 @@ export default function RFB(defaults) {
|
||||||
|
|
||||||
xvpOp: function (ver, op) {
|
xvpOp: function (ver, op) {
|
||||||
if (this._rfb_xvp_ver < ver) { return false; }
|
if (this._rfb_xvp_ver < ver) { return false; }
|
||||||
Util.Info("Sending XVP operation " + op + " (version " + ver + ")");
|
Log.Info("Sending XVP operation " + op + " (version " + ver + ")");
|
||||||
this._sock.send_string("\xFA\x00" + String.fromCharCode(ver) + String.fromCharCode(op));
|
this._sock.send_string("\xFA\x00" + String.fromCharCode(ver) + String.fromCharCode(op));
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
@ -331,10 +331,10 @@ export default function RFB(defaults) {
|
||||||
sendKey: function (keysym, down) {
|
sendKey: function (keysym, down) {
|
||||||
if (this._rfb_connection_state !== 'connected' || this._view_only) { return false; }
|
if (this._rfb_connection_state !== 'connected' || this._view_only) { return false; }
|
||||||
if (typeof down !== 'undefined') {
|
if (typeof down !== 'undefined') {
|
||||||
Util.Info("Sending keysym (" + (down ? "down" : "up") + "): " + keysym);
|
Log.Info("Sending keysym (" + (down ? "down" : "up") + "): " + keysym);
|
||||||
RFB.messages.keyEvent(this._sock, keysym, down ? 1 : 0);
|
RFB.messages.keyEvent(this._sock, keysym, down ? 1 : 0);
|
||||||
} else {
|
} else {
|
||||||
Util.Info("Sending keysym (down + up): " + keysym);
|
Log.Info("Sending keysym (down + up): " + keysym);
|
||||||
RFB.messages.keyEvent(this._sock, keysym, 1);
|
RFB.messages.keyEvent(this._sock, keysym, 1);
|
||||||
RFB.messages.keyEvent(this._sock, keysym, 0);
|
RFB.messages.keyEvent(this._sock, keysym, 0);
|
||||||
}
|
}
|
||||||
|
@ -342,9 +342,7 @@ export default function RFB(defaults) {
|
||||||
},
|
},
|
||||||
|
|
||||||
clipboardPasteFrom: function (text) {
|
clipboardPasteFrom: function (text) {
|
||||||
if (this._rfb_connection_state !== 'connected' || this._view_only) {
|
if (this._rfb_connection_state !== 'connected' || this._view_only) { return; }
|
||||||
return;
|
|
||||||
}
|
|
||||||
RFB.messages.clientCutText(this._sock, text);
|
RFB.messages.clientCutText(this._sock, text);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -370,7 +368,7 @@ export default function RFB(defaults) {
|
||||||
// Private methods
|
// Private methods
|
||||||
|
|
||||||
_connect: function () {
|
_connect: function () {
|
||||||
Util.Debug(">> RFB.connect");
|
Log.Debug(">> RFB.connect");
|
||||||
this._init_vars();
|
this._init_vars();
|
||||||
|
|
||||||
var uri;
|
var uri;
|
||||||
|
@ -381,7 +379,7 @@ export default function RFB(defaults) {
|
||||||
}
|
}
|
||||||
|
|
||||||
uri += '://' + this._rfb_host + ':' + this._rfb_port + '/' + this._rfb_path;
|
uri += '://' + this._rfb_host + ':' + this._rfb_port + '/' + this._rfb_path;
|
||||||
Util.Info("connecting to " + uri);
|
Log.Info("connecting to " + uri);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// WebSocket.onopen transitions to the RFB init states
|
// WebSocket.onopen transitions to the RFB init states
|
||||||
|
@ -394,15 +392,15 @@ export default function RFB(defaults) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Util.Debug("<< RFB.connect");
|
Log.Debug("<< RFB.connect");
|
||||||
},
|
},
|
||||||
|
|
||||||
_disconnect: function () {
|
_disconnect: function () {
|
||||||
Util.Debug(">> RFB.disconnect");
|
Log.Debug(">> RFB.disconnect");
|
||||||
this._cleanup();
|
this._cleanup();
|
||||||
this._sock.close();
|
this._sock.close();
|
||||||
this._print_stats();
|
this._print_stats();
|
||||||
Util.Debug("<< RFB.disconnect");
|
Log.Debug("<< RFB.disconnect");
|
||||||
},
|
},
|
||||||
|
|
||||||
_init_vars: function () {
|
_init_vars: function () {
|
||||||
|
@ -428,19 +426,19 @@ export default function RFB(defaults) {
|
||||||
},
|
},
|
||||||
|
|
||||||
_print_stats: function () {
|
_print_stats: function () {
|
||||||
Util.Info("Encoding stats for this connection:");
|
Log.Info("Encoding stats for this connection:");
|
||||||
var i, s;
|
var i, s;
|
||||||
for (i = 0; i < this._encodings.length; i++) {
|
for (i = 0; i < this._encodings.length; i++) {
|
||||||
s = this._encStats[this._encodings[i][1]];
|
s = this._encStats[this._encodings[i][1]];
|
||||||
if (s[0] + s[1] > 0) {
|
if (s[0] + s[1] > 0) {
|
||||||
Util.Info(" " + this._encodings[i][0] + ": " + s[0] + " rects");
|
Log.Info(" " + this._encodings[i][0] + ": " + s[0] + " rects");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Util.Info("Encoding stats since page load:");
|
Log.Info("Encoding stats since page load:");
|
||||||
for (i = 0; i < this._encodings.length; i++) {
|
for (i = 0; i < this._encodings.length; i++) {
|
||||||
s = this._encStats[this._encodings[i][1]];
|
s = this._encStats[this._encodings[i][1]];
|
||||||
Util.Info(" " + this._encodings[i][0] + ": " + s[1] + " rects");
|
Log.Info(" " + this._encodings[i][0] + ": " + s[1] + " rects");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -448,7 +446,7 @@ export default function RFB(defaults) {
|
||||||
if (!this._view_only) { this._keyboard.ungrab(); }
|
if (!this._view_only) { this._keyboard.ungrab(); }
|
||||||
if (!this._view_only) { this._mouse.ungrab(); }
|
if (!this._view_only) { this._mouse.ungrab(); }
|
||||||
this._display.defaultCursor();
|
this._display.defaultCursor();
|
||||||
if (Util.get_logging() !== 'debug') {
|
if (Log.get_logging() !== 'debug') {
|
||||||
// Show noVNC logo on load and when disconnected, unless in
|
// Show noVNC logo on load and when disconnected, unless in
|
||||||
// debug mode
|
// debug mode
|
||||||
this._display.clear();
|
this._display.clear();
|
||||||
|
@ -466,13 +464,13 @@ export default function RFB(defaults) {
|
||||||
var oldstate = this._rfb_connection_state;
|
var oldstate = this._rfb_connection_state;
|
||||||
|
|
||||||
if (state === oldstate) {
|
if (state === oldstate) {
|
||||||
Util.Debug("Already in state '" + state + "', ignoring");
|
Log.Debug("Already in state '" + state + "', ignoring");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The 'disconnected' state is permanent for each RFB object
|
// The 'disconnected' state is permanent for each RFB object
|
||||||
if (oldstate === 'disconnected') {
|
if (oldstate === 'disconnected') {
|
||||||
Util.Error("Tried changing state of a disconnected RFB object");
|
Log.Error("Tried changing state of a disconnected RFB object");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,7 +478,7 @@ export default function RFB(defaults) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case 'connected':
|
case 'connected':
|
||||||
if (oldstate !== 'connecting') {
|
if (oldstate !== 'connecting') {
|
||||||
Util.Error("Bad transition to connected state, " +
|
Log.Error("Bad transition to connected state, " +
|
||||||
"previous connection state: " + oldstate);
|
"previous connection state: " + oldstate);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -488,7 +486,7 @@ export default function RFB(defaults) {
|
||||||
|
|
||||||
case 'disconnected':
|
case 'disconnected':
|
||||||
if (oldstate !== 'disconnecting') {
|
if (oldstate !== 'disconnecting') {
|
||||||
Util.Error("Bad transition to disconnected state, " +
|
Log.Error("Bad transition to disconnected state, " +
|
||||||
"previous connection state: " + oldstate);
|
"previous connection state: " + oldstate);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -496,7 +494,7 @@ export default function RFB(defaults) {
|
||||||
|
|
||||||
case 'connecting':
|
case 'connecting':
|
||||||
if (oldstate !== '') {
|
if (oldstate !== '') {
|
||||||
Util.Error("Bad transition to connecting state, " +
|
Log.Error("Bad transition to connecting state, " +
|
||||||
"previous connection state: " + oldstate);
|
"previous connection state: " + oldstate);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -504,14 +502,14 @@ export default function RFB(defaults) {
|
||||||
|
|
||||||
case 'disconnecting':
|
case 'disconnecting':
|
||||||
if (oldstate !== 'connected' && oldstate !== 'connecting') {
|
if (oldstate !== 'connected' && oldstate !== 'connecting') {
|
||||||
Util.Error("Bad transition to disconnecting state, " +
|
Log.Error("Bad transition to disconnecting state, " +
|
||||||
"previous connection state: " + oldstate);
|
"previous connection state: " + oldstate);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
Util.Error("Unknown connection state: " + state);
|
Log.Error("Unknown connection state: " + state);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -521,10 +519,10 @@ export default function RFB(defaults) {
|
||||||
this._onUpdateState(this, state, oldstate);
|
this._onUpdateState(this, state, oldstate);
|
||||||
|
|
||||||
var smsg = "New state '" + state + "', was '" + oldstate + "'.";
|
var smsg = "New state '" + state + "', was '" + oldstate + "'.";
|
||||||
Util.Debug(smsg);
|
Log.Debug(smsg);
|
||||||
|
|
||||||
if (this._disconnTimer && state !== 'disconnecting') {
|
if (this._disconnTimer && state !== 'disconnecting') {
|
||||||
Util.Debug("Clearing disconnect timer");
|
Log.Debug("Clearing disconnect timer");
|
||||||
clearTimeout(this._disconnTimer);
|
clearTimeout(this._disconnTimer);
|
||||||
this._disconnTimer = null;
|
this._disconnTimer = null;
|
||||||
|
|
||||||
|
@ -571,16 +569,16 @@ export default function RFB(defaults) {
|
||||||
}
|
}
|
||||||
switch (this._rfb_connection_state) {
|
switch (this._rfb_connection_state) {
|
||||||
case 'disconnecting':
|
case 'disconnecting':
|
||||||
Util.Error("Failed when disconnecting: " + fullmsg);
|
Log.Error("Failed when disconnecting: " + fullmsg);
|
||||||
break;
|
break;
|
||||||
case 'connected':
|
case 'connected':
|
||||||
Util.Error("Failed while connected: " + fullmsg);
|
Log.Error("Failed while connected: " + fullmsg);
|
||||||
break;
|
break;
|
||||||
case 'connecting':
|
case 'connecting':
|
||||||
Util.Error("Failed when connecting: " + fullmsg);
|
Log.Error("Failed when connecting: " + fullmsg);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Util.Error("RFB failure: " + fullmsg);
|
Log.Error("RFB failure: " + fullmsg);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
this._rfb_disconnect_reason = msg; //This is sent to the UI
|
this._rfb_disconnect_reason = msg; //This is sent to the UI
|
||||||
|
@ -605,10 +603,10 @@ export default function RFB(defaults) {
|
||||||
case 'normal':
|
case 'normal':
|
||||||
case 'warn':
|
case 'warn':
|
||||||
case 'error':
|
case 'error':
|
||||||
Util.Debug("Notification[" + level + "]:" + msg);
|
Log.Debug("Notification[" + level + "]:" + msg);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Util.Error("Invalid notification level: " + level);
|
Log.Error("Invalid notification level: " + level);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -621,13 +619,13 @@ export default function RFB(defaults) {
|
||||||
|
|
||||||
_handle_message: function () {
|
_handle_message: function () {
|
||||||
if (this._sock.rQlen() === 0) {
|
if (this._sock.rQlen() === 0) {
|
||||||
Util.Warn("handle_message called on an empty receive queue");
|
Log.Warn("handle_message called on an empty receive queue");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (this._rfb_connection_state) {
|
switch (this._rfb_connection_state) {
|
||||||
case 'disconnected':
|
case 'disconnected':
|
||||||
Util.Error("Got data while disconnected");
|
Log.Error("Got data while disconnected");
|
||||||
break;
|
break;
|
||||||
case 'connected':
|
case 'connected':
|
||||||
while (true) {
|
while (true) {
|
||||||
|
@ -658,7 +656,7 @@ export default function RFB(defaults) {
|
||||||
var keysym = keyevent.keysym;
|
var keysym = keyevent.keysym;
|
||||||
RFB.messages.QEMUExtendedKeyEvent(this._sock, keysym, down, scancode);
|
RFB.messages.QEMUExtendedKeyEvent(this._sock, keysym, down, scancode);
|
||||||
} else {
|
} else {
|
||||||
Util.Error('Unable to find a xt scancode for code = ' + keyevent.code);
|
Log.Error('Unable to find a xt scancode for code = ' + keyevent.code);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
keysym = keyevent.keysym.keysym;
|
keysym = keyevent.keysym.keysym;
|
||||||
|
@ -734,7 +732,7 @@ export default function RFB(defaults) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var sversion = this._sock.rQshiftStr(12).substr(4, 7);
|
var sversion = this._sock.rQshiftStr(12).substr(4, 7);
|
||||||
Util.Info("Server ProtocolVersion: " + sversion);
|
Log.Info("Server ProtocolVersion: " + sversion);
|
||||||
var is_repeater = 0;
|
var is_repeater = 0;
|
||||||
switch (sversion) {
|
switch (sversion) {
|
||||||
case "000.000": // UltraVNC repeater
|
case "000.000": // UltraVNC repeater
|
||||||
|
@ -775,12 +773,23 @@ export default function RFB(defaults) {
|
||||||
var cversion = "00" + parseInt(this._rfb_version, 10) +
|
var cversion = "00" + parseInt(this._rfb_version, 10) +
|
||||||
".00" + ((this._rfb_version * 10) % 10);
|
".00" + ((this._rfb_version * 10) % 10);
|
||||||
this._sock.send_string("RFB " + cversion + "\n");
|
this._sock.send_string("RFB " + cversion + "\n");
|
||||||
Util.Debug('Sent ProtocolVersion: ' + cversion);
|
Log.Debug('Sent ProtocolVersion: ' + cversion);
|
||||||
|
|
||||||
this._rfb_init_state = 'Security';
|
this._rfb_init_state = 'Security';
|
||||||
},
|
},
|
||||||
|
|
||||||
_negotiate_security: function () {
|
_negotiate_security: function () {
|
||||||
|
// Polyfill since IE and PhantomJS doesn't have
|
||||||
|
// TypedArray.includes()
|
||||||
|
function includes(item, array) {
|
||||||
|
for (var i = 0; i < array.length; i++) {
|
||||||
|
if (array[i] === item) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (this._rfb_version >= 3.7) {
|
if (this._rfb_version >= 3.7) {
|
||||||
// Server sends supported list, client decides
|
// Server sends supported list, client decides
|
||||||
var num_types = this._sock.rQshift8();
|
var num_types = this._sock.rQshift8();
|
||||||
|
@ -794,18 +803,7 @@ export default function RFB(defaults) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var types = this._sock.rQshiftBytes(num_types);
|
var types = this._sock.rQshiftBytes(num_types);
|
||||||
Util.Debug("Server security types: " + types);
|
Log.Debug("Server security types: " + types);
|
||||||
|
|
||||||
// Polyfill since IE and PhantomJS doesn't have
|
|
||||||
// TypedArray.includes()
|
|
||||||
function includes(item, array) {
|
|
||||||
for (var i = 0; i < array.length; i++) {
|
|
||||||
if (array[i] === item) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for each auth in preferred order
|
// Look for each auth in preferred order
|
||||||
this._rfb_auth_scheme = 0;
|
this._rfb_auth_scheme = 0;
|
||||||
|
@ -830,7 +828,7 @@ export default function RFB(defaults) {
|
||||||
}
|
}
|
||||||
|
|
||||||
this._rfb_init_state = 'Authentication';
|
this._rfb_init_state = 'Authentication';
|
||||||
Util.Debug('Authenticating using scheme: ' + this._rfb_auth_scheme);
|
Log.Debug('Authenticating using scheme: ' + this._rfb_auth_scheme);
|
||||||
|
|
||||||
return this._init_msg(); // jump to authentication
|
return this._init_msg(); // jump to authentication
|
||||||
},
|
},
|
||||||
|
@ -999,7 +997,7 @@ export default function RFB(defaults) {
|
||||||
switch (this._sock.rQshift32()) {
|
switch (this._sock.rQshift32()) {
|
||||||
case 0: // OK
|
case 0: // OK
|
||||||
this._rfb_init_state = 'ClientInitialisation';
|
this._rfb_init_state = 'ClientInitialisation';
|
||||||
Util.Debug('Authentication OK');
|
Log.Debug('Authentication OK');
|
||||||
return this._init_msg();
|
return this._init_msg();
|
||||||
case 1: // failed
|
case 1: // failed
|
||||||
if (this._rfb_version >= 3.8) {
|
if (this._rfb_version >= 3.8) {
|
||||||
|
@ -1047,7 +1045,7 @@ export default function RFB(defaults) {
|
||||||
/* Connection name/title */
|
/* Connection name/title */
|
||||||
var name_length = this._sock.rQshift32();
|
var name_length = this._sock.rQshift32();
|
||||||
if (this._sock.rQwait('server init name', name_length, 24)) { return false; }
|
if (this._sock.rQwait('server init name', name_length, 24)) { return false; }
|
||||||
this._fb_name = Util.decodeUTF8(this._sock.rQshiftStr(name_length));
|
this._fb_name = decodeUTF8(this._sock.rQshiftStr(name_length));
|
||||||
|
|
||||||
if (this._rfb_tightvnc) {
|
if (this._rfb_tightvnc) {
|
||||||
if (this._sock.rQwait('TightVNC extended server init header', 8, 24 + name_length)) { return false; }
|
if (this._sock.rQwait('TightVNC extended server init header', 8, 24 + name_length)) { return false; }
|
||||||
|
@ -1075,7 +1073,7 @@ export default function RFB(defaults) {
|
||||||
|
|
||||||
// NB(directxman12): these are down here so that we don't run them multiple times
|
// NB(directxman12): these are down here so that we don't run them multiple times
|
||||||
// if we backtrack
|
// if we backtrack
|
||||||
Util.Info("Screen: " + this._fb_width + "x" + this._fb_height +
|
Log.Info("Screen: " + this._fb_width + "x" + this._fb_height +
|
||||||
", bpp: " + bpp + ", depth: " + depth +
|
", bpp: " + bpp + ", depth: " + depth +
|
||||||
", big_endian: " + big_endian +
|
", big_endian: " + big_endian +
|
||||||
", true_color: " + true_color +
|
", true_color: " + true_color +
|
||||||
|
@ -1087,22 +1085,22 @@ export default function RFB(defaults) {
|
||||||
", blue_shift: " + blue_shift);
|
", blue_shift: " + blue_shift);
|
||||||
|
|
||||||
if (big_endian !== 0) {
|
if (big_endian !== 0) {
|
||||||
Util.Warn("Server native endian is not little endian");
|
Log.Warn("Server native endian is not little endian");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (red_shift !== 16) {
|
if (red_shift !== 16) {
|
||||||
Util.Warn("Server native red-shift is not 16");
|
Log.Warn("Server native red-shift is not 16");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (blue_shift !== 0) {
|
if (blue_shift !== 0) {
|
||||||
Util.Warn("Server native blue-shift is not 0");
|
Log.Warn("Server native blue-shift is not 0");
|
||||||
}
|
}
|
||||||
|
|
||||||
// we're past the point where we could backtrack, so it's safe to call this
|
// we're past the point where we could backtrack, so it's safe to call this
|
||||||
this._onDesktopName(this, this._fb_name);
|
this._onDesktopName(this, this._fb_name);
|
||||||
|
|
||||||
if (this._true_color && this._fb_name === "Intel(r) AMT KVM") {
|
if (this._true_color && this._fb_name === "Intel(r) AMT KVM") {
|
||||||
Util.Warn("Intel AMT KVM only supports 8/16 bit depths. Disabling true color");
|
Log.Warn("Intel AMT KVM only supports 8/16 bit depths. Disabling true color");
|
||||||
this._true_color = false;
|
this._true_color = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1169,7 +1167,7 @@ export default function RFB(defaults) {
|
||||||
},
|
},
|
||||||
|
|
||||||
_handle_set_colour_map_msg: function () {
|
_handle_set_colour_map_msg: function () {
|
||||||
Util.Debug("SetColorMapEntries");
|
Log.Debug("SetColorMapEntries");
|
||||||
this._sock.rQskip8(); // Padding
|
this._sock.rQskip8(); // Padding
|
||||||
|
|
||||||
var first_colour = this._sock.rQshift16();
|
var first_colour = this._sock.rQshift16();
|
||||||
|
@ -1182,14 +1180,14 @@ export default function RFB(defaults) {
|
||||||
var blue = parseInt(this._sock.rQshift16() / 256, 10);
|
var blue = parseInt(this._sock.rQshift16() / 256, 10);
|
||||||
this._display.set_colourMap([blue, green, red], first_colour + c);
|
this._display.set_colourMap([blue, green, red], first_colour + c);
|
||||||
}
|
}
|
||||||
Util.Debug("colourMap: " + this._display.get_colourMap());
|
Log.Debug("colourMap: " + this._display.get_colourMap());
|
||||||
Util.Info("Registered " + num_colours + " colourMap entries");
|
Log.Info("Registered " + num_colours + " colourMap entries");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
_handle_server_cut_text: function () {
|
_handle_server_cut_text: function () {
|
||||||
Util.Debug("ServerCutText");
|
Log.Debug("ServerCutText");
|
||||||
if (this._view_only) { return true; }
|
if (this._view_only) { return true; }
|
||||||
|
|
||||||
if (this._sock.rQwait("ServerCutText header", 7, 1)) { return false; }
|
if (this._sock.rQwait("ServerCutText header", 7, 1)) { return false; }
|
||||||
|
@ -1212,7 +1210,7 @@ export default function RFB(defaults) {
|
||||||
if (this._sock.rQwait("ServerFence payload", length, 9)) { return false; }
|
if (this._sock.rQwait("ServerFence payload", length, 9)) { return false; }
|
||||||
|
|
||||||
if (length > 64) {
|
if (length > 64) {
|
||||||
Util.Warn("Bad payload length (" + length + ") in fence response");
|
Log.Warn("Bad payload length (" + length + ") in fence response");
|
||||||
length = 64;
|
length = 64;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1254,12 +1252,12 @@ export default function RFB(defaults) {
|
||||||
|
|
||||||
switch (xvp_msg) {
|
switch (xvp_msg) {
|
||||||
case 0: // XVP_FAIL
|
case 0: // XVP_FAIL
|
||||||
Util.Error("Operation Failed");
|
Log.Error("Operation Failed");
|
||||||
this._notification("XVP Operation Failed", 'error');
|
this._notification("XVP Operation Failed", 'error');
|
||||||
break;
|
break;
|
||||||
case 1: // XVP_INIT
|
case 1: // XVP_INIT
|
||||||
this._rfb_xvp_ver = xvp_ver;
|
this._rfb_xvp_ver = xvp_ver;
|
||||||
Util.Info("XVP extensions enabled (version " + this._rfb_xvp_ver + ")");
|
Log.Info("XVP extensions enabled (version " + this._rfb_xvp_ver + ")");
|
||||||
this._onXvpInit(this._rfb_xvp_ver);
|
this._onXvpInit(this._rfb_xvp_ver);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -1293,7 +1291,7 @@ export default function RFB(defaults) {
|
||||||
return this._handle_set_colour_map_msg();
|
return this._handle_set_colour_map_msg();
|
||||||
|
|
||||||
case 2: // Bell
|
case 2: // Bell
|
||||||
Util.Debug("Bell");
|
Log.Debug("Bell");
|
||||||
this._onBell(this);
|
this._onBell(this);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
@ -1307,7 +1305,7 @@ export default function RFB(defaults) {
|
||||||
if (first) {
|
if (first) {
|
||||||
this._enabledContinuousUpdates = true;
|
this._enabledContinuousUpdates = true;
|
||||||
this._updateContinuousUpdates();
|
this._updateContinuousUpdates();
|
||||||
Util.Info("Enabling continuous updates.");
|
Log.Info("Enabling continuous updates.");
|
||||||
} else {
|
} else {
|
||||||
// FIXME: We need to send a framebufferupdaterequest here
|
// FIXME: We need to send a framebufferupdaterequest here
|
||||||
// if we add support for turning off continuous updates
|
// if we add support for turning off continuous updates
|
||||||
|
@ -1322,7 +1320,7 @@ export default function RFB(defaults) {
|
||||||
|
|
||||||
default:
|
default:
|
||||||
this._fail("Unexpected server message", "Type:" + msg_type);
|
this._fail("Unexpected server message", "Type:" + msg_type);
|
||||||
Util.Debug("sock.rQslice(0, 30): " + this._sock.rQslice(0, 30));
|
Log.Debug("sock.rQslice(0, 30): " + this._sock.rQslice(0, 30));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -1347,7 +1345,7 @@ export default function RFB(defaults) {
|
||||||
this._timing.cur_fbu = 0;
|
this._timing.cur_fbu = 0;
|
||||||
if (this._timing.fbu_rt_start > 0) {
|
if (this._timing.fbu_rt_start > 0) {
|
||||||
now = (new Date()).getTime();
|
now = (new Date()).getTime();
|
||||||
Util.Info("First FBU latency: " + (now - this._timing.fbu_rt_start));
|
Log.Info("First FBU latency: " + (now - this._timing.fbu_rt_start));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the previous frame is fully rendered first
|
// Make sure the previous frame is fully rendered first
|
||||||
|
@ -1407,7 +1405,7 @@ export default function RFB(defaults) {
|
||||||
this._timing.fbu_rt_start > 0) {
|
this._timing.fbu_rt_start > 0) {
|
||||||
this._timing.full_fbu_total += this._timing.cur_fbu;
|
this._timing.full_fbu_total += this._timing.cur_fbu;
|
||||||
this._timing.full_fbu_cnt++;
|
this._timing.full_fbu_cnt++;
|
||||||
Util.Info("Timing of full FBU, curr: " +
|
Log.Info("Timing of full FBU, curr: " +
|
||||||
this._timing.cur_fbu + ", total: " +
|
this._timing.cur_fbu + ", total: " +
|
||||||
this._timing.full_fbu_total + ", cnt: " +
|
this._timing.full_fbu_total + ", cnt: " +
|
||||||
this._timing.full_fbu_cnt + ", avg: " +
|
this._timing.full_fbu_cnt + ", avg: " +
|
||||||
|
@ -1418,7 +1416,7 @@ export default function RFB(defaults) {
|
||||||
var fbu_rt_diff = now - this._timing.fbu_rt_start;
|
var fbu_rt_diff = now - this._timing.fbu_rt_start;
|
||||||
this._timing.fbu_rt_total += fbu_rt_diff;
|
this._timing.fbu_rt_total += fbu_rt_diff;
|
||||||
this._timing.fbu_rt_cnt++;
|
this._timing.fbu_rt_cnt++;
|
||||||
Util.Info("full FBU round-trip, cur: " +
|
Log.Info("full FBU round-trip, cur: " +
|
||||||
fbu_rt_diff + ", total: " +
|
fbu_rt_diff + ", total: " +
|
||||||
this._timing.fbu_rt_total + ", cnt: " +
|
this._timing.fbu_rt_total + ", cnt: " +
|
||||||
this._timing.fbu_rt_cnt + ", avg: " +
|
this._timing.fbu_rt_cnt + ", avg: " +
|
||||||
|
@ -1449,7 +1447,7 @@ export default function RFB(defaults) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Util.make_properties(RFB, [
|
make_properties(RFB, [
|
||||||
['target', 'wo', 'dom'], // VNC display rendering Canvas object
|
['target', 'wo', 'dom'], // VNC display rendering Canvas object
|
||||||
['focusContainer', 'wo', 'dom'], // DOM element that captures keyboard input
|
['focusContainer', 'wo', 'dom'], // DOM element that captures keyboard input
|
||||||
['encrypt', 'rw', 'bool'], // Use TLS/SSL/wss encryption
|
['encrypt', 'rw', 'bool'], // Use TLS/SSL/wss encryption
|
||||||
|
@ -1485,7 +1483,7 @@ export default function RFB(defaults) {
|
||||||
if (this._display.get_cursor_uri()) {
|
if (this._display.get_cursor_uri()) {
|
||||||
this._local_cursor = true;
|
this._local_cursor = true;
|
||||||
} else {
|
} else {
|
||||||
Util.Warn("Browser does not support local cursor");
|
Log.Warn("Browser does not support local cursor");
|
||||||
this._display.disableLocalCursor();
|
this._display.disableLocalCursor();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1748,10 +1746,10 @@ export default function RFB(defaults) {
|
||||||
var i, j = offset + 4, cnt = 0;
|
var i, j = offset + 4, cnt = 0;
|
||||||
for (i = 0; i < encodings.length; i++) {
|
for (i = 0; i < encodings.length; i++) {
|
||||||
if (encodings[i][0] === "Cursor" && !local_cursor) {
|
if (encodings[i][0] === "Cursor" && !local_cursor) {
|
||||||
Util.Debug("Skipping Cursor pseudo-encoding");
|
Log.Debug("Skipping Cursor pseudo-encoding");
|
||||||
} else if (encodings[i][0] === "TIGHT" && !true_color) {
|
} else if (encodings[i][0] === "TIGHT" && !true_color) {
|
||||||
// TODO: remove this when we have tight+non-true-color
|
// TODO: remove this when we have tight+non-true-color
|
||||||
Util.Warn("Skipping tight as it is only supported with true color");
|
Log.Warn("Skipping tight as it is only supported with true color");
|
||||||
} else {
|
} else {
|
||||||
var enc = encodings[i][1];
|
var enc = encodings[i][1];
|
||||||
buff[j] = enc >> 24;
|
buff[j] = enc >> 24;
|
||||||
|
@ -1936,7 +1934,7 @@ export default function RFB(defaults) {
|
||||||
if (this._FBU.subencoding === 0) {
|
if (this._FBU.subencoding === 0) {
|
||||||
if (this._FBU.lastsubencoding & 0x01) {
|
if (this._FBU.lastsubencoding & 0x01) {
|
||||||
// Weird: ignore blanks are RAW
|
// Weird: ignore blanks are RAW
|
||||||
Util.Debug(" Ignoring blank after RAW");
|
Log.Debug(" Ignoring blank after RAW");
|
||||||
} else {
|
} else {
|
||||||
this._display.fillRect(x, y, w, h, this._FBU.background);
|
this._display.fillRect(x, y, w, h, this._FBU.background);
|
||||||
}
|
}
|
||||||
|
@ -2048,14 +2046,14 @@ export default function RFB(defaults) {
|
||||||
for (var i = 0; i < 4; i++) {
|
for (var i = 0; i < 4; i++) {
|
||||||
if ((resetStreams >> i) & 1) {
|
if ((resetStreams >> i) & 1) {
|
||||||
this._FBU.zlibs[i].reset();
|
this._FBU.zlibs[i].reset();
|
||||||
Util.Info("Reset zlib stream " + i);
|
Log.Info("Reset zlib stream " + i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//var uncompressed = this._FBU.zlibs[streamId].uncompress(data, 0);
|
//var uncompressed = this._FBU.zlibs[streamId].uncompress(data, 0);
|
||||||
var uncompressed = this._FBU.zlibs[streamId].inflate(data, true, expected);
|
var uncompressed = this._FBU.zlibs[streamId].inflate(data, true, expected);
|
||||||
/*if (uncompressed.status !== 0) {
|
/*if (uncompressed.status !== 0) {
|
||||||
Util.Error("Invalid data in zlib stream");
|
Log.Error("Invalid data in zlib stream");
|
||||||
}*/
|
}*/
|
||||||
|
|
||||||
//return uncompressed.data;
|
//return uncompressed.data;
|
||||||
|
@ -2431,7 +2429,7 @@ export default function RFB(defaults) {
|
||||||
},
|
},
|
||||||
|
|
||||||
Cursor: function () {
|
Cursor: function () {
|
||||||
Util.Debug(">> set_cursor");
|
Log.Debug(">> set_cursor");
|
||||||
var x = this._FBU.x; // hotspot-x
|
var x = this._FBU.x; // hotspot-x
|
||||||
var y = this._FBU.y; // hotspot-y
|
var y = this._FBU.y; // hotspot-y
|
||||||
var w = this._FBU.width;
|
var w = this._FBU.width;
|
||||||
|
@ -2450,7 +2448,7 @@ export default function RFB(defaults) {
|
||||||
this._FBU.bytes = 0;
|
this._FBU.bytes = 0;
|
||||||
this._FBU.rects--;
|
this._FBU.rects--;
|
||||||
|
|
||||||
Util.Debug("<< set_cursor");
|
Log.Debug("<< set_cursor");
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -2465,11 +2463,10 @@ export default function RFB(defaults) {
|
||||||
},
|
},
|
||||||
|
|
||||||
JPEG_quality_lo: function () {
|
JPEG_quality_lo: function () {
|
||||||
Util.Error("Server sent jpeg_quality pseudo-encoding");
|
Log.Error("Server sent jpeg_quality pseudo-encoding");
|
||||||
},
|
},
|
||||||
|
|
||||||
compress_lo: function () {
|
compress_lo: function () {
|
||||||
Util.Error("Server sent compress level pseudo-encoding");
|
Log.Error("Server sent compress level pseudo-encoding");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
|
||||||
|
|
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.
|
* read binary data off of the receive queue.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Util from "./util.js";
|
import * as Log from './util/logging.js';
|
||||||
|
|
||||||
|
|
||||||
/*jslint browser: true, bitwise: true */
|
/*jslint browser: true, bitwise: true */
|
||||||
/*global Util*/
|
/*global Util*/
|
||||||
|
@ -43,8 +42,6 @@ export default function Websock() {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
// this has performance issues in some versions Chromium, and
|
// this has performance issues in some versions Chromium, and
|
||||||
// doesn't gain a tremendous amount of performance increase in Firefox
|
// 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.
|
// at the moment. It may be valuable to turn it on in the future.
|
||||||
|
@ -172,7 +169,7 @@ export default function Websock() {
|
||||||
|
|
||||||
flush: function () {
|
flush: function () {
|
||||||
if (this._websocket.bufferedAmount !== 0) {
|
if (this._websocket.bufferedAmount !== 0) {
|
||||||
Util.Debug("bufferedAmount: " + this._websocket.bufferedAmount);
|
Log.Debug("bufferedAmount: " + this._websocket.bufferedAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) {
|
if (this._sQlen > 0 && this._websocket.readyState === WebSocket.OPEN) {
|
||||||
|
@ -222,23 +219,23 @@ export default function Websock() {
|
||||||
|
|
||||||
this._websocket.onmessage = this._recv_message.bind(this);
|
this._websocket.onmessage = this._recv_message.bind(this);
|
||||||
this._websocket.onopen = (function () {
|
this._websocket.onopen = (function () {
|
||||||
Util.Debug('>> WebSock.onopen');
|
Log.Debug('>> WebSock.onopen');
|
||||||
if (this._websocket.protocol) {
|
if (this._websocket.protocol) {
|
||||||
Util.Info("Server choose sub-protocol: " + this._websocket.protocol);
|
Log.Info("Server choose sub-protocol: " + this._websocket.protocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._eventHandlers.open();
|
this._eventHandlers.open();
|
||||||
Util.Debug("<< WebSock.onopen");
|
Log.Debug("<< WebSock.onopen");
|
||||||
}).bind(this);
|
}).bind(this);
|
||||||
this._websocket.onclose = (function (e) {
|
this._websocket.onclose = (function (e) {
|
||||||
Util.Debug(">> WebSock.onclose");
|
Log.Debug(">> WebSock.onclose");
|
||||||
this._eventHandlers.close(e);
|
this._eventHandlers.close(e);
|
||||||
Util.Debug("<< WebSock.onclose");
|
Log.Debug("<< WebSock.onclose");
|
||||||
}).bind(this);
|
}).bind(this);
|
||||||
this._websocket.onerror = (function (e) {
|
this._websocket.onerror = (function (e) {
|
||||||
Util.Debug(">> WebSock.onerror: " + e);
|
Log.Debug(">> WebSock.onerror: " + e);
|
||||||
this._eventHandlers.error(e);
|
this._eventHandlers.error(e);
|
||||||
Util.Debug("<< WebSock.onerror: " + e);
|
Log.Debug("<< WebSock.onerror: " + e);
|
||||||
}).bind(this);
|
}).bind(this);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -246,7 +243,7 @@ export default function Websock() {
|
||||||
if (this._websocket) {
|
if (this._websocket) {
|
||||||
if ((this._websocket.readyState === WebSocket.OPEN) ||
|
if ((this._websocket.readyState === WebSocket.OPEN) ||
|
||||||
(this._websocket.readyState === WebSocket.CONNECTING)) {
|
(this._websocket.readyState === WebSocket.CONNECTING)) {
|
||||||
Util.Info("Closing WebSocket connection");
|
Log.Info("Closing WebSocket connection");
|
||||||
this._websocket.close();
|
this._websocket.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,7 +318,7 @@ export default function Websock() {
|
||||||
this._expand_compact_rQ();
|
this._expand_compact_rQ();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Util.Debug("Ignoring empty message");
|
Log.Debug("Ignoring empty message");
|
||||||
}
|
}
|
||||||
} catch (exc) {
|
} catch (exc) {
|
||||||
var exception_str = "";
|
var exception_str = "";
|
||||||
|
@ -339,9 +336,9 @@ export default function Websock() {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (exception_str.length > 0) {
|
if (exception_str.length > 0) {
|
||||||
Util.Error("recv_message, caught exception: " + exception_str);
|
Log.Error("recv_message, caught exception: " + exception_str);
|
||||||
} else {
|
} else {
|
||||||
Util.Error("recv_message, caught exception: " + exc);
|
Log.Error("recv_message, caught exception: " + exc);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof exc.name !== 'undefined') {
|
if (typeof exc.name !== 'undefined') {
|
||||||
|
@ -352,4 +349,3 @@ export default function Websock() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
|
||||||
|
|
Loading…
Reference in New Issue