File Coverage

blib/lib/HTML/CalendarMonth.pm
Criterion Covered Total %
statement 247 451 54.7
branch 63 210 30.0
condition 12 54 22.2
subroutine 44 79 55.7
pod 40 42 95.2
total 406 836 48.5


line stmt bran cond sub pod time code
1             package HTML::CalendarMonth;
2             {
3             $HTML::CalendarMonth::VERSION = '2.03';
4             }
5              
6 10     10   152802 use strict;
  10         11  
  10         231  
7 10     10   28 use warnings;
  10         8  
  10         193  
8 10     10   32 use Carp;
  10         14  
  10         535  
9              
10 10     10   4292 use HTML::ElementTable 1.18;
  10         318692  
  10         63  
11 10     10   4228 use HTML::CalendarMonth::Locale;
  10         22  
  10         281  
12 10     10   4268 use HTML::CalendarMonth::DateTool;
  10         16  
  10         278  
13              
14 10     10   50 use base qw( Class::Accessor HTML::ElementTable );
  10         9  
  10         4753  
15              
16             my %Objects;
17              
18             # default complex attributes
19             my %Calmonth_Attrs = (
20             head_m => 1, # month heading mode
21             head_y => 1, # year heading mode
22             head_dow => 1, # DOW heading mode
23             head_week => 0, # weak of year
24             year_span => 2, # default col span of year
25              
26             today => undef, # DOM, if not now
27             week_begin => 1, # what DOW (1-7) is the 1st DOW?
28              
29             historic => 1, # if able to choose, use ncal/cal
30             # rather than Date::Calc, which
31             # blindly extrapolates Gregorian
32              
33             alias => {}, # what gets displayed if not
34             # the default item
35              
36             month => undef, # these will get initialized
37             year => undef,
38              
39             locale => 'en_US',
40             full_days => 0,
41             full_months => 1,
42              
43             datetool => undef,
44              
45             enable_css => 1,
46             semantic_css => 0,
47              
48             # internal muckety muck
49             _cal => undef,
50             _itoch => {},
51             _ctoih => {},
52             _caltool => undef,
53             _weeknums => undef,
54              
55             dow1st => undef,
56             lastday => undef,
57             loc => undef,
58              
59             # deprecated
60             row_offset => undef,
61             col_offset => undef,
62             );
63              
64             __PACKAGE__->mk_accessors(keys %Calmonth_Attrs);
65              
66             # Class::Accessor overrides
67              
68             sub set {
69 11588     11588 1 34475 my($self, $key) = splice(@_, 0, 2);
70 11588 50       13164 if (@_ == 1) {
    0          
71 11588         21429 $Objects{$self}{$key} = $_[0];
72             }
73             elsif (@_ > 1) {
74 0         0 $Objects{$self}{$key} = [@_];
75             }
76             else {
77 0         0 Carp::confess("wrong number of arguments received");
78             }
79             }
80              
81             sub get {
82 74077     74077 1 309419 my $self = shift;
83 74077 50       76901 if (@_ == 1) {
    0          
84 74077         190370 return $Objects{$self}{$_[0]};
85             }
86             elsif ( @_ > 1 ) {
87 0         0 return @{$Objects{$self}{@_}};
  0         0  
88             }
89             else {
90 0         0 Carp::confess("wrong number of arguments received.");
91             }
92             }
93              
94 1229     1229   893 sub _is_calmonth_attr { shift; exists $Calmonth_Attrs{shift()} }
  1229         2312  
95              
96             sub _set_defaults {
97 309     309   419 my $self = shift;
98 309         1719 foreach (keys %Calmonth_Attrs) {
99 8343         17711 $self->$_($Calmonth_Attrs{$_});
100             }
101 309         659 $self;
102             }
103              
104 309     309   1843512 sub DESTROY { delete $Objects{shift()} }
105              
106             # last dow col, first week row
107              
108 10     10   15691 use constant LDC => 6;
  10         13  
  10         493  
109 10     10   35 use constant FWR => 2;
  10         12  
  10         3381  
