• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<!--
2@license
3Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
4This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
5The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
6The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
7Code distributed by Google as part of the polymer project is also
8subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
9-->
10
11<link rel="import" href="../polymer/polymer.html">
12<link rel="import" href="../iron-meta/iron-meta.html">
13
14<script>
15  /**
16   * The `iron-iconset-svg` element allows users to define their own icon sets
17   * that contain svg icons. The svg icon elements should be children of the
18   * `iron-iconset-svg` element. Multiple icons should be given distinct id's.
19   *
20   * Using svg elements to create icons has a few advantages over traditional
21   * bitmap graphics like jpg or png. Icons that use svg are vector based so
22   * they are resolution independent and should look good on any device. They
23   * are stylable via css. Icons can be themed, colorized, and even animated.
24   *
25   * Example:
26   *
27   *     <iron-iconset-svg name="my-svg-icons" size="24">
28   *       <svg>
29   *         <defs>
30   *           <g id="shape">
31   *             <rect x="12" y="0" width="12" height="24" />
32   *             <circle cx="12" cy="12" r="12" />
33   *           </g>
34   *         </defs>
35   *       </svg>
36   *     </iron-iconset-svg>
37   *
38   * This will automatically register the icon set "my-svg-icons" to the iconset
39   * database.  To use these icons from within another element, make a
40   * `iron-iconset` element and call the `byId` method
41   * to retrieve a given iconset. To apply a particular icon inside an
42   * element use the `applyIcon` method. For example:
43   *
44   *     iconset.applyIcon(iconNode, 'car');
45   *
46   * @element iron-iconset-svg
47   * @demo demo/index.html
48   * @implements {Polymer.Iconset}
49   */
50  Polymer({
51    is: 'iron-iconset-svg',
52
53    properties: {
54
55      /**
56       * The name of the iconset.
57       */
58      name: {
59        type: String,
60        observer: '_nameChanged'
61      },
62
63      /**
64       * The size of an individual icon. Note that icons must be square.
65       */
66      size: {
67        type: Number,
68        value: 24
69      }
70
71    },
72
73    attached: function() {
74      this.style.display = 'none';
75    },
76
77    /**
78     * Construct an array of all icon names in this iconset.
79     *
80     * @return {!Array} Array of icon names.
81     */
82    getIconNames: function() {
83      this._icons = this._createIconMap();
84      return Object.keys(this._icons).map(function(n) {
85        return this.name + ':' + n;
86      }, this);
87    },
88
89    /**
90     * Applies an icon to the given element.
91     *
92     * An svg icon is prepended to the element's shadowRoot if it exists,
93     * otherwise to the element itself.
94     *
95     * @method applyIcon
96     * @param {Element} element Element to which the icon is applied.
97     * @param {string} iconName Name of the icon to apply.
98     * @return {?Element} The svg element which renders the icon.
99     */
100    applyIcon: function(element, iconName) {
101      // insert svg element into shadow root, if it exists
102      element = element.root || element;
103      // Remove old svg element
104      this.removeIcon(element);
105      // install new svg element
106      var svg = this._cloneIcon(iconName);
107      if (svg) {
108        var pde = Polymer.dom(element);
109        pde.insertBefore(svg, pde.childNodes[0]);
110        return element._svgIcon = svg;
111      }
112      return null;
113    },
114
115    /**
116     * Remove an icon from the given element by undoing the changes effected
117     * by `applyIcon`.
118     *
119     * @param {Element} element The element from which the icon is removed.
120     */
121    removeIcon: function(element) {
122      // Remove old svg element
123      if (element._svgIcon) {
124        Polymer.dom(element).removeChild(element._svgIcon);
125        element._svgIcon = null;
126      }
127    },
128
129    /**
130     *
131     * When name is changed, register iconset metadata
132     *
133     */
134    _nameChanged: function() {
135      new Polymer.IronMeta({type: 'iconset', key: this.name, value: this});
136      this.async(function() {
137        this.fire('iron-iconset-added', this, {node: window});
138      });
139    },
140
141    /**
142     * Create a map of child SVG elements by id.
143     *
144     * @return {!Object} Map of id's to SVG elements.
145     */
146    _createIconMap: function() {
147      // Objects chained to Object.prototype (`{}`) have members. Specifically,
148      // on FF there is a `watch` method that confuses the icon map, so we
149      // need to use a null-based object here.
150      var icons = Object.create(null);
151      Polymer.dom(this).querySelectorAll('[id]')
152        .forEach(function(icon) {
153          icons[icon.id] = icon;
154        });
155      return icons;
156    },
157
158    /**
159     * Produce installable clone of the SVG element matching `id` in this
160     * iconset, or `undefined` if there is no matching element.
161     *
162     * @return {Element} Returns an installable clone of the SVG element
163     * matching `id`.
164     */
165    _cloneIcon: function(id) {
166      // create the icon map on-demand, since the iconset itself has no discrete
167      // signal to know when it's children are fully parsed
168      this._icons = this._icons || this._createIconMap();
169      return this._prepareSvgClone(this._icons[id], this.size);
170    },
171
172    /**
173     * @param {Element} sourceSvg
174     * @param {number} size
175     * @return {Element}
176     */
177    _prepareSvgClone: function(sourceSvg, size) {
178      if (sourceSvg) {
179        var content = sourceSvg.cloneNode(true),
180            svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'),
181            viewBox = content.getAttribute('viewBox') || '0 0 ' + size + ' ' + size;
182        svg.setAttribute('viewBox', viewBox);
183        svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
184        // TODO(dfreedm): `pointer-events: none` works around https://crbug.com/370136
185        // TODO(sjmiles): inline style may not be ideal, but avoids requiring a shadow-root
186        svg.style.cssText = 'pointer-events: none; display: block; width: 100%; height: 100%;';
187        svg.appendChild(content).removeAttribute('id');
188        return svg;
189      }
190      return null;
191    }
192
193  });
194</script>
195