<?xml version="1.0"?>
<!DOCTYPE xsl:transform [
<!ENTITY css SYSTEM "invoice.css">
]>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
               version="1.0">
  <!-- invoice.xsl, v1.0
       Copyright 2000, 2001
       Christopher R. Maden
       crism consulting
       crism@maden.org -->

  <!-- This stylesheet takes an invoice with a series of billable
       periods, and produces an invoice in HTML for one particular
       period, itemized by project and billing rate.  The end of the
       period for which an invoice is requested may be provided as the
       $period-end parameter; otherwise, the last period in the input
       document will be used.
         For best results, start reading with the root template, and
       come back to the functional named templates as they are
       called.
         There are doubtless redundancies and impurities in this
       transformation sheet that could be cleaned up, though this
       worked well enough for the author.  Feedback to crism@maden.org
       is welcomed. -->

  <xsl:output method="html"/>

  <!-- $stylesheet-version is the version number of this
       stylesheet. -->
  <xsl:variable name="stylesheet-version" select="'1.0'"/>

  <!-- $period-end is the ISO 8601 representation of the end of the
       period for which an invoice is desired.  The string
       representation must be identical to that used in the end
       attribute of the period element.  If not specified, the last
       period element in the invoice is used. -->
  <xsl:param name="period-end"/>

  <!-- $period-end-date is the end date to use for processing.  If
       $period-end was specified, use it, otherwise use the date of
       the last period. -->
  <xsl:variable name="period-end-date">
    <xsl:choose>
      <!-- Was $period-end specified by the user? -->
      <xsl:when test="$period-end">
        <!-- Then use it. -->
        <xsl:value-of select="$period-end"/>
      </xsl:when>
      <xsl:otherwise>
        <!-- Get the end date of the last period. -->
        <xsl:variable name="temp-date"
                      select="/billable/period[last()]/@end"/>

        <!-- Tell the user which date is in use. -->
        <xsl:message>
          <xsl:text>No invoice period specified.  Using </xsl:text>
          <xsl:value-of select="$temp-date"/>
        </xsl:message>

        <!-- And then use it. -->
        <xsl:value-of select="$temp-date"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <!-- Find the period element identified by the end date. -->
  <xsl:variable name="period"
                select="/billable/period[@end=$period-end-date]"/>

  <!-- Find the start date of the selected period. -->
  <xsl:variable name="period-start-date">
    <xsl:value-of select="$period/@start"/>
  </xsl:variable>

  <!-- format-date presents the date as an English string. -->
  <xsl:template name="format-date">
    <xsl:param name="year"/>
    <xsl:param name="month"/>
    <xsl:param name="day"/>
    <xsl:param name="hour" select="'0'"/>
    <xsl:param name="minute" select="'0'"/>
    <xsl:param name="second" select="'0'"/>
    <xsl:param name="TZ" select="'0'"/>

    <!-- Strip leading zeroes from the day by turning it into a number
         first.  The string() cast is unnecessary, but more
         readable. -->
    <xsl:value-of select="string(number($day))"/>
    <xsl:text> </xsl:text>

    <!-- Output the English name of the month. -->
    <xsl:choose>
      <xsl:when test="$month = '01'">
        <xsl:text>January</xsl:text>
      </xsl:when>
      <xsl:when test="$month = '02'">
        <xsl:text>February</xsl:text>
      </xsl:when>
      <xsl:when test="$month = '03'">
        <xsl:text>March</xsl:text>
      </xsl:when>
      <xsl:when test="$month = '04'">
        <xsl:text>April</xsl:text>
      </xsl:when>
      <xsl:when test="$month = '05'">
        <xsl:text>May</xsl:text>
      </xsl:when>
      <xsl:when test="$month = '06'">
        <xsl:text>June</xsl:text>
      </xsl:when>
      <xsl:when test="$month = '07'">
        <xsl:text>July</xsl:text>
      </xsl:when>
      <xsl:when test="$month = '08'">
        <xsl:text>August</xsl:text>
      </xsl:when>
      <xsl:when test="$month = '09'">
        <xsl:text>September</xsl:text>
      </xsl:when>
      <xsl:when test="$month = '10'">
        <xsl:text>October</xsl:text>
      </xsl:when>
      <xsl:when test="$month = '11'">
        <xsl:text>November</xsl:text>
      </xsl:when>
      <xsl:when test="$month = '12'">
        <xsl:text>December</xsl:text>
      </xsl:when>
    </xsl:choose>
    <xsl:text> </xsl:text>

    <!-- And finally, the year. -->
    <xsl:value-of select="$year"/>

    <!-- If there is a time component, add it. -->
    <xsl:if test="$hour">
      <xsl:text> at </xsl:text>

      <!-- Use 24-hour time format; no mucking about with AM and
           PM. -->
      <xsl:value-of select="format-number($hour,'00')"/>
      <xsl:text>:</xsl:text>
      <xsl:value-of select="format-number($minute,'00')"/>
      <xsl:text>:</xsl:text>

      <!-- Use fractional seconds if necessary. -->
      <xsl:value-of select="format-number($second,'00.###')"/>

      <!-- Present the time zone as "GMT" plus or minus an offset. -->
      <xsl:text> GMT</xsl:text>
      <xsl:choose>
        <xsl:when test="$TZ &gt; 0">
          <xsl:text>+</xsl:text>

          <!-- Split the time zone offset back into hours and
               minutes. -->
          <xsl:value-of select="format-number(floor($TZ),'00')"/>
          <xsl:text>:</xsl:text>
          <xsl:value-of select="format-number(($TZ * 60) mod 60,
                        '00')"/>
        </xsl:when>
        <xsl:when test="$TZ &lt; 0">
          <!-- Take the absolute value of the time zone offset. -->
          <xsl:variable name="newTZ" select="-$TZ"/>
          <xsl:text>-</xsl:text>

          <!-- Split the time zone offset back into hours and
               minutes. -->
          <xsl:value-of select="format-number(floor($newTZ),'00')"/>
          <xsl:text>:</xsl:text>
          <xsl:value-of select="format-number(($newTZ * 60) mod 60,
                        '00')"/>
        </xsl:when>
      </xsl:choose>
    </xsl:if>
  </xsl:template>

  <!-- process-date takes one or two dates in unparsed or partially
       parsed form, and a command.  It can currently format the date
       in English or subtract two dates, after the date strings have
       been parsed. -->
  <!-- There is no error checking.  This template assumes that the
       date strings are of the form noted in the DTD, a subset of ISO
       8601. -->
  <xsl:template name="process-date">
    <!-- Parsed structures for the first date. -->
    <xsl:param name="year"/>
    <xsl:param name="month"/>
    <xsl:param name="day"/>
    <xsl:param name="hour"/>
    <xsl:param name="minute"/>
    <xsl:param name="second"/>
    <xsl:param name="TZ"/>
    <!-- The unparsed first string or a remnant thereof. -->
    <xsl:param name="string"/>

    <!-- Parsed structures for the second date. -->
    <xsl:param name="year2"/>
    <xsl:param name="month2"/>
    <xsl:param name="day2"/>
    <xsl:param name="hour2"/>
    <xsl:param name="minute2"/>
    <xsl:param name="second2"/>
    <xsl:param name="TZ2"/>
    <!-- The unparsed second string or a remnant thereof. -->
    <xsl:param name="string2"/>

    <!-- The command string; currently only 'f' and 's' are
         recognized. -->
    <xsl:param name="command"/>

    <xsl:choose>
      <!-- The first string is not completely parsed. -->
      <xsl:when test="$string">
        <xsl:choose>
          <!-- The first year has not yet been found. -->
          <xsl:when test="not($year)">
            <xsl:call-template name="process-date">
              <!-- The year is the first token before a hyphen. -->
              <xsl:with-param name="year"
                              select="substring-before($string,'-')"/>
              <!-- We still need to parse the rest of the string. -->
              <xsl:with-param name="string"
                              select="substring-after($string,'-')"/>
              <!-- Everything else is unchanged. -->
              <xsl:with-param name="year2" select="$year2"/>
              <xsl:with-param name="month2" select="$month2"/>
              <xsl:with-param name="day2" select="$day2"/>
              <xsl:with-param name="hour2" select="$hour2"/>
              <xsl:with-param name="minute2" select="$minute2"/>
              <xsl:with-param name="second2" select="$second2"/>
              <xsl:with-param name="TZ2" select="$TZ2"/>
              <xsl:with-param name="string2" select="$string2"/>
              <xsl:with-param name="command" select="$command"/>
            </xsl:call-template>
          </xsl:when>
          <!-- If we have the year, do we have the month? -->
          <xsl:when test="not($month)">
            <xsl:call-template name="process-date">
              <xsl:with-param name="year" select="$year"/>
              <!-- The month is the first remaining token before a
                   hyphen. -->
              <xsl:with-param name="month"
                              select="substring-before($string,'-')"/>
              <!-- And we still need to parse the rest. -->
              <xsl:with-param name="string"
                              select="substring-after($string,'-')"/>
              <xsl:with-param name="year2" select="$year2"/>
              <xsl:with-param name="month2" select="$month2"/>
              <xsl:with-param name="day2" select="$day2"/>
              <xsl:with-param name="hour2" select="$hour2"/>
              <xsl:with-param name="minute2" select="$minute2"/>
              <xsl:with-param name="second2" select="$second2"/>
              <xsl:with-param name="TZ2" select="$TZ2"/>
              <xsl:with-param name="string2" select="$string2"/>
              <xsl:with-param name="command" select="$command"/>
            </xsl:call-template>
          </xsl:when>
          <!-- We have the year and month, but no day. -->
          <xsl:when test="not($day)">
            <xsl:choose>
              <!-- A time is present in the string. -->
              <xsl:when test="contains($string,'T')">
                <xsl:call-template name="process-date">
                  <xsl:with-param name="year" select="$year"/>
                  <xsl:with-param name="month" select="$month"/>
                  <!-- The day is the part before the T. -->
                  <xsl:with-param name="day"
                                  select="substring-before($string,'T')"/>
                  <!-- We need to parse the rest. -->
                  <xsl:with-param name="string"
                                  select="substring-after($string,'T')"/>
                  <xsl:with-param name="year2" select="$year2"/>
                  <xsl:with-param name="month2" select="$month2"/>
                  <xsl:with-param name="day2" select="$day2"/>
                  <xsl:with-param name="hour2" select="$hour2"/>
                  <xsl:with-param name="minute2" select="$minute2"/>
                  <xsl:with-param name="second2" select="$second2"/>
                  <xsl:with-param name="TZ2" select="$TZ2"/>
                  <xsl:with-param name="string2" select="$string2"/>
                  <xsl:with-param name="command" select="$command"/>
                </xsl:call-template>
              </xsl:when>
              <!-- The entire remaining string is the day. -->
              <xsl:otherwise>
                <xsl:call-template name="process-date">
                  <xsl:with-param name="year" select="$year"/>
                  <xsl:with-param name="month" select="$month"/>
                  <xsl:with-param name="day" select="$string"/>
                  <xsl:with-param name="year2" select="$year2"/>
                  <xsl:with-param name="month2" select="$month2"/>
                  <xsl:with-param name="day2" select="$day2"/>
                  <xsl:with-param name="hour2" select="$hour2"/>
                  <xsl:with-param name="minute2" select="$minute2"/>
                  <xsl:with-param name="second2" select="$second2"/>
                  <xsl:with-param name="TZ2" select="$TZ2"/>
                  <xsl:with-param name="string2" select="$string2"/>
                  <xsl:with-param name="command" select="$command"/>
                </xsl:call-template>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:when>
          <!-- There's still a string, but no hour. -->
          <xsl:when test="not($hour)">
            <xsl:call-template name="process-date">
              <xsl:with-param name="year" select="$year"/>
              <xsl:with-param name="month" select="$month"/>
              <xsl:with-param name="day" select="$day"/>
              <!-- Then the hour is the first token before a
                   colon. -->
              <xsl:with-param name="hour"
                              select="substring-before($string,':')"/>
              <!-- And we'll parse the rest. -->
              <xsl:with-param name="string"
                              select="substring-after($string,':')"/>
              <xsl:with-param name="year2" select="$year2"/>
              <xsl:with-param name="month2" select="$month2"/>
              <xsl:with-param name="day2" select="$day2"/>
              <xsl:with-param name="hour2" select="$hour2"/>
              <xsl:with-param name="minute2" select="$minute2"/>
              <xsl:with-param name="second2" select="$second2"/>
              <xsl:with-param name="TZ2" select="$TZ2"/>
              <xsl:with-param name="string2" select="$string2"/>
              <xsl:with-param name="command" select="$command"/>
            </xsl:call-template>
          </xsl:when>
          <!-- A string, but no minute. -->
          <xsl:when test="not($minute)">
            <xsl:choose>
              <!-- The time zone is GMT. -->
              <xsl:when test="contains($string,'Z')">
                <xsl:choose>
                  <!-- There are seconds specified. -->
                  <xsl:when test="contains($string,':')">
                    <xsl:call-template name="process-date">
                      <xsl:with-param name="year" select="$year"/>
                      <xsl:with-param name="month" select="$month"/>
                      <xsl:with-param name="day" select="$day"/>
                      <xsl:with-param name="hour" select="$hour"/>
                      <!-- The minutes are the part before the
                           colon. -->
                      <xsl:with-param name="minute"
                                      select="substring-before(
                                                $string,':')"/>
                      <!-- The seconds are between the colon and the
                           Z. -->
                      <xsl:with-param name="second"
                                      select="substring-after(
                                                substring-before($string,
                                                  'Z'),
                                                ':')"/>
                      <!-- The time zone is zero-offset. -->
                      <xsl:with-param name="TZ" select="'0'"/>
                      <xsl:with-param name="year2" select="$year2"/>
                      <xsl:with-param name="month2" select="$month2"/>
                      <xsl:with-param name="day2" select="$day2"/>
                      <xsl:with-param name="hour2" select="$hour2"/>
                      <xsl:with-param name="minute2"
                                      select="$minute2"/>
                      <xsl:with-param name="second2"
                                      select="$second2"/>
                      <xsl:with-param name="TZ2" select="$TZ2"/>
                      <xsl:with-param name="string2"
                                      select="$string2"/>
                      <xsl:with-param name="command"
                                      select="$command"/>
                    </xsl:call-template>
                  </xsl:when>
                  <!-- No seconds. -->
                  <xsl:otherwise>
                    <xsl:call-template name="process-date">
                      <xsl:with-param name="year" select="$year"/>
                      <xsl:with-param name="month" select="$month"/>
                      <xsl:with-param name="day" select="$day"/>
                      <xsl:with-param name="hour" select="$hour"/>
                      <!-- The minutes are before the Z. -->
                      <xsl:with-param name="minute"
                                     select="substring-before(
                                               $string,'Z')"/>
                      <!-- No seconds. -->
                      <xsl:with-param name="second" select="'0'"/>
                      <!-- And the time zone is zero-offset. -->
                      <xsl:with-param name="TZ" select="'0'"/>
                      <xsl:with-param name="year2" select="$year2"/>
                      <xsl:with-param name="month2" select="$month2"/>
                      <xsl:with-param name="day2" select="$day2"/>
                      <xsl:with-param name="hour2" select="$hour2"/>
                      <xsl:with-param name="minute2"
                                      select="$minute2"/>
                      <xsl:with-param name="second2"
                                      select="$second2"/>
                      <xsl:with-param name="TZ2" select="$TZ2"/>
                      <xsl:with-param name="string2"
                                      select="$string2"/>
                      <xsl:with-param name="command"
                                      select="$command"/>
                    </xsl:call-template>
                  </xsl:otherwise>
                </xsl:choose>
              </xsl:when>
              <!-- Not GMT, but a negative offset. -->
              <xsl:when test="contains($string,'-')">
                <!-- $pre-TZ-string has the minutes and maybe
                     seconds. -->
                <xsl:variable name="pre-TZ-string"
                              select="substring-before($string,
                                                       '-')"/>

                <xsl:choose>
                  <!-- There are seconds. -->
                  <xsl:when test="contains($pre-TZ-string,':')">
                    <xsl:call-template name="process-date">
                      <xsl:with-param name="year" select="$year"/>
                      <xsl:with-param name="month" select="$month"/>
                      <xsl:with-param name="day" select="$day"/>
                      <xsl:with-param name="hour" select="$hour"/>
                      <!-- The minutes are the part before the
                           colon. -->
                      <xsl:with-param name="minute"
                                      select="substring-before(
                                                $string,':')"/>
                      <!-- The seconds are after the colon and before
                           the time zone. -->
                      <xsl:with-param name="second"
                                      select="substring-after(
                                                $pre-TZ-string,':')"/>
                      <!-- Send the time zone though one more
                           pass. -->
                      <xsl:with-param name="string"
                                      select="concat('-',
                                                substring-after(
                                                  $string,'-'))"/>
                      <xsl:with-param name="year2" select="$year2"/>
                      <xsl:with-param name="month2" select="$month2"/>
                      <xsl:with-param name="day2" select="$day2"/>
                      <xsl:with-param name="hour2" select="$hour2"/>
                      <xsl:with-param name="minute2"
                                      select="$minute2"/>
                      <xsl:with-param name="second2"
                                      select="$second2"/>
                      <xsl:with-param name="TZ2" select="$TZ2"/>
                      <xsl:with-param name="string2"
                                      select="$string2"/>
                      <xsl:with-param name="command"
                                      select="$command"/>
                    </xsl:call-template>
                  </xsl:when>
                  <!-- There are no seconds. -->
                  <xsl:otherwise>
                    <xsl:call-template name="process-date">
                      <xsl:with-param name="year" select="$year"/>
                      <xsl:with-param name="month" select="$month"/>
                      <xsl:with-param name="day" select="$day"/>
                      <xsl:with-param name="hour" select="$hour"/>
                      <!-- The minutes are the pre-time-zone
                           string. -->
                      <xsl:with-param name="minute"
                                      select="$pre-TZ-string"/>
                      <!-- There are no seconds. -->
                      <xsl:with-param name="second" select="'0'"/>
                      <!-- The time zone, as an offset in hours, is
                           minus the part of the time zone before the
                           colon (hours) minus the part after the
                           colon (minutes) divided by sixty. -->
                      <xsl:with-param name="TZ"
                                      select="-number(
                                                substring-before(
                                                  substring-after(
                                                    $string,'-'),
                                                  ':')) -
                                              (number(
                                                 substring-after(
                                                   $string,':'))
                                               div 60)"/>
                      <xsl:with-param name="year2" select="$year2"/>
                      <xsl:with-param name="month2" select="$month2"/>
                      <xsl:with-param name="day2" select="$day2"/>
                      <xsl:with-param name="hour2" select="$hour2"/>
                      <xsl:with-param name="minute2"
                                      select="$minute2"/>
                      <xsl:with-param name="second2"
                                      select="$second2"/>
                      <xsl:with-param name="TZ2" select="$TZ2"/>
                      <xsl:with-param name="string2"
                                      select="$string2"/>
                      <xsl:with-param name="command"
                                      select="$command"/>
                    </xsl:call-template>
                  </xsl:otherwise>
                </xsl:choose>
              </xsl:when>
              <!-- A positive time-zone offset. -->
              <xsl:when test="contains($string,'+')">
                <!-- $pre-TZ-string has the minutes and maybe
                     seconds. -->
                <xsl:variable name="pre-TZ-string"
                              select="substring-before($string,
                                                       '+')"/>
                <xsl:choose>
                  <!-- There are seconds. -->
                  <xsl:when test="contains($pre-TZ-string,':')">
                    <xsl:call-template name="process-date">
                      <xsl:with-param name="year" select="$year"/>
                      <xsl:with-param name="month" select="$month"/>
                      <xsl:with-param name="day" select="$day"/>
                      <xsl:with-param name="hour" select="$hour"/>
                      <!-- The minutes are the part before the
                           colon. -->
                      <xsl:with-param name="minute"
                                      select="substring-before(
                                                $string,':')"/>
                      <!-- The seconds are after the colon and before
                           the time zone. -->
                      <xsl:with-param name="second"
                                      select="substring-after(
                                                $pre-TZ-string,':')"/>
                      <!-- Send the time zone though one more
                           pass. -->
                      <xsl:with-param name="string"
                                      select="concat('+',
                                                substring-after(
                                                  $string,'+'))"/>
                      <xsl:with-param name="year2" select="$year2"/>
                      <xsl:with-param name="month2" select="$month2"/>
                      <xsl:with-param name="day2" select="$day2"/>
                      <xsl:with-param name="hour2" select="$hour2"/>
                      <xsl:with-param name="minute2"
                                      select="$minute2"/>
                      <xsl:with-param name="second2"
                                      select="$second2"/>
                      <xsl:with-param name="TZ2" select="$TZ2"/>
                      <xsl:with-param name="string2"
                                      select="$string2"/>
                      <xsl:with-param name="command"
                                      select="$command"/>
                    </xsl:call-template>
                  </xsl:when>
                  <!-- There are no seconds. -->
                  <xsl:otherwise>
                    <xsl:call-template name="process-date">
                      <xsl:with-param name="year" select="$year"/>
                      <xsl:with-param name="month" select="$month"/>
                      <xsl:with-param name="day" select="$day"/>
                      <xsl:with-param name="hour" select="$hour"/>
                      <!-- The minutes are the pre-time-zone
                           string. -->
                      <xsl:with-param name="minute"
                                      select="$pre-TZ-string"/>
                      <!-- There are no seconds. -->
                      <xsl:with-param name="second" select="'0'"/>
                      <!-- The time zone, as an offset in hours, is
                           minus the part of the time zone before the
                           colon (hours) minus the part after the
                           colon (minutes) divided by sixty. -->
                      <xsl:with-param name="TZ"
                                      select="number(substring-before(
                                                substring-after(
                                                  $string,'+'),
                                                ':')) +
                                              (number(
                                                 substring-after(
                                                   $string,':'))
                                               div 60)"/>
                      <xsl:with-param name="year2" select="$year2"/>
                      <xsl:with-param name="month2" select="$month2"/>
                      <xsl:with-param name="day2" select="$day2"/>
                      <xsl:with-param name="hour2" select="$hour2"/>
                      <xsl:with-param name="minute2"
                                      select="$minute2"/>
                      <xsl:with-param name="second2"
                                      select="$second2"/>
                      <xsl:with-param name="TZ2" select="$TZ2"/>
                      <xsl:with-param name="string2"
                                      select="$string2"/>
                      <xsl:with-param name="command"
                                      select="$command"/>
                    </xsl:call-template>
                  </xsl:otherwise>
                </xsl:choose>
              </xsl:when>
            </xsl:choose>
          </xsl:when>
          <!-- We came around again to finish the time zone -->
          <xsl:when test="not($TZ)">
            <xsl:choose>
              <!-- The time zone offset is negative -->
              <xsl:when test="contains($string,'-')">
                <xsl:call-template name="process-date">
                  <xsl:with-param name="year" select="$year"/>
                  <xsl:with-param name="month" select="$month"/>
                  <xsl:with-param name="day" select="$day"/>
                  <xsl:with-param name="hour" select="$hour"/>
                  <xsl:with-param name="minute" select="$minute"/>
                  <xsl:with-param name="second" select="$second"/>
                  <!-- The time zone is always minutes and
                       seconds. -->
                  <xsl:with-param name="TZ"
                                  select="-number(substring-before(
                                            substring-after(
                                              $string,'-'),
                                            ':')) -
                                          (number(
                                             substring-after(
                                               $string,':'))
                                           div 60)"/>
                  <xsl:with-param name="year2" select="$year2"/>
                  <xsl:with-param name="month2" select="$month2"/>
                  <xsl:with-param name="day2" select="$day2"/>
                  <xsl:with-param name="hour2" select="$hour2"/>
                  <xsl:with-param name="minute2" select="$minute2"/>
                  <xsl:with-param name="second2" select="$second2"/>
                  <xsl:with-param name="TZ2" select="$TZ2"/>
                  <xsl:with-param name="string2" select="$string2"/>
                  <xsl:with-param name="command" select="$command"/>
                </xsl:call-template>
              </xsl:when>
              <!-- The time one offset is positive. -->
              <xsl:when test="contains($string,'+')">
                <xsl:call-template name="process-date">
                  <xsl:with-param name="year" select="$year"/>
                  <xsl:with-param name="month" select="$month"/>
                  <xsl:with-param name="day" select="$day"/>
                  <xsl:with-param name="hour" select="$hour"/>
                  <xsl:with-param name="minute" select="$minute"/>
                  <xsl:with-param name="second" select="$second"/>
                  <!-- The time zone is always minutes and
                       seconds. -->
                  <xsl:with-param name="TZ"
                                  select="number(substring-before(
                                            substring-after(
                                              $string,'+'),
                                            ':')) +
                                          (number(
                                             substring-after(
                                               $string,':'))
                                           div 60)"/>
                  <xsl:with-param name="year2" select="$year2"/>
                  <xsl:with-param name="month2" select="$month2"/>
                  <xsl:with-param name="day2" select="$day2"/>
                  <xsl:with-param name="hour2" select="$hour2"/>
                  <xsl:with-param name="minute2" select="$minute2"/>
                  <xsl:with-param name="second2" select="$second2"/>
                  <xsl:with-param name="TZ2" select="$TZ2"/>
                  <xsl:with-param name="string2" select="$string2"/>
                  <xsl:with-param name="command" select="$command"/>
                </xsl:call-template>
              </xsl:when>
            </xsl:choose>
          </xsl:when>
        </xsl:choose>
      </xsl:when>

      <!-- The first string has been completely processed. -->
      <xsl:otherwise>
        <xsl:choose>
          <!-- The command is to format the first string and ignore
               the second. -->
          <xsl:when test="$command = 'f'">
            <xsl:call-template name="format-date">
              <!-- Pass the parsed date to the formatter. -->
              <xsl:with-param name="year" select="$year"/>
              <xsl:with-param name="month" select="$month"/>
              <xsl:with-param name="day" select="$day"/>
              <xsl:with-param name="hour" select="$hour"/>
              <xsl:with-param name="minute" select="$minute"/>
              <xsl:with-param name="second" select="$second"/>
              <xsl:with-param name="TZ" select="$TZ"/>
            </xsl:call-template>
          </xsl:when>
          <!-- There's a second string that needs parsing. -->
          <xsl:when test="$string2">
            <xsl:call-template name="process-date">
              <!-- Flip the first and second dates and re-run the date
                   parser. -->
              <xsl:with-param name="string" select="$string2"/>
              <xsl:with-param name="year2" select="$year"/>
              <xsl:with-param name="month2" select="$month"/>
              <xsl:with-param name="day2" select="$day"/>
              <xsl:with-param name="hour2" select="$hour"/>
              <xsl:with-param name="minute2" select="$minute"/>
              <xsl:with-param name="second2" select="$second"/>
              <xsl:with-param name="TZ2" select="$TZ"/>
              <xsl:with-param name="command" select="$command"/>
            </xsl:call-template>
          </xsl:when>
          <!-- Both strings have been processed and the command is to
               subtract them. -->
          <xsl:when test="$command = 's'">
            <xsl:call-template name="subtract-date">
              <!-- Remember that parsing the second date flipped them
                   around.  The first date was the second string.
                   We'll subtract 1 from 2. -->
              <xsl:with-param name="year" select="$year"/>
              <xsl:with-param name="month" select="$month"/>
              <xsl:with-param name="day" select="$day"/>
              <xsl:with-param name="hour" select="$hour"/>
              <xsl:with-param name="minute" select="$minute"/>
              <xsl:with-param name="second" select="$second"/>
              <xsl:with-param name="TZ" select="$TZ"/>
              <xsl:with-param name="year2" select="$year2"/>
              <xsl:with-param name="month2" select="$month2"/>
              <xsl:with-param name="day2" select="$day2"/>
              <xsl:with-param name="hour2" select="$hour2"/>
              <xsl:with-param name="minute2" select="$minute2"/>
              <xsl:with-param name="second2" select="$second2"/>
              <xsl:with-param name="TZ2" select="$TZ2"/>
            </xsl:call-template>
          </xsl:when>
          <!-- No command was given.  Return the unformatted string
               (dd mm yyyy). -->
          <xsl:otherwise>
            <xsl:value-of select="$day"/>
            <xsl:text> </xsl:text>
            <xsl:value-of select="$month"/>
            <xsl:text> </xsl:text>
            <xsl:value-of select="$year"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <!-- subtract-date subtracts the second date structure from the
       first and returns the difference in hours. -->
  <!-- This template recurses.  First it eliminates any difference in
       time, then it rewinds the second date one month at a time,
       adding to a running total of hours.  When the two dates are in
       the same month, it exits with the total difference. -->
  <xsl:template name="subtract-date">
    <xsl:param name="year" select="'0'"/>
    <xsl:param name="month" select="'0'"/>
    <xsl:param name="day" select="'0'"/>
    <xsl:param name="hour" select="'0'"/>
    <xsl:param name="minute" select="'0'"/>
    <xsl:param name="second" select="'0'"/>
    <xsl:param name="TZ" select="'0'"/>
    <xsl:param name="year2" select="'0'"/>
    <xsl:param name="month2" select="'0'"/>
    <xsl:param name="day2" select="'0'"/>
    <xsl:param name="hour2" select="'0'"/>
    <xsl:param name="minute2" select="'0'"/>
    <xsl:param name="second2" select="'0'"/>
    <xsl:param name="TZ2" select="'0'"/>
    <!-- $hour-diff is a running total of the difference between the
         two times, in hours. -->
    <xsl:param name="hour-diff" select="'0'"/>

    <xsl:choose>
      <!-- The times of day are different. -->
      <xsl:when test="$second != $second2 or
                      $minute != $minute2 or
                      $hour != $hour2">
        <!-- Call the template again with the time subtracted. -->
        <xsl:call-template name="subtract-date">
          <xsl:with-param name="year" select="$year"/>
          <xsl:with-param name="month" select="$month"/>
          <xsl:with-param name="day" select="$day"/>
          <xsl:with-param name="year2" select="$year2"/>
          <xsl:with-param name="month2" select="$month2"/>
          <xsl:with-param name="day2" select="$day2"/>
          <!-- The difference in hours based on the times alone; may
               be negative. -->
          <xsl:with-param name="hour-diff"
                          select="$hour2 - $hour +
                                  (($minute2 - $minute +
                                    (($second2 - $second)
                                     div 60.0))
                                   div 60.0)"/>
        </xsl:call-template>
      </xsl:when>
      <!-- There's an existing hour difference, and it's negative. -->
      <xsl:when test="$hour-diff &lt; 0">
        <xsl:call-template name="subtract-date">
          <xsl:with-param name="year" select="$year"/>
          <xsl:with-param name="month" select="$month"/>
          <xsl:with-param name="day" select="$day"/>
          <xsl:with-param name="year2" select="$year2"/>
          <xsl:with-param name="month2" select="$month2"/>
          <!-- Take away one day (may make the day 0)... -->
          <xsl:with-param name="day2" select="$day2 - 1"/>
          <!-- ... and add 24 to the hours.  Carrying! -->
          <xsl:with-param name="hour-diff" select="$hour-diff + 24"/>
        </xsl:call-template>
      </xsl:when>
      <!-- The dates are in the same month. -->
      <xsl:when test="$day != $day2 and $month = $month2
                      and $year = $year2">
        <!-- Exit with the difference in days plus any time
             difference. -->
        <xsl:value-of select="($day2 - $day) * 24 + $hour-diff"/>
      </xsl:when>
      <!-- The dates are in different months. -->
      <xsl:when test="$month != $month2 or $year != $year2">
        <xsl:choose>
          <!-- The second month is January. -->
          <xsl:when test="$month2 = 1">
            <xsl:call-template name="subtract-date">
              <xsl:with-param name="year" select="$year"/>
              <xsl:with-param name="month" select="$month"/>
              <xsl:with-param name="day" select="$day"/>
              <!-- Set the second date to 31 December of the preceding
                   year. -->
              <xsl:with-param name="year2" select="$year2 - 1"/>
              <xsl:with-param name="month2" select="12"/>
              <xsl:with-param name="day2" select="31"/>
              <!-- Add the number of days in January to the
                   subtotal. -->
              <xsl:with-param name="hour-diff"
                              select="$hour-diff + ($day2 * 24)"/>
            </xsl:call-template>
          </xsl:when>
          <!-- The second month is March. -->
          <xsl:when test="$month2 = 3">
            <xsl:choose>
              <!-- It's a leap year! -->
              <xsl:when test="$year mod 4 = 0 and
                               not($year mod 100 = 0 and
                                   not($year mod 400 = 0))">
                <xsl:call-template name="subtract-date">
                  <xsl:with-param name="year" select="$year"/>
                  <xsl:with-param name="month" select="$month"/>
                  <xsl:with-param name="day" select="$day"/>
                  <xsl:with-param name="year2" select="$year2"/>
                  <!-- Set the second date to 29 February. -->
                  <xsl:with-param name="month2" select="$month2 - 1"/>
                  <xsl:with-param name="day2" select="29"/>
                  <!-- Add the number of days in March to the
                       subtotal. -->
                  <xsl:with-param name="hour-diff"
                                  select="$hour-diff + ($day2 * 24)"/>
                </xsl:call-template>
              </xsl:when>
              <!-- Just a boring year. -->
              <xsl:otherwise>
                <xsl:call-template name="subtract-date">
                  <xsl:with-param name="year" select="$year"/>
                  <xsl:with-param name="month" select="$month"/>
                  <xsl:with-param name="day" select="$day"/>
                  <xsl:with-param name="year2" select="$year2"/>
                  <!-- Set the second date to 28 February. -->
                  <xsl:with-param name="month2" select="$month2 - 1"/>
                  <xsl:with-param name="day2" select="28"/>
                  <!-- Add the number of days in March to the
                       subtotal. -->
                  <xsl:with-param name="hour-diff"
                                  select="$hour-diff + ($day2 * 24)"/>
                </xsl:call-template>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:when>
          <!-- The second month is May, July, October, or December -->
          <!-- Thirty days hath September,
               April, June, and November. -->
          <xsl:when test="$month2 = 5 or $month2 = 7 or $month2 = 10
                          or $month2 = 12">
            <xsl:call-template name="subtract-date">
              <xsl:with-param name="year" select="$year"/>
              <xsl:with-param name="month" select="$month"/>
              <xsl:with-param name="day" select="$day"/>
              <xsl:with-param name="year2" select="$year2"/>
              <!-- Set the second date to the 30th of the previous
                   month. -->
              <xsl:with-param name="month2" select="$month2 - 1"/>
              <xsl:with-param name="day2" select="30"/>
              <!-- Add the number of days in the partial month to the
                   subtotal. -->
              <xsl:with-param name="hour-diff"
                              select="$hour-diff + ($day2 * 24)"/>
            </xsl:call-template>
          </xsl:when>
          <!-- All the rest have thirty-one.
               How does that poem end? -->
          <xsl:otherwise>
            <xsl:call-template name="subtract-date">
              <xsl:with-param name="year" select="$year"/>
              <xsl:with-param name="month" select="$month"/>
              <xsl:with-param name="day" select="$day"/>
              <xsl:with-param name="year2" select="$year2"/>
              <!-- Set the second date to the 31st of the previous
                   month. -->
              <xsl:with-param name="month2" select="$month2 - 1"/>
              <xsl:with-param name="day2" select="31"/>
              <!-- Add the number of days in the partial month to the
                   subtotal. -->
              <xsl:with-param name="hour-diff"
                              select="$hour-diff + ($day2 * 24)"/>
            </xsl:call-template>
          </xsl:otherwise>
          <!-- Thirty days hath September;
               All the rest I can't remember.
               There's a calendar there on the wall;
               Why are you asking me at all? -->
        </xsl:choose>
      </xsl:when>
      <!-- The dates are the same. -->
      <xsl:otherwise>
        <xsl:value-of select="$hour-diff"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <!-- sum-items produces a series of tr elements.  There is one such
       element for every set of billed periods that have the same
       billed rate and project.  Each such row lists the project
       worked on, the number of hours, the rate, and the subtotal for
       that project and rate.  The last row gives the total of all
       projects and rates. -->
  <!-- A note on currencies:
         The DTD has a currency attribute on the time element, so
       different billable times could be billed in different
       currencies for an international agency.
         To fully enable this, the DTD needs a conversion rate and a
       master currency for a given invoice; this is not present, so
       the DTD can only support invoices that are entirely in one
       currency.
         Beyond that, the stylesheet is hardcoded to output a dollar
       sign and two decimal places in the output.  This should be
       fixed at some point. -->
  <xsl:template name="sum-items">
    <!-- This template is recursive.  All of these parameters must be
         given on every call, including initialization on the first
         call. -->

    <!-- $this-item is the item to begin processing with; all items
         with the same rate and project will be handled. -->
    <xsl:param name="this-item"/>

    <!-- $items is the list of items (time elements) left to process,
         including $this-item. -->
    <xsl:param name="items"/>

    <!-- $this-proj is the project of the current item.  I could have
         determined this from the current item, but it's easier this
         way. -->
    <xsl:param name="this-proj"/>

    <!-- $this-rate is the rate of the current item.  Ditto comment on
         $this-proj. -->
    <xsl:param name="this-rate"/>

    <!-- $hours-this-proj-rate is a running subtotal of the hours
         processed so far on this project at this rate. -->
    <xsl:param name="hours-this-proj-rate"/>

    <!-- $total is the running total billed amount for all items
         processed so far. -->
    <xsl:param name="total"/>

    <!-- $other-items is the list of items other than the current
         one. -->
    <xsl:variable name="other-items"
                  select="$items[@start != $this-item/@start]"/>

    <!-- $last-at-proj-rate is true if this is the last time item to
         be billed for the current project and rate. -->
    <xsl:variable name="last-at-proj-rate"
                  select="not($other-items and
                              $other-items[(@rate = $this-rate) and
                                           (@projref = $this-proj)])"/>

    <!-- $hours-this-item is how many hours this particular chunk of
         time is for. -->
    <xsl:variable name="hours-this-item">
      <!-- Call the process-date template to do the math.  That will
           subtract the second string's date from that of the first
           string. -->
      <xsl:call-template name="process-date">
        <!-- Give it the ISO 8601 end attribute as the first
             string. -->
        <xsl:with-param name="string" select="$this-item/@end"/>

        <!-- Send the ISO 8601 start attribute as the second
             string. -->
        <xsl:with-param name="string2" select="$this-item/@start"/>

        <!-- The command for process-date is 's' for subtract. -->
        <xsl:with-param name="command" select="'s'"/>
      </xsl:call-template>
    </xsl:variable>

    <!-- $hours-at-proj-rate is the new subtotal for the number of
         hours on this project at this rate.  Add the previous
         subtotal to the hours represented by this billable chunk. -->
    <xsl:variable name="hours-at-proj-rate"
                  select="$hours-this-proj-rate + $hours-this-item"/>

    <!-- If this is the last chunk of time on this project at this
         billable rate, add a tr to the result tree with the subtotal
         for this rate and project combination. -->
    <xsl:if test="$last-at-proj-rate">
      <tr valign="baseline">
        <td>
          <!-- Give the short description of the project, with a link
               to the full description. -->
          <xsl:apply-templates select="id($this-proj)/shortdesc"
                               mode="itemized"/>
        </td>
        <td>
          <!-- Give the number of hours to up to four decimal places.
               No real reason why four and not two, three, or
               five. -->
          <xsl:value-of select="format-number($hours-at-proj-rate,
                                              '0.####')"/>
          <xsl:text> hour</xsl:text>
	  <xsl:if test="not($hours-at-proj-rate = 1)">
	    <xsl:text>s</xsl:text>
	  </xsl:if>
        </td>
        <td align="right">
          <!-- Dollars (though not necessarily USD) are hard-coded.
               This should probably change. -->
          <xsl:text>@ $</xsl:text>

          <!-- Give the hourly rate for the current billable item. -->
          <xsl:value-of select="$this-rate"/>
          <xsl:text>/hr</xsl:text>
        </td>
        <td align="right">
          <!-- Again, dollars are hard-coded. -->
          <xsl:text>$</xsl:text>

          <!-- As are two decimal places.  This should probably use
               Java currency format strings. -->
          <xsl:value-of select="format-number($hours-at-proj-rate *
                                                $this-rate,
                                              '#,##0.00')"/>
        </td>
      </tr>
    </xsl:if>

    <!-- Now what? -->
    <xsl:choose>
      <!-- There are other items left to process. -->
      <xsl:when test="$other-items">
        <xsl:choose>
          <!-- This was the last at the current project and rate. -->
          <xsl:when test="$last-at-proj-rate">
            <!-- Recurse to the same template. -->
            <xsl:call-template name="sum-items">
              <!-- The active item is the first unprocessed one. -->
              <xsl:with-param name="this-item"
                              select="$other-items[1]"/>

              <!-- The list is all items except the current one. -->
              <xsl:with-param name="items" select="$other-items"/>

              <!-- The project is the project of the first unprocessed
                   item. -->
              <xsl:with-param name="this-proj"
                              select="$other-items[1]/@projref"/>

              <!-- The rate is the rate of the first unprocessed
                   item. -->
              <xsl:with-param name="this-rate"
                              select="$other-items[1]/@rate"/>

              <!-- There are not yet any hours at that project and
                   rate. -->
              <xsl:with-param name="hours-this-proj-rate"
                              select="'0'"/>

              <!-- The new running total is the old total plus the
                   subtotal from the current project and rate. -->
              <xsl:with-param name="total"
                              select="$total + ($hours-at-proj-rate *
                                                $this-rate)"/>
            </xsl:call-template>
          </xsl:when>
          <!-- There are other items at the current project and
               rate. -->
          <xsl:otherwise>
            <!-- Recurse to the same template. -->
            <xsl:call-template name="sum-items">
              <!-- This item is the next item at the same rate and
                   project. -->
              <xsl:with-param name="this-item"
                              select="$other-items[(@rate =
                                                    $this-rate) and
                                                   (@projref =
                                                    $this-proj)][1]"/>

              <!-- The items are the current other items. -->
              <xsl:with-param name="items" select="$other-items"/>

              <!-- This project is unchanged. -->
              <xsl:with-param name="this-proj" select="$this-proj"/>

              <!-- As is the rate. -->
              <xsl:with-param name="this-rate" select="$this-rate"/>

              <!-- The hours at this project and rate have already been
                   set by adding the current item's amount to the
                   running total. -->
              <xsl:with-param name="hours-this-proj-rate"
                              select="$hours-at-proj-rate"/>

              <!-- The total is as-yet unchanged. -->
              <xsl:with-param name="total" select="$total"/>
            </xsl:call-template>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>
      <!-- That was the last item of all. -->
      <xsl:otherwise>
        <!-- Output a grand total row. -->
        <tr valign="baseline">
          <td colspan="3" align="right">
            <xsl:text>Total:</xsl:text>
          </td>
          <th align="right">
            <!-- Once again, dollars are hard-coded. -->
            <xsl:text>$</xsl:text>
            <!-- Add the last item to the total and format it. -->
            <xsl:value-of select="format-number($total +
                                                ($hours-at-proj-rate *
                                                 $this-rate),
                                                '#,##0.00')"/>
          </th>
        </tr>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <!-- Entry point for processing.  Creates the HTML framework and
       initiates processing of the appropriate period. -->
  <xsl:template match="/">
    <html>
      <head>
        <title>
          <xsl:text>Invoice for </xsl:text>
          <xsl:value-of select="billable/client/name"/>
          <xsl:text>, </xsl:text>

          <!-- Format the start date. -->
          <xsl:call-template name="process-date">
            <xsl:with-param name="string"
                            select="$period-start-date"/>
            <xsl:with-param name="command" select="'f'"/>
          </xsl:call-template>

          <xsl:text> through </xsl:text>

          <!-- Format the end date. -->
          <xsl:call-template name="process-date">
            <xsl:with-param name="string"
                            select="$period-end-date"/>
            <xsl:with-param name="command" select="'f'"/>
          </xsl:call-template>
        </title>
        <!-- Used to link to the CSS stylesheet, but delivering two
             files to accountant types was too complicated. -->
        <!-- <link rel="stylesheet" type="text/css"
               href="invoice.css"/> -->

        <!-- Simply include the CSS file in the output HTML. -->
        <!-- Note that the CSS file must be a well-formed XML entity
             (i.e., using &lt; for < and &amp; for &).-->
        <style type="text/css">
          <xsl:text>
