Last active
May 22, 2025 06:48
-
-
Save RickCogley/646ecc29636b4811420115d76b36f75f to your computer and use it in GitHub Desktop.
eSolia new site main.js for refactoring
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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