Rule Spec
=========

**Note:** This document was written before the code, and needs to be corrected (it's close to accurate, though).

Selectors
---------

Rules use selectors.  Selectors look like::

  type:selector1 || selector2 || ...

The type is optional, and defaults to "elements:" for the content and "children:" for the theme.  It can be one of:

  elements: select the elements (also "element"?  E.g., theme must always select a single element)
  children: select the children of the element (including its text)
  attributes: select the attributes of an element
  tag: select the "tag", but not its children
  attributes(attr1,attr2): select just the named attributes

Because CSS selectors and types overlap in general syntax, only these specific strings are caught.  Strings like ``div:first-child`` are valid CSS selectors, and because ``div:`` is not one of these prefixes it won't be interpreted as a selector type.

The selectors can start with a /, in which case it is an XPath expression, otherwise it is interpreted as a CSS selector.

Multiple selectors can be given with ||.  Each selector can be CSS or XPath.  The first selector that returns any results is used, and all later selectors are ignored.

Some types are invalid in some positions; this will be noted later on.



Actions
-------

These are the available actions:

  replace: replace the theme with the content
  append: append the content to the theme
  prepend: prepend the content with the theme
  drop: drop elements (from theme or content)

drop is the odd one out.

For replace, append, and prepend these attributes are required:

  theme: a selector for the theme
  content: a selector for the content

These are optional:

  ``href``: a place to find the "content" for this rule.  You can use this to include external content.

  ``if-content``: a selector; if the selector matches anything then the rule will be run.  If not the rule will be skipped.  No types (elements:, etc) are allowed for this, but you may prefix "not " before the selector.

  ``notheme``: one of "ignore", "abort", "warn", where the default is "warn".  This is the error action if the theme selector doesn't match anything.

  ``nocontent``: like notheme.

  ``manytheme``: if the theme selector matches more than one element, then this is used to decide the action.  The default is "warn:first".  If you use "warn:", then a warning will given.  You can also use "abort" and "ignore", and "first" or "last".  E.g., manytheme="ignore:last", then the last element selected will be used, and no warning given.  If you give just "first" or "last", then ignore: is presumed.  "abort" by itself aborts the theming.
  ``manycontent``: like manytheme, but for those (few) instances when the content selector must match a single element.

  ``move``: contains "0" or "1" (default "1").  If "1", then the elements are removed from the content.  You can use this to take pieces out of the content; otherwise if an inner element and outer element from the content are both copied in, then the inner element will show up twice.

For drop:

  theme *or* content: one of these selectors is allowed, but not both.

Optional attributes for drop:

  ``if-content``, ``notheme``, ``nocontent``

Behavior: replace
~~~~~~~~~~~~~~~~~

Replace replaces the theme with the content.  The theme selector should identify a single element.

theme=children, content=elements:

    The theme element is cleared of any content and text, and replaced with the content elements.

theme=elements, content=elements:

    This element is removed, and the content element(s) are put in the same location.

theme=elements, content=children:

    The theme element is removed, and replaced with all the children of the content elements (including text).

theme=children, content=children:

    The theme element is cleared and replaced with the children of the content elements.

theme=attributes, content=attributes:

    The theme element has all its attributes cleared, and replaced with the attributes of the content.

    If the theme is attributes, the content must also be attributes (and vice versa).

theme=tag, content=tag:

    Both the theme and content selector must match a single element.  The theme tag is removed and replaced with the content tag, but the theme's children are moved into the content tag.

    Both theme and content must be tag.


Behavior: append/prepend
~~~~~~~~~~~~~~~~~~~~~~~~

Both these rules work very similarly.

theme=children, content=elements:

    The theme (a single element) has the content elements appended after all the theme's children.  prepend puts the children of the contents at the start of the theme element, in their original order.  For prepend any leading text in the theme goes after the children elements.

theme=elements, content=elements:

    The theme (a single element) has all the elements from the content put immediate after the theme element.  prepend puts the content elements immediately before the element, but the content elements themselves retain their original order.

theme=elements, content=children:

    The theme element has the children of the content elements appended after it.

theme=children, content=children:

    The theme element has the children of the content appended or prepended before its children.

theme=attributes, content=attributes:

    The content attributes are added to the theme attributes.  When attributes overlap, the content attribute is dropped when using append.  With prepend, the theme attribute is dropped.  The class attribute is a special case: these are combined (space-separated).

theme=tag, or content=tag:

    Disallowed.

Behavior: drop
~~~~~~~~~~~~~~

You may provide either a theme selector, to drop theme elements, or a content selector to drop content elements.

elements:

    Drop the elements.  (Doesn't include tail text)

children:

    Clear the elements of their children (including contained text).

attributes:

    Clear the elements' attributes.

tag:

    Remove the element tags, but keep their children in place.



Examples
--------

Here are some examples, explained in English then the rule(s) given:

You want to add the class of the content's body element to the class of the resulting page:

.. code-block:: xml

  <append content="attribute(class):body"
          theme="attribute:body" />

Note that the "class" in "attribute(class):" is only required in one of the selectors -- when given in one and not the other, it is assumed they are intended to match.



You want to take content from one of several places (different products put it in different locations).  In all cases it is merged into a single theme element:

.. code-block:: xml

  <replace content="#portal-content || children:body"
           theme="children:#content" nocontent="abort" />

Here we want children in one case, but not the other case.  With || it should be possible to mix in that case (though it only makes sense to mix elements and children).



You want to copy over content CSS, except for one annoying file:

.. code-block:: xml

  <drop content="link[href$='/bad-style.css']" nocontent="ignore" />
  <append theme="children:head" content="link[rel='stylesheet']" />

I wonder if nocontent/notheme should default to "ignore" for drop?  

FIXME: In this case the behavior when nothing is matched is obvious (just don't do anything).



You want to copy over the title from the content, but if the content has no title then use the title from the theme:

.. code-block:: xml

  <drop theme="title" if-content="title" />
  <append theme="children:head" content="title" nocontent="ignore" />

You want to copy over the title from the content.  The theme looks like ``<title> | Site Name</title>``, and you want to keep ``| Site Name`` as the tail of the title:

.. code-block:: xml

  <prepend theme="children:title" content="children:title" />



You want to copy the login button from the content to a known location of the theme.  It might be a login, or it might be the username.

.. code-block:: xml

  <replace theme="#place-for-login"
   content="form[action*='login'] || div.user-info"
   nocontent="ignore" />



The content has a class that uses a CSS style that breaks out of the theme.  You want to just get rid of that class:

.. code-block:: xml

  <drop content="attributes(class):.stupid-class"
        nocontent="ignore" />



You want to copy all the items with class .one and all the items with class .three, preserving their order:

.. code-block:: xml
  
  <replace content=".one, .three"
  	   theme="div#place-to-put-them" />

