refactor(a11y): simply code

This commit is contained in:
ZhenShuo Leo
2025-08-05 16:14:24 +08:00
parent f8d272d9ad
commit 10ffbca4c0
+156 -182
View File
@@ -1,194 +1,168 @@
const getA11ySettings = () => { window.A11yPanel = (() => {
const settings = localStorage.getItem("a11ySettings"); const FEATURES = {
return settings disableBlur: {
? JSON.parse(settings) default: false,
: { apply: (enabled) => {
disableBlur: false, document.querySelectorAll("script[data-target-id]").forEach((script) => {
disableImages: false, const targetId = script.getAttribute("data-target-id");
fontSize: "default", const scrollDivisor = Number(script.getAttribute("data-scroll-divisor") || 300);
underlineLinks: false, if (typeof setBackgroundBlur === "function") {
zenMode: false, setBackgroundBlur(targetId, scrollDivisor, enabled, targetId === "menu-blur");
}; }
}; });
},
},
const saveA11ySettings = (settings) => { disableImages: {
localStorage.setItem("a11ySettings", JSON.stringify(settings)); default: false,
}; apply: (enabled) => {
document.querySelectorAll("script[data-target-id]").forEach((script) => {
const image = document.getElementById(script.getAttribute("data-image-id"));
const imageUrl = script.getAttribute("data-image-url");
if (image) {
image.style.display = enabled ? "none" : "";
if (!enabled && imageUrl && !image.src) image.src = imageUrl;
}
});
},
},
const applyImageState = (imageElement, imageUrl, disableImages) => { fontSize: {
if (!imageElement) return; default: "default",
if (disableImages) { apply: (size) => {
imageElement.style.display = "none"; document.documentElement.style.fontSize = size === "default" ? "" : size;
} else { },
imageElement.style.display = ""; },
if (imageUrl && !imageElement.getAttribute("src")) {
imageElement.setAttribute("src", imageUrl); underlineLinks: {
default: false,
apply: (enabled) => {
const existing = document.getElementById("a11y-underline-links");
if (enabled && !existing) {
const style = document.createElement("style");
style.id = "a11y-underline-links";
style.textContent = "a { text-decoration: underline !important; }";
document.head.appendChild(style);
} else if (!enabled && existing) {
existing.remove();
}
},
},
zenMode: {
default: false,
apply: (enabled) => {
const isActive = document.body?.classList.contains("zen-mode-enable");
if (enabled !== isActive) {
const checkbox = document.querySelector('[id$="zen-mode"]');
if (checkbox && typeof _toggleZenMode === "function") {
_toggleZenMode(checkbox, { scrollToHeader: false });
}
}
},
},
};
let settings = null;
const getSettings = () => {
if (settings) return settings;
const defaults = Object.fromEntries(Object.entries(FEATURES).map(([key, config]) => [key, config.default]));
try {
const saved = localStorage.getItem("a11ySettings");
settings = { ...defaults, ...JSON.parse(saved || "{}") };
} catch {
settings = defaults;
} }
} return settings;
}; };
const applyFontSize = (fontSizePx) => { const updateSetting = (key, value) => {
const isDefaultSettings = localStorage.getItem("a11ySettings") === null; const current = getSettings();
if (!isDefaultSettings && fontSizePx !== "default") { current[key] = value;
document.documentElement.style.fontSize = fontSizePx; try {
} localStorage.setItem("a11ySettings", JSON.stringify(current));
}; } catch (e) {
console.warn(`a11y.js: can not store settings: ${e}`);
const applyUnderlineLinks = (enabled) => {
let styleElement = document.getElementById("a11y-underline-links");
if (enabled) {
if (!styleElement) {
styleElement = document.createElement("style");
styleElement.id = "a11y-underline-links";
styleElement.textContent = "a { text-decoration: underline !important; }";
document.head.appendChild(styleElement);
} }
} else { FEATURES[key]?.apply(value);
if (styleElement) { };
styleElement.remove();
}
}
};
const applyZenMode = (enabled) => { const initPanel = (panelId) => {
const body = document.querySelector("body"); const prefix = panelId.replace("a11y-panel", "");
const isZenModeActive = body && body.classList.contains("zen-mode-enable"); const current = getSettings();
// Toggle only if current state doesn't match desired state Object.entries(FEATURES).forEach(([key, config]) => {
if (enabled !== isZenModeActive) { const elementId = `${prefix}${key.replace(/([A-Z])/g, "-$1").toLowerCase()}`;
const zenModeCheckbox = document.querySelector('[id$="zen-mode"]'); const element = document.getElementById(elementId) || document.getElementById(`${elementId}-select`);
if (zenModeCheckbox && typeof _toggleZenMode === "function") {
_toggleZenMode(zenModeCheckbox, { scrollToHeader: false });
}
}
};
const applyA11ySettings = () => { if (element) {
const settings = getA11ySettings(); if (element.type === "checkbox") {
document.querySelectorAll("script[data-target-id]").forEach((script) => { element.checked = current[key];
const targetId = script.getAttribute("data-target-id"); element.onchange = (e) => updateSetting(key, e.target.checked);
const scrollDivisor = Number(script.getAttribute("data-scroll-divisor") || 300); } else if (element.tagName === "SELECT") {
const imageId = script.getAttribute("data-image-id"); element.value = current[key];
const imageUrl = script.getAttribute("data-image-url"); element.onchange = (e) => updateSetting(key, e.target.value);
const isMenuBlur = targetId === "menu-blur"; }
setBackgroundBlur(targetId, scrollDivisor, settings.disableBlur, isMenuBlur);
applyImageState(document.getElementById(imageId), imageUrl, settings.disableImages);
});
applyFontSize(settings.fontSize);
applyUnderlineLinks(settings.underlineLinks);
applyZenMode(settings.zenMode);
};
const updateA11ySetting = (key, value) => {
const settings = getA11ySettings();
settings[key] = value;
saveA11ySettings(settings);
applyA11ySettings();
};
const toggleA11yPanel = (prefix = "") => {
const panel = document.getElementById(`${prefix}a11y-panel`);
const overlay = document.getElementById(`${prefix}a11y-overlay`);
const button = document.getElementById(`${prefix}a11y-toggle`);
if (!panel || !overlay || !button) return;
if (overlay.classList.contains("hidden")) {
overlay.classList.remove("hidden");
panel.classList.remove("hidden");
button.setAttribute("aria-pressed", "true");
button.setAttribute("aria-expanded", "true");
} else {
overlay.classList.add("hidden");
panel.classList.add("hidden");
button.setAttribute("aria-pressed", "false");
button.setAttribute("aria-expanded", "false");
}
};
const initA11yPanel = (prefix = "") => {
const settings = getA11ySettings();
const checkboxBlur = document.getElementById(`${prefix}disable-blur`);
const checkboxImages = document.getElementById(`${prefix}disable-images`);
const checkboxUnderline = document.getElementById(`${prefix}underline-links`);
const checkboxZenMode = document.getElementById(`${prefix}zen-mode`);
const fontSizeSelect = document.getElementById(`${prefix}font-size-select`);
const toggleButton = document.getElementById(`${prefix}a11y-toggle`);
const closeButton = document.getElementById(`${prefix}a11y-close`);
const overlay = document.getElementById(`${prefix}a11y-overlay`);
if (
!checkboxBlur ||
!checkboxImages ||
!checkboxUnderline ||
!checkboxZenMode ||
!fontSizeSelect ||
!toggleButton ||
!closeButton ||
!overlay
) {
console.warn(`One or more a11y elements not found for prefix: ${prefix}`);
return;
}
checkboxBlur.checked = settings.disableBlur;
checkboxImages.checked = settings.disableImages;
checkboxUnderline.checked = settings.underlineLinks;
checkboxZenMode.checked = settings.zenMode;
fontSizeSelect.value = settings.fontSize;
checkboxBlur.addEventListener("change", (e) => updateA11ySetting("disableBlur", e.target.checked));
checkboxImages.addEventListener("change", (e) => updateA11ySetting("disableImages", e.target.checked));
checkboxUnderline.addEventListener("change", (e) => updateA11ySetting("underlineLinks", e.target.checked));
checkboxZenMode.addEventListener("change", (e) => {
// Only save setting, let applyZenMode handle the toggle logic
updateA11ySetting("zenMode", e.target.checked);
});
fontSizeSelect.addEventListener("change", (e) => {
// Remove fontSize from localStorage when default is selected
if (e.target.value === "default") {
const settings = getA11ySettings();
delete settings.fontSize;
saveA11ySettings(settings);
document.documentElement.style.fontSize = "";
} else {
updateA11ySetting("fontSize", e.target.value);
}
});
toggleButton.addEventListener("click", () => toggleA11yPanel(prefix));
closeButton.addEventListener("click", () => toggleA11yPanel(prefix));
overlay.addEventListener("click", (e) => {
if (e.target === overlay) {
toggleA11yPanel(prefix);
}
});
document.querySelectorAll(`.ios-toggle${prefix ? `[id^="${prefix}"]` : ""}`).forEach((toggle) => {
const checkbox = toggle.querySelector('input[type="checkbox"]');
if (!checkbox) return;
const newToggle = toggle.cloneNode(true);
toggle.parentNode.replaceChild(newToggle, toggle);
newToggle.addEventListener("click", () => {
const newCheckbox = newToggle.querySelector('input[type="checkbox"]');
if (newCheckbox) {
newCheckbox.checked = !newCheckbox.checked;
newCheckbox.dispatchEvent(new Event("change", { bubbles: true }));
} }
}); });
});
};
document.querySelectorAll("script[data-target-id]").forEach((script) => { const togglePanel = () => {
const imageId = script.getAttribute("data-image-id"); const panel = document.getElementById(panelId);
const imageUrl = script.getAttribute("data-image-url"); const overlay = document.getElementById(`${prefix}a11y-overlay`);
const settings = getA11ySettings(); const toggle = document.getElementById(`${prefix}a11y-toggle`);
applyImageState(document.getElementById(imageId), imageUrl, settings.disableImages);
});
document.addEventListener("DOMContentLoaded", () => { if (!panel || !overlay) return;
applyA11ySettings();
const allPanels = document.querySelectorAll('[id$="a11y-panel"]'); const isHidden = overlay.classList.contains("hidden");
allPanels.forEach((panel) => { overlay.classList.toggle("hidden");
const prefix = panel.id.replace("a11y-panel", ""); panel.classList.toggle("hidden");
initA11yPanel(prefix);
}); if (toggle) {
}); toggle.setAttribute("aria-pressed", String(isHidden));
toggle.setAttribute("aria-expanded", String(isHidden));
}
};
const toggle = document.getElementById(`${prefix}a11y-toggle`);
const close = document.getElementById(`${prefix}a11y-close`);
const overlay = document.getElementById(`${prefix}a11y-overlay`);
if (toggle) toggle.onclick = togglePanel;
if (close) close.onclick = togglePanel;
if (overlay) overlay.onclick = (e) => e.target === overlay && togglePanel();
};
const applyAll = () => {
const current = getSettings();
Object.entries(current).forEach(([key, value]) => {
FEATURES[key]?.apply(value);
});
};
const init = () => {
applyAll();
document.querySelectorAll('[id$="a11y-panel"]').forEach((panel) => {
initPanel(panel.id);
});
};
if (getSettings().disableImages) {
FEATURES.disableImages.apply(true);
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", init);
} else {
init();
}
return {
getSettings,
updateSetting,
addFeature: (name, config) => {
FEATURES[name] = config;
FEATURES[name].apply(getSettings()[name] || config.default);
},
};
})();