package WebHACC::Output;
use strict;
require IO::Handle;
use Scalar::Util qw/refaddr/;
my $htescape = sub ($) {
my $s = $_[0];
$s =~ s/&/&/g;
$s =~ s/</g;
$s =~ s/>/>/g;
$s =~ s/"/"/g;
$s =~ s{([\x00-\x09\x0B-\x1F\x7F-\xA0\x{FEFF}\x{FFFC}-\x{FFFF}])}{
sprintf 'U+%04X', ord $1;
}ge;
return $s;
};
my $htescape_value = sub ($) {
my $s = $_[0];
$s =~ s/&/&/g;
$s =~ s/</g;
$s =~ s/>/>/g;
$s =~ s/"/"/g;
return $s;
};
sub new ($) {
require WebHACC::Input;
return bless {nav => [], section_rank => 1,
input => WebHACC::Input->new}, shift;
} # new
sub input ($;$) {
if (@_ > 1) {
if (defined $_[1]) {
$_[0]->{input} = $_[1];
} else {
$_[0]->{input} = WebHACC::Input->new;
}
}
return $_[0]->{input};
} # input
sub handle ($;$) {
if (@_ > 1) {
if (defined $_[1]) {
$_[0]->{handle} = $_[1];
} else {
delete $_[0]->{handle};
}
}
return $_[0]->{handle};
} # handle
sub set_utf8 ($) {
binmode shift->{handle}, ':utf8';
} # set_utf8
sub set_flush ($) {
shift->{handle}->autoflush (1);
} # set_flush
sub unset_flush ($) {
shift->{handle}->autoflush (0);
} # unset_flush
sub html ($$) {
shift->{handle}->print (shift);
} # html
sub text ($$) {
shift->html ($htescape->(shift));
} # text
sub url ($$%) {
my ($self, $url, %opt) = @_;
$self->html (q[<]);
$self->link ($url, %opt, url => $url);
$self->html (q[>
]);
} # url
sub start_tag ($$%) {
my ($self, $tag_name, %opt) = @_;
$self->html ('<' . $htescape_value->($tag_name)); # escape for safety
if (exists $opt{id}) {
my $id = $self->input->id_prefix . $opt{id};
$self->html (' id="' . $htescape_value->($id) . '"');
delete $opt{id};
}
for (keys %opt) { # for safety
$self->html (' ' . $htescape_value->($_) . '="' .
$htescape_value->($opt{$_}) . '"');
}
$self->html ('>');
} # start_tag
sub end_tag ($$) {
shift->html ('' . $htescape_value->(shift) . '>');
} # end_tag
sub start_section ($%) {
my ($self, %opt) = @_;
my $class = 'section';
if (defined $opt{role}) {
if ($opt{role} eq 'parse-errors') {
$opt{id} ||= 'parse-errors';
$opt{title} ||= 'Parse Errors Section';
$opt{short_title} ||= 'Parse Errors';
delete $opt{role};
} elsif ($opt{role} eq 'structure-errors') {
$opt{id} ||= 'document-errors';
$opt{title} ||= 'Structural Errors';
$opt{short_title} ||= 'Struct. Errors';
delete $opt{role};
} elsif ($opt{role} eq 'reformatted') {
$opt{id} ||= 'document-tree';
$opt{title} ||= 'Reformatted Document Source';
$opt{short_title} ||= 'Reformatted';
delete $opt{role}
} elsif ($opt{role} eq 'tree') {
$opt{id} ||= 'document-tree';
$opt{title} ||= 'Document Tree';
$opt{short_title} ||= 'Tree';
delete $opt{role};
} elsif ($opt{role} eq 'structure') {
$opt{id} ||= 'document-structure';
$opt{title} ||= 'Document Structure';
$opt{short_title} ||= 'Structure';
delete $opt{role};
} elsif ($opt{role} eq 'subdoc') {
$class .= ' subdoc';
delete $opt{role};
}
}
$self->{section_rank}++;
$self->html (qq[
');
} # start_code_block
sub end_code_block ($) {
shift->html ('
');
} # end_code_block
sub code ($$;%) {
my ($self, $content, %opt) = @_;
$self->start_tag ('code', %opt);
$self->text ($content);
$self->html ('');
} # code
sub script ($$;%) {
my ($self, $content, %opt) = @_;
$self->start_tag ('script', %opt);
$self->html ($content);
$self->html ('');
} # script
sub dt ($$;%) {
my ($self, $content, %opt) = @_;
$self->start_tag ('dt', %opt);
$self->nl_text ($content, text => $opt{text});
} # dt
sub select ($$%) {
my ($self, $options, %opt) = @_;
my $selected = $opt{selected};
delete $opt{selected};
$self->start_tag ('select', %opt);
my @options = @$options;
while (@options) {
my $opt = shift @options;
if ($opt->{options}) {
$self->html ('