Ice Bear SoftHow these pages are made

This is an attempt for literate programming of a transformation stylesheet. What does literate programming mean? This is an approach when one source file contains both the code and its verbose documentation. Some tool then extracts the code so that it can be executed and another tools generates pretty-printed documentation. There are WEB systems (do not confuse it with World Wide Web) for most programming languages and ltxdoc and companion docstrip packages for LaTeX. All these tools can be found on CTAN. Please do not say that other kind of programming is illiterate. Traditional style programmers do not like it.

XSLT as such is a good candidate for literate programming tools. Several such systems can be found on the web. However, I am not aware of any tool for literate programming of XSLT stylesheets. Although noweb or possibly other configurable tools could be used for literate programming of a stylesheet, I decided to stay with pure XML solution. I tried to make the source file and stylesheets as simple as possible. You will thus see that the test attribute of the <xsl:if> and <xsl:when> display "<" and ">" while they actually contain &lt; and &gt;. The test also includes quotes inside quotes which is not allowed. The real code mixes quotes and apostrophes.

No oo.xml

Contents

1. Introduction
2. Selection of the XML scheme
3. Page formatting
4. Choice of the XSLT procesor
5. Bootstrapping
6. Stylesheet for transformation into HTML
  6.1. Modes
  6.2. Language selection
  6.3. Stylesheet prologue
  6.4. Templates for a manual
  6.5. Modification date and time
  6.6. Sectioning
  6.7. Templates for table of contents
  6.8. Logo with a hyperlink
  6.9. Hypertext links
  6.10. JavaScript images
  6.11. Comments
  6.12. Hiding a part of the contents
  6.13. Custom information
  6.14. Processing instructions
  6.15. Other elements
  6.16. Templates for listing code examples
  6.17. Listing the bootstraping stylesheet
  6.18. Text nodes
7. Stylesheet for manuals
  7.1. Modified templates for manuals
  7.2. Logo with a hyperlink for manuals
  7.3. Processing the root element
  7.4. Postprocessing
  7.5. Reference to the cascading stylesheet
  7.6. Page footer
8. Download
9. Workflow automation
10. Uploading files to the server

1. Introduction

The pages have both English and Czech version. The contents should be the same. However, it is no easy to maintain consistently two sets of pages. It is convenient if both language version remain in the same file. Originally the pages contained two columns with the English text in the left one and the Czech text in the right one. The common parts were typed in a single column which broke the two-column layout.

Two-column layout has its disadvantages. A reader is disturbed by presence of the second language. In addition some parts of the text make sense in one language version only. Thus the columns are not well balanced. It was therefore decided to make separate Czech and English pages which will be generated from the common source written in XML.

 Top of the page 

2. Selection of the XML scheme

We should first choose the input format. It is possible to use standard DocBook. However, this format is quite involved. It offers numerous possibilities which will not be utilized here since the text should serve solely for web pages and other processing is not assumed. What is important is the visual appearance of the pages. We could download tools for transforming DocBook into HTML but refining these tools so that they satisfy our requirements would be painstaking. This format was therefore refused.

The first version of these pages was written directly in HTML. It seemed to be natural to enrich HTML with a few elements. This gave birth to the second version which was developed at the time when I knew just a little XML and XSLT and I was learning them on this project. Now I realized that a great many things were done in a complicated way without any good conception. I thus made an ambitious plan and it turned out that I knew less than I thought. When writing this document I learned again numerous new things. The idea of enriching HTML with a few elements, however, continues to live.

In the very first version generated from XML I wanted to have the extension elements in my own namespace. However, I did not like that the root <html> element contained the namespace declaration although none of these elements got into the page. At that time I did not know that it is sufficient to put the prefix of my namespace to the exclude-result-prefixes attribute of the <xsl:stylesheet> element. I considered again the use of a namespace. I came to the conclusion that the input format is defined by my own tags which intentionally agree with HTML elements in a great many cases. That way I rationalized my solution.

 Top of the page 

3. Page formatting

Page formatting can be achieved by two extreme methods:

  1. put all formatting instructions into the HTML code
  2. format everything solely by means of a cascading stylesheet (CSS)

The former method leads to an involved code. It need not matter because the pages are generated from XML but the formatting markup would mess up the transformation stylesheet. The latter method brings the following disadvantages:

  1. Implementation of cascading stylesheets is incomplete in some browsers.
  2. Implementation of CSS is buggy in some browsers. The screen resolution is often interpreted wrong, thus the definition of dimensions in units other than pixels leads to unpredictable results.
  3. Some browsers lack support of CSS at all.
  4. If the link to the cascading stylesheet is wrong, Netscape dislays just an error message about a missing stylesheet instead of the requested page. This bug is fixed in Mozilla. In case that Mozilla cannot find the stylesheet it behaves as if the stylesheet was not requested.
  5. The user's browser can have CSS switched off. The main reason is often correction of the above mentioned bug in Netscape.

Generally we must refuse formatting by CSS. On the other hand, cascading stylesheets offer useful possibilities some of which are not available in HTML. We can thus arrive at a compromise solution: the basic formatting that must be visible in all browsers must be accomplished by means of HTML markup which is further embellished by CSS. Pages built in this way can even be viewed by Lynx.

In the future the cascading stylesheet may also be literate programmed. For now it is written directly. You can look at its text version.

 Top of the page 

4. Choice of the XSLT procesor

These pages are created using the Saxon XSLT processor because it is one of the fastest and is written in Java which makes it work in all operating systems. The stylesheet was originally written for version 6.5.2 but recently has been modified to work with version 8 which supports XSLT 2.0. Several places in this document describe how it was done with XSLT 1.0.

 Top of the page 

5. Bootstrapping

The goal of literate programming is to keep the code and its documentation in sync. That is why everything is in one file. If we want to apply this method to programming the stylesheet we should create it in such a way that if run on itself the stylesheet generates its own documentation. In principle it could be done but the source file would be complicated and it would be easy to make errors in it. We therefore prepare a small stylesheet which takes webmake.xml as its source and generates a stylesheet for transformation into HTML from it. This transformation stylesheet will then be used not only for generation of the web pages but also for creation of the documentation of the stylesheet.

The stylesheet is nothing but an XML file, therefore it can be generated by transformation from another XML file. In order to do it we must use a kind of a trick. It is not sufficient to declare the XSLT namespace, we must declare an additional namespace which will be used as an alias later on.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:xslt="namespace-alias"
                version="2.0"
                xmlns:saxon="http://saxon.sf.net/"
                extension-element-prefixes="saxon">

The xsl: prefix belongs to the XSLT namespace, the xslt: prefix is assigned to an arbitrary namespace. The <xsl:namespace-alias> element declares that the elements with the xslt: prefix will have the xsl: prefix in the result file. Thus the templates can contain transformation instructions that will not be executed but written to the output file. The code will be explained later.

<xsl:namespace-alias stylesheet-prefix="xslt" result-prefix="xsl"/>
<xsl:output method="xml" indent="yes" encoding="utf-8" saxon:character-representation="native"/>
<xsl:strip-space elements="*"/>
<xsl:preserve-space elements="xslt:text xsl:text"/>
<xsl:param name="mode"/>
<xsl:template match="/">
  <xslt:stylesheet version="2.0" saxon:trace="no" exclude-result-prefixes="zw xs" extension-element-prefixes="saxon">
    <xsl:apply-templates select="//zw-pre"/>
  </xslt:stylesheet>
</xsl:template>
<xsl:template match="zw-pre">
  <xsl:if test="@mode=$mode or (not($mode) and not(@mode))">
    <xsl:apply-templates/>
  </xsl:if>
</xsl:template>
<xsl:template match="zw-hide">
  <xsl:apply-templates/>
</xsl:template>
<xsl:template match="*|@*">
  <xsl:copy>
    <xsl:apply-templates select="@*"/>
    <xsl:apply-templates/>
  </xsl:copy>
