File Coverage

lib/HTML/Object/DOM/Element/Table.pm
Criterion Covered Total %
statement 209 288 72.5
branch 56 118 47.4
condition 33 79 41.7
subroutine 41 48 85.4
pod 25 25 100.0
total 364 558 65.2


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## HTML Object - ~/lib/HTML/Object/DOM/Element/Table.pm
3             ## Version v0.2.1
4             ## Copyright(c) 2022 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest <jack@deguest.jp>
6             ## Created 2021/12/23
7             ## Modified 2022/09/20
8             ## All rights reserved
9             ##
10             ##
11             ## This program is free software; you can redistribute it and/or modify it
12             ## under the same terms as Perl itself.
13             ##----------------------------------------------------------------------------
14             package HTML::Object::DOM::Element::Table;
15             BEGIN
16             {
17 2     2   3318 use strict;
  2         4  
  2         62  
18 2     2   10 use warnings;
  2         5  
  2         56  
19 2     2   12 use parent qw( HTML::Object::DOM::Element );
  2         4  
  2         9  
20 2     2   134 use vars qw( $VERSION );
  2         4  
  2         80  
21 2     2   489 use HTML::Object::DOM::Element::Shared qw( :table );
  2         4  
  2         409  
22 2     2   15 use Want;
  2         5  
  2         113  
23 2     2   37 our $VERSION = 'v0.2.1';
24             };
25              
26 2     2   10 use strict;
  2         4  
  2         46  
27 2     2   10 use warnings;
  2         4  
  2         6140  
