# Markup::Calendar

Raku package with Markup (HTML, Markdown) calendar functions for displaying monthly, yearly, and custom calendars.

### Motivation

The package 
["Text::Calendar"](https://raku.land/zef:antononcube/Text::Calendar), [AAp1], 
provides the core functions for making the calendars in this package.
The packages 
["Data::Translators"](https://raku.land/zef:antononcube/Data::Translators), [AAp2], and 
["Pretty::Table"](https://raku.land/cpan:ANTONOV/Pretty::Table), [ULp1],
provide additional formatting functionalities.

I want to keep "Text::Calendar" lightweight, without any dependencies. Hence I made this separate 
package, "Markup::Calendar", that has more involved dependencies and use-cases.

An "involved use case" is calendar in which some of the days have tooltips and hyperlinks. 

-----

## Installation

From [Zef ecosystem](https://raku.land):

```
zef install Markup::Calendar
```

From GitHub:

```
zef install https://github.com/antononcube/Raku-Markup-Calendar.git
```

-----

## Examples

### Basic HTML calendar

```raku, results=asis
use Markup::Calendar;
use Text::Calendar;

calendar():html
```
<style>td { vertical-align: top;a}</style><table border="1"><tbody><tr><td><h3>June 2026</h3><table border="1"><thead><tr><th>Mo</th><th>Tu</th><th>We</th><th>Th</th><th>Fr</th><th>Sa</th><th>Su</th></tr></thead><tbody><tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td></tr><tr><td>8</td><td>9</td><td>10</td><td>11</td><td>12</td><td>13</td><td>14</td></tr><tr><td>15</td><td>16</td><td>17</td><td>18</td><td>19</td><td>20</td><td>21</td></tr><tr><td>22</td><td>23</td><td>24</td><td>25</td><td>26</td><td>27</td><td>28</td></tr><tr><td>29</td><td>30</td><td>  </td><td>  </td><td>  </td><td>  </td><td>  </td></tr></tbody></table></td><td><h3>July 2026</h3><table border="1"><thead><tr><th>Mo</th><th>Tu</th><th>We</th><th>Th</th><th>Fr</th><th>Sa</th><th>Su</th></tr></thead><tbody><tr><td>  </td><td>  </td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td></tr><tr><td>6</td><td>7</td><td>8</td><td>9</td><td>10</td><td>11</td><td>12</td></tr><tr><td>13</td><td>14</td><td>15</td><td>16</td><td>17</td><td>18</td><td>19</td></tr><tr><td>20</td><td>21</td><td>22</td><td>23</td><td>24</td><td>25</td><td>26</td></tr><tr><td>27</td><td>28</td><td>29</td><td>30</td><td>31</td><td>  </td><td>  </td></tr></tbody></table></td><td><h3>August 2026</h3><table border="1"><thead><tr><th>Mo</th><th>Tu</th><th>We</th><th>Th</th><th>Fr</th><th>Sa</th><th>Su</th></tr></thead><tbody><tr><td>  </td><td>  </td><td>  </td><td>  </td><td>  </td><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>10</td><td>11</td><td>12</td><td>13</td><td>14</td><td>15</td><td>16</td></tr><tr><td>17</td><td>18</td><td>19</td><td>20</td><td>21</td><td>22</td><td>23</td></tr><tr><td>24</td><td>25</td><td>26</td><td>27</td><td>28</td><td>29</td><td>30</td></tr><tr><td>31</td><td>  </td><td>  </td><td>  </td><td>  </td><td>  </td><td>  </td></tr></tbody></table></td></tr></tbody></table>


### HTML yearly calendar with highlights 

Here is an HTML calendar that weekend days are highlighted and with larger font:

```raku, results=asis
calendar-year( 
    per-row => 4, 
    highlight => (Date.new(2024,1,1)...Date.new(2024,12,31)).grep({ $_.day-of-week ≥ 6 }),
    highlight-style => 'color:orange; font-size:14pt'
):html
```
<h2>2026</h2><style>td { vertical-align: top;a}</style><table border="1"><tbody><tr><td><h3>January </h3><table border="1"><thead><tr><th>Mo</th><th>Tu</th><th>We</th><th>Th</th><th>Fr</th><th>Sa</th><th>Su</th></tr></thead><tbody><tr><td>  </td><td>  </td><td>  </td><td>1</td><td>2</td><td>3</td><td>4</td></tr><tr><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td><td>10</td><td>11</td></tr><tr><td>12</td><td>13</td><td>14</td><td>15</td><td>16</td><td>17</td><td>18</td></tr><tr><td>19</td><td>20</td><td>21</td><td>22</td><td>23</td><td>24</td><td>25</td></tr><tr><td>26</td><td>27</td><td>28</td><td>29</td><td>30</td><td>31</td><td>  </td></tr></tbody></table></td><td><h3>February </h3><table border="1"><thead><tr><th>Mo</th><th>Tu</th><th>We</th><th>Th</th><th>Fr</th><th>Sa</th><th>Su</th></tr></thead><tbody><tr><td>  </td><td>  </td><td>  </td><td>  </td><td>  </td><td>  </td><td>1</td></tr><tr><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td></tr><tr><td>9</td><td>10</td><td>11</td><td>12</td><td>13</td><td>14</td><td>15</td></tr><tr><td>16</td><td>17</td><td>18</td><td>19</td><td>20</td><td>21</td><td>22</td></tr><tr><td>23</td><td>24</td><td>25</td><td>26</td><td>27</td><td>28</td><td>  </td></tr></tbody></table></td><td><h3>March </h3><table border="1"><thead><tr><th>Mo</th><th>Tu</th><th>We</th><th>Th</th><th>Fr</th><th>Sa</th><th>Su</th></tr></thead><tbody><tr><td>  </td><td>  </td><td>  </td><td>  </td><td>  </td><td>  </td><td>1</td></tr><tr><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td></tr><tr><td>9</td><td>10</td><td>11</td><td>12</td><td>13</td><td>14</td><td>15</td></tr><tr><td>16</td><td>17</td><td>18</td><td>19</td><td>20</td><td>21</td><td>22</td></tr><tr><td>23</td><td>24</td><td>25</td><td>26</td><td>27</td><td>28</td><td>29</td></tr><tr><td>30</td><td>31</td><td>  </td><td>  </td><td>  </td><td>  </td><td>  </td></tr></tbody></table></td><td><h3>April </h3><table border="1"><thead><tr><th>Mo</th><th>Tu</th><th>We</th><th>Th</th><th>Fr</th><th>Sa</th><th>Su</th></tr></thead><tbody><tr><td>  </td><td>  </td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td></tr><tr><td>6</td><td>7</td><td>8</td><td>9</td><td>10</td><td>11</td><td>12</td></tr><tr><td>13</td><td>14</td><td>15</td><td>16</td><td>17</td><td>18</td><td>19</td></tr><tr><td>20</td><td>21</td><td>22</td><td>23</td><td>24</td><td>25</td><td>26</td></tr><tr><td>27</td><td>28</td><td>29</td><td>30</td><td>  </td><td>  </td><td>  </td></tr></tbody></table></td></tr><tr><td><h3>May </h3><table border="1"><thead><tr><th>Mo</th><th>Tu</th><th>We</th><th>Th</th><th>Fr</th><th>Sa</th><th>Su</th></tr></thead><tbody><tr><td>  </td><td>  </td><td>  </td><td>  </td><td>1</td><td>2</td><td>3</td></tr><tr><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td><td>10</td></tr><tr><td>11</td><td>12</td><td>13</td><td>14</td><td>15</td><td>16</td><td>17</td></tr><tr><td>18</td><td>19</td><td>20</td><td>21</td><td>22</td><td>23</td><td>24</td></tr><tr><td>25</td><td>26</td><td>27</td><td>28</td><td>29</td><td>30</td><td>31</td></tr></tbody></table></td><td><h3>June </h3><table border="1"><thead><tr><th>Mo</th><th>Tu</th><th>We</th><th>Th</th><th>Fr</th><th>Sa</th><th>Su</th></tr></thead><tbody><tr><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td></tr><tr><td>8</td><td>9</td><td>10</td><td>11</td><td>12</td><td>13</td><td>14</td></tr><tr><td>15</td><td>16</td><td>17</td><td>18</td><td>19</td><td>20</td><td>21</td></tr><tr><td>22</td><td>23</td><td>24</td><td>25</td><td>26</td><td>27</td><td>28</td></tr><tr><td>29</td><td>30</td><td>  </td><td>  </td><td>  </td><td>  </td><td>  </td></tr></tbody></table></td><td><h3>July </h3><table border="1"><thead><tr><th>Mo</th><th>Tu</th><th>We</th><th>Th</th><th>Fr</th><th>Sa</th><th>Su</th></tr></thead><tbody><tr><td>  </td><td>  </td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td></tr><tr><td>6</td><td>7</td><td>8</td><td>9</td><td>10</td><td>11</td><td>12</td></tr><tr><td>13</td><td>14</td><td>15</td><td>16</td><td>17</td><td>18</td><td>19</td></tr><tr><td>20</td><td>21</td><td>22</td><td>23</td><td>24</td><td>25</td><td>26</td></tr><tr><td>27</td><td>28</td><td>29</td><td>30</td><td>31</td><td>  </td><td>  </td></tr></tbody></table></td><td><h3>August </h3><table border="1"><thead><tr><th>Mo</th><th>Tu</th><th>We</th><th>Th</th><th>Fr</th><th>Sa</th><th>Su</th></tr></thead><tbody><tr><td>  </td><td>  </td><td>  </td><td>  </td><td>  </td><td>1</td><td>2</td></tr><tr><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td></tr><tr><td>10</td><td>11</td><td>12</td><td>13</td><td>14</td><td>15</td><td>16</td></tr><tr><td>17</td><td>18</td><td>19</td><td>20</td><td>21</td><td>22</td><td>23</td></tr><tr><td>24</td><td>25</td><td>26</td><td>27</td><td>28</td><td>29</td><td>30</td></tr><tr><td>31</td><td>  </td><td>  </td><td>  </td><td>  </td><td>  </td><td>  </td></tr></tbody></table></td></tr><tr><td><h3>September </h3><table border="1"><thead><tr><th>Mo</th><th>Tu</th><th>We</th><th>Th</th><th>Fr</th><th>Sa</th><th>Su</th></tr></thead><tbody><tr><td>  </td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td></tr><tr><td>7</td><td>8</td><td>9</td><td>10</td><td>11</td><td>12</td><td>13</td></tr><tr><td>14</td><td>15</td><td>16</td><td>17</td><td>18</td><td>19</td><td>20</td></tr><tr><td>21</td><td>22</td><td>23</td><td>24</td><td>25</td><td>26</td><td>27</td></tr><tr><td>28</td><td>29</td><td>30</td><td>  </td><td>  </td><td>  </td><td>  </td></tr></tbody></table></td><td><h3>October </h3><table border="1"><thead><tr><th>Mo</th><th>Tu</th><th>We</th><th>Th</th><th>Fr</th><th>Sa</th><th>Su</th></tr></thead><tbody><tr><td>  </td><td>  </td><td>  </td><td>1</td><td>2</td><td>3</td><td>4</td></tr><tr><td>5</td><td>6</td><td>7</td><td>8</td><td>9</td><td>10</td><td>11</td></tr><tr><td>12</td><td>13</td><td>14</td><td>15</td><td>16</td><td>17</td><td>18</td></tr><tr><td>19</td><td>20</td><td>21</td><td>22</td><td>23</td><td>24</td><td>25</td></tr><tr><td>26</td><td>27</td><td>28</td><td>29</td><td>30</td><td>31</td><td>  </td></tr></tbody></table></td><td><h3>November </h3><table border="1"><thead><tr><th>Mo</th><th>Tu</th><th>We</th><th>Th</th><th>Fr</th><th>Sa</th><th>Su</th></tr></thead><tbody><tr><td>  </td><td>  </td><td>  </td><td>  </td><td>  </td><td>  </td><td>1</td></tr><tr><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td><td>7</td><td>8</td></tr><tr><td>9</td><td>10</td><td>11</td><td>12</td><td>13</td><td>14</td><td>15</td></tr><tr><td>16</td><td>17</td><td>18</td><td>19</td><td>20</td><td>21</td><td>22</td></tr><tr><td>23</td><td>24</td><td>25</td><td>26</td><td>27</td><td>28</td><td>29</td></tr><tr><td>30</td><td>  </td><td>  </td><td>  </td><td>  </td><td>  </td><td>  </td></tr></tbody></table></td><td><h3>December </h3><table border="1"><thead><tr><th>Mo</th><th>Tu</th><th>We</th><th>Th</th><th>Fr</th><th>Sa</th><th>Su</th></tr></thead><tbody><tr><td>  </td><td>1</td><td>2</td><td>3</td><td>4</td><td>5</td><td>6</td></tr><tr><td>7</td><td>8</td><td>9</td><td>10</td><td>11</td><td>12</td><td>13</td></tr><tr><td>14</td><td>15</td><td>16</td><td>17</td><td>18</td><td>19</td><td>20</td></tr><tr><td>21</td><td>22</td><td>23</td><td>24</td><td>25</td><td>26</td><td>27</td></tr><tr><td>28</td><td>29</td><td>30</td><td>31</td><td>  </td><td>  </td><td>  </td></tr></tbody></table></td></tr></tbody></table>


### Standalone calendar file

Here we make a standalone calendar file:

```raku
spurt('example.html', calendar-year(year => 2024, highlight => [3=>3, 5=>24, 9=>9], highlight-style=>'color:red', format=>'html'))
```
```
# True
```

------

## CLI

The package provides the Command Line Interface (CLI) script `mcal`. Here is its usage message:

```shell
mcal --help
```
```
# Usage:
#   mcal [-y|--year=<Str>] [-m|--months=<Str>] [-h|--highlight=<Str>] [-t|--tooltip=<Str>] [-l|--hyperlink=<Str>] [-s|--highlight-style=<Str>] [--per-row[=UInt]] [--doc|--document] -- Displays a calendar in HTML format.
#   
#     -y|--year=<Str>               Year spec; if an empty string only the months spec is used. [default: '']
#     -m|--months=<Str>             Months spec, comma separated list of integers or month names. [default: 'Whatever']
#     -h|--highlight=<Str>          Highlight spec that is a comma separated list of days. [default: '']
#     -t|--tooltip=<Str>            Tooltip spec. [default: '']
#     -l|--hyperlink=<Str>          Hyperlink spec (per calendar day.) [default: '']
#     -s|--highlight-style=<Str>    Highlight style (HTML style.) [default: 'color:orange; font-…']
#     --per-row[=UInt]              Number of months per row. [default: 3]
#     --doc|--document              Should a complete HTML document be made or not? [default: False]
```

------

## TODO

- [ ] TODO Features
  - [X] DONE HTML
    - [X] DONE Full HTML calendar 
    - [X] DONE Partial HTML calendar (e.g. equivalent of `cal -3`)
    - [X] DONE Highlighted days
    - [X] DONE Tooltips for days
    - [X] DONE Hyperlinks for days
  - [ ] TODO Markdown
    - [ ] TODO Full Markdown calendar
    - [ ] TODO Partial Markdown calendar
    - [ ] TODO Highlighted days
    - [ ] TODO Tooltips for days
    - [ ] TODO Hyperlinks for days
  - [X] CLI
- [ ] Unit tests
  - [X] DONE Basic usage
  - [X] DONE Equivalence using different signatures
  - [ ] TODO Correctness
- [ ] Documentation
  - [X] DONE Basic README
  - [ ] TODO Diagrams
  - [ ] TODO Comparisons

------

## References 

[AAp1] Anton Antonov,
[Text::Calendar Raku package](https://github.com/antononcube/Raku-Text-Calendar),
(2024),
[GitHub/antononcube](https://github.com/antononcube).

[AAp2] Anton Antonov,
[Data::Translators Raku package](https://github.com/antononcube/Raku-Data-Translators),
(2023),
[GitHub/antononcube](https://github.com/antononcube).

[LUp1] Luis F. Uceta,
[Pretty::Table Raku package](https://gitlab.com/uzluisf/raku-pretty-table/),
(2020),
[GitLab/uzluisf](https://gitlab.com/uzluisf/).


