File Coverage

blib/lib/Mojo/DOM58.pm
Criterion Covered Total %
statement 234 234 100.0
branch 123 126 97.6
condition 59 66 89.3
subroutine 78 78 100.0
pod 43 44 97.7
total 537 548 97.9


line stmt bran cond sub pod time code
1             package Mojo::DOM58;
2              
3 2     2   221087 use strict;
  2         3  
  2         74  
4 2     2   9 use warnings;
  2         8  
  2         237  
5              
6             use overload
7 4     4   16 '@{}' => sub { shift->child_nodes },
8 95     95   272 '%{}' => sub { shift->attr },
9 32     32   4743 bool => sub {1},
10 142     142   24651 '""' => sub { shift->to_string },
11 2     2   1269 fallback => 1;
  2         3703  
  2         24  
12              
13 2     2   226 use Exporter 'import';
  2         3  
  2         69  
14 2     2   604 use Mojo::DOM58::_Collection;
  2         5  
  2         57  
15 2     2   1130 use Mojo::DOM58::_CSS;
  2         9  
  2         108  
16 2     2   1267 use Mojo::DOM58::_HTML 'tag_to_html';
  2         12  
  2         183  
17 2     2   13 use Scalar::Util qw(blessed weaken);
  2         3  
  2         98  
18 2     2   1371 use Storable 'dclone';
  2         8524  
  2         8861  
19              
20             our $VERSION = '3.002';
21              
22             our @EXPORT_OK = 'tag_to_html';
23              
24             sub new {
25 2184     2184 1 970208 my $class = shift;
26 2184   66     6212 my $self = bless \Mojo::DOM58::_HTML->new, ref $class || $class;
27 2184 100       7023 return @_ ? $self->parse(@_) : $self;
28             }
29              
30             sub new_tag {
31 11     11 1 3488 my $self = shift;
32 11         23 my $new = $self->new;
33 11         31 $$new->tag(@_);
34 11 100       24 $$new->xml($$self->xml) if ref $self;
35 11         34 return $new;
36             }
37              
38 1     1 0 86 sub TO_JSON { ${shift()}->render }
  1         6  
39              
40 28     28 1 117 sub all_text { _text(_nodes($_[0]->tree), $_[0]->xml, 1) }
41              
42 15     15 1 63 sub ancestors { _select($_[0]->_collect([_ancestors($_[0]->tree)]), $_[1]) }
43              
44 9     9 1 40 sub append { shift->_add(1, @_) }
45 13     13 1 43 sub append_content { shift->_content(1, 0, @_) }
46              
47             sub at {
48 646     646 1 2169 my $self = shift;
49 646 100       1787 return undef unless my $result = $self->_css->select_one(@_);
50 591         4259 return $self->_build($result, $self->xml);
51             }
52              
53             sub attr {
54 168     168 1 382 my $self = shift;
55              
56             # Hash
57 168         362 my $tree = $self->tree;
58 168 100       577 my $attrs = $tree->[0] ne 'tag' ? {} : $tree->[2];
59 168 100       1050 return $attrs unless @_;
60              
61             # Get
62 40 100 100     309 return $attrs->{$_[0]} unless @_ > 1 || ref $_[0];
63              
64             # Set
65 4 100       17 my $values = ref $_[0] ? $_[0] : {@_};
66 4         15 @$attrs{keys %$values} = values %$values;
67              
68 4         20 return $self;
69             }
70              
71 59     59 1 186 sub child_nodes { $_[0]->_collect(_nodes($_[0]->tree)) }
72              
73 13     13 1 57 sub children { _select($_[0]->_collect(_nodes($_[0]->tree, 1)), $_[1]) }
74              
75             sub content {
76 55     55 1 119 my $self = shift;
77              
78 55         234 my $type = $self->type;
79 55 100 100     268 if ($type eq 'root' || $type eq 'tag') {
80 24 100       199 return $self->_content(0, 1, @_) if @_;
81 7         21 my $html = Mojo::DOM58::_HTML->new(xml => $self->xml);
82 7         14 return join '', map { $html->tree($_)->render } @{_nodes($self->tree)};
  12         28  
  7         17  
83             }
84              
85 31 100       97 return $self->tree->[1] unless @_;
86 3         10 $self->tree->[1] = shift;
87 3         16 return $self;
88             }
89              
90 13     13 1 47 sub descendant_nodes { $_[0]->_collect(_all(_nodes($_[0]->tree))) }
91              
92             sub find {
93 428     428 1 2925 my $self = shift;
94 428         1519 return $self->_collect($self->_css->select(@_));
95             }
96              
97 8     8 1 28 sub following { _select($_[0]->_collect(_siblings($_[0]->tree, 1, 1)), $_[1]) }
98 7     7 1 26 sub following_nodes { $_[0]->_collect(_siblings($_[0]->tree, 0, 1)) }
99              
100 44     44 1 127 sub matches { shift->_css->matches(@_) }
101              
102             sub namespace {
103 18     18 1 37 my $self = shift;
104              
105 18 100       41 return undef if (my $tree = $self->tree)->[0] ne 'tag';
106              
107             # Extract namespace prefix and search parents
108 16 100       84 my $ns = $tree->[1] =~ /^(.*?):/ ? "xmlns:$1" : undef;
109 16         45 for my $node ($tree, _ancestors($tree)) {
110              
111             # Namespace for prefix
112 35         56 my $attrs = $node->[2];
113 35 100 100     79 if ($ns) { $_ eq $ns and return $attrs->{$_} for keys %$attrs }
  13 100       107  
114              
115             # Namespace attribute
116 10         208 elsif (defined $attrs->{xmlns}) { return $attrs->{xmlns} }
117             }
118              
119 1         5 return undef;
120             }
121              
122 13     13 1 35 sub next { $_[0]->_maybe(_siblings($_[0]->tree, 1, 1, 0)) }
123 5     5 1 13 sub next_node { $_[0]->_maybe(_siblings($_[0]->tree, 0, 1, 0)) }
124              
125             sub parent {
126 48     48 1 88 my $self = shift;
127 48 50       142 return undef if (my $tree = $self->tree)->[0] eq 'root';
128 48         144 return $self->_build(_parent($tree), $self->xml);
129             }
130              
131 184 50   184 1 405 sub parse { ${$_[0]}->parse($_[1]) and return $_[0] }
  184         1083  
