mirror of https://github.com/ghostfolio/ghostfolio
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
368 lines
12 KiB
368 lines
12 KiB
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 = null;
|
|
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
|
|
|