|
1 /* Copyright 2025 Mike Becker. All rights reserved. |
|
2 * |
|
3 * Redistribution and use in source and binary forms, with or without |
|
4 * modification, are permitted provided that the following conditions are met: |
|
5 * |
|
6 * 1. Redistributions of source code must retain the above copyright |
|
7 * notice, this list of conditions and the following disclaimer. |
|
8 * |
|
9 * 2. Redistributions in binary form must reproduce the above copyright |
|
10 * notice, this list of conditions and the following disclaimer in the |
|
11 * documentation and/or other materials provided with the distribution. |
|
12 * |
|
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
|
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
|
16 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE |
|
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
|
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
|
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER |
|
20 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
|
21 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
22 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
23 */ |
|
24 |
|
25 #include "html.h" |
|
26 |
|
27 #include <cstdio> |
|
28 |
|
29 namespace html { |
|
30 static unsigned indentation; |
|
31 static const char *tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; |
|
32 static void indent(int change = 0) { |
|
33 indentation += change; |
|
34 fwrite(tabs, 1, indentation, stdout); |
|
35 } |
|
36 |
|
37 static std::string encode(const std::string &data) { |
|
38 std::string buffer; |
|
39 buffer.reserve(data.size()+16); |
|
40 for (const char &pos: data) { |
|
41 switch (pos) { |
|
42 case '&': |
|
43 buffer.append("&"); |
|
44 break; |
|
45 case '\"': |
|
46 buffer.append("""); |
|
47 break; |
|
48 case '\'': |
|
49 buffer.append("'"); |
|
50 break; |
|
51 case '<': |
|
52 buffer.append("<"); |
|
53 break; |
|
54 case '>': |
|
55 buffer.append(">"); |
|
56 break; |
|
57 default: |
|
58 buffer.append(&pos, 1); |
|
59 break; |
|
60 } |
|
61 } |
|
62 return buffer; |
|
63 } |
|
64 } |
|
65 |
|
66 void html::open() { |
|
67 puts( |
|
68 R"(<html> |
|
69 <head> |
|
70 <style> |
|
71 table.heatmap { |
|
72 table-layout: fixed; |
|
73 border-collapse: collapse; |
|
74 font-family: sans-serif; |
|
75 } |
|
76 table.heatmap td, table.heatmap th { |
|
77 text-align: center; |
|
78 border: solid 1px lightgray; |
|
79 height: 1.5em; |
|
80 } |
|
81 table.heatmap td { |
|
82 border: solid 1px lightgray; |
|
83 width: 1.5em; |
|
84 height: 1.5em; |
|
85 } |
|
86 table.heatmap td.out-of-range { |
|
87 background-color: gray; |
|
88 } |
|
89 </style> |
|
90 </head> |
|
91 <body>)"); |
|
92 indentation = 2; |
|
93 } |
|
94 |
|
95 void html::close() { |
|
96 puts("\t</body>\n</html>"); |
|
97 } |
|
98 |
|
99 void html::h1(const std::string& heading) { |
|
100 indent(); |
|
101 printf("<h1>%s</h1>\n", encode(heading).c_str()); |
|
102 } |
|
103 |
|
104 void html::h2(const std::string& heading) { |
|
105 indent(); |
|
106 printf("<h2>%s</h2>\n", encode(heading).c_str()); |
|
107 } |
|
108 |
|
109 void html::table_begin() { |
|
110 indent(); |
|
111 puts("<table class=\"heatmap\">"); |
|
112 indent(1); |
|
113 puts("<tr>"); |
|
114 indent(1); |
|
115 puts("<th></th>"); |
|
116 static constexpr const char* months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; |
|
117 static constexpr int colspans[] = {5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 5}; |
|
118 for (unsigned i = 0 ; i < 12 ; i++) { |
|
119 indent(); |
|
120 printf("<th scope=\"col\" colspan=\"%d\">%s</th>\n", colspans[i], months[i]); |
|
121 } |
|
122 indent(-1); |
|
123 puts("</tr>"); |
|
124 } |
|
125 |
|
126 void html::table_end() { |
|
127 indentation--; |
|
128 indent(); |
|
129 puts("</table>"); |
|
130 } |
|
131 |
|
132 void html::row_begin(unsigned int row) { |
|
133 static constexpr const char* weekdays[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; |
|
134 indent(); |
|
135 puts("<tr>"); |
|
136 indent(1); |
|
137 printf("<th scope=\"row\">%s</th>\n", weekdays[row]); |
|
138 } |
|
139 |
|
140 void html::row_end() { |
|
141 indent(-1); |
|
142 puts("</tr>"); |
|
143 } |
|
144 |
|
145 void html::cell_out_of_range() { |
|
146 indent(); |
|
147 puts("<td class=\"out-of-range\"></td>"); |
|
148 } |
|
149 |
|
150 void html::cell(unsigned commits) { |
|
151 indent(); |
|
152 // TODO: use nice coloring and tooltips instead of printing the commits |
|
153 printf("<td>%u</td>\n", commits); |
|
154 } |