%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
);
}
}