|
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 | + if (rule.hasAnimations == null) { |
| 198 | + // Cache whether or not the rule has any animations to begin with: |
| 199 | + rule.hasAnimations = this.rx.ANIMATION_MATCH.test(input); |
| 200 | + } |
| 201 | + // If there are no animations referenced, we can skip transforms: |
| 202 | + if (rule.hasAnimations) { |
| 203 | + var transform; |
| 204 | + // If we haven't transformed this rule before, we iterate over all |
| 205 | + // transforms: |
| 206 | + if (rule.keyframeNamesToTransform == null) { |
| 207 | + rule.keyframeNamesToTransform = []; |
| 208 | + for (var keyframe in keyframeTransforms) { |
| 209 | + transform = keyframeTransforms[keyframe]; |
| 210 | + output = transform(input); |
| 211 | + // If the transform actually changed the CSS text, we cache the |
| 212 | + // transform name for future use: |
| 213 | + if (input !== output) { |
| 214 | + input = output; |
| 215 | + rule.keyframeNamesToTransform.push(keyframe); |
| 216 | + } |
| 217 | + } |
| 218 | + } else { |
| 219 | + // If we already have a list of keyframe names that apply to this |
| 220 | + // rule, we apply only those keyframe name transforms: |
| 221 | + for (var i = 0; i < rule.keyframeNamesToTransform.length; ++i) { |
| 222 | + transform = keyframeTransforms[rule.keyframeNamesToTransform[i]]; |
| 223 | + input = transform(input); |
| 224 | + } |
| 225 | + output = input; |
| 226 | + } |
| 227 | + } |
| 228 | + rule.cssText = output; |
| 229 | + }, |
| 230 | + |
184 | 231 | // Test if the rules in these styles matches the given `element` and if so, |
185 | 232 | // collect any custom properties into `props`. |
186 | 233 | propertyDataFromStyles: function(styles, element) { |
|
256 | 303 | hostSelector; |
257 | 304 | var hostRx = new RegExp(this.rx.HOST_PREFIX + rxHostSelector + |
258 | 305 | this.rx.HOST_SUFFIX); |
| 306 | + var keyframeTransforms = |
| 307 | + this._elementKeyframeTransforms(element, scopeSelector); |
259 | 308 | return styleTransformer.elementStyles(element, function(rule) { |
260 | 309 | self.applyProperties(rule, properties); |
261 | | - if (rule.cssText && !nativeShadow) { |
| 310 | + if (!nativeShadow && |
| 311 | + !Polymer.StyleUtil.isKeyframesSelector(rule) && |
| 312 | + rule.cssText) { |
| 313 | + // NOTE: keyframe transforms only scope munge animation names, so it |
| 314 | + // is not necessary to apply them in ShadowDOM. |
| 315 | + self.applyKeyframeTransforms(rule, keyframeTransforms); |
262 | 316 | self._scopeSelector(rule, hostRx, hostSelector, |
263 | 317 | element._scopeCssViaAttr, scopeSelector); |
264 | 318 | } |
265 | 319 | }); |
266 | 320 | }, |
267 | 321 |
|
| 322 | + _elementKeyframeTransforms: function(element, scopeSelector) { |
| 323 | + var keyframesRules = element._styles._keyframes; |
| 324 | + var keyframeTransforms = {}; |
| 325 | + if (!nativeShadow) { |
| 326 | + // For non-ShadowDOM, we transform all known keyframes rules in |
| 327 | + // advance for the current scope. This allows us to catch keyframes |
| 328 | + // rules that appear anywhere in the stylesheet: |
| 329 | + for (var i = 0, keyframesRule = keyframesRules[i]; |
| 330 | + i < keyframesRules.length; |
| 331 | + keyframesRule = keyframesRules[++i]) { |
| 332 | + this._scopeKeyframes(keyframesRule, scopeSelector); |
| 333 | + keyframeTransforms[keyframesRule.keyframesName] = |
| 334 | + this._keyframesRuleTransformer(keyframesRule); |
| 335 | + } |
| 336 | + } |
| 337 | + return keyframeTransforms; |
| 338 | + }, |
| 339 | + |
| 340 | + // Generate a factory for transforming a chunk of CSS text to handle a |
| 341 | + // particular scoped keyframes rule. |
| 342 | + _keyframesRuleTransformer: function(keyframesRule) { |
| 343 | + return function(cssText) { |
| 344 | + return cssText.replace( |
| 345 | + keyframesRule.keyframesNameRx, |
| 346 | + keyframesRule.transformedKeyframesName); |
| 347 | + }; |
| 348 | + }, |
| 349 | + |
| 350 | + // Transforms `@keyframes` names to be unique for the current host. |
| 351 | + // Example: @keyframes foo-anim -> @keyframes foo-anim-x-foo-0 |
| 352 | + _scopeKeyframes: function(rule, scopeId) { |
| 353 | + rule.keyframesNameRx = new RegExp(rule.keyframesName, 'g'); |
| 354 | + rule.transformedKeyframesName = rule.keyframesName + '-' + scopeId; |
| 355 | + rule.transformedSelector = rule.transformedSelector || rule.selector; |
| 356 | + rule.selector = rule.transformedSelector.replace( |
| 357 | + rule.keyframesName, rule.transformedKeyframesName); |
| 358 | + }, |
| 359 | + |
268 | 360 | // Strategy: x scope shim a selector e.g. to scope `.x-foo-42` (via classes): |
269 | 361 | // non-host selector: .a.x-foo -> .x-foo-42 .a.x-foo |
270 | 362 | // host selector: x-foo.wide -> x-foo.x-foo-42.wide |
|
359 | 451 | // var(--a, fallback-literal(with-one-nested-parentheses)) |
360 | 452 | VAR_MATCH: /(^|\W+)var\([\s]*([^,)]*)[\s]*,?[\s]*((?:[^,)]*)|(?:[^;]*\([^;)]*\)))[\s]*?\)/gi, |
361 | 453 | VAR_CAPTURE: /\([\s]*(--[^,\s)]*)(?:,[\s]*(--[^,\s)]*))?(?:\)|,)/gi, |
| 454 | + ANIMATION_MATCH: /(animation\s*:)|(animation-name\s*:)/, |
362 | 455 | IS_VAR: /^--/, |
363 | 456 | BRACKETED: /\{[^}]*\}/g, |
364 | 457 | HOST_PREFIX: '(?:^|[^.#[:])', |
|
0 commit comments