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

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

initial commit

  • Property mode set to 100644
File size: 246.1 KB
Line 
1/**
2 * @license Angular v12.2.9
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 /* When the navigation stream finishes either through error or success, we
4235 * set the `completed` or `errored` flag. However, there are some situations
4236 * where we could get here without either of those being set. For instance, a
4237 * redirect during NavigationStart. Therefore, this is a catch-all to make
4238 * sure the NavigationCancel
4239 * event is fired when a navigation gets cancelled but not caught by other
4240 * means. */
4241 if (!completed && !errored) {
4242 const cancelationReason = `Navigation ID ${t.id} is not equal to the current navigation id ${this.navigationId}`;
4243 if (this.canceledNavigationResolution === 'replace') {
4244 // Must reset to current URL tree here to ensure history.state is set. On
4245 // a fresh page load, if a new navigation comes in before a successful
4246 // navigation completes, there will be nothing in
4247 // history.state.navigationId. This can cause sync problems with
4248 // AngularJS sync code which looks for a value here in order to determine
4249 // whether or not to handle a given popstate event or to leave it to the
4250 // Angular router.
4251 this.restoreHistory(t);
4252 this.cancelNavigationTransition(t, cancelationReason);
4253 }
4254 else {
4255 // We cannot trigger a `location.historyGo` if the
4256 // cancellation was due to a new navigation before the previous could
4257 // complete. This is because `location.historyGo` triggers a `popstate`
4258 // which would also trigger another navigation. Instead, treat this as a
4259 // redirect and do not reset the state.
4260 this.cancelNavigationTransition(t, cancelationReason);
4261 // TODO(atscott): The same problem happens here with a fresh page load
4262 // and a new navigation before that completes where we won't set a page
4263 // id.
4264 }
4265 }
4266 // currentNavigation should always be reset to null here. If navigation was
4267 // successful, lastSuccessfulTransition will have already been set. Therefore
4268 // we can safely set currentNavigation to null here.
4269 this.currentNavigation = null;
4270 }), catchError((e) => {
4271 // TODO(atscott): The NavigationTransition `t` used here does not accurately
4272 // reflect the current state of the whole transition because some operations
4273 // return a new object rather than modifying the one in the outermost
4274 // `switchMap`.
4275 // The fix can likely be to:
4276 // 1. Rename the outer `t` variable so it's not shadowed all the time and
4277 // confusing
4278 // 2. Keep reassigning to the outer variable after each stage to ensure it
4279 // gets updated. Or change the implementations to not return a copy.
4280 // Not changed yet because it affects existing code and would need to be
4281 // tested more thoroughly.
4282 errored = true;
4283 /* This error type is issued during Redirect, and is handled as a
4284 * cancellation rather than an error. */
4285 if (isNavigationCancelingError(e)) {
4286 const redirecting = isUrlTree(e.url);
4287 if (!redirecting) {
4288 // Set property only if we're not redirecting. If we landed on a page and
4289 // redirect to `/` route, the new navigation is going to see the `/`
4290 // isn't a change from the default currentUrlTree and won't navigate.
4291 // This is only applicable with initial navigation, so setting
4292 // `navigated` only when not redirecting resolves this scenario.
4293 this.navigated = true;
4294 this.restoreHistory(t, true);
4295 }
4296 const navCancel = new NavigationCancel(t.id, this.serializeUrl(t.extractedUrl), e.message);
4297 eventsSubject.next(navCancel);
4298 // When redirecting, we need to delay resolving the navigation
4299 // promise and push it to the redirect navigation
4300 if (!redirecting) {
4301 t.resolve(false);
4302 }
4303 else {
4304 // setTimeout is required so this navigation finishes with
4305 // the return EMPTY below. If it isn't allowed to finish
4306 // processing, there can be multiple navigations to the same
4307 // URL.
4308 setTimeout(() => {
4309 const mergedTree = this.urlHandlingStrategy.merge(e.url, this.rawUrlTree);
4310 const extras = {
4311 skipLocationChange: t.extras.skipLocationChange,
4312 // The URL is already updated at this point if we have 'eager' URL
4313 // updates or if the navigation was triggered by the browser (back
4314 // button, URL bar, etc). We want to replace that item in history if
4315 // the navigation is rejected.
4316 replaceUrl: this.urlUpdateStrategy === 'eager' ||
4317 isBrowserTriggeredNavigation(t.source)
4318 };
4319 this.scheduleNavigation(mergedTree, 'imperative', null, extras, { resolve: t.resolve, reject: t.reject, promise: t.promise });
4320 }, 0);
4321 }
4322 /* All other errors should reset to the router's internal URL reference to
4323 * the pre-error state. */
4324 }
4325 else {
4326 this.restoreHistory(t, true);
4327 const navError = new NavigationError(t.id, this.serializeUrl(t.extractedUrl), e);
4328 eventsSubject.next(navError);
4329 try {
4330 t.resolve(this.errorHandler(e));
4331 }
4332 catch (ee) {
4333 t.reject(ee);
4334 }
4335 }
4336 return EMPTY;
4337 }));
4338 // TODO(jasonaden): remove cast once g3 is on updated TypeScript
4339 }));
4340 }
4341 /**
4342 * @internal
4343 * TODO: this should be removed once the constructor of the router made internal
4344 */
4345 resetRootComponentType(rootComponentType) {
4346 this.rootComponentType = rootComponentType;
4347 // TODO: vsavkin router 4.0 should make the root component set to null
4348 // this will simplify the lifecycle of the router.
4349 this.routerState.root.component = this.rootComponentType;
4350 }
4351 getTransition() {
4352 const transition = this.transitions.value;
4353 // TODO(atscott): This comment doesn't make it clear why this value needs to be set. In the case
4354 // described below (where we don't handle previous or current url), the `browserUrlTree` is set
4355 // to the `urlAfterRedirects` value. However, these values *are already the same* because of the
4356 // line below. So it seems that we should be able to remove the line below and the line where
4357 // `browserUrlTree` is updated when we aren't handling any part of the navigation url.
4358 // Run TGP to confirm that this can be done.
4359 // This value needs to be set. Other values such as extractedUrl are set on initial navigation
4360 // but the urlAfterRedirects may not get set if we aren't processing the new URL *and* not
4361 // processing the previous URL.
4362 transition.urlAfterRedirects = this.browserUrlTree;
4363 return transition;
4364 }
4365 setTransition(t) {
4366 this.transitions.next(Object.assign(Object.assign({}, this.getTransition()), t));
4367 }
4368 /**
4369 * Sets up the location change listener and performs the initial navigation.
4370 */
4371 initialNavigation() {
4372 this.setUpLocationChangeListener();
4373 if (this.navigationId === 0) {
4374 this.navigateByUrl(this.location.path(true), { replaceUrl: true });
4375 }
4376 }
4377 /**
4378 * Sets up the location change listener. This listener detects navigations triggered from outside
4379 * the Router (the browser back/forward buttons, for example) and schedules a corresponding Router
4380 * navigation so that the correct events, guards, etc. are triggered.
4381 */
4382 setUpLocationChangeListener() {
4383 // Don't need to use Zone.wrap any more, because zone.js
4384 // already patch onPopState, so location change callback will
4385 // run into ngZone
4386 if (!this.locationSubscription) {
4387 this.locationSubscription = this.location.subscribe(event => {
4388 const currentChange = this.extractLocationChangeInfoFromEvent(event);
4389 // The `setTimeout` was added in #12160 and is likely to support Angular/AngularJS
4390 // hybrid apps.
4391 if (this.shouldScheduleNavigation(this.lastLocationChangeInfo, currentChange)) {
4392 setTimeout(() => {
4393 const { source, state, urlTree } = currentChange;
4394 const extras = { replaceUrl: true };
4395 if (state) {
4396 const stateCopy = Object.assign({}, state);
4397 delete stateCopy.navigationId;
4398 delete stateCopy.ɵrouterPageId;
4399 if (Object.keys(stateCopy).length !== 0) {
4400 extras.state = stateCopy;
4401 }
4402 }
4403 this.scheduleNavigation(urlTree, source, state, extras);
4404 }, 0);
4405 }
4406 this.lastLocationChangeInfo = currentChange;
4407 });
4408 }
4409 }
4410 /** Extracts router-related information from a `PopStateEvent`. */
4411 extractLocationChangeInfoFromEvent(change) {
4412 var _a;
4413 return {
4414 source: change['type'] === 'popstate' ? 'popstate' : 'hashchange',
4415 urlTree: this.parseUrl(change['url']),
4416 // Navigations coming from Angular router have a navigationId state
4417 // property. When this exists, restore the state.
4418 state: ((_a = change.state) === null || _a === void 0 ? void 0 : _a.navigationId) ? change.state : null,
4419 transitionId: this.getTransition().id
4420 };
4421 }
4422 /**
4423 * Determines whether two events triggered by the Location subscription are due to the same
4424 * navigation. The location subscription can fire two events (popstate and hashchange) for a
4425 * single navigation. The second one should be ignored, that is, we should not schedule another
4426 * navigation in the Router.
4427 */
4428 shouldScheduleNavigation(previous, current) {
4429 if (!previous)
4430 return true;
4431 const sameDestination = current.urlTree.toString() === previous.urlTree.toString();
4432 const eventsOccurredAtSameTime = current.transitionId === previous.transitionId;
4433 if (!eventsOccurredAtSameTime || !sameDestination) {
4434 return true;
4435 }
4436 if ((current.source === 'hashchange' && previous.source === 'popstate') ||
4437 (current.source === 'popstate' && previous.source === 'hashchange')) {
4438 return false;
4439 }
4440 return true;
4441 }
4442 /** The current URL. */
4443 get url() {
4444 return this.serializeUrl(this.currentUrlTree);
4445 }
4446 /**
4447 * Returns the current `Navigation` object when the router is navigating,
4448 * and `null` when idle.
4449 */
4450 getCurrentNavigation() {
4451 return this.currentNavigation;
4452 }
4453 /** @internal */
4454 triggerEvent(event) {
4455 this.events.next(event);
4456 }
4457 /**
4458 * Resets the route configuration used for navigation and generating links.
4459 *
4460 * @param config The route array for the new configuration.
4461 *
4462 * @usageNotes
4463 *
4464 * ```
4465 * router.resetConfig([
4466 * { path: 'team/:id', component: TeamCmp, children: [
4467 * { path: 'simple', component: SimpleCmp },
4468 * { path: 'user/:name', component: UserCmp }
4469 * ]}
4470 * ]);
4471 * ```
4472 */
4473 resetConfig(config) {
4474 validateConfig(config);
4475 this.config = config.map(standardizeConfig);
4476 this.navigated = false;
4477 this.lastSuccessfulId = -1;
4478 }
4479 /** @nodoc */
4480 ngOnDestroy() {
4481 this.dispose();
4482 }
4483 /** Disposes of the router. */
4484 dispose() {
4485 this.transitions.complete();
4486 if (this.locationSubscription) {
4487 this.locationSubscription.unsubscribe();
4488 this.locationSubscription = undefined;
4489 }
4490 this.disposed = true;
4491 }
4492 /**
4493 * Appends URL segments to the current URL tree to create a new URL tree.
4494 *
4495 * @param commands An array of URL fragments with which to construct the new URL tree.
4496 * If the path is static, can be the literal URL string. For a dynamic path, pass an array of path
4497 * segments, followed by the parameters for each segment.
4498 * The fragments are applied to the current URL tree or the one provided in the `relativeTo`
4499 * property of the options object, if supplied.
4500 * @param navigationExtras Options that control the navigation strategy.
4501 * @returns The new URL tree.
4502 *
4503 * @usageNotes
4504 *
4505 * ```
4506 * // create /team/33/user/11
4507 * router.createUrlTree(['/team', 33, 'user', 11]);
4508 *
4509 * // create /team/33;expand=true/user/11
4510 * router.createUrlTree(['/team', 33, {expand: true}, 'user', 11]);
4511 *
4512 * // you can collapse static segments like this (this works only with the first passed-in value):
4513 * router.createUrlTree(['/team/33/user', userId]);
4514 *
4515 * // If the first segment can contain slashes, and you do not want the router to split it,
4516 * // you can do the following:
4517 * router.createUrlTree([{segmentPath: '/one/two'}]);
4518 *
4519 * // create /team/33/(user/11//right:chat)
4520 * router.createUrlTree(['/team', 33, {outlets: {primary: 'user/11', right: 'chat'}}]);
4521 *
4522 * // remove the right secondary node
4523 * router.createUrlTree(['/team', 33, {outlets: {primary: 'user/11', right: null}}]);
4524 *
4525 * // assuming the current url is `/team/33/user/11` and the route points to `user/11`
4526 *
4527 * // navigate to /team/33/user/11/details
4528 * router.createUrlTree(['details'], {relativeTo: route});
4529 *
4530 * // navigate to /team/33/user/22
4531 * router.createUrlTree(['../22'], {relativeTo: route});
4532 *
4533 * // navigate to /team/44/user/22
4534 * router.createUrlTree(['../../team/44/user/22'], {relativeTo: route});
4535 *
4536 * Note that a value of `null` or `undefined` for `relativeTo` indicates that the
4537 * tree should be created relative to the root.
4538 * ```
4539 */
4540 createUrlTree(commands, navigationExtras = {}) {
4541 const { relativeTo, queryParams, fragment, queryParamsHandling, preserveFragment } = navigationExtras;
4542 const a = relativeTo || this.routerState.root;
4543 const f = preserveFragment ? this.currentUrlTree.fragment : fragment;
4544 let q = null;
4545 switch (queryParamsHandling) {
4546 case 'merge':
4547 q = Object.assign(Object.assign({}, this.currentUrlTree.queryParams), queryParams);
4548 break;
4549 case 'preserve':
4550 q = this.currentUrlTree.queryParams;
4551 break;
4552 default:
4553 q = queryParams || null;
4554 }
4555 if (q !== null) {
4556 q = this.removeEmptyProps(q);
4557 }
4558 return createUrlTree(a, this.currentUrlTree, commands, q, f !== null && f !== void 0 ? f : null);
4559 }
4560 /**
4561 * Navigates to a view using an absolute route path.
4562 *
4563 * @param url An absolute path for a defined route. The function does not apply any delta to the
4564 * current URL.
4565 * @param extras An object containing properties that modify the navigation strategy.
4566 *
4567 * @returns A Promise that resolves to 'true' when navigation succeeds,
4568 * to 'false' when navigation fails, or is rejected on error.
4569 *
4570 * @usageNotes
4571 *
4572 * The following calls request navigation to an absolute path.
4573 *
4574 * ```
4575 * router.navigateByUrl("/team/33/user/11");
4576 *
4577 * // Navigate without updating the URL
4578 * router.navigateByUrl("/team/33/user/11", { skipLocationChange: true });
4579 * ```
4580 *
4581 * @see [Routing and Navigation guide](guide/router)
4582 *
4583 */
4584 navigateByUrl(url, extras = {
4585 skipLocationChange: false
4586 }) {
4587 if (typeof ngDevMode === 'undefined' ||
4588 ngDevMode && this.isNgZoneEnabled && !NgZone.isInAngularZone()) {
4589 this.console.warn(`Navigation triggered outside Angular zone, did you forget to call 'ngZone.run()'?`);
4590 }
4591 const urlTree = isUrlTree(url) ? url : this.parseUrl(url);
4592 const mergedTree = this.urlHandlingStrategy.merge(urlTree, this.rawUrlTree);
4593 return this.scheduleNavigation(mergedTree, 'imperative', null, extras);
4594 }
4595 /**
4596 * Navigate based on the provided array of commands and a starting point.
4597 * If no starting route is provided, the navigation is absolute.
4598 *
4599 * @param commands An array of URL fragments with which to construct the target URL.
4600 * If the path is static, can be the literal URL string. For a dynamic path, pass an array of path
4601 * segments, followed by the parameters for each segment.
4602 * The fragments are applied to the current URL or the one provided in the `relativeTo` property
4603 * of the options object, if supplied.
4604 * @param extras An options object that determines how the URL should be constructed or
4605 * interpreted.
4606 *
4607 * @returns A Promise that resolves to `true` when navigation succeeds, to `false` when navigation
4608 * fails,
4609 * or is rejected on error.
4610 *
4611 * @usageNotes
4612 *
4613 * The following calls request navigation to a dynamic route path relative to the current URL.
4614 *
4615 * ```
4616 * router.navigate(['team', 33, 'user', 11], {relativeTo: route});
4617 *
4618 * // Navigate without updating the URL, overriding the default behavior
4619 * router.navigate(['team', 33, 'user', 11], {relativeTo: route, skipLocationChange: true});
4620 * ```
4621 *
4622 * @see [Routing and Navigation guide](guide/router)
4623 *
4624 */
4625 navigate(commands, extras = { skipLocationChange: false }) {
4626 validateCommands(commands);
4627 return this.navigateByUrl(this.createUrlTree(commands, extras), extras);
4628 }
4629 /** Serializes a `UrlTree` into a string */
4630 serializeUrl(url) {
4631 return this.urlSerializer.serialize(url);
4632 }
4633 /** Parses a string into a `UrlTree` */
4634 parseUrl(url) {
4635 let urlTree;
4636 try {
4637 urlTree = this.urlSerializer.parse(url);
4638 }
4639 catch (e) {
4640 urlTree = this.malformedUriErrorHandler(e, this.urlSerializer, url);
4641 }
4642 return urlTree;
4643 }
4644 isActive(url, matchOptions) {
4645 let options;
4646 if (matchOptions === true) {
4647 options = Object.assign({}, exactMatchOptions);
4648 }
4649 else if (matchOptions === false) {
4650 options = Object.assign({}, subsetMatchOptions);
4651 }
4652 else {
4653 options = matchOptions;
4654 }
4655 if (isUrlTree(url)) {
4656 return containsTree(this.currentUrlTree, url, options);
4657 }
4658 const urlTree = this.parseUrl(url);
4659 return containsTree(this.currentUrlTree, urlTree, options);
4660 }
4661 removeEmptyProps(params) {
4662 return Object.keys(params).reduce((result, key) => {
4663 const value = params[key];
4664 if (value !== null && value !== undefined) {
4665 result[key] = value;
4666 }
4667 return result;
4668 }, {});
4669 }
4670 processNavigations() {
4671 this.navigations.subscribe(t => {
4672 this.navigated = true;
4673 this.lastSuccessfulId = t.id;
4674 this.currentPageId = t.targetPageId;
4675 this.events
4676 .next(new NavigationEnd(t.id, this.serializeUrl(t.extractedUrl), this.serializeUrl(this.currentUrlTree)));
4677 this.lastSuccessfulNavigation = this.currentNavigation;
4678 t.resolve(true);
4679 }, e => {
4680 this.console.warn(`Unhandled Navigation Error: ${e}`);
4681 });
4682 }
4683 scheduleNavigation(rawUrl, source, restoredState, extras, priorPromise) {
4684 var _a, _b;
4685 if (this.disposed) {
4686 return Promise.resolve(false);
4687 }
4688 // * Imperative navigations (router.navigate) might trigger additional navigations to the same
4689 // URL via a popstate event and the locationChangeListener. We should skip these duplicate
4690 // navs. Duplicates may also be triggered by attempts to sync AngularJS and Angular router
4691 // states.
4692 // * Imperative navigations can be cancelled by router guards, meaning the URL won't change. If
4693 // the user follows that with a navigation using the back/forward button or manual URL change,
4694 // the destination may be the same as the previous imperative attempt. We should not skip
4695 // these navigations because it's a separate case from the one above -- it's not a duplicate
4696 // navigation.
4697 const lastNavigation = this.getTransition();
4698 // We don't want to skip duplicate successful navs if they're imperative because
4699 // onSameUrlNavigation could be 'reload' (so the duplicate is intended).
4700 const browserNavPrecededByRouterNav = isBrowserTriggeredNavigation(source) && lastNavigation &&
4701 !isBrowserTriggeredNavigation(lastNavigation.source);
4702 const lastNavigationSucceeded = this.lastSuccessfulId === lastNavigation.id;
4703 // If the last navigation succeeded or is in flight, we can use the rawUrl as the comparison.
4704 // However, if it failed, we should compare to the final result (urlAfterRedirects).
4705 const lastNavigationUrl = (lastNavigationSucceeded || this.currentNavigation) ?
4706 lastNavigation.rawUrl :
4707 lastNavigation.urlAfterRedirects;
4708 const duplicateNav = lastNavigationUrl.toString() === rawUrl.toString();
4709 if (browserNavPrecededByRouterNav && duplicateNav) {
4710 return Promise.resolve(true); // return value is not used
4711 }
4712 let resolve;
4713 let reject;
4714 let promise;
4715 if (priorPromise) {
4716 resolve = priorPromise.resolve;
4717 reject = priorPromise.reject;
4718 promise = priorPromise.promise;
4719 }
4720 else {
4721 promise = new Promise((res, rej) => {
4722 resolve = res;
4723 reject = rej;
4724 });
4725 }
4726 const id = ++this.navigationId;
4727 let targetPageId;
4728 if (this.canceledNavigationResolution === 'computed') {
4729 const isInitialPage = this.currentPageId === 0;
4730 if (isInitialPage) {
4731 restoredState = this.location.getState();
4732 }
4733 // If the `ɵrouterPageId` exist in the state then `targetpageId` should have the value of
4734 // `ɵrouterPageId`. This is the case for something like a page refresh where we assign the
4735 // target id to the previously set value for that page.
4736 if (restoredState && restoredState.ɵrouterPageId) {
4737 targetPageId = restoredState.ɵrouterPageId;
4738 }
4739 else {
4740 // If we're replacing the URL or doing a silent navigation, we do not want to increment the
4741 // page id because we aren't pushing a new entry to history.
4742 if (extras.replaceUrl || extras.skipLocationChange) {
4743 targetPageId = (_a = this.browserPageId) !== null && _a !== void 0 ? _a : 0;
4744 }
4745 else {
4746 targetPageId = ((_b = this.browserPageId) !== null && _b !== void 0 ? _b : 0) + 1;
4747 }
4748 }
4749 }
4750 else {
4751 // This is unused when `canceledNavigationResolution` is not computed.
4752 targetPageId = 0;
4753 }
4754 this.setTransition({
4755 id,
4756 targetPageId,
4757 source,
4758 restoredState,
4759 currentUrlTree: this.currentUrlTree,
4760 currentRawUrl: this.rawUrlTree,
4761 rawUrl,
4762 extras,
4763 resolve,
4764 reject,
4765 promise,
4766 currentSnapshot: this.routerState.snapshot,
4767 currentRouterState: this.routerState
4768 });
4769 // Make sure that the error is propagated even though `processNavigations` catch
4770 // handler does not rethrow
4771 return promise.catch((e) => {
4772 return Promise.reject(e);
4773 });
4774 }
4775 setBrowserUrl(url, t) {
4776 const path = this.urlSerializer.serialize(url);
4777 const state = Object.assign(Object.assign({}, t.extras.state), this.generateNgRouterState(t.id, t.targetPageId));
4778 if (this.location.isCurrentPathEqualTo(path) || !!t.extras.replaceUrl) {
4779 this.location.replaceState(path, '', state);
4780 }
4781 else {
4782 this.location.go(path, '', state);
4783 }
4784 }
4785 /**
4786 * Performs the necessary rollback action to restore the browser URL to the
4787 * state before the transition.
4788 */
4789 restoreHistory(t, restoringFromCaughtError = false) {
4790 var _a, _b;
4791 if (this.canceledNavigationResolution === 'computed') {
4792 const targetPagePosition = this.currentPageId - t.targetPageId;
4793 // The navigator change the location before triggered the browser event,
4794 // so we need to go back to the current url if the navigation is canceled.
4795 // Also, when navigation gets cancelled while using url update strategy eager, then we need to
4796 // go back. Because, when `urlUpdateSrategy` is `eager`; `setBrowserUrl` method is called
4797 // before any verification.
4798 const browserUrlUpdateOccurred = (t.source === 'popstate' || this.urlUpdateStrategy === 'eager' ||
4799 this.currentUrlTree === ((_a = this.currentNavigation) === null || _a === void 0 ? void 0 : _a.finalUrl));
4800 if (browserUrlUpdateOccurred && targetPagePosition !== 0) {
4801 this.location.historyGo(targetPagePosition);
4802 }
4803 else if (this.currentUrlTree === ((_b = this.currentNavigation) === null || _b === void 0 ? void 0 : _b.finalUrl) && targetPagePosition === 0) {
4804 // We got to the activation stage (where currentUrlTree is set to the navigation's
4805 // finalUrl), but we weren't moving anywhere in history (skipLocationChange or replaceUrl).
4806 // We still need to reset the router state back to what it was when the navigation started.
4807 this.resetState(t);
4808 // TODO(atscott): resetting the `browserUrlTree` should really be done in `resetState`.
4809 // Investigate if this can be done by running TGP.
4810 this.browserUrlTree = t.currentUrlTree;
4811 this.resetUrlToCurrentUrlTree();
4812 }
4813 else {
4814 // The browser URL and router state was not updated before the navigation cancelled so
4815 // there's no restoration needed.
4816 }
4817 }
4818 else if (this.canceledNavigationResolution === 'replace') {
4819 // TODO(atscott): It seems like we should _always_ reset the state here. It would be a no-op
4820 // for `deferred` navigations that haven't change the internal state yet because guards
4821 // reject. For 'eager' navigations, it seems like we also really should reset the state
4822 // because the navigation was cancelled. Investigate if this can be done by running TGP.
4823 if (restoringFromCaughtError) {
4824 this.resetState(t);
4825 }
4826 this.resetUrlToCurrentUrlTree();
4827 }
4828 }
4829 resetState(t) {
4830 this.routerState = t.currentRouterState;
4831 this.currentUrlTree = t.currentUrlTree;
4832 this.rawUrlTree = this.urlHandlingStrategy.merge(this.currentUrlTree, t.rawUrl);
4833 }
4834 resetUrlToCurrentUrlTree() {
4835 this.location.replaceState(this.urlSerializer.serialize(this.rawUrlTree), '', this.generateNgRouterState(this.lastSuccessfulId, this.currentPageId));
4836 }
4837 cancelNavigationTransition(t, reason) {
4838 const navCancel = new NavigationCancel(t.id, this.serializeUrl(t.extractedUrl), reason);
4839 this.triggerEvent(navCancel);
4840 t.resolve(false);
4841 }
4842 generateNgRouterState(navigationId, routerPageId) {
4843 if (this.canceledNavigationResolution === 'computed') {
4844 return { navigationId, ɵrouterPageId: routerPageId };
4845 }
4846 return { navigationId };
4847 }
4848}
4849Router.ɵ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)); };
4850Router.ɵprov = /*@__PURE__*/ ɵngcc0.ɵɵdefineInjectable({ token: Router, factory: Router.ɵfac });
4851Router.ctorParameters = () => [
4852 { type: Type },
4853 { type: UrlSerializer },
4854 { type: ChildrenOutletContexts },
4855 { type: Location },
4856 { type: Injector },
4857 { type: NgModuleFactoryLoader },
4858 { type: Compiler },
4859 { type: undefined }
4860];
4861(function () { (typeof ngDevMode === "undefined" || ngDevMode) && ɵngcc0.ɵsetClassMetadata(Router, [{
4862 type: Injectable
4863 }], function () { return [{ type: ɵngcc0.Type }, { type: UrlSerializer }, { type: ChildrenOutletContexts }, { type: ɵngcc1.Location }, { type: ɵngcc0.Injector }, { type: ɵngcc0.NgModuleFactoryLoader }, { type: ɵngcc0.Compiler }, { type: undefined }]; }, null); })();
4864function validateCommands(commands) {
4865 for (let i = 0; i < commands.length; i++) {
4866 const cmd = commands[i];
4867 if (cmd == null) {
4868 throw new Error(`The requested path contains ${cmd} segment at index ${i}`);
4869 }
4870 }
4871}
4872function isBrowserTriggeredNavigation(source) {
4873 return source !== 'imperative';
4874}
4875
4876/**
4877 * @license
4878 * Copyright Google LLC All Rights Reserved.
4879 *
4880 * Use of this source code is governed by an MIT-style license that can be
4881 * found in the LICENSE file at https://angular.io/license
4882 */
4883/**
4884 * @description
4885 *
4886 * When applied to an element in a template, makes that element a link
4887 * that initiates navigation to a route. Navigation opens one or more routed components
4888 * in one or more `<router-outlet>` locations on the page.
4889 *
4890 * Given a route configuration `[{ path: 'user/:name', component: UserCmp }]`,
4891 * the following creates a static link to the route:
4892 * `<a routerLink="/user/bob">link to user component</a>`
4893 *
4894 * You can use dynamic values to generate the link.
4895 * For a dynamic link, pass an array of path segments,
4896 * followed by the params for each segment.
4897 * For example, `['/team', teamId, 'user', userName, {details: true}]`
4898 * generates a link to `/team/11/user/bob;details=true`.
4899 *
4900 * Multiple static segments can be merged into one term and combined with dynamic segements.
4901 * For example, `['/team/11/user', userName, {details: true}]`
4902 *
4903 * The input that you provide to the link is treated as a delta to the current URL.
4904 * For instance, suppose the current URL is `/user/(box//aux:team)`.
4905 * The link `<a [routerLink]="['/user/jim']">Jim</a>` creates the URL
4906 * `/user/(jim//aux:team)`.
4907 * See {@link Router#createUrlTree createUrlTree} for more information.
4908 *
4909 * @usageNotes
4910 *
4911 * You can use absolute or relative paths in a link, set query parameters,
4912 * control how parameters are handled, and keep a history of navigation states.
4913 *
4914 * ### Relative link paths
4915 *
4916 * The first segment name can be prepended with `/`, `./`, or `../`.
4917 * * If the first segment begins with `/`, the router looks up the route from the root of the
4918 * app.
4919 * * If the first segment begins with `./`, or doesn't begin with a slash, the router
4920 * looks in the children of the current activated route.
4921 * * If the first segment begins with `../`, the router goes up one level in the route tree.
4922 *
4923 * ### Setting and handling query params and fragments
4924 *
4925 * The following link adds a query parameter and a fragment to the generated URL:
4926 *
4927 * ```
4928 * <a [routerLink]="['/user/bob']" [queryParams]="{debug: true}" fragment="education">
4929 * link to user component
4930 * </a>
4931 * ```
4932 * By default, the directive constructs the new URL using the given query parameters.
4933 * The example generates the link: `/user/bob?debug=true#education`.
4934 *
4935 * You can instruct the directive to handle query parameters differently
4936 * by specifying the `queryParamsHandling` option in the link.
4937 * Allowed values are:
4938 *
4939 * - `'merge'`: Merge the given `queryParams` into the current query params.
4940 * - `'preserve'`: Preserve the current query params.
4941 *
4942 * For example:
4943 *
4944 * ```
4945 * <a [routerLink]="['/user/bob']" [queryParams]="{debug: true}" queryParamsHandling="merge">
4946 * link to user component
4947 * </a>
4948 * ```
4949 *
4950 * See {@link UrlCreationOptions.queryParamsHandling UrlCreationOptions#queryParamsHandling}.
4951 *
4952 * ### Preserving navigation history
4953 *
4954 * You can provide a `state` value to be persisted to the browser's
4955 * [`History.state` property](https://developer.mozilla.org/en-US/docs/Web/API/History#Properties).
4956 * For example:
4957 *
4958 * ```
4959 * <a [routerLink]="['/user/bob']" [state]="{tracingId: 123}">
4960 * link to user component
4961 * </a>
4962 * ```
4963 *
4964 * Use {@link Router.getCurrentNavigation() Router#getCurrentNavigation} to retrieve a saved
4965 * navigation-state value. For example, to capture the `tracingId` during the `NavigationStart`
4966 * event:
4967 *
4968 * ```
4969 * // Get NavigationStart events
4970 * router.events.pipe(filter(e => e instanceof NavigationStart)).subscribe(e => {
4971 * const navigation = router.getCurrentNavigation();
4972 * tracingService.trace({id: navigation.extras.state.tracingId});
4973 * });
4974 * ```
4975 *
4976 * @ngModule RouterModule
4977 *
4978 * @publicApi
4979 */
4980class RouterLink {
4981 constructor(router, route, tabIndex, renderer, el) {
4982 this.router = router;
4983 this.route = route;
4984 this.commands = [];
4985 /** @internal */
4986 this.onChanges = new Subject();
4987 if (tabIndex == null) {
4988 renderer.setAttribute(el.nativeElement, 'tabindex', '0');
4989 }
4990 }
4991 /** @nodoc */
4992 ngOnChanges(changes) {
4993 // This is subscribed to by `RouterLinkActive` so that it knows to update when there are changes
4994 // to the RouterLinks it's tracking.
4995 this.onChanges.next(this);
4996 }
4997 /**
4998 * Commands to pass to {@link Router#createUrlTree Router#createUrlTree}.
4999 * - **array**: commands to pass to {@link Router#createUrlTree Router#createUrlTree}.
5000 * - **string**: shorthand for array of commands with just the string, i.e. `['/route']`
5001 * - **null|undefined**: shorthand for an empty array of commands, i.e. `[]`
5002 * @see {@link Router#createUrlTree Router#createUrlTree}
5003 */
5004 set routerLink(commands) {
5005 if (commands != null) {
5006 this.commands = Array.isArray(commands) ? commands : [commands];
5007 }
5008 else {
5009 this.commands = [];
5010 }
5011 }
5012 /** @nodoc */
5013 onClick() {
5014 const extras = {
5015 skipLocationChange: attrBoolValue(this.skipLocationChange),
5016 replaceUrl: attrBoolValue(this.replaceUrl),
5017 state: this.state,
5018 };
5019 this.router.navigateByUrl(this.urlTree, extras);
5020 return true;
5021 }
5022 get urlTree() {
5023 return this.router.createUrlTree(this.commands, {
5024 // If the `relativeTo` input is not defined, we want to use `this.route` by default.
5025 // Otherwise, we should use the value provided by the user in the input.
5026 relativeTo: this.relativeTo !== undefined ? this.relativeTo : this.route,
5027 queryParams: this.queryParams,
5028 fragment: this.fragment,
5029 queryParamsHandling: this.queryParamsHandling,
5030 preserveFragment: attrBoolValue(this.preserveFragment),
5031 });
5032 }
5033}
5034RouterLink.ɵ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)); };
5035RouterLink.ɵdir = /*@__PURE__*/ ɵngcc0.ɵɵdefineDirective({ type: RouterLink, selectors: [["", "routerLink", "", 5, "a", 5, "area"]], hostBindings: function RouterLink_HostBindings(rf, ctx) { if (rf & 1) {
5036 ɵngcc0.ɵɵlistener("click", function RouterLink_click_HostBindingHandler() { return ctx.onClick(); });
5037 } }, inputs: { routerLink: "routerLink", queryParams: "queryParams", fragment: "fragment", queryParamsHandling: "queryParamsHandling", preserveFragment: "preserveFragment", skipLocationChange: "skipLocationChange", replaceUrl: "replaceUrl", state: "state", relativeTo: "relativeTo" }, features: [ɵngcc0.ɵɵNgOnChangesFeature] });
5038RouterLink.ctorParameters = () => [
5039 { type: Router },
5040 { type: ActivatedRoute },
5041 { type: String, decorators: [{ type: Attribute, args: ['tabindex',] }] },
5042 { type: Renderer2 },
5043 { type: ElementRef }
5044];
5045RouterLink.propDecorators = {
5046 queryParams: [{ type: Input }],
5047 fragment: [{ type: Input }],
5048 queryParamsHandling: [{ type: Input }],
5049 preserveFragment: [{ type: Input }],
5050 skipLocationChange: [{ type: Input }],
5051 replaceUrl: [{ type: Input }],
5052 state: [{ type: Input }],
5053 relativeTo: [{ type: Input }],
5054 routerLink: [{ type: Input }],
5055 onClick: [{ type: HostListener, args: ['click',] }]
5056};
5057(function () { (typeof ngDevMode === "undefined" || ngDevMode) && ɵngcc0.ɵsetClassMetadata(RouterLink, [{
5058 type: Directive,
5059 args: [{ selector: ':not(a):not(area)[routerLink]' }]
5060 }], function () { return [{ type: Router }, { type: ActivatedRoute }, { type: String, decorators: [{
5061 type: Attribute,
5062 args: ['tabindex']
5063 }] }, { type: ɵngcc0.Renderer2 }, { type: ɵngcc0.ElementRef }]; }, { routerLink: [{
5064 type: Input
5065 }],
5066 /** @nodoc */
5067 onClick: [{
5068 type: HostListener,
5069 args: ['click']
5070 }], queryParams: [{
5071 type: Input
5072 }], fragment: [{
5073 type: Input
5074 }], queryParamsHandling: [{
5075 type: Input
5076 }], preserveFragment: [{
5077 type: Input
5078 }], skipLocationChange: [{
5079 type: Input
5080 }], replaceUrl: [{
5081 type: Input
5082 }], state: [{
5083 type: Input
5084 }], relativeTo: [{
5085 type: Input
5086 }] }); })();
5087/**
5088 * @description
5089 *
5090 * Lets you link to specific routes in your app.
5091 *
5092 * See `RouterLink` for more information.
5093 *
5094 * @ngModule RouterModule
5095 *
5096 * @publicApi
5097 */
5098class RouterLinkWithHref {
5099 constructor(router, route, locationStrategy) {
5100 this.router = router;
5101 this.route = route;
5102 this.locationStrategy = locationStrategy;
5103 this.commands = [];
5104 /** @internal */
5105 this.onChanges = new Subject();
5106 this.subscription = router.events.subscribe((s) => {
5107 if (s instanceof NavigationEnd) {
5108 this.updateTargetUrlAndHref();
5109 }
5110 });
5111 }
5112 /**
5113 * Commands to pass to {@link Router#createUrlTree Router#createUrlTree}.
5114 * - **array**: commands to pass to {@link Router#createUrlTree Router#createUrlTree}.
5115 * - **string**: shorthand for array of commands with just the string, i.e. `['/route']`
5116 * - **null|undefined**: shorthand for an empty array of commands, i.e. `[]`
5117 * @see {@link Router#createUrlTree Router#createUrlTree}
5118 */
5119 set routerLink(commands) {
5120 if (commands != null) {
5121 this.commands = Array.isArray(commands) ? commands : [commands];
5122 }
5123 else {
5124 this.commands = [];
5125 }
5126 }
5127 /** @nodoc */
5128 ngOnChanges(changes) {
5129 this.updateTargetUrlAndHref();
5130 this.onChanges.next(this);
5131 }
5132 /** @nodoc */
5133 ngOnDestroy() {
5134 this.subscription.unsubscribe();
5135 }
5136 /** @nodoc */
5137 onClick(button, ctrlKey, shiftKey, altKey, metaKey) {
5138 if (button !== 0 || ctrlKey || shiftKey || altKey || metaKey) {
5139 return true;
5140 }
5141 if (typeof this.target === 'string' && this.target != '_self') {
5142 return true;
5143 }
5144 const extras = {
5145 skipLocationChange: attrBoolValue(this.skipLocationChange),
5146 replaceUrl: attrBoolValue(this.replaceUrl),
5147 state: this.state
5148 };
5149 this.router.navigateByUrl(this.urlTree, extras);
5150 return false;
5151 }
5152 updateTargetUrlAndHref() {
5153 this.href = this.locationStrategy.prepareExternalUrl(this.router.serializeUrl(this.urlTree));
5154 }
5155 get urlTree() {
5156 return this.router.createUrlTree(this.commands, {
5157 // If the `relativeTo` input is not defined, we want to use `this.route` by default.
5158 // Otherwise, we should use the value provided by the user in the input.
5159 relativeTo: this.relativeTo !== undefined ? this.relativeTo : this.route,
5160 queryParams: this.queryParams,
5161 fragment: this.fragment,
5162 queryParamsHandling: this.queryParamsHandling,
5163 preserveFragment: attrBoolValue(this.preserveFragment),
5164 });
5165 }
5166}
5167RouterLinkWithHref.ɵfac = function RouterLinkWithHref_Factory(t) { return new (t || RouterLinkWithHref)(ɵngcc0.ɵɵdirectiveInject(Router), ɵngcc0.ɵɵdirectiveInject(ActivatedRoute), ɵngcc0.ɵɵdirectiveInject(ɵngcc1.LocationStrategy)); };
5168RouterLinkWithHref.ɵdir = /*@__PURE__*/ ɵngcc0.ɵɵdefineDirective({ type: RouterLinkWithHref, selectors: [["a", "routerLink", ""], ["area", "routerLink", ""]], hostVars: 2, hostBindings: function RouterLinkWithHref_HostBindings(rf, ctx) { if (rf & 1) {
5169 ɵngcc0.ɵɵlistener("click", function RouterLinkWithHref_click_HostBindingHandler($event) { return ctx.onClick($event.button, $event.ctrlKey, $event.shiftKey, $event.altKey, $event.metaKey); });
5170 } if (rf & 2) {
5171 ɵngcc0.ɵɵhostProperty("href", ctx.href, ɵngcc0.ɵɵsanitizeUrl);
5172 ɵngcc0.ɵɵattribute("target", ctx.target);
5173 } }, inputs: { routerLink: "routerLink", target: "target", queryParams: "queryParams", fragment: "fragment", queryParamsHandling: "queryParamsHandling", preserveFragment: "preserveFragment", skipLocationChange: "skipLocationChange", replaceUrl: "replaceUrl", state: "state", relativeTo: "relativeTo" }, features: [ɵngcc0.ɵɵNgOnChangesFeature] });
5174RouterLinkWithHref.ctorParameters = () => [
5175 { type: Router },
5176 { type: ActivatedRoute },
5177 { type: LocationStrategy }
5178];
5179RouterLinkWithHref.propDecorators = {
5180 target: [{ type: HostBinding, args: ['attr.target',] }, { type: Input }],
5181 queryParams: [{ type: Input }],
5182 fragment: [{ type: Input }],
5183 queryParamsHandling: [{ type: Input }],
5184 preserveFragment: [{ type: Input }],
5185 skipLocationChange: [{ type: Input }],
5186 replaceUrl: [{ type: Input }],
5187 state: [{ type: Input }],
5188 relativeTo: [{ type: Input }],
5189 href: [{ type: HostBinding }],
5190 routerLink: [{ type: Input }],
5191 onClick: [{ type: HostListener, args: ['click',
5192 ['$event.button', '$event.ctrlKey', '$event.shiftKey', '$event.altKey', '$event.metaKey'],] }]
5193};
5194(function () { (typeof ngDevMode === "undefined" || ngDevMode) && ɵngcc0.ɵsetClassMetadata(RouterLinkWithHref, [{
5195 type: Directive,
5196 args: [{ selector: 'a[routerLink],area[routerLink]' }]
5197 }], function () { return [{ type: Router }, { type: ActivatedRoute }, { type: ɵngcc1.LocationStrategy }]; }, { routerLink: [{
5198 type: Input
5199 }],
5200 /** @nodoc */
5201 onClick: [{
5202 type: HostListener,
5203 args: ['click',
5204 ['$event.button', '$event.ctrlKey', '$event.shiftKey', '$event.altKey', '$event.metaKey']]
5205 }], href: [{
5206 type: HostBinding
5207 }], target: [{
5208 type: HostBinding,
5209 args: ['attr.target']
5210 }, {
5211 type: Input
5212 }], queryParams: [{
5213 type: Input
5214 }], fragment: [{
5215 type: Input
5216 }], queryParamsHandling: [{
5217 type: Input
5218 }], preserveFragment: [{
5219 type: Input
5220 }], skipLocationChange: [{
5221 type: Input
5222 }], replaceUrl: [{
5223 type: Input
5224 }], state: [{
5225 type: Input
5226 }], relativeTo: [{
5227 type: Input
5228 }] }); })();
5229function attrBoolValue(s) {
5230 return s === '' || !!s;
5231}
5232
5233/**
5234 * @license
5235 * Copyright Google LLC All Rights Reserved.
5236 *
5237 * Use of this source code is governed by an MIT-style license that can be
5238 * found in the LICENSE file at https://angular.io/license
5239 */
5240/**
5241 *
5242 * @description
5243 *
5244 * Tracks whether the linked route of an element is currently active, and allows you
5245 * to specify one or more CSS classes to add to the element when the linked route
5246 * is active.
5247 *
5248 * Use this directive to create a visual distinction for elements associated with an active route.
5249 * For example, the following code highlights the word "Bob" when the router
5250 * activates the associated route:
5251 *
5252 * ```
5253 * <a routerLink="/user/bob" routerLinkActive="active-link">Bob</a>
5254 * ```
5255 *
5256 * Whenever the URL is either '/user' or '/user/bob', the "active-link" class is
5257 * added to the anchor tag. If the URL changes, the class is removed.
5258 *
5259 * You can set more than one class using a space-separated string or an array.
5260 * For example:
5261 *
5262 * ```
5263 * <a routerLink="/user/bob" routerLinkActive="class1 class2">Bob</a>
5264 * <a routerLink="/user/bob" [routerLinkActive]="['class1', 'class2']">Bob</a>
5265 * ```
5266 *
5267 * To add the classes only when the URL matches the link exactly, add the option `exact: true`:
5268 *
5269 * ```
5270 * <a routerLink="/user/bob" routerLinkActive="active-link" [routerLinkActiveOptions]="{exact:
5271 * true}">Bob</a>
5272 * ```
5273 *
5274 * To directly check the `isActive` status of the link, assign the `RouterLinkActive`
5275 * instance to a template variable.
5276 * For example, the following checks the status without assigning any CSS classes:
5277 *
5278 * ```
5279 * <a routerLink="/user/bob" routerLinkActive #rla="routerLinkActive">
5280 * Bob {{ rla.isActive ? '(already open)' : ''}}
5281 * </a>
5282 * ```
5283 *
5284 * You can apply the `RouterLinkActive` directive to an ancestor of linked elements.
5285 * For example, the following sets the active-link class on the `<div>` parent tag
5286 * when the URL is either '/user/jim' or '/user/bob'.
5287 *
5288 * ```
5289 * <div routerLinkActive="active-link" [routerLinkActiveOptions]="{exact: true}">
5290 * <a routerLink="/user/jim">Jim</a>
5291 * <a routerLink="/user/bob">Bob</a>
5292 * </div>
5293 * ```
5294 *
5295 * @ngModule RouterModule
5296 *
5297 * @publicApi
5298 */
5299class RouterLinkActive {
5300 constructor(router, element, renderer, cdr, link, linkWithHref) {
5301 this.router = router;
5302 this.element = element;
5303 this.renderer = renderer;
5304 this.cdr = cdr;
5305 this.link = link;
5306 this.linkWithHref = linkWithHref;
5307 this.classes = [];
5308 this.isActive = false;
5309 /**
5310 * Options to configure how to determine if the router link is active.
5311 *
5312 * These options are passed to the `Router.isActive()` function.
5313 *
5314 * @see Router.isActive
5315 */
5316 this.routerLinkActiveOptions = { exact: false };
5317 this.routerEventsSubscription = router.events.subscribe((s) => {
5318 if (s instanceof NavigationEnd) {
5319 this.update();
5320 }
5321 });
5322 }
5323 /** @nodoc */
5324 ngAfterContentInit() {
5325 // `of(null)` is used to force subscribe body to execute once immediately (like `startWith`).
5326 of(this.links.changes, this.linksWithHrefs.changes, of(null)).pipe(mergeAll()).subscribe(_ => {
5327 this.update();
5328 this.subscribeToEachLinkOnChanges();
5329 });
5330 }
5331 subscribeToEachLinkOnChanges() {
5332 var _a;
5333 (_a = this.linkInputChangesSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();
5334 const allLinkChanges = [...this.links.toArray(), ...this.linksWithHrefs.toArray(), this.link, this.linkWithHref]
5335 .filter((link) => !!link)
5336 .map(link => link.onChanges);
5337 this.linkInputChangesSubscription = from(allLinkChanges).pipe(mergeAll()).subscribe(link => {
5338 if (this.isActive !== this.isLinkActive(this.router)(link)) {
5339 this.update();
5340 }
5341 });
5342 }
5343 set routerLinkActive(data) {
5344 const classes = Array.isArray(data) ? data : data.split(' ');
5345 this.classes = classes.filter(c => !!c);
5346 }
5347 /** @nodoc */
5348 ngOnChanges(changes) {
5349 this.update();
5350 }
5351 /** @nodoc */
5352 ngOnDestroy() {
5353 var _a;
5354 this.routerEventsSubscription.unsubscribe();
5355 (_a = this.linkInputChangesSubscription) === null || _a === void 0 ? void 0 : _a.unsubscribe();
5356 }
5357 update() {
5358 if (!this.links || !this.linksWithHrefs || !this.router.navigated)
5359 return;
5360 Promise.resolve().then(() => {
5361 const hasActiveLinks = this.hasActiveLinks();
5362 if (this.isActive !== hasActiveLinks) {
5363 this.isActive = hasActiveLinks;
5364 this.cdr.markForCheck();
5365 this.classes.forEach((c) => {
5366 if (hasActiveLinks) {
5367 this.renderer.addClass(this.element.nativeElement, c);
5368 }
5369 else {
5370 this.renderer.removeClass(this.element.nativeElement, c);
5371 }
5372 });
5373 }
5374 });
5375 }
5376 isLinkActive(router) {
5377 const options = isActiveMatchOptions(this.routerLinkActiveOptions) ?
5378 this.routerLinkActiveOptions :
5379 // While the types should disallow `undefined` here, it's possible without strict inputs
5380 (this.routerLinkActiveOptions.exact || false);
5381 return (link) => router.isActive(link.urlTree, options);
5382 }
5383 hasActiveLinks() {
5384 const isActiveCheckFn = this.isLinkActive(this.router);
5385 return this.link && isActiveCheckFn(this.link) ||
5386 this.linkWithHref && isActiveCheckFn(this.linkWithHref) ||
5387 this.links.some(isActiveCheckFn) || this.linksWithHrefs.some(isActiveCheckFn);
5388 }
5389}
5390RouterLinkActive.ɵ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)); };
5391RouterLinkActive.ɵdir = /*@__PURE__*/ ɵngcc0.ɵɵdefineDirective({ type: RouterLinkActive, selectors: [["", "routerLinkActive", ""]], contentQueries: function RouterLinkActive_ContentQueries(rf, ctx, dirIndex) { if (rf & 1) {
5392 ɵngcc0.ɵɵcontentQuery(dirIndex, RouterLink, 5);
5393 ɵngcc0.ɵɵcontentQuery(dirIndex, RouterLinkWithHref, 5);
5394 } if (rf & 2) {
5395 let _t;
5396 ɵngcc0.ɵɵqueryRefresh(_t = ɵngcc0.ɵɵloadQuery()) && (ctx.links = _t);
5397 ɵngcc0.ɵɵqueryRefresh(_t = ɵngcc0.ɵɵloadQuery()) && (ctx.linksWithHrefs = _t);
5398 } }, inputs: { routerLinkActiveOptions: "routerLinkActiveOptions", routerLinkActive: "routerLinkActive" }, exportAs: ["routerLinkActive"], features: [ɵngcc0.ɵɵNgOnChangesFeature] });
5399RouterLinkActive.ctorParameters = () => [
5400 { type: Router },
5401 { type: ElementRef },
5402 { type: Renderer2 },
5403 { type: ChangeDetectorRef },
5404 { type: RouterLink, decorators: [{ type: Optional }] },
5405 { type: RouterLinkWithHref, decorators: [{ type: Optional }] }
5406];
5407RouterLinkActive.propDecorators = {
5408 links: [{ type: ContentChildren, args: [RouterLink, { descendants: true },] }],
5409 linksWithHrefs: [{ type: ContentChildren, args: [RouterLinkWithHref, { descendants: true },] }],
5410 routerLinkActiveOptions: [{ type: Input }],
5411 routerLinkActive: [{ type: Input }]
5412};
5413(function () { (typeof ngDevMode === "undefined" || ngDevMode) && ɵngcc0.ɵsetClassMetadata(RouterLinkActive, [{
5414 type: Directive,
5415 args: [{
5416 selector: '[routerLinkActive]',
5417 exportAs: 'routerLinkActive'
5418 }]
5419 }], function () { return [{ type: Router }, { type: ɵngcc0.ElementRef }, { type: ɵngcc0.Renderer2 }, { type: ɵngcc0.ChangeDetectorRef }, { type: RouterLink, decorators: [{
5420 type: Optional
5421 }] }, { type: RouterLinkWithHref, decorators: [{
5422 type: Optional
5423 }] }]; }, { routerLinkActiveOptions: [{
5424 type: Input
5425 }], routerLinkActive: [{
5426 type: Input
5427 }], links: [{
5428 type: ContentChildren,
5429 args: [RouterLink, { descendants: true }]
5430 }], linksWithHrefs: [{
5431 type: ContentChildren,
5432 args: [RouterLinkWithHref, { descendants: true }]
5433 }] }); })();
5434/**
5435 * Use instead of `'paths' in options` to be compatible with property renaming
5436 */
5437function isActiveMatchOptions(options) {
5438 return !!options.paths;
5439}
5440
5441/**
5442 * @license
5443 * Copyright Google LLC All Rights Reserved.
5444 *
5445 * Use of this source code is governed by an MIT-style license that can be
5446 * found in the LICENSE file at https://angular.io/license
5447 */
5448/**
5449 * @description
5450 *
5451 * Acts as a placeholder that Angular dynamically fills based on the current router state.
5452 *
5453 * Each outlet can have a unique name, determined by the optional `name` attribute.
5454 * The name cannot be set or changed dynamically. If not set, default value is "primary".
5455 *
5456 * ```
5457 * <router-outlet></router-outlet>
5458 * <router-outlet name='left'></router-outlet>
5459 * <router-outlet name='right'></router-outlet>
5460 * ```
5461 *
5462 * Named outlets can be the targets of secondary routes.
5463 * The `Route` object for a secondary route has an `outlet` property to identify the target outlet:
5464 *
5465 * `{path: <base-path>, component: <component>, outlet: <target_outlet_name>}`
5466 *
5467 * Using named outlets and secondary routes, you can target multiple outlets in
5468 * the same `RouterLink` directive.
5469 *
5470 * The router keeps track of separate branches in a navigation tree for each named outlet and
5471 * generates a representation of that tree in the URL.
5472 * The URL for a secondary route uses the following syntax to specify both the primary and secondary
5473 * routes at the same time:
5474 *
5475 * `http://base-path/primary-route-path(outlet-name:route-path)`
5476 *
5477 * A router outlet emits an activate event when a new component is instantiated,
5478 * and a deactivate event when a component is destroyed.
5479 *
5480 * ```
5481 * <router-outlet
5482 * (activate)='onActivate($event)'
5483 * (deactivate)='onDeactivate($event)'></router-outlet>
5484 * ```
5485 *
5486 * @see [Routing tutorial](guide/router-tutorial-toh#named-outlets "Example of a named
5487 * outlet and secondary route configuration").
5488 * @see `RouterLink`
5489 * @see `Route`
5490 * @ngModule RouterModule
5491 *
5492 * @publicApi
5493 */
5494class RouterOutlet {
5495 constructor(parentContexts, location, resolver, name, changeDetector) {
5496 this.parentContexts = parentContexts;
5497 this.location = location;
5498 this.resolver = resolver;
5499 this.changeDetector = changeDetector;
5500 this.activated = null;
5501 this._activatedRoute = null;
5502 this.activateEvents = new EventEmitter();
5503 this.deactivateEvents = new EventEmitter();
5504 this.name = name || PRIMARY_OUTLET;
5505 parentContexts.onChildOutletCreated(this.name, this);
5506 }
5507 /** @nodoc */
5508 ngOnDestroy() {
5509 this.parentContexts.onChildOutletDestroyed(this.name);
5510 }
5511 /** @nodoc */
5512 ngOnInit() {
5513 if (!this.activated) {
5514 // If the outlet was not instantiated at the time the route got activated we need to populate
5515 // the outlet when it is initialized (ie inside a NgIf)
5516 const context = this.parentContexts.getContext(this.name);
5517 if (context && context.route) {
5518 if (context.attachRef) {
5519 // `attachRef` is populated when there is an existing component to mount
5520 this.attach(context.attachRef, context.route);
5521 }
5522 else {
5523 // otherwise the component defined in the configuration is created
5524 this.activateWith(context.route, context.resolver || null);
5525 }
5526 }
5527 }
5528 }
5529 get isActivated() {
5530 return !!this.activated;
5531 }
5532 /**
5533 * @returns The currently activated component instance.
5534 * @throws An error if the outlet is not activated.
5535 */
5536 get component() {
5537 if (!this.activated)
5538 throw new Error('Outlet is not activated');
5539 return this.activated.instance;
5540 }
5541 get activatedRoute() {
5542 if (!this.activated)
5543 throw new Error('Outlet is not activated');
5544 return this._activatedRoute;
5545 }
5546 get activatedRouteData() {
5547 if (this._activatedRoute) {
5548 return this._activatedRoute.snapshot.data;
5549 }
5550 return {};
5551 }
5552 /**
5553 * Called when the `RouteReuseStrategy` instructs to detach the subtree
5554 */
5555 detach() {
5556 if (!this.activated)
5557 throw new Error('Outlet is not activated');
5558 this.location.detach();
5559 const cmp = this.activated;
5560 this.activated = null;
5561 this._activatedRoute = null;
5562 return cmp;
5563 }
5564 /**
5565 * Called when the `RouteReuseStrategy` instructs to re-attach a previously detached subtree
5566 */
5567 attach(ref, activatedRoute) {
5568 this.activated = ref;
5569 this._activatedRoute = activatedRoute;
5570 this.location.insert(ref.hostView);
5571 }
5572 deactivate() {
5573 if (this.activated) {
5574 const c = this.component;
5575 this.activated.destroy();
5576 this.activated = null;
5577 this._activatedRoute = null;
5578 this.deactivateEvents.emit(c);
5579 }
5580 }
5581 activateWith(activatedRoute, resolver) {
5582 if (this.isActivated) {
5583 throw new Error('Cannot activate an already activated outlet');
5584 }
5585 this._activatedRoute = activatedRoute;
5586 const snapshot = activatedRoute._futureSnapshot;
5587 const component = snapshot.routeConfig.component;
5588 resolver = resolver || this.resolver;
5589 const factory = resolver.resolveComponentFactory(component);
5590 const childContexts = this.parentContexts.getOrCreateContext(this.name).children;
5591 const injector = new OutletInjector(activatedRoute, childContexts, this.location.injector);
5592 this.activated = this.location.createComponent(factory, this.location.length, injector);
5593 // Calling `markForCheck` to make sure we will run the change detection when the
5594 // `RouterOutlet` is inside a `ChangeDetectionStrategy.OnPush` component.
5595 this.changeDetector.markForCheck();
5596 this.activateEvents.emit(this.activated.instance);
5597 }
5598}
5599RouterOutlet.ɵ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)); };
5600RouterOutlet.ɵdir = /*@__PURE__*/ ɵngcc0.ɵɵdefineDirective({ type: RouterOutlet, selectors: [["router-outlet"]], outputs: { activateEvents: "activate", deactivateEvents: "deactivate" }, exportAs: ["outlet"] });
5601RouterOutlet.ctorParameters = () => [
5602 { type: ChildrenOutletContexts },
5603 { type: ViewContainerRef },
5604 { type: ComponentFactoryResolver },
5605 { type: String, decorators: [{ type: Attribute, args: ['name',] }] },
5606 { type: ChangeDetectorRef }
5607];
5608RouterOutlet.propDecorators = {
5609 activateEvents: [{ type: Output, args: ['activate',] }],
5610 deactivateEvents: [{ type: Output, args: ['deactivate',] }]
5611};
5612(function () { (typeof ngDevMode === "undefined" || ngDevMode) && ɵngcc0.ɵsetClassMetadata(RouterOutlet, [{
5613 type: Directive,
5614 args: [{ selector: 'router-outlet', exportAs: 'outlet' }]
5615 }], function () { return [{ type: ChildrenOutletContexts }, { type: ɵngcc0.ViewContainerRef }, { type: ɵngcc0.ComponentFactoryResolver }, { type: String, decorators: [{
5616 type: Attribute,
5617 args: ['name']
5618 }] }, { type: ɵngcc0.ChangeDetectorRef }]; }, { activateEvents: [{
5619 type: Output,
5620 args: ['activate']
5621 }], deactivateEvents: [{
5622 type: Output,
5623 args: ['deactivate']
5624 }] }); })();
5625class OutletInjector {
5626 constructor(route, childContexts, parent) {
5627 this.route = route;
5628 this.childContexts = childContexts;
5629 this.parent = parent;
5630 }
5631 get(token, notFoundValue) {
5632 if (token === ActivatedRoute) {
5633 return this.route;
5634 }
5635 if (token === ChildrenOutletContexts) {
5636 return this.childContexts;
5637 }
5638 return this.parent.get(token, notFoundValue);
5639 }
5640}
5641
5642/**
5643 * @license
5644 * Copyright Google LLC All Rights Reserved.
5645 *
5646 * Use of this source code is governed by an MIT-style license that can be
5647 * found in the LICENSE file at https://angular.io/license
5648 */
5649/**
5650 * @description
5651 *
5652 * Provides a preloading strategy.
5653 *
5654 * @publicApi
5655 */
5656class PreloadingStrategy {
5657}
5658/**
5659 * @description
5660 *
5661 * Provides a preloading strategy that preloads all modules as quickly as possible.
5662 *
5663 * ```
5664 * RouterModule.forRoot(ROUTES, {preloadingStrategy: PreloadAllModules})
5665 * ```
5666 *
5667 * @publicApi
5668 */
5669class PreloadAllModules {
5670 preload(route, fn) {
5671 return fn().pipe(catchError(() => of(null)));
5672 }
5673}
5674/**
5675 * @description
5676 *
5677 * Provides a preloading strategy that does not preload any modules.
5678 *
5679 * This strategy is enabled by default.
5680 *
5681 * @publicApi
5682 */
5683class NoPreloading {
5684 preload(route, fn) {
5685 return of(null);
5686 }
5687}
5688/**
5689 * The preloader optimistically loads all router configurations to
5690 * make navigations into lazily-loaded sections of the application faster.
5691 *
5692 * The preloader runs in the background. When the router bootstraps, the preloader
5693 * starts listening to all navigation events. After every such event, the preloader
5694 * will check if any configurations can be loaded lazily.
5695 *
5696 * If a route is protected by `canLoad` guards, the preloaded will not load it.
5697 *
5698 * @publicApi
5699 */
5700class RouterPreloader {
5701 constructor(router, moduleLoader, compiler, injector, preloadingStrategy) {
5702 this.router = router;
5703 this.injector = injector;
5704 this.preloadingStrategy = preloadingStrategy;
5705 const onStartLoad = (r) => router.triggerEvent(new RouteConfigLoadStart(r));
5706 const onEndLoad = (r) => router.triggerEvent(new RouteConfigLoadEnd(r));
5707 this.loader = new RouterConfigLoader(moduleLoader, compiler, onStartLoad, onEndLoad);
5708 }
5709 setUpPreloading() {
5710 this.subscription =
5711 this.router.events
5712 .pipe(filter((e) => e instanceof NavigationEnd), concatMap(() => this.preload()))
5713 .subscribe(() => { });
5714 }
5715 preload() {
5716 const ngModule = this.injector.get(NgModuleRef);
5717 return this.processRoutes(ngModule, this.router.config);
5718 }
5719 /** @nodoc */
5720 ngOnDestroy() {
5721 if (this.subscription) {
5722 this.subscription.unsubscribe();
5723 }
5724 }
5725 processRoutes(ngModule, routes) {
5726 const res = [];
5727 for (const route of routes) {
5728 // we already have the config loaded, just recurse
5729 if (route.loadChildren && !route.canLoad && route._loadedConfig) {
5730 const childConfig = route._loadedConfig;
5731 res.push(this.processRoutes(childConfig.module, childConfig.routes));
5732 // no config loaded, fetch the config
5733 }
5734 else if (route.loadChildren && !route.canLoad) {
5735 res.push(this.preloadConfig(ngModule, route));
5736 // recurse into children
5737 }
5738 else if (route.children) {
5739 res.push(this.processRoutes(ngModule, route.children));
5740 }
5741 }
5742 return from(res).pipe(mergeAll(), map((_) => void 0));
5743 }
5744 preloadConfig(ngModule, route) {
5745 return this.preloadingStrategy.preload(route, () => {
5746 const loaded$ = route._loadedConfig ? of(route._loadedConfig) :
5747 this.loader.load(ngModule.injector, route);
5748 return loaded$.pipe(mergeMap((config) => {
5749 route._loadedConfig = config;
5750 return this.processRoutes(config.module, config.routes);
5751 }));
5752 });
5753 }
5754}
5755RouterPreloader.ɵ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)); };
5756RouterPreloader.ɵprov = /*@__PURE__*/ ɵngcc0.ɵɵdefineInjectable({ token: RouterPreloader, factory: RouterPreloader.ɵfac });
5757RouterPreloader.ctorParameters = () => [
5758 { type: Router },
5759 { type: NgModuleFactoryLoader },
5760 { type: Compiler },
5761 { type: Injector },
5762 { type: PreloadingStrategy }
5763];
5764(function () { (typeof ngDevMode === "undefined" || ngDevMode) && ɵngcc0.ɵsetClassMetadata(RouterPreloader, [{
5765 type: Injectable
5766 }], function () { return [{ type: Router }, { type: ɵngcc0.NgModuleFactoryLoader }, { type: ɵngcc0.Compiler }, { type: ɵngcc0.Injector }, { type: PreloadingStrategy }]; }, null); })();
5767
5768/**
5769 * @license
5770 * Copyright Google LLC All Rights Reserved.
5771 *
5772 * Use of this source code is governed by an MIT-style license that can be
5773 * found in the LICENSE file at https://angular.io/license
5774 */
5775class RouterScroller {
5776 constructor(router,
5777 /** @docsNotRequired */ viewportScroller, options = {}) {
5778 this.router = router;
5779 this.viewportScroller = viewportScroller;
5780 this.options = options;
5781 this.lastId = 0;
5782 this.lastSource = 'imperative';
5783 this.restoredId = 0;
5784 this.store = {};
5785 // Default both options to 'disabled'
5786 options.scrollPositionRestoration = options.scrollPositionRestoration || 'disabled';
5787 options.anchorScrolling = options.anchorScrolling || 'disabled';
5788 }
5789 init() {
5790 // we want to disable the automatic scrolling because having two places
5791 // responsible for scrolling results race conditions, especially given
5792 // that browser don't implement this behavior consistently
5793 if (this.options.scrollPositionRestoration !== 'disabled') {
5794 this.viewportScroller.setHistoryScrollRestoration('manual');
5795 }
5796 this.routerEventsSubscription = this.createScrollEvents();
5797 this.scrollEventsSubscription = this.consumeScrollEvents();
5798 }
5799 createScrollEvents() {
5800 return this.router.events.subscribe(e => {
5801 if (e instanceof NavigationStart) {
5802 // store the scroll position of the current stable navigations.
5803 this.store[this.lastId] = this.viewportScroller.getScrollPosition();
5804 this.lastSource = e.navigationTrigger;
5805 this.restoredId = e.restoredState ? e.restoredState.navigationId : 0;
5806 }
5807 else if (e instanceof NavigationEnd) {
5808 this.lastId = e.id;
5809 this.scheduleScrollEvent(e, this.router.parseUrl(e.urlAfterRedirects).fragment);
5810 }
5811 });
5812 }
5813 consumeScrollEvents() {
5814 return this.router.events.subscribe(e => {
5815 if (!(e instanceof Scroll))
5816 return;
5817 // a popstate event. The pop state event will always ignore anchor scrolling.
5818 if (e.position) {
5819 if (this.options.scrollPositionRestoration === 'top') {
5820 this.viewportScroller.scrollToPosition([0, 0]);
5821 }
5822 else if (this.options.scrollPositionRestoration === 'enabled') {
5823 this.viewportScroller.scrollToPosition(e.position);
5824 }
5825 // imperative navigation "forward"
5826 }
5827 else {
5828 if (e.anchor && this.options.anchorScrolling === 'enabled') {
5829 this.viewportScroller.scrollToAnchor(e.anchor);
5830 }
5831 else if (this.options.scrollPositionRestoration !== 'disabled') {
5832 this.viewportScroller.scrollToPosition([0, 0]);
5833 }
5834 }
5835 });
5836 }
5837 scheduleScrollEvent(routerEvent, anchor) {
5838 this.router.triggerEvent(new Scroll(routerEvent, this.lastSource === 'popstate' ? this.store[this.restoredId] : null, anchor));
5839 }
5840 /** @nodoc */
5841 ngOnDestroy() {
5842 if (this.routerEventsSubscription) {
5843 this.routerEventsSubscription.unsubscribe();
5844 }
5845 if (this.scrollEventsSubscription) {
5846 this.scrollEventsSubscription.unsubscribe();
5847 }
5848 }
5849}
5850RouterScroller.ɵfac = function RouterScroller_Factory(t) { return new (t || RouterScroller)(ɵngcc0.ɵɵinject(Router), ɵngcc0.ɵɵinject(ɵngcc1.ViewportScroller), ɵngcc0.ɵɵinject(undefined)); };
5851RouterScroller.ɵprov = /*@__PURE__*/ ɵngcc0.ɵɵdefineInjectable({ token: RouterScroller, factory: RouterScroller.ɵfac });
5852RouterScroller.ctorParameters = () => [
5853 { type: Router },
5854 { type: ViewportScroller },
5855 { type: undefined }
5856];
5857(function () { (typeof ngDevMode === "undefined" || ngDevMode) && ɵngcc0.ɵsetClassMetadata(RouterScroller, [{
5858 type: Injectable
5859 }], function () { return [{ type: Router }, { type: ɵngcc1.ViewportScroller }, { type: undefined }]; }, null); })();
5860
5861/**
5862 * @license
5863 * Copyright Google LLC All Rights Reserved.
5864 *
5865 * Use of this source code is governed by an MIT-style license that can be
5866 * found in the LICENSE file at https://angular.io/license
5867 */
5868/**
5869 * The directives defined in the `RouterModule`.
5870 */
5871const ROUTER_DIRECTIVES = [RouterOutlet, RouterLink, RouterLinkWithHref, RouterLinkActive, ɵEmptyOutletComponent];
5872/**
5873 * A [DI token](guide/glossary/#di-token) for the router service.
5874 *
5875 * @publicApi
5876 */
5877const ROUTER_CONFIGURATION = new InjectionToken('ROUTER_CONFIGURATION');
5878/**
5879 * @docsNotRequired
5880 */
5881const ROUTER_FORROOT_GUARD = new InjectionToken('ROUTER_FORROOT_GUARD');
5882const ɵ0 = { enableTracing: false };
5883const ROUTER_PROVIDERS = [
5884 Location,
5885 { provide: UrlSerializer, useClass: DefaultUrlSerializer },
5886 {
5887 provide: Router,
5888 useFactory: setupRouter,
5889 deps: [
5890 UrlSerializer, ChildrenOutletContexts, Location, Injector, NgModuleFactoryLoader, Compiler,
5891 ROUTES, ROUTER_CONFIGURATION, [UrlHandlingStrategy, new Optional()],
5892 [RouteReuseStrategy, new Optional()]
5893 ]
5894 },
5895 ChildrenOutletContexts,
5896 { provide: ActivatedRoute, useFactory: rootRoute, deps: [Router] },
5897 { provide: NgModuleFactoryLoader, useClass: SystemJsNgModuleLoader },
5898 RouterPreloader,
5899 NoPreloading,
5900 PreloadAllModules,
5901 { provide: ROUTER_CONFIGURATION, useValue: ɵ0 },
5902];
5903function routerNgProbeToken() {
5904 return new NgProbeToken('Router', Router);
5905}
5906/**
5907 * @description
5908 *
5909 * Adds directives and providers for in-app navigation among views defined in an application.
5910 * Use the Angular `Router` service to declaratively specify application states and manage state
5911 * transitions.
5912 *
5913 * You can import this NgModule multiple times, once for each lazy-loaded bundle.
5914 * However, only one `Router` service can be active.
5915 * To ensure this, there are two ways to register routes when importing this module:
5916 *
5917 * * The `forRoot()` method creates an `NgModule` that contains all the directives, the given
5918 * routes, and the `Router` service itself.
5919 * * The `forChild()` method creates an `NgModule` that contains all the directives and the given
5920 * routes, but does not include the `Router` service.
5921 *
5922 * @see [Routing and Navigation guide](guide/router) for an
5923 * overview of how the `Router` service should be used.
5924 *
5925 * @publicApi
5926 */
5927class RouterModule {
5928 // Note: We are injecting the Router so it gets created eagerly...
5929 constructor(guard, router) { }
5930 /**
5931 * Creates and configures a module with all the router providers and directives.
5932 * Optionally sets up an application listener to perform an initial navigation.
5933 *
5934 * When registering the NgModule at the root, import as follows:
5935 *
5936 * ```
5937 * @NgModule({
5938 * imports: [RouterModule.forRoot(ROUTES)]
5939 * })
5940 * class MyNgModule {}
5941 * ```
5942 *
5943 * @param routes An array of `Route` objects that define the navigation paths for the application.
5944 * @param config An `ExtraOptions` configuration object that controls how navigation is performed.
5945 * @return The new `NgModule`.
5946 *
5947 */
5948 static forRoot(routes, config) {
5949 return {
5950 ngModule: RouterModule,
5951 providers: [
5952 ROUTER_PROVIDERS,
5953 provideRoutes(routes),
5954 {
5955 provide: ROUTER_FORROOT_GUARD,
5956 useFactory: provideForRootGuard,
5957 deps: [[Router, new Optional(), new SkipSelf()]]
5958 },
5959 { provide: ROUTER_CONFIGURATION, useValue: config ? config : {} },
5960 {
5961 provide: LocationStrategy,
5962 useFactory: provideLocationStrategy,
5963 deps: [PlatformLocation, [new Inject(APP_BASE_HREF), new Optional()], ROUTER_CONFIGURATION]
5964 },
5965 {
5966 provide: RouterScroller,
5967 useFactory: createRouterScroller,
5968 deps: [Router, ViewportScroller, ROUTER_CONFIGURATION]
5969 },
5970 {
5971 provide: PreloadingStrategy,
5972 useExisting: config && config.preloadingStrategy ? config.preloadingStrategy :
5973 NoPreloading
5974 },
5975 { provide: NgProbeToken, multi: true, useFactory: routerNgProbeToken },
5976 provideRouterInitializer(),
5977 ],
5978 };
5979 }
5980 /**
5981 * Creates a module with all the router directives and a provider registering routes,
5982 * without creating a new Router service.
5983 * When registering for submodules and lazy-loaded submodules, create the NgModule as follows:
5984 *
5985 * ```
5986 * @NgModule({
5987 * imports: [RouterModule.forChild(ROUTES)]
5988 * })
5989 * class MyNgModule {}
5990 * ```
5991 *
5992 * @param routes An array of `Route` objects that define the navigation paths for the submodule.
5993 * @return The new NgModule.
5994 *
5995 */
5996 static forChild(routes) {
5997 return { ngModule: RouterModule, providers: [provideRoutes(routes)] };
5998 }
5999}
6000RouterModule.ɵfac = function RouterModule_Factory(t) { return new (t || RouterModule)(ɵngcc0.ɵɵinject(ROUTER_FORROOT_GUARD, 8), ɵngcc0.ɵɵinject(Router, 8)); };
6001RouterModule.ɵmod = /*@__PURE__*/ ɵngcc0.ɵɵdefineNgModule({ type: RouterModule });
6002RouterModule.ɵinj = /*@__PURE__*/ ɵngcc0.ɵɵdefineInjector({});
6003RouterModule.ctorParameters = () => [
6004 { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [ROUTER_FORROOT_GUARD,] }] },
6005 { type: Router, decorators: [{ type: Optional }] }
6006];
6007(function () { (typeof ngDevMode === "undefined" || ngDevMode) && ɵngcc0.ɵsetClassMetadata(RouterModule, [{
6008 type: NgModule,
6009 args: [{
6010 declarations: ROUTER_DIRECTIVES,
6011 exports: ROUTER_DIRECTIVES,
6012 entryComponents: [ɵEmptyOutletComponent]
6013 }]
6014 }], function () { return [{ type: undefined, decorators: [{
6015 type: Optional
6016 }, {
6017 type: Inject,
6018 args: [ROUTER_FORROOT_GUARD]
6019 }] }, { type: Router, decorators: [{
6020 type: Optional
6021 }] }]; }, null); })();
6022(function () { (typeof ngJitMode === "undefined" || ngJitMode) && ɵngcc0.ɵɵsetNgModuleScope(RouterModule, { declarations: [RouterOutlet, RouterLink, RouterLinkWithHref, RouterLinkActive, ɵEmptyOutletComponent], exports: [RouterOutlet, RouterLink, RouterLinkWithHref, RouterLinkActive, ɵEmptyOutletComponent] }); })();
6023function createRouterScroller(router, viewportScroller, config) {
6024 if (config.scrollOffset) {
6025 viewportScroller.setOffset(config.scrollOffset);
6026 }
6027 return new RouterScroller(router, viewportScroller, config);
6028}
6029function provideLocationStrategy(platformLocationStrategy, baseHref, options = {}) {
6030 return options.useHash ? new HashLocationStrategy(platformLocationStrategy, baseHref) :
6031 new PathLocationStrategy(platformLocationStrategy, baseHref);
6032}
6033function provideForRootGuard(router) {
6034 if ((typeof ngDevMode === 'undefined' || ngDevMode) && router) {
6035 throw new Error(`RouterModule.forRoot() called twice. Lazy loaded modules should use RouterModule.forChild() instead.`);
6036 }
6037 return 'guarded';
6038}
6039/**
6040 * Registers a [DI provider](guide/glossary#provider) for a set of routes.
6041 * @param routes The route configuration to provide.
6042 *
6043 * @usageNotes
6044 *
6045 * ```
6046 * @NgModule({
6047 * imports: [RouterModule.forChild(ROUTES)],
6048 * providers: [provideRoutes(EXTRA_ROUTES)]
6049 * })
6050 * class MyNgModule {}
6051 * ```
6052 *
6053 * @publicApi
6054 */
6055function provideRoutes(routes) {
6056 return [
6057 { provide: ANALYZE_FOR_ENTRY_COMPONENTS, multi: true, useValue: routes },
6058 { provide: ROUTES, multi: true, useValue: routes },
6059 ];
6060}
6061function setupRouter(urlSerializer, contexts, location, injector, loader, compiler, config, opts = {}, urlHandlingStrategy, routeReuseStrategy) {
6062 const router = new Router(null, urlSerializer, contexts, location, injector, loader, compiler, flatten(config));
6063 if (urlHandlingStrategy) {
6064 router.urlHandlingStrategy = urlHandlingStrategy;
6065 }
6066 if (routeReuseStrategy) {
6067 router.routeReuseStrategy = routeReuseStrategy;
6068 }
6069 assignExtraOptionsToRouter(opts, router);
6070 if (opts.enableTracing) {
6071 router.events.subscribe((e) => {
6072 var _a, _b;
6073 // tslint:disable:no-console
6074 (_a = console.group) === null || _a === void 0 ? void 0 : _a.call(console, `Router Event: ${e.constructor.name}`);
6075 console.log(e.toString());
6076 console.log(e);
6077 (_b = console.groupEnd) === null || _b === void 0 ? void 0 : _b.call(console);
6078 // tslint:enable:no-console
6079 });
6080 }
6081 return router;
6082}
6083function assignExtraOptionsToRouter(opts, router) {
6084 if (opts.errorHandler) {
6085 router.errorHandler = opts.errorHandler;
6086 }
6087 if (opts.malformedUriErrorHandler) {
6088 router.malformedUriErrorHandler = opts.malformedUriErrorHandler;
6089 }
6090 if (opts.onSameUrlNavigation) {
6091 router.onSameUrlNavigation = opts.onSameUrlNavigation;
6092 }
6093 if (opts.paramsInheritanceStrategy) {
6094 router.paramsInheritanceStrategy = opts.paramsInheritanceStrategy;
6095 }
6096 if (opts.relativeLinkResolution) {
6097 router.relativeLinkResolution = opts.relativeLinkResolution;
6098 }
6099 if (opts.urlUpdateStrategy) {
6100 router.urlUpdateStrategy = opts.urlUpdateStrategy;
6101 }
6102}
6103function rootRoute(router) {
6104 return router.routerState.root;
6105}
6106/**
6107 * Router initialization requires two steps:
6108 *
6109 * First, we start the navigation in a `APP_INITIALIZER` to block the bootstrap if
6110 * a resolver or a guard executes asynchronously.
6111 *
6112 * Next, we actually run activation in a `BOOTSTRAP_LISTENER`, using the
6113 * `afterPreactivation` hook provided by the router.
6114 * The router navigation starts, reaches the point when preactivation is done, and then
6115 * pauses. It waits for the hook to be resolved. We then resolve it only in a bootstrap listener.
6116 */
6117class RouterInitializer {
6118 constructor(injector) {
6119 this.injector = injector;
6120 this.initNavigation = false;
6121 this.destroyed = false;
6122 this.resultOfPreactivationDone = new Subject();
6123 }
6124 appInitializer() {
6125 const p = this.injector.get(LOCATION_INITIALIZED, Promise.resolve(null));
6126 return p.then(() => {
6127 // If the injector was destroyed, the DI lookups below will fail.
6128 if (this.destroyed) {
6129 return Promise.resolve(true);
6130 }
6131 let resolve = null;
6132 const res = new Promise(r => resolve = r);
6133 const router = this.injector.get(Router);
6134 const opts = this.injector.get(ROUTER_CONFIGURATION);
6135 if (opts.initialNavigation === 'disabled') {
6136 router.setUpLocationChangeListener();
6137 resolve(true);
6138 }
6139 else if (
6140 // TODO: enabled is deprecated as of v11, can be removed in v13
6141 opts.initialNavigation === 'enabled' || opts.initialNavigation === 'enabledBlocking') {
6142 router.hooks.afterPreactivation = () => {
6143 // only the initial navigation should be delayed
6144 if (!this.initNavigation) {
6145 this.initNavigation = true;
6146 resolve(true);
6147 return this.resultOfPreactivationDone;
6148 // subsequent navigations should not be delayed
6149 }
6150 else {
6151 return of(null);
6152 }
6153 };
6154 router.initialNavigation();
6155 }
6156 else {
6157 resolve(true);
6158 }
6159 return res;
6160 });
6161 }
6162 bootstrapListener(bootstrappedComponentRef) {
6163 const opts = this.injector.get(ROUTER_CONFIGURATION);
6164 const preloader = this.injector.get(RouterPreloader);
6165 const routerScroller = this.injector.get(RouterScroller);
6166 const router = this.injector.get(Router);
6167 const ref = this.injector.get(ApplicationRef);
6168 if (bootstrappedComponentRef !== ref.components[0]) {
6169 return;
6170 }
6171 // Default case
6172 if (opts.initialNavigation === 'enabledNonBlocking' || opts.initialNavigation === undefined) {
6173 router.initialNavigation();
6174 }
6175 preloader.setUpPreloading();
6176 routerScroller.init();
6177 router.resetRootComponentType(ref.componentTypes[0]);
6178 this.resultOfPreactivationDone.next(null);
6179 this.resultOfPreactivationDone.complete();
6180 }
6181 ngOnDestroy() {
6182 this.destroyed = true;
6183 }
6184}
6185RouterInitializer.ɵfac = function RouterInitializer_Factory(t) { return new (t || RouterInitializer)(ɵngcc0.ɵɵinject(ɵngcc0.Injector)); };
6186RouterInitializer.ɵprov = /*@__PURE__*/ ɵngcc0.ɵɵdefineInjectable({ token: RouterInitializer, factory: RouterInitializer.ɵfac });
6187RouterInitializer.ctorParameters = () => [
6188 { type: Injector }
6189];
6190(function () { (typeof ngDevMode === "undefined" || ngDevMode) && ɵngcc0.ɵsetClassMetadata(RouterInitializer, [{
6191 type: Injectable
6192 }], function () { return [{ type: ɵngcc0.Injector }]; }, null); })();
6193function getAppInitializer(r) {
6194 return r.appInitializer.bind(r);
6195}
6196function getBootstrapListener(r) {
6197 return r.bootstrapListener.bind(r);
6198}
6199/**
6200 * A [DI token](guide/glossary/#di-token) for the router initializer that
6201 * is called after the app is bootstrapped.
6202 *
6203 * @publicApi
6204 */
6205const ROUTER_INITIALIZER = new InjectionToken('Router Initializer');
6206function provideRouterInitializer() {
6207 return [
6208 RouterInitializer,
6209 {
6210 provide: APP_INITIALIZER,
6211 multi: true,
6212 useFactory: getAppInitializer,
6213 deps: [RouterInitializer]
6214 },
6215 { provide: ROUTER_INITIALIZER, useFactory: getBootstrapListener, deps: [RouterInitializer] },
6216 { provide: APP_BOOTSTRAP_LISTENER, multi: true, useExisting: ROUTER_INITIALIZER },
6217 ];
6218}
6219
6220/**
6221 * @license
6222 * Copyright Google LLC All Rights Reserved.
6223 *
6224 * Use of this source code is governed by an MIT-style license that can be
6225 * found in the LICENSE file at https://angular.io/license
6226 */
6227/**
6228 * @publicApi
6229 */
6230const VERSION = new Version('12.2.9');
6231
6232/**
6233 * @license
6234 * Copyright Google LLC All Rights Reserved.
6235 *
6236 * Use of this source code is governed by an MIT-style license that can be
6237 * found in the LICENSE file at https://angular.io/license
6238 */
6239
6240/**
6241 * @license
6242 * Copyright Google LLC All Rights Reserved.
6243 *
6244 * Use of this source code is governed by an MIT-style license that can be
6245 * found in the LICENSE file at https://angular.io/license
6246 */
6247
6248/**
6249 * @license
6250 * Copyright Google LLC All Rights Reserved.
6251 *
6252 * Use of this source code is governed by an MIT-style license that can be
6253 * found in the LICENSE file at https://angular.io/license
6254 */
6255// This file only reexports content of the `src` folder. Keep it that way.
6256
6257/**
6258 * @license
6259 * Copyright Google LLC All Rights Reserved.
6260 *
6261 * Use of this source code is governed by an MIT-style license that can be
6262 * found in the LICENSE file at https://angular.io/license
6263 */
6264
6265/**
6266 * Generated bundle index. Do not edit.
6267 */
6268
6269export { 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 };
6270
6271//# sourceMappingURL=router.js.map
Note: See TracBrowser for help on using the repository browser.