/** * GoingsOn - Router Module * URL routing with History API for bookmarking, back button, and deep links. * * URL Structure: * /projects -> Projects list * /project/{id} -> Project dashboard * /tasks -> Tasks list * /events -> Events list * /emails -> Emails list * /day-plan -> Day planning */ (function() { 'use strict'; // Flag to prevent recursive URL pushes when handling popstate let suppressPush = false; // Standard view mappings const VIEW_ROUTES = { 'work': 'tasks', // tab alias 'time': 'day-plan', // tab alias 'messages': 'emails', // tab alias 'projects': 'projects', 'tasks': 'tasks', 'events': 'events', 'emails': 'emails', 'contacts': 'contacts', 'day-plan': 'day-plan', 'weekly-review': 'weekly-review', 'monthly-review': 'monthly-review', }; /** * Navigate to a URL, optionally replacing current history entry. * @param {string} path - The path to navigate to (e.g., '/tasks') * @param {boolean} replace - If true, replace current entry instead of push */ function navigate(path, replace = false) { if (suppressPush) return; const method = replace ? 'replaceState' : 'pushState'; history[method]({ path }, '', path); } /** * Handle a route change (from URL or popstate event). * @param {string} pathname - The URL pathname * @param {string} search - The URL search string (query params) */ function handleRoute(pathname, search) { suppressPush = true; try { // Remove leading slash and get base path const path = pathname.replace(/^\//, '') || 'tasks'; const query = Object.fromEntries(new URLSearchParams(search)); // Project dashboard: /project/{id} if (path.startsWith('project/')) { const id = path.split('/')[1]; if (id && typeof GoingsOn.projects?.open === 'function') { GoingsOn.projects.open(id); } return; } // Standard views const viewName = VIEW_ROUTES[path]; if (viewName) { if (typeof GoingsOn.navigation?.switchView === 'function') { GoingsOn.navigation.switchView(viewName); } return; } // Unknown route - redirect to tasks navigate('/tasks', true); if (typeof GoingsOn.navigation?.switchView === 'function') { GoingsOn.navigation.switchView('tasks'); } } finally { suppressPush = false; } } /** * Initialize the router - handle current URL on page load. */ function init() { handleRoute(location.pathname, location.search); } // Listen for browser back/forward navigation window.addEventListener('popstate', (e) => { handleRoute(location.pathname, location.search); }); // ============ Populate GoingsOn.router Namespace ============ GoingsOn.router = { navigate, init, get suppressPush() { return suppressPush; }, }; })();