132              
133 5     5 1 54 sub preceding { _select($_[0]->_collect(_siblings($_[0]->tree, 1, 0)), $_[1]) }
134 7     7 1 21 sub preceding_nodes { $_[0]->_collect(_siblings($_[0]->tree, 0)) }
135              
136 11     11 1 36 sub prepend { shift->_add(0, @_) }
137 6     6 1 17 sub prepend_content { shift->_content(0, 0, @_) }
138              
139 7     7 1 22 sub previous { $_[0]->_maybe(_siblings($_[0]->tree, 1, 0, -1)) }
140 5     5 1 14 sub previous_node { $_[0]->_maybe(_siblings($_[0]->tree, 0, 0, -1)) }
141              
142 6     6 1 20 sub remove { shift->replace('') }
143              
144             sub replace {
145 24     24 1 72 my ($self, $new) = @_;
146 24 100       56 return $self->parse($new) if (my $tree = $self->tree)->[0] eq 'root';
147 16         46 return $self->_replace(_parent($tree), $tree, _nodes($self->_parse($new)));
148             }
149              
150             sub root {
151 12     12 1 29 my $self = shift;
152 12 100       33 return $self unless my $tree = _ancestors($self->tree, 1);
153 9         29 return $self->_build($tree, $self->xml);
154             }
155              
156             sub selector {
157 13 100   13 1 33 return undef unless (my $tree = shift->tree)->[0] eq 'tag';
158             return join ' > ',
159 11         24 reverse map { $_->[1] . ':nth-child(' . (@{_siblings($_, 1)} + 1) . ')' }
  31         34  
  31         43  
160             $tree, _ancestors($tree);
161             }
162              
163             sub strip {
164 9     9 1 18 my $self = shift;
165 9 100       22 return $self if (my $tree = $self->tree)->[0] ne 'tag';
166 7         22 return $self->_replace($tree->[3], $tree, _nodes($tree));
167             }
168              
169             sub tag {
170 96     96 1 260 my ($self, $tag) = @_;
171 96 100       275 return undef if (my $tree = $self->tree)->[0] ne 'tag';
172 94 100       579 return $tree->[1] unless $tag;
173 1         3 $tree->[1] = $tag;
174 1         4 return $self;
175             }
176              
177 1     1 1 5 sub tap { Mojo::DOM58::_Collection::tap(@_) }
178              
179 757     757 1 2334 sub text { _text(_nodes(shift->tree), 0, 0) }
180              
181 152     152 1 351 sub to_string { ${shift()}->render }
  152         627  
