File Coverage

blib/lib/Web/Query.pm
Criterion Covered Total %
statement 426 430 99.0
branch 125 138 90.5
condition 36 83 43.3
subroutine 75 75 100.0
pod 47 49 95.9
total 709 775 91.4


line stmt bran cond sub pod time code
1             package Web::Query;
2             our $AUTHORITY = 'cpan:TOKUHIROM';
3             # ABSTRACT: Yet another scraping library like jQuery
4             $Web::Query::VERSION = '0.38';
5 45     45   551850 use strict;
  45         72  
  45         1143  
6 45     45   149 use warnings;
  45         46  
  45         925  
7 45     45   687 use 5.008001;
  45         101  
8 45     45   16083 use parent qw/Exporter/;
  45         10230  
  45         206  
9 45     45   21359 use HTML::TreeBuilder::XPath;
  45         2145999  
  45         336  
10 45     45   26149 use LWP::UserAgent;
  45         1302848  
  45         1498  
11 45     45   19066 use HTML::Selector::XPath 0.20 qw/selector_to_xpath/;
  45         85721  
  45         2515  
12 45     45   226 use Scalar::Util qw/blessed refaddr/;
  45         56  
  45         2139  
13 45     45   179 use HTML::Entities qw/encode_entities/;
  45         55  
  45         2261  
14              
15 45     45   460 use List::Util 1.44 qw/ reduce uniq /;
  45         701  
  45         2233  
16 45     45   166 use Scalar::Util qw/ refaddr /;
  45         49  
  45         55747  