</xsl:template>
<xsl:template match="text()">
  <xsl:value-of select="." disable-output-escaping="yes"/>
</xsl:template>
<xsl:template match="processing-instruction()">
  <xsl:processing-instruction name="{name()}"><xsl:value-of select="."/></xsl:processing-instruction>
</xsl:template>
<xsl:template match="zw-text">
  <xslt:text>
    <xsl:value-of select="."/>
  </xslt:text>
</xsl:template>

The output format is always XML in the UTF-8 encoding. We ignore spaces in all elements except for <xsl:text> and <xslt:text>. The template selects only contents of the <zw-pre> elements no matter how deeply they are nested. Several stylesheets will be generated from a single source file. We will distinguish them by the global mode parameter and an attribute of the same name used in the <zw-pre> element. In the webmake.xml file, which is the source of the stylesheet, these elements must belong to the XSLT namespace, otherwise we would not get a working stylesheet. If we made elements dynamically by <xsl:element>, we would have to use the xsl: prefix in the templates, not the xslt: alias.

The <zw-hide> element is used for hiding the contents which we do not wish to display for security reason. Here we must copy its contents but the listing will be replaced with an alternative text as will be shown later.

When we transform the <text> element and processing instructions, we wish to preserve real characters and do not replace them with entities. We will therefore use the disable-output-escaping='yes' attribute.

The <zw-text> must be transformed into <xsl:text> and characters entities must be used inside it.

Notice that it is not necessary to declare the XSLT namespace in the <xslt:stylesheet> element because the XSLT processor will insert it automatically. More precisely, two identical namespaces for both prefixes xslt: and xsl: will be declared. We need not declare the Saxon namespace either since the XSLT processor will add it where it is required. And this is the very stumbling block. This namespace is required in the <xsl:output> element because it contains the saxon:character-representation attribute. It is not formally necessary in the <xslt:stylesheet> element. However, the extension-element-prefixes attribute requires that all prefixes are assigned to valid namespaces. We therefore add the saxon:trace='no' attribute although it is the default. It is nothing but a coercive device which forces the XSLT processor to insert the namespace declaration.

The following templates require extensions of the Saxon processor. The templates will not work with other XSLT processors.

The end of the file needs a closing tag.

</xsl:stylesheet>
 Top of the page 

6. Stylesheet for transformation into HTML

6.1. Modes
6.2. Language selection
6.3. Stylesheet prologue
6.4. Templates for a manual
6.5. Modification date and time
6.6. Sectioning
6.7. Templates for table of contents
6.8. Logo with a hyperlink
6.9. Hypertext links
6.10. JavaScript images
6.11. Comments
6.12. Hiding a part of the contents
6.13. Custom information
6.14. Processing instructions
6.15. Other elements
6.16. Templates for listing code examples
6.17. Listing the bootstraping stylesheet
6.18. Text nodes

In the previous part we generated the transformation stylesheet from the document which you are just reading. The stylesheet is quite long. Its description will therefore be subdivided into sections.

6.1. Modes

When transforming the document it will be nacessary to process some elements repeatedly in a different way. We will make use of the mode attribute. XSLT 1.0 allows to process the template in one mode only. If we need to perform the same action in several modes, we have to create a named template for one of the modes:

<xsl:template match="something" mode="foo" name="something-foo">
  <xsl:text>Do something here</xsl:text>
</xsl:template>

The templates for other modes will call the previous template by name:

<xsl:template match="something" mode="bar">
  <xsl:call-template name="something-foo"/>
</xsl:template>

XSLT 2.0 allows to use the template in several modes. The example can thus be rewritten as:

<xsl:template match="something" mode="foo bar">
  <xsl:text>Do something here</xsl:text>
</xsl:template>

Exact syntax can be found in the specification at http://www.w3.org/TR/xslt20/#modes.

 Top of the page 

6.2. Language selection

One of the main tasks is to divide the contents according to language. For this purpose the standard xml:lang attribute and lang() function can be used. This approach is advantageous if we have large parts of the document in the same language. However, we wish to interleave small pieces of text in various languages and moreover we want to have common parts which belong to all language versions. Making use of the standard would thus lead to difficulties.

Element <zw> was therefore invented for language selection. It was intended for bilingual pages only. It contained a simple condition and could not be nested.

Obsolete code
<xsl:template match="zw" name="zw">
  <xsl:if test="@lang=$DocumentLanguage">
    <xsl:apply-templates/>
  </xsl:if>
</xsl:template>

The new template for the <zw> element is multilingual and enables nesting. The element has two attributes: lang and notlang. Both of them may contain a list of language codes separated by spaces or commas. The notlang attribute specifies the list of languages in which the contents must not appear, the lang attribute selects languages in which the contents should appear. it makes little sense to use both attributes together but if it happens the notlang attribute has higher priority. If both attributes are missing, the contents of the <zw> element is treated as a comment which may contain any well formed markup including comments. The template must work identically in all modes.

<xsl:template match="zw" mode="#all">
  <xsl:choose>
    <xsl:when test="not(@lang) and not(@notlang)"/>
    <xsl:when test="contains(@notlang, $DocumentLanguage)">
      <xsl:apply-templates select="zw" mode="#current"/>
    </xsl:when>
    <xsl:when test="contains(@lang, $DocumentLanguage) or not(@lang)">
      <xsl:apply-templates mode="#current"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates select="zw" mode="#current"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

The WWW browser will select the language version by means of content negotiation implemented in a PHP script. Thus we must generate an index script for each document. We will do it with a simple stylesheet which processes the root node only. Notice that the PHP code is generated as text. The comment defines the place where the ISP puts an advertisement banner. It appears afer the program code so that the banner does not appear twice. As you will see later, the advertisement banner will be added as a part of processing the <document> element.

mode = index
<xsl:output method="text" indent="yes" encoding="utf-8"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
  <zw-text><?php require 'engine.php'; if ($inc) { include $fn; }
            else virtual($fn); exit; ?>
            <!--WZ-REKLAMA-1.0--> </zw-text>
</xsl:template>
 Top of the page 

6.3. Stylesheet prologue

The output method is html and we request indentation. Language selection requires knowledge of the code that will be specified on the command line.

<xsl:output method="html" indent="yes" encoding="utf-8"/>
<xsl:param name="DocumentLanguage"/>

The previous version made use of two tiny stylesheets which only set parameters of the respective language and included the main file by element <xsl:import href="xml2html.xsl"/>.

The XML document contains a lot of spaces which would spoil formatting of the resulting document, mainly the code listing of the stylesheet. Therefore we will ignore all spaces. We must, however, preserve spaces in the <xsl:text> element as well as some other inline elements.

<xsl:strip-space elements="*"/>
<xsl:preserve-space elements="xsl:text p i b li dt dd zw pre"/>

During further processing we will need the document name without directories and an extension. We know that the source document has always the .xml extension, therefore we store everything between the last slash and this extension into the global variable basename.

<xsl:variable name="basename">
  <xsl:analyze-string select="saxon:systemId()" regex="[^/]+\.xml$">
    <xsl:matching-substring>
      <xsl:value-of select="."/>
    </xsl:matching-substring>
  </xsl:analyze-string>
</xsl:variable>

Version for XSLT 1.0 contained:

Obsolete code
<xsl:variable name="basename" select="substring-before(saxon:tokenize(saxon:systemId(),'/')[last()],'.xml')"/>

All pages will refer to the main page and the cascading stylesheet. The pages should be independet of their location so that it is possible to test them locally. The URL must therefore be relative. It it thus necessary to determine the number of back steps. The base directory with the XML documents is .../xml/. We will therefore obtain the system identifier of the currently processed document and take everything after this string. We split it into parts by slashes. The directories will then be replaced with two dots and the result will be stored in global variable backdir.

