// router.js - Simple SPA router import { renderHomePage, getHomePageSidebar } from './pages/HomePage.js'; import { renderAreaPage, getAreaPageSidebar } from './pages/AreaPage.js'; import { renderResourcesPage, getResourcesPageSidebar } from './pages/ResourcesPage.js'; class Router { constructor() { this.routes = { '/': 'home', '/home': 'home', '/efficiency': 'efficiency', '/personal': 'personal', '/rights': 'rights', '/ecosystems': 'ecosystems', '/about': 'resources' }; this.currentPage = null; this.init(); } init() { // Handle initial page load this.loadPage(window.location.pathname); // Handle browser back/forward window.addEventListener('popstate', (e) => { const path = window.location.pathname; const hash = window.location.hash; const fullUrl = path + hash; // Use navigateToUrl to ensure proper scroll behavior this.navigateToUrl(fullUrl); }); // Handle hash changes for navigation window.addEventListener('hashchange', (e) => { const hash = window.location.hash.substring(1); if (hash) { // Scroll to the section after a short delay to ensure content is loaded setTimeout(() => this.scrollToSection(hash), 100); } }); // Handle initial hash if present const initialHash = window.location.hash.substring(1); if (initialHash) { setTimeout(() => this.scrollToSection(initialHash), 200); } // Initialize unified navigation handling this.initializeNavigation(); } async loadPage(path) { const route = this.routes[path] || 'home'; if (this.currentPage === route) { // Same page, no need to reload return; } this.currentPage = route; try { switch (route) { case 'home': await this.loadHomePage(); break; case 'efficiency': case 'personal': case 'rights': case 'ecosystems': await this.loadAreaPage(route); break; case 'resources': await this.loadResourcesPage(); break; default: await this.loadHomePage(); } } catch (error) { console.error('Error loading page:', error); // Fallback to home page await this.loadHomePage(); } return Promise.resolve(); } async loadHomePage() { const mainContent = document.getElementById('main-content'); const leftSidebar = document.getElementById('left-sidebar'); if (!mainContent) { return; } try { // Get page content and sidebar const homePage = renderHomePage(); // Update content const contentContainer = mainContent.querySelector('.max-w-4xl') || mainContent; contentContainer.innerHTML = homePage.content; // Update sidebar if it exists if (leftSidebar) { leftSidebar.innerHTML = getHomePageSidebar(); } // Initialize page if (homePage.init) { homePage.init(); } // Update navigation state this.updateNavigation('home'); } catch (error) { // Fallback to error content const contentContainer = mainContent.querySelector('.max-w-4xl') || mainContent; contentContainer.innerHTML = `

Error Loading Page

Sorry, there was an error loading the home page.

`; } } async loadAreaPage(area) { const mainContent = document.getElementById('main-content'); const leftSidebar = document.getElementById('left-sidebar'); if (!mainContent) { return; } try { // Get area page content and sidebar const areaPage = renderAreaPage(area); // Update content const contentContainer = mainContent.querySelector('.max-w-4xl') || mainContent; contentContainer.innerHTML = areaPage.content; // Update sidebar if (leftSidebar) { leftSidebar.innerHTML = getAreaPageSidebar(area); } // Initialize page if (areaPage.init) { areaPage.init(); } } catch (error) { console.error(`Error loading ${area} page:`, error); // Fallback content const contentContainer = mainContent.querySelector('.max-w-4xl') || mainContent; contentContainer.innerHTML = `

Error Loading Page

Sorry, there was an error loading the ${area} page.

`; } this.updateNavigation(area); } async loadResourcesPage() { const mainContent = document.getElementById('main-content'); const leftSidebar = document.getElementById('left-sidebar'); if (!mainContent) return; try { // Get resources page content and sidebar const resourcesPage = renderResourcesPage(); // Update content const contentContainer = mainContent.querySelector('.max-w-4xl') || mainContent; contentContainer.innerHTML = resourcesPage.content; // Update sidebar if (leftSidebar) { leftSidebar.innerHTML = getResourcesPageSidebar(); } // Initialize page if (resourcesPage.init) { resourcesPage.init(); } } catch (error) { // Fallback content const contentContainer = mainContent.querySelector('.max-w-4xl') || mainContent; contentContainer.innerHTML = `

Error Loading Page

Sorry, there was an error loading the resources page.

`; } this.updateNavigation('resources'); } updateNavigation(currentPage) { // Update header navigation active states const navLinks = document.querySelectorAll('header nav a'); navLinks.forEach(link => { link.classList.remove('text-blue-600', 'bg-blue-50'); link.classList.add('text-gray-700'); const href = link.getAttribute('href'); if ((currentPage === 'home' && (href === '/' || href === '/home')) || (currentPage === 'resources' && href === '/about') || (currentPage !== 'home' && currentPage !== 'resources' && href === `/${currentPage}`)) { link.classList.remove('text-gray-700'); link.classList.add('text-blue-600', 'bg-blue-50'); } }); } initializeNavigation() { // Use event delegation to handle all navigation links document.addEventListener('click', (e) => { const link = e.target.closest('a'); if (!link) return; const href = link.getAttribute('href'); if (!href) return; // Handle different types of links if (href.startsWith('/') && !href.startsWith('/js/') && !href.startsWith('/css/') && !href.startsWith('/images/')) { // Check if this is a known SPA route const path = href.split('#')[0]; if (this.routes[path]) { e.preventDefault(); this.navigateToUrl(href); } } else if (href.startsWith('#')) { // Same-page hash navigation e.preventDefault(); this.scrollToSection(href.substring(1)); } // External links are handled normally by browser }); } navigateToUrl(fullUrl) { // Parse URL into path and hash const [path, hash] = fullUrl.split('#'); // Update browser URL window.history.pushState({}, '', fullUrl); // Load page and then handle hash this.loadPage(path).then(() => { if (hash) { setTimeout(() => this.scrollToSection(hash), 100); } else { // No hash - scroll to top of page with a delay to ensure content is rendered setTimeout(() => { window.scrollTo({ top: 0, behavior: 'smooth' }); }, 100); } }); } scrollToSection(sectionId) { const element = document.getElementById(sectionId); if (element) { // Scroll to element with offset to show title const elementRect = element.getBoundingClientRect(); const absoluteElementTop = elementRect.top + window.pageYOffset; const offset = 120; // Account for fixed header + some padding window.scrollTo({ top: absoluteElementTop - offset, behavior: 'smooth' }); } } } // Export router instance export const router = new Router();