• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1
2(function() {
3
4window.CoreStyle = window.CoreStyle || {
5  g: {},
6  list: {},
7  refMap: {}
8};
9
10Polymer('core-style', {
11  /**
12   * The `id` property should be set if the `core-style` is a producer
13   * of styles. In this case, the `core-style` should have text content
14   * that is cssText.
15   *
16   * @attribute id
17   * @type string
18   * @default ''
19   */
20
21
22  publish: {
23    /**
24     * The `ref` property should be set if the `core-style` element is a
25     * consumer of styles. Set it to the `id` of the desired `core-style`
26     * element.
27     *
28     * @attribute ref
29     * @type string
30     * @default ''
31     */
32    ref: ''
33  },
34
35  // static
36  g: CoreStyle.g,
37  refMap: CoreStyle.refMap,
38
39  /**
40   * The `list` is a map of all `core-style` producers stored by `id`. It
41   * should be considered readonly. It's useful for nesting one `core-style`
42   * inside another.
43   *
44   * @attribute list
45   * @type object (readonly)
46   * @default {map of all `core-style` producers}
47   */
48  list: CoreStyle.list,
49
50  // if we have an id, we provide style
51  // if we have a ref, we consume/require style
52  ready: function() {
53    if (this.id) {
54      this.provide();
55    } else {
56      this.registerRef(this.ref);
57      if (!window.ShadowDOMPolyfill) {
58        this.require();
59      }
60    }
61  },
62
63  // can't shim until attached if using SD polyfill because need to find host
64  attached: function() {
65    if (!this.id && window.ShadowDOMPolyfill) {
66      this.require();
67    }
68  },
69
70  /****** producer stuff *******/
71
72  provide: function() {
73    this.register();
74    // we want to do this asap, especially so we can do so before definitions
75    // that use this core-style are registered.
76    if (this.textContent) {
77      this._completeProvide();
78    } else {
79      this.async(this._completeProvide);
80    }
81  },
82
83  register: function() {
84    var i = this.list[this.id];
85    if (i) {
86      if (!Array.isArray(i)) {
87        this.list[this.id] = [i];
88      }
89      this.list[this.id].push(this);
90    } else {
91      this.list[this.id] = this;
92    }
93  },
94
95  // stamp into a shadowRoot so we can monitor dom of the bound output
96  _completeProvide: function() {
97    this.createShadowRoot();
98    this.domObserver = new MutationObserver(this.domModified.bind(this))
99        .observe(this.shadowRoot, {subtree: true,
100        characterData: true, childList: true});
101    this.provideContent();
102  },
103
104  provideContent: function() {
105    this.ensureTemplate();
106    this.shadowRoot.textContent = '';
107    this.shadowRoot.appendChild(this.instanceTemplate(this.template));
108    this.cssText = this.shadowRoot.textContent;
109  },
110
111  ensureTemplate: function() {
112    if (!this.template) {
113      this.template = this.querySelector('template:not([repeat]):not([bind])');
114      // move content into the template
115      if (!this.template) {
116        this.template = document.createElement('template');
117        var n = this.firstChild;
118        while (n) {
119          this.template.content.appendChild(n.cloneNode(true));
120          n = n.nextSibling;
121        }
122      }
123    }
124  },
125
126  domModified: function() {
127    this.cssText = this.shadowRoot.textContent;
128    this.notify();
129  },
130
131  // notify instances that reference this element
132  notify: function() {
133    var s$ = this.refMap[this.id];
134    if (s$) {
135      for (var i=0, s; (s=s$[i]); i++) {
136        s.require();
137      }
138    }
139  },
140
141  /****** consumer stuff *******/
142
143  registerRef: function(ref) {
144    //console.log('register', ref);
145    this.refMap[this.ref] = this.refMap[this.ref] || [];
146    this.refMap[this.ref].push(this);
147  },
148
149  applyRef: function(ref) {
150    this.ref = ref;
151    this.registerRef(this.ref);
152    this.require();
153  },
154
155  require: function() {
156    var cssText = this.cssTextForRef(this.ref);
157    //console.log('require', this.ref, cssText);
158    if (cssText) {
159      this.ensureStyleElement();
160      // do nothing if cssText has not changed
161      if (this.styleElement._cssText === cssText) {
162        return;
163      }
164      this.styleElement._cssText = cssText;
165      if (window.ShadowDOMPolyfill) {
166        this.styleElement.textContent = cssText;
167        cssText = Platform.ShadowCSS.shimStyle(this.styleElement,
168            this.getScopeSelector());
169      }
170      this.styleElement.textContent = cssText;
171    }
172  },
173
174  cssTextForRef: function(ref) {
175    var s$ = this.byId(ref);
176    var cssText = '';
177    if (s$) {
178      if (Array.isArray(s$)) {
179        var p = [];
180        for (var i=0, l=s$.length, s; (i<l) && (s=s$[i]); i++) {
181          p.push(s.cssText);
182        }
183        cssText = p.join('\n\n');
184      } else {
185        cssText = s$.cssText;
186      }
187    }
188    if (s$ && !cssText) {
189      console.warn('No styles provided for ref:', ref);
190    }
191    return cssText;
192  },
193
194  byId: function(id) {
195    return this.list[id];
196  },
197
198  ensureStyleElement: function() {
199    if (!this.styleElement) {
200      this.styleElement = window.ShadowDOMPolyfill ?
201          this.makeShimStyle() :
202          this.makeRootStyle();
203    }
204    if (!this.styleElement) {
205      console.warn(this.localName, 'could not setup style.');
206    }
207  },
208
209  makeRootStyle: function() {
210    var style = document.createElement('style');
211    this.appendChild(style);
212    return style;
213  },
214
215  makeShimStyle: function() {
216    var host = this.findHost(this);
217    if (host) {
218      var name = host.localName;
219      var style = document.querySelector('style[' + name + '=' + this.ref +']');
220      if (!style) {
221        style = document.createElement('style');
222        style.setAttribute(name, this.ref);
223        document.head.appendChild(style);
224      }
225      return style;
226    }
227  },
228
229  getScopeSelector: function() {
230    if (!this._scopeSelector) {
231      var selector = '', host = this.findHost(this);
232      if (host) {
233        var typeExtension = host.hasAttribute('is');
234        var name = typeExtension ? host.getAttribute('is') : host.localName;
235        selector = Platform.ShadowCSS.makeScopeSelector(name,
236            typeExtension);
237      }
238      this._scopeSelector = selector;
239    }
240    return this._scopeSelector;
241  },
242
243  findHost: function(node) {
244    while (node.parentNode) {
245      node = node.parentNode;
246    }
247    return node.host || wrap(document.documentElement);
248  },
249
250  /* filters! */
251  // TODO(dfreedm): add more filters!
252
253  cycle: function(rgb, amount) {
254    if (rgb.match('#')) {
255      var o = this.hexToRgb(rgb);
256      if (!o) {
257        return rgb;
258      }
259      rgb = 'rgb(' + o.r + ',' + o.b + ',' + o.g + ')';
260    }
261
262    function cycleChannel(v) {
263      return Math.abs((Number(v) - amount) % 255);
264    }
265
266    return rgb.replace(/rgb\(([^,]*),([^,]*),([^,]*)\)/, function(m, a, b, c) {
267      return 'rgb(' + cycleChannel(a) + ',' + cycleChannel(b) + ', '
268          + cycleChannel(c) + ')';
269    });
270  },
271
272  hexToRgb: function(hex) {
273    var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
274    return result ? {
275        r: parseInt(result[1], 16),
276        g: parseInt(result[2], 16),
277        b: parseInt(result[3], 16)
278    } : null;
279  }
280
281});
282
283
284})();
285