Use RequireJS module system
Use a standard module system rather than a home grown one, and RequireJS fits better with browsers than CommonJS.
This commit is contained in:
parent
8ad1347fe3
commit
6bc9cadaef
|
|
@ -1,5 +1,7 @@
|
||||||
*.pyc
|
*.pyc
|
||||||
*.o
|
*.o
|
||||||
|
inflator/inflator-merged.js
|
||||||
|
inflator/pako
|
||||||
tests/data_*.js
|
tests/data_*.js
|
||||||
utils/rebind.so
|
utils/rebind.so
|
||||||
utils/websockify
|
utils/websockify
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@
|
||||||
* DO NOT EDIT!
|
* DO NOT EDIT!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Language = {
|
"use strict";
|
||||||
|
|
||||||
|
define(function() {
|
||||||
|
return {
|
||||||
"Connecting...": "Verbunden...",
|
"Connecting...": "Verbunden...",
|
||||||
"Connected (encrypted) to ": "Verbunden mit (verschlüsselt) ",
|
"Connected (encrypted) to ": "Verbunden mit (verschlüsselt) ",
|
||||||
"Connected (unencrypted) to ": "Verbunden mit (unverschlüsselt) ",
|
"Connected (unencrypted) to ": "Verbunden mit (unverschlüsselt) ",
|
||||||
|
|
@ -16,3 +19,4 @@ Language = {
|
||||||
"Forcing clipping mode since scrollbars aren't supported by IE in fullscreen": "'Clipping-Modus' aktiviert, Scrollbalken in 'IE-Vollbildmodus' werden nicht unterstützt",
|
"Forcing clipping mode since scrollbars aren't supported by IE in fullscreen": "'Clipping-Modus' aktiviert, Scrollbalken in 'IE-Vollbildmodus' werden nicht unterstützt",
|
||||||
"Disconnect timeout": "Timeout beim trennen",
|
"Disconnect timeout": "Timeout beim trennen",
|
||||||
};
|
};
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@
|
||||||
* DO NOT EDIT!
|
* DO NOT EDIT!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Language = {
|
"use strict";
|
||||||
|
|
||||||
|
define(function() {
|
||||||
|
return {
|
||||||
"Connecting...": "Συνδέεται...",
|
"Connecting...": "Συνδέεται...",
|
||||||
"Connected (encrypted) to ": "Συνδέθηκε (κρυπτογραφημένα) με το ",
|
"Connected (encrypted) to ": "Συνδέθηκε (κρυπτογραφημένα) με το ",
|
||||||
"Connected (unencrypted) to ": "Συνδέθηκε (μη κρυπτογραφημένα) με το ",
|
"Connected (unencrypted) to ": "Συνδέθηκε (μη κρυπτογραφημένα) με το ",
|
||||||
|
|
@ -72,3 +75,4 @@ Language = {
|
||||||
"Send Password": "Αποστολή Κωδικού Πρόσβασης",
|
"Send Password": "Αποστολή Κωδικού Πρόσβασης",
|
||||||
"Canvas not supported.": "Δεν υποστηρίζεται το στοιχείο Canvas",
|
"Canvas not supported.": "Δεν υποστηρίζεται το στοιχείο Canvas",
|
||||||
};
|
};
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@
|
||||||
* DO NOT EDIT!
|
* DO NOT EDIT!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Language = {
|
"use strict";
|
||||||
|
|
||||||
|
define(function() {
|
||||||
|
return {
|
||||||
"Connecting...": "Verbinden...",
|
"Connecting...": "Verbinden...",
|
||||||
"Connected (encrypted) to ": "Verbonden (versleuteld) met ",
|
"Connected (encrypted) to ": "Verbonden (versleuteld) met ",
|
||||||
"Connected (unencrypted) to ": "Verbonden (onversleuteld) met ",
|
"Connected (unencrypted) to ": "Verbonden (onversleuteld) met ",
|
||||||
|
|
@ -16,3 +19,4 @@ Language = {
|
||||||
"Forcing clipping mode since scrollbars aren't supported by IE in fullscreen": "''Clipping mode' ingeschakeld, omdat schuifbalken in volledige-scherm-modus in IE niet worden ondersteund",
|
"Forcing clipping mode since scrollbars aren't supported by IE in fullscreen": "''Clipping mode' ingeschakeld, omdat schuifbalken in volledige-scherm-modus in IE niet worden ondersteund",
|
||||||
"Disconnect timeout": "Timeout tijdens verbreken van verbinding",
|
"Disconnect timeout": "Timeout tijdens verbreken van verbinding",
|
||||||
};
|
};
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,10 @@
|
||||||
* DO NOT EDIT!
|
* DO NOT EDIT!
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Language = {
|
"use strict";
|
||||||
|
|
||||||
|
define(function() {
|
||||||
|
return {
|
||||||
"Connecting...": "Ansluter...",
|
"Connecting...": "Ansluter...",
|
||||||
"Connected (encrypted) to ": "Ansluten (krypterat) till ",
|
"Connected (encrypted) to ": "Ansluten (krypterat) till ",
|
||||||
"Connected (unencrypted) to ": "Ansluten (okrypterat) till ",
|
"Connected (unencrypted) to ": "Ansluten (okrypterat) till ",
|
||||||
|
|
@ -75,3 +78,4 @@ Language = {
|
||||||
"Send Password": "Skicka Lösenord",
|
"Send Password": "Skicka Lösenord",
|
||||||
"Canvas not supported.": "Canvas stöds ej",
|
"Canvas not supported.": "Canvas stöds ej",
|
||||||
};
|
};
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* noVNC: HTML5 VNC client
|
||||||
|
* Copyright (C) 2017 Pierre Ossman for Cendio AB
|
||||||
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
|
*
|
||||||
|
* See README.md for usage and integration instructions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* jslint white: false, browser: true */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
requirejs.config({
|
||||||
|
baseUrl: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
requirejs(['app/ui', 'core/util'],
|
||||||
|
function(ui, util) {
|
||||||
|
// Set up translations, then start the UI
|
||||||
|
var LINGUAS = ["de", "el", "nl", "sv"];
|
||||||
|
util.Localisation.setup(LINGUAS, "app/locale", ui.load);
|
||||||
|
});
|
||||||
File diff suppressed because one or more lines are too long
66
app/ui.js
66
app/ui.js
|
|
@ -2,29 +2,19 @@
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Copyright (C) 2016 Samuel Mannehed for Cendio AB
|
* Copyright (C) 2016 Samuel Mannehed for Cendio AB
|
||||||
* Copyright (C) 2016 Pierre Ossman for Cendio AB
|
* Copyright (C) 2017 Pierre Ossman for Cendio AB
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* jslint white: false, browser: true */
|
/* jslint white: false, browser: true */
|
||||||
/* global window, document.getElementById, Util, WebUtil, RFB, Display */
|
|
||||||
|
|
||||||
/* [module]
|
|
||||||
* import Util from "../core/util";
|
|
||||||
* import KeyTable from "../core/input/keysym";
|
|
||||||
* import keysyms from "./keysymdef";
|
|
||||||
* import RFB from "../core/rfb";
|
|
||||||
* import Display from "../core/display";
|
|
||||||
* import WebUtil from "./webutil";
|
|
||||||
*/
|
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var UI;
|
define(["app/webutil", "core/rfb", "core/util",
|
||||||
|
"core/input/keysym", "core/input/keysymdef"],
|
||||||
(function () {
|
function (WebUtil, rfb, Util, KeyTable, keysyms) {
|
||||||
|
|
||||||
// Fallback for all uncought errors
|
// Fallback for all uncought errors
|
||||||
window.addEventListener('error', function(event) {
|
window.addEventListener('error', function(event) {
|
||||||
|
|
@ -66,27 +56,9 @@ var UI;
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set up translations
|
|
||||||
var LINGUAS = ["de", "el", "nl", "sv"];
|
|
||||||
Util.Localisation.setup(LINGUAS);
|
|
||||||
if (Util.Localisation.language !== "en") {
|
|
||||||
WebUtil.load_scripts(
|
|
||||||
{'app': ["locale/" + Util.Localisation.language + ".js"]});
|
|
||||||
}
|
|
||||||
|
|
||||||
/* [begin skip-as-module] */
|
|
||||||
// Load supporting scripts
|
|
||||||
WebUtil.load_scripts(
|
|
||||||
{'core': ["base64.js", "websock.js", "des.js", "input/keysymdef.js",
|
|
||||||
"input/xtscancodes.js", "input/util.js", "input/devices.js",
|
|
||||||
"display.js", "inflator.js", "rfb.js", "input/keysym.js"]});
|
|
||||||
|
|
||||||
window.onscriptsload = function () { UI.load(); };
|
|
||||||
/* [end skip-as-module] */
|
|
||||||
|
|
||||||
var _ = Util.Localisation.get;
|
var _ = Util.Localisation.get;
|
||||||
|
|
||||||
UI = {
|
var UI = {
|
||||||
|
|
||||||
connected: false,
|
connected: false,
|
||||||
desktopName: "",
|
desktopName: "",
|
||||||
|
|
@ -390,17 +362,17 @@ var UI;
|
||||||
|
|
||||||
initRFB: function() {
|
initRFB: function() {
|
||||||
try {
|
try {
|
||||||
UI.rfb = new RFB({'target': document.getElementById('noVNC_canvas'),
|
UI.rfb = new rfb.RFB({'target': document.getElementById('noVNC_canvas'),
|
||||||
'onNotification': UI.notification,
|
'onNotification': UI.notification,
|
||||||
'onUpdateState': UI.updateState,
|
'onUpdateState': UI.updateState,
|
||||||
'onDisconnected': UI.disconnectFinished,
|
'onDisconnected': UI.disconnectFinished,
|
||||||
'onPasswordRequired': UI.passwordRequired,
|
'onPasswordRequired': UI.passwordRequired,
|
||||||
'onXvpInit': UI.updateXvpButton,
|
'onXvpInit': UI.updateXvpButton,
|
||||||
'onClipboard': UI.clipboardReceive,
|
'onClipboard': UI.clipboardReceive,
|
||||||
'onBell': UI.bell,
|
'onBell': UI.bell,
|
||||||
'onFBUComplete': UI.initialResize,
|
'onFBUComplete': UI.initialResize,
|
||||||
'onFBResize': UI.updateSessionSize,
|
'onFBResize': UI.updateSessionSize,
|
||||||
'onDesktopName': UI.updateDesktopName});
|
'onDesktopName': UI.updateDesktopName});
|
||||||
return true;
|
return true;
|
||||||
} catch (exc) {
|
} catch (exc) {
|
||||||
var msg = "Unable to create RFB client -- " + exc;
|
var msg = "Unable to create RFB client -- " + exc;
|
||||||
|
|
@ -1679,7 +1651,5 @@ var UI;
|
||||||
*/
|
*/
|
||||||
};
|
};
|
||||||
|
|
||||||
/* [module] UI.load(); */
|
return UI;
|
||||||
})();
|
});
|
||||||
|
|
||||||
/* [module] export default UI; */
|
|
||||||
|
|
|
||||||
641
app/webutil.js
641
app/webutil.js
|
|
@ -2,393 +2,316 @@
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Copyright (C) 2013 NTT corp.
|
* Copyright (C) 2013 NTT corp.
|
||||||
|
* Copyright (C) 2017 Pierre Ossman for Cendio AB
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*jslint bitwise: false, white: false, browser: true, devel: true */
|
/*jslint bitwise: false, white: false, browser: true, devel: true */
|
||||||
/*global Util, window, document */
|
|
||||||
|
|
||||||
/* [module]
|
|
||||||
* import Util from "../core/util";
|
|
||||||
*/
|
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// Globals defined here
|
define(['core/util'],
|
||||||
var WebUtil = {};
|
function(Util) {
|
||||||
|
var WebUtil = {};
|
||||||
|
|
||||||
/*
|
// init log level reading the logging HTTP param
|
||||||
* ------------------------------------------------------
|
WebUtil.init_logging = function (level) {
|
||||||
* Namespaced in WebUtil
|
if (typeof level === "undefined") {
|
||||||
* ------------------------------------------------------
|
var param = document.location.href.match(/logging=([A-Za-z0-9\._\-]*)/);
|
||||||
*/
|
if (param !== undefined)
|
||||||
|
level = param;
|
||||||
// init log level reading the logging HTTP param
|
|
||||||
WebUtil.init_logging = function (level) {
|
|
||||||
if (typeof level !== "undefined") {
|
|
||||||
Util._log_level = level;
|
|
||||||
} else {
|
|
||||||
var param = document.location.href.match(/logging=([A-Za-z0-9\._\-]*)/);
|
|
||||||
Util._log_level = (param || ['', Util._log_level])[1];
|
|
||||||
}
|
|
||||||
Util.init_logging();
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
WebUtil.dirObj = function (obj, depth, parent) {
|
|
||||||
if (! depth) { depth = 2; }
|
|
||||||
if (! parent) { parent = ""; }
|
|
||||||
|
|
||||||
// Print the properties of the passed-in object
|
|
||||||
var msg = "";
|
|
||||||
for (var i in obj) {
|
|
||||||
if ((depth > 1) && (typeof obj[i] === "object")) {
|
|
||||||
// Recurse attributes that are objects
|
|
||||||
msg += WebUtil.dirObj(obj[i], depth - 1, parent + "." + i);
|
|
||||||
} else {
|
|
||||||
//val = new String(obj[i]).replace("\n", " ");
|
|
||||||
var val = "";
|
|
||||||
if (typeof(obj[i]) === "undefined") {
|
|
||||||
val = "undefined";
|
|
||||||
} else {
|
|
||||||
val = obj[i].toString().replace("\n", " ");
|
|
||||||
}
|
|
||||||
if (val.length > 30) {
|
|
||||||
val = val.substr(0, 30) + "...";
|
|
||||||
}
|
|
||||||
msg += parent + "." + i + ": " + val + "\n";
|
|
||||||
}
|
}
|
||||||
}
|
Util.init_logging(level);
|
||||||
return msg;
|
};
|
||||||
};
|
|
||||||
|
|
||||||
// Read a query string variable
|
|
||||||
WebUtil.getQueryVar = function (name, defVal) {
|
|
||||||
var re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
|
|
||||||
match = document.location.href.match(re);
|
|
||||||
if (typeof defVal === 'undefined') { defVal = null; }
|
|
||||||
if (match) {
|
|
||||||
return decodeURIComponent(match[1]);
|
|
||||||
} else {
|
|
||||||
return defVal;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Read a hash fragment variable
|
WebUtil.dirObj = function (obj, depth, parent) {
|
||||||
WebUtil.getHashVar = function (name, defVal) {
|
if (! depth) { depth = 2; }
|
||||||
var re = new RegExp('.*[&#]' + name + '=([^&]*)'),
|
if (! parent) { parent = ""; }
|
||||||
match = document.location.hash.match(re);
|
|
||||||
if (typeof defVal === 'undefined') { defVal = null; }
|
|
||||||
if (match) {
|
|
||||||
return decodeURIComponent(match[1]);
|
|
||||||
} else {
|
|
||||||
return defVal;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Read a variable from the fragment or the query string
|
// Print the properties of the passed-in object
|
||||||
// Fragment takes precedence
|
var msg = "";
|
||||||
WebUtil.getConfigVar = function (name, defVal) {
|
for (var i in obj) {
|
||||||
var val = WebUtil.getHashVar(name);
|
if ((depth > 1) && (typeof obj[i] === "object")) {
|
||||||
if (val === null) {
|
// Recurse attributes that are objects
|
||||||
val = WebUtil.getQueryVar(name, defVal);
|
msg += WebUtil.dirObj(obj[i], depth - 1, parent + "." + i);
|
||||||
}
|
} else {
|
||||||
return val;
|
//val = new String(obj[i]).replace("\n", " ");
|
||||||
};
|
var val = "";
|
||||||
|
if (typeof(obj[i]) === "undefined") {
|
||||||
|
val = "undefined";
|
||||||
|
} else {
|
||||||
|
val = obj[i].toString().replace("\n", " ");
|
||||||
|
}
|
||||||
|
if (val.length > 30) {
|
||||||
|
val = val.substr(0, 30) + "...";
|
||||||
|
}
|
||||||
|
msg += parent + "." + i + ": " + val + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return msg;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
// Read a query string variable
|
||||||
* Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
|
WebUtil.getQueryVar = function (name, defVal) {
|
||||||
*/
|
var re = new RegExp('.*[?&]' + name + '=([^&#]*)'),
|
||||||
|
match = document.location.href.match(re);
|
||||||
|
if (typeof defVal === 'undefined') { defVal = null; }
|
||||||
|
if (match) {
|
||||||
|
return decodeURIComponent(match[1]);
|
||||||
|
} else {
|
||||||
|
return defVal;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// No days means only for this browser session
|
// Read a hash fragment variable
|
||||||
WebUtil.createCookie = function (name, value, days) {
|
WebUtil.getHashVar = function (name, defVal) {
|
||||||
var date, expires;
|
var re = new RegExp('.*[&#]' + name + '=([^&]*)'),
|
||||||
if (days) {
|
match = document.location.hash.match(re);
|
||||||
date = new Date();
|
if (typeof defVal === 'undefined') { defVal = null; }
|
||||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
if (match) {
|
||||||
expires = "; expires=" + date.toGMTString();
|
return decodeURIComponent(match[1]);
|
||||||
} else {
|
} else {
|
||||||
expires = "";
|
return defVal;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
var secure;
|
// Read a variable from the fragment or the query string
|
||||||
if (document.location.protocol === "https:") {
|
// Fragment takes precedence
|
||||||
secure = "; secure";
|
WebUtil.getConfigVar = function (name, defVal) {
|
||||||
} else {
|
var val = WebUtil.getHashVar(name);
|
||||||
secure = "";
|
if (val === null) {
|
||||||
}
|
val = WebUtil.getQueryVar(name, defVal);
|
||||||
document.cookie = name + "=" + value + expires + "; path=/" + secure;
|
}
|
||||||
};
|
return val;
|
||||||
|
};
|
||||||
|
|
||||||
WebUtil.readCookie = function (name, defaultValue) {
|
/*
|
||||||
var nameEQ = name + "=",
|
* Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
|
||||||
ca = document.cookie.split(';');
|
*/
|
||||||
|
|
||||||
for (var i = 0; i < ca.length; i += 1) {
|
// No days means only for this browser session
|
||||||
var c = ca[i];
|
WebUtil.createCookie = function (name, value, days) {
|
||||||
while (c.charAt(0) === ' ') { c = c.substring(1, c.length); }
|
var date, expires;
|
||||||
if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length, c.length); }
|
if (days) {
|
||||||
}
|
date = new Date();
|
||||||
return (typeof defaultValue !== 'undefined') ? defaultValue : null;
|
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||||
};
|
expires = "; expires=" + date.toGMTString();
|
||||||
|
} else {
|
||||||
|
expires = "";
|
||||||
|
}
|
||||||
|
|
||||||
WebUtil.eraseCookie = function (name) {
|
var secure;
|
||||||
WebUtil.createCookie(name, "", -1);
|
if (document.location.protocol === "https:") {
|
||||||
};
|
secure = "; secure";
|
||||||
|
} else {
|
||||||
|
secure = "";
|
||||||
|
}
|
||||||
|
document.cookie = name + "=" + value + expires + "; path=/" + secure;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
WebUtil.readCookie = function (name, defaultValue) {
|
||||||
* Setting handling.
|
var nameEQ = name + "=",
|
||||||
*/
|
ca = document.cookie.split(';');
|
||||||
|
|
||||||
WebUtil.initSettings = function (callback /*, ...callbackArgs */) {
|
for (var i = 0; i < ca.length; i += 1) {
|
||||||
var callbackArgs = Array.prototype.slice.call(arguments, 1);
|
var c = ca[i];
|
||||||
if (window.chrome && window.chrome.storage) {
|
while (c.charAt(0) === ' ') { c = c.substring(1, c.length); }
|
||||||
window.chrome.storage.sync.get(function (cfg) {
|
if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length, c.length); }
|
||||||
WebUtil.settings = cfg;
|
}
|
||||||
console.log(WebUtil.settings);
|
return (typeof defaultValue !== 'undefined') ? defaultValue : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
WebUtil.eraseCookie = function (name) {
|
||||||
|
WebUtil.createCookie(name, "", -1);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Setting handling.
|
||||||
|
*/
|
||||||
|
|
||||||
|
WebUtil.initSettings = function (callback /*, ...callbackArgs */) {
|
||||||
|
var callbackArgs = Array.prototype.slice.call(arguments, 1);
|
||||||
|
if (window.chrome && window.chrome.storage) {
|
||||||
|
window.chrome.storage.sync.get(function (cfg) {
|
||||||
|
WebUtil.settings = cfg;
|
||||||
|
console.log(WebUtil.settings);
|
||||||
|
if (callback) {
|
||||||
|
callback.apply(this, callbackArgs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// No-op
|
||||||
if (callback) {
|
if (callback) {
|
||||||
callback.apply(this, callbackArgs);
|
callback.apply(this, callbackArgs);
|
||||||
}
|
}
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// No-op
|
|
||||||
if (callback) {
|
|
||||||
callback.apply(this, callbackArgs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// No days means only for this browser session
|
|
||||||
WebUtil.writeSetting = function (name, value) {
|
|
||||||
if (window.chrome && window.chrome.storage) {
|
|
||||||
//console.log("writeSetting:", name, value);
|
|
||||||
if (WebUtil.settings[name] !== value) {
|
|
||||||
WebUtil.settings[name] = value;
|
|
||||||
window.chrome.storage.sync.set(WebUtil.settings);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
localStorage.setItem(name, value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
WebUtil.readSetting = function (name, defaultValue) {
|
|
||||||
var value;
|
|
||||||
if (window.chrome && window.chrome.storage) {
|
|
||||||
value = WebUtil.settings[name];
|
|
||||||
} else {
|
|
||||||
value = localStorage.getItem(name);
|
|
||||||
}
|
|
||||||
if (typeof value === "undefined") {
|
|
||||||
value = null;
|
|
||||||
}
|
|
||||||
if (value === null && typeof defaultValue !== undefined) {
|
|
||||||
return defaultValue;
|
|
||||||
} else {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
WebUtil.eraseSetting = function (name) {
|
|
||||||
if (window.chrome && window.chrome.storage) {
|
|
||||||
window.chrome.storage.sync.remove(name);
|
|
||||||
delete WebUtil.settings[name];
|
|
||||||
} else {
|
|
||||||
localStorage.removeItem(name);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
WebUtil.injectParamIfMissing = function (path, param, value) {
|
|
||||||
// force pretend that we're dealing with a relative path
|
|
||||||
// (assume that we wanted an extra if we pass one in)
|
|
||||||
path = "/" + path;
|
|
||||||
|
|
||||||
var elem = document.createElement('a');
|
|
||||||
elem.href = path;
|
|
||||||
|
|
||||||
var param_eq = encodeURIComponent(param) + "=";
|
|
||||||
var query;
|
|
||||||
if (elem.search) {
|
|
||||||
query = elem.search.slice(1).split('&');
|
|
||||||
} else {
|
|
||||||
query = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!query.some(function (v) { return v.startsWith(param_eq); })) {
|
|
||||||
query.push(param_eq + encodeURIComponent(value));
|
|
||||||
elem.search = "?" + query.join("&");
|
|
||||||
}
|
|
||||||
|
|
||||||
// some browsers (e.g. IE11) may occasionally omit the leading slash
|
|
||||||
// in the elem.pathname string. Handle that case gracefully.
|
|
||||||
if (elem.pathname.charAt(0) == "/") {
|
|
||||||
return elem.pathname.slice(1) + elem.search + elem.hash;
|
|
||||||
} else {
|
|
||||||
return elem.pathname + elem.search + elem.hash;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Emulate Element.setCapture() when not supported
|
|
||||||
|
|
||||||
WebUtil._captureRecursion = false;
|
|
||||||
WebUtil._captureProxy = function (e) {
|
|
||||||
// Recursion protection as we'll see our own event
|
|
||||||
if (WebUtil._captureRecursion) return;
|
|
||||||
|
|
||||||
// Clone the event as we cannot dispatch an already dispatched event
|
|
||||||
var newEv = new e.constructor(e.type, e);
|
|
||||||
|
|
||||||
WebUtil._captureRecursion = true;
|
|
||||||
WebUtil._captureElem.dispatchEvent(newEv);
|
|
||||||
WebUtil._captureRecursion = false;
|
|
||||||
|
|
||||||
// Implicitly release the capture on button release
|
|
||||||
if ((e.type === "mouseup") || (e.type === "touchend")) {
|
|
||||||
WebUtil.releaseCapture();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
WebUtil.setCapture = function (elem) {
|
|
||||||
if (elem.setCapture) {
|
|
||||||
|
|
||||||
elem.setCapture();
|
|
||||||
|
|
||||||
// IE releases capture on 'click' events which might not trigger
|
|
||||||
elem.addEventListener('mouseup', WebUtil.releaseCapture);
|
|
||||||
elem.addEventListener('touchend', WebUtil.releaseCapture);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
captureElem.addEventListener('mousemove', WebUtil._captureProxy);
|
|
||||||
captureElem.addEventListener('mouseup', WebUtil._captureProxy);
|
|
||||||
|
|
||||||
captureElem.addEventListener('touchmove', WebUtil._captureProxy);
|
|
||||||
captureElem.addEventListener('touchend', WebUtil._captureProxy);
|
|
||||||
}
|
|
||||||
|
|
||||||
WebUtil._captureElem = elem;
|
|
||||||
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', WebUtil._captureProxy);
|
|
||||||
window.addEventListener('mouseup', WebUtil._captureProxy);
|
|
||||||
|
|
||||||
window.addEventListener('touchmove', WebUtil._captureProxy);
|
|
||||||
window.addEventListener('touchend', WebUtil._captureProxy);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
WebUtil.releaseCapture = function () {
|
|
||||||
if (document.releaseCapture) {
|
|
||||||
|
|
||||||
document.releaseCapture();
|
|
||||||
|
|
||||||
} else {
|
|
||||||
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
|
||||||
WebUtil._captureElem = null;
|
|
||||||
captureElem.style.display = "none";
|
|
||||||
|
|
||||||
window.removeEventListener('mousemove', WebUtil._captureProxy);
|
|
||||||
window.removeEventListener('mouseup', WebUtil._captureProxy);
|
|
||||||
|
|
||||||
window.removeEventListener('touchmove', WebUtil._captureProxy);
|
|
||||||
window.removeEventListener('touchend', WebUtil._captureProxy);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// Dynamically load scripts without using document.write()
|
|
||||||
// Reference: http://unixpapa.com/js/dyna.html
|
|
||||||
//
|
|
||||||
// Handles the case where load_scripts is invoked from a script that
|
|
||||||
// itself is loaded via load_scripts. Once all scripts are loaded the
|
|
||||||
// window.onscriptsloaded handler is called (if set).
|
|
||||||
WebUtil.get_include_uri = function (root_dir) {
|
|
||||||
return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI + root_dir + '/' : root_dir + '/';
|
|
||||||
};
|
|
||||||
WebUtil._loading_scripts = [];
|
|
||||||
WebUtil._pending_scripts = [];
|
|
||||||
WebUtil.load_scripts = function (files_by_dir) {
|
|
||||||
var head = document.getElementsByTagName('head')[0], script,
|
|
||||||
ls = WebUtil._loading_scripts, ps = WebUtil._pending_scripts;
|
|
||||||
|
|
||||||
var loadFunc = function (e) {
|
|
||||||
while (ls.length > 0 && (ls[0].readyState === 'loaded' ||
|
|
||||||
ls[0].readyState === 'complete')) {
|
|
||||||
// For IE, append the script to trigger execution
|
|
||||||
var s = ls.shift();
|
|
||||||
//console.log("loaded script: " + s.src);
|
|
||||||
head.appendChild(s);
|
|
||||||
}
|
|
||||||
if (!this.readyState ||
|
|
||||||
(Util.Engine.presto && this.readyState === 'loaded') ||
|
|
||||||
this.readyState === 'complete') {
|
|
||||||
if (ps.indexOf(this) >= 0) {
|
|
||||||
this.onload = this.onreadystatechange = null;
|
|
||||||
//console.log("completed script: " + this.src);
|
|
||||||
ps.splice(ps.indexOf(this), 1);
|
|
||||||
|
|
||||||
// Call window.onscriptsload after last script loads
|
|
||||||
if (ps.length === 0 && window.onscriptsload) {
|
|
||||||
window.onscriptsload();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var root_dirs = Object.keys(files_by_dir);
|
// No days means only for this browser session
|
||||||
|
WebUtil.writeSetting = function (name, value) {
|
||||||
for (var d = 0; d < root_dirs.length; d++) {
|
if (window.chrome && window.chrome.storage) {
|
||||||
var root_dir = root_dirs[d];
|
//console.log("writeSetting:", name, value);
|
||||||
var files = files_by_dir[root_dir];
|
if (WebUtil.settings[name] !== value) {
|
||||||
|
WebUtil.settings[name] = value;
|
||||||
for (var f = 0; f < files.length; f++) {
|
window.chrome.storage.sync.set(WebUtil.settings);
|
||||||
script = document.createElement('script');
|
|
||||||
script.type = 'text/javascript';
|
|
||||||
script.src = WebUtil.get_include_uri(root_dir) + files[f];
|
|
||||||
//console.log("loading script: " + script.src);
|
|
||||||
script.onload = script.onreadystatechange = loadFunc;
|
|
||||||
// In-order script execution tricks
|
|
||||||
if (Util.Engine.trident) {
|
|
||||||
// For IE wait until readyState is 'loaded' before
|
|
||||||
// appending it which will trigger execution
|
|
||||||
// http://wiki.whatwg.org/wiki/Dynamic_Script_Execution_Order
|
|
||||||
ls.push(script);
|
|
||||||
} else {
|
|
||||||
// For webkit and firefox set async=false and append now
|
|
||||||
// https://developer.mozilla.org/en-US/docs/HTML/Element/script
|
|
||||||
script.async = false;
|
|
||||||
head.appendChild(script);
|
|
||||||
}
|
}
|
||||||
ps.push(script);
|
} else {
|
||||||
|
localStorage.setItem(name, value);
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
|
||||||
/* [module] export default WebUtil; */
|
WebUtil.readSetting = function (name, defaultValue) {
|
||||||
|
var value;
|
||||||
|
if (window.chrome && window.chrome.storage) {
|
||||||
|
value = WebUtil.settings[name];
|
||||||
|
} else {
|
||||||
|
value = localStorage.getItem(name);
|
||||||
|
}
|
||||||
|
if (typeof value === "undefined") {
|
||||||
|
value = null;
|
||||||
|
}
|
||||||
|
if (value === null && typeof defaultValue !== undefined) {
|
||||||
|
return defaultValue;
|
||||||
|
} else {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
WebUtil.eraseSetting = function (name) {
|
||||||
|
if (window.chrome && window.chrome.storage) {
|
||||||
|
window.chrome.storage.sync.remove(name);
|
||||||
|
delete WebUtil.settings[name];
|
||||||
|
} else {
|
||||||
|
localStorage.removeItem(name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
WebUtil.injectParamIfMissing = function (path, param, value) {
|
||||||
|
// force pretend that we're dealing with a relative path
|
||||||
|
// (assume that we wanted an extra if we pass one in)
|
||||||
|
path = "/" + path;
|
||||||
|
|
||||||
|
var elem = document.createElement('a');
|
||||||
|
elem.href = path;
|
||||||
|
|
||||||
|
var param_eq = encodeURIComponent(param) + "=";
|
||||||
|
var query;
|
||||||
|
if (elem.search) {
|
||||||
|
query = elem.search.slice(1).split('&');
|
||||||
|
} else {
|
||||||
|
query = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!query.some(function (v) { return v.startsWith(param_eq); })) {
|
||||||
|
query.push(param_eq + encodeURIComponent(value));
|
||||||
|
elem.search = "?" + query.join("&");
|
||||||
|
}
|
||||||
|
|
||||||
|
// some browsers (e.g. IE11) may occasionally omit the leading slash
|
||||||
|
// in the elem.pathname string. Handle that case gracefully.
|
||||||
|
if (elem.pathname.charAt(0) == "/") {
|
||||||
|
return elem.pathname.slice(1) + elem.search + elem.hash;
|
||||||
|
} else {
|
||||||
|
return elem.pathname + elem.search + elem.hash;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Emulate Element.setCapture() when not supported
|
||||||
|
|
||||||
|
var _captureElem;
|
||||||
|
var _captureRecursion = false;
|
||||||
|
var _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;
|
||||||
|
|
||||||
|
// Implicitly release the capture on button release
|
||||||
|
if ((e.type === "mouseup") || (e.type === "touchend")) {
|
||||||
|
WebUtil.releaseCapture();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
WebUtil.setCapture = function (elem) {
|
||||||
|
if (elem.setCapture) {
|
||||||
|
|
||||||
|
elem.setCapture();
|
||||||
|
|
||||||
|
// IE releases capture on 'click' events which might not trigger
|
||||||
|
elem.addEventListener('mouseup', WebUtil.releaseCapture);
|
||||||
|
elem.addEventListener('touchend', WebUtil.releaseCapture);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
captureElem.addEventListener('mousemove', _captureProxy);
|
||||||
|
captureElem.addEventListener('mouseup', _captureProxy);
|
||||||
|
|
||||||
|
captureElem.addEventListener('touchmove', _captureProxy);
|
||||||
|
captureElem.addEventListener('touchend', _captureProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
_captureElem = elem;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
WebUtil.releaseCapture = function () {
|
||||||
|
if (document.releaseCapture) {
|
||||||
|
|
||||||
|
document.releaseCapture();
|
||||||
|
|
||||||
|
} else {
|
||||||
|
var captureElem = document.getElementById("noVNC_mouse_capture_elem");
|
||||||
|
_captureElem = null;
|
||||||
|
captureElem.style.display = "none";
|
||||||
|
|
||||||
|
window.removeEventListener('mousemove', _captureProxy);
|
||||||
|
window.removeEventListener('mouseup', _captureProxy);
|
||||||
|
|
||||||
|
window.removeEventListener('touchmove', _captureProxy);
|
||||||
|
window.removeEventListener('touchend', _captureProxy);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return WebUtil;
|
||||||
|
});
|
||||||
|
|
|
||||||
197
core/base64.js
197
core/base64.js
|
|
@ -5,113 +5,114 @@
|
||||||
// From: http://hg.mozilla.org/mozilla-central/raw-file/ec10630b1a54/js/src/devtools/jint/sunspider/string-base64.js
|
// From: http://hg.mozilla.org/mozilla-central/raw-file/ec10630b1a54/js/src/devtools/jint/sunspider/string-base64.js
|
||||||
|
|
||||||
/*jslint white: false */
|
/*jslint white: false */
|
||||||
/*global console */
|
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var Base64 = {
|
define(function () {
|
||||||
/* Convert data (an array of integers) to a Base64 string. */
|
var Base64 = {
|
||||||
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
|
/* Convert data (an array of integers) to a Base64 string. */
|
||||||
base64Pad : '=',
|
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
|
||||||
|
base64Pad : '=',
|
||||||
|
|
||||||
encode: function (data) {
|
encode: function (data) {
|
||||||
"use strict";
|
"use strict";
|
||||||
var result = '';
|
var result = '';
|
||||||
var toBase64Table = Base64.toBase64Table;
|
var toBase64Table = Base64.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.
|
||||||
|
|
||||||
for (var i = 0; i < (length - 2); i += 3) {
|
for (var i = 0; i < (length - 2); i += 3) {
|
||||||
result += toBase64Table[data[i] >> 2];
|
result += toBase64Table[data[i] >> 2];
|
||||||
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
|
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
|
||||||
result += toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
|
result += toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
|
||||||
result += toBase64Table[data[i + 2] & 0x3f];
|
result += toBase64Table[data[i + 2] & 0x3f];
|
||||||
}
|
|
||||||
|
|
||||||
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
|
|
||||||
var j = 0;
|
|
||||||
if (lengthpad === 2) {
|
|
||||||
j = length - lengthpad;
|
|
||||||
result += toBase64Table[data[j] >> 2];
|
|
||||||
result += toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)];
|
|
||||||
result += toBase64Table[(data[j + 1] & 0x0f) << 2];
|
|
||||||
result += toBase64Table[64];
|
|
||||||
} else if (lengthpad === 1) {
|
|
||||||
j = length - lengthpad;
|
|
||||||
result += toBase64Table[data[j] >> 2];
|
|
||||||
result += toBase64Table[(data[j] & 0x03) << 4];
|
|
||||||
result += toBase64Table[64];
|
|
||||||
result += toBase64Table[64];
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Convert Base64 data to a string */
|
|
||||||
/* jshint -W013 */
|
|
||||||
toBinaryTable : [
|
|
||||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
|
||||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
|
||||||
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
|
|
||||||
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
|
|
||||||
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
|
|
||||||
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
|
|
||||||
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
|
|
||||||
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
|
|
||||||
],
|
|
||||||
/* jshint +W013 */
|
|
||||||
|
|
||||||
decode: function (data, offset) {
|
|
||||||
"use strict";
|
|
||||||
offset = typeof(offset) !== 'undefined' ? offset : 0;
|
|
||||||
var toBinaryTable = Base64.toBinaryTable;
|
|
||||||
var base64Pad = Base64.base64Pad;
|
|
||||||
var result, result_length;
|
|
||||||
var leftbits = 0; // number of bits decoded, but yet to be appended
|
|
||||||
var leftdata = 0; // bits decoded, but yet to be appended
|
|
||||||
var data_length = data.indexOf('=') - offset;
|
|
||||||
|
|
||||||
if (data_length < 0) { data_length = data.length - offset; }
|
|
||||||
|
|
||||||
/* Every four characters is 3 resulting numbers */
|
|
||||||
result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5);
|
|
||||||
result = new Array(result_length);
|
|
||||||
|
|
||||||
// Convert one by one.
|
|
||||||
for (var idx = 0, i = offset; i < data.length; i++) {
|
|
||||||
var c = toBinaryTable[data.charCodeAt(i) & 0x7f];
|
|
||||||
var padding = (data.charAt(i) === base64Pad);
|
|
||||||
// Skip illegal characters and whitespace
|
|
||||||
if (c === -1) {
|
|
||||||
console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Collect data into leftdata, update bitcount
|
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
|
||||||
leftdata = (leftdata << 6) | c;
|
var j = 0;
|
||||||
leftbits += 6;
|
if (lengthpad === 2) {
|
||||||
|
j = length - lengthpad;
|
||||||
|
result += toBase64Table[data[j] >> 2];
|
||||||
|
result += toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)];
|
||||||
|
result += toBase64Table[(data[j + 1] & 0x0f) << 2];
|
||||||
|
result += toBase64Table[64];
|
||||||
|
} else if (lengthpad === 1) {
|
||||||
|
j = length - lengthpad;
|
||||||
|
result += toBase64Table[data[j] >> 2];
|
||||||
|
result += toBase64Table[(data[j] & 0x03) << 4];
|
||||||
|
result += toBase64Table[64];
|
||||||
|
result += toBase64Table[64];
|
||||||
|
}
|
||||||
|
|
||||||
// If we have 8 or more bits, append 8 bits to the result
|
return result;
|
||||||
if (leftbits >= 8) {
|
},
|
||||||
leftbits -= 8;
|
|
||||||
// Append if not padding.
|
/* Convert Base64 data to a string */
|
||||||
if (!padding) {
|
/* jshint -W013 */
|
||||||
result[idx++] = (leftdata >> leftbits) & 0xff;
|
toBinaryTable : [
|
||||||
|
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||||
|
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
|
||||||
|
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
|
||||||
|
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
|
||||||
|
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
|
||||||
|
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
|
||||||
|
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
|
||||||
|
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
|
||||||
|
],
|
||||||
|
/* jshint +W013 */
|
||||||
|
|
||||||
|
decode: function (data, offset) {
|
||||||
|
"use strict";
|
||||||
|
offset = typeof(offset) !== 'undefined' ? offset : 0;
|
||||||
|
var toBinaryTable = Base64.toBinaryTable;
|
||||||
|
var base64Pad = Base64.base64Pad;
|
||||||
|
var result, result_length;
|
||||||
|
var leftbits = 0; // number of bits decoded, but yet to be appended
|
||||||
|
var leftdata = 0; // bits decoded, but yet to be appended
|
||||||
|
var data_length = data.indexOf('=') - offset;
|
||||||
|
|
||||||
|
if (data_length < 0) { data_length = data.length - offset; }
|
||||||
|
|
||||||
|
/* Every four characters is 3 resulting numbers */
|
||||||
|
result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5);
|
||||||
|
result = new Array(result_length);
|
||||||
|
|
||||||
|
// Convert one by one.
|
||||||
|
for (var idx = 0, i = offset; i < data.length; i++) {
|
||||||
|
var c = toBinaryTable[data.charCodeAt(i) & 0x7f];
|
||||||
|
var padding = (data.charAt(i) === base64Pad);
|
||||||
|
// Skip illegal characters and whitespace
|
||||||
|
if (c === -1) {
|
||||||
|
console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect data into leftdata, update bitcount
|
||||||
|
leftdata = (leftdata << 6) | c;
|
||||||
|
leftbits += 6;
|
||||||
|
|
||||||
|
// If we have 8 or more bits, append 8 bits to the result
|
||||||
|
if (leftbits >= 8) {
|
||||||
|
leftbits -= 8;
|
||||||
|
// Append if not padding.
|
||||||
|
if (!padding) {
|
||||||
|
result[idx++] = (leftdata >> leftbits) & 0xff;
|
||||||
|
}
|
||||||
|
leftdata &= (1 << leftbits) - 1;
|
||||||
}
|
}
|
||||||
leftdata &= (1 << leftbits) - 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If there are any bits left, the base64 string was corrupted
|
||||||
|
if (leftbits) {
|
||||||
|
err = new Error('Corrupted base64 string');
|
||||||
|
err.name = 'Base64-Error';
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// If there are any bits left, the base64 string was corrupted
|
return Base64;
|
||||||
if (leftbits) {
|
});
|
||||||
err = new Error('Corrupted base64 string');
|
|
||||||
err.name = 'Base64-Error';
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}; /* End of Base64 namespace */
|
|
||||||
|
|
||||||
/* [module] export default Base64; */
|
|
||||||
|
|
|
||||||
355
core/des.js
355
core/des.js
|
|
@ -79,199 +79,202 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
/* [module] export default */ function DES(passwd) {
|
define(function () {
|
||||||
|
function DES(passwd) {
|
||||||
|
// Tables, permutations, S-boxes, etc.
|
||||||
|
// jshint -W013
|
||||||
|
var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,
|
||||||
|
25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,
|
||||||
|
50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],
|
||||||
|
totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28],
|
||||||
|
z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8,
|
||||||
|
keys = [];
|
||||||
|
|
||||||
// Tables, permutations, S-boxes, etc.
|
// jshint -W015
|
||||||
// jshint -W013
|
a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e;
|
||||||
var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,
|
SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d,
|
||||||
25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,
|
z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z,
|
||||||
50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],
|
a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f,
|
||||||
totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28],
|
c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d];
|
||||||
z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8,
|
a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e;
|
||||||
keys = [];
|
SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d,
|
||||||
|
a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f,
|
||||||
|
z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z,
|
||||||
|
z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e];
|
||||||
|
a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e;
|
||||||
|
SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f,
|
||||||
|
b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z,
|
||||||
|
c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d,
|
||||||
|
b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e];
|
||||||
|
a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e;
|
||||||
|
SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d,
|
||||||
|
z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f,
|
||||||
|
b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e,
|
||||||
|
c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e];
|
||||||
|
a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e;
|
||||||
|
SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z,
|
||||||
|
a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f,
|
||||||
|
z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e,
|
||||||
|
c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d];
|
||||||
|
a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e;
|
||||||
|
SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f,
|
||||||
|
z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z,
|
||||||
|
b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z,
|
||||||
|
a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f];
|
||||||
|
a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e;
|
||||||
|
SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f,
|
||||||
|
b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e,
|
||||||
|
b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e,
|
||||||
|
z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d];
|
||||||
|
a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e;
|
||||||
|
SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d,
|
||||||
|
c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z,
|
||||||
|
a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f,
|
||||||
|
z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e];
|
||||||
|
// jshint +W013,+W015
|
||||||
|
|
||||||
// jshint -W015
|
// Set the key.
|
||||||
a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e;
|
function setKeys(keyBlock) {
|
||||||
SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d,
|
var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [],
|
||||||
z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z,
|
raw0, raw1, rawi, KnLi;
|
||||||
a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f,
|
|
||||||
c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d];
|
|
||||||
a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e;
|
|
||||||
SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d,
|
|
||||||
a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f,
|
|
||||||
z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z,
|
|
||||||
z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e];
|
|
||||||
a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e;
|
|
||||||
SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f,
|
|
||||||
b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z,
|
|
||||||
c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d,
|
|
||||||
b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e];
|
|
||||||
a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e;
|
|
||||||
SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d,
|
|
||||||
z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f,
|
|
||||||
b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e,
|
|
||||||
c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e];
|
|
||||||
a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e;
|
|
||||||
SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z,
|
|
||||||
a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f,
|
|
||||||
z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e,
|
|
||||||
c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d];
|
|
||||||
a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e;
|
|
||||||
SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f,
|
|
||||||
z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z,
|
|
||||||
b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z,
|
|
||||||
a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f];
|
|
||||||
a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e;
|
|
||||||
SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f,
|
|
||||||
b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e,
|
|
||||||
b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e,
|
|
||||||
z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d];
|
|
||||||
a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e;
|
|
||||||
SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d,
|
|
||||||
c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z,
|
|
||||||
a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f,
|
|
||||||
z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e];
|
|
||||||
// jshint +W013,+W015
|
|
||||||
|
|
||||||
// Set the key.
|
for (j = 0, l = 56; j < 56; ++j, l -= 8) {
|
||||||
function setKeys(keyBlock) {
|
l += l < -5 ? 65 : l < -3 ? 31 : l < -1 ? 63 : l === 27 ? 35 : 0; // PC1
|
||||||
var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [],
|
m = l & 0x7;
|
||||||
raw0, raw1, rawi, KnLi;
|
pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
|
||||||
|
}
|
||||||
|
|
||||||
for (j = 0, l = 56; j < 56; ++j, l -= 8) {
|
for (i = 0; i < 16; ++i) {
|
||||||
l += l < -5 ? 65 : l < -3 ? 31 : l < -1 ? 63 : l === 27 ? 35 : 0; // PC1
|
m = i << 1;
|
||||||
m = l & 0x7;
|
n = m + 1;
|
||||||
pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
|
kn[m] = kn[n] = 0;
|
||||||
}
|
for (o = 28; o < 59; o += 28) {
|
||||||
|
for (j = o - 28; j < o; ++j) {
|
||||||
for (i = 0; i < 16; ++i) {
|
l = j + totrot[i];
|
||||||
m = i << 1;
|
if (l < o) {
|
||||||
n = m + 1;
|
pcr[j] = pc1m[l];
|
||||||
kn[m] = kn[n] = 0;
|
} else {
|
||||||
for (o = 28; o < 59; o += 28) {
|
pcr[j] = pc1m[l - 28];
|
||||||
for (j = o - 28; j < o; ++j) {
|
}
|
||||||
l = j + totrot[i];
|
}
|
||||||
if (l < o) {
|
}
|
||||||
pcr[j] = pc1m[l];
|
for (j = 0; j < 24; ++j) {
|
||||||
} else {
|
if (pcr[PC2[j]] !== 0) {
|
||||||
pcr[j] = pc1m[l - 28];
|
kn[m] |= 1 << (23 - j);
|
||||||
|
}
|
||||||
|
if (pcr[PC2[j + 24]] !== 0) {
|
||||||
|
kn[n] |= 1 << (23 - j);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (j = 0; j < 24; ++j) {
|
|
||||||
if (pcr[PC2[j]] !== 0) {
|
// cookey
|
||||||
kn[m] |= 1 << (23 - j);
|
for (i = 0, rawi = 0, KnLi = 0; i < 16; ++i) {
|
||||||
}
|
raw0 = kn[rawi++];
|
||||||
if (pcr[PC2[j + 24]] !== 0) {
|
raw1 = kn[rawi++];
|
||||||
kn[n] |= 1 << (23 - j);
|
keys[KnLi] = (raw0 & 0x00fc0000) << 6;
|
||||||
}
|
keys[KnLi] |= (raw0 & 0x00000fc0) << 10;
|
||||||
|
keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
|
||||||
|
keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6;
|
||||||
|
++KnLi;
|
||||||
|
keys[KnLi] = (raw0 & 0x0003f000) << 12;
|
||||||
|
keys[KnLi] |= (raw0 & 0x0000003f) << 16;
|
||||||
|
keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;
|
||||||
|
keys[KnLi] |= (raw1 & 0x0000003f);
|
||||||
|
++KnLi;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// cookey
|
// Encrypt 8 bytes of text
|
||||||
for (i = 0, rawi = 0, KnLi = 0; i < 16; ++i) {
|
function enc8(text) {
|
||||||
raw0 = kn[rawi++];
|
var i = 0, b = text.slice(), fval, keysi = 0,
|
||||||
raw1 = kn[rawi++];
|
l, r, x; // left, right, accumulator
|
||||||
keys[KnLi] = (raw0 & 0x00fc0000) << 6;
|
|
||||||
keys[KnLi] |= (raw0 & 0x00000fc0) << 10;
|
|
||||||
keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
|
|
||||||
keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6;
|
|
||||||
++KnLi;
|
|
||||||
keys[KnLi] = (raw0 & 0x0003f000) << 12;
|
|
||||||
keys[KnLi] |= (raw0 & 0x0000003f) << 16;
|
|
||||||
keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;
|
|
||||||
keys[KnLi] |= (raw1 & 0x0000003f);
|
|
||||||
++KnLi;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encrypt 8 bytes of text
|
// Squash 8 bytes to 2 ints
|
||||||
function enc8(text) {
|
l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
|
||||||
var i = 0, b = text.slice(), fval, keysi = 0,
|
r = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
|
||||||
l, r, x; // left, right, accumulator
|
|
||||||
|
|
||||||
// Squash 8 bytes to 2 ints
|
x = ((l >>> 4) ^ r) & 0x0f0f0f0f;
|
||||||
l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
|
r ^= x;
|
||||||
r = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
|
l ^= (x << 4);
|
||||||
|
x = ((l >>> 16) ^ r) & 0x0000ffff;
|
||||||
|
r ^= x;
|
||||||
|
l ^= (x << 16);
|
||||||
|
x = ((r >>> 2) ^ l) & 0x33333333;
|
||||||
|
l ^= x;
|
||||||
|
r ^= (x << 2);
|
||||||
|
x = ((r >>> 8) ^ l) & 0x00ff00ff;
|
||||||
|
l ^= x;
|
||||||
|
r ^= (x << 8);
|
||||||
|
r = (r << 1) | ((r >>> 31) & 1);
|
||||||
|
x = (l ^ r) & 0xaaaaaaaa;
|
||||||
|
l ^= x;
|
||||||
|
r ^= x;
|
||||||
|
l = (l << 1) | ((l >>> 31) & 1);
|
||||||
|
|
||||||
x = ((l >>> 4) ^ r) & 0x0f0f0f0f;
|
for (i = 0; i < 8; ++i) {
|
||||||
r ^= x;
|
x = (r << 28) | (r >>> 4);
|
||||||
l ^= (x << 4);
|
x ^= keys[keysi++];
|
||||||
x = ((l >>> 16) ^ r) & 0x0000ffff;
|
fval = SP7[x & 0x3f];
|
||||||
r ^= x;
|
fval |= SP5[(x >>> 8) & 0x3f];
|
||||||
l ^= (x << 16);
|
fval |= SP3[(x >>> 16) & 0x3f];
|
||||||
x = ((r >>> 2) ^ l) & 0x33333333;
|
fval |= SP1[(x >>> 24) & 0x3f];
|
||||||
l ^= x;
|
x = r ^ keys[keysi++];
|
||||||
r ^= (x << 2);
|
fval |= SP8[x & 0x3f];
|
||||||
x = ((r >>> 8) ^ l) & 0x00ff00ff;
|
fval |= SP6[(x >>> 8) & 0x3f];
|
||||||
l ^= x;
|
fval |= SP4[(x >>> 16) & 0x3f];
|
||||||
r ^= (x << 8);
|
fval |= SP2[(x >>> 24) & 0x3f];
|
||||||
r = (r << 1) | ((r >>> 31) & 1);
|
l ^= fval;
|
||||||
x = (l ^ r) & 0xaaaaaaaa;
|
x = (l << 28) | (l >>> 4);
|
||||||
l ^= x;
|
x ^= keys[keysi++];
|
||||||
r ^= x;
|
fval = SP7[x & 0x3f];
|
||||||
l = (l << 1) | ((l >>> 31) & 1);
|
fval |= SP5[(x >>> 8) & 0x3f];
|
||||||
|
fval |= SP3[(x >>> 16) & 0x3f];
|
||||||
|
fval |= SP1[(x >>> 24) & 0x3f];
|
||||||
|
x = l ^ keys[keysi++];
|
||||||
|
fval |= SP8[x & 0x0000003f];
|
||||||
|
fval |= SP6[(x >>> 8) & 0x3f];
|
||||||
|
fval |= SP4[(x >>> 16) & 0x3f];
|
||||||
|
fval |= SP2[(x >>> 24) & 0x3f];
|
||||||
|
r ^= fval;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < 8; ++i) {
|
r = (r << 31) | (r >>> 1);
|
||||||
x = (r << 28) | (r >>> 4);
|
x = (l ^ r) & 0xaaaaaaaa;
|
||||||
x ^= keys[keysi++];
|
l ^= x;
|
||||||
fval = SP7[x & 0x3f];
|
r ^= x;
|
||||||
fval |= SP5[(x >>> 8) & 0x3f];
|
l = (l << 31) | (l >>> 1);
|
||||||
fval |= SP3[(x >>> 16) & 0x3f];
|
x = ((l >>> 8) ^ r) & 0x00ff00ff;
|
||||||
fval |= SP1[(x >>> 24) & 0x3f];
|
r ^= x;
|
||||||
x = r ^ keys[keysi++];
|
l ^= (x << 8);
|
||||||
fval |= SP8[x & 0x3f];
|
x = ((l >>> 2) ^ r) & 0x33333333;
|
||||||
fval |= SP6[(x >>> 8) & 0x3f];
|
r ^= x;
|
||||||
fval |= SP4[(x >>> 16) & 0x3f];
|
l ^= (x << 2);
|
||||||
fval |= SP2[(x >>> 24) & 0x3f];
|
x = ((r >>> 16) ^ l) & 0x0000ffff;
|
||||||
l ^= fval;
|
l ^= x;
|
||||||
x = (l << 28) | (l >>> 4);
|
r ^= (x << 16);
|
||||||
x ^= keys[keysi++];
|
x = ((r >>> 4) ^ l) & 0x0f0f0f0f;
|
||||||
fval = SP7[x & 0x3f];
|
l ^= x;
|
||||||
fval |= SP5[(x >>> 8) & 0x3f];
|
r ^= (x << 4);
|
||||||
fval |= SP3[(x >>> 16) & 0x3f];
|
|
||||||
fval |= SP1[(x >>> 24) & 0x3f];
|
// Spread ints to bytes
|
||||||
x = l ^ keys[keysi++];
|
x = [r, l];
|
||||||
fval |= SP8[x & 0x0000003f];
|
for (i = 0; i < 8; i++) {
|
||||||
fval |= SP6[(x >>> 8) & 0x3f];
|
b[i] = (x[i>>>2] >>> (8 * (3 - (i % 4)))) % 256;
|
||||||
fval |= SP4[(x >>> 16) & 0x3f];
|
if (b[i] < 0) { b[i] += 256; } // unsigned
|
||||||
fval |= SP2[(x >>> 24) & 0x3f];
|
}
|
||||||
r ^= fval;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = (r << 31) | (r >>> 1);
|
// Encrypt 16 bytes of text using passwd as key
|
||||||
x = (l ^ r) & 0xaaaaaaaa;
|
function encrypt(t) {
|
||||||
l ^= x;
|
return enc8(t.slice(0, 8)).concat(enc8(t.slice(8, 16)));
|
||||||
r ^= x;
|
|
||||||
l = (l << 31) | (l >>> 1);
|
|
||||||
x = ((l >>> 8) ^ r) & 0x00ff00ff;
|
|
||||||
r ^= x;
|
|
||||||
l ^= (x << 8);
|
|
||||||
x = ((l >>> 2) ^ r) & 0x33333333;
|
|
||||||
r ^= x;
|
|
||||||
l ^= (x << 2);
|
|
||||||
x = ((r >>> 16) ^ l) & 0x0000ffff;
|
|
||||||
l ^= x;
|
|
||||||
r ^= (x << 16);
|
|
||||||
x = ((r >>> 4) ^ l) & 0x0f0f0f0f;
|
|
||||||
l ^= x;
|
|
||||||
r ^= (x << 4);
|
|
||||||
|
|
||||||
// Spread ints to bytes
|
|
||||||
x = [r, l];
|
|
||||||
for (i = 0; i < 8; i++) {
|
|
||||||
b[i] = (x[i>>>2] >>> (8 * (3 - (i % 4)))) % 256;
|
|
||||||
if (b[i] < 0) { b[i] += 256; } // unsigned
|
|
||||||
}
|
}
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encrypt 16 bytes of text using passwd as key
|
setKeys(passwd); // Setup keys
|
||||||
function encrypt(t) {
|
return {'encrypt': encrypt}; // Public interface
|
||||||
return enc8(t.slice(0, 8)).concat(enc8(t.slice(8, 16)));
|
|
||||||
}
|
|
||||||
|
|
||||||
setKeys(passwd); // Setup keys
|
}; // function DES
|
||||||
return {'encrypt': encrypt}; // Public interface
|
|
||||||
|
|
||||||
}; // function DES
|
return DES;
|
||||||
|
});
|
||||||
|
|
|
||||||
180
core/display.js
180
core/display.js
|
|
@ -2,106 +2,18 @@
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Copyright (C) 2015 Samuel Mannehed for Cendio AB
|
* Copyright (C) 2015 Samuel Mannehed for Cendio AB
|
||||||
|
* Copyright (C) 2017 Pierre Ossman for Cendio AB
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*jslint browser: true, white: false */
|
/*jslint browser: true, white: false */
|
||||||
/*global Util, Base64, changeCursor */
|
|
||||||
|
|
||||||
/* [module]
|
|
||||||
* import Util from "./util";
|
|
||||||
* import Base64 from "./base64";
|
|
||||||
*/
|
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
/* [module] export default */ function Display(defaults) {
|
define(["core/util", "core/base64"],
|
||||||
this._drawCtx = null;
|
function (Util, Base64) {
|
||||||
this._c_forceCanvas = false;
|
|
||||||
|
|
||||||
this._renderQ = []; // queue drawing actions for in-oder rendering
|
|
||||||
this._flushing = false;
|
|
||||||
|
|
||||||
// the full frame buffer (logical canvas) size
|
|
||||||
this._fb_width = 0;
|
|
||||||
this._fb_height = 0;
|
|
||||||
|
|
||||||
this._prevDrawStyle = "";
|
|
||||||
this._tile = null;
|
|
||||||
this._tile16x16 = null;
|
|
||||||
this._tile_x = 0;
|
|
||||||
this._tile_y = 0;
|
|
||||||
|
|
||||||
Util.set_defaults(this, defaults, {
|
|
||||||
'true_color': true,
|
|
||||||
'colourMap': [],
|
|
||||||
'scale': 1.0,
|
|
||||||
'viewport': false,
|
|
||||||
'render_mode': '',
|
|
||||||
"onFlush": function () {},
|
|
||||||
});
|
|
||||||
|
|
||||||
Util.Debug(">> Display.constructor");
|
|
||||||
|
|
||||||
// The visible canvas
|
|
||||||
if (!this._target) {
|
|
||||||
throw new Error("Target must be set");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof this._target === 'string') {
|
|
||||||
throw new Error('target must be a DOM element');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this._target.getContext) {
|
|
||||||
throw new Error("no getContext method");
|
|
||||||
}
|
|
||||||
|
|
||||||
this._targetCtx = this._target.getContext('2d');
|
|
||||||
|
|
||||||
// the visible canvas viewport (i.e. what actually gets seen)
|
|
||||||
this._viewportLoc = { 'x': 0, 'y': 0, 'w': this._target.width, 'h': this._target.height };
|
|
||||||
|
|
||||||
// The hidden canvas, where we do the actual rendering
|
|
||||||
this._backbuffer = document.createElement('canvas');
|
|
||||||
this._drawCtx = this._backbuffer.getContext('2d');
|
|
||||||
|
|
||||||
this._damageBounds = { left:0, top:0,
|
|
||||||
right: this._backbuffer.width,
|
|
||||||
bottom: this._backbuffer.height };
|
|
||||||
|
|
||||||
Util.Debug("User Agent: " + navigator.userAgent);
|
|
||||||
if (Util.Engine.gecko) { Util.Debug("Browser: gecko " + Util.Engine.gecko); }
|
|
||||||
if (Util.Engine.webkit) { Util.Debug("Browser: webkit " + Util.Engine.webkit); }
|
|
||||||
if (Util.Engine.trident) { Util.Debug("Browser: trident " + Util.Engine.trident); }
|
|
||||||
if (Util.Engine.presto) { Util.Debug("Browser: presto " + Util.Engine.presto); }
|
|
||||||
|
|
||||||
this.clear();
|
|
||||||
|
|
||||||
// Check canvas features
|
|
||||||
if ('createImageData' in this._drawCtx) {
|
|
||||||
this._render_mode = 'canvas rendering';
|
|
||||||
} else {
|
|
||||||
throw new Error("Canvas does not support createImageData");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this._prefer_js === null) {
|
|
||||||
Util.Info("Prefering javascript operations");
|
|
||||||
this._prefer_js = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine browser support for setting the cursor via data URI scheme
|
|
||||||
if (this._cursor_uri || this._cursor_uri === null ||
|
|
||||||
this._cursor_uri === undefined) {
|
|
||||||
this._cursor_uri = Util.browserSupportsCursorURIs();
|
|
||||||
}
|
|
||||||
|
|
||||||
Util.Debug("<< Display.constructor");
|
|
||||||
};
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
@ -110,6 +22,88 @@
|
||||||
// ignore failure
|
// ignore failure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function Display(defaults) {
|
||||||
|
this._drawCtx = null;
|
||||||
|
this._c_forceCanvas = false;
|
||||||
|
|
||||||
|
this._renderQ = []; // queue drawing actions for in-oder rendering
|
||||||
|
this._flushing = false;
|
||||||
|
|
||||||
|
// the full frame buffer (logical canvas) size
|
||||||
|
this._fb_width = 0;
|
||||||
|
this._fb_height = 0;
|
||||||
|
|
||||||
|
this._prevDrawStyle = "";
|
||||||
|
this._tile = null;
|
||||||
|
this._tile16x16 = null;
|
||||||
|
this._tile_x = 0;
|
||||||
|
this._tile_y = 0;
|
||||||
|
|
||||||
|
Util.set_defaults(this, defaults, {
|
||||||
|
'true_color': true,
|
||||||
|
'colourMap': [],
|
||||||
|
'scale': 1.0,
|
||||||
|
'viewport': false,
|
||||||
|
'render_mode': '',
|
||||||
|
"onFlush": function () {},
|
||||||
|
});
|
||||||
|
|
||||||
|
Util.Debug(">> Display.constructor");
|
||||||
|
|
||||||
|
// The visible canvas
|
||||||
|
if (!this._target) {
|
||||||
|
throw new Error("Target must be set");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof this._target === 'string') {
|
||||||
|
throw new Error('target must be a DOM element');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this._target.getContext) {
|
||||||
|
throw new Error("no getContext method");
|
||||||
|
}
|
||||||
|
|
||||||
|
this._targetCtx = this._target.getContext('2d');
|
||||||
|
|
||||||
|
// the visible canvas viewport (i.e. what actually gets seen)
|
||||||
|
this._viewportLoc = { 'x': 0, 'y': 0, 'w': this._target.width, 'h': this._target.height };
|
||||||
|
|
||||||
|
// The hidden canvas, where we do the actual rendering
|
||||||
|
this._backbuffer = document.createElement('canvas');
|
||||||
|
this._drawCtx = this._backbuffer.getContext('2d');
|
||||||
|
|
||||||
|
this._damageBounds = { left:0, top:0,
|
||||||
|
right: this._backbuffer.width,
|
||||||
|
bottom: this._backbuffer.height };
|
||||||
|
|
||||||
|
Util.Debug("User Agent: " + navigator.userAgent);
|
||||||
|
if (Util.Engine.gecko) { Util.Debug("Browser: gecko " + Util.Engine.gecko); }
|
||||||
|
if (Util.Engine.webkit) { Util.Debug("Browser: webkit " + Util.Engine.webkit); }
|
||||||
|
if (Util.Engine.trident) { Util.Debug("Browser: trident " + Util.Engine.trident); }
|
||||||
|
if (Util.Engine.presto) { Util.Debug("Browser: presto " + Util.Engine.presto); }
|
||||||
|
|
||||||
|
this.clear();
|
||||||
|
|
||||||
|
// Check canvas features
|
||||||
|
if ('createImageData' in this._drawCtx) {
|
||||||
|
this._render_mode = 'canvas rendering';
|
||||||
|
} else {
|
||||||
|
throw new Error("Canvas does not support createImageData");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._prefer_js === null) {
|
||||||
|
Util.Info("Prefering javascript operations");
|
||||||
|
this._prefer_js = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine browser support for setting the cursor via data URI scheme
|
||||||
|
if (this._cursor_uri || this._cursor_uri === null ||
|
||||||
|
this._cursor_uri === undefined) {
|
||||||
|
this._cursor_uri = Util.browserSupportsCursorURIs();
|
||||||
|
}
|
||||||
|
|
||||||
|
Util.Debug("<< Display.constructor");
|
||||||
|
};
|
||||||
|
|
||||||
Display.prototype = {
|
Display.prototype = {
|
||||||
// Public methods
|
// Public methods
|
||||||
|
|
@ -876,4 +870,6 @@
|
||||||
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';
|
||||||
};
|
};
|
||||||
})();
|
|
||||||
|
return { Display: Display };
|
||||||
|
});
|
||||||
|
|
|
||||||
2455
core/inflator.js
2455
core/inflator.js
File diff suppressed because one or more lines are too long
|
|
@ -1,40 +0,0 @@
|
||||||
var zlib = require('pako/lib/zlib/inflate.js');
|
|
||||||
var ZStream = require('pako/lib/zlib/zstream.js');
|
|
||||||
|
|
||||||
function Inflate() {
|
|
||||||
this.strm = new ZStream();
|
|
||||||
this.chunkSize = 1024 * 10 * 10;
|
|
||||||
this.strm.output = new Uint8Array(this.chunkSize);
|
|
||||||
this.windowBits = 5;
|
|
||||||
|
|
||||||
zlib.inflateInit(this.strm, this.windowBits);
|
|
||||||
};
|
|
||||||
|
|
||||||
Inflate.prototype = {
|
|
||||||
inflate: function (data, flush, expected) {
|
|
||||||
this.strm.input = data;
|
|
||||||
this.strm.avail_in = this.strm.input.length;
|
|
||||||
this.strm.next_in = 0;
|
|
||||||
this.strm.next_out = 0;
|
|
||||||
|
|
||||||
// resize our output buffer if it's too small
|
|
||||||
// (we could just use multiple chunks, but that would cause an extra
|
|
||||||
// allocation each time to flatten the chunks)
|
|
||||||
if (expected > this.chunkSize) {
|
|
||||||
this.chunkSize = expected;
|
|
||||||
this.strm.output = new Uint8Array(this.chunkSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.strm.avail_out = this.chunkSize;
|
|
||||||
|
|
||||||
zlib.inflate(this.strm, flush);
|
|
||||||
|
|
||||||
return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
|
|
||||||
},
|
|
||||||
|
|
||||||
reset: function () {
|
|
||||||
zlib.inflateReset(this.strm);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = { Inflate: Inflate };
|
|
||||||
|
|
@ -2,28 +2,21 @@
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Copyright (C) 2013 Samuel Mannehed for Cendio AB
|
* Copyright (C) 2013 Samuel Mannehed for Cendio AB
|
||||||
|
* Copyright (C) 2017 Pierre Ossman for Cendio AB
|
||||||
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*jslint browser: true, white: false */
|
/*jslint browser: true, white: false */
|
||||||
/*global window, Util */
|
|
||||||
|
|
||||||
/* [module]
|
|
||||||
* import Util from "../util";
|
|
||||||
* import KeyboardUtil from "./util";
|
|
||||||
*/
|
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
/* [module] export */ var Keyboard;
|
define(["core/util", "core/input/util"],
|
||||||
|
function (Util, KeyboardUtil) {
|
||||||
(function () {
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Keyboard event handler
|
// Keyboard event handler
|
||||||
//
|
//
|
||||||
|
|
||||||
Keyboard = function (defaults) {
|
function Keyboard(defaults) {
|
||||||
this._keyDownList = []; // List of depressed keys
|
this._keyDownList = []; // List of depressed keys
|
||||||
// (even if they are happy)
|
// (even if they are happy)
|
||||||
|
|
||||||
|
|
@ -159,12 +152,8 @@
|
||||||
|
|
||||||
['onKeyPress', 'rw', 'func'] // Handler for key press/release
|
['onKeyPress', 'rw', 'func'] // Handler for key press/release
|
||||||
]);
|
]);
|
||||||
})();
|
|
||||||
|
|
||||||
/* [module] export */ var Mouse;
|
function Mouse(defaults) {
|
||||||
|
|
||||||
(function () {
|
|
||||||
Mouse = function (defaults) {
|
|
||||||
this._mouseCaptured = false;
|
this._mouseCaptured = false;
|
||||||
|
|
||||||
this._doubleClickTimer = null;
|
this._doubleClickTimer = null;
|
||||||
|
|
@ -403,4 +392,7 @@
|
||||||
['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)
|
||||||
]);
|
]);
|
||||||
})();
|
|
||||||
|
return { Keyboard: Keyboard,
|
||||||
|
Mouse: Mouse };
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,384 +1,386 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var KeyTable = {
|
define(function () {
|
||||||
XK_VoidSymbol: 0xffffff, /* Void symbol */
|
var KeyTable = {
|
||||||
|
XK_VoidSymbol: 0xffffff, /* Void symbol */
|
||||||
|
|
||||||
XK_BackSpace: 0xff08, /* Back space, back char */
|
XK_BackSpace: 0xff08, /* Back space, back char */
|
||||||
XK_Tab: 0xff09,
|
XK_Tab: 0xff09,
|
||||||
XK_Linefeed: 0xff0a, /* Linefeed, LF */
|
XK_Linefeed: 0xff0a, /* Linefeed, LF */
|
||||||
XK_Clear: 0xff0b,
|
XK_Clear: 0xff0b,
|
||||||
XK_Return: 0xff0d, /* Return, enter */
|
XK_Return: 0xff0d, /* Return, enter */
|
||||||
XK_Pause: 0xff13, /* Pause, hold */
|
XK_Pause: 0xff13, /* Pause, hold */
|
||||||
XK_Scroll_Lock: 0xff14,
|
XK_Scroll_Lock: 0xff14,
|
||||||
XK_Sys_Req: 0xff15,
|
XK_Sys_Req: 0xff15,
|
||||||
XK_Escape: 0xff1b,
|
XK_Escape: 0xff1b,
|
||||||
XK_Delete: 0xffff, /* Delete, rubout */
|
XK_Delete: 0xffff, /* Delete, rubout */
|
||||||
|
|
||||||
/* Cursor control & motion */
|
/* Cursor control & motion */
|
||||||
|
|
||||||
XK_Home: 0xff50,
|
XK_Home: 0xff50,
|
||||||
XK_Left: 0xff51, /* Move left, left arrow */
|
XK_Left: 0xff51, /* Move left, left arrow */
|
||||||
XK_Up: 0xff52, /* Move up, up arrow */
|
XK_Up: 0xff52, /* Move up, up arrow */
|
||||||
XK_Right: 0xff53, /* Move right, right arrow */
|
XK_Right: 0xff53, /* Move right, right arrow */
|
||||||
XK_Down: 0xff54, /* Move down, down arrow */
|
XK_Down: 0xff54, /* Move down, down arrow */
|
||||||
XK_Prior: 0xff55, /* Prior, previous */
|
XK_Prior: 0xff55, /* Prior, previous */
|
||||||
XK_Page_Up: 0xff55,
|
XK_Page_Up: 0xff55,
|
||||||
XK_Next: 0xff56, /* Next */
|
XK_Next: 0xff56, /* Next */
|
||||||
XK_Page_Down: 0xff56,
|
XK_Page_Down: 0xff56,
|
||||||
XK_End: 0xff57, /* EOL */
|
XK_End: 0xff57, /* EOL */
|
||||||
XK_Begin: 0xff58, /* BOL */
|
XK_Begin: 0xff58, /* BOL */
|
||||||
|
|
||||||
|
|
||||||
/* Misc functions */
|
/* Misc functions */
|
||||||
|
|
||||||
XK_Select: 0xff60, /* Select, mark */
|
XK_Select: 0xff60, /* Select, mark */
|
||||||
XK_Print: 0xff61,
|
XK_Print: 0xff61,
|
||||||
XK_Execute: 0xff62, /* Execute, run, do */
|
XK_Execute: 0xff62, /* Execute, run, do */
|
||||||
XK_Insert: 0xff63, /* Insert, insert here */
|
XK_Insert: 0xff63, /* Insert, insert here */
|
||||||
XK_Undo: 0xff65,
|
XK_Undo: 0xff65,
|
||||||
XK_Redo: 0xff66, /* Redo, again */
|
XK_Redo: 0xff66, /* Redo, again */
|
||||||
XK_Menu: 0xff67,
|
XK_Menu: 0xff67,
|
||||||
XK_Find: 0xff68, /* Find, search */
|
XK_Find: 0xff68, /* Find, search */
|
||||||
XK_Cancel: 0xff69, /* Cancel, stop, abort, exit */
|
XK_Cancel: 0xff69, /* Cancel, stop, abort, exit */
|
||||||
XK_Help: 0xff6a, /* Help */
|
XK_Help: 0xff6a, /* Help */
|
||||||
XK_Break: 0xff6b,
|
XK_Break: 0xff6b,
|
||||||
XK_Mode_switch: 0xff7e, /* Character set switch */
|
XK_Mode_switch: 0xff7e, /* Character set switch */
|
||||||
XK_script_switch: 0xff7e, /* Alias for mode_switch */
|
XK_script_switch: 0xff7e, /* Alias for mode_switch */
|
||||||
XK_Num_Lock: 0xff7f,
|
XK_Num_Lock: 0xff7f,
|
||||||
|
|
||||||
/* Keypad functions, keypad numbers cleverly chosen to map to ASCII */
|
/* Keypad functions, keypad numbers cleverly chosen to map to ASCII */
|
||||||
|
|
||||||
XK_KP_Space: 0xff80, /* Space */
|
XK_KP_Space: 0xff80, /* Space */
|
||||||
XK_KP_Tab: 0xff89,
|
XK_KP_Tab: 0xff89,
|
||||||
XK_KP_Enter: 0xff8d, /* Enter */
|
XK_KP_Enter: 0xff8d, /* Enter */
|
||||||
XK_KP_F1: 0xff91, /* PF1, KP_A, ... */
|
XK_KP_F1: 0xff91, /* PF1, KP_A, ... */
|
||||||
XK_KP_F2: 0xff92,
|
XK_KP_F2: 0xff92,
|
||||||
XK_KP_F3: 0xff93,
|
XK_KP_F3: 0xff93,
|
||||||
XK_KP_F4: 0xff94,
|
XK_KP_F4: 0xff94,
|
||||||
XK_KP_Home: 0xff95,
|
XK_KP_Home: 0xff95,
|
||||||
XK_KP_Left: 0xff96,
|
XK_KP_Left: 0xff96,
|
||||||
XK_KP_Up: 0xff97,
|
XK_KP_Up: 0xff97,
|
||||||
XK_KP_Right: 0xff98,
|
XK_KP_Right: 0xff98,
|
||||||
XK_KP_Down: 0xff99,
|
XK_KP_Down: 0xff99,
|
||||||
XK_KP_Prior: 0xff9a,
|
XK_KP_Prior: 0xff9a,
|
||||||
XK_KP_Page_Up: 0xff9a,
|
XK_KP_Page_Up: 0xff9a,
|
||||||
XK_KP_Next: 0xff9b,
|
XK_KP_Next: 0xff9b,
|
||||||
XK_KP_Page_Down: 0xff9b,
|
XK_KP_Page_Down: 0xff9b,
|
||||||
XK_KP_End: 0xff9c,
|
XK_KP_End: 0xff9c,
|
||||||
XK_KP_Begin: 0xff9d,
|
XK_KP_Begin: 0xff9d,
|
||||||
XK_KP_Insert: 0xff9e,
|
XK_KP_Insert: 0xff9e,
|
||||||
XK_KP_Delete: 0xff9f,
|
XK_KP_Delete: 0xff9f,
|
||||||
XK_KP_Equal: 0xffbd, /* Equals */
|
XK_KP_Equal: 0xffbd, /* Equals */
|
||||||
XK_KP_Multiply: 0xffaa,
|
XK_KP_Multiply: 0xffaa,
|
||||||
XK_KP_Add: 0xffab,
|
XK_KP_Add: 0xffab,
|
||||||
XK_KP_Separator: 0xffac, /* Separator, often comma */
|
XK_KP_Separator: 0xffac, /* Separator, often comma */
|
||||||
XK_KP_Subtract: 0xffad,
|
XK_KP_Subtract: 0xffad,
|
||||||
XK_KP_Decimal: 0xffae,
|
XK_KP_Decimal: 0xffae,
|
||||||
XK_KP_Divide: 0xffaf,
|
XK_KP_Divide: 0xffaf,
|
||||||
|
|
||||||
XK_KP_0: 0xffb0,
|
XK_KP_0: 0xffb0,
|
||||||
XK_KP_1: 0xffb1,
|
XK_KP_1: 0xffb1,
|
||||||
XK_KP_2: 0xffb2,
|
XK_KP_2: 0xffb2,
|
||||||
XK_KP_3: 0xffb3,
|
XK_KP_3: 0xffb3,
|
||||||
XK_KP_4: 0xffb4,
|
XK_KP_4: 0xffb4,
|
||||||
XK_KP_5: 0xffb5,
|
XK_KP_5: 0xffb5,
|
||||||
XK_KP_6: 0xffb6,
|
XK_KP_6: 0xffb6,
|
||||||
XK_KP_7: 0xffb7,
|
XK_KP_7: 0xffb7,
|
||||||
XK_KP_8: 0xffb8,
|
XK_KP_8: 0xffb8,
|
||||||
XK_KP_9: 0xffb9,
|
XK_KP_9: 0xffb9,
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Auxiliary functions; note the duplicate definitions for left and right
|
* Auxiliary functions; note the duplicate definitions for left and right
|
||||||
* function keys; Sun keyboards and a few other manufacturers have such
|
* function keys; Sun keyboards and a few other manufacturers have such
|
||||||
* function key groups on the left and/or right sides of the keyboard.
|
* function key groups on the left and/or right sides of the keyboard.
|
||||||
* We've not found a keyboard with more than 35 function keys total.
|
* We've not found a keyboard with more than 35 function keys total.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
XK_F1: 0xffbe,
|
XK_F1: 0xffbe,
|
||||||
XK_F2: 0xffbf,
|
XK_F2: 0xffbf,
|
||||||
XK_F3: 0xffc0,
|
XK_F3: 0xffc0,
|
||||||
XK_F4: 0xffc1,
|
XK_F4: 0xffc1,
|
||||||
XK_F5: 0xffc2,
|
XK_F5: 0xffc2,
|
||||||
XK_F6: 0xffc3,
|
XK_F6: 0xffc3,
|
||||||
XK_F7: 0xffc4,
|
XK_F7: 0xffc4,
|
||||||
XK_F8: 0xffc5,
|
XK_F8: 0xffc5,
|
||||||
XK_F9: 0xffc6,
|
XK_F9: 0xffc6,
|
||||||
XK_F10: 0xffc7,
|
XK_F10: 0xffc7,
|
||||||
XK_F11: 0xffc8,
|
XK_F11: 0xffc8,
|
||||||
XK_L1: 0xffc8,
|
XK_L1: 0xffc8,
|
||||||
XK_F12: 0xffc9,
|
XK_F12: 0xffc9,
|
||||||
XK_L2: 0xffc9,
|
XK_L2: 0xffc9,
|
||||||
XK_F13: 0xffca,
|
XK_F13: 0xffca,
|
||||||
XK_L3: 0xffca,
|
XK_L3: 0xffca,
|
||||||
XK_F14: 0xffcb,
|
XK_F14: 0xffcb,
|
||||||
XK_L4: 0xffcb,
|
XK_L4: 0xffcb,
|
||||||
XK_F15: 0xffcc,
|
XK_F15: 0xffcc,
|
||||||
XK_L5: 0xffcc,
|
XK_L5: 0xffcc,
|
||||||
XK_F16: 0xffcd,
|
XK_F16: 0xffcd,
|
||||||
XK_L6: 0xffcd,
|
XK_L6: 0xffcd,
|
||||||
XK_F17: 0xffce,
|
XK_F17: 0xffce,
|
||||||
XK_L7: 0xffce,
|
XK_L7: 0xffce,
|
||||||
XK_F18: 0xffcf,
|
XK_F18: 0xffcf,
|
||||||
XK_L8: 0xffcf,
|
XK_L8: 0xffcf,
|
||||||
XK_F19: 0xffd0,
|
XK_F19: 0xffd0,
|
||||||
XK_L9: 0xffd0,
|
XK_L9: 0xffd0,
|
||||||
XK_F20: 0xffd1,
|
XK_F20: 0xffd1,
|
||||||
XK_L10: 0xffd1,
|
XK_L10: 0xffd1,
|
||||||
XK_F21: 0xffd2,
|
XK_F21: 0xffd2,
|
||||||
XK_R1: 0xffd2,
|
XK_R1: 0xffd2,
|
||||||
XK_F22: 0xffd3,
|
XK_F22: 0xffd3,
|
||||||
XK_R2: 0xffd3,
|
XK_R2: 0xffd3,
|
||||||
XK_F23: 0xffd4,
|
XK_F23: 0xffd4,
|
||||||
XK_R3: 0xffd4,
|
XK_R3: 0xffd4,
|
||||||
XK_F24: 0xffd5,
|
XK_F24: 0xffd5,
|
||||||
XK_R4: 0xffd5,
|
XK_R4: 0xffd5,
|
||||||
XK_F25: 0xffd6,
|
XK_F25: 0xffd6,
|
||||||
XK_R5: 0xffd6,
|
XK_R5: 0xffd6,
|
||||||
XK_F26: 0xffd7,
|
XK_F26: 0xffd7,
|
||||||
XK_R6: 0xffd7,
|
XK_R6: 0xffd7,
|
||||||
XK_F27: 0xffd8,
|
XK_F27: 0xffd8,
|
||||||
XK_R7: 0xffd8,
|
XK_R7: 0xffd8,
|
||||||
XK_F28: 0xffd9,
|
XK_F28: 0xffd9,
|
||||||
XK_R8: 0xffd9,
|
XK_R8: 0xffd9,
|
||||||
XK_F29: 0xffda,
|
XK_F29: 0xffda,
|
||||||
XK_R9: 0xffda,
|
XK_R9: 0xffda,
|
||||||
XK_F30: 0xffdb,
|
XK_F30: 0xffdb,
|
||||||
XK_R10: 0xffdb,
|
XK_R10: 0xffdb,
|
||||||
XK_F31: 0xffdc,
|
XK_F31: 0xffdc,
|
||||||
XK_R11: 0xffdc,
|
XK_R11: 0xffdc,
|
||||||
XK_F32: 0xffdd,
|
XK_F32: 0xffdd,
|
||||||
XK_R12: 0xffdd,
|
XK_R12: 0xffdd,
|
||||||
XK_F33: 0xffde,
|
XK_F33: 0xffde,
|
||||||
XK_R13: 0xffde,
|
XK_R13: 0xffde,
|
||||||
XK_F34: 0xffdf,
|
XK_F34: 0xffdf,
|
||||||
XK_R14: 0xffdf,
|
XK_R14: 0xffdf,
|
||||||
XK_F35: 0xffe0,
|
XK_F35: 0xffe0,
|
||||||
XK_R15: 0xffe0,
|
XK_R15: 0xffe0,
|
||||||
|
|
||||||
/* Modifiers */
|
/* Modifiers */
|
||||||
|
|
||||||
XK_Shift_L: 0xffe1, /* Left shift */
|
XK_Shift_L: 0xffe1, /* Left shift */
|
||||||
XK_Shift_R: 0xffe2, /* Right shift */
|
XK_Shift_R: 0xffe2, /* Right shift */
|
||||||
XK_Control_L: 0xffe3, /* Left control */
|
XK_Control_L: 0xffe3, /* Left control */
|
||||||
XK_Control_R: 0xffe4, /* Right control */
|
XK_Control_R: 0xffe4, /* Right control */
|
||||||
XK_Caps_Lock: 0xffe5, /* Caps lock */
|
XK_Caps_Lock: 0xffe5, /* Caps lock */
|
||||||
XK_Shift_Lock: 0xffe6, /* Shift lock */
|
XK_Shift_Lock: 0xffe6, /* Shift lock */
|
||||||
|
|
||||||
XK_Meta_L: 0xffe7, /* Left meta */
|
XK_Meta_L: 0xffe7, /* Left meta */
|
||||||
XK_Meta_R: 0xffe8, /* Right meta */
|
XK_Meta_R: 0xffe8, /* Right meta */
|
||||||
XK_Alt_L: 0xffe9, /* Left alt */
|
XK_Alt_L: 0xffe9, /* Left alt */
|
||||||
XK_Alt_R: 0xffea, /* Right alt */
|
XK_Alt_R: 0xffea, /* Right alt */
|
||||||
XK_Super_L: 0xffeb, /* Left super */
|
XK_Super_L: 0xffeb, /* Left super */
|
||||||
XK_Super_R: 0xffec, /* Right super */
|
XK_Super_R: 0xffec, /* Right super */
|
||||||
XK_Hyper_L: 0xffed, /* Left hyper */
|
XK_Hyper_L: 0xffed, /* Left hyper */
|
||||||
XK_Hyper_R: 0xffee, /* Right hyper */
|
XK_Hyper_R: 0xffee, /* Right hyper */
|
||||||
|
|
||||||
XK_ISO_Level3_Shift: 0xfe03, /* AltGr */
|
XK_ISO_Level3_Shift: 0xfe03, /* AltGr */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Latin 1
|
* Latin 1
|
||||||
* (ISO/IEC 8859-1: Unicode U+0020..U+00FF)
|
* (ISO/IEC 8859-1: Unicode U+0020..U+00FF)
|
||||||
* Byte 3: 0
|
* Byte 3: 0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
XK_space: 0x0020, /* U+0020 SPACE */
|
XK_space: 0x0020, /* U+0020 SPACE */
|
||||||
XK_exclam: 0x0021, /* U+0021 EXCLAMATION MARK */
|
XK_exclam: 0x0021, /* U+0021 EXCLAMATION MARK */
|
||||||
XK_quotedbl: 0x0022, /* U+0022 QUOTATION MARK */
|
XK_quotedbl: 0x0022, /* U+0022 QUOTATION MARK */
|
||||||
XK_numbersign: 0x0023, /* U+0023 NUMBER SIGN */
|
XK_numbersign: 0x0023, /* U+0023 NUMBER SIGN */
|
||||||
XK_dollar: 0x0024, /* U+0024 DOLLAR SIGN */
|
XK_dollar: 0x0024, /* U+0024 DOLLAR SIGN */
|
||||||
XK_percent: 0x0025, /* U+0025 PERCENT SIGN */
|
XK_percent: 0x0025, /* U+0025 PERCENT SIGN */
|
||||||
XK_ampersand: 0x0026, /* U+0026 AMPERSAND */
|
XK_ampersand: 0x0026, /* U+0026 AMPERSAND */
|
||||||
XK_apostrophe: 0x0027, /* U+0027 APOSTROPHE */
|
XK_apostrophe: 0x0027, /* U+0027 APOSTROPHE */
|
||||||
XK_quoteright: 0x0027, /* deprecated */
|
XK_quoteright: 0x0027, /* deprecated */
|
||||||
XK_parenleft: 0x0028, /* U+0028 LEFT PARENTHESIS */
|
XK_parenleft: 0x0028, /* U+0028 LEFT PARENTHESIS */
|
||||||
XK_parenright: 0x0029, /* U+0029 RIGHT PARENTHESIS */
|
XK_parenright: 0x0029, /* U+0029 RIGHT PARENTHESIS */
|
||||||
XK_asterisk: 0x002a, /* U+002A ASTERISK */
|
XK_asterisk: 0x002a, /* U+002A ASTERISK */
|
||||||
XK_plus: 0x002b, /* U+002B PLUS SIGN */
|
XK_plus: 0x002b, /* U+002B PLUS SIGN */
|
||||||
XK_comma: 0x002c, /* U+002C COMMA */
|
XK_comma: 0x002c, /* U+002C COMMA */
|
||||||
XK_minus: 0x002d, /* U+002D HYPHEN-MINUS */
|
XK_minus: 0x002d, /* U+002D HYPHEN-MINUS */
|
||||||
XK_period: 0x002e, /* U+002E FULL STOP */
|
XK_period: 0x002e, /* U+002E FULL STOP */
|
||||||
XK_slash: 0x002f, /* U+002F SOLIDUS */
|
XK_slash: 0x002f, /* U+002F SOLIDUS */
|
||||||
XK_0: 0x0030, /* U+0030 DIGIT ZERO */
|
XK_0: 0x0030, /* U+0030 DIGIT ZERO */
|
||||||
XK_1: 0x0031, /* U+0031 DIGIT ONE */
|
XK_1: 0x0031, /* U+0031 DIGIT ONE */
|
||||||
XK_2: 0x0032, /* U+0032 DIGIT TWO */
|
XK_2: 0x0032, /* U+0032 DIGIT TWO */
|
||||||
XK_3: 0x0033, /* U+0033 DIGIT THREE */
|
XK_3: 0x0033, /* U+0033 DIGIT THREE */
|
||||||
XK_4: 0x0034, /* U+0034 DIGIT FOUR */
|
XK_4: 0x0034, /* U+0034 DIGIT FOUR */
|
||||||
XK_5: 0x0035, /* U+0035 DIGIT FIVE */
|
XK_5: 0x0035, /* U+0035 DIGIT FIVE */
|
||||||
XK_6: 0x0036, /* U+0036 DIGIT SIX */
|
XK_6: 0x0036, /* U+0036 DIGIT SIX */
|
||||||
XK_7: 0x0037, /* U+0037 DIGIT SEVEN */
|
XK_7: 0x0037, /* U+0037 DIGIT SEVEN */
|
||||||
XK_8: 0x0038, /* U+0038 DIGIT EIGHT */
|
XK_8: 0x0038, /* U+0038 DIGIT EIGHT */
|
||||||
XK_9: 0x0039, /* U+0039 DIGIT NINE */
|
XK_9: 0x0039, /* U+0039 DIGIT NINE */
|
||||||
XK_colon: 0x003a, /* U+003A COLON */
|
XK_colon: 0x003a, /* U+003A COLON */
|
||||||
XK_semicolon: 0x003b, /* U+003B SEMICOLON */
|
XK_semicolon: 0x003b, /* U+003B SEMICOLON */
|
||||||
XK_less: 0x003c, /* U+003C LESS-THAN SIGN */
|
XK_less: 0x003c, /* U+003C LESS-THAN SIGN */
|
||||||
XK_equal: 0x003d, /* U+003D EQUALS SIGN */
|
XK_equal: 0x003d, /* U+003D EQUALS SIGN */
|
||||||
XK_greater: 0x003e, /* U+003E GREATER-THAN SIGN */
|
XK_greater: 0x003e, /* U+003E GREATER-THAN SIGN */
|
||||||
XK_question: 0x003f, /* U+003F QUESTION MARK */
|
XK_question: 0x003f, /* U+003F QUESTION MARK */
|
||||||
XK_at: 0x0040, /* U+0040 COMMERCIAL AT */
|
XK_at: 0x0040, /* U+0040 COMMERCIAL AT */
|
||||||
XK_A: 0x0041, /* U+0041 LATIN CAPITAL LETTER A */
|
XK_A: 0x0041, /* U+0041 LATIN CAPITAL LETTER A */
|
||||||
XK_B: 0x0042, /* U+0042 LATIN CAPITAL LETTER B */
|
XK_B: 0x0042, /* U+0042 LATIN CAPITAL LETTER B */
|
||||||
XK_C: 0x0043, /* U+0043 LATIN CAPITAL LETTER C */
|
XK_C: 0x0043, /* U+0043 LATIN CAPITAL LETTER C */
|
||||||
XK_D: 0x0044, /* U+0044 LATIN CAPITAL LETTER D */
|
XK_D: 0x0044, /* U+0044 LATIN CAPITAL LETTER D */
|
||||||
XK_E: 0x0045, /* U+0045 LATIN CAPITAL LETTER E */
|
XK_E: 0x0045, /* U+0045 LATIN CAPITAL LETTER E */
|
||||||
XK_F: 0x0046, /* U+0046 LATIN CAPITAL LETTER F */
|
XK_F: 0x0046, /* U+0046 LATIN CAPITAL LETTER F */
|
||||||
XK_G: 0x0047, /* U+0047 LATIN CAPITAL LETTER G */
|
XK_G: 0x0047, /* U+0047 LATIN CAPITAL LETTER G */
|
||||||
XK_H: 0x0048, /* U+0048 LATIN CAPITAL LETTER H */
|
XK_H: 0x0048, /* U+0048 LATIN CAPITAL LETTER H */
|
||||||
XK_I: 0x0049, /* U+0049 LATIN CAPITAL LETTER I */
|
XK_I: 0x0049, /* U+0049 LATIN CAPITAL LETTER I */
|
||||||
XK_J: 0x004a, /* U+004A LATIN CAPITAL LETTER J */
|
XK_J: 0x004a, /* U+004A LATIN CAPITAL LETTER J */
|
||||||
XK_K: 0x004b, /* U+004B LATIN CAPITAL LETTER K */
|
XK_K: 0x004b, /* U+004B LATIN CAPITAL LETTER K */
|
||||||
XK_L: 0x004c, /* U+004C LATIN CAPITAL LETTER L */
|
XK_L: 0x004c, /* U+004C LATIN CAPITAL LETTER L */
|
||||||
XK_M: 0x004d, /* U+004D LATIN CAPITAL LETTER M */
|
XK_M: 0x004d, /* U+004D LATIN CAPITAL LETTER M */
|
||||||
XK_N: 0x004e, /* U+004E LATIN CAPITAL LETTER N */
|
XK_N: 0x004e, /* U+004E LATIN CAPITAL LETTER N */
|
||||||
XK_O: 0x004f, /* U+004F LATIN CAPITAL LETTER O */
|
XK_O: 0x004f, /* U+004F LATIN CAPITAL LETTER O */
|
||||||
XK_P: 0x0050, /* U+0050 LATIN CAPITAL LETTER P */
|
XK_P: 0x0050, /* U+0050 LATIN CAPITAL LETTER P */
|
||||||
XK_Q: 0x0051, /* U+0051 LATIN CAPITAL LETTER Q */
|
XK_Q: 0x0051, /* U+0051 LATIN CAPITAL LETTER Q */
|
||||||
XK_R: 0x0052, /* U+0052 LATIN CAPITAL LETTER R */
|
XK_R: 0x0052, /* U+0052 LATIN CAPITAL LETTER R */
|
||||||
XK_S: 0x0053, /* U+0053 LATIN CAPITAL LETTER S */
|
XK_S: 0x0053, /* U+0053 LATIN CAPITAL LETTER S */
|
||||||
XK_T: 0x0054, /* U+0054 LATIN CAPITAL LETTER T */
|
XK_T: 0x0054, /* U+0054 LATIN CAPITAL LETTER T */
|
||||||
XK_U: 0x0055, /* U+0055 LATIN CAPITAL LETTER U */
|
XK_U: 0x0055, /* U+0055 LATIN CAPITAL LETTER U */
|
||||||
XK_V: 0x0056, /* U+0056 LATIN CAPITAL LETTER V */
|
XK_V: 0x0056, /* U+0056 LATIN CAPITAL LETTER V */
|
||||||
XK_W: 0x0057, /* U+0057 LATIN CAPITAL LETTER W */
|
XK_W: 0x0057, /* U+0057 LATIN CAPITAL LETTER W */
|
||||||
XK_X: 0x0058, /* U+0058 LATIN CAPITAL LETTER X */
|
XK_X: 0x0058, /* U+0058 LATIN CAPITAL LETTER X */
|
||||||
XK_Y: 0x0059, /* U+0059 LATIN CAPITAL LETTER Y */
|
XK_Y: 0x0059, /* U+0059 LATIN CAPITAL LETTER Y */
|
||||||
XK_Z: 0x005a, /* U+005A LATIN CAPITAL LETTER Z */
|
XK_Z: 0x005a, /* U+005A LATIN CAPITAL LETTER Z */
|
||||||
XK_bracketleft: 0x005b, /* U+005B LEFT SQUARE BRACKET */
|
XK_bracketleft: 0x005b, /* U+005B LEFT SQUARE BRACKET */
|
||||||
XK_backslash: 0x005c, /* U+005C REVERSE SOLIDUS */
|
XK_backslash: 0x005c, /* U+005C REVERSE SOLIDUS */
|
||||||
XK_bracketright: 0x005d, /* U+005D RIGHT SQUARE BRACKET */
|
XK_bracketright: 0x005d, /* U+005D RIGHT SQUARE BRACKET */
|
||||||
XK_asciicircum: 0x005e, /* U+005E CIRCUMFLEX ACCENT */
|
XK_asciicircum: 0x005e, /* U+005E CIRCUMFLEX ACCENT */
|
||||||
XK_underscore: 0x005f, /* U+005F LOW LINE */
|
XK_underscore: 0x005f, /* U+005F LOW LINE */
|
||||||
XK_grave: 0x0060, /* U+0060 GRAVE ACCENT */
|
XK_grave: 0x0060, /* U+0060 GRAVE ACCENT */
|
||||||
XK_quoteleft: 0x0060, /* deprecated */
|
XK_quoteleft: 0x0060, /* deprecated */
|
||||||
XK_a: 0x0061, /* U+0061 LATIN SMALL LETTER A */
|
XK_a: 0x0061, /* U+0061 LATIN SMALL LETTER A */
|
||||||
XK_b: 0x0062, /* U+0062 LATIN SMALL LETTER B */
|
XK_b: 0x0062, /* U+0062 LATIN SMALL LETTER B */
|
||||||
XK_c: 0x0063, /* U+0063 LATIN SMALL LETTER C */
|
XK_c: 0x0063, /* U+0063 LATIN SMALL LETTER C */
|
||||||
XK_d: 0x0064, /* U+0064 LATIN SMALL LETTER D */
|
XK_d: 0x0064, /* U+0064 LATIN SMALL LETTER D */
|
||||||
XK_e: 0x0065, /* U+0065 LATIN SMALL LETTER E */
|
XK_e: 0x0065, /* U+0065 LATIN SMALL LETTER E */
|
||||||
XK_f: 0x0066, /* U+0066 LATIN SMALL LETTER F */
|
XK_f: 0x0066, /* U+0066 LATIN SMALL LETTER F */
|
||||||
XK_g: 0x0067, /* U+0067 LATIN SMALL LETTER G */
|
XK_g: 0x0067, /* U+0067 LATIN SMALL LETTER G */
|
||||||
XK_h: 0x0068, /* U+0068 LATIN SMALL LETTER H */
|
XK_h: 0x0068, /* U+0068 LATIN SMALL LETTER H */
|
||||||
XK_i: 0x0069, /* U+0069 LATIN SMALL LETTER I */
|
XK_i: 0x0069, /* U+0069 LATIN SMALL LETTER I */
|
||||||
XK_j: 0x006a, /* U+006A LATIN SMALL LETTER J */
|
XK_j: 0x006a, /* U+006A LATIN SMALL LETTER J */
|
||||||
XK_k: 0x006b, /* U+006B LATIN SMALL LETTER K */
|
XK_k: 0x006b, /* U+006B LATIN SMALL LETTER K */
|
||||||
XK_l: 0x006c, /* U+006C LATIN SMALL LETTER L */
|
XK_l: 0x006c, /* U+006C LATIN SMALL LETTER L */
|
||||||
XK_m: 0x006d, /* U+006D LATIN SMALL LETTER M */
|
XK_m: 0x006d, /* U+006D LATIN SMALL LETTER M */
|
||||||
XK_n: 0x006e, /* U+006E LATIN SMALL LETTER N */
|
XK_n: 0x006e, /* U+006E LATIN SMALL LETTER N */
|
||||||
XK_o: 0x006f, /* U+006F LATIN SMALL LETTER O */
|
XK_o: 0x006f, /* U+006F LATIN SMALL LETTER O */
|
||||||
XK_p: 0x0070, /* U+0070 LATIN SMALL LETTER P */
|
XK_p: 0x0070, /* U+0070 LATIN SMALL LETTER P */
|
||||||
XK_q: 0x0071, /* U+0071 LATIN SMALL LETTER Q */
|
XK_q: 0x0071, /* U+0071 LATIN SMALL LETTER Q */
|
||||||
XK_r: 0x0072, /* U+0072 LATIN SMALL LETTER R */
|
XK_r: 0x0072, /* U+0072 LATIN SMALL LETTER R */
|
||||||
XK_s: 0x0073, /* U+0073 LATIN SMALL LETTER S */
|
XK_s: 0x0073, /* U+0073 LATIN SMALL LETTER S */
|
||||||
XK_t: 0x0074, /* U+0074 LATIN SMALL LETTER T */
|
XK_t: 0x0074, /* U+0074 LATIN SMALL LETTER T */
|
||||||
XK_u: 0x0075, /* U+0075 LATIN SMALL LETTER U */
|
XK_u: 0x0075, /* U+0075 LATIN SMALL LETTER U */
|
||||||
XK_v: 0x0076, /* U+0076 LATIN SMALL LETTER V */
|
XK_v: 0x0076, /* U+0076 LATIN SMALL LETTER V */
|
||||||
XK_w: 0x0077, /* U+0077 LATIN SMALL LETTER W */
|
XK_w: 0x0077, /* U+0077 LATIN SMALL LETTER W */
|
||||||
XK_x: 0x0078, /* U+0078 LATIN SMALL LETTER X */
|
XK_x: 0x0078, /* U+0078 LATIN SMALL LETTER X */
|
||||||
XK_y: 0x0079, /* U+0079 LATIN SMALL LETTER Y */
|
XK_y: 0x0079, /* U+0079 LATIN SMALL LETTER Y */
|
||||||
XK_z: 0x007a, /* U+007A LATIN SMALL LETTER Z */
|
XK_z: 0x007a, /* U+007A LATIN SMALL LETTER Z */
|
||||||
XK_braceleft: 0x007b, /* U+007B LEFT CURLY BRACKET */
|
XK_braceleft: 0x007b, /* U+007B LEFT CURLY BRACKET */
|
||||||
XK_bar: 0x007c, /* U+007C VERTICAL LINE */
|
XK_bar: 0x007c, /* U+007C VERTICAL LINE */
|
||||||
XK_braceright: 0x007d, /* U+007D RIGHT CURLY BRACKET */
|
XK_braceright: 0x007d, /* U+007D RIGHT CURLY BRACKET */
|
||||||
XK_asciitilde: 0x007e, /* U+007E TILDE */
|
XK_asciitilde: 0x007e, /* U+007E TILDE */
|
||||||
|
|
||||||
XK_nobreakspace: 0x00a0, /* U+00A0 NO-BREAK SPACE */
|
XK_nobreakspace: 0x00a0, /* U+00A0 NO-BREAK SPACE */
|
||||||
XK_exclamdown: 0x00a1, /* U+00A1 INVERTED EXCLAMATION MARK */
|
XK_exclamdown: 0x00a1, /* U+00A1 INVERTED EXCLAMATION MARK */
|
||||||
XK_cent: 0x00a2, /* U+00A2 CENT SIGN */
|
XK_cent: 0x00a2, /* U+00A2 CENT SIGN */
|
||||||
XK_sterling: 0x00a3, /* U+00A3 POUND SIGN */
|
XK_sterling: 0x00a3, /* U+00A3 POUND SIGN */
|
||||||
XK_currency: 0x00a4, /* U+00A4 CURRENCY SIGN */
|
XK_currency: 0x00a4, /* U+00A4 CURRENCY SIGN */
|
||||||
XK_yen: 0x00a5, /* U+00A5 YEN SIGN */
|
XK_yen: 0x00a5, /* U+00A5 YEN SIGN */
|
||||||
XK_brokenbar: 0x00a6, /* U+00A6 BROKEN BAR */
|
XK_brokenbar: 0x00a6, /* U+00A6 BROKEN BAR */
|
||||||
XK_section: 0x00a7, /* U+00A7 SECTION SIGN */
|
XK_section: 0x00a7, /* U+00A7 SECTION SIGN */
|
||||||
XK_diaeresis: 0x00a8, /* U+00A8 DIAERESIS */
|
XK_diaeresis: 0x00a8, /* U+00A8 DIAERESIS */
|
||||||
XK_copyright: 0x00a9, /* U+00A9 COPYRIGHT SIGN */
|
XK_copyright: 0x00a9, /* U+00A9 COPYRIGHT SIGN */
|
||||||
XK_ordfeminine: 0x00aa, /* U+00AA FEMININE ORDINAL INDICATOR */
|
XK_ordfeminine: 0x00aa, /* U+00AA FEMININE ORDINAL INDICATOR */
|
||||||
XK_guillemotleft: 0x00ab, /* U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */
|
XK_guillemotleft: 0x00ab, /* U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */
|
||||||
XK_notsign: 0x00ac, /* U+00AC NOT SIGN */
|
XK_notsign: 0x00ac, /* U+00AC NOT SIGN */
|
||||||
XK_hyphen: 0x00ad, /* U+00AD SOFT HYPHEN */
|
XK_hyphen: 0x00ad, /* U+00AD SOFT HYPHEN */
|
||||||
XK_registered: 0x00ae, /* U+00AE REGISTERED SIGN */
|
XK_registered: 0x00ae, /* U+00AE REGISTERED SIGN */
|
||||||
XK_macron: 0x00af, /* U+00AF MACRON */
|
XK_macron: 0x00af, /* U+00AF MACRON */
|
||||||
XK_degree: 0x00b0, /* U+00B0 DEGREE SIGN */
|
XK_degree: 0x00b0, /* U+00B0 DEGREE SIGN */
|
||||||
XK_plusminus: 0x00b1, /* U+00B1 PLUS-MINUS SIGN */
|
XK_plusminus: 0x00b1, /* U+00B1 PLUS-MINUS SIGN */
|
||||||
XK_twosuperior: 0x00b2, /* U+00B2 SUPERSCRIPT TWO */
|
XK_twosuperior: 0x00b2, /* U+00B2 SUPERSCRIPT TWO */
|
||||||
XK_threesuperior: 0x00b3, /* U+00B3 SUPERSCRIPT THREE */
|
XK_threesuperior: 0x00b3, /* U+00B3 SUPERSCRIPT THREE */
|
||||||
XK_acute: 0x00b4, /* U+00B4 ACUTE ACCENT */
|
XK_acute: 0x00b4, /* U+00B4 ACUTE ACCENT */
|
||||||
XK_mu: 0x00b5, /* U+00B5 MICRO SIGN */
|
XK_mu: 0x00b5, /* U+00B5 MICRO SIGN */
|
||||||
XK_paragraph: 0x00b6, /* U+00B6 PILCROW SIGN */
|
XK_paragraph: 0x00b6, /* U+00B6 PILCROW SIGN */
|
||||||
XK_periodcentered: 0x00b7, /* U+00B7 MIDDLE DOT */
|
XK_periodcentered: 0x00b7, /* U+00B7 MIDDLE DOT */
|
||||||
XK_cedilla: 0x00b8, /* U+00B8 CEDILLA */
|
XK_cedilla: 0x00b8, /* U+00B8 CEDILLA */
|
||||||
XK_onesuperior: 0x00b9, /* U+00B9 SUPERSCRIPT ONE */
|
XK_onesuperior: 0x00b9, /* U+00B9 SUPERSCRIPT ONE */
|
||||||
XK_masculine: 0x00ba, /* U+00BA MASCULINE ORDINAL INDICATOR */
|
XK_masculine: 0x00ba, /* U+00BA MASCULINE ORDINAL INDICATOR */
|
||||||
XK_guillemotright: 0x00bb, /* U+00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */
|
XK_guillemotright: 0x00bb, /* U+00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */
|
||||||
XK_onequarter: 0x00bc, /* U+00BC VULGAR FRACTION ONE QUARTER */
|
XK_onequarter: 0x00bc, /* U+00BC VULGAR FRACTION ONE QUARTER */
|
||||||
XK_onehalf: 0x00bd, /* U+00BD VULGAR FRACTION ONE HALF */
|
XK_onehalf: 0x00bd, /* U+00BD VULGAR FRACTION ONE HALF */
|
||||||
XK_threequarters: 0x00be, /* U+00BE VULGAR FRACTION THREE QUARTERS */
|
XK_threequarters: 0x00be, /* U+00BE VULGAR FRACTION THREE QUARTERS */
|
||||||
XK_questiondown: 0x00bf, /* U+00BF INVERTED QUESTION MARK */
|
XK_questiondown: 0x00bf, /* U+00BF INVERTED QUESTION MARK */
|
||||||
XK_Agrave: 0x00c0, /* U+00C0 LATIN CAPITAL LETTER A WITH GRAVE */
|
XK_Agrave: 0x00c0, /* U+00C0 LATIN CAPITAL LETTER A WITH GRAVE */
|
||||||
XK_Aacute: 0x00c1, /* U+00C1 LATIN CAPITAL LETTER A WITH ACUTE */
|
XK_Aacute: 0x00c1, /* U+00C1 LATIN CAPITAL LETTER A WITH ACUTE */
|
||||||
XK_Acircumflex: 0x00c2, /* U+00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */
|
XK_Acircumflex: 0x00c2, /* U+00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */
|
||||||
XK_Atilde: 0x00c3, /* U+00C3 LATIN CAPITAL LETTER A WITH TILDE */
|
XK_Atilde: 0x00c3, /* U+00C3 LATIN CAPITAL LETTER A WITH TILDE */
|
||||||
XK_Adiaeresis: 0x00c4, /* U+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS */
|
XK_Adiaeresis: 0x00c4, /* U+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS */
|
||||||
XK_Aring: 0x00c5, /* U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE */
|
XK_Aring: 0x00c5, /* U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE */
|
||||||
XK_AE: 0x00c6, /* U+00C6 LATIN CAPITAL LETTER AE */
|
XK_AE: 0x00c6, /* U+00C6 LATIN CAPITAL LETTER AE */
|
||||||
XK_Ccedilla: 0x00c7, /* U+00C7 LATIN CAPITAL LETTER C WITH CEDILLA */
|
XK_Ccedilla: 0x00c7, /* U+00C7 LATIN CAPITAL LETTER C WITH CEDILLA */
|
||||||
XK_Egrave: 0x00c8, /* U+00C8 LATIN CAPITAL LETTER E WITH GRAVE */
|
XK_Egrave: 0x00c8, /* U+00C8 LATIN CAPITAL LETTER E WITH GRAVE */
|
||||||
XK_Eacute: 0x00c9, /* U+00C9 LATIN CAPITAL LETTER E WITH ACUTE */
|
XK_Eacute: 0x00c9, /* U+00C9 LATIN CAPITAL LETTER E WITH ACUTE */
|
||||||
XK_Ecircumflex: 0x00ca, /* U+00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX */
|
XK_Ecircumflex: 0x00ca, /* U+00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX */
|
||||||
XK_Ediaeresis: 0x00cb, /* U+00CB LATIN CAPITAL LETTER E WITH DIAERESIS */
|
XK_Ediaeresis: 0x00cb, /* U+00CB LATIN CAPITAL LETTER E WITH DIAERESIS */
|
||||||
XK_Igrave: 0x00cc, /* U+00CC LATIN CAPITAL LETTER I WITH GRAVE */
|
XK_Igrave: 0x00cc, /* U+00CC LATIN CAPITAL LETTER I WITH GRAVE */
|
||||||
XK_Iacute: 0x00cd, /* U+00CD LATIN CAPITAL LETTER I WITH ACUTE */
|
XK_Iacute: 0x00cd, /* U+00CD LATIN CAPITAL LETTER I WITH ACUTE */
|
||||||
XK_Icircumflex: 0x00ce, /* U+00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX */
|
XK_Icircumflex: 0x00ce, /* U+00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX */
|
||||||
XK_Idiaeresis: 0x00cf, /* U+00CF LATIN CAPITAL LETTER I WITH DIAERESIS */
|
XK_Idiaeresis: 0x00cf, /* U+00CF LATIN CAPITAL LETTER I WITH DIAERESIS */
|
||||||
XK_ETH: 0x00d0, /* U+00D0 LATIN CAPITAL LETTER ETH */
|
XK_ETH: 0x00d0, /* U+00D0 LATIN CAPITAL LETTER ETH */
|
||||||
XK_Eth: 0x00d0, /* deprecated */
|
XK_Eth: 0x00d0, /* deprecated */
|
||||||
XK_Ntilde: 0x00d1, /* U+00D1 LATIN CAPITAL LETTER N WITH TILDE */
|
XK_Ntilde: 0x00d1, /* U+00D1 LATIN CAPITAL LETTER N WITH TILDE */
|
||||||
XK_Ograve: 0x00d2, /* U+00D2 LATIN CAPITAL LETTER O WITH GRAVE */
|
XK_Ograve: 0x00d2, /* U+00D2 LATIN CAPITAL LETTER O WITH GRAVE */
|
||||||
XK_Oacute: 0x00d3, /* U+00D3 LATIN CAPITAL LETTER O WITH ACUTE */
|
XK_Oacute: 0x00d3, /* U+00D3 LATIN CAPITAL LETTER O WITH ACUTE */
|
||||||
XK_Ocircumflex: 0x00d4, /* U+00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */
|
XK_Ocircumflex: 0x00d4, /* U+00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */
|
||||||
XK_Otilde: 0x00d5, /* U+00D5 LATIN CAPITAL LETTER O WITH TILDE */
|
XK_Otilde: 0x00d5, /* U+00D5 LATIN CAPITAL LETTER O WITH TILDE */
|
||||||
XK_Odiaeresis: 0x00d6, /* U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS */
|
XK_Odiaeresis: 0x00d6, /* U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS */
|
||||||
XK_multiply: 0x00d7, /* U+00D7 MULTIPLICATION SIGN */
|
XK_multiply: 0x00d7, /* U+00D7 MULTIPLICATION SIGN */
|
||||||
XK_Oslash: 0x00d8, /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */
|
XK_Oslash: 0x00d8, /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */
|
||||||
XK_Ooblique: 0x00d8, /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */
|
XK_Ooblique: 0x00d8, /* U+00D8 LATIN CAPITAL LETTER O WITH STROKE */
|
||||||
XK_Ugrave: 0x00d9, /* U+00D9 LATIN CAPITAL LETTER U WITH GRAVE */
|
XK_Ugrave: 0x00d9, /* U+00D9 LATIN CAPITAL LETTER U WITH GRAVE */
|
||||||
XK_Uacute: 0x00da, /* U+00DA LATIN CAPITAL LETTER U WITH ACUTE */
|
XK_Uacute: 0x00da, /* U+00DA LATIN CAPITAL LETTER U WITH ACUTE */
|
||||||
XK_Ucircumflex: 0x00db, /* U+00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX */
|
XK_Ucircumflex: 0x00db, /* U+00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX */
|
||||||
XK_Udiaeresis: 0x00dc, /* U+00DC LATIN CAPITAL LETTER U WITH DIAERESIS */
|
XK_Udiaeresis: 0x00dc, /* U+00DC LATIN CAPITAL LETTER U WITH DIAERESIS */
|
||||||
XK_Yacute: 0x00dd, /* U+00DD LATIN CAPITAL LETTER Y WITH ACUTE */
|
XK_Yacute: 0x00dd, /* U+00DD LATIN CAPITAL LETTER Y WITH ACUTE */
|
||||||
XK_THORN: 0x00de, /* U+00DE LATIN CAPITAL LETTER THORN */
|
XK_THORN: 0x00de, /* U+00DE LATIN CAPITAL LETTER THORN */
|
||||||
XK_Thorn: 0x00de, /* deprecated */
|
XK_Thorn: 0x00de, /* deprecated */
|
||||||
XK_ssharp: 0x00df, /* U+00DF LATIN SMALL LETTER SHARP S */
|
XK_ssharp: 0x00df, /* U+00DF LATIN SMALL LETTER SHARP S */
|
||||||
XK_agrave: 0x00e0, /* U+00E0 LATIN SMALL LETTER A WITH GRAVE */
|
XK_agrave: 0x00e0, /* U+00E0 LATIN SMALL LETTER A WITH GRAVE */
|
||||||
XK_aacute: 0x00e1, /* U+00E1 LATIN SMALL LETTER A WITH ACUTE */
|
XK_aacute: 0x00e1, /* U+00E1 LATIN SMALL LETTER A WITH ACUTE */
|
||||||
XK_acircumflex: 0x00e2, /* U+00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX */
|
XK_acircumflex: 0x00e2, /* U+00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX */
|
||||||
XK_atilde: 0x00e3, /* U+00E3 LATIN SMALL LETTER A WITH TILDE */
|
XK_atilde: 0x00e3, /* U+00E3 LATIN SMALL LETTER A WITH TILDE */
|
||||||
XK_adiaeresis: 0x00e4, /* U+00E4 LATIN SMALL LETTER A WITH DIAERESIS */
|
XK_adiaeresis: 0x00e4, /* U+00E4 LATIN SMALL LETTER A WITH DIAERESIS */
|
||||||
XK_aring: 0x00e5, /* U+00E5 LATIN SMALL LETTER A WITH RING ABOVE */
|
XK_aring: 0x00e5, /* U+00E5 LATIN SMALL LETTER A WITH RING ABOVE */
|
||||||
XK_ae: 0x00e6, /* U+00E6 LATIN SMALL LETTER AE */
|
XK_ae: 0x00e6, /* U+00E6 LATIN SMALL LETTER AE */
|
||||||
XK_ccedilla: 0x00e7, /* U+00E7 LATIN SMALL LETTER C WITH CEDILLA */
|
XK_ccedilla: 0x00e7, /* U+00E7 LATIN SMALL LETTER C WITH CEDILLA */
|
||||||
XK_egrave: 0x00e8, /* U+00E8 LATIN SMALL LETTER E WITH GRAVE */
|
XK_egrave: 0x00e8, /* U+00E8 LATIN SMALL LETTER E WITH GRAVE */
|
||||||
XK_eacute: 0x00e9, /* U+00E9 LATIN SMALL LETTER E WITH ACUTE */
|
XK_eacute: 0x00e9, /* U+00E9 LATIN SMALL LETTER E WITH ACUTE */
|
||||||
XK_ecircumflex: 0x00ea, /* U+00EA LATIN SMALL LETTER E WITH CIRCUMFLEX */
|
XK_ecircumflex: 0x00ea, /* U+00EA LATIN SMALL LETTER E WITH CIRCUMFLEX */
|
||||||
XK_ediaeresis: 0x00eb, /* U+00EB LATIN SMALL LETTER E WITH DIAERESIS */
|
XK_ediaeresis: 0x00eb, /* U+00EB LATIN SMALL LETTER E WITH DIAERESIS */
|
||||||
XK_igrave: 0x00ec, /* U+00EC LATIN SMALL LETTER I WITH GRAVE */
|
XK_igrave: 0x00ec, /* U+00EC LATIN SMALL LETTER I WITH GRAVE */
|
||||||
XK_iacute: 0x00ed, /* U+00ED LATIN SMALL LETTER I WITH ACUTE */
|
XK_iacute: 0x00ed, /* U+00ED LATIN SMALL LETTER I WITH ACUTE */
|
||||||
XK_icircumflex: 0x00ee, /* U+00EE LATIN SMALL LETTER I WITH CIRCUMFLEX */
|
XK_icircumflex: 0x00ee, /* U+00EE LATIN SMALL LETTER I WITH CIRCUMFLEX */
|
||||||
XK_idiaeresis: 0x00ef, /* U+00EF LATIN SMALL LETTER I WITH DIAERESIS */
|
XK_idiaeresis: 0x00ef, /* U+00EF LATIN SMALL LETTER I WITH DIAERESIS */
|
||||||
XK_eth: 0x00f0, /* U+00F0 LATIN SMALL LETTER ETH */
|
XK_eth: 0x00f0, /* U+00F0 LATIN SMALL LETTER ETH */
|
||||||
XK_ntilde: 0x00f1, /* U+00F1 LATIN SMALL LETTER N WITH TILDE */
|
XK_ntilde: 0x00f1, /* U+00F1 LATIN SMALL LETTER N WITH TILDE */
|
||||||
XK_ograve: 0x00f2, /* U+00F2 LATIN SMALL LETTER O WITH GRAVE */
|
XK_ograve: 0x00f2, /* U+00F2 LATIN SMALL LETTER O WITH GRAVE */
|
||||||
XK_oacute: 0x00f3, /* U+00F3 LATIN SMALL LETTER O WITH ACUTE */
|
XK_oacute: 0x00f3, /* U+00F3 LATIN SMALL LETTER O WITH ACUTE */
|
||||||
XK_ocircumflex: 0x00f4, /* U+00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX */
|
XK_ocircumflex: 0x00f4, /* U+00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX */
|
||||||
XK_otilde: 0x00f5, /* U+00F5 LATIN SMALL LETTER O WITH TILDE */
|
XK_otilde: 0x00f5, /* U+00F5 LATIN SMALL LETTER O WITH TILDE */
|
||||||
XK_odiaeresis: 0x00f6, /* U+00F6 LATIN SMALL LETTER O WITH DIAERESIS */
|
XK_odiaeresis: 0x00f6, /* U+00F6 LATIN SMALL LETTER O WITH DIAERESIS */
|
||||||
XK_division: 0x00f7, /* U+00F7 DIVISION SIGN */
|
XK_division: 0x00f7, /* U+00F7 DIVISION SIGN */
|
||||||
XK_oslash: 0x00f8, /* U+00F8 LATIN SMALL LETTER O WITH STROKE */
|
XK_oslash: 0x00f8, /* U+00F8 LATIN SMALL LETTER O WITH STROKE */
|
||||||
XK_ooblique: 0x00f8, /* U+00F8 LATIN SMALL LETTER O WITH STROKE */
|
XK_ooblique: 0x00f8, /* U+00F8 LATIN SMALL LETTER O WITH STROKE */
|
||||||
XK_ugrave: 0x00f9, /* U+00F9 LATIN SMALL LETTER U WITH GRAVE */
|
XK_ugrave: 0x00f9, /* U+00F9 LATIN SMALL LETTER U WITH GRAVE */
|
||||||
XK_uacute: 0x00fa, /* U+00FA LATIN SMALL LETTER U WITH ACUTE */
|
XK_uacute: 0x00fa, /* U+00FA LATIN SMALL LETTER U WITH ACUTE */
|
||||||
XK_ucircumflex: 0x00fb, /* U+00FB LATIN SMALL LETTER U WITH CIRCUMFLEX */
|
XK_ucircumflex: 0x00fb, /* U+00FB LATIN SMALL LETTER U WITH CIRCUMFLEX */
|
||||||
XK_udiaeresis: 0x00fc, /* U+00FC LATIN SMALL LETTER U WITH DIAERESIS */
|
XK_udiaeresis: 0x00fc, /* U+00FC LATIN SMALL LETTER U WITH DIAERESIS */
|
||||||
XK_yacute: 0x00fd, /* U+00FD LATIN SMALL LETTER Y WITH ACUTE */
|
XK_yacute: 0x00fd, /* U+00FD LATIN SMALL LETTER Y WITH ACUTE */
|
||||||
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 */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* [module] export default KeyTable; */
|
return KeyTable;
|
||||||
|
});
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
|
|
@ -1,13 +1,17 @@
|
||||||
/* [module]
|
/*
|
||||||
* import KeyTable from "./keysym";
|
* noVNC: HTML5 VNC client
|
||||||
* import keysyms from "./keysymdef";
|
* Copyright (C) 2012 Joel Martin
|
||||||
|
* Copyright (C) 2017 Pierre Ossman for Cendio AB
|
||||||
|
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*jslint browser: true, white: false */
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var KeyboardUtil = {};
|
define(["core/input/keysym", "core/input/keysymdef"],
|
||||||
|
function (KeyTable, keysyms) {
|
||||||
(function() {
|
var KeyboardUtil = {};
|
||||||
|
|
||||||
function substituteCodepoint(cp) {
|
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
|
||||||
|
|
@ -287,387 +291,387 @@ var KeyboardUtil = {};
|
||||||
KeyboardUtil.keysymFromKeyCode = keysymFromKeyCode;
|
KeyboardUtil.keysymFromKeyCode = keysymFromKeyCode;
|
||||||
KeyboardUtil.nonCharacterKey = nonCharacterKey;
|
KeyboardUtil.nonCharacterKey = nonCharacterKey;
|
||||||
KeyboardUtil.substituteCodepoint = substituteCodepoint;
|
KeyboardUtil.substituteCodepoint = substituteCodepoint;
|
||||||
})();
|
|
||||||
|
|
||||||
KeyboardUtil.QEMUKeyEventDecoder = function(modifierState, next) {
|
KeyboardUtil.QEMUKeyEventDecoder = function(modifierState, next) {
|
||||||
function sendAll(evts) {
|
function sendAll(evts) {
|
||||||
for (var i = 0; i < evts.length; ++i) {
|
for (var i = 0; i < evts.length; ++i) {
|
||||||
next(evts[i]);
|
next(evts[i]);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var numPadCodes = ["Numpad0", "Numpad1", "Numpad2",
|
|
||||||
"Numpad3", "Numpad4", "Numpad5", "Numpad6",
|
|
||||||
"Numpad7", "Numpad8", "Numpad9", "NumpadDecimal"];
|
|
||||||
|
|
||||||
var numLockOnKeySyms = {
|
|
||||||
"Numpad0": 0xffb0, "Numpad1": 0xffb1, "Numpad2": 0xffb2,
|
|
||||||
"Numpad3": 0xffb3, "Numpad4": 0xffb4, "Numpad5": 0xffb5,
|
|
||||||
"Numpad6": 0xffb6, "Numpad7": 0xffb7, "Numpad8": 0xffb8,
|
|
||||||
"Numpad9": 0xffb9, "NumpadDecimal": 0xffac
|
|
||||||
};
|
|
||||||
|
|
||||||
var numLockOnKeyCodes = [96, 97, 98, 99, 100, 101, 102,
|
|
||||||
103, 104, 105, 108, 110];
|
|
||||||
|
|
||||||
function isNumPadMultiKey(evt) {
|
|
||||||
return (numPadCodes.indexOf(evt.code) !== -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getNumPadKeySym(evt) {
|
|
||||||
if (numLockOnKeyCodes.indexOf(evt.keyCode) !== -1) {
|
|
||||||
return numLockOnKeySyms[evt.code];
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function process(evt, type) {
|
|
||||||
var result = {type: type};
|
|
||||||
result.code = evt.code;
|
|
||||||
result.keysym = 0;
|
|
||||||
|
|
||||||
if (isNumPadMultiKey(evt)) {
|
|
||||||
result.keysym = getNumPadKeySym(evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
|
|
||||||
var isShift = evt.keyCode === 0x10 || evt.key === 'Shift';
|
|
||||||
|
|
||||||
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!KeyboardUtil.nonCharacterKey(evt));
|
|
||||||
|
|
||||||
next(result);
|
|
||||||
return suppress;
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
keydown: function(evt) {
|
|
||||||
sendAll(modifierState.keydown(evt));
|
|
||||||
return process(evt, 'keydown');
|
|
||||||
},
|
|
||||||
keypress: function(evt) {
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
keyup: function(evt) {
|
|
||||||
sendAll(modifierState.keyup(evt));
|
|
||||||
return process(evt, 'keyup');
|
|
||||||
},
|
|
||||||
syncModifiers: function(evt) {
|
|
||||||
sendAll(modifierState.syncAny(evt));
|
|
||||||
},
|
|
||||||
releaseAll: function() { next({type: 'releaseall'}); }
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
KeyboardUtil.TrackQEMUKeyState = function(next) {
|
|
||||||
var state = [];
|
|
||||||
|
|
||||||
return function (evt) {
|
|
||||||
var last = state.length !== 0 ? state[state.length-1] : null;
|
|
||||||
|
|
||||||
switch (evt.type) {
|
|
||||||
case 'keydown':
|
|
||||||
|
|
||||||
if (!last || last.code !== evt.code) {
|
|
||||||
last = {code: evt.code};
|
|
||||||
|
|
||||||
if (state.length > 0 && state[state.length-1].code == 'ControlLeft') {
|
|
||||||
if (evt.code !== 'AltRight') {
|
|
||||||
next({code: 'ControlLeft', type: 'keydown', keysym: 0});
|
|
||||||
} else {
|
|
||||||
state.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
state.push(last);
|
|
||||||
}
|
|
||||||
if (evt.code !== 'ControlLeft') {
|
|
||||||
next(evt);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'keyup':
|
|
||||||
if (state.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var idx = null;
|
|
||||||
// do we have a matching key tracked as being down?
|
|
||||||
for (var i = 0; i !== state.length; ++i) {
|
|
||||||
if (state[i].code === evt.code) {
|
|
||||||
idx = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if we couldn't find a match (it happens), assume it was the last key pressed
|
|
||||||
if (idx === null) {
|
|
||||||
if (evt.code === 'ControlLeft') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
idx = state.length - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
state.splice(idx, 1);
|
|
||||||
next(evt);
|
|
||||||
break;
|
|
||||||
case 'releaseall':
|
|
||||||
/* jshint shadow: true */
|
|
||||||
for (var i = 0; i < state.length; ++i) {
|
|
||||||
next({code: state[i].code, keysym: 0, type: 'keyup'});
|
|
||||||
}
|
|
||||||
/* jshint shadow: false */
|
|
||||||
state = [];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Takes a DOM keyboard event and:
|
|
||||||
// - determines which keysym it represents
|
|
||||||
// - determines a keyId identifying the key that was pressed (corresponding to the key/keyCode properties on the DOM event)
|
|
||||||
// - synthesizes events to synchronize modifier key state between which modifiers are actually down, and which we thought were down
|
|
||||||
// - marks each event with an 'escape' property if a modifier was down which should be "escaped"
|
|
||||||
// - generates a "stall" event in cases where it might be necessary to wait and see if a keypress event follows a keydown
|
|
||||||
// This information is collected into an object which is passed to the next() function. (one call per event)
|
|
||||||
KeyboardUtil.KeyEventDecoder = function(modifierState, next) {
|
|
||||||
function sendAll(evts) {
|
|
||||||
for (var i = 0; i < evts.length; ++i) {
|
|
||||||
next(evts[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function process(evt, type) {
|
|
||||||
var result = {type: type};
|
|
||||||
var keyId = KeyboardUtil.getKey(evt);
|
|
||||||
if (keyId) {
|
|
||||||
result.keyId = keyId;
|
|
||||||
}
|
|
||||||
|
|
||||||
var keysym = KeyboardUtil.getKeysym(evt);
|
|
||||||
|
|
||||||
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
|
|
||||||
// Is this a case where we have to decide on the keysym right away, rather than waiting for the keypress?
|
|
||||||
// "special" keys like enter, tab or backspace don't send keypress events,
|
|
||||||
// and some browsers don't send keypresses at all if a modifier is down
|
|
||||||
if (keysym && (type !== 'keydown' || KeyboardUtil.nonCharacterKey(evt) || hasModifier)) {
|
|
||||||
result.keysym = keysym;
|
|
||||||
}
|
|
||||||
|
|
||||||
var isShift = evt.keyCode === 0x10 || evt.key === 'Shift';
|
|
||||||
|
|
||||||
// Should we prevent the browser from handling the event?
|
|
||||||
// Doing so on a keydown (in most browsers) prevents keypress from being generated
|
|
||||||
// so only do that if we have to.
|
|
||||||
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!KeyboardUtil.nonCharacterKey(evt));
|
|
||||||
|
|
||||||
// If a char modifier is down on a keydown, we need to insert a stall,
|
|
||||||
// so VerifyCharModifier knows to wait and see if a keypress is comnig
|
|
||||||
var stall = type === 'keydown' && modifierState.activeCharModifier() && !KeyboardUtil.nonCharacterKey(evt);
|
|
||||||
|
|
||||||
// if a char modifier is pressed, get the keys it consists of (on Windows, AltGr is equivalent to Ctrl+Alt)
|
|
||||||
var active = modifierState.activeCharModifier();
|
|
||||||
|
|
||||||
// If we have a char modifier down, and we're able to determine a keysym reliably
|
|
||||||
// then (a) we know to treat the modifier as a char modifier,
|
|
||||||
// and (b) we'll have to "escape" the modifier to undo the modifier when sending the char.
|
|
||||||
if (active && keysym) {
|
|
||||||
var isCharModifier = false;
|
|
||||||
for (var i = 0; i < active.length; ++i) {
|
|
||||||
if (active[i] === keysym.keysym) {
|
|
||||||
isCharModifier = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (type === 'keypress' && !isCharModifier) {
|
|
||||||
result.escape = modifierState.activeCharModifier();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stall) {
|
var numPadCodes = ["Numpad0", "Numpad1", "Numpad2",
|
||||||
// insert a fake "stall" event
|
"Numpad3", "Numpad4", "Numpad5", "Numpad6",
|
||||||
next({type: 'stall'});
|
"Numpad7", "Numpad8", "Numpad9", "NumpadDecimal"];
|
||||||
}
|
|
||||||
next(result);
|
|
||||||
|
|
||||||
return suppress;
|
var numLockOnKeySyms = {
|
||||||
}
|
"Numpad0": 0xffb0, "Numpad1": 0xffb1, "Numpad2": 0xffb2,
|
||||||
|
"Numpad3": 0xffb3, "Numpad4": 0xffb4, "Numpad5": 0xffb5,
|
||||||
return {
|
"Numpad6": 0xffb6, "Numpad7": 0xffb7, "Numpad8": 0xffb8,
|
||||||
keydown: function(evt) {
|
"Numpad9": 0xffb9, "NumpadDecimal": 0xffac
|
||||||
sendAll(modifierState.keydown(evt));
|
|
||||||
return process(evt, 'keydown');
|
|
||||||
},
|
|
||||||
keypress: function(evt) {
|
|
||||||
return process(evt, 'keypress');
|
|
||||||
},
|
|
||||||
keyup: function(evt) {
|
|
||||||
sendAll(modifierState.keyup(evt));
|
|
||||||
return process(evt, 'keyup');
|
|
||||||
},
|
|
||||||
syncModifiers: function(evt) {
|
|
||||||
sendAll(modifierState.syncAny(evt));
|
|
||||||
},
|
|
||||||
releaseAll: function() { next({type: 'releaseall'}); }
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
// Combines keydown and keypress events where necessary to handle char modifiers.
|
|
||||||
// On some OS'es, a char modifier is sometimes used as a shortcut modifier.
|
|
||||||
// For example, on Windows, AltGr is synonymous with Ctrl-Alt. On a Danish keyboard layout, AltGr-2 yields a @, but Ctrl-Alt-D does nothing
|
|
||||||
// so when used with the '2' key, Ctrl-Alt counts as a char modifier (and should be escaped), but when used with 'D', it does not.
|
|
||||||
// The only way we can distinguish these cases is to wait and see if a keypress event arrives
|
|
||||||
// When we receive a "stall" event, wait a few ms before processing the next keydown. If a keypress has also arrived, merge the two
|
|
||||||
KeyboardUtil.VerifyCharModifier = function(next) {
|
|
||||||
var queue = [];
|
|
||||||
var timer = null;
|
|
||||||
function process() {
|
|
||||||
if (timer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var delayProcess = function () {
|
|
||||||
clearTimeout(timer);
|
|
||||||
timer = null;
|
|
||||||
process();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
while (queue.length !== 0) {
|
var numLockOnKeyCodes = [96, 97, 98, 99, 100, 101, 102,
|
||||||
var cur = queue[0];
|
103, 104, 105, 108, 110];
|
||||||
queue = queue.splice(1);
|
|
||||||
switch (cur.type) {
|
function isNumPadMultiKey(evt) {
|
||||||
case 'stall':
|
return (numPadCodes.indexOf(evt.code) !== -1);
|
||||||
// insert a delay before processing available events.
|
}
|
||||||
/* jshint loopfunc: true */
|
|
||||||
timer = setTimeout(delayProcess, 5);
|
function getNumPadKeySym(evt) {
|
||||||
/* jshint loopfunc: false */
|
if (numLockOnKeyCodes.indexOf(evt.keyCode) !== -1) {
|
||||||
return;
|
return numLockOnKeySyms[evt.code];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function process(evt, type) {
|
||||||
|
var result = {type: type};
|
||||||
|
result.code = evt.code;
|
||||||
|
result.keysym = 0;
|
||||||
|
|
||||||
|
if (isNumPadMultiKey(evt)) {
|
||||||
|
result.keysym = getNumPadKeySym(evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
|
||||||
|
var isShift = evt.keyCode === 0x10 || evt.key === 'Shift';
|
||||||
|
|
||||||
|
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!KeyboardUtil.nonCharacterKey(evt));
|
||||||
|
|
||||||
|
next(result);
|
||||||
|
return suppress;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
keydown: function(evt) {
|
||||||
|
sendAll(modifierState.keydown(evt));
|
||||||
|
return process(evt, 'keydown');
|
||||||
|
},
|
||||||
|
keypress: function(evt) {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
keyup: function(evt) {
|
||||||
|
sendAll(modifierState.keyup(evt));
|
||||||
|
return process(evt, 'keyup');
|
||||||
|
},
|
||||||
|
syncModifiers: function(evt) {
|
||||||
|
sendAll(modifierState.syncAny(evt));
|
||||||
|
},
|
||||||
|
releaseAll: function() { next({type: 'releaseall'}); }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
KeyboardUtil.TrackQEMUKeyState = function(next) {
|
||||||
|
var state = [];
|
||||||
|
|
||||||
|
return function (evt) {
|
||||||
|
var last = state.length !== 0 ? state[state.length-1] : null;
|
||||||
|
|
||||||
|
switch (evt.type) {
|
||||||
case 'keydown':
|
case 'keydown':
|
||||||
// is the next element a keypress? Then we should merge the two
|
|
||||||
if (queue.length !== 0 && queue[0].type === 'keypress') {
|
if (!last || last.code !== evt.code) {
|
||||||
// Firefox sends keypress even when no char is generated.
|
last = {code: evt.code};
|
||||||
// so, if keypress keysym is the same as we'd have guessed from keydown,
|
|
||||||
// the modifier didn't have any effect, and should not be escaped
|
if (state.length > 0 && state[state.length-1].code == 'ControlLeft') {
|
||||||
if (queue[0].escape && (!cur.keysym || cur.keysym.keysym !== queue[0].keysym.keysym)) {
|
if (evt.code !== 'AltRight') {
|
||||||
cur.escape = queue[0].escape;
|
next({code: 'ControlLeft', type: 'keydown', keysym: 0});
|
||||||
|
} else {
|
||||||
|
state.pop();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
cur.keysym = queue[0].keysym;
|
state.push(last);
|
||||||
queue = queue.splice(1);
|
}
|
||||||
|
if (evt.code !== 'ControlLeft') {
|
||||||
|
next(evt);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
// swallow stall events, and pass all others to the next stage
|
case 'keyup':
|
||||||
if (cur.type !== 'stall') {
|
if (state.length === 0) {
|
||||||
next(cur);
|
return;
|
||||||
|
}
|
||||||
|
var idx = null;
|
||||||
|
// do we have a matching key tracked as being down?
|
||||||
|
for (var i = 0; i !== state.length; ++i) {
|
||||||
|
if (state[i].code === evt.code) {
|
||||||
|
idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if we couldn't find a match (it happens), assume it was the last key pressed
|
||||||
|
if (idx === null) {
|
||||||
|
if (evt.code === 'ControlLeft') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
idx = state.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.splice(idx, 1);
|
||||||
|
next(evt);
|
||||||
|
break;
|
||||||
|
case 'releaseall':
|
||||||
|
/* jshint shadow: true */
|
||||||
|
for (var i = 0; i < state.length; ++i) {
|
||||||
|
next({code: state[i].code, keysym: 0, type: 'keyup'});
|
||||||
|
}
|
||||||
|
/* jshint shadow: false */
|
||||||
|
state = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Takes a DOM keyboard event and:
|
||||||
|
// - determines which keysym it represents
|
||||||
|
// - determines a keyId identifying the key that was pressed (corresponding to the key/keyCode properties on the DOM event)
|
||||||
|
// - synthesizes events to synchronize modifier key state between which modifiers are actually down, and which we thought were down
|
||||||
|
// - marks each event with an 'escape' property if a modifier was down which should be "escaped"
|
||||||
|
// - generates a "stall" event in cases where it might be necessary to wait and see if a keypress event follows a keydown
|
||||||
|
// This information is collected into an object which is passed to the next() function. (one call per event)
|
||||||
|
KeyboardUtil.KeyEventDecoder = function(modifierState, next) {
|
||||||
|
function sendAll(evts) {
|
||||||
|
for (var i = 0; i < evts.length; ++i) {
|
||||||
|
next(evts[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
function process(evt, type) {
|
||||||
return function(evt) {
|
var result = {type: type};
|
||||||
queue.push(evt);
|
var keyId = KeyboardUtil.getKey(evt);
|
||||||
process();
|
if (keyId) {
|
||||||
|
result.keyId = keyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
var keysym = KeyboardUtil.getKeysym(evt);
|
||||||
|
|
||||||
|
var hasModifier = modifierState.hasShortcutModifier() || !!modifierState.activeCharModifier();
|
||||||
|
// Is this a case where we have to decide on the keysym right away, rather than waiting for the keypress?
|
||||||
|
// "special" keys like enter, tab or backspace don't send keypress events,
|
||||||
|
// and some browsers don't send keypresses at all if a modifier is down
|
||||||
|
if (keysym && (type !== 'keydown' || KeyboardUtil.nonCharacterKey(evt) || hasModifier)) {
|
||||||
|
result.keysym = keysym;
|
||||||
|
}
|
||||||
|
|
||||||
|
var isShift = evt.keyCode === 0x10 || evt.key === 'Shift';
|
||||||
|
|
||||||
|
// Should we prevent the browser from handling the event?
|
||||||
|
// Doing so on a keydown (in most browsers) prevents keypress from being generated
|
||||||
|
// so only do that if we have to.
|
||||||
|
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!KeyboardUtil.nonCharacterKey(evt));
|
||||||
|
|
||||||
|
// If a char modifier is down on a keydown, we need to insert a stall,
|
||||||
|
// so VerifyCharModifier knows to wait and see if a keypress is comnig
|
||||||
|
var stall = type === 'keydown' && modifierState.activeCharModifier() && !KeyboardUtil.nonCharacterKey(evt);
|
||||||
|
|
||||||
|
// if a char modifier is pressed, get the keys it consists of (on Windows, AltGr is equivalent to Ctrl+Alt)
|
||||||
|
var active = modifierState.activeCharModifier();
|
||||||
|
|
||||||
|
// If we have a char modifier down, and we're able to determine a keysym reliably
|
||||||
|
// then (a) we know to treat the modifier as a char modifier,
|
||||||
|
// and (b) we'll have to "escape" the modifier to undo the modifier when sending the char.
|
||||||
|
if (active && keysym) {
|
||||||
|
var isCharModifier = false;
|
||||||
|
for (var i = 0; i < active.length; ++i) {
|
||||||
|
if (active[i] === keysym.keysym) {
|
||||||
|
isCharModifier = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (type === 'keypress' && !isCharModifier) {
|
||||||
|
result.escape = modifierState.activeCharModifier();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stall) {
|
||||||
|
// insert a fake "stall" event
|
||||||
|
next({type: 'stall'});
|
||||||
|
}
|
||||||
|
next(result);
|
||||||
|
|
||||||
|
return suppress;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
keydown: function(evt) {
|
||||||
|
sendAll(modifierState.keydown(evt));
|
||||||
|
return process(evt, 'keydown');
|
||||||
|
},
|
||||||
|
keypress: function(evt) {
|
||||||
|
return process(evt, 'keypress');
|
||||||
|
},
|
||||||
|
keyup: function(evt) {
|
||||||
|
sendAll(modifierState.keyup(evt));
|
||||||
|
return process(evt, 'keyup');
|
||||||
|
},
|
||||||
|
syncModifiers: function(evt) {
|
||||||
|
sendAll(modifierState.syncAny(evt));
|
||||||
|
},
|
||||||
|
releaseAll: function() { next({type: 'releaseall'}); }
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
// Keeps track of which keys we (and the server) believe are down
|
// Combines keydown and keypress events where necessary to handle char modifiers.
|
||||||
// When a keyup is received, match it against this list, to determine the corresponding keysym(s)
|
// On some OS'es, a char modifier is sometimes used as a shortcut modifier.
|
||||||
// in some cases, a single key may produce multiple keysyms, so the corresponding keyup event must release all of these chars
|
// For example, on Windows, AltGr is synonymous with Ctrl-Alt. On a Danish keyboard layout, AltGr-2 yields a @, but Ctrl-Alt-D does nothing
|
||||||
// key repeat events should be merged into a single entry.
|
// 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.
|
||||||
// Because we can't always identify which entry a keydown or keyup event corresponds to, we sometimes have to guess
|
// The only way we can distinguish these cases is to wait and see if a keypress event arrives
|
||||||
KeyboardUtil.TrackKeyState = function(next) {
|
// When we receive a "stall" event, wait a few ms before processing the next keydown. If a keypress has also arrived, merge the two
|
||||||
var state = [];
|
KeyboardUtil.VerifyCharModifier = function(next) {
|
||||||
|
var queue = [];
|
||||||
return function (evt) {
|
var timer = null;
|
||||||
var last = state.length !== 0 ? state[state.length-1] : null;
|
function process() {
|
||||||
|
if (timer) {
|
||||||
switch (evt.type) {
|
|
||||||
case 'keydown':
|
|
||||||
// insert a new entry if last seen key was different.
|
|
||||||
if (!last || !evt.keyId || last.keyId !== evt.keyId) {
|
|
||||||
last = {keyId: evt.keyId, keysyms: {}};
|
|
||||||
state.push(last);
|
|
||||||
}
|
|
||||||
if (evt.keysym) {
|
|
||||||
// make sure last event contains this keysym (a single "logical" keyevent
|
|
||||||
// can cause multiple key events to be sent to the VNC server)
|
|
||||||
last.keysyms[evt.keysym.keysym] = evt.keysym;
|
|
||||||
last.ignoreKeyPress = true;
|
|
||||||
next(evt);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'keypress':
|
|
||||||
if (!last) {
|
|
||||||
last = {keyId: evt.keyId, keysyms: {}};
|
|
||||||
state.push(last);
|
|
||||||
}
|
|
||||||
if (!evt.keysym) {
|
|
||||||
console.log('keypress with no keysym:', evt);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we didn't expect a keypress, and already sent a keydown to the VNC server
|
|
||||||
// based on the keydown, make sure to skip this event.
|
|
||||||
if (evt.keysym && !last.ignoreKeyPress) {
|
|
||||||
last.keysyms[evt.keysym.keysym] = evt.keysym;
|
|
||||||
evt.type = 'keydown';
|
|
||||||
next(evt);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'keyup':
|
|
||||||
if (state.length === 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var idx = null;
|
|
||||||
// do we have a matching key tracked as being down?
|
var delayProcess = function () {
|
||||||
for (var i = 0; i !== state.length; ++i) {
|
clearTimeout(timer);
|
||||||
if (state[i].keyId === evt.keyId) {
|
timer = null;
|
||||||
idx = i;
|
process();
|
||||||
|
};
|
||||||
|
|
||||||
|
while (queue.length !== 0) {
|
||||||
|
var cur = queue[0];
|
||||||
|
queue = queue.splice(1);
|
||||||
|
switch (cur.type) {
|
||||||
|
case 'stall':
|
||||||
|
// insert a delay before processing available events.
|
||||||
|
/* jshint loopfunc: true */
|
||||||
|
timer = setTimeout(delayProcess, 5);
|
||||||
|
/* jshint loopfunc: false */
|
||||||
|
return;
|
||||||
|
case 'keydown':
|
||||||
|
// is the next element a keypress? Then we should merge the two
|
||||||
|
if (queue.length !== 0 && queue[0].type === 'keypress') {
|
||||||
|
// Firefox sends keypress even when no char is generated.
|
||||||
|
// so, if keypress keysym is the same as we'd have guessed from keydown,
|
||||||
|
// the modifier didn't have any effect, and should not be escaped
|
||||||
|
if (queue[0].escape && (!cur.keysym || cur.keysym.keysym !== queue[0].keysym.keysym)) {
|
||||||
|
cur.escape = queue[0].escape;
|
||||||
|
}
|
||||||
|
cur.keysym = queue[0].keysym;
|
||||||
|
queue = queue.splice(1);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
// if we couldn't find a match (it happens), assume it was the last key pressed
|
|
||||||
if (idx === null) {
|
|
||||||
idx = state.length - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
var item = state.splice(idx, 1)[0];
|
// swallow stall events, and pass all others to the next stage
|
||||||
// for each keysym tracked by this key entry, clone the current event and override the keysym
|
if (cur.type !== 'stall') {
|
||||||
var clone = (function(){
|
next(cur);
|
||||||
function Clone(){}
|
|
||||||
return function (obj) { Clone.prototype=obj; return new Clone(); };
|
|
||||||
}());
|
|
||||||
for (var key in item.keysyms) {
|
|
||||||
var out = clone(evt);
|
|
||||||
out.keysym = item.keysyms[key];
|
|
||||||
next(out);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'releaseall':
|
|
||||||
/* jshint shadow: true */
|
|
||||||
for (var i = 0; i < state.length; ++i) {
|
|
||||||
for (var key in state[i].keysyms) {
|
|
||||||
var keysym = state[i].keysyms[key];
|
|
||||||
next({keyId: 0, keysym: keysym, type: 'keyup'});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* jshint shadow: false */
|
|
||||||
state = [];
|
|
||||||
}
|
}
|
||||||
|
return function(evt) {
|
||||||
|
queue.push(evt);
|
||||||
|
process();
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
// Handles "escaping" of modifiers: if a char modifier is used to produce a keysym (such as AltGr-2 to generate an @),
|
// Keeps track of which keys we (and the server) believe are down
|
||||||
// then the modifier must be "undone" before sending the @, and "redone" afterwards.
|
// When a keyup is received, match it against this list, to determine the corresponding keysym(s)
|
||||||
KeyboardUtil.EscapeModifiers = function(next) {
|
// in some cases, a single key may produce multiple keysyms, so the corresponding keyup event must release all of these chars
|
||||||
return function(evt) {
|
// key repeat events should be merged into a single entry.
|
||||||
if (evt.type !== 'keydown' || evt.escape === undefined) {
|
// Because we can't always identify which entry a keydown or keyup event corresponds to, we sometimes have to guess
|
||||||
|
KeyboardUtil.TrackKeyState = function(next) {
|
||||||
|
var state = [];
|
||||||
|
|
||||||
|
return function (evt) {
|
||||||
|
var last = state.length !== 0 ? state[state.length-1] : null;
|
||||||
|
|
||||||
|
switch (evt.type) {
|
||||||
|
case 'keydown':
|
||||||
|
// insert a new entry if last seen key was different.
|
||||||
|
if (!last || !evt.keyId || last.keyId !== evt.keyId) {
|
||||||
|
last = {keyId: evt.keyId, keysyms: {}};
|
||||||
|
state.push(last);
|
||||||
|
}
|
||||||
|
if (evt.keysym) {
|
||||||
|
// make sure last event contains this keysym (a single "logical" keyevent
|
||||||
|
// can cause multiple key events to be sent to the VNC server)
|
||||||
|
last.keysyms[evt.keysym.keysym] = evt.keysym;
|
||||||
|
last.ignoreKeyPress = true;
|
||||||
|
next(evt);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'keypress':
|
||||||
|
if (!last) {
|
||||||
|
last = {keyId: evt.keyId, keysyms: {}};
|
||||||
|
state.push(last);
|
||||||
|
}
|
||||||
|
if (!evt.keysym) {
|
||||||
|
console.log('keypress with no keysym:', evt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we didn't expect a keypress, and already sent a keydown to the VNC server
|
||||||
|
// based on the keydown, make sure to skip this event.
|
||||||
|
if (evt.keysym && !last.ignoreKeyPress) {
|
||||||
|
last.keysyms[evt.keysym.keysym] = evt.keysym;
|
||||||
|
evt.type = 'keydown';
|
||||||
|
next(evt);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'keyup':
|
||||||
|
if (state.length === 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var idx = null;
|
||||||
|
// do we have a matching key tracked as being down?
|
||||||
|
for (var i = 0; i !== state.length; ++i) {
|
||||||
|
if (state[i].keyId === evt.keyId) {
|
||||||
|
idx = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if we couldn't find a match (it happens), assume it was the last key pressed
|
||||||
|
if (idx === null) {
|
||||||
|
idx = state.length - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
var item = state.splice(idx, 1)[0];
|
||||||
|
// for each keysym tracked by this key entry, clone the current event and override the keysym
|
||||||
|
var clone = (function(){
|
||||||
|
function Clone(){}
|
||||||
|
return function (obj) { Clone.prototype=obj; return new Clone(); };
|
||||||
|
}());
|
||||||
|
for (var key in item.keysyms) {
|
||||||
|
var out = clone(evt);
|
||||||
|
out.keysym = item.keysyms[key];
|
||||||
|
next(out);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'releaseall':
|
||||||
|
/* jshint shadow: true */
|
||||||
|
for (var i = 0; i < state.length; ++i) {
|
||||||
|
for (var key in state[i].keysyms) {
|
||||||
|
var keysym = state[i].keysyms[key];
|
||||||
|
next({keyId: 0, keysym: keysym, type: 'keyup'});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* jshint shadow: false */
|
||||||
|
state = [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handles "escaping" of modifiers: if a char modifier is used to produce a keysym (such as AltGr-2 to generate an @),
|
||||||
|
// then the modifier must be "undone" before sending the @, and "redone" afterwards.
|
||||||
|
KeyboardUtil.EscapeModifiers = function(next) {
|
||||||
|
return function(evt) {
|
||||||
|
if (evt.type !== 'keydown' || evt.escape === undefined) {
|
||||||
|
next(evt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// undo modifiers
|
||||||
|
for (var i = 0; i < evt.escape.length; ++i) {
|
||||||
|
next({type: 'keyup', keyId: 0, keysym: keysyms.lookup(evt.escape[i])});
|
||||||
|
}
|
||||||
|
// send the character event
|
||||||
next(evt);
|
next(evt);
|
||||||
return;
|
// redo modifiers
|
||||||
}
|
/* jshint shadow: true */
|
||||||
// undo modifiers
|
for (var i = 0; i < evt.escape.length; ++i) {
|
||||||
for (var i = 0; i < evt.escape.length; ++i) {
|
next({type: 'keydown', keyId: 0, keysym: keysyms.lookup(evt.escape[i])});
|
||||||
next({type: 'keyup', keyId: 0, keysym: keysyms.lookup(evt.escape[i])});
|
}
|
||||||
}
|
/* jshint shadow: false */
|
||||||
// send the character event
|
};
|
||||||
next(evt);
|
|
||||||
// redo modifiers
|
|
||||||
/* jshint shadow: true */
|
|
||||||
for (var i = 0; i < evt.escape.length; ++i) {
|
|
||||||
next({type: 'keydown', keyId: 0, keysym: keysyms.lookup(evt.escape[i])});
|
|
||||||
}
|
|
||||||
/* jshint shadow: false */
|
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
/* [module] export default KeyboardUtil; */
|
return KeyboardUtil;
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,153 +1,155 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var XtScancode = {
|
define(function () {
|
||||||
"Escape": 0x0001,
|
var XtScancode = {
|
||||||
"Digit1": 0x0002,
|
"Escape": 0x0001,
|
||||||
"Digit2": 0x0003,
|
"Digit1": 0x0002,
|
||||||
"Digit3": 0x0004,
|
"Digit2": 0x0003,
|
||||||
"Digit4": 0x0005,
|
"Digit3": 0x0004,
|
||||||
"Digit5": 0x0006,
|
"Digit4": 0x0005,
|
||||||
"Digit6": 0x0007,
|
"Digit5": 0x0006,
|
||||||
"Digit7": 0x0008,
|
"Digit6": 0x0007,
|
||||||
"Digit8": 0x0009,
|
"Digit7": 0x0008,
|
||||||
"Digit9": 0x000A,
|
"Digit8": 0x0009,
|
||||||
"Digit0": 0x000B,
|
"Digit9": 0x000A,
|
||||||
"Minus": 0x000C,
|
"Digit0": 0x000B,
|
||||||
"Equal": 0x000D,
|
"Minus": 0x000C,
|
||||||
"Backspace": 0x000E,
|
"Equal": 0x000D,
|
||||||
"Tab": 0x000F,
|
"Backspace": 0x000E,
|
||||||
"KeyQ": 0x0010,
|
"Tab": 0x000F,
|
||||||
"KeyW": 0x0011,
|
"KeyQ": 0x0010,
|
||||||
"KeyE": 0x0012,
|
"KeyW": 0x0011,
|
||||||
"KeyR": 0x0013,
|
"KeyE": 0x0012,
|
||||||
"KeyT": 0x0014,
|
"KeyR": 0x0013,
|
||||||
"KeyY": 0x0015,
|
"KeyT": 0x0014,
|
||||||
"KeyU": 0x0016,
|
"KeyY": 0x0015,
|
||||||
"KeyI": 0x0017,
|
"KeyU": 0x0016,
|
||||||
"KeyO": 0x0018,
|
"KeyI": 0x0017,
|
||||||
"KeyP": 0x0019,
|
"KeyO": 0x0018,
|
||||||
"BracketLeft": 0x001A,
|
"KeyP": 0x0019,
|
||||||
"BracketRight": 0x001B,
|
"BracketLeft": 0x001A,
|
||||||
"Enter": 0x001C,
|
"BracketRight": 0x001B,
|
||||||
"ControlLeft": 0x001D,
|
"Enter": 0x001C,
|
||||||
"KeyA": 0x001E,
|
"ControlLeft": 0x001D,
|
||||||
"KeyS": 0x001F,
|
"KeyA": 0x001E,
|
||||||
"KeyD": 0x0020,
|
"KeyS": 0x001F,
|
||||||
"KeyF": 0x0021,
|
"KeyD": 0x0020,
|
||||||
"KeyG": 0x0022,
|
"KeyF": 0x0021,
|
||||||
"KeyH": 0x0023,
|
"KeyG": 0x0022,
|
||||||
"KeyJ": 0x0024,
|
"KeyH": 0x0023,
|
||||||
"KeyK": 0x0025,
|
"KeyJ": 0x0024,
|
||||||
"KeyL": 0x0026,
|
"KeyK": 0x0025,
|
||||||
"Semicolon": 0x0027,
|
"KeyL": 0x0026,
|
||||||
"Quote": 0x0028,
|
"Semicolon": 0x0027,
|
||||||
"Backquote": 0x0029,
|
"Quote": 0x0028,
|
||||||
"ShiftLeft": 0x002A,
|
"Backquote": 0x0029,
|
||||||
"Backslash": 0x002B,
|
"ShiftLeft": 0x002A,
|
||||||
"KeyZ": 0x002C,
|
"Backslash": 0x002B,
|
||||||
"KeyX": 0x002D,
|
"KeyZ": 0x002C,
|
||||||
"KeyC": 0x002E,
|
"KeyX": 0x002D,
|
||||||
"KeyV": 0x002F,
|
"KeyC": 0x002E,
|
||||||
"KeyB": 0x0030,
|
"KeyV": 0x002F,
|
||||||
"KeyN": 0x0031,
|
"KeyB": 0x0030,
|
||||||
"KeyM": 0x0032,
|
"KeyN": 0x0031,
|
||||||
"Comma": 0x0033,
|
"KeyM": 0x0032,
|
||||||
"Period": 0x0034,
|
"Comma": 0x0033,
|
||||||
"Slash": 0x0035,
|
"Period": 0x0034,
|
||||||
"ShiftRight": 0x0036,
|
"Slash": 0x0035,
|
||||||
"NumpadMultiply": 0x0037,
|
"ShiftRight": 0x0036,
|
||||||
"AltLeft": 0x0038,
|
"NumpadMultiply": 0x0037,
|
||||||
"Space": 0x0039,
|
"AltLeft": 0x0038,
|
||||||
"CapsLock": 0x003A,
|
"Space": 0x0039,
|
||||||
"F1": 0x003B,
|
"CapsLock": 0x003A,
|
||||||
"F2": 0x003C,
|
"F1": 0x003B,
|
||||||
"F3": 0x003D,
|
"F2": 0x003C,
|
||||||
"F4": 0x003E,
|
"F3": 0x003D,
|
||||||
"F5": 0x003F,
|
"F4": 0x003E,
|
||||||
"F6": 0x0040,
|
"F5": 0x003F,
|
||||||
"F7": 0x0041,
|
"F6": 0x0040,
|
||||||
"F8": 0x0042,
|
"F7": 0x0041,
|
||||||
"F9": 0x0043,
|
"F8": 0x0042,
|
||||||
"F10": 0x0044,
|
"F9": 0x0043,
|
||||||
"Pause": 0xE045,
|
"F10": 0x0044,
|
||||||
"ScrollLock": 0x0046,
|
"Pause": 0xE045,
|
||||||
"Numpad7": 0x0047,
|
"ScrollLock": 0x0046,
|
||||||
"Numpad8": 0x0048,
|
"Numpad7": 0x0047,
|
||||||
"Numpad9": 0x0049,
|
"Numpad8": 0x0048,
|
||||||
"NumpadSubtract": 0x004A,
|
"Numpad9": 0x0049,
|
||||||
"Numpad4": 0x004B,
|
"NumpadSubtract": 0x004A,
|
||||||
"Numpad5": 0x004C,
|
"Numpad4": 0x004B,
|
||||||
"Numpad6": 0x004D,
|
"Numpad5": 0x004C,
|
||||||
"NumpadAdd": 0x004E,
|
"Numpad6": 0x004D,
|
||||||
"Numpad1": 0x004F,
|
"NumpadAdd": 0x004E,
|
||||||
"Numpad2": 0x0050,
|
"Numpad1": 0x004F,
|
||||||
"Numpad3": 0x0051,
|
"Numpad2": 0x0050,
|
||||||
"Numpad0": 0x0052,
|
"Numpad3": 0x0051,
|
||||||
"NumpadDecimal": 0x0053,
|
"Numpad0": 0x0052,
|
||||||
"IntlBackslash": 0x0056,
|
"NumpadDecimal": 0x0053,
|
||||||
"F11": 0x0057,
|
"IntlBackslash": 0x0056,
|
||||||
"F12": 0x0058,
|
"F11": 0x0057,
|
||||||
"IntlYen": 0x007D,
|
"F12": 0x0058,
|
||||||
"MediaTrackPrevious": 0xE010,
|
"IntlYen": 0x007D,
|
||||||
"MediaTrackNext": 0xE019,
|
"MediaTrackPrevious": 0xE010,
|
||||||
"NumpadEnter": 0xE01C,
|
"MediaTrackNext": 0xE019,
|
||||||
"ControlRight": 0xE01D,
|
"NumpadEnter": 0xE01C,
|
||||||
"VolumeMute": 0xE020,
|
"ControlRight": 0xE01D,
|
||||||
"MediaPlayPause": 0xE022,
|
"VolumeMute": 0xE020,
|
||||||
"MediaStop": 0xE024,
|
"MediaPlayPause": 0xE022,
|
||||||
"VolumeDown": 0xE02E,
|
"MediaStop": 0xE024,
|
||||||
"VolumeUp": 0xE030,
|
"VolumeDown": 0xE02E,
|
||||||
"BrowserHome": 0xE032,
|
"VolumeUp": 0xE030,
|
||||||
"NumpadDivide": 0xE035,
|
"BrowserHome": 0xE032,
|
||||||
"PrintScreen": 0xE037,
|
"NumpadDivide": 0xE035,
|
||||||
"AltRight": 0xE038,
|
"PrintScreen": 0xE037,
|
||||||
"NumLock": 0x0045,
|
"AltRight": 0xE038,
|
||||||
"Home": 0xE047,
|
"NumLock": 0x0045,
|
||||||
"ArrowUp": 0xE048,
|
"Home": 0xE047,
|
||||||
"PageUp": 0xE049,
|
"ArrowUp": 0xE048,
|
||||||
"ArrowLeft": 0xE04B,
|
"PageUp": 0xE049,
|
||||||
"ArrowRight": 0xE04D,
|
"ArrowLeft": 0xE04B,
|
||||||
"End": 0xE04F,
|
"ArrowRight": 0xE04D,
|
||||||
"ArrowDown": 0xE050,
|
"End": 0xE04F,
|
||||||
"PageDown": 0xE051,
|
"ArrowDown": 0xE050,
|
||||||
"Insert": 0xE052,
|
"PageDown": 0xE051,
|
||||||
"Delete": 0xE053,
|
"Insert": 0xE052,
|
||||||
"MetaLeft": 0xE05B,
|
"Delete": 0xE053,
|
||||||
"MetaRight": 0xE05C,
|
"MetaLeft": 0xE05B,
|
||||||
"OSLeft": 0xE05B, // OSLeft and OSRight are kept for compatability since
|
"MetaRight": 0xE05C,
|
||||||
"OSRight": 0xE05C, // Firefox haven't updated to MetaLeft and MetaRight yet
|
"OSLeft": 0xE05B, // OSLeft and OSRight are kept for compatability since
|
||||||
"ContextMenu": 0xE05D,
|
"OSRight": 0xE05C, // Firefox haven't updated to MetaLeft and MetaRight yet
|
||||||
"BrowserSearch": 0xE065,
|
"ContextMenu": 0xE05D,
|
||||||
"BrowserFavorites": 0xE066,
|
"BrowserSearch": 0xE065,
|
||||||
"BrowserRefresh": 0xE067,
|
"BrowserFavorites": 0xE066,
|
||||||
"BrowserStop": 0xE068,
|
"BrowserRefresh": 0xE067,
|
||||||
"BrowserForward": 0xE069,
|
"BrowserStop": 0xE068,
|
||||||
"BrowserBack": 0xE06A,
|
"BrowserForward": 0xE069,
|
||||||
"NumpadComma": 0x007E,
|
"BrowserBack": 0xE06A,
|
||||||
"NumpadEqual": 0x0059,
|
"NumpadComma": 0x007E,
|
||||||
"F13": 0x0064,
|
"NumpadEqual": 0x0059,
|
||||||
"F14": 0x0065,
|
"F13": 0x0064,
|
||||||
"F15": 0x0066,
|
"F14": 0x0065,
|
||||||
"F16": 0x0067,
|
"F15": 0x0066,
|
||||||
"F17": 0x0068,
|
"F16": 0x0067,
|
||||||
"F18": 0x0069,
|
"F17": 0x0068,
|
||||||
"F19": 0x006A,
|
"F18": 0x0069,
|
||||||
"F20": 0x006B,
|
"F19": 0x006A,
|
||||||
"F21": 0x006C,
|
"F20": 0x006B,
|
||||||
"F22": 0x006D,
|
"F21": 0x006C,
|
||||||
"F23": 0x006E,
|
"F22": 0x006D,
|
||||||
"F24": 0x0076,
|
"F23": 0x006E,
|
||||||
"KanaMode": 0x0070,
|
"F24": 0x0076,
|
||||||
"Lang2": 0x0071,
|
"KanaMode": 0x0070,
|
||||||
"Lang1": 0x0072,
|
"Lang2": 0x0071,
|
||||||
"IntlRo": 0x0073,
|
"Lang1": 0x0072,
|
||||||
"Convert": 0x0079,
|
"IntlRo": 0x0073,
|
||||||
"NonConvert": 0x007B,
|
"Convert": 0x0079,
|
||||||
"LaunchApp2": 0xE021,
|
"NonConvert": 0x007B,
|
||||||
"Power": 0xE05E,
|
"LaunchApp2": 0xE021,
|
||||||
"LaunchApp1": 0xE06B,
|
"Power": 0xE05E,
|
||||||
"LaunchMail": 0xE06C,
|
"LaunchApp1": 0xE06B,
|
||||||
"MediaSelect": 0xE06D,
|
"LaunchMail": 0xE06C,
|
||||||
};
|
"MediaSelect": 0xE06D,
|
||||||
|
};
|
||||||
|
|
||||||
/* [module] export default XtScancode */
|
return XtScancode;
|
||||||
|
});
|
||||||
|
|
|
||||||
498
core/rfb.js
498
core/rfb.js
|
|
@ -2,6 +2,7 @@
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
* Copyright (C) 2016 Samuel Mannehed for Cendio AB
|
* Copyright (C) 2016 Samuel Mannehed for Cendio AB
|
||||||
|
* Copyright (C) 2017 Pierre Ossman for Cendio AB
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
|
|
@ -10,262 +11,255 @@
|
||||||
* (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
|
* (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* [module]
|
|
||||||
* import Util from "./util";
|
|
||||||
* import Display from "./display";
|
|
||||||
* import { Keyboard, Mouse } from "./input/devices"
|
|
||||||
* import Websock from "./websock"
|
|
||||||
* import Base64 from "./base64";
|
|
||||||
* import DES from "./des";
|
|
||||||
* import KeyTable from "./input/keysym";
|
|
||||||
* import XtScancode from "./input/xtscancodes";
|
|
||||||
* import Inflator from "./inflator.mod";
|
|
||||||
*/
|
|
||||||
/*jslint white: false, browser: true */
|
/*jslint white: false, browser: true */
|
||||||
/*global window, Util, Display, Keyboard, Mouse, Websock, Websock_native, Base64, DES, KeyTable, Inflator, XtScancode */
|
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
/* [module] export default */ function RFB(defaults) {
|
define(["core/util", "core/display", "core/websock",
|
||||||
if (!defaults) {
|
"core/inflator", "core/des",
|
||||||
defaults = {};
|
"core/input/devices", "core/input/keysym",
|
||||||
}
|
"core/input/xtscancodes"],
|
||||||
|
function (Util, display, websock, Inflator, DES, devices,
|
||||||
this._rfb_host = '';
|
KeyTable, XtScancode) {
|
||||||
this._rfb_port = 5900;
|
|
||||||
this._rfb_password = '';
|
|
||||||
this._rfb_path = '';
|
|
||||||
|
|
||||||
this._rfb_connection_state = '';
|
|
||||||
this._rfb_init_state = '';
|
|
||||||
this._rfb_version = 0;
|
|
||||||
this._rfb_max_version = 3.8;
|
|
||||||
this._rfb_auth_scheme = '';
|
|
||||||
this._rfb_disconnect_reason = "";
|
|
||||||
|
|
||||||
this._rfb_tightvnc = false;
|
|
||||||
this._rfb_xvp_ver = 0;
|
|
||||||
|
|
||||||
// In preference order
|
|
||||||
this._encodings = [
|
|
||||||
['COPYRECT', 0x01 ],
|
|
||||||
['TIGHT', 0x07 ],
|
|
||||||
['TIGHT_PNG', -260 ],
|
|
||||||
['HEXTILE', 0x05 ],
|
|
||||||
['RRE', 0x02 ],
|
|
||||||
['RAW', 0x00 ],
|
|
||||||
|
|
||||||
// Psuedo-encoding settings
|
|
||||||
|
|
||||||
//['JPEG_quality_lo', -32 ],
|
|
||||||
['JPEG_quality_med', -26 ],
|
|
||||||
//['JPEG_quality_hi', -23 ],
|
|
||||||
//['compress_lo', -255 ],
|
|
||||||
['compress_hi', -247 ],
|
|
||||||
|
|
||||||
['DesktopSize', -223 ],
|
|
||||||
['last_rect', -224 ],
|
|
||||||
['Cursor', -239 ],
|
|
||||||
['QEMUExtendedKeyEvent', -258 ],
|
|
||||||
['ExtendedDesktopSize', -308 ],
|
|
||||||
['xvp', -309 ],
|
|
||||||
['Fence', -312 ],
|
|
||||||
['ContinuousUpdates', -313 ]
|
|
||||||
];
|
|
||||||
|
|
||||||
this._encHandlers = {};
|
|
||||||
this._encNames = {};
|
|
||||||
this._encStats = {};
|
|
||||||
|
|
||||||
this._sock = null; // Websock object
|
|
||||||
this._display = null; // Display object
|
|
||||||
this._flushing = false; // Display flushing state
|
|
||||||
this._keyboard = null; // Keyboard input handler object
|
|
||||||
this._mouse = null; // Mouse input handler object
|
|
||||||
this._disconnTimer = null; // disconnection timer
|
|
||||||
|
|
||||||
this._supportsFence = false;
|
|
||||||
|
|
||||||
this._supportsContinuousUpdates = false;
|
|
||||||
this._enabledContinuousUpdates = false;
|
|
||||||
|
|
||||||
// Frame buffer update state
|
|
||||||
this._FBU = {
|
|
||||||
rects: 0,
|
|
||||||
subrects: 0, // RRE
|
|
||||||
lines: 0, // RAW
|
|
||||||
tiles: 0, // HEXTILE
|
|
||||||
bytes: 0,
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: 0,
|
|
||||||
height: 0,
|
|
||||||
encoding: 0,
|
|
||||||
subencoding: -1,
|
|
||||||
background: null,
|
|
||||||
zlib: [] // TIGHT zlib streams
|
|
||||||
};
|
|
||||||
|
|
||||||
this._fb_Bpp = 4;
|
|
||||||
this._fb_depth = 3;
|
|
||||||
this._fb_width = 0;
|
|
||||||
this._fb_height = 0;
|
|
||||||
this._fb_name = "";
|
|
||||||
|
|
||||||
this._destBuff = null;
|
|
||||||
this._paletteBuff = new Uint8Array(1024); // 256 * 4 (max palette size * max bytes-per-pixel)
|
|
||||||
|
|
||||||
this._rre_chunk_sz = 100;
|
|
||||||
|
|
||||||
this._timing = {
|
|
||||||
last_fbu: 0,
|
|
||||||
fbu_total: 0,
|
|
||||||
fbu_total_cnt: 0,
|
|
||||||
full_fbu_total: 0,
|
|
||||||
full_fbu_cnt: 0,
|
|
||||||
|
|
||||||
fbu_rt_start: 0,
|
|
||||||
fbu_rt_total: 0,
|
|
||||||
fbu_rt_cnt: 0,
|
|
||||||
pixels: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
this._supportsSetDesktopSize = false;
|
|
||||||
this._screen_id = 0;
|
|
||||||
this._screen_flags = 0;
|
|
||||||
|
|
||||||
// Mouse state
|
|
||||||
this._mouse_buttonMask = 0;
|
|
||||||
this._mouse_arr = [];
|
|
||||||
this._viewportDragging = false;
|
|
||||||
this._viewportDragPos = {};
|
|
||||||
this._viewportHasMoved = false;
|
|
||||||
|
|
||||||
// QEMU Extended Key Event support - default to false
|
|
||||||
this._qemuExtKeyEventSupported = false;
|
|
||||||
|
|
||||||
// set the default value on user-facing properties
|
|
||||||
Util.set_defaults(this, defaults, {
|
|
||||||
'target': 'null', // VNC display rendering Canvas object
|
|
||||||
'focusContainer': document, // DOM element that captures keyboard input
|
|
||||||
'encrypt': false, // Use TLS/SSL/wss encryption
|
|
||||||
'true_color': true, // Request true color pixel data
|
|
||||||
'local_cursor': false, // Request locally rendered cursor
|
|
||||||
'shared': true, // Request shared mode
|
|
||||||
'view_only': false, // Disable client mouse/keyboard
|
|
||||||
'xvp_password_sep': '@', // Separator for XVP password fields
|
|
||||||
'disconnectTimeout': 3, // Time (s) to wait for disconnection
|
|
||||||
'wsProtocols': ['binary'], // Protocols to use in the WebSocket connection
|
|
||||||
'repeaterID': '', // [UltraVNC] RepeaterID to connect to
|
|
||||||
'viewportDrag': false, // Move the viewport on mouse drags
|
|
||||||
|
|
||||||
// Callback functions
|
|
||||||
'onUpdateState': function () { }, // onUpdateState(rfb, state, oldstate): connection state change
|
|
||||||
'onNotification': function () { }, // onNotification(rfb, msg, level, options): notification for UI
|
|
||||||
'onDisconnected': function () { }, // onDisconnected(rfb, reason): disconnection finished
|
|
||||||
'onPasswordRequired': function () { }, // onPasswordRequired(rfb, msg): VNC password is required
|
|
||||||
'onClipboard': function () { }, // onClipboard(rfb, text): RFB clipboard contents received
|
|
||||||
'onBell': function () { }, // onBell(rfb): RFB Bell message received
|
|
||||||
'onFBUReceive': function () { }, // onFBUReceive(rfb, fbu): RFB FBU received but not yet processed
|
|
||||||
'onFBUComplete': function () { }, // onFBUComplete(rfb, fbu): RFB FBU received and processed
|
|
||||||
'onFBResize': function () { }, // onFBResize(rfb, width, height): frame buffer resized
|
|
||||||
'onDesktopName': function () { }, // onDesktopName(rfb, name): desktop name received
|
|
||||||
'onXvpInit': function () { } // onXvpInit(version): XVP extensions active for this connection
|
|
||||||
});
|
|
||||||
|
|
||||||
// main setup
|
|
||||||
Util.Debug(">> RFB.constructor");
|
|
||||||
|
|
||||||
// populate encHandlers with bound versions
|
|
||||||
Object.keys(RFB.encodingHandlers).forEach(function (encName) {
|
|
||||||
this._encHandlers[encName] = RFB.encodingHandlers[encName].bind(this);
|
|
||||||
}.bind(this));
|
|
||||||
|
|
||||||
// Create lookup tables based on encoding number
|
|
||||||
for (var i = 0; i < this._encodings.length; i++) {
|
|
||||||
this._encHandlers[this._encodings[i][1]] = this._encHandlers[this._encodings[i][0]];
|
|
||||||
this._encNames[this._encodings[i][1]] = this._encodings[i][0];
|
|
||||||
this._encStats[this._encodings[i][1]] = [0, 0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// NB: nothing that needs explicit teardown should be done
|
|
||||||
// before this point, since this can throw an exception
|
|
||||||
try {
|
|
||||||
this._display = new Display({target: this._target,
|
|
||||||
onFlush: this._onFlush.bind(this)});
|
|
||||||
} catch (exc) {
|
|
||||||
Util.Error("Display exception: " + exc);
|
|
||||||
throw exc;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._keyboard = new Keyboard({target: this._focusContainer,
|
|
||||||
onKeyPress: this._handleKeyPress.bind(this)});
|
|
||||||
|
|
||||||
this._mouse = new Mouse({target: this._target,
|
|
||||||
onMouseButton: this._handleMouseButton.bind(this),
|
|
||||||
onMouseMove: this._handleMouseMove.bind(this),
|
|
||||||
notify: this._keyboard.sync.bind(this._keyboard)});
|
|
||||||
|
|
||||||
this._sock = new Websock();
|
|
||||||
this._sock.on('message', this._handle_message.bind(this));
|
|
||||||
this._sock.on('open', function () {
|
|
||||||
if ((this._rfb_connection_state === 'connecting') &&
|
|
||||||
(this._rfb_init_state === '')) {
|
|
||||||
this._rfb_init_state = 'ProtocolVersion';
|
|
||||||
Util.Debug("Starting VNC handshake");
|
|
||||||
} else {
|
|
||||||
this._fail("Unexpected server connection");
|
|
||||||
}
|
|
||||||
}.bind(this));
|
|
||||||
this._sock.on('close', function (e) {
|
|
||||||
Util.Warn("WebSocket on-close event");
|
|
||||||
var msg = "";
|
|
||||||
if (e.code) {
|
|
||||||
msg = " (code: " + e.code;
|
|
||||||
if (e.reason) {
|
|
||||||
msg += ", reason: " + e.reason;
|
|
||||||
}
|
|
||||||
msg += ")";
|
|
||||||
}
|
|
||||||
switch (this._rfb_connection_state) {
|
|
||||||
case 'disconnecting':
|
|
||||||
this._updateConnectionState('disconnected');
|
|
||||||
break;
|
|
||||||
case 'connecting':
|
|
||||||
this._fail('Failed to connect to server', msg);
|
|
||||||
break;
|
|
||||||
case 'connected':
|
|
||||||
// Handle disconnects that were initiated server-side
|
|
||||||
this._updateConnectionState('disconnecting');
|
|
||||||
this._updateConnectionState('disconnected');
|
|
||||||
break;
|
|
||||||
case 'disconnected':
|
|
||||||
this._fail("Unexpected server disconnect",
|
|
||||||
"Already disconnected: " + msg);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this._fail("Unexpected server disconnect",
|
|
||||||
"Not in any state yet: " + msg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
this._sock.off('close');
|
|
||||||
}.bind(this));
|
|
||||||
this._sock.on('error', function (e) {
|
|
||||||
Util.Warn("WebSocket on-error event");
|
|
||||||
});
|
|
||||||
|
|
||||||
this._init_vars();
|
|
||||||
this._cleanup();
|
|
||||||
|
|
||||||
var rmode = this._display.get_render_mode();
|
|
||||||
Util.Info("Using native WebSockets, render mode: " + rmode);
|
|
||||||
|
|
||||||
Util.Debug("<< RFB.constructor");
|
|
||||||
};
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
var _ = Util.Localisation.get;
|
var _ = Util.Localisation.get;
|
||||||
|
|
||||||
|
function RFB(defaults) {
|
||||||
|
if (!defaults) {
|
||||||
|
defaults = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
this._rfb_host = '';
|
||||||
|
this._rfb_port = 5900;
|
||||||
|
this._rfb_password = '';
|
||||||
|
this._rfb_path = '';
|
||||||
|
|
||||||
|
this._rfb_connection_state = '';
|
||||||
|
this._rfb_init_state = '';
|
||||||
|
this._rfb_version = 0;
|
||||||
|
this._rfb_max_version = 3.8;
|
||||||
|
this._rfb_auth_scheme = '';
|
||||||
|
this._rfb_disconnect_reason = "";
|
||||||
|
|
||||||
|
this._rfb_tightvnc = false;
|
||||||
|
this._rfb_xvp_ver = 0;
|
||||||
|
|
||||||
|
// In preference order
|
||||||
|
this._encodings = [
|
||||||
|
['COPYRECT', 0x01 ],
|
||||||
|
['TIGHT', 0x07 ],
|
||||||
|
['TIGHT_PNG', -260 ],
|
||||||
|
['HEXTILE', 0x05 ],
|
||||||
|
['RRE', 0x02 ],
|
||||||
|
['RAW', 0x00 ],
|
||||||
|
|
||||||
|
// Psuedo-encoding settings
|
||||||
|
|
||||||
|
//['JPEG_quality_lo', -32 ],
|
||||||
|
['JPEG_quality_med', -26 ],
|
||||||
|
//['JPEG_quality_hi', -23 ],
|
||||||
|
//['compress_lo', -255 ],
|
||||||
|
['compress_hi', -247 ],
|
||||||
|
|
||||||
|
['DesktopSize', -223 ],
|
||||||
|
['last_rect', -224 ],
|
||||||
|
['Cursor', -239 ],
|
||||||
|
['QEMUExtendedKeyEvent', -258 ],
|
||||||
|
['ExtendedDesktopSize', -308 ],
|
||||||
|
['xvp', -309 ],
|
||||||
|
['Fence', -312 ],
|
||||||
|
['ContinuousUpdates', -313 ]
|
||||||
|
];
|
||||||
|
|
||||||
|
this._encHandlers = {};
|
||||||
|
this._encNames = {};
|
||||||
|
this._encStats = {};
|
||||||
|
|
||||||
|
this._sock = null; // Websock object
|
||||||
|
this._display = null; // Display object
|
||||||
|
this._flushing = false; // Display flushing state
|
||||||
|
this._keyboard = null; // Keyboard input handler object
|
||||||
|
this._mouse = null; // Mouse input handler object
|
||||||
|
this._disconnTimer = null; // disconnection timer
|
||||||
|
|
||||||
|
this._supportsFence = false;
|
||||||
|
|
||||||
|
this._supportsContinuousUpdates = false;
|
||||||
|
this._enabledContinuousUpdates = false;
|
||||||
|
|
||||||
|
// Frame buffer update state
|
||||||
|
this._FBU = {
|
||||||
|
rects: 0,
|
||||||
|
subrects: 0, // RRE
|
||||||
|
lines: 0, // RAW
|
||||||
|
tiles: 0, // HEXTILE
|
||||||
|
bytes: 0,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: 0,
|
||||||
|
height: 0,
|
||||||
|
encoding: 0,
|
||||||
|
subencoding: -1,
|
||||||
|
background: null,
|
||||||
|
zlib: [] // TIGHT zlib streams
|
||||||
|
};
|
||||||
|
|
||||||
|
this._fb_Bpp = 4;
|
||||||
|
this._fb_depth = 3;
|
||||||
|
this._fb_width = 0;
|
||||||
|
this._fb_height = 0;
|
||||||
|
this._fb_name = "";
|
||||||
|
|
||||||
|
this._destBuff = null;
|
||||||
|
this._paletteBuff = new Uint8Array(1024); // 256 * 4 (max palette size * max bytes-per-pixel)
|
||||||
|
|
||||||
|
this._rre_chunk_sz = 100;
|
||||||
|
|
||||||
|
this._timing = {
|
||||||
|
last_fbu: 0,
|
||||||
|
fbu_total: 0,
|
||||||
|
fbu_total_cnt: 0,
|
||||||
|
full_fbu_total: 0,
|
||||||
|
full_fbu_cnt: 0,
|
||||||
|
|
||||||
|
fbu_rt_start: 0,
|
||||||
|
fbu_rt_total: 0,
|
||||||
|
fbu_rt_cnt: 0,
|
||||||
|
pixels: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
this._supportsSetDesktopSize = false;
|
||||||
|
this._screen_id = 0;
|
||||||
|
this._screen_flags = 0;
|
||||||
|
|
||||||
|
// Mouse state
|
||||||
|
this._mouse_buttonMask = 0;
|
||||||
|
this._mouse_arr = [];
|
||||||
|
this._viewportDragging = false;
|
||||||
|
this._viewportDragPos = {};
|
||||||
|
this._viewportHasMoved = false;
|
||||||
|
|
||||||
|
// QEMU Extended Key Event support - default to false
|
||||||
|
this._qemuExtKeyEventSupported = false;
|
||||||
|
|
||||||
|
// set the default value on user-facing properties
|
||||||
|
Util.set_defaults(this, defaults, {
|
||||||
|
'target': 'null', // VNC display rendering Canvas object
|
||||||
|
'focusContainer': document, // DOM element that captures keyboard input
|
||||||
|
'encrypt': false, // Use TLS/SSL/wss encryption
|
||||||
|
'true_color': true, // Request true color pixel data
|
||||||
|
'local_cursor': false, // Request locally rendered cursor
|
||||||
|
'shared': true, // Request shared mode
|
||||||
|
'view_only': false, // Disable client mouse/keyboard
|
||||||
|
'xvp_password_sep': '@', // Separator for XVP password fields
|
||||||
|
'disconnectTimeout': 3, // Time (s) to wait for disconnection
|
||||||
|
'wsProtocols': ['binary'], // Protocols to use in the WebSocket connection
|
||||||
|
'repeaterID': '', // [UltraVNC] RepeaterID to connect to
|
||||||
|
'viewportDrag': false, // Move the viewport on mouse drags
|
||||||
|
|
||||||
|
// Callback functions
|
||||||
|
'onUpdateState': function () { }, // onUpdateState(rfb, state, oldstate): connection state change
|
||||||
|
'onNotification': function () { }, // onNotification(rfb, msg, level, options): notification for UI
|
||||||
|
'onDisconnected': function () { }, // onDisconnected(rfb, reason): disconnection finished
|
||||||
|
'onPasswordRequired': function () { }, // onPasswordRequired(rfb, msg): VNC password is required
|
||||||
|
'onClipboard': function () { }, // onClipboard(rfb, text): RFB clipboard contents received
|
||||||
|
'onBell': function () { }, // onBell(rfb): RFB Bell message received
|
||||||
|
'onFBUReceive': function () { }, // onFBUReceive(rfb, fbu): RFB FBU received but not yet processed
|
||||||
|
'onFBUComplete': function () { }, // onFBUComplete(rfb, fbu): RFB FBU received and processed
|
||||||
|
'onFBResize': function () { }, // onFBResize(rfb, width, height): frame buffer resized
|
||||||
|
'onDesktopName': function () { }, // onDesktopName(rfb, name): desktop name received
|
||||||
|
'onXvpInit': function () { } // onXvpInit(version): XVP extensions active for this connection
|
||||||
|
});
|
||||||
|
|
||||||
|
// main setup
|
||||||
|
Util.Debug(">> RFB.constructor");
|
||||||
|
|
||||||
|
// populate encHandlers with bound versions
|
||||||
|
Object.keys(RFB.encodingHandlers).forEach(function (encName) {
|
||||||
|
this._encHandlers[encName] = RFB.encodingHandlers[encName].bind(this);
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
// Create lookup tables based on encoding number
|
||||||
|
for (var i = 0; i < this._encodings.length; i++) {
|
||||||
|
this._encHandlers[this._encodings[i][1]] = this._encHandlers[this._encodings[i][0]];
|
||||||
|
this._encNames[this._encodings[i][1]] = this._encodings[i][0];
|
||||||
|
this._encStats[this._encodings[i][1]] = [0, 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
// NB: nothing that needs explicit teardown should be done
|
||||||
|
// before this point, since this can throw an exception
|
||||||
|
try {
|
||||||
|
this._display = new display.Display({target: this._target,
|
||||||
|
onFlush: this._onFlush.bind(this)});
|
||||||
|
} catch (exc) {
|
||||||
|
Util.Error("Display exception: " + exc);
|
||||||
|
throw exc;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._keyboard = new devices.Keyboard({target: this._focusContainer,
|
||||||
|
onKeyPress: this._handleKeyPress.bind(this)});
|
||||||
|
|
||||||
|
this._mouse = new devices.Mouse({target: this._target,
|
||||||
|
onMouseButton: this._handleMouseButton.bind(this),
|
||||||
|
onMouseMove: this._handleMouseMove.bind(this),
|
||||||
|
notify: this._keyboard.sync.bind(this._keyboard)});
|
||||||
|
|
||||||
|
this._sock = new websock.Websock();
|
||||||
|
this._sock.on('message', this._handle_message.bind(this));
|
||||||
|
this._sock.on('open', function () {
|
||||||
|
if ((this._rfb_connection_state === 'connecting') &&
|
||||||
|
(this._rfb_init_state === '')) {
|
||||||
|
this._rfb_init_state = 'ProtocolVersion';
|
||||||
|
Util.Debug("Starting VNC handshake");
|
||||||
|
} else {
|
||||||
|
this._fail("Unexpected server connection");
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
this._sock.on('close', function (e) {
|
||||||
|
Util.Warn("WebSocket on-close event");
|
||||||
|
var msg = "";
|
||||||
|
if (e.code) {
|
||||||
|
msg = " (code: " + e.code;
|
||||||
|
if (e.reason) {
|
||||||
|
msg += ", reason: " + e.reason;
|
||||||
|
}
|
||||||
|
msg += ")";
|
||||||
|
}
|
||||||
|
switch (this._rfb_connection_state) {
|
||||||
|
case 'disconnecting':
|
||||||
|
this._updateConnectionState('disconnected');
|
||||||
|
break;
|
||||||
|
case 'connecting':
|
||||||
|
this._fail('Failed to connect to server', msg);
|
||||||
|
break;
|
||||||
|
case 'connected':
|
||||||
|
// Handle disconnects that were initiated server-side
|
||||||
|
this._updateConnectionState('disconnecting');
|
||||||
|
this._updateConnectionState('disconnected');
|
||||||
|
break;
|
||||||
|
case 'disconnected':
|
||||||
|
this._fail("Unexpected server disconnect",
|
||||||
|
"Already disconnected: " + msg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
this._fail("Unexpected server disconnect",
|
||||||
|
"Not in any state yet: " + msg);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
this._sock.off('close');
|
||||||
|
}.bind(this));
|
||||||
|
this._sock.on('error', function (e) {
|
||||||
|
Util.Warn("WebSocket on-error event");
|
||||||
|
});
|
||||||
|
|
||||||
|
this._init_vars();
|
||||||
|
this._cleanup();
|
||||||
|
|
||||||
|
var rmode = this._display.get_render_mode();
|
||||||
|
Util.Info("Using native WebSockets, render mode: " + rmode);
|
||||||
|
|
||||||
|
Util.Debug("<< RFB.constructor");
|
||||||
|
};
|
||||||
|
|
||||||
RFB.prototype = {
|
RFB.prototype = {
|
||||||
// Public methods
|
// Public methods
|
||||||
connect: function (host, port, password, path) {
|
connect: function (host, port, password, path) {
|
||||||
|
|
@ -2443,4 +2437,6 @@
|
||||||
Util.Error("Server sent compress level pseudo-encoding");
|
Util.Error("Server sent compress level pseudo-encoding");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
|
||||||
|
return { RFB: RFB };
|
||||||
|
});
|
||||||
|
|
|
||||||
774
core/util.js
774
core/util.js
|
|
@ -1,282 +1,276 @@
|
||||||
/*
|
/*
|
||||||
* noVNC: HTML5 VNC client
|
* noVNC: HTML5 VNC client
|
||||||
* Copyright (C) 2012 Joel Martin
|
* Copyright (C) 2012 Joel Martin
|
||||||
|
* Copyright (C) 2017 Pierre Ossman for Cendio AB
|
||||||
* Licensed under MPL 2.0 (see LICENSE.txt)
|
* Licensed under MPL 2.0 (see LICENSE.txt)
|
||||||
*
|
*
|
||||||
* See README.md for usage and integration instructions.
|
* See README.md for usage and integration instructions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* jshint white: false, nonstandard: true */
|
/* jshint white: false, nonstandard: true */
|
||||||
/*global window, console, document, navigator, ActiveXObject, INCLUDE_URI */
|
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var Util = {};
|
define(function() {
|
||||||
|
var Util = {};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ------------------------------------------------------
|
* Logging/debug routines
|
||||||
* Namespaced in Util
|
*/
|
||||||
* ------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
var _log_level = 'warn';
|
||||||
* Logging/debug routines
|
Util.init_logging = function (level) {
|
||||||
*/
|
if (typeof level === 'undefined') {
|
||||||
|
level = _log_level;
|
||||||
Util._log_level = 'warn';
|
} else {
|
||||||
Util.init_logging = function (level) {
|
_log_level = level;
|
||||||
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 = function (msg) { console.log(msg); };
|
|
||||||
case 'info':
|
|
||||||
Util.Info = function (msg) { console.info(msg); };
|
|
||||||
case 'warn':
|
|
||||||
Util.Warn = function (msg) { console.warn(msg); };
|
|
||||||
case 'error':
|
|
||||||
Util.Error = function (msg) { console.error(msg); };
|
|
||||||
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) {
|
Util.Debug = Util.Info = Util.Warn = Util.Error = function (msg) {};
|
||||||
|
if (typeof window.console !== "undefined") {
|
||||||
|
/* jshint -W086 */
|
||||||
|
switch (level) {
|
||||||
|
case 'debug':
|
||||||
|
Util.Debug = function (msg) { console.log(msg); };
|
||||||
|
case 'info':
|
||||||
|
Util.Info = function (msg) { console.info(msg); };
|
||||||
|
case 'warn':
|
||||||
|
Util.Warn = function (msg) { console.warn(msg); };
|
||||||
|
case 'error':
|
||||||
|
Util.Error = function (msg) { console.error(msg); };
|
||||||
|
case 'none':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("invalid logging type '" + level + "'");
|
||||||
|
}
|
||||||
|
/* jshint +W086 */
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Util.get_logging = function () {
|
||||||
|
return _log_level;
|
||||||
|
};
|
||||||
|
// Initialize logging level
|
||||||
|
Util.init_logging();
|
||||||
|
|
||||||
var getter;
|
Util.make_property = function (proto, name, mode, type) {
|
||||||
if (type === 'arr') {
|
|
||||||
getter = function (idx) {
|
var getter;
|
||||||
if (typeof idx !== 'undefined') {
|
if (type === 'arr') {
|
||||||
return this['_' + name][idx];
|
getter = function (idx) {
|
||||||
} else {
|
if (typeof idx !== 'undefined') {
|
||||||
|
return this['_' + name][idx];
|
||||||
|
} else {
|
||||||
|
return this['_' + name];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
getter = function () {
|
||||||
return this['_' + name];
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
} else {
|
|
||||||
getter = function () {
|
|
||||||
return this['_' + name];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var make_setter = function (process_val) {
|
var setter;
|
||||||
if (process_val) {
|
if (type === 'bool') {
|
||||||
return function (val, idx) {
|
setter = make_setter(function (val) {
|
||||||
if (typeof idx !== 'undefined') {
|
if (!val || (val in {'0': 1, 'no': 1, 'false': 1})) {
|
||||||
this['_' + name][idx] = process_val(val);
|
return false;
|
||||||
} else {
|
} else {
|
||||||
this['_' + name] = process_val(val);
|
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 {
|
} else {
|
||||||
return function (val, idx) {
|
throw new Error('Unknown property type ' + type); // some sanity checking
|
||||||
if (typeof idx !== 'undefined') {
|
}
|
||||||
this['_' + name][idx] = val;
|
|
||||||
} else {
|
// set the getter
|
||||||
this['_' + name] = val;
|
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) {
|
||||||
|
for (var i = 0; i < arr.length; i++) {
|
||||||
|
Util.make_property(constructor.prototype, arr[i][0], arr[i][1], arr[i][2]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var setter;
|
Util.set_defaults = function (obj, conf, defaults) {
|
||||||
if (type === 'bool') {
|
var defaults_keys = Object.keys(defaults);
|
||||||
setter = make_setter(function (val) {
|
var conf_keys = Object.keys(conf);
|
||||||
if (!val || (val in {'0': 1, 'no': 1, 'false': 1})) {
|
var keys_obj = {};
|
||||||
return false;
|
var i;
|
||||||
} else {
|
for (i = 0; i < defaults_keys.length; i++) { keys_obj[defaults_keys[i]] = 1; }
|
||||||
return true;
|
for (i = 0; i < conf_keys.length; i++) { keys_obj[conf_keys[i]] = 1; }
|
||||||
}
|
var keys = Object.keys(keys_obj);
|
||||||
});
|
|
||||||
} 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
|
for (i = 0; i < keys.length; i++) {
|
||||||
if (typeof proto['get_' + name] === 'undefined') {
|
var setter = obj['_raw_set_' + keys[i]];
|
||||||
proto['get_' + name] = getter;
|
if (!setter) {
|
||||||
}
|
Util.Warn('Invalid property ' + keys[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// set the setter if needed
|
if (keys[i] in conf) {
|
||||||
if (typeof proto['set_' + name] === 'undefined') {
|
setter.call(obj, conf[keys[i]]);
|
||||||
if (mode === 'rw') {
|
} else {
|
||||||
proto['set_' + name] = setter;
|
setter.call(obj, defaults[keys[i]]);
|
||||||
} 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) {
|
/*
|
||||||
for (var i = 0; i < arr.length; i++) {
|
* Decode from UTF-8
|
||||||
Util.make_property(constructor.prototype, arr[i][0], arr[i][1], arr[i][2]);
|
*/
|
||||||
}
|
Util.decodeUTF8 = function (utf8string) {
|
||||||
};
|
return decodeURIComponent(escape(utf8string));
|
||||||
|
};
|
||||||
|
|
||||||
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) {
|
* Cross-browser routines
|
||||||
Util.Warn('Invalid property ' + keys[i]);
|
*/
|
||||||
continue;
|
|
||||||
|
Util.getPosition = function(obj) {
|
||||||
|
// NB(sross): the Mozilla developer reference seems to indicate that
|
||||||
|
// getBoundingClientRect includes border and padding, so the canvas
|
||||||
|
// style should NOT include either.
|
||||||
|
var objPosition = obj.getBoundingClientRect();
|
||||||
|
return {'x': objPosition.left + window.pageXOffset, 'y': objPosition.top + window.pageYOffset,
|
||||||
|
'width': objPosition.width, 'height': objPosition.height};
|
||||||
|
};
|
||||||
|
|
||||||
|
Util.getPointerEvent = function (e) {
|
||||||
|
var evt;
|
||||||
|
evt = (e ? e : window.event);
|
||||||
|
evt = (evt.changedTouches ? evt.changedTouches[0] : evt.touches ? evt.touches[0] : evt);
|
||||||
|
return evt;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get mouse event position in DOM element
|
||||||
|
Util.getEventPosition = function (e, obj, scale) {
|
||||||
|
var evt, docX, docY, pos;
|
||||||
|
evt = Util.getPointerEvent(e);
|
||||||
|
if (evt.pageX || evt.pageY) {
|
||||||
|
docX = evt.pageX;
|
||||||
|
docY = evt.pageY;
|
||||||
|
} else if (evt.clientX || evt.clientY) {
|
||||||
|
docX = evt.clientX + document.body.scrollLeft +
|
||||||
|
document.documentElement.scrollLeft;
|
||||||
|
docY = evt.clientY + document.body.scrollTop +
|
||||||
|
document.documentElement.scrollTop;
|
||||||
}
|
}
|
||||||
|
pos = Util.getPosition(obj);
|
||||||
if (keys[i] in conf) {
|
if (typeof scale === "undefined") {
|
||||||
setter.call(obj, conf[keys[i]]);
|
scale = 1;
|
||||||
} else {
|
|
||||||
setter.call(obj, defaults[keys[i]]);
|
|
||||||
}
|
}
|
||||||
}
|
var realx = docX - pos.x;
|
||||||
};
|
var realy = docY - pos.y;
|
||||||
|
var x = Math.max(Math.min(realx, pos.width - 1), 0);
|
||||||
|
var y = Math.max(Math.min(realy, pos.height - 1), 0);
|
||||||
|
return {'x': x / scale, 'y': y / scale, 'realx': realx / scale, 'realy': realy / scale};
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
Util.stopEvent = function (e) {
|
||||||
* Decode from UTF-8
|
e.stopPropagation();
|
||||||
*/
|
e.preventDefault();
|
||||||
Util.decodeUTF8 = function (utf8string) {
|
};
|
||||||
return decodeURIComponent(escape(utf8string));
|
|
||||||
};
|
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
var _cursor_uris_supported = null;
|
||||||
|
|
||||||
/*
|
Util.browserSupportsCursorURIs = function () {
|
||||||
* Cross-browser routines
|
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';
|
||||||
|
|
||||||
Util.getPosition = function(obj) {
|
if (target.style.cursor) {
|
||||||
// NB(sross): the Mozilla developer reference seems to indicate that
|
Util.Info("Data URI scheme cursor supported");
|
||||||
// getBoundingClientRect includes border and padding, so the canvas
|
_cursor_uris_supported = true;
|
||||||
// style should NOT include either.
|
} else {
|
||||||
var objPosition = obj.getBoundingClientRect();
|
Util.Warn("Data URI scheme cursor not supported");
|
||||||
return {'x': objPosition.left + window.pageXOffset, 'y': objPosition.top + window.pageYOffset,
|
_cursor_uris_supported = false;
|
||||||
'width': objPosition.width, 'height': objPosition.height};
|
}
|
||||||
};
|
} catch (exc) {
|
||||||
|
Util.Error("Data URI scheme cursor test exception: " + exc);
|
||||||
Util.getPointerEvent = function (e) {
|
_cursor_uris_supported = false;
|
||||||
var evt;
|
|
||||||
evt = (e ? e : window.event);
|
|
||||||
evt = (evt.changedTouches ? evt.changedTouches[0] : evt.touches ? evt.touches[0] : evt);
|
|
||||||
return evt;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Get mouse event position in DOM element
|
|
||||||
Util.getEventPosition = function (e, obj, scale) {
|
|
||||||
var evt, docX, docY, pos;
|
|
||||||
evt = Util.getPointerEvent(e);
|
|
||||||
if (evt.pageX || evt.pageY) {
|
|
||||||
docX = evt.pageX;
|
|
||||||
docY = evt.pageY;
|
|
||||||
} else if (evt.clientX || evt.clientY) {
|
|
||||||
docX = evt.clientX + document.body.scrollLeft +
|
|
||||||
document.documentElement.scrollLeft;
|
|
||||||
docY = evt.clientY + document.body.scrollTop +
|
|
||||||
document.documentElement.scrollTop;
|
|
||||||
}
|
|
||||||
pos = Util.getPosition(obj);
|
|
||||||
if (typeof scale === "undefined") {
|
|
||||||
scale = 1;
|
|
||||||
}
|
|
||||||
var realx = docX - pos.x;
|
|
||||||
var realy = docY - pos.y;
|
|
||||||
var x = Math.max(Math.min(realx, pos.width - 1), 0);
|
|
||||||
var y = Math.max(Math.min(realy, pos.height - 1), 0);
|
|
||||||
return {'x': x / scale, 'y': y / scale, 'realx': realx / scale, 'realy': realy / scale};
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
return _cursor_uris_supported;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set browser engine versions. Based on mootools.
|
// Set browser engine versions. Based on mootools.
|
||||||
Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)};
|
Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !!(document.querySelector)};
|
||||||
|
|
||||||
(function () {
|
|
||||||
// 'presto': (function () { return (!window.opera) ? false : true; }()),
|
// 'presto': (function () { return (!window.opera) ? false : true; }()),
|
||||||
var detectPresto = function () {
|
var detectPresto = function () {
|
||||||
return !!window.opera;
|
return !!window.opera;
|
||||||
|
|
@ -343,172 +337,202 @@ Util.Features = {xpath: !!(document.evaluate), air: !!(window.runtime), query: !
|
||||||
// Extract actual webkit version if available
|
// Extract actual webkit version if available
|
||||||
Util.Engine.webkit = detectActualWebkit(Util.Engine.webkit);
|
Util.Engine.webkit = detectActualWebkit(Util.Engine.webkit);
|
||||||
}
|
}
|
||||||
})();
|
|
||||||
|
|
||||||
Util.Flash = (function () {
|
Util.Flash = (function () {
|
||||||
var v, version;
|
var v, version;
|
||||||
try {
|
|
||||||
v = navigator.plugins['Shockwave Flash'].description;
|
|
||||||
} catch (err1) {
|
|
||||||
try {
|
try {
|
||||||
v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
|
v = navigator.plugins['Shockwave Flash'].description;
|
||||||
} catch (err2) {
|
} catch (err1) {
|
||||||
v = '0 r0';
|
try {
|
||||||
|
v = new ActiveXObject('ShockwaveFlash.ShockwaveFlash').GetVariable('$version');
|
||||||
|
} catch (err2) {
|
||||||
|
v = '0 r0';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
version = v.match(/\d+/g);
|
||||||
version = v.match(/\d+/g);
|
return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
|
||||||
return {version: parseInt(version[0] || 0 + '.' + version[1], 10) || 0, build: parseInt(version[2], 10) || 0};
|
}());
|
||||||
}());
|
|
||||||
|
|
||||||
|
|
||||||
Util.Localisation = {
|
Util.Localisation = {
|
||||||
// Currently configured language
|
// Currently configured language
|
||||||
language: 'en',
|
language: 'en',
|
||||||
|
|
||||||
// Configure suitable language based on user preferences
|
// Translation data
|
||||||
setup: function (supportedLanguages) {
|
_translations: null,
|
||||||
var userLanguages;
|
|
||||||
|
|
||||||
Util.Localisation.language = 'en'; // Default: US English
|
// Configure and load suitable language based on user preferences
|
||||||
|
setup: function (supportedLanguages, basedir, callback) {
|
||||||
|
Util.Localisation._setLanguageCode(supportedLanguages);
|
||||||
|
|
||||||
/*
|
if (Util.Localisation.language === 'en') {
|
||||||
* Navigator.languages only available in Chrome (32+) and FireFox (32+)
|
if (callback !== undefined) {
|
||||||
* Fall back to navigator.language for other browsers
|
callback();
|
||||||
*/
|
}
|
||||||
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// First pass: perfect match
|
if (basedir[basedir.length - 1] !== '/') {
|
||||||
for (var j = 0;j < supportedLanguages.length;j++) {
|
basedir = basedir + '/';
|
||||||
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
|
require([basedir + Util.Localisation.language],
|
||||||
for (var j = 0;j < supportedLanguages.length;j++) {
|
function(lang) {
|
||||||
supLang = supportedLanguages[j];
|
Util.Localisation._translations = lang;
|
||||||
supLang = supLang.toLowerCase();
|
|
||||||
supLang = supLang.replace("_", "-");
|
|
||||||
supLang = supLang.split("-");
|
|
||||||
|
|
||||||
if (userLang[0] !== supLang[0])
|
if (callback !== undefined) {
|
||||||
continue;
|
callback();
|
||||||
if (supLang[1] !== undefined)
|
}
|
||||||
continue;
|
});
|
||||||
|
},
|
||||||
|
|
||||||
Util.Localisation.language = supportedLanguages[j];
|
// Internal function to figure out the proper language code
|
||||||
return;
|
_setLanguageCode: function (supportedLanguages) {
|
||||||
}
|
var userLanguages;
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Retrieve localised text
|
Util.Localisation.language = 'en'; // Default: US English
|
||||||
get: function (id) {
|
|
||||||
if (typeof Language !== 'undefined' && Language[id]) {
|
|
||||||
return Language[id];
|
|
||||||
} else {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Traverses the DOM and translates relevant fields
|
/*
|
||||||
// See https://html.spec.whatwg.org/multipage/dom.html#attr-translate
|
* Navigator.languages only available in Chrome (32+) and FireFox (32+)
|
||||||
translateDOM: function () {
|
* Fall back to navigator.language for other browsers
|
||||||
function process(elem, enabled) {
|
*/
|
||||||
function isAnyOf(searchElement, items) {
|
if (typeof window.navigator.languages == 'object') {
|
||||||
return items.indexOf(searchElement) !== -1;
|
userLanguages = window.navigator.languages;
|
||||||
|
} else {
|
||||||
|
userLanguages = [navigator.language || navigator.userLanguage];
|
||||||
}
|
}
|
||||||
|
|
||||||
function translateAttribute(elem, attr) {
|
for (var i = 0;i < userLanguages.length;i++) {
|
||||||
var str = elem.getAttribute(attr);
|
var userLang = userLanguages[i];
|
||||||
str = Util.Localisation.get(str);
|
userLang = userLang.toLowerCase();
|
||||||
elem.setAttribute(attr, str);
|
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 ((Util.Localisation._translations === null) ||
|
||||||
|
(Util.Localisation._translations[id] === undefined)) {
|
||||||
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
function translateTextNode(node) {
|
return Util.Localisation._translations[id];
|
||||||
var str = node.data.trim();
|
},
|
||||||
str = Util.Localisation.get(str);
|
|
||||||
node.data = str;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (elem.hasAttribute("translate")) {
|
// Traverses the DOM and translates relevant fields
|
||||||
if (isAnyOf(elem.getAttribute("translate"), ["", "yes"])) {
|
// See https://html.spec.whatwg.org/multipage/dom.html#attr-translate
|
||||||
enabled = true;
|
translateDOM: function () {
|
||||||
} else if (isAnyOf(elem.getAttribute("translate"), ["no"])) {
|
function process(elem, enabled) {
|
||||||
enabled = false;
|
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 in ["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++) {
|
||||||
|
var node = elem.childNodes[i];
|
||||||
|
if (node.nodeType === node.ELEMENT_NODE) {
|
||||||
|
process(node, enabled);
|
||||||
|
} else if (node.nodeType === node.TEXT_NODE && enabled) {
|
||||||
|
translateTextNode(node);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enabled) {
|
process(document.body, true);
|
||||||
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 in ["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++) {
|
return Util;
|
||||||
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);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/* [module] export default Util; */
|
|
||||||
|
|
|
||||||
|
|
@ -14,44 +14,39 @@
|
||||||
* read binary data off of the receive queue.
|
* read binary data off of the receive queue.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* [module]
|
|
||||||
* import Util from "./util";
|
|
||||||
* import Base64 from "./base64";
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*jslint browser: true, bitwise: true */
|
/*jslint browser: true, bitwise: true */
|
||||||
/*global Util*/
|
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
/* [module] export default */ function Websock() {
|
define(["core/util"],
|
||||||
|
function (Util) {
|
||||||
|
|
||||||
this._websocket = null; // WebSocket object
|
function Websock() {
|
||||||
|
this._websocket = null; // WebSocket object
|
||||||
|
|
||||||
this._rQi = 0; // Receive queue index
|
this._rQi = 0; // Receive queue index
|
||||||
this._rQlen = 0; // Next write position in the receive queue
|
this._rQlen = 0; // Next write position in the receive queue
|
||||||
this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
|
this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
|
||||||
this._rQmax = this._rQbufferSize / 8;
|
this._rQmax = this._rQbufferSize / 8;
|
||||||
// called in init: this._rQ = new Uint8Array(this._rQbufferSize);
|
// called in init: this._rQ = new Uint8Array(this._rQbufferSize);
|
||||||
this._rQ = null; // Receive queue
|
this._rQ = null; // Receive queue
|
||||||
|
|
||||||
this._sQbufferSize = 1024 * 10; // 10 KiB
|
this._sQbufferSize = 1024 * 10; // 10 KiB
|
||||||
// called in init: this._sQ = new Uint8Array(this._sQbufferSize);
|
// called in init: this._sQ = new Uint8Array(this._sQbufferSize);
|
||||||
this._sQlen = 0;
|
this._sQlen = 0;
|
||||||
this._sQ = null; // Send queue
|
this._sQ = null; // Send queue
|
||||||
|
|
||||||
this._mode = 'binary'; // Current WebSocket mode: 'binary', 'base64'
|
this._mode = 'binary'; // Current WebSocket mode: 'binary', 'base64'
|
||||||
this.maxBufferedAmount = 200;
|
this.maxBufferedAmount = 200;
|
||||||
|
|
||||||
this._eventHandlers = {
|
this._eventHandlers = {
|
||||||
'message': function () {},
|
'message': function () {},
|
||||||
'open': function () {},
|
'open': function () {},
|
||||||
'close': function () {},
|
'close': function () {},
|
||||||
'error': function () {}
|
'error': function () {}
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
|
||||||
|
|
||||||
(function () {
|
|
||||||
// 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.
|
||||||
|
|
@ -423,4 +418,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
|
||||||
|
return { Websock: Websock };
|
||||||
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
Rebuilding inflator.js
|
Rebuilding inflator.js
|
||||||
|
|
||||||
- Download pako from npm
|
- Download pako from npm
|
||||||
- Install browserify using npm
|
- Install requirejs using npm
|
||||||
- browserify core/inflator.mod.js -o core/inflator.js -s Inflator
|
- make -C inflator install
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
.PHONY: all install clean
|
||||||
|
|
||||||
|
all: inflator-merged.js
|
||||||
|
|
||||||
|
RJS := r.js
|
||||||
|
|
||||||
|
inflator-merged.js: build.js inflator.js pako
|
||||||
|
r.js -o build.js
|
||||||
|
|
||||||
|
pako: ../node_modules/pako
|
||||||
|
$(RJS) -convert $< pako/
|
||||||
|
|
||||||
|
install: all
|
||||||
|
rm -f ../core/inflator.js
|
||||||
|
cp inflator-merged.js ../core/inflator.js
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f inflator.js
|
||||||
|
rm -rf pako
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
({
|
||||||
|
baseUrl: ".",
|
||||||
|
name: "core/inflator",
|
||||||
|
paths: {
|
||||||
|
"core": ".",
|
||||||
|
},
|
||||||
|
out: "inflator-merged.js"
|
||||||
|
})
|
||||||
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* noVNC: HTML5 VNC client
|
||||||
|
* Copyright (C) 2012 Joel Martin
|
||||||
|
* Copyright (C) 2017 Pierre Ossman for Cendio AB
|
||||||
|
* Licensed under MPL 2.0 or any later version (see LICENSE.txt)
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*jslint browser: true, white: false */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
define(["pako/lib/zlib/inflate.js", "pako/lib/zlib/zstream.js"],
|
||||||
|
function (zlib, ZStream) {
|
||||||
|
|
||||||
|
function Inflate() {
|
||||||
|
this.strm = new ZStream();
|
||||||
|
this.chunkSize = 1024 * 10 * 10;
|
||||||
|
this.strm.output = new Uint8Array(this.chunkSize);
|
||||||
|
this.windowBits = 5;
|
||||||
|
|
||||||
|
zlib.inflateInit(this.strm, this.windowBits);
|
||||||
|
};
|
||||||
|
|
||||||
|
Inflate.prototype = {
|
||||||
|
inflate: function (data, flush, expected) {
|
||||||
|
this.strm.input = data;
|
||||||
|
this.strm.avail_in = this.strm.input.length;
|
||||||
|
this.strm.next_in = 0;
|
||||||
|
this.strm.next_out = 0;
|
||||||
|
|
||||||
|
// resize our output buffer if it's too small
|
||||||
|
// (we could just use multiple chunks, but that would cause an extra
|
||||||
|
// allocation each time to flatten the chunks)
|
||||||
|
if (expected > this.chunkSize) {
|
||||||
|
this.chunkSize = expected;
|
||||||
|
this.strm.output = new Uint8Array(this.chunkSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.strm.avail_out = this.chunkSize;
|
||||||
|
|
||||||
|
zlib.inflate(this.strm, flush);
|
||||||
|
|
||||||
|
return new Uint8Array(this.strm.output.buffer, 0, this.strm.next_out);
|
||||||
|
},
|
||||||
|
|
||||||
|
reset: function () {
|
||||||
|
zlib.inflateReset(this.strm);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return { Inflate: Inflate };
|
||||||
|
});
|
||||||
|
|
@ -108,34 +108,15 @@ module.exports = function(config) {
|
||||||
|
|
||||||
// list of files / patterns to load in the browser (loaded in order)
|
// list of files / patterns to load in the browser (loaded in order)
|
||||||
files: [
|
files: [
|
||||||
|
'node_modules/requirejs/require.js',
|
||||||
|
'tests/test-main.js',
|
||||||
'tests/fake.*.js',
|
'tests/fake.*.js',
|
||||||
'tests/assertions.js',
|
'tests/assertions.js',
|
||||||
'core/util.js', // load first to avoid issues, since methods are called immediately
|
'tests/test.*.js',
|
||||||
//'../core/*.js',
|
// Only packaged, not included in browser as RequireJS will
|
||||||
'core/base64.js',
|
// do the actual loading
|
||||||
'core/input/keysym.js',
|
{pattern: 'core/*.js', included: false},
|
||||||
'core/input/keysymdef.js',
|
{pattern: 'core/input/*.js', included: false},
|
||||||
'core/input/xtscancodes.js',
|
|
||||||
'core/input/util.js',
|
|
||||||
'core/input/devices.js',
|
|
||||||
'core/websock.js',
|
|
||||||
'core/rfb.js',
|
|
||||||
'core/des.js',
|
|
||||||
'core/display.js',
|
|
||||||
'core/inflator.js',
|
|
||||||
'tests/test.*.js'
|
|
||||||
],
|
|
||||||
|
|
||||||
client: {
|
|
||||||
mocha: {
|
|
||||||
'ui': 'bdd'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// list of files to exclude
|
|
||||||
exclude: [
|
|
||||||
'../tests/playback.js',
|
|
||||||
'../app/ui.js'
|
|
||||||
],
|
],
|
||||||
|
|
||||||
customLaunchers: customLaunchers,
|
customLaunchers: customLaunchers,
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@
|
||||||
"open": "^0.0.5",
|
"open": "^0.0.5",
|
||||||
"phantomjs-prebuilt": "^2.1.13",
|
"phantomjs-prebuilt": "^2.1.13",
|
||||||
"po2json": "*",
|
"po2json": "*",
|
||||||
|
"requirejs": "*",
|
||||||
"sinon": "^1.17.6",
|
"sinon": "^1.17.6",
|
||||||
"sinon-chai": "^2.8.0",
|
"sinon-chai": "^2.8.0",
|
||||||
"spooky": "^0.2.5",
|
"spooky": "^0.2.5",
|
||||||
|
|
|
||||||
6
po/po2js
6
po/po2js
|
|
@ -40,7 +40,10 @@ var output =
|
||||||
" * DO NOT EDIT!\n" +
|
" * DO NOT EDIT!\n" +
|
||||||
" */\n" +
|
" */\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"Language = {\n";
|
"\"use strict\";\n" +
|
||||||
|
"\n" +
|
||||||
|
"define(function() {\n" +
|
||||||
|
"return {\n";
|
||||||
|
|
||||||
for (msgid in data) {
|
for (msgid in data) {
|
||||||
if (msgid === "")
|
if (msgid === "")
|
||||||
|
|
@ -52,5 +55,6 @@ for (msgid in data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
output += "};\n";
|
output += "};\n";
|
||||||
|
output += "});\n";
|
||||||
|
|
||||||
fs.writeFileSync(opt.argv[1], output);
|
fs.writeFileSync(opt.argv[1], output);
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,8 @@ if (program.autoInject) {
|
||||||
template.header += "\n" + template.script_tag(get_path('node_modules/mocha/mocha.js'));
|
template.header += "\n" + template.script_tag(get_path('node_modules/mocha/mocha.js'));
|
||||||
template.header += "\n" + template.script_tag(get_path('node_modules/sinon/pkg/sinon.js'));
|
template.header += "\n" + template.script_tag(get_path('node_modules/sinon/pkg/sinon.js'));
|
||||||
template.header += "\n" + template.script_tag(get_path('node_modules/sinon-chai/lib/sinon-chai.js'));
|
template.header += "\n" + template.script_tag(get_path('node_modules/sinon-chai/lib/sinon-chai.js'));
|
||||||
|
template.header += "\n" + template.script_tag(get_path('node_modules/requirejs/require.js'));
|
||||||
|
template.header += "\n<script>requirejs.config({ baseUrl: \"" + get_path('.') + "\" });</script>";
|
||||||
template.header += "\n<script>mocha.setup('bdd');</script>";
|
template.header += "\n<script>mocha.setup('bdd');</script>";
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
requirejs.config({
|
||||||
|
// Karma serves files under /base
|
||||||
|
baseUrl: '/base'
|
||||||
|
});
|
||||||
|
|
||||||
|
|
@ -1,10 +1,19 @@
|
||||||
// requires local modules: base64
|
|
||||||
var assert = chai.assert;
|
var assert = chai.assert;
|
||||||
var expect = chai.expect;
|
var expect = chai.expect;
|
||||||
|
|
||||||
describe('Base64 Tools', function() {
|
describe('Base64 Tools', function(done) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var Base64;
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
requirejs(["core/base64"],
|
||||||
|
function (b) {
|
||||||
|
Base64 = b;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
var BIN_ARR = new Array(256);
|
var BIN_ARR = new Array(256);
|
||||||
for (var i = 0; i < 256; i++) {
|
for (var i = 0; i < 256; i++) {
|
||||||
BIN_ARR[i] = i;
|
BIN_ARR[i] = i;
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,22 @@
|
||||||
// requires local modules: util, base64, display
|
|
||||||
// requires test modules: assertions
|
// requires test modules: assertions
|
||||||
/* jshint expr: true */
|
/* jshint expr: true */
|
||||||
var expect = chai.expect;
|
var expect = chai.expect;
|
||||||
|
|
||||||
describe('Display/Canvas Helper', function () {
|
describe('Display/Canvas Helper', function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var Util, Base64, Display;
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
requirejs(["core/util", "core/base64", "core/display"],
|
||||||
|
function (u, b, d) {
|
||||||
|
Util = u;
|
||||||
|
Base64 = b;
|
||||||
|
Display = d.Display;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
var checked_data = [
|
var checked_data = [
|
||||||
0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
|
0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
|
||||||
0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
|
0x00, 0x00, 0xff, 255, 0x00, 0x00, 0xff, 255, 0x00, 0xff, 0x00, 255, 0x00, 0xff, 0x00, 255,
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,20 @@
|
||||||
// requires local modules: input/keysym, input/keysymdef, input/util
|
|
||||||
|
|
||||||
var assert = chai.assert;
|
var assert = chai.assert;
|
||||||
var expect = chai.expect;
|
var expect = chai.expect;
|
||||||
|
|
||||||
describe('Helpers', function() {
|
describe('Helpers', function() {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var keysyms, KeyboardUtil;
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
requirejs(["core/input/keysymdef", "core/input/util"],
|
||||||
|
function (k, u) {
|
||||||
|
keysyms = k;
|
||||||
|
KeyboardUtil = u;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('keysymFromKeyCode', function() {
|
describe('keysymFromKeyCode', function() {
|
||||||
it('should map known keycodes to keysyms', function() {
|
it('should map known keycodes to keysyms', function() {
|
||||||
expect(KeyboardUtil.keysymFromKeyCode(0x41, false), 'a').to.be.equal(0x61);
|
expect(KeyboardUtil.keysymFromKeyCode(0x41, false), 'a').to.be.equal(0x61);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,21 @@
|
||||||
// requires local modules: input/devices, input/util, input/keysymdef, input/keysym
|
|
||||||
var assert = chai.assert;
|
var assert = chai.assert;
|
||||||
var expect = chai.expect;
|
var expect = chai.expect;
|
||||||
|
|
||||||
/* jshint newcap: false, expr: true */
|
/* jshint newcap: false, expr: true */
|
||||||
describe('Key Event Pipeline Stages', function() {
|
describe('Key Event Pipeline Stages', function() {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var keysyms, KeyboardUtil;
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
requirejs(["core/input/keysymdef", "core/input/util"],
|
||||||
|
function (k, u) {
|
||||||
|
keysyms = k;
|
||||||
|
KeyboardUtil = u;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Decode Keyboard Events', function() {
|
describe('Decode Keyboard Events', function() {
|
||||||
it('should pass events to the next stage', function(done) {
|
it('should pass events to the next stage', function(done) {
|
||||||
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,10 @@
|
||||||
// requires local modules: util, websock, rfb, input/util, input/keysym, input/keysymdef, input/devices, inflator, des, display
|
|
||||||
// requires test modules: fake.websocket, assertions
|
// requires test modules: fake.websocket, assertions
|
||||||
/* jshint expr: true */
|
/* jshint expr: true */
|
||||||
var assert = chai.assert;
|
var assert = chai.assert;
|
||||||
var expect = chai.expect;
|
var expect = chai.expect;
|
||||||
|
|
||||||
|
var RFB;
|
||||||
|
|
||||||
function make_rfb (extra_opts) {
|
function make_rfb (extra_opts) {
|
||||||
if (!extra_opts) {
|
if (!extra_opts) {
|
||||||
extra_opts = {};
|
extra_opts = {};
|
||||||
|
|
@ -34,9 +35,21 @@ var push32 = function (arr, num) {
|
||||||
|
|
||||||
describe('Remote Frame Buffer Protocol Client', function() {
|
describe('Remote Frame Buffer Protocol Client', function() {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
before(FakeWebSocket.replace);
|
before(FakeWebSocket.replace);
|
||||||
after(FakeWebSocket.restore);
|
after(FakeWebSocket.restore);
|
||||||
|
|
||||||
|
var Websock;
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
requirejs(["core/rfb", "core/websock"],
|
||||||
|
function (r, w) {
|
||||||
|
RFB = r.RFB;
|
||||||
|
Websock = w.Websock;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
before(function () {
|
before(function () {
|
||||||
this.clock = sinon.useFakeTimers();
|
this.clock = sinon.useFakeTimers();
|
||||||
// Use a single set of buffers instead of reallocating to
|
// Use a single set of buffers instead of reallocating to
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
// requires local modules: util
|
|
||||||
/* jshint expr: true */
|
/* jshint expr: true */
|
||||||
|
|
||||||
var assert = chai.assert;
|
var assert = chai.assert;
|
||||||
|
|
@ -7,6 +6,16 @@ var expect = chai.expect;
|
||||||
describe('Utils', function() {
|
describe('Utils', function() {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var Util;
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
requirejs(["core/util"],
|
||||||
|
function (u) {
|
||||||
|
Util = u;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('logging functions', function () {
|
describe('logging functions', function () {
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
sinon.spy(console, 'log');
|
sinon.spy(console, 'log');
|
||||||
|
|
@ -57,7 +66,7 @@ describe('Utils', function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('language selection', function () {
|
describe('language selection', function () {
|
||||||
var origNavigator;
|
var origNavigator, origRequire;
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
// window.navigator is a protected read-only property in many
|
// window.navigator is a protected read-only property in many
|
||||||
// environments, so we need to redefine it whilst running these
|
// environments, so we need to redefine it whilst running these
|
||||||
|
|
@ -77,8 +86,13 @@ describe('Utils', function() {
|
||||||
}
|
}
|
||||||
|
|
||||||
window.navigator.languages = [];
|
window.navigator.languages = [];
|
||||||
|
|
||||||
|
// Prevent the code from trying to actually load translations
|
||||||
|
origRequire = require;
|
||||||
|
require = function (mods, func) { func([]); };
|
||||||
});
|
});
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
|
require = origRequire;
|
||||||
Object.defineProperty(window, "navigator", origNavigator);
|
Object.defineProperty(window, "navigator", origNavigator);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -87,37 +101,37 @@ describe('Utils', function() {
|
||||||
});
|
});
|
||||||
it('should use English if no user language matches', function() {
|
it('should use English if no user language matches', function() {
|
||||||
window.navigator.languages = ["nl", "de"];
|
window.navigator.languages = ["nl", "de"];
|
||||||
Util.Localisation.setup(["es", "fr"]);
|
Util.Localisation.setup(["es", "fr"], ".");
|
||||||
expect(Util.Localisation.language).to.equal('en');
|
expect(Util.Localisation.language).to.equal('en');
|
||||||
});
|
});
|
||||||
it('should use the most preferred user language', function() {
|
it('should use the most preferred user language', function() {
|
||||||
window.navigator.languages = ["nl", "de", "fr"];
|
window.navigator.languages = ["nl", "de", "fr"];
|
||||||
Util.Localisation.setup(["es", "fr", "de"]);
|
Util.Localisation.setup(["es", "fr", "de"], ".");
|
||||||
expect(Util.Localisation.language).to.equal('de');
|
expect(Util.Localisation.language).to.equal('de');
|
||||||
});
|
});
|
||||||
it('should prefer sub-languages languages', function() {
|
it('should prefer sub-languages languages', function() {
|
||||||
window.navigator.languages = ["pt-BR"];
|
window.navigator.languages = ["pt-BR"];
|
||||||
Util.Localisation.setup(["pt", "pt-BR"]);
|
Util.Localisation.setup(["pt", "pt-BR"], ".");
|
||||||
expect(Util.Localisation.language).to.equal('pt-BR');
|
expect(Util.Localisation.language).to.equal('pt-BR');
|
||||||
});
|
});
|
||||||
it('should fall back to language "parents"', function() {
|
it('should fall back to language "parents"', function() {
|
||||||
window.navigator.languages = ["pt-BR"];
|
window.navigator.languages = ["pt-BR"];
|
||||||
Util.Localisation.setup(["fr", "pt", "de"]);
|
Util.Localisation.setup(["fr", "pt", "de"], ".");
|
||||||
expect(Util.Localisation.language).to.equal('pt');
|
expect(Util.Localisation.language).to.equal('pt');
|
||||||
});
|
});
|
||||||
it('should not use specific language when user asks for a generic language', function() {
|
it('should not use specific language when user asks for a generic language', function() {
|
||||||
window.navigator.languages = ["pt", "de"];
|
window.navigator.languages = ["pt", "de"];
|
||||||
Util.Localisation.setup(["fr", "pt-BR", "de"]);
|
Util.Localisation.setup(["fr", "pt-BR", "de"], ".");
|
||||||
expect(Util.Localisation.language).to.equal('de');
|
expect(Util.Localisation.language).to.equal('de');
|
||||||
});
|
});
|
||||||
it('should handle underscore as a separator', function() {
|
it('should handle underscore as a separator', function() {
|
||||||
window.navigator.languages = ["pt-BR"];
|
window.navigator.languages = ["pt-BR"];
|
||||||
Util.Localisation.setup(["pt_BR"]);
|
Util.Localisation.setup(["pt_BR"], ".");
|
||||||
expect(Util.Localisation.language).to.equal('pt_BR');
|
expect(Util.Localisation.language).to.equal('pt_BR');
|
||||||
});
|
});
|
||||||
it('should handle difference in case', function() {
|
it('should handle difference in case', function() {
|
||||||
window.navigator.languages = ["pt-br"];
|
window.navigator.languages = ["pt-br"];
|
||||||
Util.Localisation.setup(["pt-BR"]);
|
Util.Localisation.setup(["pt-BR"], ".");
|
||||||
expect(Util.Localisation.language).to.equal('pt-BR');
|
expect(Util.Localisation.language).to.equal('pt-BR');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
// requires local modules: websock, util
|
|
||||||
// requires test modules: fake.websocket, assertions
|
// requires test modules: fake.websocket, assertions
|
||||||
/* jshint expr: true */
|
/* jshint expr: true */
|
||||||
var assert = chai.assert;
|
var assert = chai.assert;
|
||||||
|
|
@ -7,6 +6,16 @@ var expect = chai.expect;
|
||||||
describe('Websock', function() {
|
describe('Websock', function() {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
var Websock;
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
requirejs(["core/websock"],
|
||||||
|
function (w) {
|
||||||
|
Websock = w.Websock;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('Queue methods', function () {
|
describe('Queue methods', function () {
|
||||||
var sock;
|
var sock;
|
||||||
var RQ_TEMPLATE = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]);
|
var RQ_TEMPLATE = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7]);
|
||||||
|
|
|
||||||
|
|
@ -80,12 +80,15 @@ var out = "// This file describes mappings from Unicode codepoints to the keysym
|
||||||
"// (and optionally, key names) expected by the RFB protocol\n" +
|
"// (and optionally, key names) expected by the RFB protocol\n" +
|
||||||
"// How this file was generated:\n" +
|
"// How this file was generated:\n" +
|
||||||
"// " + process.argv.join(" ") + "\n" +
|
"// " + process.argv.join(" ") + "\n" +
|
||||||
"var keysyms = (function(){\n" +
|
"\n" +
|
||||||
" \"use strict\";\n" +
|
"\"use strict\";\n" +
|
||||||
|
"\n" +
|
||||||
|
"define(function () {\n" +
|
||||||
" var keynames = {keysyms};\n" +
|
" var keynames = {keysyms};\n" +
|
||||||
" var codepoints = {codepoints};\n" +
|
" var codepoints = {codepoints};\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
" function lookup(k) { return k ? {keysym: k, keyname: keynames ? keynames[k] : k} : undefined; }\n" +
|
" function lookup(k) { return k ? {keysym: k, keyname: keynames ? keynames[k] : k} : undefined; }\n" +
|
||||||
|
"\n" +
|
||||||
" return {\n" +
|
" return {\n" +
|
||||||
" fromUnicode : function(u) {\n" +
|
" fromUnicode : function(u) {\n" +
|
||||||
" var keysym = codepoints[u];\n" +
|
" var keysym = codepoints[u];\n" +
|
||||||
|
|
@ -96,7 +99,7 @@ var out = "// This file describes mappings from Unicode codepoints to the keysym
|
||||||
" },\n" +
|
" },\n" +
|
||||||
" lookup : lookup\n" +
|
" lookup : lookup\n" +
|
||||||
" };\n" +
|
" };\n" +
|
||||||
"})();\n";
|
"});\n";
|
||||||
out = out.replace('{keysyms}', use_keynames ? JSON.stringify(keysyms) : "null");
|
out = out.replace('{keysyms}', use_keynames ? JSON.stringify(keysyms) : "null");
|
||||||
out = out.replace('{codepoints}', JSON.stringify(codepoints));
|
out = out.replace('{codepoints}', JSON.stringify(codepoints));
|
||||||
|
|
||||||
|
|
|
||||||
12
vnc.html
12
vnc.html
|
|
@ -55,10 +55,7 @@
|
||||||
<!-- Stylesheets -->
|
<!-- Stylesheets -->
|
||||||
<link rel="stylesheet" href="app/styles/base.css" />
|
<link rel="stylesheet" href="app/styles/base.css" />
|
||||||
|
|
||||||
<!--
|
<script type='text/javascript' data-main='app/novnc.js' src='app/require.js'></script>
|
||||||
<script type='text/javascript'
|
|
||||||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
|
||||||
-->
|
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
@ -314,12 +311,5 @@
|
||||||
<source src="app/sounds/bell.oga" type="audio/ogg">
|
<source src="app/sounds/bell.oga" type="audio/ogg">
|
||||||
<source src="app/sounds/bell.mp3" type="audio/mpeg">
|
<source src="app/sounds/bell.mp3" type="audio/mpeg">
|
||||||
</audio>
|
</audio>
|
||||||
|
|
||||||
<!-- begin scripts -->
|
|
||||||
<script src="core/util.js"></script>
|
|
||||||
<script src="app/webutil.js"></script>
|
|
||||||
<script src="app/ui.js"></script>
|
|
||||||
<!-- end scripts -->
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
300
vnc_auto.html
300
vnc_auto.html
|
|
@ -38,12 +38,7 @@
|
||||||
<!-- Stylesheets -->
|
<!-- Stylesheets -->
|
||||||
<link rel="stylesheet" href="app/styles/auto.css">
|
<link rel="stylesheet" href="app/styles/auto.css">
|
||||||
|
|
||||||
<!--
|
<script src="app/require.js"></script>
|
||||||
<script type='text/javascript'
|
|
||||||
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
|
|
||||||
-->
|
|
||||||
<script src="core/util.js"></script>
|
|
||||||
<script src="app/webutil.js"></script>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body style="margin: 0px;">
|
<body style="margin: 0px;">
|
||||||
|
|
@ -74,148 +69,143 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
/*jslint white: false */
|
/*jslint white: false */
|
||||||
/*global window, $, Util, RFB, */
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// Load supporting scripts
|
require(["app/webutil", "core/rfb"],
|
||||||
WebUtil.load_scripts({
|
function(WebUtil, rfbmod) {
|
||||||
'core': ["base64.js", "websock.js", "des.js", "input/keysymdef.js",
|
|
||||||
"input/xtscancodes.js", "input/util.js", "input/devices.js",
|
|
||||||
"display.js", "inflator.js", "rfb.js", "input/keysym.js"]});
|
|
||||||
|
|
||||||
var rfb;
|
var rfb;
|
||||||
var resizeTimeout;
|
var resizeTimeout;
|
||||||
var desktopName;
|
var desktopName;
|
||||||
|
|
||||||
|
|
||||||
function UIresize() {
|
function UIresize() {
|
||||||
if (WebUtil.getConfigVar('resize', false)) {
|
if (WebUtil.getConfigVar('resize', false)) {
|
||||||
var innerW = window.innerWidth;
|
var innerW = window.innerWidth;
|
||||||
var innerH = window.innerHeight;
|
var innerH = window.innerHeight;
|
||||||
var controlbarH = document.getElementById('noVNC_status_bar').offsetHeight;
|
var controlbarH = document.getElementById('noVNC_status_bar').offsetHeight;
|
||||||
if (innerW !== undefined && innerH !== undefined)
|
if (innerW !== undefined && innerH !== undefined)
|
||||||
rfb.requestDesktopSize(innerW, innerH - controlbarH);
|
rfb.requestDesktopSize(innerW, innerH - controlbarH);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
function FBUComplete(rfb, fbu) {
|
||||||
function FBUComplete(rfb, fbu) {
|
|
||||||
UIresize();
|
|
||||||
rfb.set_onFBUComplete(function() { });
|
|
||||||
}
|
|
||||||
function updateDesktopName(rfb, name) {
|
|
||||||
desktopName = name;
|
|
||||||
}
|
|
||||||
function passwordRequired(rfb, msg) {
|
|
||||||
if (typeof msg === 'undefined') {
|
|
||||||
msg = 'Password Required: ';
|
|
||||||
}
|
|
||||||
var html;
|
|
||||||
html = '<form onsubmit="return setPassword();"';
|
|
||||||
html += ' style="margin-bottom: 0px">';
|
|
||||||
html += '<label></label>'
|
|
||||||
html += '<input type=password size=10 id="password_input" class="noVNC_status">';
|
|
||||||
html += '<\/form>';
|
|
||||||
|
|
||||||
// bypass status() because it sets text content
|
|
||||||
document.getElementById('noVNC_status_bar').setAttribute("class", "noVNC_status_warn");
|
|
||||||
document.getElementById('noVNC_status').innerHTML = html;
|
|
||||||
document.getElementById('noVNC_status').querySelector('label').textContent = msg;
|
|
||||||
}
|
|
||||||
function setPassword() {
|
|
||||||
rfb.sendPassword(document.getElementById('password_input').value);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
function sendCtrlAltDel() {
|
|
||||||
rfb.sendCtrlAltDel();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
function xvpShutdown() {
|
|
||||||
rfb.xvpShutdown();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
function xvpReboot() {
|
|
||||||
rfb.xvpReboot();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
function xvpReset() {
|
|
||||||
rfb.xvpReset();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
function status(text, level) {
|
|
||||||
switch (level) {
|
|
||||||
case 'normal':
|
|
||||||
case 'warn':
|
|
||||||
case 'error':
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
level = "warn";
|
|
||||||
}
|
|
||||||
document.getElementById('noVNC_status_bar').setAttribute("class", "noVNC_status_" + level);
|
|
||||||
document.getElementById('noVNC_status').textContent = text;
|
|
||||||
}
|
|
||||||
function updateState(rfb, state, oldstate) {
|
|
||||||
var cad = document.getElementById('sendCtrlAltDelButton');
|
|
||||||
switch (state) {
|
|
||||||
case 'connecting':
|
|
||||||
status("Connecting", "normal");
|
|
||||||
break;
|
|
||||||
case 'connected':
|
|
||||||
if (rfb && rfb.get_encrypt()) {
|
|
||||||
status("Connected (encrypted) to " +
|
|
||||||
desktopName, "normal");
|
|
||||||
} else {
|
|
||||||
status("Connected (unencrypted) to " +
|
|
||||||
desktopName, "normal");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'disconnecting':
|
|
||||||
status("Disconnecting", "normal");
|
|
||||||
break;
|
|
||||||
case 'disconnected':
|
|
||||||
status("Disconnected", "normal");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
status(state, "warn");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state === 'connected') {
|
|
||||||
cad.disabled = false;
|
|
||||||
} else {
|
|
||||||
cad.disabled = true;
|
|
||||||
xvpInit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
function disconnected(rfb, reason) {
|
|
||||||
if (typeof(reason) !== 'undefined') {
|
|
||||||
status(reason, "error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function notification(rfb, msg, level, options) {
|
|
||||||
status(msg, level);
|
|
||||||
}
|
|
||||||
|
|
||||||
window.onresize = function () {
|
|
||||||
// When the window has been resized, wait until the size remains
|
|
||||||
// the same for 0.5 seconds before sending the request for changing
|
|
||||||
// the resolution of the session
|
|
||||||
clearTimeout(resizeTimeout);
|
|
||||||
resizeTimeout = setTimeout(function(){
|
|
||||||
UIresize();
|
UIresize();
|
||||||
}, 500);
|
rfb.set_onFBUComplete(function() { });
|
||||||
};
|
}
|
||||||
|
function updateDesktopName(rfb, name) {
|
||||||
function xvpInit(ver) {
|
desktopName = name;
|
||||||
var xvpbuttons;
|
}
|
||||||
xvpbuttons = document.getElementById('noVNC_xvp_buttons');
|
function passwordRequired(rfb, msg) {
|
||||||
if (ver >= 1) {
|
if (typeof msg === 'undefined') {
|
||||||
xvpbuttons.style.display = 'inline';
|
msg = 'Password Required: ';
|
||||||
} else {
|
}
|
||||||
xvpbuttons.style.display = 'none';
|
var html;
|
||||||
|
html = '<form onsubmit="return setPassword();"';
|
||||||
|
html += ' style="margin-bottom: 0px">';
|
||||||
|
html += '<label></label>'
|
||||||
|
html += '<input type=password size=10 id="password_input" class="noVNC_status">';
|
||||||
|
html += '<\/form>';
|
||||||
|
|
||||||
|
// bypass status() because it sets text content
|
||||||
|
document.getElementById('noVNC_status_bar').setAttribute("class", "noVNC_status_warn");
|
||||||
|
document.getElementById('noVNC_status').innerHTML = html;
|
||||||
|
document.getElementById('noVNC_status').querySelector('label').textContent = msg;
|
||||||
|
}
|
||||||
|
function setPassword() {
|
||||||
|
rfb.sendPassword(document.getElementById('password_input').value);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function sendCtrlAltDel() {
|
||||||
|
rfb.sendCtrlAltDel();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function xvpShutdown() {
|
||||||
|
rfb.xvpShutdown();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function xvpReboot() {
|
||||||
|
rfb.xvpReboot();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function xvpReset() {
|
||||||
|
rfb.xvpReset();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
function status(text, level) {
|
||||||
|
switch (level) {
|
||||||
|
case 'normal':
|
||||||
|
case 'warn':
|
||||||
|
case 'error':
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
level = "warn";
|
||||||
|
}
|
||||||
|
document.getElementById('noVNC_status_bar').setAttribute("class", "noVNC_status_" + level);
|
||||||
|
document.getElementById('noVNC_status').textContent = text;
|
||||||
|
}
|
||||||
|
function updateState(rfb, state, oldstate) {
|
||||||
|
var cad = document.getElementById('sendCtrlAltDelButton');
|
||||||
|
switch (state) {
|
||||||
|
case 'connecting':
|
||||||
|
status("Connecting", "normal");
|
||||||
|
break;
|
||||||
|
case 'connected':
|
||||||
|
if (rfb && rfb.get_encrypt()) {
|
||||||
|
status("Connected (encrypted) to " +
|
||||||
|
desktopName, "normal");
|
||||||
|
} else {
|
||||||
|
status("Connected (unencrypted) to " +
|
||||||
|
desktopName, "normal");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'disconnecting':
|
||||||
|
status("Disconnecting", "normal");
|
||||||
|
break;
|
||||||
|
case 'disconnected':
|
||||||
|
status("Disconnected", "normal");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
status(state, "warn");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state === 'connected') {
|
||||||
|
cad.disabled = false;
|
||||||
|
} else {
|
||||||
|
cad.disabled = true;
|
||||||
|
xvpInit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
function disconnected(rfb, reason) {
|
||||||
|
if (typeof(reason) !== 'undefined') {
|
||||||
|
status(reason, "error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function notification(rfb, msg, level, options) {
|
||||||
|
status(msg, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
window.onresize = function () {
|
||||||
|
// When the window has been resized, wait until the size remains
|
||||||
|
// the same for 0.5 seconds before sending the request for changing
|
||||||
|
// the resolution of the session
|
||||||
|
clearTimeout(resizeTimeout);
|
||||||
|
resizeTimeout = setTimeout(function(){
|
||||||
|
UIresize();
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
|
||||||
|
function xvpInit(ver) {
|
||||||
|
var xvpbuttons;
|
||||||
|
xvpbuttons = document.getElementById('noVNC_xvp_buttons');
|
||||||
|
if (ver >= 1) {
|
||||||
|
xvpbuttons.style.display = 'inline';
|
||||||
|
} else {
|
||||||
|
xvpbuttons.style.display = 'none';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
window.onscriptsload = function () {
|
|
||||||
var host, port, password, path, token;
|
var host, port, password, path, token;
|
||||||
|
|
||||||
document.getElementById('sendCtrlAltDelButton').style.display = "inline";
|
document.getElementById('sendCtrlAltDelButton').style.display = "inline";
|
||||||
|
|
@ -261,28 +251,28 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
rfb = new RFB({'target': document.getElementById('noVNC_canvas'),
|
rfb = new rfbmod.RFB({'target': document.getElementById('noVNC_canvas'),
|
||||||
'encrypt': WebUtil.getConfigVar('encrypt',
|
'encrypt': WebUtil.getConfigVar('encrypt',
|
||||||
(window.location.protocol === "https:")),
|
(window.location.protocol === "https:")),
|
||||||
'repeaterID': WebUtil.getConfigVar('repeaterID', ''),
|
'repeaterID': WebUtil.getConfigVar('repeaterID', ''),
|
||||||
'true_color': WebUtil.getConfigVar('true_color', true),
|
'true_color': WebUtil.getConfigVar('true_color', true),
|
||||||
'local_cursor': WebUtil.getConfigVar('cursor', true),
|
'local_cursor': WebUtil.getConfigVar('cursor', true),
|
||||||
'shared': WebUtil.getConfigVar('shared', true),
|
'shared': WebUtil.getConfigVar('shared', true),
|
||||||
'view_only': WebUtil.getConfigVar('view_only', false),
|
'view_only': WebUtil.getConfigVar('view_only', false),
|
||||||
'onNotification': notification,
|
'onNotification': notification,
|
||||||
'onUpdateState': updateState,
|
'onUpdateState': updateState,
|
||||||
'onDisconnected': disconnected,
|
'onDisconnected': disconnected,
|
||||||
'onXvpInit': xvpInit,
|
'onXvpInit': xvpInit,
|
||||||
'onPasswordRequired': passwordRequired,
|
'onPasswordRequired': passwordRequired,
|
||||||
'onFBUComplete': FBUComplete,
|
'onFBUComplete': FBUComplete,
|
||||||
'onDesktopName': updateDesktopName});
|
'onDesktopName': updateDesktopName});
|
||||||
} catch (exc) {
|
} catch (exc) {
|
||||||
status('Unable to create RFB client -- ' + exc, 'error');
|
status('Unable to create RFB client -- ' + exc, 'error');
|
||||||
return; // don't continue trying to connect
|
return; // don't continue trying to connect
|
||||||
}
|
}
|
||||||
|
|
||||||
rfb.connect(host, port, password, path);
|
rfb.connect(host, port, password, path);
|
||||||
};
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue