File Coverage

blib/lib/HTML/Menu/DateTime.pm
Criterion Covered Total %
statement 232 235 98.7
branch 114 120 95.0
condition 21 21 100.0
subroutine 13 14 92.8
pod 7 7 100.0
total 387 397 97.4


line stmt bran cond sub pod time code
1             package HTML::Menu::DateTime;
2 24     24   1013597 use strict;
  24         67  
  24         1110  
3 24     24   256 use Carp 'croak';
  24         55  
  24         150289  
4              
5             our $VERSION = '1.00';
6             our $AUTOLOAD;
7              
8             our $DEFAULT_MONTH_FORMAT = 'long';
9             our $DEFAULT_SECOND_INCREMENT = 1;
10             our $DEFAULT_MINUTE_INCREMENT = 1;
11              
12              
13             sub new {
14 53     53 1 247322 my $pkg = shift;
15 53         94 my $date;
16            
17 53 100       703 if (@_ == 1 ) {
18 11         18 $date = shift;
19             }
20            
21 53         5365 my ($SEC, $MIN, $HOUR, $DAY, $MONTH, $YEAR) = (localtime(time))[0..5];
22 53         142 $MONTH += 1;
23 53         106 $YEAR += 1900;
24            
25             # setup defaults, then override with input (if any)
26 53         909 my $self = bless ({second => $SEC,
27             minute => $MIN,
28             hour => $HOUR,
29             day => $DAY,
30             month => $MONTH,
31             year => $YEAR,
32             date => $date,
33             less_years => 5,
34             plus_years => 5,
35             month_format => $DEFAULT_MONTH_FORMAT,
36             locale => undef,
37             second_increment => $DEFAULT_SECOND_INCREMENT,
38             minute_increment => $DEFAULT_MINUTE_INCREMENT,
39             html => '',
40             @_,
41             }, $pkg);
42            
43            
44 53 100       521 if ($self->{date}) {
45 29 100       536 if ($self->{date} =~ /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
46             # YYYY-MM-DD
47 9         15556 $self->{year} = $1;
48 9         32 $self->{month} = $2;
49 9         21 $self->{day} = $3;
50 9         15 $self->{hour} = 0;
51 9         14 $self->{minute} = 0;
52 9         22 $self->{second} = 0;
53             }
54             elsif ($self->{date} =~
55             /^([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})$/)
56             {
57             # YYYY-MM-DD hh:mm:ss
58 1         10 $self->{year} = $1;
59 1         3 $self->{month} = $2;
60 1         2 $self->{day} = $3;
61 1         2 $self->{hour} = $4;
62 1         2 $self->{minute} = $5;
63 1         2 $self->{second} = $6;
64             }
65             elsif ($self->{date} =~ /^([0-9]{4})$/) {
66             # YYYY
67 1         4 $self->{year} = $1;
68 1         1 $self->{month} = 0;
69 1         2 $self->{day} = 0;
70 1         3 $self->{hour} = 0;
71 1         1 $self->{minute} = 0;
72 1         3 $self->{second} = 0;
73             }
74             elsif ($self->{date} =~ /^([0-9]{4})([0-9]{2})$/) {
75             # YYYYMM
76 1         3 $self->{year} = $1;
77 1         3 $self->{month} = $2;
78 1         2 $self->{day} = 0;
79 1         2 $self->{hour} = 0;
80 1         46 $self->{minute} = 0;
81 1         2 $self->{second} = 0;
82             }
83             elsif ($self->{date} =~ /^([0-9]{4})([0-9]{2})([0-9]{2})$/) {
84             # YYYYMMDD
85 1         3 $self->{year} = $1;
86 1         8 $self->{month} = $2;
87 1         2 $self->{day} = $3;
88 1         2 $self->{hour} = 0;
89 1         2 $self->{minute} = 0;
90 1         2 $self->{second} = 0;
91             }
92             elsif ($self->{date} =~ /^([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})$/) {
93             # YYYYMMDDhh
94 1         3 $self->{year} = $1;
95 1         2 $self->{month} = $2;
96 1         2 $self->{day} = $3;
97 1         3 $self->{hour} = $4;
98 1         2 $self->{minute} = 0;
99 1         2 $self->{second} = 0;
100             }
101             elsif ($self->{date} =~
102             /^([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})$/)
103             {
104             # YYYYMMDDhhmm
105 2         9 $self->{year} = $1;
106 2         7 $self->{month} = $2;
107 2         7 $self->{day} = $3;
108 2         9 $self->{hour} = $4;
109 2         6 $self->{minute} = $5;
110 2         5 $self->{second} = 0;
111             }
112             elsif ($self->{date} =~
113             /^([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})$/)
114             {
115             # YYYYMMDDhhmmss
116 11         57 $self->{year} = $1;
117 11         42 $self->{month} = $2;
118 11         39 $self->{day} = $3;
119 11         49 $self->{hour} = $4;
120 11         53 $self->{minute} = $5;
121 11         31 $self->{second} = $6;
122             }
123 1         121 elsif ($self->{date} =~ /^([0-9]{2}):([0-9]{2}):([0-9]{2})$/) {
124             # hh:mm:ss
125 1         3 $self->{year} = 0;
126 1         2 $self->{month} = 0;
127 1         2 $self->{day} = 0;
128 1         4 $self->{hour} = $1;
129 1         2 $self->{minute} = $2;
130 1         3 $self->{second} = $3;
131             }
132             else {croak("invalid 'date' format");}
133             }
134            
135            
136 52         204 return $self;
137             }
138              
139              
140             sub second_menu {
141 27     27 1 218 my $self = shift;
142            
143 27         131 my ($select, $html) = _parse_input ($self->{second}, @_);
144            
145 27         63 my @loop;
146            
147 27 100       124 if (defined $self->{empty_first}) {
148 1         5 push @loop, {value => '', label => $self->{empty_first}};
149             }
150            
151 27         113 for my $item ($self->_increment('second')) {
152 1284         4321 my %data = (
153             value => $item,
154             label => $item,
155             );
156 1284 100 100     3343 if (! $self->{no_select} && (grep {$_ == $item} @$select)) {
  1284         7793  
157 21         54 $data{selected} = ' selected ';
158             }
159 1284         2584 push @loop, \%data;
160             }
161            
162 25 100       303 if ($self->{html}) {
163 2         10 return $self->_html( $select, $html, \@loop );
164             }
165              
166 23         171 return \@loop;
167             }
168              
169              
170             sub minute_menu {
171 27     27 1 5492 my $self = shift;
172            
173 27         118 my ($select, $html) = _parse_input ($self->{minute}, @_);
174            
175 27         51 my @loop;
176            
177 27 100       100 if (defined $self->{empty_first}) {
178 1         4 push @loop, {value => '', label => $self->{empty_first}};
179             }
180            
181 27         112 for my $item ($self->_increment('minute')) {
182 1284         3729 my %data = (
183             value => $item,
184             label => $item,
185             );
186 1284 100 100     4099 if (! $self->{no_select} && (grep {$_ == $item} @$select)) {
  1284         5489  
187 21         51 $data{selected} = ' selected ';
188             }
189 1284         3620 push @loop, \%data;
190             }
191            
192 25 100       1299 if ($self->{html}) {
193 2         8 return $self->_html( $select, $html, \@loop );
194             }
195              
196 23         1312 return \@loop;
197             }
198              
199              
200             sub hour_menu {
201 26     26 1 82 my $self = shift;
202            
203 26         132 my ($select, $html) = _parse_input ($self->{hour}, @_);
204            
205 26         46 my @loop;
206            
207 26 100       109 if (defined $self->{empty_first}) {
208 1         4 push @loop, {value => '', label => $self->{empty_first}};
209             }
210            
211 26         99 for my $item ('00'..'09',10..23) {
212 624         1958 my %data = (
213             value => $item,
214             label => $item,
215             );
216 624 100 100     1700 if (! $self->{no_select} && (grep {$_ == $item} @$select)) {
  624         2524  
217 26         79 $data{selected} = ' selected ';
218             }
219 624         1649 push @loop, \%data;
220             }
221            
222 26 100       114 if ($self->{html}) {
223 2         8 return $self->_html( $select, $html, \@loop );
224             }
225              
226 24         686 return \@loop;
227             }
228              
229              
230             sub day_menu {
231 27     27 1 3075 my $self = shift;
232            
233 27         619 my ($select, $html) = _parse_input ($self->{day}, @_);
234            
235 27         51 my @loop;
236            
237 27 100       126 if (defined $self->{empty_first}) {
238 1         5 push @loop, {value => '', label => $self->{empty_first}};
239             }
240            
241 27         106 for my $item ('01'..'09', 10..31) {
242 837         2411 my %data = (
243             value => $item,
244             label => $item,
245             );
246 837 100 100     2301 if (! $self->{no_select} && (grep {$_ == $item} @$select)) {
  837         3268  
247 26         61 $data{selected} = ' selected ';
248             }
249 837         1955 push @loop, \%data;
250             }
251            
252 27 100       1469 if ($self->{html}) {
253 1         5 return $self->_html( $select, $html, \@loop );
254             }
255              
256 26         171 return \@loop;
257             }
258              
259              
260             sub month_menu {
261 42     42 1 115 my $self = shift;
262            
263 42         145 my ($select, $html) = _parse_input ($self->{month}, @_);
264            
265 42         236 my @decimal = ('01'..'09', 10..12);
266 42         59 my $locale;
267            
268 42 100       268 if ($self->{locale}) {
269 9         68 require DateTime::Locale;
270 9         42 $locale = DateTime::Locale->load($self->{locale});
271             }
272            
273 42         19287 my %mon;
274 42 100       194 if ($self->{month_format} eq 'decimal') {
    100          
275 5         65 @mon{@decimal} = @decimal;
276             }
277             elsif ($self->{month_format} eq 'short') {
278 3 100       11 if ($self->{locale}) {
279 2         4 @mon{@decimal} = @{$locale->month_abbreviations};
  2         19  
280             }
281             else {
282 1         8 @mon{@decimal} = qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/;
283             }
284             }
285             else {
286 34 100       86 if ($self->{locale}) {
287 4         5 @mon{@decimal} = @{$locale->month_names};
  4         29  
288             }
289             else {
290 30         338 @mon{@decimal} = qw/January February March April May June July August
291             September October November December/;
292             }
293             }
294            
295 42         1531 my @loop;
296 42 100       158 if (defined $self->{empty_first}) {
297 1         5 push @loop, {value => '', label => $self->{empty_first}};
298             }
299            
300 42         362 for my $item (sort {$a <=> $b} keys %mon) {
  1241         1440  
301 504         1554 my %data = (
302             value => $item,
303             label => $mon{$item},
304             );
305 504 100 100     1370 if (! $self->{no_select} && (grep {$_ == $item} @$select)) {
  504         2085  
306 42         87 $data{selected} = ' selected ';
307             }
308 504         1782 push @loop, \%data;
309             }
310            
311 42 50       323 if ($self->{html}) {
312 0         0 return $self->_html( $select, $html, \@loop );
313             }
314              
315 42         339 return \@loop;
316             }
317              
318              
319             sub year_menu {
320 45     45 1 129 my $self = shift;
321            
322 45         161 my ($select, $html) = _parse_input ($self->{year}, @_);
323            
324 44 100       153 my $single = @$select == 1 ? $select->[0] : undef;
325            
326 44 50       135 croak('selected year must be above 0') unless $select > 0;
327            
328 44         675 my ($start, $end);
329            
330 44 100       644 if (defined $self->{start_year} ) {
331 16         44 $start = $self->{start_year};
332             }
333             else {
334 28 100       946 croak('cannot use less_years with multiple selections')
335             if ! $single;
336            
337 25         67 $start = $single - $self->{less_years};
338             }
339            
340 43         372 croak('start_year cannot be after selected year')
341 41 100       86 if grep {$_ < $start} @$select;
342            
343 39 100       426 croak('start year must be above 0') unless $start > 0;
344            
345 37 100       124 if (defined $self->{end_year} ) {
346 15         709 $end = $self->{end_year};
347 16         379 croak('end_year cannot be before selected year')
348 15 100       33 if grep {$_ > $end} @$select;
349             }
350             else {
351 22 50       54 croak('cannot use plus_years with multiple selections')
352             if ! $single;
353            
354 22         36 $end = $single + $self->{plus_years};
355             }
356            
357 35 50       120 croak('end year must be after start year') if $start > $end;
358            
359 35         120 my @years = ( $start .. $end );
360 35         53 my @loop;
361            
362 35 100       120 if (defined $self->{empty_first}) {
363 1         5 push @loop, {value => '', label => $self->{empty_first}};
364             }
365            
366 35         76 for my $item (@years) {
367 165         456 my %data = (
368             value => $item,
369             label => $item,
370             );
371 165 100 100     546 if (! $self->{no_select} && (grep {$_ == $item} @$select)) {
  165         770  
372 35         76 $data{selected} = ' selected ';
373             }
374 165         386 push @loop, \%data;
375             }
376            
377 35 50       125 if ($self->{html}) {
378 0         0 return $self->_html( $select, $html, \@loop );
379             }
380              
381 35         192 return \@loop;
382             }
383              
384             ### PUBLIC METHODS ###
385              
386             my @public_accessors = qw/
387             start_year
388             end_year
389             less_years
390             plus_years
391             month_format
392             locale
393             second_increment
394             minute_increment
395             html
396             /;
397              
398              
399             sub AUTOLOAD {
400 73     73   34105 my $self = shift;
401            
402 73         705 my $method = $AUTOLOAD;
403 73         486 $method =~ s/.*://;
404            
405 657         1526 croak "method '$method' is not defined"
406 73 100       235 unless grep {$method eq $_} @public_accessors;
407            
408 72 100       256 $self->{$method} = shift if @_;
409            
410 72 100       416 return $self->{$method} if defined $self->{$method};
411             }
412              
413              
414             ### so AUTOLOAD doesn't get it
415 0     0   0 sub DESTROY {}
416              
417              
418             ### PRIVATE METHODS ###
419              
420             sub _parse_input {
421 194     194   458 my $time = shift;
422 194         238 my ($i, $html, @val);
423            
424 194 100       763 if (scalar @_ == 2) {
    100          
425 1         5 ($i, $html) = @_;
426             }
427             elsif (scalar @_ == 1) {
428 64 100       177 if (ref($_[0]) eq 'HASH') {
429 2         6 $html = shift;
430             }
431             else {
432 62         105 $i = shift;
433             }
434             }
435            
436 194 100       766 return ([$time], $html) unless defined $i;
437            
438 63 100       455 if (ref($i) eq 'ARRAY') {
    100          
    100          
    100          
439 9         17 @val = grep {/^\d+$/} @$i;
  18         98  
440             }
441             elsif ($i =~ /^\d+$/) {
442 21         54 @val = $i;
443             }
444             elsif ($i =~ /^\+(\d+)$/) {
445 16         60 @val = $time + $1;
446             }
447             elsif ($i =~ /^\-(\d+)$/) {
448 16         63 @val = $time - $1;
449             }
450             else {
451 1         164 croak('invalid input at _parse_input()');
452             }
453            
454 62         179 return \@val, $html;
455             }
456              
457              
458             sub _increment {
459 54     54   101 my ($self, $type) = @_;
460            
461 54         153 my $inc = $self->{"${type}_increment"};
462            
463 54 100 100     2166 croak("${type}_increment must be between 1 and 59")
464             unless (($inc >= 1) && ($inc <= 59));
465            
466 50         71 my @num;
467            
468 50         720 for (my $i=0; $i<=59; $i+=$inc) {
469 2568         7511 push @num, sprintf("%02d", $i);
470             }
471            
472 50         1130 return @num;
473             }
474              
475              
476             sub _html {
477 7     7   16 my ($self, $select, $html, $loop) = @_;
478            
479 7         74 require HTML::Menu::Select;
480            
481 319         554 my %args = (
482 319         851 values => [ map { $_->{value} } @$loop ],
483 7         16 labels => { map { $_->{value} => $_->{label} } @$loop },
484             );
485            
486 7 50       72 if (! $self->{no_select}) {
487 7         16 $args{default} = $select;
488             }
489            
490 7 100       18 if (defined $html) {
491 3         16 $args{$_} = $html->{$_} for keys %$html;
492             }
493            
494            
495 7 100       33 if ($self->{html} eq 'menu') {
    100          
496 5         27 return HTML::Menu::Select::menu( %args );
497             }
498             elsif ($self->{html} eq 'options') {
499 1         6 return HTML::Menu::Select::options( %args );
500             }
501             else {
502 1         203 croak "unknown html option";
503             }
504             }
505              
506             1;
507              
508             __END__