Neil's Place

July 31, 2005

8:23 PM How Templates Work XXXI - Tree Builder Features

Besides the label of a cell, there are several other cell properties you can set when using the tree builder. The supported properties are: label, mode, properties, src and value. The label attribute is used to set the label for a cell. The mode is used for progress meter columns. It may be set to either 'normal' for a normal progress meter or 'undetermined' for an undetermined progress meter. The value attribute is used to set the current progress value for normal progress meters. The value attribute may also be used for checkbox columns by setting it to either true or false. Whether a cell is a normal labeled value, a progress meter or a checkbox is determined by the type attribute on the column the cell is in.

For cells in normal columns, you can use the value attribute to store some other value and you can use the view's getCellValue method to retrieve it. Naturally, this will retrieve the value after any variables have been substituted. Besides the attributes mentioned above, any other attributes specified on the tree rows and cells are ignored. Since no elements are generated, you won't be able to retrieve the values for them either. Thus, the value attribute may be useful to associate an additional value with a row since it will be easier to retrieve.

The src attribute may be used to set an image to appear in a cell. For example:

<tree id="photosList" flex="1" datasources="template-guide-photos5.rdf"
      ref="http://www.xulplanet.com/rdf/myphotos" flags="dont-build-content">
  <treecols>
    <treecol id="photo" label="Photo" flex="1"/>
  </treecols>
  <template>
    <treechildren>
      <treeitem uri="rdf:*">
        <treerow>
          <treecell src="rdf:*"/>
        </treerow>
      </treeitem>
    </treechildren>
  </template>
</tree>

This tree displays each photo in the tree cells. In this case, the member resource is used since that holds the photo's URL, however it could be any other variable, a static value, or a combination of both.

Of course, we can't really see the photos, since the tree's rows are too small. Normally, you wouldn't put photos in a tree like this; instead the images would be used for icons. However, you could use a stylesheet to change the default height of the tree rows. You cannot make each row a different height, but you can change the height of all rows with some CSS:

treechildren::-moz-tree-row {
  height: 150px;
}

Since no elements are constructed by the tree builder, you cannot use the style or class attributes to change the style of a cell (This is the case with all trees). You must use syntax like that above to change the appearance. In the example above, it changes the height of a row to 150 pixels. You may want to change the syntax to refer to a specific <treechildren> element rather than all of them. Once the row height is changed, we can see the entirety of the photos.

Since we need to use special CSS for trees, the properties attribute on a cell becomes useful. It can be used to define extra properties that can be refered to in a stylesheet. For example, if the properties attribute was set to the value "?creator", you could style the photos created by different people differently. You can also use static values in addition to variables in the properties attribute. For instance, consider the following CSS:

treechildren::-moz-tree-cell(Dave) {
  background-color: lightgreen;
}

This would set the background colour of a cell to green for any cell with the "Dave" property. You can also use the properties attribute on the <treerow> to change the style for an entire row. This example sets the country associated with a photo as a property of a tree's rows. We can use that property to change the appearance of each row.

<rule>
  <conditions>
    <content uri="?start"/>
    <member container="?start" child="?photo"/>
    <triple subject="?photo"
            predicate="http://www.xulplanet.com/rdf/country"
            object="?country"/>
    <triple subject="?country"
            predicate="http://purl.org/dc/elements/1.1/title"
            object="?countrytitle"/>
  </conditions>
  <action>
    <treechildren>
      <treeitem uri="?photo">
        <treerow properties="?countrytitle">
          <treecell src="?photo" label="Cat"/>
        </treerow>
      </treeitem>
    </treechildren>
  </action>
</rule>

You might use the following CSS to change the border around rows with a particular country:

treechildren::-moz-tree-row(Netherlands) {
  border: green 1px solid;
}

The result of this example is a tree where one row has a green border around it.

Comments ( 0 )

July 27, 2005

7:21 PM How Templates Work XXX - Building Trees

The most common element to use with a template is the tree. You can use a template with a tree just like any other template. However, since templates are often used with trees, especially with large amounts of data, the template system supports a special builder just for creating trees. Rather than generate content for each row in the tree, the results are just stored in a list inside the builder. This means that DOM nodes are not constructed for any of the items. This is much more efficient as creating a lot of DOM nodes would add a lot of additional overhead. This performance advantage is possible since trees can only display text so the builder only has a few pieces of information to keep track of.

To use the tree builder, you need to add a flags attribute to the root node:

<tree datasources="template-guide-streets.rdf"
      ref="http://www.xulplanet.com/rdf/myneighbourhood"
      flags="dont-build-content">

The "dont-build-content" flag is descriptive in that it doesn't cause any content to be built. However, what it really does is use a subtype of the main builder specific to trees, called the tree builder. Without this flag, the template will be handled using the other type of builder, which is called a content builder, as it generates content. Note that while a tree builder can only be used with trees, a content builder can be used with any type of content. You can also choose to use the content builder for a tree, if you wish. There may be uses for this, especially for small amounts of data. However, you will find that the content builder will be slower as the amount of data to display gets larger.

Apart from the flags attribute, the template syntax is exactly the same for the tree builder as with the content builder. One thing though is that the tree builder requires a very specific form to the action body, specifically, the action body should be a single treeitem with its row and cells. Here is an example:

<tree id="photosList" flex="1" datasources="template-guide-photos5.rdf"
      ref="http://www.xulplanet.com/rdf/myphotos" flags="dont-build-content">
  <treecols>
    <treecol id="name" label="Name" flex="1"/>
    <treecol id="date" label="Date" flex="1"/>
  </treecols>
  <template>
    <treechildren>
      <treeitem uri="rdf:*">
        <treerow>
          <treecell label="rdf:http://purl.org/dc/elements/1.1/title"/>
          <treecell label="rdf:http://purl.org/dc/elements/1.1/date"/>
        </treerow>
      </treeitem>
    </treechildren>
  </template>
</tree>

