|
|
import { id as mid } from "module";
|
|
|
import { TraceSource } from "@implab/core-amd/log/TraceSource";
|
|
|
import { MapOf } from "@implab/core-amd/interfaces";
|
|
|
import { mixin } from "@implab/core-amd/safe";
|
|
|
|
|
|
const trace = TraceSource.get(mid);
|
|
|
|
|
|
|
|
|
function on<T extends keyof HTMLElementEventMap>(node: HTMLElement, eventName: T, handler: (this: HTMLElement, ev: HTMLElementEventMap[T]) => any): () => void {
|
|
|
// Add an event listener to a DOM node
|
|
|
node.addEventListener(eventName, handler, false);
|
|
|
|
|
|
return () => node.removeEventListener(eventName, handler, false);
|
|
|
}
|
|
|
|
|
|
interface NodeLoadResult {
|
|
|
node: HTMLElement;
|
|
|
}
|
|
|
|
|
|
class DomInject {
|
|
|
injectionPoint = document.head;
|
|
|
injectBefore = document.head.firstChild;
|
|
|
|
|
|
_map: MapOf<Promise<NodeLoadResult>> = {};
|
|
|
|
|
|
_inject<T extends keyof HTMLElementTagNameMap>(name: T, attr: Partial<HTMLElementTagNameMap[T]>) {
|
|
|
const node = document.createElement(name);
|
|
|
|
|
|
return new Promise<NodeLoadResult>((ok, fail) => {
|
|
|
|
|
|
const cleanup = () => {
|
|
|
noerr();
|
|
|
noload();
|
|
|
};
|
|
|
|
|
|
const noload = on(node, "load", () => {
|
|
|
ok({ node });
|
|
|
cleanup();
|
|
|
});
|
|
|
|
|
|
const noerr = on(node, "error", e => {
|
|
|
fail({
|
|
|
erorr: e,
|
|
|
node
|
|
|
});
|
|
|
cleanup();
|
|
|
});
|
|
|
|
|
|
mixin(node, attr);
|
|
|
|
|
|
this.injectionPoint.insertBefore(node, this.injectBefore);
|
|
|
});
|
|
|
}
|
|
|
|
|
|
async injectScript(url: string) {
|
|
|
let d = this._map[url];
|
|
|
if (!d) {
|
|
|
trace.log("js {0}", url);
|
|
|
d = this._inject("script", {
|
|
|
type: "text/javascript",
|
|
|
charset: "utf-8",
|
|
|
src: url
|
|
|
});
|
|
|
this._map[url] = d;
|
|
|
}
|
|
|
try {
|
|
|
await d;
|
|
|
trace.log("done {0}", url);
|
|
|
} catch (e) {
|
|
|
trace.error("failed {0}: {1}", url, e);
|
|
|
throw e;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
async injectStylesheet(url: string) {
|
|
|
let d = this._map[url];
|
|
|
if (!d) {
|
|
|
trace.log("js {0}", url);
|
|
|
d = this._inject("link", {
|
|
|
type: "text/css",
|
|
|
rel: "stylesheet",
|
|
|
href: url
|
|
|
});
|
|
|
this._map[url] = d;
|
|
|
}
|
|
|
try {
|
|
|
await d;
|
|
|
trace.log("done {0}", url);
|
|
|
} catch (e) {
|
|
|
trace.error("failed {0}: {1}", url, e);
|
|
|
throw e;
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
|
|
|
const instance = new DomInject();
|
|
|
export = instance;
|