Skip to content

Instantly share code, notes, and snippets.

@RickCogley
Last active May 22, 2025 06:48
Show Gist options
  • Save RickCogley/646ecc29636b4811420115d76b36f75f to your computer and use it in GitHub Desktop.
Save RickCogley/646ecc29636b4811420115d76b36f75f to your computer and use it in GitHub Desktop.
eSolia new site main.js for refactoring
// Load this first
(function() {
var userAgent = navigator.userAgent || navigator.vendor || window.opera;
if (/windows phone/i.test(userAgent)) {
document.body.classList.add('os-windows-phone');
} else if (/android/i.test(userAgent)) {
document.body.classList.add('os-android');
} else if (/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) {
document.body.classList.add('os-ios');
} else if (/Mac/i.test(userAgent)) {
document.body.classList.add('os-mac');
} else if (/Win/i.test(userAgent)) { // This would catch most Windows desktops/laptops
document.body.classList.add('os-windows');
}
})();
// Function to load a script from a CDN with additional attributes
function loadVendorScript(src, attributes, callback) {
var script = document.createElement('script');
script.src = src;
// script.async = true;
// Set additional attributes
for (var key in attributes) {
if (attributes.hasOwnProperty(key)) {
script.setAttribute(key, attributes[key]);
}
}
script.onload = callback;
document.head.appendChild(script);
}
// Swap logo on scroll and make nav bg more opaque
//window.addEventListener('scroll', () => {
// const largeLogo = document.getElementById('large-logo');
// const smallLogo = document.getElementById('small-logo');
// const topNavBG = document.getElementById('top-nav-bg');
// const scrollPosition = window.scrollY;
// Handle logo swap
// if (scrollPosition > 10) {
// largeLogo.classList.add('opacity-0');
// smallLogo.classList.remove('opacity-0');
//} else {
// largeLogo.classList.remove('opacity-0');
// smallLogo.classList.add('opacity-0');
//}
// Handle nav opacity changes based on scroll position
// if (scrollPosition > 50) {
// topNavBG.classList.remove('bg-stone-50/50', 'dark:bg-stone-700/50', 'bg-stone-50/70', 'dark:bg-stone-700/70');
// topNavBG.classList.add('bg-stone-50/95', 'dark:bg-stone-700/95');
// } else if (scrollPosition > 30 && scrollPosition <= 50) {
// topNavBG.classList.remove('bg-stone-50/50', 'dark:bg-stone-700/50', 'bg-stone-50/95', 'dark:bg-stone-700/95');
// topNavBG.classList.add('bg-stone-50/70', 'dark:bg-stone-700/70');
// } else if (scrollPosition > 10 && scrollPosition <= 30) {
// topNavBG.classList.remove('bg-stone-50/70', 'dark:bg-stone-700/70', 'bg-stone-50/95', 'dark:bg-stone-700/95');
// topNavBG.classList.add('bg-stone-50/50', 'dark:bg-stone-700/50');
// } else {
// topNavBG.classList.remove('bg-stone-50/70', 'dark:bg-stone-700/70', 'bg-stone-50/95', 'dark:bg-stone-700/95');
// topNavBG.classList.add('bg-stone-50/50', 'dark:bg-stone-700/50');
// }
//});
// Theme Toggle with Alpine.js
// document.addEventListener('alpine:init', () => {
// Alpine.data('themeToggle', () => ({
// darkMode: localStorage.getItem('darkMode') === 'true' || false,
// init() {
// this.$watch('darkMode', value => {
// localStorage.setItem('darkMode', value);
// document.body.classList.toggle('dark', value);
// });
// // Ensure the correct class is applied on page load
// document.body.classList.toggle('dark', this.darkMode);
// }
// }));
// });
// Load Mastodon Comments from a local file
import Comments from "./comments.js";
customElements.define("mastodon-comments", Comments);
// Load Alpine.js with defer
// loadVendorScript('https://cdn.jsdelivr.net/npm/[email protected]/dist/cdn.min.js', { 'defer': '' }, function() {
// console.log('Alpine.js loaded with defer');
// });
// Load Fathom Analytics script with data-site attribute and defer
loadVendorScript('https://cdn.usefathom.com/script.js', { 'data-site': 'OIXGEUHR', 'defer': '' }, function() {
console.log('Fathom Analytics loaded with defer and data-site attribute');
});
// Handle keydown event for anchor tags with role="button"
const buttons = document.querySelectorAll('a[role="button"]');
buttons.forEach(button => {
button.addEventListener('keydown', function(event) {
if (event.key === 'Enter') {
this.click();
}
});
});
document.addEventListener('DOMContentLoaded', function () {
console.log('--- DOM Content Loaded ---');
// === Shared Utility ===
function trapFocus(container) {
const focusableElements = container.querySelectorAll(
'a[href], button, textarea, input, select, [tabindex]:not([tabindex="-1"])'
);
const first = focusableElements[0];
const last = focusableElements[focusableElements.length - 1];
container.addEventListener('keydown', function (e) {
if (e.key === 'Tab') {
if (e.shiftKey && document.activeElement === first) {
e.preventDefault();
last.focus();
} else if (!e.shiftKey && document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
});
}
// === Search Modal ===
(function initSearchModal() {
const searchModal = document.getElementById("searchModal");
const searchButton = document.getElementById("search-button");
const searchModalCloseButton = document.getElementById("modal-close");
if (!(searchModal && searchButton && searchModalCloseButton)) return;
function openSearchModal() {
searchModal.style.display = "block";
const pageFind = searchModal.querySelector(".pagefind-ui__search-input");
if (pageFind) pageFind.focus();
trapFocus(searchModal);
}
function closeSearchModal() {
searchModal.style.display = "none";
searchButton.focus();
}
searchButton.addEventListener("click", openSearchModal);
searchModalCloseButton.addEventListener("click", closeSearchModal);
window.addEventListener("click", function (event) {
if (event.target === searchModal) closeSearchModal();
});
document.addEventListener("keydown", function (event) {
if (event.key === "Escape" && searchModal.style.display === "block") {
closeSearchModal();
}
const isMac = navigator.platform.toUpperCase().includes("MAC");
const isCmdOrCtrl = isMac ? event.metaKey : event.ctrlKey;
if (isCmdOrCtrl && event.key === "k") {
event.preventDefault();
openSearchModal();
}
});
})();
// === Mobile Menu Modal ===
(function initMobileMenuModal() {
const overlay = document.getElementById("mobile-menu-modal-overlay");
const panel = document.getElementById("mobile-menu-modal-panel");
const openBtn = document.getElementById("mobile-menu-button");
const closeBtn = document.getElementById("mobile-menu-close-button");
if (!(overlay && panel && openBtn && closeBtn)) return;
function showMobileMenuModal() {
overlay.classList.remove("hidden", "opacity-0", "pointer-events-none");
panel.classList.remove("hidden", "translate-x-full");
// Trigger reflow
void overlay.offsetWidth;
void panel.offsetWidth;
overlay.classList.add("opacity-100");
panel.classList.add("translate-x-0");
trapFocus(panel);
}
function hideMobileMenuModal() {
overlay.classList.remove("opacity-100");
overlay.classList.add("opacity-0", "pointer-events-none");
panel.classList.remove("translate-x-0");
panel.classList.add("translate-x-full");
setTimeout(() => {
overlay.classList.add("hidden");
panel.classList.add("hidden");
openBtn.focus();
}, 300); // Match Tailwind transition
}
openBtn.addEventListener("click", showMobileMenuModal);
closeBtn.addEventListener("click", hideMobileMenuModal);
overlay.addEventListener("click", function (event) {
if (event.target === overlay) hideMobileMenuModal();
});
document.addEventListener("keydown", function (event) {
if (event.key === "Escape" && !panel.classList.contains("hidden")) {
hideMobileMenuModal();
}
});
})();
});
// For TOC details opening
// This script will automatically open the Table of Contents (ToC) on medium and larger screens
document.addEventListener('DOMContentLoaded', () => {
const path = window.location.pathname;
// Exit early if path is '/' or matches '/xx/' where xx is a 2-letter language code
const isRootOrLangRoot = /^\/([a-z]{2}\/)?$/.test(path);
if (isRootOrLangRoot) {
return;
}
// Check if the ToC details element exists
const tocDetails = document.getElementById('toc-details');
if (!tocDetails) {
console.warn('No Table of Contents found');
return;
}
const handleDetailsState = () => {
const isMediumOrLargeScreen = window.matchMedia('(min-width: 768px)').matches;
if (isMediumOrLargeScreen) {
// On medium and larger screens:
// The 'open' attribute is set in the HTML. We DO NOT modify it here.
// This allows the user to freely open/close the ToC, and their action will persist.
} else {
// On smaller screens:
// Ensure the ToC is always closed by default (remove 'open' if present).
tocDetails.removeAttribute('open');
}
};
// Set initial state on page load
handleDetailsState();
// Re-evaluate state on window resize
window.addEventListener('resize', handleDetailsState);
});
document.addEventListener("DOMContentLoaded", function () {
const nav = document.getElementById("top-nav");
const sentinel = document.getElementById("nav-sentinel");
const largeLogo = document.getElementById("large-logo");
const symbolLogo = document.getElementById("symbol-logo");
const observer = new IntersectionObserver(
([entry]) => {
const isPinned = !entry.isIntersecting;
nav.setAttribute("data-pinned", isPinned ? "true" : "false");
if (window.innerWidth <= 640) {
if (isPinned) {
largeLogo.classList.add("opacity-0", "scale-95");
largeLogo.classList.remove("opacity-100", "scale-110");
symbolLogo.classList.remove("opacity-0", "scale-90");
symbolLogo.classList.add("opacity-100", "scale-100");
} else {
largeLogo.classList.remove("opacity-0", "scale-95");
largeLogo.classList.add("opacity-100", "scale-110");
symbolLogo.classList.add("opacity-0", "scale-90");
symbolLogo.classList.remove("opacity-100", "scale-100");
}
}
},
{ threshold: 0 }
);
observer.observe(sentinel);
});
function trapFocus(element) {
const focusableSelectors = 'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])';
const focusableElements = element.querySelectorAll(focusableSelectors);
const first = focusableElements[0];
const last = focusableElements[focusableElements.length - 1];
function handleTab(e) {
if (e.key !== 'Tab') return;
if (e.shiftKey) {
if (document.activeElement === first) {
e.preventDefault();
last.focus();
}
} else {
if (document.activeElement === last) {
e.preventDefault();
first.focus();
}
}
}
element.addEventListener('keydown', handleTab);
first.focus();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment