#include #include #include #include "util.h" #include "kutil.h" #include "error.h" #include "file.h" #include "html_util.h" #include "log.h" #include "show.h" std::ostream& operator<<(std::ostream& os, const attr_t& a) { if (std::holds_alternative(a)) os << std::get(a); else if (std::holds_alternative(a)) os << std::get(a); else if (std::holds_alternative(a)) os << std::get(a); return os; } int _indent = 0; std::ostream& operator<<(std::ostream& os, const html_element_t& h) { if (std::holds_alternative(h)) os << std::get(h); else if (std::holds_alternative(h)) os << std::get(h); return os; } std::ostream& operator<<(std::ostream& os, const elements_t& elements) { // for (const HTML& h : elements) { for (const html_element_t& h : elements) { /*n if (std::holds_alternative(h)) os << h; else if (std::holds_alternative(h)) os << h; } */ os << h; } return os; } bool attribute_with_value(attr_t attr) { return std::holds_alternative(attr) && !(std::get(attr)).empty(); } std::ostream& operator<<(std::ostream& os, const HTML& h) { bool void_element = h.void_element(h.m_tag); //bool newline = !(h.void_element(h.m_tag) or h.empty_element(h.m_tag)); //bool empty_element = h.empty_element(h.m_tag) and trim(h.m_text).size() == 0; os << "<" << h.m_tag; for (auto [t,v] : h.m_attrs) { os << " " << t; if (attribute_with_value(v)) { os << "=\"" << v << "\""; } } os << ">"; if (h.m_elements.size() > 0) { os << "\n" << h.m_elements; } //if (!empty_element) { if (trim(h.m_text).size() > 0) { if (h.m_text.find(' ') != std::string::npos) { os << "\n"; } os << trim(h.m_text); // << "\n"; } /* //os << h.m_text; std::string intext = "\n" + h.m_text; std::string sp(_indent, ' '); intext = std::regex_replace(intext, std::regex("\n"), "\n" + sp); os << intext; */ //os << trim(h.m_text) << "\n"; // } /* if (true or newline) { os << "\n"; _indent -= 2; } */ //if (!void_element and !empty_element) { if (!void_element) { if (h.m_text.find(' ') != std::string::npos) { os << "\n"; } os << ""; } os << "\n"; /* if (true or !void_element and !empty_element) { //std::string sp(_indent, ' '); //os << sp << ""; os << "\n"; } os << "\n"; */ return os; } std::string to_string(elements_t e) { std::stringstream ss{}; ss << e; return ss.str(); } bool HTML::void_element(std::string tag) const { if (tag == "!DOCTYPE") return true; else return std::find (m_void_tags.begin(), m_void_tags.end(), tag) != m_void_tags.end(); } bool HTML::empty_element(std::string tag) const { if (tag == preamble_tag) return true; else return std::find (m_empty_tags.begin(), m_empty_tags.end(), tag) != m_empty_tags.end(); } //HTML& HTML::attr(std::string name, std::variant value) HTML& HTML::attr(std::string name, attr_t value) { m_attrs.push_back({name, value}); return *this; } HTML& HTML::attrs(std::string s) { static const std::regex attr_rgx(R"((\w+)\s*=\s*\"(.*?)\")"); auto attrs_begin = std::sregex_iterator(s.begin(), s.end(), attr_rgx); auto attrs_end = std::sregex_iterator(); //std::cout << "Found " << std::distance(attrs_begin, attrs_end) << " attrs\n"; for (std::sregex_iterator iter = attrs_begin; iter != attrs_end; ++iter) { std::smatch match = *iter; //std::cout << "attr: " << s << ": " << (*iter)[1] << " " << (*iter)[2] << "\n"; m_attrs.push_back({(*iter)[1], (*iter)[2]}); } return *this; } // Functions: namespace html { HTML elt(std::string tag) { HTML h(tag); return h; } HTML elt(std::string tag, std::string text) { HTML h(tag, trim(text)); return h; } HTML elt(std::string tag, elements_t elements) { HTML h(tag, elements); return h; } HTML elt(std::string tag, HTML e, elements_t elts) { elements_t elements = {e}; elements.insert(elements.end(), elts.begin(), elts.end()); HTML h(tag, elements); return h; } HTML preamble() { return elt(preamble_tag); } // // // elements_t meta_elements() { elements_t result { elt("meta") .attr("name", "viewport") .attr("content", "width=device-width, initial-scale=1"), elt("meta") .attr("charset", "UTF-8"), // Does not conform to HTML 5: // elt("meta") // .attr("http-equiv", "Cache-Control") // .attr("content", "no-cache, no-store, must-revalidate"), // elt("meta") // .attr("http-equiv", "Pragma") // .attr("content", "no-cache"), // elt("meta") // .attr("http-equiv", "Expires") // .attr("content", "0") }; /* result.push_back( elt("link") .attr("rel", "icon") .attr("href", "/favicon.ico") .attr("type", "image/x-icon")) */ return result; } elements_t local_font_elements(strings_t fontnames) { elements_t result {}; for (auto name : fontnames) { // std::cout << "Font: " << name << "\n"; result.push_back(elt("link") .attr("href", "fonts/" + name + ".css") .attr("rel", "stylesheet")); } return result; } elements_t google_font_elements(strings_t fontnames) { elements_t result {}; if (fontnames.size() > 0) { result.push_back(elt("link") .attr("rel", "preconnect") .attr("href", "https://fonts.googleapis.com")); result.push_back(elt("link") .attr("rel", "preconnect") .attr("href", "https://fonts.gstatic.com")); } for (auto name : fontnames) result.push_back( elt("link") .attr("href", "https://fonts.googleapis.com/css2?family=" + name + "&display=swap") .attr("rel", "stylesheet")); return result; } elements_t javascript(std::string output_dir, strings_t js_filenames) { //elements_t result { jquery_elements() }; elements_t result {}; std::string js_dir = output_dir + "/js/"; fs::create_directory(js_dir); for (auto fname : js_filenames) { /* std::string base = file_basename(fname) + ".js"; std::string js_page_filename = js_dir + base; if (file_exists(js_page_filename)) { fs::remove(js_page_filename); } fs::copy(fname, js_page_filename); */ result.push_back(elt("script") .attr("src", fname)); //"js/" + base)); } return result; } HTML head(std::string title, std::string css, strings_t css_filenames, strings_t local_fonts, strings_t google_fonts) { elements_t elts = meta_elements(); // msg() << "ELTS sks: " << elts << "\n"; for (auto e : local_font_elements(local_fonts)) elts.push_back(e); for (auto e : google_font_elements(google_fonts)) elts.push_back(e); //elts += google_font_prolog(); //std::string css_dir = output_dir + "/css/"; // No; a single file at the top level...? // std::string css_dir = output_dir + "/"; // fs::create_directory(css_dir); for (auto fname : css_filenames) { // msg() << "CSS: " << fname << "\n"; /* std::string base = file_basename(fname) + ".css"; std::string page_filename = css_dir + base; if (!file_exists(page_filename)) { fs::copy(fname, page_filename); } */ elts.push_back(elt("link") .attr("rel", "stylesheet") .attr("href", fname)); // base)); } if (!css.empty()) { elts.push_back(elt("style", css)); } if (!title.empty()) { elts.push_back(elt("title", trim(title))); } HTML result = elt("head", elts); return result; } HTML button(std::string id, std::string name, std::string attrib="", std::string value="") { std::vector hidden = {"Clear", "Back"}; HTML result = elt("span", string_replace(name, " ", " ")).id(id).cls("navb"); if (attrib != "") { result.attr(attrib, value); } if (std::find(hidden.begin(), hidden.end(), name) != hidden.end()) { result.attr("hidden", ""); } return result; } elements_t navtools(int maximum_depth=5) { elements_t result {}; for (auto [name, id] : std::vector> {{ "Text only", "textshow" }, { "Next", "next" }, { "Previous", "previous" }}) { //{ "Fit", "fit" }}) { result.push_back(button(id, name)); } for (int n = 1; n <= maximum_depth; n++) { result.push_back( elt("span", std::to_string(n)).cls("navb depthchoice").attr("data-depth", std::to_string(n))); } elements_t fit_elements = { elt("input", "") .id("fit_check") .attr("name", "fit_check") .attr("type", "checkbox") .attr("active", "false") .attr("autocomplete", "off"), //.attr("checked", ""), elt("label", "Fit") .id("fit_check_label") .attr("for", "fit_check") }; result.push_back(elt("span", fit_elements) .attr("id", "fit")); elements_t linkshow_elements = { elt("input", "") .id("linkshow_check") .attr("name", "linkshow_check") .attr("type", "checkbox") .attr("active", "false") .attr("autocomplete", "off"), //.attr("checked", ""), elt("label", "Links") .id("linkshow_check_label") .attr("for", "linkshow_check") }; result.push_back(elt("span", linkshow_elements) .attr("id", "linkshow")); return result; } elements_t search() { elements_t result {}; for (auto [name, id] : std::vector> {{ "Clear", "search_clear" }, { "Back", "search_back_label"}, { "Search", "search_label" } }) result.push_back(button(id, name)); elements_t search_input { elt("span", "Ⓧ").id("search_clear_input").attr("hidden", "") }; result.push_back( elt("span", { elt("input", "").id("search_input") .attr("type", "text").attr("name", "search_input") .attr("autocomplete", "off") .attr("required", "required"), elt("span", search_input).id("search_clear_input_box") }).id("search_box")); //result.push_back(button("help", "Help")); /* elements_t search_input {}; search_input.push_back( elt("span", result).id("foldersearch")); return search_input; */ return result; } HTML navigation(int max_level) { return elt("div", { elt("span", navtools(max_level)).id("navtools"), elt("span", { elt("span", search()).id("searchtools"), elt("span", button("help", "Help"))})}) .id("nav"); } elements_t middle(std::string text, //std::string nav, std::string toc) { elements_t result {}; toc = trim(toc); text = trim(text); if (!toc.empty()) { result.push_back(elt("div", toc) .attr("id", "toc")); result.push_back(elt("div") .attr("id", "resizer")); } if (!text.empty()) { text += "
\n"; elements_t content {}; content.push_back(elt("div", text).attr("id", "text")); //if (!nav.empty()) { //content.push_back(elt("div", "

