2025-11-25 13:05:37 -05:00
|
|
|
(function () {
|
|
|
|
|
const scriptEl = document.currentScript;
|
|
|
|
|
const API_URL = scriptEl?.dataset?.apiUrl;
|
|
|
|
|
|
|
|
|
|
window.toggleTheme = () => {
|
|
|
|
|
const currentTheme = document.documentElement.getAttribute("data-theme");
|
|
|
|
|
const newTheme = currentTheme === "dark" ? "light" : "dark";
|
2025-12-17 13:33:31 -05:00
|
|
|
|
|
|
|
|
// Toggle the dark class for Tailwind
|
|
|
|
|
if (newTheme === "dark") {
|
|
|
|
|
document.documentElement.classList.add("dark");
|
|
|
|
|
} else {
|
|
|
|
|
document.documentElement.classList.remove("dark");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Dispatch event for astro-themes and other listeners
|
2025-11-25 13:05:37 -05:00
|
|
|
document.dispatchEvent(new CustomEvent("set-theme", { detail: newTheme }));
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
window.handleLogout = async () => {
|
|
|
|
|
try {
|
|
|
|
|
await fetch(`${API_URL}/auth/logout`, {
|
|
|
|
|
method: "POST",
|
|
|
|
|
credentials: "include",
|
|
|
|
|
});
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Logout failed", error);
|
|
|
|
|
} finally {
|
|
|
|
|
window.location.href = "/";
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-22 13:53:43 -05:00
|
|
|
function setLenisEnabled(enabled) {
|
|
|
|
|
const lenis = window.__lenis;
|
|
|
|
|
if (!lenis) return;
|
|
|
|
|
if (enabled) {
|
|
|
|
|
lenis.start();
|
|
|
|
|
} else {
|
|
|
|
|
lenis.stop();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-25 13:05:37 -05:00
|
|
|
function initMobileMenu() {
|
|
|
|
|
const menuBtn = document.getElementById("mobile-menu-btn");
|
|
|
|
|
const mobileMenu = document.getElementById("mobile-menu");
|
|
|
|
|
const menuIcon = document.getElementById("menu-icon");
|
|
|
|
|
const closeIcon = document.getElementById("close-icon");
|
|
|
|
|
|
|
|
|
|
if (!menuBtn || !mobileMenu || !menuIcon || !closeIcon) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const newMenuBtn = menuBtn.cloneNode(true);
|
|
|
|
|
menuBtn.parentNode.replaceChild(newMenuBtn, menuBtn);
|
|
|
|
|
|
|
|
|
|
newMenuBtn.addEventListener("click", (e) => {
|
|
|
|
|
e.stopPropagation();
|
|
|
|
|
const isOpen = mobileMenu.classList.contains("open");
|
|
|
|
|
|
|
|
|
|
if (isOpen) {
|
|
|
|
|
mobileMenu.classList.remove("open");
|
|
|
|
|
menuIcon.style.display = "block";
|
|
|
|
|
closeIcon.style.display = "none";
|
2026-02-22 13:53:43 -05:00
|
|
|
setLenisEnabled(true);
|
2025-11-25 13:05:37 -05:00
|
|
|
} else {
|
|
|
|
|
mobileMenu.classList.add("open");
|
|
|
|
|
menuIcon.style.display = "none";
|
|
|
|
|
closeIcon.style.display = "block";
|
2026-02-22 13:53:43 -05:00
|
|
|
setLenisEnabled(false);
|
2025-11-25 13:05:37 -05:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const mobileLinks = mobileMenu.querySelectorAll("a");
|
|
|
|
|
mobileLinks.forEach((link) => {
|
|
|
|
|
link.addEventListener("click", () => {
|
|
|
|
|
mobileMenu.classList.remove("open");
|
|
|
|
|
menuIcon.style.display = "block";
|
|
|
|
|
closeIcon.style.display = "none";
|
2026-02-22 13:53:43 -05:00
|
|
|
setLenisEnabled(true);
|
2025-11-25 13:05:37 -05:00
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const closeHandler = (e) => {
|
|
|
|
|
if (!mobileMenu.contains(e.target) && !newMenuBtn.contains(e.target)) {
|
|
|
|
|
if (mobileMenu.classList.contains("open")) {
|
|
|
|
|
mobileMenu.classList.remove("open");
|
|
|
|
|
menuIcon.style.display = "block";
|
|
|
|
|
closeIcon.style.display = "none";
|
2026-02-22 13:53:43 -05:00
|
|
|
setLenisEnabled(true);
|
2025-11-25 13:05:37 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
document.removeEventListener("click", closeHandler);
|
|
|
|
|
document.addEventListener("click", closeHandler);
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-22 13:53:43 -05:00
|
|
|
function initDropdownExternalLinks() {
|
|
|
|
|
// Close desktop dropdown when external links are clicked
|
|
|
|
|
const dropdownLinks = document.querySelectorAll('.nav-dropdown a[target="_blank"]');
|
|
|
|
|
dropdownLinks.forEach((link) => {
|
|
|
|
|
link.addEventListener("click", () => {
|
|
|
|
|
// Blur the parent nav item to close the CSS hover-based dropdown
|
|
|
|
|
const navItem = link.closest('.nav-item--has-children');
|
|
|
|
|
if (navItem) {
|
|
|
|
|
const parentLink = navItem.querySelector(':scope > a');
|
|
|
|
|
if (parentLink) {
|
|
|
|
|
parentLink.blur();
|
|
|
|
|
}
|
|
|
|
|
// Force close by temporarily disabling pointer events
|
|
|
|
|
navItem.style.pointerEvents = 'none';
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
navItem.style.pointerEvents = '';
|
|
|
|
|
}, 100);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function initDesktopDropdownToggle() {
|
|
|
|
|
// Desktop: click parent to toggle dropdown open/closed
|
|
|
|
|
const desktopNav = document.querySelector('nav .hidden.md\\:flex');
|
|
|
|
|
if (!desktopNav) return;
|
|
|
|
|
|
|
|
|
|
const parentItems = desktopNav.querySelectorAll('.nav-item--has-children');
|
|
|
|
|
parentItems.forEach((navItem) => {
|
|
|
|
|
const parentLink = navItem.querySelector(':scope > a');
|
|
|
|
|
if (!parentLink) return;
|
|
|
|
|
|
|
|
|
|
parentLink.addEventListener('click', (e) => {
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
const isOpen = navItem.classList.contains('dropdown-open');
|
|
|
|
|
|
|
|
|
|
// Close any other open dropdowns
|
|
|
|
|
parentItems.forEach((item) => item.classList.remove('dropdown-open'));
|
|
|
|
|
|
|
|
|
|
if (!isOpen) {
|
|
|
|
|
navItem.classList.add('dropdown-open');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Close dropdown when clicking outside
|
|
|
|
|
document.addEventListener('click', (e) => {
|
|
|
|
|
if (!desktopNav.contains(e.target)) {
|
|
|
|
|
parentItems.forEach((item) => item.classList.remove('dropdown-open'));
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function initMobileSubmenus() {
|
|
|
|
|
const mobileMenu = document.getElementById("mobile-menu");
|
|
|
|
|
if (!mobileMenu) return;
|
|
|
|
|
|
|
|
|
|
// Find all parent links that have a sibling mobile-subnav
|
|
|
|
|
const parentLinks = mobileMenu.querySelectorAll('a:has(+ .mobile-subnav)');
|
|
|
|
|
|
|
|
|
|
parentLinks.forEach((link) => {
|
|
|
|
|
const subnav = link.nextElementSibling;
|
|
|
|
|
const caret = link.querySelector('.mobile-caret');
|
|
|
|
|
|
|
|
|
|
if (!subnav || !subnav.classList.contains('mobile-subnav')) return;
|
|
|
|
|
|
|
|
|
|
// Clone to remove existing listeners
|
|
|
|
|
const newLink = link.cloneNode(true);
|
|
|
|
|
link.parentNode.replaceChild(newLink, link);
|
|
|
|
|
|
|
|
|
|
const newCaret = newLink.querySelector('.mobile-caret');
|
|
|
|
|
|
|
|
|
|
newLink.addEventListener('click', (e) => {
|
|
|
|
|
// Toggle subnav open/closed on click
|
|
|
|
|
e.preventDefault();
|
|
|
|
|
subnav.classList.toggle('open');
|
|
|
|
|
if (newCaret) newCaret.classList.toggle('open');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const ready = () => {
|
|
|
|
|
initMobileMenu();
|
|
|
|
|
initDropdownExternalLinks();
|
|
|
|
|
initDesktopDropdownToggle();
|
|
|
|
|
initMobileSubmenus();
|
|
|
|
|
};
|
2025-11-25 13:05:37 -05:00
|
|
|
|
|
|
|
|
if (document.readyState === "loading") {
|
|
|
|
|
document.addEventListener("DOMContentLoaded", ready, { once: true });
|
|
|
|
|
} else {
|
|
|
|
|
ready();
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-22 13:53:43 -05:00
|
|
|
document.addEventListener("astro:page-load", () => {
|
|
|
|
|
initMobileMenu();
|
|
|
|
|
initDropdownExternalLinks();
|
|
|
|
|
initDesktopDropdownToggle();
|
|
|
|
|
initMobileSubmenus();
|
|
|
|
|
});
|
2025-11-25 13:05:37 -05:00
|
|
|
})();
|