UNPKG

@angular/common

Version:

Angular - commonly needed directives and services

1,245 lines (1,232 loc) 310 kB
/** * @license Angular v17.3.9 * (c) 2010-2024 Google LLC. https://angular.io/ * License: MIT */ import * as i0 from '@angular/core'; import { Injectable, InjectionToken, inject, Optional, Inject, EventEmitter, ɵɵinject, ɵfindLocaleData, ɵLocaleDataIndex, ɵgetLocaleCurrencyCode, ɵgetLocalePluralCase, LOCALE_ID, ɵregisterLocaleData, ɵstringify, Directive, Input, createNgModule, NgModuleRef, ɵRuntimeError, ɵformatRuntimeError, Host, Attribute, RendererStyleFlags2, untracked, ɵisPromise, ɵisSubscribable, Pipe, DEFAULT_CURRENCY_CODE, NgModule, Version, ɵɵdefineInjectable, PLATFORM_ID, ɵIMAGE_CONFIG, Renderer2, ElementRef, Injector, ɵperformanceMarkFeature, NgZone, ChangeDetectorRef, numberAttribute, booleanAttribute, ɵIMAGE_CONFIG_DEFAULTS, ɵunwrapSafeValue } from '@angular/core'; export { ɵIMAGE_CONFIG as IMAGE_CONFIG } from '@angular/core'; let _DOM = null; function getDOM() { return _DOM; } function setRootDomAdapter(adapter) { _DOM ??= adapter; } /* tslint:disable:requireParameterType */ /** * Provides DOM operations in an environment-agnostic way. * * @security Tread carefully! Interacting with the DOM directly is dangerous and * can introduce XSS risks. */ class DomAdapter { } /** * This class wraps the platform Navigation API which allows server-specific and test * implementations. */ class PlatformNavigation { static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: PlatformNavigation, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: PlatformNavigation, providedIn: 'platform', useFactory: () => window.navigation }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: PlatformNavigation, decorators: [{ type: Injectable, args: [{ providedIn: 'platform', useFactory: () => window.navigation }] }] }); /** * A DI Token representing the main rendering context. * In a browser and SSR this is the DOM Document. * When using SSR, that document is created by [Domino](https://github.com/angular/domino). * * @publicApi */ const DOCUMENT = new InjectionToken(ngDevMode ? 'DocumentToken' : ''); /** * This class should not be used directly by an application developer. Instead, use * {@link Location}. * * `PlatformLocation` encapsulates all calls to DOM APIs, which allows the Router to be * platform-agnostic. * This means that we can have different implementation of `PlatformLocation` for the different * platforms that Angular supports. For example, `@angular/platform-browser` provides an * implementation specific to the browser environment, while `@angular/platform-server` provides * one suitable for use with server-side rendering. * * The `PlatformLocation` class is used directly by all implementations of {@link LocationStrategy} * when they need to interact with the DOM APIs like pushState, popState, etc. * * {@link LocationStrategy} in turn is used by the {@link Location} service which is used directly * by the {@link Router} in order to navigate between routes. Since all interactions between {@link * Router} / * {@link Location} / {@link LocationStrategy} and DOM APIs flow through the `PlatformLocation` * class, they are all platform-agnostic. * * @publicApi */ class PlatformLocation { historyGo(relativePosition) { throw new Error(ngDevMode ? 'Not implemented' : ''); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: PlatformLocation, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: PlatformLocation, providedIn: 'platform', useFactory: () => inject(BrowserPlatformLocation) }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: PlatformLocation, decorators: [{ type: Injectable, args: [{ providedIn: 'platform', useFactory: () => inject(BrowserPlatformLocation) }] }] }); /** * @description * Indicates when a location is initialized. * * @publicApi */ const LOCATION_INITIALIZED = new InjectionToken(ngDevMode ? 'Location Initialized' : ''); /** * `PlatformLocation` encapsulates all of the direct calls to platform APIs. * This class should not be used directly by an application developer. Instead, use * {@link Location}. * * @publicApi */ class BrowserPlatformLocation extends PlatformLocation { constructor() { super(); this._doc = inject(DOCUMENT); this._location = window.location; this._history = window.history; } getBaseHrefFromDOM() { return getDOM().getBaseHref(this._doc); } onPopState(fn) { const window = getDOM().getGlobalEventTarget(this._doc, 'window'); window.addEventListener('popstate', fn, false); return () => window.removeEventListener('popstate', fn); } onHashChange(fn) { const window = getDOM().getGlobalEventTarget(this._doc, 'window'); window.addEventListener('hashchange', fn, false); return () => window.removeEventListener('hashchange', fn); } get href() { return this._location.href; } get protocol() { return this._location.protocol; } get hostname() { return this._location.hostname; } get port() { return this._location.port; } get pathname() { return this._location.pathname; } get search() { return this._location.search; } get hash() { return this._location.hash; } set pathname(newPath) { this._location.pathname = newPath; } pushState(state, title, url) { this._history.pushState(state, title, url); } replaceState(state, title, url) { this._history.replaceState(state, title, url); } forward() { this._history.forward(); } back() { this._history.back(); } historyGo(relativePosition = 0) { this._history.go(relativePosition); } getState() { return this._history.state; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: BrowserPlatformLocation, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: BrowserPlatformLocation, providedIn: 'platform', useFactory: () => new BrowserPlatformLocation() }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: BrowserPlatformLocation, decorators: [{ type: Injectable, args: [{ providedIn: 'platform', useFactory: () => new BrowserPlatformLocation(), }] }], ctorParameters: () => [] }); /** * Joins two parts of a URL with a slash if needed. * * @param start URL string * @param end URL string * * * @returns The joined URL string. */ function joinWithSlash(start, end) { if (start.length == 0) { return end; } if (end.length == 0) { return start; } let slashes = 0; if (start.endsWith('/')) { slashes++; } if (end.startsWith('/')) { slashes++; } if (slashes == 2) { return start + end.substring(1); } if (slashes == 1) { return start + end; } return start + '/' + end; } /** * Removes a trailing slash from a URL string if needed. * Looks for the first occurrence of either `#`, `?`, or the end of the * line as `/` characters and removes the trailing slash if one exists. * * @param url URL string. * * @returns The URL string, modified if needed. */ function stripTrailingSlash(url) { const match = url.match(/#|\?|$/); const pathEndIdx = (match && match.index) || url.length; const droppedSlashIdx = pathEndIdx - (url[pathEndIdx - 1] === '/' ? 1 : 0); return url.slice(0, droppedSlashIdx) + url.slice(pathEndIdx); } /** * Normalizes URL parameters by prepending with `?` if needed. * * @param params String of URL parameters. * * @returns The normalized URL parameters string. */ function normalizeQueryParams(params) { return params && params[0] !== '?' ? '?' + params : params; } /** * Enables the `Location` service to read route state from the browser's URL. * Angular provides two strategies: * `HashLocationStrategy` and `PathLocationStrategy`. * * Applications should use the `Router` or `Location` services to * interact with application route state. * * For instance, `HashLocationStrategy` produces URLs like * <code class="no-auto-link">http://example.com#/foo</code>, * and `PathLocationStrategy` produces * <code class="no-auto-link">http://example.com/foo</code> as an equivalent URL. * * See these two classes for more. * * @publicApi */ class LocationStrategy { historyGo(relativePosition) { throw new Error(ngDevMode ? 'Not implemented' : ''); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: LocationStrategy, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: LocationStrategy, providedIn: 'root', useFactory: () => inject(PathLocationStrategy) }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: LocationStrategy, decorators: [{ type: Injectable, args: [{ providedIn: 'root', useFactory: () => inject(PathLocationStrategy) }] }] }); /** * A predefined [DI token](guide/glossary#di-token) for the base href * to be used with the `PathLocationStrategy`. * The base href is the URL prefix that should be preserved when generating * and recognizing URLs. * * @usageNotes * * The following example shows how to use this token to configure the root app injector * with a base href value, so that the DI framework can supply the dependency anywhere in the app. * * ```typescript * import {NgModule} from '@angular/core'; * import {APP_BASE_HREF} from '@angular/common'; * * @NgModule({ * providers: [{provide: APP_BASE_HREF, useValue: '/my/app'}] * }) * class AppModule {} * ``` * * @publicApi */ const APP_BASE_HREF = new InjectionToken(ngDevMode ? 'appBaseHref' : ''); /** * @description * A {@link LocationStrategy} used to configure the {@link Location} service to * represent its state in the * [path](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) of the * browser's URL. * * If you're using `PathLocationStrategy`, you may provide a {@link APP_BASE_HREF} * or add a `<base href>` element to the document to override the default. * * For instance, if you provide an `APP_BASE_HREF` of `'/my/app/'` and call * `location.go('/foo')`, the browser's URL will become * `example.com/my/app/foo`. To ensure all relative URIs resolve correctly, * the `<base href>` and/or `APP_BASE_HREF` should end with a `/`. * * Similarly, if you add `<base href='/my/app/'/>` to the document and call * `location.go('/foo')`, the browser's URL will become * `example.com/my/app/foo`. * * Note that when using `PathLocationStrategy`, neither the query nor * the fragment in the `<base href>` will be preserved, as outlined * by the [RFC](https://tools.ietf.org/html/rfc3986#section-5.2.2). * * @usageNotes * * ### Example * * {@example common/location/ts/path_location_component.ts region='LocationComponent'} * * @publicApi */ class PathLocationStrategy extends LocationStrategy { constructor(_platformLocation, href) { super(); this._platformLocation = _platformLocation; this._removeListenerFns = []; this._baseHref = href ?? this._platformLocation.getBaseHrefFromDOM() ?? inject(DOCUMENT).location?.origin ?? ''; } /** @nodoc */ ngOnDestroy() { while (this._removeListenerFns.length) { this._removeListenerFns.pop()(); } } onPopState(fn) { this._removeListenerFns.push(this._platformLocation.onPopState(fn), this._platformLocation.onHashChange(fn)); } getBaseHref() { return this._baseHref; } prepareExternalUrl(internal) { return joinWithSlash(this._baseHref, internal); } path(includeHash = false) { const pathname = this._platformLocation.pathname + normalizeQueryParams(this._platformLocation.search); const hash = this._platformLocation.hash; return hash && includeHash ? `${pathname}${hash}` : pathname; } pushState(state, title, url, queryParams) { const externalUrl = this.prepareExternalUrl(url + normalizeQueryParams(queryParams)); this._platformLocation.pushState(state, title, externalUrl); } replaceState(state, title, url, queryParams) { const externalUrl = this.prepareExternalUrl(url + normalizeQueryParams(queryParams)); this._platformLocation.replaceState(state, title, externalUrl); } forward() { this._platformLocation.forward(); } back() { this._platformLocation.back(); } getState() { return this._platformLocation.getState(); } historyGo(relativePosition = 0) { this._platformLocation.historyGo?.(relativePosition); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: PathLocationStrategy, deps: [{ token: PlatformLocation }, { token: APP_BASE_HREF, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: PathLocationStrategy, providedIn: 'root' }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: PathLocationStrategy, decorators: [{ type: Injectable, args: [{ providedIn: 'root' }] }], ctorParameters: () => [{ type: PlatformLocation }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [APP_BASE_HREF] }] }] }); /** * @description * A {@link LocationStrategy} used to configure the {@link Location} service to * represent its state in the * [hash fragment](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) * of the browser's URL. * * For instance, if you call `location.go('/foo')`, the browser's URL will become * `example.com#/foo`. * * @usageNotes * * ### Example * * {@example common/location/ts/hash_location_component.ts region='LocationComponent'} * * @publicApi */ class HashLocationStrategy extends LocationStrategy { constructor(_platformLocation, _baseHref) { super(); this._platformLocation = _platformLocation; this._baseHref = ''; this._removeListenerFns = []; if (_baseHref != null) { this._baseHref = _baseHref; } } /** @nodoc */ ngOnDestroy() { while (this._removeListenerFns.length) { this._removeListenerFns.pop()(); } } onPopState(fn) { this._removeListenerFns.push(this._platformLocation.onPopState(fn), this._platformLocation.onHashChange(fn)); } getBaseHref() { return this._baseHref; } path(includeHash = false) { // the hash value is always prefixed with a `#` // and if it is empty then it will stay empty const path = this._platformLocation.hash ?? '#'; return path.length > 0 ? path.substring(1) : path; } prepareExternalUrl(internal) { const url = joinWithSlash(this._baseHref, internal); return url.length > 0 ? '#' + url : url; } pushState(state, title, path, queryParams) { let url = this.prepareExternalUrl(path + normalizeQueryParams(queryParams)); if (url.length == 0) { url = this._platformLocation.pathname; } this._platformLocation.pushState(state, title, url); } replaceState(state, title, path, queryParams) { let url = this.prepareExternalUrl(path + normalizeQueryParams(queryParams)); if (url.length == 0) { url = this._platformLocation.pathname; } this._platformLocation.replaceState(state, title, url); } forward() { this._platformLocation.forward(); } back() { this._platformLocation.back(); } getState() { return this._platformLocation.getState(); } historyGo(relativePosition = 0) { this._platformLocation.historyGo?.(relativePosition); } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: HashLocationStrategy, deps: [{ token: PlatformLocation }, { token: APP_BASE_HREF, optional: true }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: HashLocationStrategy }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: HashLocationStrategy, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: PlatformLocation }, { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [APP_BASE_HREF] }] }] }); /** * @description * * A service that applications can use to interact with a browser's URL. * * Depending on the `LocationStrategy` used, `Location` persists * to the URL's path or the URL's hash segment. * * @usageNotes * * It's better to use the `Router.navigate()` service to trigger route changes. Use * `Location` only if you need to interact with or create normalized URLs outside of * routing. * * `Location` is responsible for normalizing the URL against the application's base href. * A normalized URL is absolute from the URL host, includes the application's base href, and has no * trailing slash: * - `/my/app/user/123` is normalized * - `my/app/user/123` **is not** normalized * - `/my/app/user/123/` **is not** normalized * * ### Example * * <code-example path='common/location/ts/path_location_component.ts' * region='LocationComponent'></code-example> * * @publicApi */ class Location { constructor(locationStrategy) { /** @internal */ this._subject = new EventEmitter(); /** @internal */ this._urlChangeListeners = []; /** @internal */ this._urlChangeSubscription = null; this._locationStrategy = locationStrategy; const baseHref = this._locationStrategy.getBaseHref(); // Note: This class's interaction with base HREF does not fully follow the rules // outlined in the spec https://www.freesoft.org/CIE/RFC/1808/18.htm. // Instead of trying to fix individual bugs with more and more code, we should // investigate using the URL constructor and providing the base as a second // argument. // https://developer.mozilla.org/en-US/docs/Web/API/URL/URL#parameters this._basePath = _stripOrigin(stripTrailingSlash(_stripIndexHtml(baseHref))); this._locationStrategy.onPopState((ev) => { this._subject.emit({ 'url': this.path(true), 'pop': true, 'state': ev.state, 'type': ev.type, }); }); } /** @nodoc */ ngOnDestroy() { this._urlChangeSubscription?.unsubscribe(); this._urlChangeListeners = []; } /** * Normalizes the URL path for this location. * * @param includeHash True to include an anchor fragment in the path. * * @returns The normalized URL path. */ // TODO: vsavkin. Remove the boolean flag and always include hash once the deprecated router is // removed. path(includeHash = false) { return this.normalize(this._locationStrategy.path(includeHash)); } /** * Reports the current state of the location history. * @returns The current value of the `history.state` object. */ getState() { return this._locationStrategy.getState(); } /** * Normalizes the given path and compares to the current normalized path. * * @param path The given URL path. * @param query Query parameters. * * @returns True if the given URL path is equal to the current normalized path, false * otherwise. */ isCurrentPathEqualTo(path, query = '') { return this.path() == this.normalize(path + normalizeQueryParams(query)); } /** * Normalizes a URL path by stripping any trailing slashes. * * @param url String representing a URL. * * @returns The normalized URL string. */ normalize(url) { return Location.stripTrailingSlash(_stripBasePath(this._basePath, _stripIndexHtml(url))); } /** * Normalizes an external URL path. * If the given URL doesn't begin with a leading slash (`'/'`), adds one * before normalizing. Adds a hash if `HashLocationStrategy` is * in use, or the `APP_BASE_HREF` if the `PathLocationStrategy` is in use. * * @param url String representing a URL. * * @returns A normalized platform-specific URL. */ prepareExternalUrl(url) { if (url && url[0] !== '/') { url = '/' + url; } return this._locationStrategy.prepareExternalUrl(url); } // TODO: rename this method to pushState /** * Changes the browser's URL to a normalized version of a given URL, and pushes a * new item onto the platform's history. * * @param path URL path to normalize. * @param query Query parameters. * @param state Location history state. * */ go(path, query = '', state = null) { this._locationStrategy.pushState(state, '', path, query); this._notifyUrlChangeListeners(this.prepareExternalUrl(path + normalizeQueryParams(query)), state); } /** * Changes the browser's URL to a normalized version of the given URL, and replaces * the top item on the platform's history stack. * * @param path URL path to normalize. * @param query Query parameters. * @param state Location history state. */ replaceState(path, query = '', state = null) { this._locationStrategy.replaceState(state, '', path, query); this._notifyUrlChangeListeners(this.prepareExternalUrl(path + normalizeQueryParams(query)), state); } /** * Navigates forward in the platform's history. */ forward() { this._locationStrategy.forward(); } /** * Navigates back in the platform's history. */ back() { this._locationStrategy.back(); } /** * Navigate to a specific page from session history, identified by its relative position to the * current page. * * @param relativePosition Position of the target page in the history relative to the current * page. * A negative value moves backwards, a positive value moves forwards, e.g. `location.historyGo(2)` * moves forward two pages and `location.historyGo(-2)` moves back two pages. When we try to go * beyond what's stored in the history session, we stay in the current page. Same behaviour occurs * when `relativePosition` equals 0. * @see https://developer.mozilla.org/en-US/docs/Web/API/History_API#Moving_to_a_specific_point_in_history */ historyGo(relativePosition = 0) { this._locationStrategy.historyGo?.(relativePosition); } /** * Registers a URL change listener. Use to catch updates performed by the Angular * framework that are not detectible through "popstate" or "hashchange" events. * * @param fn The change handler function, which take a URL and a location history state. * @returns A function that, when executed, unregisters a URL change listener. */ onUrlChange(fn) { this._urlChangeListeners.push(fn); this._urlChangeSubscription ??= this.subscribe((v) => { this._notifyUrlChangeListeners(v.url, v.state); }); return () => { const fnIndex = this._urlChangeListeners.indexOf(fn); this._urlChangeListeners.splice(fnIndex, 1); if (this._urlChangeListeners.length === 0) { this._urlChangeSubscription?.unsubscribe(); this._urlChangeSubscription = null; } }; } /** @internal */ _notifyUrlChangeListeners(url = '', state) { this._urlChangeListeners.forEach((fn) => fn(url, state)); } /** * Subscribes to the platform's `popState` events. * * Note: `Location.go()` does not trigger the `popState` event in the browser. Use * `Location.onUrlChange()` to subscribe to URL changes instead. * * @param value Event that is triggered when the state history changes. * @param exception The exception to throw. * * @see [onpopstate](https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate) * * @returns Subscribed events. */ subscribe(onNext, onThrow, onReturn) { return this._subject.subscribe({ next: onNext, error: onThrow, complete: onReturn }); } /** * Normalizes URL parameters by prepending with `?` if needed. * * @param params String of URL parameters. * * @returns The normalized URL parameters string. */ static { this.normalizeQueryParams = normalizeQueryParams; } /** * Joins two parts of a URL with a slash if needed. * * @param start URL string * @param end URL string * * * @returns The joined URL string. */ static { this.joinWithSlash = joinWithSlash; } /** * Removes a trailing slash from a URL string if needed. * Looks for the first occurrence of either `#`, `?`, or the end of the * line as `/` characters and removes the trailing slash if one exists. * * @param url URL string. * * @returns The URL string, modified if needed. */ static { this.stripTrailingSlash = stripTrailingSlash; } static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: Location, deps: [{ token: LocationStrategy }], target: i0.ɵɵFactoryTarget.Injectable }); } static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: Location, providedIn: 'root', useFactory: createLocation }); } } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.9", ngImport: i0, type: Location, decorators: [{ type: Injectable, args: [{ providedIn: 'root', // See #23917 useFactory: createLocation, }] }], ctorParameters: () => [{ type: LocationStrategy }] }); function createLocation() { return new Location(ɵɵinject(LocationStrategy)); } function _stripBasePath(basePath, url) { if (!basePath || !url.startsWith(basePath)) { return url; } const strippedUrl = url.substring(basePath.length); if (strippedUrl === '' || ['/', ';', '?', '#'].includes(strippedUrl[0])) { return strippedUrl; } return url; } function _stripIndexHtml(url) { return url.replace(/\/index.html$/, ''); } function _stripOrigin(baseHref) { // DO NOT REFACTOR! Previously, this check looked like this: // `/^(https?:)?\/\//.test(baseHref)`, but that resulted in // syntactically incorrect code after Closure Compiler minification. // This was likely caused by a bug in Closure Compiler, but // for now, the check is rewritten to use `new RegExp` instead. const isAbsoluteUrl = new RegExp('^(https?:)?//').test(baseHref); if (isAbsoluteUrl) { const [, pathname] = baseHref.split(/\/\/[^\/]+/); return pathname; } return baseHref; } /** @internal */ const CURRENCIES_EN = { "ADP": [undefined, undefined, 0], "AFN": [undefined, "؋", 0], "ALL": [undefined, undefined, 0], "AMD": [undefined, "֏", 2], "AOA": [undefined, "Kz"], "ARS": [undefined, "$"], "AUD": ["A$", "$"], "AZN": [undefined, "₼"], "BAM": [undefined, "KM"], "BBD": [undefined, "$"], "BDT": [undefined, "৳"], "BHD": [undefined, undefined, 3], "BIF": [undefined, undefined, 0], "BMD": [undefined, "$"], "BND": [undefined, "$"], "BOB": [undefined, "Bs"], "BRL": ["R$"], "BSD": [undefined, "$"], "BWP": [undefined, "P"], "BYN": [undefined, undefined, 2], "BYR": [undefined, undefined, 0], "BZD": [undefined, "$"], "CAD": ["CA$", "$", 2], "CHF": [undefined, undefined, 2], "CLF": [undefined, undefined, 4], "CLP": [undefined, "$", 0], "CNY": ["CN¥", "¥"], "COP": [undefined, "$", 2], "CRC": [undefined, "₡", 2], "CUC": [undefined, "$"], "CUP": [undefined, "$"], "CZK": [undefined, "Kč", 2], "DJF": [undefined, undefined, 0], "DKK": [undefined, "kr", 2], "DOP": [undefined, "$"], "EGP": [undefined, "E£"], "ESP": [undefined, "₧", 0], "EUR": ["€"], "FJD": [undefined, "$"], "FKP": [undefined, "£"], "GBP": ["£"], "GEL": [undefined, "₾"], "GHS": [undefined, "GH₵"], "GIP": [undefined, "£"], "GNF": [undefined, "FG", 0], "GTQ": [undefined, "Q"], "GYD": [undefined, "$", 2], "HKD": ["HK$", "$"], "HNL": [undefined, "L"], "HRK": [undefined, "kn"], "HUF": [undefined, "Ft", 2], "IDR": [undefined, "Rp", 2], "ILS": ["₪"], "INR": ["₹"], "IQD": [undefined, undefined, 0], "IRR": [undefined, undefined, 0], "ISK": [undefined, "kr", 0], "ITL": [undefined, undefined, 0], "JMD": [undefined, "$"], "JOD": [undefined, undefined, 3], "JPY": ["¥", undefined, 0], "KHR": [undefined, "៛"], "KMF": [undefined, "CF", 0], "KPW": [undefined, "₩", 0], "KRW": ["₩", undefined, 0], "KWD": [undefined, undefined, 3], "KYD": [undefined, "$"], "KZT": [undefined, "₸"], "LAK": [undefined, "₭", 0], "LBP": [undefined, "L£", 0], "LKR": [undefined, "Rs"], "LRD": [undefined, "$"], "LTL": [undefined, "Lt"], "LUF": [undefined, undefined, 0], "LVL": [undefined, "Ls"], "LYD": [undefined, undefined, 3], "MGA": [undefined, "Ar", 0], "MGF": [undefined, undefined, 0], "MMK": [undefined, "K", 0], "MNT": [undefined, "₮", 2], "MRO": [undefined, undefined, 0], "MUR": [undefined, "Rs", 2], "MXN": ["MX$", "$"], "MYR": [undefined, "RM"], "NAD": [undefined, "$"], "NGN": [undefined, "₦"], "NIO": [undefined, "C$"], "NOK": [undefined, "kr", 2], "NPR": [undefined, "Rs"], "NZD": ["NZ$", "$"], "OMR": [undefined, undefined, 3], "PHP": ["₱"], "PKR": [undefined, "Rs", 2], "PLN": [undefined, "zł"], "PYG": [undefined, "₲", 0], "RON": [undefined, "lei"], "RSD": [undefined, undefined, 0], "RUB": [undefined, "₽"], "RWF": [undefined, "RF", 0], "SBD": [undefined, "$"], "SEK": [undefined, "kr", 2], "SGD": [undefined, "$"], "SHP": [undefined, "£"], "SLE": [undefined, undefined, 2], "SLL": [undefined, undefined, 0], "SOS": [undefined, undefined, 0], "SRD": [undefined, "$"], "SSP": [undefined, "£"], "STD": [undefined, undefined, 0], "STN": [undefined, "Db"], "SYP": [undefined, "£", 0], "THB": [undefined, "฿"], "TMM": [undefined, undefined, 0], "TND": [undefined, undefined, 3], "TOP": [undefined, "T$"], "TRL": [undefined, undefined, 0], "TRY": [undefined, "₺"], "TTD": [undefined, "$"], "TWD": ["NT$", "$", 2], "TZS": [undefined, undefined, 2], "UAH": [undefined, "₴"], "UGX": [undefined, undefined, 0], "USD": ["$"], "UYI": [undefined, undefined, 0], "UYU": [undefined, "$"], "UYW": [undefined, undefined, 4], "UZS": [undefined, undefined, 2], "VEF": [undefined, "Bs", 2], "VND": ["₫", undefined, 0], "VUV": [undefined, undefined, 0], "XAF": ["FCFA", undefined, 0], "XCD": ["EC$", "$"], "XOF": ["F CFA", undefined, 0], "XPF": ["CFPF", undefined, 0], "XXX": ["¤"], "YER": [undefined, undefined, 0], "ZAR": [undefined, "R"], "ZMK": [undefined, undefined, 0], "ZMW": [undefined, "ZK"], "ZWD": [undefined, undefined, 0] }; /** * Format styles that can be used to represent numbers. * @see {@link getLocaleNumberFormat} * @see [Internationalization (i18n) Guide](/guide/i18n-overview) * * @publicApi */ var NumberFormatStyle; (function (NumberFormatStyle) { NumberFormatStyle[NumberFormatStyle["Decimal"] = 0] = "Decimal"; NumberFormatStyle[NumberFormatStyle["Percent"] = 1] = "Percent"; NumberFormatStyle[NumberFormatStyle["Currency"] = 2] = "Currency"; NumberFormatStyle[NumberFormatStyle["Scientific"] = 3] = "Scientific"; })(NumberFormatStyle || (NumberFormatStyle = {})); /** * Plurality cases used for translating plurals to different languages. * * @see {@link NgPlural} * @see {@link NgPluralCase} * @see [Internationalization (i18n) Guide](/guide/i18n-overview) * * @publicApi */ var Plural; (function (Plural) { Plural[Plural["Zero"] = 0] = "Zero"; Plural[Plural["One"] = 1] = "One"; Plural[Plural["Two"] = 2] = "Two"; Plural[Plural["Few"] = 3] = "Few"; Plural[Plural["Many"] = 4] = "Many"; Plural[Plural["Other"] = 5] = "Other"; })(Plural || (Plural = {})); /** * Context-dependant translation forms for strings. * Typically the standalone version is for the nominative form of the word, * and the format version is used for the genitive case. * @see [CLDR website](http://cldr.unicode.org/translation/date-time-1/date-time#TOC-Standalone-vs.-Format-Styles) * @see [Internationalization (i18n) Guide](/guide/i18n-overview) * * @publicApi */ var FormStyle; (function (FormStyle) { FormStyle[FormStyle["Format"] = 0] = "Format"; FormStyle[FormStyle["Standalone"] = 1] = "Standalone"; })(FormStyle || (FormStyle = {})); /** * String widths available for translations. * The specific character widths are locale-specific. * Examples are given for the word "Sunday" in English. * * @publicApi */ var TranslationWidth; (function (TranslationWidth) { /** 1 character for `en-US`. For example: 'S' */ TranslationWidth[TranslationWidth["Narrow"] = 0] = "Narrow"; /** 3 characters for `en-US`. For example: 'Sun' */ TranslationWidth[TranslationWidth["Abbreviated"] = 1] = "Abbreviated"; /** Full length for `en-US`. For example: "Sunday" */ TranslationWidth[TranslationWidth["Wide"] = 2] = "Wide"; /** 2 characters for `en-US`, For example: "Su" */ TranslationWidth[TranslationWidth["Short"] = 3] = "Short"; })(TranslationWidth || (TranslationWidth = {})); /** * String widths available for date-time formats. * The specific character widths are locale-specific. * Examples are given for `en-US`. * * @see {@link getLocaleDateFormat} * @see {@link getLocaleTimeFormat} * @see {@link getLocaleDateTimeFormat} * @see [Internationalization (i18n) Guide](/guide/i18n-overview) * @publicApi */ var FormatWidth; (function (FormatWidth) { /** * For `en-US`, 'M/d/yy, h:mm a'` * (Example: `6/15/15, 9:03 AM`) */ FormatWidth[FormatWidth["Short"] = 0] = "Short"; /** * For `en-US`, `'MMM d, y, h:mm:ss a'` * (Example: `Jun 15, 2015, 9:03:01 AM`) */ FormatWidth[FormatWidth["Medium"] = 1] = "Medium"; /** * For `en-US`, `'MMMM d, y, h:mm:ss a z'` * (Example: `June 15, 2015 at 9:03:01 AM GMT+1`) */ FormatWidth[FormatWidth["Long"] = 2] = "Long"; /** * For `en-US`, `'EEEE, MMMM d, y, h:mm:ss a zzzz'` * (Example: `Monday, June 15, 2015 at 9:03:01 AM GMT+01:00`) */ FormatWidth[FormatWidth["Full"] = 3] = "Full"; })(FormatWidth || (FormatWidth = {})); // This needs to be an object literal, rather than an enum, because TypeScript 5.4+ // doesn't allow numeric keys and we have `Infinity` and `NaN`. /** * Symbols that can be used to replace placeholders in number patterns. * Examples are based on `en-US` values. * * @see {@link getLocaleNumberSymbol} * @see [Internationalization (i18n) Guide](/guide/i18n-overview) * * @publicApi * @object-literal-as-enum */ const NumberSymbol = { /** * Decimal separator. * For `en-US`, the dot character. * Example: 2,345`.`67 */ Decimal: 0, /** * Grouping separator, typically for thousands. * For `en-US`, the comma character. * Example: 2`,`345.67 */ Group: 1, /** * List-item separator. * Example: "one, two, and three" */ List: 2, /** * Sign for percentage (out of 100). * Example: 23.4% */ PercentSign: 3, /** * Sign for positive numbers. * Example: +23 */ PlusSign: 4, /** * Sign for negative numbers. * Example: -23 */ MinusSign: 5, /** * Computer notation for exponential value (n times a power of 10). * Example: 1.2E3 */ Exponential: 6, /** * Human-readable format of exponential. * Example: 1.2x103 */ SuperscriptingExponent: 7, /** * Sign for permille (out of 1000). * Example: 23.4‰ */ PerMille: 8, /** * Infinity, can be used with plus and minus. * Example: ∞, +∞, -∞ */ Infinity: 9, /** * Not a number. * Example: NaN */ NaN: 10, /** * Symbol used between time units. * Example: 10:52 */ TimeSeparator: 11, /** * Decimal separator for currency values (fallback to `Decimal`). * Example: $2,345.67 */ CurrencyDecimal: 12, /** * Group separator for currency values (fallback to `Group`). * Example: $2,345.67 */ CurrencyGroup: 13, }; /** * The value for each day of the week, based on the `en-US` locale * * @publicApi */ var WeekDay; (function (WeekDay) { WeekDay[WeekDay["Sunday"] = 0] = "Sunday"; WeekDay[WeekDay["Monday"] = 1] = "Monday"; WeekDay[WeekDay["Tuesday"] = 2] = "Tuesday"; WeekDay[WeekDay["Wednesday"] = 3] = "Wednesday"; WeekDay[WeekDay["Thursday"] = 4] = "Thursday"; WeekDay[WeekDay["Friday"] = 5] = "Friday"; WeekDay[WeekDay["Saturday"] = 6] = "Saturday"; })(WeekDay || (WeekDay = {})); /** * Retrieves the locale ID from the currently loaded locale. * The loaded locale could be, for example, a global one rather than a regional one. * @param locale A locale code, such as `fr-FR`. * @returns The locale code. For example, `fr`. * @see [Internationalization (i18n) Guide](/guide/i18n-overview) * * @publicApi */ function getLocaleId(locale) { return ɵfindLocaleData(locale)[ɵLocaleDataIndex.LocaleId]; } /** * Retrieves day period strings for the given locale. * * @param locale A locale code for the locale format rules to use. * @param formStyle The required grammatical form. * @param width The required character width. * @returns An array of localized period strings. For example, `[AM, PM]` for `en-US`. * @see [Internationalization (i18n) Guide](/guide/i18n-overview) * * @publicApi */ function getLocaleDayPeriods(locale, formStyle, width) { const data = ɵfindLocaleData(locale); const amPmData = [ data[ɵLocaleDataIndex.DayPeriodsFormat], data[ɵLocaleDataIndex.DayPeriodsStandalone], ]; const amPm = getLastDefinedValue(amPmData, formStyle); return getLastDefinedValue(amPm, width); } /** * Retrieves days of the week for the given locale, using the Gregorian calendar. * * @param locale A locale code for the locale format rules to use. * @param formStyle The required grammatical form. * @param width The required character width. * @returns An array of localized name strings. * For example,`[Sunday, Monday, ... Saturday]` for `en-US`. * @see [Internationalization (i18n) Guide](/guide/i18n-overview) * * @publicApi */ function getLocaleDayNames(locale, formStyle, width) { const data = ɵfindLocaleData(locale); const daysData = [ data[ɵLocaleDataIndex.DaysFormat], data[ɵLocaleDataIndex.DaysStandalone], ]; const days = getLastDefinedValue(daysData, formStyle); return getLastDefinedValue(days, width); } /** * Retrieves months of the year for the given locale, using the Gregorian calendar. * * @param locale A locale code for the locale format rules to use. * @param formStyle The required grammatical form. * @param width The required character width. * @returns An array of localized name strings. * For example, `[January, February, ...]` for `en-US`. * @see [Internationalization (i18n) Guide](/guide/i18n-overview) * * @publicApi */ function getLocaleMonthNames(locale, formStyle, width) { const data = ɵfindLocaleData(locale); const monthsData = [ data[ɵLocaleDataIndex.MonthsFormat], data[ɵLocaleDataIndex.MonthsStandalone], ]; const months = getLastDefinedValue(monthsData, formStyle); return getLastDefinedValue(months, width); } /** * Retrieves Gregorian-calendar eras for the given locale. * @param locale A locale code for the locale format rules to use. * @param width The required character width. * @returns An array of localized era strings. * For example, `[AD, BC]` for `en-US`. * @see [Internationalization (i18n) Guide](/guide/i18n-overview) * * @publicApi */ function getLocaleEraNames(locale, width) { const data = ɵfindLocaleData(locale); const erasData = data[ɵLocaleDataIndex.Eras]; return getLastDefinedValue(erasData, width); } /** * Retrieves the first day of the week for the given locale. * * @param locale A locale code for the locale format rules to use. * @returns A day index number, using the 0-based week-day index for `en-US` * (Sunday = 0, Monday = 1, ...). * For example, for `fr-FR`, returns 1 to indicate that the first day is Monday. * @see [Internationalization (i18n) Guide](/guide/i18n-overview) * * @publicApi */ function getLocaleFirstDayOfWeek(locale) { const data = ɵfindLocaleData(locale); return data[ɵLocaleDataIndex.FirstDayOfWeek]; } /** * Range of week days that are considered the week-end for the given locale. * * @param locale A locale code for the locale format rules to use. * @returns The range of day values, `[startDay, endDay]`. * @see [Internationalization (i18n) Guide](/guide/i18n-overview) * * @publicApi */ function getLocaleWeekEndRange(locale) { const data = ɵfindLocaleData(locale); return data[ɵLocaleDataIndex.WeekendRange]; } /** * Retrieves a localized date-value formatting string. * * @param locale A locale code for the locale format rules to use. * @param width The format type. * @returns The localized formatting string. * @see {@link FormatWidth} * @see [Internationalization (i18n) Guide](/guide/i18n-overview) * * @publicApi */ function getLocaleDateFormat(locale, width) { const data = ɵfindLocaleData(locale); return getLastDefinedValue(data[ɵLocaleDataIndex.DateFormat], width); } /** * Retrieves a localized time-value formatting string. * * @param locale A locale code for the locale format rules to use. * @param width The format type. * @returns The localized formatting string. * @see {@link FormatWidth} * @see [Internationalization (i18n) Guide](/guide/i18n-overview) * @publicApi */ function getLocaleTimeFormat(locale, width) { const data = ɵfindLocaleData(locale); return getLastDefinedValue(data[ɵLocaleDataIndex.TimeFormat], width); } /** * Retrieves a localized date-time formatting string. * * @param locale A locale code for the locale format rules to use. * @param width The format type. * @returns The localized formatting string. * @see {@link FormatWidth} * @see [Internationalization (i18n) Guide](/guide/i18n-overview) * * @publicApi */ function getLocaleDateTimeFormat(locale, width) { const data = ɵfindLocaleData(locale); const dateTimeFormatData = data[ɵLocaleDataIndex.DateTimeFormat]; return getLastDefinedValue(dateTimeFormatData, width); } /** * Retrieves a localized number symbol that can be used to replace placeholders in number formats. * @param locale The locale code. * @param symbol The symbol to localize. Must be one of `NumberSymbol`. * @returns The character for the localized symbol. * @see {@link NumberSymbol} * @see [Internationalization (i18n) Guide](/guide/i18n-overview) * * @publicApi */ function getLocaleNumberSymbol(locale, symbol) { const data = ɵfindLocaleData(locale); const res = data[ɵLocaleDataIndex.NumberSymbols][symbol]; if (typeof res === 'undefined') { if (symbol === NumberSymbol.CurrencyDecimal) { return data[ɵLocaleDataIndex.NumberSymbols][NumberSymbol.Decimal]; } else if (symbol === NumberSymbol.CurrencyGroup) { return data[ɵLocaleDataIndex.NumberSymbols][NumberSymbol.Group]; } } return res; } /** * Retrieves a number format for a given locale. * * Numbers are formatted using patterns, like `#,###.00`. For example, the pattern `#,###.00` * when used to format the number 12345.678 could result in "12'345,678". That would happen if the * grouping separator for your language is an apostrophe, and the decimal separator is a comma. * * <b>Important:</b> The characters `.` `,` `0` `#` (and others below) are special placeholders * that stand for the decimal separator, and so on, and are NOT real characters. * You must NOT "translate" the placeholders. For example, don't change `.` to `,` even though in * your language the decimal point is written with a comma. The symbols should be replaced by the * local equivalents, using the appropriate `NumberSymbol` for your language. * * Here are the special characters used in number patterns: * * | Symbol | Meaning | * |--------|---------| * | . | Replaced automatically by the character used for the decimal point. | * | , | Replaced by the "grouping" (thousands) separator. | * | 0 | Replaced by a digit (or zero if there aren't enough digits). | * | # | Replaced by a digit (or nothing if there aren't enough). | * | ¤ | Replaced by a currency symbol, such as $ or USD. | * | % | Marks a percent format. The % symbol may change position, but must be retained. | * | E | Marks a scientific format. The E symbol may change position, but must be retained. | * | ' | Special characters used as literal characters are quoted with ASCII single quotes. | * * @param locale A locale code for the locale format rules to use. * @param type The type of numeric value to be formatted (such as `Decimal` or `Currency`.) * @returns The localized format string. * @see {@link NumberFormatStyle} * @see [CLDR website](http://cldr.unicode.org/translation/number-patterns) * @see [Internationalization (i18n) Guide](/guide/i18n-overview) * * @publicApi */ function getLocaleNumberFormat(locale, type) { const data = ɵfindLocaleData(locale); return data[ɵLocaleDataIndex.NumberFormats][type]; } /** * Retrieves the symbol used to represent the currency for the main country * corresponding to a given locale. For example, '$' for `en-US`. * * @param locale A locale code for the locale format rules to use. * @returns The localized symbol character, * or `null` if the main country cannot be determined. * @see [Internationalization (i18n) Guide](/guide/i18n-overview) * * @publicApi */ function getLocaleCurrencySymbol(locale) { const data = ɵfindLocaleData(locale); return data[ɵLocaleDataIndex.CurrencySymbol] || null; } /** * Retrieves the name of the currency for the main country corresponding * to a given locale. For example, 'US Dollar' for `en-US`. * @param locale A locale code for the locale format rules to use. * @returns The currency name, * or `null` if the main country cannot be determined. * @see [Internationalization (i18n) Guide](/guide/i18n-overview) * * @publicApi */ function getLocaleCurrencyName(locale) { const data = ɵfindLocaleData(locale); return data[ɵLocaleDataIndex.CurrencyName] || null; } /** * Retrieves the default currency code for the given locale. * * The default is defined as the first currency which is still in use. * * @param locale The code of the locale whose currency code we want. * @returns The code of the default currency for the given locale. * * @publicApi */ function getLocaleCurrencyCode(locale) { return ɵgetLocaleCurrencyCode(locale); } /** * Retrieves the currency values for a given locale. * @param locale A locale code for the locale format rules to use. * @returns The currency values. * @see [Internationalization (i18n) Guide](/guide/i18n-overview) */ function getLocaleCurrencies(locale) { const data = ɵfindLocaleData(locale); return data[ɵLocaleDataIndex.Currencies]; } /** * @alias core/ɵgetLocalePluralCase * @publicApi */ const getLocalePluralCase = ɵgetLocalePluralCase; function checkFullData(data) { if (!data[ɵLocaleDataIndex.ExtraData]) { throw new Error(`Missing extra locale data for the locale "${data[ɵLocaleDataIndex.LocaleId]}". Use "registerLocaleData" to load new data. See the "I18n guide" on angular.io to know more.`); } } /** * Retrieves locale-specific rules used to determine which day period to use * when more than one period is defined for a locale. * * There is a rule for each defined day period. The * first rule is applied to the first day period and so on. * Fall back to AM/PM when no rules are available. * * A rule can specify a period as time range, or as a single time value. * * This functionality is only available when you have loaded the full locale data. * See the ["I18n guide"](guide/i18n-common-format-data-locale). * * @param locale A locale code for the locale format rules to use. * @returns The rules for the locale, a single time value