src/html.cpp

changeset 5
60c2588b4455
child 6
1040ba37d4c9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/html.cpp	Fri Jan 31 22:11:04 2025 +0100
@@ -0,0 +1,154 @@
+/* Copyright 2025 Mike Becker. All rights reserved.
+*
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "html.h"
+
+#include <cstdio>
+
+namespace html {
+    static unsigned indentation;
+    static const char *tabs = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
+    static void indent(int change = 0) {
+        indentation += change;
+        fwrite(tabs, 1, indentation, stdout);
+    }
+
+    static std::string encode(const std::string &data) {
+        std::string buffer;
+        buffer.reserve(data.size()+16);
+        for (const char &pos: data) {
+            switch (pos) {
+                case '&':
+                    buffer.append("&amp;");
+                    break;
+                case '\"':
+                    buffer.append("&quot;");
+                    break;
+                case '\'':
+                    buffer.append("&apos;");
+                    break;
+                case '<':
+                    buffer.append("&lt;");
+                    break;
+                case '>':
+                    buffer.append("&gt;");
+                    break;
+                default:
+                    buffer.append(&pos, 1);
+                    break;
+            }
+        }
+        return buffer;
+    }
+}
+
+void html::open() {
+    puts(
+R"(<html>
+    <head>
+        <style>
+            table.heatmap {
+                table-layout: fixed;
+                border-collapse: collapse;
+                font-family: sans-serif;
+            }
+            table.heatmap td, table.heatmap th {
+                text-align: center;
+                border: solid 1px lightgray;
+                height: 1.5em;
+            }
+            table.heatmap td {
+                border: solid 1px lightgray;
+                width: 1.5em;
+                height: 1.5em;
+            }
+            table.heatmap td.out-of-range {
+                background-color: gray;
+            }
+        </style>
+    </head>
+    <body>)");
+    indentation = 2;
+}
+
+void html::close() {
+    puts("\t</body>\n</html>");
+}
+
+void html::h1(const std::string& heading) {
+    indent();
+    printf("<h1>%s</h1>\n", encode(heading).c_str());
+}
+
+void html::h2(const std::string& heading) {
+    indent();
+    printf("<h2>%s</h2>\n", encode(heading).c_str());
+}
+
+void html::table_begin() {
+    indent();
+    puts("<table class=\"heatmap\">");
+    indent(1);
+    puts("<tr>");
+    indent(1);
+    puts("<th></th>");
+    static constexpr const char* months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
+    static constexpr int colspans[] = {5, 4, 4, 5, 4, 4, 5, 4, 4, 5, 4, 5};
+    for (unsigned i = 0 ; i < 12 ; i++) {
+        indent();
+        printf("<th scope=\"col\" colspan=\"%d\">%s</th>\n", colspans[i], months[i]);
+    }
+    indent(-1);
+    puts("</tr>");
+}
+
+void html::table_end() {
+    indentation--;
+    indent();
+    puts("</table>");
+}
+
+void html::row_begin(unsigned int row) {
+    static constexpr const char* weekdays[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"};
+    indent();
+    puts("<tr>");
+    indent(1);
+    printf("<th scope=\"row\">%s</th>\n", weekdays[row]);
+}
+
+void html::row_end() {
+    indent(-1);
+    puts("</tr>");
+}
+
+void html::cell_out_of_range() {
+    indent();
+    puts("<td class=\"out-of-range\"></td>");
+}
+
+void html::cell(unsigned commits) {
+    indent();
+    // TODO: use nice coloring and tooltips instead of printing the commits
+    printf("<td>%u</td>\n", commits);
+}

mercurial