File Coverage

blib/lib/Mojo/DOM/HTML.pm
Criterion Covered Total %
statement 130 132 98.4
branch 114 118 96.6
condition 48 52 92.3
subroutine 15 15 100.0
pod 4 4 100.0
total 311 321 96.8


line stmt bran cond sub pod time code
1             package Mojo::DOM::HTML;
2 65     65   342 use Mojo::Base -base;
  65         123  
  65         426  
3              
4 65     65   308 use Exporter qw(import);
  65         92  
  65         1974  
5 65     65   249 use Mojo::Util qw(html_attr_unescape html_unescape xml_escape);
  65         89  
  65         3195  
6 65     65   260 use Scalar::Util qw(weaken);
  65         140  
  65         124463  
7              
8             our @EXPORT_OK = ('tag_to_html');
9              
10             has tree => sub { ['root'] };
11             has 'xml';
12              
13             my $ATTR_RE = qr/
14             ([^<>=\s\/0-9.\-][^<>=\s\/]*|\/) # Key
15             (?:
16             \s*=\s*
17             (?s:(["'])(.*?)\g{-2}|([^>\s]*)) # Value
18             )?
19             \s*
20             /x;
21             my $TOKEN_RE = qr/
22             ([^<]+)? # Text
23             (?:
24             <(?:
25             !(?:
26             DOCTYPE(
27             \s+\w+ # Doctype
28             (?:(?:\s+\w+)?(?:\s+(?:"[^"]*"|'[^']*'))+)? # External ID
29             (?:\s+\[.+?\])? # Int Subset
30             \s*)
31             |
32             --(.*?)(?:--!?|(?<=' if $type eq 'comment';
257              
258             # CDATA
259 6 100       20 return '[1] . ']]>' if $type eq 'cdata';
260              
261             # Processing instruction
262 3 50       16 return '[1] . '?>' if $type eq 'pi';
263              
264             # Everything else
265 0         0 return '';
266             }
267              
268             sub _script_content {
269 38     38   44 my $html = shift;
270 38         60 my $start = pos $$html;
271              
272 38         47 my $state = 0;
273              
274 38         46 while (1) {
275 66 100       84 if ($state == 0) { $$html =~ /\G[^<]*/gcs }
  53         110  
276 13         19 else { $$html =~ /\G[^<\-]*/gcs }
277              
278 66         72 my $p = pos $$html;
279 66 100       252 return (substr($$html, $start), 0) if $p >= length $$html;
280              
281 65 100       90 if ($state == 0) {
    100          
282 52 100       190 if ($$html =~ m!\G)!gcsi) { return (substr($$html, $start, $p - $start), 1) }
  35 100       257  
283 6         8 elsif ($$html =~ /\G/gcs) { $state = 0 }
289 3         5 elsif ($$html =~ m!\G])!gcsi) { $state = 2 }
290 2         8 else { pos($$html) = $p + 1 }
291             }
292             else {
293 3 100       10 if ($$html =~ m!\G)!gcsi) { $state = 1 }
  2 50       6  
294 1         2 elsif ($$html =~ /\G-->/gcs) { $state = 0 }
295 0         0 else { pos($$html) = $p + 1 }
296             }
297             }
298             }
299              
300             sub _start {
301 2159     2159   2707 my ($start, $attrs, $xml, $current) = @_;
302              
303             # Autoclose optional HTML elements
304 2159 100 100     4964 if (!$xml && $$current->[0] ne 'root') {
305 1728 100       3435 if (my $end = $END{$start}) { _end($end, 0, $current) }
  566 100       811  
306              
307             elsif (my $close = $CLOSE{$start}) {
308 599         726 my ($allowed, $scope) = @$close;
309              
310             # Close allowed parent elements in scope
311 599         601 my $parent = $$current;
312 599   66     1641 while ($parent->[0] ne 'root' && !$scope->{$parent->[1]}) {
313 440 100       707 _end($parent->[1], 0, $current) if $allowed->{$parent->[1]};
314 440         1067 $parent = $parent->[3];
315             }
316             }
317             }
318              
319             # New tag
320 2159         4436 push @$$current, my $new = ['tag', $start, $attrs, $$current];
321 2159         2722 weaken $new->[3];
322 2159         2551 $$current = $new;
323             }
324              
325             sub _tag {
326 965     965   1824 my $tree = ['tag', shift, undef, undef];
327              
328             # Content
329 965 100       2737 push @$tree, ref $_[-1] eq 'CODE' ? ['raw', pop->()] : ['text', pop] if @_ % 2;
    100          
330              
331             # Attributes
332 965         2500 my $attrs = $tree->[2] = {@_};
333 965 100 100     2860 return $tree unless exists $attrs->{data} && ref $attrs->{data} eq 'HASH';
334 5         11 my $data = delete $attrs->{data};
335 5         16 @$attrs{map { y/_/-/; lc "data-$_" } keys %$data} = values %$data;
  9         16  
  9         32  
336 5         15 return $tree;
337             }
338              
339             1;
340              
341             =encoding utf8
342              
343             =head1 NAME
344              
345             Mojo::DOM::HTML - HTML/XML engine
346              
347             =head1 SYNOPSIS
348              
349             use Mojo::DOM::HTML;
350              
351             # Turn HTML into DOM tree
352             my $html = Mojo::DOM::HTML->new;
353             $html->parse('

Test

123

');
354             my $tree = $html->tree;
355              
356             =head1 DESCRIPTION
357              
358             L is the HTML/XML engine used by L, based on the L
359             Standard|https://html.spec.whatwg.org> and the L.
360              
361             =head1 FUNCTIONS
362              
363             L implements the following functions, which can be imported individually.
364              
365             =head2 tag_to_html
366              
367             my $str = tag_to_html 'div', id => 'foo', 'safe content';
368              
369             Generate HTML/XML tag and render it right away. This is a significantly faster alternative to L for template
370             systems that have to generate a lot of tags.
371              
372             =head1 ATTRIBUTES
373              
374             L implements the following attributes.
375              
376             =head2 tree
377              
378             my $tree = $html->tree;
379             $html = $html->tree(['root']);
380              
381             Document Object Model. Note that this structure should only be used very carefully since it is very dynamic.
382              
383             =head2 xml
384              
385             my $bool = $html->xml;
386             $html = $html->xml($bool);
387              
388             Disable HTML semantics in parser and activate case-sensitivity, defaults to auto-detection based on XML declarations.
389              
390             =head1 METHODS
391              
392             L inherits all methods from L and implements the following new ones.
393              
394             =head2 parse
395              
396             $html = $html->parse('I ♥ Mojolicious!');
397              
398             Parse HTML/XML fragment.
399              
400             =head2 render
401              
402             my $str = $html->render;
403              
404             Render DOM to HTML/XML.
405              
406             =head2 tag
407              
408             $html = $html->tag('div', id => 'foo', 'safe content');
409              
410             Generate HTML/XML tag.
411              
412             =head1 SEE ALSO
413              
414             L, L, L.
415              
416             =cut