Introducing XWeb

Some common patterns for XSLT

Here are some XSLT fragments that might be useful if you want to create your own stylesheets. The examples are taken from the generic stylesheet, you can find more complicated examples in there.

Recursing through the tree

Sometimes you want to recurse through the whole document to change only specific parts but keeping the others. This template does the recursion part, if you add other templates with higher priority than this one and the same mode they can change specific elements. Note that the priority of a rule matching all elements is automatically lower than the priority of templates matching specific nodes.

  <xsl:template match="*" mode="body">
    <xsl:copy>
      <xsl:for-each select="@*">
        <xsl:copy-of select="."/>
      </xsl:for-each>
      <xsl:apply-templates select="node()" mode="body"/>
    </xsl:copy>
  </xsl:template>

The template matches all elements, copies the element itself with the xsl:copy command and its attributes with the for-each loop. Afterwards the recursion on all other nodes is performed. Note that an attribute does not match the node() in XSLT.

If another template like this is added, all images will be replaces with the alternative text, the rest of the document will be kept as is:

  <xsl:template match="img" mode="body">
    <xsl:value-of select="@alt"/>
  </xsl:template>

Including a given file

The following code includes the whole XML document given in the "feature.include.header" parameter:

  <xsl:if test="$feature.include.header != '' ">
    <xsl:copy-of select="document($feature.include.header)/header/node()"/>
  </xsl:if>

The parameter is assumed to be a URL to an XML document with the root element "header", all nodes below this root element are copied into the output of the script.

Adding JavaScript for mouseOver effects

In this example we create JavaScript code to initialize all mouse over images for sections:

  <script language="JavaScript" type="text/javascript">
    <xsl:comment>Begin
      <xsl:for-each select="/html/section">
        <xsl:choose>
          <xsl:when test="img[starts-with(@xwebtype, 'mouseOverSectionButton')]">
            <xsl:apply-templates mode="insertImagePreload" 
	              select="img[starts-with(@xwebtype, 'mouseOverSectionButton')]"/>
          </xsl:when>
        </xsl:choose>
      </xsl:for-each>
    // End</xsl:comment>
  </script>

The <script> element is copied into the output and will contain commented JavaScript code. It is commented to hide it from browsers not capabable of JavaScript and the comments are created using the specific XSLT command for this. Note that <xsl:comment> is not a marker for comments (that is done with <-- and -->) but a command to create commands in the XML output.

The mouse over buttons are images whose types start with "mouseOverSectionButton". Checking for the beginning of the string allows using different buttons for different sections, e.g. having color codes for the sections. The buttons could be called like "mouseOverSectionButtonBlue" for a blue section.

The JavaScript commands themself are created in a different template. This is useful since there are usually different buttons for mouse over, e.g. you might want to add mouse over effects to the page buttons, too. Using a separate template allows us to reuse the code:

  <xsl:template match="img" mode="insertImagePreload">
    <xsl:value-of select="@name"/>
    <xsl:text> = new Image( </xsl:text>
    <xsl:value-of select="@width"/>
    <xsl:text>, </xsl:text>
    <xsl:value-of select="@height"/>
    <xsl:text>);</xsl:text>
    <xsl:value-of select="@name"/>
    <xsl:text>.src = "</xsl:text>
    <xsl:value-of select="@src"/>
    <xsl:text>";</xsl:text>
  </xsl:template>

The name of the JavaScript object is the name XWeb adds. This is the entry or section name with some changes to make it usable in JavaScript, e.g. whitespace is replaced with underscores and it will never start with a digit. Note the use of xsl:text to handle the whitespace in the output.

Adding an image if found

Here we check if the active entry has an image called "entryBanner" and add it if found:

  <xsl:if test="/html/section[@active='true']/
            entry[@active='true']/img[@xwebtype='entryBanner']">
    <div class="entryBanner">
      <xsl:for-each select="/html/section[@active='true']/
              entry[@active='true']/img[@xwebtype='entryBanner']">
        <img src="{@src}" name="{@name}" border="0" 
	       alt="{concat(@alt,$i18n.bannerMarker)}" 
	       width="{@width}" height="{@height}"/>
      </xsl:for-each>
    </div>
  </xsl:if>

The <div> allows CSS markup later (e.g. to center the banner), the alt attribute used is concatenated from the name XWeb added (the page name) and a parameter called "i18n.bannerMarker". The result could be something like "MyPage (Banner)".

The foreach should find only one element since XWeb does not add multiple images of the same type. Alternatively a separate template could be used, which is useful if you can use the same code somewhere else.

The check for the active section is not necessary since there will be only one active entry in the whole structure. It might increase the performance of the XSLT process, though. This way the XSLT processor does not have to check all section if they contain an active entry.