OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 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. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library csslib.parser; | 5 library csslib.parser; |
6 | 6 |
7 import 'dart:math' as math; | 7 import 'dart:math' as math; |
8 | 8 |
9 import 'package:source_maps/span.dart' show SourceFile, Span, FileSpan; | 9 import 'package:source_maps/span.dart' show SourceFile, Span, FileSpan; |
10 | 10 |
(...skipping 12 matching lines...) Expand all Loading... |
23 | 23 |
24 /** Used for parser lookup ahead (used for nested selectors Less support). */ | 24 /** Used for parser lookup ahead (used for nested selectors Less support). */ |
25 class ParserState extends TokenizerState { | 25 class ParserState extends TokenizerState { |
26 final Token peekToken; | 26 final Token peekToken; |
27 final Token previousToken; | 27 final Token previousToken; |
28 | 28 |
29 ParserState(this.peekToken, this.previousToken, Tokenizer tokenizer) | 29 ParserState(this.peekToken, this.previousToken, Tokenizer tokenizer) |
30 : super(tokenizer); | 30 : super(tokenizer); |
31 } | 31 } |
32 | 32 |
33 void _createMessages({List errors, List options}) { | 33 void _createMessages({List<Message> errors, List<String> options}) { |
34 if (errors == null) errors = []; | 34 if (errors == null) errors = []; |
35 | 35 |
36 if (options == null) { | 36 if (options == null) { |
37 options = ['--no-colors', 'memory']; | 37 options = ['--no-colors', 'memory']; |
38 } | 38 } |
39 var opt = PreprocessorOptions.parse(options); | 39 var opt = PreprocessorOptions.parse(options); |
40 messages = new Messages(options: opt, printHandler: errors.add); | 40 messages = new Messages(options: opt, printHandler: errors.add); |
41 } | 41 } |
42 | 42 |
43 /** CSS checked mode enabled. */ | 43 /** CSS checked mode enabled. */ |
44 bool get isChecked => messages.options.checked; | 44 bool get isChecked => messages.options.checked; |
45 | 45 |
46 // TODO(terry): Remove nested name parameter. | 46 // TODO(terry): Remove nested name parameter. |
47 /** Parse and analyze the CSS file. */ | 47 /** Parse and analyze the CSS file. */ |
48 StyleSheet compile(var input, {List errors, List options, bool nested: true, | 48 StyleSheet compile(var input, {List<Message> errors, List<String> options, |
49 bool polyfill: false, List<StyleSheet> includes: null}) { | 49 bool nested: true, |
| 50 bool polyfill: false, |
| 51 List<StyleSheet> includes: null}) { |
| 52 |
50 if (includes == null) { | 53 if (includes == null) { |
51 includes = []; | 54 includes = []; |
52 } | 55 } |
53 | 56 |
54 var source = _inputAsString(input); | 57 var source = _inputAsString(input); |
55 | 58 |
56 _createMessages(errors: errors, options: options); | 59 _createMessages(errors: errors, options: options); |
57 | 60 |
58 var file = new SourceFile.text(null, source); | 61 var file = new SourceFile.text(null, source); |
59 | 62 |
60 var tree = new _Parser(file, source).parse(); | 63 var tree = new _Parser(file, source).parse(); |
61 | 64 |
62 analyze([tree], errors: errors, options: options); | 65 analyze([tree], errors: errors, options: options); |
63 | 66 |
64 if (polyfill) { | 67 if (polyfill) { |
65 var processCss = new PolyFill(messages, true); | 68 var processCss = new PolyFill(messages, true); |
66 processCss.process(tree, includes: includes); | 69 processCss.process(tree, includes: includes); |
67 } | 70 } |
68 | 71 |
69 return tree; | 72 return tree; |
70 } | 73 } |
71 | 74 |
72 /** Analyze the CSS file. */ | 75 /** Analyze the CSS file. */ |
73 void analyze(List<StyleSheet> styleSheets, {List errors, List options}) { | 76 void analyze(List<StyleSheet> styleSheets, |
| 77 {List<Message> errors, List<String> options}) { |
| 78 |
74 _createMessages(errors: errors, options: options); | 79 _createMessages(errors: errors, options: options); |
75 new Analyzer(styleSheets, messages).run(); | 80 new Analyzer(styleSheets, messages).run(); |
76 } | 81 } |
77 | 82 |
78 /** | 83 /** |
79 * Parse the [input] CSS stylesheet into a tree. The [input] can be a [String], | 84 * Parse the [input] CSS stylesheet into a tree. The [input] can be a [String], |
80 * or [List<int>] of bytes and returns a [StyleSheet] AST. The optional | 85 * or [List<int>] of bytes and returns a [StyleSheet] AST. The optional |
81 * [errors] list will contain each error/warning as a [Message]. | 86 * [errors] list will contain each error/warning as a [Message]. |
82 */ | 87 */ |
83 StyleSheet parse(var input, {List errors, List options}) { | 88 StyleSheet parse(var input, {List<Message> errors, List<String> options}) { |
84 var source = _inputAsString(input); | 89 var source = _inputAsString(input); |
85 | 90 |
86 _createMessages(errors: errors, options: options); | 91 _createMessages(errors: errors, options: options); |
87 | 92 |
88 var file = new SourceFile.text(null, source); | 93 var file = new SourceFile.text(null, source); |
89 | 94 |
90 return new _Parser(file, source).parse(); | 95 return new _Parser(file, source).parse(); |
91 } | 96 } |
92 | 97 |
93 /** | 98 /** |
94 * Parse the [input] CSS selector into a tree. The [input] can be a [String], | 99 * Parse the [input] CSS selector into a tree. The [input] can be a [String], |
95 * or [List<int>] of bytes and returns a [StyleSheet] AST. The optional | 100 * or [List<int>] of bytes and returns a [StyleSheet] AST. The optional |
96 * [errors] list will contain each error/warning as a [Message]. | 101 * [errors] list will contain each error/warning as a [Message]. |
97 */ | 102 */ |
98 StyleSheet selector(var input, {List errors}) { | 103 StyleSheet selector(var input, {List<Message> errors}) { |
99 var source = _inputAsString(input); | 104 var source = _inputAsString(input); |
100 | 105 |
101 _createMessages(errors: errors); | 106 _createMessages(errors: errors); |
102 | 107 |
103 var file = new SourceFile.text(null, source); | 108 var file = new SourceFile.text(null, source); |
104 | 109 |
105 return new _Parser(file, source).parseSelector(); | 110 return new _Parser(file, source).parseSelector(); |
106 } | 111 } |
107 | 112 |
108 String _inputAsString(var input) { | 113 String _inputAsString(var input) { |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
143 final _Parser _parser; | 148 final _Parser _parser; |
144 | 149 |
145 Parser(SourceFile file, String text, {int start: 0, String baseUrl}) : | 150 Parser(SourceFile file, String text, {int start: 0, String baseUrl}) : |
146 _parser = new _Parser(file, text, start: start, baseUrl: baseUrl); | 151 _parser = new _Parser(file, text, start: start, baseUrl: baseUrl); |
147 | 152 |
148 StyleSheet parse() => _parser.parse(); | 153 StyleSheet parse() => _parser.parse(); |
149 } | 154 } |
150 | 155 |
151 /** A simple recursive descent parser for CSS. */ | 156 /** A simple recursive descent parser for CSS. */ |
152 class _Parser { | 157 class _Parser { |
153 Tokenizer tokenizer; | 158 final Tokenizer tokenizer; |
154 | 159 |
155 /** Base url of CSS file. */ | 160 /** Base url of CSS file. */ |
156 final String _baseUrl; | 161 final String _baseUrl; |
157 | 162 |
158 /** | 163 /** |
159 * File containing the source being parsed, used to report errors with | 164 * File containing the source being parsed, used to report errors with |
160 * source-span locations. | 165 * source-span locations. |
161 */ | 166 */ |
162 final SourceFile file; | 167 final SourceFile file; |
163 | 168 |
(...skipping 1013 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1177 } while (_maybeEat(TokenKind.COMMA)); | 1182 } while (_maybeEat(TokenKind.COMMA)); |
1178 | 1183 |
1179 if (selectors.length > 0) { | 1184 if (selectors.length > 0) { |
1180 return new SelectorGroup(selectors, _makeSpan(start)); | 1185 return new SelectorGroup(selectors, _makeSpan(start)); |
1181 } | 1186 } |
1182 } | 1187 } |
1183 | 1188 |
1184 /** | 1189 /** |
1185 * Return list of selectors | 1190 * Return list of selectors |
1186 */ | 1191 */ |
1187 processSelector() { | 1192 Selector processSelector() { |
1188 List<SimpleSelectorSequence> simpleSequences = []; | 1193 var simpleSequences = <SimpleSelectorSequence>[]; |
1189 int start = _peekToken.start; | 1194 var start = _peekToken.start; |
1190 while (true) { | 1195 while (true) { |
1191 // First item is never descendant make sure it's COMBINATOR_NONE. | 1196 // First item is never descendant make sure it's COMBINATOR_NONE. |
1192 var selectorItem = simpleSelectorSequence(simpleSequences.length == 0); | 1197 var selectorItem = simpleSelectorSequence(simpleSequences.length == 0); |
1193 if (selectorItem != null) { | 1198 if (selectorItem != null) { |
1194 simpleSequences.add(selectorItem); | 1199 simpleSequences.add(selectorItem); |
1195 } else { | 1200 } else { |
1196 break; | 1201 break; |
1197 } | 1202 } |
1198 } | 1203 } |
1199 | 1204 |
(...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1337 } | 1342 } |
1338 | 1343 |
1339 return false; | 1344 return false; |
1340 } | 1345 } |
1341 | 1346 |
1342 /** | 1347 /** |
1343 * type_selector | universal | HASH | class | attrib | pseudo | 1348 * type_selector | universal | HASH | class | attrib | pseudo |
1344 */ | 1349 */ |
1345 simpleSelectorTail() { | 1350 simpleSelectorTail() { |
1346 // Check for HASH | class | attrib | pseudo | negation | 1351 // Check for HASH | class | attrib | pseudo | negation |
1347 int start = _peekToken.start; | 1352 var start = _peekToken.start; |
1348 switch (_peek()) { | 1353 switch (_peek()) { |
1349 case TokenKind.HASH: | 1354 case TokenKind.HASH: |
1350 _eat(TokenKind.HASH); | 1355 _eat(TokenKind.HASH); |
1351 | 1356 |
1352 bool hasWhiteSpace = false; | 1357 var hasWhiteSpace = false; |
1353 if (_anyWhiteSpaceBeforePeekToken(TokenKind.HASH)) { | 1358 if (_anyWhiteSpaceBeforePeekToken(TokenKind.HASH)) { |
1354 _warning("Not a valid ID selector expected #id", _makeSpan(start)); | 1359 _warning("Not a valid ID selector expected #id", _makeSpan(start)); |
1355 hasWhiteSpace = true; | 1360 hasWhiteSpace = true; |
1356 } | 1361 } |
1357 if (_peekIdentifier()) { | 1362 if (_peekIdentifier()) { |
1358 var id = identifier(); | 1363 var id = identifier(); |
1359 if (hasWhiteSpace) { | 1364 if (hasWhiteSpace) { |
1360 // Generate bad selector id (normalized). | 1365 // Generate bad selector id (normalized). |
1361 id.name = " ${id.name}"; | 1366 id.name = " ${id.name}"; |
1362 } | 1367 } |
(...skipping 25 matching lines...) Expand all Loading... |
1388 _peekToken.span); | 1393 _peekToken.span); |
1389 _next(); | 1394 _next(); |
1390 break; | 1395 break; |
1391 } | 1396 } |
1392 } | 1397 } |
1393 | 1398 |
1394 processPseudoSelector(int start) { | 1399 processPseudoSelector(int start) { |
1395 // :pseudo-class ::pseudo-element | 1400 // :pseudo-class ::pseudo-element |
1396 // TODO(terry): '::' should be token. | 1401 // TODO(terry): '::' should be token. |
1397 _eat(TokenKind.COLON); | 1402 _eat(TokenKind.COLON); |
1398 bool pseudoElement = _maybeEat(TokenKind.COLON); | 1403 var pseudoElement = _maybeEat(TokenKind.COLON); |
1399 | 1404 |
1400 // TODO(terry): If no identifier specified consider optimizing out the | 1405 // TODO(terry): If no identifier specified consider optimizing out the |
1401 // : or :: and making this a normal selector. For now, | 1406 // : or :: and making this a normal selector. For now, |
1402 // create an empty pseudoName. | 1407 // create an empty pseudoName. |
1403 var pseudoName; | 1408 var pseudoName; |
1404 if (_peekIdentifier()) { | 1409 if (_peekIdentifier()) { |
1405 pseudoName = identifier(); | 1410 pseudoName = identifier(); |
1406 } else { | 1411 } else { |
1407 return null; | 1412 return null; |
1408 } | 1413 } |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1449 * In CSS3, the expressions are identifiers, strings, or of the form "an+b". | 1454 * In CSS3, the expressions are identifiers, strings, or of the form "an+b". |
1450 * | 1455 * |
1451 * : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+ | 1456 * : [ [ PLUS | '-' | DIMENSION | NUMBER | STRING | IDENT ] S* ]+ |
1452 * | 1457 * |
1453 * num [0-9]+|[0-9]*\.[0-9]+ | 1458 * num [0-9]+|[0-9]*\.[0-9]+ |
1454 * PLUS '+' | 1459 * PLUS '+' |
1455 * DIMENSION {num}{ident} | 1460 * DIMENSION {num}{ident} |
1456 * NUMBER {num} | 1461 * NUMBER {num} |
1457 */ | 1462 */ |
1458 processSelectorExpression() { | 1463 processSelectorExpression() { |
1459 int start = _peekToken.start; | 1464 var start = _peekToken.start; |
1460 | 1465 |
1461 var expression = new SelectorExpression(_makeSpan(start)); | 1466 var expression = new SelectorExpression(_makeSpan(start)); |
1462 | 1467 |
1463 Token termToken; | 1468 Token termToken; |
1464 var value; | 1469 var value; |
1465 | 1470 |
1466 // Special parsing for expressions in pseudo functions. Minus is used as | 1471 // Special parsing for expressions in pseudo functions. Minus is used as |
1467 // operator not identifier. | 1472 // operator not identifier. |
1468 tokenizer.selectorExpression = true; | 1473 tokenizer.selectorExpression = true; |
1469 | 1474 |
1470 bool keepParsing = true; | 1475 var keepParsing = true; |
1471 while (keepParsing) { | 1476 while (keepParsing) { |
1472 switch (_peek()) { | 1477 switch (_peek()) { |
1473 case TokenKind.PLUS: | 1478 case TokenKind.PLUS: |
1474 start = _peekToken.start; | 1479 start = _peekToken.start; |
1475 termToken = _next(); | 1480 termToken = _next(); |
1476 expression.add(new OperatorPlus(_makeSpan(start))); | 1481 expression.add(new OperatorPlus(_makeSpan(start))); |
1477 break; | 1482 break; |
1478 case TokenKind.MINUS: | 1483 case TokenKind.MINUS: |
1479 start = _peekToken.start; | 1484 start = _peekToken.start; |
1480 termToken = _next(); | 1485 termToken = _next(); |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1532 // | 1537 // |
1533 // DASHMATCH: '|=' | 1538 // DASHMATCH: '|=' |
1534 // | 1539 // |
1535 // PREFIXMATCH: '^=' | 1540 // PREFIXMATCH: '^=' |
1536 // | 1541 // |
1537 // SUFFIXMATCH: '$=' | 1542 // SUFFIXMATCH: '$=' |
1538 // | 1543 // |
1539 // SUBSTRMATCH: '*=' | 1544 // SUBSTRMATCH: '*=' |
1540 // | 1545 // |
1541 // | 1546 // |
1542 processAttribute() { | 1547 AttributeSelector processAttribute() { |
1543 int start = _peekToken.start; | 1548 var start = _peekToken.start; |
1544 | 1549 |
1545 if (_maybeEat(TokenKind.LBRACK)) { | 1550 if (_maybeEat(TokenKind.LBRACK)) { |
1546 var attrName = identifier(); | 1551 var attrName = identifier(); |
1547 | 1552 |
1548 int op; | 1553 int op; |
1549 switch (_peek()) { | 1554 switch (_peek()) { |
1550 case TokenKind.EQUALS: | 1555 case TokenKind.EQUALS: |
1551 case TokenKind.INCLUDES: // ~= | 1556 case TokenKind.INCLUDES: // ~= |
1552 case TokenKind.DASH_MATCH: // |= | 1557 case TokenKind.DASH_MATCH: // |= |
1553 case TokenKind.PREFIX_MATCH: // ^= | 1558 case TokenKind.PREFIX_MATCH: // ^= |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1586 // | 1591 // |
1587 // property: IDENT [or IE hacks] | 1592 // property: IDENT [or IE hacks] |
1588 // prio: !important | 1593 // prio: !important |
1589 // expr: (see processExpr) | 1594 // expr: (see processExpr) |
1590 // | 1595 // |
1591 // Here are the ugly IE hacks we need to support: | 1596 // Here are the ugly IE hacks we need to support: |
1592 // property: expr prio? \9; - IE8 and below property, /9 before semi-colon | 1597 // property: expr prio? \9; - IE8 and below property, /9 before semi-colon |
1593 // *IDENT - IE7 or below | 1598 // *IDENT - IE7 or below |
1594 // _IDENT - IE6 property (automatically a valid ident) | 1599 // _IDENT - IE6 property (automatically a valid ident) |
1595 // | 1600 // |
1596 processDeclaration(List dartStyles) { | 1601 Declaration processDeclaration(List dartStyles) { |
1597 Declaration decl; | 1602 Declaration decl; |
1598 | 1603 |
1599 int start = _peekToken.start; | 1604 int start = _peekToken.start; |
1600 | 1605 |
1601 // IE7 hack of * before property name if so the property is IE7 or below. | 1606 // IE7 hack of * before property name if so the property is IE7 or below. |
1602 var ie7 = _peekKind(TokenKind.ASTERISK); | 1607 var ie7 = _peekKind(TokenKind.ASTERISK); |
1603 if (ie7) { | 1608 if (ie7) { |
1604 _next(); | 1609 _next(); |
1605 } | 1610 } |
1606 | 1611 |
(...skipping 23 matching lines...) Expand all Loading... |
1630 | 1635 |
1631 Expressions exprs = processExpr(); | 1636 Expressions exprs = processExpr(); |
1632 | 1637 |
1633 decl = new VarDefinition(definedName, exprs, _makeSpan(start)); | 1638 decl = new VarDefinition(definedName, exprs, _makeSpan(start)); |
1634 } else if (_peekToken.kind == TokenKind.DIRECTIVE_INCLUDE) { | 1639 } else if (_peekToken.kind == TokenKind.DIRECTIVE_INCLUDE) { |
1635 // @include mixinName in the declaration area. | 1640 // @include mixinName in the declaration area. |
1636 var span = _makeSpan(start); | 1641 var span = _makeSpan(start); |
1637 var include = processInclude(span, eatSemiColon: false); | 1642 var include = processInclude(span, eatSemiColon: false); |
1638 decl = new IncludeMixinAtDeclaration(include, span); | 1643 decl = new IncludeMixinAtDeclaration(include, span); |
1639 } else if (_peekToken.kind == TokenKind.DIRECTIVE_EXTEND) { | 1644 } else if (_peekToken.kind == TokenKind.DIRECTIVE_EXTEND) { |
1640 List<SimpleSelectorSequence> simpleSequences = []; | 1645 var simpleSequences = <TreeNode>[]; |
1641 | 1646 |
1642 _next(); | 1647 _next(); |
1643 var span = _makeSpan(start); | 1648 var span = _makeSpan(start); |
1644 var selector = simpleSelector(); | 1649 var selector = simpleSelector(); |
1645 if (selector == null) { | 1650 if (selector == null) { |
1646 _warning("@extends expecting simple selector name", span); | 1651 _warning("@extends expecting simple selector name", span); |
1647 } else { | 1652 } else { |
1648 simpleSequences.add(selector); | 1653 simpleSequences.add(selector); |
1649 } | 1654 } |
1650 if (_peekKind(TokenKind.COLON)) { | 1655 if (_peekKind(TokenKind.COLON)) { |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1723 'padding-top': _paddingPartTop, | 1728 'padding-top': _paddingPartTop, |
1724 'padding-right': _paddingPartRight, | 1729 'padding-right': _paddingPartRight, |
1725 'padding-bottom': _paddingPartBottom | 1730 'padding-bottom': _paddingPartBottom |
1726 }; | 1731 }; |
1727 | 1732 |
1728 static const Map<String, int> _nameToFontWeight = const { | 1733 static const Map<String, int> _nameToFontWeight = const { |
1729 'bold' : FontWeight.bold, | 1734 'bold' : FontWeight.bold, |
1730 'normal' : FontWeight.normal | 1735 'normal' : FontWeight.normal |
1731 }; | 1736 }; |
1732 | 1737 |
1733 static _findStyle(String styleName) { | 1738 static int _findStyle(String styleName) => _stylesToDart[styleName]; |
1734 if (_stylesToDart.containsKey(styleName)) { | |
1735 return _stylesToDart[styleName]; | |
1736 } | |
1737 } | |
1738 | 1739 |
1739 _styleForDart(Identifier property, Expressions exprs, List dartStyles) { | 1740 DartStyleExpression _styleForDart(Identifier property, Expressions exprs, |
1740 int styleType = _findStyle(property.name.toLowerCase()); | 1741 List dartStyles) { |
| 1742 var styleType = _findStyle(property.name.toLowerCase()); |
1741 if (styleType != null) { | 1743 if (styleType != null) { |
1742 return buildDartStyleNode(styleType, exprs, dartStyles); | 1744 return buildDartStyleNode(styleType, exprs, dartStyles); |
1743 } | 1745 } |
1744 } | 1746 } |
1745 | 1747 |
1746 FontExpression _mergeFontStyles(FontExpression fontExpr, List dartStyles) { | 1748 FontExpression _mergeFontStyles(FontExpression fontExpr, List dartStyles) { |
1747 // Merge all font styles for this class selector. | 1749 // Merge all font styles for this class selector. |
1748 for (var dartStyle in dartStyles) { | 1750 for (var dartStyle in dartStyles) { |
1749 if (dartStyle.isFont) { | 1751 if (dartStyle.isFont) { |
1750 fontExpr = new FontExpression.merge(dartStyle, fontExpr); | 1752 fontExpr = new FontExpression.merge(dartStyle, fontExpr); |
1751 } | 1753 } |
1752 } | 1754 } |
1753 | 1755 |
1754 return fontExpr; | 1756 return fontExpr; |
1755 } | 1757 } |
1756 | 1758 |
1757 buildDartStyleNode(int styleType, Expressions exprs, List dartStyles) { | 1759 DartStyleExpression buildDartStyleNode(int styleType, Expressions exprs, |
| 1760 List dartStyles) { |
| 1761 |
1758 switch (styleType) { | 1762 switch (styleType) { |
1759 /* | 1763 /* |
1760 * Properties in order: | 1764 * Properties in order: |
1761 * | 1765 * |
1762 * font-style font-variant font-weight font-size/line-height font-family | 1766 * font-style font-variant font-weight font-size/line-height font-family |
1763 * | 1767 * |
1764 * The font-size and font-family values are required. If other values are | 1768 * The font-size and font-family values are required. If other values are |
1765 * missing; a default, if it exist, will be used. | 1769 * missing; a default, if it exist, will be used. |
1766 */ | 1770 */ |
1767 case _fontPartFont: | 1771 case _fontPartFont: |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1890 } | 1894 } |
1891 break; | 1895 break; |
1892 default: | 1896 default: |
1893 // Don't handle it. | 1897 // Don't handle it. |
1894 return null; | 1898 return null; |
1895 } | 1899 } |
1896 } | 1900 } |
1897 | 1901 |
1898 // TODO(terry): Look at handling width of thin, thick, etc. any none numbers | 1902 // TODO(terry): Look at handling width of thin, thick, etc. any none numbers |
1899 // to convert to a number. | 1903 // to convert to a number. |
1900 processOneNumber(Expressions exprs, int part) { | 1904 DartStyleExpression processOneNumber(Expressions exprs, int part) { |
1901 var value = marginValue(exprs.expressions[0]); | 1905 var value = marginValue(exprs.expressions[0]); |
1902 if (value != null) { | 1906 if (value != null) { |
1903 switch (part) { | 1907 switch (part) { |
1904 case _marginPartLeft: | 1908 case _marginPartLeft: |
1905 return new MarginExpression(exprs.span, left: value); | 1909 return new MarginExpression(exprs.span, left: value); |
1906 case _marginPartTop: | 1910 case _marginPartTop: |
1907 return new MarginExpression(exprs.span, top: value); | 1911 return new MarginExpression(exprs.span, top: value); |
1908 case _marginPartRight: | 1912 case _marginPartRight: |
1909 return new MarginExpression(exprs.span, right: value); | 1913 return new MarginExpression(exprs.span, right: value); |
1910 case _marginPartBottom: | 1914 case _marginPartBottom: |
(...skipping 29 matching lines...) Expand all Loading... |
1940 /** | 1944 /** |
1941 * Margins are of the format: | 1945 * Margins are of the format: |
1942 * | 1946 * |
1943 * top,right,bottom,left (4 parameters) | 1947 * top,right,bottom,left (4 parameters) |
1944 * top,right/left, bottom (3 parameters) | 1948 * top,right/left, bottom (3 parameters) |
1945 * top/bottom,right/left (2 parameters) | 1949 * top/bottom,right/left (2 parameters) |
1946 * top/right/bottom/left (1 parameter) | 1950 * top/right/bottom/left (1 parameter) |
1947 * | 1951 * |
1948 * The values of the margins can be a unit or unitless or auto. | 1952 * The values of the margins can be a unit or unitless or auto. |
1949 */ | 1953 */ |
1950 processFourNums(Expressions exprs) { | 1954 BoxEdge processFourNums(Expressions exprs) { |
1951 num top; | 1955 num top; |
1952 num right; | 1956 num right; |
1953 num bottom; | 1957 num bottom; |
1954 num left; | 1958 num left; |
1955 | 1959 |
1956 int totalExprs = exprs.expressions.length; | 1960 int totalExprs = exprs.expressions.length; |
1957 switch (totalExprs) { | 1961 switch (totalExprs) { |
1958 case 1: | 1962 case 1: |
1959 top = marginValue(exprs.expressions[0]); | 1963 top = marginValue(exprs.expressions[0]); |
1960 right = top; | 1964 right = top; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1993 } | 1997 } |
1994 } | 1998 } |
1995 | 1999 |
1996 // Expression grammar: | 2000 // Expression grammar: |
1997 // | 2001 // |
1998 // expression: term [ operator? term]* | 2002 // expression: term [ operator? term]* |
1999 // | 2003 // |
2000 // operator: '/' | ',' | 2004 // operator: '/' | ',' |
2001 // term: (see processTerm) | 2005 // term: (see processTerm) |
2002 // | 2006 // |
2003 processExpr([bool ieFilter = false]) { | 2007 Expressions processExpr([bool ieFilter = false]) { |
2004 int start = _peekToken.start; | 2008 var start = _peekToken.start; |
2005 Expressions expressions = new Expressions(_makeSpan(start)); | 2009 var expressions = new Expressions(_makeSpan(start)); |
2006 | 2010 |
2007 bool keepGoing = true; | 2011 var keepGoing = true; |
2008 var expr; | 2012 var expr; |
2009 while (keepGoing && (expr = processTerm(ieFilter)) != null) { | 2013 while (keepGoing && (expr = processTerm(ieFilter)) != null) { |
2010 var op; | 2014 var op; |
2011 | 2015 |
2012 int opStart = _peekToken.start; | 2016 var opStart = _peekToken.start; |
2013 | 2017 |
2014 switch (_peek()) { | 2018 switch (_peek()) { |
2015 case TokenKind.SLASH: | 2019 case TokenKind.SLASH: |
2016 op = new OperatorSlash(_makeSpan(opStart)); | 2020 op = new OperatorSlash(_makeSpan(opStart)); |
2017 break; | 2021 break; |
2018 case TokenKind.COMMA: | 2022 case TokenKind.COMMA: |
2019 op = new OperatorComma(_makeSpan(opStart)); | 2023 op = new OperatorComma(_makeSpan(opStart)); |
2020 break; | 2024 break; |
2021 case TokenKind.BACKSLASH: | 2025 case TokenKind.BACKSLASH: |
2022 // Backslash outside of string; detected IE8 or older signaled by \9 at | 2026 // Backslash outside of string; detected IE8 or older signaled by \9 at |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2054 keepGoing = false; | 2058 keepGoing = false; |
2055 } else { | 2059 } else { |
2056 _next(); | 2060 _next(); |
2057 } | 2061 } |
2058 } | 2062 } |
2059 } | 2063 } |
2060 | 2064 |
2061 return expressions; | 2065 return expressions; |
2062 } | 2066 } |
2063 | 2067 |
2064 static int MAX_UNICODE = int.parse('0x10FFFF'); | 2068 static final int MAX_UNICODE = int.parse('0x10FFFF'); |
2065 | 2069 |
2066 // Term grammar: | 2070 // Term grammar: |
2067 // | 2071 // |
2068 // term: | 2072 // term: |
2069 // unary_operator? | 2073 // unary_operator? |
2070 // [ term_value ] | 2074 // [ term_value ] |
2071 // | STRING S* | IDENT S* | URI S* | UNICODERANGE S* | hexcolor | 2075 // | STRING S* | IDENT S* | URI S* | UNICODERANGE S* | hexcolor |
2072 // | 2076 // |
2073 // term_value: | 2077 // term_value: |
2074 // NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* | | 2078 // NUMBER S* | PERCENTAGE S* | LENGTH S* | EMS S* | EXS S* | ANGLE S* | |
2075 // TIME S* | FREQ S* | function | 2079 // TIME S* | FREQ S* | function |
2076 // | 2080 // |
2077 // NUMBER: {num} | 2081 // NUMBER: {num} |
2078 // PERCENTAGE: {num}% | 2082 // PERCENTAGE: {num}% |
2079 // LENGTH: {num}['px' | 'cm' | 'mm' | 'in' | 'pt' | 'pc'] | 2083 // LENGTH: {num}['px' | 'cm' | 'mm' | 'in' | 'pt' | 'pc'] |
2080 // EMS: {num}'em' | 2084 // EMS: {num}'em' |
2081 // EXS: {num}'ex' | 2085 // EXS: {num}'ex' |
2082 // ANGLE: {num}['deg' | 'rad' | 'grad'] | 2086 // ANGLE: {num}['deg' | 'rad' | 'grad'] |
2083 // TIME: {num}['ms' | 's'] | 2087 // TIME: {num}['ms' | 's'] |
2084 // FREQ: {num}['hz' | 'khz'] | 2088 // FREQ: {num}['hz' | 'khz'] |
2085 // function: IDENT '(' expr ')' | 2089 // function: IDENT '(' expr ')' |
2086 // | 2090 // |
2087 processTerm([bool ieFilter = false]) { | 2091 processTerm([bool ieFilter = false]) { |
2088 int start = _peekToken.start; | 2092 var start = _peekToken.start; |
2089 Token t; // token for term's value | 2093 Token t; // token for term's value |
2090 var value; // value of term (numeric values) | 2094 var value; // value of term (numeric values) |
2091 | 2095 |
2092 var unary = ""; | 2096 var unary = ""; |
2093 switch (_peek()) { | 2097 switch (_peek()) { |
2094 case TokenKind.HASH: | 2098 case TokenKind.HASH: |
2095 this._eat(TokenKind.HASH); | 2099 this._eat(TokenKind.HASH); |
2096 if (!_anyWhiteSpaceBeforePeekToken(TokenKind.HASH)) { | 2100 if (!_anyWhiteSpaceBeforePeekToken(TokenKind.HASH)) { |
2097 String hexText; | 2101 String hexText; |
2098 if (_peekKind(TokenKind.INTEGER)) { | 2102 if (_peekKind(TokenKind.INTEGER)) { |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2186 var propName = nameValue.name; | 2190 var propName = nameValue.name; |
2187 var errMsg = TokenKind.isPredefinedName(propName) ? | 2191 var errMsg = TokenKind.isPredefinedName(propName) ? |
2188 "Improper use of property value ${propName}" : | 2192 "Improper use of property value ${propName}" : |
2189 "Unknown property value ${propName}"; | 2193 "Unknown property value ${propName}"; |
2190 _warning(errMsg, _makeSpan(start)); | 2194 _warning(errMsg, _makeSpan(start)); |
2191 } | 2195 } |
2192 return new LiteralTerm(nameValue, nameValue.name, _makeSpan(start)); | 2196 return new LiteralTerm(nameValue, nameValue.name, _makeSpan(start)); |
2193 } | 2197 } |
2194 | 2198 |
2195 // Yes, process the color as an RGB value. | 2199 // Yes, process the color as an RGB value. |
2196 String rgbColor = TokenKind.decimalToHex( | 2200 var rgbColor = |
2197 TokenKind.colorValue(colorEntry), 6); | 2201 TokenKind.decimalToHex(TokenKind.colorValue(colorEntry), 6); |
2198 return _parseHex(rgbColor, _makeSpan(start)); | 2202 return _parseHex(rgbColor, _makeSpan(start)); |
2199 case TokenKind.UNICODE_RANGE: | 2203 case TokenKind.UNICODE_RANGE: |
2200 var first; | 2204 var first; |
2201 var second; | 2205 var second; |
2202 var firstNumber; | 2206 var firstNumber; |
2203 var secondNumber; | 2207 var secondNumber; |
2204 _eat(TokenKind.UNICODE_RANGE, unicodeRange: true); | 2208 _eat(TokenKind.UNICODE_RANGE, unicodeRange: true); |
2205 if (_maybeEat(TokenKind.HEX_INTEGER, unicodeRange: true)) { | 2209 if (_maybeEat(TokenKind.HEX_INTEGER, unicodeRange: true)) { |
2206 first = _previousToken.text; | 2210 first = _previousToken.text; |
2207 firstNumber = int.parse('0x$first'); | 2211 firstNumber = int.parse('0x$first'); |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2241 expr.expressions[0] = varUsage; | 2245 expr.expressions[0] = varUsage; |
2242 return expr.expressions; | 2246 return expr.expressions; |
2243 } | 2247 } |
2244 break; | 2248 break; |
2245 } | 2249 } |
2246 | 2250 |
2247 return processDimension(t, value, _makeSpan(start)); | 2251 return processDimension(t, value, _makeSpan(start)); |
2248 } | 2252 } |
2249 | 2253 |
2250 /** Process all dimension units. */ | 2254 /** Process all dimension units. */ |
2251 processDimension(Token t, var value, Span span) { | 2255 LiteralTerm processDimension(Token t, var value, Span span) { |
2252 var term; | 2256 LiteralTerm term; |
2253 var unitType = this._peek(); | 2257 var unitType = this._peek(); |
2254 | 2258 |
2255 switch (unitType) { | 2259 switch (unitType) { |
2256 case TokenKind.UNIT_EM: | 2260 case TokenKind.UNIT_EM: |
2257 term = new EmTerm(value, t.text, span); | 2261 term = new EmTerm(value, t.text, span); |
2258 _next(); // Skip the unit | 2262 _next(); // Skip the unit |
2259 break; | 2263 break; |
2260 case TokenKind.UNIT_EX: | 2264 case TokenKind.UNIT_EX: |
2261 term = new ExTerm(value, t.text, span); | 2265 term = new ExTerm(value, t.text, span); |
2262 _next(); // Skip the unit | 2266 _next(); // Skip the unit |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2321 term = (value is Identifier) | 2325 term = (value is Identifier) |
2322 ? new LiteralTerm(value, value.name, span) | 2326 ? new LiteralTerm(value, value.name, span) |
2323 : new NumberTerm(value, t.text, span); | 2327 : new NumberTerm(value, t.text, span); |
2324 } | 2328 } |
2325 break; | 2329 break; |
2326 } | 2330 } |
2327 | 2331 |
2328 return term; | 2332 return term; |
2329 } | 2333 } |
2330 | 2334 |
2331 processQuotedString([bool urlString = false]) { | 2335 String processQuotedString([bool urlString = false]) { |
2332 int start = _peekToken.start; | 2336 var start = _peekToken.start; |
2333 | 2337 |
2334 // URI term sucks up everything inside of quotes(' or ") or between parens | 2338 // URI term sucks up everything inside of quotes(' or ") or between parens |
2335 int stopToken = urlString ? TokenKind.RPAREN : -1; | 2339 var stopToken = urlString ? TokenKind.RPAREN : -1; |
2336 switch (_peek()) { | 2340 switch (_peek()) { |
2337 case TokenKind.SINGLE_QUOTE: | 2341 case TokenKind.SINGLE_QUOTE: |
2338 stopToken = TokenKind.SINGLE_QUOTE; | 2342 stopToken = TokenKind.SINGLE_QUOTE; |
2339 start = _peekToken.start + 1; // Skip the quote might have whitespace. | 2343 start = _peekToken.start + 1; // Skip the quote might have whitespace. |
2340 _next(); // Skip the SINGLE_QUOTE. | 2344 _next(); // Skip the SINGLE_QUOTE. |
2341 break; | 2345 break; |
2342 case TokenKind.DOUBLE_QUOTE: | 2346 case TokenKind.DOUBLE_QUOTE: |
2343 stopToken = TokenKind.DOUBLE_QUOTE; | 2347 stopToken = TokenKind.DOUBLE_QUOTE; |
2344 start = _peekToken.start + 1; // Skip the quote might have whitespace. | 2348 start = _peekToken.start + 1; // Skip the quote might have whitespace. |
2345 _next(); // Skip the DOUBLE_QUOTE. | 2349 _next(); // Skip the DOUBLE_QUOTE. |
2346 break; | 2350 break; |
2347 default: | 2351 default: |
2348 if (urlString) { | 2352 if (urlString) { |
2349 if (_peek() == TokenKind.LPAREN) { | 2353 if (_peek() == TokenKind.LPAREN) { |
2350 _next(); // Skip the LPAREN. | 2354 _next(); // Skip the LPAREN. |
2351 start = _peekToken.start; | 2355 start = _peekToken.start; |
2352 } | 2356 } |
2353 stopToken = TokenKind.RPAREN; | 2357 stopToken = TokenKind.RPAREN; |
2354 } else { | 2358 } else { |
2355 _error('unexpected string', _makeSpan(start)); | 2359 _error('unexpected string', _makeSpan(start)); |
2356 } | 2360 } |
2357 break; | 2361 break; |
2358 } | 2362 } |
2359 | 2363 |
2360 // Gobble up everything until we hit our stop token. | 2364 // Gobble up everything until we hit our stop token. |
2361 int runningStart = _peekToken.start; | 2365 var runningStart = _peekToken.start; |
2362 while (_peek() != stopToken && _peek() != TokenKind.END_OF_FILE) { | 2366 while (_peek() != stopToken && _peek() != TokenKind.END_OF_FILE) { |
2363 var tok = _next(); | 2367 var tok = _next(); |
2364 } | 2368 } |
2365 | 2369 |
2366 // All characters between quotes is the string. | 2370 // All characters between quotes is the string. |
2367 int end = _peekToken.end; | 2371 var end = _peekToken.end; |
2368 var stringValue = (_peekToken.span as FileSpan).file.getText(start, | 2372 var stringValue = (_peekToken.span as FileSpan).file.getText(start, |
2369 end - 1); | 2373 end - 1); |
2370 | 2374 |
2371 if (stopToken != TokenKind.RPAREN) { | 2375 if (stopToken != TokenKind.RPAREN) { |
2372 _next(); // Skip the SINGLE_QUOTE or DOUBLE_QUOTE; | 2376 _next(); // Skip the SINGLE_QUOTE or DOUBLE_QUOTE; |
2373 } | 2377 } |
2374 | 2378 |
2375 return stringValue; | 2379 return stringValue; |
2376 } | 2380 } |
2377 | 2381 |
2378 // TODO(terry): Should probably understand IE's non-standard filter syntax to | 2382 // TODO(terry): Should probably understand IE's non-standard filter syntax to |
2379 // fully support calc, var(), etc. | 2383 // fully support calc, var(), etc. |
2380 /** | 2384 /** |
2381 * IE's filter property breaks CSS value parsing. IE's format can be: | 2385 * IE's filter property breaks CSS value parsing. IE's format can be: |
2382 * | 2386 * |
2383 * filter: progid:DXImageTransform.MS.gradient(Type=0, Color='#9d8b83'); | 2387 * filter: progid:DXImageTransform.MS.gradient(Type=0, Color='#9d8b83'); |
2384 * | 2388 * |
2385 * We'll just parse everything after the 'progid:' look for the left paren | 2389 * We'll just parse everything after the 'progid:' look for the left paren |
2386 * then parse to the right paren ignoring everything in between. | 2390 * then parse to the right paren ignoring everything in between. |
2387 */ | 2391 */ |
2388 processIEFilter(int startAfterProgidColon) { | 2392 processIEFilter(int startAfterProgidColon) { |
2389 int parens = 0; | 2393 var parens = 0; |
2390 | 2394 |
2391 while (_peek() != TokenKind.END_OF_FILE) { | 2395 while (_peek() != TokenKind.END_OF_FILE) { |
2392 switch (_peek()) { | 2396 switch (_peek()) { |
2393 case TokenKind.LPAREN: | 2397 case TokenKind.LPAREN: |
2394 _eat(TokenKind.LPAREN); | 2398 _eat(TokenKind.LPAREN); |
2395 parens++; | 2399 parens++; |
2396 break; | 2400 break; |
2397 case TokenKind.RPAREN: | 2401 case TokenKind.RPAREN: |
2398 _eat(TokenKind.RPAREN); | 2402 _eat(TokenKind.RPAREN); |
2399 if (--parens == 0) { | 2403 if (--parens == 0) { |
2400 var tok = tokenizer.makeIEFilter(startAfterProgidColon, | 2404 var tok = tokenizer.makeIEFilter(startAfterProgidColon, |
2401 _peekToken.start); | 2405 _peekToken.start); |
2402 return new LiteralTerm(tok.text, tok.text, tok.span); | 2406 return new LiteralTerm(tok.text, tok.text, tok.span); |
2403 } | 2407 } |
2404 break; | 2408 break; |
2405 default: | 2409 default: |
2406 _eat(_peek()); | 2410 _eat(_peek()); |
2407 } | 2411 } |
2408 } | 2412 } |
2409 } | 2413 } |
2410 | 2414 |
2411 // Function grammar: | 2415 // Function grammar: |
2412 // | 2416 // |
2413 // function: IDENT '(' expr ')' | 2417 // function: IDENT '(' expr ')' |
2414 // | 2418 // |
2415 processFunction(Identifier func) { | 2419 processFunction(Identifier func) { |
2416 int start = _peekToken.start; | 2420 var start = _peekToken.start; |
2417 | 2421 |
2418 String name = func.name; | 2422 var name = func.name; |
2419 | 2423 |
2420 switch (name) { | 2424 switch (name) { |
2421 case 'url': | 2425 case 'url': |
2422 // URI term sucks up everything inside of quotes(' or ") or between parens | 2426 // URI term sucks up everything inside of quotes(' or ") or between parens |
2423 String urlParam = processQuotedString(true); | 2427 var urlParam = processQuotedString(true); |
2424 | 2428 |
2425 // TODO(terry): Better error messge and checking for mismatched quotes. | 2429 // TODO(terry): Better error messge and checking for mismatched quotes. |
2426 if (_peek() == TokenKind.END_OF_FILE) { | 2430 if (_peek() == TokenKind.END_OF_FILE) { |
2427 _error("problem parsing URI", _peekToken.span); | 2431 _error("problem parsing URI", _peekToken.span); |
2428 } | 2432 } |
2429 | 2433 |
2430 if (_peek() == TokenKind.RPAREN) { | 2434 if (_peek() == TokenKind.RPAREN) { |
2431 _next(); | 2435 _next(); |
2432 } | 2436 } |
2433 | 2437 |
(...skipping 29 matching lines...) Expand all Loading... |
2463 if (!_maybeEat(TokenKind.RPAREN)) { | 2467 if (!_maybeEat(TokenKind.RPAREN)) { |
2464 _error("problem parsing function expected ), ", _peekToken.span); | 2468 _error("problem parsing function expected ), ", _peekToken.span); |
2465 } | 2469 } |
2466 | 2470 |
2467 return new FunctionTerm(name, name, expr, _makeSpan(start)); | 2471 return new FunctionTerm(name, name, expr, _makeSpan(start)); |
2468 } | 2472 } |
2469 | 2473 |
2470 return null; | 2474 return null; |
2471 } | 2475 } |
2472 | 2476 |
2473 identifier() { | 2477 Identifier identifier() { |
2474 var tok = _next(); | 2478 var tok = _next(); |
2475 | 2479 |
2476 if (!TokenKind.isIdentifier(tok.kind) && | 2480 if (!TokenKind.isIdentifier(tok.kind) && |
2477 !TokenKind.isKindIdentifier(tok.kind)) { | 2481 !TokenKind.isKindIdentifier(tok.kind)) { |
2478 if (isChecked) { | 2482 if (isChecked) { |
2479 _warning('expected identifier, but found $tok', tok.span); | 2483 _warning('expected identifier, but found $tok', tok.span); |
2480 } | 2484 } |
2481 return new Identifier("", _makeSpan(tok.start)); | 2485 return new Identifier("", _makeSpan(tok.start)); |
2482 } | 2486 } |
2483 | 2487 |
2484 return new Identifier(tok.text, _makeSpan(tok.start)); | 2488 return new Identifier(tok.text, _makeSpan(tok.start)); |
2485 } | 2489 } |
2486 | 2490 |
2487 // TODO(terry): Move this to base <= 36 and into shared code. | 2491 // TODO(terry): Move this to base <= 36 and into shared code. |
2488 static int _hexDigit(int c) { | 2492 static int _hexDigit(int c) { |
2489 if(c >= 48/*0*/ && c <= 57/*9*/) { | 2493 if (c >= 48/*0*/ && c <= 57/*9*/) { |
2490 return c - 48; | 2494 return c - 48; |
2491 } else if (c >= 97/*a*/ && c <= 102/*f*/) { | 2495 } else if (c >= 97/*a*/ && c <= 102/*f*/) { |
2492 return c - 87; | 2496 return c - 87; |
2493 } else if (c >= 65/*A*/ && c <= 70/*F*/) { | 2497 } else if (c >= 65/*A*/ && c <= 70/*F*/) { |
2494 return c - 55; | 2498 return c - 55; |
2495 } else { | 2499 } else { |
2496 return -1; | 2500 return -1; |
2497 } | 2501 } |
2498 } | 2502 } |
2499 | 2503 |
2500 HexColorTerm _parseHex(String hexText, Span span) { | 2504 HexColorTerm _parseHex(String hexText, Span span) { |
2501 int hexValue = 0; | 2505 var hexValue = 0; |
2502 | 2506 |
2503 for (int i = 0; i < hexText.length; i++) { | 2507 for (var i = 0; i < hexText.length; i++) { |
2504 var digit = _hexDigit(hexText.codeUnitAt(i)); | 2508 var digit = _hexDigit(hexText.codeUnitAt(i)); |
2505 if (digit < 0) { | 2509 if (digit < 0) { |
2506 _warning('Bad hex number', span); | 2510 _warning('Bad hex number', span); |
2507 return new HexColorTerm(new BAD_HEX_VALUE(), hexText, span); | 2511 return new HexColorTerm(new BAD_HEX_VALUE(), hexText, span); |
2508 } | 2512 } |
2509 hexValue = (hexValue << 4) + digit; | 2513 hexValue = (hexValue << 4) + digit; |
2510 } | 2514 } |
2511 | 2515 |
2512 // Make 3 character hex value #RRGGBB => #RGB iff: | 2516 // Make 3 character hex value #RRGGBB => #RGB iff: |
2513 // high/low nibble of RR is the same, high/low nibble of GG is the same and | 2517 // high/low nibble of RR is the same, high/low nibble of GG is the same and |
(...skipping 14 matching lines...) Expand all Loading... |
2528 } | 2532 } |
2529 } | 2533 } |
2530 | 2534 |
2531 class ExpressionsProcessor { | 2535 class ExpressionsProcessor { |
2532 final Expressions _exprs; | 2536 final Expressions _exprs; |
2533 int _index = 0; | 2537 int _index = 0; |
2534 | 2538 |
2535 ExpressionsProcessor(this._exprs); | 2539 ExpressionsProcessor(this._exprs); |
2536 | 2540 |
2537 // TODO(terry): Only handles ##px unit. | 2541 // TODO(terry): Only handles ##px unit. |
2538 processFontSize() { | 2542 FontExpression processFontSize() { |
2539 /* font-size[/line-height] | 2543 /* font-size[/line-height] |
2540 * | 2544 * |
2541 * Possible size values: | 2545 * Possible size values: |
2542 * xx-small | 2546 * xx-small |
2543 * small | 2547 * small |
2544 * medium [default] | 2548 * medium [default] |
2545 * large | 2549 * large |
2546 * x-large | 2550 * x-large |
2547 * xx-large | 2551 * xx-large |
2548 * smaller | 2552 * smaller |
2549 * larger | 2553 * larger |
2550 * ##length in px, pt, etc. | 2554 * ##length in px, pt, etc. |
2551 * ##%, percent of parent elem's font-size | 2555 * ##%, percent of parent elem's font-size |
2552 * inherit | 2556 * inherit |
2553 */ | 2557 */ |
2554 LengthTerm size; | 2558 LengthTerm size; |
2555 LineHeight lineHt; | 2559 LineHeight lineHt; |
2556 bool nextIsLineHeight = false; | 2560 var nextIsLineHeight = false; |
2557 for (; _index < _exprs.expressions.length; _index++) { | 2561 for (; _index < _exprs.expressions.length; _index++) { |
2558 var expr = _exprs.expressions[_index]; | 2562 var expr = _exprs.expressions[_index]; |
2559 if (size == null && expr is LengthTerm) { | 2563 if (size == null && expr is LengthTerm) { |
2560 // font-size part. | 2564 // font-size part. |
2561 size = expr; | 2565 size = expr; |
2562 } else if (size != null) { | 2566 } else if (size != null) { |
2563 if (expr is OperatorSlash) { | 2567 if (expr is OperatorSlash) { |
2564 // LineHeight could follow? | 2568 // LineHeight could follow? |
2565 nextIsLineHeight = true; | 2569 nextIsLineHeight = true; |
2566 } else if (nextIsLineHeight && expr is LengthTerm) { | 2570 } else if (nextIsLineHeight && expr is LengthTerm) { |
2567 assert(expr.unit == TokenKind.UNIT_LENGTH_PX); | 2571 assert(expr.unit == TokenKind.UNIT_LENGTH_PX); |
2568 lineHt = new LineHeight(expr.value, inPixels: true); | 2572 lineHt = new LineHeight(expr.value, inPixels: true); |
2569 nextIsLineHeight = false; | 2573 nextIsLineHeight = false; |
2570 _index++; | 2574 _index++; |
2571 break; | 2575 break; |
2572 } else { | 2576 } else { |
2573 break; | 2577 break; |
2574 } | 2578 } |
2575 } else { | 2579 } else { |
2576 break; | 2580 break; |
2577 } | 2581 } |
2578 } | 2582 } |
2579 | 2583 |
2580 return new FontExpression(_exprs.span, size: size, lineHeight: lineHt); | 2584 return new FontExpression(_exprs.span, size: size, lineHeight: lineHt); |
2581 } | 2585 } |
2582 | 2586 |
2583 processFontFamily() { | 2587 FontExpression processFontFamily() { |
2584 final List<String> family = <String>[]; | 2588 var family = <String>[]; |
2585 | 2589 |
2586 /* Possible family values: | 2590 /* Possible family values: |
2587 * font-family: arial, Times new roman ,Lucida Sans Unicode,Courier; | 2591 * font-family: arial, Times new roman ,Lucida Sans Unicode,Courier; |
2588 * font-family: "Times New Roman", arial, Lucida Sans Unicode, Courier; | 2592 * font-family: "Times New Roman", arial, Lucida Sans Unicode, Courier; |
2589 */ | 2593 */ |
2590 bool moreFamilies = false; | 2594 var moreFamilies = false; |
2591 | 2595 |
2592 for (; _index < _exprs.expressions.length; _index++) { | 2596 for (; _index < _exprs.expressions.length; _index++) { |
2593 Expression expr = _exprs.expressions[_index]; | 2597 Expression expr = _exprs.expressions[_index]; |
2594 if (expr is LiteralTerm) { | 2598 if (expr is LiteralTerm) { |
2595 if (family.length == 0 || moreFamilies) { | 2599 if (family.length == 0 || moreFamilies) { |
2596 // It's font-family now. | 2600 // It's font-family now. |
2597 family.add(expr.toString()); | 2601 family.add(expr.toString()); |
2598 moreFamilies = false; | 2602 moreFamilies = false; |
2599 } else if (isChecked) { | 2603 } else if (isChecked) { |
2600 messages.warning('Only font-family can be a list', _exprs.span); | 2604 messages.warning('Only font-family can be a list', _exprs.span); |
2601 } | 2605 } |
2602 } else if (expr is OperatorComma && family.length > 0) { | 2606 } else if (expr is OperatorComma && family.length > 0) { |
2603 moreFamilies = true; | 2607 moreFamilies = true; |
2604 } else { | 2608 } else { |
2605 break; | 2609 break; |
2606 } | 2610 } |
2607 } | 2611 } |
2608 | 2612 |
2609 return new FontExpression(_exprs.span, family: family); | 2613 return new FontExpression(_exprs.span, family: family); |
2610 } | 2614 } |
2611 | 2615 |
2612 processFont() { | 2616 FontExpression processFont() { |
2613 var family; | 2617 List<String> family; |
2614 | 2618 |
2615 // Process all parts of the font expression. | 2619 // Process all parts of the font expression. |
2616 FontExpression fontSize; | 2620 FontExpression fontSize; |
2617 FontExpression fontFamily; | 2621 FontExpression fontFamily; |
2618 for (; _index < _exprs.expressions.length; _index++) { | 2622 for (; _index < _exprs.expressions.length; _index++) { |
2619 var expr = _exprs.expressions[_index]; | 2623 var expr = _exprs.expressions[_index]; |
2620 // Order is font-size font-family | 2624 // Order is font-size font-family |
2621 if (fontSize == null) { | 2625 if (fontSize == null) { |
2622 fontSize = processFontSize(); | 2626 fontSize = processFontSize(); |
2623 } | 2627 } |
(...skipping 14 matching lines...) Expand all Loading... |
2638 } | 2642 } |
2639 | 2643 |
2640 /** | 2644 /** |
2641 * Escapes [text] for use in a CSS string. | 2645 * Escapes [text] for use in a CSS string. |
2642 * [single] specifies single quote `'` vs double quote `"`. | 2646 * [single] specifies single quote `'` vs double quote `"`. |
2643 */ | 2647 */ |
2644 String _escapeString(String text, {bool single: false}) { | 2648 String _escapeString(String text, {bool single: false}) { |
2645 StringBuffer result = null; | 2649 StringBuffer result = null; |
2646 | 2650 |
2647 for (int i = 0; i < text.length; i++) { | 2651 for (int i = 0; i < text.length; i++) { |
2648 int code = text.codeUnitAt(i); | 2652 var code = text.codeUnitAt(i); |
2649 var replace = null; | 2653 String replace = null; |
2650 switch (code) { | 2654 switch (code) { |
2651 case 34/*'"'*/: if (!single) replace = r'\"'; break; | 2655 case 34/*'"'*/: if (!single) replace = r'\"'; break; |
2652 case 39/*"'"*/: if (single) replace = r"\'"; break; | 2656 case 39/*"'"*/: if (single) replace = r"\'"; break; |
2653 } | 2657 } |
2654 | 2658 |
2655 if (replace != null && result == null) { | 2659 if (replace != null && result == null) { |
2656 result = new StringBuffer(text.substring(0, i)); | 2660 result = new StringBuffer(text.substring(0, i)); |
2657 } | 2661 } |
2658 | 2662 |
2659 if (result != null) result.write(replace != null ? replace : text[i]); | 2663 if (result != null) result.write(replace != null ? replace : text[i]); |
2660 } | 2664 } |
2661 | 2665 |
2662 return result == null ? text : result.toString(); | 2666 return result == null ? text : result.toString(); |
2663 } | 2667 } |
OLD | NEW |