SharedDocsLib/docslib/templates/loader.js
2026-08-10 17:28:30 +00:00

106 lines
3.7 KiB
JavaScript

(() => {
"use strict";
const embedded = JSON.parse(document.getElementById("docslib-manifest").textContent);
const config = JSON.parse(document.getElementById("docslib-config").textContent);
const loading = document.getElementById("docslib-loading");
const loaderStyle = document.getElementById("docslib-loader-style");
function isManifest(value) {
return Boolean(
value &&
value.schema_version >= 2 &&
Array.isArray(value.pages) &&
value.client &&
value.client.css &&
typeof value.client.css.system === "string" &&
value.client.js &&
typeof value.client.js.system === "string"
);
}
const dbPromise = new Promise((resolve, reject) => {
if (!window.indexedDB) return reject(new Error("IndexedDB"));
const request = indexedDB.open("docslib-cache", 1);
request.onupgradeneeded = () => {
if (!request.result.objectStoreNames.contains("manifests")) {
request.result.createObjectStore("manifests");
}
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
async function cacheGet() {
const db = await dbPromise;
return new Promise((resolve, reject) => {
const request = db.transaction("manifests", "readonly").objectStore("manifests").get(config.cacheKey);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async function cachePut(value) {
const db = await dbPromise;
return new Promise((resolve, reject) => {
const transaction = db.transaction("manifests", "readwrite");
transaction.objectStore("manifests").put(value, config.cacheKey);
transaction.oncomplete = resolve;
transaction.onerror = () => reject(transaction.error);
});
}
async function selectManifest() {
let selected = embedded;
try {
const cached = await cacheGet();
if (
isManifest(cached) &&
cached.schema_version === embedded.schema_version &&
String(cached.updated_at || "") >= String(embedded.updated_at || "")
) selected = cached;
else await cachePut(embedded);
} catch (_) { /* embedded manifest remains available */ }
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 4000);
try {
const response = await fetch(config.manifestUrl, { cache: "no-store", signal: controller.signal });
if (!response.ok) throw new Error(String(response.status));
const remote = await response.json();
if (!isManifest(remote) || remote.schema_version !== embedded.schema_version) {
throw new Error("manifest");
}
selected = remote;
await cachePut(remote).catch(() => {});
} catch (_) { /* cached or embedded manifest remains available */ }
finally { clearTimeout(timeout); }
return selected;
}
function applyRuntime(manifest) {
const style = document.createElement("style");
style.id = "docslib-runtime-style";
style.textContent = [manifest.client.css.system, manifest.client.css.custom || ""].join("\n");
document.head.append(style);
window.__DOCSLIB_MANIFEST__ = manifest;
window.__DOCSLIB_CONFIG__ = config;
const script = document.createElement("script");
script.id = "docslib-runtime-script";
script.textContent = `${manifest.client.js.system}\n//# sourceURL=docslib-client.js`;
document.body.append(script);
}
document.addEventListener("docslib:rendered", () => {
loading?.remove();
loaderStyle?.remove();
}, { once: true });
selectManifest()
.then((manifest) => {
if (!isManifest(manifest)) throw new Error("manifest");
applyRuntime(manifest);
})
.catch((error) => console.error("SharedDocsLib", error));
})();