/[suikacvs]/markup/html/whatpm/Whatpm/HTMLTable.pm
Suika

Contents of /markup/html/whatpm/Whatpm/HTMLTable.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (hide annotations) (download)
Sun May 27 06:38:58 2007 UTC (17 years, 5 months ago) by wakaba
Branch: MAIN
Changes since 1.1: +6 -4 lines
++ whatpm/Whatpm/ChangeLog	27 May 2007 06:38:49 -0000
	* ContentChecker.pm ($HTMLURIAttrChecker): Include
	error position in the |type| option of the error.

	* HTMLTable.pm (form_table): The |$onerror| parameter
	is now optional.  Some bugs are fixed.

2007-05-27  Wakaba  <wakaba@suika.fam.cx>

1 wakaba 1.1 package Whatpm::HTMLTable;
2     use strict;
3    
4     ## An implementation of "Forming a table" algorithm in HTML5
5     sub form_table ($$$) {
6     my (undef, $table_el, $onerror) = @_;
7 wakaba 1.2 $onerror ||= sub { };
8 wakaba 1.1
9     ## Step 1
10     my $x_max = 0;
11    
12     ## Step 2
13     my $y_max = 0;
14     my $y_max_node;
15    
16     ## Step 3
17     my $table = {
18     #caption
19     column => [],
20     column_group => [],
21 wakaba 1.2 # no |row| since HTML5 algorithm doesn't associate rows with <tr>s
22 wakaba 1.1 row_group => [],
23     cell => [],
24     };
25    
26     my @has_anchored_cell;
27     my @column_generated_by;
28     my $check_empty_column = sub {
29     for (1..$x_max) {
30     unless ($has_anchored_cell[$_]) {
31     if ($table->{column}->[$_]) {
32     $onerror->(type => 'column with no anchored cell',
33     node => $table->{column}->[$_]->{element});
34     } else {
35     $onerror->(type => 'colspan makes column with no anchored cell',
36     node => $column_generated_by[$_]);
37     }
38     }
39     }
40     }; # $check_empty_column
41    
42     ## Step 4
43     ## "If the table element has no table children, then return the table (which will be empty), and abort these steps."
44     ## ISSUE: What is "table children"?
45     my @table_child = @{$table_el->child_nodes};
46     return unless @table_child; # don't call $check_empty_column
47    
48     ## Step 5, 6, 8
49     my $current_element;
50     my $current_ln;
51     NEXT_CHILD: {
52     $current_element = shift @table_child;
53     if (defined $current_element) {
54     redo NEXT_CHILD unless $current_element->node_type == 1;
55     my $nsuri = $current_element->namespace_uri;
56     redo NEXT_CHILD unless defined $nsuri and
57     $nsuri eq q<http://www.w3.org/1999/xhtml>;
58     $current_ln = $current_element->manakai_local_name;
59    
60     if ($current_ln eq 'caption' and not defined $table->{caption}) {
61     ## Step 7
62     $table->{caption} = {element => $current_element};
63     redo NEXT_CHILD; # Step 8
64     }
65    
66     redo NEXT_CHILD unless {
67     #caption => 1, ## Step 6
68     colgroup => 1,
69     thead => 1,
70     tbody => 1,
71     tfoot => 1,
72     tr => 1,
73     }->{$current_ln};
74     } else {
75     ## End of subsection
76     $check_empty_column->();
77     ## Step 5 2nd paragraph
78     return $table;
79     }
80     } # NEXT_CHILD
81    
82     ## ISSUE: Step 9.1 /next column/ is not used.
83    
84     ## Step 9
85     while ($current_ln eq 'colgroup') { # Step 9, Step 9.5
86     ## Step 2: column groups
87     my @col = grep {
88     $_->node_type == 1 and
89     defined $_->namespace_uri and
90     $_->namespace_uri eq q<http://www.w3.org/1999/xhtml> and
91     $_->manakai_local_name eq 'col'
92     } @{$current_element->child_nodes};
93     if (@col) {
94     ## Step 1
95     my $x_start = $x_max + 1;
96    
97     ## Step 2, 6
98     while (@col) {
99     my $current_column = shift @col;
100    
101     ## Step 3: columns
102     my $span = 1;
103     my $col_span = $current_column->get_attribute_ns (undef, 'span');
104     ## Parse non-negative integer
105     if (defined $col_span and $col_span =~ /^[\x09-\x0D\x20]*([0-9]+)/) {
106     $span = $1 || 1;
107     }
108     ## ISSUE: If span=0, what is /span/ value?
109    
110     ## Step 4, 5
111     $table->{column}->[++$x_max] = {element => $current_column} for 1..$span;
112     }
113    
114     ## Step 7
115     my $cg = {element => $current_element,
116     x => $x_start, y => 1,
117 wakaba 1.2 width => $x_max - $x_start - 1}; ## ISSUE: Spec incorrect
118     $cg->{width} = $x_max - $x_start + 1;
119 wakaba 1.1 $table->{column_group}->[$_] = $cg for $x_start .. $x_max;
120     } else { # no <col> children
121     ## Step 1
122     my $span = 1;
123     my $col_span = $current_element->get_attribute_ns (undef, 'span');
124     ## Parse non-negative integer
125     if (defined $col_span and $col_span =~ /^[\x09-\x0D\x20]*([0-9]+)/) {
126     $span = $1 || 1;
127     }
128     ## ISSUE: If span=0, what is /span/ value?
129    
130     ## Step 2
131     $x_max += $span;
132    
133     ## Step 3
134     my $cg = {element => $current_element,
135     x => $x_max - $span + 1, y => 1,
136     width => $span};
137     $table->{column_group}->[$_] = $cg for (($x_max - $span + 1) .. $x_max);
138     }
139    
140     ## Step 3, 4
141     NEXT_CHILD: {
142     $current_element = shift @table_child;
143     if (defined $current_element) {
144     redo NEXT_CHILD unless $current_element->node_type == 1;
145     my $nsuri = $current_element->namespace_uri;
146     redo NEXT_CHILD unless defined $nsuri and
147     $nsuri eq q<http://www.w3.org/1999/xhtml>;
148     $current_ln = $current_element->manakai_local_name;
149    
150     redo NEXT_CHILD unless {
151     colgroup => 1,
152     thead => 1,
153     tbody => 1,
154     tfoot => 1,
155     tr => 1,
156     }->{$current_ln};
157     } else {
158     ## End of subsection
159     $check_empty_column->();
160     ## Step 5 of overall steps 2nd paragraph
161     return $table;
162     }
163     } # NEXT_CHILD
164     }
165    
166     ## Step 10
167     my $y_current = 0;
168    
169     ## Step 11
170     my @downward_growing_cells;
171    
172     my $growing_downward_growing_cells = sub {
173     ## Step 1
174     return unless @downward_growing_cells;
175    
176     ## Step 2
177     if ($y_max < $y_current) {
178     $y_max++;
179     undef $y_max_node;
180     }
181    
182     ## Step 3
183     for (@downward_growing_cells) {
184     for my $x ($_->[1] .. ($_->[1] + $_->[2] - 1)) {
185     $table->{cell}->[$x]->[$y_current] = [$_->[0]];
186     $_->[0]->{height}++;
187     }
188     }
189     }; # $growing_downward_growing_cells
190    
191     my $process_row = sub {
192     ## Step 1
193     $y_current++;
194    
195     ## Step 2
196     $growing_downward_growing_cells->();
197    
198     ## Step 3
199     my $x_current = 1;
200    
201     ## Step 4
202     my $tr = shift;
203     my @tdth = grep {
204     $_->node_type == 1 and
205     defined $_->namespace_uri and
206     $_->namespace_uri eq q<http://www.w3.org/1999/xhtml> and
207     {td => 1, th => 1}->{$_->manakai_local_name}
208     } @{$tr->child_nodes};
209     #return unless @tdth; # redundant with |while| below
210    
211     ## Step 5, 16, 17, 18
212     ## ISSUE: Step 18 says "step 5 (cells)" while "cells" is step 6.
213     while (@tdth) {
214     my $current_cell = shift @tdth;
215    
216     ## Step 6: cells
217     $x_current++
218     while ($x_current <= $x_max and
219     $table->{cell}->[$x_current]->[$y_current]);
220    
221     ## Step 7
222     if ($x_current > $x_max) {
223     $x_max++;
224     }
225    
226     ## Step 8
227     ## ISSUE: How to parse |colspan| is not explicitly specified
228     ## (while |span| was).
229     my $colspan = 1;
230     my $attr_value = $current_cell->get_attribute_ns (undef, 'colspan');
231     if (defined $attr_value and $attr_value =~ /^[\x09-\x0D\x20]*([0-9]+)/) {
232     $colspan = $1 || 1;
233     }
234    
235     ## Step 9
236     my $rowspan = 1;
237     ## ISSUE: How to parse
238     my $attr_value = $current_cell->get_attribute_ns (undef, 'rowspan');
239     if (defined $attr_value and $attr_value =~ /^[\x09-\x0D\x20]*([0-9]+)/) {
240     $rowspan = $1;
241     }
242    
243     ## Step 10
244     my $cell_grows_downward;
245     if ($rowspan == 0) {
246     $cell_grows_downward = 1;
247     $rowspan = 1;
248     }
249    
250     ## Step 11
251     if ($x_max < $x_current + $colspan - 1) {
252     @column_generated_by[$_] = $current_cell
253     for $x_max + 1 .. $x_current + $colspan - 1;
254     $x_max = $x_current + $colspan - 1;
255     }
256    
257     ## Step 12
258     if ($y_max < $y_current + $rowspan - 1) {
259     $y_max = $y_current + $rowspan - 1;
260     $y_max_node = $current_cell;
261     }
262    
263     ## Step 13
264     my $cell = {
265 wakaba 1.2 is_header => ($current_cell->manakai_local_name eq 'th'),
266 wakaba 1.1 element => $current_cell,
267     x => $x_current, y => $y_current,
268     width => $colspan, height => $rowspan,
269     };
270     $has_anchored_cell[$x_current] = 1;
271     for my $x ($x_current .. ($x_current + $colspan - 1)) {
272     for my $y ($y_current .. ($y_current + $rowspan - 1)) {
273     unless ($table->{cell}->[$x]->[$y]) {
274     $table->{cell}->[$x]->[$y] = [$cell];
275     } else {
276     $onerror->(type => "cell overlapping:$x:$y", node => $current_cell);
277     push @{$table->{cell}->[$x]->[$y]}, $cell;
278     }
279     }
280     }
281    
282     ## Step 14
283     if ($cell_grows_downward) {
284     push @downward_growing_cells, [$cell, $x_current, $colspan];
285     }
286    
287     ## Step 15
288     $x_current += $colspan;
289     }
290     }; # $process_row
291    
292     ## Step 12: rows
293     unshift @table_child, $current_element;
294     ROWS: {
295     NEXT_CHILD: {
296     $current_element = shift @table_child;
297     if (defined $current_element) {
298     redo NEXT_CHILD unless $current_element->node_type == 1;
299     my $nsuri = $current_element->namespace_uri;
300     redo NEXT_CHILD unless defined $nsuri and
301     $nsuri eq q<http://www.w3.org/1999/xhtml>;
302     $current_ln = $current_element->manakai_local_name;
303    
304     redo NEXT_CHILD unless {
305     thead => 1,
306     tbody => 1,
307     tfoot => 1,
308     tr => 1,
309     }->{$current_ln};
310     } else {
311     ## Step 10 2nd sentense
312     if ($y_current != $y_max) {
313     $onerror->(type => 'rowspan expands table',
314     node => $y_max_node);
315     }
316     ## End of subsection
317     $check_empty_column->();
318     ## Step 5 2nd paragraph
319     return $table;
320     }
321     } # NEXT_CHILD
322    
323     ## Step 13
324     if ($current_ln eq 'tr') {
325     $process_row->($current_element);
326     redo ROWS;
327     }
328    
329     ## Step 14
330     ## Ending a row group
331     ## Step 1
332     if ($y_current < $y_max) {
333     $onerror->(type => 'rowspan expands table', node => $y_max_node);
334     }
335     ## Step 2
336     while ($y_current < $y_max) {
337     ## Step 1
338     $y_current++;
339     $growing_downward_growing_cells->();
340     }
341     ## Step 3
342     @downward_growing_cells = ();
343    
344     ## Step 15
345     my $y_start = $y_max + 1;
346    
347     ## Step 16
348     for (grep {
349     $_->node_type == 1 and
350     defined $_->namespace_uri and
351     $_->namespace_uri eq q<http://www.w3.org/1999/xhtml> and
352     $_->manakai_local_name eq 'tr'
353     } @{$current_element->child_nodes}) {
354     $process_row->($_);
355     }
356    
357     ## Step 17
358     if ($y_max >= $y_start) {
359     my $rg = {element => $current_element,
360     x => 1, y => $y_start,
361     height => $y_max - $y_start + 1};
362     $table->{row_group}->[$_] = $rg for $y_start .. $y_max;
363     }
364    
365     ## Step 18
366     ## Ending a row group
367     ## Step 1
368     if ($y_current < $y_max) {
369     $onerror->(type => 'rowspan expands table', node => $y_max_node);
370     }
371     ## Step 2
372     while ($y_current < $y_max) {
373     ## Step 1
374     $y_current++;
375     $growing_downward_growing_cells->();
376     }
377     ## Step 3
378     @downward_growing_cells = ();
379    
380     ## Step 19
381     redo ROWS; # Step 12
382     } # ROWS
383     } # form_table
384    
385     ## TODO: Implement scope="" algorithm
386    
387     1;
388 wakaba 1.2 ## $Date: 2007/05/26 16:33:53 $

admin@suikawiki.org
ViewVC Help
Powered by ViewVC 1.1.24