File Coverage

blib/lib/HTML/Make/Calendar.pm
Criterion Covered Total %
statement 21 155 13.5
branch 0 42 0.0
condition 0 9 0.0
subroutine 7 14 50.0
pod 1 7 14.2
total 29 227 12.7


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