summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-x.local/bin/rem2html790
1 files changed, 790 insertions, 0 deletions
diff --git a/.local/bin/rem2html b/.local/bin/rem2html
new file mode 100755
index 0000000..f298fb3
--- /dev/null
+++ b/.local/bin/rem2html
@@ -0,0 +1,790 @@
1#!/usr/bin/env perl
2# SPDX-License-Identifier: GPL-2.0-only
3
4use strict;
5use warnings;
6
7use Getopt::Long;
8use JSON::MaybeXS;
9
10my %Options;
11
12my $rem2html_version = '2.1';
13
14my($days, $shades, $moons, $classes, $Month, $Year, $Numdays, $Firstwkday, $Mondayfirst, $weeks,
15 @Daynames, $Nextmon, $Nextlen, $Prevmon, $Prevlen);
16
17my $TIDY_PROGNAME = $0;
18$TIDY_PROGNAME =~ s|^.*/||;
19
20# rem2html -- convert the output of "remind -pp" to HTML
21
22=head1 NAME
23
24rem2html - Convert the output of "remind -pp" to HTML
25
26=head1 SYNOPSIS
27
28 remind -pp ... | rem2html [options]
29
30You can also use the old interchange format as below, but the -pp
31version is preferred.
32
33 remind -p ... | rem2html [options]
34
35=head1 OPTIONS
36
37=over 4
38
39=item --help, -h
40
41Print usage information
42
43=item --version
44
45Print version
46
47=item --backurl I<url>
48
49When producing the small calendar for the previous month, make the
50month name a link to I<url>.
51
52=item --forwurl I<url>
53
54When producing the small calendar for the next month, make the
55month name a link to I<url>.
56
57=item --imgbase I<url>
58
59When creating URLs for the stylesheet or external images, use I<url>
60as the base URL.
61
62=item --pngs
63
64Normally, rem2html uses inline "data:" URLs for the moon phase images,
65yielding a standalone HTML file. The C<--pngs> option makes it use
66external images named firstquarter.png, fullmoon.png, lastquarter.png
67and newmoon.png, which are expected to live in C<--imgbase>.
68
69=item --stylesheet I<url.css>
70
71Use I<url.css> as the stylesheet. If this option is used,
72I<url.css> is interpreted relative to B<imgbase> I<unless> it starts
73with a "/".
74
75=item --nostyle
76
77Produce basic HTML that does not use a CSS stylesheet.
78
79=item --tableonly
80
81Output results as a E<lt>tableE<gt> ... E<lt>/tableE<gt> sequence only
82without any E<lt>htmlE<gt> or E<lt>bodyE<gt> tags.
83
84=item --title I<title>
85
86Use I<title> as the content between E<lt>titleE<gt> and E<lt>/titleE<gt>
87tags.
88
89
90=item --prologue I<html_text>
91
92Insert I<html_text> right after the E<lt>bodyE<gt> tag.
93
94=item --epilogue I<html_text>
95
96Insert I<html_text> right before the E<lt>/bodyE<gt> tag.
97
98=back
99
100=head1 SPECIALS SUPPORTED
101
102The rem2html back-end supports the following SPECIAL reminders:
103
104=over
105
106=item HTML
107
108Add an HTML reminder to the calendar. All HTML tags are available.
109
110=item HTMLCLASS
111
112Add a CSS class to the box representing the trigger date. See
113"HIGHLIGHTING TODAY" for an example
114
115=item WEEK, MOON, SHARE, COLOR
116
117The standard SPECIALs supported by all back-ends
118
119=back
120
121=head1 HIGHLIGHTING TODAY
122
123Older versions of rem2html used to highlight today's date with a red outline.
124The current version does not do that by default. If you wish to highlight
125today's date, add the following reminder to your reminders file:
126
127 REM [realtoday()] SPECIAL HTMLCLASS rem-today
128
129=head1 AUTHOR
130
131rem2html was written by Dianne Skoll with much inspiration from an
132earlier version by Don Schwarz.
133
134=head1 HOME PAGE
135
136L<https://dianne.skoll.ca/projects/remind/>
137
138=head1 SEE ALSO
139
140B<remind>, B<rem2ps>, B<rem2pdf>, B<tkremind>
141=cut
142
143sub usage
144{
145 my ($exit_status) = @_;
146 if (!defined($exit_status)) {
147 $exit_status = 1;
148 }
149 print STDERR <<"EOM";
150$TIDY_PROGNAME: Produce an HTML calendar from the output of "remind -pp"
151
152Usage: remind -pp ... | rem2html [options]
153
154Options:
155
156--help, -h Print usage information
157--man Show man page (requires "perldoc")
158--version Print version
159--backurl url Make the title on the previous month's small calendar
160 entry a link to <url>
161--forwurl url Same as --backurl, but for the next month's small calendar
162--imgbase url Base URL of images and default stylesheet file
163--pngs Use external .PNG images for moon phases rater than
164 inline data: URLs
165--stylesheet url.css URL of CSS stylesheet. If specified, imgbase is NOT
166 prepended to url.css
167--nostyle Produce basic HTML that does not use a CSS stylesheet
168--tableonly Output results as a <table> only, no <html>, <body>, etc.
169--title string What to put in <title>...</title> tags
170--prologue html_text Text to insert at the top of the body
171--epilogue html_text Text to insert at the end of the body
172EOM
173 exit($exit_status);
174}
175
176sub smoosh
177{
178 my ($first, $second) = @_;
179 return $second unless defined ($first);
180 return $second if $first eq '';
181 return $second if ($second =~ m|^/|); # Absolute path given for second
182
183 # Squash multiple slashes
184 $first =~ s|/+|/|g;
185
186 # Special case
187 return "/$second" if ($first eq '/');
188
189 # Delete trailing slash
190 $first =~ s|/$||;
191
192 return "$first/$second";
193}
194
195sub parse_options
196{
197 local $SIG{__WARN__} = sub { print STDERR "$TIDY_PROGNAME: $_[0]\n"; };
198 if (!GetOptions(\%Options, "help|h",
199 "man",
200 "pngs",
201 "version",
202 "stylesheet=s",
203 "nostyle",
204 "backurl=s",
205 "forwurl=s",
206 "title=s",
207 "prologue=s",
208 "epilogue=s",
209 "imgbase=s",
210 "tableonly")) {
211 usage(1);
212 }
213 $Options{title} ||= 'HTML Calendar';
214
215 my $stylesheet = $Options{stylesheet};
216 if ($stylesheet) {
217 $Options{stylesheet} = smoosh($Options{imgbase}, $stylesheet);
218 }
219}
220
221sub start_output
222{
223 return if ($Options{tableonly});
224
225 print("<!doctype html>\n");
226 print("<html>\n<head>\n<title>" . $Options{title} . "</title>\n");
227 print('<meta charset="utf-8">' . "\n");
228 print('<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">' . "\n");
229 if (!$Options{nostyle}) {
230 if ($Options{stylesheet}) {
231 print('<link rel="stylesheet" type="text/css" href="' .
232 $Options{stylesheet} . '">' . "\n");
233 } else {
234 print("<style>\n");
235 print default_stylesheet();
236 print("</style>\n");
237 }
238 }
239 print("</head>\n<body>\n");
240 if ($Options{prologue}) {
241 print $Options{prologue} . "\n";
242 }
243}
244
245sub end_output
246{
247 return if ($Options{tableonly});
248 if ($Options{epilogue}) {
249 print $Options{epilogue} . "\n";
250 }
251 print("</body>\n</html>\n");
252}
253
254sub parse_input
255{
256 undef $days;
257 undef $shades;
258 undef $moons;
259 undef $classes;
260 undef $weeks;
261
262 my $found_data = 0;
263 while(<STDIN>) {
264 chomp;
265 last if /^\# rem2ps2? begin$/;
266 }
267
268 my $line;
269 # Month Year numdays firstday monday_first_flag
270 $line = <STDIN>;
271 return 0 unless $line;
272 chomp($line);
273 ($Month, $Year, $Numdays, $Firstwkday, $Mondayfirst) = split(' ', $line);
274
275 $Month =~ s/_/ /g;
276 # Day names
277 $line = <STDIN>;
278 return 0 unless $line;
279 chomp($line);
280 @Daynames = split(' ', $line);
281
282 for (my $i=0; $i<7; $i++) {
283 $Daynames[$i] =~ s/_/ /g;
284 }
285
286 # Prevmon prevlen
287 $line = <STDIN>;
288 return 0 unless $line;
289 chomp($line);
290 ($Prevmon, $Prevlen) = split(' ', $line);
291 $Prevmon =~ s/_/ /g;
292
293 # Nextmon nextlen
294 $line = <STDIN>;
295 return 0 unless $line;
296 chomp($line);
297 ($Nextmon, $Nextlen) = split(' ', $line);
298 $Nextmon =~ s/_/ /g;
299
300 $found_data = 1;
301 my $class;
302 if ($Options{nostyle}) {
303 $class = '';
304 } else {
305 $class = ' class="rem-entry"';
306 }
307 while(<STDIN>) {
308 chomp;
309 last if /^\# rem2ps2? end$/;
310 next if /^\#/;
311 my ($y, $m, $d, $special, $tag, $duration, $time, $body);
312 if (m/^(\d*).(\d*).(\d*)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*(.*)$/) {
313 ($y, $m, $d, $special, $tag, $duration, $time, $body) =
314 ($1, $2, $3, $4, $5, $6, $7, $8);
315 } elsif (/\{/) {
316 my $obj = decode_json($_);
317 next unless ($obj->{date} =~ /^(\d+)-(\d+)-(\d+)$/);
318 $y = $1;
319 $m = $2;
320 $d = $3;
321 $special = $obj->{passthru} || '*';
322 $tag = $obj->{tags} || '*';
323 $duration = $obj->{duration} || '*';
324 $time = $obj->{time} || '*';
325 $body = $obj->{body};
326 } else {
327 next;
328 }
329 my $d1 = $d;
330 $d1 =~ s/^0+//;
331 $special = uc($special);
332 if ($special eq 'HTML') {
333 push(@{$days->[$d]}, $body);
334 } elsif ($special eq 'HTMLCLASS') {
335 $classes->[$d] = $body;
336 } elsif ($special eq 'WEEK') {
337 $body =~ s/^\s+//;
338 $body =~ s/\s+$//;
339 $weeks->{$d1} = $body;
340 } elsif ($special eq 'MOON') {
341 if ($body =~ /(\S+)\s+(\S+)\s+(\S+)\s+(.*)$/) {
342 my ($phase, $moonsize, $fontsize, $msg) = ($1, $2, $3, $4);
343 $moons->[$d]->{'phase'} = $phase;
344 $moons->[$d]->{'msg'} = $msg;
345 } elsif ($body =~ /(\S+)/) {
346 $moons->[$d]->{'phase'} = $1;
347 $moons->[$d]->{'msg'} = '';
348 }
349 } elsif ($special eq 'SHADE') {
350 if ($body =~ /(\d+)\s+(\d+)\s+(\d+)/) {
351 $shades->[$d] = sprintf("#%02X%02X%02X",
352 ($1 % 256), ($2 % 256), ($3 % 256));
353 } elsif ($body =~ /(\d+)/) {
354 $shades->[$d] = sprintf("#%02X%02X%02X",
355 ($1 % 256), ($1 % 256), ($1 % 256));
356 }
357 } elsif ($special eq 'COLOR' || $special eq 'COLOUR') {
358 if ($body =~ /(\d+)\s+(\d+)\s+(\d+)\s+(.*)$/) {
359 my($r, $g, $b, $text) = ($1, $2, $3, $4);
360 my $color = sprintf("style=\"color: #%02X%02X%02X;\"",
361 $r % 256, $g % 256, $b % 256);
362 push(@{$days->[$d]}, "<p$class $color>" . escape_html($text) . '</p>');
363 }
364 } elsif ($special eq '*') {
365 push(@{$days->[$d]}, "<p$class>" . escape_html($body) . '</p>');
366 }
367 }
368 return $found_data;
369}
370
371sub small_calendar
372{
373 my($month, $monlen, $url, $first_col) = @_;
374 if ($Mondayfirst) {
375 $first_col--;
376 if ($first_col < 0) {
377 $first_col = 6;
378 }
379 }
380
381 if ($Options{nostyle}) {
382 print "<td width=\"14%\">\n";
383 print "<table border=\"0\">\n";
384 print "<caption>";
385 } else {
386 print "<td class=\"rem-small-calendar\">\n";
387 print "<table class=\"rem-sc-table\">\n";
388 print "<caption class=\"rem-sc-caption\">";
389 }
390 print "<a href=\"$url\">" if ($url);
391 print $month;
392 print "</a>" if ($url);
393 print "</caption>\n";
394
395 my $class;
396 if ($Options{nostyle}) {
397 print '<tr>';
398 $class = ' align="right"';
399 } else {
400 print '<tr class="rem-sc-hdr-row">';
401 $class = ' class="rem-sc-hdr"';
402 }
403 if (!$Mondayfirst) {
404 print "<th$class>" . substr($Daynames[0], 0, 1) . '</th>';
405 }
406 for (my $i=1; $i<7; $i++) {
407 print "<th$class>" . substr($Daynames[$i], 0, 1) . '</th>';
408 }
409 if ($Mondayfirst) {
410 print "<th$class>" . substr($Daynames[0], 0, 1) . '</th>';
411 }
412 print("</tr>\n");
413 my $col = 0;
414 for (; $col<$first_col; $col++) {
415 if ($col == 0) {
416 print("<tr>\n");
417 }
418 if ($Options{nostyle}) {
419 print("<td align=\"right\" width=\"14%\">&nbsp;</td>");
420 } else {
421 print("<td class=\"rem-sc-empty-cell\">&nbsp;</td>");
422 }
423 }
424
425 for (my $day=1; $day <= $monlen; $day++) {
426 if ($col == 0) {
427 print("<tr>\n");
428 }
429 $col++;
430 if ($Options{nostyle}) {
431 print("<td align=\"right\" width=\"14%\">$day</td>");
432 } else {
433 print("<td class=\"rem-sc-cell\">$day</td>");
434 }
435 if ($col == 7) {
436 print("</tr>\n");
437 $col = 0;
438 }
439 }
440 if ($col) {
441 while ($col < 7) {
442 if ($Options{nostyle}) {
443 print("<td align=\"right\" width=\"14%\">&nbsp;</td>");
444 } else {
445 print("<td class=\"rem-sc-empty-cell\">&nbsp;</td>");
446 }
447 $col++;
448 }
449 print("</tr>\n");
450 }
451 print("</table>\n");
452 print "</td>\n";
453}
454
455sub output_calendar
456{
457 # Which column is 1st of month in?
458 my $first_col = $Firstwkday;
459 if ($Mondayfirst) {
460 $first_col--;
461 if ($first_col < 0) {
462 $first_col = 6;
463 }
464 }
465
466 # Last column
467 my $last_col = ($first_col + $Numdays - 1) % 7;
468
469 # Figure out how many rows
470 my $number_of_rows = int(($first_col + $Numdays ) / 7 + 0.999);
471
472 # Add a row for small calendars if necessary
473 if ($first_col == 0 && $last_col == 6) {
474 $number_of_rows++;
475 }
476
477 # Start the table
478 my $class;
479 if ($Options{nostyle}) {
480 print '<table width="100%" border="1" cellspacing=\"0\"><caption>' .
481 $Month . ' ' . $Year . '</caption>' . "\n";
482 print '<tr>';
483 $class = ' width="14%"';
484 } else {
485 print '<table class="rem-cal"><caption class="rem-cal-caption">' .
486 $Month . ' ' . $Year . '</caption>' . "\n";
487 print '<tr class="rem-cal-hdr-row">';
488 $class = ' class="rem-cal-hdr"';
489 }
490 if (!$Mondayfirst) {
491 print "<th$class>" . $Daynames[0] . '</th>';
492 }
493 for (my $i=1; $i<7; $i++) {
494 print "<th$class>" . $Daynames[$i] . '</th>';
495 }
496 if ($Mondayfirst) {
497 print "<th$class>" . $Daynames[0] . '</th>';
498 }
499 print "</tr>\n";
500
501 # Start the calendar rows
502 my $col = 0;
503 if ($Options{nostyle}) {
504 print "<tr>\n";
505 } else {
506 print "<tr class=\"rem-cal-row rem-cal-row-$number_of_rows-rows\">\n";
507 }
508 if ($first_col > 0) {
509 small_calendar($Prevmon, $Prevlen, $Options{backurl},
510 ($Firstwkday - $Prevlen + 35) % 7);
511 $col++;
512 }
513
514 if ($last_col == 6 && $first_col > 0) {
515 small_calendar($Nextmon, $Nextlen, $Options{forwurl},
516 ($Firstwkday + $Numdays) % 7);
517 $col++;
518 }
519 if ($Options{nostyle}) {
520 $class = ' width="14%"';
521 } else {
522 $class = ' class="rem-empty rem-empty-$number_of_rows-rows"';
523 }
524 while ($col < $first_col) {
525 print("<td$class>&nbsp;</td>\n");
526 $col++;
527 }
528
529 for (my $day=1; $day<=$Numdays; $day++) {
530 draw_day_cell($day, $number_of_rows);
531 $col++;
532 if ($col == 7) {
533 $col = 0;
534 print "</tr>\n";
535 if ($day < $Numdays) {
536 if ($Options{nostyle}) {
537 print "<tr>\n";
538 } else {
539 print "<tr class=\"rem-cal-row rem-cal-row-$number_of_rows-rows\">\n";
540 }
541 }
542 }
543 }
544
545 if ($col) {
546 while ($col < 7) {
547 if ($col == 5) {
548 if ($first_col == 0) {
549 small_calendar($Prevmon, $Prevlen, $Options{backurl},
550 ($Firstwkday - $Prevlen + 35) % 7);
551 } else {
552 print("<td$class>&nbsp;</td>\n");
553 }
554 } elsif ($col == 6) {
555 small_calendar($Nextmon, $Nextlen, $Options{forwurl},
556 ($Firstwkday + $Numdays) % 7);
557 } else {
558 print("<td$class>&nbsp;</td>\n");
559 }
560 $col++;
561 }
562 print "</tr>\n";
563 }
564
565 # Add a row for small calendars if they were not yet done!
566 if ($first_col == 0 && $last_col == 6) {
567 if ($Options{nostyle}) {
568 print "<tr>\n";
569 } else {
570 print "<tr class=\"rem-cal-row rem-cal-row-$number_of_rows-rows\">\n";
571 }
572 small_calendar($Prevmon, $Prevlen, $Options{backurl},
573 ($Firstwkday - $Prevlen + 35) % 7);
574 for (my $i=0; $i<5; $i++) {
575 print("<td$class>&nbsp;</td>\n");
576 }
577 small_calendar($Nextmon, $Nextlen, $Options{forwurl},
578 ($Firstwkday + $Numdays) % 7);
579 print("</tr>\n");
580 }
581 # End the table
582 print "</table>\n";
583}
584
585sub draw_day_cell
586{
587 my($day, $number_of_rows) = @_;
588 my $shade = $shades->[$day];
589 my $week = '';
590 if (exists($weeks->{$day})) {
591 $week = ' ' . $weeks->{$day};
592 }
593 my $class;
594 if ($Options{nostyle}) {
595 $class = $classes->[$day] || '';
596 } else {
597 $class = $classes->[$day] || "rem-cell rem-cell-$number_of_rows-rows";
598 }
599 if ($shade) {
600 $shade = " style=\"background: $shade;\"";
601 } else {
602 $shade = "";
603 }
604 if ($class ne '') {
605 print "<td class=\"$class\"$shade>\n";
606 } else {
607 print "<td valign=\"top\" $shade>\n";
608 }
609 if ($moons->[$day]) {
610 my $phase = $moons->[$day]->{'phase'};
611 my $msg = $moons->[$day]->{'msg'};
612 $msg ||= '';
613 if ($msg ne '') {
614 $msg = '&nbsp;' . escape_html($msg);
615 }
616 my $img;
617 my $alt;
618 my $title;
619 if ($phase == 0) {
620 if ($Options{pngs}) {
621 $img = smoosh($Options{imgbase}, 'newmoon.png');
622 } else {
623 $img = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAGQAAABkABchkaRQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAC6SURBVDiNpdNNbsIwFATgL0HKolchHKBX6yFaBOEyoPYUabvOIVKJRaCL2JX5TRNGGvnJ8ozGz89cYoElPvET+BX2yivn/1Bggw5HHMKa1h2qcPZC/JEIhvh+brIZIY6sorhMYo9hh3KGFzzfa84NZNjDt9OG/ZcH1BlaPE1IAG0+URhxzNGESKPFaHJs9Q0Ziww7HnvGeXSrJhis0jiFfjwnj3I0WRv+TKtr4hQl3lDrZ6QN9Wt654hfWfGDmBpUwDkAAAAASUVORK5CYII=';
624 }
625 $title = 'New Moon';
626 $alt = 'new';
627 } elsif ($phase == 1) {
628 if ($Options{pngs}) {
629 $img = smoosh($Options{imgbase}, 'firstquarter.png');
630 } else {
631 $img = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAGQAAABkABchkaRQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAADfSURBVDiNndM9TsNAFATgzy5yjZSAE85JBygETgENUPF3iBCitHAFQkcIhZ/Ryn9gRlrZmp2Z3ef3TBOHOMULPrDBMrhpi/4HI5xjix2+4nmJRbx/Yh7ahvkpRPVV4QDXwT3UQy46zGkAZDgK/iytefvHgCrkJsqZUH6cLnNbABSxd5Jhhf1IbkMXv8Qux7hH1Ic1xvk/jBWy6gavumvtwx7ectwZXkKh7MA95XgObeOtpI2U4zl0kGbpxgiPvwQUcXLrKFchc82f6Ur0PK49azOnmOI4TBu84zm4SV38DeIVYkrYJyNbAAAAAElFTkSuQmCC';
632 }
633 $title = 'First Quarter';
634 $alt = '1st';
635 } elsif ($phase == 2) {
636 if ($Options{pngs}) {
637 $img = smoosh($Options{imgbase}, 'fullmoon.png');
638 } else {
639 $img = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAGQAAABkABchkaRQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAADlSURBVDiNrdNBUsJAEAXQlyw4hq4hwWPqTixET6ELkZ16CcAq7oFLqXExjaYgQVNlV/Viev7/6XT/4TjGuME7PiLXUatb8N8xwB12SFjiIXIZtU/MAntEfgvQE4YtHxhiHpjXQ5H7uLhEcaLLAleBvd0Xx9Ha/BdyU+Q5OBV5OKmj7a4YBWdSyNPe4aKHAHkzqcQZNj3JgnNexqE8heyIAulffuFF3kTfIVbBVeu/xoXGGsn2TLJJ/mqkafNiINszySYZdbS90GHlvcgsWktY4TFy7ecxTdvIzahxHQLbyFXUqkPwF2ASRNYgB/PXAAAAAElFTkSuQmCC';
640 }
641 $alt = 'full';
642 $title = 'Full Moon';
643 } else {
644 if ($Options{pngs}) {
645 $img = smoosh($Options{imgbase}, 'lastquarter.png');
646 } else {
647 $img = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAGQAAABkABchkaRQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAADmSURBVDiNndMxTsNAEIXhzy5yCyQ6FAgcE7oQheQWUAAl5BIkREoZrgB0GFNkHBl7bURGsryaee/3jHeXdpxjghU+8InXyI0S+n0MMEeBEi+4jfV3vAvMQtsyL0J0j2GtViaeRRMyj8IlsgY8BSijE2Kur/hy09wHKMJrEolhwtwHKDHOsI4OLnoAXfl1jiNsOkR9keE4P8D4q4scbzg5xIxtjie709f1E7siC+9+Gx/8fxvPKtEsklcJSBdgWhcN8ByFR5z+AWgd5QpyE+OUWOJO+zJNU+Z6jHAdgHe7K73CuD5zFT9nCmRDIssCaAAAAABJRU5ErkJggg==';
648 }
649 $alt = 'last';
650 $title = 'Last Quarter';
651 }
652 if ($Options{nostyle}) {
653 print("<div style=\"float: left\"><img border=\"0\" width=\"16\" height=\"16\" alt=\"$alt\" title=\"$title\" src=\"$img\">$msg</div>");
654 } else {
655 print("<div class=\"rem-moon\"><img width=\"16\" height=\"16\" alt=\"$alt\" title=\"$title\" src=\"$img\">$msg</div>");
656 }
657 }
658
659 if ($Options{nostyle}) {
660 print "<div style=\"float: right\">$day$week</div>\n";
661 print "<p>&nbsp;</p>\n";
662 } else {
663 print "<div class=\"rem-daynumber\">$day$week</div>\n";
664 }
665 if ($days->[$day]) {
666 print(join("\n", @{$days->[$day]}));
667 }
668
669 print "</td>\n";
670}
671
672sub escape_html
673{
674 my($in) = @_;
675 $in =~ s/\&/\&amp;/g;
676 $in =~ s/\</\&lt;/g;
677 $in =~ s/\>/\&gt;/g;
678 return $in;
679}
680
681parse_options();
682if ($Options{help}) {
683 usage(0);
684 exit(0);
685} elsif ($Options{man}) {
686 system("perldoc $0");
687 exit(0);
688} elsif ($Options{version}) {
689 print "rem2html version $rem2html_version.\n";
690 exit(0);
691}
692
693if (-t STDIN) { ## no critic
694 print STDERR "$TIDY_PROGNAME: Input should not come from a terminal.\n\n";
695 usage(1);
696}
697
698my $found_something = 0;
699while(1) {
700 last if (!parse_input());
701 start_output() unless $found_something;
702 $found_something = 1;
703 output_calendar();
704}
705if ($found_something) {
706 end_output();
707 exit(0);
708} else {
709 print STDERR "$TIDY_PROGNAME: Could not find any calendar data on STDIN.\n";
710 exit(1);
711}
712
713sub default_stylesheet
714{
715 return <<'EOF';
716table.rem-cal {
717 font-family: helvetica, arial, sans-serif;
718 font-size: 12pt;
719}
720
721table.rem-sc-table {
722 font-family: helvetica, arial, sans-serif;
723 font-size: 10pt;
724 width: 95%;
725 float: left;
726}
727
728caption.rem-cal-caption {
729 font-size: 14pt;
730 font-weight: bold;
731}
732
733th.rem-cal-hdr {
734 width: 14%;
735 border-style: solid;
736 border-width: 1px;
737 vertical-align: top;
738}
739td.rem-empty, td.rem-cell, td.rem-small-calendar {
740 width: 14%;
741 height: 7em;
742 border-style: solid;
743 border-width: 1px;
744 vertical-align: top;
745}
746td.rem-today {
747 width: 14%;
748 height: 7em;
749 border-style: solid;
750 border-width: 2px;
751 border-color: #EE3333;
752 vertical-align: top;
753}
754
755table.rem-cal {
756 width: 100%;
757 border-collapse: collapse;
758}
759
760div.rem-daynumber {
761 float: right;
762 text-align: right;
763 vertical-align: top;
764 font-size: 14pt;
765}
766
767p.rem-entry {
768 clear: both;
769}
770
771div.rem-moon {
772 float: left;
773 text-align: left;
774 vertical-align: top;
775}
776
777th.rem-sc-hdr {
778 text-align: right;
779}
780
781td.rem-sc-empty-cell, td.rem-sc-cell {
782 text-align: right;
783 width: 14%;
784}
785
786caption.rem-sc-caption {
787 font-size: 12pt;
788}
789EOF
790}