mirror of
https://github.com/Polymer/polymer.git
synced 2025-02-25 18:55:30 -06:00
use class attribute in applyElementScopeSelector
className can be a special object in IE Fixes #3285
This commit is contained in:
parent
9cd6b796a1
commit
07d8c0622e
@ -1,380 +1,380 @@
|
||||
<!--
|
||||
@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="style-transformer.html">
|
||||
|
||||
<script>
|
||||
|
||||
Polymer.StyleProperties = (function() {
|
||||
'use strict';
|
||||
|
||||
var nativeShadow = Polymer.Settings.useNativeShadow;
|
||||
var matchesSelector = Polymer.DomApi.matchesSelector;
|
||||
var styleUtil = Polymer.StyleUtil;
|
||||
var styleTransformer = Polymer.StyleTransformer;
|
||||
|
||||
return {
|
||||
|
||||
// decorates styles with rule info and returns an array of used style
|
||||
// property names
|
||||
decorateStyles: function(styles) {
|
||||
var self = this, props = {};
|
||||
styleUtil.forRulesInStyles(styles, function(rule) {
|
||||
self.decorateRule(rule);
|
||||
self.collectPropertiesInCssText(rule.propertyInfo.cssText, props);
|
||||
});
|
||||
// return this list of property names *consumes* in these styles.
|
||||
var names = [];
|
||||
for (var i in props) {
|
||||
names.push(i);
|
||||
}
|
||||
return names;
|
||||
},
|
||||
|
||||
// decorate a single rule with property info
|
||||
decorateRule: function(rule) {
|
||||
if (rule.propertyInfo) {
|
||||
return rule.propertyInfo;
|
||||
}
|
||||
var info = {}, properties = {};
|
||||
var hasProperties = this.collectProperties(rule, properties);
|
||||
if (hasProperties) {
|
||||
info.properties = properties;
|
||||
// TODO(sorvell): workaround parser seeing mixins as additional rules
|
||||
rule.rules = null;
|
||||
}
|
||||
info.cssText = this.collectCssText(rule);
|
||||
rule.propertyInfo = info;
|
||||
return info;
|
||||
},
|
||||
|
||||
// collects the custom properties from a rule's cssText
|
||||
collectProperties: function(rule, properties) {
|
||||
var info = rule.propertyInfo;
|
||||
if (info) {
|
||||
if (info.properties) {
|
||||
Polymer.Base.mixin(properties, info.properties);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
var m, rx = this.rx.VAR_ASSIGN;
|
||||
var cssText = rule.parsedCssText;
|
||||
var any;
|
||||
while (m = rx.exec(cssText)) {
|
||||
// note: group 2 is var, 3 is mixin
|
||||
properties[m[1]] = (m[2] || m[3]).trim();
|
||||
any = true;
|
||||
}
|
||||
return any;
|
||||
}
|
||||
},
|
||||
|
||||
// returns cssText of properties that consume variables/mixins
|
||||
collectCssText: function(rule) {
|
||||
var customCssText = '';
|
||||
var cssText = rule.parsedCssText;
|
||||
// NOTE: we support consumption inside mixin assignment
|
||||
// but not production, so strip out {...}
|
||||
cssText = cssText.replace(this.rx.BRACKETED, '')
|
||||
.replace(this.rx.VAR_ASSIGN, '');
|
||||
var parts = cssText.split(';');
|
||||
for (var i=0, p; i<parts.length; i++) {
|
||||
p = parts[i];
|
||||
customCssText += p + ';\n';
|
||||
}
|
||||
return customCssText;
|
||||
},
|
||||
|
||||
collectPropertiesInCssText: function(cssText, props) {
|
||||
var m;
|
||||
while (m = this.rx.VAR_CAPTURE.exec(cssText)) {
|
||||
props[m[1]] = true;
|
||||
var def = m[2];
|
||||
if (def && def.match(this.rx.IS_VAR)) {
|
||||
props[def] = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// turns custom properties into realized values.
|
||||
reify: function(props) {
|
||||
// big perf optimization here: reify only *own* properties
|
||||
// since this object has __proto__ of the element's scope properties
|
||||
var names = Object.getOwnPropertyNames(props);
|
||||
for (var i=0, n; i < names.length; i++) {
|
||||
n = names[i];
|
||||
props[n] = this.valueForProperty(props[n], props);
|
||||
}
|
||||
},
|
||||
|
||||
// given a property value, returns the reified value
|
||||
// a property value may be:
|
||||
// (1) a literal value like: red or 5px;
|
||||
// (2) a variable value like: var(--a), var(--a, red), or var(--a, --b);
|
||||
// (3) a literal mixin value like { properties }. Each of these properties
|
||||
// can have values that are: (a) literal, (b) variables, (c) @apply mixins.
|
||||
valueForProperty: function(property, props) {
|
||||
// case (1) default
|
||||
// case (3) defines a mixin and we have to reify the internals
|
||||
if (property) {
|
||||
if (property.indexOf(';') >=0) {
|
||||
property = this.valueForProperties(property, props);
|
||||
} else {
|
||||
// case (2) variable
|
||||
var self = this;
|
||||
var fn = function(all, prefix, value, fallback) {
|
||||
var propertyValue = (self.valueForProperty(props[value], props) ||
|
||||
(props[fallback] ?
|
||||
self.valueForProperty(props[fallback], props) :
|
||||
fallback));
|
||||
return prefix + (propertyValue || '');
|
||||
};
|
||||
property = property.replace(this.rx.VAR_MATCH, fn);
|
||||
}
|
||||
}
|
||||
return property && property.trim() || '';
|
||||
},
|
||||
|
||||
// note: we do not yet support mixin within mixin
|
||||
valueForProperties: function(property, props) {
|
||||
var parts = property.split(';');
|
||||
for (var i=0, p, m; i<parts.length; i++) {
|
||||
if (p = parts[i]) {
|
||||
m = p.match(this.rx.MIXIN_MATCH);
|
||||
if (m) {
|
||||
p = this.valueForProperty(props[m[1]], props);
|
||||
} else {
|
||||
var pp = p.split(':');
|
||||
if (pp[1]) {
|
||||
pp[1] = pp[1].trim();
|
||||
pp[1] = this.valueForProperty(pp[1], props) || pp[1];
|
||||
}
|
||||
p = pp.join(':');
|
||||
}
|
||||
parts[i] = (p && p.lastIndexOf(';') === p.length - 1) ?
|
||||
// strip trailing ;
|
||||
p.slice(0, -1) :
|
||||
p || '';
|
||||
}
|
||||
}
|
||||
return parts.join(';');
|
||||
},
|
||||
|
||||
applyProperties: function(rule, props) {
|
||||
var output = '';
|
||||
// dynamically added sheets may not be decorated so ensure they are.
|
||||
if (!rule.propertyInfo) {
|
||||
this.decorateRule(rule);
|
||||
}
|
||||
if (rule.propertyInfo.cssText) {
|
||||
output = this.valueForProperties(rule.propertyInfo.cssText, props);
|
||||
}
|
||||
rule.cssText = output;
|
||||
},
|
||||
|
||||
// Test if the rules in these styles matches the given `element` and if so,
|
||||
// collect any custom properties into `props`.
|
||||
propertyDataFromStyles: function(styles, element) {
|
||||
var props = {}, self = this;
|
||||
// generates a unique key for these matches
|
||||
var o = [], i = 0;
|
||||
styleUtil.forRulesInStyles(styles, function(rule) {
|
||||
// TODO(sorvell): we could trim the set of rules at declaration
|
||||
// time to only include ones that have properties
|
||||
if (!rule.propertyInfo) {
|
||||
self.decorateRule(rule);
|
||||
}
|
||||
// match element against transformedSelector: selector may contain
|
||||
// unwanted uniquification and parsedSelector does not directly match
|
||||
// for :host selectors.
|
||||
if (element && rule.propertyInfo.properties &&
|
||||
matchesSelector.call(element, rule.transformedSelector
|
||||
|| rule.parsedSelector)) {
|
||||
self.collectProperties(rule, props);
|
||||
// produce numeric key for these matches for lookup
|
||||
addToBitMask(i, o);
|
||||
}
|
||||
i++;
|
||||
});
|
||||
return {properties: props, key: o};
|
||||
},
|
||||
|
||||
// Test if a rule matches scope criteria (* or :root) and if so,
|
||||
// collect any custom properties into `props`.
|
||||
scopePropertiesFromStyles: function(styles) {
|
||||
if (!styles._scopeStyleProperties) {
|
||||
styles._scopeStyleProperties =
|
||||
this.selectedPropertiesFromStyles(styles, this.SCOPE_SELECTORS);
|
||||
}
|
||||
return styles._scopeStyleProperties;
|
||||
},
|
||||
|
||||
// Test if a rule matches host criteria (:host) and if so,
|
||||
// collect any custom properties into `props`.
|
||||
//
|
||||
// TODO(sorvell): this should change to collecting properties from any
|
||||
// :host(...) and then matching these against self.
|
||||
hostPropertiesFromStyles: function(styles) {
|
||||
if (!styles._hostStyleProperties) {
|
||||
styles._hostStyleProperties =
|
||||
this.selectedPropertiesFromStyles(styles, this.HOST_SELECTORS);
|
||||
}
|
||||
return styles._hostStyleProperties;
|
||||
},
|
||||
|
||||
selectedPropertiesFromStyles: function(styles, selectors) {
|
||||
var props = {}, self = this;
|
||||
styleUtil.forRulesInStyles(styles, function(rule) {
|
||||
if (!rule.propertyInfo) {
|
||||
self.decorateRule(rule);
|
||||
}
|
||||
for (var i=0; i < selectors.length; i++) {
|
||||
if (rule.parsedSelector === selectors[i]) {
|
||||
self.collectProperties(rule, props);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
return props;
|
||||
},
|
||||
|
||||
transformStyles: function(element, properties, scopeSelector) {
|
||||
var self = this;
|
||||
var hostSelector = styleTransformer
|
||||
._calcHostScope(element.is, element.extends);
|
||||
var rxHostSelector = element.extends ?
|
||||
'\\' + hostSelector.slice(0, -1) + '\\]' :
|
||||
hostSelector;
|
||||
var hostRx = new RegExp(this.rx.HOST_PREFIX + rxHostSelector +
|
||||
this.rx.HOST_SUFFIX);
|
||||
return styleTransformer.elementStyles(element, function(rule) {
|
||||
self.applyProperties(rule, properties);
|
||||
if (rule.cssText && !nativeShadow) {
|
||||
self._scopeSelector(rule, hostRx, hostSelector,
|
||||
element._scopeCssViaAttr, scopeSelector);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Strategy: x scope shim a selector e.g. to scope `.x-foo-42` (via classes):
|
||||
// non-host selector: .a.x-foo -> .x-foo-42 .a.x-foo
|
||||
// host selector: x-foo.wide -> x-foo.x-foo-42.wide
|
||||
_scopeSelector: function(rule, hostRx, hostSelector, viaAttr, scopeId) {
|
||||
rule.transformedSelector = rule.transformedSelector || rule.selector;
|
||||
var selector = rule.transformedSelector;
|
||||
var scope = viaAttr ? '[' + styleTransformer.SCOPE_NAME + '~=' +
|
||||
scopeId + ']' :
|
||||
'.' + scopeId;
|
||||
var parts = selector.split(',');
|
||||
for (var i=0, l=parts.length, p; (i<l) && (p=parts[i]); i++) {
|
||||
parts[i] = p.match(hostRx) ?
|
||||
p.replace(hostSelector, hostSelector + scope) :
|
||||
scope + ' ' + p;
|
||||
}
|
||||
rule.selector = parts.join(',');
|
||||
},
|
||||
|
||||
applyElementScopeSelector: function(element, selector, old, viaAttr) {
|
||||
var c = viaAttr ? element.getAttribute(styleTransformer.SCOPE_NAME) :
|
||||
element.className;
|
||||
var v = old ? c.replace(old, selector) :
|
||||
(c ? c + ' ' : '') + this.XSCOPE_NAME + ' ' + selector;
|
||||
if (c !== v) {
|
||||
if (viaAttr) {
|
||||
element.setAttribute(styleTransformer.SCOPE_NAME, v);
|
||||
} else {
|
||||
element.className = v;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
applyElementStyle: function(element, properties, selector, style) {
|
||||
// calculate cssText to apply
|
||||
var cssText = style ? style.textContent || '' :
|
||||
this.transformStyles(element, properties, selector);
|
||||
// if shady and we have a cached style that is not style, decrement
|
||||
var s = element._customStyle;
|
||||
if (s && !nativeShadow && (s !== style)) {
|
||||
s._useCount--;
|
||||
if (s._useCount <= 0 && s.parentNode) {
|
||||
s.parentNode.removeChild(s);
|
||||
}
|
||||
}
|
||||
// apply styling always under native or if we generated style
|
||||
// or the cached style is not in document(!)
|
||||
if (nativeShadow || (!style || !style.parentNode)) {
|
||||
// update existing style only under native
|
||||
if (nativeShadow && element._customStyle) {
|
||||
element._customStyle.textContent = cssText;
|
||||
style = element._customStyle;
|
||||
// otherwise, if we have css to apply, do so
|
||||
} else if (cssText) {
|
||||
// apply css after the scope style of the element to help with
|
||||
// style precedence rules.
|
||||
style = styleUtil.applyCss(cssText, selector,
|
||||
nativeShadow ? element.root : null, element._scopeStyle);
|
||||
}
|
||||
}
|
||||
// ensure this style is our custom style and increment its use count.
|
||||
if (style) {
|
||||
style._useCount = style._useCount || 0;
|
||||
// increment use count if we changed styles
|
||||
if (element._customStyle != style) {
|
||||
style._useCount++;
|
||||
}
|
||||
element._customStyle = style;
|
||||
}
|
||||
return style;
|
||||
},
|
||||
|
||||
// customStyle properties are applied if they are truthy or 0. Otherwise,
|
||||
// they are skipped; this allows properties previously set in customStyle
|
||||
// to be easily reset to inherited values.
|
||||
mixinCustomStyle: function(props, customStyle) {
|
||||
var v;
|
||||
for (var i in customStyle) {
|
||||
v = customStyle[i];
|
||||
if (v || v === 0) {
|
||||
props[i] = v;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
rx: {
|
||||
VAR_ASSIGN: /(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:([^;{]*)|{([^}]*)})(?:(?=[;\s}])|$)/gi,
|
||||
MIXIN_MATCH: /(?:^|\W+)@apply[\s]*\(([^)]*)\)/i,
|
||||
// note, this supports:
|
||||
// var(--a)
|
||||
// var(--a, --b)
|
||||
// var(--a, fallback-literal)
|
||||
// var(--a, fallback-literal(with-one-nested-parentheses))
|
||||
VAR_MATCH: /(^|\W+)var\([\s]*([^,)]*)[\s]*,?[\s]*((?:[^,)]*)|(?:[^;]*\([^;)]*\)))[\s]*?\)/gi,
|
||||
VAR_CAPTURE: /\([\s]*(--[^,\s)]*)(?:,[\s]*(--[^,\s)]*))?(?:\)|,)/gi,
|
||||
IS_VAR: /^--/,
|
||||
BRACKETED: /\{[^}]*\}/g,
|
||||
HOST_PREFIX: '(?:^|[^.#[:])',
|
||||
HOST_SUFFIX: '($|[.:[\\s>+~])'
|
||||
},
|
||||
|
||||
HOST_SELECTORS: [':host'],
|
||||
SCOPE_SELECTORS: [':root'],
|
||||
XSCOPE_NAME: 'x-scope'
|
||||
|
||||
};
|
||||
|
||||
function addToBitMask(n, bits) {
|
||||
var o = parseInt(n / 32);
|
||||
var v = 1 << (n % 32);
|
||||
bits[o] = (bits[o] || 0) | v;
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
</script>
|
||||
<!--
|
||||
@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="style-transformer.html">
|
||||
|
||||
<script>
|
||||
|
||||
Polymer.StyleProperties = (function() {
|
||||
'use strict';
|
||||
|
||||
var nativeShadow = Polymer.Settings.useNativeShadow;
|
||||
var matchesSelector = Polymer.DomApi.matchesSelector;
|
||||
var styleUtil = Polymer.StyleUtil;
|
||||
var styleTransformer = Polymer.StyleTransformer;
|
||||
|
||||
return {
|
||||
|
||||
// decorates styles with rule info and returns an array of used style
|
||||
// property names
|
||||
decorateStyles: function(styles) {
|
||||
var self = this, props = {};
|
||||
styleUtil.forRulesInStyles(styles, function(rule) {
|
||||
self.decorateRule(rule);
|
||||
self.collectPropertiesInCssText(rule.propertyInfo.cssText, props);
|
||||
});
|
||||
// return this list of property names *consumes* in these styles.
|
||||
var names = [];
|
||||
for (var i in props) {
|
||||
names.push(i);
|
||||
}
|
||||
return names;
|
||||
},
|
||||
|
||||
// decorate a single rule with property info
|
||||
decorateRule: function(rule) {
|
||||
if (rule.propertyInfo) {
|
||||
return rule.propertyInfo;
|
||||
}
|
||||
var info = {}, properties = {};
|
||||
var hasProperties = this.collectProperties(rule, properties);
|
||||
if (hasProperties) {
|
||||
info.properties = properties;
|
||||
// TODO(sorvell): workaround parser seeing mixins as additional rules
|
||||
rule.rules = null;
|
||||
}
|
||||
info.cssText = this.collectCssText(rule);
|
||||
rule.propertyInfo = info;
|
||||
return info;
|
||||
},
|
||||
|
||||
// collects the custom properties from a rule's cssText
|
||||
collectProperties: function(rule, properties) {
|
||||
var info = rule.propertyInfo;
|
||||
if (info) {
|
||||
if (info.properties) {
|
||||
Polymer.Base.mixin(properties, info.properties);
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
var m, rx = this.rx.VAR_ASSIGN;
|
||||
var cssText = rule.parsedCssText;
|
||||
var any;
|
||||
while (m = rx.exec(cssText)) {
|
||||
// note: group 2 is var, 3 is mixin
|
||||
properties[m[1]] = (m[2] || m[3]).trim();
|
||||
any = true;
|
||||
}
|
||||
return any;
|
||||
}
|
||||
},
|
||||
|
||||
// returns cssText of properties that consume variables/mixins
|
||||
collectCssText: function(rule) {
|
||||
var customCssText = '';
|
||||
var cssText = rule.parsedCssText;
|
||||
// NOTE: we support consumption inside mixin assignment
|
||||
// but not production, so strip out {...}
|
||||
cssText = cssText.replace(this.rx.BRACKETED, '')
|
||||
.replace(this.rx.VAR_ASSIGN, '');
|
||||
var parts = cssText.split(';');
|
||||
for (var i=0, p; i<parts.length; i++) {
|
||||
p = parts[i];
|
||||
customCssText += p + ';\n';
|
||||
}
|
||||
return customCssText;
|
||||
},
|
||||
|
||||
collectPropertiesInCssText: function(cssText, props) {
|
||||
var m;
|
||||
while (m = this.rx.VAR_CAPTURE.exec(cssText)) {
|
||||
props[m[1]] = true;
|
||||
var def = m[2];
|
||||
if (def && def.match(this.rx.IS_VAR)) {
|
||||
props[def] = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// turns custom properties into realized values.
|
||||
reify: function(props) {
|
||||
// big perf optimization here: reify only *own* properties
|
||||
// since this object has __proto__ of the element's scope properties
|
||||
var names = Object.getOwnPropertyNames(props);
|
||||
for (var i=0, n; i < names.length; i++) {
|
||||
n = names[i];
|
||||
props[n] = this.valueForProperty(props[n], props);
|
||||
}
|
||||
},
|
||||
|
||||
// given a property value, returns the reified value
|
||||
// a property value may be:
|
||||
// (1) a literal value like: red or 5px;
|
||||
// (2) a variable value like: var(--a), var(--a, red), or var(--a, --b);
|
||||
// (3) a literal mixin value like { properties }. Each of these properties
|
||||
// can have values that are: (a) literal, (b) variables, (c) @apply mixins.
|
||||
valueForProperty: function(property, props) {
|
||||
// case (1) default
|
||||
// case (3) defines a mixin and we have to reify the internals
|
||||
if (property) {
|
||||
if (property.indexOf(';') >=0) {
|
||||
property = this.valueForProperties(property, props);
|
||||
} else {
|
||||
// case (2) variable
|
||||
var self = this;
|
||||
var fn = function(all, prefix, value, fallback) {
|
||||
var propertyValue = (self.valueForProperty(props[value], props) ||
|
||||
(props[fallback] ?
|
||||
self.valueForProperty(props[fallback], props) :
|
||||
fallback));
|
||||
return prefix + (propertyValue || '');
|
||||
};
|
||||
property = property.replace(this.rx.VAR_MATCH, fn);
|
||||
}
|
||||
}
|
||||
return property && property.trim() || '';
|
||||
},
|
||||
|
||||
// note: we do not yet support mixin within mixin
|
||||
valueForProperties: function(property, props) {
|
||||
var parts = property.split(';');
|
||||
for (var i=0, p, m; i<parts.length; i++) {
|
||||
if (p = parts[i]) {
|
||||
m = p.match(this.rx.MIXIN_MATCH);
|
||||
if (m) {
|
||||
p = this.valueForProperty(props[m[1]], props);
|
||||
} else {
|
||||
var pp = p.split(':');
|
||||
if (pp[1]) {
|
||||
pp[1] = pp[1].trim();
|
||||
pp[1] = this.valueForProperty(pp[1], props) || pp[1];
|
||||
}
|
||||
p = pp.join(':');
|
||||
}
|
||||
parts[i] = (p && p.lastIndexOf(';') === p.length - 1) ?
|
||||
// strip trailing ;
|
||||
p.slice(0, -1) :
|
||||
p || '';
|
||||
}
|
||||
}
|
||||
return parts.join(';');
|
||||
},
|
||||
|
||||
applyProperties: function(rule, props) {
|
||||
var output = '';
|
||||
// dynamically added sheets may not be decorated so ensure they are.
|
||||
if (!rule.propertyInfo) {
|
||||
this.decorateRule(rule);
|
||||
}
|
||||
if (rule.propertyInfo.cssText) {
|
||||
output = this.valueForProperties(rule.propertyInfo.cssText, props);
|
||||
}
|
||||
rule.cssText = output;
|
||||
},
|
||||
|
||||
// Test if the rules in these styles matches the given `element` and if so,
|
||||
// collect any custom properties into `props`.
|
||||
propertyDataFromStyles: function(styles, element) {
|
||||
var props = {}, self = this;
|
||||
// generates a unique key for these matches
|
||||
var o = [], i = 0;
|
||||
styleUtil.forRulesInStyles(styles, function(rule) {
|
||||
// TODO(sorvell): we could trim the set of rules at declaration
|
||||
// time to only include ones that have properties
|
||||
if (!rule.propertyInfo) {
|
||||
self.decorateRule(rule);
|
||||
}
|
||||
// match element against transformedSelector: selector may contain
|
||||
// unwanted uniquification and parsedSelector does not directly match
|
||||
// for :host selectors.
|
||||
if (element && rule.propertyInfo.properties &&
|
||||
matchesSelector.call(element, rule.transformedSelector
|
||||
|| rule.parsedSelector)) {
|
||||
self.collectProperties(rule, props);
|
||||
// produce numeric key for these matches for lookup
|
||||
addToBitMask(i, o);
|
||||
}
|
||||
i++;
|
||||
});
|
||||
return {properties: props, key: o};
|
||||
},
|
||||
|
||||
// Test if a rule matches scope criteria (* or :root) and if so,
|
||||
// collect any custom properties into `props`.
|
||||
scopePropertiesFromStyles: function(styles) {
|
||||
if (!styles._scopeStyleProperties) {
|
||||
styles._scopeStyleProperties =
|
||||
this.selectedPropertiesFromStyles(styles, this.SCOPE_SELECTORS);
|
||||
}
|
||||
return styles._scopeStyleProperties;
|
||||
},
|
||||
|
||||
// Test if a rule matches host criteria (:host) and if so,
|
||||
// collect any custom properties into `props`.
|
||||
//
|
||||
// TODO(sorvell): this should change to collecting properties from any
|
||||
// :host(...) and then matching these against self.
|
||||
hostPropertiesFromStyles: function(styles) {
|
||||
if (!styles._hostStyleProperties) {
|
||||
styles._hostStyleProperties =
|
||||
this.selectedPropertiesFromStyles(styles, this.HOST_SELECTORS);
|
||||
}
|
||||
return styles._hostStyleProperties;
|
||||
},
|
||||
|
||||
selectedPropertiesFromStyles: function(styles, selectors) {
|
||||
var props = {}, self = this;
|
||||
styleUtil.forRulesInStyles(styles, function(rule) {
|
||||
if (!rule.propertyInfo) {
|
||||
self.decorateRule(rule);
|
||||
}
|
||||
for (var i=0; i < selectors.length; i++) {
|
||||
if (rule.parsedSelector === selectors[i]) {
|
||||
self.collectProperties(rule, props);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
return props;
|
||||
},
|
||||
|
||||
transformStyles: function(element, properties, scopeSelector) {
|
||||
var self = this;
|
||||
var hostSelector = styleTransformer
|
||||
._calcHostScope(element.is, element.extends);
|
||||
var rxHostSelector = element.extends ?
|
||||
'\\' + hostSelector.slice(0, -1) + '\\]' :
|
||||
hostSelector;
|
||||
var hostRx = new RegExp(this.rx.HOST_PREFIX + rxHostSelector +
|
||||
this.rx.HOST_SUFFIX);
|
||||
return styleTransformer.elementStyles(element, function(rule) {
|
||||
self.applyProperties(rule, properties);
|
||||
if (rule.cssText && !nativeShadow) {
|
||||
self._scopeSelector(rule, hostRx, hostSelector,
|
||||
element._scopeCssViaAttr, scopeSelector);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Strategy: x scope shim a selector e.g. to scope `.x-foo-42` (via classes):
|
||||
// non-host selector: .a.x-foo -> .x-foo-42 .a.x-foo
|
||||
// host selector: x-foo.wide -> x-foo.x-foo-42.wide
|
||||
_scopeSelector: function(rule, hostRx, hostSelector, viaAttr, scopeId) {
|
||||
rule.transformedSelector = rule.transformedSelector || rule.selector;
|
||||
var selector = rule.transformedSelector;
|
||||
var scope = viaAttr ? '[' + styleTransformer.SCOPE_NAME + '~=' +
|
||||
scopeId + ']' :
|
||||
'.' + scopeId;
|
||||
var parts = selector.split(',');
|
||||
for (var i=0, l=parts.length, p; (i<l) && (p=parts[i]); i++) {
|
||||
parts[i] = p.match(hostRx) ?
|
||||
p.replace(hostSelector, hostSelector + scope) :
|
||||
scope + ' ' + p;
|
||||
}
|
||||
rule.selector = parts.join(',');
|
||||
},
|
||||
|
||||
applyElementScopeSelector: function(element, selector, old, viaAttr) {
|
||||
var c = viaAttr ? element.getAttribute(styleTransformer.SCOPE_NAME) :
|
||||
(element.getAttribute('class') || '');
|
||||
var v = old ? c.replace(old, selector) :
|
||||
(c ? c + ' ' : '') + this.XSCOPE_NAME + ' ' + selector;
|
||||
if (c !== v) {
|
||||
if (viaAttr) {
|
||||
element.setAttribute(styleTransformer.SCOPE_NAME, v);
|
||||
} else {
|
||||
element.setAttribute('class', v);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
applyElementStyle: function(element, properties, selector, style) {
|
||||
// calculate cssText to apply
|
||||
var cssText = style ? style.textContent || '' :
|
||||
this.transformStyles(element, properties, selector);
|
||||
// if shady and we have a cached style that is not style, decrement
|
||||
var s = element._customStyle;
|
||||
if (s && !nativeShadow && (s !== style)) {
|
||||
s._useCount--;
|
||||
if (s._useCount <= 0 && s.parentNode) {
|
||||
s.parentNode.removeChild(s);
|
||||
}
|
||||
}
|
||||
// apply styling always under native or if we generated style
|
||||
// or the cached style is not in document(!)
|
||||
if (nativeShadow || (!style || !style.parentNode)) {
|
||||
// update existing style only under native
|
||||
if (nativeShadow && element._customStyle) {
|
||||
element._customStyle.textContent = cssText;
|
||||
style = element._customStyle;
|
||||
// otherwise, if we have css to apply, do so
|
||||
} else if (cssText) {
|
||||
// apply css after the scope style of the element to help with
|
||||
// style precedence rules.
|
||||
style = styleUtil.applyCss(cssText, selector,
|
||||
nativeShadow ? element.root : null, element._scopeStyle);
|
||||
}
|
||||
}
|
||||
// ensure this style is our custom style and increment its use count.
|
||||
if (style) {
|
||||
style._useCount = style._useCount || 0;
|
||||
// increment use count if we changed styles
|
||||
if (element._customStyle != style) {
|
||||
style._useCount++;
|
||||
}
|
||||
element._customStyle = style;
|
||||
}
|
||||
return style;
|
||||
},
|
||||
|
||||
// customStyle properties are applied if they are truthy or 0. Otherwise,
|
||||
// they are skipped; this allows properties previously set in customStyle
|
||||
// to be easily reset to inherited values.
|
||||
mixinCustomStyle: function(props, customStyle) {
|
||||
var v;
|
||||
for (var i in customStyle) {
|
||||
v = customStyle[i];
|
||||
if (v || v === 0) {
|
||||
props[i] = v;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
rx: {
|
||||
VAR_ASSIGN: /(?:^|[;\s{]\s*)(--[\w-]*?)\s*:\s*(?:([^;{]*)|{([^}]*)})(?:(?=[;\s}])|$)/gi,
|
||||
MIXIN_MATCH: /(?:^|\W+)@apply[\s]*\(([^)]*)\)/i,
|
||||
// note, this supports:
|
||||
// var(--a)
|
||||
// var(--a, --b)
|
||||
// var(--a, fallback-literal)
|
||||
// var(--a, fallback-literal(with-one-nested-parentheses))
|
||||
VAR_MATCH: /(^|\W+)var\([\s]*([^,)]*)[\s]*,?[\s]*((?:[^,)]*)|(?:[^;]*\([^;)]*\)))[\s]*?\)/gi,
|
||||
VAR_CAPTURE: /\([\s]*(--[^,\s)]*)(?:,[\s]*(--[^,\s)]*))?(?:\)|,)/gi,
|
||||
IS_VAR: /^--/,
|
||||
BRACKETED: /\{[^}]*\}/g,
|
||||
HOST_PREFIX: '(?:^|[^.#[:])',
|
||||
HOST_SUFFIX: '($|[.:[\\s>+~])'
|
||||
},
|
||||
|
||||
HOST_SELECTORS: [':host'],
|
||||
SCOPE_SELECTORS: [':root'],
|
||||
XSCOPE_NAME: 'x-scope'
|
||||
|
||||
};
|
||||
|
||||
function addToBitMask(n, bits) {
|
||||
var o = parseInt(n / 32);
|
||||
var v = 1 << (n % 32);
|
||||
bits[o] = (bits[o] || 0) | v;
|
||||
}
|
||||
|
||||
})();
|
||||
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user