/*! * Angular Material Design * https://github.com/angular/material * @license MIT * v0.10.0 */ (function( window, angular, undefined ){ "use strict"; /** * @ngdoc module * @name material.components.icon * @description * Icon */ angular.module('material.components.icon', [ 'material.core' ]) .directive('mdIcon', mdIconDirective); /** * @ngdoc directive * @name mdIcon * @module material.components.icon * * @restrict E * * @description * The `` directive is an markup element useful for showing an icon based on a font-icon * or a SVG. Icons are view-only elements that should not be used directly as buttons; instead nest a `` * inside a `md-button` to add hover and click features. * * When using SVGs, both external SVGs (via URLs) or sets of SVGs [from icon sets] can be * easily loaded and used.When use font-icons, developers must following three (3) simple steps: * *
    *
  1. Load the font library. e.g.
    * <link href="https://fonts.googleapis.com/icon?family=Material+Icons" * rel="stylesheet"> *
  2. *
  3. Use either (a) font-icon class names or (b) font ligatures to render the font glyph by using its textual name
  4. *
  5. Use <md-icon md-font-icon="classname" /> or
    * use <md-icon md-font-set="font library classname or alias"> textual_name </md-icon> or
    * use <md-icon md-font-set="font library classname or alias"> numerical_character_reference </md-icon> *
  6. *
* * Full details for these steps can be found: * * * * The Material Design icon style .material-icons and the icon font references are published in * Material Design Icons: * * * *

Material Design Icons

* Using the Material Design Icon-Selector, developers can easily and quickly search for a Material Design font-icon and * determine its textual name and character reference code. Click on any icon to see the slide-up information * panel with details regarding a SVG download or information on the font-icon usage. * * * * * * * Click on the image above to link to the * Material Design Icon-Selector. * * * @param {string} md-font-icon Name of CSS icon associated with the font-face will be used * to render the icon. Requires the fonts and the named CSS styles to be preloaded. * @param {string} md-font-set CSS style name associated with the font library; which will be assigned as * the class for the font-icon ligature. This value may also be an alias that is used to lookup the classname; * internally use `$mdIconProvider.fontSet()` to determine the style name. * @param {string} md-svg-src URL [or expression ] used to load, cache, and display an external SVG. * @param {string} md-svg-icon Name used for lookup of the icon from the internal cache; interpolated strings or * expressions may also be used. Specific set names can be used with the syntax `:`.

* To use icon sets, developers are required to pre-register the sets using the `$mdIconProvider` service. * @param {string=} aria-label Labels icon for accessibility. If an empty string is provided, icon * will be hidden from accessibility layer with `aria-hidden="true"`. If there's no aria-label on the icon * nor a label on the parent element, a warning will be logged to the console. * * @usage * When using SVGs: * * * * * * * * * * * * Use the $mdIconProvider to configure your application with * svg iconsets. * * * angular.module('appSvgIconSets', ['ngMaterial']) * .controller('DemoCtrl', function($scope) {}) * .config(function($mdIconProvider) { * $mdIconProvider * .iconSet('social', 'img/icons/sets/social-icons.svg', 24) * .defaultIconSet('img/icons/sets/core-icons.svg', 24); * }); * * * * When using Font Icons with classnames: * * * * * * * * When using Material Font Icons with ligatures: * * * * face * face * face * #xE87C; * * * When using other Font-Icon libraries: * * * // Specify a font-icon style alias * angular.config(function($mdIconProvider) { * $mdIconProvider.fontSet('fa', 'fontawesome'); * }); * * * * email * * */ function mdIconDirective($mdIcon, $mdTheming, $mdAria, $interpolate ) { return { scope: { fontSet : '@mdFontSet', fontIcon: '@mdFontIcon', svgIcon : '@mdSvgIcon', svgSrc : '@mdSvgSrc' }, restrict: 'E', link : postLink }; /** * Directive postLink * Supports embedded SVGs, font-icons, & external SVGs */ function postLink(scope, element, attr) { $mdTheming(element); prepareForFontIcon(); // If using a font-icon, then the textual name of the icon itself // provides the aria-label. var label = attr.alt || scope.fontIcon || scope.svgIcon || element.text(); var attrName = attr.$normalize(attr.$attr.mdSvgIcon || attr.$attr.mdSvgSrc || ''); if ( !attr['aria-label'] ) { if (label != '' && !parentsHaveText() ) { $mdAria.expect(element, 'aria-label', label); $mdAria.expect(element, 'role', 'img'); } else if ( !element.text() ) { // If not a font-icon with ligature, then // hide from the accessibility layer. $mdAria.expect(element, 'aria-hidden', 'true'); } } if (attrName) { // Use either pre-configured SVG or URL source, respectively. attr.$observe(attrName, function(attrVal) { element.empty(); if (attrVal) { $mdIcon(attrVal).then(function(svg) { element.append(svg); }); } }); } function parentsHaveText() { var parent = element.parent(); if (parent.attr('aria-label') || parent.text()) { return true; } else if(parent.parent().attr('aria-label') || parent.parent().text()) { return true; } return false; } function prepareForFontIcon () { if (!scope.svgIcon && !scope.svgSrc) { if (scope.fontIcon) { element.addClass('md-font'); element.addClass(scope.fontIcon); } else { element.addClass($mdIcon.fontSet(scope.fontSet)); } } } } } mdIconDirective.$inject = ["$mdIcon", "$mdTheming", "$mdAria", "$interpolate"]; angular .module('material.components.icon' ) .provider('$mdIcon', MdIconProvider); /** * @ngdoc service * @name $mdIconProvider * @module material.components.icon * * @description * `$mdIconProvider` is used only to register icon IDs with URLs. These configuration features allow * icons and icon sets to be pre-registered and associated with source URLs **before** the `` * directives are compiled. * * If using font-icons, the developer is repsonsible for loading the fonts. * * If using SVGs, loading of the actual svg files are deferred to on-demand requests and are loaded * internally by the `$mdIcon` service using the `$http` service. When an SVG is requested by name/ID, * the `$mdIcon` service searches its registry for the associated source URL; * that URL is used to on-demand load and parse the SVG dynamically. * * @usage * * app.config(function($mdIconProvider) { * * // Configure URLs for icons specified by [set:]id. * * $mdIconProvider * .defaultFontSet( 'fontawesome' ) * .defaultIconSet('my/app/icons.svg') // Register a default set of SVG icons * .iconSet('social', 'my/app/social.svg') // Register a named icon set of SVGs * .icon('android', 'my/app/android.svg') // Register a specific icon (by name) * .icon('work:chair', 'my/app/chair.svg'); // Register icon in a specific set * }); * * * SVG icons and icon sets can be easily pre-loaded and cached using either (a) a build process or (b) a runtime * **startup** process (shown below): * * * app.config(function($mdIconProvider) { * * // Register a default set of SVG icon definitions * $mdIconProvider.defaultIconSet('my/app/icons.svg') * * }) * .run(function($http, $templateCache){ * * // Pre-fetch icons sources by URL and cache in the $templateCache... * // subsequent $http calls will look there first. * * var urls = [ 'imy/app/icons.svg', 'img/icons/android.svg']; * * angular.forEach(urls, function(url) { * $http.get(url, {cache: $templateCache}); * }); * * }); * * * * NOTE: the loaded SVG data is subsequently cached internally for future requests. * */ /** * @ngdoc method * @name $mdIconProvider#icon * * @description * Register a source URL for a specific icon name; the name may include optional 'icon set' name prefix. * These icons will later be retrieved from the cache using `$mdIcon( )` * * @param {string} id Icon name/id used to register the icon * @param {string} url specifies the external location for the data file. Used internally by `$http` to load the * data or as part of the lookup in `$templateCache` if pre-loading was configured. * @param {number=} viewBoxSize Sets the width and height the icon's viewBox. * It is ignored for icons with an existing viewBox. Default size is 24. * * @returns {obj} an `$mdIconProvider` reference; used to support method call chains for the API * * @usage * * app.config(function($mdIconProvider) { * * // Configure URLs for icons specified by [set:]id. * * $mdIconProvider * .icon('android', 'my/app/android.svg') // Register a specific icon (by name) * .icon('work:chair', 'my/app/chair.svg'); // Register icon in a specific set * }); * * */ /** * @ngdoc method * @name $mdIconProvider#iconSet * * @description * Register a source URL for a 'named' set of icons; group of SVG definitions where each definition * has an icon id. Individual icons can be subsequently retrieved from this cached set using * `$mdIcon(:)` * * @param {string} id Icon name/id used to register the iconset * @param {string} url specifies the external location for the data file. Used internally by `$http` to load the * data or as part of the lookup in `$templateCache` if pre-loading was configured. * @param {number=} viewBoxSize Sets the width and height of the viewBox of all icons in the set. * It is ignored for icons with an existing viewBox. All icons in the icon set should be the same size. * Default value is 24. * * @returns {obj} an `$mdIconProvider` reference; used to support method call chains for the API * * * @usage * * app.config(function($mdIconProvider) { * * // Configure URLs for icons specified by [set:]id. * * $mdIconProvider * .iconSet('social', 'my/app/social.svg') // Register a named icon set * }); * * */ /** * @ngdoc method * @name $mdIconProvider#defaultIconSet * * @description * Register a source URL for the default 'named' set of icons. Unless explicitly registered, * subsequent lookups of icons will failover to search this 'default' icon set. * Icon can be retrieved from this cached, default set using `$mdIcon()` * * @param {string} url specifies the external location for the data file. Used internally by `$http` to load the * data or as part of the lookup in `$templateCache` if pre-loading was configured. * @param {number=} viewBoxSize Sets the width and height of the viewBox of all icons in the set. * It is ignored for icons with an existing viewBox. All icons in the icon set should be the same size. * Default value is 24. * * @returns {obj} an `$mdIconProvider` reference; used to support method call chains for the API * * @usage * * app.config(function($mdIconProvider) { * * // Configure URLs for icons specified by [set:]id. * * $mdIconProvider * .defaultIconSet( 'my/app/social.svg' ) // Register a default icon set * }); * * */ /** * @ngdoc method * @name $mdIconProvider#defaultFontSet * * @description * When using Font-Icons, Angular Material assumes the the Material Design icons will be used and automatically * configures the default font-set == 'material-icons'. Note that the font-set references the font-icon library * class style that should be applied to the ``. * * Configuring the default means that the attributes * `md-font-set="material-icons"` or `class="material-icons"` do not need to be explicitly declared on the * `` markup. For example: * * ` face ` * will render as * ` face `, and * * ` face ` * will render as * ` face ` * * @param {string} name of the font-library style that should be applied to the md-icon DOM element * * @usage * * app.config(function($mdIconProvider) { * $mdIconProvider.defaultFontSet( 'fontawesome' ); * }); * * */ /** * @ngdoc method * @name $mdIconProvider#defaultViewBoxSize * * @description * While `` markup can also be style with sizing CSS, this method configures * the default width **and** height used for all icons; unless overridden by specific CSS. * The default sizing is (24px, 24px). * @param {number=} viewBoxSize Sets the width and height of the viewBox for an icon or an icon set. * All icons in a set should be the same size. The default value is 24. * * @returns {obj} an `$mdIconProvider` reference; used to support method call chains for the API * * @usage * * app.config(function($mdIconProvider) { * * // Configure URLs for icons specified by [set:]id. * * $mdIconProvider * .defaultViewBoxSize(36) // Register a default icon size (width == height) * }); * * */ var config = { defaultViewBoxSize: 24, defaultFontSet: 'material-icons', fontSets : [ ] }; function MdIconProvider() { } MdIconProvider.prototype = { icon : function (id, url, viewBoxSize) { if ( id.indexOf(':') == -1 ) id = '$default:' + id; config[id] = new ConfigurationItem(url, viewBoxSize ); return this; }, iconSet : function (id, url, viewBoxSize) { config[id] = new ConfigurationItem(url, viewBoxSize ); return this; }, defaultIconSet : function (url, viewBoxSize) { var setName = '$default'; if ( !config[setName] ) { config[setName] = new ConfigurationItem(url, viewBoxSize ); } config[setName].viewBoxSize = viewBoxSize || config.defaultViewBoxSize; return this; }, defaultViewBoxSize : function (viewBoxSize) { config.defaultViewBoxSize = viewBoxSize; return this; }, /** * Register an alias name associated with a font-icon library style ; */ fontSet : function fontSet(alias, className) { config.fontSets.push({ alias : alias, fontSet : className || alias }); }, /** * Specify a default style name associated with a font-icon library * fallback to Material Icons. * */ defaultFontSet : function defaultFontSet(className) { config.defaultFontSet = !className ? '' : className; return this; }, defaultIconSize : function defaultIconSize(iconSize) { config.defaultIconSize = iconSize; return this; }, preloadIcons: function ($templateCache) { var iconProvider = this; var svgRegistry = [ { id : 'md-tabs-arrow', url: 'md-tabs-arrow.svg', svg: '' }, { id : 'md-close', url: 'md-close.svg', svg: '' }, { id: 'md-cancel', url: 'md-cancel.svg', svg: '' }, { id: 'md-menu', url: 'md-menu.svg', svg: '' }, { id: 'md-toggle-arrow', url: 'md-toggle-arrow-svg', svg: '' } ]; svgRegistry.forEach(function(asset){ iconProvider.icon(asset.id, asset.url); $templateCache.put(asset.url, asset.svg); }); }, $get : ['$http', '$q', '$log', '$templateCache', function($http, $q, $log, $templateCache) { this.preloadIcons($templateCache); return MdIconService(config, $http, $q, $log, $templateCache); }] }; /** * Configuration item stored in the Icon registry; used for lookups * to load if not already cached in the `loaded` cache */ function ConfigurationItem(url, viewBoxSize) { this.url = url; this.viewBoxSize = viewBoxSize || config.defaultViewBoxSize; } /** * @ngdoc service * @name $mdIcon * @module material.components.icon * * @description * The `$mdIcon` service is a function used to lookup SVG icons. * * @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 * 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 * performed. The Id may be a unique icon ID or may include an iconSet ID prefix. * * For the **id** query to work properly, this means that all id-to-URL mappings must have been previously configured * using the `$mdIconProvider`. * * @returns {obj} Clone of the initial SVG DOM element; which was created from the SVG markup in the SVG data file. * * @usage * * function SomeDirective($mdIcon) { * * // See if the icon has already been loaded, if not * // then lookup the icon from the registry cache, load and cache * // it for future requests. * // NOTE: ID queries require configuration with $mdIconProvider * * $mdIcon('android').then(function(iconEl) { element.append(iconEl); }); * $mdIcon('work:chair').then(function(iconEl) { element.append(iconEl); }); * * // Load and cache the external SVG using a URL * * $mdIcon('img/icons/android.svg').then(function(iconEl) { * element.append(iconEl); * }); * }; * * * NOTE: The ` ` directive internally uses the `$mdIcon` service to query, loaded, and instantiate * SVG DOM elements. */ function MdIconService(config, $http, $q, $log, $templateCache) { var iconCache = {}; var urlRegex = /[-a-zA-Z0-9@:%_\+.~#?&//=]{2,256}\.[a-z]{2,4}\b(\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?/i; Icon.prototype = { clone : cloneSVG, prepare: prepareAndStyle }; getIcon.fontSet = findRegisteredFontSet; // Publish service... return getIcon; /** * Actual $mdIcon service is essentially a lookup function */ function getIcon(id) { id = id || ''; // If already loaded and cached, use a clone of the cached icon. // Otherwise either load by URL, or lookup in the registry and then load by URL, and cache. if ( iconCache[id] ) return $q.when( iconCache[id].clone() ); if ( urlRegex.test(id) ) return loadByURL(id).then( cacheIcon(id) ); if ( id.indexOf(':') == -1 ) id = '$default:' + id; return loadByID(id) .catch(loadFromIconSet) .catch(announceIdNotFound) .catch(announceNotFound) .then( cacheIcon(id) ); } /** * Lookup registered fontSet style using its alias... * If not found, */ function findRegisteredFontSet(alias) { var useDefault = angular.isUndefined(alias) || !(alias && alias.length); if ( useDefault ) return config.defaultFontSet; var result = alias; angular.forEach(config.fontSets, function(it){ if ( it.alias == alias ) result = it.fontSet || result; }); return result; } /** * Prepare and cache the loaded icon for the specified `id` */ function cacheIcon( id ) { return function updateCache( icon ) { iconCache[id] = isIcon(icon) ? icon : new Icon(icon, config[id]); return iconCache[id].clone(); }; } /** * Lookup the configuration in the registry, if !registered throw an error * otherwise load the icon [on-demand] using the registered URL. * */ function loadByID(id) { var iconConfig = config[id]; return !iconConfig ? $q.reject(id) : loadByURL(iconConfig.url).then(function(icon) { return new Icon(icon, iconConfig); }); } /** * Loads the file as XML and uses querySelector( ) to find * the desired node... */ function loadFromIconSet(id) { var setName = id.substring(0, id.lastIndexOf(':')) || '$default'; var iconSetConfig = config[setName]; return !iconSetConfig ? $q.reject(id) : loadByURL(iconSetConfig.url).then(extractFromSet); function extractFromSet(set) { var iconName = id.slice(id.lastIndexOf(':') + 1); var icon = set.querySelector('#' + iconName); return !icon ? $q.reject(id) : new Icon(icon, iconSetConfig); } } /** * Load the icon by URL (may use the $templateCache). * Extract the data for later conversion to Icon */ function loadByURL(url) { return $http .get(url, { cache: $templateCache }) .then(function(response) { return angular.element('
').append(response.data).find('svg')[0]; }); } /** * User did not specify a URL and the ID has not been registered with the $mdIcon * registry */ function announceIdNotFound(id) { var msg; if (angular.isString(id)) { msg = 'icon ' + id + ' not found'; $log.warn(msg); } return $q.reject(msg || id); } /** * Catch HTTP or generic errors not related to incorrect icon IDs. */ function announceNotFound(err) { var msg = angular.isString(err) ? err : (err.message || err.data || err.statusText); $log.warn(msg); return $q.reject(msg); } /** * Check target signature to see if it is an Icon instance. */ function isIcon(target) { return angular.isDefined(target.element) && angular.isDefined(target.config); } /** * Define the Icon class */ function Icon(el, config) { if (el.tagName != 'svg') { el = angular.element('').append(el)[0]; } // Inject the namespace if not available... if ( !el.getAttribute('xmlns') ) { el.setAttribute('xmlns', "http://www.w3.org/2000/svg"); } this.element = el; this.config = config; this.prepare(); } /** * Prepare the DOM element that will be cached in the * loaded iconCache store. */ function prepareAndStyle() { var viewBoxSize = this.config ? this.config.viewBoxSize : config.defaultViewBoxSize; angular.forEach({ 'fit' : '', 'height': '100%', 'width' : '100%', 'preserveAspectRatio': 'xMidYMid meet', 'viewBox' : this.element.getAttribute('viewBox') || ('0 0 ' + viewBoxSize + ' ' + viewBoxSize) }, function(val, attr) { this.element.setAttribute(attr, val); }, this); angular.forEach({ 'pointer-events' : 'none', 'display' : 'block' }, function(val, style) { this.element.style[style] = val; }, this); } /** * Clone the Icon DOM element. */ function cloneSVG(){ return this.element.cloneNode(true); } } })(window, window.angular);