<xsl:variable name="backdir">
  <xsl:analyze-string select="substring-after(saxon:systemId(), "/xml/")" regex="[^/]+/">
    <xsl:matching-substring>
      <xsl:text>../</xsl:text>
    </xsl:matching-substring>
  </xsl:analyze-string>
</xsl:variable>

Here is the code used in the old version:

Obsolete code
<xsl:variable name="backdir">
  <xsl:variable name="ntoks" select="count(saxon:tokenize(substring-after(saxon:systemId(), '/xml/'), '/'))"/>
  <xsl:if test="$ntoks > 1">
    <xsl:for-each select="saxon:range(2, $ntoks)">
      <xsl:text>../</xsl:text>
    </xsl:for-each>
  </xsl:if>
</xsl:variable>

The link to the home page is either the contents of the $backdir variable or ./.

<xsl:variable name="home">
  <xsl:choose>
    <xsl:when test="$backdir = ''">
      <xsl:text>./</xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$backdir"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable>

Finally we store the text "Main page" in the respective language. The file extension will always be .php.

<xsl:variable name="mainpage">
  <xsl:choose>
    <xsl:when test="$DocumentLanguage = 'cs'">
      <xsl:text>Hlavní stránka</xsl:text>
    </xsl:when>
    <xsl:when test="$DocumentLanguage = 'hi'">
      <xsl:text>मुख्य पृष्ठ</xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <xsl:text>Main page</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:variable>
<xsl:variable name="htmlext">
  <xsl:text>.php</xsl:text>
</xsl:variable>

Now the document can be processed. The template will make use of a simple trick. The root element in our format must be <document>. Verification that the document satisfies this condition is easy. The template will specify that other templates should be applied to the <document> element. If the root element is different, the resulting page will contain only empty <html> element which will be easily noticed. The processing instruction which is being generated at the beginning of the file is used for inputting the code for language selection by the method described in the preceding section.

<xsl:template match="/">
  <xsl:processing-instruction name="php">
if (!function_exists('FindLanguage')) {
require 'functions.php';
EmitHeader(basename($_SERVER['SCRIPT_NAME']));
}
Expires();
?</xsl:processing-instruction>
  <html>
    <xsl:apply-templates select="document"/>
  </html>
</xsl:template>
 Top of the page 

6.4. Templates for a manual

Offline manual is generated from some pages. Part of the contents makes sense on the web pages only, other part is useful for the manual only. We therefore build two templates which make possible to enter alternatives. it is assumed that they will be redefined in the stylesheet for generating the manual.

<xsl:template match="zw-online">
  <xsl:apply-templates/>
</xsl:template>
<xsl:template match="zw-offline"/>

These elements must not contain elements <section> and <title>, neither directly nor indirectly. In case of element <title> the solution is easy, switches <zw-online> and <zw-offline> can appear inside. For use with the <section> element the role attribute is prepared. This will be explained in sections "Sectioning" a "Templates for table of contents". Here we provide a variable containing the name of the role to be ignored.

<xsl:variable name="ignore-role">
offline</xsl:variable>
 Top of the page 

6.5. Modification date and time

XPath contains a device for determining current date and time but cannot find the date and time of file modification. In the old version of the stylesheet we used an external program which scanned the tree of the source documents and created an auxilliary file with required information. The name of the external file was entered as a parameter and its document node was stored in a variable.

Obsolete code
<xsl:param name="FileInfo" required="yes"/>
<xsl:variable name="filenode" select="document($FileInfo)/zw-fileinfo"/>

The above mentioned file contained only empty elements <zw-file> that had two attributes. The name attribute contained the file name, the lastmod attribute was the date and time of the last modification. The date and time were separated by a nonbreakable space. We defined a key for more efficient use.

Obsolete code
<xsl:key name="flastmod" match="zw-file" use="@name"/>

This template used to return modification date and time of a file given as a parameter. As default it returned modification date and time of the file containing the context node.

Obsolete code
<xsl:template name="flastmod">
  <xsl:param name="file">
    <xsl:value-of select="saxon:system-id()"/>
  </xsl:param>
  <xsl:for-each select="$filenode">
    <xsl:value-of select="key("flastmod", $file)/@lastmod"/>
  </xsl:for-each>
</xsl:template>

The <zw-datetime> element will write the modification date and time of a file making use of the package with the Saxon extension function. The file name is taken from the file attribute, or, in case of XML files, from the xmlfile attribute. If none of these attributes is given, the template will create the modification date and time of the file containing the context node. The template will be named so that it can be used later.

<xsl:template match="zw-datetime" name="zw-datetime">
  <xsl:choose use-when="function-available("zw:last-modified")">
    <xsl:when test="@file">
      <xsl:value-of select="zw:file-timestamp(@file)"/>
    </xsl:when>
    <xsl:when test="@xmlfile">
      <xsl:value-of select="zw:last-modified(document(@xmlfile))"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="zw:last-modified()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

Modificaton date will be obtained by stripping time from the value determined by means of the preceding template.

<xsl:template match="zw-date">
  <xsl:variable name="datetime">
    <xsl:call-template name="zw-datetime"/>
  </xsl:variable>
  <xsl:value-of select="substring-before($datetime, "T")"/>
</xsl:template>
 Top of the page 

6.6. Sectioning

We perform three actions during processing the document. first, inside the header, we generate the page title which will be dispayed by the browser. In the second step we create the link to the cascading stylesheet. Finally we process the body of the document. Each of these steps will be executed by a separate template which will be invoked by <xsl:call-template>. We do it purely for the reason of clarity because each of these templates is used in one place only.

<xsl:template match="document">
  <head>
    <meta name="google-site-verification" content="pQX0Li9frYMCGMBkoPTHoG3lgeK_Gx9i-BO8-jcjn18"/>
    <meta name="viewport" content="width=device-width, initial-scale=1"/>
    <meta name="http-equiv" content="{concat('Content-Language: ', $DocumentLanguage)}"/>
    <xsl:call-template name="doc-title"/>
    <xsl:if test="@css">
      <xsl:call-template name="doc-css"/>
    </xsl:if>
    <xsl:if test="@redirect">
      <meta http-equiv="refresh" content="{concat('1; ', @redirect)}"/>
    </xsl:if>
    <script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','https://www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-98691706-1', 'auto'); ga('send', 'pageview');    </script>
  </head>
  <xsl:call-template name="doc-body"/>
</xsl:template>

The title which should be displayed on the titlebar will be taken from the <title> element. This element is, however, primarily used as a page title which may not be suitable for the titlebar. We would therefore prefer the <titlebar> element if it exists. Notice the mode='titlebar' attribute in the <xsl:apply-templates> element. Its meaning will be explained later.

This is also a good place for insertion of othe contents into the <head> element. We will insert the contents of all elements of that name.

<xsl:template name="doc-title">
  <title>
    <xsl:choose>
      <xsl:when test="count(titlebar)>0">
        <xsl:apply-templates select="titlebar" mode="titlebar"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:apply-templates select="title" mode="titlebar"/>
      </xsl:otherwise>
    </xsl:choose>
  </title>
  <xsl:apply-templates select="head" mode="title"/>
</xsl:template>

Processing of the document body is easy. First we copy all attributes of the <document> element except of top and css. Then we display the title. We attach label top to it so that we can later generate the links to the top of the page. The template for processing the <title> element is called with the mode='title' attribute. Afterwards templates are applied to all elements. We must, however, send them the hdr parameter which will be used in the template for the <section> element. If the value of the top attribute is positive, we also call the template for generating the link to the top of the page which will be defined later. The bottom of the page will hold a standard footer.

<xsl:template name="doc-body">
  <body>
    <xsl:copy-of select="@* except (@top|@css)"/>
    <h1 id="top">
      <xsl:apply-templates select="title" mode="title"/>
    </h1>
    <xsl:apply-templates>
      <xsl:with-param name="hdr" select="2"/>
    </xsl:apply-templates>
    <xsl:if test="@top > 0">
      <xsl:call-template name="top-link"/>
    </xsl:if>
    <xsl:call-template name="footer"/>
  </body>
