Newsgroup: it.comp.lang.perl
Titolo: calendario in html

Salve,
     il seguente script mi riporta un calendarietto del mese corrente:
#!/perl -w
use strict;
use Time::Local;
use Carp;
my @days = qw(31 xx 31 30 31 30 31 31 30 31 30 31);
my @month = calendar(10, 2005, 1);
print "Mo Tu We Th Fr Sa Su\n";
  foreach (@month) {
    print map { $_ ? sprintf "%2d ", $_ : '   ' } @$_;
    print "\n";
  }
 
__END__;
Mo Tu We Th Fr Sa Su
                1  2 
 3  4  5  6  7  8  9 
10 11 12 13 14 15 16 
17 18 19 20 21 22 23 
24 25 26 27 28 29 30 
31
mi piacerebbe incastrare questo calendario in una tabella html 
table,tr,td, ma la riga: map { $_ ? sprintf "%2d ", $_ : '   ' } @$_; è 
un pò criptica...
qualcuno mi può aiutare?
grazie
-- 
Alla batteria dai retta, ballA!

Undefined subroutine &main::calendar called at 
calendar.pl
 line 9.
Probabilmente ti sei perso la funzione calendar per strada. Ad occhio e
croce, questa funzione restituisce un AoA (array of arrays), ciascuno
dei quali contiene il valore del giorno oppure qualcosa come undef, ''
o 0 (o anche '0').
L'espressione:
   $_ ? sprintf "%2d ", $_ : '   '
