Files
asimonson1125.github.io/src/static/js/responsive.js
2026-09-05 20:37:31 -05:00

122 lines
3.8 KiB
JavaScript
Executable File

function toggleMenu(collapse) {
if (window.innerWidth < 1400) {
const menu = document.querySelector(".navControl");
const bar = document.querySelector(".header");
const btn = document.getElementById("menu");
const isCollapsed = !menu.style.maxHeight || menu.style.maxHeight === "0px";
if (isCollapsed && !collapse) {
menu.style.maxHeight = `${menu.scrollHeight + 10}px`;
bar.style.borderBottomWidth = "0px";
if (btn) btn.setAttribute("aria-expanded", "true");
} else {
menu.style.maxHeight = "0px";
bar.style.borderBottomWidth = "3px";
if (btn) btn.setAttribute("aria-expanded", "false");
}
}
}
async function goto(location, { push = true } = {}) {
const loadingBar = document.getElementById('loading-bar');
if (loadingBar) {
loadingBar.style.width = ''; // Clear inline style from previous run
}
let loadingTimeout = setTimeout(() => {
if (loadingBar) {
loadingBar.classList.remove('finish');
loadingBar.classList.add('active');
loadingBar.classList.add('visible');
}
}, 150);
try {
const response = await fetch("/api/goto/" + location, {
credentials: "include",
method: "GET",
mode: "cors",
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
// Wait for the full body to download - this is usually the slow part
const [metadata, content] = await response.json();
document.dispatchEvent(new Event('beforenavigate'));
const root = document.getElementById("root");
root.innerHTML = content;
// Re-execute scripts
root.querySelectorAll("script").forEach(function(oldScript) {
const newScript = document.createElement("script");
Array.from(oldScript.attributes).forEach(function(attr) {
newScript.setAttribute(attr.name, attr.value);
});
newScript.textContent = oldScript.textContent;
oldScript.parentNode.replaceChild(newScript, oldScript);
});
if (window.location.href.includes("#")) {
const id = decodeURIComponent(window.location.hash.substring(1));
const el = document.getElementById(id);
if (el) el.scrollIntoView();
} else {
window.scrollTo({ top: 0, left: 0, behavior: "instant" });
}
toggleMenu(true);
document.querySelector("title").textContent = metadata["title"];
if (push) {
history.pushState(null, null, metadata["canonical"]);
}
} catch (err) {
console.error("Navigation failed:", err);
} finally {
clearTimeout(loadingTimeout);
if (loadingBar && loadingBar.classList.contains('active')) {
loadingBar.classList.add('finish');
loadingBar.classList.remove('active');
setTimeout(() => {
if (!loadingBar.classList.contains('active')) {
loadingBar.style.width = '0%';
loadingBar.classList.remove('finish');
loadingBar.classList.remove('visible');
}
}, 500);
}
}
}
function backButton() {
const path = window.location.pathname;
goto(path.substring(1), { push: false });
}
function toggleFeatureProject(header) {
const panel = header.closest(".feature-project");
const expanded = panel.classList.toggle("expanded");
header.setAttribute("aria-expanded", expanded ? "true" : "false");
}
function activeSkill(obj) {
let skill = obj.closest(".skill");
if (skill.classList.contains("activeSkill")) {
skill.classList.remove("activeSkill");
const btn = obj.closest('.skillname') || obj.querySelector?.('.skillname') || obj;
if (btn && btn.setAttribute) btn.setAttribute("aria-expanded", "false");
return;
}
while (skill) {
skill.classList.add("activeSkill");
const nameEl = skill.querySelector?.(':scope > .skillname') || skill.querySelector('.skillname');
if (nameEl) nameEl.setAttribute("aria-expanded", "true");
skill = skill.parentElement.closest(".skill");
}
}