KGRKJGETMRETU895U-589TY5MIGM5JGB5SDFESFREWTGR54TY
Server : Apache/2.4.62
System : FreeBSD fbsdweb2.web.rcn.net 14.1-RELEASE FreeBSD 14.1-RELEASE releng/14.1-n267679-10e31f0946d8 GENERIC amd64
User : www ( 80)
PHP Version : 8.3.8
Disable Function : NONE
Directory :  /domains/compasssysweb/calendar/CalciumDir39/Calendar/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /domains/compasssysweb/calendar/CalciumDir39/Calendar/TimeBlock.pm
# Copyright 2001-2003, Fred Steinberg, Brown Bear Software

# Utils for day or week block views, with vertical time blocks

package TimeBlock;
use strict;

use CGI;

sub new {
  my ($class, %args) = @_;
  my $self = {cgi     => CGI->new,
              op      => undef,
              dates   => [],
              headers => {},
              events  => {},
              %args};
  $self->{numHeaders} = keys %{$self->{headers}};
  bless $self, $class;
  $self->_initialize;
  $self;
}

sub hourLabels {
    shift->{hourLabels};
}

sub _initialize {
    my $self = shift;
    my $op = $self->{op};
    my $prefs = $op->prefs;
    my ($startHour, $numHours) = $op->getParams (qw (DayViewStart
                                                     DayViewHours));

    $numHours  = $prefs->DayViewHours     || 8 unless defined $numHours;
    $startHour = $prefs->DayViewStart          unless defined $startHour;
    $startHour = 9 unless defined $startHour;

    if ($startHour + $numHours > 24) {
        $startHour = 24 - $numHours;
    } elsif ($startHour < 0) {
        $startHour = 0;
    }

    # Convert integer hours to hour strings
    my $milTime = $prefs->MilitaryTime;
    my @hours = ($startHour .. ($startHour + $numHours - 1));
    @hours = map {_timeLabel ($_, $milTime)} @hours;

    $self->{startHour}  = $startHour;
    $self->{numHours}   = $numHours;
    $self->{hourLabels} = \@hours;

    $self->{colors}->{Hours} = [$prefs->color ('WeekHeaderBG') || '',
                                $prefs->color ('WeekHeaderFG') || ''];
    $self->{colors}->{Days}  = [$prefs->color ('DayHeaderBG') || '',
                                $prefs->color ('DayHeaderFG') || ''];
    $self->{colors}->{DaySep} = [$prefs->color ('MainPageBG') || '',
                                 $prefs->color ('MainPageFG') || ''];
    $self->{colors}->{Cells}  = [$prefs->color ('EventBG') || '',
                                 $prefs->color ('EventFG') || ''];

    $self->{fonts}->{Event}    = [$prefs->font ('BlockEvent')];
    $self->{fonts}->{Time}     = [$prefs->font ('BlockEventTime')];
    $self->{fonts}->{Include}  = [$prefs->font ('BlockInclude')];
    $self->{fonts}->{Category} = [$prefs->font ('BlockCategory')];

    return $self;
}

