File Coverage

blib/lib/Spreadsheet/HTML.pm
Criterion Covered Total %
statement 146 229 63.7
branch 83 150 55.3
condition 34 70 48.5
subroutine 20 41 48.7
pod 25 25 100.0
total 308 515 59.8


line stmt bran cond sub pod time code
1             package Spreadsheet::HTML;
2 5     5   85825 use strict;
  5         10  
  5         149  
3 5     5   27 use warnings FATAL => 'all';
  5         8  
  5         254  
4             our $VERSION = '1.16';
5              
6 5     5   32 use Exporter 'import';
  5         9  
  5         286  
7             our @EXPORT_OK = qw(
8             generate portrait landscape
9             north east south west handson
10             layout checkerboard scroll
11             chess checkers conway sudoku
12             calculator calendar banner maze
13             beadwork animate list
14             );
15              
16 5     5   4952 use HTML::AutoTag;
  5         52473  
  5         160  
17 5     5   2799 use Spreadsheet::HTML::Engine;
  5         12  
  5         160  
18 5     5   3028 use Spreadsheet::HTML::Presets;
  5         25  
  5         210  
19 5     5   37 use Spreadsheet::HTML::File::Loader;
  5         12  
  5         9574  
20              
21 0     0 1 0 sub portrait { generate( @_, theta => 0 ) }
22 0     0 1 0 sub landscape { generate( @_, theta => -270, tgroups => 0 ) }
23              
24 10     10 1 11771 sub north { generate( @_, theta => 0 ) }
25 10     10 1 11621 sub east { generate( @_, theta => 90, tgroups => 0, pinhead => 1 ) }
26 10     10 1 11872 sub south { generate( @_, theta => -180, tgroups => 0, pinhead => 1 ) }
27 10     10 1 11151 sub west { generate( @_, theta => -270, tgroups => 0 ) }
28              
29 0     0 1 0 sub layout { Spreadsheet::HTML::Presets::layout( @_ ) }
30 0     0 1 0 sub list { Spreadsheet::HTML::Presets::List::list( @_ ) }
31 0     0 1 0 sub select { Spreadsheet::HTML::Presets::List::select( @_ ) }
32 0     0 1 0 sub handson { Spreadsheet::HTML::Presets::Handson::handson( @_ ) }
33 0     0 1 0 sub conway { Spreadsheet::HTML::Presets::Conway::conway( @_ ) }
34 0     0 1 0 sub calculator { Spreadsheet::HTML::Presets::Calculator::calculator( @_ ) }
35 0     0 1 0 sub chess { Spreadsheet::HTML::Presets::Chess::chess( @_ ) }
36 0     0 1 0 sub checkers { Spreadsheet::HTML::Presets::checkers( @_ ) }
37 0     0 1 0 sub tictactoe { Spreadsheet::HTML::Presets::TicTacToe::tictactoe( @_ ) }
38 0     0 1 0 sub sudoku { Spreadsheet::HTML::Presets::Sudoku::sudoku( @_ ) }
39 0     0 1 0 sub checkerboard { Spreadsheet::HTML::Presets::checkerboard( @_ ) }
40 0     0 1 0 sub calendar { Spreadsheet::HTML::Presets::calendar( @_ ) }
41 0     0 1 0 sub scroll { Spreadsheet::HTML::Presets::Scroll::scroll( @_ ) }
42 0     0 1 0 sub animate { Spreadsheet::HTML::Presets::Scroll::animate( @_ ) }
43 0     0 1 0 sub maze { Spreadsheet::HTML::Presets::maze( @_ ) }
44 0     0 1 0 sub banner { Spreadsheet::HTML::Presets::banner( @_ ) }
45 0     0 1 0 sub beadwork { Spreadsheet::HTML::Presets::Beadwork::beadwork( @_ ) }
46              
47             sub generate {
48 119     119 1 79161 my %args = _process( @_ );
49              
50 119 100 66     589 $args{theta} *= -1 if $args{theta} and $args{flip};
51              
52 119 100       566 if (!$args{theta}) { # north
    100          
    100          
    100          
    100          
    100          
    50          
53              
54 29 100       101 $args{data} = $args{flip} ? [ map [ CORE::reverse @$_ ], @{ $args{data} } ] : $args{data};
  7         60  
55              
56             } elsif ($args{theta} == -90) {
57              
58 15         21 $args{data} = [ CORE::reverse @{ _transpose( $args{data} ) }];
  15         39  
59             $args{data} = ($args{pinhead} and !$args{headless})
60 5         49 ? [ map [ @$_[1 .. $#$_], $_->[0] ], @{ $args{data} } ]
61 15 100 100     103 : [ map [ CORE::reverse @$_ ], @{ $args{data} } ];
  10         63  
62              
63             } elsif ($args{theta} == 90) { # east
64              
65 15         40 $args{data} = _transpose( $args{data} );
66             $args{data} = ($args{pinhead} and !$args{headless})
67 5         48 ? [ map [ @$_[1 .. $#$_], $_->[0] ], @{ $args{data} } ]
68 15 100 100     93 : [ map [ CORE::reverse @$_ ], @{ $args{data} } ];
  10         68  
69              
70             } elsif ($args{theta} == -180) { # south
71              
72             $args{data} = ($args{pinhead} and !$args{headless})
73 5         17 ? [ @{ $args{data} }[1 .. $#{ $args{data} }], $args{data}[0] ]
  5         15  
74 15 100 100     67 : [ CORE::reverse @{ $args{data} } ];
  10         33  
75              
76             } elsif ($args{theta} == 180) {
77              
78             $args{data} = ($args{pinhead} and !$args{headless})
79 5         51 ? [ map [ CORE::reverse @$_ ], @{ $args{data} }[1 .. $#{ $args{data} }], $args{data}[0] ]
  5         13  
80 15 100 100     71 : [ map [ CORE::reverse @$_ ], CORE::reverse @{ $args{data} } ];
  10         70  
81              
82             } elsif ($args{theta} == -270) { # west
83              
84 15         23 $args{data} = [@{ _transpose( $args{data} ) }];
  15         36  
85              
86             } elsif ($args{theta} == 270) {
87              
88 15         20 $args{data} = [ CORE::reverse @{ _transpose( $args{data} ) }];
  15         36  
89             }
90              
91 119 50       473 if ($args{animate}) {
92 0         0 warn "animate is deprecated, use scroll instead\n";
93 0         0 $args{scroll} = $args{animate};
94             }
95              
96 119 50       279 if ($args{scroll}) {
97             my ($js, %new_args) = Spreadsheet::HTML::Presets::Scroll::scroll(
98             %args,
99 0         0 data => [ map [ map $_->{cdata}, @$_ ], @{ $args{data} } ],
  0         0  
100             );
101 0         0 for (keys %args) {
102 0 0       0 if (ref $args{$_} eq 'HASH') {
103 0 0       0 $new_args{$_} = { %{ $new_args{$_} || {} }, %{ $args{$_} || {} } };
  0 0       0  
  0         0  
104             }
105             }
106 0         0 my $table = _make_table( _process( %new_args ) );
107 0         0 return $js . $table;
108             }
109              
110 119         391 return _make_table( %args );
111             }
112              
113             sub new {
114 12     12 1 2314 my $class = shift;
115 12 50       70 my %attrs = ref($_[0]) eq 'HASH' ? %{+shift} : @_;
  0         0  
116 12         73 return bless { %attrs }, $class;
117             }
118              
119             sub _process {
120 127     127   337 my ($self,$data,$args) = _args( @_ );
121              
122 127 50 66     433 if ($self and $self->{is_cached}) {
123 0 0       0 return wantarray ? ( data => $self->{data}, %{ $args || {} } ) : $data;
  0 0       0  
124             }
125              
126             # headings is an alias for -r0
127 127 50       373 $args->{-r0} = $args->{headings} if exists $args->{headings};
128              
129             # headings to index mapping (alias for some -cX)
130 127         224 my %index = ();
131 127 100       158 if ($#{ $data->[0] }) {
  127         378  
132 120   50     171 %index = map { '-' . ($data->[0][$_] || '') => $_ } 0 .. $#{ $data->[0] };
  492         1980  
  120         258  
133 120         812 for (grep /^-/, keys %$args) {
134 0 0       0 $args->{"-c$index{$_}" } = $args->{$_} if exists $index{$_};
135             }
136             }
137              
138 127 100       407 my $empty = exists $args->{empty} ? $args->{empty} : ' ';
139 127 100 66     495 my $tag = ($args->{headless} or $args->{matrix}) ? 'td' : 'th';
140 127         459 for my $row (0 .. $args->{_max_rows} - 1) {
141              
142 576 50       1495 unless ($args->{_layout}) {
143 576         883 push @{ $data->[$row] }, undef for 1 .. $args->{_max_cols} - $#{ $data->[$row] } + 1; # pad
  576         1605  
  1152         2868  
144 576         991 pop @{ $data->[$row] } for $args->{_max_cols} .. $#{ $data->[$row] }; # truncate
  576         1474  
  1152         2439  
145             }
146              
147 576         937 for my $col (0 .. $#{ $data->[$row] }) {
  576         1231  
148              
149 2292         3870 my ( $cdata, $attr ) = ( $data->[$row][$col], undef );
150 2292         6068 for ($tag, "-c$col", "-r$row", "-r${row}c${col}") {
151 9168 50       28206 next unless exists $args->{$_};
152 0         0 ( $cdata, $attr ) = _extrapolate( $cdata, $attr, $args->{$_} );
153             }
154              
155 5     5   36 do{ no warnings;
  5         17  
  5         10390  
  2292         2806  
156 2292 100 66     8340 $cdata = HTML::Entities::encode_entities( $cdata, $args->{encodes} ) if $args->{encode} || exists $args->{encodes};
157 2292         6099 $cdata =~ s/^\s*$/$empty/g;
158             };
159              
160 2292 50       13512 $data->[$row][$col] = {
    50          
161             tag => $tag,
162             (defined( $cdata ) ? (cdata => $cdata) : ()),
163             (keys( %$attr ) ? (attr => $attr) : ()),
164             };
165             }
166 576         1284 $tag = 'td';
167             }
168              
169 127 0 33     411 if ($args->{cache} and $self and !$self->{is_cached}) {
      33        
170 0         0 $self->{data} = $data;
171 0         0 $self->{is_cached} = 1;
172             }
173              
174 127 100       297 shift @$data if $args->{headless};
175              
176 127 100       1258 return wantarray ? ( data => $data, %$args ) : $data;
177             }
178              
179             sub _make_table {
180 119     119   434 my %args = @_;
181              
182 119   66     407 my @cdata = ( _tag( %args, tag => 'caption' ) || (), _colgroup( %args ) );
183              
184 119 50       342 if ($args{tgroups}) {
185              
186 0         0 my @body = @{ $args{data} };
  0         0  
187 0 0 0     0 my $head = shift @body unless $args{matrix} and scalar @{ $args{data} } > 2;
  0         0  
188 0 0 0     0 my $foot = pop @body if !$args{matrix} and $args{tgroups} > 1 and scalar @{ $args{data} } > 2;
  0   0     0  
189              
190 0         0 my $head_row = { tag => 'tr', attr => $args{'thead.tr'}, cdata => $head };
191 0         0 my $foot_row = { tag => 'tr', attr => $args{'tfoot.tr'}, cdata => $foot };
192 0         0 my $body_rows = [ map { tag => 'tr', attr => $args{tr}, cdata => $_ }, @body ];
193              
194 0 0 0     0 if (int($args{group} || 0) > 1) {
195             $body_rows = [
196             map [ @$body_rows[$_ .. $_ + $args{group} - 1] ],
197             _range( 0, $#$body_rows, $args{group} )
198 0         0 ];
199 0         0 pop @{ $body_rows->[-1] } while !defined $body_rows->[-1][-1];
  0         0  
200             } else {
201 0         0 $body_rows = [ $body_rows ];
202             }
203              
204             push @cdata, (
205             ( $head ? { tag => 'thead', attr => $args{thead}, cdata => $head_row } : () ),
206             ( $foot ? { tag => 'tfoot', attr => $args{tfoot}, cdata => $foot_row } : () ),
207 0 0       0 ( map { tag => 'tbody', attr => $args{tbody}, cdata => $_ }, @$body_rows ),
    0          
208             );
209              
210              
211             } else {
212 119         165 push @cdata, map { tag => 'tr', attr => $args{tr}, cdata => $_ }, @{ $args{data} };
  119         1052  
213             }
214              
215 119         604 return $args{_auto}->tag( tag => 'table', attr => $args{table}, cdata => \@cdata );
216             }
217              
218             sub _args {
219 127     127   197 my ($self,@data,$data,@args,$args);
220 127 100       851 $self = shift if UNIVERSAL::isa( $_[0], __PACKAGE__ );
221 127 50       340 $data = shift if (@_ == 1);
222              
223 127         325 while (@_) {
224 345 100       727 if (ref( $_[0] )) {
225 60         100 push @data, shift;
226 60 100       172 if (ref( $_[0] )) {
    50          
227 30         89 push @data, shift;
228             } elsif (defined $_[0]) {
229 30         103 push @args, shift, shift;
230             }
231             } else {
232 285         864 push @args, shift, shift;
233             }
234             }
235              
236 127 100 66     838 $data ||= (@data == 1) ? $data[0] : (@data) ? [ @data ] : undef;
    100          
237 127 100       608 $args = scalar @args ? { @args } : {};
238 127 100       196 $args = { %{ $self || {} }, %{ $args || {} } };
  127 50       582  
  127         579  
239 127 100       548 $data = delete $args->{data} if exists $args->{data};
240              
241             $args->{_auto} ||= HTML::AutoTag->new(
242             indent => $args->{indent},
243             level => $args->{level},
244             sorted => $args->{sorted_attrs},
245 127   33     981 );
246              
247 127 50 66     2986 return ( $self, $self->{data}, $args ) if $self and $self->{is_cached};
248              
249 127   50     526 $args->{worksheet} ||= 1;
250 127 50       316 $args->{worksheet} = 1 if $args->{worksheet} < 1;
251 127 100       314 if ($args->{file}) {
252 5         18 $data = Spreadsheet::HTML::File::Loader::_parse( $args, $data );
253 5 50       20 unlink $args->{file} if $args->{_unlink};
254             }
255              
256 127 100       389 $data = [ $data ] unless ref($data) eq 'ARRAY';
257 127 100       352 $data = [ $data ] unless ref($data->[0]) eq 'ARRAY';
258              
259 127 50 33     365 if ($args->{wrap} and defined $data->[0][0]) {
260 0         0 my @flat = map @$_, @$data;
261             $data = [
262             map [ @flat[$_ .. $_ + $args->{wrap} - 1] ],
263             _range( 0, $#flat, $args->{wrap} )
264 0         0 ];
265             }
266              
267 127 50       313 $data = Spreadsheet::HTML::Engine::_apply( $data, $args->{apply} ) if $args->{apply};
268              
269 127   50     176 $args->{_max_rows} = scalar @{ $data } || 1;
270 127   50     200 $args->{_max_cols} = scalar @{ $data->[0] } || 1;
271              
272 127 50       316 if ($args->{fill}) {
273 0         0 my ($row,$col) = split /\D/, $args->{fill};
274 0 0 0     0 $args->{_max_rows} = $row if (int($row || 0)) > ($args->{_max_rows});
275 0 0 0     0 $args->{_max_cols} = $col if (int($col || 0)) > ($args->{_max_cols});
276             }
277              
278 127         1262 return ( $self, [ map [@$_], @$data], $args );
279             }
280              
281             sub _extrapolate {
282 0     0   0 my ( $cdata, $attr, $thingy ) = @_;
283 0         0 my $new_attr;
284 0 0       0 $thingy = [ $thingy ] unless ref( $thingy ) eq 'ARRAY';
285 0         0 for (@{ $thingy }) {
  0         0  
286 0 0       0 if (ref($_) eq 'CODE') {
    0          
287 0         0 $cdata = $_->($cdata);
288             } elsif (ref($_) eq 'HASH') {
289 0         0 $new_attr = $_;
290             }
291             }
292 0 0       0 $attr = { %{ $attr || {} }, %{ $new_attr || {} } };
  0 0       0  
  0         0  
293 0         0 return ( $cdata, $attr );
294             }
295              
296             sub _colgroup {
297 119     119   348 my %args = @_;
298              
299 119         170 my @colgroup;
300 119 50       347 $args{col} = [ $args{col} ] if ref($args{col}) eq 'HASH';
301              
302 119 50       316 if (ref($args{col}) eq 'ARRAY') {
303              
304 0 0       0 if (ref $args{colgroup} eq 'ARRAY') {
305             @colgroup = map {
306             tag => 'colgroup',
307             attr => $_,
308 0         0 cdata => [ map { tag => 'col', attr => $_ }, @{ $args{col} } ]
309 0         0 }, @{ $args{colgroup} };
  0         0  
310             } else {
311             @colgroup = {
312             tag => 'colgroup',
313             attr => $args{colgroup},
314 0         0 cdata => [ map { tag => 'col', attr => $_ }, @{ $args{col} } ]
  0         0  
315             };
316             }
317              
318             } else {
319              
320 119 50       284 $args{colgroup} = [ $args{colgroup} ] if ref($args{colgroup}) eq 'HASH';
321 119 50       314 if (ref $args{colgroup} eq 'ARRAY') {
322 0         0 @colgroup = map { tag => 'colgroup', attr => $_ }, @{ $args{colgroup} };
  0         0  
323             }
324             }
325              
326 119         362 return @colgroup;
327             }
328              
329             sub _tag {
330 119     119   417 my %args = @_;
331 119         235 my $thingy = $args{ $args{tag} };
332 119 50       995 return unless defined $thingy;
333 0         0 my $tag = { tag => $args{tag}, cdata => $thingy };
334 0 0       0 if (ref $thingy eq 'HASH') {
335 0         0 $tag->{cdata} = ( keys %$thingy )[0];
336 0         0 $tag->{attr} = ( values %$thingy )[0];
337             }
338 0         0 return $tag;
339             }
340              
341             # credit: Math::Matrix
342             sub _transpose {
343 60     60   93 my $data = shift;
344 60         85 my @trans;
345 60         75 for my $i (0 .. $#{ $data->[0] }) {
  60         163  
346 240         1057 push @trans, [ map $_->[$i], @$data ]
347             }
348 60         216 return \@trans;
349             }
350              
351 0   0 0     sub _range {grep!(($_-$_[0])%($_[2]||1)),$_[0]..$_[1]}
352              
353              
354             1;
355              
356             __END__