The tree columns are declared as static content since we only want to declare them once. This template uses the simple rule syntax, although the extended syntax could also be used. The uri attribute must be declared on the <treeitem> element set to either "rdf:*" for the simple syntax or the member variable for the extended syntax. The remaining tags are like the syntax of a tree with a single row. This row will be used as the template data by the tree builder. Instead of generating content, the builder will use the cell attributes to determine what to display. The tree builder implements the nsITreeView interface so it becomes the tree's view. (That is, the tree's view and the tree's builder are the same object.) When the tree is displayed, it asks the view for the contents of each cell. The builder looks at the label for the corresponding cell, translates any variables or predicates into values, and returns the value.

In the example above, the first cell should display the title. The builder doesn't compute any labels until the view asks for them. When the view does request a label for the first cell, the builder looks up the "http://purl.org/dc/elements/1.1/title" predicate for the row in question and returns it.

The content builder will generate the content in the template body and do substitution of the RDF predicates right away. However, it will generate the same result on screen to the user as with the tree builder. Compare the example with a tree builder and the same example using a content builder.

Comments ( 0 )

July 23, 2005

10:42 PM How Templates Work XXIX - Using Multiple Rules to Generate More Results

One interesting technique is to use several rules to combine two sets of unrelated data together. To do this, we create one rule to generate one set of results and a second rule to generate another set of results. If a result from the second rule wasn't also matched by the first rule, it will have content created for it. Recall that when the member resource for a rule matches several rules, only the earliest rule that matches will have content generated for it. If resources don't overlap, we can generate content for two different parts of the RDF data. This technique isn't commonly used. Usually, all of the rules will be similar but just have different filters to match based on different criteria.

If we add the following data about people to the neighbourhood datasource:

  <rdf:Description rdf:about="http://www.xulplanet.com/rdf/myneighbourhood">
    <r:people>
      <rdf:Seq>
        <rdf:li rdf:resource="http://www.xulplanet.com/rdf/person/1"/>
        <rdf:li rdf:resource="http://www.xulplanet.com/rdf/person/2"/>
      </rdf:Seq>
    </r:people>
  </rdf:Description>
  <rdf:Description rdf:about="http://www.xulplanet.com/rdf/person/1"
                   dc:title="Nathan"/>
  <rdf:Description rdf:about="http://www.xulplanet.com/rdf/person/2"
                   dc:title="Karen"/>

We can then use two rules to generate results from different parts of the datasource. The first rule will match the streets as before, but the second rule will generate a result for each person. A header class is used to distinguish the content, although you could use exactly the same content if you wish.

<template>
  <rule>
    <conditions>
      <content uri="?start"/>
      <member container="?start" child="?item"/>
    </conditions>
    <bindings>
      <binding subject="?item" predicate="http://purl.org/dc/elements/1.1/title" object="?title"/>
    </bindings>
    <action>
      <label uri="?item" value="?title" class="header"/>
    </action>
  </rule>
  <rule>
    <conditions>
      <content uri="?start"/>
      <triple subject="?start" predicate="http://www.xulplanet.com/rdf/people" object="?people"/>
      <member container="?people" child="?item"/>
    </conditions>
    <bindings>
      <binding subject="?item" predicate="http://purl.org/dc/elements/1.1/title" object="?title"/>
    </bindings>
    <action>
      <label uri="?item" value="?title"/>
    </action>
  </rule>
</template>

You can view the example.

Comments ( 2 )

July 21, 2005

6:34 PM How Templates Work XXVIII - Parent Conditions

Sometimes you want to simply generate one block of content at the top level and different content at the recurisive level. For example, the bookmarks toolbar in Firefox displays buttons at the first level, but menus and submenus for content below that. The entire bookmarks toolbar is generated by a XUL template.

Templates have a means of allowing a rule to match only if the generated content would be inserted inside an element with a particular tag name. For instance, if the container was a <vbox>, a rule could be created that would only match a <vbox> element. This is useful for recursive templates, since the inner iterations may use different content. It's most useful to distinguish between the outer and inner levels during template generation. For the bookmarks toolbar, the outer content is inserted into an <hbox>, but at lower levels, the content will be inserted into a <menu>

In case you aren't clear, the tag that must match for the outer iteration is the root element, the one with the datasources attribute on it. For inner iterations, it will be the element with the uri attribute from the previous iteration.

To do this kind of matching for the simple template syntax, you place a parent attribute on the rule, set to the tag to match. For instance, we might use the following:

<vbox datasources="template-guide-streets.rdf"
            ref="http://www.xulplanet.com/rdf/myneighbourhood">
  <template>
    <rule parent="vbox">
      <groupbox uri="rdf:*">
        <caption label="rdf:http://purl.org/dc/elements/1.1/title"/>
      </groupbox>
    </rule>
    <rule>
      <label uri="rdf:*" value="rdf:http://www.xulplanet.com/rdf/address"/>
    </rule>
  </template>
</vbox>

On the first pass, the container where generated content would be inserted is a <vbox>, so the first rule will match and a captioned <groupbox> will be created. On the next pass, the parent container will be the element with the uri attribute from the previous pass, in this case, the <groupbox> The first rule will not match in this case, but the second rule will match and a label will be created. The result can be seen in you try the example.

A tag test can also be used with the extended syntax, although the syntax for using it is different. Instead of placing a parent attribute on the <rule>, you place a tag attribute on the <content> tag in the conditions. For instance, the equivalent tag test using the extended syntax for the previous example is the following:

<content uri="?start" tag="vbox">

This example generates the same output content as when using the simple template syntax.

As we've seen in the past few examples, there are many different ways of structuring the two rules to match differently at different levels. General triple tests, tests on an RDF type, container tests and parent tag tests all provide a wide variety of ways to match in very specific ways. Of course, in the simple examples we've been using, the advantages of one kind of condition test over another are not obvious. In more complex examples however, you will see the benefit of one test over others depending on the structure of the data and the UI that you wish to create. By combining the different types of conditions together, more complex interfaces can be created just with templates.

Comments ( 0 )

July 18, 2005

8:18 PM XUL Tutorial Now Part of the Mozilla Developer Documentation

