Skip to content

Instantly share code, notes, and snippets.

@pyrat
Created April 21, 2009 10:05
Show Gist options
  • Save pyrat/99058 to your computer and use it in GitHub Desktop.
Save pyrat/99058 to your computer and use it in GitHub Desktop.
<?xml version="1.0" encoding="UTF-8" ?>
<!-- This file is part of the DITA Open Toolkit project hosted on
Sourceforge.net. See the accompanying license.txt file for
applicable licenses.-->
<!-- (c) Copyright IBM Corp. 2004, 2005 All Rights Reserved. -->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:saxon="http://icl.com/saxon"
extension-element-prefixes="saxon"
>
<!-- map2htmltoc.xsl main stylesheet
Convert DITA map to a simple HTML table-of-contents view.
Input = one DITA map file
Output = one HTML file for viewing/checking by the author.
Options:
OUTEXT = XHTML output extension (default is '.html')
WORKDIR = The working directory that contains the document being transformed.
Needed as a directory prefix for the @href "document()" function calls.
Default is './'
-->
<!-- Include error message template -->
<xsl:import href="common/output-message.xsl"/>
<xsl:output method="html" indent="no"/>
<!-- Set the prefix for error message numbers -->
<xsl:variable name="msgprefix">DOTX</xsl:variable>
<!-- *************************** Command line parameters *********************** -->
<xsl:param name="OUTEXT" select="'.html'"/><!-- "htm" and "html" are valid values -->
<xsl:param name="WORKDIR" select="'./'"/>
<xsl:param name="DITAEXT" select="'.xml'"/>
<xsl:param name="FILEREF" select="'file://'"/>
<xsl:param name="contenttarget" select="'contentwin'"/>
<xsl:param name="CSS"/>
<xsl:param name="CSSPATH"/>
<xsl:param name="OUTPUTCLASS"/> <!-- class to put on body element. -->
<!-- the path back to the project. Used for c.gif, delta.gif, css to allow user's to have
these files in 1 location. -->
<xsl:param name="PATH2PROJ">
<xsl:apply-templates select="/processing-instruction('path2project')" mode="get-path2project"/>
</xsl:param>
<!-- Define a newline character -->
<xsl:variable name="newline"><xsl:text>
</xsl:text></xsl:variable>
<xsl:template match="processing-instruction('path2project')" mode="get-path2project">
<xsl:value-of select="."/>
</xsl:template>
<!-- *********************************************************************************
Setup the HTML wrapper for the table of contents
********************************************************************************* -->
<xsl:template match="/">
<html><xsl:value-of select="$newline"/>
<head><xsl:value-of select="$newline"/>
<xsl:if test="string-length($contenttarget)>0 and
$contenttarget!='NONE'">
<base target="{$contenttarget}"/>
</xsl:if>
<xsl:choose>
<xsl:when test="/*[contains(@class,' map/map ')]/*[contains(@class,' topic/title ')]">
<title><xsl:value-of select="/*[contains(@class,' map/map ')]/*[contains(@class,' topic/title ')]"/></title><xsl:value-of select="$newline"/>
</xsl:when>
<xsl:when test="/*[contains(@class,' map/map ')]/@title">
<title><xsl:value-of select="/*[contains(@class,' map/map ')]/@title"/></title><xsl:value-of select="$newline"/>
</xsl:when>
</xsl:choose>
<xsl:call-template name="generateCssLinks"/>
<xsl:call-template name="gen-user-head"/>
<xsl:call-template name="gen-user-scripts"/>
<xsl:call-template name="gen-user-styles"/>
</head><xsl:value-of select="$newline"/>
<body>
<xsl:if test="string-length($OUTPUTCLASS) &gt; 0">
<xsl:attribute name="class">
<xsl:value-of select="$OUTPUTCLASS"/>
</xsl:attribute>
</xsl:if>
<xsl:value-of select="$newline"/>
<xsl:apply-templates/>
</body><xsl:value-of select="$newline"/>
</html>
</xsl:template>
<!-- *********************************************************************************
If processing only a single map, setup the HTML wrapper and output the contents.
Otherwise, just process the contents.
********************************************************************************* -->
<xsl:template match="/*[contains(@class, ' map/map ')]">
<xsl:param name="pathFromMaplist"/>
<xsl:if test=".//*[contains(@class, ' map/topicref ')][not(@toc='no')]">
<ul><xsl:value-of select="$newline"/>
<xsl:apply-templates select="*[contains(@class, ' map/topicref ')]">
<xsl:with-param name="pathFromMaplist" select="$pathFromMaplist"/>
</xsl:apply-templates>
</ul><xsl:value-of select="$newline"/>
</xsl:if>
</xsl:template>
<!-- *********************************************************************************
Output each topic as an <li> with an A-link. Each item takes 2 values:
- A title. If a navtitle is specified on <topicref>, use that.
Otherwise try to open the file and retrieve the title. First look for a navigation title in the topic,
followed by the main topic title. Last, try to use <linktext> specified in the map.
Failing that, use *** and issue a message.
- An HREF is optional. If none is specified, this will generate a wrapper.
Include the HREF if specified.
- Ignore if TOC=no.
If this topicref has any child topicref's that will be part of the navigation,
output a <ul> around them and process the contents.
********************************************************************************* -->
<xsl:template match="*[contains(@class, ' map/topicref ')][not(@toc='no')]">
<xsl:param name="pathFromMaplist"/>
<xsl:variable name="title">
<xsl:call-template name="navtitle"/>
</xsl:variable>
<xsl:choose>
<xsl:when test="$title and $title!=''">
<li>
<xsl:choose>
<!-- If there is a reference to a DITA or HTML file, and it is not external: -->
<xsl:when test="@href and not(@href='')">
<xsl:element name="a">
<xsl:attribute name="href">
<xsl:choose> <!-- What if targeting a nested topic? Need to keep the ID? -->
<xsl:when test="contains(@copy-to, $DITAEXT)">
<xsl:if test="not(@scope='external')"><xsl:value-of select="$pathFromMaplist"/></xsl:if>
<xsl:value-of select="substring-before(@copy-to,$DITAEXT)"/><xsl:value-of select="$OUTEXT"/>
</xsl:when>
<xsl:when test="contains(@href,$DITAEXT)">
<xsl:if test="not(@scope='external')"><xsl:value-of select="$pathFromMaplist"/></xsl:if>
<xsl:value-of select="substring-before(@href,$DITAEXT)"/><xsl:value-of select="$OUTEXT"/>
</xsl:when>
<xsl:otherwise> <!-- If non-DITA, keep the href as-is -->
<xsl:if test="not(@scope='external')"><xsl:value-of select="$pathFromMaplist"/></xsl:if>
<xsl:value-of select="@href"/>
</xsl:otherwise>
</xsl:choose>
</xsl:attribute>
<xsl:if test="@scope='external' or @type='external' or ((@format='PDF' or @format='pdf') and not(@scope='local'))">
<xsl:attribute name="target">_blank</xsl:attribute>
</xsl:if>
<xsl:value-of select="$title"/>
</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$title"/>
</xsl:otherwise>
</xsl:choose>
<!-- If there are any children that should be in the TOC, process them -->
<xsl:if test="descendant::*[contains(@class, ' map/topicref ')][not(contains(@toc,'no'))]">
<xsl:value-of select="$newline"/><ul><xsl:value-of select="$newline"/>
<xsl:apply-templates select="*[contains(@class, ' map/topicref ')]">
<xsl:with-param name="pathFromMaplist" select="$pathFromMaplist"/>
</xsl:apply-templates>
</ul><xsl:value-of select="$newline"/>
</xsl:if>
</li><xsl:value-of select="$newline"/>
</xsl:when>
<xsl:otherwise>
<!-- if it is an empty topicref -->
<xsl:apply-templates select="*[contains(@class, ' map/topicref ')]">
<xsl:with-param name="pathFromMaplist" select="$pathFromMaplist"/>
</xsl:apply-templates>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- If toc=no, but a child has toc=yes, that child should bubble up to the top -->
<xsl:template match="*[contains(@class, ' map/topicref ')][@toc='no']">
<xsl:param name="pathFromMaplist"/>
<xsl:apply-templates select="*[contains(@class, ' map/topicref ')]">
<xsl:with-param name="pathFromMaplist" select="$pathFromMaplist"/>
</xsl:apply-templates>
</xsl:template>
<xsl:template match="processing-instruction('workdir')" mode="get-work-dir">
<xsl:value-of select="."/><xsl:text>/</xsl:text>
</xsl:template>
<xsl:template name="navtitle">
<xsl:variable name="WORKDIR">
<xsl:value-of select="$FILEREF"/>
<xsl:apply-templates select="/processing-instruction()" mode="get-work-dir"/>
</xsl:variable>
<xsl:choose>
<!-- If navtitle is specified, use it (!?but it should only be used when locktitle=yes is specifed?!) -->
<xsl:when test="@navtitle"><xsl:value-of select="@navtitle"/></xsl:when>
<!-- If this references a DITA file (has @href, not "local" or "external"),
try to open the file and get the title -->
<xsl:when test="@href and not(@href='') and
not ((ancestor-or-self::*/@scope)[last()]='external') and
not ((ancestor-or-self::*/@scope)[last()]='peer') and
not ((ancestor-or-self::*/@type)[last()]='external') and
not ((ancestor-or-self::*/@type)[last()]='local')">
<!-- Need to worry about targeting a nested topic? Not for now. -->
<!--<xsl:variable name="FileWithPath"><xsl:value-of select="$WORKDIR"/><xsl:choose>-->
<xsl:variable name="FileWithPath"><xsl:choose>
<xsl:when test="@copy-to"><xsl:value-of select="$WORKDIR"/><xsl:value-of select="@copy-to"/></xsl:when>
<xsl:when test="contains(@href,'#')"><xsl:value-of select="$WORKDIR"/><xsl:value-of select="substring-before(@href,'#')"/></xsl:when>
<xsl:otherwise><xsl:value-of select="$WORKDIR"/><xsl:value-of select="@href"/></xsl:otherwise></xsl:choose></xsl:variable>
<xsl:variable name="TargetFile" select="document($FileWithPath,/)"/>
<xsl:choose>
<xsl:when test="not($TargetFile)"> <!-- DITA file does not exist -->
<xsl:choose>
<xsl:when test="*[contains(@class, ' map/topicmeta ')]/*[contains(@class, ' map/linktext ')]"> <!-- attempt to recover by using linktext -->
<xsl:value-of select="*[contains(@class, ' map/topicmeta ')]/*[contains(@class, ' map/linktext ')]"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="output-message">
<xsl:with-param name="msgnum">008</xsl:with-param>
<xsl:with-param name="msgsev">W</xsl:with-param>
<xsl:with-param name="msgparams">%1=<xsl:value-of select="@href"/></xsl:with-param>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<!-- First choice for navtitle: topic/titlealts/navtitle -->
<xsl:when test="$TargetFile/*[contains(@class,' topic/topic ')]/*[contains(@class,' topic/titlealts ')]/*[contains(@class,' topic/navtitle ')]">
<xsl:value-of select="$TargetFile/*[contains(@class,' topic/topic ')]/*[contains(@class,' topic/titlealts ')]/*[contains(@class,' topic/navtitle ')]"/>
</xsl:when>
<!-- Second choice for navtitle: topic/title -->
<xsl:when test="$TargetFile/*[contains(@class,' topic/topic ')]/*[contains(@class,' topic/title ')]">
<xsl:value-of select="$TargetFile/*[contains(@class,' topic/topic ')]/*[contains(@class,' topic/title ')]"/>
</xsl:when>
<!-- This might be a combo article; modify the same queries: dita/topic/titlealts/navtitle -->
<xsl:when test="$TargetFile/dita/*[contains(@class,' topic/topic ')]/*[contains(@class,' topic/titlealts ')]/*[contains(@class,' topic/navtitle ')]">
<xsl:value-of select="$TargetFile/dita/*[contains(@class,' topic/topic ')]/*[contains(@class,' topic/titlealts ')]/*[contains(@class,' topic/navtitle ')]"/>
</xsl:when>
<!-- Second choice: dita/topic/title -->
<xsl:when test="$TargetFile/dita/*[contains(@class,' topic/topic ')]/*[contains(@class,' topic/title ')]">
<xsl:value-of select="$TargetFile/dita/*[contains(@class,' topic/topic ')]/*[contains(@class,' topic/title ')]"/>
</xsl:when>
<!-- Last choice: use the linktext specified within the topicref -->
<xsl:when test="*[contains(@class, ' map/topicmeta ')]/*[contains(@class, ' map/linktext ')]">
<xsl:value-of select="*[contains(@class, ' map/topicmeta ')]/*[contains(@class, ' map/linktext ')]"/>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="output-message">
<xsl:with-param name="msgnum">009</xsl:with-param>
<xsl:with-param name="msgsev">W</xsl:with-param>
<xsl:with-param name="msgparams">%1=<xsl:value-of select="@TargetFile"/>;%2=***</xsl:with-param>
</xsl:call-template>
<xsl:text>***</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:when>
<!-- If there is no title and none can be retrieved, check for <linktext> -->
<xsl:when test="*[contains(@class, ' map/topicmeta ')]/*[contains(@class, ' map/linktext ')]">
<xsl:value-of select="*[contains(@class, ' map/topicmeta ')]/*[contains(@class, ' map/linktext ')]"/>
</xsl:when>
<!-- No local title, and not targeting a DITA file. Could be just a container setting
metadata, or a file reference with no title. Issue message for the second case. -->
<xsl:otherwise>
<xsl:if test="@href and not(@href='')">
<xsl:call-template name="output-message">
<xsl:with-param name="msgnum">009</xsl:with-param>
<xsl:with-param name="msgsev">W</xsl:with-param>
<xsl:with-param name="msgparams">%1=<xsl:value-of select="@href"/>;%2=<xsl:value-of select="@href"/></xsl:with-param>
</xsl:call-template>
<xsl:value-of select="@href"/>
</xsl:if>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Link to user CSS. -->
<!-- Test for URL: returns "url" when the content starts with a URL;
Otherwise, leave blank -->
<xsl:template name="url-string">
<xsl:param name="urltext"/>
<xsl:choose>
<xsl:when test="contains($urltext,'http://')">url</xsl:when>
<xsl:when test="contains($urltext,'https://')">url</xsl:when>
<xsl:otherwise/>
</xsl:choose>
</xsl:template>
<!-- Can't link to commonltr.css or commonrtl.css because we don't know what language the map is in. -->
<xsl:template name="generateCssLinks">
<xsl:variable name="urltest">
<xsl:call-template name="url-string">
<xsl:with-param name="urltext">
<xsl:value-of select="concat($CSSPATH,$CSS)"/>
</xsl:with-param>
</xsl:call-template>
</xsl:variable>
<xsl:if test="string-length($CSS)>0">
<xsl:choose>
<xsl:when test="$urltest='url'">
<link rel="stylesheet" type="text/css" href="{$CSSPATH}{$CSS}" />
</xsl:when>
<xsl:otherwise>
<link rel="stylesheet" type="text/css" href="{$PATH2PROJ}{$CSSPATH}{$CSS}" />
</xsl:otherwise>
</xsl:choose><xsl:value-of select="$newline"/>
</xsl:if>
</xsl:template>
<!-- To be overridden by user shell. -->
<xsl:template name="gen-user-head"/>
<xsl:template name="gen-user-scripts"/>
<xsl:template name="gen-user-styles"/>
<!-- These are here just to prevent accidental fallthrough -->
<xsl:template match="*[contains(@class, ' map/navref ')]"/>
<xsl:template match="*[contains(@class, ' map/anchor ')]"/>
<xsl:template match="*[contains(@class, ' map/reltable ')]"/>
<xsl:template match="*[contains(@class, ' map/topicmeta ')]"/>
<!--xsl:template match="*[contains(@class, ' map/topicref ') and contains(@class, '/topicgroup ')]"/-->
<xsl:template match="*">
<xsl:apply-templates/>
</xsl:template>
<!-- Convert the input value to lowercase & return it -->
<xsl:template name="convert-to-lower">
<xsl:param name="inputval"/>
<xsl:value-of select="translate($inputval,
'-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
'-abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz')"/>
</xsl:template>
<!-- Template to get the relative path to a map -->
<xsl:template name="getRelativePath">
<xsl:param name="remainingPath" select="@file"/>
<xsl:choose>
<xsl:when test="contains($remainingPath,'/')">
<xsl:value-of select="substring-before($remainingPath,'/')"/><xsl:text>/</xsl:text>
<xsl:call-template name="getRelativePath">
<xsl:with-param name="remainingPath" select="substring-after($remainingPath,'/')"/>
</xsl:call-template>
</xsl:when>
<xsl:when test="contains($remainingPath,'\')">
<xsl:value-of select="substring-before($remainingPath,'\')"/><xsl:text>/</xsl:text>
<xsl:call-template name="getRelativePath">
<xsl:with-param name="remainingPath" select="substring-after($remainingPath,'\')"/>
</xsl:call-template>
</xsl:when>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment