OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library polymer.src.css_emitters; |
| 6 |
| 7 import 'package:csslib/visitor.dart' show Visitor, CssPrinter, ElementSelector, |
| 8 UriTerm, Selector, HostDirective, SimpleSelectorSequence, StyleSheet; |
| 9 |
| 10 import 'info.dart'; |
| 11 import 'paths.dart' show PathMapper; |
| 12 import 'utils.dart'; |
| 13 |
| 14 void rewriteCssUris(PathMapper pathMapper, String cssPath, bool rewriteUrls, |
| 15 StyleSheet styleSheet) { |
| 16 new _UriVisitor(pathMapper, cssPath, rewriteUrls).visitTree(styleSheet); |
| 17 } |
| 18 |
| 19 /** Compute each CSS URI resource relative from the generated CSS file. */ |
| 20 class _UriVisitor extends Visitor { |
| 21 /** |
| 22 * Relative path from the output css file to the location of the original |
| 23 * css file that contained the URI to each resource. |
| 24 */ |
| 25 final String _pathToOriginalCss; |
| 26 |
| 27 factory _UriVisitor(PathMapper pathMapper, String cssPath, bool rewriteUrl) { |
| 28 var cssDir = path.dirname(cssPath); |
| 29 var outCssDir = rewriteUrl ? pathMapper.outputDirPath(cssPath) |
| 30 : path.dirname(cssPath); |
| 31 return new _UriVisitor._internal(path.relative(cssDir, from: outCssDir)); |
| 32 } |
| 33 |
| 34 _UriVisitor._internal(this._pathToOriginalCss); |
| 35 |
| 36 void visitUriTerm(UriTerm node) { |
| 37 // Don't touch URIs that have any scheme (http, etc.). |
| 38 var uri = Uri.parse(node.text); |
| 39 if (uri.host != '') return; |
| 40 if (uri.scheme != '' && uri.scheme != 'package') return; |
| 41 |
| 42 node.text = pathToUrl( |
| 43 path.normalize(path.join(_pathToOriginalCss, node.text))); |
| 44 } |
| 45 } |
| 46 |
| 47 |
| 48 /** Emit the contents of the style tag outside of a component. */ |
| 49 String emitStyleSheet(StyleSheet ss, FileInfo file) => |
| 50 (new _CssEmitter(file.components.keys.toSet()) |
| 51 ..visitTree(ss, pretty: true)).toString(); |
| 52 |
| 53 /** Emit a component's style tag content emulating scoped css. */ |
| 54 String emitComponentStyleSheet(StyleSheet ss, String tagName) => |
| 55 (new _ComponentCssEmitter(tagName)..visitTree(ss, pretty: true)).toString(); |
| 56 |
| 57 String emitOriginalCss(StyleSheet css) => |
| 58 (new CssPrinter()..visitTree(css)).toString(); |
| 59 |
| 60 /** Only x-tag name element selectors are emitted as [is="x-"]. */ |
| 61 class _CssEmitter extends CssPrinter { |
| 62 final Set _componentsTag; |
| 63 _CssEmitter(this._componentsTag); |
| 64 |
| 65 void visitElementSelector(ElementSelector node) { |
| 66 // If element selector is a component's tag name, then change selector to |
| 67 // find element who's is attribute's the component's name. |
| 68 if (_componentsTag.contains(node.name)) { |
| 69 emit('[is="${node.name}"]'); |
| 70 return; |
| 71 } |
| 72 super.visitElementSelector(node); |
| 73 } |
| 74 } |
| 75 |
| 76 /** |
| 77 * Emits a css stylesheet applying rules to emulate scoped css. The rules adjust |
| 78 * element selectors to include the component's tag name. |
| 79 */ |
| 80 class _ComponentCssEmitter extends CssPrinter { |
| 81 final String _componentTagName; |
| 82 bool _inHostDirective = false; |
| 83 bool _selectorStartInHostDirective = false; |
| 84 |
| 85 _ComponentCssEmitter(this._componentTagName); |
| 86 |
| 87 /** Is the element selector an x-tag name. */ |
| 88 bool _isSelectorElementXTag(Selector node) { |
| 89 if (node.simpleSelectorSequences.length > 0) { |
| 90 var selector = node.simpleSelectorSequences[0].simpleSelector; |
| 91 return selector is ElementSelector && selector.name == _componentTagName; |
| 92 } |
| 93 return false; |
| 94 } |
| 95 |
| 96 void visitSelector(Selector node) { |
| 97 // If the selector starts with an x-tag name don't emit it twice. |
| 98 if (!_isSelectorElementXTag(node)) { |
| 99 if (_inHostDirective) { |
| 100 // Style the element that's hosting the component, therefore don't emit |
| 101 // the descendent combinator (first space after the [is="x-..."]). |
| 102 emit('[is="$_componentTagName"]'); |
| 103 // Signal that first simpleSelector must be checked. |
| 104 _selectorStartInHostDirective = true; |
| 105 } else { |
| 106 // Emit its scoped as a descendent (space at end). |
| 107 emit('[is="$_componentTagName"] '); |
| 108 } |
| 109 } |
| 110 super.visitSelector(node); |
| 111 } |
| 112 |
| 113 /** |
| 114 * If first simple selector of a ruleset in a @host directive is a wildcard |
| 115 * then don't emit the wildcard. |
| 116 */ |
| 117 void visitSimpleSelectorSequence(SimpleSelectorSequence node) { |
| 118 if (_selectorStartInHostDirective) { |
| 119 _selectorStartInHostDirective = false; |
| 120 if (node.simpleSelector.isWildcard) { |
| 121 // Skip the wildcard if first item in the sequence. |
| 122 return; |
| 123 } |
| 124 assert(node.isCombinatorNone); |
| 125 } |
| 126 |
| 127 super.visitSimpleSelectorSequence(node); |
| 128 } |
| 129 |
| 130 void visitElementSelector(ElementSelector node) { |
| 131 // If element selector is the component's tag name, then change selector to |
| 132 // find element who's is attribute is the component's name. |
| 133 if (_componentTagName == node.name) { |
| 134 emit('[is="$_componentTagName"]'); |
| 135 return; |
| 136 } |
| 137 super.visitElementSelector(node); |
| 138 } |
| 139 |
| 140 /** |
| 141 * If we're polyfilling scoped styles the @host directive is stripped. Any |
| 142 * ruleset(s) processed in an @host will fixup the first selector. See |
| 143 * visitSelector and visitSimpleSelectorSequence in this class, they adjust |
| 144 * the selectors so it styles the element hosting the compopnent. |
| 145 */ |
| 146 void visitHostDirective(HostDirective node) { |
| 147 _inHostDirective = true; |
| 148 emit('/* @host */'); |
| 149 for (var ruleset in node.rulesets) { |
| 150 ruleset.visit(this); |
| 151 } |
| 152 _inHostDirective = false; |
| 153 emit('/* end of @host */\n'); |
| 154 } |
| 155 } |
OLD | NEW |