mirror of
https://github.com/Tria-plc/emaui.git
synced 2026-08-27 09:42:55 +00:00
210 lines
6.7 KiB
JavaScript
210 lines
6.7 KiB
JavaScript
import { warn } from "./utils.js";
|
|
|
|
//#region src/dom.ts
|
|
async function safeWrapper(callback, disableWarn) {
|
|
try {
|
|
return await callback();
|
|
} catch (e) {
|
|
!disableWarn && warn(e);
|
|
return;
|
|
}
|
|
}
|
|
function isStaticResourcesEqual(url1, url2) {
|
|
const REG_EXP = /^(https?:)?\/\//i;
|
|
return url1.replace(REG_EXP, "").replace(/\/$/, "") === url2.replace(REG_EXP, "").replace(/\/$/, "");
|
|
}
|
|
function createScript(info) {
|
|
let script = null;
|
|
let needAttach = true;
|
|
let timeout = 2e4;
|
|
let timeoutId;
|
|
const scripts = document.getElementsByTagName("script");
|
|
for (let i = 0; i < scripts.length; i++) {
|
|
const s = scripts[i];
|
|
const scriptSrc = s.getAttribute("src");
|
|
if (scriptSrc && isStaticResourcesEqual(scriptSrc, info.url)) {
|
|
script = s;
|
|
needAttach = false;
|
|
break;
|
|
}
|
|
}
|
|
if (!script) {
|
|
const attrs = info.attrs;
|
|
script = document.createElement("script");
|
|
script.type = attrs?.["type"] === "module" ? "module" : "text/javascript";
|
|
let createScriptRes = void 0;
|
|
if (info.createScriptHook) {
|
|
createScriptRes = info.createScriptHook(info.url, info.attrs);
|
|
if (createScriptRes instanceof HTMLScriptElement) script = createScriptRes;
|
|
else if (typeof createScriptRes === "object") {
|
|
if ("script" in createScriptRes && createScriptRes.script) script = createScriptRes.script;
|
|
if ("timeout" in createScriptRes && createScriptRes.timeout) timeout = createScriptRes.timeout;
|
|
}
|
|
}
|
|
if (!script.src) script.src = info.url;
|
|
if (attrs && !createScriptRes) Object.keys(attrs).forEach((name) => {
|
|
if (script) {
|
|
if (name === "async" || name === "defer") script[name] = attrs[name];
|
|
else if (!script.getAttribute(name)) script.setAttribute(name, attrs[name]);
|
|
}
|
|
});
|
|
}
|
|
let executionError = null;
|
|
const executionErrorHandler = typeof window !== "undefined" ? (evt) => {
|
|
if (evt.filename && isStaticResourcesEqual(evt.filename, info.url)) {
|
|
const err = /* @__PURE__ */ new Error(`ScriptExecutionError: Script "${info.url}" loaded but threw a runtime error during execution: ${evt.message} (${evt.filename}:${evt.lineno}:${evt.colno})`);
|
|
err.name = "ScriptExecutionError";
|
|
executionError = err;
|
|
}
|
|
} : null;
|
|
if (executionErrorHandler) window.addEventListener("error", executionErrorHandler);
|
|
const onScriptComplete = async (prev, event) => {
|
|
clearTimeout(timeoutId);
|
|
if (executionErrorHandler) window.removeEventListener("error", executionErrorHandler);
|
|
const onScriptCompleteCallback = () => {
|
|
if (event?.type === "error") {
|
|
const networkError = /* @__PURE__ */ new Error(event?.isTimeout ? `ScriptNetworkError: Script "${info.url}" timed out.` : `ScriptNetworkError: Failed to load script "${info.url}" - the script URL is unreachable or the server returned an error (network failure, 404, CORS, etc.)`);
|
|
networkError.name = "ScriptNetworkError";
|
|
info?.onErrorCallback && info?.onErrorCallback(networkError);
|
|
} else if (executionError) info?.onErrorCallback && info?.onErrorCallback(executionError);
|
|
else info?.cb && info?.cb();
|
|
};
|
|
if (script) {
|
|
script.onerror = null;
|
|
script.onload = null;
|
|
safeWrapper(() => {
|
|
const { needDeleteScript = true } = info;
|
|
if (needDeleteScript) script?.parentNode && script.parentNode.removeChild(script);
|
|
});
|
|
if (prev && typeof prev === "function") {
|
|
const result = prev(event);
|
|
if (result instanceof Promise) {
|
|
const res = await result;
|
|
onScriptCompleteCallback();
|
|
return res;
|
|
}
|
|
onScriptCompleteCallback();
|
|
return result;
|
|
}
|
|
}
|
|
onScriptCompleteCallback();
|
|
};
|
|
script.onerror = onScriptComplete.bind(null, script.onerror);
|
|
script.onload = onScriptComplete.bind(null, script.onload);
|
|
timeoutId = setTimeout(() => {
|
|
onScriptComplete(null, {
|
|
type: "error",
|
|
isTimeout: true
|
|
});
|
|
}, timeout);
|
|
return {
|
|
script,
|
|
needAttach
|
|
};
|
|
}
|
|
function createLink(info) {
|
|
let link = null;
|
|
let needAttach = true;
|
|
let timeout = 2e4;
|
|
let timeoutId;
|
|
const links = document.getElementsByTagName("link");
|
|
for (let i = 0; i < links.length; i++) {
|
|
const l = links[i];
|
|
const linkHref = l.getAttribute("href");
|
|
const linkRel = l.getAttribute("rel");
|
|
if (linkHref && isStaticResourcesEqual(linkHref, info.url) && linkRel === info.attrs["rel"]) {
|
|
link = l;
|
|
needAttach = false;
|
|
break;
|
|
}
|
|
}
|
|
if (!link) {
|
|
link = document.createElement("link");
|
|
link.setAttribute("href", info.url);
|
|
let createLinkRes = void 0;
|
|
let shouldApplyAttrs = true;
|
|
const attrs = info.attrs;
|
|
if (info.createLinkHook) {
|
|
createLinkRes = info.createLinkHook(info.url, attrs);
|
|
if (createLinkRes instanceof HTMLLinkElement) {
|
|
link = createLinkRes;
|
|
shouldApplyAttrs = false;
|
|
} else if (typeof createLinkRes === "object") {
|
|
if ("link" in createLinkRes && createLinkRes.link) {
|
|
link = createLinkRes.link;
|
|
shouldApplyAttrs = false;
|
|
}
|
|
if ("timeout" in createLinkRes && createLinkRes.timeout) timeout = createLinkRes.timeout;
|
|
}
|
|
}
|
|
if (attrs && shouldApplyAttrs) Object.keys(attrs).forEach((name) => {
|
|
if (link && !link.getAttribute(name)) link.setAttribute(name, attrs[name]);
|
|
});
|
|
}
|
|
if (!needAttach) {
|
|
Promise.resolve().then(() => {
|
|
info?.cb && info?.cb();
|
|
});
|
|
return {
|
|
link,
|
|
needAttach
|
|
};
|
|
}
|
|
const onLinkComplete = (prev, event) => {
|
|
if (timeoutId) clearTimeout(timeoutId);
|
|
const onLinkCompleteCallback = () => {
|
|
if (event?.type === "error") {
|
|
const linkError = /* @__PURE__ */ new Error(event?.isTimeout ? `LinkNetworkError: Link "${info.url}" timed out.` : `LinkNetworkError: Failed to load link "${info.url}" - the URL is unreachable or the server returned an error.`);
|
|
linkError.name = "LinkNetworkError";
|
|
info?.onErrorCallback && info?.onErrorCallback(linkError);
|
|
} else info?.cb && info?.cb();
|
|
};
|
|
if (link) {
|
|
link.onerror = null;
|
|
link.onload = null;
|
|
safeWrapper(() => {
|
|
const { needDeleteLink = true } = info;
|
|
if (needDeleteLink) link?.parentNode && link.parentNode.removeChild(link);
|
|
});
|
|
if (prev) {
|
|
const res = prev(event);
|
|
onLinkCompleteCallback();
|
|
return res;
|
|
}
|
|
}
|
|
onLinkCompleteCallback();
|
|
};
|
|
link.onerror = onLinkComplete.bind(null, link.onerror);
|
|
link.onload = onLinkComplete.bind(null, link.onload);
|
|
timeoutId = setTimeout(() => {
|
|
onLinkComplete(null, {
|
|
type: "error",
|
|
isTimeout: true
|
|
});
|
|
}, timeout);
|
|
return {
|
|
link,
|
|
needAttach
|
|
};
|
|
}
|
|
function loadScript(url, info) {
|
|
const { attrs = {}, createScriptHook } = info;
|
|
return new Promise((resolve, reject) => {
|
|
const { script, needAttach } = createScript({
|
|
url,
|
|
cb: resolve,
|
|
onErrorCallback: reject,
|
|
attrs: {
|
|
fetchpriority: "high",
|
|
...attrs
|
|
},
|
|
createScriptHook,
|
|
needDeleteScript: true
|
|
});
|
|
needAttach && document.head.appendChild(script);
|
|
});
|
|
}
|
|
|
|
//#endregion
|
|
export { createLink, createScript, isStaticResourcesEqual, loadScript, safeWrapper };
|
|
//# sourceMappingURL=dom.js.map
|