moth/www/res/terminal.js

159 lines
2.8 KiB
JavaScript

// 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];
function Terminal(target, bps) {
bps = bps || 1200;
var outq = [];
var outTimer;
function tx(nodes, bps, scroll) {
var drawTimer;
// Looks like EMCAScript 6 has a yield statement. That'll be nice.
//
// for (var node of nodes) {
// var text = "";
// for (var c of node._text) {
// text += c;
// node.textContent = text;
// }
// }
var nodeIndex = 0;
var node = nodes[0];
var textIndex = 0;
var text = "";
function draw() {
var src = node._text;
var c = src[textIndex];
text += c;
node.textContent = text;
textIndex += 1;
if (textIndex == src.length) {
textIndex = 0;
text = "";
nodeIndex += 1;
if (nodeIndex == nodes.length) {
clearInterval(drawTimer);
return;
}
node = nodes[nodeIndex];
}
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) {
outTimer = setInterval(drawElement, 150);
}
}
function stop() {
if (outTimer) {
clearInterval(outTimer);
outTimer = null;
}
}
function drawElement() {
var element = outq.shift();
console.log(element);
if (! element) {
stop();
return;
}
tx(element._terminalNodes, bps);
}
function prepare(element) {
var nodes = [];
walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);
while (walker.nextNode()) {
var node = walker.currentNode;
var text = node.textContent;
node.textContent = "";
nodes.push(node);
}
element._terminalNodes = nodes;
}
// The main entry point: works like appendChild
this.append = function(element) {
prepare(element);
target.appendChild(element);
outq.push(element);
start();
}
// A cool effect where it despools children in parallel
this.appendShallow = function(element) {
for (var child of element.childNodes) {
prepare(child);
outq.push(child);
}
target.appendChild(element);
start();
}
this.clear = function() {
stop();
outq = [];
while (target.firstChild) {
target.removeChild(target.firstChild);
}
}
this.par = function(txt) {
var e = document.createElement("p");
e.textContent = txt;
this.append(e);
}
this.pre = function(txt) {
var e = document.createElement("pre");
e.textContent = txt;
this.append(e);
}
}
//
// Usage:
//
// var e = Terminal(document.getElementById("output"));
// e.output("This is a paragraph. It has sentences.");
// e.output("This is a second paragraph.");
//