dom-inject.ts
94 lines
| 2.5 KiB
| video/mp2t
|
TypeScriptLexer
cin
|
r6 | 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); | |||
} | |||
} | |||
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); | |||
} | |||
} | |||
}; | |||
const instance = new DomInject(); | |||
export = instance; |