464 lines
8.5 KiB
JavaScript
464 lines
8.5 KiB
JavaScript
// VD.js -- a viewdata terminal emulator
|
|
|
|
// constructor
|
|
function VD(wd, ht, scr_id, initscr)
|
|
{
|
|
var r;
|
|
var c;
|
|
var scr = document.getElementById(scr_id);
|
|
|
|
this.wd_ = wd;
|
|
this.ht_ = ht;
|
|
this.text_ = new Array(ht);
|
|
for (r=0; r<ht; ++r) {
|
|
this.text_[r] = new Array(wd);
|
|
}
|
|
|
|
this.scr_ = scr;
|
|
this.cursor_vis_ = true;
|
|
this.reveal_ = 0;
|
|
this.getch_isr_ = undefined;
|
|
this.grab_events_ = false;
|
|
this.key_buf_ = [];
|
|
this.esc_state_ = 0;
|
|
this.col_ = 0;
|
|
this.row_ = 0;
|
|
|
|
// Initial Screen
|
|
if (typeof initscr !== 'undefined') {
|
|
this.write("\x0c" + this.txthash(initscr) + "\x14\x1e");
|
|
} else {
|
|
this.write("\x0c\x11\x1e");
|
|
}
|
|
}
|
|
|
|
VD.A_REVERSE = 2;
|
|
VD.A_BLINK = 4;
|
|
|
|
// class variables
|
|
VD.the_vt_ = undefined;
|
|
|
|
// object methods
|
|
VD.prototype.html_colours_ = function(at_fg, at_bg, at_mode) {
|
|
var co0, co1;
|
|
if (at_mode & VD.A_REVERSE) {
|
|
co0 = 'ff';
|
|
co1 = '00';
|
|
} else {
|
|
co0 = '00';
|
|
co1 = 'ff';
|
|
}
|
|
|
|
return {
|
|
f: '#' + (at_fg & 1 ? co1 : co0) +
|
|
(at_fg & 2 ? co1 : co0) +
|
|
(at_fg & 4 ? co1 : co0),
|
|
b: '#' + (at_bg & 1 ? co1 : co0) +
|
|
(at_bg & 2 ? co1 : co0) +
|
|
(at_bg & 4 ? co1 : co0)
|
|
};
|
|
}
|
|
|
|
VD.prototype.txthash = function(hashstring) {
|
|
var currentcode = 0, stuff = '';
|
|
|
|
if (hashstring.indexOf(':') > -1)
|
|
var hashstring = hashstring.split(':')[1];
|
|
|
|
hashstring = hashstring.substring(0,this.wd_*this.ht_);
|
|
|
|
for (var p=0; p<hashstring.length; p++) {
|
|
var pc = hashstring.charAt(p);
|
|
var pc_dec = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
|
|
.indexOf(hashstring.charAt(p));
|
|
|
|
// b is the bit in the 6-bit base-64 character.
|
|
for (var b=0; b<6; b++) {
|
|
// The current bit position in the character being
|
|
// written to.
|
|
var charbit = (6*p+b) % 7;
|
|
|
|
// The bit value (set or unset) of the bit we're
|
|
// reading from.
|
|
var b64bit = pc_dec & (1 << (5-b));
|
|
if ( b64bit > 0 ) {
|
|
b64bit = 1;
|
|
}
|
|
|
|
// Update the current code.
|
|
currentcode |= b64bit << (6-charbit);
|
|
|
|
// If we've reached the end of this character cell
|
|
// and it's the last bit in the character we're
|
|
// writing to, set the character code or place the
|
|
// code.
|
|
if (charbit === 6) {
|
|
if (currentcode < 32) {
|
|
stuff += '\x1b';
|
|
currentcode += 64;
|
|
}
|
|
|
|
stuff += String.fromCharCode(currentcode);
|
|
currentcode = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return stuff;
|
|
}
|
|
|
|
VD.prototype.clear = function() {
|
|
this.row_ = this.col_ = 0;
|
|
|
|
for (r=0; r<this.ht_; ++r) {
|
|
for (c = 0; c < this.wd_; ++c) {
|
|
this.text_[r][c] = ' ';
|
|
}
|
|
}
|
|
}
|
|
|
|
VD.prototype.curs_set = function(vis) {
|
|
if (vis !== undefined)
|
|
this.cursor_vis_ = (vis > 0);
|
|
}
|
|
|
|
VD.prototype.getch = function(isr) {
|
|
this.getch_isr_ = isr;
|
|
|
|
setTimeout(VD.go_getch_, 0);
|
|
}
|
|
|
|
VD.go_getch_ = function VD_go_getch() {
|
|
var vt = VD.the_vt_;
|
|
if (vt === undefined)
|
|
return;
|
|
|
|
var isr = vt.getch_isr_;
|
|
vt.getch_isr_ = undefined;
|
|
if (isr === undefined)
|
|
return;
|
|
|
|
var ch = vt.key_buf_.shift();
|
|
if (ch === undefined) {
|
|
vt.getch_isr_ = isr;
|
|
return;
|
|
}
|
|
|
|
isr(ch,vt);
|
|
}
|
|
|
|
VD.prototype.move = function(r, c) {
|
|
if (r < 0)
|
|
r = 0;
|
|
|
|
else if (r >= this.ht_)
|
|
r = this.ht_ - 1;
|
|
|
|
if (c < 0)
|
|
c = 0;
|
|
|
|
else if (c >= this.wd_)
|
|
c = this.wd_ - 1;
|
|
this.row_ = r;
|
|
this.col_ = c;
|
|
}
|
|
|
|
VD.prototype.refresh = function() {
|
|
var r, c, stuff = "", start_tag = "", end_tag = "", ch, chtype,
|
|
pair, cr, cc, ht, wd, cv, n_fg, n_bg, n_mode, n_type, a_fg=-1, a_bg=-1, a_mode=-1, n_dblnextline=-2, n_heldchar, n_heldtype;
|
|
ht = this.ht_;
|
|
wd = this.wd_;
|
|
cr = this.row_;
|
|
cc = this.col_;
|
|
cv = this.cursor_vis_;
|
|
|
|
if (cc >= wd)
|
|
cc = wd - 1;
|
|
|
|
for (r = 0; r < ht; ++r) {
|
|
if (r > 0) {
|
|
stuff += '\n';
|
|
}
|
|
|
|
n_fg = 7;
|
|
n_bg = 0;
|
|
n_mode = 0;
|
|
n_type = 0;
|
|
n_heldchar = 32;
|
|
n_heldtype = 0;
|
|
|
|
if (n_dblnextline+1 < r)
|
|
n_dblnextline = -2;
|
|
|
|
for (c = 0; c < wd; ++c) {
|
|
if (cv && r == cr && c == cc) {
|
|
// Draw the cursor here.
|
|
n_mode |= VD.A_REVERSE;
|
|
} else {
|
|
n_mode &= ~VD.A_REVERSE;
|
|
}
|
|
|
|
if (r == n_dblnextline+1) {
|
|
ch = this.text_[r-1][c].charCodeAt(0);
|
|
} else {
|
|
ch = this.text_[r][c].charCodeAt(0);
|
|
}
|
|
|
|
ctrlch = ch;
|
|
|
|
// set-at
|
|
if (ctrlch == 0x89) // steady
|
|
n_mode &= ~VD.A_BLINK;
|
|
|
|
if (ctrlch == 0x8c) { // normal
|
|
if (n_type & 8) {
|
|
n_heldchar = 32;
|
|
n_heldtype = 0;
|
|
}
|
|
n_type &= ~8;
|
|
}
|
|
|
|
if (ctrlch == 0x98 && this.reveal_ == false) // conceal
|
|
n_type |= 16;
|
|
|
|
if (ctrlch == 0x99) // contig
|
|
n_type &= ~1;
|
|
|
|
if (ctrlch == 0x9a) // sep
|
|
n_type |= 1;
|
|
|
|
if (ctrlch == 0x9c) // black
|
|
n_bg = 0;
|
|
|
|
if (ctrlch == 0x9d) // newback
|
|
n_bg = n_fg;
|
|
|
|
if (ctrlch == 0x9e) // hold
|
|
n_type |= 32;
|
|
|
|
// If the attributes changed, make a new span.
|
|
if (n_mode != a_mode || n_fg != a_fg || n_bg != a_bg) {
|
|
stuff += end_tag;
|
|
pair = this.html_colours_(n_fg, n_bg, n_mode);
|
|
stuff += '<span style="color:' + pair.f + ';background-color:' + pair.b + ";" +'">'
|
|
end_tag = "</span>";
|
|
if (n_mode & VD.A_BLINK) {
|
|
stuff += '<span class="flashing";>';
|
|
end_tag += "</span>";
|
|
}
|
|
a_fg = n_fg;
|
|
a_bg = n_bg;
|
|
a_mode = n_mode;
|
|
}
|
|
|
|
chtype = n_type;
|
|
|
|
if (ch >= 128) {
|
|
if (n_type & 32) {
|
|
ch = n_heldchar;
|
|
chtype = n_heldtype;
|
|
} else {
|
|
ch = 32;
|
|
}
|
|
}
|
|
|
|
if (ch == 127) {
|
|
if (n_type & 2) {
|
|
ch = 0xe23f+((chtype & 1)*0xc0);
|
|
n_heldchar = 127;
|
|
} else {
|
|
ch = 0xb6;
|
|
}
|
|
}
|
|
|
|
if (n_type & 2) {
|
|
if ( ch >= 0x20 && ch <= 0x3f) {
|
|
n_heldchar = ch;
|
|
ch = ch+0xe1e0+((chtype & 1)*0xc0);
|
|
} else if ( ch >= 0x60 && ch <= 0x7e) {
|
|
n_heldchar = ch;
|
|
ch = ch+0xe1c0+((chtype & 1)*0xc0);
|
|
}
|
|
}
|
|
|
|
if (n_type & 16)
|
|
ch = 32;
|
|
|
|
n_heldtype = chtype;
|
|
|
|
switch (ch) {
|
|
case 32:
|
|
ch = 0xa0; break;
|
|
case 0x7e:
|
|
ch = 0xf7; break;
|
|
case 0x5f: // _
|
|
ch = 0x23; break // #
|
|
case 123: // {
|
|
ch = 0xbc; break
|
|
case 92: // \
|
|
ch = 0xbd; break
|
|
case 125: // }
|
|
ch = 0xbe; break
|
|
case 0x23: // #
|
|
ch = 0xa3; break
|
|
}
|
|
|
|
if (n_type & 8) {
|
|
if (ch < 0x100) {
|
|
ch = ch+0xe000+((r==n_dblnextline+1)*0x100);
|
|
} else {
|
|
ch = ch+0x40+((r==n_dblnextline+1)*0x40)
|
|
}
|
|
|
|
} else if (r==n_dblnextline+1) {
|
|
ch = 0xa0;
|
|
}
|
|
|
|
stuff += "&#x" + ch.toString(16) + ";"
|
|
|
|
// set-after
|
|
if (ctrlch >= 129 && ctrlch <= 135) {
|
|
if (n_type & 2) {
|
|
n_heldchar=32;
|
|
n_heldtype=0;
|
|
}
|
|
n_fg = ctrlch-128;
|
|
n_type &= ~2;
|
|
n_type &= ~16;
|
|
}
|
|
|
|
if (ctrlch >= 145 && ctrlch <= 151) {
|
|
if (n_type & 2 == 0) {
|
|
n_heldchar=32;
|
|
n_heldtype=0;
|
|
}
|
|
n_fg = ctrlch-144;
|
|
n_type |= 2;
|
|
n_type &= ~16;
|
|
}
|
|
|
|
if (ctrlch == 0x88) // flash
|
|
n_mode |= VD.A_BLINK;
|
|
|
|
if (ctrlch == 0x8d) { // double
|
|
if (n_type & 8 == 0) {
|
|
n_heldchar = 32;
|
|
n_heldtype = 0;
|
|
}
|
|
n_type |= 8;
|
|
if (n_dblnextline < 0)
|
|
n_dblnextline = r;
|
|
}
|
|
|
|
if (ctrlch == 0x9f) // release
|
|
n_type &= ~32;
|
|
}
|
|
}
|
|
|
|
stuff += end_tag
|
|
this.scr_.innerHTML = "<b>" + stuff + "</b>\n";
|
|
}
|
|
|
|
VD.prototype.write = function(stuff) {
|
|
var ch, i;
|
|
|
|
for (i = 0; i < stuff.length; ++i) {
|
|
ch = stuff.charCodeAt(i);
|
|
|
|
if (this.esc_state_ && ch > 31)
|
|
ch = (ch % 32) + 128;
|
|
|
|
this.esc_state_ = 0;
|
|
|
|
switch (ch) {
|
|
case 8:
|
|
if (this.col_ != 0) {
|
|
--this.col_;
|
|
} else {
|
|
this.col_ = this.wd_-1;
|
|
if (this.row_ == 0) {
|
|
this.row_ = this.ht_-1;
|
|
} else {
|
|
--this.row_;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case 9:
|
|
if (this.col_ >= this.wd_) {
|
|
this.col_ = 0;
|
|
if (this.row_ == this.ht_-1) {
|
|
this.row_ = 0;
|
|
} else {
|
|
++this.row_;
|
|
}
|
|
} else {
|
|
++this.col_;
|
|
}
|
|
|
|
break;
|
|
|
|
case 10:
|
|
if (this.row_ >= this.ht_-1) {
|
|
this.row_ = 0;
|
|
} else {
|
|
++this.row_;
|
|
}
|
|
|
|
break;
|
|
|
|
case 11:
|
|
if (this.row_ == 0) {
|
|
this.row_ = this.ht_-1;
|
|
} else {
|
|
--this.row_;
|
|
}
|
|
|
|
break;
|
|
|
|
case 12:
|
|
this.clear();
|
|
this.move(0, 0);
|
|
this.reveal_ = false;
|
|
|
|
break;
|
|
|
|
case 13:
|
|
this.col_ = 0;
|
|
break;
|
|
case 17:
|
|
this.curs_set(1);
|
|
break;
|
|
case 20:
|
|
this.curs_set(0);
|
|
break;
|
|
case 27:
|
|
this.esc_state_ = 1;
|
|
break;
|
|
case 30:
|
|
this.move(0, 0);
|
|
break;
|
|
default:
|
|
if (ch > 31) {
|
|
this.text_[this.row_][this.col_] = String.fromCharCode(ch);
|
|
|
|
if (this.col_ >= this.wd_-1) {
|
|
this.col_ = 0;
|
|
|
|
if (this.row_ >= this.ht_-1) {
|
|
this.row_ = 0;
|
|
} else {
|
|
++this.row_;
|
|
}
|
|
|
|
} else {
|
|
++this.col_;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this.refresh();
|
|
}
|