This document is a reference guide for XUL templates. It assumes that you have a basic knowledge of the RDF data model, and a working knowledge of the XUL syntax. Of course, there is no better reference than the code, but that's probably impenetrable to just about everyone, especially the authors.
See also the XUL Template Primer for an overview of the extended template syntax. If you're new to XUL templates, the "primer" may be an better introduction to the basic concepts than this document.
XUL templates are a mechanism for translating data contained in an RDF graph into a XUL content model for presentation [1]. Using a XUL template, it is possible to specify a content "fragment" that will be copied in a cookie-cutter fashion to generate a content model from the nodes and arcs in an RDF graph.
Building content from RDF requires you specify several things:
You specify an element in the XUL which will "root" the RDF generated content. To this element, you attach an RDF composite data source that aggregates together the assertions from one or more individual RDF datasources. This makes the assertions contained in the composite datasource "visible" to the XUL content model generation engine.
Contained immediately within the datasource element you specify one or more templates. A template is a fragment of normal XUL and HTML content. The fragment isn't ever displayed in the content model, but instead is used to construct new content from the assertions in the RDF graph.
Appendix 1 contains two sample XUL templates, which may be useful to refer to while reading the drier portions of this document.
To specify the RDF graph from which content will be built, you add a datasources attribute to an element in your XUL file. This element is called the datasource element for the generated content.
The Datasource Element.
<tag datasources="rdf-datasource-list" [other attributes]> ... </tag> |
The datasources attribute creates a composite data source that is associated with the datasource element. You can access the composite datasource programatically via the database property of the element; e.g., from JavaScript,
var tree = document.getElementById('MyTree'); tree.database.AddDataSource(...);
You may attach a datasources attribute to any element (including HTML elements) within a XUL document.
You may attach a datasources attribute to any number of elements in a single XUL file. Each datasource element will is associated with its own composite datasource. The composite datasource in one datasource element may aggregate RDF datasources that "overlap" with another datasource element. Datasource elements may not currently be nested.
Specifying the RDF Datasources. The rdf-datasource-list is a whitespace-separated list of RDF datasource identifiers that are aggregated into the composite datasource. These must be of the following form:
Specifying RDF datasources.
rdf-datasource-list ::= rdf-datasource-id [rdf-datasource-id ...] rdf-datasource-id ::= rdf:rdf-contractid-suffix ("Built-in" form) rdf-datasource-id ::= URL ("Remote" form) |
If the rdf-datasource-id is a "built-in" datasource, it will load the component using the XPCOM service manager. The ContractID of the component will be constructed by appending the rdf-contractid-suffix to the prefix @mozilla.org/rdf/datasource;1?name=. The resulting component will be QueryInterface()'d for the nsIRDFDataSource interface. Note that because components are loaded as services; i.e., using nsServiceManager::GetService(), a single component may end up being shared across many content models..
If the rdf-datsource-id is a "remote" datasource, the URL will be assumed to refer to an RDF/XML file. An RDF/XML datasource will be created whose contents will be loaded from the specified URL. Note that the content may arrive asynchronously.
For example,
<tree datasources="rdf:mail rdf:bookmarks http://foo.com/sitemap.rdf"> ... </tree>
will create a XUL tree with a composite datasource that includes the two built-in datasources that will be loaded using the ContractIDs:
@mozilla.org/rdf/datasource;1?name=mail @mozilla.org/rdf/datasource;1?name=bookmarks
and the assertions from the remote RDF/XML file, which will be loaded from http://foo.com/sitemap.rdf.
Directly inside the datasource element, you specify the content model templates that will be used to construct content from the RDF graph. The content model template is specified inside a template tag, using one of the two forms, below:
Form 1. Simple
<template> template XUL </template>Form 2. Rule Match <template> <rule [match attributes]> template-XUL </rule> </template> |
The first form specifies a single template that should be used to build content. The second form allows specification of multiple templates from which one will be selected to generate content. See Content Generation, below, for details on the matching and generation process.
The template-XUL is the set of XUL, XML, and HTML tags that will be copied from the template into the content model. Template XUL must be of the following form:
Template XUL
<shared-XUL> <template-resource-element uri="rdf:*" [other attributes]> <per-resource-XUL> ... </pre-resource-XUL> </template-resource-element> </shared-XUL> |
The Template Resource Element. You use the uri="rdf:*" attribute/value pair to indicate which element will be the template resource element. The template resource element is the element that will correspond to a node in the RDF graph.
The template resource element will be automatically assigned an id attribute whose value will be the fully-qualified URI of the resource to which it corresponds in the graph. Any content "above" this node in the template will be shared by all of the "real" resource elements in a particular subtree; any content "below" this node in the template will be duplicated once for each "real" resource element (see below).
To apply the template, you must specify the URI of the node in the graph from which to begin building content. You do this by specifying a ref attribute the ``root'' node where that datasources attribute was specified. The ref attribute's value should refer to the URI of a resource in the graph. This element is called a resource element.
The URI of a resource element may be specified either in absolute or relative form. If specified as a relative URI, it will be resolved relative to the XUL document's URL. [TBD: Overlays]
Generation Once an appropriate template has been selected for a resource element, the template engine will copy the template's contents as children of the resource element as follows.
First, it will find the resource node in the RDF graph that corresponds to the resource element in the XUL content model.
Then, for each containment arc (described below) that leads out of the resource node into another node in the graph (the "child node"), it will construct content using the template:
For the shared XUL, the XUL engine will ensure that the content model "beneath" (i.e. contained within) the resource element (and above the child resource element) contains a common subtree copied from the template.
A child resource element will be created that corresponds to the child node in the RDF graph. The child resource element will be created from the template resource element (that is, the element in the template that has the uri="rdf:*" attribute). A distinct child resource element will be constructed for each child node in the RDF graph. Each child resource element will share the same shared XUL. See Sorting, below, for more information on how individual child resource elements will be ordered in relation to one another.
Beneath each child resource element, a copy of the per-resource XUL will be made. Inside the per-resource XUL, attribute substitution will be performed as described below.
Because each template application generates a new child resource element, XUL templates may be recursively applied to generate a tree-like content model.
Rule Matching. If more than one template is specified using the rule element, then the template engine will iterate through each rule to find the first rule whose match attributes are present as properties on the child node in the RDF graph. If a rule has no match attributes, it will automatically match.
The special match attributes iscontainer and isempty will be dynamically computed while matching templates. The iscontainer attribute will be matched by "looking ahead" in the RDF graph and examining the arcs that lead out of the child node. If the child node may potentially have its own resource children [2] (based on the currently active containment properties, see below), this attribute will be match as true. If any of the arcs actually refer to other nodes in the graph, then the isempty attribute will match as false; if not, it will match as true.
Attribute Substitution. Inside the per-resource XUL that is generated for each child resource element, the XUL engine will perform attribute substitution. Any attribute whose value is specified using the prefix rdf: is treated as a "property substitution". The rdf: prefix will be removed from the attribute's value, and the resulting string will be treated as a URI that names an RDF property. The content model builder will construct a value for this property by querying the RDF graph for the target of an assertion whose source is the resource that corresponds to the resource element and whose property corresponds to the named RDF property.
The special attributes container and empty will be set on the resource element based on whether or not the resource element may potentially have its own resource children, and whether or not it actualy has resource children, respectively. [2]
text Elements. A text element with a value attribute that specifies an rdf: property will be replaced by a simple text node in the content model.
Containment. The XUL engine determines a parent/child relationship between nodes in the RDF graph using a set of containment properties. The set of containment properties that are active at any point during content model generation are specified explicitly using the containment and ignores attributes.
Containment
<tag containment="containment-property-list"> ... </tag> containment-property-list ::= containment-property [containment-property ...] containment-property ::= URI |
The following properties are automatically assumed to be containment properties.
Automatic Containment Properties
http://home.netscape.com/NC-rdf#childIn addition, any RDF "ordinal" property (e.g., rdf:_1) is automatically considered to be a containment property. |
You can specify that a property be explicitly ignored for containment purposes using the ignores attribute.
The containment properties that are in effect at a particular point in content model generation are determined by finding the nearest ancestor with a containment attribute.
[TBD]
[1] Originally, we thought that it would be possibly to have a generic "graph-to-content" translation algorithm that, when combined with CSS2, could produce any appropriate content model (see this document). It quickly became clear that this was not the case.
[2]. Computing container and iscontainer works by calling nsRDFDatasource::ArcLabelsOut. If any of the properties returned by ArcLabelsOut is a containment property, then the element is annotated as a "container".ArcLabelsOut's semantics are defined such that it will return all of the potential arcs that may lead out of the specified resource. In other words, a property may be returned that does not actually participate in any assertions whose subject is the specified resource.
The motivation for this somewhat bizarre behavior is as follows. An element in the content model may need to be annotated as a "container", even though it doesn't currently contain any elements; e.g., an empty folder in a file system, an empty bookmarks folder, etc. This needs to be done in a "schema agnostic" sort of way so that user interface interactions that know nothing about the underlying graph (e.g., drag-and-drop) can function properly.
Computing empty and isempty is much more straight-forward. It works by calling nsRDFDatasource::GetTarget on each containment property to see the property really does have a value. If it discovers that none of the containment properties yield values, then the element is annotated as "empty".
Below are some examples that illustrate the use of the template code. These examples use the graph specified by the common RDF/XML file, listed here:
template-example.rdf
<?xml version="1.0"?> <RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:NC="http://home.netscape.com/NC-rdf#"> <RDF:Seq ID="root"> <RDF:li> <RDF:Description ID="R1" NC:Name="Number One"> <NC:child> <RDF:Description ID="R1_1" NC:Name="Number One Point One" /> </NC:child> <NC:child> <RDF:Description ID="R1_2" NC:Name="Number One Point Two" /> </NC:child> </RDF:Description> <RDF:Description ID="R2" NC:Name="Number Two" /> </RDF:li> </RDF:Seq> </RDF:RDF> |
This example illustrates use of two rules to generate a walk-down menu.
template-example-menu.xul
<?xml version="1.0"?> <?xml-stylesheet href="chrome://global/skin" type="text/css"?> <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" align="vertical"> <menubar> <menu datasources="template-example.rdf" ref="template-example.rdf#root" label="Test"> <template> <rule iscontainer="true"> <menupopup> <menu uri="rdf:*" label="rdf:http://home.netscape.com/NC-rdf#Name"> <menupopup /> </menu> </menupopup> </rule> <rule> <menupopup> <menuitem uri="rdf:*" label="rdf:http://home.netscape.com/NC-rdf#Name" /> </menupopup> </rule> </template> <!-- We need to ``seed'' menus with the first popup --> <menupopup /> </menu> </menubar> </window> |
This yields the following content model at runtime:
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> align="vertical"> <menubar> <menu datasources="template-example.rdf" ref="template-example.rdf#root" label="Test"> <menupopup> <menu id="http://www.mozilla.org/rdf/doc/template-example-menu.xul#R1" value="Number One"> <menupopup> <menuitem id="http://www.mozilla.org/rdf/doc/template-example-menu.xul#R1_1" label="Number One Point One" /> <menuitem id="http://www.mozilla.org/rdf/doc/template-example-menu.xul#R1_2" label="Number One Point Two" /> </menupopup> </menu> <menupopup> <menuitem id="http://www.mozilla.org/rdf/doc/template-example-menu.xul#R2" label="Number Two" /> </menupopup> </menu> </menubar> </window> |
(For illustrative purposes, the template tag and its children have been removed; in reality, these would still be in the content model.)
template-example-tree.xul
<?xml version="1.0"?> <?xml-stylesheet href="chrome://global/skin" type="text/css"?> <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" align="vertical"> <tree datasources="template-example.rdf" flex="1" ref="template-example.rdf#root"> <template> <treechildren> <treeitem uri="rdf:*"> <treerow> <treecell class="treecell-indent" label="rdf:http://home.netscape.com/NC-rdf#Name" /> </treerow> </treeitem> </treechildren> </template> <treehead> <treerow> <treecell label="Name" /> </treerow> </treehead> </tree> </window> |
The following attributes are ignored when building content from the template:
Ignored Attributes
containercontentsgenerated containter id lazycontent ref rootcontainment subcontainment templatecontentsgenerated uri |