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   138 use strict;
  25         40  
  25         578  
4 25     25   96 use warnings;
  25         40  
  25         575  
5              
6 25     25   102 use HTML::DOM::Exception qw 'HIERARCHY_REQUEST_ERR INDEX_SIZE_ERR';
  25         40  
  25         63918  
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.058';
14              
15             sub caption {
16 13     13 1 51 my $old = ((my $self = shift)->content_list)[0];
17 13 100 100     39 undef $old unless $old and $old->tag eq 'caption';
18 13 100       27 if(@_) {
19 4         6 my $new = shift;
20 4   50     5 my $tag = (eval{$new->tag}||'');
21 4 50       18 $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       6 if ($old) {
26 1         7 $self->replaceChild($new, $old);
27             } else {
28 2         11 $self->unshift_content($new)
29             }
30             }
31 12   100     55 return $old || ();
32             }
33             sub tHead {
34 32     32 1 51 my $self = shift;
35 32         70 for($self->content_list) {
36 42         73 (my $tag = tag $_);
37 42 100       128 if($tag =~ /^t(?:head|body|foot)\z/) {
38 28 100       52 if(@_) {
39 3         4 my $new = shift;
40 3   50     5 my $new_tag = (eval{$new->tag}||'');
41 3 50       11 $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         4 $_->${\qw[preinsert replace_with][$tag eq 'thead']}(
  2         10  
48             $new
49             );
50 2         5 $self->ownerDocument->_modified;
51             }
52 27 100       93 return $tag eq 'thead' ? $_:();
53             }
54             }
55 4 50       11 @_ and $self->appendChild(shift);
56 4         9 return;
57             }
58             sub tFoot {
59 32     32 1 50 my $self = shift;
60 32         60 for($self->content_list) {
61 56         81 (my $tag = tag $_);
62 56 100       168 if($tag =~ /^t(?:body|foot)\z/) {
63 28 100       48 if(@_) {
64 3         4 my $new = shift;
65 3   50     4 my $new_tag = (eval{$new->tag}||'');
66 3 50       10 $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         9  
73             $new
74             );
75 2         4 $self->ownerDocument->_modified;
76             }
77 27 100       101 return $tag eq 'tfoot' ? $_ : ();
78             }
79             }
80 4 50       8 @_ and $self->appendChild(shift);
81 4         22 return;
82             }
83             sub rows { # ~~~ I need to make this cache the resulting collection obj
84 16     16 1 22 my $self = shift;
85 16 100       30 if (wantarray) {
86             # I need a grep in order to exclude text nodes.
87 2         7 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   47 sub { grep tag $_ eq 'tr', map $_->content_list,
94             map $self->$_, qw/ tHead tBodies tFoot /; }
95 14         75 ));
96 14         33 $self->ownerDocument-> _register_magic_node_list($list);
97 14         242 $collection;
98             }
99             }
100             sub tBodies { # ~~~ I need to make this cache the resulting collection obj
101 26     26 1 38 my $self = shift;
102 26 100       45 if (wantarray) {
103 21         33 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   11 sub { grep tag $_ eq 'tbody', $self->content_list }
109 5         26 ));
110 5         11 $self->ownerDocument-> _register_magic_node_list($list);
111 5         72 $collection;
112             }
113             }
114 30     30 1 3156 sub align { lc shift->_attr('align' => @_) }
115 15     15 1 2564 sub bgColor { shift->_attr('bgcolor' => @_) }
116 5     5 1 858 sub border { shift->_attr( border => @_) }
117 5     5 1 849 sub cellPadding { shift->_attr('cellpadding' => @_) }
118 5     5 1 846 sub cellSpacing { shift->_attr('cellspacing' => @_) }
119 5     5 1 880 sub frame { shift->_attr('frame' => @_) }
120 5     5 1 847 sub rules { lc shift->_attr('rules' => @_) }
121 5     5 1 848 sub summary { shift->_attr('summary' => @_) }
122 10     10 1 1709 sub width { shift->_attr('width' => @_) }
123              
124             sub createTHead {
125 2     2 1 4 my $self = shift;
126 2         6 my $th = $self->tHead;
127 2 100       7 $th and return $th;
128              
129 1         3 my $inserted;
130 1         3 $th = $self->ownerDocument->createElement('thead');
131 1         3 for($self->content_list) {
132 1 50       3 next if tag $_ =~ /^c(?:aption|ol(?:group)?)\z/;
133 1         3 $_->preinsert($th), ++$inserted,
134             $self->ownerDocument->_modified, last
135             }
136 1 50       4 $self->appendChild($th) unless $inserted;
137              
138 1         6 $th
139             }
140              
141             sub deleteTHead {
142 1     1 1 2 my $self = shift;
143 1   50     3 ($self->tHead||return)->delete; # ~~~ once I weaken upward refs, should I make this less destructive?
144 1         5 $self->ownerDocument->_modified;
145 1         4 return;
146             }
147              
148             sub createTFoot {
149 2     2 1 5 my $self = shift;
150 2         5 my $tf = $self->tFoot;
151 2 100       8 $tf and return $tf;
152              
153 1         2 my $inserted;
154 1         3 $tf = $self->ownerDocument->createElement('tfoot');
155 1         3 for($self->content_list) {
156 2 100       5 next if tag $_ =~ /^(?:c(?:aption|ol(?:group)?)|thead)\z/;
157 1         3 $_->preinsert($tf), ++$inserted,
158             $self->ownerDocument->_modified, last
159             }
160 1 50       3 $self->appendChild($tf) unless $inserted;
161              
162 1         3 $tf
163             }
164              
165             sub deleteTFoot {
166 1     1 1 2 my $self = shift;
167 1   50     3 ($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 4 my $self = shift; my $th;
  2         3  
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 3 my $self = shift;
183 1   50     2 ($self->caption||return)->delete; # ~~~ once I weaken upward refs, should I make this less destructive?
184 1         4 $self->ownerDocument->_modified;
185 1         4 return;
186             }
187              
188             sub insertRow {
189 9     9 1 21 my $self = shift;
190 9         13 my $ix = shift;
191 9         16 my $len = (my $rows = $self->rows)->length;
192 9         22 my $row = $self->ownerDocument->createElement('tr');
193 9 100 100     37 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         14 (my $tb = $self->ownerDocument
199             ->createElement('tbody'))
200             ->appendChild($row);
201 2         6 $self->appendChild($tb);
202             }
203             }
204             elsif($ix == -1 || $ix == $len) {
205 2         36 $rows->item(-1)->postinsert(
206             $row
207             );
208 2         3 $self->ownerDocument->_modified;
209             }
210             elsif($ix < $len && $ix >= 0) {
211 3         57 $rows->item($ix)->preinsert($row);
212 3         8 $self->ownerDocument->_modified
213             }
214             else {
215 2         9 die new HTML::DOM::Exception INDEX_SIZE_ERR,
216             "Index $ix is out of range"
217             }
218              
219 7         47 return $row;
220             }
221              
222             sub deleteRow {
223 2     2 1 12 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         12 $self->ownerDocument->_modified;
226 2         9 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.058
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.058';
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.058';
363             our @ISA = 'HTML::DOM::Element';
364             *align = \&HTML::DOM::Element::Table::align;
365 20     20   4399 sub ch { shift->_attr('char' => @_) }
366 20     20   4436 sub chOff { shift->_attr( charoff => @_) }
367 5     5   1454 sub span { shift->_attr('span' => @_) }
368 20     20   4421 sub vAlign { lc shift->_attr('valign' => @_) }
369 5     5   1381 sub width { shift->_attr('width' => @_) }
370              
371             # ------- HTMLTableSectionElement interface ---------- #
372              
373             package HTML::DOM::Element::TableSection;
374             our $VERSION = '0.058';
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   557 my $self = shift;
382 15 100       29 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         56 ));
391 12         32 $self->ownerDocument-> _register_magic_node_list($list);
392 12         197 $collection;
393             }
394             }
395             sub insertRow {
396 7     7   19 my $self = shift;
397 7   100     16 my $ix = shift||0;
398 7         11 my $len = (my $rows = $self->rows)->length;
399 7         13 my $row = $self->ownerDocument->createElement('tr');
400 7 100 100     28 if(!$len) {
    100 100        
    100          
401 1         4 $self->appendChild($row);
402             }
403             elsif($ix == -1 || $ix == $len) {
404 2         35 $rows->item(-1)->postinsert(
405             $row
406             );
407 2         5 $self->ownerDocument->_modified;
408             }
409             elsif($ix < $len && $ix >= 0) {
410 2         36 $rows->item($ix)->preinsert($row);
411 2         5 $self->ownerDocument->_modified;
412             }
413             else {
414 2         10 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.058';
428             our @ISA = 'HTML::DOM::Element';
429             sub rowIndex {
430 1     1   2 my $self = shift;
431 1         3 my $ix = 0;
432 1         10 for($self->look_up(_tag => 'table')->rows){
433 2 100       8 return $ix if $self == $_;
434 1         2 $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   2 my $self = shift;
442 1         4 my $parent = $self->parent;
443 1         5 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         2 my $ix = 0;
449 1         3 for($parent->rows){
450 1 50       5 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   16 my $self = shift;
459 11 100       35 if (wantarray) {
460             # I need a grep in order to exclude text nodes.
461 3         8 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   32 sub { grep tag $_ =~ /^t[hd]\z/, $self->content_list; }
467 8         40 ));
468 8         18 $self->ownerDocument-> _register_magic_node_list($list);
469 8         150 $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   9 my $self = shift;
479 6   100     15 my $ix = shift||0;
480 6         12 my $len = (my $cels = $self->cells)->length;
481 6         16 my $cel = $self->ownerDocument->createElement('td');
482 6 50 100     29 if(!$len) {
    100 100        
    100          
483 0         0 $self->appendChild($cel);
484             }
485             elsif($ix == -1 || $ix == $len) {
486 2         35 $cels->item(-1)->postinsert(
487             $cel
488             );
489 2         5 $self->ownerDocument->_modified;
490             }
491             elsif($ix < $len && $ix >= 0) {
492 2         37 $cels->item($ix)->preinsert($cel);
493 2         4 $self->ownerDocument->_modified;
494             }
495             else {
496 2         160 die new HTML::DOM::Exception
497             HTML::DOM::Exception::INDEX_SIZE_ERR,
498             "Index $ix is out of range"
499             }
500              
501 4         27 return $cel;
502             }
503             sub deleteCell {
504 1     1   7 my $self = shift;
505 1   50     3 ($self->cells->item(shift)||return)->delete; # ~~~ once I weaken upward refs, should I make this less destructive?
506 1         6 $self->ownerDocument->_modified;
507 1         5 return;
508             }
509              
510             # ------- HTMLTableCellElement interface ---------- #
511              
512             package HTML::DOM::Element::TableCell;
513             our $VERSION = '0.058';
514             our @ISA = 'HTML::DOM::Element';
515             sub cellIndex {
516 1     1   6 my $self = shift;
517 1         2 my $ix = 0;
518 1         2 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   442 sub abbr { shift->_attr('abbr' => @_) }
527             *align = \&HTML::DOM::Element::Table::align;
528 5     5   849 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   845 sub colSpan { shift->_attr('colspan' => @_) }
533 5     5   858 sub headers { shift->_attr('headers' => @_) }
534 5     5   850 sub height { shift->_attr('height' => @_) }
535 6 100   6   70 sub noWrap { shift->_attr(nowrap => @_ ? $_[0] ? 'nowrap' : undef : ()) }
    100          
536 5     5   847 sub rowSpan { shift->_attr('rowspan' => @_) }
537 5     5   868 sub scope { lc shift->_attr('scope' => @_) }
538             *vAlign = \&HTML::DOM::Element::TableColumn::vAlign;
539             *width = \&HTML::DOM::Element::Table::width;
540