fondamentalmente dice: se "$_" può essere considerata "vera" (ossia,
è differente da undef, "", 0 o "0"), allora restituisci sprintf "%2d
", $_, altrimenti restituisci una stringa con tre spazi. Trovi questo
in perldoc perlop.
La map applica questa espressione ternaria al suo ingresso (ossia
l'array @$_) e raccoglie tutte queste sottostringhe, che vengono
passate a print. Trovi questo in perldoc -f map.

 Undefined subroutine &main::calendar called at 
calendar.pl
 line 9.
sub calendar {
  my ($mon, $year, $start_day) = @_;
  my @now = (localtime)[4, 5];
  $mon = ($now[0] + 1) unless $mon;
  $year = ($now[1] + 1900) unless $year;
  $start_day = 0 unless defined $start_day;
  # croak "Year $year out of range" if $year < 1970 && !$dt;
  croak "Month $mon out of range" if ($mon  < 1 || $mon > 12);
  croak "Start day $start_day out of range" if ($start_day < 0 || 
$start_day > 6);
  my $first;
  $first = (localtime timelocal 0, 0, 0, 1, $mon -1, $year - 1900)[6];
  $first -= $start_day;
  $first += 7 if ($first < 0);
  my @mon = (1 .. _days($mon, $year));
  my @first_wk = (undef) x 7;
  @first_wk[$first .. 6] = splice @mon, 0, 6 - $first + 1;
  my @month = (\@first_wk);
  while (my @wk = splice @mon, 0, 7) {
    push @month, \@wk;
  }
  $#{$month[-1]} = 6;
  return wantarray ? @month : \@month;
}
sub _days {
  my ($mon, $yr) = @_;
  return $days[$mon - 1] unless $mon == 2;
  return _isleap($yr) ? 29 : 28;
}
sub _isleap {
  return 1 unless $_[0] % 400;
  return   unless $_[0] % 100;
  return 1 unless $_[0] % 4;
  return;


 un pò criptica...
Un modo (schematico e puramente esemplificativo) per ottenere quello che 
  hai richiesto, è il seguente.
Sostituisci il blocco:
 > print "Mo Tu We Th Fr Sa Su\n";
 >
 >   foreach (@month) {
 >     print map { $_ ? sprintf "%2d ", $_ : '   ' } @$_;
 >     print "\n";
 >   }
con:
	print '<table><tr>';
	print "<td>$_</td>" foreach qw(Mo Tu We Th Fr Sa Su);
	print '</tr>';
	foreach ( @month ) {
		print '<tr style="text-align: right;">';
		print "<td>$_</td>" for @$_;
		print '</tr>'
	}
	print '</table>';
Inutile aggiungere che per ottenere una pagina HTML correttamente 
visualizzabile dal browser, dovrai ovviamente (perlomeno) stampare prima 
gli header corretti ed i tag di apertura e chiusura dell'HTML.
Una cosa del tipo:
	use CGI ':standard';
	print header;
	print start_html;
	# qui va il codice di generazione
	# della tabella esposto sopra
	print end_html;
Detto tutto questo, l'esempio riportato serviva solo per rispondere 
direttamente alla tua domanda, ma non per incoraggiarti ad usarlo, 
perché la generazione in-line dell'HTML è una pessima cosa in termini di 
leggibilità e scalabilità del codice.
Per concludere, su CPAN c'era già tutto bell'e pronto ;-)
HTML::CalendarMonth
http://www.mojotoad.com/sisk/projects/HTML-CalendarMonth/examples.html
HTML::Calendar::Simple
http://search.cpan.org/~strytoast/HTML-Calendar-Simple-0.04/Simple.pm
... E magari anche qualcos'altro.
Ciao,
Emanuele.

 	print '</table>';
ti ringrazio ma senti,
mi piacerebbe che il giorno odierno fosse messo in grassetto...
come modifico:    print "<td>$_</td>" for @$_; ?? (sapendo che in $today 
c'è il giorno odierno: 29)
ciao e grazie!


 c'è il giorno odierno: 29)
	for ( @$_ ) {
		my $day = $_ eq $today ? "<b>$_</b>" : $_;
		print "<td>$day</td>"
	};
Rimane a te la responsabilità di dichiarare e valorizzare correttamente 
$today (prima del ciclo).
Con la stessa tecnica puoi cambiare anche il colore, aggiungere un link etc.
Ciao,
Emanuele.

 Con la stessa tecnica puoi cambiare anche il colore, aggiungere un link etc.
infatti mi piacerebbe aggiungere il link alle celle non vuote...





Direi che a questo punto sarebbe più produttivo (per entrambi ;-) che tu 
mi dicessi in dettaglio cosa c'è che ancora non è chiaro nel codice che 
ti ho mostrato.
P.S.
Il mio news server fa le bizze e non mostra l'ultimo messaggio che avevo 
postato in questo thread, varie ore fa.
Lo incollo qui di sotto.
===========================
Emanuele Zeppieri wrote:
 >
 >     for ( @$_ ) {
 >         my $day = $_ eq $today ? "<b>$_</b>" : $_;
 >         print "<td>$day</td>"
 >     };
 >
Mi sono accorto soltanto adesso che la versione del codice sopra 
fornita, pur funzionando correttamente, genera un warning (se hai i 
warning attivati, ovviamente), che va a finire nel file di log del tuo 
web server.
Questa è la versione warning-free:
     for ( @$_ ) {
         my $day =
             defined $_ ?
                 $_ eq $today ? "<b>$_</b>" : $_
             : '&nbsp;';
         print "<td>$day</td>"
     };
Che è identica, a parte un test in più e la valorizzazione di $day con 
l'entity HTML non-breaking space se $_ non è definito.
Ciao,
Emanuele.

      };
perfetto!!!!!!!
grazie mille!


 >      };
purtroppo ho due problemi, 
foreach (@month) {
   $output .= qq~</tr>~;
   for ( @$_ ) {
      my $day = defined $_ ? $_ eq $today ? "<b>$_</b>" : $_ : '&nbsp;';
 
      if ($day eq '&nbsp;')
      {
         $output .= qq~<td>$day</td>~;
      } else {
         $output .= qq~<td><a href="$current_year-$current_month-$day" 
title="$current_year-$current_month-$day" class="link">$day</a></td>~;
      }
 
   }
   $output .= qq~</tr>~;
}
quando passo il mouse sul link leggo tutte le date tipo 2005-10-29 
etc... ma quando passo sulla data odierna (quella in grassetto) mi trovo 
sul link: 2005-10-<b>30</b> !!!! 
l'ultimo problema è che vorrei controllare $_ eq $today e se $current = 
true prima di mettere il giorno in grassetto...
grazie mille


 true prima di mettere il giorno in grassetto...
