From 71fc76521a539e931a0e3caec806a9b0646523ce Mon Sep 17 00:00:00 2001
From: Starbeamrainbowlabs <sbrl@starbeamrainbowlabs.com>
Date: Sat, 27 Apr 2019 00:38:02 +0100
Subject: [PATCH] Start writing the documentation, but there's a bunch of work
 left still to do.

---
 Client/Router.mjs   |  59 ++++++++++++++++++++++++--------------------
 README.md           |  47 ++++++++++++++++++++++++++++++++++-
 Server/Router.mjs   |  28 ++-------------------
 Shared/Pathspec.mjs |  28 +++++++++++++++++++++
 logo-large.png      | Bin 0 -> 696 bytes
 package-lock.json   |  10 ++++++--
 package.json        |   5 ++--
 7 files changed, 119 insertions(+), 58 deletions(-)
 create mode 100644 Shared/Pathspec.mjs
 create mode 100644 logo-large.png

diff --git a/Client/Router.mjs b/Client/Router.mjs
index 214cc50..dae5540 100644
--- a/Client/Router.mjs
+++ b/Client/Router.mjs
@@ -2,7 +2,18 @@
 
 import EventEmitter from 'event-emitter-es6';
 
+import { pathspec_to_regex } from '../Shared/Pathspec.mjs';
+
+/**
+ * Client-side request router.
+ * @extends EventEmitter
+ */
 class ClientRouter extends EventEmitter {
+	/**
+	 * Creates a new client-side request router.
+	 * You should use this in a browser. If you're not in a browser, you probably want ServerRouter instead.
+	 * @param	{object}	options	The options object to use when creating the router.
+	 */
 	constructor(options) {
 		super();
 		
@@ -30,18 +41,34 @@ class ClientRouter extends EventEmitter {
 		}).bind(this));
 	}
 	
+	/**
+	 * Sets the function to execute when no other route could be matched.
+	 * @param	{Function}	callback The callback to execute.
+	 * @example router.add_404((path) => console.log(`Oops! Couldn't find a route to handle '${path}'.`));
+	 */
 	add_404(callback) {
 		this.callback_404 = callback;
 	}
 	
+	/**
+	 * Adds a route to the router.
+	 * @param	{string|RegExp}	routespec	The route specification that the route should match against. May contain regular expression syntax, and the domain-specific :param syntax. A raw regular expression may also be passed, if you need the flexibility.
+	 * @param	{Function}	callback	The callback to execute when the route is matched.
+	 * @example router.add_page("/add/vegetable/:name/:weight", (params) => console.log(`We added a ${params.name} with a weight of ${params.weight}g.`));
+	 */
 	add_page(routespec, callback) {
 		this.routes.push({
 			spec: routespec,
-			match: this.pathspec_to_regex(routespec),
+			match: pathspec instanceof RegExp ? { regex: routespec, tokens: [] } : pathspec_to_regex(routespec),
 			callback
 		});
 	}
 	
