apfl/webpage/playground/web_playground.js

168 lines
4.6 KiB
JavaScript
Raw Normal View History

window.apfl_playground = (() => {
let instances = new Map();
function playground_write(handle, error, s) {
let inst = instances.get(handle);
if (inst) {
inst.write(error, s);
}
}
window.playground_write = playground_write;
let repl_new_for_playground = undefined;
let repl_run = undefined;
let repl_destroy = undefined;
function Playground() {
let h = repl_new_for_playground();
if (!h) {
throw new Error("Could not init playground");
}
this.echoSource = true;
this._h = h;
this._onOutput = null;
this._onError = null;
this._lastResult = Playground.REPL_OK;
instances.set(h, this);
}
Playground.REPL_OK = 0;
Playground.REPL_MORE_INPUT = 1;
Playground.REPL_ERR = 2;
Playground.REPL_FATAL = 3;
Playground.prototype.destroy = function () {
repl_destroy(this._h);
instances.delete(this._h);
};
Playground.prototype.onOutput = function (f) {
this._onOutput = f;
};
Playground.prototype.onError = function (f) {
this._onError = f;
};
Playground.prototype.write = function (error, s) {
let writer = error ? this._onError : this._onOutput;
if (writer) {
writer(s);
}
}
Playground.prototype.getPrompt = function () {
return this._lastResult === Playground.REPL_MORE_INPUT ? "..." : ">";
};
Playground.prototype.runCode = function (source) {
source += "";
if (this.echoSource) {
this.write(false, `${this.getPrompt()} ${source}`);
}
return (this._lastResult = repl_run(this._h, source));
};
Playground.prototype.interactive = function (parent) {
const elem = (name, inner) => {
let out = document.createElement(name);
if (inner) {
inner(out);
}
return out;
};
let output;
let form;
let prompt;
let input;
parent.appendChild(elem("DIV", outer => {
outer.classList = "apfl_playground";
outer.appendChild(output = elem("PRE", output => {
output.classList = "apfl_playground_output";
}))
outer.appendChild(form = elem("FORM", form => {
form.appendChild(prompt = elem("SPAN", prompt => {
prompt.className = "apfl_playground_prompt";
prompt.innerText = this.getPrompt();
}));
form.appendChild(input = elem("INPUT", input => {
input.type = "text";
input.className = "apfl_playground_input";
input.placeholder = "code ...";
}));
}))
}));
form.onsubmit = (e) => {
e.preventDefault();
let code = input.value;
input.value = "";
if (!code) {
return;
}
this.runCode(code + "\n");
prompt.innerText = this.getPrompt();
};
const write = (error, s) => {
var span = elem("SPAN");
if (error) {
span.classList.add("error");
}
var lines = s.split("\n");
var scroll = false;
for (var i = 0 ; i < lines.length; i++) {
if (i > 0) {
span.appendChild(elem("BR"));
scroll = true;
}
var line = lines[i];
if (line != "") {
span.appendChild(document.createTextNode(lines[i]));
}
}
output.append(span);
if (scroll) {
output.scrollTop = output.scrollHeight - output.clientHeight;
}
};
this.onOutput(s => write(false, s));
this.onError(s => write(true, s));
};
let ok = false
let onModLoaded = [];
return async function () {
if (ok) {
return Playground;
}
let p = new Promise(resolve => {
onModLoaded.push(resolve);
});
WasmApflPlayground().then(instance => {
repl_new_for_playground = instance.cwrap("repl_new_for_playground", "number", []);
repl_run = instance.cwrap("repl_run", "number", ["number", "string"]);
repl_destroy = instance.cwrap("repl_destroy", "void", ["number"]);
ok = true;
onModLoaded.forEach(l => l(Playground));
});
return await p;
};
})();