The Renderer objects
====================

Creating a renderer
-------------------

As described in :doc:`components`, the views of a component receives
a renderer. A renderer is a DOM objects factory that the developer uses to
build the DOM tree of the :doc:`view <presentation>`:

.. code-block:: python

     from nagare import presentation

     class App(object):
         pass

     @presentation.render_for(App):
     def render(self, h, *args):
         # the `h` parameter is the renderer, used to generate a DOM tree
         return h.div(h.h1('Hello world!'))

By default, each view receives a new XHTML renderer
(instance of :class:`nagare.namespaces.xhtml.Renderer`) suitable to build a
synchronous (X)HTML DOM tree.

A single view can also use a different renderer, for example to generate
another XML dialect than XHTML:

.. code-block:: python

     from nagare import presentation
     from nagare.namespaces import xml

     class App(object):
         pass

     @presentation.render_for(App):
     def render(self, h, *args):
         h.response.content_type = 'text/xml'

         x = xml.Renderer(h)    # Creating a XML renderer
         return x.contact(x.name('John Doe'), x.age(20))

or to generate an asynchronous view of a component:

.. code-block:: python

     from nagare import presentation, component
     from nagare.namespaces import xhtml
     from MyBlog import Blog

     class App:
         def __init__(self):
             self.content = component.Component(Blog())

     @presentation.render_for(App):
     def render(self, h, *args):
         # Creating an asynchronous XHTML renderer
         a = h.AsyncRenderer()
         return h.div(self.content.render(a))

.. note::

   The new renderer must be created with the current renderer as first parameter,
   to maintain a parent/child relationship between them.

Now, if all your views need to generate XML (or use any other renderer), it would be
cumbersome to create a renderer in each view. Instead, you can tell Nagare to use
another default renderer by setting the ``renderer_factory`` attribute of the
:doc:`WSGIApp <wsgiapp>`, as shown in the example below:

.. code-block:: python

    from nagare import wsgi
    from nagare.namespaces import xml

    class WSGIApp(wsgi.WSGIApp):
        # use the XML renderer by default
        renderer_factory = xml.Renderer

Then, all the views will receive an instance of ``xml.Renderer`` as renderer. Note that
you can still create another renderer in the view as explained above.


Default renderers
-----------------

XHTML renderer
~~~~~~~~~~~~~~

A XHTML renderer is an instance of :class:`nagare.namespaces.xhtml.Renderer`.

A XHTML renderer is a factory for all the possible XHTML tags. It also has:

- a ``request`` and a ``response`` attributes which are WebOb
  `request <https://webob.readthedocs.io/en/stable/reference.html#request>`_
  and
  `response <https://webob.readthedocs.io/en/stable/reference.html#response>`_
  objects.

- a ``head`` attribute which is a Head renderer instance, see below.

- the following methods:

  - ``parse_html(self, source, fragment=False, no_leading_text=False, xhtml=False)`` --
    to read HTML or XHTML (chosen with the ``xhtml`` parameter) from the
    ``source`` file to a DOM tree. If ``fragment`` is ``True``, the (X)HTML
    read can have multiple roots and the result will always be a list of DOM
    trees. Also, if ``fragment`` is ``True``, if ``no_leading_text`` is
    ``True`` and the (X)HTML begins by a text instead of a node, this text
    is removed.

  - ``parse_htmlstring(self, text, fragment=False, no_leading_text=False, xhtml=False)`` --
    as ``parse_html()`` but the (X)HTML is read from the ``text`` string.

Head renderer
~~~~~~~~~~~~~

The ``head`` attribute of a XHTML renderer is a Head renderer, an instance
of :class:`nagare.namespaces.xhtml.HeadRenderer`.

A head renderer is a factory for all the XHTML tags that can be embedded into
the ``<head>`` section: ``base``, ``head``, ``link``, ``meta``, ``title``,
``style`` and ``script``:

.. code-block:: python

     from nagare import presentation

     class App:
         pass

     @presentation.render_for(App):
     def render(self, h, *args):
         # h.head is a Head renderer
         h.head << h.head.title('My Application')

         return h.div(h.h1('Hello world !'))

A head renderer has also the following method:

- ``css(self, name, style)`` -- add a css style definition. The style must
  be named so that, if added by several views, it will be included only
  once in the generated page.

  .. code-block:: python

     @presentation.render_for(App):
     def render(self, h, *args):
         h.head.css('main_content', '.main { border: 1px red solid }')

         return h.div(h.h1('Hello world!'), class_='main')

- ``css_url(self, url)`` -- add a css url. If the url is relative, it's
  relative to the ``static`` directory of the :doc:`application <configuration_file>`.

  .. code-block:: python

     @presentation.render_for(App):
     def render(self, h, *args):
         h.head.css_url('http://www.nagare.org/site.css') # Absolute URL
         h.head.css_url('css/my_application.css')         # Relative URL

         return h.div(h.h1('Hello world!'), class_='main')

