oqtane.framework/Oqtane.Server/wwwroot/Oqtane.Server.lib.module.js
2024-12-13 16:46:08 -05:00

172 lines
5.6 KiB
JavaScript

const pageScriptInfoBySrc = new Map();
function getKey(element) {
if (element.src !== "") {
return element.src;
} else {
return element.content;
}
}
function registerPageScriptElement(element) {
let key = getKey(element);
let pageScriptInfo = pageScriptInfoBySrc.get(key);
if (pageScriptInfo) {
pageScriptInfo.referenceCount++;
} else {
if (element.src.startsWith("./")) {
element.src = new URL(element.src.substr(2), document.baseURI).toString();
}
pageScriptInfo = { src: element.src, type: element.type, integrity: element.integrity, crossorigin: element.crossorigin, content: element.content, location: element.location, reload: element.reload, module: null, referenceCount: 1 };
pageScriptInfoBySrc.set(key, pageScriptInfo);
initializePageScript(pageScriptInfo);
}
}
function unregisterPageScriptElement(element) {
const pageScriptInfo = pageScriptInfoBySrc.get(getKey(element));
if (!pageScriptInfo) {
return;
}
pageScriptInfo.referenceCount--;
}
async function initializePageScript(pageScriptInfo) {
if (pageScriptInfo.reload) {
const module = await import(pageScriptInfo.src);
pageScriptInfo.module = module;
module.onLoad?.();
module.onUpdate?.();
} else {
try {
injectScript(pageScriptInfo);
} catch (error) {
console.error("Failed to load script: ${pageScriptInfo.src}", error);
}
}
}
function onEnhancedLoad() {
for (const [key, pageScriptInfo] of pageScriptInfoBySrc) {
if (pageScriptInfo.referenceCount <= 0) {
if (pageScriptInfo.module) {
pageScriptInfo.module.onDispose?.();
}
pageScriptInfoBySrc.delete(key);
}
}
for (const [key, pageScriptInfo] of pageScriptInfoBySrc) {
if (pageScriptInfo.module) {
pageScriptInfo.module.onUpdate?.();
} else {
try {
injectScript(pageScriptInfo);
} catch (error) {
if (pageScriptInfo.src !== "") {
console.error("Failed to load script library: ${pageScriptInfo.src}", error);
} else {
console.error("Failed to load inline script: ${pageScriptInfo.content}", error);
}
}
}
}
}
function injectScript(pageScriptInfo) {
return new Promise((resolve, reject) => {
var pageScript;
var script = document.createElement("script");
script.async = false;
if (pageScriptInfo.src !== "") {
pageScript = document.querySelector("page-script[src=\"" + pageScriptInfo.src + "\"]");
script.src = pageScriptInfo.src;
if (pageScriptInfo.type !== "") {
script.type = pageScriptInfo.type;
}
if (pageScriptInfo.integrity !== "") {
script.integrity = pageScriptInfo.integrity;
}
if (pageScriptInfo.crossorigin !== "") {
script.crossOrigin = pageScriptInfo.crossorigin;
}
} else {
pageScript = document.querySelector("page-script[content=\"" + CSS.escape(pageScriptInfo.content) + "\"]");
script.innerHTML = pageScriptInfo.content;
}
script.onload = () => resolve();
script.onerror = (error) => reject(error);
// add script to page
if (pageScriptInfo.location === "head") {
document.head.appendChild(script);
} else {
document.body.appendChild(script);
// note this throws an exception when page-script is on a page which has interactive components (ie Counter.razor)
// Error: Uncaught (in promise) TypeError: can't access property "attributes" of null
// this seems to be related to Blazor-Server-Component-State which is also injected at the end of the body
}
// remove page-script element from page
if (pageScript !== null) {
pageScript.remove();
}
});
}
export function afterWebStarted(blazor) {
customElements.define('page-script', class extends HTMLElement {
static observedAttributes = ['src', 'type', 'integrity', 'crossorigin', 'content', 'location', 'reload'];
constructor() {
super();
this.src = "";
this.type = "";
this.integrity = "";
this.crossorigin = "";
this.content = "";
this.location = "head";
this.reload = false;
}
attributeChangedCallback(name, oldValue, newValue) {
switch (name) {
case "src":
this.src = newValue;
break;
case "type":
this.type = newValue;
break;
case "integrity":
this.integrity = newValue;
break;
case "crossorigin":
this.crossorigin = newValue;
break;
case "content":
this.content = newValue;
break;
case "location":
this.location = newValue;
break;
case "reload":
this.reload = newValue;
break;
}
}
connectedCallback() {
registerPageScriptElement(this);
}
disconnectedCallback() {
unregisterPageScriptElement(this);
}
});
blazor.addEventListener('enhancedload', onEnhancedLoad);
}