In case you haven't noticed, the XUL tutorial is now available as part of the Mozilla Developer Documentation site. An advantage of this is that errors can be corrected quickly and by anyone who spots them. The user notes created by users haven't been transfered though. I'm not sure if that's a good thing or not.

The user notes include a number of useful tips that people have written relating to a particular page. Currently the user notes aren't posted until I actually read them. This is a good thing since half of the notes are either questions (which I usually just ignore), or are misleading or incorrect. On one hand, I don't want to discourage someone by not accepting a comment they've made, but on the other hand, I feel that if a comment is misleading or uses, for example, some code that is a bad way of doing something, I don't want to post it, since the point of the user notes is to improve the documentation.

Anyway, I think there are plans on posting some other XULPlanet stuff, and I also plan on posting the template guide I've been writing on here. The Aaron countdown timer seems to have disappeared recently perhaps indicating his return a few days early so maybe the App tutorial or Prefbar will get updated also.

In other news, I've been hacking a bit at templates using XML data. Here's the first ever image of a XUL template fed with only XML input. It doesn't look very exciting but that's because the excitment is in disguise. Vlad's just reviewed the main part of the code for this, so we could very well see this stuff early in the 1.9 timeframe.

Comments ( 3 )


7:52 PM How Templates Work XXVII - Container Tests

The simple rule syntax supports two special conditional tests that are commonly used with multiple rules. The first of these tests can be used to test if an element is a container or not. To use this test, place an iscontainer attribute on a <rule>. The iscontainer attribute should be set to true if you only want to match containers, and false if you only want to match non-containers. A container is an RDF container such as a Seq.

The iscontainer attribute makes it easier to handle recursive content since you can have one rule for all containers and another rule for all non-containers. You don't need to match by type or some other predicate. This allows you to recurse down to larger levels without needing additional rules. It is commonly used with menus, and we can rewrite the previous example using the iscontainer attribute instead.

<button label="Houses in my Neighbourhood" type="menu"
        datasources="template-guide-streets.rdf"
        ref="http://www.xulplanet.com/rdf/myneighbourhood">
  <template>
    <rule iscontainer="true">
      <menupopup>
        <menu uri="rdf:*" label="rdf:http://purl.org/dc/elements/1.1/title"/>
      </menupopup>
    </rule>
    <rule>
      <menupopup>
        <menuitem uri="rdf:*" label="rdf:http://www.xulplanet.com/rdf/address"/>
      </menupopup>
    </rule>
  </template>
</button>

The only difference in the code in this example is that the order of the rules has been switched around, the condition check for house has been removed and the iscontainer attribute has been added. Since the iscontainer attribute is set to true, the rule will match as long as the member value or child of the starting node is an RDF container. We could also have left the rules in the original order and set the iscontainer on the first rule to false. The only thing we need to make sure is that the rules are in the proper order, so that the right data will be matched by the right rule. Remember, the more specific rules should go before less specific rules.

Note that leaving out the iscontainer attribute is not the same as setting it to either true or false. If you don't use the iscontainer attribute, the rule will match regardless of whether the node is a container or not.

The iscontainer attribute will also match containers appropriately if you have used the containment attribute in the template to change the predicates that indicate containership. If the node has one of the predicates listed in the containment attribute pointing out of it, it will also be considered to be a container. For instance, we might add the following to the previous example:

<button label="Houses in my Neighbourhood" type="menu"
        datasources="template-guide-streets.rdf"
        containment="http://www.xulplanet.com/rdf/address"
        ref="http://www.xulplanet.com/rdf/myneighbourhood">

The houses do have a value for the "http://www.xulplanet.com/rdf/address" predicate, so they will also be considered to be containers as well, resulting in another level of menus. Of course, we will need to update the predicates and labels to retrieve the right data. But this example demonstrates that something different is indeed happening.

The second special condition attribute tests for empty containers. This invloves setting the isempty attribute on a rule to either true or false. Setting it to true will match all empty containers, that is, containers with no children. Setting it to false will match all containers that have at least one child. Leaving out the isempty attribute will match anything. This condition test is commonly used to display the generated content differently for empty and non-empty containers.

You will commonly use the two attributes iscontainer and isempty together in different combinations to create the effect you need. Typically, this will mean one rule for a container with children, a second rule for empty containers, and a third rule for non-containers. Considering the case of bookmarks, the first two rules would match folders, while the third rule would match bookmarks. Naturally, the emptiness test does not apply to nodes that are not containers.

Note that both the iscontainer and isempty attributes are only available for rules that use the simple syntax.

Comments ( 0 )

July 16, 2005

7:29 PM How Templates Work XXVI - Generating a Recursive Menu

When creating recursive menus, you will need to use multiple rules, since leaf items will need to be created differently than non-leaf items. Leaf items will need to use a <menuitem> element whereas non-leaf items will need to use a <menu> element. This will involve at least two rules, although you might use other rules if you had other differences to handle.

<button label="Houses in my Neighbourhood" type="menu"
        datasources="template-guide-streets.rdf"
        ref="http://www.xulplanet.com/rdf/myneighbourhood"
        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
  <template>
    <rule rdf:type="http://www.xulplanet.com/rdf/House">
      <menupopup>
        <menuitem uri="rdf:*" label="rdf:http://www.xulplanet.com/rdf/address"/>
      </menupopup>
    </rule>
    <rule>
      <menupopup>
        <menu uri="rdf:*" label="rdf:http://purl.org/dc/elements/1.1/title"/>
      </menupopup>
    </rule>
  </template>
</button>

The first rule matches all houses, while the second rule is used for streets. The content generated for each rule differs in only two ways. First, the menu tag is different (menuitem versus menu), and the label is taken from a different RDF predicate. In the first pass, the second rule matches the streets, so a <menupopup> and <menu> element are created. The uri attribute is on the <menu> element since we don't want to repeat the popup for every result. After the first pass, the content will be equivalent to the following (ignoring the template related content):

<button label="Houses in my Neighbourhood" type="menu">
  <menupopup>
    <menu uri="http://www.xulplanet.com/rdf/marion" label="Marion Street"/>
    <menu uri="http://www.xulplanet.com/rdf/garden" label="Garden Avenue"/>
  </menupopup>
