Commit 6628b0e9 authored by Solly Ross's avatar Solly Ross

Enable noVNC to become Browserifiable

This commit restructures noVNC, splitting it into the core directory
and the app directory, with the former containing core noVNC parts,
and the latter containing parts specific to the application.

Additionally, it introduces a new utility for transforming the noVNC
javascript files into a browserifiable application.  Module names and
requirements are listed at the top of each file, and the script
`use_require.js` may be used to convert them into a form that uses
`module.exports` and `require`.  If you pass the '-b' flag to
the script, it will aslo run browserify, and copy over sufficient
resources to use 'vnc.html'.
parent 1138bdd4
......@@ -4,3 +4,4 @@ tests/data_*.js
utils/rebind.so
utils/websockify
node_modules
build
[submodule "include/web-socket-js-project"]
path = include/web-socket-js-project
url = https://github.com/gimite/web-socket-js.git
......@@ -51,12 +51,6 @@ licenses (all MPL 2.0 compatible):
include/jsunzip.js : zlib/libpng license
include/web-socket-js/ : New BSD license (3-clause). Source code at
http://github.com/gimite/web-socket-js
include/chrome-app/tcp-stream.js
: Apache 2.0 license
utils/websockify
utils/websocket.py : LGPL 3
......
......@@ -69,11 +69,7 @@ See more screenshots <a href="http://kanaka.github.com/noVNC/screenshots.html">h
* HTML5 Canvas (with createImageData): Chrome, Firefox 3.6+, iOS
Safari, Opera 11+, Internet Explorer 9+, etc.
* HTML5 WebSockets: For browsers that do not have builtin
WebSockets support, the project includes
<a href="http://github.com/gimite/web-socket-js">web-socket-js</a>,
a WebSockets emulator using Adobe Flash. iOS 4.2+ has built-in
WebSocket support.
* HTML5 WebSockets
* Fast Javascript Engine: this is not strictly a requirement, but
without a fast Javascript engine, noVNC might be painfully slow.
......@@ -130,7 +126,6 @@ use a WebSockets to TCP socket proxy. There is a python proxy included
* tight encoding : Michael Tinglof (Mercuri.ca)
* Included libraries:
* web-socket-js : Hiroshi Ichikawa (github.com/gimite/web-socket-js)
* as3crypto : Henri Torgemane (code.google.com/p/as3crypto)
* base64 : Martijn Pieters (Digital Creations 2), Samuel Sieb (sieb.net)
* jsunzip : Erik Moller (github.com/operasoftware/jsunzip),
......
......@@ -8,8 +8,9 @@
*/
/* jslint white: false, browser: true */
/* global window, $D, Util, WebUtil, RFB, Display */
/* global window, document.getElementById, Util, WebUtil, RFB, Display */
/* [module] name: UI; requires: Util, WebUtil, KeyTable, RFB, Display */
var UI;
(function () {
......@@ -17,11 +18,16 @@ var UI;
var resizeTimeout;
/* [begin skip-as-module] */
// Load supporting scripts
Util.load_scripts(
{'../core': ["base64.js", "websock.js", "des.js", "keysymdef.js",
"keyboard.js", "input.js", "display.js", "jsunzip.js",
"rfb.js", "keysym.js"],
'.': ["webutil.js"]});
window.onscriptsload = function () { UI.load(); };
Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
"keysymdef.js", "keyboard.js", "input.js", "display.js",
"jsunzip.js", "rfb.js", "keysym.js"]);
/* [end skip-as-module] */
UI = {
......@@ -54,13 +60,13 @@ var UI;
var sheets = WebUtil.getStylesheets();
var i;
for (i = 0; i < sheets.length; i += 1) {
UI.addOption($D('noVNC_stylesheet'),sheets[i].title, sheets[i].title);
UI.addOption(document.getElementById('noVNC_stylesheet'),sheets[i].title, sheets[i].title);
}
// Logging selection dropdown
var llevels = ['error', 'warn', 'info', 'debug'];
for (i = 0; i < llevels.length; i += 1) {
UI.addOption($D('noVNC_logging'),llevels[i], llevels[i]);
UI.addOption(document.getElementById('noVNC_logging'),llevels[i], llevels[i]);
}
// Settings with immediate effects
......@@ -107,12 +113,12 @@ var UI;
UI.updateVisualState();
$D('noVNC_host').focus();
document.getElementById('noVNC_host').focus();
// Show mouse selector buttons on touch screen devices
if (UI.isTouchDevice) {
// Show mobile buttons
$D('noVNC_mobile_buttons').style.display = "inline";
document.getElementById('noVNC_mobile_buttons').style.display = "inline";
UI.setMouseButton();
// Remove the address bar
setTimeout(function() { window.scrollTo(0, 1); }, 100);
......@@ -142,7 +148,7 @@ var UI;
// Show description by default when hosted at for kanaka.github.com
if (location.host === "kanaka.github.io") {
// Open the description dialog
$D('noVNC_description').style.display = "block";
document.getElementById('noVNC_description').style.display = "block";
} else {
// Show the connect panel on first load unless autoconnecting
if (autoconnect === UI.connSettingsOpen) {
......@@ -160,7 +166,7 @@ var UI;
initRFB: function () {
try {
UI.rfb = new RFB({'target': $D('noVNC_canvas'),
UI.rfb = new RFB({'target': document.getElementById('noVNC_canvas'),
'onUpdateState': UI.updateState,
'onXvpInit': UI.updateXvpVisualState,
'onClipboard': UI.clipReceive,
......@@ -176,48 +182,48 @@ var UI;
addMouseHandlers: function() {
// Setup interface handlers that can't be inline
$D("noVNC_view_drag_button").onclick = UI.setViewDrag;
$D("noVNC_mouse_button0").onclick = function () { UI.setMouseButton(1); };
$D("noVNC_mouse_button1").onclick = function () { UI.setMouseButton(2); };
$D("noVNC_mouse_button2").onclick = function () { UI.setMouseButton(4); };
$D("noVNC_mouse_button4").onclick = function () { UI.setMouseButton(0); };
$D("showKeyboard").onclick = UI.showKeyboard;
$D("keyboardinput").oninput = UI.keyInput;
$D("keyboardinput").onblur = UI.keyInputBlur;
$D("keyboardinput").onsubmit = function () { return false; };
$D("showExtraKeysButton").onclick = UI.showExtraKeys;
$D("toggleCtrlButton").onclick = UI.toggleCtrl;
$D("toggleAltButton").onclick = UI.toggleAlt;
$D("sendTabButton").onclick = UI.sendTab;
$D("sendEscButton").onclick = UI.sendEsc;
$D("sendCtrlAltDelButton").onclick = UI.sendCtrlAltDel;
$D("xvpShutdownButton").onclick = UI.xvpShutdown;
$D("xvpRebootButton").onclick = UI.xvpReboot;
$D("xvpResetButton").onclick = UI.xvpReset;
$D("noVNC_status").onclick = UI.togglePopupStatusPanel;
$D("noVNC_popup_status_panel").onclick = UI.togglePopupStatusPanel;
$D("xvpButton").onclick = UI.toggleXvpPanel;
$D("clipboardButton").onclick = UI.toggleClipboardPanel;
$D("settingsButton").onclick = UI.toggleSettingsPanel;
$D("connectButton").onclick = UI.toggleConnectPanel;
$D("disconnectButton").onclick = UI.disconnect;
$D("descriptionButton").onclick = UI.toggleConnectPanel;
$D("noVNC_clipboard_text").onfocus = UI.displayBlur;
$D("noVNC_clipboard_text").onblur = UI.displayFocus;
$D("noVNC_clipboard_text").onchange = UI.clipSend;
$D("noVNC_clipboard_clear_button").onclick = UI.clipClear;
$D("noVNC_settings_menu").onmouseover = UI.displayBlur;
$D("noVNC_settings_menu").onmouseover = UI.displayFocus;
$D("noVNC_apply").onclick = UI.settingsApply;
$D("noVNC_connect_button").onclick = UI.connect;
$D("noVNC_resize").onchange = function () {
document.getElementById("noVNC_view_drag_button").onclick = UI.setViewDrag;
document.getElementById("noVNC_mouse_button0").onclick = function () { UI.setMouseButton(1); };
document.getElementById("noVNC_mouse_button1").onclick = function () { UI.setMouseButton(2); };
document.getElementById("noVNC_mouse_button2").onclick = function () { UI.setMouseButton(4); };
document.getElementById("noVNC_mouse_button4").onclick = function () { UI.setMouseButton(0); };
document.getElementById("showKeyboard").onclick = UI.showKeyboard;
document.getElementById("keyboardinput").oninput = UI.keyInput;
document.getElementById("keyboardinput").onblur = UI.keyInputBlur;
document.getElementById("keyboardinput").onsubmit = function () { return false; };
document.getElementById("showExtraKeysButton").onclick = UI.showExtraKeys;
document.getElementById("toggleCtrlButton").onclick = UI.toggleCtrl;
document.getElementById("toggleAltButton").onclick = UI.toggleAlt;
document.getElementById("sendTabButton").onclick = UI.sendTab;
document.getElementById("sendEscButton").onclick = UI.sendEsc;
document.getElementById("sendCtrlAltDelButton").onclick = UI.sendCtrlAltDel;
document.getElementById("xvpShutdownButton").onclick = UI.xvpShutdown;
document.getElementById("xvpRebootButton").onclick = UI.xvpReboot;
document.getElementById("xvpResetButton").onclick = UI.xvpReset;
document.getElementById("noVNC_status").onclick = UI.togglePopupStatusPanel;
document.getElementById("noVNC_popup_status_panel").onclick = UI.togglePopupStatusPanel;
document.getElementById("xvpButton").onclick = UI.toggleXvpPanel;
document.getElementById("clipboardButton").onclick = UI.toggleClipboardPanel;
document.getElementById("settingsButton").onclick = UI.toggleSettingsPanel;
document.getElementById("connectButton").onclick = UI.toggleConnectPanel;
document.getElementById("disconnectButton").onclick = UI.disconnect;
document.getElementById("descriptionButton").onclick = UI.toggleConnectPanel;
document.getElementById("noVNC_clipboard_text").onfocus = UI.displayBlur;
document.getElementById("noVNC_clipboard_text").onblur = UI.displayFocus;
document.getElementById("noVNC_clipboard_text").onchange = UI.clipSend;
document.getElementById("noVNC_clipboard_clear_button").onclick = UI.clipClear;
document.getElementById("noVNC_settings_menu").onmouseover = UI.displayBlur;
document.getElementById("noVNC_settings_menu").onmouseover = UI.displayFocus;
document.getElementById("noVNC_apply").onclick = UI.settingsApply;
document.getElementById("noVNC_connect_button").onclick = UI.connect;
document.getElementById("noVNC_resize").onchange = function () {
var connected = UI.rfb && UI.rfb_state === 'normal';
UI.enableDisableClip(connected);
};
......@@ -257,7 +263,7 @@ var UI;
},
getCanvasLimit: function () {
var container = $D('noVNC_container');
var container = document.getElementById('noVNC_container');
// Hide the scrollbars until the size is calculated
container.style.overflow = "hidden";
......@@ -277,7 +283,7 @@ var UI;
// Read form control compatible setting from cookie
getSetting: function(name) {
var ctrl = $D('noVNC_' + name);
var ctrl = document.getElementById('noVNC_' + name);
var val = WebUtil.readSetting(name);
if (typeof val !== 'undefined' && val !== null && ctrl.type === 'checkbox') {
if (val.toString().toLowerCase() in {'0':1, 'no':1, 'false':1}) {
......@@ -301,7 +307,7 @@ var UI;
// Update the settings control
value = UI.getSetting(name);
var ctrl = $D('noVNC_' + name);
var ctrl = document.getElementById('noVNC_' + name);
if (ctrl.type === 'checkbox') {
ctrl.checked = value;
......@@ -324,7 +330,7 @@ var UI;
// Save control setting to cookie
saveSetting: function(name) {
var val, ctrl = $D('noVNC_' + name);
var val, ctrl = document.getElementById('noVNC_' + name);
if (ctrl.type === 'checkbox') {
val = ctrl.checked;
} else if (typeof ctrl.options !== 'undefined') {
......@@ -357,12 +363,12 @@ var UI;
// Show the popup status panel
togglePopupStatusPanel: function() {
var psp = $D('noVNC_popup_status_panel');
var psp = document.getElementById('noVNC_popup_status_panel');
if (UI.popupStatusOpen === true) {
psp.style.display = "none";
UI.popupStatusOpen = false;
} else {
psp.innerHTML = $D('noVNC_status').innerHTML;
psp.innerHTML = document.getElementById('noVNC_status').innerHTML;
psp.style.display = "block";
psp.style.left = window.innerWidth/2 -
parseInt(window.getComputedStyle(psp, false).width)/2 -30 + "px";
......@@ -373,7 +379,7 @@ var UI;
// Show the XVP panel
toggleXvpPanel: function() {
// Close the description panel
$D('noVNC_description').style.display = "none";
document.getElementById('noVNC_description').style.display = "none";
// Close settings if open
if (UI.settingsOpen === true) {
UI.settingsApply();
......@@ -393,12 +399,12 @@ var UI;
}
// Toggle XVP panel
if (UI.xvpOpen === true) {
$D('noVNC_xvp').style.display = "none";
$D('xvpButton').className = "noVNC_status_button";
document.getElementById('noVNC_xvp').style.display = "none";
document.getElementById('xvpButton').className = "noVNC_status_button";
UI.xvpOpen = false;
} else {
$D('noVNC_xvp').style.display = "block";
$D('xvpButton').className = "noVNC_status_button_selected";
document.getElementById('noVNC_xvp').style.display = "block";
document.getElementById('xvpButton').className = "noVNC_status_button_selected";
UI.xvpOpen = true;
}
},
......@@ -406,7 +412,7 @@ var UI;
// Show the clipboard panel
toggleClipboardPanel: function() {
// Close the description panel
$D('noVNC_description').style.display = "none";
document.getElementById('noVNC_description').style.display = "none";
// Close settings if open
if (UI.settingsOpen === true) {
UI.settingsApply();
......@@ -426,12 +432,12 @@ var UI;
}
// Toggle Clipboard Panel
if (UI.clipboardOpen === true) {
$D('noVNC_clipboard').style.display = "none";
$D('clipboardButton').className = "noVNC_status_button";
document.getElementById('noVNC_clipboard').style.display = "none";
document.getElementById('clipboardButton').className = "noVNC_status_button";
UI.clipboardOpen = false;
} else {
$D('noVNC_clipboard').style.display = "block";
$D('clipboardButton').className = "noVNC_status_button_selected";
document.getElementById('noVNC_clipboard').style.display = "block";
document.getElementById('clipboardButton').className = "noVNC_status_button_selected";
UI.clipboardOpen = true;
}
},
......@@ -439,12 +445,12 @@ var UI;
// Show the connection settings panel/menu
toggleConnectPanel: function() {
// Close the description panel
$D('noVNC_description').style.display = "none";
document.getElementById('noVNC_description').style.display = "none";
// Close connection settings if open
if (UI.settingsOpen === true) {
UI.settingsApply();
UI.closeSettingsMenu();
$D('connectButton').className = "noVNC_status_button";
document.getElementById('connectButton').className = "noVNC_status_button";
}
// Close clipboard panel if open
if (UI.clipboardOpen === true) {
......@@ -461,17 +467,17 @@ var UI;
// Toggle Connection Panel
if (UI.connSettingsOpen === true) {
$D('noVNC_controls').style.display = "none";
$D('connectButton').className = "noVNC_status_button";
document.getElementById('noVNC_controls').style.display = "none";
document.getElementById('connectButton').className = "noVNC_status_button";
UI.connSettingsOpen = false;
UI.saveSetting('host');
UI.saveSetting('port');
//UI.saveSetting('password');
} else {
$D('noVNC_controls').style.display = "block";
$D('connectButton').className = "noVNC_status_button_selected";
document.getElementById('noVNC_controls').style.display = "block";
document.getElementById('connectButton').className = "noVNC_status_button_selected";
UI.connSettingsOpen = true;
$D('noVNC_host').focus();
document.getElementById('noVNC_host').focus();
}
},
......@@ -480,7 +486,7 @@ var UI;
// On close, settings are applied
toggleSettingsPanel: function() {
// Close the description panel
$D('noVNC_description').style.display = "none";
document.getElementById('noVNC_description').style.display = "none";
if (UI.settingsOpen) {
UI.settingsApply();
UI.closeSettingsMenu();
......@@ -491,7 +497,7 @@ var UI;
UI.updateSetting('cursor');
} else {
UI.updateSetting('cursor', !UI.isTouchDevice);
$D('noVNC_cursor').disabled = true;
document.getElementById('noVNC_cursor').disabled = true;
}
UI.updateSetting('clip');
UI.updateSetting('resize');
......@@ -509,7 +515,7 @@ var UI;
// Open menu
openSettingsMenu: function() {
// Close the description panel
$D('noVNC_description').style.display = "none";
document.getElementById('noVNC_description').style.display = "none";
// Close clipboard panel if open
if (UI.clipboardOpen === true) {
UI.toggleClipboardPanel();
......@@ -526,15 +532,15 @@ var UI;
if (UI.xvpOpen === true) {
UI.toggleXvpPanel();
}
$D('noVNC_settings').style.display = "block";
$D('settingsButton').className = "noVNC_status_button_selected";
document.getElementById('noVNC_settings').style.display = "block";
document.getElementById('settingsButton').className = "noVNC_status_button_selected";
UI.settingsOpen = true;
},
// Close menu (without applying settings)
closeSettingsMenu: function() {
$D('noVNC_settings').style.display = "none";
$D('settingsButton').className = "noVNC_status_button";
document.getElementById('noVNC_settings').style.display = "none";
document.getElementById('settingsButton').className = "noVNC_status_button";
UI.settingsOpen = false;
},
......@@ -572,10 +578,10 @@ var UI;
setPassword: function() {
UI.rfb.sendPassword($D('noVNC_password').value);
UI.rfb.sendPassword(document.getElementById('noVNC_password').value);
//Reset connect button.
$D('noVNC_connect_button').value = "Connect";
$D('noVNC_connect_button').onclick = UI.Connect;
document.getElementById('noVNC_connect_button').value = "Connect";
document.getElementById('noVNC_connect_button').onclick = UI.Connect;
//Hide connection panel.
UI.toggleConnectPanel();
return false;
......@@ -608,7 +614,7 @@ var UI;
var blist = [0, 1,2,4];
for (var b = 0; b < blist.length; b++) {
var button = $D('noVNC_mouse_button' + blist[b]);
var button = document.getElementById('noVNC_mouse_button' + blist[b]);
if (blist[b] === num) {
button.style.display = "";
} else {
......@@ -629,8 +635,8 @@ var UI;
klass = "noVNC_status_normal";
break;
case 'disconnected':
$D('noVNC_logo').style.display = "block";
$D('noVNC_container').style.display = "none";
document.getElementById('noVNC_logo').style.display = "block";
document.getElementById('noVNC_container').style.display = "none";
/* falls through */
case 'loaded':
klass = "noVNC_status_normal";
......@@ -638,9 +644,9 @@ var UI;
case 'password':
UI.toggleConnectPanel();
$D('noVNC_connect_button').value = "Send Password";
$D('noVNC_connect_button').onclick = UI.setPassword;
$D('noVNC_password').focus();
document.getElementById('noVNC_connect_button').value = "Send Password";
document.getElementById('noVNC_connect_button').onclick = UI.setPassword;
document.getElementById('noVNC_password').focus();
klass = "noVNC_status_warn";
break;
......@@ -650,8 +656,8 @@ var UI;
}
if (typeof(msg) !== 'undefined') {
$D('noVNC-control-bar').setAttribute("class", klass);
$D('noVNC_status').innerHTML = msg;
document.getElementById('noVNC-control-bar').setAttribute("class", klass);
document.getElementById('noVNC_status').innerHTML = msg;
}
UI.updateVisualState();
......@@ -662,35 +668,35 @@ var UI;
var connected = UI.rfb && UI.rfb_state === 'normal';
//Util.Debug(">> updateVisualState");
$D('noVNC_encrypt').disabled = connected;
$D('noVNC_true_color').disabled = connected;
document.getElementById('noVNC_encrypt').disabled = connected;
document.getElementById('noVNC_true_color').disabled = connected;
if (Util.browserSupportsCursorURIs()) {
$D('noVNC_cursor').disabled = connected;
document.getElementById('noVNC_cursor').disabled = connected;
} else {
UI.updateSetting('cursor', !UI.isTouchDevice);
$D('noVNC_cursor').disabled = true;
document.getElementById('noVNC_cursor').disabled = true;
}
UI.enableDisableClip(connected);
$D('noVNC_resize').disabled = connected;
$D('noVNC_shared').disabled = connected;
$D('noVNC_view_only').disabled = connected;
$D('noVNC_path').disabled = connected;
$D('noVNC_repeaterID').disabled = connected;
document.getElementById('noVNC_resize').disabled = connected;
document.getElementById('noVNC_shared').disabled = connected;
document.getElementById('noVNC_view_only').disabled = connected;
document.getElementById('noVNC_path').disabled = connected;
document.getElementById('noVNC_repeaterID').disabled = connected;
if (connected) {
UI.setViewClip();
UI.setMouseButton(1);
$D('clipboardButton').style.display = "inline";
$D('showKeyboard').style.display = "inline";
$D('noVNC_extra_keys').style.display = "";
$D('sendCtrlAltDelButton').style.display = "inline";
document.getElementById('clipboardButton').style.display = "inline";
document.getElementById('showKeyboard').style.display = "inline";
document.getElementById('noVNC_extra_keys').style.display = "";
document.getElementById('sendCtrlAltDelButton').style.display = "inline";
} else {
UI.setMouseButton();
$D('clipboardButton').style.display = "none";
$D('showKeyboard').style.display = "none";
$D('noVNC_extra_keys').style.display = "none";
$D('sendCtrlAltDelButton').style.display = "none";
document.getElementById('clipboardButton').style.display = "none";
document.getElementById('showKeyboard').style.display = "none";
document.getElementById('noVNC_extra_keys').style.display = "none";
document.getElementById('sendCtrlAltDelButton').style.display = "none";
UI.updateXvpVisualState(0);
}
......@@ -702,18 +708,18 @@ var UI;
case 'fatal':
case 'failed':
case 'disconnected':
$D('connectButton').style.display = "";
$D('disconnectButton').style.display = "none";
document.getElementById('connectButton').style.display = "";
document.getElementById('disconnectButton').style.display = "none";
UI.connSettingsOpen = false;
UI.toggleConnectPanel();
break;
case 'loaded':
$D('connectButton').style.display = "";
$D('disconnectButton').style.display = "none";
document.getElementById('connectButton').style.display = "";
document.getElementById('disconnectButton').style.display = "none";
break;
default:
$D('connectButton').style.display = "none";
$D('disconnectButton').style.display = "";
document.getElementById('connectButton').style.display = "none";
document.getElementById('disconnectButton').style.display = "";
break;
}
......@@ -723,9 +729,9 @@ var UI;
// Disable/enable XVP button
updateXvpVisualState: function(ver) {
if (ver >= 1) {
$D('xvpButton').style.display = 'inline';
document.getElementById('xvpButton').style.display = 'inline';
} else {
$D('xvpButton').style.display = 'none';
document.getElementById('xvpButton').style.display = 'none';
// Close XVP panel if open
if (UI.xvpOpen === true) {
UI.toggleXvpPanel();
......@@ -734,12 +740,12 @@ var UI;
},
enableDisableClip: function (connected) {
var resizeElem = $D('noVNC_resize');
var resizeElem = document.getElementById('noVNC_resize');
if (resizeElem.value === 'downscale' || resizeElem.value === 'scale') {
UI.forceSetting('clip', false);
$D('noVNC_clip').disabled = true;
document.getElementById('noVNC_clip').disabled = true;
} else {
$D('noVNC_clip').disabled = connected || UI.isTouchDevice;
document.getElementById('noVNC_clip').disabled = connected || UI.isTouchDevice;
if (UI.isTouchDevice) {
UI.forceSetting('clip', true);
}
......@@ -763,7 +769,7 @@ var UI;
clipReceive: function(rfb, text) {
Util.Debug(">> UI.clipReceive: " + text.substr(0,40) + "...");
$D('noVNC_clipboard_text').value = text;
document.getElementById('noVNC_clipboard_text').value = text;
Util.Debug("<< UI.clipReceive");
},
......@@ -771,10 +777,10 @@ var UI;
UI.closeSettingsMenu();
UI.toggleConnectPanel();
var host = $D('noVNC_host').value;
var port = $D('noVNC_port').value;
var password = $D('noVNC_password').value;
var path = $D('noVNC_path').value;
var host = document.getElementById('noVNC_host').value;
var port = document.getElementById('noVNC_port').value;
var password = document.getElementById('noVNC_password').value;
var path = document.getElementById('noVNC_path').value;
if ((!host) || (!port)) {
throw new Error("Must set host and port");
}
......@@ -792,8 +798,8 @@ var UI;
//Close dialog.
setTimeout(UI.setBarPosition, 100);
$D('noVNC_logo').style.display = "none";
$D('noVNC_container').style.display = "inline";
document.getElementById('noVNC_logo').style.display = "none";
document.getElementById('noVNC_container').style.display = "inline";
},
disconnect: function() {
......@@ -803,8 +809,8 @@ var UI;
// Restore the callback used for initial resize
UI.rfb.set_onFBUComplete(UI.FBUComplete);
$D('noVNC_logo').style.display = "block";
$D('noVNC_container').style.display = "none";
document.getElementById('noVNC_logo').style.display = "block";
document.getElementById('noVNC_container').style.display = "none";
// Don't display the connection settings until we're actually disconnected
},
......@@ -824,12 +830,12 @@ var UI;
},
clipClear: function() {
$D('noVNC_clipboard_text').value = "";
document.getElementById('noVNC_clipboard_text').value = "";
UI.rfb.clipboardPasteFrom("");
},
clipSend: function() {
var text = $D('noVNC_clipboard_text').value;
var text = document.getElementById('noVNC_clipboard_text').value;
Util.Debug(">> UI.clipSend: " + text.substr(0,40) + "...");
UI.rfb.clipboardPasteFrom(text);
Util.Debug("<< UI.clipSend");
......@@ -872,13 +878,13 @@ var UI;
display.set_maxHeight(size.h);
// Hide potential scrollbars that can skew the position
$D('noVNC_container').style.overflow = "hidden";
document.getElementById('noVNC_container').style.overflow = "hidden";
// The x position marks the left margin of the canvas,
// remove the margin from both sides to keep it centered
var new_w = size.w - (2 * Util.getPosition($D('noVNC_canvas')).x);
var new_w = size.w - (2 * Util.getPosition(document.getElementById('noVNC_canvas')).x);
$D('noVNC_container').style.overflow = "visible";
document.getElementById('noVNC_container').style.overflow = "visible";
display.viewportChangeSize(new_w, size.h);
}
......@@ -896,7 +902,7 @@ var UI;
// If not specified, then toggle
drag = !UI.rfb.get_viewportDrag();
}
var vmb = $D('noVNC_view_drag_button');
var vmb = document.getElementById('noVNC_view_drag_button');
if (drag) {
vmb.className = "noVNC_status_button_selected";
UI.rfb.set_viewportDrag(true);
......@@ -907,7 +913,7 @@ var UI;
},
updateViewDragButton: function() {
var vmb = $D('noVNC_view_drag_button');
var vmb = document.getElementById('noVNC_view_drag_button');
if (UI.rfb_state === 'normal' &&
UI.rfb.get_display().get_viewport() &&
UI.rfb.get_display().clippingDisplay()) {
......@@ -919,8 +925,8 @@ var UI;
// On touch devices, show the OS keyboard
showKeyboard: function() {
var kbi = $D('keyboardinput');
var skb = $D('showKeyboard');
var kbi = document.getElementById('keyboardinput');
var skb = document.getElementById('showKeyboard');
var l = kbi.value.length;
if(UI.keyboardVisible === false) {
kbi.focus();
......@@ -938,16 +944,16 @@ var UI;
keepKeyboard: function() {
clearTimeout(UI.hideKeyboardTimeout);
if(UI.keyboardVisible === true) {
$D('keyboardinput').focus();
$D('showKeyboard').className = "noVNC_status_button_selected";
document.getElementById('keyboardinput').focus();
document.getElementById('showKeyboard').className = "noVNC_status_button_selected";
} else if(UI.keyboardVisible === false) {
$D('keyboardinput').blur();
$D('showKeyboard').className = "noVNC_status_button";
document.getElementById('keyboardinput').blur();
document.getElementById('showKeyboard').className = "noVNC_status_button";
}
},
keyboardinputReset: function() {
var kbi = $D('keyboardinput');
var kbi = document.getElementById('keyboardinput');
kbi.value = new Array(UI.defaultKeyboardinputLen).join("_");
UI.lastKeyboardinput = kbi.value;
},
......@@ -999,7 +1005,7 @@ var UI;
// Send the key events
for (i = 0; i < backspaces; i++) {
UI.rfb.sendKey(XK_BackSpace);
UI.rfb.sendKey(KeyTable.XK_BackSpace);
}
for (i = newLen - inputs; i < newLen; i++) {
UI.rfb.sendKey(newValue.charCodeAt(i));
......@@ -1024,7 +1030,7 @@ var UI;
},
keyInputBlur: function() {
$D('showKeyboard').className = "noVNC_status_button";
document.getElementById('showKeyboard').className = "noVNC_status_button";
//Weird bug in iOS if you change keyboardVisible
//here it does not actually occur so next time
//you click keyboard icon it doesnt work.
......@@ -1034,18 +1040,18 @@ var UI;
showExtraKeys: function() {
UI.keepKeyboard();
if(UI.extraKeysVisible === false) {
$D('toggleCtrlButton').style.display = "inline";
$D('toggleAltButton').style.display = "inline";
$D('sendTabButton').style.display = "inline";
$D('sendEscButton').style.display = "inline";
$D('showExtraKeysButton').className = "noVNC_status_button_selected";
document.getElementById('toggleCtrlButton').style.display = "inline";
document.getElementById('toggleAltButton').style.display = "inline";
document.getElementById('sendTabButton').style.display = "inline";
document.getElementById('sendEscButton').style.display = "inline";
document.getElementById('showExtraKeysButton').className = "noVNC_status_button_selected";
UI.extraKeysVisible = true;
} else if(UI.extraKeysVisible === true) {
$D('toggleCtrlButton').style.display = "";
$D('toggleAltButton').style.display = "";
$D('sendTabButton').style.display = "";
$D('sendEscButton').style.display = "";
$D('showExtraKeysButton').className = "noVNC_status_button";
document.getElementById('toggleCtrlButton').style.display = "";
document.getElementById('toggleAltButton').style.display = "";
document.getElementById('sendTabButton').style.display = "";
document.getElementById('sendEscButton').style.display = "";
document.getElementById('showExtraKeysButton').className = "noVNC_status_button";
UI.extraKeysVisible = false;
}
},
......@@ -1053,12 +1059,12 @@ var UI;
toggleCtrl: function() {
UI.keepKeyboard();
if(UI.ctrlOn === false) {
UI.rfb.sendKey(XK_Control_L, true);
$D('toggleCtrlButton').className = "noVNC_status_button_selected";
UI.rfb.sendKey(KeyTable.XK_Control_L, true);
document.getElementById('toggleCtrlButton').className = "noVNC_status_button_selected";
UI.ctrlOn = true;
} else if(UI.ctrlOn === true) {
UI.rfb.sendKey(XK_Control_L, false);
$D('toggleCtrlButton').className = "noVNC_status_button";
UI.rfb.sendKey(KeyTable.XK_Control_L, false);
document.getElementById('toggleCtrlButton').className = "noVNC_status_button";
UI.ctrlOn = false;
}
},
......@@ -1066,24 +1072,24 @@ var UI;
toggleAlt: function() {
UI.keepKeyboard();
if(UI.altOn === false) {
UI.rfb.sendKey(XK_Alt_L, true);
$D('toggleAltButton').className = "noVNC_status_button_selected";
UI.rfb.sendKey(KeyTable.XK_Alt_L, true);
document.getElementById('toggleAltButton').className = "noVNC_status_button_selected";
UI.altOn = true;
} else if(UI.altOn === true) {
UI.rfb.sendKey(XK_Alt_L, false);
$D('toggleAltButton').className = "noVNC_status_button";
UI.rfb.sendKey(KeyTable.XK_Alt_L, false);
document.getElementById('toggleAltButton').className = "noVNC_status_button";
UI.altOn = false;
}
},
sendTab: function() {
UI.keepKeyboard();
UI.rfb.sendKey(XK_Tab);
UI.rfb.sendKey(KeyTable.XK_Tab);
},
sendEsc: function() {
UI.keepKeyboard();
UI.rfb.sendKey(XK_Escape);
UI.rfb.sendKey(KeyTable.XK_Escape);
},
setKeyboard: function() {
......@@ -1099,12 +1105,14 @@ var UI;
},
setBarPosition: function() {
$D('noVNC-control-bar').style.top = (window.pageYOffset) + 'px';
$D('noVNC_mobile_buttons').style.left = (window.pageXOffset) + 'px';
document.getElementById('noVNC-control-bar').style.top = (window.pageYOffset) + 'px';
document.getElementById('noVNC_mobile_buttons').style.left = (window.pageXOffset) + 'px';
var vncwidth = $D('noVNC_screen').style.offsetWidth;
$D('noVNC-control-bar').style.width = vncwidth + 'px';
var vncwidth = document.getElementById('noVNC_screen').style.offsetWidth;
document.getElementById('noVNC-control-bar').style.width = vncwidth + 'px';
}
};
/* [as-module] UI.load(); */
})();
......@@ -34,9 +34,9 @@
<!-- Stylesheets -->
<link rel="stylesheet" href="include/base.css" />
<link rel="alternate stylesheet" href="include/black.css" TITLE="Black" />
<link rel="alternate stylesheet" href="include/blue.css" TITLE="Blue" />
<link rel="stylesheet" href="base.css" />
<link rel="alternate stylesheet" href="black.css" TITLE="Black" />
<link rel="alternate stylesheet" href="blue.css" TITLE="Blue" />
<!--
<script type='text/javascript'
......@@ -212,8 +212,10 @@
</div>
</div>
<script src="include/util.js"></script>
<script src="include/ui.js"></script>
<!-- begin scripts -->
<script src="../core/util.js"></script>
<script src="ui.js"></script>
<!-- end scripts -->
</body>
</html>
......@@ -34,13 +34,13 @@
<!-- Stylesheets -->
<link rel="stylesheet" href="include/base.css" title="plain">
<link rel="stylesheet" href="base.css" title="plain">
<!--
<script type='text/javascript'
src='http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js'></script>
-->
<script src="include/util.js"></script>
<script src="../core/util.js"></script>
</head>
<body style="margin: 0px;">
......@@ -75,9 +75,11 @@
"use strict";
// Load supporting scripts
Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
"keysymdef.js", "keyboard.js", "input.js", "display.js",
"jsunzip.js", "rfb.js", "keysym.js"]);
Util.load_scripts(
{'../core': ["base64.js", "websock.js", "des.js", "keysymdef.js",
"keyboard.js", "input.js", "display.js", "jsunzip.js",
"rfb.js", "keysym.js"],
'.': ["webutil.js"]});
var rfb;
var resizeTimeout;
......@@ -87,7 +89,7 @@
if (WebUtil.getQueryVar('resize', false)) {
var innerW = window.innerWidth;
var innerH = window.innerHeight;
var controlbarH = $D('noVNC_status_bar').offsetHeight;
var controlbarH = document.getElementById('noVNC_status_bar').offsetHeight;
var padding = 5;
if (innerW !== undefined && innerH !== undefined)
rfb.setDesktopSize(innerW, innerH - controlbarH - padding);
......@@ -104,11 +106,11 @@
msg += 'Password Required: ';
msg += '<input type=password size=10 id="password_input" class="noVNC_status">';
msg += '<\/form>';
$D('noVNC_status_bar').setAttribute("class", "noVNC_status_warn");
$D('noVNC_status').innerHTML = msg;
document.getElementById('noVNC_status_bar').setAttribute("class", "noVNC_status_warn");
document.getElementById('noVNC_status').innerHTML = msg;
}
function setPassword() {
rfb.sendPassword($D('password_input').value);
rfb.sendPassword(document.getElementById('password_input').value);
return false;
}
function sendCtrlAltDel() {
......@@ -129,9 +131,9 @@
}
function updateState(rfb, state, oldstate, msg) {
var s, sb, cad, level;
s = $D('noVNC_status');
sb = $D('noVNC_status_bar');
cad = $D('sendCtrlAltDelButton');
s = document.getElementById('noVNC_status');
sb = document.getElementById('noVNC_status_bar');
cad = document.getElementById('sendCtrlAltDelButton');
switch (state) {
case 'failed': level = "error"; break;
case 'fatal': level = "error"; break;
......@@ -166,7 +168,7 @@
function xvpInit(ver) {
var xvpbuttons;
xvpbuttons = $D('noVNC_xvp_buttons');
xvpbuttons = document.getElementById('noVNC_xvp_buttons');
if (ver >= 1) {
xvpbuttons.style.display = 'inline';
} else {
......@@ -177,11 +179,11 @@
window.onscriptsload = function () {
var host, port, password, path, token;
$D('sendCtrlAltDelButton').style.display = "inline";
$D('sendCtrlAltDelButton').onclick = sendCtrlAltDel;
$D('xvpShutdownButton').onclick = xvpShutdown;
$D('xvpRebootButton').onclick = xvpReboot;
$D('xvpResetButton').onclick = xvpReset;
document.getElementById('sendCtrlAltDelButton').style.display = "inline";
document.getElementById('sendCtrlAltDelButton').onclick = sendCtrlAltDel;
document.getElementById('xvpShutdownButton').onclick = xvpShutdown;
document.getElementById('xvpRebootButton').onclick = xvpReboot;
document.getElementById('xvpResetButton').onclick = xvpReset;
WebUtil.init_logging(WebUtil.getQueryVar('logging', 'warn'));
document.title = unescape(WebUtil.getQueryVar('title', 'noVNC'));
......@@ -217,7 +219,7 @@
}
try {
rfb = new RFB({'target': $D('noVNC_canvas'),
rfb = new RFB({'target': document.getElementById('noVNC_canvas'),
'encrypt': WebUtil.getQueryVar('encrypt',
(window.location.protocol === "https:")),
'repeaterID': WebUtil.getQueryVar('repeaterID', ''),
......
......@@ -11,24 +11,8 @@
/*global Util, window, document */
// Globals defined here
var WebUtil = {}, $D;
/*
* Simple DOM selector by ID
*/
if (!window.$D) {
window.$D = function (id) {
if (document.getElementById) {
return document.getElementById(id);
} else if (document.all) {
return document.all[id];
} else if (document.layers) {
return document.layers[id];
}
return undefined;
};
}
/* [module] name: WebUtil; requires: Util */
var WebUtil = {};
/*
* ------------------------------------------------------
......
......@@ -2,6 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* [module] name: Base64 */
// From: http://hg.mozilla.org/mozilla-central/raw-file/ec10630b1a54/js/src/devtools/jint/sunspider/string-base64.js
/*jslint white: false */
......
......@@ -77,7 +77,9 @@
/* jslint white: false */
function DES(passwd) {
/* [module] name: DES */
var DES = function (passwd) {
"use strict";
// Tables, permutations, S-boxes, etc.
......@@ -273,4 +275,4 @@ function DES(passwd) {
setKeys(passwd); // Setup keys
return {'encrypt': encrypt}; // Public interface
} // function DES
}; // function DES
......@@ -10,6 +10,7 @@
/*jslint browser: true, white: false */
/*global Util, Base64, changeCursor */
/* [module] name: Display; requires: Util, Base64 */
var Display;
(function () {
......
......@@ -8,7 +8,8 @@
/*jslint browser: true, white: false */
/*global window, Util */
var Keyboard, Mouse;
/* [module] name: Keyboard; requires: Util, KeyboardUtil */
var Keyboard;
(function () {
"use strict";
......@@ -27,10 +28,10 @@ var Keyboard, Mouse;
});
// create the keyboard handler
this._handler = new KeyEventDecoder(kbdUtil.ModifierSync(),
VerifyCharModifier( /* jshint newcap: false */
TrackKeyState(
EscapeModifiers(this._handleRfbEvent.bind(this))
this._handler = new KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(),
KeyboardUtil.VerifyCharModifier( /* jshint newcap: false */
KeyboardUtil.TrackKeyState(
KeyboardUtil.EscapeModifiers(this._handleRfbEvent.bind(this))
)
)
); /* jshint newcap: true */
......@@ -149,7 +150,12 @@ var Keyboard, Mouse;
//
// Mouse event handler
//
})();
/* [module] name: Mouse; requires: Util */
var Mouse;
(function () {
Mouse = function (defaults) {
this._mouseCaptured = false;
......
/*
* JSUnzip
*
* Copyright (c) 2011 by Erik Moller
* All Rights Reserved
*
* This software is provided 'as-is', without any express
* or implied warranty. In no event will the authors be
* held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software
* for any purpose, including commercial applications,
* and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be
* misrepresented; you must not claim that you
* wrote the original software. If you use this
* software in a product, an acknowledgment in
* the product documentation would be appreciated
* but is not required.
*
* 2. Altered source versions must be plainly marked
* as such, and must not be misrepresented as
* being the original software.
*
* 3. This notice may not be removed or altered from
* any source distribution.
*/
var tinf;
function JSUnzip() {
this.getInt = function(offset, size) {
switch (size) {
case 4:
return (this.data.charCodeAt(offset + 3) & 0xff) << 24 |
(this.data.charCodeAt(offset + 2) & 0xff) << 16 |
(this.data.charCodeAt(offset + 1) & 0xff) << 8 |
(this.data.charCodeAt(offset + 0) & 0xff);
break;
case 2:
return (this.data.charCodeAt(offset + 1) & 0xff) << 8 |
(this.data.charCodeAt(offset + 0) & 0xff);
break;
default:
return this.data.charCodeAt(offset) & 0xff;
break;
}
};
this.getDOSDate = function(dosdate, dostime) {
var day = dosdate & 0x1f;
var month = ((dosdate >> 5) & 0xf) - 1;
var year = 1980 + ((dosdate >> 9) & 0x7f)
var second = (dostime & 0x1f) * 2;
var minute = (dostime >> 5) & 0x3f;
hour = (dostime >> 11) & 0x1f;
return new Date(year, month, day, hour, minute, second);
}
this.open = function(data) {
this.data = data;
this.files = [];
if (this.data.length < 22)
return { 'status' : false, 'error' : 'Invalid data' };
var endOfCentralDirectory = this.data.length - 22;
while (endOfCentralDirectory >= 0 && this.getInt(endOfCentralDirectory, 4) != 0x06054b50)
--endOfCentralDirectory;
if (endOfCentralDirectory < 0)
return { 'status' : false, 'error' : 'Invalid data' };
if (this.getInt(endOfCentralDirectory + 4, 2) != 0 || this.getInt(endOfCentralDirectory + 6, 2) != 0)
return { 'status' : false, 'error' : 'No multidisk support' };
var entriesInThisDisk = this.getInt(endOfCentralDirectory + 8, 2);
var centralDirectoryOffset = this.getInt(endOfCentralDirectory + 16, 4);
var globalCommentLength = this.getInt(endOfCentralDirectory + 20, 2);
this.comment = this.data.slice(endOfCentralDirectory + 22, endOfCentralDirectory + 22 + globalCommentLength);
var fileOffset = centralDirectoryOffset;
for (var i = 0; i < entriesInThisDisk; ++i) {
if (this.getInt(fileOffset + 0, 4) != 0x02014b50)
return { 'status' : false, 'error' : 'Invalid data' };
if (this.getInt(fileOffset + 6, 2) > 20)
return { 'status' : false, 'error' : 'Unsupported version' };
if (this.getInt(fileOffset + 8, 2) & 1)
return { 'status' : false, 'error' : 'Encryption not implemented' };
var compressionMethod = this.getInt(fileOffset + 10, 2);
if (compressionMethod != 0 && compressionMethod != 8)
return { 'status' : false, 'error' : 'Unsupported compression method' };
var lastModFileTime = this.getInt(fileOffset + 12, 2);
var lastModFileDate = this.getInt(fileOffset + 14, 2);
var lastModifiedDate = this.getDOSDate(lastModFileDate, lastModFileTime);
var crc = this.getInt(fileOffset + 16, 4);
// TODO: crc
var compressedSize = this.getInt(fileOffset + 20, 4);
var uncompressedSize = this.getInt(fileOffset + 24, 4);
var fileNameLength = this.getInt(fileOffset + 28, 2);
var extraFieldLength = this.getInt(fileOffset + 30, 2);
var fileCommentLength = this.getInt(fileOffset + 32, 2);
var relativeOffsetOfLocalHeader = this.getInt(fileOffset + 42, 4);
var fileName = this.data.slice(fileOffset + 46, fileOffset + 46 + fileNameLength);
var fileComment = this.data.slice(fileOffset + 46 + fileNameLength + extraFieldLength, fileOffset + 46 + fileNameLength + extraFieldLength + fileCommentLength);
if (this.getInt(relativeOffsetOfLocalHeader + 0, 4) != 0x04034b50)
return { 'status' : false, 'error' : 'Invalid data' };
var localFileNameLength = this.getInt(relativeOffsetOfLocalHeader + 26, 2);
var localExtraFieldLength = this.getInt(relativeOffsetOfLocalHeader + 28, 2);
var localFileContent = relativeOffsetOfLocalHeader + 30 + localFileNameLength + localExtraFieldLength;
this.files[fileName] =
{
'fileComment' : fileComment,
'compressionMethod' : compressionMethod,
'compressedSize' : compressedSize,
'uncompressedSize' : uncompressedSize,
'localFileContent' : localFileContent,
'lastModifiedDate' : lastModifiedDate
};
fileOffset += 46 + fileNameLength + extraFieldLength + fileCommentLength;
}
return { 'status' : true }
};
this.read = function(fileName) {
var fileInfo = this.files[fileName];
if (fileInfo) {
if (fileInfo.compressionMethod == 8) {
if (!tinf) {
tinf = new TINF();
tinf.init();
}
var result = tinf.uncompress(this.data, fileInfo.localFileContent);
if (result.status == tinf.OK)
return { 'status' : true, 'data' : result.data };
else
return { 'status' : false, 'error' : result.error };
} else {
return { 'status' : true, 'data' : this.data.slice(fileInfo.localFileContent, fileInfo.localFileContent + fileInfo.uncompressedSize) };
}
}
return { 'status' : false, 'error' : "File '" + fileName + "' doesn't exist in zip" };
};
};
/*
* tinflate - tiny inflate
*
......@@ -200,9 +39,11 @@ function JSUnzip() {
* reading more then 8 bits (needed in some zlib streams)
*/
/* [module] name: TINF */
"use strict";
function TINF() {
function TINF () {
this.OK = 0;
this.DATA_ERROR = (-3);
......
var kbdUtil = (function() {
/* [module] name: KeyboardUtil; requires: KeyTable, keysyms */
var KeyboardUtil = {};
(function() {
"use strict";
function substituteCodepoint(cp) {
......@@ -31,7 +34,7 @@ var kbdUtil = (function() {
function hasShortcutModifier(charModifier, currentModifiers) {
var mods = {};
for (var key in currentModifiers) {
if (parseInt(key) !== XK_Shift_L) {
if (parseInt(key) !== KeyTable.XK_Shift_L) {
mods[key] = currentModifiers[key];
}
}
......@@ -68,15 +71,15 @@ var kbdUtil = (function() {
if (!charModifier) {
if (isMac()) {
// on Mac, Option (AKA Alt) is used as a char modifier
charModifier = [XK_Alt_L];
charModifier = [KeyTable.XK_Alt_L];
}
else if (isWindows()) {
// on Windows, Ctrl+Alt is used as a char modifier
charModifier = [XK_Alt_L, XK_Control_L];
charModifier = [KeyTable.XK_Alt_L, KeyTable.XK_Control_L];
}
else if (isLinux()) {
// on Linux, ISO Level 3 Shift (AltGr) is used as a char modifier
charModifier = [XK_ISO_Level3_Shift];
charModifier = [KeyTable.XK_ISO_Level3_Shift];
}
else {
charModifier = [];
......@@ -84,11 +87,11 @@ var kbdUtil = (function() {
}
var state = {};
state[XK_Control_L] = false;
state[XK_Alt_L] = false;
state[XK_ISO_Level3_Shift] = false;
state[XK_Shift_L] = false;
state[XK_Meta_L] = false;
state[KeyTable.XK_Control_L] = false;
state[KeyTable.XK_Alt_L] = false;
state[KeyTable.XK_ISO_Level3_Shift] = false;
state[KeyTable.XK_Shift_L] = false;
state[KeyTable.XK_Meta_L] = false;
function sync(evt, keysym) {
var result = [];
......@@ -97,29 +100,29 @@ var kbdUtil = (function() {
}
if (evt.ctrlKey !== undefined &&
evt.ctrlKey !== state[XK_Control_L] && keysym !== XK_Control_L) {
state[XK_Control_L] = evt.ctrlKey;
result.push(syncKey(XK_Control_L));
evt.ctrlKey !== state[KeyTable.XK_Control_L] && keysym !== KeyTable.XK_Control_L) {
state[KeyTable.XK_Control_L] = evt.ctrlKey;
result.push(syncKey(KeyTable.XK_Control_L));
}
if (evt.altKey !== undefined &&
evt.altKey !== state[XK_Alt_L] && keysym !== XK_Alt_L) {
state[XK_Alt_L] = evt.altKey;
result.push(syncKey(XK_Alt_L));
evt.altKey !== state[KeyTable.XK_Alt_L] && keysym !== KeyTable.XK_Alt_L) {
state[KeyTable.XK_Alt_L] = evt.altKey;
result.push(syncKey(KeyTable.XK_Alt_L));
}
if (evt.altGraphKey !== undefined &&
evt.altGraphKey !== state[XK_ISO_Level3_Shift] && keysym !== XK_ISO_Level3_Shift) {
state[XK_ISO_Level3_Shift] = evt.altGraphKey;
result.push(syncKey(XK_ISO_Level3_Shift));
evt.altGraphKey !== state[KeyTable.XK_ISO_Level3_Shift] && keysym !== KeyTable.XK_ISO_Level3_Shift) {
state[KeyTable.XK_ISO_Level3_Shift] = evt.altGraphKey;
result.push(syncKey(KeyTable.XK_ISO_Level3_Shift));
}
if (evt.shiftKey !== undefined &&
evt.shiftKey !== state[XK_Shift_L] && keysym !== XK_Shift_L) {
state[XK_Shift_L] = evt.shiftKey;
result.push(syncKey(XK_Shift_L));
evt.shiftKey !== state[KeyTable.XK_Shift_L] && keysym !== KeyTable.XK_Shift_L) {
state[KeyTable.XK_Shift_L] = evt.shiftKey;
result.push(syncKey(KeyTable.XK_Shift_L));
}
if (evt.metaKey !== undefined &&
evt.metaKey !== state[XK_Meta_L] && keysym !== XK_Meta_L) {
state[XK_Meta_L] = evt.metaKey;
result.push(syncKey(XK_Meta_L));
evt.metaKey !== state[KeyTable.XK_Meta_L] && keysym !== KeyTable.XK_Meta_L) {
state[KeyTable.XK_Meta_L] = evt.metaKey;
result.push(syncKey(KeyTable.XK_Meta_L));
}
return result;
}
......@@ -210,21 +213,21 @@ var kbdUtil = (function() {
return shiftPressed ? keycode : keycode + 32; // A-Z
}
if (keycode >= 0x60 && keycode <= 0x69) {
return XK_KP_0 + (keycode - 0x60); // numpad 0-9
return KeyTable.XK_KP_0 + (keycode - 0x60); // numpad 0-9
}
switch(keycode) {
case 0x20: return XK_space;
case 0x6a: return XK_KP_Multiply;
case 0x6b: return XK_KP_Add;
case 0x6c: return XK_KP_Separator;
case 0x6d: return XK_KP_Subtract;
case 0x6e: return XK_KP_Decimal;
case 0x6f: return XK_KP_Divide;
case 0xbb: return XK_plus;
case 0xbc: return XK_comma;
case 0xbd: return XK_minus;
case 0xbe: return XK_period;
case 0x20: return KeyTable.XK_space;
case 0x6a: return KeyTable.XK_KP_Multiply;
case 0x6b: return KeyTable.XK_KP_Add;
case 0x6c: return KeyTable.XK_KP_Separator;
case 0x6d: return KeyTable.XK_KP_Subtract;
case 0x6e: return KeyTable.XK_KP_Decimal;
case 0x6f: return KeyTable.XK_KP_Divide;
case 0xbb: return KeyTable.XK_plus;
case 0xbc: return KeyTable.XK_comma;
case 0xbd: return KeyTable.XK_minus;
case 0xbe: return KeyTable.XK_period;
}
return nonCharacterKey({keyCode: keycode});
......@@ -238,51 +241,50 @@ var kbdUtil = (function() {
var keycode = evt.keyCode;
if (keycode >= 0x70 && keycode <= 0x87) {
return XK_F1 + keycode - 0x70; // F1-F24
return KeyTable.XK_F1 + keycode - 0x70; // F1-F24
}
switch (keycode) {
case 8 : return XK_BackSpace;
case 13 : return XK_Return;
case 8 : return KeyTable.XK_BackSpace;
case 13 : return KeyTable.XK_Return;
case 9 : return XK_Tab;
case 9 : return KeyTable.XK_Tab;
case 27 : return XK_Escape;
case 46 : return XK_Delete;
case 27 : return KeyTable.XK_Escape;
case 46 : return KeyTable.XK_Delete;
case 36 : return XK_Home;
case 35 : return XK_End;
case 33 : return XK_Page_Up;
case 34 : return XK_Page_Down;
case 45 : return XK_Insert;
case 36 : return KeyTable.XK_Home;
case 35 : return KeyTable.XK_End;
case 33 : return KeyTable.XK_Page_Up;
case 34 : return KeyTable.XK_Page_Down;
case 45 : return KeyTable.XK_Insert;
case 37 : return XK_Left;
case 38 : return XK_Up;
case 39 : return XK_Right;
case 40 : return XK_Down;
case 37 : return KeyTable.XK_Left;
case 38 : return KeyTable.XK_Up;
case 39 : return KeyTable.XK_Right;
case 40 : return KeyTable.XK_Down;
case 16 : return XK_Shift_L;
case 17 : return XK_Control_L;
case 18 : return XK_Alt_L; // also: Option-key on Mac
case 16 : return KeyTable.XK_Shift_L;
case 17 : return KeyTable.XK_Control_L;
case 18 : return KeyTable.XK_Alt_L; // also: Option-key on Mac
case 224 : return XK_Meta_L;
case 225 : return XK_ISO_Level3_Shift; // AltGr
case 91 : return XK_Super_L; // also: Windows-key
case 92 : return XK_Super_R; // also: Windows-key
case 93 : return XK_Menu; // also: Windows-Menu, Command on Mac
case 224 : return KeyTable.XK_Meta_L;
case 225 : return KeyTable.XK_ISO_Level3_Shift; // AltGr
case 91 : return KeyTable.XK_Super_L; // also: Windows-key
case 92 : return KeyTable.XK_Super_R; // also: Windows-key
case 93 : return KeyTable.XK_Menu; // also: Windows-Menu, Command on Mac
default: return null;
}
}
return {
hasShortcutModifier : hasShortcutModifier,
hasCharModifier : hasCharModifier,
ModifierSync : ModifierSync,
getKey : getKey,
getKeysym : getKeysym,
keysymFromKeyCode : keysymFromKeyCode,
nonCharacterKey : nonCharacterKey,
substituteCodepoint : substituteCodepoint
};
KeyboardUtil.hasShortcutModifier = hasShortcutModifier;
KeyboardUtil.hasCharModifier = hasCharModifier;
KeyboardUtil.ModifierSync = ModifierSync;
KeyboardUtil.getKey = getKey;
KeyboardUtil.getKeysym = getKeysym;
KeyboardUtil.keysymFromKeyCode = keysymFromKeyCode;
KeyboardUtil.nonCharacterKey = nonCharacterKey;
KeyboardUtil.substituteCodepoint = substituteCodepoint;
})();
// Takes a DOM keyboard event and:
......@@ -292,7 +294,7 @@ var kbdUtil = (function() {
// - 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)
function KeyEventDecoder(modifierState, next) {
KeyboardUtil.KeyEventDecoder = function (modifierState, next) {
"use strict";
function sendAll(evts) {
for (var i = 0; i < evts.length; ++i) {
......@@ -301,18 +303,18 @@ function KeyEventDecoder(modifierState, next) {
}
function process(evt, type) {
var result = {type: type};
var keyId = kbdUtil.getKey(evt);
var keyId = KeyboardUtil.getKey(evt);
if (keyId) {
result.keyId = keyId;
}
var keysym = kbdUtil.getKeysym(evt);
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' || kbdUtil.nonCharacterKey(evt) || hasModifier)) {
if (keysym && (type !== 'keydown' || KeyboardUtil.nonCharacterKey(evt) || hasModifier)) {
result.keysym = keysym;
}
......@@ -321,11 +323,11 @@ function KeyEventDecoder(modifierState, next) {
// Should we prevent the browser from handling the event?
// Doing so on a keydown (in most browsers) prevents keypress from being generated
// so only do that if we have to.
var suppress = !isShift && (type !== 'keydown' || modifierState.hasShortcutModifier() || !!kbdUtil.nonCharacterKey(evt));
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() && !kbdUtil.nonCharacterKey(evt);
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();
......@@ -371,7 +373,7 @@ function KeyEventDecoder(modifierState, next) {
},
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.
......@@ -379,7 +381,7 @@ function KeyEventDecoder(modifierState, next) {
// so when used with the '2' key, Ctrl-Alt counts as a char modifier (and should be escaped), but when used with 'D', it does not.
// The only way we can distinguish these cases is to wait and see if a keypress event arrives
// When we receive a "stall" event, wait a few ms before processing the next keydown. If a keypress has also arrived, merge the two
function VerifyCharModifier(next) {
KeyboardUtil.VerifyCharModifier = function (next) {
"use strict";
var queue = [];
var timer = null;
......@@ -429,14 +431,14 @@ function VerifyCharModifier(next) {
queue.push(evt);
process();
};
}
};
// Keeps track of which keys we (and the server) believe are down
// When a keyup is received, match it against this list, to determine the corresponding keysym(s)
// in some cases, a single key may produce multiple keysyms, so the corresponding keyup event must release all of these chars
// key repeat events should be merged into a single entry.
// Because we can't always identify which entry a keydown or keyup event corresponds to, we sometimes have to guess
function TrackKeyState(next) {
KeyboardUtil.TrackKeyState = function (next) {
"use strict";
var state = [];
......@@ -516,11 +518,11 @@ function TrackKeyState(next) {
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.
function EscapeModifiers(next) {
KeyboardUtil.EscapeModifiers = function (next) {
"use strict";
return function(evt) {
if (evt.type !== 'keydown' || evt.escape === undefined) {
......@@ -540,4 +542,4 @@ function EscapeModifiers(next) {
}
/* jshint shadow: false */
};
}
};
/* [module] name: KeyTable */
var KeyTable = {
XK_VoidSymbol: 0xffffff, /* Void symbol */
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 */
/* Cursor control & motion */
XK_Home: 0xff50,
XK_Left: 0xff51, /* Move left, left arrow */
XK_Up: 0xff52, /* Move up, up arrow */
XK_Right: 0xff53, /* Move right, right arrow */
XK_Down: 0xff54, /* Move down, down arrow */
XK_Prior: 0xff55, /* Prior, previous */
XK_Page_Up: 0xff55,
XK_Next: 0xff56, /* Next */
XK_Page_Down: 0xff56,
XK_End: 0xff57, /* EOL */
XK_Begin: 0xff58, /* BOL */
/* Misc functions */
XK_Select: 0xff60, /* Select, mark */
XK_Print: 0xff61,
XK_Execute: 0xff62, /* Execute, run, do */
XK_Insert: 0xff63, /* Insert, insert here */
XK_Undo: 0xff65,
XK_Redo: 0xff66, /* Redo, again */
XK_Menu: 0xff67,
XK_Find: 0xff68, /* Find, search */
XK_Cancel: 0xff69, /* Cancel, stop, abort, exit */
XK_Help: 0xff6a, /* Help */
XK_Break: 0xff6b,
XK_Mode_switch: 0xff7e, /* Character set switch */
XK_script_switch: 0xff7e, /* Alias for mode_switch */
XK_Num_Lock: 0xff7f,
/* Keypad functions, keypad numbers cleverly chosen to map to ASCII */
XK_KP_Space: 0xff80, /* Space */
XK_KP_Tab: 0xff89,
XK_KP_Enter: 0xff8d, /* Enter */
XK_KP_F1: 0xff91, /* PF1, KP_A, ... */
XK_KP_F2: 0xff92,
XK_KP_F3: 0xff93,
XK_KP_F4: 0xff94,
XK_KP_Home: 0xff95,
XK_KP_Left: 0xff96,
XK_KP_Up: 0xff97,
XK_KP_Right: 0xff98,
XK_KP_Down: 0xff99,
XK_KP_Prior: 0xff9a,
XK_KP_Page_Up: 0xff9a,
XK_KP_Next: 0xff9b,
XK_KP_Page_Down: 0xff9b,
XK_KP_End: 0xff9c,
XK_KP_Begin: 0xff9d,
XK_KP_Insert: 0xff9e,
XK_KP_Delete: 0xff9f,
XK_KP_Equal: 0xffbd, /* Equals */
XK_KP_Multiply: 0xffaa,
XK_KP_Add: 0xffab,
XK_KP_Separator: 0xffac, /* Separator, often comma */
XK_KP_Subtract: 0xffad,
XK_KP_Decimal: 0xffae,
XK_KP_Divide: 0xffaf,
XK_KP_0: 0xffb0,
XK_KP_1: 0xffb1,
XK_KP_2: 0xffb2,
XK_KP_3: 0xffb3,
XK_KP_4: 0xffb4,
XK_KP_5: 0xffb5,
XK_KP_6: 0xffb6,
XK_KP_7: 0xffb7,
XK_KP_8: 0xffb8,
XK_KP_9: 0xffb9,
/*
* Auxiliary functions; note the duplicate definitions for left and right
* function keys; Sun keyboards and a few other manufacturers have such
* 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.
*/
XK_F1: 0xffbe,
XK_F2: 0xffbf,
XK_F3: 0xffc0,
XK_F4: 0xffc1,
XK_F5: 0xffc2,
XK_F6: 0xffc3,
XK_F7: 0xffc4,
XK_F8: 0xffc5,
XK_F9: 0xffc6,
XK_F10: 0xffc7,
XK_F11: 0xffc8,
XK_L1: 0xffc8,
XK_F12: 0xffc9,
XK_L2: 0xffc9,
XK_F13: 0xffca,
XK_L3: 0xffca,
XK_F14: 0xffcb,
XK_L4: 0xffcb,
XK_F15: 0xffcc,
XK_L5: 0xffcc,
XK_F16: 0xffcd,
XK_L6: 0xffcd,
XK_F17: 0xffce,
XK_L7: 0xffce,
XK_F18: 0xffcf,
XK_L8: 0xffcf,
XK_F19: 0xffd0,
XK_L9: 0xffd0,
XK_F20: 0xffd1,
XK_L10: 0xffd1,
XK_F21: 0xffd2,
XK_R1: 0xffd2,
XK_F22: 0xffd3,
XK_R2: 0xffd3,
XK_F23: 0xffd4,
XK_R3: 0xffd4,
XK_F24: 0xffd5,
XK_R4: 0xffd5,
XK_F25: 0xffd6,
XK_R5: 0xffd6,
XK_F26: 0xffd7,
XK_R6: 0xffd7,
XK_F27: 0xffd8,
XK_R7: 0xffd8,
XK_F28: 0xffd9,
XK_R8: 0xffd9,
XK_F29: 0xffda,
XK_R9: 0xffda,
XK_F30: 0xffdb,
XK_R10: 0xffdb,
XK_F31: 0xffdc,
XK_R11: 0xffdc,
XK_F32: 0xffdd,
XK_R12: 0xffdd,
XK_F33: 0xffde,
XK_R13: 0xffde,
XK_F34: 0xffdf,
XK_R14: 0xffdf,
XK_F35: 0xffe0,
XK_R15: 0xffe0,
/* Modifiers */
XK_Shift_L: 0xffe1, /* Left shift */
XK_Shift_R: 0xffe2, /* Right shift */
XK_Control_L: 0xffe3, /* Left control */
XK_Control_R: 0xffe4, /* Right control */
XK_Caps_Lock: 0xffe5, /* Caps lock */
XK_Shift_Lock: 0xffe6, /* Shift lock */
XK_Meta_L: 0xffe7, /* Left meta */
XK_Meta_R: 0xffe8, /* Right meta */
XK_Alt_L: 0xffe9, /* Left alt */
XK_Alt_R: 0xffea, /* Right alt */
XK_Super_L: 0xffeb, /* Left super */
XK_Super_R: 0xffec, /* Right super */
XK_Hyper_L: 0xffed, /* Left hyper */
XK_Hyper_R: 0xffee, /* Right hyper */
XK_ISO_Level3_Shift: 0xfe03, /* AltGr */
/*
* Latin 1
* (ISO/IEC 8859-1: Unicode U+0020..U+00FF)
* Byte 3: 0
*/
XK_space: 0x0020, /* U+0020 SPACE */
XK_exclam: 0x0021, /* U+0021 EXCLAMATION MARK */
XK_quotedbl: 0x0022, /* U+0022 QUOTATION MARK */
XK_numbersign: 0x0023, /* U+0023 NUMBER SIGN */
XK_dollar: 0x0024, /* U+0024 DOLLAR SIGN */
XK_percent: 0x0025, /* U+0025 PERCENT SIGN */
XK_ampersand: 0x0026, /* U+0026 AMPERSAND */
XK_apostrophe: 0x0027, /* U+0027 APOSTROPHE */
XK_quoteright: 0x0027, /* deprecated */
XK_parenleft: 0x0028, /* U+0028 LEFT PARENTHESIS */
XK_parenright: 0x0029, /* U+0029 RIGHT PARENTHESIS */
XK_asterisk: 0x002a, /* U+002A ASTERISK */
XK_plus: 0x002b, /* U+002B PLUS SIGN */
XK_comma: 0x002c, /* U+002C COMMA */
XK_minus: 0x002d, /* U+002D HYPHEN-MINUS */
XK_period: 0x002e, /* U+002E FULL STOP */
XK_slash: 0x002f, /* U+002F SOLIDUS */
XK_0: 0x0030, /* U+0030 DIGIT ZERO */
XK_1: 0x0031, /* U+0031 DIGIT ONE */
XK_2: 0x0032, /* U+0032 DIGIT TWO */
XK_3: 0x0033, /* U+0033 DIGIT THREE */
XK_4: 0x0034, /* U+0034 DIGIT FOUR */
XK_5: 0x0035, /* U+0035 DIGIT FIVE */
XK_6: 0x0036, /* U+0036 DIGIT SIX */
XK_7: 0x0037, /* U+0037 DIGIT SEVEN */
XK_8: 0x0038, /* U+0038 DIGIT EIGHT */
XK_9: 0x0039, /* U+0039 DIGIT NINE */
XK_colon: 0x003a, /* U+003A COLON */
XK_semicolon: 0x003b, /* U+003B SEMICOLON */
XK_less: 0x003c, /* U+003C LESS-THAN SIGN */
XK_equal: 0x003d, /* U+003D EQUALS SIGN */
XK_greater: 0x003e, /* U+003E GREATER-THAN SIGN */
XK_question: 0x003f, /* U+003F QUESTION MARK */
XK_at: 0x0040, /* U+0040 COMMERCIAL AT */
XK_A: 0x0041, /* U+0041 LATIN CAPITAL LETTER A */
XK_B: 0x0042, /* U+0042 LATIN CAPITAL LETTER B */
XK_C: 0x0043, /* U+0043 LATIN CAPITAL LETTER C */
XK_D: 0x0044, /* U+0044 LATIN CAPITAL LETTER D */
XK_E: 0x0045, /* U+0045 LATIN CAPITAL LETTER E */
XK_F: 0x0046, /* U+0046 LATIN CAPITAL LETTER F */
XK_G: 0x0047, /* U+0047 LATIN CAPITAL LETTER G */
XK_H: 0x0048, /* U+0048 LATIN CAPITAL LETTER H */
XK_I: 0x0049, /* U+0049 LATIN CAPITAL LETTER I */
XK_J: 0x004a, /* U+004A LATIN CAPITAL LETTER J */
XK_K: 0x004b, /* U+004B LATIN CAPITAL LETTER K */
XK_L: 0x004c, /* U+004C LATIN CAPITAL LETTER L */
XK_M: 0x004d, /* U+004D LATIN CAPITAL LETTER M */
XK_N: 0x004e, /* U+004E LATIN CAPITAL LETTER N */
XK_O: 0x004f, /* U+004F LATIN CAPITAL LETTER O */
XK_P: 0x0050, /* U+0050 LATIN CAPITAL LETTER P */
XK_Q: 0x0051, /* U+0051 LATIN CAPITAL LETTER Q */
XK_R: 0x0052, /* U+0052 LATIN CAPITAL LETTER R */
XK_S: 0x0053, /* U+0053 LATIN CAPITAL LETTER S */
XK_T: 0x0054, /* U+0054 LATIN CAPITAL LETTER T */
XK_U: 0x0055, /* U+0055 LATIN CAPITAL LETTER U */
XK_V: 0x0056, /* U+0056 LATIN CAPITAL LETTER V */
XK_W: 0x0057, /* U+0057 LATIN CAPITAL LETTER W */
XK_X: 0x0058, /* U+0058 LATIN CAPITAL LETTER X */
XK_Y: 0x0059, /* U+0059 LATIN CAPITAL LETTER Y */
XK_Z: 0x005a, /* U+005A LATIN CAPITAL LETTER Z */
XK_bracketleft: 0x005b, /* U+005B LEFT SQUARE BRACKET */
XK_backslash: 0x005c, /* U+005C REVERSE SOLIDUS */
XK_bracketright: 0x005d, /* U+005D RIGHT SQUARE BRACKET */
XK_asciicircum: 0x005e, /* U+005E CIRCUMFLEX ACCENT */
XK_underscore: 0x005f, /* U+005F LOW LINE */
XK_grave: 0x0060, /* U+0060 GRAVE ACCENT */
XK_quoteleft: 0x0060, /* deprecated */
XK_a: 0x0061, /* U+0061 LATIN SMALL LETTER A */
XK_b: 0x0062, /* U+0062 LATIN SMALL LETTER B */
XK_c: 0x0063, /* U+0063 LATIN SMALL LETTER C */
XK_d: 0x0064, /* U+0064 LATIN SMALL LETTER D */
XK_e: 0x0065, /* U+0065 LATIN SMALL LETTER E */
XK_f: 0x0066, /* U+0066 LATIN SMALL LETTER F */
XK_g: 0x0067, /* U+0067 LATIN SMALL LETTER G */
XK_h: 0x0068, /* U+0068 LATIN SMALL LETTER H */
XK_i: 0x0069, /* U+0069 LATIN SMALL LETTER I */
XK_j: 0x006a, /* U+006A LATIN SMALL LETTER J */
XK_k: 0x006b, /* U+006B LATIN SMALL LETTER K */
XK_l: 0x006c, /* U+006C LATIN SMALL LETTER L */
XK_m: 0x006d, /* U+006D LATIN SMALL LETTER M */
XK_n: 0x006e, /* U+006E LATIN SMALL LETTER N */
XK_o: 0x006f, /* U+006F LATIN SMALL LETTER O */
XK_p: 0x0070, /* U+0070 LATIN SMALL LETTER P */
XK_q: 0x0071, /* U+0071 LATIN SMALL LETTER Q */
XK_r: 0x0072, /* U+0072 LATIN SMALL LETTER R */
XK_s: 0x0073, /* U+0073 LATIN SMALL LETTER S */
XK_t: 0x0074, /* U+0074 LATIN SMALL LETTER T */
XK_u: 0x0075, /* U+0075 LATIN SMALL LETTER U */
XK_v: 0x0076, /* U+0076 LATIN SMALL LETTER V */
XK_w: 0x0077, /* U+0077 LATIN SMALL LETTER W */
XK_x: 0x0078, /* U+0078 LATIN SMALL LETTER X */
XK_y: 0x0079, /* U+0079 LATIN SMALL LETTER Y */
XK_z: 0x007a, /* U+007A LATIN SMALL LETTER Z */
XK_braceleft: 0x007b, /* U+007B LEFT CURLY BRACKET */
XK_bar: 0x007c, /* U+007C VERTICAL LINE */
XK_braceright: 0x007d, /* U+007D RIGHT CURLY BRACKET */
XK_asciitilde: 0x007e, /* U+007E TILDE */
XK_nobreakspace: 0x00a0, /* U+00A0 NO-BREAK SPACE */
XK_exclamdown: 0x00a1, /* U+00A1 INVERTED EXCLAMATION MARK */
XK_cent: 0x00a2, /* U+00A2 CENT SIGN */
XK_sterling: 0x00a3, /* U+00A3 POUND SIGN */
XK_currency: 0x00a4, /* U+00A4 CURRENCY SIGN */
XK_yen: 0x00a5, /* U+00A5 YEN SIGN */
XK_brokenbar: 0x00a6, /* U+00A6 BROKEN BAR */
XK_section: 0x00a7, /* U+00A7 SECTION SIGN */
XK_diaeresis: 0x00a8, /* U+00A8 DIAERESIS */
XK_copyright: 0x00a9, /* U+00A9 COPYRIGHT SIGN */
XK_ordfeminine: 0x00aa, /* U+00AA FEMININE ORDINAL INDICATOR */
XK_guillemotleft: 0x00ab, /* U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */
XK_notsign: 0x00ac, /* U+00AC NOT SIGN */
XK_hyphen: 0x00ad, /* U+00AD SOFT HYPHEN */
XK_registered: 0x00ae, /* U+00AE REGISTERED SIGN */
XK_macron: 0x00af, /* U+00AF MACRON */
XK_degree: 0x00b0, /* U+00B0 DEGREE SIGN */
XK_plusminus: 0x00b1, /* U+00B1 PLUS-MINUS SIGN */
XK_twosuperior: 0x00b2, /* U+00B2 SUPERSCRIPT TWO */
XK_threesuperior: 0x00b3, /* U+00B3 SUPERSCRIPT THREE */
XK_acute: 0x00b4, /* U+00B4 ACUTE ACCENT */
XK_mu: 0x00b5, /* U+00B5 MICRO SIGN */
XK_paragraph: 0x00b6, /* U+00B6 PILCROW SIGN */
XK_periodcentered: 0x00b7, /* U+00B7 MIDDLE DOT */
XK_cedilla: 0x00b8, /* U+00B8 CEDILLA */
XK_onesuperior: 0x00b9, /* U+00B9 SUPERSCRIPT ONE */
XK_masculine: 0x00ba, /* U+00BA MASCULINE ORDINAL INDICATOR */
XK_guillemotright: 0x00bb, /* U+00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */
XK_onequarter: 0x00bc, /* U+00BC VULGAR FRACTION ONE QUARTER */
XK_onehalf: 0x00bd, /* U+00BD VULGAR FRACTION ONE HALF */
XK_threequarters: 0x00be, /* U+00BE VULGAR FRACTION THREE QUARTERS */
XK_questiondown: 0x00bf, /* U+00BF INVERTED QUESTION MARK */
XK_Agrave: 0x00c0, /* U+00C0 LATIN CAPITAL LETTER A WITH GRAVE */
XK_Aacute: 0x00c1, /* U+00C1 LATIN CAPITAL LETTER A WITH ACUTE */
XK_Acircumflex: 0x00c2, /* U+00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */
XK_Atilde: 0x00c3, /* U+00C3 LATIN CAPITAL LETTER A WITH TILDE */
XK_Adiaeresis: 0x00c4, /* U+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS */
XK_Aring: 0x00c5, /* U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE */
XK_AE: 0x00c6, /* U+00C6 LATIN CAPITAL LETTER AE */
XK_Ccedilla: 0x00c7, /* U+00C7 LATIN CAPITAL LETTER C WITH CEDILLA */
XK_Egrave: 0x00c8, /* U+00C8 LATIN CAPITAL LETTER E WITH GRAVE */
XK_Eacute: 0x00c9, /* U+00C9 LATIN CAPITAL LETTER E WITH ACUTE */
XK_Ecircumflex: 0x00ca, /* U+00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX */
XK_Ediaeresis: 0x00cb, /* U+00CB LATIN CAPITAL LETTER E WITH DIAERESIS */
XK_Igrave: 0x00cc, /* U+00CC LATIN CAPITAL LETTER I WITH GRAVE */
XK_Iacute: 0x00cd, /* U+00CD LATIN CAPITAL LETTER I WITH ACUTE */
XK_Icircumflex: 0x00ce, /* U+00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX */
XK_Idiaeresis: 0x00cf, /* U+00CF LATIN CAPITAL LETTER I WITH DIAERESIS */
XK_ETH: 0x00d0, /* U+00D0 LATIN CAPITAL LETTER ETH */
XK_Eth: 0x00d0, /* deprecated */
XK_Ntilde: 0x00d1, /* U+00D1 LATIN CAPITAL LETTER N WITH TILDE */
XK_Ograve: 0x00d2, /* U+00D2 LATIN CAPITAL LETTER O WITH GRAVE */
XK_Oacute: 0x00d3, /* U+00D3 LATIN CAPITAL LETTER O WITH ACUTE */
XK_Ocircumflex: 0x00d4, /* U+00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */
XK_Otilde: 0x00d5, /* U+00D5 LATIN CAPITAL LETTER O WITH TILDE */
XK_Odiaeresis: 0x00d6, /* U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS */
XK_multiply: 0x00d7, /* U+00D7 MULTIPLICATION SIGN */
XK_Oslash: 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_Uacute: 0x00da, /* U+00DA LATIN CAPITAL LETTER U WITH ACUTE */
XK_Ucircumflex: 0x00db, /* U+00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX */
XK_Udiaeresis: 0x00dc, /* U+00DC LATIN CAPITAL LETTER U WITH DIAERESIS */
XK_Yacute: 0x00dd, /* U+00DD LATIN CAPITAL LETTER Y WITH ACUTE */
XK_THORN: 0x00de, /* U+00DE LATIN CAPITAL LETTER THORN */
XK_Thorn: 0x00de, /* deprecated */
XK_ssharp: 0x00df, /* U+00DF LATIN SMALL LETTER SHARP S */
XK_agrave: 0x00e0, /* U+00E0 LATIN SMALL LETTER A WITH GRAVE */
XK_aacute: 0x00e1, /* U+00E1 LATIN SMALL LETTER A WITH ACUTE */
XK_acircumflex: 0x00e2, /* U+00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX */
XK_atilde: 0x00e3, /* U+00E3 LATIN SMALL LETTER A WITH TILDE */
XK_adiaeresis: 0x00e4, /* U+00E4 LATIN SMALL LETTER A WITH DIAERESIS */
XK_aring: 0x00e5, /* U+00E5 LATIN SMALL LETTER A WITH RING ABOVE */
XK_ae: 0x00e6, /* U+00E6 LATIN SMALL LETTER AE */
XK_ccedilla: 0x00e7, /* U+00E7 LATIN SMALL LETTER C WITH CEDILLA */
XK_egrave: 0x00e8, /* U+00E8 LATIN SMALL LETTER E WITH GRAVE */
XK_eacute: 0x00e9, /* U+00E9 LATIN SMALL LETTER E WITH ACUTE */
XK_ecircumflex: 0x00ea, /* U+00EA LATIN SMALL LETTER E WITH CIRCUMFLEX */
XK_ediaeresis: 0x00eb, /* U+00EB LATIN SMALL LETTER E WITH DIAERESIS */
XK_igrave: 0x00ec, /* U+00EC LATIN SMALL LETTER I WITH GRAVE */
XK_iacute: 0x00ed, /* U+00ED LATIN SMALL LETTER I WITH ACUTE */
XK_icircumflex: 0x00ee, /* U+00EE LATIN SMALL LETTER I WITH CIRCUMFLEX */
XK_idiaeresis: 0x00ef, /* U+00EF LATIN SMALL LETTER I WITH DIAERESIS */
XK_eth: 0x00f0, /* U+00F0 LATIN SMALL LETTER ETH */
XK_ntilde: 0x00f1, /* U+00F1 LATIN SMALL LETTER N WITH TILDE */
XK_ograve: 0x00f2, /* U+00F2 LATIN SMALL LETTER O WITH GRAVE */
XK_oacute: 0x00f3, /* U+00F3 LATIN SMALL LETTER O WITH ACUTE */
XK_ocircumflex: 0x00f4, /* U+00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX */
XK_otilde: 0x00f5, /* U+00F5 LATIN SMALL LETTER O WITH TILDE */
XK_odiaeresis: 0x00f6, /* U+00F6 LATIN SMALL LETTER O WITH DIAERESIS */
XK_division: 0x00f7, /* U+00F7 DIVISION SIGN */
XK_oslash: 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_uacute: 0x00fa, /* U+00FA LATIN SMALL LETTER U WITH ACUTE */
XK_ucircumflex: 0x00fb, /* U+00FB LATIN SMALL LETTER U WITH CIRCUMFLEX */
XK_udiaeresis: 0x00fc, /* U+00FC LATIN SMALL LETTER U WITH DIAERESIS */
XK_yacute: 0x00fd, /* U+00FD LATIN SMALL LETTER Y WITH ACUTE */
XK_thorn: 0x00fe, /* U+00FE LATIN SMALL LETTER THORN */
XK_ydiaeresis: 0x00ff, /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */
};
/* [module] Name: keysyms */
// This file describes mappings from Unicode codepoints to the keysym values
// (and optionally, key names) expected by the RFB protocol
// How this file was generated:
......
......@@ -10,8 +10,10 @@
* (c) 2012 Michael Tinglof, Joe Balaz, Les Piech (Mercuri.ca)
*/
/* [module] Name: RFB ; Requires: Util, Display, Keyboard, Mouse, Websock, Base64, DES, KeyTable, TINF */
/*jslint white: false, browser: true */
/*global window, Util, Display, Keyboard, Mouse, Websock, Websock_native, Base64, DES */
/*global window, Util, Display, Keyboard, Mouse, Websock, Base64, DES, KeyTable */
var RFB;
......@@ -213,21 +215,8 @@ var RFB;
this._init_vars();
var rmode = this._display.get_render_mode();
if (Websock_native) {
Util.Info("Using native WebSockets");
this._updateState('loaded', 'noVNC ready: native WebSockets, ' + rmode);
} else {
Util.Warn("Using web-socket-js bridge. Flash version: " + Util.Flash.version);
if (!Util.Flash || Util.Flash.version < 9) {
this._cleanupSocket('fatal');
throw new Exception("WebSockets or <a href='http://get.adobe.com/flashplayer'>Adobe Flash</a> is required");
} else if (document.location.href.substr(0, 7) === 'file://') {
this._cleanupSocket('fatal');
throw new Exception("'file://' URL is incompatible with Adobe Flash");
} else {
this._updateState('loaded', 'noVNC ready: WebSockets emulation, ' + rmode);
}
}
Util.Info("Using native WebSockets");
this._updateState('loaded', 'noVNC ready: native WebSockets, ' + rmode);
Util.Debug("<< RFB.constructor");
};
......@@ -265,12 +254,12 @@ var RFB;
Util.Info("Sending Ctrl-Alt-Del");
var arr = [];
arr = arr.concat(RFB.messages.keyEvent(XK_Control_L, 1));
arr = arr.concat(RFB.messages.keyEvent(XK_Alt_L, 1));
arr = arr.concat(RFB.messages.keyEvent(XK_Delete, 1));
arr = arr.concat(RFB.messages.keyEvent(XK_Delete, 0));
arr = arr.concat(RFB.messages.keyEvent(XK_Alt_L, 0));
arr = arr.concat(RFB.messages.keyEvent(XK_Control_L, 0));
arr = arr.concat(RFB.messages.keyEvent(KeyTable.XK_Control_L, 1));
arr = arr.concat(RFB.messages.keyEvent(KeyTable.XK_Alt_L, 1));
arr = arr.concat(RFB.messages.keyEvent(KeyTable.XK_Delete, 1));
arr = arr.concat(RFB.messages.keyEvent(KeyTable.XK_Delete, 0));
arr = arr.concat(RFB.messages.keyEvent(KeyTable.XK_Alt_L, 0));
arr = arr.concat(RFB.messages.keyEvent(KeyTable.XK_Control_L, 0));
this._sock.send(arr);
},
......
......@@ -6,6 +6,8 @@
* See README.md for usage and integration instructions.
*/
/* [module] Name: Util */
/* jshint white: false, nonstandard: true */
/*global window, console, document, navigator, ActiveXObject, INCLUDE_URI */
......@@ -376,12 +378,12 @@ Util.decodeUTF8 = function (utf8string) {
// 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).
Util.get_include_uri = function () {
return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI : "include/";
Util.get_include_uri = function (root_dir) {
return (typeof INCLUDE_URI !== "undefined") ? INCLUDE_URI + root_dir + '/' : root_dir + '/';
};
Util._loading_scripts = [];
Util._pending_scripts = [];
Util.load_scripts = function (files) {
Util.load_scripts = function (files_by_dir) {
"use strict";
var head = document.getElementsByTagName('head')[0], script,
ls = Util._loading_scripts, ps = Util._pending_scripts;
......@@ -410,25 +412,32 @@ Util.load_scripts = function (files) {
}
};
for (var f = 0; f < files.length; f++) {
script = document.createElement('script');
script.type = 'text/javascript';
script.src = Util.get_include_uri() + 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);
var root_dirs = Object.keys(files_by_dir);
for (var d = 0; d < root_dirs.length; d++) {
var root_dir = root_dirs[d];
var files = files_by_dir[root_dir];
for (var f = 0; f < files.length; f++) {
script = document.createElement('script');
script.type = 'text/javascript';
script.src = Util.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);
}
ps.push(script);
}
};
......
......@@ -14,40 +14,12 @@
* read binary data off of the receive queue.
*/
/* [module] Name: Websock ; Requires: Util, Base64 */
/*jslint browser: true, bitwise: true */
/*global Util, Base64 */
// Load Flash WebSocket emulator if needed
// To force WebSocket emulator even when native WebSocket available
//window.WEB_SOCKET_FORCE_FLASH = true;
// To enable WebSocket emulator debug:
//window.WEB_SOCKET_DEBUG=1;
if (window.WebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
Websock_native = true;
} else if (window.MozWebSocket && !window.WEB_SOCKET_FORCE_FLASH) {
Websock_native = true;
window.WebSocket = window.MozWebSocket;
} else {
/* no builtin WebSocket so load web_socket.js */
Websock_native = false;
(function () {
window.WEB_SOCKET_SWF_LOCATION = Util.get_include_uri() +
"web-socket-js/WebSocketMain.swf";
if (Util.Engine.trident) {
Util.Debug("Forcing uncached load of WebSocketMain.swf");
window.WEB_SOCKET_SWF_LOCATION += "?" + Math.random();
}
Util.load_scripts(["web-socket-js/swfobject.js",
"web-socket-js/web_socket.js"]);
})();
}
function Websock() {
var Websock = function () {
"use strict";
this._websocket = null; // WebSocket object
......@@ -65,7 +37,7 @@ function Websock() {
'close': function () {},
'error': function () {}
};
}
};
(function () {
"use strict";
......
/*
Copyright 2012 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Author: Boris Smus (smus@chromium.org)
*/
(function(exports) {
// Define some local variables here.
var socket = chrome.socket || chrome.experimental.socket;
var dns = chrome.experimental.dns;
/**
* Creates an instance of the client
*
* @param {String} host The remote host to connect to
* @param {Number} port The port to connect to at the remote host
*/
function TcpClient(host, port, pollInterval) {
this.host = host;
this.port = port;
this.pollInterval = pollInterval || 15;
// Callback functions.
this.callbacks = {
connect: null, // Called when socket is connected.
disconnect: null, // Called when socket is disconnected.
recvBuffer: null, // Called (as ArrayBuffer) when client receives data from server.
recvString: null, // Called (as string) when client receives data from server.
sent: null // Called when client sends data to server.
};
// Socket.
this.socketId = null;
this.isConnected = false;
log('initialized tcp client');
}
/**
* Connects to the TCP socket, and creates an open socket.
*
* @see http://developer.chrome.com/trunk/apps/socket.html#method-create
* @param {Function} callback The function to call on connection
*/
TcpClient.prototype.connect = function(callback) {
// First resolve the hostname to an IP.
dns.resolve(this.host, function(result) {
this.addr = result.address;
socket.create('tcp', {}, this._onCreate.bind(this));
// Register connect callback.
this.callbacks.connect = callback;
}.bind(this));
};
/**
* Sends an arraybuffer/view down the wire to the remote side
*
* @see http://developer.chrome.com/trunk/apps/socket.html#method-write
* @param {String} msg The arraybuffer/view to send
* @param {Function} callback The function to call when the message has sent
*/
TcpClient.prototype.sendBuffer = function(buf, callback) {
if (buf.buffer) {
buf = buf.buffer;
}
/*
// Debug
var bytes = [], u8 = new Uint8Array(buf);
for (var i = 0; i < u8.length; i++) {
bytes.push(u8[i]);
}
log("sending bytes: " + (bytes.join(',')));
*/
socket.write(this.socketId, buf, this._onWriteComplete.bind(this));
// Register sent callback.
this.callbacks.sent = callback;
};
/**
* Sends a string down the wire to the remote side
*
* @see http://developer.chrome.com/trunk/apps/socket.html#method-write
* @param {String} msg The string to send
* @param {Function} callback The function to call when the message has sent
*/
TcpClient.prototype.sendString = function(msg, callback) {
/*
// Debug
log("sending string: " + msg);
*/
this._stringToArrayBuffer(msg, function(arrayBuffer) {
socket.write(this.socketId, arrayBuffer, this._onWriteComplete.bind(this));
}.bind(this));
// Register sent callback.
this.callbacks.sent = callback;
};
/**
* Sets the callback for when a message is received
*
* @param {Function} callback The function to call when a message has arrived
* @param {String} type The callback argument type: "arraybuffer" or "string"
*/
TcpClient.prototype.addResponseListener = function(callback, type) {
if (typeof type === "undefined") {
type = "arraybuffer";
}
// Register received callback.
if (type === "string") {
this.callbacks.recvString = callback;
} else {
this.callbacks.recvBuffer = callback;
}
};
/**
* Sets the callback for when the socket disconnects
*
* @param {Function} callback The function to call when the socket disconnects
* @param {String} type The callback argument type: "arraybuffer" or "string"
*/
TcpClient.prototype.addDisconnectListener = function(callback) {
// Register disconnect callback.
this.callbacks.disconnect = callback;
};
/**
* Disconnects from the remote side
*
* @see http://developer.chrome.com/trunk/apps/socket.html#method-disconnect
*/
TcpClient.prototype.disconnect = function() {
if (this.isConnected) {
this.isConnected = false;
socket.disconnect(this.socketId);
if (this.callbacks.disconnect) {
this.callbacks.disconnect();
}
log('socket disconnected');
}
};
/**
* The callback function used for when we attempt to have Chrome
* create a socket. If the socket is successfully created
* we go ahead and connect to the remote side.
*
* @private
* @see http://developer.chrome.com/trunk/apps/socket.html#method-connect
* @param {Object} createInfo The socket details
*/
TcpClient.prototype._onCreate = function(createInfo) {
this.socketId = createInfo.socketId;
if (this.socketId > 0) {
socket.connect(this.socketId, this.addr, this.port, this._onConnectComplete.bind(this));
} else {
error('Unable to create socket');
}
};
/**
* The callback function used for when we attempt to have Chrome
* connect to the remote side. If a successful connection is
* made then polling starts to check for data to read
*
* @private
* @param {Number} resultCode Indicates whether the connection was successful
*/
TcpClient.prototype._onConnectComplete = function(resultCode) {
// Start polling for reads.
this.isConnected = true;
setTimeout(this._periodicallyRead.bind(this), this.pollInterval);
if (this.callbacks.connect) {
log('connect complete');
this.callbacks.connect();
}
log('onConnectComplete');
};
/**
* Checks for new data to read from the socket
*
* @see http://developer.chrome.com/trunk/apps/socket.html#method-read
*/
TcpClient.prototype._periodicallyRead = function() {
var that = this;
socket.getInfo(this.socketId, function (info) {
if (info.connected) {
setTimeout(that._periodicallyRead.bind(that), that.pollInterval);
socket.read(that.socketId, null, that._onDataRead.bind(that));
} else if (that.isConnected) {
log('socket disconnect detected');
that.disconnect();
}
});
};
/**
* Callback function for when data has been read from the socket.
* Converts the array buffer that is read in to a string
* and sends it on for further processing by passing it to
* the previously assigned callback function.
*
* @private
* @see TcpClient.prototype.addResponseListener
* @param {Object} readInfo The incoming message
*/
TcpClient.prototype._onDataRead = function(readInfo) {
// Call received callback if there's data in the response.
if (readInfo.resultCode > 0) {
log('onDataRead');
/*
// Debug
var bytes = [], u8 = new Uint8Array(readInfo.data);
for (var i = 0; i < u8.length; i++) {
bytes.push(u8[i]);
}
log("received bytes: " + (bytes.join(',')));
*/
if (this.callbacks.recvBuffer) {
// Return raw ArrayBuffer directly.
this.callbacks.recvBuffer(readInfo.data);
}
if (this.callbacks.recvString) {
// Convert ArrayBuffer to string.
this._arrayBufferToString(readInfo.data, function(str) {
this.callbacks.recvString(str);
}.bind(this));
}
// Trigger another read right away
setTimeout(this._periodicallyRead.bind(this), 0);
}
};
/**
* Callback for when data has been successfully
* written to the socket.
*
* @private
* @param {Object} writeInfo The outgoing message
*/
TcpClient.prototype._onWriteComplete = function(writeInfo) {
log('onWriteComplete');
// Call sent callback.
if (this.callbacks.sent) {
this.callbacks.sent(writeInfo);
}
};
/**
* Converts an array buffer to a string
*
* @private
* @param {ArrayBuffer} buf The buffer to convert
* @param {Function} callback The function to call when conversion is complete
*/
TcpClient.prototype._arrayBufferToString = function(buf, callback) {
var bb = new Blob([new Uint8Array(buf)]);
var f = new FileReader();
f.onload = function(e) {
callback(e.target.result);
};
f.readAsText(bb);
};
/**
* Converts a string to an array buffer
*
* @private
* @param {String} str The string to convert
* @param {Function} callback The function to call when conversion is complete
*/
TcpClient.prototype._stringToArrayBuffer = function(str, callback) {
var bb = new Blob([str]);
var f = new FileReader();
f.onload = function(e) {
callback(e.target.result);
};
f.readAsArrayBuffer(bb);
};
/**
* Wrapper function for logging
*/
function log(msg) {
console.log(msg);
}
/**
* Wrapper function for error logging
*/
function error(msg) {
console.error(msg);
}
exports.TcpClient = TcpClient;
})(window);
var XK_VoidSymbol = 0xffffff, /* Void symbol */
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 */
/* Cursor control & motion */
XK_Home = 0xff50,
XK_Left = 0xff51, /* Move left, left arrow */
XK_Up = 0xff52, /* Move up, up arrow */
XK_Right = 0xff53, /* Move right, right arrow */
XK_Down = 0xff54, /* Move down, down arrow */
XK_Prior = 0xff55, /* Prior, previous */
XK_Page_Up = 0xff55,
XK_Next = 0xff56, /* Next */
XK_Page_Down = 0xff56,
XK_End = 0xff57, /* EOL */
XK_Begin = 0xff58, /* BOL */
/* Misc functions */
XK_Select = 0xff60, /* Select, mark */
XK_Print = 0xff61,
XK_Execute = 0xff62, /* Execute, run, do */
XK_Insert = 0xff63, /* Insert, insert here */
XK_Undo = 0xff65,
XK_Redo = 0xff66, /* Redo, again */
XK_Menu = 0xff67,
XK_Find = 0xff68, /* Find, search */
XK_Cancel = 0xff69, /* Cancel, stop, abort, exit */
XK_Help = 0xff6a, /* Help */
XK_Break = 0xff6b,
XK_Mode_switch = 0xff7e, /* Character set switch */
XK_script_switch = 0xff7e, /* Alias for mode_switch */
XK_Num_Lock = 0xff7f,
/* Keypad functions, keypad numbers cleverly chosen to map to ASCII */
XK_KP_Space = 0xff80, /* Space */
XK_KP_Tab = 0xff89,
XK_KP_Enter = 0xff8d, /* Enter */
XK_KP_F1 = 0xff91, /* PF1, KP_A, ... */
XK_KP_F2 = 0xff92,
XK_KP_F3 = 0xff93,
XK_KP_F4 = 0xff94,
XK_KP_Home = 0xff95,
XK_KP_Left = 0xff96,
XK_KP_Up = 0xff97,
XK_KP_Right = 0xff98,
XK_KP_Down = 0xff99,
XK_KP_Prior = 0xff9a,
XK_KP_Page_Up = 0xff9a,
XK_KP_Next = 0xff9b,
XK_KP_Page_Down = 0xff9b,
XK_KP_End = 0xff9c,
XK_KP_Begin = 0xff9d,
XK_KP_Insert = 0xff9e,
XK_KP_Delete = 0xff9f,
XK_KP_Equal = 0xffbd, /* Equals */
XK_KP_Multiply = 0xffaa,
XK_KP_Add = 0xffab,
XK_KP_Separator = 0xffac, /* Separator, often comma */
XK_KP_Subtract = 0xffad,
XK_KP_Decimal = 0xffae,
XK_KP_Divide = 0xffaf,
XK_KP_0 = 0xffb0,
XK_KP_1 = 0xffb1,
XK_KP_2 = 0xffb2,
XK_KP_3 = 0xffb3,
XK_KP_4 = 0xffb4,
XK_KP_5 = 0xffb5,
XK_KP_6 = 0xffb6,
XK_KP_7 = 0xffb7,
XK_KP_8 = 0xffb8,
XK_KP_9 = 0xffb9,
/*
* Auxiliary functions; note the duplicate definitions for left and right
* function keys; Sun keyboards and a few other manufacturers have such
* 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.
*/
XK_F1 = 0xffbe,
XK_F2 = 0xffbf,
XK_F3 = 0xffc0,
XK_F4 = 0xffc1,
XK_F5 = 0xffc2,
XK_F6 = 0xffc3,
XK_F7 = 0xffc4,
XK_F8 = 0xffc5,
XK_F9 = 0xffc6,
XK_F10 = 0xffc7,
XK_F11 = 0xffc8,
XK_L1 = 0xffc8,
XK_F12 = 0xffc9,
XK_L2 = 0xffc9,
XK_F13 = 0xffca,
XK_L3 = 0xffca,
XK_F14 = 0xffcb,
XK_L4 = 0xffcb,
XK_F15 = 0xffcc,
XK_L5 = 0xffcc,
XK_F16 = 0xffcd,
XK_L6 = 0xffcd,
XK_F17 = 0xffce,
XK_L7 = 0xffce,
XK_F18 = 0xffcf,
XK_L8 = 0xffcf,
XK_F19 = 0xffd0,
XK_L9 = 0xffd0,
XK_F20 = 0xffd1,
XK_L10 = 0xffd1,
XK_F21 = 0xffd2,
XK_R1 = 0xffd2,
XK_F22 = 0xffd3,
XK_R2 = 0xffd3,
XK_F23 = 0xffd4,
XK_R3 = 0xffd4,
XK_F24 = 0xffd5,
XK_R4 = 0xffd5,
XK_F25 = 0xffd6,
XK_R5 = 0xffd6,
XK_F26 = 0xffd7,
XK_R6 = 0xffd7,
XK_F27 = 0xffd8,
XK_R7 = 0xffd8,
XK_F28 = 0xffd9,
XK_R8 = 0xffd9,
XK_F29 = 0xffda,
XK_R9 = 0xffda,
XK_F30 = 0xffdb,
XK_R10 = 0xffdb,
XK_F31 = 0xffdc,
XK_R11 = 0xffdc,
XK_F32 = 0xffdd,
XK_R12 = 0xffdd,
XK_F33 = 0xffde,
XK_R13 = 0xffde,
XK_F34 = 0xffdf,
XK_R14 = 0xffdf,
XK_F35 = 0xffe0,
XK_R15 = 0xffe0,
/* Modifiers */
XK_Shift_L = 0xffe1, /* Left shift */
XK_Shift_R = 0xffe2, /* Right shift */
XK_Control_L = 0xffe3, /* Left control */
XK_Control_R = 0xffe4, /* Right control */
XK_Caps_Lock = 0xffe5, /* Caps lock */
XK_Shift_Lock = 0xffe6, /* Shift lock */
XK_Meta_L = 0xffe7, /* Left meta */
XK_Meta_R = 0xffe8, /* Right meta */
XK_Alt_L = 0xffe9, /* Left alt */
XK_Alt_R = 0xffea, /* Right alt */
XK_Super_L = 0xffeb, /* Left super */
XK_Super_R = 0xffec, /* Right super */
XK_Hyper_L = 0xffed, /* Left hyper */
XK_Hyper_R = 0xffee, /* Right hyper */
XK_ISO_Level3_Shift = 0xfe03, /* AltGr */
/*
* Latin 1
* (ISO/IEC 8859-1 = Unicode U+0020..U+00FF)
* Byte 3 = 0
*/
XK_space = 0x0020, /* U+0020 SPACE */
XK_exclam = 0x0021, /* U+0021 EXCLAMATION MARK */
XK_quotedbl = 0x0022, /* U+0022 QUOTATION MARK */
XK_numbersign = 0x0023, /* U+0023 NUMBER SIGN */
XK_dollar = 0x0024, /* U+0024 DOLLAR SIGN */
XK_percent = 0x0025, /* U+0025 PERCENT SIGN */
XK_ampersand = 0x0026, /* U+0026 AMPERSAND */
XK_apostrophe = 0x0027, /* U+0027 APOSTROPHE */
XK_quoteright = 0x0027, /* deprecated */
XK_parenleft = 0x0028, /* U+0028 LEFT PARENTHESIS */
XK_parenright = 0x0029, /* U+0029 RIGHT PARENTHESIS */
XK_asterisk = 0x002a, /* U+002A ASTERISK */
XK_plus = 0x002b, /* U+002B PLUS SIGN */
XK_comma = 0x002c, /* U+002C COMMA */
XK_minus = 0x002d, /* U+002D HYPHEN-MINUS */
XK_period = 0x002e, /* U+002E FULL STOP */
XK_slash = 0x002f, /* U+002F SOLIDUS */
XK_0 = 0x0030, /* U+0030 DIGIT ZERO */
XK_1 = 0x0031, /* U+0031 DIGIT ONE */
XK_2 = 0x0032, /* U+0032 DIGIT TWO */
XK_3 = 0x0033, /* U+0033 DIGIT THREE */
XK_4 = 0x0034, /* U+0034 DIGIT FOUR */
XK_5 = 0x0035, /* U+0035 DIGIT FIVE */
XK_6 = 0x0036, /* U+0036 DIGIT SIX */
XK_7 = 0x0037, /* U+0037 DIGIT SEVEN */
XK_8 = 0x0038, /* U+0038 DIGIT EIGHT */
XK_9 = 0x0039, /* U+0039 DIGIT NINE */
XK_colon = 0x003a, /* U+003A COLON */
XK_semicolon = 0x003b, /* U+003B SEMICOLON */
XK_less = 0x003c, /* U+003C LESS-THAN SIGN */
XK_equal = 0x003d, /* U+003D EQUALS SIGN */
XK_greater = 0x003e, /* U+003E GREATER-THAN SIGN */
XK_question = 0x003f, /* U+003F QUESTION MARK */
XK_at = 0x0040, /* U+0040 COMMERCIAL AT */
XK_A = 0x0041, /* U+0041 LATIN CAPITAL LETTER A */
XK_B = 0x0042, /* U+0042 LATIN CAPITAL LETTER B */
XK_C = 0x0043, /* U+0043 LATIN CAPITAL LETTER C */
XK_D = 0x0044, /* U+0044 LATIN CAPITAL LETTER D */
XK_E = 0x0045, /* U+0045 LATIN CAPITAL LETTER E */
XK_F = 0x0046, /* U+0046 LATIN CAPITAL LETTER F */
XK_G = 0x0047, /* U+0047 LATIN CAPITAL LETTER G */
XK_H = 0x0048, /* U+0048 LATIN CAPITAL LETTER H */
XK_I = 0x0049, /* U+0049 LATIN CAPITAL LETTER I */
XK_J = 0x004a, /* U+004A LATIN CAPITAL LETTER J */
XK_K = 0x004b, /* U+004B LATIN CAPITAL LETTER K */
XK_L = 0x004c, /* U+004C LATIN CAPITAL LETTER L */
XK_M = 0x004d, /* U+004D LATIN CAPITAL LETTER M */
XK_N = 0x004e, /* U+004E LATIN CAPITAL LETTER N */
XK_O = 0x004f, /* U+004F LATIN CAPITAL LETTER O */
XK_P = 0x0050, /* U+0050 LATIN CAPITAL LETTER P */
XK_Q = 0x0051, /* U+0051 LATIN CAPITAL LETTER Q */
XK_R = 0x0052, /* U+0052 LATIN CAPITAL LETTER R */
XK_S = 0x0053, /* U+0053 LATIN CAPITAL LETTER S */
XK_T = 0x0054, /* U+0054 LATIN CAPITAL LETTER T */
XK_U = 0x0055, /* U+0055 LATIN CAPITAL LETTER U */
XK_V = 0x0056, /* U+0056 LATIN CAPITAL LETTER V */
XK_W = 0x0057, /* U+0057 LATIN CAPITAL LETTER W */
XK_X = 0x0058, /* U+0058 LATIN CAPITAL LETTER X */
XK_Y = 0x0059, /* U+0059 LATIN CAPITAL LETTER Y */
XK_Z = 0x005a, /* U+005A LATIN CAPITAL LETTER Z */
XK_bracketleft = 0x005b, /* U+005B LEFT SQUARE BRACKET */
XK_backslash = 0x005c, /* U+005C REVERSE SOLIDUS */
XK_bracketright = 0x005d, /* U+005D RIGHT SQUARE BRACKET */
XK_asciicircum = 0x005e, /* U+005E CIRCUMFLEX ACCENT */
XK_underscore = 0x005f, /* U+005F LOW LINE */
XK_grave = 0x0060, /* U+0060 GRAVE ACCENT */
XK_quoteleft = 0x0060, /* deprecated */
XK_a = 0x0061, /* U+0061 LATIN SMALL LETTER A */
XK_b = 0x0062, /* U+0062 LATIN SMALL LETTER B */
XK_c = 0x0063, /* U+0063 LATIN SMALL LETTER C */
XK_d = 0x0064, /* U+0064 LATIN SMALL LETTER D */
XK_e = 0x0065, /* U+0065 LATIN SMALL LETTER E */
XK_f = 0x0066, /* U+0066 LATIN SMALL LETTER F */
XK_g = 0x0067, /* U+0067 LATIN SMALL LETTER G */
XK_h = 0x0068, /* U+0068 LATIN SMALL LETTER H */
XK_i = 0x0069, /* U+0069 LATIN SMALL LETTER I */
XK_j = 0x006a, /* U+006A LATIN SMALL LETTER J */
XK_k = 0x006b, /* U+006B LATIN SMALL LETTER K */
XK_l = 0x006c, /* U+006C LATIN SMALL LETTER L */
XK_m = 0x006d, /* U+006D LATIN SMALL LETTER M */
XK_n = 0x006e, /* U+006E LATIN SMALL LETTER N */
XK_o = 0x006f, /* U+006F LATIN SMALL LETTER O */
XK_p = 0x0070, /* U+0070 LATIN SMALL LETTER P */
XK_q = 0x0071, /* U+0071 LATIN SMALL LETTER Q */
XK_r = 0x0072, /* U+0072 LATIN SMALL LETTER R */
XK_s = 0x0073, /* U+0073 LATIN SMALL LETTER S */
XK_t = 0x0074, /* U+0074 LATIN SMALL LETTER T */
XK_u = 0x0075, /* U+0075 LATIN SMALL LETTER U */
XK_v = 0x0076, /* U+0076 LATIN SMALL LETTER V */
XK_w = 0x0077, /* U+0077 LATIN SMALL LETTER W */
XK_x = 0x0078, /* U+0078 LATIN SMALL LETTER X */
XK_y = 0x0079, /* U+0079 LATIN SMALL LETTER Y */
XK_z = 0x007a, /* U+007A LATIN SMALL LETTER Z */
XK_braceleft = 0x007b, /* U+007B LEFT CURLY BRACKET */
XK_bar = 0x007c, /* U+007C VERTICAL LINE */
XK_braceright = 0x007d, /* U+007D RIGHT CURLY BRACKET */
XK_asciitilde = 0x007e, /* U+007E TILDE */
XK_nobreakspace = 0x00a0, /* U+00A0 NO-BREAK SPACE */
XK_exclamdown = 0x00a1, /* U+00A1 INVERTED EXCLAMATION MARK */
XK_cent = 0x00a2, /* U+00A2 CENT SIGN */
XK_sterling = 0x00a3, /* U+00A3 POUND SIGN */
XK_currency = 0x00a4, /* U+00A4 CURRENCY SIGN */
XK_yen = 0x00a5, /* U+00A5 YEN SIGN */
XK_brokenbar = 0x00a6, /* U+00A6 BROKEN BAR */
XK_section = 0x00a7, /* U+00A7 SECTION SIGN */
XK_diaeresis = 0x00a8, /* U+00A8 DIAERESIS */
XK_copyright = 0x00a9, /* U+00A9 COPYRIGHT SIGN */
XK_ordfeminine = 0x00aa, /* U+00AA FEMININE ORDINAL INDICATOR */
XK_guillemotleft = 0x00ab, /* U+00AB LEFT-POINTING DOUBLE ANGLE QUOTATION MARK */
XK_notsign = 0x00ac, /* U+00AC NOT SIGN */
XK_hyphen = 0x00ad, /* U+00AD SOFT HYPHEN */
XK_registered = 0x00ae, /* U+00AE REGISTERED SIGN */
XK_macron = 0x00af, /* U+00AF MACRON */
XK_degree = 0x00b0, /* U+00B0 DEGREE SIGN */
XK_plusminus = 0x00b1, /* U+00B1 PLUS-MINUS SIGN */
XK_twosuperior = 0x00b2, /* U+00B2 SUPERSCRIPT TWO */
XK_threesuperior = 0x00b3, /* U+00B3 SUPERSCRIPT THREE */
XK_acute = 0x00b4, /* U+00B4 ACUTE ACCENT */
XK_mu = 0x00b5, /* U+00B5 MICRO SIGN */
XK_paragraph = 0x00b6, /* U+00B6 PILCROW SIGN */
XK_periodcentered = 0x00b7, /* U+00B7 MIDDLE DOT */
XK_cedilla = 0x00b8, /* U+00B8 CEDILLA */
XK_onesuperior = 0x00b9, /* U+00B9 SUPERSCRIPT ONE */
XK_masculine = 0x00ba, /* U+00BA MASCULINE ORDINAL INDICATOR */
XK_guillemotright = 0x00bb, /* U+00BB RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK */
XK_onequarter = 0x00bc, /* U+00BC VULGAR FRACTION ONE QUARTER */
XK_onehalf = 0x00bd, /* U+00BD VULGAR FRACTION ONE HALF */
XK_threequarters = 0x00be, /* U+00BE VULGAR FRACTION THREE QUARTERS */
XK_questiondown = 0x00bf, /* U+00BF INVERTED QUESTION MARK */
XK_Agrave = 0x00c0, /* U+00C0 LATIN CAPITAL LETTER A WITH GRAVE */
XK_Aacute = 0x00c1, /* U+00C1 LATIN CAPITAL LETTER A WITH ACUTE */
XK_Acircumflex = 0x00c2, /* U+00C2 LATIN CAPITAL LETTER A WITH CIRCUMFLEX */
XK_Atilde = 0x00c3, /* U+00C3 LATIN CAPITAL LETTER A WITH TILDE */
XK_Adiaeresis = 0x00c4, /* U+00C4 LATIN CAPITAL LETTER A WITH DIAERESIS */
XK_Aring = 0x00c5, /* U+00C5 LATIN CAPITAL LETTER A WITH RING ABOVE */
XK_AE = 0x00c6, /* U+00C6 LATIN CAPITAL LETTER AE */
XK_Ccedilla = 0x00c7, /* U+00C7 LATIN CAPITAL LETTER C WITH CEDILLA */
XK_Egrave = 0x00c8, /* U+00C8 LATIN CAPITAL LETTER E WITH GRAVE */
XK_Eacute = 0x00c9, /* U+00C9 LATIN CAPITAL LETTER E WITH ACUTE */
XK_Ecircumflex = 0x00ca, /* U+00CA LATIN CAPITAL LETTER E WITH CIRCUMFLEX */
XK_Ediaeresis = 0x00cb, /* U+00CB LATIN CAPITAL LETTER E WITH DIAERESIS */
XK_Igrave = 0x00cc, /* U+00CC LATIN CAPITAL LETTER I WITH GRAVE */
XK_Iacute = 0x00cd, /* U+00CD LATIN CAPITAL LETTER I WITH ACUTE */
XK_Icircumflex = 0x00ce, /* U+00CE LATIN CAPITAL LETTER I WITH CIRCUMFLEX */
XK_Idiaeresis = 0x00cf, /* U+00CF LATIN CAPITAL LETTER I WITH DIAERESIS */
XK_ETH = 0x00d0, /* U+00D0 LATIN CAPITAL LETTER ETH */
XK_Eth = 0x00d0, /* deprecated */
XK_Ntilde = 0x00d1, /* U+00D1 LATIN CAPITAL LETTER N WITH TILDE */
XK_Ograve = 0x00d2, /* U+00D2 LATIN CAPITAL LETTER O WITH GRAVE */
XK_Oacute = 0x00d3, /* U+00D3 LATIN CAPITAL LETTER O WITH ACUTE */
XK_Ocircumflex = 0x00d4, /* U+00D4 LATIN CAPITAL LETTER O WITH CIRCUMFLEX */
XK_Otilde = 0x00d5, /* U+00D5 LATIN CAPITAL LETTER O WITH TILDE */
XK_Odiaeresis = 0x00d6, /* U+00D6 LATIN CAPITAL LETTER O WITH DIAERESIS */
XK_multiply = 0x00d7, /* U+00D7 MULTIPLICATION SIGN */
XK_Oslash = 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_Uacute = 0x00da, /* U+00DA LATIN CAPITAL LETTER U WITH ACUTE */
XK_Ucircumflex = 0x00db, /* U+00DB LATIN CAPITAL LETTER U WITH CIRCUMFLEX */
XK_Udiaeresis = 0x00dc, /* U+00DC LATIN CAPITAL LETTER U WITH DIAERESIS */
XK_Yacute = 0x00dd, /* U+00DD LATIN CAPITAL LETTER Y WITH ACUTE */
XK_THORN = 0x00de, /* U+00DE LATIN CAPITAL LETTER THORN */
XK_Thorn = 0x00de, /* deprecated */
XK_ssharp = 0x00df, /* U+00DF LATIN SMALL LETTER SHARP S */
XK_agrave = 0x00e0, /* U+00E0 LATIN SMALL LETTER A WITH GRAVE */
XK_aacute = 0x00e1, /* U+00E1 LATIN SMALL LETTER A WITH ACUTE */
XK_acircumflex = 0x00e2, /* U+00E2 LATIN SMALL LETTER A WITH CIRCUMFLEX */
XK_atilde = 0x00e3, /* U+00E3 LATIN SMALL LETTER A WITH TILDE */
XK_adiaeresis = 0x00e4, /* U+00E4 LATIN SMALL LETTER A WITH DIAERESIS */
XK_aring = 0x00e5, /* U+00E5 LATIN SMALL LETTER A WITH RING ABOVE */
XK_ae = 0x00e6, /* U+00E6 LATIN SMALL LETTER AE */
XK_ccedilla = 0x00e7, /* U+00E7 LATIN SMALL LETTER C WITH CEDILLA */
XK_egrave = 0x00e8, /* U+00E8 LATIN SMALL LETTER E WITH GRAVE */
XK_eacute = 0x00e9, /* U+00E9 LATIN SMALL LETTER E WITH ACUTE */
XK_ecircumflex = 0x00ea, /* U+00EA LATIN SMALL LETTER E WITH CIRCUMFLEX */
XK_ediaeresis = 0x00eb, /* U+00EB LATIN SMALL LETTER E WITH DIAERESIS */
XK_igrave = 0x00ec, /* U+00EC LATIN SMALL LETTER I WITH GRAVE */
XK_iacute = 0x00ed, /* U+00ED LATIN SMALL LETTER I WITH ACUTE */
XK_icircumflex = 0x00ee, /* U+00EE LATIN SMALL LETTER I WITH CIRCUMFLEX */
XK_idiaeresis = 0x00ef, /* U+00EF LATIN SMALL LETTER I WITH DIAERESIS */
XK_eth = 0x00f0, /* U+00F0 LATIN SMALL LETTER ETH */
XK_ntilde = 0x00f1, /* U+00F1 LATIN SMALL LETTER N WITH TILDE */
XK_ograve = 0x00f2, /* U+00F2 LATIN SMALL LETTER O WITH GRAVE */
XK_oacute = 0x00f3, /* U+00F3 LATIN SMALL LETTER O WITH ACUTE */
XK_ocircumflex = 0x00f4, /* U+00F4 LATIN SMALL LETTER O WITH CIRCUMFLEX */
XK_otilde = 0x00f5, /* U+00F5 LATIN SMALL LETTER O WITH TILDE */
XK_odiaeresis = 0x00f6, /* U+00F6 LATIN SMALL LETTER O WITH DIAERESIS */
XK_division = 0x00f7, /* U+00F7 DIVISION SIGN */
XK_oslash = 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_uacute = 0x00fa, /* U+00FA LATIN SMALL LETTER U WITH ACUTE */
XK_ucircumflex = 0x00fb, /* U+00FB LATIN SMALL LETTER U WITH CIRCUMFLEX */
XK_udiaeresis = 0x00fc, /* U+00FC LATIN SMALL LETTER U WITH DIAERESIS */
XK_yacute = 0x00fd, /* U+00FD LATIN SMALL LETTER Y WITH ACUTE */
XK_thorn = 0x00fe, /* U+00FE LATIN SMALL LETTER THORN */
XK_ydiaeresis = 0x00ff; /* U+00FF LATIN SMALL LETTER Y WITH DIAERESIS */
Subproject commit c0855c6caec589c33acc22b6ee5e562287e65f3d
* How to try
Assuming you have Web server (e.g. Apache) running at http://example.com/ .
- Download web_socket.rb from:
http://github.com/gimite/web-socket-ruby/tree/master
- Run sample Web Socket server (echo server) in example.com with: (#1)
$ ruby web-socket-ruby/samples/echo_server.rb example.com 10081
- If your server already provides socket policy file at port 843, modify the file to allow access to port 10081. Otherwise you can skip this step. See below for details.
- Publish the web-socket-js directory with your Web server (e.g. put it in ~/public_html).
- Change ws://localhost:10081 to ws://example.com:10081 in sample.html.
- Open sample.html in your browser.
- After "onopen" is shown, input something, click [Send] and confirm echo back.
#1: First argument of echo_server.rb means that it accepts Web Socket connection from HTML pages in example.com.
* Troubleshooting
If it doesn't work, try these:
1. Try Chrome and Firefox 3.x.
- It doesn't work on Chrome:
-- It's likely an issue of your code or the server. Debug your code as usual e.g. using console.log.
- It works on Chrome but it doesn't work on Firefox:
-- It's likely an issue of web-socket-js specific configuration (e.g. 3 and 4 below).
- It works on both Chrome and Firefox, but it doesn't work on your browser:
-- Check "Supported environment" section below. Your browser may not be supported by web-socket-js.
2. Add this line before your code:
WEB_SOCKET_DEBUG = true;
and use Developer Tools (Chrome/Safari) or Firebug (Firefox) to see if console.log outputs any errors.
3. Make sure you do NOT open your HTML page as local file e.g. file:///.../sample.html. web-socket-js doesn't work on local file. Open it via Web server e.g. http:///.../sample.html.
4. If you are NOT using web-socket-ruby as your WebSocket server, you need to place Flash socket policy file on your server. See "Flash socket policy file" section below for details.
5. Check if sample.html bundled with web-socket-js works.
6. Make sure the port used for WebSocket (10081 in example above) is not blocked by your server/client's firewall.
7. Install debugger version of Flash Player available here to see Flash errors:
http://www.adobe.com/support/flashplayer/downloads.html
* Supported environments
It should work on:
- Google Chrome 4 or later (just uses native implementation)
- Firefox 3.x, Internet Explorer 8 + Flash Player 9 or later
It may or may not work on other browsers such as Safari, Opera or IE 6. Patch for these browsers are appreciated, but I will not work on fixing issues specific to these browsers by myself.
* Flash socket policy file
This implementation uses Flash's socket, which means that your server must provide Flash socket policy file to declare the server accepts connections from Flash.
If you use web-socket-ruby available at
http://github.com/gimite/web-socket-ruby/tree/master
, you don't need anything special, because web-socket-ruby handles Flash socket policy file request. But if you already provide socket policy file at port 843, you need to modify the file to allow access to Web Socket port, because it precedes what web-socket-ruby provides.
If you use other Web Socket server implementation, you need to provide socket policy file yourself. See
http://www.lightsphere.com/dev/articles/flash_socket_policy.html
for details and sample script to run socket policy file server. node.js implementation is available here:
http://github.com/LearnBoost/Socket.IO-node/blob/master/lib/socket.io/transports/flashsocket.js
Actually, it's still better to provide socket policy file at port 843 even if you use web-socket-ruby. Flash always try to connect to port 843 first, so providing the file at port 843 makes startup faster.
* Cookie considerations
Cookie is sent if Web Socket host is the same as the origin of JavaScript. Otherwise it is not sent, because I don't know way to send right Cookie (which is Cookie of the host of Web Socket, I heard).
Note that it's technically possible that client sends arbitrary string as Cookie and any other headers (by modifying this library for example) once you place Flash socket policy file in your server. So don't trust Cookie and other headers if you allow connection from untrusted origin.
* Proxy considerations
The WebSocket spec (http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol) specifies instructions for User Agents to support proxied connections by implementing the HTTP CONNECT method.
The AS3 Socket class doesn't implement this mechanism, which renders it useless for the scenarios where the user trying to open a socket is behind a proxy.
The class RFC2817Socket (by Christian Cantrell) effectively lets us implement this, as long as the proxy settings are known and provided by the interface that instantiates the WebSocket. As such, if you want to support proxied conncetions, you'll have to supply this information to the WebSocket constructor when Flash is being used. One way to go about it would be to ask the user for proxy settings information if the initial connection fails.
* How to host HTML file and SWF file in different domains
By default, HTML file and SWF file must be in the same domain. You can follow steps below to allow hosting them in different domain.
WARNING: If you use the method below, HTML files in ANY domains can send arbitrary TCP data to your WebSocket server, regardless of configuration in Flash socket policy file. Arbitrary TCP data means that they can even fake request headers including Origin and Cookie.
- Unzip WebSocketMainInsecure.zip to extract WebSocketMainInsecure.swf.
- Put WebSocketMainInsecure.swf on your server, instead of WebSocketMain.swf.
- In JavaScript, set WEB_SOCKET_SWF_LOCATION to URL of your WebSocketMainInsecure.swf.
* How to build WebSocketMain.swf
Install Flex 4 SDK:
http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+4
$ cd flash-src
$ ./build.sh
* License
New BSD License.
/* SWFObject v2.2 <http://code.google.com/p/swfobject/>
is released under the MIT License <http://www.opensource.org/licenses/mit-license.php>
*/
var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O.ActiveXObject!=D){try{var ad=new ActiveXObject(W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y<X;Y++){U[Y]()}}function K(X){if(J){X()}else{U[U.length]=X}}function s(Y){if(typeof O.addEventListener!=D){O.addEventListener("load",Y,false)}else{if(typeof j.addEventListener!=D){j.addEventListener("load",Y,false)}else{if(typeof O.attachEvent!=D){i(O,"onload",Y)}else{if(typeof O.onload=="function"){var X=O.onload;O.onload=function(){X();Y()}}else{O.onload=Y}}}}}function h(){if(T){V()}else{H()}}function V(){var X=j.getElementsByTagName("body")[0];var aa=C(r);aa.setAttribute("type",q);var Z=X.appendChild(aa);if(Z){var Y=0;(function(){if(typeof Z.GetVariable!=D){var ab=Z.GetVariable("$version");if(ab){ab=ab.split(" ")[1].split(",");M.pv=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}else{if(Y<10){Y++;setTimeout(arguments.callee,10);return}}X.removeChild(aa);Z=null;H()})()}else{H()}}function H(){var ag=o.length;if(ag>0){for(var af=0;af<ag;af++){var Y=o[af].id;var ab=o[af].callbackFn;var aa={success:false,id:Y};if(M.pv[0]>0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad<ac;ad++){if(X[ad].getAttribute("name").toLowerCase()!="movie"){ah[X[ad].getAttribute("name")]=X[ad].getAttribute("value")}}P(ai,ah,Y,ab)}else{p(ae);if(ab){ab(aa)}}}}}else{w(Y,true);if(ab){var Z=z(Y);if(Z&&typeof Z.SetVariable!=D){aa.success=true;aa.ref=Z}ab(aa)}}}}}function z(aa){var X=null;var Y=c(aa);if(Y&&Y.nodeName=="OBJECT"){if(typeof Y.SetVariable!=D){X=Y}else{var Z=Y.getElementsByTagName(r)[0];if(Z){X=Z}}}return X}function A(){return !a&&F("6.0.65")&&(M.win||M.mac)&&!(M.wk&&M.wk<312)}function P(aa,ab,X,Z){a=true;E=Z||null;B={success:false,id:X};var ae=c(X);if(ae){if(ae.nodeName=="OBJECT"){l=g(ae);Q=null}else{l=ae;Q=X}aa.id=R;if(typeof aa.width==D||(!/%$/.test(aa.width)&&parseInt(aa.width,10)<310)){aa.width="310"}if(typeof aa.height==D||(!/%$/.test(aa.height)&&parseInt(aa.height,10)<137)){aa.height="137"}j.title=j.title.slice(0,47)+" - Flash Player Installation";var ad=M.ie&&M.win?"ActiveX":"PlugIn",ac="MMredirectURL="+O.location.toString().replace(/&/g,"%26")+"&MMplayerType="+ad+"&MMdoctitle="+j.title;if(typeof ab.flashvars!=D){ab.flashvars+="&"+ac}else{ab.flashvars=ac}if(M.ie&&M.win&&ae.readyState!=4){var Y=C("div");X+="SWFObjectNew";Y.setAttribute("id",X);ae.parentNode.insertBefore(Y,ae);ae.style.display="none";(function(){if(ae.readyState==4){ae.parentNode.removeChild(ae)}else{setTimeout(arguments.callee,10)}})()}u(aa,ab,X)}}function p(Y){if(M.ie&&M.win&&Y.readyState!=4){var X=C("div");Y.parentNode.insertBefore(X,Y);X.parentNode.replaceChild(g(Y),X);Y.style.display="none";(function(){if(Y.readyState==4){Y.parentNode.removeChild(Y)}else{setTimeout(arguments.callee,10)}})()}else{Y.parentNode.replaceChild(g(Y),Y)}}function g(ab){var aa=C("div");if(M.win&&M.ie){aa.innerHTML=ab.innerHTML}else{var Y=ab.getElementsByTagName(r)[0];if(Y){var ad=Y.childNodes;if(ad){var X=ad.length;for(var Z=0;Z<X;Z++){if(!(ad[Z].nodeType==1&&ad[Z].nodeName=="PARAM")&&!(ad[Z].nodeType==8)){aa.appendChild(ad[Z].cloneNode(true))}}}}}return aa}function u(ai,ag,Y){var X,aa=c(Y);if(M.wk&&M.wk<312){return X}if(aa){if(typeof ai.id==D){ai.id=Y}if(M.ie&&M.win){var ah="";for(var ae in ai){if(ai[ae]!=Object.prototype[ae]){if(ae.toLowerCase()=="data"){ag.movie=ai[ae]}else{if(ae.toLowerCase()=="styleclass"){ah+=' class="'+ai[ae]+'"'}else{if(ae.toLowerCase()!="classid"){ah+=" "+ae+'="'+ai[ae]+'"'}}}}}var af="";for(var ad in ag){if(ag[ad]!=Object.prototype[ad]){af+='<param name="'+ad+'" value="'+ag[ad]+'" />'}}aa.outerHTML='<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"'+ah+">"+af+"</object>";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab<ac;ab++){I[ab][0].detachEvent(I[ab][1],I[ab][2])}var Z=N.length;for(var aa=0;aa<Z;aa++){y(N[aa])}for(var Y in M){M[Y]=null}M=null;for(var X in swfobject){swfobject[X]=null}swfobject=null})}}();return{registerObject:function(ab,X,aa,Z){if(M.w3&&ab&&X){var Y={};Y.id=ab;Y.swfVersion=X;Y.expressInstall=aa;Y.callbackFn=Z;o[o.length]=Y;w(ab,false)}else{if(Z){Z({success:false,id:ab})}}},getObjectById:function(X){if(M.w3){return z(X)}},embedSWF:function(ab,ah,ae,ag,Y,aa,Z,ad,af,ac){var X={success:false,id:ah};if(M.w3&&!(M.wk&&M.wk<312)&&ab&&ah&&ae&&ag&&Y){w(ah,false);K(function(){ae+="";ag+="";var aj={};if(af&&typeof af===r){for(var al in af){aj[al]=af[al]}}aj.data=ab;aj.width=ae;aj.height=ag;var am={};if(ad&&typeof ad===r){for(var ak in ad){am[ak]=ad[ak]}}if(Z&&typeof Z===r){for(var ai in Z){if(typeof am.flashvars!=D){am.flashvars+="&"+ai+"="+Z[ai]}else{am.flashvars=ai+"="+Z[ai]}}}if(F(Y)){var an=u(aj,am,ah);if(aj.id==ah){w(ah,true)}X.success=true;X.ref=an}else{if(aa&&A()){aj.data=aa;P(aj,am,ah,ac);return}else{w(ah,true)}}if(ac){ac(X)}})}else{if(ac){ac(X)}}},switchOffAutoHideShow:function(){m=false},ua:M,getFlashPlayerVersion:function(){return{major:M.pv[0],minor:M.pv[1],release:M.pv[2]}},hasFlashPlayerVersion:F,createSWF:function(Z,Y,X){if(M.w3){return u(Z,Y,X)}else{return undefined}},showExpressInstall:function(Z,aa,X,Y){if(M.w3&&A()){P(Z,aa,X,Y)}},removeSWF:function(X){if(M.w3){y(X)}},createCSS:function(aa,Z,Y,X){if(M.w3){v(aa,Z,Y,X)}},addDomLoadEvent:K,addLoadEvent:s,getQueryParamValue:function(aa){var Z=j.location.search||j.location.hash;if(Z){if(/\?/.test(Z)){Z=Z.split("?")[1]}if(aa==null){return L(Z)}var Y=Z.split("&");for(var X=0;X<Y.length;X++){if(Y[X].substring(0,Y[X].indexOf("="))==aa){return L(Y[X].substring((Y[X].indexOf("=")+1)))}}}return""},expressInstallCallback:function(){if(a){var X=c(R);if(X&&l){X.parentNode.replaceChild(l,X);if(Q){w(Q,true);if(M.ie&&M.win){l.style.display="block"}}if(E){E(B)}}a=false}}}}();
\ No newline at end of file
// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
// License: New BSD License
// Reference: http://dev.w3.org/html5/websockets/
// Reference: http://tools.ietf.org/html/rfc6455
(function() {
if (window.WEB_SOCKET_FORCE_FLASH) {
// Keeps going.
} else if (window.WebSocket) {
return;
} else if (window.MozWebSocket) {
// Firefox.
window.WebSocket = MozWebSocket;
return;
}
var logger;
if (window.WEB_SOCKET_LOGGER) {
logger = WEB_SOCKET_LOGGER;
} else if (window.console && window.console.log && window.console.error) {
// In some environment, console is defined but console.log or console.error is missing.
logger = window.console;
} else {
logger = {log: function(){ }, error: function(){ }};
}
// swfobject.hasFlashPlayerVersion("10.0.0") doesn't work with Gnash.
if (swfobject.getFlashPlayerVersion().major < 10) {
logger.error("Flash Player >= 10.0.0 is required.");
return;
}
if (location.protocol == "file:") {
logger.error(
"WARNING: web-socket-js doesn't work in file:///... URL " +
"unless you set Flash Security Settings properly. " +
"Open the page via Web server i.e. http://...");
}
/**
* Our own implementation of WebSocket class using Flash.
* @param {string} url
* @param {array or string} protocols
* @param {string} proxyHost
* @param {int} proxyPort
* @param {string} headers
*/
window.WebSocket = function(url, protocols, proxyHost, proxyPort, headers) {
var self = this;
self.__id = WebSocket.__nextId++;
WebSocket.__instances[self.__id] = self;
self.readyState = WebSocket.CONNECTING;
self.bufferedAmount = 0;
self.__events = {};
if (!protocols) {
protocols = [];
} else if (typeof protocols == "string") {
protocols = [protocols];
}
// Uses setTimeout() to make sure __createFlash() runs after the caller sets ws.onopen etc.
// Otherwise, when onopen fires immediately, onopen is called before it is set.
self.__createTask = setTimeout(function() {
WebSocket.__addTask(function() {
self.__createTask = null;
WebSocket.__flash.create(
self.__id, url, protocols, proxyHost || null, proxyPort || 0, headers || null);
});
}, 0);
};
/**
* Send data to the web socket.
* @param {string} data The data to send to the socket.
* @return {boolean} True for success, false for failure.
*/
WebSocket.prototype.send = function(data) {
if (this.readyState == WebSocket.CONNECTING) {
throw "INVALID_STATE_ERR: Web Socket connection has not been established";
}
// We use encodeURIComponent() here, because FABridge doesn't work if
// the argument includes some characters. We don't use escape() here
// because of this:
// https://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Functions#escape_and_unescape_Functions
// But it looks decodeURIComponent(encodeURIComponent(s)) doesn't
// preserve all Unicode characters either e.g. "\uffff" in Firefox.
// Note by wtritch: Hopefully this will not be necessary using ExternalInterface. Will require
// additional testing.
var result = WebSocket.__flash.send(this.__id, encodeURIComponent(data));
if (result < 0) { // success
return true;
} else {
this.bufferedAmount += result;
return false;
}
};
/**
* Close this web socket gracefully.
*/
WebSocket.prototype.close = function() {
if (this.__createTask) {
clearTimeout(this.__createTask);
this.__createTask = null;
this.readyState = WebSocket.CLOSED;
return;
}
if (this.readyState == WebSocket.CLOSED || this.readyState == WebSocket.CLOSING) {
return;
}
this.readyState = WebSocket.CLOSING;
WebSocket.__flash.close(this.__id);
};
/**
* Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
*
* @param {string} type
* @param {function} listener
* @param {boolean} useCapture
* @return void
*/
WebSocket.prototype.addEventListener = function(type, listener, useCapture) {
if (!(type in this.__events)) {
this.__events[type] = [];
}
this.__events[type].push(listener);
};
/**
* Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
*
* @param {string} type
* @param {function} listener
* @param {boolean} useCapture
* @return void
*/
WebSocket.prototype.removeEventListener = function(type, listener, useCapture) {
if (!(type in this.__events)) return;
var events = this.__events[type];
for (var i = events.length - 1; i >= 0; --i) {
if (events[i] === listener) {
events.splice(i, 1);
break;
}
}
};
/**
* Implementation of {@link <a href="http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-registration">DOM 2 EventTarget Interface</a>}
*
* @param {Event} event
* @return void
*/
WebSocket.prototype.dispatchEvent = function(event) {
var events = this.__events[event.type] || [];
for (var i = 0; i < events.length; ++i) {
events[i](event);
}
var handler = this["on" + event.type];
if (handler) handler.apply(this, [event]);
};
/**
* Handles an event from Flash.
* @param {Object} flashEvent
*/
WebSocket.prototype.__handleEvent = function(flashEvent) {
if ("readyState" in flashEvent) {
this.readyState = flashEvent.readyState;
}
if ("protocol" in flashEvent) {
this.protocol = flashEvent.protocol;
}
var jsEvent;
if (flashEvent.type == "open" || flashEvent.type == "error") {
jsEvent = this.__createSimpleEvent(flashEvent.type);
} else if (flashEvent.type == "close") {
jsEvent = this.__createSimpleEvent("close");
jsEvent.wasClean = flashEvent.wasClean ? true : false;
jsEvent.code = flashEvent.code;
jsEvent.reason = flashEvent.reason;
} else if (flashEvent.type == "message") {
var data = decodeURIComponent(flashEvent.message);
jsEvent = this.__createMessageEvent("message", data);
} else {
throw "unknown event type: " + flashEvent.type;
}
this.dispatchEvent(jsEvent);
};
WebSocket.prototype.__createSimpleEvent = function(type) {
if (document.createEvent && window.Event) {
var event = document.createEvent("Event");
event.initEvent(type, false, false);
return event;
} else {
return {type: type, bubbles: false, cancelable: false};
}
};
WebSocket.prototype.__createMessageEvent = function(type, data) {
if (document.createEvent && window.MessageEvent && !window.opera) {
var event = document.createEvent("MessageEvent");
event.initMessageEvent("message", false, false, data, null, null, window, null);
return event;
} else {
// IE and Opera, the latter one truncates the data parameter after any 0x00 bytes.
return {type: type, data: data, bubbles: false, cancelable: false};
}
};
/**
* Define the WebSocket readyState enumeration.
*/
WebSocket.CONNECTING = 0;
WebSocket.OPEN = 1;
WebSocket.CLOSING = 2;
WebSocket.CLOSED = 3;
// Field to check implementation of WebSocket.
WebSocket.__isFlashImplementation = true;
WebSocket.__initialized = false;
WebSocket.__flash = null;
WebSocket.__instances = {};
WebSocket.__tasks = [];
WebSocket.__nextId = 0;
/**
* Load a new flash security policy file.
* @param {string} url
*/
WebSocket.loadFlashPolicyFile = function(url){
WebSocket.__addTask(function() {
WebSocket.__flash.loadManualPolicyFile(url);
});
};
/**
* Loads WebSocketMain.swf and creates WebSocketMain object in Flash.
*/
WebSocket.__initialize = function() {
if (WebSocket.__initialized) return;
WebSocket.__initialized = true;
if (WebSocket.__swfLocation) {
// For backword compatibility.
window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation;
}
if (!window.WEB_SOCKET_SWF_LOCATION) {
logger.error("[WebSocket] set WEB_SOCKET_SWF_LOCATION to location of WebSocketMain.swf");
return;
}
if (!window.WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR &&
!WEB_SOCKET_SWF_LOCATION.match(/(^|\/)WebSocketMainInsecure\.swf(\?.*)?$/) &&
WEB_SOCKET_SWF_LOCATION.match(/^\w+:\/\/([^\/]+)/)) {
var swfHost = RegExp.$1;
if (location.host != swfHost) {
logger.error(
"[WebSocket] You must host HTML and WebSocketMain.swf in the same host " +
"('" + location.host + "' != '" + swfHost + "'). " +
"See also 'How to host HTML file and SWF file in different domains' section " +
"in README.md. If you use WebSocketMainInsecure.swf, you can suppress this message " +
"by WEB_SOCKET_SUPPRESS_CROSS_DOMAIN_SWF_ERROR = true;");
}
}
var container = document.createElement("div");
container.id = "webSocketContainer";
// Hides Flash box. We cannot use display: none or visibility: hidden because it prevents
// Flash from loading at least in IE. So we move it out of the screen at (-100, -100).
// But this even doesn't work with Flash Lite (e.g. in Droid Incredible). So with Flash
// Lite, we put it at (0, 0). This shows 1x1 box visible at left-top corner but this is
// the best we can do as far as we know now.
container.style.position = "absolute";
if (WebSocket.__isFlashLite()) {
container.style.left = "0px";
container.style.top = "0px";
} else {
container.style.left = "-100px";
container.style.top = "-100px";
}
var holder = document.createElement("div");
holder.id = "webSocketFlash";
container.appendChild(holder);
document.body.appendChild(container);
// See this article for hasPriority:
// http://help.adobe.com/en_US/as3/mobile/WS4bebcd66a74275c36cfb8137124318eebc6-7ffd.html
swfobject.embedSWF(
WEB_SOCKET_SWF_LOCATION,
"webSocketFlash",
"1" /* width */,
"1" /* height */,
"10.0.0" /* SWF version */,
null,
null,
{hasPriority: true, swliveconnect : true, allowScriptAccess: "always"},
null,
function(e) {
if (!e.success) {
logger.error("[WebSocket] swfobject.embedSWF failed");
}
}
);
};
/**
* Called by Flash to notify JS that it's fully loaded and ready
* for communication.
*/
WebSocket.__onFlashInitialized = function() {
// We need to set a timeout here to avoid round-trip calls
// to flash during the initialization process.
setTimeout(function() {
WebSocket.__flash = document.getElementById("webSocketFlash");
WebSocket.__flash.setCallerUrl(location.href);
WebSocket.__flash.setDebug(!!window.WEB_SOCKET_DEBUG);
for (var i = 0; i < WebSocket.__tasks.length; ++i) {
WebSocket.__tasks[i]();
}
WebSocket.__tasks = [];
}, 0);
};
/**
* Called by Flash to notify WebSockets events are fired.
*/
WebSocket.__onFlashEvent = function() {
setTimeout(function() {
try {
// Gets events using receiveEvents() instead of getting it from event object
// of Flash event. This is to make sure to keep message order.
// It seems sometimes Flash events don't arrive in the same order as they are sent.
var events = WebSocket.__flash.receiveEvents();
for (var i = 0; i < events.length; ++i) {
WebSocket.__instances[events[i].webSocketId].__handleEvent(events[i]);
}
} catch (e) {
logger.error(e);
}
}, 0);
return true;
};
// Called by Flash.
WebSocket.__log = function(message) {
logger.log(decodeURIComponent(message));
};
// Called by Flash.
WebSocket.__error = function(message) {
logger.error(decodeURIComponent(message));
};
WebSocket.__addTask = function(task) {
if (WebSocket.__flash) {
task();
} else {
WebSocket.__tasks.push(task);
}
};
/**
* Test if the browser is running flash lite.
* @return {boolean} True if flash lite is running, false otherwise.
*/
WebSocket.__isFlashLite = function() {
if (!window.navigator || !window.navigator.mimeTypes) {
return false;
}
var mimeType = window.navigator.mimeTypes["application/x-shockwave-flash"];
if (!mimeType || !mimeType.enabledPlugin || !mimeType.enabledPlugin.filename) {
return false;
}
return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false;
};
if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) {
// NOTE:
// This fires immediately if web_socket.js is dynamically loaded after
// the document is loaded.
swfobject.addDomLoadEvent(function() {
WebSocket.__initialize();
});
}
})();
......@@ -110,18 +110,18 @@ module.exports = function(config) {
files: [
'tests/fake.*.js',
'tests/assertions.js',
'include/util.js', // load first to avoid issues, since methods are called immediately
'core/util.js', // load first to avoid issues, since methods are called immediately
//'../include/*.js',
'include/base64.js',
'include/keysym.js',
'include/keysymdef.js',
'include/keyboard.js',
'include/input.js',
'include/websock.js',
'include/rfb.js',
'include/jsunzip.js',
'include/des.js',
'include/display.js',
'core/base64.js',
'core/keysym.js',
'core/keysymdef.js',
'core/keyboard.js',
'core/input.js',
'core/websock.js',
'core/rfb.js',
'core/jsunzip.js',
'core/des.js',
'core/display.js',
'tests/test.*.js'
],
......
......@@ -66,7 +66,7 @@ if (all_js && !program.autoInject) {
var eol = content.indexOf('\n', ind);
var modules = content.slice(ind, eol).split(/,\s*/);
modules.forEach(function (mod) {
all_modules[get_path('include/', mod) + '.js'] = 1;
all_modules[get_path('core/', mod) + '.js'] = 1;
});
}
......
......@@ -7,18 +7,18 @@ describe('Helpers', function() {
"use strict";
describe('keysymFromKeyCode', function() {
it('should map known keycodes to keysyms', function() {
expect(kbdUtil.keysymFromKeyCode(0x41, false), 'a').to.be.equal(0x61);
expect(kbdUtil.keysymFromKeyCode(0x41, true), 'A').to.be.equal(0x41);
expect(kbdUtil.keysymFromKeyCode(0xd, false), 'enter').to.be.equal(0xFF0D);
expect(kbdUtil.keysymFromKeyCode(0x11, false), 'ctrl').to.be.equal(0xFFE3);
expect(kbdUtil.keysymFromKeyCode(0x12, false), 'alt').to.be.equal(0xFFE9);
expect(kbdUtil.keysymFromKeyCode(0xe1, false), 'altgr').to.be.equal(0xFE03);
expect(kbdUtil.keysymFromKeyCode(0x1b, false), 'esc').to.be.equal(0xFF1B);
expect(kbdUtil.keysymFromKeyCode(0x26, false), 'up').to.be.equal(0xFF52);
expect(KeyboardUtil.keysymFromKeyCode(0x41, false), 'a').to.be.equal(0x61);
expect(KeyboardUtil.keysymFromKeyCode(0x41, true), 'A').to.be.equal(0x41);
expect(KeyboardUtil.keysymFromKeyCode(0xd, false), 'enter').to.be.equal(0xFF0D);
expect(KeyboardUtil.keysymFromKeyCode(0x11, false), 'ctrl').to.be.equal(0xFFE3);
expect(KeyboardUtil.keysymFromKeyCode(0x12, false), 'alt').to.be.equal(0xFFE9);
expect(KeyboardUtil.keysymFromKeyCode(0xe1, false), 'altgr').to.be.equal(0xFE03);
expect(KeyboardUtil.keysymFromKeyCode(0x1b, false), 'esc').to.be.equal(0xFF1B);
expect(KeyboardUtil.keysymFromKeyCode(0x26, false), 'up').to.be.equal(0xFF52);
});
it('should return null for unknown keycodes', function() {
expect(kbdUtil.keysymFromKeyCode(0xc0, false), 'DK æ').to.be.null;
expect(kbdUtil.keysymFromKeyCode(0xde, false), 'DK ø').to.be.null;
expect(KeyboardUtil.keysymFromKeyCode(0xc0, false), 'DK æ').to.be.null;
expect(KeyboardUtil.keysymFromKeyCode(0xde, false), 'DK ø').to.be.null;
});
});
......@@ -46,59 +46,59 @@ describe('Helpers', function() {
describe('substituteCodepoint', function() {
it('should replace characters which don\'t have a keysym', function() {
expect(kbdUtil.substituteCodepoint('Ș'.charCodeAt())).to.equal('Ş'.charCodeAt());
expect(kbdUtil.substituteCodepoint('ș'.charCodeAt())).to.equal('ş'.charCodeAt());
expect(kbdUtil.substituteCodepoint('Ț'.charCodeAt())).to.equal('Ţ'.charCodeAt());
expect(kbdUtil.substituteCodepoint('ț'.charCodeAt())).to.equal('ţ'.charCodeAt());
expect(KeyboardUtil.substituteCodepoint('Ș'.charCodeAt())).to.equal('Ş'.charCodeAt());
expect(KeyboardUtil.substituteCodepoint('ș'.charCodeAt())).to.equal('ş'.charCodeAt());
expect(KeyboardUtil.substituteCodepoint('Ț'.charCodeAt())).to.equal('Ţ'.charCodeAt());
expect(KeyboardUtil.substituteCodepoint('ț'.charCodeAt())).to.equal('ţ'.charCodeAt());
});
it('should pass other characters through unchanged', function() {
expect(kbdUtil.substituteCodepoint('T'.charCodeAt())).to.equal('T'.charCodeAt());
expect(KeyboardUtil.substituteCodepoint('T'.charCodeAt())).to.equal('T'.charCodeAt());
});
});
describe('nonCharacterKey', function() {
it('should recognize the right keys', function() {
expect(kbdUtil.nonCharacterKey({keyCode: 0xd}), 'enter').to.be.defined;
expect(kbdUtil.nonCharacterKey({keyCode: 0x08}), 'backspace').to.be.defined;
expect(kbdUtil.nonCharacterKey({keyCode: 0x09}), 'tab').to.be.defined;
expect(kbdUtil.nonCharacterKey({keyCode: 0x10}), 'shift').to.be.defined;
expect(kbdUtil.nonCharacterKey({keyCode: 0x11}), 'ctrl').to.be.defined;
expect(kbdUtil.nonCharacterKey({keyCode: 0x12}), 'alt').to.be.defined;
expect(kbdUtil.nonCharacterKey({keyCode: 0xe0}), 'meta').to.be.defined;
expect(KeyboardUtil.nonCharacterKey({keyCode: 0xd}), 'enter').to.be.defined;
expect(KeyboardUtil.nonCharacterKey({keyCode: 0x08}), 'backspace').to.be.defined;
expect(KeyboardUtil.nonCharacterKey({keyCode: 0x09}), 'tab').to.be.defined;
expect(KeyboardUtil.nonCharacterKey({keyCode: 0x10}), 'shift').to.be.defined;
expect(KeyboardUtil.nonCharacterKey({keyCode: 0x11}), 'ctrl').to.be.defined;
expect(KeyboardUtil.nonCharacterKey({keyCode: 0x12}), 'alt').to.be.defined;
expect(KeyboardUtil.nonCharacterKey({keyCode: 0xe0}), 'meta').to.be.defined;
});
it('should not recognize character keys', function() {
expect(kbdUtil.nonCharacterKey({keyCode: 'A'}), 'A').to.be.null;
expect(kbdUtil.nonCharacterKey({keyCode: '1'}), '1').to.be.null;
expect(kbdUtil.nonCharacterKey({keyCode: '.'}), '.').to.be.null;
expect(kbdUtil.nonCharacterKey({keyCode: ' '}), 'space').to.be.null;
expect(KeyboardUtil.nonCharacterKey({keyCode: 'A'}), 'A').to.be.null;
expect(KeyboardUtil.nonCharacterKey({keyCode: '1'}), '1').to.be.null;
expect(KeyboardUtil.nonCharacterKey({keyCode: '.'}), '.').to.be.null;
expect(KeyboardUtil.nonCharacterKey({keyCode: ' '}), 'space').to.be.null;
});
});
describe('getKeysym', function() {
it('should prefer char', function() {
expect(kbdUtil.getKeysym({char : 'a', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x61);
expect(KeyboardUtil.getKeysym({char : 'a', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x61);
});
it('should use charCode if no char', function() {
expect(kbdUtil.getKeysym({char : '', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x01a9);
expect(kbdUtil.getKeysym({charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x01a9);
expect(kbdUtil.getKeysym({char : 'hello', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x01a9);
expect(KeyboardUtil.getKeysym({char : '', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x01a9);
expect(KeyboardUtil.getKeysym({charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x01a9);
expect(KeyboardUtil.getKeysym({char : 'hello', charCode: 'Š'.charCodeAt(), keyCode: 0x42, which: 0x43})).to.have.property('keysym', 0x01a9);
});
it('should use keyCode if no charCode', function() {
expect(kbdUtil.getKeysym({keyCode: 0x42, which: 0x43, shiftKey: false})).to.have.property('keysym', 0x62);
expect(kbdUtil.getKeysym({keyCode: 0x42, which: 0x43, shiftKey: true})).to.have.property('keysym', 0x42);
expect(KeyboardUtil.getKeysym({keyCode: 0x42, which: 0x43, shiftKey: false})).to.have.property('keysym', 0x62);
expect(KeyboardUtil.getKeysym({keyCode: 0x42, which: 0x43, shiftKey: true})).to.have.property('keysym', 0x42);
});
it('should use which if no keyCode', function() {
expect(kbdUtil.getKeysym({which: 0x43, shiftKey: false})).to.have.property('keysym', 0x63);
expect(kbdUtil.getKeysym({which: 0x43, shiftKey: true})).to.have.property('keysym', 0x43);
expect(KeyboardUtil.getKeysym({which: 0x43, shiftKey: false})).to.have.property('keysym', 0x63);
expect(KeyboardUtil.getKeysym({which: 0x43, shiftKey: true})).to.have.property('keysym', 0x43);
});
it('should substitute where applicable', function() {
expect(kbdUtil.getKeysym({char : 'Ș'})).to.have.property('keysym', 0x1aa);
expect(KeyboardUtil.getKeysym({char : 'Ș'})).to.have.property('keysym', 0x1aa);
});
});
describe('Modifier Sync', function() { // return a list of fake events necessary to fix modifier state
describe('Toggle all modifiers', function() {
var sync = kbdUtil.ModifierSync();
var sync = KeyboardUtil.ModifierSync();
it ('should do nothing if all modifiers are up as expected', function() {
expect(sync.keydown({
keyCode: 0x41,
......@@ -141,7 +141,7 @@ describe('Helpers', function() {
});
});
describe('Toggle Ctrl', function() {
var sync = kbdUtil.ModifierSync();
var sync = KeyboardUtil.ModifierSync();
it('should sync if modifier is suddenly down', function() {
expect(sync.keydown({
keyCode: 0x41,
......@@ -156,7 +156,7 @@ describe('Helpers', function() {
});
});
describe('Toggle Alt', function() {
var sync = kbdUtil.ModifierSync();
var sync = KeyboardUtil.ModifierSync();
it('should sync if modifier is suddenly down', function() {
expect(sync.keydown({
keyCode: 0x41,
......@@ -171,7 +171,7 @@ describe('Helpers', function() {
});
});
describe('Toggle AltGr', function() {
var sync = kbdUtil.ModifierSync();
var sync = KeyboardUtil.ModifierSync();
it('should sync if modifier is suddenly down', function() {
expect(sync.keydown({
keyCode: 0x41,
......@@ -186,7 +186,7 @@ describe('Helpers', function() {
});
});
describe('Toggle Shift', function() {
var sync = kbdUtil.ModifierSync();
var sync = KeyboardUtil.ModifierSync();
it('should sync if modifier is suddenly down', function() {
expect(sync.keydown({
keyCode: 0x41,
......@@ -201,7 +201,7 @@ describe('Helpers', function() {
});
});
describe('Toggle Meta', function() {
var sync = kbdUtil.ModifierSync();
var sync = KeyboardUtil.ModifierSync();
it('should sync if modifier is suddenly down', function() {
expect(sync.keydown({
keyCode: 0x41,
......@@ -217,17 +217,17 @@ describe('Helpers', function() {
});
describe('Modifier keyevents', function() {
it('should not sync a modifier on its own events', function() {
expect(kbdUtil.ModifierSync().keydown({
expect(KeyboardUtil.ModifierSync().keydown({
keyCode: 0x11,
ctrlKey: false
})).to.be.deep.equal([]);
expect(kbdUtil.ModifierSync().keydown({
expect(KeyboardUtil.ModifierSync().keydown({
keyCode: 0x11,
ctrlKey: true
}), 'B').to.be.deep.equal([]);
})
it('should update state on modifier keyevents', function() {
var sync = kbdUtil.ModifierSync();
var sync = KeyboardUtil.ModifierSync();
sync.keydown({
keyCode: 0x11,
});
......@@ -237,7 +237,7 @@ describe('Helpers', function() {
})).to.be.deep.equal([]);
});
it('should sync other modifiers on ctrl events', function() {
expect(kbdUtil.ModifierSync().keydown({
expect(KeyboardUtil.ModifierSync().keydown({
keyCode: 0x11,
altKey: true
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe9), type: 'keydown'}]);
......@@ -245,17 +245,17 @@ describe('Helpers', function() {
});
describe('sync modifiers on non-key events', function() {
it('should generate sync events when receiving non-keyboard events', function() {
expect(kbdUtil.ModifierSync().syncAny({
expect(KeyboardUtil.ModifierSync().syncAny({
altKey: true
})).to.be.deep.equal([{keysym: keysyms.lookup(0xffe9), type: 'keydown'}]);
});
});
describe('do not treat shift as a modifier key', function() {
it('should not treat shift as a shortcut modifier', function() {
expect(kbdUtil.hasShortcutModifier([], {0xffe1 : true})).to.be.false;
expect(KeyboardUtil.hasShortcutModifier([], {0xffe1 : true})).to.be.false;
});
it('should not treat shift as a char modifier', function() {
expect(kbdUtil.hasCharModifier([], {0xffe1 : true})).to.be.false;
expect(KeyboardUtil.hasCharModifier([], {0xffe1 : true})).to.be.false;
});
});
});
......
// requires local modules: input, keyboard, keysymdef
// requires local modules: input, keyboard, keysymdef, keysym
var assert = chai.assert;
var expect = chai.expect;
......@@ -7,19 +7,19 @@ describe('Key Event Pipeline Stages', function() {
"use strict";
describe('Decode Keyboard Events', function() {
it('should pass events to the next stage', function(done) {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
expect(evt).to.be.an.object;
done();
}).keydown({keyCode: 0x41});
});
it('should pass the right keysym through', function(done) {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
expect(evt.keysym).to.be.deep.equal(keysyms.lookup(0x61));
done();
}).keypress({keyCode: 0x41});
});
it('should pass the right keyid through', function(done) {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
expect(evt).to.have.property('keyId', 0x41);
done();
}).keydown({keyCode: 0x41});
......@@ -27,14 +27,14 @@ describe('Key Event Pipeline Stages', function() {
it('should not sync modifiers on a keypress', function() {
// Firefox provides unreliable modifier state on keypress events
var count = 0;
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
++count;
}).keypress({keyCode: 0x41, ctrlKey: true});
expect(count).to.be.equal(1);
});
it('should sync modifiers if necessary', function(done) {
var count = 0;
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
switch (count) {
case 0: // fake a ctrl keydown
expect(evt).to.be.deep.equal({keysym: keysyms.lookup(0xffe3), type: 'keydown'});
......@@ -48,26 +48,26 @@ describe('Key Event Pipeline Stages', function() {
}).keydown({keyCode: 0x41, ctrlKey: true});
});
it('should forward keydown events with the right type', function(done) {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
expect(evt).to.be.deep.equal({keyId: 0x41, type: 'keydown'});
done();
}).keydown({keyCode: 0x41});
});
it('should forward keyup events with the right type', function(done) {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keyup'});
done();
}).keyup({keyCode: 0x41});
});
it('should forward keypress events with the right type', function(done) {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keypress'});
done();
}).keypress({keyCode: 0x41});
});
it('should generate stalls if a char modifier is down while a key is pressed', function(done) {
var count = 0;
KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) {
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) {
switch (count) {
case 0: // fake altgr
expect(evt).to.be.deep.equal({keysym: keysyms.lookup(0xfe03), type: 'keydown'});
......@@ -92,7 +92,7 @@ describe('Key Event Pipeline Stages', function() {
});
describe('suppress the right events at the right time', function() {
it('should suppress anything while a shortcut modifier is down', function() {
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {});
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
obj.keydown({keyCode: 0x11}); // press ctrl
expect(obj.keydown({keyCode: 'A'.charCodeAt()})).to.be.true;
......@@ -102,7 +102,7 @@ describe('Key Event Pipeline Stages', function() {
expect(obj.keydown({keyCode: 0xde})).to.be.true; // Ø key on DK
});
it('should suppress non-character keys', function() {
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {});
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
expect(obj.keydown({keyCode: 0x08}), 'a').to.be.true;
expect(obj.keydown({keyCode: 0x09}), 'b').to.be.true;
......@@ -110,20 +110,20 @@ describe('Key Event Pipeline Stages', function() {
expect(obj.keydown({keyCode: 0x12}), 'e').to.be.true;
});
it('should not suppress shift', function() {
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {});
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
expect(obj.keydown({keyCode: 0x10}), 'd').to.be.false;
});
it('should generate event for shift keydown', function() {
var called = false;
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
expect(evt).to.have.property('keysym');
called = true;
}).keydown({keyCode: 0x10});
expect(called).to.be.true;
});
it('should not suppress character keys', function() {
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {});
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
expect(obj.keydown({keyCode: 'A'.charCodeAt()})).to.be.false;
expect(obj.keydown({keyCode: ' '.charCodeAt()})).to.be.false;
......@@ -132,7 +132,7 @@ describe('Key Event Pipeline Stages', function() {
expect(obj.keydown({keyCode: 0xde})).to.be.false; // Ø key on DK
});
it('should not suppress if a char modifier is down', function() {
var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) {});
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) {});
obj.keydown({keyCode: 0xe1}); // press altgr
expect(obj.keydown({keyCode: 'A'.charCodeAt()})).to.be.false;
......@@ -144,7 +144,7 @@ describe('Key Event Pipeline Stages', function() {
});
describe('Keypress and keyup events', function() {
it('should always suppress event propagation', function() {
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {});
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {});
expect(obj.keypress({keyCode: 'A'.charCodeAt()})).to.be.true;
expect(obj.keypress({keyCode: 0x3c})).to.be.true; // < key on DK Windows
......@@ -155,7 +155,7 @@ describe('Key Event Pipeline Stages', function() {
expect(obj.keyup({keyCode: 0x11})).to.be.true;
});
it('should never generate stalls', function() {
var obj = KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
expect(evt.type).to.not.be.equal('stall');
});
......@@ -171,7 +171,7 @@ describe('Key Event Pipeline Stages', function() {
describe('mark events if a char modifier is down', function() {
it('should not mark modifiers on a keydown event', function() {
var times_called = 0;
var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) {
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) {
switch (times_called++) {
case 0: //altgr
break;
......@@ -187,7 +187,7 @@ describe('Key Event Pipeline Stages', function() {
it('should indicate on events if a single-key char modifier is down', function(done) {
var times_called = 0;
var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) {
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) {
switch (times_called++) {
case 0: //altgr
break;
......@@ -208,7 +208,7 @@ describe('Key Event Pipeline Stages', function() {
});
it('should indicate on events if a multi-key char modifier is down', function(done) {
var times_called = 0;
var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xffe9, 0xffe3]), function(evt) {
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xffe9, 0xffe3]), function(evt) {
switch (times_called++) {
case 0: //ctrl
break;
......@@ -231,7 +231,7 @@ describe('Key Event Pipeline Stages', function() {
obj.keypress({keyCode: 'A'.charCodeAt()});
});
it('should not consider a char modifier to be down on the modifier key itself', function() {
var obj = KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) {
var obj = KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) {
expect(evt).to.not.have.property('escape');
});
......@@ -241,13 +241,13 @@ describe('Key Event Pipeline Stages', function() {
});
describe('add/remove keysym', function() {
it('should remove keysym from keydown if a char key and no modifier', function() {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
expect(evt).to.be.deep.equal({keyId: 0x41, type: 'keydown'});
}).keydown({keyCode: 0x41});
});
it('should not remove keysym from keydown if a shortcut modifier is down', function() {
var times_called = 0;
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
switch (times_called++) {
case 1:
expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keydown'});
......@@ -258,7 +258,7 @@ describe('Key Event Pipeline Stages', function() {
});
it('should not remove keysym from keydown if a char modifier is down', function() {
var times_called = 0;
KeyEventDecoder(kbdUtil.ModifierSync([0xfe03]), function(evt) {
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync([0xfe03]), function(evt) {
switch (times_called++) {
case 2:
expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keydown'});
......@@ -268,21 +268,21 @@ describe('Key Event Pipeline Stages', function() {
expect(times_called).to.be.equal(3);
});
it('should not remove keysym from keydown if key is noncharacter', function() {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
expect(evt, 'bacobjpace').to.be.deep.equal({keyId: 0x09, keysym: keysyms.lookup(0xff09), type: 'keydown'});
}).keydown({keyCode: 0x09});
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
expect(evt, 'ctrl').to.be.deep.equal({keyId: 0x11, keysym: keysyms.lookup(0xffe3), type: 'keydown'});
}).keydown({keyCode: 0x11});
});
it('should never remove keysym from keypress', function() {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keypress'});
}).keypress({keyCode: 0x41});
});
it('should never remove keysym from keyup', function() {
KeyEventDecoder(kbdUtil.ModifierSync(), function(evt) {
KeyboardUtil.KeyEventDecoder(KeyboardUtil.ModifierSync(), function(evt) {
expect(evt).to.be.deep.equal({keyId: 0x41, keysym: keysyms.lookup(0x61), type: 'keyup'});
}).keyup({keyCode: 0x41});
});
......@@ -293,25 +293,25 @@ describe('Key Event Pipeline Stages', function() {
describe('Verify that char modifiers are active', function() {
it('should pass keydown events through if there is no stall', function(done) {
var obj = VerifyCharModifier(function(evt){
var obj = KeyboardUtil.VerifyCharModifier(function(evt){
expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x41)});
done();
})({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x41)});
});
it('should pass keyup events through if there is no stall', function(done) {
var obj = VerifyCharModifier(function(evt){
var obj = KeyboardUtil.VerifyCharModifier(function(evt){
expect(evt).to.deep.equal({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x41)});
done();
})({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x41)});
});
it('should pass keypress events through if there is no stall', function(done) {
var obj = VerifyCharModifier(function(evt){
var obj = KeyboardUtil.VerifyCharModifier(function(evt){
expect(evt).to.deep.equal({type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x41)});
done();
})({type: 'keypress', keyId: 0x41, keysym: keysyms.lookup(0x41)});
});
it('should not pass stall events through', function(done){
var obj = VerifyCharModifier(function(evt){
var obj = KeyboardUtil.VerifyCharModifier(function(evt){
// should only be called once, for the keydown
expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x41)});
done();
......@@ -322,7 +322,7 @@ describe('Key Event Pipeline Stages', function() {
});
it('should merge keydown and keypress events if they come after a stall', function(done) {
var next_called = false;
var obj = VerifyCharModifier(function(evt){
var obj = KeyboardUtil.VerifyCharModifier(function(evt){
// should only be called once, for the keydown
expect(next_called).to.be.false;
next_called = true;
......@@ -337,7 +337,7 @@ describe('Key Event Pipeline Stages', function() {
});
it('should preserve modifier attribute when merging if keysyms differ', function(done) {
var next_called = false;
var obj = VerifyCharModifier(function(evt){
var obj = KeyboardUtil.VerifyCharModifier(function(evt){
// should only be called once, for the keydown
expect(next_called).to.be.false;
next_called = true;
......@@ -351,7 +351,7 @@ describe('Key Event Pipeline Stages', function() {
expect(next_called).to.be.false;
});
it('should not preserve modifier attribute when merging if keysyms are the same', function() {
var obj = VerifyCharModifier(function(evt){
var obj = KeyboardUtil.VerifyCharModifier(function(evt){
expect(evt).to.not.have.property('escape');
});
......@@ -361,7 +361,7 @@ describe('Key Event Pipeline Stages', function() {
});
it('should not merge keydown and keypress events if there is no stall', function(done) {
var times_called = 0;
var obj = VerifyCharModifier(function(evt){
var obj = KeyboardUtil.VerifyCharModifier(function(evt){
switch(times_called) {
case 0:
expect(evt).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
......@@ -380,7 +380,7 @@ describe('Key Event Pipeline Stages', function() {
});
it('should not merge keydown and keypress events if separated by another event', function(done) {
var times_called = 0;
var obj = VerifyCharModifier(function(evt){
var obj = KeyboardUtil.VerifyCharModifier(function(evt){
switch(times_called) {
case 0:
expect(evt,1).to.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
......@@ -406,7 +406,7 @@ describe('Key Event Pipeline Stages', function() {
describe('Track Key State', function() {
it('should do nothing on keyup events if no keys are down', function() {
var obj = TrackKeyState(function(evt) {
var obj = KeyboardUtil.TrackKeyState(function(evt) {
expect(true).to.be.false;
});
obj({type: 'keyup', keyId: 0x41});
......@@ -415,7 +415,7 @@ describe('Key Event Pipeline Stages', function() {
var times_called = 0;
var elem = null;
var keysymsdown = {};
var obj = TrackKeyState(function(evt) {
var obj = KeyboardUtil.TrackKeyState(function(evt) {
++times_called;
if (elem.type == 'keyup') {
expect(evt).to.have.property('keysym');
......@@ -443,7 +443,7 @@ describe('Key Event Pipeline Stages', function() {
var times_called = 0;
var elem = null;
var keysymsdown = {};
var obj = TrackKeyState(function(evt) {
var obj = KeyboardUtil.TrackKeyState(function(evt) {
++times_called;
if (elem.type == 'keyup') {
expect(evt).to.have.property('keysym');
......@@ -472,7 +472,7 @@ describe('Key Event Pipeline Stages', function() {
var times_called = 0;
var elem = null;
var keysymsdown = {};
var obj = TrackKeyState(function(evt) {
var obj = KeyboardUtil.TrackKeyState(function(evt) {
++times_called;
if (elem.type == 'keyup') {
expect(evt).to.have.property('keysym');
......@@ -504,7 +504,7 @@ describe('Key Event Pipeline Stages', function() {
var times_called = 0;
var elem = null;
var keysymsdown = {};
var obj = TrackKeyState(function(evt) {
var obj = KeyboardUtil.TrackKeyState(function(evt) {
++times_called;
if (elem.type == 'keyup') {
expect(evt).to.have.property('keysym');
......@@ -540,7 +540,7 @@ describe('Key Event Pipeline Stages', function() {
var times_called = 0;
var elem = null;
var keysymsdown = {};
var obj = TrackKeyState(function(evt) {
var obj = KeyboardUtil.TrackKeyState(function(evt) {
++times_called;
if (elem.type == 'keyup') {
expect(evt).to.have.property('keysym');
......@@ -573,7 +573,7 @@ describe('Key Event Pipeline Stages', function() {
var times_called = 0;
var elem = null;
var keysymsdown = {};
var obj = TrackKeyState(function(evt) {
var obj = KeyboardUtil.TrackKeyState(function(evt) {
++times_called;
if (elem.type == 'keyup') {
expect(evt).to.have.property('keysym');
......@@ -608,7 +608,7 @@ describe('Key Event Pipeline Stages', function() {
var times_called = 0;
var elem = null;
var keysymsdown = {};
var obj = TrackKeyState(function(evt) {
var obj = KeyboardUtil.TrackKeyState(function(evt) {
++times_called;
if (elem.type == 'keyup') {
expect(evt).to.have.property('keysym');
......@@ -644,7 +644,7 @@ describe('Key Event Pipeline Stages', function() {
var times_called = 0;
var elem = null;
var keysymsdown = {};
var obj = TrackKeyState(function(evt) {
var obj = KeyboardUtil.TrackKeyState(function(evt) {
++times_called;
if (elem.type == 'keyup') {
expect(evt).to.have.property('keysym');
......@@ -676,7 +676,7 @@ describe('Key Event Pipeline Stages', function() {
});
it('should pop matching key event on keyup', function() {
var times_called = 0;
var obj = TrackKeyState(function(evt) {
var obj = KeyboardUtil.TrackKeyState(function(evt) {
switch (times_called++) {
case 0:
case 1:
......@@ -697,7 +697,7 @@ describe('Key Event Pipeline Stages', function() {
});
it('should pop the first zero keyevent on keyup with zero keyId', function() {
var times_called = 0;
var obj = TrackKeyState(function(evt) {
var obj = KeyboardUtil.TrackKeyState(function(evt) {
switch (times_called++) {
case 0:
case 1:
......@@ -718,7 +718,7 @@ describe('Key Event Pipeline Stages', function() {
});
it('should pop the last keyevents keysym if no match is found for keyId', function() {
var times_called = 0;
var obj = TrackKeyState(function(evt) {
var obj = KeyboardUtil.TrackKeyState(function(evt) {
switch (times_called++) {
case 0:
case 1:
......@@ -740,7 +740,7 @@ describe('Key Event Pipeline Stages', function() {
describe('Firefox sends keypress even when keydown is suppressed', function() {
it('should discard the keypress', function() {
var times_called = 0;
var obj = TrackKeyState(function(evt) {
var obj = KeyboardUtil.TrackKeyState(function(evt) {
expect(times_called).to.be.equal(0);
++times_called;
});
......@@ -753,7 +753,7 @@ describe('Key Event Pipeline Stages', function() {
describe('releaseAll', function() {
it('should do nothing if no keys have been pressed', function() {
var times_called = 0;
var obj = TrackKeyState(function(evt) {
var obj = KeyboardUtil.TrackKeyState(function(evt) {
++times_called;
});
obj({type: 'releaseall'});
......@@ -761,7 +761,7 @@ describe('Key Event Pipeline Stages', function() {
});
it('should release the keys that have been pressed', function() {
var times_called = 0;
var obj = TrackKeyState(function(evt) {
var obj = KeyboardUtil.TrackKeyState(function(evt) {
switch (times_called++) {
case 2:
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: keysyms.lookup(0x41)});
......@@ -787,7 +787,7 @@ describe('Key Event Pipeline Stages', function() {
describe('Keydown', function() {
it('should pass through when a char modifier is not down', function() {
var times_called = 0;
EscapeModifiers(function(evt) {
KeyboardUtil.EscapeModifiers(function(evt) {
expect(times_called).to.be.equal(0);
++times_called;
expect(evt).to.be.deep.equal({type: 'keydown', keyId: 0x41, keysym: keysyms.lookup(0x42)});
......@@ -796,7 +796,7 @@ describe('Key Event Pipeline Stages', function() {
});
it('should generate fake undo/redo events when a char modifier is down', function() {
var times_called = 0;
EscapeModifiers(function(evt) {
KeyboardUtil.EscapeModifiers(function(evt) {
switch(times_called++) {
case 0:
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0, keysym: keysyms.lookup(0xffe9)});
......@@ -821,7 +821,7 @@ describe('Key Event Pipeline Stages', function() {
describe('Keyup', function() {
it('should pass through when a char modifier is down', function() {
var times_called = 0;
EscapeModifiers(function(evt) {
KeyboardUtil.EscapeModifiers(function(evt) {
expect(times_called).to.be.equal(0);
++times_called;
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x42), escape: [0xfe03]});
......@@ -830,7 +830,7 @@ describe('Key Event Pipeline Stages', function() {
});
it('should pass through when a char modifier is not down', function() {
var times_called = 0;
EscapeModifiers(function(evt) {
KeyboardUtil.EscapeModifiers(function(evt) {
expect(times_called).to.be.equal(0);
++times_called;
expect(evt).to.be.deep.equal({type: 'keyup', keyId: 0x41, keysym: keysyms.lookup(0x42)});
......
#!/usr/bin/env node
var path = require('path');
var program = require('commander');
var fs = require('fs');
var fse = require('fs-extra');
var esprima = require('esprima');
program
.option('-b, --browserify', 'create a browserify bundled app')
.parse(process.argv);
// the various important paths
var core_path = path.resolve(__dirname, 'core');
var app_path = path.resolve(__dirname, 'app');
var out_dir_base = path.resolve(__dirname, 'build');
var out_dir_core = path.join(out_dir_base, 'novnc');
var out_dir_app = path.join(out_dir_base, 'app');
var out_dir_full = path.resolve(out_dir_base, 'full');
var find_js_files = function (dir) {
var filenames = fs.readdirSync(dir).filter(function (f) { return f.slice(-3) == '.js'; });
return filenames.map(function (f) { return path.resolve(dir, f); });
};
// load the list of files to process
var target_files_core = find_js_files(core_path);
var target_files_app = find_js_files(app_path);
// make sure the output directory exists
fse.ensureDir(out_dir_base);
fse.emptyDirSync(out_dir_core);
fse.emptyDirSync(out_dir_app);
var module_names = {};
var module_info = {};
var output_files = function (cb) {
// actually write the output files
var cnt = 0;
Object.keys(module_info).forEach(function (out_file, ind, keys) {
var info = module_info[out_file];
var contents = info.module;
contents = contents.replace(/\/\* \[as-module\] (.+) \*\//g, '$1');
contents = contents.replace(/\/\* \[begin skip-as-module\] \*\/(.|\n)+\/\* \[end skip-as-module\] \*\//g, '');
if (info.requires) {
var req_strs = info.requires.map(function (val) {
var req_info = module_info[module_names[val]];
var req_base_path = '.';
if (info.namespace !== req_info.namespace) {
req_base_path = path.join('..', req_info.namespace);
}
var req_path = path.join(req_base_path, req_info.filename);
return "var " + val + " = require('./" + req_path + "');";
});
contents = req_strs.join("\n") + contents;
}
contents = info.header + contents + '\nmodule.exports = ' + info.name + ';';
fs.writeFile(out_file, contents, function (err) {
if (err) { throw err; }
console.log("Wrote " + out_file);
cnt++;
if (cnt == keys.length) {
cb();
}
});
});
// write the index.js file for core
var rfb_file = module_info[module_names.RFB].filename;
var index_js_core = "module.exports = require('./" + rfb_file + "');;'";
var core_index_file = path.join(out_dir_core, 'index.js');
fs.writeFile(core_index_file, index_js_core, function (err) {
if (err) { throw err; }
console.log("Wrote " + core_index_file);
});
// write the index.js file for app
var index_js_app = "var UI = require('./ui');";
var app_index_file = path.join(out_dir_app, 'index.js');
fs.writeFile(app_index_file, index_js_app, function (err) {
if (err) { throw err; }
console.log("Wrote " + app_index_file);
});
};
// populate the module info
var load_files = function (target_files, out_dir, ns, cb) {
var cnt = 0;
target_files.forEach(function (file_path) {
fs.readFile(file_path, function (err, contents_raw) {
console.log("Processing '" + file_path + "'");
if (err) { throw err; }
var contents = contents_raw.toString();
var module_parts = contents.split('/* [module] ');
var module_header = module_parts[0];
module_parts = module_parts.slice(1);
module_parts.forEach(function (module_part) {
var info_end = module_part.indexOf('*/');
var info_raw = module_part.slice(0, info_end);
var module_rest = module_part.slice(info_end + 3);
var info_parts = info_raw.split(';');
var info = {};
info_parts.forEach(function (val) {
var val_parts = val.split(':');
info[val_parts[0].trim().toLowerCase()] = val_parts[1].trim();
});
if (info.requires) {
info.requires = info.requires.split(',').map(function (v) { return v.trim(); });
}
//var file_name = path.basename(file_path);
var mod_file_name = info.name.toLowerCase();
var file_name = mod_file_name + '.js';
var out_file = path.resolve(out_dir, file_name);
info.filename = mod_file_name;
info.module = module_rest;
info.header = module_header;
info.namespace = ns;
// set the name in the global list
module_names[info.name] = out_file;
module_info[out_file] = info;
});
cnt++;
if (cnt == target_files.length) {
cb();
}
});
});
};
var make_full_app = function () {
process.chdir(out_dir_base);
fse.emptyDirSync(out_dir_full);
var app_file = path.join(out_dir_full, 'app.js');
var browserify = require('browserify')();
browserify.add(path.join(out_dir_app, 'index.js'));
browserify.bundle().pipe(fs.createWriteStream(app_file));
console.log("Wrote ", app_file);
var src_dir_app = path.join(__dirname, 'app');
fs.readdir(src_dir_app, function (err, files) {
if (err) { throw err; }
files.forEach(function (src_file) {
var src_file_path = path.resolve(src_dir_app, src_file);
var out_file_path = path.resolve(out_dir_full, src_file);
var ext = path.extname(src_file);
if (ext === '.js' || ext === '.html') return;
fse.copy(src_file_path, out_file_path, function (err) {
if (err) { throw err; }
console.log("Copied file(s) from " + src_file_path + " to " + out_file_path);
});
});
});
var src_html_path = path.resolve(src_dir_app, 'vnc.html');
var out_html_path = path.resolve(out_dir_full, 'vnc.html');
fs.readFile(src_html_path, function (err, contents_raw) {
if (err) { throw err; }
var contents = contents_raw.toString();
var start_marker = '<!-- begin scripts -->\n';
var end_marker = '<!-- end scripts -->';
var start_ind = contents.indexOf(start_marker) + start_marker.length;
var end_ind = contents.indexOf(end_marker, start_ind);
contents = contents.slice(0, start_ind) + '<script src="app.js"></script>\n' + contents.slice(end_ind);
fs.writeFile(out_html_path, contents, function (err) {
if (err) { throw err; }
console.log("Wrote " + out_html_path);
});
});
};
load_files(target_files_core, out_dir_core, 'novnc', function () {
load_files(target_files_app, out_dir_app, 'app', output_files.bind(null, function () {
if (program.browserify) {
make_full_app();
} else {
fs.emptyDirSync(out_dir_full);
fs.rmdir(out_dir_full);
}
}));
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment