File Coverage

HTML/Calendar/Simple.pm
Criterion Covered Total %
statement 136 146 93.1
branch 40 52 76.9
condition 11 12 91.6
subroutine 24 26 92.3
pod 8 9 88.8
total 219 245 89.3


line stmt bran cond sub pod time code
1             package HTML::Calendar::Simple;
2              
3             $HTML::Calendar::Simple::VERSION = "0.05";
4              
5             =pod
6              
7             =head1 NAME
8              
9             HTML::Calendar::Simple - A simple html calendar
10              
11             =head1 SYNOPSIS
12              
13             use HTML::Calendar::Simple;
14              
15             my $cal = HTML::Calendar::Simple->new; # This month, this year
16             $cal = HTML::Calendar::Simple->new({ 'month' => $month }); # This year
17             $cal = HTML::Calendar::Simple->new({ 'month' => $month,
18             'year' => $year});
19              
20             my $month = $cal->month;
21             my $year = $cal->year;
22              
23             $cal->pin_up(a_picture_location);
24             $cal->daily_info({ 'day' => $day,
25             'day_link' => $location,
26             $type1 => $info1,
27             $type2 => $info2,
28             'link' => [$link, $tag],
29             });
30              
31             print $cal;
32            
33             print $cal->calendar_month;
34             or
35             print $cal->calendar_month({border => 0}); #this allows you to change the border of the table ( default is set to 1 )
36             which shows the border with padding.
37              
38             my $html = HTML::Calendar::Simple->calendar_year;
39             $html = HTML::Calendar::Simple->calendar_year({ 'year' => $year });
40             $html = HTML::Calendar::Simple->calendar_year(
41             { 'pin_up' => $where_to_find_the_picture,
42             'year' => $year,
43             $month => { $day1 => $link1,
44             $day2 => $link2, }
45             });
46              
47             =head1 DESCRIPTION
48              
49             This is a simple module which will make an HTML representation of a
50             given month. You can add links to individual days, or in fact, any
51             sort of information you want.
52              
53             Yes, the inspiration for this came out of me looking at
54             HTML::CalendarMonthSimple, and thinking 'Hmmm. A bit too complicated
55             for what I want. I know, I will write a simplified version.' So I did.
56              
57             =cut
58              
59 1     1   22375 use strict;
  1         2  
  1         35  
60 1     1   928 use Date::Simple;
  1         8560  
  1         35  
61 1     1   121080 use CGI;
  1         16148  
  1         9  
62              
63             use overload
64 1     1   61 '""' => '_stringify';
  1         2  
  1         3  
65              
66             my %days = ( 'Sun' => 0, 'Mon' => 1,
67             'Tue' => 2, 'Wed' => 3,
68             'Thu' => 4, 'Fri' => 5,
69             'Sat' => 6 );
70              
71             my %months = ( 1 => 'Jan', 2 => 'Feb', 3 => 'Mar',
72             4 => 'Apr', 5 => 'May', 6 => 'Jun',
73             7 => 'Jul', 8 => 'Aug', 9 => 'Sep',
74             10 => 'Oct', 11 => 'Nov', 12 => 'Dec' );
75              
76             =head2 new
77              
78             my $cal = HTML::Calendar::Simple->new;
79             my $cal = HTML::Calendar::Simple->new({ 'month' => $month });
80             my $cal = HTML::Calendar::Simple->new({ 'month' => $month,
81             'year' => $year });
82              
83             This will make a new HTML::Calendar::Simple object.
84              
85             =cut
86              
87             sub new {
88 28     28 1 62 my $self = {};
89 28         85 bless $self, shift;
90 28         80 $self->_init(@_);
91 28         131 return $self;
92             }
93              
94             sub _init {
95 28     28   41 my $self = shift;
96             # validate the args passed to new, if there were any.
97 28         93 my $valid_day = Date::Simple->new;
98 28         3676 my $ref = shift;
99 28 100 66     171 if (defined $ref && ref $ref eq 'HASH') {
100 27 100       288 my $month = exists $ref->{month} ? $ref->{month} : $valid_day->month;
101 27 100       106 my $year = exists $ref->{year} ? $ref->{year} : $valid_day->year;
102 27         69 $valid_day = $self->_date_obj($year, $month, 1);
103 27 50       494 $valid_day = defined $valid_day ? $valid_day : Date::Simple->new;
104             }
105 28         339 $self->{month} = $valid_day->month;
106 28         75 $self->{year} = $valid_day->year;
107 28         71 $self->{the_month} = $self->_days_list($self->{month}, $self->{year});
108 28         494 $self;
109             }
110              
111             =head2 month
112              
113             my $month = $cal->month;
114              
115             This will return the numerical value of the month.
116              
117             =head2 year
118              
119             my $year = $cal->year;
120              
121             This will return the four-digit year of the calendar
122              
123             =cut
124              
125 42     42 1 3032 sub month { $_[0]->{month} } # month in numerical format
126 42     42 1 1139 sub year { $_[0]->{year} } # year in YYYY form
127 1211     1211   19083 sub _spacer { return "" } # the filler for the first few entries
128 38     38   47 sub _the_month { @{ $_[0]->{the_month} } } # this is the list of hashrefs.
  38         342  
129              
130             sub _cgi {
131 1047     1047   1411 my $self = shift;
132 1047 100       2494 unless (exists $self->{cgi}) { $self->{cgi} = CGI->new; }
  25         130  
133 1047         22777 return $self->{cgi};
134             }
135              
136             =head2 daily_info
137            
138             $cal->daily_info({ 'day' => $day,
139             'day_link' => $location, # puts an href on the day
140             $type1 => $info1,
141             $type2 => $info2,
142             'link' => [$link, $tag],
143             });
144              
145             This will record that fact that $info of $type happen(s|ed) on $day.
146              
147             Now, if there is no method defined to cope with $type, then the information
148             pased as $info will just be text printed in the cell of $day. So, if you want
149             something special to happen to (say) a type of 'meeting', you would have to
150             define a method called _meeting.
151              
152             For example:
153            
154             $cal->daily_info({ 'day' => 12,
155             'meeting' => 'Meet swm' });
156              
157             and somewhere else in this module...
158              
159             sub _meeting {
160             my $self = shift;
161             return $self->_cgi->h1( shift );
162             }
163              
164             So any day that had a meeting key in its hash would be displayed as
165             an

$info

166              
167             Note: If you call daily_info again with the same day with the same type
168             BUT with different info, then the old info will get clobbered.
169              
170             There is already one method in here, and that is _link. So, you can do:
171              
172             $cal->daily_info({ 'day' => $day,
173             'link' => [$link, $tag],
174             });
175              
176             Note that the key 'link' takes an array ref.
177              
178             Also, if you don't pass valid uris as values of the keys 'link' and
179             'day_link', well, that is your out if they don't work!
180              
181             More Examples:
182              
183              
184             you can use a loop to span over days to add info to rather then manualy
185             making one for each event.
186            
187             my $cal = HTML::Calendar::Simple->new();
188            
189             for my $day (0..$end_day_of_month){
190             $cal->daily_info({ 'day' => $day,
191             'day_link' => $location, # puts an href on the day
192             $type1 => $info1,
193             $type2 => $info2,
194             'link' => [$link, $tag],
195             });
196             }
197            
198             print $cal->calendar_month();
199            
200              
201              
202             =cut
203              
204             sub _current_day {
205 0     0   0 my $self = shift;
206 0         0 my $class = shift;
207 0         0 return $self->_cgi->a({ -href => 'http://#', -class => $class }, undef)
208             }
209              
210             sub daily_info {
211 4     4 1 3529 my $self = shift;
212 4 50       13 my $ref = shift or return;
213 4 50       15 ref $ref eq 'HASH' or return;
214 4 50       17 my $day = $self->_date_obj($self->year, $self->month, $ref->{'day'})
215             or return;
216 4         175 my %info = %{ $ref };
  4         20  
217 4         13 delete $info{'day'};
218 4         13 foreach my $day_ref ($self->_the_month) {
219 51 100 100     222 next unless $day_ref && $day_ref->{date} == $day;
220 4         23 $day_ref->{$_} = $info{$_} foreach keys %info;
221 4         35 last;
222             }
223             }
224              
225             # Glerg. Make each cell in the calendar table a table of its own. And each row
226             # of this table will contain a little snippet of information.
227              
228             sub _row_elem {
229 1091     1091   1649 my $self = shift;
230 1091 100       2607 my $ref = shift or return $self->_spacer;
231 1009 50       1880 return $ref if $ref eq $self->_spacer;
232 1009         2089 my $q = $self->_cgi;
233 1009 100       5604 my $day = exists $ref->{day_link}
234             ? $q->a({ -href => $ref->{day_link} }, $ref->{date}->day)
235             : $ref->{date}->day;
236 1009         22535 my $elem = $q->start_table . $q->Tr($q->td($day));
237 1009         159120 my %info = %{ $ref };
  1009         3408  
238 1009         2225 foreach my $key (keys %info) {
239 1030 100 100     5264 next if ($key eq 'date' or $key eq 'day_link');
240 14         27 my $method = "_$key";
241 14 100       311 $elem .= $self->can($method)
242             ? $q->Tr($q->td($self->$method($info{$key})))
243             : $q->Tr($q->td($info{$key}));
244             }
245 1009         24517 $elem .= $q->end_table;
246 1009         37527 return $elem;
247             }
248              
249             sub _link {
250 6     6   10 my $self = shift;
251 6 50       20 my $ref = shift or return;
252 6 100       33 ref $ref eq 'ARRAY' or return;
253 5         14 my ($link, $tag, $class) = @$ref;
254 5         16 return $self->_cgi->a({ -href => $link }, $tag);
255             }
256              
257             sub _table_row {
258 170     170   232 my $self = shift;
259 170         352 my @week = @_; my @row;
  170         179  
260 170         530 push @row, $self->_row_elem($_) foreach @week;
261 170         784 return @row;
262             }
263              
264             =head2 pin_up
265              
266             $cal->pin_up(a_picture_with_location);
267              
268             This will add a picture above the calendar month, just like the
269             calendar I have hanging up in my kitchen, (It is a cat calendar, if
270             you are interested, as my second son loves cats. As do I!)
271              
272             This could be used to have a mechanic's garage Pirelli-style pr0n
273             calendar, but that would be your call. Mine would be something including
274             a Triumph Daytona 955i. Mmmm, nice.
275              
276             =cut
277              
278             sub pin_up {
279 1     1 1 3 my ($self, $pic) = @_;
280 1 50       6 return unless $pic;
281 1         4 $self->{picture} = $pic;
282             }
283              
284             sub picture {
285 35     35 0 2421 my $self = shift;
286 35 100       195 return exists $self->{picture} ? $self->{picture} : 0;
287             }
288              
289             =head2 calendar_month
290              
291             my $html = $cal->calendar_month;
292              
293             This will return an html string of the calendar month in question.
294              
295             =head2 html
296              
297             my $html = $cal->html;
298              
299             This will return an html string of the calendar month in question.
300              
301             THIS CALL HAS BEEN DEPRECATED.
302              
303             =cut
304              
305 5     5 1 69 sub html { $_[0]->calendar_month }
306              
307             sub calendar_month {
308 33     33 1 2061 my ($self, $alt_args) = @_;
309 33 100 100     163 my $border = (defined $alt_args && ref $alt_args eq 'HASH' && exists $alt_args->{border}) ? $alt_args->{border} : 1;
310 33         101 my @seq = $self->_the_month;
311 33         118 my $q = $self->_cgi;
312 33         124 my $mnth = $q->h3($months{$self->month} . " " . $self->year);
313 462         4615 my $cal = $q->start_table({-border => $border})
314 33         3296 . $q->th([sort { $days{$a} <=> $days{$b} } keys %days]);
315 33         2053 while (@seq) {
316 170         20882 my @week_row = $self->_table_row(splice @seq, 0, 7);
317 170         4297 $cal .= $q->Tr($q->td([@week_row]));
318             }
319 33         4149 $cal .= $q->end_table;
320 33         1972 $cal = $q->start_table . $q->Tr($q->td({ align => 'center' }, $mnth))
321             . $q->Tr($q->td($cal)) . $q->end_table;
322 33 50       13229 $cal = $self->_add_pic($cal) if $self->picture;
323 33         213 return $cal;
324             }
325              
326             =head2 calendar_year
327            
328             my $html = HTML::Calendar::Simple->calendar_year;
329             $html = HTML::Calendar::Simple->calendar_year({ 'year' => $year });
330             $html = HTML::Calendar::Simple->calendar_year(
331             { 'pin_up' => $where_to_find_the_picture,
332             'year' => $year,
333             $month => { $day1 => $link1,
334             $day2 => $link2, }
335             });
336              
337             This will return the an html string for every month in the year passed,
338             or the current year if nothing passed in.
339              
340             This key of the hashref month is *another* hashref, where the key here
341             is the day in that month, and the value a link.
342              
343             This is icky, I know, and now puts me in mind of making HTML::Calendar::Day,
344             HTML::Calendar::Month and HTML::Calendar::Year, and having an overarching
345             HTML::Calendar.
346              
347             =cut
348              
349             sub _generate_months {
350 2     2   5 my ($class, $year, $ref) = @_;
351 2         3 my @year;
352 2         7 for my $month (1 .. 12) {
353 24         114 my $cal = $class->new({ 'month' => $month, 'year' => $year });
354 24 50       94 if (defined $ref->{$month}) {
355 0         0 my %links = %{ $ref->{$month} };
  0         0  
356 0         0 foreach my $day (keys %links) {
357 0         0 $cal->daily_info({ 'day' => $day,
358             'day_link' => $links{$day},
359             });
360             }
361             }
362 24         53 push @year, $cal;
363             }
364 2         15 return @year;
365             }
366              
367             sub calendar_year {
368 2     2 1 4835 my ($class, $ref) = @_;
369 2         8 my $year = $ref->{year};
370 2 100       19 my $when = defined $year
371             ? Date::Simple->new($year, 1, 1)
372             : Date::Simple->new;
373 2 50       564 $when = defined $when ? $when : Date::Simple->new;
374 2         10 $year = $when->year;
375 2         8 my @year = $class->_generate_months($year, $ref);
376 2         5 my $year_string;
377 2         20 my $q = CGI->new;
378 2         1116 while (@year) {
379 8         4111 my @qrtr = map { $_->calendar_month } splice @year, 0, 3;
  24         79  
380 8         64 s/$year//g for @qrtr;
381 8         2647 $year_string .= $q->start_table . $q->Tr($q->td({valign => 'top'}, [@qrtr]))
382             . $q->end_table . $q->br;
383             }
384 2 50       574 my $pic = defined $ref->{'pin_up'} ? $ref->{'pin_up'} : "";
385 2 50       9 $pic = $q->Tr($q->td({ align => 'center' }, $q->img({ src => $pic }))) if $pic;
386 2         33 $year_string = $q->start_table . $pic . $q->th($year)
387             . $q->Tr($q->td($year_string))
388             . $q->end_table;
389 2         900 return $year_string;
390             }
391              
392             sub _add_pic {
393 0     0   0 my ($self, $cal) = @_;
394 0         0 my $q = $self->_cgi;
395 0         0 return $q->start_table
396             . $q->Tr($q->td({ align => 'center' },
397             $q->img({ src => $self->picture })))
398             . $q->Tr($q->td($cal))
399             . $q->end_table;
400             }
401              
402 87     87   283 sub _date_obj { Date::Simple->new($_[1], $_[2], $_[3]) }
403              
404             # here is the format of what is returned from this call. Let us say a list of
405             # hashrefs, so that I can tag lots of things in with it. Ick, I know, but this
406             # is just a messing-about at the mo. And a hashref, mmmm, makes me think of
407             # an object is needed here. A Day object if I thieved an idea from somewhere else.
408              
409             sub _days_list {
410 28     28   32 my $self = shift;
411             # Fill in a Date::Simple object for every day, Why not Date::Range object?
412             # Because I haven't installed it yet, and not sure it would be appropriate
413             # for the way I have set this up.
414 28         40 my ($month, $year) = @_;
415 28         59 my $start = $self->_date_obj($year, $month, 1);
416 28         527 my $end = $start + 31;
417 28         548 $end = $self->_date_obj($end->year, $end->month, 1);
418 28         570 my @seq = map $self->_spacer, (1 .. $days{$start->format("%a")});
419 28         450 push @seq, { 'date' => $start++ } while ($start < $end);
420 28         19889 return \@seq;
421             }
422              
423             sub _stringify {
424 1     1   868 my $self = shift;
425 1         5 my @month = $self->_the_month;
426 1         10 my $string = "\t\t\t" . $months{ $self->month } . " " . $self->year . "\n\n";
427 1         11 $string .= join "\t", sort { $days{$a} <=> $days{$b} } keys %days;
  14         24  
428 1         3 $string .= "\n";
429 1         5 while (@month) {
430 5 100       12 $string .= join "\t", map { $_ eq $self->_spacer ? "" : $_->{date}->day }
  32         55  
431             splice @month, 0, 7;
432 5         19 $string .= "\n";
433             }
434 1         4 return $string;
435             }
436              
437             =head1 BUGS
438              
439             None known
440              
441             =head2 TODO
442              
443             Oh....lots of things.
444              
445             o Rip out the CGI stuff and put all the HTML in a template, so the user
446             can decide on the format of the calendar themselves.
447             o Allow for the setting of borders etc like HTML::CalendarMonthSimple.
448             o Format the output better if there is info in a daily cell.
449             o Perhaps overload '.' so you could add two calendars. Not sure.
450             o Check the links passed in are of format http://www.stray-toaster.co.uk
451             or something.
452             o Get rid of the days and months hashes and replace with something better.
453             o And if all that happens, it may as well be HTML::CalendarMonthSimple!!
454             o Make HTML::Calendar::Day, HTML::Calendar::Month and HTML::Calendar::Year
455              
456             =head1 SHOWING YOUR APPRECIATION
457              
458             There was a thread on london.pm mailing list about working in a vacumn
459             - that it was a bit depressing to keep writing modules but never get
460             any feedback. So, if you use and like this module then please send me
461             an email and make my day.
462              
463             All it takes is a few little bytes.
464              
465             (Leon wrote that, not me!)
466              
467             =head1 AUTHOR
468              
469             Stray Toaster EFE
470              
471             =head2 With Thanks
472              
473             o To swm EFE for some roadtesting!
474             o To F for the pin-up idea
475             o To F patch for being able to change the table border
476              
477             =head1 COPYRIGHT
478              
479             Copyright (C) 2012, mwk
480              
481             This module is free software; you can redistribute it or modify it
482             under the same terms as Perl itself.
483              
484             =cut
485              
486             return qw/Now beat it you bother me/;