sub render {
    my $self = shift;
    my $cgi = $self->{cgi};

    my %tablesPerDay;           # key is date; val is "table"
    my %colsPerDay;             # number of columns each day needs

    my %untimedEvents;
    my $untimedCount = 0;

    # "Process" events for each day; 1 (or more) columns per day
    foreach my $date (@{$self->{dates}}) {
        my $events = $self->{events}->{"$date"};
        my $untimed;
        ($events, $untimed) = $self->filterAndMungeEvents ($events, $date);

        $untimedEvents{"$date"} = $untimed;
        $untimedCount += @$untimed;

        my @colsForDate;         # different columns for this single date
        my @table;
        foreach my $event (@$events) {
            my ($startRow, $numRows) = $self->getRowsForEvent ($event);
            next unless defined $startRow;
            my $column = $self->getColumnForEvent ($event, \@colsForDate);
            $table[$startRow][$column] = [$event, $numRows];

            # keep track so we don't put nbsp fillers later
            for (my $i=1; $i<$numRows; $i++) {
                $table[$startRow + $i][$column] = 'fnord';
            }
        }
        $colsPerDay{$date} = @colsForDate || 1; # don't want 0, if no events
        $tablesPerDay{$date} = \@table;
    }

    my @rows;

    my ($bg, $fg) = @{$self->{colors}->{Days}};
    my $fonts = $self->{fonts};
    my $op    = $self->{op};

    # Do header row; first item is blank cell above hour labels
#    my @tds = ($cgi->td ({bgcolor => $self->{colors}->{DaySep}->[0]},
    my @tds = ($cgi->td ({bgcolor => $self->{colors}->{Hours}->[0]},
                         '&nbsp;'));
    my @untimedTds = @tds;
    my $rowSpan = $self->{numHours} + 1 + ($untimedCount ? 1 : 0);
    my $colWidth = int (100 / $self->{numHeaders}) - 1 . '%';
    foreach my $date (@{$self->{dates}}) {
        push @tds, $cgi->td ({colspan => $colsPerDay{$date},
                              width   => $colWidth,     # IE needs this
                              bgcolor => $bg},
                             $cgi->font ({color => $fg},
                                         $self->{headers}->{$date}));
        push @tds, $cgi->td ({rowspan => $rowSpan,
                              bgcolor => $self->{colors}->{DaySep}->[0],
                              width   => '0%'}, ' ')
#                              width   => 1}, ' ')
            unless ($date == $self->{dates}->[-1]);

        my $untimedHTML = '';
        foreach my $ev (Event->sort ($untimedEvents{$date} || [],
                                     $op->prefs->EventSorting)) {
            my ($fg, $bg) = $ev->colors ($op->calendarName, $op->prefs,
                                         'no default');
            $bg ||= $op->prefs->color ('MonthTailBG');
            $fg ||= $op->prefs->color ('MonthTailFG');
            # Just for $textID
            my ($blah, $textID);
            my $incFrom = $ev->includedFrom || '';
            if ($incFrom ne $op->calendarName) {
                ($blah, $blah, $blah, $textID) =
                            $ev->getIncludedOverrides ($op->prefs->Includes);
            }
            my ($eventSize, $timeSize, $includeSize, $categorySize) =
                            ($fonts->{Event}->[1],  $fonts->{Time}->[1],
                             $fonts->{Include}->[1], $fonts->{Category}->[1]);
            $eventSize--    if defined ($eventSize);
            $timeSize--     if defined ($timeSize);
            $includeSize--  if defined ($includeSize);
            $categorySize-- if defined ($categorySize);

            my $html = $ev->getHTML ({op => $op,
                               calName   => $op->calendarName,
                               date      => $date,
                               prefs     => $op->prefs,
                               i18n      => $op->I18N,
                               textFG    => $fg,
                               textID    => $textID,
                               eventFace => $fonts->{Event}->[0],
                               eventSize => $eventSize,
                               timeFace  => $fonts->{Time}->[0],
                               timeSize  => $timeSize,
                               includeFace  => $fonts->{Include}->[0],
                               includeSize  => $includeSize,
                               categoryFace  => $fonts->{Category}->[0],
                               categorySize  => $categorySize});
            $untimedHTML .= $cgi->table ({-width => '100%',},
                                         $cgi->Tr ($cgi->td ({bgcolor => $bg},
                                                             $html)));
        }
        $untimedHTML ||= '&nbsp;';
        push @untimedTds, $cgi->td ({colspan => $colsPerDay{$date},
                                     bgcolor => $op->prefs->color
                                                        ('MonthTailBG')},
                                    $untimedHTML);
    }

    push @rows, $cgi->Tr ({align => 'center'}, @tds);
    push @rows, $cgi->Tr ({valign => 'top'}, @untimedTds) if $untimedCount;

    my $showTimes = $op->prefs->TimePlanShowTimes || 'always';

    # And build up the table, one row at a time
    my @hourLabels = @{$self->hourLabels};
    for (my $row=0; $row<$self->{numHours}; $row++) {
        # First, the leftmost hour cell
        my @tds = ($cgi->td ({-width => '5%',
                              -bgcolor => $self->{colors}->{Hours}->[0]},
                         $cgi->font ({-color => $self->{colors}->{Hours}->[1]},
                                     shift @hourLabels)));
        # Then, cells for each day. 1 (or more) columns per day.
        foreach my $date (@{$self->{dates}}) {
            my $nbspSpan = 0;
            for (my $col=1; $col<=$colsPerDay{$date}; $col++) {
                my $eventInfo = $tablesPerDay{$date}[$row][$col];
                if (ref $eventInfo) {
                    # If needed, put spaces which come before event this row
                    if ($nbspSpan) {
                        push @tds, $cgi->td ({-colSpan => $nbspSpan},
                                             '&nbsp;');
                        $nbspSpan = 0;
                    }
                    my $ev = $eventInfo->[0];
                    my ($fg, $bg) = $ev->colors ($op->calendarName,
                                                 $op->prefs);

                    # Just for $textID
                    my ($blah, $textID);
                    my $incFrom = $ev->includedFrom || '';
                    if ($incFrom ne $op->calendarName) {
                        ($blah, $blah, $blah, $textID) =
                            $ev->getIncludedOverrides ($op->prefs->Includes);
                    }

                    # Maybe don't display times
                    my $hideTimes;
                    if (lc ($showTimes) eq 'never') {
                        $hideTimes = 1;
                    } elsif (lc ($showTimes eq 'unaligned')) {
                        $hideTimes = 1 unless (($ev->startTime || 0) % 60
                                               or
                                               ($ev->endTime   || 0) % 60);
                    }

                    push @tds, $cgi->td ({-rowSpan => $eventInfo->[1],
                                          -bgcolor => $bg},
                                         $ev->getHTML ({op => $op,
                                             calName   => $op->calendarName,
                                             date      => $date,
                                             prefs     => $op->prefs,
                                             i18n      => $op->I18N,
                                             textFG    => $fg,
                                             textID    => $textID,
                                             eventFace => $fonts->{Event}->[0],
                                             eventSize => $fonts->{Event}->[1],
                                             hideTimes => $hideTimes,
                                             timeFace  => $fonts->{Time}->[0],
                                             timeSize  => $fonts->{Time}->[1],
                                      includeFace  => $fonts->{Include}->[0],
                                      includeSize  => $fonts->{Include}->[1],
                                      categoryFace  => $fonts->{Category}->[0],
                                      categorySize  => $fonts->{Category}->[1],
                                                       }));
                } elsif ($eventInfo and $eventInfo eq 'fnord') {
                    if ($nbspSpan) {
                        push @tds, $cgi->td ({-colSpan => $nbspSpan},
                                             '&nbsp;');
                        $nbspSpan = 0;
                    }
                } else {
                    $nbspSpan++; # fill in for multi-row events in other cols.
                }
            }
            if ($nbspSpan) {
                my $data = '&nbsp; &nbsp;'; # workaround for goofy browsers
                push @tds, $cgi->td ({-colSpan => $nbspSpan}, $data);
                $nbspSpan = 0;
            }
        }
        push @rows, $cgi->Tr (@tds);
    }

    ($bg, $fg) = @{$self->{colors}->{Cells}};
    my $html = $cgi->table ({border      => 1,
                             cellpadding => 2,
                             cellspacing => 1,
                             bgcolor     => $bg,
                             width       => '100%',
                             cols        => $self->{numHeaders} * 2
                            },
                            @rows);
}

