mirror of
https://github.com/BetterSEQTA/BetterSEQTA-Plus.git
synced 2026-08-28 09:11:06 +00:00
3bac45032d
Co-authored-by: Cursor <[email protected]>
171 lines
4.8 KiB
JavaScript
171 lines
4.8 KiB
JavaScript
/**
|
|
* PAGE context only — theme CustomCSS and decorative image variables must live
|
|
* in page-origin stylesheets. Extension-injected #custom-theme cannot load
|
|
* page blob: URLs referenced via var() on Firefox.
|
|
*/
|
|
(function () {
|
|
if (window.__bsplusThemePagePatched) return;
|
|
window.__bsplusThemePagePatched = true;
|
|
|
|
var LOG = "[BetterSEQTA+] theme page:";
|
|
var BRIDGE_ID = "bsplus-theme-image-bridge";
|
|
var PAYLOAD_ID = "bsplus-theme-image-payload";
|
|
var IMAGES_STYLE_ID = "bsplus-theme-images";
|
|
var THEME_STYLE_ID = "custom-theme";
|
|
var PREVIEW_STYLE_ID = "custom-theme-preview";
|
|
var urlCache = {};
|
|
var headObserver = null;
|
|
|
|
function log(event, detail) {
|
|
if (!document.documentElement.hasAttribute("data-bsplus-verbose-log")) return;
|
|
console.info(LOG, event, detail);
|
|
}
|
|
|
|
function base64ToBlob(base64, mime) {
|
|
var bytes = atob(base64);
|
|
var ab = new ArrayBuffer(bytes.length);
|
|
var view = new Uint8Array(ab);
|
|
for (var i = 0; i < bytes.length; i++) view[i] = bytes.charCodeAt(i);
|
|
return new Blob([ab], { type: mime || "image/png" });
|
|
}
|
|
|
|
function releaseCachedUrls() {
|
|
for (var key in urlCache) {
|
|
if (!Object.prototype.hasOwnProperty.call(urlCache, key)) continue;
|
|
try {
|
|
URL.revokeObjectURL(urlCache[key]);
|
|
} catch (e) {}
|
|
}
|
|
urlCache = {};
|
|
}
|
|
|
|
function styleEl(id) {
|
|
var el = document.getElementById(id);
|
|
if (!el) {
|
|
el = document.createElement("style");
|
|
el.id = id;
|
|
document.head.appendChild(el);
|
|
}
|
|
return el;
|
|
}
|
|
|
|
function keepThemeStyleLast() {
|
|
var themeStyle = document.getElementById(THEME_STYLE_ID);
|
|
if (themeStyle && document.head.contains(themeStyle) && document.head.lastElementChild !== themeStyle) {
|
|
document.head.appendChild(themeStyle);
|
|
}
|
|
}
|
|
|
|
function watchHead() {
|
|
if (headObserver) return;
|
|
headObserver = new MutationObserver(keepThemeStyleLast);
|
|
headObserver.observe(document.head, { childList: true });
|
|
}
|
|
|
|
function setStyleText(id, text, watchThemeOrder) {
|
|
if (!text) {
|
|
document.getElementById(id)?.remove();
|
|
return;
|
|
}
|
|
styleEl(id).textContent = text;
|
|
if (watchThemeOrder) {
|
|
watchHead();
|
|
keepThemeStyleLast();
|
|
}
|
|
}
|
|
|
|
function clearAll() {
|
|
releaseCachedUrls();
|
|
setStyleText(IMAGES_STYLE_ID, "");
|
|
document.getElementById(THEME_STYLE_ID)?.remove();
|
|
document.getElementById(PREVIEW_STYLE_ID)?.remove();
|
|
headObserver?.disconnect();
|
|
headObserver = null;
|
|
log("cleared");
|
|
}
|
|
|
|
function applyThemeImages(images) {
|
|
releaseCachedUrls();
|
|
if (!images?.length) {
|
|
setStyleText(IMAGES_STYLE_ID, "");
|
|
return;
|
|
}
|
|
|
|
var lines = [":root {"];
|
|
for (var i = 0; i < images.length; i++) {
|
|
var img = images[i];
|
|
if (!img?.variableName || !img.data) continue;
|
|
try {
|
|
var url = URL.createObjectURL(base64ToBlob(img.data, img.mime));
|
|
urlCache[img.variableName] = url;
|
|
lines.push(' --' + img.variableName + ': url("' + url + '");');
|
|
} catch (e) {
|
|
console.warn(LOG, "skip image", img.variableName, e);
|
|
}
|
|
}
|
|
lines.push("}");
|
|
styleEl(IMAGES_STYLE_ID).textContent = lines.join("\n");
|
|
log("images applied", { count: images.length });
|
|
}
|
|
|
|
function processPayload() {
|
|
var raw = document.getElementById(PAYLOAD_ID)?.value;
|
|
if (!raw) {
|
|
clearAll();
|
|
return;
|
|
}
|
|
|
|
try {
|
|
var payload = JSON.parse(raw);
|
|
if (!payload || payload.clear) {
|
|
clearAll();
|
|
return;
|
|
}
|
|
|
|
if (payload.images !== undefined) applyThemeImages(payload.images);
|
|
|
|
if (payload.customCss !== undefined) {
|
|
setStyleText(THEME_STYLE_ID, payload.customCss || "", true);
|
|
log("custom css applied");
|
|
}
|
|
|
|
if (payload.previewCss !== undefined) {
|
|
setStyleText(PREVIEW_STYLE_ID, payload.previewCss || "");
|
|
}
|
|
|
|
if (payload.clearPreview) {
|
|
setStyleText(PREVIEW_STYLE_ID, "");
|
|
}
|
|
} catch (e) {
|
|
console.warn(LOG, "invalid payload", e);
|
|
}
|
|
}
|
|
|
|
function ensureBridge() {
|
|
if (!document.getElementById(PAYLOAD_ID)) {
|
|
var payload = document.createElement("textarea");
|
|
payload.id = PAYLOAD_ID;
|
|
payload.hidden = true;
|
|
payload.setAttribute("aria-hidden", "true");
|
|
payload.tabIndex = -1;
|
|
document.documentElement.appendChild(payload);
|
|
}
|
|
if (!document.getElementById(BRIDGE_ID)) {
|
|
var bridge = document.createElement("div");
|
|
bridge.id = BRIDGE_ID;
|
|
bridge.hidden = true;
|
|
bridge.setAttribute("aria-hidden", "true");
|
|
document.documentElement.appendChild(bridge);
|
|
}
|
|
}
|
|
|
|
ensureBridge();
|
|
new MutationObserver(processPayload).observe(document.getElementById(BRIDGE_ID), {
|
|
attributes: true,
|
|
attributeFilter: ["data-rev"],
|
|
});
|
|
|
|
processPayload();
|
|
log("patch active");
|
|
})();
|