</xsl:template>

First we leave a little empty space, otherwise the optional link to the top of the page could collide with the footer. Afterwards we make sure whether we are not on the main page. In such a case we would display just a horizontal line. If we are not in the main directory, we call a template for adding links defined in the <zw-a> elements. Afterwards we put the link to the main document of the directory. Its name will be retrieved from the <title> element processed in the titlebar mode. Further we put the copyright and the date of the last modification making use of already defined template. At the very end of the page we insert the links to the other language versions in order not to confuse indexing robots. The cascading stylesheet will move them to the top of the page. The comment <!--WZ-REKLAMA-1.0--> specifies a location where the server automatically inserts an advertisement banner. It can be followed by another advertisement which is read from a document the URL of which is defined in the reklama attribute.

<xsl:template name="footer">
  <br/>
  <br/>
  <br/>
  <xsl:variable name="urlbase" select="concat($backdir, $basename)"/>
  <xsl:choose>
    <xsl:when test="$urlbase = 'index.xml'">
      <hr/>
    </xsl:when>
    <xsl:otherwise>
      <div class="links">
        <xsl:call-template name="other-links"/>
        <xsl:if test="starts-with($urlbase, '../') and $basename != 'index.xml'">
          <a href="./">
            <xsl:attribute name="title">
              <xsl:choose>
                <xsl:when test="count(document('index.xml', /)/document/titlebar) > 0">
                  <xsl:apply-templates select="document("index.xml", /)/document/titlebar" mode="titlebar"/>
                </xsl:when>
                <xsl:otherwise>
                  <xsl:apply-templates select="document("index.xml", /)/document/title" mode="titlebar"/>
                </xsl:otherwise>
              </xsl:choose>
            </xsl:attribute>
            <xsl:apply-templates select="document("index.xml", /)/document/title" mode="titlebar"/>
          </a>
          <br/>
        </xsl:if>
        <a title="{$mainpage}" href="{$home}">
          <xsl:value-of select="$mainpage"/>
        </a>
      </div>
    </xsl:otherwise>
  </xsl:choose>
  <div id="copyright">
    <xsl:text>&#169; Z. Wagner - Ice Bear Soft, </xsl:text>
    <xsl:value-of select="zw:last-modified()" use-when="function-available("zw:last-modified")"/>
  </div>
  <xsl:text>
</xsl:text>
  <xsl:processing-instruction name="php"> LangLinks(); Counter(); ?</xsl:processing-instruction>
  <xsl:comment>WZ-REKLAMA-1.0</xsl:comment>
  <xsl:if test="@reklama">
    <hr/>
    <xsl:apply-templates select="document(@reklama)" mode="include"/>
  </xsl:if>
</xsl:template>

This template inserts the contents of all <zw-a> elements processed in the footer mode.

<xsl:template name="other-links">
  <xsl:apply-templates select="//zw-a" mode="footer"/>
</xsl:template>

The <zw-a> element has almost the same syntax as the <a> element. The only difference is that the title attribute of the <a> element can be given in the <title> element so that we can enter several language versions. In the footer mode the <zw-a> element will therefore be replaced with the <a> element followed by the line break, in the default mode it will be ignored. However, if the contents of the <zw-a> element is empty, we fill it in together with the title attribute from the title of the referenced document. When inserting the link we take into account the language version according to the attributes lang and notlang.

<xsl:template match="zw-a" mode="footer">
  <xsl:choose>
    <xsl:when test="@notlang eq $DocumentLanguage"/>
    <xsl:when test="@lang ne '' and @lang ne $DocumentLanguage"/>
    <xsl:otherwise>
      <xsl:call-template name="zw-a"/>
      <br/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
<xsl:template name="zw-a">
  <a>
    <xsl:copy-of select="@*"/>
    <xsl:call-template name="zw-a-content"/>
  </a>
</xsl:template>
<xsl:template match="zw-a"/>
<xsl:template name="zw-a-content">
  <xsl:choose>
    <xsl:when test="count(*) > 0 or string-length(.) > 0">
      <xsl:if test="title">
        <xsl:attribute name="title">
          <xsl:apply-templates select="title" mode="titlebar"/>
        </xsl:attribute>
      </xsl:if>
      <xsl:apply-templates select="*|text() except title"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:choose>
        <xsl:when test="contains(@href, "#")">
          <xsl:variable name="xml" as="xs:string" select="if (starts-with(@href, "#")) then @href else replace(replace(@href, "/#", "/index.php#"), "\.php#", ".xml#")"/>
          <xsl:variable name="root" select="if (starts-with(@href, "#")) then (/) else document(substring-before($xml, "#"), /)"/>
          <xsl:variable name="title" as="element()" select="$root//section[@label=substring-after($xml,"#")]/title"/>
          <xsl:attribute name="title">
            <xsl:apply-templates select="$title" mode="titlebar"/>
          </xsl:attribute>
          <xsl:apply-templates select="$title" mode="title"/>
        </xsl:when>
        <xsl:otherwise>
          <xsl:variable name="xml" as="xs:string" select="replace(replace(@href, "/$", "/index.php"), "\.php$", ".xml")"/>
          <xsl:attribute name="title">
            <xsl:choose>
              <xsl:when test="count(document($xml, /)/document/titlebar) > 0">
                <xsl:apply-templates select="document($xml, /)/document/titlebar" mode="titlebar"/>
              </xsl:when>
              <xsl:otherwise>
                <xsl:apply-templates select="document($xml, /)/document/title" mode="titlebar"/>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:attribute>
          <xsl:apply-templates select="document($xml, /)/document/title" mode="nologo"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

In the preceding templates we have explicitly called templates for the <title>, <titlebar> and <head> elements. These elements must be ignored when procesing the document body. We therefore create two templates. The first of them will contain the mode='title titlebar' attribute. The template will process nested elements in the same mode because the templates for nested elements may also differ. The second template which will be invoked in the standard mode will be empty so that the elements will be ignored.

<xsl:template match="title|titlebar|head" mode="title titlebar nologo">
  <xsl:apply-templates mode="#current"/>
</xsl:template>
<xsl:template match="title|titlebar|head"/>

Creation of the link to the cascading stylesheet is more involved. If the css attribute of the <document> element contains a URL, it is simple. The problem arises if it containt a period instead of the URL. We then must evaluate the name and location of the standard stylesheet. The URL must be relative so that the pages do not depend upon particular location and can be tested locally. We must therefore insert correct number of back steps from the global backdir variable and add the stylesheet name.

<xsl:template name="doc-css">
  <xsl:for-each select="tokenize(@css, '\s+')">
    <xsl:variable name="css">
      <xsl:choose>
        <xsl:when test=".='.'">
          <xsl:value-of select="$backdir"/>
          <xsl:text>css/style.css</xsl:text>
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="."/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <link rel="stylesheet" href="{$css}" type="text/css"/>
  </xsl:for-each>
  <xsl:variable name="favicon" select="concat($backdir, 'images/favicon.ico')"/>
  <link rel="shortcut icon" type="image/x-icon" href="{$favicon}"/>
</xsl:template>

We will need the number of a section in several templates. We herefore create a template which will be reused. The <xsl:number> element will number the sections hierarchically if the level='multiple' attribute is used.

<xsl:template name="secnum">
  <xsl:number level="multiple" format="1." count="section[not(@role) or @role != $ignore-role]"/>
</xsl:template>

Similarly we create a reusable template for generating a label. If we wish to refer to a section from another place, we must spedify the label in the label attribute of the <section> element. If we require the label in the automatically generated table of contents, we let the template create a unique identifier.

<xsl:template name="seclabel">
  <xsl:choose>
    <xsl:when test="@label">
      <xsl:value-of select="@label"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="generate-id()"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

