@angular/router 14.0.1 → 14.1.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Angular v14.0.1
2
+ * @license Angular v14.1.0-next.0
3
3
  * (c) 2010-2022 Google LLC. https://angular.io/
4
4
  * License: MIT
5
5
  */
@@ -20,1630 +20,1797 @@ import * as i1 from '@angular/platform-browser';
20
20
  * found in the LICENSE file at https://angular.io/license
21
21
  */
22
22
  /**
23
- * Base for events the router goes through, as opposed to events tied to a specific
24
- * route. Fired one time for any given navigation.
25
- *
26
- * The following code shows how a class subscribes to router events.
27
- *
28
- * ```ts
29
- * import {Event, RouterEvent, Router} from '@angular/router';
30
- *
31
- * class MyService {
32
- * constructor(public router: Router) {
33
- * router.events.pipe(
34
- * filter((e: Event): e is RouterEvent => e instanceof RouterEvent)
35
- * ).subscribe((e: RouterEvent) => {
36
- * // Do something
37
- * });
38
- * }
39
- * }
40
- * ```
23
+ * The primary routing outlet.
41
24
  *
42
- * @see `Event`
43
- * @see [Router events summary](guide/router-reference#router-events)
44
25
  * @publicApi
45
26
  */
46
- class RouterEvent {
47
- constructor(
48
- /** A unique ID that the router assigns to every router navigation. */
49
- id,
50
- /** The URL that is the destination for this navigation. */
51
- url) {
52
- this.id = id;
53
- this.url = url;
27
+ const PRIMARY_OUTLET = 'primary';
28
+ class ParamsAsMap {
29
+ constructor(params) {
30
+ this.params = params || {};
31
+ }
32
+ has(name) {
33
+ return Object.prototype.hasOwnProperty.call(this.params, name);
34
+ }
35
+ get(name) {
36
+ if (this.has(name)) {
37
+ const v = this.params[name];
38
+ return Array.isArray(v) ? v[0] : v;
39
+ }
40
+ return null;
41
+ }
42
+ getAll(name) {
43
+ if (this.has(name)) {
44
+ const v = this.params[name];
45
+ return Array.isArray(v) ? v : [v];
46
+ }
47
+ return [];
48
+ }
49
+ get keys() {
50
+ return Object.keys(this.params);
54
51
  }
55
52
  }
56
53
  /**
57
- * An event triggered when a navigation starts.
54
+ * Converts a `Params` instance to a `ParamMap`.
55
+ * @param params The instance to convert.
56
+ * @returns The new map instance.
58
57
  *
59
58
  * @publicApi
60
59
  */
61
- class NavigationStart extends RouterEvent {
62
- constructor(
63
- /** @docsNotRequired */
64
- id,
65
- /** @docsNotRequired */
66
- url,
67
- /** @docsNotRequired */
68
- navigationTrigger = 'imperative',
69
- /** @docsNotRequired */
70
- restoredState = null) {
71
- super(id, url);
72
- this.type = 0 /* EventType.NavigationStart */;
73
- this.navigationTrigger = navigationTrigger;
74
- this.restoredState = restoredState;
60
+ function convertToParamMap(params) {
61
+ return new ParamsAsMap(params);
62
+ }
63
+ const NAVIGATION_CANCELING_ERROR = 'ngNavigationCancelingError';
64
+ function navigationCancelingError(message) {
65
+ const error = Error('NavigationCancelingError: ' + message);
66
+ error[NAVIGATION_CANCELING_ERROR] = true;
67
+ return error;
68
+ }
69
+ function isNavigationCancelingError(error) {
70
+ return error && error[NAVIGATION_CANCELING_ERROR];
71
+ }
72
+ // Matches the route configuration (`route`) against the actual URL (`segments`).
73
+ function defaultUrlMatcher(segments, segmentGroup, route) {
74
+ const parts = route.path.split('/');
75
+ if (parts.length > segments.length) {
76
+ // The actual URL is shorter than the config, no match
77
+ return null;
75
78
  }
76
- /** @docsNotRequired */
77
- toString() {
78
- return `NavigationStart(id: ${this.id}, url: '${this.url}')`;
79
+ if (route.pathMatch === 'full' &&
80
+ (segmentGroup.hasChildren() || parts.length < segments.length)) {
81
+ // The config is longer than the actual URL but we are looking for a full match, return null
82
+ return null;
79
83
  }
84
+ const posParams = {};
85
+ // Check each config part against the actual URL
86
+ for (let index = 0; index < parts.length; index++) {
87
+ const part = parts[index];
88
+ const segment = segments[index];
89
+ const isParameter = part.startsWith(':');
90
+ if (isParameter) {
91
+ posParams[part.substring(1)] = segment;
92
+ }
93
+ else if (part !== segment.path) {
94
+ // The actual URL part does not match the config, no match
95
+ return null;
96
+ }
97
+ }
98
+ return { consumed: segments.slice(0, parts.length), posParams };
80
99
  }
100
+
81
101
  /**
82
- * An event triggered when a navigation ends successfully.
83
- *
84
- * @see `NavigationStart`
85
- * @see `NavigationCancel`
86
- * @see `NavigationError`
102
+ * @license
103
+ * Copyright Google LLC All Rights Reserved.
87
104
  *
88
- * @publicApi
105
+ * Use of this source code is governed by an MIT-style license that can be
106
+ * found in the LICENSE file at https://angular.io/license
89
107
  */
90
- class NavigationEnd extends RouterEvent {
91
- constructor(
92
- /** @docsNotRequired */
93
- id,
94
- /** @docsNotRequired */
95
- url,
96
- /** @docsNotRequired */
97
- urlAfterRedirects) {
98
- super(id, url);
99
- this.urlAfterRedirects = urlAfterRedirects;
100
- this.type = 1 /* EventType.NavigationEnd */;
108
+ function shallowEqualArrays(a, b) {
109
+ if (a.length !== b.length)
110
+ return false;
111
+ for (let i = 0; i < a.length; ++i) {
112
+ if (!shallowEqual(a[i], b[i]))
113
+ return false;
101
114
  }
102
- /** @docsNotRequired */
103
- toString() {
104
- return `NavigationEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}')`;
115
+ return true;
116
+ }
117
+ function shallowEqual(a, b) {
118
+ // While `undefined` should never be possible, it would sometimes be the case in IE 11
119
+ // and pre-chromium Edge. The check below accounts for this edge case.
120
+ const k1 = a ? Object.keys(a) : undefined;
121
+ const k2 = b ? Object.keys(b) : undefined;
122
+ if (!k1 || !k2 || k1.length != k2.length) {
123
+ return false;
124
+ }
125
+ let key;
126
+ for (let i = 0; i < k1.length; i++) {
127
+ key = k1[i];
128
+ if (!equalArraysOrString(a[key], b[key])) {
129
+ return false;
130
+ }
105
131
  }
132
+ return true;
106
133
  }
107
134
  /**
108
- * An event triggered when a navigation is canceled, directly or indirectly.
109
- * This can happen for several reasons including when a route guard
110
- * returns `false` or initiates a redirect by returning a `UrlTree`.
111
- *
112
- * @see `NavigationStart`
113
- * @see `NavigationEnd`
114
- * @see `NavigationError`
115
- *
116
- * @publicApi
135
+ * Test equality for arrays of strings or a string.
117
136
  */
118
- class NavigationCancel extends RouterEvent {
119
- constructor(
120
- /** @docsNotRequired */
121
- id,
122
- /** @docsNotRequired */
123
- url,
124
- /** @docsNotRequired */
125
- reason) {
126
- super(id, url);
127
- this.reason = reason;
128
- this.type = 2 /* EventType.NavigationCancel */;
137
+ function equalArraysOrString(a, b) {
138
+ if (Array.isArray(a) && Array.isArray(b)) {
139
+ if (a.length !== b.length)
140
+ return false;
141
+ const aSorted = [...a].sort();
142
+ const bSorted = [...b].sort();
143
+ return aSorted.every((val, index) => bSorted[index] === val);
129
144
  }
130
- /** @docsNotRequired */
131
- toString() {
132
- return `NavigationCancel(id: ${this.id}, url: '${this.url}')`;
145
+ else {
146
+ return a === b;
133
147
  }
134
148
  }
135
149
  /**
136
- * An event triggered when a navigation fails due to an unexpected error.
137
- *
138
- * @see `NavigationStart`
139
- * @see `NavigationEnd`
140
- * @see `NavigationCancel`
141
- *
142
- * @publicApi
150
+ * Flattens single-level nested arrays.
143
151
  */
144
- class NavigationError extends RouterEvent {
145
- constructor(
146
- /** @docsNotRequired */
147
- id,
148
- /** @docsNotRequired */
149
- url,
150
- /** @docsNotRequired */
151
- error) {
152
- super(id, url);
153
- this.error = error;
154
- this.type = 3 /* EventType.NavigationError */;
155
- }
156
- /** @docsNotRequired */
157
- toString() {
158
- return `NavigationError(id: ${this.id}, url: '${this.url}', error: ${this.error})`;
159
- }
152
+ function flatten(arr) {
153
+ return Array.prototype.concat.apply([], arr);
160
154
  }
161
155
  /**
162
- * An event triggered when routes are recognized.
163
- *
164
- * @publicApi
156
+ * Return the last element of an array.
165
157
  */
166
- class RoutesRecognized extends RouterEvent {
167
- constructor(
168
- /** @docsNotRequired */
169
- id,
170
- /** @docsNotRequired */
171
- url,
172
- /** @docsNotRequired */
173
- urlAfterRedirects,
174
- /** @docsNotRequired */
175
- state) {
176
- super(id, url);
177
- this.urlAfterRedirects = urlAfterRedirects;
178
- this.state = state;
179
- this.type = 4 /* EventType.RoutesRecognized */;
180
- }
181
- /** @docsNotRequired */
182
- toString() {
183
- return `RoutesRecognized(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
184
- }
158
+ function last(a) {
159
+ return a.length > 0 ? a[a.length - 1] : null;
185
160
  }
186
161
  /**
187
- * An event triggered at the start of the Guard phase of routing.
188
- *
189
- * @see `GuardsCheckEnd`
190
- *
191
- * @publicApi
162
+ * Verifys all booleans in an array are `true`.
192
163
  */
193
- class GuardsCheckStart extends RouterEvent {
194
- constructor(
195
- /** @docsNotRequired */
196
- id,
197
- /** @docsNotRequired */
198
- url,
199
- /** @docsNotRequired */
200
- urlAfterRedirects,
201
- /** @docsNotRequired */
202
- state) {
203
- super(id, url);
204
- this.urlAfterRedirects = urlAfterRedirects;
205
- this.state = state;
206
- this.type = 7 /* EventType.GuardsCheckStart */;
164
+ function and(bools) {
165
+ return !bools.some(v => !v);
166
+ }
167
+ function forEach(map, callback) {
168
+ for (const prop in map) {
169
+ if (map.hasOwnProperty(prop)) {
170
+ callback(map[prop], prop);
171
+ }
207
172
  }
208
- toString() {
209
- return `GuardsCheckStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
173
+ }
174
+ function wrapIntoObservable(value) {
175
+ if (ɵisObservable(value)) {
176
+ return value;
177
+ }
178
+ if (ɵisPromise(value)) {
179
+ // Use `Promise.resolve()` to wrap promise-like instances.
180
+ // Required ie when a Resolver returns a AngularJS `$q` promise to correctly trigger the
181
+ // change detection.
182
+ return from(Promise.resolve(value));
210
183
  }
184
+ return of(value);
211
185
  }
186
+
212
187
  /**
213
- * An event triggered at the end of the Guard phase of routing.
214
- *
215
- * @see `GuardsCheckStart`
188
+ * @license
189
+ * Copyright Google LLC All Rights Reserved.
216
190
  *
217
- * @publicApi
191
+ * Use of this source code is governed by an MIT-style license that can be
192
+ * found in the LICENSE file at https://angular.io/license
218
193
  */
219
- class GuardsCheckEnd extends RouterEvent {
220
- constructor(
221
- /** @docsNotRequired */
222
- id,
223
- /** @docsNotRequired */
224
- url,
225
- /** @docsNotRequired */
226
- urlAfterRedirects,
227
- /** @docsNotRequired */
228
- state,
229
- /** @docsNotRequired */
230
- shouldActivate) {
231
- super(id, url);
232
- this.urlAfterRedirects = urlAfterRedirects;
233
- this.state = state;
234
- this.shouldActivate = shouldActivate;
235
- this.type = 8 /* EventType.GuardsCheckEnd */;
194
+ function createEmptyUrlTree() {
195
+ return new UrlTree(new UrlSegmentGroup([], {}), {}, null);
196
+ }
197
+ const pathCompareMap = {
198
+ 'exact': equalSegmentGroups,
199
+ 'subset': containsSegmentGroup,
200
+ };
201
+ const paramCompareMap = {
202
+ 'exact': equalParams,
203
+ 'subset': containsParams,
204
+ 'ignored': () => true,
205
+ };
206
+ function containsTree(container, containee, options) {
207
+ return pathCompareMap[options.paths](container.root, containee.root, options.matrixParams) &&
208
+ paramCompareMap[options.queryParams](container.queryParams, containee.queryParams) &&
209
+ !(options.fragment === 'exact' && container.fragment !== containee.fragment);
210
+ }
211
+ function equalParams(container, containee) {
212
+ // TODO: This does not handle array params correctly.
213
+ return shallowEqual(container, containee);
214
+ }
215
+ function equalSegmentGroups(container, containee, matrixParams) {
216
+ if (!equalPath(container.segments, containee.segments))
217
+ return false;
218
+ if (!matrixParamsMatch(container.segments, containee.segments, matrixParams)) {
219
+ return false;
236
220
  }
237
- toString() {
238
- return `GuardsCheckEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state}, shouldActivate: ${this.shouldActivate})`;
221
+ if (container.numberOfChildren !== containee.numberOfChildren)
222
+ return false;
223
+ for (const c in containee.children) {
224
+ if (!container.children[c])
225
+ return false;
226
+ if (!equalSegmentGroups(container.children[c], containee.children[c], matrixParams))
227
+ return false;
228
+ }
229
+ return true;
230
+ }
231
+ function containsParams(container, containee) {
232
+ return Object.keys(containee).length <= Object.keys(container).length &&
233
+ Object.keys(containee).every(key => equalArraysOrString(container[key], containee[key]));
234
+ }
235
+ function containsSegmentGroup(container, containee, matrixParams) {
236
+ return containsSegmentGroupHelper(container, containee, containee.segments, matrixParams);
237
+ }
238
+ function containsSegmentGroupHelper(container, containee, containeePaths, matrixParams) {
239
+ if (container.segments.length > containeePaths.length) {
240
+ const current = container.segments.slice(0, containeePaths.length);
241
+ if (!equalPath(current, containeePaths))
242
+ return false;
243
+ if (containee.hasChildren())
244
+ return false;
245
+ if (!matrixParamsMatch(current, containeePaths, matrixParams))
246
+ return false;
247
+ return true;
248
+ }
249
+ else if (container.segments.length === containeePaths.length) {
250
+ if (!equalPath(container.segments, containeePaths))
251
+ return false;
252
+ if (!matrixParamsMatch(container.segments, containeePaths, matrixParams))
253
+ return false;
254
+ for (const c in containee.children) {
255
+ if (!container.children[c])
256
+ return false;
257
+ if (!containsSegmentGroup(container.children[c], containee.children[c], matrixParams)) {
258
+ return false;
259
+ }
260
+ }
261
+ return true;
262
+ }
263
+ else {
264
+ const current = containeePaths.slice(0, container.segments.length);
265
+ const next = containeePaths.slice(container.segments.length);
266
+ if (!equalPath(container.segments, current))
267
+ return false;
268
+ if (!matrixParamsMatch(container.segments, current, matrixParams))
269
+ return false;
270
+ if (!container.children[PRIMARY_OUTLET])
271
+ return false;
272
+ return containsSegmentGroupHelper(container.children[PRIMARY_OUTLET], containee, next, matrixParams);
239
273
  }
240
274
  }
275
+ function matrixParamsMatch(containerPaths, containeePaths, options) {
276
+ return containeePaths.every((containeeSegment, i) => {
277
+ return paramCompareMap[options](containerPaths[i].parameters, containeeSegment.parameters);
278
+ });
279
+ }
241
280
  /**
242
- * An event triggered at the start of the Resolve phase of routing.
281
+ * @description
243
282
  *
244
- * Runs in the "resolve" phase whether or not there is anything to resolve.
245
- * In future, may change to only run when there are things to be resolved.
283
+ * Represents the parsed URL.
246
284
  *
247
- * @see `ResolveEnd`
285
+ * Since a router state is a tree, and the URL is nothing but a serialized state, the URL is a
286
+ * serialized tree.
287
+ * UrlTree is a data structure that provides a lot of affordances in dealing with URLs
288
+ *
289
+ * @usageNotes
290
+ * ### Example
291
+ *
292
+ * ```
293
+ * @Component({templateUrl:'template.html'})
294
+ * class MyComponent {
295
+ * constructor(router: Router) {
296
+ * const tree: UrlTree =
297
+ * router.parseUrl('/team/33/(user/victor//support:help)?debug=true#fragment');
298
+ * const f = tree.fragment; // return 'fragment'
299
+ * const q = tree.queryParams; // returns {debug: 'true'}
300
+ * const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];
301
+ * const s: UrlSegment[] = g.segments; // returns 2 segments 'team' and '33'
302
+ * g.children[PRIMARY_OUTLET].segments; // returns 2 segments 'user' and 'victor'
303
+ * g.children['support'].segments; // return 1 segment 'help'
304
+ * }
305
+ * }
306
+ * ```
248
307
  *
249
308
  * @publicApi
250
309
  */
251
- class ResolveStart extends RouterEvent {
310
+ class UrlTree {
311
+ /** @internal */
252
312
  constructor(
253
- /** @docsNotRequired */
254
- id,
255
- /** @docsNotRequired */
256
- url,
257
- /** @docsNotRequired */
258
- urlAfterRedirects,
259
- /** @docsNotRequired */
260
- state) {
261
- super(id, url);
262
- this.urlAfterRedirects = urlAfterRedirects;
263
- this.state = state;
264
- this.type = 5 /* EventType.ResolveStart */;
313
+ /** The root segment group of the URL tree */
314
+ root,
315
+ /** The query params of the URL */
316
+ queryParams,
317
+ /** The fragment of the URL */
318
+ fragment) {
319
+ this.root = root;
320
+ this.queryParams = queryParams;
321
+ this.fragment = fragment;
265
322
  }
266
- toString() {
267
- return `ResolveStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
323
+ get queryParamMap() {
324
+ if (!this._queryParamMap) {
325
+ this._queryParamMap = convertToParamMap(this.queryParams);
326
+ }
327
+ return this._queryParamMap;
268
328
  }
269
- }
270
- /**
271
- * An event triggered at the end of the Resolve phase of routing.
272
- * @see `ResolveStart`.
273
- *
274
- * @publicApi
275
- */
276
- class ResolveEnd extends RouterEvent {
277
- constructor(
278
- /** @docsNotRequired */
279
- id,
280
- /** @docsNotRequired */
281
- url,
282
- /** @docsNotRequired */
283
- urlAfterRedirects,
284
329
  /** @docsNotRequired */
285
- state) {
286
- super(id, url);
287
- this.urlAfterRedirects = urlAfterRedirects;
288
- this.state = state;
289
- this.type = 6 /* EventType.ResolveEnd */;
290
- }
291
330
  toString() {
292
- return `ResolveEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
331
+ return DEFAULT_SERIALIZER.serialize(this);
293
332
  }
294
333
  }
295
334
  /**
296
- * An event triggered before lazy loading a route configuration.
335
+ * @description
297
336
  *
298
- * @see `RouteConfigLoadEnd`
337
+ * Represents the parsed URL segment group.
338
+ *
339
+ * See `UrlTree` for more information.
299
340
  *
300
341
  * @publicApi
301
342
  */
302
- class RouteConfigLoadStart {
343
+ class UrlSegmentGroup {
303
344
  constructor(
304
- /** @docsNotRequired */
305
- route) {
306
- this.route = route;
307
- this.type = 9 /* EventType.RouteConfigLoadStart */;
345
+ /** The URL segments of this group. See `UrlSegment` for more information */
346
+ segments,
347
+ /** The list of children of this group */
348
+ children) {
349
+ this.segments = segments;
350
+ this.children = children;
351
+ /** The parent node in the url tree */
352
+ this.parent = null;
353
+ forEach(children, (v, k) => v.parent = this);
354
+ }
355
+ /** Whether the segment has child segments */
356
+ hasChildren() {
357
+ return this.numberOfChildren > 0;
358
+ }
359
+ /** Number of child segments */
360
+ get numberOfChildren() {
361
+ return Object.keys(this.children).length;
308
362
  }
363
+ /** @docsNotRequired */
309
364
  toString() {
310
- return `RouteConfigLoadStart(path: ${this.route.path})`;
365
+ return serializePaths(this);
311
366
  }
312
367
  }
313
368
  /**
314
- * An event triggered when a route has been lazy loaded.
369
+ * @description
315
370
  *
316
- * @see `RouteConfigLoadStart`
371
+ * Represents a single URL segment.
372
+ *
373
+ * A UrlSegment is a part of a URL between the two slashes. It contains a path and the matrix
374
+ * parameters associated with the segment.
375
+ *
376
+ * @usageNotes
377
+ * ### Example
378
+ *
379
+ * ```
380
+ * @Component({templateUrl:'template.html'})
381
+ * class MyComponent {
382
+ * constructor(router: Router) {
383
+ * const tree: UrlTree = router.parseUrl('/team;id=33');
384
+ * const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];
385
+ * const s: UrlSegment[] = g.segments;
386
+ * s[0].path; // returns 'team'
387
+ * s[0].parameters; // returns {id: 33}
388
+ * }
389
+ * }
390
+ * ```
317
391
  *
318
392
  * @publicApi
319
393
  */
320
- class RouteConfigLoadEnd {
394
+ class UrlSegment {
321
395
  constructor(
322
- /** @docsNotRequired */
323
- route) {
324
- this.route = route;
325
- this.type = 10 /* EventType.RouteConfigLoadEnd */;
396
+ /** The path part of a URL segment */
397
+ path,
398
+ /** The matrix parameters associated with a segment */
399
+ parameters) {
400
+ this.path = path;
401
+ this.parameters = parameters;
326
402
  }
403
+ get parameterMap() {
404
+ if (!this._parameterMap) {
405
+ this._parameterMap = convertToParamMap(this.parameters);
406
+ }
407
+ return this._parameterMap;
408
+ }
409
+ /** @docsNotRequired */
327
410
  toString() {
328
- return `RouteConfigLoadEnd(path: ${this.route.path})`;
411
+ return serializePath(this);
329
412
  }
330
413
  }
414
+ function equalSegments(as, bs) {
415
+ return equalPath(as, bs) && as.every((a, i) => shallowEqual(a.parameters, bs[i].parameters));
416
+ }
417
+ function equalPath(as, bs) {
418
+ if (as.length !== bs.length)
419
+ return false;
420
+ return as.every((a, i) => a.path === bs[i].path);
421
+ }
422
+ function mapChildrenIntoArray(segment, fn) {
423
+ let res = [];
424
+ forEach(segment.children, (child, childOutlet) => {
425
+ if (childOutlet === PRIMARY_OUTLET) {
426
+ res = res.concat(fn(child, childOutlet));
427
+ }
428
+ });
429
+ forEach(segment.children, (child, childOutlet) => {
430
+ if (childOutlet !== PRIMARY_OUTLET) {
431
+ res = res.concat(fn(child, childOutlet));
432
+ }
433
+ });
434
+ return res;
435
+ }
331
436
  /**
332
- * An event triggered at the start of the child-activation
333
- * part of the Resolve phase of routing.
334
- * @see `ChildActivationEnd`
335
- * @see `ResolveStart`
437
+ * @description
438
+ *
439
+ * Serializes and deserializes a URL string into a URL tree.
440
+ *
441
+ * The url serialization strategy is customizable. You can
442
+ * make all URLs case insensitive by providing a custom UrlSerializer.
443
+ *
444
+ * See `DefaultUrlSerializer` for an example of a URL serializer.
336
445
  *
337
446
  * @publicApi
338
447
  */
339
- class ChildActivationStart {
340
- constructor(
341
- /** @docsNotRequired */
342
- snapshot) {
343
- this.snapshot = snapshot;
344
- this.type = 11 /* EventType.ChildActivationStart */;
345
- }
346
- toString() {
347
- const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
348
- return `ChildActivationStart(path: '${path}')`;
349
- }
448
+ class UrlSerializer {
350
449
  }
351
450
  /**
352
- * An event triggered at the end of the child-activation part
353
- * of the Resolve phase of routing.
354
- * @see `ChildActivationStart`
355
- * @see `ResolveStart`
451
+ * @description
452
+ *
453
+ * A default implementation of the `UrlSerializer`.
454
+ *
455
+ * Example URLs:
456
+ *
457
+ * ```
458
+ * /inbox/33(popup:compose)
459
+ * /inbox/33;open=true/messages/44
460
+ * ```
461
+ *
462
+ * DefaultUrlSerializer uses parentheses to serialize secondary segments (e.g., popup:compose), the
463
+ * colon syntax to specify the outlet, and the ';parameter=value' syntax (e.g., open=true) to
464
+ * specify route specific parameters.
465
+ *
356
466
  * @publicApi
357
467
  */
358
- class ChildActivationEnd {
359
- constructor(
360
- /** @docsNotRequired */
361
- snapshot) {
362
- this.snapshot = snapshot;
363
- this.type = 12 /* EventType.ChildActivationEnd */;
468
+ class DefaultUrlSerializer {
469
+ /** Parses a url into a `UrlTree` */
470
+ parse(url) {
471
+ const p = new UrlParser(url);
472
+ return new UrlTree(p.parseRootSegment(), p.parseQueryParams(), p.parseFragment());
364
473
  }
365
- toString() {
366
- const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
367
- return `ChildActivationEnd(path: '${path}')`;
474
+ /** Converts a `UrlTree` into a url */
475
+ serialize(tree) {
476
+ const segment = `/${serializeSegment(tree.root, true)}`;
477
+ const query = serializeQueryParams(tree.queryParams);
478
+ const fragment = typeof tree.fragment === `string` ? `#${encodeUriFragment(tree.fragment)}` : '';
479
+ return `${segment}${query}${fragment}`;
480
+ }
481
+ }
482
+ const DEFAULT_SERIALIZER = new DefaultUrlSerializer();
483
+ function serializePaths(segment) {
484
+ return segment.segments.map(p => serializePath(p)).join('/');
485
+ }
486
+ function serializeSegment(segment, root) {
487
+ if (!segment.hasChildren()) {
488
+ return serializePaths(segment);
489
+ }
490
+ if (root) {
491
+ const primary = segment.children[PRIMARY_OUTLET] ?
492
+ serializeSegment(segment.children[PRIMARY_OUTLET], false) :
493
+ '';
494
+ const children = [];
495
+ forEach(segment.children, (v, k) => {
496
+ if (k !== PRIMARY_OUTLET) {
497
+ children.push(`${k}:${serializeSegment(v, false)}`);
498
+ }
499
+ });
500
+ return children.length > 0 ? `${primary}(${children.join('//')})` : primary;
501
+ }
502
+ else {
503
+ const children = mapChildrenIntoArray(segment, (v, k) => {
504
+ if (k === PRIMARY_OUTLET) {
505
+ return [serializeSegment(segment.children[PRIMARY_OUTLET], false)];
506
+ }
507
+ return [`${k}:${serializeSegment(v, false)}`];
508
+ });
509
+ // use no parenthesis if the only child is a primary outlet route
510
+ if (Object.keys(segment.children).length === 1 && segment.children[PRIMARY_OUTLET] != null) {
511
+ return `${serializePaths(segment)}/${children[0]}`;
512
+ }
513
+ return `${serializePaths(segment)}/(${children.join('//')})`;
368
514
  }
369
515
  }
370
516
  /**
371
- * An event triggered at the start of the activation part
372
- * of the Resolve phase of routing.
373
- * @see `ActivationEnd`
374
- * @see `ResolveStart`
517
+ * Encodes a URI string with the default encoding. This function will only ever be called from
518
+ * `encodeUriQuery` or `encodeUriSegment` as it's the base set of encodings to be used. We need
519
+ * a custom encoding because encodeURIComponent is too aggressive and encodes stuff that doesn't
520
+ * have to be encoded per https://url.spec.whatwg.org.
521
+ */
522
+ function encodeUriString(s) {
523
+ return encodeURIComponent(s)
524
+ .replace(/%40/g, '@')
525
+ .replace(/%3A/gi, ':')
526
+ .replace(/%24/g, '$')
527
+ .replace(/%2C/gi, ',');
528
+ }
529
+ /**
530
+ * This function should be used to encode both keys and values in a query string key/value. In
531
+ * the following URL, you need to call encodeUriQuery on "k" and "v":
375
532
  *
376
- * @publicApi
533
+ * http://www.site.org/html;mk=mv?k=v#f
377
534
  */
378
- class ActivationStart {
379
- constructor(
380
- /** @docsNotRequired */
381
- snapshot) {
382
- this.snapshot = snapshot;
383
- this.type = 13 /* EventType.ActivationStart */;
384
- }
385
- toString() {
386
- const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
387
- return `ActivationStart(path: '${path}')`;
388
- }
535
+ function encodeUriQuery(s) {
536
+ return encodeUriString(s).replace(/%3B/gi, ';');
389
537
  }
390
538
  /**
391
- * An event triggered at the end of the activation part
392
- * of the Resolve phase of routing.
393
- * @see `ActivationStart`
394
- * @see `ResolveStart`
539
+ * This function should be used to encode a URL fragment. In the following URL, you need to call
540
+ * encodeUriFragment on "f":
395
541
  *
396
- * @publicApi
542
+ * http://www.site.org/html;mk=mv?k=v#f
397
543
  */
398
- class ActivationEnd {
399
- constructor(
400
- /** @docsNotRequired */
401
- snapshot) {
402
- this.snapshot = snapshot;
403
- this.type = 14 /* EventType.ActivationEnd */;
404
- }
405
- toString() {
406
- const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
407
- return `ActivationEnd(path: '${path}')`;
408
- }
544
+ function encodeUriFragment(s) {
545
+ return encodeURI(s);
409
546
  }
410
547
  /**
411
- * An event triggered by scrolling.
548
+ * This function should be run on any URI segment as well as the key and value in a key/value
549
+ * pair for matrix params. In the following URL, you need to call encodeUriSegment on "html",
550
+ * "mk", and "mv":
412
551
  *
413
- * @publicApi
552
+ * http://www.site.org/html;mk=mv?k=v#f
414
553
  */
415
- class Scroll {
416
- constructor(
417
- /** @docsNotRequired */
418
- routerEvent,
419
- /** @docsNotRequired */
420
- position,
421
- /** @docsNotRequired */
422
- anchor) {
423
- this.routerEvent = routerEvent;
424
- this.position = position;
425
- this.anchor = anchor;
426
- this.type = 15 /* EventType.Scroll */;
427
- }
428
- toString() {
429
- const pos = this.position ? `${this.position[0]}, ${this.position[1]}` : null;
430
- return `Scroll(anchor: '${this.anchor}', position: '${pos}')`;
431
- }
554
+ function encodeUriSegment(s) {
555
+ return encodeUriString(s).replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/%26/gi, '&');
432
556
  }
433
- function stringifyEvent(routerEvent) {
434
- var _a, _b, _c, _d;
435
- if (!('type' in routerEvent)) {
436
- return `Unknown Router Event: ${routerEvent.constructor.name}`;
437
- }
438
- switch (routerEvent.type) {
439
- case 14 /* EventType.ActivationEnd */:
440
- return `ActivationEnd(path: '${((_a = routerEvent.snapshot.routeConfig) === null || _a === void 0 ? void 0 : _a.path) || ''}')`;
441
- case 13 /* EventType.ActivationStart */:
442
- return `ActivationStart(path: '${((_b = routerEvent.snapshot.routeConfig) === null || _b === void 0 ? void 0 : _b.path) || ''}')`;
443
- case 12 /* EventType.ChildActivationEnd */:
444
- return `ChildActivationEnd(path: '${((_c = routerEvent.snapshot.routeConfig) === null || _c === void 0 ? void 0 : _c.path) || ''}')`;
445
- case 11 /* EventType.ChildActivationStart */:
446
- return `ChildActivationStart(path: '${((_d = routerEvent.snapshot.routeConfig) === null || _d === void 0 ? void 0 : _d.path) || ''}')`;
447
- case 8 /* EventType.GuardsCheckEnd */:
448
- return `GuardsCheckEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state}, shouldActivate: ${routerEvent.shouldActivate})`;
449
- case 7 /* EventType.GuardsCheckStart */:
450
- return `GuardsCheckStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
451
- case 2 /* EventType.NavigationCancel */:
452
- return `NavigationCancel(id: ${routerEvent.id}, url: '${routerEvent.url}')`;
453
- case 1 /* EventType.NavigationEnd */:
454
- return `NavigationEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}')`;
455
- case 3 /* EventType.NavigationError */:
456
- return `NavigationError(id: ${routerEvent.id}, url: '${routerEvent.url}', error: ${routerEvent.error})`;
457
- case 0 /* EventType.NavigationStart */:
458
- return `NavigationStart(id: ${routerEvent.id}, url: '${routerEvent.url}')`;
459
- case 6 /* EventType.ResolveEnd */:
460
- return `ResolveEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
461
- case 5 /* EventType.ResolveStart */:
462
- return `ResolveStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
463
- case 10 /* EventType.RouteConfigLoadEnd */:
464
- return `RouteConfigLoadEnd(path: ${routerEvent.route.path})`;
465
- case 9 /* EventType.RouteConfigLoadStart */:
466
- return `RouteConfigLoadStart(path: ${routerEvent.route.path})`;
467
- case 4 /* EventType.RoutesRecognized */:
468
- return `RoutesRecognized(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
469
- case 15 /* EventType.Scroll */:
470
- const pos = routerEvent.position ? `${routerEvent.position[0]}, ${routerEvent.position[1]}` : null;
471
- return `Scroll(anchor: '${routerEvent.anchor}', position: '${pos}')`;
472
- }
557
+ function decode(s) {
558
+ return decodeURIComponent(s);
473
559
  }
474
-
475
- /**
476
- * @license
477
- * Copyright Google LLC All Rights Reserved.
478
- *
479
- * Use of this source code is governed by an MIT-style license that can be
480
- * found in the LICENSE file at https://angular.io/license
481
- */
482
- /**
483
- * The primary routing outlet.
484
- *
485
- * @publicApi
486
- */
487
- const PRIMARY_OUTLET = 'primary';
488
- class ParamsAsMap {
489
- constructor(params) {
490
- this.params = params || {};
491
- }
492
- has(name) {
493
- return Object.prototype.hasOwnProperty.call(this.params, name);
494
- }
495
- get(name) {
496
- if (this.has(name)) {
497
- const v = this.params[name];
498
- return Array.isArray(v) ? v[0] : v;
499
- }
500
- return null;
501
- }
502
- getAll(name) {
503
- if (this.has(name)) {
504
- const v = this.params[name];
505
- return Array.isArray(v) ? v : [v];
506
- }
507
- return [];
508
- }
509
- get keys() {
510
- return Object.keys(this.params);
511
- }
560
+ // Query keys/values should have the "+" replaced first, as "+" in a query string is " ".
561
+ // decodeURIComponent function will not decode "+" as a space.
562
+ function decodeQuery(s) {
563
+ return decode(s.replace(/\+/g, '%20'));
512
564
  }
513
- /**
514
- * Converts a `Params` instance to a `ParamMap`.
515
- * @param params The instance to convert.
516
- * @returns The new map instance.
517
- *
518
- * @publicApi
519
- */
520
- function convertToParamMap(params) {
521
- return new ParamsAsMap(params);
565
+ function serializePath(path) {
566
+ return `${encodeUriSegment(path.path)}${serializeMatrixParams(path.parameters)}`;
522
567
  }
523
- const NAVIGATION_CANCELING_ERROR = 'ngNavigationCancelingError';
524
- function navigationCancelingError(message) {
525
- const error = Error('NavigationCancelingError: ' + message);
526
- error[NAVIGATION_CANCELING_ERROR] = true;
527
- return error;
568
+ function serializeMatrixParams(params) {
569
+ return Object.keys(params)
570
+ .map(key => `;${encodeUriSegment(key)}=${encodeUriSegment(params[key])}`)
571
+ .join('');
528
572
  }
529
- function isNavigationCancelingError(error) {
530
- return error && error[NAVIGATION_CANCELING_ERROR];
573
+ function serializeQueryParams(params) {
574
+ const strParams = Object.keys(params)
575
+ .map((name) => {
576
+ const value = params[name];
577
+ return Array.isArray(value) ?
578
+ value.map(v => `${encodeUriQuery(name)}=${encodeUriQuery(v)}`).join('&') :
579
+ `${encodeUriQuery(name)}=${encodeUriQuery(value)}`;
580
+ })
581
+ .filter(s => !!s);
582
+ return strParams.length ? `?${strParams.join('&')}` : '';
531
583
  }
532
- // Matches the route configuration (`route`) against the actual URL (`segments`).
533
- function defaultUrlMatcher(segments, segmentGroup, route) {
534
- const parts = route.path.split('/');
535
- if (parts.length > segments.length) {
536
- // The actual URL is shorter than the config, no match
537
- return null;
538
- }
539
- if (route.pathMatch === 'full' &&
540
- (segmentGroup.hasChildren() || parts.length < segments.length)) {
541
- // The config is longer than the actual URL but we are looking for a full match, return null
542
- return null;
543
- }
544
- const posParams = {};
545
- // Check each config part against the actual URL
546
- for (let index = 0; index < parts.length; index++) {
547
- const part = parts[index];
548
- const segment = segments[index];
549
- const isParameter = part.startsWith(':');
550
- if (isParameter) {
551
- posParams[part.substring(1)] = segment;
552
- }
553
- else if (part !== segment.path) {
554
- // The actual URL part does not match the config, no match
555
- return null;
556
- }
557
- }
558
- return { consumed: segments.slice(0, parts.length), posParams };
584
+ const SEGMENT_RE = /^[^\/()?;=#]+/;
585
+ function matchSegments(str) {
586
+ const match = str.match(SEGMENT_RE);
587
+ return match ? match[0] : '';
559
588
  }
560
-
561
- /**
562
- * @license
563
- * Copyright Google LLC All Rights Reserved.
564
- *
565
- * Use of this source code is governed by an MIT-style license that can be
566
- * found in the LICENSE file at https://angular.io/license
567
- */
568
- function shallowEqualArrays(a, b) {
569
- if (a.length !== b.length)
570
- return false;
571
- for (let i = 0; i < a.length; ++i) {
572
- if (!shallowEqual(a[i], b[i]))
573
- return false;
574
- }
575
- return true;
589
+ const QUERY_PARAM_RE = /^[^=?&#]+/;
590
+ // Return the name of the query param at the start of the string or an empty string
591
+ function matchQueryParams(str) {
592
+ const match = str.match(QUERY_PARAM_RE);
593
+ return match ? match[0] : '';
576
594
  }
577
- function shallowEqual(a, b) {
578
- // While `undefined` should never be possible, it would sometimes be the case in IE 11
579
- // and pre-chromium Edge. The check below accounts for this edge case.
580
- const k1 = a ? Object.keys(a) : undefined;
581
- const k2 = b ? Object.keys(b) : undefined;
582
- if (!k1 || !k2 || k1.length != k2.length) {
583
- return false;
595
+ const QUERY_PARAM_VALUE_RE = /^[^&#]+/;
596
+ // Return the value of the query param at the start of the string or an empty string
597
+ function matchUrlQueryParamValue(str) {
598
+ const match = str.match(QUERY_PARAM_VALUE_RE);
599
+ return match ? match[0] : '';
600
+ }
601
+ class UrlParser {
602
+ constructor(url) {
603
+ this.url = url;
604
+ this.remaining = url;
584
605
  }
585
- let key;
586
- for (let i = 0; i < k1.length; i++) {
587
- key = k1[i];
588
- if (!equalArraysOrString(a[key], b[key])) {
589
- return false;
606
+ parseRootSegment() {
607
+ this.consumeOptional('/');
608
+ if (this.remaining === '' || this.peekStartsWith('?') || this.peekStartsWith('#')) {
609
+ return new UrlSegmentGroup([], {});
590
610
  }
611
+ // The root segment group never has segments
612
+ return new UrlSegmentGroup([], this.parseChildren());
591
613
  }
592
- return true;
593
- }
594
- /**
595
- * Test equality for arrays of strings or a string.
596
- */
597
- function equalArraysOrString(a, b) {
598
- if (Array.isArray(a) && Array.isArray(b)) {
599
- if (a.length !== b.length)
600
- return false;
601
- const aSorted = [...a].sort();
602
- const bSorted = [...b].sort();
603
- return aSorted.every((val, index) => bSorted[index] === val);
614
+ parseQueryParams() {
615
+ const params = {};
616
+ if (this.consumeOptional('?')) {
617
+ do {
618
+ this.parseQueryParam(params);
619
+ } while (this.consumeOptional('&'));
620
+ }
621
+ return params;
604
622
  }
605
- else {
606
- return a === b;
623
+ parseFragment() {
624
+ return this.consumeOptional('#') ? decodeURIComponent(this.remaining) : null;
607
625
  }
608
- }
609
- /**
610
- * Flattens single-level nested arrays.
611
- */
612
- function flatten(arr) {
613
- return Array.prototype.concat.apply([], arr);
614
- }
615
- /**
616
- * Return the last element of an array.
617
- */
618
- function last(a) {
619
- return a.length > 0 ? a[a.length - 1] : null;
620
- }
621
- /**
622
- * Verifys all booleans in an array are `true`.
623
- */
624
- function and(bools) {
625
- return !bools.some(v => !v);
626
- }
627
- function forEach(map, callback) {
628
- for (const prop in map) {
629
- if (map.hasOwnProperty(prop)) {
630
- callback(map[prop], prop);
626
+ parseChildren() {
627
+ if (this.remaining === '') {
628
+ return {};
629
+ }
630
+ this.consumeOptional('/');
631
+ const segments = [];
632
+ if (!this.peekStartsWith('(')) {
633
+ segments.push(this.parseSegment());
634
+ }
635
+ while (this.peekStartsWith('/') && !this.peekStartsWith('//') && !this.peekStartsWith('/(')) {
636
+ this.capture('/');
637
+ segments.push(this.parseSegment());
638
+ }
639
+ let children = {};
640
+ if (this.peekStartsWith('/(')) {
641
+ this.capture('/');
642
+ children = this.parseParens(true);
643
+ }
644
+ let res = {};
645
+ if (this.peekStartsWith('(')) {
646
+ res = this.parseParens(false);
631
647
  }
648
+ if (segments.length > 0 || Object.keys(children).length > 0) {
649
+ res[PRIMARY_OUTLET] = new UrlSegmentGroup(segments, children);
650
+ }
651
+ return res;
632
652
  }
633
- }
634
- function wrapIntoObservable(value) {
635
- if (ɵisObservable(value)) {
636
- return value;
653
+ // parse a segment with its matrix parameters
654
+ // ie `name;k1=v1;k2`
655
+ parseSegment() {
656
+ const path = matchSegments(this.remaining);
657
+ if (path === '' && this.peekStartsWith(';')) {
658
+ throw new Error(`Empty path url segment cannot have parameters: '${this.remaining}'.`);
659
+ }
660
+ this.capture(path);
661
+ return new UrlSegment(decode(path), this.parseMatrixParams());
637
662
  }
638
- if (ɵisPromise(value)) {
639
- // Use `Promise.resolve()` to wrap promise-like instances.
640
- // Required ie when a Resolver returns a AngularJS `$q` promise to correctly trigger the
641
- // change detection.
642
- return from(Promise.resolve(value));
663
+ parseMatrixParams() {
664
+ const params = {};
665
+ while (this.consumeOptional(';')) {
666
+ this.parseParam(params);
667
+ }
668
+ return params;
643
669
  }
644
- return of(value);
645
- }
646
-
647
- /**
648
- * @license
649
- * Copyright Google LLC All Rights Reserved.
650
- *
651
- * Use of this source code is governed by an MIT-style license that can be
652
- * found in the LICENSE file at https://angular.io/license
653
- */
654
- function createEmptyUrlTree() {
655
- return new UrlTree(new UrlSegmentGroup([], {}), {}, null);
656
- }
657
- const pathCompareMap = {
658
- 'exact': equalSegmentGroups,
659
- 'subset': containsSegmentGroup,
660
- };
661
- const paramCompareMap = {
662
- 'exact': equalParams,
663
- 'subset': containsParams,
664
- 'ignored': () => true,
665
- };
666
- function containsTree(container, containee, options) {
667
- return pathCompareMap[options.paths](container.root, containee.root, options.matrixParams) &&
668
- paramCompareMap[options.queryParams](container.queryParams, containee.queryParams) &&
669
- !(options.fragment === 'exact' && container.fragment !== containee.fragment);
670
- }
671
- function equalParams(container, containee) {
672
- // TODO: This does not handle array params correctly.
673
- return shallowEqual(container, containee);
674
- }
675
- function equalSegmentGroups(container, containee, matrixParams) {
676
- if (!equalPath(container.segments, containee.segments))
677
- return false;
678
- if (!matrixParamsMatch(container.segments, containee.segments, matrixParams)) {
679
- return false;
670
+ parseParam(params) {
671
+ const key = matchSegments(this.remaining);
672
+ if (!key) {
673
+ return;
674
+ }
675
+ this.capture(key);
676
+ let value = '';
677
+ if (this.consumeOptional('=')) {
678
+ const valueMatch = matchSegments(this.remaining);
679
+ if (valueMatch) {
680
+ value = valueMatch;
681
+ this.capture(value);
682
+ }
683
+ }
684
+ params[decode(key)] = decode(value);
680
685
  }
681
- if (container.numberOfChildren !== containee.numberOfChildren)
686
+ // Parse a single query parameter `name[=value]`
687
+ parseQueryParam(params) {
688
+ const key = matchQueryParams(this.remaining);
689
+ if (!key) {
690
+ return;
691
+ }
692
+ this.capture(key);
693
+ let value = '';
694
+ if (this.consumeOptional('=')) {
695
+ const valueMatch = matchUrlQueryParamValue(this.remaining);
696
+ if (valueMatch) {
697
+ value = valueMatch;
698
+ this.capture(value);
699
+ }
700
+ }
701
+ const decodedKey = decodeQuery(key);
702
+ const decodedVal = decodeQuery(value);
703
+ if (params.hasOwnProperty(decodedKey)) {
704
+ // Append to existing values
705
+ let currentVal = params[decodedKey];
706
+ if (!Array.isArray(currentVal)) {
707
+ currentVal = [currentVal];
708
+ params[decodedKey] = currentVal;
709
+ }
710
+ currentVal.push(decodedVal);
711
+ }
712
+ else {
713
+ // Create a new value
714
+ params[decodedKey] = decodedVal;
715
+ }
716
+ }
717
+ // parse `(a/b//outlet_name:c/d)`
718
+ parseParens(allowPrimary) {
719
+ const segments = {};
720
+ this.capture('(');
721
+ while (!this.consumeOptional(')') && this.remaining.length > 0) {
722
+ const path = matchSegments(this.remaining);
723
+ const next = this.remaining[path.length];
724
+ // if is is not one of these characters, then the segment was unescaped
725
+ // or the group was not closed
726
+ if (next !== '/' && next !== ')' && next !== ';') {
727
+ throw new Error(`Cannot parse url '${this.url}'`);
728
+ }
729
+ let outletName = undefined;
730
+ if (path.indexOf(':') > -1) {
731
+ outletName = path.slice(0, path.indexOf(':'));
732
+ this.capture(outletName);
733
+ this.capture(':');
734
+ }
735
+ else if (allowPrimary) {
736
+ outletName = PRIMARY_OUTLET;
737
+ }
738
+ const children = this.parseChildren();
739
+ segments[outletName] = Object.keys(children).length === 1 ? children[PRIMARY_OUTLET] :
740
+ new UrlSegmentGroup([], children);
741
+ this.consumeOptional('//');
742
+ }
743
+ return segments;
744
+ }
745
+ peekStartsWith(str) {
746
+ return this.remaining.startsWith(str);
747
+ }
748
+ // Consumes the prefix when it is present and returns whether it has been consumed
749
+ consumeOptional(str) {
750
+ if (this.peekStartsWith(str)) {
751
+ this.remaining = this.remaining.substring(str.length);
752
+ return true;
753
+ }
682
754
  return false;
683
- for (const c in containee.children) {
684
- if (!container.children[c])
685
- return false;
686
- if (!equalSegmentGroups(container.children[c], containee.children[c], matrixParams))
687
- return false;
688
755
  }
689
- return true;
690
- }
691
- function containsParams(container, containee) {
692
- return Object.keys(containee).length <= Object.keys(container).length &&
693
- Object.keys(containee).every(key => equalArraysOrString(container[key], containee[key]));
756
+ capture(str) {
757
+ if (!this.consumeOptional(str)) {
758
+ throw new Error(`Expected "${str}".`);
759
+ }
760
+ }
694
761
  }
695
- function containsSegmentGroup(container, containee, matrixParams) {
696
- return containsSegmentGroupHelper(container, containee, containee.segments, matrixParams);
762
+ function createRoot(rootCandidate) {
763
+ return rootCandidate.segments.length > 0 ?
764
+ new UrlSegmentGroup([], { [PRIMARY_OUTLET]: rootCandidate }) :
765
+ rootCandidate;
697
766
  }
698
- function containsSegmentGroupHelper(container, containee, containeePaths, matrixParams) {
699
- if (container.segments.length > containeePaths.length) {
700
- const current = container.segments.slice(0, containeePaths.length);
701
- if (!equalPath(current, containeePaths))
702
- return false;
703
- if (containee.hasChildren())
704
- return false;
705
- if (!matrixParamsMatch(current, containeePaths, matrixParams))
706
- return false;
707
- return true;
708
- }
709
- else if (container.segments.length === containeePaths.length) {
710
- if (!equalPath(container.segments, containeePaths))
711
- return false;
712
- if (!matrixParamsMatch(container.segments, containeePaths, matrixParams))
713
- return false;
714
- for (const c in containee.children) {
715
- if (!container.children[c])
716
- return false;
717
- if (!containsSegmentGroup(container.children[c], containee.children[c], matrixParams)) {
718
- return false;
719
- }
767
+ /**
768
+ * Recursively merges primary segment children into their parents and also drops empty children
769
+ * (those which have no segments and no children themselves). The latter prevents serializing a
770
+ * group into something like `/a(aux:)`, where `aux` is an empty child segment.
771
+ */
772
+ function squashSegmentGroup(segmentGroup) {
773
+ const newChildren = {};
774
+ for (const childOutlet of Object.keys(segmentGroup.children)) {
775
+ const child = segmentGroup.children[childOutlet];
776
+ const childCandidate = squashSegmentGroup(child);
777
+ // don't add empty children
778
+ if (childCandidate.segments.length > 0 || childCandidate.hasChildren()) {
779
+ newChildren[childOutlet] = childCandidate;
720
780
  }
721
- return true;
722
- }
723
- else {
724
- const current = containeePaths.slice(0, container.segments.length);
725
- const next = containeePaths.slice(container.segments.length);
726
- if (!equalPath(container.segments, current))
727
- return false;
728
- if (!matrixParamsMatch(container.segments, current, matrixParams))
729
- return false;
730
- if (!container.children[PRIMARY_OUTLET])
731
- return false;
732
- return containsSegmentGroupHelper(container.children[PRIMARY_OUTLET], containee, next, matrixParams);
733
781
  }
782
+ const s = new UrlSegmentGroup(segmentGroup.segments, newChildren);
783
+ return mergeTrivialChildren(s);
734
784
  }
735
- function matrixParamsMatch(containerPaths, containeePaths, options) {
736
- return containeePaths.every((containeeSegment, i) => {
737
- return paramCompareMap[options](containerPaths[i].parameters, containeeSegment.parameters);
738
- });
785
+ /**
786
+ * When possible, merges the primary outlet child into the parent `UrlSegmentGroup`.
787
+ *
788
+ * When a segment group has only one child which is a primary outlet, merges that child into the
789
+ * parent. That is, the child segment group's segments are merged into the `s` and the child's
790
+ * children become the children of `s`. Think of this like a 'squash', merging the child segment
791
+ * group into the parent.
792
+ */
793
+ function mergeTrivialChildren(s) {
794
+ if (s.numberOfChildren === 1 && s.children[PRIMARY_OUTLET]) {
795
+ const c = s.children[PRIMARY_OUTLET];
796
+ return new UrlSegmentGroup(s.segments.concat(c.segments), c.children);
797
+ }
798
+ return s;
739
799
  }
800
+
740
801
  /**
741
- * @description
802
+ * @license
803
+ * Copyright Google LLC All Rights Reserved.
742
804
  *
743
- * Represents the parsed URL.
805
+ * Use of this source code is governed by an MIT-style license that can be
806
+ * found in the LICENSE file at https://angular.io/license
807
+ */
808
+ /**
809
+ * Creates a `UrlTree` relative to an `ActivatedRouteSnapshot`.
810
+ *
811
+ * @publicApi
744
812
  *
745
- * Since a router state is a tree, and the URL is nothing but a serialized state, the URL is a
746
- * serialized tree.
747
- * UrlTree is a data structure that provides a lot of affordances in dealing with URLs
813
+ *
814
+ * @param relativeTo The `ActivatedRouteSnapshot` to apply the commands to
815
+ * @param commands An array of URL fragments with which to construct the new URL tree.
816
+ * If the path is static, can be the literal URL string. For a dynamic path, pass an array of path
817
+ * segments, followed by the parameters for each segment.
818
+ * The fragments are applied to the one provided in the `relativeTo` parameter.
819
+ * @param queryParams The query parameters for the `UrlTree`. `null` if the `UrlTree` does not have
820
+ * any query parameters.
821
+ * @param fragment The fragment for the `UrlTree`. `null` if the `UrlTree` does not have a fragment.
748
822
  *
749
823
  * @usageNotes
750
- * ### Example
751
824
  *
752
825
  * ```
753
- * @Component({templateUrl:'template.html'})
754
- * class MyComponent {
755
- * constructor(router: Router) {
756
- * const tree: UrlTree =
757
- * router.parseUrl('/team/33/(user/victor//support:help)?debug=true#fragment');
758
- * const f = tree.fragment; // return 'fragment'
759
- * const q = tree.queryParams; // returns {debug: 'true'}
760
- * const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];
761
- * const s: UrlSegment[] = g.segments; // returns 2 segments 'team' and '33'
762
- * g.children[PRIMARY_OUTLET].segments; // returns 2 segments 'user' and 'victor'
763
- * g.children['support'].segments; // return 1 segment 'help'
764
- * }
765
- * }
766
- * ```
826
+ * // create /team/33/user/11
827
+ * createUrlTreeFromSnapshot(snapshot, ['/team', 33, 'user', 11]);
767
828
  *
768
- * @publicApi
769
- */
770
- class UrlTree {
771
- /** @internal */
772
- constructor(
773
- /** The root segment group of the URL tree */
774
- root,
775
- /** The query params of the URL */
776
- queryParams,
777
- /** The fragment of the URL */
778
- fragment) {
779
- this.root = root;
780
- this.queryParams = queryParams;
781
- this.fragment = fragment;
782
- }
783
- get queryParamMap() {
784
- if (!this._queryParamMap) {
785
- this._queryParamMap = convertToParamMap(this.queryParams);
786
- }
787
- return this._queryParamMap;
788
- }
789
- /** @docsNotRequired */
790
- toString() {
791
- return DEFAULT_SERIALIZER.serialize(this);
792
- }
793
- }
794
- /**
795
- * @description
829
+ * // create /team/33;expand=true/user/11
830
+ * createUrlTreeFromSnapshot(snapshot, ['/team', 33, {expand: true}, 'user', 11]);
796
831
  *
797
- * Represents the parsed URL segment group.
832
+ * // you can collapse static segments like this (this works only with the first passed-in value):
833
+ * createUrlTreeFromSnapshot(snapshot, ['/team/33/user', userId]);
798
834
  *
799
- * See `UrlTree` for more information.
835
+ * // If the first segment can contain slashes, and you do not want the router to split it,
836
+ * // you can do the following:
837
+ * createUrlTreeFromSnapshot(snapshot, [{segmentPath: '/one/two'}]);
800
838
  *
801
- * @publicApi
802
- */
803
- class UrlSegmentGroup {
804
- constructor(
805
- /** The URL segments of this group. See `UrlSegment` for more information */
806
- segments,
807
- /** The list of children of this group */
808
- children) {
809
- this.segments = segments;
810
- this.children = children;
811
- /** The parent node in the url tree */
812
- this.parent = null;
813
- forEach(children, (v, k) => v.parent = this);
814
- }
815
- /** Whether the segment has child segments */
816
- hasChildren() {
817
- return this.numberOfChildren > 0;
818
- }
819
- /** Number of child segments */
820
- get numberOfChildren() {
821
- return Object.keys(this.children).length;
822
- }
823
- /** @docsNotRequired */
824
- toString() {
825
- return serializePaths(this);
826
- }
827
- }
828
- /**
829
- * @description
839
+ * // create /team/33/(user/11//right:chat)
840
+ * createUrlTreeFromSnapshot(snapshot, ['/team', 33, {outlets: {primary: 'user/11', right:
841
+ * 'chat'}}], null, null);
830
842
  *
831
- * Represents a single URL segment.
843
+ * // remove the right secondary node
844
+ * createUrlTreeFromSnapshot(snapshot, ['/team', 33, {outlets: {primary: 'user/11', right: null}}]);
832
845
  *
833
- * A UrlSegment is a part of a URL between the two slashes. It contains a path and the matrix
834
- * parameters associated with the segment.
846
+ * // For the examples below, assume the current URL is for the `/team/33/user/11` and the
847
+ * `ActivatedRouteSnapshot` points to `user/11`:
835
848
  *
836
- * @usageNotes
837
- * ### Example
849
+ * // navigate to /team/33/user/11/details
850
+ * createUrlTreeFromSnapshot(snapshot, ['details']);
838
851
  *
839
- * ```
840
- * @Component({templateUrl:'template.html'})
841
- * class MyComponent {
842
- * constructor(router: Router) {
843
- * const tree: UrlTree = router.parseUrl('/team;id=33');
844
- * const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET];
845
- * const s: UrlSegment[] = g.segments;
846
- * s[0].path; // returns 'team'
847
- * s[0].parameters; // returns {id: 33}
848
- * }
849
- * }
850
- * ```
852
+ * // navigate to /team/33/user/22
853
+ * createUrlTreeFromSnapshot(snapshot, ['../22']);
851
854
  *
852
- * @publicApi
855
+ * // navigate to /team/44/user/22
856
+ * createUrlTreeFromSnapshot(snapshot, ['../../team/44/user/22']);
857
+ * ```
853
858
  */
854
- class UrlSegment {
855
- constructor(
856
- /** The path part of a URL segment */
857
- path,
858
- /** The matrix parameters associated with a segment */
859
- parameters) {
860
- this.path = path;
861
- this.parameters = parameters;
862
- }
863
- get parameterMap() {
864
- if (!this._parameterMap) {
865
- this._parameterMap = convertToParamMap(this.parameters);
866
- }
867
- return this._parameterMap;
859
+ function createUrlTreeFromSnapshot(relativeTo, commands, queryParams = null, fragment = null) {
860
+ const relativeToUrlSegmentGroup = createSegmentGroupFromRoute(relativeTo);
861
+ return createUrlTreeFromSegmentGroup(relativeToUrlSegmentGroup, commands, queryParams, fragment);
862
+ }
863
+ function createSegmentGroupFromRoute(route) {
864
+ let targetGroup;
865
+ function createSegmentGroupFromRouteRecursive(currentRoute) {
866
+ const childOutlets = {};
867
+ for (const childSnapshot of currentRoute.children) {
868
+ const root = createSegmentGroupFromRouteRecursive(childSnapshot);
869
+ childOutlets[childSnapshot.outlet] = root;
870
+ }
871
+ const segmentGroup = new UrlSegmentGroup(currentRoute.url, childOutlets);
872
+ if (currentRoute === route) {
873
+ targetGroup = segmentGroup;
874
+ }
875
+ return segmentGroup;
876
+ }
877
+ const rootCandidate = createSegmentGroupFromRouteRecursive(route.root);
878
+ const rootSegmentGroup = createRoot(rootCandidate);
879
+ return targetGroup !== null && targetGroup !== void 0 ? targetGroup : rootSegmentGroup;
880
+ }
881
+ function createUrlTreeFromSegmentGroup(relativeTo, commands, queryParams, fragment) {
882
+ let root = relativeTo;
883
+ while (root.parent) {
884
+ root = root.parent;
885
+ }
886
+ // There are no commands so the `UrlTree` goes to the same path as the one created from the
887
+ // `UrlSegmentGroup`. All we need to do is update the `queryParams` and `fragment` without
888
+ // applying any other logic.
889
+ if (commands.length === 0) {
890
+ return tree(root, root, root, queryParams, fragment);
868
891
  }
869
- /** @docsNotRequired */
870
- toString() {
871
- return serializePath(this);
892
+ const nav = computeNavigation(commands);
893
+ if (nav.toRoot()) {
894
+ return tree(root, root, new UrlSegmentGroup([], {}), queryParams, fragment);
872
895
  }
896
+ const position = findStartingPositionForTargetGroup(nav, root, relativeTo);
897
+ const newSegmentGroup = position.processChildren ?
898
+ updateSegmentGroupChildren(position.segmentGroup, position.index, nav.commands) :
899
+ updateSegmentGroup(position.segmentGroup, position.index, nav.commands);
900
+ return tree(root, position.segmentGroup, newSegmentGroup, queryParams, fragment);
873
901
  }
874
- function equalSegments(as, bs) {
875
- return equalPath(as, bs) && as.every((a, i) => shallowEqual(a.parameters, bs[i].parameters));
876
- }
877
- function equalPath(as, bs) {
878
- if (as.length !== bs.length)
879
- return false;
880
- return as.every((a, i) => a.path === bs[i].path);
881
- }
882
- function mapChildrenIntoArray(segment, fn) {
883
- let res = [];
884
- forEach(segment.children, (child, childOutlet) => {
885
- if (childOutlet === PRIMARY_OUTLET) {
886
- res = res.concat(fn(child, childOutlet));
887
- }
888
- });
889
- forEach(segment.children, (child, childOutlet) => {
890
- if (childOutlet !== PRIMARY_OUTLET) {
891
- res = res.concat(fn(child, childOutlet));
902
+ function createUrlTree(route, urlTree, commands, queryParams, fragment) {
903
+ var _a, _b;
904
+ if (commands.length === 0) {
905
+ return tree(urlTree.root, urlTree.root, urlTree.root, queryParams, fragment);
906
+ }
907
+ const nav = computeNavigation(commands);
908
+ if (nav.toRoot()) {
909
+ return tree(urlTree.root, urlTree.root, new UrlSegmentGroup([], {}), queryParams, fragment);
910
+ }
911
+ function createTreeUsingPathIndex(lastPathIndex) {
912
+ var _a;
913
+ const startingPosition = findStartingPosition(nav, urlTree, (_a = route.snapshot) === null || _a === void 0 ? void 0 : _a._urlSegment, lastPathIndex);
914
+ const segmentGroup = startingPosition.processChildren ?
915
+ updateSegmentGroupChildren(startingPosition.segmentGroup, startingPosition.index, nav.commands) :
916
+ updateSegmentGroup(startingPosition.segmentGroup, startingPosition.index, nav.commands);
917
+ return tree(urlTree.root, startingPosition.segmentGroup, segmentGroup, queryParams, fragment);
918
+ }
919
+ // Note: The types should disallow `snapshot` from being `undefined` but due to test mocks, this
920
+ // may be the case. Since we try to access it at an earlier point before the refactor to add the
921
+ // warning for `relativeLinkResolution: 'legacy'`, this may cause failures in tests where it
922
+ // didn't before.
923
+ const result = createTreeUsingPathIndex((_a = route.snapshot) === null || _a === void 0 ? void 0 : _a._lastPathIndex);
924
+ // Check if application is relying on `relativeLinkResolution: 'legacy'`
925
+ if (typeof ngDevMode === 'undefined' || !!ngDevMode) {
926
+ const correctedResult = createTreeUsingPathIndex((_b = route.snapshot) === null || _b === void 0 ? void 0 : _b._correctedLastPathIndex);
927
+ if (correctedResult.toString() !== result.toString()) {
928
+ console.warn(`relativeLinkResolution: 'legacy' is deprecated and will be removed in a future version of Angular. The link to ${result.toString()} will change to ${correctedResult.toString()} if the code is not updated before then.`);
892
929
  }
893
- });
894
- return res;
930
+ }
931
+ return result;
895
932
  }
896
- /**
897
- * @description
898
- *
899
- * Serializes and deserializes a URL string into a URL tree.
900
- *
901
- * The url serialization strategy is customizable. You can
902
- * make all URLs case insensitive by providing a custom UrlSerializer.
903
- *
904
- * See `DefaultUrlSerializer` for an example of a URL serializer.
905
- *
906
- * @publicApi
907
- */
908
- class UrlSerializer {
933
+ function isMatrixParams(command) {
934
+ return typeof command === 'object' && command != null && !command.outlets && !command.segmentPath;
909
935
  }
910
936
  /**
911
- * @description
912
- *
913
- * A default implementation of the `UrlSerializer`.
914
- *
915
- * Example URLs:
916
- *
917
- * ```
918
- * /inbox/33(popup:compose)
919
- * /inbox/33;open=true/messages/44
920
- * ```
921
- *
922
- * DefaultUrlSerializer uses parentheses to serialize secondary segments (e.g., popup:compose), the
923
- * colon syntax to specify the outlet, and the ';parameter=value' syntax (e.g., open=true) to
924
- * specify route specific parameters.
925
- *
926
- * @publicApi
937
+ * Determines if a given command has an `outlets` map. When we encounter a command
938
+ * with an outlets k/v map, we need to apply each outlet individually to the existing segment.
927
939
  */
928
- class DefaultUrlSerializer {
929
- /** Parses a url into a `UrlTree` */
930
- parse(url) {
931
- const p = new UrlParser(url);
932
- return new UrlTree(p.parseRootSegment(), p.parseQueryParams(), p.parseFragment());
933
- }
934
- /** Converts a `UrlTree` into a url */
935
- serialize(tree) {
936
- const segment = `/${serializeSegment(tree.root, true)}`;
937
- const query = serializeQueryParams(tree.queryParams);
938
- const fragment = typeof tree.fragment === `string` ? `#${encodeUriFragment(tree.fragment)}` : '';
939
- return `${segment}${query}${fragment}`;
940
- }
941
- }
942
- const DEFAULT_SERIALIZER = new DefaultUrlSerializer();
943
- function serializePaths(segment) {
944
- return segment.segments.map(p => serializePath(p)).join('/');
940
+ function isCommandWithOutlets(command) {
941
+ return typeof command === 'object' && command != null && command.outlets;
945
942
  }
946
- function serializeSegment(segment, root) {
947
- if (!segment.hasChildren()) {
948
- return serializePaths(segment);
949
- }
950
- if (root) {
951
- const primary = segment.children[PRIMARY_OUTLET] ?
952
- serializeSegment(segment.children[PRIMARY_OUTLET], false) :
953
- '';
954
- const children = [];
955
- forEach(segment.children, (v, k) => {
956
- if (k !== PRIMARY_OUTLET) {
957
- children.push(`${k}:${serializeSegment(v, false)}`);
958
- }
943
+ function tree(oldRoot, oldSegmentGroup, newSegmentGroup, queryParams, fragment) {
944
+ let qp = {};
945
+ if (queryParams) {
946
+ forEach(queryParams, (value, name) => {
947
+ qp[name] = Array.isArray(value) ? value.map((v) => `${v}`) : `${value}`;
959
948
  });
960
- return children.length > 0 ? `${primary}(${children.join('//')})` : primary;
949
+ }
950
+ let rootCandidate;
951
+ if (oldRoot === oldSegmentGroup) {
952
+ rootCandidate = newSegmentGroup;
961
953
  }
962
954
  else {
963
- const children = mapChildrenIntoArray(segment, (v, k) => {
964
- if (k === PRIMARY_OUTLET) {
965
- return [serializeSegment(segment.children[PRIMARY_OUTLET], false)];
966
- }
967
- return [`${k}:${serializeSegment(v, false)}`];
968
- });
969
- // use no parenthesis if the only child is a primary outlet route
970
- if (Object.keys(segment.children).length === 1 && segment.children[PRIMARY_OUTLET] != null) {
971
- return `${serializePaths(segment)}/${children[0]}`;
972
- }
973
- return `${serializePaths(segment)}/(${children.join('//')})`;
955
+ rootCandidate = replaceSegment(oldRoot, oldSegmentGroup, newSegmentGroup);
974
956
  }
957
+ const newRoot = createRoot(squashSegmentGroup(rootCandidate));
958
+ return new UrlTree(newRoot, qp, fragment);
975
959
  }
976
960
  /**
977
- * Encodes a URI string with the default encoding. This function will only ever be called from
978
- * `encodeUriQuery` or `encodeUriSegment` as it's the base set of encodings to be used. We need
979
- * a custom encoding because encodeURIComponent is too aggressive and encodes stuff that doesn't
980
- * have to be encoded per https://url.spec.whatwg.org.
961
+ * Replaces the `oldSegment` which is located in some child of the `current` with the `newSegment`.
962
+ * This also has the effect of creating new `UrlSegmentGroup` copies to update references. This
963
+ * shouldn't be necessary but the fallback logic for an invalid ActivatedRoute in the creation uses
964
+ * the Router's current url tree. If we don't create new segment groups, we end up modifying that
965
+ * value.
981
966
  */
982
- function encodeUriString(s) {
983
- return encodeURIComponent(s)
984
- .replace(/%40/g, '@')
985
- .replace(/%3A/gi, ':')
986
- .replace(/%24/g, '$')
987
- .replace(/%2C/gi, ',');
967
+ function replaceSegment(current, oldSegment, newSegment) {
968
+ const children = {};
969
+ forEach(current.children, (c, outletName) => {
970
+ if (c === oldSegment) {
971
+ children[outletName] = newSegment;
972
+ }
973
+ else {
974
+ children[outletName] = replaceSegment(c, oldSegment, newSegment);
975
+ }
976
+ });
977
+ return new UrlSegmentGroup(current.segments, children);
988
978
  }
989
- /**
990
- * This function should be used to encode both keys and values in a query string key/value. In
991
- * the following URL, you need to call encodeUriQuery on "k" and "v":
992
- *
993
- * http://www.site.org/html;mk=mv?k=v#f
994
- */
995
- function encodeUriQuery(s) {
996
- return encodeUriString(s).replace(/%3B/gi, ';');
997
- }
998
- /**
999
- * This function should be used to encode a URL fragment. In the following URL, you need to call
1000
- * encodeUriFragment on "f":
1001
- *
1002
- * http://www.site.org/html;mk=mv?k=v#f
1003
- */
1004
- function encodeUriFragment(s) {
1005
- return encodeURI(s);
1006
- }
1007
- /**
1008
- * This function should be run on any URI segment as well as the key and value in a key/value
1009
- * pair for matrix params. In the following URL, you need to call encodeUriSegment on "html",
1010
- * "mk", and "mv":
1011
- *
1012
- * http://www.site.org/html;mk=mv?k=v#f
1013
- */
1014
- function encodeUriSegment(s) {
1015
- return encodeUriString(s).replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/%26/gi, '&');
1016
- }
1017
- function decode(s) {
1018
- return decodeURIComponent(s);
1019
- }
1020
- // Query keys/values should have the "+" replaced first, as "+" in a query string is " ".
1021
- // decodeURIComponent function will not decode "+" as a space.
1022
- function decodeQuery(s) {
1023
- return decode(s.replace(/\+/g, '%20'));
979
+ class Navigation {
980
+ constructor(isAbsolute, numberOfDoubleDots, commands) {
981
+ this.isAbsolute = isAbsolute;
982
+ this.numberOfDoubleDots = numberOfDoubleDots;
983
+ this.commands = commands;
984
+ if (isAbsolute && commands.length > 0 && isMatrixParams(commands[0])) {
985
+ throw new Error('Root segment cannot have matrix parameters');
986
+ }
987
+ const cmdWithOutlet = commands.find(isCommandWithOutlets);
988
+ if (cmdWithOutlet && cmdWithOutlet !== last(commands)) {
989
+ throw new Error('{outlets:{}} has to be the last command');
990
+ }
991
+ }
992
+ toRoot() {
993
+ return this.isAbsolute && this.commands.length === 1 && this.commands[0] == '/';
994
+ }
1024
995
  }
1025
- function serializePath(path) {
1026
- return `${encodeUriSegment(path.path)}${serializeMatrixParams(path.parameters)}`;
996
+ /** Transforms commands to a normalized `Navigation` */
997
+ function computeNavigation(commands) {
998
+ if ((typeof commands[0] === 'string') && commands.length === 1 && commands[0] === '/') {
999
+ return new Navigation(true, 0, commands);
1000
+ }
1001
+ let numberOfDoubleDots = 0;
1002
+ let isAbsolute = false;
1003
+ const res = commands.reduce((res, cmd, cmdIdx) => {
1004
+ if (typeof cmd === 'object' && cmd != null) {
1005
+ if (cmd.outlets) {
1006
+ const outlets = {};
1007
+ forEach(cmd.outlets, (commands, name) => {
1008
+ outlets[name] = typeof commands === 'string' ? commands.split('/') : commands;
1009
+ });
1010
+ return [...res, { outlets }];
1011
+ }
1012
+ if (cmd.segmentPath) {
1013
+ return [...res, cmd.segmentPath];
1014
+ }
1015
+ }
1016
+ if (!(typeof cmd === 'string')) {
1017
+ return [...res, cmd];
1018
+ }
1019
+ if (cmdIdx === 0) {
1020
+ cmd.split('/').forEach((urlPart, partIndex) => {
1021
+ if (partIndex == 0 && urlPart === '.') {
1022
+ // skip './a'
1023
+ }
1024
+ else if (partIndex == 0 && urlPart === '') { // '/a'
1025
+ isAbsolute = true;
1026
+ }
1027
+ else if (urlPart === '..') { // '../a'
1028
+ numberOfDoubleDots++;
1029
+ }
1030
+ else if (urlPart != '') {
1031
+ res.push(urlPart);
1032
+ }
1033
+ });
1034
+ return res;
1035
+ }
1036
+ return [...res, cmd];
1037
+ }, []);
1038
+ return new Navigation(isAbsolute, numberOfDoubleDots, res);
1027
1039
  }
1028
- function serializeMatrixParams(params) {
1029
- return Object.keys(params)
1030
- .map(key => `;${encodeUriSegment(key)}=${encodeUriSegment(params[key])}`)
1031
- .join('');
1040
+ class Position {
1041
+ constructor(segmentGroup, processChildren, index) {
1042
+ this.segmentGroup = segmentGroup;
1043
+ this.processChildren = processChildren;
1044
+ this.index = index;
1045
+ }
1032
1046
  }
1033
- function serializeQueryParams(params) {
1034
- const strParams = Object.keys(params)
1035
- .map((name) => {
1036
- const value = params[name];
1037
- return Array.isArray(value) ?
1038
- value.map(v => `${encodeUriQuery(name)}=${encodeUriQuery(v)}`).join('&') :
1039
- `${encodeUriQuery(name)}=${encodeUriQuery(value)}`;
1040
- })
1041
- .filter(s => !!s);
1042
- return strParams.length ? `?${strParams.join('&')}` : '';
1047
+ function findStartingPositionForTargetGroup(nav, root, target) {
1048
+ if (nav.isAbsolute) {
1049
+ return new Position(root, true, 0);
1050
+ }
1051
+ if (!target) {
1052
+ // `NaN` is used only to maintain backwards compatibility with incorrectly mocked
1053
+ // `ActivatedRouteSnapshot` in tests. In prior versions of this code, the position here was
1054
+ // determined based on an internal property that was rarely mocked, resulting in `NaN`. In
1055
+ // reality, this code path should _never_ be touched since `target` is not allowed to be falsey.
1056
+ return new Position(root, false, NaN);
1057
+ }
1058
+ if (target.parent === null) {
1059
+ return new Position(target, true, 0);
1060
+ }
1061
+ const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1;
1062
+ const index = target.segments.length - 1 + modifier;
1063
+ return createPositionApplyingDoubleDots(target, index, nav.numberOfDoubleDots);
1043
1064
  }
1044
- const SEGMENT_RE = /^[^\/()?;=#]+/;
1045
- function matchSegments(str) {
1046
- const match = str.match(SEGMENT_RE);
1047
- return match ? match[0] : '';
1065
+ function findStartingPosition(nav, tree, segmentGroup, lastPathIndex) {
1066
+ if (nav.isAbsolute) {
1067
+ return new Position(tree.root, true, 0);
1068
+ }
1069
+ if (lastPathIndex === -1) {
1070
+ // Pathless ActivatedRoute has _lastPathIndex === -1 but should not process children
1071
+ // see issue #26224, #13011, #35687
1072
+ // However, if the ActivatedRoute is the root we should process children like above.
1073
+ const processChildren = segmentGroup === tree.root;
1074
+ return new Position(segmentGroup, processChildren, 0);
1075
+ }
1076
+ const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1;
1077
+ const index = lastPathIndex + modifier;
1078
+ return createPositionApplyingDoubleDots(segmentGroup, index, nav.numberOfDoubleDots);
1048
1079
  }
1049
- const QUERY_PARAM_RE = /^[^=?&#]+/;
1050
- // Return the name of the query param at the start of the string or an empty string
1051
- function matchQueryParams(str) {
1052
- const match = str.match(QUERY_PARAM_RE);
1053
- return match ? match[0] : '';
1080
+ function createPositionApplyingDoubleDots(group, index, numberOfDoubleDots) {
1081
+ let g = group;
1082
+ let ci = index;
1083
+ let dd = numberOfDoubleDots;
1084
+ while (dd > ci) {
1085
+ dd -= ci;
1086
+ g = g.parent;
1087
+ if (!g) {
1088
+ throw new Error('Invalid number of \'../\'');
1089
+ }
1090
+ ci = g.segments.length;
1091
+ }
1092
+ return new Position(g, false, ci - dd);
1054
1093
  }
1055
- const QUERY_PARAM_VALUE_RE = /^[^&#]+/;
1056
- // Return the value of the query param at the start of the string or an empty string
1057
- function matchUrlQueryParamValue(str) {
1058
- const match = str.match(QUERY_PARAM_VALUE_RE);
1059
- return match ? match[0] : '';
1094
+ function getOutlets(commands) {
1095
+ if (isCommandWithOutlets(commands[0])) {
1096
+ return commands[0].outlets;
1097
+ }
1098
+ return { [PRIMARY_OUTLET]: commands };
1060
1099
  }
1061
- class UrlParser {
1062
- constructor(url) {
1063
- this.url = url;
1064
- this.remaining = url;
1100
+ function updateSegmentGroup(segmentGroup, startIndex, commands) {
1101
+ if (!segmentGroup) {
1102
+ segmentGroup = new UrlSegmentGroup([], {});
1065
1103
  }
1066
- parseRootSegment() {
1067
- this.consumeOptional('/');
1068
- if (this.remaining === '' || this.peekStartsWith('?') || this.peekStartsWith('#')) {
1069
- return new UrlSegmentGroup([], {});
1070
- }
1071
- // The root segment group never has segments
1072
- return new UrlSegmentGroup([], this.parseChildren());
1104
+ if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) {
1105
+ return updateSegmentGroupChildren(segmentGroup, startIndex, commands);
1073
1106
  }
1074
- parseQueryParams() {
1075
- const params = {};
1076
- if (this.consumeOptional('?')) {
1077
- do {
1078
- this.parseQueryParam(params);
1079
- } while (this.consumeOptional('&'));
1080
- }
1081
- return params;
1107
+ const m = prefixedWith(segmentGroup, startIndex, commands);
1108
+ const slicedCommands = commands.slice(m.commandIndex);
1109
+ if (m.match && m.pathIndex < segmentGroup.segments.length) {
1110
+ const g = new UrlSegmentGroup(segmentGroup.segments.slice(0, m.pathIndex), {});
1111
+ g.children[PRIMARY_OUTLET] =
1112
+ new UrlSegmentGroup(segmentGroup.segments.slice(m.pathIndex), segmentGroup.children);
1113
+ return updateSegmentGroupChildren(g, 0, slicedCommands);
1082
1114
  }
1083
- parseFragment() {
1084
- return this.consumeOptional('#') ? decodeURIComponent(this.remaining) : null;
1115
+ else if (m.match && slicedCommands.length === 0) {
1116
+ return new UrlSegmentGroup(segmentGroup.segments, {});
1085
1117
  }
1086
- parseChildren() {
1087
- if (this.remaining === '') {
1088
- return {};
1089
- }
1090
- this.consumeOptional('/');
1091
- const segments = [];
1092
- if (!this.peekStartsWith('(')) {
1093
- segments.push(this.parseSegment());
1094
- }
1095
- while (this.peekStartsWith('/') && !this.peekStartsWith('//') && !this.peekStartsWith('/(')) {
1096
- this.capture('/');
1097
- segments.push(this.parseSegment());
1098
- }
1099
- let children = {};
1100
- if (this.peekStartsWith('/(')) {
1101
- this.capture('/');
1102
- children = this.parseParens(true);
1103
- }
1104
- let res = {};
1105
- if (this.peekStartsWith('(')) {
1106
- res = this.parseParens(false);
1107
- }
1108
- if (segments.length > 0 || Object.keys(children).length > 0) {
1109
- res[PRIMARY_OUTLET] = new UrlSegmentGroup(segments, children);
1110
- }
1111
- return res;
1118
+ else if (m.match && !segmentGroup.hasChildren()) {
1119
+ return createNewSegmentGroup(segmentGroup, startIndex, commands);
1112
1120
  }
1113
- // parse a segment with its matrix parameters
1114
- // ie `name;k1=v1;k2`
1115
- parseSegment() {
1116
- const path = matchSegments(this.remaining);
1117
- if (path === '' && this.peekStartsWith(';')) {
1118
- throw new Error(`Empty path url segment cannot have parameters: '${this.remaining}'.`);
1119
- }
1120
- this.capture(path);
1121
- return new UrlSegment(decode(path), this.parseMatrixParams());
1121
+ else if (m.match) {
1122
+ return updateSegmentGroupChildren(segmentGroup, 0, slicedCommands);
1122
1123
  }
1123
- parseMatrixParams() {
1124
- const params = {};
1125
- while (this.consumeOptional(';')) {
1126
- this.parseParam(params);
1127
- }
1128
- return params;
1124
+ else {
1125
+ return createNewSegmentGroup(segmentGroup, startIndex, commands);
1129
1126
  }
1130
- parseParam(params) {
1131
- const key = matchSegments(this.remaining);
1132
- if (!key) {
1133
- return;
1134
- }
1135
- this.capture(key);
1136
- let value = '';
1137
- if (this.consumeOptional('=')) {
1138
- const valueMatch = matchSegments(this.remaining);
1139
- if (valueMatch) {
1140
- value = valueMatch;
1141
- this.capture(value);
1127
+ }
1128
+ function updateSegmentGroupChildren(segmentGroup, startIndex, commands) {
1129
+ if (commands.length === 0) {
1130
+ return new UrlSegmentGroup(segmentGroup.segments, {});
1131
+ }
1132
+ else {
1133
+ const outlets = getOutlets(commands);
1134
+ const children = {};
1135
+ forEach(outlets, (commands, outlet) => {
1136
+ if (typeof commands === 'string') {
1137
+ commands = [commands];
1142
1138
  }
1139
+ if (commands !== null) {
1140
+ children[outlet] = updateSegmentGroup(segmentGroup.children[outlet], startIndex, commands);
1141
+ }
1142
+ });
1143
+ forEach(segmentGroup.children, (child, childOutlet) => {
1144
+ if (outlets[childOutlet] === undefined) {
1145
+ children[childOutlet] = child;
1146
+ }
1147
+ });
1148
+ return new UrlSegmentGroup(segmentGroup.segments, children);
1149
+ }
1150
+ }
1151
+ function prefixedWith(segmentGroup, startIndex, commands) {
1152
+ let currentCommandIndex = 0;
1153
+ let currentPathIndex = startIndex;
1154
+ const noMatch = { match: false, pathIndex: 0, commandIndex: 0 };
1155
+ while (currentPathIndex < segmentGroup.segments.length) {
1156
+ if (currentCommandIndex >= commands.length)
1157
+ return noMatch;
1158
+ const path = segmentGroup.segments[currentPathIndex];
1159
+ const command = commands[currentCommandIndex];
1160
+ // Do not try to consume command as part of the prefixing if it has outlets because it can
1161
+ // contain outlets other than the one being processed. Consuming the outlets command would
1162
+ // result in other outlets being ignored.
1163
+ if (isCommandWithOutlets(command)) {
1164
+ break;
1143
1165
  }
1144
- params[decode(key)] = decode(value);
1166
+ const curr = `${command}`;
1167
+ const next = currentCommandIndex < commands.length - 1 ? commands[currentCommandIndex + 1] : null;
1168
+ if (currentPathIndex > 0 && curr === undefined)
1169
+ break;
1170
+ if (curr && next && (typeof next === 'object') && next.outlets === undefined) {
1171
+ if (!compare(curr, next, path))
1172
+ return noMatch;
1173
+ currentCommandIndex += 2;
1174
+ }
1175
+ else {
1176
+ if (!compare(curr, {}, path))
1177
+ return noMatch;
1178
+ currentCommandIndex++;
1179
+ }
1180
+ currentPathIndex++;
1145
1181
  }
1146
- // Parse a single query parameter `name[=value]`
1147
- parseQueryParam(params) {
1148
- const key = matchQueryParams(this.remaining);
1149
- if (!key) {
1150
- return;
1182
+ return { match: true, pathIndex: currentPathIndex, commandIndex: currentCommandIndex };
1183
+ }
1184
+ function createNewSegmentGroup(segmentGroup, startIndex, commands) {
1185
+ const paths = segmentGroup.segments.slice(0, startIndex);
1186
+ let i = 0;
1187
+ while (i < commands.length) {
1188
+ const command = commands[i];
1189
+ if (isCommandWithOutlets(command)) {
1190
+ const children = createNewSegmentChildren(command.outlets);
1191
+ return new UrlSegmentGroup(paths, children);
1151
1192
  }
1152
- this.capture(key);
1153
- let value = '';
1154
- if (this.consumeOptional('=')) {
1155
- const valueMatch = matchUrlQueryParamValue(this.remaining);
1156
- if (valueMatch) {
1157
- value = valueMatch;
1158
- this.capture(value);
1159
- }
1193
+ // if we start with an object literal, we need to reuse the path part from the segment
1194
+ if (i === 0 && isMatrixParams(commands[0])) {
1195
+ const p = segmentGroup.segments[startIndex];
1196
+ paths.push(new UrlSegment(p.path, stringify(commands[0])));
1197
+ i++;
1198
+ continue;
1160
1199
  }
1161
- const decodedKey = decodeQuery(key);
1162
- const decodedVal = decodeQuery(value);
1163
- if (params.hasOwnProperty(decodedKey)) {
1164
- // Append to existing values
1165
- let currentVal = params[decodedKey];
1166
- if (!Array.isArray(currentVal)) {
1167
- currentVal = [currentVal];
1168
- params[decodedKey] = currentVal;
1169
- }
1170
- currentVal.push(decodedVal);
1200
+ const curr = isCommandWithOutlets(command) ? command.outlets[PRIMARY_OUTLET] : `${command}`;
1201
+ const next = (i < commands.length - 1) ? commands[i + 1] : null;
1202
+ if (curr && next && isMatrixParams(next)) {
1203
+ paths.push(new UrlSegment(curr, stringify(next)));
1204
+ i += 2;
1171
1205
  }
1172
1206
  else {
1173
- // Create a new value
1174
- params[decodedKey] = decodedVal;
1207
+ paths.push(new UrlSegment(curr, {}));
1208
+ i++;
1175
1209
  }
1176
1210
  }
1177
- // parse `(a/b//outlet_name:c/d)`
1178
- parseParens(allowPrimary) {
1179
- const segments = {};
1180
- this.capture('(');
1181
- while (!this.consumeOptional(')') && this.remaining.length > 0) {
1182
- const path = matchSegments(this.remaining);
1183
- const next = this.remaining[path.length];
1184
- // if is is not one of these characters, then the segment was unescaped
1185
- // or the group was not closed
1186
- if (next !== '/' && next !== ')' && next !== ';') {
1187
- throw new Error(`Cannot parse url '${this.url}'`);
1188
- }
1189
- let outletName = undefined;
1190
- if (path.indexOf(':') > -1) {
1191
- outletName = path.slice(0, path.indexOf(':'));
1192
- this.capture(outletName);
1193
- this.capture(':');
1194
- }
1195
- else if (allowPrimary) {
1196
- outletName = PRIMARY_OUTLET;
1197
- }
1198
- const children = this.parseChildren();
1199
- segments[outletName] = Object.keys(children).length === 1 ? children[PRIMARY_OUTLET] :
1200
- new UrlSegmentGroup([], children);
1201
- this.consumeOptional('//');
1211
+ return new UrlSegmentGroup(paths, {});
1212
+ }
1213
+ function createNewSegmentChildren(outlets) {
1214
+ const children = {};
1215
+ forEach(outlets, (commands, outlet) => {
1216
+ if (typeof commands === 'string') {
1217
+ commands = [commands];
1202
1218
  }
1203
- return segments;
1219
+ if (commands !== null) {
1220
+ children[outlet] = createNewSegmentGroup(new UrlSegmentGroup([], {}), 0, commands);
1221
+ }
1222
+ });
1223
+ return children;
1224
+ }
1225
+ function stringify(params) {
1226
+ const res = {};
1227
+ forEach(params, (v, k) => res[k] = `${v}`);
1228
+ return res;
1229
+ }
1230
+ function compare(path, params, segment) {
1231
+ return path == segment.path && shallowEqual(params, segment.parameters);
1232
+ }
1233
+
1234
+ /**
1235
+ * @license
1236
+ * Copyright Google LLC All Rights Reserved.
1237
+ *
1238
+ * Use of this source code is governed by an MIT-style license that can be
1239
+ * found in the LICENSE file at https://angular.io/license
1240
+ */
1241
+ /**
1242
+ * Base for events the router goes through, as opposed to events tied to a specific
1243
+ * route. Fired one time for any given navigation.
1244
+ *
1245
+ * The following code shows how a class subscribes to router events.
1246
+ *
1247
+ * ```ts
1248
+ * import {Event, RouterEvent, Router} from '@angular/router';
1249
+ *
1250
+ * class MyService {
1251
+ * constructor(public router: Router) {
1252
+ * router.events.pipe(
1253
+ * filter((e: Event): e is RouterEvent => e instanceof RouterEvent)
1254
+ * ).subscribe((e: RouterEvent) => {
1255
+ * // Do something
1256
+ * });
1257
+ * }
1258
+ * }
1259
+ * ```
1260
+ *
1261
+ * @see `Event`
1262
+ * @see [Router events summary](guide/router-reference#router-events)
1263
+ * @publicApi
1264
+ */
1265
+ class RouterEvent {
1266
+ constructor(
1267
+ /** A unique ID that the router assigns to every router navigation. */
1268
+ id,
1269
+ /** The URL that is the destination for this navigation. */
1270
+ url) {
1271
+ this.id = id;
1272
+ this.url = url;
1273
+ }
1274
+ }
1275
+ /**
1276
+ * An event triggered when a navigation starts.
1277
+ *
1278
+ * @publicApi
1279
+ */
1280
+ class NavigationStart extends RouterEvent {
1281
+ constructor(
1282
+ /** @docsNotRequired */
1283
+ id,
1284
+ /** @docsNotRequired */
1285
+ url,
1286
+ /** @docsNotRequired */
1287
+ navigationTrigger = 'imperative',
1288
+ /** @docsNotRequired */
1289
+ restoredState = null) {
1290
+ super(id, url);
1291
+ this.type = 0 /* EventType.NavigationStart */;
1292
+ this.navigationTrigger = navigationTrigger;
1293
+ this.restoredState = restoredState;
1294
+ }
1295
+ /** @docsNotRequired */
1296
+ toString() {
1297
+ return `NavigationStart(id: ${this.id}, url: '${this.url}')`;
1298
+ }
1299
+ }
1300
+ /**
1301
+ * An event triggered when a navigation ends successfully.
1302
+ *
1303
+ * @see `NavigationStart`
1304
+ * @see `NavigationCancel`
1305
+ * @see `NavigationError`
1306
+ *
1307
+ * @publicApi
1308
+ */
1309
+ class NavigationEnd extends RouterEvent {
1310
+ constructor(
1311
+ /** @docsNotRequired */
1312
+ id,
1313
+ /** @docsNotRequired */
1314
+ url,
1315
+ /** @docsNotRequired */
1316
+ urlAfterRedirects) {
1317
+ super(id, url);
1318
+ this.urlAfterRedirects = urlAfterRedirects;
1319
+ this.type = 1 /* EventType.NavigationEnd */;
1204
1320
  }
1205
- peekStartsWith(str) {
1206
- return this.remaining.startsWith(str);
1321
+ /** @docsNotRequired */
1322
+ toString() {
1323
+ return `NavigationEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}')`;
1207
1324
  }
1208
- // Consumes the prefix when it is present and returns whether it has been consumed
1209
- consumeOptional(str) {
1210
- if (this.peekStartsWith(str)) {
1211
- this.remaining = this.remaining.substring(str.length);
1212
- return true;
1213
- }
1214
- return false;
1325
+ }
1326
+ /**
1327
+ * An event triggered when a navigation is canceled, directly or indirectly.
1328
+ * This can happen for several reasons including when a route guard
1329
+ * returns `false` or initiates a redirect by returning a `UrlTree`.
1330
+ *
1331
+ * @see `NavigationStart`
1332
+ * @see `NavigationEnd`
1333
+ * @see `NavigationError`
1334
+ *
1335
+ * @publicApi
1336
+ */
1337
+ class NavigationCancel extends RouterEvent {
1338
+ constructor(
1339
+ /** @docsNotRequired */
1340
+ id,
1341
+ /** @docsNotRequired */
1342
+ url,
1343
+ /** @docsNotRequired */
1344
+ reason) {
1345
+ super(id, url);
1346
+ this.reason = reason;
1347
+ this.type = 2 /* EventType.NavigationCancel */;
1215
1348
  }
1216
- capture(str) {
1217
- if (!this.consumeOptional(str)) {
1218
- throw new Error(`Expected "${str}".`);
1219
- }
1349
+ /** @docsNotRequired */
1350
+ toString() {
1351
+ return `NavigationCancel(id: ${this.id}, url: '${this.url}')`;
1220
1352
  }
1221
1353
  }
1222
-
1223
1354
  /**
1224
- * @license
1225
- * Copyright Google LLC All Rights Reserved.
1355
+ * An event triggered when a navigation fails due to an unexpected error.
1226
1356
  *
1227
- * Use of this source code is governed by an MIT-style license that can be
1228
- * found in the LICENSE file at https://angular.io/license
1357
+ * @see `NavigationStart`
1358
+ * @see `NavigationEnd`
1359
+ * @see `NavigationCancel`
1360
+ *
1361
+ * @publicApi
1229
1362
  */
1230
- class Tree {
1231
- constructor(root) {
1232
- this._root = root;
1233
- }
1234
- get root() {
1235
- return this._root.value;
1363
+ class NavigationError extends RouterEvent {
1364
+ constructor(
1365
+ /** @docsNotRequired */
1366
+ id,
1367
+ /** @docsNotRequired */
1368
+ url,
1369
+ /** @docsNotRequired */
1370
+ error) {
1371
+ super(id, url);
1372
+ this.error = error;
1373
+ this.type = 3 /* EventType.NavigationError */;
1236
1374
  }
1237
- /**
1238
- * @internal
1239
- */
1240
- parent(t) {
1241
- const p = this.pathFromRoot(t);
1242
- return p.length > 1 ? p[p.length - 2] : null;
1375
+ /** @docsNotRequired */
1376
+ toString() {
1377
+ return `NavigationError(id: ${this.id}, url: '${this.url}', error: ${this.error})`;
1243
1378
  }
1244
- /**
1245
- * @internal
1246
- */
1247
- children(t) {
1248
- const n = findNode(t, this._root);
1249
- return n ? n.children.map(t => t.value) : [];
1379
+ }
1380
+ /**
1381
+ * An event triggered when routes are recognized.
1382
+ *
1383
+ * @publicApi
1384
+ */
1385
+ class RoutesRecognized extends RouterEvent {
1386
+ constructor(
1387
+ /** @docsNotRequired */
1388
+ id,
1389
+ /** @docsNotRequired */
1390
+ url,
1391
+ /** @docsNotRequired */
1392
+ urlAfterRedirects,
1393
+ /** @docsNotRequired */
1394
+ state) {
1395
+ super(id, url);
1396
+ this.urlAfterRedirects = urlAfterRedirects;
1397
+ this.state = state;
1398
+ this.type = 4 /* EventType.RoutesRecognized */;
1250
1399
  }
1251
- /**
1252
- * @internal
1253
- */
1254
- firstChild(t) {
1255
- const n = findNode(t, this._root);
1256
- return n && n.children.length > 0 ? n.children[0].value : null;
1400
+ /** @docsNotRequired */
1401
+ toString() {
1402
+ return `RoutesRecognized(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
1257
1403
  }
1258
- /**
1259
- * @internal
1260
- */
1261
- siblings(t) {
1262
- const p = findPath(t, this._root);
1263
- if (p.length < 2)
1264
- return [];
1265
- const c = p[p.length - 2].children.map(c => c.value);
1266
- return c.filter(cc => cc !== t);
1404
+ }
1405
+ /**
1406
+ * An event triggered at the start of the Guard phase of routing.
1407
+ *
1408
+ * @see `GuardsCheckEnd`
1409
+ *
1410
+ * @publicApi
1411
+ */
1412
+ class GuardsCheckStart extends RouterEvent {
1413
+ constructor(
1414
+ /** @docsNotRequired */
1415
+ id,
1416
+ /** @docsNotRequired */
1417
+ url,
1418
+ /** @docsNotRequired */
1419
+ urlAfterRedirects,
1420
+ /** @docsNotRequired */
1421
+ state) {
1422
+ super(id, url);
1423
+ this.urlAfterRedirects = urlAfterRedirects;
1424
+ this.state = state;
1425
+ this.type = 7 /* EventType.GuardsCheckStart */;
1267
1426
  }
1268
- /**
1269
- * @internal
1270
- */
1271
- pathFromRoot(t) {
1272
- return findPath(t, this._root).map(s => s.value);
1427
+ toString() {
1428
+ return `GuardsCheckStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
1273
1429
  }
1274
1430
  }
1275
- // DFS for the node matching the value
1276
- function findNode(value, node) {
1277
- if (value === node.value)
1278
- return node;
1279
- for (const child of node.children) {
1280
- const node = findNode(value, child);
1281
- if (node)
1282
- return node;
1431
+ /**
1432
+ * An event triggered at the end of the Guard phase of routing.
1433
+ *
1434
+ * @see `GuardsCheckStart`
1435
+ *
1436
+ * @publicApi
1437
+ */
1438
+ class GuardsCheckEnd extends RouterEvent {
1439
+ constructor(
1440
+ /** @docsNotRequired */
1441
+ id,
1442
+ /** @docsNotRequired */
1443
+ url,
1444
+ /** @docsNotRequired */
1445
+ urlAfterRedirects,
1446
+ /** @docsNotRequired */
1447
+ state,
1448
+ /** @docsNotRequired */
1449
+ shouldActivate) {
1450
+ super(id, url);
1451
+ this.urlAfterRedirects = urlAfterRedirects;
1452
+ this.state = state;
1453
+ this.shouldActivate = shouldActivate;
1454
+ this.type = 8 /* EventType.GuardsCheckEnd */;
1283
1455
  }
1284
- return null;
1285
- }
1286
- // Return the path to the node with the given value using DFS
1287
- function findPath(value, node) {
1288
- if (value === node.value)
1289
- return [node];
1290
- for (const child of node.children) {
1291
- const path = findPath(value, child);
1292
- if (path.length) {
1293
- path.unshift(node);
1294
- return path;
1295
- }
1456
+ toString() {
1457
+ return `GuardsCheckEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state}, shouldActivate: ${this.shouldActivate})`;
1296
1458
  }
1297
- return [];
1298
1459
  }
1299
- class TreeNode {
1300
- constructor(value, children) {
1301
- this.value = value;
1302
- this.children = children;
1460
+ /**
1461
+ * An event triggered at the start of the Resolve phase of routing.
1462
+ *
1463
+ * Runs in the "resolve" phase whether or not there is anything to resolve.
1464
+ * In future, may change to only run when there are things to be resolved.
1465
+ *
1466
+ * @see `ResolveEnd`
1467
+ *
1468
+ * @publicApi
1469
+ */
1470
+ class ResolveStart extends RouterEvent {
1471
+ constructor(
1472
+ /** @docsNotRequired */
1473
+ id,
1474
+ /** @docsNotRequired */
1475
+ url,
1476
+ /** @docsNotRequired */
1477
+ urlAfterRedirects,
1478
+ /** @docsNotRequired */
1479
+ state) {
1480
+ super(id, url);
1481
+ this.urlAfterRedirects = urlAfterRedirects;
1482
+ this.state = state;
1483
+ this.type = 5 /* EventType.ResolveStart */;
1303
1484
  }
1304
1485
  toString() {
1305
- return `TreeNode(${this.value})`;
1486
+ return `ResolveStart(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
1306
1487
  }
1307
1488
  }
1308
- // Return the list of T indexed by outlet name
1309
- function nodeChildrenAsMap(node) {
1310
- const map = {};
1311
- if (node) {
1312
- node.children.forEach(child => map[child.value.outlet] = child);
1489
+ /**
1490
+ * An event triggered at the end of the Resolve phase of routing.
1491
+ * @see `ResolveStart`.
1492
+ *
1493
+ * @publicApi
1494
+ */
1495
+ class ResolveEnd extends RouterEvent {
1496
+ constructor(
1497
+ /** @docsNotRequired */
1498
+ id,
1499
+ /** @docsNotRequired */
1500
+ url,
1501
+ /** @docsNotRequired */
1502
+ urlAfterRedirects,
1503
+ /** @docsNotRequired */
1504
+ state) {
1505
+ super(id, url);
1506
+ this.urlAfterRedirects = urlAfterRedirects;
1507
+ this.state = state;
1508
+ this.type = 6 /* EventType.ResolveEnd */;
1509
+ }
1510
+ toString() {
1511
+ return `ResolveEnd(id: ${this.id}, url: '${this.url}', urlAfterRedirects: '${this.urlAfterRedirects}', state: ${this.state})`;
1313
1512
  }
1314
- return map;
1315
1513
  }
1316
-
1317
- /**
1318
- * @license
1319
- * Copyright Google LLC All Rights Reserved.
1320
- *
1321
- * Use of this source code is governed by an MIT-style license that can be
1322
- * found in the LICENSE file at https://angular.io/license
1323
- */
1324
1514
  /**
1325
- * Represents the state of the router as a tree of activated routes.
1326
- *
1327
- * @usageNotes
1328
- *
1329
- * Every node in the route tree is an `ActivatedRoute` instance
1330
- * that knows about the "consumed" URL segments, the extracted parameters,
1331
- * and the resolved data.
1332
- * Use the `ActivatedRoute` properties to traverse the tree from any node.
1333
- *
1334
- * The following fragment shows how a component gets the root node
1335
- * of the current state to establish its own route tree:
1336
- *
1337
- * ```
1338
- * @Component({templateUrl:'template.html'})
1339
- * class MyComponent {
1340
- * constructor(router: Router) {
1341
- * const state: RouterState = router.routerState;
1342
- * const root: ActivatedRoute = state.root;
1343
- * const child = root.firstChild;
1344
- * const id: Observable<string> = child.params.map(p => p.id);
1345
- * //...
1346
- * }
1347
- * }
1348
- * ```
1515
+ * An event triggered before lazy loading a route configuration.
1349
1516
  *
1350
- * @see `ActivatedRoute`
1351
- * @see [Getting route information](guide/router#getting-route-information)
1517
+ * @see `RouteConfigLoadEnd`
1352
1518
  *
1353
1519
  * @publicApi
1354
1520
  */
1355
- class RouterState extends Tree {
1356
- /** @internal */
1357
- constructor(root,
1358
- /** The current snapshot of the router state */
1359
- snapshot) {
1360
- super(root);
1361
- this.snapshot = snapshot;
1362
- setRouterState(this, root);
1521
+ class RouteConfigLoadStart {
1522
+ constructor(
1523
+ /** @docsNotRequired */
1524
+ route) {
1525
+ this.route = route;
1526
+ this.type = 9 /* EventType.RouteConfigLoadStart */;
1363
1527
  }
1364
1528
  toString() {
1365
- return this.snapshot.toString();
1529
+ return `RouteConfigLoadStart(path: ${this.route.path})`;
1366
1530
  }
1367
1531
  }
1368
- function createEmptyState(urlTree, rootComponent) {
1369
- const snapshot = createEmptyStateSnapshot(urlTree, rootComponent);
1370
- const emptyUrl = new BehaviorSubject([new UrlSegment('', {})]);
1371
- const emptyParams = new BehaviorSubject({});
1372
- const emptyData = new BehaviorSubject({});
1373
- const emptyQueryParams = new BehaviorSubject({});
1374
- const fragment = new BehaviorSubject('');
1375
- const activated = new ActivatedRoute(emptyUrl, emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, snapshot.root);
1376
- activated.snapshot = snapshot.root;
1377
- return new RouterState(new TreeNode(activated, []), snapshot);
1378
- }
1379
- function createEmptyStateSnapshot(urlTree, rootComponent) {
1380
- const emptyParams = {};
1381
- const emptyData = {};
1382
- const emptyQueryParams = {};
1383
- const fragment = '';
1384
- const activated = new ActivatedRouteSnapshot([], emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, null, urlTree.root, -1, {});
1385
- return new RouterStateSnapshot('', new TreeNode(activated, []));
1386
- }
1387
1532
  /**
1388
- * Provides access to information about a route associated with a component
1389
- * that is loaded in an outlet.
1390
- * Use to traverse the `RouterState` tree and extract information from nodes.
1391
- *
1392
- * The following example shows how to construct a component using information from a
1393
- * currently activated route.
1394
- *
1395
- * Note: the observables in this class only emit when the current and previous values differ based
1396
- * on shallow equality. For example, changing deeply nested properties in resolved `data` will not
1397
- * cause the `ActivatedRoute.data` `Observable` to emit a new value.
1398
- *
1399
- * {@example router/activated-route/module.ts region="activated-route"
1400
- * header="activated-route.component.ts"}
1533
+ * An event triggered when a route has been lazy loaded.
1401
1534
  *
1402
- * @see [Getting route information](guide/router#getting-route-information)
1535
+ * @see `RouteConfigLoadStart`
1403
1536
  *
1404
1537
  * @publicApi
1405
1538
  */
1406
- class ActivatedRoute {
1407
- /** @internal */
1539
+ class RouteConfigLoadEnd {
1408
1540
  constructor(
1409
- /** An observable of the URL segments matched by this route. */
1410
- url,
1411
- /** An observable of the matrix parameters scoped to this route. */
1412
- params,
1413
- /** An observable of the query parameters shared by all the routes. */
1414
- queryParams,
1415
- /** An observable of the URL fragment shared by all the routes. */
1416
- fragment,
1417
- /** An observable of the static and resolved data of this route. */
1418
- data,
1419
- /** The outlet name of the route, a constant. */
1420
- outlet,
1421
- /** The component of the route, a constant. */
1422
- component, futureSnapshot) {
1423
- this.url = url;
1424
- this.params = params;
1425
- this.queryParams = queryParams;
1426
- this.fragment = fragment;
1427
- this.data = data;
1428
- this.outlet = outlet;
1429
- this.component = component;
1430
- this._futureSnapshot = futureSnapshot;
1431
- }
1432
- /** The configuration used to match this route. */
1433
- get routeConfig() {
1434
- return this._futureSnapshot.routeConfig;
1435
- }
1436
- /** The root of the router state. */
1437
- get root() {
1438
- return this._routerState.root;
1541
+ /** @docsNotRequired */
1542
+ route) {
1543
+ this.route = route;
1544
+ this.type = 10 /* EventType.RouteConfigLoadEnd */;
1439
1545
  }
1440
- /** The parent of this route in the router state tree. */
1441
- get parent() {
1442
- return this._routerState.parent(this);
1546
+ toString() {
1547
+ return `RouteConfigLoadEnd(path: ${this.route.path})`;
1443
1548
  }
1444
- /** The first child of this route in the router state tree. */
1445
- get firstChild() {
1446
- return this._routerState.firstChild(this);
1549
+ }
1550
+ /**
1551
+ * An event triggered at the start of the child-activation
1552
+ * part of the Resolve phase of routing.
1553
+ * @see `ChildActivationEnd`
1554
+ * @see `ResolveStart`
1555
+ *
1556
+ * @publicApi
1557
+ */
1558
+ class ChildActivationStart {
1559
+ constructor(
1560
+ /** @docsNotRequired */
1561
+ snapshot) {
1562
+ this.snapshot = snapshot;
1563
+ this.type = 11 /* EventType.ChildActivationStart */;
1447
1564
  }
1448
- /** The children of this route in the router state tree. */
1449
- get children() {
1450
- return this._routerState.children(this);
1565
+ toString() {
1566
+ const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
1567
+ return `ChildActivationStart(path: '${path}')`;
1451
1568
  }
1452
- /** The path from the root of the router state tree to this route. */
1453
- get pathFromRoot() {
1454
- return this._routerState.pathFromRoot(this);
1569
+ }
1570
+ /**
1571
+ * An event triggered at the end of the child-activation part
1572
+ * of the Resolve phase of routing.
1573
+ * @see `ChildActivationStart`
1574
+ * @see `ResolveStart`
1575
+ * @publicApi
1576
+ */
1577
+ class ChildActivationEnd {
1578
+ constructor(
1579
+ /** @docsNotRequired */
1580
+ snapshot) {
1581
+ this.snapshot = snapshot;
1582
+ this.type = 12 /* EventType.ChildActivationEnd */;
1455
1583
  }
1456
- /**
1457
- * An Observable that contains a map of the required and optional parameters
1458
- * specific to the route.
1459
- * The map supports retrieving single and multiple values from the same parameter.
1460
- */
1461
- get paramMap() {
1462
- if (!this._paramMap) {
1463
- this._paramMap = this.params.pipe(map((p) => convertToParamMap(p)));
1464
- }
1465
- return this._paramMap;
1584
+ toString() {
1585
+ const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
1586
+ return `ChildActivationEnd(path: '${path}')`;
1466
1587
  }
1467
- /**
1468
- * An Observable that contains a map of the query parameters available to all routes.
1469
- * The map supports retrieving single and multiple values from the query parameter.
1470
- */
1471
- get queryParamMap() {
1472
- if (!this._queryParamMap) {
1473
- this._queryParamMap =
1474
- this.queryParams.pipe(map((p) => convertToParamMap(p)));
1475
- }
1476
- return this._queryParamMap;
1588
+ }
1589
+ /**
1590
+ * An event triggered at the start of the activation part
1591
+ * of the Resolve phase of routing.
1592
+ * @see `ActivationEnd`
1593
+ * @see `ResolveStart`
1594
+ *
1595
+ * @publicApi
1596
+ */
1597
+ class ActivationStart {
1598
+ constructor(
1599
+ /** @docsNotRequired */
1600
+ snapshot) {
1601
+ this.snapshot = snapshot;
1602
+ this.type = 13 /* EventType.ActivationStart */;
1477
1603
  }
1478
1604
  toString() {
1479
- return this.snapshot ? this.snapshot.toString() : `Future(${this._futureSnapshot})`;
1605
+ const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
1606
+ return `ActivationStart(path: '${path}')`;
1480
1607
  }
1481
1608
  }
1482
1609
  /**
1483
- * Returns the inherited params, data, and resolve for a given route.
1484
- * By default, this only inherits values up to the nearest path-less or component-less route.
1485
- * @internal
1610
+ * An event triggered at the end of the activation part
1611
+ * of the Resolve phase of routing.
1612
+ * @see `ActivationStart`
1613
+ * @see `ResolveStart`
1614
+ *
1615
+ * @publicApi
1486
1616
  */
1487
- function inheritedParamsDataResolve(route, paramsInheritanceStrategy = 'emptyOnly') {
1488
- const pathFromRoot = route.pathFromRoot;
1489
- let inheritingStartingFrom = 0;
1490
- if (paramsInheritanceStrategy !== 'always') {
1491
- inheritingStartingFrom = pathFromRoot.length - 1;
1492
- while (inheritingStartingFrom >= 1) {
1493
- const current = pathFromRoot[inheritingStartingFrom];
1494
- const parent = pathFromRoot[inheritingStartingFrom - 1];
1495
- // current route is an empty path => inherits its parent's params and data
1496
- if (current.routeConfig && current.routeConfig.path === '') {
1497
- inheritingStartingFrom--;
1498
- // parent is componentless => current route should inherit its params and data
1499
- }
1500
- else if (!parent.component) {
1501
- inheritingStartingFrom--;
1502
- }
1503
- else {
1504
- break;
1505
- }
1506
- }
1617
+ class ActivationEnd {
1618
+ constructor(
1619
+ /** @docsNotRequired */
1620
+ snapshot) {
1621
+ this.snapshot = snapshot;
1622
+ this.type = 14 /* EventType.ActivationEnd */;
1623
+ }
1624
+ toString() {
1625
+ const path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || '';
1626
+ return `ActivationEnd(path: '${path}')`;
1507
1627
  }
1508
- return flattenInherited(pathFromRoot.slice(inheritingStartingFrom));
1509
- }
1510
- /** @internal */
1511
- function flattenInherited(pathFromRoot) {
1512
- return pathFromRoot.reduce((res, curr) => {
1513
- var _a;
1514
- const params = Object.assign(Object.assign({}, res.params), curr.params);
1515
- const data = Object.assign(Object.assign({}, res.data), curr.data);
1516
- 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);
1517
- return { params, data, resolve };
1518
- }, { params: {}, data: {}, resolve: {} });
1519
1628
  }
1520
1629
  /**
1521
- * @description
1522
- *
1523
- * Contains the information about a route associated with a component loaded in an
1524
- * outlet at a particular moment in time. ActivatedRouteSnapshot can also be used to
1525
- * traverse the router state tree.
1526
- *
1527
- * The following example initializes a component with route information extracted
1528
- * from the snapshot of the root node at the time of creation.
1529
- *
1530
- * ```
1531
- * @Component({templateUrl:'./my-component.html'})
1532
- * class MyComponent {
1533
- * constructor(route: ActivatedRoute) {
1534
- * const id: string = route.snapshot.params.id;
1535
- * const url: string = route.snapshot.url.join('');
1536
- * const user = route.snapshot.data.user;
1537
- * }
1538
- * }
1539
- * ```
1630
+ * An event triggered by scrolling.
1540
1631
  *
1541
1632
  * @publicApi
1542
1633
  */
1543
- class ActivatedRouteSnapshot {
1544
- /** @internal */
1634
+ class Scroll {
1545
1635
  constructor(
1546
- /** The URL segments matched by this route */
1547
- url,
1636
+ /** @docsNotRequired */
1637
+ routerEvent,
1638
+ /** @docsNotRequired */
1639
+ position,
1640
+ /** @docsNotRequired */
1641
+ anchor) {
1642
+ this.routerEvent = routerEvent;
1643
+ this.position = position;
1644
+ this.anchor = anchor;
1645
+ this.type = 15 /* EventType.Scroll */;
1646
+ }
1647
+ toString() {
1648
+ const pos = this.position ? `${this.position[0]}, ${this.position[1]}` : null;
1649
+ return `Scroll(anchor: '${this.anchor}', position: '${pos}')`;
1650
+ }
1651
+ }
1652
+ function stringifyEvent(routerEvent) {
1653
+ var _a, _b, _c, _d;
1654
+ if (!('type' in routerEvent)) {
1655
+ return `Unknown Router Event: ${routerEvent.constructor.name}`;
1656
+ }
1657
+ switch (routerEvent.type) {
1658
+ case 14 /* EventType.ActivationEnd */:
1659
+ return `ActivationEnd(path: '${((_a = routerEvent.snapshot.routeConfig) === null || _a === void 0 ? void 0 : _a.path) || ''}')`;
1660
+ case 13 /* EventType.ActivationStart */:
1661
+ return `ActivationStart(path: '${((_b = routerEvent.snapshot.routeConfig) === null || _b === void 0 ? void 0 : _b.path) || ''}')`;
1662
+ case 12 /* EventType.ChildActivationEnd */:
1663
+ return `ChildActivationEnd(path: '${((_c = routerEvent.snapshot.routeConfig) === null || _c === void 0 ? void 0 : _c.path) || ''}')`;
1664
+ case 11 /* EventType.ChildActivationStart */:
1665
+ return `ChildActivationStart(path: '${((_d = routerEvent.snapshot.routeConfig) === null || _d === void 0 ? void 0 : _d.path) || ''}')`;
1666
+ case 8 /* EventType.GuardsCheckEnd */:
1667
+ return `GuardsCheckEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state}, shouldActivate: ${routerEvent.shouldActivate})`;
1668
+ case 7 /* EventType.GuardsCheckStart */:
1669
+ return `GuardsCheckStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
1670
+ case 2 /* EventType.NavigationCancel */:
1671
+ return `NavigationCancel(id: ${routerEvent.id}, url: '${routerEvent.url}')`;
1672
+ case 1 /* EventType.NavigationEnd */:
1673
+ return `NavigationEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}')`;
1674
+ case 3 /* EventType.NavigationError */:
1675
+ return `NavigationError(id: ${routerEvent.id}, url: '${routerEvent.url}', error: ${routerEvent.error})`;
1676
+ case 0 /* EventType.NavigationStart */:
1677
+ return `NavigationStart(id: ${routerEvent.id}, url: '${routerEvent.url}')`;
1678
+ case 6 /* EventType.ResolveEnd */:
1679
+ return `ResolveEnd(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
1680
+ case 5 /* EventType.ResolveStart */:
1681
+ return `ResolveStart(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
1682
+ case 10 /* EventType.RouteConfigLoadEnd */:
1683
+ return `RouteConfigLoadEnd(path: ${routerEvent.route.path})`;
1684
+ case 9 /* EventType.RouteConfigLoadStart */:
1685
+ return `RouteConfigLoadStart(path: ${routerEvent.route.path})`;
1686
+ case 4 /* EventType.RoutesRecognized */:
1687
+ return `RoutesRecognized(id: ${routerEvent.id}, url: '${routerEvent.url}', urlAfterRedirects: '${routerEvent.urlAfterRedirects}', state: ${routerEvent.state})`;
1688
+ case 15 /* EventType.Scroll */:
1689
+ const pos = routerEvent.position ? `${routerEvent.position[0]}, ${routerEvent.position[1]}` : null;
1690
+ return `Scroll(anchor: '${routerEvent.anchor}', position: '${pos}')`;
1691
+ }
1692
+ }
1693
+
1694
+ /**
1695
+ * @license
1696
+ * Copyright Google LLC All Rights Reserved.
1697
+ *
1698
+ * Use of this source code is governed by an MIT-style license that can be
1699
+ * found in the LICENSE file at https://angular.io/license
1700
+ */
1701
+ class Tree {
1702
+ constructor(root) {
1703
+ this._root = root;
1704
+ }
1705
+ get root() {
1706
+ return this._root.value;
1707
+ }
1548
1708
  /**
1549
- * The matrix parameters scoped to this route.
1550
- *
1551
- * You can compute all params (or data) in the router state or to get params outside
1552
- * of an activated component by traversing the `RouterState` tree as in the following
1553
- * example:
1554
- * ```
1555
- * collectRouteParams(router: Router) {
1556
- * let params = {};
1557
- * let stack: ActivatedRouteSnapshot[] = [router.routerState.snapshot.root];
1558
- * while (stack.length > 0) {
1559
- * const route = stack.pop()!;
1560
- * params = {...params, ...route.params};
1561
- * stack.push(...route.children);
1562
- * }
1563
- * return params;
1564
- * }
1565
- * ```
1709
+ * @internal
1566
1710
  */
1567
- params,
1568
- /** The query parameters shared by all the routes */
1569
- queryParams,
1570
- /** The URL fragment shared by all the routes */
1571
- fragment,
1572
- /** The static and resolved data of this route */
1573
- data,
1574
- /** The outlet name of the route */
1575
- outlet,
1576
- /** The component of the route */
1577
- component, routeConfig, urlSegment, lastPathIndex, resolve, correctedLastPathIndex) {
1578
- this.url = url;
1579
- this.params = params;
1580
- this.queryParams = queryParams;
1581
- this.fragment = fragment;
1582
- this.data = data;
1583
- this.outlet = outlet;
1584
- this.component = component;
1585
- this.routeConfig = routeConfig;
1586
- this._urlSegment = urlSegment;
1587
- this._lastPathIndex = lastPathIndex;
1588
- this._correctedLastPathIndex = correctedLastPathIndex !== null && correctedLastPathIndex !== void 0 ? correctedLastPathIndex : lastPathIndex;
1589
- this._resolve = resolve;
1711
+ parent(t) {
1712
+ const p = this.pathFromRoot(t);
1713
+ return p.length > 1 ? p[p.length - 2] : null;
1590
1714
  }
1591
- /** The root of the router state */
1592
- get root() {
1593
- return this._routerState.root;
1715
+ /**
1716
+ * @internal
1717
+ */
1718
+ children(t) {
1719
+ const n = findNode(t, this._root);
1720
+ return n ? n.children.map(t => t.value) : [];
1594
1721
  }
1595
- /** The parent of this route in the router state tree */
1596
- get parent() {
1597
- return this._routerState.parent(this);
1722
+ /**
1723
+ * @internal
1724
+ */
1725
+ firstChild(t) {
1726
+ const n = findNode(t, this._root);
1727
+ return n && n.children.length > 0 ? n.children[0].value : null;
1598
1728
  }
1599
- /** The first child of this route in the router state tree */
1600
- get firstChild() {
1601
- return this._routerState.firstChild(this);
1729
+ /**
1730
+ * @internal
1731
+ */
1732
+ siblings(t) {
1733
+ const p = findPath(t, this._root);
1734
+ if (p.length < 2)
1735
+ return [];
1736
+ const c = p[p.length - 2].children.map(c => c.value);
1737
+ return c.filter(cc => cc !== t);
1602
1738
  }
1603
- /** The children of this route in the router state tree */
1604
- get children() {
1605
- return this._routerState.children(this);
1739
+ /**
1740
+ * @internal
1741
+ */
1742
+ pathFromRoot(t) {
1743
+ return findPath(t, this._root).map(s => s.value);
1606
1744
  }
1607
- /** The path from the root of the router state tree to this route */
1608
- get pathFromRoot() {
1609
- return this._routerState.pathFromRoot(this);
1745
+ }
1746
+ // DFS for the node matching the value
1747
+ function findNode(value, node) {
1748
+ if (value === node.value)
1749
+ return node;
1750
+ for (const child of node.children) {
1751
+ const node = findNode(value, child);
1752
+ if (node)
1753
+ return node;
1610
1754
  }
1611
- get paramMap() {
1612
- if (!this._paramMap) {
1613
- this._paramMap = convertToParamMap(this.params);
1755
+ return null;
1756
+ }
1757
+ // Return the path to the node with the given value using DFS
1758
+ function findPath(value, node) {
1759
+ if (value === node.value)
1760
+ return [node];
1761
+ for (const child of node.children) {
1762
+ const path = findPath(value, child);
1763
+ if (path.length) {
1764
+ path.unshift(node);
1765
+ return path;
1614
1766
  }
1615
- return this._paramMap;
1616
1767
  }
1617
- get queryParamMap() {
1618
- if (!this._queryParamMap) {
1619
- this._queryParamMap = convertToParamMap(this.queryParams);
1620
- }
1621
- return this._queryParamMap;
1768
+ return [];
1769
+ }
1770
+ class TreeNode {
1771
+ constructor(value, children) {
1772
+ this.value = value;
1773
+ this.children = children;
1622
1774
  }
1623
1775
  toString() {
1624
- const url = this.url.map(segment => segment.toString()).join('/');
1625
- const matched = this.routeConfig ? this.routeConfig.path : '';
1626
- return `Route(url:'${url}', path:'${matched}')`;
1776
+ return `TreeNode(${this.value})`;
1777
+ }
1778
+ }
1779
+ // Return the list of T indexed by outlet name
1780
+ function nodeChildrenAsMap(node) {
1781
+ const map = {};
1782
+ if (node) {
1783
+ node.children.forEach(child => map[child.value.outlet] = child);
1627
1784
  }
1785
+ return map;
1628
1786
  }
1787
+
1629
1788
  /**
1630
- * @description
1789
+ * @license
1790
+ * Copyright Google LLC All Rights Reserved.
1631
1791
  *
1632
- * Represents the state of the router at a moment in time.
1792
+ * Use of this source code is governed by an MIT-style license that can be
1793
+ * found in the LICENSE file at https://angular.io/license
1794
+ */
1795
+ /**
1796
+ * Represents the state of the router as a tree of activated routes.
1633
1797
  *
1634
- * This is a tree of activated route snapshots. Every node in this tree knows about
1635
- * the "consumed" URL segments, the extracted parameters, and the resolved data.
1798
+ * @usageNotes
1799
+ *
1800
+ * Every node in the route tree is an `ActivatedRoute` instance
1801
+ * that knows about the "consumed" URL segments, the extracted parameters,
1802
+ * and the resolved data.
1803
+ * Use the `ActivatedRoute` properties to traverse the tree from any node.
1636
1804
  *
1637
- * The following example shows how a component is initialized with information
1638
- * from the snapshot of the root node's state at the time of creation.
1805
+ * The following fragment shows how a component gets the root node
1806
+ * of the current state to establish its own route tree:
1639
1807
  *
1640
1808
  * ```
1641
1809
  * @Component({templateUrl:'template.html'})
1642
1810
  * class MyComponent {
1643
1811
  * constructor(router: Router) {
1644
1812
  * const state: RouterState = router.routerState;
1645
- * const snapshot: RouterStateSnapshot = state.snapshot;
1646
- * const root: ActivatedRouteSnapshot = snapshot.root;
1813
+ * const root: ActivatedRoute = state.root;
1647
1814
  * const child = root.firstChild;
1648
1815
  * const id: Observable<string> = child.params.map(p => p.id);
1649
1816
  * //...
@@ -1651,425 +1818,419 @@ class ActivatedRouteSnapshot {
1651
1818
  * }
1652
1819
  * ```
1653
1820
  *
1821
+ * @see `ActivatedRoute`
1822
+ * @see [Getting route information](guide/router#getting-route-information)
1823
+ *
1654
1824
  * @publicApi
1655
1825
  */
1656
- class RouterStateSnapshot extends Tree {
1826
+ class RouterState extends Tree {
1657
1827
  /** @internal */
1658
- constructor(
1659
- /** The url from which this snapshot was created */
1660
- url, root) {
1828
+ constructor(root,
1829
+ /** The current snapshot of the router state */
1830
+ snapshot) {
1661
1831
  super(root);
1662
- this.url = url;
1832
+ this.snapshot = snapshot;
1663
1833
  setRouterState(this, root);
1664
1834
  }
1665
1835
  toString() {
1666
- return serializeNode(this._root);
1836
+ return this.snapshot.toString();
1667
1837
  }
1668
1838
  }
1669
- function setRouterState(state, node) {
1670
- node.value._routerState = state;
1671
- node.children.forEach(c => setRouterState(state, c));
1672
- }
1673
- function serializeNode(node) {
1674
- const c = node.children.length > 0 ? ` { ${node.children.map(serializeNode).join(', ')} } ` : '';
1675
- return `${node.value}${c}`;
1676
- }
1677
- /**
1678
- * The expectation is that the activate route is created with the right set of parameters.
1679
- * So we push new values into the observables only when they are not the initial values.
1680
- * And we detect that by checking if the snapshot field is set.
1681
- */
1682
- function advanceActivatedRoute(route) {
1683
- if (route.snapshot) {
1684
- const currentSnapshot = route.snapshot;
1685
- const nextSnapshot = route._futureSnapshot;
1686
- route.snapshot = nextSnapshot;
1687
- if (!shallowEqual(currentSnapshot.queryParams, nextSnapshot.queryParams)) {
1688
- route.queryParams.next(nextSnapshot.queryParams);
1689
- }
1690
- if (currentSnapshot.fragment !== nextSnapshot.fragment) {
1691
- route.fragment.next(nextSnapshot.fragment);
1692
- }
1693
- if (!shallowEqual(currentSnapshot.params, nextSnapshot.params)) {
1694
- route.params.next(nextSnapshot.params);
1695
- }
1696
- if (!shallowEqualArrays(currentSnapshot.url, nextSnapshot.url)) {
1697
- route.url.next(nextSnapshot.url);
1698
- }
1699
- if (!shallowEqual(currentSnapshot.data, nextSnapshot.data)) {
1700
- route.data.next(nextSnapshot.data);
1701
- }
1702
- }
1703
- else {
1704
- route.snapshot = route._futureSnapshot;
1705
- // this is for resolved data
1706
- route.data.next(route._futureSnapshot.data);
1707
- }
1839
+ function createEmptyState(urlTree, rootComponent) {
1840
+ const snapshot = createEmptyStateSnapshot(urlTree, rootComponent);
1841
+ const emptyUrl = new BehaviorSubject([new UrlSegment('', {})]);
1842
+ const emptyParams = new BehaviorSubject({});
1843
+ const emptyData = new BehaviorSubject({});
1844
+ const emptyQueryParams = new BehaviorSubject({});
1845
+ const fragment = new BehaviorSubject('');
1846
+ const activated = new ActivatedRoute(emptyUrl, emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, snapshot.root);
1847
+ activated.snapshot = snapshot.root;
1848
+ return new RouterState(new TreeNode(activated, []), snapshot);
1708
1849
  }
1709
- function equalParamsAndUrlSegments(a, b) {
1710
- const equalUrlParams = shallowEqual(a.params, b.params) && equalSegments(a.url, b.url);
1711
- const parentsMismatch = !a.parent !== !b.parent;
1712
- return equalUrlParams && !parentsMismatch &&
1713
- (!a.parent || equalParamsAndUrlSegments(a.parent, b.parent));
1850
+ function createEmptyStateSnapshot(urlTree, rootComponent) {
1851
+ const emptyParams = {};
1852
+ const emptyData = {};
1853
+ const emptyQueryParams = {};
1854
+ const fragment = '';
1855
+ const activated = new ActivatedRouteSnapshot([], emptyParams, emptyQueryParams, fragment, emptyData, PRIMARY_OUTLET, rootComponent, null, urlTree.root, -1, {});
1856
+ return new RouterStateSnapshot('', new TreeNode(activated, []));
1714
1857
  }
1715
-
1716
1858
  /**
1717
- * @license
1718
- * Copyright Google LLC All Rights Reserved.
1859
+ * Provides access to information about a route associated with a component
1860
+ * that is loaded in an outlet.
1861
+ * Use to traverse the `RouterState` tree and extract information from nodes.
1719
1862
  *
1720
- * Use of this source code is governed by an MIT-style license that can be
1721
- * found in the LICENSE file at https://angular.io/license
1722
- */
1723
- function createRouterState(routeReuseStrategy, curr, prevState) {
1724
- const root = createNode(routeReuseStrategy, curr._root, prevState ? prevState._root : undefined);
1725
- return new RouterState(root, curr);
1726
- }
1727
- function createNode(routeReuseStrategy, curr, prevState) {
1728
- // reuse an activated route that is currently displayed on the screen
1729
- if (prevState && routeReuseStrategy.shouldReuseRoute(curr.value, prevState.value.snapshot)) {
1730
- const value = prevState.value;
1731
- value._futureSnapshot = curr.value;
1732
- const children = createOrReuseChildren(routeReuseStrategy, curr, prevState);
1733
- return new TreeNode(value, children);
1734
- }
1735
- else {
1736
- if (routeReuseStrategy.shouldAttach(curr.value)) {
1737
- // retrieve an activated route that is used to be displayed, but is not currently displayed
1738
- const detachedRouteHandle = routeReuseStrategy.retrieve(curr.value);
1739
- if (detachedRouteHandle !== null) {
1740
- const tree = detachedRouteHandle.route;
1741
- tree.value._futureSnapshot = curr.value;
1742
- tree.children = curr.children.map(c => createNode(routeReuseStrategy, c));
1743
- return tree;
1744
- }
1745
- }
1746
- const value = createActivatedRoute(curr.value);
1747
- const children = curr.children.map(c => createNode(routeReuseStrategy, c));
1748
- return new TreeNode(value, children);
1749
- }
1750
- }
1751
- function createOrReuseChildren(routeReuseStrategy, curr, prevState) {
1752
- return curr.children.map(child => {
1753
- for (const p of prevState.children) {
1754
- if (routeReuseStrategy.shouldReuseRoute(child.value, p.value.snapshot)) {
1755
- return createNode(routeReuseStrategy, child, p);
1756
- }
1757
- }
1758
- return createNode(routeReuseStrategy, child);
1759
- });
1760
- }
1761
- function createActivatedRoute(c) {
1762
- 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);
1763
- }
1764
-
1765
- /**
1766
- * @license
1767
- * Copyright Google LLC All Rights Reserved.
1863
+ * The following example shows how to construct a component using information from a
1864
+ * currently activated route.
1768
1865
  *
1769
- * Use of this source code is governed by an MIT-style license that can be
1770
- * found in the LICENSE file at https://angular.io/license
1866
+ * Note: the observables in this class only emit when the current and previous values differ based
1867
+ * on shallow equality. For example, changing deeply nested properties in resolved `data` will not
1868
+ * cause the `ActivatedRoute.data` `Observable` to emit a new value.
1869
+ *
1870
+ * {@example router/activated-route/module.ts region="activated-route"
1871
+ * header="activated-route.component.ts"}
1872
+ *
1873
+ * @see [Getting route information](guide/router#getting-route-information)
1874
+ *
1875
+ * @publicApi
1771
1876
  */
1772
- function createUrlTree(route, urlTree, commands, queryParams, fragment) {
1773
- var _a, _b;
1774
- if (commands.length === 0) {
1775
- return tree(urlTree.root, urlTree.root, urlTree.root, queryParams, fragment);
1776
- }
1777
- const nav = computeNavigation(commands);
1778
- if (nav.toRoot()) {
1779
- return tree(urlTree.root, urlTree.root, new UrlSegmentGroup([], {}), queryParams, fragment);
1780
- }
1781
- function createTreeUsingPathIndex(lastPathIndex) {
1782
- var _a;
1783
- const startingPosition = findStartingPosition(nav, urlTree, (_a = route.snapshot) === null || _a === void 0 ? void 0 : _a._urlSegment, lastPathIndex);
1784
- const segmentGroup = startingPosition.processChildren ?
1785
- updateSegmentGroupChildren(startingPosition.segmentGroup, startingPosition.index, nav.commands) :
1786
- updateSegmentGroup(startingPosition.segmentGroup, startingPosition.index, nav.commands);
1787
- return tree(urlTree.root, startingPosition.segmentGroup, segmentGroup, queryParams, fragment);
1788
- }
1789
- // Note: The types should disallow `snapshot` from being `undefined` but due to test mocks, this
1790
- // may be the case. Since we try to access it at an earlier point before the refactor to add the
1791
- // warning for `relativeLinkResolution: 'legacy'`, this may cause failures in tests where it
1792
- // didn't before.
1793
- const result = createTreeUsingPathIndex((_a = route.snapshot) === null || _a === void 0 ? void 0 : _a._lastPathIndex);
1794
- // Check if application is relying on `relativeLinkResolution: 'legacy'`
1795
- if (typeof ngDevMode === 'undefined' || !!ngDevMode) {
1796
- const correctedResult = createTreeUsingPathIndex((_b = route.snapshot) === null || _b === void 0 ? void 0 : _b._correctedLastPathIndex);
1797
- if (correctedResult.toString() !== result.toString()) {
1798
- console.warn(`relativeLinkResolution: 'legacy' is deprecated and will be removed in a future version of Angular. The link to ${result.toString()} will change to ${correctedResult.toString()} if the code is not updated before then.`);
1799
- }
1877
+ class ActivatedRoute {
1878
+ /** @internal */
1879
+ constructor(
1880
+ /** An observable of the URL segments matched by this route. */
1881
+ url,
1882
+ /** An observable of the matrix parameters scoped to this route. */
1883
+ params,
1884
+ /** An observable of the query parameters shared by all the routes. */
1885
+ queryParams,
1886
+ /** An observable of the URL fragment shared by all the routes. */
1887
+ fragment,
1888
+ /** An observable of the static and resolved data of this route. */
1889
+ data,
1890
+ /** The outlet name of the route, a constant. */
1891
+ outlet,
1892
+ /** The component of the route, a constant. */
1893
+ component, futureSnapshot) {
1894
+ this.url = url;
1895
+ this.params = params;
1896
+ this.queryParams = queryParams;
1897
+ this.fragment = fragment;
1898
+ this.data = data;
1899
+ this.outlet = outlet;
1900
+ this.component = component;
1901
+ this._futureSnapshot = futureSnapshot;
1800
1902
  }
1801
- return result;
1802
- }
1803
- function isMatrixParams(command) {
1804
- return typeof command === 'object' && command != null && !command.outlets && !command.segmentPath;
1805
- }
1806
- /**
1807
- * Determines if a given command has an `outlets` map. When we encounter a command
1808
- * with an outlets k/v map, we need to apply each outlet individually to the existing segment.
1809
- */
1810
- function isCommandWithOutlets(command) {
1811
- return typeof command === 'object' && command != null && command.outlets;
1812
- }
1813
- function tree(oldRoot, oldSegmentGroup, newSegmentGroup, queryParams, fragment) {
1814
- let qp = {};
1815
- if (queryParams) {
1816
- forEach(queryParams, (value, name) => {
1817
- qp[name] = Array.isArray(value) ? value.map((v) => `${v}`) : `${value}`;
1818
- });
1903
+ /** The configuration used to match this route. */
1904
+ get routeConfig() {
1905
+ return this._futureSnapshot.routeConfig;
1819
1906
  }
1820
- if (oldRoot === oldSegmentGroup) {
1821
- return new UrlTree(newSegmentGroup, qp, fragment);
1907
+ /** The root of the router state. */
1908
+ get root() {
1909
+ return this._routerState.root;
1822
1910
  }
1823
- const newRoot = replaceSegment(oldRoot, oldSegmentGroup, newSegmentGroup);
1824
- return new UrlTree(newRoot, qp, fragment);
1825
- }
1826
- function replaceSegment(current, oldSegment, newSegment) {
1827
- const children = {};
1828
- forEach(current.children, (c, outletName) => {
1829
- if (c === oldSegment) {
1830
- children[outletName] = newSegment;
1831
- }
1832
- else {
1833
- children[outletName] = replaceSegment(c, oldSegment, newSegment);
1834
- }
1835
- });
1836
- return new UrlSegmentGroup(current.segments, children);
1837
- }
1838
- class Navigation {
1839
- constructor(isAbsolute, numberOfDoubleDots, commands) {
1840
- this.isAbsolute = isAbsolute;
1841
- this.numberOfDoubleDots = numberOfDoubleDots;
1842
- this.commands = commands;
1843
- if (isAbsolute && commands.length > 0 && isMatrixParams(commands[0])) {
1844
- throw new Error('Root segment cannot have matrix parameters');
1911
+ /** The parent of this route in the router state tree. */
1912
+ get parent() {
1913
+ return this._routerState.parent(this);
1914
+ }
1915
+ /** The first child of this route in the router state tree. */
1916
+ get firstChild() {
1917
+ return this._routerState.firstChild(this);
1918
+ }
1919
+ /** The children of this route in the router state tree. */
1920
+ get children() {
1921
+ return this._routerState.children(this);
1922
+ }
1923
+ /** The path from the root of the router state tree to this route. */
1924
+ get pathFromRoot() {
1925
+ return this._routerState.pathFromRoot(this);
1926
+ }
1927
+ /**
1928
+ * An Observable that contains a map of the required and optional parameters
1929
+ * specific to the route.
1930
+ * The map supports retrieving single and multiple values from the same parameter.
1931
+ */
1932
+ get paramMap() {
1933
+ if (!this._paramMap) {
1934
+ this._paramMap = this.params.pipe(map((p) => convertToParamMap(p)));
1845
1935
  }
1846
- const cmdWithOutlet = commands.find(isCommandWithOutlets);
1847
- if (cmdWithOutlet && cmdWithOutlet !== last(commands)) {
1848
- throw new Error('{outlets:{}} has to be the last command');
1936
+ return this._paramMap;
1937
+ }
1938
+ /**
1939
+ * An Observable that contains a map of the query parameters available to all routes.
1940
+ * The map supports retrieving single and multiple values from the query parameter.
1941
+ */
1942
+ get queryParamMap() {
1943
+ if (!this._queryParamMap) {
1944
+ this._queryParamMap =
1945
+ this.queryParams.pipe(map((p) => convertToParamMap(p)));
1849
1946
  }
1947
+ return this._queryParamMap;
1850
1948
  }
1851
- toRoot() {
1852
- return this.isAbsolute && this.commands.length === 1 && this.commands[0] == '/';
1949
+ toString() {
1950
+ return this.snapshot ? this.snapshot.toString() : `Future(${this._futureSnapshot})`;
1853
1951
  }
1854
1952
  }
1855
- /** Transforms commands to a normalized `Navigation` */
1856
- function computeNavigation(commands) {
1857
- if ((typeof commands[0] === 'string') && commands.length === 1 && commands[0] === '/') {
1858
- return new Navigation(true, 0, commands);
1859
- }
1860
- let numberOfDoubleDots = 0;
1861
- let isAbsolute = false;
1862
- const res = commands.reduce((res, cmd, cmdIdx) => {
1863
- if (typeof cmd === 'object' && cmd != null) {
1864
- if (cmd.outlets) {
1865
- const outlets = {};
1866
- forEach(cmd.outlets, (commands, name) => {
1867
- outlets[name] = typeof commands === 'string' ? commands.split('/') : commands;
1868
- });
1869
- return [...res, { outlets }];
1953
+ /**
1954
+ * Returns the inherited params, data, and resolve for a given route.
1955
+ * By default, this only inherits values up to the nearest path-less or component-less route.
1956
+ * @internal
1957
+ */
1958
+ function inheritedParamsDataResolve(route, paramsInheritanceStrategy = 'emptyOnly') {
1959
+ const pathFromRoot = route.pathFromRoot;
1960
+ let inheritingStartingFrom = 0;
1961
+ if (paramsInheritanceStrategy !== 'always') {
1962
+ inheritingStartingFrom = pathFromRoot.length - 1;
1963
+ while (inheritingStartingFrom >= 1) {
1964
+ const current = pathFromRoot[inheritingStartingFrom];
1965
+ const parent = pathFromRoot[inheritingStartingFrom - 1];
1966
+ // current route is an empty path => inherits its parent's params and data
1967
+ if (current.routeConfig && current.routeConfig.path === '') {
1968
+ inheritingStartingFrom--;
1969
+ // parent is componentless => current route should inherit its params and data
1870
1970
  }
1871
- if (cmd.segmentPath) {
1872
- return [...res, cmd.segmentPath];
1971
+ else if (!parent.component) {
1972
+ inheritingStartingFrom--;
1973
+ }
1974
+ else {
1975
+ break;
1873
1976
  }
1874
1977
  }
1875
- if (!(typeof cmd === 'string')) {
1876
- return [...res, cmd];
1877
- }
1878
- if (cmdIdx === 0) {
1879
- cmd.split('/').forEach((urlPart, partIndex) => {
1880
- if (partIndex == 0 && urlPart === '.') {
1881
- // skip './a'
1882
- }
1883
- else if (partIndex == 0 && urlPart === '') { // '/a'
1884
- isAbsolute = true;
1885
- }
1886
- else if (urlPart === '..') { // '../a'
1887
- numberOfDoubleDots++;
1888
- }
1889
- else if (urlPart != '') {
1890
- res.push(urlPart);
1891
- }
1892
- });
1893
- return res;
1894
- }
1895
- return [...res, cmd];
1896
- }, []);
1897
- return new Navigation(isAbsolute, numberOfDoubleDots, res);
1898
- }
1899
- class Position {
1900
- constructor(segmentGroup, processChildren, index) {
1901
- this.segmentGroup = segmentGroup;
1902
- this.processChildren = processChildren;
1903
- this.index = index;
1904
1978
  }
1979
+ return flattenInherited(pathFromRoot.slice(inheritingStartingFrom));
1905
1980
  }
1906
- function findStartingPosition(nav, tree, segmentGroup, lastPathIndex) {
1907
- if (nav.isAbsolute) {
1908
- return new Position(tree.root, true, 0);
1909
- }
1910
- if (lastPathIndex === -1) {
1911
- // Pathless ActivatedRoute has _lastPathIndex === -1 but should not process children
1912
- // see issue #26224, #13011, #35687
1913
- // However, if the ActivatedRoute is the root we should process children like above.
1914
- const processChildren = segmentGroup === tree.root;
1915
- return new Position(segmentGroup, processChildren, 0);
1916
- }
1917
- const modifier = isMatrixParams(nav.commands[0]) ? 0 : 1;
1918
- const index = lastPathIndex + modifier;
1919
- return createPositionApplyingDoubleDots(segmentGroup, index, nav.numberOfDoubleDots);
1981
+ /** @internal */
1982
+ function flattenInherited(pathFromRoot) {
1983
+ return pathFromRoot.reduce((res, curr) => {
1984
+ var _a;
1985
+ const params = Object.assign(Object.assign({}, res.params), curr.params);
1986
+ const data = Object.assign(Object.assign({}, res.data), curr.data);
1987
+ 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);
1988
+ return { params, data, resolve };
1989
+ }, { params: {}, data: {}, resolve: {} });
1920
1990
  }
1921
- function createPositionApplyingDoubleDots(group, index, numberOfDoubleDots) {
1922
- let g = group;
1923
- let ci = index;
1924
- let dd = numberOfDoubleDots;
1925
- while (dd > ci) {
1926
- dd -= ci;
1927
- g = g.parent;
1928
- if (!g) {
1929
- throw new Error('Invalid number of \'../\'');
1930
- }
1931
- ci = g.segments.length;
1991
+ /**
1992
+ * @description
1993
+ *
1994
+ * Contains the information about a route associated with a component loaded in an
1995
+ * outlet at a particular moment in time. ActivatedRouteSnapshot can also be used to
1996
+ * traverse the router state tree.
1997
+ *
1998
+ * The following example initializes a component with route information extracted
1999
+ * from the snapshot of the root node at the time of creation.
2000
+ *
2001
+ * ```
2002
+ * @Component({templateUrl:'./my-component.html'})
2003
+ * class MyComponent {
2004
+ * constructor(route: ActivatedRoute) {
2005
+ * const id: string = route.snapshot.params.id;
2006
+ * const url: string = route.snapshot.url.join('');
2007
+ * const user = route.snapshot.data.user;
2008
+ * }
2009
+ * }
2010
+ * ```
2011
+ *
2012
+ * @publicApi
2013
+ */
2014
+ class ActivatedRouteSnapshot {
2015
+ /** @internal */
2016
+ constructor(
2017
+ /** The URL segments matched by this route */
2018
+ url,
2019
+ /**
2020
+ * The matrix parameters scoped to this route.
2021
+ *
2022
+ * You can compute all params (or data) in the router state or to get params outside
2023
+ * of an activated component by traversing the `RouterState` tree as in the following
2024
+ * example:
2025
+ * ```
2026
+ * collectRouteParams(router: Router) {
2027
+ * let params = {};
2028
+ * let stack: ActivatedRouteSnapshot[] = [router.routerState.snapshot.root];
2029
+ * while (stack.length > 0) {
2030
+ * const route = stack.pop()!;
2031
+ * params = {...params, ...route.params};
2032
+ * stack.push(...route.children);
2033
+ * }
2034
+ * return params;
2035
+ * }
2036
+ * ```
2037
+ */
2038
+ params,
2039
+ /** The query parameters shared by all the routes */
2040
+ queryParams,
2041
+ /** The URL fragment shared by all the routes */
2042
+ fragment,
2043
+ /** The static and resolved data of this route */
2044
+ data,
2045
+ /** The outlet name of the route */
2046
+ outlet,
2047
+ /** The component of the route */
2048
+ component, routeConfig, urlSegment, lastPathIndex, resolve, correctedLastPathIndex) {
2049
+ this.url = url;
2050
+ this.params = params;
2051
+ this.queryParams = queryParams;
2052
+ this.fragment = fragment;
2053
+ this.data = data;
2054
+ this.outlet = outlet;
2055
+ this.component = component;
2056
+ this.routeConfig = routeConfig;
2057
+ this._urlSegment = urlSegment;
2058
+ this._lastPathIndex = lastPathIndex;
2059
+ this._correctedLastPathIndex = correctedLastPathIndex !== null && correctedLastPathIndex !== void 0 ? correctedLastPathIndex : lastPathIndex;
2060
+ this._resolve = resolve;
1932
2061
  }
1933
- return new Position(g, false, ci - dd);
1934
- }
1935
- function getOutlets(commands) {
1936
- if (isCommandWithOutlets(commands[0])) {
1937
- return commands[0].outlets;
2062
+ /** The root of the router state */
2063
+ get root() {
2064
+ return this._routerState.root;
1938
2065
  }
1939
- return { [PRIMARY_OUTLET]: commands };
1940
- }
1941
- function updateSegmentGroup(segmentGroup, startIndex, commands) {
1942
- if (!segmentGroup) {
1943
- segmentGroup = new UrlSegmentGroup([], {});
2066
+ /** The parent of this route in the router state tree */
2067
+ get parent() {
2068
+ return this._routerState.parent(this);
1944
2069
  }
1945
- if (segmentGroup.segments.length === 0 && segmentGroup.hasChildren()) {
1946
- return updateSegmentGroupChildren(segmentGroup, startIndex, commands);
2070
+ /** The first child of this route in the router state tree */
2071
+ get firstChild() {
2072
+ return this._routerState.firstChild(this);
1947
2073
  }
1948
- const m = prefixedWith(segmentGroup, startIndex, commands);
1949
- const slicedCommands = commands.slice(m.commandIndex);
1950
- if (m.match && m.pathIndex < segmentGroup.segments.length) {
1951
- const g = new UrlSegmentGroup(segmentGroup.segments.slice(0, m.pathIndex), {});
1952
- g.children[PRIMARY_OUTLET] =
1953
- new UrlSegmentGroup(segmentGroup.segments.slice(m.pathIndex), segmentGroup.children);
1954
- return updateSegmentGroupChildren(g, 0, slicedCommands);
2074
+ /** The children of this route in the router state tree */
2075
+ get children() {
2076
+ return this._routerState.children(this);
1955
2077
  }
1956
- else if (m.match && slicedCommands.length === 0) {
1957
- return new UrlSegmentGroup(segmentGroup.segments, {});
2078
+ /** The path from the root of the router state tree to this route */
2079
+ get pathFromRoot() {
2080
+ return this._routerState.pathFromRoot(this);
1958
2081
  }
1959
- else if (m.match && !segmentGroup.hasChildren()) {
1960
- return createNewSegmentGroup(segmentGroup, startIndex, commands);
2082
+ get paramMap() {
2083
+ if (!this._paramMap) {
2084
+ this._paramMap = convertToParamMap(this.params);
2085
+ }
2086
+ return this._paramMap;
1961
2087
  }
1962
- else if (m.match) {
1963
- return updateSegmentGroupChildren(segmentGroup, 0, slicedCommands);
2088
+ get queryParamMap() {
2089
+ if (!this._queryParamMap) {
2090
+ this._queryParamMap = convertToParamMap(this.queryParams);
2091
+ }
2092
+ return this._queryParamMap;
1964
2093
  }
1965
- else {
1966
- return createNewSegmentGroup(segmentGroup, startIndex, commands);
2094
+ toString() {
2095
+ const url = this.url.map(segment => segment.toString()).join('/');
2096
+ const matched = this.routeConfig ? this.routeConfig.path : '';
2097
+ return `Route(url:'${url}', path:'${matched}')`;
1967
2098
  }
1968
2099
  }
1969
- function updateSegmentGroupChildren(segmentGroup, startIndex, commands) {
1970
- if (commands.length === 0) {
1971
- return new UrlSegmentGroup(segmentGroup.segments, {});
2100
+ /**
2101
+ * @description
2102
+ *
2103
+ * Represents the state of the router at a moment in time.
2104
+ *
2105
+ * This is a tree of activated route snapshots. Every node in this tree knows about
2106
+ * the "consumed" URL segments, the extracted parameters, and the resolved data.
2107
+ *
2108
+ * The following example shows how a component is initialized with information
2109
+ * from the snapshot of the root node's state at the time of creation.
2110
+ *
2111
+ * ```
2112
+ * @Component({templateUrl:'template.html'})
2113
+ * class MyComponent {
2114
+ * constructor(router: Router) {
2115
+ * const state: RouterState = router.routerState;
2116
+ * const snapshot: RouterStateSnapshot = state.snapshot;
2117
+ * const root: ActivatedRouteSnapshot = snapshot.root;
2118
+ * const child = root.firstChild;
2119
+ * const id: Observable<string> = child.params.map(p => p.id);
2120
+ * //...
2121
+ * }
2122
+ * }
2123
+ * ```
2124
+ *
2125
+ * @publicApi
2126
+ */
2127
+ class RouterStateSnapshot extends Tree {
2128
+ /** @internal */
2129
+ constructor(
2130
+ /** The url from which this snapshot was created */
2131
+ url, root) {
2132
+ super(root);
2133
+ this.url = url;
2134
+ setRouterState(this, root);
1972
2135
  }
1973
- else {
1974
- const outlets = getOutlets(commands);
1975
- const children = {};
1976
- forEach(outlets, (commands, outlet) => {
1977
- if (typeof commands === 'string') {
1978
- commands = [commands];
1979
- }
1980
- if (commands !== null) {
1981
- children[outlet] = updateSegmentGroup(segmentGroup.children[outlet], startIndex, commands);
1982
- }
1983
- });
1984
- forEach(segmentGroup.children, (child, childOutlet) => {
1985
- if (outlets[childOutlet] === undefined) {
1986
- children[childOutlet] = child;
1987
- }
1988
- });
1989
- return new UrlSegmentGroup(segmentGroup.segments, children);
2136
+ toString() {
2137
+ return serializeNode(this._root);
1990
2138
  }
1991
2139
  }
1992
- function prefixedWith(segmentGroup, startIndex, commands) {
1993
- let currentCommandIndex = 0;
1994
- let currentPathIndex = startIndex;
1995
- const noMatch = { match: false, pathIndex: 0, commandIndex: 0 };
1996
- while (currentPathIndex < segmentGroup.segments.length) {
1997
- if (currentCommandIndex >= commands.length)
1998
- return noMatch;
1999
- const path = segmentGroup.segments[currentPathIndex];
2000
- const command = commands[currentCommandIndex];
2001
- // Do not try to consume command as part of the prefixing if it has outlets because it can
2002
- // contain outlets other than the one being processed. Consuming the outlets command would
2003
- // result in other outlets being ignored.
2004
- if (isCommandWithOutlets(command)) {
2005
- break;
2006
- }
2007
- const curr = `${command}`;
2008
- const next = currentCommandIndex < commands.length - 1 ? commands[currentCommandIndex + 1] : null;
2009
- if (currentPathIndex > 0 && curr === undefined)
2010
- break;
2011
- if (curr && next && (typeof next === 'object') && next.outlets === undefined) {
2012
- if (!compare(curr, next, path))
2013
- return noMatch;
2014
- currentCommandIndex += 2;
2015
- }
2016
- else {
2017
- if (!compare(curr, {}, path))
2018
- return noMatch;
2019
- currentCommandIndex++;
2020
- }
2021
- currentPathIndex++;
2022
- }
2023
- return { match: true, pathIndex: currentPathIndex, commandIndex: currentCommandIndex };
2140
+ function setRouterState(state, node) {
2141
+ node.value._routerState = state;
2142
+ node.children.forEach(c => setRouterState(state, c));
2024
2143
  }
2025
- function createNewSegmentGroup(segmentGroup, startIndex, commands) {
2026
- const paths = segmentGroup.segments.slice(0, startIndex);
2027
- let i = 0;
2028
- while (i < commands.length) {
2029
- const command = commands[i];
2030
- if (isCommandWithOutlets(command)) {
2031
- const children = createNewSegmentChildren(command.outlets);
2032
- return new UrlSegmentGroup(paths, children);
2144
+ function serializeNode(node) {
2145
+ const c = node.children.length > 0 ? ` { ${node.children.map(serializeNode).join(', ')} } ` : '';
2146
+ return `${node.value}${c}`;
2147
+ }
2148
+ /**
2149
+ * The expectation is that the activate route is created with the right set of parameters.
2150
+ * So we push new values into the observables only when they are not the initial values.
2151
+ * And we detect that by checking if the snapshot field is set.
2152
+ */
2153
+ function advanceActivatedRoute(route) {
2154
+ if (route.snapshot) {
2155
+ const currentSnapshot = route.snapshot;
2156
+ const nextSnapshot = route._futureSnapshot;
2157
+ route.snapshot = nextSnapshot;
2158
+ if (!shallowEqual(currentSnapshot.queryParams, nextSnapshot.queryParams)) {
2159
+ route.queryParams.next(nextSnapshot.queryParams);
2033
2160
  }
2034
- // if we start with an object literal, we need to reuse the path part from the segment
2035
- if (i === 0 && isMatrixParams(commands[0])) {
2036
- const p = segmentGroup.segments[startIndex];
2037
- paths.push(new UrlSegment(p.path, stringify(commands[0])));
2038
- i++;
2039
- continue;
2161
+ if (currentSnapshot.fragment !== nextSnapshot.fragment) {
2162
+ route.fragment.next(nextSnapshot.fragment);
2040
2163
  }
2041
- const curr = isCommandWithOutlets(command) ? command.outlets[PRIMARY_OUTLET] : `${command}`;
2042
- const next = (i < commands.length - 1) ? commands[i + 1] : null;
2043
- if (curr && next && isMatrixParams(next)) {
2044
- paths.push(new UrlSegment(curr, stringify(next)));
2045
- i += 2;
2164
+ if (!shallowEqual(currentSnapshot.params, nextSnapshot.params)) {
2165
+ route.params.next(nextSnapshot.params);
2046
2166
  }
2047
- else {
2048
- paths.push(new UrlSegment(curr, {}));
2049
- i++;
2167
+ if (!shallowEqualArrays(currentSnapshot.url, nextSnapshot.url)) {
2168
+ route.url.next(nextSnapshot.url);
2169
+ }
2170
+ if (!shallowEqual(currentSnapshot.data, nextSnapshot.data)) {
2171
+ route.data.next(nextSnapshot.data);
2050
2172
  }
2051
2173
  }
2052
- return new UrlSegmentGroup(paths, {});
2174
+ else {
2175
+ route.snapshot = route._futureSnapshot;
2176
+ // this is for resolved data
2177
+ route.data.next(route._futureSnapshot.data);
2178
+ }
2053
2179
  }
2054
- function createNewSegmentChildren(outlets) {
2055
- const children = {};
2056
- forEach(outlets, (commands, outlet) => {
2057
- if (typeof commands === 'string') {
2058
- commands = [commands];
2180
+ function equalParamsAndUrlSegments(a, b) {
2181
+ const equalUrlParams = shallowEqual(a.params, b.params) && equalSegments(a.url, b.url);
2182
+ const parentsMismatch = !a.parent !== !b.parent;
2183
+ return equalUrlParams && !parentsMismatch &&
2184
+ (!a.parent || equalParamsAndUrlSegments(a.parent, b.parent));
2185
+ }
2186
+
2187
+ /**
2188
+ * @license
2189
+ * Copyright Google LLC All Rights Reserved.
2190
+ *
2191
+ * Use of this source code is governed by an MIT-style license that can be
2192
+ * found in the LICENSE file at https://angular.io/license
2193
+ */
2194
+ function createRouterState(routeReuseStrategy, curr, prevState) {
2195
+ const root = createNode(routeReuseStrategy, curr._root, prevState ? prevState._root : undefined);
2196
+ return new RouterState(root, curr);
2197
+ }
2198
+ function createNode(routeReuseStrategy, curr, prevState) {
2199
+ // reuse an activated route that is currently displayed on the screen
2200
+ if (prevState && routeReuseStrategy.shouldReuseRoute(curr.value, prevState.value.snapshot)) {
2201
+ const value = prevState.value;
2202
+ value._futureSnapshot = curr.value;
2203
+ const children = createOrReuseChildren(routeReuseStrategy, curr, prevState);
2204
+ return new TreeNode(value, children);
2205
+ }
2206
+ else {
2207
+ if (routeReuseStrategy.shouldAttach(curr.value)) {
2208
+ // retrieve an activated route that is used to be displayed, but is not currently displayed
2209
+ const detachedRouteHandle = routeReuseStrategy.retrieve(curr.value);
2210
+ if (detachedRouteHandle !== null) {
2211
+ const tree = detachedRouteHandle.route;
2212
+ tree.value._futureSnapshot = curr.value;
2213
+ tree.children = curr.children.map(c => createNode(routeReuseStrategy, c));
2214
+ return tree;
2215
+ }
2059
2216
  }
2060
- if (commands !== null) {
2061
- children[outlet] = createNewSegmentGroup(new UrlSegmentGroup([], {}), 0, commands);
2217
+ const value = createActivatedRoute(curr.value);
2218
+ const children = curr.children.map(c => createNode(routeReuseStrategy, c));
2219
+ return new TreeNode(value, children);
2220
+ }
2221
+ }
2222
+ function createOrReuseChildren(routeReuseStrategy, curr, prevState) {
2223
+ return curr.children.map(child => {
2224
+ for (const p of prevState.children) {
2225
+ if (routeReuseStrategy.shouldReuseRoute(child.value, p.value.snapshot)) {
2226
+ return createNode(routeReuseStrategy, child, p);
2227
+ }
2062
2228
  }
2229
+ return createNode(routeReuseStrategy, child);
2063
2230
  });
2064
- return children;
2065
- }
2066
- function stringify(params) {
2067
- const res = {};
2068
- forEach(params, (v, k) => res[k] = `${v}`);
2069
- return res;
2070
2231
  }
2071
- function compare(path, params, segment) {
2072
- return path == segment.path && shallowEqual(params, segment.parameters);
2232
+ function createActivatedRoute(c) {
2233
+ 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);
2073
2234
  }
2074
2235
 
2075
2236
  /**
@@ -2332,9 +2493,9 @@ class RouterOutlet {
2332
2493
  this.activateEvents.emit(this.activated.instance);
2333
2494
  }
2334
2495
  }
2335
- RouterOutlet.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterOutlet, deps: [{ token: ChildrenOutletContexts }, { token: i0.ViewContainerRef }, { token: 'name', attribute: true }, { token: i0.ChangeDetectorRef }, { token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Directive });
2336
- RouterOutlet.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: RouterOutlet, selector: "router-outlet", outputs: { activateEvents: "activate", deactivateEvents: "deactivate", attachEvents: "attach", detachEvents: "detach" }, exportAs: ["outlet"], ngImport: i0 });
2337
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterOutlet, decorators: [{
2496
+ RouterOutlet.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterOutlet, deps: [{ token: ChildrenOutletContexts }, { token: i0.ViewContainerRef }, { token: 'name', attribute: true }, { token: i0.ChangeDetectorRef }, { token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Directive });
2497
+ RouterOutlet.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.1.0-next.0", type: RouterOutlet, selector: "router-outlet", outputs: { activateEvents: "activate", deactivateEvents: "deactivate", attachEvents: "attach", detachEvents: "detach" }, exportAs: ["outlet"], ngImport: i0 });
2498
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterOutlet, decorators: [{
2338
2499
  type: Directive,
2339
2500
  args: [{ selector: 'router-outlet', exportAs: 'outlet' }]
2340
2501
  }], ctorParameters: function () {
@@ -2393,9 +2554,9 @@ function isComponentFactoryResolver(item) {
2393
2554
  */
2394
2555
  class ɵEmptyOutletComponent {
2395
2556
  }
2396
- ɵEmptyOutletComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: ɵEmptyOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2397
- ɵEmptyOutletComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.0.1", type: ɵEmptyOutletComponent, selector: "ng-component", ngImport: i0, template: `<router-outlet></router-outlet>`, isInline: true, dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }] });
2398
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: ɵEmptyOutletComponent, decorators: [{
2557
+ ɵEmptyOutletComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: ɵEmptyOutletComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2558
+ ɵEmptyOutletComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "14.1.0-next.0", type: ɵEmptyOutletComponent, selector: "ng-component", ngImport: i0, template: `<router-outlet></router-outlet>`, isInline: true, dependencies: [{ kind: "directive", type: RouterOutlet, selector: "router-outlet", outputs: ["activate", "deactivate", "attach", "detach"], exportAs: ["outlet"] }] });
2559
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: ɵEmptyOutletComponent, decorators: [{
2399
2560
  type: Component,
2400
2561
  args: [{ template: `<router-outlet></router-outlet>` }]
2401
2562
  }] });
@@ -3082,9 +3243,7 @@ class ApplyRedirects {
3082
3243
  return new Error(`Cannot match any routes. URL Segment: '${e.segmentGroup}'`);
3083
3244
  }
3084
3245
  createUrlTree(rootCandidate, queryParams, fragment) {
3085
- const root = rootCandidate.segments.length > 0 ?
3086
- new UrlSegmentGroup([], { [PRIMARY_OUTLET]: rootCandidate }) :
3087
- rootCandidate;
3246
+ const root = createRoot(rootCandidate);
3088
3247
  return new UrlTree(root, queryParams, fragment);
3089
3248
  }
3090
3249
  expandSegmentGroup(injector, routes, segmentGroup, outlet) {
@@ -3337,39 +3496,6 @@ class ApplyRedirects {
3337
3496
  return redirectToUrlSegment;
3338
3497
  }
3339
3498
  }
3340
- /**
3341
- * When possible, merges the primary outlet child into the parent `UrlSegmentGroup`.
3342
- *
3343
- * When a segment group has only one child which is a primary outlet, merges that child into the
3344
- * parent. That is, the child segment group's segments are merged into the `s` and the child's
3345
- * children become the children of `s`. Think of this like a 'squash', merging the child segment
3346
- * group into the parent.
3347
- */
3348
- function mergeTrivialChildren(s) {
3349
- if (s.numberOfChildren === 1 && s.children[PRIMARY_OUTLET]) {
3350
- const c = s.children[PRIMARY_OUTLET];
3351
- return new UrlSegmentGroup(s.segments.concat(c.segments), c.children);
3352
- }
3353
- return s;
3354
- }
3355
- /**
3356
- * Recursively merges primary segment children into their parents and also drops empty children
3357
- * (those which have no segments and no children themselves). The latter prevents serializing a
3358
- * group into something like `/a(aux:)`, where `aux` is an empty child segment.
3359
- */
3360
- function squashSegmentGroup(segmentGroup) {
3361
- const newChildren = {};
3362
- for (const childOutlet of Object.keys(segmentGroup.children)) {
3363
- const child = segmentGroup.children[childOutlet];
3364
- const childCandidate = squashSegmentGroup(child);
3365
- // don't add empty children
3366
- if (childCandidate.segments.length > 0 || childCandidate.hasChildren()) {
3367
- newChildren[childOutlet] = childCandidate;
3368
- }
3369
- }
3370
- const s = new UrlSegmentGroup(segmentGroup.segments, newChildren);
3371
- return mergeTrivialChildren(s);
3372
- }
3373
3499
 
3374
3500
  /**
3375
3501
  * @license
@@ -4200,9 +4326,9 @@ class RouterConfigLoader {
4200
4326
  }));
4201
4327
  }
4202
4328
  }
4203
- RouterConfigLoader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterConfigLoader, deps: [{ token: i0.Injector }, { token: i0.Compiler }], target: i0.ɵɵFactoryTarget.Injectable });
4204
- RouterConfigLoader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterConfigLoader });
4205
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterConfigLoader, decorators: [{
4329
+ RouterConfigLoader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterConfigLoader, deps: [{ token: i0.Injector }, { token: i0.Compiler }], target: i0.ɵɵFactoryTarget.Injectable });
4330
+ RouterConfigLoader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterConfigLoader });
4331
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterConfigLoader, decorators: [{
4206
4332
  type: Injectable
4207
4333
  }], ctorParameters: function () { return [{ type: i0.Injector }, { type: i0.Compiler }]; } });
4208
4334
 
@@ -5195,9 +5321,9 @@ class Router {
5195
5321
  return { navigationId };
5196
5322
  }
5197
5323
  }
5198
- Router.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: Router, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
5199
- Router.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: Router });
5200
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: Router, decorators: [{
5324
+ Router.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: Router, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
5325
+ Router.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: Router });
5326
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: Router, decorators: [{
5201
5327
  type: Injectable
5202
5328
  }], ctorParameters: function () { return [{ type: i0.Type }, { type: UrlSerializer }, { type: ChildrenOutletContexts }, { type: i3.Location }, { type: i0.Injector }, { type: i0.Compiler }, { type: undefined }]; } });
5203
5329
  function validateCommands(commands) {
@@ -5389,9 +5515,9 @@ class RouterLink {
5389
5515
  });
5390
5516
  }
5391
5517
  }
5392
- RouterLink.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterLink, deps: [{ token: Router }, { token: ActivatedRoute }, { token: 'tabindex', attribute: true }, { token: i0.Renderer2 }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
5393
- RouterLink.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: RouterLink, selector: ":not(a):not(area)[routerLink]", inputs: { queryParams: "queryParams", fragment: "fragment", queryParamsHandling: "queryParamsHandling", preserveFragment: "preserveFragment", skipLocationChange: "skipLocationChange", replaceUrl: "replaceUrl", state: "state", relativeTo: "relativeTo", routerLink: "routerLink" }, host: { listeners: { "click": "onClick()" } }, usesOnChanges: true, ngImport: i0 });
5394
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterLink, decorators: [{
5518
+ RouterLink.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLink, deps: [{ token: Router }, { token: ActivatedRoute }, { token: 'tabindex', attribute: true }, { token: i0.Renderer2 }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Directive });
5519
+ RouterLink.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.1.0-next.0", type: RouterLink, selector: ":not(a):not(area)[routerLink]", inputs: { queryParams: "queryParams", fragment: "fragment", queryParamsHandling: "queryParamsHandling", preserveFragment: "preserveFragment", skipLocationChange: "skipLocationChange", replaceUrl: "replaceUrl", state: "state", relativeTo: "relativeTo", routerLink: "routerLink" }, host: { listeners: { "click": "onClick()" } }, usesOnChanges: true, ngImport: i0 });
5520
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLink, decorators: [{
5395
5521
  type: Directive,
5396
5522
  args: [{ selector: ':not(a):not(area)[routerLink]' }]
5397
5523
  }], ctorParameters: function () {
@@ -5510,9 +5636,9 @@ class RouterLinkWithHref {
5510
5636
  });
5511
5637
  }
5512
5638
  }
5513
- RouterLinkWithHref.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterLinkWithHref, deps: [{ token: Router }, { token: ActivatedRoute }, { token: i3.LocationStrategy }], target: i0.ɵɵFactoryTarget.Directive });
5514
- RouterLinkWithHref.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: RouterLinkWithHref, selector: "a[routerLink],area[routerLink]", inputs: { target: "target", queryParams: "queryParams", fragment: "fragment", queryParamsHandling: "queryParamsHandling", preserveFragment: "preserveFragment", skipLocationChange: "skipLocationChange", replaceUrl: "replaceUrl", state: "state", relativeTo: "relativeTo", routerLink: "routerLink" }, host: { listeners: { "click": "onClick($event.button,$event.ctrlKey,$event.shiftKey,$event.altKey,$event.metaKey)" }, properties: { "attr.target": "this.target", "attr.href": "this.href" } }, usesOnChanges: true, ngImport: i0 });
5515
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterLinkWithHref, decorators: [{
5639
+ RouterLinkWithHref.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLinkWithHref, deps: [{ token: Router }, { token: ActivatedRoute }, { token: i3.LocationStrategy }], target: i0.ɵɵFactoryTarget.Directive });
5640
+ RouterLinkWithHref.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.1.0-next.0", type: RouterLinkWithHref, selector: "a[routerLink],area[routerLink]", inputs: { target: "target", queryParams: "queryParams", fragment: "fragment", queryParamsHandling: "queryParamsHandling", preserveFragment: "preserveFragment", skipLocationChange: "skipLocationChange", replaceUrl: "replaceUrl", state: "state", relativeTo: "relativeTo", routerLink: "routerLink" }, host: { listeners: { "click": "onClick($event.button,$event.ctrlKey,$event.shiftKey,$event.altKey,$event.metaKey)" }, properties: { "attr.target": "this.target", "attr.href": "this.href" } }, usesOnChanges: true, ngImport: i0 });
5641
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLinkWithHref, decorators: [{
5516
5642
  type: Directive,
5517
5643
  args: [{ selector: 'a[routerLink],area[routerLink]' }]
5518
5644
  }], ctorParameters: function () { return [{ type: Router }, { type: ActivatedRoute }, { type: i3.LocationStrategy }]; }, propDecorators: { target: [{
@@ -5739,9 +5865,9 @@ class RouterLinkActive {
5739
5865
  this.links.some(isActiveCheckFn) || this.linksWithHrefs.some(isActiveCheckFn);
5740
5866
  }
5741
5867
  }
5742
- RouterLinkActive.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterLinkActive, deps: [{ token: Router }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }, { token: RouterLink, optional: true }, { token: RouterLinkWithHref, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
5743
- RouterLinkActive.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.0.1", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: { routerLinkActiveOptions: "routerLinkActiveOptions", ariaCurrentWhenActive: "ariaCurrentWhenActive", routerLinkActive: "routerLinkActive" }, outputs: { isActiveChange: "isActiveChange" }, queries: [{ propertyName: "links", predicate: RouterLink, descendants: true }, { propertyName: "linksWithHrefs", predicate: RouterLinkWithHref, descendants: true }], exportAs: ["routerLinkActive"], usesOnChanges: true, ngImport: i0 });
5744
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterLinkActive, decorators: [{
5868
+ RouterLinkActive.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLinkActive, deps: [{ token: Router }, { token: i0.ElementRef }, { token: i0.Renderer2 }, { token: i0.ChangeDetectorRef }, { token: RouterLink, optional: true }, { token: RouterLinkWithHref, optional: true }], target: i0.ɵɵFactoryTarget.Directive });
5869
+ RouterLinkActive.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.1.0-next.0", type: RouterLinkActive, selector: "[routerLinkActive]", inputs: { routerLinkActiveOptions: "routerLinkActiveOptions", ariaCurrentWhenActive: "ariaCurrentWhenActive", routerLinkActive: "routerLinkActive" }, outputs: { isActiveChange: "isActiveChange" }, queries: [{ propertyName: "links", predicate: RouterLink, descendants: true }, { propertyName: "linksWithHrefs", predicate: RouterLinkWithHref, descendants: true }], exportAs: ["routerLinkActive"], usesOnChanges: true, ngImport: i0 });
5870
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterLinkActive, decorators: [{
5745
5871
  type: Directive,
5746
5872
  args: [{
5747
5873
  selector: '[routerLinkActive]',
@@ -5847,9 +5973,9 @@ class DefaultTitleStrategy extends TitleStrategy {
5847
5973
  }
5848
5974
  }
5849
5975
  }
5850
- DefaultTitleStrategy.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: DefaultTitleStrategy, deps: [{ token: i1.Title }], target: i0.ɵɵFactoryTarget.Injectable });
5851
- DefaultTitleStrategy.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: DefaultTitleStrategy, providedIn: 'root' });
5852
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: DefaultTitleStrategy, decorators: [{
5976
+ DefaultTitleStrategy.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: DefaultTitleStrategy, deps: [{ token: i1.Title }], target: i0.ɵɵFactoryTarget.Injectable });
5977
+ DefaultTitleStrategy.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: DefaultTitleStrategy, providedIn: 'root' });
5978
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: DefaultTitleStrategy, decorators: [{
5853
5979
  type: Injectable,
5854
5980
  args: [{ providedIn: 'root' }]
5855
5981
  }], ctorParameters: function () { return [{ type: i1.Title }]; } });
@@ -5984,9 +6110,9 @@ class RouterPreloader {
5984
6110
  });
5985
6111
  }
5986
6112
  }
5987
- RouterPreloader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterPreloader, deps: [{ token: Router }, { token: i0.Compiler }, { token: i0.EnvironmentInjector }, { token: PreloadingStrategy }, { token: RouterConfigLoader }], target: i0.ɵɵFactoryTarget.Injectable });
5988
- RouterPreloader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterPreloader });
5989
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterPreloader, decorators: [{
6113
+ RouterPreloader.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterPreloader, deps: [{ token: Router }, { token: i0.Compiler }, { token: i0.EnvironmentInjector }, { token: PreloadingStrategy }, { token: RouterConfigLoader }], target: i0.ɵɵFactoryTarget.Injectable });
6114
+ RouterPreloader.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterPreloader });
6115
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterPreloader, decorators: [{
5990
6116
  type: Injectable
5991
6117
  }], ctorParameters: function () { return [{ type: Router }, { type: i0.Compiler }, { type: i0.EnvironmentInjector }, { type: PreloadingStrategy }, { type: RouterConfigLoader }]; } });
5992
6118
 
@@ -6065,9 +6191,9 @@ class RouterScroller {
6065
6191
  }
6066
6192
  }
6067
6193
  }
6068
- RouterScroller.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterScroller, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
6069
- RouterScroller.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterScroller });
6070
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterScroller, decorators: [{
6194
+ RouterScroller.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterScroller, deps: "invalid", target: i0.ɵɵFactoryTarget.Injectable });
6195
+ RouterScroller.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterScroller });
6196
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterScroller, decorators: [{
6071
6197
  type: Injectable
6072
6198
  }], ctorParameters: function () { return [{ type: Router }, { type: i3.ViewportScroller }, { type: undefined }]; } });
6073
6199
 
@@ -6209,10 +6335,10 @@ class RouterModule {
6209
6335
  return { ngModule: RouterModule, providers: [provideRoutes(routes)] };
6210
6336
  }
6211
6337
  }
6212
- RouterModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterModule, deps: [{ token: ROUTER_FORROOT_GUARD, optional: true }, { token: Router, optional: true }], target: i0.ɵɵFactoryTarget.NgModule });
6213
- RouterModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.0.1", ngImport: i0, type: RouterModule, declarations: [RouterOutlet, RouterLink, RouterLinkWithHref, RouterLinkActive, ɵEmptyOutletComponent], exports: [RouterOutlet, RouterLink, RouterLinkWithHref, RouterLinkActive, ɵEmptyOutletComponent] });
6214
- RouterModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterModule });
6215
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterModule, decorators: [{
6338
+ RouterModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterModule, deps: [{ token: ROUTER_FORROOT_GUARD, optional: true }, { token: Router, optional: true }], target: i0.ɵɵFactoryTarget.NgModule });
6339
+ RouterModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterModule, declarations: [RouterOutlet, RouterLink, RouterLinkWithHref, RouterLinkActive, ɵEmptyOutletComponent], exports: [RouterOutlet, RouterLink, RouterLinkWithHref, RouterLinkActive, ɵEmptyOutletComponent] });
6340
+ RouterModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterModule });
6341
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterModule, decorators: [{
6216
6342
  type: NgModule,
6217
6343
  args: [{
6218
6344
  declarations: ROUTER_DIRECTIVES,
@@ -6392,9 +6518,9 @@ class RouterInitializer {
6392
6518
  this.destroyed = true;
6393
6519
  }
6394
6520
  }
6395
- RouterInitializer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterInitializer, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
6396
- RouterInitializer.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterInitializer });
6397
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.0.1", ngImport: i0, type: RouterInitializer, decorators: [{
6521
+ RouterInitializer.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterInitializer, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable });
6522
+ RouterInitializer.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterInitializer });
6523
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.1.0-next.0", ngImport: i0, type: RouterInitializer, decorators: [{
6398
6524
  type: Injectable
6399
6525
  }], ctorParameters: function () { return [{ type: i0.Injector }]; } });
6400
6526
  function getAppInitializer(r) {
@@ -6434,7 +6560,7 @@ function provideRouterInitializer() {
6434
6560
  /**
6435
6561
  * @publicApi
6436
6562
  */
6437
- const VERSION = new Version('14.0.1');
6563
+ const VERSION = new Version('14.1.0-next.0');
6438
6564
 
6439
6565
  /**
6440
6566
  * @license
@@ -6473,5 +6599,5 @@ const VERSION = new Version('14.0.1');
6473
6599
  * Generated bundle index. Do not edit.
6474
6600
  */
6475
6601
 
6476
- export { ActivatedRoute, ActivatedRouteSnapshot, ActivationEnd, ActivationStart, BaseRouteReuseStrategy, ChildActivationEnd, ChildActivationStart, ChildrenOutletContexts, DefaultTitleStrategy, DefaultUrlSerializer, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, NoPreloading, OutletContext, PRIMARY_OUTLET, PreloadAllModules, PreloadingStrategy, ROUTER_CONFIGURATION, ROUTER_INITIALIZER, ROUTES, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RouteReuseStrategy, Router, RouterEvent, RouterLink, RouterLinkActive, RouterLinkWithHref, RouterModule, RouterOutlet, RouterPreloader, RouterState, RouterStateSnapshot, RoutesRecognized, Scroll, TitleStrategy, UrlHandlingStrategy, UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree, VERSION, convertToParamMap, provideRoutes, ɵEmptyOutletComponent, ROUTER_PROVIDERS as ɵROUTER_PROVIDERS, assignExtraOptionsToRouter as ɵassignExtraOptionsToRouter, flatten as ɵflatten };
6602
+ export { ActivatedRoute, ActivatedRouteSnapshot, ActivationEnd, ActivationStart, BaseRouteReuseStrategy, ChildActivationEnd, ChildActivationStart, ChildrenOutletContexts, DefaultTitleStrategy, DefaultUrlSerializer, GuardsCheckEnd, GuardsCheckStart, NavigationCancel, NavigationEnd, NavigationError, NavigationStart, NoPreloading, OutletContext, PRIMARY_OUTLET, PreloadAllModules, PreloadingStrategy, ROUTER_CONFIGURATION, ROUTER_INITIALIZER, ROUTES, ResolveEnd, ResolveStart, RouteConfigLoadEnd, RouteConfigLoadStart, RouteReuseStrategy, Router, RouterEvent, RouterLink, RouterLinkActive, RouterLinkWithHref, RouterModule, RouterOutlet, RouterPreloader, RouterState, RouterStateSnapshot, RoutesRecognized, Scroll, TitleStrategy, UrlHandlingStrategy, UrlSegment, UrlSegmentGroup, UrlSerializer, UrlTree, VERSION, convertToParamMap, createUrlTreeFromSnapshot, provideRoutes, ɵEmptyOutletComponent, ROUTER_PROVIDERS as ɵROUTER_PROVIDERS, assignExtraOptionsToRouter as ɵassignExtraOptionsToRouter, flatten as ɵflatten };
6477
6603
  //# sourceMappingURL=router.mjs.map