Newer
Older
XML / modules / paper-calendar.xml
<?xml version="1.0" encoding="utf-8"?>

<!--
	Elements for generating a teaching calendar for a paper.
-->

<stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

	
	<!--
		The top level calendar element.
		
		@number-of-weeks: The duration of the paper in weeks. [required]
		
		@lectures-per-week: The number of lectures per week in the paper. Each lecture is assumed to occupy exactly one row in the resulting table, so this effectively determines the number of rows in the resulting table that each week occupies. [required]
	-->
	<template match="calendar">
		<common>
			<!--
				Work out which columns to include based on the elements within the calendar. The week number and date columns will always appear in positions 1 and 2, and the rest follow in the order section, lecture, reading, laboratory, tutorial and assessment. This is slightly cumbersome, but it's pretty much forced on us by the way XSLT works.
				
				This is mainly relevant to (Xe)LaTeX in order to underline cells correctly. In (Xe)LaTeX it doesn't matter if we draw the same \clines multiple times, so if a column doesn't exist, we just set it's column number to the column number of the previous column. HTML doesn't really care, but we need to do the calculation anyway in order to work out the correct number of columns.
			-->
			<xsl:variable name="week-column">1</xsl:variable>
			<xsl:variable name="date-column">2</xsl:variable>
			<xsl:variable name="section-column" select="
				if ( count( body//section ) > 0 )
			    then $date-column + 1
			    else $date-column" />
			<xsl:variable name="lecture-column-1" select="
				if ( count( body//lecture ) > 0 )
			    then $section-column + 1
			    else $section-column" />
			<xsl:variable name="lecture-column-2" select="
				if ( count( body//lecture ) > 0 )
			    then $lecture-column-1 + 1
			    else $lecture-column-1" />
			<xsl:variable name="reading-column" select="
				if ( count( body//reading ) > 0 )
			    then $lecture-column-2 + 1
			    else $lecture-column-2" />
			<xsl:variable name="laboratory-column-1" select="
				if ( count( body//laboratory ) > 0 )
			    then $reading-column + 1
			    else $reading-column" />
			<xsl:variable name="laboratory-column-2" select="
				if ( count( body//laboratory ) > 0 )
			    then $laboratory-column-1 + 1
			    else $laboratory-column-1" />
			<xsl:variable name="tutorial-column-1" select="
				if ( count( body//tutorial ) > 0 )
			    then $laboratory-column-2 + 1
			    else $laboratory-column-2" />
			<xsl:variable name="tutorial-column-2" select="
				if ( count( body//tutorial ) > 0 )
			    then $tutorial-column-1 + 1
			    else $tutorial-column-1" />
			<xsl:variable name="assessment-column" select="
				if ( count( body//assessment ) > 0 )
			    then $tutorial-column-2 + 1
			    else $tutorial-column-2" />
			
			<!--
				The number of columns is equal to the column number of the last column.
			-->
			<xsl:variable name="num-columns" select="xs:integer( $assessment-column )" />
			
		</common>
		<common formats="/latex/xelatex/">
			<xsl:if test="not( @number-of-weeks )">
				<xsl:message terminate="yes">
					<xsl:text>Required attribute "number-of-weeks" has not been supplied in calendar.</xsl:text>
				</xsl:message>
			</xsl:if>
			<xsl:if test="not( @lectures-per-week )">
				<xsl:message terminate="yes">
					<xsl:text>Required attribute "lectures-per-week" has not been supplied in calendar.</xsl:text>
				</xsl:message>
			</xsl:if>
			
			<!--
				This is used in a few places to set the width of column cells, etc., that contain numbers. I can't remember why I decided to pick twice the number of weeks as the size, other than to ensure a decent spacing around the numbers?
			-->
			<xsl:text>\newlength{\numberwidth}</xsl:text>
			<xsl:text>\settowidth{\numberwidth}{</xsl:text>
			<xsl:number value="@number-of-weeks * 2" />
			<xsl:text>}</xsl:text>
			<xsl:call-template name="newline-internal" />

			<xsl:text>\begin{center}</xsl:text>
			<xsl:call-template name="newline-internal" />
							
			<!-- TODO: Probably not if standalone! -->
			<xsl:text>\begin{sideways}</xsl:text>
			<xsl:call-template name="newline-internal" />
							
			<xsl:text>\resizebox{24cm}{!}{%</xsl:text>
			<xsl:call-template name="newline-internal" />
							
			<xsl:text>\begin{tabular}{|c|c</xsl:text>
			<xsl:if test="$section-column > $date-column">
				<xsl:text>|c</xsl:text>
			</xsl:if>
			<xsl:if test="$lecture-column-1 > $section-column">
				<xsl:text>|c|l</xsl:text>
			</xsl:if>
			<xsl:if test="$reading-column > $lecture-column-2">
				<xsl:text>|c</xsl:text>
			</xsl:if>
			<xsl:if test="$laboratory-column-1 > $reading-column">
				<xsl:text>|c|c</xsl:text>
			</xsl:if>
			<xsl:if test="$tutorial-column-1 > $laboratory-column-2">
				<xsl:text>|c|c</xsl:text>
			</xsl:if>
			<xsl:if test="$assessment-column > $tutorial-column-2">
				<xsl:text>|c</xsl:text>
			</xsl:if>
			<xsl:text>|}</xsl:text>
			<xsl:call-template name="newline-internal" />
							
			<!--
				Calendar heading. We can't use generate-content-cell for this, because we're doing more complex processing of the cell contents than is possible with generate-content-cell. Here, we're calling three separate templates interspersed with text, whereas generate-content-cell can only accept a simple list of nodes to apply-templates to.
			-->
			<xsl:text>\multicolumn{</xsl:text>
			<xsl:value-of select="$num-columns" />
			<xsl:text>}{c}{\LARGE\textbf{</xsl:text>
			<xsl:call-template name="PaperCode" />
			<xsl:text> Teaching Calendar, </xsl:text>
			<xsl:call-template name="PaperPeriod" />
			<xsl:text>, </xsl:text>
			<xsl:call-template name="PaperYear" />
			<xsl:text>}}	\\</xsl:text>
			<xsl:call-template name="newline-internal" />
			
			<!--
				Note that using generate-empty-cell here requires about three time as much code as just doing it inline :), but we gain in terms of output consistency.
			-->
			<xsl:call-template name="generate-empty-cell">
				<xsl:with-param name="columns" select="xs:integer( $num-columns )" />
				<xsl:with-param name="rows">1</xsl:with-param>
				<xsl:with-param name="column-format">c</xsl:with-param>
			</xsl:call-template>
			<xsl:text>	\\</xsl:text>
			<xsl:call-template name="newline-internal" />
			
			<xsl:text>\hline</xsl:text>
			<xsl:call-template name="newline-internal" />
			
			<!-- The calendar itself. -->
			<xsl:apply-templates select="header">
				<xsl:with-param name="num-columns" select="xs:integer( $num-columns )" />
			</xsl:apply-templates>
			<xsl:call-template name="newline-internal" />
			
			<xsl:text>\hline\hline</xsl:text>
			<xsl:call-template name="newline-internal" />
			
			<xsl:apply-templates select="body">
				<xsl:with-param name="week-column" select="xs:integer( $week-column )" />
				<xsl:with-param name="date-column" select="xs:integer( $date-column )" />
				<xsl:with-param name="section-column" select="xs:integer( $section-column )" />
				<xsl:with-param name="lecture-column-1" select="xs:integer( $lecture-column-1 )" />
				<xsl:with-param name="lecture-column-2" select="xs:integer( $lecture-column-2 )" />
				<xsl:with-param name="reading-column" select="xs:integer( $reading-column )" />
				<xsl:with-param name="laboratory-column-1" select="xs:integer( $laboratory-column-1 )" />
				<xsl:with-param name="laboratory-column-2" select="xs:integer( $laboratory-column-2 )" />
				<xsl:with-param name="tutorial-column-1" select="xs:integer( $tutorial-column-1 )" />
				<xsl:with-param name="tutorial-column-2" select="xs:integer( $tutorial-column-2 )" />
				<xsl:with-param name="assessment-column" select="xs:integer( $assessment-column )" />
				<xsl:with-param name="num-columns" select="xs:integer( $num-columns )" />
			</xsl:apply-templates>
			<xsl:call-template name="newline-internal" />
			
			<xsl:text>\hline\hline</xsl:text>
			<xsl:call-template name="newline-internal" />
			
			<xsl:apply-templates select="footer">
				<xsl:with-param name="num-columns" select="xs:integer( $num-columns )" />
			</xsl:apply-templates>
			<xsl:call-template name="newline-internal" />
			
			<xsl:text>\hline</xsl:text>
			<xsl:call-template name="newline-internal" />

			<xsl:text>\end{tabular}%</xsl:text>
			<xsl:call-template name="newline-internal" />
			
			<xsl:text>}</xsl:text>
			<xsl:call-template name="newline-internal" />
			
			<xsl:text>\end{sideways}</xsl:text>
			<xsl:call-template name="newline-internal" />
			
			<xsl:text>\end{center}</xsl:text>
			<xsl:call-template name="newline-internal" />
		</common>
		<common formats="/html/xhtml/">
			<!-- Calendar heading. -->
			<h2>
				<xsl:call-template name="PaperCode" />
				<xsl:text> Teaching Calendar, </xsl:text>
				<xsl:call-template name="PaperPeriod" />
				<xsl:text>, </xsl:text>
				<xsl:call-template name="PaperYear" />
			</h2>
			
			<!-- The calendar itself. -->
			<div class="sans">
				<table class="small calendar" summary="Paper calendar with relevant links" cellspacing="0">
					<!--
						I was going to generate COL elements here, but they don't seem to be supported by all browsers. Damn. The same applies for THEAD/TFOOT/TBODY, but I may as well leave them in for the browsers that do support them.
					-->
					<thead class="calendar">
						<xsl:apply-templates select="header">
							<xsl:with-param name="num-columns" select="xs:integer( $num-columns )" />
						</xsl:apply-templates>
					</thead>
					<tfoot class="calendar">
						<xsl:apply-templates select="footer">
							<xsl:with-param name="num-columns" select="xs:integer( $num-columns )" />
						</xsl:apply-templates>
					</tfoot>
					<tbody class="calendar">
						<xsl:apply-templates select="body">
							<xsl:with-param name="week-column" select="xs:integer( $week-column )" />
							<xsl:with-param name="date-column" select="xs:integer( $date-column )" />
							<xsl:with-param name="section-column" select="xs:integer( $section-column )" />
							<xsl:with-param name="lecture-column-1" select="xs:integer( $lecture-column-1 )" />
							<xsl:with-param name="lecture-column-2" select="xs:integer( $lecture-column-2 )" />
							<xsl:with-param name="reading-column" select="xs:integer( $reading-column )" />
							<xsl:with-param name="laboratory-column-1" select="xs:integer( $laboratory-column-1 )" />
							<xsl:with-param name="laboratory-column-2" select="xs:integer( $laboratory-column-2 )" />
							<xsl:with-param name="tutorial-column-1" select="xs:integer( $tutorial-column-1 )" />
							<xsl:with-param name="tutorial-column-2" select="xs:integer( $tutorial-column-2 )" />
							<xsl:with-param name="assessment-column" select="xs:integer( $assessment-column )" />
							<xsl:with-param name="num-columns" select="xs:integer( $num-columns )" />
						</xsl:apply-templates>
					</tbody>
				</table>
			</div>
		</common>
	</template>
	
	
	<!--
		Header/footer rows for the calendar table. To get proper table headings/footings, use HEADING/FOOTING. For other text, use NOTE.
	-->
	<template match="header|footer">
		<common>
			<xsl:param name="num-columns" />
		</common>
		<common formats="/latex/xelatex/">
			<xsl:apply-templates>
				<xsl:with-param name="num-columns" select="xs:integer( $num-columns )" />
			</xsl:apply-templates>
			<xsl:text>	\\</xsl:text>
			
			<!--
				It's left to the parent template to add horizontal rules after the last header/footer element, as what gets output may vary depending on position in the table (e.g., double rule after the last header, but single rule after the last footer).
			-->
			<xsl:if test="position() != last()">
				<xsl:text>	\hline</xsl:text>
			</xsl:if>
			<xsl:call-template name="newline-internal" />
		</common>
		<common formats="/html/xhtml/">
			<tr>
				<xsl:apply-templates>
					<xsl:with-param name="num-columns" select="xs:integer( $num-columns )" />
				</xsl:apply-templates>
			</tr>
		</common>
	</template>
	
	
	<!--
		A table heading or footing, bolded, centered, etc. Headings and footings are assumed to occupy a single row only.
		
		@columns: The number of columns that the heading or footing spans.
	-->
	<template match="heading|footing">
		<common>
			<xsl:param name="num-columns" />
		</common>
		<common formats="/latex/xelatex/">
			<xsl:call-template name="generate-content-cell">
				<!-- I'm slightly amazed that this actually works! -->
				<xsl:with-param name="columns" select="if ( @columns ) then @columns else 1" />
				<xsl:with-param name="rows">1</xsl:with-param>
				<xsl:with-param name="style">\bfseries</xsl:with-param>
			</xsl:call-template>

			<xsl:if test="position() != last()">
				<xsl:call-template name="tabular-column-separator" />
			</xsl:if>
		</common>
		<common formats="/html/xhtml/">
			<!--
				We can't use generate-content-cell here because we need TH rather than TD. This is a one-off anyway.
			-->
			<th class="calendar">
				<xsl:if test="@columns">
					<xsl:attribute name="colspan">
						<xsl:value-of select="@columns" />
					</xsl:attribute>
				</xsl:if>
				<xsl:apply-templates />
			</th>
		</common>
	</template>
	
	
	<!--
		A miscellaneous piece of text to be inserted somewhere in the calendar table. Notes are assumed to occupy a single row only. This is particularly true for LaTeX where we're working within a tabular environment, which means there's no automatic wrapping of long lines anyway.
		
		@columns: The number of columns that the heading or footing spans.
	-->
	<template match="note">
		<common>
			<xsl:param name="num-columns" />
		</common>
		<common formats="/latex/xelatex/">
			<xsl:call-template name="generate-content-cell">
				<xsl:with-param name="columns" select="if ( @columns ) then @columns else xs:integer( $num-columns )" />
				<xsl:with-param name="rows">1</xsl:with-param>
				<xsl:with-param name="align">left</xsl:with-param>
				<xsl:with-param name="column-format">|l|</xsl:with-param>
			</xsl:call-template>
		</common>
		<common formats="/html/xhtml/">
			<xsl:call-template name="generate-content-cell">
				<xsl:with-param name="columns" select="if ( @columns ) then @columns else xs:integer( $num-columns )" />
				<xsl:with-param name="rows">1</xsl:with-param>
				<xsl:with-param name="align">left</xsl:with-param>
			</xsl:call-template>
		</common>
	</template>
	
	
	<!--
		A week in the calendar. Each week has an associated number (automatically generated) and a date range, and spans some defined number of rows (default 2). The week number and date range are only generated for the first of these rows.
	-->
	<template match="week">
		<common>
			<xsl:param name="num-columns" />
		</common>
		<common formats="/latex/xelatex/">
			<xsl:param name="week-column" />
			<xsl:param name="date-column" />
			<xsl:param name="section-column" />
			<xsl:param name="lecture-column-1" />
			<xsl:param name="lecture-column-2" />
			<xsl:param name="reading-column" />
			<xsl:param name="laboratory-column-1" />
			<xsl:param name="laboratory-column-2" />
			<xsl:param name="tutorial-column-1" />
			<xsl:param name="tutorial-column-2" />
			<xsl:param name="assessment-column" />
		
			<xsl:for-each select="row">
				
				<!--
					If we're going to start a new section on this row, draw a rule under the previous section to separate them (editorial: tabular borders in LaTeX are pretty awful compared to HTML). The horrible XPath logic is as follows:
					
						* the current row contains at least one section element; and
							* either
								* there is at least one week preceding the current week; and
								* the immediately preceding week /isn't/ a holiday;
							* or
								* this is not the first row of the current week; and
									* the current week is the first week in the calendar; or
									* the immediately preceding week /is/ a holiday.
					
					(YOUR HEAD A SPLODE)
				-->
				<xsl:if test="		child::section
								and	(	(	( count( ../preceding-sibling::week ) > 0 )
										and	( not( ../preceding-sibling::week[1]/@holiday ) )
										)
									or	(	( position() > 1 )
										and	(	( count( ../preceding-sibling::week ) = 0 )
											or	( ../preceding-sibling::week[1]/@holiday )
											)
										)
									)">
					<xsl:text> \cline{</xsl:text>
					<xsl:value-of select="$section-column" />
					<xsl:text>-</xsl:text>
					<xsl:value-of select="$section-column" />
					<xsl:text>}</xsl:text>
				</xsl:if>
	
				<!-- Output the week number and dates columns, but only on the first row of the week. -->
				<xsl:choose>
					<xsl:when test="position() = 1">
						<!-- Week number. -->
						<xsl:call-template name="generate-number-cell">
							<xsl:with-param name="number" select="1 + count( preceding::week[not( @holiday )] )" />
						</xsl:call-template>
						<xsl:call-template name="tabular-column-separator" />
		
						<!-- Date range. -->
						<xsl:call-template name="generate-content-cell">
							<xsl:with-param name="nodes" select="../dates" />
						</xsl:call-template>
					</xsl:when>
					<xsl:otherwise>
						<xsl:call-template name="tabular-column-separator" />
					</xsl:otherwise>
				</xsl:choose>
				
				<!--
					We have to apply each of the sub-templates manually, not so much to ensure the correct ordering, but so that we can correctly output empty cells when a lecture, laboratory or tutorial element is missing.
				-->
				<xsl:if test="$section-column > $date-column">
					<xsl:call-template name="tabular-column-separator" />
				</xsl:if>
				<xsl:apply-templates select="section" />
				
				<!--
					Lecture cells span two columns, so we need to explicitly output a two-column empty cell if there's no lecture element for the current row.
				-->
				<xsl:if test="$lecture-column-1 > $section-column">
					<xsl:call-template name="tabular-column-separator" />
					<xsl:if test="count( lecture ) = 0">
						<xsl:call-template name="generate-empty-cell">
							<xsl:with-param name="columns">2</xsl:with-param>
							<xsl:with-param name="rows">1</xsl:with-param>
							<xsl:with-param name="column-format">c|</xsl:with-param>
						</xsl:call-template>
					</xsl:if>
				</xsl:if>
				<xsl:apply-templates select="lecture" />
	
				<xsl:if test="$reading-column > $lecture-column-2">
					<xsl:call-template name="tabular-column-separator" />
				</xsl:if>
				<xsl:apply-templates select="reading" />
	
				<!--
					Labs and tutorials are similar to lectures, except that they span two columns and all rows for the week, so only the the first row of the week will have a laboratory or tutorial element, empty or otherwise. If this laboratory or tutorial element has content (i.e., isn't empty), then we need to skip over these cells for the remaining rows of the week. If this element is empty (or there's no element at all) then we output a two-column empty cell for all rows.
					
					Note that we use "../row[1]" rather than one of the "preceding" axes, as the latter are effectively indexed in reverse.
				-->
				<xsl:if test="$laboratory-column-1 > $reading-column">
					<xsl:call-template name="tabular-column-separator" />
					<xsl:if test="count( laboratory ) = 0">
						<xsl:choose>
							<xsl:when test="not( ../row[1]/laboratory/node() )">
								<xsl:call-template name="generate-empty-cell">
									<xsl:with-param name="columns">2</xsl:with-param>
									<xsl:with-param name="rows">1</xsl:with-param>
									<xsl:with-param name="column-format">c|</xsl:with-param>
								</xsl:call-template>
							</xsl:when>
							<xsl:otherwise>
								<xsl:call-template name="tabular-column-separator" />
							</xsl:otherwise>
						</xsl:choose>
					</xsl:if>
				</xsl:if>
				<xsl:apply-templates select="laboratory" />
	
				<xsl:if test="$tutorial-column-1 > $laboratory-column-2">
					<xsl:call-template name="tabular-column-separator" />
					<xsl:if test="count( tutorial ) = 0">
						<xsl:choose>
							<xsl:when test="not( ../row[1]/tutorial/node() )">
								<xsl:call-template name="generate-empty-cell">
									<xsl:with-param name="columns">2</xsl:with-param>
									<xsl:with-param name="rows">1</xsl:with-param>
									<xsl:with-param name="column-format">c|</xsl:with-param>
								</xsl:call-template>
							</xsl:when>
							<xsl:otherwise>
								<xsl:call-template name="tabular-column-separator" />
							</xsl:otherwise>
						</xsl:choose>
					</xsl:if>
				</xsl:if>
				<xsl:apply-templates select="tutorial" />
				
				<!--
					Assessments always span a single column and span all rows for a week, so no additional special handling is required for missing elements.
				-->
				<xsl:if test="$assessment-column > $tutorial-column-2">
					<xsl:call-template name="tabular-column-separator" />
				</xsl:if>
				<xsl:apply-templates select="assessment" />
	
				<!-- Note assumption that a lecture is always a single row. -->
				<xsl:text>	\\</xsl:text>
				<!--
					Put a border beneath a lecture. The horrible XPath logic is as follows:
					
						* either
							* there is at least one week following the current week; and
							* the immediately following week /isn't/ a holiday;
						* or
							* this is not the last row of the current week; and
								* the current week is the last week in the calendar; or
								* the immediately following week /is/ a holiday.
					
					(YOUR HEAD A SPLODE ... AGAIN)
				-->
				<xsl:if test="	(	( count( ../following-sibling::week ) > 0 )
								and	( not( ../following-sibling::week[1]/@holiday ) )
								)
								or	(	( position() &lt; last() )
									and	(	( count( ../following-sibling::week ) = 0 )
										or	( ../following-sibling::week[1]/@holiday )
										)
									)">
					<xsl:text> \cline{</xsl:text>
					<xsl:value-of select="$lecture-column-1" />
					<xsl:text>-</xsl:text>
					<xsl:value-of select="$lecture-column-2" />
					<xsl:text>}</xsl:text>
				</xsl:if>
				
				<!--
					Underline laboratory and tutorial cells. Logic is as above, plus a test whether this is the last row of the week. (KER-SPLODE)
				-->
				<xsl:if test="	( position() = ancestor::calendar/@lectures-per-week )
							and	(	(	( count( ../following-sibling::week ) > 0 )
									and	( not( ../following-sibling::week[1]/@holiday ) )
									)
								or	(	( position() &lt; last() )
									and	(	( count( ../following-sibling::week ) = 0 )
										or	( ../following-sibling::week[1]/@holiday )
										)
									)
								)">
					<!-- Week number & date columns. -->
					<xsl:text> \cline{</xsl:text>
					<xsl:value-of select="$week-column" />
					<xsl:text>-</xsl:text>
					<xsl:value-of select="$date-column" />
					<xsl:text>}</xsl:text>
					<!-- Reading column. -->
					<xsl:text> \cline{</xsl:text>
					<xsl:value-of select="$reading-column" />
					<xsl:text>-</xsl:text>
					<xsl:value-of select="$reading-column" />
					<xsl:text>}</xsl:text>
					<!-- Assessment column. -->
					<xsl:text> \cline{</xsl:text>
					<xsl:value-of select="$assessment-column" />
					<xsl:text>-</xsl:text>
					<xsl:value-of select="$assessment-column" />
					<xsl:text>}</xsl:text>
					<!-- Laboratory columns. -->
					<xsl:if test="not( ../row[1]/laboratory/@rows )">
						<xsl:text> \cline{</xsl:text>
						<xsl:value-of select="$laboratory-column-1" />
						<xsl:text>-</xsl:text>
						<xsl:value-of select="$laboratory-column-2" />
						<xsl:text>}</xsl:text>
					</xsl:if>
					<!-- Tutorial columns. -->
					<xsl:if test="not( ../row[1]/tutorial/@rows )">
						<xsl:text> \cline{</xsl:text>
						<xsl:value-of select="$tutorial-column-1" />
						<xsl:text>-</xsl:text>
						<xsl:value-of select="$tutorial-column-2" />
						<xsl:text>}</xsl:text>
					</xsl:if>
				</xsl:if>
				
				<xsl:call-template name="newline-internal" />
			</xsl:for-each>
		</common>
		<common formats="/html/xhtml/">
			<!-- Not sure why, but this doesn't work if you just access @current directly in the if below. -->
<!-- 
			<xsl:variable name="current" select="@current" />
 -->
			
			<xsl:for-each select="row">
				<tr>
					<!-- Output the week number and dates columns. -->
					<xsl:if test="position() = 1">
						<!-- Week number, first row only. -->
						<xsl:call-template name="generate-number-cell">
							<xsl:with-param name="colour" select="if ( ../@current = 'yes' ) then 'red' else 'white'" />
							<xsl:with-param name="number" select="1 + count( preceding::week[not( @holiday )] )" />
						</xsl:call-template>

						<!-- Date range, first row only. -->
						<xsl:call-template name="generate-content-cell">
							<xsl:with-param name="nodes" select="../dates" />
						</xsl:call-template>
					</xsl:if>
	
					<!--
						Apply each of the sub-templates in the correct order.
					-->
					<xsl:apply-templates select="section" />
					<xsl:apply-templates select="lecture" />
					<xsl:apply-templates select="reading" />
					<xsl:apply-templates select="laboratory" />
					<xsl:apply-templates select="tutorial" />
					<xsl:apply-templates select="assessment" />
				</tr>
			</xsl:for-each>
		</common>
	</template>
	
	
	<!--
		Weeks that are holidays have no week number or date range, just a text description.
	-->
	<template match="week[@holiday]">
		<common>
			<xsl:param name="num-columns" />
		</common>
		<common formats="/latex/xelatex/">
			<xsl:text>\hline\hline</xsl:text>
			<xsl:call-template name="newline-internal" />
			
			<xsl:call-template name="generate-content-cell">
				<xsl:with-param name="columns" select="xs:integer( $num-columns )" />
				<xsl:with-param name="rows">2</xsl:with-param>
				<xsl:with-param name="column-format">|c|</xsl:with-param>
				<xsl:with-param name="style">\LARGE\sffamily\bfseries</xsl:with-param>
			</xsl:call-template>
			<xsl:text>	\\</xsl:text>
			<xsl:call-template name="newline-internal" />
			
			<xsl:call-template name="generate-empty-cell">
				<xsl:with-param name="columns" select="xs:integer( $num-columns )" />
				<xsl:with-param name="rows">1</xsl:with-param>
				<xsl:with-param name="column-format">|c|</xsl:with-param>
			</xsl:call-template>
			<xsl:text>	\\</xsl:text>
			<xsl:call-template name="newline-internal" />
			
			<xsl:text>\hline\hline</xsl:text>
			<xsl:call-template name="newline-internal" />
		</common>
		<common formats="/html/xhtml/">
			<tr>
				<xsl:call-template name="generate-content-cell">
					<xsl:with-param name="columns" select="xs:integer( $num-columns )" />
					<xsl:with-param name="rows">1</xsl:with-param>
					<xsl:with-param name="colour">blue-ou large</xsl:with-param>
					<xsl:with-param name="style">border: 2px solid #777777; font-style: italic; font-weight: bold;</xsl:with-param>
				</xsl:call-template>
			</tr>
		</common>
	</template>


	<!--
		Output a calendar entry for a lecture. Note that lectures are always assumed to span a single row. Lectures are automatically numbered.
	-->
	<template match="lecture[node() and not( @holiday )]">
		<common formats="/latex/xelatex/">
			<xsl:call-template name="generate-number-cell">
				<xsl:with-param name="rows">1</xsl:with-param>
				<xsl:with-param name="number" select="1 + count( preceding::lecture[node() and not( @holiday )] )" />
			</xsl:call-template>

			<xsl:call-template name="tabular-column-separator" />
			
			<xsl:call-template name="generate-content-cell">
				<xsl:with-param name="rows">1</xsl:with-param>
				<xsl:with-param name="align">left</xsl:with-param>
			</xsl:call-template>
		</common>
		<common formats="/html/xhtml/">
			<xsl:call-template name="generate-number-cell">
				<xsl:with-param name="rows">1</xsl:with-param>
				<xsl:with-param name="colour">ltgrey</xsl:with-param>
				<xsl:with-param name="number" select="1 + count( preceding::week[not( @holiday )] )" />
			</xsl:call-template>

			<xsl:call-template name="generate-content-cell">
				<xsl:with-param name="rows">1</xsl:with-param>
				<xsl:with-param name="colour">ltgrey</xsl:with-param>
				<xsl:with-param name="align">left</xsl:with-param>
			</xsl:call-template>
		</common>
	</template>


	<!--
		Output a calendar entry for a lecture that occurs on a holiday. These aren't numbered.
	-->
	<template match="lecture[@holiday]">
		<common formats="/latex/xelatex/">
			<xsl:call-template name="generate-content-cell">
				<xsl:with-param name="columns">2</xsl:with-param>
				<xsl:with-param name="rows">1</xsl:with-param>
				<xsl:with-param name="style">\sffamily\bfseries</xsl:with-param>
			</xsl:call-template>
		</common>
		<common formats="/html/xhtml/">
			<xsl:call-template name="generate-content-cell">
				<xsl:with-param name="columns">2</xsl:with-param>
				<xsl:with-param name="rows">1</xsl:with-param>
				<xsl:with-param name="colour">blue-ou</xsl:with-param>
				<xsl:with-param name="style">font-style: italic; font-weight: bold;</xsl:with-param>
			</xsl:call-template>
		</common>
	</template>


	<!--
		Output an empty lecture cell.
	-->
	<template match="lecture[not( node() )]">
		<common>
			<xsl:call-template name="generate-empty-cell">
				<xsl:with-param name="columns">2</xsl:with-param>
			</xsl:call-template>
		</common>
	</template>


	<!--
		Output a calendar entry for a laboratory. Laboratories are automatically numbered.
	-->
	<template match="laboratory[node()]">
		<common formats="/latex/xelatex/">
			<xsl:call-template name="generate-number-cell">
				<xsl:with-param name="number" select="1 + count( preceding::laboratory[node()] )" />
			</xsl:call-template>
			
			<xsl:call-template name="tabular-column-separator" />
	
			<xsl:call-template name="generate-content-cell" />
		</common>
		<common formats="/html/xhtml/">
			<xsl:call-template name="generate-number-cell">
				<xsl:with-param name="colour">ltblue</xsl:with-param>
				<xsl:with-param name="number" select="1 + count( preceding::laboratory[node()] )" />
			</xsl:call-template>

			<xsl:call-template name="generate-content-cell">
				<xsl:with-param name="colour">ltblue</xsl:with-param>
			</xsl:call-template>
		</common>
	</template>


	<!--
		Output a calendar entry for a tutorial. Tutorials are automatically numbered.
	-->
	<template match="tutorial[node()]">
		<common formats="/latex/xelatex/">
			<xsl:call-template name="generate-number-cell">
				<xsl:with-param name="number" select="1 + count( preceding::tutorial[node()] )" />
			</xsl:call-template>
			
			<xsl:call-template name="tabular-column-separator" />
	
			<xsl:call-template name="generate-content-cell" />
		</common>
		<common formats="/html/xhtml/">
			<xsl:call-template name="generate-number-cell">
				<xsl:with-param name="colour">medgreen</xsl:with-param>
				<xsl:with-param name="number" select="1 + count( preceding::tutorial[node()] )" />
			</xsl:call-template>

			<xsl:call-template name="generate-content-cell">
				<xsl:with-param name="colour">medgreen</xsl:with-param>
			</xsl:call-template>
		</common>
	</template>


	<!--
		Output an empty laboratory/tutorial cell.
	-->
	<template match="laboratory[not( node() )]|tutorial[not( node() )]">
		<common>
			<xsl:call-template name="generate-empty-cell">
				<xsl:with-param name="columns">2</xsl:with-param>
				<xsl:with-param name="rows" select="if ( @rows ) then @rows else ancestor::calendar/@lectures-per-week" />
			</xsl:call-template>
		</common>
	</template>


	<!--
		Output a calendar entry for a section, reading or asssement. All use the same formatting in (Xe)LaTeX, but assessments are displayed using a different colour in (X)HTML, hence the separate template for those.
	-->
	<template match="calendar//section[node()]|reading[node()]">
		<common formats="/latex/xelatex/">
			<xsl:call-template name="generate-content-cell">
				<xsl:with-param name="rows" select="if ( @rows ) then @rows else ancestor::calendar/@lectures-per-week" />
			</xsl:call-template>
		</common>
		<common formats="/html/xhtml/">
			<xsl:call-template name="generate-content-cell">
				<xsl:with-param name="rows" select="if ( @rows ) then @rows else ancestor::calendar/@lectures-per-week" />
			</xsl:call-template>
		</common>
	</template>
	
	<template match="assessment[node()]">
		<common formats="/latex/xelatex/">
			<xsl:call-template name="generate-content-cell">
				<xsl:with-param name="rows" select="if ( @rows ) then @rows else ancestor::calendar/@lectures-per-week" />
			</xsl:call-template>
		</common>
		<common formats="/html/xhtml/">
			<xsl:call-template name="generate-content-cell">
				<xsl:with-param name="rows" select="if ( @rows ) then @rows else ancestor::calendar/@lectures-per-week" />
				<xsl:with-param name="colour">peach</xsl:with-param>
			</xsl:call-template>
		</common>
	</template>


	<!--
		Output an empty section, reading or assessment cell.
	-->
	<template match="calendar//section[not( node() )]|reading[not( node() )]|assessment[not( node() )]">
		<common>
			<xsl:call-template name="generate-empty-cell">
				<xsl:with-param name="rows" select="if ( @rows ) then @rows else ancestor::calendar/@lectures-per-week" />
			</xsl:call-template>
		</common>
	</template>


	<!--
		Generate a cell containing a single, dynamically-generated number, centered within the cell. We can't just use generate-content-cell for these cells, because the number doesn't exist until the point where the template is called. Generate-content-cell does have the @nodes attribute, but that expects a node list, not a scalar value. The (X)eLaTeX version of this template also uses \makebox rather than a tabular to reduce the amount of horizontal space generated (we can do this because numbers don't include line breaks), and omits the @style and @align attributes, as they're pretty much irrelevant (for now, at least).
		
		TODO: Is it possible to refactor both templates so that there's less code duplication?
		
		@columns: The number of columns the cell spans. [default 1]
		
		@rows: The number of rows the cell spans. [default @lectures-per-week]
		
		@number: The number to be output in the cell. [default 0]
		
		(XeLaTeX) only
		@column-format: A LaTeX tabular column specifiation for the cell. [default c|]
		
		(X)HTML only
		@colour: The display colour of the cell, which maps to a predefined CSS class. [default white]
	-->
	<template name="generate-number-cell">
		<common>
			<xsl:param name="columns" as="xs:integer">1</xsl:param>
			<xsl:param name="rows" as="xs:integer" select="ancestor::calendar/@lectures-per-week" />
			<!-- No data type for this one, as there isn't a single top-level numeric primitive type in XML Schema :(. -->
			<xsl:param name="number">0</xsl:param>
		</common>
		<common formats="/latex/xelatex/">
			<!--
				
			-->
			<xsl:param name="column-format" as="xs:string">c|</xsl:param>

			<xsl:if test="$columns > 1">
				<xsl:text>\multicolumn{</xsl:text>
				<xsl:value-of select="$columns" />
				<xsl:text>}{</xsl:text>
				<xsl:value-of select="$column-format" />
				<xsl:text>}{</xsl:text>
			</xsl:if>
			<xsl:if test="$rows > 1">
				<xsl:text>\multirow{</xsl:text>
				<xsl:value-of select="$rows" />
				<xsl:text>}{*}{</xsl:text>
			</xsl:if>
			
			<xsl:text>\makebox[\numberwidth][c]{</xsl:text>
			<xsl:value-of select="$number" />
			<xsl:text>}</xsl:text>

			<xsl:if test="$rows > 1">
				<xsl:text>}</xsl:text>
			</xsl:if>
			<xsl:if test="$columns > 1">
				<xsl:text>}</xsl:text>
			</xsl:if>
		</common>
		<common formats="/html/xhtml/">
			<xsl:param name="colour" as="xs:string">white</xsl:param>
			
			<!-- Alignment is slightly odd in that it's done via a CSS class rather than explicitly. -->
			<td class="{$colour} center calendar" colspan="{$columns}" rowspan="{$rows}">
				<xsl:value-of select="$number" />
			</td>
		</common>
	</template>
	
	
	<!--
		Generate a cell containing general content of some sort. In (Xe)LaTeX, the cell content is embedded within a tabular environment, as it may contain embedded line breaks. (A \shortstack would also work, but the line spacing doesn't look as nice as it does with a tabular.)
		
		@columns: The number of columns the cell spans. [default 1]
		
		@rows: The number of rows the cell spans. [default @lectures-per-week]
		
		@align: The alignment of the content within the cell. [default center]
		
		@style: Styling information for the cell.
				
				- For (Xe)LaTeX, a string containing styling /declarations/ (e.g., \bfseries, \sffamily). You can't use the macro forms (e.g., \textbf, \textsf), because of the embedded tabular environment.
				
				- For (X)HTML, a string containing CSS styling information. Note: use @align for cell alignment and @colour for cell colouring.
		
		@nodes: An optional list of XML document nodes that are processed to generate the cell content. [default: all sub-nodes of the current context node]
		
		(XeLaTeX) only
		@column-format: A LaTeX tabular column specifiation for the cell. [default c|]
		
		(X)HTML only
		@colour: The display colour of the cell, which maps to a predefined CSS class. [default white]
	-->
	<template name="generate-content-cell">
		<common>
			<xsl:param name="columns" as="xs:integer">1</xsl:param>
			<xsl:param name="rows" as="xs:integer" select="ancestor::calendar/@lectures-per-week" />
			<xsl:param name="align" as="xs:string">center</xsl:param>
			<!--
				Hmm, it appears that if you specify a parameter as xs:string, you can't supply an empty default value. Even a blank doesn't work! The only way to have an empty default is to not specify a data type. That's pretty damn stupid :(.
			-->
			<xsl:param name="style" />
			<xsl:param name="nodes" select="node()" />
		</common>
		<common formats="/latex/xelatex/">
			<xsl:param name="column-format" as="xs:string">c|</xsl:param>

			<xsl:if test="$columns > 1">
				<xsl:text>\multicolumn{</xsl:text>
				<xsl:value-of select="$columns" />
				<xsl:text>}{</xsl:text>
				<xsl:value-of select="$column-format" />
				<xsl:text>}{</xsl:text>
			</xsl:if>
			<xsl:if test="$rows > 1">
				<xsl:text>\multirow{</xsl:text>
				<xsl:value-of select="$rows" />
				<xsl:text>}{*}{</xsl:text>
			</xsl:if>
			
 			<!--
 				We need to apply any styling outside the tabular so that it applies to all lines of the tabular (style declarations only last until the next & or \\ in a tabular). Wrap everything up in a group to ensure that it reverts back to the original styling at the end (that should happen anyway, but I'm paranoid).
 			-->
			<xsl:text>{</xsl:text>
			<xsl:value-of select="$style" />

			<xsl:text>\begin{tabular}{</xsl:text>
			<xsl:value-of select="substring( $align, 1, 1 )" />
			<xsl:text>}</xsl:text>
			
			<xsl:apply-templates select="$nodes" />
			
			<xsl:text>\end{tabular}</xsl:text>
			
			<xsl:text>}</xsl:text>

			<xsl:if test="$rows > 1">
				<xsl:text>}</xsl:text>
			</xsl:if>
			<xsl:if test="$columns > 1">
				<xsl:text>}</xsl:text>
			</xsl:if>
		</common>
		<common formats="/html/xhtml/">
			<xsl:param name="colour" as="xs:string">white</xsl:param>
			
			<!-- Alignment is slightly odd in that it's done via a CSS class rather than explicitly. -->
			<td class="{$colour} {$align} calendar" style="{$style}" colspan="{$columns}" rowspan="{$rows}">
				<xsl:apply-templates select="$nodes" />
			</td>
		</common>
	</template>
	
	
	<!--
		Generate an empty cell.
		
		@columns: The number of columns the cell spans. [default 1]
		
		@rows: The number of rows the cell spans. [default @lectures-per-week]
		
		(XeLaTeX) only
		@column-format: A LaTeX tabular column specifiation for the cell. [default c|]
		
		(X)HTML only
		@colour: The display colour of the cell, which maps to a predefined CSS class. [default white]
	-->
	<template name="generate-empty-cell">
		<common>
			<xsl:param name="columns" as="xs:integer">1</xsl:param>
			<xsl:param name="rows" as="xs:integer" select="ancestor::calendar/@lectures-per-week" />
		</common>
		<common formats="/latex/xelatex/">
			<xsl:param name="column-format" as="xs:string">c|</xsl:param>

			<xsl:if test="$columns > 1">
				<xsl:text>\multicolumn{</xsl:text>
				<xsl:value-of select="$columns" />
				<xsl:text>}{</xsl:text>
				<xsl:value-of select="$column-format" />
				<xsl:text>}{</xsl:text>
			</xsl:if>
			<xsl:if test="$rows > 1">
				<xsl:text>\multirow{</xsl:text>
				<xsl:value-of select="$rows" />
				<xsl:text>}{*}{</xsl:text>
			</xsl:if>

			<xsl:call-template name="non-breaking-space" />

			<xsl:if test="$rows > 1">
				<xsl:text>}</xsl:text>
			</xsl:if>
			<xsl:if test="$columns > 1">
				<xsl:text>}</xsl:text>
			</xsl:if>
		</common>
		<common formats="/html/xhtml/">
			<xsl:param name="colour" as="xs:string">medgrey</xsl:param>

			<td class="{$colour} center calendar" colspan="{$columns}" rowspan="{$rows}">
				<xsl:call-template name="non-breaking-space" />
			</td>
		</common>
	</template>
	
	
</stylesheet>