Commit | Line | Data |
---|---|---|
fbe18cc0 FM |
1 | <?php |
2 | ||
3 | namespace Sabberworm\CSS\Value; | |
4 | ||
376eb156 MM |
5 | use Sabberworm\CSS\Parsing\ParserState; |
6 | use Sabberworm\CSS\Parsing\UnexpectedTokenException; | |
fbe18cc0 FM |
7 | use Sabberworm\CSS\Renderable; |
8 | ||
9 | abstract class Value implements Renderable { | |
376eb156 MM |
10 | protected $iLineNo; |
11 | ||
12 | public function __construct($iLineNo = 0) { | |
13 | $this->iLineNo = $iLineNo; | |
14 | } | |
15 | ||
16 | public static function parseValue(ParserState $oParserState, $aListDelimiters = array()) { | |
17 | $aStack = array(); | |
18 | $oParserState->consumeWhiteSpace(); | |
19 | //Build a list of delimiters and parsed values | |
20 | while (!($oParserState->comes('}') || $oParserState->comes(';') || $oParserState->comes('!') || $oParserState->comes(')') || $oParserState->comes('\\'))) { | |
21 | if (count($aStack) > 0) { | |
22 | $bFoundDelimiter = false; | |
23 | foreach ($aListDelimiters as $sDelimiter) { | |
24 | if ($oParserState->comes($sDelimiter)) { | |
25 | array_push($aStack, $oParserState->consume($sDelimiter)); | |
26 | $oParserState->consumeWhiteSpace(); | |
27 | $bFoundDelimiter = true; | |
28 | break; | |
29 | } | |
30 | } | |
31 | if (!$bFoundDelimiter) { | |
32 | //Whitespace was the list delimiter | |
33 | array_push($aStack, ' '); | |
34 | } | |
35 | } | |
36 | array_push($aStack, self::parsePrimitiveValue($oParserState)); | |
37 | $oParserState->consumeWhiteSpace(); | |
38 | } | |
39 | //Convert the list to list objects | |
40 | foreach ($aListDelimiters as $sDelimiter) { | |
41 | if (count($aStack) === 1) { | |
42 | return $aStack[0]; | |
43 | } | |
44 | $iStartPosition = null; | |
45 | while (($iStartPosition = array_search($sDelimiter, $aStack, true)) !== false) { | |
46 | $iLength = 2; //Number of elements to be joined | |
47 | for ($i = $iStartPosition + 2; $i < count($aStack); $i+=2, ++$iLength) { | |
48 | if ($sDelimiter !== $aStack[$i]) { | |
49 | break; | |
50 | } | |
51 | } | |
52 | $oList = new RuleValueList($sDelimiter, $oParserState->currentLine()); | |
53 | for ($i = $iStartPosition - 1; $i - $iStartPosition + 1 < $iLength * 2; $i+=2) { | |
54 | $oList->addListComponent($aStack[$i]); | |
55 | } | |
56 | array_splice($aStack, $iStartPosition - 1, $iLength * 2 - 1, array($oList)); | |
57 | } | |
58 | } | |
59 | if (!isset($aStack[0])) { | |
60 | throw new UnexpectedTokenException(" {$oParserState->peek()} ", $oParserState->peek(1, -1) . $oParserState->peek(2), 'literal', $oParserState->currentLine()); | |
61 | } | |
62 | return $aStack[0]; | |
63 | } | |
64 | ||
65 | public static function parseIdentifierOrFunction(ParserState $oParserState, $bIgnoreCase = false) { | |
66 | $sResult = $oParserState->parseIdentifier($bIgnoreCase); | |
67 | ||
68 | if ($oParserState->comes('(')) { | |
69 | $oParserState->consume('('); | |
70 | $aArguments = Value::parseValue($oParserState, array('=', ' ', ',')); | |
71 | $sResult = new CSSFunction($sResult, $aArguments, ',', $oParserState->currentLine()); | |
72 | $oParserState->consume(')'); | |
73 | } | |
74 | ||
75 | return $sResult; | |
76 | } | |
77 | ||
78 | public static function parsePrimitiveValue(ParserState $oParserState) { | |
79 | $oValue = null; | |
80 | $oParserState->consumeWhiteSpace(); | |
81 | if (is_numeric($oParserState->peek()) || ($oParserState->comes('-.') && is_numeric($oParserState->peek(1, 2))) || (($oParserState->comes('-') || $oParserState->comes('.')) && is_numeric($oParserState->peek(1, 1)))) { | |
82 | $oValue = Size::parse($oParserState); | |
83 | } else if ($oParserState->comes('#') || $oParserState->comes('rgb', true) || $oParserState->comes('hsl', true)) { | |
84 | $oValue = Color::parse($oParserState); | |
85 | } else if ($oParserState->comes('url', true)) { | |
86 | $oValue = URL::parse($oParserState); | |
87 | } else if ($oParserState->comes('calc', true) || $oParserState->comes('-webkit-calc', true) || $oParserState->comes('-moz-calc', true)) { | |
88 | $oValue = CalcFunction::parse($oParserState); | |
89 | } else if ($oParserState->comes("'") || $oParserState->comes('"')) { | |
90 | $oValue = CSSString::parse($oParserState); | |
91 | } else if ($oParserState->comes("progid:") && $oParserState->getSettings()->bLenientParsing) { | |
92 | $oValue = self::parseMicrosoftFilter($oParserState); | |
93 | } else if ($oParserState->comes("[")) { | |
94 | $oValue = LineName::parse($oParserState); | |
95 | } else if ($oParserState->comes("U+")) { | |
96 | $oValue = self::parseUnicodeRangeValue($oParserState); | |
97 | } else { | |
98 | $oValue = self::parseIdentifierOrFunction($oParserState); | |
99 | } | |
100 | $oParserState->consumeWhiteSpace(); | |
101 | return $oValue; | |
102 | } | |
103 | ||
104 | private static function parseMicrosoftFilter(ParserState $oParserState) { | |
105 | $sFunction = $oParserState->consumeUntil('(', false, true); | |
106 | $aArguments = Value::parseValue($oParserState, array(',', '=')); | |
107 | return new CSSFunction($sFunction, $aArguments, ',', $oParserState->currentLine()); | |
108 | } | |
109 | ||
110 | private static function parseUnicodeRangeValue(ParserState $oParserState) { | |
111 | $iCodepointMaxLenth = 6; // Code points outside BMP can use up to six digits | |
112 | $sRange = ""; | |
113 | $oParserState->consume("U+"); | |
114 | do { | |
115 | if ($oParserState->comes('-')) $iCodepointMaxLenth = 13; // Max length is 2 six digit code points + the dash(-) between them | |
116 | $sRange .= $oParserState->consume(1); | |
117 | } while (strlen($sRange) < $iCodepointMaxLenth && preg_match("/[A-Fa-f0-9\?-]/", $oParserState->peek())); | |
118 | return "U+{$sRange}"; | |
119 | } | |
120 | ||
121 | /** | |
122 | * @return int | |
123 | */ | |
124 | public function getLineNo() { | |
125 | return $this->iLineNo; | |
126 | } | |
127 | ||
128 | //Methods are commented out because re-declaring them here is a fatal error in PHP < 5.3.9 | |
fbe18cc0 FM |
129 | //public abstract function __toString(); |
130 | //public abstract function render(\Sabberworm\CSS\OutputFormat $oOutputFormat); | |
131 | } |