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]
		
		@number-lectures
		@number-tutorials
		@number-laboratories: Whether to generate numbers for lectures, tutorials and labs, respectively. These can only be specified globally because you normally want to switch this off for the entire calendar. They're not actually used in this template; rather they're just attached to the root element for ease of reference.
		    "yes" [default]
		    "no"
	-->
	<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.
				
				Note that we need to cover both the case of a calendar for a single period, which will have the body element as an immediate child of calendar, versus a calendar for multiple periods, which will have the body elements as immmediate children of calendar/period.
				
				Note also the implied assumption that a calendar with multiple periods will have the same column structure for all periods. This may or may not be a valid assumption in future!
			-->
			<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 | period/body//section) gt 0)
			    then $date-column + 1
			    else $date-column" />
			<xsl:variable name="lecture-column-1" select="
				if (count(body//lecture | period/body//lecture) gt 0)
			    then $section-column + 1
			    else $section-column" />
			<xsl:variable name="lecture-column-2" select="
				if (count(body//lecture | period/body//lecture) gt 0)
			    then $lecture-column-1 + 1
			    else $lecture-column-1" />
			<xsl:variable name="reading-column" select="
				if (count(body//reading | period/body//reading) gt 0)
			    then $lecture-column-2 + 1
			    else $lecture-column-2" />
			<xsl:variable name="laboratory-column-1" select="
				if (count(body//laboratory | period/body//laboratory) gt 0)
			    then $reading-column + 1
			    else $reading-column" />
			<xsl:variable name="laboratory-column-2" select="
				if (count(body//laboratory | period/body//laboratory) gt 0)
			    then $laboratory-column-1 + 1
			    else $laboratory-column-1" />
			<xsl:variable name="tutorial-column-1" select="
				if (count(body//tutorial | period/body//tutorial) gt 0)
			    then $laboratory-column-2 + 1
			    else $laboratory-column-2" />
			<xsl:variable name="tutorial-column-2" select="
				if (count(body//tutorial | period/body//tutorial) gt 0)
			    then $tutorial-column-1 + 1
			    else $tutorial-column-1" />
			<xsl:variable name="assessment-column" select="
				if (count(body//assessment | period/body//assessment) gt 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" />
			
			<!--
				The processing of the actual calendar content is split off into a separate callable template (generate-calendar-content) so that we can handle single-period and multiple-period calendars with the same code. We use tunnelled parameters to avoid having to explicitly pass the damn things all over the place! (Except for the first time, of course.)
			-->
			<xsl:choose>
				<!--
					If the calendar has multiple periods, then each period will be enclosed inside its own period element. We call the template once for each of these, generating a new tabular each time (i.e., each period effectively gets output as a calendar in its own right). The teaching period for each part is expanded from its @code attribute.
				-->
				<xsl:when test="period">
					<xsl:for-each select="period">
						<xsl:call-template name="generate-calendar-content">
							<xsl:with-param name="week-column" select="xs:integer( $week-column )" tunnel="yes" />
							<xsl:with-param name="date-column" select="xs:integer( $date-column )" tunnel="yes" />
							<xsl:with-param name="section-column" select="xs:integer( $section-column )" tunnel="yes" />
							<xsl:with-param name="lecture-column-1" select="xs:integer( $lecture-column-1 )" tunnel="yes" />
							<xsl:with-param name="lecture-column-2" select="xs:integer( $lecture-column-2 )" tunnel="yes" />
							<xsl:with-param name="reading-column" select="xs:integer( $reading-column )" tunnel="yes" />
							<xsl:with-param name="laboratory-column-1" select="xs:integer( $laboratory-column-1 )" tunnel="yes" />
							<xsl:with-param name="laboratory-column-2" select="xs:integer( $laboratory-column-2 )" tunnel="yes" />
							<xsl:with-param name="tutorial-column-1" select="xs:integer( $tutorial-column-1 )" tunnel="yes" />
							<xsl:with-param name="tutorial-column-2" select="xs:integer( $tutorial-column-2 )" tunnel="yes" />
							<xsl:with-param name="assessment-column" select="xs:integer( $assessment-column )" tunnel="yes" />
							<xsl:with-param name="num-columns" select="xs:integer( $num-columns )" tunnel="yes" />
							<xsl:with-param name="teaching-period" select="infosci:expand-period-code( @code )" tunnel="yes" />
						</xsl:call-template>
					</xsl:for-each>
				</xsl:when>
				<!--
					If the calendar has only one period, there will be no period elements, and we thus need to call the template just once. The teaching period for the calendar is specified by the global stylesheet variable $period-string (which is already expanded).
				-->
				<xsl:otherwise>
					<xsl:call-template name="generate-calendar-content">
						<xsl:with-param name="week-column" select="xs:integer( $week-column )" tunnel="yes" />
						<xsl:with-param name="date-column" select="xs:integer( $date-column )" tunnel="yes" />
						<xsl:with-param name="section-column" select="xs:integer( $section-column )" tunnel="yes" />
						<xsl:with-param name="lecture-column-1" select="xs:integer( $lecture-column-1 )" tunnel="yes" />
						<xsl:with-param name="lecture-column-2" select="xs:integer( $lecture-column-2 )" tunnel="yes" />
						<xsl:with-param name="reading-column" select="xs:integer( $reading-column )" tunnel="yes" />
						<xsl:with-param name="laboratory-column-1" select="xs:integer( $laboratory-column-1 )" tunnel="yes" />
						<xsl:with-param name="laboratory-column-2" select="xs:integer( $laboratory-column-2 )" tunnel="yes" />
						<xsl:with-param name="tutorial-column-1" select="xs:integer( $tutorial-column-1 )" tunnel="yes" />
						<xsl:with-param name="tutorial-column-2" select="xs:integer( $tutorial-column-2 )" tunnel="yes" />
						<xsl:with-param name="assessment-column" select="xs:integer( $assessment-column )" tunnel="yes" />
						<xsl:with-param name="num-columns" select="xs:integer( $num-columns )" tunnel="yes" />
						<xsl:with-param name="teaching-period" select="$period-string" tunnel="yes" />
					</xsl:call-template>
				</xsl:otherwise>
			</xsl:choose>
		</common>
		<common formats="/html/xhtml/">
			<!--
				This is identical to the calendar/period handling in the LaTeX formats above. I'd prefer to define this code only once, but can't because of the way the common element works when no formats are specified, i.e., ALL non-format-specific common code appears at the start of the template.
				
				Incidentally, it sounds nice for multiple-period calendars to generate a single HTML table covering all periods rather than splitting it into separate tables. However, this becomes a bit fraught when you start to consider what to do with (possibly different) headers and footers for each period, and how to handle the break between periods. You could probably do something with lots of conditional processing, but it would be pretty hacky. It's much easier to just output a separate self-contained table for each period.
			-->
			<xsl:choose>
				<xsl:when test="period">
					<xsl:for-each select="period">
						<xsl:call-template name="generate-calendar-content">
							<xsl:with-param name="week-column" select="xs:integer( $week-column )" tunnel="yes" />
							<xsl:with-param name="date-column" select="xs:integer( $date-column )" tunnel="yes" />
							<xsl:with-param name="section-column" select="xs:integer( $section-column )" tunnel="yes" />
							<xsl:with-param name="lecture-column-1" select="xs:integer( $lecture-column-1 )" tunnel="yes" />
							<xsl:with-param name="lecture-column-2" select="xs:integer( $lecture-column-2 )" tunnel="yes" />
							<xsl:with-param name="reading-column" select="xs:integer( $reading-column )" tunnel="yes" />
							<xsl:with-param name="laboratory-column-1" select="xs:integer( $laboratory-column-1 )" tunnel="yes" />
							<xsl:with-param name="laboratory-column-2" select="xs:integer( $laboratory-column-2 )" tunnel="yes" />
							<xsl:with-param name="tutorial-column-1" select="xs:integer( $tutorial-column-1 )" tunnel="yes" />
							<xsl:with-param name="tutorial-column-2" select="xs:integer( $tutorial-column-2 )" tunnel="yes" />
							<xsl:with-param name="assessment-column" select="xs:integer( $assessment-column )" tunnel="yes" />
							<xsl:with-param name="num-columns" select="xs:integer( $num-columns )" tunnel="yes" />
							<xsl:with-param name="teaching-period" select="infosci:expand-period-code( @code )" tunnel="yes" />
						</xsl:call-template>
					</xsl:for-each>
				</xsl:when>
				<xsl:otherwise>
					<xsl:call-template name="generate-calendar-content">
						<xsl:with-param name="week-column" select="xs:integer( $week-column )" tunnel="yes" />
						<xsl:with-param name="date-column" select="xs:integer( $date-column )" tunnel="yes" />
						<xsl:with-param name="section-column" select="xs:integer( $section-column )" tunnel="yes" />
						<xsl:with-param name="lecture-column-1" select="xs:integer( $lecture-column-1 )" tunnel="yes" />
						<xsl:with-param name="lecture-column-2" select="xs:integer( $lecture-column-2 )" tunnel="yes" />
						<xsl:with-param name="reading-column" select="xs:integer( $reading-column )" tunnel="yes" />
						<xsl:with-param name="laboratory-column-1" select="xs:integer( $laboratory-column-1 )" tunnel="yes" />
						<xsl:with-param name="laboratory-column-2" select="xs:integer( $laboratory-column-2 )" tunnel="yes" />
						<xsl:with-param name="tutorial-column-1" select="xs:integer( $tutorial-column-1 )" tunnel="yes" />
						<xsl:with-param name="tutorial-column-2" select="xs:integer( $tutorial-column-2 )" tunnel="yes" />
						<xsl:with-param name="assessment-column" select="xs:integer( $assessment-column )" tunnel="yes" />
						<xsl:with-param name="num-columns" select="xs:integer( $num-columns )" tunnel="yes" />
						<xsl:with-param name="teaching-period" select="$period-string" tunnel="yes" />
					</xsl:call-template>
				</xsl:otherwise>
			</xsl:choose>
		</common>
	</template>
	
	
	<!--
		Generate the content of a calendar or period, as applicable. An entire LaTeX tabular or HTML table is generated.
		
		Every variable defined in the calendar template above is passed as a parameter, so there's little point in listing them here. The only extra one is $teaching-period.
	-->
	<template name="generate-calendar-content">
		<common>
			<xsl:param name="week-column" tunnel="yes" />
			<xsl:param name="date-column" tunnel="yes" />
			<xsl:param name="section-column" tunnel="yes" />
			<xsl:param name="lecture-column-1" tunnel="yes" />
			<xsl:param name="lecture-column-2" tunnel="yes" />
			<xsl:param name="reading-column" tunnel="yes" />
			<xsl:param name="laboratory-column-1" tunnel="yes" />
			<xsl:param name="laboratory-column-2" tunnel="yes" />
			<xsl:param name="tutorial-column-1" tunnel="yes" />
			<xsl:param name="tutorial-column-2" tunnel="yes" />
			<xsl:param name="assessment-column" tunnel="yes" />
			<xsl:param name="num-columns" tunnel="yes" />
			<xsl:param name="teaching-period" tunnel="yes" />
		</common>
		<common formats="/latex/xelatex/">
		    <!-- Calendar heading boilerplate, i.e., all the \begin{environment}, etc. -->
		    <xsl:call-template name="generate-latex-calendar-preamble" />
		    
			<!-- The calendar itself. -->
			<xsl:apply-templates select="header" />
			<xsl:call-template name="newline-internal" />
			
			<xsl:text>\hline\hline</xsl:text>
			<xsl:call-template name="newline-internal" />
			
			<xsl:apply-templates select="body" />
			<xsl:call-template name="newline-internal" />
		    
			<xsl:text>\hline\hline</xsl:text>
			<xsl:call-template name="newline-internal" />
			
			<xsl:apply-templates select="footer" />
			<xsl:call-template name="newline-internal" />
			
			<!-- Calendar footing boilerplate, i.e., all the \end{environment}, etc. -->
			<xsl:call-template name="generate-latex-calendar-postamble" />
		</common>
		<common formats="/html/xhtml/">
			<!-- Calendar heading. -->
			<h2>
				<xsl:call-template name="PaperCode" />
				<xsl:text> Teaching Calendar, </xsl:text>
				<xsl:value-of select="$teaching-period" />
				<xsl:text>, </xsl:text>
				<xsl:call-template name="PaperYear" />
			</h2>
			
			<!-- The calendar itself. -->
			<div class="sans small">
				<table class="calendar" summary="Teaching 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" />
					</thead>
					<tfoot class="calendar">
						<xsl:apply-templates select="footer" />
					</tfoot>
					<tbody class="calendar">
						<xsl:apply-templates select="body" />
					</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 formats="/latex/xelatex/">
			<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() ne last()">
				<xsl:text>	\hline</xsl:text>
			</xsl:if>
			<xsl:call-template name="newline-internal" />
		</common>
		<common formats="/html/xhtml/">
			<tr>
				<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 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() ne 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:attribute name="colspan">
                    <xsl:value-of select="if ( @columns ) then @columns else 1" />
                </xsl:attribute>
				<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 note spans. Defaults to the overall number of columns passed in.
	-->
	<template match="note">
		<common>
			<xsl:param name="num-columns" tunnel="yes" />
		</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 @lectures-per-week). The week number and date range are only generated for the first of these rows.
	-->
	<template match="week">
		<common>
			<xsl:param name="num-columns" tunnel="yes" />
		</common>
		<common formats="/latex/xelatex/">
			<xsl:param name="week-column" tunnel="yes" />
			<xsl:param name="date-column" tunnel="yes" />
			<xsl:param name="section-column" tunnel="yes" />
			<xsl:param name="lecture-column-1" tunnel="yes" />
			<xsl:param name="lecture-column-2" tunnel="yes" />
			<xsl:param name="reading-column" tunnel="yes" />
			<xsl:param name="laboratory-column-1" tunnel="yes" />
			<xsl:param name="laboratory-column-2" tunnel="yes" />
			<xsl:param name="tutorial-column-1" tunnel="yes" />
			<xsl:param name="tutorial-column-2" tunnel="yes" />
			<xsl:param name="assessment-column" tunnel="yes" />
		
			<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) gt 0)
										and	(not(../preceding-sibling::week[1]/@holiday))
										)
									or	(	(position() gt 1)
										and	(	(count( ../preceding-sibling::week ) eq 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() eq 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 gt $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 gt $section-column">
					<xsl:call-template name="tabular-column-separator" />
					<xsl:if test="count(lecture) eq 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 gt $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 gt $reading-column">
					<xsl:call-template name="tabular-column-separator" />
					<xsl:if test="count(laboratory) eq 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 gt $laboratory-column-2">
					<xsl:call-template name="tabular-column-separator" />
					<xsl:if test="count(tutorial) eq 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 gt $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) gt 0)
								and	(not(../following-sibling::week[1]/@holiday))
								)
								or	(	(position() lt last())
									and	(	(count(../following-sibling::week) eq 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) gt 0)
									and	(not(../following-sibling::week[1]/@holiday))
									)
								or	(	(position() lt last())
									and	(	(count(../following-sibling::week) eq 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/">
			<xsl:for-each select="row">
				<tr>
					<!-- Output the week number and dates columns. -->
					<xsl:if test="position() eq 1">
						<!-- Week number, first row only. -->
						<xsl:call-template name="generate-number-cell">
							<xsl:with-param name="colour" select="if ( ../@current = ('yes', 'y', 'true', 't', '1') ) 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" tunnel="yes" />
		</common>
		<common formats="/latex/xelatex/">
			<!--
			    Output a double \hline, *except* when we're inside a latex-calendar-break element, because the double \hline has already been done by the latex-calendar-break template.
			-->
			<xsl:if test="not( parent::latex-calendar-break )">
                <xsl:text>\hline\hline</xsl:text>
                <xsl:call-template name="newline-internal" />
            </xsl:if>
			
			<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-weight: normal;</xsl:with-param>
				</xsl:call-template>
			</tr>
		</common>
	</template>


	<!--
		Output a calendar entry for a lecture. Lectures are assumed to span a single row unless otherwise specified. Lectures are automatically numbered unless otherwise specified. Note that the lecture occupies two cells in the generated table regardless of whether it's numbered.
		
		@rows: The number of rows the lecture spans [default 1].
		
		@number: Whether or not to generate a number for this lecture. The value of this attribute will override any value of parent::calendar/@number-lectures (so you can have all lectures numbered apart from certain ones, or vice versa). If unspecified, it defaults to the value of parent::calendar/@number-lectures, or failing that, "yes".
		    "yes"   [default if parent::calendar/@number-lectures not specified]
		    "no"
	-->
	<template match="lecture[node() and not( @holiday )]">
	    <common>
	        <xsl:variable name="numbered" select="
	            if ( @number )                                  then @number
	            else if ( ancestor::calendar/@number-lectures ) then ancestor::calendar/@number-lectures
	            else                                            'yes'" />
	    </common>
		<common formats="/latex/xelatex/">
		    <xsl:if test="$numbered = ('yes', 'y', 'true', 't', '1')">
                <xsl:call-template name="generate-number-cell">
                    <xsl:with-param name="rows" select="if ( @rows ) then @rows else 1" />
                    <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:if>
			
			<xsl:call-template name="generate-content-cell">
				<xsl:with-param name="columns" select="if ( $numbered = ('yes', 'y', 'true', 't', '1') ) then 1 else 2" />
                <xsl:with-param name="rows" select="if ( @rows ) then @rows else 1" />
				<xsl:with-param name="align">left</xsl:with-param>
			    <xsl:with-param name="column-format" select="if ( $numbered = ('no', 'n', 'false', 'f', '0') ) then 'l|' else 'c|'" />
			</xsl:call-template>
		</common>
		<common formats="/html/xhtml/">
		    <xsl:if test="$numbered = ('yes', 'y', 'true', 't', '1')">
                <xsl:call-template name="generate-number-cell">
                    <xsl:with-param name="colour">ltgrey</xsl:with-param>
                    <xsl:with-param name="rows" select="if ( @rows ) then @rows else 1" />
                    <xsl:with-param name="number" select="1 + count( preceding::lecture[node() and not( @holiday )] )" />
                </xsl:call-template>
            </xsl:if>

			<xsl:call-template name="generate-content-cell">
				<xsl:with-param name="colour">ltgrey</xsl:with-param>
				<xsl:with-param name="columns" select="if ( $numbered = ('yes', 'y', 'true', 't', '1') ) then 1 else 2" />
                <xsl:with-param name="rows" select="if ( @rows ) then @rows else 1" />
				<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 regardless.
	-->
	<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" select="if ( @rows ) then @rows else 1" />
				<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="colour">blue-ou</xsl:with-param>
				<xsl:with-param name="columns">2</xsl:with-param>
                <xsl:with-param name="rows" select="if ( @rows ) then @rows else 1" />
				<xsl:with-param name="style">font-style: italic; font-weight: normal;</xsl:with-param>
			</xsl:call-template>
		</common>
	</template>


	<!--
		Output an empty lecture cell. These aren't numbered regardless.
	-->
	<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 unless otherwise specified. Note that the laboratory occupies two cells in the generated table regardless of whether it's numbered.
		
		@rows: The number of rows the laboratory spans [default 2].
		
		@number: Whether or not to generate a number for this laboratory. The value of this attribute will override any value of parent::calendar/@number-laboratories (so you can have all laboratories numbered apart from certain ones, or vice versa). If unspecified, it defaults to the value of parent::calendar/@number-laboratories, or failing that, "yes".
		    "yes"   [default if parent::calendar/@number-laboratories not specified]
		    "no"
	-->
	<template match="laboratory[node()]">
	    <common>
	        <xsl:variable name="numbered" select="
	            if ( @number )                                      then @number
	            else if ( ancestor::calendar/@number-laboratories ) then ancestor::calendar/@number-laboratories
	            else                                                'yes'" />
	    </common>
		<common formats="/latex/xelatex/">
		    <xsl:if test="$numbered = ('yes', 'y', 'true', 't', '1')">
                <xsl:call-template name="generate-number-cell">
                    <xsl:with-param name="rows" select="if ( @rows ) then @rows else ancestor::calendar/@lectures-per-week" />
                    <xsl:with-param name="number" select="1 + count( preceding::laboratory[node()] )" />
                </xsl:call-template>
                
                <xsl:call-template name="tabular-column-separator" />
            </xsl:if>
	
			<xsl:call-template name="generate-content-cell">
				<xsl:with-param name="columns" select="if ( $numbered = ('yes', 'y', 'true', 't', '1') ) then 1 else 2" />
                <xsl:with-param name="rows" select="if ( @rows ) then @rows else ancestor::calendar/@lectures-per-week" />
				<xsl:with-param name="align">left</xsl:with-param>
			    <xsl:with-param name="column-format" select="if ( $numbered = ('no', 'n', 'false', 'f', '0') ) then 'l|' else 'c|'" />
			</xsl:call-template>
		</common>
		<common formats="/html/xhtml/">
		    <xsl:if test="$numbered = ('yes', 'y', 'true', 't', '1')">
                <xsl:call-template name="generate-number-cell">
                    <xsl:with-param name="colour">ltblue</xsl:with-param>
                    <xsl:with-param name="rows" select="if ( @rows ) then @rows else ancestor::calendar/@lectures-per-week" />
                    <xsl:with-param name="number" select="1 + count( preceding::laboratory[node()] )" />
                </xsl:call-template>
            </xsl:if>

			<xsl:call-template name="generate-content-cell">
				<xsl:with-param name="colour">ltblue</xsl:with-param>
				<xsl:with-param name="columns" select="if ( $numbered = ('yes', 'y', 'true', 't', '1') ) then 1 else 2" />
                <xsl:with-param name="rows" select="if ( @rows ) then @rows else ancestor::calendar/@lectures-per-week" />
				<xsl:with-param name="align">left</xsl:with-param>
			</xsl:call-template>
		</common>
	</template>


	<!--
		Output a calendar entry for a tutorial. Tutorials are automatically numbered unless otherwise specified. Note that the tutorial occupies two cells in the generated table regardless of whether it's numbered.
		
		@rows: The number of rows the tutorial spans [default 2].
		
		@number: Whether or not to generate a number for this tutorial. The value of this attribute will override any value of parent::calendar/@number-tutorials (so you can have all tutorials numbered apart from certain ones, or vice versa). If unspecified, it defaults to the value of parent::calendar/@number-tutorials, or failing that, "yes".
		    "yes"   [default if parent::calendar/@number-tutorials not specified]
		    "no"
	-->
	<template match="tutorial[node()]">
	    <common>
	        <xsl:variable name="numbered" select="
	            if ( @number )                                   then @number
	            else if ( ancestor::calendar/@number-tutorials ) then ancestor::calendar/@number-tutorials
	            else                                             'yes'" />
	    </common>
		<common formats="/latex/xelatex/">
		    <xsl:if test="$numbered = ('yes', 'y', 'true', 't', '1')">
                <xsl:call-template name="generate-number-cell">
                    <xsl:with-param name="rows" select="if ( @rows ) then @rows else ancestor::calendar/@lectures-per-week" />
                    <xsl:with-param name="number" select="1 + count( preceding::tutorial[node()] )" />
                </xsl:call-template>
                
                <xsl:call-template name="tabular-column-separator" />
            </xsl:if>
	
			<xsl:call-template name="generate-content-cell">
				<xsl:with-param name="columns" select="if ( $numbered = ('yes', 'y', 'true', 't', '1') ) then 1 else 2" />
                <xsl:with-param name="rows" select="if ( @rows ) then @rows else ancestor::calendar/@lectures-per-week" />
				<xsl:with-param name="align">left</xsl:with-param>
			    <xsl:with-param name="column-format" select="if ( $numbered = ('no', 'n', 'false', 'f', '0') ) then 'l|' else 'c|'" />
			</xsl:call-template>
		</common>
		<common formats="/html/xhtml/">
		    <xsl:if test="$numbered = ('yes', 'y', 'true', 't', '1')">
                <xsl:call-template name="generate-number-cell">
                    <xsl:with-param name="colour">medgreen</xsl:with-param>
                    <xsl:with-param name="rows" select="if ( @rows ) then @rows else ancestor::calendar/@lectures-per-week" />
                    <xsl:with-param name="number" select="1 + count( preceding::tutorial[node()] )" />
                </xsl:call-template>
            </xsl:if>

			<xsl:call-template name="generate-content-cell">
				<xsl:with-param name="colour">medgreen</xsl:with-param>
				<xsl:with-param name="columns" select="if ( $numbered = ('yes', 'y', 'true', 't', '1') ) then 1 else 2" />
                <xsl:with-param name="rows" select="if ( @rows ) then @rows else ancestor::calendar/@lectures-per-week" />
				<xsl:with-param name="align">left</xsl:with-param>
			</xsl:call-template>
		</common>
	</template>


	<!--
		Output an empty laboratory/tutorial cell. These aren't numbered regardless.
	-->
	<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 (Xe)LaTeX 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]
		
		(Xe)LaTeX 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 gt 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 gt 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 gt 1">
				<xsl:text>}</xsl:text>
			</xsl:if>
			<xsl:if test="$columns gt 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]
		
		(Xe)LaTeX 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 gt 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 gt 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 gt 1">
				<xsl:text>}</xsl:text>
			</xsl:if>
			<xsl:if test="$columns gt 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]
		
		(Xe)LaTeX 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 gt 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 gt 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 gt 1">
				<xsl:text>}</xsl:text>
			</xsl:if>
			<xsl:if test="$columns gt 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>
	
	
    <!--
        Generate calendar heading boilerplate, i.e., all the \begin{environment}, title, etc.
    -->
	<template name="generate-latex-calendar-preamble">
	    <common>
            <xsl:param name="date-column" tunnel="yes" />
            <xsl:param name="section-column" tunnel="yes" />
            <xsl:param name="lecture-column-1" tunnel="yes" />
            <xsl:param name="lecture-column-2" tunnel="yes" />
            <xsl:param name="reading-column" tunnel="yes" />
            <xsl:param name="laboratory-column-1" tunnel="yes" />
            <xsl:param name="laboratory-column-2" tunnel="yes" />
            <xsl:param name="tutorial-column-1" tunnel="yes" />
            <xsl:param name="tutorial-column-2" tunnel="yes" />
            <xsl:param name="assessment-column" tunnel="yes" />
            <xsl:param name="num-columns" tunnel="yes" />
			<xsl:param name="teaching-period" tunnel="yes" />
        </common>
		<common formats="/latex/xelatex/">
		    <xsl:if test="@position-vertical = ('center', 'centre', 'bottom')">
                <xsl:text>\mbox{}\vfill</xsl:text>
                <xsl:call-template name="newline-internal" />
            </xsl:if>
            
            <xsl:if test="@position-horizontal">
                <xsl:text>\begin{</xsl:text>
                <xsl:value-of select="
                    if (@position-horizontal = ('left', 'right')) then 'flush{@position-horizontal}'
                    else if (@position-horizontal = ('center', 'centre')) then @position-horizontal
                    else 'flushleft'" />
                <xsl:text>}</xsl:text>
                <xsl:call-template name="newline-internal" />
            </xsl:if>
            
            <!--
                An explicit @height always takes precedence over @fit-height.
            -->
            <xsl:variable name="table-height" select ="
                if (@height) then @height
                else if (@fit-height) then '\textheight'
                     else '!'" />
                
            <!--
                An explicit @width always takes precedence over @fit-width.
            -->
            <xsl:variable name="table-width" select ="
                if (@width) then @width
                else if (@fit-width) then '\textwidth'
                     else '!'" />
            
            <!-- No point resizing if no height or width is specified. -->
            <xsl:if test="($table-width ne '!') or ($table-height ne '!')">
                <xsl:text>\resizebox*{</xsl:text>
                <xsl:value-of select="$table-width" />
                <xsl:text>}{</xsl:text>
                <xsl:value-of select="$table-height" />
                <xsl:text>}{%</xsl:text>
                <xsl:call-template name="newline-internal" />
            </xsl:if>
                            
            <!-- The default is to print sideways. -->
            <xsl:if test="not(@sideways) or (@sideways = ('yes', 'y', 'true', 't', '1'))">
                <xsl:text>\begin{sideways}</xsl:text>
                <xsl:call-template name="newline-internal" />
            </xsl:if>
            
            <xsl:text>\begin{tabular}{|c|c</xsl:text>
            <xsl:if test="$section-column gt $date-column">
                <xsl:text>|c</xsl:text>
            </xsl:if>
            <xsl:if test="$lecture-column-1 gt $section-column">
                <xsl:text>|c|l</xsl:text>
            </xsl:if>
            <xsl:if test="$reading-column gt $lecture-column-2">
                <xsl:text>|c</xsl:text>
            </xsl:if>
            <xsl:if test="$laboratory-column-1 gt $reading-column">
                <xsl:text>|c|c</xsl:text>
            </xsl:if>
            <xsl:if test="$tutorial-column-1 gt $laboratory-column-2">
                <xsl:text>|c|c</xsl:text>
            </xsl:if>
            <xsl:if test="$assessment-column gt $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:value-of select="$teaching-period" />
            <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" />
		</common>
	</template>
	
	
    <!--
        Generate calendar footing boilerplate, i.e., all the \end{environment}, etc.
    -->
	<template name="generate-latex-calendar-postamble">
		<common formats="/latex/xelatex/">
		    <xsl:param name="num-columns" tunnel="yes" />
		    <xsl:param name="caption" tunnel="yes" />
		    <xsl:param name="caption-text" tunnel="yes" />
			
			<xsl:text>\hline</xsl:text>
			<xsl:call-template name="newline-internal" />
			
			<xsl:if test="( $caption = ('yes', 'y', 'true', 't', '1') )">
			    <xsl:text>\multicolumn{</xsl:text>
			    <xsl:value-of select="$num-columns" />
			    <xsl:text>}{r}{\emph{</xsl:text>
			    <xsl:value-of select="if ($caption-text ne '') then $caption-text else 'continues over\ldots'" />
			    <xsl:text>}} \\</xsl:text>
    			<xsl:call-template name="newline-internal" />
			</xsl:if>

			<xsl:text>\end{tabular}%</xsl:text>
			<xsl:call-template name="newline-internal" />
			
            <!--
                An explicit @height always takes precedence over @fit-height.
            -->
            <xsl:variable name="table-height" select ="
                if (@height) then @height
                else if (@fit-height) then '\textheight'
                     else '!'" />
                
            <!--
                An explicit @width always takes precedence over @fit-width.
            -->
            <xsl:variable name="table-width" select ="
                if (@width) then @width
                else if (@fit-width) then '\textwidth'
                     else '!'" />
                
            <!-- The default is to print sideways. -->
            <xsl:if test="not(@sideways) or (@sideways = ('yes', 'y', 'true', 't', '1'))">
                <xsl:text>\end{sideways}</xsl:text>
                <xsl:call-template name="newline-internal" />
			</xsl:if>
			
            <xsl:if test="($table-width ne '!') or ($table-height ne '!')">
                <xsl:text>}</xsl:text>
                <xsl:call-template name="newline-internal" />
			</xsl:if>
			
            <xsl:if test="@position-horizontal">
                <xsl:text>\end{</xsl:text>
                <xsl:value-of select="
                    if (@position-horizontal = ('left', 'right')) then 'flush{@position-horizontal}'
                    else if (@position-horizontal = ('center', 'centre')) then @position-horizontal
                    else 'flushleft'" />
                <xsl:text>}</xsl:text>
                <xsl:call-template name="newline-internal" />
            </xsl:if>
            
            <xsl:if test="@position-vertical = ('top', 'center')">
                <xsl:text>\vfill\mbox{}</xsl:text>
                <xsl:call-template name="newline-internal" />
            </xsl:if>
	    </common>
	</template>
	
	
	<!--
	    Break a calendar across pages. This is only relevant to (Xe)LaTeX. The current (following) footer is appended to the closing part of the table, and the current (preceding) header is inserted at the start of the new table.
	    
	    Any child elements of the latex-calendar-break will be inserted into the table after the header.
	-->
	<template name="latex-calendar-break" match="latex-calendar-break">
		<common formats="/latex/xelatex/">
		    <xsl:param name="caption" select="if ( @caption ) then @caption else 'yes'" tunnel="yes" />
		    <xsl:param name="caption-text" select="if ( @caption-text ) then @caption-text else ''" tunnel="yes"/>
		    
		    <xsl:apply-templates select="following::footer" />
		    <xsl:call-template name="generate-latex-calendar-postamble" />
			<xsl:call-template name="newline-internal" />
			
		    <xsl:text>\newpage</xsl:text>
			<xsl:call-template name="newline-internal" />
			
		    <xsl:call-template name="generate-latex-calendar-preamble" />
			<xsl:apply-templates select="preceding::header" />
			<xsl:call-template name="newline-internal" />
			
			<xsl:text>\hline\hline</xsl:text>
			<xsl:call-template name="newline-internal" />
			
			<xsl:apply-templates />
		</common>
	</template>
	
	
</stylesheet>