sub filterAndMungeEvents {
    my ($self, $events, $date) = @_;

    my (@events, @untimed);

    # First, keep only events with times
    foreach my $event (@$events) {
        if (defined $event->startTime) {
            push @events, $event;
        } else {
            push @untimed, $event;
        }
    }

    my %munged;

    # Keep track of which events (if any) start on previous
    # day.
    foreach (@events) {
        if ($_->Date and $_->Date != $date) {
            my $key = $_->includedFrom || '' . $_->id;
            $munged{$key} ||= 1;
        }
    }

    $self->{displayAtMidnight} = \%munged;

    # Sort on time; if starts on previous day, start time is 0
    @events = sort {if    ($munged{$a->includedFrom || '' . $a->id}) {-1}
                    elsif ($munged{$b->includedFrom || '' . $b->id}) {1}
                    else { $a->startTime <=> $b->startTime or
                          ($a->endTime || 0) <=> ($b->endTime || 0)}} @events;
    return (\@events, \@untimed);
}

sub getRowsForEvent {
    my ($self, $event) = @_;
    my $startTime = $self->_displayTime ($event);
    my $endTime   = $event->endTime;

    my $startHour = $self->{startHour};
    my $numHours  = $self->{numHours};

    $endTime = $startTime if (defined $startTime and !defined $endTime);

    $endTime = 1440 if ($endTime < $startTime); # it ends on next day

    # out of bounds; just return;
    return if ($startTime >= ($startHour + $numHours) * 60 or
               $endTime   <  $startHour * 60               or
               ($endTime != $startTime and $endTime == $startHour * 60));

    my ($startRow, $rowSpan, $blah);
    $startRow  = int ($startTime / 60) - $startHour;
    $startRow = 0 if ($startRow < 0);

    $blah = $endTime / 60;
    my $endHour = int $blah;
    $endHour++ unless ($endHour == $blah);
    $endHour = $numHours + $startHour if $endHour > $numHours + $startHour;
    $rowSpan = $endHour - $startHour - $startRow;
    $rowSpan = 1 if $rowSpan < 1;

    return ($startRow, $rowSpan);
}

