Files
Robert Rapp 1f354130bb Theme fest ins Repo uebernehmen statt als Submodule
Ein normaler git clone holt Submodules nicht mit - geprueft: nach dem
Klonen des Repos war themes/gokarna leer. Portainer klont ebenso, der
Build im Container waere also an dem fehlenden Theme gescheitert.

Gokarna liegt jetzt direkt im Repo, festgeschrieben auf Commit 8306042,
den Stand, mit dem die Seite bisher gebaut wurde. .gitmodules entfaellt.

Nachteil: Theme-Updates muessen kuenftig von Hand eingespielt werden.
Dafuer baut das Repo aus jedem beliebigen Klon heraus, ohne
Sonderbehandlung.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 01:25:39 +02:00

147 lines
5.2 KiB
JavaScript

document.addEventListener('DOMContentLoaded', ready, false);
const THEME_PREF_STORAGE_KEY = "theme-preference";
const THEME_TO_ICON_CLASS = {
'dark': 'feather-moon',
'light': 'feather-sun'
};
const THEME_TO_ICON_TEXT_CLASS = {
'dark': 'Dark mode',
'light': 'Light mode'
};
let toggleIcon = '';
let darkThemeCss = '';
const HEADING_TO_TOC_CLASS = {
'H1': 'level-1',
'H2': 'level-2',
'H3': 'level-3',
'H4': 'level-4'
}
function ready() {
feather.replace({ 'stroke-width': 1, width: 20, height: 20 });
setThemeByUserPref();
if (document.querySelector('main#content > .container') !== null &&
document.querySelector('main#content > .container').classList.contains('post')) {
if (document.getElementById('TableOfContents') !== null) {
fixTocItemsIndent();
createScrollSpy();
} else {
document.querySelector('main#content > .container.post').style.display = "block";
}
}
// Elements to inject
const svgsToInject = document.querySelectorAll('img.svg-inject');
// Do the injection
SVGInjector(svgsToInject);
const observer = new MutationObserver(() => {
normalizeSvgPaths();
});
observer.observe(document.body, { childList: true, subtree: true });
function normalizeSvgPaths() {
document.querySelectorAll('.nav-link a .svg-inject').forEach(path => {
const bbox = path.getBBox();
const scaleX = 20 / bbox.width;
const scaleY = 20 / bbox.height;
const scale = Math.min(scaleX, scaleY);
path.setAttribute('transform', `scale(${scale}) translate(${-bbox.x}, ${-bbox.y})`);
path.setAttribute('stroke', 'currentColor');
path.setAttribute('stroke-width', '1');
path.setAttribute('fill', 'transparent');
});
}
document.getElementById('hamburger-menu-toggle').addEventListener('click', () => {
const hamburgerMenu = document.getElementsByClassName('nav-hamburger-list')[0]
const hamburgerMenuToggleTarget = document.getElementById("hamburger-menu-toggle-target")
if (hamburgerMenu.classList.contains('visibility-hidden')) {
hamburgerMenu.classList.remove('visibility-hidden');
hamburgerMenuToggleTarget.setAttribute("aria-checked", "true");
} else {
hamburgerMenu.classList.add('visibility-hidden');
hamburgerMenuToggleTarget.setAttribute("aria-checked", "false");
}
})
}
window.addEventListener('scroll', () => {
if (window.innerWidth <= 820) {
// For smaller screen, show shadow earlier
toggleHeaderShadow(50);
} else {
toggleHeaderShadow(100);
}
});
function fixTocItemsIndent() {
document.querySelectorAll('#TableOfContents a').forEach($tocItem => {
const itemId = $tocItem.getAttribute("href").substring(1)
$tocItem.classList.add(HEADING_TO_TOC_CLASS[document.getElementById(itemId).tagName]);
});
}
function createScrollSpy() {
var elements = document.querySelectorAll('#toc a');
document.addEventListener('scroll', function() {
elements.forEach(function(element) {
const boundingRect = document.getElementById(element.getAttribute('href').substring(1)).getBoundingClientRect();
if (boundingRect.top <= 55 && boundingRect.bottom >= 0) {
elements.forEach(function(elem) {
elem.classList.remove('active');
});
element.classList.add('active');
}
});
});
}
function toggleHeaderShadow(scrollY) {
if (window.scrollY > scrollY) {
document.querySelectorAll('.header').forEach(function(item) {
item.classList.add('header-shadow')
})
} else {
document.querySelectorAll('.header').forEach(function(item) {
item.classList.remove('header-shadow')
})
}
}
function setThemeByUserPref() {
darkThemeCss = document.getElementById("dark-theme");
const savedTheme = localStorage.getItem(THEME_PREF_STORAGE_KEY) ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
const darkThemeToggles = document.querySelectorAll('.dark-theme-toggle');
setTheme(savedTheme, darkThemeToggles);
darkThemeToggles.forEach(el => el.addEventListener('click', toggleTheme, { capture: true }))
}
function toggleTheme(event) {
toggleIcon = event.currentTarget.querySelector("a svg.feather");
if (toggleIcon.classList[1] === THEME_TO_ICON_CLASS.dark) {
setThemeAndStore('light', [event.currentTarget]);
} else if (toggleIcon.classList[1] === THEME_TO_ICON_CLASS.light) {
setThemeAndStore('dark', [event.currentTarget]);
}
}
function setTheme(themeToSet, targets) {
darkThemeCss.disabled = themeToSet === 'light';
targets.forEach((target) => {
target.querySelector('a').innerHTML = feather.icons[THEME_TO_ICON_CLASS[themeToSet].split('-')[1]].toSvg();
target.querySelector(".dark-theme-toggle-screen-reader-target").textContent = [THEME_TO_ICON_TEXT_CLASS[themeToSet]];
});
}
function setThemeAndStore(themeToSet, targets) {
setTheme(themeToSet, targets);
localStorage.setItem(THEME_PREF_STORAGE_KEY, themeToSet);
}