blob: b74beb898ec315dfe741dd5ae7ec7bf66aab61d0 [file] [log] [blame]
Austin Schuh9d823002019-04-14 12:53:17 -07001// Copyright (c) 2013-2017, Matt Godbolt
Austin Schuh24adb6b2015-09-06 17:37:40 -07002// All rights reserved.
Austin Schuh9d823002019-04-14 12:53:17 -07003//
4// Redistribution and use in source and binary forms, with or without
Austin Schuh24adb6b2015-09-06 17:37:40 -07005// modification, are permitted provided that the following conditions are met:
Austin Schuh9d823002019-04-14 12:53:17 -07006//
7// Redistributions of source code must retain the above copyright notice, this
Austin Schuh24adb6b2015-09-06 17:37:40 -07008// list of conditions and the following disclaimer.
Austin Schuh9d823002019-04-14 12:53:17 -07009//
10// Redistributions in binary form must reproduce the above copyright notice,
11// this list of conditions and the following disclaimer in the documentation
Austin Schuh24adb6b2015-09-06 17:37:40 -070012// and/or other materials provided with the distribution.
Austin Schuh9d823002019-04-14 12:53:17 -070013//
14// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
18// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
19// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
20// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
21// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
22// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
23// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
Austin Schuh24adb6b2015-09-06 17:37:40 -070024// POSSIBILITY OF SUCH DAMAGE.
25
26#pragma once
27
28#include "seasocks/ToString.h"
29
30#include <functional>
31#include <iostream>
32#include <list>
33#include <sstream>
34#include <string>
35
36namespace seasocks {
37
38namespace html {
39
40class Element {
41 bool _isTextNode;
42 bool _needsClose;
43 std::string _nameOrText;
44 std::string _attributes;
45 std::list<Element> _children;
46
47 void append() {
48 }
49
Austin Schuh9d823002019-04-14 12:53:17 -070050 template <typename T, typename... Args>
51 void append(const T& t, Args&&... args) {
Austin Schuh24adb6b2015-09-06 17:37:40 -070052 operator<<(t);
53 append(std::forward<Args>(args)...);
54 }
55
Austin Schuh9d823002019-04-14 12:53:17 -070056 Element()
57 : _isTextNode(true), _needsClose(false) {
58 }
59
Austin Schuh24adb6b2015-09-06 17:37:40 -070060public:
Austin Schuh9d823002019-04-14 12:53:17 -070061 template <typename... Args>
62 Element(const std::string& name, bool needsClose, Args&&... args)
63 : _isTextNode(false),
64 _needsClose(needsClose),
65 _nameOrText(name) {
Austin Schuh24adb6b2015-09-06 17:37:40 -070066 append(std::forward<Args>(args)...);
67 }
68
Austin Schuh9d823002019-04-14 12:53:17 -070069 template <typename T>
Austin Schuh24adb6b2015-09-06 17:37:40 -070070 static Element textElement(const T& text) {
71 Element e;
72 e._nameOrText = toString(text);
73 return e;
74 }
75
Austin Schuh9d823002019-04-14 12:53:17 -070076 template <typename T>
Austin Schuh24adb6b2015-09-06 17:37:40 -070077 Element& addAttribute(const char* attr, const T& value) {
78 _attributes += std::string(" ") + attr + "=\"" + toString(value) + "\"";
79 return *this;
80 }
81
82 Element& clazz(const std::string& clazz) {
83 return addAttribute("class", clazz);
84 }
85
86 Element& title(const std::string& title) {
87 return addAttribute("title", title);
88 }
89
90 Element& style(const std::string& style) {
91 return addAttribute("style", style);
92 }
93
94 Element& alt(const std::string& alt) {
95 return addAttribute("alt", alt);
96 }
97
98 Element& hidden() {
99 return addAttribute("style", "display:none;");
100 }
101
102 Element& id(const std::string& id) {
103 return addAttribute("id", id);
104 }
105
Austin Schuh9d823002019-04-14 12:53:17 -0700106 Element& operator<<(const char* child) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700107 _children.push_back(textElement(child));
108 return *this;
109 }
110
Austin Schuh9d823002019-04-14 12:53:17 -0700111 Element& operator<<(const std::string& child) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700112 _children.push_back(textElement(child));
113 return *this;
114 }
115
Austin Schuh9d823002019-04-14 12:53:17 -0700116 Element& operator<<(const Element& child) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700117 _children.push_back(child);
118 return *this;
119 }
120
Austin Schuh9d823002019-04-14 12:53:17 -0700121 template <class T>
Austin Schuh24adb6b2015-09-06 17:37:40 -0700122 typename std::enable_if<std::is_integral<T>::value || std::is_floating_point<T>::value, Element&>::type
Austin Schuh9d823002019-04-14 12:53:17 -0700123 operator<<(const T& child) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700124 _children.push_back(textElement(child));
125 return *this;
126 }
127
Austin Schuh9d823002019-04-14 12:53:17 -0700128 friend std::ostream& operator<<(std::ostream& os, const Element& elem) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700129 if (elem._isTextNode) {
130 os << elem._nameOrText;
Austin Schuh9d823002019-04-14 12:53:17 -0700131 for (const auto& it : elem._children) {
132 os << it;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700133 }
134 return os;
135 }
136 os << "<" << elem._nameOrText << elem._attributes << ">";
Austin Schuh9d823002019-04-14 12:53:17 -0700137 for (const auto& it : elem._children) {
138 os << it;
Austin Schuh24adb6b2015-09-06 17:37:40 -0700139 }
140 if (elem._needsClose) {
141 os << "</" << elem._nameOrText << ">";
142 }
143 return os;
144 }
145
Austin Schuh9d823002019-04-14 12:53:17 -0700146 template <typename C, typename F>
Austin Schuh24adb6b2015-09-06 17:37:40 -0700147 Element& addAll(const C& container, F functor) {
148 for (auto it = container.cbegin(); it != container.cend(); ++it) {
149 operator<<(functor(*it));
150 }
151 return *this;
152 }
153
154 std::string str() const {
155 std::ostringstream os;
156 os << *this;
157 return os.str();
158 }
159};
160
Austin Schuh9d823002019-04-14 12:53:17 -0700161#define HTMLELEM(XX) \
162 template <typename... Args> \
163 inline Element XX(Args&&... args) { \
164 return Element(#XX, true, std::forward<Args>(args)...); \
165 }
Austin Schuh24adb6b2015-09-06 17:37:40 -0700166HTMLELEM(html)
167HTMLELEM(head)
168HTMLELEM(title)
169HTMLELEM(body)
170HTMLELEM(h1)
171HTMLELEM(h2)
172HTMLELEM(h3)
173HTMLELEM(h4)
174HTMLELEM(h5)
175HTMLELEM(table)
176HTMLELEM(thead)
177HTMLELEM(tbody)
178HTMLELEM(tr)
179HTMLELEM(td)
180HTMLELEM(th)
181HTMLELEM(div)
182HTMLELEM(span)
183HTMLELEM(ul)
184HTMLELEM(ol)
185HTMLELEM(li)
186HTMLELEM(label)
187HTMLELEM(button)
188#undef HTMLELEM
189
Austin Schuh9d823002019-04-14 12:53:17 -0700190inline Element empty() {
191 return Element::textElement("");
192}
Austin Schuh24adb6b2015-09-06 17:37:40 -0700193
Austin Schuh9d823002019-04-14 12:53:17 -0700194template <typename T>
195inline Element text(const T& t) {
196 return Element::textElement(t);
197}
Austin Schuh24adb6b2015-09-06 17:37:40 -0700198
Austin Schuh9d823002019-04-14 12:53:17 -0700199inline Element img(const std::string& src) {
200 return Element("img", false).addAttribute("src", src);
201}
Austin Schuh24adb6b2015-09-06 17:37:40 -0700202
Austin Schuh9d823002019-04-14 12:53:17 -0700203inline Element checkbox() {
204 return Element("input", false).addAttribute("type", "checkbox");
205}
Austin Schuh24adb6b2015-09-06 17:37:40 -0700206
Austin Schuh9d823002019-04-14 12:53:17 -0700207inline Element externalScript(const std::string& src) {
208 return Element("script", true).addAttribute("src", src).addAttribute("type", "text/javascript");
209}
Austin Schuh24adb6b2015-09-06 17:37:40 -0700210
Austin Schuh9d823002019-04-14 12:53:17 -0700211inline Element inlineScript(const std::string& script) {
212 return Element("script", true, script).addAttribute("type", "text/javascript");
213}
Austin Schuh24adb6b2015-09-06 17:37:40 -0700214
215inline Element link(const std::string& href, const std::string& rel) {
216 return Element("link", false).addAttribute("href", href).addAttribute("rel", rel);
217}
218
Austin Schuh9d823002019-04-14 12:53:17 -0700219template <typename... Args>
220inline Element a(const std::string& href, Args&&... args) {
Austin Schuh24adb6b2015-09-06 17:37:40 -0700221 return Element("a", true, std::forward<Args>(args)...).addAttribute("href", href);
222}
223
Austin Schuh9d823002019-04-14 12:53:17 -0700224} // namespace html
Austin Schuh24adb6b2015-09-06 17:37:40 -0700225
Austin Schuh9d823002019-04-14 12:53:17 -0700226} // namespace seasocks