182              
183 4707 100 50 4707 1 10091 sub tree { @_ > 1 ? (${$_[0]}->tree($_[1]) and return $_[0]) : ${$_[0]}->tree }
  2714         9530  
184              
185 78     78 1 184 sub type { shift->tree->[0] }
186              
187             sub val {
188 27     27 1 54 my $self = shift;
189              
190             # "option"
191 27 100       76 return defined($self->{value}) ? $self->{value} : $self->text
    100          
192             if (my $tag = $self->tag) eq 'option';
193              
194             # "input" ("type=checkbox" and "type=radio")
195 17   100     60 my $type = $self->{type} || '';
196 17 100 100     71 return defined $self->{value} ? $self->{value} : 'on'
    100 100        
197             if $tag eq 'input' && ($type eq 'radio' || $type eq 'checkbox');
198              
199             # "textarea", "input" or "button"
200 12 100       47 return $tag eq 'textarea' ? $self->text : $self->{value} if $tag ne 'select';
    100          
201              
202             # "select"
203             my $v = $self->find('option:checked:not([disabled])')
204 5     6   20 ->grep(sub { !$_->ancestors('optgroup[disabled]')->size })->map('val');
  6         26  
205 5 100       91 return exists $self->{multiple} ? $v->size ? $v->to_array : undef : $v->last;
    100          
206             }
207              
208 1     1 1 1805 sub with_roles { Mojo::DOM58::_Collection::with_roles(@_) }
209              
210 9     9 1 36 sub wrap { shift->_wrap(0, @_) }
211 7     7 1 24 sub wrap_content { shift->_wrap(1, @_) }
212              
213 3352 100 50 3352 1 6709 sub xml { @_ > 1 ? (${$_[0]}->xml($_[1]) and return $_[0]) : ${$_[0]}->xml }
  1340         5077  
