/[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.1 - (show annotations) (download)
Sat May 26 16:33:53 2007 UTC (19 years, 2 months ago) by wakaba
Branch: MAIN
++ whatpm/Whatpm/ChangeLog	26 May 2007 16:33:44 -0000
2007-05-27  Wakaba  <wakaba@suika.fam.cx>

	* HTMLTable.pm: New module.

	* ContentChecker.pm (table): Invoke table model error checker.

	* NanoDOM.pm (first_child, get_attribute_ns): New.

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

[email protected]
ViewVC Help
Powered by ViewVC 1.1.24