+	/**
+	 * Manually navigate to a given path.
+	 * @param	{string}	path	The path to navigate to.
+	 * @example router.navigate("/add/carrot/frederick/10001");
+	 */
 	navigate(path) {
 		for(let route_info of this.routes) {
 			const matches = path.match(route_info.match.regex);
@@ -71,35 +98,13 @@ class ClientRouter extends EventEmitter {
 			this.callback_404(path);
 	}
 	
+	/**
+	 * Navigate to the current URL's hash value - i.e. the bit after the '#' symbol in the URL.
+	 * @example router.navigate_current_hash();
+	 */
 	navigate_current_hash() {
 		this.navigate(window.location.hash.substr(1));
 	}
-	
-	/**
-	 * Converts a path specification into a regular expression.
-	 * From the server-side sibling of this (client-side) router.
-	 * @param	{string}	pathspec	The path specification to convert.
-	 * @return	{RegExp}	The resulting regular expression
-	 */
-	pathspec_to_regex(pathspec) {
-		if(pathspec == "*") // Support wildcards
-			return { regex: /^/, tokens: [] };
-		
-		let tokens = [];
-		let regex = new RegExp("^" + pathspec.replace(/::?([a-zA-Z0-9\-_]+)/g, (substr/*, index, template (not used)*/) => {
-			tokens.push(substr.replace(/:/g, ""));
-			
-			// FUTURE: We could add optional param support here too
-			if(substr.startsWith("::"))
-				return `(.+)`;
-			else
-				return `([^\/]+)`; 
-		}) + "$", "i");
-		
-		if(this.verbose) console.info("[router/verbose] Created regex", regex);
-		
-		return { regex, tokens };
-	}
 }
 
 export default ClientRouter;
diff --git a/README.md b/README.md
index db28a19..ab8e7c7 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,52 @@
-# powahroot
+# ![](https://raw.githubusercontent.com/sbrl/powahroot/master/logo-large.png)powahroot
+
 > Client and server-side routing micro frameworks
 
+_Powerahroot_ is a pair of micro routing frameworks, presented as an ES6 module:
 
+ - The first is for client-side single-page web applications
+ - The other is for handling server-side Node.js requests
+
+## Getting Started
+Install powahroot as a dependency with npm:
+
+```bash
+npm install --save powahroot
+```
+
+Then `import` the router you're after:
+
+```js
+import { ServerRouter } from 'powahroot';
+```
+
+```js
+import { ClientRouter } from 'powahroot';
+```
+
+## Usage
+
+### Paths
+Powahroot supports multiple syntax bells and whistles when defining routes. These are documented below:
+
+Syntax							| Meaning
+--------------------------------|----------------------------------------
+`/index.html`					| Regular route. Matches exactly what it says on the tin.
+`*`								| Special key(word?) that matches _any_ route. Must be present on its own without any other characters.
+`/add/vegetable/:name/:weight`	| Parameters. Match values an pull them into an object automatically. Does not like forward slashes in parameter values.
+`/images/::path`				| Parameter values with forward slashes. If you want to use parameters, but need values to be able to contain forward slashes `/`, this is for you. Don't forget you can mix-and-match this with the previous example!
+
+
+### Client
+Initialise a new router like this:
+
+```js
+const router = new ClientRouter({
+	// Options object. Default settings:
+	verbose: false, // Whether to be verbose in console.log() messages
+	listen_pushstate: true, // Whether to react to browser pushstate events (excluding those generated by powahroot itself, because that would cause an infinite loop :P)
+});
+```
 
 ## Licence
 Everything in this repository _except_ the logo is licenced under the _Mozilla Public License 2.0.
diff --git a/Server/Router.mjs b/Server/Router.mjs
index 147f12a..bf9cebc 100644
--- a/Server/Router.mjs
+++ b/Server/Router.mjs
@@ -1,6 +1,7 @@
 "use strict";
 
 import RouterContext from './RouterContext.mjs';
+import { pathspec_to_regex } from '../Shared/Pathspec.mjs';
 
 /**
  * A standalone HTTP router that's based on the principle of middleware.
@@ -50,7 +51,7 @@ class Router
 	 * @param	{Function}		action		The action to execute. Will be passed the parameters `context` (Object) and `next` (Function).
 	 */
 	on(methods, pathspec, action) {
-		let regex_info = pathspec instanceof RegExp ? {regex: pathspec, tokens: [] } : this.pathspec_to_regex(pathspec);
+		let regex_info = pathspec instanceof RegExp ? {regex: pathspec, tokens: [] } : pathspec_to_regex(pathspec);
 		
 		// next must be a generator that returns each action in turn
 		this.actions.push(async (context, next) => {
@@ -93,31 +94,6 @@ class Router
 		})
 	}
 	
-	/**
-	 * Converts a path specification into a regular expression.
-	 * @param	{string}	pathspec	The path specification to convert.
-	 * @return	{RegExp}	The resulting regular expression
-	 */
-	pathspec_to_regex(pathspec) {
-		if(pathspec == "*") // Support wildcards
-			return { regex: /^/, tokens: [] };
-		
-		let tokens = [];
-		let regex = new RegExp("^" + pathspec.replace(/::?([a-zA-Z0-9\-_]+)/g, (substr/*, index, template (not actually used)*/) => {
-			tokens.push(substr.replace(/:/g, ""));
-			
-			// FUTURE: We could add optional param support here too
-			if(substr.startsWith("::"))
-				return `(.+)`;
-			else
-				return `([^\/]+)`; 
-		}) + "$", "i");
-		
-		/*if(this.verbose)*/ console.error("[router/verbose] Created regex", regex);
-		
-		return { regex, tokens };
-	}
-	
 	/**
 	 * Handles the specified request.
 	 * @param  {http.ClientRequest} request  The request to handle.
diff --git a/Shared/Pathspec.mjs b/Shared/Pathspec.mjs
new file mode 100644
index 0000000..6aef43c
--- /dev/null
+++ b/Shared/Pathspec.mjs
@@ -0,0 +1,28 @@
+/**
+ * Converts a path specification into a regular expression.
+ * Originally from the server-side sibling of this (client-side) router.
+ * @param	{string}	pathspec	The path specification to convert.
+ * @param	{Boolean}	verbose		Optional. Whether to be verbose and log some stuff to the console. Default: false
+ * @return	{RegExp}	The resulting regular expression
+ */
+function pathspec_to_regex(pathspec, verbose = false) {
+	if(pathspec == "*") // Support wildcards
+		return { regex: /^/, tokens: [] };
+	
+	let tokens = [];
+	let regex = new RegExp("^" + pathspec.replace(/::?([a-zA-Z0-9\-_]+)/g, (substr/*, index, template (not used)*/) => {
+		tokens.push(substr.replace(/:/g, ""));
+		
+		// FUTURE: We could add optional param support here too
+		if(substr.startsWith("::"))
+			return `(.+)`;
+		else
+			return `([^\/]+)`; 
+	}) + "$", "i");
+	
+	if(verbose) console.info("[router/verbose] Created regex", regex);
+	
+	return { regex, tokens };
+}
+
+export { pathspec_to_regex };
diff --git a/logo-large.png b/logo-large.png
new file mode 100644
index 0000000000000000000000000000000000000000..9aaa3a81ed644e3a2673d7014f5e851fb05dd410
GIT binary patch
diff --git a/package-lock.json b/package-lock.json
index 284fb08..ff1e530 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,9 +1,15 @@
 {
-  "name": "powerroot",
-  "version": "1.0.0",
+  "name": "powahroot",
+  "version": "0.1.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
+    "@types/event-emitter-es6": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@types/event-emitter-es6/-/event-emitter-es6-1.1.0.tgz",
+      "integrity": "sha512-A0010GUMSopVYUlDdzhYAD8Z2qGC2hG2H32IHy554NpGEHmsx8M6/lLZRRqxXCxLq28aYrmaZtGwDJjeNXbTDQ==",
+      "dev": true
+    },
     "await-fs": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/await-fs/-/await-fs-1.0.0.tgz",
diff --git a/package.json b/package.json
index f1116eb..19f27ea 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
-  "name": "powerroot",
-  "version": "1.0.0",
+  "name": "powahroot",
+  "version": "0.1.0",
   "description": "Client and server-side routing micro frameworks",
   "main": "index.mjs",
   "scripts": {
@@ -24,6 +24,7 @@
     "nightink": "^0.1.2"
   },
   "devDependencies": {
+    "@types/event-emitter-es6": "^1.1.0",
     "event-emitter-es6": "^1.1.5"
   }
 }
