diff options
| author | Joel Klinghed <the_jk@spawned.biz> | 2021-11-17 22:34:57 +0100 |
|---|---|---|
| committer | Joel Klinghed <the_jk@spawned.biz> | 2021-11-17 22:34:57 +0100 |
| commit | 6232d13f5321b87ddf12a1aa36b4545da45f173d (patch) | |
| tree | 23f3316470a14136debd9d02f9e920ca2b06f4cc /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.cc | 139 |
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)); +} |
