@angular/common
Version:
Angular - commonly needed directives and services
1 lines • 144 kB
Source Map (JSON)
{"version":3,"file":"common.mjs","sources":["../../../../../darwin_arm64-fastbuild-ST-2d99d9656325/bin/packages/common/src/i18n/locale_data.ts","../../../../../darwin_arm64-fastbuild-ST-2d99d9656325/bin/packages/common/src/version.ts","../../../../../darwin_arm64-fastbuild-ST-2d99d9656325/bin/packages/common/src/viewport_scroller.ts","../../../../../darwin_arm64-fastbuild-ST-2d99d9656325/bin/packages/common/src/directives/ng_optimized_image/image_loaders/constants.ts","../../../../../darwin_arm64-fastbuild-ST-2d99d9656325/bin/packages/common/src/directives/ng_optimized_image/url.ts","../../../../../darwin_arm64-fastbuild-ST-2d99d9656325/bin/packages/common/src/directives/ng_optimized_image/image_loaders/image_loader.ts","../../../../../darwin_arm64-fastbuild-ST-2d99d9656325/bin/packages/common/src/directives/ng_optimized_image/image_loaders/cloudflare_loader.ts","../../../../../darwin_arm64-fastbuild-ST-2d99d9656325/bin/packages/common/src/directives/ng_optimized_image/image_loaders/cloudinary_loader.ts","../../../../../darwin_arm64-fastbuild-ST-2d99d9656325/bin/packages/common/src/directives/ng_optimized_image/image_loaders/imagekit_loader.ts","../../../../../darwin_arm64-fastbuild-ST-2d99d9656325/bin/packages/common/src/directives/ng_optimized_image/image_loaders/imgix_loader.ts","../../../../../darwin_arm64-fastbuild-ST-2d99d9656325/bin/packages/common/src/directives/ng_optimized_image/image_loaders/netlify_loader.ts","../../../../../darwin_arm64-fastbuild-ST-2d99d9656325/bin/packages/common/src/directives/ng_optimized_image/error_helper.ts","../../../../../darwin_arm64-fastbuild-ST-2d99d9656325/bin/packages/common/src/directives/ng_optimized_image/asserts.ts","../../../../../darwin_arm64-fastbuild-ST-2d99d9656325/bin/packages/common/src/directives/ng_optimized_image/lcp_image_observer.ts","../../../../../darwin_arm64-fastbuild-ST-2d99d9656325/bin/packages/common/src/directives/ng_optimized_image/preconnect_link_checker.ts","../../../../../darwin_arm64-fastbuild-ST-2d99d9656325/bin/packages/common/src/directives/ng_optimized_image/tokens.ts","../../../../../darwin_arm64-fastbuild-ST-2d99d9656325/bin/packages/common/src/directives/ng_optimized_image/preload-link-creator.ts","../../../../../darwin_arm64-fastbuild-ST-2d99d9656325/bin/packages/common/src/directives/ng_optimized_image/ng_optimized_image.ts"],"sourcesContent":["/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {ɵregisterLocaleData} from '@angular/core';\n\n/**\n * Register global data to be used internally by Angular. See the\n * [\"I18n guide\"](guide/i18n/format-data-locale) to know how to import additional locale\n * data.\n *\n * The signature registerLocaleData(data: any, extraData?: any) is deprecated since v5.1\n *\n * @publicApi\n */\nexport function registerLocaleData(data: any, localeId?: string | any, extraData?: any): void {\n return ɵregisterLocaleData(data, localeId, extraData);\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\n/**\n * @module\n * @description\n * Entry point for all public APIs of the common package.\n */\n\nimport {Version} from '@angular/core';\n\n/**\n * @publicApi\n */\nexport const VERSION = new Version('19.2.12');\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {inject, ɵɵdefineInjectable} from '@angular/core';\n\nimport {DOCUMENT} from './dom_tokens';\n\n/**\n * Defines a scroll position manager. Implemented by `BrowserViewportScroller`.\n *\n * @publicApi\n */\nexport abstract class ViewportScroller {\n // De-sugared tree-shakable injection\n // See #23917\n /** @nocollapse */\n static ɵprov = /** @pureOrBreakMyCode */ /* @__PURE__ */ ɵɵdefineInjectable({\n token: ViewportScroller,\n providedIn: 'root',\n factory: () =>\n typeof ngServerMode !== 'undefined' && ngServerMode\n ? new NullViewportScroller()\n : new BrowserViewportScroller(inject(DOCUMENT), window),\n });\n\n /**\n * Configures the top offset used when scrolling to an anchor.\n * @param offset A position in screen coordinates (a tuple with x and y values)\n * or a function that returns the top offset position.\n *\n */\n abstract setOffset(offset: [number, number] | (() => [number, number])): void;\n\n /**\n * Retrieves the current scroll position.\n * @returns A position in screen coordinates (a tuple with x and y values).\n */\n abstract getScrollPosition(): [number, number];\n\n /**\n * Scrolls to a specified position.\n * @param position A position in screen coordinates (a tuple with x and y values).\n */\n abstract scrollToPosition(position: [number, number]): void;\n\n /**\n * Scrolls to an anchor element.\n * @param anchor The ID of the anchor element.\n */\n abstract scrollToAnchor(anchor: string): void;\n\n /**\n * Disables automatic scroll restoration provided by the browser.\n * See also [window.history.scrollRestoration\n * info](https://developers.google.com/web/updates/2015/09/history-api-scroll-restoration).\n */\n abstract setHistoryScrollRestoration(scrollRestoration: 'auto' | 'manual'): void;\n}\n\n/**\n * Manages the scroll position for a browser window.\n */\nexport class BrowserViewportScroller implements ViewportScroller {\n private offset: () => [number, number] = () => [0, 0];\n\n constructor(\n private document: Document,\n private window: Window,\n ) {}\n\n /**\n * Configures the top offset used when scrolling to an anchor.\n * @param offset A position in screen coordinates (a tuple with x and y values)\n * or a function that returns the top offset position.\n *\n */\n setOffset(offset: [number, number] | (() => [number, number])): void {\n if (Array.isArray(offset)) {\n this.offset = () => offset;\n } else {\n this.offset = offset;\n }\n }\n\n /**\n * Retrieves the current scroll position.\n * @returns The position in screen coordinates.\n */\n getScrollPosition(): [number, number] {\n return [this.window.scrollX, this.window.scrollY];\n }\n\n /**\n * Sets the scroll position.\n * @param position The new position in screen coordinates.\n */\n scrollToPosition(position: [number, number]): void {\n this.window.scrollTo(position[0], position[1]);\n }\n\n /**\n * Scrolls to an element and attempts to focus the element.\n *\n * Note that the function name here is misleading in that the target string may be an ID for a\n * non-anchor element.\n *\n * @param target The ID of an element or name of the anchor.\n *\n * @see https://html.spec.whatwg.org/#the-indicated-part-of-the-document\n * @see https://html.spec.whatwg.org/#scroll-to-fragid\n */\n scrollToAnchor(target: string): void {\n const elSelected = findAnchorFromDocument(this.document, target);\n\n if (elSelected) {\n this.scrollToElement(elSelected);\n // After scrolling to the element, the spec dictates that we follow the focus steps for the\n // target. Rather than following the robust steps, simply attempt focus.\n //\n // @see https://html.spec.whatwg.org/#get-the-focusable-area\n // @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLOrForeignElement/focus\n // @see https://html.spec.whatwg.org/#focusable-area\n elSelected.focus();\n }\n }\n\n /**\n * Disables automatic scroll restoration provided by the browser.\n */\n setHistoryScrollRestoration(scrollRestoration: 'auto' | 'manual'): void {\n this.window.history.scrollRestoration = scrollRestoration;\n }\n\n /**\n * Scrolls to an element using the native offset and the specified offset set on this scroller.\n *\n * The offset can be used when we know that there is a floating header and scrolling naively to an\n * element (ex: `scrollIntoView`) leaves the element hidden behind the floating header.\n */\n private scrollToElement(el: HTMLElement): void {\n const rect = el.getBoundingClientRect();\n const left = rect.left + this.window.pageXOffset;\n const top = rect.top + this.window.pageYOffset;\n const offset = this.offset();\n this.window.scrollTo(left - offset[0], top - offset[1]);\n }\n}\n\nfunction findAnchorFromDocument(document: Document, target: string): HTMLElement | null {\n const documentResult = document.getElementById(target) || document.getElementsByName(target)[0];\n\n if (documentResult) {\n return documentResult;\n }\n\n // `getElementById` and `getElementsByName` won't pierce through the shadow DOM so we\n // have to traverse the DOM manually and do the lookup through the shadow roots.\n if (\n typeof document.createTreeWalker === 'function' &&\n document.body &&\n typeof document.body.attachShadow === 'function'\n ) {\n const treeWalker = document.createTreeWalker(document.body, NodeFilter.SHOW_ELEMENT);\n let currentNode = treeWalker.currentNode as HTMLElement | null;\n\n while (currentNode) {\n const shadowRoot = currentNode.shadowRoot;\n\n if (shadowRoot) {\n // Note that `ShadowRoot` doesn't support `getElementsByName`\n // so we have to fall back to `querySelector`.\n const result =\n shadowRoot.getElementById(target) || shadowRoot.querySelector(`[name=\"${target}\"]`);\n if (result) {\n return result;\n }\n }\n\n currentNode = treeWalker.nextNode() as HTMLElement | null;\n }\n }\n\n return null;\n}\n\n/**\n * Provides an empty implementation of the viewport scroller.\n */\nexport class NullViewportScroller implements ViewportScroller {\n /**\n * Empty implementation\n */\n setOffset(offset: [number, number] | (() => [number, number])): void {}\n\n /**\n * Empty implementation\n */\n getScrollPosition(): [number, number] {\n return [0, 0];\n }\n\n /**\n * Empty implementation\n */\n scrollToPosition(position: [number, number]): void {}\n\n /**\n * Empty implementation\n */\n scrollToAnchor(anchor: string): void {}\n\n /**\n * Empty implementation\n */\n setHistoryScrollRestoration(scrollRestoration: 'auto' | 'manual'): void {}\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\n/**\n * Value (out of 100) of the requested quality for placeholder images.\n */\nexport const PLACEHOLDER_QUALITY = '20';\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\n// Converts a string that represents a URL into a URL class instance.\nexport function getUrl(src: string, win: Window): URL {\n // Don't use a base URL is the URL is absolute.\n return isAbsoluteUrl(src) ? new URL(src) : new URL(src, win.location.href);\n}\n\n// Checks whether a URL is absolute (i.e. starts with `http://` or `https://`).\nexport function isAbsoluteUrl(src: string): boolean {\n return /^https?:\\/\\//.test(src);\n}\n\n// Given a URL, extract the hostname part.\n// If a URL is a relative one - the URL is returned as is.\nexport function extractHostname(url: string): string {\n return isAbsoluteUrl(url) ? new URL(url).hostname : url;\n}\n\nexport function isValidPath(path: unknown): boolean {\n const isString = typeof path === 'string';\n\n if (!isString || path.trim() === '') {\n return false;\n }\n\n // Calling new URL() will throw if the path string is malformed\n try {\n const url = new URL(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function normalizePath(path: string): string {\n return path.endsWith('/') ? path.slice(0, -1) : path;\n}\n\nexport function normalizeSrc(src: string): string {\n return src.startsWith('/') ? src.slice(1) : src;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {InjectionToken, Provider, ɵRuntimeError as RuntimeError} from '@angular/core';\n\nimport {RuntimeErrorCode} from '../../../errors';\nimport {isAbsoluteUrl, isValidPath, normalizePath, normalizeSrc} from '../url';\n\n/**\n * Config options recognized by the image loader function.\n *\n * @see {@link ImageLoader}\n * @see {@link NgOptimizedImage}\n * @publicApi\n */\nexport interface ImageLoaderConfig {\n /**\n * Image file name to be added to the image request URL.\n */\n src: string;\n /**\n * Width of the requested image (to be used when generating srcset).\n */\n width?: number;\n /**\n * Whether the loader should generate a URL for a small image placeholder instead of a full-sized\n * image.\n */\n isPlaceholder?: boolean;\n /**\n * Additional user-provided parameters for use by the ImageLoader.\n */\n loaderParams?: {[key: string]: any};\n}\n\n/**\n * Represents an image loader function. Image loader functions are used by the\n * NgOptimizedImage directive to produce full image URL based on the image name and its width.\n *\n * @publicApi\n */\nexport type ImageLoader = (config: ImageLoaderConfig) => string;\n\n/**\n * Noop image loader that does no transformation to the original src and just returns it as is.\n * This loader is used as a default one if more specific logic is not provided in an app config.\n *\n * @see {@link ImageLoader}\n * @see {@link NgOptimizedImage}\n */\nexport const noopImageLoader = (config: ImageLoaderConfig) => config.src;\n\n/**\n * Metadata about the image loader.\n */\nexport type ImageLoaderInfo = {\n name: string;\n testUrl: (url: string) => boolean;\n};\n\n/**\n * Injection token that configures the image loader function.\n *\n * @see {@link ImageLoader}\n * @see {@link NgOptimizedImage}\n * @publicApi\n */\nexport const IMAGE_LOADER = new InjectionToken<ImageLoader>(ngDevMode ? 'ImageLoader' : '', {\n providedIn: 'root',\n factory: () => noopImageLoader,\n});\n\n/**\n * Internal helper function that makes it easier to introduce custom image loaders for the\n * `NgOptimizedImage` directive. It is enough to specify a URL builder function to obtain full DI\n * configuration for a given loader: a DI token corresponding to the actual loader function, plus DI\n * tokens managing preconnect check functionality.\n * @param buildUrlFn a function returning a full URL based on loader's configuration\n * @param exampleUrls example of full URLs for a given loader (used in error messages)\n * @returns a set of DI providers corresponding to the configured image loader\n */\nexport function createImageLoader(\n buildUrlFn: (path: string, config: ImageLoaderConfig) => string,\n exampleUrls?: string[],\n) {\n return function provideImageLoader(path: string) {\n if (!isValidPath(path)) {\n throwInvalidPathError(path, exampleUrls || []);\n }\n\n // The trailing / is stripped (if provided) to make URL construction (concatenation) easier in\n // the individual loader functions.\n path = normalizePath(path);\n\n const loaderFn = (config: ImageLoaderConfig) => {\n if (isAbsoluteUrl(config.src)) {\n // Image loader functions expect an image file name (e.g. `my-image.png`)\n // or a relative path + a file name (e.g. `/a/b/c/my-image.png`) as an input,\n // so the final absolute URL can be constructed.\n // When an absolute URL is provided instead - the loader can not\n // build a final URL, thus the error is thrown to indicate that.\n throwUnexpectedAbsoluteUrlError(path, config.src);\n }\n\n return buildUrlFn(path, {...config, src: normalizeSrc(config.src)});\n };\n\n const providers: Provider[] = [{provide: IMAGE_LOADER, useValue: loaderFn}];\n return providers;\n };\n}\n\nfunction throwInvalidPathError(path: unknown, exampleUrls: string[]): never {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_LOADER_ARGUMENTS,\n ngDevMode &&\n `Image loader has detected an invalid path (\\`${path}\\`). ` +\n `To fix this, supply a path using one of the following formats: ${exampleUrls.join(\n ' or ',\n )}`,\n );\n}\n\nfunction throwUnexpectedAbsoluteUrlError(path: string, url: string): never {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_LOADER_ARGUMENTS,\n ngDevMode &&\n `Image loader has detected a \\`<img>\\` tag with an invalid \\`ngSrc\\` attribute: ${url}. ` +\n `This image loader expects \\`ngSrc\\` to be a relative URL - ` +\n `however the provided value is an absolute URL. ` +\n `To fix this, provide \\`ngSrc\\` as a path relative to the base URL ` +\n `configured for this loader (\\`${path}\\`).`,\n );\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {Provider} from '@angular/core';\nimport {PLACEHOLDER_QUALITY} from './constants';\nimport {createImageLoader, ImageLoaderConfig} from './image_loader';\n\n/**\n * Function that generates an ImageLoader for [Cloudflare Image\n * Resizing](https://developers.cloudflare.com/images/image-resizing/) and turns it into an Angular\n * provider. Note: Cloudflare has multiple image products - this provider is specifically for\n * Cloudflare Image Resizing; it will not work with Cloudflare Images or Cloudflare Polish.\n *\n * @param path Your domain name, e.g. https://mysite.com\n * @returns Provider that provides an ImageLoader function\n *\n * @publicApi\n */\nexport const provideCloudflareLoader: (path: string) => Provider[] = createImageLoader(\n createCloudflareUrl,\n ngDevMode ? ['https://<ZONE>/cdn-cgi/image/<OPTIONS>/<SOURCE-IMAGE>'] : undefined,\n);\n\nfunction createCloudflareUrl(path: string, config: ImageLoaderConfig) {\n let params = `format=auto`;\n if (config.width) {\n params += `,width=${config.width}`;\n }\n\n // When requesting a placeholder image we ask for a low quality image to reduce the load time.\n if (config.isPlaceholder) {\n params += `,quality=${PLACEHOLDER_QUALITY}`;\n }\n\n // Cloudflare image URLs format:\n // https://developers.cloudflare.com/images/image-resizing/url-format/\n return `${path}/cdn-cgi/image/${params}/${config.src}`;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {Provider} from '@angular/core';\nimport {createImageLoader, ImageLoaderConfig, ImageLoaderInfo} from './image_loader';\n\n/**\n * Name and URL tester for Cloudinary.\n */\nexport const cloudinaryLoaderInfo: ImageLoaderInfo = {\n name: 'Cloudinary',\n testUrl: isCloudinaryUrl,\n};\n\nconst CLOUDINARY_LOADER_REGEX = /https?\\:\\/\\/[^\\/]+\\.cloudinary\\.com\\/.+/;\n/**\n * Tests whether a URL is from Cloudinary CDN.\n */\nfunction isCloudinaryUrl(url: string): boolean {\n return CLOUDINARY_LOADER_REGEX.test(url);\n}\n\n/**\n * Function that generates an ImageLoader for Cloudinary and turns it into an Angular provider.\n *\n * @param path Base URL of your Cloudinary images\n * This URL should match one of the following formats:\n * https://res.cloudinary.com/mysite\n * https://mysite.cloudinary.com\n * https://subdomain.mysite.com\n * @returns Set of providers to configure the Cloudinary loader.\n *\n * @publicApi\n */\nexport const provideCloudinaryLoader: (path: string) => Provider[] = createImageLoader(\n createCloudinaryUrl,\n ngDevMode\n ? [\n 'https://res.cloudinary.com/mysite',\n 'https://mysite.cloudinary.com',\n 'https://subdomain.mysite.com',\n ]\n : undefined,\n);\n\nfunction createCloudinaryUrl(path: string, config: ImageLoaderConfig) {\n // Cloudinary image URLformat:\n // https://cloudinary.com/documentation/image_transformations#transformation_url_structure\n // Example of a Cloudinary image URL:\n // https://res.cloudinary.com/mysite/image/upload/c_scale,f_auto,q_auto,w_600/marketing/tile-topics-m.png\n\n // For a placeholder image, we use the lowest image setting available to reduce the load time\n // else we use the auto size\n const quality = config.isPlaceholder ? 'q_auto:low' : 'q_auto';\n\n let params = `f_auto,${quality}`;\n if (config.width) {\n params += `,w_${config.width}`;\n }\n\n if (config.loaderParams?.['rounded']) {\n params += `,r_max`;\n }\n\n return `${path}/image/upload/${params}/${config.src}`;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {Provider} from '@angular/core';\nimport {PLACEHOLDER_QUALITY} from './constants';\nimport {createImageLoader, ImageLoaderConfig, ImageLoaderInfo} from './image_loader';\n\n/**\n * Name and URL tester for ImageKit.\n */\nexport const imageKitLoaderInfo: ImageLoaderInfo = {\n name: 'ImageKit',\n testUrl: isImageKitUrl,\n};\n\nconst IMAGE_KIT_LOADER_REGEX = /https?\\:\\/\\/[^\\/]+\\.imagekit\\.io\\/.+/;\n/**\n * Tests whether a URL is from ImageKit CDN.\n */\nfunction isImageKitUrl(url: string): boolean {\n return IMAGE_KIT_LOADER_REGEX.test(url);\n}\n\n/**\n * Function that generates an ImageLoader for ImageKit and turns it into an Angular provider.\n *\n * @param path Base URL of your ImageKit images\n * This URL should match one of the following formats:\n * https://ik.imagekit.io/myaccount\n * https://subdomain.mysite.com\n * @returns Set of providers to configure the ImageKit loader.\n *\n * @publicApi\n */\nexport const provideImageKitLoader: (path: string) => Provider[] = createImageLoader(\n createImagekitUrl,\n ngDevMode ? ['https://ik.imagekit.io/mysite', 'https://subdomain.mysite.com'] : undefined,\n);\n\nexport function createImagekitUrl(path: string, config: ImageLoaderConfig): string {\n // Example of an ImageKit image URL:\n // https://ik.imagekit.io/demo/tr:w-300,h-300/medium_cafe_B1iTdD0C.jpg\n const {src, width} = config;\n const params: string[] = [];\n\n if (width) {\n params.push(`w-${width}`);\n }\n\n // When requesting a placeholder image we ask for a low quality image to reduce the load time.\n if (config.isPlaceholder) {\n params.push(`q-${PLACEHOLDER_QUALITY}`);\n }\n\n const urlSegments = params.length ? [path, `tr:${params.join(',')}`, src] : [path, src];\n const url = new URL(urlSegments.join('/'));\n return url.href;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {Provider} from '@angular/core';\nimport {PLACEHOLDER_QUALITY} from './constants';\nimport {createImageLoader, ImageLoaderConfig, ImageLoaderInfo} from './image_loader';\n\n/**\n * Name and URL tester for Imgix.\n */\nexport const imgixLoaderInfo: ImageLoaderInfo = {\n name: 'Imgix',\n testUrl: isImgixUrl,\n};\n\nconst IMGIX_LOADER_REGEX = /https?\\:\\/\\/[^\\/]+\\.imgix\\.net\\/.+/;\n/**\n * Tests whether a URL is from Imgix CDN.\n */\nfunction isImgixUrl(url: string): boolean {\n return IMGIX_LOADER_REGEX.test(url);\n}\n\n/**\n * Function that generates an ImageLoader for Imgix and turns it into an Angular provider.\n *\n * @param path path to the desired Imgix origin,\n * e.g. https://somepath.imgix.net or https://images.mysite.com\n * @returns Set of providers to configure the Imgix loader.\n *\n * @publicApi\n */\nexport const provideImgixLoader: (path: string) => Provider[] = createImageLoader(\n createImgixUrl,\n ngDevMode ? ['https://somepath.imgix.net/'] : undefined,\n);\n\nfunction createImgixUrl(path: string, config: ImageLoaderConfig) {\n const url = new URL(`${path}/${config.src}`);\n // This setting ensures the smallest allowable format is set.\n url.searchParams.set('auto', 'format');\n if (config.width) {\n url.searchParams.set('w', config.width.toString());\n }\n\n // When requesting a placeholder image we ask a low quality image to reduce the load time.\n if (config.isPlaceholder) {\n url.searchParams.set('q', PLACEHOLDER_QUALITY);\n }\n return url.href;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {\n Provider,\n ɵformatRuntimeError as formatRuntimeError,\n ɵRuntimeError as RuntimeError,\n} from '@angular/core';\n\nimport {RuntimeErrorCode} from '../../../errors';\nimport {isAbsoluteUrl, isValidPath} from '../url';\n\nimport {IMAGE_LOADER, ImageLoaderConfig, ImageLoaderInfo} from './image_loader';\nimport {PLACEHOLDER_QUALITY} from './constants';\n\n/**\n * Name and URL tester for Netlify.\n */\nexport const netlifyLoaderInfo: ImageLoaderInfo = {\n name: 'Netlify',\n testUrl: isNetlifyUrl,\n};\n\nconst NETLIFY_LOADER_REGEX = /https?\\:\\/\\/[^\\/]+\\.netlify\\.app\\/.+/;\n\n/**\n * Tests whether a URL is from a Netlify site. This won't catch sites with a custom domain,\n * but it's a good start for sites in development. This is only used to warn users who haven't\n * configured an image loader.\n */\nfunction isNetlifyUrl(url: string): boolean {\n return NETLIFY_LOADER_REGEX.test(url);\n}\n\n/**\n * Function that generates an ImageLoader for Netlify and turns it into an Angular provider.\n *\n * @param path optional URL of the desired Netlify site. Defaults to the current site.\n * @returns Set of providers to configure the Netlify loader.\n *\n * @publicApi\n */\nexport function provideNetlifyLoader(path?: string) {\n if (path && !isValidPath(path)) {\n throw new RuntimeError(\n RuntimeErrorCode.INVALID_LOADER_ARGUMENTS,\n ngDevMode &&\n `Image loader has detected an invalid path (\\`${path}\\`). ` +\n `To fix this, supply either the full URL to the Netlify site, or leave it empty to use the current site.`,\n );\n }\n\n if (path) {\n const url = new URL(path);\n path = url.origin;\n }\n\n const loaderFn = (config: ImageLoaderConfig) => {\n return createNetlifyUrl(config, path);\n };\n\n const providers: Provider[] = [{provide: IMAGE_LOADER, useValue: loaderFn}];\n return providers;\n}\n\nconst validParams = new Map<string, string>([\n ['height', 'h'],\n ['fit', 'fit'],\n ['quality', 'q'],\n ['q', 'q'],\n ['position', 'position'],\n]);\n\nfunction createNetlifyUrl(config: ImageLoaderConfig, path?: string) {\n // Note: `path` can be undefined, in which case we use a fake one to construct a `URL` instance.\n const url = new URL(path ?? 'https://a/');\n url.pathname = '/.netlify/images';\n\n if (!isAbsoluteUrl(config.src) && !config.src.startsWith('/')) {\n config.src = '/' + config.src;\n }\n\n url.searchParams.set('url', config.src);\n\n if (config.width) {\n url.searchParams.set('w', config.width.toString());\n }\n\n // When requesting a placeholder image we ask for a low quality image to reduce the load time.\n // If the quality is specified in the loader config - always use provided value.\n const configQuality = config.loaderParams?.['quality'] ?? config.loaderParams?.['q'];\n if (config.isPlaceholder && !configQuality) {\n url.searchParams.set('q', PLACEHOLDER_QUALITY);\n }\n\n for (const [param, value] of Object.entries(config.loaderParams ?? {})) {\n if (validParams.has(param)) {\n url.searchParams.set(validParams.get(param)!, value.toString());\n } else {\n if (ngDevMode) {\n console.warn(\n formatRuntimeError(\n RuntimeErrorCode.INVALID_LOADER_ARGUMENTS,\n `The Netlify image loader has detected an \\`<img>\\` tag with the unsupported attribute \"\\`${param}\\`\".`,\n ),\n );\n }\n }\n }\n // The \"a\" hostname is used for relative URLs, so we can remove it from the final URL.\n return url.hostname === 'a' ? url.href.replace(url.origin, '') : url.href;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\n// Assembles directive details string, useful for error messages.\nexport function imgDirectiveDetails(ngSrc: string, includeNgSrc = true) {\n const ngSrcInfo = includeNgSrc\n ? `(activated on an <img> element with the \\`ngSrc=\"${ngSrc}\"\\`) `\n : '';\n return `The NgOptimizedImage directive ${ngSrcInfo}has detected that`;\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {ɵRuntimeError as RuntimeError} from '@angular/core';\n\nimport {RuntimeErrorCode} from '../../errors';\n\n/**\n * Asserts that the application is in development mode. Throws an error if the application is in\n * production mode. This assert can be used to make sure that there is no dev-mode code invoked in\n * the prod mode accidentally.\n */\nexport function assertDevMode(checkName: string) {\n if (!ngDevMode) {\n throw new RuntimeError(\n RuntimeErrorCode.UNEXPECTED_DEV_MODE_CHECK_IN_PROD_MODE,\n `Unexpected invocation of the ${checkName} in the prod mode. ` +\n `Please make sure that the prod mode is enabled for production builds.`,\n );\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {\n inject,\n Injectable,\n OnDestroy,\n ɵformatRuntimeError as formatRuntimeError,\n PLATFORM_ID,\n} from '@angular/core';\n\nimport {DOCUMENT} from '../../dom_tokens';\nimport {RuntimeErrorCode} from '../../errors';\n\nimport {assertDevMode} from './asserts';\nimport {imgDirectiveDetails} from './error_helper';\nimport {getUrl} from './url';\nimport {isPlatformBrowser} from '../../platform_id';\n\ninterface ObservedImageState {\n priority: boolean;\n modified: boolean;\n alreadyWarnedPriority: boolean;\n alreadyWarnedModified: boolean;\n}\n\n/**\n * Observer that detects whether an image with `NgOptimizedImage`\n * is treated as a Largest Contentful Paint (LCP) element. If so,\n * asserts that the image has the `priority` attribute.\n *\n * Note: this is a dev-mode only class and it does not appear in prod bundles,\n * thus there is no `ngDevMode` use in the code.\n *\n * Based on https://web.dev/lcp/#measure-lcp-in-javascript.\n */\n@Injectable({providedIn: 'root'})\nexport class LCPImageObserver implements OnDestroy {\n // Map of full image URLs -> original `ngSrc` values.\n private images = new Map<string, ObservedImageState>();\n\n private window: Window | null = null;\n private observer: PerformanceObserver | null = null;\n\n constructor() {\n const isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n assertDevMode('LCP checker');\n const win = inject(DOCUMENT).defaultView;\n if (isBrowser && typeof PerformanceObserver !== 'undefined') {\n this.window = win;\n this.observer = this.initPerformanceObserver();\n }\n }\n\n /**\n * Inits PerformanceObserver and subscribes to LCP events.\n * Based on https://web.dev/lcp/#measure-lcp-in-javascript\n */\n private initPerformanceObserver(): PerformanceObserver {\n const observer = new PerformanceObserver((entryList) => {\n const entries = entryList.getEntries();\n if (entries.length === 0) return;\n // We use the latest entry produced by the `PerformanceObserver` as the best\n // signal on which element is actually an LCP one. As an example, the first image to load on\n // a page, by virtue of being the only thing on the page so far, is often a LCP candidate\n // and gets reported by PerformanceObserver, but isn't necessarily the LCP element.\n const lcpElement = entries[entries.length - 1];\n\n // Cast to `any` due to missing `element` on the `LargestContentfulPaint` type of entry.\n // See https://developer.mozilla.org/en-US/docs/Web/API/LargestContentfulPaint\n const imgSrc = (lcpElement as any).element?.src ?? '';\n\n // Exclude `data:` and `blob:` URLs, since they are not supported by the directive.\n if (imgSrc.startsWith('data:') || imgSrc.startsWith('blob:')) return;\n\n const img = this.images.get(imgSrc);\n if (!img) return;\n if (!img.priority && !img.alreadyWarnedPriority) {\n img.alreadyWarnedPriority = true;\n logMissingPriorityError(imgSrc);\n }\n if (img.modified && !img.alreadyWarnedModified) {\n img.alreadyWarnedModified = true;\n logModifiedWarning(imgSrc);\n }\n });\n observer.observe({type: 'largest-contentful-paint', buffered: true});\n return observer;\n }\n\n registerImage(rewrittenSrc: string, originalNgSrc: string, isPriority: boolean) {\n if (!this.observer) return;\n const newObservedImageState: ObservedImageState = {\n priority: isPriority,\n modified: false,\n alreadyWarnedModified: false,\n alreadyWarnedPriority: false,\n };\n this.images.set(getUrl(rewrittenSrc, this.window!).href, newObservedImageState);\n }\n\n unregisterImage(rewrittenSrc: string) {\n if (!this.observer) return;\n this.images.delete(getUrl(rewrittenSrc, this.window!).href);\n }\n\n updateImage(originalSrc: string, newSrc: string) {\n if (!this.observer) return;\n const originalUrl = getUrl(originalSrc, this.window!).href;\n const img = this.images.get(originalUrl);\n if (img) {\n img.modified = true;\n this.images.set(getUrl(newSrc, this.window!).href, img);\n this.images.delete(originalUrl);\n }\n }\n\n ngOnDestroy() {\n if (!this.observer) return;\n this.observer.disconnect();\n this.images.clear();\n }\n}\n\nfunction logMissingPriorityError(ngSrc: string) {\n const directiveDetails = imgDirectiveDetails(ngSrc);\n console.error(\n formatRuntimeError(\n RuntimeErrorCode.LCP_IMG_MISSING_PRIORITY,\n `${directiveDetails} this image is the Largest Contentful Paint (LCP) ` +\n `element but was not marked \"priority\". This image should be marked ` +\n `\"priority\" in order to prioritize its loading. ` +\n `To fix this, add the \"priority\" attribute.`,\n ),\n );\n}\n\nfunction logModifiedWarning(ngSrc: string) {\n const directiveDetails = imgDirectiveDetails(ngSrc);\n console.warn(\n formatRuntimeError(\n RuntimeErrorCode.LCP_IMG_NGSRC_MODIFIED,\n `${directiveDetails} this image is the Largest Contentful Paint (LCP) ` +\n `element and has had its \"ngSrc\" attribute modified. This can cause ` +\n `slower loading performance. It is recommended not to modify the \"ngSrc\" ` +\n `property on any image which could be the LCP element.`,\n ),\n );\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {\n inject,\n Injectable,\n InjectionToken,\n ɵformatRuntimeError as formatRuntimeError,\n} from '@angular/core';\n\nimport {DOCUMENT} from '../../dom_tokens';\nimport {RuntimeErrorCode} from '../../errors';\n\nimport {assertDevMode} from './asserts';\nimport {imgDirectiveDetails} from './error_helper';\nimport {extractHostname, getUrl} from './url';\n\n// Set of origins that are always excluded from the preconnect checks.\nconst INTERNAL_PRECONNECT_CHECK_BLOCKLIST = new Set(['localhost', '127.0.0.1', '0.0.0.0']);\n\n/**\n * Injection token to configure which origins should be excluded\n * from the preconnect checks. It can either be a single string or an array of strings\n * to represent a group of origins, for example:\n *\n * ```ts\n * {provide: PRECONNECT_CHECK_BLOCKLIST, useValue: 'https://your-domain.com'}\n * ```\n *\n * or:\n *\n * ```ts\n * {provide: PRECONNECT_CHECK_BLOCKLIST,\n * useValue: ['https://your-domain-1.com', 'https://your-domain-2.com']}\n * ```\n *\n * @publicApi\n */\nexport const PRECONNECT_CHECK_BLOCKLIST = new InjectionToken<Array<string | string[]>>(\n ngDevMode ? 'PRECONNECT_CHECK_BLOCKLIST' : '',\n);\n\n/**\n * Contains the logic to detect whether an image, marked with the \"priority\" attribute\n * has a corresponding `<link rel=\"preconnect\">` tag in the `document.head`.\n *\n * Note: this is a dev-mode only class, which should not appear in prod bundles,\n * thus there is no `ngDevMode` use in the code.\n */\n@Injectable({providedIn: 'root'})\nexport class PreconnectLinkChecker {\n private document = inject(DOCUMENT);\n\n /**\n * Set of <link rel=\"preconnect\"> tags found on this page.\n * The `null` value indicates that there was no DOM query operation performed.\n */\n private preconnectLinks: Set<string> | null = null;\n\n /*\n * Keep track of all already seen origin URLs to avoid repeating the same check.\n */\n private alreadySeen = new Set<string>();\n\n private window: Window | null = this.document.defaultView;\n\n private blocklist = new Set<string>(INTERNAL_PRECONNECT_CHECK_BLOCKLIST);\n\n constructor() {\n assertDevMode('preconnect link checker');\n const blocklist = inject(PRECONNECT_CHECK_BLOCKLIST, {optional: true});\n if (blocklist) {\n this.populateBlocklist(blocklist);\n }\n }\n\n private populateBlocklist(origins: Array<string | string[]> | string) {\n if (Array.isArray(origins)) {\n deepForEach(origins, (origin) => {\n this.blocklist.add(extractHostname(origin));\n });\n } else {\n this.blocklist.add(extractHostname(origins));\n }\n }\n\n /**\n * Checks that a preconnect resource hint exists in the head for the\n * given src.\n *\n * @param rewrittenSrc src formatted with loader\n * @param originalNgSrc ngSrc value\n */\n assertPreconnect(rewrittenSrc: string, originalNgSrc: string): void {\n if (typeof ngServerMode !== 'undefined' && ngServerMode) return;\n\n const imgUrl = getUrl(rewrittenSrc, this.window!);\n if (this.blocklist.has(imgUrl.hostname) || this.alreadySeen.has(imgUrl.origin)) return;\n\n // Register this origin as seen, so we don't check it again later.\n this.alreadySeen.add(imgUrl.origin);\n\n // Note: we query for preconnect links only *once* and cache the results\n // for the entire lifespan of an application, since it's unlikely that the\n // list would change frequently. This allows to make sure there are no\n // performance implications of making extra DOM lookups for each image.\n this.preconnectLinks ??= this.queryPreconnectLinks();\n\n if (!this.preconnectLinks.has(imgUrl.origin)) {\n console.warn(\n formatRuntimeError(\n RuntimeErrorCode.PRIORITY_IMG_MISSING_PRECONNECT_TAG,\n `${imgDirectiveDetails(originalNgSrc)} there is no preconnect tag present for this ` +\n `image. Preconnecting to the origin(s) that serve priority images ensures that these ` +\n `images are delivered as soon as possible. To fix this, please add the following ` +\n `element into the <head> of the document:\\n` +\n ` <link rel=\"preconnect\" href=\"${imgUrl.origin}\">`,\n ),\n );\n }\n }\n\n private queryPreconnectLinks(): Set<string> {\n const preconnectUrls = new Set<string>();\n const links = this.document.querySelectorAll<HTMLLinkElement>('link[rel=preconnect]');\n for (const link of links) {\n const url = getUrl(link.href, this.window!);\n preconnectUrls.add(url.origin);\n }\n return preconnectUrls;\n }\n\n ngOnDestroy() {\n this.preconnectLinks?.clear();\n this.alreadySeen.clear();\n }\n}\n\n/**\n * Invokes a callback for each element in the array. Also invokes a callback\n * recursively for each nested array.\n */\nfunction deepForEach<T>(input: (T | any[])[], fn: (value: T) => void): void {\n for (let value of input) {\n Array.isArray(value) ? deepForEach(value, fn) : fn(value);\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {InjectionToken} from '@angular/core';\n\n/**\n * In SSR scenarios, a preload `<link>` element is generated for priority images.\n * Having a large number of preload tags may negatively affect the performance,\n * so we warn developers (by throwing an error) if the number of preloaded images\n * is above a certain threshold. This const specifies this threshold.\n */\nexport const DEFAULT_PRELOADED_IMAGES_LIMIT = 5;\n\n/**\n * Helps to keep track of priority images that already have a corresponding\n * preload tag (to avoid generating multiple preload tags with the same URL).\n *\n * This Set tracks the original src passed into the `ngSrc` input not the src after it has been\n * run through the specified `IMAGE_LOADER`.\n */\nexport const PRELOADED_IMAGES = new InjectionToken<Set<string>>(\n typeof ngDevMode === 'undefined' || ngDevMode ? 'NG_OPTIMIZED_PRELOADED_IMAGES' : '',\n {\n providedIn: 'root',\n factory: () => new Set<string>(),\n },\n);\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {\n inject,\n Injectable,\n Renderer2,\n ɵformatRuntimeError as formatRuntimeError,\n} from '@angular/core';\nimport {DOCUMENT} from '../../dom_tokens';\nimport {RuntimeErrorCode} from '../../errors';\n\nimport {DEFAULT_PRELOADED_IMAGES_LIMIT, PRELOADED_IMAGES} from './tokens';\n\n/**\n * @description Contains the logic needed to track and add preload link tags to the `<head>` tag. It\n * will also track what images have already had preload link tags added so as to not duplicate link\n * tags.\n *\n * In dev mode this service will validate that the number of preloaded images does not exceed the\n * configured default preloaded images limit: {@link DEFAULT_PRELOADED_IMAGES_LIMIT}.\n */\n@Injectable({providedIn: 'root'})\nexport class PreloadLinkCreator {\n private readonly preloadedImages = inject(PRELOADED_IMAGES);\n private readonly document = inject(DOCUMENT);\n private errorShown = false;\n\n /**\n * @description Add a preload `<link>` to the `<head>` of the `index.html` that is served from the\n * server while using Angular Universal and SSR to kick off image loads for high priority images.\n *\n * The `sizes` (passed in from the user) and `srcset` (parsed and formatted from `ngSrcset`)\n * properties used to set the corresponding attributes, `imagesizes` and `imagesrcset`\n * respectively, on the preload `<link>` tag so that the correctly sized image is preloaded from\n * the CDN.\n *\n * {@link https://web.dev/preload-responsive-images/#imagesrcset-and-imagesizes}\n *\n * @param renderer The `Renderer2` passed in from the directive\n * @param src The original src of the image that is set on the `ngSrc` input.\n * @param srcset The parsed and formatted srcset created from the `ngSrcset` input\n * @param sizes The value of the `sizes` attribute passed in to the `<img>` tag\n */\n createPreloadLinkTag(renderer: Renderer2, src: string, srcset?: string, sizes?: string): void {\n if (\n ngDevMode &&\n !this.errorShown &&\n this.preloadedImages.size >= DEFAULT_PRELOADED_IMAGES_LIMIT\n ) {\n this.errorShown = true;\n console.warn(\n formatRuntimeError(\n RuntimeErrorCode.TOO_MANY_PRELOADED_IMAGES,\n `The \\`NgOptimizedImage\\` directive has detected that more than ` +\n `${DEFAULT_PRELOADED_IMAGES_LIMIT} images were marked as priority. ` +\n `This might negatively affect an overall performance of the page. ` +\n `To fix this, remove the \"priority\" attribute from images with less priority.`,\n ),\n );\n }\n\n if (this.preloadedImages.has(src)) {\n return;\n }\n\n this.preloadedImages.add(src);\n\n const preload = renderer.createElement('link');\n renderer.setAttribute(preload, 'as', 'image');\n renderer.setAttribute(preload, 'href', src);\n renderer.setAttribute(preload, 'rel', 'preload');\n renderer.setAttribute(preload, 'fetchpriority', 'high');\n\n if (sizes) {\n renderer.setAttribute(preload, 'imageSizes', sizes);\n }\n\n if (srcset) {\n renderer.setAttribute(preload, 'imageSrcset', srcset);\n }\n\n renderer.appendChild(this.document.head, preload);\n }\n}\n","/**\n * @license\n * Copyright Google LLC All Rights Reserved.\n *\n * Use of this source code is governed by an MIT-style license that can be\n * found in the LICENSE file at https://angular.dev/license\n */\n\nimport {\n ApplicationRef,\n booleanAttribute,\n ChangeDetectorRef,\n DestroyRef,\n Directive,\n ElementRef,\n ɵformatRuntimeError as formatRuntimeError,\n ɵIMAGE_CONFIG as IMAGE_CONFIG,\n ɵIMAGE_CONFIG_DEFAULTS as IMAGE_CONFIG_DEFAULTS,\n ɵImageConfig as ImageConfig,\n inject,\n Injector,\n Input,\n NgZone,\n numberAttribute,\n OnChanges,\n OnInit,\n ɵperformanceMarkFeature as performanceMarkFeature,\n Renderer2,\n ɵRuntimeError as RuntimeError,\n ɵSafeValue as SafeValue,\n SimpleChanges,\n ɵunwrapSafeValue as unwrapSafeValue,\n} from '@angular/core';\n\nimport {RuntimeErrorCode} from '../../errors';\n\nimport {imgDirectiveDetails} from './error_helper';\nimport {cloudinaryLoaderInfo} from './image_loaders/cloudinary_loader';\nimport {\n IMAGE_LOADER,\n ImageLoader,\n ImageLoaderConfig,\n noopImageLoader,\n} from './image_loaders/image_loader';\nimport {imageKitLoaderInfo} from './image_loaders/imagekit_loader';\nimport {imgixLoaderInfo} from './image_loaders/imgix_loader';\nimport {netlifyLoaderInfo} from './image_loaders/netlify_loader';\nimport {LCPImageObserver} from './lcp_image_observer';\nimport {PreconnectLinkChecker} from './preconnect_link_checker';\nimport {PreloadLinkCreator} from './preload-link-creator';\n\n/**\n * When a Base64-encoded image is passed as an input to the `NgOptimizedImage` directive,\n * an error is thrown. The image content (as a string) might be very long, thus making\n * it hard to read an error message if the entire string is included. This const defines\n * the number of characters that should be included into the error message. The rest\n * of the content is truncated.\n */\nconst BASE64_IMG_MAX_LENGTH_IN_ERROR = 50;\n\n/**\n * RegExpr to determine whether a src in a srcset is using width descriptors.\n * Should match something like: \"100w, 200w\".\n */\nconst VALID_WIDTH_DESCRIPTOR_SRCSET = /^((\\s*\\d+w\\s*(,|$)){1,})$/;\n\n/**\n * RegExpr to determine whether a src in a srcset is using density descriptors.\n * Should match something like: \"1x, 2x, 50x\". Also supports decimals like \"1.5x, 1.50x\".\n */\nconst VALID_DENSITY_DESCRIPTOR_SRCSET = /^((\\s*\\d+(\\.\\d+)?x\\s*(,|$)){1,})$/;\n\n/**\n * Srcset values with a density descriptor higher than this value will actively\n * throw an error. Such densities are not permitted as they cause image sizes\n * to be unreasonably large and slow down LCP.\n */\nexport const ABSOLUTE_SRCSET_DENSITY_CAP = 3;\n\n/**\n * Used only in error message text to communicate best practices, as we will\n * only throw based on the slightly more conservative ABSOLUTE_SRCSET_DENSITY_CAP.\n */\nexport const RECOMMENDED_SRCSET_DENSITY_CAP = 2;\n\n/**\n * Used in generating automatic density-based srcsets\n */\nconst DENSITY_SRCSET_MULTIPLIERS = [1, 2];\n\n/**\n * Used to determine which breakpoints to use on full-width images\n */\nconst VIEWPORT_BREAKPOINT_CUTOFF = 640;\n/**\n * Used to determine whether two aspect ratios are similar in value.\n */\nconst ASPECT_RATIO_TOLERANCE = 0.1;\n\n/**\n * Used to determine whether the image has been requested at an overly\n * large size compared to the actual rendered image size (after taking\n * into account a typical device pixel ratio). In pixels.\n */\nconst OVERSIZED_IMAGE_TOLERANCE = 1000;\n\n/**\n * Used to limit automatic srcset generation of very large sources for\n * fixed-size images. In pixels.\n */\nconst FIXED_SRCSET_WIDTH_LIMIT = 1920;\nconst FIXED_SRCSET_HEIGHT_LIMIT = 1080;\n\n/**\n * Default blur radius of the CSS filter used on placeholder images, in pixels\n */\nexport const PLACEHOLDER_BLUR_AMOUNT = 15;\n\n/**\n * Placeholder dimension (height or width) limit in pixels. Angular produces a warning\n * when this limit is crossed.\n */\nconst PLACEHOLDER_DIMENSION_LIMIT = 1000;\n\n/**\n * Used to warn or error when the user provides an overly large dataURL for the placeholder\n * attribute.\n * Character count of Base64 images is 1 character per byte, and base64 encoding is approximately\n * 33% larger than base images, so 4000 characters is around 3KB on disk and 10000 characters is\n * around 7.7KB. Experimentally, 4000 characters is about 20x20px in PNG or medium-quality JPEG\n *