%PDF- %PDF-
Direktori : /home/q/g/b/qgbqkvz/www/wp-content/plugins/wp-scss/scssphp/src/Ast/Css/ |
Current File : /home/q/g/b/qgbqkvz/www/wp-content/plugins/wp-scss/scssphp/src/Ast/Css/CssMediaQuery.php |
<?php /** * SCSSPHP * * @copyright 2012-2020 Leaf Corcoran * * @license http://opensource.org/licenses/MIT MIT * * @link http://scssphp.github.io/scssphp */ namespace ScssPhp\ScssPhp\Ast\Css; use ScssPhp\ScssPhp\Exception\SassFormatException; use ScssPhp\ScssPhp\Logger\LoggerInterface; use ScssPhp\ScssPhp\Parser\MediaQueryParser; /** * A plain CSS media query, as used in `@media` and `@import`. * * @internal */ final class CssMediaQuery { public const MERGE_RESULT_EMPTY = 'empty'; public const MERGE_RESULT_UNREPRESENTABLE = 'unrepresentable'; /** * The modifier, probably either "not" or "only". * * This may be `null` if no modifier is in use. * * @var string|null * @readonly */ private $modifier; /** * The media type, for example "screen" or "print". * * This may be `null`. If so, {@see $conditions} will not be empty. * * @var string|null * @readonly */ private $type; /** * Whether {@see $conditions} is a conjunction or a disjunction. * * In other words, if this is `true` this query matches when _all_ * {@see $conditions} are met, and if it's `false` this query matches when _any_ * condition in {@see $conditions} is met. * * If this is `false`, {@see $modifier} and {@see $type} will both be `null`. * * @var bool * @readonly */ private $conjunction; /** * Media conditions, including parentheses. * * This is anything that can appear in the [`<media-in-parens>`] production. * * [`<media-in-parens>`]: https://drafts.csswg.org/mediaqueries-4/#typedef-media-in-parens * * @var list<string> * @readonly */ private $conditions; /** * Parses a media query from $contents. * * If passed, $url is the name of the file from which $contents comes. * * @return list<CssMediaQuery> * * @throws SassFormatException if parsing fails */ public static function parseList(string $contents, ?LoggerInterface $logger = null, ?string $url = null): array { return (new MediaQueryParser($contents, $logger, $url))->parse(); } /** * @param list<string> $conditions */ private function __construct(array $conditions = [], bool $conjunction = true, ?string $type = null, ?string $modifier = null) { $this->modifier = $modifier; $this->type = $type; $this->conditions = $conditions; $this->conjunction = $conjunction; } /** * Creates a media query specifies a type and, optionally, conditions. * * This always sets {@see $conjunction} to `true`. * * @param list<string> $conditions */ public static function type(?string $type, ?string $modifier = null, array $conditions = []): CssMediaQuery { return new CssMediaQuery($conditions, true, $type, $modifier); } /** * Creates a media query that matches $conditions according to * $conjunction. * * The $conjunction argument may not be null if $conditions is longer than * a single element. * * @param list<string> $conditions */ public static function condition(array $conditions, ?bool $conjunction = null): CssMediaQuery { if (\count($conditions) > 1 && $conjunction === null) { throw new \InvalidArgumentException('If conditions is longer than one element, conjunction may not be null.'); } return new CssMediaQuery($conditions, $conjunction ?? true); } public function getModifier(): ?string { return $this->modifier; } public function getType(): ?string { return $this->type; } public function isConjunction(): bool { return $this->conjunction; } /** * @return list<string> */ public function getConditions(): array { return $this->conditions; } /** * Whether this media query matches all media types. */ public function matchesAllTypes(): bool { return $this->type === null || strtolower($this->type) === 'all'; } /** * Merges this with $other to return a query that matches the intersection * of both inputs. * * @return CssMediaQuery|string * @phpstan-return CssMediaQuery|CssMediaQuery::* */ public function merge(CssMediaQuery $other) { if (!$this->conjunction || !$other->conjunction) { return self::MERGE_RESULT_UNREPRESENTABLE; } $ourModifier = $this->modifier !== null ? strtolower($this->modifier) : null; $ourType = $this->type !== null ? strtolower($this->type) : null; $theirModifier = $other->modifier !== null ? strtolower($other->modifier) : null; $theirType = $other->type !== null ? strtolower($other->type) : null; if ($ourType === null && $theirType === null) { return self::condition(array_merge($this->conditions, $other->conditions), true); } if (($ourModifier === 'not') !== ($theirModifier === 'not')) { if ($ourType === $theirType) { $negativeConditions = $ourModifier === 'not' ? $this->conditions : $other->conditions; $positiveConditions = $ourModifier === 'not' ? $other->conditions : $this->conditions; // If the negative conditions are a subset of the positive conditions, the // query is empty. For example, `not screen and (color)` has no // intersection with `screen and (color) and (grid)`. // // However, `not screen and (color)` *does* intersect with `screen and // (grid)`, because it means `not (screen and (color))` and so it allows // a screen with no color but with a grid. if (empty(array_diff($negativeConditions, $positiveConditions))) { return self::MERGE_RESULT_EMPTY; } return self::MERGE_RESULT_UNREPRESENTABLE; } if ($this->matchesAllTypes() || $other->matchesAllTypes()) { return self::MERGE_RESULT_UNREPRESENTABLE; } if ($ourModifier === 'not') { $modifier = $theirModifier; $type = $theirType; $conditions = $other->conditions; } else { $modifier = $ourModifier; $type = $ourType; $conditions = $this->conditions; } } elseif ($ourModifier === 'not') { // CSS has no way of representing "neither screen nor print". if ($ourType !== $theirType) { return self::MERGE_RESULT_UNREPRESENTABLE; } $moreConditions = \count($this->conditions) > \count($other->conditions) ? $this->conditions : $other->conditions; $fewerConditions = \count($this->conditions) > \count($other->conditions) ? $other->conditions : $this->conditions; // If one set of features is a superset of the other, use those features // because they're strictly narrower. if (empty(array_diff($fewerConditions, $moreConditions))) { $modifier = $ourModifier; // "not" $type = $ourType; $conditions = $moreConditions; } else { // Otherwise, there's no way to represent the intersection. return self::MERGE_RESULT_UNREPRESENTABLE; } } elseif ($this->matchesAllTypes()) { $modifier = $theirModifier; // Omit the type if either input query did, since that indicates that they // aren't targeting a browser that requires "all and". $type = $other->matchesAllTypes() && $ourType === null ? null : $theirType; $conditions = array_merge($this->conditions, $other->conditions); } elseif ($other->matchesAllTypes()) { $modifier = $ourModifier; $type = $ourType; $conditions = array_merge($this->conditions, $other->conditions); } elseif ($ourType !== $theirType) { return self::MERGE_RESULT_EMPTY; } else { $modifier = $ourModifier ?? $theirModifier; $type = $ourType; $conditions = array_merge($this->conditions, $other->conditions); } return CssMediaQuery::type( $type === $ourType ? $this->type : $other->type, $modifier === $ourModifier ? $this->modifier : $other->modifier, $conditions ); } }