File Coverage

blib/lib/Spreadsheet/HTML.pm
Criterion Covered Total %
statement 145 225 64.4
branch 82 148 55.4
condition 34 70 48.5
subroutine 20 40 50.0
pod 24 24 100.0
total 305 507 60.1


line stmt bran cond sub pod time code
1             package Spreadsheet::HTML;
2 5     5   134913 use strict;
  5         9  
  5         131  
3 5     5   16 use warnings FATAL => 'all';
  5         4  
  5         205  
4             our $VERSION = '1.19';
5              
6 5     5   15 use Exporter 'import';
  5         9  
  5         230  
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 list
14             );
15              
16 5     5   2073 use HTML::AutoTag;
  5         31540  
  5         116  
17 5     5   1677 use Spreadsheet::HTML::Engine;
  5         5  
  5         126  
18 5     5   1808 use Spreadsheet::HTML::Presets;
  5         19  
  5         149  
19 5     5   24 use Spreadsheet::HTML::File::Loader;
  5         6  
  5         5545  
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 7190 sub north { generate( @_, theta => 0 ) }
25 10     10 1 7275 sub east { generate( @_, theta => 90, tgroups => 0, pinhead => 1 ) }
26 10     10 1 7273 sub south { generate( @_, theta => -180, tgroups => 0, pinhead => 1 ) }
27 10     10 1 7020 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 maze { Spreadsheet::HTML::Presets::maze( @_ ) }
43 0     0 1 0 sub banner { Spreadsheet::HTML::Presets::banner( @_ ) }
44 0     0 1 0 sub beadwork { Spreadsheet::HTML::Presets::Beadwork::beadwork( @_ ) }
45              
46             sub generate {
47 119     119 1 49736 my %args = _process( @_ );
48              
49 119 100 66     342 $args{theta} *= -1 if $args{theta} and $args{flip};
50              
51 119 100       337 if (!$args{theta}) { # north
    100          
    100          
    100          
    100          
    100          
    50          
52              
53 29 100       54 $args{data} = $args{flip} ? [ map [ CORE::reverse @$_ ], @{ $args{data} } ] : $args{data};
  7         35  
54              
55             } elsif ($args{theta} == -90) {
56              
57 15         14 $args{data} = [ CORE::reverse @{ _transpose( $args{data} ) }];
  15         27  
58             $args{data} = ($args{pinhead} and !$args{headless})
59 5         32 ? [ map [ @$_[1 .. $#$_], $_->[0] ], @{ $args{data} } ]
60 15 100 100     69 : [ map [ CORE::reverse @$_ ], @{ $args{data} } ];
  10         39  
61              
62             } elsif ($args{theta} == 90) { # east
63              
64 15         30 $args{data} = _transpose( $args{data} );
65             $args{data} = ($args{pinhead} and !$args{headless})
66 5         36 ? [ map [ @$_[1 .. $#$_], $_->[0] ], @{ $args{data} } ]
67 15 100 100     60 : [ map [ CORE::reverse @$_ ], @{ $args{data} } ];
  10         45  
68              
69             } elsif ($args{theta} == -180) { # south
70              
71             $args{data} = ($args{pinhead} and !$args{headless})
72 5         12 ? [ @{ $args{data} }[1 .. $#{ $args{data} }], $args{data}[0] ]
  5         8  
73 15 100 100     48 : [ CORE::reverse @{ $args{data} } ];
  10         21  
74              
75             } elsif ($args{theta} == 180) {
76              
77             $args{data} = ($args{pinhead} and !$args{headless})
78 5         30 ? [ map [ CORE::reverse @$_ ], @{ $args{data} }[1 .. $#{ $args{data} }], $args{data}[0] ]
  5         10  
79 15 100 100     47 : [ map [ CORE::reverse @$_ ], CORE::reverse @{ $args{data} } ];
  10         54  
80              
81             } elsif ($args{theta} == -270) { # west
82              
83 15         16 $args{data} = [@{ _transpose( $args{data} ) }];
  15         23  
84              
85             } elsif ($args{theta} == 270) {
86              
87 15         14 $args{data} = [ CORE::reverse @{ _transpose( $args{data} ) }];
  15         25  
88             }
89              
90 119 50       259 if ($args{scroll}) {
91             my ($js, %new_args) = Spreadsheet::HTML::Presets::Scroll::scroll(
92             %args,
93 0         0 data => [ map [ map $_->{cdata}, @$_ ], @{ $args{data} } ],
  0         0  
94             );
95 0         0 for (keys %args) {
96 0 0       0 if (ref $args{$_} eq 'HASH') {
97 0 0       0 $new_args{$_} = { %{ $new_args{$_} || {} }, %{ $args{$_} || {} } };
  0 0       0  
  0         0  
98             }
99             }
100 0         0 my $table = _make_table( _process( %new_args ) );
101 0         0 return $js . $table;
102             }
103              
104 119         246 return _make_table( %args );
105             }
106              
107             sub new {
108 12     12 1 331052 my $class = shift;
109 12 50       57 my %attrs = ref($_[0]) eq 'HASH' ? %{+shift} : @_;
  0         0  
110 12         59 return bless { %attrs }, $class;
111             }
112              
113             sub _process {
114 127     127   274 my ($self,$data,$args) = _args( @_ );
115              
116 127 50 66     289 if ($self and $self->{is_cached}) {
117 0 0       0 return wantarray ? ( data => $self->{data}, %{ $args || {} } ) : $data;
  0 0       0  
118             }
119              
120             # headings is an alias for -r0
121 127 50       199 $args->{-r0} = $args->{headings} if exists $args->{headings};
122              
123             # headings to index mapping (alias for some -cX)
124 127         155 my %index = ();
125 127 100       92 if ($#{ $data->[0] }) {
  127         232  
126 120   50     122 %index = map { '-' . ($data->[0][$_] || '') => $_ } 0 .. $#{ $data->[0] };
  492         1145  
  120         180  
127 120         581 for (grep /^-/, keys %$args) {
128 0 0       0 $args->{"-c$index{$_}" } = $args->{$_} if exists $index{$_};
129             }
130             }
131              
132 127 100       252 my $empty = exists $args->{empty} ? $args->{empty} : ' ';
133 127 100 66     346 my $tag = ($args->{headless} or $args->{matrix}) ? 'td' : 'th';
134 127         278 for my $row (0 .. $args->{_max_rows} - 1) {
135              
136 576 50       754 unless ($args->{_layout}) {
137 576         431 push @{ $data->[$row] }, undef for 1 .. $args->{_max_cols} - $#{ $data->[$row] } + 1; # pad
  576         957  
  1152         1428  
138 576         461 pop @{ $data->[$row] } for $args->{_max_cols} .. $#{ $data->[$row] }; # truncate
  576         757  
  1152         1190  
139             }
140              
141 576         436 for my $col (0 .. $#{ $data->[$row] }) {
  576         689  
142              
143 2292         1895 my ( $cdata, $attr ) = ( $data->[$row][$col], undef );
144 2292         3192 for ($tag, "-c$col", "-r$row", "-r${row}c${col}") {
145 9168 50       13357 next unless exists $args->{$_};
146 0         0 ( $cdata, $attr ) = _extrapolate( $cdata, $attr, $args->{$_} );
147             }
148              
149 5     5   35 do{ no warnings;
  5         29  
  5         6365  
  2292         1418  
150 2292 100 66     4335 $cdata = HTML::Entities::encode_entities( $cdata, $args->{encodes} ) if $args->{encode} || exists $args->{encodes};
151 2292         3500 $cdata =~ s/^\s*$/$empty/g;
152             };
153              
154 2292 50       6549 $data->[$row][$col] = {
    50          
155             tag => $tag,
156             (defined( $cdata ) ? (cdata => $cdata) : ()),
157             (keys( %$attr ) ? (attr => $attr) : ()),
158             };
159             }
160 576         626 $tag = 'td';
161             }
162              
163 127 0 33     240 if ($args->{cache} and $self and !$self->{is_cached}) {
      33        
164 0         0 $self->{data} = $data;
165 0         0 $self->{is_cached} = 1;
166             }
167              
168 127 100       181 shift @$data if $args->{headless};
169              
170 127 100       785 return wantarray ? ( data => $data, %$args ) : $data;
171             }
172              
173             sub _make_table {
174 119     119   266 my %args = @_;
175              
176 119   66     256 my @cdata = ( _tag( %args, tag => 'caption' ) || (), _colgroup( %args ) );
177              
178 119 50       205 if ($args{tgroups}) {
179              
180 0         0 my @body = @{ $args{data} };
  0         0  
181 0 0 0     0 my $head = shift @body unless $args{matrix} and scalar @{ $args{data} } > 2;
  0         0  
182 0 0 0     0 my $foot = pop @body if !$args{matrix} and $args{tgroups} > 1 and scalar @{ $args{data} } > 2;
  0   0     0  
183              
184 0         0 my $head_row = { tag => 'tr', attr => $args{'thead.tr'}, cdata => $head };
185 0         0 my $foot_row = { tag => 'tr', attr => $args{'tfoot.tr'}, cdata => $foot };
186 0         0 my $body_rows = [ map { tag => 'tr', attr => $args{tr}, cdata => $_ }, @body ];
187              
188 0 0 0     0 if (int($args{group} || 0) > 1) {
189             $body_rows = [
190             map [ @$body_rows[$_ .. $_ + $args{group} - 1] ],
191             _range( 0, $#$body_rows, $args{group} )
192 0         0 ];
193 0         0 pop @{ $body_rows->[-1] } while !defined $body_rows->[-1][-1];
  0         0  
194             } else {
195 0         0 $body_rows = [ $body_rows ];
196             }
197              
198             push @cdata, (
199             ( $head ? { tag => 'thead', attr => $args{thead}, cdata => $head_row } : () ),
200             ( $foot ? { tag => 'tfoot', attr => $args{tfoot}, cdata => $foot_row } : () ),
201 0 0       0 ( map { tag => 'tbody', attr => $args{tbody}, cdata => $_ }, @$body_rows ),
    0          
202             );
203              
204              
205             } else {
206 119         86 push @cdata, map { tag => 'tr', attr => $args{tr}, cdata => $_ }, @{ $args{data} };
  119         573  
207             }
208              
209 119         393 return $args{_auto}->tag( tag => 'table', attr => $args{table}, cdata => \@cdata );
210             }
211              
212             sub _args {
213 127     127   125 my ($self,@data,$data,@args,$args);
214 127 100       506 $self = shift if UNIVERSAL::isa( $_[0], __PACKAGE__ );
215 127 50       212 $data = shift if (@_ == 1);
216              
217 127         200 while (@_) {
218 345 100       394 if (ref( $_[0] )) {
219 60         56 push @data, shift;
220 60 100       102 if (ref( $_[0] )) {
    50          
221 30         49 push @data, shift;
222             } elsif (defined $_[0]) {
223 30         62 push @args, shift, shift;
224             }
225             } else {
226 285         501 push @args, shift, shift;
227             }
228             }
229              
230 127 100 66     578 $data ||= (@data == 1) ? $data[0] : (@data) ? [ @data ] : undef;
    100          
231 127 100       341 $args = scalar @args ? { @args } : {};
232 127 100       105 $args = { %{ $self || {} }, %{ $args || {} } };
  127 50       404  
  127         401  
233 127 100       355 $data = delete $args->{data} if exists $args->{data};
234              
235             $args->{_auto} ||= HTML::AutoTag->new(
236             indent => $args->{indent},
237             level => $args->{level},
238             sorted => $args->{sorted_attrs},
239 127   33     692 );
240              
241 127 50 66     1920 return ( $self, $self->{data}, $args ) if $self and $self->{is_cached};
242              
243 127   50     335 $args->{worksheet} ||= 1;
244 127 50       197 $args->{worksheet} = 1 if $args->{worksheet} < 1;
245 127 100       190 if ($args->{file}) {
246 5         13 $data = Spreadsheet::HTML::File::Loader::_parse( $args, $data );
247 5 50       15 unlink $args->{file} if $args->{_unlink};
248             }
249              
250 127 100       241 $data = [ $data ] unless ref($data) eq 'ARRAY';
251 127 100       213 $data = [ $data ] unless ref($data->[0]) eq 'ARRAY';
252              
253 127 50 33     223 if ($args->{wrap} and defined $data->[0][0]) {
254 0         0 my @flat = map @$_, @$data;
255             $data = [
256             map [ @flat[$_ .. $_ + $args->{wrap} - 1] ],
257             _range( 0, $#flat, $args->{wrap} )
258 0         0 ];
259             }
260              
261 127 50       196 $data = Spreadsheet::HTML::Engine::_apply( $data, $args->{apply} ) if $args->{apply};
262              
263 127   50     94 $args->{_max_rows} = scalar @{ $data } || 1;
264 127   50     105 $args->{_max_cols} = scalar @{ $data->[0] } || 1;
265              
266 127 50       220 if ($args->{fill}) {
267 0         0 my ($row,$col) = split /\D/, $args->{fill};
268 0 0 0     0 $args->{_max_rows} = $row if (int($row || 0)) > ($args->{_max_rows});
269 0 0 0     0 $args->{_max_cols} = $col if (int($col || 0)) > ($args->{_max_cols});
270             }
271              
272 127         811 return ( $self, [ map [@$_], @$data], $args );
273             }
274              
275             sub _extrapolate {
276 0     0   0 my ( $cdata, $attr, $thingy ) = @_;
277 0         0 my $new_attr;
278 0 0       0 $thingy = [ $thingy ] unless ref( $thingy ) eq 'ARRAY';
279 0         0 for (@{ $thingy }) {
  0         0  
280 0 0       0 if (ref($_) eq 'CODE') {
    0          
281 0         0 $cdata = $_->($cdata);
282             } elsif (ref($_) eq 'HASH') {
283 0         0 $new_attr = $_;
284             }
285             }
286 0 0       0 $attr = { %{ $attr || {} }, %{ $new_attr || {} } };
  0 0       0  
  0         0  
287 0         0 return ( $cdata, $attr );
288             }
289              
290             sub _colgroup {
291 119     119   195 my %args = @_;
292              
293 119         80 my @colgroup;
294 119 50       201 $args{col} = [ $args{col} ] if ref($args{col}) eq 'HASH';
295              
296 119 50       146 if (ref($args{col}) eq 'ARRAY') {
297              
298 0 0       0 if (ref $args{colgroup} eq 'ARRAY') {
299             @colgroup = map {
300             tag => 'colgroup',
301             attr => $_,
302 0         0 cdata => [ map { tag => 'col', attr => $_ }, @{ $args{col} } ]
303 0         0 }, @{ $args{colgroup} };
  0         0  
304             } else {
305             @colgroup = {
306             tag => 'colgroup',
307             attr => $args{colgroup},
308 0         0 cdata => [ map { tag => 'col', attr => $_ }, @{ $args{col} } ]
  0         0  
309             };
310             }
311              
312             } else {
313              
314 119 50       156 $args{colgroup} = [ $args{colgroup} ] if ref($args{colgroup}) eq 'HASH';
315 119 50       171 if (ref $args{colgroup} eq 'ARRAY') {
316 0         0 @colgroup = map { tag => 'colgroup', attr => $_ }, @{ $args{colgroup} };
  0         0  
317             }
318             }
319              
320 119         201 return @colgroup;
321             }
322              
323             sub _tag {
324 119     119   214 my %args = @_;
325 119         140 my $thingy = $args{ $args{tag} };
326 119 50       660 return unless defined $thingy;
327 0         0 my $tag = { tag => $args{tag}, cdata => $thingy };
328 0 0       0 if (ref $thingy eq 'HASH') {
329 0         0 $tag->{cdata} = ( keys %$thingy )[0];
330 0         0 $tag->{attr} = ( values %$thingy )[0];
331             }
332 0         0 return $tag;
333             }
334              
335             # credit: Math::Matrix
336             sub _transpose {
337 60     60   54 my $data = shift;
338 60         45 my @trans;
339 60         44 for my $i (0 .. $#{ $data->[0] }) {
  60         100  
340 240         571 push @trans, [ map $_->[$i], @$data ]
341             }
342 60         131 return \@trans;
343             }
344              
345 0   0 0     sub _range {grep!(($_-$_[0])%($_[2]||1)),$_[0]..$_[1]}
346              
347              
348             1;
349              
350             __END__