File Coverage

blib/lib/Spreadsheet/HTML/Presets.pm
Criterion Covered Total %
statement 47 202 23.2
branch 0 106 0.0
condition 0 27 0.0
subroutine 17 25 68.0
pod 6 6 100.0
total 70 366 19.1


line stmt bran cond sub pod time code
1             package Spreadsheet::HTML::Presets;
2 5     5   17 use strict;
  5         26  
  5         122  
3 5     5   15 use warnings FATAL => 'all';
  5         4  
  5         118  
4              
5 5     5   15 use Spreadsheet::HTML;
  5         5  
  5         154  
6 5     5   1879 use Spreadsheet::HTML::Presets::List;
  5         8  
  5         117  
7 5     5   1803 use Spreadsheet::HTML::Presets::Scroll;
  5         7  
  5         114  
8 5     5   1895 use Spreadsheet::HTML::Presets::Beadwork;
  5         8  
  5         147  
9 5     5   2119 use Spreadsheet::HTML::Presets::Calculator;
  5         7  
  5         121  
10 5     5   1847 use Spreadsheet::HTML::Presets::Conway;
  5         5  
  5         134  
11 5     5   1706 use Spreadsheet::HTML::Presets::Chess;
  5         7  
  5         114  
12 5     5   1785 use Spreadsheet::HTML::Presets::TicTacToe;
  5         8  
  5         110  
13 5     5   1754 use Spreadsheet::HTML::Presets::Handson;
  5         8  
  5         125  
14 5     5   1805 use Spreadsheet::HTML::Presets::Sudoku;
  5         6  
  5         3993  
15              
16 5     5   789 eval "use JavaScript::Minifier";
  0         0  
  0         0  
17             our $NO_MINIFY = $@;
18 5     5   717 eval "use Text::FIGlet";
  0         0  
  0         0  
19             our $NO_FIGLET = $@;
20 5     5   2316 eval "use Time::Piece";
  5         28201  
  5         17  
21             our $NO_TIMEPIECE = $@;
22 5     5   23 eval "use List::Util";
  5         6  
  5         198  
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   25 ) { no warnings; push @neighbors, $_ if List::Util::sum( @{ $_->[1]->{walls} } ) == 4 }
  5         33  
  5         5273  
  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           my $mday = '-' . Time::Piece->new->mday;
233 0 0         $args->{$mday} = exists $args->{$mday} ? $args->{$mday} : $args->{today};
234 0           my %day_args = map {($_ => $args->{$_})} grep /^-\d+$/, keys %$args;
  0            
235 0           for (keys %day_args) {
236 0           my $day = abs($_);
237 0 0         next if $day > $last;
238 0           my $index = $day + $first + 5;
239 0           my $row = int($index / 7);
240 0           my $col = $index % 7;
241 0           push @cal_args, ( sprintf( '-r%sc%s', $row, $col ) => $day_args{$_} );
242             }
243              
244 0           my $caption = join( ' ', $time->fullmonth, $time->year );
245 0 0         if ($args->{scroll}) {
246 0           $caption = qq{

$caption

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