28              
29             sub init
30             {
31 1     1 1 91 my $self = shift( @_ );
32 1         169 $self->{_init_strict_use_sub} = 1;
33 1 50       11 $self->SUPER::init( @_ ) || return( $self->pass_error );
34 1 50 33     10 $self->{tag} = 'table' if( !defined( $self->{tag} ) || !CORE::length( "$self->{tag}" ) );
35 1         13 $self->{_table_reset_caption} = 1;
36 1         3 $self->{_table_reset_tbody} = 1;
37 1         3 $self->{_table_reset_tfoot} = 1;
38 1         4 $self->{_table_reset_thead} = 1;
39 1         2 $self->{_table_reset_rows} = 1;
40 1         4 $self->{_reset_fields} = [qw( _table_reset_caption _table_reset_tbody _table_reset_tfoot _table_reset_thead _table_reset_rows )];
41             my $callback = sub
42             {
43 16     16   2483 my $def = shift( @_ );
44             # my $info = [caller(5)];
45             # print( STDERR ref( $self ), "::children->callback(add): called from package ", $info->[0], " at line ", $info->[2], " for '", $def->{added}->[0], "'\n" );
46            
47             # Our children were modified from outside our package.
48             # We need to check if it affects our rows and reset the cache accordingly
49 16 100       118 unless( $def->{caller}->[0] eq ref( $self ) )
50             {
51             # my $has_row = 0;
52             # for( @{$def->{data}} )
53             # {
54             # $has_row++, last if( $self->_is_a( $_ => 'HTML::Object::DOM::Element::TableRow' ) );
55             # }
56             # $self->_reset_table( 'rows' ) if( $has_row );
57 12         55 $self->_reset_table( 'all' );
58             }
59 16         123 return(1);
60 1         5 };
61 1         9 $self->children->callback( add => $callback );
62 1         257 $self->children->callback( remove => $callback );
63 1         115 return( $self );
64             }
65              
66             # Note: deprecated property align is inherited
67              
68             # Note: deprecated property bgColor
69 0     0 1 0 sub bgColor : lvalue { return( shift->_set_get_property( 'bgcolor', @_ ) ); }
70              
71             # Note: deprecated property border
72 0     0 1 0 sub border : lvalue { return( shift->_set_get_property( 'border', @_ ) ); }
73              
74             # Note: property caption
75 4     4 1 1274 sub caption : lvalue { return( shift->_set_get_section( caption => 'HTML::Object::DOM::Element::TableCaption', @_ ) ); }
76              
77             # Note: deprecated property cellPadding
78 0     0 1 0 sub cellPadding : lvalue { return( shift->_set_get_property( 'cellpadding', @_ ) ); }
79              
80             # Note: deprecated property cellSpacing
81 0     0 1 0 sub cellSpacing : lvalue { return( shift->_set_get_property( 'cellspacing', @_ ) ); }
82              
83             sub createCaption
84             {
85 1     1 1 1374 my $self = shift( @_ );
86 1 0 33     8 if( $self->{_table_captions} && !$self->_is_table_reset( 'caption' ) && !$self->{_table_captions}->is_empty )
      33        
87             {
88 0         0 return( $self->{_table_captions}->first );
89             }
90              
91 1     11   5 my $list = $self->children->grep(sub{ $self->_is_a( $_ => 'HTML::Object::DOM::Element::TableCaption' ) });
  11         450  
92 1         116 my $capt = $list->first;
93 1 50       72 if( !$capt )
94             {
95 0 0       0 $self->_load_class( 'HTML::Object::DOM::Element::TableCaption' ) ||
96             return( $self->pass_error );
97 0   0     0 $capt = HTML::Object::DOM::Element::TableCaption->new( @_ ) ||
98             return( $self->pass_error( HTML::Object::DOM::Element::TableCaption->error ) );
99 0         0 $capt->close;
100 0         0 $capt->parent( $self );
101 0         0 $self->children->unshift( $capt );
102 0         0 $list->unshift( $capt );
103 0         0 $self->reset(1);
104 0 0       0 $self->_load_class( 'HTML::Object::DOM::Collection' ) ||
105             return( $self->pass_error );
106 0         0 $self->{_table_captions} = HTML::Object::DOM::Collection->new( $list );
107 0         0 $self->_remove_table_reset( 'caption' );
108             }
109 1         6 return( $capt );
110             }
111              
112 1     1 1 41302 sub createTBody { return( shift->_create_tsection( tbody => @_ ) ); }
113              
114 1     1 1 41909 sub createTFoot { return( shift->_create_tsection( tfoot => @_ ) ); }
115              
116 1     1 1 750 sub createTHead { return( shift->_create_tsection( thead => @_ ) ); }
117              
118 1     1 1 734 sub deleteCaption { return( shift->_delete_first_element( caption => @_ ) ); }
119              
120             sub deleteRow
121             {
122 1     1 1 42375 my $self = shift( @_ );
123 1         5 my $pos = shift( @_ );
124 1 50 0     19 return( $self->error({
      33        
125             message => "Value provided (" . overload::StrVal( $pos // '' ) . ") is not an integer.",
126             class => 'HTML::Object::IndexSizeError',
127             }) ) if( !defined( $pos ) || !$self->_is_integer( $pos ) );
128 1         26 my $rows = $self->rows;
129 1         6 my $size = $rows->size;
130 1 50       40228 return( $self->error({
131             message => "Value provided ($pos) is greater than the zero-based number of rows available (" . $rows->size . ").",
132             class => 'HTML::Object::IndexSizeError',
133             }) ) if( $pos > $size );
134 1 50 33     161 return( $self->error({
135             message => "Value provided ($pos) is lower than the zero-based number of rows available (" . $rows->size . "). If you want to specify a negative index, it must be between -1 and -${size}",
136             class => 'HTML::Object::IndexSizeError',
137             }) ) if( $pos < 0 && abs( $pos ) > $size );
138 1 50       105 $pos = ( $rows->length + $pos ) if( $pos < 0 );
139 1         41452 my $elem = $rows->index( $pos );
140 1         211 my $parent = $elem->parent;
141 1         37 my $children = $parent->children;
142 1         91 my $kid_pos = $children->pos( $elem );
143 1 50       229 return( $self->error({
144             message => "Unable to find the row element for index $pos",
145             class => 'HTML::Object::HierarchyRequestError',
146             }) ) if( !defined( $kid_pos ) );
147 1         8 my $rv = $children->splice( $kid_pos, 1 );
148 1         15 $elem->parent( undef );
149 1         30 $parent->reset(1);
150 1         6 $self->_reset_table( 'rows' );
151 1         7 return( $elem );
152             }
153              
154 1     1 1 42825 sub deleteTFoot { return( shift->_delete_first_element( tfoot => @_ ) ); }
155              
156 1     1 1 41480 sub deleteTHead { return( shift->_delete_first_element( thead => @_ ) ); }
157              
158             # Note: deprecated property frame
159 0     0 1 0 sub frame : lvalue { return( shift->_set_get_property( 'frame', @_ ) ); }
160              
161             sub insertRow
162             {
163 1     1 1 41701 my $self = shift( @_ );
164 1         3 my $pos = shift( @_ );
165 1         6 my $rows = $self->rows;
166 1         19 my $size = $rows->size;
167 1 50       40110 if( defined( $pos ) )
168             {
169 0 0 0     0 return( $self->error({
170             message => "Value provided (" . overload::StrVal( $pos // '' ) . ") is not an integer.",
171             class => 'HTML::Object::IndexSizeError',
172             }) ) if( !$self->_is_integer( $pos ) );
173 0 0       0 return( $self->error({
174             message => "Value provided ($pos) is greater than the zero-based number of rows available (" . $rows->size . ").",
175             class => 'HTML::Object::IndexSizeError',
176             }) ) if( $pos > $size );
177 0 0 0     0 return( $self->error({
178             message => "Value provided ($pos) is lower than the zero-based number of rows available (" . $rows->size . "). If you want to specify a negative index, it must be between -1 and -${size}",
179             class => 'HTML::Object::IndexSizeError',
180             }) ) if( $pos < 0 && abs( $pos ) > $size );
181 0 0       0 $pos = ( $rows->length + $pos ) if( $pos < 0 );
182             }
183             # "If a table has multiple <tbody> elements, by default, the new row is inserted into the last <tbody>."
184 1         8 my $body = $self->tbodies->last;
185             # If there is no tbody and no rows yet, we create a tbody
186 1 0 33     85 if( !$rows->length && !$body )
187             {
188 0         0 $body = $self->createTBody();
189             }
190 1 50       41669 $self->_load_class( 'HTML::Object::DOM::Element::TableRow' ) || return( $self->pass_error );
191 1         194 my $row;
192             # A position was provided
193 1 50       9 if( defined( $pos ) )
    50          
194             {
195             # ..., but there are no rows yet
196 0 0       0 if( $rows->is_empty )
197             {
198             # if we have a tbody, we add the new row there
199 0 0       0 if( $body )
200             {
201 0         0 $row = $body->insertRow();
202             }
203             # otherwise, we just add as the last child of the table.
204             # However, this should not happen, because if there are no rows and no tbody, oen is created and this condition is never reached
205             else
206             {
207 0         0 $self->children->push( $row );
208 0         0 $row->parent( $self );
209 0         0 $self->reset(1);
210             }
211             }
212             else
213             {
214 0         0 my $elem = $rows->index( $pos );
215 0 0       0 return( $self->error({
216             message => "No element could be found at row index $pos",
217             class => 'HTML::Object::HierarchyRequestError',
218             }) ) if( !defined( $elem ) );
219 0         0 my $parent = $elem->parent;
220 0         0 my $children = $parent->children;
221 0 0       0 return( $self->error({
222             message => "Element at row index $pos has no parent!",
223             class => 'HTML::Object::HierarchyRequestError',
224             }) ) if( !$parent );
225 0         0 my $real_pos = $children->pos( $elem );
226 0 0       0 return( $self->error({
227             message => "Unable to find the row element for index $pos",
228             class => 'HTML::Object::HierarchyRequestError',
229             }) ) if( !defined( $real_pos ) );
230 0   0     0 $row = HTML::Object::DOM::Element::TableRow->new( @_ ) ||
231             return( $self->pass_error( HTML::Object::DOM::Element::TableRow->error ) );
232 0         0 $row->close;
233 0         0 $row->parent( $parent );
234 0         0 $children->splice( $real_pos, 0, $row );
235 0         0 $parent->reset(1);
236             }
237             }
238             # If there is already a tbody, the new row will be added there
239             elsif( $body )
240             {
241 1         8 $row = $body->insertRow();
242             }
243             # otherwise, there are already other rows directly under <table> and the new row is just added at the end of the table, even if there is a <tfoot> element.
244             else
245             {
246 0   0     0 $row = HTML::Object::DOM::Element::TableRow->new( @_ ) ||
247             return( $self->pass_error( HTML::Object::DOM::Element::TableRow->error ) );
248 0         0 $row->close;
249 0         0 $self->children->push( $row );
250 0         0 $row->parent( $self );
251 0         0 $self->reset(1);
252             }
253 1         159 $self->_reset_table( 'rows' );
254 1         6 return( $row );
255             }
256              
257             # Note: property rows read-only
258             sub rows
259             {
260 21     21 1 4476 my $self = shift( @_ );
261 21 100 100     137 return( $self->{_table_rows} ) if( $self->{_table_rows} && !$self->_is_table_reset( qw( rows tbody tfoot thead ) ) );
262 19         106 my $results = $self->new_array;
263 19         494 my $children = $self->children;
264             $children->foreach(sub
265             {
266 140     140   5252 my $tag = $_->tag;
267 140 50 100     129688 if( $tag eq 'tr' )
    100 100        
268             {
269 0         0 $results->push( $_ );
270             }
271             elsif( $tag eq 'tbody' || $tag eq 'tfoot' || $tag eq 'thead' )
272             {
273 36         945 my $rows = $_->rows;
274 36 100       159 $results->push( $rows->list ) if( !$rows->is_empty );
275             }
276 140         4589 return(1);
277 19         1477 });
278 19 100       810 unless( $self->{_table_rows} )
279             {
280 1 50       13 $self->_load_class( 'HTML::Object::DOM::Collection' ) ||
281             return( $self->pass_error );
282 1   50     57 $self->{_table_rows} = HTML::Object::DOM::Collection->new ||
283             return( $self->pass_error( HTML::Object::DOM::Collection->error ) );
284             }
285             # Re-use the same object, so that we can update the object the user may have retrieved
286             # e.g.:
287             # my $rows = $table->rows
288             # then do some row changes, and
289             # say $rows->length; # shows updated number of rows. No need to redo $table->rows
290 19         117 $self->{_table_rows}->set( $results );
291 19         378 $self->_remove_table_reset( 'rows' );
292 19         92 return( $self->{_table_rows} );
293             }
294              
295             # Note: deprecated property rules
296 0     0 1 0 sub rules : lvalue { return( shift->_set_get_property( 'rules', @_ ) ); }
297              
298             # Note: deprecated property summary
299 0     0 1 0 sub summary : lvalue { return( shift->_set_get_property( 'summary', @_ ) ); }
300              
301             # Note: property tBodies read-only
302 5     5 1 28 sub tBodies { return( shift->_get_tsection_collection( 'tbody' ) ); }
303              
304 5     5 1 84754 sub tbodies { return( shift->tBodies( @_ ) ); }
305              
306             # Note: property tFoot
307 1     1 1 7 sub tFoot : lvalue { return( shift->_set_get_section( tfoot => 'HTML::Object::DOM::Element::TableSection', @_ ) ); }
308              
309 1     1 1 102 sub tfoot : lvalue { return( shift->tFoot( @_ ) ); }
310              
311             # Note: property tHead
312 1     1 1 6 sub tHead : lvalue { return( shift->_set_get_section( thead => 'HTML::Object::DOM::Element::TableSection', @_ ) ); }
313              
314 1     1 1 102 sub thead : lvalue { return( shift->tHead( @_ ) ); }
315              
316             # Note: deprecated property width is inherited
317              
318             # Common for tbody, thead and tfoot
319             sub _create_tsection
320             {
321 3     3   10 my $self = shift( @_ );
322 3   50     50 my $tag = shift( @_ ) || return( $self->error( "No tag name was provided for this table section." ) );
323 3         30 my $opts = $self->_get_args_as_hash( @_ );
324 3 50       34 $self->_load_class( 'HTML::Object::DOM::Element::TableSection' ) ||
325             return( $self->pass_error );
326 3         137 $opts->{tag} = $tag;
327 3         16 my $children = $self->children;
328 3   50     263 my $elem = HTML::Object::DOM::Element::TableSection->new( %$opts ) ||
329             return( $self->pass_error( HTML::Object::DOM::Element::TableSection->error ) );
330 3         60 $elem->close;
331 3         12 $elem->parent( $self );
332 3 100   35   116 my $list = $children->grep(sub{ $self->_is_a( $_ => 'HTML::Object::DOM::Element::TableSection' ) && $_->tag eq $tag });
  35         11324  
333 3 100       345 if( $tag eq 'tbody' )
    100          
    50          
334             {
335 1         7 my $last_elem = $list->last;
336 1 50       74 if( $last_elem )
337             {
338 1         12 $last_elem->after( $elem );
339             }
340             else
341             {
342 0         0 $children->push( $elem );
343             }
344 1         12 $self->reset(1);
345 1         3 $self->_reset_table( $tag );
346             }
347             elsif( $tag eq 'tfoot' )
348             {
349 1 50       6 if( $list->is_empty )
350             {
351 0         0 $children->push( $elem );
352 0         0 $self->reset(1);
353 0         0 $self->_reset_table( $tag );
354             }
355             else
356             {
357 1         24 return( $list->first );
358             }
359             }
360             elsif( $tag eq 'thead' )
361             {
362 1 50       271 if( $list->is_empty )
363             {
364 0         0 my $len = $children->length;
365 0         0 my $pos;
366 0         0 for( my $i = 0; $i < $len; $i++ )
367             {
368 0         0 my $e = $children->[$i];
369 0 0       0 next if( !$e->_is_a( 'HTML::Object::DOM::Node' ) );
370 0         0 my $tag = $e->tag;
371 0 0 0     0 if( $tag ne 'caption' && $tag ne 'colgroup' )
372             {
373 0         0 $pos = $i;
374 0         0 $children->splice( $i, 0, $elem );
375 0         0 $self->reset(1);
376 0         0 $self->_reset_table( $tag );
377 0         0 last;
378             }
379             }
380 0 0       0 if( !defined( $pos ) )
381             {
382 0         0 $children->push( $elem );
383 0         0 $self->reset(1);
384 0         0 $self->_reset_table( $tag );
385             }
386             }
387             else
388             {
389 1         24 return( $list->first );
390             }
391             }
392 1         4 return( $elem );
393             }
394              
395             sub _delete_first_element
396             {
397 3     3   10 my $self = shift( @_ );
398 3   50     18 my $tag = shift( @_ ) || return( $self->error( "No tag was provided." ) );
399 3         172 $tag = lc( $tag );
400 3         20 my $children = $self->children;
401 3         262 my $len = $children->length;
402 3         121779 for( my $i = 0; $i < $len; $i++ )
403             {
404 15 100 66     13429 if( $self->_is_a( $children->[$i] => 'HTML::Object::Element' ) &&
405             $children->[$i]->tag eq $tag )
406             {
407 3         2840 my $elem = $children->[$i];
408 3         39 $children->splice( $i, 1 );
409 3         52 $elem->parent( undef );
410 3         109 $self->reset(1);
411 3         19 $self->_reset_table( $tag );
412 3         29 return( $elem );
413             }
414             }
415 0         0 return;
416             }
417              
418             sub _get_tsection_collection
419             {
420 10     10   30 my $self = shift( @_ );
421 10   50     47 my $tag = shift( @_ ) || return( $self->error( "No tag name was provided for this table section." ) );
422 10         37 my $cache_name = "_table_collection_${tag}";
423 10 100 100     58 return( $self->{ $cache_name } ) if( $self->{ $cache_name } && !$self->_is_table_reset( $tag ) );
424 8         41 my $results = $self->new_array;
425 8         200 my $children = $self->children;
426             $children->foreach(sub
427             {
428             # if( $_->tag eq $tag && !$self->_is_a( $_ => 'HTML::Object::DOM::Closing' ) )
429 90 100   90   72095 if( $_->tag eq $tag )
430             {
431 8         7291 $results->push( $_ );
432             }
433 8         622 });
434 8 50       7568 $self->_load_class( 'HTML::Object::DOM::Collection' ) || return( $self->pass_error );
435 8   50     429 my $col = HTML::Object::DOM::Collection->new( $results ) ||
436             return( $self->pass_error( HTML::Object::DOM::Collection->error ) );
437 8         33 $self->{ $cache_name } = $col;
438 8         36 $self->_remove_table_reset( $tag );
439 8         48 return( $col );
440             }
441              
442             sub _is_table_reset
443             {
444 26     26   85 my $self = shift( @_ );
445 26         107 my @types = @_;
446 26         69 my $type = shift( @_ );
447 26 50       106 if( scalar( @types ) )
448             {
449 26         90 foreach my $type ( @types )
450             {
451 38 50 33     218 if( defined( $type ) && CORE::length( $type ) )
452             {
453 38 100       288 return(1) if( CORE::length( $self->{ "_table_reset_${type}" } ) );
454             }
455             }
456             }
457             else
458             {
459 0         0 for( @{$self->{_reset_fields}} )
  0         0  
460             {
461 0 0 0     0 return(1) if( CORE::exists( $self->{ $_ } ) && CORE::length( $self->{ $_ } ) );
462             }
463             }
464 4         51 return(0);
465             }
466              
467             sub _remove_table_reset
468             {
469 27     27   84 my $self = shift( @_ );
470 27         87 my @types = @_;
471 27         85 for( @types )
472             {
473 27         147 CORE::delete( $self->{ "_table_reset_${_}" } );
474             }
475 27         63 return( $self );
476             }
477              
478             sub _reset_table
479             {
480 101     101   306 my $self = shift( @_ );
481 101         223 my $type = shift( @_ );
482 101 100       436 if( $type eq 'all' )
483             {
484 12         26 for( @{$self->{_reset_fields}} )
  12         125  
485             {
486 60         151 $self->{ $_ } = 1;
487             }
488             }
489             else
490             {
491 89         1126 $self->{ "_table_reset_${type}" } = 1;
492             }
493 101 100 100     784 $self->rows if( $type eq 'rows' || $type eq 'all' );
494 101         1902 $self->reset(1);
495 101         284 return( $self );
496             }
497              
498             sub _set_get_section : lvalue
499             {
500 6     6   18 my $self = shift( @_ );
501 6         17 my $tag = shift( @_ );
502 6         13 my $class = shift( @_ );
503              
504             return( $self->_set_get_callback({
505             get => sub
506             {
507 5     5   3259 my $self = shift( @_ );
508 5         38 my $list = $self->_get_tsection_collection( $tag );
509 5         27 return( $list->first );
510             },
511             set => sub
512             {
513 1     1   377 my $self = shift( @_ );
514 1         3 my $new = shift( @_ );
515 1 50 33     5 if( !$self->_is_a( $new => $class ) || $new->tag ne $tag )
516             {
517 0         0 return( $self->error({ message => "New ${tag} object provided is not an ${class} object", class => 'HTML::Object::HierarchyRequestError' }) );
518             }
519 1         907 $new->detach;
520 1         3 my( $old, $pos );
521 1         4 my $children = $self->children;
522 1         67 my $len = $children->length;
523 1         40058 for( my $i = 0; $i < $len; $i++ )
524             {
525 9         1142 my $e = $children->[$i];
526 9         88 my $e_tag = $e->tag;
527 9 50 33     8373 if( $e_tag eq $tag )
    50 33        
528             {
529 0         0 $old = $e;
530 0         0 $pos = $i;
531 0         0 last;
532             }
533             # Find out the default position we would insert the new element if there is no previous ($old) element
534             elsif( !defined( $pos ) &&
535             (
536             ( $tag eq 'thead' && $e_tag ne 'caption' && $e_tag ne 'colgroup' ) ||
537             ( $tag eq 'tfoot' && $e_tag ne 'caption' && $e_tag ne 'colgroup' && $e_tag ne 'thead' )
538             ) )
539             {
540 0         0 $pos = $i;
541             }
542             }
543            
544 1 50       122 if( !defined( $pos ) )
545             {
546 1 50       6 if( $tag eq 'caption' )
547             {
548 1         3 $pos = 0;
549             }
550             # For thead, or tfoot, put them as the last element
551             else
552             {
553 0         0 $pos = $len;
554             }
555             }
556            
557 1         5 $self->reset(1);
558 1         5 $self->_reset_table( $tag );
559 1         6 $new->parent( $self );
560 1 50       37 if( defined( $old ) )
561             {
562 0         0 $children->splice( $pos, 1, $new );
563 0         0 return( $old );
564             }
565             else
566             {
567 1         8 $children->splice( $pos, 0, $new );
568 1         16 return;
569             }
570             },
571 6         83 }, @_ ) );
572             }
573              
574             1;
575             # NOTE: POD
576             __END__
577              
578             =encoding utf-8
579              
580             =head1 NAME
581              
582             HTML::Object::DOM::Element::Table - HTML Object DOM Table Class
583              
584             =head1 SYNOPSIS
585              
586             use HTML::Object::DOM::Element::Table;
587             my $table = HTML::Object::DOM::Element::Table->new ||
588             die( HTML::Object::DOM::Element::Table->error, "\n" );
589              
590             =head1 VERSION
591              
592             v0.2.1
593              
594             =head1 DESCRIPTION
595              
596             This interface provides special properties and methods (beyond the regular L<HTML::Object::DOM::Element> object interface it also has available to it by inheritance) for manipulating the layout and presentation of tables in an HTML document.
597              
598             Tables can have, in this order:
599              
600             =over 4
601              
602             =item 1. an optional L<caption|HTML::Object::DOM::Element::TableCaption> element,
603              
604             =item 2. zero or more L<colgroup|HTML::Object::DOM::Element::TableCol> elements,
605              
606             =item 3. an optional L<thead|HTML::Object::DOM::Element::TableSection> element,
607              
608             =item 4. either one of the following:
609              
610             =over 8
611              
612             =item * zero or more L<tbody|HTML::Object::DOM::Element::TableSection> elements
613              
614             =item * one or more L<tr|HTML::Object::DOM::Element::TableRow> elements
615              
616             =back
617              
618             =item 5. an optional L<tfoot|HTML::Object::DOM::Element::TableSection> element
619              
620             =back
621              
622             The above is for reference only and is not enforced by this interface.
623              
624             =head1 INHERITANCE
625              
626             +-----------------------+ +---------------------------+ +-------------------------+ +----------------------------+ +-----------------------------------+
627             | HTML::Object::Element | --> | HTML::Object::EventTarget | --> | HTML::Object::DOM::Node | --> | HTML::Object::DOM::Element | --> | HTML::Object::DOM::Element::Table |
628             +-----------------------+ +---------------------------+ +-------------------------+ +----------------------------+ +-----------------------------------+
629              
630             =head1 PROPERTIES
631              
632             Inherits properties from its parent L<HTML::Object::DOM::Element>
633              
634             =head2 caption
635              
636             Is a L<table caption element|HTML::Object::DOM::Element::TableCaption> representing the first L<caption|HTML::Object::DOM::Element::TableCaption> that is a child of the element, or C<undef> if none is found.
637              
638             When set, if the object does not represent a L<caption|HTML::Object::DOM::Element::TableCaption>, a C<HTML::Object::HierarchyRequestError> error is returned. If a correct object is given, it is inserted in the tree as the first child of this element and the first L<caption|HTML::Object::DOM::Element::TableCaption> that is a child of this element is removed from the tree, if any, and returned.
639              
640             Example:
641              
642             my $table = $doc->getElementsByTagName( 'table' )->first;
643             my $old_caption = $table->caption( $new_caption );
644             # or
645             $table->caption = $new_caption;
646              
647             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/caption>
648              
649             =head2 rows
650              
651             Read-only.
652              
653             Returns a L<HTML Collection|HTML::Object::DOM::Collection> containing all the rows of the element, that is all C<tr> that are a child of the element, or a child of one of its C<thead>, C<tbody> and C<tfoot> children. The rows members of a C<thead> appear first, in tree order, and those members of a C<tbody> last, also in tree order.
654              
655             Note that for performance improvement, the collection is cached until changes are made that would affect the results.
656              
657             Example:
658              
659             <table id="myTable">
660             <tr></tr>
661             <tbody>
662             <tr></tr>
663             </tbody>
664             </table>
665              
666             my $rows = $doc->getElementById( 'myTable' )->rows;
667             say $rows->length; # 2
668              
669             <table id="myTable2">
670             <tr>
671             <td>
672             <table>
673             <tr></tr>
674             </table>
675             </td>
676             </tr>
677             <tbody>
678             <tr></tr>
679             </tbody>
680             </table>
681              
682             my $rows = $doc->getElementById( 'myTable2' )->rows;
683             say $rows->length; # Still 2
684              
685             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/rows>
686              
687             =head2 tBodies
688              
689             Read-only.
690              
691             Returns a L<HTML Collection|HTML::Object::DOM::Collection> containing all the L<tbody|HTML::Object::DOM::Element::TableSection> of the element.
692              
693             Note that for performance improvement, the collection is cached until changes are made that would affect the results.
694              
695             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/tBodies>
696              
697             =head2 tbodies
698              
699             Alias for L</tBodies>
700              
701             =head2 tFoot
702              
703             Is a L<HTML::Object::DOM::Element::TableSection> representing the first L<tfoot|HTML::Object::DOM::Element::TableSection> that is a child of the element, or C<undef> if none is found.
704              
705             When set, if the object does not represent a L<tfoot|HTML::Object::DOM::Element::TableSection>, a C<HTML::Object::HierarchyRequestError> error is returned.
706              
707             If a correct object is given, it is inserted in the tree immediately before the first element that is neither a L<caption|HTML::Object::DOM::Element::TableCaption>, a L<colgroup|HTML::Object::DOM::Element::TableCol>, nor a L<thead|HTML::Object::DOM::Element::TableSection>, or as the last child if there is no such element, and the first L<tfoot|HTML::Object::DOM::Element::TableSection> that is a child of this element is removed from the tree, if any.
708              
709             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/tFoot>
710              
711             =head2 tHead
712              
713             Is a L<HTML::Object::DOM::Element::TableSection> representing the first L<thead|HTML::Object::DOM::Element::TableSection> that is a child of the element, or C<undef> if none is found.
714              
715             When set, if the object does not represent a L<thead|HTML::Object::DOM::Element::TableSection>, a C<HTML::Object::HierarchyRequestError> error is returned.
716              
717             If a correct object is given, it is inserted in the tree immediately before the first element that is neither a L<caption|HTML::Object::DOM::Element::TableCaption>, nor a L<colgroup|HTML::Object::DOM::Element::TableCol>, or as the last child if there is no such element, and the first L<thead|HTML::Object::DOM::Element::TableSection> that is a child of this element is removed from the tree, if any.
718              
719             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/tHead>
720              
721             =head1 METHODS
722              
723             Inherits methods from its parent L<HTML::Object::DOM::Element>
724              
725             =head2 createCaption
726              
727             Returns an L<HTML::Object::DOM::Element> representing the first L<caption|HTML::Object::DOM::Element::TableCaption> that is a child of the element. If none is found, a new one is created and inserted in the tree as the first child of the C<table> element.
728              
729             Example:
730              
731             <table>
732             <tr><td>Cell 1.1</td><td>Cell 1.2</td><td>Cell 1.3</td></tr>
733             <tr><td>Cell 2.1</td><td>Cell 2.2</td><td>Cell 2.3</td></tr>
734             </table>
735              
736             my $table = $doc->querySelector('table');
737             my $caption = $table->createCaption();
738             $caption->textContent = 'This caption was created by JavaScript!';
739              
740             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/createCaption>
741              
742             =head2 createTBody
743              
744             Returns a L<HTML::Object::DOM::Element::TableSection> representing a new L<tbody|HTML::Object::DOM::Element::TableSection> that is a child of the element. It is inserted in the tree after the last element that is a L<tbody|HTML::Object::DOM::Element::TableSection>, or as the last child if there is no such element.
745              
746             Example:
747              
748             my $mybody = $mytable->createTBody();
749             # Now this should be true: $mybody == mytable->tBodies->item( $mytable->tBodies->length - 1 )
750              
751             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/createTBody>
752              
753             =head2 createTFoot
754              
755             Returns an L<HTML::Object::DOM::Element::TableSection> representing the first L<tfoot|HTML::Object::DOM::Element::TableSection> that is a child of the element. If none is found, a new one is created and inserted in the tree as the last child.
756              
757             Example:
758              
759             my $myfoot = $mytable->createTFoot();
760             # Now this should be true: $myfoot == $mytable->tFoot
761              
762             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/createTFoot>
763              
764             =head2 createTHead
765              
766             Returns an L<HTML::Object::DOM::Element::TableSection> representing the first L<thead|HTML::Object::DOM::Element::TableSection> that is a child of the element. If none is found, a new one is created and inserted in the tree immediately before the first element that is neither a L<caption|HTML::Object::DOM::Element::TableCaption>, nor a L<colgroup|HTML::Object::DOM::Element::TableCol>, or as the last child if there is no such element.
767              
768             Example:
769              
770             my $myhead = mytable->createTHead();
771             # Now this should be true: $myhead == mytable->tHead
772              
773             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/createTHead>
774              
775             =head2 deleteCaption
776              
777             Removes the first L<caption|HTML::Object::DOM::Element::TableCaption> that is a child of the element and returns the object of the caption element.
778              
779             Example:
780              
781             <table>
782             <caption>This caption will be deleted!</caption>
783             <tr><td>Cell 1.1</td><td>Cell 1.2</td></tr>
784             <tr><td>Cell 2.1</td><td>Cell 2.2</td></tr>
785             </table>
786              
787             my $table = $doc->querySelector('table');
788             $table->deleteCaption();
789              
790             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/deleteCaption>
791              
792             =head2 deleteRow
793              
794             Removes the row corresponding to the index given in parameter. If the index value is C<-1> the last row is removed; if it smaller than C<-1> or greater than the amount of rows in the collection, an C<HTML::Object::IndexSizeError> is returned.
795              
796             Example:
797              
798             <table>
799             <tr><td>Cell 1.1</td><td>Cell 1.2</td><td>Cell 1.3</td></tr>
800             <tr><td>Cell 2.1</td><td>Cell 2.2</td><td>Cell 2.3</td></tr>
801             <tr><td>Cell 3.1</td><td>Cell 3.2</td><td>Cell 3.3</td></tr>
802             </table>
803              
804             my $table = $doc->querySelector('table');
805             # Delete second row
806             $table->deleteRow(1);
807              
808             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/deleteRow>
809              
810             =head2 deleteTFoot
811              
812             Removes the first L<tfoot|HTML::Object::DOM::Element::TableSection> that is a child of the element and returns the object of the caption element.
813              
814             Example:
815              
816             <table>
817             <thead><th>Name</th><th>Score</th></thead>
818             <tr><td>Bob</td><td>541</td></tr>
819             <tr><td>Jim</td><td>225</td></tr>
820             <tfoot><th>Average</th><td>383</td></tfoot>
821             </table>
822              
823             my $table = $doc->querySelector('table');
824             $table->deleteTFoot();
825              
826             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/deleteTFoot>
827              
828             =head2 deleteTHead
829              
830             Removes the first L<thead|HTML::Object::DOM::Element::TableSection> that is a child of the element and returns the object of the caption element.
831              
832             Example:
833              
834             <table>
835             <thead><th>Name</th><th>Occupation</th></thead>
836             <tr><td>Bob</td><td>Plumber</td></tr>
837             <tr><td>Jim</td><td>Roofer</td></tr>
838             </table>
839              
840             my $table = $doc->querySelector('table');
841             $table->deleteTHead();
842              
843             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/deleteTHead>
844              
845             =head2 insertRow
846              
847             Returns an L<HTML::Object::DOM::Element::TableRow> representing a new row of the table. It inserts it in the rows collection immediately before the C<tr> element at the given index position, if any was provided. If there are no existing rows yet, a L<tbody|HTML::Object::DOM::Element::TableSection> is created and the new row inserted into it. If a table has multiple C<tbody> elements, by default, the new row is inserted into the last C<tbody>.
848              
849             If the index is not given or is C<-1>, the new row is appended to the collection. If the index is smaller than C<-1>, it will start that far back from the end of the collection array. If index is greater than the number of rows in the collection, an C<HTML::Object::IndexSizeError> error is returned.
850              
851             Example:
852              
853             <table></table>
854              
855             $doc->getElementsByTagName('table')->[0]->insertRow();
856              
857             Table is now:
858              
859             <table>
860             <tbody>
861             <tr></tr>
862             </tbody>
863             </table>
864              
865             But if there are already existing rows and no tbody, the new row will merely be added as the las child of the table.
866              
867             <table>
868             <tr></tr>
869             </table>
870              
871             $doc->getElementsByTagName('table')->[0]->insertRow();
872              
873             Table is now:
874              
875             <table>
876             <tr></tr>
877             <tr></tr>
878             </table>
879              
880             Even if there is a C<tfoot>, the new row will be added after:
881              
882             <table>
883             <tr></tr>
884             <tfoot></tfoot>
885             </table>
886              
887             $doc->getElementsByTagName('table')->[0]->insertRow();
888              
889             Table is now:
890              
891             <table>
892             <tr></tr>
893             <tfoot></tfoot>
894             <tr></tr>
895             </table>
896              
897             If an index is negative, the new row will be added that far back from the end:
898              
899             <table>
900             <tr id="one"></tr>
901             <tr id="two"></tr>
902             <tr id="three"></tr>
903             </table>
904              
905             $doc->getElementsByTagName('table')->[0]->insertRow(-2);
906              
907             Table is now:
908              
909             <table>
910             <tr id="one"></tr>
911             <tr></tr>
912             <tr id="two"></tr>
913             <tr id="three"></tr>
914             </table>
915              
916             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/insertRow>
917              
918             =head2 DEPRECATED PROPERTIES
919              
920             =head2 align
921              
922             Is a string containing an enumerated value reflecting the align attribute. It indicates the alignment of the element's contents with respect to the surrounding context. The possible values are "left", "right", and "center".
923              
924             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/align>
925              
926             =head2 bgColor
927              
928             A string containing the background color of the table. It reflects the obsolete bgcolor attribute.
929              
930             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/bgColor>
931              
932             =head2 border
933              
934             Is a string containing the width in pixels of the border of the table. It reflects the obsolete border attribute.
935              
936             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/border>
937              
938             =head2 cellPadding
939              
940             Is a string containing the width in pixels of the horizontal and vertical sapce between cell content and cell borders. It reflects the obsolete cellpadding attribute.
941              
942             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/cellPadding>
943              
944             =head2 cellSpacing
945              
946             Is a string containing the width in pixels of the horizontal and vertical separation between cells. It reflects the obsolete cellspacing attribute.
947              
948             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/cellSpacing>
949              
950             =head2 frame
951              
952             Is a string containing the type of the external borders of the table. It reflects the obsolete frame attribute and can take one of the following values: C<void>, C<above>, C<below>, C<hsides>, C<vsides>, C<lhs>, C<rhs>, C<box>, or C<border>.
953              
954             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/frame>
955              
956             =head2 rules
957              
958             Is a string containing the type of the internal borders of the table. It reflects the obsolete rules attribute and can take one of the following values: C<none>, C<groups>, C<rows>, C<cols>, or C<all>.
959              
960             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/rules>
961              
962             =head2 summary
963              
964             Is a string containing a description of the purpose or the structure of the table. It reflects the obsolete summary attribute.
965              
966             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/summary>
967              
968             =head2 width
969              
970             Is a string containing the length in pixels or in percentage of the desired width fo the entire table. It reflects the obsolete width attribute.
971              
972             See also L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement/width>
973              
974             =head1 AUTHOR
975              
976             Jacques Deguest E<lt>F<jack@deguest.jp>E<gt>
977              
978             =head1 SEE ALSO
979              
980             L<Mozilla documentation|https://developer.mozilla.org/en-US/docs/Web/API/HTMLTableElement>, L<Mozilla documentation on table element|https://developer.mozilla.org/en-US/docs/Web/HTML/Element/table>
981              
982             =head1 COPYRIGHT & LICENSE
983              
984             Copyright(c) 2021 DEGUEST Pte. Ltd.
985              
986             All rights reserved
987              
988             This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
989              
990             =cut