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

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

primeNG components

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