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.04';
4             }
5              
6 10     10   147395 use strict;
  10         14  
  10         224  
7 10     10   28 use warnings;
  10         9  
  10         174  
8 10     10   30 use Carp;
  10         11  
  10         443  
9              
10 10     10   4333 use HTML::ElementTable 1.18;
  10         303613  
  10         61  
11 10     10   3866 use HTML::CalendarMonth::Locale;
  10         19  
  10         254  
12 10     10   4006 use HTML::CalendarMonth::DateTool;
  10         14  
  10         262  
13              
14 10     10   41 use base qw( Class::Accessor HTML::ElementTable );
  10         9  
  10         4450  
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 32828 my($self, $key) = splice(@_, 0, 2);
70 11588 50       12275 if (@_ == 1) {
    0          
71 11588         20557 $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 299266 my $self = shift;
83 74077 50       77390 if (@_ == 1) {
    0          
84 74077         184250 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   863 sub _is_calmonth_attr { shift; exists $Calmonth_Attrs{shift()} }
  1229         2011  
95              
96             sub _set_defaults {
97 309     309   325 my $self = shift;
98 309         1432 foreach (keys %Calmonth_Attrs) {
99 8343         15131 $self->$_($Calmonth_Attrs{$_});
100             }
101 309         571 $self;
102             }
103              
104 309     309   1728753 sub DESTROY { delete $Objects{shift()} }
105              
106             # last dow col, first week row
107              
108 10     10   14588 use constant LDC => 6;
  10         10  
  10         444  
109 10     10   33 use constant FWR => 2;
  10         10  
  10         3103  
110              
111             # alias
112              
113             sub item_alias {
114 12120     12120 0 1044075 my($self, $item) = splice(@_, 0, 2);
115 12120 50       18946 defined $item or croak "item name required";
116 12120 50       15177 $self->alias->{$item} = shift if @_;
117 12120 100       18636 $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   537 my($self, $mode) = splice(@_, 0, 2);
139 307 50 0     553 $self->head_m($mode) && $self->head_y($mode) if defined $mode;
140 307 50       624 $self->head_m || $self->head_y;
141             }
142              
143             sub _initialized {
144 307     307   255 my $self = shift;
145 307 50       763 @_ ? $self->{_initialized} = shift : $self->{_initialized};
146             }
147              
148             # circa interface
149              
150             sub _date {
151             # set target month, year
152 307     307   276 my $self = shift;
153 307 50       527 if (@_) {
154 307         401 my ($month, $year) = @_;
155 307 50 33     1048 $month && defined $year || croak "date method requires month and year";
156 307 50       599 croak "Date already set" if $self->_initialized();
157              
158             # get rid of possible leading 0's
159 307         361 $month += 0;
160 307         267 $year += 0;
161              
162 307 50 33     1024 $month <= 12 && $month >= 1 or croak "Month $month out of range (1-12)\n";
163 307 50       502 $year > 0 or croak "Negative years are unacceptable\n";
164              
165 307         727 $self->month($self->monthname($month));
166 307         584 $self->year($year);
167 307         733 $month = $self->monthnum($month);
168              
169             # trigger _gencal...this should be the only place where this occurs
170 307         616 $self->_gencal;
171             }
172 307         589 return($self->month, $self->year);
173             }
174              
175             # class factory access
176              
177 10     10   42 use constant CLASS_HET => 'HTML::ElementTable';
  10         11  
  10         388  
178 10     10   31 use constant CLASS_DATETOOL => 'HTML::CalendarMonth::DateTool';
  10         12  
  10         349  
179 10     10   32 use constant CLASS_LOCALE => 'HTML::CalendarMonth::Locale';
  10         10  
  10         28387  
180              
181             sub _gencal {
182             # generate internal calendar representation
183 307     307   320 my $self = shift;
184              
185             # new calendar...clobber day-specific settings
186 307         643 my $itoc = $self->_itoch({});
187 307         632 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         735 $self->_anchor_month();
192              
193             # row count for weeks in grid
194 307         291 my $wcnt = 0;
195              
196 307         517 my ($dowc) = $self->dow1st;
197 307         506 my $skips = $self->_caltool->_skips;
198              
199             # for each day
200 307         548 foreach (1 .. $self->lastday) {
201 9351 50       10325 next if $skips->{$_};
202 9351         5948 my $r = $wcnt + FWR;
203 9351         5186 my $c = $dowc;
204             # this is a bootstrap until we know the number of rows in the month.
205 9351         10322 $itoc->{$_} = [$r, $c];
206 9351         6336 $dowc = ++$dowc % 7;
207 9351 100 100     13474 ++$wcnt unless $dowc || $_ == $self->lastday;
208             }
209              
210 307         490 $self->{_week_rows} = $wcnt;
211              
212 307         341 my $row_extent = $wcnt + FWR;
213 307         292 my $col_extent = LDC;
214 307 50       539 $col_extent += 1 if $self->head_week;
215              
216 307         719 $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         2110372 foreach (keys %$itoc) {
221 9351         6402 my $cellref = $self->cell(@{$itoc->{$_}});
  9351         15905  
222 9351         849535 $self->_itoc($_, $cellref);
223 9351         10156 $self->_ctoi($cellref, $_);
224             }
225              
226             # week num affects month/year spans
227 307 50       1049 my $width = $self->head_week ? 8 : 7;
228              
229             # month/year headers
230 307         584 my $cellref = $self->cell(0, 0);
231 307         28649 $self->_itoc($self->month, $cellref);
232 307         623 $self->_ctoi($cellref, $self->month);
233 307         666 $cellref = $self->cell(0, $width - $self->year_span);
234 307         28697 $self->_itoc($self->year, $cellref);
235 307         605 $self->_ctoi($cellref, $self->year);
236              
237 307         684 $self->item($self->month)->replace_content($self->item_alias($self->month));
238 307         5881 $self->item($self->year)->replace_content($self->item_alias($self->year));
239              
240 307 50       4898 if ($self->_head_my) {
241 307 50 33     567 if ($self->head_m && $self->head_y) {
    0          
    0          
242 307         648 $self->item($self->year) ->attr('colspan', $self->year_span);
243 307         44503 $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         39845 my $trans;
260 307         823 my $days = $self->loc->days;
261 307         774 foreach (0..$#$days) {
262             # Transform for week_begin 1..7
263 2149         3228 $trans = ($_ + $self->week_begin - 1) % 7;
264 2149         3402 my $cellref = $self->cell(1, $_);
265 2149         197900 $self->_itoc($days->[$trans], $cellref);
266 2149         2625 $self->_ctoi($cellref, $days->[$trans]);
267             }
268 307 50       732 if ($self->head_dow) {
269 307         659 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       4713 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         335 my $i;
296 307         700 foreach my $r (FWR .. $self->last_row) {
297 1598         18190 foreach my $c (0 .. LDC) {
298 11186 100       111154 $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       1306 if ($self->enable_css) {
305 307         845 $self->push_attr(class => 'hcm-table');
306 307 50       8790 $self->item_row($self->dayheaders)->push_attr(class => 'hcm-day-head')
307             if $self->head_dow;
308 307 50       515170 $self->item($self->year)->push_attr(class => 'hcm-year-head')
309             if $self->head_y;
310 307 50       37826 $self->item($self->month)->push_attr(class => 'hcm-month-head')
311             if $self->head_m;
312 307 50       37076 $self->item($self->week_nums) ->push_attr(class => 'hcm-week-head')
313             if $self->head_week;
314             }
315              
316 307 50       789 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         788 $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   324 my $self = shift;
391 307         248 my $ct;
392 307 50       519 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         386 $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   249 my $self = shift;
409              
410 307         596 my $month = $self->monthnum($self->month);
411 307         579 my $year = $self->year;
412              
413 307         621 my $tool = $self->_datetool;
414              
415 307         985 my $dow1st = $tool->dow1st; # 0..6, starting with Sun
416 307         631 my $lastday = $tool->lastday;
417              
418             # week_begin given as 1..7 starting with Sun
419 307         667 $dow1st = ($dow1st - ($self->week_begin - 1)) % 7;
420              
421 307         576 $self->dow1st($dow1st);
422 307         562 $self->lastday($lastday);
423              
424 307         332 $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 682 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 287 my $self = shift;
589 307         674 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 28302 my $self = shift;
625 3991 50       5802 @_ || croak "item(s) must be provided";
626 3991         6370 $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 258 my $self = shift;
632 307         396 $self->row(map { $self->row_of($_) } @_);
  2149         2629  
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 4506 my $self = shift;
713 6447 50 33     19863 croak "undefined value passed to coords_of()" if @_ && ! defined $_[0];
714 6447         7652 my $ref = $self->_itoc(@_);
715 6447 50       14511 my @pos = ref $ref ? $ref->position : ();
716 6447 50       492328 @pos ? (@pos[$#pos - 1, $#pos]) : ();
717             }
718              
719             sub item_at {
720             # convert grid coords into item
721 11186     11186 1 8053 my $self = shift;
722 11186         16259 $self->_ctoi($self->cell(@_));
723             }
724              
725             sub _itoc {
726             # item to grid
727 18561     18561   24948 my($self, $item, $ref) = splice(@_, 0, 3);
728 18561 50       26567 defined $item or croak "item required";
729 18561         27263 my $itoc = $self->_itoch;
730 18561 100       25418 if ($ref) {
731 12114 50       15276 croak "Reference required" unless ref $ref;
732 12114         12798 $itoc->{$item} = $ref;
733             }
734 18561         19487 $itoc->{$item};
735             }
736              
737             sub _ctoi {
738             # cell reference to item
739 23300     23300   1031885 my($self, $refstring, $item) = splice(@_, 0, 3);
740 23300 50       32770 defined $refstring or croak "cell id required";
741 23300         34932 my $ctoi = $self->_ctoih;
742 23300 100       31387 if (defined $item) {
743 12114         19247 $ctoi->{$refstring} = $item;
744             }
745 23300         52801 $ctoi->{$refstring};
746             }
747              
748             sub row_of {
749 2149     2149 1 1532 my $self = shift;
750 2149         2492 ($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 318 my $self = shift;
761 307 50       473 return $self->month unless @_;
762 307         542 my $loc = $self->loc;
763 307         286 my @names;
764 307         508 for my $m (@_) {
765 307 50 33     2029 $m = ($m - 1) % 12 if $m && $m =~ /^\d+$/;
766 307   33     943 $m = $loc->monthname($m) || croak "month not found " . join(', ', @_);
767 307 50       1210 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 494 my $self = shift;
776 614 50       1230 my @months = @_ ? @_ : $self->month;
777 614         982 my $loc = $self->loc;
778 614         456 my @nums;
779 614         765 for my $m (@months) {
780 614 50 33     2476 $m = ($m - 1) % 12 if $m && $m =~ /^\d+$/;
781 614         1244 $m = $loc->monthnum($m);
782 614 50       933 croak "month not found ", join(', ', @_) unless defined $m;
783 614         553 $m += 1;
784 614 50       1485 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 4419 my $class = shift;
855 309         1153 my %parms = @_;
856 309         370 my(%attrs, %tattrs);
857 309         860 foreach (keys %parms) {
858 1229 50       1733 if (__PACKAGE__->_is_calmonth_attr($_)) {
859 1229         1733 $attrs{$_} = $parms{$_};
860             }
861             else {
862 0         0 $tattrs{$_} = $parms{$_};
863             }
864             }
865              
866 309         1048 my $self = CLASS_HET->new(%tattrs);
867 309         263856 bless $self, $class;
868              
869             # set defaults
870 309         794 $self->_set_defaults;
871              
872 309         516 my $month = delete $attrs{month};
873 309         396 my $year = delete $attrs{year};
874 309 50 33     1139 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         559 $self->month($month);
881 309         506 $self->year($year);
882              
883             # set overrides
884 309         662 for my $k (keys %attrs) {
885 611 100       1602 $self->$k($attrs{$k}) if defined $attrs{$k};
886             }
887              
888 309 50       608 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         673 $self->loc($loc);
894              
895 309         627 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         912 $self->_caltool($dt);
903              
904             $self->week_begin($loc->first_day_of_week + 1)
905 307 100       677 unless defined $attrs{week_begin};
906              
907             my $dom_now = defined $attrs{today} ? $dt->_dom_now(delete $attrs{today})
908 307 50       1250 : $dt->_dom_now;
909 307         729 $self->today($dom_now);
910              
911 307   50     1101 my $alias = $attrs{alias} || {};
912 307 100       607 if ($self->full_days < 0) {
913 6         11 my @full = $self->loc->days;
914 6         15 my @narrow = $self->loc->narrow_days;
915 6         16 for my $i (0 .. $#narrow) {
916 42         79 $alias->{$full[$i]} = $narrow[$i];
917             }
918             }
919 307 50       606 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       779 $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         670 $self->_date($month, $year);
931              
932 307         1854 $self;
933             }
934              
935             ### overrides (our table is static)
936              
937       0 1   sub extent { }
938 42168     42168 1 640585 sub maxrow { shift->SUPER::maxrow }
939 6329     6329 1 957967 sub maxcol { shift->SUPER::maxcol }
940              
941             ### deprecated
942              
943 10     10   49 use constant row_offset => 0;
  10         12  
  10         460  
944 10     10   76 use constant col_offset => 0;
  10         13  
  10         367  
945 10     10   33 use constant first_col => 0;
  10         9  
  10         390  
946 10     10   32 use constant first_row => 0;
  10         13  
  10         361  
947 10     10   135 use constant first_week_col => 0;
  10         12  
  10         402  
948 10     10   31 use constant last_week_col => 6;
  10         9  
  10         466  
949              
950             ###
951              
952             1;
953              
954             __END__