<!-- @license Copyright (c) 2015 The Polymer Project Authors. All rights reserved. This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt --> <link rel="import" href="../polymer/polymer.html"> <link rel="import" href="../iron-meta/iron-meta.html"> <script> /** * The `iron-iconset-svg` element allows users to define their own icon sets * that contain svg icons. The svg icon elements should be children of the * `iron-iconset-svg` element. Multiple icons should be given distinct id's. * * Using svg elements to create icons has a few advantages over traditional * bitmap graphics like jpg or png. Icons that use svg are vector based so * they are resolution independent and should look good on any device. They * are stylable via css. Icons can be themed, colorized, and even animated. * * Example: * * <iron-iconset-svg name="my-svg-icons" size="24"> * <svg> * <defs> * <g id="shape"> * <rect x="50" y="50" width="50" height="50" /> * <circle cx="50" cy="50" r="50" /> * </g> * </defs> * </svg> * </iron-iconset-svg> * * This will automatically register the icon set "my-svg-icons" to the iconset * database. To use these icons from within another element, make a * `iron-iconset` element and call the `byId` method * to retrieve a given iconset. To apply a particular icon inside an * element use the `applyIcon` method. For example: * * iconset.applyIcon(iconNode, 'car'); * * @element iron-iconset-svg * @demo demo/index.html * @implements {Polymer.Iconset} */ Polymer({ is: 'iron-iconset-svg', properties: { /** * The name of the iconset. */ name: { type: String, observer: '_nameChanged' }, /** * The size of an individual icon. Note that icons must be square. */ size: { type: Number, value: 24 } }, attached: function() { this.style.display = 'none'; }, /** * Construct an array of all icon names in this iconset. * * @return {!Array} Array of icon names. */ getIconNames: function() { this._icons = this._createIconMap(); return Object.keys(this._icons).map(function(n) { return this.name + ':' + n; }, this); }, /** * Applies an icon to the given element. * * An svg icon is prepended to the element's shadowRoot if it exists, * otherwise to the element itself. * * @method applyIcon * @param {Element} element Element to which the icon is applied. * @param {string} iconName Name of the icon to apply. * @return {?Element} The svg element which renders the icon. */ applyIcon: function(element, iconName) { // insert svg element into shadow root, if it exists element = element.root || element; // Remove old svg element this.removeIcon(element); // install new svg element var svg = this._cloneIcon(iconName); if (svg) { var pde = Polymer.dom(element); pde.insertBefore(svg, pde.childNodes[0]); return element._svgIcon = svg; } return null; }, /** * Remove an icon from the given element by undoing the changes effected * by `applyIcon`. * * @param {Element} element The element from which the icon is removed. */ removeIcon: function(element) { // Remove old svg element if (element._svgIcon) { Polymer.dom(element).removeChild(element._svgIcon); element._svgIcon = null; } }, /** * * When name is changed, register iconset metadata * */ _nameChanged: function() { new Polymer.IronMeta({type: 'iconset', key: this.name, value: this}); this.async(function() { this.fire('iron-iconset-added', this, {node: window}); }); }, /** * Create a map of child SVG elements by id. * * @return {!Object} Map of id's to SVG elements. */ _createIconMap: function() { // Objects chained to Object.prototype (`{}`) have members. Specifically, // on FF there is a `watch` method that confuses the icon map, so we // need to use a null-based object here. var icons = Object.create(null); Polymer.dom(this).querySelectorAll('[id]') .forEach(function(icon) { icons[icon.id] = icon; }); return icons; }, /** * Produce installable clone of the SVG element matching `id` in this * iconset, or `undefined` if there is no matching element. * * @return {Element} Returns an installable clone of the SVG element * matching `id`. */ _cloneIcon: function(id) { // create the icon map on-demand, since the iconset itself has no discrete // signal to know when it's children are fully parsed this._icons = this._icons || this._createIconMap(); return this._prepareSvgClone(this._icons[id], this.size); }, /** * @param {Element} sourceSvg * @param {number} size * @return {Element} */ _prepareSvgClone: function(sourceSvg, size) { if (sourceSvg) { var content = sourceSvg.cloneNode(true), svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'), viewBox = content.getAttribute('viewBox') || '0 0 ' + size + ' ' + size; svg.setAttribute('viewBox', viewBox); svg.setAttribute('preserveAspectRatio', 'xMidYMid meet'); // TODO(dfreedm): `pointer-events: none` works around https://crbug.com/370136 // TODO(sjmiles): inline style may not be ideal, but avoids requiring a shadow-root svg.style.cssText = 'pointer-events: none; display: block; width: 100%; height: 100%;'; svg.appendChild(content).removeAttribute('id'); return svg; } return null; } }); </script>