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   142808 use strict;
  2         14  
  2         59  
4 2     2   10 use warnings;
  2         4  
  2         227  
5              
6             use overload
7 4     4   13 '@{}' => sub { shift->child_nodes },
8 95     95   251 '%{}' => sub { shift->attr },
9 32     32   3373 bool => sub {1},
10 129     129   15132 '""' => sub { shift->to_string },
11 2     2   2748 fallback => 1;
  2         2076  
  2         22  
12              
13 2     2   212 use Exporter 'import';
  2         4  
  2         56  
14 2     2   445 use Mojo::DOM58::_Collection;
  2         5  
  2         50  
15 2     2   952 use Mojo::DOM58::_CSS;
  2         6  
  2         76  
16 2     2   954 use Mojo::DOM58::_HTML 'tag_to_html';
  2         9  
  2         228  
17 2     2   17 use Scalar::Util qw(blessed weaken);
  2         4  
  2         107  
18 2     2   1663 use Storable 'dclone';
  2         6686  
  2         8077  
19              
20             our $VERSION = '3.001';
21              
22             our @EXPORT_OK = 'tag_to_html';
23              
24             sub new {
25 2128     2128 1 325302 my $class = shift;
26 2128   66     5414 my $self = bless \Mojo::DOM58::_HTML->new, ref $class || $class;
27 2128 100       5992 return @_ ? $self->parse(@_) : $self;
28             }
29              
30             sub new_tag {
31 11     11 1 2547 my $self = shift;
32 11         27 my $new = $self->new;
33 11         43 $$new->tag(@_);
34 11 100       30 $$new->xml($$self->xml) if ref $self;
35 11         43 return $new;
36             }
37              
38 1     1 0 90 sub TO_JSON { ${shift()}->render }
  1         5  
39              
40 25     25 1 75 sub all_text { _text(_nodes($_[0]->tree), $_[0]->xml, 1) }
41              
42 15     15 1 49 sub ancestors { _select($_[0]->_collect([_ancestors($_[0]->tree)]), $_[1]) }
43              
44 9     9 1 36 sub append { shift->_add(1, @_) }
45 13     13 1 98 sub append_content { shift->_content(1, 0, @_) }
46              
47             sub at {
48 612     612 1 2042 my $self = shift;
49 612 100       1459 return undef unless my $result = $self->_css->select_one(@_);
50 564         3165 return $self->_build($result, $self->xml);
51             }
52              
53             sub attr {
54 168     168 1 328 my $self = shift;
55              
56             # Hash
57 168         298 my $tree = $self->tree;
58 168 100       431 my $attrs = $tree->[0] ne 'tag' ? {} : $tree->[2];
59 168 100       870 return $attrs unless @_;
60              
61             # Get
62 40 100 100     285 return $attrs->{$_[0]} unless @_ > 1 || ref $_[0];
63              
64             # Set
65 4 100       15 my $values = ref $_[0] ? $_[0] : {@_};
66 4         17 @$attrs{keys %$values} = values %$values;
67              
68 4         19 return $self;
69             }
70              
71 59     59 1 169 sub child_nodes { $_[0]->_collect(_nodes($_[0]->tree)) }
72              
73 13     13 1 122 sub children { _select($_[0]->_collect(_nodes($_[0]->tree, 1)), $_[1]) }
74              
75             sub content {
76 55     55 1 106 my $self = shift;
77              
78 55         119 my $type = $self->type;
79 55 100 100     223 if ($type eq 'root' || $type eq 'tag') {
80 24 100       79 return $self->_content(0, 1, @_) if @_;
81 7         19 my $html = Mojo::DOM58::_HTML->new(xml => $self->xml);
82 7         13 return join '', map { $html->tree($_)->render } @{_nodes($self->tree)};
  12         29  
  7         14  
83             }
84              
85 31 100       73 return $self->tree->[1] unless @_;
86 3         7 $self->tree->[1] = shift;
87 3         10 return $self;
88             }
89              
90 13     13 1 34 sub descendant_nodes { $_[0]->_collect(_all(_nodes($_[0]->tree))) }
91              
92             sub find {
93 427     427 1 2661 my $self = shift;
94 427         1084 return $self->_collect($self->_css->select(@_));
95             }
96              
97 8     8 1 21 sub following { _select($_[0]->_collect(_siblings($_[0]->tree, 1, 1)), $_[1]) }
98 7     7 1 20 sub following_nodes { $_[0]->_collect(_siblings($_[0]->tree, 0, 1)) }
99              
100 44     44 1 109 sub matches { shift->_css->matches(@_) }
101              
102             sub namespace {
103 18     18 1 34 my $self = shift;
104              
105 18 100       38 return undef if (my $tree = $self->tree)->[0] ne 'tag';
106              
107             # Extract namespace prefix and search parents
108 16 100       172 my $ns = $tree->[1] =~ /^(.*?):/ ? "xmlns:$1" : undef;
109 16         41 for my $node ($tree, _ancestors($tree)) {
110              
111             # Namespace for prefix
112 35         53 my $attrs = $node->[2];
113 35 100 100     82 if ($ns) { $_ eq $ns and return $attrs->{$_} for keys %$attrs }
  13 100       71  
114              
115             # Namespace attribute
116 10         55 elsif (defined $attrs->{xmlns}) { return $attrs->{xmlns} }
117             }
118              
119 1         6 return undef;
120             }
121              
122 13     13 1 30 sub next { $_[0]->_maybe(_siblings($_[0]->tree, 1, 1, 0)) }
123 5     5 1 14 sub next_node { $_[0]->_maybe(_siblings($_[0]->tree, 0, 1, 0)) }
124              
125             sub parent {
126 48     48 1 93 my $self = shift;
127 48 50       82 return undef if (my $tree = $self->tree)->[0] eq 'root';
128 48         109 return $self->_build(_parent($tree), $self->xml);
129             }
130              
131 166 50   166 1 285 sub parse { ${$_[0]}->parse($_[1]) and return $_[0] }
  166         703  
