--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/qmsk/web/html.py Sat Jun 07 16:21:39 2014 +0300
@@ -0,0 +1,188 @@
+from qmsk.utils import flatten, merge
+from html import escape
+
+import qmsk.web
+
+class Tag :
+ def __init__ (self, _name=None, *_contents,
+ _selfclosing=True,
+ _whitespace_sensitive=False,
+ **_attrs) :
+ self.name = _name
+ self.contents = _contents or [ ]
+ self.attrs = _attrs or { }
+
+ # options
+ self.selfclosing = _selfclosing
+ self.whitespace_sensitive = _whitespace_sensitive
+
+ def __call__ (self, *_contents,
+ _selfclosing=None,
+ _whitespace_sensitive=None,
+ **_attrs):
+ return type(self)(self.name, *tuple(flatten(self.contents, _contents)),
+ _selfclosing = self.selfclosing if _selfclosing is None else _selfclosing,
+ _whitespace_sensitive = self.whitespace_sensitive if _whitespace_sensitive is None else _whitespace_sensitive,
+ **merge(self.attrs, _attrs)
+ )
+
+ def render_attrs (self):
+ for name, value in self.attrs.items():
+ name = name.strip('_').replace('_', '-')
+
+ if value is True:
+ value = name
+ elif value is None:
+ continue
+ else:
+ value = str(value)
+
+ yield '{name}="{value}"'.format(name=name, value=escape(value, quote=True))
+
+ def render (self, indent):
+ """
+ Iterate over lines of output HTML.
+ """
+
+ open = (self.contents or not self.selfclosing)
+
+ if self.name and self.attrs and open:
+ yield indent, '<{name} {attrs}>'.format(name=self.name, attrs=' '.join(self.render_attrs()))
+ elif self.name and self.attrs:
+ yield indent, '<{name} {attrs} />'.format(name=self.name, attrs=' '.join(self.render_attrs()))
+ elif self.name and open:
+ yield indent, '<{name}>'.format(name=self.name)
+ elif self.name:
+ yield indent, '<{name} />'.format(name=self.name)
+
+ for item in self.contents:
+ if isinstance(item, Tag):
+ yield from item.render(indent=indent+1)
+ elif self.whitespace_sensitive:
+ yield 0, str(item)
+ else:
+ yield indent + 1, str(item)
+
+ if self.name and open:
+ yield indent, '</{name}>'.format(name=self.name)
+
+ def __str__ (self):
+ """
+ Render as HTML.
+ """
+
+ return '\n'.join('\t'*indent+line for indent, line in self.render(indent=0))
+
+ def __repr__ (self):
+ return '{name}({args})'.format(
+ name = self.__class__.__name__,
+ args = ', '.join(
+ [repr(self.name)]
+ + [repr(item) for item in self.contents]
+ + ['{name}={value!r}'.format(name=name, value=value) for name, value in self.attrs.items()]
+ ),
+ )
+
+class HTML:
+ pre = Tag('pre', _whitespace_sensitive=True)
+
+ def __getattr__ (self, name):
+ """
+ Get an empty Tag object.
+ """
+
+ return Tag(name)
+
+ def __call__ (self, *values):
+ """
+ Raw HTML.
+ """
+ return Tag(None, *values)
+
+class HTML5 (HTML):
+ span = Tag('span', _selfclosing=False)
+ script = Tag('script', _selfclosing=False)
+
+html5 = HTML5()
+
+class HTMLHandler (qmsk.web.Handler):
+ """
+ A handler that renders a full HTML page.
+ """
+
+ # HTML5
+ html = html5
+
+ DOCTYPE = 'html'
+ HTML_XMLNS = None
+ HTML_LANG = 'en'
+
+ # <head>
+ TITLE = None
+ STYLE = None
+ SCRIPT = None
+ CSS = (
+
+ )
+ JS = (
+
+ )
+ HEAD = None
+
+ def title (self):
+ return self.TITLE
+
+ def render (self):
+ raise NotImplementedError()
+
+ def render_html (self) :
+ """
+ Render HTML <html> tag.
+ """
+
+ html = self.html
+
+ return html.html(
+ html.head(
+ html.title(self.title()),
+ (
+ html.link(rel='Stylesheet', type="text/css", href=src) for src in self.CSS
+ ),
+ (
+ html.script(src=src, type='text/javascript', _selfclosing=False) for src in self.JS
+ ),
+ html.style(type='text/css')(self.STYLE) if self.STYLE else None,
+ html.script(type='text/javascript')(self.SCRIPT) if self.SCRIPT else None,
+ self.HEAD,
+ ),
+ html.body(
+ self.render(),
+ ),
+ xmlns=self.HTML_XMLNS, lang=self.HTML_LANG)
+
+ def render_response (self):
+ """
+ Render entire HTML response.
+ """
+
+ return """\
+<!DOCTYPE {doctype}>
+{html}\
+""".format(doctype=self.DOCTYPE, html=self.render_html())
+
+if __name__ == '__main__':
+ html = HTML5()
+
+ print(html.html(
+ html.head(
+ html.title("Testing")
+ ),
+ html.body(
+ html.h1("Testing"),
+ html.p("Just testing this..."),
+ html("Raw HTML <tags>"),
+ html.pre(repr(
+ html.a(href="/foo")("Foo!")
+ ))
+ ),
+ ))