X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FDOM%2FTiny.pm;h=cf6cddc398bbb604125249cc3c2d802dd6a6aa17;hb=8563f5274bc14e4ee3994b6ceefc66d816d87f1d;hp=84dfeeafff9901f2be233737e667e7a10ca61a16;hpb=927f135111d601e084c5a50e2d806bddd5c9cefb;p=catagits%2FDOM-Tiny.git diff --git a/lib/DOM/Tiny.pm b/lib/DOM/Tiny.pm index 84dfeea..cf6cddc 100644 --- a/lib/DOM/Tiny.pm +++ b/lib/DOM/Tiny.pm @@ -11,13 +11,21 @@ use overload fallback => 1; use Carp 'croak'; -use DOM::Tiny::Collection; +use DOM::Tiny::_Collection; use DOM::Tiny::CSS; use DOM::Tiny::HTML; use Scalar::Util qw(blessed weaken); our $VERSION = '0.001'; +sub new { + my $class = shift; + my $self = bless \DOM::Tiny::HTML->new, ref $class || $class; + return @_ ? $self->parse(@_) : $self; +} + +sub TO_JSON { shift->_delegate('render') } + sub all_text { shift->_all_text(1, @_) } sub ancestors { _select($_[0]->_collect($_[0]->_ancestors), $_[1]) } @@ -97,12 +105,6 @@ sub namespace { return undef; } -sub new { - my $class = shift; - my $self = bless \DOM::Tiny::HTML->new, ref $class || $class; - return @_ ? $self->parse(@_) : $self; -} - sub next { $_[0]->_maybe($_[0]->_siblings(1, 0)->[1]) } sub next_node { $_[0]->_maybe($_[0]->_siblings(0, 0)->[1]) } @@ -151,7 +153,7 @@ sub tag { return $self; } -sub tap { shift->DOM::Tiny::Collection::tap(@_) } +sub tap { shift->DOM::Tiny::_Collection::tap(@_) } sub text { shift->_all_text(0, @_) } @@ -164,10 +166,8 @@ sub type { shift->tree->[0] } sub val { my $self = shift; - my $tag = $self->tag; - # "option" - return defined $self->{value} ? $self->{value} : $self->text if $tag eq 'option'; + return $self->{value} // $self->text if (my $tag = $self->tag) eq 'option'; # "textarea", "input" or "button" return $tag eq 'textarea' ? $self->text : $self->{value} if $tag ne 'select'; @@ -225,7 +225,7 @@ sub _build { shift->new->tree(shift)->xml(shift) } sub _collect { my $self = shift; my $xml = $self->xml; - return DOM::Tiny::Collection->new(map { $self->_build($_, $xml) } @_); + return DOM::Tiny::_Collection->new(map { $self->_build($_, $xml) } @_); } sub _content { @@ -389,6 +389,8 @@ sub _wrap { =encoding utf8 +=for Pod::Coverage TO_JSON + =head1 NAME DOM::Tiny - Minimalistic HTML/XML DOM parser with CSS selectors @@ -422,9 +424,11 @@ DOM::Tiny - Minimalistic HTML/XML DOM parser with CSS selectors =head1 DESCRIPTION -L is a minimalistic and relaxed HTML/XML DOM parser with CSS -selector support based on L. It will even try to interpret broken -HTML and XML, so you should not use it for validation. +L is a minimalistic and relaxed pure-perl HTML/XML DOM parser with +support for the L and +L based on L. It +will even try to interpret broken HTML and XML, so you should not use it for +validation. =head1 NODES AND ELEMENTS @@ -482,6 +486,14 @@ XML detection can also be disabled with the L method. L implements the following methods. +=head2 new + + my $dom = DOM::Tiny->new; + my $dom = DOM::Tiny->new('I ♥ DOM::Tiny!'); + +Construct a new scalar-based L object and L HTML/XML +fragment if necessary. + =head2 all_text my $trimmed = $dom->all_text; @@ -502,7 +514,7 @@ whitespace trimming is enabled by default. my $collection = $dom->ancestors('div ~ p'); Find all ancestor elements of this node matching the CSS selector and return a -L object containing these elements as L +L containing these elements as L objects. All selectors from L are supported. # List tag names of ancestor elements @@ -573,7 +585,7 @@ This element's attributes. my $collection = $dom->child_nodes; -Return a L object containing all child nodes of this +Return a L containing all child nodes of this element as L objects. # "

123

" @@ -591,7 +603,7 @@ element as L objects. my $collection = $dom->children('div ~ p'); Find all child elements of this element matching the CSS selector and return a -L object containing these elements as L +L containing these elements as L objects. All selectors from L are supported. # Show tag name of random child element @@ -628,7 +640,7 @@ and C nodes) or raw content. my $collection = $dom->descendant_nodes; -Return a L object containing all descendant nodes of +Return a L containing all descendant nodes of this element as L objects. # "

123

" @@ -646,7 +658,7 @@ this element as L objects. my $collection = $dom->find('div ~ p'); Find all descendant elements of this element matching the CSS selector and -return a L object containing these elements as +return a L containing these elements as L objects. All selectors from L are supported. @@ -668,7 +680,7 @@ supported. my $collection = $dom->following('div ~ p'); Find all sibling elements after this node matching the CSS selector and return -a L object containing these elements as L +a L containing these elements as L objects. All selectors from L are supported. # List tags of sibling elements after this node @@ -678,7 +690,7 @@ objects. All selectors from L are supported. my $collection = $dom->following_nodes; -Return a L object containing all sibling nodes after +Return a L containing all sibling nodes after this node as L objects. # "C" @@ -711,14 +723,6 @@ Find this element's namespace or return C if none could be found. # Find namespace for an element that may or may not have a namespace prefix my $namespace = $dom->at('svg > circle')->namespace; -=head2 new - - my $dom = DOM::Tiny->new; - my $dom = DOM::Tiny->new('I ♥ DOM::Tiny!'); - -Construct a new scalar-based L object and L HTML/XML -fragment if necessary. - =head2 next my $sibling = $dom->next; @@ -766,7 +770,7 @@ Parse HTML/XML fragment with L. my $collection = $dom->preceding('div ~ p'); Find all sibling elements before this node matching the CSS selector and return -a L object containing these elements as L +a L containing these elements as L objects. All selectors from L are supported. # List tags of sibling elements before this node @@ -776,8 +780,8 @@ objects. All selectors from L are supported. my $collection = $dom->preceding_nodes; -Return a L object containing all sibling nodes before -this node as L objects. +Return a L containing all sibling nodes +before this node as L objects. # "A" $dom->parse('A

C

')->at('p')->preceding_nodes->first->content; @@ -895,7 +899,7 @@ This element's tag name. $dom = $dom->tap(sub {...}); -Alias for L. +Equivalent to L. =head2 text @@ -1060,6 +1064,217 @@ Alias for L. Alias for L. +=head1 COLLECTION METHODS + +Some L methods return an array-based collection object, which can +either be accessed directly as an array reference, or with the following +methods. + + # Chain methods + $collection->map(sub { ucfirst })->shuffle->each(sub { + my ($word, $num) = @_; + say "$num: $word"; + }); + + # Access array directly to manipulate collection + $collection->[23] += 100; + say for @$collection; + +=head2 compact + + my $new = $collection->compact; + +Create a new collection with all elements that are defined and not an empty +string. + + # $collection contains (0, 1, undef, 2, '', 3) + $collection->compact->join(', '); # "0, 1, 2, 3" + +=head2 each + + my @elements = $collection->each; + $collection = $collection->each(sub {...}); + +Evaluate callback for each element in collection or return all elements as a +list if none has been provided. The element will be the first argument passed +to the callback and is also available as C<$_>. + + # Make a numbered list + $collection->each(sub { + my ($e, $num) = @_; + say "$num: $e"; + }); + +=head2 first + + my $first = $collection->first; + my $first = $collection->first(qr/foo/); + my $first = $collection->first(sub {...}); + my $first = $collection->first($method); + my $first = $collection->first($method, @args); + +Evaluate regular expression/callback for, or call method on, each element in +collection and return the first one that matched the regular expression, or for +which the callback/method returned true. The element will be the first argument +passed to the callback and is also available as C<$_>. + + # Longer version + my $first = $collection->first(sub { $_->$method(@args) }); + + # Find first value that contains the word "dom" + my $interesting = $collection->first(qr/dom/i); + + # Find first value that is greater than 5 + my $greater = $collection->first(sub { $_ > 5 }); + +=head2 flatten + + my $new = $collection->flatten; + +Flatten nested collections/arrays recursively and create a new collection with +all elements. + + # $collection contains (1, [2, [3, 4], 5, [6]], 7) + $collection->flatten->join(', '); # "1, 2, 3, 4, 5, 6, 7" + +=head2 grep + + my $new = $collection->grep(qr/foo/); + my $new = $collection->grep(sub {...}); + my $new = $collection->grep($method); + my $new = $collection->grep($method, @args); + +Evaluate regular expression/callback for, or call method on, each element in +collection and create a new collection with all elements that matched the +regular expression, or for which the callback/method returned true. The element +will be the first argument passed to the callback and is also available as +C<$_>. + + # Longer version + my $new = $collection->grep(sub { $_->$method(@args) }); + + # Find all values that contain the word "dom" + my $interesting = $collection->grep(qr/dom/i); + + # Find all values that are greater than 5 + my $greater = $collection->grep(sub { $_ > 5 }); + +=head2 join + + my $stream = $collection->join; + my $stream = $collection->join("\n"); + +Turn collection into string. + + # Join all values with commas + $collection->join(', '); + +=head2 last + + my $last = $collection->last; + +Return the last element in collection. + +=head2 map + + my $new = $collection->map(sub {...}); + my $new = $collection->map($method); + my $new = $collection->map($method, @args); + +Evaluate callback for, or call method on, each element in collection and create +a new collection from the results. The element will be the first argument +passed to the callback and is also available as C<$_>. + + # Longer version + my $new = $collection->map(sub { $_->$method(@args) }); + + # Append the word "dom" to all values + my $domified = $collection->map(sub { $_ . 'dom' }); + +=head2 reduce + + my $result = $collection->reduce(sub {...}); + my $result = $collection->reduce(sub {...}, $initial); + +Reduce elements in collection with callback, the first element will be used as +initial value if none has been provided. + + # Calculate the sum of all values + my $sum = $collection->reduce(sub { $a + $b }); + + # Count how often each value occurs in collection + my $hash = $collection->reduce(sub { $a->{$b}++; $a }, {}); + +=head2 reverse + + my $new = $collection->reverse; + +Create a new collection with all elements in reverse order. + +=head2 slice + + my $new = $collection->slice(4 .. 7); + +Create a new collection with all selected elements. + + # $collection contains ('A', 'B', 'C', 'D', 'E') + $collection->slice(1, 2, 4)->join(' '); # "B C E" + +=head2 shuffle + + my $new = $collection->shuffle; + +Create a new collection with all elements in random order. + +=head2 size + + my $size = $collection->size; + +Number of elements in collection. + +=head2 sort + + my $new = $collection->sort; + my $new = $collection->sort(sub {...}); + +Sort elements based on return value of callback and create a new collection +from the results. + + # Sort values case-insensitive + my $case_insensitive = $collection->sort(sub { uc($a) cmp uc($b) }); + +=head2 tap + + $collection = $collection->tap(sub {...}); + +Equivalent to L. + +=head2 to_array + + my $array = $collection->to_array; + +Turn collection into array reference. + +=head2 uniq + + my $new = $collection->uniq; + my $new = $collection->uniq(sub {...}); + my $new = $collection->uniq($method); + my $new = $collection->uniq($method, @args); + +Create a new collection without duplicate elements, using the string +representation of either the elements or the return value of the +callback/method. + + # Longer version + my $new = $collection->uniq(sub { $_->$method(@args) }); + + # $collection contains ('foo', 'bar', 'bar', 'baz') + $collection->uniq->join(' '); # "foo bar baz" + + # $collection contains ([1, 2], [2, 1], [3, 2]) + $collection->uniq(sub{ $_->[1] })->to_array; # "[[1, 2], [2, 1]]" + =head1 BUGS Report any issues on the public bugtracker.