UNPKG

five-bells-visualization

Version:
214 lines (182 loc) 7.21 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="style-util.html"> <script> (function() { /* Transforms ShadowDOM styling into ShadyDOM styling * scoping: * elements in scope get scoping selector class="x-foo-scope" * selectors re-written as follows: div button -> div.x-foo-scope button.x-foo-scope * :host -> scopeName * :host(...) -> scopeName... * ::content -> ' ' NOTE: requires use of scoping selector and selectors cannot otherwise be scoped: e.g. :host ::content > .bar -> x-foo > .bar * ::shadow, /deep/: processed simimlar to ::content * :host-context(...): NOT SUPPORTED */ // Given a node and scope name, add a scoping class to each node // in the tree. This facilitates transforming css into scoped rules. function transformDom(node, scope, useAttr, shouldRemoveScope) { _transformDom(node, scope || '', useAttr, shouldRemoveScope); } function _transformDom(node, selector, useAttr, shouldRemoveScope) { if (node.setAttribute) { transformElement(node, selector, useAttr, shouldRemoveScope); } var c$ = Polymer.dom(node).childNodes; for (var i=0; i<c$.length; i++) { _transformDom(c$[i], selector, useAttr, shouldRemoveScope); } } function transformElement(element, scope, useAttr, shouldRemoveScope) { if (useAttr) { if (shouldRemoveScope) { element.removeAttribute(SCOPE_NAME); } else { element.setAttribute(SCOPE_NAME, scope); } } else { // note: if using classes, we add both the general 'style-scope' class // as well as the specific scope. This enables easy filtering of all // `style-scope` elements if (scope) { if (shouldRemoveScope) { element.classList.remove(SCOPE_NAME, scope); } else { element.classList.add(SCOPE_NAME, scope); } } } } function transformHost(host, scope) { } // Given a string of cssText and a scoping string (scope), returns // a string of scoped css where each selector is transformed to include // a class created from the scope. ShadowDOM selectors are also transformed // (e.g. :host) to use the scoping selector. function transformCss(rules, scope, ext, callback, useAttr) { var hostScope = calcHostScope(scope, ext); scope = calcElementScope(scope, useAttr); return Polymer.StyleUtil.toCssText(rules, function(rule) { transformRule(rule, scope, hostScope); if (callback) { callback(rule, scope, hostScope); } }); } function calcElementScope(scope, useAttr) { if (scope) { return useAttr ? CSS_ATTR_PREFIX + scope + CSS_ATTR_SUFFIX : CSS_CLASS_PREFIX + scope; } else { return ''; } } function calcHostScope(scope, ext) { return ext ? '[is=' + scope + ']' : scope; } function transformRule(rule, scope, hostScope) { _transformRule(rule, transformComplexSelector, scope, hostScope); } // transforms a css rule to a scoped rule. function _transformRule(rule, transformer, scope, hostScope) { var p$ = rule.selector.split(COMPLEX_SELECTOR_SEP); for (var i=0, l=p$.length, p; (i<l) && (p=p$[i]); i++) { p$[i] = transformer(p, scope, hostScope); } rule.selector = p$.join(COMPLEX_SELECTOR_SEP); } function transformComplexSelector(selector, scope, hostScope) { var stop = false; selector = selector.replace(SIMPLE_SELECTOR_SEP, function(m, c, s) { if (!stop) { var o = transformCompoundSelector(s, c, scope, hostScope); if (o.stop) { stop = true; } c = o.combinator; s = o.value; } return c + s; }); return selector; } function transformCompoundSelector(selector, combinator, scope, hostScope) { // replace :host with host scoping class var jumpIndex = selector.search(SCOPE_JUMP); if (selector.indexOf(HOST) >=0) { // :host(...) selector = selector.replace(HOST_PAREN, function(m, host, paren) { return hostScope + paren; }); // now normal :host selector = selector.replace(HOST, hostScope); // replace other selectors with scoping class } else if (jumpIndex !== 0) { selector = scope ? transformSimpleSelector(selector, scope) : selector; } // remove left-side combinator when dealing with ::content. if (selector.indexOf(CONTENT) >= 0) { combinator = ''; } // process scope jumping selectors up to the scope jump and then stop // e.g. .zonk ::content > .foo ==> .zonk.scope > .foo var stop; if (jumpIndex >= 0) { selector = selector.replace(SCOPE_JUMP, ' '); stop = true; } return {value: selector, combinator: combinator, stop: stop}; } function transformSimpleSelector(selector, scope) { var p$ = selector.split(PSEUDO_PREFIX); p$[0] += scope; return p$.join(PSEUDO_PREFIX); } function transformRootRule(rule) { _transformRule(rule, transformRootSelector); } function transformRootSelector(selector) { return selector.match(SCOPE_JUMP) ? transformComplexSelector(selector) : selector.trim() + SCOPE_ROOT_SELECTOR; } var SCOPE_NAME = 'style-scope'; var SCOPE_ROOT_SELECTOR = ':not([' + SCOPE_NAME + '])' + ':not(.' + SCOPE_NAME + ')'; var COMPLEX_SELECTOR_SEP = ','; var SIMPLE_SELECTOR_SEP = /(^|[\s>+~]+)([^\s>+~]+)/g; var HOST = ':host'; // NOTE: this supports 1 nested () pair for things like // :host(:not([selected]), more general support requires // parsing which seems like overkill var HOST_PAREN = /(\:host)(?:\(((?:\([^)(]*\)|[^)(]*)+?)\))/g; var CONTENT = '::content'; var SCOPE_JUMP = /\:\:content|\:\:shadow|\/deep\//; var CSS_CLASS_PREFIX = '.'; var CSS_ATTR_PREFIX = '[' + SCOPE_NAME + '~='; var CSS_ATTR_SUFFIX = ']'; var PSEUDO_PREFIX = ':'; // exports Polymer.StyleTransformer = { element: transformElement, dom: transformDom, host: transformHost, css: transformCss, rule: transformRule, rootRule: transformRootRule, SCOPE_NAME: SCOPE_NAME }; })(); </script>