The contents of the element will be processed only if it is not prohibitted by the role attribute (see Templates for a manual). First we store the label value to the seclabel variable so that we could work with it more comfortably. Afterwards we have to create the element contaning the title. The title level can be obtained from the hdr parameter. Remember that the template for the <document> element set the value of this parameter to 2 when calling other templates. The <section> element of the highest level will thus have its title in the <h2> element. The title will have a unique label and will be automatically numbered. Notice that the template for the <title> element is again called in the title mode. When calling the templates for other elements the value of the hdr parameter must be incremented so that nested sections use correct elements for their titles. The root element <document> may contain the top attribute specifying the deepest level of the title of the <section> element where the link to the beginning of the page should be generated. The title level is stored in the value of the hdr parameter. Several nested sections may end at the same place. In such a case we would get several consecutive links to the top of the page. We therefore apply the count(following-sibling::node()) > 0 to ensure that we have some nodes after the end of section. White spaces are ignored because we have specfied <xsl:strip-space> everywhere but in the <xsl:text> element.

<xsl:template match="section">
  <xsl:param name="hdr"/>
  <xsl:if test="not(@role) or @role != $ignore-role">
    <xsl:variable name="label">
      <xsl:call-template name="seclabel"/>
    </xsl:variable>
    <xsl:element name="{concat('h', string($hdr))}">
      <xsl:attribute name="id" select="$label"/>
      <xsl:call-template name="secnum"/>
      <xsl:text> </xsl:text>
      <xsl:apply-templates select="title" mode="title"/>
    </xsl:element>
    <xsl:apply-templates>
      <xsl:with-param name="hdr" select="$hdr + 1"/>
    </xsl:apply-templates>
    <xsl:if test="/document/@top>=$hdr and count(following-sibling::element()[name()!='zw-a'] except following-sibling::section[@role=$ignore-role]/descendant-or-self::element())>0">
      <xsl:call-template name="top-link"/>
    </xsl:if>
  </xsl:if>
</xsl:template>

The title may contain a hyperlink which should be preserved. We will thus make use of a template for <zw-a> which works in the required mode.

<xsl:template match="a" mode="title">
  <xsl:call-template name="zw-a"/>
</xsl:template>
<xsl:template name="top-link">
  <xsl:variable name="top">
    <xsl:text>&#160;</xsl:text>
    <xsl:call-template name="top"/>
    <xsl:text>&#160;</xsl:text>
  </xsl:variable>
  <div>
    <table class="top" align="right" border="1" frame="box" rules="none">
      <tr>
        <td>
          <a href="#top" title="{$top}">
            <xsl:value-of select="$top"/>
          </a>
        </td>
      </tr>
    </table>
  </div>
</xsl:template>

The text which will be displayed in the link to the top of the page will be created for clarity in a separate template.

<xsl:template name="top">
  <xsl:choose>
    <xsl:when test="$DocumentLanguage='cs'">
      <xsl:text>Začátek&#160;stránky</xsl:text>
    </xsl:when>
    <xsl:when test="$DocumentLanguage='en'">
      <xsl:text>Top&#160;of&#160;the&#160;page</xsl:text>
    </xsl:when>
    <xsl:otherwise>
      <xsl:text>Top&#160;of&#160;the&#160;page</xsl:text>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
 Top of the page 

6.7. Templates for table of contents

First we will describe an auxiliary template. Notice that the template has the match atribute, so that it could be used for processing the <toc-heading> element, as well as the name attribute, so that it could be called by name from another template.

<xsl:template name="toc-heading" match="toc-heading">
  <p>
    <b>
      <xsl:choose>
        <xsl:when test="$DocumentLanguage='cs'">
          <xsl:text>Obsah</xsl:text>
        </xsl:when>
        <xsl:when test="$DocumentLanguage='en'">
          <xsl:text>Contents</xsl:text>
        </xsl:when>
        <xsl:when test="$DocumentLanguage='hi'">
          <xsl:text>विषय-सूची</xsl:text>
        </xsl:when>
        <xsl:otherwise>
          <xsl:text>Contents</xsl:text>
        </xsl:otherwise>
      </xsl:choose>
    </b>
  </p>
</xsl:template>

The <toc> element serves for displaying the table of contents both at the beginning of the page and inside a particular section. However, if the section does not contain any nested subsections, the element should be ignored. We must therefore first find whether the element, in which <toc> appears, contains any sections. It could be easily verified by the count(../section) > 0 condition. We will, however, need also the maximum nesting level. We determine it by means of an XPath 2.0 expression. The for loop will create a sequence of all <section> elements which do not contain any other nested section and count the numbers of their ancestors. We also added the root node so that the sequence was not empty if the current section did not contain any nested section. On the other hand we removed sections which have to be ignored (see Templates for a manual). We find the maximum number of ancestors and subtract the number of ancestors of the current section.If the nesting level is positive, the table of contents will be displayed. First we process the contents of the <toc> element. If the contents of the element is empty and the value of the heading attribute is yes, we call the toc-heading template. Finally we apply templates to the paren element <section>. Notice that the templates will be applied in the toc mode and the value of the depth variable is supplied in the span parameter.

<xsl:template match="toc">
  <xsl:variable name="depth">
    <xsl:value-of select="max(for $s in ((..//section[not(section)]|/) except (//element()[role=$ignore-role]/descendant-or-self::element())) return count($s/ancestor::element())) - count(../ancestor::element())"/>
  </xsl:variable>
  <xsl:if test="$depth > 0">
    <xsl:choose>
      <xsl:when test="count(*) > 0">
        <xsl:apply-templates/>
      </xsl:when>
      <xsl:when test="@heading='yes'">
        <xsl:call-template name="toc-heading"/>
      </xsl:when>
    </xsl:choose>
    <table border="0" frame="none" rules="none" class="toc">
      <xsl:apply-templates select="../section" mode="toc">
        <xsl:with-param name="span" select="$depth"/>
      </xsl:apply-templates>
    </table>
  </xsl:if>
</xsl:template>

Old method made use of XPath 1.0 and Saxon extension. We began with the ../section expression and step by step appended /section within a <saxon:while> loop while incrementing the depth variable. In these days offline manuals were not automatically generated, therefore the <section> element did not have the role attribute.

Obsolete code
<xsl:variable name="depth" select="0" saxon:assignable="yes"/>
<xsl:variable name="path" saxon:assignable="yes">
  <xsl:text>../section</xsl:text>
</xsl:variable>
<saxon:while test="count(saxon:evaluate($path)) > 0">
  <saxon:assign name="depth" select="$depth + 1"/>
  <saxon:assign name="path">
    <xsl:value-of select="concat($path, '/section')"/>
  </saxon:assign>
</saxon:while>

Now we are going to explain the code which inserts information to the table of contents. If there are nested sections, we would like to have them properly indented. It could be reached by a listing environment in HTML. Unfortunaly, <ol> will number the first level, mark the second level with letters and will lose hierarchical numbering. It could be solved by a cascading stylesheet, bat as was already mentioned, cascading stylesheets are nor correctly implemented in all browsers and are even not implemented at all in some of them. We must therefore generate the table of contents in such a way that it is viewable even in a browser which does not support cascading stylesheets. The preceding template thus started a table for the contents. In order to indent the titles of the nested sections we must insert an empty cell spanning the correct number of column at the beginning of the row. The next column contains the right justified number and finally we put the section title spanning over the remaining columns. The title is retrieved fron the <title> element but its template must be invoked in the toc mode. The number as well as the label will be obtained by templates which have already been used and explained.

At the first call the span parameter got the value of the maximum nesting, the indent parameter had the default zero value. After the row is written, we process a nested <section> element whence indent is increased and span is decreased. Recursion will continue to the point when we will process a nested section with a zero value of the span parameter. The table will then go haywire. Fortunately this cannot happen. We have calculated the maximum nesting level in advance and thus we know that such a section does not exist in the document. It is not necessary to use a condition inside the template because <xsl:apply-templates> finds no element for the zero value of the span parameter.

