File Coverage

blib/lib/HTML/Make/Calendar.pm
Criterion Covered Total %
statement 21 149 14.0
branch 0 40 0.0
condition 0 9 0.0
subroutine 7 14 50.0
pod 1 7 14.2
total 29 219 13.2


line stmt bran cond sub pod time code
1             package HTML::Make::Calendar;
2 1     1   820 use warnings;
  1         2  
  1         33  
3 1     1   6 use strict;
  1         1  
  1         18  
4 1     1   5 use Carp;
  1         2  
  1         53  
5 1     1   5 use utf8;
  1         2  
  1         4  
6             require Exporter;
7             our @ISA = qw(Exporter);
8             our @EXPORT_OK = qw/calendar/;
9             our %EXPORT_TAGS = (
10             all => \@EXPORT_OK,
11             );
12              
13             our $VERSION = '0.00_05';
14              
15 1     1   680 use Date::Calc ':all';
  1         7009  
  1         400  
16 1     1   505 use HTML::Make;
  1         15720  
  1         37  
17 1     1   514 use Table::Readable 'read_table';
  1         1329  
  1         1729  
18             #use Data::Dumper;
19              
20             # Default HTML elements and classes.
21              
22             my @dowclass = (undef, "mon", "tue", "wed", "thu", "fri", "sat", "sun");
23              
24             # Read the configuration file.
25              
26             my $html_file = __FILE__;
27             $html_file =~ s!\.pm!/html.txt!;
28             my @html = read_table ($html_file);
29             my %html;
30             for (@html) {
31             $html{$_->{item}} = $_
32             }
33              
34             # Add an HTML element defined by $thing to $parent.
35              
36             sub add_el
37             {
38 0     0 0   my ($parent, $thing) = @_;
39 0           my $class = $thing->{class};
40 0           my $type = $thing->{element};
41 0           my $element;
42 0 0         if ($class) {
43             # HTML::Make should have a class pusher since it is so common
44             # http://mikan/bugs/bug/2108
45 0           $element = $parent->push ($type, attr => {class => $class});
46             }
47             else {
48             # Allow non-class elements if the user doesn't want a class.
49 0           $element = $parent->push ($type);
50             }
51 0           return $element;
52             }
53              
54             sub add_month_heading
55             {
56 0     0 0   my ($o, $tbody) = @_;
57             # Add the title to the calendar
58 0           my $titler = $tbody->push ('tr');
59 0           my $titleh = $titler->push ('th', attr => {colspan => 7});
60 0           my $my;
61 0 0         if ($o->{monthc}) {
62 0           my $date = {month => $o->{month}, year => $o->{year}};
63 0           $my = &{$o->{monthc}} ($o->{cdata}, $date, $titleh);
  0            
64             }
65             else {
66 0           $my = Month_to_Text ($o->{month}) . " $o->{year}";
67 0           $titleh->add_text ($my);
68             }
69             # To do: Allow the caller to override this.
70 0           my $wdr = $tbody;
71 0 0         if (! $o->{weekless}) {
72 0           $wdr = $tbody->push ('tr');
73             }
74 0           for my $col (1..7) {
75 0           my $dow = $o->{col2dow}{$col};
76 0           my $wdt = $o->{daynames}[$dow];
77 0           my $dow_el = add_el ($wdr, $html{dow});
78 0           $dow_el->add_text ($wdt);
79             }
80             }
81              
82             sub option
83             {
84 0     0 0   my ($o, $options, $what) = @_;
85 0 0         if ($options->{$what}) {
86 0 0         if ($o->{verbose}) {
87 0           vmsg ("Setting $what to $options->{$what}");
88             }
89 0           $o->{$what} = $options->{$what};
90 0           delete $options->{$what};
91             }
92             }
93              
94             sub check_first
95             {
96 0     0 0   my ($o) = @_;
97 0 0         if ($o->{first} != 1) {
98 0 0 0       if (int ($o->{first}) != $o->{first} ||
      0        
99             $o->{first} < 1 ||
100             $o->{first} > 7) {
101 0           carp "Use a number between 1 (Monday) and 7 (Sunday) for first";
102 0           $o->{first} = 1;
103             }
104             }
105             }
106              
107             # Map from columns of the calendar to days of the week, e.g. 1 -> 7 if
108             # Sunday is the first day of the week.
109              
110             sub map_dow2col
111             {
112 0     0 0   my ($o) = @_;
113 0           my %col2dow;
114 0           for (1..7) {
115 0           my $col2dow = $_ + $o->{first} - 1;
116 0 0         if ($col2dow > 7) {
117 0           $col2dow -= 7;
118             }
119 0           $col2dow{$_} = $col2dow;
120             }
121 0           my %dow2col = reverse %col2dow;
122 0           $o->{col2dow} = \%col2dow;
123 0           $o->{dow2col} = \%dow2col;
124             }
125              
126             sub calendar
127             {
128 0     0 1   my (%options) = @_;
129 0           my $o = {};
130 0           bless $o;
131 0           $o->option (\%options, 'verbose');
132 0           ($o->{year}, $o->{month}, undef) = Today ();
133 0           $o->option (\%options, 'year');
134 0           $o->option (\%options, 'month');
135 0           $o->option (\%options, 'dayc');
136 0           $o->option (\%options, 'monthc');
137 0           $o->option (\%options, 'cdata');
138 0           $o->{first} = 1;
139 0           $o->option (\%options, 'first');
140 0           $o->check_first ();
141 0           $o->option (\%options, 'weekless');
142 0           $o->option (\%options, 'daynames');
143             # To do: Allow the user to use their own HTML tags.
144 0           $o->{html_week} = $html{week};
145 0           $o->{html_month} = $html{month}{element};
146 0 0         if ($o->{daynames}) {
147 0 0 0       if (defined $o->{daynames}[0] && scalar (@{$o->{daynames}}) == 7) {
  0            
148             # Off-by-one
149 0           unshift @{$o->{daynames}}, '';
  0            
150             }
151             }
152             else {
153 0           for (1..7) {
154 0           $o->{daynames}[$_] = substr (Day_of_Week_to_Text ($_), 0, 2);
155             }
156             }
157             # $o->option (\%options, 'html_month');
158             # $o->option (\%options, 'html_week');
159 0           for my $k (sort keys %options) {
160 0 0         if ($options{$k}) {
161 0           carp "Unknown option '$k'";
162 0           delete $options{$k};
163             }
164             }
165 0           $o->map_dow2col ();
166 0           my $dim = Days_in_Month ($o->{year}, $o->{month});
167 0 0         if ($o->{verbose}) {
168 0           vmsg ("There are $dim days in month $o->{month} of $o->{year}");
169             }
170 0           my @col;
171             # The number of weeks
172 0           my $weeks = 1;
173 0           my $prev = 0;
174 0           for my $day (1..$dim) {
175 0           my $dow = Day_of_Week ($o->{year}, $o->{month}, $day);
176 0           my $col = $o->{dow2col}{$dow};
177 0           $col[$day] = $col;
178 0 0         if ($col < $prev) {
179 0           $weeks++;
180             }
181 0           $prev = $col;
182             }
183             # The number of empty cells we need at the start of the month.
184 0           $o->{fill_start} = $col[1] - 1;
185 0           $o->{fill_end} = 7 - $col[-1];
186 0 0         if ($o->{verbose}) {
187 0           vmsg ("Start $o->{fill_start}, end $o->{fill_end}, weeks $weeks");
188             }
189 0           my @cells;
190             # To do: Allow the user to colour or otherwise alter empty cells,
191             # for example with a callback or with a user-defined class.
192 0           for (1..$o->{fill_start}) {
193 0           push @cells, {};
194             }
195 0           for (1..$dim) {
196 0           my $col = $col[$_];
197 0           push @cells, {dom => $_, col => $col, dow => $o->{col2dow}{$col}};
198             }
199 0           for (1..$o->{fill_end}) {
200 0           push @cells, {};
201             }
202 0           my $calendar = HTML::Make->new ($o->{html_month});
203 0           my $tbody = $calendar;
204 0           my $table;
205 0 0         if ($o->{html_month} eq 'table') {
206 0           $tbody = $calendar->push ('tbody');
207 0           $table = 1;
208             }
209 0 0         if (! $o->{weekless}) {
210 0 0         if ($table) {
211 0           $o->add_month_heading ($tbody);
212             }
213             }
214             # wom = week of month
215 0           for my $wom (1..$weeks) {
216 0           my $week = $tbody;
217 0 0         if (! $o->{weekless}) {
218 0           $week = add_el ($tbody, $o->{html_week});
219             }
220 0           for my $col (1..7) {
221             # dow = day of week
222 0           my $dow = $o->{col2dow}{$col};
223 0           my $day = add_el ($week, $html{day});
224 0           my $cell = shift @cells;
225             # dom = day of month
226 0           my $dom = $cell->{dom};
227 0 0         if (defined $dom) {
228 0           $day->add_class ('cal-' . $dowclass[$dow]);
229 0 0         if ($o->{dayc}) {
230 0           &{$o->{dayc}} ($o->{cdata},
231             {
232             year => $o->{year},
233             month => $o->{month},
234 0           dom => $dom,
235             dow => $dow,
236             wom => $wom,
237             },
238             $day);
239             }
240             else {
241 0           $day->push ('span', text => $dom,
242             attr => {class => 'cal-dom'});
243             }
244             }
245             else {
246 0           $day->add_class ('cal-noday');
247             }
248             # To do: allow a callback on the packing cells
249             }
250             }
251 0           return $calendar;
252             }
253              
254             # To do: Add caller line numbers rather than just use print.
255             sub vmsg
256             {
257 0     0 0   print "@_\n";
258             }
259              
260             1;