File: //usr/share/yelp-xsl/xslt/mallard/common/mal-gloss.xsl
<?xml version='1.0' encoding='UTF-8'?><!-- -*- indent-tabs-mode: nil -*- -->
<!--
This program is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your option) any
later version.
This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
details.
You should have received a copy of the GNU Lesser General Public License
along with this program; see the file COPYING.LGPL. If not, see <http://www.gnu.org/licenses/>.
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:mal="http://projectmallard.org/1.0/"
xmlns:cache="http://projectmallard.org/cache/1.0/"
xmlns:gloss="http://projectmallard.org/experimental/gloss/"
xmlns:exsl="http://exslt.org/common"
xmlns:str="http://exslt.org/strings"
exclude-result-prefixes="mal cache gloss exsl str"
version="1.0">
<!--!!==========================================================================
Mallard Glossaries
Common templates for the Mallard Glossary extension.
This stylesheet contains utility templates for locating and linking to terms
with the Mallard Glossary extension.
-->
<!--++==========================================================================
mal.gloss.key
Get a `gloss:term` element from its `id` attribute.
This key returns `gloss:term` elements based on their `id` attribute. This
key only applies to elements inside a cache file. Make sure to make the cache
file the context document before calling this key.
-->
<xsl:key name="mal.gloss.key"
match="/cache:cache//mal:info/gloss:term[@id]"
use="@id"/>
<!--**==========================================================================
mal.gloss.match
Determine whether a glossary term matches a criterion.
[xsl:params]
$match: A `gloss:match` element containing criteria.
$term: A `gloss:term` element to attempt to match.
This template determines whether a glossary term matches a condition, as given
by a `gloss:match` element. If the term matches, an empty string is output.
Otherwise, a non-empty string is output.
To determine if a term matches a set of matches, call this template for each
`gloss:match` element, then check if the concatenated result is empty.
-->
<xsl:template name="mal.gloss.match">
<xsl:param name="match"/>
<xsl:param name="term"/>
<xsl:if test="$match/@tags and not(str:split($match/@tags) = str:split($term/@tags))">
<xsl:text>x</xsl:text>
</xsl:if>
</xsl:template>
<!--**==========================================================================
mal.gloss.terms
Output the glossary terms for a page or section.
[xsl:params]
$node: The glossary `page` or `section` to output terms for.
This template outputs the terms that should be displayed for $node.This output
is a result tree fragment. To use these results, call `exsl:node-set` on them.
This template locates all terms throughout all pages and filters them based on
any `gloss:match` elements in the `info` child of $node, and also excludes
terms that are matched by child sections of $node.
The filtered terms are then grouped by matching ID. For each unique ID, this
template outputs a `gloss:term` element with the corresponding `id` attribute.
Each of these elements contains `title` elements reflecting the titles in the
actual term definitions. These titles have duplicates removed, compared by the
space-normalized string value, and are sorted.
These `gloss:term` elements then contain further `gloss:term` elements, which
are copies of the actual terms with the same ID. These elements have an `xref`
attribute added containing the ID of the containing page.
The top-level `gloss:term` elements and the `gloss:term` elements they contain
are not sorted. Only the `title` elements in the top-level `gloss:term`
elements are sorted.
-->
<xsl:template name="mal.gloss.terms">
<xsl:param name="node" select="."/>
<xsl:variable name="allterms_">
<xsl:for-each select="$mal.cache//mal:info/gloss:term[@id]">
<xsl:variable name="term" select="."/>
<xsl:variable name="exclude">
<xsl:for-each select="$node/ancestor-or-self::*/mal:info/gloss:match">
<xsl:call-template name="mal.gloss.match">
<xsl:with-param name="match" select="."/>
<xsl:with-param name="term" select="$term"/>
</xsl:call-template>
</xsl:for-each>
<xsl:for-each select="$node/mal:section/mal:info/gloss:match">
<xsl:variable name="secmatch">
<xsl:call-template name="mal.gloss.match">
<xsl:with-param name="match" select="."/>
<xsl:with-param name="term" select="$term"/>
</xsl:call-template>
</xsl:variable>
<xsl:if test="$secmatch = ''">
<xsl:text>x</xsl:text>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:if test="$exclude = ''">
<xsl:copy>
<xsl:attribute name="xref">
<xsl:value-of select="ancestor::mal:page[1]/@id"/>
</xsl:attribute>
<xsl:for-each select="@*[name() != 'xref'] | *">
<xsl:choose>
<xsl:when test="self::mal:title">
<xsl:copy>
<xsl:attribute name="title">
<xsl:value-of select="normalize-space(.)"/>
</xsl:attribute>
<xsl:copy-of select="node()"/>
</xsl:copy>
</xsl:when>
<xsl:otherwise>
<xsl:copy-of select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:copy>
</xsl:if>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="allterms" select="exsl:node-set($allterms_)/gloss:term"/>
<xsl:for-each select="$allterms">
<xsl:if test="not(@id = preceding-sibling::gloss:term/@id)">
<xsl:variable name="id" select="@id"/>
<gloss:term id="{$id}">
<xsl:variable name="entries" select="$allterms/self::gloss:term[@id = $id]"/>
<xsl:variable name="titles_">
<xsl:for-each select="$entries/mal:title">
<xsl:copy-of select="."/>
</xsl:for-each>
</xsl:variable>
<xsl:variable name="titles" select="exsl:node-set($titles_)/mal:title"/>
<xsl:for-each select="$titles">
<xsl:sort select="string(.)"/>
<xsl:if test="not(@title = preceding-sibling::mal:title/@title)">
<xsl:copy-of select="."/>
</xsl:if>
</xsl:for-each>
<xsl:copy-of select="$entries"/>
</gloss:term>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>