OLD | NEW |
(Empty) | |
| 1 |
| 2 (function() { |
| 3 |
| 4 window.CoreStyle = window.CoreStyle || { |
| 5 g: {}, |
| 6 list: {}, |
| 7 refMap: {} |
| 8 }; |
| 9 |
| 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 */ |
| 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 })(); |
OLD | NEW |