2019-01-11 04:19:00 +01:00
|
|
|
|
2019-01-11 16:46:33 +01:00
|
|
|
(function (){
|
2019-01-11 04:19:00 +01:00
|
|
|
const importCache = {};
|
|
|
|
|
|
|
|
function fetchImport(id) {
|
|
|
|
return new Promise((fulfil, reject) => {
|
|
|
|
curl([`https://bundle.run/${id}`]).then(module => {
|
|
|
|
importCache[id] = module;
|
|
|
|
fulfil(module);
|
|
|
|
}, err => {
|
|
|
|
console.error(err.stack);
|
|
|
|
reject(new Error(`Error loading ${id} from bundle.run`));
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function fetchImports(bundle, progressFunc) {
|
|
|
|
const missingImports = bundle.imports.filter(x => !importCache[x]);
|
|
|
|
let pendingImports = missingImports.length;
|
|
|
|
|
|
|
|
if (missingImports.length) {
|
|
|
|
let promise = Promise.all(
|
|
|
|
missingImports.map(id => fetchImport(id).then(() => {
|
|
|
|
pendingImports -= 1;
|
|
|
|
if (progressFunc) progressFunc(pendingImports);
|
|
|
|
}))
|
|
|
|
);
|
|
|
|
|
|
|
|
return promise
|
|
|
|
} else {
|
2019-01-11 16:46:33 +01:00
|
|
|
return Promise.resolve();
|
2019-01-11 04:19:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function handleMessage(ev) {
|
2019-01-11 16:46:33 +01:00
|
|
|
let { action, cmdId } = ev.data;
|
|
|
|
const sendMessage = (payload) => parent.postMessage( { ...payload }, ev.origin);
|
|
|
|
const sendReply = (payload) => sendMessage({ ...payload, cmdId })
|
|
|
|
const sendOk = () => sendReply({ action: "cmdOk" });
|
|
|
|
const sendError = (message, stack) => sendReply({ action: "cmdError", message, stack })
|
|
|
|
|
2019-02-03 01:51:19 +01:00
|
|
|
|
2019-01-11 16:46:33 +01:00
|
|
|
if (action == "eval") {
|
|
|
|
let { script } = ev.data.args;
|
2019-01-11 04:19:00 +01:00
|
|
|
try {
|
|
|
|
eval(script);
|
2019-01-11 16:46:33 +01:00
|
|
|
sendOk();
|
2019-01-11 04:19:00 +01:00
|
|
|
} catch (e) {
|
2019-02-03 01:51:19 +01:00
|
|
|
sendError(e.message, e.stack);
|
2019-01-11 04:19:00 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-11 16:46:33 +01:00
|
|
|
if (action == "bind_props") {
|
2019-01-11 04:19:00 +01:00
|
|
|
let { props } = ev.data.args
|
|
|
|
|
|
|
|
if (!window.component) {
|
|
|
|
// TODO can this happen?
|
2019-02-03 01:51:19 +01:00
|
|
|
console.warn('no component to bind to');
|
|
|
|
sendOk();
|
2019-01-11 04:19:00 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-02-03 01:51:19 +01:00
|
|
|
try {
|
|
|
|
props.forEach(prop => {
|
|
|
|
// TODO should there be a public API for binding?
|
|
|
|
// e.g. `component.$watch(prop, handler)`?
|
|
|
|
// (answer: probably)
|
|
|
|
window.component.$$.bound[prop] = value => {
|
|
|
|
sendMessage({ action:"prop_update", args: { prop, value } })
|
|
|
|
};
|
|
|
|
});
|
|
|
|
sendOk();
|
|
|
|
} catch (e) {
|
|
|
|
|
|
|
|
sendError(e.message, e.stack);
|
|
|
|
}
|
2019-01-11 04:19:00 +01:00
|
|
|
}
|
|
|
|
|
2019-01-11 16:46:33 +01:00
|
|
|
if (action == "set_prop") {
|
2019-02-03 01:51:19 +01:00
|
|
|
try {
|
|
|
|
if (!window.component) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
let { prop, value } = ev.data.args;
|
|
|
|
component[prop] = value;
|
|
|
|
sendOk();
|
|
|
|
} catch (e) {
|
|
|
|
sendError(e.message, e.stack);
|
2019-01-11 04:19:00 +01:00
|
|
|
}
|
|
|
|
}
|
2019-01-11 16:46:33 +01:00
|
|
|
|
|
|
|
if (action == "catch_clicks") {
|
2019-02-03 01:51:19 +01:00
|
|
|
try {
|
|
|
|
let topOrigin = ev.origin;
|
|
|
|
document.body.addEventListener('click', event => {
|
|
|
|
if (event.which !== 1) return;
|
|
|
|
if (event.metaKey || event.ctrlKey || event.shiftKey) return;
|
|
|
|
if (event.defaultPrevented) return;
|
|
|
|
|
|
|
|
// ensure target is a link
|
|
|
|
let el = event.target;
|
|
|
|
while (el && el.nodeName !== 'A') el = el.parentNode;
|
|
|
|
if (!el || el.nodeName !== 'A') return;
|
|
|
|
|
|
|
|
if (el.hasAttribute('download') || el.getAttribute('rel') === 'external' || el.target) return;
|
|
|
|
|
|
|
|
event.preventDefault();
|
|
|
|
|
|
|
|
if (el.href.startsWith(topOrigin)) {
|
|
|
|
const url = new URL(el.href);
|
|
|
|
if (url.hash[0] === '#') {
|
|
|
|
window.location.hash = url.hash;
|
|
|
|
return;
|
|
|
|
}
|
2019-01-11 16:46:33 +01:00
|
|
|
}
|
2019-02-03 01:51:19 +01:00
|
|
|
|
|
|
|
window.open(el.href, '_blank');
|
|
|
|
});
|
|
|
|
sendOk();
|
|
|
|
} catch(e) {
|
|
|
|
sendError(e.message, e.stack);
|
|
|
|
}
|
2019-01-11 16:46:33 +01:00
|
|
|
}
|
2019-01-11 04:19:00 +01:00
|
|
|
|
2019-01-11 16:46:33 +01:00
|
|
|
|
|
|
|
if (action == "fetch_imports") {
|
|
|
|
let { bundle } = ev.data.args;
|
2019-01-11 04:19:00 +01:00
|
|
|
fetchImports(bundle, (remaining) => {
|
2019-01-11 16:46:33 +01:00
|
|
|
sendMessage({action: "fetch_progress", args: { remaining }});
|
2019-01-11 04:19:00 +01:00
|
|
|
})
|
|
|
|
.then(() => {
|
|
|
|
bundle.imports.forEach(x=> {
|
|
|
|
const module = importCache[x];
|
|
|
|
const name = bundle.importMap.get(x);
|
|
|
|
window[name] = module;
|
|
|
|
});
|
2019-01-11 16:46:33 +01:00
|
|
|
sendOk();
|
2019-01-11 04:19:00 +01:00
|
|
|
})
|
|
|
|
.catch(e => {
|
2019-01-11 16:46:33 +01:00
|
|
|
sendError(e.message, e.stack);
|
2019-01-11 04:19:00 +01:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
window.addEventListener("message", handleMessage, false)
|
|
|
|
|
|
|
|
console.log("repl-runner initialized");
|
|
|
|
|
2019-01-11 16:46:33 +01:00
|
|
|
})();
|