</button>

The inner pass through the data handles the houses. The houses match the first rule so a <menupopup> and <menuitem> element are generated and inserted inside the street content (the <menu> element). Again, the popup is only created once since the uri attribute is on the <menuitem> element. The effect is a menu with a submenu. There's nothing special about the way menus are handled -- the builder follows the same method for any type of content. However, the nature of menus can make this tricky to follow. Here is the result of the above example after both levels have been handled.

<button label="Houses in my Neighbourhood" type="menu">
  <menupopup>
    <menu uri="http://www.xulplanet.com/rdf/marion" label="Marion Street">
      <menupopup>
        <menuitem uri="http://www.xulplanet.com/rdf/garden/16" label="16"/>
        <menuitem uri="http://www.xulplanet.com/rdf/garden/18" label="18"/>
      </menupopup>
    </menu>
    <menu uri="http://www.xulplanet.com/rdf/garden" label="Garden Avenue">
      <menupopup>
        <menuitem uri="http://www.xulplanet.com/rdf/garden/25" label="25"/>
        <menuitem uri="http://www.xulplanet.com/rdf/garden/37" label="37"/>
      </menupopup>
    </menu>
  </menupopup>
</button>

Comments ( 0 )

July 15, 2005

11:51 PM How Templates Work XXV - Multiple Rules with Recursion

You may recall that templates generate content recursively. After the data is generated, each result is used as the new reference point for a nested iteration of the template. This is usually used to generate content in a tree or menu. The inner iteration uses the same rules as the outer iteration. However, it is quite possible that you would like child or leaf nodes to appear differently than the parent nodes. Multiple rules are useful in this situation. In this case, one rule would be used to match the outer data and another rule would be used to match the inner data. The builder will apply all rules in both cases, however, if the rules are created correctly, there will only be matches for the rules that you want.

For instance, we might have a datasource which represents the houses in a neighbourhood. The top node contains several children, one for each street. Each street also contains children, one for each house. Naturally, you would want the streets to be displayed in a different manner to the houses. The recursive nature of templates can be used for this example. The outer pass will start at the top node and generate the content for each street. The next pass will use a street as the starting point and generate the content for each house. We could go further and generate data for each room in each house by adding more rules.

Here is an example which shows some sample neighbourhood data.

<hbox datasources="template-guide-streets.rdf"
      ref="http://www.xulplanet.com/rdf/myneighbourhood"
      xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
  <template>
    <rule rdf:type="http://www.xulplanet.com/rdf/House">
      <vbox uri="rdf:*" class="box-padded">
        <label value="Address: rdf:http://www.xulplanet.com/rdf/address"/>
        <label value="Floors: rdf:http://www.xulplanet.com/rdf/floors"/>
      </vbox>
    </rule>
    <rule>
      <groupbox uri="rdf:*" class="box-padded">
        <caption label="rdf:http://purl.org/dc/elements/1.1/title"/>
      </groupbox>
    </rule>
  </template>
</hbox>

The first rule matches only those items that have an RDF type of "http://www.xulplanet.com/rdf/House". The second rule doesn't have any condition filter so will match any result. The starting point indicated by the ref attribute is "http://www.xulplanet.com/rdf/myneighbourhood". In the RDF data, this is an RDF Bag with two children. Since the simple rule syntax is used in both rules, the builder will iterate over the children to generate results. At this pass, both of the children of "http://www.xulplanet.com/rdf/myneighbourhood" are streets and not houses so neither child will match the first rule. However, both children will match the second rule. Thus, two matches will be created using the second rule. The second rule creates a <groupbox> with a <caption>. If you look at image of the example, you will note that two groupboxes have been created.

The builder then recurses, using the previous result as the new starting point. For the first street, this new starting point will be "http://www.xulplanet.com/rdf/marion". The builder reapplies the rules starting from this new location in the RDF graph. The new node is an RDF Seq with children so the simple rules can generate some results. However, these results are houses, so the first rule will match. The second rule, since it has no conditions, will also match, but since the first rule takes priority, these rules would never apply. The effect is that the content for the first rule would be used for each house. This content is inserted inside the outer content generated for the street. This means that the <vbox> and the two labels will be placed inside the <groupbox> generated from the previous pass.

We could be more specific and specify a type in the datasource for the streets as well. This wouldn't affect the output in this example, but it may be more optimal in more complex templates to be as specific as possible when creating conditions. If there were other types of buildings on a particular street, we might add an additional rule for this. For instance, we might add another rule after the first:

<rule rdf:type="http://www.xulplanet.com/rdf/Store">
  <vbox uri="rdf:*" class="box-padded">
    <label value="Address: rdf:http://www.xulplanet.com/rdf/address"/>
    <label value="Sells: rdf:http://www.xulplanet.com/rdf/sells"/>
  </vbox>
</rule>

This rule is similar to the first rule, however is matches only those items that have an RDF type of "http://www.xulplanet.com/rdf/Store".

Comments ( 0 )

July 11, 2005

10:50 PM How Templates Work XXIV - Another Multiple Rule Example

The most common use of multiple rules is to apply different action bodies to different results. Often this will be because a particular result has a property that others do not. For instance, in an earlier example, one of the photos had a description and the other photos did not. In this case, you might wish to display the photo with a description in a different manner. This is useful if you wish to hide any content that would be needed to display the description.

The next example shows how we can do this.