- ``javascript(self, name, script)`` -- add a javascript definition.
  The javascript code must be named so that, if added by several views, it
  will be included only once in the generated page.

  .. code-block:: python

     @presentation.render_for(App):
     def render(self, h, *args):
         h.head.javascript('debug', 'function debug(msg) { alert(msg) }')

         return h.div(h.h1('Hello world!'), class_='main')

- ``javascript_url(self, url)`` -- add a javascript url. If the url is
  relative, it's relative to the ``static`` directory of the
  :doc:`application <configuration_file>`

  .. code-block:: python

     @presentation.render_for(App):
     def render(self, h, *args):
         h.head.javascript_url('http://www.nagare.org/anim.js') # Absolute URL
         h.head.javascript_url('js/debug.js')                   # Relative URL

         return h.div(h.h1('Hello world!'), class_='main')

After the rendering phase, Nagare generates a ``<head>`` section which is the
concatenation of all the DOM objects the different views have put into the
Head renderer.

XHTML5 renderer
~~~~~~~~~~~~~~~

A XHTML5 renderer is an instance of :class:`nagare.namespaces.xhtml5.Renderer`.

A XHTML5 renderer is a factory for all the possible HTML5 or XHTML5 tags from
the `HTML5 specification`_.

Since it's extremely likely that you want to use this renderer in all the views
of your application instead of the default (X)HTML renderer, you can install it
like this:

.. code-block:: python

    from nagare import wsgi
    from nagare.namespaces import xhtml5

    class WSGIApp(wsgi.WSGIApp):
        # use the (X)HTML5 renderer by default
        renderer_factory = xhtml5.Renderer

Then, all the views will receive a (X)HTML5 renderer by default.

This renderer is like an XHTML renderer except that the set of available tags and
attributes is a little different. For example, HTML5 offers ``<audio>``, ``<video>``
and ``<canvas>`` tags and more structural markup such as ``<section>``, ``<article>``,
``<aside>``, ``<nav>``, ``<figure>``, ``<header>`` and ``<footer>`` tags.

.. _`HTML5 specification`: http://www.w3.org/TR/html5/

XML renderer
~~~~~~~~~~~~

A XML renderer is an instance of :class:`nagare.namespaces.xml.Renderer`.

It's a simple renderer which accepts any tag creation:

.. code-block:: pycon

     >>> from nagare.namespaces import xml

     >>> x = xml.Renderer()
     >>> tree = x.contact(x.name('John Doe'), x.age(20))

     >>> tree.write_xmlstring()
     '<contact><name>John Doe</name><age>20</age></contact>'

XSL renderer
~~~~~~~~~~~~

A `XSL <http://www.w3.org/TR/xslt>`_ renderer is an instance of
:class:`nagare.namespaces.xsl.Renderer` and has a factory for
all the possible XSL tags.

ESI renderer
~~~~~~~~~~~~

A `ESI <http://www.w3.org/TR/esi-lang>`_ renderer is an instance of
:class:`nagare.namespaces.esi.Renderer` and has
a factory for all the possible ESI tags :  ``attempt``, ``choose``, ``comment``,
``include``, ``inline``, ``otherwise``, ``remove``, ``try``, ``vars``, ``when``.

Namespaces and renderers mixin
------------------------------

A renderer can create DOM object in a specific
`XML namespace <http://www.w3.org/TR/REC-xml-names/>`_. First the
namespace(s) must be declared to the renderer by setting its ``namespaces``
attribute to a *namespace prefix* -> *namespace uri* dictionary. Then, at any
moment, the namespace to use is choosen by setting its ``default_namespace``
atttribut to the prefix of the namespace:

.. code-block:: pycon

   >>> from nagare.namespaces import xsl

   >>> x = xsl.Renderer()
   >>> x.namespaces = {'xsl': 'http://www.w3.org/1999/XSL/Transform' }
   >>> x.default_namespace = 'xsl'

   >>> xsl = x.stylesheet(
   ...           x.output(encoding='utf-8'),
   ...           x.template(
   ...               x.copy_of(select='.'),
   ...               match='/'
   ...           )
   ...       )

   >>> print xsl.write_xmlstring(pretty_print=True)
   <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output encoding="utf-8"/>
     <xsl:template match="/">
       <xsl:copy-of select="."/>
     </xsl:template>
   </xsl:stylesheet>

Different renderers, with different namespaces, can be combined to build
a DOM tree:

.. code-block:: pycon

   >>> from nagare.namespaces import xhtml, esi

   >>> h = xhtml.Renderer()

   >>> s = esi.Renderer()
   >>> s.namespaces = {'esi': esi.NS }
   >>> s.default_namespace = 'esi'

   >>> tree = h.html(
   ...            h.body(
   ...                h.h1('Hello'),
   ...                s.include(src='http://www.nagare.org'),
   ...                h.p('world')
   ...            )
   ...        )
   >>> print tree.write_xmlstring(pretty_print=True)
   <html>
     <body>
       <h1>Hello</h1>
       <esi:include xmlns:esi="http://www.edge-delivery.org/esi/1.0" src="http://www.nagare.org"/>
       <p>world</p>
     </body>
   </html>

