Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 1
<?php
2
 
3
namespace Sabberworm\CSS\Value;
4
 
5
use Sabberworm\CSS\Parsing\ParserState;
6
use Sabberworm\CSS\Parsing\SourceException;
7
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
8
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
9
use Sabberworm\CSS\Renderable;
10
 
11
/**
12
 * Abstract base class for specific classes of CSS values: `Size`, `Color`, `CSSString` and `URL`, and another
13
 * abstract subclass `ValueList`.
14
 */
15
abstract class Value implements Renderable
16
{
17
    /**
18
     * @var int
19
     */
20
    protected $iLineNo;
21
 
22
    /**
23
     * @param int $iLineNo
24
     */
25
    public function __construct($iLineNo = 0)
26
    {
27
        $this->iLineNo = $iLineNo;
28
    }
29
 
30
    /**
31
     * @param array<array-key, string> $aListDelimiters
32
     *
33
     * @return RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string
34
     *
35
     * @throws UnexpectedTokenException
36
     * @throws UnexpectedEOFException
37
     */
38
    public static function parseValue(ParserState $oParserState, array $aListDelimiters = [])
39
    {
40
        /** @var array<int, RuleValueList|CSSFunction|CSSString|LineName|Size|URL|string> $aStack */
41
        $aStack = [];
42
        $oParserState->consumeWhiteSpace();
43
        //Build a list of delimiters and parsed values
44
        while (
45
            !($oParserState->comes('}') || $oParserState->comes(';') || $oParserState->comes('!')
46
                || $oParserState->comes(')')
47
                || $oParserState->comes('\\')
48
                || $oParserState->isEnd())
49
        ) {
50
            if (count($aStack) > 0) {
51
                $bFoundDelimiter = false;
52
                foreach ($aListDelimiters as $sDelimiter) {
53
                    if ($oParserState->comes($sDelimiter)) {
54
                        array_push($aStack, $oParserState->consume($sDelimiter));
55
                        $oParserState->consumeWhiteSpace();
56
                        $bFoundDelimiter = true;
57
                        break;
58
                    }
59
                }
60
                if (!$bFoundDelimiter) {
61
                    //Whitespace was the list delimiter
62
                    array_push($aStack, ' ');
63
                }
64
            }
65
            array_push($aStack, self::parsePrimitiveValue($oParserState));
66
            $oParserState->consumeWhiteSpace();
67
        }
68
        // Convert the list to list objects
69
        foreach ($aListDelimiters as $sDelimiter) {
70
            $iStackLength = count($aStack);
71
            if ($iStackLength === 1) {
72
                return $aStack[0];
73
            }
74
            $aNewStack = [];
75
            for ($iStartPosition = 0; $iStartPosition < $iStackLength; ++$iStartPosition) {
76
                if ($iStartPosition === ($iStackLength - 1) || $sDelimiter !== $aStack[$iStartPosition + 1]) {
77
                    $aNewStack[] = $aStack[$iStartPosition];
78
                    continue;
79
                }
80
                $iLength = 2; //Number of elements to be joined
81
                for ($i = $iStartPosition + 3; $i < $iStackLength; $i += 2, ++$iLength) {
82
                    if ($sDelimiter !== $aStack[$i]) {
83
                        break;
84
                    }
85
                }
86
                $oList = new RuleValueList($sDelimiter, $oParserState->currentLine());
87
                for ($i = $iStartPosition; $i - $iStartPosition < $iLength * 2; $i += 2) {
88
                    $oList->addListComponent($aStack[$i]);
89
                }
90
                $aNewStack[] = $oList;
91
                $iStartPosition += $iLength * 2 - 2;
92
            }
93
            $aStack = $aNewStack;
94
        }
95
        if (!isset($aStack[0])) {
96
            throw new UnexpectedTokenException(
97
                " {$oParserState->peek()} ",
98
                $oParserState->peek(1, -1) . $oParserState->peek(2),
99
                'literal',
100
                $oParserState->currentLine()
101
            );
102
        }
103
        return $aStack[0];
104
    }
105
 
106
    /**
107
     * @param bool $bIgnoreCase
108
     *
109
     * @return CSSFunction|string
110
     *
111
     * @throws UnexpectedEOFException
112
     * @throws UnexpectedTokenException
113
     */
114
    public static function parseIdentifierOrFunction(ParserState $oParserState, $bIgnoreCase = false)
115
    {
116
        $oAnchor = $oParserState->anchor();
117
        $mResult = $oParserState->parseIdentifier($bIgnoreCase);
118
 
119
        if ($oParserState->comes('(')) {
120
            $oAnchor->backtrack();
121
            if ($oParserState->streql('url', $mResult)) {
122
                $mResult = URL::parse($oParserState);
123
            } elseif (
124
                $oParserState->streql('calc', $mResult)
125
                || $oParserState->streql('-webkit-calc', $mResult)
126
                || $oParserState->streql('-moz-calc', $mResult)
127
            ) {
128
                $mResult = CalcFunction::parse($oParserState);
129
            } else {
130
                $mResult = CSSFunction::parse($oParserState, $bIgnoreCase);
131
            }
132
        }
133
 
134
        return $mResult;
135
    }
136
 
137
    /**
138
     * @return CSSFunction|CSSString|LineName|Size|URL|string
139
     *
140
     * @throws UnexpectedEOFException
141
     * @throws UnexpectedTokenException
142
     * @throws SourceException
143
     */
144
    public static function parsePrimitiveValue(ParserState $oParserState)
145
    {
146
        $oValue = null;
147
        $oParserState->consumeWhiteSpace();
148
        if (
149
            is_numeric($oParserState->peek())
150
            || ($oParserState->comes('-.')
151
                && is_numeric($oParserState->peek(1, 2)))
152
            || (($oParserState->comes('-') || $oParserState->comes('.')) && is_numeric($oParserState->peek(1, 1)))
153
        ) {
154
            $oValue = Size::parse($oParserState);
155
        } elseif ($oParserState->comes('#') || $oParserState->comes('rgb', true) || $oParserState->comes('hsl', true)) {
156
            $oValue = Color::parse($oParserState);
157
        } elseif ($oParserState->comes("'") || $oParserState->comes('"')) {
158
            $oValue = CSSString::parse($oParserState);
159
        } elseif ($oParserState->comes("progid:") && $oParserState->getSettings()->bLenientParsing) {
160
            $oValue = self::parseMicrosoftFilter($oParserState);
161
        } elseif ($oParserState->comes("[")) {
162
            $oValue = LineName::parse($oParserState);
163
        } elseif ($oParserState->comes("U+")) {
164
            $oValue = self::parseUnicodeRangeValue($oParserState);
165
        } else {
166
            $sNextChar = $oParserState->peek(1);
167
            try {
168
                $oValue = self::parseIdentifierOrFunction($oParserState);
169
            } catch (UnexpectedTokenException $e) {
170
                if (\in_array($sNextChar, ['+', '-', '*', '/'], true)) {
171
                    $oValue = $oParserState->consume(1);
172
                } else {
173
                    throw $e;
174
                }
175
            }
176
        }
177
        $oParserState->consumeWhiteSpace();
178
        return $oValue;
179
    }
180
 
181
    /**
182
     * @return CSSFunction
183
     *
184
     * @throws UnexpectedEOFException
185
     * @throws UnexpectedTokenException
186
     */
187
    private static function parseMicrosoftFilter(ParserState $oParserState)
188
    {
189
        $sFunction = $oParserState->consumeUntil('(', false, true);
190
        $aArguments = Value::parseValue($oParserState, [',', '=']);
191
        return new CSSFunction($sFunction, $aArguments, ',', $oParserState->currentLine());
192
    }
193
 
194
    /**
195
     * @return string
196
     *
197
     * @throws UnexpectedEOFException
198
     * @throws UnexpectedTokenException
199
     */
200
    private static function parseUnicodeRangeValue(ParserState $oParserState)
201
    {
202
        $iCodepointMaxLength = 6; // Code points outside BMP can use up to six digits
203
        $sRange = "";
204
        $oParserState->consume("U+");
205
        do {
206
            if ($oParserState->comes('-')) {
207
                $iCodepointMaxLength = 13; // Max length is 2 six digit code points + the dash(-) between them
208
            }
209
            $sRange .= $oParserState->consume(1);
210
        } while (strlen($sRange) < $iCodepointMaxLength && preg_match("/[A-Fa-f0-9\?-]/", $oParserState->peek()));
211
        return "U+{$sRange}";
212
    }
213
 
214
    /**
215
     * @return int
216
     */
217
    public function getLineNo()
218
    {
219
        return $this->iLineNo;
220
    }
221
}