You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

916 lines
32 KiB

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