HTMLTemplate
0.4.3

----------------------------------------------------------------------
SUMMARY

A fast, powerful, easy-to-use HTML templating engine.

----------------------------------------------------------------------
DESCRIPTION

HTMLTemplate converts HTML/XHTML templates into simple Python object models that can be manipulated via callback functions in your scripts.


======= About HTML Templates =======

An HTML template is usually a complete HTML/XHTML document, though it may also be a fragment of one - HTMLTemplate doesn't require the document be complete, nor even that it has a single root element.

To create a Template object model, selected HTML elements must be annotated with 'compiler directives', special tag attributes that indicate how the object model is to be constructed. Here are some examples:

	<h1 node="con:title">Welcome</h1>

	<img node="con:photo" src="" />

	<a node="rep:navlink" href="#">Catalogue</a>

	<span node="-sep:navlink"> | </span>

	<div node="del:"> ... </div>


One restriction does apply when authoring templates: the template's HTML elements must be correctly closed according to XHTML rules, as in:

	<p>Hello World</p>
	<hr />

and not:

	<p>Hello World
	<hr>


======= Compiler Directives =======

HTMLTemplate defines four types of compiler directive:

- 'con' defines a Container node that can appear only once at the given location
- 'rep' defines a Repeater node that can appear any number of times
- 'sep' defines a separator string to be inserted between each iteration of a Repeater object of the same name
- 'del' indicates a section of markup to be omitted in the compiled template.

