// 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