From e10ed4bce896d7d94b266f83ac32a3c3ab94be9a Mon Sep 17 00:00:00 2001
From: rr- <rr-@sakuya.pl>
Date: Sun, 12 Jun 2016 21:57:19 +0200
Subject: [PATCH] client/router: refactor call chains

---
 client/js/main.js   |  7 -----
 client/js/router.js | 75 ++++++++++++++++++---------------------------
 2 files changed, 29 insertions(+), 53 deletions(-)

diff --git a/client/js/main.js b/client/js/main.js
index 103d25b..ceca26b 100644
--- a/client/js/main.js
+++ b/client/js/main.js
@@ -11,19 +11,12 @@ router.Context.prototype.pushState = function() {
     origPushState.call(this);
 };
 
-router.cancel = function(ctx) {
-    prevContext = ctx;
-    ctx.pushState();
-};
-
 router.exit(
     /.*/,
     (ctx, next) => {
         views.unlistenToMessages();
         if (misc.confirmPageExit()) {
             next();
-        } else {
-            router.cancel(ctx);
         }
     });
 
diff --git a/client/js/router.js b/client/js/router.js
index 8473326..5767081 100644
--- a/client/js/router.js
+++ b/client/js/router.js
@@ -3,13 +3,14 @@
 // modified page.js by visionmedia
 // - removed unused crap
 // - refactored to classes
+// - simplified method chains
+// - added ability to call .save() in .exit() without side effects
 
 const pathToRegexp = require('path-to-regexp');
 const clickEvent = document.ontouchstart ? 'touchstart' : 'click';
 let location = window.history.location || window.location;
 
 const base = '';
-let prevContext = null;
 
 function _decodeURLEncodedURIComponent(val) {
     if (typeof val !== 'string') {
@@ -93,7 +94,6 @@ class Router {
     constructor() {
         this._callbacks = [];
         this._exits = [];
-        this._current = '';
     }
 
     enter(path) {
@@ -127,76 +127,59 @@ class Router {
         if (!this._running) {
             return;
         }
-        this._current = '';
         this._running = false;
         document.removeEventListener(clickEvent, this._onClick, false);
         window.removeEventListener('popstate', this._onPopState, false);
     }
 
     show(path, state, push) {
+        const oldPath = this.ctx ? this.ctx.path : ctx.path;
         const ctx = new Context(path, state);
-        this._current = ctx.path;
-        this.dispatch(ctx);
-        if (ctx.handled !== false && push !== false) {
-            ctx.pushState();
-        }
+        this.dispatch(ctx, () => {
+            if (ctx.path !== oldPath && push !== false) {
+                ctx.pushState();
+            }
+        });
         return ctx;
     }
 
     replace(path, state, dispatch) {
         var ctx = new Context(path, state);
-        this._current = ctx.path;
-        ctx.save();
         if (dispatch) {
-            this.dispatch(ctx);
+            this.dispatch(ctx, () => {
+                ctx.save();
+            });
+        } else {
+            ctx.save();
         }
         return ctx;
     }
 
-    dispatch(ctx) {
-        const prev = prevContext;
+    dispatch(ctx, middle) {
+        const swap = (_ctx, next) => {
+            this.ctx = ctx;
+            middle();
+            next();
+        };
+        const callChain = (this.ctx ? this._exits : [])
+            .concat(
+                [swap],
+                this._callbacks,
+                [this._unhandled, (ctx, next) => {}]);
+
         let i = 0;
-        let j = 0;
-
-        prevContext = ctx;
-
-        const nextExit = () => {
-            const fn = this._exits[j++];
-            if (!fn) {
-                return nextEnter();
-            }
-            fn(prev, nextExit);
+        let fn = () => {
+            callChain[i++](this.ctx, fn);
         };
-
-        const nextEnter = () => {
-            const fn = this._callbacks[i++];
-            if (ctx.path !== this._current) {
-                ctx.handled = false;
-                return;
-            }
-            if (!fn) {
-                return this._unhandled(ctx);
-            }
-            fn(ctx, nextEnter);
-        };
-
-        if (prev) {
-            nextExit();
-        } else {
-            nextEnter();
-        }
+        fn();
     }
 
-    _unhandled(ctx) {
-        if (ctx.handled) {
-            return;
-        }
+    _unhandled(ctx, next) {
         let current = location.pathname + location.search;
         if (current === ctx.canonicalPath) {
             return;
         }
         router.stop();
-        ctx.handled = false;
         location.href = ctx.canonicalPath;
     }
 };