commonroutesApp/www/lib/angular-material/modules/js/icon/icon.js
www/lib/angular-material/modules/js/icon/icon.jsRaw
1/*!
2 * Angular Material Design
3 * https://github.com/angular/material
4 * @license MIT
5 * v1.1.1
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 * ### SVG
45 * For SVGs, the problem with using `<img>` or a CSS `background-image` is that you can't take
46 * advantage of some SVG features, such as styling specific parts of the icon with CSS or SVG
47 * animation.
48 *
49 * `md-icon` makes it easier to use SVG icons by *inlining* the SVG into an `<svg>` element in the
50 * document. The most straightforward way of referencing an SVG icon is via URL, just like a
51 * traditional `<img>`. `$mdIconProvider`, as a convenience, lets you _name_ an icon so you can
52 * reference it by name instead of URL throughout your templates.
53 *
54 * Additionally, you may not want to make separate HTTP requests for every icon, so you can bundle
55 * your SVG icons together and pre-load them with $mdIconProvider as an icon set. An icon set can
56 * also be given a name, which acts as a namespace for individual icons, so you can reference them
57 * like `"social:cake"`.
58 *
59 * When using SVGs, both external SVGs (via URLs) or sets of SVGs [from icon sets] can be
60 * easily loaded and used.When use font-icons, developers must following three (3) simple steps:
61 *
62 * <ol>
63 * <li>Load the font library. e.g.<br/>
64 * `<link href="https://fonts.googleapis.com/icon?family=Material+Icons"
65 * rel="stylesheet">`
66 * </li>
67 * <li>
68 * Use either (a) font-icon class names or (b) font ligatures to render the font glyph by using
69 * its textual name
70 * </li>
71 * <li>
72 * Use `<md-icon md-font-icon="classname" />` or <br/>
73 * use `<md-icon md-font-set="font library classname or alias"> textual_name </md-icon>` or <br/>
74 * use `<md-icon md-font-set="font library classname or alias"> numerical_character_reference </md-icon>`
75 * </li>
76 * </ol>
77 *
78 * Full details for these steps can be found:
79 *
80 * <ul>
81 * <li>http://google.github.io/material-design-icons/</li>
82 * <li>http://google.github.io/material-design-icons/#icon-font-for-the-web</li>
83 * </ul>
84 *
85 * The Material Design icon style <code>.material-icons</code> and the icon font references are published in
86 * Material Design Icons:
87 *
88 * <ul>
89 * <li>https://design.google.com/icons/</li>
90 * <li>https://design.google.com/icons/#ic_accessibility</li>
91 * </ul>
92 *
93 * <h2 id="material_design_icons">Material Design Icons</h2>
94 * Using the Material Design Icon-Selector, developers can easily and quickly search for a Material Design font-icon and
95 * determine its textual name and character reference code. Click on any icon to see the slide-up information
96 * panel with details regarding a SVG download or information on the font-icon usage.
97 *
98 * <a href="https://www.google.com/design/icons/#ic_accessibility" target="_blank" style="border-bottom:none;">
99 * <img src="https://cloud.githubusercontent.com/assets/210413/7902490/fe8dd14c-0780-11e5-98fb-c821cc6475e6.png"
100 * aria-label="Material Design Icon-Selector" style="max-width:75%;padding-left:10%">
101 * </a>
102 *
103 * <span class="image_caption">
104 * Click on the image above to link to the
105 * <a href="https://design.google.com/icons/#ic_accessibility" target="_blank">Material Design Icon-Selector</a>.
106 * </span>
107 *
108 * @param {string} md-font-icon String name of CSS icon associated with the font-face will be used
109 * to render the icon. Requires the fonts and the named CSS styles to be preloaded.
110 * @param {string} md-font-set CSS style name associated with the font library; which will be assigned as
111 * the class for the font-icon ligature. This value may also be an alias that is used to lookup the classname;
112 * internally use `$mdIconProvider.fontSet(<alias>)` to determine the style name.
113 * @param {string} md-svg-src String URL (or expression) used to load, cache, and display an
114 * external SVG.
115 * @param {string} md-svg-icon md-svg-icon String name used for lookup of the icon from the internal cache;
116 * interpolated strings or expressions may also be used. Specific set names can be used with
117 * the syntax `<set name>:<icon name>`.<br/><br/>
118 * To use icon sets, developers are required to pre-register the sets using the `$mdIconProvider` service.
119 * @param {string=} aria-label Labels icon for accessibility. If an empty string is provided, icon
120 * will be hidden from accessibility layer with `aria-hidden="true"`. If there's no aria-label on the icon
121 * nor a label on the parent element, a warning will be logged to the console.
122 * @param {string=} alt Labels icon for accessibility. If an empty string is provided, icon
123 * will be hidden from accessibility layer with `aria-hidden="true"`. If there's no alt on the icon
124 * nor a label on the parent element, a warning will be logged to the console.
125 *
126 * @usage
127 * When using SVGs:
128 * <hljs lang="html">
129 *
130 * <!-- Icon ID; may contain optional icon set prefix; icons must registered using $mdIconProvider -->
131 * <md-icon md-svg-icon="social:android" aria-label="android " ></md-icon>
132 *
133 * <!-- Icon urls; may be preloaded in templateCache -->
134 * <md-icon md-svg-src="/android.svg" aria-label="android " ></md-icon>
135 * <md-icon md-svg-src="{{ getAndroid() }}" aria-label="android " ></md-icon>
136 *
137 * </hljs>
138 *
139 * Use the <code>$mdIconProvider</code> to configure your application with
140 * svg iconsets.
141 *
142 * <hljs lang="js">
143 * angular.module('appSvgIconSets', ['ngMaterial'])
144 * .controller('DemoCtrl', function($scope) {})
145 * .config(function($mdIconProvider) {
146 * $mdIconProvider
147 * .iconSet('social', 'img/icons/sets/social-icons.svg', 24)
148 * .defaultIconSet('img/icons/sets/core-icons.svg', 24);
149 * });
150 * </hljs>
151 *
152 *
153 * When using Font Icons with classnames:
154 * <hljs lang="html">
155 *
156 * <md-icon md-font-icon="android" aria-label="android" ></md-icon>
157 * <md-icon class="icon_home" aria-label="Home" ></md-icon>
158 *
159 * </hljs>
160 *
161 * When using Material Font Icons with ligatures:
162 * <hljs lang="html">
163 * <!--
164 * For Material Design Icons
165 * The class '.material-icons' is auto-added if a style has NOT been specified
166 * since `material-icons` is the default fontset. So your markup:
167 * -->
168 * <md-icon> face </md-icon>
169 * <!-- becomes this at runtime: -->
170 * <md-icon md-font-set="material-icons"> face </md-icon>
171 * <!-- If the fontset does not support ligature names, then we need to use the ligature unicode.-->
172 * <md-icon>  </md-icon>
173 * <!-- The class '.material-icons' must be manually added if other styles are also specified-->
174 * <md-icon class="material-icons md-light md-48"> face </md-icon>
175 * </hljs>
176 *
177 * When using other Font-Icon libraries:
178 *
179 * <hljs lang="js">
180 * // Specify a font-icon style alias
181 * angular.config(function($mdIconProvider) {
182 * $mdIconProvider.fontSet('md', 'material-icons');
183 * });
184 * </hljs>
185 *
186 * <hljs lang="html">
187 * <md-icon md-font-set="md">favorite</md-icon>
188 * </hljs>
189 *
190 */
191function mdIconDirective($mdIcon, $mdTheming, $mdAria, $sce) {
192
193 return {
194 restrict: 'E',
195 link : postLink
196 };
197
198
199 /**
200 * Directive postLink
201 * Supports embedded SVGs, font-icons, & external SVGs
202 */
203 function postLink(scope, element, attr) {
204 $mdTheming(element);
205 var lastFontIcon = attr.mdFontIcon;
206 var lastFontSet = $mdIcon.fontSet(attr.mdFontSet);
207
208 prepareForFontIcon();
209
210 attr.$observe('mdFontIcon', fontIconChanged);
211 attr.$observe('mdFontSet', fontIconChanged);
212
213 // Keep track of the content of the svg src so we can compare against it later to see if the
214 // attribute is static (and thus safe).
215 var originalSvgSrc = element[0].getAttribute(attr.$attr.mdSvgSrc);
216
217 // If using a font-icon, then the textual name of the icon itself
218 // provides the aria-label.
219
220 var label = attr.alt || attr.mdFontIcon || attr.mdSvgIcon || element.text();
221 var attrName = attr.$normalize(attr.$attr.mdSvgIcon || attr.$attr.mdSvgSrc || '');
222
223 if ( !attr['aria-label'] ) {
224
225 if (label !== '' && !parentsHaveText() ) {
226
227 $mdAria.expect(element, 'aria-label', label);
228 $mdAria.expect(element, 'role', 'img');
229
230 } else if ( !element.text() ) {
231 // If not a font-icon with ligature, then
232 // hide from the accessibility layer.
233
234 $mdAria.expect(element, 'aria-hidden', 'true');
235 }
236 }
237
238 if (attrName) {
239 // Use either pre-configured SVG or URL source, respectively.
240 attr.$observe(attrName, function(attrVal) {
241 element.empty();
242 if (attrVal) {
243 $mdIcon(attrVal)
244 .then(function(svg) {
245 element.empty();
246 element.append(svg);
247 });
248 }
249
250 });
251 }
252
253 function parentsHaveText() {
254 var parent = element.parent();
255 if (parent.attr('aria-label') || parent.text()) {
256 return true;
257 }
258 else if(parent.parent().attr('aria-label') || parent.parent().text()) {
259 return true;
260 }
261 return false;
262 }
263
264 function prepareForFontIcon() {
265 if (!attr.mdSvgIcon && !attr.mdSvgSrc) {
266 if (attr.mdFontIcon) {
267 element.addClass('md-font ' + attr.mdFontIcon);
268 }
269
270 element.addClass(lastFontSet);
271 }
272 }
273
274 function fontIconChanged() {
275 if (!attr.mdSvgIcon && !attr.mdSvgSrc) {
276 if (attr.mdFontIcon) {
277 element.removeClass(lastFontIcon);
278 element.addClass(attr.mdFontIcon);
279
280 lastFontIcon = attr.mdFontIcon;
281 }
282
283 var fontSet = $mdIcon.fontSet(attr.mdFontSet);
284
285 if (lastFontSet !== fontSet) {
286 element.removeClass(lastFontSet);
287 element.addClass(fontSet);
288
289 lastFontSet = fontSet;
290 }
291 }
292 }
293 }
294}
295
296
297MdIconService.$inject = ["config", "$templateRequest", "$q", "$log", "$mdUtil", "$sce"];angular
298 .module('material.components.icon')
299 .constant('$$mdSvgRegistry', {
300 'mdTabsArrow': 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxnPjxwb2x5Z29uIHBvaW50cz0iMTUuNCw3LjQgMTQsNiA4LDEyIDE0LDE4IDE1LjQsMTYuNiAxMC44LDEyICIvPjwvZz48L3N2Zz4=',
301 'mdClose': 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxnPjxwYXRoIGQ9Ik0xOSA2LjQxbC0xLjQxLTEuNDEtNS41OSA1LjU5LTUuNTktNS41OS0xLjQxIDEuNDEgNS41OSA1LjU5LTUuNTkgNS41OSAxLjQxIDEuNDEgNS41OS01LjU5IDUuNTkgNS41OSAxLjQxLTEuNDEtNS41OS01LjU5eiIvPjwvZz48L3N2Zz4=',
302 'mdCancel': 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxnPjxwYXRoIGQ9Ik0xMiAyYy01LjUzIDAtMTAgNC40Ny0xMCAxMHM0LjQ3IDEwIDEwIDEwIDEwLTQuNDcgMTAtMTAtNC40Ny0xMC0xMC0xMHptNSAxMy41OWwtMS40MSAxLjQxLTMuNTktMy41OS0zLjU5IDMuNTktMS40MS0xLjQxIDMuNTktMy41OS0zLjU5LTMuNTkgMS40MS0xLjQxIDMuNTkgMy41OSAzLjU5LTMuNTkgMS40MSAxLjQxLTMuNTkgMy41OSAzLjU5IDMuNTl6Ii8+PC9nPjwvc3ZnPg==',
303 'mdMenu': 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxwYXRoIGQ9Ik0zLDZIMjFWOEgzVjZNMywxMUgyMVYxM0gzVjExTTMsMTZIMjFWMThIM1YxNloiIC8+PC9zdmc+',
304 'mdToggleArrow': 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgNDggNDgiPjxwYXRoIGQ9Ik0yNCAxNmwtMTIgMTIgMi44MyAyLjgzIDkuMTctOS4xNyA5LjE3IDkuMTcgMi44My0yLjgzeiIvPjxwYXRoIGQ9Ik0wIDBoNDh2NDhoLTQ4eiIgZmlsbD0ibm9uZSIvPjwvc3ZnPg==',
305 'mdCalendar': 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTkgM2gtMVYxaC0ydjJIOFYxSDZ2Mkg1Yy0xLjExIDAtMS45OS45LTEuOTkgMkwzIDE5YzAgMS4xLjg5IDIgMiAyaDE0YzEuMSAwIDItLjkgMi0yVjVjMC0xLjEtLjktMi0yLTJ6bTAgMTZINVY4aDE0djExek03IDEwaDV2NUg3eiIvPjwvc3ZnPg==',
306 'mdChecked': 'data:image/svg+xml;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHg9IjBweCIgeT0iMHB4IiB2aWV3Qm94PSIwIDAgMjQgMjQiPjxnPjxwYXRoIGQ9Ik05IDE2LjE3TDQuODMgMTJsLTEuNDIgMS40MUw5IDE5IDIxIDdsLTEuNDEtMS40MXoiLz48L2c+PC9zdmc+'
307 })
308 .provider('$mdIcon', MdIconProvider);
309
310/**
311 * @ngdoc service
312 * @name $mdIconProvider
313 * @module material.components.icon
314 *
315 * @description
316 * `$mdIconProvider` is used only to register icon IDs with URLs. These configuration features allow
317 * icons and icon sets to be pre-registered and associated with source URLs **before** the `<md-icon />`
318 * directives are compiled.
319 *
320 * If using font-icons, the developer is responsible for loading the fonts.
321 *
322 * If using SVGs, loading of the actual svg files are deferred to on-demand requests and are loaded
323 * internally by the `$mdIcon` service using the `$templateRequest` service. When an SVG is
324 * requested by name/ID, the `$mdIcon` service searches its registry for the associated source URL;
325 * that URL is used to on-demand load and parse the SVG dynamically.
326 *
327 * The `$templateRequest` service expects the icons source to be loaded over trusted URLs.<br/>
328 * This means, when loading icons from an external URL, you have to trust the URL in the `$sceDelegateProvider`.
329 *
330 * <hljs lang="js">
331 * app.config(function($sceDelegateProvider) {
332 * $sceDelegateProvider.resourceUrlWhitelist([
333 * // Adding 'self' to the whitelist, will allow requests from the current origin.
334 * 'self',
335 * // Using double asterisks here, will allow all URLs to load.
336 * // We recommend to only specify the given domain you want to allow.
337 * '**'
338 * ]);
339 * });
340 * </hljs>
341 *
342 * Read more about the [$sceDelegateProvider](https://docs.angularjs.org/api/ng/provider/$sceDelegateProvider).
343 *
344 * **Notice:** Most font-icons libraries do not support ligatures (for example `fontawesome`).<br/>
345 * In such cases you are not able to use the icon's ligature name - Like so:
346 *
347 * <hljs lang="html">
348 * <md-icon md-font-set="fa">fa-bell</md-icon>
349 * </hljs>
350 *
351 * You should instead use the given unicode, instead of the ligature name.
352 *
353 * <p ng-hide="true"> ##// Notice we can't use a hljs element here, because the characters will be escaped.</p>
354 * ```html
355 * <md-icon md-font-set="fa"></md-icon>
356 * ```
357 *
358 * All unicode ligatures are prefixed with the `&#x` string.
359 *
360 * @usage
361 * <hljs lang="js">
362 * app.config(function($mdIconProvider) {
363 *
364 * // Configure URLs for icons specified by [set:]id.
365 *
366 * $mdIconProvider
367 * .defaultFontSet( 'fa' ) // This sets our default fontset className.
368 * .defaultIconSet('my/app/icons.svg') // Register a default set of SVG icons
369 * .iconSet('social', 'my/app/social.svg') // Register a named icon set of SVGs
370 * .icon('android', 'my/app/android.svg') // Register a specific icon (by name)
371 * .icon('work:chair', 'my/app/chair.svg'); // Register icon in a specific set
372 * });
373 * </hljs>
374 *
375 * SVG icons and icon sets can be easily pre-loaded and cached using either (a) a build process or (b) a runtime
376 * **startup** process (shown below):
377 *
378 * <hljs lang="js">
379 * app.config(function($mdIconProvider) {
380 *
381 * // Register a default set of SVG icon definitions
382 * $mdIconProvider.defaultIconSet('my/app/icons.svg')
383 *
384 * })
385 * .run(function($templateRequest){
386 *
387 * // Pre-fetch icons sources by URL and cache in the $templateCache...
388 * // subsequent $templateRequest calls will look there first.
389 *
390 * var urls = [ 'imy/app/icons.svg', 'img/icons/android.svg'];
391 *
392 * angular.forEach(urls, function(url) {
393 * $templateRequest(url);
394 * });
395 *
396 * });
397 *
398 * </hljs>
399 *
400 * > <b>Note:</b> The loaded SVG data is subsequently cached internally for future requests.
401 *
402 */
403
404/**
405 * @ngdoc method
406 * @name $mdIconProvider#icon
407 *
408 * @description
409 * Register a source URL for a specific icon name; the name may include optional 'icon set' name prefix.
410 * These icons will later be retrieved from the cache using `$mdIcon( <icon name> )`
411 *
412 * @param {string} id Icon name/id used to register the icon
413 * @param {string} url specifies the external location for the data file. Used internally by
414 * `$templateRequest` to load the data or as part of the lookup in `$templateCache` if pre-loading
415 * was configured.
416 * @param {number=} viewBoxSize Sets the width and height the icon's viewBox.
417 * It is ignored for icons with an existing viewBox. Default size is 24.
418 *
419 * @returns {obj} an `$mdIconProvider` reference; used to support method call chains for the API
420 *
421 * @usage
422 * <hljs lang="js">
423 * app.config(function($mdIconProvider) {
424 *
425 * // Configure URLs for icons specified by [set:]id.
426 *
427 * $mdIconProvider
428 * .icon('android', 'my/app/android.svg') // Register a specific icon (by name)
429 * .icon('work:chair', 'my/app/chair.svg'); // Register icon in a specific set
430 * });
431 * </hljs>
432 *
433 */
434/**
435 * @ngdoc method
436 * @name $mdIconProvider#iconSet
437 *
438 * @description
439 * Register a source URL for a 'named' set of icons; group of SVG definitions where each definition
440 * has an icon id. Individual icons can be subsequently retrieved from this cached set using
441 * `$mdIcon(<icon set name>:<icon name>)`
442 *
443 * @param {string} id Icon name/id used to register the iconset
444 * @param {string} url specifies the external location for the data file. Used internally by
445 * `$templateRequest` to load the data or as part of the lookup in `$templateCache` if pre-loading
446 * was configured.
447 * @param {number=} viewBoxSize Sets the width and height of the viewBox of all icons in the set.
448 * It is ignored for icons with an existing viewBox. All icons in the icon set should be the same size.
449 * Default value is 24.
450 *
451 * @returns {obj} an `$mdIconProvider` reference; used to support method call chains for the API
452 *
453 *
454 * @usage
455 * <hljs lang="js">
456 * app.config(function($mdIconProvider) {
457 *
458 * // Configure URLs for icons specified by [set:]id.
459 *
460 * $mdIconProvider
461 * .iconSet('social', 'my/app/social.svg') // Register a named icon set
462 * });
463 * </hljs>
464 *
465 */
466/**
467 * @ngdoc method
468 * @name $mdIconProvider#defaultIconSet
469 *
470 * @description
471 * Register a source URL for the default 'named' set of icons. Unless explicitly registered,
472 * subsequent lookups of icons will failover to search this 'default' icon set.
473 * Icon can be retrieved from this cached, default set using `$mdIcon(<name>)`
474 *
475 * @param {string} url specifies the external location for the data file. Used internally by
476 * `$templateRequest` to load the data or as part of the lookup in `$templateCache` if pre-loading
477 * was configured.
478 * @param {number=} viewBoxSize Sets the width and height of the viewBox of all icons in the set.
479 * It is ignored for icons with an existing viewBox. All icons in the icon set should be the same size.
480 * Default value is 24.
481 *
482 * @returns {obj} an `$mdIconProvider` reference; used to support method call chains for the API
483 *
484 * @usage
485 * <hljs lang="js">
486 * app.config(function($mdIconProvider) {
487 *
488 * // Configure URLs for icons specified by [set:]id.
489 *
490 * $mdIconProvider
491 * .defaultIconSet( 'my/app/social.svg' ) // Register a default icon set
492 * });
493 * </hljs>
494 *
495 */
496/**
497 * @ngdoc method
498 * @name $mdIconProvider#defaultFontSet
499 *
500 * @description
501 * When using Font-Icons, Angular Material assumes the the Material Design icons will be used and automatically
502 * configures the default font-set == 'material-icons'. Note that the font-set references the font-icon library
503 * class style that should be applied to the `<md-icon>`.
504 *
505 * Configuring the default means that the attributes
506 * `md-font-set="material-icons"` or `class="material-icons"` do not need to be explicitly declared on the
507 * `<md-icon>` markup. For example:
508 *
509 * `<md-icon> face </md-icon>`
510 * will render as
511 * `<span class="material-icons"> face </span>`, and
512 *
513 * `<md-icon md-font-set="fa"> face </md-icon>`
514 * will render as
515 * `<span class="fa"> face </span>`
516 *
517 * @param {string} name of the font-library style that should be applied to the md-icon DOM element
518 *
519 * @usage
520 * <hljs lang="js">
521 * app.config(function($mdIconProvider) {
522 * $mdIconProvider.defaultFontSet( 'fa' );
523 * });
524 * </hljs>
525 *
526 */
527
528/**
529 * @ngdoc method
530 * @name $mdIconProvider#fontSet
531 *
532 * @description
533 * When using a font set for `<md-icon>` you must specify the correct font classname in the `md-font-set`
534 * attribute. If the fonset className is really long, your markup may become cluttered... an easy
535 * solution is to define an `alias` for your fontset:
536 *
537 * @param {string} alias of the specified fontset.
538 * @param {string} className of the fontset.
539 *
540 * @usage
541 * <hljs lang="js">
542 * app.config(function($mdIconProvider) {
543 * // In this case, we set an alias for the `material-icons` fontset.
544 * $mdIconProvider.fontSet('md', 'material-icons');
545 * });
546 * </hljs>
547 *
548 */
549
550/**
551 * @ngdoc method
552 * @name $mdIconProvider#defaultViewBoxSize
553 *
554 * @description
555 * While `<md-icon />` markup can also be style with sizing CSS, this method configures
556 * the default width **and** height used for all icons; unless overridden by specific CSS.
557 * The default sizing is (24px, 24px).
558 * @param {number=} viewBoxSize Sets the width and height of the viewBox for an icon or an icon set.
559 * All icons in a set should be the same size. The default value is 24.
560 *
561 * @returns {obj} an `$mdIconProvider` reference; used to support method call chains for the API
562 *
563 * @usage
564 * <hljs lang="js">
565 * app.config(function($mdIconProvider) {
566 *
567 * // Configure URLs for icons specified by [set:]id.
568 *
569 * $mdIconProvider
570 * .defaultViewBoxSize(36) // Register a default icon size (width == height)
571 * });
572 * </hljs>
573 *
574 */
575
576var config = {
577 defaultViewBoxSize: 24,
578 defaultFontSet: 'material-icons',
579 fontSets: []
580};
581
582function MdIconProvider() {
583}
584
585MdIconProvider.prototype = {
586 icon: function(id, url, viewBoxSize) {
587 if (id.indexOf(':') == -1) id = '$default:' + id;
588
589 config[id] = new ConfigurationItem(url, viewBoxSize);
590 return this;
591 },
592
593 iconSet: function(id, url, viewBoxSize) {
594 config[id] = new ConfigurationItem(url, viewBoxSize);
595 return this;
596 },
597
598 defaultIconSet: function(url, viewBoxSize) {
599 var setName = '$default';
600
601 if (!config[setName]) {
602 config[setName] = new ConfigurationItem(url, viewBoxSize);
603 }
604
605 config[setName].viewBoxSize = viewBoxSize || config.defaultViewBoxSize;
606
607 return this;
608 },
609
610 defaultViewBoxSize: function(viewBoxSize) {
611 config.defaultViewBoxSize = viewBoxSize;
612 return this;
613 },
614
615 /**
616 * Register an alias name associated with a font-icon library style ;
617 */
618 fontSet: function fontSet(alias, className) {
619 config.fontSets.push({
620 alias: alias,
621 fontSet: className || alias
622 });
623 return this;
624 },
625
626 /**
627 * Specify a default style name associated with a font-icon library
628 * fallback to Material Icons.
629 *
630 */
631 defaultFontSet: function defaultFontSet(className) {
632 config.defaultFontSet = !className ? '' : className;
633 return this;
634 },
635
636 defaultIconSize: function defaultIconSize(iconSize) {
637 config.defaultIconSize = iconSize;
638 return this;
639 },
640
641 $get: ['$templateRequest', '$q', '$log', '$mdUtil', '$sce', function($templateRequest, $q, $log, $mdUtil, $sce) {
642 return MdIconService(config, $templateRequest, $q, $log, $mdUtil, $sce);
643 }]
644};
645
646/**
647 * Configuration item stored in the Icon registry; used for lookups
648 * to load if not already cached in the `loaded` cache
649 */
650function ConfigurationItem(url, viewBoxSize) {
651 this.url = url;
652 this.viewBoxSize = viewBoxSize || config.defaultViewBoxSize;
653}
654
655/**
656 * @ngdoc service
657 * @name $mdIcon
658 * @module material.components.icon
659 *
660 * @description
661 * The `$mdIcon` service is a function used to lookup SVG icons.
662 *
663 * @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
664 * 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
665 * performed. The Id may be a unique icon ID or may include an iconSet ID prefix.
666 *
667 * For the **id** query to work properly, this means that all id-to-URL mappings must have been previously configured
668 * using the `$mdIconProvider`.
669 *
670 * @returns {angular.$q.Promise} A promise that gets resolved to a clone of the initial SVG DOM element; which was
671 * created from the SVG markup in the SVG data file. If an error occurs (e.g. the icon cannot be found) the promise
672 * will get rejected.
673 *
674 * @usage
675 * <hljs lang="js">
676 * function SomeDirective($mdIcon) {
677 *
678 * // See if the icon has already been loaded, if not
679 * // then lookup the icon from the registry cache, load and cache
680 * // it for future requests.
681 * // NOTE: ID queries require configuration with $mdIconProvider
682 *
683 * $mdIcon('android').then(function(iconEl) { element.append(iconEl); });
684 * $mdIcon('work:chair').then(function(iconEl) { element.append(iconEl); });
685 *
686 * // Load and cache the external SVG using a URL
687 *
688 * $mdIcon('img/icons/android.svg').then(function(iconEl) {
689 * element.append(iconEl);
690 * });
691 * };
692 * </hljs>
693 *
694 * > <b>Note:</b> The `<md-icon>` directive internally uses the `$mdIcon` service to query, loaded,
695 * and instantiate SVG DOM elements.
696 */
697
698/* ngInject */
699function MdIconService(config, $templateRequest, $q, $log, $mdUtil, $sce) {
700 var iconCache = {};
701 var svgCache = {};
702 var urlRegex = /[-\w@:%\+.~#?&//=]{2,}\.[a-z]{2,4}\b(\/[-\w@:%\+.~#?&//=]*)?/i;
703 var dataUrlRegex = /^data:image\/svg\+xml[\s*;\w\-\=]*?(base64)?,(.*)$/i;
704
705 Icon.prototype = {clone: cloneSVG, prepare: prepareAndStyle};
706 getIcon.fontSet = findRegisteredFontSet;
707
708 // Publish service...
709 return getIcon;
710
711 /**
712 * Actual $mdIcon service is essentially a lookup function
713 */
714 function getIcon(id) {
715 id = id || '';
716
717 // If the "id" provided is not a string, the only other valid value is a $sce trust wrapper
718 // over a URL string. If the value is not trusted, this will intentionally throw an error
719 // because the user is attempted to use an unsafe URL, potentially opening themselves up
720 // to an XSS attack.
721 if (!angular.isString(id)) {
722 id = $sce.getTrustedUrl(id);
723 }
724
725 // If already loaded and cached, use a clone of the cached icon.
726 // Otherwise either load by URL, or lookup in the registry and then load by URL, and cache.
727
728 if (iconCache[id]) {
729 return $q.when(transformClone(iconCache[id]));
730 }
731
732 if (urlRegex.test(id) || dataUrlRegex.test(id)) {
733 return loadByURL(id).then(cacheIcon(id));
734 }
735
736 if (id.indexOf(':') == -1) {
737 id = '$default:' + id;
738 }
739
740 var load = config[id] ? loadByID : loadFromIconSet;
741 return load(id)
742 .then(cacheIcon(id));
743 }
744
745 /**
746 * Lookup registered fontSet style using its alias...
747 * If not found,
748 */
749 function findRegisteredFontSet(alias) {
750 var useDefault = angular.isUndefined(alias) || !(alias && alias.length);
751 if (useDefault) return config.defaultFontSet;
752
753 var result = alias;
754 angular.forEach(config.fontSets, function(it) {
755 if (it.alias == alias) result = it.fontSet || result;
756 });
757
758 return result;
759 }
760
761 function transformClone(cacheElement) {
762 var clone = cacheElement.clone();
763 var cacheSuffix = '_cache' + $mdUtil.nextUid();
764
765 // We need to modify for each cached icon the id attributes.
766 // This is needed because SVG id's are treated as normal DOM ids
767 // and should not have a duplicated id.
768 if (clone.id) clone.id += cacheSuffix;
769 angular.forEach(clone.querySelectorAll('[id]'), function(item) {
770 item.id += cacheSuffix;
771 });
772
773 return clone;
774 }
775
776 /**
777 * Prepare and cache the loaded icon for the specified `id`
778 */
779 function cacheIcon(id) {
780
781 return function updateCache(icon) {
782 iconCache[id] = isIcon(icon) ? icon : new Icon(icon, config[id]);
783
784 return iconCache[id].clone();
785 };
786 }
787
788 /**
789 * Lookup the configuration in the registry, if !registered throw an error
790 * otherwise load the icon [on-demand] using the registered URL.
791 *
792 */
793 function loadByID(id) {
794 var iconConfig = config[id];
795 return loadByURL(iconConfig.url).then(function(icon) {
796 return new Icon(icon, iconConfig);
797 });
798 }
799
800 /**
801 * Loads the file as XML and uses querySelector( <id> ) to find
802 * the desired node...
803 */
804 function loadFromIconSet(id) {
805 var setName = id.substring(0, id.lastIndexOf(':')) || '$default';
806 var iconSetConfig = config[setName];
807
808 return !iconSetConfig ? announceIdNotFound(id) : loadByURL(iconSetConfig.url).then(extractFromSet);
809
810 function extractFromSet(set) {
811 var iconName = id.slice(id.lastIndexOf(':') + 1);
812 var icon = set.querySelector('#' + iconName);
813 return icon ? new Icon(icon, iconSetConfig) : announceIdNotFound(id);
814 }
815
816 function announceIdNotFound(id) {
817 var msg = 'icon ' + id + ' not found';
818 $log.warn(msg);
819
820 return $q.reject(msg || id);
821 }
822 }
823
824 /**
825 * Load the icon by URL (may use the $templateCache).
826 * Extract the data for later conversion to Icon
827 */
828 function loadByURL(url) {
829 /* Load the icon from embedded data URL. */
830 function loadByDataUrl(url) {
831 var results = dataUrlRegex.exec(url);
832 var isBase64 = /base64/i.test(url);
833 var data = isBase64 ? window.atob(results[2]) : results[2];
834
835 return $q.when(angular.element(data)[0]);
836 }
837
838 /* Load the icon by URL using HTTP. */
839 function loadByHttpUrl(url) {
840 return $q(function(resolve, reject) {
841 // Catch HTTP or generic errors not related to incorrect icon IDs.
842 var announceAndReject = function(err) {
843 var msg = angular.isString(err) ? err : (err.message || err.data || err.statusText);
844 $log.warn(msg);
845 reject(err);
846 },
847 extractSvg = function(response) {
848 if (!svgCache[url]) {
849 svgCache[url] = angular.element('<div>').append(response)[0].querySelector('svg');
850 }
851 resolve(svgCache[url]);
852 };
853
854 $templateRequest(url, true).then(extractSvg, announceAndReject);
855 });
856 }
857
858 return dataUrlRegex.test(url)
859 ? loadByDataUrl(url)
860 : loadByHttpUrl(url);
861 }
862
863 /**
864 * Check target signature to see if it is an Icon instance.
865 */
866 function isIcon(target) {
867 return angular.isDefined(target.element) && angular.isDefined(target.config);
868 }
869
870 /**
871 * Define the Icon class
872 */
873 function Icon(el, config) {
874 if (el && el.tagName != 'svg') {
875 el = angular.element('<svg xmlns="http://www.w3.org/2000/svg">').append(el.cloneNode(true))[0];
876 }
877
878 // Inject the namespace if not available...
879 if (!el.getAttribute('xmlns')) {
880 el.setAttribute('xmlns', "http://www.w3.org/2000/svg");
881 }
882
883 this.element = el;
884 this.config = config;
885 this.prepare();
886 }
887
888 /**
889 * Prepare the DOM element that will be cached in the
890 * loaded iconCache store.
891 */
892 function prepareAndStyle() {
893 var viewBoxSize = this.config ? this.config.viewBoxSize : config.defaultViewBoxSize;
894 angular.forEach({
895 'fit': '',
896 'height': '100%',
897 'width': '100%',
898 'preserveAspectRatio': 'xMidYMid meet',
899 'viewBox': this.element.getAttribute('viewBox') || ('0 0 ' + viewBoxSize + ' ' + viewBoxSize),
900 'focusable': false // Disable IE11s default behavior to make SVGs focusable
901 }, function(val, attr) {
902 this.element.setAttribute(attr, val);
903 }, this);
904 }
905
906 /**
907 * Clone the Icon DOM element.
908 */
909 function cloneSVG() {
910 // If the element or any of its children have a style attribute, then a CSP policy without
911 // 'unsafe-inline' in the style-src directive, will result in a violation.
912 return this.element.cloneNode(true);
913 }
914
915}
916
917})(window, window.angular);