110              
111             # alias
112              
113             sub item_alias {
114 12120     12120 0 1067774 my($self, $item) = splice(@_, 0, 2);
115 12120 50       19625 defined $item or croak "item name required";
116 12120 50       15631 $self->alias->{$item} = shift if @_;
117 12120 100       19162 $self->alias->{$item} || $item;
118             }
119              
120             sub item_aliased {
121 0     0 0 0 my($self, $item) = splice(@_, 0, 2);
122 0 0       0 defined $item or croak "item name required.\n";
123 0         0 defined $self->alias->{$item};
124             }
125              
126             # header toggles
127              
128             sub _head {
129             # Set/test entire heading (month,year,and dow headers) (does not
130             # affect week number column). Return true if either heading active.
131 0     0   0 my $self = shift;
132 0 0 0     0 $self->head_m(@_) && $self->head_dow(@_) if @_;
133 0 0       0 $self->_head_my || $self->head_dow;
134             }
135              
136             sub _head_my {
137             # Set/test month and year header mode
138 307     307   666 my($self, $mode) = splice(@_, 0, 2);
139 307 50 0     682 $self->head_m($mode) && $self->head_y($mode) if defined $mode;
140 307 50       751 $self->head_m || $self->head_y;
141             }
142              
143             sub _initialized {
144 307     307   371 my $self = shift;
145 307 50       806 @_ ? $self->{_initialized} = shift : $self->{_initialized};
146             }
147              
148             # circa interface
149              
150             sub _date {
151             # set target month, year
152 307     307   381 my $self = shift;
153 307 50       630 if (@_) {
154 307         419 my ($month, $year) = @_;
155 307 50 33     1133 $month && defined $year || croak "date method requires month and year";
156 307 50       700 croak "Date already set" if $self->_initialized();
157              
158             # get rid of possible leading 0's
159 307         450 $month += 0;
160 307         376 $year += 0;
161              
162 307 50 33     1212 $month <= 12 && $month >= 1 or croak "Month $month out of range (1-12)\n";
163 307 50       587 $year > 0 or croak "Negative years are unacceptable\n";
164              
165 307         850 $self->month($self->monthname($month));
166 307         607 $self->year($year);
167 307         707 $month = $self->monthnum($month);
168              
169             # trigger _gencal...this should be the only place where this occurs
170 307         838 $self->_gencal;
171             }
172 307         711 return($self->month, $self->year);
173             }
174              
175             # class factory access
176              
177 10     10   43 use constant CLASS_HET => 'HTML::ElementTable';
  10         12  
  10         408  
178 10     10   31 use constant CLASS_DATETOOL => 'HTML::CalendarMonth::DateTool';
  10         14  
  10         415  
179 10     10   41 use constant CLASS_LOCALE => 'HTML::CalendarMonth::Locale';
  10         12  
  10         30777  
