UNPKG

@angular/router

Version:
1,324 lines (1,304 loc) 250 kB
/** * @license Angular v7.0.1 * (c) 2010-2018 Google, Inc. https://angular.io/ * License: MIT */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('rxjs'), require('rxjs/operators'), require('@angular/common'), require('@angular/platform-browser')) : typeof define === 'function' && define.amd ? define('@angular/router', ['exports', '@angular/core', 'rxjs', 'rxjs/operators', '@angular/common', '@angular/platform-browser'], factory) : (factory((global.ng = global.ng || {}, global.ng.router = {}),global.ng.core,global.rxjs,global.rxjs.operators,global.ng.common,global.ng.platformBrowser)); }(this, (function (exports,core,rxjs,operators,common,platformBrowser) { 'use strict'; /*! ***************************************************************************** Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT. See the Apache Version 2.0 License for specific language governing permissions and limitations under the License. ***************************************************************************** */ /* global Reflect, Promise */ var extendStatics = function(d, b) { extendStatics = Object.setPrototypeOf || ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; return extendStatics(d, b); }; function __extends(d, b) { extendStatics(d, b); function __() { this.constructor = d; } d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); } var __assign = function() { __assign = Object.assign || function __assign(t) { for (var s, i = 1, n = arguments.length; i < n; i++) { s = arguments[i]; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; } return t; }; return __assign.apply(this, arguments); }; function __decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } function __param(paramIndex, decorator) { return function (target, key) { decorator(target, key, paramIndex); } } function __metadata(metadataKey, metadataValue) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); } function __values(o) { var m = typeof Symbol === "function" && o[Symbol.iterator], i = 0; if (m) return m.call(o); return { next: function () { if (o && i >= o.length) o = void 0; return { value: o && o[i++], done: !o }; } }; } function __read(o, n) { var m = typeof Symbol === "function" && o[Symbol.iterator]; if (!m) return o; var i = m.call(o), r, ar = [], e; try { while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); } catch (error) { e = { error: error }; } finally { try { if (r && !r.done && (m = i["return"])) m.call(i); } finally { if (e) throw e.error; } } return ar; } function __spread() { for (var ar = [], i = 0; i < arguments.length; i++) ar = ar.concat(__read(arguments[i])); return ar; } /** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** * @description * * Base for events the Router goes through, as opposed to events tied to a specific * Route. `RouterEvent`s will only be fired one time for any given navigation. * * Example: * * ``` * class MyService { * constructor(public router: Router, logger: Logger) { * router.events.filter(e => e instanceof RouterEvent).subscribe(e => { * logger.log(e.id, e.url); * }); * } * } * ``` * * @publicApi */ var RouterEvent = /** @class */ (function () { function RouterEvent( /** @docsNotRequired */ id, /** @docsNotRequired */ url) { this.id = id; this.url = url; } return RouterEvent; }()); /** * @description * * Represents an event triggered when a navigation starts. * * @publicApi */ var NavigationStart = /** @class */ (function (_super) { __extends(NavigationStart, _super); function NavigationStart( /** @docsNotRequired */ id, /** @docsNotRequired */ url, /** @docsNotRequired */ navigationTrigger, /** @docsNotRequired */ restoredState) { if (navigationTrigger === void 0) { navigationTrigger = 'imperative'; } if (restoredState === void 0) { restoredState = null; } var _this = _super.call(this, id, url) || this; _this.navigationTrigger = navigationTrigger; _this.restoredState = restoredState; return _this; } /** @docsNotRequired */ NavigationStart.prototype.toString = function () { return "NavigationStart(id: " + this.id + ", url: '" + this.url + "')"; }; return NavigationStart; }(RouterEvent)); /** * @description * * Represents an event triggered when a navigation ends successfully. * * @publicApi */ var NavigationEnd = /** @class */ (function (_super) { __extends(NavigationEnd, _super); function NavigationEnd( /** @docsNotRequired */ id, /** @docsNotRequired */ url, /** @docsNotRequired */ urlAfterRedirects) { var _this = _super.call(this, id, url) || this; _this.urlAfterRedirects = urlAfterRedirects; return _this; } /** @docsNotRequired */ NavigationEnd.prototype.toString = function () { return "NavigationEnd(id: " + this.id + ", url: '" + this.url + "', urlAfterRedirects: '" + this.urlAfterRedirects + "')"; }; return NavigationEnd; }(RouterEvent)); /** * @description * * Represents an event triggered when a navigation is canceled. * * @publicApi */ var NavigationCancel = /** @class */ (function (_super) { __extends(NavigationCancel, _super); function NavigationCancel( /** @docsNotRequired */ id, /** @docsNotRequired */ url, /** @docsNotRequired */ reason) { var _this = _super.call(this, id, url) || this; _this.reason = reason; return _this; } /** @docsNotRequired */ NavigationCancel.prototype.toString = function () { return "NavigationCancel(id: " + this.id + ", url: '" + this.url + "')"; }; return NavigationCancel; }(RouterEvent)); /** * @description * * Represents an event triggered when a navigation fails due to an unexpected error. * * @publicApi */ var NavigationError = /** @class */ (function (_super) { __extends(NavigationError, _super); function NavigationError( /** @docsNotRequired */ id, /** @docsNotRequired */ url, /** @docsNotRequired */ error) { var _this = _super.call(this, id, url) || this; _this.error = error; return _this; } /** @docsNotRequired */ NavigationError.prototype.toString = function () { return "NavigationError(id: " + this.id + ", url: '" + this.url + "', error: " + this.error + ")"; }; return NavigationError; }(RouterEvent)); /** * @description * * Represents an event triggered when routes are recognized. * * @publicApi */ var RoutesRecognized = /** @class */ (function (_super) { __extends(RoutesRecognized, _super); function RoutesRecognized( /** @docsNotRequired */ id, /** @docsNotRequired */ url, /** @docsNotRequired */ urlAfterRedirects, /** @docsNotRequired */ state) { var _this = _super.call(this, id, url) || this; _this.urlAfterRedirects = urlAfterRedirects; _this.state = state; return _this; } /** @docsNotRequired */ RoutesRecognized.prototype.toString = function () { return "RoutesRecognized(id: " + this.id + ", url: '" + this.url + "', urlAfterRedirects: '" + this.urlAfterRedirects + "', state: " + this.state + ")"; }; return RoutesRecognized; }(RouterEvent)); /** * @description * * Represents the start of the Guard phase of routing. * * @publicApi */ var GuardsCheckStart = /** @class */ (function (_super) { __extends(GuardsCheckStart, _super); function GuardsCheckStart( /** @docsNotRequired */ id, /** @docsNotRequired */ url, /** @docsNotRequired */ urlAfterRedirects, /** @docsNotRequired */ state) { var _this = _super.call(this, id, url) || this; _this.urlAfterRedirects = urlAfterRedirects; _this.state = state; return _this; } GuardsCheckStart.prototype.toString = function () { return "GuardsCheckStart(id: " + this.id + ", url: '" + this.url + "', urlAfterRedirects: '" + this.urlAfterRedirects + "', state: " + this.state + ")"; }; return GuardsCheckStart; }(RouterEvent)); /** * @description * * Represents the end of the Guard phase of routing. * * @publicApi */ var GuardsCheckEnd = /** @class */ (function (_super) { __extends(GuardsCheckEnd, _super); function GuardsCheckEnd( /** @docsNotRequired */ id, /** @docsNotRequired */ url, /** @docsNotRequired */ urlAfterRedirects, /** @docsNotRequired */ state, /** @docsNotRequired */ shouldActivate) { var _this = _super.call(this, id, url) || this; _this.urlAfterRedirects = urlAfterRedirects; _this.state = state; _this.shouldActivate = shouldActivate; return _this; } GuardsCheckEnd.prototype.toString = function () { return "GuardsCheckEnd(id: " + this.id + ", url: '" + this.url + "', urlAfterRedirects: '" + this.urlAfterRedirects + "', state: " + this.state + ", shouldActivate: " + this.shouldActivate + ")"; }; return GuardsCheckEnd; }(RouterEvent)); /** * @description * * Represents the start of the Resolve phase of routing. The timing of this * event may change, thus it's experimental. In the current iteration it will run * in the "resolve" phase whether there's things to resolve or not. In the future this * behavior may change to only run when there are things to be resolved. * * @publicApi */ var ResolveStart = /** @class */ (function (_super) { __extends(ResolveStart, _super); function ResolveStart( /** @docsNotRequired */ id, /** @docsNotRequired */ url, /** @docsNotRequired */ urlAfterRedirects, /** @docsNotRequired */ state) { var _this = _super.call(this, id, url) || this; _this.urlAfterRedirects = urlAfterRedirects; _this.state = state; return _this; } ResolveStart.prototype.toString = function () { return "ResolveStart(id: " + this.id + ", url: '" + this.url + "', urlAfterRedirects: '" + this.urlAfterRedirects + "', state: " + this.state + ")"; }; return ResolveStart; }(RouterEvent)); /** * @description * * Represents the end of the Resolve phase of routing. See note on * `ResolveStart` for use of this experimental API. * * @publicApi */ var ResolveEnd = /** @class */ (function (_super) { __extends(ResolveEnd, _super); function ResolveEnd( /** @docsNotRequired */ id, /** @docsNotRequired */ url, /** @docsNotRequired */ urlAfterRedirects, /** @docsNotRequired */ state) { var _this = _super.call(this, id, url) || this; _this.urlAfterRedirects = urlAfterRedirects; _this.state = state; return _this; } ResolveEnd.prototype.toString = function () { return "ResolveEnd(id: " + this.id + ", url: '" + this.url + "', urlAfterRedirects: '" + this.urlAfterRedirects + "', state: " + this.state + ")"; }; return ResolveEnd; }(RouterEvent)); /** * @description * * Represents an event triggered before lazy loading a route config. * * @publicApi */ var RouteConfigLoadStart = /** @class */ (function () { function RouteConfigLoadStart( /** @docsNotRequired */ route) { this.route = route; } RouteConfigLoadStart.prototype.toString = function () { return "RouteConfigLoadStart(path: " + this.route.path + ")"; }; return RouteConfigLoadStart; }()); /** * @description * * Represents an event triggered when a route has been lazy loaded. * * @publicApi */ var RouteConfigLoadEnd = /** @class */ (function () { function RouteConfigLoadEnd( /** @docsNotRequired */ route) { this.route = route; } RouteConfigLoadEnd.prototype.toString = function () { return "RouteConfigLoadEnd(path: " + this.route.path + ")"; }; return RouteConfigLoadEnd; }()); /** * @description * * Represents the start of end of the Resolve phase of routing. See note on * `ChildActivationEnd` for use of this experimental API. * * @publicApi */ var ChildActivationStart = /** @class */ (function () { function ChildActivationStart( /** @docsNotRequired */ snapshot) { this.snapshot = snapshot; } ChildActivationStart.prototype.toString = function () { var path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || ''; return "ChildActivationStart(path: '" + path + "')"; }; return ChildActivationStart; }()); /** * @description * * Represents the start of end of the Resolve phase of routing. See note on * `ChildActivationStart` for use of this experimental API. * * @publicApi */ var ChildActivationEnd = /** @class */ (function () { function ChildActivationEnd( /** @docsNotRequired */ snapshot) { this.snapshot = snapshot; } ChildActivationEnd.prototype.toString = function () { var path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || ''; return "ChildActivationEnd(path: '" + path + "')"; }; return ChildActivationEnd; }()); /** * @description * * Represents the start of end of the Resolve phase of routing. See note on * `ActivationEnd` for use of this experimental API. * * @publicApi */ var ActivationStart = /** @class */ (function () { function ActivationStart( /** @docsNotRequired */ snapshot) { this.snapshot = snapshot; } ActivationStart.prototype.toString = function () { var path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || ''; return "ActivationStart(path: '" + path + "')"; }; return ActivationStart; }()); /** * @description * * Represents the start of end of the Resolve phase of routing. See note on * `ActivationStart` for use of this experimental API. * * @publicApi */ var ActivationEnd = /** @class */ (function () { function ActivationEnd( /** @docsNotRequired */ snapshot) { this.snapshot = snapshot; } ActivationEnd.prototype.toString = function () { var path = this.snapshot.routeConfig && this.snapshot.routeConfig.path || ''; return "ActivationEnd(path: '" + path + "')"; }; return ActivationEnd; }()); /** * @description * * Represents a scrolling event. * * @publicApi */ var Scroll = /** @class */ (function () { function Scroll( /** @docsNotRequired */ routerEvent, /** @docsNotRequired */ position, /** @docsNotRequired */ anchor) { this.routerEvent = routerEvent; this.position = position; this.anchor = anchor; } Scroll.prototype.toString = function () { var pos = this.position ? this.position[0] + ", " + this.position[1] : null; return "Scroll(anchor: '" + this.anchor + "', position: '" + pos + "')"; }; return Scroll; }()); /** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** * This component is used internally within the router to be a placeholder when an empty * router-outlet is needed. For example, with a config such as: * * `{path: 'parent', outlet: 'nav', children: [...]}` * * In order to render, there needs to be a component on this config, which will default * to this `EmptyOutletComponent`. */ var EmptyOutletComponent = /** @class */ (function () { function EmptyOutletComponent() { } EmptyOutletComponent = __decorate([ core.Component({ template: "<router-outlet></router-outlet>" }) ], EmptyOutletComponent); return EmptyOutletComponent; }()); /** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ /** * @description * * Name of the primary outlet. * * @publicApi */ var PRIMARY_OUTLET = 'primary'; var ParamsAsMap = /** @class */ (function () { function ParamsAsMap(params) { this.params = params || {}; } ParamsAsMap.prototype.has = function (name) { return this.params.hasOwnProperty(name); }; ParamsAsMap.prototype.get = function (name) { if (this.has(name)) { var v = this.params[name]; return Array.isArray(v) ? v[0] : v; } return null; }; ParamsAsMap.prototype.getAll = function (name) { if (this.has(name)) { var v = this.params[name]; return Array.isArray(v) ? v : [v]; } return []; }; Object.defineProperty(ParamsAsMap.prototype, "keys", { get: function () { return Object.keys(this.params); }, enumerable: true, configurable: true }); return ParamsAsMap; }()); /** * Convert a `Params` instance to a `ParamMap`. * * @publicApi */ function convertToParamMap(params) { return new ParamsAsMap(params); } var NAVIGATION_CANCELING_ERROR = 'ngNavigationCancelingError'; function navigationCancelingError(message) { var error = Error('NavigationCancelingError: ' + message); error[NAVIGATION_CANCELING_ERROR] = true; return error; } function isNavigationCancelingError(error) { return error && error[NAVIGATION_CANCELING_ERROR]; } // Matches the route configuration (`route`) against the actual URL (`segments`). function defaultUrlMatcher(segments, segmentGroup, route) { var parts = route.path.split('/'); if (parts.length > segments.length) { // The actual URL is shorter than the config, no match return null; } if (route.pathMatch === 'full' && (segmentGroup.hasChildren() || parts.length < segments.length)) { // The config is longer than the actual URL but we are looking for a full match, return null return null; } var posParams = {}; // Check each config part against the actual URL for (var index = 0; index < parts.length; index++) { var part = parts[index]; var segment = segments[index]; var isParameter = part.startsWith(':'); if (isParameter) { posParams[part.substring(1)] = segment; } else if (part !== segment.path) { // The actual URL part does not match the config, no match return null; } } return { consumed: segments.slice(0, parts.length), posParams: posParams }; } /** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ var LoadedRouterConfig = /** @class */ (function () { function LoadedRouterConfig(routes, module) { this.routes = routes; this.module = module; } return LoadedRouterConfig; }()); function validateConfig(config, parentPath) { if (parentPath === void 0) { parentPath = ''; } // forEach doesn't iterate undefined values for (var i = 0; i < config.length; i++) { var route = config[i]; var fullPath = getFullPath(parentPath, route); validateNode(route, fullPath); } } function validateNode(route, fullPath) { if (!route) { throw new Error("\n Invalid configuration of route '" + fullPath + "': Encountered undefined route.\n The reason might be an extra comma.\n\n Example:\n const routes: Routes = [\n { path: '', redirectTo: '/dashboard', pathMatch: 'full' },\n { path: 'dashboard', component: DashboardComponent },, << two commas\n { path: 'detail/:id', component: HeroDetailComponent }\n ];\n "); } if (Array.isArray(route)) { throw new Error("Invalid configuration of route '" + fullPath + "': Array cannot be specified"); } if (!route.component && !route.children && !route.loadChildren && (route.outlet && route.outlet !== PRIMARY_OUTLET)) { throw new Error("Invalid configuration of route '" + fullPath + "': a componentless route without children or loadChildren cannot have a named outlet set"); } if (route.redirectTo && route.children) { throw new Error("Invalid configuration of route '" + fullPath + "': redirectTo and children cannot be used together"); } if (route.redirectTo && route.loadChildren) { throw new Error("Invalid configuration of route '" + fullPath + "': redirectTo and loadChildren cannot be used together"); } if (route.children && route.loadChildren) { throw new Error("Invalid configuration of route '" + fullPath + "': children and loadChildren cannot be used together"); } if (route.redirectTo && route.component) { throw new Error("Invalid configuration of route '" + fullPath + "': redirectTo and component cannot be used together"); } if (route.path && route.matcher) { throw new Error("Invalid configuration of route '" + fullPath + "': path and matcher cannot be used together"); } if (route.redirectTo === void 0 && !route.component && !route.children && !route.loadChildren) { throw new Error("Invalid configuration of route '" + fullPath + "'. One of the following must be provided: component, redirectTo, children or loadChildren"); } if (route.path === void 0 && route.matcher === void 0) { throw new Error("Invalid configuration of route '" + fullPath + "': routes must have either a path or a matcher specified"); } if (typeof route.path === 'string' && route.path.charAt(0) === '/') { throw new Error("Invalid configuration of route '" + fullPath + "': path cannot start with a slash"); } if (route.path === '' && route.redirectTo !== void 0 && route.pathMatch === void 0) { var exp = "The default value of 'pathMatch' is 'prefix', but often the intent is to use 'full'."; throw new Error("Invalid configuration of route '{path: \"" + fullPath + "\", redirectTo: \"" + route.redirectTo + "\"}': please provide 'pathMatch'. " + exp); } if (route.pathMatch !== void 0 && route.pathMatch !== 'full' && route.pathMatch !== 'prefix') { throw new Error("Invalid configuration of route '" + fullPath + "': pathMatch can only be set to 'prefix' or 'full'"); } if (route.children) { validateConfig(route.children, fullPath); } } function getFullPath(parentPath, currentRoute) { if (!currentRoute) { return parentPath; } if (!parentPath && !currentRoute.path) { return ''; } else if (parentPath && !currentRoute.path) { return parentPath + "/"; } else if (!parentPath && currentRoute.path) { return currentRoute.path; } else { return parentPath + "/" + currentRoute.path; } } /** * Makes a copy of the config and adds any default required properties. */ function standardizeConfig(r) { var children = r.children && r.children.map(standardizeConfig); var c = children ? __assign({}, r, { children: children }) : __assign({}, r); if (!c.component && (children || c.loadChildren) && (c.outlet && c.outlet !== PRIMARY_OUTLET)) { c.component = EmptyOutletComponent; } return c; } /** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ function shallowEqualArrays(a, b) { if (a.length !== b.length) return false; for (var i = 0; i < a.length; ++i) { if (!shallowEqual(a[i], b[i])) return false; } return true; } function shallowEqual(a, b) { var k1 = Object.keys(a); var k2 = Object.keys(b); if (k1.length != k2.length) { return false; } var key; for (var i = 0; i < k1.length; i++) { key = k1[i]; if (a[key] !== b[key]) { return false; } } return true; } /** * Flattens single-level nested arrays. */ function flatten(arr) { return Array.prototype.concat.apply([], arr); } /** * Return the last element of an array. */ function last(a) { return a.length > 0 ? a[a.length - 1] : null; } function forEach(map, callback) { for (var prop in map) { if (map.hasOwnProperty(prop)) { callback(map[prop], prop); } } } function waitForMap(obj, fn) { if (Object.keys(obj).length === 0) { return rxjs.of({}); } var waitHead = []; var waitTail = []; var res = {}; forEach(obj, function (a, k) { var mapped = fn(k, a).pipe(operators.map(function (r) { return res[k] = r; })); if (k === PRIMARY_OUTLET) { waitHead.push(mapped); } else { waitTail.push(mapped); } }); // Closure compiler has problem with using spread operator here. So just using Array.concat. return rxjs.of.apply(null, waitHead.concat(waitTail)).pipe(operators.concatAll(), operators.last(), operators.map(function () { return res; })); } /** * ANDs Observables by merging all input observables, reducing to an Observable verifying all * input Observables return `true`. */ function andObservables(observables) { return observables.pipe(operators.mergeAll(), operators.every(function (result) { return result === true; })); } function wrapIntoObservable(value) { if (core.ɵisObservable(value)) { return value; } if (core.ɵisPromise(value)) { // Use `Promise.resolve()` to wrap promise-like instances. // Required ie when a Resolver returns a AngularJS `$q` promise to correctly trigger the // change detection. return rxjs.from(Promise.resolve(value)); } return rxjs.of(value); } /** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ function createEmptyUrlTree() { return new UrlTree(new UrlSegmentGroup([], {}), {}, null); } function containsTree(container, containee, exact) { if (exact) { return equalQueryParams(container.queryParams, containee.queryParams) && equalSegmentGroups(container.root, containee.root); } return containsQueryParams(container.queryParams, containee.queryParams) && containsSegmentGroup(container.root, containee.root); } function equalQueryParams(container, containee) { // TODO: This does not handle array params correctly. return shallowEqual(container, containee); } function equalSegmentGroups(container, containee) { if (!equalPath(container.segments, containee.segments)) return false; if (container.numberOfChildren !== containee.numberOfChildren) return false; for (var c in containee.children) { if (!container.children[c]) return false; if (!equalSegmentGroups(container.children[c], containee.children[c])) return false; } return true; } function containsQueryParams(container, containee) { // TODO: This does not handle array params correctly. return Object.keys(containee).length <= Object.keys(container).length && Object.keys(containee).every(function (key) { return containee[key] === container[key]; }); } function containsSegmentGroup(container, containee) { return containsSegmentGroupHelper(container, containee, containee.segments); } function containsSegmentGroupHelper(container, containee, containeePaths) { if (container.segments.length > containeePaths.length) { var current = container.segments.slice(0, containeePaths.length); if (!equalPath(current, containeePaths)) return false; if (containee.hasChildren()) return false; return true; } else if (container.segments.length === containeePaths.length) { if (!equalPath(container.segments, containeePaths)) return false; for (var c in containee.children) { if (!container.children[c]) return false; if (!containsSegmentGroup(container.children[c], containee.children[c])) return false; } return true; } else { var current = containeePaths.slice(0, container.segments.length); var next = containeePaths.slice(container.segments.length); if (!equalPath(container.segments, current)) return false; if (!container.children[PRIMARY_OUTLET]) return false; return containsSegmentGroupHelper(container.children[PRIMARY_OUTLET], containee, next); } } /** * @description * * Represents the parsed URL. * * Since a router state is a tree, and the URL is nothing but a serialized state, the URL is a * serialized tree. * UrlTree is a data structure that provides a lot of affordances in dealing with URLs * * @usageNotes * ### Example * * ``` * @Component({templateUrl:'template.html'}) * class MyComponent { * constructor(router: Router) { * const tree: UrlTree = * router.parseUrl('/team/33/(user/victor//support:help)?debug=true#fragment'); * const f = tree.fragment; // return 'fragment' * const q = tree.queryParams; // returns {debug: 'true'} * const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET]; * const s: UrlSegment[] = g.segments; // returns 2 segments 'team' and '33' * g.children[PRIMARY_OUTLET].segments; // returns 2 segments 'user' and 'victor' * g.children['support'].segments; // return 1 segment 'help' * } * } * ``` * * @publicApi */ var UrlTree = /** @class */ (function () { /** @internal */ function UrlTree( /** The root segment group of the URL tree */ root, /** The query params of the URL */ queryParams, /** The fragment of the URL */ fragment) { this.root = root; this.queryParams = queryParams; this.fragment = fragment; } Object.defineProperty(UrlTree.prototype, "queryParamMap", { get: function () { if (!this._queryParamMap) { this._queryParamMap = convertToParamMap(this.queryParams); } return this._queryParamMap; }, enumerable: true, configurable: true }); /** @docsNotRequired */ UrlTree.prototype.toString = function () { return DEFAULT_SERIALIZER.serialize(this); }; return UrlTree; }()); /** * @description * * Represents the parsed URL segment group. * * See `UrlTree` for more information. * * @publicApi */ var UrlSegmentGroup = /** @class */ (function () { function UrlSegmentGroup( /** The URL segments of this group. See `UrlSegment` for more information */ segments, /** The list of children of this group */ children) { var _this = this; this.segments = segments; this.children = children; /** The parent node in the url tree */ this.parent = null; forEach(children, function (v, k) { return v.parent = _this; }); } /** Whether the segment has child segments */ UrlSegmentGroup.prototype.hasChildren = function () { return this.numberOfChildren > 0; }; Object.defineProperty(UrlSegmentGroup.prototype, "numberOfChildren", { /** Number of child segments */ get: function () { return Object.keys(this.children).length; }, enumerable: true, configurable: true }); /** @docsNotRequired */ UrlSegmentGroup.prototype.toString = function () { return serializePaths(this); }; return UrlSegmentGroup; }()); /** * @description * * Represents a single URL segment. * * A UrlSegment is a part of a URL between the two slashes. It contains a path and the matrix * parameters associated with the segment. * * @usageNotes * ### Example * * ``` * @Component({templateUrl:'template.html'}) * class MyComponent { * constructor(router: Router) { * const tree: UrlTree = router.parseUrl('/team;id=33'); * const g: UrlSegmentGroup = tree.root.children[PRIMARY_OUTLET]; * const s: UrlSegment[] = g.segments; * s[0].path; // returns 'team' * s[0].parameters; // returns {id: 33} * } * } * ``` * * @publicApi */ var UrlSegment = /** @class */ (function () { function UrlSegment( /** The path part of a URL segment */ path, /** The matrix parameters associated with a segment */ parameters) { this.path = path; this.parameters = parameters; } Object.defineProperty(UrlSegment.prototype, "parameterMap", { get: function () { if (!this._parameterMap) { this._parameterMap = convertToParamMap(this.parameters); } return this._parameterMap; }, enumerable: true, configurable: true }); /** @docsNotRequired */ UrlSegment.prototype.toString = function () { return serializePath(this); }; return UrlSegment; }()); function equalSegments(as, bs) { return equalPath(as, bs) && as.every(function (a, i) { return shallowEqual(a.parameters, bs[i].parameters); }); } function equalPath(as, bs) { if (as.length !== bs.length) return false; return as.every(function (a, i) { return a.path === bs[i].path; }); } function mapChildrenIntoArray(segment, fn) { var res = []; forEach(segment.children, function (child, childOutlet) { if (childOutlet === PRIMARY_OUTLET) { res = res.concat(fn(child, childOutlet)); } }); forEach(segment.children, function (child, childOutlet) { if (childOutlet !== PRIMARY_OUTLET) { res = res.concat(fn(child, childOutlet)); } }); return res; } /** * @description * * Serializes and deserializes a URL string into a URL tree. * * The url serialization strategy is customizable. You can * make all URLs case insensitive by providing a custom UrlSerializer. * * See `DefaultUrlSerializer` for an example of a URL serializer. * * @publicApi */ var UrlSerializer = /** @class */ (function () { function UrlSerializer() { } return UrlSerializer; }()); /** * @description * * A default implementation of the `UrlSerializer`. * * Example URLs: * * ``` * /inbox/33(popup:compose) * /inbox/33;open=true/messages/44 * ``` * * DefaultUrlSerializer uses parentheses to serialize secondary segments (e.g., popup:compose), the * colon syntax to specify the outlet, and the ';parameter=value' syntax (e.g., open=true) to * specify route specific parameters. * * @publicApi */ var DefaultUrlSerializer = /** @class */ (function () { function DefaultUrlSerializer() { } /** Parses a url into a `UrlTree` */ DefaultUrlSerializer.prototype.parse = function (url) { var p = new UrlParser(url); return new UrlTree(p.parseRootSegment(), p.parseQueryParams(), p.parseFragment()); }; /** Converts a `UrlTree` into a url */ DefaultUrlSerializer.prototype.serialize = function (tree) { var segment = "/" + serializeSegment(tree.root, true); var query = serializeQueryParams(tree.queryParams); var fragment = typeof tree.fragment === "string" ? "#" + encodeUriFragment(tree.fragment) : ''; return "" + segment + query + fragment; }; return DefaultUrlSerializer; }()); var DEFAULT_SERIALIZER = new DefaultUrlSerializer(); function serializePaths(segment) { return segment.segments.map(function (p) { return serializePath(p); }).join('/'); } function serializeSegment(segment, root) { if (!segment.hasChildren()) { return serializePaths(segment); } if (root) { var primary = segment.children[PRIMARY_OUTLET] ? serializeSegment(segment.children[PRIMARY_OUTLET], false) : ''; var children_1 = []; forEach(segment.children, function (v, k) { if (k !== PRIMARY_OUTLET) { children_1.push(k + ":" + serializeSegment(v, false)); } }); return children_1.length > 0 ? primary + "(" + children_1.join('//') + ")" : primary; } else { var children = mapChildrenIntoArray(segment, function (v, k) { if (k === PRIMARY_OUTLET) { return [serializeSegment(segment.children[PRIMARY_OUTLET], false)]; } return [k + ":" + serializeSegment(v, false)]; }); return serializePaths(segment) + "/(" + children.join('//') + ")"; } } /** * Encodes a URI string with the default encoding. This function will only ever be called from * `encodeUriQuery` or `encodeUriSegment` as it's the base set of encodings to be used. We need * a custom encoding because encodeURIComponent is too aggressive and encodes stuff that doesn't * have to be encoded per https://url.spec.whatwg.org. */ function encodeUriString(s) { return encodeURIComponent(s) .replace(/%40/g, '@') .replace(/%3A/gi, ':') .replace(/%24/g, '$') .replace(/%2C/gi, ','); } /** * This function should be used to encode both keys and values in a query string key/value. In * the following URL, you need to call encodeUriQuery on "k" and "v": * * http://www.site.org/html;mk=mv?k=v#f */ function encodeUriQuery(s) { return encodeUriString(s).replace(/%3B/gi, ';'); } /** * This function should be used to encode a URL fragment. In the following URL, you need to call * encodeUriFragment on "f": * * http://www.site.org/html;mk=mv?k=v#f */ function encodeUriFragment(s) { return encodeURI(s); } /** * This function should be run on any URI segment as well as the key and value in a key/value * pair for matrix params. In the following URL, you need to call encodeUriSegment on "html", * "mk", and "mv": * * http://www.site.org/html;mk=mv?k=v#f */ function encodeUriSegment(s) { return encodeUriString(s).replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/%26/gi, '&'); } function decode(s) { return decodeURIComponent(s); } // Query keys/values should have the "+" replaced first, as "+" in a query string is " ". // decodeURIComponent function will not decode "+" as a space. function decodeQuery(s) { return decode(s.replace(/\+/g, '%20')); } function serializePath(path) { return "" + encodeUriSegment(path.path) + serializeMatrixParams(path.parameters); } function serializeMatrixParams(params) { return Object.keys(params) .map(function (key) { return ";" + encodeUriSegment(key) + "=" + encodeUriSegment(params[key]); }) .join(''); } function serializeQueryParams(params) { var strParams = Object.keys(params).map(function (name) { var value = params[name]; return Array.isArray(value) ? value.map(function (v) { return encodeUriQuery(name) + "=" + encodeUriQuery(v); }).join('&') : encodeUriQuery(name) + "=" + encodeUriQuery(value); }); return strParams.length ? "?" + strParams.join("&") : ''; } var SEGMENT_RE = /^[^\/()?;=#]+/; function matchSegments(str) { var match = str.match(SEGMENT_RE); return match ? match[0] : ''; } var QUERY_PARAM_RE = /^[^=?&#]+/; // Return the name of the query param at the start of the string or an empty string function matchQueryParams(str) { var match = str.match(QUERY_PARAM_RE); return match ? match[0] : ''; } var QUERY_PARAM_VALUE_RE = /^[^?&#]+/; // Return the value of the query param at the start of the string or an empty string function matchUrlQueryParamValue(str) { var match = str.match(QUERY_PARAM_VALUE_RE); return match ? match[0] : ''; } var UrlParser = /** @class */ (function () { function UrlParser(url) { this.url = url; this.remaining = url; } UrlParser.prototype.parseRootSegment = function () { this.consumeOptional('/'); if (this.remaining === '' || this.peekStartsWith('?') || this.peekStartsWith('#')) { return new UrlSegmentGroup([], {}); } // The root segment group never has segments return new UrlSegmentGroup([], this.parseChildren()); }; UrlParser.prototype.parseQueryParams = function () { var params = {}; if (this.consumeOptional('?')) { do { this.parseQueryParam(params); } while (this.consumeOptional('&')); } return params; }; UrlParser.prototype.parseFragment = function () { return this.consumeOptional('#') ? decodeURIComponent(this.remaining) : null; }; UrlParser.prototype.parseChildren = function () { if (this.remaining === '') { return {}; } this.consumeOptional('/'); var segments = []; if (!this.peekStartsWith('(')) { segments.push(this.parseSegment()); } while (this.peekStartsWith('/') && !this.peekStartsWith('//') && !this.peekStartsWith('/(')) { this.capture('/'); segments.push(this.parseSegment()); } var children = {}; if (this.peekStartsWith('/(')) { this.capture('/'); children = this.parseParens(true); } var res = {}; if (this.peekStartsWith('(')) { res = this.parseParens(false); } if (segments.length > 0 || Object.keys(children).length > 0) { res[PRIMARY_OUTLET] = new UrlSegmentGroup(segments, children); } return res; }; // parse a segment with its matrix parameters // ie `name;k1=v1;k2` UrlParser.prototype.parseSegment = function () { var path = matchSegments(this.remaining); if (path === '' && this.peekStartsWith(';')) { throw new Error("Empty path url segment cannot have parameters: '" + this.remaining + "'."); } this.capture(path); return new UrlSegment(decode(path), this.parseMatrixParams()); }; UrlParser.prototype.parseMatrixParams = function () { var params = {}; while (this.consumeOptional(';')) { this.parseParam(params); } return params; }; UrlParser.prototype.parseParam = function (params) { var key = matchSegments(this.remaining); if (!key) { return; } this.capture(key); var value = ''; if (this.consumeOptional('=')) { var valueMatch = matchSegments(this.remaining); if (valueMatch) { value = valueMatch; this.capture(value); } } params[decode(key)] = decode(value); }; // Parse a single query parameter `name[=value]` UrlParser.prototype.parseQueryParam = function (params) { var key = matchQueryParams(this.remaining); if (!key) { return; } this.capture(key); var value = ''; if (this.consumeOptional('=')) {