@angular/material
Version:
Angular Material
369 lines (366 loc) • 12.5 kB
JavaScript
import { trustedHTMLFromString } from '@angular/cdk/private';
import * as i1 from '@angular/common/http';
import * as i0 from '@angular/core';
import { SecurityContext, DOCUMENT, Injectable, Optional, Inject } from '@angular/core';
import * as i2 from '@angular/platform-browser';
import { of, throwError, forkJoin } from 'rxjs';
import { tap, map, catchError, finalize, share } from 'rxjs/operators';
function getMatIconNameNotFoundError(iconName) {
return Error(`Unable to find icon with the name "${iconName}"`);
}
function getMatIconNoHttpProviderError() {
return Error('Could not find HttpClient for use with Angular Material icons. ' + 'Please add provideHttpClient() to your providers.');
}
function getMatIconFailedToSanitizeUrlError(url) {
return Error(`The URL provided to MatIconRegistry was not trusted as a resource URL ` + `via Angular's DomSanitizer. Attempted URL was "${url}".`);
}
function getMatIconFailedToSanitizeLiteralError(literal) {
return Error(`The literal provided to MatIconRegistry was not trusted as safe HTML by ` + `Angular's DomSanitizer. Attempted literal was "${literal}".`);
}
class SvgIconConfig {
url;
svgText;
options;
svgElement;
constructor(url, svgText, options) {
this.url = url;
this.svgText = svgText;
this.options = options;
}
}
class MatIconRegistry {
_httpClient;
_sanitizer;
_errorHandler;
_document;
_svgIconConfigs = new Map();
_iconSetConfigs = new Map();
_cachedIconsByUrl = new Map();
_inProgressUrlFetches = new Map();
_fontCssClassesByAlias = new Map();
_resolvers = [];
_defaultFontSetClass = ['material-icons', 'mat-ligature-font'];
constructor(_httpClient, _sanitizer, document, _errorHandler) {
this._httpClient = _httpClient;
this._sanitizer = _sanitizer;
this._errorHandler = _errorHandler;
this._document = document;
}
addSvgIcon(iconName, url, options) {
return this.addSvgIconInNamespace('', iconName, url, options);
}
addSvgIconLiteral(iconName, literal, options) {
return this.addSvgIconLiteralInNamespace('', iconName, literal, options);
}
addSvgIconInNamespace(namespace, iconName, url, options) {
return this._addSvgIconConfig(namespace, iconName, new SvgIconConfig(url, null, options));
}
addSvgIconResolver(resolver) {
this._resolvers.push(resolver);
return this;
}
addSvgIconLiteralInNamespace(namespace, iconName, literal, options) {
const cleanLiteral = this._sanitizer.sanitize(SecurityContext.HTML, literal);
if (!cleanLiteral) {
throw getMatIconFailedToSanitizeLiteralError(literal);
}
const trustedLiteral = trustedHTMLFromString(cleanLiteral);
return this._addSvgIconConfig(namespace, iconName, new SvgIconConfig('', trustedLiteral, options));
}
addSvgIconSet(url, options) {
return this.addSvgIconSetInNamespace('', url, options);
}
addSvgIconSetLiteral(literal, options) {
return this.addSvgIconSetLiteralInNamespace('', literal, options);
}
addSvgIconSetInNamespace(namespace, url, options) {
return this._addSvgIconSetConfig(namespace, new SvgIconConfig(url, null, options));
}
addSvgIconSetLiteralInNamespace(namespace, literal, options) {
const cleanLiteral = this._sanitizer.sanitize(SecurityContext.HTML, literal);
if (!cleanLiteral) {
throw getMatIconFailedToSanitizeLiteralError(literal);
}
const trustedLiteral = trustedHTMLFromString(cleanLiteral);
return this._addSvgIconSetConfig(namespace, new SvgIconConfig('', trustedLiteral, options));
}
registerFontClassAlias(alias, classNames = alias) {
this._fontCssClassesByAlias.set(alias, classNames);
return this;
}
classNameForFontAlias(alias) {
return this._fontCssClassesByAlias.get(alias) || alias;
}
setDefaultFontSetClass(...classNames) {
this._defaultFontSetClass = classNames;
return this;
}
getDefaultFontSetClass() {
return this._defaultFontSetClass;
}
getSvgIconFromUrl(safeUrl) {
const url = this._sanitizer.sanitize(SecurityContext.RESOURCE_URL, safeUrl);
if (!url) {
throw getMatIconFailedToSanitizeUrlError(safeUrl);
}
const cachedIcon = this._cachedIconsByUrl.get(url);
if (cachedIcon) {
return of(cloneSvg(cachedIcon));
}
return this._loadSvgIconFromConfig(new SvgIconConfig(safeUrl, null)).pipe(tap(svg => this._cachedIconsByUrl.set(url, svg)), map(svg => cloneSvg(svg)));
}
getNamedSvgIcon(name, namespace = '') {
const key = iconKey(namespace, name);
let config = this._svgIconConfigs.get(key);
if (config) {
return this._getSvgFromConfig(config);
}
config = this._getIconConfigFromResolvers(namespace, name);
if (config) {
this._svgIconConfigs.set(key, config);
return this._getSvgFromConfig(config);
}
const iconSetConfigs = this._iconSetConfigs.get(namespace);
if (iconSetConfigs) {
return this._getSvgFromIconSetConfigs(name, iconSetConfigs);
}
return throwError(getMatIconNameNotFoundError(key));
}
ngOnDestroy() {
this._resolvers = [];
this._svgIconConfigs.clear();
this._iconSetConfigs.clear();
this._cachedIconsByUrl.clear();
}
_getSvgFromConfig(config) {
if (config.svgText) {
return of(cloneSvg(this._svgElementFromConfig(config)));
} else {
return this._loadSvgIconFromConfig(config).pipe(map(svg => cloneSvg(svg)));
}
}
_getSvgFromIconSetConfigs(name, iconSetConfigs) {
const namedIcon = this._extractIconWithNameFromAnySet(name, iconSetConfigs);
if (namedIcon) {
return of(namedIcon);
}
const iconSetFetchRequests = iconSetConfigs.filter(iconSetConfig => !iconSetConfig.svgText).map(iconSetConfig => {
return this._loadSvgIconSetFromConfig(iconSetConfig).pipe(catchError(err => {
const url = this._sanitizer.sanitize(SecurityContext.RESOURCE_URL, iconSetConfig.url);
const errorMessage = `Loading icon set URL: ${url} failed: ${err.message}`;
this._errorHandler.handleError(new Error(errorMessage));
return of(null);
}));
});
return forkJoin(iconSetFetchRequests).pipe(map(() => {
const foundIcon = this._extractIconWithNameFromAnySet(name, iconSetConfigs);
if (!foundIcon) {
throw getMatIconNameNotFoundError(name);
}
return foundIcon;
}));
}
_extractIconWithNameFromAnySet(iconName, iconSetConfigs) {
for (let i = iconSetConfigs.length - 1; i >= 0; i--) {
const config = iconSetConfigs[i];
if (config.svgText && config.svgText.toString().indexOf(iconName) > -1) {
const svg = this._svgElementFromConfig(config);
const foundIcon = this._extractSvgIconFromSet(svg, iconName, config.options);
if (foundIcon) {
return foundIcon;
}
}
}
return null;
}
_loadSvgIconFromConfig(config) {
return this._fetchIcon(config).pipe(tap(svgText => config.svgText = svgText), map(() => this._svgElementFromConfig(config)));
}
_loadSvgIconSetFromConfig(config) {
if (config.svgText) {
return of(null);
}
return this._fetchIcon(config).pipe(tap(svgText => config.svgText = svgText));
}
_extractSvgIconFromSet(iconSet, iconName, options) {
const iconSource = iconSet.querySelector(`[id="${iconName}"]`);
if (!iconSource) {
return null;
}
const iconElement = iconSource.cloneNode(true);
iconElement.removeAttribute('id');
if (iconElement.nodeName.toLowerCase() === 'svg') {
return this._setSvgAttributes(iconElement, options);
}
if (iconElement.nodeName.toLowerCase() === 'symbol') {
return this._setSvgAttributes(this._toSvgElement(iconElement), options);
}
const svg = this._svgElementFromString(trustedHTMLFromString('<svg></svg>'));
svg.appendChild(iconElement);
return this._setSvgAttributes(svg, options);
}
_svgElementFromString(str) {
const div = this._document.createElement('DIV');
div.innerHTML = str;
const svg = div.querySelector('svg');
if (!svg) {
throw Error('<svg> tag not found');
}
return svg;
}
_toSvgElement(element) {
const svg = this._svgElementFromString(trustedHTMLFromString('<svg></svg>'));
const attributes = element.attributes;
for (let i = 0; i < attributes.length; i++) {
const {
name,
value
} = attributes[i];
if (name !== 'id') {
svg.setAttribute(name, value);
}
}
for (let i = 0; i < element.childNodes.length; i++) {
if (element.childNodes[i].nodeType === this._document.ELEMENT_NODE) {
svg.appendChild(element.childNodes[i].cloneNode(true));
}
}
return svg;
}
_setSvgAttributes(svg, options) {
svg.setAttribute('fit', '');
svg.setAttribute('height', '100%');
svg.setAttribute('width', '100%');
svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
svg.setAttribute('focusable', 'false');
if (options && options.viewBox) {
svg.setAttribute('viewBox', options.viewBox);
}
return svg;
}
_fetchIcon(iconConfig) {
const {
url: safeUrl,
options
} = iconConfig;
const withCredentials = options?.withCredentials ?? false;
if (!this._httpClient) {
throw getMatIconNoHttpProviderError();
}
if (safeUrl == null) {
throw Error(`Cannot fetch icon from URL "${safeUrl}".`);
}
const url = this._sanitizer.sanitize(SecurityContext.RESOURCE_URL, safeUrl);
if (!url) {
throw getMatIconFailedToSanitizeUrlError(safeUrl);
}
const inProgressFetch = this._inProgressUrlFetches.get(url);
if (inProgressFetch) {
return inProgressFetch;
}
const req = this._httpClient.get(url, {
responseType: 'text',
withCredentials
}).pipe(map(svg => {
return trustedHTMLFromString(svg);
}), finalize(() => this._inProgressUrlFetches.delete(url)), share());
this._inProgressUrlFetches.set(url, req);
return req;
}
_addSvgIconConfig(namespace, iconName, config) {
this._svgIconConfigs.set(iconKey(namespace, iconName), config);
return this;
}
_addSvgIconSetConfig(namespace, config) {
const configNamespace = this._iconSetConfigs.get(namespace);
if (configNamespace) {
configNamespace.push(config);
} else {
this._iconSetConfigs.set(namespace, [config]);
}
return this;
}
_svgElementFromConfig(config) {
if (!config.svgElement) {
const svg = this._svgElementFromString(config.svgText);
this._setSvgAttributes(svg, config.options);
config.svgElement = svg;
}
return config.svgElement;
}
_getIconConfigFromResolvers(namespace, name) {
for (let i = 0; i < this._resolvers.length; i++) {
const result = this._resolvers[i](name, namespace);
if (result) {
return isSafeUrlWithOptions(result) ? new SvgIconConfig(result.url, null, result.options) : new SvgIconConfig(result, null);
}
}
return undefined;
}
static ɵfac = i0.ɵɵngDeclareFactory({
minVersion: "12.0.0",
version: "21.0.3",
ngImport: i0,
type: MatIconRegistry,
deps: [{
token: i1.HttpClient,
optional: true
}, {
token: i2.DomSanitizer
}, {
token: DOCUMENT,
optional: true
}, {
token: i0.ErrorHandler
}],
target: i0.ɵɵFactoryTarget.Injectable
});
static ɵprov = i0.ɵɵngDeclareInjectable({
minVersion: "12.0.0",
version: "21.0.3",
ngImport: i0,
type: MatIconRegistry,
providedIn: 'root'
});
}
i0.ɵɵngDeclareClassMetadata({
minVersion: "12.0.0",
version: "21.0.3",
ngImport: i0,
type: MatIconRegistry,
decorators: [{
type: Injectable,
args: [{
providedIn: 'root'
}]
}],
ctorParameters: () => [{
type: i1.HttpClient,
decorators: [{
type: Optional
}]
}, {
type: i2.DomSanitizer
}, {
type: undefined,
decorators: [{
type: Optional
}, {
type: Inject,
args: [DOCUMENT]
}]
}, {
type: i0.ErrorHandler
}]
});
function cloneSvg(svg) {
return svg.cloneNode(true);
}
function iconKey(namespace, name) {
return namespace + ':' + name;
}
function isSafeUrlWithOptions(value) {
return !!(value.url && value.options);
}
export { MatIconRegistry, getMatIconFailedToSanitizeLiteralError, getMatIconFailedToSanitizeUrlError, getMatIconNameNotFoundError, getMatIconNoHttpProviderError };
//# sourceMappingURL=_icon-registry-chunk.mjs.map