| HTML-Menu-DateTime documentation | Contained in the HTML-Menu-DateTime distribution. |
HTML::Menu::DateTime - Easily create popup menus for use with templates.
use HTML::Menu::DateTime;
my $menu = HTML::Menu::DateTime->new (
date => '2004-02-26',
no_select => 1,
empty_first => '');
$menu->start_year (2000);
$menu->end_year (2010);
$menu->less_years (1);
$menu->plus_years (5);
$menu->month_format ('short');
$menu->locale ('en_GB');
$menu->second_increment (15);
$menu->minute_increment (5);
$menu->html ('menu');
$menu->second_menu;
$menu->minute_menu;
$menu->hour_menu;
$menu->day_menu;
$menu->month_menu;
$menu->year_menu;
Creates data structures suitable for populating HTML::Template, Template Toolkit or Template::Magic templates with dropdown date and time menus.
Allows any number of dropdown menus to be displayed on a single page, each independantly configurable.
Distribution includes ready-to-use template include files.
Can output valid HTML, allowing quick prototyping of pages, with the freedom to easily switch to using templates later.
To keep the creation of HTML completely seperate from the program, to easily allow a non-programmer to add css styles, javascript, etc. to individual menus.
To make the creation of menus as simple as possible, with extra options if needed. HTML Menus can be created as easily as:
my $template = HTML::Template->new (filename => $filename);
my $menu = HTML::Menu::DateTime->new;
$template->param (day => $menu->day_menu,
month => $menu->month_menu,
year => $menu->year_menu);
print $template->output;
To install this module, run the following commands:
perl Makefile.PL make make test make install
Alternatively, to install with Module::Build, you can use the following commands:
perl Build.PL ./Build ./Build test ./Build install
my $menu1 = HTML::Menu::DateTime->new
(date => $date,
start_year => $start,
end_year => $end,
no_select => 1,
empty_first => 1,
month_format => 'short',
locale => 'en_GB',
second_increment => 15,
minute_increment => 5,
html => 'menu');
my $menu2 = HTML::Menu::DateTime->new
(less_years => $less,
plus_years => $plus);
new() accepts the following arguments:
Can be in any of the formats 'YYYY-MM-DD hh:mm:ss', 'YYYYMMDDhhmmss', 'YYYYMMDDhhmm', 'YYYYMMDDhh', 'YYYYMMDD', 'YYYYMM', 'YYYY', 'YYYY-MM--DD', 'hh:mm:ss'.
The date passed to "new()" is used to decide which item should be selected in all of the menu methods.
Accepts the same values as the "start_year()" method.
Accepts the same values as the "end_year()" method.
Accepts the same values as the "less_years()" method.
Accepts the same values as the "plus_years()" method.
If true, ensures no item in any menu will be selected. (Otherwise, the current date and time will be used).
If defined, will create an extra list item at the start of each menu. The form value will be the empty string (''), the value passed to "empty_first()" will be the visible label for the first item (the empty string is allowed).
Accepts the same values as the "month_format()" method.
Accepts the same values as the "locale()" method.
Accepts the same values as the "second_increment()" method.
Accepts the same values as the "minute_increment()" method.
Accepts the same values as the "html()" method.
$date->start_year (2004);
Sets the absolute year that the dropdown menu will start from.
$date->end_year (2009);
Sets the absolute year that the dropdown menu will end on.
$date->less_years (2);
Sets the year that the dropdown menu will start from, relative to the selected year.
May not be used if multiple values for selection are passed to "year_menu()".
$date->plus_years (7);
Sets the year that the dropdown menu will end on, relative to the selected year.
May not be used if multiple values for selection are passed to "year_menu()".
Each item in the month menu has a label. By default this is the long English month name, such as 'January', 'February', etc. The format of the label can be changed as shown in the list below.
$date->month_format ('long'); # January, February, ...
$date->month_format ('short'); # Jan, Feb, ...
$date->month_format ('decimal'); # 01, 02, ...
The 'ornate' option, available only in developer release 0.90_01 has been dropped, as it isn't supported by the DateTime::Locale module (see "locale()").
If locale is used, the DateTime::Locale module must be installed.
Setting locale changes the names used for the 'long' and 'short' options of "month_format()".
$date->locale ('de');
$date->month_format ('long');
# the labels in the month_menu would now have the values
# Januar, Februar, ...
The value passed to locale is used as the argument to
DateTime::Locale->load(), see the DateTime::Locale documentation for a
full list of available locales.
The "second_menu()" normally lists the seconds from '00' up to '59'. Setting "second_increment()" allows the menu to be shorter by skipping some numbers.
For example:
$date->second_increment (5); # the menu contains: '00', '05', '10', '15' up to '55' $date->second_increment (15); # the menu contains: '00', '15', '30', '45'
"second_increment()" can be set to any number from 1 to 59, though it would normally make sense to only set it to a number that 60 can be divided by, such as 5, 10, 15, 20 or 30.
The "minute_menu()" normally lists the minutes from '00' up to '59'. Setting "minute_increment()" allows the menu to be shorter by skipping some numbers.
For example:
$date->minute_increment (5); # the menu contains: '00', '05', '10', '15' up to '55' $date->minute_increment (15); # the menu contains: '00', '15', '30', '45'
"minute_increment()" can be set to any number from 1 to 59, though it would normally make sense to only set it to a number that 60 can be divided by, such as 5, 10, 15, 20 or 30.
$date->html ('menu');
$date->html ('options');
$date->html (undef);
Causes the _menu methods to output HTML, rather than data for a template.
Valid values are menu and options, which will generate the HTML using
HTML::Menu::Select's menu and options routines, respectively.
When set to menu, the HTML will contain a SELECT tag containing the
appropriate OPTION tags. This allows the entire SELECT tag to be replaced
within a template with a single, simple template tag.
When set to options, the HTML will contain the appropriate OPTION tags,
without the surrounding SELECT tags. This allows the SELECT tags to remain
in the template file, so that the page visuals can still be seen in
WYSIWYG HTML editors.
<SELECT name="">
<TMPL_VAR name=select_menu>
</SELECT>
Values for values, labels and default are automatically passed to the menu or options routines. If a hash-ref is passed to any of the _menu methods, it will also be passed to the menu or options routine, allowing any of HTML::Menu::Select's other options to be set, such as specifiying JavaScript or CSS attributes. See the HTML::Menu::Select documentation for more details.
"html()" can be given undef to switch off HTML generation.
Any value other than 'menu', 'options' or undef is an error.
The 'examples/html-template' folder in this distribution contains the files second.tmpl, minute.tmpl, hour.tmpl, day.tmpl, month.tmpl and year.tmpl. Simply copy these files into the folder containing the rest of your templates.
To create, for example, 2 'month' menus in a single page you could copy the
month.tmpl file to end_month.tmpl and then change the line
<select name="month"> in end_month.tmpl to
<select name="end_month">.
Then include both files in your main template:
<html>
<body>
<form method="POST" action="/cgi-bin/test.pl">
<TMPL_INCLUDE month.tmpl>
<TMPL_INCLUDE end_month.tmpl>
<input type="submit" name="Submit" value="Submit">
</form>
</body>
</html>
When this form is submitted, it will send 2 different values, 'month' and 'end_month'.
The 'examples/template-toolkit' folder in this distribution contains the files second.html, minute.html, hour.html, day.html, month.html and year.html. Simply copy these files into the folder containing the rest of your templates.
To create, for example, 2 'month' menus in a single page you could copy the
month.tmpl file to end_month.tmpl and then change the line
<select name="month"> in end_month.tmpl to
<select name="end_month">.
Then include both files in your main template:
<html>
<body>
<form method="POST" action="">
[% INCLUDE month.html %]
[% INCLUDE end_month.html %]
<input type="submit" name="Submit" value="Submit">
</form>
</body>
</html>
When this form is submitted, it will send 2 different values, 'month' and 'end_month'.
The 'examples/template-magic' folder in this distribution contains the files second.html, minute.html, hour.html, day.html, month.html and year.html. Simply copy these files into the folder containing the rest of your templates.
To create, for example, 2 'month' menus in a single page you could copy the
month.html file to end_month.html and then change the line
<select name="month"> in end_month.html to
<select name="end_month">.
Then include both files in your main template:
<html>
<body>
<form method="POST" action="">
<!--{INCLUDE_TEMPLATE month.html}-->
<!--{INCLUDE_TEMPLATE end_month.html}-->
<input type="submit" name="Submit" value="Submit">
</form>
</body>
</html>
When this form is submitted, it will send 2 different values, 'month' and 'end_month'.
If a date is not passed to the "new()" or menu methods, then
localtime(time) is called.
If neither 'start_year' or 'less_years' is set, the default used is
less_years(5).
If neither 'end_year' or 'plus_years' is set, the default used is
plus_years(5).
If 'locale' is not set, the month_menu() labels are English.
None.
Years before 1000 AD passed to the "new()" method in the 'YYYYMMDDhhmmss' format should be passed as strings, as the leading zeros are necessary. (e.g. '09990101000000'). (Years before 1000 AD may be passed to the "year_menu()" method as literal numbers.)
Years before 1 AD are not allowed at all.
DO NOT set both 'start_year' and 'less_years' at the same time, it just doesn't make sense.
DO NOT set both with 'end_year' and 'plus_years' at the same time, it just doesn't make sense.
To start or end the range on the same year as selected, set less_years or plus_years to zero, DO NOT set start_year or end_year to zero.
When settting either 'start_year' or 'end_year', ensure that the selected year will fall within the range of years.
When passing relative values to methods, ensure they are sent as strings.
+1 numerically means 1 which is not the same as the string '+1'.
If a date is set in "new()" and either less_years or plus_years' set
and then a value passed to the "year_menu()" method. The start / end year
of the menu will be relative to the value passed to "year_menu()", not the
date set in "new()".
'Relative' parameter values sent to menu methods, which result in out-of-range selections are silently ignored and no item in the output menu will be selected.
If 'html' is set in "new()", or "html()" is set, then the HTML::Menu::Select module must be installed.
If 'locale' is set in "new()", or "locale()" is set, then the DateTime::Locale module must be installed.
As of version 1.00, if "month_format()", "second_increment()" or "minute_increment()" is called with no arguments, the value is no longer reset to it's default. (The previous behaviour was not documented, other than in the test suite.)
As of version 1.00, if "locale()", "start_year()", "end_year()", "less_years()" or "plus_years()" is called with no arguments, the value is no longer set to undef. (The previous behaviour was neither documented or tested for.)
The 'ornate' option to "month_format()", available only in developer release 0.90_01 has been dropped, as it isn't supported by the DateTime::Locale module (see "locale()").
May change "year_menu()" such that less_years / plus_years works with multiple selections - it would probably have to start / end the list in relation to the lowest / highest year.
Please log bugs, feature requests and patch submissions at http://sourceforge.net/projects/html-menu.
Support mailing list: html-menu-users@lists.sourceforge.net
HTML::Menu::Select, HTML::Template, Template Toolkit, Template::MagicTemplate::Magic, DateTime::Locale.
Carl Franks
Pedro Santelmo (suggesting DateTime::Locale for multi-lingual) Domizio Demichelis (template-magic examples and tutorial) Mark Stosberg (naming style feedback)
Copyright 2004-2005, Carl Franks. All rights reserved.
This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
Licenses are in the files "Artistic" and "Copying" in this distribution.
| HTML-Menu-DateTime documentation | Contained in the HTML-Menu-DateTime distribution. |
package HTML::Menu::DateTime; use strict; use Carp 'croak'; our $VERSION = '1.00'; our $AUTOLOAD; our $DEFAULT_MONTH_FORMAT = 'long'; our $DEFAULT_SECOND_INCREMENT = 1; our $DEFAULT_MINUTE_INCREMENT = 1; sub new { my $pkg = shift; my $date; if (@_ == 1 ) { $date = shift; } my ($SEC, $MIN, $HOUR, $DAY, $MONTH, $YEAR) = (localtime(time))[0..5]; $MONTH += 1; $YEAR += 1900; # setup defaults, then override with input (if any) my $self = bless ({second => $SEC, minute => $MIN, hour => $HOUR, day => $DAY, month => $MONTH, year => $YEAR, date => $date, less_years => 5, plus_years => 5, month_format => $DEFAULT_MONTH_FORMAT, locale => undef, second_increment => $DEFAULT_SECOND_INCREMENT, minute_increment => $DEFAULT_MINUTE_INCREMENT, html => '', @_, }, $pkg); if ($self->{date}) { if ($self->{date} =~ /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/) { # YYYY-MM-DD $self->{year} = $1; $self->{month} = $2; $self->{day} = $3; $self->{hour} = 0; $self->{minute} = 0; $self->{second} = 0; } elsif ($self->{date} =~ /^([0-9]{4})-([0-9]{2})-([0-9]{2}) ([0-9]{2}):([0-9]{2}):([0-9]{2})$/) { # YYYY-MM-DD hh:mm:ss $self->{year} = $1; $self->{month} = $2; $self->{day} = $3; $self->{hour} = $4; $self->{minute} = $5; $self->{second} = $6; } elsif ($self->{date} =~ /^([0-9]{4})$/) { # YYYY $self->{year} = $1; $self->{month} = 0; $self->{day} = 0; $self->{hour} = 0; $self->{minute} = 0; $self->{second} = 0; } elsif ($self->{date} =~ /^([0-9]{4})([0-9]{2})$/) { # YYYYMM $self->{year} = $1; $self->{month} = $2; $self->{day} = 0; $self->{hour} = 0; $self->{minute} = 0; $self->{second} = 0; } elsif ($self->{date} =~ /^([0-9]{4})([0-9]{2})([0-9]{2})$/) { # YYYYMMDD $self->{year} = $1; $self->{month} = $2; $self->{day} = $3; $self->{hour} = 0; $self->{minute} = 0; $self->{second} = 0; } elsif ($self->{date} =~ /^([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})$/) { # YYYYMMDDhh $self->{year} = $1; $self->{month} = $2; $self->{day} = $3; $self->{hour} = $4; $self->{minute} = 0; $self->{second} = 0; } elsif ($self->{date} =~ /^([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})$/) { # YYYYMMDDhhmm $self->{year} = $1; $self->{month} = $2; $self->{day} = $3; $self->{hour} = $4; $self->{minute} = $5; $self->{second} = 0; } elsif ($self->{date} =~ /^([0-9]{4})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})([0-9]{2})$/) { # YYYYMMDDhhmmss $self->{year} = $1; $self->{month} = $2; $self->{day} = $3; $self->{hour} = $4; $self->{minute} = $5; $self->{second} = $6; } elsif ($self->{date} =~ /^([0-9]{2}):([0-9]{2}):([0-9]{2})$/) { # hh:mm:ss $self->{year} = 0; $self->{month} = 0; $self->{day} = 0; $self->{hour} = $1; $self->{minute} = $2; $self->{second} = $3; } else {croak("invalid 'date' format");} } return $self; } sub second_menu { my $self = shift; my ($select, $html) = _parse_input ($self->{second}, @_); my @loop; if (defined $self->{empty_first}) { push @loop, {value => '', label => $self->{empty_first}}; } for my $item ($self->_increment('second')) { my %data = ( value => $item, label => $item, ); if (! $self->{no_select} && (grep {$_ == $item} @$select)) { $data{selected} = ' selected '; } push @loop, \%data; } if ($self->{html}) { return $self->_html( $select, $html, \@loop ); } return \@loop; } sub minute_menu { my $self = shift; my ($select, $html) = _parse_input ($self->{minute}, @_); my @loop; if (defined $self->{empty_first}) { push @loop, {value => '', label => $self->{empty_first}}; } for my $item ($self->_increment('minute')) { my %data = ( value => $item, label => $item, ); if (! $self->{no_select} && (grep {$_ == $item} @$select)) { $data{selected} = ' selected '; } push @loop, \%data; } if ($self->{html}) { return $self->_html( $select, $html, \@loop ); } return \@loop; } sub hour_menu { my $self = shift; my ($select, $html) = _parse_input ($self->{hour}, @_); my @loop; if (defined $self->{empty_first}) { push @loop, {value => '', label => $self->{empty_first}}; } for my $item ('00'..'09',10..23) { my %data = ( value => $item, label => $item, ); if (! $self->{no_select} && (grep {$_ == $item} @$select)) { $data{selected} = ' selected '; } push @loop, \%data; } if ($self->{html}) { return $self->_html( $select, $html, \@loop ); } return \@loop; } sub day_menu { my $self = shift; my ($select, $html) = _parse_input ($self->{day}, @_); my @loop; if (defined $self->{empty_first}) { push @loop, {value => '', label => $self->{empty_first}}; } for my $item ('01'..'09', 10..31) { my %data = ( value => $item, label => $item, ); if (! $self->{no_select} && (grep {$_ == $item} @$select)) { $data{selected} = ' selected '; } push @loop, \%data; } if ($self->{html}) { return $self->_html( $select, $html, \@loop ); } return \@loop; } sub month_menu { my $self = shift; my ($select, $html) = _parse_input ($self->{month}, @_); my @decimal = ('01'..'09', 10..12); my $locale; if ($self->{locale}) { require DateTime::Locale; $locale = DateTime::Locale->load($self->{locale}); } my %mon; if ($self->{month_format} eq 'decimal') { @mon{@decimal} = @decimal; } elsif ($self->{month_format} eq 'short') { if ($self->{locale}) { @mon{@decimal} = @{$locale->month_abbreviations}; } else { @mon{@decimal} = qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/; } } else { if ($self->{locale}) { @mon{@decimal} = @{$locale->month_names}; } else { @mon{@decimal} = qw/January February March April May June July August September October November December/; } } my @loop; if (defined $self->{empty_first}) { push @loop, {value => '', label => $self->{empty_first}}; } for my $item (sort {$a <=> $b} keys %mon) { my %data = ( value => $item, label => $mon{$item}, ); if (! $self->{no_select} && (grep {$_ == $item} @$select)) { $data{selected} = ' selected '; } push @loop, \%data; } if ($self->{html}) { return $self->_html( $select, $html, \@loop ); } return \@loop; } sub year_menu { my $self = shift; my ($select, $html) = _parse_input ($self->{year}, @_); my $single = @$select == 1 ? $select->[0] : undef; croak('selected year must be above 0') unless $select > 0; my ($start, $end); if (defined $self->{start_year} ) { $start = $self->{start_year}; } else { croak('cannot use less_years with multiple selections') if ! $single; $start = $single - $self->{less_years}; } croak('start_year cannot be after selected year') if grep {$_ < $start} @$select; croak('start year must be above 0') unless $start > 0; if (defined $self->{end_year} ) { $end = $self->{end_year}; croak('end_year cannot be before selected year') if grep {$_ > $end} @$select; } else { croak('cannot use plus_years with multiple selections') if ! $single; $end = $single + $self->{plus_years}; } croak('end year must be after start year') if $start > $end; my @years = ( $start .. $end ); my @loop; if (defined $self->{empty_first}) { push @loop, {value => '', label => $self->{empty_first}}; } for my $item (@years) { my %data = ( value => $item, label => $item, ); if (! $self->{no_select} && (grep {$_ == $item} @$select)) { $data{selected} = ' selected '; } push @loop, \%data; } if ($self->{html}) { return $self->_html( $select, $html, \@loop ); } return \@loop; } ### PUBLIC METHODS ### my @public_accessors = qw/ start_year end_year less_years plus_years month_format locale second_increment minute_increment html /; sub AUTOLOAD { my $self = shift; my $method = $AUTOLOAD; $method =~ s/.*://; croak "method '$method' is not defined" unless grep {$method eq $_} @public_accessors; $self->{$method} = shift if @_; return $self->{$method} if defined $self->{$method}; } ### so AUTOLOAD doesn't get it sub DESTROY {} ### PRIVATE METHODS ### sub _parse_input { my $time = shift; my ($i, $html, @val); if (scalar @_ == 2) { ($i, $html) = @_; } elsif (scalar @_ == 1) { if (ref($_[0]) eq 'HASH') { $html = shift; } else { $i = shift; } } return ([$time], $html) unless defined $i; if (ref($i) eq 'ARRAY') { @val = grep {/^\d+$/} @$i; } elsif ($i =~ /^\d+$/) { @val = $i; } elsif ($i =~ /^\+(\d+)$/) { @val = $time + $1; } elsif ($i =~ /^\-(\d+)$/) { @val = $time - $1; } else { croak('invalid input at _parse_input()'); } return \@val, $html; } sub _increment { my ($self, $type) = @_; my $inc = $self->{"${type}_increment"}; croak("${type}_increment must be between 1 and 59") unless (($inc >= 1) && ($inc <= 59)); my @num; for (my $i=0; $i<=59; $i+=$inc) { push @num, sprintf("%02d", $i); } return @num; } sub _html { my ($self, $select, $html, $loop) = @_; require HTML::Menu::Select; my %args = ( values => [ map { $_->{value} } @$loop ], labels => { map { $_->{value} => $_->{label} } @$loop }, ); if (! $self->{no_select}) { $args{default} = $select; } if (defined $html) { $args{$_} = $html->{$_} for keys %$html; } if ($self->{html} eq 'menu') { return HTML::Menu::Select::menu( %args ); } elsif ($self->{html} eq 'options') { return HTML::Menu::Select::options( %args ); } else { croak "unknown html option"; } } 1; __END__