/** * Minimal hash-based SPA router. * * Route format: { pattern: RegExp, title: string, template: string, onEnter: function(params, container) } * * The first matching route's onEnter is called with parsed params and a container element. * The container is cleared before each navigation. * * Usage: * Router.add(/^\/(.+)\/info$/, 'Server', 'html/serverInfo.html', ServerInfoPage); * Router.add(/^\/(.+)\/player\/(.+)$/, 'Player Info', 'html/playerInfo.html', PlayerInfoPage); * Router.start('#app-view'); */ var Router = (function () { 'use strict'; var routes = []; var viewSelector = null; var currentCleanup = null; function add(pattern, title, template, onEnter) { routes.push({ pattern: pattern, title: title, template: template, onEnter: onEnter }); } function navigate(hash) { // strip leading # and optional / var path = (hash || '').replace(/^#\/?/, '/'); if (!path || path === '/') path = '/home'; for (var i = 0; i < routes.length; i++) { var r = routes[i]; var match = path.match(r.pattern); if (match) { // Store params for later use var container = document.querySelector(viewSelector); if (!container) return; // Cleanup previous page if (currentCleanup && typeof currentCleanup === 'function') { currentCleanup(); currentCleanup = null; } // Clear container container.innerHTML = ''; // Call onEnter with match groups + container if (typeof r.onEnter === 'function') { var result = r.onEnter(match, container); if (typeof result === 'function') { currentCleanup = result; } } return; } } } function start(selector) { viewSelector = selector; window.addEventListener('hashchange', function () { navigate(window.location.hash); }); // Initial navigation if (window.location.hash) { navigate(window.location.hash); } } function go(hash) { window.location.hash = hash; } return { add: add, start: start, go: go, navigate: navigate }; })();