Skip to content

Instantly share code, notes, and snippets.

/scope.diff Secret

Created August 14, 2015 16:28
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/a5df9f736b9e033964f4 to your computer and use it in GitHub Desktop.
Save anonymous/a5df9f736b9e033964f4 to your computer and use it in GitHub Desktop.
diff --git a/lib/Mojo/DOM/CSS.pm b/lib/Mojo/DOM/CSS.pm
index 88de1dc..a6f2721 100644
--- a/lib/Mojo/DOM/CSS.pm
+++ b/lib/Mojo/DOM/CSS.pm
@@ -27,7 +27,9 @@ sub _ancestor {
my ($selectors, $current, $tree, $one, $pos) = @_;
while ($current = $current->[3]) {
- return undef if $current->[0] eq 'root' || $current eq $tree;
+ return undef
+ if ($current->[0] eq 'root' || $current eq $tree)
+ && !_scope($selectors->[$pos]);
return 1 if _combinator($selectors, $current, $tree, $pos);
last if $one;
}
@@ -54,7 +56,7 @@ sub _combinator {
# Selector
return undef unless my $c = $selectors->[$pos];
if (ref $c) {
- return undef unless _selector($c, $current);
+ return undef unless _selector($c, $current, $tree);
return 1 unless $c = $selectors->[++$pos];
}
@@ -82,8 +84,11 @@ sub _compile {
# Separator
if ($css =~ /\G\s*,\s*/gc) { push @$group, [] }
- # Combinator
- elsif ($css =~ /\G\s*([ >+~])\s*/gc) { push @$selectors, $1 }
+ # Combinator (and implicit ":scope")
+ elsif ($css =~ /\G\s*([ >+~])\s*/gc) {
+ push @$last, ['pc', 'scope'] unless @$last;
+ push @$selectors, $1;
+ }
# Class or ID
elsif ($css =~ /\G([.#])((?:$ESCAPE_RE\s|\\.|[^,.#:[ >~+])+)/gco) {
@@ -142,7 +147,10 @@ sub _match {
sub _name {qr/(?:^|:)\Q@{[_unescape(shift)]}\E$/}
sub _pc {
- my ($class, $args, $current) = @_;
+ my ($class, $args, $current, $tree) = @_;
+
+ # ":scope"
+ return $current->[0] eq 'root' || $current eq $tree if $class eq 'scope';
# ":empty"
return !grep { !_empty($_) } @$current[4 .. $#$current] if $class eq 'empty';
@@ -186,6 +194,8 @@ sub _pc {
return undef;
}
+sub _scope { $_[0] && $_[0][0][0] eq 'pc' && $_[0][0][1] eq 'scope' }
+
sub _select {
my ($one, $tree, $group) = @_;
@@ -203,7 +213,7 @@ sub _select {
}
sub _selector {
- my ($selector, $current) = @_;
+ my ($selector, $current, $tree) = @_;
for my $s (@$selector) {
my $type = $s->[0];
@@ -215,7 +225,9 @@ 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, $tree);
+ }
}
return 1;
diff --git a/t/mojo/dom.t b/t/mojo/dom.t
index da76dee..b23dd17 100644
--- a/t/mojo/dom.t
+++ b/t/mojo/dom.t
@@ -1218,6 +1218,21 @@ is $dom->at('#♥ ~ #☃ ~ *:nth-last-child(1)')->text, 'G', 'right text';
is $dom->at('#♥ + *:nth-last-child(2)')->text, 'F', 'right text';
is $dom->at('#♥ ~ *:nth-last-child(2)')->text, 'F', 'right text';
+# Scoped selectors
+$dom = Mojo::DOM::->new(<<EOF);
+<div>
+ <p>One</p>
+ <p>Two</p>
+</div>
+<div>
+ <p>Three</p>
+ <p>Four</p>
+</div>
+EOF
+is $dom->at('div')->at(':scope > p')->text, 'One', 'right text';
+is $dom->at('p')->at(':scope + p')->text, 'Two', 'right text';
+is $dom->find('div')->last->at(':scope > p')->text, 'Three', 'right text';
+
# Adding nodes
$dom = Mojo::DOM->new(<<EOF);
<ul>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment