<?xml version="1.0" encoding="utf-8"?>
<!--

	xdoc.xsl - an XSLT stylesheet to document other stylesheets
	Copyright (C) 2004 Anton Triest

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU 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 General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

	(http://www.gnu.org/licenses/gpl.html)

	Anton Triest [anton@cking.be]
	http://www.cking.be/webstuff/xdoc/

-->
<?xml-stylesheet type="text/xsl" href="xdoc.xsl"?>

<xsl:stylesheet
     version="1.0"
     xmlns="http://www.w3.org/1999/xhtml"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:xdoc="http://cking.be/xdoc"
     extension-element-prefixes="xdoc"
     >

	<xsl:strip-space elements="*"/>

	<xdoc:head title="xdoc"><!-- optional version="" revised="" -->
		<xdoc:author id="AT" name="Anton Triest" e-mail="anton@cking.be"/>
		<xdoc:history>
			<xdoc:revision version="0.2.6" revised="2004-10-07" author="AT">
				<ul>
					<li>made xdoc:head/@version optional</li>
					<li>added xdoc:head/@revised instead of xdoc:head/xdoc:revised (also optional)</li>
					<li>if xdoc:head has no @version or @revised, use xdoc:head/xdoc:history/xdoc:revision[1]</li>
					<li>added @author to xdoc:revision (linked to xdoc:author/@id via key)</li>
					<li>TOC heading shows number of templates</li>
				</ul>
			</xdoc:revision>
			<xdoc:revision version="0.2.5" revised="2004-10-04" author="AT">
				<ul>
					<li>new e-mail (anton@cking.be)</li>
					<li>new namespace (http://cking.be/xdoc)</li>
					<li>added <a href="#mode-get-class">mode="get-class"</a></li>
					<li>added <a href="#doc-output">mode="doc-output"</a></li>
					<li>added xdoc:history</li>
					<li>reordered templates: first xsl, then xdoc</li>
				</ul>
			</xdoc:revision>
			<xdoc:revision version="0.2.4" revised="2004-07-17" author="AT">
				<ul><li>first public beta</li></ul>
			</xdoc:revision>
		</xdoc:history>
		<xdoc:doc>
			<p>this stylesheet generates <a href="http://www.w3.org/TR/xhtml1/">XHTML</a> documentation about another stylesheet</p>
			<p>(and, as you can see, it can even generate documentation about itself)</p>
			<!-- TODO: <img src="xdoc-home.png"/> -->
			<p><a href="http://users.telenet.be/cking/webstuff/xdoc/">Instructions...</a></p>
		</xdoc:doc>
		<!-- note that comments within the xdoc namespace (like this one) don't appear in the output -->
		<xsl:fallback/>
	</xdoc:head>

	<xdoc:doc id="xo">
		<p>output as XHTML 1.0 Strict</p>
		<xsl:fallback/>
	</xdoc:doc>
	<xsl:output
	     method="xml"
	     encoding="iso-8859-1"
	     indent="yes"
	     doctype-public="-//W3C//DTD XHTML 1.0 Strict//EN"
	     doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"
	     />

	<xdoc:doc>
		<p>global variables</p>
		<xsl:fallback/>
	</xdoc:doc>
	<xsl:variable name="base-path" select="'http://users.telenet.be/cking/webstuff/xdoc/'"/>
	<xsl:variable name="tab-size" select="12"/>

	<xdoc:doc>
		<p>keys</p>
		<xsl:fallback/>
	</xdoc:doc>
	<xsl:key name="authors" match="xdoc:author" use="@id"/>

	<xdoc:doc>
		<h3>xsl templates</h3>
		<p><b>/xsl:*</b> root element (typically <i>xsl:stylesheet</i> or <i>xsl:transform</i>)</p>
		<xsl:fallback/>
	</xdoc:doc>
	<xsl:template match="/xsl:*">
		<xsl:variable name="title" select="xdoc:head/@title"/>
		<html lang="en" xml:lang="en">
			<head>
				<title><xsl:value-of select="$title"/></title>
				<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
				<meta http-equiv="imagetoolbar" content="no"/>
				<meta name="generator" content="XSLT stylesheet [xdoc.xsl]"/>
				<xdoc:doc>
					<!-- NOTE: we can't use {$base-path} here, because we're in another namespace -->
					<p>
						link to <a href="http://users.telenet.be/cking/webstuff/xdoc/xdoc.css">xdoc.css</a> stylesheet
						(must use absolute path URL to enable CSS on any input file)
					</p>
					<xsl:fallback/>
				</xdoc:doc>
				<link rel="stylesheet" type="text/css" href="{$base-path}xdoc.css"/>
			</head>
			<body>
				<xdoc:doc id="root-head">
					<p>process <b>xdoc:head</b> element (assume there's only one such in the input document)</p>
					<p>called with mode="<a href="#mode-head">head</a>" so we can <a href="#head-elem">filter</a> it out later</p>
					<xsl:fallback/>
				</xdoc:doc>
				<xsl:apply-templates select="xdoc:head" mode="head"/>
				<xdoc:doc>
					<p>generate TOC (list of all templates)</p>
					<xsl:fallback/>
				</xdoc:doc>
				<h2 id="toc"><xsl:value-of select="count(xsl:template)"/> templates</h2>
				<!-- TODO: add namespaces -->
				<!-- TODO: add id's -->
				<ul class="toc">
					<xsl:apply-templates select="xsl:template" mode="toc"/>
				</ul>				
				<h2><xsl:value-of select="name()"/></h2>
				<xdoc:doc>
					<p>build opening tag for <b>root</b> element</p>
					<xsl:fallback/>
				</xdoc:doc>
				<p class="code elem-xsl">
					<!-- NOTE: xmlns attributes not displayed in output -->
					&lt;<xsl:value-of select="name()"/><xsl:apply-templates select="@*"/>&gt;
				</p>
				<xdoc:doc>
					<p>process all children</p>
					<xsl:fallback/>
				</xdoc:doc>
				<xsl:apply-templates/>
				<xdoc:doc>
					<p>add empty heading and close root element</p>
					<xsl:fallback/>
				</xdoc:doc>
				<h2>&#0160;</h2>
				<p class="code elem-xsl">&lt;/<xsl:value-of select="name()"/>&gt;</p>
				<xdoc:doc>
					<p>revision history</p>
					<xsl:fallback/>
				</xdoc:doc>
				<xsl:apply-templates select="xdoc:head" mode="history"/>
				<xdoc:doc>
					<p>add <a href="#footer">footer</a> with xdoc link</p>
					<xsl:fallback/>
				</xdoc:doc>
					<xsl:call-template name="footer"/>
			</body>
		</html>
	</xsl:template>

	<xdoc:doc>
		<p>generate TOC</p>
		<xsl:fallback/>
	</xdoc:doc>
	<xsl:template match="xsl:template" mode="toc">
		<li>
			<a href="#{generate-id()}">
				<xsl:text>template</xsl:text>
				<xsl:apply-templates select="@*"/>
			</a>
		</li>
	</xsl:template>

	<xdoc:doc>
		<p>process any element</p>
		<xsl:fallback/>
	</xdoc:doc>
	<xsl:template match="*">
		<xdoc:doc>
			<p>calculate <i>indent-size</i> for this line (in pixels; will be passed to CSS in style attribute)</p>
			<xsl:fallback/>
		</xdoc:doc>
		<xsl:variable name="indent-size" select="count(ancestor::*) * $tab-size"/>
		<xdoc:doc>
			<p>is this an <b>xsl</b> element? if not, assume it's <b>html</b></p>
			<xsl:fallback/>
		</xdoc:doc>
		<xsl:variable name="class">
			<xsl:apply-templates select="." mode="get-class"/>
		</xsl:variable>
		<xdoc:doc>
			<p>if this is 'xsl:output' with more than 2 attributes, display all attributes on a separate line</p>
			<xsl:fallback/>
		</xdoc:doc>
		<xsl:variable name="attr-newline" select="name() = 'xsl:output' and count(@*) &gt; 2"/>
		<xdoc:doc>
			<p>if this node only contains text, no other children, display text on one line, together with its enclosing element</p>
			<xsl:fallback/>
		</xdoc:doc>
		<xsl:variable name="one-line-text" select="text() and not(*)"/>
		<xdoc:doc>
			<p>build <b>opening tag</b> (with name and all attributes, but no &lt;&gt; braces yet)</p>
			<xsl:fallback/>
		</xdoc:doc>
		<xsl:variable name="title">
			<xsl:value-of select="name()"/>
			<xsl:if test="not($attr-newline)">
				<xsl:apply-templates select="@*"/>
				<xsl:if test="count(child::*|text()|comment()) = 0">/</xsl:if>
			</xsl:if>
		</xsl:variable>
		<xdoc:doc>
			<p>if the element's name is 'xsl:template', insert <b>h2</b> heading</p>
			<xsl:fallback/>
		</xdoc:doc>
		<xsl:if test="name() = 'xsl:template'">
			<h2 style="padding-left: {$indent-size}px;">
				<a id="{generate-id()}"></a>
				<xsl:value-of select="$title"/>
			</h2>
		</xsl:if>
		<xdoc:doc>
			<p>display element (&lt;&gt; braces are added here)</p>
			<xsl:fallback/>
		</xdoc:doc>
		<p class="code {$class}" style="padding-left: {$indent-size}px;">
			&lt;<xsl:copy-of select="$title"/>
			<xsl:if test="not($attr-newline)">&gt;</xsl:if>
			<xsl:if test="$one-line-text">
				<xsl:apply-templates mode="one-line-text"/>
			</xsl:if>
		</p>
		<xsl:if test="not($one-line-text)">
			<xsl:if test="$attr-newline">
				<xsl:apply-templates select="@*" mode="attr-newline">
					<xsl:with-param name="class" select="$class"/>
				</xsl:apply-templates>
				<p class="code {$class}" style="padding-left: {$indent-size + 3 * $tab-size}px;">/&gt;</p>
			</xsl:if>
			<xdoc:doc>
				<p>process children recursively and close</p>
				<xsl:fallback/>
			</xdoc:doc>
			<xsl:if test="count(child::*|text()|comment()) &gt; 0">
				<xsl:apply-templates select="*|text()|comment()"/>
				<xsl:apply-templates select="self::node()" mode="close-element"/>
			</xsl:if>
		</xsl:if>
	</xsl:template>

	<xdoc:doc id="mode-get-class">
		<p>get <b>class</b>: elem-xsl or elem-html</p>
		<xsl:fallback/>
	</xdoc:doc>
	<xsl:template match="*" mode="get-class">
		<xsl:choose>
			<xsl:when test="starts-with(name(), 'xsl:')">elem-xsl</xsl:when>
			<xsl:otherwise>elem-html</xsl:otherwise>
			<!-- TODO: handle other namespaces -->
		</xsl:choose>
	</xsl:template>

	<xdoc:doc>
		<p>build <b>closing tag</b></p>
		<xsl:fallback/>
	</xdoc:doc>
	<xsl:template match="*" mode="close-element">
		<xsl:variable name="indent-size" select="count(ancestor::*) * $tab-size"/>
		<xsl:variable name="class">
			<xsl:apply-templates select="." mode="get-class"/>
		</xsl:variable>
		<p class="code {$class}" style="padding-left: {$indent-size}px;">
			<xsl:text>&lt;/</xsl:text>
			<xsl:value-of select="name()"/>
			<xsl:text>&gt;</xsl:text>
		</p>
	</xsl:template>

	<xdoc:doc>
		<p>process <b>comment</b> node</p>
		<xsl:fallback/>
	</xdoc:doc>
	<xsl:template match="comment()">
		<xdoc:doc>
			<p>only if there's an ancestor (don't display comments at root level)</p>
			<xsl:fallback/>
		</xdoc:doc>
		<xsl:if test="count(ancestor::*) &gt; 0">
			<xsl:variable name="content" select="normalize-space(.)"/>
			<xsl:if test="string($content)">
				<xsl:variable name="indent-size" select="count(ancestor::*) * $tab-size"/>
				<p class="code comment" style="padding-left: {$indent-size}px;">
					&lt;!-- <xsl:copy-of select="$content"/> --&gt;
				</p>
			</xsl:if>
		</xsl:if>
	</xsl:template>

	<xdoc:doc>
		<p>process <b>text</b> node</p>
		<xsl:fallback/>
	</xdoc:doc>
	<xsl:template match="text()">
		<xsl:variable name="content" select="normalize-space(.)"/>
		<xsl:if test="string($content)">
			<xsl:variable name="indent-size" select="count(ancestor::*) * $tab-size"/>
			<p class="code elem-xsl" style="padding-left: {$indent-size}px;">
				&lt;xsl:text&gt;<span class="text"><xsl:copy-of select="$content"/></span>&lt;/xsl:text&gt;
			</p>
		</xsl:if>
	</xsl:template>

	<xdoc:doc>
		<p>process <b>text</b> node on one line (called for text nodes that have no siblings)</p>
		<xsl:fallback/>
	</xdoc:doc>
	<xsl:template match="text()" mode="one-line-text">
		<span class="text">
			<xsl:value-of select="normalize-space(.)"/>
		</span>
		<xdoc:doc>
			<p>close element</p>
			<xsl:fallback/>
		</xdoc:doc>
		<xsl:text>&lt;/</xsl:text>
		<xsl:value-of select="name(..)"/>
		<xsl:text>&gt;</xsl:text>
	</xsl:template>

	<xdoc:doc>
		<p>display <b>attribute</b> in the format of <i>name="value"</i></p>
		<xsl:fallback/>
	</xdoc:doc>
	<xsl:template match="@*">
		<xsl:text> </xsl:text>
		<span class="attr-name">
			<xsl:value-of select="name()"/>
		</span>
		<xsl:text>="</xsl:text>
		<span class="attr-value">
			<xsl:value-of select="."/>
		</span>
		<xsl:text>"</xsl:text>
	</xsl:template>

	<xdoc:doc>
		<p>display <b>attribute</b> on a new line (used by <a href="#xo">xsl:output</a> template)</p>
		<xsl:fallback/>
	</xdoc:doc>
	<xsl:template match="@*" mode="attr-newline">
		<xsl:param name="class"/>
		<xdoc:doc>
			<p>add 2 extra indents</p>
			<xsl:fallback/>
		</xdoc:doc>
		<xsl:variable name="indent-size" select="(count(ancestor::*) + 2) * $tab-size"/>
		<p class="code {$class}" style="padding-left:{$indent-size}px;">
			<xsl:apply-templates select="self::node()"/>
		</p>
	</xsl:template>

	<xdoc:doc>
		<p>exclude <b>xsl:fallback</b> elements from output</p>
		<xsl:fallback/>
	</xdoc:doc>
	<xsl:template match="xsl:fallback"/>
	<!-- TODO check in saxon output if this works -->

	<xdoc:doc id="mode-head">
		<h3>xdoc templates</h3>
		<p>process <b>xdoc:head</b> element and its contents</p>
		<xsl:fallback/>
	</xdoc:doc>
	<xsl:template match="xdoc:head" mode="head">
		<div class="head">
			<xsl:apply-templates select="@title" mode="meta-title"/>
			<xsl:apply-templates select="@version" mode="meta-version"/>
			<xsl:if test="not(@version)">
				<xsl:apply-templates select="xdoc:history/xdoc:revision[1]/@version" mode="meta-version"/>
			</xsl:if>
			<xsl:apply-templates select="xdoc:author"/>
			<xsl:apply-templates select="@revised" mode="meta-revised"/>
			<xsl:if test="not(@revised)">
				<xsl:apply-templates select="xdoc:history/xdoc:revision[1]/@revised" mode="meta-revised"/>
			</xsl:if>
			<xsl:apply-templates select="xdoc:doc"/>
			<xsl:apply-templates select="xdoc:history" mode="link"/>
		</div>
	</xsl:template>

	<xdoc:doc id="mode-history">
		<p>display revision history from <b>xdoc:head</b></p>
		<xsl:fallback/>
	</xdoc:doc>
	<xsl:template match="xdoc:head" mode="history">
		<div class="head">
			<xsl:apply-templates select="xdoc:history"/>
		</div>
	</xsl:template>

	<xsl:template match="xdoc:head">
		<xdoc:doc id="head-elem">
			<p>do nothing (already handled in mode="<a href="#mode-head">head</a>")</p>
			<xsl:fallback/>
		</xdoc:doc>
	</xsl:template>

	<xsl:template match="xdoc:author">
		<p class="meta">Author: <xsl:value-of select="@name"/><xsl:apply-templates select="@e-mail"/></p>
	</xsl:template>

	<xsl:template match="xdoc:author/@e-mail">
		<xsl:text> [</xsl:text>
		<a href="mailto:{.}"><xsl:value-of select="."/></a>
		<xsl:text>]</xsl:text>
	</xsl:template>

	<xsl:template match="xdoc:history" mode="link">
		<p class="small"><a href="#revision-history">Revision history</a></p>
	</xsl:template>

	<xsl:template match="xdoc:history">
		<p class="small" id="revision-history">Revision history:</p>
		<table border="0" class="small">
			<tr>
				<th>version</th>
				<th>date</th>
				<th>description</th>
				<xsl:if test="xdoc:revision/@author">
					<th>author</th>
				</xsl:if>
			</tr>
			<xsl:apply-templates select="xdoc:revision">
				<xsl:sort select="@version" data-type="text" order="descending"/>
			</xsl:apply-templates>
		</table>
	</xsl:template>

	<xsl:template match="xdoc:revision">
		<tr>
			<td><xsl:value-of select="@version"/></td>
			<td><xsl:value-of select="@revised"/></td>
			<td><xsl:copy-of select="*"/></td>
			<xsl:if test="../xdoc:revision/@author">
				<td><xsl:apply-templates select="@author" mode="author-link"/></td>
			</xsl:if>
		</tr>
	</xsl:template>

	<xsl:template match="xdoc:*|@*" mode="author-link">
		<xsl:variable name="author" select="key('authors',.)"/>
		<xsl:choose>
			<xsl:when test="string($author/@name) and string($author/@e-mail)">
				<a title="{$author/@name}" href="mailto:{$author/@e-mail}"><xsl:value-of select="."/></a>
			</xsl:when>
			<xsl:when test="string($author/@name)">
				<acronym title="{$author/@name}"><xsl:value-of select="."/></acronym>
			</xsl:when>
			<xsl:when test="string($author/@e-mail)">
				<a href="mailto:{$author/@e-mail}"><xsl:value-of select="."/></a>
			</xsl:when>
			<xsl:otherwise><xsl:value-of select="."/></xsl:otherwise>
		</xsl:choose>
	</xsl:template>

	<xsl:template match="xdoc:*|@*" mode="meta-title">
		<h1><xsl:value-of select="."/></h1>
	</xsl:template>

	<xsl:template match="xdoc:*|@*" mode="meta-version">
		<p class="small">Version: <xsl:value-of select="."/></p>
	</xsl:template>

	<xsl:template match="xdoc:*|@*" mode="meta-revised">
		<p class="meta">Revised: <xsl:value-of select="."/></p>
	</xsl:template>

	<xdoc:doc>
		<p>process <b>xdoc:doc</b> element</p>
		<xsl:fallback/>
	</xdoc:doc>
	<xsl:template match="xdoc:doc">
		<xdoc:doc>
			<p>calculate <i>indent-size</i> for this line (in pixels; will be passed to CSS in style attribute)</p>
			<xsl:fallback/>
		</xdoc:doc>
		<xsl:variable name="indent-size" select="count(ancestor::*) * $tab-size"/>
		<xdoc:doc id="test-style-attr">
			<p>note that Mozilla does not display the style attribute values (cfr <a href="http://users.telenet.be/cking/webstuff/test/style-attribute/info.html">testcase</a>)</p>
			<xsl:fallback/>
		</xdoc:doc>
		<div class="doc" style="padding-left: {$indent-size}px;">
			<xsl:apply-templates select="." mode="doc-output"/>
		</div>
	</xsl:template>

	<xdoc:doc>
		<p>process <b>xdoc:doc</b> element inside <b>xdoc:head</b></p>
		<xsl:fallback/>
	</xdoc:doc>
	<xsl:template match="xdoc:head/xdoc:doc">
		<xsl:apply-templates select="." mode="doc-output"/>
	</xsl:template>

	<xdoc:doc id="doc-output">
		<p>output <b>xdoc:doc</b> element</p>
		<xsl:fallback/>
	</xdoc:doc>
	<xsl:template match="xdoc:doc" mode="doc-output">
			<xdoc:doc>
				<p>if there's an <i>id</i> attribute, use it in an <b>anchor</b></p>
				<xsl:fallback/>
			</xdoc:doc>
			<xsl:if test="string(@id)">
				<a id="{@id}"></a>
			</xsl:if>
			<xdoc:doc id="test-copy-of">
				<p>copy the contents of this element to the result tree</p>
				<xsl:fallback/>
			</xdoc:doc>
			<xsl:copy-of select="*"/>
	</xsl:template>

	<xsl:template name="footer">
		<xdoc:doc>
			<p>display <b>footer</b> with xdoc link</p>
			<xsl:fallback/>
		</xdoc:doc>
		<p class="footer" id="footer">XSLT stylesheet documentation generated by <a href="{$base-path}">xdoc</a>&#0160;&#0160;&#0169;cking 2004</p>
	</xsl:template>

</xsl:stylesheet>

