/[suikacvs]/test/html-webhacc/WebHACC/Output.pm
Suika

Contents of /test/html-webhacc/WebHACC/Output.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.6 - (show annotations) (download)
Mon Jul 21 09:54:59 2008 UTC (16 years, 4 months ago) by wakaba
Branch: MAIN
Changes since 1.5: +2 -3 lines
Time to goodbye WDCC, hello WebHACC

1 package WebHACC::Output;
2 use strict;
3
4 require IO::Handle;
5 use Scalar::Util qw/refaddr/;
6
7 my $htescape = sub ($) {
8 my $s = $_[0];
9 $s =~ s/&/&/g;
10 $s =~ s/</&lt;/g;
11 $s =~ s/>/&gt;/g;
12 $s =~ s/"/&quot;/g;
13 $s =~ s{([\x00-\x09\x0B-\x1F\x7F-\xA0\x{FEFF}\x{FFFC}-\x{FFFF}])}{
14 sprintf '<var>U+%04X</var>', ord $1;
15 }ge;
16 return $s;
17 };
18
19 sub new ($) {
20 return bless {nav => [], section_rank => 1}, shift;
21 } # new
22
23 sub input ($;$) {
24 if (@_ > 1) {
25 if (defined $_[1]) {
26 $_[0]->{input} = $_[1];
27 } else {
28 delete $_[0]->{input};
29 }
30 }
31
32 return $_[0]->{input};
33 } # input
34
35 sub handle ($;$) {
36 if (@_ > 1) {
37 if (defined $_[1]) {
38 $_[0]->{handle} = $_[1];
39 } else {
40 delete $_[0]->{handle};
41 }
42 }
43
44 return $_[0]->{handle};
45 } # handle
46
47 sub set_utf8 ($) {
48 binmode shift->{handle}, ':utf8';
49 } # set_utf8
50
51 sub set_flush ($) {
52 shift->{handle}->autoflush (1);
53 } # set_flush
54
55 sub unset_flush ($) {
56 shift->{handle}->autoflush (0);
57 } # unset_flush
58
59 sub html ($$) {
60 shift->{handle}->print (shift);
61 } # html
62
63 sub text ($$) {
64 shift->html ($htescape->(shift));
65 } # text
66
67 sub url ($$%) {
68 my ($self, $url, %opt) = @_;
69 $self->html (q[<code class=uri>&lt;]);
70 $self->link ($url, %opt, url => $url);
71 $self->html (q[></code>]);
72 } # url
73
74 sub start_tag ($$%) {
75 my ($self, $tag_name, %opt) = @_;
76 $self->html ('<' . $htescape->($tag_name)); # escape for safety
77 if (exists $opt{id}) {
78 my $id = $self->input->id_prefix . $opt{id};
79 $self->html (' id="' . $htescape->($id) . '"');
80 delete $opt{id};
81 }
82 for (keys %opt) { # for safety
83 $self->html (' ' . $htescape->($_) . '="' . $htescape->($opt{$_}) . '"');
84 }
85 $self->html ('>');
86 } # start_tag
87
88 sub end_tag ($$) {
89 shift->html ('</' . $htescape->(shift) . '>');
90 } # end_tag
91
92 sub start_section ($%) {
93 my ($self, %opt) = @_;
94
95 if (defined $opt{role}) {
96 if ($opt{role} eq 'parse-errors') {
97 $opt{id} ||= 'parse-errors';
98 $opt{title} ||= 'Parse Errors';
99 delete $opt{role};
100 } elsif ($opt{role} eq 'structure-errors') {
101 $opt{id} ||= 'document-errors';
102 $opt{title} ||= 'Structural Errors';
103 $opt{short_title} ||= 'Struct. Errors';
104 delete $opt{role};
105 } elsif ($opt{role} eq 'reformatted') {
106 $opt{id} ||= 'document-tree';
107 $opt{title} ||= 'Reformatted Document Source';
108 $opt{short_title} ||= 'Reformatted';
109 delete $opt{role}
110 } elsif ($opt{role} eq 'tree') {
111 $opt{id} ||= 'document-tree';
112 $opt{title} ||= 'Document Tree';
113 $opt{short_title} ||= 'Tree';
114 delete $opt{role};
115 } elsif ($opt{role} eq 'structure') {
116 $opt{id} ||= 'document-structure';
117 $opt{title} ||= 'Document Structure';
118 $opt{short_title} ||= 'Structure';
119 delete $opt{role};
120 }
121 }
122
123 $self->{section_rank}++;
124 $self->html ('<div class=section');
125 if (defined $opt{id}) {
126 my $id = $self->input->id_prefix . $opt{id};
127 $self->html (' id="' . $htescape->($id) . '"');
128 push @{$self->{nav}}, [$id => $opt{short_title} || $opt{title}]
129 if $self->{section_rank} == 2;
130 }
131 my $section_rank = $self->{section_rank};
132 $section_rank = 6 if $section_rank > 6;
133 $self->html ('><h' . $section_rank . '>' .
134 $htescape->($opt{title}) .
135 '</h' . $section_rank . '>');
136 } # start_section
137
138 sub end_section ($) {
139 my $self = shift;
140 $self->html ('</div>');
141 $self->{handle}->flush;
142 $self->{section_rank}--;
143 } # end_section
144
145 sub start_error_list ($%) {
146 my ($self, %opt) = @_;
147
148 if (defined $opt{role}) {
149 if ($opt{role} eq 'parse-errors') {
150 $opt{id} ||= 'parse-errors-list';
151 delete $opt{role};
152 } elsif ($opt{role} eq 'structure-errors') {
153 $opt{id} ||= 'document-errors-list';
154 delete $opt{role};
155 }
156 }
157
158 $self->start_tag ('dl', %opt);
159 } # start_error_list
160
161 sub end_error_list ($%) {
162 my ($self, %opt) = @_;
163
164 if (defined $opt{role}) {
165 if ($opt{role} eq 'parse-errors') {
166 delete $opt{role};
167 $self->end_tag ('dl');
168 ## NOTE: For parse error list, the |add_source_to_parse_error_list|
169 ## method is invoked at the end of |generate_source_string_section|,
170 ## since that generation method is invoked after the error list
171 ## is generated.
172 } elsif ($opt{role} eq 'structure-errors') {
173 delete $opt{role};
174 $self->end_tag ('dl');
175 $self->add_source_to_parse_error_list ('document-errors-list');
176 } else {
177 $self->end_tag ('dl');
178 }
179 } else {
180 $self->end_tag ('dl');
181 }
182 } # end_error_list
183
184 sub add_source_to_parse_error_list ($$) {
185 my $self = shift;
186
187 $self->script (q[addSourceToParseErrorList ('] . $self->input->id_prefix .
188 q[', '] . shift . q[')]);
189 } # add_source_to_parse_error_list
190
191 sub start_code_block ($) {
192 shift->html ('<pre><code>');
193 } # start_code_block
194
195 sub end_code_block ($) {
196 shift->html ('</code></pre>');
197 } # end_code_block
198
199 sub code ($$;%) {
200 my ($self, $content, %opt) = @_;
201 $self->start_tag ('code', %opt);
202 $self->text ($content);
203 $self->html ('</code>');
204 } # code
205
206 sub script ($$;%) {
207 my ($self, $content, %opt) = @_;
208 $self->start_tag ('script', %opt);
209 $self->html ($content);
210 $self->html ('</script>');
211 } # script
212
213 sub dt ($$;%) {
214 my ($self, $content, %opt) = @_;
215 $self->start_tag ('dt', %opt);
216 $self->text ($content);
217 } # dt
218
219 sub link ($$%) {
220 my ($self, $content, %opt) = @_;
221 $self->start_tag ('a', %opt, href => $opt{url});
222 $self->text ($content);
223 $self->html ('</a>');
224 } # link
225
226 sub xref ($$%) {
227 my ($self, $content, %opt) = @_;
228 $self->html ('<a href="#' . $htescape->($self->input->id_prefix . $opt{target}) . '">');
229 $self->text ($content);
230 $self->html ('</a>');
231 } # xref
232
233 sub link_to_webhacc ($$%) {
234 my ($self, $content, %opt) = @_;
235 $opt{url} = './?uri=' . $self->encode_url_component ($opt{url});
236 $self->link ($content, %opt);
237 } # link_to_webhacc
238
239
240 my $get_node_path = sub ($) {
241 my $node = shift;
242 my @r;
243 while (defined $node) {
244 my $rs;
245 if ($node->node_type == 1) {
246 $rs = $node->node_name;
247 $node = $node->parent_node;
248 } elsif ($node->node_type == 2) {
249 $rs = '@' . $node->node_name;
250 $node = $node->owner_element;
251 } elsif ($node->node_type == 3) {
252 $rs = '"' . $node->data . '"';
253 $node = $node->parent_node;
254 } elsif ($node->node_type == 9) {
255 @r = ('') unless @r;
256 $rs = '';
257 $node = $node->parent_node;
258 } else {
259 $rs = '#' . $node->node_type;
260 $node = $node->parent_node;
261 }
262 unshift @r, $rs;
263 }
264 return join '/', @r;
265 }; # $get_node_path
266
267 sub node_link ($$) {
268 my ($self, $node) = @_;
269 $self->xref ($get_node_path->($node), target => 'node-' . refaddr $node);
270 } # node_link
271
272 sub nav_list ($) {
273 my $self = shift;
274 $self->html (q[<ul class="navigation" id="nav-items">]);
275 for (@{$self->{nav}}) {
276 $self->html (qq[<li><a href="#@{[$htescape->($_->[0])]}">@{[$htescape->($_->[1])]}</a>]);
277 }
278 $self->html ('</ul>');
279 } # nav_list
280
281 sub http_header ($) {
282 shift->html (qq[Content-Type: text/html; charset=utf-8\n\n]);
283 } # http_header
284
285 sub http_error ($$) {
286 my $self = shift;
287 my $code = 0+shift;
288 my $text = {
289 404 => 'Not Found',
290 }->{$code};
291 $self->html (qq[Status: $code $text\nContent-Type: text/html ; charset=us-ascii\n\n$code $text]);
292 } # http_error
293
294 sub html_header ($) {
295 my $self = shift;
296 $self->html (q[<!DOCTYPE html>
297 <html lang="en">
298 <head>
299 <title>WebHACC (BETA) Result</title>
300 <link rel="stylesheet" href="../cc-style.css" type="text/css">
301 </head>
302 <body>
303 <h1><a href="../cc-interface"><abbr title="Web Hypertext Application Conformance Checker (BETA)"><img src="../icons/title" alt="WebHACC"></abbr></a></h1>
304 ]);
305 } # html_header
306
307 sub encode_url_component ($$) {
308 shift;
309 require Encode;
310 my $s = Encode::encode ('utf8', shift);
311 $s =~ s/([^0-9A-Za-z_.~-])/sprintf '%%%02X', ord $1/ge;
312 return $s;
313 } # encode_url_component
314
315 1;

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24