UNPKG

@bespunky/angular-zen

Version:

The Angular tools you always wished were there.

740 lines (730 loc) 34.7 kB
import * as i0 from '@angular/core'; import { InjectionToken, EventEmitter, Injectable, Directive, Attribute, NgModule, Optional, SkipSelf, Inject } from '@angular/core'; import * as i1$1 from '@bespunky/angular-zen/core'; import { Destroyable, CoreModule } from '@bespunky/angular-zen/core'; import * as i1 from '@angular/router'; import { PRIMARY_OUTLET, NavigationStart, RouteConfigLoadStart, RouteConfigLoadEnd, RoutesRecognized, GuardsCheckStart, ChildActivationStart, ActivationStart, GuardsCheckEnd, ResolveStart, ResolveEnd, ChildActivationEnd, ActivationEnd, NavigationEnd, NavigationCancel, NavigationError, Scroll } from '@angular/router'; import { BehaviorSubject, of, from } from 'rxjs'; import { takeUntil, filter, concatAll, toArray, finalize } from 'rxjs/operators'; /** * An injection token for the provided router configuration. * `RouterExModule.forRoot()` facilitates the injection of this token. No need to inject directly. */ const RouterX = new InjectionToken('RouterX.Config'); /** The default configuration for the router-x module. */ const DefaultRouterXConfig = {}; /** * Creates a provider for the router-x module configuration. * Options not provided will be replaced with their default values according to `DefaultRouterXConfig`. * * @export * @param {RouterXConfig} config * @returns {Provider} */ function provideRouterXConfig(config) { config = Object.assign({}, DefaultRouterXConfig, config); return { provide: RouterX, useValue: config }; } /** * Holds data related with a router outlet event. * * @export * @class RouterOutletEventData */ class RouterOutletEventData { /** * Creates an instance of RouterOutletEventData. * * @param {string} outletName The name of the outlet which triggered the event. For the primary unnamed outlet, this will be angular's PRIMARY_OUTLET. */ constructor(outletName) { this.outletName = outletName; } /** * `true` if the event was triggered by the primary unnamed outlet; otherwise `false`. * * @readonly * @type {boolean} */ get isPrimaryOutlet() { return this.outletName === PRIMARY_OUTLET; } } /** * Holds data related with component publishing triggered by outlet activation. * * @export * @class ComponentPublishEventData * @extends {RouterOutletEventData} */ class ComponentPublishEventData extends RouterOutletEventData { /** * Creates an instance of ComponentPublishEventData. * * @param {BehaviorSubject<AnyObject | null>} changes The observable used to track changes to the activated component of the triggering outlet. * @param {string} outletName The name of the outlet which triggered the event. For the primary unnamed outlet, this will be angular's PRIMARY_OUTLET. */ constructor(changes, outletName) { super(outletName); this.changes = changes; } /** * The instance of the last component activated by the outlet which triggered the event. * This will be null if the outlet has deactivated the component. * * @readonly * @type {(AnyObject | null)} */ get componentInstance() { return this.changes.value; } } /** * Provides a publish bus for the currently rendered component. * * **Why?** * Angular's router only provides the type of component being rendered for a specific route, but not the instance it has created for it. * This service is a bridge which allows other services to get a hold of the instance of a currently rendered component. * * **How to use:** * Use the [`publishComponent`](/directives/PublishComponentDirective.html) directive on your `<router-outlet>` element. This will hook into the outlet's `activate` event and pass * the activated component to the bus service: * @example * <!-- Component template --> * <router-outlet publishComponent name="header"></router-outlet> * <router-outlet publishComponent ></router-outlet> * <router-outlet publishComponent name="footer"></router-outlet> * * @export * @class RouterOutletComponentBus */ class RouterOutletComponentBus { constructor() { this._outletsState = new Map(); /** * A map of the currently instantiated components by outlet name. * Users can either subscribe to changes, or get the current value of a component. * * The primary unnamed outlet component will be accessible via PRIMARY_OUTLET, but for scalability it is better to access it via the `instance()` method. * * @private */ this.components = new Map(); /** * Emits whenever a router outlet marked with the `publishComponent` directive activates a component. * When an outlet deactivates a component, the published component instance will be `null`. * * @type {EventEmitter<ComponentPublishEventData>} */ this.componentPublished = new EventEmitter(); /** * Emits whenever a router outlet marked with the `publishComponent` directive is removed from the DOM. * * @type {EventEmitter<ComponentPublishEventData>} */ this.componentUnpublished = new EventEmitter(); } /** * Gets a shallow clone of the current state outlet state. * * @readonly * @type {(Map<string, AnyObject | null>)} */ get outletsState() { return new Map(this._outletsState); } /** * Publishes the instance of a currently activated or deactivated component by the specified outlet. * When an outlet first publishes, this will create an observable for tracking the outlet's changes. * The observable can be fetched using the `changes()` method. * Following calls to publish a component by the same outlet will subscribers. * * The last published component of an outlet can be fetched using the `instance()` method. * * @param {AnyObject | null} instance The instance of the activated component. For publishing deactivation of a component pass `null`. * @param {string} [outletName=PRIMARY_OUTLET] (Optional) The name of the outlet which activated or deactivated the component. The primary unnamed outlet will be used when not specified. */ publishComponent(instance, outletName = PRIMARY_OUTLET) { const components = this.components; let componentChanges = components.get(outletName); if (!componentChanges) { componentChanges = new BehaviorSubject(instance); components.set(outletName, componentChanges); } this._outletsState.set(outletName, instance); componentChanges.next(instance); this.componentPublished.emit(new ComponentPublishEventData(componentChanges, outletName)); } /** * Notifies any subscribers to the outlet's changes observable that the outlet is being removed by completing * the observable and removes the observable from the service. * * @param {string} [outletName=PRIMARY_OUTLET] (Optional) The name of the outlet to unpublish. The primary unnamed outlet will be used when not specified. */ unpublishComponent(outletName = PRIMARY_OUTLET) { const components = this.components; if (components.has(outletName)) { // Notify any subscribers that the outlet will stop emitting components.get(outletName)?.complete(); // Make sure the outlet is no longer present on the bus components.delete(outletName); this._outletsState.delete(outletName); this.componentUnpublished.emit(new RouterOutletEventData(outletName)); } } /** * Checks whether the outlet by the given name is present in the DOM and has already activated at least one component. * This will be `true` even if the outlet currently has no active component (component is `null`). * * A `false` value can either mean the outlet hasn't been marked with `publishComponent`, or that the outlet is not currently rendered (not present in the DOM). * * When `true`, the user can subscribe to changes of that outlet through the `changes()` method. * * @param {string} [outletName=PRIMARY_OUTLET] (Optional) The name of the outlet to check. The primary unnamed outlet will be checked if no name is provided. * @returns {boolean} `true` if the outlet has published a component at least once; otherwise `false`. */ isComponentPublished(outletName = PRIMARY_OUTLET) { return this.components.has(outletName); } /** * Gets an observable which can be used to track changes to the activated component of the specified outlet. * If the outlet is not rendered (present in the DOM), or hasn't been marked with `publishComponent`, this will be `null`. * * @param {string} [outletName=PRIMARY_OUTLET] (Optional) The name of the outlet to track changes for. The primary unnamed outlet will be used when not specified. * @returns {(BehaviorSubject<AnyObject | null> | null)} An observable to use for tracking changes to the activated component for the specified outlet, or `null` if no such outlet exists. */ changes(outletName = PRIMARY_OUTLET) { return this.components.get(outletName) ?? null; } /** * Gets the current instance of the component created by the specified outlet. * * @param {string} [outletName=PRIMARY_OUTLET] (Optional) The name of the outlet to fetch the component instance for. If not provided, the primary unnamed outlet's component will be fetched. * @returns {(AnyObject | null)} The instance of the component created by the specified outlet. If the outlet doesn't exist, or there is no component instance for the requested outlet, returns `null`. */ instance(outletName = PRIMARY_OUTLET) { return this.components.get(outletName)?.value ?? null; } } RouterOutletComponentBus.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RouterOutletComponentBus, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); RouterOutletComponentBus.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RouterOutletComponentBus, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RouterOutletComponentBus, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }] }); /** * Hooks into a router outlet's events and publishes the current component to the [`RouterOutletComponentBus`](/injectables/RouterOutletComponentBus.html) to create a mapping * of component instances by outlet name. * * Components instantiated by outlets marked with `publishComponent` will be accessible by outlet name in the bus service. * * @example * <!-- Component template --> * <router-outlet publishComponent name="header"></router-outlet> * <router-outlet publishComponent ></router-outlet> * <router-outlet publishComponent name="footer"></router-outlet> * * @See `RouterOutletComponentBus` for more details. * * @export * @class PublishComponentDirective * @extends {Destroyable} * @implements {OnInit} */ class PublishComponentDirective extends Destroyable { constructor(outlet, componentBus, outletName) { super(); this.outlet = outlet; this.componentBus = componentBus; this.outletName = outletName; } /** * Registers to outlet events to publish the activated and deactivated components to the bus. * */ ngOnInit() { // When the outlet activates a new instance, update the component on the bus this.subscribe(this.outlet.activateEvents, this.updateComponentOnBus.bind(this)); // When the outlet deactivates an instance, set the component to null on the bus. this.subscribe(this.outlet.deactivateEvents, () => this.updateComponentOnBus(null)); } /** * Unpublishes the outlet from the bus. */ ngOnDestroy() { // An outlet might be kept alive while its component is switched. So when the outlet is completely destroyed, // it will be completely removed from the bus, even though its value on the bus is null. this.componentBus.unpublishComponent(this.outletName); super.ngOnDestroy(); } updateComponentOnBus(instance) { this.componentBus.publishComponent(instance, this.outletName); } } PublishComponentDirective.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: PublishComponentDirective, deps: [{ token: i1.RouterOutlet }, { token: RouterOutletComponentBus }, { token: 'name', attribute: true }], target: i0.ɵɵFactoryTarget.Directive }); PublishComponentDirective.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.12", type: PublishComponentDirective, selector: "router-outlet[publishComponent]", usesInheritance: true, ngImport: i0 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: PublishComponentDirective, decorators: [{ type: Directive, args: [{ // eslint-disable-next-line @angular-eslint/directive-selector selector: 'router-outlet[publishComponent]' }] }], ctorParameters: function () { return [{ type: i1.RouterOutlet }, { type: RouterOutletComponentBus }, { type: undefined, decorators: [{ type: Attribute, args: ['name'] }] }]; } }); /** * Provides services for libraries requiring integration with their user's language services. * * @export * @class RouterXModule */ class RouterXModule { constructor(parentModule) { if (parentModule) throw new Error('`RouterXModule` has already been loaded. Import it only once, in your app module using, `forRoot()`.'); } /** * Generates the router-x module with the appropriate providers. * * @static * @param {RouterXConfig} config (Optional) The configuration for the router extension module. */ static forRoot(config) { return { ngModule: RouterXModule, providers: [provideRouterXConfig(config)] }; } /** * Generates the router-x module for child modules. * * @static */ static forChild() { return { ngModule: RouterXModule }; } } RouterXModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RouterXModule, deps: [{ token: RouterXModule, optional: true, skipSelf: true }], target: i0.ɵɵFactoryTarget.NgModule }); RouterXModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "14.2.12", ngImport: i0, type: RouterXModule, declarations: [PublishComponentDirective], imports: [CoreModule], exports: [PublishComponentDirective] }); RouterXModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RouterXModule, imports: [CoreModule] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RouterXModule, decorators: [{ type: NgModule, args: [{ imports: [CoreModule], declarations: [PublishComponentDirective], exports: [PublishComponentDirective] }] }], ctorParameters: function () { return [{ type: RouterXModule, decorators: [{ type: Optional }, { type: SkipSelf }] }]; } }); /** * Hard-codes event names as strings. * When AOT compilation is run and constructor names change, the dispatcher will still be able to get a hold * of the correct event name using this map. */ const EventMap = { [NavigationStart.prototype.constructor.name]: 'NavigationStart', [RouteConfigLoadStart.prototype.constructor.name]: 'RouteConfigLoadStart', [RouteConfigLoadEnd.prototype.constructor.name]: 'RouteConfigLoadEnd', [RoutesRecognized.prototype.constructor.name]: 'RoutesRecognized', [GuardsCheckStart.prototype.constructor.name]: 'GuardsCheckStart', [ChildActivationStart.prototype.constructor.name]: 'ChildActivationStart', [ActivationStart.prototype.constructor.name]: 'ActivationStart', [GuardsCheckEnd.prototype.constructor.name]: 'GuardsCheckEnd', [ResolveStart.prototype.constructor.name]: 'ResolveStart', [ResolveEnd.prototype.constructor.name]: 'ResolveEnd', [ChildActivationEnd.prototype.constructor.name]: 'ChildActivationEnd', [ActivationEnd.prototype.constructor.name]: 'ActivationEnd', [NavigationEnd.prototype.constructor.name]: 'NavigationEnd', [NavigationCancel.prototype.constructor.name]: 'NavigationCancel', [NavigationError.prototype.constructor.name]: 'NavigationError', [Scroll.prototype.constructor.name]: 'Scroll' }; /** * The prefix of the id generated for zone macro tasks when calling `RouteAware.resolveInMacroTask()`. * * Generated ids will confrom to a `{prefix}-{random number}` format. */ const ResolverMacroTaskIdPrefix = 'route-aware-resolver'; /** * Provides functionality for extending class to easily work with routes and process changes. * * @export * @abstract * @class RouteAware * @extends {Destroyable} */ // eslint-disable-next-line @angular-eslint/directive-class-suffix class RouteAware extends Destroyable { /** * Creates an instance of RouteAware. * * @param {Router} router The instance of Angular's router service. * @param {ActivatedRoute} route The instance of Angular's active route service. * @param {RouterOutletComponentBus} [componentBus] (Optional) The component bus for router-x functionality. * Provide this when you want your route-aware service to have access to the instance(s) of the activated component(s). */ constructor(router, route, componentBus) { super(); this.router = router; this.route = route; this.componentBus = componentBus; // TODO: Scan class and only subscribe if handlers were defined this.subscribe(this.router.events, this.dispatchRouterEvent.bind(this)); } /** * Checks if a handler method for the specific event type exists on the service and calls it. * Handler methods should comply with `onEventType` naming (lowercase 'on', first-upper event type). * * @private * @param {Event} event The event data received from the router. */ dispatchRouterEvent(event) { // AOT compilation changes class names, causing the dispacher to look for a handler methods with // wrong names (e.g. `onJ`). The EventMap is used to restore the original names. const typeName = event.constructor.name; const handlerName = `on${EventMap[typeName]}`; const handle = this[handlerName]; if (handle) handle.call(this, event); } /** * Creates an observable that emits only the specified router events and is automatically destroyed when the service/component is destroyed. * * @protected * @template TEvent The type of router event to emit. * @param {Type<TEvent>} eventType The type of router event to emit. * @param {boolean} [autoUnsubscribe=true] (Optional) `true` to make the observable complete when the service/component is destroyed; otherwise `false`. Default is `true`. * @returns {Observable<TEvent>} */ observeRouterEvent(eventType, autoUnsubscribe = true) { let observable = this.router.events; if (autoUnsubscribe) observable = observable.pipe(takeUntil(this.destroyed)); return observable.pipe(filter(event => event.constructor === eventType)); } /** * Recoursively runs a processing function on the route and its children. * Scan is done from parent to child, meaning the parent is the first to process. * * @protected * @param {ActivatedRouteSnapshot} route The top route on which to apply the processing function. * @param {(route: ActivatedRouteSnapshot, component: any) => boolean | void} process The function to run on the route and its children. The function receives a `route` argument which reflects the route being processed, * and a `component` argument which reflects the component that was loaded for the route's outlet. * If the corresponding outlet wasn't marked with the `publishComponent` directive, the `component` argument will be null. * * Returning `true` from the process function is equal to saying 'work has completed' and will stop propogation to the route's children. * @param {number} [levels=-1] (Optional) The number of levels (excluding the parent) to dive deeper into the route tree. * A value of 1 for example, will process the route and its first-level children only. By default, scans all levels of the route tree. */ deepScanRoute(route, process, levels = -1) { // Make sure the caller wants scan to proceed, then make sure level limit wasn't reached. const processingConcluded = process(route, this.componentBus?.instance(route.outlet)); // Negative values will scan all, positives will scan until reaching zero. const shouldScanChildren = !processingConcluded && levels !== 0; if (shouldScanChildren && route.children) route.children.forEach(childRoute => this.deepScanRoute(childRoute, process, levels - 1)); } /** * Creates an observable that runs all the specified resolvers and concats their results as an array. * The resolvers will be passed with the instance of the component for the currently activated route. * * @protected * @param {(Resolver | Resolver[])} resolvers The resolver(s) to concat. * @param {...any[]} resolverArgs (Optional) Any arguments to pass into the resolvers in addition to the component. * @returns {Observable<any[]>} An array with the concatenated results of the resolvers. */ resolve(resolvers, ...resolverArgs) { if (!resolvers) return of([]); // Cast array if (!Array.isArray(resolvers)) resolvers = [resolvers]; // Run resolvers to create observable tasks const observables = resolvers.map(resolve => resolve(this.activatedRouteComponent, ...resolverArgs)); // Run tasks and output their returned data as an array return from(observables).pipe(concatAll(), toArray()); } /** * Creates an observable that runs all the specified resolvers and concats their results as an array. * The resolvers will be passed with the instance of the component for the currently activated route. * * **Angular Universal:** * In SSR, the server doesn't wait for async code to complete. The result is scrapers and search engines receiving a page without resolved data, * which is bad in case you need them to read some resolved metadata tags for example. * * Using `Zone` directly, this method creates a macro task and completes it when resolves are done or have errored. * This makes the server block and wait until everything is resolved or errors before returning the rendered page. * * > *ℹ Make sure your resolves and process function are fast enough so that the server won't hang too much trying to render.* * * @see https://stackoverflow.com/a/50065783/4371525 for the discussion. * * @see {ResolverMacroTaskIdPrefix} if you need to identify the created macro task in your code. * * @protected * @param {(Resolver | Resolver[])} resolvers The resolver(s) to concat. * @param {...any[]} resolverArgs (Optional) Any arguments to pass into the resolvers in addition to the component. */ resolveInMacroTask(resolvers, ...resolverArgs) { // eslint-disable-next-line @typescript-eslint/no-empty-function const macroTask = Zone.current.scheduleMacroTask(`${ResolverMacroTaskIdPrefix}-${Math.random()}`, () => { }, {}, () => { }, () => { }); return this.resolve(resolvers, ...resolverArgs) // Signal end of macro task on completion or error and allow server to return .pipe(finalize(() => macroTask.invoke())); } /** * The instance of the component created for the currently activated route. * If no component bus was supplied at construction time, this will be `undefined`. * * @readonly * @protected * @type {(any | null)} */ get activatedRouteComponent() { return this.componentBus?.instance(this.route.outlet); } } RouteAware.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RouteAware, deps: [{ token: i1.Router }, { token: i1.ActivatedRoute }, { token: RouterOutletComponentBus }], target: i0.ɵɵFactoryTarget.Directive }); RouteAware.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.12", type: RouteAware, usesInheritance: true, ngImport: i0 }); RouteAware.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RouteAware }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: RouteAware, decorators: [{ type: Directive }, { type: Injectable }], ctorParameters: function () { return [{ type: i1.Router }, { type: i1.ActivatedRoute }, { type: RouterOutletComponentBus }]; } }); /** * Provides tools for breaking the current and any url to their different parts. * * @export * @class UrlReflectionService */ class UrlReflectionService { constructor(document, router, route, config) { this.document = document; this.router = router; this.route = route; this.config = config; /** * A regular expression to match the route part of a url. The url can be fully qualified or start at the route. * The extracted group will be named 'route'. * * @example * The regex will extract '/this/is/the/route' for all of the following: * * Fully qualified urls: * `https://some.website.com/this/is/the/route?a=1&b=2&c=3` * `https://some.website.com/this/is/the/route#someFragment` * `https://some.website.com/this/is/the/route?debug=true#fragment` * * Relative routes: * `/this/is/the/route?a=1&b=2&c=3` * `/this/is/the/route#someFragment` * `/this/is/the/route?debug=true#fragment` * * The regex will extract 'this/is/the/route' (no head slash) for all of the following: * `this/is/the/route?a=1&b=2&c=3` * `this/is/the/route#someFragment` * `this/is/the/route?debug=true#fragment` **/ this.RouteRegex = /^(?:http[s]?:\/\/[^/]+)?(?<route>[^?#]+)(?=[?#]|$)/; /** * A regular expression to match all segments of a route. * Looks for `/<segment>/` parts and extract them without the slashes. * The extracted groups will be named 'segment'. */ this.RouteSegmentsRegex = /(?!\/)(?<segment>[^/]+)/g; /** * A regular expression to match the question mark and everything that follows in a url. * The extracted group will be named 'queryString'. * * @example * The regex will extract '?a=1&b=2&c=3' for all of the following: * https://some.website.com/some/route?a=1&b=2&c=3 * https://some.website.com/some/route?a=1&b=2&c=3#fragment * /some/route?a=1&b=2&c=3#fragment * ?a=1&b=2&c=3#fragment */ this.QueryStringRegex = /(?<queryString>\?[^#]*)/; /** * A regular expression to match the hash sign and everything that follows in a url. * The extracted group will be named 'fragment'. * * @example * The regex will extract '#fragment' for all of the following: * https://some.website.com/some/route?a=1&b=2&c=3#fragment * /some/route?a=1&b=2&c=3#fragment * some/route?a=1&b=2&c=3#fragment */ this.FragmentRegex = /(?<fragment>#.*)$/; const hostUrl = this.config?.hostUrl; // If the hostUrl has been provided by the user, use it; otherwise, fetch from the location service this.hostUrl = hostUrl || this.document.nativeDocument.location.origin; } /** * Extracts the route portion of a given url. * * @example * routeOf('https://some.website.com/some/route?a=1&b=2&c=3') === '/some/route' * * @param {string} url The url for which to extract the route portion. * @returns {string} The route portion of the url. */ routeOf(url) { return url.match(this.RouteRegex)?.groups?.['route'] || ''; } /** * Extracts the route portion of a url as an array of route segments, not including the empty root segment. * * @example * routeSegmentsOf('https://some.website.com/some/route?a=1&b=2&c=3') === ['some', 'route'] * routeSegmentsOf('/some/route') === ['some', 'route'] * * @param {string} routeOrUrl The route or complete url from which to extract the route segments. * @returns {string[]} The segments of the route. */ routeSegmentsOf(routeOrUrl) { // Extract the route portion only, then match with the regex to extract the array of segments return this.routeOf(routeOrUrl).match(this.RouteSegmentsRegex) || []; } /** * Extracts the query string of a specified url. * * @example * queryStringOf('https://some.website.com/some/route?a=1&b=2&c=3') === '?a=1&b=2&c=3' * * @param {string} url The url from which to extract the query string. * @returns {string} The query string extracted from the url. */ queryStringOf(url) { const matches = url.match(this.QueryStringRegex) || ['']; return matches[0]; } /** * Removes the query portion of a url. * * @example * stripQuery('https://some.website.com/some/route?a=1&b=2&c=3#fragment') === 'https://some.website.com/some/route#fragment' * * @param {string} url The url from which to remove the query. * @returns {string} The specified url without the query portion. */ stripQuery(url) { return url.replace(this.QueryStringRegex, ''); } /** * Extracts the fragment from a url. * * @example * fragmentOf('https://some.website.com/some/route?a=1&b=2&c=3#fragment') === '#fragment' * * @param {string} url The url from which to extract the fragment. * @returns {string} The fragment extracted from the url. */ fragmentOf(url) { const matches = url.match(this.FragmentRegex) || ['']; return matches[0]; } /** * Removes the fragment portion of a url. * * @example * stripFragment('https://some.website.com/some/route?a=1&b=2&c=3#fragment') === 'https://some.website.com/some/route?a=1&b=2&c=3' * * @param {string} url The url to remove the fragment. * @returns {string} The url without the fragment portion. */ stripFragment(url) { return url.replace(this.FragmentRegex, ''); } /** * Makes sure the url is prefixed with https instead of http. * * @param {string} url The url to secure. * @returns {string} The secure url. */ forceHttps(url) { return url.replace(/^http:\/\//, 'https://'); } /** * The fully qualified url of the currently navigated route (e.g. 'https://some.website.com/some/route?a=1&b=2&c=3#fragment'). * * @readonly * @type {string} */ get fullUrl() { return `${this.hostUrl}${this.router.url}`; } /** * The route url of the currently navigated route (e.g. '/some/route'). * * @readonly * @type {string} */ get routeUrl() { return this.routeOf(this.router.url); } /** * The segments of the currently navigated route (e.g. ['some', 'route']). * * @readonly * @type {string[]} */ get routeSegments() { return this.routeSegmentsOf(this.routeUrl); } /** * The object representing the query params in the currently navigated route. * * @readonly * @type {*} */ get queryParams() { return { ...this.route.snapshot.queryParams }; } /** * The query string portion of the currently navigated route (e.g. '?a=1&b=2&c=3'). * * @readonly * @type {string} */ get queryString() { return this.queryStringOf(this.router.url); } /** * The fragment portion of the currently navigated route, without the hash sign (e.g. 'fragment'). * * @readonly * @type {string} */ get fragment() { return this.route.snapshot.fragment || ''; } /** * The fragment portion of the currently navigated route, with the hash sign (e.g. '#fragment'). * * @readonly * @type {string} */ get fragmentString() { return `#${this.fragment}`; } } UrlReflectionService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: UrlReflectionService, deps: [{ token: i1$1.DocumentRef }, { token: i1.Router }, { token: i1.ActivatedRoute }, { token: RouterX, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); UrlReflectionService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: UrlReflectionService, providedIn: 'root' }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.12", ngImport: i0, type: UrlReflectionService, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: function () { return [{ type: i1$1.DocumentRef }, { type: i1.Router }, { type: i1.ActivatedRoute }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [RouterX] }] }]; } }); /** * Generated bundle index. Do not edit. */ export { ComponentPublishEventData, PublishComponentDirective, ResolverMacroTaskIdPrefix, RouteAware, RouterOutletComponentBus, RouterOutletEventData, RouterXModule, UrlReflectionService }; //# sourceMappingURL=bespunky-angular-zen-router-x.mjs.map