/[suikacvs]/test/html-webhacc/cc.cgi
Suika

Contents of /test/html-webhacc/cc.cgi

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.7 - (show annotations) (download)
Sun Jul 1 06:21:46 2007 UTC (17 years, 5 months ago) by wakaba
Branch: MAIN
Changes since 1.6: +65 -23 lines
++ ChangeLog	1 Jul 2007 06:21:44 -0000
2007-07-01  Wakaba  <wakaba@suika.fam.cx>

	* cc-style.css: New rules for empty error lists,
	error levels, and |code|.

	* cc.cgi: Support for error message catalog.
	Support for empty (zero-length) document.
	(print_source_string): Support for empty document.
	(print_document_tree): Element |has_child_nodes| test
	was incorrect.  Output compat mode and is html flag
	for |Document| node.
	(load_text_catalog, get_text): New functions.

	* LICENSE, large-alert.png, large-info.png, large-stop.png:
	New files.

1 #!/usr/bin/perl
2 use strict;
3
4 use lib qw[/home/httpd/html/www/markup/html/whatpm
5 /home/wakaba/work/manakai/lib
6 /home/wakaba/public_html/-temp/wiki/lib];
7 use CGI::Carp qw[fatalsToBrowser];
8 use Scalar::Util qw[refaddr];
9
10 use SuikaWiki::Input::HTTP; ## TODO: Use some better CGI module
11
12 sub htescape ($) {
13 my $s = $_[0];
14 $s =~ s/&/&amp;/g;
15 $s =~ s/</&lt;/g;
16 $s =~ s/>/&gt;/g;
17 $s =~ s/"/&quot;/g;
18 $s =~ s!([\x00-\x09\x0B-\x1F\x7F-\x80])!sprintf '<var>U+%04X</var>', ord $1!ge;
19 return $s;
20 } # htescape
21
22 my $http = SuikaWiki::Input::HTTP->new;
23
24 ## TODO: _charset_
25
26 my $input_format = $http->parameter ('i') || 'text/html';
27 my $inner_html_element = $http->parameter ('e');
28 my $input_uri = 'thismessage:/';
29
30 my $s = $http->parameter ('s');
31 if (length $s > 1000_000) {
32 print STDOUT "Status: 400 Document Too Long\nContent-Type: text/plain; charset=us-ascii\n\nToo long";
33 exit;
34 }
35
36 load_text_catalog ('en'); ## TODO: conneg
37
38 my @nav;
39 print STDOUT qq[Content-Type: text/html; charset=utf-8
40
41 <!DOCTYPE html>
42 <html lang="en">
43 <head>
44 <title>Web Document Conformance Checker (BETA)</title>
45 <link rel="stylesheet" href="../cc-style.css" type="text/css">
46 </head>
47 <body>
48 <h1>Web Document Conformance Checker (<em>beta</em>)</h1>
49
50 <div id="document-info" class="section">
51 <dl>
52 <dt>Document URI</dt>
53 <dd><code class="URI" lang="">&lt;<a href="@{[htescape $input_uri]}">@{[htescape $input_uri]}</a>&gt;</code></dd>
54 <dt>Internet Media Type</dt>
55 <dd><code class="MIME" lang="en">@{[htescape $input_format]}</code></dd>
56 ]; # no </dl> yet
57 push @nav, ['#document-info' => 'Information'];
58
59 require Message::DOM::DOMImplementation;
60 my $dom = Message::DOM::DOMImplementation->____new;
61 my $doc;
62 my $el;
63
64 if ($input_format eq 'text/html') {
65 require Encode;
66 require Whatpm::HTML;
67
68 $s = Encode::decode ('utf-8', $s);
69
70 print STDOUT qq[
71 <dt>Character Encoding</dt>
72 <dd>(none)</dd>
73 </dl>
74 </div>
75
76 <div id="source-string" class="section">
77 <h2>Document Source</h2>
78 ];
79 push @nav, ['#source-string' => 'Source'];
80 print_source_string (\$s);
81 print STDOUT qq[
82 </div>
83
84 <div id="parse-errors" class="section">
85 <h2>Parse Errors</h2>
86
87 <dl>
88 ];
89 push @nav, ['#parse-errors' => 'Parse Error'];
90
91 my $onerror = sub {
92 my (%opt) = @_;
93 my ($cls, $msg) = get_text ($opt{type}, $opt{level});
94 if ($opt{column} > 0) {
95 print STDOUT qq[<dt class="$cls"><a href="#line-$opt{line}">Line $opt{line}</a> column $opt{column}</dt>\n];
96 } else {
97 $opt{line} = $opt{line} - 1 || 1;
98 print STDOUT qq[<dt class="$cls"><a href="#line-$opt{line}">Line $opt{line}</a></dt>\n];
99 }
100 print STDOUT qq[<dd class="$cls">$msg</dd>\n];
101 };
102
103 $doc = $dom->create_document;
104 if (defined $inner_html_element and length $inner_html_element) {
105 $el = $doc->create_element_ns
106 ('http://www.w3.org/1999/xhtml', [undef, $inner_html_element]);
107 Whatpm::HTML->set_inner_html ($el, $s, $onerror);
108 } else {
109 Whatpm::HTML->parse_string ($s => $doc, $onerror);
110 }
111
112 print STDOUT qq[
113 </dl>
114 </div>
115 ];
116 } elsif ($input_format eq 'application/xhtml+xml') {
117 require Message::DOM::XMLParserTemp;
118 require Encode;
119
120 my $t = Encode::decode ('utf-8', $s);
121
122 print STDOUT qq[
123 <dt>Character Encoding</dt>
124 <dd>(none)</dd>
125 </dl>
126 </div>
127
128 <div id="source-string" class="section">
129 <h2>Document Source</h2>
130 ];
131 push @nav, ['#source-string' => 'Source'];
132 print_source_string (\$t);
133 print STDOUT qq[
134 </div>
135
136 <div id="parse-errors" class="section">
137 <h2>Parse Errors</h2>
138
139 <dl>];
140 push @nav, ['#parse-errors' => 'Parse Error'];
141
142 my $onerror = sub {
143 my $err = shift;
144 my $line = $err->location->line_number;
145 print STDOUT qq[<dt><a href="#line-$line">Line $line</a> column ];
146 print STDOUT $err->location->column_number, "</dt><dd>";
147 print STDOUT htescape $err->text, "</dd>\n";
148 return 1;
149 };
150
151 open my $fh, '<', \$s;
152 $doc = Message::DOM::XMLParserTemp->parse_byte_stream
153 ($fh => $dom, $onerror, charset => 'utf-8');
154
155 print STDOUT qq[</dl>
156 </div>
157 ];
158 } else {
159 print STDOUT qq[
160 </dl>
161 </div>
162
163 <div id="result-summary" class="section">
164 <p><em>Media type <code class="MIME" lang="en">@{[htescape $input_format]}</code> is not supported!</em></p>
165 </div>
166 ];
167 push @nav, ['#result-summary' => 'Result'];
168 }
169
170
171 if (defined $doc or defined $el) {
172 print STDOUT qq[
173 <div id="document-tree" class="section">
174 <h2>Document Tree</h2>
175 ];
176 push @nav, ['#document-tree' => 'Tree'];
177
178 print_document_tree ($el || $doc);
179
180 print STDOUT qq[
181 </div>
182
183 <div id="document-errors" class="section">
184 <h2>Document Errors</h2>
185
186 <dl>];
187 push @nav, ['#document-errors' => 'Document Error'];
188
189 require Whatpm::ContentChecker;
190 my $onerror = sub {
191 my %opt = @_;
192 my ($cls, $msg) = get_text ($opt{type}, $opt{level});
193 print STDOUT qq[<dt class="$cls">] . get_node_link ($opt{node}) .
194 qq[</dt>\n<dd class="$cls">], $msg, "</dd>\n";
195 };
196
197 my $elements;
198 if ($el) {
199 $elements = Whatpm::ContentChecker->check_element ($el, $onerror);
200 } else {
201 $elements = Whatpm::ContentChecker->check_document ($doc, $onerror);
202 }
203
204 print STDOUT qq[</dl>
205 </div>
206 ];
207
208 if (@{$elements->{table}}) {
209 require JSON;
210
211 print STDOUT qq[
212 <div id="tables" class="section">
213 <h2>Tables</h2>
214
215 <!--[if IE]><script type="text/javascript" src="../excanvas.js"></script><![endif]-->
216 <script src="../table-script.js" type="text/javascript"></script>
217 <noscript>
218 <p><em>Structure of tables are visualized here if scripting is enabled.</em></p>
219 </noscript>
220 ];
221
222 my $i = 0;
223 for my $table_el (@{$elements->{table}}) {
224 $i++;
225 print STDOUT qq[<div class="section" id="table-$i"><h3>] .
226 get_node_link ($table_el) . q[</h3>];
227
228 my $table = Whatpm::HTMLTable->form_table ($table_el);
229
230 for (@{$table->{column_group}}, @{$table->{column}}, $table->{caption}) {
231 next unless $_;
232 delete $_->{element};
233 }
234
235 for (@{$table->{row_group}}) {
236 next unless $_;
237 next unless $_->{element};
238 $_->{type} = $_->{element}->manakai_local_name;
239 delete $_->{element};
240 }
241
242 for (@{$table->{cell}}) {
243 next unless $_;
244 for (@{$_}) {
245 next unless $_;
246 for (@$_) {
247 $_->{id} = refaddr $_->{element} if defined $_->{element};
248 delete $_->{element};
249 }
250 }
251 }
252
253 print STDOUT '</div><script type="text/javascript">tableToCanvas (';
254 print STDOUT JSON::objToJson ($table);
255 print STDOUT qq[, document.getElementById ('table-$i'));</script>];
256 }
257
258 print STDOUT qq[</div>];
259 }
260
261 if (keys %{$elements->{term}}) {
262 print STDOUT qq[
263 <div id="terms" class="section">
264 <h2>Terms</h2>
265
266 <dl>
267 ];
268 for my $term (sort {$a cmp $b} keys %{$elements->{term}}) {
269 print STDOUT qq[<dt>@{[htescape $term]}</dt>];
270 for (@{$elements->{term}->{$term}}) {
271 print STDOUT qq[<dd>].get_node_link ($_).qq[</dd>];
272 }
273 }
274 print STDOUT qq[</dl></div>];
275 }
276 }
277
278 ## TODO: Show result
279
280 print STDOUT qq[
281 <ul class="navigation" id="nav-items">
282 ];
283 for (@nav) {
284 print STDOUT qq[<li><a href="$_->[0]">$_->[1]</a></li>];
285 }
286 print STDOUT qq[
287 </ul>
288 </body>
289 </html>
290 ];
291
292 exit;
293
294 sub print_source_string ($) {
295 my $s = $_[0];
296 my $i = 1;
297 print STDOUT qq[<ol lang="">\n];
298 if (length $$s) {
299 while ($$s =~ /\G([^\x0A]*?)\x0D?\x0A/gc) {
300 print STDOUT qq[<li id="line-$i">], htescape $1, "</li>\n";
301 $i++;
302 }
303 if ($$s =~ /\G([^\x0A]+)/gc) {
304 print STDOUT qq[<li id="line-$i">], htescape $1, "</li>\n";
305 }
306 } else {
307 print STDOUT q[<li id="line-1"></li>];
308 }
309 print STDOUT "</ol>";
310 } # print_input_string
311
312 sub print_document_tree ($) {
313 my $node = shift;
314 my $r = '<ol class="xoxo">';
315
316 my @node = ($node);
317 while (@node) {
318 my $child = shift @node;
319 unless (ref $child) {
320 $r .= $child;
321 next;
322 }
323
324 my $node_id = 'node-'.refaddr $child;
325 my $nt = $child->node_type;
326 if ($nt == $child->ELEMENT_NODE) {
327 my $child_nsuri = $child->namespace_uri;
328 $r .= qq[<li id="$node_id" class="tree-element"><code title="@{[defined $child_nsuri ? $child_nsuri : '']}">] . htescape ($child->tag_name) .
329 '</code>'; ## ISSUE: case
330
331 if ($child->has_attributes) {
332 $r .= '<ul class="attributes">';
333 for my $attr (sort {$a->[0] cmp $b->[0]} map { [$_->name, $_->value, $_->namespace_uri, 'node-'.refaddr $_] }
334 @{$child->attributes}) {
335 $r .= qq[<li id="$attr->[3]" class="tree-attribute"><code title="@{[defined $_->[2] ? $_->[2] : '']}">] . htescape ($attr->[0]) . '</code> = '; ## ISSUE: case?
336 $r .= '<q>' . htescape ($attr->[1]) . '</q></li>'; ## TODO: children
337 }
338 $r .= '</ul>';
339 }
340
341 if ($child->has_child_nodes) {
342 $r .= '<ol class="children">';
343 unshift @node, @{$child->child_nodes}, '</ol></li>';
344 } else {
345 $r .= '</li>';
346 }
347 } elsif ($nt == $child->TEXT_NODE) {
348 $r .= qq'<li id="$node_id" class="tree-text"><q lang="">' . htescape ($child->data) . '</q></li>';
349 } elsif ($nt == $child->CDATA_SECTION_NODE) {
350 $r .= qq'<li id="$node_id" class="tree-cdata"><code>&lt;[CDATA[</code><q lang="">' . htescape ($child->data) . '</q><code>]]&gt;</code></li>';
351 } elsif ($nt == $child->COMMENT_NODE) {
352 $r .= qq'<li id="$node_id" class="tree-comment"><code>&lt;!--</code><q lang="">' . htescape ($child->data) . '</q><code>--&gt;</code></li>';
353 } elsif ($nt == $child->DOCUMENT_NODE) {
354 $r .= qq'<li id="$node_id" class="tree-document">Document';
355 $r .= qq[<ul class="attributes">];
356 $r .= qq[<li>@{[scalar get_text ('manakaiIsHTML:'.($child->manakai_is_html?1:0))]}</li>];
357 $r .= qq[<li>@{[scalar get_text ('manakaiCompatMode:'.$child->manakai_compat_mode)]}</li>];
358 $r .= qq[</ul>];
359 if ($child->has_child_nodes) {
360 $r .= '<ol class="children">';
361 unshift @node, @{$child->child_nodes}, '</ol></li>';
362 }
363 } elsif ($nt == $child->DOCUMENT_TYPE_NODE) {
364 $r .= qq'<li id="$node_id" class="tree-doctype"><code>&lt;!DOCTYPE&gt;</code><ul class="attributes">';
365 $r .= qq[<li class="tree-doctype-name">Name = <q>@{[htescape ($child->name)]}</q></li>];
366 $r .= qq[<li class="tree-doctype-publicid">Public identifier = <q>@{[htescape ($child->public_id)]}</q></li>];
367 $r .= qq[<li class="tree-doctype-systemid">System identifier = <q>@{[htescape ($child->system_id)]}</q></li>];
368 $r .= '</ul></li>';
369 } elsif ($nt == $child->PROCESSING_INSTRUCTION_NODE) {
370 $r .= qq'<li id="$node_id" class="tree-id"><code>&lt;?@{[htescape ($child->target)]}</code> <q>@{[htescape ($child->data)]}</q><code>?&gt;</code></li>';
371 } else {
372 $r .= qq'<li id="$node_id" class="tree-unknown">@{[$child->node_type]} @{[htescape ($child->node_name)]}</li>'; # error
373 }
374 }
375
376 $r .= '</ol>';
377 print STDOUT $r;
378 } # print_document_tree
379
380 sub get_node_path ($) {
381 my $node = shift;
382 my @r;
383 while (defined $node) {
384 my $rs;
385 if ($node->node_type == 1) {
386 $rs = $node->manakai_local_name;
387 $node = $node->parent_node;
388 } elsif ($node->node_type == 2) {
389 $rs = '@' . $node->manakai_local_name;
390 $node = $node->owner_element;
391 } elsif ($node->node_type == 3) {
392 $rs = '"' . $node->data . '"';
393 $node = $node->parent_node;
394 } elsif ($node->node_type == 9) {
395 $rs = '';
396 $node = $node->parent_node;
397 } else {
398 $rs = '#' . $node->node_type;
399 $node = $node->parent_node;
400 }
401 unshift @r, $rs;
402 }
403 return join '/', @r;
404 } # get_node_path
405
406 sub get_node_link ($) {
407 return qq[<a href="#node-@{[refaddr $_[0]]}">] .
408 htescape (get_node_path ($_[0])) . qq[</a>];
409 } # get_node_link
410
411 {
412 my $Msg = {};
413
414 sub load_text_catalog ($) {
415 my $lang = shift; # MUST be a canonical lang name
416 open my $file, '<', "cc-msg.$lang.txt" or die "$0: cc-msg.$lang.txt: $!";
417 while (<$file>) {
418 if (s/^([^;]+);([^;]*);//) {
419 my ($type, $cls, $msg) = ($1, $2, $_);
420 $msg =~ tr/\x0D\x0A//d;
421 $Msg->{$type} = [$cls, $msg];
422 }
423 }
424 } # load_text_catalog
425
426 sub get_text ($) {
427 my ($type, $level) = @_;
428 $type = $level . ':' . $type if defined $level;
429 my @arg;
430 {
431 if (defined $Msg->{$type}) {
432 my $msg = $Msg->{$type}->[1];
433 $msg =~ s/\$([0-9]+)/defined $arg[$1] ? htescape ($arg[$1]) : '(undef)'/ge;
434 return ($Msg->{$type}->[0], $msg);
435 } elsif ($type =~ s/:([^:]*)$//) {
436 unshift @arg, $1;
437 redo;
438 }
439 }
440 return ('', htescape ($_[0]));
441 } # get_text
442
443 }
444
445 =head1 AUTHOR
446
447 Wakaba <w@suika.fam.cx>.
448
449 =head1 LICENSE
450
451 Copyright 2007 Wakaba <w@suika.fam.cx>
452
453 This library is free software; you can redistribute it
454 and/or modify it under the same terms as Perl itself.
455
456 =cut
457
458 ## $Date: 2007/06/30 14:51:10 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24