Add an exponential backoff to rechecking the app list doodle.
[chromium-blink-merge.git] / third_party / polymer / components-chromium / core-style / core-style-extracted.js
blob9bbe571ba26dfe8a798d2f42e9401e96353cccfb
2 (function() {
4 window.CoreStyle = window.CoreStyle || {
5   g: {},
6   list: {},
7   refMap: {}
8 };
10 Polymer('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    */
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   },
35   // static
36   g: CoreStyle.g,
37   refMap: CoreStyle.refMap,
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,
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   },
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   },
70   /****** producer stuff *******/
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   },
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   },
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   },
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   },
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   },
126   domModified: function() {
127     this.cssText = this.shadowRoot.textContent;
128     this.notify();
129   },
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   },
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);
147   },
149   applyRef: function(ref) {
150     this.ref = ref;
151     this.registerRef(this.ref);
152     this.require();
153   },
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 = WebComponents.ShadowCSS.shimStyle(this.styleElement,
168             this.getScopeSelector());
169       }
170       this.styleElement.textContent = cssText;
171     }
172   },
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   },
194   byId: function(id) {
195     return this.list[id];
196   },
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   },
209   makeRootStyle: function() {
210     var style = document.createElement('style');
211     this.appendChild(style);
212     return style;
213   },
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   },
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 = WebComponents.ShadowCSS.makeScopeSelector(name, 
236             typeExtension);
237       }
238       this._scopeSelector = selector;
239     }
240     return this._scopeSelector;
241   },
243   findHost: function(node) {
244     while (node.parentNode) {
245       node = node.parentNode;
246     }
247     return node.host || wrap(document.documentElement);
248   },
250   /* filters! */
251   // TODO(dfreedm): add more filters!
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     }
262     function cycleChannel(v) {
263       return Math.abs((Number(v) - amount) % 255);
264     }
266     return rgb.replace(/rgb\(([^,]*),([^,]*),([^,]*)\)/, function(m, a, b, c) {
267       return 'rgb(' + cycleChannel(a) + ',' + cycleChannel(b) + ', ' 
268           + cycleChannel(c) + ')';
269     });
270   },
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   }
284 })();