document.addEventListener('DOMContentLoaded', () => { // Listen for back/forward browser buttons window.addEventListener('popstate', (e) => { if (e.state && e.state.url) { fetchPage(e.state.url, false); } else { fetchPage(window.location.href, false); } }); // Intercept clicks globally document.body.addEventListener('click', (e) => { // Find closest anchor tag const a = e.target.closest('a'); if (!a) return; // Ignore external links, new tabs, and non-http links if (a.target === '_blank' || a.host !== window.location.host || a.protocol !== window.location.protocol) return; // Ignore links meant for modals or javascript functions const href = a.getAttribute('href'); if (!href || href === '#' || href.startsWith('javascript:') || a.hasAttribute('data-toggle')) return; // Only apply SPA to internal .php pages or root if (a.pathname.endsWith('.php') || a.pathname === '/' || a.pathname === '') { e.preventDefault(); const url = a.href; if (url !== window.location.href) { fetchPage(url, true); } } }); }); function getSkeletonHtml() { return `




`; } function fetchPage(url, push = true) { const mainWrapper = document.querySelector('.main-panel'); if (!mainWrapper) { window.location.href = url; // Hard fallback if structure is weird return; } let contentWrapper = mainWrapper.querySelector('.content-wrapper'); if(contentWrapper) { // Show skeleton immediately contentWrapper.innerHTML = getSkeletonHtml(); } // Update active state in sidebar immediately to provide UX feedback updateSidebarActiveState(url); fetch(url) .then(response => { if (!response.ok) throw new Error('Network response was not ok'); return response.text(); }) .then(html => { const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); // Extract and update title const newTitle = doc.querySelector('title'); if (newTitle) { document.title = newTitle.textContent; } // Extract new main-panel const newMain = doc.querySelector('.main-panel'); if (newMain && mainWrapper) { mainWrapper.innerHTML = newMain.innerHTML; // Push history state to URL if (push) { history.pushState({url: url}, '', url); } // Execute inline scripts that come natively with the injected HTML executeScripts(mainWrapper); // Dispatch event so global listeners (like DataTables) can reinitialize document.dispatchEvent(new CustomEvent('spaContentReplaced', { detail: { url: url } })); } else { // If the target page doesn't have the expected structure, fallback to full load window.location.href = url; } }) .catch(err => { console.error('SPA Fetch Error:', err); window.location.href = url; // Fallback to normal navigation on error }); } function executeScripts(container) { // When elements are injected via innerHTML, their