dom-inject.ts
100 lines
| 2.7 KiB
| video/mp2t
|
TypeScriptLexer
cin
|
r65 | 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?: HTMLElement; | ||||
injectAfter?: HTMLElement; | ||||
_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({ | ||||
error: e, | ||||
node | ||||
}); | ||||
cleanup(); | ||||
}); | ||||
mixin(node, attr); | ||||
const _injectionPoint = this.injectionPoint || document.head; | ||||
const _injectBefore = this.injectAfter ? this.injectAfter.nextSibling : null; | ||||
_injectionPoint.insertBefore(node, _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; | ||||
} | ||||
} | ||||
cin
|
r79 | } | ||
cin
|
r65 | |||
const instance = new DomInject(); | ||||
export default instance; | ||||