File Coverage

blib/lib/HTML/ElementTable.pm
Criterion Covered Total %
statement 54 392 13.7
branch 0 180 0.0
condition 0 85 0.0
subroutine 18 59 30.5
pod 11 16 68.7
total 83 732 11.3


tags. tag rather than each
line stmt bran cond sub pod time code
1             package HTML::ElementTable;
2              
3 1     1   2950 use strict;
  1         3  
  1         48  
4 1     1   6 use vars qw($VERSION @ISA $AUTOLOAD);
  1         2  
  1         65  
5 1     1   6 use Carp;
  1         2  
  1         77  
6              
7 1     1   6 use HTML::ElementGlob;
  1         1  
  1         12372  
8              
9             @ISA = qw(HTML::ElementTable::Element);
10              
11             $VERSION = '1.18';
12              
13             my $DEBUG = 0;
14              
15             # Enforced adoption policy such that positional coords are untainted.
16             my @Valid_Children = qw( HTML::ElementTable::RowElement );
17              
18             ##################
19             # Native Methods #
20             ##################
21              
22             sub extent {
23 0     0 1   my $self = shift;
24 0 0         @_ || return ($self->maxrow,$self->maxcol);
25 0           my($maxrow, $maxcol) = @_;
26 0 0 0       defined $maxrow && defined $maxcol
27             or croak "Max row and col dimensions required";
28              
29             # Hit rows
30 0 0         $self->_adjust_content($self, $maxrow, $self->maxrow)
31             if $maxrow != $self->maxrow;
32            
33             # Hit columns
34 0           my @rows = ();
35 0           foreach ($self->content_list) {
36 0 0 0       push(@rows, $_) if ref && $_->tag eq 'tr';
37             }
38 0 0         if ($maxcol != $self->maxcol) {
39 0           grep { $self->_adjust_content($_, $maxcol, $self->maxcol) } @rows;
  0            
40             }
41              
42             # New data cells caused by new rows will be automatically taken care
43             # of within _adjust_content
44              
45             # Re-glob
46 0           $self->refresh;
47             }
48              
49             sub refresh {
50 0     0 0   my $self = shift;
51 0           my($row,$col,$p_row,$p_col);
52              
53             # Reconstruct globs. There are two main globs - the row and column
54             # collections - plus the globs representing each row and each column
55             # of cells.
56            
57             # Clear old row and column globs
58 0 0         grep { $_->glob_delete_content } @{$self->_rows->glob_content}
  0            
  0            
59             unless $self->_rows->glob_is_empty;
60 0 0         grep { $_->glob_delete_content } @{$self->_cols->glob_content}
  0            
  0            
61             unless $self->_cols->glob_is_empty;
62 0           $self->_rows->glob_delete_content;
63 0           $self->_cols->glob_delete_content;
64              
65 0           my $colcnt;
66 0           my $maxcol = -1;
67 0           foreach $row ($self->content_list) {
68             # New glob for each row, added to rows glob
69 0 0         next unless ref $row;
70 0           $p_row = $self->_rowglob;
71 0           $p_row->alias($row);
72 0           $self->_rows->glob_push_content($p_row);
73 0           $colcnt = 0;
74 0 0         foreach $col ($row->is_empty ? () : @{$row->content}) {
  0            
75             # Add each cell to the individual row glob
76 0 0         next unless ref $col;
77 0           $p_row->glob_push_content($col);
78 0 0         if ($colcnt > $maxcol) {
79             # If a new column, make column glob
80 0           $p_col = $self->_colglob;
81 0           $self->_cols->glob_push_content($p_col);
82 0           ++$maxcol;
83             }
84             else {
85             # Otherwise use the existing column glob
86 0           $p_col = $self->_cols->glob_content->[$colcnt];
87             }
88             # Add the cell to the column glob
89 0           $p_col->glob_push_content($col);
90 0           ++$colcnt;
91             }
92             }
93 0           $self;
94             }
95              
96             sub _adjust_content {
97 0     0     my $self = shift;
98 0           my($e,$limit,$old) = @_;
99 0 0         ref $e or croak "Element required";
100 0 0         defined $limit or croak "Index limit required";
101 0 0         if (!defined $old) {
102 0           grep { ++$old } @{$e->content};
  0            
  0            
103             }
104 0 0         if ($limit < $old) {
    0          
105             # We are trimming
106 0           my($i, $c, $found);
107 0           $i = $c = -1;
108             # We mess with $i like this to avoid having non data elements throw
109             # off our grid count
110 0           foreach (@{$e->content}) {
  0            
111 0           ++$c;
112 0 0         next unless ref;
113 0           ++$i;
114 0 0         if ($i == $limit) {
115 0           $found = $c;
116 0           next;
117             }
118 0 0         $_->delete if $found;
119             }
120 0           @{$e->content} = @{$e->content}[0..$found];
  0            
  0            
121             }
122             elsif ($limit > $old) {
123             # We are growing
124 0           my($tag,$d,$r);
125 0           foreach ($old+1..$limit) {
126 0 0         if ($e->tag eq 'table') {
127 0           $r = HTML::ElementTable::RowElement->new();
128 0 0         if ($self->maxcol != -1) {
129             # Brand new colums...use -1 as old to get 0
130 0           $self->_adjust_content($r,$self->maxcol,-1);
131             }
132 0           $e->push_content($r);
133             }
134             else {
135 0           $d = HTML::ElementTable::DataElement->new();
136 0           $d->blank_fill($self->blank_fill);
137 0           $e->push_content($d);
138             }
139             }
140             }
141 0           $e;
142             }
143              
144             sub maxrow {
145 0     0 1   my($self, $maxrow) = @_;
146 0 0         $self->extent($maxrow,$self->maxcol) if defined $maxrow;
147 0 0         $self->_rows->glob_is_empty ? -1 : $#{$self->_rows->glob_content};
  0            
148             }
149              
150             sub maxcol {
151 0     0 1   my($self, $maxcol) = @_;
152 0 0         $self->extent($self->maxrow, $maxcol) if defined $maxcol;
153 0 0         $self->_cols->glob_is_empty ? -1 : $#{$self->_cols->glob_content};
  0            
154             }
155              
156             # Index and glob hooks
157             sub cell {
158 0     0 1   my $self = shift;
159 0           my @elements;
160 0           while (@_) {
161 0           my($r, $c) = splice(@_, 0, 2);
162 0 0 0       defined $r && defined $c || croak "Missing coordinate";
163 0           my $row = $self->row($r);
164 0 0         croak "Row $r is empty" if $row->glob_is_empty;
165 0 0 0       if ($#{$row->glob_content} < $c || $c < 0) {
  0            
166 0           croak "Cell ($r,$c) is out of range";
167             }
168 0           push(@elements, $row->glob_content->[$c]);
169             }
170 0 0         return undef unless @elements;
171 0 0         @elements > 1 ? $self->_cellglob(@elements) : $elements[0];
172             }
173              
174             sub row {
175 0     0 1   my $self = shift;
176 0 0         @_ || croak "Index required";
177 0           my @out = grep { $_ > $self->maxrow } @_;
  0            
178 0 0         croak "Rows(@out) out of range" if @out;
179 0 0         @_ > 1 ? $self->_rowglob(@{$self->_rows->glob_content}[@_])
  0            
180             : $self->_rows->glob_content->[$_[0]];
181             }
182              
183             sub col {
184 0     0 1   my $self = shift;
185 0 0         @_ || croak "Index required";
186 0           my @out = grep { $_ > $self->maxcol } @_;
  0            
187 0 0         if (@out) {
188 0           croak "Columns(" . join(',', @out) . ") out of range";
189             }
190 0 0         @_ > 1 ? $self->_colglob(@{$self->_cols->glob_content}[@_])
  0            
191             : $self->_cols->glob_content->[$_[0]];
192             }
193              
194             sub box {
195 0     0 1   my $self = shift;
196 0           my($r1,$c1,$r2,$c2) = @_;
197 0 0 0       defined $r1 && defined $c1 && defined $r2 && defined $c2 ||
      0        
      0        
198             croak "Two coordinate pairs required";
199             # Normalize for ascending counts
200 0 0         ($r1, $r2) = ($r2, $r1) if $r2 < $r1;
201 0 0         ($c1, $c2) = ($c2, $c1) if $c2 < $c1;
202             # Optimize on rows if we can
203 0 0 0       if ($c1 == 0 && $c2 == $self->maxcol) {
204 0           return $self->row($r1 .. $r2);
205             }
206             # Otherwise glob the box
207 0           my(@coords,$r,$c);
208 0           foreach $r ($r1 .. $r2) {
209 0           foreach $c ($c1 .. $c2) {
210 0           push(@coords,$r,$c);
211             }
212             }
213 0           $self->cell(@coords);
214             }
215              
216             sub table {
217 0     0 1   my $self = shift;
218             # Both _rows and _cols are effectively globs of the whole table. We
219             # return row here so that valid TR attrs can be captured.
220 0           $self->_rows;
221             }
222              
223             sub mask_mode {
224             # Should span antics of children push/pull or mask/reveal siblings?
225 0     0 0   my($self,$mode) = @_;
226 0 0         $self->{_maskmode} = $mode if defined $mode;
227 0           $self->{_maskmode};
228             }
229              
230             # Main glob hooks
231             sub _rows {
232 0     0     my $self = shift;
233 0           return $self->{_rows};
234             }
235             sub _cols {
236 0     0     my $self = shift;
237 0           return $self->{_cols};
238             }
239              
240             sub _glob {
241 0     0     my $self = shift;
242 0   0       my $tag = shift || croak "No tag";
243 0           my $g = HTML::ElementGlob->new($tag);
244 0 0         $g->glob_push_content(@_) if @_;
245 0           $g;
246             }
247              
248             sub _colglob {
249 0     0     my $self = shift;
250 0           $self->_glob('tr',@_);
251             }
252              
253             sub _rowglob {
254 0     0     my $self = shift;
255 0           my $g = HTML::ElementTable::RowGlob->new();
256 0 0         $g->glob_push_content(@_) if @_;
257 0           $g;
258             }
259              
260             sub _cellglob {
261 0     0     my $self = shift;
262 0           $self->_glob('tr',@_);
263             }
264              
265             sub rowspan_dispatch {
266 0     0 0   my $self = shift;
267 0           $self->_dimspan_dispatch('rowspan', @_);
268             }
269              
270             sub colspan_dispatch {
271 0     0 0   my $self = shift;
272 0           $self->_dimspan_dispatch('colspan', @_);
273             }
274              
275             sub _dimspan_dispatch {
276             # Dispatch for children to use to send notice of span changes, in rows
277             # or columns.
278 0     0     my($self, $attr, $row, $col, $span) = @_;
279 0 0 0       defined $row && defined $col || croak "Cell row and column required";
280 0 0         defined $span || croak "Span setting required";
281 0 0         my $orth_attr = $attr eq 'colspan' ? 'rowspan' : 'colspan';
282 0 0         $span = 1 unless $span;
283 0           my $oldspan = $self->cell($row,$col)->attr($attr);
284 0 0         $oldspan = 1 unless $oldspan;
285 0 0         return if $span == $oldspan;
286 0           my $ospan = $self->cell($row,$col)->attr($orth_attr);
287 0 0         $ospan = 1 unless $ospan;
288             # We are either masking or revealing
289 0 0         my $mask = $span > $oldspan ? 1 : 0;
290 0 0         ($span, $oldspan) = ($oldspan, $span) if $oldspan > $span;
291 0           my $tc;
292 0 0         my($dim,$odim) = $attr eq 'colspan' ? ($col,$row) : ($row,$col);
293 0           foreach my $d ($dim + $oldspan .. $dim + $span - 1) {
294 0           foreach my $o ($odim .. $odim + $ospan - 1) {
295 0 0 0       next if $d == $dim && $o == $odim;
296 0   0       $tc = $self->cell($attr eq 'colspan' ? ($o,$d) : ($d,$o)) || next;
297 0           $tc->mask($mask & $self->mask_mode);
298             }
299             }
300             }
301              
302             sub blank_fill {
303             # Should blank cells be populated with " " in order for BGCOLOR
304             # to show up?
305 0     0 1   my $self = shift;
306 0           my $mode = shift;
307 0 0         if (defined $mode) {
308 0           $self->{_blank_fill} = $mode;
309 0           $self->table->blank_fill($mode);
310             }
311 0           $self->{_blank_fill};
312             }
313              
314             sub beautify {
315             # Set mode for making as_HTML output human readable. Broadcasts to
316             # component elements.
317 0     0 0   my $self = shift;
318 0           my $mode = shift;
319 0 0         if (defined $mode) {
320 0           $self->{_beautify} = $mode;
321             # Broadcast to row elements as well as data elements
322 0           $self->row(0..$self->maxrow)->beautify($mode);
323 0           $self->col(0..$self->maxcol)->beautify($mode);
324             }
325 0           $self->{_beautify};
326             }
327              
328             sub new {
329 0     0 1   my $that = shift;
330 0   0       my $class = ref($that) || $that;
331              
332             # Extract complex attributes
333 0           my($attr,$val,$maxrow,$maxcol,%e_attrs);
334 0           while ($attr = shift) {
335 0           $val = shift;
336 0 0         if ($attr =~ /^maxrow/) {
    0          
337 0           $maxrow = $val;
338             }
339             elsif ($attr =~ /^maxcol/) {
340 0           $maxcol = $val;
341             }
342             else {
343 0           $e_attrs{$attr} = $val;
344             }
345             }
346 0           my $self = $class->SUPER::new('table', %e_attrs);
347 0           bless $self,$class;
348              
349             # Default to single cell
350 0   0       $maxrow ||= 0;
351 0   0       $maxcol ||= 0;
352              
353 0           $self->_initialize_table;
354              
355 0           $self->extent($maxrow, $maxcol);
356              
357 0           $self;
358             }
359              
360             sub new_from_tree {
361             # takes a regular HTML::Element table tree structure and reblesses and
362             # configures it into an HTML::ElementTable structure.
363             #
364             # Dealing with row and column span issues properly is a real PITA, so
365             # we cheat here a little bit by creating a new table structure with
366             # fully rendered spans and use that as a template for normalizing the
367             # old table.
368 0     0 1   my($class, $tree) = @_;
369 0 0         ref $tree or croak "Ref to element tree required.\n";
370 0 0         $tree->tag eq 'table' or croak "element tree should represent a table.\n";
371              
372             # First get rid of non elements -- note this WILL zap comments within
373             # the html of the table structure (i.e. in between adjacent tr tags or
374             # td/th tags). While we're at it, determine dimensions.
375 0           my($maxrow, $maxcol) = (-1, -1);
376 0           my @rows;
377 0           my @content = reverse $tree->detach_content;
378 0           while (@content) {
379 0           my $row = pop @content;
380 0 0         next unless UNIVERSAL::isa($row, 'HTML::Element');
381 0           my $tag = $row->tag;
382             # hack around tbody, thead, tfoot - yes, this means they get
383             # stripped out of the resulting table tree
384 0 0 0       if ($tag eq 'tbody' || $tag eq 'thead' || $tag eq 'tfoot') {
      0        
385 0           push(@content, reverse $row->detach_content);
386 0           next;
387             }
388 0 0         if ($tag eq 'tr') {
389 0           ++$maxrow;
390 0           my @cells;
391 0           foreach my $cell ($row->detach_content) {
392 0 0 0       if (UNIVERSAL::isa($cell, 'HTML::Element') &&
      0        
393             ($cell->tag eq 'td' || $cell->tag eq 'th')) {
394 0           push(@cells, $cell);
395             }
396             }
397 0 0         $maxcol = $#cells if $#cells > $maxcol;
398 0           $row->push_content(@cells);
399 0           push(@rows, $row);
400             }
401             }
402 0           $tree->push_content(@rows);
403              
404             # Rasterize the tree table into a grid template -- use that as a guide
405             # to flesh out our new H::ET
406 0           eval "use HTML::TableExtract 2.08 qw(tree)";
407 0 0         croak "Problem loading HTML::TableExtract : $@\n" if $@;
408 0           my $rasterizer = HTML::TableExtract::Rasterize->make_rasterizer;
409 0           @rows = $tree->content_list;
410 0           foreach my $r (0 .. $#rows) {
411 0           my $row = $rows[$r];
412 0           foreach my $cell ($row->content_list) {
413 0   0       my $rowspan = $cell->attr('rowspan') || 1;
414 0   0       my $colspan = $cell->attr('colspan') || 1;
415 0           $rasterizer->($r, $rowspan, $colspan);
416             }
417             }
418 0           my $grid = $rasterizer->();
419              
420             # Flesh out the tree structure, inserting masked cells where
421             # appropriate
422 0           foreach my $r (0 .. $#$grid) {
423 0           my $row = $rows[$r];
424 0           my $grid_row = $grid->[$r];
425 0           my $content = $row->content_array_ref;
426 0 0         print STDERR "Flesh row $r ($#$content) to $#$grid_row\n" if $DEBUG;
427 0           foreach my $c (0 .. $#$grid_row) {
428 0           my $cell = $content->[$c];
429 0 0         print STDERR $grid_row->[$c] ? '1' : '0' if $DEBUG;
    0          
430 0 0         if ($grid_row->[$c]) {
431 0           bless $cell, 'HTML::ElementTable::DataElement';
432 0           next;
433             }
434             else {
435 0           my $masked = HTML::ElementTable::DataElement->new;
436 0           $masked->mask(1);
437 0           $row->splice_content($c, 0, $masked);
438             }
439             }
440 0 0         print STDERR "\n" if $DEBUG;
441 0 0         croak "row $r splice mismatch: $#$content vs $#$grid_row\n"
442             unless $#$content == $#$grid_row;
443 0           bless $row, 'HTML::ElementTable::RowElement';
444             }
445 0           bless $tree, 'HTML::ElementTable';
446 0           $tree->_initialize_table;
447 0           $tree->refresh;
448 0 0         print $tree->as_HTML, "\n" if $DEBUG > 1;
449 0           return $tree;
450             }
451              
452             sub _initialize_table {
453 0     0     my $self = shift;
454             # Content police for aggregate integrity
455 0           $self->watchdog(\@Valid_Children);
456              
457             # The tag choices for globs are arbitrary, but these should at least
458             # make some sort of since if the globs are rendered as_HTML.
459 0           $self->{_rows} = $self->_rowglob;
460 0           $self->{_rows}->tag('table');
461 0           $self->{_cols} = $self->_colglob;
462              
463 0           $self->mask_mode(1);
464 0           $self->blank_fill(0);
465              
466 0           $self;
467             }
468              
469             ################
470             # Sub packages #
471             ################
472              
473             {
474              
475             package HTML::ElementTable::Element;
476              
477 1     1   15 use strict;
  1         3  
  1         54  
478 1     1   7 use vars qw( @ISA );
  1         2  
  1         50  
479 1     1   6 use HTML::ElementSuper;
  1         2  
  1         11  
480              
481             @ISA = qw(HTML::ElementSuper);
482              
483             # "Beautify" mode
484             # Primarily intended for as_HTML, this mode affects how the source HTML
485             # appears. When beautified, the starttag and endtags are modified to
486             # include indentation.
487             sub beautify {
488 0     0     my $self = shift;
489 0 0         defined $_[0] ? $self->{_beautify} = shift : $self->{_beautify};
490             }
491              
492             sub starttag {
493 0     0     my $self = shift;
494 0           my $spc = '';
495 0 0 0       if ($self->beautify && !$self->mask) {
496 0           $spc = ' ' x $self->depth;
497 0           $spc = "\n$spc";
498             }
499 0           $spc . $self->SUPER::starttag;
500             }
501              
502             sub new {
503 0     0     my $that = shift;
504 0   0       my $class = ref($that) || $that;
505 0           my $self = $class->SUPER::new(@_);
506 0           bless $self, $class;
507 0           $self;
508             }
509              
510             # End HTML::ElementTable::Element
511             }
512              
513             {
514              
515             package HTML::ElementTable::DataElement;
516              
517 1     1   233 use strict;
  1         3  
  1         37  
518 1     1   5 use vars qw( @ISA $AUTOLOAD );
  1         2  
  1         442  
519              
520             @ISA = qw(HTML::ElementTable::Element);
521              
522             ####################
523             # Override Methods #
524             ####################
525              
526             sub attr {
527             # Keep tabs on colspan and rowspan
528 0     0     my($self, $attr) = splice(@_, 0, 2);
529 0           $attr = lc $attr;
530 0 0         if (@_) {
531 0           my $val = $_[0];
532 0 0         if (defined $val) {
533 0 0         if ($attr eq 'colspan') {
    0          
534 0           $self->parent->colspan_dispatch($self->addr, $val);
535             }
536             elsif ($attr eq 'rowspan') {
537 0           $self->parent->rowspan_dispatch($self->addr, $val);
538             }
539             }
540             else {
541             # Deleting an attr
542 0 0 0       if ($attr eq 'colspan' || $attr eq 'rowspan') {
543             # Make sure and dispatch zero value
544 0           $self->attr($attr, 0);
545             }
546             }
547             }
548 0           $self->SUPER::attr($attr, @_);
549             }
550              
551             sub blank_fill {
552             # Set/return mode for populating empty cells with " " so that
553             # BGCOLOR will show up.
554 0     0     my $self = shift;
555 0 0         @_ ? $self->{_blank_fill} = shift : $self->{_blank_fill};
556             }
557              
558             ##################
559             # Codus horribilus #
560             ####################
561              
562             # This bit of unfortunate code is necessary because of the shortcomings
563             # of the as_HTML method in HTML::Element. as_HTML uses
564             # HTML::Entity::encode_entities to process nodes that are not elements.
565             # For some reason, "<>&" is passed to the encode_entities method, which
566             # effectively makes it impossible to pass a literal "&" into your HTML
567             # output. Specifically, in order for the BGCOLOR to show up in an empty
568             # table cell, you must include a " ". However, you cannot pass a
569             # literal "&", for it always gets translated to "&", thus placing
570             # " " as literal text in your cells. Nor can you pass the code for
571             # a non-breaking space - it remains unchanged since the encode list is
572             # limited.
573             #
574             # So we cheat. We override the starttag method, including the " "
575             # along with the starttag if the cell is empty. This could be avoided if
576             # HTML::Element relaxed a little and laid off the hand holding.
577             #
578             # Oh - we can't just override as_HTML() and do it correctly, because
579             # as_HTML() is only invoked from the top level element - which could be
580             # a plain jane HTML::Element and know nothing of HTML::Element::Table
581             # elements.
582             #
583             # Ooo-glay!
584             sub starttag {
585 0     0     my $self = shift;
586 0           my @c = $self->content_list;
587 0 0 0       (!@c) && $self->blank_fill && !$self->mask ?
588             $self->SUPER::starttag . "  " : $self->SUPER::starttag;
589             }
590              
591             # Constructor
592              
593             sub new {
594 0     0     my $that = shift;
595 0   0       my $class = ref($that) || $that;
596 0 0         my @args = @_ ? @_ : ('td');
597 0           my $self = $class->SUPER::new(@args);
598 0           bless $self, $class;
599 0           $self->blank_fill(0);
600 0           $self;
601             }
602              
603             # End HTML::ElementTable::DataElement
604             }
605              
606             {
607              
608             package HTML::ElementTable::HeaderElement;
609              
610 1     1   7 use strict;
  1         2  
  1         40  
611 1     1   7 use vars qw( @ISA );
  1         2  
  1         39  
612 1     1   13 use Carp;
  1         1  
  1         200  
613              
614             @ISA = qw(HTML::ElementTable::DataElement);
615              
616             sub new {
617 0     0     my $that = shift;
618 0   0       my $class = ref($that) || $that;
619 0 0         my @args = @_ ? @_ : ('th');
620 0           my $self = $class->SUPER::new(@args);
621 0           bless $self, $class;
622 0           $self;
623             }
624              
625             # End HTML::ElementTable::HeaderElement
626             }
627              
628             {
629              
630             package HTML::ElementTable::RowElement;
631              
632 1     1   5 use strict;
  1         2  
  1         29  
633 1     1   5 use vars qw( @ISA $AUTOLOAD );
  1         3  
  1         41  
634 1     1   5 use Carp;
  1         2  
  1         453  
635              
636             @ISA = qw(HTML::ElementTable::Element);
637              
638             # Restrict children so that Table coordinate system is untainted.
639             my @Valid_Children = qw(
640             HTML::ElementTable::DataElement
641             HTML::ElementTable::HeaderElement
642             );
643              
644             ##################
645             # Native Methods #
646             ##################
647              
648             sub colspan_dispatch {
649             # Dispatch for children to send notice of colspan changes
650 0     0     my $self = shift;
651 0           $self->parent->colspan_dispatch($self->addr, @_);
652             }
653              
654             sub rowspan_dispatch {
655             # Dispatch for children to send notice of rowspan changes
656 0     0     my $self = shift;
657 0           $self->parent->rowspan_dispatch($self->addr, @_);
658             }
659              
660             sub new {
661 0     0     my $that = shift;
662 0   0       my $class = ref($that) || $that;
663 0 0         my @args = @_ ? @_ : ('tr');
664 0           my $self = $class->SUPER::new(@args);
665 0           bless $self,$class;
666              
667             # Content police for aggregate integrity
668 0           $self->watchdog(\@Valid_Children);
669              
670 0           $self;
671             }
672              
673             # End HTML::ElementTable::RowElement
674             }
675              
676             {
677              
678             package HTML::ElementTable::RowGlob;
679              
680 1     1   6 use strict;
  1         2  
  1         44  
681 1     1   5 use vars qw( @ISA );
  1         1  
  1         83  
682              
683 1     1   7 use HTML::ElementGlob;
  1         1  
  1         357  
684              
685             @ISA = qw(HTML::ElementGlob);
686              
687             # Designate attributes that are valid for
688             my %TR_ATTRS;
689             grep { ++$TR_ATTRS{$_} } qw( id class align valign bgcolor );
690              
691             sub alias {
692             # alias() allows us to designate an actual row element that contains
693             # our data/header elements. If we can optimize an attribute on the
694             #
or tag, then we do so.
695 0     0     my $self = shift;
696 0           my $alias = shift;
697 0 0         if (ref $alias) {
698 0           $self->{_alias} = $alias;
699             }
700 0           $self->{_alias};
701             }
702              
703             sub attr {
704             # alias intercept
705 0     0     my $self = shift;
706 0 0 0       if ($self->alias && $TR_ATTRS{lc $_[0]}) {
707 0           return $self->alias->attr(@_);
708             }
709 0           $self->SUPER::attr(@_);
710             }
711              
712             sub mask {
713             # In addition to masking all children and tags, we have to
714             # mask the row itself - accessible via the alias().
715 0     0     my $self = shift;
716 0 0         if ($self->alias) {
717 0           return $self->alias->mask(@_);
718             }
719 0           $self->SUPER::mask(@_);
720             }
721              
722             sub beautify {
723             # Broadcast beautify to alias
724 0     0     my $self = shift;
725 0 0         if ($self->alias) {
726 0           return $self->alias->beautify(@_);
727             }
728 0           $self->SUPER::beautify(@_);
729             }
730              
731             sub new {
732 0     0     my $that = shift;
733 0   0       my $class = ref($that) || $that;
734 0 0         my @args = @_ ? @_ : ('table');
735 0           my $self = $class->SUPER::new(@args);
736 0           bless $self, $class;
737 0           $self;
738             }
739              
740             # End HTML::ElementTable::RowGlob
741             }
742              
743             1;
744             __END__