Skip to content

Commit

Permalink
added following, following_siblings, preceding and preceding_siblings…
Browse files Browse the repository at this point in the history
… methods to Mojo::DOM
  • Loading branch information
kraih committed Dec 9, 2014
1 parent d05e330 commit 761137b
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 5 deletions.
2 changes: 2 additions & 0 deletions Changes
Expand Up @@ -3,6 +3,8 @@
- Removed deprecated emit_safe method from Mojo::EventEmitter.
- Removed deprecated render_static method from Mojolicious::Controller.
- Removed deprecated has_conditions method from Mojolicious::Routes::Route.
- Added following, following_siblings, preceding and preceding_siblings
methods to Mojo::DOM.
- Improved Mojo::DOM::HTML performance slightly.
- Fixed parent combinator bug in Mojo::DOM::CSS.
- Fixed whitespace bug in Mojo::DOM::HTML.
Expand Down
62 changes: 57 additions & 5 deletions lib/Mojo/DOM.pm
Expand Up @@ -94,6 +94,9 @@ sub contents { $_[0]->_collect(_nodes($_[0]->tree)) }

sub find { $_[0]->_collect(@{$_[0]->_css->select($_[1])}) }

sub following_siblings { $_[0]->_collect(@{_siblings($_[0])->[1]}) }
sub following { _select($_[0]->_collect(@{_siblings($_[0], 1)->[1]}), $_[1]) }

sub match { $_[0]->_css->match($_[1]) ? $_[0] : undef }

