source: trip-planner-front/node_modules/@angular/material/fesm2015/icon.js@ eed0bf8

Last change on this file since eed0bf8 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • Property mode set to 100644
File size: 41.6 KB
RevLine 
[6a3a178]1import * as i0 from '@angular/core';
2import { SecurityContext, Injectable, Optional, Inject, ErrorHandler, SkipSelf, InjectionToken, inject, Component, ViewEncapsulation, ChangeDetectionStrategy, ElementRef, Attribute, Input, NgModule } from '@angular/core';
3import { mixinColor, MatCommonModule } from '@angular/material/core';
4import { coerceBooleanProperty } from '@angular/cdk/coercion';
5import * as i3 from '@angular/common';
6import { DOCUMENT } from '@angular/common';
7import { of, throwError, forkJoin, Subscription } from 'rxjs';
8import { tap, map, catchError, finalize, share, take } from 'rxjs/operators';
9import * as i1 from '@angular/common/http';
10import { HttpClient } from '@angular/common/http';
11import * as i2 from '@angular/platform-browser';
12import { DomSanitizer } from '@angular/platform-browser';
13
14/**
15 * @license
16 * Copyright Google LLC All Rights Reserved.
17 *
18 * Use of this source code is governed by an MIT-style license that can be
19 * found in the LICENSE file at https://angular.io/license
20 */
21/**
22 * Returns an exception to be thrown in the case when attempting to
23 * load an icon with a name that cannot be found.
24 * @docs-private
25 */
26function getMatIconNameNotFoundError(iconName) {
27 return Error(`Unable to find icon with the name "${iconName}"`);
28}
29/**
30 * Returns an exception to be thrown when the consumer attempts to use
31 * `<mat-icon>` without including @angular/common/http.
32 * @docs-private
33 */
34function getMatIconNoHttpProviderError() {
35 return Error('Could not find HttpClient provider for use with Angular Material icons. ' +
36 'Please include the HttpClientModule from @angular/common/http in your ' +
37 'app imports.');
38}
39/**
40 * Returns an exception to be thrown when a URL couldn't be sanitized.
41 * @param url URL that was attempted to be sanitized.
42 * @docs-private
43 */
44function getMatIconFailedToSanitizeUrlError(url) {
45 return Error(`The URL provided to MatIconRegistry was not trusted as a resource URL ` +
46 `via Angular's DomSanitizer. Attempted URL was "${url}".`);
47}
48/**
49 * Returns an exception to be thrown when a HTML string couldn't be sanitized.
50 * @param literal HTML that was attempted to be sanitized.
51 * @docs-private
52 */
53function getMatIconFailedToSanitizeLiteralError(literal) {
54 return Error(`The literal provided to MatIconRegistry was not trusted as safe HTML by ` +
55 `Angular's DomSanitizer. Attempted literal was "${literal}".`);
56}
57/**
58 * Configuration for an icon, including the URL and possibly the cached SVG element.
59 * @docs-private
60 */
61class SvgIconConfig {
62 constructor(url, svgText, options) {
63 this.url = url;
64 this.svgText = svgText;
65 this.options = options;
66 }
67}
68/**
69 * Service to register and display icons used by the `<mat-icon>` component.
70 * - Registers icon URLs by namespace and name.
71 * - Registers icon set URLs by namespace.
72 * - Registers aliases for CSS classes, for use with icon fonts.
73 * - Loads icons from URLs and extracts individual icons from icon sets.
74 */
75class MatIconRegistry {
76 constructor(_httpClient, _sanitizer, document, _errorHandler) {
77 this._httpClient = _httpClient;
78 this._sanitizer = _sanitizer;
79 this._errorHandler = _errorHandler;
80 /**
81 * URLs and cached SVG elements for individual icons. Keys are of the format "[namespace]:[icon]".
82 */
83 this._svgIconConfigs = new Map();
84 /**
85 * SvgIconConfig objects and cached SVG elements for icon sets, keyed by namespace.
86 * Multiple icon sets can be registered under the same namespace.
87 */
88 this._iconSetConfigs = new Map();
89 /** Cache for icons loaded by direct URLs. */
90 this._cachedIconsByUrl = new Map();
91 /** In-progress icon fetches. Used to coalesce multiple requests to the same URL. */
92 this._inProgressUrlFetches = new Map();
93 /** Map from font identifiers to their CSS class names. Used for icon fonts. */
94 this._fontCssClassesByAlias = new Map();
95 /** Registered icon resolver functions. */
96 this._resolvers = [];
97 /**
98 * The CSS class to apply when an `<mat-icon>` component has no icon name, url, or font specified.
99 * The default 'material-icons' value assumes that the material icon font has been loaded as
100 * described at http://google.github.io/material-design-icons/#icon-font-for-the-web
101 */
102 this._defaultFontSetClass = 'material-icons';
103 this._document = document;
104 }
105 /**
106 * Registers an icon by URL in the default namespace.
107 * @param iconName Name under which the icon should be registered.
108 * @param url
109 */
110 addSvgIcon(iconName, url, options) {
111 return this.addSvgIconInNamespace('', iconName, url, options);
112 }
113 /**
114 * Registers an icon using an HTML string in the default namespace.
115 * @param iconName Name under which the icon should be registered.
116 * @param literal SVG source of the icon.
117 */
118 addSvgIconLiteral(iconName, literal, options) {
119 return this.addSvgIconLiteralInNamespace('', iconName, literal, options);
120 }
121 /**
122 * Registers an icon by URL in the specified namespace.
123 * @param namespace Namespace in which the icon should be registered.
124 * @param iconName Name under which the icon should be registered.
125 * @param url
126 */
127 addSvgIconInNamespace(namespace, iconName, url, options) {
128 return this._addSvgIconConfig(namespace, iconName, new SvgIconConfig(url, null, options));
129 }
130 /**
131 * Registers an icon resolver function with the registry. The function will be invoked with the
132 * name and namespace of an icon when the registry tries to resolve the URL from which to fetch
133 * the icon. The resolver is expected to return a `SafeResourceUrl` that points to the icon,
134 * an object with the icon URL and icon options, or `null` if the icon is not supported. Resolvers
135 * will be invoked in the order in which they have been registered.
136 * @param resolver Resolver function to be registered.
137 */
138 addSvgIconResolver(resolver) {
139 this._resolvers.push(resolver);
140 return this;
141 }
142 /**
143 * Registers an icon using an HTML string in the specified namespace.
144 * @param namespace Namespace in which the icon should be registered.
145 * @param iconName Name under which the icon should be registered.
146 * @param literal SVG source of the icon.
147 */
148 addSvgIconLiteralInNamespace(namespace, iconName, literal, options) {
149 const cleanLiteral = this._sanitizer.sanitize(SecurityContext.HTML, literal);
150 // TODO: add an ngDevMode check
151 if (!cleanLiteral) {
152 throw getMatIconFailedToSanitizeLiteralError(literal);
153 }
154 return this._addSvgIconConfig(namespace, iconName, new SvgIconConfig('', cleanLiteral, options));
155 }
156 /**
157 * Registers an icon set by URL in the default namespace.
158 * @param url
159 */
160 addSvgIconSet(url, options) {
161 return this.addSvgIconSetInNamespace('', url, options);
162 }
163 /**
164 * Registers an icon set using an HTML string in the default namespace.
165 * @param literal SVG source of the icon set.
166 */
167 addSvgIconSetLiteral(literal, options) {
168 return this.addSvgIconSetLiteralInNamespace('', literal, options);
169 }
170 /**
171 * Registers an icon set by URL in the specified namespace.
172 * @param namespace Namespace in which to register the icon set.
173 * @param url
174 */
175 addSvgIconSetInNamespace(namespace, url, options) {
176 return this._addSvgIconSetConfig(namespace, new SvgIconConfig(url, null, options));
177 }
178 /**
179 * Registers an icon set using an HTML string in the specified namespace.
180 * @param namespace Namespace in which to register the icon set.
181 * @param literal SVG source of the icon set.
182 */
183 addSvgIconSetLiteralInNamespace(namespace, literal, options) {
184 const cleanLiteral = this._sanitizer.sanitize(SecurityContext.HTML, literal);
185 if (!cleanLiteral) {
186 throw getMatIconFailedToSanitizeLiteralError(literal);
187 }
188 return this._addSvgIconSetConfig(namespace, new SvgIconConfig('', cleanLiteral, options));
189 }
190 /**
191 * Defines an alias for a CSS class name to be used for icon fonts. Creating an matIcon
192 * component with the alias as the fontSet input will cause the class name to be applied
193 * to the `<mat-icon>` element.
194 *
195 * @param alias Alias for the font.
196 * @param className Class name override to be used instead of the alias.
197 */
198 registerFontClassAlias(alias, className = alias) {
199 this._fontCssClassesByAlias.set(alias, className);
200 return this;
201 }
202 /**
203 * Returns the CSS class name associated with the alias by a previous call to
204 * registerFontClassAlias. If no CSS class has been associated, returns the alias unmodified.
205 */
206 classNameForFontAlias(alias) {
207 return this._fontCssClassesByAlias.get(alias) || alias;
208 }
209 /**
210 * Sets the CSS class name to be used for icon fonts when an `<mat-icon>` component does not
211 * have a fontSet input value, and is not loading an icon by name or URL.
212 *
213 * @param className
214 */
215 setDefaultFontSetClass(className) {
216 this._defaultFontSetClass = className;
217 return this;
218 }
219 /**
220 * Returns the CSS class name to be used for icon fonts when an `<mat-icon>` component does not
221 * have a fontSet input value, and is not loading an icon by name or URL.
222 */
223 getDefaultFontSetClass() {
224 return this._defaultFontSetClass;
225 }
226 /**
227 * Returns an Observable that produces the icon (as an `<svg>` DOM element) from the given URL.
228 * The response from the URL may be cached so this will not always cause an HTTP request, but
229 * the produced element will always be a new copy of the originally fetched icon. (That is,
230 * it will not contain any modifications made to elements previously returned).
231 *
232 * @param safeUrl URL from which to fetch the SVG icon.
233 */
234 getSvgIconFromUrl(safeUrl) {
235 const url = this._sanitizer.sanitize(SecurityContext.RESOURCE_URL, safeUrl);
236 if (!url) {
237 throw getMatIconFailedToSanitizeUrlError(safeUrl);
238 }
239 const cachedIcon = this._cachedIconsByUrl.get(url);
240 if (cachedIcon) {
241 return of(cloneSvg(cachedIcon));
242 }
243 return this._loadSvgIconFromConfig(new SvgIconConfig(safeUrl, null)).pipe(tap(svg => this._cachedIconsByUrl.set(url, svg)), map(svg => cloneSvg(svg)));
244 }
245 /**
246 * Returns an Observable that produces the icon (as an `<svg>` DOM element) with the given name
247 * and namespace. The icon must have been previously registered with addIcon or addIconSet;
248 * if not, the Observable will throw an error.
249 *
250 * @param name Name of the icon to be retrieved.
251 * @param namespace Namespace in which to look for the icon.
252 */
253 getNamedSvgIcon(name, namespace = '') {
254 const key = iconKey(namespace, name);
255 let config = this._svgIconConfigs.get(key);
256 // Return (copy of) cached icon if possible.
257 if (config) {
258 return this._getSvgFromConfig(config);
259 }
260 // Otherwise try to resolve the config from one of the resolver functions.
261 config = this._getIconConfigFromResolvers(namespace, name);
262 if (config) {
263 this._svgIconConfigs.set(key, config);
264 return this._getSvgFromConfig(config);
265 }
266 // See if we have any icon sets registered for the namespace.
267 const iconSetConfigs = this._iconSetConfigs.get(namespace);
268 if (iconSetConfigs) {
269 return this._getSvgFromIconSetConfigs(name, iconSetConfigs);
270 }
271 return throwError(getMatIconNameNotFoundError(key));
272 }
273 ngOnDestroy() {
274 this._resolvers = [];
275 this._svgIconConfigs.clear();
276 this._iconSetConfigs.clear();
277 this._cachedIconsByUrl.clear();
278 }
279 /**
280 * Returns the cached icon for a SvgIconConfig if available, or fetches it from its URL if not.
281 */
282 _getSvgFromConfig(config) {
283 if (config.svgText) {
284 // We already have the SVG element for this icon, return a copy.
285 return of(cloneSvg(this._svgElementFromConfig(config)));
286 }
287 else {
288 // Fetch the icon from the config's URL, cache it, and return a copy.
289 return this._loadSvgIconFromConfig(config).pipe(map(svg => cloneSvg(svg)));
290 }
291 }
292 /**
293 * Attempts to find an icon with the specified name in any of the SVG icon sets.
294 * First searches the available cached icons for a nested element with a matching name, and
295 * if found copies the element to a new `<svg>` element. If not found, fetches all icon sets
296 * that have not been cached, and searches again after all fetches are completed.
297 * The returned Observable produces the SVG element if possible, and throws
298 * an error if no icon with the specified name can be found.
299 */
300 _getSvgFromIconSetConfigs(name, iconSetConfigs) {
301 // For all the icon set SVG elements we've fetched, see if any contain an icon with the
302 // requested name.
303 const namedIcon = this._extractIconWithNameFromAnySet(name, iconSetConfigs);
304 if (namedIcon) {
305 // We could cache namedIcon in _svgIconConfigs, but since we have to make a copy every
306 // time anyway, there's probably not much advantage compared to just always extracting
307 // it from the icon set.
308 return of(namedIcon);
309 }
310 // Not found in any cached icon sets. If there are icon sets with URLs that we haven't
311 // fetched, fetch them now and look for iconName in the results.
312 const iconSetFetchRequests = iconSetConfigs
313 .filter(iconSetConfig => !iconSetConfig.svgText)
314 .map(iconSetConfig => {
315 return this._loadSvgIconSetFromConfig(iconSetConfig).pipe(catchError((err) => {
316 const url = this._sanitizer.sanitize(SecurityContext.RESOURCE_URL, iconSetConfig.url);
317 // Swallow errors fetching individual URLs so the
318 // combined Observable won't necessarily fail.
319 const errorMessage = `Loading icon set URL: ${url} failed: ${err.message}`;
320 this._errorHandler.handleError(new Error(errorMessage));
321 return of(null);
322 }));
323 });
324 // Fetch all the icon set URLs. When the requests complete, every IconSet should have a
325 // cached SVG element (unless the request failed), and we can check again for the icon.
326 return forkJoin(iconSetFetchRequests).pipe(map(() => {
327 const foundIcon = this._extractIconWithNameFromAnySet(name, iconSetConfigs);
328 // TODO: add an ngDevMode check
329 if (!foundIcon) {
330 throw getMatIconNameNotFoundError(name);
331 }
332 return foundIcon;
333 }));
334 }
335 /**
336 * Searches the cached SVG elements for the given icon sets for a nested icon element whose "id"
337 * tag matches the specified name. If found, copies the nested element to a new SVG element and
338 * returns it. Returns null if no matching element is found.
339 */
340 _extractIconWithNameFromAnySet(iconName, iconSetConfigs) {
341 // Iterate backwards, so icon sets added later have precedence.
342 for (let i = iconSetConfigs.length - 1; i >= 0; i--) {
343 const config = iconSetConfigs[i];
344 // Parsing the icon set's text into an SVG element can be expensive. We can avoid some of
345 // the parsing by doing a quick check using `indexOf` to see if there's any chance for the
346 // icon to be in the set. This won't be 100% accurate, but it should help us avoid at least
347 // some of the parsing.
348 if (config.svgText && config.svgText.indexOf(iconName) > -1) {
349 const svg = this._svgElementFromConfig(config);
350 const foundIcon = this._extractSvgIconFromSet(svg, iconName, config.options);
351 if (foundIcon) {
352 return foundIcon;
353 }
354 }
355 }
356 return null;
357 }
358 /**
359 * Loads the content of the icon URL specified in the SvgIconConfig and creates an SVG element
360 * from it.
361 */
362 _loadSvgIconFromConfig(config) {
363 return this._fetchIcon(config).pipe(tap(svgText => config.svgText = svgText), map(() => this._svgElementFromConfig(config)));
364 }
365 /**
366 * Loads the content of the icon set URL specified in the
367 * SvgIconConfig and attaches it to the config.
368 */
369 _loadSvgIconSetFromConfig(config) {
370 if (config.svgText) {
371 return of(null);
372 }
373 return this._fetchIcon(config).pipe(tap(svgText => config.svgText = svgText));
374 }
375 /**
376 * Searches the cached element of the given SvgIconConfig for a nested icon element whose "id"
377 * tag matches the specified name. If found, copies the nested element to a new SVG element and
378 * returns it. Returns null if no matching element is found.
379 */
380 _extractSvgIconFromSet(iconSet, iconName, options) {
381 // Use the `id="iconName"` syntax in order to escape special
382 // characters in the ID (versus using the #iconName syntax).
383 const iconSource = iconSet.querySelector(`[id="${iconName}"]`);
384 if (!iconSource) {
385 return null;
386 }
387 // Clone the element and remove the ID to prevent multiple elements from being added
388 // to the page with the same ID.
389 const iconElement = iconSource.cloneNode(true);
390 iconElement.removeAttribute('id');
391 // If the icon node is itself an <svg> node, clone and return it directly. If not, set it as
392 // the content of a new <svg> node.
393 if (iconElement.nodeName.toLowerCase() === 'svg') {
394 return this._setSvgAttributes(iconElement, options);
395 }
396 // If the node is a <symbol>, it won't be rendered so we have to convert it into <svg>. Note
397 // that the same could be achieved by referring to it via <use href="#id">, however the <use>
398 // tag is problematic on Firefox, because it needs to include the current page path.
399 if (iconElement.nodeName.toLowerCase() === 'symbol') {
400 return this._setSvgAttributes(this._toSvgElement(iconElement), options);
401 }
402 // createElement('SVG') doesn't work as expected; the DOM ends up with
403 // the correct nodes, but the SVG content doesn't render. Instead we
404 // have to create an empty SVG node using innerHTML and append its content.
405 // Elements created using DOMParser.parseFromString have the same problem.
406 // http://stackoverflow.com/questions/23003278/svg-innerhtml-in-firefox-can-not-display
407 const svg = this._svgElementFromString('<svg></svg>');
408 // Clone the node so we don't remove it from the parent icon set element.
409 svg.appendChild(iconElement);
410 return this._setSvgAttributes(svg, options);
411 }
412 /**
413 * Creates a DOM element from the given SVG string.
414 */
415 _svgElementFromString(str) {
416 const div = this._document.createElement('DIV');
417 div.innerHTML = str;
418 const svg = div.querySelector('svg');
419 // TODO: add an ngDevMode check
420 if (!svg) {
421 throw Error('<svg> tag not found');
422 }
423 return svg;
424 }
425 /**
426 * Converts an element into an SVG node by cloning all of its children.
427 */
428 _toSvgElement(element) {
429 const svg = this._svgElementFromString('<svg></svg>');
430 const attributes = element.attributes;
431 // Copy over all the attributes from the `symbol` to the new SVG, except the id.
432 for (let i = 0; i < attributes.length; i++) {
433 const { name, value } = attributes[i];
434 if (name !== 'id') {
435 svg.setAttribute(name, value);
436 }
437 }
438 for (let i = 0; i < element.childNodes.length; i++) {
439 if (element.childNodes[i].nodeType === this._document.ELEMENT_NODE) {
440 svg.appendChild(element.childNodes[i].cloneNode(true));
441 }
442 }
443 return svg;
444 }
445 /**
446 * Sets the default attributes for an SVG element to be used as an icon.
447 */
448 _setSvgAttributes(svg, options) {
449 svg.setAttribute('fit', '');
450 svg.setAttribute('height', '100%');
451 svg.setAttribute('width', '100%');
452 svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
453 svg.setAttribute('focusable', 'false'); // Disable IE11 default behavior to make SVGs focusable.
454 if (options && options.viewBox) {
455 svg.setAttribute('viewBox', options.viewBox);
456 }
457 return svg;
458 }
459 /**
460 * Returns an Observable which produces the string contents of the given icon. Results may be
461 * cached, so future calls with the same URL may not cause another HTTP request.
462 */
463 _fetchIcon(iconConfig) {
464 var _a;
465 const { url: safeUrl, options } = iconConfig;
466 const withCredentials = (_a = options === null || options === void 0 ? void 0 : options.withCredentials) !== null && _a !== void 0 ? _a : false;
467 if (!this._httpClient) {
468 throw getMatIconNoHttpProviderError();
469 }
470 // TODO: add an ngDevMode check
471 if (safeUrl == null) {
472 throw Error(`Cannot fetch icon from URL "${safeUrl}".`);
473 }
474 const url = this._sanitizer.sanitize(SecurityContext.RESOURCE_URL, safeUrl);
475 // TODO: add an ngDevMode check
476 if (!url) {
477 throw getMatIconFailedToSanitizeUrlError(safeUrl);
478 }
479 // Store in-progress fetches to avoid sending a duplicate request for a URL when there is
480 // already a request in progress for that URL. It's necessary to call share() on the
481 // Observable returned by http.get() so that multiple subscribers don't cause multiple XHRs.
482 const inProgressFetch = this._inProgressUrlFetches.get(url);
483 if (inProgressFetch) {
484 return inProgressFetch;
485 }
486 const req = this._httpClient.get(url, { responseType: 'text', withCredentials }).pipe(finalize(() => this._inProgressUrlFetches.delete(url)), share());
487 this._inProgressUrlFetches.set(url, req);
488 return req;
489 }
490 /**
491 * Registers an icon config by name in the specified namespace.
492 * @param namespace Namespace in which to register the icon config.
493 * @param iconName Name under which to register the config.
494 * @param config Config to be registered.
495 */
496 _addSvgIconConfig(namespace, iconName, config) {
497 this._svgIconConfigs.set(iconKey(namespace, iconName), config);
498 return this;
499 }
500 /**
501 * Registers an icon set config in the specified namespace.
502 * @param namespace Namespace in which to register the icon config.
503 * @param config Config to be registered.
504 */
505 _addSvgIconSetConfig(namespace, config) {
506 const configNamespace = this._iconSetConfigs.get(namespace);
507 if (configNamespace) {
508 configNamespace.push(config);
509 }
510 else {
511 this._iconSetConfigs.set(namespace, [config]);
512 }
513 return this;
514 }
515 /** Parses a config's text into an SVG element. */
516 _svgElementFromConfig(config) {
517 if (!config.svgElement) {
518 const svg = this._svgElementFromString(config.svgText);
519 this._setSvgAttributes(svg, config.options);
520 config.svgElement = svg;
521 }
522 return config.svgElement;
523 }
524 /** Tries to create an icon config through the registered resolver functions. */
525 _getIconConfigFromResolvers(namespace, name) {
526 for (let i = 0; i < this._resolvers.length; i++) {
527 const result = this._resolvers[i](name, namespace);
528 if (result) {
529 return isSafeUrlWithOptions(result) ?
530 new SvgIconConfig(result.url, null, result.options) :
531 new SvgIconConfig(result, null);
532 }
533 }
534 return undefined;
535 }
536}
537MatIconRegistry.ɵprov = i0.ɵɵdefineInjectable({ factory: function MatIconRegistry_Factory() { return new MatIconRegistry(i0.ɵɵinject(i1.HttpClient, 8), i0.ɵɵinject(i2.DomSanitizer), i0.ɵɵinject(i3.DOCUMENT, 8), i0.ɵɵinject(i0.ErrorHandler)); }, token: MatIconRegistry, providedIn: "root" });
538MatIconRegistry.decorators = [
539 { type: Injectable, args: [{ providedIn: 'root' },] }
540];
541MatIconRegistry.ctorParameters = () => [
542 { type: HttpClient, decorators: [{ type: Optional }] },
543 { type: DomSanitizer },
544 { type: undefined, decorators: [{ type: Optional }, { type: Inject, args: [DOCUMENT,] }] },
545 { type: ErrorHandler }
546];
547/** @docs-private */
548function ICON_REGISTRY_PROVIDER_FACTORY(parentRegistry, httpClient, sanitizer, errorHandler, document) {
549 return parentRegistry || new MatIconRegistry(httpClient, sanitizer, document, errorHandler);
550}
551/** @docs-private */
552const ICON_REGISTRY_PROVIDER = {
553 // If there is already an MatIconRegistry available, use that. Otherwise, provide a new one.
554 provide: MatIconRegistry,
555 deps: [
556 [new Optional(), new SkipSelf(), MatIconRegistry],
557 [new Optional(), HttpClient],
558 DomSanitizer,
559 ErrorHandler,
560 [new Optional(), DOCUMENT],
561 ],
562 useFactory: ICON_REGISTRY_PROVIDER_FACTORY,
563};
564/** Clones an SVGElement while preserving type information. */
565function cloneSvg(svg) {
566 return svg.cloneNode(true);
567}
568/** Returns the cache key to use for an icon namespace and name. */
569function iconKey(namespace, name) {
570 return namespace + ':' + name;
571}
572function isSafeUrlWithOptions(value) {
573 return !!(value.url && value.options);
574}
575
576/**
577 * @license
578 * Copyright Google LLC All Rights Reserved.
579 *
580 * Use of this source code is governed by an MIT-style license that can be
581 * found in the LICENSE file at https://angular.io/license
582 */
583// Boilerplate for applying mixins to MatIcon.
584/** @docs-private */
585const _MatIconBase = mixinColor(class {
586 constructor(_elementRef) {
587 this._elementRef = _elementRef;
588 }
589});
590/**
591 * Injection token used to provide the current location to `MatIcon`.
592 * Used to handle server-side rendering and to stub out during unit tests.
593 * @docs-private
594 */
595const MAT_ICON_LOCATION = new InjectionToken('mat-icon-location', {
596 providedIn: 'root',
597 factory: MAT_ICON_LOCATION_FACTORY
598});
599/** @docs-private */
600function MAT_ICON_LOCATION_FACTORY() {
601 const _document = inject(DOCUMENT);
602 const _location = _document ? _document.location : null;
603 return {
604 // Note that this needs to be a function, rather than a property, because Angular
605 // will only resolve it once, but we want the current path on each call.
606 getPathname: () => _location ? (_location.pathname + _location.search) : ''
607 };
608}
609/** SVG attributes that accept a FuncIRI (e.g. `url(<something>)`). */
610const funcIriAttributes = [
611 'clip-path',
612 'color-profile',
613 'src',
614 'cursor',
615 'fill',
616 'filter',
617 'marker',
618 'marker-start',
619 'marker-mid',
620 'marker-end',
621 'mask',
622 'stroke'
623];
624const ɵ0 = attr => `[${attr}]`;
625/** Selector that can be used to find all elements that are using a `FuncIRI`. */
626const funcIriAttributeSelector = funcIriAttributes.map(ɵ0).join(', ');
627/** Regex that can be used to extract the id out of a FuncIRI. */
628const funcIriPattern = /^url\(['"]?#(.*?)['"]?\)$/;
629/**
630 * Component to display an icon. It can be used in the following ways:
631 *
632 * - Specify the svgIcon input to load an SVG icon from a URL previously registered with the
633 * addSvgIcon, addSvgIconInNamespace, addSvgIconSet, or addSvgIconSetInNamespace methods of
634 * MatIconRegistry. If the svgIcon value contains a colon it is assumed to be in the format
635 * "[namespace]:[name]", if not the value will be the name of an icon in the default namespace.
636 * Examples:
637 * `<mat-icon svgIcon="left-arrow"></mat-icon>
638 * <mat-icon svgIcon="animals:cat"></mat-icon>`
639 *
640 * - Use a font ligature as an icon by putting the ligature text in the content of the `<mat-icon>`
641 * component. By default the Material icons font is used as described at
642 * http://google.github.io/material-design-icons/#icon-font-for-the-web. You can specify an
643 * alternate font by setting the fontSet input to either the CSS class to apply to use the
644 * desired font, or to an alias previously registered with MatIconRegistry.registerFontClassAlias.
645 * Examples:
646 * `<mat-icon>home</mat-icon>
647 * <mat-icon fontSet="myfont">sun</mat-icon>`
648 *
649 * - Specify a font glyph to be included via CSS rules by setting the fontSet input to specify the
650 * font, and the fontIcon input to specify the icon. Typically the fontIcon will specify a
651 * CSS class which causes the glyph to be displayed via a :before selector, as in
652 * https://fortawesome.github.io/Font-Awesome/examples/
653 * Example:
654 * `<mat-icon fontSet="fa" fontIcon="alarm"></mat-icon>`
655 */
656class MatIcon extends _MatIconBase {
657 constructor(elementRef, _iconRegistry, ariaHidden, _location, _errorHandler) {
658 super(elementRef);
659 this._iconRegistry = _iconRegistry;
660 this._location = _location;
661 this._errorHandler = _errorHandler;
662 this._inline = false;
663 /** Subscription to the current in-progress SVG icon request. */
664 this._currentIconFetch = Subscription.EMPTY;
665 // If the user has not explicitly set aria-hidden, mark the icon as hidden, as this is
666 // the right thing to do for the majority of icon use-cases.
667 if (!ariaHidden) {
668 elementRef.nativeElement.setAttribute('aria-hidden', 'true');
669 }
670 }
671 /**
672 * Whether the icon should be inlined, automatically sizing the icon to match the font size of
673 * the element the icon is contained in.
674 */
675 get inline() {
676 return this._inline;
677 }
678 set inline(inline) {
679 this._inline = coerceBooleanProperty(inline);
680 }
681 /** Name of the icon in the SVG icon set. */
682 get svgIcon() { return this._svgIcon; }
683 set svgIcon(value) {
684 if (value !== this._svgIcon) {
685 if (value) {
686 this._updateSvgIcon(value);
687 }
688 else if (this._svgIcon) {
689 this._clearSvgElement();
690 }
691 this._svgIcon = value;
692 }
693 }
694 /** Font set that the icon is a part of. */
695 get fontSet() { return this._fontSet; }
696 set fontSet(value) {
697 const newValue = this._cleanupFontValue(value);
698 if (newValue !== this._fontSet) {
699 this._fontSet = newValue;
700 this._updateFontIconClasses();
701 }
702 }
703 /** Name of an icon within a font set. */
704 get fontIcon() { return this._fontIcon; }
705 set fontIcon(value) {
706 const newValue = this._cleanupFontValue(value);
707 if (newValue !== this._fontIcon) {
708 this._fontIcon = newValue;
709 this._updateFontIconClasses();
710 }
711 }
712 /**
713 * Splits an svgIcon binding value into its icon set and icon name components.
714 * Returns a 2-element array of [(icon set), (icon name)].
715 * The separator for the two fields is ':'. If there is no separator, an empty
716 * string is returned for the icon set and the entire value is returned for
717 * the icon name. If the argument is falsy, returns an array of two empty strings.
718 * Throws an error if the name contains two or more ':' separators.
719 * Examples:
720 * `'social:cake' -> ['social', 'cake']
721 * 'penguin' -> ['', 'penguin']
722 * null -> ['', '']
723 * 'a:b:c' -> (throws Error)`
724 */
725 _splitIconName(iconName) {
726 if (!iconName) {
727 return ['', ''];
728 }
729 const parts = iconName.split(':');
730 switch (parts.length) {
731 case 1: return ['', parts[0]]; // Use default namespace.
732 case 2: return parts;
733 default: throw Error(`Invalid icon name: "${iconName}"`); // TODO: add an ngDevMode check
734 }
735 }
736 ngOnInit() {
737 // Update font classes because ngOnChanges won't be called if none of the inputs are present,
738 // e.g. <mat-icon>arrow</mat-icon> In this case we need to add a CSS class for the default font.
739 this._updateFontIconClasses();
740 }
741 ngAfterViewChecked() {
742 const cachedElements = this._elementsWithExternalReferences;
743 if (cachedElements && cachedElements.size) {
744 const newPath = this._location.getPathname();
745 // We need to check whether the URL has changed on each change detection since
746 // the browser doesn't have an API that will let us react on link clicks and
747 // we can't depend on the Angular router. The references need to be updated,
748 // because while most browsers don't care whether the URL is correct after
749 // the first render, Safari will break if the user navigates to a different
750 // page and the SVG isn't re-rendered.
751 if (newPath !== this._previousPath) {
752 this._previousPath = newPath;
753 this._prependPathToReferences(newPath);
754 }
755 }
756 }
757 ngOnDestroy() {
758 this._currentIconFetch.unsubscribe();
759 if (this._elementsWithExternalReferences) {
760 this._elementsWithExternalReferences.clear();
761 }
762 }
763 _usingFontIcon() {
764 return !this.svgIcon;
765 }
766 _setSvgElement(svg) {
767 this._clearSvgElement();
768 // Workaround for IE11 and Edge ignoring `style` tags inside dynamically-created SVGs.
769 // See: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/10898469/
770 // Do this before inserting the element into the DOM, in order to avoid a style recalculation.
771 const styleTags = svg.querySelectorAll('style');
772 for (let i = 0; i < styleTags.length; i++) {
773 styleTags[i].textContent += ' ';
774 }
775 // Note: we do this fix here, rather than the icon registry, because the
776 // references have to point to the URL at the time that the icon was created.
777 const path = this._location.getPathname();
778 this._previousPath = path;
779 this._cacheChildrenWithExternalReferences(svg);
780 this._prependPathToReferences(path);
781 this._elementRef.nativeElement.appendChild(svg);
782 }
783 _clearSvgElement() {
784 const layoutElement = this._elementRef.nativeElement;
785 let childCount = layoutElement.childNodes.length;
786 if (this._elementsWithExternalReferences) {
787 this._elementsWithExternalReferences.clear();
788 }
789 // Remove existing non-element child nodes and SVGs, and add the new SVG element. Note that
790 // we can't use innerHTML, because IE will throw if the element has a data binding.
791 while (childCount--) {
792 const child = layoutElement.childNodes[childCount];
793 // 1 corresponds to Node.ELEMENT_NODE. We remove all non-element nodes in order to get rid
794 // of any loose text nodes, as well as any SVG elements in order to remove any old icons.
795 if (child.nodeType !== 1 || child.nodeName.toLowerCase() === 'svg') {
796 layoutElement.removeChild(child);
797 }
798 }
799 }
800 _updateFontIconClasses() {
801 if (!this._usingFontIcon()) {
802 return;
803 }
804 const elem = this._elementRef.nativeElement;
805 const fontSetClass = this.fontSet ?
806 this._iconRegistry.classNameForFontAlias(this.fontSet) :
807 this._iconRegistry.getDefaultFontSetClass();
808 if (fontSetClass != this._previousFontSetClass) {
809 if (this._previousFontSetClass) {
810 elem.classList.remove(this._previousFontSetClass);
811 }
812 if (fontSetClass) {
813 elem.classList.add(fontSetClass);
814 }
815 this._previousFontSetClass = fontSetClass;
816 }
817 if (this.fontIcon != this._previousFontIconClass) {
818 if (this._previousFontIconClass) {
819 elem.classList.remove(this._previousFontIconClass);
820 }
821 if (this.fontIcon) {
822 elem.classList.add(this.fontIcon);
823 }
824 this._previousFontIconClass = this.fontIcon;
825 }
826 }
827 /**
828 * Cleans up a value to be used as a fontIcon or fontSet.
829 * Since the value ends up being assigned as a CSS class, we
830 * have to trim the value and omit space-separated values.
831 */
832 _cleanupFontValue(value) {
833 return typeof value === 'string' ? value.trim().split(' ')[0] : value;
834 }
835 /**
836 * Prepends the current path to all elements that have an attribute pointing to a `FuncIRI`
837 * reference. This is required because WebKit browsers require references to be prefixed with
838 * the current path, if the page has a `base` tag.
839 */
840 _prependPathToReferences(path) {
841 const elements = this._elementsWithExternalReferences;
842 if (elements) {
843 elements.forEach((attrs, element) => {
844 attrs.forEach(attr => {
845 element.setAttribute(attr.name, `url('${path}#${attr.value}')`);
846 });
847 });
848 }
849 }
850 /**
851 * Caches the children of an SVG element that have `url()`
852 * references that we need to prefix with the current path.
853 */
854 _cacheChildrenWithExternalReferences(element) {
855 const elementsWithFuncIri = element.querySelectorAll(funcIriAttributeSelector);
856 const elements = this._elementsWithExternalReferences =
857 this._elementsWithExternalReferences || new Map();
858 for (let i = 0; i < elementsWithFuncIri.length; i++) {
859 funcIriAttributes.forEach(attr => {
860 const elementWithReference = elementsWithFuncIri[i];
861 const value = elementWithReference.getAttribute(attr);
862 const match = value ? value.match(funcIriPattern) : null;
863 if (match) {
864 let attributes = elements.get(elementWithReference);
865 if (!attributes) {
866 attributes = [];
867 elements.set(elementWithReference, attributes);
868 }
869 attributes.push({ name: attr, value: match[1] });
870 }
871 });
872 }
873 }
874 /** Sets a new SVG icon with a particular name. */
875 _updateSvgIcon(rawName) {
876 this._svgNamespace = null;
877 this._svgName = null;
878 this._currentIconFetch.unsubscribe();
879 if (rawName) {
880 const [namespace, iconName] = this._splitIconName(rawName);
881 if (namespace) {
882 this._svgNamespace = namespace;
883 }
884 if (iconName) {
885 this._svgName = iconName;
886 }
887 this._currentIconFetch = this._iconRegistry.getNamedSvgIcon(iconName, namespace)
888 .pipe(take(1))
889 .subscribe(svg => this._setSvgElement(svg), (err) => {
890 const errorMessage = `Error retrieving icon ${namespace}:${iconName}! ${err.message}`;
891 this._errorHandler.handleError(new Error(errorMessage));
892 });
893 }
894 }
895}
896MatIcon.decorators = [
897 { type: Component, args: [{
898 template: '<ng-content></ng-content>',
899 selector: 'mat-icon',
900 exportAs: 'matIcon',
901 inputs: ['color'],
902 host: {
903 'role': 'img',
904 'class': 'mat-icon notranslate',
905 '[attr.data-mat-icon-type]': '_usingFontIcon() ? "font" : "svg"',
906 '[attr.data-mat-icon-name]': '_svgName || fontIcon',
907 '[attr.data-mat-icon-namespace]': '_svgNamespace || fontSet',
908 '[class.mat-icon-inline]': 'inline',
909 '[class.mat-icon-no-color]': 'color !== "primary" && color !== "accent" && color !== "warn"',
910 },
911 encapsulation: ViewEncapsulation.None,
912 changeDetection: ChangeDetectionStrategy.OnPush,
913 styles: [".mat-icon{background-repeat:no-repeat;display:inline-block;fill:currentColor;height:24px;width:24px}.mat-icon.mat-icon-inline{font-size:inherit;height:inherit;line-height:inherit;width:inherit}[dir=rtl] .mat-icon-rtl-mirror{transform:scale(-1, 1)}.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-prefix .mat-icon,.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-suffix .mat-icon{display:block}.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-prefix .mat-icon-button .mat-icon,.mat-form-field:not(.mat-form-field-appearance-legacy) .mat-form-field-suffix .mat-icon-button .mat-icon{margin:auto}\n"]
914 },] }
915];
916MatIcon.ctorParameters = () => [
917 { type: ElementRef },
918 { type: MatIconRegistry },
919 { type: String, decorators: [{ type: Attribute, args: ['aria-hidden',] }] },
920 { type: undefined, decorators: [{ type: Inject, args: [MAT_ICON_LOCATION,] }] },
921 { type: ErrorHandler }
922];
923MatIcon.propDecorators = {
924 inline: [{ type: Input }],
925 svgIcon: [{ type: Input }],
926 fontSet: [{ type: Input }],
927 fontIcon: [{ type: Input }]
928};
929
930/**
931 * @license
932 * Copyright Google LLC All Rights Reserved.
933 *
934 * Use of this source code is governed by an MIT-style license that can be
935 * found in the LICENSE file at https://angular.io/license
936 */
937class MatIconModule {
938}
939MatIconModule.decorators = [
940 { type: NgModule, args: [{
941 imports: [MatCommonModule],
942 exports: [MatIcon, MatCommonModule],
943 declarations: [MatIcon],
944 },] }
945];
946
947/**
948 * @license
949 * Copyright Google LLC All Rights Reserved.
950 *
951 * Use of this source code is governed by an MIT-style license that can be
952 * found in the LICENSE file at https://angular.io/license
953 */
954
955/**
956 * Generated bundle index. Do not edit.
957 */
958
959export { ICON_REGISTRY_PROVIDER, ICON_REGISTRY_PROVIDER_FACTORY, MAT_ICON_LOCATION, MAT_ICON_LOCATION_FACTORY, MatIcon, MatIconModule, MatIconRegistry, getMatIconFailedToSanitizeLiteralError, getMatIconFailedToSanitizeUrlError, getMatIconNameNotFoundError, getMatIconNoHttpProviderError, ɵ0 };
960//# sourceMappingURL=icon.js.map
Note: See TracBrowser for help on using the repository browser.