214              
215             sub _add {
216 20     20   49 my ($self, $offset, $new) = @_;
217              
218 20 100       53 return $self if (my $tree = $self->tree)->[0] eq 'root';
219              
220 16         43 my $parent = _parent($tree);
221             splice @$parent, _offset($parent, $tree) + $offset, 0,
222 16         49 @{_link($parent, _nodes($self->_parse($new)))};
  16         52  
223              
224 16         77 return $self;
225             }
226              
227             sub _all {
228 21     21   43 my $nodes = shift;
229 21 100       46 @$nodes = map { $_->[0] eq 'tag' ? ($_, @{_all(_nodes($_))}) : ($_) } @$nodes;
  60         195  
  8         18  
230 21         63 return $nodes;
231             }
232              
233             sub _ancestors {
234 54     54   125 my ($tree, $root) = @_;
235              
236 54 100       129 return () unless $tree = _parent($tree);
237 51         94 my @ancestors;
238 51   66     64 do { push @ancestors, $tree }
  137         554  
239             while ($tree->[0] eq 'tag') && ($tree = $tree->[3]);
240 51 100       257 return $root ? $ancestors[-1] : @ancestors[0 .. $#ancestors - 1];
241             }
242              
243 1993     1993   5226 sub _build { shift->new->tree(shift)->xml(shift) }
244              
245             sub _collect {
246 554   50 554   1790 my ($self, $nodes) = (shift, shift || []);
247 554         1312 my $xml = $self->xml;
248 554         1506 return Mojo::DOM58::_Collection->new(map { $self->_build($_, $xml) } @$nodes);
  1325         2811  
249             }
250              
251             sub _content {
252 36     36   82 my ($self, $start, $offset, $new) = @_;
253              
254 36         70 my $tree = $self->tree;
255 36 100 100     139 unless ($tree->[0] eq 'root' || $tree->[0] eq 'tag') {
256 2         7 my $old = $self->content;
257 2 100       12 return $self->content($start ? $old . $new : $new . $old);
258             }
259              
260 34 100       94 $start = $start ? ($#$tree + 1) : _start($tree);
261 34 100       67 $offset = $offset ? $#$tree : 0;
262 34         48 splice @$tree, $start, $offset, @{_link($tree, _nodes($self->_parse($new)))};
  34         86  
263              
264 34         149 return $self;
265             }
266              
267 1118     1118   3421 sub _css { Mojo::DOM58::_CSS->new(tree => shift->tree) }
268              
269 1     1   6 sub _fragment { _link(my $r = ['root', @_], [@_]); $r }
  1         4  
270              
271             sub _link {
272 98     98   154 my ($parent, $children) = @_;
273              
274             # Link parent to children
275 98         161 for my $node (@$children) {
276 102 100       211 my $offset = $node->[0] eq 'tag' ? 3 : 2;
277 102         184 $node->[$offset] = $parent;
278 102         197 weaken $node->[$offset];
279             }
280              
281 98         297 return $children;
282             }
283              
284 30 100   30   122 sub _maybe { $_[1] ? $_[0]->_build($_[1], $_[0]->xml) : undef }
285              
286             sub _nodes {
287 1232 50   1232   2769 return () unless my $tree = shift;
288 1232         3565 my @nodes = @$tree[_start($tree) .. $#$tree];
289 1232 100       4204 return shift() ? [grep { $_->[0] eq 'tag' } @nodes] : \@nodes;
  84         823  
290             }
291              
292             sub _offset {
293 46     46   87 my ($parent, $child) = @_;
294 46         96 my $i = _start($parent);
295 46 100       307 $_ eq $child ? last : $i++ for @$parent[$i .. $#$parent];
296 46         84 return $i;
297             }
298              
299 223 100   223   766 sub _parent { $_[0]->[$_[0][0] eq 'tag' ? 3 : 2] }
300              
301             sub _parse {
302 80     80   145 my ($self, $input) = @_;
303 80 100 66     312 return Mojo::DOM58::_HTML->new(xml => $self->xml)->parse($input)->tree
304             unless blessed $input && $input->isa('Mojo::DOM58');
305 21         36 my $tree = dclone $input->tree;
306 21 100       91 return $tree->[0] eq 'root' ? $tree : _fragment($tree);
307             }
308              
309             sub _replace {
310 30     30   67 my ($self, $parent, $child, $nodes) = @_;
311 30         75 splice @$parent, _offset($parent, $child), 1, @{_link($parent, $nodes)};
  30         80  
312 30         90 return $self->parent;
313             }
314              
315 41 100   41   282 sub _select { $_[1] ? $_[0]->grep(matches => $_[1]) : $_[0] }
316              
317             sub _siblings {
318 88     88   194 my ($tree, $tags, $tail, $i) = @_;
319              
320 88 100       235 return defined $i ? undef : [] if $tree->[0] eq 'root';
    100          
321              
322 82         151 my $nodes = _nodes(_parent($tree));
323 82         118 my $match = -1;
324 82   66     698 defined($match++) and $_ eq $tree and last for @$nodes;
      100        
325              
326 82 100       194 if ($tail) { splice @$nodes, 0, $match + 1 }
  30         167  
327 52         107 else { splice @$nodes, $match, ($#$nodes + 1) - $match }
328              
329 82 100       297 @$nodes = grep { $_->[0] eq 'tag' } @$nodes if $tags;
  171         333  
330              
331 82 100 100     375 return defined $i ? $i == -1 && !@$nodes ? undef : $nodes->[$i] : $nodes;
    100          
332             }
333              
334 1305 100   1305   5560 sub _start { $_[0][0] eq 'root' ? 1 : 4 }
335              
336             sub _text {
337 785     785   2645 my ($nodes, $xml, $all) = @_;
338              
339 785         1982 my $text = '';
340 785         2032 while (my $node = shift @$nodes) {
341 1140         2232 my $type = $node->[0];
342              
343             # Text
344 1140 100 100     4900 if ($type eq 'text' || $type eq 'cdata' || $type eq 'raw') {
    100 100        
      100        
345 926         2845 $text .= $node->[1];
346             }
347              
348             # Nested tag
349             elsif ($type eq 'tag' && $all) {
350 155 100 100     778 unshift @$nodes, @{_nodes($node)} if $xml || ($node->[1] ne 'script' && $node->[1] ne 'style');
  143   100     264  
351             }
352             }
353              
354 785         4052 return $text;
355             }
356              
357             sub _wrap {
358 16     16   106 my ($self, $content, $new) = @_;
359              
360 16 100 100     31 return $self if (my $tree = $self->tree)->[0] eq 'root' && !$content;
361 15 100 100     88 return $self if $tree->[0] ne 'root' && $tree->[0] ne 'tag' && $content;
      100        
362              
363             # Find innermost tag
364 14         16 my $current;
365 14         34 my $first = $new = $self->_parse($new);
366 14         45 $current = $first while $first = _nodes($first, 1)->[0];
367 14 100       36 return $self unless $current;
368              
369             # Wrap content
370 12 100       28 if ($content) {
371 5         9 push @$current, @{_link($current, _nodes($tree))};
  5         8  
372 5         11 splice @$tree, _start($tree), $#$tree, @{_link($tree, _nodes($new))};
  5         13  
373 5         21 return $self;
374             }
375              
376             # Wrap element
377 7         15 $self->_replace(_parent($tree), $tree, _nodes($new));
378 7         19 push @$current, @{_link($current, [$tree])};
  7         16  
379 7         32 return $self;
380             }
381              
382             1;
383              
384             =encoding utf8
385              
386             =head1 NAME
387              
388             Mojo::DOM58 - Minimalistic HTML/XML DOM parser with CSS selectors
389              
390             =head1 SYNOPSIS
391              
392             use Mojo::DOM58;
393              
394             # Parse
395             my $dom = Mojo::DOM58->new('

Test

123

');
396              
397             # Find
398             say $dom->at('#b')->text;
399             say $dom->find('p')->map('text')->join("\n");
400             say $dom->find('[id]')->map(attr => 'id')->join("\n");
401              
402             # Iterate
403             $dom->find('p[id]')->reverse->each(sub { say $_->{id} });
404              
405             # Loop
406             for my $e ($dom->find('p[id]')->each) {
407             say $e->{id}, ':', $e->text;
408             }
409              
410             # Modify
411             $dom->find('div p')->last->append('

456

');
412             $dom->at('#c')->prepend($dom->new_tag('p', id => 'd', '789'));
413             $dom->find(':not(p)')->map('strip');
414              
415             # Render
416             say "$dom";
417              
418             =head1 DESCRIPTION
419              
420             L is a minimalistic and relaxed pure-perl HTML/XML DOM parser based
421             on L. It supports the L
422             and L, and
423             matching based on L. It will
424             even try to interpret broken HTML and XML, so you should not use it for
425             validation.
426              
427             =head1 FORK INFO
428              
429             L is a fork of L and tracks features and fixes to stay
430             closely compatible with upstream. It differs only in the standalone format and
431             compatibility with Perl 5.8. Any bugs or patches not related to these changes
432             should be reported directly to the L issue tracker.
433              
434             This release of L is up to date with version C<9.40> of
435             L.
436              
437             =head1 NODES AND ELEMENTS
438              
439             When we parse an HTML/XML fragment, it gets turned into a tree of nodes.
440              
441            
442            
443             Hello
444             World!
445            
446              
447             There are currently eight different kinds of nodes, C, C,
448             C, C, C, C, C and C. Elements are nodes of
449             the type C.
450              
451             root
452             |- doctype (html)
453             +- tag (html)
454             |- tag (head)
455             | +- tag (title)
456             | +- raw (Hello)
457             +- tag (body)
458             +- text (World!)
459              
460             While all node types are represented as L objects, some methods like
461             L and L only apply to elements.
462              
463             =head1 HTML AND XML
464              
465             L defaults to HTML semantics, that means all tags and attribute
466             names are lowercased and selectors need to be lowercase as well.
467              
468             # HTML semantics
469             my $dom = Mojo::DOM58->new('

Hi!

');
470             say $dom->at('p[id]')->text;
471              
472             If an XML declaration is found, the parser will automatically switch into XML
473             mode and everything becomes case-sensitive.
474              
475             # XML semantics
476             my $dom = Mojo::DOM58->new('

Hi!

');
477             say $dom->at('P[ID]')->text;
478              
479             HTML or XML semantics can also be forced with the L method.
480              
481             # Force HTML semantics
482             my $dom = Mojo::DOM58->new->xml(0)->parse('

Hi!

');
483             say $dom->at('p[id]')->text;
484              
485             # Force XML semantics
486             my $dom = Mojo::DOM58->new->xml(1)->parse('

Hi!

');
487             say $dom->at('P[ID]')->text;
488              
489             =head1 SELECTORS
490              
491             L uses a CSS selector engine based on L. All CSS
492             selectors that make sense for a standalone parser are supported.
493              
494             =over
495              
496             =item Z<>*
497              
498             Any element.
499              
500             my $all = $dom->find('*');
501              
502             =item E
503              
504             An element of type C.
505              
506             my $title = $dom->at('title');
507              
508             =item E[foo]
509              
510             An C element with a C attribute.
511              
512             my $links = $dom->find('a[href]');
513              
514             =item E[foo="bar"]
515              
516             An C element whose C attribute value is exactly equal to C.
517              
518             my $case_sensitive = $dom->find('input[type="hidden"]');
519             my $case_sensitive = $dom->find('input[type=hidden]');
520              
521             =item E[foo="bar" i]
522              
523             An C element whose C attribute value is exactly equal to any
524             (ASCII-range) case-permutation of C. Note that this selector is
525             B and might change without warning!
526              
527             my $case_insensitive = $dom->find('input[type="hidden" i]');
528             my $case_insensitive = $dom->find('input[type=hidden i]');
529             my $case_insensitive = $dom->find('input[class~="foo" i]');
530              
531             This selector is part of
532             L, which is still a work
533             in progress.
534              
535             =item E[foo="bar" s]
536              
537             An C element whose C attribute value is exactly and case-sensitively
538             equal to C. Note that this selector is B and might change
539             without warning!
540              
541             my $case_sensitive = $dom->find('input[type="hidden" s]');
542              
543             This selector is part of
544             L, which is still a work
545             in progress.
546              
547             =item E[foo~="bar"]
548              
549             An C element whose C attribute value is a list of whitespace-separated
550             values, one of which is exactly equal to C.
551              
552             my $foo = $dom->find('input[class~="foo"]');
553             my $foo = $dom->find('input[class~=foo]');
554              
555             =item E[foo^="bar"]
556              
557             An C element whose C attribute value begins exactly with the string
558             C.
559              
560             my $begins_with = $dom->find('input[name^="f"]');
561             my $begins_with = $dom->find('input[name^=f]');
562              
563             =item E[foo$="bar"]
564              
565             An C element whose C attribute value ends exactly with the string
566             C.
567              
568             my $ends_with = $dom->find('input[name$="o"]');
569             my $ends_with = $dom->find('input[name$=o]');
570              
571             =item E[foo*="bar"]
572              
573             An C element whose C attribute value contains the substring C.
574              
575             my $contains = $dom->find('input[name*="fo"]');
576             my $contains = $dom->find('input[name*=fo]');
577              
578             =item E[foo|="en"]
579              
580             An C element whose C attribute has a hyphen-separated list of values
581             beginning (from the left) with C.
582              
583             my $english = $dom->find('link[hreflang|=en]');
584              
585             =item E:root
586              
587             An C element, root of the document.
588              
589             my $root = $dom->at(':root');
590              
591             =item E:nth-child(n)
592              
593             An C element, the C child of its parent.
594              
595             my $third = $dom->find('div:nth-child(3)');
596             my $odd = $dom->find('div:nth-child(odd)');
597             my $even = $dom->find('div:nth-child(even)');
598             my $top3 = $dom->find('div:nth-child(-n+3)');
599              
600             =item E:nth-last-child(n)
601              
602             An C element, the C child of its parent, counting from the last one.
603              
604             my $third = $dom->find('div:nth-last-child(3)');
605             my $odd = $dom->find('div:nth-last-child(odd)');
606             my $even = $dom->find('div:nth-last-child(even)');
607             my $bottom3 = $dom->find('div:nth-last-child(-n+3)');
608              
609             =item E:nth-of-type(n)
610              
611             An C element, the C sibling of its type.
612              
613             my $third = $dom->find('div:nth-of-type(3)');
614             my $odd = $dom->find('div:nth-of-type(odd)');
615             my $even = $dom->find('div:nth-of-type(even)');
616             my $top3 = $dom->find('div:nth-of-type(-n+3)');
617              
618             =item E:nth-last-of-type(n)
619              
620             An C element, the C sibling of its type, counting from the last one.
621              
622             my $third = $dom->find('div:nth-last-of-type(3)');
623             my $odd = $dom->find('div:nth-last-of-type(odd)');
624             my $even = $dom->find('div:nth-last-of-type(even)');
625             my $bottom3 = $dom->find('div:nth-last-of-type(-n+3)');
626              
627             =item E:first-child
628              
629             An C element, first child of its parent.
630              
631             my $first = $dom->find('div p:first-child');
632              
633             =item E:last-child
634              
635             An C element, last child of its parent.
636              
637             my $last = $dom->find('div p:last-child');
638              
639             =item E:first-of-type
640              
641             An C element, first sibling of its type.
642              
643             my $first = $dom->find('div p:first-of-type');
644              
645             =item E:last-of-type
646              
647             An C element, last sibling of its type.
648              
649             my $last = $dom->find('div p:last-of-type');
650              
651             =item E:only-child
652              
653             An C element, only child of its parent.
654              
655             my $lonely = $dom->find('div p:only-child');
656              
657             =item E:only-of-type
658              
659             An C element, only sibling of its type.
660              
661             my $lonely = $dom->find('div p:only-of-type');
662              
663             =item E:empty
664              
665             An C element that has no children (including text nodes).
666              
667             my $empty = $dom->find(':empty');
668              
669             =item E:any-link
670              
671             Alias for L. Note that this selector is B and might
672             change without warning! This selector is part of
673             L, which is still a
674             work in progress.
675              
676             =item E:link
677              
678             An C element being the source anchor of a hyperlink of which the target is
679             not yet visited (C<:link>) or already visited (C<:visited>). Note that
680             L is not stateful, therefore C<:any-link>, C<:link> and
681             C<:visited> yield exactly the same results.
682              
683             my $links = $dom->find(':any-link');
684             my $links = $dom->find(':link');
685             my $links = $dom->find(':visited');
686              
687             =item E:visited
688              
689             Alias for L.
690              
691             =item E:scope
692              
693             An C element being a designated reference element. Note that this selector is B and might change
694             without warning!
695              
696             my $scoped = $dom->find('a:not(:scope > a)');
697             my $scoped = $dom->find('div :scope p');
698             my $scoped = $dom->find('~ p');
699              
700             This selector is part of L, which is still a work in progress.
701              
702             =item E:checked
703              
704             A user interface element C which is checked (for instance a radio-button or
705             checkbox).
706              
707             my $input = $dom->find(':checked');
708              
709             =item E.warning
710              
711             An C element whose class is "warning".
712              
713             my $warning = $dom->find('div.warning');
714              
715             =item E#myid
716              
717             An C element with C equal to "myid".
718              
719             my $foo = $dom->at('div#foo');
720              
721             =item E:not(s1, s2)
722              
723             An C element that does not match either compound selector C or compound
724             selector C. Note that support for compound selectors is B and
725             might change without warning!
726              
727             my $others = $dom->find('div p:not(:first-child, :last-child)');
728              
729             Support for compound selectors was added as part of
730             L, which is still a work
731             in progress.
732              
733             =item E:is(s1, s2)
734              
735             An C element that matches compound selector C and/or compound selector
736             C. Note that this selector is B and might change without warning!
737              
738             my $headers = $dom->find(':is(section, article, aside, nav) h1');
739              
740             This selector is part of
741             L, which is still a work
742             in progress.
743              
744             =item E:has(rs1, rs2)
745              
746             An C element, if either of the relative selectors C or C, when evaluated with C as the :scope elements,
747             match an element. Note that this selector is B and might change without warning!
748              
749             my $link = $dom->find('a:has(> img)');
750              
751             This selector is part of L, which is still a work in progress.
752             Also be aware that this feature is currently marked C, so there is a high chance that it will get removed
753             completely.
754              
755             =item E:text(string_or_regex)
756              
757             An C element containing text content that substring matches C case-insensitively or that regex
758             matches C. For regular expressions use the format C<:text(/.../)>. Note that this selector is
759             B and might change without warning!
760              
761             # Substring match
762             my $login = $dom->find(':text(Log in)');
763              
764             # Regex match
765             my $login = $dom->find(':text(/Log ?in/)');
766              
767             # Regex match (case-insensitive)
768             my $login = $dom->find(':text(/(?i:Log ?in)/)');
769              
770             This is a custom selector for L and not part of any spec.
771              
772             =item A|E
773              
774             An C element that belongs to the namespace alias C from
775             L.
776             Key/value pairs passed to selector methods are used to declare namespace
777             aliases.
778              
779             my $elem = $dom->find('lq|elem', lq => 'http://example.com/q-markup');
780              
781             Using an empty alias searches for an element that belongs to no namespace.
782              
783             my $div = $dom->find('|div');
784              
785             =item E F
786              
787             An C element descendant of an C element.
788              
789             my $headlines = $dom->find('div h1');
790              
791             =item E E F
792              
793             An C element child of an C element.
794              
795             my $headlines = $dom->find('html > body > div > h1');
796              
797             =item E + F
798              
799             An C element immediately preceded by an C element.
800              
801             my $second = $dom->find('h1 + h2');
802              
803             =item E ~ F
804              
805             An C element preceded by an C element.
806              
807             my $second = $dom->find('h1 ~ h2');
808              
809             =item E, F, G
810              
811             Elements of type C, C and C.
812              
813             my $headlines = $dom->find('h1, h2, h3');
814              
815             =item E[foo=bar][bar=baz]
816              
817             An C element whose attributes match all following attribute selectors.
818              
819             my $links = $dom->find('a[foo^=b][foo$=ar]');
820              
821             =back
822              
823             =head1 OPERATORS
824              
825             L overloads the following operators.
826              
827             =head2 array
828              
829             my @nodes = @$dom;
830              
831             Alias for L.
832              
833             # ""
834             $dom->parse('123')->[0];
835              
836             =head2 bool
837              
838             my $bool = !!$dom;
839              
840             Always true.
841              
842             =head2 hash
843              
844             my %attrs = %$dom;
845              
846             Alias for L.
847              
848             # "test"
849             $dom->parse('
Test
')->at('div')->{id};
850              
851             =head2 stringify
852              
853             my $str = "$dom";
854              
855             Alias for L.
856              
857             =head1 FUNCTIONS
858              
859             L implements the following functions, which can be imported
860             individually.
861              
862             =head2 tag_to_html
863              
864             my $str = tag_to_html 'div', id => 'foo', 'safe content';
865              
866             Generate HTML/XML tag and render it right away. This is a significantly faster
867             alternative to L for template systems that have to generate a lot
868             of tags.
869              
870             =head1 METHODS
871              
872             L implements the following methods.
873              
874             =head2 new
875              
876             my $dom = Mojo::DOM58->new;
877             my $dom = Mojo::DOM58->new('I ♥ Mojo::DOM58!');
878              
879             Construct a new scalar-based L object and L HTML/XML
880             fragment if necessary.
881              
882             =head2 new_tag
883              
884             my $tag = Mojo::DOM58->new_tag('div');
885             my $tag = $dom->new_tag('div');
886             my $tag = $dom->new_tag('div', id => 'foo', hidden => undef);
887             my $tag = $dom->new_tag('div', 'safe content');
888             my $tag = $dom->new_tag('div', id => 'foo', 'safe content');
889             my $tag = $dom->new_tag('div', data => {mojo => 'rocks'}, 'safe content');
890             my $tag = $dom->new_tag('div', id => 'foo', sub { 'unsafe content' });
891              
892             Construct a new L object for an HTML/XML tag with or without
893             attributes and content. The C attribute may contain a hash reference with
894             key/value pairs to generate attributes from.
895              
896             # "
"
897             $dom->new_tag('br');
898              
899             # "
"
900             $dom->new_tag('div');
901              
902             # ""
903             $dom->new_tag('div', id => 'foo', hidden => undef);
904              
905             # "
test & 123
"
906             $dom->new_tag('div', 'test & 123');
907              
908             # "
test & 123
"
909             $dom->new_tag('div', id => 'foo', 'test & 123');
910              
911             # "
test & 123
""
912             $dom->new_tag('div', data => {foo => 1, Bar => 'test'}, 'test & 123');
913              
914             # "
test & 123
"
915             $dom->new_tag('div', id => 'foo', sub { 'test & 123' });
916              
917             # "
HelloMojo!
"
918             $dom->parse('
Hello
')->at('div')
919             ->append_content($dom->new_tag('b', 'Mojo!'))->root;
920              
921             =head2 all_text
922              
923             my $text = $dom->all_text;
924              
925             Extract text content from all descendant nodes of this element. For HTML documents C