const themeSwitcher = {
_scheme: "auto",
toggleButton: document.querySelectorAll(".theme-toggle"),
rootAttribute: "data-theme",
localStorageKey: "picoPreferredColorScheme",
init() {
this.scheme = this.schemeFromLocalStorage;
this.initToggle();
this.updateIcon();
},
get schemeFromLocalStorage() {
return window.localStorage?.getItem(this.localStorageKey);
},
get preferredColorScheme() {
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
},
initToggle() {
this.toggleButton.forEach((button) => {
button.addEventListener(
"click",
(event) => {
event.preventDefault();
this.scheme = this.scheme === "dark" ? "light" : "dark";
this.updateIcon();
},
false
);
});
},
set scheme(scheme) {
if (scheme == "auto") {
this._scheme = this.preferredColorScheme;
} else if (scheme == "dark" || scheme == "light") {
this._scheme = scheme;
}
this.applyScheme();
this.schemeToLocalStorage();
},
get scheme() {
return this._scheme;
},
applyScheme() {
document.querySelector("html")?.setAttribute(this.rootAttribute, this.scheme);
},
schemeToLocalStorage() {
window.localStorage?.setItem(this.localStorageKey, this.scheme);
},
updateIcon() {
this.toggleButton.forEach((button) => {
if (this.scheme === "dark") {
button.innerHTML = "☼"; button.title = "light mode";
} else {
button.innerHTML = "☽"; button.title = "dark mode";
}
});
},
};
themeSwitcher.init();
function colorschemeSwitcher() {
const colorschemes = [
'catppuccin',
'clean',
'dracula',
'github',
'gruvbox',
'iceberg',
'minimal',
'minimal_wb',
'monokai',
'nord',
'one',
'solarized',
'typewriter'
];
const colorschemeDropdown = document.querySelectorAll('.colorscheme-toggle');
colorschemeDropdown.forEach((dropdown) => {
dropdown.addEventListener('change', function () {
const colorscheme = this.value;
const colorschemeLink = document.querySelector('#colorscheme-link');
if (colorscheme === 'default') {
if (colorschemeLink) {
colorschemeLink.remove();
}
localStorage.removeItem('marmitePreferredColorScheme');
return;
}
if (colorschemeLink) {
colorschemeLink.href = `static/colorschemes/${colorscheme}.css`;
} else {
const link = document.createElement('link');
link.id = 'colorscheme-link';
link.rel = 'stylesheet';
link.href = `static/colorschemes/${colorscheme}.css`;
document.head.appendChild(link);
}
localStorage.setItem('marmitePreferredColorScheme', colorscheme);
colorschemeDropdown.forEach((dropdown) => {
dropdown.value = colorscheme;
});
});
colorschemes.forEach((colorscheme) => {
const option = document.createElement('option');
option.value = colorscheme;
option.textContent = colorscheme;
dropdown.appendChild(option);
});
const colorscheme = localStorage.getItem('marmitePreferredColorScheme');
if (colorscheme) {
dropdown.value = colorscheme;
dropdown.dispatchEvent(new Event('change'));
}
});
}
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
themeSwitcher.scheme = e.matches ? 'dark' : 'light';
});
document.addEventListener('DOMContentLoaded', function() {
initializeSearch();
initializeMobileMenu();
initializeSmoothScroll();
initializeExternalLinks();
});
function initializeSearch() {
const searchToggle = document.getElementById('search-toggle');
const searchOverlay = document.getElementById('search-overlay');
const searchClose = document.getElementById('search-close');
if (!searchToggle || !searchOverlay) {
return; }
searchToggle.addEventListener('click', function(e) {
e.preventDefault();
searchOverlay.style.display = 'flex';
const searchInput = document.getElementById('search-input');
if (searchInput) searchInput.focus();
});
function hideSearch() {
searchOverlay.style.display = 'none';
const searchInput = document.getElementById('search-input');
if (searchInput) searchInput.value = '';
const searchResults = document.getElementById('search-results');
if (searchResults) searchResults.innerHTML = '';
}
if (searchClose) {
searchClose.addEventListener('click', hideSearch);
}
searchOverlay.addEventListener('click', function(e) {
if (e.target === searchOverlay) {
hideSearch();
}
});
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && searchOverlay.style.display === 'flex') {
hideSearch();
}
});
document.addEventListener('keydown', function(e) {
if ((e.ctrlKey || e.metaKey) && e.shiftKey && e.key === 'F') {
e.preventDefault();
searchOverlay.style.display = 'flex';
const searchInput = document.getElementById('search-input');
if (searchInput) searchInput.focus();
}
});
}
function initializeMobileMenu() {
const menuToggle = document.getElementById('menu-toggle');
const menu = document.querySelector('.site-nav ul');
if (!menuToggle || !menu) {
return;
}
menuToggle.addEventListener('click', function() {
menu.classList.toggle('mobile-menu-open');
});
}
function initializeSmoothScroll() {
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth',
block: 'start'
});
}
});
});
}
function initializeExternalLinks() {
document.querySelectorAll('a[href^="http"]').forEach(link => {
if (!link.hostname || link.hostname !== window.location.hostname) {
link.target = '_blank';
link.rel = 'noopener noreferrer';
if (!link.querySelector('.external-indicator')) {
const indicator = document.createElement('span');
indicator.className = 'external-indicator';
indicator.innerHTML = ' ↗';
indicator.setAttribute('aria-hidden', 'true');
link.appendChild(indicator);
}
}
});
}
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
function throttle(func, limit) {
let inThrottle;
return function() {
const args = arguments;
const context = this;
if (!inThrottle) {
func.apply(context, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}