132              
133 5     5 1 13 sub preceding { _select($_[0]->_collect(_siblings($_[0]->tree, 1, 0)), $_[1]) }
134 7     7 1 18 sub preceding_nodes { $_[0]->_collect(_siblings($_[0]->tree, 0)) }
135              
136 11     11 1 31 sub prepend { shift->_add(0, @_) }
137 6     6 1 24 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 11 sub previous_node { $_[0]->_maybe(_siblings($_[0]->tree, 0, 0, -1)) }
141              
142 6     6 1 17 sub remove { shift->replace('') }
143              
144             sub replace {
145 24     24 1 65 my ($self, $new) = @_;
146 24 100       49 return $self->parse($new) if (my $tree = $self->tree)->[0] eq 'root';
147 16         40 return $self->_replace(_parent($tree), $tree, _nodes($self->_parse($new)));
148             }
149              
150             sub root {
151 12     12 1 26 my $self = shift;
152 12 100       32 return $self unless my $tree = _ancestors($self->tree, 1);
153 9         25 return $self->_build($tree, $self->xml);
154             }
155              
156             sub selector {
157 13 100   13 1 30 return undef unless (my $tree = shift->tree)->[0] eq 'tag';
158             return join ' > ',
159 11         43 reverse map { $_->[1] . ':nth-child(' . (@{_siblings($_, 1)} + 1) . ')' }
  31         69  
  31         52  
160             $tree, _ancestors($tree);
161             }
162              
163             sub strip {
164 9     9 1 15 my $self = shift;
165 9 100       22 return $self if (my $tree = $self->tree)->[0] ne 'tag';
166 7         21 return $self->_replace($tree->[3], $tree, _nodes($tree));
167             }
168              
169             sub tag {
170 96     96 1 270 my ($self, $tag) = @_;
171 96 100       176 return undef if (my $tree = $self->tree)->[0] ne 'tag';
172 94 100       440 return $tree->[1] unless $tag;
173 1         4 $tree->[1] = $tag;
174 1         3 return $self;
175             }
176              
177 1     1 1 6 sub tap { Mojo::DOM58::_Collection::tap(@_) }
178              
179 722     722 1 1822 sub text { _text(_nodes(shift->tree), 0, 0) }
180              
181 139     139 1 217 sub to_string { ${shift()}->render }
  139         459  
182              
183 4596 100 50 4596 1 9504 sub tree { @_ > 1 ? (${$_[0]}->tree($_[1]) and return $_[0]) : ${$_[0]}->tree }
  2641         7856  
184              
185 78     78 1 150 sub type { shift->tree->[0] }
186              
187             sub val {
188 27     27 1 45 my $self = shift;
189              
190             # "option"
191 27 100       60 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     48 my $type = $self->{type} || '';
196 17 100 100     70 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       38 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   12 ->grep(sub { !$_->ancestors('optgroup[disabled]')->size })->map('val');
  6         17  
205 5 100       31 return exists $self->{multiple} ? $v->size ? $v->to_array : undef : $v->last;
    100          
206             }
207              
208 1     1 1 1429 sub with_roles { Mojo::DOM58::_Collection::with_roles(@_) }
209              
210 9     9 1 36 sub wrap { shift->_wrap(0, @_) }
211 7     7 1 20 sub wrap_content { shift->_wrap(1, @_) }
212              
213 3275 100 50 3275 1 6099 sub xml { @_ > 1 ? (${$_[0]}->xml($_[1]) and return $_[0]) : ${$_[0]}->xml }
  1309         4272  
214              
215             sub _add {
216 20     20   44 my ($self, $offset, $new) = @_;
217              
218 20 100       47 return $self if (my $tree = $self->tree)->[0] eq 'root';
219              
220 16         38 my $parent = _parent($tree);
221             splice @$parent, _offset($parent, $tree) + $offset, 0,
222 16         46 @{_link($parent, _nodes($self->_parse($new)))};
  16         45  
223              
224 16         76 return $self;
225             }
226              
227             sub _all {
228 21     21   30 my $nodes = shift;
229 21 100       39 @$nodes = map { $_->[0] eq 'tag' ? ($_, @{_all(_nodes($_))}) : ($_) } @$nodes;
  60         131  
  8         16  
230 21         53 return $nodes;
231             }
232              
233             sub _ancestors {
234 54     54   124 my ($tree, $root) = @_;
235              
236 54 100       117 return () unless $tree = _parent($tree);
237 51         91 my @ancestors;
238 51   66     76 do { push @ancestors, $tree }
  137         450  
239             while ($tree->[0] eq 'tag') && ($tree = $tree->[3]);
240 51 100       237 return $root ? $ancestors[-1] : @ancestors[0 .. $#ancestors - 1];
241             }
242              
243 1955     1955   4096 sub _build { shift->new->tree(shift)->xml(shift) }
244              
245             sub _collect {
246 553   50 553   1529 my ($self, $nodes) = (shift, shift || []);
247 553         1281 my $xml = $self->xml;
248 553         1208 return Mojo::DOM58::_Collection->new(map { $self->_build($_, $xml) } @$nodes);
  1314         2279  
249             }
250              
251             sub _content {
252 36     36   86 my ($self, $start, $offset, $new) = @_;
253              
254 36         67 my $tree = $self->tree;
255 36 100 100     149 unless ($tree->[0] eq 'root' || $tree->[0] eq 'tag') {
256 2         6 my $old = $self->content;
257 2 100       11 return $self->content($start ? $old . $new : $new . $old);
258             }
259              
260 34 100       84 $start = $start ? ($#$tree + 1) : _start($tree);
261 34 100       76 $offset = $offset ? $#$tree : 0;
262 34         51 splice @$tree, $start, $offset, @{_link($tree, _nodes($self->_parse($new)))};
  34         79  
263              
264 34         153 return $self;
265             }
266              
267 1083     1083   2329 sub _css { Mojo::DOM58::_CSS->new(tree => shift->tree) }
268              
269 1     1   8 sub _fragment { _link(my $r = ['root', @_], [@_]); $r }
  1         5  
270              
271             sub _link {
272 98     98   176 my ($parent, $children) = @_;
273              
274             # Link parent to children
275 98         183 for my $node (@$children) {
276 102 100       234 my $offset = $node->[0] eq 'tag' ? 3 : 2;
277 102         187 $node->[$offset] = $parent;
278 102         301 weaken $node->[$offset];
279             }
280              
281 98         322 return $children;
282             }
283              
284 30 100   30   106 sub _maybe { $_[1] ? $_[0]->_build($_[1], $_[0]->xml) : undef }
285              
286             sub _nodes {
287 1191 50   1191   2612 return () unless my $tree = shift;
288 1191         2297 my @nodes = @$tree[_start($tree) .. $#$tree];
289 1191 100       3562 return shift() ? [grep { $_->[0] eq 'tag' } @nodes] : \@nodes;
  84         262  
290             }
291              
292             sub _offset {
293 46     46   136 my ($parent, $child) = @_;
294 46         92 my $i = _start($parent);
295 46 100       289 $_ eq $child ? last : $i++ for @$parent[$i .. $#$parent];
296 46         99 return $i;
297             }
298              
299 223 100   223   733 sub _parent { $_[0]->[$_[0][0] eq 'tag' ? 3 : 2] }
300              
301             sub _parse {
302 80     80   166 my ($self, $input) = @_;
303 80 100 66     420 return Mojo::DOM58::_HTML->new(xml => $self->xml)->parse($input)->tree
304             unless blessed $input && $input->isa('Mojo::DOM58');
305 21         51 my $tree = dclone $input->tree;
306 21 100       125 return $tree->[0] eq 'root' ? $tree : _fragment($tree);
307             }
308              
309             sub _replace {
310 30     30   71 my ($self, $parent, $child, $nodes) = @_;
311 30         88 splice @$parent, _offset($parent, $child), 1, @{_link($parent, $nodes)};
  30         66  
312 30         76 return $self->parent;
313             }
314              
315 41 100   41   219 sub _select { $_[1] ? $_[0]->grep(matches => $_[1]) : $_[0] }
316              
317             sub _siblings {
318 88     88   178 my ($tree, $tags, $tail, $i) = @_;
319              
320 88 100       211 return defined $i ? undef : [] if $tree->[0] eq 'root';
    100          
321              
322 82         147 my $nodes = _nodes(_parent($tree));
323 82         124 my $match = -1;
324 82   66     652 defined($match++) and $_ eq $tree and last for @$nodes;
      100        
325              
326 82 100       152 if ($tail) { splice @$nodes, 0, $match + 1 }
  30         64  
327 52         122 else { splice @$nodes, $match, ($#$nodes + 1) - $match }
328              
329 82 100       187 @$nodes = grep { $_->[0] eq 'tag' } @$nodes if $tags;
  171         354  
330              
331 82 100 100     376 return defined $i ? $i == -1 && !@$nodes ? undef : $nodes->[$i] : $nodes;
    100          
332             }
333              
334 1264 100   1264   4164 sub _start { $_[0][0] eq 'root' ? 1 : 4 }
335              
336             sub _text {
337 747     747   1364 my ($nodes, $xml, $all) = @_;
338              
339 747         1219 my $text = '';
340 747         1682 while (my $node = shift @$nodes) {
341 1076         1569 my $type = $node->[0];
342              
343             # Text
344 1076 100 100     3436 if ($type eq 'text' || $type eq 'cdata' || $type eq 'raw') {
    100 100        
      100        
345 867         2525 $text .= $node->[1];
346             }
347              
348             # Nested tag
349             elsif ($type eq 'tag' && $all) {
350 152 100 100     561 unshift @$nodes, @{_nodes($node)} if $xml || ($node->[1] ne 'script' && $node->[1] ne 'style');
  140   100     223  
351             }
352             }
353              
354 747         3489 return $text;
355             }
356              
357             sub _wrap {
358 16     16   38 my ($self, $content, $new) = @_;
359              
360 16 100 100     30 return $self if (my $tree = $self->tree)->[0] eq 'root' && !$content;
361 15 100 100     74 return $self if $tree->[0] ne 'root' && $tree->[0] ne 'tag' && $content;
      100        
362              
363             # Find innermost tag
364 14         26 my $current;
365 14         34 my $first = $new = $self->_parse($new);
366 14         48 $current = $first while $first = _nodes($first, 1)->[0];
367 14 100       51 return $self unless $current;
368              
369             # Wrap content
370 12 100       24 if ($content) {
371 5         11 push @$current, @{_link($current, _nodes($tree))};
  5         11  
372 5         18 splice @$tree, _start($tree), $#$tree, @{_link($tree, _nodes($new))};
  5         10  
373 5         23 return $self;
374             }
375              
376             # Wrap element
377 7         18 $self->_replace(_parent($tree), $tree, _nodes($new));
378 7         27 push @$current, @{_link($current, [$tree])};
  7         24  
379 7         33 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.0> 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 CASE-SENSITIVITY
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 A|E
756              
757             An C element that belongs to the namespace alias C from
758             L.
759             Key/value pairs passed to selector methods are used to declare namespace
760             aliases.
761              
762             my $elem = $dom->find('lq|elem', lq => 'http://example.com/q-markup');
763              
764             Using an empty alias searches for an element that belongs to no namespace.
765              
766             my $div = $dom->find('|div');
767              
768             =item E F
769              
770             An C element descendant of an C element.
771              
772             my $headlines = $dom->find('div h1');
773              
774             =item E E F
775              
776             An C element child of an C element.
777              
778             my $headlines = $dom->find('html > body > div > h1');
779              
780             =item E + F
781              
782             An C element immediately preceded by an C element.
783              
784             my $second = $dom->find('h1 + h2');
785              
786             =item E ~ F
787              
788             An C element preceded by an C element.
789              
790             my $second = $dom->find('h1 ~ h2');
791              
792             =item E, F, G
793              
794             Elements of type C, C and C.
795              
796             my $headlines = $dom->find('h1, h2, h3');
797              
798             =item E[foo=bar][bar=baz]
799              
800             An C element whose attributes match all following attribute selectors.
801              
802             my $links = $dom->find('a[foo^=b][foo$=ar]');
803              
804             =back
805              
806             =head1 OPERATORS
807              
808             L overloads the following operators.
809              
810             =head2 array
811              
812             my @nodes = @$dom;
813              
814             Alias for L.
815              
816             # ""
817             $dom->parse('123')->[0];
818              
819             =head2 bool
820              
821             my $bool = !!$dom;
822              
823             Always true.
824              
825             =head2 hash
826              
827             my %attrs = %$dom;
828              
829             Alias for L.
830              
831             # "test"
832             $dom->parse('
Test
')->at('div')->{id};
833              
834             =head2 stringify
835              
836             my $str = "$dom";
837              
838             Alias for L.
839              
840             =head1 FUNCTIONS
841              
842             L implements the following functions, which can be imported
843             individually.
844              
845             =head2 tag_to_html
846              
847             my $str = tag_to_html 'div', id => 'foo', 'safe content';
848              
849             Generate HTML/XML tag and render it right away. This is a significantly faster
850             alternative to L for template systems that have to generate a lot
851             of tags.
852              
853             =head1 METHODS
854              
855             L implements the following methods.
856              
857             =head2 new
858              
859             my $dom = Mojo::DOM58->new;
860             my $dom = Mojo::DOM58->new('I ♥ Mojo::DOM58!');
861              
862             Construct a new scalar-based L object and L HTML/XML
863             fragment if necessary.
864              
865             =head2 new_tag
866              
867             my $tag = Mojo::DOM58->new_tag('div');
868             my $tag = $dom->new_tag('div');
869             my $tag = $dom->new_tag('div', id => 'foo', hidden => undef);
870             my $tag = $dom->new_tag('div', 'safe content');
871             my $tag = $dom->new_tag('div', id => 'foo', 'safe content');
872             my $tag = $dom->new_tag('div', data => {mojo => 'rocks'}, 'safe content');
873             my $tag = $dom->new_tag('div', id => 'foo', sub { 'unsafe content' });
874              
875             Construct a new L object for an HTML/XML tag with or without
876             attributes and content. The C attribute may contain a hash reference with
877             key/value pairs to generate attributes from.
878              
879             # "
"
880             $dom->new_tag('br');
881              
882             # "
"
883             $dom->new_tag('div');
884              
885             # ""
886             $dom->new_tag('div', id => 'foo', hidden => undef);
887              
888             # "
test & 123
"
889             $dom->new_tag('div', 'test & 123');
890              
891             # "
test & 123
"
892             $dom->new_tag('div', id => 'foo', 'test & 123');
893              
894             # "
test & 123
""
895             $dom->new_tag('div', data => {foo => 1, Bar => 'test'}, 'test & 123');
896              
897             # "
test & 123
"
898             $dom->new_tag('div', id => 'foo', sub { 'test & 123' });
899              
900             # "
HelloMojo!
"
901             $dom->parse('
Hello
')->at('div')
902             ->append_content($dom->new_tag('b', 'Mojo!'))->root;
903              
904             =head2 all_text
905              
906             my $text = $dom->all_text;
907              
908             Extract text content from all descendant nodes of this element. For HTML documents C