UNPKG

210 kBJavaScriptView Raw
1/**
2 * @license Angular v10.0.5
3 * (c) 2010-2020 Google LLC. https://angular.io/
4 * License: MIT
5 */
6
7import { Location, LocationStrategy, ViewportScroller, PlatformLocation, APP_BASE_HREF, HashLocationStrategy, PathLocationStrategy, ɵgetDOM, LOCATION_INITIALIZED } from '@angular/common';
8import { Component, ɵisObservable, ɵisPromise, NgModuleRef, InjectionToken, NgModuleFactory, ɵConsole, NgZone, isDevMode, Injectable, Type, Injector, NgModuleFactoryLoader, Compiler, Directive, Attribute, Renderer2, ElementRef, Input, HostListener, HostBinding, ChangeDetectorRef, Optional, ContentChildren, EventEmitter, ViewContainerRef, ComponentFactoryResolver, Output, SystemJsNgModuleLoader, NgProbeToken, ANALYZE_FOR_ENTRY_COMPONENTS, SkipSelf, Inject, APP_INITIALIZER, APP_BOOTSTRAP_LISTENER, NgModule, ApplicationRef, Version } from '@angular/core';
9import { of, from, BehaviorSubject, Observable, EmptyError, combineLatest, defer, EMPTY, Subject } from 'rxjs';
10import { map, concatAll, last as last$1, catchError, first, mergeMap, tap, every, switchMap, take, startWith, scan, filter, concatMap, takeLast, finalize, mergeAll } from 'rxjs/operators';
11
12/**
13 * @license
14 * Copyright Google LLC All Rights Reserved.
15 *
16 * Use of this source code is governed by an MIT-style license that can be
17 * found in the LICENSE file at https://angular.io/license
18 */
19/**
20 * Base for events the router goes through, as opposed to events tied to a specific
21 * route. Fired one time for any given navigation.
22 *
23 * The following code shows how a class subscribes to router events.
24 *
25 * ```ts
26 * class MyService {
27 * constructor(public router: Router, logger: Logger) {
28 * router.events.pipe(
29 * filter((e: Event): e is RouterEvent => e instanceof RouterEvent)
30 * ).subscribe((e: RouterEvent) => {
31 * logger.log(e.id, e.url);
32 * });
33 * }
34 * }
35 * ```
36 *
37 * @see `Event`
38 * @see [Router events summary](guide/router#router-events)
39 * @publicApi
40 */
41class RouterEvent {
42 constructor(
43 /** A unique ID that the router assigns to every router navigation. */
44 id,
45 /** The URL that is the destination for this navigation. */
46 url) {
47 this.id = id;
48 this.url = url;
49 }
50}
51/**
52 * An event triggered when a navigation starts.
53 *
54 * @publicApi
55 */
56class NavigationStart extends RouterEvent {
57 constructor(
58 /** @docsNotRequired */
59 id,
60 /** @docsNotRequired */
61 url,
62 /** @docsNotRequired */
63 navigationTrigger = 'imperative',
64 /** @docsNotRequired */
65 restoredState = null) {
66 super(id, url);
67 this.navigationTrigger = navigationTrigger;
68 this.restoredState = restoredState;
69 }
70 /** @docsNotRequired */
71 toString() {
72 return `NavigationStart(id: ${this.id}, url: '${this.url}')`;
73 }
74}
75/**
76 * An event triggered when a navigation ends successfully.
77 *
78 * @see `NavigationStart`
79 * @see `NavigationCancel`
80 * @see `NavigationError`
81 *
82 * @publicApi
83 */
84class NavigationEnd extends RouterEvent {
85 constructor(
86 /** @docsNotRequired */
87 id,
88 /** @docsNotRequired */
89 url,
90 /** @docsNotRequired */
91 urlAfterRedirects) {
92 super(id, url);
93 this.urlAfterRedirects = urlAfterRedirects;
94 }
95 /** @docsNotRequired */
96 toString() {
97 return `NavigationEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}')`;
98 }
99}
100/**
101 * An event triggered when a navigation is canceled, directly or indirectly.
102 * This can happen when a route guard
103 * returns `false` or initiates a redirect by returning a `UrlTree`.
104 *
105 * @see `NavigationStart`
106 * @see `NavigationEnd`
107 * @see `NavigationError`
108 *
109 * @publicApi
110 */
111class NavigationCancel extends RouterEvent {
112 constructor(
113 /** @docsNotRequired */
114 id,
115 /** @docsNotRequired */
116 url,
117 /** @docsNotRequired */
118 reason) {
119 super(id, url);
120 this.reason = reason;
121 }
122 /** @docsNotRequired */
123 toString() {
124 return `NavigationCancel(id: ${this.id}, url: '${this.url}')`;
125 }
126}
127/**
128 * An event triggered when a navigation fails due to an unexpected error.
129 *
130 * @see `NavigationStart`
131 * @see `NavigationEnd`
132 * @see `NavigationCancel`
133 *
134 * @publicApi
135 */
136class NavigationError extends RouterEvent {
137 constructor(
138 /** @docsNotRequired */
139 id,
140 /** @docsNotRequired */
141 url,
142 /** @docsNotRequired */
143 error) {
144 super(id, url);
145 this.error = error;
146 }
147 /** @docsNotRequired */
148 toString() {
149 return `NavigationError(id: ${this.id}, url: '${this.url}', error: ${this.error})`;
150 }
151}
152/**
153 * An event triggered when routes are recognized.
154 *
155 * @publicApi
156 */
157class RoutesRecognized extends RouterEvent {
158 constructor(
159 /** @docsNotRequired */
160 id,
161 /** @docsNotRequired */
162 url,
163 /** @docsNotRequired */
164 urlAfterRedirects,
165 /** @docsNotRequired */
166 state) {
167 super(id, url);
168 this.urlAfterRedirects = urlAfterRedirects;
169 this.state = state;
170 }
171 /** @docsNotRequired */
172 toString() {
173 return `RoutesRecognized(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
174 }
175}
176/**
177 * An event triggered at the start of the Guard phase of routing.
178 *
179 * @see `GuardsCheckEnd`
180 *
181 * @publicApi
182 */
183class GuardsCheckStart extends RouterEvent {
184 constructor(
185 /** @docsNotRequired */
186 id,
187 /** @docsNotRequired */
188 url,
189 /** @docsNotRequired */
190 urlAfterRedirects,
191 /** @docsNotRequired */
192 state) {
193 super(id, url);
194 this.urlAfterRedirects = urlAfterRedirects;
195 this.state = state;
196 }
197 toString() {
198 return `GuardsCheckStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
199 }
200}
201/**
202 * An event triggered at the end of the Guard phase of routing.
203 *
204 * @see `GuardsCheckStart`
205 *
206 * @publicApi
207 */
208class GuardsCheckEnd extends RouterEvent {
209 constructor(
210 /** @docsNotRequired */
211 id,
212 /** @docsNotRequired */
213 url,
214 /** @docsNotRequired */
215 urlAfterRedirects,
216 /** @docsNotRequired */
217 state,
218 /** @docsNotRequired */
219 shouldActivate) {
220 super(id, url);
221 this.urlAfterRedirects = urlAfterRedirects;
222 this.state = state;
223 this.shouldActivate = shouldActivate;
224 }
225 toString() {
226 return `GuardsCheckEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state}, shouldActivate: ${this.shouldActivate})`;
227 }
228}
229/**
230 * An event triggered at the the start of the Resolve phase of routing.
231 *
232 * Runs in the "resolve" phase whether or not there is anything to resolve.
233 * In future, may change to only run when there are things to be resolved.
234 *
235 * @see `ResolveEnd`
236 *
237 * @publicApi
238 */
239class ResolveStart extends RouterEvent {
240 constructor(
241 /** @docsNotRequired */
242 id,
243 /** @docsNotRequired */
244 url,
245 /** @docsNotRequired */
246 urlAfterRedirects,
247 /** @docsNotRequired */
248 state) {
249 super(id, url);
250 this.urlAfterRedirects = urlAfterRedirects;
251 this.state = state;
252 }
253 toString() {
254 return `ResolveStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
255 }
256}
257/**
258 * An event triggered at the end of the Resolve phase of routing.
259 * @see `ResolveStart`.
260 *
261 * @publicApi
262 */
263class ResolveEnd extends RouterEvent {
264 constructor(
265 /** @docsNotRequired */
266 id,
267 /** @docsNotRequired */
268 url,
269 /** @docsNotRequired */
270 urlAfterRedirects,
271 /** @docsNotRequired */
272 state) {
273 super(id, url);
274 this.urlAfterRedirects = urlAfterRedirects;
275 this.state = state;
276 }
277 toString() {
278 return `ResolveEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
279 }
280}
281/**
282 * An event triggered before lazy loading a route configuration.
283 *
284 * @see `RouteConfigLoadEnd`
285 *
286 * @publicApi
287 */
288class RouteConfigLoadStart {
289 constructor(
290 /** @docsNotRequired */
291 route) {
292 this.route = route;
293 }
294 toString() {
295 return `RouteConfigLoadStart(path: ${this.route.path})`;
296 }
297}
298/**
299 * An event triggered when a route has been lazy loaded.
300 *
301 * @see `RouteConfigLoadStart`
302 *
303 * @publicApi
304 */
305class RouteConfigLoadEnd {
306 constructor(
307 /** @docsNotRequired */
308 route) {
309 this.route = route;
310 }
311 toString() {
312 return `RouteConfigLoadEnd(path: ${this.route.path})`;
313 }
314}
315/**
316 * An event triggered at the start of the child-activation
317 * part of the Resolve phase of routing.
318 * @see `ChildActivationEnd`
319 * @see `ResolveStart`
320 *
321 * @publicApi
322 */
323class ChildActivationStart {
324 constructor(
325 /** @docsNotRequired */
326 snapshot) {
327 this.snapshot = snapshot;
328 }
329 toString() {
330 const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
331 return `ChildActivationStart(path: '${path}')`;
332 }
333}
334/**
335 * An event triggered at the end of the child-activation part
336 * of the Resolve phase of routing.
337 * @see `ChildActivationStart`
338 * @see `ResolveStart`
339 * @publicApi
340 */
341class ChildActivationEnd {
342 constructor(
343 /** @docsNotRequired */
344 snapshot) {
345 this.snapshot = snapshot;
346 }
347 toString() {
348 const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
349 return `ChildActivationEnd(path: '${path}')`;
350 }
351}
352/**
353 * An event triggered at the start of the activation part
354 * of the Resolve phase of routing.
355 * @see `ActivationEnd`
356 * @see `ResolveStart`
357 *
358 * @publicApi
359 */
360class ActivationStart {
361 constructor(
362 /** @docsNotRequired */
363 snapshot) {
364 this.snapshot = snapshot;
365 }
366 toString() {
367 const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
368 return `ActivationStart(path: '${path}')`;
369 }
370}
371/**
372 * An event triggered at the end of the activation part
373 * of the Resolve phase of routing.
374 * @see `ActivationStart`
375 * @see `ResolveStart`
376 *
377 * @publicApi
378 */
379class ActivationEnd {
380 constructor(
381 /** @docsNotRequired */
382 snapshot) {
383 this.snapshot = snapshot;
384 }
385 toString() {
386 const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
387 return `ActivationEnd(path: '${path}')`;
388 }
389}
390/**
391 * An event triggered by scrolling.
392 *
393 * @publicApi
394 */
395class Scroll {
396 constructor(
397 /** @docsNotRequired */
398 routerEvent,
399 /** @docsNotRequired */
400 position,
401 /** @docsNotRequired */
402 anchor) {
403 this.routerEvent = routerEvent;
404 this.position = position;
405 this.anchor = anchor;
406 }
407 toString() {
408 const pos = this.position ? `${this.position[0]}, ${this.position[1]}` : null;
409 return `Scroll(anchor: '${this.anchor}', position: '${pos}')`;
410 }
411}
412
413/**
414 * @license
415 * Copyright Google LLC All Rights Reserved.
416 *
417 * Use of this source code is governed by an MIT-style license that can be
418 * found in the LICENSE file at https://angular.io/license
419 */
420/**
421 * This component is used internally within the router to be a placeholder when an empty
422 * router-outlet is needed. For example, with a config such as:
423 *
424 * `{path: 'parent', outlet: 'nav', children: [...]}`
425 *
426 * In order to render, there needs to be a component on this config, which will default
427 * to this `EmptyOutletComponent`.
428 */
429class ɵEmptyOutletComponent {
430}
431ɵEmptyOutletComponent.decorators = [
432 { type: Component, args: [{ template: `<router-outlet></router-outlet>` },] }
433];
434
435/**
436 * @license
437 * Copyright Google LLC All Rights Reserved.
438 *
439 * Use of this source code is governed by an MIT-style license that can be
440 * found in the LICENSE file at https://angular.io/license
441 */
442/**
443 * The primary routing outlet.
444 *
445 * @publicApi
446 */
447const PRIMARY_OUTLET = 'primary';
448class ParamsAsMap {
449 constructor(params) {
450 this.params = params || {};
451 }
452 has(name) {
453 return Object.prototype.hasOwnProperty.call(this.params, name);
454 }
455 get(name) {
456 if (this.has(name)) {
457 const v = this.params[name];
458 return Array.isArray(v) ? v[0] : v;
459 }
460 return null;
461 }
462 getAll(name) {
463 if (this.has(name)) {
464 const v = this.params[name];
465 return Array.isArray(v) ? v : [v];
466 }
467 return [];
468 }
469 get keys() {
470 return Object.keys(this.params);
471 }
472}
473/**
474 * Converts a `Params` instance to a `ParamMap`.
475 * @param params The instance to convert.
476 * @returns The new map instance.
477 *
478 * @publicApi
479 */
480function convertToParamMap(params) {
481 return new ParamsAsMap(params);
482}
483const NAVIGATION_CANCELING_ERROR = 'ngNavigationCancelingError';
484function navigationCancelingError(message) {
485 const error = Error('NavigationCancelingError: ' + message);
486 error[NAVIGATION_CANCELING_ERROR] = true;
487 return error;
488}
489function isNavigationCancelingError(error) {
490 return error && error[NAVIGATION_CANCELING_ERROR];
491}
492// Matches the route configuration (`route`) against the actual URL (`segments`).
493function defaultUrlMatcher(segments, segmentGroup, route) {
494 const parts = route.path.split('/');
495 if (parts.length > segments.length) {
496 // The actual URL is shorter than the config, no match
497 return null;
498 }
499 if (route.pathMatch === 'full' &&
500 (segmentGroup.hasChildren() || parts.length < segments.length)) {
501 // The config is longer than the actual URL but we are looking for a full match, return null
502 return null;
503 }
504 const posParams = {};
505 // Check each config part against the actual URL
506 for (let index = 0; index < parts.length; index++) {
507 const part = parts[index];
508 const segment = segments[index];
509 const isParameter = part.startsWith(':');
510 if (isParameter) {
511 posParams[part.substring(1)] = segment;
512 }
513 else if (part !== segment.path) {
514 // The actual URL part does not match the config, no match
515 return null;
516 }
517 }
518 return { consumed: segments.slice(0, parts.length), posParams };
519}
520
521/**
522 * @license
523 * Copyright Google LLC All Rights Reserved.
524 *
525 * Use of this source code is governed by an MIT-style license that can be
526 * found in the LICENSE file at https://angular.io/license
527 */
528class LoadedRouterConfig {
529 constructor(routes, module) {
530 this.routes = routes;
531 this.module = module;
532 }
533}
534function validateConfig(config, parentPath = '') {
535 // forEach doesn't iterate undefined values
536 for (let i = 0; i < config.length; i++) {
537 const route = config[i];
538 const fullPath = getFullPath(parentPath, route);
539 validateNode(route, fullPath);
540 }
541}
542function validateNode(route, fullPath) {
543 if (!route) {
544 throw new Error(`
545 Invalid configuration of route '${fullPath}': Encountered undefined route.
546 The reason might be an extra comma.
547
548 Example:
549 const routes: Routes = [
550 { path: '', redirectTo: '/dashboard', pathMatch: 'full' },
551 { path: 'dashboard', component: DashboardComponent },, << two commas
552 { path: 'detail/:id', component: HeroDetailComponent }
553 ];
554 `);
555 }
556 if (Array.isArray(route)) {
557 throw new Error(`Invalid configuration of route '${fullPath}': Array cannot be specified`);
558 }
559 if (!route.component && !route.children && !route.loadChildren &&
560 (route.outlet && route.outlet !== PRIMARY_OUTLET)) {
561 throw new Error(`Invalid configuration of route '${fullPath}': a componentless route without children or loadChildren cannot have a named outlet set`);
562 }
563 if (route.redirectTo && route.children) {
564 throw new Error(`Invalid configuration of route '${fullPath}': redirectTo and children cannot be used together`);
565 }
566 if (route.redirectTo && route.loadChildren) {
567 throw new Error(`Invalid configuration of route '${fullPath}': redirectTo and loadChildren cannot be used together`);
568 }
569 if (route.children && route.loadChildren) {
570 throw new Error(`Invalid configuration of route '${fullPath}': children and loadChildren cannot be used together`);
571 }
572 if (route.redirectTo && route.component) {
573 throw new Error(`Invalid configuration of route '${fullPath}': redirectTo and component cannot be used together`);
574 }
575 if (route.path && route.matcher) {
576 throw new Error(`Invalid configuration of route '${fullPath}': path and matcher cannot be used together`);
577 }
578 if (route.redirectTo === void 0 && !route.component && !route.children && !route.loadChildren) {
579 throw new Error(`Invalid configuration of route '${fullPath}'. One of the following must be provided: component, redirectTo, children or loadChildren`);
580 }
581 if (route.path === void 0 && route.matcher === void 0) {
582 throw new Error(`Invalid configuration of route '${fullPath}': routes must have either a path or a matcher specified`);
583 }
584 if (typeof route.path === 'string' && route.path.charAt(0) === '/') {
585 throw new Error(`Invalid configuration of route '${fullPath}': path cannot start with a slash`);
586 }
587 if (route.path === '' && route.redirectTo !== void 0 && route.pathMatch === void 0) {
588 const exp = `The default value of 'pathMatch' is 'prefix', but often the intent is to use 'full'.`;
589 throw new Error(`Invalid configuration of route '{path: "${fullPath}", redirectTo: "${route.redirectTo}"}': please provide 'pathMatch'. ${exp}`);
590 }
591 if (route.pathMatch !== void 0 && route.pathMatch !== 'full' && route.pathMatch !== 'prefix') {
592 throw new Error(`Invalid configuration of route '${fullPath}': pathMatch can only be set to 'prefix' or 'full'`);
593 }
594 if (route.children) {
595 validateConfig(route.children, fullPath);
596 }
597}
598function getFullPath(parentPath, currentRoute) {
599 if (!currentRoute) {
600 return parentPath;
601 }
602 if (!parentPath && !currentRoute.path) {
603 return '';
604 }
605 else if (parentPath && !currentRoute.path) {
606 return `${parentPath}/`;
607 }
608 else if (!parentPath && currentRoute.path) {
609 return currentRoute.path;
610 }
611 else {
612 return `${parentPath}/${currentRoute.path}`;
613 }
614}
615/**
616 * Makes a copy of the config and adds any default required properties.
617 */
618function standardizeConfig(r) {
619 const children = r.children && r.children.map(standardizeConfig);
620 const c = children ? Object.assign(Object.assign({}, r), { children }) : Object.assign({}, r);
621 if (!c.component && (children || c.loadChildren) && (c.outlet && c.outlet !== PRIMARY_OUTLET)) {
622 c.component = ɵEmptyOutletComponent;
623 }
624 return c;
625}
626
627/**
628 * @license
629 * Copyright Google LLC All Rights Reserved.
630 *
631 * Use of this source code is governed by an MIT-style license that can be
632 * found in the LICENSE file at https://angular.io/license
633 */
634function shallowEqualArrays(a, b) {
635 if (a.length !== b.length)
636 return false;
637 for (let i = 0; i < a.length; ++i) {
638 if (!shallowEqual(a[i], b[i]))
639 return false;
640 }
641 return true;
642}
643function shallowEqual(a, b) {
644 // Casting Object.keys return values to include `undefined` as there are some cases
645 // in IE 11 where this can happen. Cannot provide a test because the behavior only
646 // exists in certain circumstances in IE 11, therefore doing this cast ensures the
647 // logic is correct for when this edge case is hit.
648 const k1 = Object.keys(a);
649 const k2 = Object.keys(b);
650 if (!k1 || !k2 || k1.length != k2.length) {
651 return false;
652 }
653 let key;
654 for (let i = 0; i < k1.length; i++) {
655 key = k1[i];
656 if (!equalArraysOrString(a[key], b[key])) {
657 return false;
658 }
659 }
660 return true;
661}
662/**
663 * Test equality for arrays of strings or a string.
664 */
665function equalArraysOrString(a, b) {
666 if (Array.isArray(a) && Array.isArray(b)) {
667 if (a.length != b.length)
668 return false;
669 return a.every(aItem => b.indexOf(aItem) > -1);
670 }
671 else {
672 return a === b;
673 }
674}
675/**
676 * Flattens single-level nested arrays.
677 */
678function flatten(arr) {
679 return Array.prototype.concat.apply([], arr);
680}
681/**
682 * Return the last element of an array.
683 */
684function last(a) {
685 return a.length > 0 ? a[a.length - 1] : null;
686}
687/**
688 * Verifys all booleans in an array are `true`.
689 */
690function and(bools) {
691 return !bools.some(v => !v);
692}
693function forEach(map, callback) {
694 for (const prop in map) {
695 if (map.hasOwnProperty(prop)) {
696 callback(map[prop], prop);
697 }
698 }
699}
700function waitForMap(obj, fn) {
701 if (Object.keys(obj).length === 0) {
702 return of({});
703 }
704 const waitHead = [];
705 const waitTail = [];
706 const res = {};
707 forEach(obj, (a, k) => {
708 const mapped = fn(k, a).pipe(map((r) => res[k] = r));
709 if (k === PRIMARY_OUTLET) {
710 waitHead.push(mapped);
711 }
712 else {
713 waitTail.push(mapped);
714 }
715 });
716 // Closure compiler has problem with using spread operator here. So we use "Array.concat".
717 // Note that we also need to cast the new promise because TypeScript cannot infer the type
718 // when calling the "of" function through "Function.apply"
719 return of.apply(null, waitHead.concat(waitTail))
720 .pipe(concatAll(), last$1(), map(() => res));
721}
722function wrapIntoObservable(value) {
723 if (ɵisObservable(value)) {
724 return value;
725 }
726 if (ɵisPromise(value)) {
727 // Use `Promise.resolve()` to wrap promise-like instances.
728 // Required ie when a Resolver returns a AngularJS `$q` promise to correctly trigger the
729 // change detection.
730 return from(Promise.resolve(value));
731 }
732 return of(value);
733}
734
735/**
736 * @license
737 * Copyright Google LLC All Rights Reserved.
738 *
739 * Use of this source code is governed by an MIT-style license that can be
740 * found in the LICENSE file at https://angular.io/license
741 */
742function createEmptyUrlTree() {
743 return new UrlTree(new UrlSegmentGroup([], {}), {}, null);
744}
745function containsTree(container, containee, exact) {
746 if (exact) {
747 return equalQueryParams(container.queryParams, containee.queryParams) &&
748 equalSegmentGroups(container.root, containee.root);
749 }
750 return containsQueryParams(container.queryParams, containee.queryParams) &&
751 containsSegmentGroup(container.root, containee.root);
752}
753function equalQueryParams(container, containee) {
754 // TODO: This does not handle array params correctly.
755 return shallowEqual(container, containee);
756}
757function equalSegmentGroups(container, containee) {
758 if (!equalPath(container.segments, containee.segments))
759 return false;
760 if (container.numberOfChildren !== containee.numberOfChildren)
761 return false;
762 for (const c in containee.children) {
763 if (!container.children[c])
764 return false;
765 if (!equalSegmentGroups(container.children[c], containee.children[c]))
766 return false;
767 }
768 return true;
769}
770function containsQueryParams(container, containee) {
771 // TODO: This does not handle array params correctly.
772 return Object.keys(containee).length <= Object.keys(container).length &&
773 Object.keys(containee).every(key => equalArraysOrString(container[key], containee[key]));
774}
775function containsSegmentGroup(container, containee) {
776 return containsSegmentGroupHelper(container, containee, containee.segments);
777}
778function containsSegmentGroupHelper(container, containee, containeePaths) {
779 if (container.segments.length > containeePaths.length) {
780 const current = container.segments.slice(0, containeePaths.length);
781 if (!equalPath(current, containeePaths))
782 return false;
783 if (containee.hasChildren())
784 return false;
785 return true;
786 }
787 else if (container.segments.length === containeePaths.length) {
788 if (!equalPath(container.segments, containeePaths))
789 return false;
790 for (const c in containee.children) {
791 if (!container.children[c])
792 return false;
793 if (!containsSegmentGroup(container.children[c], containee.children[c]))
794 return false;
795 }
796 return true;
797 }
798 else {
799 const current = containeePaths.slice(0, container.segments.length);
800 const next = containeePaths.slice(container.segments.length);
801 if (!equalPath(container.segments, current))
802 return false;
803 if (!container.children[PRIMARY_OUTLET])
804 return false;
805 return containsSegmentGroupHelper(container.children[PRIMARY_OUTLET], containee, next);
806 }
807}
808/**
809 * @description
810 *
811 * Represents the parsed URL.
812 *
813 * Since a router state is a tree, and the URL is nothing but a serialized state, the URL is a
814 * serialized tree.
815 * UrlTree is a data structure that provides a lot of affordances in dealing with URLs
816 *
817 * @usageNotes
818 * ### Example
819 *
820 * ```
821 * @Component({templateUrl:'template.html'})
822 * class MyComponent {
823 * constructor(router: Router) {
824 * const tree: UrlTree =
825 * router.parseUrl('/team/33/(user/victor//support:help)?debug=true#fragment');
826 * const f = tree.fragment; // return 'fragment'
827 * const q = tree.queryParams; // returns {debug: 'true'}
828 * const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];
829 * const s: UrlSegment[] = g.segments; // returns 2 segments 'team' and '33'
830 * g.children[PRIMARY_OUTLET].segments; // returns 2 segments 'user' and 'victor'
831 * g.children['support'].segments; // return 1 segment 'help'
832 * }
833 * }
834 * ```
835 *
836 * @publicApi
837 */
838class UrlTree {
839 /** @internal */
840 constructor(
841 /** The root segment group of the URL tree */
842 root,
843 /** The query params of the URL */
844 queryParams,
845 /** The fragment of the URL */
846 fragment) {
847 this.root = root;
848 this.queryParams = queryParams;
849 this.fragment = fragment;
850 }
851 get queryParamMap() {
852 if (!this._queryParamMap) {
853 this._queryParamMap = convertToParamMap(this.queryParams);
854 }
855 return this._queryParamMap;
856 }
857 /** @docsNotRequired */
858 toString() {
859 return DEFAULT_SERIALIZER.serialize(this);
860 }
861}
862/**
863 * @description
864 *
865 * Represents the parsed URL segment group.
866 *
867 * See `UrlTree` for more information.
868 *
869 * @publicApi
870 */
871class UrlSegmentGroup {
872 constructor(
873 /** The URL segments of this group. See `UrlSegment` for more information */
874 segments,
875 /** The list of children of this group */
876 children) {
877 this.segments = segments;
878 this.children = children;
879 /** The parent node in the url tree */
880 this.parent = null;
881 forEach(children, (v, k) => v.parent = this);
882 }
883 /** Whether the segment has child segments */
884 hasChildren() {
885 return this.numberOfChildren > 0;
886 }
887 /** Number of child segments */
888 get numberOfChildren() {
889 return Object.keys(this.children).length;
890 }
891 /** @docsNotRequired */
892 toString() {
893 return serializePaths(this);
894 }
895}
896/**
897 * @description
898 *
899 * Represents a single URL segment.
900 *
901 * A UrlSegment is a part of a URL between the two slashes. It contains a path and the matrix
902 * parameters associated with the segment.
903 *
904 * @usageNotes
905 * ### Example
906 *
907 * ```
908 * @Component({templateUrl:'template.html'})
909 * class MyComponent {
910 * constructor(router: Router) {
911 * const tree: UrlTree = router.parseUrl('/team;id=33');
912 * const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];
913 * const s: UrlSegment[] = g.segments;
914 * s[0].path; // returns 'team'
915 * s[0].parameters; // returns {id: 33}
916 * }
917 * }
918 * ```
919 *
920 * @publicApi
921 */
922class UrlSegment {
923 constructor(
924 /** The path part of a URL segment */
925 path,
926 /** The matrix parameters associated with a segment */
927 parameters) {
928 this.path = path;
929 this.parameters = parameters;
930 }
931 get parameterMap() {
932 if (!this._parameterMap) {
933 this._parameterMap = convertToParamMap(this.parameters);
934 }
935 return this._parameterMap;
936 }
937 /** @docsNotRequired */
938 toString() {
939 return serializePath(this);
940 }
941}
942function equalSegments(as, bs) {
943 return equalPath(as, bs) && as.every((a, i) => shallowEqual(a.parameters, bs[i].parameters));
944}
945function equalPath(as, bs) {
946 if (as.length !== bs.length)
947 return false;
948 return as.every((a, i) => a.path === bs[i].path);
949}
950function mapChildrenIntoArray(segment, fn) {
951 let res = [];
952 forEach(segment.children, (child, childOutlet) => {
953 if (childOutlet === PRIMARY_OUTLET) {
954 res = res.concat(fn(child, childOutlet));
955 }
956 });
957 forEach(segment.children, (child, childOutlet) => {
958 if (childOutlet !== PRIMARY_OUTLET) {
959 res = res.concat(fn(child, childOutlet));
960 }
961 });
962 return res;
963}
964/**
965 * @description
966 *
967 * Serializes and deserializes a URL string into a URL tree.
968 *
969 * The url serialization strategy is customizable. You can
970 * make all URLs case insensitive by providing a custom UrlSerializer.
971 *
972 * See `DefaultUrlSerializer` for an example of a URL serializer.
973 *
974 * @publicApi
975 */
976class UrlSerializer {
977}
978/**
979 * @description
980 *
981 * A default implementation of the `UrlSerializer`.
982 *
983 * Example URLs:
984 *
985 * ```
986 * /inbox/33(popup:compose)
987 * /inbox/33;open=true/messages/44
988 * ```
989 *
990 * DefaultUrlSerializer uses parentheses to serialize secondary segments (e.g., popup:compose), the
991 * colon syntax to specify the outlet, and the ';parameter=value' syntax (e.g., open=true) to
992 * specify route specific parameters.
993 *
994 * @publicApi
995 */
996class DefaultUrlSerializer {
997 /** Parses a url into a `UrlTree` */
998 parse(url) {
999 const p = new UrlParser(url);
1000 return new UrlTree(p.parseRootSegment(), p.parseQueryParams(), p.parseFragment());
1001 }
1002 /** Converts a `UrlTree` into a url */
1003 serialize(tree) {
1004 const segment = `/${serializeSegment(tree.root, true)}`;
1005 const query = serializeQueryParams(tree.queryParams);
1006 const fragment = typeof tree.fragment === `string` ? `#${encodeUriFragment(tree.fragment)}` : '';
1007 return `${segment}${query}${fragment}`;
1008 }
1009}
1010const DEFAULT_SERIALIZER = new DefaultUrlSerializer();
1011function serializePaths(segment) {
1012 return segment.segments.map(p => serializePath(p)).join('/');
1013}
1014function serializeSegment(segment, root) {
1015 if (!segment.hasChildren()) {
1016 return serializePaths(segment);
1017 }
1018 if (root) {
1019 const primary = segment.children[PRIMARY_OUTLET] ?
1020 serializeSegment(segment.children[PRIMARY_OUTLET], false) :
1021 '';
1022 const children = [];
1023 forEach(segment.children, (v, k) => {
1024 if (k !== PRIMARY_OUTLET) {
1025 children.push(`${k}:${serializeSegment(v, false)}`);
1026 }
1027 });
1028 return children.length > 0 ? `${primary}(${children.join('//')})` : primary;
1029 }
1030 else {
1031 const children = mapChildrenIntoArray(segment, (v, k) => {
1032 if (k === PRIMARY_OUTLET) {
1033 return [serializeSegment(segment.children[PRIMARY_OUTLET], false)];
1034 }
1035 return [`${k}:${serializeSegment(v, false)}`];
1036 });
1037 return `${serializePaths(segment)}/(${children.join('//')})`;
1038 }
1039}
1040/**
1041 * Encodes a URI string with the default encoding. This function will only ever be called from
1042 * `encodeUriQuery` or `encodeUriSegment` as it's the base set of encodings to be used. We need
1043 * a custom encoding because encodeURIComponent is too aggressive and encodes stuff that doesn't
1044 * have to be encoded per https://url.spec.whatwg.org.
1045 */
1046function encodeUriString(s) {
1047 return encodeURIComponent(s)
1048 .replace(/%40/g, '@')
1049 .replace(/%3A/gi, ':')
1050 .replace(/%24/g, '$')
1051 .replace(/%2C/gi, ',');
1052}
1053/**
1054 * This function should be used to encode both keys and values in a query string key/value. In
1055 * the following URL, you need to call encodeUriQuery on "k" and "v":
1056 *
1057 * http://www.site.org/html;mk=mv?k=v#f
1058 */
1059function encodeUriQuery(s) {
1060 return encodeUriString(s).replace(/%3B/gi, ';');
1061}
1062/**
1063 * This function should be used to encode a URL fragment. In the following URL, you need to call
1064 * encodeUriFragment on "f":
1065 *
1066 * http://www.site.org/html;mk=mv?k=v#f
1067 */
1068function encodeUriFragment(s) {
1069 return encodeURI(s);
1070}
1071/**
1072 * This function should be run on any URI segment as well as the key and value in a key/value
1073 * pair for matrix params. In the following URL, you need to call encodeUriSegment on "html",
1074 * "mk", and "mv":
1075 *
1076 * http://www.site.org/html;mk=mv?k=v#f
1077 */
1078function encodeUriSegment(s) {
1079 return encodeUriString(s).replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/%26/gi, '&');
1080}
1081function decode(s) {
1082 return decodeURIComponent(s);
1083}
1084// Query keys/values should have the "+" replaced first, as "+" in a query string is " ".
1085// decodeURIComponent function will not decode "+" as a space.
1086function decodeQuery(s) {
1087 return decode(s.replace(/\+/g, '%20'));
1088}
1089function serializePath(path) {
1090 return `${encodeUriSegment(path.path)}${serializeMatrixParams(path.parameters)}`;
1091}
1092function serializeMatrixParams(params) {
1093 return Object.keys(params)
1094 .map(key => `;${encodeUriSegment(key)}=${encodeUriSegment(params[key])}`)
1095 .join('');
1096}
1097function serializeQueryParams(params) {
1098 const strParams = Object.keys(params).map((name) => {
1099 const value = params[name];
1100 return Array.isArray(value) ?
1101 value.map(v => `${encodeUriQuery(name)}=${encodeUriQuery(v)}`).join('&') :
1102 `${encodeUriQuery(name)}=${encodeUriQuery(value)}`;
1103 });
1104 return strParams.length ? `?${strParams.join('&')}` : '';
1105}
1106const SEGMENT_RE = /^[^\/()?;=#]+/;
1107function matchSegments(str) {
1108 const match = str.match(SEGMENT_RE);
1109 return match ? match[0] : '';
1110}
1111const QUERY_PARAM_RE = /^[^=?&#]+/;
1112// Return the name of the query param at the start of the string or an empty string
1113function matchQueryParams(str) {
1114 const match = str.match(QUERY_PARAM_RE);
1115 return match ? match[0] : '';
1116}
1117const QUERY_PARAM_VALUE_RE = /^[^?&#]+/;
1118// Return the value of the query param at the start of the string or an empty string
1119function matchUrlQueryParamValue(str) {
1120 const match = str.match(QUERY_PARAM_VALUE_RE);
1121 return match ? match[0] : '';
1122}
1123class UrlParser {
1124 constructor(url) {
1125 this.url = url;
1126 this.remaining = url;
1127 }
1128 parseRootSegment() {
1129 this.consumeOptional('/');
1130 if (this.remaining === '' || this.peekStartsWith('?') || this.peekStartsWith('#')) {
1131 return new UrlSegmentGroup([], {});
1132 }
1133 // The root segment group never has segments
1134 return new UrlSegmentGroup([], this.parseChildren());
1135 }
1136 parseQueryParams() {
1137 const params = {};
1138 if (this.consumeOptional('?')) {
1139 do {
1140 this.parseQueryParam(params);
1141 } while (this.consumeOptional('&'));
1142 }
1143 return params;
1144 }
1145 parseFragment() {
1146 return this.consumeOptional('#') ? decodeURIComponent(this.remaining) : null;
1147 }
1148 parseChildren() {
1149 if (this.remaining === '') {
1150 return {};
1151 }
1152 this.consumeOptional('/');
1153 const segments = [];
1154 if (!this.peekStartsWith('(')) {
1155 segments.push(this.parseSegment());
1156 }
1157 while (this.peekStartsWith('/') && !this.peekStartsWith('//') && !this.peekStartsWith('/(')) {
1158 this.capture('/');
1159 segments.push(this.parseSegment());
1160 }
1161 let children = {};
1162 if (this.peekStartsWith('/(')) {
1163 this.capture('/');
1164 children = this.parseParens(true);
1165 }
1166 let res = {};
1167 if (this.peekStartsWith('(')) {
1168 res = this.parseParens(false);
1169 }
1170 if (segments.length > 0 || Object.keys(children).length > 0) {
1171 res[PRIMARY_OUTLET] = new UrlSegmentGroup(segments, children);
1172 }
1173 return res;
1174 }
1175 // parse a segment with its matrix parameters
1176 // ie `name;k1=v1;k2`
1177 parseSegment() {
1178 const path = matchSegments(this.remaining);
1179 if (path === '' && this.peekStartsWith(';')) {
1180 throw new Error(`Empty path url segment cannot have parameters: '${this.remaining}'.`);
1181 }
1182 this.capture(path);
1183 return new UrlSegment(decode(path), this.parseMatrixParams());
1184 }
1185 parseMatrixParams() {
1186 const params = {};
1187 while (this.consumeOptional(';')) {
1188 this.parseParam(params);
1189 }
1190 return params;
1191 }
1192 parseParam(params) {
1193 const key = matchSegments(this.remaining);
1194 if (!key) {
1195 return;
1196 }
1197 this.capture(key);
1198 let value = '';
1199 if (this.consumeOptional('=')) {
1200 const valueMatch = matchSegments(this.remaining);
1201 if (valueMatch) {
1202 value = valueMatch;
1203 this.capture(value);
1204 }
1205 }
1206 params[decode(key)] = decode(value);
1207 }
1208 // Parse a single query parameter `name[=value]`
1209 parseQueryParam(params) {
1210 const key = matchQueryParams(this.remaining);
1211 if (!key) {
1212 return;
1213 }
1214 this.capture(key);
1215 let value = '';
1216 if (this.consumeOptional('=')) {
1217 const valueMatch = matchUrlQueryParamValue(this.remaining);
1218 if (valueMatch) {
1219 value = valueMatch;
1220 this.capture(value);
1221 }
1222 }
1223 const decodedKey = decodeQuery(key);
1224 const decodedVal = decodeQuery(value);
1225 if (params.hasOwnProperty(decodedKey)) {
1226 // Append to existing values
1227 let currentVal = params[decodedKey];
1228 if (!Array.isArray(currentVal)) {
1229 currentVal = [currentVal];
1230 params[decodedKey] = currentVal;
1231 }
1232 currentVal.push(decodedVal);
1233 }
1234 else {
1235 // Create a new value
1236 params[decodedKey] = decodedVal;
1237 }
1238 }
1239 // parse `(a/b//outlet_name:c/d)`
1240 parseParens(allowPrimary) {
1241 const segments = {};
1242 this.capture('(');
1243 while (!this.consumeOptional(')') && this.remaining.length > 0) {
1244 const path = matchSegments(this.remaining);
1245 const next = this.remaining[path.length];
1246 // if is is not one of these characters, then the segment was unescaped
1247 // or the group was not closed
1248 if (next !== '/' && next !== ')' && next !== ';') {
1249 throw new Error(`Cannot parse url '${this.url}'`);
1250 }
1251 let outletName = undefined;
1252 if (path.indexOf(':') > -1) {
1253 outletName = path.substr(0, path.indexOf(':'));
1254 this.capture(outletName);
1255 this.capture(':');
1256 }
1257 else if (allowPrimary) {
1258 outletName = PRIMARY_OUTLET;
1259 }
1260 const children = this.parseChildren();
1261 segments[outletName] = Object.keys(children).length === 1 ? children[PRIMARY_OUTLET] :
1262 new UrlSegmentGroup([], children);
1263 this.consumeOptional('//');
1264 }
1265 return segments;
1266 }
1267 peekStartsWith(str) {
1268 return this.remaining.startsWith(str);
1269 }
1270 // Consumes the prefix when it is present and returns whether it has been consumed
1271 consumeOptional(str) {
1272 if (this.peekStartsWith(str)) {
1273 this.remaining = this.remaining.substring(str.length);
1274 return true;
1275 }
1276 return false;
1277 }
1278 capture(str) {
1279 if (!this.consumeOptional(str)) {
1280 throw new Error(`Expected "${str}".`);
1281 }
1282 }
1283}
1284
1285/**
1286 * @license
1287 * Copyright Google LLC All Rights Reserved.
1288 *
1289 * Use of this source code is governed by an MIT-style license that can be
1290 * found in the LICENSE file at https://angular.io/license
1291 */
1292class Tree {
1293 constructor(root) {
1294 this._root = root;
1295 }
1296 get root() {
1297 return this._root.value;
1298 }
1299 /**
1300 * @internal
1301 */
1302 parent(t) {
1303 const p = this.pathFromRoot(t);
1304 return p.length > 1 ? p[p.length - 2] : null;
1305 }
1306 /**
1307 * @internal
1308 */
1309 children(t) {
1310 const n = findNode(t, this._root);
1311 return n ? n.children.map(t => t.value) : [];
1312 }
1313 /**
1314 * @internal
1315 */
1316 firstChild(t) {
1317 const n = findNode(t, this._root);
1318 return n && n.children.length > 0 ? n.children[0].value : null;
1319 }
1320 /**
1321 * @internal
1322 */
1323 siblings(t) {
1324 const p = findPath(t, this._root);
1325 if (p.length < 2)
1326 return [];
1327 const c = p[p.length - 2].children.map(c => c.value);
1328 return c.filter(cc => cc !== t);
1329 }
1330 /**
1331 * @internal
1332 */
1333 pathFromRoot(t) {
1334 return findPath(t, this._root).map(s => s.value);
1335 }
1336}
1337// DFS for the node matching the value
1338function findNode(value, node) {
1339 if (value === node.value)
1340 return node;
1341 for (const child of node.children) {
1342 const node = findNode(value, child);
1343 if (node)
1344 return node;
1345 }
1346 return null;
1347}
1348// Return the path to the node with the given value using DFS
1349function findPath(value, node) {
1350 if (value === node.value)
1351 return [node];
1352 for (const child of node.children) {
1353 const path = findPath(value, child);
1354 if (path.length) {
1355 path.unshift(node);
1356 return path;
1357 }
1358 }
1359 return [];
1360}
1361class TreeNode {
1362 constructor(value, children) {
1363 this.value = value;
1364 this.children = children;
1365 }
1366 toString() {
1367 return `TreeNode(${this.value})`;
1368 }
1369}
1370// Return the list of T indexed by outlet name
1371function nodeChildrenAsMap(node) {
1372 const map = {};
1373 if (node) {
1374 node.children.forEach(child => map[child.value.outlet] = child);
1375 }
1376 return map;
1377}
1378
1379/**
1380 * @license
1381 * Copyright Google LLC All Rights Reserved.
1382 *
1383 * Use of this source code is governed by an MIT-style license that can be
1384 * found in the LICENSE file at https://angular.io/license
1385 */
1386/**
1387 * Represents the state of the router as a tree of activated routes.
1388 *
1389 * @usageNotes
1390 *
1391 * Every node in the route tree is an `ActivatedRoute` instance
1392 * that knows about the "consumed" URL segments, the extracted parameters,
1393 * and the resolved data.
1394 * Use the `ActivatedRoute` properties to traverse the tree from any node.
1395 *
1396 * The following fragment shows how a component gets the root node
1397 * of the current state to establish its own route tree:
1398 *
1399 * ```
1400 * @Component({templateUrl:'template.html'})
1401 * class MyComponent {
1402 * constructor(router: Router) {
1403 * const state: RouterState = router.routerState;
1404 * const root: ActivatedRoute = state.root;
1405 * const child = root.firstChild;
1406 * const id: Observable<string> = child.params.map(p => p.id);
1407 * //...
1408 * }
1409 * }
1410 * ```
1411 *
1412 * @see `ActivatedRoute`
1413 * @see [Getting route information](guide/router#getting-route-information)
1414 *
1415 * @publicApi
1416 */
1417class RouterState extends Tree {
1418 /** @internal */
1419 constructor(root,
1420 /** The current snapshot of the router state */
1421 snapshot) {
1422 super(root);
1423 this.snapshot = snapshot;
1424 setRouterState(this, root);
1425 }
1426 toString() {
1427 return this.snapshot.toString();
1428 }
1429}
1430function createEmptyState(urlTree, rootComponent) {
1431 const snapshot = createEmptyStateSnapshot(urlTree, rootComponent);
1432 const emptyUrl = new BehaviorSubject([new UrlSegment('', {})]);
1433 const emptyParams = new BehaviorSubject({});
1434 const emptyData = new BehaviorSubject({});
1435 const emptyQueryParams = new BehaviorSubject({});
1436 const fragment = new BehaviorSubject('');
1437 const activated = new ActivatedRoute(emptyUrl, emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, snapshot.root);
1438 activated.snapshot = snapshot.root;
1439 return new RouterState(new TreeNode(activated, []), snapshot);
1440}
1441function createEmptyStateSnapshot(urlTree, rootComponent) {
1442 const emptyParams = {};
1443 const emptyData = {};
1444 const emptyQueryParams = {};
1445 const fragment = '';
1446 const activated = new ActivatedRouteSnapshot([], emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, null, urlTree.root, -1, {});
1447 return new RouterStateSnapshot('', new TreeNode(activated, []));
1448}
1449/**
1450 * Provides access to information about a route associated with a component
1451 * that is loaded in an outlet.
1452 * Use to traverse the `RouterState` tree and extract information from nodes.
1453 *
1454 * The following example shows how to construct a component using information from a
1455 * currently activated route.
1456 *
1457 * {@example router/activated-route/module.ts region="activated-route"
1458 * header="activated-route.component.ts"}
1459 *
1460 * @see [Getting route information](guide/router#getting-route-information)
1461 *
1462 * @publicApi
1463 */
1464class ActivatedRoute {
1465 /** @internal */
1466 constructor(
1467 /** An observable of the URL segments matched by this route. */
1468 url,
1469 /** An observable of the matrix parameters scoped to this route. */
1470 params,
1471 /** An observable of the query parameters shared by all the routes. */
1472 queryParams,
1473 /** An observable of the URL fragment shared by all the routes. */
1474 fragment,
1475 /** An observable of the static and resolved data of this route. */
1476 data,
1477 /** The outlet name of the route, a constant. */
1478 outlet,
1479 /** The component of the route, a constant. */
1480 // TODO(vsavkin): remove |string
1481 component, futureSnapshot) {
1482 this.url = url;
1483 this.params = params;
1484 this.queryParams = queryParams;
1485 this.fragment = fragment;
1486 this.data = data;
1487 this.outlet = outlet;
1488 this.component = component;
1489 this._futureSnapshot = futureSnapshot;
1490 }
1491 /** The configuration used to match this route. */
1492 get routeConfig() {
1493 return this._futureSnapshot.routeConfig;
1494 }
1495 /** The root of the router state. */
1496 get root() {
1497 return this._routerState.root;
1498 }
1499 /** The parent of this route in the router state tree. */
1500 get parent() {
1501 return this._routerState.parent(this);
1502 }
1503 /** The first child of this route in the router state tree. */
1504 get firstChild() {
1505 return this._routerState.firstChild(this);
1506 }
1507 /** The children of this route in the router state tree. */
1508 get children() {
1509 return this._routerState.children(this);
1510 }
1511 /** The path from the root of the router state tree to this route. */
1512 get pathFromRoot() {
1513 return this._routerState.pathFromRoot(this);
1514 }
1515 /**
1516 * An Observable that contains a map of the required and optional parameters
1517 * specific to the route.
1518 * The map supports retrieving single and multiple values from the same parameter.
1519 */
1520 get paramMap() {
1521 if (!this._paramMap) {
1522 this._paramMap = this.params.pipe(map((p) => convertToParamMap(p)));
1523 }
1524 return this._paramMap;
1525 }
1526 /**
1527 * An Observable that contains a map of the query parameters available to all routes.
1528 * The map supports retrieving single and multiple values from the query parameter.
1529 */
1530 get queryParamMap() {
1531 if (!this._queryParamMap) {
1532 this._queryParamMap =
1533 this.queryParams.pipe(map((p) => convertToParamMap(p)));
1534 }
1535 return this._queryParamMap;
1536 }
1537 toString() {
1538 return this.snapshot ? this.snapshot.toString() : `Future(${this._futureSnapshot})`;
1539 }
1540}
1541/**
1542 * Returns the inherited params, data, and resolve for a given route.
1543 * By default, this only inherits values up to the nearest path-less or component-less route.
1544 * @internal
1545 */
1546function inheritedParamsDataResolve(route, paramsInheritanceStrategy = 'emptyOnly') {
1547 const pathFromRoot = route.pathFromRoot;
1548 let inheritingStartingFrom = 0;
1549 if (paramsInheritanceStrategy !== 'always') {
1550 inheritingStartingFrom = pathFromRoot.length - 1;
1551 while (inheritingStartingFrom >= 1) {
1552 const current = pathFromRoot[inheritingStartingFrom];
1553 const parent = pathFromRoot[inheritingStartingFrom - 1];
1554 // current route is an empty path => inherits its parent's params and data
1555 if (current.routeConfig && current.routeConfig.path === '') {
1556 inheritingStartingFrom--;
1557 // parent is componentless => current route should inherit its params and data
1558 }
1559 else if (!parent.component) {
1560 inheritingStartingFrom--;
1561 }
1562 else {
1563 break;
1564 }
1565 }
1566 }
1567 return flattenInherited(pathFromRoot.slice(inheritingStartingFrom));
1568}
1569/** @internal */
1570function flattenInherited(pathFromRoot) {
1571 return pathFromRoot.reduce((res, curr) => {
1572 const params = Object.assign(Object.assign({}, res.params), curr.params);
1573 const data = Object.assign(Object.assign({}, res.data), curr.data);
1574 const resolve = Object.assign(Object.assign({}, res.resolve), curr._resolvedData);
1575 return { params, data, resolve };
1576 }, { params: {}, data: {}, resolve: {} });
1577}
1578/**
1579 * @description
1580 *
1581 * Contains the information about a route associated with a component loaded in an
1582 * outlet at a particular moment in time. ActivatedRouteSnapshot can also be used to
1583 * traverse the router state tree.
1584 *
1585 * The following example initializes a component with route information extracted
1586 * from the snapshot of the root node at the time of creation.
1587 *
1588 * ```
1589 * @Component({templateUrl:'./my-component.html'})
1590 * class MyComponent {
1591 * constructor(route: ActivatedRoute) {
1592 * const id: string = route.snapshot.params.id;
1593 * const url: string = route.snapshot.url.join('');
1594 * const user = route.snapshot.data.user;
1595 * }
1596 * }
1597 * ```
1598 *
1599 * @publicApi
1600 */
1601class ActivatedRouteSnapshot {
1602 /** @internal */
1603 constructor(
1604 /** The URL segments matched by this route */
1605 url,
1606 /** The matrix parameters scoped to this route */
1607 params,
1608 /** The query parameters shared by all the routes */
1609 queryParams,
1610 /** The URL fragment shared by all the routes */
1611 fragment,
1612 /** The static and resolved data of this route */
1613 data,
1614 /** The outlet name of the route */
1615 outlet,
1616 /** The component of the route */
1617 component, routeConfig, urlSegment, lastPathIndex, resolve) {
1618 this.url = url;
1619 this.params = params;
1620 this.queryParams = queryParams;
1621 this.fragment = fragment;
1622 this.data = data;
1623 this.outlet = outlet;
1624 this.component = component;
1625 this.routeConfig = routeConfig;
1626 this._urlSegment = urlSegment;
1627 this._lastPathIndex = lastPathIndex;
1628 this._resolve = resolve;
1629 }
1630 /** The root of the router state */
1631 get root() {
1632 return this._routerState.root;
1633 }
1634 /** The parent of this route in the router state tree */
1635 get parent() {
1636 return this._routerState.parent(this);
1637 }
1638 /** The first child of this route in the router state tree */
1639 get firstChild() {
1640 return this._routerState.firstChild(this);
1641 }
1642 /** The children of this route in the router state tree */
1643 get children() {
1644 return this._routerState.children(this);
1645 }
1646 /** The path from the root of the router state tree to this route */
1647 get pathFromRoot() {
1648 return this._routerState.pathFromRoot(this);
1649 }
1650 get paramMap() {
1651 if (!this._paramMap) {
1652 this._paramMap = convertToParamMap(this.params);
1653 }
1654 return this._paramMap;
1655 }
1656 get queryParamMap() {
1657 if (!this._queryParamMap) {
1658 this._queryParamMap = convertToParamMap(this.queryParams);
1659 }
1660 return this._queryParamMap;
1661 }
1662 toString() {
1663 const url = this.url.map(segment => segment.toString()).join('/');
1664 const matched = this.routeConfig ? this.routeConfig.path : '';
1665 return `Route(url:'${url}', path:'${matched}')`;
1666 }
1667}
1668/**
1669 * @description
1670 *
1671 * Represents the state of the router at a moment in time.
1672 *
1673 * This is a tree of activated route snapshots. Every node in this tree knows about
1674 * the "consumed" URL segments, the extracted parameters, and the resolved data.
1675 *
1676 * The following example shows how a component is initialized with information
1677 * from the snapshot of the root node's state at the time of creation.
1678 *
1679 * ```
1680 * @Component({templateUrl:'template.html'})
1681 * class MyComponent {
1682 * constructor(router: Router) {
1683 * const state: RouterState = router.routerState;
1684 * const snapshot: RouterStateSnapshot = state.snapshot;
1685 * const root: ActivatedRouteSnapshot = snapshot.root;
1686 * const child = root.firstChild;
1687 * const id: Observable<string> = child.params.map(p => p.id);
1688 * //...
1689 * }
1690 * }
1691 * ```
1692 *
1693 * @publicApi
1694 */
1695class RouterStateSnapshot extends Tree {
1696 /** @internal */
1697 constructor(
1698 /** The url from which this snapshot was created */
1699 url, root) {
1700 super(root);
1701 this.url = url;
1702 setRouterState(this, root);
1703 }
1704 toString() {
1705 return serializeNode(this._root);
1706 }
1707}
1708function setRouterState(state, node) {
1709 node.value._routerState = state;
1710 node.children.forEach(c => setRouterState(state, c));
1711}
1712function serializeNode(node) {
1713 const c = node.children.length > 0 ? ` { ${node.children.map(serializeNode).join(', ')} } ` : '';
1714 return `${node.value}${c}`;
1715}
1716/**
1717 * The expectation is that the activate route is created with the right set of parameters.
1718 * So we push new values into the observables only when they are not the initial values.
1719 * And we detect that by checking if the snapshot field is set.
1720 */
1721function advanceActivatedRoute(route) {
1722 if (route.snapshot) {
1723 const currentSnapshot = route.snapshot;
1724 const nextSnapshot = route._futureSnapshot;
1725 route.snapshot = nextSnapshot;
1726 if (!shallowEqual(currentSnapshot.queryParams, nextSnapshot.queryParams)) {
1727 route.queryParams.next(nextSnapshot.queryParams);
1728 }
1729 if (currentSnapshot.fragment !== nextSnapshot.fragment) {
1730 route.fragment.next(nextSnapshot.fragment);
1731 }
1732 if (!shallowEqual(currentSnapshot.params, nextSnapshot.params)) {
1733 route.params.next(nextSnapshot.params);
1734 }
1735 if (!shallowEqualArrays(currentSnapshot.url, nextSnapshot.url)) {
1736 route.url.next(nextSnapshot.url);
1737 }
1738 if (!shallowEqual(currentSnapshot.data, nextSnapshot.data)) {
1739 route.data.next(nextSnapshot.data);
1740 }
1741 }
1742 else {
1743 route.snapshot = route._futureSnapshot;
1744 // this is for resolved data
1745 route.data.next(route._futureSnapshot.data);
1746 }
1747}
1748function equalParamsAndUrlSegments(a, b) {
1749 const equalUrlParams = shallowEqual(a.params, b.params) && equalSegments(a.url, b.url);
1750 const parentsMismatch = !a.parent !== !b.parent;
1751 return equalUrlParams && !parentsMismatch &&
1752 (!a.parent || equalParamsAndUrlSegments(a.parent, b.parent));
1753}
1754
1755/**
1756 * @license
1757 * Copyright Google LLC All Rights Reserved.
1758 *
1759 * Use of this source code is governed by an MIT-style license that can be
1760 * found in the LICENSE file at https://angular.io/license
1761 */
1762function createRouterState(routeReuseStrategy, curr, prevState) {
1763 const root = createNode(routeReuseStrategy, curr._root, prevState ? prevState._root : undefined);
1764 return new RouterState(root, curr);
1765}
1766function createNode(routeReuseStrategy, curr, prevState) {
1767 // reuse an activated route that is currently displayed on the screen
1768 if (prevState && routeReuseStrategy.shouldReuseRoute(curr.value, prevState.value.snapshot)) {
1769 const value = prevState.value;
1770 value._futureSnapshot = curr.value;
1771 const children = createOrReuseChildren(routeReuseStrategy, curr, prevState);
1772 return new TreeNode(value, children);
1773 // retrieve an activated route that is used to be displayed, but is not currently displayed
1774 }
1775 else {
1776 const detachedRouteHandle = routeReuseStrategy.retrieve(curr.value);
1777 if (detachedRouteHandle) {
1778 const tree = detachedRouteHandle.route;
1779 setFutureSnapshotsOfActivatedRoutes(curr, tree);
1780 return tree;
1781 }
1782 else {
1783 const value = createActivatedRoute(curr.value);
1784 const children = curr.children.map(c => createNode(routeReuseStrategy, c));
1785 return new TreeNode(value, children);
1786 }
1787 }
1788}
1789function setFutureSnapshotsOfActivatedRoutes(curr, result) {
1790 if (curr.value.routeConfig !== result.value.routeConfig) {
1791 throw new Error('Cannot reattach ActivatedRouteSnapshot created from a different route');
1792 }
1793 if (curr.children.length !== result.children.length) {
1794 throw new Error('Cannot reattach ActivatedRouteSnapshot with a different number of children');
1795 }
1796 result.value._futureSnapshot = curr.value;
1797 for (let i = 0; i < curr.children.length; ++i) {
1798 setFutureSnapshotsOfActivatedRoutes(curr.children[i], result.children[i]);
1799 }
1800}
1801function createOrReuseChildren(routeReuseStrategy, curr, prevState) {
1802 return curr.children.map(child => {
1803 for (const p of prevState.children) {
1804 if (routeReuseStrategy.shouldReuseRoute(p.value.snapshot, child.value)) {
1805 return createNode(routeReuseStrategy, child, p);
1806 }
1807 }
1808 return createNode(routeReuseStrategy, child);
1809 });
1810}
1811function createActivatedRoute(c) {
1812 return new ActivatedRoute(new BehaviorSubject(c.url), new BehaviorSubject(c.params), new BehaviorSubject(c.queryParams), new BehaviorSubject(c.fragment), new BehaviorSubject(c.data), c.outlet, c.component, c);
1813}
1814
1815/**
1816 * @license
1817 * Copyright Google LLC All Rights Reserved.
1818 *
1819 * Use of this source code is governed by an MIT-style license that can be
1820 * found in the LICENSE file at https://angular.io/license
1821 */
1822function createUrlTree(route, urlTree, commands, queryParams, fragment) {
1823 if (commands.length === 0) {
1824 return tree(urlTree.root, urlTree.root, urlTree, queryParams, fragment);
1825 }
1826 const nav = computeNavigation(commands);
1827 if (nav.toRoot()) {
1828 return tree(urlTree.root, new UrlSegmentGroup([], {}), urlTree, queryParams, fragment);
1829 }
1830 const startingPosition = findStartingPosition(nav, urlTree, route);
1831 const segmentGroup = startingPosition.processChildren ?
1832 updateSegmentGroupChildren(startingPosition.segmentGroup, startingPosition.index, nav.commands) :
1833 updateSegmentGroup(startingPosition.segmentGroup, startingPosition.index, nav.commands);
1834 return tree(startingPosition.segmentGroup, segmentGroup, urlTree, queryParams, fragment);
1835}
1836function isMatrixParams(command) {
1837 return typeof command === 'object' && command != null && !command.outlets && !command.segmentPath;
1838}
1839function tree(oldSegmentGroup, newSegmentGroup, urlTree, queryParams, fragment) {
1840 let qp = {};
1841 if (queryParams) {
1842 forEach(queryParams, (value, name) => {
1843 qp[name] = Array.isArray(value) ? value.map((v) => `${v}`) : `${value}`;
1844 });
1845 }
1846 if (urlTree.root === oldSegmentGroup) {
1847 return new UrlTree(newSegmentGroup, qp, fragment);
1848 }
1849 return new UrlTree(replaceSegment(urlTree.root, oldSegmentGroup, newSegmentGroup), qp, fragment);
1850}
1851function replaceSegment(current, oldSegment, newSegment) {
1852 const children = {};
1853 forEach(current.children, (c, outletName) => {
1854 if (c === oldSegment) {
1855 children[outletName] = newSegment;
1856 }
1857 else {
1858 children[outletName] = replaceSegment(c, oldSegment, newSegment);
1859 }
1860 });
1861 return new UrlSegmentGroup(current.segments, children);
1862}
1863class Navigation {
1864 constructor(isAbsolute, numberOfDoubleDots, commands) {
1865 this.isAbsolute = isAbsolute;
1866 this.numberOfDoubleDots = numberOfDoubleDots;
1867 this.commands = commands;
1868 if (isAbsolute && commands.length > 0 && isMatrixParams(commands[0])) {
1869 throw new Error('Root segment cannot have matrix parameters');
1870 }
1871 const cmdWithOutlet = commands.find(c => typeof c === 'object' && c != null && c.outlets);
1872 if (cmdWithOutlet && cmdWithOutlet !== last(commands)) {
1873 throw new Error('{outlets:{}} has to be the last command');
1874 }
1875 }
1876 toRoot() {
1877 return this.isAbsolute && this.commands.length === 1 && this.commands[0] == '/';
1878 }
1879}
1880/** Transforms commands to a normalized `Navigation` */
1881function computeNavigation(commands) {
1882 if ((typeof commands[0] === 'string') && commands.length === 1 && commands[0] === '/') {
1883 return new Navigation(true, 0, commands);
1884 }
1885 let numberOfDoubleDots = 0;
1886 let isAbsolute = false;
1887 const res = commands.reduce((res, cmd, cmdIdx) => {
1888 if (typeof cmd === 'object' && cmd != null) {
1889 if (cmd.outlets) {
1890 const outlets = {};
1891 forEach(cmd.outlets, (commands, name) => {
1892 outlets[name] = typeof commands === 'string' ? commands.split('/') : commands;
1893 });
1894 return [...res, { outlets }];
1895 }
1896 if (cmd.segmentPath) {
1897 return [...res, cmd.segmentPath];
1898 }
1899 }
1900 if (!(typeof cmd === 'string')) {
1901 return [...res, cmd];
1902 }
1903 if (cmdIdx === 0) {
1904 cmd.split('/').forEach((urlPart, partIndex) => {
1905 if (partIndex == 0 && urlPart === '.') {
1906 // skip './a'
1907 }
1908 else if (partIndex == 0 && urlPart === '') { // '/a'
1909 isAbsolute = true;
1910 }
1911 else if (urlPart === '..') { // '../a'
1912 numberOfDoubleDots++;
1913 }
1914 else if (urlPart != '') {
1915 res.push(urlPart);
1916 }
1917 });
1918 return res;
1919 }
1920 return [...res, cmd];
1921 }, []);
1922 return new Navigation(isAbsolute, numberOfDoubleDots, res);
1923}
1924class Position {
1925 constructor(segmentGroup, processChildren, index) {
1926 this.segmentGroup = segmentGroup;
1927 this.processChildren = processChildren;
1928 this.index = index;
1929 }
1930}
1931function findStartingPosition(nav, tree, route) {
1932 if (nav.isAbsolute) {
1933 return new Position(tree.root, true, 0);
1934 }
1935 if (route.snapshot._lastPathIndex === -1) {
1936 const segmentGroup = route.snapshot._urlSegment;
1937 // Pathless ActivatedRoute has _lastPathIndex === -1 but should not process children
1938 // see issue #26224, #13011, #35687
1939 // However, if the ActivatedRoute is the root we should process children like above.
1940 const processChildren = segmentGroup === tree.root;
1941 return new Position(segmentGroup, processChildren, 0);
1942 }
1943 const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1;
1944 const index = route.snapshot._lastPathIndex + modifier;
1945 return createPositionApplyingDoubleDots(route.snapshot._urlSegment, index, nav.numberOfDoubleDots);
1946}
1947function createPositionApplyingDoubleDots(group, index, numberOfDoubleDots) {
1948 let g = group;
1949 let ci = index;
1950 let dd = numberOfDoubleDots;
1951 while (dd > ci) {
1952 dd -= ci;
1953 g = g.parent;
1954 if (!g) {
1955 throw new Error('Invalid number of \'../\'');
1956 }
1957 ci = g.segments.length;
1958 }
1959 return new Position(g, false, ci - dd);
1960}
1961function getPath(command) {
1962 if (typeof command === 'object' && command != null && command.outlets) {
1963 return command.outlets[PRIMARY_OUTLET];
1964 }
1965 return `${command}`;
1966}
1967function getOutlets(commands) {
1968 if (typeof commands[0] === 'object' && commands[0] !== null && commands[0].outlets) {
1969 return commands[0].outlets;
1970 }
1971 return { [PRIMARY_OUTLET]: commands };
1972}
1973function updateSegmentGroup(segmentGroup, startIndex, commands) {
1974 if (!segmentGroup) {
1975 segmentGroup = new UrlSegmentGroup([], {});
1976 }
1977 if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) {
1978 return updateSegmentGroupChildren(segmentGroup, startIndex, commands);
1979 }
1980 const m = prefixedWith(segmentGroup, startIndex, commands);
1981 const slicedCommands = commands.slice(m.commandIndex);
1982 if (m.match && m.pathIndex < segmentGroup.segments.length) {
1983 const g = new UrlSegmentGroup(segmentGroup.segments.slice(0, m.pathIndex), {});
1984 g.children[PRIMARY_OUTLET] =
1985 new UrlSegmentGroup(segmentGroup.segments.slice(m.pathIndex), segmentGroup.children);
1986 return updateSegmentGroupChildren(g, 0, slicedCommands);
1987 }
1988 else if (m.match && slicedCommands.length === 0) {
1989 return new UrlSegmentGroup(segmentGroup.segments, {});
1990 }
1991 else if (m.match && !segmentGroup.hasChildren()) {
1992 return createNewSegmentGroup(segmentGroup, startIndex, commands);
1993 }
1994 else if (m.match) {
1995 return updateSegmentGroupChildren(segmentGroup, 0, slicedCommands);
1996 }
1997 else {
1998 return createNewSegmentGroup(segmentGroup, startIndex, commands);
1999 }
2000}
2001function updateSegmentGroupChildren(segmentGroup, startIndex, commands) {
2002 if (commands.length === 0) {
2003 return new UrlSegmentGroup(segmentGroup.segments, {});
2004 }
2005 else {
2006 const outlets = getOutlets(commands);
2007 const children = {};
2008 forEach(outlets, (commands, outlet) => {
2009 if (commands !== null) {
2010 children[outlet] = updateSegmentGroup(segmentGroup.children[outlet], startIndex, commands);
2011 }
2012 });
2013 forEach(segmentGroup.children, (child, childOutlet) => {
2014 if (outlets[childOutlet] === undefined) {
2015 children[childOutlet] = child;
2016 }
2017 });
2018 return new UrlSegmentGroup(segmentGroup.segments, children);
2019 }
2020}
2021function prefixedWith(segmentGroup, startIndex, commands) {
2022 let currentCommandIndex = 0;
2023 let currentPathIndex = startIndex;
2024 const noMatch = { match: false, pathIndex: 0, commandIndex: 0 };
2025 while (currentPathIndex < segmentGroup.segments.length) {
2026 if (currentCommandIndex >= commands.length)
2027 return noMatch;
2028 const path = segmentGroup.segments[currentPathIndex];
2029 const curr = getPath(commands[currentCommandIndex]);
2030 const next = currentCommandIndex < commands.length - 1 ? commands[currentCommandIndex + 1] : null;
2031 if (currentPathIndex > 0 && curr === undefined)
2032 break;
2033 if (curr && next && (typeof next === 'object') && next.outlets === undefined) {
2034 if (!compare(curr, next, path))
2035 return noMatch;
2036 currentCommandIndex += 2;
2037 }
2038 else {
2039 if (!compare(curr, {}, path))
2040 return noMatch;
2041 currentCommandIndex++;
2042 }
2043 currentPathIndex++;
2044 }
2045 return { match: true, pathIndex: currentPathIndex, commandIndex: currentCommandIndex };
2046}
2047function createNewSegmentGroup(segmentGroup, startIndex, commands) {
2048 const paths = segmentGroup.segments.slice(0, startIndex);
2049 let i = 0;
2050 while (i < commands.length) {
2051 if (typeof commands[i] === 'object' && commands[i] !== null &&
2052 commands[i].outlets !== undefined) {
2053 const children = createNewSegmentChildren(commands[i].outlets);
2054 return new UrlSegmentGroup(paths, children);
2055 }
2056 // if we start with an object literal, we need to reuse the path part from the segment
2057 if (i === 0 && isMatrixParams(commands[0])) {
2058 const p = segmentGroup.segments[startIndex];
2059 paths.push(new UrlSegment(p.path, commands[0]));
2060 i++;
2061 continue;
2062 }
2063 const curr = getPath(commands[i]);
2064 const next = (i < commands.length - 1) ? commands[i + 1] : null;
2065 if (curr && next && isMatrixParams(next)) {
2066 paths.push(new UrlSegment(curr, stringify(next)));
2067 i += 2;
2068 }
2069 else {
2070 paths.push(new UrlSegment(curr, {}));
2071 i++;
2072 }
2073 }
2074 return new UrlSegmentGroup(paths, {});
2075}
2076function createNewSegmentChildren(outlets) {
2077 const children = {};
2078 forEach(outlets, (commands, outlet) => {
2079 if (commands !== null) {
2080 children[outlet] = createNewSegmentGroup(new UrlSegmentGroup([], {}), 0, commands);
2081 }
2082 });
2083 return children;
2084}
2085function stringify(params) {
2086 const res = {};
2087 forEach(params, (v, k) => res[k] = `${v}`);
2088 return res;
2089}
2090function compare(path, params, segment) {
2091 return path == segment.path && shallowEqual(params, segment.parameters);
2092}
2093
2094/**
2095 * @license
2096 * Copyright Google LLC All Rights Reserved.
2097 *
2098 * Use of this source code is governed by an MIT-style license that can be
2099 * found in the LICENSE file at https://angular.io/license
2100 */
2101const activateRoutes = (rootContexts, routeReuseStrategy, forwardEvent) => map(t => {
2102 new ActivateRoutes(routeReuseStrategy, t.targetRouterState, t.currentRouterState, forwardEvent)
2103 .activate(rootContexts);
2104 return t;
2105});
2106class ActivateRoutes {
2107 constructor(routeReuseStrategy, futureState, currState, forwardEvent) {
2108 this.routeReuseStrategy = routeReuseStrategy;
2109 this.futureState = futureState;
2110 this.currState = currState;
2111 this.forwardEvent = forwardEvent;
2112 }
2113 activate(parentContexts) {
2114 const futureRoot = this.futureState._root;
2115 const currRoot = this.currState ? this.currState._root : null;
2116 this.deactivateChildRoutes(futureRoot, currRoot, parentContexts);
2117 advanceActivatedRoute(this.futureState.root);
2118 this.activateChildRoutes(futureRoot, currRoot, parentContexts);
2119 }
2120 // De-activate the child route that are not re-used for the future state
2121 deactivateChildRoutes(futureNode, currNode, contexts) {
2122 const children = nodeChildrenAsMap(currNode);
2123 // Recurse on the routes active in the future state to de-activate deeper children
2124 futureNode.children.forEach(futureChild => {
2125 const childOutletName = futureChild.value.outlet;
2126 this.deactivateRoutes(futureChild, children[childOutletName], contexts);
2127 delete children[childOutletName];
2128 });
2129 // De-activate the routes that will not be re-used
2130 forEach(children, (v, childName) => {
2131 this.deactivateRouteAndItsChildren(v, contexts);
2132 });
2133 }
2134 deactivateRoutes(futureNode, currNode, parentContext) {
2135 const future = futureNode.value;
2136 const curr = currNode ? currNode.value : null;
2137 if (future === curr) {
2138 // Reusing the node, check to see if the children need to be de-activated
2139 if (future.component) {
2140 // If we have a normal route, we need to go through an outlet.
2141 const context = parentContext.getContext(future.outlet);
2142 if (context) {
2143 this.deactivateChildRoutes(futureNode, currNode, context.children);
2144 }
2145 }
2146 else {
2147 // if we have a componentless route, we recurse but keep the same outlet map.
2148 this.deactivateChildRoutes(futureNode, currNode, parentContext);
2149 }
2150 }
2151 else {
2152 if (curr) {
2153 // Deactivate the current route which will not be re-used
2154 this.deactivateRouteAndItsChildren(currNode, parentContext);
2155 }
2156 }
2157 }
2158 deactivateRouteAndItsChildren(route, parentContexts) {
2159 if (this.routeReuseStrategy.shouldDetach(route.value.snapshot)) {
2160 this.detachAndStoreRouteSubtree(route, parentContexts);
2161 }
2162 else {
2163 this.deactivateRouteAndOutlet(route, parentContexts);
2164 }
2165 }
2166 detachAndStoreRouteSubtree(route, parentContexts) {
2167 const context = parentContexts.getContext(route.value.outlet);
2168 if (context && context.outlet) {
2169 const componentRef = context.outlet.detach();
2170 const contexts = context.children.onOutletDeactivated();
2171 this.routeReuseStrategy.store(route.value.snapshot, { componentRef, route, contexts });
2172 }
2173 }
2174 deactivateRouteAndOutlet(route, parentContexts) {
2175 const context = parentContexts.getContext(route.value.outlet);
2176 if (context) {
2177 const children = nodeChildrenAsMap(route);
2178 const contexts = route.value.component ? context.children : parentContexts;
2179 forEach(children, (v, k) => this.deactivateRouteAndItsChildren(v, contexts));
2180 if (context.outlet) {
2181 // Destroy the component
2182 context.outlet.deactivate();
2183 // Destroy the contexts for all the outlets that were in the component
2184 context.children.onOutletDeactivated();
2185 }
2186 }
2187 }
2188 activateChildRoutes(futureNode, currNode, contexts) {
2189 const children = nodeChildrenAsMap(currNode);
2190 futureNode.children.forEach(c => {
2191 this.activateRoutes(c, children[c.value.outlet], contexts);
2192 this.forwardEvent(new ActivationEnd(c.value.snapshot));
2193 });
2194 if (futureNode.children.length) {
2195 this.forwardEvent(new ChildActivationEnd(futureNode.value.snapshot));
2196 }
2197 }
2198 activateRoutes(futureNode, currNode, parentContexts) {
2199 const future = futureNode.value;
2200 const curr = currNode ? currNode.value : null;
2201 advanceActivatedRoute(future);
2202 // reusing the node
2203 if (future === curr) {
2204 if (future.component) {
2205 // If we have a normal route, we need to go through an outlet.
2206 const context = parentContexts.getOrCreateContext(future.outlet);
2207 this.activateChildRoutes(futureNode, currNode, context.children);
2208 }
2209 else {
2210 // if we have a componentless route, we recurse but keep the same outlet map.
2211 this.activateChildRoutes(futureNode, currNode, parentContexts);
2212 }
2213 }
2214 else {
2215 if (future.component) {
2216 // if we have a normal route, we need to place the component into the outlet and recurse.
2217 const context = parentContexts.getOrCreateContext(future.outlet);
2218 if (this.routeReuseStrategy.shouldAttach(future.snapshot)) {
2219 const stored = this.routeReuseStrategy.retrieve(future.snapshot);
2220 this.routeReuseStrategy.store(future.snapshot, null);
2221 context.children.onOutletReAttached(stored.contexts);
2222 context.attachRef = stored.componentRef;
2223 context.route = stored.route.value;
2224 if (context.outlet) {
2225 // Attach right away when the outlet has already been instantiated
2226 // Otherwise attach from `RouterOutlet.ngOnInit` when it is instantiated
2227 context.outlet.attach(stored.componentRef, stored.route.value);
2228 }
2229 advanceActivatedRouteNodeAndItsChildren(stored.route);
2230 }
2231 else {
2232 const config = parentLoadedConfig(future.snapshot);
2233 const cmpFactoryResolver = config ? config.module.componentFactoryResolver : null;
2234 context.attachRef = null;
2235 context.route = future;
2236 context.resolver = cmpFactoryResolver;
2237 if (context.outlet) {
2238 // Activate the outlet when it has already been instantiated
2239 // Otherwise it will get activated from its `ngOnInit` when instantiated
2240 context.outlet.activateWith(future, cmpFactoryResolver);
2241 }
2242 this.activateChildRoutes(futureNode, null, context.children);
2243 }
2244 }
2245 else {
2246 // if we have a componentless route, we recurse but keep the same outlet map.
2247 this.activateChildRoutes(futureNode, null, parentContexts);
2248 }
2249 }
2250 }
2251}
2252function advanceActivatedRouteNodeAndItsChildren(node) {
2253 advanceActivatedRoute(node.value);
2254 node.children.forEach(advanceActivatedRouteNodeAndItsChildren);
2255}
2256function parentLoadedConfig(snapshot) {
2257 for (let s = snapshot.parent; s; s = s.parent) {
2258 const route = s.routeConfig;
2259 if (route && route._loadedConfig)
2260 return route._loadedConfig;
2261 if (route && route.component)
2262 return null;
2263 }
2264 return null;
2265}
2266
2267/**
2268 * @license
2269 * Copyright Google LLC All Rights Reserved.
2270 *
2271 * Use of this source code is governed by an MIT-style license that can be
2272 * found in the LICENSE file at https://angular.io/license
2273 */
2274/**
2275 * Simple function check, but generic so type inference will flow. Example:
2276 *
2277 * function product(a: number, b: number) {
2278 * return a * b;
2279 * }
2280 *
2281 * if (isFunction<product>(fn)) {
2282 * return fn(1, 2);
2283 * } else {
2284 * throw "Must provide the `product` function";
2285 * }
2286 */
2287function isFunction(v) {
2288 return typeof v === 'function';
2289}
2290function isBoolean(v) {
2291 return typeof v === 'boolean';
2292}
2293function isUrlTree(v) {
2294 return v instanceof UrlTree;
2295}
2296function isCanLoad(guard) {
2297 return guard && isFunction(guard.canLoad);
2298}
2299function isCanActivate(guard) {
2300 return guard && isFunction(guard.canActivate);
2301}
2302function isCanActivateChild(guard) {
2303 return guard && isFunction(guard.canActivateChild);
2304}
2305function isCanDeactivate(guard) {
2306 return guard && isFunction(guard.canDeactivate);
2307}
2308
2309/**
2310 * @license
2311 * Copyright Google LLC All Rights Reserved.
2312 *
2313 * Use of this source code is governed by an MIT-style license that can be
2314 * found in the LICENSE file at https://angular.io/license
2315 */
2316class NoMatch {
2317 constructor(segmentGroup) {
2318 this.segmentGroup = segmentGroup || null;
2319 }
2320}
2321class AbsoluteRedirect {
2322 constructor(urlTree) {
2323 this.urlTree = urlTree;
2324 }
2325}
2326function noMatch(segmentGroup) {
2327 return new Observable((obs) => obs.error(new NoMatch(segmentGroup)));
2328}
2329function absoluteRedirect(newTree) {
2330 return new Observable((obs) => obs.error(new AbsoluteRedirect(newTree)));
2331}
2332function namedOutletsRedirect(redirectTo) {
2333 return new Observable((obs) => obs.error(new Error(`Only absolute redirects can have named outlets. redirectTo: '${redirectTo}'`)));
2334}
2335function canLoadFails(route) {
2336 return new Observable((obs) => obs.error(navigationCancelingError(`Cannot load children because the guard of the route "path: '${route.path}'" returned false`)));
2337}
2338/**
2339 * Returns the `UrlTree` with the redirection applied.
2340 *
2341 * Lazy modules are loaded along the way.
2342 */
2343function applyRedirects(moduleInjector, configLoader, urlSerializer, urlTree, config) {
2344 return new ApplyRedirects(moduleInjector, configLoader, urlSerializer, urlTree, config).apply();
2345}
2346class ApplyRedirects {
2347 constructor(moduleInjector, configLoader, urlSerializer, urlTree, config) {
2348 this.configLoader = configLoader;
2349 this.urlSerializer = urlSerializer;
2350 this.urlTree = urlTree;
2351 this.config = config;
2352 this.allowRedirects = true;
2353 this.ngModule = moduleInjector.get(NgModuleRef);
2354 }
2355 apply() {
2356 const expanded$ = this.expandSegmentGroup(this.ngModule, this.config, this.urlTree.root, PRIMARY_OUTLET);
2357 const urlTrees$ = expanded$.pipe(map((rootSegmentGroup) => this.createUrlTree(rootSegmentGroup, this.urlTree.queryParams, this.urlTree.fragment)));
2358 return urlTrees$.pipe(catchError((e) => {
2359 if (e instanceof AbsoluteRedirect) {
2360 // after an absolute redirect we do not apply any more redirects!
2361 this.allowRedirects = false;
2362 // we need to run matching, so we can fetch all lazy-loaded modules
2363 return this.match(e.urlTree);
2364 }
2365 if (e instanceof NoMatch) {
2366 throw this.noMatchError(e);
2367 }
2368 throw e;
2369 }));
2370 }
2371 match(tree) {
2372 const expanded$ = this.expandSegmentGroup(this.ngModule, this.config, tree.root, PRIMARY_OUTLET);
2373 const mapped$ = expanded$.pipe(map((rootSegmentGroup) => this.createUrlTree(rootSegmentGroup, tree.queryParams, tree.fragment)));
2374 return mapped$.pipe(catchError((e) => {
2375 if (e instanceof NoMatch) {
2376 throw this.noMatchError(e);
2377 }
2378 throw e;
2379 }));
2380 }
2381 noMatchError(e) {
2382 return new Error(`Cannot match any routes. URL Segment: '${e.segmentGroup}'`);
2383 }
2384 createUrlTree(rootCandidate, queryParams, fragment) {
2385 const root = rootCandidate.segments.length > 0 ?
2386 new UrlSegmentGroup([], { [PRIMARY_OUTLET]: rootCandidate }) :
2387 rootCandidate;
2388 return new UrlTree(root, queryParams, fragment);
2389 }
2390 expandSegmentGroup(ngModule, routes, segmentGroup, outlet) {
2391 if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) {
2392 return this.expandChildren(ngModule, routes, segmentGroup)
2393 .pipe(map((children) => new UrlSegmentGroup([], children)));
2394 }
2395 return this.expandSegment(ngModule, segmentGroup, routes, segmentGroup.segments, outlet, true);
2396 }
2397 // Recursively expand segment groups for all the child outlets
2398 expandChildren(ngModule, routes, segmentGroup) {
2399 return waitForMap(segmentGroup.children, (childOutlet, child) => this.expandSegmentGroup(ngModule, routes, child, childOutlet));
2400 }
2401 expandSegment(ngModule, segmentGroup, routes, segments, outlet, allowRedirects) {
2402 return of(...routes).pipe(map((r) => {
2403 const expanded$ = this.expandSegmentAgainstRoute(ngModule, segmentGroup, routes, r, segments, outlet, allowRedirects);
2404 return expanded$.pipe(catchError((e) => {
2405 if (e instanceof NoMatch) {
2406 // TODO(i): this return type doesn't match the declared Observable<UrlSegmentGroup> -
2407 // talk to Jason
2408 return of(null);
2409 }
2410 throw e;
2411 }));
2412 }), concatAll(), first((s) => !!s), catchError((e, _) => {
2413 if (e instanceof EmptyError || e.name === 'EmptyError') {
2414 if (this.noLeftoversInUrl(segmentGroup, segments, outlet)) {
2415 return of(new UrlSegmentGroup([], {}));
2416 }
2417 throw new NoMatch(segmentGroup);
2418 }
2419 throw e;
2420 }));
2421 }
2422 noLeftoversInUrl(segmentGroup, segments, outlet) {
2423 return segments.length === 0 && !segmentGroup.children[outlet];
2424 }
2425 expandSegmentAgainstRoute(ngModule, segmentGroup, routes, route, paths, outlet, allowRedirects) {
2426 if (getOutlet(route) !== outlet) {
2427 return noMatch(segmentGroup);
2428 }
2429 if (route.redirectTo === undefined) {
2430 return this.matchSegmentAgainstRoute(ngModule, segmentGroup, route, paths);
2431 }
2432 if (allowRedirects && this.allowRedirects) {
2433 return this.expandSegmentAgainstRouteUsingRedirect(ngModule, segmentGroup, routes, route, paths, outlet);
2434 }
2435 return noMatch(segmentGroup);
2436 }
2437 expandSegmentAgainstRouteUsingRedirect(ngModule, segmentGroup, routes, route, segments, outlet) {
2438 if (route.path === '**') {
2439 return this.expandWildCardWithParamsAgainstRouteUsingRedirect(ngModule, routes, route, outlet);
2440 }
2441 return this.expandRegularSegmentAgainstRouteUsingRedirect(ngModule, segmentGroup, routes, route, segments, outlet);
2442 }
2443 expandWildCardWithParamsAgainstRouteUsingRedirect(ngModule, routes, route, outlet) {
2444 const newTree = this.applyRedirectCommands([], route.redirectTo, {});
2445 if (route.redirectTo.startsWith('/')) {
2446 return absoluteRedirect(newTree);
2447 }
2448 return this.lineralizeSegments(route, newTree).pipe(mergeMap((newSegments) => {
2449 const group = new UrlSegmentGroup(newSegments, {});
2450 return this.expandSegment(ngModule, group, routes, newSegments, outlet, false);
2451 }));
2452 }
2453 expandRegularSegmentAgainstRouteUsingRedirect(ngModule, segmentGroup, routes, route, segments, outlet) {
2454 const { matched, consumedSegments, lastChild, positionalParamSegments } = match(segmentGroup, route, segments);
2455 if (!matched)
2456 return noMatch(segmentGroup);
2457 const newTree = this.applyRedirectCommands(consumedSegments, route.redirectTo, positionalParamSegments);
2458 if (route.redirectTo.startsWith('/')) {
2459 return absoluteRedirect(newTree);
2460 }
2461 return this.lineralizeSegments(route, newTree).pipe(mergeMap((newSegments) => {
2462 return this.expandSegment(ngModule, segmentGroup, routes, newSegments.concat(segments.slice(lastChild)), outlet, false);
2463 }));
2464 }
2465 matchSegmentAgainstRoute(ngModule, rawSegmentGroup, route, segments) {
2466 if (route.path === '**') {
2467 if (route.loadChildren) {
2468 return this.configLoader.load(ngModule.injector, route)
2469 .pipe(map((cfg) => {
2470 route._loadedConfig = cfg;
2471 return new UrlSegmentGroup(segments, {});
2472 }));
2473 }
2474 return of(new UrlSegmentGroup(segments, {}));
2475 }
2476 const { matched, consumedSegments, lastChild } = match(rawSegmentGroup, route, segments);
2477 if (!matched)
2478 return noMatch(rawSegmentGroup);
2479 const rawSlicedSegments = segments.slice(lastChild);
2480 const childConfig$ = this.getChildConfig(ngModule, route, segments);
2481 return childConfig$.pipe(mergeMap((routerConfig) => {
2482 const childModule = routerConfig.module;
2483 const childConfig = routerConfig.routes;
2484 const { segmentGroup, slicedSegments } = split(rawSegmentGroup, consumedSegments, rawSlicedSegments, childConfig);
2485 if (slicedSegments.length === 0 && segmentGroup.hasChildren()) {
2486 const expanded$ = this.expandChildren(childModule, childConfig, segmentGroup);
2487 return expanded$.pipe(map((children) => new UrlSegmentGroup(consumedSegments, children)));
2488 }
2489 if (childConfig.length === 0 && slicedSegments.length === 0) {
2490 return of(new UrlSegmentGroup(consumedSegments, {}));
2491 }
2492 const expanded$ = this.expandSegment(childModule, segmentGroup, childConfig, slicedSegments, PRIMARY_OUTLET, true);
2493 return expanded$.pipe(map((cs) => new UrlSegmentGroup(consumedSegments.concat(cs.segments), cs.children)));
2494 }));
2495 }
2496 getChildConfig(ngModule, route, segments) {
2497 if (route.children) {
2498 // The children belong to the same module
2499 return of(new LoadedRouterConfig(route.children, ngModule));
2500 }
2501 if (route.loadChildren) {
2502 // lazy children belong to the loaded module
2503 if (route._loadedConfig !== undefined) {
2504 return of(route._loadedConfig);
2505 }
2506 return this.runCanLoadGuards(ngModule.injector, route, segments)
2507 .pipe(mergeMap((shouldLoadResult) => {
2508 if (shouldLoadResult) {
2509 return this.configLoader.load(ngModule.injector, route)
2510 .pipe(map((cfg) => {
2511 route._loadedConfig = cfg;
2512 return cfg;
2513 }));
2514 }
2515 return canLoadFails(route);
2516 }));
2517 }
2518 return of(new LoadedRouterConfig([], ngModule));
2519 }
2520 runCanLoadGuards(moduleInjector, route, segments) {
2521 const canLoad = route.canLoad;
2522 if (!canLoad || canLoad.length === 0)
2523 return of(true);
2524 const obs = from(canLoad).pipe(map((injectionToken) => {
2525 const guard = moduleInjector.get(injectionToken);
2526 let guardVal;
2527 if (isCanLoad(guard)) {
2528 guardVal = guard.canLoad(route, segments);
2529 }
2530 else if (isFunction(guard)) {
2531 guardVal = guard(route, segments);
2532 }
2533 else {
2534 throw new Error('Invalid CanLoad guard');
2535 }
2536 return wrapIntoObservable(guardVal);
2537 }));
2538 return obs.pipe(concatAll(), tap((result) => {
2539 if (!isUrlTree(result))
2540 return;
2541 const error = navigationCancelingError(`Redirecting to "${this.urlSerializer.serialize(result)}"`);
2542 error.url = result;
2543 throw error;
2544 }), every(result => result === true));
2545 }
2546 lineralizeSegments(route, urlTree) {
2547 let res = [];
2548 let c = urlTree.root;
2549 while (true) {
2550 res = res.concat(c.segments);
2551 if (c.numberOfChildren === 0) {
2552 return of(res);
2553 }
2554 if (c.numberOfChildren > 1 || !c.children[PRIMARY_OUTLET]) {
2555 return namedOutletsRedirect(route.redirectTo);
2556 }
2557 c = c.children[PRIMARY_OUTLET];
2558 }
2559 }
2560 applyRedirectCommands(segments, redirectTo, posParams) {
2561 return this.applyRedirectCreatreUrlTree(redirectTo, this.urlSerializer.parse(redirectTo), segments, posParams);
2562 }
2563 applyRedirectCreatreUrlTree(redirectTo, urlTree, segments, posParams) {
2564 const newRoot = this.createSegmentGroup(redirectTo, urlTree.root, segments, posParams);
2565 return new UrlTree(newRoot, this.createQueryParams(urlTree.queryParams, this.urlTree.queryParams), urlTree.fragment);
2566 }
2567 createQueryParams(redirectToParams, actualParams) {
2568 const res = {};
2569 forEach(redirectToParams, (v, k) => {
2570 const copySourceValue = typeof v === 'string' && v.startsWith(':');
2571 if (copySourceValue) {
2572 const sourceName = v.substring(1);
2573 res[k] = actualParams[sourceName];
2574 }
2575 else {
2576 res[k] = v;
2577 }
2578 });
2579 return res;
2580 }
2581 createSegmentGroup(redirectTo, group, segments, posParams) {
2582 const updatedSegments = this.createSegments(redirectTo, group.segments, segments, posParams);
2583 let children = {};
2584 forEach(group.children, (child, name) => {
2585 children[name] = this.createSegmentGroup(redirectTo, child, segments, posParams);
2586 });
2587 return new UrlSegmentGroup(updatedSegments, children);
2588 }
2589 createSegments(redirectTo, redirectToSegments, actualSegments, posParams) {
2590 return redirectToSegments.map(s => s.path.startsWith(':') ? this.findPosParam(redirectTo, s, posParams) :
2591 this.findOrReturn(s, actualSegments));
2592 }
2593 findPosParam(redirectTo, redirectToUrlSegment, posParams) {
2594 const pos = posParams[redirectToUrlSegment.path.substring(1)];
2595 if (!pos)
2596 throw new Error(`Cannot redirect to '${redirectTo}'. Cannot find '${redirectToUrlSegment.path}'.`);
2597 return pos;
2598 }
2599 findOrReturn(redirectToUrlSegment, actualSegments) {
2600 let idx = 0;
2601 for (const s of actualSegments) {
2602 if (s.path === redirectToUrlSegment.path) {
2603 actualSegments.splice(idx);
2604 return s;
2605 }
2606 idx++;
2607 }
2608 return redirectToUrlSegment;
2609 }
2610}
2611function match(segmentGroup, route, segments) {
2612 if (route.path === '') {
2613 if ((route.pathMatch === 'full') && (segmentGroup.hasChildren() || segments.length > 0)) {
2614 return { matched: false, consumedSegments: [], lastChild: 0, positionalParamSegments: {} };
2615 }
2616 return { matched: true, consumedSegments: [], lastChild: 0, positionalParamSegments: {} };
2617 }
2618 const matcher = route.matcher || defaultUrlMatcher;
2619 const res = matcher(segments, segmentGroup, route);
2620 if (!res) {
2621 return {
2622 matched: false,
2623 consumedSegments: [],
2624 lastChild: 0,
2625 positionalParamSegments: {},
2626 };
2627 }
2628 return {
2629 matched: true,
2630 consumedSegments: res.consumed,
2631 lastChild: res.consumed.length,
2632 positionalParamSegments: res.posParams,
2633 };
2634}
2635function split(segmentGroup, consumedSegments, slicedSegments, config) {
2636 if (slicedSegments.length > 0 &&
2637 containsEmptyPathRedirectsWithNamedOutlets(segmentGroup, slicedSegments, config)) {
2638 const s = new UrlSegmentGroup(consumedSegments, createChildrenForEmptySegments(config, new UrlSegmentGroup(slicedSegments, segmentGroup.children)));
2639 return { segmentGroup: mergeTrivialChildren(s), slicedSegments: [] };
2640 }
2641 if (slicedSegments.length === 0 &&
2642 containsEmptyPathRedirects(segmentGroup, slicedSegments, config)) {
2643 const s = new UrlSegmentGroup(segmentGroup.segments, addEmptySegmentsToChildrenIfNeeded(segmentGroup, slicedSegments, config, segmentGroup.children));
2644 return { segmentGroup: mergeTrivialChildren(s), slicedSegments };
2645 }
2646 return { segmentGroup, slicedSegments };
2647}
2648function mergeTrivialChildren(s) {
2649 if (s.numberOfChildren === 1 && s.children[PRIMARY_OUTLET]) {
2650 const c = s.children[PRIMARY_OUTLET];
2651 return new UrlSegmentGroup(s.segments.concat(c.segments), c.children);
2652 }
2653 return s;
2654}
2655function addEmptySegmentsToChildrenIfNeeded(segmentGroup, slicedSegments, routes, children) {
2656 const res = {};
2657 for (const r of routes) {
2658 if (isEmptyPathRedirect(segmentGroup, slicedSegments, r) && !children[getOutlet(r)]) {
2659 res[getOutlet(r)] = new UrlSegmentGroup([], {});
2660 }
2661 }
2662 return Object.assign(Object.assign({}, children), res);
2663}
2664function createChildrenForEmptySegments(routes, primarySegmentGroup) {
2665 const res = {};
2666 res[PRIMARY_OUTLET] = primarySegmentGroup;
2667 for (const r of routes) {
2668 if (r.path === '' && getOutlet(r) !== PRIMARY_OUTLET) {
2669 res[getOutlet(r)] = new UrlSegmentGroup([], {});
2670 }
2671 }
2672 return res;
2673}
2674function containsEmptyPathRedirectsWithNamedOutlets(segmentGroup, segments, routes) {
2675 return routes.some(r => isEmptyPathRedirect(segmentGroup, segments, r) && getOutlet(r) !== PRIMARY_OUTLET);
2676}
2677function containsEmptyPathRedirects(segmentGroup, segments, routes) {
2678 return routes.some(r => isEmptyPathRedirect(segmentGroup, segments, r));
2679}
2680function isEmptyPathRedirect(segmentGroup, segments, r) {
2681 if ((segmentGroup.hasChildren() || segments.length > 0) && r.pathMatch === 'full') {
2682 return false;
2683 }
2684 return r.path === '' && r.redirectTo !== undefined;
2685}
2686function getOutlet(route) {
2687 return route.outlet || PRIMARY_OUTLET;
2688}
2689
2690/**
2691 * @license
2692 * Copyright Google LLC All Rights Reserved.
2693 *
2694 * Use of this source code is governed by an MIT-style license that can be
2695 * found in the LICENSE file at https://angular.io/license
2696 */
2697function applyRedirects$1(moduleInjector, configLoader, urlSerializer, config) {
2698 return function (source) {
2699 return source.pipe(switchMap(t => applyRedirects(moduleInjector, configLoader, urlSerializer, t.extractedUrl, config)
2700 .pipe(map(urlAfterRedirects => (Object.assign(Object.assign({}, t), { urlAfterRedirects }))))));
2701 };
2702}
2703
2704/**
2705 * @license
2706 * Copyright Google LLC All Rights Reserved.
2707 *
2708 * Use of this source code is governed by an MIT-style license that can be
2709 * found in the LICENSE file at https://angular.io/license
2710 */
2711class CanActivate {
2712 constructor(path) {
2713 this.path = path;
2714 this.route = this.path[this.path.length - 1];
2715 }
2716}
2717class CanDeactivate {
2718 constructor(component, route) {
2719 this.component = component;
2720 this.route = route;
2721 }
2722}
2723function getAllRouteGuards(future, curr, parentContexts) {
2724 const futureRoot = future._root;
2725 const currRoot = curr ? curr._root : null;
2726 return getChildRouteGuards(futureRoot, currRoot, parentContexts, [futureRoot.value]);
2727}
2728function getCanActivateChild(p) {
2729 const canActivateChild = p.routeConfig ? p.routeConfig.canActivateChild : null;
2730 if (!canActivateChild || canActivateChild.length === 0)
2731 return null;
2732 return { node: p, guards: canActivateChild };
2733}
2734function getToken(token, snapshot, moduleInjector) {
2735 const config = getClosestLoadedConfig(snapshot);
2736 const injector = config ? config.module.injector : moduleInjector;
2737 return injector.get(token);
2738}
2739function getClosestLoadedConfig(snapshot) {
2740 if (!snapshot)
2741 return null;
2742 for (let s = snapshot.parent; s; s = s.parent) {
2743 const route = s.routeConfig;
2744 if (route && route._loadedConfig)
2745 return route._loadedConfig;
2746 }
2747 return null;
2748}
2749function getChildRouteGuards(futureNode, currNode, contexts, futurePath, checks = {
2750 canDeactivateChecks: [],
2751 canActivateChecks: []
2752}) {
2753 const prevChildren = nodeChildrenAsMap(currNode);
2754 // Process the children of the future route
2755 futureNode.children.forEach(c => {
2756 getRouteGuards(c, prevChildren[c.value.outlet], contexts, futurePath.concat([c.value]), checks);
2757 delete prevChildren[c.value.outlet];
2758 });
2759 // Process any children left from the current route (not active for the future route)
2760 forEach(prevChildren, (v, k) => deactivateRouteAndItsChildren(v, contexts.getContext(k), checks));
2761 return checks;
2762}
2763function getRouteGuards(futureNode, currNode, parentContexts, futurePath, checks = {
2764 canDeactivateChecks: [],
2765 canActivateChecks: []
2766}) {
2767 const future = futureNode.value;
2768 const curr = currNode ? currNode.value : null;
2769 const context = parentContexts ? parentContexts.getContext(futureNode.value.outlet) : null;
2770 // reusing the node
2771 if (curr && future.routeConfig === curr.routeConfig) {
2772 const shouldRun = shouldRunGuardsAndResolvers(curr, future, future.routeConfig.runGuardsAndResolvers);
2773 if (shouldRun) {
2774 checks.canActivateChecks.push(new CanActivate(futurePath));
2775 }
2776 else {
2777 // we need to set the data
2778 future.data = curr.data;
2779 future._resolvedData = curr._resolvedData;
2780 }
2781 // If we have a component, we need to go through an outlet.
2782 if (future.component) {
2783 getChildRouteGuards(futureNode, currNode, context ? context.children : null, futurePath, checks);
2784 // if we have a componentless route, we recurse but keep the same outlet map.
2785 }
2786 else {
2787 getChildRouteGuards(futureNode, currNode, parentContexts, futurePath, checks);
2788 }
2789 if (shouldRun) {
2790 const component = context && context.outlet && context.outlet.component || null;
2791 checks.canDeactivateChecks.push(new CanDeactivate(component, curr));
2792 }
2793 }
2794 else {
2795 if (curr) {
2796 deactivateRouteAndItsChildren(currNode, context, checks);
2797 }
2798 checks.canActivateChecks.push(new CanActivate(futurePath));
2799 // If we have a component, we need to go through an outlet.
2800 if (future.component) {
2801 getChildRouteGuards(futureNode, null, context ? context.children : null, futurePath, checks);
2802 // if we have a componentless route, we recurse but keep the same outlet map.
2803 }
2804 else {
2805 getChildRouteGuards(futureNode, null, parentContexts, futurePath, checks);
2806 }
2807 }
2808 return checks;
2809}
2810function shouldRunGuardsAndResolvers(curr, future, mode) {
2811 if (typeof mode === 'function') {
2812 return mode(curr, future);
2813 }
2814 switch (mode) {
2815 case 'pathParamsChange':
2816 return !equalPath(curr.url, future.url);
2817 case 'pathParamsOrQueryParamsChange':
2818 return !equalPath(curr.url, future.url) ||
2819 !shallowEqual(curr.queryParams, future.queryParams);
2820 case 'always':
2821 return true;
2822 case 'paramsOrQueryParamsChange':
2823 return !equalParamsAndUrlSegments(curr, future) ||
2824 !shallowEqual(curr.queryParams, future.queryParams);
2825 case 'paramsChange':
2826 default:
2827 return !equalParamsAndUrlSegments(curr, future);
2828 }
2829}
2830function deactivateRouteAndItsChildren(route, context, checks) {
2831 const children = nodeChildrenAsMap(route);
2832 const r = route.value;
2833 forEach(children, (node, childName) => {
2834 if (!r.component) {
2835 deactivateRouteAndItsChildren(node, context, checks);
2836 }
2837 else if (context) {
2838 deactivateRouteAndItsChildren(node, context.children.getContext(childName), checks);
2839 }
2840 else {
2841 deactivateRouteAndItsChildren(node, null, checks);
2842 }
2843 });
2844 if (!r.component) {
2845 checks.canDeactivateChecks.push(new CanDeactivate(null, r));
2846 }
2847 else if (context && context.outlet && context.outlet.isActivated) {
2848 checks.canDeactivateChecks.push(new CanDeactivate(context.outlet.component, r));
2849 }
2850 else {
2851 checks.canDeactivateChecks.push(new CanDeactivate(null, r));
2852 }
2853}
2854
2855/**
2856 * @license
2857 * Copyright Google LLC All Rights Reserved.
2858 *
2859 * Use of this source code is governed by an MIT-style license that can be
2860 * found in the LICENSE file at https://angular.io/license
2861 */
2862const INITIAL_VALUE = Symbol('INITIAL_VALUE');
2863function prioritizedGuardValue() {
2864 return switchMap(obs => {
2865 return combineLatest(...obs.map(o => o.pipe(take(1), startWith(INITIAL_VALUE))))
2866 .pipe(scan((acc, list) => {
2867 let isPending = false;
2868 return list.reduce((innerAcc, val, i) => {
2869 if (innerAcc !== INITIAL_VALUE)
2870 return innerAcc;
2871 // Toggle pending flag if any values haven't been set yet
2872 if (val === INITIAL_VALUE)
2873 isPending = true;
2874 // Any other return values are only valid if we haven't yet hit a pending
2875 // call. This guarantees that in the case of a guard at the bottom of the
2876 // tree that returns a redirect, we will wait for the higher priority
2877 // guard at the top to finish before performing the redirect.
2878 if (!isPending) {
2879 // Early return when we hit a `false` value as that should always
2880 // cancel navigation
2881 if (val === false)
2882 return val;
2883 if (i === list.length - 1 || isUrlTree(val)) {
2884 return val;
2885 }
2886 }
2887 return innerAcc;
2888 }, acc);
2889 }, INITIAL_VALUE), filter(item => item !== INITIAL_VALUE), map(item => isUrlTree(item) ? item : item === true), //
2890 take(1));
2891 });
2892}
2893
2894/**
2895 * @license
2896 * Copyright Google LLC All Rights Reserved.
2897 *
2898 * Use of this source code is governed by an MIT-style license that can be
2899 * found in the LICENSE file at https://angular.io/license
2900 */
2901function checkGuards(moduleInjector, forwardEvent) {
2902 return function (source) {
2903 return source.pipe(mergeMap(t => {
2904 const { targetSnapshot, currentSnapshot, guards: { canActivateChecks, canDeactivateChecks } } = t;
2905 if (canDeactivateChecks.length === 0 && canActivateChecks.length === 0) {
2906 return of(Object.assign(Object.assign({}, t), { guardsResult: true }));
2907 }
2908 return runCanDeactivateChecks(canDeactivateChecks, targetSnapshot, currentSnapshot, moduleInjector)
2909 .pipe(mergeMap(canDeactivate => {
2910 return canDeactivate && isBoolean(canDeactivate) ?
2911 runCanActivateChecks(targetSnapshot, canActivateChecks, moduleInjector, forwardEvent) :
2912 of(canDeactivate);
2913 }), map(guardsResult => (Object.assign(Object.assign({}, t), { guardsResult }))));
2914 }));
2915 };
2916}
2917function runCanDeactivateChecks(checks, futureRSS, currRSS, moduleInjector) {
2918 return from(checks).pipe(mergeMap(check => runCanDeactivate(check.component, check.route, currRSS, futureRSS, moduleInjector)), first(result => {
2919 return result !== true;
2920 }, true));
2921}
2922function runCanActivateChecks(futureSnapshot, checks, moduleInjector, forwardEvent) {
2923 return from(checks).pipe(concatMap((check) => {
2924 return from([
2925 fireChildActivationStart(check.route.parent, forwardEvent),
2926 fireActivationStart(check.route, forwardEvent),
2927 runCanActivateChild(futureSnapshot, check.path, moduleInjector),
2928 runCanActivate(futureSnapshot, check.route, moduleInjector)
2929 ])
2930 .pipe(concatAll(), first(result => {
2931 return result !== true;
2932 }, true));
2933 }), first(result => {
2934 return result !== true;
2935 }, true));
2936}
2937/**
2938 * This should fire off `ActivationStart` events for each route being activated at this
2939 * level.
2940 * In other words, if you're activating `a` and `b` below, `path` will contain the
2941 * `ActivatedRouteSnapshot`s for both and we will fire `ActivationStart` for both. Always
2942 * return
2943 * `true` so checks continue to run.
2944 */
2945function fireActivationStart(snapshot, forwardEvent) {
2946 if (snapshot !== null && forwardEvent) {
2947 forwardEvent(new ActivationStart(snapshot));
2948 }
2949 return of(true);
2950}
2951/**
2952 * This should fire off `ChildActivationStart` events for each route being activated at this
2953 * level.
2954 * In other words, if you're activating `a` and `b` below, `path` will contain the
2955 * `ActivatedRouteSnapshot`s for both and we will fire `ChildActivationStart` for both. Always
2956 * return
2957 * `true` so checks continue to run.
2958 */
2959function fireChildActivationStart(snapshot, forwardEvent) {
2960 if (snapshot !== null && forwardEvent) {
2961 forwardEvent(new ChildActivationStart(snapshot));
2962 }
2963 return of(true);
2964}
2965function runCanActivate(futureRSS, futureARS, moduleInjector) {
2966 const canActivate = futureARS.routeConfig ? futureARS.routeConfig.canActivate : null;
2967 if (!canActivate || canActivate.length === 0)
2968 return of(true);
2969 const canActivateObservables = canActivate.map((c) => {
2970 return defer(() => {
2971 const guard = getToken(c, futureARS, moduleInjector);
2972 let observable;
2973 if (isCanActivate(guard)) {
2974 observable = wrapIntoObservable(guard.canActivate(futureARS, futureRSS));
2975 }
2976 else if (isFunction(guard)) {
2977 observable = wrapIntoObservable(guard(futureARS, futureRSS));
2978 }
2979 else {
2980 throw new Error('Invalid CanActivate guard');
2981 }
2982 return observable.pipe(first());
2983 });
2984 });
2985 return of(canActivateObservables).pipe(prioritizedGuardValue());
2986}
2987function runCanActivateChild(futureRSS, path, moduleInjector) {
2988 const futureARS = path[path.length - 1];
2989 const canActivateChildGuards = path.slice(0, path.length - 1)
2990 .reverse()
2991 .map(p => getCanActivateChild(p))
2992 .filter(_ => _ !== null);
2993 const canActivateChildGuardsMapped = canActivateChildGuards.map((d) => {
2994 return defer(() => {
2995 const guardsMapped = d.guards.map((c) => {
2996 const guard = getToken(c, d.node, moduleInjector);
2997 let observable;
2998 if (isCanActivateChild(guard)) {
2999 observable = wrapIntoObservable(guard.canActivateChild(futureARS, futureRSS));
3000 }
3001 else if (isFunction(guard)) {
3002 observable = wrapIntoObservable(guard(futureARS, futureRSS));
3003 }
3004 else {
3005 throw new Error('Invalid CanActivateChild guard');
3006 }
3007 return observable.pipe(first());
3008 });
3009 return of(guardsMapped).pipe(prioritizedGuardValue());
3010 });
3011 });
3012 return of(canActivateChildGuardsMapped).pipe(prioritizedGuardValue());
3013}
3014function runCanDeactivate(component, currARS, currRSS, futureRSS, moduleInjector) {
3015 const canDeactivate = currARS && currARS.routeConfig ? currARS.routeConfig.canDeactivate : null;
3016 if (!canDeactivate || canDeactivate.length === 0)
3017 return of(true);
3018 const canDeactivateObservables = canDeactivate.map((c) => {
3019 const guard = getToken(c, currARS, moduleInjector);
3020 let observable;
3021 if (isCanDeactivate(guard)) {
3022 observable = wrapIntoObservable(guard.canDeactivate(component, currARS, currRSS, futureRSS));
3023 }
3024 else if (isFunction(guard)) {
3025 observable = wrapIntoObservable(guard(component, currARS, currRSS, futureRSS));
3026 }
3027 else {
3028 throw new Error('Invalid CanDeactivate guard');
3029 }
3030 return observable.pipe(first());
3031 });
3032 return of(canDeactivateObservables).pipe(prioritizedGuardValue());
3033}
3034
3035/**
3036 * @license
3037 * Copyright Google LLC All Rights Reserved.
3038 *
3039 * Use of this source code is governed by an MIT-style license that can be
3040 * found in the LICENSE file at https://angular.io/license
3041 */
3042class NoMatch$1 {
3043}
3044function recognize(rootComponentType, config, urlTree, url, paramsInheritanceStrategy = 'emptyOnly', relativeLinkResolution = 'legacy') {
3045 return new Recognizer(rootComponentType, config, urlTree, url, paramsInheritanceStrategy, relativeLinkResolution)
3046 .recognize();
3047}
3048class Recognizer {
3049 constructor(rootComponentType, config, urlTree, url, paramsInheritanceStrategy, relativeLinkResolution) {
3050 this.rootComponentType = rootComponentType;
3051 this.config = config;
3052 this.urlTree = urlTree;
3053 this.url = url;
3054 this.paramsInheritanceStrategy = paramsInheritanceStrategy;
3055 this.relativeLinkResolution = relativeLinkResolution;
3056 }
3057 recognize() {
3058 try {
3059 const rootSegmentGroup = split$1(this.urlTree.root, [], [], this.config, this.relativeLinkResolution).segmentGroup;
3060 const children = this.processSegmentGroup(this.config, rootSegmentGroup, PRIMARY_OUTLET);
3061 const root = new ActivatedRouteSnapshot([], Object.freeze({}), Object.freeze(Object.assign({}, this.urlTree.queryParams)), this.urlTree.fragment, {}, PRIMARY_OUTLET, this.rootComponentType, null, this.urlTree.root, -1, {});
3062 const rootNode = new TreeNode(root, children);
3063 const routeState = new RouterStateSnapshot(this.url, rootNode);
3064 this.inheritParamsAndData(routeState._root);
3065 return of(routeState);
3066 }
3067 catch (e) {
3068 return new Observable((obs) => obs.error(e));
3069 }
3070 }
3071 inheritParamsAndData(routeNode) {
3072 const route = routeNode.value;
3073 const i = inheritedParamsDataResolve(route, this.paramsInheritanceStrategy);
3074 route.params = Object.freeze(i.params);
3075 route.data = Object.freeze(i.data);
3076 routeNode.children.forEach(n => this.inheritParamsAndData(n));
3077 }
3078 processSegmentGroup(config, segmentGroup, outlet) {
3079 if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) {
3080 return this.processChildren(config, segmentGroup);
3081 }
3082 return this.processSegment(config, segmentGroup, segmentGroup.segments, outlet);
3083 }
3084 processChildren(config, segmentGroup) {
3085 const children = mapChildrenIntoArray(segmentGroup, (child, childOutlet) => this.processSegmentGroup(config, child, childOutlet));
3086 checkOutletNameUniqueness(children);
3087 sortActivatedRouteSnapshots(children);
3088 return children;
3089 }
3090 processSegment(config, segmentGroup, segments, outlet) {
3091 for (const r of config) {
3092 try {
3093 return this.processSegmentAgainstRoute(r, segmentGroup, segments, outlet);
3094 }
3095 catch (e) {
3096 if (!(e instanceof NoMatch$1))
3097 throw e;
3098 }
3099 }
3100 if (this.noLeftoversInUrl(segmentGroup, segments, outlet)) {
3101 return [];
3102 }
3103 throw new NoMatch$1();
3104 }
3105 noLeftoversInUrl(segmentGroup, segments, outlet) {
3106 return segments.length === 0 && !segmentGroup.children[outlet];
3107 }
3108 processSegmentAgainstRoute(route, rawSegment, segments, outlet) {
3109 if (route.redirectTo)
3110 throw new NoMatch$1();
3111 if ((route.outlet || PRIMARY_OUTLET) !== outlet)
3112 throw new NoMatch$1();
3113 let snapshot;
3114 let consumedSegments = [];
3115 let rawSlicedSegments = [];
3116 if (route.path === '**') {
3117 const params = segments.length > 0 ? last(segments).parameters : {};
3118 snapshot = new ActivatedRouteSnapshot(segments, params, Object.freeze(Object.assign({}, this.urlTree.queryParams)), this.urlTree.fragment, getData(route), outlet, route.component, route, getSourceSegmentGroup(rawSegment), getPathIndexShift(rawSegment) + segments.length, getResolve(route));
3119 }
3120 else {
3121 const result = match$1(rawSegment, route, segments);
3122 consumedSegments = result.consumedSegments;
3123 rawSlicedSegments = segments.slice(result.lastChild);
3124 snapshot = new ActivatedRouteSnapshot(consumedSegments, result.parameters, Object.freeze(Object.assign({}, this.urlTree.queryParams)), this.urlTree.fragment, getData(route), outlet, route.component, route, getSourceSegmentGroup(rawSegment), getPathIndexShift(rawSegment) + consumedSegments.length, getResolve(route));
3125 }
3126 const childConfig = getChildConfig(route);
3127 const { segmentGroup, slicedSegments } = split$1(rawSegment, consumedSegments, rawSlicedSegments, childConfig, this.relativeLinkResolution);
3128 if (slicedSegments.length === 0 && segmentGroup.hasChildren()) {
3129 const children = this.processChildren(childConfig, segmentGroup);
3130 return [new TreeNode(snapshot, children)];
3131 }
3132 if (childConfig.length === 0 && slicedSegments.length === 0) {
3133 return [new TreeNode(snapshot, [])];
3134 }
3135 const children = this.processSegment(childConfig, segmentGroup, slicedSegments, PRIMARY_OUTLET);
3136 return [new TreeNode(snapshot, children)];
3137 }
3138}
3139function sortActivatedRouteSnapshots(nodes) {
3140 nodes.sort((a, b) => {
3141 if (a.value.outlet === PRIMARY_OUTLET)
3142 return -1;
3143 if (b.value.outlet === PRIMARY_OUTLET)
3144 return 1;
3145 return a.value.outlet.localeCompare(b.value.outlet);
3146 });
3147}
3148function getChildConfig(route) {
3149 if (route.children) {
3150 return route.children;
3151 }
3152 if (route.loadChildren) {
3153 return route._loadedConfig.routes;
3154 }
3155 return [];
3156}
3157function match$1(segmentGroup, route, segments) {
3158 if (route.path === '') {
3159 if (route.pathMatch === 'full' && (segmentGroup.hasChildren() || segments.length > 0)) {
3160 throw new NoMatch$1();
3161 }
3162 return { consumedSegments: [], lastChild: 0, parameters: {} };
3163 }
3164 const matcher = route.matcher || defaultUrlMatcher;
3165 const res = matcher(segments, segmentGroup, route);
3166 if (!res)
3167 throw new NoMatch$1();
3168 const posParams = {};
3169 forEach(res.posParams, (v, k) => {
3170 posParams[k] = v.path;
3171 });
3172 const parameters = res.consumed.length > 0 ? Object.assign(Object.assign({}, posParams), res.consumed[res.consumed.length - 1].parameters) :
3173 posParams;
3174 return { consumedSegments: res.consumed, lastChild: res.consumed.length, parameters };
3175}
3176function checkOutletNameUniqueness(nodes) {
3177 const names = {};
3178 nodes.forEach(n => {
3179 const routeWithSameOutletName = names[n.value.outlet];
3180 if (routeWithSameOutletName) {
3181 const p = routeWithSameOutletName.url.map(s => s.toString()).join('/');
3182 const c = n.value.url.map(s => s.toString()).join('/');
3183 throw new Error(`Two segments cannot have the same outlet name: '${p}' and '${c}'.`);
3184 }
3185 names[n.value.outlet] = n.value;
3186 });
3187}
3188function getSourceSegmentGroup(segmentGroup) {
3189 let s = segmentGroup;
3190 while (s._sourceSegment) {
3191 s = s._sourceSegment;
3192 }
3193 return s;
3194}
3195function getPathIndexShift(segmentGroup) {
3196 let s = segmentGroup;
3197 let res = (s._segmentIndexShift ? s._segmentIndexShift : 0);
3198 while (s._sourceSegment) {
3199 s = s._sourceSegment;
3200 res += (s._segmentIndexShift ? s._segmentIndexShift : 0);
3201 }
3202 return res - 1;
3203}
3204function split$1(segmentGroup, consumedSegments, slicedSegments, config, relativeLinkResolution) {
3205 if (slicedSegments.length > 0 &&
3206 containsEmptyPathMatchesWithNamedOutlets(segmentGroup, slicedSegments, config)) {
3207 const s = new UrlSegmentGroup(consumedSegments, createChildrenForEmptyPaths(segmentGroup, consumedSegments, config, new UrlSegmentGroup(slicedSegments, segmentGroup.children)));
3208 s._sourceSegment = segmentGroup;
3209 s._segmentIndexShift = consumedSegments.length;
3210 return { segmentGroup: s, slicedSegments: [] };
3211 }
3212 if (slicedSegments.length === 0 &&
3213 containsEmptyPathMatches(segmentGroup, slicedSegments, config)) {
3214 const s = new UrlSegmentGroup(segmentGroup.segments, addEmptyPathsToChildrenIfNeeded(segmentGroup, consumedSegments, slicedSegments, config, segmentGroup.children, relativeLinkResolution));
3215 s._sourceSegment = segmentGroup;
3216 s._segmentIndexShift = consumedSegments.length;
3217 return { segmentGroup: s, slicedSegments };
3218 }
3219 const s = new UrlSegmentGroup(segmentGroup.segments, segmentGroup.children);
3220 s._sourceSegment = segmentGroup;
3221 s._segmentIndexShift = consumedSegments.length;
3222 return { segmentGroup: s, slicedSegments };
3223}
3224function addEmptyPathsToChildrenIfNeeded(segmentGroup, consumedSegments, slicedSegments, routes, children, relativeLinkResolution) {
3225 const res = {};
3226 for (const r of routes) {
3227 if (emptyPathMatch(segmentGroup, slicedSegments, r) && !children[getOutlet$1(r)]) {
3228 const s = new UrlSegmentGroup([], {});
3229 s._sourceSegment = segmentGroup;
3230 if (relativeLinkResolution === 'legacy') {
3231 s._segmentIndexShift = segmentGroup.segments.length;
3232 }
3233 else {
3234 s._segmentIndexShift = consumedSegments.length;
3235 }
3236 res[getOutlet$1(r)] = s;
3237 }
3238 }
3239 return Object.assign(Object.assign({}, children), res);
3240}
3241function createChildrenForEmptyPaths(segmentGroup, consumedSegments, routes, primarySegment) {
3242 const res = {};
3243 res[PRIMARY_OUTLET] = primarySegment;
3244 primarySegment._sourceSegment = segmentGroup;
3245 primarySegment._segmentIndexShift = consumedSegments.length;
3246 for (const r of routes) {
3247 if (r.path === '' && getOutlet$1(r) !== PRIMARY_OUTLET) {
3248 const s = new UrlSegmentGroup([], {});
3249 s._sourceSegment = segmentGroup;
3250 s._segmentIndexShift = consumedSegments.length;
3251 res[getOutlet$1(r)] = s;
3252 }
3253 }
3254 return res;
3255}
3256function containsEmptyPathMatchesWithNamedOutlets(segmentGroup, slicedSegments, routes) {
3257 return routes.some(r => emptyPathMatch(segmentGroup, slicedSegments, r) && getOutlet$1(r) !== PRIMARY_OUTLET);
3258}
3259function containsEmptyPathMatches(segmentGroup, slicedSegments, routes) {
3260 return routes.some(r => emptyPathMatch(segmentGroup, slicedSegments, r));
3261}
3262function emptyPathMatch(segmentGroup, slicedSegments, r) {
3263 if ((segmentGroup.hasChildren() || slicedSegments.length > 0) && r.pathMatch === 'full') {
3264 return false;
3265 }
3266 return r.path === '' && r.redirectTo === undefined;
3267}
3268function getOutlet$1(route) {
3269 return route.outlet || PRIMARY_OUTLET;
3270}
3271function getData(route) {
3272 return route.data || {};
3273}
3274function getResolve(route) {
3275 return route.resolve || {};
3276}
3277
3278/**
3279 * @license
3280 * Copyright Google LLC All Rights Reserved.
3281 *
3282 * Use of this source code is governed by an MIT-style license that can be
3283 * found in the LICENSE file at https://angular.io/license
3284 */
3285function recognize$1(rootComponentType, config, serializer, paramsInheritanceStrategy, relativeLinkResolution) {
3286 return function (source) {
3287 return source.pipe(mergeMap(t => recognize(rootComponentType, config, t.urlAfterRedirects, serializer(t.urlAfterRedirects), paramsInheritanceStrategy, relativeLinkResolution)
3288 .pipe(map(targetSnapshot => (Object.assign(Object.assign({}, t), { targetSnapshot }))))));
3289 };
3290}
3291
3292/**
3293 * @license
3294 * Copyright Google LLC All Rights Reserved.
3295 *
3296 * Use of this source code is governed by an MIT-style license that can be
3297 * found in the LICENSE file at https://angular.io/license
3298 */
3299function resolveData(paramsInheritanceStrategy, moduleInjector) {
3300 return function (source) {
3301 return source.pipe(mergeMap(t => {
3302 const { targetSnapshot, guards: { canActivateChecks } } = t;
3303 if (!canActivateChecks.length) {
3304 return of(t);
3305 }
3306 let canActivateChecksResolved = 0;
3307 return from(canActivateChecks)
3308 .pipe(concatMap(check => runResolve(check.route, targetSnapshot, paramsInheritanceStrategy, moduleInjector)), tap(() => canActivateChecksResolved++), takeLast(1), mergeMap(_ => canActivateChecksResolved === canActivateChecks.length ? of(t) : EMPTY));
3309 }));
3310 };
3311}
3312function runResolve(futureARS, futureRSS, paramsInheritanceStrategy, moduleInjector) {
3313 const resolve = futureARS._resolve;
3314 return resolveNode(resolve, futureARS, futureRSS, moduleInjector)
3315 .pipe(map((resolvedData) => {
3316 futureARS._resolvedData = resolvedData;
3317 futureARS.data = Object.assign(Object.assign({}, futureARS.data), inheritedParamsDataResolve(futureARS, paramsInheritanceStrategy).resolve);
3318 return null;
3319 }));
3320}
3321function resolveNode(resolve, futureARS, futureRSS, moduleInjector) {
3322 const keys = Object.keys(resolve);
3323 if (keys.length === 0) {
3324 return of({});
3325 }
3326 const data = {};
3327 return from(keys).pipe(mergeMap((key) => getResolver(resolve[key], futureARS, futureRSS, moduleInjector)
3328 .pipe(tap((value) => {
3329 data[key] = value;
3330 }))), takeLast(1), mergeMap(() => {
3331 // Ensure all resolvers returned values, otherwise don't emit any "next" and just complete
3332 // the chain which will cancel navigation
3333 if (Object.keys(data).length === keys.length) {
3334 return of(data);
3335 }
3336 return EMPTY;
3337 }));
3338}
3339function getResolver(injectionToken, futureARS, futureRSS, moduleInjector) {
3340 const resolver = getToken(injectionToken, futureARS, moduleInjector);
3341 return resolver.resolve ? wrapIntoObservable(resolver.resolve(futureARS, futureRSS)) :
3342 wrapIntoObservable(resolver(futureARS, futureRSS));
3343}
3344
3345/**
3346 * @license
3347 * Copyright Google LLC All Rights Reserved.
3348 *
3349 * Use of this source code is governed by an MIT-style license that can be
3350 * found in the LICENSE file at https://angular.io/license
3351 */
3352/**
3353 * Perform a side effect through a switchMap for every emission on the source Observable,
3354 * but return an Observable that is identical to the source. It's essentially the same as
3355 * the `tap` operator, but if the side effectful `next` function returns an ObservableInput,
3356 * it will wait before continuing with the original value.
3357 */
3358function switchTap(next) {
3359 return function (source) {
3360 return source.pipe(switchMap(v => {
3361 const nextResult = next(v);
3362 if (nextResult) {
3363 return from(nextResult).pipe(map(() => v));
3364 }
3365 return from([v]);
3366 }));
3367 };
3368}
3369
3370/**
3371 * @license
3372 * Copyright Google LLC All Rights Reserved.
3373 *
3374 * Use of this source code is governed by an MIT-style license that can be
3375 * found in the LICENSE file at https://angular.io/license
3376 */
3377/**
3378 * @description
3379 *
3380 * Provides a way to customize when activated routes get reused.
3381 *
3382 * @publicApi
3383 */
3384class RouteReuseStrategy {
3385}
3386/**
3387 * Does not detach any subtrees. Reuses routes as long as their route config is the same.
3388 */
3389class DefaultRouteReuseStrategy {
3390 shouldDetach(route) {
3391 return false;
3392 }
3393 store(route, detachedTree) { }
3394 shouldAttach(route) {
3395 return false;
3396 }
3397 retrieve(route) {
3398 return null;
3399 }
3400 shouldReuseRoute(future, curr) {
3401 return future.routeConfig === curr.routeConfig;
3402 }
3403}
3404
3405/**
3406 * @license
3407 * Copyright Google LLC All Rights Reserved.
3408 *
3409 * Use of this source code is governed by an MIT-style license that can be
3410 * found in the LICENSE file at https://angular.io/license
3411 */
3412/**
3413 * The [DI token](guide/glossary/#di-token) for a router configuration.
3414 * @see `ROUTES`
3415 * @publicApi
3416 */
3417const ROUTES = new InjectionToken('ROUTES');
3418class RouterConfigLoader {
3419 constructor(loader, compiler, onLoadStartListener, onLoadEndListener) {
3420 this.loader = loader;
3421 this.compiler = compiler;
3422 this.onLoadStartListener = onLoadStartListener;
3423 this.onLoadEndListener = onLoadEndListener;
3424 }
3425 load(parentInjector, route) {
3426 if (this.onLoadStartListener) {
3427 this.onLoadStartListener(route);
3428 }
3429 const moduleFactory$ = this.loadModuleFactory(route.loadChildren);
3430 return moduleFactory$.pipe(map((factory) => {
3431 if (this.onLoadEndListener) {
3432 this.onLoadEndListener(route);
3433 }
3434 const module = factory.create(parentInjector);
3435 return new LoadedRouterConfig(flatten(module.injector.get(ROUTES)).map(standardizeConfig), module);
3436 }));
3437 }
3438 loadModuleFactory(loadChildren) {
3439 if (typeof loadChildren === 'string') {
3440 return from(this.loader.load(loadChildren));
3441 }
3442 else {
3443 return wrapIntoObservable(loadChildren()).pipe(mergeMap((t) => {
3444 if (t instanceof NgModuleFactory) {
3445 return of(t);
3446 }
3447 else {
3448 return from(this.compiler.compileModuleAsync(t));
3449 }
3450 }));
3451 }
3452 }
3453}
3454
3455/**
3456 * @license
3457 * Copyright Google LLC All Rights Reserved.
3458 *
3459 * Use of this source code is governed by an MIT-style license that can be
3460 * found in the LICENSE file at https://angular.io/license
3461 */
3462/**
3463 * Store contextual information about a `RouterOutlet`
3464 *
3465 * @publicApi
3466 */
3467class OutletContext {
3468 constructor() {
3469 this.outlet = null;
3470 this.route = null;
3471 this.resolver = null;
3472 this.children = new ChildrenOutletContexts();
3473 this.attachRef = null;
3474 }
3475}
3476/**
3477 * Store contextual information about the children (= nested) `RouterOutlet`
3478 *
3479 * @publicApi
3480 */
3481class ChildrenOutletContexts {
3482 constructor() {
3483 // contexts for child outlets, by name.
3484 this.contexts = new Map();
3485 }
3486 /** Called when a `RouterOutlet` directive is instantiated */
3487 onChildOutletCreated(childName, outlet) {
3488 const context = this.getOrCreateContext(childName);
3489 context.outlet = outlet;
3490 this.contexts.set(childName, context);
3491 }
3492 /**
3493 * Called when a `RouterOutlet` directive is destroyed.
3494 * We need to keep the context as the outlet could be destroyed inside a NgIf and might be
3495 * re-created later.
3496 */
3497 onChildOutletDestroyed(childName) {
3498 const context = this.getContext(childName);
3499 if (context) {
3500 context.outlet = null;
3501 }
3502 }
3503 /**
3504 * Called when the corresponding route is deactivated during navigation.
3505 * Because the component get destroyed, all children outlet are destroyed.
3506 */
3507 onOutletDeactivated() {
3508 const contexts = this.contexts;
3509 this.contexts = new Map();
3510 return contexts;
3511 }
3512 onOutletReAttached(contexts) {
3513 this.contexts = contexts;
3514 }
3515 getOrCreateContext(childName) {
3516 let context = this.getContext(childName);
3517 if (!context) {
3518 context = new OutletContext();
3519 this.contexts.set(childName, context);
3520 }
3521 return context;
3522 }
3523 getContext(childName) {
3524 return this.contexts.get(childName) || null;
3525 }
3526}
3527
3528/**
3529 * @license
3530 * Copyright Google LLC All Rights Reserved.
3531 *
3532 * Use of this source code is governed by an MIT-style license that can be
3533 * found in the LICENSE file at https://angular.io/license
3534 */
3535/**
3536 * @description
3537 *
3538 * Provides a way to migrate AngularJS applications to Angular.
3539 *
3540 * @publicApi
3541 */
3542class UrlHandlingStrategy {
3543}
3544/**
3545 * @publicApi
3546 */
3547class DefaultUrlHandlingStrategy {
3548 shouldProcessUrl(url) {
3549 return true;
3550 }
3551 extract(url) {
3552 return url;
3553 }
3554 merge(newUrlPart, wholeUrl) {
3555 return newUrlPart;
3556 }
3557}
3558
3559/**
3560 * @license
3561 * Copyright Google LLC All Rights Reserved.
3562 *
3563 * Use of this source code is governed by an MIT-style license that can be
3564 * found in the LICENSE file at https://angular.io/license
3565 */
3566function defaultErrorHandler(error) {
3567 throw error;
3568}
3569function defaultMalformedUriErrorHandler(error, urlSerializer, url) {
3570 return urlSerializer.parse('/');
3571}
3572/**
3573 * @internal
3574 */
3575function defaultRouterHook(snapshot, runExtras) {
3576 return of(null);
3577}
3578/**
3579 * @description
3580 *
3581 * A service that provides navigation among views and URL manipulation capabilities.
3582 *
3583 * @see `Route`.
3584 * @see [Routing and Navigation Guide](guide/router).
3585 *
3586 * @ngModule RouterModule
3587 *
3588 * @publicApi
3589 */
3590class Router {
3591 /**
3592 * Creates the router service.
3593 */
3594 // TODO: vsavkin make internal after the final is out.
3595 constructor(rootComponentType, urlSerializer, rootContexts, location, injector, loader, compiler, config) {
3596 this.rootComponentType = rootComponentType;
3597 this.urlSerializer = urlSerializer;
3598 this.rootContexts = rootContexts;
3599 this.location = location;
3600 this.config = config;
3601 this.lastSuccessfulNavigation = null;
3602 this.currentNavigation = null;
3603 this.navigationId = 0;
3604 this.isNgZoneEnabled = false;
3605 /**
3606 * An event stream for routing events in this NgModule.
3607 */
3608 this.events = new Subject();
3609 /**
3610 * A handler for navigation errors in this NgModule.
3611 */
3612 this.errorHandler = defaultErrorHandler;
3613 /**
3614 * A handler for errors thrown by `Router.parseUrl(url)`
3615 * when `url` contains an invalid character.
3616 * The most common case is a `%` sign
3617 * that's not encoded and is not part of a percent encoded sequence.
3618 */
3619 this.malformedUriErrorHandler = defaultMalformedUriErrorHandler;
3620 /**
3621 * True if at least one navigation event has occurred,
3622 * false otherwise.
3623 */
3624 this.navigated = false;
3625 this.lastSuccessfulId = -1;
3626 /**
3627 * Hooks that enable you to pause navigation,
3628 * either before or after the preactivation phase.
3629 * Used by `RouterModule`.
3630 *
3631 * @internal
3632 */
3633 this.hooks = { beforePreactivation: defaultRouterHook, afterPreactivation: defaultRouterHook };
3634 /**
3635 * A strategy for extracting and merging URLs.
3636 * Used for AngularJS to Angular migrations.
3637 */
3638 this.urlHandlingStrategy = new DefaultUrlHandlingStrategy();
3639 /**
3640 * A strategy for re-using routes.
3641 */
3642 this.routeReuseStrategy = new DefaultRouteReuseStrategy();
3643 /**
3644 * How to handle a navigation request to the current URL. One of:
3645 * - `'ignore'` : The router ignores the request.
3646 * - `'reload'` : The router reloads the URL. Use to implement a "refresh" feature.
3647 */
3648 this.onSameUrlNavigation = 'ignore';
3649 /**
3650 * How to merge parameters, data, and resolved data from parent to child
3651 * routes. One of:
3652 *
3653 * - `'emptyOnly'` : Inherit parent parameters, data, and resolved data
3654 * for path-less or component-less routes.
3655 * - `'always'` : Inherit parent parameters, data, and resolved data
3656 * for all child routes.
3657 */
3658 this.paramsInheritanceStrategy = 'emptyOnly';
3659 /**
3660 * Determines when the router updates the browser URL.
3661 * By default (`"deferred"`), updates the browser URL after navigation has finished.
3662 * Set to `'eager'` to update the browser URL at the beginning of navigation.
3663 * You can choose to update early so that, if navigation fails,
3664 * you can show an error message with the URL that failed.
3665 */
3666 this.urlUpdateStrategy = 'deferred';
3667 /**
3668 * Enables a bug fix that corrects relative link resolution in components with empty paths.
3669 * @see `RouterModule`
3670 */
3671 this.relativeLinkResolution = 'legacy';
3672 const onLoadStart = (r) => this.triggerEvent(new RouteConfigLoadStart(r));
3673 const onLoadEnd = (r) => this.triggerEvent(new RouteConfigLoadEnd(r));
3674 this.ngModule = injector.get(NgModuleRef);
3675 this.console = injector.get(ɵConsole);
3676 const ngZone = injector.get(NgZone);
3677 this.isNgZoneEnabled = ngZone instanceof NgZone;
3678 this.resetConfig(config);
3679 this.currentUrlTree = createEmptyUrlTree();
3680 this.rawUrlTree = this.currentUrlTree;
3681 this.browserUrlTree = this.currentUrlTree;
3682 this.configLoader = new RouterConfigLoader(loader, compiler, onLoadStart, onLoadEnd);
3683 this.routerState = createEmptyState(this.currentUrlTree, this.rootComponentType);
3684 this.transitions = new BehaviorSubject({
3685 id: 0,
3686 currentUrlTree: this.currentUrlTree,
3687 currentRawUrl: this.currentUrlTree,
3688 extractedUrl: this.urlHandlingStrategy.extract(this.currentUrlTree),
3689 urlAfterRedirects: this.urlHandlingStrategy.extract(this.currentUrlTree),
3690 rawUrl: this.currentUrlTree,
3691 extras: {},
3692 resolve: null,
3693 reject: null,
3694 promise: Promise.resolve(true),
3695 source: 'imperative',
3696 restoredState: null,
3697 currentSnapshot: this.routerState.snapshot,
3698 targetSnapshot: null,
3699 currentRouterState: this.routerState,
3700 targetRouterState: null,
3701 guards: { canActivateChecks: [], canDeactivateChecks: [] },
3702 guardsResult: null,
3703 });
3704 this.navigations = this.setupNavigations(this.transitions);
3705 this.processNavigations();
3706 }
3707 setupNavigations(transitions) {
3708 const eventsSubject = this.events;
3709 return transitions.pipe(filter(t => t.id !== 0),
3710 // Extract URL
3711 map(t => (Object.assign(Object.assign({}, t), { extractedUrl: this.urlHandlingStrategy.extract(t.rawUrl) }))),
3712 // Using switchMap so we cancel executing navigations when a new one comes in
3713 switchMap(t => {
3714 let completed = false;
3715 let errored = false;
3716 return of(t).pipe(
3717 // Store the Navigation object
3718 tap(t => {
3719 this.currentNavigation = {
3720 id: t.id,
3721 initialUrl: t.currentRawUrl,
3722 extractedUrl: t.extractedUrl,
3723 trigger: t.source,
3724 extras: t.extras,
3725 previousNavigation: this.lastSuccessfulNavigation ? Object.assign(Object.assign({}, this.lastSuccessfulNavigation), { previousNavigation: null }) :
3726 null
3727 };
3728 }), switchMap(t => {
3729 const urlTransition = !this.navigated ||
3730 t.extractedUrl.toString() !== this.browserUrlTree.toString();
3731 const processCurrentUrl = (this.onSameUrlNavigation === 'reload' ? true : urlTransition) &&
3732 this.urlHandlingStrategy.shouldProcessUrl(t.rawUrl);
3733 if (processCurrentUrl) {
3734 return of(t).pipe(
3735 // Fire NavigationStart event
3736 switchMap(t => {
3737 const transition = this.transitions.getValue();
3738 eventsSubject.next(new NavigationStart(t.id, this.serializeUrl(t.extractedUrl), t.source, t.restoredState));
3739 if (transition !== this.transitions.getValue()) {
3740 return EMPTY;
3741 }
3742 return [t];
3743 }),
3744 // This delay is required to match old behavior that forced navigation
3745 // to always be async
3746 switchMap(t => Promise.resolve(t)),
3747 // ApplyRedirects
3748 applyRedirects$1(this.ngModule.injector, this.configLoader, this.urlSerializer, this.config),
3749 // Update the currentNavigation
3750 tap(t => {
3751 this.currentNavigation = Object.assign(Object.assign({}, this.currentNavigation), { finalUrl: t.urlAfterRedirects });
3752 }),
3753 // Recognize
3754 recognize$1(this.rootComponentType, this.config, (url) => this.serializeUrl(url), this.paramsInheritanceStrategy, this.relativeLinkResolution),
3755 // Update URL if in `eager` update mode
3756 tap(t => {
3757 if (this.urlUpdateStrategy === 'eager') {
3758 if (!t.extras.skipLocationChange) {
3759 this.setBrowserUrl(t.urlAfterRedirects, !!t.extras.replaceUrl, t.id, t.extras.state);
3760 }
3761 this.browserUrlTree = t.urlAfterRedirects;
3762 }
3763 }),
3764 // Fire RoutesRecognized
3765 tap(t => {
3766 const routesRecognized = new RoutesRecognized(t.id, this.serializeUrl(t.extractedUrl), this.serializeUrl(t.urlAfterRedirects), t.targetSnapshot);
3767 eventsSubject.next(routesRecognized);
3768 }));
3769 }
3770 else {
3771 const processPreviousUrl = urlTransition && this.rawUrlTree &&
3772 this.urlHandlingStrategy.shouldProcessUrl(this.rawUrlTree);
3773 /* When the current URL shouldn't be processed, but the previous one was,
3774 * we handle this "error condition" by navigating to the previously
3775 * successful URL, but leaving the URL intact.*/
3776 if (processPreviousUrl) {
3777 const { id, extractedUrl, source, restoredState, extras } = t;
3778 const navStart = new NavigationStart(id, this.serializeUrl(extractedUrl), source, restoredState);
3779 eventsSubject.next(navStart);
3780 const targetSnapshot = createEmptyState(extractedUrl, this.rootComponentType).snapshot;
3781 return of(Object.assign(Object.assign({}, t), { targetSnapshot, urlAfterRedirects: extractedUrl, extras: Object.assign(Object.assign({}, extras), { skipLocationChange: false, replaceUrl: false }) }));
3782 }
3783 else {
3784 /* When neither the current or previous URL can be processed, do nothing
3785 * other than update router's internal reference to the current "settled"
3786 * URL. This way the next navigation will be coming from the current URL
3787 * in the browser.
3788 */
3789 this.rawUrlTree = t.rawUrl;
3790 this.browserUrlTree = t.urlAfterRedirects;
3791 t.resolve(null);
3792 return EMPTY;
3793 }
3794 }
3795 }),
3796 // Before Preactivation
3797 switchTap(t => {
3798 const { targetSnapshot, id: navigationId, extractedUrl: appliedUrlTree, rawUrl: rawUrlTree, extras: { skipLocationChange, replaceUrl } } = t;
3799 return this.hooks.beforePreactivation(targetSnapshot, {
3800 navigationId,
3801 appliedUrlTree,
3802 rawUrlTree,
3803 skipLocationChange: !!skipLocationChange,
3804 replaceUrl: !!replaceUrl,
3805 });
3806 }),
3807 // --- GUARDS ---
3808 tap(t => {
3809 const guardsStart = new GuardsCheckStart(t.id, this.serializeUrl(t.extractedUrl), this.serializeUrl(t.urlAfterRedirects), t.targetSnapshot);
3810 this.triggerEvent(guardsStart);
3811 }), map(t => (Object.assign(Object.assign({}, t), { guards: getAllRouteGuards(t.targetSnapshot, t.currentSnapshot, this.rootContexts) }))), checkGuards(this.ngModule.injector, (evt) => this.triggerEvent(evt)), tap(t => {
3812 if (isUrlTree(t.guardsResult)) {
3813 const error = navigationCancelingError(`Redirecting to "${this.serializeUrl(t.guardsResult)}"`);
3814 error.url = t.guardsResult;
3815 throw error;
3816 }
3817 }), tap(t => {
3818 const guardsEnd = new GuardsCheckEnd(t.id, this.serializeUrl(t.extractedUrl), this.serializeUrl(t.urlAfterRedirects), t.targetSnapshot, !!t.guardsResult);
3819 this.triggerEvent(guardsEnd);
3820 }), filter(t => {
3821 if (!t.guardsResult) {
3822 this.resetUrlToCurrentUrlTree();
3823 const navCancel = new NavigationCancel(t.id, this.serializeUrl(t.extractedUrl), '');
3824 eventsSubject.next(navCancel);
3825 t.resolve(false);
3826 return false;
3827 }
3828 return true;
3829 }),
3830 // --- RESOLVE ---
3831 switchTap(t => {
3832 if (t.guards.canActivateChecks.length) {
3833 return of(t).pipe(tap(t => {
3834 const resolveStart = new ResolveStart(t.id, this.serializeUrl(t.extractedUrl), this.serializeUrl(t.urlAfterRedirects), t.targetSnapshot);
3835 this.triggerEvent(resolveStart);
3836 }), switchMap(t => {
3837 let dataResolved = false;
3838 return of(t).pipe(resolveData(this.paramsInheritanceStrategy, this.ngModule.injector), tap({
3839 next: () => dataResolved = true,
3840 complete: () => {
3841 if (!dataResolved) {
3842 const navCancel = new NavigationCancel(t.id, this.serializeUrl(t.extractedUrl), `At least one route resolver didn't emit any value.`);
3843 eventsSubject.next(navCancel);
3844 t.resolve(false);
3845 }
3846 }
3847 }));
3848 }), tap(t => {
3849 const resolveEnd = new ResolveEnd(t.id, this.serializeUrl(t.extractedUrl), this.serializeUrl(t.urlAfterRedirects), t.targetSnapshot);
3850 this.triggerEvent(resolveEnd);
3851 }));
3852 }
3853 return undefined;
3854 }),
3855 // --- AFTER PREACTIVATION ---
3856 switchTap((t) => {
3857 const { targetSnapshot, id: navigationId, extractedUrl: appliedUrlTree, rawUrl: rawUrlTree, extras: { skipLocationChange, replaceUrl } } = t;
3858 return this.hooks.afterPreactivation(targetSnapshot, {
3859 navigationId,
3860 appliedUrlTree,
3861 rawUrlTree,
3862 skipLocationChange: !!skipLocationChange,
3863 replaceUrl: !!replaceUrl,
3864 });
3865 }), map((t) => {
3866 const targetRouterState = createRouterState(this.routeReuseStrategy, t.targetSnapshot, t.currentRouterState);
3867 return (Object.assign(Object.assign({}, t), { targetRouterState }));
3868 }),
3869 /* Once here, we are about to activate syncronously. The assumption is this
3870 will succeed, and user code may read from the Router service. Therefore
3871 before activation, we need to update router properties storing the current
3872 URL and the RouterState, as well as updated the browser URL. All this should
3873 happen *before* activating. */
3874 tap((t) => {
3875 this.currentUrlTree = t.urlAfterRedirects;
3876 this.rawUrlTree =
3877 this.urlHandlingStrategy.merge(this.currentUrlTree, t.rawUrl);
3878 this.routerState = t.targetRouterState;
3879 if (this.urlUpdateStrategy === 'deferred') {
3880 if (!t.extras.skipLocationChange) {
3881 this.setBrowserUrl(this.rawUrlTree, !!t.extras.replaceUrl, t.id, t.extras.state);
3882 }
3883 this.browserUrlTree = t.urlAfterRedirects;
3884 }
3885 }), activateRoutes(this.rootContexts, this.routeReuseStrategy, (evt) => this.triggerEvent(evt)), tap({
3886 next() {
3887 completed = true;
3888 },
3889 complete() {
3890 completed = true;
3891 }
3892 }), finalize(() => {
3893 /* When the navigation stream finishes either through error or success, we
3894 * set the `completed` or `errored` flag. However, there are some situations
3895 * where we could get here without either of those being set. For instance, a
3896 * redirect during NavigationStart. Therefore, this is a catch-all to make
3897 * sure the NavigationCancel
3898 * event is fired when a navigation gets cancelled but not caught by other
3899 * means. */
3900 if (!completed && !errored) {
3901 // Must reset to current URL tree here to ensure history.state is set. On a
3902 // fresh page load, if a new navigation comes in before a successful
3903 // navigation completes, there will be nothing in
3904 // history.state.navigationId. This can cause sync problems with AngularJS
3905 // sync code which looks for a value here in order to determine whether or
3906 // not to handle a given popstate event or to leave it to the Angualr
3907 // router.
3908 this.resetUrlToCurrentUrlTree();
3909 const navCancel = new NavigationCancel(t.id, this.serializeUrl(t.extractedUrl), `Navigation ID ${t.id} is not equal to the current navigation id ${this.navigationId}`);
3910 eventsSubject.next(navCancel);
3911 t.resolve(false);
3912 }
3913 // currentNavigation should always be reset to null here. If navigation was
3914 // successful, lastSuccessfulTransition will have already been set. Therefore
3915 // we can safely set currentNavigation to null here.
3916 this.currentNavigation = null;
3917 }), catchError((e) => {
3918 errored = true;
3919 /* This error type is issued during Redirect, and is handled as a
3920 * cancellation rather than an error. */
3921 if (isNavigationCancelingError(e)) {
3922 const redirecting = isUrlTree(e.url);
3923 if (!redirecting) {
3924 // Set property only if we're not redirecting. If we landed on a page and
3925 // redirect to `/` route, the new navigation is going to see the `/`
3926 // isn't a change from the default currentUrlTree and won't navigate.
3927 // This is only applicable with initial navigation, so setting
3928 // `navigated` only when not redirecting resolves this scenario.
3929 this.navigated = true;
3930 this.resetStateAndUrl(t.currentRouterState, t.currentUrlTree, t.rawUrl);
3931 }
3932 const navCancel = new NavigationCancel(t.id, this.serializeUrl(t.extractedUrl), e.message);
3933 eventsSubject.next(navCancel);
3934 // When redirecting, we need to delay resolving the navigation
3935 // promise and push it to the redirect navigation
3936 if (!redirecting) {
3937 t.resolve(false);
3938 }
3939 else {
3940 // setTimeout is required so this navigation finishes with
3941 // the return EMPTY below. If it isn't allowed to finish
3942 // processing, there can be multiple navigations to the same
3943 // URL.
3944 setTimeout(() => {
3945 const mergedTree = this.urlHandlingStrategy.merge(e.url, this.rawUrlTree);
3946 const extras = {
3947 skipLocationChange: t.extras.skipLocationChange,
3948 replaceUrl: this.urlUpdateStrategy === 'eager'
3949 };
3950 return this.scheduleNavigation(mergedTree, 'imperative', null, extras, { resolve: t.resolve, reject: t.reject, promise: t.promise });
3951 }, 0);
3952 }
3953 /* All other errors should reset to the router's internal URL reference to
3954 * the pre-error state. */
3955 }
3956 else {
3957 this.resetStateAndUrl(t.currentRouterState, t.currentUrlTree, t.rawUrl);
3958 const navError = new NavigationError(t.id, this.serializeUrl(t.extractedUrl), e);
3959 eventsSubject.next(navError);
3960 try {
3961 t.resolve(this.errorHandler(e));
3962 }
3963 catch (ee) {
3964 t.reject(ee);
3965 }
3966 }
3967 return EMPTY;
3968 }));
3969 // TODO(jasonaden): remove cast once g3 is on updated TypeScript
3970 }));
3971 }
3972 /**
3973 * @internal
3974 * TODO: this should be removed once the constructor of the router made internal
3975 */
3976 resetRootComponentType(rootComponentType) {
3977 this.rootComponentType = rootComponentType;
3978 // TODO: vsavkin router 4.0 should make the root component set to null
3979 // this will simplify the lifecycle of the router.
3980 this.routerState.root.component = this.rootComponentType;
3981 }
3982 getTransition() {
3983 const transition = this.transitions.value;
3984 // This value needs to be set. Other values such as extractedUrl are set on initial navigation
3985 // but the urlAfterRedirects may not get set if we aren't processing the new URL *and* not
3986 // processing the previous URL.
3987 transition.urlAfterRedirects = this.browserUrlTree;
3988 return transition;
3989 }
3990 setTransition(t) {
3991 this.transitions.next(Object.assign(Object.assign({}, this.getTransition()), t));
3992 }
3993 /**
3994 * Sets up the location change listener and performs the initial navigation.
3995 */
3996 initialNavigation() {
3997 this.setUpLocationChangeListener();
3998 if (this.navigationId === 0) {
3999 this.navigateByUrl(this.location.path(true), { replaceUrl: true });
4000 }
4001 }
4002 /**
4003 * Sets up the location change listener.
4004 */
4005 setUpLocationChangeListener() {
4006 // Don't need to use Zone.wrap any more, because zone.js
4007 // already patch onPopState, so location change callback will
4008 // run into ngZone
4009 if (!this.locationSubscription) {
4010 this.locationSubscription = this.location.subscribe((change) => {
4011 let rawUrlTree = this.parseUrl(change['url']);
4012 const source = change['type'] === 'popstate' ? 'popstate' : 'hashchange';
4013 // Navigations coming from Angular router have a navigationId state property. When this
4014 // exists, restore the state.
4015 const state = change.state && change.state.navigationId ? change.state : null;
4016 setTimeout(() => {
4017 this.scheduleNavigation(rawUrlTree, source, state, { replaceUrl: true });
4018 }, 0);
4019 });
4020 }
4021 }
4022 /** The current URL. */
4023 get url() {
4024 return this.serializeUrl(this.currentUrlTree);
4025 }
4026 /** The current Navigation object if one exists */
4027 getCurrentNavigation() {
4028 return this.currentNavigation;
4029 }
4030 /** @internal */
4031 triggerEvent(event) {
4032 this.events.next(event);
4033 }
4034 /**
4035 * Resets the route configuration used for navigation and generating links.
4036 *
4037 * @param config The route array for the new configuration.
4038 *
4039 * @usageNotes
4040 *
4041 * ```
4042 * router.resetConfig([
4043 * { path: 'team/:id', component: TeamCmp, children: [
4044 * { path: 'simple', component: SimpleCmp },
4045 * { path: 'user/:name', component: UserCmp }
4046 * ]}
4047 * ]);
4048 * ```
4049 */
4050 resetConfig(config) {
4051 validateConfig(config);
4052 this.config = config.map(standardizeConfig);
4053 this.navigated = false;
4054 this.lastSuccessfulId = -1;
4055 }
4056 /** @docsNotRequired */
4057 ngOnDestroy() {
4058 this.dispose();
4059 }
4060 /** Disposes of the router. */
4061 dispose() {
4062 if (this.locationSubscription) {
4063 this.locationSubscription.unsubscribe();
4064 this.locationSubscription = null;
4065 }
4066 }
4067 /**
4068 * Appends URL segments to the current URL tree to create a new URL tree.
4069 *
4070 * @param commands An array of URL fragments with which to construct the new URL tree.
4071 * If the path is static, can be the literal URL string. For a dynamic path, pass an array of path
4072 * segments, followed by the parameters for each segment.
4073 * The fragments are applied to the current URL tree or the one provided in the `relativeTo`
4074 * property of the options object, if supplied.
4075 * @param navigationExtras Options that control the navigation strategy. This function
4076 * only uses properties in `NavigationExtras` that would change the provided URL.
4077 * @returns The new URL tree.
4078 *
4079 * @usageNotes
4080 *
4081 * ```
4082 * // create /team/33/user/11
4083 * router.createUrlTree(['/team', 33, 'user', 11]);
4084 *
4085 * // create /team/33;expand=true/user/11
4086 * router.createUrlTree(['/team', 33, {expand: true}, 'user', 11]);
4087 *
4088 * // you can collapse static segments like this (this works only with the first passed-in value):
4089 * router.createUrlTree(['/team/33/user', userId]);
4090 *
4091 * // If the first segment can contain slashes, and you do not want the router to split it,
4092 * // you can do the following:
4093 * router.createUrlTree([{segmentPath: '/one/two'}]);
4094 *
4095 * // create /team/33/(user/11//right:chat)
4096 * router.createUrlTree(['/team', 33, {outlets: {primary: 'user/11', right: 'chat'}}]);
4097 *
4098 * // remove the right secondary node
4099 * router.createUrlTree(['/team', 33, {outlets: {primary: 'user/11', right: null}}]);
4100 *
4101 * // assuming the current url is `/team/33/user/11` and the route points to `user/11`
4102 *
4103 * // navigate to /team/33/user/11/details
4104 * router.createUrlTree(['details'], {relativeTo: route});
4105 *
4106 * // navigate to /team/33/user/22
4107 * router.createUrlTree(['../22'], {relativeTo: route});
4108 *
4109 * // navigate to /team/44/user/22
4110 * router.createUrlTree(['../../team/44/user/22'], {relativeTo: route});
4111 * ```
4112 */
4113 createUrlTree(commands, navigationExtras = {}) {
4114 const { relativeTo, queryParams, fragment, preserveQueryParams, queryParamsHandling, preserveFragment } = navigationExtras;
4115 if (isDevMode() && preserveQueryParams && console && console.warn) {
4116 console.warn('preserveQueryParams is deprecated, use queryParamsHandling instead.');
4117 }
4118 const a = relativeTo || this.routerState.root;
4119 const f = preserveFragment ? this.currentUrlTree.fragment : fragment;
4120 let q = null;
4121 if (queryParamsHandling) {
4122 switch (queryParamsHandling) {
4123 case 'merge':
4124 q = Object.assign(Object.assign({}, this.currentUrlTree.queryParams), queryParams);
4125 break;
4126 case 'preserve':
4127 q = this.currentUrlTree.queryParams;
4128 break;
4129 default:
4130 q = queryParams || null;
4131 }
4132 }
4133 else {
4134 q = preserveQueryParams ? this.currentUrlTree.queryParams : queryParams || null;
4135 }
4136 if (q !== null) {
4137 q = this.removeEmptyProps(q);
4138 }
4139 return createUrlTree(a, this.currentUrlTree, commands, q, f);
4140 }
4141 /**
4142 * Navigates to a view using an absolute route path.
4143 *
4144 * @param url An absolute path for a defined route. The function does not apply any delta to the
4145 * current URL.
4146 * @param extras An object containing properties that modify the navigation strategy.
4147 * The function ignores any properties in the `NavigationExtras` that would change the
4148 * provided URL.
4149 *
4150 * @returns A Promise that resolves to 'true' when navigation succeeds,
4151 * to 'false' when navigation fails, or is rejected on error.
4152 *
4153 * @usageNotes
4154 *
4155 * The following calls request navigation to an absolute path.
4156 *
4157 * ```
4158 * router.navigateByUrl("/team/33/user/11");
4159 *
4160 * // Navigate without updating the URL
4161 * router.navigateByUrl("/team/33/user/11", { skipLocationChange: true });
4162 * ```
4163 *
4164 * @see [Routing and Navigation guide](guide/router)
4165 *
4166 */
4167 navigateByUrl(url, extras = { skipLocationChange: false }) {
4168 if (isDevMode() && this.isNgZoneEnabled && !NgZone.isInAngularZone()) {
4169 this.console.warn(`Navigation triggered outside Angular zone, did you forget to call 'ngZone.run()'?`);
4170 }
4171 const urlTree = isUrlTree(url) ? url : this.parseUrl(url);
4172 const mergedTree = this.urlHandlingStrategy.merge(urlTree, this.rawUrlTree);
4173 return this.scheduleNavigation(mergedTree, 'imperative', null, extras);
4174 }
4175 /**
4176 * Navigate based on the provided array of commands and a starting point.
4177 * If no starting route is provided, the navigation is absolute.
4178 *
4179 * @param commands An array of URL fragments with which to construct the target URL.
4180 * If the path is static, can be the literal URL string. For a dynamic path, pass an array of path
4181 * segments, followed by the parameters for each segment.
4182 * The fragments are applied to the current URL or the one provided in the `relativeTo` property
4183 * of the options object, if supplied.
4184 * @param extras An options object that determines how the URL should be constructed or
4185 * interpreted.
4186 *
4187 * @returns A Promise that resolves to `true` when navigation succeeds, to `false` when navigation
4188 * fails,
4189 * or is rejected on error.
4190 *
4191 * @usageNotes
4192 *
4193 * The following calls request navigation to a dynamic route path relative to the current URL.
4194 *
4195 * ```
4196 * router.navigate(['team', 33, 'user', 11], {relativeTo: route});
4197 *
4198 * // Navigate without updating the URL, overriding the default behavior
4199 * router.navigate(['team', 33, 'user', 11], {relativeTo: route, skipLocationChange: true});
4200 * ```
4201 *
4202 * @see [Routing and Navigation guide](guide/router)
4203 *
4204 */
4205 navigate(commands, extras = { skipLocationChange: false }) {
4206 validateCommands(commands);
4207 return this.navigateByUrl(this.createUrlTree(commands, extras), extras);
4208 }
4209 /** Serializes a `UrlTree` into a string */
4210 serializeUrl(url) {
4211 return this.urlSerializer.serialize(url);
4212 }
4213 /** Parses a string into a `UrlTree` */
4214 parseUrl(url) {
4215 let urlTree;
4216 try {
4217 urlTree = this.urlSerializer.parse(url);
4218 }
4219 catch (e) {
4220 urlTree = this.malformedUriErrorHandler(e, this.urlSerializer, url);
4221 }
4222 return urlTree;
4223 }
4224 /** Returns whether the url is activated */
4225 isActive(url, exact) {
4226 if (isUrlTree(url)) {
4227 return containsTree(this.currentUrlTree, url, exact);
4228 }
4229 const urlTree = this.parseUrl(url);
4230 return containsTree(this.currentUrlTree, urlTree, exact);
4231 }
4232 removeEmptyProps(params) {
4233 return Object.keys(params).reduce((result, key) => {
4234 const value = params[key];
4235 if (value !== null && value !== undefined) {
4236 result[key] = value;
4237 }
4238 return result;
4239 }, {});
4240 }
4241 processNavigations() {
4242 this.navigations.subscribe(t => {
4243 this.navigated = true;
4244 this.lastSuccessfulId = t.id;
4245 this.events
4246 .next(new NavigationEnd(t.id, this.serializeUrl(t.extractedUrl), this.serializeUrl(this.currentUrlTree)));
4247 this.lastSuccessfulNavigation = this.currentNavigation;
4248 this.currentNavigation = null;
4249 t.resolve(true);
4250 }, e => {
4251 this.console.warn(`Unhandled Navigation Error: `);
4252 });
4253 }
4254 scheduleNavigation(rawUrl, source, restoredState, extras, priorPromise) {
4255 const lastNavigation = this.getTransition();
4256 // If the user triggers a navigation imperatively (e.g., by using navigateByUrl),
4257 // and that navigation results in 'replaceState' that leads to the same URL,
4258 // we should skip those.
4259 if (lastNavigation && source !== 'imperative' && lastNavigation.source === 'imperative' &&
4260 lastNavigation.rawUrl.toString() === rawUrl.toString()) {
4261 return Promise.resolve(true); // return value is not used
4262 }
4263 // Because of a bug in IE and Edge, the location class fires two events (popstate and
4264 // hashchange) every single time. The second one should be ignored. Otherwise, the URL will
4265 // flicker. Handles the case when a popstate was emitted first.
4266 if (lastNavigation && source == 'hashchange' && lastNavigation.source === 'popstate' &&
4267 lastNavigation.rawUrl.toString() === rawUrl.toString()) {
4268 return Promise.resolve(true); // return value is not used
4269 }
4270 // Because of a bug in IE and Edge, the location class fires two events (popstate and
4271 // hashchange) every single time. The second one should be ignored. Otherwise, the URL will
4272 // flicker. Handles the case when a hashchange was emitted first.
4273 if (lastNavigation && source == 'popstate' && lastNavigation.source === 'hashchange' &&
4274 lastNavigation.rawUrl.toString() === rawUrl.toString()) {
4275 return Promise.resolve(true); // return value is not used
4276 }
4277 let resolve;
4278 let reject;
4279 let promise;
4280 if (priorPromise) {
4281 resolve = priorPromise.resolve;
4282 reject = priorPromise.reject;
4283 promise = priorPromise.promise;
4284 }
4285 else {
4286 promise = new Promise((res, rej) => {
4287 resolve = res;
4288 reject = rej;
4289 });
4290 }
4291 const id = ++this.navigationId;
4292 this.setTransition({
4293 id,
4294 source,
4295 restoredState,
4296 currentUrlTree: this.currentUrlTree,
4297 currentRawUrl: this.rawUrlTree,
4298 rawUrl,
4299 extras,
4300 resolve,
4301 reject,
4302 promise,
4303 currentSnapshot: this.routerState.snapshot,
4304 currentRouterState: this.routerState
4305 });
4306 // Make sure that the error is propagated even though `processNavigations` catch
4307 // handler does not rethrow
4308 return promise.catch((e) => {
4309 return Promise.reject(e);
4310 });
4311 }
4312 setBrowserUrl(url, replaceUrl, id, state) {
4313 const path = this.urlSerializer.serialize(url);
4314 state = state || {};
4315 if (this.location.isCurrentPathEqualTo(path) || replaceUrl) {
4316 // TODO(jasonaden): Remove first `navigationId` and rely on `ng` namespace.
4317 this.location.replaceState(path, '', Object.assign(Object.assign({}, state), { navigationId: id }));
4318 }
4319 else {
4320 this.location.go(path, '', Object.assign(Object.assign({}, state), { navigationId: id }));
4321 }
4322 }
4323 resetStateAndUrl(storedState, storedUrl, rawUrl) {
4324 this.routerState = storedState;
4325 this.currentUrlTree = storedUrl;
4326 this.rawUrlTree = this.urlHandlingStrategy.merge(this.currentUrlTree, rawUrl);
4327 this.resetUrlToCurrentUrlTree();
4328 }
4329 resetUrlToCurrentUrlTree() {
4330 this.location.replaceState(this.urlSerializer.serialize(this.rawUrlTree), '', { navigationId: this.lastSuccessfulId });
4331 }
4332}
4333Router.decorators = [
4334 { type: Injectable }
4335];
4336Router.ctorParameters = () => [
4337 { type: Type },
4338 { type: UrlSerializer },
4339 { type: ChildrenOutletContexts },
4340 { type: Location },
4341 { type: Injector },
4342 { type: NgModuleFactoryLoader },
4343 { type: Compiler },
4344 { type: undefined }
4345];
4346function validateCommands(commands) {
4347 for (let i = 0; i < commands.length; i++) {
4348 const cmd = commands[i];
4349 if (cmd == null) {
4350 throw new Error(`The requested path contains ${cmd} segment at index ${i}`);
4351 }
4352 }
4353}
4354
4355/**
4356 * @license
4357 * Copyright Google LLC All Rights Reserved.
4358 *
4359 * Use of this source code is governed by an MIT-style license that can be
4360 * found in the LICENSE file at https://angular.io/license
4361 */
4362/**
4363 * @description
4364 *
4365 * Lets you link to specific routes in your app.
4366 *
4367 * Consider the following route configuration:
4368 * `[{ path: 'user/:name', component: UserCmp }]`.
4369 * When linking to this `user/:name` route, you use the `RouterLink` directive.
4370 *
4371 * If the link is static, you can use the directive as follows:
4372 * `<a routerLink="/user/bob">link to user component</a>`
4373 *
4374 * If you use dynamic values to generate the link, you can pass an array of path
4375 * segments, followed by the params for each segment.
4376 *
4377 * For instance `['/team', teamId, 'user', userName, {details: true}]`
4378 * means that we want to generate a link to `/team/11/user/bob;details=true`.
4379 *
4380 * Multiple static segments can be merged into one
4381 * (e.g., `['/team/11/user', userName, {details: true}]`).
4382 *
4383 * The first segment name can be prepended with `/`, `./`, or `../`:
4384 * * If the first segment begins with `/`, the router will look up the route from the root of the
4385 * app.
4386 * * If the first segment begins with `./`, or doesn't begin with a slash, the router will
4387 * instead look in the children of the current activated route.
4388 * * And if the first segment begins with `../`, the router will go up one level.
4389 *
4390 * You can set query params and fragment as follows:
4391 *
4392 * ```
4393 * <a [routerLink]="['/user/bob']" [queryParams]="{debug: true}" fragment="education">
4394 * link to user component
4395 * </a>
4396 * ```
4397 * RouterLink will use these to generate this link: `/user/bob?debug=true#education`.
4398 *
4399 * (Deprecated in v4.0.0 use `queryParamsHandling` instead) You can also tell the
4400 * directive to preserve the current query params and fragment:
4401 *
4402 * ```
4403 * <a [routerLink]="['/user/bob']" preserveQueryParams preserveFragment>
4404 * link to user component
4405 * </a>
4406 * ```
4407 *
4408 * You can tell the directive how to handle queryParams. Available options are:
4409 * - `'merge'`: merge the queryParams into the current queryParams
4410 * - `'preserve'`: preserve the current queryParams
4411 * - default/`''`: use the queryParams only
4412 *
4413 * Same options for {@link NavigationExtras#queryParamsHandling
4414 * NavigationExtras#queryParamsHandling}.
4415 *
4416 * ```
4417 * <a [routerLink]="['/user/bob']" [queryParams]="{debug: true}" queryParamsHandling="merge">
4418 * link to user component
4419 * </a>
4420 * ```
4421 *
4422 * You can provide a `state` value to be persisted to the browser's History.state
4423 * property (See https://developer.mozilla.org/en-US/docs/Web/API/History#Properties). It's
4424 * used as follows:
4425 *
4426 * ```
4427 * <a [routerLink]="['/user/bob']" [state]="{tracingId: 123}">
4428 * link to user component
4429 * </a>
4430 * ```
4431 *
4432 * And later the value can be read from the router through `router.getCurrentNavigation`.
4433 * For example, to capture the `tracingId` above during the `NavigationStart` event:
4434 *
4435 * ```
4436 * // Get NavigationStart events
4437 * router.events.pipe(filter(e => e instanceof NavigationStart)).subscribe(e => {
4438 * const navigation = router.getCurrentNavigation();
4439 * tracingService.trace({id: navigation.extras.state.tracingId});
4440 * });
4441 * ```
4442 *
4443 * The router link directive always treats the provided input as a delta to the current url.
4444 *
4445 * For instance, if the current url is `/user/(box//aux:team)`.
4446 *
4447 * Then the following link `<a [routerLink]="['/user/jim']">Jim</a>` will generate the link
4448 * `/user/(jim//aux:team)`.
4449 *
4450 * See {@link Router#createUrlTree createUrlTree} for more information.
4451 *
4452 * @ngModule RouterModule
4453 *
4454 * @publicApi
4455 */
4456class RouterLink {
4457 constructor(router, route, tabIndex, renderer, el) {
4458 this.router = router;
4459 this.route = route;
4460 this.commands = [];
4461 if (tabIndex == null) {
4462 renderer.setAttribute(el.nativeElement, 'tabindex', '0');
4463 }
4464 }
4465 /**
4466 * Commands to pass to {@link Router#createUrlTree Router#createUrlTree}.
4467 * - **array**: commands to pass to {@link Router#createUrlTree Router#createUrlTree}.
4468 * - **string**: shorthand for array of commands with just the string, i.e. `['/route']`
4469 * - **null|undefined**: shorthand for an empty array of commands, i.e. `[]`
4470 * @see {@link Router#createUrlTree Router#createUrlTree}
4471 */
4472 set routerLink(commands) {
4473 if (commands != null) {
4474 this.commands = Array.isArray(commands) ? commands : [commands];
4475 }
4476 else {
4477 this.commands = [];
4478 }
4479 }
4480 /**
4481 * @deprecated As of Angular v4.0 use `queryParamsHandling` instead.
4482 */
4483 set preserveQueryParams(value) {
4484 if (isDevMode() && console && console.warn) {
4485 console.warn('preserveQueryParams is deprecated!, use queryParamsHandling instead.');
4486 }
4487 this.preserve = value;
4488 }
4489 onClick() {
4490 const extras = {
4491 skipLocationChange: attrBoolValue(this.skipLocationChange),
4492 replaceUrl: attrBoolValue(this.replaceUrl),
4493 state: this.state,
4494 };
4495 this.router.navigateByUrl(this.urlTree, extras);
4496 return true;
4497 }
4498 get urlTree() {
4499 return this.router.createUrlTree(this.commands, {
4500 relativeTo: this.route,
4501 queryParams: this.queryParams,
4502 fragment: this.fragment,
4503 preserveQueryParams: attrBoolValue(this.preserve),
4504 queryParamsHandling: this.queryParamsHandling,
4505 preserveFragment: attrBoolValue(this.preserveFragment),
4506 });
4507 }
4508}
4509RouterLink.decorators = [
4510 { type: Directive, args: [{ selector: ':not(a):not(area)[routerLink]' },] }
4511];
4512RouterLink.ctorParameters = () => [
4513 { type: Router },
4514 { type: ActivatedRoute },
4515 { type: String, decorators: [{ type: Attribute, args: ['tabindex',] }] },
4516 { type: Renderer2 },
4517 { type: ElementRef }
4518];
4519RouterLink.propDecorators = {
4520 queryParams: [{ type: Input }],
4521 fragment: [{ type: Input }],
4522 queryParamsHandling: [{ type: Input }],
4523 preserveFragment: [{ type: Input }],
4524 skipLocationChange: [{ type: Input }],
4525 replaceUrl: [{ type: Input }],
4526 state: [{ type: Input }],
4527 routerLink: [{ type: Input }],
4528 preserveQueryParams: [{ type: Input }],
4529 onClick: [{ type: HostListener, args: ['click',] }]
4530};
4531/**
4532 * @description
4533 *
4534 * Lets you link to specific routes in your app.
4535 *
4536 * See `RouterLink` for more information.
4537 *
4538 * @ngModule RouterModule
4539 *
4540 * @publicApi
4541 */
4542class RouterLinkWithHref {
4543 constructor(router, route, locationStrategy) {
4544 this.router = router;
4545 this.route = route;
4546 this.locationStrategy = locationStrategy;
4547 this.commands = [];
4548 this.subscription = router.events.subscribe((s) => {
4549 if (s instanceof NavigationEnd) {
4550 this.updateTargetUrlAndHref();
4551 }
4552 });
4553 }
4554 /**
4555 * Commands to pass to {@link Router#createUrlTree Router#createUrlTree}.
4556 * - **array**: commands to pass to {@link Router#createUrlTree Router#createUrlTree}.
4557 * - **string**: shorthand for array of commands with just the string, i.e. `['/route']`
4558 * - **null|undefined**: shorthand for an empty array of commands, i.e. `[]`
4559 * @see {@link Router#createUrlTree Router#createUrlTree}
4560 */
4561 set routerLink(commands) {
4562 if (commands != null) {
4563 this.commands = Array.isArray(commands) ? commands : [commands];
4564 }
4565 else {
4566 this.commands = [];
4567 }
4568 }
4569 /**
4570 * @deprecated As of Angular v4.0 use `queryParamsHandling` instead.
4571 */
4572 set preserveQueryParams(value) {
4573 if (isDevMode() && console && console.warn) {
4574 console.warn('preserveQueryParams is deprecated, use queryParamsHandling instead.');
4575 }
4576 this.preserve = value;
4577 }
4578 ngOnChanges(changes) {
4579 this.updateTargetUrlAndHref();
4580 }
4581 ngOnDestroy() {
4582 this.subscription.unsubscribe();
4583 }
4584 onClick(button, ctrlKey, metaKey, shiftKey) {
4585 if (button !== 0 || ctrlKey || metaKey || shiftKey) {
4586 return true;
4587 }
4588 if (typeof this.target === 'string' && this.target != '_self') {
4589 return true;
4590 }
4591 const extras = {
4592 skipLocationChange: attrBoolValue(this.skipLocationChange),
4593 replaceUrl: attrBoolValue(this.replaceUrl),
4594 state: this.state
4595 };
4596 this.router.navigateByUrl(this.urlTree, extras);
4597 return false;
4598 }
4599 updateTargetUrlAndHref() {
4600 this.href = this.locationStrategy.prepareExternalUrl(this.router.serializeUrl(this.urlTree));
4601 }
4602 get urlTree() {
4603 return this.router.createUrlTree(this.commands, {
4604 relativeTo: this.route,
4605 queryParams: this.queryParams,
4606 fragment: this.fragment,
4607 preserveQueryParams: attrBoolValue(this.preserve),
4608 queryParamsHandling: this.queryParamsHandling,
4609 preserveFragment: attrBoolValue(this.preserveFragment),
4610 });
4611 }
4612}
4613RouterLinkWithHref.decorators = [
4614 { type: Directive, args: [{ selector: 'a[routerLink],area[routerLink]' },] }
4615];
4616RouterLinkWithHref.ctorParameters = () => [
4617 { type: Router },
4618 { type: ActivatedRoute },
4619 { type: LocationStrategy }
4620];
4621RouterLinkWithHref.propDecorators = {
4622 target: [{ type: HostBinding, args: ['attr.target',] }, { type: Input }],
4623 queryParams: [{ type: Input }],
4624 fragment: [{ type: Input }],
4625 queryParamsHandling: [{ type: Input }],
4626 preserveFragment: [{ type: Input }],
4627 skipLocationChange: [{ type: Input }],
4628 replaceUrl: [{ type: Input }],
4629 state: [{ type: Input }],
4630 href: [{ type: HostBinding }],
4631 routerLink: [{ type: Input }],
4632 preserveQueryParams: [{ type: Input }],
4633 onClick: [{ type: HostListener, args: ['click', ['$event.button', '$event.ctrlKey', '$event.metaKey', '$event.shiftKey'],] }]
4634};
4635function attrBoolValue(s) {
4636 return s === '' || !!s;
4637}
4638
4639/**
4640 * @license
4641 * Copyright Google LLC All Rights Reserved.
4642 *
4643 * Use of this source code is governed by an MIT-style license that can be
4644 * found in the LICENSE file at https://angular.io/license
4645 */
4646/**
4647 *
4648 * @description
4649 *
4650 * Lets you add a CSS class to an element when the link's route becomes active.
4651 *
4652 * This directive lets you add a CSS class to an element when the link's route
4653 * becomes active.
4654 *
4655 * Consider the following example:
4656 *
4657 * ```
4658 * <a routerLink="/user/bob" routerLinkActive="active-link">Bob</a>
4659 * ```
4660 *
4661 * When the url is either '/user' or '/user/bob', the active-link class will
4662 * be added to the `a` tag. If the url changes, the class will be removed.
4663 *
4664 * You can set more than one class, as follows:
4665 *
4666 * ```
4667 * <a routerLink="/user/bob" routerLinkActive="class1 class2">Bob</a>
4668 * <a routerLink="/user/bob" [routerLinkActive]="['class1', 'class2']">Bob</a>
4669 * ```
4670 *
4671 * You can configure RouterLinkActive by passing `exact: true`. This will add the classes
4672 * only when the url matches the link exactly.
4673 *
4674 * ```
4675 * <a routerLink="/user/bob" routerLinkActive="active-link" [routerLinkActiveOptions]="{exact:
4676 * true}">Bob</a>
4677 * ```
4678 *
4679 * You can assign the RouterLinkActive instance to a template variable and directly check
4680 * the `isActive` status.
4681 * ```
4682 * <a routerLink="/user/bob" routerLinkActive #rla="routerLinkActive">
4683 * Bob {{ rla.isActive ? '(already open)' : ''}}
4684 * </a>
4685 * ```
4686 *
4687 * Finally, you can apply the RouterLinkActive directive to an ancestor of a RouterLink.
4688 *
4689 * ```
4690 * <div routerLinkActive="active-link" [routerLinkActiveOptions]="{exact: true}">
4691 * <a routerLink="/user/jim">Jim</a>
4692 * <a routerLink="/user/bob">Bob</a>
4693 * </div>
4694 * ```
4695 *
4696 * This will set the active-link class on the div tag if the url is either '/user/jim' or
4697 * '/user/bob'.
4698 *
4699 * @ngModule RouterModule
4700 *
4701 * @publicApi
4702 */
4703class RouterLinkActive {
4704 constructor(router, element, renderer, cdr, link, linkWithHref) {
4705 this.router = router;
4706 this.element = element;
4707 this.renderer = renderer;
4708 this.cdr = cdr;
4709 this.link = link;
4710 this.linkWithHref = linkWithHref;
4711 this.classes = [];
4712 this.isActive = false;
4713 this.routerLinkActiveOptions = { exact: false };
4714 this.subscription = router.events.subscribe((s) => {
4715 if (s instanceof NavigationEnd) {
4716 this.update();
4717 }
4718 });
4719 }
4720 ngAfterContentInit() {
4721 this.links.changes.subscribe(_ => this.update());
4722 this.linksWithHrefs.changes.subscribe(_ => this.update());
4723 this.update();
4724 }
4725 set routerLinkActive(data) {
4726 const classes = Array.isArray(data) ? data : data.split(' ');
4727 this.classes = classes.filter(c => !!c);
4728 }
4729 ngOnChanges(changes) {
4730 this.update();
4731 }
4732 ngOnDestroy() {
4733 this.subscription.unsubscribe();
4734 }
4735 update() {
4736 if (!this.links || !this.linksWithHrefs || !this.router.navigated)
4737 return;
4738 Promise.resolve().then(() => {
4739 const hasActiveLinks = this.hasActiveLinks();
4740 if (this.isActive !== hasActiveLinks) {
4741 this.isActive = hasActiveLinks;
4742 this.cdr.markForCheck();
4743 this.classes.forEach((c) => {
4744 if (hasActiveLinks) {
4745 this.renderer.addClass(this.element.nativeElement, c);
4746 }
4747 else {
4748 this.renderer.removeClass(this.element.nativeElement, c);
4749 }
4750 });
4751 }
4752 });
4753 }
4754 isLinkActive(router) {
4755 return (link) => router.isActive(link.urlTree, this.routerLinkActiveOptions.exact);
4756 }
4757 hasActiveLinks() {
4758 const isActiveCheckFn = this.isLinkActive(this.router);
4759 return this.link && isActiveCheckFn(this.link) ||
4760 this.linkWithHref && isActiveCheckFn(this.linkWithHref) ||
4761 this.links.some(isActiveCheckFn) || this.linksWithHrefs.some(isActiveCheckFn);
4762 }
4763}
4764RouterLinkActive.decorators = [
4765 { type: Directive, args: [{
4766 selector: '[routerLinkActive]',
4767 exportAs: 'routerLinkActive',
4768 },] }
4769];
4770RouterLinkActive.ctorParameters = () => [
4771 { type: Router },
4772 { type: ElementRef },
4773 { type: Renderer2 },
4774 { type: ChangeDetectorRef },
4775 { type: RouterLink, decorators: [{ type: Optional }] },
4776 { type: RouterLinkWithHref, decorators: [{ type: Optional }] }
4777];
4778RouterLinkActive.propDecorators = {
4779 links: [{ type: ContentChildren, args: [RouterLink, { descendants: true },] }],
4780 linksWithHrefs: [{ type: ContentChildren, args: [RouterLinkWithHref, { descendants: true },] }],
4781 routerLinkActiveOptions: [{ type: Input }],
4782 routerLinkActive: [{ type: Input }]
4783};
4784
4785/**
4786 * @license
4787 * Copyright Google LLC All Rights Reserved.
4788 *
4789 * Use of this source code is governed by an MIT-style license that can be
4790 * found in the LICENSE file at https://angular.io/license
4791 */
4792/**
4793 * @description
4794 *
4795 * Acts as a placeholder that Angular dynamically fills based on the current router state.
4796 *
4797 * Each outlet can have a unique name, determined by the optional `name` attribute.
4798 * The name cannot be set or changed dynamically. If not set, default value is "primary".
4799 *
4800 * ```
4801 * <router-outlet></router-outlet>
4802 * <router-outlet name='left'></router-outlet>
4803 * <router-outlet name='right'></router-outlet>
4804 * ```
4805 *
4806 * A router outlet emits an activate event when a new component is instantiated,
4807 * and a deactivate event when a component is destroyed.
4808 *
4809 * ```
4810 * <router-outlet
4811 * (activate)='onActivate($event)'
4812 * (deactivate)='onDeactivate($event)'></router-outlet>
4813 * ```
4814 * @ngModule RouterModule
4815 *
4816 * @publicApi
4817 */
4818class RouterOutlet {
4819 constructor(parentContexts, location, resolver, name, changeDetector) {
4820 this.parentContexts = parentContexts;
4821 this.location = location;
4822 this.resolver = resolver;
4823 this.changeDetector = changeDetector;
4824 this.activated = null;
4825 this._activatedRoute = null;
4826 this.activateEvents = new EventEmitter();
4827 this.deactivateEvents = new EventEmitter();
4828 this.name = name || PRIMARY_OUTLET;
4829 parentContexts.onChildOutletCreated(this.name, this);
4830 }
4831 ngOnDestroy() {
4832 this.parentContexts.onChildOutletDestroyed(this.name);
4833 }
4834 ngOnInit() {
4835 if (!this.activated) {
4836 // If the outlet was not instantiated at the time the route got activated we need to populate
4837 // the outlet when it is initialized (ie inside a NgIf)
4838 const context = this.parentContexts.getContext(this.name);
4839 if (context && context.route) {
4840 if (context.attachRef) {
4841 // `attachRef` is populated when there is an existing component to mount
4842 this.attach(context.attachRef, context.route);
4843 }
4844 else {
4845 // otherwise the component defined in the configuration is created
4846 this.activateWith(context.route, context.resolver || null);
4847 }
4848 }
4849 }
4850 }
4851 get isActivated() {
4852 return !!this.activated;
4853 }
4854 get component() {
4855 if (!this.activated)
4856 throw new Error('Outlet is not activated');
4857 return this.activated.instance;
4858 }
4859 get activatedRoute() {
4860 if (!this.activated)
4861 throw new Error('Outlet is not activated');
4862 return this._activatedRoute;
4863 }
4864 get activatedRouteData() {
4865 if (this._activatedRoute) {
4866 return this._activatedRoute.snapshot.data;
4867 }
4868 return {};
4869 }
4870 /**
4871 * Called when the `RouteReuseStrategy` instructs to detach the subtree
4872 */
4873 detach() {
4874 if (!this.activated)
4875 throw new Error('Outlet is not activated');
4876 this.location.detach();
4877 const cmp = this.activated;
4878 this.activated = null;
4879 this._activatedRoute = null;
4880 return cmp;
4881 }
4882 /**
4883 * Called when the `RouteReuseStrategy` instructs to re-attach a previously detached subtree
4884 */
4885 attach(ref, activatedRoute) {
4886 this.activated = ref;
4887 this._activatedRoute = activatedRoute;
4888 this.location.insert(ref.hostView);
4889 }
4890 deactivate() {
4891 if (this.activated) {
4892 const c = this.component;
4893 this.activated.destroy();
4894 this.activated = null;
4895 this._activatedRoute = null;
4896 this.deactivateEvents.emit(c);
4897 }
4898 }
4899 activateWith(activatedRoute, resolver) {
4900 if (this.isActivated) {
4901 throw new Error('Cannot activate an already activated outlet');
4902 }
4903 this._activatedRoute = activatedRoute;
4904 const snapshot = activatedRoute._futureSnapshot;
4905 const component = snapshot.routeConfig.component;
4906 resolver = resolver || this.resolver;
4907 const factory = resolver.resolveComponentFactory(component);
4908 const childContexts = this.parentContexts.getOrCreateContext(this.name).children;
4909 const injector = new OutletInjector(activatedRoute, childContexts, this.location.injector);
4910 this.activated = this.location.createComponent(factory, this.location.length, injector);
4911 // Calling `markForCheck` to make sure we will run the change detection when the
4912 // `RouterOutlet` is inside a `ChangeDetectionStrategy.OnPush` component.
4913 this.changeDetector.markForCheck();
4914 this.activateEvents.emit(this.activated.instance);
4915 }
4916}
4917RouterOutlet.decorators = [
4918 { type: Directive, args: [{ selector: 'router-outlet', exportAs: 'outlet' },] }
4919];
4920RouterOutlet.ctorParameters = () => [
4921 { type: ChildrenOutletContexts },
4922 { type: ViewContainerRef },
4923 { type: ComponentFactoryResolver },
4924 { type: String, decorators: [{ type: Attribute, args: ['name',] }] },
4925 { type: ChangeDetectorRef }
4926];
4927RouterOutlet.propDecorators = {
4928 activateEvents: [{ type: Output, args: ['activate',] }],
4929 deactivateEvents: [{ type: Output, args: ['deactivate',] }]
4930};
4931class OutletInjector {
4932 constructor(route, childContexts, parent) {
4933 this.route = route;
4934 this.childContexts = childContexts;
4935 this.parent = parent;
4936 }
4937 get(token, notFoundValue) {
4938 if (token === ActivatedRoute) {
4939 return this.route;
4940 }
4941 if (token === ChildrenOutletContexts) {
4942 return this.childContexts;
4943 }
4944 return this.parent.get(token, notFoundValue);
4945 }
4946}
4947
4948/**
4949 * @license
4950 * Copyright Google LLC All Rights Reserved.
4951 *
4952 * Use of this source code is governed by an MIT-style license that can be
4953 * found in the LICENSE file at https://angular.io/license
4954 */
4955/**
4956 * @description
4957 *
4958 * Provides a preloading strategy.
4959 *
4960 * @publicApi
4961 */
4962class PreloadingStrategy {
4963}
4964/**
4965 * @description
4966 *
4967 * Provides a preloading strategy that preloads all modules as quickly as possible.
4968 *
4969 * ```
4970 * RouteModule.forRoot(ROUTES, {preloadingStrategy: PreloadAllModules})
4971 * ```
4972 *
4973 * @publicApi
4974 */
4975class PreloadAllModules {
4976 preload(route, fn) {
4977 return fn().pipe(catchError(() => of(null)));
4978 }
4979}
4980/**
4981 * @description
4982 *
4983 * Provides a preloading strategy that does not preload any modules.
4984 *
4985 * This strategy is enabled by default.
4986 *
4987 * @publicApi
4988 */
4989class NoPreloading {
4990 preload(route, fn) {
4991 return of(null);
4992 }
4993}
4994/**
4995 * The preloader optimistically loads all router configurations to
4996 * make navigations into lazily-loaded sections of the application faster.
4997 *
4998 * The preloader runs in the background. When the router bootstraps, the preloader
4999 * starts listening to all navigation events. After every such event, the preloader
5000 * will check if any configurations can be loaded lazily.
5001 *
5002 * If a route is protected by `canLoad` guards, the preloaded will not load it.
5003 *
5004 * @publicApi
5005 */
5006class RouterPreloader {
5007 constructor(router, moduleLoader, compiler, injector, preloadingStrategy) {
5008 this.router = router;
5009 this.injector = injector;
5010 this.preloadingStrategy = preloadingStrategy;
5011 const onStartLoad = (r) => router.triggerEvent(new RouteConfigLoadStart(r));
5012 const onEndLoad = (r) => router.triggerEvent(new RouteConfigLoadEnd(r));
5013 this.loader = new RouterConfigLoader(moduleLoader, compiler, onStartLoad, onEndLoad);
5014 }
5015 setUpPreloading() {
5016 this.subscription =
5017 this.router.events
5018 .pipe(filter((e) => e instanceof NavigationEnd), concatMap(() => this.preload()))
5019 .subscribe(() => { });
5020 }
5021 preload() {
5022 const ngModule = this.injector.get(NgModuleRef);
5023 return this.processRoutes(ngModule, this.router.config);
5024 }
5025 // TODO(jasonaden): This class relies on code external to the class to call setUpPreloading. If
5026 // this hasn't been done, ngOnDestroy will fail as this.subscription will be undefined. This
5027 // should be refactored.
5028 ngOnDestroy() {
5029 this.subscription.unsubscribe();
5030 }
5031 processRoutes(ngModule, routes) {
5032 const res = [];
5033 for (const route of routes) {
5034 // we already have the config loaded, just recurse
5035 if (route.loadChildren && !route.canLoad && route._loadedConfig) {
5036 const childConfig = route._loadedConfig;
5037 res.push(this.processRoutes(childConfig.module, childConfig.routes));
5038 // no config loaded, fetch the config
5039 }
5040 else if (route.loadChildren && !route.canLoad) {
5041 res.push(this.preloadConfig(ngModule, route));
5042 // recurse into children
5043 }
5044 else if (route.children) {
5045 res.push(this.processRoutes(ngModule, route.children));
5046 }
5047 }
5048 return from(res).pipe(mergeAll(), map((_) => void 0));
5049 }
5050 preloadConfig(ngModule, route) {
5051 return this.preloadingStrategy.preload(route, () => {
5052 const loaded$ = this.loader.load(ngModule.injector, route);
5053 return loaded$.pipe(mergeMap((config) => {
5054 route._loadedConfig = config;
5055 return this.processRoutes(config.module, config.routes);
5056 }));
5057 });
5058 }
5059}
5060RouterPreloader.decorators = [
5061 { type: Injectable }
5062];
5063RouterPreloader.ctorParameters = () => [
5064 { type: Router },
5065 { type: NgModuleFactoryLoader },
5066 { type: Compiler },
5067 { type: Injector },
5068 { type: PreloadingStrategy }
5069];
5070
5071/**
5072 * @license
5073 * Copyright Google LLC All Rights Reserved.
5074 *
5075 * Use of this source code is governed by an MIT-style license that can be
5076 * found in the LICENSE file at https://angular.io/license
5077 */
5078class RouterScroller {
5079 constructor(router,
5080 /** @docsNotRequired */ viewportScroller, options = {}) {
5081 this.router = router;
5082 this.viewportScroller = viewportScroller;
5083 this.options = options;
5084 this.lastId = 0;
5085 this.lastSource = 'imperative';
5086 this.restoredId = 0;
5087 this.store = {};
5088 // Default both options to 'disabled'
5089 options.scrollPositionRestoration = options.scrollPositionRestoration || 'disabled';
5090 options.anchorScrolling = options.anchorScrolling || 'disabled';
5091 }
5092 init() {
5093 // we want to disable the automatic scrolling because having two places
5094 // responsible for scrolling results race conditions, especially given
5095 // that browser don't implement this behavior consistently
5096 if (this.options.scrollPositionRestoration !== 'disabled') {
5097 this.viewportScroller.setHistoryScrollRestoration('manual');
5098 }
5099 this.routerEventsSubscription = this.createScrollEvents();
5100 this.scrollEventsSubscription = this.consumeScrollEvents();
5101 }
5102 createScrollEvents() {
5103 return this.router.events.subscribe(e => {
5104 if (e instanceof NavigationStart) {
5105 // store the scroll position of the current stable navigations.
5106 this.store[this.lastId] = this.viewportScroller.getScrollPosition();
5107 this.lastSource = e.navigationTrigger;
5108 this.restoredId = e.restoredState ? e.restoredState.navigationId : 0;
5109 }
5110 else if (e instanceof NavigationEnd) {
5111 this.lastId = e.id;
5112 this.scheduleScrollEvent(e, this.router.parseUrl(e.urlAfterRedirects).fragment);
5113 }
5114 });
5115 }
5116 consumeScrollEvents() {
5117 return this.router.events.subscribe(e => {
5118 if (!(e instanceof Scroll))
5119 return;
5120 // a popstate event. The pop state event will always ignore anchor scrolling.
5121 if (e.position) {
5122 if (this.options.scrollPositionRestoration === 'top') {
5123 this.viewportScroller.scrollToPosition([0, 0]);
5124 }
5125 else if (this.options.scrollPositionRestoration === 'enabled') {
5126 this.viewportScroller.scrollToPosition(e.position);
5127 }
5128 // imperative navigation "forward"
5129 }
5130 else {
5131 if (e.anchor && this.options.anchorScrolling === 'enabled') {
5132 this.viewportScroller.scrollToAnchor(e.anchor);
5133 }
5134 else if (this.options.scrollPositionRestoration !== 'disabled') {
5135 this.viewportScroller.scrollToPosition([0, 0]);
5136 }
5137 }
5138 });
5139 }
5140 scheduleScrollEvent(routerEvent, anchor) {
5141 this.router.triggerEvent(new Scroll(routerEvent, this.lastSource === 'popstate' ? this.store[this.restoredId] : null, anchor));
5142 }
5143 ngOnDestroy() {
5144 if (this.routerEventsSubscription) {
5145 this.routerEventsSubscription.unsubscribe();
5146 }
5147 if (this.scrollEventsSubscription) {
5148 this.scrollEventsSubscription.unsubscribe();
5149 }
5150 }
5151}
5152RouterScroller.decorators = [
5153 { type: Injectable }
5154];
5155RouterScroller.ctorParameters = () => [
5156 { type: Router },
5157 { type: ViewportScroller },
5158 { type: undefined }
5159];
5160
5161/**
5162 * @license
5163 * Copyright Google LLC All Rights Reserved.
5164 *
5165 * Use of this source code is governed by an MIT-style license that can be
5166 * found in the LICENSE file at https://angular.io/license
5167 */
5168/**
5169 * The directives defined in the `RouterModule`.
5170 */
5171const ROUTER_DIRECTIVES = [RouterOutlet, RouterLink, RouterLinkWithHref, RouterLinkActive, ɵEmptyOutletComponent];
5172/**
5173 * A [DI token](guide/glossary/#di-token) for the router service.
5174 *
5175 * @publicApi
5176 */
5177const ROUTER_CONFIGURATION = new InjectionToken('ROUTER_CONFIGURATION');
5178/**
5179 * @docsNotRequired
5180 */
5181const ROUTER_FORROOT_GUARD = new InjectionToken('ROUTER_FORROOT_GUARD');
5182const ɵ0 = { enableTracing: false };
5183const ROUTER_PROVIDERS = [
5184 Location,
5185 { provide: UrlSerializer, useClass: DefaultUrlSerializer },
5186 {
5187 provide: Router,
5188 useFactory: setupRouter,
5189 deps: [
5190 UrlSerializer, ChildrenOutletContexts, Location, Injector, NgModuleFactoryLoader, Compiler,
5191 ROUTES, ROUTER_CONFIGURATION, [UrlHandlingStrategy, new Optional()],
5192 [RouteReuseStrategy, new Optional()]
5193 ]
5194 },
5195 ChildrenOutletContexts,
5196 { provide: ActivatedRoute, useFactory: rootRoute, deps: [Router] },
5197 { provide: NgModuleFactoryLoader, useClass: SystemJsNgModuleLoader },
5198 RouterPreloader,
5199 NoPreloading,
5200 PreloadAllModules,
5201 { provide: ROUTER_CONFIGURATION, useValue: ɵ0 },
5202];
5203function routerNgProbeToken() {
5204 return new NgProbeToken('Router', Router);
5205}
5206/**
5207 * @description
5208 *
5209 * Adds directives and providers for in-app navigation among views defined in an application.
5210 * Use the Angular `Router` service to declaratively specify application states and manage state
5211 * transitions.
5212 *
5213 * You can import this NgModule multiple times, once for each lazy-loaded bundle.
5214 * However, only one `Router` service can be active.
5215 * To ensure this, there are two ways to register routes when importing this module:
5216 *
5217 * * The `forRoot()` method creates an `NgModule` that contains all the directives, the given
5218 * routes, and the `Router` service itself.
5219 * * The `forChild()` method creates an `NgModule` that contains all the directives and the given
5220 * routes, but does not include the `Router` service.
5221 *
5222 * @see [Routing and Navigation guide](guide/router) for an
5223 * overview of how the `Router` service should be used.
5224 *
5225 * @publicApi
5226 */
5227class RouterModule {
5228 // Note: We are injecting the Router so it gets created eagerly...
5229 constructor(guard, router) { }
5230 /**
5231 * Creates and configures a module with all the router providers and directives.
5232 * Optionally sets up an application listener to perform an initial navigation.
5233 *
5234 * When registering the NgModule at the root, import as follows:
5235 *
5236 * ```
5237 * @NgModule({
5238 * imports: [RouterModule.forRoot(ROUTES)]
5239 * })
5240 * class MyNgModule {}
5241 * ```
5242 *
5243 * @param routes An array of `Route` objects that define the navigation paths for the application.
5244 * @param config An `ExtraOptions` configuration object that controls how navigation is performed.
5245 * @return The new `NgModule`.
5246 *
5247 */
5248 static forRoot(routes, config) {
5249 return {
5250 ngModule: RouterModule,
5251 providers: [
5252 ROUTER_PROVIDERS,
5253 provideRoutes(routes),
5254 {
5255 provide: ROUTER_FORROOT_GUARD,
5256 useFactory: provideForRootGuard,
5257 deps: [[Router, new Optional(), new SkipSelf()]]
5258 },
5259 { provide: ROUTER_CONFIGURATION, useValue: config ? config : {} },
5260 {
5261 provide: LocationStrategy,
5262 useFactory: provideLocationStrategy,
5263 deps: [PlatformLocation, [new Inject(APP_BASE_HREF), new Optional()], ROUTER_CONFIGURATION]
5264 },
5265 {
5266 provide: RouterScroller,
5267 useFactory: createRouterScroller,
5268 deps: [Router, ViewportScroller, ROUTER_CONFIGURATION]
5269 },
5270 {
5271 provide: PreloadingStrategy,
5272 useExisting: config && config.preloadingStrategy ? config.preloadingStrategy :
5273 NoPreloading
5274 },
5275 { provide: NgProbeToken, multi: true, useFactory: routerNgProbeToken },
5276 provideRouterInitializer(),
5277 ],
5278 };
5279 }
5280 /**
5281 * Creates a module with all the router directives and a provider registering routes,
5282 * without creating a new Router service.
5283 * When registering for submodules and lazy-loaded submodules, create the NgModule as follows:
5284 *
5285 * ```
5286 * @NgModule({
5287 * imports: [RouterModule.forChild(ROUTES)]
5288 * })
5289 * class MyNgModule {}
5290 * ```
5291 *
5292 * @param routes An array of `Route` objects that define the navigation paths for the submodule.
5293 * @return The new NgModule.
5294 *
5295 */
5296 static forChild(routes) {
5297 return { ngModule: RouterModule, providers: [provideRoutes(routes)] };
5298 }
5299}
5300RouterModule.decorators = [
5301 { type: NgModule, args: [{
5302 declarations: ROUTER_DIRECTIVES,
5303 exports: ROUTER_DIRECTIVES,
5304 entryComponents: [ɵEmptyOutletComponent]
5305 },] }
5306];
5307RouterModule.ctorParameters = () => [
5308 { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [ROUTER_FORROOT_GUARD,] }] },
5309 { type: Router, decorators: [{ type: Optional }] }
5310];
5311function createRouterScroller(router, viewportScroller, config) {
5312 if (config.scrollOffset) {
5313 viewportScroller.setOffset(config.scrollOffset);
5314 }
5315 return new RouterScroller(router, viewportScroller, config);
5316}
5317function provideLocationStrategy(platformLocationStrategy, baseHref, options = {}) {
5318 return options.useHash ? new HashLocationStrategy(platformLocationStrategy, baseHref) :
5319 new PathLocationStrategy(platformLocationStrategy, baseHref);
5320}
5321function provideForRootGuard(router) {
5322 if (router) {
5323 throw new Error(`RouterModule.forRoot() called twice. Lazy loaded modules should use RouterModule.forChild() instead.`);
5324 }
5325 return 'guarded';
5326}
5327/**
5328 * Registers a [DI provider](guide/glossary#provider) for a set of routes.
5329 * @param routes The route configuration to provide.
5330 *
5331 * @usageNotes
5332 *
5333 * ```
5334 * @NgModule({
5335 * imports: [RouterModule.forChild(ROUTES)],
5336 * providers: [provideRoutes(EXTRA_ROUTES)]
5337 * })
5338 * class MyNgModule {}
5339 * ```
5340 *
5341 * @publicApi
5342 */
5343function provideRoutes(routes) {
5344 return [
5345 { provide: ANALYZE_FOR_ENTRY_COMPONENTS, multi: true, useValue: routes },
5346 { provide: ROUTES, multi: true, useValue: routes },
5347 ];
5348}
5349function setupRouter(urlSerializer, contexts, location, injector, loader, compiler, config, opts = {}, urlHandlingStrategy, routeReuseStrategy) {
5350 const router = new Router(null, urlSerializer, contexts, location, injector, loader, compiler, flatten(config));
5351 if (urlHandlingStrategy) {
5352 router.urlHandlingStrategy = urlHandlingStrategy;
5353 }
5354 if (routeReuseStrategy) {
5355 router.routeReuseStrategy = routeReuseStrategy;
5356 }
5357 if (opts.errorHandler) {
5358 router.errorHandler = opts.errorHandler;
5359 }
5360 if (opts.malformedUriErrorHandler) {
5361 router.malformedUriErrorHandler = opts.malformedUriErrorHandler;
5362 }
5363 if (opts.enableTracing) {
5364 const dom = ɵgetDOM();
5365 router.events.subscribe((e) => {
5366 dom.logGroup(`Router Event: ${e.constructor.name}`);
5367 dom.log(e.toString());
5368 dom.log(e);
5369 dom.logGroupEnd();
5370 });
5371 }
5372 if (opts.onSameUrlNavigation) {
5373 router.onSameUrlNavigation = opts.onSameUrlNavigation;
5374 }
5375 if (opts.paramsInheritanceStrategy) {
5376 router.paramsInheritanceStrategy = opts.paramsInheritanceStrategy;
5377 }
5378 if (opts.urlUpdateStrategy) {
5379 router.urlUpdateStrategy = opts.urlUpdateStrategy;
5380 }
5381 if (opts.relativeLinkResolution) {
5382 router.relativeLinkResolution = opts.relativeLinkResolution;
5383 }
5384 return router;
5385}
5386function rootRoute(router) {
5387 return router.routerState.root;
5388}
5389/**
5390 * Router initialization requires two steps:
5391 *
5392 * First, we start the navigation in a `APP_INITIALIZER` to block the bootstrap if
5393 * a resolver or a guard executes asynchronously.
5394 *
5395 * Next, we actually run activation in a `BOOTSTRAP_LISTENER`, using the
5396 * `afterPreactivation` hook provided by the router.
5397 * The router navigation starts, reaches the point when preactivation is done, and then
5398 * pauses. It waits for the hook to be resolved. We then resolve it only in a bootstrap listener.
5399 */
5400class RouterInitializer {
5401 constructor(injector) {
5402 this.injector = injector;
5403 this.initNavigation = false;
5404 this.resultOfPreactivationDone = new Subject();
5405 }
5406 appInitializer() {
5407 const p = this.injector.get(LOCATION_INITIALIZED, Promise.resolve(null));
5408 return p.then(() => {
5409 let resolve = null;
5410 const res = new Promise(r => resolve = r);
5411 const router = this.injector.get(Router);
5412 const opts = this.injector.get(ROUTER_CONFIGURATION);
5413 if (this.isLegacyDisabled(opts) || this.isLegacyEnabled(opts)) {
5414 resolve(true);
5415 }
5416 else if (opts.initialNavigation === 'disabled') {
5417 router.setUpLocationChangeListener();
5418 resolve(true);
5419 }
5420 else if (opts.initialNavigation === 'enabled') {
5421 router.hooks.afterPreactivation = () => {
5422 // only the initial navigation should be delayed
5423 if (!this.initNavigation) {
5424 this.initNavigation = true;
5425 resolve(true);
5426 return this.resultOfPreactivationDone;
5427 // subsequent navigations should not be delayed
5428 }
5429 else {
5430 return of(null);
5431 }
5432 };
5433 router.initialNavigation();
5434 }
5435 else {
5436 throw new Error(`Invalid initialNavigation options: '${opts.initialNavigation}'`);
5437 }
5438 return res;
5439 });
5440 }
5441 bootstrapListener(bootstrappedComponentRef) {
5442 const opts = this.injector.get(ROUTER_CONFIGURATION);
5443 const preloader = this.injector.get(RouterPreloader);
5444 const routerScroller = this.injector.get(RouterScroller);
5445 const router = this.injector.get(Router);
5446 const ref = this.injector.get(ApplicationRef);
5447 if (bootstrappedComponentRef !== ref.components[0]) {
5448 return;
5449 }
5450 if (this.isLegacyEnabled(opts)) {
5451 router.initialNavigation();
5452 }
5453 else if (this.isLegacyDisabled(opts)) {
5454 router.setUpLocationChangeListener();
5455 }
5456 preloader.setUpPreloading();
5457 routerScroller.init();
5458 router.resetRootComponentType(ref.componentTypes[0]);
5459 this.resultOfPreactivationDone.next(null);
5460 this.resultOfPreactivationDone.complete();
5461 }
5462 isLegacyEnabled(opts) {
5463 return opts.initialNavigation === 'legacy_enabled' || opts.initialNavigation === true ||
5464 opts.initialNavigation === undefined;
5465 }
5466 isLegacyDisabled(opts) {
5467 return opts.initialNavigation === 'legacy_disabled' || opts.initialNavigation === false;
5468 }
5469}
5470RouterInitializer.decorators = [
5471 { type: Injectable }
5472];
5473RouterInitializer.ctorParameters = () => [
5474 { type: Injector }
5475];
5476function getAppInitializer(r) {
5477 return r.appInitializer.bind(r);
5478}
5479function getBootstrapListener(r) {
5480 return r.bootstrapListener.bind(r);
5481}
5482/**
5483 * A [DI token](guide/glossary/#di-token) for the router initializer that
5484 * is called after the app is bootstrapped.
5485 *
5486 * @publicApi
5487 */
5488const ROUTER_INITIALIZER = new InjectionToken('Router Initializer');
5489function provideRouterInitializer() {
5490 return [
5491 RouterInitializer,
5492 {
5493 provide: APP_INITIALIZER,
5494 multi: true,
5495 useFactory: getAppInitializer,
5496 deps: [RouterInitializer]
5497 },
5498 { provide: ROUTER_INITIALIZER, useFactory: getBootstrapListener, deps: [RouterInitializer] },
5499 { provide: APP_BOOTSTRAP_LISTENER, multi: true, useExisting: ROUTER_INITIALIZER },
5500 ];
5501}
5502
5503/**
5504 * @license
5505 * Copyright Google LLC All Rights Reserved.
5506 *
5507 * Use of this source code is governed by an MIT-style license that can be
5508 * found in the LICENSE file at https://angular.io/license
5509 */
5510/**
5511 * @publicApi
5512 */
5513const VERSION = new Version('10.0.5');
5514
5515/**
5516 * @license
5517 * Copyright Google LLC All Rights Reserved.
5518 *
5519 * Use of this source code is governed by an MIT-style license that can be
5520 * found in the LICENSE file at https://angular.io/license
5521 */
5522
5523/**
5524 * @license
5525 * Copyright Google LLC All Rights Reserved.
5526 *
5527 * Use of this source code is governed by an MIT-style license that can be
5528 * found in the LICENSE file at https://angular.io/license
5529 */
5530
5531/**
5532 * @license
5533 * Copyright Google LLC All Rights Reserved.
5534 *
5535 * Use of this source code is governed by an MIT-style license that can be
5536 * found in the LICENSE file at https://angular.io/license
5537 */
5538// This file only reexports content of the `src` folder. Keep it that way.
5539
5540/**
5541 * @license
5542 * Copyright Google LLC All Rights Reserved.
5543 *
5544 * Use of this source code is governed by an MIT-style license that can be
5545 * found in the LICENSE file at https://angular.io/license
5546 */
5547
5548/**
5549 * Generated bundle index. Do not edit.
5550 */
5551
5552export { ActivatedRoute, ActivatedRouteSnapshot, ActivationEnd, ActivationStart, ChildActivationEnd, ChildActivationStart, ChildrenOutletContexts, DefaultUrlSerializer, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, NoPreloading, OutletContext, PRIMARY_OUTLET, PreloadAllModules, PreloadingStrategy, ROUTER_CONFIGURATION, ROUTER_INITIALIZER, ROUTES, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RouteReuseStrategy, Router, RouterEvent, RouterLink, RouterLinkActive, RouterLinkWithHref, RouterModule, RouterOutlet, RouterPreloader, RouterState, RouterStateSnapshot, RoutesRecognized, Scroll, UrlHandlingStrategy, UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree, VERSION, convertToParamMap, provideRoutes, ɵEmptyOutletComponent, ROUTER_PROVIDERS as ɵROUTER_PROVIDERS, ROUTER_FORROOT_GUARD as ɵangular_packages_router_router_a, routerNgProbeToken as ɵangular_packages_router_router_b, createRouterScroller as ɵangular_packages_router_router_c, provideLocationStrategy as ɵangular_packages_router_router_d, provideForRootGuard as ɵangular_packages_router_router_e, setupRouter as ɵangular_packages_router_router_f, rootRoute as ɵangular_packages_router_router_g, RouterInitializer as ɵangular_packages_router_router_h, getAppInitializer as ɵangular_packages_router_router_i, getBootstrapListener as ɵangular_packages_router_router_j, provideRouterInitializer as ɵangular_packages_router_router_k, ɵEmptyOutletComponent as ɵangular_packages_router_router_l, Tree as ɵangular_packages_router_router_m, TreeNode as ɵangular_packages_router_router_n, RouterScroller as ɵangular_packages_router_router_o, flatten as ɵflatten };
5553//# sourceMappingURL=router.js.map