File Coverage

blib/lib/Spreadsheet/HTML/Presets.pm
Criterion Covered Total %
statement 47 201 23.3
branch 0 104 0.0
condition 0 27 0.0
subroutine 17 25 68.0
pod 6 6 100.0
total 70 363 19.2


line stmt bran cond sub pod time code
1             package Spreadsheet::HTML::Presets;
2 5     5   25 use strict;
  5         38  
  5         156  
3 5     5   25 use warnings FATAL => 'all';
  5         7  
  5         153  
4              
5 5     5   26 use Spreadsheet::HTML;
  5         7  
  5         212  
6 5     5   2866 use Spreadsheet::HTML::Presets::List;
  5         13  
  5         166  
7 5     5   2946 use Spreadsheet::HTML::Presets::Scroll;
  5         17  
  5         142  
8 5     5   3239 use Spreadsheet::HTML::Presets::Beadwork;
  5         15  
  5         167  
9 5     5   3182 use Spreadsheet::HTML::Presets::Calculator;
  5         14  
  5         152  
10 5     5   2961 use Spreadsheet::HTML::Presets::Conway;
  5         13  
  5         156  
11 5     5   2786 use Spreadsheet::HTML::Presets::Chess;
  5         13  
  5         143  
12 5     5   2808 use Spreadsheet::HTML::Presets::TicTacToe;
  5         13  
  5         142  
13 5     5   2778 use Spreadsheet::HTML::Presets::Handson;
  5         12  
  5         154  
14 5     5   2944 use Spreadsheet::HTML::Presets::Sudoku;
  5         17  
  5         6231  
15              
16 5     5   1748 eval "use JavaScript::Minifier";
  0         0  
  0         0  
17             our $NO_MINIFY = $@;
18 5     5   1634 eval "use Text::FIGlet";
  0         0  
  0         0  
19             our $NO_FIGLET = $@;
20 5     5   11220 eval "use Time::Piece";
  5         75176  
  5         30  
21             our $NO_TIMEPIECE = $@;
22 5     5   52 eval "use List::Util";
  5         36  
  5         331  
