File Coverage

blib/lib/HTML/Make/Calendar.pm
Criterion Covered Total %
statement 21 111 18.9
branch 0 22 0.0
condition 0 6 0.0
subroutine 7 10 70.0
pod 1 3 33.3
total 29 152 19.0


line stmt bran cond sub pod time code
1             package HTML::Make::Calendar;
2 1     1   762 use warnings;
  1         2  
  1         34  
3 1     1   6 use strict;
  1         1  
  1         80  
4 1     1   7 use Carp;
  1         2  
  1         61  
5 1     1   6 use utf8;
  1         2  
  1         5  
6             require Exporter;
7             our @ISA = qw(Exporter);
8             our @EXPORT_OK = qw/calendar/;
9             our %EXPORT_TAGS = (
10             all => \@EXPORT_OK,
11             );
12             our $VERSION = '0.00_04';
13              
14 1     1   664 use Date::Calc ':all';
  1         6542  
  1         388  
15 1     1   601 use HTML::Make;
  1         15596  
  1         39  
16 1     1   601 use Table::Readable 'read_table';
  1         1407  
  1         1184  
17              
18             # Default HTML elements and classes.
19              
20             my @dowclass = (undef, "mon", "tue", "wed", "thu", "fri", "sat", "sun");
21              
22             # Read the configuration file.
23              
24             my $html_file = __FILE__;
25             $html_file =~ s!\.pm!/html.txt!;
26             my @html = read_table ($html_file);
27             my %html;
28             for (@html) {
29             $html{$_->{item}} = $_
30             }
31              
32             # Add an HTML element defined by $thing to $parent.
33              
34             sub add_el
35             {
36 0     0 0   my ($parent, $thing) = @_;
37 0           my $class = $thing->{class};
38 0           my $type = $thing->{element};
39 0           my $element;
40 0 0         if ($class) {
41             # HTML::Make should have a class pusher since it is so common
42             # http://mikan/bugs/bug/2108
43 0           $element = $parent->push ($type, attr => {class => $class});
44             }
45             else {
46             # Allow non-class elements if the user doesn't want a class.
47 0           $element = $parent->push ($type);
48             }
49 0           return $element;
50             }
51              
52             sub option
53             {
54 0     0 0   my ($ref, $options, $what) = @_;
55 0 0         if ($options->{$what}) {
56 0           $$ref = $options->{$what};
57 0           delete $options->{$what};
58             }
59             }
60              
61             sub calendar
62             {
63 0     0 1   my (%options) = @_;
64 0           option (\my $verbose, \%options, 'verbose');
65 0           my ($year, $month, undef) = Today ();
66 0           option (\$year, \%options, 'year');
67 0           option (\$month, \%options, 'month');
68 0           option (\my $dayc, \%options, 'dayc');
69 0           option (\my $cdata, \%options, 'cdata');
70 0           my $html_week = $html{week}{element};
71 0           option (\$html_week, \%options, 'html_week');
72 0           my $first = 1;
73 0           option (\$first, \%options, 'first');
74 0 0         if ($first != 1) {
75 0 0 0       if (int ($first) != $first || $first < 1 || $first > 7) {
      0        
76 0           carp "Use a number between 1 (Monday) and 7 (Sunday) for first";
77 0           $first = 1;
78             }
79             }
80             # To do: Allow the user to use their own HTML tags.
81              
82 0           for my $k (sort keys %options) {
83 0 0         if ($options{$k}) {
84 0           carp "Unknown option '$k'";
85 0           delete $options{$k};
86             }
87             }
88             # Map from columns of the calendar to days of the week, e.g. 1 ->
89             # 7 if Sunday is the first day of the week.
90 0           my %col2dow;
91 0           for (1..7) {
92 0           my $col2dow = $_ + $first - 1;
93 0 0         if ($col2dow > 7) {
94 0           $col2dow -= 7;
95             }
96 0           $col2dow{$_} = $col2dow;
97             }
98 0           my %dow2col = reverse %col2dow;
99 0           my $dim = Days_in_Month ($year, $month);
100 0 0         if ($verbose) {
101             # To do: Add a messaging routine with caller line numbers
102             # rather than just use print.
103 0           print "There are $dim days in month $month of $year.\n";
104             }
105 0           my @col;
106             # The number of weeks
107 0           my $weeks = 1;
108 0           my $prev = 0;
109 0           for my $day (1..$dim) {
110 0           my $dow = Day_of_Week ($year, $month, $day);
111 0           my $col = $dow2col{$dow};
112 0           $col[$day] = $col;
113 0 0         if ($col < $prev) {
114 0           $weeks++;
115             }
116 0           $prev = $col;
117             }
118             # The number of empty cells we need at the start of the month.
119 0           my $fill_start = $col[1] - 1;
120 0           my $fill_end = 7 - $col[-1];
121 0 0         if ($verbose) {
122 0           print "Start $fill_start, end $fill_end, weeks $weeks\n";
123             }
124 0           my @cells;
125             # To do: Allow the user to colour or otherwise alter empty cells,
126             # for example with a callback or with a user-defined class.
127 0           for (1..$fill_start) {
128 0           push @cells, {};
129             }
130 0           for (1..$dim) {
131 0           my $col = $col[$_];
132 0           push @cells, {dom => $_, col => $col, dow => $col2dow{$col}};
133             }
134 0           for (1..$fill_end) {
135 0           push @cells, {};
136             }
137 0           my $calendar = HTML::Make->new ($html{calendar}{element});
138             # As far as I know, is the correct HTML, although unless it is a
139             # nobody really does this.
140              
141             # To do: inspect the type of $html{calendar} and don't add the
142             #
element.
143 0           my $tbody = $calendar->push ('tbody');
144             # To do: These should be overridden if the caller doesn't want to
145             # use table, tr, td to construct the calendar.
146 0           my $titler = $tbody->push ('tr');
147 0           my $titleh = $titler->push ('th', attr => {colspan => 7});
148             # To do: Allow the caller to override this.
149 0           my $my = Month_to_Text ($month) . " $year";
150 0           $titleh->add_text ($my);
151             # To do: Allow the user to override this.
152 0           my $wdr = $tbody->push ('tr');
153 0           for my $col (1..7) {
154             # To do: Allow the user to use their own weekdays (possibly
155             # allow them to use the language specifier of Date::Calc).
156 0           my $dow = $col2dow{$col};
157 0           my $wdt = substr (Day_of_Week_to_Text ($dow), 0, 2);
158 0           my $dow_el = add_el ($wdr, $html{dow});
159 0           $dow_el->add_text ($wdt);
160             }
161             # wom = week of month
162 0           for my $wom (1..$weeks) {
163 0           my $week = add_el ($tbody, $html{week});
164 0           for my $col (1..7) {
165             # dow = day of week
166 0           my $dow = $col2dow{$col};
167 0           my $day = add_el ($week, $html{day});
168 0           my $cell = shift @cells;
169             # dom = day of month
170 0           my $dom = $cell->{dom};
171 0 0         if (defined $dom) {
172 0           $day->add_class ('cal-' . $dowclass[$dow]);
173 0 0         if ($dayc) {
174 0           &{$dayc} ($cdata,
  0            
175             {
176             year => $year,
177             month => $month,
178             dom => $dom,
179             dow => $dow,
180             wom => $wom,
181             },
182             $day);
183             }
184             else {
185 0           $day->push ('span', text => $dom,
186             attr => {class => 'cal-dom'});
187             }
188             }
189             else {
190 0           $day->add_class ('cal-noday');
191             }
192             # To do: allow a callback on the packing cells
193             }
194             }
195 0           return $calendar;
196             }
197              
198             1;