summaryrefslogtreecommitdiff
path: root/src/tag.cc
diff options
context:
space:
mode:
authorJoel Klinghed <the_jk@spawned.biz>2021-11-17 22:34:57 +0100
committerJoel Klinghed <the_jk@spawned.biz>2021-11-17 22:34:57 +0100
commit6232d13f5321b87ddf12a1aa36b4545da45f173d (patch)
tree23f3316470a14136debd9d02f9e920ca2b06f4cc /src/tag.cc
Travel3: Simple image and video display site
Reads the images and videos from filesystem and builds a site in memroy.
Diffstat (limited to 'src/tag.cc')
-rw-r--r--src/tag.cc139
1 files changed, 139 insertions, 0 deletions
diff --git a/src/tag.cc b/src/tag.cc
new file mode 100644
index 0000000..a563b9d
--- /dev/null
+++ b/src/tag.cc
@@ -0,0 +1,139 @@
+#include "common.hh"
+
+#include "htmlutil.hh"
+#include "tag.hh"
+
+#include <map>
+#include <vector>
+
+namespace {
+
+class TextRenderable : public virtual Renderable {
+public:
+ explicit TextRenderable(std::string content)
+ : content_(std::move(content)) {}
+
+ void render(std::string* out) const override {
+ html::escape(content_, out);
+ }
+
+private:
+ std::string content_;
+};
+
+class ScriptRenderable : public virtual Renderable {
+public:
+ explicit ScriptRenderable(std::string content)
+ : content_(std::move(content)) {}
+
+ void render(std::string* out) const override {
+ out->append(content_);
+ }
+
+private:
+ std::string content_;
+};
+
+class TagImpl : public Tag {
+public:
+ TagImpl(std::string name, std::string content)
+ : name_(std::move(name)) {
+ add(std::move(content));
+ assert(html::escape(name_) == name_);
+ }
+
+ std::string_view name() const override {
+ return name_;
+ }
+
+ bool empty() const override {
+ return child_.empty();
+ }
+
+ bool has_attr(std::string const& name) const override {
+ return attr_.count(name);
+ }
+
+ Tag* clear_content() override {
+ child_.clear();
+ return this;
+ }
+
+ Tag* attr(std::string name, std::string value) override {
+ assert(html::escape(name) == name);
+ attr_[name] = std::move(value);
+ return this;
+ }
+
+ Tag* add(std::string content) override {
+ if (!content.empty()) {
+ if (name_ == "script") {
+ child_.push_back(
+ std::make_unique<ScriptRenderable>(std::move(content)));
+ } else {
+ child_.push_back(std::make_unique<TextRenderable>(std::move(content)));
+ }
+ }
+ return this;
+ }
+
+ Tag* add(std::unique_ptr<Tag> tag) override {
+ auto* ret = tag.get();
+ child_.push_back(std::move(tag));
+ return ret;
+ }
+
+ void render(std::string* out) const override {
+ size_t need = 1 + name_.size();
+ for (auto const& pair : attr_)
+ need += 1 + pair.first.size() + 2 + pair.second.size() + 1;
+ if (empty())
+ ++need;
+ out->reserve(need);
+ out->push_back('<');
+ out->append(name_);
+ for (auto const& pair : attr_) {
+ out->push_back(' ');
+ out->append(pair.first);
+ out->append("=\"");
+ html::escape(pair.second, out, html::EscapeTarget::ATTRIBUTE);
+ out->push_back('"');
+ }
+ if (empty()) {
+ if (name_ == "script") {
+ // Some browsers don't allow <script src=""/>, must be written as
+ // <script src=""></script>
+ out->append("></script>");
+ } else {
+ // There are tags, like <br> where the / is optional but why botter.
+ out->append("/>");
+ }
+ return;
+ }
+ out->push_back('>');
+
+ for (auto& child : child_) {
+ child->render(out);
+ }
+
+ out->reserve(name_.size() + 3);
+ out->append("</");
+ out->append(name_);
+ out->push_back('>');
+ }
+
+private:
+ std::string name_;
+ std::map<std::string, std::string> attr_;
+ std::vector<std::unique_ptr<Renderable>> child_;
+};
+
+} // namespace
+
+Tag* Tag::add_tag(std::string name, std::string content) {
+ return add(create(std::move(name), std::move(content)));
+}
+
+std::unique_ptr<Tag> Tag::create(std::string name, std::string content) {
+ return std::make_unique<TagImpl>(std::move(name), std::move(content));
+}