forked from colin/resume
2
0
Fork 0
lucky-ddg/docker/resume/includes.js

275 lines
12 KiB
JavaScript

/**
* Includes.js - Handles the inclusion of header and footer files
* and applies the correct active states to navigation items
*/
document.addEventListener('DOMContentLoaded', function() {
// Function to include HTML content
async function includeHTML(elementId, filePath, callback) {
try {
const response = await fetch(filePath);
if (!response.ok) {
throw new Error(`Failed to load ${filePath}: ${response.status} ${response.statusText}`);
}
const content = await response.text();
document.getElementById(elementId).innerHTML = content;
if (callback) callback();
} catch (error) {
console.error('Error including HTML:', error);
}
}
// Function to include HTML content in the head
async function includeInHead(filePath) {
try {
const response = await fetch(filePath);
if (!response.ok) {
throw new Error(`Failed to load ${filePath}: ${response.status} ${response.statusText}`);
}
const content = await response.text();
const headElement = document.getElementsByTagName('head')[0];
const tempDiv = document.createElement('div');
tempDiv.innerHTML = content;
// Append each child from the loaded content to the head
while (tempDiv.firstChild) {
headElement.appendChild(tempDiv.firstChild);
}
} catch (error) {
console.error('Error including HTML in head:', error);
}
}
// Function to set active navigation item
function setActiveNavItem() {
const currentPath = window.location.pathname;
// Wait for the navigation to be loaded
setTimeout(() => {
// Remove all active classes first
document.querySelectorAll('.main-nav a').forEach(link => {
link.classList.remove('active');
});
// Set active class based on current path
if (currentPath === '/' || currentPath === '/index.html') {
const portfolioLink = document.getElementById('nav-portfolio');
if (portfolioLink) portfolioLink.classList.add('active');
} else if (currentPath.includes('/stories/')) {
const storiesLink = document.getElementById('nav-stories');
if (storiesLink) storiesLink.classList.add('active');
// Check for specific story pages
if (currentPath.includes('viperwire.html')) {
const link = document.getElementById('nav-viperwire');
if (link) link.classList.add('active');
} else if (currentPath.includes('fawe-plotsquared.html')) {
const link = document.getElementById('nav-fawe');
if (link) link.classList.add('active');
} else if (currentPath.includes('healthcare-platform.html')) {
const link = document.getElementById('nav-healthcare');
if (link) link.classList.add('active');
} else if (currentPath.includes('wordpress-security.html')) {
const link = document.getElementById('nav-wordpress');
if (link) link.classList.add('active');
} else if (currentPath.includes('airport-dns.html')) {
const link = document.getElementById('nav-airport');
if (link) link.classList.add('active');
} else if (currentPath.includes('nitric-leadership.html')) {
const link = document.getElementById('nav-nitric');
if (link) link.classList.add('active');
} else if (currentPath.includes('open-source-success.html')) {
const link = document.getElementById('nav-opensource');
if (link) link.classList.add('active');
}
} else if (currentPath.includes('/one-pager-tools/')) {
const toolsLink = document.getElementById('nav-tools');
if (toolsLink) toolsLink.classList.add('active');
// Check for specific tool pages
if (currentPath.includes('csv-tool.html')) {
const link = document.getElementById('nav-csv');
if (link) link.classList.add('active');
}
}
}, 100); // Small delay to ensure the DOM is updated
}
// Function to initialize theme toggle
function initThemeToggle() {
const themeToggle = document.getElementById('themeToggle');
if (!themeToggle) {
console.log('Theme toggle button not found on this page');
return;
}
// Check for saved theme preference, default to auto
const savedTheme = localStorage.getItem('theme') || 'auto';
// Set initial value for aria-checked attribute
themeToggle.setAttribute('aria-checked', savedTheme !== 'auto');
updateTheme(savedTheme);
function updateTheme(theme) {
// Update button state and labels
const themeLabels = {
light: 'Theme mode: Light',
dark: 'Theme mode: Dark',
auto: 'Theme mode: Auto'
};
themeToggle.setAttribute('aria-label', themeLabels[theme]);
themeToggle.setAttribute('aria-checked', theme !== 'auto');
// Update button icon
const themeIcons = {
light: '🌞',
dark: '🌙',
auto: '🌓'
};
themeToggle.textContent = themeIcons[theme];
const html = document.documentElement;
if (theme === 'auto') {
html.removeAttribute('data-theme');
} else {
html.setAttribute('data-theme', theme);
}
}
themeToggle.addEventListener('click', () => {
const html = document.documentElement;
const currentTheme = html.getAttribute('data-theme') || 'auto';
let newTheme;
switch(currentTheme) {
case 'light':
newTheme = 'dark';
break;
case 'dark':
newTheme = 'auto';
break;
default:
newTheme = 'light';
}
updateTheme(newTheme);
localStorage.setItem('theme', newTheme);
});
// Handle keyboard navigation
themeToggle.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
themeToggle.click();
}
});
}
// Include favicon links
includeInHead('/includes/favicon-links.html');
// Process header and footer placeholders
const headerElement = document.getElementById('header-include');
const footerElement = document.getElementById('footer-include');
if (headerElement) {
includeHTML('header-include', '/includes/header.html', () => {
setActiveNavItem();
setupNavDropdowns();
initThemeToggle();
});
}
if (footerElement) {
includeHTML('footer-include', '/includes/footer.html');
}
// Setup dropdown behavior with delay
function setupNavDropdowns() {
const dropdowns = document.querySelectorAll('.main-nav .dropdown');
let timeoutId;
dropdowns.forEach(dropdown => {
// Mouse interactions
dropdown.addEventListener('mouseenter', () => {
clearTimeout(timeoutId);
dropdowns.forEach(d => {
if (d !== dropdown) {
d.querySelector('.dropdown-content').style.display = 'none';
d.querySelector('.dropdown-content').style.opacity = '0';
d.querySelector('.dropdown-content').style.visibility = 'hidden';
}
});
const dropdownContent = dropdown.querySelector('.dropdown-content');
dropdownContent.style.display = 'block';
// Small delay to allow CSS transition to work properly
setTimeout(() => {
dropdownContent.style.opacity = '1';
dropdownContent.style.visibility = 'visible';
}, 10);
});
dropdown.addEventListener('mouseleave', () => {
const dropdownContent = dropdown.querySelector('.dropdown-content');
// Add delay before hiding the dropdown
timeoutId = setTimeout(() => {
dropdownContent.style.opacity = '0';
dropdownContent.style.visibility = 'hidden';
// Wait for transition to complete before changing display
setTimeout(() => {
if (dropdownContent.style.opacity === '0') {
dropdownContent.style.display = 'none';
}
}, 200);
}, 300); // 300ms delay before starting to close
});
// Keyboard interactions
const dropdownLink = dropdown.querySelector('a');
dropdownLink.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') {
e.preventDefault();
const dropdownContent = dropdown.querySelector('.dropdown-content');
dropdownContent.style.display = 'block';
setTimeout(() => {
dropdownContent.style.opacity = '1';
dropdownContent.style.visibility = 'visible';
// Focus the first link in the dropdown
const firstLink = dropdownContent.querySelector('a');
if (firstLink) firstLink.focus();
}, 10);
}
});
// Add keyboard navigation for dropdown items
const dropdownLinks = dropdown.querySelectorAll('.dropdown-content a');
dropdownLinks.forEach((link, index) => {
link.addEventListener('keydown', (e) => {
if (e.key === 'ArrowDown') {
e.preventDefault();
const nextLink = dropdownLinks[index + 1] || dropdownLinks[0];
nextLink.focus();
} else if (e.key === 'ArrowUp') {
e.preventDefault();
const prevLink = dropdownLinks[index - 1] || dropdownLinks[dropdownLinks.length - 1];
prevLink.focus();
} else if (e.key === 'Escape') {
e.preventDefault();
dropdown.querySelector('a').focus();
const dropdownContent = dropdown.querySelector('.dropdown-content');
dropdownContent.style.opacity = '0';
dropdownContent.style.visibility = 'hidden';
setTimeout(() => {
dropdownContent.style.display = 'none';
}, 200);
}
});
});
});
}
});