/** * 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); } }); }); }); } });