4 window.CoreStyle = window.CoreStyle || {
10 Polymer('core-style', {
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
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`
37 refMap: CoreStyle.refMap,
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`
45 * @type object (readonly)
46 * @default {map of all `core-style` producers}
50 // if we have an id, we provide style
51 // if we have a ref, we consume/require style
56 this.registerRef(this.ref);
57 if (!window.ShadowDOMPolyfill) {
63 // can't shim until attached if using SD polyfill because need to find host
64 attached: function() {
65 if (!this.id && window.ShadowDOMPolyfill) {
70 /****** producer stuff *******/
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();
79 this.async(this._completeProvide);
83 register: function() {
84 var i = this.list[this.id];
86 if (!Array.isArray(i)) {
87 this.list[this.id] = [i];
89 this.list[this.id].push(this);
91 this.list[this.id] = this;
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();
104 provideContent: function() {
105 this.ensureTemplate();
106 this.shadowRoot.textContent = '';
107 this.shadowRoot.appendChild(this.instanceTemplate(this.template));
108 this.cssText = this.shadowRoot.textContent;
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;
119 this.template.content.appendChild(n.cloneNode(true));
126 domModified: function() {
127 this.cssText = this.shadowRoot.textContent;
131 // notify instances that reference this element
133 var s$ = this.refMap[this.id];
135 for (var i=0, s; (s=s$[i]); i++) {
141 /****** consumer stuff *******/
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);
149 applyRef: function(ref) {
151 this.registerRef(this.ref);
155 require: function() {
156 var cssText = this.cssTextForRef(this.ref);
157 //console.log('require', this.ref, cssText);
159 this.ensureStyleElement();
160 // do nothing if cssText has not changed
161 if (this.styleElement._cssText === cssText) {
164 this.styleElement._cssText = cssText;
165 if (window.ShadowDOMPolyfill) {
166 this.styleElement.textContent = cssText;
167 cssText = WebComponents.ShadowCSS.shimStyle(this.styleElement,
168 this.getScopeSelector());
170 this.styleElement.textContent = cssText;
174 cssTextForRef: function(ref) {
175 var s$ = this.byId(ref);
178 if (Array.isArray(s$)) {
180 for (var i=0, l=s$.length, s; (i<l) && (s=s$[i]); i++) {
183 cssText = p.join('\n\n');
185 cssText = s$.cssText;
188 if (s$ && !cssText) {
189 console.warn('No styles provided for ref:', ref);
195 return this.list[id];
198 ensureStyleElement: function() {
199 if (!this.styleElement) {
200 this.styleElement = window.ShadowDOMPolyfill ?
201 this.makeShimStyle() :
202 this.makeRootStyle();
204 if (!this.styleElement) {
205 console.warn(this.localName, 'could not setup style.');
209 makeRootStyle: function() {
210 var style = document.createElement('style');
211 this.appendChild(style);
215 makeShimStyle: function() {
216 var host = this.findHost(this);
218 var name = host.localName;
219 var style = document.querySelector('style[' + name + '=' + this.ref +']');
221 style = document.createElement('style');
222 style.setAttribute(name, this.ref);
223 document.head.appendChild(style);
229 getScopeSelector: function() {
230 if (!this._scopeSelector) {
231 var selector = '', host = this.findHost(this);
233 var typeExtension = host.hasAttribute('is');
234 var name = typeExtension ? host.getAttribute('is') : host.localName;
235 selector = WebComponents.ShadowCSS.makeScopeSelector(name,
238 this._scopeSelector = selector;
240 return this._scopeSelector;
243 findHost: function(node) {
244 while (node.parentNode) {
245 node = node.parentNode;
247 return node.host || wrap(document.documentElement);
251 // TODO(dfreedm): add more filters!
253 cycle: function(rgb, amount) {
254 if (rgb.match('#')) {
255 var o = this.hexToRgb(rgb);
259 rgb = 'rgb(' + o.r + ',' + o.b + ',' + o.g + ')';
262 function cycleChannel(v) {
263 return Math.abs((Number(v) - amount) % 255);
266 return rgb.replace(/rgb\(([^,]*),([^,]*),([^,]*)\)/, function(m, a, b, c) {
267 return 'rgb(' + cycleChannel(a) + ',' + cycleChannel(b) + ', '
268 + cycleChannel(c) + ')';
272 hexToRgb: function(hex) {
273 var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
275 r: parseInt(result[1], 16),
276 g: parseInt(result[2], 16),
277 b: parseInt(result[3], 16)