Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(9)

Unified Diff: appengine/config_service/ui/bower_components/shadycss/src/style-properties.js

Issue 2923973003: Added base template for config ui. (Closed)
Patch Set: Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: appengine/config_service/ui/bower_components/shadycss/src/style-properties.js
diff --git a/appengine/config_service/ui/bower_components/shadycss/src/style-properties.js b/appengine/config_service/ui/bower_components/shadycss/src/style-properties.js
new file mode 100644
index 0000000000000000000000000000000000000000..7f72bda794afcd5bf34db6630787a1b6a0744c46
--- /dev/null
+++ b/appengine/config_service/ui/bower_components/shadycss/src/style-properties.js
@@ -0,0 +1,604 @@
+/**
+@license
+Copyright (c) 2017 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
+*/
+
+'use strict';
+
+import {removeCustomPropAssignment, StyleNode} from './css-parse.js' // eslint-disable-line no-unused-vars
+import {nativeShadow} from './style-settings.js'
+import StyleTransformer from './style-transformer.js'
+import * as StyleUtil from './style-util.js'
+import * as RX from './common-regex.js'
+import StyleInfo from './style-info.js'
+
+// TODO: dedupe with shady
+/**
+ * @const {function(string):boolean}
+ */
+const matchesSelector = ((p) => p.matches || p.matchesSelector ||
+ p.mozMatchesSelector || p.msMatchesSelector ||
+p.oMatchesSelector || p.webkitMatchesSelector)(window.Element.prototype);
+
+const IS_IE = navigator.userAgent.match('Trident');
+
+const XSCOPE_NAME = 'x-scope';
+
+class StyleProperties {
+ get XSCOPE_NAME() {
+ return XSCOPE_NAME;
+ }
+/**
+ * decorates styles with rule info and returns an array of used style property names
+ *
+ * @param {StyleNode} rules
+ * @return {Array<string>}
+ */
+ decorateStyles(rules) {
+ let self = this, props = {}, keyframes = [], ruleIndex = 0;
+ StyleUtil.forEachRule(rules, function(rule) {
+ self.decorateRule(rule);
+ // mark in-order position of ast rule in styles block, used for cache key
+ rule.index = ruleIndex++;
+ self.collectPropertiesInCssText(rule.propertyInfo.cssText, props);
+ }, function onKeyframesRule(rule) {
+ keyframes.push(rule);
+ });
+ // Cache all found keyframes rules for later reference:
+ rules._keyframes = keyframes;
+ // return this list of property names *consumes* in these styles.
+ let names = [];
+ for (let i in props) {
+ names.push(i);
+ }
+ return names;
+ }
+
+ // decorate a single rule with property info
+ decorateRule(rule) {
+ if (rule.propertyInfo) {
+ return rule.propertyInfo;
+ }
+ let info = {}, properties = {};
+ let 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(rule, properties) {
+ let info = rule.propertyInfo;
+ if (info) {
+ if (info.properties) {
+ Object.assign(properties, info.properties);
+ return true;
+ }
+ } else {
+ let m, rx = RX.VAR_ASSIGN;
+ let cssText = rule['parsedCssText'];
+ let value;
+ let any;
+ while ((m = rx.exec(cssText))) {
+ // note: group 2 is var, 3 is mixin
+ value = (m[2] || m[3]).trim();
+ // value of 'inherit' or 'unset' is equivalent to not setting the property here
+ if (value !== 'inherit' || value !== 'unset') {
+ properties[m[1].trim()] = value;
+ }
+ any = true;
+ }
+ return any;
+ }
+
+ }
+
+ // returns cssText of properties that consume variables/mixins
+ collectCssText(rule) {
+ return this.collectConsumingCssText(rule['parsedCssText']);
+ }
+
+ // NOTE: we support consumption inside mixin assignment
+ // but not production, so strip out {...}
+ collectConsumingCssText(cssText) {
+ return cssText.replace(RX.BRACKETED, '')
+ .replace(RX.VAR_ASSIGN, '');
+ }
+
+ collectPropertiesInCssText(cssText, props) {
+ let m;
+ while ((m = RX.VAR_CONSUMED.exec(cssText))) {
+ let name = m[1];
+ // This regex catches all variable names, and following non-whitespace char
+ // If next char is not ':', then variable is a consumer
+ if (m[2] !== ':') {
+ props[name] = true;
+ }
+ }
+ }
+
+ // turns custom properties into realized values.
+ reify(props) {
+ // big perf optimization here: reify only *own* properties
+ // since this object has __proto__ of the element's scope properties
+ let names = Object.getOwnPropertyNames(props);
+ for (let 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) or
+ // var(--a, var(--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(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
+ let self = this;
+ let fn = function(prefix, value, fallback, suffix) {
+ if (!value) {
+ return prefix + suffix;
+ }
+ let propertyValue = self.valueForProperty(props[value], props);
+ // if value is "initial", then the variable should be treated as unset
+ if (!propertyValue || propertyValue === 'initial') {
+ // fallback may be --a or var(--a) or literal
+ propertyValue = self.valueForProperty(props[fallback] || fallback, props) ||
+ fallback;
+ } else if (propertyValue === 'apply-shim-inherit') {
+ // CSS build will replace `inherit` with `apply-shim-inherit`
+ // for use with native css variables.
+ // Since we have full control, we can use `inherit` directly.
+ propertyValue = 'inherit';
+ }
+ return prefix + (propertyValue || '') + suffix;
+ };
+ property = StyleUtil.processVariableAndFallback(property, fn);
+ }
+ }
+ return property && property.trim() || '';
+ }
+
+ // note: we do not yet support mixin within mixin
+ valueForProperties(property, props) {
+ let parts = property.split(';');
+ for (let i=0, p, m; i<parts.length; i++) {
+ if ((p = parts[i])) {
+ RX.MIXIN_MATCH.lastIndex = 0;
+ m = RX.MIXIN_MATCH.exec(p);
+ if (m) {
+ p = this.valueForProperty(props[m[1]], props);
+ } else {
+ let colon = p.indexOf(':');
+ if (colon !== -1) {
+ let pp = p.substring(colon);
+ pp = pp.trim();
+ pp = this.valueForProperty(pp, props) || pp;
+ p = p.substring(0, colon) + pp;
+ }
+ }
+ parts[i] = (p && p.lastIndexOf(';') === p.length - 1) ?
+ // strip trailing ;
+ p.slice(0, -1) :
+ p || '';
+ }
+ }
+ return parts.join(';');
+ }
+
+ applyProperties(rule, props) {
+ let 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;
+ }
+
+ // Apply keyframe transformations to the cssText of a given rule. The
+ // keyframeTransforms object is a map of keyframe names to transformer
+ // functions which take in cssText and spit out transformed cssText.
+ applyKeyframeTransforms(rule, keyframeTransforms) {
+ let input = rule['cssText'];
+ let output = rule['cssText'];
+ if (rule.hasAnimations == null) {
+ // Cache whether or not the rule has any animations to begin with:
+ rule.hasAnimations = RX.ANIMATION_MATCH.test(input);
+ }
+ // If there are no animations referenced, we can skip transforms:
+ if (rule.hasAnimations) {
+ let transform;
+ // If we haven't transformed this rule before, we iterate over all
+ // transforms:
+ if (rule.keyframeNamesToTransform == null) {
+ rule.keyframeNamesToTransform = [];
+ for (let keyframe in keyframeTransforms) {
+ transform = keyframeTransforms[keyframe];
+ output = transform(input);
+ // If the transform actually changed the CSS text, we cache the
+ // transform name for future use:
+ if (input !== output) {
+ input = output;
+ rule.keyframeNamesToTransform.push(keyframe);
+ }
+ }
+ } else {
+ // If we already have a list of keyframe names that apply to this
+ // rule, we apply only those keyframe name transforms:
+ for (let i = 0; i < rule.keyframeNamesToTransform.length; ++i) {
+ transform = keyframeTransforms[rule.keyframeNamesToTransform[i]];
+ input = transform(input);
+ }
+ output = input;
+ }
+ }
+ rule['cssText'] = output;
+ }
+
+ // Test if the rules in these styles matches the given `element` and if so,
+ // collect any custom properties into `props`.
+ /**
+ * @param {StyleNode} rules
+ * @param {Element} element
+ */
+ propertyDataFromStyles(rules, element) {
+ let props = {}, self = this;
+ // generates a unique key for these matches
+ let o = [];
+ // note: active rules excludes non-matching @media rules
+ StyleUtil.forEachRule(rules, 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.
+ let selectorToMatch = rule.transformedSelector || rule['parsedSelector'];
+ if (element && rule.propertyInfo.properties && selectorToMatch) {
+ if (matchesSelector.call(element, selectorToMatch)) {
+ self.collectProperties(rule, props);
+ // produce numeric key for these matches for lookup
+ addToBitMask(rule.index, o);
+ }
+ }
+ }, null, true);
+ return {properties: props, key: o};
+ }
+
+ /**
+ * @param {Element} scope
+ * @param {StyleNode} rule
+ * @param {string|undefined} cssBuild
+ * @param {function(Object)} callback
+ */
+ whenHostOrRootRule(scope, rule, cssBuild, callback) {
+ if (!rule.propertyInfo) {
+ this.decorateRule(rule);
+ }
+ if (!rule.propertyInfo.properties) {
+ return;
+ }
+ let {is, typeExtension} = StyleUtil.getIsExtends(scope);
+ let hostScope = is ?
+ StyleTransformer._calcHostScope(is, typeExtension) :
+ 'html';
+ let parsedSelector = rule['parsedSelector'];
+ let isRoot = (parsedSelector === ':host > *' || parsedSelector === 'html');
+ let isHost = parsedSelector.indexOf(':host') === 0 && !isRoot;
+ // build info is either in scope (when scope is an element) or in the style
+ // when scope is the default scope; note: this allows default scope to have
+ // mixed mode built and unbuilt styles.
+ if (cssBuild === 'shady') {
+ // :root -> x-foo > *.x-foo for elements and html for custom-style
+ isRoot = parsedSelector === (hostScope + ' > *.' + hostScope) || parsedSelector.indexOf('html') !== -1;
+ // :host -> x-foo for elements, but sub-rules have .x-foo in them
+ isHost = !isRoot && parsedSelector.indexOf(hostScope) === 0;
+ }
+ if (cssBuild === 'shadow') {
+ isRoot = parsedSelector === ':host > *' || parsedSelector === 'html';
+ isHost = isHost && !isRoot;
+ }
+ if (!isRoot && !isHost) {
+ return;
+ }
+ let selectorToMatch = hostScope;
+ if (isHost) {
+ // need to transform :host under ShadowDOM because `:host` does not work with `matches`
+ if (nativeShadow && !rule.transformedSelector) {
+ // transform :host into a matchable selector
+ rule.transformedSelector =
+ StyleTransformer._transformRuleCss(
+ rule,
+ StyleTransformer._transformComplexSelector,
+ StyleTransformer._calcElementScope(is),
+ hostScope
+ );
+ }
+ selectorToMatch = rule.transformedSelector || hostScope;
+ }
+ callback({
+ selector: selectorToMatch,
+ isHost: isHost,
+ isRoot: isRoot
+ });
+ }
+/**
+ * @param {Element} scope
+ * @param {StyleNode} rules
+ * @return {Object}
+ */
+ hostAndRootPropertiesForScope(scope, rules) {
+ let hostProps = {}, rootProps = {}, self = this;
+ // note: active rules excludes non-matching @media rules
+ let cssBuild = rules && rules['__cssBuild'];
+ StyleUtil.forEachRule(rules, function(rule) {
+ // if scope is StyleDefaults, use _element for matchesSelector
+ self.whenHostOrRootRule(scope, rule, cssBuild, function(info) {
+ let element = scope._element || scope;
+ if (matchesSelector.call(element, info.selector)) {
+ if (info.isHost) {
+ self.collectProperties(rule, hostProps);
+ } else {
+ self.collectProperties(rule, rootProps);
+ }
+ }
+ });
+ }, null, true);
+ return {rootProps: rootProps, hostProps: hostProps};
+ }
+
+ /**
+ * @param {Element} element
+ * @param {Object} properties
+ * @param {string} scopeSelector
+ */
+ transformStyles(element, properties, scopeSelector) {
+ let self = this;
+ let {is, typeExtension} = StyleUtil.getIsExtends(element);
+ let hostSelector = StyleTransformer
+ ._calcHostScope(is, typeExtension);
+ let rxHostSelector = element.extends ?
+ '\\' + hostSelector.slice(0, -1) + '\\]' :
+ hostSelector;
+ let hostRx = new RegExp(RX.HOST_PREFIX + rxHostSelector +
+ RX.HOST_SUFFIX);
+ let rules = StyleInfo.get(element).styleRules;
+ let keyframeTransforms =
+ this._elementKeyframeTransforms(element, rules, scopeSelector);
+ return StyleTransformer.elementStyles(element, rules, function(rule) {
+ self.applyProperties(rule, properties);
+ if (!nativeShadow &&
+ !StyleUtil.isKeyframesSelector(rule) &&
+ rule['cssText']) {
+ // NOTE: keyframe transforms only scope munge animation names, so it
+ // is not necessary to apply them in ShadowDOM.
+ self.applyKeyframeTransforms(rule, keyframeTransforms);
+ self._scopeSelector(rule, hostRx, hostSelector, scopeSelector);
+ }
+ });
+ }
+
+ /**
+ * @param {Element} element
+ * @param {StyleNode} rules
+ * @param {string} scopeSelector
+ * @return {Object}
+ */
+ _elementKeyframeTransforms(element, rules, scopeSelector) {
+ let keyframesRules = rules._keyframes;
+ let keyframeTransforms = {};
+ if (!nativeShadow && keyframesRules) {
+ // For non-ShadowDOM, we transform all known keyframes rules in
+ // advance for the current scope. This allows us to catch keyframes
+ // rules that appear anywhere in the stylesheet:
+ for (let i = 0, keyframesRule = keyframesRules[i];
+ i < keyframesRules.length;
+ keyframesRule = keyframesRules[++i]) {
+ this._scopeKeyframes(keyframesRule, scopeSelector);
+ keyframeTransforms[keyframesRule['keyframesName']] =
+ this._keyframesRuleTransformer(keyframesRule);
+ }
+ }
+ return keyframeTransforms;
+ }
+
+ // Generate a factory for transforming a chunk of CSS text to handle a
+ // particular scoped keyframes rule.
+ /**
+ * @param {StyleNode} keyframesRule
+ * @return {function(string):string}
+ */
+ _keyframesRuleTransformer(keyframesRule) {
+ return function(cssText) {
+ return cssText.replace(
+ keyframesRule.keyframesNameRx,
+ keyframesRule.transformedKeyframesName);
+ };
+ }
+
+/**
+ * Transforms `@keyframes` names to be unique for the current host.
+ * Example: @keyframes foo-anim -> @keyframes foo-anim-x-foo-0
+ *
+ * @param {StyleNode} rule
+ * @param {string} scopeId
+ */
+ _scopeKeyframes(rule, scopeId) {
+ rule.keyframesNameRx = new RegExp(rule['keyframesName'], 'g');
+ rule.transformedKeyframesName = rule['keyframesName'] + '-' + scopeId;
+ rule.transformedSelector = rule.transformedSelector || rule['selector'];
+ rule['selector'] = rule.transformedSelector.replace(
+ rule['keyframesName'], rule.transformedKeyframesName);
+ }
+
+ // 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-42.wide
+ // note: we use only the scope class (.x-foo-42) and not the hostSelector
+ // (x-foo) to scope :host rules; this helps make property host rules
+ // have low specificity. They are overrideable by class selectors but,
+ // unfortunately, not by type selectors (e.g. overriding via
+ // `.special` is ok, but not by `x-foo`).
+ /**
+ * @param {StyleNode} rule
+ * @param {RegExp} hostRx
+ * @param {string} hostSelector
+ * @param {string} scopeId
+ */
+ _scopeSelector(rule, hostRx, hostSelector, scopeId) {
+ rule.transformedSelector = rule.transformedSelector || rule['selector'];
+ let selector = rule.transformedSelector;
+ let scope = '.' + scopeId;
+ let parts = selector.split(',');
+ for (let i=0, l=parts.length, p; (i<l) && (p=parts[i]); i++) {
+ parts[i] = p.match(hostRx) ?
+ p.replace(hostSelector, scope) :
+ scope + ' ' + p;
+ }
+ rule['selector'] = parts.join(',');
+ }
+
+ /**
+ * @param {Element} element
+ * @param {string} selector
+ * @param {string} old
+ */
+ applyElementScopeSelector(element, selector, old) {
+ let c = element.getAttribute('class') || '';
+ let v = c;
+ if (old) {
+ v = c.replace(
+ new RegExp('\\s*' + XSCOPE_NAME + '\\s*' + old + '\\s*', 'g'), ' ');
+ }
+ v += (v ? ' ' : '') + XSCOPE_NAME + ' ' + selector;
+ if (c !== v) {
+ StyleUtil.setElementClassRaw(element, v);
+ }
+ }
+
+ /**
+ * @param {HTMLElement} element
+ * @param {Object} properties
+ * @param {string} selector
+ * @param {HTMLStyleElement} style
+ * @return {HTMLStyleElement}
+ */
+ applyElementStyle(element, properties, selector, style) {
+ // calculate cssText to apply
+ let cssText = style ? style.textContent || '' :
+ this.transformStyles(element, properties, selector);
+ // if shady and we have a cached style that is not style, decrement
+ let styleInfo = StyleInfo.get(element);
+ let s = styleInfo.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) {
+ // update existing style only under native
+ if (styleInfo.customStyle) {
+ styleInfo.customStyle.textContent = cssText;
+ style = styleInfo.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, element.shadowRoot,
+ styleInfo.placeholder);
+ }
+ } else {
+ // shady and no cache hit
+ if (!style) {
+ // apply css after the scope style of the element to help with
+ // style precedence rules.
+ if (cssText) {
+ style = StyleUtil.applyCss(cssText, selector, null,
+ styleInfo.placeholder);
+ }
+ // shady and cache hit but not in document
+ } else if (!style.parentNode) {
+ if (IS_IE && cssText.indexOf('@media') > -1) {
+ // @media rules may be stale in IE 10 and 11
+ // refresh the text content of the style to revalidate them.
+ style.textContent = cssText;
+ }
+ StyleUtil.applyStyle(style, null, styleInfo.placeholder);
+ }
+ }
+ // 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 (styleInfo.customStyle != style) {
+ style['_useCount']++;
+ }
+ styleInfo.customStyle = style;
+ }
+ return style;
+ }
+
+ /**
+ * @param {Element} style
+ * @param {Object} properties
+ */
+ applyCustomStyle(style, properties) {
+ let rules = StyleUtil.rulesForStyle(/** @type {HTMLStyleElement} */(style));
+ let self = this;
+ style.textContent = StyleUtil.toCssText(rules, function(/** StyleNode */rule) {
+ let css = rule['cssText'] = rule['parsedCssText'];
+ if (rule.propertyInfo && rule.propertyInfo.cssText) {
+ // remove property assignments
+ // so next function isn't confused
+ // NOTE: we have 3 categories of css:
+ // (1) normal properties,
+ // (2) custom property assignments (--foo: red;),
+ // (3) custom property usage: border: var(--foo); @apply(--foo);
+ // In elements, 1 and 3 are separated for efficiency; here they
+ // are not and this makes this case unique.
+ css = removeCustomPropAssignment(/** @type {string} */(css));
+ // replace with reified properties, scenario is same as mixin
+ rule['cssText'] = self.valueForProperties(css, properties);
+ }
+ });
+ }
+}
+
+/**
+ * @param {number} n
+ * @param {Array<number>} bits
+ */
+function addToBitMask(n, bits) {
+ let o = parseInt(n / 32, 10);
+ let v = 1 << (n % 32);
+ bits[o] = (bits[o] || 0) | v;
+}
+
+export default new StyleProperties();

Powered by Google App Engine
This is Rietveld 408576698