UNPKG

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