The special attribute name can be anything (the default is 'node' but any other name may be specified via the Template constructor), and its values are typically of form "FOO:BAR", where FOO is a three-letter code indicating the type of directive and BAR is the name of the node to create. Every node name must be a valid Python identifier with two additional restrictions: 1. it cannot begin with an underscore, and 2. it cannot match the name of any method or property belonging to the Container, Repeater and Template classes. (Note: the 'del' directive doesn't create a named node, so may simply be written as "del:") Directive types and node names are both case-sensitive.

HTMLTemplate also supports a single directive modifier: '-' (aka the 'minus tags' modifier). When prepended to the directive type (e.g. "-con:foo") this indicates that the HTML element's tags should be omitted in the compiled node/separator string. Use this modifier when adding an arbitrary HTML element (typically <div> or <span>) to an HTML template purely to construct a node or separator string to prevent the rendered page being cluttered with the leftover tags.


======= The Template Object Model =======

The HTMLTemplate object model is really just a greatly simplified, highly specialised variation of the standard DOM, in which only specified HTML elements can be manipulated via a very compact, simple API that's designed specifically for templating.

The Template object model is constructed from three classes: Template, Container and Repeater.

The Template object forms the template's root node, representing the complete HTML document. Contains one or more Container/Repeater child nodes, and implements the render() method used to generate finished pages.

A Container object represents a modifiable HTML element. e.g.:

	<title node="con:pagetitle">...</title>

The HTML element may be empty - e.g. '<br />' - in which case it has no content and only its tag attributes are modifiable, or non-empty - e.g. '<p>...</p>' - in which case it may contain either modifiable content (plain text/markup) or other Container/Repeater nodes. The standard Container node has a one-to-one relationship with its parent node, appearing only once [1] when rendered.

A Repeater node is a Container node that has a one-to-many relationship with its parent node, appearing zero or more times - once for each item [1] in the collection being iterated by its repeat() method. e.g.:

	<ul>
		<li node="rep:listitem">...</li>
	</ul>


The Repeater class's repeat() method is roughly analogous to Python's built-in map() function, except that it passes the object to be modified as one of its arguments and doesn't return a result. For example, the call:

	myRepeaterNode.repeat(callback, [1, 2, 3, 4, 5], *args)

will call the given callback function five times, each time passing it a copy of myRepeaterNode and an item from the given list, as well as any additional arguments supplied by the user. The callback function can then manipulate the supplied Repeater object, inserting data into the HTML element's tag attributes and/or content, or modifying its child nodes, or calling its omit() method to prevent that instance of the Repeater from being rendered in the finished page.


-------

[1] Except when the object's omit() method is called, in which case the HTML element it represents is omitted from the finished page.


======= Controlling Template Rendering =======

Template rendering is controlled by a user-defined function, typically named 'renderTemplate', that's attached to the Template object as it's created and triggered automatically each time the Template object's render() method is called.

When the Template's render() method is called, the Template object calls its attached renderTemplate function, passing it a copy of itself along with any additional arguments passed via the render() call. This allows the renderTemplate function to manipulate this object model - inserting the user-supplied data into nodes as tag attributes and content, omitting unwanted sections, even rearranging the object model itself(!). Once the renderTemplate function returns, the now-modified object model is rendered to text and returned.

It's also possible to manipulate the Template object model directly, prior to calling its render() method. This can be useful if you have some data you want to appear in every rendered page but don't wish to re-render it each time for efficiency's sake.


======= Compiling a Template =======

A single Template object can be used to render any number of pages.

To compile a template, create a new instance of HTMLTemplate's Template class with the main callback function and the HTML text as arguments:

	template = HTMLTemplate.Template(renderTemplate, html)

Two optional arguments may additionally be provided:

- node -- The name of the tag attributes used to hold compiler directives. The default is 'node', but may be changed to any other valid attribute name; e.g. 'id', 'obj', 'foo:bar'. This may be useful if you have to edit your templates in a program that rejects non-valid HTML attribute names such as 'node' (e.g. use 'id' instead), or if you wish to define a meta-template that generates other templates (e.g. use 'metanode' and 'node' to distinguish between compiler directives belonging to the meta-template and those intended for the generated template).

- codecs -- Allows the default HTML entity encoding/decoding functions to be replaced. These functions are applied when getting or setting the value of a Container or Repeater node's content property. By default, only the four markup characters, <>&" are converted. This minimal level of conversion is provided for security's sake, but you may want to replace these functions with your own if you need also to escape non-ASCII or other characters as standard.


----------------------------------------------------------------------
CLASSES

Node -- Abstract base class
	Properties:
		NAME : Container | Repeater -- a (child) node defined by the source HTML template ('NAME' = the node's name)



Attributes -- A simple dict-like structure containing an HTML tag's attributes; supports getting, setting and deleting of attributes by name, e.g. node.atts['href'] = 'foo.html'



Container(Node) -- A mutable HTML element ('con')
	Properties:
		atts : Attributes -- the tag's attributes

		content : string -- the HTML element's content with &<>" characters automatically escaped (note: when inserting raw HTML, use the raw property instead) [1]

		raw : string -- the HTML element's raw content (i.e. no automatic character escaping) [1]

	Methods:
		omit() -- don't render this node

		omittags() -- don't render this node's tags (only its content)



Repeater(Container) -- A mutable, repeatable HTML element ('rep')
	Methods:
		repeat(fn, sequence, *args) -- render an instance of this node for each item in sequence
			fn : function -- the function to call for each item in list [2]
			sequence : anything -- a list, tuple, or other iterable collection
			*args : anything -- any values to be passed directly to this node's callback function



Template(Node) -- The top-level template node
	Methods:
		__init__(self, callback, html, attribute='node', codecs=(defaultEncoder, defaultDecoder))
			callback : function -- the main function controlling template rendering [3]
			html : string or unicode -- the HTML template
			[attribute : string or unicode] -- name of the tag attribute used to hold compiler directives
			[codecs : tuple] -- a tuple containing two functions used by the content property to encode/decode HTML entities [4]

		render(*args) -- render this template
			*args : anything -- any values to be passed directly to this template's callback function 
		
		structure() -- print the object model's structure for diagnostic use



ParseError : (inherits from Exception) A template parsing error


-------

[1] The content and raw properties can only be used when the Container/Repeater object is derived from a non-empty HTML element containing plain text/markup only. If the HTML element is empty (e.g. <br />) or contains any child nodes, the operation is ignored/an AttributeError occurs.

[2] The Repeater's callback function must accept the following arguments: 
	node : instance -- a copy of the Repeater object
	item : anything -- an item from the sequence being iterated
	*args : anything -- zero or more additional parameters corresponding to any extra arguments passed by the user to the Repeater's repeat() method

[3] The Template's callback function must accept the following arguments: 
	node : instance -- a copy of the Template object
	*args : anything -- zero or more additional parameters corresponding to any extra arguments passed by the user to the Template's render() method

[4] The default codec functions encode/decode the four standard markup characters: &<>". When supplying your own, both replacement functions should accept and return a single string/unicode value. The first function should convert specified characters into HTML entities; the second should perform the reverse operation.

----------------------------------------------------------------------
EXAMPLES

Bundled examples:

- Demo1_Quote.py
- Demo2_Table.py
- Demo3_Links.py
- Demo4_SimpleCalendar.py

Other scripts (by same author):

- HTMLCalendar module
- appscript.htmldoc.Renderer sub-module
- iTunes_albums_to_HTML.py script

----------------------------------------------------------------------
NOTES

======= Template design tips =======

- Where two or more sibling nodes share the same type and name, only the first is included in the compiled template and the rest discarded. (If two or more sibling nodes share the same name but have different types, then unless the first node is type 'rep' and the other of type 'sep' a ParseError will occur.)

- The parser automatically removes the special attribute from any element it converts into a template node. Tag attributes whose name is the same as that used for special attributes but whose value isn't a recognised compiler directive are treated are left unchanged.

- Separators cannot be declared before the Repeater nodes they belong to.

- When authoring a template, you'll sometimes want to group two or more adjacent nodes so they can be repeated as a single block. If the HTML doesn't already contain a suitable parent element to add the 'rep' compiler directive to, insert an extra <div> or <span> element that wraps these nodes and add the 'rep' directive to convert it to create your Repeater node. You can the use the 'minus tags' modifier to omit this element from the rendered page. The Tutorials.txt file covers this technique in more detail.


======= Controller design tips =======

- When setting a node's content, make sure you write:

	node.foo.content = val

not:

	node.foo = val

The first assigns val as the node's content, the second replaces the node itself.


======= Template rendering tips =======

- The attributes property, atts, performs basic validation of user-supplied attribute names and values for security:
	- An attribute's name must match the pattern '^[a-zA-Z_][-.:a-zA-Z_0-9]*$'
	- An attribute's value may not contain both single and double quotes. 

- While HTMLTemplate will single/double-quote attribute values as appropriate, it won't perform any special encoding of values. Any attribute value encoding is left to the user's code, e.g. using urllib's quote() and unquote() functions.


======= Miscellaneous notes =======

- The public class structure shown in this documentation is slightly simplified from the actual (multiple inheritance-based) implementation to make it easier to understand. This is not something end users should worry about.

----------------------------------------------------------------------
KNOWN PROBLEMS

- Jarek Zgoda reports that Python's HTMLParser module expands the following entity references where they appear in tag attributes values: &amp; &lt; &gt; &quot; - this is probably a bug. When defining HTML templates, use the equivalent character references - &#38; &#60; &#62; &#34; - within attribute values as these are not affected. Note that entity references appearing within HTML elements' content are not affected by this problem, nor are values inserted during template rendering (which are already subject to their own escaping rules).

- HTMLParser module automatically lowercases all tag and attribute names. This shouldn't present any problems for templating HTML (which is case-insensitive by nature) nor for templating XHTML (which is all-lowercase anyway), but will cause problems for any ad-hoc XML templating where tags and attributes contain both lower and uppercase characters.

----------------------------------------------------------------------
TO DO

- see if there'd be a more helpful (but still agnostic) way to handle attribute value encoding/decoding then the current 'hands-off' approach
- find out if Templates are fully re-entrant (i.e. thread-safe); if so, list this as a feature (reminding users they'll have to make their callback functions thread-safe themselves)
- edit manual
- finish tutorials, FAQ
- get user feedback
- final tests
- 1.0.0 release

----------------------------------------------------------------------
HISTORY

2004-05-18 -- 0.4.3; fixed minor design flaw in Repeater._clone() that prevented rendered items from showing up when repeat() method is called from outside its callback handler (e.g. in HTMLCalendar's MonthCal.__init__() method).

2004-05-03 -- 0.4.2; documented a couple of issues found in Python 2.3's HTMLParser module that affect HTMLTemplate (thanks Jarek!)

2004-05-02 -- 0.4.1; fixed a stupid bug in template parser's handle_charref() method (thanks Jarek!)

2004-04-26 -- 0.4.0; redesigned the API, execution model and documentation to make it simpler and easier to understand. (Note: scripts written for older versions of HTMLTemplate will require some modifications to use the new API.)

----------------------------------------------------------------------
AUTHORS

- HAS <hamish.sanderson@virgin.net>

----------------------------------------------------------------------
CREDITS

- Many thanks to Bud P Bruegger, Ronald van Engelen, Matthias Fiebig, Tomas Jogin, Simon Willison and Jarek Zgoda for comments, suggestions and bug reports.

----------------------------------------------------------------------
COPYRIGHT

HTMLTemplate - A fast, powerful, easy-to-use HTML templating engine.

Copyright (C) 2004 HAS <hamish.sanderson@virgin.net>

This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA