2016-04-04 22:07:28 -06:00
|
|
|
// A class to turn an element into a cybersteampunk terminal.
|
|
|
|
// Runs at 1200 baud by default, but unlike an actual modem,
|
|
|
|
// will despool in parallel. This looks pretty cool.
|
|
|
|
|
2016-04-04 21:45:00 -06:00
|
|
|
// XXX: Hack for chrome not supporting an iterator method on HTMLCollection
|
|
|
|
HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
|
|
|
|
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
|
2016-04-03 18:07:38 -06:00
|
|
|
|
|
|
|
function Terminal(target, bps) {
|
|
|
|
bps = bps || 1200;
|
|
|
|
|
|
|
|
var outq = [];
|
|
|
|
var outTimer;
|
|
|
|
|
2016-04-04 22:07:28 -06:00
|
|
|
// Heavy lifting happens here.
|
|
|
|
// At first I had it auto-scrolling to the bottom, like xterm (1987).
|
|
|
|
// But that was actually kind of annoying, since this is meant to be read.
|
|
|
|
// So now it leaves the scrollbar in place, and the user has to scroll.
|
|
|
|
// This is how the Plan 9 terminal (1991) works.
|
|
|
|
function tx(pairs, bps, scroll) {
|
2016-04-04 21:45:00 -06:00
|
|
|
var drawTimer;
|
|
|
|
|
2016-04-04 22:07:28 -06:00
|
|
|
// Looks like EMCAScript 6 has a yield statement.
|
|
|
|
// That would make this mess a lot easier to understand.
|
2016-04-04 21:45:00 -06:00
|
|
|
|
2016-04-04 22:07:28 -06:00
|
|
|
var pairIndex = 0;
|
|
|
|
var pair = pairs[0];
|
2016-04-04 21:45:00 -06:00
|
|
|
|
|
|
|
var textIndex = 0;
|
|
|
|
var text = "";
|
|
|
|
|
|
|
|
function draw() {
|
2016-04-04 22:07:28 -06:00
|
|
|
var node = pair[0];
|
|
|
|
var src = pair[1];
|
2016-04-04 21:45:00 -06:00
|
|
|
var c = src[textIndex];
|
|
|
|
|
|
|
|
text += c;
|
|
|
|
node.textContent = text;
|
|
|
|
|
|
|
|
textIndex += 1;
|
|
|
|
if (textIndex == src.length) {
|
|
|
|
textIndex = 0;
|
|
|
|
text = "";
|
2016-04-03 18:07:38 -06:00
|
|
|
|
2016-04-04 22:07:28 -06:00
|
|
|
pairIndex += 1;
|
|
|
|
if (pairIndex == pairs.length) {
|
2016-04-04 21:45:00 -06:00
|
|
|
clearInterval(drawTimer);
|
|
|
|
return;
|
|
|
|
}
|
2016-04-04 22:07:28 -06:00
|
|
|
pair = pairs[pairIndex];
|
2016-04-04 21:45:00 -06:00
|
|
|
}
|
2016-04-03 18:07:38 -06:00
|
|
|
|
2016-04-04 21:45:00 -06:00
|
|
|
if (scroll) {
|
|
|
|
node.scrollIntoView();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// N81 uses 1 stop bit, and 1 parity bit.
|
|
|
|
// That works out to exactly 10 bits per byte.
|
|
|
|
msec = 10000 / bps;
|
|
|
|
|
|
|
|
drawTimer = setInterval(draw, msec);
|
|
|
|
draw();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function start() {
|
|
|
|
if (! outTimer) {
|
2016-04-04 22:07:28 -06:00
|
|
|
outTimer = setInterval(drawElement, 75);
|
2016-04-04 21:45:00 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function stop() {
|
|
|
|
if (outTimer) {
|
2016-04-03 18:07:38 -06:00
|
|
|
clearInterval(outTimer);
|
2016-04-04 21:45:00 -06:00
|
|
|
outTimer = null;
|
2016-04-03 18:07:38 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-04 21:45:00 -06:00
|
|
|
|
|
|
|
function drawElement() {
|
2016-04-04 22:07:28 -06:00
|
|
|
var pairs = outq.shift();
|
2016-04-04 21:45:00 -06:00
|
|
|
|
2016-04-04 22:07:28 -06:00
|
|
|
if (! pairs) {
|
2016-04-04 21:45:00 -06:00
|
|
|
stop();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-04-04 22:07:28 -06:00
|
|
|
tx(pairs, bps);
|
2016-04-04 21:45:00 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function prepare(element) {
|
2016-04-04 22:07:28 -06:00
|
|
|
var pairs = [];
|
2016-04-04 21:45:00 -06:00
|
|
|
|
|
|
|
walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);
|
|
|
|
while (walker.nextNode()) {
|
|
|
|
var node = walker.currentNode;
|
|
|
|
var text = node.textContent;
|
|
|
|
|
|
|
|
node.textContent = "";
|
2016-04-04 22:07:28 -06:00
|
|
|
pairs.push([node, text]);
|
2016-04-04 21:45:00 -06:00
|
|
|
}
|
|
|
|
|
2016-04-04 22:07:28 -06:00
|
|
|
return pairs;
|
2016-04-04 21:45:00 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// The main entry point: works like appendChild
|
|
|
|
this.append = function(element) {
|
2016-04-04 22:07:28 -06:00
|
|
|
pairs = prepare(element);
|
2016-04-04 21:45:00 -06:00
|
|
|
target.appendChild(element);
|
2016-04-04 22:07:28 -06:00
|
|
|
outq.push(pairs);
|
2016-04-04 21:45:00 -06:00
|
|
|
start();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// A cool effect where it despools children in parallel
|
|
|
|
this.appendShallow = function(element) {
|
|
|
|
for (var child of element.childNodes) {
|
2016-04-04 22:07:28 -06:00
|
|
|
pairs = prepare(child);
|
|
|
|
outq.push(pairs);
|
2016-04-04 21:45:00 -06:00
|
|
|
}
|
|
|
|
target.appendChild(element);
|
|
|
|
start();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-03 21:12:48 -06:00
|
|
|
this.clear = function() {
|
2016-04-04 21:45:00 -06:00
|
|
|
stop();
|
|
|
|
outq = [];
|
2016-04-03 21:12:48 -06:00
|
|
|
while (target.firstChild) {
|
|
|
|
target.removeChild(target.firstChild);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-03 18:07:38 -06:00
|
|
|
|
|
|
|
this.par = function(txt) {
|
2016-04-04 21:45:00 -06:00
|
|
|
var e = document.createElement("p");
|
|
|
|
e.textContent = txt;
|
|
|
|
this.append(e);
|
2016-04-03 18:07:38 -06:00
|
|
|
}
|
|
|
|
|
2016-04-04 21:45:00 -06:00
|
|
|
|
2016-04-03 18:07:38 -06:00
|
|
|
this.pre = function(txt) {
|
2016-04-04 21:45:00 -06:00
|
|
|
var e = document.createElement("pre");
|
|
|
|
e.textContent = txt;
|
|
|
|
this.append(e);
|
2016-04-03 18:07:38 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Usage:
|
|
|
|
//
|
|
|
|
// var e = Terminal(document.getElementById("output"));
|
|
|
|
// e.output("This is a paragraph. It has sentences.");
|
|
|
|
// e.output("This is a second paragraph.");
|
|
|
|
//
|
|
|
|
|