File Coverage

blib/lib/HTML/DOM/Element/Table.pm
Criterion Covered Total %
statement 192 200 96.0
branch 74 86 86.0
condition 36 44 81.8
subroutine 50 50 100.0
pod 22 22 100.0
total 374 402 93.0


line stmt bran cond sub pod time code
1             package HTML::DOM::Element::Table;
2              
3 25     25   89 use strict;
  25         25  
  25         572  
4 25     25   73 use warnings;
  25         26  
  25         542  
5              
6 25     25   78 use HTML::DOM::Exception qw 'HIERARCHY_REQUEST_ERR INDEX_SIZE_ERR';
  25         27  
  25         54782  
7              
8             require HTML::DOM::Collection;
9             require HTML::DOM::Element;
10             #require HTML::DOM::NodeList::Magic;
11              
12             our @ISA = qw'HTML::DOM::Element';
13             our $VERSION = '0.057';
14              
15             sub caption {
16 13     13 1 54 my $old = ((my $self = shift)->content_list)[0];
17 13 100 100     166 undef $old unless $old and $old->tag eq 'caption';
18 13 100       28 if(@_) {
19 4         4 my $new = shift;
20 4   50     6 my $tag = (eval{$new->tag}||'');
21 4 50       17 $tag eq 'caption' or die new HTML'DOM'Exception
    100          
22             HIERARCHY_REQUEST_ERR,
23             $tag ? "A $tag element cannot be a table caption"
24             : "Not a valid table caption";
25 3 100       7 if ($old) {
26 1         7 $self->replaceChild($new, $old);
27             } else {
28 2         8 $self->unshift_content($new)
29             }
30             }
31 12   100     63 return $old || ();
32             }
33             sub tHead {
34 32     32 1 61 my $self = shift;
35 32         80 for($self->content_list) {
36 42         75 (my $tag = tag $_);
37 42 100       130 if($tag =~ /^t(?:head|body|foot)\z/) {
38 28 100       44 if(@_) {
39 3         3 my $new = shift;
40 3   50     3 my $new_tag = (eval{$new->tag}||'');
41 3 50       10 $new_tag eq 'thead' or die
    100          
42             new HTML'DOM'Exception
43             HIERARCHY_REQUEST_ERR,
44             $tag
45             ? "A $new_tag element cannot be a table header"
46             : "Not a valid table header";
47 2         3 $_->${\qw[preinsert replace_with][$tag eq 'thead']}(
  2         10  
48             $new
49             );
50 2         4 $self->ownerDocument->_modified;
51             }
52 27 100       113 return $tag eq 'thead' ? $_:();
53             }
54             }
55 4 50       11 @_ and $self->appendChild(shift);
56 4         10 return;
57             }
58             sub tFoot {
59 32     32 1 38 my $self = shift;
60 32         63 for($self->content_list) {
61 56         89 (my $tag = tag $_);
62 56 100       148 if($tag =~ /^t(?:body|foot)\z/) {
63 28 100       43 if(@_) {
64 3         4 my $new = shift;
65 3   50     4 my $new_tag = (eval{$new->tag}||'');
66 3 50       12 $new_tag eq 'tfoot' or die
    100          
67             new HTML'DOM'Exception
68             HIERARCHY_REQUEST_ERR,
69             $tag
70             ? "A $new_tag element cannot be a table footer"
71             : "Not a valid table footer";
72 2         3 $_->${\qw[preinsert replace_with][$tag eq 'tfoot']}(
  2         7  
73             $new
74             );
75 2         5 $self->ownerDocument->_modified;
76             }
77 27 100       108 return $tag eq 'tfoot' ? $_ : ();
78             }
79             }
80 4 50       10 @_ and $self->appendChild(shift);
81 4         17 return;
82             }
83             sub rows { # ~~~ I need to make this cache the resulting collection obj
84 16     16 1 18 my $self = shift;
85 16 100       31 if (wantarray) {
86             # I need a grep in order to exclude text nodes.
87 2         9 return grep tag $_ eq 'tr', map $_->content_list,
88             map $self->$_, qw/ tHead tBodies tFoot /;
89             }
90             else {
91             my $collection = HTML::DOM::Collection->new(
92             my $list = HTML::DOM::NodeList::Magic->new(
93 18     18   43 sub { grep tag $_ eq 'tr', map $_->content_list,
94             map $self->$_, qw/ tHead tBodies tFoot /; }
95 14         88 ));
96 14         38 $self->ownerDocument-> _register_magic_node_list($list);
97 14         308 $collection;
98             }
99             }
100             sub tBodies { # ~~~ I need to make this cache the resulting collection obj
101 26     26 1 33 my $self = shift;
102 26 100       41 if (wantarray) {
103 21         43 return grep tag $_ eq 'tbody', $self->content_list;
104             }
105             else {
106             my $collection = HTML::DOM::Collection->new(
107             my $list = HTML::DOM::NodeList::Magic->new(
108 5     5   15 sub { grep tag $_ eq 'tbody', $self->content_list }
109 5         28 ));
110 5         12 $self->ownerDocument-> _register_magic_node_list($list);
111 5         98 $collection;
112             }
113             }
114 30     30 1 2882 sub align { lc shift->_attr('align' => @_) }
115 15     15 1 2378 sub bgColor { shift->_attr('bgcolor' => @_) }
116 5     5 1 804 sub border { shift->_attr( border => @_) }
117 5     5 1 777 sub cellPadding { shift->_attr('cellpadding' => @_) }
118 5     5 1 798 sub cellSpacing { shift->_attr('cellspacing' => @_) }
119 5     5 1 777 sub frame { shift->_attr('frame' => @_) }
120 5     5 1 828 sub rules { lc shift->_attr('rules' => @_) }
121 5     5 1 779 sub summary { shift->_attr('summary' => @_) }
122 10     10 1 1566 sub width { shift->_attr('width' => @_) }
123              
124             sub createTHead {
125 2     2 1 2 my $self = shift;
126 2         5 my $th = $self->tHead;
127 2 100       7 $th and return $th;
128              
129 1         2 my $inserted;
130 1         3 $th = $self->ownerDocument->createElement('thead');
131 1         4 for($self->content_list) {
132 1 50       3 next if tag $_ =~ /^c(?:aption|ol(?:group)?)\z/;
133 1         4 $_->preinsert($th), ++$inserted,
134             $self->ownerDocument->_modified, last
135             }
136 1 50       3 $self->appendChild($th) unless $inserted;
137              
138 1         8 $th
139             }
140              
141             sub deleteTHead {
142 1     1 1 2 my $self = shift;
143 1   50     4 ($self->tHead||return)->delete; # ~~~ once I weaken upward refs, should I make this less destructive?
144 1         9 $self->ownerDocument->_modified;
145 1         4 return;
146             }
147              
148             sub createTFoot {
149 2     2 1 3 my $self = shift;
150 2         5 my $tf = $self->tFoot;
151 2 100       7 $tf and return $tf;
152              
153 1         1 my $inserted;
154 1         3 $tf = $self->ownerDocument->createElement('tfoot');
155 1         3 for($self->content_list) {
156 2 100       4 next if tag $_ =~ /^(?:c(?:aption|ol(?:group)?)|thead)\z/;
157 1         3 $_->preinsert($tf), ++$inserted,
158             $self->ownerDocument->_modified, last
159             }
160 1 50       2 $self->appendChild($tf) unless $inserted;
161              
162 1         5 $tf
163             }
164              
165             sub deleteTFoot {
166 1     1 1 2 my $self = shift;
167 1   50     4 ($self->tFoot||return)->delete; # ~~~ once I weaken upward refs, should I make this less destructive?
168 1         4 $self->ownerDocument->_modified;
169 1         4 return;
170             }
171              
172             sub createCaption {
173 2     2 1 2 my $self = shift; my $th;
  2         1  
174 2 100       5 $self->caption or
175             $self->unshift_content($th =
176             $self->ownerDocument->createElement('caption')),
177             $self->ownerDocument->_modified,
178             $th;
179             }
180              
181             sub deleteCaption {
182 1     1 1 2 my $self = shift;
183 1   50     2 ($self->caption||return)->delete; # ~~~ once I weaken upward refs, should I make this less destructive?
184 1         6 $self->ownerDocument->_modified;
185 1         4 return;
186             }
187              
188             sub insertRow {
189 9     9 1 21 my $self = shift;
190 9         9 my $ix = shift;
191 9         19 my $len = (my $rows = $self->rows)->length;
192 9         27 my $row = $self->ownerDocument->createElement('tr');
193 9 100 100     55 if(!$len) { # worst case
    100 100        
    100          
194 2 50       4 if(my $tb = $self->tBodies->item(0)) {
195 0         0 $tb->appendChild($row);
196             }
197             else {
198 2         9 (my $tb = $self->ownerDocument
199             ->createElement('tbody'))
200             ->appendChild($row);
201 2         7 $self->appendChild($tb);
202             }
203             }
204             elsif($ix == -1 || $ix == $len) {
205 2         59 $rows->item(-1)->postinsert(
206             $row
207             );
208 2         5 $self->ownerDocument->_modified;
209             }
210             elsif($ix < $len && $ix >= 0) {
211 3         71 $rows->item($ix)->preinsert($row);
212 3         7 $self->ownerDocument->_modified
213             }
214             else {
215 2         12 die new HTML::DOM::Exception INDEX_SIZE_ERR,
216             "Index $ix is out of range"
217             }
218              
219 7         53 return $row;
220             }
221              
222             sub deleteRow {
223 2     2 1 10 my $self = shift;
224 2   50     5 ($self->rows->item(shift)||return)->delete; # ~~~ once I weaken upward refs, should I make this less destructive?
225 2         11 $self->ownerDocument->_modified;
226 2         8 return;
227             }
228              
229              
230             =head1 NAME
231              
232             HTML::DOM::Element::Table - A Perl class for representing 'table' elements in an HTML DOM tree
233              
234             =head1 VERSION
235              
236             Version 0.057
237              
238             =head1 SYNOPSIS
239              
240             use HTML::DOM;
241             $doc = HTML::DOM->new;
242             $elem = $doc->createElement('table');
243              
244             $elem->tHead;
245             $elem->tBodies->[0];
246             $elem->createTFoot;
247             # etc
248              
249             =head1 DESCRIPTION
250              
251             This class represents 'table' elements in an HTML::DOM tree. It implements the HTMLTableElement DOM interface and inherits from L
252             (q.v.).
253              
254             =head1 METHODS
255              
256             In addition to those inherited from HTML::DOM::Element and its
257             superclasses, this class implements the following DOM methods:
258              
259             =over 4
260              
261             =item caption
262              
263             =item tHead
264              
265             =item tFoot
266              
267             Each of these returns the table's corresponding element, if it exists, or
268             an empty list otherwise.
269              
270             =item rows
271              
272             Returns a collection of all table row elements, or a list in list context.
273              
274             =item tBodies
275              
276             Returns a collection of all 'tbody' elements, or a list in list context.
277              
278             =item align
279              
280             =item bgColor
281              
282             =item border
283              
284             =item cellPadding
285              
286             =item cellSpacing
287              
288             =item frame
289              
290             =item rules
291              
292             =item summary
293              
294             =item width
295              
296             These get (optionally set) the corresponding HTML attributes.
297              
298             =item createTHead
299              
300             Returns the table's 'thead' element, creating it if it doesn't exist.
301              
302             =item deleteTHead
303              
304             Deletes the table's 'thead' element.
305              
306             =item createTFoot
307              
308             Returns the table's 'tfoot' element, creating it if it doesn't exist.
309              
310             =item deleteTFoot
311              
312             Does what you would think.
313              
314             =item createCaption
315              
316             Returns the table's 'caption' element, creating it if it doesn't exist.
317              
318             =item deleteCaption
319              
320             Deletes the caption.
321              
322             =item insertRow
323              
324             Insert a new 'tr' element at the index specified by the first argument, and
325             returns that new row.
326              
327             =item deleteRow
328              
329             Deletes the row at the index specified by the first arg.
330              
331             =back
332              
333             =head1 SEE ALSO
334              
335             L
336              
337             L
338              
339             L
340              
341             L
342              
343             L
344              
345             L
346              
347             L
348              
349             =cut
350              
351              
352             # ------- HTMLTableCaptionElement interface ---------- #
353              
354             package HTML::DOM::Element::Caption;
355             our $VERSION = '0.057';
356             our @ISA = 'HTML::DOM::Element';
357             *align = \&HTML::DOM::Element::Table::align;
358              
359             # ------- HTMLTableColElement interface ---------- #
360              
361             package HTML::DOM::Element::TableColumn;
362             our $VERSION = '0.057';
363             our @ISA = 'HTML::DOM::Element';
364             *align = \&HTML::DOM::Element::Table::align;
365 20     20   3764 sub ch { shift->_attr('char' => @_) }
366 20     20   3736 sub chOff { shift->_attr( charoff => @_) }
367 5     5   1151 sub span { shift->_attr('span' => @_) }
368 20     20   3775 sub vAlign { lc shift->_attr('valign' => @_) }
369 5     5   1127 sub width { shift->_attr('width' => @_) }
370              
371             # ------- HTMLTableSectionElement interface ---------- #
372              
373             package HTML::DOM::Element::TableSection;
374             our $VERSION = '0.057';
375             our @ISA = 'HTML::DOM::Element';
376             *align = \&HTML::DOM::Element::Table::align;
377             *ch = \&HTML::DOM::Element::TableColumn::ch;
378             *chOff = \&HTML::DOM::Element::TableColumn::chOff;
379             *vAlign = \&HTML::DOM::Element::TableColumn::vAlign;
380             sub rows { # ~~~ I need to make this cache the resulting collection obj
381 15     15   288 my $self = shift;
382 15 100       25 if (wantarray) {
383             # I need a grep in order to exclude text nodes.
384 3         9 return grep tag $_ eq 'tr', $self->content_list,
385             }
386             else {
387             my $collection = HTML::DOM::Collection->new(
388             my $list = HTML::DOM::NodeList::Magic->new(
389 16     16   35 sub { grep tag $_ eq 'tr', $self->content_list; }
390 12         60 ));
391 12         26 $self->ownerDocument-> _register_magic_node_list($list);
392 12         241 $collection;
393             }
394             }
395             sub insertRow {
396 7     7   16 my $self = shift;
397 7   100     19 my $ix = shift||0;
398 7         12 my $len = (my $rows = $self->rows)->length;
399 7         17 my $row = $self->ownerDocument->createElement('tr');
400 7 100 100     42 if(!$len) {
    100 100        
    100          
401 1         3 $self->appendChild($row);
402             }
403             elsif($ix == -1 || $ix == $len) {
404 2         41 $rows->item(-1)->postinsert(
405             $row
406             );
407 2         4 $self->ownerDocument->_modified;
408             }
409             elsif($ix < $len && $ix >= 0) {
410 2         42 $rows->item($ix)->preinsert($row);
411 2         5 $self->ownerDocument->_modified;
412             }
413             else {
414 2         12 die new HTML::DOM::Exception
415             HTML::DOM::Exception::INDEX_SIZE_ERR,
416             "Index $ix is out of range"
417             }
418              
419 5         28 return $row;
420             }
421              
422             *deleteRow = \&HTML::DOM::Element::Table::deleteRow;
423              
424             # ------- HTMLTableRowElement interface ---------- #
425              
426             package HTML::DOM::Element::TR;
427             our $VERSION = '0.057';
428             our @ISA = 'HTML::DOM::Element';
429             sub rowIndex {
430 1     1   3 my $self = shift;
431 1         3 my $ix = 0;
432 1         13 for($self->look_up(_tag => 'table')->rows){
433 2 100       12 return $ix if $self == $_;
434 1         4 $ix++
435             }
436 0         0 die "Internal error in HTML::DOM::Element::TR::rowIndex: " .
437             "This table row is not inside the table it is inside. " .
438             "Please report this bug."
439             }
440             sub sectionRowIndex {
441 1     1   3 my $self = shift;
442 1         5 my $parent = $self->parent;
443 1         10 while(!$parent->isa('HTML::DOM::Element::TableSection')) {
444             # If we get here, there is probably something wrong, should
445             # I just throw an error instead?
446 0         0 $parent = $parent->parent;
447             }
448 1         11 my $ix = 0;
449 1         4 for($parent->rows){
450 1 50       10 return $ix if $self == $_;
451 0         0 $ix++
452             }
453 0         0 die "Internal error in HTML::DOM::Element::TR::sectionRowIndex: " .
454             "This table row is not inside the table section it is " .
455             "inside. Please report this bug."
456             }
457             sub cells { # ~~~ I need to make this cache the resulting collection obj
458 11     11   15 my $self = shift;
459 11 100       23 if (wantarray) {
460             # I need a grep in order to exclude text nodes.
461 3         11 return grep tag $_ =~ /^t[hd]\z/, $self->content_list,
462             }
463             else {
464             my $collection = HTML::DOM::Collection->new(
465             my $list = HTML::DOM::NodeList::Magic->new(
466 14     14   42 sub { grep tag $_ =~ /^t[hd]\z/, $self->content_list; }
467 8         51 ));
468 8         27 $self->ownerDocument-> _register_magic_node_list($list);
469 8         191 $collection;
470             }
471             }
472             *align = \&HTML::DOM::Element::Table::align;
473             *bgColor = \&HTML::DOM::Element::Table::bgColor;
474             *ch = \&HTML::DOM::Element::TableColumn::ch;
475             *chOff = \&HTML::DOM::Element::TableColumn::chOff;
476             *vAlign = \&HTML::DOM::Element::TableColumn::vAlign;
477             sub insertCell {
478 6     6   11 my $self = shift;
479 6   100     16 my $ix = shift||0;
480 6         12 my $len = (my $cels = $self->cells)->length;
481 6         15 my $cel = $self->ownerDocument->createElement('td');
482 6 50 100     49 if(!$len) {
    100 100        
    100          
483 0         0 $self->appendChild($cel);
484             }
485             elsif($ix == -1 || $ix == $len) {
486 2         43 $cels->item(-1)->postinsert(
487             $cel
488             );
489 2         4 $self->ownerDocument->_modified;
490             }
491             elsif($ix < $len && $ix >= 0) {
492 2         58 $cels->item($ix)->preinsert($cel);
493 2         5 $self->ownerDocument->_modified;
494             }
495             else {
496 2         12 die new HTML::DOM::Exception
497             HTML::DOM::Exception::INDEX_SIZE_ERR,
498             "Index $ix is out of range"
499             }
500              
501 4         25 return $cel;
502             }
503             sub deleteCell {
504 1     1   6 my $self = shift;
505 1   50     4 ($self->cells->item(shift)||return)->delete; # ~~~ once I weaken upward refs, should I make this less destructive?
506 1         7 $self->ownerDocument->_modified;
507 1         4 return;
508             }
509              
510             # ------- HTMLTableCellElement interface ---------- #
511              
512             package HTML::DOM::Element::TableCell;
513             our $VERSION = '0.057';
514             our @ISA = 'HTML::DOM::Element';
515             sub cellIndex {
516 1     1   6 my $self = shift;
517 1         1 my $ix = 0;
518 1         3 for($self->parent->cells){
519 1 50       7 return $ix if $self == $_;
520 0         0 $ix++
521             }
522 0         0 die "Internal error in HTML::DOM::Element::TR::rowIndex: " .
523             "This table row is not inside the table it is inside. " .
524             "Please report this bug."
525             }
526 5     5   410 sub abbr { shift->_attr('abbr' => @_) }
527             *align = \&HTML::DOM::Element::Table::align;
528 5     5   829 sub axis { shift->_attr('axis' => @_) }
529             *bgColor = \&HTML::DOM::Element::Table::bgColor;
530             *ch = \&HTML::DOM::Element::TableColumn::ch;
531             *chOff = \&HTML::DOM::Element::TableColumn::chOff;
532 5     5   801 sub colSpan { shift->_attr('colspan' => @_) }
533 5     5   779 sub headers { shift->_attr('headers' => @_) }
534 5     5   774 sub height { shift->_attr('height' => @_) }
535 6 100   6   59 sub noWrap { shift->_attr(nowrap => @_ ? $_[0] ? 'nowrap' : undef : ()) }
    100          
536 5     5   797 sub rowSpan { shift->_attr('rowspan' => @_) }
537 5     5   778 sub scope { lc shift->_attr('scope' => @_) }
538             *vAlign = \&HTML::DOM::Element::TableColumn::vAlign;
539             *width = \&HTML::DOM::Element::Table::width;
540