sub namespace {
Expand Down Expand Up @@ -122,8 +125,8 @@ sub new {
return @_ ? $self->parse(@_) : $self;
}

sub next { _maybe($_[0], $_[0]->_siblings(1)->[1]) }
sub next_sibling { _maybe($_[0], $_[0]->_siblings->[1]) }
sub next { _maybe($_[0], $_[0]->_siblings(1)->[1][0]) }
sub next_sibling { _maybe($_[0], $_[0]->_siblings->[1][0]) }

sub node { shift->tree->[0] }

Expand All @@ -135,12 +138,15 @@ sub parent {

sub parse { shift->_delegate(parse => @_) }

sub preceding { _select($_[0]->_collect(@{_siblings($_[0], 1)->[0]}), $_[1]) }
sub preceding_siblings { $_[0]->_collect(@{_siblings($_[0])->[0]}) }

sub prepend { shift->_add(0, @_) }

sub prepend_content { shift->_content(0, 0, @_) }

sub previous { _maybe($_[0], $_[0]->_siblings(1)->[0]) }
sub previous_sibling { _maybe($_[0], $_[0]->_siblings->[0]) }
sub previous { _maybe($_[0], $_[0]->_siblings(1)->[0][-1]) }
sub previous_sibling { _maybe($_[0], $_[0]->_siblings->[0][-1]) }

sub remove { shift->replace('') }

Expand Down Expand Up @@ -338,7 +344,7 @@ sub _siblings {
$match ? push @after, $node : push @before, $node;
}

return $all ? [@before, @after] : [$before[-1], $after[0]];
return $all ? [@before, @after] : [\@before, \@after];
}

sub _start { $_[0][0] eq 'root' ? 1 : 4 }
Expand Down Expand Up @@ -635,6 +641,29 @@ All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
# Find elements with a class that contains dots
my @divs = $dom->find('div.foo\.bar')->each;
=head2 following
my $collection = $dom->following;
my $collection = $dom->following('div > p');
Find all sibling elements after this node matching the CSS selector and return
a L<Mojo::Collection> object containing these elements as L<Mojo::DOM>
objects. All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
# List types of sibling elements after this node
say $dom->following->map('type')->join("\n");
=head2 following_siblings
my $collection = $dom->following_siblings;
Return a L<Mojo::Collection> object containing the sibling nodes after this
element as L<Mojo::DOM> objects.
# " C "
$dom->parse('<p>A</p>B<!-- C -->')
->at('p')->following_siblings->last->content;
=head2 match
my $result = $dom->match('html title');
Expand Down Expand Up @@ -711,6 +740,29 @@ Parse HTML/XML fragment with L<Mojo::DOM::HTML>.
# Parse XML
my $dom = Mojo::DOM->new->xml(1)->parse($xml);
=head2 preceding
my $collection = $dom->preceding;
my $collection = $dom->preceding('div > p');
Find all sibling elements before this node matching the CSS selector and
return a L<Mojo::Collection> object containing these elements as L<Mojo::DOM>
objects. All selectors from L<Mojo::DOM::CSS/"SELECTORS"> are supported.
# List types of sibling elements before this node
say $dom->preceding->map('type')->join("\n");
=head2 preceding_siblings
my $collection = $dom->preceding_siblings;
Return a L<Mojo::Collection> object containing the sibling nodes before this
element as L<Mojo::DOM> objects.
# "B"
$dom->parse('<!-- A -->B<p>C</p>D')
->at('p')->preceding_siblings->last->content;
=head2 prepend
$dom = $dom->prepend('<p>I ♥ Mojolicious!</p>');
Expand Down
33 changes: 33 additions & 0 deletions t/mojo/dom.t
Expand Up @@ -150,6 +150,23 @@ ok !$dom->at('simple')->ancestors->first->xml, 'XML mode not active';
# Nodes
$dom = Mojo::DOM->new(
'<!DOCTYPE before><p>test<![CDATA[123]]><!-- 456 --></p><?after?>');
is $dom->at('p')->preceding_siblings->first->content, ' before',
'right content';
is $dom->at('p')->preceding_siblings->size, 1, 'right number of nodes';
is $dom->at('p')->contents->last->preceding_siblings->first->content, 'test',
'right content';
is $dom->at('p')->contents->last->preceding_siblings->last->content, '123',
'right content';
is $dom->at('p')->contents->last->preceding_siblings->size, 2,
'right number of nodes';
is $dom->contents->first->preceding_siblings->size, 0, 'no preceding nodes';
is $dom->at('p')->following_siblings->first->content, 'after', 'right content';
is $dom->at('p')->following_siblings->size, 1, 'right number of nodes';
is $dom->contents->first->following_siblings->first->type, 'p', 'right type';
is $dom->contents->first->following_siblings->last->content, 'after',
'right content';
is $dom->contents->first->following_siblings->size, 2, 'right number of nodes';
is $dom->contents->last->following_siblings->size, 0, 'no following nodes';
is $dom->at('p')->previous_sibling->content, ' before', 'right content';
is $dom->at('p')->previous_sibling->previous_sibling, undef,
'no more siblings';
Expand Down Expand Up @@ -222,6 +239,22 @@ is $dom->at('b')->contents->first->prepend_content('h')->content, 'h:)g',
'right content';
is "$dom", '<script><i><b>h:)g</b>a</i><b>fce</b>1<b>d</b></script>',
'right result';
is $dom->at('i')->following->last->text, 'd', 'right text';
is $dom->at('i')->following->size, 2, 'right number of following elements';
is $dom->at('i')->following('b:last-of-type')->first->text, 'd', 'right text';
is $dom->at('i')->following('b:last-of-type')->size, 1,
'right number of following elements';
is $dom->at('script > b:last-of-type')->following->size, 0,
'no following elements';
is $dom->at('script > b:last-of-type')->preceding->first->type, 'i',
'right type';
is $dom->at('script > b:last-of-type')->preceding->size, 2,
'right number of preceding elements';
is $dom->at('script > b:last-of-type')->preceding('b')->first->type, 'b',
'right type';
is $dom->at('script > b:last-of-type')->preceding('b')->size, 1,
'right number of preceding elements';
is $dom->at('i')->preceding->size, 0, 'no preceding elements';

# XML nodes
$dom = Mojo::DOM->new->xml(1)->parse('<b>test<image /></b>');
Expand Down

0 comments on commit 761137b

Please sign in to comment.