Run
Forrest
Trees
?
/**.:....1....:....2....:....3....:....4....:....5....:....6....:.
*
* JS-P1: Input/Output
*
* by John P. Spurgeon
* https://errless.blogspot.com/2018/06/js-p1.html
*
* revised on 16 June 2018
*/
// to Don Knuth
/**
* Table of Contents
*
* Preface
* Introduction
*
* Part 1. A Library
*
* 1.00. Lib
* 1.01. Timer
* 1.02. ArrayStringBuffer
* 1.03. StringStringBuffer
* 1.04. StringBuilder
* 1.05. PopupWindow
* 1.06. FunStats
* 1.07. ReadonlyFunStats
* 1.08. Reader
* 1.09. Writer
* 1.10. ReaderWriter
* 1.11. PageElement
* 1.12. Assemble the object.
*
* Part 2. A Webpage
*
* 2.00. Page
* 2.01. setInnerHTML
* 2.02. setHtml
* 2.03. setHead
* 2.04. setStyle
* 2.05. setBody
* 2.06. addClickEventListeners
*
* Part 3. Input/Output Objects
*
* 3.01. System.io
* 3.02. System.out
*
* PART 4. Page.io
*
*/
/**
* Preface
*
*/
/**
* Introduction
*
* This is a unique variation of the traditional first program
* to write when learning a new programming language, namely,
*
* Print the words
* hello, world
*
* See:
*
* 1. Brian W. Kernighan and Dennis M. Ritchie,
* _The C Programming Language_, 2nd edition (1988, 1978);
* Chapter 1, "A Tutorial Introduction";
* Section 1.1, "Getting Started," page 5.
*
* 2. https://en.wikipedia.org/wiki/%22Hello,_World!%22_program
*/
/**
* Genuine examples of large software programs are rarely
* found in books; yet students of computer science will get
* a warped perspective of the subject if they experience
* only "toy programs" of textbook proportions.
*
* -- DONALD E. KNUTH, TeX: The Program (1986)
*
* We all begin as close readers. Even before we learn to read,
* the process of being read aloud to, and of listening, is one
* in which we are taking in one word after another, one phrase
* at a time, in which we are paying attention to whatever each
* word or phrase is transmitting. Word by word is how we learn
* to hear and then read, which seems fitting, because it is how
* the books we are reading were written in the first place.
*
* -- FRANCINE PROSE, Reading Like a Writer (2006)
*/
/**
* PART 1. A Library
*
* 1.00. Lib
* 1.01. Timer
* 1.02. ArrayStringBuffer
* 1.03. StringStringBuffer
* 1.04. StringBuilder
* 1.05. PopupWindow
* 1.06. FunStats
* 1.07. ReadonlyFunStats
* 1.08. Reader
* 1.09. Writer
* 1.10. ReaderWriter
* 1.11. PageElement
* 1.12. Assemble the object.
*/
/**
* 1.00. Lib
*/
Lib = (function makeLib()
{
/** 1.01. Timer.
*
*/
function Timer(name)
{
const that = this;
let t1 = 0, t2 = 0;
that.getElapsedTime = function() {
return t2 - t1;
};
that.getUnits = function() {
return "ms";
}
that.start = function() {
t1 = getTimeInMs();
return that;
};
that.stop = function() {
t2 = getTimeInMs();
return that;
};
that.toString = function() {
let string = "snapshotOfThatTimer = {"
string += "\n name: \"" + name + '"';
string += ",\n elapsedTime: " + that.getElapsedTime();
string += ",\n units: \"" + that.getUnits() + '"';
string += ",\n t1: " + t1;
string += ",\n t2: " + t2;
string += "\n};";
return string;
};
function getTimeInMs() {
return performance.now();
}
}
/**
* 1.02. StringStringBuffer
*
*/
function StringStringBuffer()
{
const that = this;
let string = "";
that.appendString = function(s) { string += s; }
that.clearBuffer = function() { string = ""; }
that.toString = function() { return string; }
}
/**
* 1.03. ArrayStringBuffer
*
*/
function ArrayStringBuffer()
{
const that = this;
let array = [];
that.appendString = function(s) { array.push(s); };
that.clearBuffer = function() { array = []; };
that.toString = function() { return array.join(""); };
}
/**
* 1.04. StringBuilder
*
*/
function StringBuilder(buffer)
{
const that = this;
const sb = (buffer === undefined) ?
new ArrayStringBuffer : buffer;
that.append = function(object) {
return appendString(object.toString());
};
that.appendln = function(object) {
return appendString(object.toString() + "\n");
};
that.replace = function(object) {
return that.erase().append(object);
};
that.replaceln = function(object) {
return that.erase().appendln(object);
};
that.erase = function() {
sb.clearBuffer();
return that;
};
that.toString = function() {
return sb.toString();
};
function appendString(s) {
sb.appendString(s);
return that;
}
}
/**
* 1.05. PopupWindow
*
*/
function PopupWindow(method)
{
const that = this;
const contents = new StringBuilder();
let footnote = "";
let enabled = true;
if (method === undefined || method === null) {
method = confirm;
footnote = "\n(Cancel disables this window.)"
}
that.appendText = function(moreText) {
contents.append(moreText);
return that;
};
that.replaceText = function(newText) {
contents.replace(newText);
return that;
};
that.getText = function() {
return contents.toString();
};
that.showWindow = function() {
if (enabled) {
if (!method(contents.toString() + footnote)) {
enabled = false;
}
}
return that;
};
that.enable = function() {
enabled = true;
};
that.disable = function() {
enabled = false;
};
}
/**
* 1.06. FunStats
*
*/
function FunStats(kind, units)
{
const that = this;
const INDENT1 = " ";
const INDENT2 = INDENT1 + INDENT1;
const UNDEFINED = "undefined";
const values = [];
let sum, min, max;
that.add = function(value) {
values.push(value);
if (values.length === 1) {
sum = min = max = value;
}
else {
sum += value;
if (value < min) {
min = value;
}
else if (value > max) {
max = value;
}
}
};
that.count = function() { return values.length; };
that.sum = function() { return getValue(sum); };
that.min = function() { return getValue(min); };
that.max = function() { return getValue(max); };
that.avg = function() { return computeValue(getAvg); };
that.sd = function() { return computeValue(getSD); };
that.toString = function() {
const sb = new StringBuilder();
sb.appendln(INDENT1 + kind + ": " + that.count());
sb.appendln(getLabel("sum") + that.sum());
sb.appendln(getLabel("min") + that.min());
sb.appendln(getLabel("max") + that.max());
sb.appendln(getLabel("avg") + that.avg());
sb.appendln(getLabel("sd") + that.sd());
return sb.toString();
};
function getLabel(statName) {
return INDENT2 + statName + " (" + units + "): ";
}
function getValue(statVariable) {
return that.count() == 0 ? UNDEFINED : statVariable;
}
function computeValue(statFunction) {
return that.count() == 0 ? UNDEFINED : statFunction();
}
function getAvg() { return sum / values.length; }
function getSD() { return Math.sqrt(getVariance()); }
function getVariance() {
const n = values.length;
const avg = sum / n;
let sumOfSquares = 0;
for (let i = 0; i < n; i++) {
const diff = values[i] - avg;
sumOfSquares += diff * diff;
}
return sumOfSquares / n;
}
}
/** 1.07. ReadonlyFunStats.
*
*/
function ReadonlyFunStats(stats)
{
const that = this;
that.count = function() { return stats.count(); };
that.sum = function() { return stats.sum(); };
that.min = function() { return stats.min(); };
that.max = function() { return stats.max(); };
that.avg = function() { return stats.avg(); };
that.sd = function() { return stats.sd(); };
that.toString = function() { return stats.toString(); }
}
/** 1.08. Reader.
*
*/
function Reader(name, readMethod)
{
const that = this;
const timer = new Timer();
const stats = new FunStats("reads", timer.getUnits());
const readonlyStats = new ReadonlyFunStats(stats);
that.read = function(args) {
timer.start();
const contents = readMethod(args);
stats.add(timer.stop().getElapsedTime());
return contents;
};
that.getReaderFunStats = function() {
return readonlyStats;
};
that.toString = function() {
return name + "\n" + readonlyStats.toString();
};
}
/** 1.09. Writer.
*
*/
function Writer(name, appendMethod, replaceMethod)
{
const that = this;
const timer = new Timer();
const stats = new FunStats("writes", timer.getUnits());
const readonlyStats = new ReadonlyFunStats(stats);
that.append = function(object) {
return appendString(stringify(object));
};
that.appendln = function(object) {
return appendString(stringify(object) + "\n");
};
that.replace = function(object) {
return replaceString(stringify(object));
};
that.replaceln = function(object) {
return replaceString(stringify(object) + "\n");
};
that.erase = function(object) {
return eraseString();
};
that.getWriterFunStats = function() {
return readonlyStats;
};
that.toString = function() {
return name + "\n" + readonlyStats.toString();
};
function stringify(object) {
return object !== undefined && object !== null ?
object.toString() : "";
}
function appendString(notUnefinedNotNullString) {
timer.start();
appendMethod(notUnefinedNotNullString);
stats.add(timer.stop().getElapsedTime());
return that;
}
function replaceString(notUnefinedNotNullString) {
timer.start();
replaceMethod(notUnefinedNotNullString);
stats.add(timer.stop().getElapsedTime());
return that;
}
function eraseString() {
timer.start();
replaceMethod("");
stats.add(timer.stop().getElapsedTime());
return that;
}
}
/** 1.10. ReaderWriter.
*
*/
function ReaderWriter(
name,
readMethod,
appendMethod,
replaceMethod)
{
const that = this;
Reader.call(that, name, readMethod);
Writer.call(that, name, appendMethod, replaceMethod);
that.toString = function() {
sb = new StringBuilder();
sb.appendln(name);
sb.append(that.getReaderFunStats());
sb.append(that.getWriterFunStats());
return sb.toString();
};
}
/** 1.11. PageElement.
*
*/
function PageElement(element)
{
const that = this;
that.bindByTagName = function(name, i) {
if (i === undefined) i = 0;
element = document.getElementsByTagName(name)[i];
return this;
}
that.bindById = function(id) {
element = document.getElementById(id);
return this;
}
that.setText = function(contents) {
return set("innerText", contents);
};
that.setHtml = function(contents) {
return set("innerHTML", contents);
};
that.setValue = function(contents) {
return set("value", contents);
};
that.getText = function() {
return get("innerText");
};
that.getHtml = function() {
return get("innerHTML");
};
that.getValue = function() {
return get("value");
};
function set(propertyName, contents) {
if (element === null || element === undefined) {
return null;
}
element[propertyName] = contents.toString();
return element;
}
function get(propertyName) {
return (element != null && element != undefined) ?
element[propertyName] : null;
}
};
/** 1.12. Assemble the object.
*
* Now that all the components of the library are defined, we
* assemble them together in an object, freeze the object, and
* return it.
*
* See also: 1.00. Lib.
*/
return Object.freeze({
Timer: Timer,
ArrayStringBuffer: ArrayStringBuffer,
StringStringBuffer: StringStringBuffer,
StringBuilder: StringBuilder,
PopupWindow: PopupWindow,
FunStats: FunStats,
ReadonlyFunStats: ReadonlyFunStats,
Reader: Reader,
Writer: Writer,
ReaderWriter: ReaderWriter,
PageElement: PageElement,
});
})();
/*..:....1....:....2....:....3....:....4....:....5....:....6....:.
*
* PART 2. A Webpage
*
* 2.00. Page
* 2.01. setInnerHTML
* 2.02. setHtml
* 2.03. setHead
* 2.04. setStyle
* 2.05. setBody
* 2.06. addElementsToPage
* 2.07. addClickEventListeners
* 2.08. return page
*/
/**
* 2.00. Page
*
*/
Page = (function makePage() {
const helloButtonId = "hello";
const resetButtonId = "reset";
const dcodeButtonId = "dcode";
const statsButtonId = "stats";
const a51TextAreaId = "a51ta";
const bpxTextAreaId = "bpxta";
const page = {};
/** 2.01. setInnerHTML
*
*/
function setInnerHTML(tagName, html)
{
const e = (new Lib.PageElement()).bindByTagName(tagName);
return e.setHtml(html);
}
/** 2.02. setHtml
*
*/
(function setHtml()
{
const html = new Lib.StringBuilder();
html.appendln("<head>");
html.appendln("</head>");
html.appendln("<body>");
html.appendln("</body>");
return setInnerHTML("html", html);
})();
/** 2.03. setHead
*
*/
(function setHead()
{
const html = new Lib.StringBuilder();
html.appendln("<title>ErrLess JS-P1</title>");
html.appendln("<style>");
html.appendln("</style>");
return setInnerHTML("head", html);
})();
/** 2.04. setStyle
*
*/
(function setStyle()
{
const css = new Lib.StringBuilder();
css.appendln("div {");
css.appendln(" display: block;");
css.appendln("}");
css.appendln("h1 {");
css.appendln(" color: black;");
css.appendln("}");
css.appendln("h2 {");
css.appendln(" color: darkgray;");
css.appendln("}");
css.appendln("textarea {");
css.appendln(" width: 32em;");
//css.appendln(" height: 51em;");
css.appendln(" height: 20em;");
css.appendln("}");
css.appendln("button {");
css.appendln(" margin-top: 0.5em;");
css.appendln("}");
css.appendln("#" + resetButtonId + " {");
css.appendln(" background-color: tomato;");
css.appendln(" color: white;");
css.appendln("}");
css.appendln("#" + dcodeButtonId + " {");
css.appendln(" background-color: black;");
css.appendln(" color: white;");
css.appendln("}");
css.appendln("#" + helloButtonId + " {");
css.appendln(" background-color: green;");
css.appendln(" color: white;");
css.appendln("}");
css.appendln("#" + statsButtonId + " {");
css.appendln(" background-color: gold;");
css.appendln(" color: black;");
css.appendln("}");
return setInnerHTML("style", css);
})();
/** 2.05. setBody
*
*/
(function setBody()
{
const blog = "errless.blogspot.com";
const post = "/2018/06/js1-io.html";
const http = "https://" + blog + post;
const link = '<a href="' + http + '">source code</a>';
const html = new Lib.StringBuilder();
html.appendln("<center>");
html.appendln("<div>");
html.appendln("<h1>Input/Output</h1>");
html.append('<button id="' + helloButtonId + '">');
html.append('Page.io.broadcast("hello, world");');
html.appendln('</button>');
html.append('<button id="' + resetButtonId + '">');
html.append('Reset');
html.appendln('</button>');
html.appendln("</div>");
html.appendln("<div>");
html.appendln("<h2>Area 51</h2>");
html.append("<textarea id=" + '"');
html.append(a51TextAreaId + '"' + ">");
html.appendln("</textarea>");
html.appendln("</br>");
html.append('<button id="' + statsButtonId + '">');
html.append('Page.io.a51.replace(Page.io);');
html.appendln('</button>');
html.appendln("</div>");
html.appendln("<div>");
html.appendln("<h2>Bletchley Park</h2>");
html.append("<textarea id=" + '"');
html.append(bpxTextAreaId + '"' + ">");
html.appendln("</textarea>");
html.appendln("</br>");
html.append('<button id="' + dcodeButtonId + '">');
html.append("Page.io.popup.replace(Page.io.bpx.read());");
html.appendln('</button>');
html.appendln("</div>");
html.appendln("<p>" + link);
html.appendln("</center>");
return setInnerHTML("body", html);
})();
/** 2.06. addElementsToPage
*
*/
(function addElementsToPage()
{
page.helloButton = document.getElementById(helloButtonId);
page.statsButton = document.getElementById(statsButtonId);
page.dcodeButton = document.getElementById(dcodeButtonId);
page.resetButton = document.getElementById(resetButtonId);
page.a51TextArea = document.getElementById(a51TextAreaId);
page.bpxTextArea = document.getElementById(bpxTextAreaId);
})();
/** 2.07. addClickEventListeners
*
*/
(function addClickEventListeners()
{
page.helloButton.addEventListener("click",
function() { Page.io.broadcast("hello, world"); }
);
page.statsButton.addEventListener("click",
function() { Page.io.a51.replace(Page.io); }
);
page.dcodeButton.addEventListener("click",
function() {
Page.io.popup.replace(Page.io.bpx.read());
}
);
page.resetButton.addEventListener("click",
function() { Page.io.reset(); }
);
})();
/** 2.08. return page
*
*/
return page;
})();
/**.:....1....:....2....:....3....:....4....:....5....:....6....:.
*
* PART 3. System
*
*/
System = {};
/** 3.01. System.io
*
*/
System.io = (function makeSystemIO()
{
const asb = new Lib.ArrayStringBuffer();
const ssb = new Lib.StringStringBuffer()
const logA = new Lib.StringBuilder(asb);
const logS = new Lib.StringBuilder(ssb);
const popupWindow = new Lib.PopupWindow();
const that = Object.freeze(
{
console: new Lib.Writer("console",
console.log,
function() {
alert("Operation not supported.");
}
),
logA: new Lib.ReaderWriter("logA",
logA.toString,
logA.append,
logA.replace
),
logS: new Lib.ReaderWriter("logS",
logS.toString,
logS.append,
logS.replace
),
popup: new Lib.ReaderWriter("popup",
popupWindow.showWindow,
function(object) {
popupWindow.appendText(object).showWindow();
},
function(object) {
popupWindow.replaceText(object).showWindow();
}
),
prompt: new Lib.Reader("prompt",
prompt
),
reset: function() {
that.logA.replace("");
that.logS.replace("");
popupWindow.replaceText("").enable();
},
toString: function() {
const sb = new Lib.StringBuilder();
sb.appendln(that.popup);
sb.appendln(that.logA);
sb.appendln(that.logS);
sb.appendln(that.console);
sb.appendln(that.prompt);
return sb.toString();
},
});
return that;
})();
/** 3.02. System.out
*
*/
System.out = {
print: function(object) {
System.io.console.append(object);
return System.out;
},
println: function(object) {
System.io.console.appendln(object);
return System.out;
}
};
Object.freeze(System);
/**..:....1....:....2....:....3....:....4....:....5....:....6....:.
*
* PART 4. Page.io
*
*/
Page.io = (function makePageIO()
{
const encode = btoa;
const decode = atob;
const a51 = new Lib.PageElement(Page.a51TextArea);
const bpx = new Lib.PageElement(Page.bpxTextArea);
const that = Object.freeze(
{
popup: System.io.popup,
logA: System.io.logA,
logS: System.io.logS,
console: System.io.console,
prompt: System.io.prompt,
a51: new Lib.ReaderWriter("a51",
a51.getValue,
function(val) {
a51.setValue(a51.getValue() + val.toString());
},
a51.setValue
),
bpx: new Lib.ReaderWriter("bpx",
function() {
return decode(bpx.getValue());
},
function(val) {
const unencoded = decode(bpx.getValue()) + val.toString();
bpx.setValue(encode(unencoded));
},
function(val) { bpx.setValue(encode(val.toString())); }
),
broadcast: function(message) {
that.popup.appendln(message);
that.bpx.appendln(message);
that.a51.appendln(message);
that.logA.appendln(message);
that.logS.appendln(message);
that.console.append(message);
return that;
},
toString: function() {
const sb = new Lib.StringBuilder();
sb.appendln(that.a51);
sb.appendln(that.bpx);
sb.appendln(that.popup);
sb.appendln(that.logA);
sb.appendln(that.logS);
sb.appendln(that.console);
sb.appendln(that.prompt);
return sb.toString();
},
reset: function() {
that.a51.replaceln("TESTBEDS\n");
that.a51.appendln("Google Chrome: 67.0.3396.87 (64-bit)");
that.a51.appendln("OS: Windows");
that.a51.appendln("JavaScript: V8 6.7.288.46");
that.a51.appendln();
that.a51.appendln("Internet Explorer: 11.0.9600.19035CO");
that.a51.appendln();
that.bpx.replaceln("Station X");
that.bpx.appendln(decode("QWxhbiBUdXJpbmcgd2FzIGhlcmUu"));
System.io.reset();
return that;
}
});
return that;
})();
Object.freeze(Page).io.reset();