-
Notifications
You must be signed in to change notification settings - Fork 144
/
Copy pathCSSBlockList.php
165 lines (153 loc) · 6.06 KB
/
CSSBlockList.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
<?php
declare(strict_types=1);
namespace Sabberworm\CSS\CSSList;
use Sabberworm\CSS\CSSElement;
use Sabberworm\CSS\Property\Selector;
use Sabberworm\CSS\Rule\Rule;
use Sabberworm\CSS\RuleSet\DeclarationBlock;
use Sabberworm\CSS\RuleSet\RuleSet;
use Sabberworm\CSS\Value\CSSFunction;
use Sabberworm\CSS\Value\Value;
use Sabberworm\CSS\Value\ValueList;
/**
* A `CSSBlockList` is a `CSSList` whose `DeclarationBlock`s are guaranteed to contain valid declaration blocks or
* at-rules.
*
* Most `CSSList`s conform to this category but some at-rules (such as `@keyframes`) do not.
*/
abstract class CSSBlockList extends CSSList
{
/**
* Gets all `DeclarationBlock` objects recursively, no matter how deeply nested the selectors are.
*
* @return list<DeclarationBlock>
*/
public function getAllDeclarationBlocks(): array
{
$result = [];
foreach ($this->contents as $item) {
if ($item instanceof DeclarationBlock) {
$result[] = $item;
} elseif ($item instanceof CSSBlockList) {
$result = \array_merge($result, $item->getAllDeclarationBlocks());
}
}
return $result;
}
/**
* Returns all `RuleSet` objects recursively found in the tree, no matter how deeply nested the rule sets are.
*
* @return list<RuleSet>
*/
public function getAllRuleSets(): array
{
$result = [];
foreach ($this->contents as $item) {
if ($item instanceof RuleSet) {
$result[] = $item;
} elseif ($item instanceof CSSBlockList) {
$result = \array_merge($result, $item->getAllRuleSets());
}
}
return $result;
}
/**
* Returns all `Value` objects found recursively in `Rule`s in the tree.
*
* @param CSSElement|null $element
* This is the `CSSList` or `RuleSet` to start the search from (defaults to the whole document).
* @param string|null $ruleSearchPattern
* This allows filtering rules by property name
* (e.g. if "color" is passed, only `Value`s from `color` properties will be returned,
* or if "font-" is provided, `Value`s from all font rules, like `font-size`, and including `font` itself,
* will be returned).
* @param bool $searchInFunctionArguments whether to also return `Value` objects used as `CSSFunction` arguments.
*
* @return array<int, Value>
*
* @see RuleSet->getRules()
*/
public function getAllValues(
?CSSElement $element = null,
?string $ruleSearchPattern = null,
bool $searchInFunctionArguments = false
): array {
$result = [];
$this->allValues($element ?? $this, $result, $ruleSearchPattern, $searchInFunctionArguments);
return $result;
}
/**
* @param CSSElement|string $element
* @param list<Value> $result
*/
protected function allValues(
$element,
array &$result,
?string $searchString = null,
bool $searchInFunctionArguments = false
): void {
if ($element instanceof CSSBlockList) {
foreach ($element->getContents() as $content) {
$this->allValues($content, $result, $searchString, $searchInFunctionArguments);
}
} elseif ($element instanceof RuleSet) {
foreach ($element->getRules($searchString) as $rule) {
$this->allValues($rule, $result, $searchString, $searchInFunctionArguments);
}
} elseif ($element instanceof Rule) {
$this->allValues($element->getValue(), $result, $searchString, $searchInFunctionArguments);
} elseif ($element instanceof ValueList) {
if ($searchInFunctionArguments || !($element instanceof CSSFunction)) {
foreach ($element->getListComponents() as $component) {
$this->allValues($component, $result, $searchString, $searchInFunctionArguments);
}
}
} elseif ($element instanceof Value) {
$result[] = $element;
}
}
/**
* @param list<Selector> $result
*/
protected function allSelectors(array &$result, ?string $specificitySearch = null): void
{
foreach ($this->getAllDeclarationBlocks() as $declarationBlock) {
foreach ($declarationBlock->getSelectors() as $selector) {
if ($specificitySearch === null) {
$result[] = $selector;
} else {
$comparator = '===';
$expressionParts = \explode(' ', $specificitySearch);
$targetSpecificity = $expressionParts[0];
if (\count($expressionParts) > 1) {
$comparator = $expressionParts[0];
$targetSpecificity = $expressionParts[1];
}
$targetSpecificity = (int) $targetSpecificity;
$selectorSpecificity = $selector->getSpecificity();
$comparatorMatched = false;
switch ($comparator) {
case '<=':
$comparatorMatched = $selectorSpecificity <= $targetSpecificity;
break;
case '<':
$comparatorMatched = $selectorSpecificity < $targetSpecificity;
break;
case '>=':
$comparatorMatched = $selectorSpecificity >= $targetSpecificity;
break;
case '>':
$comparatorMatched = $selectorSpecificity > $targetSpecificity;
break;
default:
$comparatorMatched = $selectorSpecificity === $targetSpecificity;
break;
}
if ($comparatorMatched) {
$result[] = $selector;
}
}
}
}
}
}