Più che un problema questa mi sembra una nuova richiesta. Ho messo anche
questo ulteriore test nel codice (sempre che abbia capito bene).
Per concludere, ti faccio vedere una soluzione che sfrutta le funzioni
di generazione dell'HTML di CGI.pm e la loro proprietà distributiva.
Neanche questo è il massimo in termini di disaccoppiamento (l'uso di un
template engine tipo TT sarebbe meglio) ma è già un grosso passo avanti
rispetto a del codice procedurale intercalato disordinatamente con
l'HTML (questo lo fanno i programmatori PHP, noi siamo diversi ;-)
my @cal_days;
push
     @cal_days,
     td([
         map
             {
                 $_ ?
                     a(
                         {-href => "$current_year-$current_month-$_"},
                         $current && $_ eq $today ? b($_) : $_
                     )
                 : ''
             }
             @$_
     ])
         foreach @month;
print table(
     Tr(
         {-style => 'text-align: center;'},
         [ th( [qw(Mo Tu We Th Fr Sa Su)] ) ]
     ),
     Tr(
         { -style => 'text-align: right;'},
         [ @cal_days ]
     )
);
Sembra complicato? In realtà non lo è: grazie all'assenza dell'HTML
in-line il codice è molto più leggibile e scalabile (posto che si
conosca bene il modulo CGI).
Ad esempio vuoi aggiungere un bordo di spessore 1 alla tabella? Basta
passare un ulteriore parametro (al *primo* posto) alla funzione table:
print table(
     {-border => 1},
     # qui gli altri 2 parametri Tr() esattamente come sopra.
)
Vuoi generare codice XHTML compliant? Basta sostituire le funzioni (e i
parametri) che generano tag non conformi, ad esempio l'invocazione
b($_)
con:
span( {-style => 'font-weight: bold;'}, $_ )
Vuoi che il giorno corrente oltre che in neretto sia anche più grande?
Allora basta usare:
span( {-style => 'font-weight: bold; font-size: larger;'}, $_ )
Vuoi che il giorno corrente appaia anche di un colore diverso?
span( {-style => 'font-weight:bold; font-size:larger; color:red'}, $_ )
Vuoi che la riga con i nomi dei giorni abbia un colore di sfondo diverso?
print table(
     {-border => 1},
     Tr(
         {-style => 'text-align: center; background-color: yellow;'},
         [ th( [qw(Mo Tu We Th Fr Sa Su)] ) ]
     ),
     Tr(
         { -style => 'text-align: right;'},
         [ @cal_days ]
     )
);
Eccetera, ad libitum.
Certo, con moduli ad-hoc tipo HTML::Table sarebbe stato più facile, e
con HTML::CalendarMonth ancora di più! Ma tu non mi dai retta...
Ciao,
Emanuele.
P.S.
Esercizio per il lettore interessato: generare il colore di sfondo per
le righe del corpo della tabella alternativamente bianco e grigio.
Suggerimento: basta aggiungere un parametro all'invocazione di td() e...

 di generazione dell'HTML di CGI.pm
senza parole, grazie mille!


 
Di nulla, è un piacere ;-)
Solo un minuscolo intervento di refactoring al mio codice: la variabile
@cal_days avrei fatto meglio a chiamarla @weeks; si tratta di un array i 
cui elementi sono i reference agli array delle settimane, per cui questo 
nome è molto più appropriato.
Ciao,
Emanuele.

 
Come dice Emanuele, su CPAN c'è già tutto quello che serve a te per quanto
riguarda il widget calendario. 
Inoltre mi permetto di suggerirti (ancora) l'analisi di questo calendario
JavaScript (visto che a te serve un calendario HTML quindi da visualizzare sul
sul browser)
 
http://www.dynarch.com/projects/calendar
E' free, opensource e completamente customizzabile (con supporto skin css) e non
carica il server dell'overhead per il rendering HTML
by
	- Enrico
--- have fun with 
www.perl.it
 ---