Put this project under version control, finally.
This commit is contained in:
commit
8dec6dff87
11 changed files with 3461 additions and 0 deletions
485
rlgterm.js
Normal file
485
rlgterm.js
Normal file
|
|
@ -0,0 +1,485 @@
|
|||
/* rlgterm.js: Roguelike Gallery's driver for termemu.js */
|
||||
|
||||
// A state machine that keeps track of polling the server.
|
||||
var ajaxstate = {
|
||||
state: 0,
|
||||
timerID: null,
|
||||
clear: function () {
|
||||
if (this.timerID != null) {
|
||||
window.clearTimeout(this.timerID);
|
||||
this.timerID = null;
|
||||
}
|
||||
},
|
||||
set: function (ms) {
|
||||
this.clear();
|
||||
this.timerID = window.setTimeout(getData, ms);
|
||||
},
|
||||
gotdata: function () {
|
||||
this.set(100);
|
||||
this.state = 0;
|
||||
},
|
||||
gotnothing: function () {
|
||||
if (this.state == 0) {
|
||||
this.set(100);
|
||||
this.state = 1;
|
||||
}
|
||||
else if (this.state == 1) {
|
||||
this.set(300);
|
||||
this.state = 2;
|
||||
}
|
||||
else if (this.state == 2) {
|
||||
this.set(1000);
|
||||
this.state = 3;
|
||||
}
|
||||
else {
|
||||
this.set(5000);
|
||||
this.state = 3;
|
||||
}
|
||||
},
|
||||
posted: function () {
|
||||
this.set(100);
|
||||
this.state = 0;
|
||||
}
|
||||
};
|
||||
|
||||
function writeData(hexstr) {
|
||||
var codenum;
|
||||
var codes = [];
|
||||
var nc;
|
||||
var u8wait = 0; /* Stores bits from previous bytes of multibyte sequences. */
|
||||
var expect = 0; /* The number of 10------ bytes expected. */
|
||||
/* UTF-8 translation. */
|
||||
for (var i = 0; i < hexstr.length; i += 2) {
|
||||
nc = Number("0x" + hexstr.substr(i, 2));
|
||||
if (nc < 0x7F) {
|
||||
/* 0------- */
|
||||
codes.push(nc);
|
||||
/* Any incomplete sequence will be discarded. */
|
||||
u8wait = 0;
|
||||
expect = 0;
|
||||
}
|
||||
else if (nc < 0xC0) {
|
||||
/* 10------ : part of a multibyte sequence */
|
||||
if (expect > 0) {
|
||||
u8wait <<= 6;
|
||||
u8wait += (nc & 0x3F);
|
||||
expect--;
|
||||
if (expect == 0) {
|
||||
codes.push(u8wait);
|
||||
u8wait = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Assume an initial byte was missed. */
|
||||
u8wait = 0;
|
||||
}
|
||||
}
|
||||
/* These will all discard any incomplete sequence. */
|
||||
else if (nc < 0xE0) {
|
||||
/* 110----- : introduces 2-byte sequence */
|
||||
u8wait = (nc & 0x1F);
|
||||
expect = 1;
|
||||
}
|
||||
else if (nc < 0xF0) {
|
||||
/* 1110---- : introduces 3-byte sequence */
|
||||
u8wait = (nc & 0x0F);
|
||||
expect = 2;
|
||||
}
|
||||
else if (nc < 0xF8) {
|
||||
/* 11110--- : introduces 4-byte sequence */
|
||||
u8wait = (nc & 0x07);
|
||||
expect = 3;
|
||||
}
|
||||
else if (nc < 0xFC) {
|
||||
/* 111110-- : introduces 5-byte sequence */
|
||||
u8wait = (nc & 0x03);
|
||||
expect = 4;
|
||||
}
|
||||
else if (nc < 0xFE) {
|
||||
/* 1111110- : introduces 6-byte sequence */
|
||||
u8wait = (nc & 0x01);
|
||||
expect = 5;
|
||||
}
|
||||
else {
|
||||
/* 1111111- : should never appear */
|
||||
u8wait = 0;
|
||||
expect = 0;
|
||||
}
|
||||
/* Supporting all 31 bits is probably overkill... */
|
||||
}
|
||||
termemu.write(codes);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Processes a message from the server, returning true or false if it was a
|
||||
* data message with or without data, null if not data. */
|
||||
function processMsg(msg) {
|
||||
var msglines = msg.split("\n");
|
||||
var havedata = null;
|
||||
if (!msglines[0])
|
||||
return null;
|
||||
if (msglines[0].charAt(0) == 'd') {
|
||||
if (msglines[1]){
|
||||
writeData(msglines[1]);
|
||||
havedata = true;
|
||||
}
|
||||
else {
|
||||
havedata = false;
|
||||
}
|
||||
}
|
||||
else if (msglines[0] == "E1") {
|
||||
logout();
|
||||
}
|
||||
else if (msglines[0].charAt(0) == "T") {
|
||||
setTitle(msglines[1]);
|
||||
}
|
||||
else if (msglines[0] == "q1") {
|
||||
logout();
|
||||
}
|
||||
else {
|
||||
debug(1, "Unrecognized server message " + msglines[0]);
|
||||
}
|
||||
return havedata;
|
||||
}
|
||||
|
||||
function getData() {
|
||||
if (termemu.sessid == null)
|
||||
return;
|
||||
var datareq = new XMLHttpRequest();
|
||||
datareq.onreadystatechange = function () {
|
||||
if (datareq.readyState == 4 && datareq.status == 200) {
|
||||
var wasdata = processMsg(datareq.responseText);
|
||||
if (wasdata != null) {
|
||||
if (wasdata)
|
||||
ajaxstate.gotdata();
|
||||
else
|
||||
ajaxstate.gotnothing();
|
||||
}
|
||||
return;
|
||||
}
|
||||
};
|
||||
datareq.open('POST', '/feed', true);
|
||||
datareq.send("id=" + termemu.sessid);
|
||||
return;
|
||||
}
|
||||
|
||||
function postResponseHandler() {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
// We might want to do something with wasdata someday.
|
||||
var wasdata = processMsg(this.responseText);
|
||||
ajaxstate.posted();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function sendback(str) {
|
||||
/* For responding to terminal queries. */
|
||||
var datareq = new XMLHttpRequest();
|
||||
datareq.onreadystatechange = postResponseHandler;
|
||||
datareq.open('POST', '/feed', true);
|
||||
datareq.send("id=" + termemu.sessid + "&keys=" + str);
|
||||
return;
|
||||
}
|
||||
|
||||
/* ASCII values of keys 0-9. */
|
||||
var numShifts = [41, 33, 64, 35, 36, 37, 94, 38, 42, 40];
|
||||
|
||||
var keyHexCodes = {
|
||||
init: function () {
|
||||
this[KeyboardEvent.DOM_VK_RETURN] = ["0d", "0d"];
|
||||
this[KeyboardEvent.DOM_VK_SPACE] = ["20", "20"];
|
||||
this[KeyboardEvent.DOM_VK_TAB] = ["09", "09"];
|
||||
this[KeyboardEvent.DOM_VK_BACK_QUOTE] = ["60", "7e"];
|
||||
this[KeyboardEvent.DOM_VK_OPEN_BRACKET] = ["5b", "7b"];
|
||||
this[KeyboardEvent.DOM_VK_CLOSE_BRACKET] = ["5d", "7d"];
|
||||
this[KeyboardEvent.DOM_VK_BACK_SLASH] = ["5c", "7c"];
|
||||
this[KeyboardEvent.DOM_VK_SEMICOLON] = ["3b", "3a"];
|
||||
this[KeyboardEvent.DOM_VK_QUOTE] = ["27", "22"];
|
||||
this[KeyboardEvent.DOM_VK_COMMA] = ["2c", "3c"];
|
||||
this[KeyboardEvent.DOM_VK_PERIOD] = ["2e", "3e"];
|
||||
this[KeyboardEvent.DOM_VK_SLASH] = ["2f", "3f"];
|
||||
this[KeyboardEvent.DOM_VK_EQUALS] = ["3d", "2b"];
|
||||
this[KeyboardEvent.DOM_VK_SUBTRACT] = ["2d", "5f"];
|
||||
this[KeyboardEvent.DOM_VK_BACK_SPACE] = ["08", "08"];
|
||||
this[KeyboardEvent.DOM_VK_ESCAPE] = ["1b", "1b"];
|
||||
/* Multi-char control sequences! Neat! */
|
||||
this[KeyboardEvent.DOM_VK_PAGE_UP] = ["1b5b357e", "1b5b357e"];
|
||||
this[KeyboardEvent.DOM_VK_PAGE_DOWN] = ["1b5b367e", "1b5b367e"];
|
||||
this.appCursor(false);
|
||||
this.appKeypad(false);
|
||||
},
|
||||
appCursor: function (on) {
|
||||
if (on) {
|
||||
this[KeyboardEvent.DOM_VK_LEFT] = ["1b4f44", "1b4f44"];
|
||||
this[KeyboardEvent.DOM_VK_RIGHT] = ["1b4f43", "1b4f43"];
|
||||
this[KeyboardEvent.DOM_VK_UP] = ["1b4f41", "1b4f41"];
|
||||
this[KeyboardEvent.DOM_VK_DOWN] = ["1b4f42", "1b4f42"];
|
||||
this[KeyboardEvent.DOM_VK_END] = ["1b4f46", "1b4f46"];
|
||||
this[KeyboardEvent.DOM_VK_HOME] = ["1b4f48", "1b4f48"];
|
||||
}
|
||||
else {
|
||||
this[KeyboardEvent.DOM_VK_LEFT] = ["1b5b44", "1b5b44"];
|
||||
this[KeyboardEvent.DOM_VK_RIGHT] = ["1b5b43", "1b5b43"];
|
||||
this[KeyboardEvent.DOM_VK_UP] = ["1b5b41", "1b5b41"];
|
||||
this[KeyboardEvent.DOM_VK_DOWN] = ["1b5b42", "1b5b42"];
|
||||
this[KeyboardEvent.DOM_VK_END] = ["1b5b46", "1b5b46"];
|
||||
this[KeyboardEvent.DOM_VK_HOME] = ["1b5b48", "1b5b48"];
|
||||
}
|
||||
},
|
||||
appKeypad: function (on) {
|
||||
/* In theory, these should produce either numerals or the k[a-c][1-3]
|
||||
* sequences. Since we can't count on the terminfo description actually
|
||||
* containing those sequences, pretend they're just arrow keys etc.
|
||||
*/
|
||||
this[KeyboardEvent.DOM_VK_NUMPAD1] = ["1b4f46", "1b4f46"];
|
||||
this[KeyboardEvent.DOM_VK_NUMPAD2] = ["1b4f42", "1b4f42"];
|
||||
this[KeyboardEvent.DOM_VK_NUMPAD3] = ["1b5b367e", "1b5b367e"];
|
||||
this[KeyboardEvent.DOM_VK_NUMPAD4] = ["1b4f44", "1b4f44"];
|
||||
this[KeyboardEvent.DOM_VK_NUMPAD5] = ["1b5b45", "1b5b45"];
|
||||
this[KeyboardEvent.DOM_VK_NUMPAD6] = ["1b4f43", "1b4f43"];
|
||||
this[KeyboardEvent.DOM_VK_NUMPAD7] = ["1b4f48", "1b4f48"];
|
||||
this[KeyboardEvent.DOM_VK_NUMPAD8] = ["1b4f41", "1b4f41"];
|
||||
this[KeyboardEvent.DOM_VK_NUMPAD9] = ["1b5b357e", "1b5b357e"];
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
function sendkey(ev) {
|
||||
if (termemu.sessid == null)
|
||||
return;
|
||||
var keynum = ev.keyCode;
|
||||
var code;
|
||||
if (keynum >= ev.DOM_VK_A && keynum <= ev.DOM_VK_Z) {
|
||||
/* Letters. This assumes the codes are 65-90. */
|
||||
if (ev.ctrlKey)
|
||||
keynum -= 64;
|
||||
else if (!ev.shiftKey)
|
||||
keynum += 32;
|
||||
code = keynum.toString(16);
|
||||
if (code.length < 2)
|
||||
code = "0" + code;
|
||||
}
|
||||
else if (keynum >= ev.DOM_VK_0 && keynum <= ev.DOM_VK_9) {
|
||||
/* The number row, NOT the numpad. */
|
||||
if (ev.shiftKey) {
|
||||
code = numShifts[keynum - 48].toString(16);
|
||||
}
|
||||
else {
|
||||
code = keynum.toString(16);
|
||||
}
|
||||
}
|
||||
else if (keynum in keyHexCodes) {
|
||||
if (ev.shiftKey)
|
||||
code = keyHexCodes[keynum][1];
|
||||
else
|
||||
code = keyHexCodes[keynum][0];
|
||||
}
|
||||
else if (keynum == ev.DOM_VK_SHIFT || keynum == ev.DOM_VK_CONTROL ||
|
||||
keynum == ev.DOM_VK_ALT || keynum == ev.DOM_VK_CAPS_LOCK) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
debug(1, "Ignoring keycode " + keynum);
|
||||
return;
|
||||
}
|
||||
if (termemu.sessid != null)
|
||||
ev.preventDefault();
|
||||
var datareq = new XMLHttpRequest();
|
||||
datareq.onreadystatechange = postResponseHandler;
|
||||
datareq.open('POST', '/feed', true);
|
||||
datareq.send("id=" + termemu.sessid + "&keys=" + code);
|
||||
return;
|
||||
}
|
||||
|
||||
var charshifts = { '-': "5f", '=': "2b", '[': "7b", ']': "7d", '\\': "7c",
|
||||
';': "3a", '\'': "22", ',': "3c", '.': "3e", '/': "3f", '`': "7e"
|
||||
}
|
||||
|
||||
function vkey(c) {
|
||||
if (termemu.sessid == null)
|
||||
return;
|
||||
var keystr;
|
||||
if (c.match(/^[a-z]$/)) {
|
||||
if (termemu.ctrlp()) {
|
||||
var n = c.charCodeAt(0) - 96;
|
||||
keystr = n.toString(16);
|
||||
if (keystr.length < 2)
|
||||
keystr = "0" + keystr;
|
||||
}
|
||||
else if (termemu.shiftp())
|
||||
keystr = c.toUpperCase().charCodeAt(0).toString(16);
|
||||
else
|
||||
keystr = c.charCodeAt(0).toString(16);
|
||||
}
|
||||
else if (c.match(/^[0-9]$/)) {
|
||||
if (termemu.shiftp())
|
||||
keystr = numShifts[c.charCodeAt(0) - 48].toString(16);
|
||||
else
|
||||
keystr = c.charCodeAt(0).toString(16);
|
||||
}
|
||||
else if (c == '\n')
|
||||
keystr = "0a";
|
||||
else if (c == '\t')
|
||||
keystr = "09";
|
||||
else if (c == '\b')
|
||||
keystr = "08";
|
||||
else if (c == ' ')
|
||||
keystr = "20";
|
||||
else if (c in charshifts) {
|
||||
if (termemu.shiftp())
|
||||
keystr = charshifts[c];
|
||||
else
|
||||
keystr = c.charCodeAt(0).toString(16);
|
||||
}
|
||||
else
|
||||
return;
|
||||
//writeData("Sending " + keystr);
|
||||
var datareq = new XMLHttpRequest();
|
||||
datareq.onreadystatechange = postResponseHandler;
|
||||
datareq.open('POST', '/feed', true);
|
||||
datareq.send("id=" + termemu.sessid + "&keys=" + keystr);
|
||||
return;
|
||||
}
|
||||
|
||||
function setup() {
|
||||
keyHexCodes.init();
|
||||
termemu.init("termwrap");
|
||||
setTitle("Not connected.");
|
||||
return;
|
||||
}
|
||||
|
||||
function toggleshift() {
|
||||
termemu.toggleshift();
|
||||
keydiv = document.getElementById("shiftkey");
|
||||
if (termemu.shiftp())
|
||||
keydiv.className = "keysel";
|
||||
else
|
||||
keydiv.className = "key";
|
||||
return;
|
||||
}
|
||||
|
||||
function togglectrl() {
|
||||
termemu.togglectrl();
|
||||
keydiv = document.getElementById("ctrlkey");
|
||||
if (termemu.ctrlp())
|
||||
keydiv.className = "keysel";
|
||||
else
|
||||
keydiv.className = "key";
|
||||
return;
|
||||
}
|
||||
|
||||
function formlogin(ev) {
|
||||
ev.preventDefault();
|
||||
if (termemu.sessid != null)
|
||||
return;
|
||||
var formname = document.getElementById("input_name").value;
|
||||
var formpass = document.getElementById("input_pw").value;
|
||||
var formgame = document.getElementById("input_game").value;
|
||||
var formdata = "game=" + encodeURIComponent(formgame) + "&name=" + encodeURIComponent(formname) + "&pw=" + encodeURIComponent(formpass);
|
||||
var req = new XMLHttpRequest();
|
||||
req.onreadystatechange = function () {
|
||||
if (req.readyState == 4 && req.status == 200) {
|
||||
var datalines = req.responseText.split("\n");
|
||||
if (datalines[0] == 'l1') {
|
||||
/* Success */
|
||||
termemu.sessid = datalines[1];
|
||||
setTitle("Playing as " + formname);
|
||||
debug(1, "Logged in with id " + termemu.sessid);
|
||||
document.getElementById("loginform").style.display = "none";
|
||||
getData();
|
||||
}
|
||||
else {
|
||||
debug(1, "Could not start game: " + datalines[1]);
|
||||
document.getElementById("input_name").value = "";
|
||||
document.getElementById("input_pw").value = "";
|
||||
}
|
||||
}
|
||||
};
|
||||
req.open('POST', '/login', true);
|
||||
req.send(formdata);
|
||||
return;
|
||||
}
|
||||
|
||||
function logout() {
|
||||
if (termemu.sessid == null)
|
||||
return;
|
||||
termemu.sessid = null;
|
||||
setTitle("Game over.");
|
||||
document.getElementById("loginform").style.display = "block";
|
||||
return;
|
||||
}
|
||||
|
||||
function stop() {
|
||||
var req = new XMLHttpRequest();
|
||||
req.onreadystatechange = function () {
|
||||
if (req.readyState == 4 && req.status == 200) {
|
||||
processMsg(req.responseText);
|
||||
return;
|
||||
}
|
||||
};
|
||||
req.open('POST', '/feed', true);
|
||||
req.send("id=" + termemu.sessid + "&quit=quit");
|
||||
return;
|
||||
}
|
||||
|
||||
function debug(level, msg) {
|
||||
if (level < debugSuppress)
|
||||
return;
|
||||
var msgdiv = document.createElement("div");
|
||||
var msgtext = document.createTextNode(msg);
|
||||
msgdiv.appendChild(msgtext);
|
||||
document.getElementById("debug").appendChild(msgdiv);
|
||||
return;
|
||||
}
|
||||
|
||||
function textsize(larger) {
|
||||
var cssSize = termemu.view.style.fontSize;
|
||||
if (!cssSize) {
|
||||
return;
|
||||
}
|
||||
var match = cssSize.match(/\d*/);
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
var csize = Number(match[0]);
|
||||
var nsize;
|
||||
if (larger) {
|
||||
if (csize >= 48)
|
||||
nsize = 48;
|
||||
else if (csize >= 20)
|
||||
nsize = csize + 4;
|
||||
else if (csize >= 12)
|
||||
nsize = csize + 2;
|
||||
else if (csize >= 8)
|
||||
nsize = csize + 1;
|
||||
else
|
||||
nsize = 8;
|
||||
}
|
||||
else {
|
||||
if (csize <= 8)
|
||||
nsize = 8;
|
||||
else if (csize <= 12)
|
||||
nsize = csize - 1;
|
||||
else if (csize <= 20)
|
||||
nsize = csize - 2;
|
||||
else if (csize <= 48)
|
||||
nsize = csize - 4;
|
||||
else
|
||||
nsize = 48;
|
||||
}
|
||||
document.getElementById("term").style.fontSize = nsize.toString() + "px";
|
||||
termemu.resize();
|
||||
debug(1, "Changing font size to " + nsize.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
function bell(on) {
|
||||
var imgnode = document.getElementById("bell");
|
||||
if (on) {
|
||||
imgnode.style.visibility = "visible";
|
||||
window.setTimeout(bell, 1500, false);
|
||||
}
|
||||
else
|
||||
imgnode.style.visibility = "hidden";
|
||||
return;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue