allow vendord
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m3s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 32s
Build, Push and Deploy / deploy-production (push) Has been skipped
Build, Push and Deploy / cleanup (push) Successful in 1s
All checks were successful
Build, Push and Deploy / build-and-push (push) Successful in 3m3s
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 16s
Build, Push and Deploy / deploy-staging (push) Successful in 32s
Build, Push and Deploy / deploy-production (push) Has been skipped
Build, Push and Deploy / cleanup (push) Successful in 1s
This commit is contained in:
116
vendor/sabberworm/php-css-parser/src/Value/CSSFunction.php
vendored
Normal file
116
vendor/sabberworm/php-css-parser/src/Value/CSSFunction.php
vendored
Normal file
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\SourceException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
|
||||
/**
|
||||
* A `CSSFunction` represents a special kind of value that also contains a function name and where the values are the
|
||||
* function’s arguments. It also handles equals-sign-separated argument lists like `filter: alpha(opacity=90);`.
|
||||
*/
|
||||
class CSSFunction extends ValueList
|
||||
{
|
||||
/**
|
||||
* @var non-empty-string
|
||||
*
|
||||
* @internal since 8.8.0
|
||||
*/
|
||||
protected $name;
|
||||
|
||||
/**
|
||||
* @param non-empty-string $name
|
||||
* @param RuleValueList|array<Value|string> $arguments
|
||||
* @param non-empty-string $separator
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(string $name, $arguments, string $separator = ',', ?int $lineNumber = null)
|
||||
{
|
||||
if ($arguments instanceof RuleValueList) {
|
||||
$separator = $arguments->getListSeparator();
|
||||
$arguments = $arguments->getListComponents();
|
||||
}
|
||||
$this->name = $name;
|
||||
$this->setPosition($lineNumber); // TODO: redundant?
|
||||
parent::__construct($arguments, $separator, $lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SourceException
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parse(ParserState $parserState, bool $ignoreCase = false): CSSFunction
|
||||
{
|
||||
$name = self::parseName($parserState, $ignoreCase);
|
||||
$parserState->consume('(');
|
||||
$arguments = self::parseArguments($parserState);
|
||||
|
||||
$result = new CSSFunction($name, $arguments, ',', $parserState->currentLine());
|
||||
$parserState->consume(')');
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SourceException
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
private static function parseName(ParserState $parserState, bool $ignoreCase = false): string
|
||||
{
|
||||
return $parserState->parseIdentifier($ignoreCase);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Value|string
|
||||
*
|
||||
* @throws SourceException
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
private static function parseArguments(ParserState $parserState)
|
||||
{
|
||||
return Value::parseValue($parserState, ['=', ' ', ',']);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $name
|
||||
*/
|
||||
public function setName(string $name): void
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<Value|string>
|
||||
*/
|
||||
public function getArguments(): array
|
||||
{
|
||||
return $this->components;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
$arguments = parent::render($outputFormat);
|
||||
return "{$this->name}({$arguments})";
|
||||
}
|
||||
}
|
||||
95
vendor/sabberworm/php-css-parser/src/Value/CSSString.php
vendored
Normal file
95
vendor/sabberworm/php-css-parser/src/Value/CSSString.php
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\SourceException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
|
||||
/**
|
||||
* This class is a wrapper for quoted strings to distinguish them from keywords.
|
||||
*
|
||||
* `CSSString`s always output with double quotes.
|
||||
*/
|
||||
class CSSString extends PrimitiveValue
|
||||
{
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $string;
|
||||
|
||||
/**
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(string $string, ?int $lineNumber = null)
|
||||
{
|
||||
$this->string = $string;
|
||||
parent::__construct($lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SourceException
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parse(ParserState $parserState): CSSString
|
||||
{
|
||||
$begin = $parserState->peek();
|
||||
$quote = null;
|
||||
if ($begin === "'") {
|
||||
$quote = "'";
|
||||
} elseif ($begin === '"') {
|
||||
$quote = '"';
|
||||
}
|
||||
if ($quote !== null) {
|
||||
$parserState->consume($quote);
|
||||
}
|
||||
$result = '';
|
||||
$content = null;
|
||||
if ($quote === null) {
|
||||
// Unquoted strings end in whitespace or with braces, brackets, parentheses
|
||||
while (\preg_match('/[\\s{}()<>\\[\\]]/isu', $parserState->peek()) !== 1) {
|
||||
$result .= $parserState->parseCharacter(false);
|
||||
}
|
||||
} else {
|
||||
while (!$parserState->comes($quote)) {
|
||||
$content = $parserState->parseCharacter(false);
|
||||
if ($content === null) {
|
||||
throw new SourceException(
|
||||
"Non-well-formed quoted string {$parserState->peek(3)}",
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
$result .= $content;
|
||||
}
|
||||
$parserState->consume($quote);
|
||||
}
|
||||
return new CSSString($result, $parserState->currentLine());
|
||||
}
|
||||
|
||||
public function setString(string $string): void
|
||||
{
|
||||
$this->string = $string;
|
||||
}
|
||||
|
||||
public function getString(): string
|
||||
{
|
||||
return $this->string;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
$string = \addslashes($this->string);
|
||||
$string = \str_replace("\n", '\\A', $string);
|
||||
return $outputFormat->getStringQuotingType() . $string . $outputFormat->getStringQuotingType();
|
||||
}
|
||||
}
|
||||
105
vendor/sabberworm/php-css-parser/src/Value/CalcFunction.php
vendored
Normal file
105
vendor/sabberworm/php-css-parser/src/Value/CalcFunction.php
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
|
||||
class CalcFunction extends CSSFunction
|
||||
{
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private const T_OPERAND = 1;
|
||||
|
||||
/**
|
||||
* @var int
|
||||
*/
|
||||
private const T_OPERATOR = 2;
|
||||
|
||||
/**
|
||||
* @throws UnexpectedTokenException
|
||||
* @throws UnexpectedEOFException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parse(ParserState $parserState, bool $ignoreCase = false): CSSFunction
|
||||
{
|
||||
$operators = ['+', '-', '*', '/'];
|
||||
$function = $parserState->parseIdentifier();
|
||||
if ($parserState->peek() !== '(') {
|
||||
// Found ; or end of line before an opening bracket
|
||||
throw new UnexpectedTokenException('(', $parserState->peek(), 'literal', $parserState->currentLine());
|
||||
} elseif ($function !== 'calc') {
|
||||
// Found invalid calc definition. Example calc (...
|
||||
throw new UnexpectedTokenException('calc', $function, 'literal', $parserState->currentLine());
|
||||
}
|
||||
$parserState->consume('(');
|
||||
$calcRuleValueList = new CalcRuleValueList($parserState->currentLine());
|
||||
$list = new RuleValueList(',', $parserState->currentLine());
|
||||
$nestingLevel = 0;
|
||||
$lastComponentType = null;
|
||||
while (!$parserState->comes(')') || $nestingLevel > 0) {
|
||||
if ($parserState->isEnd() && $nestingLevel === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
$parserState->consumeWhiteSpace();
|
||||
if ($parserState->comes('(')) {
|
||||
$nestingLevel++;
|
||||
$calcRuleValueList->addListComponent($parserState->consume(1));
|
||||
$parserState->consumeWhiteSpace();
|
||||
continue;
|
||||
} elseif ($parserState->comes(')')) {
|
||||
$nestingLevel--;
|
||||
$calcRuleValueList->addListComponent($parserState->consume(1));
|
||||
$parserState->consumeWhiteSpace();
|
||||
continue;
|
||||
}
|
||||
if ($lastComponentType !== CalcFunction::T_OPERAND) {
|
||||
$value = Value::parsePrimitiveValue($parserState);
|
||||
$calcRuleValueList->addListComponent($value);
|
||||
$lastComponentType = CalcFunction::T_OPERAND;
|
||||
} else {
|
||||
if (\in_array($parserState->peek(), $operators, true)) {
|
||||
if (($parserState->comes('-') || $parserState->comes('+'))) {
|
||||
if (
|
||||
$parserState->peek(1, -1) !== ' '
|
||||
|| !($parserState->comes('- ')
|
||||
|| $parserState->comes('+ '))
|
||||
) {
|
||||
throw new UnexpectedTokenException(
|
||||
" {$parserState->peek()} ",
|
||||
$parserState->peek(1, -1) . $parserState->peek(2),
|
||||
'literal',
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
}
|
||||
$calcRuleValueList->addListComponent($parserState->consume(1));
|
||||
$lastComponentType = CalcFunction::T_OPERATOR;
|
||||
} else {
|
||||
throw new UnexpectedTokenException(
|
||||
\sprintf(
|
||||
'Next token was expected to be an operand of type %s. Instead "%s" was found.',
|
||||
\implode(', ', $operators),
|
||||
$parserState->peek()
|
||||
),
|
||||
'',
|
||||
'custom',
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
}
|
||||
$parserState->consumeWhiteSpace();
|
||||
}
|
||||
$list->addListComponent($calcRuleValueList);
|
||||
if (!$parserState->isEnd()) {
|
||||
$parserState->consume(')');
|
||||
}
|
||||
return new CalcFunction($function, $list, ',', $parserState->currentLine());
|
||||
}
|
||||
}
|
||||
23
vendor/sabberworm/php-css-parser/src/Value/CalcRuleValueList.php
vendored
Normal file
23
vendor/sabberworm/php-css-parser/src/Value/CalcRuleValueList.php
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
|
||||
class CalcRuleValueList extends RuleValueList
|
||||
{
|
||||
/**
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(?int $lineNumber = null)
|
||||
{
|
||||
parent::__construct(',', $lineNumber);
|
||||
}
|
||||
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
return $outputFormat->getFormatter()->implode(' ', $this->components);
|
||||
}
|
||||
}
|
||||
390
vendor/sabberworm/php-css-parser/src/Value/Color.php
vendored
Normal file
390
vendor/sabberworm/php-css-parser/src/Value/Color.php
vendored
Normal file
@@ -0,0 +1,390 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
|
||||
/**
|
||||
* `Color's can be input in the form #rrggbb, #rgb or schema(val1, val2, …) but are always stored as an array of
|
||||
* ('s' => val1, 'c' => val2, 'h' => val3, …) and output in the second form.
|
||||
*/
|
||||
class Color extends CSSFunction
|
||||
{
|
||||
/**
|
||||
* @param array<non-empty-string, Value|string> $colorValues
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(array $colorValues, ?int $lineNumber = null)
|
||||
{
|
||||
parent::__construct(\implode('', \array_keys($colorValues)), $colorValues, ',', $lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parse(ParserState $parserState, bool $ignoreCase = false): CSSFunction
|
||||
{
|
||||
return $parserState->comes('#')
|
||||
? self::parseHexColor($parserState)
|
||||
: self::parseColorFunction($parserState);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
private static function parseHexColor(ParserState $parserState): Color
|
||||
{
|
||||
$parserState->consume('#');
|
||||
$hexValue = $parserState->parseIdentifier(false);
|
||||
if ($parserState->strlen($hexValue) === 3) {
|
||||
$hexValue = $hexValue[0] . $hexValue[0] . $hexValue[1] . $hexValue[1] . $hexValue[2] . $hexValue[2];
|
||||
} elseif ($parserState->strlen($hexValue) === 4) {
|
||||
$hexValue = $hexValue[0] . $hexValue[0] . $hexValue[1] . $hexValue[1] . $hexValue[2] . $hexValue[2]
|
||||
. $hexValue[3] . $hexValue[3];
|
||||
}
|
||||
|
||||
if ($parserState->strlen($hexValue) === 8) {
|
||||
$colorValues = [
|
||||
'r' => new Size(\intval($hexValue[0] . $hexValue[1], 16), null, true, $parserState->currentLine()),
|
||||
'g' => new Size(\intval($hexValue[2] . $hexValue[3], 16), null, true, $parserState->currentLine()),
|
||||
'b' => new Size(\intval($hexValue[4] . $hexValue[5], 16), null, true, $parserState->currentLine()),
|
||||
'a' => new Size(
|
||||
\round(self::mapRange(\intval($hexValue[6] . $hexValue[7], 16), 0, 255, 0, 1), 2),
|
||||
null,
|
||||
true,
|
||||
$parserState->currentLine()
|
||||
),
|
||||
];
|
||||
} elseif ($parserState->strlen($hexValue) === 6) {
|
||||
$colorValues = [
|
||||
'r' => new Size(\intval($hexValue[0] . $hexValue[1], 16), null, true, $parserState->currentLine()),
|
||||
'g' => new Size(\intval($hexValue[2] . $hexValue[3], 16), null, true, $parserState->currentLine()),
|
||||
'b' => new Size(\intval($hexValue[4] . $hexValue[5], 16), null, true, $parserState->currentLine()),
|
||||
];
|
||||
} else {
|
||||
throw new UnexpectedTokenException(
|
||||
'Invalid hex color value',
|
||||
$hexValue,
|
||||
'custom',
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
|
||||
return new Color($colorValues, $parserState->currentLine());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
private static function parseColorFunction(ParserState $parserState): CSSFunction
|
||||
{
|
||||
$colorValues = [];
|
||||
|
||||
$colorMode = $parserState->parseIdentifier(true);
|
||||
$parserState->consumeWhiteSpace();
|
||||
$parserState->consume('(');
|
||||
|
||||
// CSS Color Module Level 4 says that `rgb` and `rgba` are now aliases; likewise `hsl` and `hsla`.
|
||||
// So, attempt to parse with the `a`, and allow for it not being there.
|
||||
switch ($colorMode) {
|
||||
case 'rgb':
|
||||
$colorModeForParsing = 'rgba';
|
||||
$mayHaveOptionalAlpha = true;
|
||||
break;
|
||||
case 'hsl':
|
||||
$colorModeForParsing = 'hsla';
|
||||
$mayHaveOptionalAlpha = true;
|
||||
break;
|
||||
case 'rgba':
|
||||
// This is handled identically to the following case.
|
||||
case 'hsla':
|
||||
$colorModeForParsing = $colorMode;
|
||||
$mayHaveOptionalAlpha = true;
|
||||
break;
|
||||
default:
|
||||
$colorModeForParsing = $colorMode;
|
||||
$mayHaveOptionalAlpha = false;
|
||||
}
|
||||
|
||||
$containsVar = false;
|
||||
$containsNone = false;
|
||||
$isLegacySyntax = false;
|
||||
$expectedArgumentCount = $parserState->strlen($colorModeForParsing);
|
||||
for ($argumentIndex = 0; $argumentIndex < $expectedArgumentCount; ++$argumentIndex) {
|
||||
$parserState->consumeWhiteSpace();
|
||||
$valueKey = $colorModeForParsing[$argumentIndex];
|
||||
if ($parserState->comes('var')) {
|
||||
$colorValues[$valueKey] = CSSFunction::parseIdentifierOrFunction($parserState);
|
||||
$containsVar = true;
|
||||
} elseif (!$isLegacySyntax && $parserState->comes('none')) {
|
||||
$colorValues[$valueKey] = $parserState->parseIdentifier();
|
||||
$containsNone = true;
|
||||
} else {
|
||||
$colorValues[$valueKey] = Size::parse($parserState, true);
|
||||
}
|
||||
|
||||
// This must be done first, to consume comments as well, so that the `comes` test will work.
|
||||
$parserState->consumeWhiteSpace();
|
||||
|
||||
// With a `var` argument, the function can have fewer arguments.
|
||||
// And as of CSS Color Module Level 4, the alpha argument is optional.
|
||||
$canCloseNow =
|
||||
$containsVar
|
||||
|| ($mayHaveOptionalAlpha && $argumentIndex >= $expectedArgumentCount - 2);
|
||||
if ($canCloseNow && $parserState->comes(')')) {
|
||||
break;
|
||||
}
|
||||
|
||||
// "Legacy" syntax is comma-delimited, and does not allow the `none` keyword.
|
||||
// "Modern" syntax is space-delimited, with `/` as alpha delimiter.
|
||||
// They cannot be mixed.
|
||||
if ($argumentIndex === 0 && !$containsNone) {
|
||||
// An immediate closing parenthesis is not valid.
|
||||
if ($parserState->comes(')')) {
|
||||
throw new UnexpectedTokenException(
|
||||
'Color function with no arguments',
|
||||
'',
|
||||
'custom',
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
$isLegacySyntax = $parserState->comes(',');
|
||||
}
|
||||
|
||||
if ($isLegacySyntax && $argumentIndex < ($expectedArgumentCount - 1)) {
|
||||
$parserState->consume(',');
|
||||
}
|
||||
|
||||
// In the "modern" syntax, the alpha value must be delimited with `/`.
|
||||
if (!$isLegacySyntax) {
|
||||
if ($containsVar) {
|
||||
// If the `var` substitution encompasses more than one argument,
|
||||
// the alpha deliminator may come at any time.
|
||||
if ($parserState->comes('/')) {
|
||||
$parserState->consume('/');
|
||||
}
|
||||
} elseif (($colorModeForParsing[$argumentIndex + 1] ?? '') === 'a') {
|
||||
// Alpha value is the next expected argument.
|
||||
// Since a closing parenthesis was not found, a `/` separator is now required.
|
||||
$parserState->consume('/');
|
||||
}
|
||||
}
|
||||
}
|
||||
$parserState->consume(')');
|
||||
|
||||
return $containsVar
|
||||
? new CSSFunction($colorMode, \array_values($colorValues), ',', $parserState->currentLine())
|
||||
: new Color($colorValues, $parserState->currentLine());
|
||||
}
|
||||
|
||||
private static function mapRange(float $value, float $fromMin, float $fromMax, float $toMin, float $toMax): float
|
||||
{
|
||||
$fromRange = $fromMax - $fromMin;
|
||||
$toRange = $toMax - $toMin;
|
||||
$multiplier = $toRange / $fromRange;
|
||||
$newValue = $value - $fromMin;
|
||||
$newValue *= $multiplier;
|
||||
|
||||
return $newValue + $toMin;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<non-empty-string, Value|string>
|
||||
*/
|
||||
public function getColor(): array
|
||||
{
|
||||
return $this->components;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<non-empty-string, Value|string> $colorValues
|
||||
*/
|
||||
public function setColor(array $colorValues): void
|
||||
{
|
||||
$this->setName(\implode('', \array_keys($colorValues)));
|
||||
$this->components = $colorValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function getColorDescription(): string
|
||||
{
|
||||
return $this->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
if ($this->shouldRenderAsHex($outputFormat)) {
|
||||
return $this->renderAsHex();
|
||||
}
|
||||
|
||||
if ($this->shouldRenderInModernSyntax()) {
|
||||
return $this->renderInModernSyntax($outputFormat);
|
||||
}
|
||||
|
||||
return parent::render($outputFormat);
|
||||
}
|
||||
|
||||
private function shouldRenderAsHex(OutputFormat $outputFormat): bool
|
||||
{
|
||||
return
|
||||
$outputFormat->usesRgbHashNotation()
|
||||
&& $this->getRealName() === 'rgb'
|
||||
&& $this->allComponentsAreNumbers();
|
||||
}
|
||||
|
||||
/**
|
||||
* The function name is a concatenation of the array keys of the components, which is passed to the constructor.
|
||||
* However, this can be changed by calling {@see CSSFunction::setName},
|
||||
* so is not reliable in situations where it's necessary to determine the function name based on the components.
|
||||
*/
|
||||
private function getRealName(): string
|
||||
{
|
||||
return \implode('', \array_keys($this->components));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test whether all color components are absolute numbers (CSS type `number`), not percentages or anything else.
|
||||
* If any component is not an instance of `Size`, the method will also return `false`.
|
||||
*/
|
||||
private function allComponentsAreNumbers(): bool
|
||||
{
|
||||
foreach ($this->components as $component) {
|
||||
if (!($component instanceof Size) || $component->getUnit() !== null) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Note that this method assumes the following:
|
||||
* - The `components` array has keys for `r`, `g` and `b`;
|
||||
* - The values in the array are all instances of `Size`.
|
||||
*
|
||||
* Errors will be triggered or thrown if this is not the case.
|
||||
*
|
||||
* @return non-empty-string
|
||||
*/
|
||||
private function renderAsHex(): string
|
||||
{
|
||||
$result = \sprintf(
|
||||
'%02x%02x%02x',
|
||||
$this->components['r']->getSize(),
|
||||
$this->components['g']->getSize(),
|
||||
$this->components['b']->getSize()
|
||||
);
|
||||
$canUseShortVariant = ($result[0] === $result[1]) && ($result[2] === $result[3]) && ($result[4] === $result[5]);
|
||||
|
||||
return '#' . ($canUseShortVariant ? $result[0] . $result[2] . $result[4] : $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* The "legacy" syntax does not allow RGB colors to have a mixture of `percentage`s and `number`s,
|
||||
* and does not allow `none` as any component value.
|
||||
*
|
||||
* The "legacy" and "modern" monikers are part of the formal W3C syntax.
|
||||
* See the following for more information:
|
||||
* - {@link
|
||||
* https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/rgb#formal_syntax
|
||||
* Description of the formal syntax for `rgb()` on MDN
|
||||
* };
|
||||
* - {@link
|
||||
* https://www.w3.org/TR/css-color-4/#rgb-functions
|
||||
* The same in the CSS Color Module Level 4 W3C Candidate Recommendation Draft
|
||||
* } (as of 13 February 2024, at time of writing).
|
||||
*/
|
||||
private function shouldRenderInModernSyntax(): bool
|
||||
{
|
||||
if ($this->hasNoneAsComponentValue()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!$this->colorFunctionMayHaveMixedValueTypes($this->getRealName())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$hasPercentage = false;
|
||||
$hasNumber = false;
|
||||
foreach ($this->components as $key => $value) {
|
||||
if ($key === 'a') {
|
||||
// Alpha can have units that don't match those of the RGB components in the "legacy" syntax.
|
||||
// So it is not necessary to check it. It's also always last, hence `break` rather than `continue`.
|
||||
break;
|
||||
}
|
||||
if (!($value instanceof Size)) {
|
||||
// Unexpected, unknown, or modified via the API
|
||||
return false;
|
||||
}
|
||||
$unit = $value->getUnit();
|
||||
// `switch` only does loose comparison
|
||||
if ($unit === null) {
|
||||
$hasNumber = true;
|
||||
} elseif ($unit === '%') {
|
||||
$hasPercentage = true;
|
||||
} else {
|
||||
// Invalid unit
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $hasPercentage && $hasNumber;
|
||||
}
|
||||
|
||||
private function hasNoneAsComponentValue(): bool
|
||||
{
|
||||
return \in_array('none', $this->components, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Some color functions, such as `rgb`,
|
||||
* may have a mixture of `percentage`, `number`, or possibly other types in their arguments.
|
||||
*
|
||||
* Note that this excludes the alpha component, which is treated separately.
|
||||
*/
|
||||
private function colorFunctionMayHaveMixedValueTypes(string $function): bool
|
||||
{
|
||||
$functionsThatMayHaveMixedValueTypes = ['rgb', 'rgba'];
|
||||
|
||||
return \in_array($function, $functionsThatMayHaveMixedValueTypes, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
private function renderInModernSyntax(OutputFormat $outputFormat): string
|
||||
{
|
||||
// Maybe not yet without alpha, but will be...
|
||||
$componentsWithoutAlpha = $this->components;
|
||||
\end($componentsWithoutAlpha);
|
||||
if (\key($componentsWithoutAlpha) === 'a') {
|
||||
$alpha = $this->components['a'];
|
||||
unset($componentsWithoutAlpha['a']);
|
||||
}
|
||||
|
||||
$formatter = $outputFormat->getFormatter();
|
||||
$arguments = $formatter->implode(' ', $componentsWithoutAlpha);
|
||||
if (isset($alpha)) {
|
||||
$separator = $formatter->spaceBeforeListArgumentSeparator('/')
|
||||
. '/' . $formatter->spaceAfterListArgumentSeparator('/');
|
||||
$arguments = $formatter->implode($separator, [$arguments, $alpha]);
|
||||
}
|
||||
|
||||
return $this->getName() . '(' . $arguments . ')';
|
||||
}
|
||||
}
|
||||
59
vendor/sabberworm/php-css-parser/src/Value/LineName.php
vendored
Normal file
59
vendor/sabberworm/php-css-parser/src/Value/LineName.php
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
|
||||
class LineName extends ValueList
|
||||
{
|
||||
/**
|
||||
* @param array<Value|string> $components
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(array $components = [], ?int $lineNumber = null)
|
||||
{
|
||||
parent::__construct($components, ' ', $lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnexpectedTokenException
|
||||
* @throws UnexpectedEOFException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parse(ParserState $parserState): LineName
|
||||
{
|
||||
$parserState->consume('[');
|
||||
$parserState->consumeWhiteSpace();
|
||||
$names = [];
|
||||
do {
|
||||
if ($parserState->getSettings()->usesLenientParsing()) {
|
||||
try {
|
||||
$names[] = $parserState->parseIdentifier();
|
||||
} catch (UnexpectedTokenException $e) {
|
||||
if (!$parserState->comes(']')) {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$names[] = $parserState->parseIdentifier();
|
||||
}
|
||||
$parserState->consumeWhiteSpace();
|
||||
} while (!$parserState->comes(']'));
|
||||
$parserState->consume(']');
|
||||
return new LineName($names, $parserState->currentLine());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
return '[' . parent::render(OutputFormat::createCompact()) . ']';
|
||||
}
|
||||
}
|
||||
7
vendor/sabberworm/php-css-parser/src/Value/PrimitiveValue.php
vendored
Normal file
7
vendor/sabberworm/php-css-parser/src/Value/PrimitiveValue.php
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
abstract class PrimitiveValue extends Value {}
|
||||
22
vendor/sabberworm/php-css-parser/src/Value/RuleValueList.php
vendored
Normal file
22
vendor/sabberworm/php-css-parser/src/Value/RuleValueList.php
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
/**
|
||||
* This class is used to represent all multivalued rules like `font: bold 12px/3 Helvetica, Verdana, sans-serif;`
|
||||
* (where the value would be a whitespace-separated list of the primitive value `bold`, a slash-separated list
|
||||
* and a comma-separated list).
|
||||
*/
|
||||
class RuleValueList extends ValueList
|
||||
{
|
||||
/**
|
||||
* @param non-empty-string $separator
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(string $separator = ',', ?int $lineNumber = null)
|
||||
{
|
||||
parent::__construct([], $separator, $lineNumber);
|
||||
}
|
||||
}
|
||||
210
vendor/sabberworm/php-css-parser/src/Value/Size.php
vendored
Normal file
210
vendor/sabberworm/php-css-parser/src/Value/Size.php
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
|
||||
/**
|
||||
* A `Size` consists of a numeric `size` value and a unit.
|
||||
*/
|
||||
class Size extends PrimitiveValue
|
||||
{
|
||||
/**
|
||||
* vh/vw/vm(ax)/vmin/rem are absolute insofar as they don’t scale to the immediate parent (only the viewport)
|
||||
*
|
||||
* @var list<non-empty-string>
|
||||
*/
|
||||
private const ABSOLUTE_SIZE_UNITS = [
|
||||
'px',
|
||||
'pt',
|
||||
'pc',
|
||||
'cm',
|
||||
'mm',
|
||||
'mozmm',
|
||||
'in',
|
||||
'vh',
|
||||
'dvh',
|
||||
'svh',
|
||||
'lvh',
|
||||
'vw',
|
||||
'vmin',
|
||||
'vmax',
|
||||
'rem',
|
||||
];
|
||||
|
||||
/**
|
||||
* @var list<non-empty-string>
|
||||
*/
|
||||
private const RELATIVE_SIZE_UNITS = ['%', 'em', 'ex', 'ch', 'fr'];
|
||||
|
||||
/**
|
||||
* @var list<non-empty-string>
|
||||
*/
|
||||
private const NON_SIZE_UNITS = ['deg', 'grad', 'rad', 's', 'ms', 'turn', 'Hz', 'kHz'];
|
||||
|
||||
/**
|
||||
* @var array<int<1, max>, array<lowercase-string, non-empty-string>>|null
|
||||
*/
|
||||
private static $SIZE_UNITS = null;
|
||||
|
||||
/**
|
||||
* @var float
|
||||
*/
|
||||
private $size;
|
||||
|
||||
/**
|
||||
* @var string|null
|
||||
*/
|
||||
private $unit;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private $isColorComponent;
|
||||
|
||||
/**
|
||||
* @param float|int|string $size
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct($size, ?string $unit = null, bool $isColorComponent = false, ?int $lineNumber = null)
|
||||
{
|
||||
parent::__construct($lineNumber);
|
||||
$this->size = (float) $size;
|
||||
$this->unit = $unit;
|
||||
$this->isColorComponent = $isColorComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parse(ParserState $parserState, bool $isColorComponent = false): Size
|
||||
{
|
||||
$size = '';
|
||||
if ($parserState->comes('-')) {
|
||||
$size .= $parserState->consume('-');
|
||||
}
|
||||
while (\is_numeric($parserState->peek()) || $parserState->comes('.') || $parserState->comes('e', true)) {
|
||||
if ($parserState->comes('.')) {
|
||||
$size .= $parserState->consume('.');
|
||||
} elseif ($parserState->comes('e', true)) {
|
||||
$lookahead = $parserState->peek(1, 1);
|
||||
if (\is_numeric($lookahead) || $lookahead === '+' || $lookahead === '-') {
|
||||
$size .= $parserState->consume(2);
|
||||
} else {
|
||||
break; // Reached the unit part of the number like "em" or "ex"
|
||||
}
|
||||
} else {
|
||||
$size .= $parserState->consume(1);
|
||||
}
|
||||
}
|
||||
|
||||
$unit = null;
|
||||
$sizeUnits = self::getSizeUnits();
|
||||
foreach ($sizeUnits as $length => &$values) {
|
||||
$key = \strtolower($parserState->peek($length));
|
||||
if (\array_key_exists($key, $values)) {
|
||||
if (($unit = $values[$key]) !== null) {
|
||||
$parserState->consume($length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Size((float) $size, $unit, $isColorComponent, $parserState->currentLine());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<int<1, max>, array<lowercase-string, non-empty-string>>
|
||||
*/
|
||||
private static function getSizeUnits(): array
|
||||
{
|
||||
if (!\is_array(self::$SIZE_UNITS)) {
|
||||
self::$SIZE_UNITS = [];
|
||||
$sizeUnits = \array_merge(self::ABSOLUTE_SIZE_UNITS, self::RELATIVE_SIZE_UNITS, self::NON_SIZE_UNITS);
|
||||
foreach ($sizeUnits as $sizeUnit) {
|
||||
$tokenLength = \strlen($sizeUnit);
|
||||
if (!isset(self::$SIZE_UNITS[$tokenLength])) {
|
||||
self::$SIZE_UNITS[$tokenLength] = [];
|
||||
}
|
||||
self::$SIZE_UNITS[$tokenLength][\strtolower($sizeUnit)] = $sizeUnit;
|
||||
}
|
||||
|
||||
\krsort(self::$SIZE_UNITS, SORT_NUMERIC);
|
||||
}
|
||||
|
||||
return self::$SIZE_UNITS;
|
||||
}
|
||||
|
||||
public function setUnit(string $unit): void
|
||||
{
|
||||
$this->unit = $unit;
|
||||
}
|
||||
|
||||
public function getUnit(): ?string
|
||||
{
|
||||
return $this->unit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param float|int|string $size
|
||||
*/
|
||||
public function setSize($size): void
|
||||
{
|
||||
$this->size = (float) $size;
|
||||
}
|
||||
|
||||
public function getSize(): float
|
||||
{
|
||||
return $this->size;
|
||||
}
|
||||
|
||||
public function isColorComponent(): bool
|
||||
{
|
||||
return $this->isColorComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the number stored in this Size really represents a size (as in a length of something on screen).
|
||||
*
|
||||
* Returns `false` if the unit is an angle, a duration, a frequency, or the number is a component in a `Color`
|
||||
* object.
|
||||
*/
|
||||
public function isSize(): bool
|
||||
{
|
||||
if (\in_array($this->unit, self::NON_SIZE_UNITS, true)) {
|
||||
return false;
|
||||
}
|
||||
return !$this->isColorComponent();
|
||||
}
|
||||
|
||||
public function isRelative(): bool
|
||||
{
|
||||
if (\in_array($this->unit, self::RELATIVE_SIZE_UNITS, true)) {
|
||||
return true;
|
||||
}
|
||||
if ($this->unit === null && $this->size !== 0.0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
$locale = \localeconv();
|
||||
$decimalPoint = \preg_quote($locale['decimal_point'], '/');
|
||||
$size = \preg_match('/[\\d\\.]+e[+-]?\\d+/i', (string) $this->size)
|
||||
? \preg_replace("/$decimalPoint?0+$/", '', \sprintf('%f', $this->size)) : (string) $this->size;
|
||||
|
||||
return \preg_replace(["/$decimalPoint/", '/^(-?)0\\./'], ['.', '$1.'], $size) . ($this->unit ?? '');
|
||||
}
|
||||
}
|
||||
83
vendor/sabberworm/php-css-parser/src/Value/URL.php
vendored
Normal file
83
vendor/sabberworm/php-css-parser/src/Value/URL.php
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\SourceException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
|
||||
/**
|
||||
* This class represents URLs in CSS. `URL`s always output in `URL("")` notation.
|
||||
*/
|
||||
class URL extends PrimitiveValue
|
||||
{
|
||||
/**
|
||||
* @var CSSString
|
||||
*/
|
||||
private $url;
|
||||
|
||||
/**
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(CSSString $url, ?int $lineNumber = null)
|
||||
{
|
||||
parent::__construct($lineNumber);
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SourceException
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parse(ParserState $parserState): URL
|
||||
{
|
||||
$anchor = $parserState->anchor();
|
||||
$identifier = '';
|
||||
for ($i = 0; $i < 3; $i++) {
|
||||
$character = $parserState->parseCharacter(true);
|
||||
if ($character === null) {
|
||||
break;
|
||||
}
|
||||
$identifier .= $character;
|
||||
}
|
||||
$useUrl = $parserState->streql($identifier, 'url');
|
||||
if ($useUrl) {
|
||||
$parserState->consumeWhiteSpace();
|
||||
$parserState->consume('(');
|
||||
} else {
|
||||
$anchor->backtrack();
|
||||
}
|
||||
$parserState->consumeWhiteSpace();
|
||||
$result = new URL(CSSString::parse($parserState), $parserState->currentLine());
|
||||
if ($useUrl) {
|
||||
$parserState->consumeWhiteSpace();
|
||||
$parserState->consume(')');
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function setURL(CSSString $url): void
|
||||
{
|
||||
$this->url = $url;
|
||||
}
|
||||
|
||||
public function getURL(): CSSString
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
return "url({$this->url->render($outputFormat)})";
|
||||
}
|
||||
}
|
||||
211
vendor/sabberworm/php-css-parser/src/Value/Value.php
vendored
Normal file
211
vendor/sabberworm/php-css-parser/src/Value/Value.php
vendored
Normal file
@@ -0,0 +1,211 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\CSSElement;
|
||||
use Sabberworm\CSS\Parsing\ParserState;
|
||||
use Sabberworm\CSS\Parsing\SourceException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
|
||||
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
|
||||
use Sabberworm\CSS\Position\Position;
|
||||
use Sabberworm\CSS\Position\Positionable;
|
||||
|
||||
/**
|
||||
* Abstract base class for specific classes of CSS values: `Size`, `Color`, `CSSString` and `URL`, and another
|
||||
* abstract subclass `ValueList`.
|
||||
*/
|
||||
abstract class Value implements CSSElement, Positionable
|
||||
{
|
||||
use Position;
|
||||
|
||||
/**
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct(?int $lineNumber = null)
|
||||
{
|
||||
$this->setPosition($lineNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<non-empty-string> $listDelimiters
|
||||
*
|
||||
* @return Value|string
|
||||
*
|
||||
* @throws UnexpectedTokenException
|
||||
* @throws UnexpectedEOFException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parseValue(ParserState $parserState, array $listDelimiters = [])
|
||||
{
|
||||
/** @var list<Value|string> $stack */
|
||||
$stack = [];
|
||||
$parserState->consumeWhiteSpace();
|
||||
//Build a list of delimiters and parsed values
|
||||
while (
|
||||
!($parserState->comes('}') || $parserState->comes(';') || $parserState->comes('!')
|
||||
|| $parserState->comes(')')
|
||||
|| $parserState->isEnd())
|
||||
) {
|
||||
if (\count($stack) > 0) {
|
||||
$foundDelimiter = false;
|
||||
foreach ($listDelimiters as $delimiter) {
|
||||
if ($parserState->comes($delimiter)) {
|
||||
\array_push($stack, $parserState->consume($delimiter));
|
||||
$parserState->consumeWhiteSpace();
|
||||
$foundDelimiter = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$foundDelimiter) {
|
||||
//Whitespace was the list delimiter
|
||||
\array_push($stack, ' ');
|
||||
}
|
||||
}
|
||||
\array_push($stack, self::parsePrimitiveValue($parserState));
|
||||
$parserState->consumeWhiteSpace();
|
||||
}
|
||||
// Convert the list to list objects
|
||||
foreach ($listDelimiters as $delimiter) {
|
||||
$stackSize = \count($stack);
|
||||
if ($stackSize === 1) {
|
||||
return $stack[0];
|
||||
}
|
||||
$newStack = [];
|
||||
for ($offset = 0; $offset < $stackSize; ++$offset) {
|
||||
if ($offset === ($stackSize - 1) || $delimiter !== $stack[$offset + 1]) {
|
||||
$newStack[] = $stack[$offset];
|
||||
continue;
|
||||
}
|
||||
$length = 2; //Number of elements to be joined
|
||||
for ($i = $offset + 3; $i < $stackSize; $i += 2, ++$length) {
|
||||
if ($delimiter !== $stack[$i]) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$list = new RuleValueList($delimiter, $parserState->currentLine());
|
||||
for ($i = $offset; $i - $offset < $length * 2; $i += 2) {
|
||||
$list->addListComponent($stack[$i]);
|
||||
}
|
||||
$newStack[] = $list;
|
||||
$offset += $length * 2 - 2;
|
||||
}
|
||||
$stack = $newStack;
|
||||
}
|
||||
if (!isset($stack[0])) {
|
||||
throw new UnexpectedTokenException(
|
||||
" {$parserState->peek()} ",
|
||||
$parserState->peek(1, -1) . $parserState->peek(2),
|
||||
'literal',
|
||||
$parserState->currentLine()
|
||||
);
|
||||
}
|
||||
return $stack[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CSSFunction|string
|
||||
*
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parseIdentifierOrFunction(ParserState $parserState, bool $ignoreCase = false)
|
||||
{
|
||||
$anchor = $parserState->anchor();
|
||||
$result = $parserState->parseIdentifier($ignoreCase);
|
||||
|
||||
if ($parserState->comes('(')) {
|
||||
$anchor->backtrack();
|
||||
if ($parserState->streql('url', $result)) {
|
||||
$result = URL::parse($parserState);
|
||||
} elseif ($parserState->streql('calc', $result)) {
|
||||
$result = CalcFunction::parse($parserState);
|
||||
} else {
|
||||
$result = CSSFunction::parse($parserState, $ignoreCase);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return CSSFunction|CSSString|LineName|Size|URL|string
|
||||
*
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
* @throws SourceException
|
||||
*
|
||||
* @internal since V8.8.0
|
||||
*/
|
||||
public static function parsePrimitiveValue(ParserState $parserState)
|
||||
{
|
||||
$value = null;
|
||||
$parserState->consumeWhiteSpace();
|
||||
if (
|
||||
\is_numeric($parserState->peek())
|
||||
|| ($parserState->comes('-.')
|
||||
&& \is_numeric($parserState->peek(1, 2)))
|
||||
|| (($parserState->comes('-') || $parserState->comes('.')) && \is_numeric($parserState->peek(1, 1)))
|
||||
) {
|
||||
$value = Size::parse($parserState);
|
||||
} elseif ($parserState->comes('#') || $parserState->comes('rgb', true) || $parserState->comes('hsl', true)) {
|
||||
$value = Color::parse($parserState);
|
||||
} elseif ($parserState->comes("'") || $parserState->comes('"')) {
|
||||
$value = CSSString::parse($parserState);
|
||||
} elseif ($parserState->comes('progid:') && $parserState->getSettings()->usesLenientParsing()) {
|
||||
$value = self::parseMicrosoftFilter($parserState);
|
||||
} elseif ($parserState->comes('[')) {
|
||||
$value = LineName::parse($parserState);
|
||||
} elseif ($parserState->comes('U+')) {
|
||||
$value = self::parseUnicodeRangeValue($parserState);
|
||||
} else {
|
||||
$nextCharacter = $parserState->peek(1);
|
||||
try {
|
||||
$value = self::parseIdentifierOrFunction($parserState);
|
||||
} catch (UnexpectedTokenException $e) {
|
||||
if (\in_array($nextCharacter, ['+', '-', '*', '/'], true)) {
|
||||
$value = $parserState->consume(1);
|
||||
} else {
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
$parserState->consumeWhiteSpace();
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
private static function parseMicrosoftFilter(ParserState $parserState): CSSFunction
|
||||
{
|
||||
$function = $parserState->consumeUntil('(', false, true);
|
||||
$arguments = Value::parseValue($parserState, [',', '=']);
|
||||
return new CSSFunction($function, $arguments, ',', $parserState->currentLine());
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws UnexpectedEOFException
|
||||
* @throws UnexpectedTokenException
|
||||
*/
|
||||
private static function parseUnicodeRangeValue(ParserState $parserState): string
|
||||
{
|
||||
$codepointMaxLength = 6; // Code points outside BMP can use up to six digits
|
||||
$range = '';
|
||||
$parserState->consume('U+');
|
||||
do {
|
||||
if ($parserState->comes('-')) {
|
||||
$codepointMaxLength = 13; // Max length is 2 six-digit code points + the dash(-) between them
|
||||
}
|
||||
$range .= $parserState->consume(1);
|
||||
} while (\strlen($range) < $codepointMaxLength && \preg_match('/[A-Fa-f0-9\\?-]/', $parserState->peek()));
|
||||
|
||||
return "U+{$range}";
|
||||
}
|
||||
}
|
||||
96
vendor/sabberworm/php-css-parser/src/Value/ValueList.php
vendored
Normal file
96
vendor/sabberworm/php-css-parser/src/Value/ValueList.php
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Sabberworm\CSS\Value;
|
||||
|
||||
use Sabberworm\CSS\OutputFormat;
|
||||
|
||||
/**
|
||||
* A `ValueList` represents a lists of `Value`s, separated by some separation character
|
||||
* (mostly `,`, whitespace, or `/`).
|
||||
*
|
||||
* There are two types of `ValueList`s: `RuleValueList` and `CSSFunction`
|
||||
*/
|
||||
abstract class ValueList extends Value
|
||||
{
|
||||
/**
|
||||
* @var array<Value|string>
|
||||
*
|
||||
* @internal since 8.8.0
|
||||
*/
|
||||
protected $components;
|
||||
|
||||
/**
|
||||
* @var non-empty-string
|
||||
*
|
||||
* @internal since 8.8.0
|
||||
*/
|
||||
protected $separator;
|
||||
|
||||
/**
|
||||
* @param array<Value|string>|Value|string $components
|
||||
* @param non-empty-string $separator
|
||||
* @param int<1, max>|null $lineNumber
|
||||
*/
|
||||
public function __construct($components = [], $separator = ',', ?int $lineNumber = null)
|
||||
{
|
||||
parent::__construct($lineNumber);
|
||||
if (!\is_array($components)) {
|
||||
$components = [$components];
|
||||
}
|
||||
$this->components = $components;
|
||||
$this->separator = $separator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Value|string $component
|
||||
*/
|
||||
public function addListComponent($component): void
|
||||
{
|
||||
$this->components[] = $component;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<Value|string>
|
||||
*/
|
||||
public function getListComponents(): array
|
||||
{
|
||||
return $this->components;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<Value|string> $components
|
||||
*/
|
||||
public function setListComponents(array $components): void
|
||||
{
|
||||
$this->components = $components;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return non-empty-string
|
||||
*/
|
||||
public function getListSeparator(): string
|
||||
{
|
||||
return $this->separator;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param non-empty-string $separator
|
||||
*/
|
||||
public function setListSeparator(string $separator): void
|
||||
{
|
||||
$this->separator = $separator;
|
||||
}
|
||||
|
||||
public function render(OutputFormat $outputFormat): string
|
||||
{
|
||||
$formatter = $outputFormat->getFormatter();
|
||||
|
||||
return $formatter->implode(
|
||||
$formatter->spaceBeforeListArgumentSeparator($this->separator) . $this->separator
|
||||
. $formatter->spaceAfterListArgumentSeparator($this->separator),
|
||||
$this->components
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user