diff options
-rwxr-xr-x | .local/bin/rem2html | 790 |
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 | |||
4 | use strict; | ||
5 | use warnings; | ||
6 | |||
7 | use Getopt::Long; | ||
8 | use JSON::MaybeXS; | ||
9 | |||
10 | my %Options; | ||
11 | |||
12 | my $rem2html_version = '2.1'; | ||
13 | |||
14 | my($days, $shades, $moons, $classes, $Month, $Year, $Numdays, $Firstwkday, $Mondayfirst, $weeks, | ||
15 | @Daynames, $Nextmon, $Nextlen, $Prevmon, $Prevlen); | ||
16 | |||
17 | my $TIDY_PROGNAME = $0; | ||
18 | $TIDY_PROGNAME =~ s|^.*/||; | ||
19 | |||
20 | # rem2html -- convert the output of "remind -pp" to HTML | ||
21 | |||
22 | =head1 NAME | ||
23 | |||
24 | rem2html - Convert the output of "remind -pp" to HTML | ||
25 | |||
26 | =head1 SYNOPSIS | ||
27 | |||
28 | remind -pp ... | rem2html [options] | ||
29 | |||
30 | You can also use the old interchange format as below, but the -pp | ||
31 | version is preferred. | ||
32 | |||
33 | remind -p ... | rem2html [options] | ||
34 | |||
35 | =head1 OPTIONS | ||
36 | |||
37 | =over 4 | ||
38 | |||
39 | =item --help, -h | ||
40 | |||
41 | Print usage information | ||
42 | |||
43 | =item --version | ||
44 | |||
45 | Print version | ||
46 | |||
47 | =item --backurl I<url> | ||
48 | |||
49 | When producing the small calendar for the previous month, make the | ||
50 | month name a link to I<url>. | ||
51 | |||
52 | =item --forwurl I<url> | ||
53 | |||
54 | When producing the small calendar for the next month, make the | ||
55 | month name a link to I<url>. | ||
56 | |||
57 | =item --imgbase I<url> | ||
58 | |||
59 | When creating URLs for the stylesheet or external images, use I<url> | ||
60 | as the base URL. | ||
61 | |||
62 | =item --pngs | ||
63 | |||
64 | Normally, rem2html uses inline "data:" URLs for the moon phase images, | ||
65 | yielding a standalone HTML file. The C<--pngs> option makes it use | ||
66 | external images named firstquarter.png, fullmoon.png, lastquarter.png | ||
67 | and newmoon.png, which are expected to live in C<--imgbase>. | ||
68 | |||
69 | =item --stylesheet I<url.css> | ||
70 | |||
71 | Use I<url.css> as the stylesheet. If this option is used, | ||
72 | I<url.css> is interpreted relative to B<imgbase> I<unless> it starts | ||
73 | with a "/". | ||
74 | |||
75 | =item --nostyle | ||
76 | |||
77 | Produce basic HTML that does not use a CSS stylesheet. | ||
78 | |||
79 | =item --tableonly | ||
80 | |||
81 | Output results as a E<lt>tableE<gt> ... E<lt>/tableE<gt> sequence only | ||
82 | without any E<lt>htmlE<gt> or E<lt>bodyE<gt> tags. | ||
83 | |||
84 | =item --title I<title> | ||
85 | |||
86 | Use I<title> as the content between E<lt>titleE<gt> and E<lt>/titleE<gt> | ||
87 | tags. | ||
88 | |||
89 | |||
90 | =item --prologue I<html_text> | ||
91 | |||
92 | Insert I<html_text> right after the E<lt>bodyE<gt> tag. | ||
93 | |||
94 | =item --epilogue I<html_text> | ||
95 | |||
96 | Insert I<html_text> right before the E<lt>/bodyE<gt> tag. | ||
97 | |||
98 | =back | ||
99 | |||
100 | =head1 SPECIALS SUPPORTED | ||
101 | |||
102 | The rem2html back-end supports the following SPECIAL reminders: | ||
103 | |||
104 | =over | ||
105 | |||
106 | =item HTML | ||
107 | |||
108 | Add an HTML reminder to the calendar. All HTML tags are available. | ||
109 | |||
110 | =item HTMLCLASS | ||
111 | |||
112 | Add 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 | |||
117 | The standard SPECIALs supported by all back-ends | ||
118 | |||
119 | =back | ||
120 | |||
121 | =head1 HIGHLIGHTING TODAY | ||
122 | |||
123 | Older versions of rem2html used to highlight today's date with a red outline. | ||
124 | The current version does not do that by default. If you wish to highlight | ||
125 | today's date, add the following reminder to your reminders file: | ||
126 | |||
127 | REM [realtoday()] SPECIAL HTMLCLASS rem-today | ||
128 | |||
129 | =head1 AUTHOR | ||
130 | |||
131 | rem2html was written by Dianne Skoll with much inspiration from an | ||
132 | earlier version by Don Schwarz. | ||
133 | |||
134 | =head1 HOME PAGE | ||
135 | |||
136 | L<https://dianne.skoll.ca/projects/remind/> | ||
137 | |||
138 | =head1 SEE ALSO | ||
139 | |||
140 | B<remind>, B<rem2ps>, B<rem2pdf>, B<tkremind> | ||
141 | =cut | ||
142 | |||
143 | sub 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 | |||
152 | Usage: remind -pp ... | rem2html [options] | ||
153 | |||
154 | Options: | ||
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 | ||
172 | EOM | ||
173 | exit($exit_status); | ||
174 | } | ||
175 | |||
176 | sub 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 | |||
195 | sub 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 | |||
221 | sub 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 | |||
245 | sub 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 | |||
254 | sub 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 | |||
371 | sub 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%\"> </td>"); | ||
420 | } else { | ||
421 | print("<td class=\"rem-sc-empty-cell\"> </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%\"> </td>"); | ||
444 | } else { | ||
445 | print("<td class=\"rem-sc-empty-cell\"> </td>"); | ||
446 | } | ||
447 | $col++; | ||
448 | } | ||
449 | print("</tr>\n"); | ||
450 | } | ||
451 | print("</table>\n"); | ||
452 | print "</td>\n"; | ||
453 | } | ||
454 | |||
455 | sub 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> </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> </td>\n"); | ||
553 | } | ||
554 | } elsif ($col == 6) { | ||
555 | small_calendar($Nextmon, $Nextlen, $Options{forwurl}, | ||
556 | ($Firstwkday + $Numdays) % 7); | ||
557 | } else { | ||
558 | print("<td$class> </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> </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 | |||
585 | sub 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 = ' ' . 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> </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 | |||
672 | sub escape_html | ||
673 | { | ||
674 | my($in) = @_; | ||
675 | $in =~ s/\&/\&/g; | ||
676 | $in =~ s/\</\</g; | ||
677 | $in =~ s/\>/\>/g; | ||
678 | return $in; | ||
679 | } | ||
680 | |||
681 | parse_options(); | ||
682 | if ($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 | |||
693 | if (-t STDIN) { ## no critic | ||
694 | print STDERR "$TIDY_PROGNAME: Input should not come from a terminal.\n\n"; | ||
695 | usage(1); | ||
696 | } | ||
697 | |||
698 | my $found_something = 0; | ||
699 | while(1) { | ||
700 | last if (!parse_input()); | ||
701 | start_output() unless $found_something; | ||
702 | $found_something = 1; | ||
703 | output_calendar(); | ||
704 | } | ||
705 | if ($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 | |||
713 | sub default_stylesheet | ||
714 | { | ||
715 | return <<'EOF'; | ||
716 | table.rem-cal { | ||
717 | font-family: helvetica, arial, sans-serif; | ||
718 | font-size: 12pt; | ||
719 | } | ||
720 | |||
721 | table.rem-sc-table { | ||
722 | font-family: helvetica, arial, sans-serif; | ||
723 | font-size: 10pt; | ||
724 | width: 95%; | ||
725 | float: left; | ||
726 | } | ||
727 | |||
728 | caption.rem-cal-caption { | ||
729 | font-size: 14pt; | ||
730 | font-weight: bold; | ||
731 | } | ||
732 | |||
733 | th.rem-cal-hdr { | ||
734 | width: 14%; | ||
735 | border-style: solid; | ||
736 | border-width: 1px; | ||
737 | vertical-align: top; | ||
738 | } | ||
739 | td.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 | } | ||
746 | td.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 | |||
755 | table.rem-cal { | ||
756 | width: 100%; | ||
757 | border-collapse: collapse; | ||
758 | } | ||
759 | |||
760 | div.rem-daynumber { | ||
761 | float: right; | ||
762 | text-align: right; | ||
763 | vertical-align: top; | ||
764 | font-size: 14pt; | ||
765 | } | ||
766 | |||
767 | p.rem-entry { | ||
768 | clear: both; | ||
769 | } | ||
770 | |||
771 | div.rem-moon { | ||
772 | float: left; | ||
773 | text-align: left; | ||
774 | vertical-align: top; | ||
775 | } | ||
776 | |||
777 | th.rem-sc-hdr { | ||
778 | text-align: right; | ||
779 | } | ||
780 | |||
781 | td.rem-sc-empty-cell, td.rem-sc-cell { | ||
782 | text-align: right; | ||
783 | width: 14%; | ||
784 | } | ||
785 | |||
786 | caption.rem-sc-caption { | ||
787 | font-size: 12pt; | ||
788 | } | ||
789 | EOF | ||
790 | } | ||