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

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

As described in :wiki:`ComponentModel`, the views of a component received
a renderer. A renderer is a DOM objects factory that the developer uses to
build the DOM tree of the view (see :wiki:`PresentationTier`):

  .. code-block:: python

     from nagare import presentation

     class App:
         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 views receive a new XHTML renderer
(instance of the ``Renderer`` class of
:apidoc:`namespaces.xhtml#xhtml.Renderer`) suitable to build a
synchronous (X)HTML DOM tree.

A view can also create a different renderer, for example to generate an other
XML dialect than XHTML:

  .. code-block:: python

     from nagare import presentation
     from nagare.namespaces import xml

     class App:
         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 = xhtml.AsyncRenderer(h)
         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.

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

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

A XHTML renderer is an instance of the ``Renderer`` class of
:apidoc:`namespaces.xhtml#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 and
    Response <http://pythonpaste.org/webob/reference.html>`_ 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 (choosen 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 the class ``HeadRenderer`` of
:apidoc:`namespaces.xhtml#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 application
    (see :wiki:`ApplicationConfiguration`)

    .. 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 application
    (see :wiki:`ApplicationConfiguration`)

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

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

A XML renderer is an instance of the ``Renderer`` class of
:apidoc:`namespaces.xml#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 the ``Renderer``
class of :apidoc:`namespaces.xsl#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 the
``Renderer`` class of :apidoc:`namespaces.esi#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 combinated 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>

.. wikiname: RendererObjects
