view shterm.js @ 195:3bdee6371c3f

Change various filenames. The shell script previously used to launch the daemon is now called "initscript". The script files have had the ".js" extension removed from their names.
author John "Elwin" Edwards
date Thu, 14 Jan 2016 20:52:29 -0500
parents c4a32007d2dc
children
line wrap: on
line source

/* shterm.js: browser-side JavaScript to handle I/O for termemu.js when it 
 * is running a shell via the webtty.js server.
 */

var isalive = false; // Whether the session is currently active.
var conn = null; // WebSocket

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

function sendback(str) {
  /* For responding to terminal queries. */
  if (conn) {
    var msgObj = {"t": "d", "d": str};
    conn.send(JSON.stringify(msgObj));
  }
  else {
    debug(1, "No connection: cannot send " + str);
  }
  return;
}

function sendkey(ev) {
  if (!isalive)
    return;
  var keynum = ev.keyCode;
  var code;
  if (keynum >= 65 && keynum <= 90) {
    /* Letters. */
    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 >= 48 && keynum <= 57) {
    /* The number row. */
    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 >= 16 && keynum <= 20) {
    return;
  }
  else {
    debug(1, "Ignoring keycode " + keynum);
    return;
  }
  ev.preventDefault();
  var msgObj = {"t": "d", "d": code};
  conn.send(JSON.stringify(msgObj));
  //dkey(code);
  return;
}

var charshifts = { '-': "5f", '=': "2b", '[': "7b", ']': "7d", '\\': "7c",
  ';': "3a", '\'': "22", ',': "3c", '.': "3e", '/': "3f", '`': "7e"
}

var kpkeys = { "KP1": "1b4f46", "KP2": "1b4f42", "KP3": "1b5b367e", 
               "KP4": "1b4f44", "KP5": "1b5b45", "KP6": "1b4f43", 
               "KP7": "1b4f48", "KP8": "1b4f41", "KP9": "1b5b357e" };

function vkey(c) {
  if (!isalive)
    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 if (c in kpkeys) {
    keystr = kpkeys[c];
  }
  else
    return;
  //writeData("Sending " + keystr);
  var msgObj = {"t": "d", "d": keystr};
  conn.send(JSON.stringify(msgObj));
  return;
}

function setup() {
  keyHexCodes.init();
  termemu.init("termwrap", 24, 80);
  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 login(h, w) {
  if (conn)
    return;
  if (!(window.WebSocket)) {
    debug(1, "Cannot connect: WebSockets not supported");
    return;
  }
  var sockurl = "ws://" + window.location.host + "/sock?w=" + w + "&h=" + h;
  conn = new WebSocket(sockurl);
  conn.onopen = function (event) {
    isalive = true;
    setTitle("Logged in");
    debug(1, "Logged in via WebSocket");
  }
  conn.onmessage = function (event) {
    var msgObj = JSON.parse(event.data);
    if (msgObj.t == 'l') {
      termemu.resize(msgObj.h, msgObj.w);
    }
    else if (msgObj.t == 'd') {
      debug(0, msgObj.d);
      writeData(msgObj.d);
    }
    else if (msgObj.t == 'q') {
      debug(0, "Quit message!");
      conn.close();
    }
  }
  conn.onclose = function (event) {
    conn = null;
    isalive = false;
    debug(1, "WebSocket connection closed.");
    setTitle("Not connected.");
  }
}

function stop() {
  if (conn) {
    conn.close();
  }
  else {
    debug(1, "Cannot stop: connection already closed");
  }
  return;
}

function setTitle(tstr) {
  var titlespan = document.getElementById("ttitle");
  var tnode = document.createTextNode(tstr);
  if (titlespan.childNodes.length == 0)
    titlespan.appendChild(tnode);
  else
    titlespan.replaceChild(tnode, titlespan.childNodes[0]);
  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;
}

/* This should be a termemu method. */
function textsize(larger) {
  var cssSize = document.getElementById("term").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.fixsize();
  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;
}

function dkey(codestr) {
  var dstr = "Keystring: ";
  for (var i = 0; i < codestr.length; i += 2) {
    code = Number("0x" + codestr.substr(i, 2));
    if (code < 32 || (code >= 127 && code < 160))
      dstr += "\\x" + code.toString(16);
    else
      dstr += String.fromCharCode(code);
  }
  debug(1, dstr);
  return;
}