23             our $NO_LISTUTIL = $@;
24              
25             sub layout {
26 0     0 1   my ($self,$data,$args);
27 0 0         $self = shift if ref($_[0]) =~ /^Spreadsheet::HTML/;
28 0 0         ($self,$data,$args) = $self ? $self->_args( @_ ) : Spreadsheet::HTML::_args( @_ );
29              
30             my @args = (
31             @_,
32             table => {
33 0 0         %{ $args->{table} || {} },
34             role => 'presentation',
35 0           ( map {$_ => 0} qw( border cellspacing cellpadding ) ),
  0            
36             },
37             encodes => '',
38             matrix => 1,
39             _layout => 1,
40             );
41              
42 0 0         $self ? $self->generate( @args ) : Spreadsheet::HTML::generate( @args );
43             }
44              
45             sub checkerboard {
46 0     0 1   my ($self,$data,$args);
47 0 0         $self = shift if ref($_[0]) =~ /^Spreadsheet::HTML/;
48 0 0         ($self,$data,$args) = $self ? $self->_args( @_ ) : Spreadsheet::HTML::_args( @_ );
49              
50 0 0         my $colors = $args->{colors} ? $args->{colors} : [qw(red green)];
51 0 0         $colors = [ $colors ] unless ref $colors;
52              
53 0           my @rows;
54 0           for my $row (0 .. $args->{_max_rows} - 1) {
55 0           push @rows, ( "-r$row" => { style => { 'background-color' => [@$colors] } } );
56 0           Tie::Hash::Attribute::_rotate( $colors );
57             }
58              
59 0           my @args = (
60             @_,
61             wrap => 0,
62             @rows,
63             );
64              
65 0 0         $self ? $self->generate( @args ) : Spreadsheet::HTML::generate( @args );
66             }
67              
68             sub banner {
69 0     0 1   my ($self,$data,$args);
70 0 0         $self = shift if ref($_[0]) =~ /^Spreadsheet::HTML/;
71 0 0         ($self,$data,$args) = $self ? $self->_args( @_ ) : Spreadsheet::HTML::_args( @_ );
72              
73 0           my @cells = ();
74 0 0         unless ($NO_FIGLET) {
75              
76 0           my @banner;
77 0           eval {
78             @banner = Text::FIGlet
79             ->new( -d => $args->{dir}, -f => $args->{emboss} ? 'block' : 'banner' )
80 0 0 0       ->figify( -A => uc( $args->{text} || '' ), -w => 9999 );
81             };
82 0 0         if ($@) {
83 0           $data = [ ['Error'], ["could not create banner: $@"] ];
84             }
85              
86 0 0         if (@banner) {
87 0           push @cells, ( fill => join 'x', scalar( @banner ), length( $banner[0] ) );
88 0   0       my $on = $args->{on} || 'black';
89 0   0       my $off = $args->{off} || 'white';
90              
91 0           for my $row (0 .. $#banner) {
92 0           my @line = split //, $banner[$row];
93 0           for my $col (0 .. $#line) {
94 0           my $key = sprintf '-r%sc%s', $row, $col;
95 0 0         if ($args->{emboss}) {
96 0 0         if ($line[$col] eq ' ') {
    0          
    0          
97 0           push @cells, ( $key => { style => { 'background-color' => $off } } );
98             } elsif ($line[$col] eq '_') {
99 0           push @cells, ( $key => { style => { 'background-color' => $off, 'border-bottom' => "1px solid $on" } } );
100             } elsif ($args->{flip}) {
101 0           push @cells, ( $key => { style => { 'background-color' => $off, 'border-right' => "1px solid $on" } } );
102             } else {
103 0           push @cells, ( $key => { style => { 'background-color' => $off, 'border-left' => "1px solid $on" } } );
104             }
105             } else {
106 0 0         my $color = $line[$col] eq ' ' ? $off : $on;
107 0           push @cells, ( $key => { style => { 'background-color' => $color } } );
108             }
109             }
110             }
111             }
112             }
113             else {
114 0           $data = [ ['could not create banner'], [ 'Text::FIGlet not installed' ] ];
115             }
116              
117 0           my @args = (
118             data => $data,
119             @_,
120             @cells,
121             wrap => 0,
122             );
123              
124 0 0         my $table = $self ? $self->generate( @args ) : Spreadsheet::HTML::generate( @args );
125 0           return $table;
126             }
127              
128             sub maze {
129 0     0 1   my ($self,$data,$args);
130 0 0         $self = shift if ref($_[0]) =~ /^Spreadsheet::HTML/;
131 0 0         ($self,$data,$args) = $self ? $self->_args( @_ ) : Spreadsheet::HTML::_args( @_ );
132              
133 0           my @cells = ();
134 0 0         unless ($NO_LISTUTIL) {
135              
136 0 0         my $rows = $args->{_max_rows} == 1 ? 20 : $args->{_max_rows};
137 0 0         my $cols = $args->{_max_cols} == 1 ? 16 : $args->{_max_cols};
138 0   0       my $off = $args->{off} || 'white';
139 0   0       my $on = $args->{on} || 'black';
140              
141 0           push @cells, ( fill => "${rows}x${cols}" );
142              
143 0           my (@grid,@stack);
144 0           for my $h (0 .. $rows - 1) {
145 0           $grid[$h] = [ map {
146             x => $_, y => $h,
147             walls => [1,1,1,1], # W S E N
148             }, 0 .. $cols - 1 ];
149             }
150              
151 0           my %neighbor = ( 0 => 2, 1 => 3, 2 => 0, 3 => 1 );
152 0           my $visited = 1;
153 0           my $curr = $grid[rand $rows][rand $cols];
154 0           while ($visited < $rows * $cols) {
155 0           my @neighbors;
156 0           for (
157             [ 3, $grid[ $curr->{y} - 1 ][ $curr->{x} ] ], # north
158             [ 2, $grid[ $curr->{y} ][ $curr->{x} + 1 ] ], # east
159             [ 1, $grid[ $curr->{y} + 1 ][ $curr->{x} ] ], # south
160             [ 0, $grid[ $curr->{y} ][ $curr->{x} - 1 ] ], # west
161 5 0   5   29 ) { no warnings; push @neighbors, $_ if List::Util::sum( @{ $_->[1]->{walls} } ) == 4 }
  5         9  
  5         8323  
  0            
  0            
162              
163 0 0         if (@neighbors) {
164 0           my ($pos,$cell) = @{ $neighbors[rand @neighbors] };
  0            
165 0           $curr->{walls}[$pos] = 0;
166 0           $cell->{walls}[$neighbor{$pos}] = 0;
167 0           push @stack, $curr;
168 0           $curr = $cell;
169 0           $visited++;
170             } else {
171 0           $curr = pop @stack;
172             }
173 0           @neighbors = ();
174             }
175              
176 0           my %style_map = (
177             0 => 'border-left',
178             1 => 'border-bottom',
179             2 => 'border-right',
180             3 => 'border-top',
181             );
182              
183 0           for my $row (0 .. $#grid) {
184 0           for my $col (0 .. @{ $grid[$row] }) {
  0            
185 0           my $key = sprintf '-r%sc%s', $row, $col;
186 0           my %style = ( 'background-color' => $off );
187 0           for (0 .. $#{ $grid[$row][$col]{walls} } ) {
  0            
188 0 0         $style{$style_map{$_}} = "2px solid $on" if $grid[$row][$col]{walls}[$_];
189             }
190 0           push @cells, ( $key => { height => '20px', width => '20px', style => {%style} } );
191             }
192             }
193             }
194              
195 0           my @args = (
196             @_,
197             @cells,
198             matrix => 1,
199             tgroups => 0,
200             flip => 0,
201             theta => 0,
202             headless => 0,
203             );
204              
205 0 0         my $table = $self ? $self->generate( @args ) : Spreadsheet::HTML::generate( @args );
206 0           return $table;
207             }
208              
209             sub calendar {
210 0     0 1   my ($self,$data,$args);
211 0 0         $self = shift if ref($_[0]) =~ /^Spreadsheet::HTML/;
212 0 0         ($self,$data,$args) = $self ? $self->_args( @_ ) : Spreadsheet::HTML::_args( @_ );
213              
214 0           my @cal_args;
215 0 0         unless ($NO_TIMEPIECE) {
216             my $time = Time::Piece->strptime(
217             join( '-',
218             $args->{month} || (localtime)[4]+1,
219 0   0       $args->{year} || (localtime)[5]+1900,
      0        
220             ), '%m-%Y'
221             );
222 0           my $first = $time->wday;
223 0           my $last = $time->month_last_day;
224 0           my @flat = (
225             (map Time::Piece->strptime($_,"%d")->day, 4 .. 10),
226             ('') x ($first - 1),
227             1 .. $last
228             );
229            
230 0           push @cal_args, ( data => \@flat );
231              
232 0           $args->{ '-' . Time::Piece->new->mday } = $args->{today};
233 0           my %day_args = map {($_ => $args->{$_})} grep /^-\d+$/, keys %$args;
  0            
234 0           for (keys %day_args) {
235 0           my $day = abs($_);
236 0 0         next if $day > $last;
237 0           my $index = $day + $first + 5;
238 0           my $row = int($index / 7);
239 0           my $col = $index % 7;
240 0           push @cal_args, ( sprintf( '-r%sc%s', $row, $col ) => $day_args{$_} );
241             }
242              
243 0           my $caption = join( ' ', $time->fullmonth, $time->year );
244 0 0         if ($args->{scroll}) {
245 0           $caption = qq{

$caption

};
246             }
247              
248 0           my $attr = { style => { 'font-weight' => 'bold' } };
249 0 0 0       if ($args->{caption} and ref $args->{caption} eq 'HASH') {
250 0           ($attr) = values %{ $args->{caption} };
  0            
251             }
252              
253 0           push @cal_args, ( caption => { $caption => $attr } );
254             }
255              
256             my @args = (
257             @cal_args,
258             @_,
259 0 0         td => { %{ $args->{td} || {} }, style => { %{ $args->{td}{style} || {} }, 'text-align' => 'right' } },
  0 0          
  0            
260             wrap => 7,
261             theta => 0,
262             flip => 0,
263             matrix => 0,
264             );
265              
266 0 0         return $self ? $self->generate( @args ) : Spreadsheet::HTML::generate( @args );
267             }
268              
269             sub checkers {
270 0     0 1   my ($self,$data,$args);
271 0 0         $self = shift if ref($_[0]) =~ /^Spreadsheet::HTML/;
272 0 0         ($self,$data,$args) = $self ? $self->_args( @_ ) : Spreadsheet::HTML::_args( @_ );
273              
274 0           my @data = (
275             [ '', '⛂', '', '⛂', '', '⛂', '', '⛂' ],
276             [ '⛂', '', '⛂', '', '⛂', '', '⛂', '' ],
277             [ '', '⛂', '', '⛂', '', '⛂', '', '⛂' ],
278             [ ('') x 8 ], [ ('') x 8 ],
279             [ '⛀', '', '⛀', '', '⛀', '', '⛀', '' ],
280             [ '', '⛀', '', '⛀', '', '⛀', '', '⛀' ],
281             [ '⛀', '', '⛀', '', '⛀', '', '⛀', '' ],
282             );
283              
284 0   0       my $on = $args->{on} || 'red';
285 0   0       my $off = $args->{off} || 'white';
286              
287             my @args = (
288             table => {
289             width => '65%',
290             style => {
291             border => 'thick outset',
292 0 0         %{ $args->{table}{style} || {} },
293             },
294 0 0         %{ $args->{table} || {} },
295             },
296             @_,
297             td => [
298             {
299             height => 65,
300             width => 65,
301             align => 'center',
302             style => {
303             'font-size' => 'xx-large',
304             border => 'thin inset',
305             'background-color' => [ ($off, $on)x4, ($on, $off)x4 ],
306 0 0         %{ $args->{td}{style} || {} },
307             },
308 0 0         %{ $args->{td} || {} },
309 0 0   0     }, sub { $_[0] ? qq(
$_[0]
) : '' }
310 0           ],
311             tgroups => 0,
312             headless => 0,
313             pinhead => 0,
314             matrix => 1,
315             wrap => 0,
316             fill => '8x8',
317             data => \@data,
318             );
319              
320 0           my $js = Spreadsheet::HTML::Presets::Chess::_javascript( %$args );
321 0 0         my $table = $self ? $self->generate( @args ) : Spreadsheet::HTML::generate( @args );
322 0           return $js . $table;
323             }
324              
325             sub _js_wrapper {
326 0     0     my %args = @_;
327              
328 0 0         unless ($NO_MINIFY) {
329             $args{code} = JavaScript::Minifier::minify(
330             input => $args{code},
331 0   0       copyright => $args{copyright} || 'Copyright 2016 Jeff Anderson',
332             stripDebug => 1,
333             );
334             }
335              
336 0           my $js = $args{_auto}->tag( tag => 'script', cdata => $args{code}, attr => { type => 'text/javascript' } );
337 0 0         return $js if $args{bare};
338              
339 0   0       $args{jquery} ||= 'https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js';
340              
341 0           my $html = $args{_auto}->tag( tag => 'script', cdata => '', attr => { src => $args{jquery} } );
342 0 0         $html .= $args{_auto}->tag( tag => 'script', cdata => '', attr => { src => $args{jqueryui} } ) if $args{jqueryui};
343 0 0         $html .= $args{_auto}->tag( tag => 'script', cdata => '', attr => { src => $args{handsonjs} } ) if $args{handsonjs};
344 0 0         $html .= $args{_auto}->tag( tag => 'link', attr => { rel => 'stylesheet', media => 'screen', href => $args{css} } ) if $args{css};
345              
346 0           return $html . $js;
347             }
348              
349             =head1 NAME
350              
351             Spreadsheet::HTML::Presets - Generate preset HTML tables.
352              
353             =head1 DESCRIPTION
354              
355             This is a container for L preset methods.
356             These methods are not meant to be called from this package.
357             Instead, use the Spreadsheet::HTML interface:
358              
359             use Spreadsheet::HTML;
360             my $generator = Spreadsheet::HTML->new( data => \@data );
361             print $generator->layout;
362              
363             # or
364             use Spreadsheet::HTML qw( layout );
365             print layout( data => \@data );
366              
367             =head1 METHODS
368              
369             =over 4
370              
371             =item * C
372              
373             Layout tables are not recommended, but if you choose to
374             use them you should label them as such. This adds W3C
375             recommended layout attributes to the table tag and features:
376             emit only tags, no padding or pruning of rows, forces
377             no HTML entity encoding in table cells.
378              
379             =item * C
380              
381             Preset for tables with checkerboard colors.
382              
383             checkerboard( colors => [qw(yellow orange blue)] )
384              
385             Forms diagonal patterns by alternating the starting background
386             colors for each row. C defaults to red and green.
387              
388             =item * C
389              
390             Will generate and display a banner using the given C in the
391             'banner' font. Set C to a true value and the font 'block'
392             will be emulated by highlighting the left and bottom borders of the cell.
393             Set the foreground color with C and the background with C.
394             You Must have L installed AND configured in order to use
395             this preset. If Text::FIGlet cannot find the fonts directory then it
396             will silently fail and produce no banner.
397              
398             banner( dir => '/path/to/figlet/fonts', text => 'HI', on => 'red' )
399              
400             =item * C
401              
402             Generates a static calendar. Defaults to current month and year.
403              
404             calendar( month => 7, year, 2012 )
405              
406             Mark a day of the month like so:
407              
408             calendar( month => 12, -25 => { bgcolor => 'red' } )
409              
410             Or mark today, whenever it is:
411              
412             calendar( today => { bgcolor => 'red' } )
413              
414             Default rules still apply to styling columns by any heading:
415              
416             calendar( -Tue => { class => 'ruby' } )
417              
418             =item * C
419              
420             Generates a static maze.
421              
422             maze( fill => '10x10', on => 'red', off => 'black' )
423              
424             =item * C
425              
426             Generates a checkers game board (US). Currently you can only
427             move the pieces around without regard to any rules. Defaults
428             to red and white squares which can be overriden with C
429             and C, respectively:
430              
431             checkers( on => 'blue', off => 'gray' )
432              
433             =back
434              
435             =head1 MORE PRESETS
436              
437             =over 4
438              
439             =item * L
440              
441             Generate
442              
443             =item * L
444              
445             Provides the scroll param.
446              
447             =item * L
448              
449             Turn cell backgrounds into 8-bit pixel art.
450              
451             =item * L
452              
453             A simple HTML table calculator.
454              
455             =item * L
456              
457             Turn cell backgrounds into Conway's game of life.
458              
459             =item * L
460              
461             Creats a Tic-Tac-Toe board.
462              
463             =item * L
464              
465             Work in progress. Hope to interface with a pre-existing
466             Javascript Chess engine someday.
467              
468             =item * L
469              
470             Handsontable HTML tables. See L.
471              
472             =item * L
473              
474             Generate 9x9 HTML table sudoku boards.
475              
476             =back
477              
478             =head1 SEE ALSO
479              
480             =over 4
481              
482             =item * L
483              
484             The interface for this functionality.
485              
486             =back
487              
488             =head1 AUTHOR
489              
490             Jeff Anderson, C<< >>
491              
492             =head1 LICENSE AND COPYRIGHT
493              
494             Copyright 2016 Jeff Anderson.
495              
496             This program is free software; you can redistribute it and/or modify it
497             under the terms of the the Artistic License (2.0). You may obtain a
498             copy of the full license at:
499              
500             L
501              
502             Any use, modification, and distribution of the Standard or Modified
503             Versions is governed by this Artistic License. By using, modifying or
504             distributing the Package, you accept this license. Do not use, modify,
505             or distribute the Package, if you do not accept this license.
506              
507             If your Modified Version has been derived from a Modified Version made
508             by someone other than you, you are nevertheless required to ensure that
509             your Modified Version complies with the requirements of this license.
510              
511             This license does not grant you the right to use any trademark, service
512             mark, tradename, or logo of the Copyright Holder.
513              
514             This license includes the non-exclusive, worldwide, free-of-charge
515             patent license to make, have made, use, offer to sell, sell, import and
516             otherwise transfer the Package with respect to any patent claims
517             licensable by the Copyright Holder that are necessarily infringed by the
518             Package. If you institute patent litigation (including a cross-claim or
519             counterclaim) against any party alleging that the Package constitutes
520             direct or contributory patent infringement, then this Artistic License
521             to you shall terminate on the date that such litigation is filed.
522              
523             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
524             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
525             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
526             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
527             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
528             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
529             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
530             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
531              
532             =cut
533              
534             1;