# Find which column an event goes in for a particular day; only multiple
# columns per day if there are time conflicts in that day.
# Returns the number of the column, e.g. '3'
sub getColumnForEvent {
    my ($self, $event, $columns) = @_;

    # $columns is list of list of events already placed in each column

    my ($isInColumn, $i);
    foreach my $thisColumn (@$columns) {
        $i++;
        next if $self->_conflicts ($event, $thisColumn);
        push @$thisColumn, $event;
        $isInColumn = $i;
        last;
    }
    unless ($isInColumn) {
        push @$columns, [$event]; # put new listref, w/event in it
        $isInColumn = @$columns;
    }
    return $isInColumn;
}

sub hourControls {
    my ($self) = @_;

    my $cgi   = $self->{cgi};
    my $op    = $self->{op};
    my $i18n  = $op->I18N;
    my $prefs = $op->prefs;
    my $startHour = $self->{startHour};
    my $numHours  = $self->{numHours};

    return '' if (($prefs->DayViewControls || '') eq 'hide');

    my $milTime = $prefs->MilitaryTime;

    my @startAtValues = (0..23);
    my %startAtLables = map {$_ =>_timeLabel ($_, $milTime)} @startAtValues;
    my ($hours18, $hour18) = ($i18n->get ('hours'), $i18n->get ('hour'));
    my %displayLabels = map {$_ => "$_ $hours18"} (1..24);
    $displayLabels{1} = "1 $hour18";

    my ($full, $half) = ($numHours, int ($numHours / 2));
    my $backFull = $op->makeURL ({DayViewStart => $startHour - $full});
    my $backHalf = $op->makeURL ({DayViewStart => $startHour - $half});
    my $foreHalf = $op->makeURL ({DayViewStart => $startHour + $half});
    my $foreFull = $op->makeURL ({DayViewStart => $startHour + $full});

    my ($bg, $fg) = ($prefs->color ('DayViewControlsBG') || '',
                     $prefs->color ('DayViewControlsFG') || '');
    my ($face, $size) = $prefs->font ('DayViewControls');

    my $fontParams = {-color => $fg,
                      -face  => $face,
                      -size  => $size};
    my @links =
        ($cgi->a ({href => $backFull},
                  $cgi->font ($fontParams, "<$full " . $i18n->get ('hours'))),
         '&nbsp;',
         $cgi->a ({href => $backHalf},
                  $cgi->font ($fontParams, "<$half " . $i18n->get ('hours'))),
         '&nbsp;',
         $cgi->b ($cgi->font ($fontParams, $i18n->get ('Shift Hours'))),
         '&nbsp;',
         $cgi->a ({href => $foreHalf},
                  $cgi->font ($fontParams, "$half " . $i18n->get('hours') .
                              '>')),
         '&nbsp;',
         $cgi->a ({href => $foreFull},
                  $cgi->font ($fontParams, "$full " . $i18n->get('hours') .
                              '>')));

    my $hourShifts = $cgi->td (\@links);

    my $html = $cgi->startform;

    if (!$prefs->PrintPrefs) {
        $html .= $cgi->table
            ({align       => 'center',
              width       => '100%',
              cellspacing => 0,
              cellpadding => 0,
              border      => 0,
              bgcolor     => 'gray'},
             $cgi->Tr ({-align   => 'left',
                        -bgcolor => $bg},
                       $cgi->td
                       ('&nbsp;' .
                        $cgi->font
                        ($fontParams,
                         $i18n->get ('Start at: ') .
                         $cgi->popup_menu (-name     => 'DayViewStart',
                                           -onChange =>
                                           'document.forms[0].submit()',
                                           -default  => $startHour,
                                           -values   => \@startAtValues,
                                           -labels   => \%startAtLables))),
                       $cgi->td
                       ($cgi->font
                        ($fontParams,
                         $i18n->get ('Display: '),
                         $cgi->popup_menu (-name     => 'DayViewHours',
                                           -onChange =>
                                           'document.forms[0].submit()',
                                           -default  => $numHours,
                                           -values   => [1..24],
                                           -labels   => \%displayLabels))),
                       $hourShifts));
    }

    $html .= $cgi->hidden (-name  => 'CalendarName',
                           -value => $op->calendarName);
    $html .= $op->hiddenDisplaySpecs;
    $html .= $cgi->endform;
    return $html;
}

