UNPKG

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