UNPKG

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