180              
181             sub _gencal {
182             # generate internal calendar representation
183 307     307   323 my $self = shift;
184              
185             # new calendar...clobber day-specific settings
186 307         762 my $itoc = $self->_itoch({});
187 307         662 my $ctoi = $self->_ctoih({});
188              
189             # figure out dow of 1st day of the month as well as last day of the
190             # month (uses date calculator backends)
191 307         834 $self->_anchor_month();
192              
193             # row count for weeks in grid
194 307         282 my $wcnt = 0;
195              
196 307         560 my ($dowc) = $self->dow1st;
197 307         570 my $skips = $self->_caltool->_skips;
198              
199             # for each day
200 307         651 foreach (1 .. $self->lastday) {
201 9351 50       10828 next if $skips->{$_};
202 9351         6083 my $r = $wcnt + FWR;
203 9351         5250 my $c = $dowc;
204             # this is a bootstrap until we know the number of rows in the month.
205 9351         11447 $itoc->{$_} = [$r, $c];
206 9351         6151 $dowc = ++$dowc % 7;
207 9351 100 100     14053 ++$wcnt unless $dowc || $_ == $self->lastday;
208             }
209              
210 307         571 $self->{_week_rows} = $wcnt;
211              
212 307         371 my $row_extent = $wcnt + FWR;
213 307         383 my $col_extent = LDC;
214 307 50       595 $col_extent += 1 if $self->head_week;
215              
216 307         845 $self->SUPER::extent($row_extent, $col_extent);
217              
218             # table can contain the days now, so replace our bootstrap coordinates
219             # with references to the actual elements.
220 307         2177203 foreach (keys %$itoc) {
221 9351         6342 my $cellref = $self->cell(@{$itoc->{$_}});
  9351         17874  
222 9351         868923 $self->_itoc($_, $cellref);
223 9351         10459 $self->_ctoi($cellref, $_);
224             }
225              
226             # week num affects month/year spans
227 307 50       1346 my $width = $self->head_week ? 8 : 7;
228              
229             # month/year headers
230 307         712 my $cellref = $self->cell(0, 0);
231 307         29341 $self->_itoc($self->month, $cellref);
232 307         758 $self->_ctoi($cellref, $self->month);
233 307         1034 $cellref = $self->cell(0, $width - $self->year_span);
234 307         29367 $self->_itoc($self->year, $cellref);
235 307         639 $self->_ctoi($cellref, $self->year);
236              
237 307         687 $self->item($self->month)->replace_content($self->item_alias($self->month));
238 307         6773 $self->item($self->year)->replace_content($self->item_alias($self->year));
239              
240 307 50       5114 if ($self->_head_my) {
241 307 50 33     569 if ($self->head_m && $self->head_y) {
    0          
    0          
242 307         628 $self->item($self->year) ->attr('colspan', $self->year_span);
243 307         48619 $self->item($self->month)->attr('colspan', $width - $self->year_span);
244             }
245             elsif ($self->head_y) {
246 0         0 $self->item($self->month)->mask(1);
247 0         0 $self->item($self->year)->attr('colspan', $width);
248             }
249             elsif ($self->head_m) {
250 0         0 $self->item($self->year)->mask(1);
251 0         0 $self->item($self->month)->attr('colspan', $width);
252             }
253             }
254             else {
255 0         0 $self->row(0)->mask(1);
256             }
257              
258             # DOW headers
259 307         40702 my $trans;
260 307         1040 my $days = $self->loc->days;
261 307         926 foreach (0..$#$days) {
262             # Transform for week_begin 1..7
263 2149         3302 $trans = ($_ + $self->week_begin - 1) % 7;
264 2149         3816 my $cellref = $self->cell(1, $_);
265 2149         203042 $self->_itoc($days->[$trans], $cellref);
266 2149         2827 $self->_ctoi($cellref, $days->[$trans]);
267             }
268 307 50       970 if ($self->head_dow) {
269 307         821 grep($self->item($_)->replace_content($self->item_alias($_)), @$days);
270             }
271             else {
272 0         0 $self->row(1)->mask(1);
273             }
274              
275             # week number column
276 307 50       5122 if ($self->head_week) {
277             # week nums can collide with days. Use "w" in front of the number
278             # for uniqueness, and automatically alias to just the number (unless
279             # already aliased, of course).
280 0         0 $self->_gen_week_nums();
281 0         0 my $ws;
282 0         0 my $row_count = FWR;
283 0         0 foreach ($self->_numeric_week_nums) {
284 0         0 $ws = "w$_";
285 0 0       0 $self->item_alias($ws, $_) unless $self->item_aliased($ws);
286 0         0 my $cellref = $self->cell($row_count, $self->last_col);
287 0         0 $self->_itoc($ws, $cellref);
288 0         0 $self->_ctoi($cellref, $ws);
289 0         0 $self->item($ws)->replace_content($self->item_alias($ws));
290 0         0 ++$row_count;
291             }
292             }
293              
294             # fill in days of the month
295 307         416 my $i;
296 307         996 foreach my $r (FWR .. $self->last_row) {
297 1598         18995 foreach my $c (0 .. LDC) {
298 11186 100       115073 $self->cell($r,$c)->replace_content($self->item_alias($i))
299             if ($i = $self->item_at($r,$c));
300             }
301             }
302              
303             # css classes
304 307 50       1615 if ($self->enable_css) {
305 307         1119 $self->push_attr(class => 'hcm-table');
306 307 50       10849 $self->item_row($self->dayheaders)->push_attr(class => 'hcm-day-head')
307             if $self->head_dow;
308 307 50       532000 $self->item($self->year)->push_attr(class => 'hcm-year-head')
309             if $self->head_y;
310 307 50       39388 $self->item($self->month)->push_attr(class => 'hcm-month-head')
311             if $self->head_m;
312 307 50       37998 $self->item($self->week_nums) ->push_attr(class => 'hcm-week-head')
313             if $self->head_week;
314             }
315              
316 307 50       996 if ($self->semantic_css) {
317 0         0 my $today = $self->today;
318 0 0       0 if ($today < 0) {
    0          
319 0         0 $self->item($self->days)->push_attr(class => 'hcm-past');
320             }
321             elsif ($today == 0) {
322 0         0 $self->item($self->days)->push_attr(class => 'hcm-future');
323             }
324             else {
325 0         0 for my $d ($self->days) {
326 0 0       0 if ($d < $today) {
    0          
327 0         0 $self->item($d)->push_attr(class => 'hcm-past');
328             }
329             elsif ($d > $today) {
330 0         0 $self->item($d)->push_attr(class => 'hcm-future');
331             }
332             else {
333 0         0 $self->item($d)->push_attr(class => 'hcm-today');
334             }
335             }
336             }
337             }
338              
339 307         995 $self;
340             }
341              
342             sub default_css {
343 0     0 1 0 my $hbgc = '#DDDDDD';
344 0         0 my $bc = '#888888';
345              
346 0         0 my $str = <<__CSS;
347            
385             __CSS
386              
387             }
388              
389             sub _datetool {
390 307     307   355 my $self = shift;
391 307         270 my $ct;
392 307 50       589 if (! ($ct = $self->_caltool)) {
393 0         0 $ct = $self->_caltool(CLASS_DATETOOL->new(
394             year => $self->year,
395             month => $self->month,
396             weeknum => $self->head_week,
397             historic => $self->historic,
398             datetool => $self->datetool,
399             ));
400             }
401 307         467 $ct;
402             }
403              
404             sub _anchor_month {
405             # Figure out what our month grid looks like.
406             # Let HTML::CalendarMonth::DateTool determine which method is
407             # appropriate.
408 307     307   377 my $self = shift;
409              
410 307         674 my $month = $self->monthnum($self->month);
411 307         709 my $year = $self->year;
412              
413 307         1036 my $tool = $self->_datetool;
414              
415 307         1108 my $dow1st = $tool->dow1st; # 0..6, starting with Sun
416 307         644 my $lastday = $tool->lastday;
417              
418             # week_begin given as 1..7 starting with Sun
419 307         743 $dow1st = ($dow1st - ($self->week_begin - 1)) % 7;
420              
421 307         621 $self->dow1st($dow1st);
422 307         554 $self->lastday($lastday);
423              
424 307         292 $self;
425             }
426              
427             sub _gen_week_nums {
428             # Generate week-of-the-year numbers. The first week is generally
429             # agreed upon to be the week that contains the 4th of January.
430             #
431             # For purposes of shenanigans with 'week_begin', we anchor the week
432             # number off of Thursday in each row.
433              
434 0     0   0 my $self = shift;
435              
436 0         0 my($year, $month, $lastday) = ($self->year, $self->monthnum, $self->lastday);
437              
438 0         0 my $tool = $self->_caltool;
439 0 0       0 croak "Oops. " . ref $tool . " not set up for week of year calculations.\n"
440             unless $tool->can('week_of_year');
441              
442 0         0 my $fdow = $self->dow1st;
443 0         0 my $delta = 4 - $fdow;
444 0 0       0 if ($delta < 0) {
445 0         0 $delta += 7;
446             }
447 0         0 my @ft = $tool->add_days($delta, 1);
448              
449 0         0 my $ldow = $tool->dow($lastday);
450 0         0 $delta = 4 - $ldow;
451 0 0       0 if ($delta > 0) {
452 0         0 $delta -= 7;
453             }
454 0         0 my @lt = $tool->add_days($delta, $lastday);
455              
456 0         0 my $fweek = $tool->week_of_year(@ft);
457 0         0 my $lweek = $tool->week_of_year(@lt);
458 0 0       0 my @wnums = $fweek > $lweek ? ($fweek, 1 .. $lweek) : ($fweek .. $lweek);
459              
460             # do we have days above our first Thursday?
461 0 0       0 if ($self->row_of($ft[0]) != FWR) {
462 0         0 unshift(@wnums, $wnums[0] -1);
463             }
464              
465             # do we have days below our last Thursday?
466 0 0       0 if ($self->row_of($lt[0]) != $self->last_row) {
467 0         0 push(@wnums, $wnums[-1] + 1);
468             }
469              
470             # first visible week is from last year
471 0 0       0 if ($wnums[0] == 0) {
472 0         0 $wnums[0] = $tool->week_of_year($tool->add_days(-7, $ft[0]));
473             }
474              
475             # last visible week is from subsequent year
476 0 0       0 if ($wnums[-1] > $lweek) {
477 0         0 $wnums[-1] = $tool->week_of_year($tool->add_days(7, $lt[0]));
478             }
479              
480 0         0 $self->_weeknums(\@wnums);
481             }
482              
483             # month hooks
484              
485             sub row_items {
486             # given a list of items, return all items in rows shared by the
487             # provided items.
488 0     0 1 0 my $self = shift;
489 0         0 my %items;
490 0         0 foreach my $item (@_) {
491 0         0 my $row = ($self->coords_of($item))[0];
492 0         0 foreach my $col (0 .. $self->last_col) {
493 0   0     0 my $i = $self->item_at($row, $col) || next;
494 0         0 ++$items{$i};
495             }
496             }
497 0 0       0 keys %items > 1 ? keys %items : (keys %items)[0];
498             }
499              
500             sub col_items {
501             # return all item cells in the columns occupied by the provided list
502             # of items.
503 0     0 1 0 my $self = shift;
504 0         0 $self->_col_items(0, $self->last_row, @_);
505             }
506              
507             sub daycol_items {
508             # same as col_items(), but excludes header cells.
509 0     0 1 0 my $self = shift;
510 0         0 $self->_col_items(FWR, $self->last_row, @_);
511             }
512              
513             sub _col_items {
514             # given row bounds and a list of items, return all item elements
515             # in the columns occupied by the provided items. Does not return
516             # empty cells.
517 0     0   0 my($self, $rfirst, $rlast) = splice(@_, 0, 3);
518 0         0 my %items;
519 0         0 my($item, $row, $col, %i);
520 0         0 foreach my $item (@_) {
521 0         0 my $col = ($self->coords_of($item))[1];
522 0         0 foreach my $row ($rfirst .. $rlast) {
523 0   0     0 my $i = $self->item_at($row,$col) || next;
524 0         0 ++$items{$i};
525             }
526             }
527 0 0       0 keys %items > 1 ? keys %items : (keys %items)[0];
528             }
529              
530             sub daytime {
531             # return seconds since epoch for a given day
532 0     0 1 0 my($self, $day) = splice(@_, 0, 2);
533 0 0       0 $day or croak "must specify day of month";
534 0 0       0 croak "day does not exist" unless $self->_daycheck($day);
535 0         0 $self->_caltool->day_epoch($day);
536             }
537              
538             sub week_nums {
539             # return list of all week number labels
540 0     0 1 0 my @wnums = map("w$_", shift->_numeric_week_nums);
541 0 0       0 wantarray ? @wnums : \@wnums;
542             }
543              
544             sub _numeric_week_nums {
545             # return list of all week numbers as numbers
546 0     0   0 my $self = shift;
547 0 0       0 return unless $self->head_week;
548 0 0       0 wantarray ? @{$self->_weeknums} : $self->_weeknums;
  0         0  
549             }
550              
551             sub days {
552             # return list of all days of the month (1..$c->lastday).
553 0     0 1 0 my $self = shift;
554 0         0 my $skips = $self->_caltool->_skips;
555 0         0 my @days = grep { !$skips->{$_} } (1 .. $self->lastday);
  0         0  
556 0 0       0 wantarray ? @days : \@days;
557             }
558              
559             sub dayheaders {
560             # return list of all day headers (Su..Sa).
561 311     311 1 854 shift->loc->days;
562             }
563              
564             sub headers {
565             # return list of all headers (month,year,dayheaders)
566 0     0 1 0 my $self = shift;
567 0 0       0 wantarray ? ($self->year, $self->month, $self->dayheaders)
568             : [$self->year, $self->month, $self->dayheaders];
569             }
570              
571             sub items {
572             # return list of all items (days, headers)
573 0     0 1 0 my $self = shift;
574 0 0       0 wantarray ? ($self->headers, $self->days)
575             : [$self->headers, $self->days];
576             }
577              
578             sub last_col {
579             # what's the max col of the calendar?
580 0     0 1 0 my $self = shift;
581 0 0       0 $self->head_week ? LDC + 1 : LDC;
582             }
583              
584 0     0 1 0 sub last_day_col { LDC }
585              
586             sub last_row {
587             # last row of the calendar
588 307     307 1 314 my $self = shift;
589 307         965 return ($self->coords_of($self->lastday))[0];
590             }
591              
592             *last_week_row = \&last_row;
593              
594 0     0 1 0 sub first_week_row { FWR };
595              
596             sub past_days {
597 0     0 1 0 my $self = shift;
598 0         0 my $today = $self->today;
599 0 0       0 if ($today < 0) {
    0          
600 0         0 return $self->days;
601             }
602             elsif ($today == 0) {
603 0         0 return;
604             }
605 0         0 return(1 .. $today);
606             }
607              
608             sub future_days {
609 0     0 1 0 my $self = shift;
610 0         0 my $today = $self->today;
611 0 0       0 if ($today < 0) {
    0          
612 0         0 return;
613             }
614             elsif ($today == 0) {
615 0         0 return $self->days;
616             }
617 0         0 return($today .. $self->last_day);
618             }
619              
620             # custom glob interfaces
621              
622             sub item {
623             # return TD elements containing items
624 3991     3991 1 29342 my $self = shift;
625 3991 50       6535 @_ || croak "item(s) must be provided";
626 3991         7175 $self->cell(grep(defined $_, map($self->coords_of($_), @_)));
627             }
628              
629             sub item_row {
630             # return a glob of the rows of a list of items, including empty cells.
631 307     307 1 382 my $self = shift;
632 307         474 $self->row(map { $self->row_of($_) } @_);
  2149         3064  
633             }
634              
635             sub item_day_row {
636             # same as item_row, but excludes possible week number cells
637 0     0 1 0 my $self = shift;
638 0 0       0 return $self->item_row(@_) unless $self->head_week;
639 0         0 my(%rows, @coords);
640 0         0 for my $r (map { $self->row_of($_) } @_) {
  0         0  
641 0 0       0 next if ++$rows{$r} > 1;
642 0         0 for my $c (0 .. 6) {
643 0         0 push(@coords, ($r, $c));
644             }
645             }
646 0         0 $self->cell(@coords);
647             }
648              
649             sub item_week_nums {
650             # glob of all week numbers
651 0     0 1 0 my $self = shift;
652 0         0 $self->item($self->week_nums);
653             }
654              
655             sub item_col {
656             # return a glob of the cols of a list of items, including empty cells.
657 0     0 1 0 my $self = shift;
658 0         0 $self->_item_col(0, $self->last_row, @_);
659             }
660              
661             sub item_daycol {
662             # same as item_col(), but excludes header cells.
663 0     0 1 0 my $self = shift;
664 0         0 $self->_item_col(2, $self->last_row, @_);
665             }
666              
667             sub _item_col {
668             # given row bounds and a list of items, return a glob representing
669             # the cells in the columns occupied by the provided items, including
670             # empty cells.
671 0     0   0 my($self, $rfirst, $rlast) = splice(@_, 0, 3);
672 0 0 0     0 defined $rfirst && defined $rlast or Carp::confess "No items provided";
673 0         0 my(%seen, @coords);
674 0         0 foreach my $col (map { $self->col_of($_) } @_) {
  0         0  
675 0 0       0 next if ++$seen{$col} > 1;
676 0         0 foreach my $row ($rfirst .. $rlast) {
677 0         0 push(@coords, $row, $col);
678             }
679             }
680 0         0 $self->cell(@coords);
681             }
682              
683             sub item_box {
684             # return a glob of the box defined by two items
685 0     0 1 0 my($self, $item1, $item2) = splice(@_, 0, 3);
686 0 0 0     0 defined $item1 && defined $item2 or croak "Two items required";
687 0         0 $self->box($self->coords_of($item1), $self->coords_of($item2));
688             }
689              
690             sub all {
691             # return a glob of all calendar cells, including empty cells.
692 0     0 1 0 my $self = shift;
693 0         0 $self->box( 0,0 => $self->last_row, $self->last_col );
694             }
695              
696             sub alldays {
697             # return a glob of all cells other than header cells
698 0     0 1 0 my $self = shift;
699 0         0 $self->box( 2, 0 => $self->last_row, 6 );
700             }
701              
702             sub allheaders {
703             # return a glob of all header cells
704 0     0 1 0 my $self = shift;
705 0         0 $self->item($self->headers);
706             }
707              
708             # transformation Methods
709              
710             sub coords_of {
711             # convert an item into grid coordinates
712 6447     6447 1 4541 my $self = shift;
713 6447 50 33     20229 croak "undefined value passed to coords_of()" if @_ && ! defined $_[0];
714 6447         8257 my $ref = $self->_itoc(@_);
715 6447 50       16028 my @pos = ref $ref ? $ref->position : ();
716 6447 50       518232 @pos ? (@pos[$#pos - 1, $#pos]) : ();
717             }
718              
719             sub item_at {
720             # convert grid coords into item
721 11186     11186 1 8222 my $self = shift;
722 11186         16736 $self->_ctoi($self->cell(@_));
723             }
724              
725             sub _itoc {
726             # item to grid
727 18561     18561   25712 my($self, $item, $ref) = splice(@_, 0, 3);
728 18561 50       27426 defined $item or croak "item required";
729 18561         27857 my $itoc = $self->_itoch;
730 18561 100       25184 if ($ref) {
731 12114 50       15639 croak "Reference required" unless ref $ref;
732 12114         13810 $itoc->{$item} = $ref;
733             }
734 18561         20632 $itoc->{$item};
735             }
736              
737             sub _ctoi {
738             # cell reference to item
739 23300     23300   1056491 my($self, $refstring, $item) = splice(@_, 0, 3);
740 23300 50       33522 defined $refstring or croak "cell id required";
741 23300         35729 my $ctoi = $self->_ctoih;
742 23300 100       29549 if (defined $item) {
743 12114         21120 $ctoi->{$refstring} = $item;
744             }
745 23300         53925 $ctoi->{$refstring};
746             }
747              
748             sub row_of {
749 2149     2149 1 1527 my $self = shift;
750 2149         2844 ($self->coords_of(@_))[0];
751             }
752              
753             sub col_of {
754 0     0 1 0 my $self = shift;
755 0         0 ($self->coords_of(@_))[1];
756             }
757              
758             sub monthname {
759             # check/return month...returns name. Accepts month number or string.
760 307     307 1 322 my $self = shift;
761 307 50       611 return $self->month unless @_;
762 307         632 my $loc = $self->loc;
763 307         349 my @names;
764 307         580 for my $m (@_) {
765 307 50 33     2231 $m = ($m - 1) % 12 if $m && $m =~ /^\d+$/;
766 307   33     1067 $m = $loc->monthname($m) || croak "month not found " . join(', ', @_);
767 307 50       1308 return $m if @_ == 1;
768 0         0 push(@names, $m);
769             }
770 0         0 @names;
771             }
772              
773             sub monthnum {
774             # check/return month, returns number. Accepts month number or string.
775 614     614 1 611 my $self = shift;
776 614 50       1379 my @months = @_ ? @_ : $self->month;
777 614         1058 my $loc = $self->loc;
778 614         587 my @nums;
779 614         785 for my $m (@months) {
780 614 50 33     2741 $m = ($m - 1) % 12 if $m && $m =~ /^\d+$/;
781 614         1351 $m = $loc->monthnum($m);
782 614 50       1072 croak "month not found ", join(', ', @_) unless defined $m;
783 614         638 $m += 1;
784 614 50       1715 return $m if @_ == 1;
785 0         0 push(@nums, $m);
786             }
787 0         0 @nums;
788             }
789              
790             sub dayname {
791             # check/return day...returns name. Accepts 1..7, or Su..Sa
792 0     0 1 0 my $self = shift;
793 0 0       0 @_ || croak "day string or num required";
794 0         0 my $loc = $self->loc;
795 0         0 my @names;
796 0         0 for my $d (@_) {
797 0 0       0 if ($d =~ /^\d+$/) {
798 0         0 $d = (($d - 1) % 7) + $self->week_begin - 1;
799             }
800 0   0     0 $d = $loc->dayname($d) || croak "day not found ", join(', ', @_);
801 0 0       0 return $d if @_ == 1;
802 0         0 push(@names, $d);
803             }
804 0         0 @names;
805             }
806              
807             sub daynum {
808             # check/return day number 1..7, returns number. Accepts 1..7,
809             # or Su..Sa
810 0     0 1 0 my $self = shift;
811 0 0       0 @_ || croak "day string or num required";
812 0         0 my $loc = $self->loc;
813 0         0 my @nums;
814 0         0 for my $d (@_) {
815 0 0       0 if ($d =~ /^\d+$/) {
816 0         0 $d = (($d - 1) % 7) + $self->week_begin - 1;
817             }
818 0         0 $d = $loc->daynum($d);
819 0 0       0 croak "day not found ", join(', ', @_) unless defined $d;
820 0         0 $d += 1;
821 0 0       0 return $d if @_ == 1;
822 0         0 push(@nums, $d);
823             }
824 0         0 @nums;
825             }
826              
827             # tests-n-checks
828              
829             sub _dayheadcheck {
830             # test day head names
831 0     0   0 my($self, $name) = splice(@_, 0, 2);
832 0 0       0 $name or croak "name missing";
833 0 0       0 return if $name =~ /^\d+$/;
834 0         0 $self->daynum($name);
835             }
836              
837             sub _daycheck {
838             # check if an item is a day of the month (1..31)
839 0     0   0 my($self, $item) = splice(@_, 0, 2);
840 0 0       0 croak "item required" unless $item;
841             # can't just invert _headcheck because coords_of() needs _daycheck,
842             # and _headcheck uses coords_of()
843 0 0       0 $item =~ /^\d{1,2}$/ && $item <= 31;
844             }
845              
846             sub _headcheck {
847             # check if an item is a header
848 0     0   0 !_daycheck(@_);
849             }
850              
851             # constructors/destructors
852              
853             sub new {
854 309     309 1 4651 my $class = shift;
855 309         1277 my %parms = @_;
856 309         416 my(%attrs, %tattrs);
857 309         957 foreach (keys %parms) {
858 1229 50       2029 if (__PACKAGE__->_is_calmonth_attr($_)) {
859 1229         1892 $attrs{$_} = $parms{$_};
860             }
861             else {
862 0         0 $tattrs{$_} = $parms{$_};
863             }
864             }
865              
866 309         1426 my $self = CLASS_HET->new(%tattrs);
867 309         286139 bless $self, $class;
868              
869             # set defaults
870 309         917 $self->_set_defaults;
871              
872 309         581 my $month = delete $attrs{month};
873 309         508 my $year = delete $attrs{year};
874 309 50 33     1353 if (!$month || !$year) {
875 0         0 my ($nmonth,$nyear) = (localtime(time))[4,5];
876 0         0 ++$nmonth; $nyear += 1900;
  0         0  
877 0   0     0 $month ||= $nmonth;
878 0   0     0 $year ||= $nyear;
879             }
880 309         739 $self->month($month);
881 309         582 $self->year($year);
882              
883             # set overrides
884 309         678 for my $k (keys %attrs) {
885 611 100       1738 $self->$k($attrs{$k}) if defined $attrs{$k};
886             }
887              
888 309 50       740 my $loc = CLASS_LOCALE->new(
889             id => $self->locale,
890             full_days => $self->full_days,
891             full_months => $self->full_months,
892             ) or croak "Problem creating locale " . $self->locale . "\n";
893 309         766 $self->loc($loc);
894              
895 309         708 my $dt = CLASS_DATETOOL->new(
896             year => $self->year,
897             month => $self->month,
898             weeknum => $self->head_week,
899             historic => $self->historic,
900             datetool => $self->datetool,
901             );
902 307         1064 $self->_caltool($dt);
903              
904             $self->week_begin($loc->first_day_of_week + 1)
905 307 100       777 unless defined $attrs{week_begin};
906              
907             my $dom_now = defined $attrs{today} ? $dt->_dom_now(delete $attrs{today})
908 307 50       1401 : $dt->_dom_now;
909 307         784 $self->today($dom_now);
910              
911 307   50     1152 my $alias = $attrs{alias} || {};
912 307 100       654 if ($self->full_days < 0) {
913 6         14 my @full = $self->loc->days;
914 6         14 my @narrow = $self->loc->narrow_days;
915 6         18 for my $i (0 .. $#narrow) {
916 42         60 $alias->{$full[$i]} = $narrow[$i];
917             }
918             }
919 307 50       688 if ($self->full_months < 0) {
920 0         0 my @full = $self->loc->months;
921 0         0 my @narrow = $self->loc->narrow_months;
922 0         0 for my $i (0 .. $#narrow) {
923 0         0 $alias->{$full[$i]} = $narrow[$i];
924             }
925             }
926 307 100       799 $self->alias($alias) if keys %$alias;
927              
928             # for now, this is the only time this will every happen for this
929             # object. It is now 'initialized'.
930 307         776 $self->_date($month, $year);
931              
932 307         2232 $self;
933             }
934              
935             ### overrides (our table is static)
936              
937       0 1   sub extent { }
938 42168     42168 1 657521 sub maxrow { shift->SUPER::maxrow }
939 6329     6329 1 988856 sub maxcol { shift->SUPER::maxcol }
940              
941             ### deprecated
942              
943 10     10   60 use constant row_offset => 0;
  10         10  
  10         518  
944 10     10   41 use constant col_offset => 0;
  10         11  
  10         424  
945 10     10   46 use constant first_col => 0;
  10         13  
  10         428  
946 10     10   39 use constant first_row => 0;
  10         13  
  10         335  
947 10     10   159 use constant first_week_col => 0;
  10         11  
  10         423  
948 10     10   36 use constant last_week_col => 6;
  10         16  
  10         439  
949              
950             ###
951              
952             1;
953              
954             __END__