.. _library_html:

``html``
========

This library provides predicates for generating HTML content using
either HTML 5 or XHTML 1.1 formats from a term representation. The
library performs minimal validation, checking only that all elements are
valid. No attempt is made to generate nicely indented output.

Normal elements are represented using a compound term with one argument
(the element content) or two arguments (the element attributes
represented by a list of ``Key=Value`` or ``Key-Value`` pairs and the
element content). The element content can be another element or a list
of elements. For example:

::

   ol([type-a], [li(foo), li(bar), li(baz)])

The two exceptions are the ``pre`` or ``code`` elements whose content is
never interpreted as an element or a list of elements. For example, the
fragment:

::

   pre([foo,bar,baz])

is translated to:

::

   <pre>
   [foo,bar,baz]
   </pre>

Void elements are represented using a compound term with one argument,
the (possibly empty) list of attributes represented by a list of
``Key=Value`` or ``Key-Value`` pairs. For example:

::

   hr([class-separator])

Atomic arguments of the compound terms are interpreted as element
content. Non-atomic element content can be represented as a quoted atom
or by using the ``pre`` or ``code`` elements as explained above.

This library is a work in progress.

API documentation
-----------------

Open the
`../../apis/library_index.html#html <../../apis/library_index.html#html>`__
link in a web browser.

Loading
-------

To load all entities in this library, load the ``loader.lgt`` file:

::

   | ?- logtalk_load(html(loader)).

Testing
-------

To test this library predicates, load the ``tester.lgt`` file:

::

   | ?- logtalk_load(html(tester)).

Generating a HTML document
--------------------------

HTML documents can be generated from a compound term representation and
written to a file or a stream. For example, assuming we want to generate
a HTML 5 file:

::

   | ?- html5::generate(
            file('hello.html'),
            html([lang=en], [head(title('Hello world!')), body(p('Bye!'))])
        ).

When the second argument is a ``html/1`` or ``html/2`` compound term, a
*doctype* is automatically written. If we prefer instead e.g. a XHTML
1.1 document, we use the ``xhtml11`` object:

::

   | ?- xhtml11::generate(
            file('hello.html'),
            html([lang=en], [head(title('Hello world!')), body(p('Bye!'))])
        ).

Generating a HTML fragment
--------------------------

It's also possible to generate just a fragment of a (X)HTML document by
using a list of compound terms or a compound term for an element other
than ``html``. For example:

::

   | ?- current_output(Stream),
        html5::generate(stream(Stream), ul([li(foo), li(bar), li(baz)])).

   <ul>
   <li>
   foo</li>
   <li>
   bar</li>
   <li>
   baz</li>
   </ul>

   Stream = ...

Working with callbacks to generate content
------------------------------------------

Often we need to programmatically generate HTML content from queries. In
other cases, we may have fixed content that we don't want to keep
repeating (e.g., a navigation bar). The library supports a ``(::)/2``
pseudo-element that sends a message to an object to retrieve content. As
an example, assume the following predicate definition in ``user``:

::

   content(strong('Hello world!')).

This predicate can then be called from the HTML term representation. For
example:

::

   | ?- current_output(Stream),
        html5::generate(stream(Stream), span(user::content)).

   <span><strong>Hello world!</strong></span>

   Stream = ...

Note that the callback always takes the form ``Object::Closure`` where
``Closure`` is extended with a single argument (to be bound to the
generated content). More complex callbacks are possible by using lambda
expressions.

Working with resource declarations
----------------------------------

The renderer accepts explicit resource declarations in addition to the
usual HTML elements. The supported resource declarations are:

- ``resource(Type, Target)``
- ``resource(Type, Target, Attributes)``
- ``css(Href)``
- ``js(Src)``
- ``resources(Declarations)``
- ``dependency(Dependent, Required)``

Here, ``Type`` is ``css`` or ``js``, ``Target`` is the stylesheet or
script location, ``Attributes`` is an optional list of HTML attributes
added to the generated ``<link>`` or ``<script>`` element, ``Href`` and
``Src`` are shorthand forms for the corresponding stylesheet or script
target, ``Declarations`` is a list of resource specifications to
aggregate, and ``dependency(Dependent, Required)`` expresses that the
resource ``Dependent`` must be emitted after ``Required`` when ordering
the generated output.

The atom ``stylesheet`` can also be used as an alternative to ``css``.
The atoms ``javascript`` and ``script`` can also be used as an
alternative to ``js``.

For example, the following term renders standard stylesheet and
JavaScript links directly:

::

   | ?- current_output(Stream),
        html5::generate(stream(Stream),
            resources([
                css('base.css'),
                resource(js, 'app.js', [defer-true])
        ])).

This produces ``<link rel="stylesheet" href="base.css" />`` and
``<script defer src="app.js"></script>``-style output.

Aggregating and ordering resources
----------------------------------

A list of resource declarations can be wrapped with ``resources/1`` to
normalize and aggregate them into a deterministic output order. Repeated
entries are removed automatically, and
``dependency(Dependent, Required)`` can be used to force a resource to
be emitted after its prerequisite.

For example:

::

   | ?- current_output(Stream),
        html5::generate(stream(Stream),
            resources([
                dependency(resource(css, 'theme.css'), resource(css, 'base.css')),
                css('theme.css'),
                css('base.css'),
                css('theme.css')
        ])).

This renders the ``base.css`` stylesheet before ``theme.css``, while
also removing the duplicated ``theme.css`` entry from the final output.

Working with custom elements
----------------------------

The ``html5`` and ``xhtml11`` objects recognize the same set of standard
HTML 5 normal and void elements and generate an error for non-standard
elements. If you need to generate HTML content containing custom
elements, define a new object that extends one of the library objects.
For example:

::

   :- object(html5custom,
       extends(html5)).

       normal_element(foo, inline).
       normal_element(bar, block).
       normal_element(Name, Display) :-
           ^^normal_element(Name, Display).

   :- end_object.
