UNPKG

93.9 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', { value: true });
4
5var LogManager = require('aurelia-logging');
6var aureliaDependencyInjection = require('aurelia-dependency-injection');
7var aureliaHistory = require('aurelia-history');
8var aureliaRouteRecognizer = require('aurelia-route-recognizer');
9var aureliaEventAggregator = require('aurelia-event-aggregator');
10
11/*! *****************************************************************************
12Copyright (c) Microsoft Corporation. All rights reserved.
13Licensed under the Apache License, Version 2.0 (the "License"); you may not use
14this file except in compliance with the License. You may obtain a copy of the
15License at http://www.apache.org/licenses/LICENSE-2.0
16
17THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
19WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
20MERCHANTABLITY OR NON-INFRINGEMENT.
21
22See the Apache Version 2.0 License for specific language governing permissions
23and limitations under the License.
24***************************************************************************** */
25/* global Reflect, Promise */
26
27var extendStatics = function(d, b) {
28 extendStatics = Object.setPrototypeOf ||
29 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
30 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
31 return extendStatics(d, b);
32};
33
34function __extends(d, b) {
35 extendStatics(d, b);
36 function __() { this.constructor = d; }
37 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
38}
39
40/**
41 * Class used to represent an instruction during a navigation.
42 */
43var NavigationInstruction = /** @class */ (function () {
44 function NavigationInstruction(init) {
45 /**
46 * Current built viewport plan of this nav instruction
47 */
48 this.plan = null;
49 this.options = {};
50 Object.assign(this, init);
51 this.params = this.params || {};
52 this.viewPortInstructions = {};
53 var ancestorParams = [];
54 var current = this;
55 do {
56 var currentParams = Object.assign({}, current.params);
57 if (current.config && current.config.hasChildRouter) {
58 // remove the param for the injected child route segment
59 delete currentParams[current.getWildCardName()];
60 }
61 ancestorParams.unshift(currentParams);
62 current = current.parentInstruction;
63 } while (current);
64 var allParams = Object.assign.apply(Object, [{}, this.queryParams].concat(ancestorParams));
65 this.lifecycleArgs = [allParams, this.config, this];
66 }
67 /**
68 * Gets an array containing this instruction and all child instructions for the current navigation.
69 */
70 NavigationInstruction.prototype.getAllInstructions = function () {
71 var instructions = [this];
72 var viewPortInstructions = this.viewPortInstructions;
73 for (var key in viewPortInstructions) {
74 var childInstruction = viewPortInstructions[key].childNavigationInstruction;
75 if (childInstruction) {
76 instructions.push.apply(instructions, childInstruction.getAllInstructions());
77 }
78 }
79 return instructions;
80 };
81 /**
82 * Gets an array containing the instruction and all child instructions for the previous navigation.
83 * Previous instructions are no longer available after navigation completes.
84 */
85 NavigationInstruction.prototype.getAllPreviousInstructions = function () {
86 return this.getAllInstructions().map(function (c) { return c.previousInstruction; }).filter(function (c) { return c; });
87 };
88 NavigationInstruction.prototype.addViewPortInstruction = function (nameOrInitOptions, strategy, moduleId, component) {
89 var viewPortInstruction;
90 var viewPortName = typeof nameOrInitOptions === 'string' ? nameOrInitOptions : nameOrInitOptions.name;
91 var lifecycleArgs = this.lifecycleArgs;
92 var config = Object.assign({}, lifecycleArgs[1], { currentViewPort: viewPortName });
93 if (typeof nameOrInitOptions === 'string') {
94 viewPortInstruction = {
95 name: nameOrInitOptions,
96 strategy: strategy,
97 moduleId: moduleId,
98 component: component,
99 childRouter: component.childRouter,
100 lifecycleArgs: [lifecycleArgs[0], config, lifecycleArgs[2]]
101 };
102 }
103 else {
104 viewPortInstruction = {
105 name: viewPortName,
106 strategy: nameOrInitOptions.strategy,
107 component: nameOrInitOptions.component,
108 moduleId: nameOrInitOptions.moduleId,
109 childRouter: nameOrInitOptions.component.childRouter,
110 lifecycleArgs: [lifecycleArgs[0], config, lifecycleArgs[2]]
111 };
112 }
113 return this.viewPortInstructions[viewPortName] = viewPortInstruction;
114 };
115 /**
116 * Gets the name of the route pattern's wildcard parameter, if applicable.
117 */
118 NavigationInstruction.prototype.getWildCardName = function () {
119 // todo: potential issue, or at least unsafe typings
120 var configRoute = this.config.route;
121 var wildcardIndex = configRoute.lastIndexOf('*');
122 return configRoute.substr(wildcardIndex + 1);
123 };
124 /**
125 * Gets the path and query string created by filling the route
126 * pattern's wildcard parameter with the matching param.
127 */
128 NavigationInstruction.prototype.getWildcardPath = function () {
129 var wildcardName = this.getWildCardName();
130 var path = this.params[wildcardName] || '';
131 var queryString = this.queryString;
132 if (queryString) {
133 path += '?' + queryString;
134 }
135 return path;
136 };
137 /**
138 * Gets the instruction's base URL, accounting for wildcard route parameters.
139 */
140 NavigationInstruction.prototype.getBaseUrl = function () {
141 var _this = this;
142 var $encodeURI = encodeURI;
143 var fragment = decodeURI(this.fragment);
144 if (fragment === '') {
145 var nonEmptyRoute = this.router.routes.find(function (route) {
146 return route.name === _this.config.name &&
147 route.route !== '';
148 });
149 if (nonEmptyRoute) {
150 fragment = nonEmptyRoute.route;
151 }
152 }
153 if (!this.params) {
154 return $encodeURI(fragment);
155 }
156 var wildcardName = this.getWildCardName();
157 var path = this.params[wildcardName] || '';
158 if (!path) {
159 return $encodeURI(fragment);
160 }
161 return $encodeURI(fragment.substr(0, fragment.lastIndexOf(path)));
162 };
163 /**
164 * Finalize a viewport instruction
165 * @internal
166 */
167 NavigationInstruction.prototype._commitChanges = function (waitToSwap) {
168 var _this = this;
169 var router = this.router;
170 router.currentInstruction = this;
171 var previousInstruction = this.previousInstruction;
172 if (previousInstruction) {
173 previousInstruction.config.navModel.isActive = false;
174 }
175 this.config.navModel.isActive = true;
176 router.refreshNavigation();
177 var loads = [];
178 var delaySwaps = [];
179 var viewPortInstructions = this.viewPortInstructions;
180 var _loop_1 = function (viewPortName) {
181 var viewPortInstruction = viewPortInstructions[viewPortName];
182 var viewPort = router.viewPorts[viewPortName];
183 if (!viewPort) {
184 throw new Error("There was no router-view found in the view for " + viewPortInstruction.moduleId + ".");
185 }
186 var childNavInstruction = viewPortInstruction.childNavigationInstruction;
187 if (viewPortInstruction.strategy === "replace" /* Replace */) {
188 if (childNavInstruction && childNavInstruction.parentCatchHandler) {
189 loads.push(childNavInstruction._commitChanges(waitToSwap));
190 }
191 else {
192 if (waitToSwap) {
193 delaySwaps.push({ viewPort: viewPort, viewPortInstruction: viewPortInstruction });
194 }
195 loads.push(viewPort
196 .process(viewPortInstruction, waitToSwap)
197 .then(function () { return childNavInstruction
198 ? childNavInstruction._commitChanges(waitToSwap)
199 : Promise.resolve(); }));
200 }
201 }
202 else {
203 if (childNavInstruction) {
204 loads.push(childNavInstruction._commitChanges(waitToSwap));
205 }
206 }
207 };
208 for (var viewPortName in viewPortInstructions) {
209 _loop_1(viewPortName);
210 }
211 return Promise
212 .all(loads)
213 .then(function () {
214 delaySwaps.forEach(function (x) { return x.viewPort.swap(x.viewPortInstruction); });
215 return null;
216 })
217 .then(function () { return prune(_this); });
218 };
219 /**@internal */
220 NavigationInstruction.prototype._updateTitle = function () {
221 var router = this.router;
222 var title = this._buildTitle(router.titleSeparator);
223 if (title) {
224 router.history.setTitle(title);
225 }
226 };
227 /**@internal */
228 NavigationInstruction.prototype._buildTitle = function (separator) {
229 if (separator === void 0) { separator = ' | '; }
230 var title = '';
231 var childTitles = [];
232 var navModelTitle = this.config.navModel.title;
233 var instructionRouter = this.router;
234 var viewPortInstructions = this.viewPortInstructions;
235 if (navModelTitle) {
236 title = instructionRouter.transformTitle(navModelTitle);
237 }
238 for (var viewPortName in viewPortInstructions) {
239 var viewPortInstruction = viewPortInstructions[viewPortName];
240 var child_nav_instruction = viewPortInstruction.childNavigationInstruction;
241 if (child_nav_instruction) {
242 var childTitle = child_nav_instruction._buildTitle(separator);
243 if (childTitle) {
244 childTitles.push(childTitle);
245 }
246 }
247 }
248 if (childTitles.length) {
249 title = childTitles.join(separator) + (title ? separator : '') + title;
250 }
251 if (instructionRouter.title) {
252 title += (title ? separator : '') + instructionRouter.transformTitle(instructionRouter.title);
253 }
254 return title;
255 };
256 return NavigationInstruction;
257}());
258var prune = function (instruction) {
259 instruction.previousInstruction = null;
260 instruction.plan = null;
261};
262
263/**
264* Class for storing and interacting with a route's navigation settings.
265*/
266var NavModel = /** @class */ (function () {
267 function NavModel(router, relativeHref) {
268 /**
269 * True if this nav item is currently active.
270 */
271 this.isActive = false;
272 /**
273 * The title.
274 */
275 this.title = null;
276 /**
277 * This nav item's absolute href.
278 */
279 this.href = null;
280 /**
281 * This nav item's relative href.
282 */
283 this.relativeHref = null;
284 /**
285 * Data attached to the route at configuration time.
286 */
287 this.settings = {};
288 /**
289 * The route config.
290 */
291 this.config = null;
292 this.router = router;
293 this.relativeHref = relativeHref;
294 }
295 /**
296 * Sets the route's title and updates document.title.
297 * If the a navigation is in progress, the change will be applied
298 * to document.title when the navigation completes.
299 *
300 * @param title The new title.
301 */
302 NavModel.prototype.setTitle = function (title) {
303 this.title = title;
304 if (this.isActive) {
305 this.router.updateTitle();
306 }
307 };
308 return NavModel;
309}());
310
311function _normalizeAbsolutePath(path, hasPushState, absolute) {
312 if (absolute === void 0) { absolute = false; }
313 if (!hasPushState && path[0] !== '#') {
314 path = '#' + path;
315 }
316 if (hasPushState && absolute) {
317 path = path.substring(1, path.length);
318 }
319 return path;
320}
321function _createRootedPath(fragment, baseUrl, hasPushState, absolute) {
322 if (isAbsoluteUrl.test(fragment)) {
323 return fragment;
324 }
325 var path = '';
326 if (baseUrl.length && baseUrl[0] !== '/') {
327 path += '/';
328 }
329 path += baseUrl;
330 if ((!path.length || path[path.length - 1] !== '/') && fragment[0] !== '/') {
331 path += '/';
332 }
333 if (path.length && path[path.length - 1] === '/' && fragment[0] === '/') {
334 path = path.substring(0, path.length - 1);
335 }
336 return _normalizeAbsolutePath(path + fragment, hasPushState, absolute);
337}
338function _resolveUrl(fragment, baseUrl, hasPushState) {
339 if (isRootedPath.test(fragment)) {
340 return _normalizeAbsolutePath(fragment, hasPushState);
341 }
342 return _createRootedPath(fragment, baseUrl, hasPushState);
343}
344function _ensureArrayWithSingleRoutePerConfig(config) {
345 var routeConfigs = [];
346 if (Array.isArray(config.route)) {
347 for (var i = 0, ii = config.route.length; i < ii; ++i) {
348 var current = Object.assign({}, config);
349 current.route = config.route[i];
350 routeConfigs.push(current);
351 }
352 }
353 else {
354 routeConfigs.push(Object.assign({}, config));
355 }
356 return routeConfigs;
357}
358var isRootedPath = /^#?\//;
359var isAbsoluteUrl = /^([a-z][a-z0-9+\-.]*:)?\/\//i;
360
361/**
362 * Class used to configure a [[Router]] instance.
363 *
364 * @constructor
365 */
366var RouterConfiguration = /** @class */ (function () {
367 function RouterConfiguration() {
368 this.instructions = [];
369 this.options = {};
370 this.pipelineSteps = [];
371 }
372 /**
373 * Adds a step to be run during the [[Router]]'s navigation pipeline.
374 *
375 * @param name The name of the pipeline slot to insert the step into.
376 * @param step The pipeline step.
377 * @chainable
378 */
379 RouterConfiguration.prototype.addPipelineStep = function (name, step) {
380 if (step === null || step === undefined) {
381 throw new Error('Pipeline step cannot be null or undefined.');
382 }
383 this.pipelineSteps.push({ name: name, step: step });
384 return this;
385 };
386 /**
387 * Adds a step to be run during the [[Router]]'s authorize pipeline slot.
388 *
389 * @param step The pipeline step.
390 * @chainable
391 */
392 RouterConfiguration.prototype.addAuthorizeStep = function (step) {
393 return this.addPipelineStep("authorize" /* Authorize */, step);
394 };
395 /**
396 * Adds a step to be run during the [[Router]]'s preActivate pipeline slot.
397 *
398 * @param step The pipeline step.
399 * @chainable
400 */
401 RouterConfiguration.prototype.addPreActivateStep = function (step) {
402 return this.addPipelineStep("preActivate" /* PreActivate */, step);
403 };
404 /**
405 * Adds a step to be run during the [[Router]]'s preRender pipeline slot.
406 *
407 * @param step The pipeline step.
408 * @chainable
409 */
410 RouterConfiguration.prototype.addPreRenderStep = function (step) {
411 return this.addPipelineStep("preRender" /* PreRender */, step);
412 };
413 /**
414 * Adds a step to be run during the [[Router]]'s postRender pipeline slot.
415 *
416 * @param step The pipeline step.
417 * @chainable
418 */
419 RouterConfiguration.prototype.addPostRenderStep = function (step) {
420 return this.addPipelineStep("postRender" /* PostRender */, step);
421 };
422 /**
423 * Configures a route that will be used if there is no previous location available on navigation cancellation.
424 *
425 * @param fragment The URL fragment to use as the navigation destination.
426 * @chainable
427 */
428 RouterConfiguration.prototype.fallbackRoute = function (fragment) {
429 this._fallbackRoute = fragment;
430 return this;
431 };
432 /**
433 * Maps one or more routes to be registered with the router.
434 *
435 * @param route The [[RouteConfig]] to map, or an array of [[RouteConfig]] to map.
436 * @chainable
437 */
438 RouterConfiguration.prototype.map = function (route) {
439 var _this = this;
440 if (Array.isArray(route)) {
441 route.forEach(function (r) { return _this.map(r); });
442 return this;
443 }
444 return this.mapRoute(route);
445 };
446 /**
447 * Configures defaults to use for any view ports.
448 *
449 * @param viewPortConfig a view port configuration object to use as a
450 * default, of the form { viewPortName: { moduleId } }.
451 * @chainable
452 */
453 RouterConfiguration.prototype.useViewPortDefaults = function (viewPortConfig) {
454 this.viewPortDefaults = viewPortConfig;
455 return this;
456 };
457 /**
458 * Maps a single route to be registered with the router.
459 *
460 * @param route The [[RouteConfig]] to map.
461 * @chainable
462 */
463 RouterConfiguration.prototype.mapRoute = function (config) {
464 this.instructions.push(function (router) {
465 var routeConfigs = _ensureArrayWithSingleRoutePerConfig(config);
466 var navModel;
467 for (var i = 0, ii = routeConfigs.length; i < ii; ++i) {
468 var routeConfig = routeConfigs[i];
469 routeConfig.settings = routeConfig.settings || {};
470 if (!navModel) {
471 navModel = router.createNavModel(routeConfig);
472 }
473 router.addRoute(routeConfig, navModel);
474 }
475 });
476 return this;
477 };
478 /**
479 * Registers an unknown route handler to be run when the URL fragment doesn't match any registered routes.
480 *
481 * @param config A string containing a moduleId to load, or a [[RouteConfig]], or a function that takes the
482 * [[NavigationInstruction]] and selects a moduleId to load.
483 * @chainable
484 */
485 RouterConfiguration.prototype.mapUnknownRoutes = function (config) {
486 this.unknownRouteConfig = config;
487 return this;
488 };
489 /**
490 * Applies the current configuration to the specified [[Router]].
491 *
492 * @param router The [[Router]] to apply the configuration to.
493 */
494 RouterConfiguration.prototype.exportToRouter = function (router) {
495 var instructions = this.instructions;
496 for (var i = 0, ii = instructions.length; i < ii; ++i) {
497 instructions[i](router);
498 }
499 var _a = this, title = _a.title, titleSeparator = _a.titleSeparator, unknownRouteConfig = _a.unknownRouteConfig, _fallbackRoute = _a._fallbackRoute, viewPortDefaults = _a.viewPortDefaults;
500 if (title) {
501 router.title = title;
502 }
503 if (titleSeparator) {
504 router.titleSeparator = titleSeparator;
505 }
506 if (unknownRouteConfig) {
507 router.handleUnknownRoutes(unknownRouteConfig);
508 }
509 if (_fallbackRoute) {
510 router.fallbackRoute = _fallbackRoute;
511 }
512 if (viewPortDefaults) {
513 router.useViewPortDefaults(viewPortDefaults);
514 }
515 Object.assign(router.options, this.options);
516 var pipelineSteps = this.pipelineSteps;
517 var pipelineStepCount = pipelineSteps.length;
518 if (pipelineStepCount) {
519 if (!router.isRoot) {
520 throw new Error('Pipeline steps can only be added to the root router');
521 }
522 var pipelineProvider = router.pipelineProvider;
523 for (var i = 0, ii = pipelineStepCount; i < ii; ++i) {
524 var _b = pipelineSteps[i], name_1 = _b.name, step = _b.step;
525 pipelineProvider.addStep(name_1, step);
526 }
527 }
528 };
529 return RouterConfiguration;
530}());
531
532/**
533 * The primary class responsible for handling routing and navigation.
534 */
535var Router = /** @class */ (function () {
536 /**
537 * @param container The [[Container]] to use when child routers.
538 * @param history The [[History]] implementation to delegate navigation requests to.
539 */
540 function Router(container, history) {
541 var _this = this;
542 /**
543 * The parent router, or null if this instance is not a child router.
544 */
545 this.parent = null;
546 this.options = {};
547 /**
548 * The defaults used when a viewport lacks specified content
549 */
550 this.viewPortDefaults = {};
551 /**
552 * Extension point to transform the document title before it is built and displayed.
553 * By default, child routers delegate to the parent router, and the app router
554 * returns the title unchanged.
555 */
556 this.transformTitle = function (title) {
557 if (_this.parent) {
558 return _this.parent.transformTitle(title);
559 }
560 return title;
561 };
562 this.container = container;
563 this.history = history;
564 this.reset();
565 }
566 /**
567 * Fully resets the router's internal state. Primarily used internally by the framework when multiple calls to setRoot are made.
568 * Use with caution (actually, avoid using this). Do not use this to simply change your navigation model.
569 */
570 Router.prototype.reset = function () {
571 var _this = this;
572 this.viewPorts = {};
573 this.routes = [];
574 this.baseUrl = '';
575 this.isConfigured = false;
576 this.isNavigating = false;
577 this.isExplicitNavigation = false;
578 this.isExplicitNavigationBack = false;
579 this.isNavigatingFirst = false;
580 this.isNavigatingNew = false;
581 this.isNavigatingRefresh = false;
582 this.isNavigatingForward = false;
583 this.isNavigatingBack = false;
584 this.couldDeactivate = false;
585 this.navigation = [];
586 this.currentInstruction = null;
587 this.viewPortDefaults = {};
588 this._fallbackOrder = 100;
589 this._recognizer = new aureliaRouteRecognizer.RouteRecognizer();
590 this._childRecognizer = new aureliaRouteRecognizer.RouteRecognizer();
591 this._configuredPromise = new Promise(function (resolve) {
592 _this._resolveConfiguredPromise = resolve;
593 });
594 };
595 Object.defineProperty(Router.prototype, "isRoot", {
596 /**
597 * Gets a value indicating whether or not this [[Router]] is the root in the router tree. I.e., it has no parent.
598 */
599 get: function () {
600 return !this.parent;
601 },
602 enumerable: true,
603 configurable: true
604 });
605 /**
606 * Registers a viewPort to be used as a rendering target for activated routes.
607 *
608 * @param viewPort The viewPort.
609 * @param name The name of the viewPort. 'default' if unspecified.
610 */
611 Router.prototype.registerViewPort = function (viewPort, name) {
612 name = name || 'default';
613 this.viewPorts[name] = viewPort;
614 };
615 /**
616 * Returns a Promise that resolves when the router is configured.
617 */
618 Router.prototype.ensureConfigured = function () {
619 return this._configuredPromise;
620 };
621 /**
622 * Configures the router.
623 *
624 * @param callbackOrConfig The [[RouterConfiguration]] or a callback that takes a [[RouterConfiguration]].
625 */
626 Router.prototype.configure = function (callbackOrConfig) {
627 var _this = this;
628 this.isConfigured = true;
629 var result = callbackOrConfig;
630 var config;
631 if (typeof callbackOrConfig === 'function') {
632 config = new RouterConfiguration();
633 result = callbackOrConfig(config);
634 }
635 return Promise
636 .resolve(result)
637 .then(function (c) {
638 if (c && c.exportToRouter) {
639 config = c;
640 }
641 config.exportToRouter(_this);
642 _this.isConfigured = true;
643 _this._resolveConfiguredPromise();
644 });
645 };
646 /**
647 * Navigates to a new location.
648 *
649 * @param fragment The URL fragment to use as the navigation destination.
650 * @param options The navigation options.
651 */
652 Router.prototype.navigate = function (fragment, options) {
653 if (!this.isConfigured && this.parent) {
654 return this.parent.navigate(fragment, options);
655 }
656 this.isExplicitNavigation = true;
657 return this.history.navigate(_resolveUrl(fragment, this.baseUrl, this.history._hasPushState), options);
658 };
659 /**
660 * Navigates to a new location corresponding to the route and params specified. Equivallent to [[Router.generate]] followed
661 * by [[Router.navigate]].
662 *
663 * @param route The name of the route to use when generating the navigation location.
664 * @param params The route parameters to be used when populating the route pattern.
665 * @param options The navigation options.
666 */
667 Router.prototype.navigateToRoute = function (route, params, options) {
668 var path = this.generate(route, params);
669 return this.navigate(path, options);
670 };
671 /**
672 * Navigates back to the most recent location in history.
673 */
674 Router.prototype.navigateBack = function () {
675 this.isExplicitNavigationBack = true;
676 this.history.navigateBack();
677 };
678 /**
679 * Creates a child router of the current router.
680 *
681 * @param container The [[Container]] to provide to the child router. Uses the current [[Router]]'s [[Container]] if unspecified.
682 * @returns {Router} The new child Router.
683 */
684 Router.prototype.createChild = function (container) {
685 var childRouter = new Router(container || this.container.createChild(), this.history);
686 childRouter.parent = this;
687 return childRouter;
688 };
689 /**
690 * Generates a URL fragment matching the specified route pattern.
691 *
692 * @param name The name of the route whose pattern should be used to generate the fragment.
693 * @param params The route params to be used to populate the route pattern.
694 * @param options If options.absolute = true, then absolute url will be generated; otherwise, it will be relative url.
695 * @returns {string} A string containing the generated URL fragment.
696 */
697 Router.prototype.generate = function (nameOrRoute, params, options) {
698 if (params === void 0) { params = {}; }
699 if (options === void 0) { options = {}; }
700 // A child recognizer generates routes for potential child routes. Any potential child route is added
701 // to the childRoute property of params for the childRouter to recognize. When generating routes, we
702 // use the childRecognizer when childRoute params are available to generate a child router enabled route.
703 var recognizer = 'childRoute' in params ? this._childRecognizer : this._recognizer;
704 var hasRoute = recognizer.hasRoute(nameOrRoute);
705 if (!hasRoute) {
706 if (this.parent) {
707 return this.parent.generate(nameOrRoute, params, options);
708 }
709 throw new Error("A route with name '" + nameOrRoute + "' could not be found. Check that `name: '" + nameOrRoute + "'` was specified in the route's config.");
710 }
711 var path = recognizer.generate(nameOrRoute, params);
712 var rootedPath = _createRootedPath(path, this.baseUrl, this.history._hasPushState, options.absolute);
713 return options.absolute ? "" + this.history.getAbsoluteRoot() + rootedPath : rootedPath;
714 };
715 /**
716 * Creates a [[NavModel]] for the specified route config.
717 *
718 * @param config The route config.
719 */
720 Router.prototype.createNavModel = function (config) {
721 var navModel = new NavModel(this, 'href' in config
722 ? config.href
723 // potential error when config.route is a string[] ?
724 : config.route);
725 navModel.title = config.title;
726 navModel.order = config.nav;
727 navModel.href = config.href;
728 navModel.settings = config.settings;
729 navModel.config = config;
730 return navModel;
731 };
732 /**
733 * Registers a new route with the router.
734 *
735 * @param config The [[RouteConfig]].
736 * @param navModel The [[NavModel]] to use for the route. May be omitted for single-pattern routes.
737 */
738 Router.prototype.addRoute = function (config, navModel) {
739 if (Array.isArray(config.route)) {
740 var routeConfigs = _ensureArrayWithSingleRoutePerConfig(config);
741 // the following is wrong. todo: fix this after TS refactoring release
742 routeConfigs.forEach(this.addRoute.bind(this));
743 return;
744 }
745 validateRouteConfig(config);
746 if (!('viewPorts' in config) && !config.navigationStrategy) {
747 config.viewPorts = {
748 'default': {
749 moduleId: config.moduleId,
750 view: config.view
751 }
752 };
753 }
754 if (!navModel) {
755 navModel = this.createNavModel(config);
756 }
757 this.routes.push(config);
758 var path = config.route;
759 if (path.charAt(0) === '/') {
760 path = path.substr(1);
761 }
762 var caseSensitive = config.caseSensitive === true;
763 var state = this._recognizer.add({
764 path: path,
765 handler: config,
766 caseSensitive: caseSensitive
767 });
768 if (path) {
769 var settings = config.settings;
770 delete config.settings;
771 var withChild = JSON.parse(JSON.stringify(config));
772 config.settings = settings;
773 withChild.route = path + "/*childRoute";
774 withChild.hasChildRouter = true;
775 this._childRecognizer.add({
776 path: withChild.route,
777 handler: withChild,
778 caseSensitive: caseSensitive
779 });
780 withChild.navModel = navModel;
781 withChild.settings = config.settings;
782 withChild.navigationStrategy = config.navigationStrategy;
783 }
784 config.navModel = navModel;
785 var navigation = this.navigation;
786 if ((navModel.order || navModel.order === 0) && navigation.indexOf(navModel) === -1) {
787 if ((!navModel.href && navModel.href !== '') && (state.types.dynamics || state.types.stars)) {
788 throw new Error('Invalid route config for "' + config.route + '" : dynamic routes must specify an "href:" to be included in the navigation model.');
789 }
790 if (typeof navModel.order !== 'number') {
791 navModel.order = ++this._fallbackOrder;
792 }
793 navigation.push(navModel);
794 // this is a potential error / inconsistency between browsers
795 //
796 // MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort
797 // If compareFunction(a, b) returns 0, leave a and b unchanged with respect to each other,
798 // but sorted with respect to all different elements.
799 // Note: the ECMAscript standard does not guarantee this behaviour,
800 // and thus not all browsers (e.g. Mozilla versions dating back to at least 2003) respect this.
801 navigation.sort(function (a, b) { return a.order - b.order; });
802 }
803 };
804 /**
805 * Gets a value indicating whether or not this [[Router]] or one of its ancestors has a route registered with the specified name.
806 *
807 * @param name The name of the route to check.
808 */
809 Router.prototype.hasRoute = function (name) {
810 return !!(this._recognizer.hasRoute(name) || this.parent && this.parent.hasRoute(name));
811 };
812 /**
813 * Gets a value indicating whether or not this [[Router]] has a route registered with the specified name.
814 *
815 * @param name The name of the route to check.
816 */
817 Router.prototype.hasOwnRoute = function (name) {
818 return this._recognizer.hasRoute(name);
819 };
820 /**
821 * Register a handler to use when the incoming URL fragment doesn't match any registered routes.
822 *
823 * @param config The moduleId, or a function that selects the moduleId, or a [[RouteConfig]].
824 */
825 Router.prototype.handleUnknownRoutes = function (config) {
826 var _this = this;
827 if (!config) {
828 throw new Error('Invalid unknown route handler');
829 }
830 this.catchAllHandler = function (instruction) {
831 return _this
832 ._createRouteConfig(config, instruction)
833 .then(function (c) {
834 instruction.config = c;
835 return instruction;
836 });
837 };
838 };
839 /**
840 * Updates the document title using the current navigation instruction.
841 */
842 Router.prototype.updateTitle = function () {
843 var parentRouter = this.parent;
844 if (parentRouter) {
845 return parentRouter.updateTitle();
846 }
847 var currentInstruction = this.currentInstruction;
848 if (currentInstruction) {
849 currentInstruction._updateTitle();
850 }
851 return undefined;
852 };
853 /**
854 * Updates the navigation routes with hrefs relative to the current location.
855 * Note: This method will likely move to a plugin in a future release.
856 */
857 Router.prototype.refreshNavigation = function () {
858 var nav = this.navigation;
859 for (var i = 0, length_1 = nav.length; i < length_1; i++) {
860 var current = nav[i];
861 if (!current.config.href) {
862 current.href = _createRootedPath(current.relativeHref, this.baseUrl, this.history._hasPushState);
863 }
864 else {
865 current.href = _normalizeAbsolutePath(current.config.href, this.history._hasPushState);
866 }
867 }
868 };
869 /**
870 * Sets the default configuration for the view ports. This specifies how to
871 * populate a view port for which no module is specified. The default is
872 * an empty view/view-model pair.
873 */
874 Router.prototype.useViewPortDefaults = function ($viewPortDefaults) {
875 // a workaround to have strong typings while not requiring to expose interface ViewPortInstruction
876 var viewPortDefaults = $viewPortDefaults;
877 for (var viewPortName in viewPortDefaults) {
878 var viewPortConfig = viewPortDefaults[viewPortName];
879 this.viewPortDefaults[viewPortName] = {
880 moduleId: viewPortConfig.moduleId
881 };
882 }
883 };
884 /**@internal */
885 Router.prototype._refreshBaseUrl = function () {
886 var parentRouter = this.parent;
887 if (parentRouter) {
888 this.baseUrl = generateBaseUrl(parentRouter, parentRouter.currentInstruction);
889 }
890 };
891 /**@internal */
892 Router.prototype._createNavigationInstruction = function (url, parentInstruction) {
893 if (url === void 0) { url = ''; }
894 if (parentInstruction === void 0) { parentInstruction = null; }
895 var fragment = url;
896 var queryString = '';
897 var queryIndex = url.indexOf('?');
898 if (queryIndex !== -1) {
899 fragment = url.substr(0, queryIndex);
900 queryString = url.substr(queryIndex + 1);
901 }
902 var urlRecognizationResults = this._recognizer.recognize(url);
903 if (!urlRecognizationResults || !urlRecognizationResults.length) {
904 urlRecognizationResults = this._childRecognizer.recognize(url);
905 }
906 var instructionInit = {
907 fragment: fragment,
908 queryString: queryString,
909 config: null,
910 parentInstruction: parentInstruction,
911 previousInstruction: this.currentInstruction,
912 router: this,
913 options: {
914 compareQueryParams: this.options.compareQueryParams
915 }
916 };
917 var result;
918 if (urlRecognizationResults && urlRecognizationResults.length) {
919 var first = urlRecognizationResults[0];
920 var instruction = new NavigationInstruction(Object.assign({}, instructionInit, {
921 params: first.params,
922 queryParams: first.queryParams || urlRecognizationResults.queryParams,
923 config: first.config || first.handler
924 }));
925 if (typeof first.handler === 'function') {
926 result = evaluateNavigationStrategy(instruction, first.handler, first);
927 }
928 else if (first.handler && typeof first.handler.navigationStrategy === 'function') {
929 result = evaluateNavigationStrategy(instruction, first.handler.navigationStrategy, first.handler);
930 }
931 else {
932 result = Promise.resolve(instruction);
933 }
934 }
935 else if (this.catchAllHandler) {
936 var instruction = new NavigationInstruction(Object.assign({}, instructionInit, {
937 params: { path: fragment },
938 queryParams: urlRecognizationResults ? urlRecognizationResults.queryParams : {},
939 config: null // config will be created by the catchAllHandler
940 }));
941 result = evaluateNavigationStrategy(instruction, this.catchAllHandler);
942 }
943 else if (this.parent) {
944 var router = this._parentCatchAllHandler(this.parent);
945 if (router) {
946 var newParentInstruction = this._findParentInstructionFromRouter(router, parentInstruction);
947 var instruction = new NavigationInstruction(Object.assign({}, instructionInit, {
948 params: { path: fragment },
949 queryParams: urlRecognizationResults ? urlRecognizationResults.queryParams : {},
950 router: router,
951 parentInstruction: newParentInstruction,
952 parentCatchHandler: true,
953 config: null // config will be created by the chained parent catchAllHandler
954 }));
955 result = evaluateNavigationStrategy(instruction, router.catchAllHandler);
956 }
957 }
958 if (result && parentInstruction) {
959 this.baseUrl = generateBaseUrl(this.parent, parentInstruction);
960 }
961 return result || Promise.reject(new Error("Route not found: " + url));
962 };
963 /**@internal */
964 Router.prototype._findParentInstructionFromRouter = function (router, instruction) {
965 if (instruction.router === router) {
966 instruction.fragment = router.baseUrl; // need to change the fragment in case of a redirect instead of moduleId
967 return instruction;
968 }
969 else if (instruction.parentInstruction) {
970 return this._findParentInstructionFromRouter(router, instruction.parentInstruction);
971 }
972 return undefined;
973 };
974 /**@internal */
975 Router.prototype._parentCatchAllHandler = function (router) {
976 if (router.catchAllHandler) {
977 return router;
978 }
979 else if (router.parent) {
980 return this._parentCatchAllHandler(router.parent);
981 }
982 return false;
983 };
984 /**
985 * @internal
986 */
987 Router.prototype._createRouteConfig = function (config, instruction) {
988 var _this = this;
989 return Promise
990 .resolve(config)
991 .then(function (c) {
992 if (typeof c === 'string') {
993 return { moduleId: c };
994 }
995 else if (typeof c === 'function') {
996 return c(instruction);
997 }
998 return c;
999 })
1000 // typing here could be either RouteConfig or RedirectConfig
1001 // but temporarily treat both as RouteConfig
1002 // todo: improve typings precision
1003 .then(function (c) { return typeof c === 'string' ? { moduleId: c } : c; })
1004 .then(function (c) {
1005 c.route = instruction.params.path;
1006 validateRouteConfig(c);
1007 if (!c.navModel) {
1008 c.navModel = _this.createNavModel(c);
1009 }
1010 return c;
1011 });
1012 };
1013 return Router;
1014}());
1015/* @internal exported for unit testing */
1016var generateBaseUrl = function (router, instruction) {
1017 return "" + (router.baseUrl || '') + (instruction.getBaseUrl() || '');
1018};
1019/* @internal exported for unit testing */
1020var validateRouteConfig = function (config) {
1021 if (typeof config !== 'object') {
1022 throw new Error('Invalid Route Config');
1023 }
1024 if (typeof config.route !== 'string') {
1025 var name_1 = config.name || '(no name)';
1026 throw new Error('Invalid Route Config for "' + name_1 + '": You must specify a "route:" pattern.');
1027 }
1028 if (!('redirect' in config || config.moduleId || config.navigationStrategy || config.viewPorts)) {
1029 throw new Error('Invalid Route Config for "' + config.route + '": You must specify a "moduleId:", "redirect:", "navigationStrategy:", or "viewPorts:".');
1030 }
1031};
1032/* @internal exported for unit testing */
1033var evaluateNavigationStrategy = function (instruction, evaluator, context) {
1034 return Promise
1035 .resolve(evaluator.call(context, instruction))
1036 .then(function () {
1037 if (!('viewPorts' in instruction.config)) {
1038 instruction.config.viewPorts = {
1039 'default': {
1040 moduleId: instruction.config.moduleId
1041 }
1042 };
1043 }
1044 return instruction;
1045 });
1046};
1047
1048/**@internal exported for unit testing */
1049var createNextFn = function (instruction, steps) {
1050 var index = -1;
1051 var next = function () {
1052 index++;
1053 if (index < steps.length) {
1054 var currentStep = steps[index];
1055 try {
1056 return currentStep(instruction, next);
1057 }
1058 catch (e) {
1059 return next.reject(e);
1060 }
1061 }
1062 else {
1063 return next.complete();
1064 }
1065 };
1066 next.complete = createCompletionHandler(next, "completed" /* Completed */);
1067 next.cancel = createCompletionHandler(next, "canceled" /* Canceled */);
1068 next.reject = createCompletionHandler(next, "rejected" /* Rejected */);
1069 return next;
1070};
1071/**@internal exported for unit testing */
1072var createCompletionHandler = function (next, status) {
1073 return function (output) { return Promise
1074 .resolve({
1075 status: status,
1076 output: output,
1077 completed: status === "completed" /* Completed */
1078 }); };
1079};
1080
1081/**
1082 * The class responsible for managing and processing the navigation pipeline.
1083 */
1084var Pipeline = /** @class */ (function () {
1085 function Pipeline() {
1086 /**
1087 * The pipeline steps. And steps added via addStep will be converted to a function
1088 * The actualy running functions with correct step contexts of this pipeline
1089 */
1090 this.steps = [];
1091 }
1092 /**
1093 * Adds a step to the pipeline.
1094 *
1095 * @param step The pipeline step.
1096 */
1097 Pipeline.prototype.addStep = function (step) {
1098 var run;
1099 if (typeof step === 'function') {
1100 run = step;
1101 }
1102 else if (typeof step.getSteps === 'function') {
1103 // getSteps is to enable support open slots
1104 // where devs can add multiple steps into the same slot name
1105 var steps = step.getSteps();
1106 for (var i = 0, l = steps.length; i < l; i++) {
1107 this.addStep(steps[i]);
1108 }
1109 return this;
1110 }
1111 else {
1112 run = step.run.bind(step);
1113 }
1114 this.steps.push(run);
1115 return this;
1116 };
1117 /**
1118 * Runs the pipeline.
1119 *
1120 * @param instruction The navigation instruction to process.
1121 */
1122 Pipeline.prototype.run = function (instruction) {
1123 var nextFn = createNextFn(instruction, this.steps);
1124 return nextFn();
1125 };
1126 return Pipeline;
1127}());
1128
1129/**
1130* Determines if the provided object is a navigation command.
1131* A navigation command is anything with a navigate method.
1132*
1133* @param obj The object to check.
1134*/
1135function isNavigationCommand(obj) {
1136 return obj && typeof obj.navigate === 'function';
1137}
1138/**
1139* Used during the activation lifecycle to cause a redirect.
1140*/
1141var Redirect = /** @class */ (function () {
1142 /**
1143 * @param url The URL fragment to use as the navigation destination.
1144 * @param options The navigation options.
1145 */
1146 function Redirect(url, options) {
1147 if (options === void 0) { options = {}; }
1148 this.url = url;
1149 this.options = Object.assign({ trigger: true, replace: true }, options);
1150 this.shouldContinueProcessing = false;
1151 }
1152 /**
1153 * Called by the activation system to set the child router.
1154 *
1155 * @param router The router.
1156 */
1157 Redirect.prototype.setRouter = function (router) {
1158 this.router = router;
1159 };
1160 /**
1161 * Called by the navigation pipeline to navigate.
1162 *
1163 * @param appRouter The router to be redirected.
1164 */
1165 Redirect.prototype.navigate = function (appRouter) {
1166 var navigatingRouter = this.options.useAppRouter ? appRouter : (this.router || appRouter);
1167 navigatingRouter.navigate(this.url, this.options);
1168 };
1169 return Redirect;
1170}());
1171/**
1172 * Used during the activation lifecycle to cause a redirect to a named route.
1173 */
1174var RedirectToRoute = /** @class */ (function () {
1175 /**
1176 * @param route The name of the route.
1177 * @param params The parameters to be sent to the activation method.
1178 * @param options The options to use for navigation.
1179 */
1180 function RedirectToRoute(route, params, options) {
1181 if (params === void 0) { params = {}; }
1182 if (options === void 0) { options = {}; }
1183 this.route = route;
1184 this.params = params;
1185 this.options = Object.assign({ trigger: true, replace: true }, options);
1186 this.shouldContinueProcessing = false;
1187 }
1188 /**
1189 * Called by the activation system to set the child router.
1190 *
1191 * @param router The router.
1192 */
1193 RedirectToRoute.prototype.setRouter = function (router) {
1194 this.router = router;
1195 };
1196 /**
1197 * Called by the navigation pipeline to navigate.
1198 *
1199 * @param appRouter The router to be redirected.
1200 */
1201 RedirectToRoute.prototype.navigate = function (appRouter) {
1202 var navigatingRouter = this.options.useAppRouter ? appRouter : (this.router || appRouter);
1203 navigatingRouter.navigateToRoute(this.route, this.params, this.options);
1204 };
1205 return RedirectToRoute;
1206}());
1207
1208/**
1209 * @internal exported for unit testing
1210 */
1211function _buildNavigationPlan(instruction, forceLifecycleMinimum) {
1212 var config = instruction.config;
1213 if ('redirect' in config) {
1214 return buildRedirectPlan(instruction);
1215 }
1216 var prevInstruction = instruction.previousInstruction;
1217 var defaultViewPortConfigs = instruction.router.viewPortDefaults;
1218 if (prevInstruction) {
1219 return buildTransitionPlans(instruction, prevInstruction, defaultViewPortConfigs, forceLifecycleMinimum);
1220 }
1221 // first navigation, only need to prepare a few information for each viewport plan
1222 var viewPortPlans = {};
1223 var viewPortConfigs = config.viewPorts;
1224 for (var viewPortName in viewPortConfigs) {
1225 var viewPortConfig = viewPortConfigs[viewPortName];
1226 if (viewPortConfig.moduleId === null && viewPortName in defaultViewPortConfigs) {
1227 viewPortConfig = defaultViewPortConfigs[viewPortName];
1228 }
1229 viewPortPlans[viewPortName] = {
1230 name: viewPortName,
1231 strategy: "replace" /* Replace */,
1232 config: viewPortConfig
1233 };
1234 }
1235 return Promise.resolve(viewPortPlans);
1236}
1237/**
1238 * Build redirect plan based on config of a navigation instruction
1239 * @internal exported for unit testing
1240 */
1241var buildRedirectPlan = function (instruction) {
1242 var config = instruction.config;
1243 var router = instruction.router;
1244 return router
1245 ._createNavigationInstruction(config.redirect)
1246 .then(function (redirectInstruction) {
1247 var params = {};
1248 var originalInstructionParams = instruction.params;
1249 var redirectInstructionParams = redirectInstruction.params;
1250 for (var key in redirectInstructionParams) {
1251 // If the param on the redirect points to another param, e.g. { route: first/:this, redirect: second/:this }
1252 var val = redirectInstructionParams[key];
1253 if (typeof val === 'string' && val[0] === ':') {
1254 val = val.slice(1);
1255 // And if that param is found on the original instruction then use it
1256 if (val in originalInstructionParams) {
1257 params[key] = originalInstructionParams[val];
1258 }
1259 }
1260 else {
1261 params[key] = redirectInstructionParams[key];
1262 }
1263 }
1264 var redirectLocation = router.generate(redirectInstruction.config, params, instruction.options);
1265 // Special handling for child routes
1266 for (var key in originalInstructionParams) {
1267 redirectLocation = redirectLocation.replace(":" + key, originalInstructionParams[key]);
1268 }
1269 var queryString = instruction.queryString;
1270 if (queryString) {
1271 redirectLocation += '?' + queryString;
1272 }
1273 return Promise.resolve(new Redirect(redirectLocation));
1274 });
1275};
1276/**
1277 * @param viewPortPlans the Plan record that holds information about built plans
1278 * @internal exported for unit testing
1279 */
1280var buildTransitionPlans = function (currentInstruction, previousInstruction, defaultViewPortConfigs, forceLifecycleMinimum) {
1281 var viewPortPlans = {};
1282 var newInstructionConfig = currentInstruction.config;
1283 var hasNewParams = hasDifferentParameterValues(previousInstruction, currentInstruction);
1284 var pending = [];
1285 var previousViewPortInstructions = previousInstruction.viewPortInstructions;
1286 var _loop_1 = function (viewPortName) {
1287 var prevViewPortInstruction = previousViewPortInstructions[viewPortName];
1288 var prevViewPortComponent = prevViewPortInstruction.component;
1289 var newInstructionViewPortConfigs = newInstructionConfig.viewPorts;
1290 // if this is invoked on a viewport without any changes, based on new url,
1291 // newViewPortConfig will be the existing viewport instruction
1292 var nextViewPortConfig = viewPortName in newInstructionViewPortConfigs
1293 ? newInstructionViewPortConfigs[viewPortName]
1294 : prevViewPortInstruction;
1295 if (nextViewPortConfig.moduleId === null && viewPortName in defaultViewPortConfigs) {
1296 nextViewPortConfig = defaultViewPortConfigs[viewPortName];
1297 }
1298 var viewPortActivationStrategy = determineActivationStrategy(currentInstruction, prevViewPortInstruction, nextViewPortConfig, hasNewParams, forceLifecycleMinimum);
1299 var viewPortPlan = viewPortPlans[viewPortName] = {
1300 name: viewPortName,
1301 // ViewPortInstruction can quack like a RouteConfig
1302 config: nextViewPortConfig,
1303 prevComponent: prevViewPortComponent,
1304 prevModuleId: prevViewPortInstruction.moduleId,
1305 strategy: viewPortActivationStrategy
1306 };
1307 // recursively build nav plans for all existing child routers/viewports of this viewport
1308 // this is possible because existing child viewports and routers already have necessary information
1309 // to process the wildcard path from parent instruction
1310 if (viewPortActivationStrategy !== "replace" /* Replace */ && prevViewPortInstruction.childRouter) {
1311 var path = currentInstruction.getWildcardPath();
1312 var task = prevViewPortInstruction
1313 .childRouter
1314 ._createNavigationInstruction(path, currentInstruction)
1315 .then(function (childInstruction) {
1316 viewPortPlan.childNavigationInstruction = childInstruction;
1317 return _buildNavigationPlan(childInstruction,
1318 // is it safe to assume viewPortPlan has not been changed from previous assignment?
1319 // if so, can just use local variable viewPortPlanStrategy
1320 // there could be user code modifying viewport plan during _createNavigationInstruction?
1321 viewPortPlan.strategy === "invoke-lifecycle" /* InvokeLifecycle */)
1322 .then(function (childPlan) {
1323 if (childPlan instanceof Redirect) {
1324 return Promise.reject(childPlan);
1325 }
1326 childInstruction.plan = childPlan;
1327 // for bluebird ?
1328 return null;
1329 });
1330 });
1331 pending.push(task);
1332 }
1333 };
1334 for (var viewPortName in previousViewPortInstructions) {
1335 _loop_1(viewPortName);
1336 }
1337 return Promise.all(pending).then(function () { return viewPortPlans; });
1338};
1339/**
1340 * @param newViewPortConfig if this is invoked on a viewport without any changes, based on new url, newViewPortConfig will be the existing viewport instruction
1341 * @internal exported for unit testing
1342 */
1343var determineActivationStrategy = function (currentNavInstruction, prevViewPortInstruction, newViewPortConfig,
1344// indicates whether there is difference between old and new url params
1345hasNewParams, forceLifecycleMinimum) {
1346 var newInstructionConfig = currentNavInstruction.config;
1347 var prevViewPortViewModel = prevViewPortInstruction.component.viewModel;
1348 var viewPortPlanStrategy;
1349 if (prevViewPortInstruction.moduleId !== newViewPortConfig.moduleId) {
1350 viewPortPlanStrategy = "replace" /* Replace */;
1351 }
1352 else if ('determineActivationStrategy' in prevViewPortViewModel) {
1353 viewPortPlanStrategy = prevViewPortViewModel.determineActivationStrategy.apply(prevViewPortViewModel, currentNavInstruction.lifecycleArgs);
1354 }
1355 else if (newInstructionConfig.activationStrategy) {
1356 viewPortPlanStrategy = newInstructionConfig.activationStrategy;
1357 }
1358 else if (hasNewParams || forceLifecycleMinimum) {
1359 viewPortPlanStrategy = "invoke-lifecycle" /* InvokeLifecycle */;
1360 }
1361 else {
1362 viewPortPlanStrategy = "no-change" /* NoChange */;
1363 }
1364 return viewPortPlanStrategy;
1365};
1366/**@internal exported for unit testing */
1367var hasDifferentParameterValues = function (prev, next) {
1368 var prevParams = prev.params;
1369 var nextParams = next.params;
1370 var nextWildCardName = next.config.hasChildRouter ? next.getWildCardName() : null;
1371 for (var key in nextParams) {
1372 if (key === nextWildCardName) {
1373 continue;
1374 }
1375 if (prevParams[key] !== nextParams[key]) {
1376 return true;
1377 }
1378 }
1379 for (var key in prevParams) {
1380 if (key === nextWildCardName) {
1381 continue;
1382 }
1383 if (prevParams[key] !== nextParams[key]) {
1384 return true;
1385 }
1386 }
1387 if (!next.options.compareQueryParams) {
1388 return false;
1389 }
1390 var prevQueryParams = prev.queryParams;
1391 var nextQueryParams = next.queryParams;
1392 for (var key in nextQueryParams) {
1393 if (prevQueryParams[key] !== nextQueryParams[key]) {
1394 return true;
1395 }
1396 }
1397 for (var key in prevQueryParams) {
1398 if (prevQueryParams[key] !== nextQueryParams[key]) {
1399 return true;
1400 }
1401 }
1402 return false;
1403};
1404
1405/**
1406 * Transform a navigation instruction into viewport plan record object,
1407 * or a redirect request if user viewmodel demands
1408 */
1409var BuildNavigationPlanStep = /** @class */ (function () {
1410 function BuildNavigationPlanStep() {
1411 }
1412 BuildNavigationPlanStep.prototype.run = function (navigationInstruction, next) {
1413 return _buildNavigationPlan(navigationInstruction)
1414 .then(function (plan) {
1415 if (plan instanceof Redirect) {
1416 return next.cancel(plan);
1417 }
1418 navigationInstruction.plan = plan;
1419 return next();
1420 })
1421 .catch(next.cancel);
1422 };
1423 return BuildNavigationPlanStep;
1424}());
1425
1426/**
1427 * @internal Exported for unit testing
1428 */
1429var loadNewRoute = function (routeLoader, navigationInstruction) {
1430 var loadingPlans = determineLoadingPlans(navigationInstruction);
1431 var loadPromises = loadingPlans.map(function (loadingPlan) { return loadRoute(routeLoader, loadingPlan.navigationInstruction, loadingPlan.viewPortPlan); });
1432 return Promise.all(loadPromises);
1433};
1434/**
1435 * @internal Exported for unit testing
1436 */
1437var determineLoadingPlans = function (navigationInstruction, loadingPlans) {
1438 if (loadingPlans === void 0) { loadingPlans = []; }
1439 var viewPortPlans = navigationInstruction.plan;
1440 for (var viewPortName in viewPortPlans) {
1441 var viewPortPlan = viewPortPlans[viewPortName];
1442 var childNavInstruction = viewPortPlan.childNavigationInstruction;
1443 if (viewPortPlan.strategy === "replace" /* Replace */) {
1444 loadingPlans.push({ viewPortPlan: viewPortPlan, navigationInstruction: navigationInstruction });
1445 if (childNavInstruction) {
1446 determineLoadingPlans(childNavInstruction, loadingPlans);
1447 }
1448 }
1449 else {
1450 var viewPortInstruction = navigationInstruction.addViewPortInstruction({
1451 name: viewPortName,
1452 strategy: viewPortPlan.strategy,
1453 moduleId: viewPortPlan.prevModuleId,
1454 component: viewPortPlan.prevComponent
1455 });
1456 if (childNavInstruction) {
1457 viewPortInstruction.childNavigationInstruction = childNavInstruction;
1458 determineLoadingPlans(childNavInstruction, loadingPlans);
1459 }
1460 }
1461 }
1462 return loadingPlans;
1463};
1464/**
1465 * @internal Exported for unit testing
1466 */
1467var loadRoute = function (routeLoader, navigationInstruction, viewPortPlan) {
1468 var planConfig = viewPortPlan.config;
1469 var moduleId = planConfig ? planConfig.moduleId : null;
1470 return loadComponent(routeLoader, navigationInstruction, planConfig)
1471 .then(function (component) {
1472 var viewPortInstruction = navigationInstruction.addViewPortInstruction({
1473 name: viewPortPlan.name,
1474 strategy: viewPortPlan.strategy,
1475 moduleId: moduleId,
1476 component: component
1477 });
1478 var childRouter = component.childRouter;
1479 if (childRouter) {
1480 var path = navigationInstruction.getWildcardPath();
1481 return childRouter
1482 ._createNavigationInstruction(path, navigationInstruction)
1483 .then(function (childInstruction) {
1484 viewPortPlan.childNavigationInstruction = childInstruction;
1485 return _buildNavigationPlan(childInstruction)
1486 .then(function (childPlan) {
1487 if (childPlan instanceof Redirect) {
1488 return Promise.reject(childPlan);
1489 }
1490 childInstruction.plan = childPlan;
1491 viewPortInstruction.childNavigationInstruction = childInstruction;
1492 return loadNewRoute(routeLoader, childInstruction);
1493 });
1494 });
1495 }
1496 // ts complains without this, though they are same
1497 return void 0;
1498 });
1499};
1500/**
1501 * Load a routed-component based on navigation instruction and route config
1502 * @internal exported for unit testing only
1503 */
1504var loadComponent = function (routeLoader, navigationInstruction, config) {
1505 var router = navigationInstruction.router;
1506 var lifecycleArgs = navigationInstruction.lifecycleArgs;
1507 return Promise.resolve()
1508 .then(function () { return routeLoader.loadRoute(router, config, navigationInstruction); })
1509 .then(
1510 /**
1511 * @param component an object carrying information about loaded route
1512 * typically contains information about view model, childContainer, view and router
1513 */
1514 function (component) {
1515 var viewModel = component.viewModel, childContainer = component.childContainer;
1516 component.router = router;
1517 component.config = config;
1518 if ('configureRouter' in viewModel) {
1519 var childRouter_1 = childContainer.getChildRouter();
1520 component.childRouter = childRouter_1;
1521 return childRouter_1
1522 .configure(function (c) { return viewModel.configureRouter(c, childRouter_1, lifecycleArgs[0], lifecycleArgs[1], lifecycleArgs[2]); })
1523 .then(function () { return component; });
1524 }
1525 return component;
1526 });
1527};
1528
1529/**
1530 * Abstract class that is responsible for loading view / view model from a route config
1531 * The default implementation can be found in `aurelia-templating-router`
1532 */
1533var RouteLoader = /** @class */ (function () {
1534 function RouteLoader() {
1535 }
1536 /**
1537 * Load a route config based on its viewmodel / view configuration
1538 */
1539 // return typing: return typings used to be never
1540 // as it was a throw. Changing it to Promise<any> should not cause any issues
1541 RouteLoader.prototype.loadRoute = function (router, config, navigationInstruction) {
1542 throw new Error('Route loaders must implement "loadRoute(router, config, navigationInstruction)".');
1543 };
1544 return RouteLoader;
1545}());
1546
1547/**
1548 * A pipeline step responsible for loading a route config of a navigation instruction
1549 */
1550var LoadRouteStep = /** @class */ (function () {
1551 function LoadRouteStep(routeLoader) {
1552 this.routeLoader = routeLoader;
1553 }
1554 /**@internal */
1555 LoadRouteStep.inject = function () { return [RouteLoader]; };
1556 /**
1557 * Run the internal to load route config of a navigation instruction to prepare for next steps in the pipeline
1558 */
1559 LoadRouteStep.prototype.run = function (navigationInstruction, next) {
1560 return loadNewRoute(this.routeLoader, navigationInstruction)
1561 .then(next, next.cancel);
1562 };
1563 return LoadRouteStep;
1564}());
1565
1566/**
1567 * A pipeline step for instructing a piepline to commit changes on a navigation instruction
1568 */
1569var CommitChangesStep = /** @class */ (function () {
1570 function CommitChangesStep() {
1571 }
1572 CommitChangesStep.prototype.run = function (navigationInstruction, next) {
1573 return navigationInstruction
1574 ._commitChanges(/*wait to swap?*/ true)
1575 .then(function () {
1576 navigationInstruction._updateTitle();
1577 return next();
1578 });
1579 };
1580 return CommitChangesStep;
1581}());
1582
1583/**
1584 * An optional interface describing the available activation strategies.
1585 * @internal Used internally.
1586 */
1587var InternalActivationStrategy;
1588(function (InternalActivationStrategy) {
1589 /**
1590 * Reuse the existing view model, without invoking Router lifecycle hooks.
1591 */
1592 InternalActivationStrategy["NoChange"] = "no-change";
1593 /**
1594 * Reuse the existing view model, invoking Router lifecycle hooks.
1595 */
1596 InternalActivationStrategy["InvokeLifecycle"] = "invoke-lifecycle";
1597 /**
1598 * Replace the existing view model, invoking Router lifecycle hooks.
1599 */
1600 InternalActivationStrategy["Replace"] = "replace";
1601})(InternalActivationStrategy || (InternalActivationStrategy = {}));
1602/**
1603 * The strategy to use when activating modules during navigation.
1604 */
1605// kept for compat reason
1606var activationStrategy = {
1607 noChange: "no-change" /* NoChange */,
1608 invokeLifecycle: "invoke-lifecycle" /* InvokeLifecycle */,
1609 replace: "replace" /* Replace */
1610};
1611
1612/**
1613 * Recursively find list of deactivate-able view models
1614 * and invoke the either 'canDeactivate' or 'deactivate' on each
1615 * @internal exported for unit testing
1616 */
1617var processDeactivatable = function (navigationInstruction, callbackName, next, ignoreResult) {
1618 var plan = navigationInstruction.plan;
1619 var infos = findDeactivatable(plan, callbackName);
1620 var i = infos.length; // query from inside out
1621 function inspect(val) {
1622 if (ignoreResult || shouldContinue(val)) {
1623 return iterate();
1624 }
1625 return next.cancel(val);
1626 }
1627 function iterate() {
1628 if (i--) {
1629 try {
1630 var viewModel = infos[i];
1631 var result = viewModel[callbackName](navigationInstruction);
1632 return processPotential(result, inspect, next.cancel);
1633 }
1634 catch (error) {
1635 return next.cancel(error);
1636 }
1637 }
1638 navigationInstruction.router.couldDeactivate = true;
1639 return next();
1640 }
1641 return iterate();
1642};
1643/**
1644 * Recursively find and returns a list of deactivate-able view models
1645 * @internal exported for unit testing
1646 */
1647var findDeactivatable = function (plan, callbackName, list) {
1648 if (list === void 0) { list = []; }
1649 for (var viewPortName in plan) {
1650 var viewPortPlan = plan[viewPortName];
1651 var prevComponent = viewPortPlan.prevComponent;
1652 if ((viewPortPlan.strategy === activationStrategy.invokeLifecycle || viewPortPlan.strategy === activationStrategy.replace)
1653 && prevComponent) {
1654 var viewModel = prevComponent.viewModel;
1655 if (callbackName in viewModel) {
1656 list.push(viewModel);
1657 }
1658 }
1659 if (viewPortPlan.strategy === activationStrategy.replace && prevComponent) {
1660 addPreviousDeactivatable(prevComponent, callbackName, list);
1661 }
1662 else if (viewPortPlan.childNavigationInstruction) {
1663 findDeactivatable(viewPortPlan.childNavigationInstruction.plan, callbackName, list);
1664 }
1665 }
1666 return list;
1667};
1668/**
1669 * @internal exported for unit testing
1670 */
1671var addPreviousDeactivatable = function (component, callbackName, list) {
1672 var childRouter = component.childRouter;
1673 if (childRouter && childRouter.currentInstruction) {
1674 var viewPortInstructions = childRouter.currentInstruction.viewPortInstructions;
1675 for (var viewPortName in viewPortInstructions) {
1676 var viewPortInstruction = viewPortInstructions[viewPortName];
1677 var prevComponent = viewPortInstruction.component;
1678 var prevViewModel = prevComponent.viewModel;
1679 if (callbackName in prevViewModel) {
1680 list.push(prevViewModel);
1681 }
1682 addPreviousDeactivatable(prevComponent, callbackName, list);
1683 }
1684 }
1685};
1686/**
1687 * @internal exported for unit testing
1688 */
1689var processActivatable = function (navigationInstruction, callbackName, next, ignoreResult) {
1690 var infos = findActivatable(navigationInstruction, callbackName);
1691 var length = infos.length;
1692 var i = -1; // query from top down
1693 function inspect(val, router) {
1694 if (ignoreResult || shouldContinue(val, router)) {
1695 return iterate();
1696 }
1697 return next.cancel(val);
1698 }
1699 function iterate() {
1700 var _a;
1701 i++;
1702 if (i < length) {
1703 try {
1704 var current_1 = infos[i];
1705 var result = (_a = current_1.viewModel)[callbackName].apply(_a, current_1.lifecycleArgs);
1706 return processPotential(result, function (val) { return inspect(val, current_1.router); }, next.cancel);
1707 }
1708 catch (error) {
1709 return next.cancel(error);
1710 }
1711 }
1712 return next();
1713 }
1714 return iterate();
1715};
1716/**
1717 * Find list of activatable view model and add to list (3rd parameter)
1718 * @internal exported for unit testing
1719 */
1720var findActivatable = function (navigationInstruction, callbackName, list, router) {
1721 if (list === void 0) { list = []; }
1722 var plan = navigationInstruction.plan;
1723 Object
1724 .keys(plan)
1725 .forEach(function (viewPortName) {
1726 var viewPortPlan = plan[viewPortName];
1727 var viewPortInstruction = navigationInstruction.viewPortInstructions[viewPortName];
1728 var viewPortComponent = viewPortInstruction.component;
1729 var viewModel = viewPortComponent.viewModel;
1730 if ((viewPortPlan.strategy === activationStrategy.invokeLifecycle
1731 || viewPortPlan.strategy === activationStrategy.replace)
1732 && callbackName in viewModel) {
1733 list.push({
1734 viewModel: viewModel,
1735 lifecycleArgs: viewPortInstruction.lifecycleArgs,
1736 router: router
1737 });
1738 }
1739 var childNavInstruction = viewPortPlan.childNavigationInstruction;
1740 if (childNavInstruction) {
1741 findActivatable(childNavInstruction, callbackName, list, viewPortComponent.childRouter || router);
1742 }
1743 });
1744 return list;
1745};
1746var shouldContinue = function (output, router) {
1747 if (output instanceof Error) {
1748 return false;
1749 }
1750 if (isNavigationCommand(output)) {
1751 if (typeof output.setRouter === 'function') {
1752 output.setRouter(router);
1753 }
1754 return !!output.shouldContinueProcessing;
1755 }
1756 if (output === undefined) {
1757 return true;
1758 }
1759 return output;
1760};
1761/**
1762 * wraps a subscription, allowing unsubscribe calls even if
1763 * the first value comes synchronously
1764 */
1765var SafeSubscription = /** @class */ (function () {
1766 function SafeSubscription(subscriptionFunc) {
1767 this._subscribed = true;
1768 this._subscription = subscriptionFunc(this);
1769 if (!this._subscribed) {
1770 this.unsubscribe();
1771 }
1772 }
1773 Object.defineProperty(SafeSubscription.prototype, "subscribed", {
1774 get: function () {
1775 return this._subscribed;
1776 },
1777 enumerable: true,
1778 configurable: true
1779 });
1780 SafeSubscription.prototype.unsubscribe = function () {
1781 if (this._subscribed && this._subscription) {
1782 this._subscription.unsubscribe();
1783 }
1784 this._subscribed = false;
1785 };
1786 return SafeSubscription;
1787}());
1788/**
1789 * A function to process return value from `activate`/`canActivate` steps
1790 * Supports observable/promise
1791 *
1792 * For observable, resolve at first next() or on complete()
1793 */
1794var processPotential = function (obj, resolve, reject) {
1795 // if promise like
1796 if (obj && typeof obj.then === 'function') {
1797 return Promise.resolve(obj).then(resolve).catch(reject);
1798 }
1799 // if observable
1800 if (obj && typeof obj.subscribe === 'function') {
1801 var obs_1 = obj;
1802 return new SafeSubscription(function (sub) { return obs_1.subscribe({
1803 next: function () {
1804 if (sub.subscribed) {
1805 sub.unsubscribe();
1806 resolve(obj);
1807 }
1808 },
1809 error: function (error) {
1810 if (sub.subscribed) {
1811 sub.unsubscribe();
1812 reject(error);
1813 }
1814 },
1815 complete: function () {
1816 if (sub.subscribed) {
1817 sub.unsubscribe();
1818 resolve(obj);
1819 }
1820 }
1821 }); });
1822 }
1823 // else just resolve
1824 try {
1825 return resolve(obj);
1826 }
1827 catch (error) {
1828 return reject(error);
1829 }
1830};
1831
1832/**
1833 * A pipeline step responsible for finding and activating method `canDeactivate` on a view model of a route
1834 */
1835var CanDeactivatePreviousStep = /** @class */ (function () {
1836 function CanDeactivatePreviousStep() {
1837 }
1838 CanDeactivatePreviousStep.prototype.run = function (navigationInstruction, next) {
1839 return processDeactivatable(navigationInstruction, 'canDeactivate', next);
1840 };
1841 return CanDeactivatePreviousStep;
1842}());
1843/**
1844 * A pipeline step responsible for finding and activating method `canActivate` on a view model of a route
1845 */
1846var CanActivateNextStep = /** @class */ (function () {
1847 function CanActivateNextStep() {
1848 }
1849 CanActivateNextStep.prototype.run = function (navigationInstruction, next) {
1850 return processActivatable(navigationInstruction, 'canActivate', next);
1851 };
1852 return CanActivateNextStep;
1853}());
1854/**
1855 * A pipeline step responsible for finding and activating method `deactivate` on a view model of a route
1856 */
1857var DeactivatePreviousStep = /** @class */ (function () {
1858 function DeactivatePreviousStep() {
1859 }
1860 DeactivatePreviousStep.prototype.run = function (navigationInstruction, next) {
1861 return processDeactivatable(navigationInstruction, 'deactivate', next, true);
1862 };
1863 return DeactivatePreviousStep;
1864}());
1865/**
1866 * A pipeline step responsible for finding and activating method `activate` on a view model of a route
1867 */
1868var ActivateNextStep = /** @class */ (function () {
1869 function ActivateNextStep() {
1870 }
1871 ActivateNextStep.prototype.run = function (navigationInstruction, next) {
1872 return processActivatable(navigationInstruction, 'activate', next, true);
1873 };
1874 return ActivateNextStep;
1875}());
1876
1877/**
1878 * A multi-slots Pipeline Placeholder Step for hooking into a pipeline execution
1879 */
1880var PipelineSlot = /** @class */ (function () {
1881 function PipelineSlot(container, name, alias) {
1882 this.steps = [];
1883 this.container = container;
1884 this.slotName = name;
1885 this.slotAlias = alias;
1886 }
1887 PipelineSlot.prototype.getSteps = function () {
1888 var _this = this;
1889 return this.steps.map(function (x) { return _this.container.get(x); });
1890 };
1891 return PipelineSlot;
1892}());
1893/**
1894 * Class responsible for creating the navigation pipeline.
1895 */
1896var PipelineProvider = /** @class */ (function () {
1897 function PipelineProvider(container) {
1898 this.container = container;
1899 this.steps = [
1900 BuildNavigationPlanStep,
1901 CanDeactivatePreviousStep,
1902 LoadRouteStep,
1903 createPipelineSlot(container, "authorize" /* Authorize */),
1904 CanActivateNextStep,
1905 createPipelineSlot(container, "preActivate" /* PreActivate */, 'modelbind'),
1906 // NOTE: app state changes start below - point of no return
1907 DeactivatePreviousStep,
1908 ActivateNextStep,
1909 createPipelineSlot(container, "preRender" /* PreRender */, 'precommit'),
1910 CommitChangesStep,
1911 createPipelineSlot(container, "postRender" /* PostRender */, 'postcomplete')
1912 ];
1913 }
1914 /**@internal */
1915 PipelineProvider.inject = function () { return [aureliaDependencyInjection.Container]; };
1916 /**
1917 * Create the navigation pipeline.
1918 */
1919 PipelineProvider.prototype.createPipeline = function (useCanDeactivateStep) {
1920 var _this = this;
1921 if (useCanDeactivateStep === void 0) { useCanDeactivateStep = true; }
1922 var pipeline = new Pipeline();
1923 this.steps.forEach(function (step) {
1924 if (useCanDeactivateStep || step !== CanDeactivatePreviousStep) {
1925 pipeline.addStep(_this.container.get(step));
1926 }
1927 });
1928 return pipeline;
1929 };
1930 /**@internal */
1931 PipelineProvider.prototype._findStep = function (name) {
1932 // Steps that are not PipelineSlots are constructor functions, and they will automatically fail. Probably.
1933 return this.steps.find(function (x) { return x.slotName === name || x.slotAlias === name; });
1934 };
1935 /**
1936 * Adds a step into the pipeline at a known slot location.
1937 */
1938 PipelineProvider.prototype.addStep = function (name, step) {
1939 var found = this._findStep(name);
1940 if (found) {
1941 var slotSteps = found.steps;
1942 // prevent duplicates
1943 if (!slotSteps.includes(step)) {
1944 slotSteps.push(step);
1945 }
1946 }
1947 else {
1948 throw new Error("Invalid pipeline slot name: " + name + ".");
1949 }
1950 };
1951 /**
1952 * Removes a step from a slot in the pipeline
1953 */
1954 PipelineProvider.prototype.removeStep = function (name, step) {
1955 var slot = this._findStep(name);
1956 if (slot) {
1957 var slotSteps = slot.steps;
1958 slotSteps.splice(slotSteps.indexOf(step), 1);
1959 }
1960 };
1961 /**
1962 * Clears all steps from a slot in the pipeline
1963 * @internal
1964 */
1965 PipelineProvider.prototype._clearSteps = function (name) {
1966 if (name === void 0) { name = ''; }
1967 var slot = this._findStep(name);
1968 if (slot) {
1969 slot.steps = [];
1970 }
1971 };
1972 /**
1973 * Resets all pipeline slots
1974 */
1975 PipelineProvider.prototype.reset = function () {
1976 this._clearSteps("authorize" /* Authorize */);
1977 this._clearSteps("preActivate" /* PreActivate */);
1978 this._clearSteps("preRender" /* PreRender */);
1979 this._clearSteps("postRender" /* PostRender */);
1980 };
1981 return PipelineProvider;
1982}());
1983/**@internal */
1984var createPipelineSlot = function (container, name, alias) {
1985 return new PipelineSlot(container, name, alias);
1986};
1987
1988var logger = LogManager.getLogger('app-router');
1989/**
1990 * The main application router.
1991 */
1992var AppRouter = /** @class */ (function (_super) {
1993 __extends(AppRouter, _super);
1994 function AppRouter(container, history, pipelineProvider, events) {
1995 var _this = _super.call(this, container, history) || this;
1996 _this.pipelineProvider = pipelineProvider;
1997 _this.events = events;
1998 return _this;
1999 }
2000 /**@internal */
2001 AppRouter.inject = function () { return [aureliaDependencyInjection.Container, aureliaHistory.History, PipelineProvider, aureliaEventAggregator.EventAggregator]; };
2002 /**
2003 * Fully resets the router's internal state. Primarily used internally by the framework when multiple calls to setRoot are made.
2004 * Use with caution (actually, avoid using this). Do not use this to simply change your navigation model.
2005 */
2006 AppRouter.prototype.reset = function () {
2007 _super.prototype.reset.call(this);
2008 this.maxInstructionCount = 10;
2009 if (!this._queue) {
2010 this._queue = [];
2011 }
2012 else {
2013 this._queue.length = 0;
2014 }
2015 };
2016 /**
2017 * Loads the specified URL.
2018 *
2019 * @param url The URL fragment to load.
2020 */
2021 AppRouter.prototype.loadUrl = function (url) {
2022 var _this = this;
2023 return this
2024 ._createNavigationInstruction(url)
2025 .then(function (instruction) { return _this._queueInstruction(instruction); })
2026 .catch(function (error) {
2027 logger.error(error);
2028 restorePreviousLocation(_this);
2029 });
2030 };
2031 /**
2032 * Registers a viewPort to be used as a rendering target for activated routes.
2033 *
2034 * @param viewPort The viewPort. This is typically a <router-view/> element in Aurelia default impl
2035 * @param name The name of the viewPort. 'default' if unspecified.
2036 */
2037 AppRouter.prototype.registerViewPort = function (viewPort, name) {
2038 var _this = this;
2039 // having strong typing without changing public API
2040 var $viewPort = viewPort;
2041 _super.prototype.registerViewPort.call(this, $viewPort, name);
2042 // beside adding viewport to the registry of this instance
2043 // AppRouter also configure routing/history to start routing functionality
2044 // There are situation where there are more than 1 <router-view/> element at root view
2045 // in that case, still only activate once via the following guard
2046 if (!this.isActive) {
2047 var viewModel_1 = this._findViewModel($viewPort);
2048 if ('configureRouter' in viewModel_1) {
2049 // If there are more than one <router-view/> element at root view
2050 // use this flag to guard against configure method being invoked multiple times
2051 // this flag is set inside method configure
2052 if (!this.isConfigured) {
2053 // replace the real resolve with a noop to guarantee that any action in base class Router
2054 // won't resolve the configurePromise prematurely
2055 var resolveConfiguredPromise_1 = this._resolveConfiguredPromise;
2056 this._resolveConfiguredPromise = function () { };
2057 return this
2058 .configure(function (config) {
2059 return Promise
2060 .resolve(viewModel_1.configureRouter(config, _this))
2061 // an issue with configure interface. Should be fixed there
2062 // todo: fix this via configure interface in router
2063 .then(function () { return config; });
2064 })
2065 .then(function () {
2066 _this.activate();
2067 resolveConfiguredPromise_1();
2068 });
2069 }
2070 }
2071 else {
2072 this.activate();
2073 }
2074 }
2075 // when a viewport is added dynamically to a root view that is already activated
2076 // just process the navigation instruction
2077 else {
2078 this._dequeueInstruction();
2079 }
2080 return Promise.resolve();
2081 };
2082 /**
2083 * Activates the router. This instructs the router to begin listening for history changes and processing instructions.
2084 *
2085 * @params options The set of options to activate the router with.
2086 */
2087 AppRouter.prototype.activate = function (options) {
2088 if (this.isActive) {
2089 return;
2090 }
2091 this.isActive = true;
2092 // route handler property is responsible for handling url change
2093 // the interface of aurelia-history isn't clear on this perspective
2094 this.options = Object.assign({ routeHandler: this.loadUrl.bind(this) }, this.options, options);
2095 this.history.activate(this.options);
2096 this._dequeueInstruction();
2097 };
2098 /**
2099 * Deactivates the router.
2100 */
2101 AppRouter.prototype.deactivate = function () {
2102 this.isActive = false;
2103 this.history.deactivate();
2104 };
2105 /**@internal */
2106 AppRouter.prototype._queueInstruction = function (instruction) {
2107 var _this = this;
2108 return new Promise(function (resolve) {
2109 instruction.resolve = resolve;
2110 _this._queue.unshift(instruction);
2111 _this._dequeueInstruction();
2112 });
2113 };
2114 /**@internal */
2115 AppRouter.prototype._dequeueInstruction = function (instructionCount) {
2116 var _this = this;
2117 if (instructionCount === void 0) { instructionCount = 0; }
2118 return Promise.resolve().then(function () {
2119 if (_this.isNavigating && !instructionCount) {
2120 // ts complains about inconsistent returns without void 0
2121 return void 0;
2122 }
2123 var instruction = _this._queue.shift();
2124 _this._queue.length = 0;
2125 if (!instruction) {
2126 // ts complains about inconsistent returns without void 0
2127 return void 0;
2128 }
2129 _this.isNavigating = true;
2130 var navtracker = _this.history.getState('NavigationTracker');
2131 var currentNavTracker = _this.currentNavigationTracker;
2132 if (!navtracker && !currentNavTracker) {
2133 _this.isNavigatingFirst = true;
2134 _this.isNavigatingNew = true;
2135 }
2136 else if (!navtracker) {
2137 _this.isNavigatingNew = true;
2138 }
2139 else if (!currentNavTracker) {
2140 _this.isNavigatingRefresh = true;
2141 }
2142 else if (currentNavTracker < navtracker) {
2143 _this.isNavigatingForward = true;
2144 }
2145 else if (currentNavTracker > navtracker) {
2146 _this.isNavigatingBack = true;
2147 }
2148 if (!navtracker) {
2149 navtracker = Date.now();
2150 _this.history.setState('NavigationTracker', navtracker);
2151 }
2152 _this.currentNavigationTracker = navtracker;
2153 instruction.previousInstruction = _this.currentInstruction;
2154 var maxInstructionCount = _this.maxInstructionCount;
2155 if (!instructionCount) {
2156 _this.events.publish("router:navigation:processing" /* Processing */, { instruction: instruction });
2157 }
2158 else if (instructionCount === maxInstructionCount - 1) {
2159 logger.error(instructionCount + 1 + " navigation instructions have been attempted without success. Restoring last known good location.");
2160 restorePreviousLocation(_this);
2161 return _this._dequeueInstruction(instructionCount + 1);
2162 }
2163 else if (instructionCount > maxInstructionCount) {
2164 throw new Error('Maximum navigation attempts exceeded. Giving up.');
2165 }
2166 var pipeline = _this.pipelineProvider.createPipeline(!_this.couldDeactivate);
2167 return pipeline
2168 .run(instruction)
2169 .then(function (result) { return processResult(instruction, result, instructionCount, _this); })
2170 .catch(function (error) {
2171 return { output: error instanceof Error ? error : new Error(error) };
2172 })
2173 .then(function (result) { return resolveInstruction(instruction, result, !!instructionCount, _this); });
2174 });
2175 };
2176 /**@internal */
2177 AppRouter.prototype._findViewModel = function (viewPort) {
2178 if (this.container.viewModel) {
2179 return this.container.viewModel;
2180 }
2181 if (viewPort.container) {
2182 var container = viewPort.container;
2183 while (container) {
2184 if (container.viewModel) {
2185 this.container.viewModel = container.viewModel;
2186 return container.viewModel;
2187 }
2188 container = container.parent;
2189 }
2190 }
2191 return undefined;
2192 };
2193 return AppRouter;
2194}(Router));
2195var processResult = function (instruction, result, instructionCount, router) {
2196 if (!(result && 'completed' in result && 'output' in result)) {
2197 result = result || {};
2198 result.output = new Error("Expected router pipeline to return a navigation result, but got [" + JSON.stringify(result) + "] instead.");
2199 }
2200 var finalResult = null;
2201 var navigationCommandResult = null;
2202 if (isNavigationCommand(result.output)) {
2203 navigationCommandResult = result.output.navigate(router);
2204 }
2205 else {
2206 finalResult = result;
2207 if (!result.completed) {
2208 if (result.output instanceof Error) {
2209 logger.error(result.output.toString());
2210 }
2211 restorePreviousLocation(router);
2212 }
2213 }
2214 return Promise.resolve(navigationCommandResult)
2215 .then(function (_) { return router._dequeueInstruction(instructionCount + 1); })
2216 .then(function (innerResult) { return finalResult || innerResult || result; });
2217};
2218var resolveInstruction = function (instruction, result, isInnerInstruction, router) {
2219 instruction.resolve(result);
2220 var eventAggregator = router.events;
2221 var eventArgs = { instruction: instruction, result: result };
2222 if (!isInnerInstruction) {
2223 router.isNavigating = false;
2224 router.isExplicitNavigation = false;
2225 router.isExplicitNavigationBack = false;
2226 router.isNavigatingFirst = false;
2227 router.isNavigatingNew = false;
2228 router.isNavigatingRefresh = false;
2229 router.isNavigatingForward = false;
2230 router.isNavigatingBack = false;
2231 router.couldDeactivate = false;
2232 var eventName = void 0;
2233 if (result.output instanceof Error) {
2234 eventName = "router:navigation:error" /* Error */;
2235 }
2236 else if (!result.completed) {
2237 eventName = "router:navigation:canceled" /* Canceled */;
2238 }
2239 else {
2240 var queryString = instruction.queryString ? ('?' + instruction.queryString) : '';
2241 router.history.previousLocation = instruction.fragment + queryString;
2242 eventName = "router:navigation:success" /* Success */;
2243 }
2244 eventAggregator.publish(eventName, eventArgs);
2245 eventAggregator.publish("router:navigation:complete" /* Complete */, eventArgs);
2246 }
2247 else {
2248 eventAggregator.publish("router:navigation:child:complete" /* ChildComplete */, eventArgs);
2249 }
2250 return result;
2251};
2252var restorePreviousLocation = function (router) {
2253 var previousLocation = router.history.previousLocation;
2254 if (previousLocation) {
2255 router.navigate(previousLocation, { trigger: false, replace: true });
2256 }
2257 else if (router.fallbackRoute) {
2258 router.navigate(router.fallbackRoute, { trigger: true, replace: true });
2259 }
2260 else {
2261 logger.error('Router navigation failed, and no previous location or fallbackRoute could be restored.');
2262 }
2263};
2264
2265/**
2266* The status of a Pipeline.
2267*/
2268(function (PipelineStatus) {
2269 PipelineStatus["Completed"] = "completed";
2270 PipelineStatus["Canceled"] = "canceled";
2271 PipelineStatus["Rejected"] = "rejected";
2272 PipelineStatus["Running"] = "running";
2273})(exports.PipelineStatus || (exports.PipelineStatus = {}));
2274
2275/**
2276 * A list of known router events used by the Aurelia router
2277 * to signal the pipeline has come to a certain state
2278 */
2279(function (RouterEvent) {
2280 RouterEvent["Processing"] = "router:navigation:processing";
2281 RouterEvent["Error"] = "router:navigation:error";
2282 RouterEvent["Canceled"] = "router:navigation:canceled";
2283 RouterEvent["Complete"] = "router:navigation:complete";
2284 RouterEvent["Success"] = "router:navigation:success";
2285 RouterEvent["ChildComplete"] = "router:navigation:child:complete";
2286})(exports.RouterEvent || (exports.RouterEvent = {}));
2287
2288/**
2289 * Available pipeline slot names to insert interceptor into router pipeline
2290 */
2291(function (PipelineSlotName) {
2292 /**
2293 * Authorization slot. Invoked early in the pipeline,
2294 * before `canActivate` hook of incoming route
2295 */
2296 PipelineSlotName["Authorize"] = "authorize";
2297 /**
2298 * Pre-activation slot. Invoked early in the pipeline,
2299 * Invoked timing:
2300 * - after Authorization slot
2301 * - after canActivate hook on new view model
2302 * - before deactivate hook on old view model
2303 * - before activate hook on new view model
2304 */
2305 PipelineSlotName["PreActivate"] = "preActivate";
2306 /**
2307 * Pre-render slot. Invoked later in the pipeline
2308 * Invokcation timing:
2309 * - after activate hook on new view model
2310 * - before commit step on new navigation instruction
2311 */
2312 PipelineSlotName["PreRender"] = "preRender";
2313 /**
2314 * Post-render slot. Invoked last in the pipeline
2315 */
2316 PipelineSlotName["PostRender"] = "postRender";
2317})(exports.PipelineSlotName || (exports.PipelineSlotName = {}));
2318
2319exports.ActivateNextStep = ActivateNextStep;
2320exports.AppRouter = AppRouter;
2321exports.BuildNavigationPlanStep = BuildNavigationPlanStep;
2322exports.CanActivateNextStep = CanActivateNextStep;
2323exports.CanDeactivatePreviousStep = CanDeactivatePreviousStep;
2324exports.CommitChangesStep = CommitChangesStep;
2325exports.DeactivatePreviousStep = DeactivatePreviousStep;
2326exports.LoadRouteStep = LoadRouteStep;
2327exports.NavModel = NavModel;
2328exports.NavigationInstruction = NavigationInstruction;
2329exports.Pipeline = Pipeline;
2330exports.PipelineProvider = PipelineProvider;
2331exports.Redirect = Redirect;
2332exports.RedirectToRoute = RedirectToRoute;
2333exports.RouteLoader = RouteLoader;
2334exports.Router = Router;
2335exports.RouterConfiguration = RouterConfiguration;
2336exports.activationStrategy = activationStrategy;
2337exports.isNavigationCommand = isNavigationCommand;
2338//# sourceMappingURL=aurelia-router.js.map