|
25 | 25 | // decorates styles with rule info and returns an array of used style
|
26 | 26 | // property names
|
27 | 27 | decorateStyles: function(styles) {
|
28 |
| - var self = this, props = {}; |
| 28 | + var self = this, props = {}, keyframes = []; |
29 | 29 | styleUtil.forRulesInStyles(styles, function(rule) {
|
30 | 30 | self.decorateRule(rule);
|
31 | 31 | self.collectPropertiesInCssText(rule.propertyInfo.cssText, props);
|
| 32 | + }, function onKeyframesRule(rule) { |
| 33 | + keyframes.push(rule); |
32 | 34 | });
|
| 35 | + // Cache all found keyframes rules for later reference: |
| 36 | + styles._keyframes = keyframes; |
33 | 37 | // return this list of property names *consumes* in these styles.
|
34 | 38 | var names = [];
|
35 | 39 | for (var i in props) {
|
|
87 | 91 | var parts = cssText.split(';');
|
88 | 92 | for (var i=0, p; i<parts.length; i++) {
|
89 | 93 | p = parts[i];
|
90 |
| - if (p.match(this.rx.MIXIN_MATCH) || p.match(this.rx.VAR_MATCH)) { |
| 94 | + if (p.match(this.rx.MIXIN_MATCH) || |
| 95 | + p.match(this.rx.VAR_MATCH) || |
| 96 | + this.rx.ANIMATION_MATCH.test(p) || |
| 97 | + styleUtil.isKeyframesSelector(rule)) { |
91 | 98 | customCssText += p + ';\n';
|
92 | 99 | }
|
93 | 100 | }
|
|
181 | 188 | rule.cssText = output;
|
182 | 189 | },
|
183 | 190 |
|
| 191 | + // Apply keyframe transformations to the cssText of a given rule. The |
| 192 | + // keyframeTransforms object is a map of keyframe names to transformer |
| 193 | + // functions which take in cssText and spit out transformed cssText. |
| 194 | + applyKeyframeTransforms: function(rule, keyframeTransforms) { |
| 195 | + var input = rule.cssText; |
| 196 | + var output = rule.cssText; |
| 197 | + |
| 198 | + if (rule.hasAnimations == null) { |
| 199 | + // Cache whether or not the rule has any animations to begin with: |
| 200 | + rule.hasAnimations = this.rx.ANIMATION_MATCH.test(input); |
| 201 | + } |
| 202 | + |
| 203 | + // If there are no animations referenced, we can skip transforms: |
| 204 | + if (rule.hasAnimations) { |
| 205 | + var transform; |
| 206 | + // If we haven't transformed this rule before, we iterate over all |
| 207 | + // transforms: |
| 208 | + if (rule.keyframeNamesToTransform == null) { |
| 209 | + rule.keyframeNamesToTransform = []; |
| 210 | + |
| 211 | + for (var keyframe in keyframeTransforms) { |
| 212 | + transform = keyframeTransforms[keyframe]; |
| 213 | + output = transform(input); |
| 214 | + // If the transform actually changed the CSS text, we cache the |
| 215 | + // transform name for future use: |
| 216 | + if (input !== output) { |
| 217 | + input = output; |
| 218 | + rule.keyframeNamesToTransform.push(keyframe); |
| 219 | + } |
| 220 | + } |
| 221 | + } else { |
| 222 | + // If we already have a list of keyframe names that apply to this |
| 223 | + // rule, we apply only those keyframe name transforms: |
| 224 | + for (var i = 0; i < rule.keyframeNamesToTransform.length; ++i) { |
| 225 | + transform = keyframeTransforms[rule.keyframeNamesToTransform[i]]; |
| 226 | + input = transform(input); |
| 227 | + } |
| 228 | + output = input; |
| 229 | + } |
| 230 | + } |
| 231 | + |
| 232 | + rule.cssText = output; |
| 233 | + }, |
| 234 | + |
184 | 235 | // Test if the rules in these styles matches the given `element` and if so,
|
185 | 236 | // collect any custom properties into `props`.
|
186 | 237 | propertyDataFromStyles: function(styles, element) {
|
|
256 | 307 | hostSelector;
|
257 | 308 | var hostRx = new RegExp(this.rx.HOST_PREFIX + rxHostSelector +
|
258 | 309 | this.rx.HOST_SUFFIX);
|
| 310 | + |
| 311 | + var keyframesRules = element._styles._keyframes; |
| 312 | + var keyframeTransforms = {}; |
| 313 | + |
| 314 | + if (!nativeShadow) { |
| 315 | + // For non-ShadowDOM, we transform all known keyframes rules in |
| 316 | + // advance for the current scope. This allows us to catch keyframes |
| 317 | + // rules that appear anywhere in the stylesheet: |
| 318 | + for (var i = 0, keyframesRule = keyframesRules[i]; |
| 319 | + i < keyframesRules.length; |
| 320 | + keyframesRule = keyframesRules[++i]) { |
| 321 | + this._scopeKeyframes(keyframesRule, scopeSelector); |
| 322 | + keyframeTransforms[keyframesRule.keyframesName] = |
| 323 | + this._keyframesRuleTransformer(keyframesRule); |
| 324 | + } |
| 325 | + } |
259 | 326 | return styleTransformer.elementStyles(element, function(rule) {
|
260 | 327 | self.applyProperties(rule, properties);
|
261 |
| - if (rule.cssText && !nativeShadow) { |
| 328 | + self.applyKeyframeTransforms(rule, keyframeTransforms); |
| 329 | + |
| 330 | + if (!nativeShadow && |
| 331 | + !Polymer.StyleUtil.isKeyframesSelector(rule) && |
| 332 | + rule.cssText) { |
262 | 333 | self._scopeSelector(rule, hostRx, hostSelector,
|
263 | 334 | element._scopeCssViaAttr, scopeSelector);
|
264 | 335 | }
|
265 | 336 | });
|
266 | 337 | },
|
267 | 338 |
|
| 339 | + // Generate a factory for transforming a chunk of CSS text to handle a |
| 340 | + // particular scoped keyframes rule. |
| 341 | + _keyframesRuleTransformer: function(keyframesRule) { |
| 342 | + return function(cssText) { |
| 343 | + return cssText.replace( |
| 344 | + keyframesRule.keyframesNameRx, |
| 345 | + keyframesRule.transformedKeyframesName); |
| 346 | + }; |
| 347 | + }, |
| 348 | + |
| 349 | + // Transforms `@keyframes` names to be unique for the current host. |
| 350 | + // Example: @keyframes foo-anim -> @keyframes foo-anim-x-foo-0 |
| 351 | + _scopeKeyframes: function(rule, scopeId) { |
| 352 | + rule.keyframesNameRx = new RegExp(rule.keyframesName, 'g'); |
| 353 | + rule.transformedKeyframesName = rule.keyframesName + '-' + scopeId; |
| 354 | + rule.transformedSelector = rule.transformedSelector || rule.selector; |
| 355 | + rule.selector = rule.transformedSelector.replace( |
| 356 | + rule.keyframesName, rule.transformedKeyframesName); |
| 357 | + }, |
| 358 | + |
268 | 359 | // Strategy: x scope shim a selector e.g. to scope `.x-foo-42` (via classes):
|
269 | 360 | // non-host selector: .a.x-foo -> .x-foo-42 .a.x-foo
|
270 | 361 | // host selector: x-foo.wide -> x-foo.x-foo-42.wide
|
|
359 | 450 | // var(--a, fallback-literal(with-one-nested-parentheses))
|
360 | 451 | VAR_MATCH: /(^|\W+)var\([\s]*([^,)]*)[\s]*,?[\s]*((?:[^,)]*)|(?:[^;]*\([^;)]*\)))[\s]*?\)/gi,
|
361 | 452 | VAR_CAPTURE: /\([\s]*(--[^,\s)]*)(?:,[\s]*(--[^,\s)]*))?(?:\)|,)/gi,
|
| 453 | + ANIMATION_MATCH: /(animation\s*:)|(animation-name\s*:)/, |
362 | 454 | IS_VAR: /^--/,
|
363 | 455 | BRACKETED: /\{[^}]*\}/g,
|
364 | 456 | HOST_PREFIX: '(?:^|[^.#[:])',
|
|
0 commit comments