|
Home - Old Man Programmer
| Displaying webapps/vt100/vt100.js
var terminal = {
FGMASK : 15,
BGMASK : 240,
BOLD : 256,
UNDERLINE : 512,
REVERSE : 1024,
BLINK : 2048,
colors : [
"#000000", "#cd0000", "#00cd00", "#cdcd00", "#0000ee", "#cd00cd", "#00cdcd", "#e5e5e5",
"#7f7f7f", "#ff0000", "#00ff00", "#ffff00", "#5c5cff", "#ff00ff", "#00ffff", "#ffffff"
],
window : null, scrollback : null, tc : null,
kbmode : false,
sc : null,
m : null, row : null,
// m: { e : col, char : " ", fg : this.fg, bg: this.bg };
rows : 0, cols : 0,
homeRow: 0, homeCol : 0,
tmargin: 0, bmargin: 0,
cx : 0, cy : 0,
cq : "", cqlen : 0,
// Attributes:
fg : 7, bg : 0,
bold: false, underline: false, blink: false, reverse: false,
enablescrollback : false,
sbbuffer : [],
sbtimer : null,
mode : 0,
// 0 = normal, 1 = escape encountered:
// 2 = [ encountered, 3 = [? encountered,
// 4 = ) encountered, 5 = ( encountered
sbupdate : function() {
for(var i = 0; i < terminal.sbbuffer.length; i++)
terminal.scrollback.appendChild(terminal.sbbuffer[i]);
terminal.sbbuffer = [];
terminal.sbtimer = null;
terminal.tc.scrollTop = terminal.tc.scrollHeight+30;
},
newrow : function(oldline) {
var row = document.createElement("tr");
var m = [];
for(var c = 0; c < this.cols; c ++) {
var col = document.createElement("td");
col.innerHTML = " ";
if (oldline != undefined) {
m[c] = { e : col, char : " ", fg : oldline[c].fg, bg: oldline[c].bg};
} else {
m[c] = { e : col, char : " ", fg : this.fg, bg: this.bg };
}
this.setColor(m[c], m[c].fg, m[c].bg);
row.appendChild(col);
}
return { m: m, row: row };
},
scrollup : function() {
if (this.tmargin == 0 && this.enablescrollback) {
this.sbbuffer.push(this.row[0]);
if (this.sbtimer != null) clearTimeout(this.sbtimer);
this.sbtimer = setTimeout(this.sbupdate, 100);
}
this.window.removeChild(this.row[this.tmargin]);
var nr = this.newrow();
for(var r = this.tmargin; r < this.bmargin; r++) {
this.m[r] = this.m[r+1];
this.row[r] = this.row[r+1];
}
this.m[this.bmargin] = nr.m;
this.row[this.bmargin] = nr.row;
if (this.bmargin == this.rows-1) {
this.window.appendChild(nr.row);
this.tc.scrollTop = this.tc.scrollHeight+30;
} else
this.window.insertBefore(nr.row, this.row[this.bmargin+1]);
},
scrolldown : function() {
var nr = this.newrow();
var old = this.row[this.bmargin];
for(var r = this.bmargin; r > this.tmargin; r--) {
this.m[r] = this.m[r-1];
this.row[r] = this.row[r-1];
}
this.m[this.tmargin] = nr.m;
this.row[this.tmargin] = nr.row;
this.window.insertBefore(nr.row, this.row[this.tmargin+1]);
this.window.removeChild(old);
},
tab : function() {
this.cx = Math.min(this.cols-1, (8*Math.floor((this.cx+8)/8)));
},
cr : function() {
this.cx = 0;
},
down : function(lines, scroll) {
do {
if (this.cy == this.bmargin) {
if (scroll) this.scrollup();
} else this.cy++;
} while (--lines > 0);
},
up : function(lines, scroll) {
do {
if (this.cy == this.tmargin) {
if (scroll) this.scrolldown();
} else this.cy--;
} while (--lines > 0);
},
left : function(cols) {
this.cx = Math.max(0, this.cx-(cols?cols:1));
},
right : function(cols) {
this.cx = Math.min(this.cols-1, this.cx + (cols? cols : 1));
},
move : function(y, x) {
this.cy = Math.max(Math.min(this.rows-1,y-1), 0);
this.cx = Math.max(Math.min(this.cols-1,x-1), 0);
},
clrline : function(r, s, e) {
var m = this.m[r];
for(var c = s; c <= e; c++) {
m[c].char = " ";
m[c].e.innerHTML = " ";
this.setColor(m[c], this.fg, this.bg);
}
},
eid : function(n) {
switch(n) {
case 0: // erase cursor to eos
this.clrline(this.cy, this.cx, this.cols-1);
for(var r = this.cy+1; r < this.rows; r++)
this.clrline(r, 0, this.cols-1);
break;
case 1: // start of screen to cursor
for(var r = 0; r < this.cy; r++)
this.clrline(r, 0, this.cols-1);
this.clrline(this.cy, 0, this.cx);
break;
case 2: // clear screen
for(var r = 0; r < this.rows; r++)
this.clrline(r, 0, this.cols-1);
}
},
eil : function(n) {
switch(n) {
case 0: // erase cursor to eol
return this.clrline(this.cy, this.cx, this.cols-1);
case 1: // erase from bol to cursor
return this.clrline(this.cy, 0, this.cx);
case 2: // erase entire line
return this.clrline(this.cy, 0, this.cols-1);
}
},
setMargins : function(t, b) {
t = Math.min(Math.max(0, t-1), this.rows-2);
b = Math.min(Math.max(0, b-1), this.rows-1);
if (t == 0 && b == 0) b = this.rows-1;
if (t >= b) return;
this.tmargin = t;
this.bmargin = b;
this.move(this.homeRow, this.homeCol);
return;
},
setAttrs : function(pn) {
var v, l = Math.max(this.cqlen, 1);
if (this.cqlen == 0) pn[0] = 0;
while (l-- > 0) {
switch(v = pn[0]) {
case 0:
this.fg = 7; this.bg = 0;
this.bold = this.underline = this.blink = this.reverse = false;
break;
case 1:
this.bold = true;
break;
case 4:
this.underline = true;
break;
case 5:
this.blink = true;
break;
case 7:
this.reverse = true;
break;
case 8:
this.fg = this.bg;
break;
case 21:
this.bold = false;
break;
case 24:
this.underline = false;
break;
case 27:
this.reverse = false;
break;
case 28:
this.fg = 0;
break;
default:
if (v >= 30 && v <= 37) this.fg = v-30;
else if (v >= 40 && v <= 47) this.bg = v-40;
else if (v >= 90 && v <= 97) this.fg = (v-90)+8;
else if (v >= 100 && v <= 107) this.bg = (v-100)+8;
break;
}
pn.shift();
}
},
normal : function(ch) {
switch(ch) {
case '\033':
this.mode = 1;
return;
case '\007': return;
case '\b': return this.left(1);
case '\t': return this.tab();
case '\n':
case '\v':
case '\f': return this.down(1,true);
case '\r': return this.cr();
default:
if (ch < ' ' || ch == 127) return;
}
var m = this.m[this.cy][this.cx];
m.char = ch;
m.e.innerHTML = (ch == ' ')? " " : ch;
if (this.reverse) this.setColor(m,this.colorInverse(this.fg), this.colorInverse(this.bg));
else this.setColor(m, this.fg, this.bg);
m.e.style.textDecoration = this.underline? "underline" : "initial";
m.e.style.fontWeight = this.bold? "bold" : "normal";
this.cx++;
if (this.cx >= this.cols) {
this.cx = 0;
if (this.cy+1 >= this.rows) this.scrollup();
else this.cy++;
}
},
parsecq : function() {
if (this.cq.length == 0) return [ 0, 0 ];
var a = [0, 0], s = this.cq.split(";");
for(var i = 0; i < s.length; i++) {
a[i] = parseInt(s[i]);
}
this.cqlen = s.length;
this.cq = "";
return a;
},
cqreset : function() {
this.cqlen = 0;
this.cq = "";
},
setmode : function(pn) {
for(var i = 0; i < this.cqlen; i++) {
switch(pn[i]) {
case 1: // application cursor keys
// case 2: // keyboard lock mode
// case 4: // insert mode
// case 12: // disable local echo
// case 20: // LF,FF or VT moves to 1st column on next line (CR+LF)
}
}
},
resetmode : function(pn) {
},
qmode : function(ch) {
if (ch >= '0' && ch <='9' || ch == ';') this.cq += ch;
else {
this.mode = 0;
var pn = this.parsecq();
switch(ch) {
case 'l':
this.setmode(pn);
break;
case 'h':
this.resetmode(pn);
break;
}
}
},
brac: function(ch) {
if (ch >= '0' && ch <='9' || ch == ';') this.cq += ch;
else {
this.mode = 0;
var pn = this.parsecq();
switch(ch) {
case 'A': // up
return this.up(pn[0], false);
case 'B': // down
return this.down(pn[0], false);
case 'C': // right
return this.right(pn[0]);
case 'D': // left
return this.left(pn[0]);
case 'f':
case 'H':
return this.move(pn[0], pn[1]);
case 'J':
return this.eid(pn[0]);
case 'K':
return this.eil(pn[0]);
case 'r':
return this.setMargins(pn[0], pn[1]);
case 'm':
return this.setAttrs(pn);
case '?':
if (this.cqlen == 0) this.mode = 3;
return;
default:
}
}
},
escape : function(ch) {
this.mode = 0;
switch(ch) {
case '[': return this.cqreset(), this.mode = 2;
case '(': return this.cqreset(), this.mode = 3;
case ')': return this.cqreset(), this.mode = 4;
case 'c': return this.reset();
case 'D': return this.down(1,true);
case 'M': return this.up(1,true);
case 'E':
this.cr();
this.down(1, true);
return;
case '7':
this.sc = { cy : this.cy, cx : this.cx, cset : null };
return;
case '8':
this.cx = this.sc.cx; this.cy = this.sc.cy;
return;
case '=':
this.kbmode = true;
return;
case '>':
this.kbmode = false;
return;
default:
}
},
addch : function(ch) {
// console.log(String.prototype.charCodeAt(ch));
switch(this.mode) {
case 3: return this.qmode(ch);
case 2: return this.brac(ch);
case 1: return this.escape(ch);
case 0:
default:
this.normal(ch);
}
},
addstr : function(s) {
// console.log("]%s[", s);
this.eraseCursor();
for(var i = 0; i < s.length; i++) {
this.addch(s[i]);
}
this.drawCursor();
},
loadState: function(k) {
this.eraseCursor();
this.cx = k.cx; this.cy = k.cy; this.sc = k.sc;
this.mode = k.mode; this.cq = k.cq;
this.tmargin = k.tmargin; this.bmargin = k.bmargin;
this.fg = k.fg, this.bg = k.bg;
this.bold = k.bold; this.underline = k.underline;
this.blink = k.blink; this.reverse = k.reverse;
var nm = k.m;
for(var r = 0; r < this.rows; r++) {
var ch = nm[r].c;
var attr = nm[r].a;
this.row[r].innerHTML = "";
for(var c = 0; c < this.cols; c++) {
var m = this.m[r][c];
var td = (m.e = document.createElement("td"));
td.innerHTML = ((m.char = ch[c]) == ' ')? " " : ch[c];
this.setColor(m, Math.floor(attr[c] & this.FGMASK), Math.floor((attr[c] & this.BGMASK) >> 4));
if (attr[c] & this.BOLD) td.style.fontWeight = "bold";
if (attr[c] & this.UNDERLINE) td.style.textDecoration = "underline";
this.row[r].appendChild(td);
}
}
this.drawCursor();
},
reset : function() {
this.homeRow = this.homeCol = this.cx = this.cy = this.mode = 0;
this.sc = { cy : 0, cx: 0, charset: null };
this.tmargin = 0; this.bmargin = this.rows-1;
this.fg = 7; this.bg = 0;
this.bold = this.underline = this.blink = this.reverse = false;
this.m = [];
this.row = [];
this.window.innerHTML = "";
for(var r = 0; r < this.rows; r++) {
var nr = this.newrow();
this.m[r] = nr.m;
this.window.appendChild(this.row[r] = nr.row);
}
this.drawCursor();
},
init: function(w, h, scrollback) {
this.rows = h;
this.cols = w;
this.enablescrollback = scrollback;
this.window = document.getElementById("terminal");
this.scrollback = document.getElementById("scrollback");
this.tc = document.getElementById("tc");
this.reset();
tc.style.height = this.window.clientHeight+12 + "px";
if (scrollback) this.addstr("01234567890123456789012345678901234567890123456789012345678901234567890123456789");
},
colorInverse : function(color) {
if (color < 8) return 7-color;
return 15-color;
},
setColor : function(m, f, b) {
m.fg = f;
m.bg = b;
m.e.style.color = this.colors[f];
m.e.style.backgroundColor = this.colors[b];
},
eraseCursor : function() {
var m = this.m[this.cy][this.cx];
m.e.style.color = this.colors[m.fg];
m.e.style.backgroundColor = this.colors[m.bg];
},
drawCursor: function() {
var m = this.m[this.cy][this.cx];
m.e.style.color = this.colors[this.colorInverse(m.fg)];
m.e.style.backgroundColor = this.colors[this.colorInverse(m.bg)];
}
};
|