line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Mojo::DOM58::_CSS; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# This file is part of Mojo::DOM58 which is released under: |
4
|
|
|
|
|
|
|
# The Artistic License 2.0 (GPL Compatible) |
5
|
|
|
|
|
|
|
# See the documentation for Mojo::DOM58 for full license details. |
6
|
|
|
|
|
|
|
|
7
|
2
|
|
|
2
|
|
14
|
use strict; |
|
2
|
|
|
|
|
5
|
|
|
2
|
|
|
|
|
57
|
|
8
|
2
|
|
|
2
|
|
10
|
use warnings; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
5850
|
|
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
our $VERSION = '2.000'; |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
my $ESCAPE_RE = qr/\\[^0-9a-fA-F]|\\[0-9a-fA-F]{1,6}/; |
13
|
|
|
|
|
|
|
my $ATTR_RE = qr/ |
14
|
|
|
|
|
|
|
\[ |
15
|
|
|
|
|
|
|
((?:$ESCAPE_RE|[\w\-])+) # Key |
16
|
|
|
|
|
|
|
(?: |
17
|
|
|
|
|
|
|
(\W)?= # Operator |
18
|
|
|
|
|
|
|
(?:"((?:\\"|[^"])*)"|'((?:\\'|[^'])*)'|([^\]]+?)) # Value |
19
|
|
|
|
|
|
|
(?:\s+(i))? # Case-sensitivity |
20
|
|
|
|
|
|
|
)? |
21
|
|
|
|
|
|
|
\] |
22
|
|
|
|
|
|
|
/x; |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
sub new { |
25
|
982
|
|
|
982
|
0
|
1665
|
my $class = shift; |
26
|
982
|
50
|
33
|
|
|
6719
|
bless @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {}, ref $class || $class; |
|
0
|
50
|
|
|
|
0
|
|
27
|
|
|
|
|
|
|
} |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
sub tree { |
30
|
982
|
|
|
982
|
0
|
1510
|
my $self = shift; |
31
|
982
|
50
|
|
|
|
2958
|
return $self->{tree} unless @_; |
32
|
0
|
|
|
|
|
0
|
$self->{tree} = shift; |
33
|
0
|
|
|
|
|
0
|
return $self; |
34
|
|
|
|
|
|
|
} |
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
sub matches { |
37
|
44
|
|
|
44
|
0
|
102
|
my $tree = shift->tree; |
38
|
44
|
100
|
|
|
|
139
|
return $tree->[0] ne 'tag' ? undef : _match(_compile(@_), $tree, $tree); |
39
|
|
|
|
|
|
|
} |
40
|
|
|
|
|
|
|
|
41
|
402
|
|
|
402
|
0
|
1032
|
sub select { _select(0, shift->tree, _compile(@_)) } |
42
|
536
|
|
|
536
|
0
|
1213
|
sub select_one { _select(1, shift->tree, _compile(@_)) } |
43
|
|
|
|
|
|
|
|
44
|
|
|
|
|
|
|
sub _ancestor { |
45
|
1315
|
|
|
1315
|
|
2359
|
my ($selectors, $current, $tree, $one, $pos) = @_; |
46
|
|
|
|
|
|
|
|
47
|
1315
|
|
|
|
|
2763
|
while ($current = $current->[3]) { |
48
|
1385
|
100
|
100
|
|
|
5045
|
return undef if $current->[0] eq 'root' || $current eq $tree; |
49
|
1300
|
100
|
|
|
|
2616
|
return 1 if _combinator($selectors, $current, $tree, $pos); |
50
|
207
|
100
|
|
|
|
554
|
last if $one; |
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
|
53
|
137
|
|
|
|
|
366
|
return undef; |
54
|
|
|
|
|
|
|
} |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
sub _attr { |
57
|
1124
|
|
|
1124
|
|
1856
|
my ($name_re, $value_re, $current) = @_; |
58
|
|
|
|
|
|
|
|
59
|
1124
|
|
|
|
|
1560
|
my $attrs = $current->[2]; |
60
|
1124
|
|
|
|
|
2452
|
for my $name (keys %$attrs) { |
61
|
972
|
|
|
|
|
1692
|
my $value = $attrs->{$name}; |
62
|
972
|
100
|
100
|
|
|
5509
|
next if $name !~ $name_re || (!defined $value && defined $value_re); |
|
|
|
100
|
|
|
|
|
63
|
607
|
100
|
100
|
|
|
4639
|
return 1 if !(defined $value && defined $value_re) || $value =~ $value_re; |
|
|
|
100
|
|
|
|
|
64
|
|
|
|
|
|
|
} |
65
|
|
|
|
|
|
|
|
66
|
738
|
|
|
|
|
3058
|
return undef; |
67
|
|
|
|
|
|
|
} |
68
|
|
|
|
|
|
|
|
69
|
|
|
|
|
|
|
sub _combinator { |
70
|
7369
|
|
|
7369
|
|
12265
|
my ($selectors, $current, $tree, $pos) = @_; |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# Selector |
73
|
7369
|
100
|
|
|
|
14100
|
return undef unless my $c = $selectors->[$pos]; |
74
|
7367
|
100
|
|
|
|
13284
|
if (ref $c) { |
75
|
7364
|
100
|
|
|
|
11195
|
return undef unless _selector($c, $current); |
76
|
2934
|
100
|
|
|
|
11219
|
return 1 unless $c = $selectors->[++$pos]; |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
# ">" (parent only) |
80
|
1416
|
100
|
|
|
|
3578
|
return _ancestor($selectors, $current, $tree, 1, ++$pos) if $c eq '>'; |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
# "~" (preceding siblings) |
83
|
528
|
100
|
|
|
|
1051
|
return _sibling($selectors, $current, $tree, 0, ++$pos) if $c eq '~'; |
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
# "+" (immediately preceding siblings) |
86
|
481
|
100
|
|
|
|
877
|
return _sibling($selectors, $current, $tree, 1, ++$pos) if $c eq '+'; |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# " " (ancestor) |
89
|
427
|
|
|
|
|
868
|
return _ancestor($selectors, $current, $tree, 0, ++$pos); |
90
|
|
|
|
|
|
|
} |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
sub _compile { |
93
|
1026
|
|
|
1026
|
|
3136
|
my ($css, %ns) = ('' . shift, @_); |
94
|
1026
|
|
|
|
|
3370
|
$css =~ s/^\s+//; |
95
|
1026
|
|
|
|
|
2710
|
$css =~ s/\s+$//; |
96
|
|
|
|
|
|
|
|
97
|
1026
|
|
|
|
|
1949
|
my $group = [[]]; |
98
|
1026
|
|
|
|
|
2815
|
while (my $selectors = $group->[-1]) { |
99
|
3640
|
100
|
100
|
|
|
13057
|
push @$selectors, [] unless @$selectors && ref $selectors->[-1]; |
100
|
3640
|
|
|
|
|
5320
|
my $last = $selectors->[-1]; |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
# Separator |
103
|
3640
|
100
|
|
|
|
23538
|
if ($css =~ /\G\s*,\s*/gc) { push @$group, [] } |
|
11
|
100
|
|
|
|
34
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
104
|
|
|
|
|
|
|
|
105
|
|
|
|
|
|
|
# Combinator |
106
|
619
|
|
|
|
|
2177
|
elsif ($css =~ /\G\s*([ >+~])\s*/gc) { push @$selectors, $1 } |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
# Class or ID |
109
|
|
|
|
|
|
|
elsif ($css =~ /\G([.#])((?:$ESCAPE_RE\s|\\.|[^,.#:[ >~+])+)/gco) { |
110
|
140
|
100
|
|
|
|
515
|
my ($name, $op) = $1 eq '.' ? ('class', '~') : ('id', ''); |
111
|
140
|
|
|
|
|
350
|
push @$last, ['attr', _name($name), _value($op, $2)]; |
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
# Attributes |
115
|
|
|
|
|
|
|
elsif ($css =~ /\G$ATTR_RE/gco) { |
116
|
245
|
100
|
|
|
|
622
|
push @$last, [ |
|
|
100
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
117
|
|
|
|
|
|
|
'attr', _name($1), |
118
|
|
|
|
|
|
|
_value( |
119
|
|
|
|
|
|
|
defined($2) ? $2 : '', |
120
|
|
|
|
|
|
|
defined($3) ? $3 : defined($4) ? $4 : $5, |
121
|
|
|
|
|
|
|
$6 |
122
|
|
|
|
|
|
|
), |
123
|
|
|
|
|
|
|
]; |
124
|
|
|
|
|
|
|
} |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
# Pseudo-class |
127
|
|
|
|
|
|
|
elsif ($css =~ /\G:([\w\-]+)(?:\(((?:\([^)]+\)|[^)])+)\))?/gcs) { |
128
|
234
|
|
|
|
|
906
|
my ($name, $args) = (lc $1, $2); |
129
|
|
|
|
|
|
|
|
130
|
|
|
|
|
|
|
# ":matches" and ":not" (contains more selectors) |
131
|
234
|
100
|
100
|
|
|
967
|
$args = _compile($args, %ns) if $name eq 'matches' || $name eq 'not'; |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
# ":nth-*" (with An+B notation) |
134
|
234
|
100
|
|
|
|
714
|
$args = _equation($args) if $name =~ /^nth-/; |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
# ":first-*" (rewrite to ":nth-*") |
137
|
234
|
100
|
|
|
|
537
|
($name, $args) = ("nth-$1", [0, 1]) if $name =~ /^first-(.+)$/; |
138
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
# ":last-*" (rewrite to ":nth-*") |
140
|
234
|
100
|
|
|
|
483
|
($name, $args) = ("nth-$name", [-1, 1]) if $name =~ /^last-/; |
141
|
|
|
|
|
|
|
|
142
|
234
|
|
|
|
|
927
|
push @$last, ['pc', $name, $args]; |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
# Tag |
146
|
|
|
|
|
|
|
elsif ($css =~ /\G((?:$ESCAPE_RE\s|\\.|[^,.#:[ >~+])+)/gco) { |
147
|
1366
|
100
|
100
|
|
|
5857
|
my $alias = (my $name = $1) =~ s/^([^|]*)\|// && $1 ne '*' ? $1 : undef; |
148
|
1366
|
100
|
100
|
|
|
3534
|
return [['invalid']] if defined $alias && length $alias && !defined $ns{$alias}; |
|
|
|
100
|
|
|
|
|
149
|
1365
|
100
|
100
|
|
|
2814
|
my $ns = defined $alias && length $alias ? $ns{$alias} : $alias; |
150
|
1365
|
100
|
|
|
|
3957
|
push @$last, ['tag', $name eq '*' ? undef : _name($name), _unescape($ns)]; |
151
|
|
|
|
|
|
|
} |
152
|
|
|
|
|
|
|
|
153
|
1025
|
|
|
|
|
1939
|
else {last} |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
1025
|
|
|
|
|
3503
|
return $group; |
157
|
|
|
|
|
|
|
} |
158
|
|
|
|
|
|
|
|
159
|
58
|
100
|
|
58
|
|
226
|
sub _empty { $_[0][0] eq 'comment' || $_[0][0] eq 'pi' } |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
sub _equation { |
162
|
113
|
100
|
|
113
|
|
271
|
return [0, 0] unless my $equation = shift; |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
# "even" |
165
|
111
|
100
|
|
|
|
271
|
return [2, 2] if $equation =~ /^\s*even\s*$/i; |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
# "odd" |
168
|
102
|
100
|
|
|
|
255
|
return [2, 1] if $equation =~ /^\s*odd\s*$/i; |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
# "4", "+4" or "-4" |
171
|
90
|
100
|
|
|
|
446
|
return [0, $1] if $equation =~ /^\s*((?:\+|-)?\d+)\s*$/; |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
# "n", "4n", "+4n", "-4n", "n+1", "4n-1", "+4n-1" (and other variations) |
174
|
51
|
100
|
|
|
|
259
|
return [0, 0] |
175
|
|
|
|
|
|
|
unless $equation =~ /^\s*((?:\+|-)?(?:\d+)?)?n\s*((?:\+|-)\s*\d+)?\s*$/i; |
176
|
50
|
100
|
100
|
|
|
386
|
return [$1 eq '-' ? -1 : !length $1 ? 1 : $1, join('', split(' ', $2 || 0))]; |
|
|
100
|
|
|
|
|
|
177
|
|
|
|
|
|
|
} |
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
sub _match { |
180
|
5850
|
|
|
5850
|
|
10191
|
my ($group, $current, $tree) = @_; |
181
|
5850
|
|
100
|
|
|
15776
|
_combinator([reverse @$_], $current, $tree, 0) and return 1 for @$group; |
182
|
4333
|
|
|
|
|
13451
|
return undef; |
183
|
|
|
|
|
|
|
} |
184
|
|
|
|
|
|
|
|
185
|
1729
|
|
|
1729
|
|
2944
|
sub _name {qr/(?:^|:)\Q@{[_unescape(shift)]}\E$/} |
|
1729
|
|
|
|
|
3162
|
|
186
|
|
|
|
|
|
|
|
187
|
|
|
|
|
|
|
sub _namespace { |
188
|
77
|
|
|
77
|
|
133
|
my ($ns, $current) = @_; |
189
|
|
|
|
|
|
|
|
190
|
77
|
100
|
|
|
|
189
|
my $attr = $current->[1] =~ /^([^:]+):/ ? "xmlns:$1" : 'xmlns'; |
191
|
77
|
|
|
|
|
127
|
while ($current) { |
192
|
121
|
100
|
|
|
|
174
|
last if $current->[0] eq 'root'; |
193
|
117
|
100
|
|
|
|
391
|
return $current->[2]{$attr} eq $ns if exists $current->[2]{$attr}; |
194
|
|
|
|
|
|
|
|
195
|
44
|
|
|
|
|
66
|
$current = $current->[3]; |
196
|
|
|
|
|
|
|
} |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
# Failing to match yields true if searching for no namespace, false otherwise |
199
|
4
|
|
|
|
|
18
|
return !length $ns; |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub _pc { |
203
|
1463
|
|
|
1463
|
|
2504
|
my ($class, $args, $current) = @_; |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# ":checked" |
206
|
|
|
|
|
|
|
return exists $current->[2]{checked} || exists $current->[2]{selected} |
207
|
1463
|
100
|
100
|
|
|
3387
|
if $class eq 'checked'; |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
# ":not" |
210
|
1259
|
100
|
|
|
|
2231
|
return !_match($args, $current, $current) if $class eq 'not'; |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
# ":matches" |
213
|
1114
|
100
|
|
|
|
1917
|
return !!_match($args, $current, $current) if $class eq 'matches'; |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
# ":empty" |
216
|
1105
|
100
|
|
|
|
1767
|
return !grep { !_empty($_) } @$current[4 .. $#$current] if $class eq 'empty'; |
|
58
|
|
|
|
|
89
|
|
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
# ":root" |
219
|
1077
|
100
|
66
|
|
|
2029
|
return $current->[3] && $current->[3][0] eq 'root' if $class eq 'root'; |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# ":link" and ":visited" |
222
|
1025
|
100
|
100
|
|
|
2891
|
if ($class eq 'link' || $class eq 'visited') { |
223
|
26
|
100
|
66
|
|
|
128
|
return undef unless $current->[0] eq 'tag' && exists $current->[2]{href}; |
224
|
14
|
|
|
|
|
27
|
return !!grep { $current->[1] eq $_ } qw(a area link); |
|
42
|
|
|
|
|
110
|
|
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
# ":only-child" or ":only-of-type" |
228
|
999
|
100
|
100
|
|
|
2467
|
if ($class eq 'only-child' || $class eq 'only-of-type') { |
229
|
40
|
100
|
|
|
|
76
|
my $type = $class eq 'only-of-type' ? $current->[1] : undef; |
230
|
40
|
|
100
|
|
|
45
|
$_ ne $current and return undef for @{_siblings($current, $type)}; |
|
40
|
|
|
|
|
95
|
|
231
|
7
|
|
|
|
|
23
|
return 1; |
232
|
|
|
|
|
|
|
} |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
# ":nth-child", ":nth-last-child", ":nth-of-type" or ":nth-last-of-type" |
235
|
959
|
100
|
|
|
|
1702
|
if (ref $args) { |
236
|
951
|
100
|
100
|
|
|
2623
|
my $type = $class eq 'nth-of-type' |
237
|
|
|
|
|
|
|
|| $class eq 'nth-last-of-type' ? $current->[1] : undef; |
238
|
951
|
|
|
|
|
1229
|
my @siblings = @{_siblings($current, $type)}; |
|
951
|
|
|
|
|
1489
|
|
239
|
951
|
100
|
100
|
|
|
3178
|
@siblings = reverse @siblings |
240
|
|
|
|
|
|
|
if $class eq 'nth-last-child' || $class eq 'nth-last-of-type'; |
241
|
|
|
|
|
|
|
|
242
|
951
|
|
|
|
|
1979
|
for my $i (0 .. $#siblings) { |
243
|
4156
|
100
|
|
|
|
8074
|
next if (my $result = $args->[0] * $i + $args->[1]) < 1; |
244
|
2989
|
100
|
|
|
|
5861
|
return undef unless my $sibling = $siblings[$result - 1]; |
245
|
2811
|
100
|
|
|
|
7089
|
return 1 if $sibling eq $current; |
246
|
|
|
|
|
|
|
} |
247
|
|
|
|
|
|
|
} |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
# Everything else |
250
|
381
|
|
|
|
|
1597
|
return undef; |
251
|
|
|
|
|
|
|
} |
252
|
|
|
|
|
|
|
|
253
|
|
|
|
|
|
|
sub _select { |
254
|
938
|
|
|
938
|
|
1842
|
my ($one, $tree, $group) = @_; |
255
|
|
|
|
|
|
|
|
256
|
938
|
|
|
|
|
1341
|
my @results; |
257
|
938
|
100
|
|
|
|
3245
|
my @queue = @$tree[($tree->[0] eq 'root' ? 1 : 4) .. $#$tree]; |
258
|
938
|
|
|
|
|
2525
|
while (my $current = shift @queue) { |
259
|
14157
|
100
|
|
|
|
30682
|
next unless $current->[0] eq 'tag'; |
260
|
|
|
|
|
|
|
|
261
|
5653
|
|
|
|
|
11598
|
unshift @queue, @$current[4 .. $#$current]; |
262
|
5653
|
100
|
|
|
|
9991
|
next unless _match($group, $current, $tree); |
263
|
1457
|
100
|
|
|
|
5318
|
$one ? return $current : push @results, $current; |
264
|
|
|
|
|
|
|
} |
265
|
|
|
|
|
|
|
|
266
|
445
|
100
|
|
|
|
2878
|
return $one ? undef : \@results; |
267
|
|
|
|
|
|
|
} |
268
|
|
|
|
|
|
|
|
269
|
|
|
|
|
|
|
sub _selector { |
270
|
7364
|
|
|
7364
|
|
11030
|
my ($selector, $current) = @_; |
271
|
|
|
|
|
|
|
|
272
|
7364
|
|
|
|
|
11205
|
for my $s (@$selector) { |
273
|
8471
|
|
|
|
|
11574
|
my $type = $s->[0]; |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
# Tag |
276
|
8471
|
100
|
|
|
|
15245
|
if ($type eq 'tag') { |
|
|
100
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
277
|
5884
|
100
|
100
|
|
|
37795
|
return undef if defined $s->[1] && $current->[1] !~ $s->[1]; |
278
|
3057
|
100
|
100
|
|
|
8019
|
return undef if defined $s->[2] && !_namespace($s->[2], $current); |
279
|
|
|
|
|
|
|
} |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
# Attribute |
282
|
1124
|
100
|
|
|
|
2017
|
elsif ($type eq 'attr') { return undef unless _attr(@$s[1, 2], $current) } |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
# Pseudo-class |
285
|
1463
|
100
|
|
|
|
2792
|
elsif ($type eq 'pc') { return undef unless _pc(@$s[1, 2], $current) } |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
# Invalid selector |
288
|
0
|
|
|
|
|
0
|
else { return undef } |
289
|
|
|
|
|
|
|
} |
290
|
|
|
|
|
|
|
|
291
|
2934
|
|
|
|
|
5686
|
return 1; |
292
|
|
|
|
|
|
|
} |
293
|
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
sub _sibling { |
295
|
101
|
|
|
101
|
|
195
|
my ($selectors, $current, $tree, $immediate, $pos) = @_; |
296
|
|
|
|
|
|
|
|
297
|
101
|
|
|
|
|
125
|
my $found; |
298
|
101
|
|
|
|
|
120
|
for my $sibling (@{_siblings($current)}) { |
|
101
|
|
|
|
|
164
|
|
299
|
267
|
100
|
|
|
|
821
|
return $found if $sibling eq $current; |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
# "+" (immediately preceding sibling) |
302
|
191
|
100
|
|
|
|
301
|
if ($immediate) { $found = _combinator($selectors, $sibling, $tree, $pos) } |
|
110
|
|
|
|
|
187
|
|
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
# "~" (preceding sibling) |
305
|
81
|
100
|
|
|
|
130
|
else { return 1 if _combinator($selectors, $sibling, $tree, $pos) } |
306
|
|
|
|
|
|
|
} |
307
|
|
|
|
|
|
|
|
308
|
0
|
|
|
|
|
0
|
return undef; |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
sub _siblings { |
312
|
1092
|
|
|
1092
|
|
1788
|
my ($current, $type) = @_; |
313
|
|
|
|
|
|
|
|
314
|
1092
|
|
|
|
|
1501
|
my $parent = $current->[3]; |
315
|
1092
|
100
|
|
|
|
3174
|
my @siblings = grep { $_->[0] eq 'tag' } |
|
14887
|
|
|
|
|
25103
|
|
316
|
|
|
|
|
|
|
@$parent[($parent->[0] eq 'root' ? 1 : 4) .. $#$parent]; |
317
|
1092
|
100
|
|
|
|
2465
|
@siblings = grep { $type eq $_->[1] } @siblings if defined $type; |
|
643
|
|
|
|
|
1053
|
|
318
|
|
|
|
|
|
|
|
319
|
1092
|
|
|
|
|
2619
|
return \@siblings; |
320
|
|
|
|
|
|
|
} |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
sub _unescape { |
323
|
3431
|
100
|
|
3431
|
|
11373
|
return undef unless defined(my $value = shift); |
324
|
|
|
|
|
|
|
|
325
|
|
|
|
|
|
|
# Remove escaped newlines |
326
|
2134
|
|
|
|
|
3339
|
$value =~ s/\\\n//g; |
327
|
|
|
|
|
|
|
|
328
|
|
|
|
|
|
|
# Unescape Unicode characters |
329
|
2134
|
|
|
|
|
2999
|
$value =~ s/\\([0-9a-fA-F]{1,6})\s?/pack 'U', hex $1/ge; |
|
35
|
|
|
|
|
228
|
|
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
# Remove backslash |
332
|
2134
|
|
|
|
|
3036
|
$value =~ s/\\//g; |
333
|
|
|
|
|
|
|
|
334
|
2134
|
|
|
|
|
31168
|
return $value; |
335
|
|
|
|
|
|
|
} |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
sub _value { |
338
|
385
|
|
|
385
|
|
1458
|
my ($op, $value, $insensitive) = @_; |
339
|
385
|
100
|
|
|
|
1034
|
return undef unless defined $value; |
340
|
337
|
100
|
|
|
|
822
|
$value = ($insensitive ? '(?i)' : '') . quotemeta _unescape($value); |
341
|
|
|
|
|
|
|
|
342
|
|
|
|
|
|
|
# "~=" (word) |
343
|
337
|
100
|
|
|
|
1681
|
return qr/(?:^|\s+)$value(?:\s+|$)/ if $op eq '~'; |
344
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
# "|=" (hyphen-separated) |
346
|
274
|
100
|
|
|
|
630
|
return qr/^$value(?:-|$)/ if $op eq '|'; |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
# "*=" (contains) |
349
|
264
|
100
|
|
|
|
1126
|
return qr/$value/ if $op eq '*'; |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
# "^=" (begins with) |
352
|
253
|
100
|
|
|
|
674
|
return qr/^$value/ if $op eq '^'; |
353
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
# "$=" (ends with) |
355
|
223
|
100
|
|
|
|
624
|
return qr/$value$/ if $op eq '$'; |
356
|
|
|
|
|
|
|
|
357
|
|
|
|
|
|
|
# Everything else |
358
|
192
|
|
|
|
|
2091
|
return qr/^$value$/; |
359
|
|
|
|
|
|
|
} |
360
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
1; |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
=for Pod::Coverage *EVERYTHING* |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
=cut |