+<!--
+ Allow abbr to apply to document titles too, since these are generated
+ and kramdown's abbr support won't influence them. We do this by just
+ checking each word of the heading to see if is identical to an existing
+ abbr tag, and just substituting that in its place.
+ -->
+<xsl:key name='abbr' match='xhtml:abbr' use='string(.)' />
+<xsl:template name='insert-abbr' match='xhtml:h1/text()'>
+ <xsl:param name='string' select='normalize-space(.)' />
+
+ <xsl:variable name='head'
+ select='substring-before(concat($string, " "), " ")' />
+ <xsl:variable name='tail' select='substring-after($string, " ")' />
+ <xsl:variable name='match' select='key("abbr", $head)[1]' />
+
+ <xsl:choose>
+ <xsl:when test='$match'><xsl:apply-templates select='$match' /></xsl:when>
+ <xsl:otherwise><xsl:value-of select='$head' /></xsl:otherwise>
+ </xsl:choose>
+ <xsl:if test='$tail'>
+ <xsl:text> </xsl:text>
+ <xsl:call-template name='insert-abbr'>
+ <xsl:with-param name='string' select='$tail' />
+ </xsl:call-template>
+ </xsl:if>
+</xsl:template>
+
+<!--
+ Convert caption attribute on tables into proper caption elements, to allow
+ a simple way to add captions to kramdown tables.
+-->
+<xsl:template match='@caption[parent::xhtml:table]' />
+<xsl:template match='xhtml:table[@caption]'>
+ <xsl:copy>
+ <xsl:apply-templates select='@*' />
+ <caption><xsl:value-of select='normalize-space(@caption)' /></caption>
+ <xsl:apply-templates select='node()' />
+ </xsl:copy>
+</xsl:template>
+
+<!--
+ Delete style elements, as they will get hoisted occur under <head> below.
+ If the generate-listing attribute was specified, produce a code listing
+ where the style attribute was found.
+-->
+<xsl:template match='xhtml:style|@generate-listing[parent::xhtml:style]' />
+<xsl:template match='xhtml:style[@generate-listing]'>
+ <pre>⁠<code><xsl:value-of select='f:strip-leading(.)' /></code></pre>
+</xsl:template>
+
+<!--
+ Attempt to wrap the first bit of linked email addresses to allow
+ linewrapping to occur after the '@'.
+-->
+<xsl:template match='xhtml:a[starts-with(@href,"mailto:")]/text()'>
+ <xsl:variable name='addr' select='substring-after(../@href, "mailto:")' />
+
+ <xsl:variable name='wrap'
+ select='concat(substring-before($addr, "@"), "@")' />
+
+ <xsl:choose>
+ <xsl:when test='contains(., $wrap)'>
+ <xsl:value-of select='substring-before(., $wrap)' />
+ <span class='wbr'>
+ <xsl:value-of select='$wrap' />
+ </span>
+ <xsl:value-of select='substring-after(., $wrap)' />
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:copy />
+ </xsl:otherwise>
+ </xsl:choose>
+</xsl:template>
+
+<!--
+ Add a simple way to reference a document node by ID and include the XHTML
+ code listing directly in the document.
+-->
+<xsl:template match='xhtml:generate-xhtml-listing'>
+ <xsl:variable name='target' select='@target' />
+ <pre>⁠<code>
+ <xsl:value-of select='f:xhtml-listing(//xhtml:*[@id=$target])' />
+ </code></pre>
+</xsl:template>
+
+<!-- For paragraphs containing only kbd elements, wrap in blockquote. -->
+<xsl:template match='xhtml:p[*[last()=count(../xhtml:kbd|../xhtml:br)]]'>
+ <blockquote>
+ <xsl:copy>
+ <xsl:apply-templates select='node()|@*' />
+ </xsl:copy>
+ </blockquote>
+</xsl:template>
+
+<!--
+ Wrap each word of text in kbd elements in spans, so they can be styled
+ to avoid linebreaks in the middle of option names and other bad places.
+-->
+<xsl:template name='spanify-text' match='xhtml:kbd/text()'>
+ <xsl:param name='text' select='normalize-space(.)' />
+ <xsl:variable name='firstword' select='substring-before($text, " ")' />
+ <xsl:choose>
+ <xsl:when test='$firstword'>
+ <span><xsl:value-of select='$firstword' /></span>
+ <xsl:text> </xsl:text>
+ </xsl:when>
+ <xsl:when test='$text'>
+ <span><xsl:value-of select='$text' /></span>
+ </xsl:when>
+ </xsl:choose>
+ <xsl:if test='$firstword'>
+ <xsl:call-template name='spanify-text'>
+ <xsl:with-param name='text' select='substring-after($text, " ")' />
+ </xsl:call-template>
+ </xsl:if>
+</xsl:template>
+