SEARCH

") // .attr("id", "search_page")); //content.push_back(elt("div", "

HELP

").attr("id", "help_page")); //} result.push_back(elt("div", content).attr("id", "content")); } else { throw Argument_error("The text content of the HTML page is not defined"); } return result; } void install_local_fonts(strings_t font_dirs, strings_t names, std::string output_dir) { std::string output_font_dir = output_dir + "/fonts"; //std::cout << "Font directory for website: " << output_font_dir << "\n"; if (!file_exists(output_font_dir)) { fs::create_directory(output_font_dir); //std::cout << " Font directory created: " << output_font_dir << "\n"; } for (auto name : names) { //std::cout << "Font search for " << name << ": " << klammertext_dir() << "/sks/font/fonts/\n"; std::string src_font_dir = klammertext_dir() + "/sks/font/fonts/" + name; if (!file_exists(src_font_dir)) { bool found = false; for (std::string font_dir : font_dirs) { src_font_dir = font_dir + "/" + name; //std::cout << "Font search for " << name << ": " << font_dir << "/\n"; if (file_exists(src_font_dir)) { found = true; break; } } if (!found) { throw Argument_error( "Local font directory \"" + src_font_dir + "\" does not exist"); } } //std::cout << "Font " << name << ": " << src_font_dir << "\n"; std::string src_font_css = src_font_dir + ".css"; if (!fs::exists(src_font_css)) { throw Argument_error( "Local font CSS file \"" + src_font_css + "\" does not exist"); } std::string font_copy = "cp -r " + src_font_css + " " + src_font_dir + " " + output_font_dir; //std::string files_copy = "cp -r " + basename + " " + font_dir; //cout << css_copy << "\n" << files_copy << "\n"; //std::cout << system(css_copy.c_str()) << "\n"; //std::cout << system(files_copy.c_str()) << "\n"; if (system(font_copy.c_str()) != 0) { std::cout << "Font copy command: " << font_copy << "\n"; throw Argument_error("Local font \"" + name + "\" not found"); } } } elements_t status(std::string date, std::string version, std::string copyright) { elements_t result {}; if (!date.empty()) { result.push_back(elt("span", date).cls("footeritem")); } if (!version.empty()) { result.push_back(elt("span", version).cls("footeritem")); } if (!copyright.empty()) { result.push_back(elt("span", copyright).cls("footeritem")); } return result; } elements_t page( std::string title, std::string page_title, std::string output_dir, std::string nav, int max_level, std::string toc, std::string text, std::string date, std::string version, std::string copyright, std::string css, strings_t css_filenames, strings_t js_filenames, strings_t local_fonts, strings_t google_fonts, std::string logo) { if (page_title.empty()) page_title = title; elements_t body {}; if (!title.empty()) { elements_t title_bar {}; title_bar.push_back(elt("span", trim(title)).attr("id", "title_text")); if (!logo.empty()) { title_bar.push_back(elt("span", trim(logo)).attr("id", "logo")); } body.push_back(elt("div", title_bar).attr("id", "title")); } if (!nav.empty()) { body.push_back(navigation(max_level)); } if (!nav.empty() and !toc.empty()) { body.push_back(elt("div", elt("div", middle(text, toc)) .attr("id", "middle"))); } else { //std::cout << "ONLY TEXT\n" << text << "\n\n"; body.push_back(elt("div", elt("div", text) .attr("id", "text")) .attr("id", "middle")); } elements_t status_elements = status(date, version, copyright); if (!empty(status_elements)) { body.push_back(elt("div", status_elements).attr("id", "status")); } //if (!status.empty()) // body.push_back(elt("div", trim(status)).attr("id", "status")); if (!nav.empty() and !toc.empty()) body.push_back(elt("div", "").attr("id", "pagecache")); if (!js_filenames.empty()) { auto js_elements { javascript(output_dir, js_filenames) }; body.insert(std::end(body), std::begin(js_elements), std::end(js_elements)); } elements_t result { preamble() }; if (page_title.empty()) page_title = title; result.push_back( elt("html", { head(page_title, css, css_filenames, local_fonts, google_fonts), elt("body", body) }).attr("lang", "en")); return result; } void add_title(elements_t& body, std::string title, std::string logo) { elements_t title_bar {}; title_bar.push_back(elt("span", trim(title)).attr("id", "title_text")); if (!logo.empty()) { title_bar.push_back(elt("span", trim(logo)).attr("id", "logo")); } body.push_back(elt("div", title_bar).attr("id", "title")); } void add_nav(elements_t& body, int max_level) { body.push_back(navigation(max_level)); } void add_text(elements_t& body, std::string text, std::string toc, bool text_only) { if (text_only) { elements_t content {}; content.push_back(elt("div", text).attr("id", "text")); body.push_back( elt("div", elt("div", content).attr("id", "content")).attr("id", "middle")); } else { body.push_back(elt("div", middle(text, toc)).attr("id", "middle")); } } void add_bottom_spacer(elements_t& body) { body.push_back(elt("div").cls("endspace")); } void add_status_bar( elements_t& body, std::string date, std::string version, std::string copyright) { elements_t status_elements = status(date, version, copyright); if (!empty(status_elements)) { body.push_back(elt("div", status_elements).attr("id", "status")); } } void add_page_cache(elements_t& body) { body.push_back(elt("div", "").attr("id", "pagecache")); } void add_js_links(elements_t& body, strings_t js_filenames, std::string output_dir) { if (!js_filenames.empty()) { auto js_elements { javascript(output_dir, js_filenames) }; body.insert(std::end(body), std::begin(js_elements), std::end(js_elements)); } } elements_t make_page( elements_t body, std::string page_title, std::string css, strings_t css_filenames, strings_t local_fonts, strings_t google_fonts) { elements_t page { preamble() }; page.push_back( elt("html", { head(page_title, css, css_filenames, local_fonts, google_fonts), elt("body", body) }).attr("lang", "en")); return page; } bool tag_is_block_element(std::string tag) { auto location = std::find(block_elements.begin(), block_elements.end(), tag); return location != block_elements.end(); } bool block_element(std::string par) { (void)K::log(3); bool result = false; par = trim(par); if (par[0] == '<') { static const std::regex element_rgx(R"(^\s*]+).*?>)"); std::smatch match {}; std::regex_search(par, match, element_rgx); result = tag_is_block_element(match[1]); } return result; } std::string make_paragraphs(std::string html_text) { (void)K::log(3); std::string result {}; //html_text = std::regex_replace(html_text, std::regex(R"((
)\s+)"), "$1\n");
        //html_text = std::regex_replace(html_text, std::regex(R"(\s+
)"), "\n"); static const std::regex paragraph_split_rgx(R"(\n *(\n *)+)"); for (auto par : regex_split(html_text, paragraph_split_rgx)) { par = trim(par); if (par.size() > 0) { if (!block_element(par)) { par = "

" + par + "

"; } result += par + "\n\n"; } } return result; } std::string minimize_css(std::string src) { (void)K::log(3); std::string result = src; //std::smatch match {}; std::regex line_end_space(R"(\\}[ ]+)", std::regex::awk); result = regex_replace(result, line_end_space, "}"); std::regex whitespace(R"([\n ]+)", std::regex::awk); result = regex_replace(result, whitespace, " "); std::regex line_end(R"(}[ ]+)", std::regex::awk); result = regex_replace(result, line_end, "}\n"); return result; } std::string minimize_js(std::string src) { (void)K::log(3); return src; } }