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 | */
|
---|
16 | angular.module('material.components.icon', ['material.core']);
|
---|
17 |
|
---|
18 | angular
|
---|
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>  </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 | */
|
---|
211 | function 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 |
|
---|
317 | MdIconService['$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"></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 |
|
---|
582 | var config = {
|
---|
583 | defaultViewBoxSize: 24,
|
---|
584 | defaultFontSet: 'material-icons',
|
---|
585 | fontSets: []
|
---|
586 | };
|
---|
587 |
|
---|
588 | function MdIconProvider() {
|
---|
589 | }
|
---|
590 |
|
---|
591 | MdIconProvider.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 */
|
---|
705 | function 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); |
---|