{"id":61,"date":"2019-06-01T19:19:35","date_gmt":"2019-06-01T18:19:35","guid":{"rendered":"https:\/\/blog.inplico.uk\/?p=61"},"modified":"2023-06-16T00:07:51","modified_gmt":"2023-06-15T23:07:51","slug":"formatted-html","status":"publish","type":"post","link":"https:\/\/blog.inplico.uk\/?p=61","title":{"rendered":"Formatted html &#8211; html_tree (pt.1)"},"content":{"rendered":"<p>There is a good, if somewhat basic tutorial available at tutorialspoint here: <a href=\"https:\/\/www.tutorialspoint.com\/cplusplus\/cpp_web_programming.htm\">https:\/\/www.tutorialspoint.com\/cplusplus\/cpp_web_programming.htm<\/a> so if you know nothing of using cgi then this is a good starting point.\u00a0 While cgicc works pretty well, debugging can be a bit of a nightmare unless you format your code to make it readable.<\/p>\n<p><strong>Please note:\u00a0<\/strong> This is reference material on how this class was created and its internals, if you want to simply download it and see examples of how to use it then please click <a href=\"https:\/\/blog.inplico.uk\/?p=81\">here<\/a>.<\/p>\n<p>Out of the box you have 2 options, neither of which I found were satisfactory.<\/p>\n<p>The first is to use escape characters; lots of escape characters; just about everywhere.\u00a0 This is quite error prone and if you want to output clean, nested, nodular html using this method can be quite a task.<\/p>\n<p>Here is an example of the sort of string that you may have to produce:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">\/\/**************************************************************\/\/\r\n\/\/ GET TIME HISTORY RECORDS                                     \/\/\r\n\/\/**************************************************************\/\/\r\nstd::string job_times::build_html_job_times_table() {\r\n\r\n    lookup_records2_psql jobTimes(m_Conn, \"ic_machine_job_times\");\r\n    get_pg_time_history(jobTimes);\r\n\r\n    string table (\r\n                  \"&lt;div  class=\\\"w3-container\\\"&gt;\\n\"\r\n                      \"\\t&lt;br&gt;\\n\"\r\n                      \"\\t&lt;table class=\\\"w3-table-all w3-border\\\" id=\\\"icdefault\\\"&gt;\\n\"\r\n                      \"\\t&lt;tr&gt;&lt;th class=\\\"w3-blue-gray\\\"&gt;&lt;h3&gt;Total Time:&lt;\/h3&gt;&lt;\/th&gt;&lt;td id=\\\"totalcell\\\" class=\\\"w3-border\\\"&gt;\"+ job_time_validate::get_total() +\"&lt;\/td&gt;&lt;\/tr&gt;\\n\"\r\n                      \"\\t&lt;tr&gt;&lt;th colspan=\\\"2\\\"&gt;Job Time Log:&lt;\/th&gt;&lt;\/tr&gt;\\n\"\r\n                  );\r\n\r\n\r\n    for(int i =0; i != jobTimes.get_nrows(); ++i) {\r\n            table.append(\r\n                        \"&lt;tr&gt;\\n\"\r\n                            \"\\t&lt;th class=\\\"w3-green\\\" id=\\\"icdefault\\\" width=\\\"80%\\\"&gt;Start:&lt;\/th&gt; &lt;th class=\\\"w3-black\\\" id=\\\"icdefault\\\"&gt;Total:&lt;\/th&gt;\\n\"\r\n                        \"&lt;\/tr&gt;\\n\"\r\n                        \"&lt;tr&gt;\\n\"\r\n                            \"\\t&lt;td id=\\\"icdefault\\\"&gt;\" + jobTimes.get_field_value(\"start_time\", i) + \"&lt;\/td&gt;\\n\"\r\n                            \"\\t&lt;td rowspan=\\\"3\\\" id=\\\"totalcell\\\" class=\\\"w3-border\\\"&gt;\" + jobTimes.get_field_value(\"session_total_time\", i) + \"&lt;\/td&gt;\\n\"\r\n                        \"&lt;\/tr&gt;\\n\"\r\n                        \"&lt;tr&gt;\\n\"\r\n                            \"\\t&lt;th class=\\\"w3-red\\\" id=\\\"icdefault\\\"&gt;Finish:&lt;\/td&gt;\\n\"\r\n                        \"&lt;\/tr&gt;\\n\"\r\n                        \"&lt;tr&gt;\\n\"\r\n                            \"\\t&lt;td id=\\\"icdefault\\\"&gt;\" + jobTimes.get_field_value(\"finish_time\", i) + \"&lt;\/td&gt;\\n\"\r\n                        \"&lt;\/tr&gt;\\n\"\r\n                        );\r\n    }\r\n\r\n    table.append(\r\n                    \"\\t&lt;\/table&gt;\\n\"\r\n                    \"\\t&lt;br&gt;\\n\"\r\n                \"&lt;\/div&gt;\\n\"\r\n                \"&lt;hr&gt;\\n\"\r\n                );\r\n\r\n    return(table);\r\n\r\n\r\n}\r\n\r\n<\/pre>\n<p>As you can see, while this is still better than just outputting a continual stream, it is not ideal so I decided to look at the alternatives.<\/p>\n<p>The first option that I looked at was trying to use raw strings. eg.:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">\/\/**************************************************************\/\/\r\n\/\/ HTML STYLES                                                  \/\/\r\n\/\/**************************************************************\/\/\r\nstring job_times::html_input_time() {\r\n\r\n    string headerText;\r\n\r\n    if(in_progress())\r\n        headerText = \"Finish Time:\";\r\n    else {\r\n        headerText = \"Start Time:\";\r\n    }\r\n\r\n\r\nreturn(R\"***(\r\n&lt;div class=\"w3-container\" id=\"times\" style=\"margin-top:90px\"&gt;\r\n    &lt;h4&gt;\r\n        )***\" + headerText + R\"***(\r\n    &lt;\/h4&gt;\r\n    &lt;input data-clocklet id=\"time_widget\"&gt;\r\n    &lt;br&gt;\r\n&lt;\/div&gt;\r\n&lt;div class=\"w3-container\"&gt;\r\n    &lt;hr&gt;\r\n    &lt;button class=\"w3-button w3-teal\" onclick=\"post_input_time()\"&gt;Save&lt;\/button&gt;\r\n&lt;\/div&gt;\r\n\r\n)***\");\r\n\r\n}<\/pre>\n<p>While I found this to be slightly less error prone than using escape sequences everywhere, it was still not ideal, especially not when handing variable data.<\/p>\n<p>Rather helpfully here, the code widget has made the same sort of mess of the formatting (colours) as my IDE, but also I do feel that this code is too alien to be jammed in the middle of a load of c++ code and you may very well become a cropper when trying to maintain it years down the line.<\/p>\n<p>Unhappy with my lot I felt that it would benefit if I wrote a class to resolve this problem, and to be honest, while it may not be perfect, it works pretty well for a couple of hundred lines of code.<\/p>\n<p>There are essentially 2 parts to this; the building of the structure and the parsing into a valid html string.\u00a0 I decided to put the structure in a class that I will call html_tree, but left the parsing in a namespace as a class was a little overkill.<\/p>\n<p>One of the things that I struggled to get my head around, and even now am having some difficulty in explaining, is the necessity to have a vector of iterations of the class within itself.\u00a0 To be honest, it is one of those conundrums where the code speaks for itself better than any explanation:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">#include &lt;vector&gt;\r\n#include &lt;string&gt;\r\n\r\nclass html_tree {\r\n\r\n    public:\r\n        \/\/CONSTRUCTOR\r\n        html_tree(\r\n                std::string     htmlTag,\r\n                std::string     &amp;htmlPage,\r\n                html_tree       *previousBranch = nullptr\r\n                );\r\n\r\n        \/\/DECLARATIONS\r\n        std::vector&lt;html_tree*&gt;      childNodes\r\n        html_tree                    *previousBranch;\r\n\r\n        \/\/DESTRUCTOR\r\n        ~html_tree();\r\n\r\n};<\/pre>\n<p>You will note that the nested iteration is a pointer; The reason for this is that (on my build box in any event) I got a segmentation fault after nesting a certain number of nodes (around 12 if I remember correctly) and so found it necessary to build this on the heap.\u00a0 PS. I do not like c++&#8217;s auto pointers as they just seem to create as many problems as they solve, but I see no reason why you could not use them if that is your greatest desire!<\/p>\n<p>Each iteration of this class will contain a single node along with its content.\u00a0 Next we need a series of methods to add content and create nested nodes:<\/p>\n<p><strong>ADDING A NEW NODE:<\/strong> In oder to create a new node, we are going to create a new_node() method. Each call to this method will create a nested iteration of the class and return a pointer to that iteration in order that it can be accessed.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">\/\/**************************************************************\/\/\r\n\/\/  SET NODE CONTENT                                            \/\/\r\n\/\/**************************************************************\/\/\r\nvoid html_tree::set_node_content(string content, bool lineBreak) { \r\n\r\n    m_tagContent.push_back( {content, lineBreak} );\r\n\r\n}\r\n<\/pre>\n<p>The &#8220;htmlTag&#8221; argument is whatever you wish to place between &#8220;&lt;&#8221; and &#8220;&gt;&#8221; in the declaration. so if you wanted to declare a node called &lt;body&gt; you would simply call the method literally as follows:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">html_tree *newNode = previousNode-&gt;new_node(\"body\");<\/pre>\n<p><strong>SETTING THE NODE CONTENT:<\/strong>\u00a0 Now that we have a node, we may want to add some content.<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">\/\/**************************************************************\/\/\r\n\/\/  SET NODE CONTENT                                            \/\/\r\n\/\/**************************************************************\/\/\r\nvoid html_tree::set_node_content(string content, bool lineBreak) { \r\n\r\n    m_tagContent.push_back( {content, lineBreak} );\r\n\r\n}<\/pre>\n<p>You will note the lineBreak flag in the function declaration, this is simply to tell the class if we want a &lt;br&gt; at the end of our content.\u00a0 LineBreak defaults to &#8220;true&#8221;\u00a0 if the function is not presented with an argument.<\/p>\n<p>There are several other methods in this class that are used for parsing, but these are the only nodes that we use for setting the nodes.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>HTML NODE TYPES:<\/strong>\u00a0 By default when we build our html page our function will nest each child node within a parent, but there are certain exceptions.\u00a0 The exception that we need to handle are <strong>void tags<\/strong> and <strong>block tags<\/strong>.\u00a0 Void tags are tags such as <strong>&lt;br&gt;<\/strong> that do not have a closing tag.\u00a0 block tags are tags where we do not want any physical line breaks between the opening and closing tags.\u00a0 Another feature of block tags is that they do not contain nested nodes.<\/p>\n<p>In order to handle these tags we maintain a couple of string vectors (probably should be a list really) in the header file; these can be changed easily at any point in the future if we have missed something or the language changes.\u00a0 At the moment we have:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">\/\/Void Tags (should not be closed)\r\n        const std::vector&lt;std::string&gt; voidTags {\r\n            \"area\",\r\n            \"base\",\r\n            \"br\",\r\n            \"col\",\r\n            \"command\",\r\n            \"embed\",\r\n            \"hr\",\r\n            \"img\",\r\n            \"input\",\r\n            \"keygen\",\r\n            \"link\",\r\n            \"meta\",\r\n            \"param\",\r\n            \"source\",\r\n            \"track\",\r\n            \"wbr\"\r\n        };\r\n\r\n        \/\/Blocks (That should not have new lines)\r\n        const std::vector&lt;std::string&gt; blockTags {\r\n            \"textarea\",\r\n            \"p\",\r\n            \"h1\",\r\n            \"h2\",\r\n            \"h3\",\r\n            \"h4\",\r\n            \"h5\",\r\n            \"h6\",\r\n            \"title\",\r\n            \"a\",\r\n            \"button\",\r\n            \"label\"\r\n        };<\/pre>\n<p>We can now create a simple function to check whether the tag is in one of the lists:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">\/\/**************************************************************\/\/\r\n\/\/ IS THE CURRENT TAG A VOID                                    \/\/\r\n\/\/**************************************************************\/\/\r\nbool html_tree::is_special_tag(string tag, const vector&lt;string&gt; &amp;table) {\r\n    bool voidTag = false;\r\n\r\n    for(auto &amp;it : table) {\r\n        if(tag == it) voidTag = true;\r\n    }\r\n\r\n    return (voidTag);\r\n}<\/pre>\n<p>And another to determine if the node has any children:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">\/\/**************************************************************\/\/\r\n\/\/ IS THIS A LEAF NODE                                          \/\/\r\n\/\/**************************************************************\/\/\r\nbool html_tree::is_leaf_node() {\r\n\r\n    return((childNodes.empty()) ? true : false);\r\n\r\n}<\/pre>\n<p>Finally we need another method to see if the node has been closed:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">\/\/**************************************************************\/\/\r\n\/\/ IS NODE CLOSED                                               \/\/\r\n\/\/**************************************************************\/\/\r\nhtml_tree::status html_tree::closed() { return(m_nodestatus); }<\/pre>\n<p>All that is required now is a couple of methods to open and close the nodes:<\/p>\n<p><strong>OPENING THE NODE:\u00a0<\/strong> This method opens the node formats its contents accordingly<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">\/\/**************************************************************\/\/\r\n\/\/ OPEN NODE                                                    \/\/\r\n\/\/**************************************************************\/\/\r\nvoid html_tree::open_node(unsigned tabs) {\r\n\r\n    if(tabs)\r\n        m_htmlPage.append(tabs, '\\t');\r\n\r\n    string tag(m_htmlTag.substr(0, m_htmlTag.find_first_of(\" \")));\r\n    bool blockTag(is_special_tag(tag, blockTags));\r\n\r\n    m_htmlPage.append(\"&lt;\" + m_htmlTag + \"&gt;\");\r\n\r\n    if(!blockTag)\r\n        m_htmlPage.append(\"\\n\");\r\n\r\n    if(!m_tagContent.empty()) {\r\n        for(TAG_CONTENT &amp;s : m_tagContent) {\r\n\r\n            if(blockTag) {\r\n                m_htmlPage.append(s.text);\r\n                if((s.lineBreak) &amp;&amp; (&amp;s != &amp;m_tagContent.back()))\r\n                    m_htmlPage.append(\"&lt;br&gt;\");\r\n            }\r\n            else {\r\n                m_htmlPage.append(tabs +1, '\\t');\r\n                m_htmlPage.append(s.text);\r\n                if((s.lineBreak) &amp;&amp; (&amp;s != &amp;m_tagContent.back()))\r\n                    m_htmlPage.append(\"&lt;br&gt;\");\r\n\r\n                m_htmlPage.append(\"\\n\");\r\n            }\r\n        }\r\n    }\r\n    m_nodestatus = status::OPEN;\r\n\r\n}<\/pre>\n<p>You will note that the only parameter is tabs, this is the number of indents to prepend the line with.<\/p>\n<p>In order to determine the name of the node the first thing that the method does is look for the first space within the declaration; anything that precedes it is the node name.<\/p>\n<p>All that this method does is wrap the tag declaration around opening and closing brackets (&#8220;&lt;&#8221; and\u00a0 &#8220;&gt;&#8221;) and formats any content depending on the type of node declared.<\/p>\n<p>&nbsp;<\/p>\n<p><strong>CLOSING THE NODE:<\/strong> When you use this class you do not have to handle the closing of nodes, this is done automatically by calling the close_node() method:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"cpp\">\/\/**************************************************************\/\/\r\n\/\/ CLOSE NODE                                                   \/\/\r\n\/\/**************************************************************\/\/\r\nvoid html_tree::close_node(unsigned tabs) {\r\n\r\n    string tag(m_htmlTag.substr(0, m_htmlTag.find_first_of(\" \")));\r\n\r\n    if(!is_special_tag(tag, voidTags)) {\r\n\r\n        if(!is_special_tag(tag, blockTags)) {\r\n            if(tabs)\r\n                m_htmlPage.append(tabs, '\\t');\r\n        }\r\n\r\n            m_htmlPage.append(\"&lt;\/\" + tag + \"&gt;\\n\");\r\n\r\n    }\r\n\r\n    m_nodestatus = status::CLOSED;\r\n\r\n}<\/pre>\n<p>That is basically all there is to the html_tree class, In order to use it effectively to produce reasonably formatted html you will need my pretty_html functions to turn the parent node into a formatted html page.<\/p>\n<p><strong>THE DESTRUCTOR:<\/strong>\u00a0 As we have dynamically created our child nodes, when we have finished with this node, before destroying it, we need to destroy its children:<\/p>\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\">\/\/----------------------------\/\/\r\n\/\/ DESTRUCTOR\t\t      \/\/\r\n\/\/----------------------------\/\/\r\nhtml_tree::~html_tree() {\r\n    for(html_tree *it : childNodes) {\r\n        delete it;\r\n    }\r\n}<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>There is a good, if somewhat basic tutorial available at tutorialspoint here: https:\/\/www.tutorialspoint.com\/cplusplus\/cpp_web_programming.htm so if you know nothing of using cgi then this is a good starting point.\u00a0 While cgicc works pretty well, debugging can be a bit of a nightmare unless you format your code to make it readable. Please note:\u00a0 This is reference [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[5],"tags":[7,8,6],"class_list":["post-61","post","type-post","status-publish","format-standard","hentry","category-cgi","tag-cgi","tag-common-gateway-interface","tag-html"],"_links":{"self":[{"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=\/wp\/v2\/posts\/61","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=61"}],"version-history":[{"count":26,"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=\/wp\/v2\/posts\/61\/revisions"}],"predecessor-version":[{"id":435,"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=\/wp\/v2\/posts\/61\/revisions\/435"}],"wp:attachment":[{"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=61"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=61"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.inplico.uk\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=61"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}