tildefriends/packages/cory/libiframe/libiframe.js

137 lines
3.3 KiB
JavaScript

"use strict";
//! {"category": "libraries"}
const kRpcSource = `
class RPC {
constructor() {
this._functions = [];
this.exports = null;
this.sendCallback = null;
}
export(functions) {
return this._send(["export", functions]);
}
_call(index) {
return this._send(["call", index, Array.prototype.slice.call(arguments, 1)]);
}
_send(message) {
var serialized = this._serialize(message);
if (this.sendCallback) {
return this.sendCallback(serialized);
}
return serialized;
}
deliver(message) {
var deserialized = this._deserialize(message);
if (deserialized[0] == "export") {
this.exports = deserialized[1];
} else if (deserialized[0] == "call") {
var f = this._functions[deserialized[1]];
f.apply(null, deserialized[2]);
}
}
_deserialize(data) {
var result = null;
if (data[0] == "array") {
result = new Array(data[1].length);
for (var i = 0; i < data[1].length; i++) {
result[i] = this._deserialize(data[1][i]);
}
} else if (data[0] == "object") {
result = {};
for (var i in data[1]) {
result[i] = this._deserialize(data[1][i]);
}
} else if (data[0] == "number" || data[0] == "boolean" || data[0] == "string") {
result = data[1];
} else if (data[0] == "function") {
result = this._call.bind(this, data[1]);
}
return result;
}
_serialize(data) {
var result = null;
if (Array.isArray(data)) {
result = ["array", data.map(x => this._serialize(x, this.functions))];
} else if (typeof data == "function") {
var index = this._functions.indexOf(data);
if (index == -1) {
index = this._functions.length;
this._functions.push(data);
}
result = ["function", index];
} else if (typeof data == "object") {
var fields = {};
while (data) {
var own = Object.getOwnPropertyNames(data);
for (var i = 0; i < own.length; i++) {
var name = own[i];
if (name != "constructor") {
var descriptor = Object.getOwnPropertyDescriptor(data, name);
if (descriptor&& typeof descriptor.value == "function") {
fields[name] = this._serialize(descriptor.value, this.functions);
}
}
}
if (data.__proto__ == Object.prototype) {
break;
}
data = Object.getPrototypeOf(data);
}
result = ["object", fields];
} else {
result = [typeof data, data];
}
return result;
}
}`
exports.iframe = function(options) {
return new Promise(function(resolve, reject) {
var iframeName = options.name || "iframe";
var RPC = eval(kRpcSource);
var rpc = new RPC();
rpc.sendCallback = terminal.postMessageToIframe.bind(null, iframeName);
core.register("onWindowMessage", function(message) {
if (message.message == "ready") {
resolve(rpc.exports);
} else {
rpc.deliver(message.message);
}
});
terminal.split([{name: "terminal", type: "vertical"}]);
terminal.print({
iframe: `
<!DOCTYPE html>
<body style="background-color: #fff"></body>
<script language="javascript">
${kRpcSource}
window.addEventListener("load", function() {
parent.postMessage("ready", "*");
});
let rpc = new RPC();
window.addEventListener("message", function(event) {
rpc.deliver(event.data);
});
rpc.sendCallback = function(data) {
parent.postMessage(data, "*");
}
${options.source}
</script>
`,
name: iframeName,
style: "flex: 1 1; " + options.style,
width: null,
height: null,
});
});
}