| 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 = WebComponents.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 = WebComponents.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 |