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   132485 use strict;
  5         7  
  5         130  
3 5     5   14 use warnings FATAL => 'all';
  5         6  
  5         225  
4             our $VERSION = '1.20';
5              
6 5     5   18 use Exporter 'import';
  5         7  
  5         229  
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   2102 use HTML::AutoTag;
  5         30715  
  5         134  
17 5     5   1658 use Spreadsheet::HTML::Engine;
  5         7  
  5         121  
18 5     5   1792 use Spreadsheet::HTML::Presets;
  5         10  
  5         136  
19 5     5   20 use Spreadsheet::HTML::File::Loader;
  5         5  
  5         4893  
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 7557 sub north { generate( @_, theta => 0 ) }
25 10     10 1 7279 sub east { generate( @_, theta => 90, tgroups => 0, pinhead => 1 ) }
26 10     10 1 7420 sub south { generate( @_, theta => -180, tgroups => 0, pinhead => 1 ) }
27 10     10 1 7078 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 50141 my %args = _process( @_ );
48              
49 119 100 66     348 $args{theta} *= -1 if $args{theta} and $args{flip};
50              
51 119 100       354 if (!$args{theta}) { # north
    100          
    100          
    100          
    100          
    100          
    50          
52              
53 29 100       52 $args{data} = $args{flip} ? [ map [ CORE::reverse @$_ ], @{ $args{data} } ] : $args{data};
  7         37  
54              
55             } elsif ($args{theta} == -90) {
56              
57 15         15 $args{data} = [ CORE::reverse @{ _transpose( $args{data} ) }];
  15         23  
58             $args{data} = ($args{pinhead} and !$args{headless})
59 5         31 ? [ map [ @$_[1 .. $#$_], $_->[0] ], @{ $args{data} } ]
60 15 100 100     72 : [ map [ CORE::reverse @$_ ], @{ $args{data} } ];
  10         40  
61              
62             } elsif ($args{theta} == 90) { # east
63              
64 15         27 $args{data} = _transpose( $args{data} );
65             $args{data} = ($args{pinhead} and !$args{headless})
66 5         37 ? [ map [ @$_[1 .. $#$_], $_->[0] ], @{ $args{data} } ]
67 15 100 100     61 : [ map [ CORE::reverse @$_ ], @{ $args{data} } ];
  10         40  
68              
69             } elsif ($args{theta} == -180) { # south
70              
71             $args{data} = ($args{pinhead} and !$args{headless})
72 5         13 ? [ @{ $args{data} }[1 .. $#{ $args{data} }], $args{data}[0] ]
  5         9  
73 15 100 100     45 : [ CORE::reverse @{ $args{data} } ];
  10         24  
74              
75             } elsif ($args{theta} == 180) {
76              
77             $args{data} = ($args{pinhead} and !$args{headless})
78 5         31 ? [ map [ CORE::reverse @$_ ], @{ $args{data} }[1 .. $#{ $args{data} }], $args{data}[0] ]
  5         10  
79 15 100 100     46 : [ map [ CORE::reverse @$_ ], CORE::reverse @{ $args{data} } ];
  10         52  
80              
81             } elsif ($args{theta} == -270) { # west
82              
83 15         15 $args{data} = [@{ _transpose( $args{data} ) }];
  15         25  
84              
85             } elsif ($args{theta} == 270) {
86              
87 15         13 $args{data} = [ CORE::reverse @{ _transpose( $args{data} ) }];
  15         25  
88             }
89              
90 119 50       270 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         249 return _make_table( %args );
105             }
106              
107             sub new {
108 12     12 1 352410 my $class = shift;
109 12 50       57 my %attrs = ref($_[0]) eq 'HASH' ? %{+shift} : @_;
  0         0  
110 12         48 return bless { %attrs }, $class;
111             }
112              
113             sub _process {
114 127     127   218 my ($self,$data,$args) = _args( @_ );
115              
116 127 50 66     277 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       179 $args->{-r0} = $args->{headings} if exists $args->{headings};
122              
123             # headings to index mapping (alias for some -cX)
124 127         166 my %index = ();
125 127 100       78 if ($#{ $data->[0] }) {
  127         246  
126 120   50     115 %index = map { '-' . ($data->[0][$_] || '') => $_ } 0 .. $#{ $data->[0] };
  492         1127  
  120         155  
127 120         572 for (grep /^-/, keys %$args) {
128 0 0       0 $args->{"-c$index{$_}" } = $args->{$_} if exists $index{$_};
129             }
130             }
131              
132 127 100       245 my $empty = exists $args->{empty} ? $args->{empty} : ' ';
133 127 100 66     299 my $tag = ($args->{headless} or $args->{matrix}) ? 'td' : 'th';
134 127         255 for my $row (0 .. $args->{_max_rows} - 1) {
135              
136 576 50       769 unless ($args->{_layout}) {
137 576         432 push @{ $data->[$row] }, undef for 1 .. $args->{_max_cols} - $#{ $data->[$row] } + 1; # pad
  576         975  
  1152         1495  
138 576         466 pop @{ $data->[$row] } for $args->{_max_cols} .. $#{ $data->[$row] }; # truncate
  576         814  
  1152         1220  
139             }
140              
141 576         405 for my $col (0 .. $#{ $data->[$row] }) {
  576         676  
142              
143 2292         2060 my ( $cdata, $attr ) = ( $data->[$row][$col], undef );
144 2292         3455 for ($tag, "-c$col", "-r$row", "-r${row}c${col}") {
145 9168 50       13603 next unless exists $args->{$_};
146 0         0 ( $cdata, $attr ) = _extrapolate( $cdata, $attr, $args->{$_} );
147             }
148              
149 5     5   26 do{ no warnings;
  5         18  
  5         6063  
  2292         1387  
150 2292 100 66     4400 $cdata = HTML::Entities::encode_entities( $cdata, $args->{encodes} ) if $args->{encode} || exists $args->{encodes};
151 2292         3682 $cdata =~ s/^\s*$/$empty/g;
152             };
153              
154 2292 50       6894 $data->[$row][$col] = {
    50          
155             tag => $tag,
156             (defined( $cdata ) ? (cdata => $cdata) : ()),
157             (keys( %$attr ) ? (attr => $attr) : ()),
158             };
159             }
160 576         640 $tag = 'td';
161             }
162              
163 127 0 33     256 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       184 shift @$data if $args->{headless};
169              
170 127 100       804 return wantarray ? ( data => $data, %$args ) : $data;
171             }
172              
173             sub _make_table {
174 119     119   281 my %args = @_;
175              
176 119   66     258 my @cdata = ( _tag( %args, tag => 'caption' ) || (), _colgroup( %args ) );
177              
178 119 50       197 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         630  
207             }
208              
209 119         434 return $args{_auto}->tag( tag => 'table', attr => $args{table}, cdata => \@cdata );
210             }
211              
212             sub _args {
213 127     127   120 my ($self,@data,$data,@args,$args);
214 127 100       500 $self = shift if UNIVERSAL::isa( $_[0], __PACKAGE__ );
215 127 50       209 $data = shift if (@_ == 1);
216              
217 127         204 while (@_) {
218 345 100       422 if (ref( $_[0] )) {
219 60         61 push @data, shift;
220 60 100       91 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         482 push @args, shift, shift;
227             }
228             }
229              
230 127 100 66     529 $data ||= (@data == 1) ? $data[0] : (@data) ? [ @data ] : undef;
    100          
231 127 100       325 $args = scalar @args ? { @args } : {};
232 127 100       99 $args = { %{ $self || {} }, %{ $args || {} } };
  127 50       375  
  127         377  
233 127 100       350 $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     633 );
240              
241 127 50 66     1883 return ( $self, $self->{data}, $args ) if $self and $self->{is_cached};
242              
243 127   50     322 $args->{worksheet} ||= 1;
244 127 50       179 $args->{worksheet} = 1 if $args->{worksheet} < 1;
245 127 100       212 if ($args->{file}) {
246 5         12 $data = Spreadsheet::HTML::File::Loader::_parse( $args, $data );
247 5 50       14 unlink $args->{file} if $args->{_unlink};
248             }
249              
250 127 100       222 $data = [ $data ] unless ref($data) eq 'ARRAY';
251 127 100       224 $data = [ $data ] unless ref($data->[0]) eq 'ARRAY';
252              
253 127 50 33     224 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       154 $data = Spreadsheet::HTML::Engine::_apply( $data, $args->{apply} ) if $args->{apply};
262              
263 127   50     96 $args->{_max_rows} = scalar @{ $data } || 1;
264 127   50     89 $args->{_max_cols} = scalar @{ $data->[0] } || 1;
265              
266 127 50       200 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         818 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   198 my %args = @_;
292              
293 119         96 my @colgroup;
294 119 50       205 $args{col} = [ $args{col} ] if ref($args{col}) eq 'HASH';
295              
296 119 50       147 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       165 $args{colgroup} = [ $args{colgroup} ] if ref($args{colgroup}) eq 'HASH';
315 119 50       176 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   233 my %args = @_;
325 119         136 my $thingy = $args{ $args{tag} };
326 119 50       653 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         43 my @trans;
339 60         40 for my $i (0 .. $#{ $data->[0] }) {
  60         114  
340 240         578 push @trans, [ map $_->[$i], @$data ]
341             }
342 60         123 return \@trans;
343             }
344              
345 0   0 0     sub _range {grep!(($_-$_[0])%($_[2]||1)),$_[0]..$_[1]}
346              
347              
348             1;
349              
350             __END__