UNPKG

five-bells-visualization

Version:
365 lines (328 loc) 12.8 kB
<!-- @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-util.html"> <link rel="import" href="../lib/style-transformer.html"> <link rel="import" href="../lib/settings.html"> <link rel="import" href="../lib/style-defaults.html"> <script> (function() { var baseAttachedCallback = Polymer.Base.attachedCallback; var baseSerializeValueToAttribute = Polymer.Base.serializeValueToAttribute; var nativeShadow = Polymer.Settings.useNativeShadow; // TODO(sorvell): consider if calculating properties and applying // styles with properties should be separate modules. Polymer.Base._addFeature({ attachedCallback: function() { baseAttachedCallback.call(this); if (!this._xScopeSelector) { this._updateOwnStyles(); } }, _updateOwnStyles: function() { if (this.enableCustomStyleProperties) { this._styleProperties = this._computeStyleProperties(); this._applyStyleProperties(this._styleProperties); } }, _computeStyleProperties: function() { var props = {}; this.simpleMixin(props, this._computeStylePropertiesFromHost()); this.simpleMixin(props, this._computeOwnStyleProperties()); this._reifyCustomProperties(props); return props; }, _computeStylePropertiesFromHost: function() { // TODO(sorvell): experimental feature, global defaults! var props = {}, styles = [Polymer.StyleDefaults.defaultSheet]; var host = this.domHost; if (host) { // enable finding styles in hosts without `enableStyleCustomProperties` if (!host._styleProperties) { host._styleProperties = host._computeStyleProperties(); } props = Object.create(host._styleProperties); styles = host._styles; } this.simpleMixin(props, this._customPropertiesFromStyles(styles, host)); return props; }, _computeOwnStyleProperties: function() { var props = {}; this.simpleMixin(props, this._customPropertiesFromStyles(this._styles)); if (this.styleProperties) { for (var i in this.styleProperties) { props[i] = this.styleProperties[i]; } } return props; }, _customPropertiesFromStyles: function(styles, hostNode) { var props = {}; var p = this._customPropertiesFromRule.bind(this, props, hostNode); if (styles) { for (var i=0, l=styles.length, s; (i<l) && (s=styles[i]); i++) { Polymer.StyleUtil.forEachStyleRule(this._rulesForStyle(s), p); } } return props; }, // test if a rule matches the given node and if so, // collect any custom properties // TODO(sorvell): support custom variable assignment within mixins _customPropertiesFromRule: function(props, hostNode, rule) { hostNode = hostNode || this; // TODO(sorvell): file crbug, ':host' does not match element. if (this.elementMatches(rule.selector) || ((hostNode === this) && (rule.selector === ':host'))) { // --g: var(--b); or --g: 5; this._collectPropertiesFromRule(rule, CUSTOM_VAR_ASSIGN, props); // --g: { ... } this._collectPropertiesFromRule(rule, CUSTOM_MIXIN_ASSIGN, props); } }, // given a rule and rx that matches key and value, set key in properties // to value _collectPropertiesFromRule: function(rule, rx, properties) { var m; while (m = rx.exec(rule.cssText)) { properties[m[1]] = m[2].trim(); } }, _reifyCustomProperties: function(props) { for (var i in props) { props[i] = this._valueForCustomProperty(props[i], props); } }, _valueForCustomProperty: function(property, props) { var cv; while ((typeof property === 'string') && (cv = property.match(CUSTOM_VAR_VALUE))) { property = props[cv[1]]; } return property; }, // apply styles _applyStyleProperties: function(bag) { var s$ = this._styles; if (s$) { var style = styleFromCache(this.is, bag, s$); var old = this._xScopeSelector; this._ensureScopeSelector(style ? style._scope : null); if (!style) { var cssText = this._generateCustomStyleCss(bag, s$); style = cssText ? this._applyCustomCss(cssText) : {}; cacheStyle(this.is, style, this._xScopeSelector, this._styleProperties, s$); } else if (nativeShadow) { this._applyCustomCss(style.textContent); } if (style.textContent || old /*&& !nativeShadow*/) { this._applyXScopeSelector(this._xScopeSelector, old); } } }, _applyXScopeSelector: function(selector, old) { var c = this._scopeCssViaAttr ? this.getAttribute(SCOPE_NAME) : this.className; v = old ? c.replace(old, selector) : (c ? c + ' ' : '') + XSCOPE_NAME + ' ' + selector; if (c !== v) { if (this._scopeCssViaAttr) { this.setAttribute(SCOPE_NAME, v); } else { this.className = v; } } }, _generateCustomStyleCss: function(properties, styles) { var b = this._applyPropertiesToRule.bind(this, properties); var cssText = ''; // TODO(sorvell): don't redo parsing work each time as below; // instead create a sheet with just custom properties for (var i=0, l=styles.length, s; (i<l) && (s=styles[i]); i++) { cssText += this._transformCss(s.textContent, b) + '\n\n'; } return cssText.trim(); }, _transformCss: function(cssText, callback) { return nativeShadow ? Polymer.StyleUtil.toCssText(cssText, callback) : Polymer.StyleTransformer.css(cssText, this.is, this.extends, callback, this._scopeCssViaAttr); }, _xScopeCount: 0, _ensureScopeSelector: function(selector) { selector = selector || (this.is + '-' + (Object.getPrototypeOf(this)._xScopeCount++)); this._xScopeSelector = selector; }, _applyCustomCss: function(cssText) { if (this._customStyle) { this._customStyle.textContent = cssText; } else if (cssText) { this._customStyle = Polymer.StyleUtil.applyCss(cssText, this._xScopeSelector, nativeShadow ? this.root : null); } return this._customStyle; }, _applyPropertiesToRule: function(properties, rule) { if (!nativeShadow) { this._scopifyRule(rule); } if (rule.cssText.match(CUSTOM_RULE_RX)) { rule.cssText = this._applyPropertiesToText(rule.cssText, properties); } else { rule.cssText = ''; } //console.log(rule.cssText); }, _applyPropertiesToText: function(cssText, props) { var output = ''; var m, v; // e.g. color: var(--color); while (m = CUSTOM_VAR_USE.exec(cssText)) { v = props[m[2]]; if (v) { output += '\t' + m[1].trim() + ': ' + this._propertyToCss(v); } } // e.g. @mixin(--stuff); while (m = CUSTOM_MIXIN_USE.exec(cssText)) { v = m[1]; if (v) { var parts = v.split(' '); for (var i=0, p; i < parts.length; i++) { p = props[parts[i].trim()]; if (p) { output += '\t' + this._propertyToCss(p); } } } } return output; }, _propertyToCss: function(property) { var p = property.trim(); p = p[p.length-1] === ';' ? p : p + ';'; return p + '\n'; }, // 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 _scopifyRule: function(rule) { var selector = rule.selector; var host = this.is; var rx = new RegExp(HOST_SELECTOR_PREFIX + host + HOST_SELECTOR_SUFFIX); var parts = selector.split(','); var scope = this._scopeCssViaAttr ? SCOPE_PREFIX + this._xScopeSelector + SCOPE_SUFFIX : '.' + this._xScopeSelector; for (var i=0, l=parts.length, p; (i<l) && (p=parts[i]); i++) { parts[i] = p.match(rx) ? p.replace(host, host + scope) : scope + ' ' + p; } rule.selector = parts.join(','); }, _scopeElementClass: function(element, selector) { if (!nativeShadow && !this._scopeCssViaAttr) { selector += (selector ? ' ' : '') + SCOPE_NAME + ' ' + this.is + (element._xScopeSelector ? ' ' + XSCOPE_NAME + ' ' + element._xScopeSelector : ''); } return selector; }, // override to ensure whenever classes are set, we need to shim them. serializeValueToAttribute: function(value, attribute, node) { if (attribute === 'class') { // host needed to scope styling. var host = node === this ? Polymer.dom(this).getOwnerRoot() || this.dataHost : this; if (host) { value = host._scopeElementClass(node, value); } } baseSerializeValueToAttribute.call(this, value, attribute, node); }, updateStyles: function() { this._updateOwnStyles(); this._updateRootStyles(this.root); }, updateHostStyles: function() { var host = Polymer.dom(this).getOwnerRoot() || this.dataHost; if (host) { host.updateStyles(); } else { this._updateRootStyles(document); } }, _updateRootStyles: function(root) { // TODO(sorvell): temporary way to find local dom that needs // x-scope styling. var scopeSelector = this._scopeCssViaAttr ? '[' + SCOPE_NAME + '~=' + XSCOPE_NAME + ']' : '.' + XSCOPE_NAME; var c$ = Polymer.dom(root).querySelectorAll(scopeSelector); for (var i=0, l= c$.length, c; (i<l) && (c=c$[i]); i++) { if (c.updateStyles) { c.updateStyles(); } } } }); var styleCache = {}; function cacheStyle(is, style, scope, bag, styles) { style._scope = scope; style._properties = bag; style._styles = styles; var s$ = styleCache[is] = styleCache[is] || []; s$.push(style); } function styleFromCache(is, bag, checkStyles) { var styles = styleCache[is]; if (styles) { for (var i=0, s; i < styles.length; i++) { s = styles[i]; if (objectsEqual(bag, s._properties) && objectsEqual(checkStyles, s._styles)) { return s; } } } } function objectsEqual(a, b) { for (var i in a) { if (a[i] !== b[i]) { return false; } } for (var i in b) { if (a[i] !== b[i]) { return false; } } return true; } var SCOPE_NAME= Polymer.StyleTransformer.SCOPE_NAME; var XSCOPE_NAME = 'x-scope'; var SCOPE_PREFIX = '[' + SCOPE_NAME + '~='; var SCOPE_SUFFIX = ']'; var HOST_SELECTOR_PREFIX = '(?:^|[^.])'; var HOST_SELECTOR_SUFFIX = '($|[.:[\\s>+~])'; var CUSTOM_RULE_RX = /mixin|var/; var CUSTOM_VAR_ASSIGN = /(--[^\:;]*?):\s*?([^;{]*?);/g; var CUSTOM_MIXIN_ASSIGN = /(--[^\:;]*?):[^{;]*?{([^}]*?)}/g; var CUSTOM_VAR_VALUE = /^var\(([^)]*?)\)/; var CUSTOM_VAR_USE = /(?:^|[;}\s])([^;{}]*?):[\s]*?var\(([^)]*)?\)/gim; var CUSTOM_MIXIN_USE = /mixin\(([^)]*)\)/gim; })(); </script>