<?xml version='1.0' encoding='UTF-8' ?>
<!-- Copyright 2004 Rowan Rodrik van der Molen
   - Distributed under the terms of the GNU General Public Licence v2
   - Transform an XBEL file into a (bunch of) XHTML file(s) -->

<xsl:stylesheet version='1.0' extension-element-prefixes='exsl'
  xmlns:bs="uri:BigSmoke:months"
  xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
  xmlns:exsl='http://exslt.org/common'
  xmlns='http://www.w3.org/1999/xhtml'>

  <!-- Output a valid XHTML1 Strict document
     - (The 'strictness' remains to be proven after generation.) -->
  <xsl:output method='xml' media-type="text/xhtml" encoding="UTF-8"
    doctype-system='http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
    doctype-public='-//W3C//DTD XHTML 1.0 Strict//EN' />

  <!-- The dir where all the XHTML chunks are to be written to. -->
  <xsl:param name="html_dir">html</xsl:param>

  <!-- The file extension for the generated XHTML files -->
  <xsl:param name="html_ext">htm</xsl:param>

  <!-- Are we to create chunked output (one folder per document) or one
     - big monolith. To create a monolity, use the 'mono' mode, or use
     - 'chunky' for the former. -->
  <xsl:param name="mode">chunky</xsl:param>

  <xsl:include href="locale/locale.xsl" />


  <!-- Process the root element -->
  <xsl:template match="/">
    <xsl:choose>
      <!-- Are we to create one file for each folder -->
      <xsl:when test="$mode='chunky'">

        <!-- Process the <xbel> element and all <folder> elements -->
        <xsl:apply-templates select="xbel|//folder" mode="chunky" />
      </xsl:when>

      <!-- Or, will we create one file for the entire XBEL file -->
      <xsl:when test="$mode='mono'">

        <!-- Process just the <xbel> element -->
        <xsl:apply-templates select="xbel" mode="mono" />
      </xsl:when>
    </xsl:choose>
  </xsl:template>


  <!-- Process the XBEL document element in mono mode -->
  <xsl:template match="xbel" mode="mono">

    <!-- Include comment about auto generation -->
    <xsl:call-template name='autogen_comment' />

    <!-- This is an XHTML1 document -->
    <html xmlns='http://www.w3.org/1999/xhtml'>
      <head>
        <title><xsl:value-of select="title" /></title>
      </head>

      <body>
        <xsl:apply-templates select="title|desc" />

        <!-- Add a ToC, which lists all folders -->
        <xsl:call-template name='toc' />

        <!-- Process all folders recursively -->
        <xsl:apply-templates select="folder|alias[name(id(@ref)) = 'folder']"
          mode="mono" />

        <!-- Process all bookmarks and aliases -->
        <xsl:apply-templates
          select="bookmark|alias[name(id(@ref)) = 'bookmark']" />
      </body>
    </html>
  </xsl:template>


  <!-- Process the title for this XBEL bookmark collection -->
  <xsl:template match="xbel/title">
    <div class='title'>
      <h1 class='title'><xsl:apply-templates /></h1>
    </div>
  </xsl:template>


  <!-- Process the description for this XBEL bookmark collection -->
  <xsl:template match="xbel/desc">

    <!-- Example: <div class="desc"><p>Some description</p></div> -->
    <div class="desc">
      <p><xsl:apply-templates /></p>
    </div>
  </xsl:template>


  <!-- Generate a table of contents (ToC),
     - a listing of all folders in the current context -->
  <xsl:template name="toc">
    <xsl:if test="folder[1]|alias[id(@ref)/self::folder]">
      <div class="toc" id="toc">
        <!-- The title of the ToC consists of the number of folders
           - and the 'folders' string in the current locale.
           - Example: 12 folders -->
        <div class='title'>
          <h3 class="title">
            <xsl:call-template name="toc_title">
              <xsl:with-param name="folder_count"
                select="count(.//folder|id(.//alias/@ref)/descendant-or-self::folder)" />

              <xsl:with-param name="folder_bookmark_count"
                select="count(.//folder/bookmark|id(.//alias/@ref)/descendant-or-self::bookmark)" /> 
            </xsl:call-template>
          </h3>
        </div>

        <!-- Create a definition list containing <dt>'s for each folder,
           - and (optionally) <dd>'s for each subfolder. -->
        <ul class="toc">
          <xsl:apply-templates select="folder|alias[id(@ref)/self::folder]"
            mode="toc" />
        </ul>
      </div>
    </xsl:if>
  </xsl:template>


  <!-- Process a folder for in a ToC -->
  <xsl:template match="folder" mode="toc" name="toc_folder">
    <xsl:param name='folder_class'>folder_closed</xsl:param>

    <!-- Get the unique ID for this folder [$folder_id] -->
    <xsl:variable name="folder_id">
      <xsl:call-template name='unique_id' />
    </xsl:variable>

    <li class='{$folder_class}'>
      <span class="folder">
        <a>
          <xsl:call-template name="href_attr" />

          <xsl:if test="desc">
            <xsl:attribute name="title">
              <xsl:value-of select="desc" />
            </xsl:attribute>
          </xsl:if>

          <!-- The folder title is used for the visible part of the anchor -->
          <xsl:value-of select="title" />
        </a>
      </span>
    </li>

    <!-- Does this folder contain any (references to) subfolders? -->
    <xsl:if test="folder|alias[id(@ref)/self::folder]">
      <ul class='toc'>
        <!-- Process these (references to) subfolders -->
        <xsl:apply-templates select="folder|alias[id(@ref)/self::folder]"
          mode="toc" />
      </ul>
    </xsl:if>
  </xsl:template>


  <!-- Process an folder alias for a ToC -->
  <xsl:template match="alias" mode="toc">
    <!-- Put an extra <div> around it so it can be properly styled -->
    <div class='alias'>
      <xsl:for-each select="id(@ref)">
        <xsl:call-template name="toc_folder">
          <xsl:with-param name="folder_class">folder_alias</xsl:with-param>
        </xsl:call-template>
      </xsl:for-each>
    </div>
  </xsl:template>


  <!-- Process a folder into a document -->
  <xsl:template match="xbel|folder" mode="chunky">
    <xsl:variable name='unique_id'>
      <xsl:call-template name='unique_id' />
    </xsl:variable>

    <!-- The physical file location where the folder chunk will go -->
    <xsl:variable name="folder_href"
      select="concat($html_dir, '/', $unique_id, '.', $html_ext)" />

    <!-- Which position does this folder hold in the list of all folders -->
    <xsl:variable name="container_position" select="position()" />

    <!-- How many folders are there in total? -->
    <xsl:variable name="container_count" select="last()" />

    <!-- TODO: figure out why positioning is 1 off -->
    <xsl:variable name="prev_container"
      select="/descendant::*[name()='folder' or name()='xbel']
                            [position() = ($container_position - 1)]" />

    <xsl:variable name="next_container"
      select="/descendant::*[name()='folder' or name()='xbel']
                            [position() = $container_position + 1]" />

    <xsl:variable name='supercontainer'
      select="/descendant::*[name()='folder' or name()='xbel']
                            [child::folder = current()]" />

    <xsl:variable name='last_container'
      select="/descendant::*[name()='folder' or name()='xbel']
                            [position() = last()]" />

    <!-- Write out a chunky document for this folder -->
    <exsl:document href="{$folder_href}"  method='xml' media-type="text/xhtml"
      doctype-system='http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
      doctype-public='-//W3C//DTD XHTML 1.0 Strict//EN'
      encoding="UTF-8">

      <!-- Tell the user we're writing this XML document -->
      <xsl:message>
        <xsl:value-of select="concat('Writing ', $folder_href)" />
      </xsl:message>

      <html xmlns='http://www.w3.org/1999/xhtml'>
        <!-- Inform users of the generated content of its generatedness -->
        <xsl:call-template name='autogen_comment' />

        <head>
          <title>
            <xsl:call-template name="path" />
          </title>

          <link type='text/css' rel='stylesheet' href='css/style.css' />

          <xsl:if test="$supercontainer">
            <link rel='up'>
              <xsl:apply-templates select="$supercontainer" mode="href_attr" />
              <xsl:apply-templates select="$supercontainer" mode="title_attr" />
            </link>
          </xsl:if>

          <xsl:if test="$prev_container">
            <link rel='prev'>
              <xsl:apply-templates select="$prev_container" mode="href_attr" />
              <xsl:apply-templates select="$prev_container" mode="title_attr" />
            </link>
          </xsl:if>

          <xsl:if test="$next_container">
            <link rel='next'>
              <xsl:apply-templates select="$next_container" mode="href_attr" />
              <xsl:apply-templates select="$next_container" mode="title_attr" />
            </link>
          </xsl:if>

          <link rel='top' href="{concat('index', '.', $html_ext)}">
            <xsl:apply-templates select="/" mode="title_attr" />
          </link>
          <link rel='first' href="{concat('index', '.', $html_ext)}">
            <xsl:apply-templates select="/" mode="title_attr" />
          </link>
          <link rel='last'>
            <xsl:apply-templates select="$last_container" mode="href_attr" />
            <xsl:apply-templates select="$last_container" mode="title_attr" />
          </link>
          <link rel='bookmark' href="http://pyxml.sf.net/topics/xbel/" title="The XML Bookmark Exchange Language Resource Page" />
        </head>

        <body>
          <div class='nav nav_header'>
            <table width="100%" summary="Navigation header">
              <tr>
                <th colspan="3" align="center">
                  <div class='path'>
                    <xsl:call-template name="linked_path" />
                  </div>

                  <hr />
                </th>
              </tr>
              <tr>
                <td width="50%" align="left">
                  <xsl:apply-templates select="$prev_container" mode="prev" />
                </td>
                <td width="50%" align="right">
                  <xsl:apply-templates select="$next_container" mode="next" />
                </td>
              </tr>
            </table>
            <hr />
          </div>

          <div class='folder'>
            <div class='title'>
              <h2 class='title'>
                <img class='icon' alt='Open folder icon'
                  src='images/folder_open-24x24.png' /><xsl:text> </xsl:text>
                <xsl:value-of select="title" /></h2>
            </div>

            <xsl:if test="desc">
              <div class='description'>
                <xsl:value-of select="desc" />
              </div>
            </xsl:if>

            <div class='details'>
              <xsl:if test="@added">
                <xsl:apply-templates select="@added" />
              </xsl:if>
            </div>

            <xsl:call-template name='toc' />

            <div class='title'>
              <xsl:if test="bookmark[1]">
                <h3 class='title'>
                  <xsl:value-of select="count(bookmark)" /> bookmarks
                </h3>
              </xsl:if>
            </div>

            <xsl:apply-templates select="bookmark|alias" />
          </div>

          <div class='nav nav_footer'>
            <hr class='thick' />
            <table width="100%" summary="Navigation footer">
              <tr>
                <td width="35%" align="left" valign="top">
                  <xsl:apply-templates select="$prev_container" mode="prev" />
                </td>
                <td width="30%" align="center">
                  <xsl:apply-templates select="$supercontainer" mode="up" />
                </td>
                <td width="35%" align="right" valign="top">
                  <xsl:apply-templates select="$next_container" mode="next" />
                </td>
              </tr>
            </table>
          </div>

          <xsl:call-template name="credits" />
        </body>
      </html>
    </exsl:document>
  </xsl:template>


  <!-- Process a <folder> in monolithic mode -->
  <xsl:template match="folder" mode="mono">
    <div class='folder'>
      <!-- Get the unique ID to identify this folder [$id] -->
      <xsl:attribute name='id'>
        <xsl:call-template name='unique_id' />
      </xsl:attribute>

      <!-- Generate the folder title as a heading of the appropriate level -->
      <div class='title'>
        <xsl:apply-templates select="title" mode="folder_title" />
      </div>

      <xsl:if test="desc">
        <div class='description'>
          <xsl:value-of select="desc" />
        </div>
      </xsl:if>

      <xsl:apply-templates select="folder" />
      <xsl:apply-templates select="bookmark|alias" />
    </div>
  </xsl:template>


  <!-- Generate a folder heading for top-level folders -->
  <xsl:template match="xbel/folder/title" mode="folder_title">
    <h2 class='title'><xsl:apply-templates /></h2>
  </xsl:template>


  <!-- Generate a folder heading for a subfolder -->
  <xsl:template match="xbel/folder/folder/title" mode="folder_title">
    <h3 class='title'><xsl:apply-templates /></h3>
  </xsl:template>


  <!-- Generate a folder heading for a subsubfolder -->
  <xsl:template match="xbel/folder/folder/folder/title" mode="folder_title">
    <h4 class='title'><xsl:apply-templates /></h4>
  </xsl:template>


  <!-- Generate a folder heading for a subsubsubfolder -->
  <xsl:template match="xbel/folder/folder/folder/folder/title"
    mode="folder_title">
    <h4 class='title'><xsl:apply-templates /></h4>
  </xsl:template>


  <!-- Generate a folder heading for antything deeper than a subsubsubfolder -->
  <xsl:template match="xbel/folder/folder/folder/folder//folder/title"
    mode="folder_title">
    <h5 class='title'><xsl:apply-templates /></h5>
  </xsl:template>


  <!-- Process a <bookmark> element -->
  <xsl:template match="bookmark">
    <p class="bookmark">
      <div class="bookmark">
        <!-- Add an id attribute to the bookmark's <div> -->
        <xsl:attribute name='id'>
          <xsl:call-template name='unique_id' />
        </xsl:attribute>

        <!-- Make the title a link to the bookmark's URL -->
        <div class='title'>
          <a href='{@href}'>
          <xsl:value-of select="position()" /><xsl:text>. </xsl:text>
          <xsl:value-of select='normalize-space(title)' /></a>
        </div>

        <!-- Add a description, if available -->
        <xsl:if test="desc">
          <div class='desc'>
            <xsl:value-of select="normalize-space(desc)" />
          </div>
        </xsl:if>

        <div class='details'>
          <span class='uri'>
            <a href='{@href}'><xsl:value-of select="@href" /></a>
          </span>

          <xsl:if test="@added|@modified|@visited">
            <span class='times'>
              <xsl:apply-templates select="@added|@modified|@visited" />
            </span>
          </xsl:if>
        </div>
      </div>
    </p>
  </xsl:template>


  <!-- Return a unique ID for a given node or the context node -->
  <xsl:template name="unique_id" mode="unique_id" match="bookmark|folder|xbel">
    <!-- The node to identify. This defaults to the context node. -->
    <xsl:param name="id_node" select="." />

    <xsl:choose>
      <xsl:when test="name($id_node) = 'xbel'">index</xsl:when>

      <xsl:when test="$id_node/@id">
        <xsl:value-of select="$id_node/@id" />
      </xsl:when>

      <xsl:when test="name($id_node) = 'folder'">
        <xsl:for-each select="//folder">
          <xsl:if test=". = $id_node">
            <xsl:value-of select="concat('folder', position())" />
          </xsl:if>
        </xsl:for-each>
      </xsl:when>
    </xsl:choose>
  </xsl:template>


  <!-- Generate a href attribute for an anchor,
     - based on the unique ID of the context node. -->
  <xsl:template name="href_attr" mode="href_attr"
    match="bookmark|folder|xbel">

    <xsl:param name="id_node" select="." />
    <xsl:param name="context_id">
      <xsl:apply-templates select="$id_node" mode="unique_id" />
    </xsl:param>

    <xsl:attribute name="href">
      <xsl:choose>
        <xsl:when test="$mode='chunky'">
          <xsl:value-of select="concat($context_id, '.', $html_ext)" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="concat('#', $context_id)" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:attribute>
  </xsl:template>


  <!-- Generate a title attribute from any titled element -->
  <xsl:template name="desc_attr" mode="desc_attr"
    match="bookmark|folder|xbel">

    <xsl:param name="titled_node" select="." />

    <xsl:attribute name="title">
      <xsl:choose>
        <xsl:when test="desc">
          <xsl:value-of select="normalize-space($titled_node/desc)" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:value-of select="normalize-space($titled_node/title)" />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:attribute>
  </xsl:template>


  <!-- Generate a title attribute from any titled element -->
  <xsl:template name="title_attr" mode="title_attr"
    match="bookmark|folder|xbel">

    <xsl:param name="titled_node" select="." />

    <xsl:attribute name="title">
      <xsl:value-of select="normalize-space($titled_node/title)" />
    </xsl:attribute>
  </xsl:template>


  <xsl:template match="xbel|folder" mode="prev" name="prev">
    <a accesskey="p">
      <xsl:call-template name="href_attr" />
      <xsl:call-template name="title_attr" />

      <img src='images/previous-16x16.png' /> <xsl:text> </xsl:text>

      <xsl:call-template name='gettext'>
        <xsl:with-param name='string'>Previous</xsl:with-param>
      </xsl:call-template>: <xsl:value-of select="title" />
    </a>
  </xsl:template>


  <xsl:template match="xbel|folder" mode="next" name="next">
    <a accesskey="n">
      <xsl:call-template name="href_attr" />
      <xsl:call-template name="desc_attr" />

      <xsl:call-template name='gettext'>
        <xsl:with-param name='string'>Next</xsl:with-param>
      </xsl:call-template>: <xsl:value-of select="title" />

      <xsl:text> </xsl:text><img src='images/next-16x16.png' />
    </a>
  </xsl:template>


  <!-- Create an anchor that allows you to go one level up in the folders -->
  <xsl:template match="xbel|folder" mode="up" name="up">
    <a accesskey="u">
      <xsl:call-template name="href_attr" />
      <xsl:call-template name="desc_attr" />

      <img src='images/up-16x16.png' /><xsl:text> </xsl:text>

      <xsl:call-template name='gettext'>
        <xsl:with-param name='string'>Up</xsl:with-param>
      </xsl:call-template>: <xsl:value-of select="title" />
    </a>
  </xsl:template>


  <xsl:template match="xbel|folder" mode="home" name="home">
    <a accesskey="a">
      <xsl:call-template name="href_attr" />
      <xsl:call-template name="desc_attr" />
      <xsl:value-of select="title" />
    </a>
  </xsl:template>


  <!-- A comment to remind people that editing computer generated content is
     - not the smartest of choises for your avarage person. -->
  <xsl:template name="autogen_comment">
    <!-- The will generate an XML comment -->
    <xsl:comment>
      Since this file is auto-generated by XBEL2XHTML.XSL,
      modifying it is about as useful as concatenating yet another
      3 acronyms.
    </xsl:comment>
  </xsl:template>


  <xsl:template name="linked_path" mode="linked_path" match="xbel|folder">
    <xsl:param name="active_node" select="." />

    <xsl:if test="not($active_node/self::xbel)">
      <xsl:if test="parent::folder|parent::xbel">
        <!-- The for-each loop is used to cheat the recursive call into
           - a different context. -->
           <xsl:for-each select="parent::*">
          <xsl:call-template name="linked_path">
            <xsl:with-param name="active_node" select="$active_node" />
          </xsl:call-template>
        </xsl:for-each>
        &gt;
      </xsl:if>

      <span class="folder">
        <xsl:choose>
          <xsl:when test="self::node()=$active_node">
            <xsl:value-of select="title" />
          </xsl:when>
          <xsl:otherwise>
            <a>
              <xsl:call-template name="href_attr" />
              <xsl:call-template name="desc_attr" />

              <xsl:value-of select="title" />
            </a>
          </xsl:otherwise>
        </xsl:choose>
      </span>
    </xsl:if>
  </xsl:template>


  <xsl:template match="xbel|folder" mode="path" name="path">
    <xsl:if test="parent::*">
      <xsl:apply-templates select="parent::*" mode="path" /> &gt;
    </xsl:if>

    <xsl:value-of select="title" />
  </xsl:template>


  <xsl:template name="toc_title">
    <xsl:param name="folder_count" />
    <xsl:param name="folder_bookmark_count" />

    <xsl:value-of select="concat($folder_count, ' folders with ',
      $folder_bookmark_count, ' more bookmarks')" />
  </xsl:template>


  <xsl:template name="bookmarks_title">
    <xsl:param name="bookmark_count" />

    <xsl:value-of select="concat($bookmark_count, ' bookmarks')" />
  </xsl:template>


  <xsl:template name="credits">
    <hr class='thin' />

    <div class="credits">
      <xsl:call-template name='gettext'>
        <xsl:with-param name='string'>Generated by XBEL2XHTML.XSL</xsl:with-param>
      </xsl:call-template>
    </div>
  </xsl:template>


  <xsl:template match="@added"> -
    <span class='added'>
      <xsl:call-template name='gettext'>
        <xsl:with-param name='string'>Added</xsl:with-param>
      </xsl:call-template>:

      <xsl:apply-templates select="." mode="datetime" />
    </span>
  </xsl:template>


  <xsl:template match="@modified"> - 
    <span class='modified'>
      <xsl:call-template name='gettext'>
        <xsl:with-param name='string'>Last modified</xsl:with-param>
      </xsl:call-template>:

      <xsl:apply-templates select="." mode="datetime" />
    </span>
  </xsl:template>


  <xsl:template match="@visited"> - 
    <span class='visited'>
      <xsl:call-template name='gettext'>
        <xsl:with-param name='string'>Last visited</xsl:with-param>
      </xsl:call-template>:

      <xsl:apply-templates select="." mode="datetime" />
    </span>
  </xsl:template>


  <xsl:template name="datetime" mode="datetime"
  match="@added|@modified|@visited">
    <xsl:param name="raw" select="." />

    <span class='date'>
      <xsl:call-template name='fancy_date'>
        <xsl:with-param name='raw'>

          <xsl:choose>
            <xsl:when test="contains($raw, 'T')">
              <xsl:value-of select="substring-before($raw, 'T')" />
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="$raw" />
            </xsl:otherwise>
          </xsl:choose>

        </xsl:with-param>
      </xsl:call-template>
    </span>

    <xsl:if test="contains($raw, 'T')">
      <span class='time'> @
        <xsl:value-of select="substring-after($raw, 'T')" />
      </span>
    </xsl:if>
  </xsl:template>


  <xsl:template name="fancy_date">
    <xsl:param name="raw" />

    <xsl:variable name="year" select="substring-before($raw, '-')" />

    <xsl:variable name="month">
      <!--<xsl:value-of select="document('')/xsl:stylesheet/months/month[substring-before(substring-after($raw, '-'), '-')]" />-->
      <xsl:value-of select="document('')/xsl:stylesheet/bs:months/bs:name[number(substring-before(substring-after($raw, '-'), '-'))]" />
    </xsl:variable>

    <xsl:variable name="day" select="number(substring-after(substring-after($raw, '-'), '-'))" />

    <xsl:value-of select="concat($day, ' ', $month, ' ', $year)" />
  </xsl:template>


  <bs:months>
    <bs:name>January</bs:name>
    <bs:name>February</bs:name>
    <bs:name>March</bs:name>
    <bs:name>April</bs:name>
    <bs:name>May</bs:name>
    <bs:name>June</bs:name>
    <bs:name>July</bs:name>
    <bs:name>August</bs:name>
    <bs:name>September</bs:name>
    <bs:name>October</bs:name>
    <bs:name>November</bs:name>
    <bs:name>December</bs:name>
  </bs:months>
</xsl:stylesheet>
