Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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