This commit is contained in:
Pierre Ossman (Work account) 2017-01-23 16:09:16 +00:00 committed by GitHub
commit 13a085a794
42 changed files with 2727 additions and 5138 deletions

2
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

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

22
app/novnc.js Normal file
View File

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

5
app/require.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -2,28 +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] "use strict";
* 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";
*/
var UI; define(["app/webutil", "core/rfb", "core/util",
"core/input/keysym", "core/input/keysymdef"],
(function () { function (WebUtil, rfb, Util, KeyTable, keysyms) {
"use strict";
// Fallback for all uncought errors // Fallback for all uncought errors
window.addEventListener('error', function(event) { window.addEventListener('error', function(event) {
@ -65,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: "",
@ -397,17 +370,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;
@ -1732,7 +1705,5 @@ var UI;
*/ */
}; };
/* [module] UI.load(); */ return UI;
})(); });
/* [module] export default UI; */

View File

@ -2,404 +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] "use strict";
* import Util from "../core/util";
*/
// 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) {
"use strict";
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) {
"use strict";
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) {
"use strict";
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; }
"use strict"; if (! parent) { parent = ""; }
var re = new RegExp('.*[&#]' + name + '=([^&]*)'),
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) {
"use strict"; if ((depth > 1) && (typeof obj[i] === "object")) {
var val = WebUtil.getHashVar(name); // Recurse attributes that are objects
if (val === null) { msg += WebUtil.dirObj(obj[i], depth - 1, parent + "." + i);
val = WebUtil.getQueryVar(name, defVal); } else {
} //val = new String(obj[i]).replace("\n", " ");
return val; 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) {
"use strict"; var re = new RegExp('.*[&#]' + name + '=([^&]*)'),
var date, expires; match = document.location.hash.match(re);
if (days) { if (typeof defVal === 'undefined') { defVal = null; }
date = new Date(); if (match) {
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); return decodeURIComponent(match[1]);
expires = "; expires=" + date.toGMTString(); } else {
} else { return defVal;
expires = ""; }
} };
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) { /*
"use strict"; * Cookie handling. Dervied from: http://www.quirksmode.org/js/cookies.html
var nameEQ = name + "=", */
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;
"use strict"; if (document.location.protocol === "https:") {
WebUtil.createCookie(name, "", -1); 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) {
"use strict"; var c = ca[i];
var callbackArgs = Array.prototype.slice.call(arguments, 1); while (c.charAt(0) === ' ') { c = c.substring(1, c.length); }
if (window.chrome && window.chrome.storage) { if (c.indexOf(nameEQ) === 0) { return c.substring(nameEQ.length, c.length); }
window.chrome.storage.sync.get(function (cfg) { }
WebUtil.settings = cfg; return (typeof defaultValue !== 'undefined') ? defaultValue : null;
console.log(WebUtil.settings); };
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) {
"use strict";
if (window.chrome && window.chrome.storage) {
//console.log("writeSetting:", name, value);
if (WebUtil.settings[name] !== value) {
WebUtil.settings[name] = value;
window.chrome.storage.sync.set(WebUtil.settings);
}
} else {
localStorage.setItem(name, value);
}
};
WebUtil.readSetting = function (name, defaultValue) {
"use strict";
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) {
"use strict";
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) {
"use strict";
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;
});

View File

@ -5,111 +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 */
var Base64 = { "use strict";
/* Convert data (an array of integers) to a Base64 string. */
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
base64Pad : '=',
encode: function (data) { define(function () {
"use strict"; var Base64 = {
var result = ''; /* Convert data (an array of integers) to a Base64 string. */
var toBase64Table = Base64.toBase64Table; toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
var length = data.length; base64Pad : '=',
var lengthpad = (length % 3);
// Convert every three bytes to 4 ascii characters.
for (var i = 0; i < (length - 2); i += 3) { encode: function (data) {
result += toBase64Table[data[i] >> 2]; "use strict";
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)]; var result = '';
result += toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)]; var toBase64Table = Base64.toBase64Table;
result += toBase64Table[data[i + 2] & 0x3f]; var length = data.length;
} var lengthpad = (length % 3);
// Convert every three bytes to 4 ascii characters.
// Convert the remaining 1 or 2 bytes, pad out to 4 characters. for (var i = 0; i < (length - 2); i += 3) {
var j = 0; result += toBase64Table[data[i] >> 2];
if (lengthpad === 2) { result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
j = length - lengthpad; result += toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
result += toBase64Table[data[j] >> 2]; result += toBase64Table[data[i + 2] & 0x3f];
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; */

View File

@ -77,200 +77,204 @@
/* jslint white: false */ /* jslint white: false */
/* [module] export default */ function DES(passwd) { "use strict";
"use strict";
// Tables, permutations, S-boxes, etc. define(function () {
// jshint -W013 function DES(passwd) {
var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3, // Tables, permutations, S-boxes, etc.
25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39, // jshint -W013
50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ], var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,
totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28], 25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,
z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8, 50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],
keys = []; 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 = [];
// jshint -W015 // jshint -W015
a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e; a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e;
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, 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,
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, 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,
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, 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]; 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; 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, 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, 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|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]; 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; 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, 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, 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, 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]; 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; 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, 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, 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, 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]; 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; 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, 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, 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, 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]; 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; 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, 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, 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, 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|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; 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, 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|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, 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]; 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; 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, 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, 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, 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]; 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 +W013,+W015
// Set the key. // Set the key.
function setKeys(keyBlock) { function setKeys(keyBlock) {
var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [], var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [],
raw0, raw1, rawi, KnLi; raw0, raw1, rawi, KnLi;
for (j = 0, l = 56; j < 56; ++j, l -= 8) { for (j = 0, l = 56; j < 56; ++j, l -= 8) {
l += l < -5 ? 65 : l < -3 ? 31 : l < -1 ? 63 : l === 27 ? 35 : 0; // PC1 l += l < -5 ? 65 : l < -3 ? 31 : l < -1 ? 63 : l === 27 ? 35 : 0; // PC1
m = l & 0x7; m = l & 0x7;
pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0; pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
} }
for (i = 0; i < 16; ++i) { for (i = 0; i < 16; ++i) {
m = i << 1; m = i << 1;
n = m + 1; n = m + 1;
kn[m] = kn[n] = 0; kn[m] = kn[n] = 0;
for (o = 28; o < 59; o += 28) { for (o = 28; o < 59; o += 28) {
for (j = o - 28; j < o; ++j) { for (j = o - 28; j < o; ++j) {
l = j + totrot[i]; l = j + totrot[i];
if (l < o) { if (l < o) {
pcr[j] = pc1m[l]; pcr[j] = pc1m[l];
} else { } else {
pcr[j] = pc1m[l - 28]; pcr[j] = pc1m[l - 28];
}
}
}
for (j = 0; j < 24; ++j) {
if (pcr[PC2[j]] !== 0) {
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;
});

View File

@ -2,105 +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] "use strict";
* import Util from "./util";
* import Base64 from "./base64";
*/
/* [module] export default */ 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");
};
(function () {
"use strict";
define(["core/util", "core/base64"],
function (Util, Base64) {
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);
@ -109,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
@ -875,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 };
});

File diff suppressed because one or more lines are too long

View File

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

View File

@ -2,27 +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] "use strict";
* import Util from "../util";
* import KeyboardUtil from "./util";
*/
/* [module] export */ var Keyboard;
(function () {
"use strict";
define(["core/util", "core/input/util"],
function (Util, KeyboardUtil) {
// //
// 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)
@ -158,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;
@ -402,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 };
});

View File

@ -1,382 +1,386 @@
var KeyTable = { "use strict";
XK_VoidSymbol: 0xffffff, /* Void symbol */
XK_BackSpace: 0xff08, /* Back space, back char */ define(function () {
XK_Tab: 0xff09, var KeyTable = {
XK_Linefeed: 0xff0a, /* Linefeed, LF */ XK_VoidSymbol: 0xffffff, /* Void symbol */
XK_Clear: 0xff0b,
XK_Return: 0xff0d, /* Return, enter */
XK_Pause: 0xff13, /* Pause, hold */
XK_Scroll_Lock: 0xff14,
XK_Sys_Req: 0xff15,
XK_Escape: 0xff1b,
XK_Delete: 0xffff, /* Delete, rubout */
/* Cursor control & motion */ XK_BackSpace: 0xff08, /* Back space, back char */
XK_Tab: 0xff09,
XK_Linefeed: 0xff0a, /* Linefeed, LF */
XK_Clear: 0xff0b,
XK_Return: 0xff0d, /* Return, enter */
XK_Pause: 0xff13, /* Pause, hold */
XK_Scroll_Lock: 0xff14,
XK_Sys_Req: 0xff15,
XK_Escape: 0xff1b,
XK_Delete: 0xffff, /* Delete, rubout */
XK_Home: 0xff50, /* Cursor control & motion */
XK_Left: 0xff51, /* Move left, left arrow */
XK_Up: 0xff52, /* Move up, up arrow */ XK_Home: 0xff50,
XK_Right: 0xff53, /* Move right, right arrow */ XK_Left: 0xff51, /* Move left, left arrow */
XK_Down: 0xff54, /* Move down, down arrow */ XK_Up: 0xff52, /* Move up, up arrow */
XK_Prior: 0xff55, /* Prior, previous */ XK_Right: 0xff53, /* Move right, right arrow */
XK_Page_Up: 0xff55, XK_Down: 0xff54, /* Move down, down arrow */
XK_Next: 0xff56, /* Next */ XK_Prior: 0xff55, /* Prior, previous */
XK_Page_Down: 0xff56, XK_Page_Up: 0xff55,
XK_End: 0xff57, /* EOL */ XK_Next: 0xff56, /* Next */
XK_Begin: 0xff58, /* BOL */ XK_Page_Down: 0xff56,
XK_End: 0xff57, /* EOL */
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

View File

@ -1,12 +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)
*/ */
var KeyboardUtil = {}; /*jslint browser: true, white: false */
(function() { "use strict";
"use strict";
define(["core/input/keysym", "core/input/keysymdef"],
function (KeyTable, keysyms) {
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
@ -286,394 +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) {
"use strict"; function sendAll(evts) {
for (var i = 0; i < evts.length; ++i) {
function sendAll(evts) { next(evts[i]);
for (var i = 0; i < evts.length; ++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) {
"use strict";
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) {
"use strict";
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) {
"use strict";
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
"use strict"; KeyboardUtil.VerifyCharModifier = function(next) {
var state = []; var queue = [];
var timer = null;
return function (evt) { function process() {
var last = state.length !== 0 ? state[state.length-1] : null; 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
"use strict"; // key repeat events should be merged into a single entry.
return function(evt) { // Because we can't always identify which entry a keydown or keyup event corresponds to, we sometimes have to guess
if (evt.type !== 'keydown' || evt.escape === undefined) { 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;
});

View File

@ -1,151 +1,155 @@
var XtScancode = { "use strict";
"Escape": 0x0001,
"Digit1": 0x0002,
"Digit2": 0x0003,
"Digit3": 0x0004,
"Digit4": 0x0005,
"Digit5": 0x0006,
"Digit6": 0x0007,
"Digit7": 0x0008,
"Digit8": 0x0009,
"Digit9": 0x000A,
"Digit0": 0x000B,
"Minus": 0x000C,
"Equal": 0x000D,
"Backspace": 0x000E,
"Tab": 0x000F,
"KeyQ": 0x0010,
"KeyW": 0x0011,
"KeyE": 0x0012,
"KeyR": 0x0013,
"KeyT": 0x0014,
"KeyY": 0x0015,
"KeyU": 0x0016,
"KeyI": 0x0017,
"KeyO": 0x0018,
"KeyP": 0x0019,
"BracketLeft": 0x001A,
"BracketRight": 0x001B,
"Enter": 0x001C,
"ControlLeft": 0x001D,
"KeyA": 0x001E,
"KeyS": 0x001F,
"KeyD": 0x0020,
"KeyF": 0x0021,
"KeyG": 0x0022,
"KeyH": 0x0023,
"KeyJ": 0x0024,
"KeyK": 0x0025,
"KeyL": 0x0026,
"Semicolon": 0x0027,
"Quote": 0x0028,
"Backquote": 0x0029,
"ShiftLeft": 0x002A,
"Backslash": 0x002B,
"KeyZ": 0x002C,
"KeyX": 0x002D,
"KeyC": 0x002E,
"KeyV": 0x002F,
"KeyB": 0x0030,
"KeyN": 0x0031,
"KeyM": 0x0032,
"Comma": 0x0033,
"Period": 0x0034,
"Slash": 0x0035,
"ShiftRight": 0x0036,
"NumpadMultiply": 0x0037,
"AltLeft": 0x0038,
"Space": 0x0039,
"CapsLock": 0x003A,
"F1": 0x003B,
"F2": 0x003C,
"F3": 0x003D,
"F4": 0x003E,
"F5": 0x003F,
"F6": 0x0040,
"F7": 0x0041,
"F8": 0x0042,
"F9": 0x0043,
"F10": 0x0044,
"Pause": 0xE045,
"ScrollLock": 0x0046,
"Numpad7": 0x0047,
"Numpad8": 0x0048,
"Numpad9": 0x0049,
"NumpadSubtract": 0x004A,
"Numpad4": 0x004B,
"Numpad5": 0x004C,
"Numpad6": 0x004D,
"NumpadAdd": 0x004E,
"Numpad1": 0x004F,
"Numpad2": 0x0050,
"Numpad3": 0x0051,
"Numpad0": 0x0052,
"NumpadDecimal": 0x0053,
"IntlBackslash": 0x0056,
"F11": 0x0057,
"F12": 0x0058,
"IntlYen": 0x007D,
"MediaTrackPrevious": 0xE010,
"MediaTrackNext": 0xE019,
"NumpadEnter": 0xE01C,
"ControlRight": 0xE01D,
"VolumeMute": 0xE020,
"MediaPlayPause": 0xE022,
"MediaStop": 0xE024,
"VolumeDown": 0xE02E,
"VolumeUp": 0xE030,
"BrowserHome": 0xE032,
"NumpadDivide": 0xE035,
"PrintScreen": 0xE037,
"AltRight": 0xE038,
"NumLock": 0x0045,
"Home": 0xE047,
"ArrowUp": 0xE048,
"PageUp": 0xE049,
"ArrowLeft": 0xE04B,
"ArrowRight": 0xE04D,
"End": 0xE04F,
"ArrowDown": 0xE050,
"PageDown": 0xE051,
"Insert": 0xE052,
"Delete": 0xE053,
"MetaLeft": 0xE05B,
"MetaRight": 0xE05C,
"OSLeft": 0xE05B, // OSLeft and OSRight are kept for compatability since
"OSRight": 0xE05C, // Firefox haven't updated to MetaLeft and MetaRight yet
"ContextMenu": 0xE05D,
"BrowserSearch": 0xE065,
"BrowserFavorites": 0xE066,
"BrowserRefresh": 0xE067,
"BrowserStop": 0xE068,
"BrowserForward": 0xE069,
"BrowserBack": 0xE06A,
"NumpadComma": 0x007E,
"NumpadEqual": 0x0059,
"F13": 0x0064,
"F14": 0x0065,
"F15": 0x0066,
"F16": 0x0067,
"F17": 0x0068,
"F18": 0x0069,
"F19": 0x006A,
"F20": 0x006B,
"F21": 0x006C,
"F22": 0x006D,
"F23": 0x006E,
"F24": 0x0076,
"KanaMode": 0x0070,
"Lang2": 0x0071,
"Lang1": 0x0072,
"IntlRo": 0x0073,
"Convert": 0x0079,
"NonConvert": 0x007B,
"LaunchApp2": 0xE021,
"Power": 0xE05E,
"LaunchApp1": 0xE06B,
"LaunchMail": 0xE06C,
"MediaSelect": 0xE06D,
};
/* [module] export default XtScancode */ define(function () {
var XtScancode = {
"Escape": 0x0001,
"Digit1": 0x0002,
"Digit2": 0x0003,
"Digit3": 0x0004,
"Digit4": 0x0005,
"Digit5": 0x0006,
"Digit6": 0x0007,
"Digit7": 0x0008,
"Digit8": 0x0009,
"Digit9": 0x000A,
"Digit0": 0x000B,
"Minus": 0x000C,
"Equal": 0x000D,
"Backspace": 0x000E,
"Tab": 0x000F,
"KeyQ": 0x0010,
"KeyW": 0x0011,
"KeyE": 0x0012,
"KeyR": 0x0013,
"KeyT": 0x0014,
"KeyY": 0x0015,
"KeyU": 0x0016,
"KeyI": 0x0017,
"KeyO": 0x0018,
"KeyP": 0x0019,
"BracketLeft": 0x001A,
"BracketRight": 0x001B,
"Enter": 0x001C,
"ControlLeft": 0x001D,
"KeyA": 0x001E,
"KeyS": 0x001F,
"KeyD": 0x0020,
"KeyF": 0x0021,
"KeyG": 0x0022,
"KeyH": 0x0023,
"KeyJ": 0x0024,
"KeyK": 0x0025,
"KeyL": 0x0026,
"Semicolon": 0x0027,
"Quote": 0x0028,
"Backquote": 0x0029,
"ShiftLeft": 0x002A,
"Backslash": 0x002B,
"KeyZ": 0x002C,
"KeyX": 0x002D,
"KeyC": 0x002E,
"KeyV": 0x002F,
"KeyB": 0x0030,
"KeyN": 0x0031,
"KeyM": 0x0032,
"Comma": 0x0033,
"Period": 0x0034,
"Slash": 0x0035,
"ShiftRight": 0x0036,
"NumpadMultiply": 0x0037,
"AltLeft": 0x0038,
"Space": 0x0039,
"CapsLock": 0x003A,
"F1": 0x003B,
"F2": 0x003C,
"F3": 0x003D,
"F4": 0x003E,
"F5": 0x003F,
"F6": 0x0040,
"F7": 0x0041,
"F8": 0x0042,
"F9": 0x0043,
"F10": 0x0044,
"Pause": 0xE045,
"ScrollLock": 0x0046,
"Numpad7": 0x0047,
"Numpad8": 0x0048,
"Numpad9": 0x0049,
"NumpadSubtract": 0x004A,
"Numpad4": 0x004B,
"Numpad5": 0x004C,
"Numpad6": 0x004D,
"NumpadAdd": 0x004E,
"Numpad1": 0x004F,
"Numpad2": 0x0050,
"Numpad3": 0x0051,
"Numpad0": 0x0052,
"NumpadDecimal": 0x0053,
"IntlBackslash": 0x0056,
"F11": 0x0057,
"F12": 0x0058,
"IntlYen": 0x007D,
"MediaTrackPrevious": 0xE010,
"MediaTrackNext": 0xE019,
"NumpadEnter": 0xE01C,
"ControlRight": 0xE01D,
"VolumeMute": 0xE020,
"MediaPlayPause": 0xE022,
"MediaStop": 0xE024,
"VolumeDown": 0xE02E,
"VolumeUp": 0xE030,
"BrowserHome": 0xE032,
"NumpadDivide": 0xE035,
"PrintScreen": 0xE037,
"AltRight": 0xE038,
"NumLock": 0x0045,
"Home": 0xE047,
"ArrowUp": 0xE048,
"PageUp": 0xE049,
"ArrowLeft": 0xE04B,
"ArrowRight": 0xE04D,
"End": 0xE04F,
"ArrowDown": 0xE050,
"PageDown": 0xE051,
"Insert": 0xE052,
"Delete": 0xE053,
"MetaLeft": 0xE05B,
"MetaRight": 0xE05C,
"OSLeft": 0xE05B, // OSLeft and OSRight are kept for compatability since
"OSRight": 0xE05C, // Firefox haven't updated to MetaLeft and MetaRight yet
"ContextMenu": 0xE05D,
"BrowserSearch": 0xE065,
"BrowserFavorites": 0xE066,
"BrowserRefresh": 0xE067,
"BrowserStop": 0xE068,
"BrowserForward": 0xE069,
"BrowserBack": 0xE06A,
"NumpadComma": 0x007E,
"NumpadEqual": 0x0059,
"F13": 0x0064,
"F14": 0x0065,
"F15": 0x0066,
"F16": 0x0067,
"F17": 0x0068,
"F18": 0x0069,
"F19": 0x006A,
"F20": 0x006B,
"F21": 0x006C,
"F22": 0x006D,
"F23": 0x006E,
"F24": 0x0076,
"KanaMode": 0x0070,
"Lang2": 0x0071,
"Lang1": 0x0072,
"IntlRo": 0x0073,
"Convert": 0x0079,
"NonConvert": 0x007B,
"LaunchApp2": 0xE021,
"Power": 0xE05E,
"LaunchApp1": 0xE06B,
"LaunchMail": 0xE06C,
"MediaSelect": 0xE06D,
};
return XtScancode;
});

View File

@ -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,261 +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 */
/* [module] export default */ function RFB(defaults) { "use strict";
"use strict";
if (!defaults) {
defaults = {};
}
this._rfb_host = ''; define(["core/util", "core/display", "core/websock",
this._rfb_port = 5900; "core/inflator", "core/des",
this._rfb_password = ''; "core/input/devices", "core/input/keysym",
this._rfb_path = ''; "core/input/xtscancodes"],
function (Util, display, websock, Inflator, DES, devices,
this._rfb_connection_state = ''; KeyTable, XtScancode) {
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) {
@ -2442,4 +2437,6 @@
Util.Error("Server sent compress level pseudo-encoding"); Util.Error("Server sent compress level pseudo-encoding");
} }
}; };
})();
return { RFB: RFB };
});

View File

@ -1,287 +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 */
var Util = {}; "use strict";
/* define(function() {
* ------------------------------------------------------ var Util = {};
* Namespaced in Util
* ------------------------------------------------------
*/
/* /*
* Logging/debug routines * Logging/debug routines
*/ */
Util._log_level = 'warn'; var _log_level = 'warn';
Util.init_logging = function (level) { Util.init_logging = function (level) {
"use strict"; if (typeof level === 'undefined') {
if (typeof level === 'undefined') { level = _log_level;
level = Util._log_level; } else {
} else { _log_level = level;
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) {};
"use strict"; 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) { /*
"use strict"; * Decode from UTF-8
for (var i = 0; i < arr.length; i++) { */
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) { };
"use strict";
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) {
"use strict"; Util.Info("Data URI scheme cursor supported");
// NB(sross): the Mozilla developer reference seems to indicate that _cursor_uris_supported = true;
// getBoundingClientRect includes border and padding, so the canvas } else {
// style should NOT include either. Util.Warn("Data URI scheme cursor not supported");
var objPosition = obj.getBoundingClientRect(); _cursor_uris_supported = false;
return {'x': objPosition.left + window.pageXOffset, 'y': objPosition.top + window.pageYOffset, }
'width': objPosition.width, 'height': objPosition.height}; } catch (exc) {
}; Util.Error("Data URI scheme cursor test exception: " + exc);
_cursor_uris_supported = false;
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) {
"use strict";
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 () {
"use strict";
// '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;
@ -348,173 +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 () {
"use strict"; 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; */

View File

@ -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*/
/* [module] export default */ function Websock() { "use strict";
"use strict";
this._websocket = null; // WebSocket object define(["core/util"],
function (Util) {
this._rQi = 0; // Receive queue index function Websock() {
this._rQlen = 0; // Next write position in the receive queue this._websocket = null; // WebSocket object
this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
this._rQmax = this._rQbufferSize / 8;
// called in init: this._rQ = new Uint8Array(this._rQbufferSize);
this._rQ = null; // Receive queue
this._sQbufferSize = 1024 * 10; // 10 KiB this._rQi = 0; // Receive queue index
// called in init: this._sQ = new Uint8Array(this._sQbufferSize); this._rQlen = 0; // Next write position in the receive queue
this._sQlen = 0; this._rQbufferSize = 1024 * 1024 * 4; // Receive queue buffer size (4 MiB)
this._sQ = null; // Send queue this._rQmax = this._rQbufferSize / 8;
// called in init: this._rQ = new Uint8Array(this._rQbufferSize);
this._rQ = null; // Receive queue
this._mode = 'binary'; // Current WebSocket mode: 'binary', 'base64' this._sQbufferSize = 1024 * 10; // 10 KiB
this.maxBufferedAmount = 200; // called in init: this._sQ = new Uint8Array(this._sQbufferSize);
this._sQlen = 0;
this._sQ = null; // Send queue
this._eventHandlers = { this._mode = 'binary'; // Current WebSocket mode: 'binary', 'base64'
'message': function () {}, this.maxBufferedAmount = 200;
'open': function () {},
'close': function () {}, this._eventHandlers = {
'error': function () {} 'message': function () {},
'open': function () {},
'close': function () {},
'error': function () {}
};
}; };
};
(function () {
"use strict";
// this has performance issues in some versions Chromium, and // this has performance issues in some versions Chromium, and
// doesn't gain a tremendous amount of performance increase in Firefox // doesn't gain a tremendous amount of performance increase in Firefox
// at the moment. It may be valuable to turn it on in the future. // at the moment. It may be valuable to turn it on in the future.
@ -423,4 +418,6 @@
} }
} }
}; };
})();
return { Websock: Websock };
});

View File

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

19
inflator/Makefile Normal file
View File

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

8
inflator/build.js Normal file
View File

@ -0,0 +1,8 @@
({
baseUrl: ".",
name: "core/inflator",
paths: {
"core": ".",
},
out: "inflator-merged.js"
})

52
inflator/inflator.js Normal file
View File

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

View File

@ -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: [
'tests/fake.*.js', 'node_modules/requirejs/require.js',
'tests/test-main.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: 'tests/fake.*.js', included: false},
'core/input/keysymdef.js', {pattern: 'core/*.js', included: false},
'core/input/xtscancodes.js', {pattern: 'core/input/*.js', included: false},
'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,

View File

@ -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",

View File

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

View File

@ -1,6 +1,6 @@
var FakeWebSocket; "use strict";
(function () { define(function () {
// PhantomJS can't create Event objects directly, so we need to use this // PhantomJS can't create Event objects directly, so we need to use this
function make_event(name, props) { function make_event(name, props) {
var evt = document.createEvent('Event'); var evt = document.createEvent('Event');
@ -13,6 +13,8 @@ var FakeWebSocket;
return evt; return evt;
} }
var FakeWebSocket;
FakeWebSocket = function (uri, protocols) { FakeWebSocket = function (uri, protocols) {
this.url = uri; this.url = uri;
this.binaryType = "arraybuffer"; this.binaryType = "arraybuffer";
@ -75,7 +77,7 @@ var FakeWebSocket;
FakeWebSocket.__is_fake = true; FakeWebSocket.__is_fake = true;
FakeWebSocket.replace = function () { function replace () {
if (!WebSocket.__is_fake) { if (!WebSocket.__is_fake) {
var real_version = WebSocket; var real_version = WebSocket;
WebSocket = FakeWebSocket; WebSocket = FakeWebSocket;
@ -83,9 +85,11 @@ var FakeWebSocket;
} }
}; };
FakeWebSocket.restore = function () { function restore () {
if (WebSocket.__is_fake) { if (WebSocket.__is_fake) {
WebSocket = WebSocket.__real_version; WebSocket = WebSocket.__real_version;
} }
}; };
})();
return { replace: replace, restore: restore };
});

View File

@ -54,77 +54,43 @@ var get_path_cwd = function (/* arguments */) {
} }
}; };
if (all_js && !program.autoInject) { var temp = require('temp');
var all_modules = {}; temp.track();
// uses the first instance of the string 'requires local modules: ' var template = {
program.tests.forEach(function (testname) { header: "<html>\n<head>\n<meta charset='utf-8' />\n<link rel='stylesheet' href='" + get_path('node_modules/mocha/mocha.css') + "'/>\n</head>\n<body><div id='mocha'></div>",
var full_path = path.resolve(process.cwd(), testname); script_tag: function(p) { return "<script src='" + p + "'></script>"; },
var content = fs.readFileSync(full_path).toString(); footer: "<script>\nmocha.checkLeaks();\nmocha.globals(['navigator', 'create', 'ClientUtils', '__utils__', 'requestAnimationFrame', 'WebSocket']);\nmocha.run(function () { window.__mocha_done = true; });\n</script>\n</body>\n</html>"
var ind = content.indexOf('requires local modules: '); };
if (ind > -1) {
ind += 'requires local modules: '.length;
var eol = content.indexOf('\n', ind);
var modules = content.slice(ind, eol).split(/,\s*/);
modules.forEach(function (mod) {
all_modules[get_path('core/', mod) + '.js'] = 1;
});
}
var fakes_ind = content.indexOf('requires test modules: '); template.header += "\n" + template.script_tag(get_path('node_modules/chai/chai.js'));
if (fakes_ind > -1) { template.header += "\n" + template.script_tag(get_path('node_modules/mocha/mocha.js'));
fakes_ind += 'requires test modules: '.length; template.header += "\n" + template.script_tag(get_path('node_modules/sinon/pkg/sinon.js'));
var fakes_eol = content.indexOf('\n', fakes_ind); template.header += "\n" + template.script_tag(get_path('node_modules/sinon-chai/lib/sinon-chai.js'));
var fakes_modules = content.slice(fakes_ind, fakes_eol).split(/,\s*/); template.header += "\n" + template.script_tag(get_path('node_modules/requirejs/require.js'));
fakes_modules.forEach(function (mod) { template.header += "\n<script>requirejs.config({ baseUrl: \"" + get_path('.') + "\" });</script>";
all_modules[get_path('tests/', mod) + '.js'] = 1; template.header += "\n<script>mocha.setup('bdd');</script>";
});
}
});
program.autoInject = Object.keys(all_modules); template.header += "\n" + template.script_tag(get_path('tests/assertions.js'));
}
if (program.autoInject) { if (program.autoInject) {
var temp = require('temp');
temp.track();
var template = {
header: "<html>\n<head>\n<meta charset='utf-8' />\n<link rel='stylesheet' href='" + get_path('node_modules/mocha/mocha.css') + "'/>\n</head>\n<body><div id='mocha'></div>",
script_tag: function(p) { return "<script src='" + p + "'></script>"; },
footer: "<script>\nmocha.checkLeaks();\nmocha.globals(['navigator', 'create', 'ClientUtils', '__utils__', 'requestAnimationFrame', 'WebSocket']);\nmocha.run(function () { window.__mocha_done = true; });\n</script>\n</body>\n</html>"
};
template.header += "\n" + template.script_tag(get_path('node_modules/chai/chai.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-chai/lib/sinon-chai.js'));
template.header += "\n<script>mocha.setup('bdd');</script>";
template.header = program.autoInject.reduce(function(acc, sn) { template.header = program.autoInject.reduce(function(acc, sn) {
return acc + "\n" + template.script_tag(get_path_cwd(sn)); return acc + "\n" + template.script_tag(get_path_cwd(sn));
}, template.header); }, template.header);
file_paths = program.tests.map(function(jsn, ind) {
var templ = template.header;
templ += "\n";
templ += template.script_tag(get_path_cwd(jsn));
templ += template.footer;
var tempfile = temp.openSync({ prefix: 'novnc-zombie-inject-', suffix: '-file_num-'+ind+'.html' });
fs.writeSync(tempfile.fd, templ);
fs.closeSync(tempfile.fd);
return tempfile.path;
});
}
else {
file_paths = program.tests.map(function(fn) {
return path.resolve(process.cwd(), fn);
});
} }
file_paths = program.tests.map(function(jsn, ind) {
var templ = template.header;
templ += "\n";
templ += template.script_tag(get_path_cwd(jsn));
templ += template.footer;
var tempfile = temp.openSync({ prefix: 'novnc-zombie-inject-', suffix: '-file_num-'+ind+'.html' });
fs.writeSync(tempfile.fd, templ);
fs.closeSync(tempfile.fd);
return tempfile.path;
});
var use_ansi = false; var use_ansi = false;
if (program.color) use_ansi = true; if (program.color) use_ansi = true;
else if (program.disableColor) use_ansi = false; else if (program.disableColor) use_ansi = false;

5
tests/test-main.js Normal file
View File

@ -0,0 +1,5 @@
requirejs.config({
// Karma serves files under /base
baseUrl: '/base'
});

View File

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

View File

@ -1,9 +1,21 @@
// requires local modules: util, base64, display
// 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,

View File

@ -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);
@ -101,8 +111,13 @@ describe('Helpers', function() {
}); });
describe('Modifier Sync', function() { // return a list of fake events necessary to fix modifier state describe('Modifier Sync', function() { // return a list of fake events necessary to fix modifier state
var sync;
beforeEach(function() {
sync = KeyboardUtil.ModifierSync();
});
describe('Toggle all modifiers', function() { describe('Toggle all modifiers', function() {
var sync = KeyboardUtil.ModifierSync();
it ('should do nothing if all modifiers are up as expected', function() { it ('should do nothing if all modifiers are up as expected', function() {
expect(sync.keydown({ expect(sync.keydown({
keyCode: 0x41, keyCode: 0x41,
@ -134,6 +149,14 @@ describe('Helpers', function() {
expect(keysyms[0xffe7]); expect(keysyms[0xffe7]);
}); });
it ('should do nothing if all modifiers are down as expected', function() { it ('should do nothing if all modifiers are down as expected', function() {
sync.keydown({
keyCode: 0x41,
ctrlKey: true,
altKey: true,
altGraphKey: true,
shiftKey: true,
metaKey: true
});
expect(sync.keydown({ expect(sync.keydown({
keyCode: 0x41, keyCode: 0x41,
ctrlKey: true, ctrlKey: true,
@ -145,7 +168,6 @@ describe('Helpers', function() {
}); });
}); });
describe('Toggle Ctrl', function() { describe('Toggle Ctrl', function() {
var sync = KeyboardUtil.ModifierSync();
it('should sync if modifier is suddenly down', function() { it('should sync if modifier is suddenly down', function() {
expect(sync.keydown({ expect(sync.keydown({
keyCode: 0x41, keyCode: 0x41,
@ -153,6 +175,10 @@ describe('Helpers', function() {
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe3), type: 'keydown'}]); })).to.be.deep.equal([{keysym: keysyms.lookup(0xffe3), type: 'keydown'}]);
}); });
it('should sync if modifier is suddenly up', function() { it('should sync if modifier is suddenly up', function() {
sync.keydown({
keyCode: 0x41,
ctrlKey: true,
});
expect(sync.keydown({ expect(sync.keydown({
keyCode: 0x41, keyCode: 0x41,
ctrlKey: false ctrlKey: false
@ -160,7 +186,6 @@ describe('Helpers', function() {
}); });
}); });
describe('Toggle Alt', function() { describe('Toggle Alt', function() {
var sync = KeyboardUtil.ModifierSync();
it('should sync if modifier is suddenly down', function() { it('should sync if modifier is suddenly down', function() {
expect(sync.keydown({ expect(sync.keydown({
keyCode: 0x41, keyCode: 0x41,
@ -168,6 +193,10 @@ describe('Helpers', function() {
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe9), type: 'keydown'}]); })).to.be.deep.equal([{keysym: keysyms.lookup(0xffe9), type: 'keydown'}]);
}); });
it('should sync if modifier is suddenly up', function() { it('should sync if modifier is suddenly up', function() {
sync.keydown({
altKey: 0x41,
ctrlKey: true,
});
expect(sync.keydown({ expect(sync.keydown({
keyCode: 0x41, keyCode: 0x41,
altKey: false altKey: false
@ -175,7 +204,6 @@ describe('Helpers', function() {
}); });
}); });
describe('Toggle AltGr', function() { describe('Toggle AltGr', function() {
var sync = KeyboardUtil.ModifierSync();
it('should sync if modifier is suddenly down', function() { it('should sync if modifier is suddenly down', function() {
expect(sync.keydown({ expect(sync.keydown({
keyCode: 0x41, keyCode: 0x41,
@ -183,6 +211,10 @@ describe('Helpers', function() {
})).to.be.deep.equal([{keysym: keysyms.lookup(0xfe03), type: 'keydown'}]); })).to.be.deep.equal([{keysym: keysyms.lookup(0xfe03), type: 'keydown'}]);
}); });
it('should sync if modifier is suddenly up', function() { it('should sync if modifier is suddenly up', function() {
sync.keydown({
keyCode: 0x41,
altGraphKey: true,
});
expect(sync.keydown({ expect(sync.keydown({
keyCode: 0x41, keyCode: 0x41,
altGraphKey: false altGraphKey: false
@ -190,7 +222,6 @@ describe('Helpers', function() {
}); });
}); });
describe('Toggle Shift', function() { describe('Toggle Shift', function() {
var sync = KeyboardUtil.ModifierSync();
it('should sync if modifier is suddenly down', function() { it('should sync if modifier is suddenly down', function() {
expect(sync.keydown({ expect(sync.keydown({
keyCode: 0x41, keyCode: 0x41,
@ -198,6 +229,10 @@ describe('Helpers', function() {
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe1), type: 'keydown'}]); })).to.be.deep.equal([{keysym: keysyms.lookup(0xffe1), type: 'keydown'}]);
}); });
it('should sync if modifier is suddenly up', function() { it('should sync if modifier is suddenly up', function() {
sync.keydown({
keyCode: 0x41,
shiftKey: true,
});
expect(sync.keydown({ expect(sync.keydown({
keyCode: 0x41, keyCode: 0x41,
shiftKey: false shiftKey: false
@ -205,7 +240,6 @@ describe('Helpers', function() {
}); });
}); });
describe('Toggle Meta', function() { describe('Toggle Meta', function() {
var sync = KeyboardUtil.ModifierSync();
it('should sync if modifier is suddenly down', function() { it('should sync if modifier is suddenly down', function() {
expect(sync.keydown({ expect(sync.keydown({
keyCode: 0x41, keyCode: 0x41,
@ -213,6 +247,10 @@ describe('Helpers', function() {
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe7), type: 'keydown'}]); })).to.be.deep.equal([{keysym: keysyms.lookup(0xffe7), type: 'keydown'}]);
}); });
it('should sync if modifier is suddenly up', function() { it('should sync if modifier is suddenly up', function() {
sync.keydown({
keyCode: 0x41,
metaKey: true,
});
expect(sync.keydown({ expect(sync.keydown({
keyCode: 0x41, keyCode: 0x41,
metaKey: false metaKey: false
@ -231,7 +269,6 @@ describe('Helpers', function() {
}), 'B').to.be.deep.equal([]); }), 'B').to.be.deep.equal([]);
}) })
it('should update state on modifier keyevents', function() { it('should update state on modifier keyevents', function() {
var sync = KeyboardUtil.ModifierSync();
sync.keydown({ sync.keydown({
keyCode: 0x11, keyCode: 0x11,
}); });

View File

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

View File

@ -1,9 +1,10 @@
// requires local modules: util, websock, rfb, input/util, input/keysym, input/keysymdef, input/devices, inflator, des, display // requires test modules: 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,8 +35,23 @@ 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);
after(FakeWebSocket.restore); var Websock, FakeWebSocket;
before(function (done) {
requirejs(["core/rfb", "core/websock", "tests/fake.websocket"],
function (r, w, f) {
RFB = r.RFB;
Websock = w.Websock;
FakeWebSocket = f;
FakeWebSocket.replace();
done();
});
});
after(function () {
FakeWebSocket.restore();
});
before(function () { before(function () {
this.clock = sinon.useFakeTimers(); this.clock = sinon.useFakeTimers();

View File

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

View File

@ -1,5 +1,3 @@
// requires local modules: websock, util
// 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;
@ -7,6 +5,17 @@ var expect = chai.expect;
describe('Websock', function() { describe('Websock', function() {
"use strict"; "use strict";
var Websock, FakeWebSocket;
before(function (done) {
requirejs(["core/websock", "tests/fake.websocket"],
function (w, f) {
Websock = w.Websock;
FakeWebSocket = f;
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]);

View File

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

View File

@ -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>
@ -325,12 +322,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>

View File

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