package Mojo::DOM::HTML; use Mojo::Base -base; use Exporter qw(import); use Mojo::Util qw(html_attr_unescape html_unescape xml_escape); use Scalar::Util qw(weaken); our @EXPORT_OK = ('tag_to_html'); has tree => sub { ['root'] }; has 'xml'; my $ATTR_RE = qr/ ([^<>=\s\/0-9.\-][^<>=\s\/]*|\/) # Key (?: \s*=\s* (?s:(["'])(.*?)\g{-2}|([^>\s]*)) # Value )? \s* /x; my $TOKEN_RE = qr/ ([^<]+)? # Text (?: <(?: !(?: DOCTYPE( \s+\w+ # Doctype (?:(?:\s+\w+)?(?:\s+(?:"[^"]*"|'[^']*'))+)? # External ID (?:\s+\[.+?\])? # Int Subset \s*) | --(.*?)(?:--!?|(?<=' if $type eq 'comment'; # CDATA return '[1] . ']]>' if $type eq 'cdata'; # Processing instruction return '' . $tree->[1] . '?>' if $type eq 'pi'; # Everything else return ''; } sub _script_content { my $html = shift; my $start = pos $$html; my $state = 0; while (1) { if ($state == 0) { $$html =~ /\G[^<]*/gcs } else { $$html =~ /\G[^<\-]*/gcs } my $p = pos $$html; return (substr($$html, $start), 0) if $p >= length $$html; if ($state == 0) { if ($$html =~ m!\G)!gcsi) { return (substr($$html, $start, $p - $start), 1) } elsif ($$html =~ /\G/gcs) { $state = 0 } elsif ($$html =~ m!\G])!gcsi) { $state = 2 } else { pos($$html) = $p + 1 } } else { if ($$html =~ m!\G)!gcsi) { $state = 1 } elsif ($$html =~ /\G-->/gcs) { $state = 0 } else { pos($$html) = $p + 1 } } } } sub _start { my ($start, $attrs, $xml, $current) = @_; # Autoclose optional HTML elements if (!$xml && $$current->[0] ne 'root') { if (my $end = $END{$start}) { _end($end, 0, $current) } elsif (my $close = $CLOSE{$start}) { my ($allowed, $scope) = @$close; # Close allowed parent elements in scope my $parent = $$current; while ($parent->[0] ne 'root' && !$scope->{$parent->[1]}) { _end($parent->[1], 0, $current) if $allowed->{$parent->[1]}; $parent = $parent->[3]; } } } # New tag push @$$current, my $new = ['tag', $start, $attrs, $$current]; weaken $new->[3]; $$current = $new; } sub _tag { my $tree = ['tag', shift, undef, undef]; # Content push @$tree, ref $_[-1] eq 'CODE' ? ['raw', pop->()] : ['text', pop] if @_ % 2; # Attributes my $attrs = $tree->[2] = {@_}; return $tree unless exists $attrs->{data} && ref $attrs->{data} eq 'HASH'; my $data = delete $attrs->{data}; @$attrs{map { y/_/-/; lc "data-$_" } keys %$data} = values %$data; return $tree; } 1; =encoding utf8 =head1 NAME Mojo::DOM::HTML - HTML/XML engine =head1 SYNOPSIS use Mojo::DOM::HTML; # Turn HTML into DOM tree my $html = Mojo::DOM::HTML->new; $html->parse('
Test
123