Similarly as in section "Sectioning" we process only elements not prohibited by the role attribute.

<xsl:template match="section" mode="toc">
  <xsl:param name="indent" select="0"/>
  <xsl:param name="span" select="-1"/>
  <xsl:if test="not(@role) or @role != $ignore-role">
    <xsl:variable name="label">
      <xsl:call-template name="seclabel"/>
    </xsl:variable>
    <tr>
      <xsl:if test="$indent > 0">
        <td>
          <xsl:if test="$indent > 1">
            <xsl:attribute name="colspan">
              <xsl:value-of select="$indent"/>
            </xsl:attribute>
          </xsl:if>
          <xsl:text>&#160;</xsl:text>
        </td>
      </xsl:if>
      <td align="right">
        <a href="{concat('#', $label)}">
          <xsl:call-template name="secnum"/>
        </a>
      </td>
      <td>
        <xsl:if test="$span > 1">
          <xsl:attribute name="colspan">
            <xsl:value-of select="$span"/>
          </xsl:attribute>
        </xsl:if>
        <a href="{concat('#', $label)}">
          <xsl:apply-templates select="title" mode="toc"/>
        </a>
      </td>
    </tr>
    <xsl:apply-templates select="section" mode="toc">
      <xsl:with-param name="indent" select="$indent + 1"/>
      <xsl:with-param name="span" select="$span - 1"/>
    </xsl:apply-templates>
  </xsl:if>
</xsl:template>

Hyperlinks will be ignored in the toc mode, only their contents will be preserved.

<xsl:template match="a" mode="toc">
  <xsl:call-template name="zw-a-content"/>
</xsl:template>
 Top of the page 

6.8. Logo with a hyperlink

All pages should clearly identify to whom they belong. We will therefore wish to have a logo with a hyperlink to the main page in the upper right corner. We will create a useful template.

<xsl:template match="zw-logo" mode="#default title">
  <a title="{$mainpage}" href="{$home}">
    <img src="{concat($backdir, 'images/ibslogo1.gif')}" width="175" height="45" alt="Ice Bear Soft" align="right" border="0"/>
  </a>
</xsl:template>

The <zw-logo> element will be used at the beginning of the <title> element. We will therefore ignore it in the titlebar mode.

<xsl:template match="zw-logo" mode="titlebar nologo"/>
 Top of the page 

6.9. Hypertext links

We often refer to our own documents and we would like to let the title attribute as well as the text fill in from the title of the corresponding document. The <a> element will therefore be processed by the template used previously for the <zw-a> element in the footer mode. Auxiliary element <zw-label> will be handled the same way.

<xsl:template match="a|zw-label">
  <xsl:call-template name="zw-a"/>
</xsl:template>
 Top of the page 

6.10. JavaScript images

Some images will be displayed in a separate window using an external JavaScript function. Code for its invocation will be generated in PHP. The template will therefore output a processing instruction.

