diff --git a/lib/page.js b/lib/page.js index b0481a5..ea1b06c 100644 --- a/lib/page.js +++ b/lib/page.js @@ -70,17 +70,15 @@ export class Page { this._strict = false; this._running = false; this._hashbang = false; + this._decodeURLComponents = true; + this._popstate = true; + this._click = true; + /** @type {!Window|undefined} */ + this._window = (hasWindow ? window : undefined); // bound functions this.clickHandler = this.clickHandler.bind(this); this._onpopstate = this._onpopstate.bind(this); - - /** @type {!Window|undefined} */ - this._window = (hasWindow ? window : undefined); - this._decodeURLComponents = true; - this._popstate = true; - this._click = true; - this._hashbang = false; } /** @@ -170,8 +168,9 @@ export class Page { * - `dispatch` perform initial dispatch [true] * * @param {PageOptions=} options + * @return {!Promise} */ - start(options) { + async start(options) { const opts = options || /** @type {!PageOptions} */ ({}); this.configure(opts); @@ -194,7 +193,7 @@ export class Page { } } - this.replace(url, null, true, opts.dispatch); + await this.replace(url, null, true, opts.dispatch); } /** Unbind click and popstate event handlers. */ @@ -292,9 +291,9 @@ export class Page { * @param {*=} state * @param {boolean=} init * @param {boolean=} dispatch - * @return {!Context} + * @return {!Promise} */ - replace(path, state, init, dispatch) { + async replace(path, state, init, dispatch) { const ctx = new Context(path, state, this); const prev = this.prevContext; this.prevContext = ctx; @@ -302,7 +301,7 @@ export class Page { ctx.init = init; ctx.save(); // save before dispatching, which may redirect if (false !== dispatch) { - this.dispatch(ctx, prev); + await this.dispatch(ctx, prev); } return ctx; } @@ -429,7 +428,7 @@ export class Page { // Also, svg href is an object and its desired value is in .baseVal property let path = svg ? svgAnchor.href.baseVal : (anchor.pathname + anchor.search + (anchor.hash || '')); - path = path[0] !== '/' ? '/' + path : path; + path = path[0] !== '/' ? `/${path}` : path; // strip leading "/[drive letter]:" on NW.js on Windows if (hasProcess && path.match(/^\/[a-zA-Z]:\//)) { @@ -658,7 +657,7 @@ export class Context { const i = path.indexOf('?'); this.canonicalPath = path; - const re = new RegExp('^' + escapeRegExp(pageBase)); + const re = new RegExp(`^${escapeRegExp(pageBase)}`); this.path = path.replace(re, '') || '/'; if (hashbang) { this.path = this.path.replace('#!', '') || '/'; @@ -727,6 +726,10 @@ export class Context { } } + replaceState() { + return this.save(); + } + /** Save the context state. */ save() { const page = this.page; diff --git a/lib/route-tree-node.js b/lib/route-tree-node.js index 2082480..c638c9f 100644 --- a/lib/route-tree-node.js +++ b/lib/route-tree-node.js @@ -282,43 +282,29 @@ class RouteTreeNode { } } - let exitIndex = 0; - let entryIndex = 0; - async function nextExit() { - const currentExitNode = exitNodes[exitIndex++]; - const nextExitNode = exitNodes[exitIndex]; - if (!currentExitNode) { - exitNodes.forEach((node) => { - const value = node.getValue(); - if (value) { - value.element = undefined; - } - }); - - return nextEntry(); - } else if (currentExitNode.getValue().element) { - const routingElem = /** @type {!BasicRoutingInterface} */ (currentExitNode.getValue().element); + // Exit nodes + for (let exitIndex = 0; exitIndex < exitNodes.length; exitIndex++) { + const currentExitNode = exitNodes[exitIndex]; + const nextExitNode = exitNodes[exitIndex + 1]; + const value = currentExitNode.getValue(); + if (value) { + const routingElem = /** @type {!BasicRoutingInterface} */ ( + /** @type {?} */ (currentExitNode.getValue().element) + ); if (!routingElem.routeExit) { throw new Error(`Element '${currentExitNode.getValue().tagName}' does not implement routeExit`); } await routingElem.routeExit(currentExitNode, nextExitNode, routeId, context); - return nextExit(); - } else { - return nextExit(); + value.element = undefined; } } - /** @param {(boolean|void)=} isNotCancelled */ - async function nextEntry(isNotCancelled) { - if (isNotCancelled === false) { - return; - } + // entry nodes + for (let entryIndex = 0; entryIndex < entryNodes.length; entryIndex++) { + const currentEntryNode = entryNodes[entryIndex]; + const nextEntryNode = entryNodes[entryIndex + 1]; - const currentEntryNode = entryNodes[entryIndex++]; - const nextEntryNode = entryNodes[entryIndex]; - if (!currentEntryNode) { - return; - } else if (currentEntryNode.getValue().element) { + if (currentEntryNode.getValue().element) { const routingElem = /** @type {!BasicRoutingInterface} */ ( /** @type {?} */ (currentEntryNode.getValue().element) ); @@ -326,13 +312,11 @@ class RouteTreeNode { throw new Error(`Element '${currentEntryNode.getValue().tagName}' does not implement routeEnter`); } const shouldContinue = await routingElem.routeEnter(currentEntryNode, nextEntryNode, routeId, context); - return nextEntry(shouldContinue); - } else { - return nextEntry(); + if (shouldContinue === false) { + break; + } } } - - await nextExit(); } } diff --git a/router.js b/router.js index caf0afd..9bc352e 100644 --- a/router.js +++ b/router.js @@ -95,15 +95,15 @@ class Router { /** * Build the routing tree and begin routing - * @return {void} + * @return {!Promise} */ - start() { + async start() { this.registerRoutes_(); document.addEventListener('tap', this.page.clickHandler.bind(this.page), false); document.addEventListener('click', this.page.clickHandler.bind(this.page), false); - this.page.start({ + return this.page.start({ click: false, popstate: true, hashbang: false, @@ -129,10 +129,11 @@ class Router { * @param {string} path * @param {Object=} params Values to use for named & query parameters * NOTE: You must quote the properties so that Closure Compiler does not rename them! + * @return {!Promise} */ - redirect(path, params) { + async redirect(path, params) { path = this.url(path, params); - this.page.replace(path); + return this.page.replace(path); } /**