&css;</xsl:text>
        </style>
      </head>

      <!-- Process the billable element. -->
      <xsl:apply-templates/>
    </html>
  </xsl:template>

  <!-- address is a street address for the participant associated with
       its parent. -->
  <xsl:template match="address">
    <!-- If there is a contact child of the parent with its own
         address, skip this one; the contact's is preferred. -->
    <xsl:if test="not(../contact/address)">
      <!-- Otherwise, make a div for the address... -->
      <div class="address">
        <!-- ... and handle the children. -->
        <xsl:apply-templates/>
      </div>
    </xsl:if>
  </xsl:template>

  <!--agent contains contact and billing information for the billing
      agent. -->
  <xsl:template match="agent">
    <!-- Just make a div with the appropriate class... -->
    <div class="agent">
      <!-- ... and handle the children. -->
      <xsl:apply-templates/>
      <!-- If the agent has a TIN, display it here. -->
      <xsl:apply-templates select="@taxpayer"/>
    </div>
  </xsl:template>

  <!-- billable contains client and agent information, billable
       periods, and maybe other things.  -->
  <xsl:template match="billable">
    <!-- Make the HTML body. -->
    <body>
      <!-- Process the billing agent. -->
      <xsl:apply-templates select="agent"/>

      <!-- Process the billed client. -->
      <xsl:apply-templates select="client"/>

      <!-- Process the single period for which this invoice is being
           generated. -->
      <xsl:apply-templates select="$period"/>

      <!-- Process the billed project descriptions. -->
      <div class="projects">
        <h4 class="projs-header">
          <xsl:text>Billed Projects</xsl:text>
        </h4>

        <!-- Select only the projects referenced in the billed
             period. -->
        <xsl:apply-templates
          select="project[@id = $period/time/@projref]"/>
      </div>

      <!-- Print a vanity footer. -->
      <hr/>
      <p class="footer">
        <xsl:text>This invoice generated by </xsl:text>
        <span class="filename">
          <xsl:text>invoice.xsl</xsl:text>
        </span>
        <xsl:text> v</xsl:text>

        <!-- Get the stylesheet version number. -->
        <xsl:value-of select="$stylesheet-version"/>

        <xsl:text>.</xsl:text>
      </p>
    </body>
  </xsl:template>

  <!-- building is a component in an address. -->
  <!-- department is a component in an address. -->
  <!-- nation is a component in an address. -->
  <!-- street contains a street address. -->
  <!-- suite is a suite or apartment number. -->
  <!-- village is a component in an address. -->
  <xsl:template
    match="building | department | nation | street | suite | village">
    <!-- Make a paragraph with the appropriate class... -->
    <p class="{name(.)}">
      <!-- ... and handle the children. -->
      <xsl:apply-templates/>
    </p>
  </xsl:template>

  <!-- city is a component in an address. -->
  <xsl:template match="city">
    <!-- If we have a city, it appears first in a paragraph with the
         state and postal code. -->
    <p>
      <!-- Give a class depending on what's in the paragraph. -->
      <xsl:attribute name="class">
        <xsl:text>city</xsl:text>
        <xsl:if test="../state">
          <xsl:text>-state</xsl:text>
        </xsl:if>
        <xsl:if test="../postcode">
          <xsl:text>-post</xsl:text>
        </xsl:if>
      </xsl:attribute>

      <!-- Create a span just for the city... -->
      <span class="city">
        <!-- ... and handle the content. -->
        <xsl:apply-templates/>
      </span>

      <!-- If there's a state, output a comma and then its value, with
           a mode to indicate that it's in a paragraph with a
           city. -->
      <xsl:if test="../state">
        <xsl:text>, </xsl:text>
        <xsl:apply-templates select="../state" mode="csp"/>
      </xsl:if>

      <!-- If there's a postal code, output a space and then its
           value, with a mode to indicate that it's in a paragraph
           with a city. -->
      <xsl:if test="../postcode">
        <xsl:text> </xsl:text>
        <xsl:apply-templates select="../postcode" mode="csp"/>
      </xsl:if>
    </p>
  </xsl:template>

  <!-- client contains contact and billing information for the billed
       customer. -->
  <!-- contact is the name of an individual within an organization who
       serves as point of contact for transactions. -->
  <xsl:template match="client | contact">
    <!-- Just make a div with the appropriate class... -->
    <div class="{name(.)}">
      <!-- ... and handle the children. -->
      <xsl:apply-templates/>
    </div>
  </xsl:template>

  <!-- email is an e-mail address for the participant associated with
       its parent. -->
  <xsl:template match="email">
    <!-- If there is a contact child of the parent with its own e-mail
         address, skip this one; the contact's is preferred. -->
    <xsl:if test="not(../contact/email)">
      <!-- Make a paragraph with the appropriate class. -->
      <p class="email">
        <!-- Make a label. -->
        <span class="contact-item">
          <xsl:text>E-mail</xsl:text>
        </span>
        <xsl:text>: </xsl:text>

        <!-- Make a link. -->
        <a class="email">
          <!-- Set the href to the e-mail address. -->
          <xsl:attribute name="href">
            <xsl:text>mailto:</xsl:text>
            <xsl:value-of select="."/>
          </xsl:attribute>

          <!-- Set the title to the name corresponding to this
               address. -->
          <xsl:attribute name="title">
            <xsl:value-of select="../name"/>
          </xsl:attribute>

          <!-- Process the children. -->
          <xsl:apply-templates/>
        </a>
      </p>
    </xsl:if>
  </xsl:template>

  <!-- fax is a facsimile number for the participant associated with
       its parent. -->
  <xsl:template match="fax">
    <!-- If there is a contact child of the parent with its own fax
         number, skip this one; the contact's is preferred. -->
    <xsl:if test="not(../contact/fax)">
      <!-- Make a paragraph with the appropriate class. -->
      <p class="fax">
        <!-- Make a label. -->
        <span class="contact-item">
          <xsl:text>Facsimile</xsl:text>
        </span>
        <xsl:text>: </xsl:text>

        <!-- Make a span for the actual number... -->
        <span class="fax">
          <!-- ... and handle the children. -->
          <xsl:apply-templates/>
        </span>
      </p>
    </xsl:if>
  </xsl:template>

  <!-- name is a company or individual, the primary business name of a
       transaction participant. -->
  <!-- Create appropriate heading levels or paragraphs, with classes
       for the different kinds of names. -->

  <xsl:template match="agent/name">
    <h3 class="agent-name">
      <xsl:apply-templates/>
    </h3>
  </xsl:template>

  <xsl:template match="client/name">
    <h2 class="client-name">
      <xsl:apply-templates/>
    </h2>
  </xsl:template>

  <xsl:template match="contact/name">
    <p class="contact-name">
      <xsl:apply-templates/>
    </p>
  </xsl:template>

  <!-- === End of name templates. === -->

  <!-- para is a textual paragraph. -->
  <xsl:template match="para">
    <p class="para">
      <xsl:apply-templates/>
    </p>
  </xsl:template>

  <!-- period represents a billing interval containing billable
       items. -->
  <!-- This is where all the fun happens. -->
  <xsl:template match="period">
    <!-- Make a table for the itemized invoice. -->
    <table width="100%" border="1">
      <caption>
        <!-- Generate an ID for the invoice based on the client's ID
             and the end date ISO 8601 representation. -->
        <span class="invoice-header">
          <xsl:text>Invoice ID </xsl:text>
          <xsl:value-of select="/billable/client/@id"/>
          <xsl:text>-</xsl:text>
          <xsl:value-of select="$period-end-date"/>
        </span>
        <br/>

        <!-- Print the start and end dates of the invoice. -->
        <span class="invoice-date">
          <!-- Format the start date. -->
          <xsl:call-template name="process-date">
            <xsl:with-param name="string"
                            select="$period-start-date"/>
            <xsl:with-param name="command" select="'f'"/>
          </xsl:call-template>

          <xsl:text> through </xsl:text>

          <!-- Format the end date. -->
          <xsl:call-template name="process-date">
            <xsl:with-param name="string"
                            select="$period-end-date"/>
            <xsl:with-param name="command" select="'f'"/>
          </xsl:call-template>
        </span>
      </caption>
      <tbody>
        <!-- Call the template that does the dirty work. -->
        <xsl:call-template name="sum-items">
          <!-- The template is recursive, and needs an initial state.
               Probably, I should set some defaults in the template so
               that the initialization happens automatically based on
               $items, but there's enough magic happening already. -->

          <!-- The list of items to sum is initialized with the first
               billable period as the first item to process, including
               finding others like it.  It might be better to
               automatically sort the billable list by rate and
               project, but this just does it in the order they come
               along. -->
          <xsl:with-param name="this-item" select="*[1]"/>

          <!-- The list of items to sum is all time elements within
               this period. -->
          <xsl:with-param name="items" select="*"/>

          <!-- The first project is the project of the first item. -->
          <xsl:with-param name="this-proj" select="*[1]/@projref"/>

          <!-- The first active rate is the rate of the first
               item. -->
          <xsl:with-param name="this-rate" select="*[1]/@rate"/>

          <!-- The running subtotal of hours on this project at this
               rate is zero. -->
          <xsl:with-param name="hours-this-proj-rate" select="'0'"/>

          <!-- The running total bill is zero. -->
          <xsl:with-param name="total" select="'0'"/>
        </xsl:call-template>
      </tbody>
    </table>
  </xsl:template>

  <!-- postcode is a component in an address. -->
  <!-- state is a component in an address. -->

  <!-- In the default mode, we have to be careful not to be processed
       twice. -->
  <xsl:template match="postcode | state">
    <!-- If there's a city in the same address, these elements need to
         be in the same paragraph.  The city template will call for
         the processing of these elements in the csp mode. -->
    <xsl:if test="not(../city)">
      <!-- Otherwise, make a paragraph for each element.  If there's
           no city, the address is a little sketchy anyway, so it's
           not really a big deal, but we shouldn't lose any
           information. -->
      <p class="{name(.)}">
        <xsl:apply-templates/>
      </p>
    </xsl:if>
  </xsl:template>

  <!-- If there is a city, make a span within the paragraph that's
       already been set up by the city template. -->
  <xsl:template match="postcode | state" mode="csp">
    <span class="{name(.)}">
      <xsl:apply-templates/>
    </span>
  </xsl:template>

  <!-- === End of postcode and state. === -->

  <!-- project is a description of a project for which billable work
       is being done. -->
  <xsl:template match="project">
    <!-- Make a division with an appropriate class. -->
    <div class="project">
      <!-- We could put an ID here, but until it's more universally
           supported for linking, I'd prefer to just use the a element
           and name attribute on the project's short description,
           which doubles as the division title.  You can't use both,
           since that makes linking ambiguous (though the net result
           is the same). -->

      <!-- Handle the children. -->
      <xsl:apply-templates/>
    </div>
  </xsl:template>

  <!-- shortdesc is a short description of a project, used in
       summaries. -->

  <!-- In the full project description, make a title for the
       project. -->
  <xsl:template match="shortdesc">
    <!-- Make a paragraph with an appropriate class for styling. -->
    <p class="proj-header">
      <!-- Make an anchor, as the billable time entries will link to
           the full project description. -->
      <a name="{../@id}" id="{../@id}">
        <!-- Handle the children. -->
        <xsl:apply-templates/>
      </a>
    </p>
  </xsl:template>

  <!-- In the itemized list, make a link to the full description. -->
  <xsl:template match="shortdesc" mode="itemized">
    <p class="proj-item">
      <a href="#{../@id}">
        <xsl:apply-templates/>
      </a>
    </p>
  </xsl:template>

  <!-- === End of shortdesc. === -->

  <!-- telephone is a telephone number for the participant associated
       with its parent. -->
  <xsl:template match="telephone">
    <!-- If there is a contact child of the parent with its own phone
         number, skip this one; the contact's is preferred. -->
    <xsl:if test="not(../contact/telephone)">
      <!-- Make a paragraph with the appropriate class. -->
      <p class="telephone">
        <!-- Make a label. -->
        <span class="contact-item">
          <xsl:text>Telephone</xsl:text>
        </span>
        <xsl:text>: </xsl:text>

        <!-- Make a span for the actual number... -->
        <span class="telephone">
          <!-- ... and handle the children. -->
          <xsl:apply-templates/>
        </span>
      </p>
    </xsl:if>
  </xsl:template>

  <!-- uri is a URI for the participant associated with its
       parent. -->
  <xsl:template match="uri">
    <!-- If there is a contact child of the parent with its own URI,
         skip this one; the contact's is preferred. -->
    <xsl:if test="not(../contact/uri)">
      <!-- Make a pargraph with the appropriate class. -->
      <p class="uri">
        <!-- Make a label. -->
        <span class="contact-item">
          <xsl:text>Web</xsl:text>
        </span>
        <xsl:text>: </xsl:text>

        <!-- Make a link. -->
        <a class="uri">
          <!-- Set the href. -->
          <xsl:attribute name="href">
            <xsl:choose>
              <!-- If the content is cosmetic, with the real URI given
                   as an attribute, use the attribute in the link. -->
              <xsl:when test="@real-uri">
                <xsl:value-of select="@real-uri"/>
              </xsl:when>

              <!-- Otherwise, use the content of the element. -->
              <xsl:otherwise>
                <xsl:value-of select="normalize-space()"/>
              </xsl:otherwise>
            </xsl:choose>
          </xsl:attribute>

          <!-- Set the title to the name corresponding to this
               URI. -->
          <xsl:attribute name="title">
            <xsl:value-of select="../name"/>
          </xsl:attribute>

          <!-- Process the children. -->
          <xsl:apply-templates/>
        </a>
      </p>
    </xsl:if>
  </xsl:template>

  <!-- The taxpayer attribute on an agent represents the agent's
       Taxpayer Identification Number. -->
  <xsl:template match="@taxpayer">
    <!-- Make an appropriate paragraph -->
    <p class="taxpayer">
      <span class="contact-item">
	<xsl:text>TIN</xsl:text>
      </span>
      <xsl:text>: </xsl:text>
      <xsl:value-of select="."/>
    </p>
  </xsl:template>
</xsl:transform>