<xsl:template match="zw-jsimg">
  <xsl:processing-instruction name="php"><xsl:text>jsimg('</xsl:text>
<xsl:value-of select="@src"/><xsl:text>', '</xsl:text>
<xsl:value-of select="@align"/><xsl:text>');</xsl:text>
?</xsl:processing-instruction>
</xsl:template>

The jsimg function relies on images.js and contains:

# JavaScript images
function jsimg($src, $align) {
if ($align) $align = "align=\"$align\"";
$sz = GetImageSize($src);
$thsrc = '';
if (preg_match('/^(.+)(\.[^.]+)$/', $src, $parts)) {
  $thsrc = $parts[1] . '_small' . $parts[2];
  $thsz = GetImageSize($thsrc);
?>
<script language="JavaScript">
<!--
<?php printf("img('%s', %d, %d, '%s', %d, %d, '%s')",
             $thsrc, $thsz[0], $thsz[1], $src, $sz[0], $sz[1], $align); ?>
// -->
</script>
<noscript>
<a href="<?php echo $src; ?>" target="image">
<img src="<?php echo $thsrc; ?>" hspace=5 vspace=1 width=<?php
     echo $thsz[0]; ?> height=<?php printf('%d %s', $thsz[1], $align); ?>>
</a></noscript>
<?php
}}
 Top of the page 

6.11. Comments

We sometimes need to write a source text which should be output as a comment. We thes create a <zw-comment> element. It can be useful for instance for generation of the server side includes directives.

<xsl:template match="zw-comment">
  <xsl:comment><xsl:apply-templates/></xsl:comment>
</xsl:template>
 Top of the page 

6.12. Hiding a part of the contents

We do not wish to display some elements for security reasons. We will therefore enclose them into the <zw-hide> element and supply an alternative text in the alt attribute.

<xsl:template match="zw-hide">
  <span class="hide">
    <xsl:value-of select="@alt"/>
  </span>
</xsl:template>
 Top of the page 

6.13. Custom information

We sometimes wish to attach some special information to the part of a document but do not like to make use of a comment. It would be useful to have the possibility of marking a part of a document in a simple way so that we could decide whether the contents should appear in the HTML page. We will therefore define the <zw-info> element which can have arbitrary attributes with arbitrary contents. If the include attribute has the yes value, the contents of the element will be processed. If the value is different or the attribute is missing, the contents will be ignored.

<xsl:template match="zw-info">
  <xsl:if test="@include = "yes"">
    <xsl:apply-templates/>
  </xsl:if>
</xsl:template>
 Top of the page 

6.14. Processing instructions

PHP Processing instructions will be just copied but with appended question mark at the end.

<xsl:template match="processing-instruction("php")">
  <xsl:processing-instruction name="php"><xsl:value-of select="."/><xsl:text>?</xsl:text>
</xsl:processing-instruction>
</xsl:template>

Other processing instructions will be ignored.

<xsl:template match="processing-instruction()" mode="#all"/>
 Top of the page 

6.15. Other elements

All other element without explicit templates will be just copied with their attributes. We do not wish to use <xsl:copy> because the element would contain the declarations of all namespaces. The same action will be performed both in the standard and title modes.

<xsl:template match="*" mode="#default title nologo">
  <xsl:element name="{name()}">
    <xsl:apply-templates select="@*"/>
    <xsl:apply-templates mode="#current"/>
  </xsl:element>
</xsl:template>

The attributes can be easily copied by means of <xsl:copy-of>.

<xsl:template match="@*">
  <xsl:copy-of select="."/>
</xsl:template>

Some templates will include whole documents but the headers must not be generated. We will therefore make use of the include mode.

<xsl:template match="/" mode="include">
  <xsl:apply-templates/>
</xsl:template>

Finally we show the link to the pages with the protest against SW patents.

<xsl:template match="zw-nopatents" mode="#default title">
  <xsl:variable name="title">
    <xsl:choose>
      <xsl:when test="$DocumentLanguage = "cs"">
        <xsl:text>Žádné softwarové patenty!</xsl:text>
      </xsl:when>
      <xsl:otherwise>
        <xsl:text>No software patents!</xsl:text>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>
  <a href="http://noepatents.eu.org" title="{$title}">
    <img width="480" height="60" src="{concat($backdir, 'images/swpatbanner.en.png')}">
      <xsl:copy-of select="@*"/>
    </img>
  </a>
</xsl:template>
 Top of the page 

6.16. Templates for listing code examples

In this document we often mention some elements. It would be uncomfortable to write the <code> elements with the angle brackets encoded as entities &lt; and &gt; and close it with the </code> tag. We therefore create an auxiliary template <zw-elem> for any element. The element can optionally have a q attribute the only meaningful value of which is a slash. Letter q was chosen because it is at the edge of the keyboard.

<xsl:template match="zw-elem" mode="#default title toc nologo">
  <xsl:call-template name="zw-elem">
    <xsl:with-param name="prefix">
      <xsl:value-of select="@q"/>
    </xsl:with-param>
  </xsl:call-template>
</xsl:template>

We also often mention elements with the xsl: prefix. It would be unconfortable to write it explicitly, we therefore create an <zw-xelem> element which will do the job for us.

<xsl:template match="zw-xelem" mode="#default title toc nologo">
  <xsl:call-template name="zw-elem">
    <xsl:with-param name="prefix">
      <xsl:value-of select="@q"/>
      <xsl:text>xsl:</xsl:text>
    </xsl:with-param>
  </xsl:call-template>
</xsl:template>

The whole job is done by the following template. The preceding templates send the prefix parameter which can contain a slash and/or the xsl; prefix.

<xsl:template name="zw-elem">
  <xsl:param name="prefix"/>
  <code>
    <xsl:text>&lt;</xsl:text>
    <xsl:value-of select="$prefix"/>
    <xsl:apply-templates/>
    <xsl:text>&gt;</xsl:text>
  </code>
</xsl:template>

The stylesheet for bootstraping generated a stylesheet for transformation into HTML from the code of the <zw-pre> elements. We now display the contents of these elements as the code samples. The cascading stylesheet will define their yellow background. Under some circumstances the lines may exceed the width of the browser window. The background-color property then does not work correctly. It seems that the background of a table is colored always correctly. The contents of the <zw-pre> element is thus displayed in a one-row one-column table. The browser which does not support cascading stylesheets will at least frame it. Only in case that the <zw-pre> element has the lang attribute a title is added to the table.

Now we cannot use the <pre> element in the document because formatting of this example would turn into garbage. Instead of it we introduce a <verbatim> synonym. This new element differs from <zw-pre> mainly because its contents will not appear in the generated stylesheet. The line edn inserted at several places is just for aesthetics. The only important thing is that nested elements are processed in the verbatim mode.

<xsl:template match="zw-pre|verbatim" name="verbatim">
  <xsl:text>&#10;</xsl:text>
  <table border="2" frame="box" rules="rows" cellpadding="5" class="pre">
    <xsl:if test="@mode">
      <tr>
        <th>
          <xsl:choose>
            <xsl:when test="@mode="old"">
              <xsl:choose>
                <xsl:when test="$DocumentLanguage='cs'">
                  <xsl:text>Zastaralý kód</xsl:text>
                </xsl:when>
                <xsl:when test="$DocumentLanguage='en'">
                  <xsl:text>Obsolete code</xsl:text>
                </xsl:when>
                <xsl:otherwise>
                  <xsl:text>Obsolete code</xsl:text>
                </xsl:otherwise>
              </xsl:choose>
            </xsl:when>
            <xsl:otherwise>
              <xsl:text>mode = </xsl:text>
              <xsl:value-of select="@mode"/>
            </xsl:otherwise>
          </xsl:choose>
        </th>
      </tr>
    </xsl:if>
    <tr>
      <td>
        <pre>
          <xsl:if test="name() != 'verbatim'">
            <xsl:text>&#10;</xsl:text>
          </xsl:if>
          <xsl:apply-templates mode="verbatim"/>
        </pre>
      </td>
    </tr>
  </table>
  <xsl:text>&#10;</xsl:text>
</xsl:template>

Now we process all elements. Since HTML requires all angle brackets be converted to character entities, the XSLT processor will not consider then as elements and indentation must be therefore inserted by us. The value of indentation is stored in the indent parameter. Its value is initially empty. The element name is written after the entity representing the opening bracket and afterwards we call a template for attributes, of course in the verbatim mode. If the element is empty, we close i by a slash and a closing bracket, otherwise we call a template for nested elements with increased indentation. Finally we output a closing tag.

<xsl:template match="*" mode="verbatim">
  <xsl:param name="indent"/>
  <xsl:param name="amount">
    <xsl:text>  </xsl:text>
  </xsl:param>
  <xsl:param name="endline">
    <xsl:text>&#10;</xsl:text>
  </xsl:param>
  <xsl:value-of select="$indent"/>
  <xsl:text>&lt;</xsl:text>
  <xsl:value-of select="name()"/>
  <xsl:apply-templates select="@*" mode="verbatim"/>
  <xsl:choose>
    <xsl:when test="count(*)=0 and string-length(.)=0">
      <xsl:text>/&gt;</xsl:text>
      <xsl:value-of select="$endline"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:text>&gt;&#10;</xsl:text>
      <xsl:apply-templates mode="verbatim">
        <xsl:with-param name="indent">
          <xsl:value-of select="concat($indent, $amount)"/>
        </xsl:with-param>
      </xsl:apply-templates>
      <xsl:value-of select="$indent"/>
      <xsl:text>&lt;/</xsl:text>
      <xsl:value-of select="name()"/>
      <xsl:text>&gt;</xsl:text>
      <xsl:value-of select="$endline"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

the contents of the <pre> element will be displayed analogically but the original formatting will be preserved. The element is listed in <xsl:preserve-spaces>.

<xsl:template match="pre">
  <table border="2" frame="box" rules="rows" cellpadding="5" class="pre">
    <tr>
      <td>
        <pre>
          <xsl:apply-templates/>
        </pre>
      </td>
    </tr>
  </table>
</xsl:template>

The templates for <xsl:text> and <zw-text> elements, for <xsl:processing-instruction> and <xsl:comment> are slightly different. We want to keep the element with its contents on a single line.

<xsl:template match="xsl:text|zw-text|xsl:comment|xsl:processing-instruction" mode="verbatim">
  <xsl:param name="indent"/>
  <xsl:value-of select="$indent"/>
  <xsl:text>&lt;</xsl:text>
  <xsl:value-of select="name()"/>
  <xsl:apply-templates select="@*" mode="verbatim"/>
  <xsl:text>&gt;</xsl:text>
  <xsl:choose>
    <xsl:when test="count(*)=0">
      <xsl:value-of select="."/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:apply-templates mode="verbatim">
        <xsl:with-param name="endline"/>
      </xsl:apply-templates>
    </xsl:otherwise>
  </xsl:choose>
  <xsl:text>&lt;/</xsl:text>
  <xsl:value-of select="name()"/>
  <xsl:text>&gt;&#10;</xsl:text>
</xsl:template>

Processing of the attributes is easy. We write the attribute name, equal sign and the value in quotes.

<xsl:template match="@*" mode="verbatim">
  <xsl:text> </xsl:text>
  <xsl:value-of select="name()"/>
  <xsl:text>="</xsl:text>
  <xsl:value-of select="normalize-space(.)"/>
  <xsl:text>"</xsl:text>
</xsl:template>

We request normalization of spaces in text nodes.

<xsl:template match="text()" mode="verbatim">
  <xsl:value-of select="normalize-space(.)"/>
</xsl:template>
 Top of the page 

6.17. Listing the bootstraping stylesheet

The relative path to the bootstraping stylesheet is defined in the document as an extern entity named bootstrap. The stylesheet is read in the Bootstrapping section by using &bootstrap;. Its root element is <xsl:stylesheet>. The following template will process the file contents by means of the same template as was used for <zw-pre>. Notice that the root element will not appear in the listing. It is the only element which was written in the manual by hand.

<xsl:template match="xsl:stylesheet">
  <xsl:call-template name="verbatim"/>
</xsl:template>
 Top of the page 

6.18. Text nodes

We want to insert nonbreakable spaces after one-letter prepositions in the texts in the Czech language. Unfortunatelly you cannot see the nonbreakable space after $1 and it is not apparent that the last square bracket in the second parameter of the replace function contains [ &#x0d;&#x0a;].

<xsl:template match="text()">
  <xsl:variable name="lang" as="node()*" select="(ancestor-or-self::*/@xml:lang | ancestor-or-self::*/@lang)[last()]"/>
  <xsl:choose>
    <xsl:when test="$lang = 'cs'">
      <xsl:value-of select="replace(., '([ (,][AaIiKkOoSsUuVvZz])[ ]+', '$1 ')"/>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="."/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

The nonbreakable spaces must not be inserted in verbatim-like elements.

<xsl:template match="zw-pre/text()|pre/text()|code/text()|verbatim/text()">
  <xsl:value-of select="."/>
</xsl:template>
 Top of the page 

7. Stylesheet for manuals

7.1. Modified templates for manuals
7.2. Logo with a hyperlink for manuals
7.3. Processing the root element
7.4. Postprocessing
7.5. Reference to the cascading stylesheet
7.6. Page footer

The stylesheet for manuals imports the stylesheet for transformation into HTML and redefines a few templates and variables.

mode = offline
<xsl:import href="xml2html.xsl"/>
<xsl:variable name="ignore-role">
online</xsl:variable>

7.1. Modified templates for manuals

Explanation of these templates has already appeared in section "Templates for a manual". The modified versions with exchanged roles are defined here.

mode = offline
<xsl:template match="zw-offline">
  <xsl:apply-templates/>
</xsl:template>
<xsl:template match="zw-online"/>
 Top of the page 

7.2. Logo with a hyperlink for manuals

Hypertext link connected with the logo must point to the page on internet. Logo itself must be distributed with the manual so that it can be viewed offline. Its name will thus be given in a parameter.

mode = offline
<xsl:variable name="ibslogo">
ibslogo1.gif</xsl:variable>
<xsl:variable name="home">
http://icebearsoft.euweb.cz/</xsl:variable>
<xsl:template match="zw-logo" mode="#default title">
  <a title="{$mainpage}" href="{$home}">
    <img src="{$ibslogo}" width="175" height="45" alt="Ice Bear Soft" align="right" border="0"/>
  </a>
</xsl:template>
 Top of the page 

7.3. Processing the root element

The manual is always pure HTML without any PHP code. We will therefore create the <html> element and process the <document> element inseide it. Hyperlinks may be created by various mechanisms. In order to convert relative hyperlinks, refering to other pages, into the absolute ones, we store the result of transformation into a variable and transform it again. We assume that images and cascading stylesheets are included so that their references need not be modified.

mode = offline
<xsl:template match="/">
  <xsl:variable name="offline-document" as="element()">
    <html>
      <xsl:apply-templates select="document"/>
    </html>
  </xsl:variable>
  <xsl:apply-templates select="$offline-document" mode="offline-postprocessing"/>
</xsl:template>
 Top of the page 

7.4. Postprocessing

The only thing to be processed is the href attribute of the <a> element. We will change neither links refering to the anchors inside the same page nor absolute links.

mode = offline
<xsl:template match="a" mode="offline-postprocessing">
  <a>
    <xsl:copy-of select="@*"/>
    <xsl:if test="name(..) != 'span' and name(..) != 'div' and (not(../@id) or ../@id != 'langlinks') and not(contains(@href, '://')) and not(starts-with(@href, '#'))">
      <xsl:attribute name="href">
        <xsl:call-template name="postprocess-href">
          <xsl:with-param name="href">
            <xsl:value-of select="replace(concat($referer, @href), '/\./', '/')"/>
          </xsl:with-param>
        </xsl:call-template>
      </xsl:attribute>
    </xsl:if>
    <xsl:apply-templates mode="#current"/>
  </a>
</xsl:template>

Variable referer contains base URL, i.e. http://icebearsoft.euweb.cz/ followed by the part of the file name after /xml/ up to the last slash.

mode = offline
<xsl:variable name="referer">
  <xsl:text>http://icebearsoft.euweb.cz/</xsl:text>
  <xsl:analyze-string select="substring-after(saxon:systemId(), '/xml/')" regex="^(.*?)[^/]+$">
    <xsl:matching-substring>
      <xsl:value-of select="regex-group(1)"/>
    </xsl:matching-substring>
  </xsl:analyze-string>
</xsl:variable>

Now we remove references to a parend directory using a recursive template.

mode = offline
<xsl:template name="postprocess-href">
  <xsl:param name="href" as="xs:string" required="yes"/>
  <xsl:analyze-string select="$href" regex="$(.*?)/[^/]+/\.\.(/.*)$">
    <xsl:matching-substring>
      <xsl:call-template name="postprocess-href">
        <xsl:with-param name="href">
          <xsl:value-of select="concat(regex-group(1), regex-group(2))"/>
        </xsl:with-param>
      </xsl:call-template>
    </xsl:matching-substring>
    <xsl:non-matching-substring>
      <xsl:value-of select="."/>
    </xsl:non-matching-substring>
  </xsl:analyze-string>
</xsl:template>

Other elements will be copied including their attributes.

mode = offline
<xsl:template match="*" mode="offline-postprocessing">
  <xsl:copy>
    <xsl:copy-of select="@*"/>
    <xsl:apply-templates mode="#current"/>
  </xsl:copy>
</xsl:template>
 Top of the page 

7.5. Reference to the cascading stylesheet

Similarly as the logo the cascading stylesheet must also be included. Its name and location will be defined in the stylesheet parameter.

mode = offline
<xsl:variable name="css">
style.css</xsl:variable>
<xsl:template name="doc-css">
  <link rel="stylesheet" href="{$css}" type="text/css"/>
</xsl:template>
 Top of the page 

7.6. Page footer

The footer will contain just the copyright and links to language variants, nothing else will be present. Files with other language variants will be specified via a parameter where the main file name (without a hyphen and extension) will be followed by a colon and comma separated list of language codes.

mode = offline
<xsl:param name="langlinks" as="xs:string" required="yes"/>
<xsl:template name="footer">
  <br/>
  <br/>
  <br/>
  <hr/>
  <div id="copyright">
    <xsl:text>&#169; Z. Wagner - Ice Bear Soft, </xsl:text>
    <xsl:value-of select="zw:last-modified()" use-when="function-available("zw:last-modified")"/>
  </div>
  <xsl:text>
</xsl:text>
  <xsl:if test="contains($langlinks, ':')">
    <div id="langlinks">
      <xsl:text>&#160;</xsl:text>
      <xsl:for-each select="tokenize(substring-after($langlinks, ':'), ',')">
        <a href="{concat(substring-before($langlinks, ':'), '-', ., '.html')}">
          <xsl:value-of select="upper-case(.)"/>
        </a>
        <xsl:text>&#160;</xsl:text>
      </xsl:for-each>
    </div>
  </xsl:if>
</xsl:template>
 Top of the page 

8. Download

You can download generated stylesheets. You will also need a module with the Saxon extension function.

 Top of the page 

9. Workflow automation

Generation of the WWW pages requires several actions which must be carried out in a correct order. Some actions need not be carried out if the source files did not change. Automation of the whole process is achieved by using Ant. There is partially bad news for OS/2 users. The starting scripts were prepared for version 1.6alpha. Several things were changed in version 1.6.1 and the scripts no longer work. You can find working scripts in version 1.6.2. You can read information about the changes in Bugzilla in bug report number 28226. Versions 1.5.x have OS/2 specific bugs, therefore you should rather not use them.

The antfile used in this project will (perhaps) be documented later. We will now show just the basic rules:

 Top of the page 

10. Uploading files to the server

I create the WWW pages at home on my computer with OS/2. After testing, the new and modified pages must be sent to the server via FTP. This action is automated by a PHP script. PHP must, however, run as a separate executable. The script expects four parameters:

  1. The root of the local tree of the HTML pages
  2. The FTP server
  3. The user name for connection to the FTP server
  4. The password
The script is able to delete old files and create new directories but it never removes old empty directories from the server.

PHP 4.0.6 for OS/2 had a bug in function ftp_put. It was fixed in version 4.1.0.

Here you can find:

The PHP script uses functions ftp_size and ftp_mdtm which may not work on some FTP servers.

 Top of the page