sub _timeLabel {
    my ($hour, $milTime) = @_;
    my $amPm = '';
    if (!$milTime) {
        $amPm = $hour < 12 ? 'am ' : 'pm ';
        $hour = 12 if $hour == 0;
        $hour -= 12 if ($hour > 12);
    }
    return "$hour:00" . $amPm;
}

# ------------------------------------------------------------------


# Note that in KISS model, we must round to floor, ceiling hours...
sub _conflicts {
    my ($self, $event, $col) = @_;
    my ($evStart, $evEnd) = $self->_normTime ($event);
    foreach my $ev (@$col) {
        my ($start, $end) = $self->_normTime ($ev);
        next if ($evStart >= $end);
        next if ($evEnd   <= $start);
        return 1;
    }
    return undef;
}

sub _normTime {
    my ($self, $event) = @_;
    my $startTime = $self->_displayTime ($event);
    my $start = int ($startTime / 60);
    my $blah = defined $event->endTime ? $event->endTime
                                       : ($startTime + 1);
    $blah /= 60;
    my $end  = int $blah;
    $end++ unless ($end == $blah);
    $end = 24 if ($end < $start); # if it ends tomorrow
    return ($start, $end);
}

sub _displayTime {
    my ($self, $event) = @_;
    my $key = $event->includedFrom || '' . $event->id;
    return $self->{displayAtMidnight}->{$key} ? 0 : $event->startTime;
}

1;

Anon7 - 2021