<vbox id="photosList" align="start" datasources="template-guide-photos5.rdf"
      ref="http://www.xulplanet.com/rdf/myphotos">
  <template>
    <rule>
      <conditions>
        <content uri="?start"/>
        <member container="?start" child="?photo"/>
        <triple subject="?photo"
                predicate="http://purl.org/dc/elements/1.1/title"
                object="?title"/>
        <triple subject="?photo"
                predicate="http://purl.org/dc/elements/1.1/description"
                object="?description"/>
        <triple subject="?photo"
                predicate="http://purl.org/dc/elements/1.1/date"
                object="?date"/>
      </conditions>
      <action>
        <hbox uri="?photo" class="box-padded">
          <vbox>
            <label value="?title"/>
            <image src="?photo"/>
          </vbox>
          <groupbox>
            <caption label="Photo Details"/>
            <label value="?description"/>
            <label value="Date: ?date"/>
          </groupbox>
        </hbox>
      </action>
    </rule>
    <rule>
      <conditions>
        <content uri="?start"/>
        <member container="?start" child="?photo"/>
        <triple subject="?photo"
                predicate="http://purl.org/dc/elements/1.1/title"
                object="?phototitle"/>
      </conditions>
      <action>
        <vbox uri="?photo" class="box-padded">
          <label value="?phototitle"/>
          <image src="?photo"/>
        </vbox>
      </action>
    </rule>
  </template>
</vbox>

In this example, the first rule matches only those photos with title, description, and date properties. The second rule matches those photos with a title. If a photo did not have a title, it would not match any rule. In the example data, only the first photo has been given all the properties to match the first rule. The second photo has a title and date but no description, while the third photo has only a title. The first photo will match both rules whereas the other two photos will match only the second rule. The resulting data will be:

(?start = http://www.xulplanet.com/rdf/myphotos,
 ?photo = http://www.xulplanet.com/ndeakin/images/t/palace.jpg,
 ?title = Palace from Above
 ?description = View from the top of the tower looking east of the Doges Palace,
 ?date = 2005-04-30T14:55:00+01.00)
(?start = http://www.xulplanet.com/rdf/myphotos,
 ?photo = http://www.xulplanet.com/ndeakin/images/t/palace.jpg,
 ?phototitle = Palace from Above)
(?start = http://www.xulplanet.com/rdf/myphotos,
 ?photo = http://www.xulplanet.com/ndeakin/images/t/canal.jpg,
 ?phototitle = Canal)
(?start = http://www.xulplanet.com/rdf/myphotos,
 ?photo = http://www.xulplanet.com/ndeakin/images/t/obelisk.jpg,
 ?phototitle = Obelisk)

The first result matches the first rule and contains variables for the two additional predicates that were examined in the conditions. Since the second rule doesn't refer to these variables, they will not be filled in. Although the canal photo has a date, the second rule doesn't use it, so you cannot refer to the ?date variable in this rule. But, you could use a <binding> to get the date without requiring it to match the conditions. Of course, the variable doesn't have to be ?date in the second rule.

You might notice that the ?title variable is used in the first rule whereas the ?phototitle variable is used for the second rule, despite that they both store the value of the title predicate. There is no reason for this -- it is only used in this example to show that there is no connection between the variables used in the two rules. You can use different variables if it makes sense in the context of the template or the data, although in this example it would seem more reasonable to use the same variable name.

The only exception to this is that the container and member variables (those that are referred to in the uri attributes), must be the same in all rules. In this example, the container variable is ?start and the member variable is ?photo. If different variables were used for these, the rules would not work properly. The reason for this is due to the manner in which the builder processes the data.

Looking back at the results listed above, the palace photo appears twice so the second one will be removed, leaving only three matches. Although the builder actually generates mutliple matches for one resource, in many cases it isn't necessary to be aware of this detail when creating templates. It is usually sufficient to assume that earlier rules are used to filter results much like an if-else construct in a programming language. If the last rule has no specific conditions (for example a simple rule with no attributes on the <rule> element), it could be considered to be the final else block that matches all data.

Since results generated by earlier rules override those of later results, you will want to ensure that the rules are placed in the right order. The rules should be ordered from most specific to least specific. If you reversed the order of the two rules in the example above, three photos would still be displayed, but the larger rule that displays the image details would be dropped as the other rule has a higher priority. The result would be that the palace photo would not show this information.

Comments ( 5 )

July 10, 2005

4:20 PM How Templates Work XXIII - Multiple Simple Rules

You can also use multiple rules with the simple rule syntax. Here is the previous example rewritten using the simple syntax:

<hbox id="photosList" datasources="template-guide-photos3.rdf"
      ref="http://www.xulplanet.com/rdf/myphotos"
      xmlns:dc="http://purl.org/dc/elements/1.1/">
  <template>
    <rule dc:title="Canal">
      <button uri="rdf:*" image="rdf:*" label="View" orient="vertical"/>
    </rule>
    <rule>
      <image uri="rdf:*" src="rdf:*"/>
    </rule>
  </template>
</hbox>

The result to the user in this example is the same as the previous example. You can also mix simple and extended rules in one template, although you may prefer to use the same style in all rules for consistency.

However, if you are going to be using a number of rules, the template builder is more efficient when using multiple rules using the simple rule syntax. This is because all simple rules will iterate over the same data, usually the children of an RDF container. So the builder only performs this step once and filters the data for each rule.

When using the extended rule syntax, the manner in which the graph is navigated may be different for every rule, so no optimization can be done. The builder needs to process every condition of every rule. If, for example, you have six rules, each with a <member> condition, the builder will need to construct the children six times. You probably won't notice any difference for small amounts of data such as the photos example we've been using, but you might for large datasets. Thus, you will want to use the simple rule syntax when possible.

If you are only going to be using one rule, it doesn't matter, of course. Speaking of using a single rule, the simple rule syntax allows a slight shorthand. You can remove the <rule> element and place the rule's conditions directly on the <template>. There's no performance benefit, but it does save some typing.

<hbox id="photosList" datasources="template-guide-photos3.rdf"
      ref="http://www.xulplanet.com/rdf/myphotos"
      xmlns:dc="http://purl.org/dc/elements/1.1/">
  <template dc:title="Canal">
      <button uri="rdf:*" image="rdf:*" label="View" orient="vertical"/>
  </template>
</hbox>

This example shows only a single photo since a condition is used to filter out the other two photos. Note that in this shorthand, the conditions are placed directly on the <template> element.

Comments ( 2 )

July 9, 2005

7:03 PM How Templates Work XXII - Using Multiple Rules

