Skip to content

Instantly share code, notes, and snippets.

Created July 29, 2017 22:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anonymous/c22ed2ec1ef8779872b71528b23d6cd3 to your computer and use it in GitHub Desktop.
Save anonymous/c22ed2ec1ef8779872b71528b23d6cd3 to your computer and use it in GitHub Desktop.
diff --git a/lib/Mojo/DOM/CSS.pm b/lib/Mojo/DOM/CSS.pm
index dbedd6ba3..fcc84e8e5 100644
--- a/lib/Mojo/DOM/CSS.pm
+++ b/lib/Mojo/DOM/CSS.pm
@@ -26,12 +26,12 @@ sub select { _select(0, shift->tree, _compile(@_)) }
sub select_one { _select(1, shift->tree, _compile(@_)) }
sub _ancestor {
- my ($selectors, $current, $tree, $one, $pos) = @_;
+ my ($selectors, $current, $scope, $one, $pos) = @_;
while ($current = $current->[3]) {
- return undef if $current->[0] eq 'root' || $current eq $tree;
- return 1 if _combinator($selectors, $current, $tree, $pos);
- last if $one;
+ return undef if $current->[0] eq 'root';
+ return 1 if _combinator($selectors, $current, $scope, $pos);
+ last if $one;
}
return undef;
@@ -51,26 +51,26 @@ sub _attr {
}
sub _combinator {
- my ($selectors, $current, $tree, $pos) = @_;
+ my ($selectors, $current, $scope, $pos) = @_;
# Selector
return undef unless my $c = $selectors->[$pos];
if (ref $c) {
- return undef unless _selector($c, $current);
+ return undef unless _selector($c, $current, $scope);
return 1 unless $c = $selectors->[++$pos];
}
# ">" (parent only)
- return _ancestor($selectors, $current, $tree, 1, ++$pos) if $c eq '>';
+ return _ancestor($selectors, $current, $scope, 1, ++$pos) if $c eq '>';
# "~" (preceding siblings)
- return _sibling($selectors, $current, $tree, 0, ++$pos) if $c eq '~';
+ return _sibling($selectors, $current, $scope, 0, ++$pos) if $c eq '~';
# "+" (immediately preceding siblings)
- return _sibling($selectors, $current, $tree, 1, ++$pos) if $c eq '+';
+ return _sibling($selectors, $current, $scope, 1, ++$pos) if $c eq '+';
# " " (ancestor)
- return _ancestor($selectors, $current, $tree, 0, ++$pos);
+ return _ancestor($selectors, $current, $scope, 0, ++$pos);
}
sub _compile {
@@ -149,15 +149,18 @@ sub _equation {
}
sub _match {
- my ($group, $current, $tree) = @_;
- _combinator([reverse @$_], $current, $tree, 0) and return 1 for @$group;
+ my ($group, $current, $scope) = @_;
+ _combinator([reverse @$_], $current, $scope, 0) and return 1 for @$group;
return undef;
}
sub _name {qr/(?:^|:)\Q@{[_unescape(shift)]}\E$/}
sub _pc {
- my ($class, $args, $current) = @_;
+ my ($class, $args, $current, $scope) = @_;
+
+ # ":scope"
+ return ($current eq $scope) if $class eq 'scope';
# ":checked"
return exists $current->[2]{checked} || exists $current->[2]{selected}
@@ -215,7 +218,7 @@ sub _select {
}
sub _selector {
- my ($selector, $current) = @_;
+ my ($selector, $current, $scope) = @_;
for my $s (@$selector) {
my $type = $s->[0];
@@ -227,24 +230,26 @@ sub _selector {
elsif ($type eq 'attr') { return undef unless _attr(@$s[1, 2], $current) }
# Pseudo-class
- elsif ($type eq 'pc') { return undef unless _pc(@$s[1, 2], $current) }
+ elsif ($type eq 'pc') {
+ return undef unless _pc(@$s[1, 2], $current, $scope);
+ }
}
return 1;
}
sub _sibling {
- my ($selectors, $current, $tree, $immediate, $pos) = @_;
+ my ($selectors, $current, $scope, $immediate, $pos) = @_;
my $found;
for my $sibling (@{_siblings($current)}) {
return $found if $sibling eq $current;
# "+" (immediately preceding sibling)
- if ($immediate) { $found = _combinator($selectors, $sibling, $tree, $pos) }
+ if ($immediate) { $found = _combinator($selectors, $sibling, $scope, $pos) }
# "~" (preceding sibling)
- else { return 1 if _combinator($selectors, $sibling, $tree, $pos) }
+ else { return 1 if _combinator($selectors, $sibling, $scope, $pos) }
}
return undef;
diff --git a/t/mojo/dom.t b/t/mojo/dom.t
index aef626323..ea0b7f16b 100644
--- a/t/mojo/dom.t
+++ b/t/mojo/dom.t
@@ -1214,6 +1214,18 @@ is_deeply \@e, ['J'], 'found only child';
$dom->find('div div:only-of-type')->each(sub { push @e, shift->text });
is_deeply \@e, [qw(J K)], 'found only child';
+# Scoped selectors
+$dom = Mojo::DOM->new(<<EOF);
+<foo>
+ <foo>
+ <bar></bar>
+ </foo>
+ <bar></bar>
+</foo>
+EOF
+is $dom->at('foo')->find('foo > bar')->size, 2, 'two elements found';
+is $dom->at('foo')->find(':scope foo > bar')->size, 1, 'one element found';
+
# Sibling combinator
$dom = Mojo::DOM->new(<<EOF);
<ul>
@@ -2081,10 +2093,10 @@ $dom->find('b')->each(
$_->find('c a')->each(sub { push @results, $_->text });
}
);
-is_deeply \@results, [qw(baz yada)], 'right results';
+is_deeply \@results, [qw(bar baz yada)], 'right results';
is $dom->at('b')->at('a')->text, 'bar', 'right text';
is $dom->at('c > b > a')->text, 'bar', 'right text';
-is $dom->at('b')->at('c > b > a'), undef, 'no result';
+is $dom->at('b')->at('c > b > a')->text, 'bar', 'right text';
# Direct hash access to attributes in XML mode
$dom = Mojo::DOM->new->xml(1)->parse(<<EOF);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment