[6a3a178] | 1 | /**
|
---|
[e29cc2e] | 2 | * @license Angular v12.2.13
|
---|
[6a3a178] | 3 | * (c) 2010-2021 Google LLC. https://angular.io/
|
---|
| 4 | * License: MIT
|
---|
| 5 | */
|
---|
| 6 |
|
---|
| 7 | import { InjectionToken, ɵɵdefineInjectable, Injectable, ɵɵinject, Inject, Optional, EventEmitter, ɵfindLocaleData, ɵLocaleDataIndex, ɵgetLocaleCurrencyCode, ɵgetLocalePluralCase, LOCALE_ID, ɵregisterLocaleData, ɵisListLikeIterable, ɵstringify, Directive, IterableDiffers, KeyValueDiffers, ElementRef, Renderer2, Input, NgModuleRef, ComponentFactoryResolver, ViewContainerRef, TemplateRef, Host, ɵRuntimeError, Attribute, ɵisPromise, ɵisSubscribable, Pipe, ChangeDetectorRef, DEFAULT_CURRENCY_CODE, NgModule, Version } from '@angular/core';
|
---|
| 8 |
|
---|
| 9 | /**
|
---|
| 10 | * @license
|
---|
| 11 | * Copyright Google LLC All Rights Reserved.
|
---|
| 12 | *
|
---|
| 13 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 14 | * found in the LICENSE file at https://angular.io/license
|
---|
| 15 | */
|
---|
| 16 | let _DOM = null;
|
---|
| 17 | function getDOM() {
|
---|
| 18 | return _DOM;
|
---|
| 19 | }
|
---|
| 20 | function setDOM(adapter) {
|
---|
| 21 | _DOM = adapter;
|
---|
| 22 | }
|
---|
| 23 | function setRootDomAdapter(adapter) {
|
---|
| 24 | if (!_DOM) {
|
---|
| 25 | _DOM = adapter;
|
---|
| 26 | }
|
---|
| 27 | }
|
---|
| 28 | /* tslint:disable:requireParameterType */
|
---|
| 29 | /**
|
---|
| 30 | * Provides DOM operations in an environment-agnostic way.
|
---|
| 31 | *
|
---|
| 32 | * @security Tread carefully! Interacting with the DOM directly is dangerous and
|
---|
| 33 | * can introduce XSS risks.
|
---|
| 34 | */
|
---|
| 35 | class DomAdapter {
|
---|
| 36 | }
|
---|
| 37 |
|
---|
| 38 | /**
|
---|
| 39 | * @license
|
---|
| 40 | * Copyright Google LLC All Rights Reserved.
|
---|
| 41 | *
|
---|
| 42 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 43 | * found in the LICENSE file at https://angular.io/license
|
---|
| 44 | */
|
---|
| 45 | /**
|
---|
| 46 | * A DI Token representing the main rendering context. In a browser this is the DOM Document.
|
---|
| 47 | *
|
---|
| 48 | * Note: Document might not be available in the Application Context when Application and Rendering
|
---|
| 49 | * Contexts are not the same (e.g. when running the application in a Web Worker).
|
---|
| 50 | *
|
---|
| 51 | * @publicApi
|
---|
| 52 | */
|
---|
| 53 | const DOCUMENT = new InjectionToken('DocumentToken');
|
---|
| 54 |
|
---|
| 55 | /**
|
---|
| 56 | * @license
|
---|
| 57 | * Copyright Google LLC All Rights Reserved.
|
---|
| 58 | *
|
---|
| 59 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 60 | * found in the LICENSE file at https://angular.io/license
|
---|
| 61 | */
|
---|
| 62 | /**
|
---|
| 63 | * This class should not be used directly by an application developer. Instead, use
|
---|
| 64 | * {@link Location}.
|
---|
| 65 | *
|
---|
| 66 | * `PlatformLocation` encapsulates all calls to DOM APIs, which allows the Router to be
|
---|
| 67 | * platform-agnostic.
|
---|
| 68 | * This means that we can have different implementation of `PlatformLocation` for the different
|
---|
| 69 | * platforms that Angular supports. For example, `@angular/platform-browser` provides an
|
---|
| 70 | * implementation specific to the browser environment, while `@angular/platform-server` provides
|
---|
| 71 | * one suitable for use with server-side rendering.
|
---|
| 72 | *
|
---|
| 73 | * The `PlatformLocation` class is used directly by all implementations of {@link LocationStrategy}
|
---|
| 74 | * when they need to interact with the DOM APIs like pushState, popState, etc.
|
---|
| 75 | *
|
---|
| 76 | * {@link LocationStrategy} in turn is used by the {@link Location} service which is used directly
|
---|
| 77 | * by the {@link Router} in order to navigate between routes. Since all interactions between {@link
|
---|
| 78 | * Router} /
|
---|
| 79 | * {@link Location} / {@link LocationStrategy} and DOM APIs flow through the `PlatformLocation`
|
---|
| 80 | * class, they are all platform-agnostic.
|
---|
| 81 | *
|
---|
| 82 | * @publicApi
|
---|
| 83 | */
|
---|
| 84 | class PlatformLocation {
|
---|
| 85 | historyGo(relativePosition) {
|
---|
| 86 | throw new Error('Not implemented');
|
---|
| 87 | }
|
---|
| 88 | }
|
---|
| 89 | PlatformLocation.ɵprov = ɵɵdefineInjectable({ factory: useBrowserPlatformLocation, token: PlatformLocation, providedIn: "platform" });
|
---|
| 90 | PlatformLocation.decorators = [
|
---|
| 91 | { type: Injectable, args: [{
|
---|
| 92 | providedIn: 'platform',
|
---|
| 93 | // See #23917
|
---|
| 94 | useFactory: useBrowserPlatformLocation
|
---|
| 95 | },] }
|
---|
| 96 | ];
|
---|
| 97 | function useBrowserPlatformLocation() {
|
---|
| 98 | return ɵɵinject(BrowserPlatformLocation);
|
---|
| 99 | }
|
---|
| 100 | /**
|
---|
| 101 | * @description
|
---|
| 102 | * Indicates when a location is initialized.
|
---|
| 103 | *
|
---|
| 104 | * @publicApi
|
---|
| 105 | */
|
---|
| 106 | const LOCATION_INITIALIZED = new InjectionToken('Location Initialized');
|
---|
| 107 | /**
|
---|
| 108 | * `PlatformLocation` encapsulates all of the direct calls to platform APIs.
|
---|
| 109 | * This class should not be used directly by an application developer. Instead, use
|
---|
| 110 | * {@link Location}.
|
---|
| 111 | */
|
---|
| 112 | class BrowserPlatformLocation extends PlatformLocation {
|
---|
| 113 | constructor(_doc) {
|
---|
| 114 | super();
|
---|
| 115 | this._doc = _doc;
|
---|
| 116 | this._init();
|
---|
| 117 | }
|
---|
| 118 | // This is moved to its own method so that `MockPlatformLocationStrategy` can overwrite it
|
---|
| 119 | /** @internal */
|
---|
| 120 | _init() {
|
---|
| 121 | this.location = window.location;
|
---|
| 122 | this._history = window.history;
|
---|
| 123 | }
|
---|
| 124 | getBaseHrefFromDOM() {
|
---|
| 125 | return getDOM().getBaseHref(this._doc);
|
---|
| 126 | }
|
---|
| 127 | onPopState(fn) {
|
---|
| 128 | const window = getDOM().getGlobalEventTarget(this._doc, 'window');
|
---|
| 129 | window.addEventListener('popstate', fn, false);
|
---|
| 130 | return () => window.removeEventListener('popstate', fn);
|
---|
| 131 | }
|
---|
| 132 | onHashChange(fn) {
|
---|
| 133 | const window = getDOM().getGlobalEventTarget(this._doc, 'window');
|
---|
| 134 | window.addEventListener('hashchange', fn, false);
|
---|
| 135 | return () => window.removeEventListener('hashchange', fn);
|
---|
| 136 | }
|
---|
| 137 | get href() {
|
---|
| 138 | return this.location.href;
|
---|
| 139 | }
|
---|
| 140 | get protocol() {
|
---|
| 141 | return this.location.protocol;
|
---|
| 142 | }
|
---|
| 143 | get hostname() {
|
---|
| 144 | return this.location.hostname;
|
---|
| 145 | }
|
---|
| 146 | get port() {
|
---|
| 147 | return this.location.port;
|
---|
| 148 | }
|
---|
| 149 | get pathname() {
|
---|
| 150 | return this.location.pathname;
|
---|
| 151 | }
|
---|
| 152 | get search() {
|
---|
| 153 | return this.location.search;
|
---|
| 154 | }
|
---|
| 155 | get hash() {
|
---|
| 156 | return this.location.hash;
|
---|
| 157 | }
|
---|
| 158 | set pathname(newPath) {
|
---|
| 159 | this.location.pathname = newPath;
|
---|
| 160 | }
|
---|
| 161 | pushState(state, title, url) {
|
---|
| 162 | if (supportsState()) {
|
---|
| 163 | this._history.pushState(state, title, url);
|
---|
| 164 | }
|
---|
| 165 | else {
|
---|
| 166 | this.location.hash = url;
|
---|
| 167 | }
|
---|
| 168 | }
|
---|
| 169 | replaceState(state, title, url) {
|
---|
| 170 | if (supportsState()) {
|
---|
| 171 | this._history.replaceState(state, title, url);
|
---|
| 172 | }
|
---|
| 173 | else {
|
---|
| 174 | this.location.hash = url;
|
---|
| 175 | }
|
---|
| 176 | }
|
---|
| 177 | forward() {
|
---|
| 178 | this._history.forward();
|
---|
| 179 | }
|
---|
| 180 | back() {
|
---|
| 181 | this._history.back();
|
---|
| 182 | }
|
---|
| 183 | historyGo(relativePosition = 0) {
|
---|
| 184 | this._history.go(relativePosition);
|
---|
| 185 | }
|
---|
| 186 | getState() {
|
---|
| 187 | return this._history.state;
|
---|
| 188 | }
|
---|
| 189 | }
|
---|
| 190 | BrowserPlatformLocation.ɵprov = ɵɵdefineInjectable({ factory: createBrowserPlatformLocation, token: BrowserPlatformLocation, providedIn: "platform" });
|
---|
| 191 | BrowserPlatformLocation.decorators = [
|
---|
| 192 | { type: Injectable, args: [{
|
---|
| 193 | providedIn: 'platform',
|
---|
| 194 | // See #23917
|
---|
| 195 | useFactory: createBrowserPlatformLocation,
|
---|
| 196 | },] }
|
---|
| 197 | ];
|
---|
| 198 | BrowserPlatformLocation.ctorParameters = () => [
|
---|
| 199 | { type: undefined, decorators: [{ type: Inject, args: [DOCUMENT,] }] }
|
---|
| 200 | ];
|
---|
| 201 | function supportsState() {
|
---|
| 202 | return !!window.history.pushState;
|
---|
| 203 | }
|
---|
| 204 | function createBrowserPlatformLocation() {
|
---|
| 205 | return new BrowserPlatformLocation(ɵɵinject(DOCUMENT));
|
---|
| 206 | }
|
---|
| 207 |
|
---|
| 208 | /**
|
---|
| 209 | * @license
|
---|
| 210 | * Copyright Google LLC All Rights Reserved.
|
---|
| 211 | *
|
---|
| 212 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 213 | * found in the LICENSE file at https://angular.io/license
|
---|
| 214 | */
|
---|
| 215 |
|
---|
| 216 | /**
|
---|
| 217 | * @license
|
---|
| 218 | * Copyright Google LLC All Rights Reserved.
|
---|
| 219 | *
|
---|
| 220 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 221 | * found in the LICENSE file at https://angular.io/license
|
---|
| 222 | */
|
---|
| 223 | /**
|
---|
| 224 | * Joins two parts of a URL with a slash if needed.
|
---|
| 225 | *
|
---|
| 226 | * @param start URL string
|
---|
| 227 | * @param end URL string
|
---|
| 228 | *
|
---|
| 229 | *
|
---|
| 230 | * @returns The joined URL string.
|
---|
| 231 | */
|
---|
| 232 | function joinWithSlash(start, end) {
|
---|
| 233 | if (start.length == 0) {
|
---|
| 234 | return end;
|
---|
| 235 | }
|
---|
| 236 | if (end.length == 0) {
|
---|
| 237 | return start;
|
---|
| 238 | }
|
---|
| 239 | let slashes = 0;
|
---|
| 240 | if (start.endsWith('/')) {
|
---|
| 241 | slashes++;
|
---|
| 242 | }
|
---|
| 243 | if (end.startsWith('/')) {
|
---|
| 244 | slashes++;
|
---|
| 245 | }
|
---|
| 246 | if (slashes == 2) {
|
---|
| 247 | return start + end.substring(1);
|
---|
| 248 | }
|
---|
| 249 | if (slashes == 1) {
|
---|
| 250 | return start + end;
|
---|
| 251 | }
|
---|
| 252 | return start + '/' + end;
|
---|
| 253 | }
|
---|
| 254 | /**
|
---|
| 255 | * Removes a trailing slash from a URL string if needed.
|
---|
| 256 | * Looks for the first occurrence of either `#`, `?`, or the end of the
|
---|
| 257 | * line as `/` characters and removes the trailing slash if one exists.
|
---|
| 258 | *
|
---|
| 259 | * @param url URL string.
|
---|
| 260 | *
|
---|
| 261 | * @returns The URL string, modified if needed.
|
---|
| 262 | */
|
---|
| 263 | function stripTrailingSlash(url) {
|
---|
| 264 | const match = url.match(/#|\?|$/);
|
---|
| 265 | const pathEndIdx = match && match.index || url.length;
|
---|
| 266 | const droppedSlashIdx = pathEndIdx - (url[pathEndIdx - 1] === '/' ? 1 : 0);
|
---|
| 267 | return url.slice(0, droppedSlashIdx) + url.slice(pathEndIdx);
|
---|
| 268 | }
|
---|
| 269 | /**
|
---|
| 270 | * Normalizes URL parameters by prepending with `?` if needed.
|
---|
| 271 | *
|
---|
| 272 | * @param params String of URL parameters.
|
---|
| 273 | *
|
---|
| 274 | * @returns The normalized URL parameters string.
|
---|
| 275 | */
|
---|
| 276 | function normalizeQueryParams(params) {
|
---|
| 277 | return params && params[0] !== '?' ? '?' + params : params;
|
---|
| 278 | }
|
---|
| 279 |
|
---|
| 280 | /**
|
---|
| 281 | * @license
|
---|
| 282 | * Copyright Google LLC All Rights Reserved.
|
---|
| 283 | *
|
---|
| 284 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 285 | * found in the LICENSE file at https://angular.io/license
|
---|
| 286 | */
|
---|
| 287 | /**
|
---|
| 288 | * Enables the `Location` service to read route state from the browser's URL.
|
---|
| 289 | * Angular provides two strategies:
|
---|
| 290 | * `HashLocationStrategy` and `PathLocationStrategy`.
|
---|
| 291 | *
|
---|
| 292 | * Applications should use the `Router` or `Location` services to
|
---|
| 293 | * interact with application route state.
|
---|
| 294 | *
|
---|
| 295 | * For instance, `HashLocationStrategy` produces URLs like
|
---|
| 296 | * <code class="no-auto-link">http://example.com#/foo</code>,
|
---|
| 297 | * and `PathLocationStrategy` produces
|
---|
| 298 | * <code class="no-auto-link">http://example.com/foo</code> as an equivalent URL.
|
---|
| 299 | *
|
---|
| 300 | * See these two classes for more.
|
---|
| 301 | *
|
---|
| 302 | * @publicApi
|
---|
| 303 | */
|
---|
| 304 | class LocationStrategy {
|
---|
| 305 | historyGo(relativePosition) {
|
---|
| 306 | throw new Error('Not implemented');
|
---|
| 307 | }
|
---|
| 308 | }
|
---|
| 309 | LocationStrategy.ɵprov = ɵɵdefineInjectable({ factory: provideLocationStrategy, token: LocationStrategy, providedIn: "root" });
|
---|
| 310 | LocationStrategy.decorators = [
|
---|
| 311 | { type: Injectable, args: [{ providedIn: 'root', useFactory: provideLocationStrategy },] }
|
---|
| 312 | ];
|
---|
| 313 | function provideLocationStrategy(platformLocation) {
|
---|
| 314 | // See #23917
|
---|
| 315 | const location = ɵɵinject(DOCUMENT).location;
|
---|
| 316 | return new PathLocationStrategy(ɵɵinject(PlatformLocation), location && location.origin || '');
|
---|
| 317 | }
|
---|
| 318 | /**
|
---|
| 319 | * A predefined [DI token](guide/glossary#di-token) for the base href
|
---|
| 320 | * to be used with the `PathLocationStrategy`.
|
---|
| 321 | * The base href is the URL prefix that should be preserved when generating
|
---|
| 322 | * and recognizing URLs.
|
---|
| 323 | *
|
---|
| 324 | * @usageNotes
|
---|
| 325 | *
|
---|
| 326 | * The following example shows how to use this token to configure the root app injector
|
---|
| 327 | * with a base href value, so that the DI framework can supply the dependency anywhere in the app.
|
---|
| 328 | *
|
---|
| 329 | * ```typescript
|
---|
| 330 | * import {Component, NgModule} from '@angular/core';
|
---|
| 331 | * import {APP_BASE_HREF} from '@angular/common';
|
---|
| 332 | *
|
---|
| 333 | * @NgModule({
|
---|
| 334 | * providers: [{provide: APP_BASE_HREF, useValue: '/my/app'}]
|
---|
| 335 | * })
|
---|
| 336 | * class AppModule {}
|
---|
| 337 | * ```
|
---|
| 338 | *
|
---|
| 339 | * @publicApi
|
---|
| 340 | */
|
---|
| 341 | const APP_BASE_HREF = new InjectionToken('appBaseHref');
|
---|
| 342 | /**
|
---|
| 343 | * @description
|
---|
| 344 | * A {@link LocationStrategy} used to configure the {@link Location} service to
|
---|
| 345 | * represent its state in the
|
---|
| 346 | * [path](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) of the
|
---|
| 347 | * browser's URL.
|
---|
| 348 | *
|
---|
| 349 | * If you're using `PathLocationStrategy`, you must provide a {@link APP_BASE_HREF}
|
---|
| 350 | * or add a `<base href>` element to the document.
|
---|
| 351 | *
|
---|
| 352 | * For instance, if you provide an `APP_BASE_HREF` of `'/my/app/'` and call
|
---|
| 353 | * `location.go('/foo')`, the browser's URL will become
|
---|
| 354 | * `example.com/my/app/foo`. To ensure all relative URIs resolve correctly,
|
---|
| 355 | * the `<base href>` and/or `APP_BASE_HREF` should end with a `/`.
|
---|
| 356 | *
|
---|
| 357 | * Similarly, if you add `<base href='/my/app/'/>` to the document and call
|
---|
| 358 | * `location.go('/foo')`, the browser's URL will become
|
---|
| 359 | * `example.com/my/app/foo`.
|
---|
| 360 | *
|
---|
| 361 | * Note that when using `PathLocationStrategy`, neither the query nor
|
---|
| 362 | * the fragment in the `<base href>` will be preserved, as outlined
|
---|
| 363 | * by the [RFC](https://tools.ietf.org/html/rfc3986#section-5.2.2).
|
---|
| 364 | *
|
---|
| 365 | * @usageNotes
|
---|
| 366 | *
|
---|
| 367 | * ### Example
|
---|
| 368 | *
|
---|
| 369 | * {@example common/location/ts/path_location_component.ts region='LocationComponent'}
|
---|
| 370 | *
|
---|
| 371 | * @publicApi
|
---|
| 372 | */
|
---|
| 373 | class PathLocationStrategy extends LocationStrategy {
|
---|
| 374 | constructor(_platformLocation, href) {
|
---|
| 375 | super();
|
---|
| 376 | this._platformLocation = _platformLocation;
|
---|
| 377 | this._removeListenerFns = [];
|
---|
| 378 | if (href == null) {
|
---|
| 379 | href = this._platformLocation.getBaseHrefFromDOM();
|
---|
| 380 | }
|
---|
| 381 | if (href == null) {
|
---|
| 382 | throw new Error(`No base href set. Please provide a value for the APP_BASE_HREF token or add a base element to the document.`);
|
---|
| 383 | }
|
---|
| 384 | this._baseHref = href;
|
---|
| 385 | }
|
---|
| 386 | ngOnDestroy() {
|
---|
| 387 | while (this._removeListenerFns.length) {
|
---|
| 388 | this._removeListenerFns.pop()();
|
---|
| 389 | }
|
---|
| 390 | }
|
---|
| 391 | onPopState(fn) {
|
---|
| 392 | this._removeListenerFns.push(this._platformLocation.onPopState(fn), this._platformLocation.onHashChange(fn));
|
---|
| 393 | }
|
---|
| 394 | getBaseHref() {
|
---|
| 395 | return this._baseHref;
|
---|
| 396 | }
|
---|
| 397 | prepareExternalUrl(internal) {
|
---|
| 398 | return joinWithSlash(this._baseHref, internal);
|
---|
| 399 | }
|
---|
| 400 | path(includeHash = false) {
|
---|
| 401 | const pathname = this._platformLocation.pathname + normalizeQueryParams(this._platformLocation.search);
|
---|
| 402 | const hash = this._platformLocation.hash;
|
---|
| 403 | return hash && includeHash ? `${pathname}${hash}` : pathname;
|
---|
| 404 | }
|
---|
| 405 | pushState(state, title, url, queryParams) {
|
---|
| 406 | const externalUrl = this.prepareExternalUrl(url + normalizeQueryParams(queryParams));
|
---|
| 407 | this._platformLocation.pushState(state, title, externalUrl);
|
---|
| 408 | }
|
---|
| 409 | replaceState(state, title, url, queryParams) {
|
---|
| 410 | const externalUrl = this.prepareExternalUrl(url + normalizeQueryParams(queryParams));
|
---|
| 411 | this._platformLocation.replaceState(state, title, externalUrl);
|
---|
| 412 | }
|
---|
| 413 | forward() {
|
---|
| 414 | this._platformLocation.forward();
|
---|
| 415 | }
|
---|
| 416 | back() {
|
---|
| 417 | this._platformLocation.back();
|
---|
| 418 | }
|
---|
| 419 | historyGo(relativePosition = 0) {
|
---|
| 420 | var _a, _b;
|
---|
| 421 | (_b = (_a = this._platformLocation).historyGo) === null || _b === void 0 ? void 0 : _b.call(_a, relativePosition);
|
---|
| 422 | }
|
---|
| 423 | }
|
---|
| 424 | PathLocationStrategy.decorators = [
|
---|
| 425 | { type: Injectable }
|
---|
| 426 | ];
|
---|
| 427 | PathLocationStrategy.ctorParameters = () => [
|
---|
| 428 | { type: PlatformLocation },
|
---|
| 429 | { type: String, decorators: [{ type: Optional }, { type: Inject, args: [APP_BASE_HREF,] }] }
|
---|
| 430 | ];
|
---|
| 431 |
|
---|
| 432 | /**
|
---|
| 433 | * @license
|
---|
| 434 | * Copyright Google LLC All Rights Reserved.
|
---|
| 435 | *
|
---|
| 436 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 437 | * found in the LICENSE file at https://angular.io/license
|
---|
| 438 | */
|
---|
| 439 | /**
|
---|
| 440 | * @description
|
---|
| 441 | * A {@link LocationStrategy} used to configure the {@link Location} service to
|
---|
| 442 | * represent its state in the
|
---|
| 443 | * [hash fragment](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax)
|
---|
| 444 | * of the browser's URL.
|
---|
| 445 | *
|
---|
| 446 | * For instance, if you call `location.go('/foo')`, the browser's URL will become
|
---|
| 447 | * `example.com#/foo`.
|
---|
| 448 | *
|
---|
| 449 | * @usageNotes
|
---|
| 450 | *
|
---|
| 451 | * ### Example
|
---|
| 452 | *
|
---|
| 453 | * {@example common/location/ts/hash_location_component.ts region='LocationComponent'}
|
---|
| 454 | *
|
---|
| 455 | * @publicApi
|
---|
| 456 | */
|
---|
| 457 | class HashLocationStrategy extends LocationStrategy {
|
---|
| 458 | constructor(_platformLocation, _baseHref) {
|
---|
| 459 | super();
|
---|
| 460 | this._platformLocation = _platformLocation;
|
---|
| 461 | this._baseHref = '';
|
---|
| 462 | this._removeListenerFns = [];
|
---|
| 463 | if (_baseHref != null) {
|
---|
| 464 | this._baseHref = _baseHref;
|
---|
| 465 | }
|
---|
| 466 | }
|
---|
| 467 | ngOnDestroy() {
|
---|
| 468 | while (this._removeListenerFns.length) {
|
---|
| 469 | this._removeListenerFns.pop()();
|
---|
| 470 | }
|
---|
| 471 | }
|
---|
| 472 | onPopState(fn) {
|
---|
| 473 | this._removeListenerFns.push(this._platformLocation.onPopState(fn), this._platformLocation.onHashChange(fn));
|
---|
| 474 | }
|
---|
| 475 | getBaseHref() {
|
---|
| 476 | return this._baseHref;
|
---|
| 477 | }
|
---|
| 478 | path(includeHash = false) {
|
---|
| 479 | // the hash value is always prefixed with a `#`
|
---|
| 480 | // and if it is empty then it will stay empty
|
---|
| 481 | let path = this._platformLocation.hash;
|
---|
| 482 | if (path == null)
|
---|
| 483 | path = '#';
|
---|
| 484 | return path.length > 0 ? path.substring(1) : path;
|
---|
| 485 | }
|
---|
| 486 | prepareExternalUrl(internal) {
|
---|
| 487 | const url = joinWithSlash(this._baseHref, internal);
|
---|
| 488 | return url.length > 0 ? ('#' + url) : url;
|
---|
| 489 | }
|
---|
| 490 | pushState(state, title, path, queryParams) {
|
---|
| 491 | let url = this.prepareExternalUrl(path + normalizeQueryParams(queryParams));
|
---|
| 492 | if (url.length == 0) {
|
---|
| 493 | url = this._platformLocation.pathname;
|
---|
| 494 | }
|
---|
| 495 | this._platformLocation.pushState(state, title, url);
|
---|
| 496 | }
|
---|
| 497 | replaceState(state, title, path, queryParams) {
|
---|
| 498 | let url = this.prepareExternalUrl(path + normalizeQueryParams(queryParams));
|
---|
| 499 | if (url.length == 0) {
|
---|
| 500 | url = this._platformLocation.pathname;
|
---|
| 501 | }
|
---|
| 502 | this._platformLocation.replaceState(state, title, url);
|
---|
| 503 | }
|
---|
| 504 | forward() {
|
---|
| 505 | this._platformLocation.forward();
|
---|
| 506 | }
|
---|
| 507 | back() {
|
---|
| 508 | this._platformLocation.back();
|
---|
| 509 | }
|
---|
| 510 | historyGo(relativePosition = 0) {
|
---|
| 511 | var _a, _b;
|
---|
| 512 | (_b = (_a = this._platformLocation).historyGo) === null || _b === void 0 ? void 0 : _b.call(_a, relativePosition);
|
---|
| 513 | }
|
---|
| 514 | }
|
---|
| 515 | HashLocationStrategy.decorators = [
|
---|
| 516 | { type: Injectable }
|
---|
| 517 | ];
|
---|
| 518 | HashLocationStrategy.ctorParameters = () => [
|
---|
| 519 | { type: PlatformLocation },
|
---|
| 520 | { type: String, decorators: [{ type: Optional }, { type: Inject, args: [APP_BASE_HREF,] }] }
|
---|
| 521 | ];
|
---|
| 522 |
|
---|
| 523 | /**
|
---|
| 524 | * @license
|
---|
| 525 | * Copyright Google LLC All Rights Reserved.
|
---|
| 526 | *
|
---|
| 527 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 528 | * found in the LICENSE file at https://angular.io/license
|
---|
| 529 | */
|
---|
| 530 | /**
|
---|
| 531 | * @description
|
---|
| 532 | *
|
---|
| 533 | * A service that applications can use to interact with a browser's URL.
|
---|
| 534 | *
|
---|
| 535 | * Depending on the `LocationStrategy` used, `Location` persists
|
---|
| 536 | * to the URL's path or the URL's hash segment.
|
---|
| 537 | *
|
---|
| 538 | * @usageNotes
|
---|
| 539 | *
|
---|
| 540 | * It's better to use the `Router.navigate()` service to trigger route changes. Use
|
---|
| 541 | * `Location` only if you need to interact with or create normalized URLs outside of
|
---|
| 542 | * routing.
|
---|
| 543 | *
|
---|
| 544 | * `Location` is responsible for normalizing the URL against the application's base href.
|
---|
| 545 | * A normalized URL is absolute from the URL host, includes the application's base href, and has no
|
---|
| 546 | * trailing slash:
|
---|
| 547 | * - `/my/app/user/123` is normalized
|
---|
| 548 | * - `my/app/user/123` **is not** normalized
|
---|
| 549 | * - `/my/app/user/123/` **is not** normalized
|
---|
| 550 | *
|
---|
| 551 | * ### Example
|
---|
| 552 | *
|
---|
| 553 | * <code-example path='common/location/ts/path_location_component.ts'
|
---|
| 554 | * region='LocationComponent'></code-example>
|
---|
| 555 | *
|
---|
| 556 | * @publicApi
|
---|
| 557 | */
|
---|
| 558 | class Location {
|
---|
| 559 | constructor(platformStrategy, platformLocation) {
|
---|
| 560 | /** @internal */
|
---|
| 561 | this._subject = new EventEmitter();
|
---|
| 562 | /** @internal */
|
---|
| 563 | this._urlChangeListeners = [];
|
---|
| 564 | this._platformStrategy = platformStrategy;
|
---|
| 565 | const browserBaseHref = this._platformStrategy.getBaseHref();
|
---|
| 566 | this._platformLocation = platformLocation;
|
---|
| 567 | this._baseHref = stripTrailingSlash(_stripIndexHtml(browserBaseHref));
|
---|
| 568 | this._platformStrategy.onPopState((ev) => {
|
---|
| 569 | this._subject.emit({
|
---|
| 570 | 'url': this.path(true),
|
---|
| 571 | 'pop': true,
|
---|
| 572 | 'state': ev.state,
|
---|
| 573 | 'type': ev.type,
|
---|
| 574 | });
|
---|
| 575 | });
|
---|
| 576 | }
|
---|
| 577 | /**
|
---|
| 578 | * Normalizes the URL path for this location.
|
---|
| 579 | *
|
---|
| 580 | * @param includeHash True to include an anchor fragment in the path.
|
---|
| 581 | *
|
---|
| 582 | * @returns The normalized URL path.
|
---|
| 583 | */
|
---|
| 584 | // TODO: vsavkin. Remove the boolean flag and always include hash once the deprecated router is
|
---|
| 585 | // removed.
|
---|
| 586 | path(includeHash = false) {
|
---|
| 587 | return this.normalize(this._platformStrategy.path(includeHash));
|
---|
| 588 | }
|
---|
| 589 | /**
|
---|
| 590 | * Reports the current state of the location history.
|
---|
| 591 | * @returns The current value of the `history.state` object.
|
---|
| 592 | */
|
---|
| 593 | getState() {
|
---|
| 594 | return this._platformLocation.getState();
|
---|
| 595 | }
|
---|
| 596 | /**
|
---|
| 597 | * Normalizes the given path and compares to the current normalized path.
|
---|
| 598 | *
|
---|
| 599 | * @param path The given URL path.
|
---|
| 600 | * @param query Query parameters.
|
---|
| 601 | *
|
---|
| 602 | * @returns True if the given URL path is equal to the current normalized path, false
|
---|
| 603 | * otherwise.
|
---|
| 604 | */
|
---|
| 605 | isCurrentPathEqualTo(path, query = '') {
|
---|
| 606 | return this.path() == this.normalize(path + normalizeQueryParams(query));
|
---|
| 607 | }
|
---|
| 608 | /**
|
---|
| 609 | * Normalizes a URL path by stripping any trailing slashes.
|
---|
| 610 | *
|
---|
| 611 | * @param url String representing a URL.
|
---|
| 612 | *
|
---|
| 613 | * @returns The normalized URL string.
|
---|
| 614 | */
|
---|
| 615 | normalize(url) {
|
---|
| 616 | return Location.stripTrailingSlash(_stripBaseHref(this._baseHref, _stripIndexHtml(url)));
|
---|
| 617 | }
|
---|
| 618 | /**
|
---|
| 619 | * Normalizes an external URL path.
|
---|
| 620 | * If the given URL doesn't begin with a leading slash (`'/'`), adds one
|
---|
| 621 | * before normalizing. Adds a hash if `HashLocationStrategy` is
|
---|
| 622 | * in use, or the `APP_BASE_HREF` if the `PathLocationStrategy` is in use.
|
---|
| 623 | *
|
---|
| 624 | * @param url String representing a URL.
|
---|
| 625 | *
|
---|
| 626 | * @returns A normalized platform-specific URL.
|
---|
| 627 | */
|
---|
| 628 | prepareExternalUrl(url) {
|
---|
| 629 | if (url && url[0] !== '/') {
|
---|
| 630 | url = '/' + url;
|
---|
| 631 | }
|
---|
| 632 | return this._platformStrategy.prepareExternalUrl(url);
|
---|
| 633 | }
|
---|
| 634 | // TODO: rename this method to pushState
|
---|
| 635 | /**
|
---|
| 636 | * Changes the browser's URL to a normalized version of a given URL, and pushes a
|
---|
| 637 | * new item onto the platform's history.
|
---|
| 638 | *
|
---|
| 639 | * @param path URL path to normalize.
|
---|
| 640 | * @param query Query parameters.
|
---|
| 641 | * @param state Location history state.
|
---|
| 642 | *
|
---|
| 643 | */
|
---|
| 644 | go(path, query = '', state = null) {
|
---|
| 645 | this._platformStrategy.pushState(state, '', path, query);
|
---|
| 646 | this._notifyUrlChangeListeners(this.prepareExternalUrl(path + normalizeQueryParams(query)), state);
|
---|
| 647 | }
|
---|
| 648 | /**
|
---|
| 649 | * Changes the browser's URL to a normalized version of the given URL, and replaces
|
---|
| 650 | * the top item on the platform's history stack.
|
---|
| 651 | *
|
---|
| 652 | * @param path URL path to normalize.
|
---|
| 653 | * @param query Query parameters.
|
---|
| 654 | * @param state Location history state.
|
---|
| 655 | */
|
---|
| 656 | replaceState(path, query = '', state = null) {
|
---|
| 657 | this._platformStrategy.replaceState(state, '', path, query);
|
---|
| 658 | this._notifyUrlChangeListeners(this.prepareExternalUrl(path + normalizeQueryParams(query)), state);
|
---|
| 659 | }
|
---|
| 660 | /**
|
---|
| 661 | * Navigates forward in the platform's history.
|
---|
| 662 | */
|
---|
| 663 | forward() {
|
---|
| 664 | this._platformStrategy.forward();
|
---|
| 665 | }
|
---|
| 666 | /**
|
---|
| 667 | * Navigates back in the platform's history.
|
---|
| 668 | */
|
---|
| 669 | back() {
|
---|
| 670 | this._platformStrategy.back();
|
---|
| 671 | }
|
---|
| 672 | /**
|
---|
| 673 | * Navigate to a specific page from session history, identified by its relative position to the
|
---|
| 674 | * current page.
|
---|
| 675 | *
|
---|
| 676 | * @param relativePosition Position of the target page in the history relative to the current
|
---|
| 677 | * page.
|
---|
| 678 | * A negative value moves backwards, a positive value moves forwards, e.g. `location.historyGo(2)`
|
---|
| 679 | * moves forward two pages and `location.historyGo(-2)` moves back two pages. When we try to go
|
---|
| 680 | * beyond what's stored in the history session, we stay in the current page. Same behaviour occurs
|
---|
| 681 | * when `relativePosition` equals 0.
|
---|
| 682 | * @see https://developer.mozilla.org/en-US/docs/Web/API/History_API#Moving_to_a_specific_point_in_history
|
---|
| 683 | */
|
---|
| 684 | historyGo(relativePosition = 0) {
|
---|
| 685 | var _a, _b;
|
---|
| 686 | (_b = (_a = this._platformStrategy).historyGo) === null || _b === void 0 ? void 0 : _b.call(_a, relativePosition);
|
---|
| 687 | }
|
---|
| 688 | /**
|
---|
| 689 | * Registers a URL change listener. Use to catch updates performed by the Angular
|
---|
| 690 | * framework that are not detectible through "popstate" or "hashchange" events.
|
---|
| 691 | *
|
---|
| 692 | * @param fn The change handler function, which take a URL and a location history state.
|
---|
| 693 | */
|
---|
| 694 | onUrlChange(fn) {
|
---|
| 695 | this._urlChangeListeners.push(fn);
|
---|
| 696 | if (!this._urlChangeSubscription) {
|
---|
| 697 | this._urlChangeSubscription = this.subscribe(v => {
|
---|
| 698 | this._notifyUrlChangeListeners(v.url, v.state);
|
---|
| 699 | });
|
---|
| 700 | }
|
---|
| 701 | }
|
---|
| 702 | /** @internal */
|
---|
| 703 | _notifyUrlChangeListeners(url = '', state) {
|
---|
| 704 | this._urlChangeListeners.forEach(fn => fn(url, state));
|
---|
| 705 | }
|
---|
| 706 | /**
|
---|
| 707 | * Subscribes to the platform's `popState` events.
|
---|
| 708 | *
|
---|
| 709 | * Note: `Location.go()` does not trigger the `popState` event in the browser. Use
|
---|
| 710 | * `Location.onUrlChange()` to subscribe to URL changes instead.
|
---|
| 711 | *
|
---|
| 712 | * @param value Event that is triggered when the state history changes.
|
---|
| 713 | * @param exception The exception to throw.
|
---|
| 714 | *
|
---|
| 715 | * @see [onpopstate](https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate)
|
---|
| 716 | *
|
---|
| 717 | * @returns Subscribed events.
|
---|
| 718 | */
|
---|
| 719 | subscribe(onNext, onThrow, onReturn) {
|
---|
| 720 | return this._subject.subscribe({ next: onNext, error: onThrow, complete: onReturn });
|
---|
| 721 | }
|
---|
| 722 | }
|
---|
| 723 | /**
|
---|
| 724 | * Normalizes URL parameters by prepending with `?` if needed.
|
---|
| 725 | *
|
---|
| 726 | * @param params String of URL parameters.
|
---|
| 727 | *
|
---|
| 728 | * @returns The normalized URL parameters string.
|
---|
| 729 | */
|
---|
| 730 | Location.normalizeQueryParams = normalizeQueryParams;
|
---|
| 731 | /**
|
---|
| 732 | * Joins two parts of a URL with a slash if needed.
|
---|
| 733 | *
|
---|
| 734 | * @param start URL string
|
---|
| 735 | * @param end URL string
|
---|
| 736 | *
|
---|
| 737 | *
|
---|
| 738 | * @returns The joined URL string.
|
---|
| 739 | */
|
---|
| 740 | Location.joinWithSlash = joinWithSlash;
|
---|
| 741 | /**
|
---|
| 742 | * Removes a trailing slash from a URL string if needed.
|
---|
| 743 | * Looks for the first occurrence of either `#`, `?`, or the end of the
|
---|
| 744 | * line as `/` characters and removes the trailing slash if one exists.
|
---|
| 745 | *
|
---|
| 746 | * @param url URL string.
|
---|
| 747 | *
|
---|
| 748 | * @returns The URL string, modified if needed.
|
---|
| 749 | */
|
---|
| 750 | Location.stripTrailingSlash = stripTrailingSlash;
|
---|
| 751 | Location.ɵprov = ɵɵdefineInjectable({ factory: createLocation, token: Location, providedIn: "root" });
|
---|
| 752 | Location.decorators = [
|
---|
| 753 | { type: Injectable, args: [{
|
---|
| 754 | providedIn: 'root',
|
---|
| 755 | // See #23917
|
---|
| 756 | useFactory: createLocation,
|
---|
| 757 | },] }
|
---|
| 758 | ];
|
---|
| 759 | Location.ctorParameters = () => [
|
---|
| 760 | { type: LocationStrategy },
|
---|
| 761 | { type: PlatformLocation }
|
---|
| 762 | ];
|
---|
| 763 | function createLocation() {
|
---|
| 764 | return new Location(ɵɵinject(LocationStrategy), ɵɵinject(PlatformLocation));
|
---|
| 765 | }
|
---|
| 766 | function _stripBaseHref(baseHref, url) {
|
---|
| 767 | return baseHref && url.startsWith(baseHref) ? url.substring(baseHref.length) : url;
|
---|
| 768 | }
|
---|
| 769 | function _stripIndexHtml(url) {
|
---|
| 770 | return url.replace(/\/index.html$/, '');
|
---|
| 771 | }
|
---|
| 772 |
|
---|
| 773 | /**
|
---|
| 774 | * @license
|
---|
| 775 | * Copyright Google LLC All Rights Reserved.
|
---|
| 776 | *
|
---|
| 777 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 778 | * found in the LICENSE file at https://angular.io/license
|
---|
| 779 | */
|
---|
| 780 |
|
---|
| 781 | /**
|
---|
| 782 | * @license
|
---|
| 783 | * Copyright Google LLC All Rights Reserved.
|
---|
| 784 | *
|
---|
| 785 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 786 | * found in the LICENSE file at https://angular.io/license
|
---|
| 787 | */
|
---|
| 788 | /** @internal */
|
---|
| 789 | const CURRENCIES_EN = { "ADP": [undefined, undefined, 0], "AFN": [undefined, undefined, 0], "ALL": [undefined, undefined, 0], "AMD": [undefined, undefined, 2], "AOA": [undefined, "Kz"], "ARS": [undefined, "$"], "AUD": ["A$", "$"], "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, "р.", 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, "₾"], "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": [undefined, "₱"], "PKR": [undefined, "Rs", 2], "PLN": [undefined, "zł"], "PYG": [undefined, "₲", 0], "RON": [undefined, "lei"], "RSD": [undefined, undefined, 0], "RUB": [undefined, "₽"], "RUR": [undefined, "р."], "RWF": [undefined, "RF", 0], "SBD": [undefined, "$"], "SEK": [undefined, "kr", 2], "SGD": [undefined, "$"], "SHP": [undefined, "£"], "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": ["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] };
|
---|
| 790 |
|
---|
| 791 | /**
|
---|
| 792 | * @license
|
---|
| 793 | * Copyright Google LLC All Rights Reserved.
|
---|
| 794 | *
|
---|
| 795 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 796 | * found in the LICENSE file at https://angular.io/license
|
---|
| 797 | */
|
---|
| 798 | /**
|
---|
| 799 | * Format styles that can be used to represent numbers.
|
---|
| 800 | * @see `getLocaleNumberFormat()`.
|
---|
| 801 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 802 | *
|
---|
| 803 | * @publicApi
|
---|
| 804 | */
|
---|
| 805 | var NumberFormatStyle;
|
---|
| 806 | (function (NumberFormatStyle) {
|
---|
| 807 | NumberFormatStyle[NumberFormatStyle["Decimal"] = 0] = "Decimal";
|
---|
| 808 | NumberFormatStyle[NumberFormatStyle["Percent"] = 1] = "Percent";
|
---|
| 809 | NumberFormatStyle[NumberFormatStyle["Currency"] = 2] = "Currency";
|
---|
| 810 | NumberFormatStyle[NumberFormatStyle["Scientific"] = 3] = "Scientific";
|
---|
| 811 | })(NumberFormatStyle || (NumberFormatStyle = {}));
|
---|
| 812 | /**
|
---|
| 813 | * Plurality cases used for translating plurals to different languages.
|
---|
| 814 | *
|
---|
| 815 | * @see `NgPlural`
|
---|
| 816 | * @see `NgPluralCase`
|
---|
| 817 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 818 | *
|
---|
| 819 | * @publicApi
|
---|
| 820 | */
|
---|
| 821 | var Plural;
|
---|
| 822 | (function (Plural) {
|
---|
| 823 | Plural[Plural["Zero"] = 0] = "Zero";
|
---|
| 824 | Plural[Plural["One"] = 1] = "One";
|
---|
| 825 | Plural[Plural["Two"] = 2] = "Two";
|
---|
| 826 | Plural[Plural["Few"] = 3] = "Few";
|
---|
| 827 | Plural[Plural["Many"] = 4] = "Many";
|
---|
| 828 | Plural[Plural["Other"] = 5] = "Other";
|
---|
| 829 | })(Plural || (Plural = {}));
|
---|
| 830 | /**
|
---|
| 831 | * Context-dependant translation forms for strings.
|
---|
| 832 | * Typically the standalone version is for the nominative form of the word,
|
---|
| 833 | * and the format version is used for the genitive case.
|
---|
| 834 | * @see [CLDR website](http://cldr.unicode.org/translation/date-time-1/date-time#TOC-Standalone-vs.-Format-Styles)
|
---|
| 835 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 836 | *
|
---|
| 837 | * @publicApi
|
---|
| 838 | */
|
---|
| 839 | var FormStyle;
|
---|
| 840 | (function (FormStyle) {
|
---|
| 841 | FormStyle[FormStyle["Format"] = 0] = "Format";
|
---|
| 842 | FormStyle[FormStyle["Standalone"] = 1] = "Standalone";
|
---|
| 843 | })(FormStyle || (FormStyle = {}));
|
---|
| 844 | /**
|
---|
| 845 | * String widths available for translations.
|
---|
| 846 | * The specific character widths are locale-specific.
|
---|
| 847 | * Examples are given for the word "Sunday" in English.
|
---|
| 848 | *
|
---|
| 849 | * @publicApi
|
---|
| 850 | */
|
---|
| 851 | var TranslationWidth;
|
---|
| 852 | (function (TranslationWidth) {
|
---|
| 853 | /** 1 character for `en-US`. For example: 'S' */
|
---|
| 854 | TranslationWidth[TranslationWidth["Narrow"] = 0] = "Narrow";
|
---|
| 855 | /** 3 characters for `en-US`. For example: 'Sun' */
|
---|
| 856 | TranslationWidth[TranslationWidth["Abbreviated"] = 1] = "Abbreviated";
|
---|
| 857 | /** Full length for `en-US`. For example: "Sunday" */
|
---|
| 858 | TranslationWidth[TranslationWidth["Wide"] = 2] = "Wide";
|
---|
| 859 | /** 2 characters for `en-US`, For example: "Su" */
|
---|
| 860 | TranslationWidth[TranslationWidth["Short"] = 3] = "Short";
|
---|
| 861 | })(TranslationWidth || (TranslationWidth = {}));
|
---|
| 862 | /**
|
---|
| 863 | * String widths available for date-time formats.
|
---|
| 864 | * The specific character widths are locale-specific.
|
---|
| 865 | * Examples are given for `en-US`.
|
---|
| 866 | *
|
---|
| 867 | * @see `getLocaleDateFormat()`
|
---|
| 868 | * @see `getLocaleTimeFormat()`
|
---|
| 869 | * @see `getLocaleDateTimeFormat()`
|
---|
| 870 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 871 | * @publicApi
|
---|
| 872 | */
|
---|
| 873 | var FormatWidth;
|
---|
| 874 | (function (FormatWidth) {
|
---|
| 875 | /**
|
---|
| 876 | * For `en-US`, 'M/d/yy, h:mm a'`
|
---|
| 877 | * (Example: `6/15/15, 9:03 AM`)
|
---|
| 878 | */
|
---|
| 879 | FormatWidth[FormatWidth["Short"] = 0] = "Short";
|
---|
| 880 | /**
|
---|
| 881 | * For `en-US`, `'MMM d, y, h:mm:ss a'`
|
---|
| 882 | * (Example: `Jun 15, 2015, 9:03:01 AM`)
|
---|
| 883 | */
|
---|
| 884 | FormatWidth[FormatWidth["Medium"] = 1] = "Medium";
|
---|
| 885 | /**
|
---|
| 886 | * For `en-US`, `'MMMM d, y, h:mm:ss a z'`
|
---|
| 887 | * (Example: `June 15, 2015 at 9:03:01 AM GMT+1`)
|
---|
| 888 | */
|
---|
| 889 | FormatWidth[FormatWidth["Long"] = 2] = "Long";
|
---|
| 890 | /**
|
---|
| 891 | * For `en-US`, `'EEEE, MMMM d, y, h:mm:ss a zzzz'`
|
---|
| 892 | * (Example: `Monday, June 15, 2015 at 9:03:01 AM GMT+01:00`)
|
---|
| 893 | */
|
---|
| 894 | FormatWidth[FormatWidth["Full"] = 3] = "Full";
|
---|
| 895 | })(FormatWidth || (FormatWidth = {}));
|
---|
| 896 | /**
|
---|
| 897 | * Symbols that can be used to replace placeholders in number patterns.
|
---|
| 898 | * Examples are based on `en-US` values.
|
---|
| 899 | *
|
---|
| 900 | * @see `getLocaleNumberSymbol()`
|
---|
| 901 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 902 | *
|
---|
| 903 | * @publicApi
|
---|
| 904 | */
|
---|
| 905 | var NumberSymbol;
|
---|
| 906 | (function (NumberSymbol) {
|
---|
| 907 | /**
|
---|
| 908 | * Decimal separator.
|
---|
| 909 | * For `en-US`, the dot character.
|
---|
| 910 | * Example: 2,345`.`67
|
---|
| 911 | */
|
---|
| 912 | NumberSymbol[NumberSymbol["Decimal"] = 0] = "Decimal";
|
---|
| 913 | /**
|
---|
| 914 | * Grouping separator, typically for thousands.
|
---|
| 915 | * For `en-US`, the comma character.
|
---|
| 916 | * Example: 2`,`345.67
|
---|
| 917 | */
|
---|
| 918 | NumberSymbol[NumberSymbol["Group"] = 1] = "Group";
|
---|
| 919 | /**
|
---|
| 920 | * List-item separator.
|
---|
| 921 | * Example: "one, two, and three"
|
---|
| 922 | */
|
---|
| 923 | NumberSymbol[NumberSymbol["List"] = 2] = "List";
|
---|
| 924 | /**
|
---|
| 925 | * Sign for percentage (out of 100).
|
---|
| 926 | * Example: 23.4%
|
---|
| 927 | */
|
---|
| 928 | NumberSymbol[NumberSymbol["PercentSign"] = 3] = "PercentSign";
|
---|
| 929 | /**
|
---|
| 930 | * Sign for positive numbers.
|
---|
| 931 | * Example: +23
|
---|
| 932 | */
|
---|
| 933 | NumberSymbol[NumberSymbol["PlusSign"] = 4] = "PlusSign";
|
---|
| 934 | /**
|
---|
| 935 | * Sign for negative numbers.
|
---|
| 936 | * Example: -23
|
---|
| 937 | */
|
---|
| 938 | NumberSymbol[NumberSymbol["MinusSign"] = 5] = "MinusSign";
|
---|
| 939 | /**
|
---|
| 940 | * Computer notation for exponential value (n times a power of 10).
|
---|
| 941 | * Example: 1.2E3
|
---|
| 942 | */
|
---|
| 943 | NumberSymbol[NumberSymbol["Exponential"] = 6] = "Exponential";
|
---|
| 944 | /**
|
---|
| 945 | * Human-readable format of exponential.
|
---|
| 946 | * Example: 1.2x103
|
---|
| 947 | */
|
---|
| 948 | NumberSymbol[NumberSymbol["SuperscriptingExponent"] = 7] = "SuperscriptingExponent";
|
---|
| 949 | /**
|
---|
| 950 | * Sign for permille (out of 1000).
|
---|
| 951 | * Example: 23.4‰
|
---|
| 952 | */
|
---|
| 953 | NumberSymbol[NumberSymbol["PerMille"] = 8] = "PerMille";
|
---|
| 954 | /**
|
---|
| 955 | * Infinity, can be used with plus and minus.
|
---|
| 956 | * Example: ∞, +∞, -∞
|
---|
| 957 | */
|
---|
| 958 | NumberSymbol[NumberSymbol["Infinity"] = 9] = "Infinity";
|
---|
| 959 | /**
|
---|
| 960 | * Not a number.
|
---|
| 961 | * Example: NaN
|
---|
| 962 | */
|
---|
| 963 | NumberSymbol[NumberSymbol["NaN"] = 10] = "NaN";
|
---|
| 964 | /**
|
---|
| 965 | * Symbol used between time units.
|
---|
| 966 | * Example: 10:52
|
---|
| 967 | */
|
---|
| 968 | NumberSymbol[NumberSymbol["TimeSeparator"] = 11] = "TimeSeparator";
|
---|
| 969 | /**
|
---|
| 970 | * Decimal separator for currency values (fallback to `Decimal`).
|
---|
| 971 | * Example: $2,345.67
|
---|
| 972 | */
|
---|
| 973 | NumberSymbol[NumberSymbol["CurrencyDecimal"] = 12] = "CurrencyDecimal";
|
---|
| 974 | /**
|
---|
| 975 | * Group separator for currency values (fallback to `Group`).
|
---|
| 976 | * Example: $2,345.67
|
---|
| 977 | */
|
---|
| 978 | NumberSymbol[NumberSymbol["CurrencyGroup"] = 13] = "CurrencyGroup";
|
---|
| 979 | })(NumberSymbol || (NumberSymbol = {}));
|
---|
| 980 | /**
|
---|
| 981 | * The value for each day of the week, based on the `en-US` locale
|
---|
| 982 | *
|
---|
| 983 | * @publicApi
|
---|
| 984 | */
|
---|
| 985 | var WeekDay;
|
---|
| 986 | (function (WeekDay) {
|
---|
| 987 | WeekDay[WeekDay["Sunday"] = 0] = "Sunday";
|
---|
| 988 | WeekDay[WeekDay["Monday"] = 1] = "Monday";
|
---|
| 989 | WeekDay[WeekDay["Tuesday"] = 2] = "Tuesday";
|
---|
| 990 | WeekDay[WeekDay["Wednesday"] = 3] = "Wednesday";
|
---|
| 991 | WeekDay[WeekDay["Thursday"] = 4] = "Thursday";
|
---|
| 992 | WeekDay[WeekDay["Friday"] = 5] = "Friday";
|
---|
| 993 | WeekDay[WeekDay["Saturday"] = 6] = "Saturday";
|
---|
| 994 | })(WeekDay || (WeekDay = {}));
|
---|
| 995 | /**
|
---|
| 996 | * Retrieves the locale ID from the currently loaded locale.
|
---|
| 997 | * The loaded locale could be, for example, a global one rather than a regional one.
|
---|
| 998 | * @param locale A locale code, such as `fr-FR`.
|
---|
| 999 | * @returns The locale code. For example, `fr`.
|
---|
| 1000 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1001 | *
|
---|
| 1002 | * @publicApi
|
---|
| 1003 | */
|
---|
| 1004 | function getLocaleId(locale) {
|
---|
| 1005 | return ɵfindLocaleData(locale)[ɵLocaleDataIndex.LocaleId];
|
---|
| 1006 | }
|
---|
| 1007 | /**
|
---|
| 1008 | * Retrieves day period strings for the given locale.
|
---|
| 1009 | *
|
---|
| 1010 | * @param locale A locale code for the locale format rules to use.
|
---|
| 1011 | * @param formStyle The required grammatical form.
|
---|
| 1012 | * @param width The required character width.
|
---|
| 1013 | * @returns An array of localized period strings. For example, `[AM, PM]` for `en-US`.
|
---|
| 1014 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1015 | *
|
---|
| 1016 | * @publicApi
|
---|
| 1017 | */
|
---|
| 1018 | function getLocaleDayPeriods(locale, formStyle, width) {
|
---|
| 1019 | const data = ɵfindLocaleData(locale);
|
---|
| 1020 | const amPmData = [
|
---|
| 1021 | data[ɵLocaleDataIndex.DayPeriodsFormat], data[ɵLocaleDataIndex.DayPeriodsStandalone]
|
---|
| 1022 | ];
|
---|
| 1023 | const amPm = getLastDefinedValue(amPmData, formStyle);
|
---|
| 1024 | return getLastDefinedValue(amPm, width);
|
---|
| 1025 | }
|
---|
| 1026 | /**
|
---|
| 1027 | * Retrieves days of the week for the given locale, using the Gregorian calendar.
|
---|
| 1028 | *
|
---|
| 1029 | * @param locale A locale code for the locale format rules to use.
|
---|
| 1030 | * @param formStyle The required grammatical form.
|
---|
| 1031 | * @param width The required character width.
|
---|
| 1032 | * @returns An array of localized name strings.
|
---|
| 1033 | * For example,`[Sunday, Monday, ... Saturday]` for `en-US`.
|
---|
| 1034 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1035 | *
|
---|
| 1036 | * @publicApi
|
---|
| 1037 | */
|
---|
| 1038 | function getLocaleDayNames(locale, formStyle, width) {
|
---|
| 1039 | const data = ɵfindLocaleData(locale);
|
---|
| 1040 | const daysData = [data[ɵLocaleDataIndex.DaysFormat], data[ɵLocaleDataIndex.DaysStandalone]];
|
---|
| 1041 | const days = getLastDefinedValue(daysData, formStyle);
|
---|
| 1042 | return getLastDefinedValue(days, width);
|
---|
| 1043 | }
|
---|
| 1044 | /**
|
---|
| 1045 | * Retrieves months of the year for the given locale, using the Gregorian calendar.
|
---|
| 1046 | *
|
---|
| 1047 | * @param locale A locale code for the locale format rules to use.
|
---|
| 1048 | * @param formStyle The required grammatical form.
|
---|
| 1049 | * @param width The required character width.
|
---|
| 1050 | * @returns An array of localized name strings.
|
---|
| 1051 | * For example, `[January, February, ...]` for `en-US`.
|
---|
| 1052 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1053 | *
|
---|
| 1054 | * @publicApi
|
---|
| 1055 | */
|
---|
| 1056 | function getLocaleMonthNames(locale, formStyle, width) {
|
---|
| 1057 | const data = ɵfindLocaleData(locale);
|
---|
| 1058 | const monthsData = [data[ɵLocaleDataIndex.MonthsFormat], data[ɵLocaleDataIndex.MonthsStandalone]];
|
---|
| 1059 | const months = getLastDefinedValue(monthsData, formStyle);
|
---|
| 1060 | return getLastDefinedValue(months, width);
|
---|
| 1061 | }
|
---|
| 1062 | /**
|
---|
| 1063 | * Retrieves Gregorian-calendar eras for the given locale.
|
---|
| 1064 | * @param locale A locale code for the locale format rules to use.
|
---|
| 1065 | * @param width The required character width.
|
---|
| 1066 |
|
---|
| 1067 | * @returns An array of localized era strings.
|
---|
| 1068 | * For example, `[AD, BC]` for `en-US`.
|
---|
| 1069 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1070 | *
|
---|
| 1071 | * @publicApi
|
---|
| 1072 | */
|
---|
| 1073 | function getLocaleEraNames(locale, width) {
|
---|
| 1074 | const data = ɵfindLocaleData(locale);
|
---|
| 1075 | const erasData = data[ɵLocaleDataIndex.Eras];
|
---|
| 1076 | return getLastDefinedValue(erasData, width);
|
---|
| 1077 | }
|
---|
| 1078 | /**
|
---|
| 1079 | * Retrieves the first day of the week for the given locale.
|
---|
| 1080 | *
|
---|
| 1081 | * @param locale A locale code for the locale format rules to use.
|
---|
| 1082 | * @returns A day index number, using the 0-based week-day index for `en-US`
|
---|
| 1083 | * (Sunday = 0, Monday = 1, ...).
|
---|
| 1084 | * For example, for `fr-FR`, returns 1 to indicate that the first day is Monday.
|
---|
| 1085 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1086 | *
|
---|
| 1087 | * @publicApi
|
---|
| 1088 | */
|
---|
| 1089 | function getLocaleFirstDayOfWeek(locale) {
|
---|
| 1090 | const data = ɵfindLocaleData(locale);
|
---|
| 1091 | return data[ɵLocaleDataIndex.FirstDayOfWeek];
|
---|
| 1092 | }
|
---|
| 1093 | /**
|
---|
| 1094 | * Range of week days that are considered the week-end for the given locale.
|
---|
| 1095 | *
|
---|
| 1096 | * @param locale A locale code for the locale format rules to use.
|
---|
| 1097 | * @returns The range of day values, `[startDay, endDay]`.
|
---|
| 1098 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1099 | *
|
---|
| 1100 | * @publicApi
|
---|
| 1101 | */
|
---|
| 1102 | function getLocaleWeekEndRange(locale) {
|
---|
| 1103 | const data = ɵfindLocaleData(locale);
|
---|
| 1104 | return data[ɵLocaleDataIndex.WeekendRange];
|
---|
| 1105 | }
|
---|
| 1106 | /**
|
---|
| 1107 | * Retrieves a localized date-value formating string.
|
---|
| 1108 | *
|
---|
| 1109 | * @param locale A locale code for the locale format rules to use.
|
---|
| 1110 | * @param width The format type.
|
---|
| 1111 | * @returns The localized formating string.
|
---|
| 1112 | * @see `FormatWidth`
|
---|
| 1113 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1114 | *
|
---|
| 1115 | * @publicApi
|
---|
| 1116 | */
|
---|
| 1117 | function getLocaleDateFormat(locale, width) {
|
---|
| 1118 | const data = ɵfindLocaleData(locale);
|
---|
| 1119 | return getLastDefinedValue(data[ɵLocaleDataIndex.DateFormat], width);
|
---|
| 1120 | }
|
---|
| 1121 | /**
|
---|
| 1122 | * Retrieves a localized time-value formatting string.
|
---|
| 1123 | *
|
---|
| 1124 | * @param locale A locale code for the locale format rules to use.
|
---|
| 1125 | * @param width The format type.
|
---|
| 1126 | * @returns The localized formatting string.
|
---|
| 1127 | * @see `FormatWidth`
|
---|
| 1128 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1129 |
|
---|
| 1130 | * @publicApi
|
---|
| 1131 | */
|
---|
| 1132 | function getLocaleTimeFormat(locale, width) {
|
---|
| 1133 | const data = ɵfindLocaleData(locale);
|
---|
| 1134 | return getLastDefinedValue(data[ɵLocaleDataIndex.TimeFormat], width);
|
---|
| 1135 | }
|
---|
| 1136 | /**
|
---|
| 1137 | * Retrieves a localized date-time formatting string.
|
---|
| 1138 | *
|
---|
| 1139 | * @param locale A locale code for the locale format rules to use.
|
---|
| 1140 | * @param width The format type.
|
---|
| 1141 | * @returns The localized formatting string.
|
---|
| 1142 | * @see `FormatWidth`
|
---|
| 1143 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1144 | *
|
---|
| 1145 | * @publicApi
|
---|
| 1146 | */
|
---|
| 1147 | function getLocaleDateTimeFormat(locale, width) {
|
---|
| 1148 | const data = ɵfindLocaleData(locale);
|
---|
| 1149 | const dateTimeFormatData = data[ɵLocaleDataIndex.DateTimeFormat];
|
---|
| 1150 | return getLastDefinedValue(dateTimeFormatData, width);
|
---|
| 1151 | }
|
---|
| 1152 | /**
|
---|
| 1153 | * Retrieves a localized number symbol that can be used to replace placeholders in number formats.
|
---|
| 1154 | * @param locale The locale code.
|
---|
| 1155 | * @param symbol The symbol to localize.
|
---|
| 1156 | * @returns The character for the localized symbol.
|
---|
| 1157 | * @see `NumberSymbol`
|
---|
| 1158 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1159 | *
|
---|
| 1160 | * @publicApi
|
---|
| 1161 | */
|
---|
| 1162 | function getLocaleNumberSymbol(locale, symbol) {
|
---|
| 1163 | const data = ɵfindLocaleData(locale);
|
---|
| 1164 | const res = data[ɵLocaleDataIndex.NumberSymbols][symbol];
|
---|
| 1165 | if (typeof res === 'undefined') {
|
---|
| 1166 | if (symbol === NumberSymbol.CurrencyDecimal) {
|
---|
| 1167 | return data[ɵLocaleDataIndex.NumberSymbols][NumberSymbol.Decimal];
|
---|
| 1168 | }
|
---|
| 1169 | else if (symbol === NumberSymbol.CurrencyGroup) {
|
---|
| 1170 | return data[ɵLocaleDataIndex.NumberSymbols][NumberSymbol.Group];
|
---|
| 1171 | }
|
---|
| 1172 | }
|
---|
| 1173 | return res;
|
---|
| 1174 | }
|
---|
| 1175 | /**
|
---|
| 1176 | * Retrieves a number format for a given locale.
|
---|
| 1177 | *
|
---|
| 1178 | * Numbers are formatted using patterns, like `#,###.00`. For example, the pattern `#,###.00`
|
---|
| 1179 | * when used to format the number 12345.678 could result in "12'345,678". That would happen if the
|
---|
| 1180 | * grouping separator for your language is an apostrophe, and the decimal separator is a comma.
|
---|
| 1181 | *
|
---|
| 1182 | * <b>Important:</b> The characters `.` `,` `0` `#` (and others below) are special placeholders
|
---|
| 1183 | * that stand for the decimal separator, and so on, and are NOT real characters.
|
---|
| 1184 | * You must NOT "translate" the placeholders. For example, don't change `.` to `,` even though in
|
---|
| 1185 | * your language the decimal point is written with a comma. The symbols should be replaced by the
|
---|
| 1186 | * local equivalents, using the appropriate `NumberSymbol` for your language.
|
---|
| 1187 | *
|
---|
| 1188 | * Here are the special characters used in number patterns:
|
---|
| 1189 | *
|
---|
| 1190 | * | Symbol | Meaning |
|
---|
| 1191 | * |--------|---------|
|
---|
| 1192 | * | . | Replaced automatically by the character used for the decimal point. |
|
---|
| 1193 | * | , | Replaced by the "grouping" (thousands) separator. |
|
---|
| 1194 | * | 0 | Replaced by a digit (or zero if there aren't enough digits). |
|
---|
| 1195 | * | # | Replaced by a digit (or nothing if there aren't enough). |
|
---|
| 1196 | * | ¤ | Replaced by a currency symbol, such as $ or USD. |
|
---|
| 1197 | * | % | Marks a percent format. The % symbol may change position, but must be retained. |
|
---|
| 1198 | * | E | Marks a scientific format. The E symbol may change position, but must be retained. |
|
---|
| 1199 | * | ' | Special characters used as literal characters are quoted with ASCII single quotes. |
|
---|
| 1200 | *
|
---|
| 1201 | * @param locale A locale code for the locale format rules to use.
|
---|
| 1202 | * @param type The type of numeric value to be formatted (such as `Decimal` or `Currency`.)
|
---|
| 1203 | * @returns The localized format string.
|
---|
| 1204 | * @see `NumberFormatStyle`
|
---|
| 1205 | * @see [CLDR website](http://cldr.unicode.org/translation/number-patterns)
|
---|
| 1206 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1207 | *
|
---|
| 1208 | * @publicApi
|
---|
| 1209 | */
|
---|
| 1210 | function getLocaleNumberFormat(locale, type) {
|
---|
| 1211 | const data = ɵfindLocaleData(locale);
|
---|
| 1212 | return data[ɵLocaleDataIndex.NumberFormats][type];
|
---|
| 1213 | }
|
---|
| 1214 | /**
|
---|
| 1215 | * Retrieves the symbol used to represent the currency for the main country
|
---|
| 1216 | * corresponding to a given locale. For example, '$' for `en-US`.
|
---|
| 1217 | *
|
---|
| 1218 | * @param locale A locale code for the locale format rules to use.
|
---|
| 1219 | * @returns The localized symbol character,
|
---|
| 1220 | * or `null` if the main country cannot be determined.
|
---|
| 1221 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1222 | *
|
---|
| 1223 | * @publicApi
|
---|
| 1224 | */
|
---|
| 1225 | function getLocaleCurrencySymbol(locale) {
|
---|
| 1226 | const data = ɵfindLocaleData(locale);
|
---|
| 1227 | return data[ɵLocaleDataIndex.CurrencySymbol] || null;
|
---|
| 1228 | }
|
---|
| 1229 | /**
|
---|
| 1230 | * Retrieves the name of the currency for the main country corresponding
|
---|
| 1231 | * to a given locale. For example, 'US Dollar' for `en-US`.
|
---|
| 1232 | * @param locale A locale code for the locale format rules to use.
|
---|
| 1233 | * @returns The currency name,
|
---|
| 1234 | * or `null` if the main country cannot be determined.
|
---|
| 1235 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1236 | *
|
---|
| 1237 | * @publicApi
|
---|
| 1238 | */
|
---|
| 1239 | function getLocaleCurrencyName(locale) {
|
---|
| 1240 | const data = ɵfindLocaleData(locale);
|
---|
| 1241 | return data[ɵLocaleDataIndex.CurrencyName] || null;
|
---|
| 1242 | }
|
---|
| 1243 | /**
|
---|
| 1244 | * Retrieves the default currency code for the given locale.
|
---|
| 1245 | *
|
---|
| 1246 | * The default is defined as the first currency which is still in use.
|
---|
| 1247 | *
|
---|
| 1248 | * @param locale The code of the locale whose currency code we want.
|
---|
| 1249 | * @returns The code of the default currency for the given locale.
|
---|
| 1250 | *
|
---|
| 1251 | * @publicApi
|
---|
| 1252 | */
|
---|
| 1253 | function getLocaleCurrencyCode(locale) {
|
---|
| 1254 | return ɵgetLocaleCurrencyCode(locale);
|
---|
| 1255 | }
|
---|
| 1256 | /**
|
---|
| 1257 | * Retrieves the currency values for a given locale.
|
---|
| 1258 | * @param locale A locale code for the locale format rules to use.
|
---|
| 1259 | * @returns The currency values.
|
---|
| 1260 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1261 | */
|
---|
| 1262 | function getLocaleCurrencies(locale) {
|
---|
| 1263 | const data = ɵfindLocaleData(locale);
|
---|
| 1264 | return data[ɵLocaleDataIndex.Currencies];
|
---|
| 1265 | }
|
---|
| 1266 | /**
|
---|
| 1267 | * @alias core/ɵgetLocalePluralCase
|
---|
| 1268 | * @publicApi
|
---|
| 1269 | */
|
---|
| 1270 | const getLocalePluralCase = ɵgetLocalePluralCase;
|
---|
| 1271 | function checkFullData(data) {
|
---|
| 1272 | if (!data[ɵLocaleDataIndex.ExtraData]) {
|
---|
| 1273 | throw new Error(`Missing extra locale data for the locale "${data[ɵLocaleDataIndex
|
---|
| 1274 | .LocaleId]}". Use "registerLocaleData" to load new data. See the "I18n guide" on angular.io to know more.`);
|
---|
| 1275 | }
|
---|
| 1276 | }
|
---|
| 1277 | /**
|
---|
| 1278 | * Retrieves locale-specific rules used to determine which day period to use
|
---|
| 1279 | * when more than one period is defined for a locale.
|
---|
| 1280 | *
|
---|
| 1281 | * There is a rule for each defined day period. The
|
---|
| 1282 | * first rule is applied to the first day period and so on.
|
---|
| 1283 | * Fall back to AM/PM when no rules are available.
|
---|
| 1284 | *
|
---|
| 1285 | * A rule can specify a period as time range, or as a single time value.
|
---|
| 1286 | *
|
---|
| 1287 | * This functionality is only available when you have loaded the full locale data.
|
---|
| 1288 | * See the ["I18n guide"](guide/i18n-common-format-data-locale).
|
---|
| 1289 | *
|
---|
| 1290 | * @param locale A locale code for the locale format rules to use.
|
---|
| 1291 | * @returns The rules for the locale, a single time value or array of *from-time, to-time*,
|
---|
| 1292 | * or null if no periods are available.
|
---|
| 1293 | *
|
---|
| 1294 | * @see `getLocaleExtraDayPeriods()`
|
---|
| 1295 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1296 | *
|
---|
| 1297 | * @publicApi
|
---|
| 1298 | */
|
---|
| 1299 | function getLocaleExtraDayPeriodRules(locale) {
|
---|
| 1300 | const data = ɵfindLocaleData(locale);
|
---|
| 1301 | checkFullData(data);
|
---|
| 1302 | const rules = data[ɵLocaleDataIndex.ExtraData][2 /* ExtraDayPeriodsRules */] || [];
|
---|
| 1303 | return rules.map((rule) => {
|
---|
| 1304 | if (typeof rule === 'string') {
|
---|
| 1305 | return extractTime(rule);
|
---|
| 1306 | }
|
---|
| 1307 | return [extractTime(rule[0]), extractTime(rule[1])];
|
---|
| 1308 | });
|
---|
| 1309 | }
|
---|
| 1310 | /**
|
---|
| 1311 | * Retrieves locale-specific day periods, which indicate roughly how a day is broken up
|
---|
| 1312 | * in different languages.
|
---|
| 1313 | * For example, for `en-US`, periods are morning, noon, afternoon, evening, and midnight.
|
---|
| 1314 | *
|
---|
| 1315 | * This functionality is only available when you have loaded the full locale data.
|
---|
| 1316 | * See the ["I18n guide"](guide/i18n-common-format-data-locale).
|
---|
| 1317 | *
|
---|
| 1318 | * @param locale A locale code for the locale format rules to use.
|
---|
| 1319 | * @param formStyle The required grammatical form.
|
---|
| 1320 | * @param width The required character width.
|
---|
| 1321 | * @returns The translated day-period strings.
|
---|
| 1322 | * @see `getLocaleExtraDayPeriodRules()`
|
---|
| 1323 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1324 | *
|
---|
| 1325 | * @publicApi
|
---|
| 1326 | */
|
---|
| 1327 | function getLocaleExtraDayPeriods(locale, formStyle, width) {
|
---|
| 1328 | const data = ɵfindLocaleData(locale);
|
---|
| 1329 | checkFullData(data);
|
---|
| 1330 | const dayPeriodsData = [
|
---|
| 1331 | data[ɵLocaleDataIndex.ExtraData][0 /* ExtraDayPeriodFormats */],
|
---|
| 1332 | data[ɵLocaleDataIndex.ExtraData][1 /* ExtraDayPeriodStandalone */]
|
---|
| 1333 | ];
|
---|
| 1334 | const dayPeriods = getLastDefinedValue(dayPeriodsData, formStyle) || [];
|
---|
| 1335 | return getLastDefinedValue(dayPeriods, width) || [];
|
---|
| 1336 | }
|
---|
| 1337 | /**
|
---|
| 1338 | * Retrieves the writing direction of a specified locale
|
---|
| 1339 | * @param locale A locale code for the locale format rules to use.
|
---|
| 1340 | * @publicApi
|
---|
| 1341 | * @returns 'rtl' or 'ltr'
|
---|
| 1342 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1343 | */
|
---|
| 1344 | function getLocaleDirection(locale) {
|
---|
| 1345 | const data = ɵfindLocaleData(locale);
|
---|
| 1346 | return data[ɵLocaleDataIndex.Directionality];
|
---|
| 1347 | }
|
---|
| 1348 | /**
|
---|
| 1349 | * Retrieves the first value that is defined in an array, going backwards from an index position.
|
---|
| 1350 | *
|
---|
| 1351 | * To avoid repeating the same data (as when the "format" and "standalone" forms are the same)
|
---|
| 1352 | * add the first value to the locale data arrays, and add other values only if they are different.
|
---|
| 1353 | *
|
---|
| 1354 | * @param data The data array to retrieve from.
|
---|
| 1355 | * @param index A 0-based index into the array to start from.
|
---|
| 1356 | * @returns The value immediately before the given index position.
|
---|
| 1357 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1358 | *
|
---|
| 1359 | * @publicApi
|
---|
| 1360 | */
|
---|
| 1361 | function getLastDefinedValue(data, index) {
|
---|
| 1362 | for (let i = index; i > -1; i--) {
|
---|
| 1363 | if (typeof data[i] !== 'undefined') {
|
---|
| 1364 | return data[i];
|
---|
| 1365 | }
|
---|
| 1366 | }
|
---|
| 1367 | throw new Error('Locale data API: locale data undefined');
|
---|
| 1368 | }
|
---|
| 1369 | /**
|
---|
| 1370 | * Extracts the hours and minutes from a string like "15:45"
|
---|
| 1371 | */
|
---|
| 1372 | function extractTime(time) {
|
---|
| 1373 | const [h, m] = time.split(':');
|
---|
| 1374 | return { hours: +h, minutes: +m };
|
---|
| 1375 | }
|
---|
| 1376 | /**
|
---|
| 1377 | * Retrieves the currency symbol for a given currency code.
|
---|
| 1378 | *
|
---|
| 1379 | * For example, for the default `en-US` locale, the code `USD` can
|
---|
| 1380 | * be represented by the narrow symbol `$` or the wide symbol `US$`.
|
---|
| 1381 | *
|
---|
| 1382 | * @param code The currency code.
|
---|
| 1383 | * @param format The format, `wide` or `narrow`.
|
---|
| 1384 | * @param locale A locale code for the locale format rules to use.
|
---|
| 1385 | *
|
---|
| 1386 | * @returns The symbol, or the currency code if no symbol is available.
|
---|
| 1387 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1388 | *
|
---|
| 1389 | * @publicApi
|
---|
| 1390 | */
|
---|
| 1391 | function getCurrencySymbol(code, format, locale = 'en') {
|
---|
| 1392 | const currency = getLocaleCurrencies(locale)[code] || CURRENCIES_EN[code] || [];
|
---|
| 1393 | const symbolNarrow = currency[1 /* SymbolNarrow */];
|
---|
| 1394 | if (format === 'narrow' && typeof symbolNarrow === 'string') {
|
---|
| 1395 | return symbolNarrow;
|
---|
| 1396 | }
|
---|
| 1397 | return currency[0 /* Symbol */] || code;
|
---|
| 1398 | }
|
---|
| 1399 | // Most currencies have cents, that's why the default is 2
|
---|
| 1400 | const DEFAULT_NB_OF_CURRENCY_DIGITS = 2;
|
---|
| 1401 | /**
|
---|
| 1402 | * Reports the number of decimal digits for a given currency.
|
---|
| 1403 | * The value depends upon the presence of cents in that particular currency.
|
---|
| 1404 | *
|
---|
| 1405 | * @param code The currency code.
|
---|
| 1406 | * @returns The number of decimal digits, typically 0 or 2.
|
---|
| 1407 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1408 | *
|
---|
| 1409 | * @publicApi
|
---|
| 1410 | */
|
---|
| 1411 | function getNumberOfCurrencyDigits(code) {
|
---|
| 1412 | let digits;
|
---|
| 1413 | const currency = CURRENCIES_EN[code];
|
---|
| 1414 | if (currency) {
|
---|
| 1415 | digits = currency[2 /* NbOfDigits */];
|
---|
| 1416 | }
|
---|
| 1417 | return typeof digits === 'number' ? digits : DEFAULT_NB_OF_CURRENCY_DIGITS;
|
---|
| 1418 | }
|
---|
| 1419 |
|
---|
| 1420 | /**
|
---|
| 1421 | * @license
|
---|
| 1422 | * Copyright Google LLC All Rights Reserved.
|
---|
| 1423 | *
|
---|
| 1424 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 1425 | * found in the LICENSE file at https://angular.io/license
|
---|
| 1426 | */
|
---|
| 1427 | const ISO8601_DATE_REGEX = /^(\d{4})-?(\d\d)-?(\d\d)(?:T(\d\d)(?::?(\d\d)(?::?(\d\d)(?:\.(\d+))?)?)?(Z|([+-])(\d\d):?(\d\d))?)?$/;
|
---|
| 1428 | // 1 2 3 4 5 6 7 8 9 10 11
|
---|
| 1429 | const NAMED_FORMATS = {};
|
---|
| 1430 | const DATE_FORMATS_SPLIT = /((?:[^BEGHLMOSWYZabcdhmswyz']+)|(?:'(?:[^']|'')*')|(?:G{1,5}|y{1,4}|Y{1,4}|M{1,5}|L{1,5}|w{1,2}|W{1}|d{1,2}|E{1,6}|c{1,6}|a{1,5}|b{1,5}|B{1,5}|h{1,2}|H{1,2}|m{1,2}|s{1,2}|S{1,3}|z{1,4}|Z{1,5}|O{1,4}))([\s\S]*)/;
|
---|
| 1431 | var ZoneWidth;
|
---|
| 1432 | (function (ZoneWidth) {
|
---|
| 1433 | ZoneWidth[ZoneWidth["Short"] = 0] = "Short";
|
---|
| 1434 | ZoneWidth[ZoneWidth["ShortGMT"] = 1] = "ShortGMT";
|
---|
| 1435 | ZoneWidth[ZoneWidth["Long"] = 2] = "Long";
|
---|
| 1436 | ZoneWidth[ZoneWidth["Extended"] = 3] = "Extended";
|
---|
| 1437 | })(ZoneWidth || (ZoneWidth = {}));
|
---|
| 1438 | var DateType;
|
---|
| 1439 | (function (DateType) {
|
---|
| 1440 | DateType[DateType["FullYear"] = 0] = "FullYear";
|
---|
| 1441 | DateType[DateType["Month"] = 1] = "Month";
|
---|
| 1442 | DateType[DateType["Date"] = 2] = "Date";
|
---|
| 1443 | DateType[DateType["Hours"] = 3] = "Hours";
|
---|
| 1444 | DateType[DateType["Minutes"] = 4] = "Minutes";
|
---|
| 1445 | DateType[DateType["Seconds"] = 5] = "Seconds";
|
---|
| 1446 | DateType[DateType["FractionalSeconds"] = 6] = "FractionalSeconds";
|
---|
| 1447 | DateType[DateType["Day"] = 7] = "Day";
|
---|
| 1448 | })(DateType || (DateType = {}));
|
---|
| 1449 | var TranslationType;
|
---|
| 1450 | (function (TranslationType) {
|
---|
| 1451 | TranslationType[TranslationType["DayPeriods"] = 0] = "DayPeriods";
|
---|
| 1452 | TranslationType[TranslationType["Days"] = 1] = "Days";
|
---|
| 1453 | TranslationType[TranslationType["Months"] = 2] = "Months";
|
---|
| 1454 | TranslationType[TranslationType["Eras"] = 3] = "Eras";
|
---|
| 1455 | })(TranslationType || (TranslationType = {}));
|
---|
| 1456 | /**
|
---|
| 1457 | * @ngModule CommonModule
|
---|
| 1458 | * @description
|
---|
| 1459 | *
|
---|
| 1460 | * Formats a date according to locale rules.
|
---|
| 1461 | *
|
---|
| 1462 | * @param value The date to format, as a Date, or a number (milliseconds since UTC epoch)
|
---|
| 1463 | * or an [ISO date-time string](https://www.w3.org/TR/NOTE-datetime).
|
---|
| 1464 | * @param format The date-time components to include. See `DatePipe` for details.
|
---|
| 1465 | * @param locale A locale code for the locale format rules to use.
|
---|
| 1466 | * @param timezone The time zone. A time zone offset from GMT (such as `'+0430'`),
|
---|
| 1467 | * or a standard UTC/GMT or continental US time zone abbreviation.
|
---|
| 1468 | * If not specified, uses host system settings.
|
---|
| 1469 | *
|
---|
| 1470 | * @returns The formatted date string.
|
---|
| 1471 | *
|
---|
| 1472 | * @see `DatePipe`
|
---|
| 1473 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 1474 | *
|
---|
| 1475 | * @publicApi
|
---|
| 1476 | */
|
---|
| 1477 | function formatDate(value, format, locale, timezone) {
|
---|
| 1478 | let date = toDate(value);
|
---|
| 1479 | const namedFormat = getNamedFormat(locale, format);
|
---|
| 1480 | format = namedFormat || format;
|
---|
| 1481 | let parts = [];
|
---|
| 1482 | let match;
|
---|
| 1483 | while (format) {
|
---|
| 1484 | match = DATE_FORMATS_SPLIT.exec(format);
|
---|
| 1485 | if (match) {
|
---|
| 1486 | parts = parts.concat(match.slice(1));
|
---|
| 1487 | const part = parts.pop();
|
---|
| 1488 | if (!part) {
|
---|
| 1489 | break;
|
---|
| 1490 | }
|
---|
| 1491 | format = part;
|
---|
| 1492 | }
|
---|
| 1493 | else {
|
---|
| 1494 | parts.push(format);
|
---|
| 1495 | break;
|
---|
| 1496 | }
|
---|
| 1497 | }
|
---|
| 1498 | let dateTimezoneOffset = date.getTimezoneOffset();
|
---|
| 1499 | if (timezone) {
|
---|
| 1500 | dateTimezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
|
---|
| 1501 | date = convertTimezoneToLocal(date, timezone, true);
|
---|
| 1502 | }
|
---|
| 1503 | let text = '';
|
---|
| 1504 | parts.forEach(value => {
|
---|
| 1505 | const dateFormatter = getDateFormatter(value);
|
---|
| 1506 | text += dateFormatter ?
|
---|
| 1507 | dateFormatter(date, locale, dateTimezoneOffset) :
|
---|
| 1508 | value === '\'\'' ? '\'' : value.replace(/(^'|'$)/g, '').replace(/''/g, '\'');
|
---|
| 1509 | });
|
---|
| 1510 | return text;
|
---|
| 1511 | }
|
---|
| 1512 | /**
|
---|
| 1513 | * Create a new Date object with the given date value, and the time set to midnight.
|
---|
| 1514 | *
|
---|
| 1515 | * We cannot use `new Date(year, month, date)` because it maps years between 0 and 99 to 1900-1999.
|
---|
| 1516 | * See: https://github.com/angular/angular/issues/40377
|
---|
| 1517 | *
|
---|
| 1518 | * Note that this function returns a Date object whose time is midnight in the current locale's
|
---|
| 1519 | * timezone. In the future we might want to change this to be midnight in UTC, but this would be a
|
---|
| 1520 | * considerable breaking change.
|
---|
| 1521 | */
|
---|
| 1522 | function createDate(year, month, date) {
|
---|
| 1523 | // The `newDate` is set to midnight (UTC) on January 1st 1970.
|
---|
| 1524 | // - In PST this will be December 31st 1969 at 4pm.
|
---|
| 1525 | // - In GMT this will be January 1st 1970 at 1am.
|
---|
| 1526 | // Note that they even have different years, dates and months!
|
---|
| 1527 | const newDate = new Date(0);
|
---|
| 1528 | // `setFullYear()` allows years like 0001 to be set correctly. This function does not
|
---|
| 1529 | // change the internal time of the date.
|
---|
| 1530 | // Consider calling `setFullYear(2019, 8, 20)` (September 20, 2019).
|
---|
| 1531 | // - In PST this will now be September 20, 2019 at 4pm
|
---|
| 1532 | // - In GMT this will now be September 20, 2019 at 1am
|
---|
| 1533 | newDate.setFullYear(year, month, date);
|
---|
| 1534 | // We want the final date to be at local midnight, so we reset the time.
|
---|
| 1535 | // - In PST this will now be September 20, 2019 at 12am
|
---|
| 1536 | // - In GMT this will now be September 20, 2019 at 12am
|
---|
| 1537 | newDate.setHours(0, 0, 0);
|
---|
| 1538 | return newDate;
|
---|
| 1539 | }
|
---|
| 1540 | function getNamedFormat(locale, format) {
|
---|
| 1541 | const localeId = getLocaleId(locale);
|
---|
| 1542 | NAMED_FORMATS[localeId] = NAMED_FORMATS[localeId] || {};
|
---|
| 1543 | if (NAMED_FORMATS[localeId][format]) {
|
---|
| 1544 | return NAMED_FORMATS[localeId][format];
|
---|
| 1545 | }
|
---|
| 1546 | let formatValue = '';
|
---|
| 1547 | switch (format) {
|
---|
| 1548 | case 'shortDate':
|
---|
| 1549 | formatValue = getLocaleDateFormat(locale, FormatWidth.Short);
|
---|
| 1550 | break;
|
---|
| 1551 | case 'mediumDate':
|
---|
| 1552 | formatValue = getLocaleDateFormat(locale, FormatWidth.Medium);
|
---|
| 1553 | break;
|
---|
| 1554 | case 'longDate':
|
---|
| 1555 | formatValue = getLocaleDateFormat(locale, FormatWidth.Long);
|
---|
| 1556 | break;
|
---|
| 1557 | case 'fullDate':
|
---|
| 1558 | formatValue = getLocaleDateFormat(locale, FormatWidth.Full);
|
---|
| 1559 | break;
|
---|
| 1560 | case 'shortTime':
|
---|
| 1561 | formatValue = getLocaleTimeFormat(locale, FormatWidth.Short);
|
---|
| 1562 | break;
|
---|
| 1563 | case 'mediumTime':
|
---|
| 1564 | formatValue = getLocaleTimeFormat(locale, FormatWidth.Medium);
|
---|
| 1565 | break;
|
---|
| 1566 | case 'longTime':
|
---|
| 1567 | formatValue = getLocaleTimeFormat(locale, FormatWidth.Long);
|
---|
| 1568 | break;
|
---|
| 1569 | case 'fullTime':
|
---|
| 1570 | formatValue = getLocaleTimeFormat(locale, FormatWidth.Full);
|
---|
| 1571 | break;
|
---|
| 1572 | case 'short':
|
---|
| 1573 | const shortTime = getNamedFormat(locale, 'shortTime');
|
---|
| 1574 | const shortDate = getNamedFormat(locale, 'shortDate');
|
---|
| 1575 | formatValue = formatDateTime(getLocaleDateTimeFormat(locale, FormatWidth.Short), [shortTime, shortDate]);
|
---|
| 1576 | break;
|
---|
| 1577 | case 'medium':
|
---|
| 1578 | const mediumTime = getNamedFormat(locale, 'mediumTime');
|
---|
| 1579 | const mediumDate = getNamedFormat(locale, 'mediumDate');
|
---|
| 1580 | formatValue = formatDateTime(getLocaleDateTimeFormat(locale, FormatWidth.Medium), [mediumTime, mediumDate]);
|
---|
| 1581 | break;
|
---|
| 1582 | case 'long':
|
---|
| 1583 | const longTime = getNamedFormat(locale, 'longTime');
|
---|
| 1584 | const longDate = getNamedFormat(locale, 'longDate');
|
---|
| 1585 | formatValue =
|
---|
| 1586 | formatDateTime(getLocaleDateTimeFormat(locale, FormatWidth.Long), [longTime, longDate]);
|
---|
| 1587 | break;
|
---|
| 1588 | case 'full':
|
---|
| 1589 | const fullTime = getNamedFormat(locale, 'fullTime');
|
---|
| 1590 | const fullDate = getNamedFormat(locale, 'fullDate');
|
---|
| 1591 | formatValue =
|
---|
| 1592 | formatDateTime(getLocaleDateTimeFormat(locale, FormatWidth.Full), [fullTime, fullDate]);
|
---|
| 1593 | break;
|
---|
| 1594 | }
|
---|
| 1595 | if (formatValue) {
|
---|
| 1596 | NAMED_FORMATS[localeId][format] = formatValue;
|
---|
| 1597 | }
|
---|
| 1598 | return formatValue;
|
---|
| 1599 | }
|
---|
| 1600 | function formatDateTime(str, opt_values) {
|
---|
| 1601 | if (opt_values) {
|
---|
| 1602 | str = str.replace(/\{([^}]+)}/g, function (match, key) {
|
---|
| 1603 | return (opt_values != null && key in opt_values) ? opt_values[key] : match;
|
---|
| 1604 | });
|
---|
| 1605 | }
|
---|
| 1606 | return str;
|
---|
| 1607 | }
|
---|
| 1608 | function padNumber(num, digits, minusSign = '-', trim, negWrap) {
|
---|
| 1609 | let neg = '';
|
---|
| 1610 | if (num < 0 || (negWrap && num <= 0)) {
|
---|
| 1611 | if (negWrap) {
|
---|
| 1612 | num = -num + 1;
|
---|
| 1613 | }
|
---|
| 1614 | else {
|
---|
| 1615 | num = -num;
|
---|
| 1616 | neg = minusSign;
|
---|
| 1617 | }
|
---|
| 1618 | }
|
---|
| 1619 | let strNum = String(num);
|
---|
| 1620 | while (strNum.length < digits) {
|
---|
| 1621 | strNum = '0' + strNum;
|
---|
| 1622 | }
|
---|
| 1623 | if (trim) {
|
---|
| 1624 | strNum = strNum.substr(strNum.length - digits);
|
---|
| 1625 | }
|
---|
| 1626 | return neg + strNum;
|
---|
| 1627 | }
|
---|
| 1628 | function formatFractionalSeconds(milliseconds, digits) {
|
---|
| 1629 | const strMs = padNumber(milliseconds, 3);
|
---|
| 1630 | return strMs.substr(0, digits);
|
---|
| 1631 | }
|
---|
| 1632 | /**
|
---|
| 1633 | * Returns a date formatter that transforms a date into its locale digit representation
|
---|
| 1634 | */
|
---|
| 1635 | function dateGetter(name, size, offset = 0, trim = false, negWrap = false) {
|
---|
| 1636 | return function (date, locale) {
|
---|
| 1637 | let part = getDatePart(name, date);
|
---|
| 1638 | if (offset > 0 || part > -offset) {
|
---|
| 1639 | part += offset;
|
---|
| 1640 | }
|
---|
| 1641 | if (name === DateType.Hours) {
|
---|
| 1642 | if (part === 0 && offset === -12) {
|
---|
| 1643 | part = 12;
|
---|
| 1644 | }
|
---|
| 1645 | }
|
---|
| 1646 | else if (name === DateType.FractionalSeconds) {
|
---|
| 1647 | return formatFractionalSeconds(part, size);
|
---|
| 1648 | }
|
---|
| 1649 | const localeMinus = getLocaleNumberSymbol(locale, NumberSymbol.MinusSign);
|
---|
| 1650 | return padNumber(part, size, localeMinus, trim, negWrap);
|
---|
| 1651 | };
|
---|
| 1652 | }
|
---|
| 1653 | function getDatePart(part, date) {
|
---|
| 1654 | switch (part) {
|
---|
| 1655 | case DateType.FullYear:
|
---|
| 1656 | return date.getFullYear();
|
---|
| 1657 | case DateType.Month:
|
---|
| 1658 | return date.getMonth();
|
---|
| 1659 | case DateType.Date:
|
---|
| 1660 | return date.getDate();
|
---|
| 1661 | case DateType.Hours:
|
---|
| 1662 | return date.getHours();
|
---|
| 1663 | case DateType.Minutes:
|
---|
| 1664 | return date.getMinutes();
|
---|
| 1665 | case DateType.Seconds:
|
---|
| 1666 | return date.getSeconds();
|
---|
| 1667 | case DateType.FractionalSeconds:
|
---|
| 1668 | return date.getMilliseconds();
|
---|
| 1669 | case DateType.Day:
|
---|
| 1670 | return date.getDay();
|
---|
| 1671 | default:
|
---|
| 1672 | throw new Error(`Unknown DateType value "${part}".`);
|
---|
| 1673 | }
|
---|
| 1674 | }
|
---|
| 1675 | /**
|
---|
| 1676 | * Returns a date formatter that transforms a date into its locale string representation
|
---|
| 1677 | */
|
---|
| 1678 | function dateStrGetter(name, width, form = FormStyle.Format, extended = false) {
|
---|
| 1679 | return function (date, locale) {
|
---|
| 1680 | return getDateTranslation(date, locale, name, width, form, extended);
|
---|
| 1681 | };
|
---|
| 1682 | }
|
---|
| 1683 | /**
|
---|
| 1684 | * Returns the locale translation of a date for a given form, type and width
|
---|
| 1685 | */
|
---|
| 1686 | function getDateTranslation(date, locale, name, width, form, extended) {
|
---|
| 1687 | switch (name) {
|
---|
| 1688 | case TranslationType.Months:
|
---|
| 1689 | return getLocaleMonthNames(locale, form, width)[date.getMonth()];
|
---|
| 1690 | case TranslationType.Days:
|
---|
| 1691 | return getLocaleDayNames(locale, form, width)[date.getDay()];
|
---|
| 1692 | case TranslationType.DayPeriods:
|
---|
| 1693 | const currentHours = date.getHours();
|
---|
| 1694 | const currentMinutes = date.getMinutes();
|
---|
| 1695 | if (extended) {
|
---|
| 1696 | const rules = getLocaleExtraDayPeriodRules(locale);
|
---|
| 1697 | const dayPeriods = getLocaleExtraDayPeriods(locale, form, width);
|
---|
| 1698 | const index = rules.findIndex(rule => {
|
---|
| 1699 | if (Array.isArray(rule)) {
|
---|
| 1700 | // morning, afternoon, evening, night
|
---|
| 1701 | const [from, to] = rule;
|
---|
| 1702 | const afterFrom = currentHours >= from.hours && currentMinutes >= from.minutes;
|
---|
| 1703 | const beforeTo = (currentHours < to.hours ||
|
---|
| 1704 | (currentHours === to.hours && currentMinutes < to.minutes));
|
---|
| 1705 | // We must account for normal rules that span a period during the day (e.g. 6am-9am)
|
---|
| 1706 | // where `from` is less (earlier) than `to`. But also rules that span midnight (e.g.
|
---|
| 1707 | // 10pm - 5am) where `from` is greater (later!) than `to`.
|
---|
| 1708 | //
|
---|
| 1709 | // In the first case the current time must be BOTH after `from` AND before `to`
|
---|
| 1710 | // (e.g. 8am is after 6am AND before 10am).
|
---|
| 1711 | //
|
---|
| 1712 | // In the second case the current time must be EITHER after `from` OR before `to`
|
---|
| 1713 | // (e.g. 4am is before 5am but not after 10pm; and 11pm is not before 5am but it is
|
---|
| 1714 | // after 10pm).
|
---|
| 1715 | if (from.hours < to.hours) {
|
---|
| 1716 | if (afterFrom && beforeTo) {
|
---|
| 1717 | return true;
|
---|
| 1718 | }
|
---|
| 1719 | }
|
---|
| 1720 | else if (afterFrom || beforeTo) {
|
---|
| 1721 | return true;
|
---|
| 1722 | }
|
---|
| 1723 | }
|
---|
| 1724 | else { // noon or midnight
|
---|
| 1725 | if (rule.hours === currentHours && rule.minutes === currentMinutes) {
|
---|
| 1726 | return true;
|
---|
| 1727 | }
|
---|
| 1728 | }
|
---|
| 1729 | return false;
|
---|
| 1730 | });
|
---|
| 1731 | if (index !== -1) {
|
---|
| 1732 | return dayPeriods[index];
|
---|
| 1733 | }
|
---|
| 1734 | }
|
---|
| 1735 | // if no rules for the day periods, we use am/pm by default
|
---|
| 1736 | return getLocaleDayPeriods(locale, form, width)[currentHours < 12 ? 0 : 1];
|
---|
| 1737 | case TranslationType.Eras:
|
---|
| 1738 | return getLocaleEraNames(locale, width)[date.getFullYear() <= 0 ? 0 : 1];
|
---|
| 1739 | default:
|
---|
| 1740 | // This default case is not needed by TypeScript compiler, as the switch is exhaustive.
|
---|
| 1741 | // However Closure Compiler does not understand that and reports an error in typed mode.
|
---|
| 1742 | // The `throw new Error` below works around the problem, and the unexpected: never variable
|
---|
| 1743 | // makes sure tsc still checks this code is unreachable.
|
---|
| 1744 | const unexpected = name;
|
---|
| 1745 | throw new Error(`unexpected translation type ${unexpected}`);
|
---|
| 1746 | }
|
---|
| 1747 | }
|
---|
| 1748 | /**
|
---|
| 1749 | * Returns a date formatter that transforms a date and an offset into a timezone with ISO8601 or
|
---|
| 1750 | * GMT format depending on the width (eg: short = +0430, short:GMT = GMT+4, long = GMT+04:30,
|
---|
| 1751 | * extended = +04:30)
|
---|
| 1752 | */
|
---|
| 1753 | function timeZoneGetter(width) {
|
---|
| 1754 | return function (date, locale, offset) {
|
---|
| 1755 | const zone = -1 * offset;
|
---|
| 1756 | const minusSign = getLocaleNumberSymbol(locale, NumberSymbol.MinusSign);
|
---|
| 1757 | const hours = zone > 0 ? Math.floor(zone / 60) : Math.ceil(zone / 60);
|
---|
| 1758 | switch (width) {
|
---|
| 1759 | case ZoneWidth.Short:
|
---|
| 1760 | return ((zone >= 0) ? '+' : '') + padNumber(hours, 2, minusSign) +
|
---|
| 1761 | padNumber(Math.abs(zone % 60), 2, minusSign);
|
---|
| 1762 | case ZoneWidth.ShortGMT:
|
---|
| 1763 | return 'GMT' + ((zone >= 0) ? '+' : '') + padNumber(hours, 1, minusSign);
|
---|
| 1764 | case ZoneWidth.Long:
|
---|
| 1765 | return 'GMT' + ((zone >= 0) ? '+' : '') + padNumber(hours, 2, minusSign) + ':' +
|
---|
| 1766 | padNumber(Math.abs(zone % 60), 2, minusSign);
|
---|
| 1767 | case ZoneWidth.Extended:
|
---|
| 1768 | if (offset === 0) {
|
---|
| 1769 | return 'Z';
|
---|
| 1770 | }
|
---|
| 1771 | else {
|
---|
| 1772 | return ((zone >= 0) ? '+' : '') + padNumber(hours, 2, minusSign) + ':' +
|
---|
| 1773 | padNumber(Math.abs(zone % 60), 2, minusSign);
|
---|
| 1774 | }
|
---|
| 1775 | default:
|
---|
| 1776 | throw new Error(`Unknown zone width "${width}"`);
|
---|
| 1777 | }
|
---|
| 1778 | };
|
---|
| 1779 | }
|
---|
| 1780 | const JANUARY = 0;
|
---|
| 1781 | const THURSDAY = 4;
|
---|
| 1782 | function getFirstThursdayOfYear(year) {
|
---|
| 1783 | const firstDayOfYear = createDate(year, JANUARY, 1).getDay();
|
---|
| 1784 | return createDate(year, 0, 1 + ((firstDayOfYear <= THURSDAY) ? THURSDAY : THURSDAY + 7) - firstDayOfYear);
|
---|
| 1785 | }
|
---|
| 1786 | function getThursdayThisWeek(datetime) {
|
---|
| 1787 | return createDate(datetime.getFullYear(), datetime.getMonth(), datetime.getDate() + (THURSDAY - datetime.getDay()));
|
---|
| 1788 | }
|
---|
| 1789 | function weekGetter(size, monthBased = false) {
|
---|
| 1790 | return function (date, locale) {
|
---|
| 1791 | let result;
|
---|
| 1792 | if (monthBased) {
|
---|
| 1793 | const nbDaysBefore1stDayOfMonth = new Date(date.getFullYear(), date.getMonth(), 1).getDay() - 1;
|
---|
| 1794 | const today = date.getDate();
|
---|
| 1795 | result = 1 + Math.floor((today + nbDaysBefore1stDayOfMonth) / 7);
|
---|
| 1796 | }
|
---|
| 1797 | else {
|
---|
| 1798 | const thisThurs = getThursdayThisWeek(date);
|
---|
| 1799 | // Some days of a year are part of next year according to ISO 8601.
|
---|
| 1800 | // Compute the firstThurs from the year of this week's Thursday
|
---|
| 1801 | const firstThurs = getFirstThursdayOfYear(thisThurs.getFullYear());
|
---|
| 1802 | const diff = thisThurs.getTime() - firstThurs.getTime();
|
---|
| 1803 | result = 1 + Math.round(diff / 6.048e8); // 6.048e8 ms per week
|
---|
| 1804 | }
|
---|
| 1805 | return padNumber(result, size, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign));
|
---|
| 1806 | };
|
---|
| 1807 | }
|
---|
| 1808 | /**
|
---|
| 1809 | * Returns a date formatter that provides the week-numbering year for the input date.
|
---|
| 1810 | */
|
---|
| 1811 | function weekNumberingYearGetter(size, trim = false) {
|
---|
| 1812 | return function (date, locale) {
|
---|
| 1813 | const thisThurs = getThursdayThisWeek(date);
|
---|
| 1814 | const weekNumberingYear = thisThurs.getFullYear();
|
---|
| 1815 | return padNumber(weekNumberingYear, size, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign), trim);
|
---|
| 1816 | };
|
---|
| 1817 | }
|
---|
| 1818 | const DATE_FORMATS = {};
|
---|
| 1819 | // Based on CLDR formats:
|
---|
| 1820 | // See complete list: http://www.unicode.org/reports/tr35/tr35-dates.html#Date_Field_Symbol_Table
|
---|
| 1821 | // See also explanations: http://cldr.unicode.org/translation/date-time
|
---|
| 1822 | // TODO(ocombe): support all missing cldr formats: U, Q, D, F, e, j, J, C, A, v, V, X, x
|
---|
| 1823 | function getDateFormatter(format) {
|
---|
| 1824 | if (DATE_FORMATS[format]) {
|
---|
| 1825 | return DATE_FORMATS[format];
|
---|
| 1826 | }
|
---|
| 1827 | let formatter;
|
---|
| 1828 | switch (format) {
|
---|
| 1829 | // Era name (AD/BC)
|
---|
| 1830 | case 'G':
|
---|
| 1831 | case 'GG':
|
---|
| 1832 | case 'GGG':
|
---|
| 1833 | formatter = dateStrGetter(TranslationType.Eras, TranslationWidth.Abbreviated);
|
---|
| 1834 | break;
|
---|
| 1835 | case 'GGGG':
|
---|
| 1836 | formatter = dateStrGetter(TranslationType.Eras, TranslationWidth.Wide);
|
---|
| 1837 | break;
|
---|
| 1838 | case 'GGGGG':
|
---|
| 1839 | formatter = dateStrGetter(TranslationType.Eras, TranslationWidth.Narrow);
|
---|
| 1840 | break;
|
---|
| 1841 | // 1 digit representation of the year, e.g. (AD 1 => 1, AD 199 => 199)
|
---|
| 1842 | case 'y':
|
---|
| 1843 | formatter = dateGetter(DateType.FullYear, 1, 0, false, true);
|
---|
| 1844 | break;
|
---|
| 1845 | // 2 digit representation of the year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
|
---|
| 1846 | case 'yy':
|
---|
| 1847 | formatter = dateGetter(DateType.FullYear, 2, 0, true, true);
|
---|
| 1848 | break;
|
---|
| 1849 | // 3 digit representation of the year, padded (000-999). (e.g. AD 2001 => 01, AD 2010 => 10)
|
---|
| 1850 | case 'yyy':
|
---|
| 1851 | formatter = dateGetter(DateType.FullYear, 3, 0, false, true);
|
---|
| 1852 | break;
|
---|
| 1853 | // 4 digit representation of the year (e.g. AD 1 => 0001, AD 2010 => 2010)
|
---|
| 1854 | case 'yyyy':
|
---|
| 1855 | formatter = dateGetter(DateType.FullYear, 4, 0, false, true);
|
---|
| 1856 | break;
|
---|
| 1857 | // 1 digit representation of the week-numbering year, e.g. (AD 1 => 1, AD 199 => 199)
|
---|
| 1858 | case 'Y':
|
---|
| 1859 | formatter = weekNumberingYearGetter(1);
|
---|
| 1860 | break;
|
---|
| 1861 | // 2 digit representation of the week-numbering year, padded (00-99). (e.g. AD 2001 => 01, AD
|
---|
| 1862 | // 2010 => 10)
|
---|
| 1863 | case 'YY':
|
---|
| 1864 | formatter = weekNumberingYearGetter(2, true);
|
---|
| 1865 | break;
|
---|
| 1866 | // 3 digit representation of the week-numbering year, padded (000-999). (e.g. AD 1 => 001, AD
|
---|
| 1867 | // 2010 => 2010)
|
---|
| 1868 | case 'YYY':
|
---|
| 1869 | formatter = weekNumberingYearGetter(3);
|
---|
| 1870 | break;
|
---|
| 1871 | // 4 digit representation of the week-numbering year (e.g. AD 1 => 0001, AD 2010 => 2010)
|
---|
| 1872 | case 'YYYY':
|
---|
| 1873 | formatter = weekNumberingYearGetter(4);
|
---|
| 1874 | break;
|
---|
| 1875 | // Month of the year (1-12), numeric
|
---|
| 1876 | case 'M':
|
---|
| 1877 | case 'L':
|
---|
| 1878 | formatter = dateGetter(DateType.Month, 1, 1);
|
---|
| 1879 | break;
|
---|
| 1880 | case 'MM':
|
---|
| 1881 | case 'LL':
|
---|
| 1882 | formatter = dateGetter(DateType.Month, 2, 1);
|
---|
| 1883 | break;
|
---|
| 1884 | // Month of the year (January, ...), string, format
|
---|
| 1885 | case 'MMM':
|
---|
| 1886 | formatter = dateStrGetter(TranslationType.Months, TranslationWidth.Abbreviated);
|
---|
| 1887 | break;
|
---|
| 1888 | case 'MMMM':
|
---|
| 1889 | formatter = dateStrGetter(TranslationType.Months, TranslationWidth.Wide);
|
---|
| 1890 | break;
|
---|
| 1891 | case 'MMMMM':
|
---|
| 1892 | formatter = dateStrGetter(TranslationType.Months, TranslationWidth.Narrow);
|
---|
| 1893 | break;
|
---|
| 1894 | // Month of the year (January, ...), string, standalone
|
---|
| 1895 | case 'LLL':
|
---|
| 1896 | formatter =
|
---|
| 1897 | dateStrGetter(TranslationType.Months, TranslationWidth.Abbreviated, FormStyle.Standalone);
|
---|
| 1898 | break;
|
---|
| 1899 | case 'LLLL':
|
---|
| 1900 | formatter =
|
---|
| 1901 | dateStrGetter(TranslationType.Months, TranslationWidth.Wide, FormStyle.Standalone);
|
---|
| 1902 | break;
|
---|
| 1903 | case 'LLLLL':
|
---|
| 1904 | formatter =
|
---|
| 1905 | dateStrGetter(TranslationType.Months, TranslationWidth.Narrow, FormStyle.Standalone);
|
---|
| 1906 | break;
|
---|
| 1907 | // Week of the year (1, ... 52)
|
---|
| 1908 | case 'w':
|
---|
| 1909 | formatter = weekGetter(1);
|
---|
| 1910 | break;
|
---|
| 1911 | case 'ww':
|
---|
| 1912 | formatter = weekGetter(2);
|
---|
| 1913 | break;
|
---|
| 1914 | // Week of the month (1, ...)
|
---|
| 1915 | case 'W':
|
---|
| 1916 | formatter = weekGetter(1, true);
|
---|
| 1917 | break;
|
---|
| 1918 | // Day of the month (1-31)
|
---|
| 1919 | case 'd':
|
---|
| 1920 | formatter = dateGetter(DateType.Date, 1);
|
---|
| 1921 | break;
|
---|
| 1922 | case 'dd':
|
---|
| 1923 | formatter = dateGetter(DateType.Date, 2);
|
---|
| 1924 | break;
|
---|
| 1925 | // Day of the Week StandAlone (1, 1, Mon, Monday, M, Mo)
|
---|
| 1926 | case 'c':
|
---|
| 1927 | case 'cc':
|
---|
| 1928 | formatter = dateGetter(DateType.Day, 1);
|
---|
| 1929 | break;
|
---|
| 1930 | case 'ccc':
|
---|
| 1931 | formatter =
|
---|
| 1932 | dateStrGetter(TranslationType.Days, TranslationWidth.Abbreviated, FormStyle.Standalone);
|
---|
| 1933 | break;
|
---|
| 1934 | case 'cccc':
|
---|
| 1935 | formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Wide, FormStyle.Standalone);
|
---|
| 1936 | break;
|
---|
| 1937 | case 'ccccc':
|
---|
| 1938 | formatter =
|
---|
| 1939 | dateStrGetter(TranslationType.Days, TranslationWidth.Narrow, FormStyle.Standalone);
|
---|
| 1940 | break;
|
---|
| 1941 | case 'cccccc':
|
---|
| 1942 | formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Short, FormStyle.Standalone);
|
---|
| 1943 | break;
|
---|
| 1944 | // Day of the Week
|
---|
| 1945 | case 'E':
|
---|
| 1946 | case 'EE':
|
---|
| 1947 | case 'EEE':
|
---|
| 1948 | formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Abbreviated);
|
---|
| 1949 | break;
|
---|
| 1950 | case 'EEEE':
|
---|
| 1951 | formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Wide);
|
---|
| 1952 | break;
|
---|
| 1953 | case 'EEEEE':
|
---|
| 1954 | formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Narrow);
|
---|
| 1955 | break;
|
---|
| 1956 | case 'EEEEEE':
|
---|
| 1957 | formatter = dateStrGetter(TranslationType.Days, TranslationWidth.Short);
|
---|
| 1958 | break;
|
---|
| 1959 | // Generic period of the day (am-pm)
|
---|
| 1960 | case 'a':
|
---|
| 1961 | case 'aa':
|
---|
| 1962 | case 'aaa':
|
---|
| 1963 | formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Abbreviated);
|
---|
| 1964 | break;
|
---|
| 1965 | case 'aaaa':
|
---|
| 1966 | formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Wide);
|
---|
| 1967 | break;
|
---|
| 1968 | case 'aaaaa':
|
---|
| 1969 | formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Narrow);
|
---|
| 1970 | break;
|
---|
| 1971 | // Extended period of the day (midnight, at night, ...), standalone
|
---|
| 1972 | case 'b':
|
---|
| 1973 | case 'bb':
|
---|
| 1974 | case 'bbb':
|
---|
| 1975 | formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Abbreviated, FormStyle.Standalone, true);
|
---|
| 1976 | break;
|
---|
| 1977 | case 'bbbb':
|
---|
| 1978 | formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Wide, FormStyle.Standalone, true);
|
---|
| 1979 | break;
|
---|
| 1980 | case 'bbbbb':
|
---|
| 1981 | formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Narrow, FormStyle.Standalone, true);
|
---|
| 1982 | break;
|
---|
| 1983 | // Extended period of the day (midnight, night, ...), standalone
|
---|
| 1984 | case 'B':
|
---|
| 1985 | case 'BB':
|
---|
| 1986 | case 'BBB':
|
---|
| 1987 | formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Abbreviated, FormStyle.Format, true);
|
---|
| 1988 | break;
|
---|
| 1989 | case 'BBBB':
|
---|
| 1990 | formatter =
|
---|
| 1991 | dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Wide, FormStyle.Format, true);
|
---|
| 1992 | break;
|
---|
| 1993 | case 'BBBBB':
|
---|
| 1994 | formatter = dateStrGetter(TranslationType.DayPeriods, TranslationWidth.Narrow, FormStyle.Format, true);
|
---|
| 1995 | break;
|
---|
| 1996 | // Hour in AM/PM, (1-12)
|
---|
| 1997 | case 'h':
|
---|
| 1998 | formatter = dateGetter(DateType.Hours, 1, -12);
|
---|
| 1999 | break;
|
---|
| 2000 | case 'hh':
|
---|
| 2001 | formatter = dateGetter(DateType.Hours, 2, -12);
|
---|
| 2002 | break;
|
---|
| 2003 | // Hour of the day (0-23)
|
---|
| 2004 | case 'H':
|
---|
| 2005 | formatter = dateGetter(DateType.Hours, 1);
|
---|
| 2006 | break;
|
---|
| 2007 | // Hour in day, padded (00-23)
|
---|
| 2008 | case 'HH':
|
---|
| 2009 | formatter = dateGetter(DateType.Hours, 2);
|
---|
| 2010 | break;
|
---|
| 2011 | // Minute of the hour (0-59)
|
---|
| 2012 | case 'm':
|
---|
| 2013 | formatter = dateGetter(DateType.Minutes, 1);
|
---|
| 2014 | break;
|
---|
| 2015 | case 'mm':
|
---|
| 2016 | formatter = dateGetter(DateType.Minutes, 2);
|
---|
| 2017 | break;
|
---|
| 2018 | // Second of the minute (0-59)
|
---|
| 2019 | case 's':
|
---|
| 2020 | formatter = dateGetter(DateType.Seconds, 1);
|
---|
| 2021 | break;
|
---|
| 2022 | case 'ss':
|
---|
| 2023 | formatter = dateGetter(DateType.Seconds, 2);
|
---|
| 2024 | break;
|
---|
| 2025 | // Fractional second
|
---|
| 2026 | case 'S':
|
---|
| 2027 | formatter = dateGetter(DateType.FractionalSeconds, 1);
|
---|
| 2028 | break;
|
---|
| 2029 | case 'SS':
|
---|
| 2030 | formatter = dateGetter(DateType.FractionalSeconds, 2);
|
---|
| 2031 | break;
|
---|
| 2032 | case 'SSS':
|
---|
| 2033 | formatter = dateGetter(DateType.FractionalSeconds, 3);
|
---|
| 2034 | break;
|
---|
| 2035 | // Timezone ISO8601 short format (-0430)
|
---|
| 2036 | case 'Z':
|
---|
| 2037 | case 'ZZ':
|
---|
| 2038 | case 'ZZZ':
|
---|
| 2039 | formatter = timeZoneGetter(ZoneWidth.Short);
|
---|
| 2040 | break;
|
---|
| 2041 | // Timezone ISO8601 extended format (-04:30)
|
---|
| 2042 | case 'ZZZZZ':
|
---|
| 2043 | formatter = timeZoneGetter(ZoneWidth.Extended);
|
---|
| 2044 | break;
|
---|
| 2045 | // Timezone GMT short format (GMT+4)
|
---|
| 2046 | case 'O':
|
---|
| 2047 | case 'OO':
|
---|
| 2048 | case 'OOO':
|
---|
| 2049 | // Should be location, but fallback to format O instead because we don't have the data yet
|
---|
| 2050 | case 'z':
|
---|
| 2051 | case 'zz':
|
---|
| 2052 | case 'zzz':
|
---|
| 2053 | formatter = timeZoneGetter(ZoneWidth.ShortGMT);
|
---|
| 2054 | break;
|
---|
| 2055 | // Timezone GMT long format (GMT+0430)
|
---|
| 2056 | case 'OOOO':
|
---|
| 2057 | case 'ZZZZ':
|
---|
| 2058 | // Should be location, but fallback to format O instead because we don't have the data yet
|
---|
| 2059 | case 'zzzz':
|
---|
| 2060 | formatter = timeZoneGetter(ZoneWidth.Long);
|
---|
| 2061 | break;
|
---|
| 2062 | default:
|
---|
| 2063 | return null;
|
---|
| 2064 | }
|
---|
| 2065 | DATE_FORMATS[format] = formatter;
|
---|
| 2066 | return formatter;
|
---|
| 2067 | }
|
---|
| 2068 | function timezoneToOffset(timezone, fallback) {
|
---|
| 2069 | // Support: IE 11 only, Edge 13-15+
|
---|
| 2070 | // IE/Edge do not "understand" colon (`:`) in timezone
|
---|
| 2071 | timezone = timezone.replace(/:/g, '');
|
---|
| 2072 | const requestedTimezoneOffset = Date.parse('Jan 01, 1970 00:00:00 ' + timezone) / 60000;
|
---|
| 2073 | return isNaN(requestedTimezoneOffset) ? fallback : requestedTimezoneOffset;
|
---|
| 2074 | }
|
---|
| 2075 | function addDateMinutes(date, minutes) {
|
---|
| 2076 | date = new Date(date.getTime());
|
---|
| 2077 | date.setMinutes(date.getMinutes() + minutes);
|
---|
| 2078 | return date;
|
---|
| 2079 | }
|
---|
| 2080 | function convertTimezoneToLocal(date, timezone, reverse) {
|
---|
| 2081 | const reverseValue = reverse ? -1 : 1;
|
---|
| 2082 | const dateTimezoneOffset = date.getTimezoneOffset();
|
---|
| 2083 | const timezoneOffset = timezoneToOffset(timezone, dateTimezoneOffset);
|
---|
| 2084 | return addDateMinutes(date, reverseValue * (timezoneOffset - dateTimezoneOffset));
|
---|
| 2085 | }
|
---|
| 2086 | /**
|
---|
| 2087 | * Converts a value to date.
|
---|
| 2088 | *
|
---|
| 2089 | * Supported input formats:
|
---|
| 2090 | * - `Date`
|
---|
| 2091 | * - number: timestamp
|
---|
| 2092 | * - string: numeric (e.g. "1234"), ISO and date strings in a format supported by
|
---|
| 2093 | * [Date.parse()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/parse).
|
---|
| 2094 | * Note: ISO strings without time return a date without timeoffset.
|
---|
| 2095 | *
|
---|
| 2096 | * Throws if unable to convert to a date.
|
---|
| 2097 | */
|
---|
| 2098 | function toDate(value) {
|
---|
| 2099 | if (isDate(value)) {
|
---|
| 2100 | return value;
|
---|
| 2101 | }
|
---|
| 2102 | if (typeof value === 'number' && !isNaN(value)) {
|
---|
| 2103 | return new Date(value);
|
---|
| 2104 | }
|
---|
| 2105 | if (typeof value === 'string') {
|
---|
| 2106 | value = value.trim();
|
---|
| 2107 | if (/^(\d{4}(-\d{1,2}(-\d{1,2})?)?)$/.test(value)) {
|
---|
| 2108 | /* For ISO Strings without time the day, month and year must be extracted from the ISO String
|
---|
| 2109 | before Date creation to avoid time offset and errors in the new Date.
|
---|
| 2110 | If we only replace '-' with ',' in the ISO String ("2015,01,01"), and try to create a new
|
---|
| 2111 | date, some browsers (e.g. IE 9) will throw an invalid Date error.
|
---|
| 2112 | If we leave the '-' ("2015-01-01") and try to create a new Date("2015-01-01") the timeoffset
|
---|
| 2113 | is applied.
|
---|
| 2114 | Note: ISO months are 0 for January, 1 for February, ... */
|
---|
| 2115 | const [y, m = 1, d = 1] = value.split('-').map((val) => +val);
|
---|
| 2116 | return createDate(y, m - 1, d);
|
---|
| 2117 | }
|
---|
| 2118 | const parsedNb = parseFloat(value);
|
---|
| 2119 | // any string that only contains numbers, like "1234" but not like "1234hello"
|
---|
| 2120 | if (!isNaN(value - parsedNb)) {
|
---|
| 2121 | return new Date(parsedNb);
|
---|
| 2122 | }
|
---|
| 2123 | let match;
|
---|
| 2124 | if (match = value.match(ISO8601_DATE_REGEX)) {
|
---|
| 2125 | return isoStringToDate(match);
|
---|
| 2126 | }
|
---|
| 2127 | }
|
---|
| 2128 | const date = new Date(value);
|
---|
| 2129 | if (!isDate(date)) {
|
---|
| 2130 | throw new Error(`Unable to convert "${value}" into a date`);
|
---|
| 2131 | }
|
---|
| 2132 | return date;
|
---|
| 2133 | }
|
---|
| 2134 | /**
|
---|
| 2135 | * Converts a date in ISO8601 to a Date.
|
---|
| 2136 | * Used instead of `Date.parse` because of browser discrepancies.
|
---|
| 2137 | */
|
---|
| 2138 | function isoStringToDate(match) {
|
---|
| 2139 | const date = new Date(0);
|
---|
| 2140 | let tzHour = 0;
|
---|
| 2141 | let tzMin = 0;
|
---|
| 2142 | // match[8] means that the string contains "Z" (UTC) or a timezone like "+01:00" or "+0100"
|
---|
| 2143 | const dateSetter = match[8] ? date.setUTCFullYear : date.setFullYear;
|
---|
| 2144 | const timeSetter = match[8] ? date.setUTCHours : date.setHours;
|
---|
| 2145 | // if there is a timezone defined like "+01:00" or "+0100"
|
---|
| 2146 | if (match[9]) {
|
---|
| 2147 | tzHour = Number(match[9] + match[10]);
|
---|
| 2148 | tzMin = Number(match[9] + match[11]);
|
---|
| 2149 | }
|
---|
| 2150 | dateSetter.call(date, Number(match[1]), Number(match[2]) - 1, Number(match[3]));
|
---|
| 2151 | const h = Number(match[4] || 0) - tzHour;
|
---|
| 2152 | const m = Number(match[5] || 0) - tzMin;
|
---|
| 2153 | const s = Number(match[6] || 0);
|
---|
| 2154 | // The ECMAScript specification (https://www.ecma-international.org/ecma-262/5.1/#sec-15.9.1.11)
|
---|
| 2155 | // defines that `DateTime` milliseconds should always be rounded down, so that `999.9ms`
|
---|
| 2156 | // becomes `999ms`.
|
---|
| 2157 | const ms = Math.floor(parseFloat('0.' + (match[7] || 0)) * 1000);
|
---|
| 2158 | timeSetter.call(date, h, m, s, ms);
|
---|
| 2159 | return date;
|
---|
| 2160 | }
|
---|
| 2161 | function isDate(value) {
|
---|
| 2162 | return value instanceof Date && !isNaN(value.valueOf());
|
---|
| 2163 | }
|
---|
| 2164 |
|
---|
| 2165 | /**
|
---|
| 2166 | * @license
|
---|
| 2167 | * Copyright Google LLC All Rights Reserved.
|
---|
| 2168 | *
|
---|
| 2169 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 2170 | * found in the LICENSE file at https://angular.io/license
|
---|
| 2171 | */
|
---|
| 2172 | const NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(-(\d+))?)?$/;
|
---|
| 2173 | const MAX_DIGITS = 22;
|
---|
| 2174 | const DECIMAL_SEP = '.';
|
---|
| 2175 | const ZERO_CHAR = '0';
|
---|
| 2176 | const PATTERN_SEP = ';';
|
---|
| 2177 | const GROUP_SEP = ',';
|
---|
| 2178 | const DIGIT_CHAR = '#';
|
---|
| 2179 | const CURRENCY_CHAR = '¤';
|
---|
| 2180 | const PERCENT_CHAR = '%';
|
---|
| 2181 | /**
|
---|
| 2182 | * Transforms a number to a locale string based on a style and a format.
|
---|
| 2183 | */
|
---|
| 2184 | function formatNumberToLocaleString(value, pattern, locale, groupSymbol, decimalSymbol, digitsInfo, isPercent = false) {
|
---|
| 2185 | let formattedText = '';
|
---|
| 2186 | let isZero = false;
|
---|
| 2187 | if (!isFinite(value)) {
|
---|
| 2188 | formattedText = getLocaleNumberSymbol(locale, NumberSymbol.Infinity);
|
---|
| 2189 | }
|
---|
| 2190 | else {
|
---|
| 2191 | let parsedNumber = parseNumber(value);
|
---|
| 2192 | if (isPercent) {
|
---|
| 2193 | parsedNumber = toPercent(parsedNumber);
|
---|
| 2194 | }
|
---|
| 2195 | let minInt = pattern.minInt;
|
---|
| 2196 | let minFraction = pattern.minFrac;
|
---|
| 2197 | let maxFraction = pattern.maxFrac;
|
---|
| 2198 | if (digitsInfo) {
|
---|
| 2199 | const parts = digitsInfo.match(NUMBER_FORMAT_REGEXP);
|
---|
| 2200 | if (parts === null) {
|
---|
| 2201 | throw new Error(`${digitsInfo} is not a valid digit info`);
|
---|
| 2202 | }
|
---|
| 2203 | const minIntPart = parts[1];
|
---|
| 2204 | const minFractionPart = parts[3];
|
---|
| 2205 | const maxFractionPart = parts[5];
|
---|
| 2206 | if (minIntPart != null) {
|
---|
| 2207 | minInt = parseIntAutoRadix(minIntPart);
|
---|
| 2208 | }
|
---|
| 2209 | if (minFractionPart != null) {
|
---|
| 2210 | minFraction = parseIntAutoRadix(minFractionPart);
|
---|
| 2211 | }
|
---|
| 2212 | if (maxFractionPart != null) {
|
---|
| 2213 | maxFraction = parseIntAutoRadix(maxFractionPart);
|
---|
| 2214 | }
|
---|
| 2215 | else if (minFractionPart != null && minFraction > maxFraction) {
|
---|
| 2216 | maxFraction = minFraction;
|
---|
| 2217 | }
|
---|
| 2218 | }
|
---|
| 2219 | roundNumber(parsedNumber, minFraction, maxFraction);
|
---|
| 2220 | let digits = parsedNumber.digits;
|
---|
| 2221 | let integerLen = parsedNumber.integerLen;
|
---|
| 2222 | const exponent = parsedNumber.exponent;
|
---|
| 2223 | let decimals = [];
|
---|
| 2224 | isZero = digits.every(d => !d);
|
---|
| 2225 | // pad zeros for small numbers
|
---|
| 2226 | for (; integerLen < minInt; integerLen++) {
|
---|
| 2227 | digits.unshift(0);
|
---|
| 2228 | }
|
---|
| 2229 | // pad zeros for small numbers
|
---|
| 2230 | for (; integerLen < 0; integerLen++) {
|
---|
| 2231 | digits.unshift(0);
|
---|
| 2232 | }
|
---|
| 2233 | // extract decimals digits
|
---|
| 2234 | if (integerLen > 0) {
|
---|
| 2235 | decimals = digits.splice(integerLen, digits.length);
|
---|
| 2236 | }
|
---|
| 2237 | else {
|
---|
| 2238 | decimals = digits;
|
---|
| 2239 | digits = [0];
|
---|
| 2240 | }
|
---|
| 2241 | // format the integer digits with grouping separators
|
---|
| 2242 | const groups = [];
|
---|
| 2243 | if (digits.length >= pattern.lgSize) {
|
---|
| 2244 | groups.unshift(digits.splice(-pattern.lgSize, digits.length).join(''));
|
---|
| 2245 | }
|
---|
| 2246 | while (digits.length > pattern.gSize) {
|
---|
| 2247 | groups.unshift(digits.splice(-pattern.gSize, digits.length).join(''));
|
---|
| 2248 | }
|
---|
| 2249 | if (digits.length) {
|
---|
| 2250 | groups.unshift(digits.join(''));
|
---|
| 2251 | }
|
---|
| 2252 | formattedText = groups.join(getLocaleNumberSymbol(locale, groupSymbol));
|
---|
| 2253 | // append the decimal digits
|
---|
| 2254 | if (decimals.length) {
|
---|
| 2255 | formattedText += getLocaleNumberSymbol(locale, decimalSymbol) + decimals.join('');
|
---|
| 2256 | }
|
---|
| 2257 | if (exponent) {
|
---|
| 2258 | formattedText += getLocaleNumberSymbol(locale, NumberSymbol.Exponential) + '+' + exponent;
|
---|
| 2259 | }
|
---|
| 2260 | }
|
---|
| 2261 | if (value < 0 && !isZero) {
|
---|
| 2262 | formattedText = pattern.negPre + formattedText + pattern.negSuf;
|
---|
| 2263 | }
|
---|
| 2264 | else {
|
---|
| 2265 | formattedText = pattern.posPre + formattedText + pattern.posSuf;
|
---|
| 2266 | }
|
---|
| 2267 | return formattedText;
|
---|
| 2268 | }
|
---|
| 2269 | /**
|
---|
| 2270 | * @ngModule CommonModule
|
---|
| 2271 | * @description
|
---|
| 2272 | *
|
---|
| 2273 | * Formats a number as currency using locale rules.
|
---|
| 2274 | *
|
---|
| 2275 | * @param value The number to format.
|
---|
| 2276 | * @param locale A locale code for the locale format rules to use.
|
---|
| 2277 | * @param currency A string containing the currency symbol or its name,
|
---|
| 2278 | * such as "$" or "Canadian Dollar". Used in output string, but does not affect the operation
|
---|
| 2279 | * of the function.
|
---|
| 2280 | * @param currencyCode The [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217)
|
---|
| 2281 | * currency code, such as `USD` for the US dollar and `EUR` for the euro.
|
---|
| 2282 | * Used to determine the number of digits in the decimal part.
|
---|
| 2283 | * @param digitsInfo Decimal representation options, specified by a string in the following format:
|
---|
| 2284 | * `{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`. See `DecimalPipe` for more details.
|
---|
| 2285 | *
|
---|
| 2286 | * @returns The formatted currency value.
|
---|
| 2287 | *
|
---|
| 2288 | * @see `formatNumber()`
|
---|
| 2289 | * @see `DecimalPipe`
|
---|
| 2290 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 2291 | *
|
---|
| 2292 | * @publicApi
|
---|
| 2293 | */
|
---|
| 2294 | function formatCurrency(value, locale, currency, currencyCode, digitsInfo) {
|
---|
| 2295 | const format = getLocaleNumberFormat(locale, NumberFormatStyle.Currency);
|
---|
| 2296 | const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign));
|
---|
| 2297 | pattern.minFrac = getNumberOfCurrencyDigits(currencyCode);
|
---|
| 2298 | pattern.maxFrac = pattern.minFrac;
|
---|
| 2299 | const res = formatNumberToLocaleString(value, pattern, locale, NumberSymbol.CurrencyGroup, NumberSymbol.CurrencyDecimal, digitsInfo);
|
---|
| 2300 | return res
|
---|
| 2301 | .replace(CURRENCY_CHAR, currency)
|
---|
| 2302 | // if we have 2 time the currency character, the second one is ignored
|
---|
| 2303 | .replace(CURRENCY_CHAR, '')
|
---|
| 2304 | // If there is a spacing between currency character and the value and
|
---|
| 2305 | // the currency character is supressed by passing an empty string, the
|
---|
| 2306 | // spacing character would remain as part of the string. Then we
|
---|
| 2307 | // should remove it.
|
---|
| 2308 | .trim();
|
---|
| 2309 | }
|
---|
| 2310 | /**
|
---|
| 2311 | * @ngModule CommonModule
|
---|
| 2312 | * @description
|
---|
| 2313 | *
|
---|
| 2314 | * Formats a number as a percentage according to locale rules.
|
---|
| 2315 | *
|
---|
| 2316 | * @param value The number to format.
|
---|
| 2317 | * @param locale A locale code for the locale format rules to use.
|
---|
| 2318 | * @param digitsInfo Decimal representation options, specified by a string in the following format:
|
---|
| 2319 | * `{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`. See `DecimalPipe` for more details.
|
---|
| 2320 | *
|
---|
| 2321 | * @returns The formatted percentage value.
|
---|
| 2322 | *
|
---|
| 2323 | * @see `formatNumber()`
|
---|
| 2324 | * @see `DecimalPipe`
|
---|
| 2325 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 2326 | * @publicApi
|
---|
| 2327 | *
|
---|
| 2328 | */
|
---|
| 2329 | function formatPercent(value, locale, digitsInfo) {
|
---|
| 2330 | const format = getLocaleNumberFormat(locale, NumberFormatStyle.Percent);
|
---|
| 2331 | const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign));
|
---|
| 2332 | const res = formatNumberToLocaleString(value, pattern, locale, NumberSymbol.Group, NumberSymbol.Decimal, digitsInfo, true);
|
---|
| 2333 | return res.replace(new RegExp(PERCENT_CHAR, 'g'), getLocaleNumberSymbol(locale, NumberSymbol.PercentSign));
|
---|
| 2334 | }
|
---|
| 2335 | /**
|
---|
| 2336 | * @ngModule CommonModule
|
---|
| 2337 | * @description
|
---|
| 2338 | *
|
---|
| 2339 | * Formats a number as text, with group sizing, separator, and other
|
---|
| 2340 | * parameters based on the locale.
|
---|
| 2341 | *
|
---|
| 2342 | * @param value The number to format.
|
---|
| 2343 | * @param locale A locale code for the locale format rules to use.
|
---|
| 2344 | * @param digitsInfo Decimal representation options, specified by a string in the following format:
|
---|
| 2345 | * `{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}`. See `DecimalPipe` for more details.
|
---|
| 2346 | *
|
---|
| 2347 | * @returns The formatted text string.
|
---|
| 2348 | * @see [Internationalization (i18n) Guide](https://angular.io/guide/i18n-overview)
|
---|
| 2349 | *
|
---|
| 2350 | * @publicApi
|
---|
| 2351 | */
|
---|
| 2352 | function formatNumber(value, locale, digitsInfo) {
|
---|
| 2353 | const format = getLocaleNumberFormat(locale, NumberFormatStyle.Decimal);
|
---|
| 2354 | const pattern = parseNumberFormat(format, getLocaleNumberSymbol(locale, NumberSymbol.MinusSign));
|
---|
| 2355 | return formatNumberToLocaleString(value, pattern, locale, NumberSymbol.Group, NumberSymbol.Decimal, digitsInfo);
|
---|
| 2356 | }
|
---|
| 2357 | function parseNumberFormat(format, minusSign = '-') {
|
---|
| 2358 | const p = {
|
---|
| 2359 | minInt: 1,
|
---|
| 2360 | minFrac: 0,
|
---|
| 2361 | maxFrac: 0,
|
---|
| 2362 | posPre: '',
|
---|
| 2363 | posSuf: '',
|
---|
| 2364 | negPre: '',
|
---|
| 2365 | negSuf: '',
|
---|
| 2366 | gSize: 0,
|
---|
| 2367 | lgSize: 0
|
---|
| 2368 | };
|
---|
| 2369 | const patternParts = format.split(PATTERN_SEP);
|
---|
| 2370 | const positive = patternParts[0];
|
---|
| 2371 | const negative = patternParts[1];
|
---|
| 2372 | const positiveParts = positive.indexOf(DECIMAL_SEP) !== -1 ?
|
---|
| 2373 | positive.split(DECIMAL_SEP) :
|
---|
| 2374 | [
|
---|
| 2375 | positive.substring(0, positive.lastIndexOf(ZERO_CHAR) + 1),
|
---|
| 2376 | positive.substring(positive.lastIndexOf(ZERO_CHAR) + 1)
|
---|
| 2377 | ], integer = positiveParts[0], fraction = positiveParts[1] || '';
|
---|
| 2378 | p.posPre = integer.substr(0, integer.indexOf(DIGIT_CHAR));
|
---|
| 2379 | for (let i = 0; i < fraction.length; i++) {
|
---|
| 2380 | const ch = fraction.charAt(i);
|
---|
| 2381 | if (ch === ZERO_CHAR) {
|
---|
| 2382 | p.minFrac = p.maxFrac = i + 1;
|
---|
| 2383 | }
|
---|
| 2384 | else if (ch === DIGIT_CHAR) {
|
---|
| 2385 | p.maxFrac = i + 1;
|
---|
| 2386 | }
|
---|
| 2387 | else {
|
---|
| 2388 | p.posSuf += ch;
|
---|
| 2389 | }
|
---|
| 2390 | }
|
---|
| 2391 | const groups = integer.split(GROUP_SEP);
|
---|
| 2392 | p.gSize = groups[1] ? groups[1].length : 0;
|
---|
| 2393 | p.lgSize = (groups[2] || groups[1]) ? (groups[2] || groups[1]).length : 0;
|
---|
| 2394 | if (negative) {
|
---|
| 2395 | const trunkLen = positive.length - p.posPre.length - p.posSuf.length, pos = negative.indexOf(DIGIT_CHAR);
|
---|
| 2396 | p.negPre = negative.substr(0, pos).replace(/'/g, '');
|
---|
| 2397 | p.negSuf = negative.substr(pos + trunkLen).replace(/'/g, '');
|
---|
| 2398 | }
|
---|
| 2399 | else {
|
---|
| 2400 | p.negPre = minusSign + p.posPre;
|
---|
| 2401 | p.negSuf = p.posSuf;
|
---|
| 2402 | }
|
---|
| 2403 | return p;
|
---|
| 2404 | }
|
---|
| 2405 | // Transforms a parsed number into a percentage by multiplying it by 100
|
---|
| 2406 | function toPercent(parsedNumber) {
|
---|
| 2407 | // if the number is 0, don't do anything
|
---|
| 2408 | if (parsedNumber.digits[0] === 0) {
|
---|
| 2409 | return parsedNumber;
|
---|
| 2410 | }
|
---|
| 2411 | // Getting the current number of decimals
|
---|
| 2412 | const fractionLen = parsedNumber.digits.length - parsedNumber.integerLen;
|
---|
| 2413 | if (parsedNumber.exponent) {
|
---|
| 2414 | parsedNumber.exponent += 2;
|
---|
| 2415 | }
|
---|
| 2416 | else {
|
---|
| 2417 | if (fractionLen === 0) {
|
---|
| 2418 | parsedNumber.digits.push(0, 0);
|
---|
| 2419 | }
|
---|
| 2420 | else if (fractionLen === 1) {
|
---|
| 2421 | parsedNumber.digits.push(0);
|
---|
| 2422 | }
|
---|
| 2423 | parsedNumber.integerLen += 2;
|
---|
| 2424 | }
|
---|
| 2425 | return parsedNumber;
|
---|
| 2426 | }
|
---|
| 2427 | /**
|
---|
| 2428 | * Parses a number.
|
---|
| 2429 | * Significant bits of this parse algorithm came from https://github.com/MikeMcl/big.js/
|
---|
| 2430 | */
|
---|
| 2431 | function parseNumber(num) {
|
---|
| 2432 | let numStr = Math.abs(num) + '';
|
---|
| 2433 | let exponent = 0, digits, integerLen;
|
---|
| 2434 | let i, j, zeros;
|
---|
| 2435 | // Decimal point?
|
---|
| 2436 | if ((integerLen = numStr.indexOf(DECIMAL_SEP)) > -1) {
|
---|
| 2437 | numStr = numStr.replace(DECIMAL_SEP, '');
|
---|
| 2438 | }
|
---|
| 2439 | // Exponential form?
|
---|
| 2440 | if ((i = numStr.search(/e/i)) > 0) {
|
---|
| 2441 | // Work out the exponent.
|
---|
| 2442 | if (integerLen < 0)
|
---|
| 2443 | integerLen = i;
|
---|
| 2444 | integerLen += +numStr.slice(i + 1);
|
---|
| 2445 | numStr = numStr.substring(0, i);
|
---|
| 2446 | }
|
---|
| 2447 | else if (integerLen < 0) {
|
---|
| 2448 | // There was no decimal point or exponent so it is an integer.
|
---|
| 2449 | integerLen = numStr.length;
|
---|
| 2450 | }
|
---|
| 2451 | // Count the number of leading zeros.
|
---|
| 2452 | for (i = 0; numStr.charAt(i) === ZERO_CHAR; i++) { /* empty */
|
---|
| 2453 | }
|
---|
| 2454 | if (i === (zeros = numStr.length)) {
|
---|
| 2455 | // The digits are all zero.
|
---|
| 2456 | digits = [0];
|
---|
| 2457 | integerLen = 1;
|
---|
| 2458 | }
|
---|
| 2459 | else {
|
---|
| 2460 | // Count the number of trailing zeros
|
---|
| 2461 | zeros--;
|
---|
| 2462 | while (numStr.charAt(zeros) === ZERO_CHAR)
|
---|
| 2463 | zeros--;
|
---|
| 2464 | // Trailing zeros are insignificant so ignore them
|
---|
| 2465 | integerLen -= i;
|
---|
| 2466 | digits = [];
|
---|
| 2467 | // Convert string to array of digits without leading/trailing zeros.
|
---|
| 2468 | for (j = 0; i <= zeros; i++, j++) {
|
---|
| 2469 | digits[j] = Number(numStr.charAt(i));
|
---|
| 2470 | }
|
---|
| 2471 | }
|
---|
| 2472 | // If the number overflows the maximum allowed digits then use an exponent.
|
---|
| 2473 | if (integerLen > MAX_DIGITS) {
|
---|
| 2474 | digits = digits.splice(0, MAX_DIGITS - 1);
|
---|
| 2475 | exponent = integerLen - 1;
|
---|
| 2476 | integerLen = 1;
|
---|
| 2477 | }
|
---|
| 2478 | return { digits, exponent, integerLen };
|
---|
| 2479 | }
|
---|
| 2480 | /**
|
---|
| 2481 | * Round the parsed number to the specified number of decimal places
|
---|
| 2482 | * This function changes the parsedNumber in-place
|
---|
| 2483 | */
|
---|
| 2484 | function roundNumber(parsedNumber, minFrac, maxFrac) {
|
---|
| 2485 | if (minFrac > maxFrac) {
|
---|
| 2486 | throw new Error(`The minimum number of digits after fraction (${minFrac}) is higher than the maximum (${maxFrac}).`);
|
---|
| 2487 | }
|
---|
| 2488 | let digits = parsedNumber.digits;
|
---|
| 2489 | let fractionLen = digits.length - parsedNumber.integerLen;
|
---|
| 2490 | const fractionSize = Math.min(Math.max(minFrac, fractionLen), maxFrac);
|
---|
| 2491 | // The index of the digit to where rounding is to occur
|
---|
| 2492 | let roundAt = fractionSize + parsedNumber.integerLen;
|
---|
| 2493 | let digit = digits[roundAt];
|
---|
| 2494 | if (roundAt > 0) {
|
---|
| 2495 | // Drop fractional digits beyond `roundAt`
|
---|
| 2496 | digits.splice(Math.max(parsedNumber.integerLen, roundAt));
|
---|
| 2497 | // Set non-fractional digits beyond `roundAt` to 0
|
---|
| 2498 | for (let j = roundAt; j < digits.length; j++) {
|
---|
| 2499 | digits[j] = 0;
|
---|
| 2500 | }
|
---|
| 2501 | }
|
---|
| 2502 | else {
|
---|
| 2503 | // We rounded to zero so reset the parsedNumber
|
---|
| 2504 | fractionLen = Math.max(0, fractionLen);
|
---|
| 2505 | parsedNumber.integerLen = 1;
|
---|
| 2506 | digits.length = Math.max(1, roundAt = fractionSize + 1);
|
---|
| 2507 | digits[0] = 0;
|
---|
| 2508 | for (let i = 1; i < roundAt; i++)
|
---|
| 2509 | digits[i] = 0;
|
---|
| 2510 | }
|
---|
| 2511 | if (digit >= 5) {
|
---|
| 2512 | if (roundAt - 1 < 0) {
|
---|
| 2513 | for (let k = 0; k > roundAt; k--) {
|
---|
| 2514 | digits.unshift(0);
|
---|
| 2515 | parsedNumber.integerLen++;
|
---|
| 2516 | }
|
---|
| 2517 | digits.unshift(1);
|
---|
| 2518 | parsedNumber.integerLen++;
|
---|
| 2519 | }
|
---|
| 2520 | else {
|
---|
| 2521 | digits[roundAt - 1]++;
|
---|
| 2522 | }
|
---|
| 2523 | }
|
---|
| 2524 | // Pad out with zeros to get the required fraction length
|
---|
| 2525 | for (; fractionLen < Math.max(0, fractionSize); fractionLen++)
|
---|
| 2526 | digits.push(0);
|
---|
| 2527 | let dropTrailingZeros = fractionSize !== 0;
|
---|
| 2528 | // Minimal length = nb of decimals required + current nb of integers
|
---|
| 2529 | // Any number besides that is optional and can be removed if it's a trailing 0
|
---|
| 2530 | const minLen = minFrac + parsedNumber.integerLen;
|
---|
| 2531 | // Do any carrying, e.g. a digit was rounded up to 10
|
---|
| 2532 | const carry = digits.reduceRight(function (carry, d, i, digits) {
|
---|
| 2533 | d = d + carry;
|
---|
| 2534 | digits[i] = d < 10 ? d : d - 10; // d % 10
|
---|
| 2535 | if (dropTrailingZeros) {
|
---|
| 2536 | // Do not keep meaningless fractional trailing zeros (e.g. 15.52000 --> 15.52)
|
---|
| 2537 | if (digits[i] === 0 && i >= minLen) {
|
---|
| 2538 | digits.pop();
|
---|
| 2539 | }
|
---|
| 2540 | else {
|
---|
| 2541 | dropTrailingZeros = false;
|
---|
| 2542 | }
|
---|
| 2543 | }
|
---|
| 2544 | return d >= 10 ? 1 : 0; // Math.floor(d / 10);
|
---|
| 2545 | }, 0);
|
---|
| 2546 | if (carry) {
|
---|
| 2547 | digits.unshift(carry);
|
---|
| 2548 | parsedNumber.integerLen++;
|
---|
| 2549 | }
|
---|
| 2550 | }
|
---|
| 2551 | function parseIntAutoRadix(text) {
|
---|
| 2552 | const result = parseInt(text);
|
---|
| 2553 | if (isNaN(result)) {
|
---|
| 2554 | throw new Error('Invalid integer literal when parsing ' + text);
|
---|
| 2555 | }
|
---|
| 2556 | return result;
|
---|
| 2557 | }
|
---|
| 2558 |
|
---|
| 2559 | /**
|
---|
| 2560 | * @license
|
---|
| 2561 | * Copyright Google LLC All Rights Reserved.
|
---|
| 2562 | *
|
---|
| 2563 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 2564 | * found in the LICENSE file at https://angular.io/license
|
---|
| 2565 | */
|
---|
| 2566 | /**
|
---|
| 2567 | * @publicApi
|
---|
| 2568 | */
|
---|
| 2569 | class NgLocalization {
|
---|
| 2570 | }
|
---|
| 2571 | /**
|
---|
| 2572 | * Returns the plural category for a given value.
|
---|
| 2573 | * - "=value" when the case exists,
|
---|
| 2574 | * - the plural category otherwise
|
---|
| 2575 | */
|
---|
| 2576 | function getPluralCategory(value, cases, ngLocalization, locale) {
|
---|
| 2577 | let key = `=${value}`;
|
---|
| 2578 | if (cases.indexOf(key) > -1) {
|
---|
| 2579 | return key;
|
---|
| 2580 | }
|
---|
| 2581 | key = ngLocalization.getPluralCategory(value, locale);
|
---|
| 2582 | if (cases.indexOf(key) > -1) {
|
---|
| 2583 | return key;
|
---|
| 2584 | }
|
---|
| 2585 | if (cases.indexOf('other') > -1) {
|
---|
| 2586 | return 'other';
|
---|
| 2587 | }
|
---|
| 2588 | throw new Error(`No plural message found for value "${value}"`);
|
---|
| 2589 | }
|
---|
| 2590 | /**
|
---|
| 2591 | * Returns the plural case based on the locale
|
---|
| 2592 | *
|
---|
| 2593 | * @publicApi
|
---|
| 2594 | */
|
---|
| 2595 | class NgLocaleLocalization extends NgLocalization {
|
---|
| 2596 | constructor(locale) {
|
---|
| 2597 | super();
|
---|
| 2598 | this.locale = locale;
|
---|
| 2599 | }
|
---|
| 2600 | getPluralCategory(value, locale) {
|
---|
| 2601 | const plural = getLocalePluralCase(locale || this.locale)(value);
|
---|
| 2602 | switch (plural) {
|
---|
| 2603 | case Plural.Zero:
|
---|
| 2604 | return 'zero';
|
---|
| 2605 | case Plural.One:
|
---|
| 2606 | return 'one';
|
---|
| 2607 | case Plural.Two:
|
---|
| 2608 | return 'two';
|
---|
| 2609 | case Plural.Few:
|
---|
| 2610 | return 'few';
|
---|
| 2611 | case Plural.Many:
|
---|
| 2612 | return 'many';
|
---|
| 2613 | default:
|
---|
| 2614 | return 'other';
|
---|
| 2615 | }
|
---|
| 2616 | }
|
---|
| 2617 | }
|
---|
| 2618 | NgLocaleLocalization.decorators = [
|
---|
| 2619 | { type: Injectable }
|
---|
| 2620 | ];
|
---|
| 2621 | NgLocaleLocalization.ctorParameters = () => [
|
---|
| 2622 | { type: String, decorators: [{ type: Inject, args: [LOCALE_ID,] }] }
|
---|
| 2623 | ];
|
---|
| 2624 |
|
---|
| 2625 | /**
|
---|
| 2626 | * @license
|
---|
| 2627 | * Copyright Google LLC All Rights Reserved.
|
---|
| 2628 | *
|
---|
| 2629 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 2630 | * found in the LICENSE file at https://angular.io/license
|
---|
| 2631 | */
|
---|
| 2632 | /**
|
---|
| 2633 | * Register global data to be used internally by Angular. See the
|
---|
| 2634 | * ["I18n guide"](guide/i18n-common-format-data-locale) to know how to import additional locale
|
---|
| 2635 | * data.
|
---|
| 2636 | *
|
---|
| 2637 | * The signature registerLocaleData(data: any, extraData?: any) is deprecated since v5.1
|
---|
| 2638 | *
|
---|
| 2639 | * @publicApi
|
---|
| 2640 | */
|
---|
| 2641 | function registerLocaleData(data, localeId, extraData) {
|
---|
| 2642 | return ɵregisterLocaleData(data, localeId, extraData);
|
---|
| 2643 | }
|
---|
| 2644 |
|
---|
| 2645 | /**
|
---|
| 2646 | * @license
|
---|
| 2647 | * Copyright Google LLC All Rights Reserved.
|
---|
| 2648 | *
|
---|
| 2649 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 2650 | * found in the LICENSE file at https://angular.io/license
|
---|
| 2651 | */
|
---|
| 2652 | function parseCookieValue(cookieStr, name) {
|
---|
| 2653 | name = encodeURIComponent(name);
|
---|
| 2654 | for (const cookie of cookieStr.split(';')) {
|
---|
| 2655 | const eqIndex = cookie.indexOf('=');
|
---|
| 2656 | const [cookieName, cookieValue] = eqIndex == -1 ? [cookie, ''] : [cookie.slice(0, eqIndex), cookie.slice(eqIndex + 1)];
|
---|
| 2657 | if (cookieName.trim() === name) {
|
---|
| 2658 | return decodeURIComponent(cookieValue);
|
---|
| 2659 | }
|
---|
| 2660 | }
|
---|
| 2661 | return null;
|
---|
| 2662 | }
|
---|
| 2663 |
|
---|
| 2664 | /**
|
---|
| 2665 | * @license
|
---|
| 2666 | * Copyright Google LLC All Rights Reserved.
|
---|
| 2667 | *
|
---|
| 2668 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 2669 | * found in the LICENSE file at https://angular.io/license
|
---|
| 2670 | */
|
---|
| 2671 | /**
|
---|
| 2672 | * @ngModule CommonModule
|
---|
| 2673 | *
|
---|
| 2674 | * @usageNotes
|
---|
| 2675 | * ```
|
---|
| 2676 | * <some-element [ngClass]="'first second'">...</some-element>
|
---|
| 2677 | *
|
---|
| 2678 | * <some-element [ngClass]="['first', 'second']">...</some-element>
|
---|
| 2679 | *
|
---|
| 2680 | * <some-element [ngClass]="{'first': true, 'second': true, 'third': false}">...</some-element>
|
---|
| 2681 | *
|
---|
| 2682 | * <some-element [ngClass]="stringExp|arrayExp|objExp">...</some-element>
|
---|
| 2683 | *
|
---|
| 2684 | * <some-element [ngClass]="{'class1 class2 class3' : true}">...</some-element>
|
---|
| 2685 | * ```
|
---|
| 2686 | *
|
---|
| 2687 | * @description
|
---|
| 2688 | *
|
---|
| 2689 | * Adds and removes CSS classes on an HTML element.
|
---|
| 2690 | *
|
---|
| 2691 | * The CSS classes are updated as follows, depending on the type of the expression evaluation:
|
---|
| 2692 | * - `string` - the CSS classes listed in the string (space delimited) are added,
|
---|
| 2693 | * - `Array` - the CSS classes declared as Array elements are added,
|
---|
| 2694 | * - `Object` - keys are CSS classes that get added when the expression given in the value
|
---|
| 2695 | * evaluates to a truthy value, otherwise they are removed.
|
---|
| 2696 | *
|
---|
| 2697 | * @publicApi
|
---|
| 2698 | */
|
---|
| 2699 | class NgClass {
|
---|
| 2700 | constructor(_iterableDiffers, _keyValueDiffers, _ngEl, _renderer) {
|
---|
| 2701 | this._iterableDiffers = _iterableDiffers;
|
---|
| 2702 | this._keyValueDiffers = _keyValueDiffers;
|
---|
| 2703 | this._ngEl = _ngEl;
|
---|
| 2704 | this._renderer = _renderer;
|
---|
| 2705 | this._iterableDiffer = null;
|
---|
| 2706 | this._keyValueDiffer = null;
|
---|
| 2707 | this._initialClasses = [];
|
---|
| 2708 | this._rawClass = null;
|
---|
| 2709 | }
|
---|
| 2710 | set klass(value) {
|
---|
| 2711 | this._removeClasses(this._initialClasses);
|
---|
| 2712 | this._initialClasses = typeof value === 'string' ? value.split(/\s+/) : [];
|
---|
| 2713 | this._applyClasses(this._initialClasses);
|
---|
| 2714 | this._applyClasses(this._rawClass);
|
---|
| 2715 | }
|
---|
| 2716 | set ngClass(value) {
|
---|
| 2717 | this._removeClasses(this._rawClass);
|
---|
| 2718 | this._applyClasses(this._initialClasses);
|
---|
| 2719 | this._iterableDiffer = null;
|
---|
| 2720 | this._keyValueDiffer = null;
|
---|
| 2721 | this._rawClass = typeof value === 'string' ? value.split(/\s+/) : value;
|
---|
| 2722 | if (this._rawClass) {
|
---|
| 2723 | if (ɵisListLikeIterable(this._rawClass)) {
|
---|
| 2724 | this._iterableDiffer = this._iterableDiffers.find(this._rawClass).create();
|
---|
| 2725 | }
|
---|
| 2726 | else {
|
---|
| 2727 | this._keyValueDiffer = this._keyValueDiffers.find(this._rawClass).create();
|
---|
| 2728 | }
|
---|
| 2729 | }
|
---|
| 2730 | }
|
---|
| 2731 | ngDoCheck() {
|
---|
| 2732 | if (this._iterableDiffer) {
|
---|
| 2733 | const iterableChanges = this._iterableDiffer.diff(this._rawClass);
|
---|
| 2734 | if (iterableChanges) {
|
---|
| 2735 | this._applyIterableChanges(iterableChanges);
|
---|
| 2736 | }
|
---|
| 2737 | }
|
---|
| 2738 | else if (this._keyValueDiffer) {
|
---|
| 2739 | const keyValueChanges = this._keyValueDiffer.diff(this._rawClass);
|
---|
| 2740 | if (keyValueChanges) {
|
---|
| 2741 | this._applyKeyValueChanges(keyValueChanges);
|
---|
| 2742 | }
|
---|
| 2743 | }
|
---|
| 2744 | }
|
---|
| 2745 | _applyKeyValueChanges(changes) {
|
---|
| 2746 | changes.forEachAddedItem((record) => this._toggleClass(record.key, record.currentValue));
|
---|
| 2747 | changes.forEachChangedItem((record) => this._toggleClass(record.key, record.currentValue));
|
---|
| 2748 | changes.forEachRemovedItem((record) => {
|
---|
| 2749 | if (record.previousValue) {
|
---|
| 2750 | this._toggleClass(record.key, false);
|
---|
| 2751 | }
|
---|
| 2752 | });
|
---|
| 2753 | }
|
---|
| 2754 | _applyIterableChanges(changes) {
|
---|
| 2755 | changes.forEachAddedItem((record) => {
|
---|
| 2756 | if (typeof record.item === 'string') {
|
---|
| 2757 | this._toggleClass(record.item, true);
|
---|
| 2758 | }
|
---|
| 2759 | else {
|
---|
| 2760 | throw new Error(`NgClass can only toggle CSS classes expressed as strings, got ${ɵstringify(record.item)}`);
|
---|
| 2761 | }
|
---|
| 2762 | });
|
---|
| 2763 | changes.forEachRemovedItem((record) => this._toggleClass(record.item, false));
|
---|
| 2764 | }
|
---|
| 2765 | /**
|
---|
| 2766 | * Applies a collection of CSS classes to the DOM element.
|
---|
| 2767 | *
|
---|
| 2768 | * For argument of type Set and Array CSS class names contained in those collections are always
|
---|
| 2769 | * added.
|
---|
| 2770 | * For argument of type Map CSS class name in the map's key is toggled based on the value (added
|
---|
| 2771 | * for truthy and removed for falsy).
|
---|
| 2772 | */
|
---|
| 2773 | _applyClasses(rawClassVal) {
|
---|
| 2774 | if (rawClassVal) {
|
---|
| 2775 | if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) {
|
---|
| 2776 | rawClassVal.forEach((klass) => this._toggleClass(klass, true));
|
---|
| 2777 | }
|
---|
| 2778 | else {
|
---|
| 2779 | Object.keys(rawClassVal).forEach(klass => this._toggleClass(klass, !!rawClassVal[klass]));
|
---|
| 2780 | }
|
---|
| 2781 | }
|
---|
| 2782 | }
|
---|
| 2783 | /**
|
---|
| 2784 | * Removes a collection of CSS classes from the DOM element. This is mostly useful for cleanup
|
---|
| 2785 | * purposes.
|
---|
| 2786 | */
|
---|
| 2787 | _removeClasses(rawClassVal) {
|
---|
| 2788 | if (rawClassVal) {
|
---|
| 2789 | if (Array.isArray(rawClassVal) || rawClassVal instanceof Set) {
|
---|
| 2790 | rawClassVal.forEach((klass) => this._toggleClass(klass, false));
|
---|
| 2791 | }
|
---|
| 2792 | else {
|
---|
| 2793 | Object.keys(rawClassVal).forEach(klass => this._toggleClass(klass, false));
|
---|
| 2794 | }
|
---|
| 2795 | }
|
---|
| 2796 | }
|
---|
| 2797 | _toggleClass(klass, enabled) {
|
---|
| 2798 | klass = klass.trim();
|
---|
| 2799 | if (klass) {
|
---|
| 2800 | klass.split(/\s+/g).forEach(klass => {
|
---|
| 2801 | if (enabled) {
|
---|
| 2802 | this._renderer.addClass(this._ngEl.nativeElement, klass);
|
---|
| 2803 | }
|
---|
| 2804 | else {
|
---|
| 2805 | this._renderer.removeClass(this._ngEl.nativeElement, klass);
|
---|
| 2806 | }
|
---|
| 2807 | });
|
---|
| 2808 | }
|
---|
| 2809 | }
|
---|
| 2810 | }
|
---|
| 2811 | NgClass.decorators = [
|
---|
| 2812 | { type: Directive, args: [{ selector: '[ngClass]' },] }
|
---|
| 2813 | ];
|
---|
| 2814 | NgClass.ctorParameters = () => [
|
---|
| 2815 | { type: IterableDiffers },
|
---|
| 2816 | { type: KeyValueDiffers },
|
---|
| 2817 | { type: ElementRef },
|
---|
| 2818 | { type: Renderer2 }
|
---|
| 2819 | ];
|
---|
| 2820 | NgClass.propDecorators = {
|
---|
| 2821 | klass: [{ type: Input, args: ['class',] }],
|
---|
| 2822 | ngClass: [{ type: Input, args: ['ngClass',] }]
|
---|
| 2823 | };
|
---|
| 2824 |
|
---|
| 2825 | /**
|
---|
| 2826 | * @license
|
---|
| 2827 | * Copyright Google LLC All Rights Reserved.
|
---|
| 2828 | *
|
---|
| 2829 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 2830 | * found in the LICENSE file at https://angular.io/license
|
---|
| 2831 | */
|
---|
| 2832 | /**
|
---|
| 2833 | * Instantiates a {@link Component} type and inserts its Host View into the current View.
|
---|
| 2834 | * `NgComponentOutlet` provides a declarative approach for dynamic component creation.
|
---|
| 2835 | *
|
---|
| 2836 | * `NgComponentOutlet` requires a component type, if a falsy value is set the view will clear and
|
---|
| 2837 | * any existing component will be destroyed.
|
---|
| 2838 | *
|
---|
| 2839 | * @usageNotes
|
---|
| 2840 | *
|
---|
| 2841 | * ### Fine tune control
|
---|
| 2842 | *
|
---|
| 2843 | * You can control the component creation process by using the following optional attributes:
|
---|
| 2844 | *
|
---|
| 2845 | * * `ngComponentOutletInjector`: Optional custom {@link Injector} that will be used as parent for
|
---|
| 2846 | * the Component. Defaults to the injector of the current view container.
|
---|
| 2847 | *
|
---|
| 2848 | * * `ngComponentOutletContent`: Optional list of projectable nodes to insert into the content
|
---|
| 2849 | * section of the component, if it exists.
|
---|
| 2850 | *
|
---|
| 2851 | * * `ngComponentOutletNgModuleFactory`: Optional module factory to allow loading another
|
---|
| 2852 | * module dynamically, then loading a component from that module.
|
---|
| 2853 | *
|
---|
| 2854 | * ### Syntax
|
---|
| 2855 | *
|
---|
| 2856 | * Simple
|
---|
| 2857 | * ```
|
---|
| 2858 | * <ng-container *ngComponentOutlet="componentTypeExpression"></ng-container>
|
---|
| 2859 | * ```
|
---|
| 2860 | *
|
---|
| 2861 | * Customized injector/content
|
---|
| 2862 | * ```
|
---|
| 2863 | * <ng-container *ngComponentOutlet="componentTypeExpression;
|
---|
| 2864 | * injector: injectorExpression;
|
---|
| 2865 | * content: contentNodesExpression;">
|
---|
| 2866 | * </ng-container>
|
---|
| 2867 | * ```
|
---|
| 2868 | *
|
---|
| 2869 | * Customized ngModuleFactory
|
---|
| 2870 | * ```
|
---|
| 2871 | * <ng-container *ngComponentOutlet="componentTypeExpression;
|
---|
| 2872 | * ngModuleFactory: moduleFactory;">
|
---|
| 2873 | * </ng-container>
|
---|
| 2874 | * ```
|
---|
| 2875 | *
|
---|
| 2876 | * ### A simple example
|
---|
| 2877 | *
|
---|
| 2878 | * {@example common/ngComponentOutlet/ts/module.ts region='SimpleExample'}
|
---|
| 2879 | *
|
---|
| 2880 | * A more complete example with additional options:
|
---|
| 2881 | *
|
---|
| 2882 | * {@example common/ngComponentOutlet/ts/module.ts region='CompleteExample'}
|
---|
| 2883 | *
|
---|
| 2884 | * @publicApi
|
---|
| 2885 | * @ngModule CommonModule
|
---|
| 2886 | */
|
---|
| 2887 | class NgComponentOutlet {
|
---|
| 2888 | constructor(_viewContainerRef) {
|
---|
| 2889 | this._viewContainerRef = _viewContainerRef;
|
---|
| 2890 | this._componentRef = null;
|
---|
| 2891 | this._moduleRef = null;
|
---|
| 2892 | }
|
---|
| 2893 | ngOnChanges(changes) {
|
---|
| 2894 | this._viewContainerRef.clear();
|
---|
| 2895 | this._componentRef = null;
|
---|
| 2896 | if (this.ngComponentOutlet) {
|
---|
| 2897 | const elInjector = this.ngComponentOutletInjector || this._viewContainerRef.parentInjector;
|
---|
| 2898 | if (changes['ngComponentOutletNgModuleFactory']) {
|
---|
| 2899 | if (this._moduleRef)
|
---|
| 2900 | this._moduleRef.destroy();
|
---|
| 2901 | if (this.ngComponentOutletNgModuleFactory) {
|
---|
| 2902 | const parentModule = elInjector.get(NgModuleRef);
|
---|
| 2903 | this._moduleRef = this.ngComponentOutletNgModuleFactory.create(parentModule.injector);
|
---|
| 2904 | }
|
---|
| 2905 | else {
|
---|
| 2906 | this._moduleRef = null;
|
---|
| 2907 | }
|
---|
| 2908 | }
|
---|
| 2909 | const componentFactoryResolver = this._moduleRef ? this._moduleRef.componentFactoryResolver :
|
---|
| 2910 | elInjector.get(ComponentFactoryResolver);
|
---|
| 2911 | const componentFactory = componentFactoryResolver.resolveComponentFactory(this.ngComponentOutlet);
|
---|
| 2912 | this._componentRef = this._viewContainerRef.createComponent(componentFactory, this._viewContainerRef.length, elInjector, this.ngComponentOutletContent);
|
---|
| 2913 | }
|
---|
| 2914 | }
|
---|
| 2915 | ngOnDestroy() {
|
---|
| 2916 | if (this._moduleRef)
|
---|
| 2917 | this._moduleRef.destroy();
|
---|
| 2918 | }
|
---|
| 2919 | }
|
---|
| 2920 | NgComponentOutlet.decorators = [
|
---|
| 2921 | { type: Directive, args: [{ selector: '[ngComponentOutlet]' },] }
|
---|
| 2922 | ];
|
---|
| 2923 | NgComponentOutlet.ctorParameters = () => [
|
---|
| 2924 | { type: ViewContainerRef }
|
---|
| 2925 | ];
|
---|
| 2926 | NgComponentOutlet.propDecorators = {
|
---|
| 2927 | ngComponentOutlet: [{ type: Input }],
|
---|
| 2928 | ngComponentOutletInjector: [{ type: Input }],
|
---|
| 2929 | ngComponentOutletContent: [{ type: Input }],
|
---|
| 2930 | ngComponentOutletNgModuleFactory: [{ type: Input }]
|
---|
| 2931 | };
|
---|
| 2932 |
|
---|
| 2933 | /**
|
---|
| 2934 | * @license
|
---|
| 2935 | * Copyright Google LLC All Rights Reserved.
|
---|
| 2936 | *
|
---|
| 2937 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 2938 | * found in the LICENSE file at https://angular.io/license
|
---|
| 2939 | */
|
---|
| 2940 | /**
|
---|
| 2941 | * @publicApi
|
---|
| 2942 | */
|
---|
| 2943 | class NgForOfContext {
|
---|
| 2944 | constructor($implicit, ngForOf, index, count) {
|
---|
| 2945 | this.$implicit = $implicit;
|
---|
| 2946 | this.ngForOf = ngForOf;
|
---|
| 2947 | this.index = index;
|
---|
| 2948 | this.count = count;
|
---|
| 2949 | }
|
---|
| 2950 | get first() {
|
---|
| 2951 | return this.index === 0;
|
---|
| 2952 | }
|
---|
| 2953 | get last() {
|
---|
| 2954 | return this.index === this.count - 1;
|
---|
| 2955 | }
|
---|
| 2956 | get even() {
|
---|
| 2957 | return this.index % 2 === 0;
|
---|
| 2958 | }
|
---|
| 2959 | get odd() {
|
---|
| 2960 | return !this.even;
|
---|
| 2961 | }
|
---|
| 2962 | }
|
---|
| 2963 | /**
|
---|
| 2964 | * A [structural directive](guide/structural-directives) that renders
|
---|
| 2965 | * a template for each item in a collection.
|
---|
| 2966 | * The directive is placed on an element, which becomes the parent
|
---|
| 2967 | * of the cloned templates.
|
---|
| 2968 | *
|
---|
| 2969 | * The `ngForOf` directive is generally used in the
|
---|
| 2970 | * [shorthand form](guide/structural-directives#asterisk) `*ngFor`.
|
---|
| 2971 | * In this form, the template to be rendered for each iteration is the content
|
---|
| 2972 | * of an anchor element containing the directive.
|
---|
| 2973 | *
|
---|
| 2974 | * The following example shows the shorthand syntax with some options,
|
---|
| 2975 | * contained in an `<li>` element.
|
---|
| 2976 | *
|
---|
| 2977 | * ```
|
---|
| 2978 | * <li *ngFor="let item of items; index as i; trackBy: trackByFn">...</li>
|
---|
| 2979 | * ```
|
---|
| 2980 | *
|
---|
| 2981 | * The shorthand form expands into a long form that uses the `ngForOf` selector
|
---|
| 2982 | * on an `<ng-template>` element.
|
---|
| 2983 | * The content of the `<ng-template>` element is the `<li>` element that held the
|
---|
| 2984 | * short-form directive.
|
---|
| 2985 | *
|
---|
| 2986 | * Here is the expanded version of the short-form example.
|
---|
| 2987 | *
|
---|
| 2988 | * ```
|
---|
| 2989 | * <ng-template ngFor let-item [ngForOf]="items" let-i="index" [ngForTrackBy]="trackByFn">
|
---|
| 2990 | * <li>...</li>
|
---|
| 2991 | * </ng-template>
|
---|
| 2992 | * ```
|
---|
| 2993 | *
|
---|
| 2994 | * Angular automatically expands the shorthand syntax as it compiles the template.
|
---|
| 2995 | * The context for each embedded view is logically merged to the current component
|
---|
| 2996 | * context according to its lexical position.
|
---|
| 2997 | *
|
---|
| 2998 | * When using the shorthand syntax, Angular allows only [one structural directive
|
---|
| 2999 | * on an element](guide/built-in-directives#one-per-element).
|
---|
| 3000 | * If you want to iterate conditionally, for example,
|
---|
| 3001 | * put the `*ngIf` on a container element that wraps the `*ngFor` element.
|
---|
| 3002 | * For futher discussion, see
|
---|
| 3003 | * [Structural Directives](guide/built-in-directives#one-per-element).
|
---|
| 3004 | *
|
---|
| 3005 | * @usageNotes
|
---|
| 3006 | *
|
---|
| 3007 | * ### Local variables
|
---|
| 3008 | *
|
---|
| 3009 | * `NgForOf` provides exported values that can be aliased to local variables.
|
---|
| 3010 | * For example:
|
---|
| 3011 | *
|
---|
| 3012 | * ```
|
---|
| 3013 | * <li *ngFor="let user of users; index as i; first as isFirst">
|
---|
| 3014 | * {{i}}/{{users.length}}. {{user}} <span *ngIf="isFirst">default</span>
|
---|
| 3015 | * </li>
|
---|
| 3016 | * ```
|
---|
| 3017 | *
|
---|
| 3018 | * The following exported values can be aliased to local variables:
|
---|
| 3019 | *
|
---|
| 3020 | * - `$implicit: T`: The value of the individual items in the iterable (`ngForOf`).
|
---|
| 3021 | * - `ngForOf: NgIterable<T>`: The value of the iterable expression. Useful when the expression is
|
---|
| 3022 | * more complex then a property access, for example when using the async pipe (`userStreams |
|
---|
| 3023 | * async`).
|
---|
| 3024 | * - `index: number`: The index of the current item in the iterable.
|
---|
| 3025 | * - `count: number`: The length of the iterable.
|
---|
| 3026 | * - `first: boolean`: True when the item is the first item in the iterable.
|
---|
| 3027 | * - `last: boolean`: True when the item is the last item in the iterable.
|
---|
| 3028 | * - `even: boolean`: True when the item has an even index in the iterable.
|
---|
| 3029 | * - `odd: boolean`: True when the item has an odd index in the iterable.
|
---|
| 3030 | *
|
---|
| 3031 | * ### Change propagation
|
---|
| 3032 | *
|
---|
| 3033 | * When the contents of the iterator changes, `NgForOf` makes the corresponding changes to the DOM:
|
---|
| 3034 | *
|
---|
| 3035 | * * When an item is added, a new instance of the template is added to the DOM.
|
---|
| 3036 | * * When an item is removed, its template instance is removed from the DOM.
|
---|
| 3037 | * * When items are reordered, their respective templates are reordered in the DOM.
|
---|
| 3038 | *
|
---|
| 3039 | * Angular uses object identity to track insertions and deletions within the iterator and reproduce
|
---|
| 3040 | * those changes in the DOM. This has important implications for animations and any stateful
|
---|
| 3041 | * controls that are present, such as `<input>` elements that accept user input. Inserted rows can
|
---|
| 3042 | * be animated in, deleted rows can be animated out, and unchanged rows retain any unsaved state
|
---|
| 3043 | * such as user input.
|
---|
| 3044 | * For more on animations, see [Transitions and Triggers](guide/transition-and-triggers).
|
---|
| 3045 | *
|
---|
| 3046 | * The identities of elements in the iterator can change while the data does not.
|
---|
| 3047 | * This can happen, for example, if the iterator is produced from an RPC to the server, and that
|
---|
| 3048 | * RPC is re-run. Even if the data hasn't changed, the second response produces objects with
|
---|
| 3049 | * different identities, and Angular must tear down the entire DOM and rebuild it (as if all old
|
---|
| 3050 | * elements were deleted and all new elements inserted).
|
---|
| 3051 | *
|
---|
| 3052 | * To avoid this expensive operation, you can customize the default tracking algorithm.
|
---|
| 3053 | * by supplying the `trackBy` option to `NgForOf`.
|
---|
| 3054 | * `trackBy` takes a function that has two arguments: `index` and `item`.
|
---|
| 3055 | * If `trackBy` is given, Angular tracks changes by the return value of the function.
|
---|
| 3056 | *
|
---|
| 3057 | * @see [Structural Directives](guide/structural-directives)
|
---|
| 3058 | * @ngModule CommonModule
|
---|
| 3059 | * @publicApi
|
---|
| 3060 | */
|
---|
| 3061 | class NgForOf {
|
---|
| 3062 | constructor(_viewContainer, _template, _differs) {
|
---|
| 3063 | this._viewContainer = _viewContainer;
|
---|
| 3064 | this._template = _template;
|
---|
| 3065 | this._differs = _differs;
|
---|
| 3066 | this._ngForOf = null;
|
---|
| 3067 | this._ngForOfDirty = true;
|
---|
| 3068 | this._differ = null;
|
---|
| 3069 | }
|
---|
| 3070 | /**
|
---|
| 3071 | * The value of the iterable expression, which can be used as a
|
---|
| 3072 | * [template input variable](guide/structural-directives#shorthand).
|
---|
| 3073 | */
|
---|
| 3074 | set ngForOf(ngForOf) {
|
---|
| 3075 | this._ngForOf = ngForOf;
|
---|
| 3076 | this._ngForOfDirty = true;
|
---|
| 3077 | }
|
---|
| 3078 | /**
|
---|
| 3079 | * Specifies a custom `TrackByFunction` to compute the identity of items in an iterable.
|
---|
| 3080 | *
|
---|
| 3081 | * If a custom `TrackByFunction` is not provided, `NgForOf` will use the item's [object
|
---|
| 3082 | * identity](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is)
|
---|
| 3083 | * as the key.
|
---|
| 3084 | *
|
---|
| 3085 | * `NgForOf` uses the computed key to associate items in an iterable with DOM elements
|
---|
| 3086 | * it produces for these items.
|
---|
| 3087 | *
|
---|
| 3088 | * A custom `TrackByFunction` is useful to provide good user experience in cases when items in an
|
---|
| 3089 | * iterable rendered using `NgForOf` have a natural identifier (for example, custom ID or a
|
---|
| 3090 | * primary key), and this iterable could be updated with new object instances that still
|
---|
| 3091 | * represent the same underlying entity (for example, when data is re-fetched from the server,
|
---|
| 3092 | * and the iterable is recreated and re-rendered, but most of the data is still the same).
|
---|
| 3093 | *
|
---|
| 3094 | * @see `TrackByFunction`
|
---|
| 3095 | */
|
---|
| 3096 | set ngForTrackBy(fn) {
|
---|
| 3097 | if ((typeof ngDevMode === 'undefined' || ngDevMode) && fn != null && typeof fn !== 'function') {
|
---|
| 3098 | // TODO(vicb): use a log service once there is a public one available
|
---|
| 3099 | if (console && console.warn) {
|
---|
| 3100 | console.warn(`trackBy must be a function, but received ${JSON.stringify(fn)}. ` +
|
---|
| 3101 | `See https://angular.io/api/common/NgForOf#change-propagation for more information.`);
|
---|
| 3102 | }
|
---|
| 3103 | }
|
---|
| 3104 | this._trackByFn = fn;
|
---|
| 3105 | }
|
---|
| 3106 | get ngForTrackBy() {
|
---|
| 3107 | return this._trackByFn;
|
---|
| 3108 | }
|
---|
| 3109 | /**
|
---|
| 3110 | * A reference to the template that is stamped out for each item in the iterable.
|
---|
| 3111 | * @see [template reference variable](guide/template-reference-variables)
|
---|
| 3112 | */
|
---|
| 3113 | set ngForTemplate(value) {
|
---|
| 3114 | // TODO(TS2.1): make TemplateRef<Partial<NgForRowOf<T>>> once we move to TS v2.1
|
---|
| 3115 | // The current type is too restrictive; a template that just uses index, for example,
|
---|
| 3116 | // should be acceptable.
|
---|
| 3117 | if (value) {
|
---|
| 3118 | this._template = value;
|
---|
| 3119 | }
|
---|
| 3120 | }
|
---|
| 3121 | /**
|
---|
| 3122 | * Applies the changes when needed.
|
---|
| 3123 | */
|
---|
| 3124 | ngDoCheck() {
|
---|
| 3125 | if (this._ngForOfDirty) {
|
---|
| 3126 | this._ngForOfDirty = false;
|
---|
| 3127 | // React on ngForOf changes only once all inputs have been initialized
|
---|
| 3128 | const value = this._ngForOf;
|
---|
| 3129 | if (!this._differ && value) {
|
---|
| 3130 | try {
|
---|
| 3131 | this._differ = this._differs.find(value).create(this.ngForTrackBy);
|
---|
| 3132 | }
|
---|
| 3133 | catch (_a) {
|
---|
| 3134 | throw new Error(`Cannot find a differ supporting object '${value}' of type '${getTypeName(value)}'. NgFor only supports binding to Iterables such as Arrays.`);
|
---|
| 3135 | }
|
---|
| 3136 | }
|
---|
| 3137 | }
|
---|
| 3138 | if (this._differ) {
|
---|
| 3139 | const changes = this._differ.diff(this._ngForOf);
|
---|
| 3140 | if (changes)
|
---|
| 3141 | this._applyChanges(changes);
|
---|
| 3142 | }
|
---|
| 3143 | }
|
---|
| 3144 | _applyChanges(changes) {
|
---|
| 3145 | const insertTuples = [];
|
---|
| 3146 | changes.forEachOperation((item, adjustedPreviousIndex, currentIndex) => {
|
---|
| 3147 | if (item.previousIndex == null) {
|
---|
| 3148 | // NgForOf is never "null" or "undefined" here because the differ detected
|
---|
| 3149 | // that a new item needs to be inserted from the iterable. This implies that
|
---|
| 3150 | // there is an iterable value for "_ngForOf".
|
---|
| 3151 | const view = this._viewContainer.createEmbeddedView(this._template, new NgForOfContext(null, this._ngForOf, -1, -1), currentIndex === null ? undefined : currentIndex);
|
---|
| 3152 | const tuple = new RecordViewTuple(item, view);
|
---|
| 3153 | insertTuples.push(tuple);
|
---|
| 3154 | }
|
---|
| 3155 | else if (currentIndex == null) {
|
---|
| 3156 | this._viewContainer.remove(adjustedPreviousIndex === null ? undefined : adjustedPreviousIndex);
|
---|
| 3157 | }
|
---|
| 3158 | else if (adjustedPreviousIndex !== null) {
|
---|
| 3159 | const view = this._viewContainer.get(adjustedPreviousIndex);
|
---|
| 3160 | this._viewContainer.move(view, currentIndex);
|
---|
| 3161 | const tuple = new RecordViewTuple(item, view);
|
---|
| 3162 | insertTuples.push(tuple);
|
---|
| 3163 | }
|
---|
| 3164 | });
|
---|
| 3165 | for (let i = 0; i < insertTuples.length; i++) {
|
---|
| 3166 | this._perViewChange(insertTuples[i].view, insertTuples[i].record);
|
---|
| 3167 | }
|
---|
| 3168 | for (let i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
|
---|
| 3169 | const viewRef = this._viewContainer.get(i);
|
---|
| 3170 | viewRef.context.index = i;
|
---|
| 3171 | viewRef.context.count = ilen;
|
---|
| 3172 | viewRef.context.ngForOf = this._ngForOf;
|
---|
| 3173 | }
|
---|
| 3174 | changes.forEachIdentityChange((record) => {
|
---|
| 3175 | const viewRef = this._viewContainer.get(record.currentIndex);
|
---|
| 3176 | viewRef.context.$implicit = record.item;
|
---|
| 3177 | });
|
---|
| 3178 | }
|
---|
| 3179 | _perViewChange(view, record) {
|
---|
| 3180 | view.context.$implicit = record.item;
|
---|
| 3181 | }
|
---|
| 3182 | /**
|
---|
| 3183 | * Asserts the correct type of the context for the template that `NgForOf` will render.
|
---|
| 3184 | *
|
---|
| 3185 | * The presence of this method is a signal to the Ivy template type-check compiler that the
|
---|
| 3186 | * `NgForOf` structural directive renders its template with a specific context type.
|
---|
| 3187 | */
|
---|
| 3188 | static ngTemplateContextGuard(dir, ctx) {
|
---|
| 3189 | return true;
|
---|
| 3190 | }
|
---|
| 3191 | }
|
---|
| 3192 | NgForOf.decorators = [
|
---|
| 3193 | { type: Directive, args: [{ selector: '[ngFor][ngForOf]' },] }
|
---|
| 3194 | ];
|
---|
| 3195 | NgForOf.ctorParameters = () => [
|
---|
| 3196 | { type: ViewContainerRef },
|
---|
| 3197 | { type: TemplateRef },
|
---|
| 3198 | { type: IterableDiffers }
|
---|
| 3199 | ];
|
---|
| 3200 | NgForOf.propDecorators = {
|
---|
| 3201 | ngForOf: [{ type: Input }],
|
---|
| 3202 | ngForTrackBy: [{ type: Input }],
|
---|
| 3203 | ngForTemplate: [{ type: Input }]
|
---|
| 3204 | };
|
---|
| 3205 | class RecordViewTuple {
|
---|
| 3206 | constructor(record, view) {
|
---|
| 3207 | this.record = record;
|
---|
| 3208 | this.view = view;
|
---|
| 3209 | }
|
---|
| 3210 | }
|
---|
| 3211 | function getTypeName(type) {
|
---|
| 3212 | return type['name'] || typeof type;
|
---|
| 3213 | }
|
---|
| 3214 |
|
---|
| 3215 | /**
|
---|
| 3216 | * @license
|
---|
| 3217 | * Copyright Google LLC All Rights Reserved.
|
---|
| 3218 | *
|
---|
| 3219 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 3220 | * found in the LICENSE file at https://angular.io/license
|
---|
| 3221 | */
|
---|
| 3222 | /**
|
---|
| 3223 | * A structural directive that conditionally includes a template based on the value of
|
---|
| 3224 | * an expression coerced to Boolean.
|
---|
| 3225 | * When the expression evaluates to true, Angular renders the template
|
---|
| 3226 | * provided in a `then` clause, and when false or null,
|
---|
| 3227 | * Angular renders the template provided in an optional `else` clause. The default
|
---|
| 3228 | * template for the `else` clause is blank.
|
---|
| 3229 | *
|
---|
| 3230 | * A [shorthand form](guide/structural-directives#asterisk) of the directive,
|
---|
| 3231 | * `*ngIf="condition"`, is generally used, provided
|
---|
| 3232 | * as an attribute of the anchor element for the inserted template.
|
---|
| 3233 | * Angular expands this into a more explicit version, in which the anchor element
|
---|
| 3234 | * is contained in an `<ng-template>` element.
|
---|
| 3235 | *
|
---|
| 3236 | * Simple form with shorthand syntax:
|
---|
| 3237 | *
|
---|
| 3238 | * ```
|
---|
| 3239 | * <div *ngIf="condition">Content to render when condition is true.</div>
|
---|
| 3240 | * ```
|
---|
| 3241 | *
|
---|
| 3242 | * Simple form with expanded syntax:
|
---|
| 3243 | *
|
---|
| 3244 | * ```
|
---|
| 3245 | * <ng-template [ngIf]="condition"><div>Content to render when condition is
|
---|
| 3246 | * true.</div></ng-template>
|
---|
| 3247 | * ```
|
---|
| 3248 | *
|
---|
| 3249 | * Form with an "else" block:
|
---|
| 3250 | *
|
---|
| 3251 | * ```
|
---|
| 3252 | * <div *ngIf="condition; else elseBlock">Content to render when condition is true.</div>
|
---|
| 3253 | * <ng-template #elseBlock>Content to render when condition is false.</ng-template>
|
---|
| 3254 | * ```
|
---|
| 3255 | *
|
---|
| 3256 | * Shorthand form with "then" and "else" blocks:
|
---|
| 3257 | *
|
---|
| 3258 | * ```
|
---|
| 3259 | * <div *ngIf="condition; then thenBlock else elseBlock"></div>
|
---|
| 3260 | * <ng-template #thenBlock>Content to render when condition is true.</ng-template>
|
---|
| 3261 | * <ng-template #elseBlock>Content to render when condition is false.</ng-template>
|
---|
| 3262 | * ```
|
---|
| 3263 | *
|
---|
| 3264 | * Form with storing the value locally:
|
---|
| 3265 | *
|
---|
| 3266 | * ```
|
---|
| 3267 | * <div *ngIf="condition as value; else elseBlock">{{value}}</div>
|
---|
| 3268 | * <ng-template #elseBlock>Content to render when value is null.</ng-template>
|
---|
| 3269 | * ```
|
---|
| 3270 | *
|
---|
| 3271 | * @usageNotes
|
---|
| 3272 | *
|
---|
| 3273 | * The `*ngIf` directive is most commonly used to conditionally show an inline template,
|
---|
| 3274 | * as seen in the following example.
|
---|
| 3275 | * The default `else` template is blank.
|
---|
| 3276 | *
|
---|
| 3277 | * {@example common/ngIf/ts/module.ts region='NgIfSimple'}
|
---|
| 3278 | *
|
---|
| 3279 | * ### Showing an alternative template using `else`
|
---|
| 3280 | *
|
---|
| 3281 | * To display a template when `expression` evaluates to false, use an `else` template
|
---|
| 3282 | * binding as shown in the following example.
|
---|
| 3283 | * The `else` binding points to an `<ng-template>` element labeled `#elseBlock`.
|
---|
| 3284 | * The template can be defined anywhere in the component view, but is typically placed right after
|
---|
| 3285 | * `ngIf` for readability.
|
---|
| 3286 | *
|
---|
| 3287 | * {@example common/ngIf/ts/module.ts region='NgIfElse'}
|
---|
| 3288 | *
|
---|
| 3289 | * ### Using an external `then` template
|
---|
| 3290 | *
|
---|
| 3291 | * In the previous example, the then-clause template is specified inline, as the content of the
|
---|
| 3292 | * tag that contains the `ngIf` directive. You can also specify a template that is defined
|
---|
| 3293 | * externally, by referencing a labeled `<ng-template>` element. When you do this, you can
|
---|
| 3294 | * change which template to use at runtime, as shown in the following example.
|
---|
| 3295 | *
|
---|
| 3296 | * {@example common/ngIf/ts/module.ts region='NgIfThenElse'}
|
---|
| 3297 | *
|
---|
| 3298 | * ### Storing a conditional result in a variable
|
---|
| 3299 | *
|
---|
| 3300 | * You might want to show a set of properties from the same object. If you are waiting
|
---|
| 3301 | * for asynchronous data, the object can be undefined.
|
---|
| 3302 | * In this case, you can use `ngIf` and store the result of the condition in a local
|
---|
| 3303 | * variable as shown in the following example.
|
---|
| 3304 | *
|
---|
| 3305 | * {@example common/ngIf/ts/module.ts region='NgIfAs'}
|
---|
| 3306 | *
|
---|
| 3307 | * This code uses only one `AsyncPipe`, so only one subscription is created.
|
---|
| 3308 | * The conditional statement stores the result of `userStream|async` in the local variable `user`.
|
---|
| 3309 | * You can then bind the local `user` repeatedly.
|
---|
| 3310 | *
|
---|
| 3311 | * The conditional displays the data only if `userStream` returns a value,
|
---|
| 3312 | * so you don't need to use the
|
---|
| 3313 | * safe-navigation-operator (`?.`)
|
---|
| 3314 | * to guard against null values when accessing properties.
|
---|
| 3315 | * You can display an alternative template while waiting for the data.
|
---|
| 3316 | *
|
---|
| 3317 | * ### Shorthand syntax
|
---|
| 3318 | *
|
---|
| 3319 | * The shorthand syntax `*ngIf` expands into two separate template specifications
|
---|
| 3320 | * for the "then" and "else" clauses. For example, consider the following shorthand statement,
|
---|
| 3321 | * that is meant to show a loading page while waiting for data to be loaded.
|
---|
| 3322 | *
|
---|
| 3323 | * ```
|
---|
| 3324 | * <div class="hero-list" *ngIf="heroes else loading">
|
---|
| 3325 | * ...
|
---|
| 3326 | * </div>
|
---|
| 3327 | *
|
---|
| 3328 | * <ng-template #loading>
|
---|
| 3329 | * <div>Loading...</div>
|
---|
| 3330 | * </ng-template>
|
---|
| 3331 | * ```
|
---|
| 3332 | *
|
---|
| 3333 | * You can see that the "else" clause references the `<ng-template>`
|
---|
| 3334 | * with the `#loading` label, and the template for the "then" clause
|
---|
| 3335 | * is provided as the content of the anchor element.
|
---|
| 3336 | *
|
---|
| 3337 | * However, when Angular expands the shorthand syntax, it creates
|
---|
| 3338 | * another `<ng-template>` tag, with `ngIf` and `ngIfElse` directives.
|
---|
| 3339 | * The anchor element containing the template for the "then" clause becomes
|
---|
| 3340 | * the content of this unlabeled `<ng-template>` tag.
|
---|
| 3341 | *
|
---|
| 3342 | * ```
|
---|
| 3343 | * <ng-template [ngIf]="heroes" [ngIfElse]="loading">
|
---|
| 3344 | * <div class="hero-list">
|
---|
| 3345 | * ...
|
---|
| 3346 | * </div>
|
---|
| 3347 | * </ng-template>
|
---|
| 3348 | *
|
---|
| 3349 | * <ng-template #loading>
|
---|
| 3350 | * <div>Loading...</div>
|
---|
| 3351 | * </ng-template>
|
---|
| 3352 | * ```
|
---|
| 3353 | *
|
---|
| 3354 | * The presence of the implicit template object has implications for the nesting of
|
---|
| 3355 | * structural directives. For more on this subject, see
|
---|
| 3356 | * [Structural Directives](https://angular.io/guide/built-in-directives#one-per-element).
|
---|
| 3357 | *
|
---|
| 3358 | * @ngModule CommonModule
|
---|
| 3359 | * @publicApi
|
---|
| 3360 | */
|
---|
| 3361 | class NgIf {
|
---|
| 3362 | constructor(_viewContainer, templateRef) {
|
---|
| 3363 | this._viewContainer = _viewContainer;
|
---|
| 3364 | this._context = new NgIfContext();
|
---|
| 3365 | this._thenTemplateRef = null;
|
---|
| 3366 | this._elseTemplateRef = null;
|
---|
| 3367 | this._thenViewRef = null;
|
---|
| 3368 | this._elseViewRef = null;
|
---|
| 3369 | this._thenTemplateRef = templateRef;
|
---|
| 3370 | }
|
---|
| 3371 | /**
|
---|
| 3372 | * The Boolean expression to evaluate as the condition for showing a template.
|
---|
| 3373 | */
|
---|
| 3374 | set ngIf(condition) {
|
---|
| 3375 | this._context.$implicit = this._context.ngIf = condition;
|
---|
| 3376 | this._updateView();
|
---|
| 3377 | }
|
---|
| 3378 | /**
|
---|
| 3379 | * A template to show if the condition expression evaluates to true.
|
---|
| 3380 | */
|
---|
| 3381 | set ngIfThen(templateRef) {
|
---|
| 3382 | assertTemplate('ngIfThen', templateRef);
|
---|
| 3383 | this._thenTemplateRef = templateRef;
|
---|
| 3384 | this._thenViewRef = null; // clear previous view if any.
|
---|
| 3385 | this._updateView();
|
---|
| 3386 | }
|
---|
| 3387 | /**
|
---|
| 3388 | * A template to show if the condition expression evaluates to false.
|
---|
| 3389 | */
|
---|
| 3390 | set ngIfElse(templateRef) {
|
---|
| 3391 | assertTemplate('ngIfElse', templateRef);
|
---|
| 3392 | this._elseTemplateRef = templateRef;
|
---|
| 3393 | this._elseViewRef = null; // clear previous view if any.
|
---|
| 3394 | this._updateView();
|
---|
| 3395 | }
|
---|
| 3396 | _updateView() {
|
---|
| 3397 | if (this._context.$implicit) {
|
---|
| 3398 | if (!this._thenViewRef) {
|
---|
| 3399 | this._viewContainer.clear();
|
---|
| 3400 | this._elseViewRef = null;
|
---|
| 3401 | if (this._thenTemplateRef) {
|
---|
| 3402 | this._thenViewRef =
|
---|
| 3403 | this._viewContainer.createEmbeddedView(this._thenTemplateRef, this._context);
|
---|
| 3404 | }
|
---|
| 3405 | }
|
---|
| 3406 | }
|
---|
| 3407 | else {
|
---|
| 3408 | if (!this._elseViewRef) {
|
---|
| 3409 | this._viewContainer.clear();
|
---|
| 3410 | this._thenViewRef = null;
|
---|
| 3411 | if (this._elseTemplateRef) {
|
---|
| 3412 | this._elseViewRef =
|
---|
| 3413 | this._viewContainer.createEmbeddedView(this._elseTemplateRef, this._context);
|
---|
| 3414 | }
|
---|
| 3415 | }
|
---|
| 3416 | }
|
---|
| 3417 | }
|
---|
| 3418 | /**
|
---|
| 3419 | * Asserts the correct type of the context for the template that `NgIf` will render.
|
---|
| 3420 | *
|
---|
| 3421 | * The presence of this method is a signal to the Ivy template type-check compiler that the
|
---|
| 3422 | * `NgIf` structural directive renders its template with a specific context type.
|
---|
| 3423 | */
|
---|
| 3424 | static ngTemplateContextGuard(dir, ctx) {
|
---|
| 3425 | return true;
|
---|
| 3426 | }
|
---|
| 3427 | }
|
---|
| 3428 | NgIf.decorators = [
|
---|
| 3429 | { type: Directive, args: [{ selector: '[ngIf]' },] }
|
---|
| 3430 | ];
|
---|
| 3431 | NgIf.ctorParameters = () => [
|
---|
| 3432 | { type: ViewContainerRef },
|
---|
| 3433 | { type: TemplateRef }
|
---|
| 3434 | ];
|
---|
| 3435 | NgIf.propDecorators = {
|
---|
| 3436 | ngIf: [{ type: Input }],
|
---|
| 3437 | ngIfThen: [{ type: Input }],
|
---|
| 3438 | ngIfElse: [{ type: Input }]
|
---|
| 3439 | };
|
---|
| 3440 | /**
|
---|
| 3441 | * @publicApi
|
---|
| 3442 | */
|
---|
| 3443 | class NgIfContext {
|
---|
| 3444 | constructor() {
|
---|
| 3445 | this.$implicit = null;
|
---|
| 3446 | this.ngIf = null;
|
---|
| 3447 | }
|
---|
| 3448 | }
|
---|
| 3449 | function assertTemplate(property, templateRef) {
|
---|
| 3450 | const isTemplateRefOrNull = !!(!templateRef || templateRef.createEmbeddedView);
|
---|
| 3451 | if (!isTemplateRefOrNull) {
|
---|
| 3452 | throw new Error(`${property} must be a TemplateRef, but received '${ɵstringify(templateRef)}'.`);
|
---|
| 3453 | }
|
---|
| 3454 | }
|
---|
| 3455 |
|
---|
| 3456 | /**
|
---|
| 3457 | * @license
|
---|
| 3458 | * Copyright Google LLC All Rights Reserved.
|
---|
| 3459 | *
|
---|
| 3460 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 3461 | * found in the LICENSE file at https://angular.io/license
|
---|
| 3462 | */
|
---|
| 3463 | class SwitchView {
|
---|
| 3464 | constructor(_viewContainerRef, _templateRef) {
|
---|
| 3465 | this._viewContainerRef = _viewContainerRef;
|
---|
| 3466 | this._templateRef = _templateRef;
|
---|
| 3467 | this._created = false;
|
---|
| 3468 | }
|
---|
| 3469 | create() {
|
---|
| 3470 | this._created = true;
|
---|
| 3471 | this._viewContainerRef.createEmbeddedView(this._templateRef);
|
---|
| 3472 | }
|
---|
| 3473 | destroy() {
|
---|
| 3474 | this._created = false;
|
---|
| 3475 | this._viewContainerRef.clear();
|
---|
| 3476 | }
|
---|
| 3477 | enforceState(created) {
|
---|
| 3478 | if (created && !this._created) {
|
---|
| 3479 | this.create();
|
---|
| 3480 | }
|
---|
| 3481 | else if (!created && this._created) {
|
---|
| 3482 | this.destroy();
|
---|
| 3483 | }
|
---|
| 3484 | }
|
---|
| 3485 | }
|
---|
| 3486 | /**
|
---|
| 3487 | * @ngModule CommonModule
|
---|
| 3488 | *
|
---|
| 3489 | * @description
|
---|
| 3490 | * The `[ngSwitch]` directive on a container specifies an expression to match against.
|
---|
| 3491 | * The expressions to match are provided by `ngSwitchCase` directives on views within the container.
|
---|
| 3492 | * - Every view that matches is rendered.
|
---|
| 3493 | * - If there are no matches, a view with the `ngSwitchDefault` directive is rendered.
|
---|
| 3494 | * - Elements within the `[NgSwitch]` statement but outside of any `NgSwitchCase`
|
---|
| 3495 | * or `ngSwitchDefault` directive are preserved at the location.
|
---|
| 3496 | *
|
---|
| 3497 | * @usageNotes
|
---|
| 3498 | * Define a container element for the directive, and specify the switch expression
|
---|
| 3499 | * to match against as an attribute:
|
---|
| 3500 | *
|
---|
| 3501 | * ```
|
---|
| 3502 | * <container-element [ngSwitch]="switch_expression">
|
---|
| 3503 | * ```
|
---|
| 3504 | *
|
---|
| 3505 | * Within the container, `*ngSwitchCase` statements specify the match expressions
|
---|
| 3506 | * as attributes. Include `*ngSwitchDefault` as the final case.
|
---|
| 3507 | *
|
---|
| 3508 | * ```
|
---|
| 3509 | * <container-element [ngSwitch]="switch_expression">
|
---|
| 3510 | * <some-element *ngSwitchCase="match_expression_1">...</some-element>
|
---|
| 3511 | * ...
|
---|
| 3512 | * <some-element *ngSwitchDefault>...</some-element>
|
---|
| 3513 | * </container-element>
|
---|
| 3514 | * ```
|
---|
| 3515 | *
|
---|
| 3516 | * ### Usage Examples
|
---|
| 3517 | *
|
---|
| 3518 | * The following example shows how to use more than one case to display the same view:
|
---|
| 3519 | *
|
---|
| 3520 | * ```
|
---|
| 3521 | * <container-element [ngSwitch]="switch_expression">
|
---|
| 3522 | * <!-- the same view can be shown in more than one case -->
|
---|
| 3523 | * <some-element *ngSwitchCase="match_expression_1">...</some-element>
|
---|
| 3524 | * <some-element *ngSwitchCase="match_expression_2">...</some-element>
|
---|
| 3525 | * <some-other-element *ngSwitchCase="match_expression_3">...</some-other-element>
|
---|
| 3526 | * <!--default case when there are no matches -->
|
---|
| 3527 | * <some-element *ngSwitchDefault>...</some-element>
|
---|
| 3528 | * </container-element>
|
---|
| 3529 | * ```
|
---|
| 3530 | *
|
---|
| 3531 | * The following example shows how cases can be nested:
|
---|
| 3532 | * ```
|
---|
| 3533 | * <container-element [ngSwitch]="switch_expression">
|
---|
| 3534 | * <some-element *ngSwitchCase="match_expression_1">...</some-element>
|
---|
| 3535 | * <some-element *ngSwitchCase="match_expression_2">...</some-element>
|
---|
| 3536 | * <some-other-element *ngSwitchCase="match_expression_3">...</some-other-element>
|
---|
| 3537 | * <ng-container *ngSwitchCase="match_expression_3">
|
---|
| 3538 | * <!-- use a ng-container to group multiple root nodes -->
|
---|
| 3539 | * <inner-element></inner-element>
|
---|
| 3540 | * <inner-other-element></inner-other-element>
|
---|
| 3541 | * </ng-container>
|
---|
| 3542 | * <some-element *ngSwitchDefault>...</some-element>
|
---|
| 3543 | * </container-element>
|
---|
| 3544 | * ```
|
---|
| 3545 | *
|
---|
| 3546 | * @publicApi
|
---|
| 3547 | * @see `NgSwitchCase`
|
---|
| 3548 | * @see `NgSwitchDefault`
|
---|
| 3549 | * @see [Structural Directives](guide/structural-directives)
|
---|
| 3550 | *
|
---|
| 3551 | */
|
---|
| 3552 | class NgSwitch {
|
---|
| 3553 | constructor() {
|
---|
| 3554 | this._defaultUsed = false;
|
---|
| 3555 | this._caseCount = 0;
|
---|
| 3556 | this._lastCaseCheckIndex = 0;
|
---|
| 3557 | this._lastCasesMatched = false;
|
---|
| 3558 | }
|
---|
| 3559 | set ngSwitch(newValue) {
|
---|
| 3560 | this._ngSwitch = newValue;
|
---|
| 3561 | if (this._caseCount === 0) {
|
---|
| 3562 | this._updateDefaultCases(true);
|
---|
| 3563 | }
|
---|
| 3564 | }
|
---|
| 3565 | /** @internal */
|
---|
| 3566 | _addCase() {
|
---|
| 3567 | return this._caseCount++;
|
---|
| 3568 | }
|
---|
| 3569 | /** @internal */
|
---|
| 3570 | _addDefault(view) {
|
---|
| 3571 | if (!this._defaultViews) {
|
---|
| 3572 | this._defaultViews = [];
|
---|
| 3573 | }
|
---|
| 3574 | this._defaultViews.push(view);
|
---|
| 3575 | }
|
---|
| 3576 | /** @internal */
|
---|
| 3577 | _matchCase(value) {
|
---|
| 3578 | const matched = value == this._ngSwitch;
|
---|
| 3579 | this._lastCasesMatched = this._lastCasesMatched || matched;
|
---|
| 3580 | this._lastCaseCheckIndex++;
|
---|
| 3581 | if (this._lastCaseCheckIndex === this._caseCount) {
|
---|
| 3582 | this._updateDefaultCases(!this._lastCasesMatched);
|
---|
| 3583 | this._lastCaseCheckIndex = 0;
|
---|
| 3584 | this._lastCasesMatched = false;
|
---|
| 3585 | }
|
---|
| 3586 | return matched;
|
---|
| 3587 | }
|
---|
| 3588 | _updateDefaultCases(useDefault) {
|
---|
| 3589 | if (this._defaultViews && useDefault !== this._defaultUsed) {
|
---|
| 3590 | this._defaultUsed = useDefault;
|
---|
| 3591 | for (let i = 0; i < this._defaultViews.length; i++) {
|
---|
| 3592 | const defaultView = this._defaultViews[i];
|
---|
| 3593 | defaultView.enforceState(useDefault);
|
---|
| 3594 | }
|
---|
| 3595 | }
|
---|
| 3596 | }
|
---|
| 3597 | }
|
---|
| 3598 | NgSwitch.decorators = [
|
---|
| 3599 | { type: Directive, args: [{ selector: '[ngSwitch]' },] }
|
---|
| 3600 | ];
|
---|
| 3601 | NgSwitch.propDecorators = {
|
---|
| 3602 | ngSwitch: [{ type: Input }]
|
---|
| 3603 | };
|
---|
| 3604 | /**
|
---|
| 3605 | * @ngModule CommonModule
|
---|
| 3606 | *
|
---|
| 3607 | * @description
|
---|
| 3608 | * Provides a switch case expression to match against an enclosing `ngSwitch` expression.
|
---|
| 3609 | * When the expressions match, the given `NgSwitchCase` template is rendered.
|
---|
| 3610 | * If multiple match expressions match the switch expression value, all of them are displayed.
|
---|
| 3611 | *
|
---|
| 3612 | * @usageNotes
|
---|
| 3613 | *
|
---|
| 3614 | * Within a switch container, `*ngSwitchCase` statements specify the match expressions
|
---|
| 3615 | * as attributes. Include `*ngSwitchDefault` as the final case.
|
---|
| 3616 | *
|
---|
| 3617 | * ```
|
---|
| 3618 | * <container-element [ngSwitch]="switch_expression">
|
---|
| 3619 | * <some-element *ngSwitchCase="match_expression_1">...</some-element>
|
---|
| 3620 | * ...
|
---|
| 3621 | * <some-element *ngSwitchDefault>...</some-element>
|
---|
| 3622 | * </container-element>
|
---|
| 3623 | * ```
|
---|
| 3624 | *
|
---|
| 3625 | * Each switch-case statement contains an in-line HTML template or template reference
|
---|
| 3626 | * that defines the subtree to be selected if the value of the match expression
|
---|
| 3627 | * matches the value of the switch expression.
|
---|
| 3628 | *
|
---|
| 3629 | * Unlike JavaScript, which uses strict equality, Angular uses loose equality.
|
---|
| 3630 | * This means that the empty string, `""` matches 0.
|
---|
| 3631 | *
|
---|
| 3632 | * @publicApi
|
---|
| 3633 | * @see `NgSwitch`
|
---|
| 3634 | * @see `NgSwitchDefault`
|
---|
| 3635 | *
|
---|
| 3636 | */
|
---|
| 3637 | class NgSwitchCase {
|
---|
| 3638 | constructor(viewContainer, templateRef, ngSwitch) {
|
---|
| 3639 | this.ngSwitch = ngSwitch;
|
---|
| 3640 | if ((typeof ngDevMode === 'undefined' || ngDevMode) && !ngSwitch) {
|
---|
| 3641 | throwNgSwitchProviderNotFoundError('ngSwitchCase', 'NgSwitchCase');
|
---|
| 3642 | }
|
---|
| 3643 | ngSwitch._addCase();
|
---|
| 3644 | this._view = new SwitchView(viewContainer, templateRef);
|
---|
| 3645 | }
|
---|
| 3646 | /**
|
---|
| 3647 | * Performs case matching. For internal use only.
|
---|
| 3648 | */
|
---|
| 3649 | ngDoCheck() {
|
---|
| 3650 | this._view.enforceState(this.ngSwitch._matchCase(this.ngSwitchCase));
|
---|
| 3651 | }
|
---|
| 3652 | }
|
---|
| 3653 | NgSwitchCase.decorators = [
|
---|
| 3654 | { type: Directive, args: [{ selector: '[ngSwitchCase]' },] }
|
---|
| 3655 | ];
|
---|
| 3656 | NgSwitchCase.ctorParameters = () => [
|
---|
| 3657 | { type: ViewContainerRef },
|
---|
| 3658 | { type: TemplateRef },
|
---|
| 3659 | { type: NgSwitch, decorators: [{ type: Optional }, { type: Host }] }
|
---|
| 3660 | ];
|
---|
| 3661 | NgSwitchCase.propDecorators = {
|
---|
| 3662 | ngSwitchCase: [{ type: Input }]
|
---|
| 3663 | };
|
---|
| 3664 | /**
|
---|
| 3665 | * @ngModule CommonModule
|
---|
| 3666 | *
|
---|
| 3667 | * @description
|
---|
| 3668 | *
|
---|
| 3669 | * Creates a view that is rendered when no `NgSwitchCase` expressions
|
---|
| 3670 | * match the `NgSwitch` expression.
|
---|
| 3671 | * This statement should be the final case in an `NgSwitch`.
|
---|
| 3672 | *
|
---|
| 3673 | * @publicApi
|
---|
| 3674 | * @see `NgSwitch`
|
---|
| 3675 | * @see `NgSwitchCase`
|
---|
| 3676 | *
|
---|
| 3677 | */
|
---|
| 3678 | class NgSwitchDefault {
|
---|
| 3679 | constructor(viewContainer, templateRef, ngSwitch) {
|
---|
| 3680 | if ((typeof ngDevMode === 'undefined' || ngDevMode) && !ngSwitch) {
|
---|
| 3681 | throwNgSwitchProviderNotFoundError('ngSwitchDefault', 'NgSwitchDefault');
|
---|
| 3682 | }
|
---|
| 3683 | ngSwitch._addDefault(new SwitchView(viewContainer, templateRef));
|
---|
| 3684 | }
|
---|
| 3685 | }
|
---|
| 3686 | NgSwitchDefault.decorators = [
|
---|
| 3687 | { type: Directive, args: [{ selector: '[ngSwitchDefault]' },] }
|
---|
| 3688 | ];
|
---|
| 3689 | NgSwitchDefault.ctorParameters = () => [
|
---|
| 3690 | { type: ViewContainerRef },
|
---|
| 3691 | { type: TemplateRef },
|
---|
| 3692 | { type: NgSwitch, decorators: [{ type: Optional }, { type: Host }] }
|
---|
| 3693 | ];
|
---|
| 3694 | function throwNgSwitchProviderNotFoundError(attrName, directiveName) {
|
---|
| 3695 | throw new ɵRuntimeError("305" /* TEMPLATE_STRUCTURE_ERROR */, `An element with the "${attrName}" attribute ` +
|
---|
| 3696 | `(matching the "${directiveName}" directive) must be located inside an element with the "ngSwitch" attribute ` +
|
---|
| 3697 | `(matching "NgSwitch" directive)`);
|
---|
| 3698 | }
|
---|
| 3699 |
|
---|
| 3700 | /**
|
---|
| 3701 | * @license
|
---|
| 3702 | * Copyright Google LLC All Rights Reserved.
|
---|
| 3703 | *
|
---|
| 3704 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 3705 | * found in the LICENSE file at https://angular.io/license
|
---|
| 3706 | */
|
---|
| 3707 | /**
|
---|
| 3708 | * @ngModule CommonModule
|
---|
| 3709 | *
|
---|
| 3710 | * @usageNotes
|
---|
| 3711 | * ```
|
---|
| 3712 | * <some-element [ngPlural]="value">
|
---|
| 3713 | * <ng-template ngPluralCase="=0">there is nothing</ng-template>
|
---|
| 3714 | * <ng-template ngPluralCase="=1">there is one</ng-template>
|
---|
| 3715 | * <ng-template ngPluralCase="few">there are a few</ng-template>
|
---|
| 3716 | * </some-element>
|
---|
| 3717 | * ```
|
---|
| 3718 | *
|
---|
| 3719 | * @description
|
---|
| 3720 | *
|
---|
| 3721 | * Adds / removes DOM sub-trees based on a numeric value. Tailored for pluralization.
|
---|
| 3722 | *
|
---|
| 3723 | * Displays DOM sub-trees that match the switch expression value, or failing that, DOM sub-trees
|
---|
| 3724 | * that match the switch expression's pluralization category.
|
---|
| 3725 | *
|
---|
| 3726 | * To use this directive you must provide a container element that sets the `[ngPlural]` attribute
|
---|
| 3727 | * to a switch expression. Inner elements with a `[ngPluralCase]` will display based on their
|
---|
| 3728 | * expression:
|
---|
| 3729 | * - if `[ngPluralCase]` is set to a value starting with `=`, it will only display if the value
|
---|
| 3730 | * matches the switch expression exactly,
|
---|
| 3731 | * - otherwise, the view will be treated as a "category match", and will only display if exact
|
---|
| 3732 | * value matches aren't found and the value maps to its category for the defined locale.
|
---|
| 3733 | *
|
---|
| 3734 | * See http://cldr.unicode.org/index/cldr-spec/plural-rules
|
---|
| 3735 | *
|
---|
| 3736 | * @publicApi
|
---|
| 3737 | */
|
---|
| 3738 | class NgPlural {
|
---|
| 3739 | constructor(_localization) {
|
---|
| 3740 | this._localization = _localization;
|
---|
| 3741 | this._caseViews = {};
|
---|
| 3742 | }
|
---|
| 3743 | set ngPlural(value) {
|
---|
| 3744 | this._switchValue = value;
|
---|
| 3745 | this._updateView();
|
---|
| 3746 | }
|
---|
| 3747 | addCase(value, switchView) {
|
---|
| 3748 | this._caseViews[value] = switchView;
|
---|
| 3749 | }
|
---|
| 3750 | _updateView() {
|
---|
| 3751 | this._clearViews();
|
---|
| 3752 | const cases = Object.keys(this._caseViews);
|
---|
| 3753 | const key = getPluralCategory(this._switchValue, cases, this._localization);
|
---|
| 3754 | this._activateView(this._caseViews[key]);
|
---|
| 3755 | }
|
---|
| 3756 | _clearViews() {
|
---|
| 3757 | if (this._activeView)
|
---|
| 3758 | this._activeView.destroy();
|
---|
| 3759 | }
|
---|
| 3760 | _activateView(view) {
|
---|
| 3761 | if (view) {
|
---|
| 3762 | this._activeView = view;
|
---|
| 3763 | this._activeView.create();
|
---|
| 3764 | }
|
---|
| 3765 | }
|
---|
| 3766 | }
|
---|
| 3767 | NgPlural.decorators = [
|
---|
| 3768 | { type: Directive, args: [{ selector: '[ngPlural]' },] }
|
---|
| 3769 | ];
|
---|
| 3770 | NgPlural.ctorParameters = () => [
|
---|
| 3771 | { type: NgLocalization }
|
---|
| 3772 | ];
|
---|
| 3773 | NgPlural.propDecorators = {
|
---|
| 3774 | ngPlural: [{ type: Input }]
|
---|
| 3775 | };
|
---|
| 3776 | /**
|
---|
| 3777 | * @ngModule CommonModule
|
---|
| 3778 | *
|
---|
| 3779 | * @description
|
---|
| 3780 | *
|
---|
| 3781 | * Creates a view that will be added/removed from the parent {@link NgPlural} when the
|
---|
| 3782 | * given expression matches the plural expression according to CLDR rules.
|
---|
| 3783 | *
|
---|
| 3784 | * @usageNotes
|
---|
| 3785 | * ```
|
---|
| 3786 | * <some-element [ngPlural]="value">
|
---|
| 3787 | * <ng-template ngPluralCase="=0">...</ng-template>
|
---|
| 3788 | * <ng-template ngPluralCase="other">...</ng-template>
|
---|
| 3789 | * </some-element>
|
---|
| 3790 | *```
|
---|
| 3791 | *
|
---|
| 3792 | * See {@link NgPlural} for more details and example.
|
---|
| 3793 | *
|
---|
| 3794 | * @publicApi
|
---|
| 3795 | */
|
---|
| 3796 | class NgPluralCase {
|
---|
| 3797 | constructor(value, template, viewContainer, ngPlural) {
|
---|
| 3798 | this.value = value;
|
---|
| 3799 | const isANumber = !isNaN(Number(value));
|
---|
| 3800 | ngPlural.addCase(isANumber ? `=${value}` : value, new SwitchView(viewContainer, template));
|
---|
| 3801 | }
|
---|
| 3802 | }
|
---|
| 3803 | NgPluralCase.decorators = [
|
---|
| 3804 | { type: Directive, args: [{ selector: '[ngPluralCase]' },] }
|
---|
| 3805 | ];
|
---|
| 3806 | NgPluralCase.ctorParameters = () => [
|
---|
| 3807 | { type: String, decorators: [{ type: Attribute, args: ['ngPluralCase',] }] },
|
---|
| 3808 | { type: TemplateRef },
|
---|
| 3809 | { type: ViewContainerRef },
|
---|
| 3810 | { type: NgPlural, decorators: [{ type: Host }] }
|
---|
| 3811 | ];
|
---|
| 3812 |
|
---|
| 3813 | /**
|
---|
| 3814 | * @license
|
---|
| 3815 | * Copyright Google LLC All Rights Reserved.
|
---|
| 3816 | *
|
---|
| 3817 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 3818 | * found in the LICENSE file at https://angular.io/license
|
---|
| 3819 | */
|
---|
| 3820 | /**
|
---|
| 3821 | * @ngModule CommonModule
|
---|
| 3822 | *
|
---|
| 3823 | * @usageNotes
|
---|
| 3824 | *
|
---|
| 3825 | * Set the font of the containing element to the result of an expression.
|
---|
| 3826 | *
|
---|
| 3827 | * ```
|
---|
| 3828 | * <some-element [ngStyle]="{'font-style': styleExp}">...</some-element>
|
---|
| 3829 | * ```
|
---|
| 3830 | *
|
---|
| 3831 | * Set the width of the containing element to a pixel value returned by an expression.
|
---|
| 3832 | *
|
---|
| 3833 | * ```
|
---|
| 3834 | * <some-element [ngStyle]="{'max-width.px': widthExp}">...</some-element>
|
---|
| 3835 | * ```
|
---|
| 3836 | *
|
---|
| 3837 | * Set a collection of style values using an expression that returns key-value pairs.
|
---|
| 3838 | *
|
---|
| 3839 | * ```
|
---|
| 3840 | * <some-element [ngStyle]="objExp">...</some-element>
|
---|
| 3841 | * ```
|
---|
| 3842 | *
|
---|
| 3843 | * @description
|
---|
| 3844 | *
|
---|
| 3845 | * An attribute directive that updates styles for the containing HTML element.
|
---|
| 3846 | * Sets one or more style properties, specified as colon-separated key-value pairs.
|
---|
| 3847 | * The key is a style name, with an optional `.<unit>` suffix
|
---|
| 3848 | * (such as 'top.px', 'font-style.em').
|
---|
| 3849 | * The value is an expression to be evaluated.
|
---|
| 3850 | * The resulting non-null value, expressed in the given unit,
|
---|
| 3851 | * is assigned to the given style property.
|
---|
| 3852 | * If the result of evaluation is null, the corresponding style is removed.
|
---|
| 3853 | *
|
---|
| 3854 | * @publicApi
|
---|
| 3855 | */
|
---|
| 3856 | class NgStyle {
|
---|
| 3857 | constructor(_ngEl, _differs, _renderer) {
|
---|
| 3858 | this._ngEl = _ngEl;
|
---|
| 3859 | this._differs = _differs;
|
---|
| 3860 | this._renderer = _renderer;
|
---|
| 3861 | this._ngStyle = null;
|
---|
| 3862 | this._differ = null;
|
---|
| 3863 | }
|
---|
| 3864 | set ngStyle(values) {
|
---|
| 3865 | this._ngStyle = values;
|
---|
| 3866 | if (!this._differ && values) {
|
---|
| 3867 | this._differ = this._differs.find(values).create();
|
---|
| 3868 | }
|
---|
| 3869 | }
|
---|
| 3870 | ngDoCheck() {
|
---|
| 3871 | if (this._differ) {
|
---|
| 3872 | const changes = this._differ.diff(this._ngStyle);
|
---|
| 3873 | if (changes) {
|
---|
| 3874 | this._applyChanges(changes);
|
---|
| 3875 | }
|
---|
| 3876 | }
|
---|
| 3877 | }
|
---|
| 3878 | _setStyle(nameAndUnit, value) {
|
---|
| 3879 | const [name, unit] = nameAndUnit.split('.');
|
---|
| 3880 | value = value != null && unit ? `${value}${unit}` : value;
|
---|
| 3881 | if (value != null) {
|
---|
| 3882 | this._renderer.setStyle(this._ngEl.nativeElement, name, value);
|
---|
| 3883 | }
|
---|
| 3884 | else {
|
---|
| 3885 | this._renderer.removeStyle(this._ngEl.nativeElement, name);
|
---|
| 3886 | }
|
---|
| 3887 | }
|
---|
| 3888 | _applyChanges(changes) {
|
---|
| 3889 | changes.forEachRemovedItem((record) => this._setStyle(record.key, null));
|
---|
| 3890 | changes.forEachAddedItem((record) => this._setStyle(record.key, record.currentValue));
|
---|
| 3891 | changes.forEachChangedItem((record) => this._setStyle(record.key, record.currentValue));
|
---|
| 3892 | }
|
---|
| 3893 | }
|
---|
| 3894 | NgStyle.decorators = [
|
---|
| 3895 | { type: Directive, args: [{ selector: '[ngStyle]' },] }
|
---|
| 3896 | ];
|
---|
| 3897 | NgStyle.ctorParameters = () => [
|
---|
| 3898 | { type: ElementRef },
|
---|
| 3899 | { type: KeyValueDiffers },
|
---|
| 3900 | { type: Renderer2 }
|
---|
| 3901 | ];
|
---|
| 3902 | NgStyle.propDecorators = {
|
---|
| 3903 | ngStyle: [{ type: Input, args: ['ngStyle',] }]
|
---|
| 3904 | };
|
---|
| 3905 |
|
---|
| 3906 | /**
|
---|
| 3907 | * @license
|
---|
| 3908 | * Copyright Google LLC All Rights Reserved.
|
---|
| 3909 | *
|
---|
| 3910 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 3911 | * found in the LICENSE file at https://angular.io/license
|
---|
| 3912 | */
|
---|
| 3913 | /**
|
---|
| 3914 | * @ngModule CommonModule
|
---|
| 3915 | *
|
---|
| 3916 | * @description
|
---|
| 3917 | *
|
---|
| 3918 | * Inserts an embedded view from a prepared `TemplateRef`.
|
---|
| 3919 | *
|
---|
| 3920 | * You can attach a context object to the `EmbeddedViewRef` by setting `[ngTemplateOutletContext]`.
|
---|
| 3921 | * `[ngTemplateOutletContext]` should be an object, the object's keys will be available for binding
|
---|
| 3922 | * by the local template `let` declarations.
|
---|
| 3923 | *
|
---|
| 3924 | * @usageNotes
|
---|
| 3925 | * ```
|
---|
| 3926 | * <ng-container *ngTemplateOutlet="templateRefExp; context: contextExp"></ng-container>
|
---|
| 3927 | * ```
|
---|
| 3928 | *
|
---|
| 3929 | * Using the key `$implicit` in the context object will set its value as default.
|
---|
| 3930 | *
|
---|
| 3931 | * ### Example
|
---|
| 3932 | *
|
---|
| 3933 | * {@example common/ngTemplateOutlet/ts/module.ts region='NgTemplateOutlet'}
|
---|
| 3934 | *
|
---|
| 3935 | * @publicApi
|
---|
| 3936 | */
|
---|
| 3937 | class NgTemplateOutlet {
|
---|
| 3938 | constructor(_viewContainerRef) {
|
---|
| 3939 | this._viewContainerRef = _viewContainerRef;
|
---|
| 3940 | this._viewRef = null;
|
---|
| 3941 | /**
|
---|
| 3942 | * A context object to attach to the {@link EmbeddedViewRef}. This should be an
|
---|
| 3943 | * object, the object's keys will be available for binding by the local template `let`
|
---|
| 3944 | * declarations.
|
---|
| 3945 | * Using the key `$implicit` in the context object will set its value as default.
|
---|
| 3946 | */
|
---|
| 3947 | this.ngTemplateOutletContext = null;
|
---|
| 3948 | /**
|
---|
| 3949 | * A string defining the template reference and optionally the context object for the template.
|
---|
| 3950 | */
|
---|
| 3951 | this.ngTemplateOutlet = null;
|
---|
| 3952 | }
|
---|
| 3953 | ngOnChanges(changes) {
|
---|
| 3954 | if (changes['ngTemplateOutlet']) {
|
---|
| 3955 | const viewContainerRef = this._viewContainerRef;
|
---|
| 3956 | if (this._viewRef) {
|
---|
| 3957 | viewContainerRef.remove(viewContainerRef.indexOf(this._viewRef));
|
---|
| 3958 | }
|
---|
| 3959 | this._viewRef = this.ngTemplateOutlet ?
|
---|
| 3960 | viewContainerRef.createEmbeddedView(this.ngTemplateOutlet, this.ngTemplateOutletContext) :
|
---|
| 3961 | null;
|
---|
| 3962 | }
|
---|
| 3963 | else if (this._viewRef && changes['ngTemplateOutletContext'] && this.ngTemplateOutletContext) {
|
---|
| 3964 | this._viewRef.context = this.ngTemplateOutletContext;
|
---|
| 3965 | }
|
---|
| 3966 | }
|
---|
| 3967 | }
|
---|
| 3968 | NgTemplateOutlet.decorators = [
|
---|
| 3969 | { type: Directive, args: [{ selector: '[ngTemplateOutlet]' },] }
|
---|
| 3970 | ];
|
---|
| 3971 | NgTemplateOutlet.ctorParameters = () => [
|
---|
| 3972 | { type: ViewContainerRef }
|
---|
| 3973 | ];
|
---|
| 3974 | NgTemplateOutlet.propDecorators = {
|
---|
| 3975 | ngTemplateOutletContext: [{ type: Input }],
|
---|
| 3976 | ngTemplateOutlet: [{ type: Input }]
|
---|
| 3977 | };
|
---|
| 3978 |
|
---|
| 3979 | /**
|
---|
| 3980 | * @license
|
---|
| 3981 | * Copyright Google LLC All Rights Reserved.
|
---|
| 3982 | *
|
---|
| 3983 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 3984 | * found in the LICENSE file at https://angular.io/license
|
---|
| 3985 | */
|
---|
| 3986 | /**
|
---|
| 3987 | * A collection of Angular directives that are likely to be used in each and every Angular
|
---|
| 3988 | * application.
|
---|
| 3989 | */
|
---|
| 3990 | const COMMON_DIRECTIVES = [
|
---|
| 3991 | NgClass,
|
---|
| 3992 | NgComponentOutlet,
|
---|
| 3993 | NgForOf,
|
---|
| 3994 | NgIf,
|
---|
| 3995 | NgTemplateOutlet,
|
---|
| 3996 | NgStyle,
|
---|
| 3997 | NgSwitch,
|
---|
| 3998 | NgSwitchCase,
|
---|
| 3999 | NgSwitchDefault,
|
---|
| 4000 | NgPlural,
|
---|
| 4001 | NgPluralCase,
|
---|
| 4002 | ];
|
---|
| 4003 |
|
---|
| 4004 | /**
|
---|
| 4005 | * @license
|
---|
| 4006 | * Copyright Google LLC All Rights Reserved.
|
---|
| 4007 | *
|
---|
| 4008 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 4009 | * found in the LICENSE file at https://angular.io/license
|
---|
| 4010 | */
|
---|
| 4011 | function invalidPipeArgumentError(type, value) {
|
---|
| 4012 | return Error(`InvalidPipeArgument: '${value}' for pipe '${ɵstringify(type)}'`);
|
---|
| 4013 | }
|
---|
| 4014 |
|
---|
| 4015 | /**
|
---|
| 4016 | * @license
|
---|
| 4017 | * Copyright Google LLC All Rights Reserved.
|
---|
| 4018 | *
|
---|
| 4019 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 4020 | * found in the LICENSE file at https://angular.io/license
|
---|
| 4021 | */
|
---|
| 4022 | class SubscribableStrategy {
|
---|
| 4023 | createSubscription(async, updateLatestValue) {
|
---|
| 4024 | return async.subscribe({
|
---|
| 4025 | next: updateLatestValue,
|
---|
| 4026 | error: (e) => {
|
---|
| 4027 | throw e;
|
---|
| 4028 | }
|
---|
| 4029 | });
|
---|
| 4030 | }
|
---|
| 4031 | dispose(subscription) {
|
---|
| 4032 | subscription.unsubscribe();
|
---|
| 4033 | }
|
---|
| 4034 | onDestroy(subscription) {
|
---|
| 4035 | subscription.unsubscribe();
|
---|
| 4036 | }
|
---|
| 4037 | }
|
---|
| 4038 | class PromiseStrategy {
|
---|
| 4039 | createSubscription(async, updateLatestValue) {
|
---|
| 4040 | return async.then(updateLatestValue, e => {
|
---|
| 4041 | throw e;
|
---|
| 4042 | });
|
---|
| 4043 | }
|
---|
| 4044 | dispose(subscription) { }
|
---|
| 4045 | onDestroy(subscription) { }
|
---|
| 4046 | }
|
---|
| 4047 | const _promiseStrategy = new PromiseStrategy();
|
---|
| 4048 | const _subscribableStrategy = new SubscribableStrategy();
|
---|
| 4049 | /**
|
---|
| 4050 | * @ngModule CommonModule
|
---|
| 4051 | * @description
|
---|
| 4052 | *
|
---|
| 4053 | * Unwraps a value from an asynchronous primitive.
|
---|
| 4054 | *
|
---|
| 4055 | * The `async` pipe subscribes to an `Observable` or `Promise` and returns the latest value it has
|
---|
| 4056 | * emitted. When a new value is emitted, the `async` pipe marks the component to be checked for
|
---|
| 4057 | * changes. When the component gets destroyed, the `async` pipe unsubscribes automatically to avoid
|
---|
[e29cc2e] | 4058 | * potential memory leaks. When the reference of the expression changes, the `async` pipe
|
---|
| 4059 | * automatically unsubscribes from the old `Observable` or `Promise` and subscribes to the new one.
|
---|
[6a3a178] | 4060 | *
|
---|
| 4061 | * @usageNotes
|
---|
| 4062 | *
|
---|
| 4063 | * ### Examples
|
---|
| 4064 | *
|
---|
| 4065 | * This example binds a `Promise` to the view. Clicking the `Resolve` button resolves the
|
---|
| 4066 | * promise.
|
---|
| 4067 | *
|
---|
| 4068 | * {@example common/pipes/ts/async_pipe.ts region='AsyncPipePromise'}
|
---|
| 4069 | *
|
---|
| 4070 | * It's also possible to use `async` with Observables. The example below binds the `time` Observable
|
---|
| 4071 | * to the view. The Observable continuously updates the view with the current time.
|
---|
| 4072 | *
|
---|
| 4073 | * {@example common/pipes/ts/async_pipe.ts region='AsyncPipeObservable'}
|
---|
| 4074 | *
|
---|
| 4075 | * @publicApi
|
---|
| 4076 | */
|
---|
| 4077 | class AsyncPipe {
|
---|
| 4078 | constructor(_ref) {
|
---|
| 4079 | this._ref = _ref;
|
---|
| 4080 | this._latestValue = null;
|
---|
| 4081 | this._subscription = null;
|
---|
| 4082 | this._obj = null;
|
---|
| 4083 | this._strategy = null;
|
---|
| 4084 | }
|
---|
| 4085 | ngOnDestroy() {
|
---|
| 4086 | if (this._subscription) {
|
---|
| 4087 | this._dispose();
|
---|
| 4088 | }
|
---|
| 4089 | }
|
---|
| 4090 | transform(obj) {
|
---|
| 4091 | if (!this._obj) {
|
---|
| 4092 | if (obj) {
|
---|
| 4093 | this._subscribe(obj);
|
---|
| 4094 | }
|
---|
| 4095 | return this._latestValue;
|
---|
| 4096 | }
|
---|
| 4097 | if (obj !== this._obj) {
|
---|
| 4098 | this._dispose();
|
---|
| 4099 | return this.transform(obj);
|
---|
| 4100 | }
|
---|
| 4101 | return this._latestValue;
|
---|
| 4102 | }
|
---|
| 4103 | _subscribe(obj) {
|
---|
| 4104 | this._obj = obj;
|
---|
| 4105 | this._strategy = this._selectStrategy(obj);
|
---|
| 4106 | this._subscription = this._strategy.createSubscription(obj, (value) => this._updateLatestValue(obj, value));
|
---|
| 4107 | }
|
---|
| 4108 | _selectStrategy(obj) {
|
---|
| 4109 | if (ɵisPromise(obj)) {
|
---|
| 4110 | return _promiseStrategy;
|
---|
| 4111 | }
|
---|
| 4112 | if (ɵisSubscribable(obj)) {
|
---|
| 4113 | return _subscribableStrategy;
|
---|
| 4114 | }
|
---|
| 4115 | throw invalidPipeArgumentError(AsyncPipe, obj);
|
---|
| 4116 | }
|
---|
| 4117 | _dispose() {
|
---|
| 4118 | this._strategy.dispose(this._subscription);
|
---|
| 4119 | this._latestValue = null;
|
---|
| 4120 | this._subscription = null;
|
---|
| 4121 | this._obj = null;
|
---|
| 4122 | }
|
---|
| 4123 | _updateLatestValue(async, value) {
|
---|
| 4124 | if (async === this._obj) {
|
---|
| 4125 | this._latestValue = value;
|
---|
| 4126 | this._ref.markForCheck();
|
---|
| 4127 | }
|
---|
| 4128 | }
|
---|
| 4129 | }
|
---|
| 4130 | AsyncPipe.decorators = [
|
---|
| 4131 | { type: Pipe, args: [{ name: 'async', pure: false },] }
|
---|
| 4132 | ];
|
---|
| 4133 | AsyncPipe.ctorParameters = () => [
|
---|
| 4134 | { type: ChangeDetectorRef }
|
---|
| 4135 | ];
|
---|
| 4136 |
|
---|
| 4137 | /**
|
---|
| 4138 | * @license
|
---|
| 4139 | * Copyright Google LLC All Rights Reserved.
|
---|
| 4140 | *
|
---|
| 4141 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 4142 | * found in the LICENSE file at https://angular.io/license
|
---|
| 4143 | */
|
---|
| 4144 | /**
|
---|
| 4145 | * Transforms text to all lower case.
|
---|
| 4146 | *
|
---|
| 4147 | * @see `UpperCasePipe`
|
---|
| 4148 | * @see `TitleCasePipe`
|
---|
| 4149 | * @usageNotes
|
---|
| 4150 | *
|
---|
| 4151 | * The following example defines a view that allows the user to enter
|
---|
| 4152 | * text, and then uses the pipe to convert the input text to all lower case.
|
---|
| 4153 | *
|
---|
| 4154 | * <code-example path="common/pipes/ts/lowerupper_pipe.ts" region='LowerUpperPipe'></code-example>
|
---|
| 4155 | *
|
---|
| 4156 | * @ngModule CommonModule
|
---|
| 4157 | * @publicApi
|
---|
| 4158 | */
|
---|
| 4159 | class LowerCasePipe {
|
---|
| 4160 | transform(value) {
|
---|
| 4161 | if (value == null)
|
---|
| 4162 | return null;
|
---|
| 4163 | if (typeof value !== 'string') {
|
---|
| 4164 | throw invalidPipeArgumentError(LowerCasePipe, value);
|
---|
| 4165 | }
|
---|
| 4166 | return value.toLowerCase();
|
---|
| 4167 | }
|
---|
| 4168 | }
|
---|
| 4169 | LowerCasePipe.decorators = [
|
---|
| 4170 | { type: Pipe, args: [{ name: 'lowercase' },] }
|
---|
| 4171 | ];
|
---|
| 4172 | //
|
---|
| 4173 | // Regex below matches any Unicode word and number compatible with ES5. In ES2018 the same result
|
---|
| 4174 | // can be achieved by using /[0-9\p{L}]\S*/gu and also known as Unicode Property Escapes
|
---|
| 4175 | // (https://2ality.com/2017/07/regexp-unicode-property-escapes.html). Since there is no
|
---|
| 4176 | // transpilation of this functionality down to ES5 without external tool, the only solution is
|
---|
| 4177 | // to use already transpiled form. Example can be found here -
|
---|
| 4178 | // https://mothereff.in/regexpu#input=var+regex+%3D+%2F%5B0-9%5Cp%7BL%7D%5D%5CS*%2Fgu%3B%0A%0A&unicodePropertyEscape=1
|
---|
| 4179 | //
|
---|
| 4180 | const unicodeWordMatch = /(?:[0-9A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0560-\u0588\u05D0-\u05EA\u05EF-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u0860-\u086A\u0870-\u0887\u0889-\u088E\u08A0-\u08C9\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u09FC\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C5D\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D04-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u1711\u171F-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1878\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4C\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312F\u3131-\u318E\u31A0-\u31BF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7CA\uA7D0\uA7D1\uA7D3\uA7D5-\uA7D9\uA7F2-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB69\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF2D-\uDF40\uDF42-\uDF49\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDD70-\uDD7A\uDD7C-\uDD8A\uDD8C-\uDD92\uDD94\uDD95\uDD97-\uDDA1\uDDA3-\uDDB1\uDDB3-\uDDB9\uDDBB\uDDBC\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67\uDF80-\uDF85\uDF87-\uDFB0\uDFB2-\uDFBA]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE35\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2\uDD00-\uDD23\uDE80-\uDEA9\uDEB0\uDEB1\uDF00-\uDF1C\uDF27\uDF30-\uDF45\uDF70-\uDF81\uDFB0-\uDFC4\uDFE0-\uDFF6]|\uD804[\uDC03-\uDC37\uDC71\uDC72\uDC75\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD44\uDD47\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC5F-\uDC61\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDEB8\uDF00-\uDF1A\uDF40-\uDF46]|\uD806[\uDC00-\uDC2B\uDCA0-\uDCDF\uDCFF-\uDD06\uDD09\uDD0C-\uDD13\uDD15\uDD16\uDD18-\uDD2F\uDD3F\uDD41\uDDA0-\uDDA7\uDDAA-\uDDD0\uDDE1\uDDE3\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE89\uDE9D\uDEB0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46\uDD60-\uDD65\uDD67\uDD68\uDD6A-\uDD89\uDD98\uDEE0-\uDEF2\uDFB0]|\uD808[\uDC00-\uDF99]|\uD809[\uDC80-\uDD43]|\uD80B[\uDF90-\uDFF0]|[\uD80C\uD81C-\uD820\uD822\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879\uD880-\uD883][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE70-\uDEBE\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDE40-\uDE7F\uDF00-\uDF4A\uDF50\uDF93-\uDF9F\uDFE0\uDFE1\uDFE3]|\uD821[\uDC00-\uDFF7]|\uD823[\uDC00-\uDCD5\uDD00-\uDD08]|\uD82B[\uDFF0-\uDFF3\uDFF5-\uDFFB\uDFFD\uDFFE]|\uD82C[\uDC00-\uDD22\uDD50-\uDD52\uDD64-\uDD67\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD837[\uDF00-\uDF1E]|\uD838[\uDD00-\uDD2C\uDD37-\uDD3D\uDD4E\uDE90-\uDEAD\uDEC0-\uDEEB]|\uD839[\uDFE0-\uDFE6\uDFE8-\uDFEB\uDFED\uDFEE\uDFF0-\uDFFE]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43\uDD4B]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDEDF\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF38\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A])\S*/g;
|
---|
| 4181 | /**
|
---|
| 4182 | * Transforms text to title case.
|
---|
| 4183 | * Capitalizes the first letter of each word and transforms the
|
---|
| 4184 | * rest of the word to lower case.
|
---|
| 4185 | * Words are delimited by any whitespace character, such as a space, tab, or line-feed character.
|
---|
| 4186 | *
|
---|
| 4187 | * @see `LowerCasePipe`
|
---|
| 4188 | * @see `UpperCasePipe`
|
---|
| 4189 | *
|
---|
| 4190 | * @usageNotes
|
---|
| 4191 | * The following example shows the result of transforming various strings into title case.
|
---|
| 4192 | *
|
---|
| 4193 | * <code-example path="common/pipes/ts/titlecase_pipe.ts" region='TitleCasePipe'></code-example>
|
---|
| 4194 | *
|
---|
| 4195 | * @ngModule CommonModule
|
---|
| 4196 | * @publicApi
|
---|
| 4197 | */
|
---|
| 4198 | class TitleCasePipe {
|
---|
| 4199 | transform(value) {
|
---|
| 4200 | if (value == null)
|
---|
| 4201 | return null;
|
---|
| 4202 | if (typeof value !== 'string') {
|
---|
| 4203 | throw invalidPipeArgumentError(TitleCasePipe, value);
|
---|
| 4204 | }
|
---|
| 4205 | return value.replace(unicodeWordMatch, (txt => txt[0].toUpperCase() + txt.substr(1).toLowerCase()));
|
---|
| 4206 | }
|
---|
| 4207 | }
|
---|
| 4208 | TitleCasePipe.decorators = [
|
---|
| 4209 | { type: Pipe, args: [{ name: 'titlecase' },] }
|
---|
| 4210 | ];
|
---|
| 4211 | /**
|
---|
| 4212 | * Transforms text to all upper case.
|
---|
| 4213 | * @see `LowerCasePipe`
|
---|
| 4214 | * @see `TitleCasePipe`
|
---|
| 4215 | *
|
---|
| 4216 | * @ngModule CommonModule
|
---|
| 4217 | * @publicApi
|
---|
| 4218 | */
|
---|
| 4219 | class UpperCasePipe {
|
---|
| 4220 | transform(value) {
|
---|
| 4221 | if (value == null)
|
---|
| 4222 | return null;
|
---|
| 4223 | if (typeof value !== 'string') {
|
---|
| 4224 | throw invalidPipeArgumentError(UpperCasePipe, value);
|
---|
| 4225 | }
|
---|
| 4226 | return value.toUpperCase();
|
---|
| 4227 | }
|
---|
| 4228 | }
|
---|
| 4229 | UpperCasePipe.decorators = [
|
---|
| 4230 | { type: Pipe, args: [{ name: 'uppercase' },] }
|
---|
| 4231 | ];
|
---|
| 4232 |
|
---|
| 4233 | /**
|
---|
| 4234 | * @license
|
---|
| 4235 | * Copyright Google LLC All Rights Reserved.
|
---|
| 4236 | *
|
---|
| 4237 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 4238 | * found in the LICENSE file at https://angular.io/license
|
---|
| 4239 | */
|
---|
| 4240 | // clang-format off
|
---|
| 4241 | /**
|
---|
| 4242 | * @ngModule CommonModule
|
---|
| 4243 | * @description
|
---|
| 4244 | *
|
---|
| 4245 | * Formats a date value according to locale rules.
|
---|
| 4246 | *
|
---|
| 4247 | * `DatePipe` is executed only when it detects a pure change to the input value.
|
---|
| 4248 | * A pure change is either a change to a primitive input value
|
---|
| 4249 | * (such as `String`, `Number`, `Boolean`, or `Symbol`),
|
---|
| 4250 | * or a changed object reference (such as `Date`, `Array`, `Function`, or `Object`).
|
---|
| 4251 | *
|
---|
| 4252 | * Note that mutating a `Date` object does not cause the pipe to be rendered again.
|
---|
| 4253 | * To ensure that the pipe is executed, you must create a new `Date` object.
|
---|
| 4254 | *
|
---|
| 4255 | * Only the `en-US` locale data comes with Angular. To localize dates
|
---|
| 4256 | * in another language, you must import the corresponding locale data.
|
---|
| 4257 | * See the [I18n guide](guide/i18n-common-format-data-locale) for more information.
|
---|
| 4258 | *
|
---|
| 4259 | * @see `formatDate()`
|
---|
| 4260 | *
|
---|
| 4261 | *
|
---|
| 4262 | * @usageNotes
|
---|
| 4263 | *
|
---|
| 4264 | * The result of this pipe is not reevaluated when the input is mutated. To avoid the need to
|
---|
| 4265 | * reformat the date on every change-detection cycle, treat the date as an immutable object
|
---|
| 4266 | * and change the reference when the pipe needs to run again.
|
---|
| 4267 | *
|
---|
| 4268 | * ### Pre-defined format options
|
---|
| 4269 | *
|
---|
| 4270 | * | Option | Equivalent to | Examples (given in `en-US` locale) |
|
---|
| 4271 | * |---------------|-------------------------------------|-------------------------------------------------|
|
---|
| 4272 | * | `'short'` | `'M/d/yy, h:mm a'` | `6/15/15, 9:03 AM` |
|
---|
| 4273 | * | `'medium'` | `'MMM d, y, h:mm:ss a'` | `Jun 15, 2015, 9:03:01 AM` |
|
---|
| 4274 | * | `'long'` | `'MMMM d, y, h:mm:ss a z'` | `June 15, 2015 at 9:03:01 AM GMT+1` |
|
---|
| 4275 | * | `'full'` | `'EEEE, MMMM d, y, h:mm:ss a zzzz'` | `Monday, June 15, 2015 at 9:03:01 AM GMT+01:00` |
|
---|
| 4276 | * | `'shortDate'` | `'M/d/yy'` | `6/15/15` |
|
---|
| 4277 | * | `'mediumDate'`| `'MMM d, y'` | `Jun 15, 2015` |
|
---|
| 4278 | * | `'longDate'` | `'MMMM d, y'` | `June 15, 2015` |
|
---|
| 4279 | * | `'fullDate'` | `'EEEE, MMMM d, y'` | `Monday, June 15, 2015` |
|
---|
| 4280 | * | `'shortTime'` | `'h:mm a'` | `9:03 AM` |
|
---|
| 4281 | * | `'mediumTime'`| `'h:mm:ss a'` | `9:03:01 AM` |
|
---|
| 4282 | * | `'longTime'` | `'h:mm:ss a z'` | `9:03:01 AM GMT+1` |
|
---|
| 4283 | * | `'fullTime'` | `'h:mm:ss a zzzz'` | `9:03:01 AM GMT+01:00` |
|
---|
| 4284 | *
|
---|
| 4285 | * ### Custom format options
|
---|
| 4286 | *
|
---|
| 4287 | * You can construct a format string using symbols to specify the components
|
---|
| 4288 | * of a date-time value, as described in the following table.
|
---|
| 4289 | * Format details depend on the locale.
|
---|
| 4290 | * Fields marked with (*) are only available in the extra data set for the given locale.
|
---|
| 4291 | *
|
---|
| 4292 | * | Field type | Format | Description | Example Value |
|
---|
| 4293 | * |-------------------- |-------------|---------------------------------------------------------------|------------------------------------------------------------|
|
---|
| 4294 | * | Era | G, GG & GGG | Abbreviated | AD |
|
---|
| 4295 | * | | GGGG | Wide | Anno Domini |
|
---|
| 4296 | * | | GGGGG | Narrow | A |
|
---|
| 4297 | * | Year | y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 |
|
---|
| 4298 | * | | yy | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 |
|
---|
| 4299 | * | | yyy | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 |
|
---|
| 4300 | * | | yyyy | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 |
|
---|
| 4301 | * | Week-numbering year | Y | Numeric: minimum digits | 2, 20, 201, 2017, 20173 |
|
---|
| 4302 | * | | YY | Numeric: 2 digits + zero padded | 02, 20, 01, 17, 73 |
|
---|
| 4303 | * | | YYY | Numeric: 3 digits + zero padded | 002, 020, 201, 2017, 20173 |
|
---|
| 4304 | * | | YYYY | Numeric: 4 digits or more + zero padded | 0002, 0020, 0201, 2017, 20173 |
|
---|
| 4305 | * | Month | M | Numeric: 1 digit | 9, 12 |
|
---|
| 4306 | * | | MM | Numeric: 2 digits + zero padded | 09, 12 |
|
---|
| 4307 | * | | MMM | Abbreviated | Sep |
|
---|
| 4308 | * | | MMMM | Wide | September |
|
---|
| 4309 | * | | MMMMM | Narrow | S |
|
---|
| 4310 | * | Month standalone | L | Numeric: 1 digit | 9, 12 |
|
---|
| 4311 | * | | LL | Numeric: 2 digits + zero padded | 09, 12 |
|
---|
| 4312 | * | | LLL | Abbreviated | Sep |
|
---|
| 4313 | * | | LLLL | Wide | September |
|
---|
| 4314 | * | | LLLLL | Narrow | S |
|
---|
| 4315 | * | Week of year | w | Numeric: minimum digits | 1... 53 |
|
---|
| 4316 | * | | ww | Numeric: 2 digits + zero padded | 01... 53 |
|
---|
| 4317 | * | Week of month | W | Numeric: 1 digit | 1... 5 |
|
---|
| 4318 | * | Day of month | d | Numeric: minimum digits | 1 |
|
---|
| 4319 | * | | dd | Numeric: 2 digits + zero padded | 01 |
|
---|
| 4320 | * | Week day | E, EE & EEE | Abbreviated | Tue |
|
---|
| 4321 | * | | EEEE | Wide | Tuesday |
|
---|
| 4322 | * | | EEEEE | Narrow | T |
|
---|
| 4323 | * | | EEEEEE | Short | Tu |
|
---|
| 4324 | * | Week day standalone | c, cc | Numeric: 1 digit | 2 |
|
---|
| 4325 | * | | ccc | Abbreviated | Tue |
|
---|
| 4326 | * | | cccc | Wide | Tuesday |
|
---|
| 4327 | * | | ccccc | Narrow | T |
|
---|
| 4328 | * | | cccccc | Short | Tu |
|
---|
| 4329 | * | Period | a, aa & aaa | Abbreviated | am/pm or AM/PM |
|
---|
| 4330 | * | | aaaa | Wide (fallback to `a` when missing) | ante meridiem/post meridiem |
|
---|
| 4331 | * | | aaaaa | Narrow | a/p |
|
---|
| 4332 | * | Period* | B, BB & BBB | Abbreviated | mid. |
|
---|
| 4333 | * | | BBBB | Wide | am, pm, midnight, noon, morning, afternoon, evening, night |
|
---|
| 4334 | * | | BBBBB | Narrow | md |
|
---|
| 4335 | * | Period standalone* | b, bb & bbb | Abbreviated | mid. |
|
---|
| 4336 | * | | bbbb | Wide | am, pm, midnight, noon, morning, afternoon, evening, night |
|
---|
| 4337 | * | | bbbbb | Narrow | md |
|
---|
| 4338 | * | Hour 1-12 | h | Numeric: minimum digits | 1, 12 |
|
---|
| 4339 | * | | hh | Numeric: 2 digits + zero padded | 01, 12 |
|
---|
| 4340 | * | Hour 0-23 | H | Numeric: minimum digits | 0, 23 |
|
---|
| 4341 | * | | HH | Numeric: 2 digits + zero padded | 00, 23 |
|
---|
| 4342 | * | Minute | m | Numeric: minimum digits | 8, 59 |
|
---|
| 4343 | * | | mm | Numeric: 2 digits + zero padded | 08, 59 |
|
---|
| 4344 | * | Second | s | Numeric: minimum digits | 0... 59 |
|
---|
| 4345 | * | | ss | Numeric: 2 digits + zero padded | 00... 59 |
|
---|
| 4346 | * | Fractional seconds | S | Numeric: 1 digit | 0... 9 |
|
---|
| 4347 | * | | SS | Numeric: 2 digits + zero padded | 00... 99 |
|
---|
| 4348 | * | | SSS | Numeric: 3 digits + zero padded (= milliseconds) | 000... 999 |
|
---|
| 4349 | * | Zone | z, zz & zzz | Short specific non location format (fallback to O) | GMT-8 |
|
---|
| 4350 | * | | zzzz | Long specific non location format (fallback to OOOO) | GMT-08:00 |
|
---|
| 4351 | * | | Z, ZZ & ZZZ | ISO8601 basic format | -0800 |
|
---|
| 4352 | * | | ZZZZ | Long localized GMT format | GMT-8:00 |
|
---|
| 4353 | * | | ZZZZZ | ISO8601 extended format + Z indicator for offset 0 (= XXXXX) | -08:00 |
|
---|
| 4354 | * | | O, OO & OOO | Short localized GMT format | GMT-8 |
|
---|
| 4355 | * | | OOOO | Long localized GMT format | GMT-08:00 |
|
---|
| 4356 | *
|
---|
| 4357 | *
|
---|
| 4358 | * ### Format examples
|
---|
| 4359 | *
|
---|
| 4360 | * These examples transform a date into various formats,
|
---|
| 4361 | * assuming that `dateObj` is a JavaScript `Date` object for
|
---|
| 4362 | * year: 2015, month: 6, day: 15, hour: 21, minute: 43, second: 11,
|
---|
| 4363 | * given in the local time for the `en-US` locale.
|
---|
| 4364 | *
|
---|
| 4365 | * ```
|
---|
| 4366 | * {{ dateObj | date }} // output is 'Jun 15, 2015'
|
---|
| 4367 | * {{ dateObj | date:'medium' }} // output is 'Jun 15, 2015, 9:43:11 PM'
|
---|
| 4368 | * {{ dateObj | date:'shortTime' }} // output is '9:43 PM'
|
---|
| 4369 | * {{ dateObj | date:'mm:ss' }} // output is '43:11'
|
---|
| 4370 | * ```
|
---|
| 4371 | *
|
---|
| 4372 | * ### Usage example
|
---|
| 4373 | *
|
---|
| 4374 | * The following component uses a date pipe to display the current date in different formats.
|
---|
| 4375 | *
|
---|
| 4376 | * ```
|
---|
| 4377 | * @Component({
|
---|
| 4378 | * selector: 'date-pipe',
|
---|
| 4379 | * template: `<div>
|
---|
| 4380 | * <p>Today is {{today | date}}</p>
|
---|
| 4381 | * <p>Or if you prefer, {{today | date:'fullDate'}}</p>
|
---|
| 4382 | * <p>The time is {{today | date:'h:mm a z'}}</p>
|
---|
| 4383 | * </div>`
|
---|
| 4384 | * })
|
---|
| 4385 | * // Get the current date and time as a date-time value.
|
---|
| 4386 | * export class DatePipeComponent {
|
---|
| 4387 | * today: number = Date.now();
|
---|
| 4388 | * }
|
---|
| 4389 | * ```
|
---|
| 4390 | *
|
---|
| 4391 | * @publicApi
|
---|
| 4392 | */
|
---|
| 4393 | // clang-format on
|
---|
| 4394 | class DatePipe {
|
---|
| 4395 | constructor(locale) {
|
---|
| 4396 | this.locale = locale;
|
---|
| 4397 | }
|
---|
| 4398 | transform(value, format = 'mediumDate', timezone, locale) {
|
---|
| 4399 | if (value == null || value === '' || value !== value)
|
---|
| 4400 | return null;
|
---|
| 4401 | try {
|
---|
| 4402 | return formatDate(value, format, locale || this.locale, timezone);
|
---|
| 4403 | }
|
---|
| 4404 | catch (error) {
|
---|
| 4405 | throw invalidPipeArgumentError(DatePipe, error.message);
|
---|
| 4406 | }
|
---|
| 4407 | }
|
---|
| 4408 | }
|
---|
| 4409 | DatePipe.decorators = [
|
---|
| 4410 | { type: Pipe, args: [{ name: 'date', pure: true },] }
|
---|
| 4411 | ];
|
---|
| 4412 | DatePipe.ctorParameters = () => [
|
---|
| 4413 | { type: String, decorators: [{ type: Inject, args: [LOCALE_ID,] }] }
|
---|
| 4414 | ];
|
---|
| 4415 |
|
---|
| 4416 | /**
|
---|
| 4417 | * @license
|
---|
| 4418 | * Copyright Google LLC All Rights Reserved.
|
---|
| 4419 | *
|
---|
| 4420 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 4421 | * found in the LICENSE file at https://angular.io/license
|
---|
| 4422 | */
|
---|
| 4423 | const _INTERPOLATION_REGEXP = /#/g;
|
---|
| 4424 | /**
|
---|
| 4425 | * @ngModule CommonModule
|
---|
| 4426 | * @description
|
---|
| 4427 | *
|
---|
| 4428 | * Maps a value to a string that pluralizes the value according to locale rules.
|
---|
| 4429 | *
|
---|
| 4430 | * @usageNotes
|
---|
| 4431 | *
|
---|
| 4432 | * ### Example
|
---|
| 4433 | *
|
---|
| 4434 | * {@example common/pipes/ts/i18n_pipe.ts region='I18nPluralPipeComponent'}
|
---|
| 4435 | *
|
---|
| 4436 | * @publicApi
|
---|
| 4437 | */
|
---|
| 4438 | class I18nPluralPipe {
|
---|
| 4439 | constructor(_localization) {
|
---|
| 4440 | this._localization = _localization;
|
---|
| 4441 | }
|
---|
| 4442 | /**
|
---|
| 4443 | * @param value the number to be formatted
|
---|
| 4444 | * @param pluralMap an object that mimics the ICU format, see
|
---|
| 4445 | * http://userguide.icu-project.org/formatparse/messages.
|
---|
| 4446 | * @param locale a `string` defining the locale to use (uses the current {@link LOCALE_ID} by
|
---|
| 4447 | * default).
|
---|
| 4448 | */
|
---|
| 4449 | transform(value, pluralMap, locale) {
|
---|
| 4450 | if (value == null)
|
---|
| 4451 | return '';
|
---|
| 4452 | if (typeof pluralMap !== 'object' || pluralMap === null) {
|
---|
| 4453 | throw invalidPipeArgumentError(I18nPluralPipe, pluralMap);
|
---|
| 4454 | }
|
---|
| 4455 | const key = getPluralCategory(value, Object.keys(pluralMap), this._localization, locale);
|
---|
| 4456 | return pluralMap[key].replace(_INTERPOLATION_REGEXP, value.toString());
|
---|
| 4457 | }
|
---|
| 4458 | }
|
---|
| 4459 | I18nPluralPipe.decorators = [
|
---|
| 4460 | { type: Pipe, args: [{ name: 'i18nPlural', pure: true },] }
|
---|
| 4461 | ];
|
---|
| 4462 | I18nPluralPipe.ctorParameters = () => [
|
---|
| 4463 | { type: NgLocalization }
|
---|
| 4464 | ];
|
---|
| 4465 |
|
---|
| 4466 | /**
|
---|
| 4467 | * @license
|
---|
| 4468 | * Copyright Google LLC All Rights Reserved.
|
---|
| 4469 | *
|
---|
| 4470 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 4471 | * found in the LICENSE file at https://angular.io/license
|
---|
| 4472 | */
|
---|
| 4473 | /**
|
---|
| 4474 | * @ngModule CommonModule
|
---|
| 4475 | * @description
|
---|
| 4476 | *
|
---|
| 4477 | * Generic selector that displays the string that matches the current value.
|
---|
| 4478 | *
|
---|
| 4479 | * If none of the keys of the `mapping` match the `value`, then the content
|
---|
| 4480 | * of the `other` key is returned when present, otherwise an empty string is returned.
|
---|
| 4481 | *
|
---|
| 4482 | * @usageNotes
|
---|
| 4483 | *
|
---|
| 4484 | * ### Example
|
---|
| 4485 | *
|
---|
| 4486 | * {@example common/pipes/ts/i18n_pipe.ts region='I18nSelectPipeComponent'}
|
---|
| 4487 | *
|
---|
| 4488 | * @publicApi
|
---|
| 4489 | */
|
---|
| 4490 | class I18nSelectPipe {
|
---|
| 4491 | /**
|
---|
| 4492 | * @param value a string to be internationalized.
|
---|
| 4493 | * @param mapping an object that indicates the text that should be displayed
|
---|
| 4494 | * for different values of the provided `value`.
|
---|
| 4495 | */
|
---|
| 4496 | transform(value, mapping) {
|
---|
| 4497 | if (value == null)
|
---|
| 4498 | return '';
|
---|
| 4499 | if (typeof mapping !== 'object' || typeof value !== 'string') {
|
---|
| 4500 | throw invalidPipeArgumentError(I18nSelectPipe, mapping);
|
---|
| 4501 | }
|
---|
| 4502 | if (mapping.hasOwnProperty(value)) {
|
---|
| 4503 | return mapping[value];
|
---|
| 4504 | }
|
---|
| 4505 | if (mapping.hasOwnProperty('other')) {
|
---|
| 4506 | return mapping['other'];
|
---|
| 4507 | }
|
---|
| 4508 | return '';
|
---|
| 4509 | }
|
---|
| 4510 | }
|
---|
| 4511 | I18nSelectPipe.decorators = [
|
---|
| 4512 | { type: Pipe, args: [{ name: 'i18nSelect', pure: true },] }
|
---|
| 4513 | ];
|
---|
| 4514 |
|
---|
| 4515 | /**
|
---|
| 4516 | * @license
|
---|
| 4517 | * Copyright Google LLC All Rights Reserved.
|
---|
| 4518 | *
|
---|
| 4519 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 4520 | * found in the LICENSE file at https://angular.io/license
|
---|
| 4521 | */
|
---|
| 4522 | /**
|
---|
| 4523 | * @ngModule CommonModule
|
---|
| 4524 | * @description
|
---|
| 4525 | *
|
---|
| 4526 | * Converts a value into its JSON-format representation. Useful for debugging.
|
---|
| 4527 | *
|
---|
| 4528 | * @usageNotes
|
---|
| 4529 | *
|
---|
| 4530 | * The following component uses a JSON pipe to convert an object
|
---|
| 4531 | * to JSON format, and displays the string in both formats for comparison.
|
---|
| 4532 | *
|
---|
| 4533 | * {@example common/pipes/ts/json_pipe.ts region='JsonPipe'}
|
---|
| 4534 | *
|
---|
| 4535 | * @publicApi
|
---|
| 4536 | */
|
---|
| 4537 | class JsonPipe {
|
---|
| 4538 | /**
|
---|
| 4539 | * @param value A value of any type to convert into a JSON-format string.
|
---|
| 4540 | */
|
---|
| 4541 | transform(value) {
|
---|
| 4542 | return JSON.stringify(value, null, 2);
|
---|
| 4543 | }
|
---|
| 4544 | }
|
---|
| 4545 | JsonPipe.decorators = [
|
---|
| 4546 | { type: Pipe, args: [{ name: 'json', pure: false },] }
|
---|
| 4547 | ];
|
---|
| 4548 |
|
---|
| 4549 | /**
|
---|
| 4550 | * @license
|
---|
| 4551 | * Copyright Google LLC All Rights Reserved.
|
---|
| 4552 | *
|
---|
| 4553 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 4554 | * found in the LICENSE file at https://angular.io/license
|
---|
| 4555 | */
|
---|
| 4556 | function makeKeyValuePair(key, value) {
|
---|
| 4557 | return { key: key, value: value };
|
---|
| 4558 | }
|
---|
| 4559 | /**
|
---|
| 4560 | * @ngModule CommonModule
|
---|
| 4561 | * @description
|
---|
| 4562 | *
|
---|
| 4563 | * Transforms Object or Map into an array of key value pairs.
|
---|
| 4564 | *
|
---|
| 4565 | * The output array will be ordered by keys.
|
---|
| 4566 | * By default the comparator will be by Unicode point value.
|
---|
| 4567 | * You can optionally pass a compareFn if your keys are complex types.
|
---|
| 4568 | *
|
---|
| 4569 | * @usageNotes
|
---|
| 4570 | * ### Examples
|
---|
| 4571 | *
|
---|
| 4572 | * This examples show how an Object or a Map can be iterated by ngFor with the use of this
|
---|
| 4573 | * keyvalue pipe.
|
---|
| 4574 | *
|
---|
| 4575 | * {@example common/pipes/ts/keyvalue_pipe.ts region='KeyValuePipe'}
|
---|
| 4576 | *
|
---|
| 4577 | * @publicApi
|
---|
| 4578 | */
|
---|
| 4579 | class KeyValuePipe {
|
---|
| 4580 | constructor(differs) {
|
---|
| 4581 | this.differs = differs;
|
---|
| 4582 | this.keyValues = [];
|
---|
| 4583 | this.compareFn = defaultComparator;
|
---|
| 4584 | }
|
---|
| 4585 | transform(input, compareFn = defaultComparator) {
|
---|
| 4586 | if (!input || (!(input instanceof Map) && typeof input !== 'object')) {
|
---|
| 4587 | return null;
|
---|
| 4588 | }
|
---|
| 4589 | if (!this.differ) {
|
---|
| 4590 | // make a differ for whatever type we've been passed in
|
---|
| 4591 | this.differ = this.differs.find(input).create();
|
---|
| 4592 | }
|
---|
| 4593 | const differChanges = this.differ.diff(input);
|
---|
| 4594 | const compareFnChanged = compareFn !== this.compareFn;
|
---|
| 4595 | if (differChanges) {
|
---|
| 4596 | this.keyValues = [];
|
---|
| 4597 | differChanges.forEachItem((r) => {
|
---|
| 4598 | this.keyValues.push(makeKeyValuePair(r.key, r.currentValue));
|
---|
| 4599 | });
|
---|
| 4600 | }
|
---|
| 4601 | if (differChanges || compareFnChanged) {
|
---|
| 4602 | this.keyValues.sort(compareFn);
|
---|
| 4603 | this.compareFn = compareFn;
|
---|
| 4604 | }
|
---|
| 4605 | return this.keyValues;
|
---|
| 4606 | }
|
---|
| 4607 | }
|
---|
| 4608 | KeyValuePipe.decorators = [
|
---|
| 4609 | { type: Pipe, args: [{ name: 'keyvalue', pure: false },] }
|
---|
| 4610 | ];
|
---|
| 4611 | KeyValuePipe.ctorParameters = () => [
|
---|
| 4612 | { type: KeyValueDiffers }
|
---|
| 4613 | ];
|
---|
| 4614 | function defaultComparator(keyValueA, keyValueB) {
|
---|
| 4615 | const a = keyValueA.key;
|
---|
| 4616 | const b = keyValueB.key;
|
---|
| 4617 | // if same exit with 0;
|
---|
| 4618 | if (a === b)
|
---|
| 4619 | return 0;
|
---|
| 4620 | // make sure that undefined are at the end of the sort.
|
---|
| 4621 | if (a === undefined)
|
---|
| 4622 | return 1;
|
---|
| 4623 | if (b === undefined)
|
---|
| 4624 | return -1;
|
---|
| 4625 | // make sure that nulls are at the end of the sort.
|
---|
| 4626 | if (a === null)
|
---|
| 4627 | return 1;
|
---|
| 4628 | if (b === null)
|
---|
| 4629 | return -1;
|
---|
| 4630 | if (typeof a == 'string' && typeof b == 'string') {
|
---|
| 4631 | return a < b ? -1 : 1;
|
---|
| 4632 | }
|
---|
| 4633 | if (typeof a == 'number' && typeof b == 'number') {
|
---|
| 4634 | return a - b;
|
---|
| 4635 | }
|
---|
| 4636 | if (typeof a == 'boolean' && typeof b == 'boolean') {
|
---|
| 4637 | return a < b ? -1 : 1;
|
---|
| 4638 | }
|
---|
| 4639 | // `a` and `b` are of different types. Compare their string values.
|
---|
| 4640 | const aString = String(a);
|
---|
| 4641 | const bString = String(b);
|
---|
| 4642 | return aString == bString ? 0 : aString < bString ? -1 : 1;
|
---|
| 4643 | }
|
---|
| 4644 |
|
---|
| 4645 | /**
|
---|
| 4646 | * @license
|
---|
| 4647 | * Copyright Google LLC All Rights Reserved.
|
---|
| 4648 | *
|
---|
| 4649 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 4650 | * found in the LICENSE file at https://angular.io/license
|
---|
| 4651 | */
|
---|
| 4652 | /**
|
---|
| 4653 | * @ngModule CommonModule
|
---|
| 4654 | * @description
|
---|
| 4655 | *
|
---|
| 4656 | * Formats a value according to digit options and locale rules.
|
---|
| 4657 | * Locale determines group sizing and separator,
|
---|
| 4658 | * decimal point character, and other locale-specific configurations.
|
---|
| 4659 | *
|
---|
| 4660 | * @see `formatNumber()`
|
---|
| 4661 | *
|
---|
| 4662 | * @usageNotes
|
---|
| 4663 | *
|
---|
| 4664 | * ### digitsInfo
|
---|
| 4665 | *
|
---|
| 4666 | * The value's decimal representation is specified by the `digitsInfo`
|
---|
| 4667 | * parameter, written in the following format:<br>
|
---|
| 4668 | *
|
---|
| 4669 | * ```
|
---|
| 4670 | * {minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}
|
---|
| 4671 | * ```
|
---|
| 4672 | *
|
---|
| 4673 | * - `minIntegerDigits`:
|
---|
| 4674 | * The minimum number of integer digits before the decimal point.
|
---|
| 4675 | * Default is 1.
|
---|
| 4676 | *
|
---|
| 4677 | * - `minFractionDigits`:
|
---|
| 4678 | * The minimum number of digits after the decimal point.
|
---|
| 4679 | * Default is 0.
|
---|
| 4680 | *
|
---|
| 4681 | * - `maxFractionDigits`:
|
---|
| 4682 | * The maximum number of digits after the decimal point.
|
---|
| 4683 | * Default is 3.
|
---|
| 4684 | *
|
---|
| 4685 | * If the formatted value is truncated it will be rounded using the "to-nearest" method:
|
---|
| 4686 | *
|
---|
| 4687 | * ```
|
---|
| 4688 | * {{3.6 | number: '1.0-0'}}
|
---|
| 4689 | * <!--will output '4'-->
|
---|
| 4690 | *
|
---|
| 4691 | * {{-3.6 | number:'1.0-0'}}
|
---|
| 4692 | * <!--will output '-4'-->
|
---|
| 4693 | * ```
|
---|
| 4694 | *
|
---|
| 4695 | * ### locale
|
---|
| 4696 | *
|
---|
| 4697 | * `locale` will format a value according to locale rules.
|
---|
| 4698 | * Locale determines group sizing and separator,
|
---|
| 4699 | * decimal point character, and other locale-specific configurations.
|
---|
| 4700 | *
|
---|
| 4701 | * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default.
|
---|
| 4702 | *
|
---|
| 4703 | * See [Setting your app locale](guide/i18n-common-locale-id).
|
---|
| 4704 | *
|
---|
| 4705 | * ### Example
|
---|
| 4706 | *
|
---|
| 4707 | * The following code shows how the pipe transforms values
|
---|
| 4708 | * according to various format specifications,
|
---|
| 4709 | * where the caller's default locale is `en-US`.
|
---|
| 4710 | *
|
---|
| 4711 | * <code-example path="common/pipes/ts/number_pipe.ts" region='NumberPipe'></code-example>
|
---|
| 4712 | *
|
---|
| 4713 | * @publicApi
|
---|
| 4714 | */
|
---|
| 4715 | class DecimalPipe {
|
---|
| 4716 | constructor(_locale) {
|
---|
| 4717 | this._locale = _locale;
|
---|
| 4718 | }
|
---|
| 4719 | /**
|
---|
| 4720 | * @param value The value to be formatted.
|
---|
| 4721 | * @param digitsInfo Sets digit and decimal representation.
|
---|
| 4722 | * [See more](#digitsinfo).
|
---|
| 4723 | * @param locale Specifies what locale format rules to use.
|
---|
| 4724 | * [See more](#locale).
|
---|
| 4725 | */
|
---|
| 4726 | transform(value, digitsInfo, locale) {
|
---|
| 4727 | if (!isValue(value))
|
---|
| 4728 | return null;
|
---|
| 4729 | locale = locale || this._locale;
|
---|
| 4730 | try {
|
---|
| 4731 | const num = strToNumber(value);
|
---|
| 4732 | return formatNumber(num, locale, digitsInfo);
|
---|
| 4733 | }
|
---|
| 4734 | catch (error) {
|
---|
| 4735 | throw invalidPipeArgumentError(DecimalPipe, error.message);
|
---|
| 4736 | }
|
---|
| 4737 | }
|
---|
| 4738 | }
|
---|
| 4739 | DecimalPipe.decorators = [
|
---|
| 4740 | { type: Pipe, args: [{ name: 'number' },] }
|
---|
| 4741 | ];
|
---|
| 4742 | DecimalPipe.ctorParameters = () => [
|
---|
| 4743 | { type: String, decorators: [{ type: Inject, args: [LOCALE_ID,] }] }
|
---|
| 4744 | ];
|
---|
| 4745 | /**
|
---|
| 4746 | * @ngModule CommonModule
|
---|
| 4747 | * @description
|
---|
| 4748 | *
|
---|
| 4749 | * Transforms a number to a percentage
|
---|
| 4750 | * string, formatted according to locale rules that determine group sizing and
|
---|
| 4751 | * separator, decimal-point character, and other locale-specific
|
---|
| 4752 | * configurations.
|
---|
| 4753 | *
|
---|
| 4754 | * @see `formatPercent()`
|
---|
| 4755 | *
|
---|
| 4756 | * @usageNotes
|
---|
| 4757 | * The following code shows how the pipe transforms numbers
|
---|
| 4758 | * into text strings, according to various format specifications,
|
---|
| 4759 | * where the caller's default locale is `en-US`.
|
---|
| 4760 | *
|
---|
| 4761 | * <code-example path="common/pipes/ts/percent_pipe.ts" region='PercentPipe'></code-example>
|
---|
| 4762 | *
|
---|
| 4763 | * @publicApi
|
---|
| 4764 | */
|
---|
| 4765 | class PercentPipe {
|
---|
| 4766 | constructor(_locale) {
|
---|
| 4767 | this._locale = _locale;
|
---|
| 4768 | }
|
---|
| 4769 | /**
|
---|
| 4770 | *
|
---|
| 4771 | * @param value The number to be formatted as a percentage.
|
---|
| 4772 | * @param digitsInfo Decimal representation options, specified by a string
|
---|
| 4773 | * in the following format:<br>
|
---|
| 4774 | * <code>{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}</code>.
|
---|
| 4775 | * - `minIntegerDigits`: The minimum number of integer digits before the decimal point.
|
---|
| 4776 | * Default is `1`.
|
---|
| 4777 | * - `minFractionDigits`: The minimum number of digits after the decimal point.
|
---|
| 4778 | * Default is `0`.
|
---|
| 4779 | * - `maxFractionDigits`: The maximum number of digits after the decimal point.
|
---|
| 4780 | * Default is `0`.
|
---|
| 4781 | * @param locale A locale code for the locale format rules to use.
|
---|
| 4782 | * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default.
|
---|
| 4783 | * See [Setting your app locale](guide/i18n-common-locale-id).
|
---|
| 4784 | */
|
---|
| 4785 | transform(value, digitsInfo, locale) {
|
---|
| 4786 | if (!isValue(value))
|
---|
| 4787 | return null;
|
---|
| 4788 | locale = locale || this._locale;
|
---|
| 4789 | try {
|
---|
| 4790 | const num = strToNumber(value);
|
---|
| 4791 | return formatPercent(num, locale, digitsInfo);
|
---|
| 4792 | }
|
---|
| 4793 | catch (error) {
|
---|
| 4794 | throw invalidPipeArgumentError(PercentPipe, error.message);
|
---|
| 4795 | }
|
---|
| 4796 | }
|
---|
| 4797 | }
|
---|
| 4798 | PercentPipe.decorators = [
|
---|
| 4799 | { type: Pipe, args: [{ name: 'percent' },] }
|
---|
| 4800 | ];
|
---|
| 4801 | PercentPipe.ctorParameters = () => [
|
---|
| 4802 | { type: String, decorators: [{ type: Inject, args: [LOCALE_ID,] }] }
|
---|
| 4803 | ];
|
---|
| 4804 | /**
|
---|
| 4805 | * @ngModule CommonModule
|
---|
| 4806 | * @description
|
---|
| 4807 | *
|
---|
| 4808 | * Transforms a number to a currency string, formatted according to locale rules
|
---|
| 4809 | * that determine group sizing and separator, decimal-point character,
|
---|
| 4810 | * and other locale-specific configurations.
|
---|
| 4811 | *
|
---|
| 4812 | * {@a currency-code-deprecation}
|
---|
| 4813 | * <div class="alert is-helpful">
|
---|
| 4814 | *
|
---|
| 4815 | * **Deprecation notice:**
|
---|
| 4816 | *
|
---|
| 4817 | * The default currency code is currently always `USD` but this is deprecated from v9.
|
---|
| 4818 | *
|
---|
| 4819 | * **In v11 the default currency code will be taken from the current locale identified by
|
---|
| 4820 | * the `LOCALE_ID` token. See the [i18n guide](guide/i18n-common-locale-id) for
|
---|
| 4821 | * more information.**
|
---|
| 4822 | *
|
---|
| 4823 | * If you need the previous behavior then set it by creating a `DEFAULT_CURRENCY_CODE` provider in
|
---|
| 4824 | * your application `NgModule`:
|
---|
| 4825 | *
|
---|
| 4826 | * ```ts
|
---|
| 4827 | * {provide: DEFAULT_CURRENCY_CODE, useValue: 'USD'}
|
---|
| 4828 | * ```
|
---|
| 4829 | *
|
---|
| 4830 | * </div>
|
---|
| 4831 | *
|
---|
| 4832 | * @see `getCurrencySymbol()`
|
---|
| 4833 | * @see `formatCurrency()`
|
---|
| 4834 | *
|
---|
| 4835 | * @usageNotes
|
---|
| 4836 | * The following code shows how the pipe transforms numbers
|
---|
| 4837 | * into text strings, according to various format specifications,
|
---|
| 4838 | * where the caller's default locale is `en-US`.
|
---|
| 4839 | *
|
---|
| 4840 | * <code-example path="common/pipes/ts/currency_pipe.ts" region='CurrencyPipe'></code-example>
|
---|
| 4841 | *
|
---|
| 4842 | * @publicApi
|
---|
| 4843 | */
|
---|
| 4844 | class CurrencyPipe {
|
---|
| 4845 | constructor(_locale, _defaultCurrencyCode = 'USD') {
|
---|
| 4846 | this._locale = _locale;
|
---|
| 4847 | this._defaultCurrencyCode = _defaultCurrencyCode;
|
---|
| 4848 | }
|
---|
| 4849 | /**
|
---|
| 4850 | *
|
---|
| 4851 | * @param value The number to be formatted as currency.
|
---|
| 4852 | * @param currencyCode The [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) currency code,
|
---|
| 4853 | * such as `USD` for the US dollar and `EUR` for the euro. The default currency code can be
|
---|
| 4854 | * configured using the `DEFAULT_CURRENCY_CODE` injection token.
|
---|
| 4855 | * @param display The format for the currency indicator. One of the following:
|
---|
| 4856 | * - `code`: Show the code (such as `USD`).
|
---|
| 4857 | * - `symbol`(default): Show the symbol (such as `$`).
|
---|
| 4858 | * - `symbol-narrow`: Use the narrow symbol for locales that have two symbols for their
|
---|
| 4859 | * currency.
|
---|
| 4860 | * For example, the Canadian dollar CAD has the symbol `CA$` and the symbol-narrow `$`. If the
|
---|
| 4861 | * locale has no narrow symbol, uses the standard symbol for the locale.
|
---|
| 4862 | * - String: Use the given string value instead of a code or a symbol.
|
---|
| 4863 | * For example, an empty string will suppress the currency & symbol.
|
---|
| 4864 | * - Boolean (marked deprecated in v5): `true` for symbol and false for `code`.
|
---|
| 4865 | *
|
---|
| 4866 | * @param digitsInfo Decimal representation options, specified by a string
|
---|
| 4867 | * in the following format:<br>
|
---|
| 4868 | * <code>{minIntegerDigits}.{minFractionDigits}-{maxFractionDigits}</code>.
|
---|
| 4869 | * - `minIntegerDigits`: The minimum number of integer digits before the decimal point.
|
---|
| 4870 | * Default is `1`.
|
---|
| 4871 | * - `minFractionDigits`: The minimum number of digits after the decimal point.
|
---|
| 4872 | * Default is `2`.
|
---|
| 4873 | * - `maxFractionDigits`: The maximum number of digits after the decimal point.
|
---|
| 4874 | * Default is `2`.
|
---|
| 4875 | * If not provided, the number will be formatted with the proper amount of digits,
|
---|
| 4876 | * depending on what the [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) specifies.
|
---|
| 4877 | * For example, the Canadian dollar has 2 digits, whereas the Chilean peso has none.
|
---|
| 4878 | * @param locale A locale code for the locale format rules to use.
|
---|
| 4879 | * When not supplied, uses the value of `LOCALE_ID`, which is `en-US` by default.
|
---|
| 4880 | * See [Setting your app locale](guide/i18n-common-locale-id).
|
---|
| 4881 | */
|
---|
| 4882 | transform(value, currencyCode = this._defaultCurrencyCode, display = 'symbol', digitsInfo, locale) {
|
---|
| 4883 | if (!isValue(value))
|
---|
| 4884 | return null;
|
---|
| 4885 | locale = locale || this._locale;
|
---|
| 4886 | if (typeof display === 'boolean') {
|
---|
| 4887 | if ((typeof ngDevMode === 'undefined' || ngDevMode) && console && console.warn) {
|
---|
| 4888 | console.warn(`Warning: the currency pipe has been changed in Angular v5. The symbolDisplay option (third parameter) is now a string instead of a boolean. The accepted values are "code", "symbol" or "symbol-narrow".`);
|
---|
| 4889 | }
|
---|
| 4890 | display = display ? 'symbol' : 'code';
|
---|
| 4891 | }
|
---|
| 4892 | let currency = currencyCode || this._defaultCurrencyCode;
|
---|
| 4893 | if (display !== 'code') {
|
---|
| 4894 | if (display === 'symbol' || display === 'symbol-narrow') {
|
---|
| 4895 | currency = getCurrencySymbol(currency, display === 'symbol' ? 'wide' : 'narrow', locale);
|
---|
| 4896 | }
|
---|
| 4897 | else {
|
---|
| 4898 | currency = display;
|
---|
| 4899 | }
|
---|
| 4900 | }
|
---|
| 4901 | try {
|
---|
| 4902 | const num = strToNumber(value);
|
---|
| 4903 | return formatCurrency(num, locale, currency, currencyCode, digitsInfo);
|
---|
| 4904 | }
|
---|
| 4905 | catch (error) {
|
---|
| 4906 | throw invalidPipeArgumentError(CurrencyPipe, error.message);
|
---|
| 4907 | }
|
---|
| 4908 | }
|
---|
| 4909 | }
|
---|
| 4910 | CurrencyPipe.decorators = [
|
---|
| 4911 | { type: Pipe, args: [{ name: 'currency' },] }
|
---|
| 4912 | ];
|
---|
| 4913 | CurrencyPipe.ctorParameters = () => [
|
---|
| 4914 | { type: String, decorators: [{ type: Inject, args: [LOCALE_ID,] }] },
|
---|
| 4915 | { type: String, decorators: [{ type: Inject, args: [DEFAULT_CURRENCY_CODE,] }] }
|
---|
| 4916 | ];
|
---|
| 4917 | function isValue(value) {
|
---|
| 4918 | return !(value == null || value === '' || value !== value);
|
---|
| 4919 | }
|
---|
| 4920 | /**
|
---|
| 4921 | * Transforms a string into a number (if needed).
|
---|
| 4922 | */
|
---|
| 4923 | function strToNumber(value) {
|
---|
| 4924 | // Convert strings to numbers
|
---|
| 4925 | if (typeof value === 'string' && !isNaN(Number(value) - parseFloat(value))) {
|
---|
| 4926 | return Number(value);
|
---|
| 4927 | }
|
---|
| 4928 | if (typeof value !== 'number') {
|
---|
| 4929 | throw new Error(`${value} is not a number`);
|
---|
| 4930 | }
|
---|
| 4931 | return value;
|
---|
| 4932 | }
|
---|
| 4933 |
|
---|
| 4934 | /**
|
---|
| 4935 | * @license
|
---|
| 4936 | * Copyright Google LLC All Rights Reserved.
|
---|
| 4937 | *
|
---|
| 4938 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 4939 | * found in the LICENSE file at https://angular.io/license
|
---|
| 4940 | */
|
---|
| 4941 | /**
|
---|
| 4942 | * @ngModule CommonModule
|
---|
| 4943 | * @description
|
---|
| 4944 | *
|
---|
| 4945 | * Creates a new `Array` or `String` containing a subset (slice) of the elements.
|
---|
| 4946 | *
|
---|
| 4947 | * @usageNotes
|
---|
| 4948 | *
|
---|
| 4949 | * All behavior is based on the expected behavior of the JavaScript API `Array.prototype.slice()`
|
---|
| 4950 | * and `String.prototype.slice()`.
|
---|
| 4951 | *
|
---|
| 4952 | * When operating on an `Array`, the returned `Array` is always a copy even when all
|
---|
| 4953 | * the elements are being returned.
|
---|
| 4954 | *
|
---|
| 4955 | * When operating on a blank value, the pipe returns the blank value.
|
---|
| 4956 | *
|
---|
| 4957 | * ### List Example
|
---|
| 4958 | *
|
---|
| 4959 | * This `ngFor` example:
|
---|
| 4960 | *
|
---|
| 4961 | * {@example common/pipes/ts/slice_pipe.ts region='SlicePipe_list'}
|
---|
| 4962 | *
|
---|
| 4963 | * produces the following:
|
---|
| 4964 | *
|
---|
| 4965 | * ```html
|
---|
| 4966 | * <li>b</li>
|
---|
| 4967 | * <li>c</li>
|
---|
| 4968 | * ```
|
---|
| 4969 | *
|
---|
| 4970 | * ### String Examples
|
---|
| 4971 | *
|
---|
| 4972 | * {@example common/pipes/ts/slice_pipe.ts region='SlicePipe_string'}
|
---|
| 4973 | *
|
---|
| 4974 | * @publicApi
|
---|
| 4975 | */
|
---|
| 4976 | class SlicePipe {
|
---|
| 4977 | transform(value, start, end) {
|
---|
| 4978 | if (value == null)
|
---|
| 4979 | return null;
|
---|
| 4980 | if (!this.supports(value)) {
|
---|
| 4981 | throw invalidPipeArgumentError(SlicePipe, value);
|
---|
| 4982 | }
|
---|
| 4983 | return value.slice(start, end);
|
---|
| 4984 | }
|
---|
| 4985 | supports(obj) {
|
---|
| 4986 | return typeof obj === 'string' || Array.isArray(obj);
|
---|
| 4987 | }
|
---|
| 4988 | }
|
---|
| 4989 | SlicePipe.decorators = [
|
---|
| 4990 | { type: Pipe, args: [{ name: 'slice', pure: false },] }
|
---|
| 4991 | ];
|
---|
| 4992 |
|
---|
| 4993 | /**
|
---|
| 4994 | * @license
|
---|
| 4995 | * Copyright Google LLC All Rights Reserved.
|
---|
| 4996 | *
|
---|
| 4997 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 4998 | * found in the LICENSE file at https://angular.io/license
|
---|
| 4999 | */
|
---|
| 5000 | /**
|
---|
| 5001 | * A collection of Angular pipes that are likely to be used in each and every application.
|
---|
| 5002 | */
|
---|
| 5003 | const COMMON_PIPES = [
|
---|
| 5004 | AsyncPipe,
|
---|
| 5005 | UpperCasePipe,
|
---|
| 5006 | LowerCasePipe,
|
---|
| 5007 | JsonPipe,
|
---|
| 5008 | SlicePipe,
|
---|
| 5009 | DecimalPipe,
|
---|
| 5010 | PercentPipe,
|
---|
| 5011 | TitleCasePipe,
|
---|
| 5012 | CurrencyPipe,
|
---|
| 5013 | DatePipe,
|
---|
| 5014 | I18nPluralPipe,
|
---|
| 5015 | I18nSelectPipe,
|
---|
| 5016 | KeyValuePipe,
|
---|
| 5017 | ];
|
---|
| 5018 |
|
---|
| 5019 | /**
|
---|
| 5020 | * @license
|
---|
| 5021 | * Copyright Google LLC All Rights Reserved.
|
---|
| 5022 | *
|
---|
| 5023 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 5024 | * found in the LICENSE file at https://angular.io/license
|
---|
| 5025 | */
|
---|
| 5026 | // Note: This does not contain the location providers,
|
---|
| 5027 | // as they need some platform specific implementations to work.
|
---|
| 5028 | /**
|
---|
| 5029 | * Exports all the basic Angular directives and pipes,
|
---|
| 5030 | * such as `NgIf`, `NgForOf`, `DecimalPipe`, and so on.
|
---|
| 5031 | * Re-exported by `BrowserModule`, which is included automatically in the root
|
---|
| 5032 | * `AppModule` when you create a new app with the CLI `new` command.
|
---|
| 5033 | *
|
---|
| 5034 | * * The `providers` options configure the NgModule's injector to provide
|
---|
| 5035 | * localization dependencies to members.
|
---|
| 5036 | * * The `exports` options make the declared directives and pipes available for import
|
---|
| 5037 | * by other NgModules.
|
---|
| 5038 | *
|
---|
| 5039 | * @publicApi
|
---|
| 5040 | */
|
---|
| 5041 | class CommonModule {
|
---|
| 5042 | }
|
---|
| 5043 | CommonModule.decorators = [
|
---|
| 5044 | { type: NgModule, args: [{
|
---|
| 5045 | declarations: [COMMON_DIRECTIVES, COMMON_PIPES],
|
---|
| 5046 | exports: [COMMON_DIRECTIVES, COMMON_PIPES],
|
---|
| 5047 | providers: [
|
---|
| 5048 | { provide: NgLocalization, useClass: NgLocaleLocalization },
|
---|
| 5049 | ],
|
---|
| 5050 | },] }
|
---|
| 5051 | ];
|
---|
| 5052 |
|
---|
| 5053 | /**
|
---|
| 5054 | * @license
|
---|
| 5055 | * Copyright Google LLC All Rights Reserved.
|
---|
| 5056 | *
|
---|
| 5057 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 5058 | * found in the LICENSE file at https://angular.io/license
|
---|
| 5059 | */
|
---|
| 5060 | const PLATFORM_BROWSER_ID = 'browser';
|
---|
| 5061 | const PLATFORM_SERVER_ID = 'server';
|
---|
| 5062 | const PLATFORM_WORKER_APP_ID = 'browserWorkerApp';
|
---|
| 5063 | const PLATFORM_WORKER_UI_ID = 'browserWorkerUi';
|
---|
| 5064 | /**
|
---|
| 5065 | * Returns whether a platform id represents a browser platform.
|
---|
| 5066 | * @publicApi
|
---|
| 5067 | */
|
---|
| 5068 | function isPlatformBrowser(platformId) {
|
---|
| 5069 | return platformId === PLATFORM_BROWSER_ID;
|
---|
| 5070 | }
|
---|
| 5071 | /**
|
---|
| 5072 | * Returns whether a platform id represents a server platform.
|
---|
| 5073 | * @publicApi
|
---|
| 5074 | */
|
---|
| 5075 | function isPlatformServer(platformId) {
|
---|
| 5076 | return platformId === PLATFORM_SERVER_ID;
|
---|
| 5077 | }
|
---|
| 5078 | /**
|
---|
| 5079 | * Returns whether a platform id represents a web worker app platform.
|
---|
| 5080 | * @publicApi
|
---|
| 5081 | */
|
---|
| 5082 | function isPlatformWorkerApp(platformId) {
|
---|
| 5083 | return platformId === PLATFORM_WORKER_APP_ID;
|
---|
| 5084 | }
|
---|
| 5085 | /**
|
---|
| 5086 | * Returns whether a platform id represents a web worker UI platform.
|
---|
| 5087 | * @publicApi
|
---|
| 5088 | */
|
---|
| 5089 | function isPlatformWorkerUi(platformId) {
|
---|
| 5090 | return platformId === PLATFORM_WORKER_UI_ID;
|
---|
| 5091 | }
|
---|
| 5092 |
|
---|
| 5093 | /**
|
---|
| 5094 | * @license
|
---|
| 5095 | * Copyright Google LLC All Rights Reserved.
|
---|
| 5096 | *
|
---|
| 5097 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 5098 | * found in the LICENSE file at https://angular.io/license
|
---|
| 5099 | */
|
---|
| 5100 | /**
|
---|
| 5101 | * @publicApi
|
---|
| 5102 | */
|
---|
[e29cc2e] | 5103 | const VERSION = new Version('12.2.13');
|
---|
[6a3a178] | 5104 |
|
---|
| 5105 | /**
|
---|
| 5106 | * @license
|
---|
| 5107 | * Copyright Google LLC All Rights Reserved.
|
---|
| 5108 | *
|
---|
| 5109 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 5110 | * found in the LICENSE file at https://angular.io/license
|
---|
| 5111 | */
|
---|
| 5112 | /**
|
---|
| 5113 | * Defines a scroll position manager. Implemented by `BrowserViewportScroller`.
|
---|
| 5114 | *
|
---|
| 5115 | * @publicApi
|
---|
| 5116 | */
|
---|
| 5117 | class ViewportScroller {
|
---|
| 5118 | }
|
---|
| 5119 | // De-sugared tree-shakable injection
|
---|
| 5120 | // See #23917
|
---|
| 5121 | /** @nocollapse */
|
---|
| 5122 | ViewportScroller.ɵprov = ɵɵdefineInjectable({
|
---|
| 5123 | token: ViewportScroller,
|
---|
| 5124 | providedIn: 'root',
|
---|
| 5125 | factory: () => new BrowserViewportScroller(ɵɵinject(DOCUMENT), window)
|
---|
| 5126 | });
|
---|
| 5127 | /**
|
---|
| 5128 | * Manages the scroll position for a browser window.
|
---|
| 5129 | */
|
---|
| 5130 | class BrowserViewportScroller {
|
---|
| 5131 | constructor(document, window) {
|
---|
| 5132 | this.document = document;
|
---|
| 5133 | this.window = window;
|
---|
| 5134 | this.offset = () => [0, 0];
|
---|
| 5135 | }
|
---|
| 5136 | /**
|
---|
| 5137 | * Configures the top offset used when scrolling to an anchor.
|
---|
| 5138 | * @param offset A position in screen coordinates (a tuple with x and y values)
|
---|
| 5139 | * or a function that returns the top offset position.
|
---|
| 5140 | *
|
---|
| 5141 | */
|
---|
| 5142 | setOffset(offset) {
|
---|
| 5143 | if (Array.isArray(offset)) {
|
---|
| 5144 | this.offset = () => offset;
|
---|
| 5145 | }
|
---|
| 5146 | else {
|
---|
| 5147 | this.offset = offset;
|
---|
| 5148 | }
|
---|
| 5149 | }
|
---|
| 5150 | /**
|
---|
| 5151 | * Retrieves the current scroll position.
|
---|
| 5152 | * @returns The position in screen coordinates.
|
---|
| 5153 | */
|
---|
| 5154 | getScrollPosition() {
|
---|
| 5155 | if (this.supportsScrolling()) {
|
---|
| 5156 | return [this.window.pageXOffset, this.window.pageYOffset];
|
---|
| 5157 | }
|
---|
| 5158 | else {
|
---|
| 5159 | return [0, 0];
|
---|
| 5160 | }
|
---|
| 5161 | }
|
---|
| 5162 | /**
|
---|
| 5163 | * Sets the scroll position.
|
---|
| 5164 | * @param position The new position in screen coordinates.
|
---|
| 5165 | */
|
---|
| 5166 | scrollToPosition(position) {
|
---|
| 5167 | if (this.supportsScrolling()) {
|
---|
| 5168 | this.window.scrollTo(position[0], position[1]);
|
---|
| 5169 | }
|
---|
| 5170 | }
|
---|
| 5171 | /**
|
---|
| 5172 | * Scrolls to an element and attempts to focus the element.
|
---|
| 5173 | *
|
---|
| 5174 | * Note that the function name here is misleading in that the target string may be an ID for a
|
---|
| 5175 | * non-anchor element.
|
---|
| 5176 | *
|
---|
| 5177 | * @param target The ID of an element or name of the anchor.
|
---|
| 5178 | *
|
---|
| 5179 | * @see https://html.spec.whatwg.org/#the-indicated-part-of-the-document
|
---|
| 5180 | * @see https://html.spec.whatwg.org/#scroll-to-fragid
|
---|
| 5181 | */
|
---|
| 5182 | scrollToAnchor(target) {
|
---|
| 5183 | if (!this.supportsScrolling()) {
|
---|
| 5184 | return;
|
---|
| 5185 | }
|
---|
| 5186 | const elSelected = findAnchorFromDocument(this.document, target);
|
---|
| 5187 | if (elSelected) {
|
---|
| 5188 | this.scrollToElement(elSelected);
|
---|
| 5189 | // After scrolling to the element, the spec dictates that we follow the focus steps for the
|
---|
| 5190 | // target. Rather than following the robust steps, simply attempt focus.
|
---|
| 5191 | this.attemptFocus(elSelected);
|
---|
| 5192 | }
|
---|
| 5193 | }
|
---|
| 5194 | /**
|
---|
| 5195 | * Disables automatic scroll restoration provided by the browser.
|
---|
| 5196 | */
|
---|
| 5197 | setHistoryScrollRestoration(scrollRestoration) {
|
---|
| 5198 | if (this.supportScrollRestoration()) {
|
---|
| 5199 | const history = this.window.history;
|
---|
| 5200 | if (history && history.scrollRestoration) {
|
---|
| 5201 | history.scrollRestoration = scrollRestoration;
|
---|
| 5202 | }
|
---|
| 5203 | }
|
---|
| 5204 | }
|
---|
| 5205 | /**
|
---|
| 5206 | * Scrolls to an element using the native offset and the specified offset set on this scroller.
|
---|
| 5207 | *
|
---|
| 5208 | * The offset can be used when we know that there is a floating header and scrolling naively to an
|
---|
| 5209 | * element (ex: `scrollIntoView`) leaves the element hidden behind the floating header.
|
---|
| 5210 | */
|
---|
| 5211 | scrollToElement(el) {
|
---|
| 5212 | const rect = el.getBoundingClientRect();
|
---|
| 5213 | const left = rect.left + this.window.pageXOffset;
|
---|
| 5214 | const top = rect.top + this.window.pageYOffset;
|
---|
| 5215 | const offset = this.offset();
|
---|
| 5216 | this.window.scrollTo(left - offset[0], top - offset[1]);
|
---|
| 5217 | }
|
---|
| 5218 | /**
|
---|
| 5219 | * Calls `focus` on the `focusTarget` and returns `true` if the element was focused successfully.
|
---|
| 5220 | *
|
---|
| 5221 | * If `false`, further steps may be necessary to determine a valid substitute to be focused
|
---|
| 5222 | * instead.
|
---|
| 5223 | *
|
---|
| 5224 | * @see https://html.spec.whatwg.org/#get-the-focusable-area
|
---|
| 5225 | * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLOrForeignElement/focus
|
---|
| 5226 | * @see https://html.spec.whatwg.org/#focusable-area
|
---|
| 5227 | */
|
---|
| 5228 | attemptFocus(focusTarget) {
|
---|
| 5229 | focusTarget.focus();
|
---|
| 5230 | return this.document.activeElement === focusTarget;
|
---|
| 5231 | }
|
---|
| 5232 | /**
|
---|
| 5233 | * We only support scroll restoration when we can get a hold of window.
|
---|
| 5234 | * This means that we do not support this behavior when running in a web worker.
|
---|
| 5235 | *
|
---|
| 5236 | * Lifting this restriction right now would require more changes in the dom adapter.
|
---|
| 5237 | * Since webworkers aren't widely used, we will lift it once RouterScroller is
|
---|
| 5238 | * battle-tested.
|
---|
| 5239 | */
|
---|
| 5240 | supportScrollRestoration() {
|
---|
| 5241 | try {
|
---|
| 5242 | if (!this.supportsScrolling()) {
|
---|
| 5243 | return false;
|
---|
| 5244 | }
|
---|
| 5245 | // The `scrollRestoration` property could be on the `history` instance or its prototype.
|
---|
| 5246 | const scrollRestorationDescriptor = getScrollRestorationProperty(this.window.history) ||
|
---|
| 5247 | getScrollRestorationProperty(Object.getPrototypeOf(this.window.history));
|
---|
| 5248 | // We can write to the `scrollRestoration` property if it is a writable data field or it has a
|
---|
| 5249 | // setter function.
|
---|
| 5250 | return !!scrollRestorationDescriptor &&
|
---|
| 5251 | !!(scrollRestorationDescriptor.writable || scrollRestorationDescriptor.set);
|
---|
| 5252 | }
|
---|
| 5253 | catch (_a) {
|
---|
| 5254 | return false;
|
---|
| 5255 | }
|
---|
| 5256 | }
|
---|
| 5257 | supportsScrolling() {
|
---|
| 5258 | try {
|
---|
| 5259 | return !!this.window && !!this.window.scrollTo && 'pageXOffset' in this.window;
|
---|
| 5260 | }
|
---|
| 5261 | catch (_a) {
|
---|
| 5262 | return false;
|
---|
| 5263 | }
|
---|
| 5264 | }
|
---|
| 5265 | }
|
---|
| 5266 | function getScrollRestorationProperty(obj) {
|
---|
| 5267 | return Object.getOwnPropertyDescriptor(obj, 'scrollRestoration');
|
---|
| 5268 | }
|
---|
| 5269 | function findAnchorFromDocument(document, target) {
|
---|
| 5270 | const documentResult = document.getElementById(target) || document.getElementsByName(target)[0];
|
---|
| 5271 | if (documentResult) {
|
---|
| 5272 | return documentResult;
|
---|
| 5273 | }
|
---|
| 5274 | // `getElementById` and `getElementsByName` won't pierce through the shadow DOM so we
|
---|
| 5275 | // have to traverse the DOM manually and do the lookup through the shadow roots.
|
---|
| 5276 | if (typeof document.createTreeWalker === 'function' && document.body &&
|
---|
| 5277 | (document.body.createShadowRoot || document.body.attachShadow)) {
|
---|
| 5278 | const treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT);
|
---|
| 5279 | let currentNode = treeWalker.currentNode;
|
---|
| 5280 | while (currentNode) {
|
---|
| 5281 | const shadowRoot = currentNode.shadowRoot;
|
---|
| 5282 | if (shadowRoot) {
|
---|
| 5283 | // Note that `ShadowRoot` doesn't support `getElementsByName`
|
---|
| 5284 | // so we have to fall back to `querySelector`.
|
---|
| 5285 | const result = shadowRoot.getElementById(target) || shadowRoot.querySelector(`[name="${target}"]`);
|
---|
| 5286 | if (result) {
|
---|
| 5287 | return result;
|
---|
| 5288 | }
|
---|
| 5289 | }
|
---|
| 5290 | currentNode = treeWalker.nextNode();
|
---|
| 5291 | }
|
---|
| 5292 | }
|
---|
| 5293 | return null;
|
---|
| 5294 | }
|
---|
| 5295 | /**
|
---|
| 5296 | * Provides an empty implementation of the viewport scroller.
|
---|
| 5297 | */
|
---|
| 5298 | class NullViewportScroller {
|
---|
| 5299 | /**
|
---|
| 5300 | * Empty implementation
|
---|
| 5301 | */
|
---|
| 5302 | setOffset(offset) { }
|
---|
| 5303 | /**
|
---|
| 5304 | * Empty implementation
|
---|
| 5305 | */
|
---|
| 5306 | getScrollPosition() {
|
---|
| 5307 | return [0, 0];
|
---|
| 5308 | }
|
---|
| 5309 | /**
|
---|
| 5310 | * Empty implementation
|
---|
| 5311 | */
|
---|
| 5312 | scrollToPosition(position) { }
|
---|
| 5313 | /**
|
---|
| 5314 | * Empty implementation
|
---|
| 5315 | */
|
---|
| 5316 | scrollToAnchor(anchor) { }
|
---|
| 5317 | /**
|
---|
| 5318 | * Empty implementation
|
---|
| 5319 | */
|
---|
| 5320 | setHistoryScrollRestoration(scrollRestoration) { }
|
---|
| 5321 | }
|
---|
| 5322 |
|
---|
| 5323 | /**
|
---|
| 5324 | * @license
|
---|
| 5325 | * Copyright Google LLC All Rights Reserved.
|
---|
| 5326 | *
|
---|
| 5327 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 5328 | * found in the LICENSE file at https://angular.io/license
|
---|
| 5329 | */
|
---|
| 5330 | /**
|
---|
| 5331 | * A wrapper around the `XMLHttpRequest` constructor.
|
---|
| 5332 | *
|
---|
| 5333 | * @publicApi
|
---|
| 5334 | */
|
---|
| 5335 | class XhrFactory {
|
---|
| 5336 | }
|
---|
| 5337 |
|
---|
| 5338 | /**
|
---|
| 5339 | * @license
|
---|
| 5340 | * Copyright Google LLC All Rights Reserved.
|
---|
| 5341 | *
|
---|
| 5342 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 5343 | * found in the LICENSE file at https://angular.io/license
|
---|
| 5344 | */
|
---|
| 5345 |
|
---|
| 5346 | /**
|
---|
| 5347 | * @license
|
---|
| 5348 | * Copyright Google LLC All Rights Reserved.
|
---|
| 5349 | *
|
---|
| 5350 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 5351 | * found in the LICENSE file at https://angular.io/license
|
---|
| 5352 | */
|
---|
| 5353 | // This file only reexports content of the `src` folder. Keep it that way.
|
---|
| 5354 |
|
---|
| 5355 | /**
|
---|
| 5356 | * @license
|
---|
| 5357 | * Copyright Google LLC All Rights Reserved.
|
---|
| 5358 | *
|
---|
| 5359 | * Use of this source code is governed by an MIT-style license that can be
|
---|
| 5360 | * found in the LICENSE file at https://angular.io/license
|
---|
| 5361 | */
|
---|
| 5362 |
|
---|
| 5363 | /**
|
---|
| 5364 | * Generated bundle index. Do not edit.
|
---|
| 5365 | */
|
---|
| 5366 |
|
---|
| 5367 | export { APP_BASE_HREF, AsyncPipe, CommonModule, CurrencyPipe, DOCUMENT, DatePipe, DecimalPipe, FormStyle, FormatWidth, HashLocationStrategy, I18nPluralPipe, I18nSelectPipe, JsonPipe, KeyValuePipe, LOCATION_INITIALIZED, Location, LocationStrategy, LowerCasePipe, NgClass, NgComponentOutlet, NgForOf, NgForOfContext, NgIf, NgIfContext, NgLocaleLocalization, NgLocalization, NgPlural, NgPluralCase, NgStyle, NgSwitch, NgSwitchCase, NgSwitchDefault, NgTemplateOutlet, NumberFormatStyle, NumberSymbol, PathLocationStrategy, PercentPipe, PlatformLocation, Plural, SlicePipe, TitleCasePipe, TranslationWidth, UpperCasePipe, VERSION, ViewportScroller, WeekDay, XhrFactory, formatCurrency, formatDate, formatNumber, formatPercent, getCurrencySymbol, getLocaleCurrencyCode, getLocaleCurrencyName, getLocaleCurrencySymbol, getLocaleDateFormat, getLocaleDateTimeFormat, getLocaleDayNames, getLocaleDayPeriods, getLocaleDirection, getLocaleEraNames, getLocaleExtraDayPeriodRules, getLocaleExtraDayPeriods, getLocaleFirstDayOfWeek, getLocaleId, getLocaleMonthNames, getLocaleNumberFormat, getLocaleNumberSymbol, getLocalePluralCase, getLocaleTimeFormat, getLocaleWeekEndRange, getNumberOfCurrencyDigits, isPlatformBrowser, isPlatformServer, isPlatformWorkerApp, isPlatformWorkerUi, registerLocaleData, BrowserPlatformLocation as ɵBrowserPlatformLocation, DomAdapter as ɵDomAdapter, NullViewportScroller as ɵNullViewportScroller, PLATFORM_BROWSER_ID as ɵPLATFORM_BROWSER_ID, PLATFORM_SERVER_ID as ɵPLATFORM_SERVER_ID, PLATFORM_WORKER_APP_ID as ɵPLATFORM_WORKER_APP_ID, PLATFORM_WORKER_UI_ID as ɵPLATFORM_WORKER_UI_ID, useBrowserPlatformLocation as ɵangular_packages_common_common_a, createBrowserPlatformLocation as ɵangular_packages_common_common_b, createLocation as ɵangular_packages_common_common_c, provideLocationStrategy as ɵangular_packages_common_common_d, COMMON_DIRECTIVES as ɵangular_packages_common_common_e, COMMON_PIPES as ɵangular_packages_common_common_f, getDOM as ɵgetDOM, parseCookieValue as ɵparseCookieValue, setRootDomAdapter as ɵsetRootDomAdapter };
|
---|
| 5368 | //# sourceMappingURL=common.js.map
|
---|