From 63aaad529c9ae9358f22fd9563aeabc679f4defd Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Mon, 10 Aug 2015 18:28:53 +0200 Subject: Add support for generating XHTML5 --- web/xhtml | 255 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 web/xhtml (limited to 'web') diff --git a/web/xhtml b/web/xhtml new file mode 100644 index 0000000..b8d6ce0 --- /dev/null +++ b/web/xhtml @@ -0,0 +1,255 @@ +// file : web/xhtml -*- C++ -*- +// copyright : Copyright (c) 2014-2015 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef WEB_XHTML +#define WEB_XHTML + +#include + +namespace web +{ + // "Canonical" XHTML5 vocabulary. + // + // One-letter tag names and local variable clash problem: + // + // a at|an|an anc anch + // b bt|bo|bl bld bold + // i it|it|it itl ital + // p pt|pr|pr par para + // q qt|qu|qt quo quot + // s st|st|st stk strk + // u ut|un|un unl undr + // + // Other options: + // - _a, a_, xa + // - A, I + // - x::i + // + // Things can actually get much worse, consider: + // + // int i; + // s << i << "text" << ~i; + // + // So perhaps this is the situation where the explicit namespace + // qualification (e.g., x::p) is the only robust option? + // + namespace xhtml + { + const char* xmlns = "http://www.w3.org/1999/xhtml"; + + using serializer_func = void (*) (xml::serializer&); + + struct attr_value_base + { + const char* name; + mutable const attr_value_base* next; + + virtual void + operator() (xml::serializer& s) const = 0; + + protected: + explicit + attr_value_base (const char* n): name (n), next (nullptr) {} + }; + + template + struct attr_value: attr_value_base + { + const T* val; + + attr_value (const char* n, const T& v): attr_value_base (n), val (&v) {} + + virtual void + operator() (xml::serializer& s) const + { + s.attribute (name, *val); + if (next != nullptr) + s << *next; + } + }; + + struct element_base; + + // Element without any content, e.g., *BR. + // + struct empty_element + { + const element_base* e; + + void + operator() (xml::serializer& s) const; + }; + + struct element_base + { + virtual void + operator() (xml::serializer& s) const = 0; + + virtual serializer_func + operator~ () const = 0; + + empty_element + operator* () const {return empty_element {this};} + }; + + inline void empty_element:: + operator() (xml::serializer& s) const {s << *e << ~*e;} + + // Element with an attribute chain, e.g., P(ID = 123, CLASS = "abc"). + // + struct attr_element: element_base + { + const element_base* e; + const attr_value_base* a; + + attr_element (const element_base& e, const attr_value_base& a) + : e (&e), a (&a) {} + + virtual void + operator() (xml::serializer& s) const {s << *e << *a;} + + virtual serializer_func + operator~ () const {return ~*e;} + }; + + struct element: element_base + { + const char* name; + + explicit + element (const char* n): name (n) {} + + virtual void + operator() (xml::serializer& s) const {s.start_element (xmlns, name);} + + virtual serializer_func + operator~ () const {return [](xml::serializer& s) {s.end_element ();};} + + // s << elem(attr1 = 123, attr2 = "abc"); + // + template + attr_element + operator () (const attr_value& a1) const + { + return attr_element (*this, a1); + } + + template + attr_element + operator () (const attr_value& a1, const attr_value&... an) const + { + a1.next = operator() (an...).a; + return attr_element (*this, a1); + } + + protected: + element () = default; + }; + + struct inline_element: element + { + using element::element; + using element::operator(); + + virtual void + operator() (xml::serializer& s) const + { + s.suspend_indentation (); + element::operator() (s); + } + + virtual serializer_func + operator~ () const + { + return [](xml::serializer& s) + { + s.end_element (); s.resume_indentation (); + }; + } + }; + + struct attribute + { + const char* name; + + explicit + attribute (const char* n): name (n) {} + + // s << (attr1 = 123) << (attr2 = "abc"); + // + template + attr_value + operator= (const T& v) const {return attr_value (name, v);} + + // s << attr1 (123) << attr2 ("abc"); + // + template + attr_value + operator() (const T& v) const {return attr_value (name, v);} + + // s << attr1 << 123 << ~attr1 << attr2 << "abc" << ~attr2; + // + void + operator() (xml::serializer& s) const {s.start_attribute (name);} + + virtual serializer_func + operator~ () const {return [](xml::serializer& s) {s.end_attribute ();};} + }; + + // Elements. + // + // Note that they are all declared static which means we may end + // up with multiple identical copies if this header get included + // into multiple translation units. The hope here is that the + // compiler will "see-through" and eliminate all of them. + // + struct html_element: element + { + html_element () {} // Uninitialized const static. + + virtual void + operator() (xml::serializer& s) const + { + s.doctype_decl ("html"); + s.start_element (xmlns, "html"); + s.namespace_decl (xmlns, ""); + } + }; + static const html_element HTML; + + struct head_element: element + { + head_element () {} // Uninitialized const static. + + virtual void + operator() (xml::serializer& s) const + { + s.start_element (xmlns, "head"); + s.start_element (xmlns, "meta"); + s.attribute ("charset", "UTF-8"); + s.end_element (); + } + }; + static const head_element HEAD; + + static const element TITLE ("title"); + static const element BODY ("body"); + static const element P ("p"); + + static const inline_element B ("b"); + static const inline_element I ("i"); + static const inline_element U ("u"); + + static const inline_element EM ("em"); + static const inline_element BR ("br"); + + // Attributes. + // + static const attribute ID ("id"); + static const attribute CLASS ("class"); + static const attribute STYLE ("style"); + } +} + +#endif // WEB_XHTML -- cgit v1.1