From 9bee6456cb05f9e868fa83215c9f6bab3a04a725 Mon Sep 17 00:00:00 2001
From: MattMo <matt@montoyatech.com>
Date: Wed, 19 May 2021 10:28:07 -0700
Subject: [PATCH] RouterMatcher is now Router. Added navigate. Added hash mode.

---
 ignite-html-router.js | 141 ++++++++++++++++++++++++++----------------
 1 file changed, 86 insertions(+), 55 deletions(-)

diff --git a/ignite-html-router.js b/ignite-html-router.js
index 9b23f4e..565eee9 100644
--- a/ignite-html-router.js
+++ b/ignite-html-router.js
@@ -46,11 +46,11 @@ class RouterLink extends IgniteElement {
         var routeMatches = false;
 
         //Check the target first.
-        routeMatches = RouteMatcher.matches(this.target);
+        routeMatches = Router.matches(this.target);
 
         //Check optional routes next.
         for (var i = 0; i < this.routes.length && !routeMatches; i++) {
-            routeMatches = RouteMatcher.matches(this.routes[i]);
+            routeMatches = Router.matches(this.routes[i]);
         }
 
         if (routeMatches && !this.active) {
@@ -62,8 +62,7 @@ class RouterLink extends IgniteElement {
 
     onClick(event) {
         event.preventDefault();
-        window.history.pushState(this.target, this.target, this.target);
-        window.dispatchEvent(new Event("pushstate"));
+        Router.navigate(this.target, false);
     }
 
     cleanup() {
@@ -92,9 +91,9 @@ class RouterView extends IgniteElement {
     }
 
     render() {
-        return this.template.child(
-            new slot(this)
-        ).style("display", this.show, null, (value) => { return value ? null : "none"; });
+        return this.template
+            .child(new slot(this))
+            .style("display", this.show, null, value => value ? null : "none");
     }
 
     ready() {
@@ -107,12 +106,12 @@ class RouterView extends IgniteElement {
         //Based on whether we are strict matching or not check if we have a match.
         if (!this.strict) {
             for (var i = 0; i < this.routes.length && !routeMatches; i++) {
-                routeMatches = RouteMatcher.matches(this.routes[i]);
+                routeMatches = Router.matches(this.routes[i]);
             }
         } else {
             routeMatches = true;
             for (var i = 0; i < this.routes.length && routeMatches; i++) {
-                routeMatches = RouteMatcher.matches(this.routes[i]);
+                routeMatches = Router.matches(this.routes[i]);
             }
         }
 
@@ -130,10 +129,82 @@ class RouterView extends IgniteElement {
     }
 }
 
-class RouteMatcher {
+class RouterLinkTemplate extends IgniteTemplate {
+    /**
+     * Initializes a new router link template.
+     * @param {String} target The target route when the link is clicked.
+     * @param {String|String[]} routes Optional routes that can be used to control the active state of the link.
+     * @param  {...any} elements Elements to render within the link.
+     */
+    constructor(target, routes, ...elements) {
+        super("router-link", elements);
+
+        if (!routes) {
+            routes = [];
+        }
+
+        if (!Array.isArray(routes)) {
+            routes = [routes];
+        }
+
+        this.property("target", target);
+        this.property("routes", routes);
+    }
+}
+
+class RouterViewTemplate extends IgniteTemplate {
+    /**
+     * Initializes a new router view.
+     * @param {String|String[]} routes Single or multiple routes to trigger this view to render. 
+     * @param {any|any[]} elements Elements to render within the view.
+     * @param {Boolean} strict If true all routes must match before this view becomes visible.
+     */
+    constructor(routes, elements, strict = false) {
+        super("router-view", Array.isArray(elements) ? elements : [elements]);
+
+        if (!Array.isArray(routes)) {
+            routes = [routes];
+        }
+
+        this.property("routes", routes);
+        this.property("strict", strict);
+    }
+}
+
+class Router {
+    static navigate(route, refresh = false) {
+        if (refresh) {
+            if (Router.hashMode) {
+                window.location.hash = route;
+                window.location.reload();
+            } else {
+                window.location.href = route;
+            }
+        } else {
+            if (Router.hashMode) {
+                window.location.hash = route;
+            } else {
+                window.history.pushState(route, route, route);
+            }
+
+            window.dispatchEvent(new Event("pushstate"));
+        }
+    }
+
     static matches(route) {
         //Get the path parts from the window
-        var pathParts = window.location.pathname.split("/").splice(1);
+        var pathParts = [];
+
+        //If hash mode is set and we have a hash location, get it and split it.
+        if (Router.hashMode && window.location.hash && window.location.hash.length > 0) {
+            pathParts = window.location.hash.substr(1).split("/");
+            if (pathParts.length > 0 && pathParts[0].length == 0) {
+                pathParts.splice(1);
+            }
+        }
+        else if (!Router.hashMode) {
+            pathParts = window.location.pathname.split("/").splice(1);
+        }
 
         //Get the route parts
         var fromRoot = (route.trim().startsWith("/"));
@@ -214,55 +285,15 @@ class RouteMatcher {
     }
 }
 
-window.RouteMatcher = RouteMatcher;
-
-class RouterLinkTemplate extends IgniteTemplate {
-    /**
-     * Initializes a new router link template.
-     * @param {String} target The target route when the link is clicked.
-     * @param {String|String[]} routes Optional routes that can be used to control the active state of the link.
-     * @param  {...any} elements Elements to render within the link.
-     */
-    constructor(target, routes, ...elements) {
-        super("router-link", elements);
-
-        if (!routes) {
-            routes = [];
-        }
-
-        if (!Array.isArray(routes)) {
-            routes = [routes];
-        }
-
-        this.property("target", target);
-        this.property("routes", routes);
-    }
-}
-
-class RouterViewTemplate extends IgniteTemplate {
-    /**
-     * Initializes a new router view.
-     * @param {String|String[]} routes Single or multiple routes to trigger this view to render. 
-     * @param {any|any[]} elements Elements to render within the view.
-     * @param {Boolean} strict If true all routes must match before this view becomes visible.
-     */
-    constructor(routes, elements, strict = false) {
-        super("router-view", Array.isArray(elements) ? elements : [elements]);
-
-        if (!Array.isArray(routes)) {
-            routes = [routes];
-        }
-
-        this.property("routes", routes);
-        this.property("strict", strict);
-    }
-}
+Router.hashMode = false;
 
 customElements.define("router-link", RouterLink);
 customElements.define("router-view", RouterView);
 
+window.Router = Router;
+
 export {
     RouterLinkTemplate as RouterLink,
     RouterViewTemplate as RouterView,
-    RouteMatcher
+    Router
 }
\ No newline at end of file