17              
18             our @EXPORT = qw/wq/;
19              
20             our $RESPONSE;
21              
22 90     90 1 19489 sub wq { Web::Query->new(@_) }
23              
24             our $UserAgent = LWP::UserAgent->new();
25              
26             sub __ua {
27 6   33 6   22 $UserAgent ||= LWP::UserAgent->new( agent => __PACKAGE__ . "/" . __PACKAGE__->VERSION );
28 6         37 $UserAgent;
29             }
30              
31             sub _build_tree {
32 120     120   394 my( $self, $options ) = @_;
33              
34             my $no_space_compacting = ref $self ? $self->{no_space_compacting}
35 120 100       454 : ref $options eq 'HASH' ? $options->{no_space_compacting} : 0;
    100          
36              
37 120         743 my $tree = HTML::TreeBuilder::XPath->new(
38             no_space_compacting => $no_space_compacting
39             );
40 120         21378 $tree->ignore_unknown(0);
41 120         992 $tree->store_comments(1);
42 120         543 $tree;
43             }
44              
45             sub new {
46 221     221 1 9543 my ($class, $stuff, $options) = @_;
47              
48 221 100       384 my $self = $class->_resolve_new($stuff,$options)
49             or return undef;
50              
51 218 100       468 $self->{indent} = $options->{indent} if $options->{indent};
52              
53 218         258 $self->{no_space_compacting} = $options->{no_space_compacting};
54              
55 218         491 return $self;
56             }
57              
58             sub _resolve_new {
59 221     221   233 my( $class, $stuff, $options) = @_;
60              
61 221 100       422 return $class->new_from_element([],undef,$options) unless defined $stuff;
62              
63 220 100       811 if (blessed $stuff) {
64 114 100       381 return $class->new_from_element([$stuff],undef,$options)
65             if $stuff->isa('HTML::Element');
66              
67 50 100       132 return $class->new_from_url($stuff->as_string,$options)
68             if $stuff->isa('URI');
69              
70 49 50       135 return $class->new_from_element($stuff->{trees}, undef, $options)
71             if $stuff->isa($class);
72              
73 0         0 die "Unknown source type: $stuff";
74             }
75              
76 106 100       290 return $class->new_from_element($stuff,undef,$options) if ref $stuff eq 'ARRAY';
77              
78 105 100       315 return $class->new_from_url($stuff,$options) if $stuff =~ m{^(?:https?|file)://};
79              
80 100 100       582 return $class->new_from_html($stuff,$options) if $stuff =~ /<.*?>/;
81              
82 9 50 33     312 return $class->new_from_file($stuff,$options) if $stuff !~ /\n/ && -f $stuff;
83              
84 0         0 die "Unknown source type: $stuff";
85             }
86              
87             sub new_from_url {
88 6     6 1 10 my ($class, $url,$options) = @_;
89              
90 6         14 $RESPONSE = __ua()->get($url);
91              
92 6 100       39544 return undef unless $RESPONSE->is_success;
93              
94 3         36 return $class->new_from_html($RESPONSE->decoded_content,$options);
95             }
96              
97             sub new_from_file {
98 9     9 1 16 my ($class, $fname, $options) = @_;
99 9         72 my $tree = $class->_build_tree($options);
100 9         37 $tree->parse_file($fname);
101 9         16149 my $self = $class->new_from_element([$tree->disembowel],undef,$options);
102 9         35 $self->{need_delete}++;
103 9         39 return $self;
104             }
105              
106             sub new_from_html {
107 110     110 1 14226 my ($class, $html,$options) = @_;
108 110         236 my $tree = $class->_build_tree($options);
109 110         572 $tree->parse_content($html);
110             my $self = $class->new_from_element([
111             map {
112 110 100       76742 ref $_ ? $_ : bless { _content => $_ }, 'HTML::TreeBuilder::XPath::TextNode'
  119         2774  
113             } $tree->disembowel
114             ],undef,$options);
115 110         318 $self->{need_delete}++;
116 110         1159 return $self;
117             }
118              
119             sub new_from_element {
120 593     593 1 914 my $self_or_class = shift;
121              
122 593 100       1011 my $trees = ref $_[0] eq 'ARRAY' ? $_[0] : [$_[0]];
123              
124 593   66     3328 return bless { trees => [ @$trees ], before => $_[1] },
125             ref $self_or_class || $self_or_class;
126             }
127              
128             sub end {
129 54     54 1 113 my $self = shift;
130 54         101 return $self->{before};
131             }
132              
133             sub size {
134 71     71 1 91 my $self = shift;
135 71         59 return scalar(@{$self->{trees}});
  71         217  
136             }
137              
138             sub parent {
139 2     2 1 2 my $self = shift;
140              
141 2         2 my @new = map { $_->parent } @{$self->{trees}};
  2         4  
  2         4  
142              
143 2   33     13 return (ref $self || $self)->new_from_element(\@new, $self);
144             }
145              
146             sub first {
147 6     6 1 13 my $self = shift;
148 6         14 return $self->eq(0);
149             }
150              
151             sub last {
152 3     3 1 5 my $self = shift;
153 3         6 return $self->eq(-1);
154             }
155              
156             sub get {
157 34     34 1 37 my ($self, $index) = @_;
158 34         78 return $self->{trees}[$index];
159             }
160              
161             sub eq {
162 13     13 0 15 my ($self, $index) = @_;
163 13   33     61 return (ref $self || $self)->new_from_element([$self->{trees}[$index] || ()], $self);
      66        
164             }
165              
166             sub find {
167 147     147 1 2925 my ($self, $selector) = @_;
168            
169 147 50       516 my $xpath = ref $selector ? $$selector : selector_to_xpath($selector, root => './');
170 147         9666 my @new = map { eval{ $_->findnodes($xpath) } } @{$self->{trees}};
  154         15675  
  154         525  
  147         286  
171            
172 147   33     266791 return (ref $self || $self)->new_from_element(\@new, $self);
173             }
174              
175             sub contents {
176 10     10 1 18 my ($self, $selector) = @_;
177              
178 10         11 my @new = map { $_->content_list } @{$self->{trees}};
  14         40  
  10         17  
179              
180 10 100       62 if ($selector) {
181 1 50       4 my $xpath = ref $selector ? $$selector : selector_to_xpath($selector);
182 1         121 @new = grep { $_->matches($xpath) } @new;
  3         1454  
183             }
184              
185 10   33     223 return (ref $self || $self)->new_from_element(\@new, $self);
186             }
187              
188             sub as_html {
189 52     52 1 1724 my $self = shift;
190 52         92 my %args = @_;
191              
192             my @html = map {
193             ref $_ ? ( $_->isa('HTML::TreeBuilder::XPath::TextNode') || $_->isa('HTML::TreeBuilder::XPath::CommentNode' ) )
194             ? $_->getValue
195             : $_->as_HTML( q{&<>'"}, $self->{indent}, {} )
196 66 100 66     3530 : $_
    100          
197 52         50 } @{$self->{trees}};
  52         96  
198              
199 52 100       13163 return join $args{join}, @html if defined $args{join};
200              
201 42 100       219 return wantarray ? @html : $html[0];
202             }
203              
204             sub html {
205 25     25 1 45 my $self = shift;
206              
207 25 100       57 if (@_) {
208             map {
209 2         6 $_->delete_content;
210 2         23 my $tree = $self->_build_tree;
211            
212 2         11 $tree->parse_content($_[0]);
213 2         509 $_->push_content($tree->disembowel);
214 2         3 } @{$self->{trees}};
  2         4  
215 2         81 return $self;
216             }
217              
218 23         23 my @html;
219 23         21 for my $t ( @{$self->{trees}} ) {
  23         47  
220             push @html, join '', map {
221             ref $_ ? $_->as_HTML( q{&<>'"}, $self->{indent}, {})
222 35 100       3104 : encode_entities($_)
223 23         104 } eval { $t->content_list };
  23         58  
224             }
225            
226 23 100       4747 return wantarray ? @html : $html[0];
227             }
228              
229             sub text {
230 44     44 1 574 my $self = shift;
231              
232 44 100       83 if (@_) {
233 1         1 map { $_->delete_content; $_->push_content($_[0]) } @{$self->{trees}};
  1         4  
  1         10  
  1         2  
234 1         11 return $self;
235             }
236              
237             my @html = map {
238 43 100       146 ref $_ ? $_->as_text : $_
239 43         53 } @{$self->{trees}};
  43         59  
240 43 100       830 return wantarray ? @html : $html[0];
241             }
242              
243             sub attr {
244 50     50 1 667 my $self = shift;
245              
246 50 100       103 if ( @_ == 1 ) { # getter
247             return wantarray
248 8         35 ? map { $_->attr(@_) } @{$self->{trees}}
  5         10  
249 33 100       53 : eval { $self->{trees}[0]->attr(@_) }
  28         82  
250             ;
251             }
252              
253 17         57 while( my( $attr, $value ) = splice @_, 0, 2 ) {
254 18 100       44 my $code = ref $value eq 'CODE' ? $value : undef;
255              
256 18         13 for my $t ( @{$self->{trees}} ) {
  18         38  
257 21 100       59 if ( $code ) {
258 45     45   228 no warnings 'uninitialized';
  45         61  
  45         33185  
259 2         6 my $orig = $_ = $t->attr($attr);
260 2         19 $code->();
261 2 100       13 next if $orig eq $_;
262 1         2 $value = $_;
263             }
264 20         48 $t->attr($attr => $value);
265             }
266             }
267              
268 17         204 return $self;
269             }
270              
271             sub id {
272 10     10 1 12 my $self = shift;
273              
274 10 100       20 if ( @_ ) { # setter
275 4         7 my $new_id = shift;
276              
277 4 50       6 return $self if $self->size == 0;
278              
279             return $self->each(sub{
280 3     3   7 $_->attr( id => $new_id->(@_) )
281 4 100       91 }) if ref $new_id eq 'CODE';
282              
283 3 100       7 if ( $self->size == 1 ) {
284 1         4 $self->attr( id => $new_id );
285             }
286             else {
287 2         7 return $self->first->attr( id => $new_id );
288             }
289             }
290             else { # getter
291              
292             # the eval is there in case there is no tree
293             return wantarray
294 4         23 ? map { $_->attr('id') } @{$self->{trees}}
  2         4  
295 6 100       13 : eval { $self->{trees}[0]->attr('id') }
  4         11  
296             ;
297             }
298             }
299              
300             sub name {
301 6     6 1 8 my $self = shift;
302 6         14 $self->attr( 'name', @_ );
303             }
304              
305             sub data {
306 2     2 1 3 my $self = shift;
307 2         3 my $name = shift;
308 2         7 $self->attr( join( '-', 'data', $name ), @_ );
309             }
310              
311             sub tagname {
312 12     12 1 28 my $self = shift;
313 12 100       73 my @retval = map { $_ eq '~comment' ? '#comment' : $_ }
314 12 100       54 map { ref $_ eq 'HTML::TreeBuilder::XPath::TextNode' ? '#text'
    50          
    100          
315             : ref $_ eq 'HTML::TreeBuilder::XPath::CommentNode' ? '#comment'
316             : ref $_ ? $_->tag(@_)
317             : '#text'
318             ;
319 12         12 } @{$self->{trees}};
  12         16  
320 12 50       41 return wantarray ? @retval : $retval[0];
321             }
322              
323             sub each {
324 26     26 1 351 my ($self, $code) = @_;
325 26         26 my $i = 0;
326              
327             # make a copy such that if we modify the list via 'delete',
328             # it won't change from under our feet (see t/each-and-delete.t
329             # for a case where it can)
330 26         26 my @trees = @{ $self->{trees} };
  26         49  
331 26         43 for my $tree ( @trees ) {
332 60   33     182 local $_ = (ref $self || $self)->new_from_element([$tree], $self);
333 60         127 $code->($i++, $_);
334             }
335 26         64 return $self;
336             }
337              
338             sub map {
339 33     33 1 45 my ($self, $code) = @_;
340 33         30 my $i = 0;
341             return +[map {
342 52         42 my $tree = $_;
343 52   33     145 local $_ = (ref $self || $self)->new($tree);
344 52         90 $code->($i++, $_);
345 33         23 } @{$self->{trees}}];
  33         50  
346             }
347              
348             sub filter {
349 32     32 1 382 my $self = shift;
350              
351 32         24 my @new;
352              
353 32 100       62 if (ref($_[0]) eq 'CODE') {
354 24         20 my $code = $_[0];
355 24         18 my $i = 0;
356             @new = grep {
357 37         29 my $tree = $_;
358 37   33     93 local $_ = (ref $self || $self)->new_from_element($tree);
359 37         62 $code->($i++, $_);
360 24         24 } @{$self->{trees}};
  24         38  
361             }
362             else {
363 8 50       26 my $xpath = ref $_[0] ? ${$_[0]} : selector_to_xpath($_[0]);
  0         0  
364 8         432 @new = grep { eval { $_->matches($xpath) } } @{$self->{trees}};
  14         10579  
  14         49  
  8         16  
365             }
366              
367 32   33     4119 return (ref $self || $self)->new_from_element(\@new, $self);
368             }
369              
370             sub _is_same_node {
371 22     22   86 refaddr($_[1]) == refaddr($_[2]);
372             }
373              
374             sub remove {
375 15     15 1 20 my $self = shift;
376 15         31 my $before = $self->end;
377            
378 15         41 while (defined $before) {
379 14         25 @{$before->{trees}} = grep {
380 20         19 my $el = $_;
381 20         15 not grep { $self->_is_same_node( $el, $_ ) } @{$self->{trees}};
  22         39  
  20         23  
382 14         13 } @{$before->{trees}};
  14         23  
383              
384 14         29 $before = $before->end;
385             }
386            
387 15         18 $_->delete for @{$self->{trees}};
  15         77  
388 15         571 @{$self->{trees}} = ();
  15         30  
389            
390 15         35 $self;
391             }
392              
393             sub replace_with {
394 5     5 1 6 my ( $self, $replacement ) = @_;
395              
396 5         5 my $i = 0;
397 5         6 for my $node ( @{ $self->{trees} } ) {
  5         6  
398 9         29 my $rep = $replacement;
399              
400 9 100       18 if ( ref $rep eq 'CODE' ) {
401 4   33     13 local $_ = (ref $self || $self)->new($node);
402 4         9 $rep = $rep->( $i++ => $_ );
403             }
404              
405 9 100 33     25 $rep = (ref $self || $self)->new_from_html( $rep )
406             unless ref $rep;
407              
408              
409            
410 9         12 my $r = $rep->{trees}->[0];
411 45     45   267 { no warnings;
  45         50  
  45         32971  
  9         7  
412 9 100       28 $r = $r->clone if ref $r;
413             }
414 9 100 66     110 $r->parent( $node->parent ) if ref $r and $node->parent;
415              
416 9         111 $node->replace_with( $r );
417             }
418              
419 5 100 33     67 $replacement->remove if ref $replacement eq (ref $self || $self);
420              
421 5         12 return $self;
422             }
423              
424             sub append {
425 1     1 1 1 my ($self, $stuff) = @_;
426 1   33     5 $stuff = (ref $self || $self)->new($stuff);
427              
428 1         2 foreach my $t (@{$self->{trees}}) {
  1         2  
429 2         41 $t->push_content($_) for ref($t)->clone_list(@{$stuff->{trees}});
  2         6  
430             }
431              
432 1         25 $self;
433             }
434              
435             sub prepend {
436 1     1 1 2 my ($self, $stuff) = @_;
437 1   33     4 $stuff = (ref $self || $self)->new($stuff);
438            
439 1         2 foreach my $t (@{$self->{trees}}) {
  1         3  
440 2         45 $t->unshift_content($_) for ref($t)->clone_list(@{$stuff->{trees}});
  2         7  
441             }
442            
443 1         25 $self;
444             }
445              
446              
447             sub before {
448 1     1 1 1 my ($self, $stuff) = @_;
449 1   33     4 $stuff = (ref $self || $self)->new($stuff);
450            
451 1         2 foreach my $t (@{$self->{trees}}) {
  1         2  
452 2         59 $t->preinsert(ref($t)->clone_list(@{$stuff->{trees}}));
  2         6  
453             }
454            
455 1         37 $self;
456             }
457              
458              
459             sub after {
460 1     1 1 2 my ($self, $stuff) = @_;
461 1   33     4 $stuff = (ref $self || $self)->new($stuff);
462            
463 1         2 foreach my $t (@{$self->{trees}}) {
  1         3  
464 2         81 $t->postinsert(ref($t)->clone_list(@{$stuff->{trees}}));
  2         6  
465             }
466            
467 1         37 $self;
468             }
469              
470              
471             sub insert_before {
472 1     1 1 2 my ($self, $target) = @_;
473            
474 1         1 foreach my $t (@{$target->{trees}}) {
  1         2  
475 2         62 $t->preinsert(ref($t)->clone_list(@{$self->{trees}}));
  2         7  
476             }
477            
478 1         36 $self;
479             }
480              
481             sub insert_after {
482 1     1 1 1 my ($self, $target) = @_;
483            
484 1         2 foreach my $t (@{$target->{trees}}) {
  1         2  
485 2         62 $t->postinsert(ref($t)->clone_list(@{$self->{trees}}));
  2         6  
486             }
487            
488 1         35 $self;
489             }
490              
491             sub detach {
492 1     1 1 2 my ($self) = @_;
493 1         1 $_->detach for @{$self->{trees}};
  1         5  
494 1         20 $self;
495             }
496              
497             sub add_class {
498 8     8 1 6 my ($self, $class) = @_;
499            
500 8         8 for (my $i = 0; $i < @{$self->{trees}}; $i++) {
  18         101  
501 10         8 my $t = $self->{trees}->[$i];
502 10   100     16 my $current_class = $t->attr('class') || '';
503            
504 10 100       73 my $classes = ref $class eq 'CODE' ? $class->($i, $current_class, $t) : $class;
505 10         31 my @classes = split /\s+/, $classes;
506            
507 10         12 foreach (@classes) {
508 16 100       219 $current_class .= " $_" unless $current_class =~ /(?:^|\s)$_(?:\s|$)/;
509             }
510            
511 10         49 $current_class =~ s/(?:^\s*|\s*$)//g;
512 10         14 $current_class =~ s/\s\s+/ /g;
513            
514 10         18 $t->attr('class', $current_class);
515             }
516            
517 8         15 $self;
518             }
519              
520              
521             sub remove_class {
522 8     8 1 10 my ($self, $class) = @_;
523              
524 8         9 for (my $i = 0; $i < @{$self->{trees}}; $i++) {
  18         105  
525 10         11 my $t = $self->{trees}->[$i];
526 10         19 my $current_class = $t->attr('class');
527 10 50       67 next unless defined $current_class;
528            
529 10 100       20 my $classes = ref $class eq 'CODE' ? $class->($i, $current_class, $t) : $class;
530 10         32 my @remove_classes = split /\s+/, $classes;
531             my @final = grep {
532 10         23 my $existing_class = $_;
  21         22  
533 21         14 not grep { $existing_class eq $_} @remove_classes;
  33         59  
534             } split /\s+/, $current_class;
535            
536 10         31 $t->attr('class', join ' ', @final);
537             }
538            
539 8         16 $self;
540            
541             }
542              
543             sub toggle_class {
544 3     3 1 301 my $self = shift;
545            
546 3         12 my @classes = uniq @_;
547              
548             $self->each(sub{
549 9     9   9 for my $class ( @classes ) {
550 12 100       16 my $method = $_->has_class($class) ? 'remove_class' : 'add_class';
551 12         131 $_->$method($class);
552             }
553 3         14 });
554             }
555              
556             sub has_class {
557 28     28 1 42 my ($self, $class) = @_;
558            
559 28         15 foreach my $t (@{$self->{trees}}) {
  28         35  
560 45     45   210 no warnings 'uninitialized';
  45         47  
  45         35392  
561 29 100       77 return 1 if $t->attr('class') =~ /(?:^|\s)$class(?:\s|$)/;
562             }
563            
564 14         171 return undef;
565             }
566              
567             sub clone {
568 1     1 1 1 my ($self) = @_;
569 1         1 my @clones = map { $_->clone } @{$self->{trees}};
  1         3  
  1         2  
570 1   33     71 return (ref $self || $self)->new_from_element(\@clones);
571             }
572              
573             sub add {
574 29     29 1 37 my ($self, @stuff) = @_;
575 29         33 my @nodes;
576            
577             # add(selector, context)
578 29 100 100     99 if (@stuff == 2 && !ref $stuff[0] && $stuff[1]->isa('HTML::Element')) {
      66        
579 1 50       4 my $xpath = ref $stuff[0] ? ${$stuff[0]} : selector_to_xpath($stuff[0]);
  0         0  
580 1         52 push @nodes, $stuff[1]->findnodes( $xpath, root => './');
581             }
582             else {
583             # handle any combination of html string, element object and web::query object
584             push @nodes, map {
585 29 100       46 $self->{need_delete} = 1 if $_->{need_delete};
586 29         27 delete $_->{need_delete};
587 29         22 @{$_->{trees}};
  29         46  
588 28   33     35 } map { (ref $self || $self)->new($_) } @stuff;
  29         68  
589             }
590              
591 29         3739 my %ids = map { $self->_node_id($_) => 1 } @{ $self->{trees} };
  40         68  
  29         41  
592              
593             $self->new_from_element( [
594 29         32 @{$self->{trees}}, grep { ! $ids{ $self->_node_id($_) } } @nodes
  29         38  
  36         51  
595             ], $self );
596             }
597              
598             sub _node_id {
599 76     76   75 my( undef, $node ) = @_;
600 76         211 refaddr $node;
601             }
602              
603             sub prev {
604 1     1 1 2 my $self = shift;
605 1         1 my @new;
606 1         1 for my $tree (@{$self->{trees}}) {
  1         3  
607 2         31 push @new, $tree->getPreviousSibling;
608             }
609 1   33     19 return (ref $self || $self)->new_from_element(\@new, $self);
610             }
611              
612             sub next {
613 22     22 1 17 my $self = shift;
614              
615 22         19 my @new = grep { $_ } map { $_->getNextSibling } @{ $self->{trees} };
  29         471  
  29         190  
  22         29  
616              
617 22   33     58 return (ref $self || $self)->new_from_element(\@new, $self);
618             }
619              
620             sub match {
621 26     26 1 23 my( $self, $selector ) = @_;
622              
623 26 50       63 my $xpath = ref $selector ? $$selector : selector_to_xpath($selector);
624              
625             my $results = $self->map(sub{
626 30     30   33 my(undef,$e) = @_;
627 30 50       45 return 0 unless ref $e; # it's a string
628 30         43 return !!$e->get(0)->matches($xpath);
629 26         1298 });
630              
631 26 100       139 return wantarray ? @$results : $results->[0];
632             }
633              
634             sub not {
635 19     19 1 18 my( $self, $selector ) = @_;
636              
637 19         19 my $class = ref $self;
638              
639 19 50       42 my $xpath = ref $selector ? $$selector : selector_to_xpath($selector);
640 19     23   996 $self->filter(sub { ! grep { $_->matches($xpath) } grep { ref $_ } $class->new($_)->{trees}[0] } );
  23         32  
  23         49  
  23         29  
641             }
642              
643             sub and_back {
644 1     1 1 1 my $self = shift;
645            
646 1         3 $self->add( $self->end );
647             }
648              
649             sub next_until {
650 5     5 1 6 my( $self, $selector ) = @_;
651              
652 5         5 my $class = ref $self;
653 5         8 my $collection = $class->new_from_element([],$self);
654              
655 5         8 my $next = $self->next->not($selector);
656 5         25 while( $next->size ) {
657 13         22 $collection = $collection->add($next);
658 13         24 $next = $next->next->not( $selector );
659             }
660              
661             # hide the loop from the outside world
662 5         6 $collection->{before} = $self;
663              
664 5         7 return $collection;
665             }
666              
667             sub split {
668 3     3 0 6 my( $self, $selector, %args ) = @_;
669              
670 3         4 my @current;
671             my @list;
672              
673             $self->contents->each(sub{
674 24     24   21 my(undef,$e)=@_;
675              
676 24 100       32 if( $e->match($selector) ) {
677 9         13 push @list, [ @current ];
678 9         18 @current = ( $e );
679             }
680             else {
681 15 100       22 if ( $current[1] ) {
682 6         14 $current[1] = $current[1]->add($e);
683             }
684             else {
685 9         14 $current[1] = $e;
686             }
687             }
688 3         7 });
689 3         12 push @list, [ @current ];
690              
691 3 100       5 if( $args{skip_leading} ) {
692 1         2 @list = grep { $_->[0] } @list;
  4         6  
693             }
694              
695 3 100       7 unless ( $args{pairs} ) {
696 1     2   1 @list = map { reduce { $a->add($b) } grep { $_ } @$_ } @list;
  4         9  
  2         2  
  7         20  
697             }
698              
699 3         10 return @list;
700             }
701              
702             sub last_response {
703 4     4 1 2006 return $RESPONSE;
704             }
705              
706             sub DESTROY {
707 593 100   593   61652 return unless $_[0]->{need_delete};
708              
709             # avoid memory leaks
710 119         130 eval { $_->delete } for @{$_[0]->{trees}};
  119         373  
  127         649  
711             }
712              
713             1;
714              
715             __END__