All of the examples shown so far have used only a single rule. The builder supports the use of multiple rules as well. This involves using additional <rule> elements after the first. There are three main reasons for using multiple rules. First, to generate different content when different criteria are met, second, to apply different content for child elements created during template recursion, and third, just to generate additional results that will be merged with the first rule. We'll see examples of each of these techniques. A multiple rule template looks like the following:

<hbox id="photosList" datasources="template-guide-photos3.rdf"
      ref="http://www.xulplanet.com/rdf/myphotos">
  <template>
    <rule>
      <conditions>
        <content uri="?start"/>
        <member container="?start" child="?photo"/>
        <triple subject="?photo"
                predicate="http://purl.org/dc/elements/1.1/title"
                object="Canal"/>
      </conditions>
      <action>
        <button uri="?photo" image="?photo" label="View" orient="vertical"/>
      </action>
    </rule>
    <rule>
      <conditions>
        <content uri="?start"/>
        <member container="?start" child="?photo"/>
      </conditions>
      <action>
        <image uri="?photo" src="?photo"/>
      </action>
    </rule>
  </template>
</hbox>

This template contains two rules, the first contains a <triple> which matches only the photo with a title of "Canal". The second rule doesn't contain such a triple and will match all three of the photos. It the first rule was used by itself, only one result would match. If the second rule was used by itself, three results would match. When used together in this example, the results are combined and only three results are shown. However, you will probably notice that the one photo that matches the first rule has appeared differently that the others. In fact, the content for this photo is that of the first rule with the button, whereas the content for the other photos is that of the second rule with the normal images.

This demonstrates the first style of using multiple rules, to generate different content under different circumstances. This is a very useful and commonly used technique since it allows for more complex content to be displayed. For instance, the Firefox bookmarks toolbar displays folders in a different manner than regular bookmarks. This is done by using multiple rules, one for folders and another for bookmarks. Actually, there are more than two rules used in bookmarks, since there are also separators, submenus, and livemarks to deal with.

We already know that a rule generates a set of results, possibly filtered based on the rule's conditions. There's no magic to the way in which the template builder processes multiple rules. It just takes the results generated from the first rule, adds the results for the second rule, adds the results for the third rule, and so on. Here are the results that would be generated by the first rule above, before any bindings are applied:

