--- /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("&"); + break; + case '\"': + buffer.append("""); + break; + case '\'': + buffer.append("'"); + break; + case '<': + buffer.append("<"); + break; + case '>': + buffer.append(">"); + 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); +}