@angular/router 11.0.5 → 11.0.9
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.
- package/bundles/router-testing.umd.js +1 -1
- package/bundles/router-testing.umd.min.js +1 -1
- package/bundles/router-testing.umd.min.js.map +1 -1
- package/bundles/router-upgrade.umd.js +1 -1
- package/bundles/router-upgrade.umd.min.js +1 -1
- package/bundles/router-upgrade.umd.min.js.map +1 -1
- package/bundles/router.umd.js +514 -405
- package/bundles/router.umd.js.map +1 -1
- package/bundles/router.umd.min.js +19 -26
- package/bundles/router.umd.min.js.map +1 -1
- package/esm2015/src/apply_redirects.js +90 -117
- package/esm2015/src/directives/router_link_active.js +2 -4
- package/esm2015/src/operators/activate_routes.js +13 -11
- package/esm2015/src/operators/apply_redirects.js +3 -5
- package/esm2015/src/operators/check_guards.js +16 -26
- package/esm2015/src/operators/prioritized_guard_value.js +2 -2
- package/esm2015/src/operators/recognize.js +3 -5
- package/esm2015/src/operators/resolve_data.js +10 -12
- package/esm2015/src/operators/switch_tap.js +9 -11
- package/esm2015/src/recognize.js +126 -122
- package/esm2015/src/router.js +8 -12
- package/esm2015/src/router_module.js +1 -1
- package/esm2015/src/router_state.js +20 -2
- package/esm2015/src/url_tree.js +1 -1
- package/esm2015/src/utils/collection.js +1 -25
- package/esm2015/src/utils/config.js +10 -14
- package/esm2015/src/utils/config_matching.js +145 -0
- package/esm2015/src/version.js +1 -1
- package/fesm2015/router.js +437 -350
- package/fesm2015/router.js.map +1 -1
- package/fesm2015/testing.js +1 -1
- package/fesm2015/upgrade.js +1 -1
- package/package.json +4 -4
- package/router.d.ts +33 -4
- package/router.metadata.json +1 -1
- package/testing/testing.d.ts +1 -1
- package/testing.d.ts +1 -1
- package/upgrade/upgrade.d.ts +1 -1
- package/upgrade.d.ts +1 -1
package/fesm2015/router.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @license Angular v11.0.
|
|
2
|
+
* @license Angular v11.0.9
|
|
3
3
|
* (c) 2010-2020 Google LLC. https://angular.io/
|
|
4
4
|
* License: MIT
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { Location, LocationStrategy, ViewportScroller, PlatformLocation, APP_BASE_HREF, HashLocationStrategy, PathLocationStrategy, ɵgetDOM, LOCATION_INITIALIZED } from '@angular/common';
|
|
8
8
|
import { ɵisObservable, ɵisPromise, Component, NgModuleRef, InjectionToken, NgModuleFactory, ɵConsole, NgZone, Injectable, Type, Injector, NgModuleFactoryLoader, Compiler, Directive, Attribute, Renderer2, ElementRef, Input, HostListener, HostBinding, ChangeDetectorRef, Optional, ContentChildren, EventEmitter, ViewContainerRef, ComponentFactoryResolver, Output, SystemJsNgModuleLoader, NgProbeToken, ANALYZE_FOR_ENTRY_COMPONENTS, SkipSelf, Inject, APP_INITIALIZER, APP_BOOTSTRAP_LISTENER, NgModule, ApplicationRef, Version } from '@angular/core';
|
|
9
|
-
import {
|
|
10
|
-
import { map,
|
|
9
|
+
import { from, of, BehaviorSubject, combineLatest, Observable, EmptyError, concat, defer, EMPTY, Subject } from 'rxjs';
|
|
10
|
+
import { map, switchMap, take, startWith, scan, filter, catchError, concatMap, last as last$1, first, mergeMap, tap, takeLast, finalize, mergeAll } from 'rxjs/operators';
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* @license
|
|
@@ -571,28 +571,6 @@ function forEach(map, callback) {
|
|
|
571
571
|
}
|
|
572
572
|
}
|
|
573
573
|
}
|
|
574
|
-
function waitForMap(obj, fn) {
|
|
575
|
-
if (Object.keys(obj).length === 0) {
|
|
576
|
-
return of({});
|
|
577
|
-
}
|
|
578
|
-
const waitHead = [];
|
|
579
|
-
const waitTail = [];
|
|
580
|
-
const res = {};
|
|
581
|
-
forEach(obj, (a, k) => {
|
|
582
|
-
const mapped = fn(k, a).pipe(map((r) => res[k] = r));
|
|
583
|
-
if (k === PRIMARY_OUTLET) {
|
|
584
|
-
waitHead.push(mapped);
|
|
585
|
-
}
|
|
586
|
-
else {
|
|
587
|
-
waitTail.push(mapped);
|
|
588
|
-
}
|
|
589
|
-
});
|
|
590
|
-
// Closure compiler has problem with using spread operator here. So we use "Array.concat".
|
|
591
|
-
// Note that we also need to cast the new promise because TypeScript cannot infer the type
|
|
592
|
-
// when calling the "of" function through "Function.apply"
|
|
593
|
-
return of.apply(null, waitHead.concat(waitTail))
|
|
594
|
-
.pipe(concatAll(), last$1(), map(() => res));
|
|
595
|
-
}
|
|
596
574
|
function wrapIntoObservable(value) {
|
|
597
575
|
if (ɵisObservable(value)) {
|
|
598
576
|
return value;
|
|
@@ -1480,7 +1458,25 @@ class ActivatedRouteSnapshot {
|
|
|
1480
1458
|
constructor(
|
|
1481
1459
|
/** The URL segments matched by this route */
|
|
1482
1460
|
url,
|
|
1483
|
-
/**
|
|
1461
|
+
/**
|
|
1462
|
+
* The matrix parameters scoped to this route.
|
|
1463
|
+
*
|
|
1464
|
+
* You can compute all params (or data) in the router state or to get params outside
|
|
1465
|
+
* of an activated component by traversing the `RouterState` tree as in the following
|
|
1466
|
+
* example:
|
|
1467
|
+
* ```
|
|
1468
|
+
* collectRouteParams(router: Router) {
|
|
1469
|
+
* let params = {};
|
|
1470
|
+
* let stack: ActivatedRouteSnapshot[] = [router.routerState.snapshot.root];
|
|
1471
|
+
* while (stack.length > 0) {
|
|
1472
|
+
* const route = stack.pop()!;
|
|
1473
|
+
* params = {...params, ...route.params};
|
|
1474
|
+
* stack.push(...route.children);
|
|
1475
|
+
* }
|
|
1476
|
+
* return params;
|
|
1477
|
+
* }
|
|
1478
|
+
* ```
|
|
1479
|
+
*/
|
|
1484
1480
|
params,
|
|
1485
1481
|
/** The query parameters shared by all the routes */
|
|
1486
1482
|
queryParams,
|
|
@@ -2064,16 +2060,18 @@ class ActivateRoutes {
|
|
|
2064
2060
|
}
|
|
2065
2061
|
deactivateRouteAndOutlet(route, parentContexts) {
|
|
2066
2062
|
const context = parentContexts.getContext(route.value.outlet);
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2063
|
+
// The context could be `null` if we are on a componentless route but there may still be
|
|
2064
|
+
// children that need deactivating.
|
|
2065
|
+
const contexts = context && route.value.component ? context.children : parentContexts;
|
|
2066
|
+
const children = nodeChildrenAsMap(route);
|
|
2067
|
+
for (const childOutlet of Object.keys(children)) {
|
|
2068
|
+
this.deactivateRouteAndItsChildren(children[childOutlet], contexts);
|
|
2069
|
+
}
|
|
2070
|
+
if (context && context.outlet) {
|
|
2071
|
+
// Destroy the component
|
|
2072
|
+
context.outlet.deactivate();
|
|
2073
|
+
// Destroy the contexts for all the outlets that were in the component
|
|
2074
|
+
context.children.onOutletDeactivated();
|
|
2077
2075
|
}
|
|
2078
2076
|
}
|
|
2079
2077
|
activateChildRoutes(futureNode, currNode, contexts) {
|
|
@@ -2221,7 +2219,7 @@ function isCanDeactivate(guard) {
|
|
|
2221
2219
|
const INITIAL_VALUE = Symbol('INITIAL_VALUE');
|
|
2222
2220
|
function prioritizedGuardValue() {
|
|
2223
2221
|
return switchMap(obs => {
|
|
2224
|
-
return combineLatest(
|
|
2222
|
+
return combineLatest(obs.map(o => o.pipe(take(1), startWith(INITIAL_VALUE))))
|
|
2225
2223
|
.pipe(scan((acc, list) => {
|
|
2226
2224
|
let isPending = false;
|
|
2227
2225
|
return list.reduce((innerAcc, val, i) => {
|
|
@@ -2373,23 +2371,160 @@ function standardizeConfig(r) {
|
|
|
2373
2371
|
}
|
|
2374
2372
|
return c;
|
|
2375
2373
|
}
|
|
2376
|
-
/** Returns of `Map` of outlet names to the `Route`s for that outlet. */
|
|
2377
|
-
function groupRoutesByOutlet(routes) {
|
|
2378
|
-
return routes.reduce((map, route) => {
|
|
2379
|
-
const routeOutlet = getOutlet(route);
|
|
2380
|
-
if (map.has(routeOutlet)) {
|
|
2381
|
-
map.get(routeOutlet).push(route);
|
|
2382
|
-
}
|
|
2383
|
-
else {
|
|
2384
|
-
map.set(routeOutlet, [route]);
|
|
2385
|
-
}
|
|
2386
|
-
return map;
|
|
2387
|
-
}, new Map());
|
|
2388
|
-
}
|
|
2389
2374
|
/** Returns the `route.outlet` or PRIMARY_OUTLET if none exists. */
|
|
2390
2375
|
function getOutlet(route) {
|
|
2391
2376
|
return route.outlet || PRIMARY_OUTLET;
|
|
2392
2377
|
}
|
|
2378
|
+
/**
|
|
2379
|
+
* Sorts the `routes` such that the ones with an outlet matching `outletName` come first.
|
|
2380
|
+
* The order of the configs is otherwise preserved.
|
|
2381
|
+
*/
|
|
2382
|
+
function sortByMatchingOutlets(routes, outletName) {
|
|
2383
|
+
const sortedConfig = routes.filter(r => getOutlet(r) === outletName);
|
|
2384
|
+
sortedConfig.push(...routes.filter(r => getOutlet(r) !== outletName));
|
|
2385
|
+
return sortedConfig;
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
/**
|
|
2389
|
+
* @license
|
|
2390
|
+
* Copyright Google LLC All Rights Reserved.
|
|
2391
|
+
*
|
|
2392
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
2393
|
+
* found in the LICENSE file at https://angular.io/license
|
|
2394
|
+
*/
|
|
2395
|
+
const noMatch = {
|
|
2396
|
+
matched: false,
|
|
2397
|
+
consumedSegments: [],
|
|
2398
|
+
lastChild: 0,
|
|
2399
|
+
parameters: {},
|
|
2400
|
+
positionalParamSegments: {}
|
|
2401
|
+
};
|
|
2402
|
+
function match(segmentGroup, route, segments) {
|
|
2403
|
+
var _a;
|
|
2404
|
+
if (route.path === '') {
|
|
2405
|
+
if (route.pathMatch === 'full' && (segmentGroup.hasChildren() || segments.length > 0)) {
|
|
2406
|
+
return Object.assign({}, noMatch);
|
|
2407
|
+
}
|
|
2408
|
+
return {
|
|
2409
|
+
matched: true,
|
|
2410
|
+
consumedSegments: [],
|
|
2411
|
+
lastChild: 0,
|
|
2412
|
+
parameters: {},
|
|
2413
|
+
positionalParamSegments: {}
|
|
2414
|
+
};
|
|
2415
|
+
}
|
|
2416
|
+
const matcher = route.matcher || defaultUrlMatcher;
|
|
2417
|
+
const res = matcher(segments, segmentGroup, route);
|
|
2418
|
+
if (!res)
|
|
2419
|
+
return Object.assign({}, noMatch);
|
|
2420
|
+
const posParams = {};
|
|
2421
|
+
forEach(res.posParams, (v, k) => {
|
|
2422
|
+
posParams[k] = v.path;
|
|
2423
|
+
});
|
|
2424
|
+
const parameters = res.consumed.length > 0 ? Object.assign(Object.assign({}, posParams), res.consumed[res.consumed.length - 1].parameters) :
|
|
2425
|
+
posParams;
|
|
2426
|
+
return {
|
|
2427
|
+
matched: true,
|
|
2428
|
+
consumedSegments: res.consumed,
|
|
2429
|
+
lastChild: res.consumed.length,
|
|
2430
|
+
// TODO(atscott): investigate combining parameters and positionalParamSegments
|
|
2431
|
+
parameters,
|
|
2432
|
+
positionalParamSegments: (_a = res.posParams) !== null && _a !== void 0 ? _a : {}
|
|
2433
|
+
};
|
|
2434
|
+
}
|
|
2435
|
+
function split(segmentGroup, consumedSegments, slicedSegments, config, relativeLinkResolution = 'corrected') {
|
|
2436
|
+
if (slicedSegments.length > 0 &&
|
|
2437
|
+
containsEmptyPathMatchesWithNamedOutlets(segmentGroup, slicedSegments, config)) {
|
|
2438
|
+
const s = new UrlSegmentGroup(consumedSegments, createChildrenForEmptyPaths(segmentGroup, consumedSegments, config, new UrlSegmentGroup(slicedSegments, segmentGroup.children)));
|
|
2439
|
+
s._sourceSegment = segmentGroup;
|
|
2440
|
+
s._segmentIndexShift = consumedSegments.length;
|
|
2441
|
+
return { segmentGroup: s, slicedSegments: [] };
|
|
2442
|
+
}
|
|
2443
|
+
if (slicedSegments.length === 0 &&
|
|
2444
|
+
containsEmptyPathMatches(segmentGroup, slicedSegments, config)) {
|
|
2445
|
+
const s = new UrlSegmentGroup(segmentGroup.segments, addEmptyPathsToChildrenIfNeeded(segmentGroup, consumedSegments, slicedSegments, config, segmentGroup.children, relativeLinkResolution));
|
|
2446
|
+
s._sourceSegment = segmentGroup;
|
|
2447
|
+
s._segmentIndexShift = consumedSegments.length;
|
|
2448
|
+
return { segmentGroup: s, slicedSegments };
|
|
2449
|
+
}
|
|
2450
|
+
const s = new UrlSegmentGroup(segmentGroup.segments, segmentGroup.children);
|
|
2451
|
+
s._sourceSegment = segmentGroup;
|
|
2452
|
+
s._segmentIndexShift = consumedSegments.length;
|
|
2453
|
+
return { segmentGroup: s, slicedSegments };
|
|
2454
|
+
}
|
|
2455
|
+
function addEmptyPathsToChildrenIfNeeded(segmentGroup, consumedSegments, slicedSegments, routes, children, relativeLinkResolution) {
|
|
2456
|
+
const res = {};
|
|
2457
|
+
for (const r of routes) {
|
|
2458
|
+
if (emptyPathMatch(segmentGroup, slicedSegments, r) && !children[getOutlet(r)]) {
|
|
2459
|
+
const s = new UrlSegmentGroup([], {});
|
|
2460
|
+
s._sourceSegment = segmentGroup;
|
|
2461
|
+
if (relativeLinkResolution === 'legacy') {
|
|
2462
|
+
s._segmentIndexShift = segmentGroup.segments.length;
|
|
2463
|
+
}
|
|
2464
|
+
else {
|
|
2465
|
+
s._segmentIndexShift = consumedSegments.length;
|
|
2466
|
+
}
|
|
2467
|
+
res[getOutlet(r)] = s;
|
|
2468
|
+
}
|
|
2469
|
+
}
|
|
2470
|
+
return Object.assign(Object.assign({}, children), res);
|
|
2471
|
+
}
|
|
2472
|
+
function createChildrenForEmptyPaths(segmentGroup, consumedSegments, routes, primarySegment) {
|
|
2473
|
+
const res = {};
|
|
2474
|
+
res[PRIMARY_OUTLET] = primarySegment;
|
|
2475
|
+
primarySegment._sourceSegment = segmentGroup;
|
|
2476
|
+
primarySegment._segmentIndexShift = consumedSegments.length;
|
|
2477
|
+
for (const r of routes) {
|
|
2478
|
+
if (r.path === '' && getOutlet(r) !== PRIMARY_OUTLET) {
|
|
2479
|
+
const s = new UrlSegmentGroup([], {});
|
|
2480
|
+
s._sourceSegment = segmentGroup;
|
|
2481
|
+
s._segmentIndexShift = consumedSegments.length;
|
|
2482
|
+
res[getOutlet(r)] = s;
|
|
2483
|
+
}
|
|
2484
|
+
}
|
|
2485
|
+
return res;
|
|
2486
|
+
}
|
|
2487
|
+
function containsEmptyPathMatchesWithNamedOutlets(segmentGroup, slicedSegments, routes) {
|
|
2488
|
+
return routes.some(r => emptyPathMatch(segmentGroup, slicedSegments, r) && getOutlet(r) !== PRIMARY_OUTLET);
|
|
2489
|
+
}
|
|
2490
|
+
function containsEmptyPathMatches(segmentGroup, slicedSegments, routes) {
|
|
2491
|
+
return routes.some(r => emptyPathMatch(segmentGroup, slicedSegments, r));
|
|
2492
|
+
}
|
|
2493
|
+
function emptyPathMatch(segmentGroup, slicedSegments, r) {
|
|
2494
|
+
if ((segmentGroup.hasChildren() || slicedSegments.length > 0) && r.pathMatch === 'full') {
|
|
2495
|
+
return false;
|
|
2496
|
+
}
|
|
2497
|
+
return r.path === '';
|
|
2498
|
+
}
|
|
2499
|
+
/**
|
|
2500
|
+
* Determines if `route` is a path match for the `rawSegment`, `segments`, and `outlet` without
|
|
2501
|
+
* verifying that its children are a full match for the remainder of the `rawSegment` children as
|
|
2502
|
+
* well.
|
|
2503
|
+
*/
|
|
2504
|
+
function isImmediateMatch(route, rawSegment, segments, outlet) {
|
|
2505
|
+
// We allow matches to empty paths when the outlets differ so we can match a url like `/(b:b)` to
|
|
2506
|
+
// a config like
|
|
2507
|
+
// * `{path: '', children: [{path: 'b', outlet: 'b'}]}`
|
|
2508
|
+
// or even
|
|
2509
|
+
// * `{path: '', outlet: 'a', children: [{path: 'b', outlet: 'b'}]`
|
|
2510
|
+
//
|
|
2511
|
+
// The exception here is when the segment outlet is for the primary outlet. This would
|
|
2512
|
+
// result in a match inside the named outlet because all children there are written as primary
|
|
2513
|
+
// outlets. So we need to prevent child named outlet matches in a url like `/b` in a config like
|
|
2514
|
+
// * `{path: '', outlet: 'x' children: [{path: 'b'}]}`
|
|
2515
|
+
// This should only match if the url is `/(x:b)`.
|
|
2516
|
+
if (getOutlet(route) !== outlet &&
|
|
2517
|
+
(outlet === PRIMARY_OUTLET || !emptyPathMatch(rawSegment, segments, route))) {
|
|
2518
|
+
return false;
|
|
2519
|
+
}
|
|
2520
|
+
if (route.path === '**') {
|
|
2521
|
+
return true;
|
|
2522
|
+
}
|
|
2523
|
+
return match(rawSegment, route, segments).matched;
|
|
2524
|
+
}
|
|
2525
|
+
function noLeftoversInUrl(segmentGroup, segments, outlet) {
|
|
2526
|
+
return segments.length === 0 && !segmentGroup.children[outlet];
|
|
2527
|
+
}
|
|
2393
2528
|
|
|
2394
2529
|
/**
|
|
2395
2530
|
* @license
|
|
@@ -2408,7 +2543,7 @@ class AbsoluteRedirect {
|
|
|
2408
2543
|
this.urlTree = urlTree;
|
|
2409
2544
|
}
|
|
2410
2545
|
}
|
|
2411
|
-
function noMatch(segmentGroup) {
|
|
2546
|
+
function noMatch$1(segmentGroup) {
|
|
2412
2547
|
return new Observable((obs) => obs.error(new NoMatch(segmentGroup)));
|
|
2413
2548
|
}
|
|
2414
2549
|
function absoluteRedirect(newTree) {
|
|
@@ -2438,8 +2573,18 @@ class ApplyRedirects {
|
|
|
2438
2573
|
this.ngModule = moduleInjector.get(NgModuleRef);
|
|
2439
2574
|
}
|
|
2440
2575
|
apply() {
|
|
2441
|
-
const
|
|
2442
|
-
|
|
2576
|
+
const splitGroup = split(this.urlTree.root, [], [], this.config).segmentGroup;
|
|
2577
|
+
// TODO(atscott): creating a new segment removes the _sourceSegment _segmentIndexShift, which is
|
|
2578
|
+
// only necessary to prevent failures in tests which assert exact object matches. The `split` is
|
|
2579
|
+
// now shared between `applyRedirects` and `recognize` but only the `recognize` step needs these
|
|
2580
|
+
// properties. Before the implementations were merged, the `applyRedirects` would not assign
|
|
2581
|
+
// them. We should be able to remove this logic as a "breaking change" but should do some more
|
|
2582
|
+
// investigation into the failures first.
|
|
2583
|
+
const rootSegmentGroup = new UrlSegmentGroup(splitGroup.segments, splitGroup.children);
|
|
2584
|
+
const expanded$ = this.expandSegmentGroup(this.ngModule, this.config, rootSegmentGroup, PRIMARY_OUTLET);
|
|
2585
|
+
const urlTrees$ = expanded$.pipe(map((rootSegmentGroup) => {
|
|
2586
|
+
return this.createUrlTree(squashSegmentGroup(rootSegmentGroup), this.urlTree.queryParams, this.urlTree.fragment);
|
|
2587
|
+
}));
|
|
2443
2588
|
return urlTrees$.pipe(catchError((e) => {
|
|
2444
2589
|
if (e instanceof AbsoluteRedirect) {
|
|
2445
2590
|
// after an absolute redirect we do not apply any more redirects!
|
|
@@ -2455,7 +2600,9 @@ class ApplyRedirects {
|
|
|
2455
2600
|
}
|
|
2456
2601
|
match(tree) {
|
|
2457
2602
|
const expanded$ = this.expandSegmentGroup(this.ngModule, this.config, tree.root, PRIMARY_OUTLET);
|
|
2458
|
-
const mapped$ = expanded$.pipe(map((rootSegmentGroup) =>
|
|
2603
|
+
const mapped$ = expanded$.pipe(map((rootSegmentGroup) => {
|
|
2604
|
+
return this.createUrlTree(squashSegmentGroup(rootSegmentGroup), tree.queryParams, tree.fragment);
|
|
2605
|
+
}));
|
|
2459
2606
|
return mapped$.pipe(catchError((e) => {
|
|
2460
2607
|
if (e instanceof NoMatch) {
|
|
2461
2608
|
throw this.noMatchError(e);
|
|
@@ -2481,62 +2628,61 @@ class ApplyRedirects {
|
|
|
2481
2628
|
}
|
|
2482
2629
|
// Recursively expand segment groups for all the child outlets
|
|
2483
2630
|
expandChildren(ngModule, routes, segmentGroup) {
|
|
2484
|
-
|
|
2631
|
+
// Expand outlets one at a time, starting with the primary outlet. We need to do it this way
|
|
2632
|
+
// because an absolute redirect from the primary outlet takes precedence.
|
|
2633
|
+
const childOutlets = [];
|
|
2634
|
+
for (const child of Object.keys(segmentGroup.children)) {
|
|
2635
|
+
if (child === 'primary') {
|
|
2636
|
+
childOutlets.unshift(child);
|
|
2637
|
+
}
|
|
2638
|
+
else {
|
|
2639
|
+
childOutlets.push(child);
|
|
2640
|
+
}
|
|
2641
|
+
}
|
|
2642
|
+
return from(childOutlets)
|
|
2643
|
+
.pipe(concatMap(childOutlet => {
|
|
2644
|
+
const child = segmentGroup.children[childOutlet];
|
|
2645
|
+
// Sort the routes so routes with outlets that match the the segment appear
|
|
2646
|
+
// first, followed by routes for other outlets, which might match if they have an
|
|
2647
|
+
// empty path.
|
|
2648
|
+
const sortedRoutes = sortByMatchingOutlets(routes, childOutlet);
|
|
2649
|
+
return this.expandSegmentGroup(ngModule, sortedRoutes, child, childOutlet)
|
|
2650
|
+
.pipe(map(s => ({ segment: s, outlet: childOutlet })));
|
|
2651
|
+
}), scan((children, expandedChild) => {
|
|
2652
|
+
children[expandedChild.outlet] = expandedChild.segment;
|
|
2653
|
+
return children;
|
|
2654
|
+
}, {}), last$1());
|
|
2485
2655
|
}
|
|
2486
2656
|
expandSegment(ngModule, segmentGroup, routes, segments, outlet, allowRedirects) {
|
|
2487
|
-
|
|
2488
|
-
|
|
2489
|
-
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
routesByOutlet.set(outlet, []);
|
|
2493
|
-
}
|
|
2494
|
-
const expandRoutes = (routes) => {
|
|
2495
|
-
return from(routes).pipe(concatMap((r) => {
|
|
2496
|
-
const expanded$ = this.expandSegmentAgainstRoute(ngModule, segmentGroup, routes, r, segments, outlet, allowRedirects);
|
|
2497
|
-
return expanded$.pipe(catchError(e => {
|
|
2498
|
-
if (e instanceof NoMatch) {
|
|
2499
|
-
return of(null);
|
|
2500
|
-
}
|
|
2501
|
-
throw e;
|
|
2502
|
-
}));
|
|
2503
|
-
}), first((s) => s !== null), catchError(e => {
|
|
2504
|
-
if (e instanceof EmptyError || e.name === 'EmptyError') {
|
|
2505
|
-
if (this.noLeftoversInUrl(segmentGroup, segments, outlet)) {
|
|
2506
|
-
return of(new UrlSegmentGroup([], {}));
|
|
2507
|
-
}
|
|
2508
|
-
throw new NoMatch(segmentGroup);
|
|
2657
|
+
return from(routes).pipe(concatMap((r) => {
|
|
2658
|
+
const expanded$ = this.expandSegmentAgainstRoute(ngModule, segmentGroup, routes, r, segments, outlet, allowRedirects);
|
|
2659
|
+
return expanded$.pipe(catchError((e) => {
|
|
2660
|
+
if (e instanceof NoMatch) {
|
|
2661
|
+
return of(null);
|
|
2509
2662
|
}
|
|
2510
2663
|
throw e;
|
|
2511
2664
|
}));
|
|
2512
|
-
}
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
// Return only the expansion for the route outlet we are trying to activate.
|
|
2522
|
-
map(results => results.find(result => result !== null)));
|
|
2523
|
-
}
|
|
2524
|
-
noLeftoversInUrl(segmentGroup, segments, outlet) {
|
|
2525
|
-
return segments.length === 0 && !segmentGroup.children[outlet];
|
|
2665
|
+
}), first((s) => !!s), catchError((e, _) => {
|
|
2666
|
+
if (e instanceof EmptyError || e.name === 'EmptyError') {
|
|
2667
|
+
if (noLeftoversInUrl(segmentGroup, segments, outlet)) {
|
|
2668
|
+
return of(new UrlSegmentGroup([], {}));
|
|
2669
|
+
}
|
|
2670
|
+
throw new NoMatch(segmentGroup);
|
|
2671
|
+
}
|
|
2672
|
+
throw e;
|
|
2673
|
+
}));
|
|
2526
2674
|
}
|
|
2527
2675
|
expandSegmentAgainstRoute(ngModule, segmentGroup, routes, route, paths, outlet, allowRedirects) {
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
if (getOutlet(route) !== outlet && route.path !== '') {
|
|
2531
|
-
return noMatch(segmentGroup);
|
|
2676
|
+
if (!isImmediateMatch(route, segmentGroup, paths, outlet)) {
|
|
2677
|
+
return noMatch$1(segmentGroup);
|
|
2532
2678
|
}
|
|
2533
2679
|
if (route.redirectTo === undefined) {
|
|
2534
|
-
return this.matchSegmentAgainstRoute(ngModule, segmentGroup, route, paths);
|
|
2680
|
+
return this.matchSegmentAgainstRoute(ngModule, segmentGroup, route, paths, outlet);
|
|
2535
2681
|
}
|
|
2536
2682
|
if (allowRedirects && this.allowRedirects) {
|
|
2537
2683
|
return this.expandSegmentAgainstRouteUsingRedirect(ngModule, segmentGroup, routes, route, paths, outlet);
|
|
2538
2684
|
}
|
|
2539
|
-
return noMatch(segmentGroup);
|
|
2685
|
+
return noMatch$1(segmentGroup);
|
|
2540
2686
|
}
|
|
2541
2687
|
expandSegmentAgainstRouteUsingRedirect(ngModule, segmentGroup, routes, route, segments, outlet) {
|
|
2542
2688
|
if (route.path === '**') {
|
|
@@ -2557,7 +2703,7 @@ class ApplyRedirects {
|
|
|
2557
2703
|
expandRegularSegmentAgainstRouteUsingRedirect(ngModule, segmentGroup, routes, route, segments, outlet) {
|
|
2558
2704
|
const { matched, consumedSegments, lastChild, positionalParamSegments } = match(segmentGroup, route, segments);
|
|
2559
2705
|
if (!matched)
|
|
2560
|
-
return noMatch(segmentGroup);
|
|
2706
|
+
return noMatch$1(segmentGroup);
|
|
2561
2707
|
const newTree = this.applyRedirectCommands(consumedSegments, route.redirectTo, positionalParamSegments);
|
|
2562
2708
|
if (route.redirectTo.startsWith('/')) {
|
|
2563
2709
|
return absoluteRedirect(newTree);
|
|
@@ -2566,7 +2712,7 @@ class ApplyRedirects {
|
|
|
2566
2712
|
return this.expandSegment(ngModule, segmentGroup, routes, newSegments.concat(segments.slice(lastChild)), outlet, false);
|
|
2567
2713
|
}));
|
|
2568
2714
|
}
|
|
2569
|
-
matchSegmentAgainstRoute(ngModule, rawSegmentGroup, route, segments) {
|
|
2715
|
+
matchSegmentAgainstRoute(ngModule, rawSegmentGroup, route, segments, outlet) {
|
|
2570
2716
|
if (route.path === '**') {
|
|
2571
2717
|
if (route.loadChildren) {
|
|
2572
2718
|
return this.configLoader.load(ngModule.injector, route)
|
|
@@ -2579,13 +2725,15 @@ class ApplyRedirects {
|
|
|
2579
2725
|
}
|
|
2580
2726
|
const { matched, consumedSegments, lastChild } = match(rawSegmentGroup, route, segments);
|
|
2581
2727
|
if (!matched)
|
|
2582
|
-
return noMatch(rawSegmentGroup);
|
|
2728
|
+
return noMatch$1(rawSegmentGroup);
|
|
2583
2729
|
const rawSlicedSegments = segments.slice(lastChild);
|
|
2584
2730
|
const childConfig$ = this.getChildConfig(ngModule, route, segments);
|
|
2585
2731
|
return childConfig$.pipe(mergeMap((routerConfig) => {
|
|
2586
2732
|
const childModule = routerConfig.module;
|
|
2587
2733
|
const childConfig = routerConfig.routes;
|
|
2588
|
-
const { segmentGroup, slicedSegments } = split(rawSegmentGroup, consumedSegments, rawSlicedSegments, childConfig);
|
|
2734
|
+
const { segmentGroup: splitSegmentGroup, slicedSegments } = split(rawSegmentGroup, consumedSegments, rawSlicedSegments, childConfig);
|
|
2735
|
+
// See comment on the other call to `split` about why this is necessary.
|
|
2736
|
+
const segmentGroup = new UrlSegmentGroup(splitSegmentGroup.segments, splitSegmentGroup.children);
|
|
2589
2737
|
if (slicedSegments.length === 0 && segmentGroup.hasChildren()) {
|
|
2590
2738
|
const expanded$ = this.expandChildren(childModule, childConfig, segmentGroup);
|
|
2591
2739
|
return expanded$.pipe(map((children) => new UrlSegmentGroup(consumedSegments, children)));
|
|
@@ -2593,7 +2741,8 @@ class ApplyRedirects {
|
|
|
2593
2741
|
if (childConfig.length === 0 && slicedSegments.length === 0) {
|
|
2594
2742
|
return of(new UrlSegmentGroup(consumedSegments, {}));
|
|
2595
2743
|
}
|
|
2596
|
-
const
|
|
2744
|
+
const matchedOnOutlet = getOutlet(route) === outlet;
|
|
2745
|
+
const expanded$ = this.expandSegment(childModule, segmentGroup, childConfig, slicedSegments, matchedOnOutlet ? PRIMARY_OUTLET : outlet, true);
|
|
2597
2746
|
return expanded$.pipe(map((cs) => new UrlSegmentGroup(consumedSegments.concat(cs.segments), cs.children)));
|
|
2598
2747
|
}));
|
|
2599
2748
|
}
|
|
@@ -2713,43 +2862,14 @@ class ApplyRedirects {
|
|
|
2713
2862
|
return redirectToUrlSegment;
|
|
2714
2863
|
}
|
|
2715
2864
|
}
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
const res = matcher(segments, segmentGroup, route);
|
|
2725
|
-
if (!res) {
|
|
2726
|
-
return {
|
|
2727
|
-
matched: false,
|
|
2728
|
-
consumedSegments: [],
|
|
2729
|
-
lastChild: 0,
|
|
2730
|
-
positionalParamSegments: {},
|
|
2731
|
-
};
|
|
2732
|
-
}
|
|
2733
|
-
return {
|
|
2734
|
-
matched: true,
|
|
2735
|
-
consumedSegments: res.consumed,
|
|
2736
|
-
lastChild: res.consumed.length,
|
|
2737
|
-
positionalParamSegments: res.posParams,
|
|
2738
|
-
};
|
|
2739
|
-
}
|
|
2740
|
-
function split(segmentGroup, consumedSegments, slicedSegments, config) {
|
|
2741
|
-
if (slicedSegments.length > 0 &&
|
|
2742
|
-
containsEmptyPathRedirectsWithNamedOutlets(segmentGroup, slicedSegments, config)) {
|
|
2743
|
-
const s = new UrlSegmentGroup(consumedSegments, createChildrenForEmptySegments(config, new UrlSegmentGroup(slicedSegments, segmentGroup.children)));
|
|
2744
|
-
return { segmentGroup: mergeTrivialChildren(s), slicedSegments: [] };
|
|
2745
|
-
}
|
|
2746
|
-
if (slicedSegments.length === 0 &&
|
|
2747
|
-
containsEmptyPathRedirects(segmentGroup, slicedSegments, config)) {
|
|
2748
|
-
const s = new UrlSegmentGroup(segmentGroup.segments, addEmptySegmentsToChildrenIfNeeded(segmentGroup, slicedSegments, config, segmentGroup.children));
|
|
2749
|
-
return { segmentGroup: mergeTrivialChildren(s), slicedSegments };
|
|
2750
|
-
}
|
|
2751
|
-
return { segmentGroup, slicedSegments };
|
|
2752
|
-
}
|
|
2865
|
+
/**
|
|
2866
|
+
* When possible, merges the primary outlet child into the parent `UrlSegmentGroup`.
|
|
2867
|
+
*
|
|
2868
|
+
* When a segment group has only one child which is a primary outlet, merges that child into the
|
|
2869
|
+
* parent. That is, the child segment group's segments are merged into the `s` and the child's
|
|
2870
|
+
* children become the children of `s`. Think of this like a 'squash', merging the child segment
|
|
2871
|
+
* group into the parent.
|
|
2872
|
+
*/
|
|
2753
2873
|
function mergeTrivialChildren(s) {
|
|
2754
2874
|
if (s.numberOfChildren === 1 && s.children[PRIMARY_OUTLET]) {
|
|
2755
2875
|
const c = s.children[PRIMARY_OUTLET];
|
|
@@ -2757,36 +2877,23 @@ function mergeTrivialChildren(s) {
|
|
|
2757
2877
|
}
|
|
2758
2878
|
return s;
|
|
2759
2879
|
}
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
}
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
|
|
2771
|
-
|
|
2772
|
-
|
|
2773
|
-
if (r.path === '' && getOutlet(r) !== PRIMARY_OUTLET) {
|
|
2774
|
-
res[getOutlet(r)] = new UrlSegmentGroup([], {});
|
|
2880
|
+
/**
|
|
2881
|
+
* Recursively merges primary segment children into their parents and also drops empty children
|
|
2882
|
+
* (those which have no segments and no children themselves). The latter prevents serializing a
|
|
2883
|
+
* group into something like `/a(aux:)`, where `aux` is an empty child segment.
|
|
2884
|
+
*/
|
|
2885
|
+
function squashSegmentGroup(segmentGroup) {
|
|
2886
|
+
const newChildren = {};
|
|
2887
|
+
for (const childOutlet of Object.keys(segmentGroup.children)) {
|
|
2888
|
+
const child = segmentGroup.children[childOutlet];
|
|
2889
|
+
const childCandidate = squashSegmentGroup(child);
|
|
2890
|
+
// don't add empty children
|
|
2891
|
+
if (childCandidate.segments.length > 0 || childCandidate.hasChildren()) {
|
|
2892
|
+
newChildren[childOutlet] = childCandidate;
|
|
2775
2893
|
}
|
|
2776
2894
|
}
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
function containsEmptyPathRedirectsWithNamedOutlets(segmentGroup, segments, routes) {
|
|
2780
|
-
return routes.some(r => isEmptyPathRedirect(segmentGroup, segments, r) && getOutlet(r) !== PRIMARY_OUTLET);
|
|
2781
|
-
}
|
|
2782
|
-
function containsEmptyPathRedirects(segmentGroup, segments, routes) {
|
|
2783
|
-
return routes.some(r => isEmptyPathRedirect(segmentGroup, segments, r));
|
|
2784
|
-
}
|
|
2785
|
-
function isEmptyPathRedirect(segmentGroup, segments, r) {
|
|
2786
|
-
if ((segmentGroup.hasChildren() || segments.length > 0) && r.pathMatch === 'full') {
|
|
2787
|
-
return false;
|
|
2788
|
-
}
|
|
2789
|
-
return r.path === '' && r.redirectTo !== undefined;
|
|
2895
|
+
const s = new UrlSegmentGroup(segmentGroup.segments, newChildren);
|
|
2896
|
+
return mergeTrivialChildren(s);
|
|
2790
2897
|
}
|
|
2791
2898
|
|
|
2792
2899
|
/**
|
|
@@ -2797,10 +2904,8 @@ function isEmptyPathRedirect(segmentGroup, segments, r) {
|
|
|
2797
2904
|
* found in the LICENSE file at https://angular.io/license
|
|
2798
2905
|
*/
|
|
2799
2906
|
function applyRedirects$1(moduleInjector, configLoader, urlSerializer, config) {
|
|
2800
|
-
return
|
|
2801
|
-
|
|
2802
|
-
.pipe(map(urlAfterRedirects => (Object.assign(Object.assign({}, t), { urlAfterRedirects }))))));
|
|
2803
|
-
};
|
|
2907
|
+
return switchMap(t => applyRedirects(moduleInjector, configLoader, urlSerializer, t.extractedUrl, config)
|
|
2908
|
+
.pipe(map(urlAfterRedirects => (Object.assign(Object.assign({}, t), { urlAfterRedirects })))));
|
|
2804
2909
|
}
|
|
2805
2910
|
|
|
2806
2911
|
/**
|
|
@@ -2961,20 +3066,18 @@ function deactivateRouteAndItsChildren(route, context, checks) {
|
|
|
2961
3066
|
* found in the LICENSE file at https://angular.io/license
|
|
2962
3067
|
*/
|
|
2963
3068
|
function checkGuards(moduleInjector, forwardEvent) {
|
|
2964
|
-
return
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
}));
|
|
2977
|
-
};
|
|
3069
|
+
return mergeMap(t => {
|
|
3070
|
+
const { targetSnapshot, currentSnapshot, guards: { canActivateChecks, canDeactivateChecks } } = t;
|
|
3071
|
+
if (canDeactivateChecks.length === 0 && canActivateChecks.length === 0) {
|
|
3072
|
+
return of(Object.assign(Object.assign({}, t), { guardsResult: true }));
|
|
3073
|
+
}
|
|
3074
|
+
return runCanDeactivateChecks(canDeactivateChecks, targetSnapshot, currentSnapshot, moduleInjector)
|
|
3075
|
+
.pipe(mergeMap(canDeactivate => {
|
|
3076
|
+
return canDeactivate && isBoolean(canDeactivate) ?
|
|
3077
|
+
runCanActivateChecks(targetSnapshot, canActivateChecks, moduleInjector, forwardEvent) :
|
|
3078
|
+
of(canDeactivate);
|
|
3079
|
+
}), map(guardsResult => (Object.assign(Object.assign({}, t), { guardsResult }))));
|
|
3080
|
+
});
|
|
2978
3081
|
}
|
|
2979
3082
|
function runCanDeactivateChecks(checks, futureRSS, currRSS, moduleInjector) {
|
|
2980
3083
|
return from(checks).pipe(mergeMap(check => runCanDeactivate(check.component, check.route, currRSS, futureRSS, moduleInjector)), first(result => {
|
|
@@ -2983,15 +3086,7 @@ function runCanDeactivateChecks(checks, futureRSS, currRSS, moduleInjector) {
|
|
|
2983
3086
|
}
|
|
2984
3087
|
function runCanActivateChecks(futureSnapshot, checks, moduleInjector, forwardEvent) {
|
|
2985
3088
|
return from(checks).pipe(concatMap((check) => {
|
|
2986
|
-
return
|
|
2987
|
-
fireChildActivationStart(check.route.parent, forwardEvent),
|
|
2988
|
-
fireActivationStart(check.route, forwardEvent),
|
|
2989
|
-
runCanActivateChild(futureSnapshot, check.path, moduleInjector),
|
|
2990
|
-
runCanActivate(futureSnapshot, check.route, moduleInjector)
|
|
2991
|
-
])
|
|
2992
|
-
.pipe(concatAll(), first(result => {
|
|
2993
|
-
return result !== true;
|
|
2994
|
-
}, true));
|
|
3089
|
+
return concat(fireChildActivationStart(check.route.parent, forwardEvent), fireActivationStart(check.route, forwardEvent), runCanActivateChild(futureSnapshot, check.path, moduleInjector), runCanActivate(futureSnapshot, check.route, moduleInjector));
|
|
2995
3090
|
}), first(result => {
|
|
2996
3091
|
return result !== true;
|
|
2997
3092
|
}, true));
|
|
@@ -3103,9 +3198,26 @@ function runCanDeactivate(component, currARS, currRSS, futureRSS, moduleInjector
|
|
|
3103
3198
|
*/
|
|
3104
3199
|
class NoMatch$1 {
|
|
3105
3200
|
}
|
|
3201
|
+
function newObservableError(e) {
|
|
3202
|
+
// TODO(atscott): This pattern is used throughout the router code and can be `throwError` instead.
|
|
3203
|
+
return new Observable((obs) => obs.error(e));
|
|
3204
|
+
}
|
|
3106
3205
|
function recognize(rootComponentType, config, urlTree, url, paramsInheritanceStrategy = 'emptyOnly', relativeLinkResolution = 'legacy') {
|
|
3107
|
-
|
|
3108
|
-
|
|
3206
|
+
try {
|
|
3207
|
+
const result = new Recognizer(rootComponentType, config, urlTree, url, paramsInheritanceStrategy, relativeLinkResolution)
|
|
3208
|
+
.recognize();
|
|
3209
|
+
if (result === null) {
|
|
3210
|
+
return newObservableError(new NoMatch$1());
|
|
3211
|
+
}
|
|
3212
|
+
else {
|
|
3213
|
+
return of(result);
|
|
3214
|
+
}
|
|
3215
|
+
}
|
|
3216
|
+
catch (e) {
|
|
3217
|
+
// Catch the potential error from recognize due to duplicate outlet matches and return as an
|
|
3218
|
+
// `Observable` error instead.
|
|
3219
|
+
return newObservableError(e);
|
|
3220
|
+
}
|
|
3109
3221
|
}
|
|
3110
3222
|
class Recognizer {
|
|
3111
3223
|
constructor(rootComponentType, config, urlTree, url, paramsInheritanceStrategy, relativeLinkResolution) {
|
|
@@ -3117,18 +3229,19 @@ class Recognizer {
|
|
|
3117
3229
|
this.relativeLinkResolution = relativeLinkResolution;
|
|
3118
3230
|
}
|
|
3119
3231
|
recognize() {
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
const routeState = new RouterStateSnapshot(this.url, rootNode);
|
|
3126
|
-
this.inheritParamsAndData(routeState._root);
|
|
3127
|
-
return of(routeState);
|
|
3128
|
-
}
|
|
3129
|
-
catch (e) {
|
|
3130
|
-
return new Observable((obs) => obs.error(e));
|
|
3232
|
+
const rootSegmentGroup = split(this.urlTree.root, [], [], this.config.filter(c => c.redirectTo === undefined), this.relativeLinkResolution)
|
|
3233
|
+
.segmentGroup;
|
|
3234
|
+
const children = this.processSegmentGroup(this.config, rootSegmentGroup, PRIMARY_OUTLET);
|
|
3235
|
+
if (children === null) {
|
|
3236
|
+
return null;
|
|
3131
3237
|
}
|
|
3238
|
+
// Use Object.freeze to prevent readers of the Router state from modifying it outside of a
|
|
3239
|
+
// navigation, resulting in the router being out of sync with the browser.
|
|
3240
|
+
const root = new ActivatedRouteSnapshot([], Object.freeze({}), Object.freeze(Object.assign({}, this.urlTree.queryParams)), this.urlTree.fragment, {}, PRIMARY_OUTLET, this.rootComponentType, null, this.urlTree.root, -1, {});
|
|
3241
|
+
const rootNode = new TreeNode(root, children);
|
|
3242
|
+
const routeState = new RouterStateSnapshot(this.url, rootNode);
|
|
3243
|
+
this.inheritParamsAndData(routeState._root);
|
|
3244
|
+
return routeState;
|
|
3132
3245
|
}
|
|
3133
3246
|
inheritParamsAndData(routeNode) {
|
|
3134
3247
|
const route = routeNode.value;
|
|
@@ -3143,58 +3256,101 @@ class Recognizer {
|
|
|
3143
3256
|
}
|
|
3144
3257
|
return this.processSegment(config, segmentGroup, segmentGroup.segments, outlet);
|
|
3145
3258
|
}
|
|
3259
|
+
/**
|
|
3260
|
+
* Matches every child outlet in the `segmentGroup` to a `Route` in the config. Returns `null` if
|
|
3261
|
+
* we cannot find a match for _any_ of the children.
|
|
3262
|
+
*
|
|
3263
|
+
* @param config - The `Routes` to match against
|
|
3264
|
+
* @param segmentGroup - The `UrlSegmentGroup` whose children need to be matched against the
|
|
3265
|
+
* config.
|
|
3266
|
+
*/
|
|
3146
3267
|
processChildren(config, segmentGroup) {
|
|
3147
|
-
const children =
|
|
3148
|
-
|
|
3149
|
-
|
|
3150
|
-
|
|
3268
|
+
const children = [];
|
|
3269
|
+
for (const childOutlet of Object.keys(segmentGroup.children)) {
|
|
3270
|
+
const child = segmentGroup.children[childOutlet];
|
|
3271
|
+
// Sort the config so that routes with outlets that match the one being activated appear
|
|
3272
|
+
// first, followed by routes for other outlets, which might match if they have an empty path.
|
|
3273
|
+
const sortedConfig = sortByMatchingOutlets(config, childOutlet);
|
|
3274
|
+
const outletChildren = this.processSegmentGroup(sortedConfig, child, childOutlet);
|
|
3275
|
+
if (outletChildren === null) {
|
|
3276
|
+
// Configs must match all segment children so because we did not find a match for this
|
|
3277
|
+
// outlet, return `null`.
|
|
3278
|
+
return null;
|
|
3279
|
+
}
|
|
3280
|
+
children.push(...outletChildren);
|
|
3281
|
+
}
|
|
3282
|
+
// Because we may have matched two outlets to the same empty path segment, we can have multiple
|
|
3283
|
+
// activated results for the same outlet. We should merge the children of these results so the
|
|
3284
|
+
// final return value is only one `TreeNode` per outlet.
|
|
3285
|
+
const mergedChildren = mergeEmptyPathMatches(children);
|
|
3286
|
+
if (typeof ngDevMode === 'undefined' || ngDevMode) {
|
|
3287
|
+
// This should really never happen - we are only taking the first match for each outlet and
|
|
3288
|
+
// merge the empty path matches.
|
|
3289
|
+
checkOutletNameUniqueness(mergedChildren);
|
|
3290
|
+
}
|
|
3291
|
+
sortActivatedRouteSnapshots(mergedChildren);
|
|
3292
|
+
return mergedChildren;
|
|
3151
3293
|
}
|
|
3152
3294
|
processSegment(config, segmentGroup, segments, outlet) {
|
|
3153
3295
|
for (const r of config) {
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3157
|
-
catch (e) {
|
|
3158
|
-
if (!(e instanceof NoMatch$1))
|
|
3159
|
-
throw e;
|
|
3296
|
+
const children = this.processSegmentAgainstRoute(r, segmentGroup, segments, outlet);
|
|
3297
|
+
if (children !== null) {
|
|
3298
|
+
return children;
|
|
3160
3299
|
}
|
|
3161
3300
|
}
|
|
3162
|
-
if (
|
|
3301
|
+
if (noLeftoversInUrl(segmentGroup, segments, outlet)) {
|
|
3163
3302
|
return [];
|
|
3164
3303
|
}
|
|
3165
|
-
|
|
3166
|
-
}
|
|
3167
|
-
noLeftoversInUrl(segmentGroup, segments, outlet) {
|
|
3168
|
-
return segments.length === 0 && !segmentGroup.children[outlet];
|
|
3304
|
+
return null;
|
|
3169
3305
|
}
|
|
3170
3306
|
processSegmentAgainstRoute(route, rawSegment, segments, outlet) {
|
|
3171
|
-
if (route.redirectTo)
|
|
3172
|
-
|
|
3173
|
-
if ((route.outlet || PRIMARY_OUTLET) !== outlet)
|
|
3174
|
-
throw new NoMatch$1();
|
|
3307
|
+
if (route.redirectTo || !isImmediateMatch(route, rawSegment, segments, outlet))
|
|
3308
|
+
return null;
|
|
3175
3309
|
let snapshot;
|
|
3176
3310
|
let consumedSegments = [];
|
|
3177
3311
|
let rawSlicedSegments = [];
|
|
3178
3312
|
if (route.path === '**') {
|
|
3179
3313
|
const params = segments.length > 0 ? last(segments).parameters : {};
|
|
3180
|
-
snapshot = new ActivatedRouteSnapshot(segments, params, Object.freeze(Object.assign({}, this.urlTree.queryParams)), this.urlTree.fragment, getData(route),
|
|
3314
|
+
snapshot = new ActivatedRouteSnapshot(segments, params, Object.freeze(Object.assign({}, this.urlTree.queryParams)), this.urlTree.fragment, getData(route), getOutlet(route), route.component, route, getSourceSegmentGroup(rawSegment), getPathIndexShift(rawSegment) + segments.length, getResolve(route));
|
|
3181
3315
|
}
|
|
3182
3316
|
else {
|
|
3183
|
-
const result = match
|
|
3317
|
+
const result = match(rawSegment, route, segments);
|
|
3318
|
+
if (!result.matched) {
|
|
3319
|
+
return null;
|
|
3320
|
+
}
|
|
3184
3321
|
consumedSegments = result.consumedSegments;
|
|
3185
3322
|
rawSlicedSegments = segments.slice(result.lastChild);
|
|
3186
|
-
snapshot = new ActivatedRouteSnapshot(consumedSegments, result.parameters, Object.freeze(Object.assign({}, this.urlTree.queryParams)), this.urlTree.fragment, getData(route),
|
|
3323
|
+
snapshot = new ActivatedRouteSnapshot(consumedSegments, result.parameters, Object.freeze(Object.assign({}, this.urlTree.queryParams)), this.urlTree.fragment, getData(route), getOutlet(route), route.component, route, getSourceSegmentGroup(rawSegment), getPathIndexShift(rawSegment) + consumedSegments.length, getResolve(route));
|
|
3187
3324
|
}
|
|
3188
3325
|
const childConfig = getChildConfig(route);
|
|
3189
|
-
const { segmentGroup, slicedSegments } = split
|
|
3326
|
+
const { segmentGroup, slicedSegments } = split(rawSegment, consumedSegments, rawSlicedSegments,
|
|
3327
|
+
// Filter out routes with redirectTo because we are trying to create activated route
|
|
3328
|
+
// snapshots and don't handle redirects here. That should have been done in
|
|
3329
|
+
// `applyRedirects`.
|
|
3330
|
+
childConfig.filter(c => c.redirectTo === undefined), this.relativeLinkResolution);
|
|
3190
3331
|
if (slicedSegments.length === 0 && segmentGroup.hasChildren()) {
|
|
3191
3332
|
const children = this.processChildren(childConfig, segmentGroup);
|
|
3333
|
+
if (children === null) {
|
|
3334
|
+
return null;
|
|
3335
|
+
}
|
|
3192
3336
|
return [new TreeNode(snapshot, children)];
|
|
3193
3337
|
}
|
|
3194
3338
|
if (childConfig.length === 0 && slicedSegments.length === 0) {
|
|
3195
3339
|
return [new TreeNode(snapshot, [])];
|
|
3196
3340
|
}
|
|
3197
|
-
const
|
|
3341
|
+
const matchedOnOutlet = getOutlet(route) === outlet;
|
|
3342
|
+
// If we matched a config due to empty path match on a different outlet, we need to continue
|
|
3343
|
+
// passing the current outlet for the segment rather than switch to PRIMARY.
|
|
3344
|
+
// Note that we switch to primary when we have a match because outlet configs look like this:
|
|
3345
|
+
// {path: 'a', outlet: 'a', children: [
|
|
3346
|
+
// {path: 'b', component: B},
|
|
3347
|
+
// {path: 'c', component: C},
|
|
3348
|
+
// ]}
|
|
3349
|
+
// Notice that the children of the named outlet are configured with the primary outlet
|
|
3350
|
+
const children = this.processSegment(childConfig, segmentGroup, slicedSegments, matchedOnOutlet ? PRIMARY_OUTLET : outlet);
|
|
3351
|
+
if (children === null) {
|
|
3352
|
+
return null;
|
|
3353
|
+
}
|
|
3198
3354
|
return [new TreeNode(snapshot, children)];
|
|
3199
3355
|
}
|
|
3200
3356
|
}
|
|
@@ -3216,24 +3372,31 @@ function getChildConfig(route) {
|
|
|
3216
3372
|
}
|
|
3217
3373
|
return [];
|
|
3218
3374
|
}
|
|
3219
|
-
function
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3375
|
+
function hasEmptyPathConfig(node) {
|
|
3376
|
+
const config = node.value.routeConfig;
|
|
3377
|
+
return config && config.path === '' && config.redirectTo === undefined;
|
|
3378
|
+
}
|
|
3379
|
+
/**
|
|
3380
|
+
* Finds `TreeNode`s with matching empty path route configs and merges them into `TreeNode` with the
|
|
3381
|
+
* children from each duplicate. This is necessary because different outlets can match a single
|
|
3382
|
+
* empty path route config and the results need to then be merged.
|
|
3383
|
+
*/
|
|
3384
|
+
function mergeEmptyPathMatches(nodes) {
|
|
3385
|
+
const result = [];
|
|
3386
|
+
for (const node of nodes) {
|
|
3387
|
+
if (!hasEmptyPathConfig(node)) {
|
|
3388
|
+
result.push(node);
|
|
3389
|
+
continue;
|
|
3390
|
+
}
|
|
3391
|
+
const duplicateEmptyPathNode = result.find(resultNode => node.value.routeConfig === resultNode.value.routeConfig);
|
|
3392
|
+
if (duplicateEmptyPathNode !== undefined) {
|
|
3393
|
+
duplicateEmptyPathNode.children.push(...node.children);
|
|
3394
|
+
}
|
|
3395
|
+
else {
|
|
3396
|
+
result.push(node);
|
|
3223
3397
|
}
|
|
3224
|
-
return { consumedSegments: [], lastChild: 0, parameters: {} };
|
|
3225
3398
|
}
|
|
3226
|
-
|
|
3227
|
-
const res = matcher(segments, segmentGroup, route);
|
|
3228
|
-
if (!res)
|
|
3229
|
-
throw new NoMatch$1();
|
|
3230
|
-
const posParams = {};
|
|
3231
|
-
forEach(res.posParams, (v, k) => {
|
|
3232
|
-
posParams[k] = v.path;
|
|
3233
|
-
});
|
|
3234
|
-
const parameters = res.consumed.length > 0 ? Object.assign(Object.assign({}, posParams), res.consumed[res.consumed.length - 1].parameters) :
|
|
3235
|
-
posParams;
|
|
3236
|
-
return { consumedSegments: res.consumed, lastChild: res.consumed.length, parameters };
|
|
3399
|
+
return result;
|
|
3237
3400
|
}
|
|
3238
3401
|
function checkOutletNameUniqueness(nodes) {
|
|
3239
3402
|
const names = {};
|
|
@@ -3263,70 +3426,6 @@ function getPathIndexShift(segmentGroup) {
|
|
|
3263
3426
|
}
|
|
3264
3427
|
return res - 1;
|
|
3265
3428
|
}
|
|
3266
|
-
function split$1(segmentGroup, consumedSegments, slicedSegments, config, relativeLinkResolution) {
|
|
3267
|
-
if (slicedSegments.length > 0 &&
|
|
3268
|
-
containsEmptyPathMatchesWithNamedOutlets(segmentGroup, slicedSegments, config)) {
|
|
3269
|
-
const s = new UrlSegmentGroup(consumedSegments, createChildrenForEmptyPaths(segmentGroup, consumedSegments, config, new UrlSegmentGroup(slicedSegments, segmentGroup.children)));
|
|
3270
|
-
s._sourceSegment = segmentGroup;
|
|
3271
|
-
s._segmentIndexShift = consumedSegments.length;
|
|
3272
|
-
return { segmentGroup: s, slicedSegments: [] };
|
|
3273
|
-
}
|
|
3274
|
-
if (slicedSegments.length === 0 &&
|
|
3275
|
-
containsEmptyPathMatches(segmentGroup, slicedSegments, config)) {
|
|
3276
|
-
const s = new UrlSegmentGroup(segmentGroup.segments, addEmptyPathsToChildrenIfNeeded(segmentGroup, consumedSegments, slicedSegments, config, segmentGroup.children, relativeLinkResolution));
|
|
3277
|
-
s._sourceSegment = segmentGroup;
|
|
3278
|
-
s._segmentIndexShift = consumedSegments.length;
|
|
3279
|
-
return { segmentGroup: s, slicedSegments };
|
|
3280
|
-
}
|
|
3281
|
-
const s = new UrlSegmentGroup(segmentGroup.segments, segmentGroup.children);
|
|
3282
|
-
s._sourceSegment = segmentGroup;
|
|
3283
|
-
s._segmentIndexShift = consumedSegments.length;
|
|
3284
|
-
return { segmentGroup: s, slicedSegments };
|
|
3285
|
-
}
|
|
3286
|
-
function addEmptyPathsToChildrenIfNeeded(segmentGroup, consumedSegments, slicedSegments, routes, children, relativeLinkResolution) {
|
|
3287
|
-
const res = {};
|
|
3288
|
-
for (const r of routes) {
|
|
3289
|
-
if (emptyPathMatch(segmentGroup, slicedSegments, r) && !children[getOutlet(r)]) {
|
|
3290
|
-
const s = new UrlSegmentGroup([], {});
|
|
3291
|
-
s._sourceSegment = segmentGroup;
|
|
3292
|
-
if (relativeLinkResolution === 'legacy') {
|
|
3293
|
-
s._segmentIndexShift = segmentGroup.segments.length;
|
|
3294
|
-
}
|
|
3295
|
-
else {
|
|
3296
|
-
s._segmentIndexShift = consumedSegments.length;
|
|
3297
|
-
}
|
|
3298
|
-
res[getOutlet(r)] = s;
|
|
3299
|
-
}
|
|
3300
|
-
}
|
|
3301
|
-
return Object.assign(Object.assign({}, children), res);
|
|
3302
|
-
}
|
|
3303
|
-
function createChildrenForEmptyPaths(segmentGroup, consumedSegments, routes, primarySegment) {
|
|
3304
|
-
const res = {};
|
|
3305
|
-
res[PRIMARY_OUTLET] = primarySegment;
|
|
3306
|
-
primarySegment._sourceSegment = segmentGroup;
|
|
3307
|
-
primarySegment._segmentIndexShift = consumedSegments.length;
|
|
3308
|
-
for (const r of routes) {
|
|
3309
|
-
if (r.path === '' && getOutlet(r) !== PRIMARY_OUTLET) {
|
|
3310
|
-
const s = new UrlSegmentGroup([], {});
|
|
3311
|
-
s._sourceSegment = segmentGroup;
|
|
3312
|
-
s._segmentIndexShift = consumedSegments.length;
|
|
3313
|
-
res[getOutlet(r)] = s;
|
|
3314
|
-
}
|
|
3315
|
-
}
|
|
3316
|
-
return res;
|
|
3317
|
-
}
|
|
3318
|
-
function containsEmptyPathMatchesWithNamedOutlets(segmentGroup, slicedSegments, routes) {
|
|
3319
|
-
return routes.some(r => emptyPathMatch(segmentGroup, slicedSegments, r) && getOutlet(r) !== PRIMARY_OUTLET);
|
|
3320
|
-
}
|
|
3321
|
-
function containsEmptyPathMatches(segmentGroup, slicedSegments, routes) {
|
|
3322
|
-
return routes.some(r => emptyPathMatch(segmentGroup, slicedSegments, r));
|
|
3323
|
-
}
|
|
3324
|
-
function emptyPathMatch(segmentGroup, slicedSegments, r) {
|
|
3325
|
-
if ((segmentGroup.hasChildren() || slicedSegments.length > 0) && r.pathMatch === 'full') {
|
|
3326
|
-
return false;
|
|
3327
|
-
}
|
|
3328
|
-
return r.path === '' && r.redirectTo === undefined;
|
|
3329
|
-
}
|
|
3330
3429
|
function getData(route) {
|
|
3331
3430
|
return route.data || {};
|
|
3332
3431
|
}
|
|
@@ -3342,10 +3441,8 @@ function getResolve(route) {
|
|
|
3342
3441
|
* found in the LICENSE file at https://angular.io/license
|
|
3343
3442
|
*/
|
|
3344
3443
|
function recognize$1(rootComponentType, config, serializer, paramsInheritanceStrategy, relativeLinkResolution) {
|
|
3345
|
-
return
|
|
3346
|
-
|
|
3347
|
-
.pipe(map(targetSnapshot => (Object.assign(Object.assign({}, t), { targetSnapshot }))))));
|
|
3348
|
-
};
|
|
3444
|
+
return mergeMap(t => recognize(rootComponentType, config, t.urlAfterRedirects, serializer(t.urlAfterRedirects), paramsInheritanceStrategy, relativeLinkResolution)
|
|
3445
|
+
.pipe(map(targetSnapshot => (Object.assign(Object.assign({}, t), { targetSnapshot })))));
|
|
3349
3446
|
}
|
|
3350
3447
|
|
|
3351
3448
|
/**
|
|
@@ -3356,17 +3453,15 @@ function recognize$1(rootComponentType, config, serializer, paramsInheritanceStr
|
|
|
3356
3453
|
* found in the LICENSE file at https://angular.io/license
|
|
3357
3454
|
*/
|
|
3358
3455
|
function resolveData(paramsInheritanceStrategy, moduleInjector) {
|
|
3359
|
-
return
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
}));
|
|
3369
|
-
};
|
|
3456
|
+
return mergeMap(t => {
|
|
3457
|
+
const { targetSnapshot, guards: { canActivateChecks } } = t;
|
|
3458
|
+
if (!canActivateChecks.length) {
|
|
3459
|
+
return of(t);
|
|
3460
|
+
}
|
|
3461
|
+
let canActivateChecksResolved = 0;
|
|
3462
|
+
return from(canActivateChecks)
|
|
3463
|
+
.pipe(concatMap(check => runResolve(check.route, targetSnapshot, paramsInheritanceStrategy, moduleInjector)), tap(() => canActivateChecksResolved++), takeLast(1), mergeMap(_ => canActivateChecksResolved === canActivateChecks.length ? of(t) : EMPTY));
|
|
3464
|
+
});
|
|
3370
3465
|
}
|
|
3371
3466
|
function runResolve(futureARS, futureRSS, paramsInheritanceStrategy, moduleInjector) {
|
|
3372
3467
|
const resolve = futureARS._resolve;
|
|
@@ -3415,15 +3510,13 @@ function getResolver(injectionToken, futureARS, futureRSS, moduleInjector) {
|
|
|
3415
3510
|
* it will wait before continuing with the original value.
|
|
3416
3511
|
*/
|
|
3417
3512
|
function switchTap(next) {
|
|
3418
|
-
return
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
}));
|
|
3426
|
-
};
|
|
3513
|
+
return switchMap(v => {
|
|
3514
|
+
const nextResult = next(v);
|
|
3515
|
+
if (nextResult) {
|
|
3516
|
+
return from(nextResult).pipe(map(() => v));
|
|
3517
|
+
}
|
|
3518
|
+
return of(v);
|
|
3519
|
+
});
|
|
3427
3520
|
}
|
|
3428
3521
|
|
|
3429
3522
|
/**
|
|
@@ -3768,7 +3861,7 @@ class Router {
|
|
|
3768
3861
|
this.ngModule = injector.get(NgModuleRef);
|
|
3769
3862
|
this.console = injector.get(ɵConsole);
|
|
3770
3863
|
const ngZone = injector.get(NgZone);
|
|
3771
|
-
this.isNgZoneEnabled = ngZone instanceof NgZone;
|
|
3864
|
+
this.isNgZoneEnabled = ngZone instanceof NgZone && NgZone.isInAngularZone();
|
|
3772
3865
|
this.resetConfig(config);
|
|
3773
3866
|
this.currentUrlTree = createEmptyUrlTree();
|
|
3774
3867
|
this.rawUrlTree = this.currentUrlTree;
|
|
@@ -3833,11 +3926,10 @@ class Router {
|
|
|
3833
3926
|
if (transition !== this.transitions.getValue()) {
|
|
3834
3927
|
return EMPTY;
|
|
3835
3928
|
}
|
|
3836
|
-
|
|
3929
|
+
// This delay is required to match old behavior that forced
|
|
3930
|
+
// navigation to always be async
|
|
3931
|
+
return Promise.resolve(t);
|
|
3837
3932
|
}),
|
|
3838
|
-
// This delay is required to match old behavior that forced navigation
|
|
3839
|
-
// to always be async
|
|
3840
|
-
switchMap(t => Promise.resolve(t)),
|
|
3841
3933
|
// ApplyRedirects
|
|
3842
3934
|
applyRedirects$1(this.ngModule.injector, this.configLoader, this.urlSerializer, this.config),
|
|
3843
3935
|
// Update the currentNavigation
|
|
@@ -3854,9 +3946,7 @@ class Router {
|
|
|
3854
3946
|
}
|
|
3855
3947
|
this.browserUrlTree = t.urlAfterRedirects;
|
|
3856
3948
|
}
|
|
3857
|
-
|
|
3858
|
-
// Fire RoutesRecognized
|
|
3859
|
-
tap(t => {
|
|
3949
|
+
// Fire RoutesRecognized
|
|
3860
3950
|
const routesRecognized = new RoutesRecognized(t.id, this.serializeUrl(t.extractedUrl), this.serializeUrl(t.urlAfterRedirects), t.targetSnapshot);
|
|
3861
3951
|
eventsSubject.next(routesRecognized);
|
|
3862
3952
|
}));
|
|
@@ -3908,7 +3998,6 @@ class Router {
|
|
|
3908
3998
|
error.url = t.guardsResult;
|
|
3909
3999
|
throw error;
|
|
3910
4000
|
}
|
|
3911
|
-
}), tap(t => {
|
|
3912
4001
|
const guardsEnd = new GuardsCheckEnd(t.id, this.serializeUrl(t.extractedUrl), this.serializeUrl(t.urlAfterRedirects), t.targetSnapshot, !!t.guardsResult);
|
|
3913
4002
|
this.triggerEvent(guardsEnd);
|
|
3914
4003
|
}), filter(t => {
|
|
@@ -3997,7 +4086,7 @@ class Router {
|
|
|
3997
4086
|
// navigation completes, there will be nothing in
|
|
3998
4087
|
// history.state.navigationId. This can cause sync problems with AngularJS
|
|
3999
4088
|
// sync code which looks for a value here in order to determine whether or
|
|
4000
|
-
// not to handle a given popstate event or to leave it to the
|
|
4089
|
+
// not to handle a given popstate event or to leave it to the Angular
|
|
4001
4090
|
// router.
|
|
4002
4091
|
this.resetUrlToCurrentUrlTree();
|
|
4003
4092
|
const navCancel = new NavigationCancel(t.id, this.serializeUrl(t.extractedUrl), `Navigation ID ${t.id} is not equal to the current navigation id ${this.navigationId}`);
|
|
@@ -4041,7 +4130,7 @@ class Router {
|
|
|
4041
4130
|
skipLocationChange: t.extras.skipLocationChange,
|
|
4042
4131
|
replaceUrl: this.urlUpdateStrategy === 'eager'
|
|
4043
4132
|
};
|
|
4044
|
-
|
|
4133
|
+
this.scheduleNavigation(mergedTree, 'imperative', null, extras, { resolve: t.resolve, reject: t.reject, promise: t.promise });
|
|
4045
4134
|
}, 0);
|
|
4046
4135
|
}
|
|
4047
4136
|
/* All other errors should reset to the router's internal URL reference to
|
|
@@ -4850,9 +4939,7 @@ class RouterLinkActive {
|
|
|
4850
4939
|
/** @nodoc */
|
|
4851
4940
|
ngAfterContentInit() {
|
|
4852
4941
|
// `of(null)` is used to force subscribe body to execute once immediately (like `startWith`).
|
|
4853
|
-
|
|
4854
|
-
.pipe(mergeAll())
|
|
4855
|
-
.subscribe(_ => {
|
|
4942
|
+
of(this.links.changes, this.linksWithHrefs.changes, of(null)).pipe(mergeAll()).subscribe(_ => {
|
|
4856
4943
|
this.update();
|
|
4857
4944
|
this.subscribeToEachLinkOnChanges();
|
|
4858
4945
|
});
|
|
@@ -5677,7 +5764,7 @@ function provideRouterInitializer() {
|
|
|
5677
5764
|
/**
|
|
5678
5765
|
* @publicApi
|
|
5679
5766
|
*/
|
|
5680
|
-
const VERSION = new Version('11.0.
|
|
5767
|
+
const VERSION = new Version('11.0.9');
|
|
5681
5768
|
|
|
5682
5769
|
/**
|
|
5683
5770
|
* @license
|