// file : web/xhtml.hxx -*- C++ -*- // copyright : Copyright (c) 2014-2019 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #ifndef WEB_XHTML_HXX #define WEB_XHTML_HXX #include <libstudxml/serializer.hxx> #include <web/version.hxx> 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 // - user-defined literals: "a"_e, "/a"_e, "id"_a // // 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? // // // * Element/attribute name clash problem (e.g., STYLE) // // - some attribute/element name decorator (STYLEA, STYLE_A, STYLE_) // - rename attribute/element (e.g., STYLEDEF or CSSSTYLE[adds TYPE]); // in case of STYLE we should probably rename the element since // attribute will be much more frequently used. // - "scope" attributes inside elements (P::STYLE); somewhat // burdensome: P(P::STYLE); could then use low-case names // for attributes // - "scope" elements inside other elements (HEAD::STYLE); also // burdensome. // // // * Text wrapping/indentation // // For some (inline) elements we want additional indentation: // // 1. Indent content on newline (e.g., for <style>). // 2. Automatically wrap and indent lines at (or before) certain // length, say, 80 characters (e.g., for <p>). // // Would be nice to be able to implement this at the XHTML level, // not XML. // namespace xhtml { const char* const xmlns = "http://www.w3.org/1999/xhtml"; 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 <typename T> 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; // End tag of an element (~P). // struct end_element { const element_base* e; void operator() (xml::serializer& s) const; }; // Element without any conten (*BR). // struct empty_element { const element_base* e; void operator() (xml::serializer& s) const; }; struct element_base { virtual void start (xml::serializer& s) const = 0; virtual void end (xml::serializer& s) const = 0; void operator() (xml::serializer& s) const {start (s);} end_element operator~ () const {return end_element {this};} empty_element operator* () const {return empty_element {this};} }; inline void end_element:: operator() (xml::serializer& s) const {e->end (s);} 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 start (xml::serializer& s) const {e->start (s); s << *a;} virtual void end (xml::serializer& s) const {e->end (s);} }; struct element: element_base { const char* name; explicit element (const char* n): name (n) {} virtual void start (xml::serializer& s) const {s.start_element (xmlns, name);} virtual void end (xml::serializer& s) const {s.end_element (xmlns, name);} // s << elem(attr1 = 123, attr2 = "abc"); // template <typename T1> attr_element operator () (const attr_value<T1>& a1) const { return attr_element (*this, a1); } template <typename T1, typename... TN> attr_element operator () (const attr_value<T1>& a1, const attr_value<TN>&... an) const { a1.next = operator() (an...).a; return attr_element (*this, a1); } using element_base::operator(); }; struct inline_element: element { using element::element; virtual void start (xml::serializer& s) const { s.suspend_indentation (); element::start (s); } virtual void end (xml::serializer& s) const { element::end (s); s.resume_indentation (); } }; struct attribute; struct end_attribute { const attribute* a; void operator() (xml::serializer& s) const; }; struct attribute { const char* name; explicit attribute (const char* n): name (n) {} // s << (attr1 = 123) << (attr2 = "abc"); // template <typename T> attr_value<T> operator= (const T& v) const {return attr_value<T> (name, v);} // s << attr1 (123) << attr2 ("abc"); // template <typename T> attr_value<T> operator() (const T& v) const {return attr_value<T> (name, v);} // s << attr1 << 123 << ~attr1 << attr2 << "abc" << ~attr2; // virtual void start (xml::serializer& s) const {s.start_attribute (name);}; virtual void end (xml::serializer& s) const {s.end_attribute (name);} void operator() (xml::serializer& s) const {start (s);} end_attribute operator~ () const {return end_attribute {this};} }; inline void end_attribute:: operator() (xml::serializer& s) const {a->end (s);} // 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 (): element ("html") {} virtual void start (xml::serializer& s) const { s.doctype_decl ("html"); s.start_element (xmlns, name); s.namespace_decl (xmlns, ""); } }; static const html_element HTML; struct head_element: element { head_element (): element ("head") {} virtual void start (xml::serializer& s) const { s.start_element (xmlns, name); s.start_element (xmlns, "meta"); s.attribute ("charset", "UTF-8"); s.end_element (); s.start_element (xmlns, "meta"); s.attribute ("name", "viewport"); s.attribute ("content", "device-width, initial-scale=1"); s.end_element (); } }; static const head_element HEAD; struct css_style_element: element { css_style_element (): element ("style") {} virtual void start (xml::serializer& s) const { s.start_element (xmlns, name); s.attribute ("type", "text/css"); } }; static const css_style_element CSS_STYLE; static const element BODY ("body"); static const element DATALIST ("datalist"); static const element DIV ("div"); static const element FORM ("form"); static const element H1 ("h1"); static const element H2 ("h2"); static const element H3 ("h3"); static const element H4 ("h4"); static const element H5 ("h5"); static const element H6 ("h6"); static const element LI ("li"); static const element LINK ("link"); static const element META ("meta"); static const element OPTION ("option"); static const element P ("p"); static const element PRE ("pre"); static const element SCRIPT ("script"); static const element SELECT ("select"); static const element TABLE ("table"); static const element TBODY ("tbody"); static const element TD ("td"); static const element TH ("th"); static const element TITLE ("title"); static const element TR ("tr"); static const element UL ("ul"); static const inline_element A ("a"); static const inline_element B ("b"); static const inline_element BR ("br"); static const inline_element CODE ("code"); static const inline_element EM ("em"); static const inline_element I ("i"); static const inline_element INPUT ("input"); static const inline_element SPAN ("span"); static const inline_element U ("u"); // Attributes. // static const attribute AUTOFOCUS ("autofocus"); static const attribute CLASS ("class"); static const attribute CONTENT ("content"); static const attribute HREF ("href"); static const attribute ID ("id"); static const attribute LIST ("list"); static const attribute NAME ("name"); static const attribute REL ("rel"); static const attribute PLACEHOLDER ("placeholder"); static const attribute SELECTED ("selected"); static const attribute STYLE ("style"); static const attribute TYPE ("type"); static const attribute VALUE ("value"); } } #endif // WEB_XHTML_HXX