-
Notifications
You must be signed in to change notification settings - Fork 2k
/
x-styling.html
309 lines (283 loc) · 11.8 KB
/
x-styling.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
<!--
@license
Copyright (c) 2014 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
-->
<link rel="import" href="../lib/style-properties.html">
<link rel="import" href="../lib/settings.html">
<link rel="import" href="../lib/style-defaults.html">
<link rel="import" href="../lib/style-cache.html">
<script>
(function() {
'use strict';
var serializeValueToAttribute = Polymer.Base.serializeValueToAttribute;
var propertyUtils = Polymer.StyleProperties;
var styleUtil = Polymer.StyleUtil;
var styleTransformer = Polymer.StyleTransformer;
var styleDefaults = Polymer.StyleDefaults;
var nativeShadow = Polymer.Settings.useNativeShadow;
Polymer.Base._addFeature({
// Skip applying CSS if there are some mixins or variables used
// since styles with mixins and variables will be added on later stages anyway,
// and will include styles applied here, no need to do this twice
_needsStaticStyles: function(styles) {
var needsStatic;
for (var i=0, l=styles.length, css; i < l; i++) {
css = styleUtil.parser._clean(styles[i].textContent);
css = propertyUtils.collectConsumingCssText(css);
needsStatic = needsStatic || Boolean(css);
if (css.match(propertyUtils.rx.MIXIN_MATCH) ||
css.match(propertyUtils.rx.VAR_MATCH)) {
return false;
}
}
return needsStatic;
},
_prepStyleProperties: function() {
// note: an element should produce an x-scope stylesheet
// if it has any _stylePropertyNames
this._ownStylePropertyNames = this._styles ?
propertyUtils.decorateStyles(this._styles) :
null;
},
/**
* An element's style properties can be directly modified by
* setting key-value pairs in `customStyle` on the element
* (analogous to setting `style`) and then calling `updateStyles()`.
*
*/
customStyle: null,
/**
* Returns the computed style value for the given property.
* @param {String} property
* @return {String} the computed value
*/
getComputedStyleValue: function(property) {
return this._styleProperties && this._styleProperties[property] ||
getComputedStyle(this).getPropertyValue(property);
},
// here we have an instance time spot to put custom property data
_setupStyleProperties: function() {
this.customStyle = {};
this._styleCache = null;
this._styleProperties = null;
this._scopeSelector = null;
this._ownStyleProperties = null;
this._customStyle = null;
},
_needsStyleProperties: function() {
return Boolean(this._ownStylePropertyNames &&
this._ownStylePropertyNames.length);
},
_beforeAttached: function() {
// note: do this once automatically,
// then requires calling `updateStyles`
if (!this._scopeSelector && this._needsStyleProperties()) {
this._updateStyleProperties();
}
},
_findStyleHost: function() {
var e = this, root;
while ((root = Polymer.dom(e).getOwnerRoot())) {
if (Polymer.isInstance(root.host)) {
return root.host;
}
e = root.host;
}
return styleDefaults;
},
_updateStyleProperties: function() {
var info, scope = this._findStyleHost();
// install cache in host if it doesn't exist.
if (!scope._styleCache) {
scope._styleCache = new Polymer.StyleCache();
}
var scopeData = propertyUtils
.propertyDataFromStyles(scope._styles, this);
// look in scope cache
scopeData.key.customStyle = this.customStyle;
info = scope._styleCache.retrieve(this.is, scopeData.key, this._styles);
// compute style properties (fast path, if cache hit)
var scopeCached = Boolean(info);
if (scopeCached) {
// when scope cached, we can safely take style propertis out of the
// scope cache because they are only for this scope.
this._styleProperties = info._styleProperties;
} else {
this._computeStyleProperties(scopeData.properties);
}
this._computeOwnStyleProperties();
// cache miss, do work!
if (!scopeCached) {
// and look in 2ndary global cache
info = styleCache.retrieve(this.is,
this._ownStyleProperties, this._styles);
}
var globalCached = Boolean(info) && !scopeCached;
// now we have properties and a cached style if one
// is available.
var style = this._applyStyleProperties(info);
// no cache so store in cache
//console.warn(this.is, scopeCached, globalCached, info && info._scopeSelector);
if (!scopeCached) {
// create an info object for caching
// TODO(sorvell): clone style node when using native Shadow DOM
// so a style used in a root does not itself get stored in the cache
// This can lead to incorrect sharing, but should be fixed
// in `Polymer.StyleProperties.applyElementStyle`
style = style && nativeShadow ? style.cloneNode(true) : style;
info = {
style: style,
_scopeSelector: this._scopeSelector,
_styleProperties: this._styleProperties
};
scopeData.key.customStyle = {};
this.mixin(scopeData.key.customStyle, this.customStyle);
scope._styleCache.store(this.is, info, scopeData.key, this._styles);
if (!globalCached) {
// save in global cache
styleCache.store(this.is, Object.create(info), this._ownStyleProperties,
this._styles);
}
}
},
_computeStyleProperties: function(scopeProps) {
// get scope and make sure it has properties
var scope = this._findStyleHost();
// force scope to compute properties if they don't exist or if forcing
// and it doesn't need properties
if (!scope._styleProperties) {
scope._computeStyleProperties();
}
// start with scope style properties
var props = Object.create(scope._styleProperties);
// mixin own host properties (lower specifity than scope props)
this.mixin(props, propertyUtils.hostPropertiesFromStyles(this._styles));
// mixin properties matching this element in scope
scopeProps = scopeProps ||
propertyUtils.propertyDataFromStyles(scope._styles, this).properties;
this.mixin(props, scopeProps);
// finally mixin properties inherent to this element
this.mixin(props,
propertyUtils.scopePropertiesFromStyles(this._styles));
propertyUtils.mixinCustomStyle(props, this.customStyle);
// reify properties (note: only does own properties)
propertyUtils.reify(props);
this._styleProperties = props;
},
_computeOwnStyleProperties: function() {
var props = {};
for (var i=0, n; i < this._ownStylePropertyNames.length; i++) {
n = this._ownStylePropertyNames[i];
props[n] = this._styleProperties[n];
}
this._ownStyleProperties = props;
},
_scopeCount: 0,
_applyStyleProperties: function(info) {
// update scope selector (needed for style transformation)
var oldScopeSelector = this._scopeSelector;
// note, the scope selector is incremented per class counter
this._scopeSelector = info ? info._scopeSelector :
this.is + '-' + this.__proto__._scopeCount++;
var style = propertyUtils.applyElementStyle(this,
this._styleProperties, this._scopeSelector, info && info.style);
// apply scope selector
if (!nativeShadow) {
propertyUtils.applyElementScopeSelector(this, this._scopeSelector,
oldScopeSelector, this._scopeCssViaAttr);
}
return style;
},
serializeValueToAttribute: function(value, attribute, node) {
// override to ensure whenever classes are set, we need to shim them.
node = node || this;
if (attribute === 'class' && !nativeShadow) {
// host needed to scope styling.
// Under Shady DOM, domHost is safe to use here because we know it
// is a Polymer element
var host = node === this ? (this.domHost || this.dataHost) : this;
if (host) {
value = host._scopeElementClass(node, value);
}
}
// note: using Polymer.dom here ensures that any attribute sets
// will provoke distribution if necessary; do this iff necessary
node = (this.shadyRoot && this.shadyRoot._hasDistributed) ?
Polymer.dom(node) : node;
serializeValueToAttribute.call(this, value, attribute, node);
},
_scopeElementClass: function(element, selector) {
if (!nativeShadow && !this._scopeCssViaAttr) {
selector = (selector ? selector + ' ' : '') + SCOPE_NAME + ' ' + this.is +
(element._scopeSelector ? ' ' + XSCOPE_NAME + ' ' +
element._scopeSelector : '');
}
return selector;
},
/**
* Re-evaluates and applies custom CSS properties based on dynamic
* changes to this element's scope, such as adding or removing classes
* in this element's local DOM.
*
* For performance reasons, Polymer's custom CSS property shim relies
* on this explicit signal from the user to indicate when changes have
* been made that affect the values of custom properties.
*
* @method updateStyles
* @param {Object=} properties Properties object which is mixed into
* the element's `customStyle` property. This argument provides a shortcut
* for setting `customStyle` and then calling `updateStyles`.
*/
updateStyles: function(properties) {
if (this.isAttached) {
if (properties) {
this.mixin(this.customStyle, properties);
}
// skip applying properties to self if not used
if (this._needsStyleProperties()) {
this._updateStyleProperties();
// when an element doesn't use style properties, its own properties
// should be invalidated so elements down the tree update ok.
} else {
this._styleProperties = null;
}
if (this._styleCache) {
this._styleCache.clear();
}
// always apply properties to root
this._updateRootStyles();
}
},
_updateRootStyles: function(root) {
root = root || this.root;
var c$ = Polymer.dom(root)._query(function(e) {
return e.shadyRoot || e.shadowRoot;
});
for (var i=0, l= c$.length, c; (i<l) && (c=c$[i]); i++) {
if (c.updateStyles) {
c.updateStyles();
}
}
}
});
/**
* Force all custom elements using cross scope custom properties,
* to update styling.
*/
Polymer.updateStyles = function(properties) {
// update default/custom styles
styleDefaults.updateStyles(properties);
// search the document for elements to update
Polymer.Base._updateRootStyles(document);
};
var styleCache = new Polymer.StyleCache();
Polymer.customStyleCache = styleCache;
var SCOPE_NAME = styleTransformer.SCOPE_NAME;
var XSCOPE_NAME = propertyUtils.XSCOPE_NAME;
})();
</script>