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.02';
4             }
5              
6 10     10   251986 use strict;
  10         22  
  10         269  
7 10     10   53 use warnings;
  10         14  
  10         278  
8 10     10   51 use Carp;
  10         22  
  10         679  
9              
10 10     10   8312 use HTML::ElementTable 1.18;
  10         537309  
  10         92  
11 10     10   7032 use HTML::CalendarMonth::Locale;
  10         36  
  10         330  
12 10     10   7026 use HTML::CalendarMonth::DateTool;
  10         26  
  10         350  
13              
14 10     10   64 use base qw( Class::Accessor HTML::ElementTable );
  10         16  
  10         8531  
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 64648 my($self, $key) = splice(@_, 0, 2);
70 11588 50       24885 if (@_ == 1) {
    0          
71 11588         40503 $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 564315 my $self = shift;
83 74077 50       144100 if (@_ == 1) {
    0          
84 74077         328017 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   1644 sub _is_calmonth_attr { shift; exists $Calmonth_Attrs{shift()} }
  1229         3878  
95              
96             sub _set_defaults {
97 309     309   484 my $self = shift;
98 309         2044 foreach (keys %Calmonth_Attrs) {
99 8343         27303 $self->$_($Calmonth_Attrs{$_});
100             }
101 309         1220 $self;
102             }
103              
104 309     309   3097810 sub DESTROY { delete $Objects{shift()} }
105              
106             # last dow col, first week row
107              
108 10     10   24052 use constant LDC => 6;
  10         24  
  10         587  
109 10     10   53 use constant FWR => 2;
  10         14  
  10         4924  
110              
111             # alias
112              
113             sub item_alias {
114 12120     12120 0 1883161 my($self, $item) = splice(@_, 0, 2);
115 12120 50       30541 defined $item or croak "item name required";
116 12120 50       25069 $self->alias->{$item} = shift if @_;
117 12120 100       30842 $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   821 my($self, $mode) = splice(@_, 0, 2);
139 307 50 0     882 $self->head_m($mode) && $self->head_y($mode) if defined $mode;
140 307 50       987 $self->head_m || $self->head_y;
141             }
142              
143             sub _initialized {
144 307     307   421 my $self = shift;
145 307 50       1167 @_ ? $self->{_initialized} = shift : $self->{_initialized};
146             }
147              
148             # circa interface
149              
150             sub _date {
151             # set target month, year
152 307     307   453 my $self = shift;
153 307 50       789 if (@_) {
154 307         745 my ($month, $year) = @_;
155 307 50 33     1529 $month && defined $year || croak "date method requires month and year";
156 307 50       896 croak "Date already set" if $self->_initialized();
157              
158             # get rid of possible leading 0's
159 307         506 $month += 0;
160 307         540 $year += 0;
161              
162 307 50 33     1446 $month <= 12 && $month >= 1 or croak "Month $month out of range (1-12)\n";
163 307 50       722 $year > 0 or croak "Negative years are unacceptable\n";
164              
165 307         1047 $self->month($self->monthname($month));
166 307         978 $self->year($year);
167 307         930 $month = $self->monthnum($month);
168              
169             # trigger _gencal...this should be the only place where this occurs
170 307         1022 $self->_gencal;
171             }
172 307         870 return($self->month, $self->year);
173             }
174              
175             # class factory access
176              
177 10     10   53 use constant CLASS_HET => 'HTML::ElementTable';
  10         21  
  10         528  
178 10     10   51 use constant CLASS_DATETOOL => 'HTML::CalendarMonth::DateTool';
  10         22  
  10         461  
179 10     10   53 use constant CLASS_LOCALE => 'HTML::CalendarMonth::Locale';
  10         18  
  10         48095  
180              
181             sub _gencal {
182             # generate internal calendar representation
183 307     307   435 my $self = shift;
184              
185             # new calendar...clobber day-specific settings
186 307         966 my $itoc = $self->_itoch({});
187 307         1024 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         984 $self->_anchor_month();
192              
193             # row count for weeks in grid
194 307         453 my $wcnt = 0;
195              
196 307         811 my ($dowc) = $self->dow1st;
197 307         900 my $skips = $self->_caltool->_skips;
198              
199             # for each day
200 307         949 foreach (1 .. $self->lastday) {
201 9351 50       20506 next if $skips->{$_};
202 9351         11926 my $r = $wcnt + FWR;
203 9351         10450 my $c = $dowc;
204             # this is a bootstrap until we know the number of rows in the month.
205 9351         22139 $itoc->{$_} = [$r, $c];
206 9351         12817 $dowc = ++$dowc % 7;
207 9351 100 100     25330 ++$wcnt unless $dowc || $_ == $self->lastday;
208             }
209              
210 307         779 $self->{_week_rows} = $wcnt;
211              
212 307         451 my $row_extent = $wcnt + FWR;
213 307         431 my $col_extent = LDC;
214 307 50       926 $col_extent += 1 if $self->head_week;
215              
216 307         1175 $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         3959307 foreach (keys %$itoc) {
221 9351         11977 my $cellref = $self->cell(@{$itoc->{$_}});
  9351         31711  
222 9351         1522667 $self->_itoc($_, $cellref);
223 9351         19922 $self->_ctoi($cellref, $_);
224             }
225              
226             # week num affects month/year spans
227 307 50       1633 my $width = $self->head_week ? 8 : 7;
228              
229             # month/year headers
230 307         963 my $cellref = $self->cell(0, 0);
231 307         49680 $self->_itoc($self->month, $cellref);
232 307         971 $self->_ctoi($cellref, $self->month);
233 307         1209 $cellref = $self->cell(0, $width - $self->year_span);
234 307         50527 $self->_itoc($self->year, $cellref);
235 307         992 $self->_ctoi($cellref, $self->year);
236              
237 307         951 $self->item($self->month)->replace_content($self->item_alias($self->month));
238 307         8931 $self->item($self->year)->replace_content($self->item_alias($self->year));
239              
240 307 50       7898 if ($self->_head_my) {
241 307 50 33     921 if ($self->head_m && $self->head_y) {
    0          
    0          
242 307         908 $self->item($self->year) ->attr('colspan', $self->year_span);
243 307         76522 $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         72313 my $trans;
260 307         1148 my $days = $self->loc->days;
261 307         958 foreach (0..$#$days) {
262             # Transform for week_begin 1..7
263 2149         6030 $trans = ($_ + $self->week_begin - 1) % 7;
264 2149         6612 my $cellref = $self->cell(1, $_);
265 2149         353876 $self->_itoc($days->[$trans], $cellref);
266 2149         4941 $self->_ctoi($cellref, $days->[$trans]);
267             }
268 307 50       1068 if ($self->head_dow) {
269 307         994 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       7943 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         481 my $i;
296 307         1065 foreach my $r (FWR .. $self->last_row) {
297 1598         31780 foreach my $c (0 .. LDC) {
298 11186 100       198425 $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       2141 if ($self->enable_css) {
305 307         1229 $self->push_attr(class => 'hcm-table');
306 307 50       13493 $self->item_row($self->dayheaders)->push_attr(class => 'hcm-day-head')
307             if $self->head_dow;
308 307 50       972474 $self->item($self->year)->push_attr(class => 'hcm-year-head')
309             if $self->head_y;
310 307 50       64899 $self->item($self->month)->push_attr(class => 'hcm-month-head')
311             if $self->head_m;
312 307 50       63297 $self->item($self->week_nums) ->push_attr(class => 'hcm-week-head')
313             if $self->head_week;
314             }
315              
316 307 50       1100 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         1029 $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   461 my $self = shift;
391 307         396 my $ct;
392 307 50       828 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         557 $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   453 my $self = shift;
409              
410 307         969 my $month = $self->monthnum($self->month);
411 307         952 my $year = $self->year;
412              
413 307         895 my $tool = $self->_datetool;
414              
415 307         1450 my $dow1st = $tool->dow1st; # 0..6, starting with Sun
416 307         1002 my $lastday = $tool->lastday;
417              
418             # week_begin given as 1..7 starting with Sun
419 307         927 $dow1st = ($dow1st - ($self->week_begin - 1)) % 7;
420              
421 307         942 $self->dow1st($dow1st);
422 307         858 $self->lastday($lastday);
423              
424 307         643 $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 958 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 497 my $self = shift;
589 307         1137 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 48766 my $self = shift;
625 3991 50       8873 @_ || croak "item(s) must be provided";
626 3991         10293 $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 533 my $self = shift;
632 307         676 $self->row(map { $self->row_of($_) } @_);
  2149         5168  
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 9603 my $self = shift;
713 6447 50 33     29676 croak "undefined value passed to coords_of()" if @_ && ! defined $_[0];
714 6447         13881 my $ref = $self->_itoc(@_);
715 6447 50       24839 my @pos = ref $ref ? $ref->position : ();
716 6447 50       938583 @pos ? (@pos[$#pos - 1, $#pos]) : ();
717             }
718              
719             sub item_at {
720             # convert grid coords into item
721 11186     11186 1 15719 my $self = shift;
722 11186         30480 $self->_ctoi($self->cell(@_));
723             }
724              
725             sub _itoc {
726             # item to grid
727 18561     18561   43778 my($self, $item, $ref) = splice(@_, 0, 3);
728 18561 50       44141 defined $item or croak "item required";
729 18561         48284 my $itoc = $self->_itoch;
730 18561 100       42549 if ($ref) {
731 12114 50       27197 croak "Reference required" unless ref $ref;
732 12114         24899 $itoc->{$item} = $ref;
733             }
734 18561         40868 $itoc->{$item};
735             }
736              
737             sub _ctoi {
738             # cell reference to item
739 23300     23300   1854988 my($self, $refstring, $item) = splice(@_, 0, 3);
740 23300 50       52206 defined $refstring or croak "cell id required";
741 23300         59440 my $ctoi = $self->_ctoih;
742 23300 100       53148 if (defined $item) {
743 12114         35159 $ctoi->{$refstring} = $item;
744             }
745 23300         104950 $ctoi->{$refstring};
746             }
747              
748             sub row_of {
749 2149     2149 1 2986 my $self = shift;
750 2149         4731 ($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 431 my $self = shift;
761 307 50       691 return $self->month unless @_;
762 307         867 my $loc = $self->loc;
763 307         537 my @names;
764 307         686 for my $m (@_) {
765 307 50 33     2413 $m = ($m - 1) % 12 if $m && $m =~ /^\d+$/;
766 307   33     1315 $m = $loc->monthname($m) || croak "month not found " . join(', ', @_);
767 307 50       1666 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 895 my $self = shift;
776 614 50       2073 my @months = @_ ? @_ : $self->month;
777 614         1700 my $loc = $self->loc;
778 614         909 my @nums;
779 614         1152 for my $m (@months) {
780 614 50 33     3237 $m = ($m - 1) % 12 if $m && $m =~ /^\d+$/;
781 614         2029 $m = $loc->monthnum($m);
782 614 50       1661 croak "month not found ", join(', ', @_) unless defined $m;
783 614         933 $m += 1;
784 614 50       2439 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 5553 my $class = shift;
855 309         1661 my %parms = @_;
856 309         516 my(%attrs, %tattrs);
857 309         1085 foreach (keys %parms) {
858 1229 50       2878 if (__PACKAGE__->_is_calmonth_attr($_)) {
859 1229         3310 $attrs{$_} = $parms{$_};
860             }
861             else {
862 0         0 $tattrs{$_} = $parms{$_};
863             }
864             }
865              
866 309         1738 my $self = CLASS_HET->new(%tattrs);
867 309         445897 bless $self, $class;
868              
869             # set defaults
870 309         1070 $self->_set_defaults;
871              
872 309         738 my $month = delete $attrs{month};
873 309         725 my $year = delete $attrs{year};
874 309 50 33     1648 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         940 $self->month($month);
881 309         829 $self->year($year);
882              
883             # set overrides
884 309         832 for my $k (keys %attrs) {
885 611 100       2705 $self->$k($attrs{$k}) if defined $attrs{$k};
886             }
887              
888 309 50       1034 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         1066 $self->loc($loc);
894              
895 309         973 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         1257 $self->_caltool($dt);
903              
904             $self->week_begin($loc->first_day_of_week + 1)
905 307 100       919 unless defined $attrs{week_begin};
906              
907             my $dom_now = defined $attrs{today} ? $dt->_dom_now(delete $attrs{today})
908 307 50       1738 : $dt->_dom_now;
909 307         1020 $self->today($dom_now);
910              
911 307   50     1486 my $alias = $attrs{alias} || {};
912 307 100       980 if ($self->full_days < 0) {
913 6         19 my @full = $self->loc->days;
914 6         21 my @narrow = $self->loc->narrow_days;
915 6         19 for my $i (0 .. $#narrow) {
916 42         108 $alias->{$full[$i]} = $narrow[$i];
917             }
918             }
919 307 50       910 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       1000 $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         1034 $self->_date($month, $year);
931              
932 307         2678 $self;
933             }
934              
935             ### overrides (our table is static)
936              
937       0 1   sub extent { }
938 42168     42168 1 1141812 sub maxrow { shift->SUPER::maxrow }
939 6329     6329 1 1776148 sub maxcol { shift->SUPER::maxcol }
940              
941             ### deprecated
942              
943 10     10   71 use constant row_offset => 0;
  10         20  
  10         630  
944 10     10   50 use constant col_offset => 0;
  10         17  
  10         477  
945 10     10   48 use constant first_col => 0;
  10         17  
  10         487  
946 10     10   51 use constant first_row => 0;
  10         21  
  10         412  
947 10     10   206 use constant first_week_col => 0;
  10         22  
  10         543  
948 10     10   50 use constant last_week_col => 6;
  10         18  
  10         613  
949              
950             ###
951              
952             1;
953              
954             __END__