Apollo transcripts: XSL formatting transformations
By Eric Hartwell - last updated last updated April 1, 2006
For the Apollo 17 project I decided to use a generic and extensible XML format for the timeline data instead of hard-coding the HTML. The formatting can easily be applied using an XSLT style sheet.
Given the XML data layout and the desired HTML output format, designing the XSLT is fairly straightforward.
Required Space
XSLT drops spaces between tags, but does not support Many sources recommend using   as a replacement, but MediaWiki rejects this. It turns out the HTML standard does not support ASCII characters higher than #FF.
For the time being, I rearranged the style sheet so that there is always a non-blank character adjacent to a required space.
- Not allowed:
<b>Cernan:</b> '<xsl:value-of select="node()" /> - Workaround:
<b>Cernan: </b><xsl:value-of select="node()" />
First Pass
The first pass translates tags that will be rendered by wikitext, adds the transcript-specific style sheet, and wraps the remaining tags with XmlTransform instructions for the second pass.
| Tag | Attribute | Action | Output Format |
|---|---|---|---|
| / (root) | Insert or append transcript's CSS styles Wrap output with <XmlTransform xsl="xsl2"> ... </XmlTransform> | <XmlTransform xsl="xsl2"> .... <style type="text/css"> ... </style> </XmlTransform> | |
| section | Tag as ==Wikitext heading 2== | </XmlTransform> ==value of chapter attribute: value of title attribute== <XmlTransform xsl="xsl2"> | |
| chapter | (optional) Prefixed to title as Chapter: Title | ||
| title | Title of section | ||
| seq | (optional) Sequence and/or section number | ||
| include | Insert content from a wikitext page {{Wikitext template}} | </XmlTransform> {{value of title attribute}} <XmlTransform xsl="xsl2"> | |
| title | Title of wikitext page to insert Note: Should use a lookup table to convert the transcript's section title to the title of the corresponding wiki page. | ||
| event/include | Insert content from a wikitext page {{Wikitext template}} | </event></XmlTransform> {{value of title attribute}} <XmlTransform xsl="xsl2"><event> | |
| title | Title of wikitext page to insert | ||
| Future tags | |||
| photo | Insert image from a wikitext page {{Wikitext template}} | </event></XmlTransform> (wikitext image reference) <XmlTransform xsl="xsl2"><event> | |
The actual Apollo 17 first pass transcript XSL looks like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" />
<!-- Main transform with style sheet at end -->
<xsl:template match="/">
<xsl:text disable-output-escaping="yes"><XmlTransform xslfile="C:/Domains/ehartwell.com/wwwroot/wiki/extensions/AS17Flight.xsl"></xsl:text>
<xsl:apply-templates />
<xsl:text disable-output-escaping="yes"><style type="text/css"></xsl:text>
.met {font-family: Arial, Helvetica, sans-serif; font-size: 12px; color: black}
.def {font-family: Arial, Helvetica, sans-serif; font-size: 12px; color: black}
.pao {font-family: Arial, Helvetica, sans-serif; font-size: 12px; color: DarkGreen; margin-left:25px}
.ob {font-family: Arial, Helvetica, sans-serif; font-size: 12px; color: #660000; margin-left:25px}
.tec {font-family: Arial, Helvetica, sans-serif; font-size: 12px; color: black; margin-left:25px}
.ed {font-family: Arial, Helvetica, sans-serif; font-size: 12px; color: Blue; margin-left:50px}
.edinc {font-family: Arial, Helvetica, sans-serif; font-size: 12px; color: MediumBlue; margin-left:60px; margin-right:50px;}
.db {font-family: Arial, Helvetica, sans-serif; font-size: 12px; color: SaddleBrown; margin-left:50px}
.alt {font-family: Arial, Helvetica, sans-serif; font-size: 11px; color: DimGray; margin-left:40px; margin-top:-10px;}
.other {font-family: Arial, Helvetica, sans-serif; font-size: 12px; color: black; margin-left:25px}
.photo {font-family: Arial, Helvetica, sans-serif; font-size: 12px; color: red; margin-left:25px}
<xsl:text disable-output-escaping="yes"></style></xsl:text>
<xsl:text disable-output-escaping="yes"></XmlTransform></xsl:text>
</xsl:template>
<xsl:template match="XmlTransformInput">
<xsl:apply-templates />
</xsl:template>
<!-- Section titles -->
<xsl:template match="section">
<xsl:text disable-output-escaping="yes"></XmlTransform></xsl:text>
==<xsl:value-of select="@chapter" /><xsl:if test="@chapter">: </xsl:if><xsl:value-of select="@title" />==
<xsl:text disable-output-escaping="yes"><XmlTransform xslfile="C:/Domains/ehartwell.com/wwwroot/wiki/extensions/AS17Flight.xsl"></xsl:text>
<xsl:apply-templates />
</xsl:template>
<!-- Include a wiki page: Root level and part of an event -->
<xsl:template match="/include">
<xsl:text disable-output-escaping="yes"></XmlTransform></xsl:text>
{{:<xsl:value-of select="@title" />}}
<xsl:text disable-output-escaping="yes"><XmlTransform xslfile="C:/Domains/ehartwell.com/wwwroot/wiki/extensions/AS17Flight.xsl"></xsl:text>
</xsl:template>
<xsl:template match="event/include">
<xsl:text disable-output-escaping="yes"></event></XmlTransform></xsl:text>
<div class="edinc"><p><b>[[<xsl:value-of select="@title" />]]: </b>{{:<xsl:value-of select="@title" />}}</p></div>
<xsl:text disable-output-escaping="yes"><XmlTransform xslfile="C:/Domains/ehartwell.com/wwwroot/wiki/extensions/AS17Flight.xsl"><event></xsl:text>
</xsl:template>
<!-- Important! Preserve all XML tags that we don't explicitly process here -->
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Second Pass
The second pass processes the content tags that weren't handled in the first pass. Most of the HTML formatting is done here because the tags would have been stripped by the wikitext parser in the previous pass.
The actual Apollo 17 second pass transcript XSL looks like this:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output omit-xml-declaration="yes" /> <xsl:template match="/"> <xsl:apply-templates /> </xsl:template> <xsl:template match="section"> <xsl:apply-templates /> </xsl:template> <xsl:template match="subsection"> <xsl:apply-templates /> </xsl:template> <xsl:template match="event"> <p class="met"><b><xsl:value-of select="@met" /></b></p> <xsl:apply-templates /> </xsl:template> <xsl:template match="event/quote[@src='TechDebrief']"> <xsl:choose > <xsl:when test = "@who='CDR'"><p class="db"><b>Cernan</b> (Technical Debriefing) <xsl:value-of select="node()" /></p></xsl:when> <xsl:when test = "@who='CMP'"><p class="db"><b>Evans</b> (Technical Debriefing) <xsl:value-of select="node()" /></p></xsl:when> <xsl:when test = "@who='LMP'"><p class="db"><b>Schmitt</b> (Technical Debriefing) <xsl:value-of select="node()" /></p></xsl:when> <xsl:otherwise><p class="db"><b><xsl:value-of select="@who" /></b> (Technical Debriefing) <xsl:value-of select="node()" /></p></xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template match="event/quote[not(@src)]"> <xsl:choose > <xsl:when test = "@who='CDR'"><p class="tec"><b>Cernan: </b><xsl:value-of select="node()" /></p></xsl:when> <xsl:when test = "@who='CMP'"><p class="tec"><b>Evans: </b><xsl:value-of select="node()" /></p></xsl:when> <xsl:when test = "@who='LMP'"><p class="tec"><b>Schmitt: </b><xsl:value-of select="node()" /></p></xsl:when> <xsl:when test = "@who='CCo'"><p class="tec"><b>Obermeyer: </b><xsl:value-of select="node()" /></p></xsl:when> <xsl:when test = "@who='CCp'"><p class="tec"><b>Parker: </b><xsl:value-of select="node()" /></p></xsl:when> <xsl:when test = "@who='PAO'"><p class="pao"><b>Public Affairs Officer: </b><xsl:value-of select="node()" /></p></xsl:when> <xsl:when test = "@who='LCC'"><p class="pao"><b>Launch Control: </b><xsl:value-of select="node()" /></p></xsl:when> <xsl:otherwise><p class="other"><b><xsl:value-of select="@who" />: </b><xsl:value-of select="node()" /></p></xsl:otherwise> </xsl:choose> <xsl:apply-templates select="alternate" /> </xsl:template> <xsl:template match="event/quote/alternate"> <p class="alt"> <xsl:choose > <xsl:when test = "@src='PAO'">(PAO) </xsl:when> <xsl:when test = "@src='Tech'">(Air-to-Ground) </xsl:when> <xsl:otherwise>(Tape 17-<xsl:value-of select="@src" />) </xsl:otherwise> </xsl:choose> <xsl:value-of select="node()" /><br /> </p> </xsl:template> <xsl:template match="event/photo"> <p class="photo"><b>Photo <xsl:value-of select="@id" /></b> <xsl:if test = "@frame"> (Frame <xsl:value-of select="@frame" />) </xsl:if> <xsl:if test = "node()"> <b>:</b><xsl:value-of select="node()" /></xsl:if> </p> </xsl:template> <xsl:template match="event/comment"> <p class="ed">[<xsl:value-of select="node()" />]</p> </xsl:template> <xsl:template match="style"> <style type="text/css"><xsl:value-of select="node()" /></style> </xsl:template> </xsl:stylesheet>
Revision History
- April 1, 2006 - two-pass transform
- March 27, 2006 - initial version