@angular/common
Version:
Angular - commonly needed directives and services
580 lines • 82.5 kB
JavaScript
/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import { Directive, ElementRef, inject, Injector, Input, NgZone, Renderer2, ɵformatRuntimeError as formatRuntimeError, ɵRuntimeError as RuntimeError } from '@angular/core';
import { imgDirectiveDetails } from './error_helper';
import { IMAGE_LOADER } from './image_loaders/image_loader';
import { LCPImageObserver } from './lcp_image_observer';
import { PreconnectLinkChecker } from './preconnect_link_checker';
import * as i0 from "@angular/core";
/**
* When a Base64-encoded image is passed as an input to the `NgOptimizedImage` directive,
* an error is thrown. The image content (as a string) might be very long, thus making
* it hard to read an error message if the entire string is included. This const defines
* the number of characters that should be included into the error message. The rest
* of the content is truncated.
*/
const BASE64_IMG_MAX_LENGTH_IN_ERROR = 50;
/**
* RegExpr to determine whether a src in a srcset is using width descriptors.
* Should match something like: "100w, 200w".
*/
const VALID_WIDTH_DESCRIPTOR_SRCSET = /^((\s*\d+w\s*(,|$)){1,})$/;
/**
* RegExpr to determine whether a src in a srcset is using density descriptors.
* Should match something like: "1x, 2x, 50x". Also supports decimals like "1.5x, 1.50x".
*/
const VALID_DENSITY_DESCRIPTOR_SRCSET = /^((\s*\d+(\.\d+)?x\s*(,|$)){1,})$/;
/**
* Srcset values with a density descriptor higher than this value will actively
* throw an error. Such densities are not permitted as they cause image sizes
* to be unreasonably large and slow down LCP.
*/
export const ABSOLUTE_SRCSET_DENSITY_CAP = 3;
/**
* Used only in error message text to communicate best practices, as we will
* only throw based on the slightly more conservative ABSOLUTE_SRCSET_DENSITY_CAP.
*/
export const RECOMMENDED_SRCSET_DENSITY_CAP = 2;
/**
* Used to determine whether two aspect ratios are similar in value.
*/
const ASPECT_RATIO_TOLERANCE = .1;
/**
* Used to determine whether the image has been requested at an overly
* large size compared to the actual rendered image size (after taking
* into account a typical device pixel ratio). In pixels.
*/
const OVERSIZED_IMAGE_TOLERANCE = 1000;
/**
* Directive that improves image loading performance by enforcing best practices.
*
* `NgOptimizedImage` ensures that the loading of the Largest Contentful Paint (LCP) image is
* prioritized by:
* - Automatically setting the `fetchpriority` attribute on the `<img>` tag
* - Lazy loading non-priority images by default
* - Asserting that there is a corresponding preconnect link tag in the document head
*
* In addition, the directive:
* - Generates appropriate asset URLs if a corresponding `ImageLoader` function is provided
* - Requires that `width` and `height` are set
* - Warns if `width` or `height` have been set incorrectly
* - Warns if the image will be visually distorted when rendered
*
* @usageNotes
* The `NgOptimizedImage` directive is marked as [standalone](guide/standalone-components) and can
* be imported directly.
*
* Follow the steps below to enable and use the directive:
* 1. Import it into the necessary NgModule or a standalone Component.
* 2. Optionally provide an `ImageLoader` if you use an image hosting service.
* 3. Update the necessary `<img>` tags in templates and replace `src` attributes with `ngSrc`.
* Using a `ngSrc` allows the directive to control when the `src` gets set, which triggers an image
* download.
*
* Step 1: import the `NgOptimizedImage` directive.
*
* ```typescript
* import { NgOptimizedImage } from '@angular/common';
*
* // Include it into the necessary NgModule
* @NgModule({
* imports: [NgOptimizedImage],
* })
* class AppModule {}
*
* // ... or a standalone Component
* @Component({
* standalone: true
* imports: [NgOptimizedImage],
* })
* class MyStandaloneComponent {}
* ```
*
* Step 2: configure a loader.
*
* To use the **default loader**: no additional code changes are necessary. The URL returned by the
* generic loader will always match the value of "src". In other words, this loader applies no
* transformations to the resource URL and the value of the `ngSrc` attribute will be used as is.
*
* To use an existing loader for a **third-party image service**: add the provider factory for your
* chosen service to the `providers` array. In the example below, the Imgix loader is used:
*
* ```typescript
* import {provideImgixLoader} from '@angular/common';
*
* // Call the function and add the result to the `providers` array:
* providers: [
* provideImgixLoader("https://my.base.url/"),
* ],
* ```
*
* The `NgOptimizedImage` directive provides the following functions:
* - `provideCloudflareLoader`
* - `provideCloudinaryLoader`
* - `provideImageKitLoader`
* - `provideImgixLoader`
*
* If you use a different image provider, you can create a custom loader function as described
* below.
*
* To use a **custom loader**: provide your loader function as a value for the `IMAGE_LOADER` DI
* token.
*
* ```typescript
* import {IMAGE_LOADER, ImageLoaderConfig} from '@angular/common';
*
* // Configure the loader using the `IMAGE_LOADER` token.
* providers: [
* {
* provide: IMAGE_LOADER,
* useValue: (config: ImageLoaderConfig) => {
* return `https://example.com/${config.src}-${config.width}.jpg}`;
* }
* },
* ],
* ```
*
* Step 3: update `<img>` tags in templates to use `ngSrc` instead of `src`.
*
* ```
* <img ngSrc="logo.png" width="200" height="100">
* ```
*
* @publicApi
* @developerPreview
*/
export class NgOptimizedImage {
constructor() {
this.imageLoader = inject(IMAGE_LOADER);
this.renderer = inject(Renderer2);
this.imgElement = inject(ElementRef).nativeElement;
this.injector = inject(Injector);
// a LCP image observer - should be injected only in the dev mode
this.lcpObserver = ngDevMode ? this.injector.get(LCPImageObserver) : null;
/**
* Calculate the rewritten `src` once and store it.
* This is needed to avoid repetitive calculations and make sure the directive cleanup in the
* `ngOnDestroy` does not rely on the `IMAGE_LOADER` logic (which in turn can rely on some other
* instance that might be already destroyed).
*/
this._renderedSrc = null;
this._priority = false;
}
/**
* Previously, the `rawSrc` attribute was used to activate the directive.
* The attribute was renamed to `ngSrc` and this input just produces an error,
* suggesting to switch to `ngSrc` instead.
*
* This error should be removed in v15.
*
* @nodoc
* @deprecated Use `ngSrc` instead.
*/
set rawSrc(value) {
if (ngDevMode) {
throw new RuntimeError(2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(value, false)} the \`rawSrc\` attribute was used ` +
`to activate the directive. Newer version of the directive uses the \`ngSrc\` ` +
`attribute instead. Please replace \`rawSrc\` with \`ngSrc\` and ` +
`\`rawSrcset\` with \`ngSrcset\` attributes in the template to ` +
`enable image optimizations.`);
}
}
/**
* The intrinsic width of the image in pixels.
*/
set width(value) {
ngDevMode && assertGreaterThanZero(this, value, 'width');
this._width = inputToInteger(value);
}
get width() {
return this._width;
}
/**
* The intrinsic height of the image in pixels.
*/
set height(value) {
ngDevMode && assertGreaterThanZero(this, value, 'height');
this._height = inputToInteger(value);
}
get height() {
return this._height;
}
/**
* Indicates whether this image should have a high priority.
*/
set priority(value) {
this._priority = inputToBoolean(value);
}
get priority() {
return this._priority;
}
ngOnInit() {
if (ngDevMode) {
assertNonEmptyInput(this, 'ngSrc', this.ngSrc);
assertValidNgSrcset(this, this.ngSrcset);
assertNoConflictingSrc(this);
assertNoConflictingSrcset(this);
assertNotBase64Image(this);
assertNotBlobUrl(this);
assertNonEmptyWidthAndHeight(this);
assertValidLoadingInput(this);
assertNoImageDistortion(this, this.imgElement, this.renderer);
if (this.priority) {
const checker = this.injector.get(PreconnectLinkChecker);
checker.assertPreconnect(this.getRewrittenSrc(), this.ngSrc);
}
else {
// Monitor whether an image is an LCP element only in case
// the `priority` attribute is missing. Otherwise, an image
// has the necessary settings and no extra checks are required.
if (this.lcpObserver !== null) {
const ngZone = this.injector.get(NgZone);
ngZone.runOutsideAngular(() => {
this.lcpObserver.registerImage(this.getRewrittenSrc(), this.ngSrc);
});
}
}
}
this.setHostAttributes();
}
setHostAttributes() {
// Must set width/height explicitly in case they are bound (in which case they will
// only be reflected and not found by the browser)
this.setHostAttribute('width', this.width.toString());
this.setHostAttribute('height', this.height.toString());
this.setHostAttribute('loading', this.getLoadingBehavior());
this.setHostAttribute('fetchpriority', this.getFetchPriority());
// The `src` and `srcset` attributes should be set last since other attributes
// could affect the image's loading behavior.
this.setHostAttribute('src', this.getRewrittenSrc());
if (this.ngSrcset) {
this.setHostAttribute('srcset', this.getRewrittenSrcset());
}
}
ngOnChanges(changes) {
if (ngDevMode) {
assertNoPostInitInputChange(this, changes, ['ngSrc', 'ngSrcset', 'width', 'height', 'priority']);
}
}
getLoadingBehavior() {
if (!this.priority && this.loading !== undefined) {
return this.loading;
}
return this.priority ? 'eager' : 'lazy';
}
getFetchPriority() {
return this.priority ? 'high' : 'auto';
}
getRewrittenSrc() {
// ImageLoaderConfig supports setting a width property. However, we're not setting width here
// because if the developer uses rendered width instead of intrinsic width in the HTML width
// attribute, the image requested may be too small for 2x+ screens.
if (!this._renderedSrc) {
const imgConfig = { src: this.ngSrc };
// Cache calculated image src to reuse it later in the code.
this._renderedSrc = this.imageLoader(imgConfig);
}
return this._renderedSrc;
}
getRewrittenSrcset() {
const widthSrcSet = VALID_WIDTH_DESCRIPTOR_SRCSET.test(this.ngSrcset);
const finalSrcs = this.ngSrcset.split(',').filter(src => src !== '').map(srcStr => {
srcStr = srcStr.trim();
const width = widthSrcSet ? parseFloat(srcStr) : parseFloat(srcStr) * this.width;
return `${this.imageLoader({ src: this.ngSrc, width })} ${srcStr}`;
});
return finalSrcs.join(', ');
}
ngOnDestroy() {
if (ngDevMode) {
if (!this.priority && this._renderedSrc !== null && this.lcpObserver !== null) {
this.lcpObserver.unregisterImage(this._renderedSrc);
}
}
}
setHostAttribute(name, value) {
this.renderer.setAttribute(this.imgElement, name, value);
}
}
NgOptimizedImage.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "14.2.5", ngImport: i0, type: NgOptimizedImage, deps: [], target: i0.ɵɵFactoryTarget.Directive });
NgOptimizedImage.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "14.2.5", type: NgOptimizedImage, isStandalone: true, selector: "img[ngSrc],img[rawSrc]", inputs: { rawSrc: "rawSrc", ngSrc: "ngSrc", ngSrcset: "ngSrcset", width: "width", height: "height", loading: "loading", priority: "priority", src: "src", srcset: "srcset" }, usesOnChanges: true, ngImport: i0 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "14.2.5", ngImport: i0, type: NgOptimizedImage, decorators: [{
type: Directive,
args: [{
standalone: true,
selector: 'img[ngSrc],img[rawSrc]',
}]
}], propDecorators: { rawSrc: [{
type: Input
}], ngSrc: [{
type: Input
}], ngSrcset: [{
type: Input
}], width: [{
type: Input
}], height: [{
type: Input
}], loading: [{
type: Input
}], priority: [{
type: Input
}], src: [{
type: Input
}], srcset: [{
type: Input
}] } });
/***** Helpers *****/
/**
* Convert input value to integer.
*/
function inputToInteger(value) {
return typeof value === 'string' ? parseInt(value, 10) : value;
}
/**
* Convert input value to boolean.
*/
function inputToBoolean(value) {
return value != null && `${value}` !== 'false';
}
/***** Assert functions *****/
/**
* Verifies that there is no `src` set on a host element.
*/
function assertNoConflictingSrc(dir) {
if (dir.src) {
throw new RuntimeError(2950 /* RuntimeErrorCode.UNEXPECTED_SRC_ATTR */, `${imgDirectiveDetails(dir.ngSrc)} both \`src\` and \`ngSrc\` have been set. ` +
`Supplying both of these attributes breaks lazy loading. ` +
`The NgOptimizedImage directive sets \`src\` itself based on the value of \`ngSrc\`. ` +
`To fix this, please remove the \`src\` attribute.`);
}
}
/**
* Verifies that there is no `srcset` set on a host element.
*/
function assertNoConflictingSrcset(dir) {
if (dir.srcset) {
throw new RuntimeError(2951 /* RuntimeErrorCode.UNEXPECTED_SRCSET_ATTR */, `${imgDirectiveDetails(dir.ngSrc)} both \`srcset\` and \`ngSrcset\` have been set. ` +
`Supplying both of these attributes breaks lazy loading. ` +
`The NgOptimizedImage directive sets \`srcset\` itself based on the value of ` +
`\`ngSrcset\`. To fix this, please remove the \`srcset\` attribute.`);
}
}
/**
* Verifies that the `ngSrc` is not a Base64-encoded image.
*/
function assertNotBase64Image(dir) {
let ngSrc = dir.ngSrc.trim();
if (ngSrc.startsWith('data:')) {
if (ngSrc.length > BASE64_IMG_MAX_LENGTH_IN_ERROR) {
ngSrc = ngSrc.substring(0, BASE64_IMG_MAX_LENGTH_IN_ERROR) + '...';
}
throw new RuntimeError(2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc, false)} \`ngSrc\` is a Base64-encoded string ` +
`(${ngSrc}). NgOptimizedImage does not support Base64-encoded strings. ` +
`To fix this, disable the NgOptimizedImage directive for this element ` +
`by removing \`ngSrc\` and using a standard \`src\` attribute instead.`);
}
}
/**
* Verifies that the `ngSrc` is not a Blob URL.
*/
function assertNotBlobUrl(dir) {
const ngSrc = dir.ngSrc.trim();
if (ngSrc.startsWith('blob:')) {
throw new RuntimeError(2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} \`ngSrc\` was set to a blob URL (${ngSrc}). ` +
`Blob URLs are not supported by the NgOptimizedImage directive. ` +
`To fix this, disable the NgOptimizedImage directive for this element ` +
`by removing \`ngSrc\` and using a regular \`src\` attribute instead.`);
}
}
/**
* Verifies that the input is set to a non-empty string.
*/
function assertNonEmptyInput(dir, name, value) {
const isString = typeof value === 'string';
const isEmptyString = isString && value.trim() === '';
if (!isString || isEmptyString) {
throw new RuntimeError(2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} \`${name}\` has an invalid value ` +
`(\`${value}\`). To fix this, change the value to a non-empty string.`);
}
}
/**
* Verifies that the `ngSrcset` is in a valid format, e.g. "100w, 200w" or "1x, 2x".
*/
export function assertValidNgSrcset(dir, value) {
if (value == null)
return;
assertNonEmptyInput(dir, 'ngSrcset', value);
const stringVal = value;
const isValidWidthDescriptor = VALID_WIDTH_DESCRIPTOR_SRCSET.test(stringVal);
const isValidDensityDescriptor = VALID_DENSITY_DESCRIPTOR_SRCSET.test(stringVal);
if (isValidDensityDescriptor) {
assertUnderDensityCap(dir, stringVal);
}
const isValidSrcset = isValidWidthDescriptor || isValidDensityDescriptor;
if (!isValidSrcset) {
throw new RuntimeError(2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} \`ngSrcset\` has an invalid value (\`${value}\`). ` +
`To fix this, supply \`ngSrcset\` using a comma-separated list of one or more width ` +
`descriptors (e.g. "100w, 200w") or density descriptors (e.g. "1x, 2x").`);
}
}
function assertUnderDensityCap(dir, value) {
const underDensityCap = value.split(',').every(num => num === '' || parseFloat(num) <= ABSOLUTE_SRCSET_DENSITY_CAP);
if (!underDensityCap) {
throw new RuntimeError(2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} the \`ngSrcset\` contains an unsupported image density:` +
`\`${value}\`. NgOptimizedImage generally recommends a max image density of ` +
`${RECOMMENDED_SRCSET_DENSITY_CAP}x but supports image densities up to ` +
`${ABSOLUTE_SRCSET_DENSITY_CAP}x. The human eye cannot distinguish between image densities ` +
`greater than ${RECOMMENDED_SRCSET_DENSITY_CAP}x - which makes them unnecessary for ` +
`most use cases. Images that will be pinch-zoomed are typically the primary use case for ` +
`${ABSOLUTE_SRCSET_DENSITY_CAP}x images. Please remove the high density descriptor and try again.`);
}
}
/**
* Creates a `RuntimeError` instance to represent a situation when an input is set after
* the directive has initialized.
*/
function postInitInputChangeError(dir, inputName) {
return new RuntimeError(2953 /* RuntimeErrorCode.UNEXPECTED_INPUT_CHANGE */, `${imgDirectiveDetails(dir.ngSrc)} \`${inputName}\` was updated after initialization. ` +
`The NgOptimizedImage directive will not react to this input change. ` +
`To fix this, switch \`${inputName}\` a static value or wrap the image element ` +
`in an *ngIf that is gated on the necessary value.`);
}
/**
* Verify that none of the listed inputs has changed.
*/
function assertNoPostInitInputChange(dir, changes, inputs) {
inputs.forEach(input => {
const isUpdated = changes.hasOwnProperty(input);
if (isUpdated && !changes[input].isFirstChange()) {
if (input === 'ngSrc') {
// When the `ngSrc` input changes, we detect that only in the
// `ngOnChanges` hook, thus the `ngSrc` is already set. We use
// `ngSrc` in the error message, so we use a previous value, but
// not the updated one in it.
dir = { ngSrc: changes[input].previousValue };
}
throw postInitInputChangeError(dir, input);
}
});
}
/**
* Verifies that a specified input is a number greater than 0.
*/
function assertGreaterThanZero(dir, inputValue, inputName) {
const validNumber = typeof inputValue === 'number' && inputValue > 0;
const validString = typeof inputValue === 'string' && /^\d+$/.test(inputValue.trim()) && parseInt(inputValue) > 0;
if (!validNumber && !validString) {
throw new RuntimeError(2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} \`${inputName}\` has an invalid value ` +
`(\`${inputValue}\`). To fix this, provide \`${inputName}\` ` +
`as a number greater than 0.`);
}
}
/**
* Verifies that the rendered image is not visually distorted. Effectively this is checking:
* - Whether the "width" and "height" attributes reflect the actual dimensions of the image.
* - Whether image styling is "correct" (see below for a longer explanation).
*/
function assertNoImageDistortion(dir, img, renderer) {
const removeListenerFn = renderer.listen(img, 'load', () => {
removeListenerFn();
// TODO: `clientWidth`, `clientHeight`, `naturalWidth` and `naturalHeight`
// are typed as number, but we run `parseFloat` (which accepts strings only).
// Verify whether `parseFloat` is needed in the cases below.
const renderedWidth = parseFloat(img.clientWidth);
const renderedHeight = parseFloat(img.clientHeight);
const renderedAspectRatio = renderedWidth / renderedHeight;
const nonZeroRenderedDimensions = renderedWidth !== 0 && renderedHeight !== 0;
const intrinsicWidth = parseFloat(img.naturalWidth);
const intrinsicHeight = parseFloat(img.naturalHeight);
const intrinsicAspectRatio = intrinsicWidth / intrinsicHeight;
const suppliedWidth = dir.width;
const suppliedHeight = dir.height;
const suppliedAspectRatio = suppliedWidth / suppliedHeight;
// Tolerance is used to account for the impact of subpixel rendering.
// Due to subpixel rendering, the rendered, intrinsic, and supplied
// aspect ratios of a correctly configured image may not exactly match.
// For example, a `width=4030 height=3020` image might have a rendered
// size of "1062w, 796.48h". (An aspect ratio of 1.334... vs. 1.333...)
const inaccurateDimensions = Math.abs(suppliedAspectRatio - intrinsicAspectRatio) > ASPECT_RATIO_TOLERANCE;
const stylingDistortion = nonZeroRenderedDimensions &&
Math.abs(intrinsicAspectRatio - renderedAspectRatio) > ASPECT_RATIO_TOLERANCE;
if (inaccurateDimensions) {
console.warn(formatRuntimeError(2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} the aspect ratio of the image does not match ` +
`the aspect ratio indicated by the width and height attributes. ` +
`\nIntrinsic image size: ${intrinsicWidth}w x ${intrinsicHeight}h ` +
`(aspect-ratio: ${intrinsicAspectRatio}). \nSupplied width and height attributes: ` +
`${suppliedWidth}w x ${suppliedHeight}h (aspect-ratio: ${suppliedAspectRatio}). ` +
`\nTo fix this, update the width and height attributes.`));
}
else if (stylingDistortion) {
console.warn(formatRuntimeError(2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} the aspect ratio of the rendered image ` +
`does not match the image's intrinsic aspect ratio. ` +
`\nIntrinsic image size: ${intrinsicWidth}w x ${intrinsicHeight}h ` +
`(aspect-ratio: ${intrinsicAspectRatio}). \nRendered image size: ` +
`${renderedWidth}w x ${renderedHeight}h (aspect-ratio: ` +
`${renderedAspectRatio}). \nThis issue can occur if "width" and "height" ` +
`attributes are added to an image without updating the corresponding ` +
`image styling. To fix this, adjust image styling. In most cases, ` +
`adding "height: auto" or "width: auto" to the image styling will fix ` +
`this issue.`));
}
else if (!dir.ngSrcset && nonZeroRenderedDimensions) {
// If `ngSrcset` hasn't been set, sanity check the intrinsic size.
const recommendedWidth = RECOMMENDED_SRCSET_DENSITY_CAP * renderedWidth;
const recommendedHeight = RECOMMENDED_SRCSET_DENSITY_CAP * renderedHeight;
const oversizedWidth = (intrinsicWidth - recommendedWidth) >= OVERSIZED_IMAGE_TOLERANCE;
const oversizedHeight = (intrinsicHeight - recommendedHeight) >= OVERSIZED_IMAGE_TOLERANCE;
if (oversizedWidth || oversizedHeight) {
console.warn(formatRuntimeError(2960 /* RuntimeErrorCode.OVERSIZED_IMAGE */, `${imgDirectiveDetails(dir.ngSrc)} the intrinsic image is significantly ` +
`larger than necessary. ` +
`\nRendered image size: ${renderedWidth}w x ${renderedHeight}h. ` +
`\nIntrinsic image size: ${intrinsicWidth}w x ${intrinsicHeight}h. ` +
`\nRecommended intrinsic image size: ${recommendedWidth}w x ${recommendedHeight}h. ` +
`\nNote: Recommended intrinsic image size is calculated assuming a maximum DPR of ` +
`${RECOMMENDED_SRCSET_DENSITY_CAP}. To improve loading time, resize the image ` +
`or consider using the "ngSrcset" and "sizes" attributes.`));
}
}
});
}
/**
* Verifies that a specified input is set.
*/
function assertNonEmptyWidthAndHeight(dir) {
let missingAttributes = [];
if (dir.width === undefined)
missingAttributes.push('width');
if (dir.height === undefined)
missingAttributes.push('height');
if (missingAttributes.length > 0) {
throw new RuntimeError(2954 /* RuntimeErrorCode.REQUIRED_INPUT_MISSING */, `${imgDirectiveDetails(dir.ngSrc)} these required attributes ` +
`are missing: ${missingAttributes.map(attr => `"${attr}"`).join(', ')}. ` +
`Including "width" and "height" attributes will prevent image-related layout shifts. ` +
`To fix this, include "width" and "height" attributes on the image tag.`);
}
}
/**
* Verifies that the `loading` attribute is set to a valid input &
* is not used on priority images.
*/
function assertValidLoadingInput(dir) {
if (dir.loading && dir.priority) {
throw new RuntimeError(2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} the \`loading\` attribute ` +
`was used on an image that was marked "priority". ` +
`Setting \`loading\` on priority images is not allowed ` +
`because these images will always be eagerly loaded. ` +
`To fix this, remove the “loading” attribute from the priority image.`);
}
const validInputs = ['auto', 'eager', 'lazy'];
if (typeof dir.loading === 'string' && !validInputs.includes(dir.loading)) {
throw new RuntimeError(2952 /* RuntimeErrorCode.INVALID_INPUT */, `${imgDirectiveDetails(dir.ngSrc)} the \`loading\` attribute ` +
`has an invalid value (\`${dir.loading}\`). ` +
`To fix this, provide a valid value ("lazy", "eager", or "auto").`);
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibmdfb3B0aW1pemVkX2ltYWdlLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vLi4vLi4vLi4vLi4vLi4vcGFja2FnZXMvY29tbW9uL3NyYy9kaXJlY3RpdmVzL25nX29wdGltaXplZF9pbWFnZS9uZ19vcHRpbWl6ZWRfaW1hZ2UudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7OztHQU1HO0FBRUgsT0FBTyxFQUFDLFNBQVMsRUFBRSxVQUFVLEVBQUUsTUFBTSxFQUFFLFFBQVEsRUFBRSxLQUFLLEVBQUUsTUFBTSxFQUFnQyxTQUFTLEVBQWlCLG1CQUFtQixJQUFJLGtCQUFrQixFQUFFLGFBQWEsSUFBSSxZQUFZLEVBQUMsTUFBTSxlQUFlLENBQUM7QUFJdk4sT0FBTyxFQUFDLG1CQUFtQixFQUFDLE1BQU0sZ0JBQWdCLENBQUM7QUFDbkQsT0FBTyxFQUFDLFlBQVksRUFBQyxNQUFNLDhCQUE4QixDQUFDO0FBQzFELE9BQU8sRUFBQyxnQkFBZ0IsRUFBQyxNQUFNLHNCQUFzQixDQUFDO0FBQ3RELE9BQU8sRUFBQyxxQkFBcUIsRUFBQyxNQUFNLDJCQUEyQixDQUFDOztBQUVoRTs7Ozs7O0dBTUc7QUFDSCxNQUFNLDhCQUE4QixHQUFHLEVBQUUsQ0FBQztBQUUxQzs7O0dBR0c7QUFDSCxNQUFNLDZCQUE2QixHQUFHLDJCQUEyQixDQUFDO0FBRWxFOzs7R0FHRztBQUNILE1BQU0sK0JBQStCLEdBQUcsbUNBQW1DLENBQUM7QUFFNUU7Ozs7R0FJRztBQUNILE1BQU0sQ0FBQyxNQUFNLDJCQUEyQixHQUFHLENBQUMsQ0FBQztBQUU3Qzs7O0dBR0c7QUFDSCxNQUFNLENBQUMsTUFBTSw4QkFBOEIsR0FBRyxDQUFDLENBQUM7QUFFaEQ7O0dBRUc7QUFDSCxNQUFNLHNCQUFzQixHQUFHLEVBQUUsQ0FBQztBQUVsQzs7OztHQUlHO0FBQ0gsTUFBTSx5QkFBeUIsR0FBRyxJQUFJLENBQUM7QUFFdkM7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7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R0FBRyxtQkFBbUIsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLDZCQUE2QjtZQUMxRCwyQkFBMkIsR0FBRyxDQUFDLE9BQU8sT0FBTztZQUM3QyxrRUFBa0UsQ0FBQyxDQUFDO0tBQzdFO0FBQ0gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8qKlxuICogQGxpY2Vuc2VcbiAqIENvcHlyaWdodCBHb29nbGUgTExDIEFsbCBSaWdodHMgUmVzZXJ2ZWQuXG4gKlxuICogVXNlIG9mIHRoaXMgc291cmNlIGNvZGUgaXMgZ292ZXJuZWQgYnkgYW4gTUlULXN0eWxlIGxpY2Vuc2UgdGhhdCBjYW4gYmVcbiAqIGZvdW5kIGluIHRoZSBMSUNFTlNFIGZpbGUgYXQgaHR0cHM6Ly9hbmd1bGFyLmlvL2xpY2Vuc2VcbiAqL1xuXG5pbXBvcnQge0RpcmVjdGl2ZSwgRWxlbWVudFJlZiwgaW5qZWN0LCBJbmplY3RvciwgSW5wdXQsIE5nWm9uZSwgT25DaGFuZ2VzLCBPbkRlc3Ryb3ksIE9uSW5pdCwgUmVuZGVyZXIyLCBTaW1wbGVDaGFuZ2VzLCDJtWZvcm1hdFJ1bnRpbWVFcnJvciBhcyBmb3JtYXRSdW50aW1lRXJyb3IsIMm1UnVudGltZUVycm9yIGFzIFJ1bnRpbWVFcnJvcn0gZnJvbSAnQGFuZ3VsYXIvY29yZSc7XG5cbmltcG9ydCB7UnVudGltZUVycm9yQ29kZX0gZnJvbSAnLi4vLi4vZXJyb3JzJztcblxuaW1wb3J0IHtpbWdEaXJlY3RpdmVEZXRhaWxzfSBmcm9tICcuL2Vycm9yX2hlbHBlcic7XG5pbXBvcnQge0lNQUdFX0xPQURFUn0gZnJvbSAnLi9pbWFnZV9sb2FkZXJzL2ltYWdlX2xvYWRlcic7XG5pbXBvcnQge0xDUEltYWdlT2JzZXJ2ZXJ9IGZyb20gJy4vbGNwX2ltYWdlX29ic2VydmVyJztcbmltcG9ydCB7UHJlY29ubmVjdExpbmtDaGVja2VyfSBmcm9tICcuL3ByZWNvbm5lY3RfbGlua19jaGVja2VyJztcblxuLyoqXG4gKiBXaGVuIGEgQmFzZTY0LWVuY29kZWQgaW1hZ2UgaXMgcGFzc2VkIGFzIGFuIGlucHV0IHRvIHRoZSBgTmdPcHRpbWl6ZWRJbWFnZWAgZGlyZWN0aXZlLFxuICogYW4gZXJyb3IgaXMgdGhyb3duLiBUaGUgaW1hZ2UgY29udGVudCAoYXMgYSBzdHJpbmcpIG1pZ2h0IGJlIHZlcnkgbG9uZywgdGh1cyBtYWtpbmdcbiAqIGl0IGhhcmQgdG8gcmVhZCBhbiBlcnJvciBtZXNzYWdlIGlmIHRoZSBlbnRpcmUgc3RyaW5nIGlzIGluY2x1ZGVkLiBUaGlzIGNvbnN0IGRlZmluZXNcbiAqIHRoZSBudW1iZXIgb2YgY2hhcmFjdGVycyB0aGF0IHNob3VsZCBiZSBpbmNsdWRlZCBpbnRvIHRoZSBlcnJvciBtZXNzYWdlLiBUaGUgcmVzdFxuICogb2YgdGhlIGNvbnRlbnQgaXMgdHJ1bmNhdGVkLlxuICovXG5jb25zdCBCQVNFNjRfSU1HX01BWF9MRU5HVEhfSU5fRVJST1IgPSA1MDtcblxuLyoqXG4gKiBSZWdFeHByIHRvIGRldGVybWluZSB3aGV0aGVyIGEgc3JjIGluIGEgc3Jjc2V0IGlzIHVzaW5nIHdpZHRoIGRlc2NyaXB0b3JzLlxuICogU2hvdWxkIG1hdGNoIHNvbWV0aGluZyBsaWtlOiBcIjEwMHcsIDIwMHdcIi5cbiAqL1xuY29uc3QgVkFMSURfV0lEVEhfREVTQ1JJUFRPUl9TUkNTRVQgPSAvXigoXFxzKlxcZCt3XFxzKigsfCQpKXsxLH0pJC87XG5cbi8qKlxuICogUmVnRXhwciB0byBkZXRlcm1pbmUgd2hldGhlciBhIHNyYyBpbiBhIHNyY3NldCBpcyB1c2luZyBkZW5zaXR5IGRlc2NyaXB0b3JzLlxuICogU2hvdWxkIG1hdGNoIHNvbWV0aGluZyBsaWtlOiBcIjF4LCAyeCwgNTB4XCIuIEFsc28gc3VwcG9ydHMgZGVjaW1hbHMgbGlrZSBcIjEuNXgsIDEuNTB4XCIuXG4gKi9cbmNvbnN0IFZBTElEX0RFTlNJVFlfREVTQ1JJUFRPUl9TUkNTRVQgPSAvXigoXFxzKlxcZCsoXFwuXFxkKyk/eFxccyooLHwkKSl7MSx9KSQvO1xuXG4vKipcbiAqIFNyY3NldCB2YWx1ZXMgd2l0aCBhIGRlbnNpdHkgZGVzY3JpcHRvciBoaWdoZXIgdGhhbiB0aGlzIHZhbHVlIHdpbGwgYWN0aXZlbHlcbiAqIHRocm93IGFuIGVycm9yLiBTdWNoIGRlbnNpdGllcyBhcmUgbm90IHBlcm1pdHRlZCBhcyB0aGV5IGNhdXNlIGltYWdlIHNpemVzXG4gKiB0byBiZSB1bnJlYXNvbmFibHkgbGFyZ2UgYW5kIHNsb3cgZG93biBMQ1AuXG4gKi9cbmV4cG9ydCBjb25zdCBBQlNPTFVURV9TUkNTRVRfREVOU0lUWV9DQVAgPSAzO1xuXG4vKipcbiAqIFVzZWQgb25seSBpbiBlcnJvciBtZXNzYWdlIHRleHQgdG8gY29tbXVuaWNhdGUgYmVzdCBwcmFjdGljZXMsIGFzIHdlIHdpbGxcbiAqIG9ubHkgdGhyb3cgYmFzZWQgb24gdGhlIHNsaWdodGx5IG1vcmUgY29uc2VydmF0aXZlIEFCU09MVVRFX1NSQ1NFVF9ERU5TSVRZX0NBUC5cbiAqL1xuZXhwb3J0IGNvbnN0IFJFQ09NTUVOREVEX1NSQ1NFVF9ERU5TSVRZX0NBUCA9IDI7XG5cbi8qKlxuICogVXNlZCB0byBkZXRlcm1pbmUgd2hldGhlciB0d28gYXNwZWN0IHJhdGlvcyBhcmUgc2ltaWxhciBpbiB2YWx1ZS5cbiAqL1xuY29uc3QgQVNQRUNUX1JBVElPX1RPTEVSQU5DRSA9IC4xO1xuXG4vKipcbiAqIFVzZWQgdG8gZGV0ZXJtaW5lIHdoZXRoZXIgdGhlIGltYWdlIGhhcyBiZWVuIHJlcXVlc3RlZCBhdCBhbiBvdmVybHlcbiAqIGxhcmdlIHNpemUgY29tcGFyZWQgdG8gdGhlIGFjdHVhbCByZW5kZXJlZCBpbWFnZSBzaXplIChhZnRlciB0YWtpbmdcbiAqIGludG8gYWNjb3VudCBhIHR5cGljYWwgZGV2aWNlIHBpeGVsIHJhdGlvKS4gSW4gcGl4ZWxzLlxuICovXG5jb25zdCBPVkVSU0laRURfSU1BR0VfVE9MRVJBTkNFID0gMTAwMDtcblxuLyoqXG4gKiBEaXJlY3RpdmUgdGhhdCBpbXByb3ZlcyBpbWFnZSBsb2FkaW5nIHBlcmZvcm1hbmNlIGJ5IGVuZm9yY2luZyBiZXN0IHByYWN0aWNlcy5cbiAqXG4gKiBgTmdPcHRpbWl6ZWRJbWFnZWAgZW5zdXJlcyB0aGF0IHRoZSBsb2FkaW5nIG9mIHRoZSBMYXJnZXN0IENvbnRlbnRmdWwgUGFpbnQgKExDUCkgaW1hZ2UgaXNcbiAqIHByaW9yaXRpemVkIGJ5OlxuICogLSBBdXRvbWF0aWNhbGx5IHNldHRpbmcgdGhlIGBmZXRjaHByaW9yaXR5YCBhdHRyaWJ1dGUgb24gdGhlIGA8aW1nPmAgdGFnXG4gKiAtIExhenkgbG9hZGluZyBub24tcHJpb3JpdHkgaW1hZ2VzIGJ5IGRlZmF1bHRcbiAqIC0gQXNzZXJ0aW5nIHRoYXQgdGhlcmUgaXMgYSBjb3JyZXNwb25kaW5nIHByZWNvbm5lY3QgbGluayB0YWcgaW4gdGhlIGRvY3VtZW50IGhlYWRcbiAqXG4gKiBJbiBhZGRpdGlvbiwgdGhlIGRpcmVjdGl2ZTpcbiAqIC0gR2VuZXJhdGVzIGFwcHJvcHJpYXRlIGFzc2V0IFVSTHMgaWYgYSBjb3JyZXNwb25kaW5nIGBJbWFnZUxvYWRlcmAgZnVuY3Rpb24gaXMgcHJvdmlkZWRcbiAqIC0gUmVxdWlyZXMgdGhhdCBgd2lkdGhgIGFuZCBgaGVpZ2h0YCBhcmUgc2V0XG4gKiAtIFdhcm5zIGlmIGB3aWR0aGAgb3IgYGhlaWdodGAgaGF2ZSBiZWVuIHNldCBpbmNvcnJlY3RseVxuICogLSBXYXJucyBpZiB0aGUgaW1hZ2Ugd2lsbCBiZSB2aXN1YWxseSBkaXN0b3J0ZWQgd2hlbiByZW5kZXJlZFxuICpcbiAqIEB1c2FnZU5vdGVzXG4gKiBUaGUgYE5nT3B0aW1pemVkSW1hZ2VgIGRpcmVjdGl2ZSBpcyBtYXJrZWQgYXMgW3N0YW5kYWxvbmVdKGd1aWRlL3N0YW5kYWxvbmUtY29tcG9uZW50cykgYW5kIGNhblxuICogYmUgaW1wb3J0ZWQgZGlyZWN0bHkuXG4gKlxuICogRm9sbG93IHRoZSBzdGVwcyBiZWxvdyB0byBlbmFibGUgYW5kIHVzZSB0aGUgZGlyZWN0aXZlOlxuICogMS4gSW1wb3J0IGl0IGludG8gdGhlIG5lY2Vzc2FyeSBOZ01vZHVsZSBvciBhIHN0YW5kYWxvbmUgQ29tcG9uZW50LlxuICogMi4gT3B0aW9uYWxseSBwcm92aWRlIGFuIGBJbWFnZUxvYWRlcmAgaWYgeW91IHVzZSBhbiBpbWFnZSBob3N0aW5nIHNlcnZpY2UuXG4gKiAzLiBVcGRhdGUgdGhlIG5lY2Vzc2FyeSBgPGltZz5gIHRhZ3MgaW4gdGVtcGxhdGVzIGFuZCByZXBsYWNlIGBzcmNgIGF0dHJpYnV0ZXMgd2l0aCBgbmdTcmNgLlxuICogVXNpbmcgYSBgbmdTcmNgIGFsbG93cyB0aGUgZGlyZWN0aXZlIHRvIGNvbnRyb2wgd2hlbiB0aGUgYHNyY2AgZ2V0cyBzZXQsIHdoaWNoIHRyaWdnZXJzIGFuIGltYWdlXG4gKiBkb3dubG9hZC5cbiAqXG4gKiBTdGVwIDE6IGltcG9ydCB0aGUgYE5nT3B0aW1pemVkSW1hZ2VgIGRpcmVjdGl2ZS5cbiAqXG4gKiBgYGB0eXBlc2NyaXB0XG4gKiBpbXBvcnQgeyBOZ09wdGltaXplZEltYWdlIH0gZnJvbSAnQGFuZ3VsYXIvY29tbW9uJztcbiAqXG4gKiAvLyBJbmNsdWRlIGl0IGludG8gdGhlIG5lY2Vzc2FyeSBOZ01vZHVsZVxuICogQE5nTW9kdWxlKHtcbiAqICAgaW1wb3J0czogW05nT3B0aW1pemVkSW1hZ2VdLFxuICogfSlcbiAqIGNsYXNzIEFwcE1vZHVsZSB7fVxuICpcbiAqIC8vIC4uLiBvciBhIHN0YW5kYWxvbmUgQ29tcG9uZW50XG4gKiBAQ29tcG9uZW50KHtcbiAqICAgc3RhbmRhbG9uZTogdHJ1ZVxuICogICBpbXBvcnRzOiBbTmdPcHRpbWl6ZWRJbWFnZV0sXG4gK