OLD | NEW |
| (Empty) |
1 <!-- | |
2 Copyright (c) 2014 The Polymer Project Authors. All rights reserved. | |
3 This code may only be used under the BSD style license found at http://polymer.g
ithub.io/LICENSE.txt | |
4 The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt | |
5 The complete set of contributors may be found at http://polymer.github.io/CONTRI
BUTORS.txt | |
6 Code distributed by Google as part of the polymer project is also | |
7 subject to an additional IP rights grant found at http://polymer.github.io/PATEN
TS.txt | |
8 --> | |
9 <!-- | |
10 | |
11 The `core-style` element helps manage styling inside other elements and can | |
12 be used to make themes. The `core-style` element can be either a producer | |
13 or consumer of styling. If it has its `id` property set, it's a producer. | |
14 Elements that are producers should include css styling as their text content. | |
15 If a `core-style` has its `ref` property set, it's a consumer. A `core-style` | |
16 typically sets its `ref` property to the value of the `id` property of the | |
17 `core-style` it wants to use. This allows a single producer to be used in | |
18 multiple places, for example, in many different elements. | |
19 | |
20 It's common to place `core-style` producer elements inside HTMLImports. | |
21 Remote stylesheets should be included this way, the @import css mechanism is | |
22 not currently supported. | |
23 | |
24 Here's a basic example: | |
25 | |
26 <polymer-element name="x-test" noscript> | |
27 <template> | |
28 <core-style ref="x-test"></core-style> | |
29 <content></content> | |
30 </template> | |
31 </polymer-element> | |
32 | |
33 The `x-test` element above will be styled by any `core-style` elements that have | |
34 `id` set to `x-test`. These `core-style` producers are separate from the element | |
35 definition, allowing a user of the element to style it independent of the author
's | |
36 styling. For example: | |
37 | |
38 <core-style id="x-test"> | |
39 :host { | |
40 backgound-color: steelblue; | |
41 } | |
42 </core-style> | |
43 | |
44 The content of the `x-test` `core-style` producer gets included inside the | |
45 shadowRoot of the `x-test` element. If the content of the `x-test` producer | |
46 `core-style` changes, all consumers of it are automatically kept in sync. This | |
47 allows updating styling on the fly. | |
48 | |
49 The `core-style` element also supports bindings and it is the producer | |
50 `core-style` element is the model for its content. Here's an example: | |
51 | |
52 <core-style id="x-test"> | |
53 :host { | |
54 background-color: {{myColor}}; | |
55 } | |
56 </core-style> | |
57 <script> | |
58 document._currentScript.ownerDocument.getElementById('x-test').myColor = '
orange'; | |
59 </script> | |
60 | |
61 Finally, to facilitate sharing data between `core-style` elements, all | |
62 `core-style` elements have a `g` property which is set to the global | |
63 `CoreStyle.g`. Here's an example: | |
64 | |
65 <core-style id="x-test"> | |
66 :host { | |
67 background-color: {{g.myColor}}; | |
68 } | |
69 </core-style> | |
70 <script> | |
71 CoreStyle.g.myColor = 'tomato'; | |
72 </script> | |
73 | |
74 Finally, one `core-style` can be nested inside another. The `core-style` | |
75 element has a `list` property which is a map of all the `core-style` producers. | |
76 A `core-style` producer's content is available via its `cssText` property. | |
77 Putting this together: | |
78 | |
79 <core-style id="common"> | |
80 :host { | |
81 font-family: sans-serif; | |
82 } | |
83 </core-style> | |
84 | |
85 <core-style id="x-test"> | |
86 {{list.common.cssText}} | |
87 | |
88 :host { | |
89 background-color: {{g.myColor}}; | |
90 } | |
91 </core-style> | |
92 | |
93 | |
94 @group Polymer Core Elements | |
95 @element core-style | |
96 @homepage github.io | |
97 --> | |
98 | |
99 <link rel="import" href="../polymer/polymer.html"> | |
100 | |
101 <polymer-element name="core-style" hidden> | |
102 <script> | |
103 (function() { | |
104 | |
105 window.CoreStyle = window.CoreStyle || { | |
106 g: {}, | |
107 list: {}, | |
108 refMap: {} | |
109 }; | |
110 | |
111 Polymer('core-style', { | |
112 /** | |
113 * The `id` property should be set if the `core-style` is a producer | |
114 * of styles. In this case, the `core-style` should have text content | |
115 * that is cssText. | |
116 * | |
117 * @attribute id | |
118 * @type string | |
119 * @default '' | |
120 */ | |
121 | |
122 | |
123 publish: { | |
124 /** | |
125 * The `ref` property should be set if the `core-style` element is a | |
126 * consumer of styles. Set it to the `id` of the desired `core-style` | |
127 * element. | |
128 * | |
129 * @attribute ref | |
130 * @type string | |
131 * @default '' | |
132 */ | |
133 ref: '' | |
134 }, | |
135 | |
136 // static | |
137 g: CoreStyle.g, | |
138 refMap: CoreStyle.refMap, | |
139 | |
140 /** | |
141 * The `list` is a map of all `core-style` producers stored by `id`. It | |
142 * should be considered readonly. It's useful for nesting one `core-style` | |
143 * inside another. | |
144 * | |
145 * @attribute list | |
146 * @type object (readonly) | |
147 * @default {map of all `core-style` producers} | |
148 */ | |
149 list: CoreStyle.list, | |
150 | |
151 // if we have an id, we provide style | |
152 // if we have a ref, we consume/require style | |
153 ready: function() { | |
154 if (this.id) { | |
155 this.provide(); | |
156 } else { | |
157 this.registerRef(this.ref); | |
158 if (!window.ShadowDOMPolyfill) { | |
159 this.require(); | |
160 } | |
161 } | |
162 }, | |
163 | |
164 // can't shim until attached if using SD polyfill because need to find host | |
165 attached: function() { | |
166 if (!this.id && window.ShadowDOMPolyfill) { | |
167 this.require(); | |
168 } | |
169 }, | |
170 | |
171 /****** producer stuff *******/ | |
172 | |
173 provide: function() { | |
174 this.register(); | |
175 // we want to do this asap, especially so we can do so before definitions | |
176 // that use this core-style are registered. | |
177 if (this.textContent) { | |
178 this._completeProvide(); | |
179 } else { | |
180 this.async(this._completeProvide); | |
181 } | |
182 }, | |
183 | |
184 register: function() { | |
185 var i = this.list[this.id]; | |
186 if (i) { | |
187 if (!Array.isArray(i)) { | |
188 this.list[this.id] = [i]; | |
189 } | |
190 this.list[this.id].push(this); | |
191 } else { | |
192 this.list[this.id] = this; | |
193 } | |
194 }, | |
195 | |
196 // stamp into a shadowRoot so we can monitor dom of the bound output | |
197 _completeProvide: function() { | |
198 this.createShadowRoot(); | |
199 this.domObserver = new MutationObserver(this.domModified.bind(this)) | |
200 .observe(this.shadowRoot, {subtree: true, | |
201 characterData: true, childList: true}); | |
202 this.provideContent(); | |
203 }, | |
204 | |
205 provideContent: function() { | |
206 this.ensureTemplate(); | |
207 this.shadowRoot.textContent = ''; | |
208 this.shadowRoot.appendChild(this.instanceTemplate(this.template)); | |
209 this.cssText = this.shadowRoot.textContent; | |
210 }, | |
211 | |
212 ensureTemplate: function() { | |
213 if (!this.template) { | |
214 this.template = this.querySelector('template:not([repeat]):not([bind])'); | |
215 // move content into the template | |
216 if (!this.template) { | |
217 this.template = document.createElement('template'); | |
218 var n = this.firstChild; | |
219 while (n) { | |
220 this.template.content.appendChild(n.cloneNode(true)); | |
221 n = n.nextSibling; | |
222 } | |
223 } | |
224 } | |
225 }, | |
226 | |
227 domModified: function() { | |
228 this.cssText = this.shadowRoot.textContent; | |
229 this.notify(); | |
230 }, | |
231 | |
232 // notify instances that reference this element | |
233 notify: function() { | |
234 var s$ = this.refMap[this.id]; | |
235 if (s$) { | |
236 for (var i=0, s; (s=s$[i]); i++) { | |
237 s.require(); | |
238 } | |
239 } | |
240 }, | |
241 | |
242 /****** consumer stuff *******/ | |
243 | |
244 registerRef: function(ref) { | |
245 //console.log('register', ref); | |
246 this.refMap[this.ref] = this.refMap[this.ref] || []; | |
247 this.refMap[this.ref].push(this); | |
248 }, | |
249 | |
250 applyRef: function(ref) { | |
251 this.ref = ref; | |
252 this.registerRef(this.ref); | |
253 this.require(); | |
254 }, | |
255 | |
256 require: function() { | |
257 var cssText = this.cssTextForRef(this.ref); | |
258 //console.log('require', this.ref, cssText); | |
259 if (cssText) { | |
260 this.ensureStyleElement(); | |
261 // do nothing if cssText has not changed | |
262 if (this.styleElement._cssText === cssText) { | |
263 return; | |
264 } | |
265 this.styleElement._cssText = cssText; | |
266 if (window.ShadowDOMPolyfill) { | |
267 this.styleElement.textContent = cssText; | |
268 cssText = Platform.ShadowCSS.shimStyle(this.styleElement, | |
269 this.getScopeSelector()); | |
270 } | |
271 this.styleElement.textContent = cssText; | |
272 } | |
273 }, | |
274 | |
275 cssTextForRef: function(ref) { | |
276 var s$ = this.byId(ref); | |
277 var cssText = ''; | |
278 if (s$) { | |
279 if (Array.isArray(s$)) { | |
280 var p = []; | |
281 for (var i=0, l=s$.length, s; (i<l) && (s=s$[i]); i++) { | |
282 p.push(s.cssText); | |
283 } | |
284 cssText = p.join('\n\n'); | |
285 } else { | |
286 cssText = s$.cssText; | |
287 } | |
288 } | |
289 if (s$ && !cssText) { | |
290 console.warn('No styles provided for ref:', ref); | |
291 } | |
292 return cssText; | |
293 }, | |
294 | |
295 byId: function(id) { | |
296 return this.list[id]; | |
297 }, | |
298 | |
299 ensureStyleElement: function() { | |
300 if (!this.styleElement) { | |
301 this.styleElement = window.ShadowDOMPolyfill ? | |
302 this.makeShimStyle() : | |
303 this.makeRootStyle(); | |
304 } | |
305 if (!this.styleElement) { | |
306 console.warn(this.localName, 'could not setup style.'); | |
307 } | |
308 }, | |
309 | |
310 makeRootStyle: function() { | |
311 var style = document.createElement('style'); | |
312 this.appendChild(style); | |
313 return style; | |
314 }, | |
315 | |
316 makeShimStyle: function() { | |
317 var host = this.findHost(this); | |
318 if (host) { | |
319 var name = host.localName; | |
320 var style = document.querySelector('style[' + name + '=' + this.ref +']'); | |
321 if (!style) { | |
322 style = document.createElement('style'); | |
323 style.setAttribute(name, this.ref); | |
324 document.head.appendChild(style); | |
325 } | |
326 return style; | |
327 } | |
328 }, | |
329 | |
330 getScopeSelector: function() { | |
331 if (!this._scopeSelector) { | |
332 var selector = '', host = this.findHost(this); | |
333 if (host) { | |
334 var typeExtension = host.hasAttribute('is'); | |
335 var name = typeExtension ? host.getAttribute('is') : host.localName; | |
336 selector = Platform.ShadowCSS.makeScopeSelector(name, | |
337 typeExtension); | |
338 } | |
339 this._scopeSelector = selector; | |
340 } | |
341 return this._scopeSelector; | |
342 }, | |
343 | |
344 findHost: function(node) { | |
345 while (node.parentNode) { | |
346 node = node.parentNode; | |
347 } | |
348 return node.host || wrap(document.documentElement); | |
349 }, | |
350 | |
351 /* filters! */ | |
352 // TODO(dfreedm): add more filters! | |
353 | |
354 cycle: function(rgb, amount) { | |
355 if (rgb.match('#')) { | |
356 var o = this.hexToRgb(rgb); | |
357 if (!o) { | |
358 return rgb; | |
359 } | |
360 rgb = 'rgb(' + o.r + ',' + o.b + ',' + o.g + ')'; | |
361 } | |
362 | |
363 function cycleChannel(v) { | |
364 return Math.abs((Number(v) - amount) % 255); | |
365 } | |
366 | |
367 return rgb.replace(/rgb\(([^,]*),([^,]*),([^,]*)\)/, function(m, a, b, c) { | |
368 return 'rgb(' + cycleChannel(a) + ',' + cycleChannel(b) + ', ' | |
369 + cycleChannel(c) + ')'; | |
370 }); | |
371 }, | |
372 | |
373 hexToRgb: function(hex) { | |
374 var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); | |
375 return result ? { | |
376 r: parseInt(result[1], 16), | |
377 g: parseInt(result[2], 16), | |
378 b: parseInt(result[3], 16) | |
379 } : null; | |
380 } | |
381 | |
382 }); | |
383 | |
384 | |
385 })(); | |
386 </script> | |
387 </polymer-element> | |
OLD | NEW |