source: trip-planner-front/node_modules/angular-material/modules/js/icon/icon.js@ 6a3a178

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

initial commit

  • Property mode set to 100644
File size: 39.1 KB
Line 
1/*!
2 * AngularJS Material Design
3 * https://github.com/angular/material
4 * @license MIT
5 * v1.2.3
6 */
7(function( window, angular, undefined ){
8"use strict";
9
10/**
11 * @ngdoc module
12 * @name material.components.icon
13 * @description
14 * Icon
15 */
16angular.module('material.components.icon', ['material.core']);
17
18angular
19 .module('material.components.icon')
20 .directive('mdIcon', ['$mdIcon', '$mdTheming', '$mdAria', '$sce', mdIconDirective]);
21
22/**
23 * @ngdoc directive
24 * @name mdIcon
25 * @module material.components.icon
26 *
27 * @restrict E
28 *
29 * @description
30 * The `md-icon` directive makes it easier to use vector-based icons in your app (as opposed to
31 * raster-based icons types like PNG). The directive supports both icon fonts and SVG icons.
32 *
33 * Icons should be considered view-only elements that should not be used directly as buttons; instead nest a `<md-icon>`
34 * inside a `md-button` to add hover and click features.
35 *
36 * ### Icon fonts
37 * Icon fonts are a technique in which you use a font where the glyphs in the font are
38 * your icons instead of text. Benefits include a straightforward way to bundle everything into a
39 * single HTTP request, simple scaling, easy color changing, and more.
40 *
41 * `md-icon` lets you consume an icon font by letting you reference specific icons in that font
42 * by name rather than character code.
43 *
44 * When using font-icons, developers must follow three (3) simple steps:
45 *
46 * <ol>
47 * <li>Load the font library. e.g.<br/>
48 * `<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">`
49 * </li>
50 * <li>
51 * Use either (a) font-icon class names or (b) a fontset and a font ligature to render the font glyph by
52 * using its textual name _or_ numerical character reference. Note that `material-icons` is the default fontset when
53 * none is specified.
54 * </li>
55 * <li> Use any of the following templates: <br/>
56 * <ul>
57 * <li>`<md-icon md-font-icon="classname"></md-icon>`</li>
58 * <li>`<md-icon md-font-set="font library classname or alias">textual_name</md-icon>`</li>
59 * <li>`<md-icon> numerical_character_reference </md-icon>`</li>
60 * <li>`<md-icon ng_bind="'textual_name'"></md-icon>`</li>
61 * <li>`<md-icon ng-bind="scopeVariable"></md-icon>`</li>
62 * </ul>
63 * </li>
64 * </ol>
65 *
66 * Full details for these steps can be found in the
67 * <a href="http://google.github.io/material-design-icons/#icon-font-for-the-web" target="_blank">
68 * Material Design Icon font for the web docs</a>.
69 *
70 * You can browse and search the Material Design icon style <code>.material-icons</code>
71 * in the <a href="https://material.io/tools/icons/" target="_blank">Material Design Icons tool</a>.
72 *
73 * ### SVG
74 * For SVGs, the problem with using `<img>` or a CSS `background-image` is that you can't take
75 * advantage of some SVG features, such as styling specific parts of the icon with CSS or SVG
76 * animation.
77 *
78 * `md-icon` makes it easier to use SVG icons by *inlining* the SVG into an `<svg>` element in the
79 * document. The most straightforward way of referencing an SVG icon is via URL, just like a
80 * traditional `<img>`. `$mdIconProvider`, as a convenience, lets you _name_ an icon so you can
81 * reference it by name instead of URL throughout your templates.
82 *
83 * Additionally, you may not want to make separate HTTP requests for every icon, so you can bundle
84 * your SVG icons together and pre-load them with `$mdIconProvider` as an icon set. An icon set can
85 * also be given a name, which acts as a namespace for individual icons, so you can reference them
86 * like `"social:cake"`.
87 *
88 * When using SVGs, both external SVGs (via URLs) or sets of SVGs (from icon sets) can be
89 * easily loaded and used.
90 *
91 * ### Localization
92 *
93 * Because an `md-icon` element's text content is not intended to be translated, it is recommended
94 * to declare the text content for an `md-icon` element in its start tag. Instead of using the HTML
95 * text content, consider using `ng-bind` with a scope variable or literal string.
96 *
97 * Examples:
98 *
99 * <ul>
100 * <li>`<md-icon ng-bind="myIconVariable"></md-icon>`</li>
101 * <li>`<md-icon ng-bind="'menu'"></md-icon>`
102 * </ul>
103 *
104 * <h2 id="material_design_icons">Material Design Icons tool</h2>
105 * Using the Material Design Icons tool, developers can easily and quickly search for a specific
106 * open source Material Design icon. The search is in the top left. Below search, you can select
107 * from the new icon themes or filter by icon category.
108 *
109 * <a href="https://material.io/tools/icons/" target="_blank" style="border-bottom:none;">
110 * <img src="https://user-images.githubusercontent.com/3506071/41942584-ef0695d0-796d-11e8-9436-44f25023a111.png"
111 * aria-label="Material Design Icons tool" style="max-width:95%">
112 * </a>
113 *
114 * <div class="md-caption" style="text-align: center; margin-bottom: 24px">
115 * Click on the image above to open the
116 * <a href="https://material.io/tools/icons/" target="_blank">Material Design Icons tool</a>.
117 * </div>
118 *
119 * Click on any icon, then click on the "Selected Icon" chevron to see the slide-up
120 * information panel with details regarding a SVG download and information on the font-icon's
121 * textual name. This panel also allows you to select a black on transparent or white on transparent
122 * icon and to change the icon size. These settings only affect the downloaded icons.
123 *
124 * @param {string} md-font-icon String name of CSS icon associated with the font-face will be used
125 * to render the icon. Requires the fonts and the named CSS styles to be preloaded.
126 * @param {string} md-font-set CSS style name associated with the font library; which will be assigned as
127 * the class for the font-icon ligature. This value may also be an alias that is used to lookup the classname;
128 * internally use `$mdIconProvider.fontSet(<alias>)` to determine the style name.
129 * @param {string} md-svg-src String URL (or expression) used to load, cache, and display an
130 * external SVG.
131 * @param {string} md-svg-icon md-svg-icon String name used for lookup of the icon from the internal cache;
132 * interpolated strings or expressions may also be used. Specific set names can be used with
133 * the syntax `<set name>:<icon name>`.<br/><br/>
134 * To use icon sets, developers are required to pre-register the sets using the `$mdIconProvider` service.
135 * @param {string=} aria-label Labels the icon for accessibility. If an empty string is provided,
136 * the icon will be hidden from the accessibility layer with `aria-hidden="true"`. If there is no
137 * `aria-label` attribute on the icon, we check the following, in order: the `alt` attribute, the
138 * `aria-label` from the parent element, the icon's `md-font-icon` or `md-svg-icon` string, and the
139 * text content inside `<md-icon></md-icon>`. If none of these have any text, the icon is hidden
140 * from the accessibility layer with `aria-hidden="true"`.
141 * @param {string=} alt Labels the icon for accessibility. If an empty string is provided and the
142 * icon has no `aria-label`, then the icon will be hidden from accessibility layer with
143 * `aria-hidden="true"`.
144 *
145 * @usage
146 * When using SVGs:
147 * <hljs lang="html">
148 *
149 *<!-- Icon ID; may contain optional icon set prefix.
150 * Icons must be registered using $mdIconProvider. -->
151 *<md-icon md-svg-icon="social:android" aria-label="android " ></md-icon>
152 *
153 *<!-- Icon urls; may be preloaded in templateCache -->
154 *<md-icon md-svg-src="/android.svg" aria-label="android " ></md-icon>
155 *<md-icon md-svg-src="{{ getAndroid() }}" aria-label="android " ></md-icon>
156 *
157 * </hljs>
158 *
159 * Use the <code>$mdIconProvider</code> to configure your application with
160 * SVG icon sets.
161 *
162 * <hljs lang="js">
163 * angular.module('appSvgIconSets', ['ngMaterial'])
164 * .controller('DemoCtrl', function($scope) {})
165 * .config(function($mdIconProvider) {
166 * $mdIconProvider
167 * .iconSet('social', 'img/icons/sets/social-icons.svg', 24)
168 * .defaultIconSet('img/icons/sets/core-icons.svg', 24);
169 * });
170 * </hljs>
171 *
172 *
173 * When using Font Icons with classnames:
174 * <hljs lang="html">
175 *
176 * <md-icon md-font-icon="android" aria-label="android" ></md-icon>
177 * <md-icon class="icon_home" aria-label="Home"></md-icon>
178 *
179 * </hljs>
180 *
181 * When using Material Font Icons with ligatures:
182 * <hljs lang="html">
183 * <!--
184 * For Material Design Icons
185 * The class '.material-icons' is auto-added if a style has NOT been specified
186 * since `material-icons` is the default fontset. So your markup:
187 * -->
188 * <md-icon> face </md-icon>
189 * <!-- becomes this at runtime: -->
190 * <md-icon md-font-set="material-icons"> face </md-icon>
191 * <!-- If the fontset does not support ligature names, then we need to use the ligature unicode.-->
192 * <md-icon> &#xE87C; </md-icon>
193 * <!-- The class '.material-icons' must be manually added if other styles are also specified-->
194 * <md-icon class="material-icons md-light md-48"> face </md-icon>
195 * </hljs>
196 *
197 * When using other Font-Icon libraries:
198 *
199 * <hljs lang="js">
200 * // Specify a font-icon style alias
201 * angular.config(function($mdIconProvider) {
202 * $mdIconProvider.fontSet('md', 'material-icons');
203 * });
204 * </hljs>
205 *
206 * <hljs lang="html">
207 * <md-icon md-font-set="md">favorite</md-icon>
208 * </hljs>
209 *
210 */
211function mdIconDirective($mdIcon, $mdTheming, $mdAria, $sce) {
212
213 return {
214 restrict: 'E',
215 link : postLink
216 };
217
218
219 /**
220 * Directive postLink
221 * Supports embedded SVGs, font-icons, & external SVGs.
222 * @param {IScope} scope
223 * @param {JQLite} element
224 * @param {IAttributes} attr
225 */
226 function postLink(scope, element, attr) {
227 $mdTheming(element);
228 var lastFontIcon = attr.mdFontIcon;
229 var lastFontSet = $mdIcon.fontSet(attr.mdFontSet);
230
231 prepareForFontIcon();
232
233 attr.$observe('mdFontIcon', fontIconChanged);
234 attr.$observe('mdFontSet', fontIconChanged);
235
236 /* Provide a default accessibility role of img */
237 if (!attr.role) {
238 $mdAria.expect(element, 'role', 'img');
239 /* manually update attr variable */
240 attr.role = 'img';
241 }
242
243 // If the aria-label is explicitly set to the empty string, then hide this element from the
244 // accessibility layer.
245 if (element[0].hasAttribute('aria-label') && attr.ariaLabel === '') {
246 element.attr('aria-hidden', true);
247 }
248
249 /* Don't process ARIA if already valid */
250 if (attr.role === "img" && !attr.ariaHidden && !$mdAria.hasAriaLabel(element)) {
251 // If the developer signals to hide this icon from the accessibility layer, do so.
252 if (element[0].hasAttribute('alt') && attr.alt === '') {
253 element.attr('aria-hidden', true);
254 } else if (attr.alt) {
255 /* Use the alt text for the aria-label by default, if available. */
256 $mdAria.expect(element, 'aria-label', attr.alt);
257 } else if ($mdAria.parentHasAriaLabel(element, 2)) {
258 /* Parent has ARIA so we will assume it will describe the icon. */
259 $mdAria.expect(element, 'aria-hidden', 'true');
260 } else if (attr.mdFontIcon || attr.mdSvgIcon || element.text()) {
261 /* Use icon name or node's text content as the aria-label */
262 $mdAria.expect(element, 'aria-label', attr.mdFontIcon || attr.mdSvgIcon || element.text());
263 } else {
264 /* No label found, hide this icon from the accessibility layer */
265 $mdAria.expect(element, 'aria-hidden', 'true');
266 }
267 }
268
269 var attrName = attr.$normalize(attr.$attr.mdSvgIcon || attr.$attr.mdSvgSrc || '');
270 if (attrName) {
271 // Use either pre-configured SVG or URL source, respectively.
272 attr.$observe(attrName, function(attrVal) {
273 element.empty();
274 if (attrVal) {
275 $mdIcon(attrVal)
276 .then(function(svg) {
277 element.empty();
278 element.append(svg);
279 });
280 }
281 });
282 }
283
284 function prepareForFontIcon() {
285 if (!attr.mdSvgIcon && !attr.mdSvgSrc) {
286 if (attr.mdFontIcon) {
287 element.addClass('md-font ' + attr.mdFontIcon);
288 }
289
290 element.addClass(lastFontSet);
291 }
292 }
293
294 function fontIconChanged() {
295 if (!attr.mdSvgIcon && !attr.mdSvgSrc) {
296 if (attr.mdFontIcon) {
297 element.removeClass(lastFontIcon);
298 element.addClass(attr.mdFontIcon);
299
300 lastFontIcon = attr.mdFontIcon;
301 }
302
303 var fontSet = $mdIcon.fontSet(attr.mdFontSet);
304
305 if (lastFontSet !== fontSet) {
306 element.removeClass(lastFontSet);
307 element.addClass(fontSet);
308
309 lastFontSet = fontSet;
310 }
311 }
312 }
313 }
314}
315
316
317MdIconService['$inject'] = ["config", "$templateRequest", "$q", "$log", "$mdUtil", "$sce"];angular
318 .module('material.components.icon')
319 .constant('$$mdSvgRegistry', {
320 'mdTabsArrow': 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxnPjxwb2x5Z29uIHBvaW50cz0iMTUuNCw3LjQgMTQsNiA4LDEyIDE0LDE4IDE1LjQsMTYuNiAxMC44LDEyICIvPjwvZz48L3N2Zz4=',
321 'mdClose': 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxnPjxwYXRoIGQ9Ik0xOSA2LjQxbC0xLjQxLTEuNDEtNS41OSA1LjU5LTUuNTktNS41OS0xLjQxIDEuNDEgNS41OSA1LjU5LTUuNTkgNS41OSAxLjQxIDEuNDEgNS41OS01LjU5IDUuNTkgNS41OSAxLjQxLTEuNDEtNS41OS01LjU5eiIvPjwvZz48L3N2Zz4=',
322 'mdCancel': 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxnPjxwYXRoIGQ9Ik0xMiAyYy01LjUzIDAtMTAgNC40Ny0xMCAxMHM0LjQ3IDEwIDEwIDEwIDEwLTQuNDcgMTAtMTAtNC40Ny0xMC0xMC0xMHptNSAxMy41OWwtMS40MSAxLjQxLTMuNTktMy41OS0zLjU5IDMuNTktMS40MS0xLjQxIDMuNTktMy41OS0zLjU5LTMuNTkgMS40MS0xLjQxIDMuNTkgMy41OSAzLjU5LTMuNTkgMS40MSAxLjQxLTMuNTkgMy41OSAzLjU5IDMuNTl6Ii8+PC9nPjwvc3ZnPg==',
323 'mdMenu': 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik0zLDZIMjFWOEgzVjZNMywxMUgyMVYxM0gzVjExTTMsMTZIMjFWMThIM1YxNloiIC8+PC9zdmc+',
324 'mdToggleArrow': 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgNDggNDgiPjxwYXRoIGQ9Ik0yNCAxNmwtMTIgMTIgMi44MyAyLjgzIDkuMTctOS4xNyA5LjE3IDkuMTcgMi44My0yLjgzeiIvPjxwYXRoIGQ9Ik0wIDBoNDh2NDhoLTQ4eiIgZmlsbD0ibm9uZSIvPjwvc3ZnPg==',
325 'mdCalendar': 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTkgM2gtMVYxaC0ydjJIOFYxSDZ2Mkg1Yy0xLjExIDAtMS45OS45LTEuOTkgMkwzIDE5YzAgMS4xLjg5IDIgMiAyaDE0YzEuMSAwIDItLjkgMi0yVjVjMC0xLjEtLjktMi0yLTJ6bTAgMTZINVY4aDE0djExek03IDEwaDV2NUg3eiIvPjwvc3ZnPg==',
326 'mdChecked': 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxnPjxwYXRoIGQ9Ik05IDE2LjE3TDQuODMgMTJsLTEuNDIgMS40MUw5IDE5IDIxIDdsLTEuNDEtMS40MXoiLz48L2c+PC9zdmc+'
327 })
328 .provider('$mdIcon', MdIconProvider);
329
330/**
331 * @ngdoc service
332 * @name $mdIconProvider
333 * @module material.components.icon
334 *
335 * @description
336 * `$mdIconProvider` is used only to register icon IDs with URLs. These configuration features allow
337 * icons and icon sets to be pre-registered and associated with source URLs **before** the
338 * `<md-icon />` directives are compiled.
339 *
340 * If using font-icons, the developer is responsible for loading the fonts.
341 *
342 * If using SVGs, loading of the actual svg files are deferred to on-demand requests and are loaded
343 * internally by the `$mdIcon` service using the `$templateRequest` service. When an SVG is
344 * requested by name/ID, the `$mdIcon` service searches its registry for the associated source URL;
345 * that URL is used to on-demand load and parse the SVG dynamically.
346 *
347 * The `$templateRequest` service expects the icons source to be loaded over trusted URLs.<br/>
348 * This means, when loading icons from an external URL, you have to trust the URL in the
349 * `$sceDelegateProvider`.
350 *
351 * <hljs lang="js">
352 * app.config(function($sceDelegateProvider) {
353 * $sceDelegateProvider.trustedResourceUrlList([
354 * // Adding 'self' to the allow-list, will allow requests from the current origin.
355 * 'self',
356 * // Using double asterisks here, will allow all URLs to load.
357 * // However, we recommend only specifying the given domain you want to allow.
358 * '**'
359 * ]);
360 * });
361 * </hljs>
362 *
363 * Read more about the [$sceDelegateProvider](https://docs.angularjs.org/api/ng/provider/$sceDelegateProvider).
364 *
365 * **Notice:** Most font-icon libraries do not support ligatures (for example `fontawesome`).<br/>
366 * In such cases you are not able to use the icon's ligature name - Like so:
367 *
368 * <hljs lang="html">
369 * <md-icon md-font-set="fa">fa-bell</md-icon>
370 * </hljs>
371 *
372 * You should instead use the given unicode, instead of the ligature name.
373 *
374 * <p ng-hide="true"> ##// Notice we can't use a hljs element here, because the characters will be escaped.</p>
375 * ```html
376 * <md-icon md-font-set="fa">&#xf0f3</md-icon>
377 * ```
378 *
379 * All unicode ligatures are prefixed with the `&#x` string.
380 *
381 * @usage
382 * <hljs lang="js">
383 * app.config(function($mdIconProvider) {
384 *
385 * // Configure URLs for icons specified by [set:]id.
386 * $mdIconProvider
387 * .defaultFontSet( 'fa' ) // This sets our default fontset className.
388 * .defaultIconSet('my/app/icons.svg') // Register a default set of SVG icons
389 * .iconSet('social', 'my/app/social.svg') // Register a named icon set of SVGs
390 * .icon('android', 'my/app/android.svg') // Register a specific icon (by name)
391 * .icon('work:chair', 'my/app/chair.svg'); // Register icon in a specific set
392 * });
393 * </hljs>
394 *
395 * SVG icons and icon sets can be easily pre-loaded and cached using either (a) a build process or
396 * (b) a runtime **startup** process (shown below):
397 *
398 * <hljs lang="js">
399 * app.config(function($mdIconProvider) {
400 *
401 * // Register a default set of SVG icon definitions
402 * $mdIconProvider.defaultIconSet('my/app/icons.svg')
403 * })
404 * .run(function($templateRequest) {
405 *
406 * // Pre-fetch icons sources by URL and cache in the $templateCache...
407 * // subsequent $templateRequest calls will look there first.
408 * var urls = [ 'imy/app/icons.svg', 'img/icons/android.svg'];
409 *
410 * angular.forEach(urls, function(url) {
411 * $templateRequest(url);
412 * });
413 * });
414 *
415 * </hljs>
416 *
417 * > <b>Note:</b> The loaded SVG data is subsequently cached internally for future requests.
418 */
419
420/**
421 * @ngdoc method
422 * @name $mdIconProvider#icon
423 *
424 * @description
425 * Register a source URL for a specific icon name; the name may include optional 'icon set' name
426 * prefix. These icons will later be retrieved from the cache using `$mdIcon(<icon name>)`.
427 *
428 * @param {string} id Icon name/id used to register the icon
429 * @param {string} url specifies the external location for the data file. Used internally by
430 * `$templateRequest` to load the data or as part of the lookup in `$templateCache` if pre-loading
431 * was configured.
432 * @param {number=} viewBoxSize Sets the width and height the icon's viewBox.
433 * It is ignored for icons with an existing viewBox. Default size is 24.
434 *
435 * @returns {obj} an `$mdIconProvider` reference; used to support method call chains for the API
436 *
437 * @usage
438 * <hljs lang="js">
439 * app.config(function($mdIconProvider) {
440 *
441 * // Configure URLs for icons specified by [set:]id.
442 * $mdIconProvider
443 * .icon('android', 'my/app/android.svg') // Register a specific icon (by name)
444 * .icon('work:chair', 'my/app/chair.svg'); // Register icon in a specific set
445 * });
446 * </hljs>
447 */
448
449/**
450 * @ngdoc method
451 * @name $mdIconProvider#iconSet
452 *
453 * @description
454 * Register a source URL for a 'named' set of icons; group of SVG definitions where each definition
455 * has an icon id. Individual icons can be subsequently retrieved from this cached set using
456 * `$mdIcon(<icon set name>:<icon name>)`
457 *
458 * @param {string} id Icon name/id used to register the iconset
459 * @param {string} url specifies the external location for the data file. Used internally by
460 * `$templateRequest` to load the data or as part of the lookup in `$templateCache` if pre-loading
461 * was configured.
462 * @param {number=} viewBoxSize Sets the width and height of the viewBox of all icons in the set.
463 * It is ignored for icons with an existing viewBox. All icons in the icon set should be the same size.
464 * Default value is 24.
465 *
466 * @returns {obj} an `$mdIconProvider` reference; used to support method call chains for the API
467 *
468 * @usage
469 * <hljs lang="js">
470 * app.config(function($mdIconProvider) {
471 *
472 * // Configure URLs for icons specified by [set:]id.
473 * $mdIconProvider
474 * .iconSet('social', 'my/app/social.svg'); // Register a named icon set
475 * });
476 * </hljs>
477 */
478
479/**
480 * @ngdoc method
481 * @name $mdIconProvider#defaultIconSet
482 *
483 * @description
484 * Register a source URL for the default 'named' set of icons. Unless explicitly registered,
485 * subsequent lookups of icons will fail over to search this 'default' icon set.
486 * Icon can be retrieved from this cached, default set using `$mdIcon(<name>)`
487 *
488 * @param {string} url specifies the external location for the data file. Used internally by
489 * `$templateRequest` to load the data or as part of the lookup in `$templateCache` if pre-loading
490 * was configured.
491 * @param {number=} viewBoxSize Sets the width and height of the viewBox of all icons in the set.
492 * It is ignored for icons with an existing viewBox. All icons in the icon set should be the same
493 * size. Default value is 24.
494 *
495 * @returns {Object} an `$mdIconProvider` reference; used to support method call chains for the API
496 *
497 * @usage
498 * <hljs lang="js">
499 * app.config(function($mdIconProvider) {
500 *
501 * // Configure URLs for icons specified by [set:]id.
502 * $mdIconProvider
503 * .defaultIconSet('my/app/social.svg'); // Register a default icon set
504 * });
505 * </hljs>
506 */
507
508/**
509 * @ngdoc method
510 * @name $mdIconProvider#defaultFontSet
511 *
512 * @description
513 * When using Font-Icons, AngularJS Material assumes the the Material Design icons will be used and
514 * automatically configures the default `font-set == 'material-icons'`. Note that the font-set
515 * references the font-icon library class style that should be applied to the `<md-icon>`.
516 *
517 * Configuring the default means that the attributes
518 * `md-font-set="material-icons"` or `class="material-icons"` do not need to be explicitly declared
519 * on the `<md-icon>` markup.
520 *
521 * For example:<br/>
522 * `<md-icon>face</md-icon>` will render as `<span class="material-icons">face</span>`,<br/>
523 * and<br/>
524 * `<md-icon md-font-set="fa">face</md-icon>` will render as `<span class="fa">face</span>`
525 *
526 * @param {string} name Name of the font-library style that should be applied to the md-icon DOM
527 * element.
528 *
529 * @usage
530 * <hljs lang="js">
531 * app.config(function($mdIconProvider) {
532 * $mdIconProvider.defaultFontSet('fa');
533 * });
534 * </hljs>
535 */
536
537/**
538 * @ngdoc method
539 * @name $mdIconProvider#fontSet
540 *
541 * @description
542 * When using a font-set for `<md-icon>` you must specify the correct font classname in the
543 * `md-font-set` attribute. If the font-set className is really long, your markup may become
544 * cluttered... an easy solution is to define an `alias` for your font-set:
545 *
546 * @param {string} alias Alias name of the specified font-set.
547 * @param {string} className Name of the class for the font-set.
548 *
549 * @usage
550 * <hljs lang="js">
551 * app.config(function($mdIconProvider) {
552 * // In this case, we set an alias for the `material-icons` font-set.
553 * $mdIconProvider.fontSet('md', 'material-icons');
554 * });
555 * </hljs>
556 */
557
558/**
559 * @ngdoc method
560 * @name $mdIconProvider#defaultViewBoxSize
561 *
562 * @description
563 * While `<md-icon>` markup can also be styled with sizing CSS, this method configures
564 * the default width **and** height used for all icons; unless overridden by specific CSS.
565 * The default sizing is (`24px`, `24px`).
566 * @param {number=} viewBoxSize Sets the width and height of the viewBox for an icon or an icon set.
567 * All icons in a set should be the same size. The default value is 24.
568 *
569 * @returns {Object} an `$mdIconProvider` reference; used to support method call chains for the API
570 *
571 * @usage
572 * <hljs lang="js">
573 * app.config(function($mdIconProvider) {
574 *
575 * // Configure URLs for icons specified by [set:]id.
576 * $mdIconProvider
577 * .defaultViewBoxSize(36); // Register a default icon size (width == height)
578 * });
579 * </hljs>
580 */
581
582var config = {
583 defaultViewBoxSize: 24,
584 defaultFontSet: 'material-icons',
585 fontSets: []
586};
587
588function MdIconProvider() {
589}
590
591MdIconProvider.prototype = {
592 icon: function(id, url, viewBoxSize) {
593 if (id.indexOf(':') == -1) id = '$default:' + id;
594
595 config[id] = new ConfigurationItem(url, viewBoxSize);
596 return this;
597 },
598
599 iconSet: function(id, url, viewBoxSize) {
600 config[id] = new ConfigurationItem(url, viewBoxSize);
601 return this;
602 },
603
604 defaultIconSet: function(url, viewBoxSize) {
605 var setName = '$default';
606
607 if (!config[setName]) {
608 config[setName] = new ConfigurationItem(url, viewBoxSize);
609 }
610
611 config[setName].viewBoxSize = viewBoxSize || config.defaultViewBoxSize;
612
613 return this;
614 },
615
616 defaultViewBoxSize: function(viewBoxSize) {
617 config.defaultViewBoxSize = viewBoxSize;
618 return this;
619 },
620
621 /**
622 * Register an alias name associated with a font-icon library style ;
623 */
624 fontSet: function fontSet(alias, className) {
625 config.fontSets.push({
626 alias: alias,
627 fontSet: className || alias
628 });
629 return this;
630 },
631
632 /**
633 * Specify a default style name associated with a font-icon library
634 * fallback to Material Icons.
635 *
636 */
637 defaultFontSet: function defaultFontSet(className) {
638 config.defaultFontSet = !className ? '' : className;
639 return this;
640 },
641
642 defaultIconSize: function defaultIconSize(iconSize) {
643 config.defaultIconSize = iconSize;
644 return this;
645 },
646
647 $get: ['$templateRequest', '$q', '$log', '$mdUtil', '$sce', function($templateRequest, $q, $log, $mdUtil, $sce) {
648 return MdIconService(config, $templateRequest, $q, $log, $mdUtil, $sce);
649 }]
650};
651
652 /**
653 * Configuration item stored in the Icon registry; used for lookups
654 * to load if not already cached in the `loaded` cache
655 * @param {string} url
656 * @param {=number} viewBoxSize
657 * @constructor
658 */
659 function ConfigurationItem(url, viewBoxSize) {
660 this.url = url;
661 this.viewBoxSize = viewBoxSize || config.defaultViewBoxSize;
662 }
663
664/**
665 * @ngdoc service
666 * @name $mdIcon
667 * @module material.components.icon
668 *
669 * @description
670 * The `$mdIcon` service is a function used to lookup SVG icons.
671 *
672 * @param {string} id Query value for a unique Id or URL. If the argument is a URL, then the service will retrieve the icon element
673 * from its internal cache or load the icon and cache it first. If the value is not a URL-type string, then an ID lookup is
674 * performed. The Id may be a unique icon ID or may include an iconSet ID prefix.
675 *
676 * For the **id** query to work properly, this means that all id-to-URL mappings must have been previously configured
677 * using the `$mdIconProvider`.
678 *
679 * @returns {angular.$q.Promise} A promise that gets resolved to a clone of the initial SVG DOM element; which was
680 * created from the SVG markup in the SVG data file. If an error occurs (e.g. the icon cannot be found) the promise
681 * will get rejected.
682 *
683 * @usage
684 * <hljs lang="js">
685 * function SomeDirective($mdIcon) {
686 *
687 * // See if the icon has already been loaded, if not then lookup the icon from the
688 * // registry cache, load and cache it for future requests.
689 * // NOTE: Non-URL queries require configuration with $mdIconProvider.
690 * $mdIcon('android').then(function(iconEl) { element.append(iconEl); });
691 * $mdIcon('work:chair').then(function(iconEl) { element.append(iconEl); });
692 *
693 * // Load and cache the external SVG using a URL.
694 * $mdIcon('img/icons/android.svg').then(function(iconEl) {
695 * element.append(iconEl);
696 * });
697 * };
698 * </hljs>
699 *
700 * > <b>Note:</b> The `<md-icon>` directive internally uses the `$mdIcon` service to query, load,
701 * and instantiate SVG DOM elements.
702 */
703
704/* ngInject */
705function MdIconService(config, $templateRequest, $q, $log, $mdUtil, $sce) {
706 var iconCache = {};
707 var svgCache = {};
708 var urlRegex = /[-\w@:%+.~#?&//=]{2,}\.[a-z]{2,4}\b(\/[-\w@:%+.~#?&//=]*)?/i;
709 var dataUrlRegex = /^data:image\/svg\+xml[\s*;\w\-=]*?(base64)?,(.*)$/i;
710
711 Icon.prototype = {clone: cloneSVG, prepare: prepareAndStyle};
712 getIcon.fontSet = findRegisteredFontSet;
713
714 // Publish service...
715 return getIcon;
716
717 /**
718 * Actual $mdIcon service is essentially a lookup function
719 * @param {*} id $sce trust wrapper over a URL string, URL, icon registry id, or icon set id
720 * @returns {angular.$q.Promise}
721 */
722 function getIcon(id) {
723 id = id || '';
724
725 // If the "id" provided is not a string, the only other valid value is a $sce trust wrapper
726 // over a URL string. If the value is not trusted, this will intentionally throw an error
727 // because the user is attempted to use an unsafe URL, potentially opening themselves up
728 // to an XSS attack.
729 if (!angular.isString(id)) {
730 id = $sce.getTrustedUrl(id);
731 }
732
733 // If already loaded and cached, use a clone of the cached icon.
734 // Otherwise either load by URL, or lookup in the registry and then load by URL, and cache.
735
736 if (iconCache[id]) {
737 return $q.when(transformClone(iconCache[id]));
738 }
739
740 if (urlRegex.test(id) || dataUrlRegex.test(id)) {
741 return loadByURL(id).then(cacheIcon(id));
742 }
743
744 if (id.indexOf(':') === -1) {
745 id = '$default:' + id;
746 }
747
748 var load = config[id] ? loadByID : loadFromIconSet;
749 return load(id)
750 .then(cacheIcon(id));
751 }
752
753 /**
754 * Lookup a registered fontSet style using its alias.
755 * @param {string} alias used to lookup the alias in the array of fontSets
756 * @returns {*} matching fontSet or the defaultFontSet if that alias does not match
757 */
758 function findRegisteredFontSet(alias) {
759 var useDefault = angular.isUndefined(alias) || !(alias && alias.length);
760 if (useDefault) {
761 return config.defaultFontSet;
762 }
763
764 var result = alias;
765 angular.forEach(config.fontSets, function(fontSet) {
766 if (fontSet.alias === alias) {
767 result = fontSet.fontSet || result;
768 }
769 });
770
771 return result;
772 }
773
774 /**
775 * @param {!Icon} cacheElement cached icon from the iconCache
776 * @returns {Icon} cloned Icon element with unique ids
777 */
778 function transformClone(cacheElement) {
779 var clone = cacheElement.clone();
780 var newUid = $mdUtil.nextUid();
781 var cacheSuffix, svgUrlQuerySelector, i, xlinkHrefValue;
782 // These are SVG attributes that can reference element ids.
783 var svgUrlAttributes = [
784 'clip-path', 'color-profile', 'cursor', 'fill', 'filter', 'href', 'marker-start',
785 'marker-mid', 'marker-end', 'mask', 'stroke', 'style', 'vector-effect'
786 ];
787 var isIeSvg = clone.innerHTML === undefined;
788
789 // Verify that the newUid only contains a number and not some XSS content.
790 if (!isFinite(Number(newUid))) {
791 throw new Error('Unsafe and unexpected non-number result from $mdUtil.nextUid().');
792 }
793 cacheSuffix = '_cache' + newUid;
794
795 // For each cached icon, we need to modify the id attributes and references.
796 // This is needed because SVG ids are treated as normal DOM ids and should not be duplicated on
797 // the page.
798 if (clone.id) {
799 clone.id += cacheSuffix;
800 }
801
802 // Do as much as possible with querySelectorAll as it provides much greater performance
803 // than RegEx against serialized DOM.
804 angular.forEach(clone.querySelectorAll('[id]'), function(descendantElem) {
805 svgUrlQuerySelector = '';
806 for (i = 0; i < svgUrlAttributes.length; i++) {
807 svgUrlQuerySelector += '[' + svgUrlAttributes[i] + '="url(#' + descendantElem.id + ')"]';
808 if (i + 1 < svgUrlAttributes.length) {
809 svgUrlQuerySelector += ', ';
810 }
811 }
812 // Append the cacheSuffix to references of the element's id within url(#id) calls.
813 angular.forEach(clone.querySelectorAll(svgUrlQuerySelector), function(refItem) {
814 updateSvgIdReferences(descendantElem, refItem, isIeSvg, newUid);
815 });
816 // Handle usages of url(#id) in the SVG's stylesheets
817 angular.forEach(clone.querySelectorAll('style'), function(refItem) {
818 updateSvgIdReferences(descendantElem, refItem, isIeSvg, newUid);
819 });
820
821 // Update ids referenced by the deprecated (in SVG v2) xlink:href XML attribute. The now
822 // preferred href attribute is handled above. However, this non-standard XML namespaced
823 // attribute cannot be handled in the same way. Explanation of this query selector here:
824 // https://stackoverflow.com/q/23034283/633107.
825 angular.forEach(clone.querySelectorAll('[*|href]:not([href])'), function(refItem) {
826 xlinkHrefValue = refItem.getAttribute('xlink:href');
827 if (xlinkHrefValue) {
828 xlinkHrefValue = xlinkHrefValue.replace("#" + descendantElem.id, "#" + descendantElem.id + cacheSuffix);
829 refItem.setAttribute('xlink:href', xlinkHrefValue);
830 }
831 });
832
833 descendantElem.id += cacheSuffix;
834 });
835
836 return clone;
837 }
838
839 /**
840 * @param {Element} referencedElement element w/ id that needs to be updated
841 * @param {Element} referencingElement element that references the original id
842 * @param {boolean} isIeSvg true if we're dealing with an SVG in IE11, false otherwise
843 * @param {string} newUid the cache id to add as part of the cache suffix
844 */
845 function updateSvgIdReferences(referencedElement, referencingElement, isIeSvg, newUid) {
846 var svgElement, cacheSuffix;
847
848 // Verify that the newUid only contains a number and not some XSS content.
849 if (!isFinite(Number(newUid))) {
850 throw new Error('Unsafe and unexpected non-number result for newUid.');
851 }
852 cacheSuffix = '_cache' + newUid;
853
854 // outerHTML of SVG elements is not supported by IE11
855 if (isIeSvg) {
856 svgElement = $mdUtil.getOuterHTML(referencingElement);
857 svgElement = svgElement.replace("url(#" + referencedElement.id + ")",
858 "url(#" + referencedElement.id + cacheSuffix + ")");
859 referencingElement.textContent = angular.element(svgElement)[0].innerHTML;
860 } else {
861 // This use of outerHTML should be safe from XSS attack since we are only injecting the
862 // cacheSuffix with content from $mdUtil.nextUid which we verify is a finite number above.
863 referencingElement.outerHTML = referencingElement.outerHTML.replace(
864 "url(#" + referencedElement.id + ")",
865 "url(#" + referencedElement.id + cacheSuffix + ")");
866 }
867 }
868
869 /**
870 * Prepare and cache the loaded icon for the specified `id`.
871 * @param {string} id icon cache id
872 * @returns {function(*=): *}
873 */
874 function cacheIcon(id) {
875
876 return function updateCache(icon) {
877 iconCache[id] = isIcon(icon) ? icon : new Icon(icon, config[id]);
878
879 return transformClone(iconCache[id]);
880 };
881 }
882
883 /**
884 * Lookup the configuration in the registry, if !registered throw an error
885 * otherwise load the icon [on-demand] using the registered URL.
886 * @param {string} id icon registry id
887 * @returns {angular.$q.Promise}
888 */
889 function loadByID(id) {
890 var iconConfig = config[id];
891 return loadByURL(iconConfig.url).then(function(icon) {
892 return new Icon(icon, iconConfig);
893 });
894 }
895
896 /**
897 * Loads the file as XML and uses querySelector( <id> ) to find the desired node...
898 * @param {string} id icon id in icon set
899 * @returns {angular.$q.Promise}
900 */
901 function loadFromIconSet(id) {
902 var setName = id.substring(0, id.lastIndexOf(':')) || '$default';
903 var iconSetConfig = config[setName];
904
905 return !iconSetConfig ? announceIdNotFound(id) : loadByURL(iconSetConfig.url).then(extractFromSet);
906
907 function extractFromSet(set) {
908 var iconName = id.slice(id.lastIndexOf(':') + 1);
909 var icon = set.querySelector('#' + iconName);
910 return icon ? new Icon(icon, iconSetConfig) : announceIdNotFound(id);
911 }
912
913 function announceIdNotFound(id) {
914 var msg = 'icon ' + id + ' not found';
915 $log.warn(msg);
916
917 return $q.reject(msg || id);
918 }
919 }
920
921 /**
922 * Load the icon by URL (may use the $templateCache).
923 * Extract the data for later conversion to Icon
924 * @param {string} url icon URL
925 * @returns {angular.$q.Promise}
926 */
927 function loadByURL(url) {
928 /* Load the icon from embedded data URL. */
929 function loadByDataUrl(url) {
930 var results = dataUrlRegex.exec(url);
931 var isBase64 = /base64/i.test(url);
932 var data = isBase64 ? window.atob(results[2]) : results[2];
933
934 return $q.when(angular.element(data)[0]);
935 }
936
937 /* Load the icon by URL using HTTP. */
938 function loadByHttpUrl(url) {
939 return $q(function(resolve, reject) {
940 // Catch HTTP or generic errors not related to incorrect icon IDs.
941 var announceAndReject = function(err) {
942 var msg = angular.isString(err) ? err : (err.message || err.data || err.statusText);
943 $log.warn(msg);
944 reject(err);
945 },
946 extractSvg = function(response) {
947 if (!svgCache[url]) {
948 svgCache[url] = angular.element('<div>').append(response)[0].querySelector('svg');
949 }
950 resolve(svgCache[url]);
951 };
952
953 $templateRequest(url, true).then(extractSvg, announceAndReject);
954 });
955 }
956
957 return dataUrlRegex.test(url)
958 ? loadByDataUrl(url)
959 : loadByHttpUrl(url);
960 }
961
962 /**
963 * Check target signature to see if it is an Icon instance.
964 * @param {Icon|Element} target
965 * @returns {boolean} true if the specified target is an Icon object, false otherwise.
966 */
967 function isIcon(target) {
968 return angular.isDefined(target.element) && angular.isDefined(target.config);
969 }
970
971 /**
972 * Define the Icon class
973 * @param {Element} el
974 * @param {=ConfigurationItem} config
975 * @constructor
976 */
977 function Icon(el, config) {
978 // If the node is a <symbol>, it won't be rendered so we have to convert it into <svg>.
979 if (el && el.tagName.toLowerCase() === 'symbol') {
980 var viewbox = el.getAttribute('viewBox');
981 // // Check if innerHTML is supported as IE11 does not support innerHTML on SVG elements.
982 if (el.innerHTML) {
983 el = angular.element('<svg xmlns="http://www.w3.org/2000/svg">')
984 .html(el.innerHTML)[0];
985 } else {
986 el = angular.element('<svg xmlns="http://www.w3.org/2000/svg">')
987 .append($mdUtil.getInnerHTML(el))[0];
988 }
989 if (viewbox) el.setAttribute('viewBox', viewbox);
990 }
991
992 if (el && el.tagName.toLowerCase() !== 'svg') {
993 el = angular.element(
994 '<svg xmlns="http://www.w3.org/2000/svg">').append(el.cloneNode(true))[0];
995 }
996
997 // Inject the namespace if not available...
998 if (!el.getAttribute('xmlns')) {
999 el.setAttribute('xmlns', "http://www.w3.org/2000/svg");
1000 }
1001
1002 this.element = el;
1003 this.config = config;
1004 this.prepare();
1005 }
1006
1007 /**
1008 * Prepare the DOM element that will be cached in the
1009 * loaded iconCache store.
1010 */
1011 function prepareAndStyle() {
1012 var viewBoxSize = this.config ? this.config.viewBoxSize : config.defaultViewBoxSize;
1013 angular.forEach({
1014 'fit': '',
1015 'height': '100%',
1016 'width': '100%',
1017 'preserveAspectRatio': 'xMidYMid meet',
1018 'viewBox': this.element.getAttribute('viewBox') || ('0 0 ' + viewBoxSize + ' ' + viewBoxSize),
1019 'focusable': false // Disable IE11s default behavior to make SVGs focusable
1020 }, function(val, attr) {
1021 this.element.setAttribute(attr, val);
1022 }, this);
1023 }
1024
1025 /**
1026 * Clone the Icon DOM element.
1027 */
1028 function cloneSVG() {
1029 // If the element or any of its children have a style attribute, then a CSP policy without
1030 // 'unsafe-inline' in the style-src directive, will result in a violation.
1031 return this.element.cloneNode(true);
1032 }
1033
1034}
1035
1036})(window, window.angular);
Note: See TracBrowser for help on using the repository browser.