(?start = http://www.xulplanet.com/rdf/myphotos,
 ?photo = http://www.xulplanet.com/ndeakin/images/t/canal.jpg,

Then, the builder adds the three results generated from the second rule:

(?start = http://www.xulplanet.com/rdf/myphotos,
 ?photo = http://www.xulplanet.com/ndeakin/images/t/canal.jpg,
(?start = http://www.xulplanet.com/rdf/myphotos,
 ?photo = http://www.xulplanet.com/ndeakin/images/t/palace.jpg)
(?start = http://www.xulplanet.com/rdf/myphotos,
 ?photo = http://www.xulplanet.com/ndeakin/images/t/canal.jpg)
(?start = http://www.xulplanet.com/rdf/myphotos,
 ?photo = http://www.xulplanet.com/ndeakin/images/t/obelisk.jpg)

So four possible results are available, one from the first rule and three from the second. However, the example shows that only content for three results are generated. What happened to the fourth result?

This is where the useful aspect of multiple rules comes in. Note that two of the results above are actually for the same photo (canal.jpg). The template builder removes any duplicate items before generating content. It does this by only allowing the match for the earliest rule. That is, the canal.jpg generated by the second rule is removed, since an earlier rule (the first rule) already generated a match for that result.

One important distinction is that the determination of duplicates is only based on the member variable, in this case the ?photo variable. It doesn't matter whether other variables are the same or not.

If you look at the example again, you might notice that the canal photo that matches the first rule has appeared in-between the other two photos, even though those photos are generated from the second rule. The builder hasn't put all the matches for the first rule before the matches for the second rule. In fact, the order is the same as the examples that only use one rule. Compare the multiple rule example with an earlier example that used only a single rule. The photos have appeared in the same order in both cases.

This is because the builder notices that the photos are in an RDF Seq in the datasource and arranges them in the order they appear in the Seq. This and other automated sorting done by the template builder is a fairly complicated process that will be discussed in more detail later.

Comments ( 5 )

July 8, 2005

7:41 PM How Templates Work XXI - Containment Properties

So far, we've seen how simple rules can generate results from the children of an RDF container. However, simple rules may also iterate over a single predicate pointing out of a resource, rather than use the children. This allows you to iterate over a non-container. To do this, use the containment attribute on the root node, set to the predicate to iterate over.

Let's look back at the very first example again. It doesn't have any containers, but we may want to iterate over the relatedItem predicate using the simple rule syntax. We can add the containment attribute to do this.

<vbox datasources="template-guide-ex1.rdf"
         ref="http://www.xulplanet.com/rdf/A"
         containment="http://www.xulplanet.com/rdf/relatedItem">
  <template>
    <rule>
      <label uri="rdf:*" value="rdf:*"/>
    </rule>
  </template>
</vbox>

Instead of iterating over a container, this example iterates over a specific predicate. This attribute is useful when the RDF data is structured in such a way that an RDF container isn't used.

The builder treats the predicate in the containment attribute as an indicator that an element is a container. If the starting node (or ref) has that predicate as one of its triples in the RDF data, the builder will use it in addition to checking if it is a container. Note the distinction here. The containment attribute doesn't replace the existing container checking. If the resource "http://www.xulplanet.com/rdf/A" was also an RDF Seq with some children, then those children would also be added to the results. This next example demonstrates this. The only difference between this and the previous example is a a couple of additional lines added to the RDF/XML:

<rdf:Seq rdf:about="http://www.xulplanet.com/rdf/A">
  <rdf:li rdf:resource="http://www.xulplanet.com/rdf/E"/>
  <rdf:li rdf:resource="http://www.xulplanet.com/rdf/F"/>
</rdf:Seq>

The effect is that there are five results instead of three. Three generated via the use of the containment attribute, and two generated because the starting resource is a container with two children. Effectively, the containment attribute allows you to specify additional predicates that provide children.

You can specify multiple predicates in the containment attribute by separating them with spaces. Each will be applied in sequence.

The containment attribute also applies to <member> tests when using the extended template syntax. That is, in addition to iterating over the children, it will iterate over the predicates listed in the containment attribute. For example, the following is equivalent to the previous example, except that the full rule syntax is used.

<vbox datasources="template-guide-ex3.rdf"
         ref="http://www.xulplanet.com/rdf/A"
         containment="http://www.xulplanet.com/rdf/relatedItem">
  <template>
    <rule>
      <conditions>
        <content uri="?start"/>
        <member container="?start" child="?child"/>
      </conditions>
      <action>
        <label uri="?child" value="?child"/>
      </action>
    </rule>
  </template>
</vbox>

Try this example. What happens is that the builder generates additional possible values for the ?child variable, so it creates an additional result for each one.

Comments ( 6 )

July 6, 2005

5:49 PM How Templates Work XX - Simple Rule Conditions

Simple rules also use a simpler way of handling conditions to filter out unwanted results. It allows you to filter for results that have certain predicates with specific values. For instance, you may filter results that have a certain category or country. You can only filter on predicates pointing out of the member resource. That is, you cannot filter on predicates pointing into the member resource nor can you cannot filter based on predicates pointing out of other resources.

The conditions are specified using attributes on the <rule> element. The namespace of the attribute and the attribute name combine to form the predicate you wish to filter by and the value of the attribute will be the value to filter by. Any results that match the criteria are accepted and any that do not match are rejected. You can use multiple attributes to filter by several criteria. In this case, all of the criteria must match for the result to be accepted.

For example, to filter for photos with a specific country:

<hbox id="photosList" datasources="template-guide-photos4.rdf"
      ref="http://www.xulplanet.com/rdf/myphotos"
      xmlns:r="http://www.xulplanet.com/rdf/">
  <template>
    <rule r:country="http://www.daml.org/2001/09/countries/iso#IT">
      <vbox class="box-padded" uri="rdf:*">
        <image src="rdf:*"/>
        <label value="rdf:http://purl.org/dc/elements/1.1/title"/>
      </vbox>
    </rule>
  </template>
</hbox>

This example shows how a single attribute may be used to filter for only those results that have a country set to "http://www.daml.org/2001/09/countries/iso#IT". Photos that do not have this country will not have any content generated for them. The attribute has a namespace that, when expanded, becomes "http://www.xulplanet.com/rdf/country". This syntax is much like how predicates are the specifed in the RDF/XML.

This form of attribute syntax on a rule is equivalent to a <triple> in the extended syntax. In fact, the builder will internally convert the attribute into the same compiled form that would be used for a <triple>. The equivalent triple for the above example might be:

<triple subject="?photo"
        predicate="http://www.xulplanet.com/rdf/country"
        object="http://www.daml.org/2001/09/countries/iso#IT"/>

The simple rule syntax is limited in the kind of filtering it can do, however it is often sufficient for many purposes. Naturally, if you want to set a filter dynamically, as the photos example does with a menulist, you will need to set the attribute on the <rule> element and rebuild the template.

function applyFilter(country)
{
  var rule = document.getElementById("filterRule");
  if (country){
    rule.setAttributeNS("http://www.xulplanet.com/rdf/", "country", country);
  }
  else {
    rule.removeAttributeNS("http://www.xulplanet.com/rdf/", "country");
  }
  document.getElementById("photosList").builder.rebuild();
}

This version of the applyFilter function only needs to set or remove the attribute on the rule as necessary. Note that the namespace aware functions (with the suffix NS) need to be used to set or remove attributes with namespaces. This example assumes that the rule has been given an id of "filterRule". Note that the menulist itself must use the extended syntax since it doesn't iterate over the children of a resource.

As mentioned earlier, you can filter by several criteria by using multiple attributes. For instance:

<rule id="filterRule"
      dc:title="Obelisk"
      r:country="http://www.daml.org/2001/09/countries/iso#IT">

This rule filters on two criteria which must both match. Rules will apply all the attributes as conditions except for the id attribute, as well as some special attributes, which are ignored. These special attributes are useful when handling multiple rules, which will be the subject of a later topic.

Comments ( 4 )

July 2, 2005

3:00 PM How Templates Work XIX - Simple Rule Syntax

When iterating over the children of an RDF container, there is a simpler rule syntax which may used. It may also be used when iterating over only one predicate. Simple RDF graph navigation such as this is common, so the simpler syntax is usually used in this situation since it avoids extra tags, although the simple syntax is not more or less efficient, at least when a single rule is involved. A simple rule is equivalent to a rule with only the content tag and a member tag, as well as optionally a set of triples from the child node. At it's simplest, the simple rule syntax is equivalent to the following conditions:

<conditions>
  <content uri="?start"/>
  <member container="?start" child="?photo"/>
</conditions>

The template builder uses the simple rule syntax whenever a template rule does not have a <conditions> element. If a <conditions> element is present, the full syntax is used instead. In the simple syntax, the builder will use the default conditions shown above, although the variable names are randomly generated.

Since the photos list iterates over the children of a container, we can use the simple syntax. Here is what it might look like:

<rule>
  <vbox class="box-padded" uri="rdf:*">
    <image src="rdf:*"/>
    <label value="rdf:http://purl.org/dc/elements/1.1/title"/>
  </vbox>
</rule>

There is no <conditions> or <action> element used. Instead, the builder uses a default set of conditions, and the action is placed directly inside the <rule> element. You can see that this code is much simpler than the full syntax. To understand how this works, let's examine how the builder processes a simple rule. First, it evaluates the default conditions as above, except that no variables are used, or at least, not ones that are used externally. In reality, the builder could be considered to convert a simple rule into an extended rule, so that it can be processed in the same way. After the default conditions are evaluated, the data network will look something like this:

(?1 = http://www.xulplanet.com/rdf/myphotos,
 ?2 = http://www.xulplanet.com/ndeakin/images/t/palace.jpg)
(?1 = http://www.xulplanet.com/rdf/myphotos,
 ?2 = http://www.xulplanet.com/ndeakin/images/t/canal.jpg)
(?1 = http://www.xulplanet.com/rdf/myphotos,
 ?2 = http://www.xulplanet.com/ndeakin/images/t/obselisk.jpg)

The numbers are used here for the variable names, but they are really just randomly generated so you can't refer to them in any way. The member data, or results are held in the variable represented by '?2'. Three matches have been found so the content will be duplicated three times.

There are several attributes in the content that have values prefixed with 'rdf:'. Since you can't declare variables in the simple syntax, a different way is used to grab RDF data. The 'rdf:' prefix means look up a predicate pointing out of the node for that result (the child or member node). For example, the label's value attribute has a value of "rdf:http://purl.org/dc/elements/1.1/title". This means look up the value of the predicate "http://purl.org/dc/elements/1.1/title" pointing out of the current result. Of course, it should be obvious that this causes the value attribute to be set to the photo's title.

You can get other predicates in the same way, just by using the predicate uri and prefixing it with 'rdf:'. You can also combine two in one attribute by separating them with a space or a caret (^) just as you can with the full syntax. For example:

<label value="rdf:http://purl.org/dc/elements/1.1/title http://www.xulplanet.com/rdf/country"/>

The 'rdf:' form is really equivalent to the <binding> tag in the full syntax. The subject of the binding is the result member resource, the predicate is the string that comes after 'rdf:' and the object isn't necessary as the value is replaced directly in the attribute. As with bindings, the value is not required to match, so the data doesn't have to exist in the RDF graph. If one photo doesn't have a title, it will be replaced with an empty string.

The special value 'rdf:*' means the uri of the result resource, rather than referring to a predicate. It would be equivalent to ?photo variable in this example if the full syntax was used. You can see in the example that the value 'rdf:*' is used in two places, the uri attribute on the hbox and as the src attribute for the image.

Here is the photos example using the simple syntax. The filtering isn't shown in this example. It is still possible to do filtering using simple rules and we'll look at this next.

Comments ( 7 )

July 1, 2005

8:08 PM How Templates Work XVIII - Static Content

Creating an menuitem in the filter list to show all items is fairly easy. This item doesn't need to be generated from the datasource. Instead it will be what is called static content in a template. The static content is any content that is a sibling of the <template> element. This content is displayed as is without affecting the generated content from the template.

<menulist datasources="template-guide-photos4.rdf"
           ref="http://www.daml.org/2001/09/countries/country-ont#Country"
           oncommand="applyFilter(event.target.value);">
  <menupopup>
     <menuitem label="All"/>
  </menupopup>
  <template>
    <rule>
      <conditions>
        <content uri="?start"/>
        <triple subject="?country"
                predicate="http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
                object="?start"/>
        <triple subject="?country"
                predicate="http://purl.org/dc/elements/1.1/title"
                object="?countrytitle"/>
      </conditions>
      <action>
        <menupopup>
          <menuitem uri="?country" label="?countrytitle" value="?country"/>
        </menupopup>
      </action>
    </rule>
  </template>
</menulist>

The only difference between the previous example and this one is that the <menulist> element has some additional content added before the template. The content will be displayed just like it would be as if the template wasn't present. That is, before the template is examined, the menulist would have one item in it, with the label 'All'. The template generates two results as before, one for each country, and appends a <menuitem> for each one.

One interesting thing about this example is that only one <menupopup> will be created, even though there are two in the code, one outside the template and another one inside the action body. This demonstrates how the template builder generates content, and the specifics of how this is done become important when dealing with recursive content and multiple rules.

What's going on here is that the XUL parser first creates the outer <menulist> element. The static content outside the template will also be created. The result will be a XUL display that looks just like the code above, except that the template and its content is hidden. At this point, the menulist will have a menupopup as a child, and the menupopup will in turn have one menuitem child. Next, the template is examined and two results are generated. Remember that only the content at the uri attribute or below inside the action are copied for each result.

When processing the action body, the builder looks at the first element inside the <action>, in this case, the <menupopup>. This element doesn't have a uri attribute, so the builder knows that this element should only be created once. You might think that the builder would then just create a <menupopup>, but it doesn't. Instead, it looks to see if a <menupopup> already exists in the location where it would be generated. In this case, there is one, the static menupopup defined outside the template. Instead of creating another menupopup, the builder uses the existing one. This is convenient since it makes it easy to combine static results and template generated results into one list.

The builder then switches where generated content would be generated to inside to existing menupopup and moves onto the next part of the action, the <menuitem>. This element has the uri attribute so this content will be duplicated for each result.

The technique of looking for an existing element applies to any type of content. The builder will treat the element as already present as long as some element with the same tag name exists. If you were to change the static content to be some other element besides a <menupopup>, it won't match, so the builder will create a new one when handling the action. This becomes useful when using multiple rules and we'll see some examples of this later.

Here is the example to try. Note that the workaround of loading the datasource beforehand as mentioned for the last example isn't necessary, as the existence of the static content is another effective workaround. So, if you're using menulists with templates and the data isn't displaying properly when the data hasn't loaded, try using a static <menupopup>. It doesn't even need to have any items in it.

The static content may be placed before or after the template, although usually you would place it first. Note that the static content is always placed before the generated content, so the 'All' item will always be the first item in the menu.

Let's look at another example, involving radio buttons. Actually, this example is similar, it just uses a radigroup instead of a menulist to display the list of countries.

<radiogroup datasources="template-guide-photos4.rdf"
            ref="http://www.daml.org/2001/09/countries/country-ont#Country"
            onselect="applyFilter(event.target.value);">
  <radio label="All" selected="true"/>
  <template>
    <rule>
      <conditions>
        <content uri="?start"/>
        <triple subject="?country"
                predicate="http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
                object="?start"/>
        <triple subject="?country"
                predicate="http://purl.org/dc/elements/1.1/title"
                object="?countrytitle"/>
      </conditions>
      <action>
        <radio uri="?country" label="?countrytitle" value="?country"/>
      </action>
    </rule>
  </template>
</radiogroup>

This example doesn't have any other content to generate outside the radio element with the uri attribute, so it will just be copied as is. The static 'All' radio button will be left as the first radio item.

Comments ( 4 )