.replace(/>/g, ">");
}
function highlightHtml(s) {
+ // Input is always the output of makeHtml() — a bounded, machine-generated
+ // string, never free user input — so ReDoS is not a concern here.
let escaped = escapeHtml(s);
escaped = escaped.replace(
/("[^"]*")/g,
@@ -328,8 +293,9 @@
← Back to Home
diff --git a/index.html b/index.html
index b7d2ed9..59954af 100644
--- a/index.html
+++ b/index.html
@@ -25,44 +25,7 @@
-
+
diff --git a/theme.js b/theme.js
index cedabf8..f563519 100644
--- a/theme.js
+++ b/theme.js
@@ -1,5 +1,5 @@
// Simple theme initialization and toggle handling.
-// Sets html[data-theme] on load and wires #themeToggle click.
+// Sets html[data-theme] on load, injects #themeToggle if absent, and wires click.
(function(){
const html = document.documentElement;
@@ -29,13 +29,43 @@
return next;
}
+ // Inject the shared theme-toggle button into so pages don't each need
+ // to duplicate the markup. Any page that already contains #themeToggle keeps
+ // its own element unchanged. Called only from init(), which itself only runs
+ // once the DOM is ready, so document.body is always available at this point.
+ function injectThemeToggleButton(){
+ if (!document.body) return null;
+ const btn = document.createElement('button');
+ btn.className = 'theme-toggle';
+ btn.id = 'themeToggle';
+ btn.setAttribute('aria-label', 'Toggle theme');
+ btn.innerHTML =
+ '' +
+ '';
+ document.body.appendChild(btn);
+ return btn;
+ }
+
function init(){
const saved = getSavedTheme();
const theme = saved || detectSystem();
applyTheme(theme);
if (!saved) saveTheme(theme);
- const btn = document.getElementById('themeToggle');
+ let btn = document.getElementById('themeToggle');
+ if (!btn) btn = injectThemeToggleButton();
if (btn) btn.addEventListener('click', toggleTheme);
}