File Coverage

blib/lib/Spreadsheet/HTML/Presets/Conway.pm
Criterion Covered Total %
statement 7 41 17.0
branch 0 16 0.0
condition 0 12 0.0
subroutine 3 6 50.0
pod 1 1 100.0
total 11 76 14.4


line stmt bran cond sub pod time code
1             package Spreadsheet::HTML::Presets::Conway;
2 5     5   23 use strict;
  5         10  
  5         165  
3 5     5   25 use warnings FATAL => 'all';
  5         16  
  5         3869  
4              
5 5     5   1851 eval "use Color::Spectrum";
  0            
  0            
6             our $NO_SPECTRUM = $@;
7              
8             sub conway {
9 0     0 1   my ($self,$data,$args);
10 0 0         $self = shift if ref($_[0]) =~ /^Spreadsheet::HTML/;
11 0 0         ($self,$data,$args) = $self ? $self->_args( @_ ) : Spreadsheet::HTML::_args( @_ );
12              
13 0   0       $args->{on} ||= '#00BFA5';
14 0   0       $args->{off} ||= '#EEEEEE';
15             $args->{colors} = ($NO_SPECTRUM or !$args->{fade})
16             ? [ ($args->{on}) x 10 ]
17 0 0 0       : [ Color::Spectrum::generate( 10, $args->{on}, $args->{off} ) ];
18              
19 0   0       $args->{interval} ||= 200;
20              
21 0           my ( @cells, @ids );
22 0           for my $r ( 0 .. $args->{_max_rows} - 1 ) {
23 0           for my $c ( 0 .. $args->{_max_cols} - 1 ) {
24 0           my $cell = sprintf '-r%sc%s', $r, $c;
25             push @cells,
26             $cell => {
27             id => join( '-', $r, $c ),
28             class => 'conway',
29             width => '30px',
30             height => '30px',
31             style => { 'background-color' => $args->{off} },
32 0           };
33             # if alpha param was set to valid value, then
34             # only non-alpha cells will be set here
35             # TODO: determine alpha value (lightest color) for client
36 0 0         push @ids, join( '-', $r, $c ) if $args->{$cell};
37             }
38             }
39              
40             # find and remove 'file' param and its value if found
41 0 0 0       if ($args->{file} and $args->{file} =~ /\.(gif|png|jpe?g)$/) {
42 0           my $index = 0;
43 0           for (0 .. $#_) {
44 0 0         next if ref $_[$_];
45 0 0         if ($_[$_] eq 'file') { $index = $_; last }
  0            
  0            
46             }
47 0           splice @_, $index, 2;
48 0           push @_, ( fill => $args->{fill} );
49             }
50              
51 0           my @args = (
52             @cells,
53             data => $data,
54             caption => { '' => { align => 'bottom' } },
55             @_,
56             );
57              
58 0           my $js = _javascript( %$args, ids => \@ids );
59 0 0         my $table = $self ? $self->generate( @args ) : Spreadsheet::HTML::generate( @args );
60 0           return $js . $table;
61             }
62              
63             sub _javascript {
64 0     0     my %args = @_;
65              
66             my $js = sprintf _js_tmpl(),
67             $args{_max_rows},
68             $args{_max_cols},
69             $args{interval},
70 0           join( ',', map "'$_'", @{ $args{ids} } ),
71             $args{off},
72 0           join( ',', map "'$_'", @{ $args{colors} } ),
  0            
73             ;
74              
75 0           return Spreadsheet::HTML::Presets::_js_wrapper( code => $js, %args );
76             }
77              
78             sub _js_tmpl {
79 0     0     return <<'END_JAVASCRIPT';
80              
81             /* Copyright 2016 Jeff Anderson */
82             /* install JavaScript::Minifier to minify this code */
83             var MATRIX;
84             var ROW = %s;
85             var COL = %s;
86             var INTERVAL = %s;
87             var tid;
88             var ids = [ %s ];
89              
90             function Cell (id) {
91             this.id = id;
92             this.neighbors = 0;
93             this.age = 0;
94             this.off = '%s';
95             this.on = [ %s ];
96              
97             this.grow = function( age ) {
98             this.age = age;
99             if (age == 0) {
100             $('#' + this.id).css( 'background-color', this.off );
101             } else {
102             $('#' + this.id).css( 'background-color', this.on[age - 1] );
103             }
104             }
105              
106             this.update = function() {
107             if (this.age) {
108             if ((this.neighbors <= 1) || (this.neighbors >= 4)) {
109             this.grow( 0 );
110             } else if (this.age < 9) {
111             this.grow( ++this.age );
112             }
113             }
114             else {
115             if (this.neighbors == 3) {
116             this.grow( 1 );
117             }
118             }
119             this.neighbors = 0;
120             }
121             }
122              
123             function toggle() {
124             if ($('#toggle').html() === 'Start') {
125             tid = setInterval( update, INTERVAL );
126             $('#toggle').html( 'Stop' );
127             } else {
128             clearInterval( tid );
129             $('#toggle').html( 'Start' );
130             }
131             }
132              
133             $(document).ready(function(){
134              
135             $('th.conway, td.conway').click( function( data ) {
136             var matches = this.id.match( /(\d+)-(\d+)/ );
137             var selected = MATRIX[matches[1]][matches[2]];
138             if (selected.age) {
139             selected.grow( 0 );
140             } else {
141             selected.grow( 1 );
142             }
143             });
144              
145             MATRIX = new Array( ROW );
146             for (var row = 0; row < ROW; row++) {
147             MATRIX[row] = new Array( COL );
148             for (var col = 0; col < COL; col++) {
149             MATRIX[row][col] = new Cell( row + '-' + col );
150             }
151             }
152              
153             // activate any "pre-loaded" IDs
154             for (var i = 0; i < ids.length; i++) {
155             var matches = ids[i].match( /(\d+)-(\d+)/ );
156             var selected = MATRIX[matches[1]][matches[2]];
157             selected.grow( 1 );
158             }
159              
160             });
161              
162             function update() {
163              
164             // count neighbors
165             for (var row = 0; row < ROW; row++) {
166             for (var col = 0; col < COL; col++) {
167              
168             for (var r = -1; r <= 1; r++) {
169             if ( (row + r >=0) & (row + r < ROW) ) {
170              
171             for (var c = -1; c <= 1; c++) {
172             if ( ((col+c >= 0) & (col+c < COL)) & ((row+r != row) | (col+c != col))) {
173             if (MATRIX[row + r][col + c].age) {
174             MATRIX[row][col].neighbors++;
175             }
176             }
177             }
178             }
179             }
180             }
181             }
182              
183             // update cells
184             for (var row = 0; row < ROW; row++) {
185             for (var col = 0; col < COL; col++) {
186             MATRIX[row][col].update();
187             }
188             }
189             }
190             END_JAVASCRIPT
191             }
192              
193             =head1 NAME
194              
195             Spreadsheet::HTML::Presets::Conway - Generate Conway's Game of Life in HTML table cells' background.
196              
197             =head1 DESCRIPTION
198              
199             This is a container for L preset methods.
200             These methods are not meant to be called from this package.
201             Instead, use the Spreadsheet::HTML interface:
202              
203             use Spreadsheet::HTML;
204             my $generator = Spreadsheet::HTML->new( data => \@data );
205             print $generator->conway;
206              
207             # or
208             use Spreadsheet::HTML qw( conway );
209             print conway( data => \@data );
210              
211             =head1 METHODS
212              
213             =over 4
214              
215             =item * C
216              
217             Game of life. From an implementation i wrote back in college.
218              
219             conway( on => 'red', off => 'gray' )
220              
221             Set the timer with C (defaults to 200 miliseconds).
222              
223             conway( interval => 75 )
224              
225             If you have L installed then you can
226             activate a fading effect like so:
227              
228             conway( on => '#FF0000', off => '#999999', fade => 1 )
229              
230             # color names via Color::Library
231             conway( on => 'red', off => 'gray', fade => 1 )
232              
233             You can also load a file and pre-populate a game grid if you know which
234             color will be used as C. This example uses #F8F8F8 and tweaks the block
235             size up to 16:
236              
237             conway( file => 'conway.png', alpha => '#f8f8f8', block => 16 )
238              
239             The C param is available whenever you supply an image file.
240              
241             Uses Google's jQuery API unless you specify another URI via
242             the C param. Javascript will be minified
243             via L if it is installed.
244              
245             =back
246              
247             =head1 SEE ALSO
248              
249             =over 4
250              
251             =item L
252              
253             The interface for this functionality.
254              
255             =item L
256              
257             More presets.
258              
259             =item L
260              
261             Generates spectrums of HTML color strings.
262              
263             =item L
264              
265             Comprehensive named color dictionary.
266              
267             =back
268              
269             =head1 AUTHOR
270              
271             Jeff Anderson, C<< >>
272              
273             =head1 LICENSE AND COPYRIGHT
274              
275             Copyright 2016 Jeff Anderson.
276              
277             This program is free software; you can redistribute it and/or modify it
278             under the terms of the the Artistic License (2.0). You may obtain a
279             copy of the full license at:
280              
281             L
282              
283             Any use, modification, and distribution of the Standard or Modified
284             Versions is governed by this Artistic License. By using, modifying or
285             distributing the Package, you accept this license. Do not use, modify,
286             or distribute the Package, if you do not accept this license.
287              
288             If your Modified Version has been derived from a Modified Version made
289             by someone other than you, you are nevertheless required to ensure that
290             your Modified Version complies with the requirements of this license.
291              
292             This license does not grant you the right to use any trademark, service
293             mark, tradename, or logo of the Copyright Holder.
294              
295             This license includes the non-exclusive, worldwide, free-of-charge
296             patent license to make, have made, use, offer to sell, sell, import and
297             otherwise transfer the Package with respect to any patent claims
298             licensable by the Copyright Holder that are necessarily infringed by the
299             Package. If you institute patent litigation (including a cross-claim or
300             counterclaim) against any party alleging that the Package constitutes
301             direct or contributory patent infringement, then this Artistic License
302             to you shall terminate on the date that such litigation is filed.
303              
304             Disclaimer of Warranty: THE PACKAGE IS PROVIDED BY THE COPYRIGHT HOLDER
305             AND CONTRIBUTORS "AS IS' AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
306             THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
307             PURPOSE, OR NON-INFRINGEMENT ARE DISCLAIMED TO THE EXTENT PERMITTED BY
308             YOUR LOCAL LAW. UNLESS REQUIRED BY LAW, NO COPYRIGHT HOLDER OR
309             CONTRIBUTOR WILL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, OR
310             CONSEQUENTIAL DAMAGES ARISING IN ANY WAY OUT OF THE USE OF THE PACKAGE,
311             EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
312              
313             =cut
314              
315             1;