source: trip-planner-front/node_modules/@angular/router/fesm2015/router.js@ e29cc2e

Last change on this file since e29cc2e was e29cc2e, checked in by Ema <ema_spirova@…>, 3 years ago

primeNG components

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