]> git.draconx.ca Git - homepage.git/blobdiff - layouts/default.xsl
Improve clicky table generation.
[homepage.git] / layouts / default.xsl
index 020d1fdc61bd89728a910eb97587f052345eaf89..5a6c3eec06785191225ba8cfb0d93809e4ee42ad 100644 (file)
@@ -2,7 +2,7 @@
 <!--
   Nick's web site: XHTML output stage
 
-  Copyright © 2018-2019 Nick Bowler
+  Copyright © 2018-2021 Nick Bowler
 
   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
 
 <xsl:output method='xml' encoding='UTF-8' indent='yes'
   doctype-public='-//W3C//DTD XHTML 1.1//EN'
-  doctype-system='http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd' />
+  doctype-system='http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'
+  cdata-section-elements='style script' />
 
 <xsl:param name='source-uri'
-  select='"//git.draconx.ca/gitweb/homepage.git/blob/"' />
+  select='"//git.draconx.ca/gitweb/homepage.git/"' />
 <xsl:param name='site-title' select='"The Citrine Citadel"' />
+<xsl:param name='section-links' select='//document/section-links' />
 
 <func:function name='f:ends-with'>
   <xsl:param name='a' />
   <xsl:call-template name='glue-preceding-span' />
   <xsl:copy>
     <xsl:apply-templates select='node()|@*' />
-    <xsl:text>&#x2060;</xsl:text> <!-- avoid breaking within a span element -->
+    <xsl:if test='*'>
+      <!-- avoid breaking within a span element -->
+      <xsl:text>&#x2060;</xsl:text>
+    </xsl:if>
   </xsl:copy>
 </xsl:template>
 
     </xsl:when>
     <!-- preserve anything under <pre> -->
     <xsl:when test='ancestor::xhtml:pre'><xsl:copy /></xsl:when>
+    <!-- preserve whitespace which is the only child node of an element -->
+    <xsl:when test='count(../node()) = 1'><xsl:copy /></xsl:when>
+    <!-- preserve whitespace between consecutive span-level elements
+         which have at least one non-whitespace sibling text element -->
+    <xsl:when test='f:element-is-span(preceding-sibling::node()[1])
+                    and f:element-is-span(following-sibling::node()[1])
+                    and ../text()[normalize-space(.) != ""]'>
+      <xsl:copy />
+    </xsl:when>
   </xsl:choose>
 </xsl:template>
 
   <xsl:variable name='fragment' select='concat("#", @id)' />
   <xsl:copy>
     <xsl:apply-templates select='node()|@*' />
-    <xsl:text> </xsl:text>
-    <small class='permalink'>
-      (<a href='{$fragment}'><xsl:value-of select='$fragment' /></a>)
-    </small>
+    <xsl:if test='$section-links = "yes"'>
+      <xsl:text> </xsl:text>
+      <small class='permalink'>
+        (<a href='{$fragment}'><xsl:value-of select='$fragment' /></a>)
+      </small>
+    </xsl:if>
   </xsl:copy>
 </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>&#x2060;<code><xsl:value-of select='f:strip-leading(.)' /></code></pre>
+</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>&#x2060;<code>
+    <xsl:value-of select='f:xhtml-listing(//xhtml:*[@id=$target])' />
+  </code></pre>
+</xsl:template>
+
 <xsl:template match='copyright'>
   <p>
     <xsl:text>Copyright © </xsl:text>
   </p>
 </xsl:template>
 
+<func:function name='f:matching-child'>
+  <xsl:param name='child' select='./copyright-holder' />
+  <xsl:param name='node' select='.' />
+  <xsl:param name='nodeset' select='$node/../*[name()=name($node)]' />
+
+  <func:result select='$nodeset[*[name()=name($child)]=$child]' />
+</func:function>
+
+<func:function name='f:attribution-order'>
+  <xsl:param name='a' />
+  <xsl:param name='b' />
+
+  <xsl:variable name='docmatch'
+    select='number($a/copyright-holder = /document/copyright-holder)
+            - number($b/copyright-holder = /document/copyright-holder)' />
+
+  <xsl:variable name='authmatch'
+    select='count(f:matching-child($a/copyright-holder, $a))
+            - count(f:matching-child($b/copyright-holder, $b))' />
+
+  <xsl:variable name='licmatch'
+    select='count(f:matching-child($a/license, $a))
+            - count(f:matching-child($b/license, $b))' />
+
+  <xsl:choose>
+    <xsl:when test='$docmatch'><func:result select='$docmatch' /></xsl:when>
+    <xsl:when test='$authmatch'><func:result select='$authmatch' /></xsl:when>
+    <xsl:when test='$licmatch'><func:result select='$licmatch' /></xsl:when>
+    <xsl:otherwise><func:result select='"nope"' /></xsl:otherwise>
+  </xsl:choose>
+</func:function>
+
+<xsl:template match='image/license'>
+  <xsl:text>, </xsl:text>
+  <a href='{uri}' rel='license'><xsl:value-of select='shortname' /></a>
+</xsl:template>
+
+<xsl:template match='image'>
+  <xsl:choose>
+    <xsl:when test='position() = 1'>, except </xsl:when>
+    <xsl:when test='position() = last()'> and </xsl:when>
+    <xsl:otherwise>, </xsl:otherwise>
+  </xsl:choose>
+  <a href='{uri}'><xsl:value-of select='title' /></a>
+  <xsl:text> © </xsl:text>
+  <xsl:value-of select='copyright' />
+  <xsl:apply-templates select='license' />
+</xsl:template>
+
+<xsl:template name='image-attribution'>
+<!--
+  <xsl:variable name='x' select='/document/image[copyright-holder="Nick Bowler"][1]' />
+  <xsl:variable name='y' select='/document/image[copyright-holder="Nick Bowler"][4]' />
+-->
+  <xsl:variable name='images-fragment'>
+    <xsl:for-each select='/document/image'>
+      <xsl:sort select='number(copyright-holder = /document/copyright-holder)'
+                data-type='number' order='descending' />
+      <xsl:sort select='count(f:matching-child(copyright-holder))'
+                data-type='number' order='descending' />
+      <xsl:sort select='copyright-holder' order='descending' />
+      <xsl:sort select='count(f:matching-child(license))'
+                data-type='number' order='descending' />
+      <xsl:sort select='license/identifier' order='descending' />
+
+      <xsl:call-template name='notransform' />
+    </xsl:for-each>
+  </xsl:variable>
+  <xsl:variable name='images' select='exslt:node-set($images-fragment)/*' />
+
+  <xsl:variable name='abbrev-split'
+      select='count($images[copyright-holder = $images[1]/copyright-holder
+                 and license/identifier = $images[1]/license/identifier])' />
+
+  <xsl:variable name='abbrev-years-fragment'>
+    <xsl:for-each select='$images[$abbrev-split >= position()]/copyright-year'>
+      <xsl:sort data-type='number' />
+      <copyright-year><xsl:value-of select='.' /></copyright-year>
+    </xsl:for-each>
+  </xsl:variable>
+  <xsl:variable name='abbrev-years'
+    select='exslt:node-set($abbrev-years-fragment)/*' />
+
+  <p>
+    <xsl:text>Images © </xsl:text>
+    <xsl:value-of select='$abbrev-years[1]' />
+    <xsl:if test='$abbrev-years[last()] != $abbrev-years[1]'>
+      <xsl:value-of select='concat("–", $abbrev-years[last()])' />
+    </xsl:if>
+    <xsl:value-of select='concat(" ", $images[1]/copyright-holder)' />
+    <xsl:apply-templates select='$images[1]/license' />
+    <xsl:apply-templates select='$images[position() > $abbrev-split]' />
+    <xsl:text>.</xsl:text>
+  </p>
+</xsl:template>
+
 <xsl:template match='source'>
   <p>
-    <xsl:text>This document was compiled from </xsl:text>
-    <a href='{concat($source-uri, revision, ":", file)}'>
-      <xsl:value-of select='file' />
-    </a>
+    <xsl:text>This document was compiled</xsl:text>
+    <xsl:choose>
+      <xsl:when test='file'>
+        <xsl:text> from </xsl:text>
+        <a href='{concat($source-uri, "blob/", revision, ":", file)}'>
+          <xsl:value-of select='file' />
+        </a>
+      </xsl:when>
+      <xsl:when test='dir'>
+        <xsl:text> from </xsl:text>
+        <a href='{concat($source-uri, "tree/", revision, ":", dir)}'>
+          <xsl:value-of select='dir' />
+        </a>
+      </xsl:when>
+    </xsl:choose>
     <xsl:text> on </xsl:text>
     <xsl:value-of select='compiletime' />
     <xsl:text>.</xsl:text>
   </p>
 </xsl:template>
 
+<xsl:template match='xhtml:h1[not(preceding::xhtml:h1)]'>
+  <xsl:copy><xsl:apply-templates select='node()|@*' /></xsl:copy>
+  <xsl:if test='/document/article/published'>
+    <div id='article-info'>
+      <p>
+        <xsl:text>Posted </xsl:text>
+        <xsl:value-of select='/document/article/published' />
+      </p>
+    </div>
+  </xsl:if>
+</xsl:template>
+
 <xsl:template match='/'>
   <html>
     <head>
       <meta name='viewport' content='width=device-width, initial-scale=1' />
       <link rel='stylesheet' type='text/css' href='/style.css' />
+      <link rel="icon" href="data:," />
       <title>
         <xsl:variable name='page-title' select='string(/document/title)' />
         <xsl:if test='$page-title and $site-title != $page-title'>
         </xsl:if>
         <xsl:value-of select='$site-title' />
       </title>
+      <!-- Hoist all style elements to <head> as required by the doctype. -->
+      <xsl:for-each select='//xhtml:style'>
+        <xsl:copy><xsl:apply-templates select='node()|@*' /></xsl:copy>
+      </xsl:for-each>
     </head>
     <body>
       <xsl:apply-templates select='/document/xhtml:html/@*' />
+
+      <xsl:if test='/document/hierarchy/parent'>
+        <p id='sitetitle'>
+          <small><xsl:value-of select='$site-title' /></small>
+        </p>
+        <div id='breadcrumbs'>
+          <strong>Return to: </strong>
+          <ul>
+            <xsl:for-each select='/document/hierarchy/parent'>
+              <li><a href='{uri}'><xsl:value-of select='name'/></a></li>
+            </xsl:for-each>
+          </ul>
+        </div>
+        <hr />
+      </xsl:if>
+
       <xsl:apply-templates select='/document/xhtml:html/node()' />
 
       <hr />
   </html>
 </xsl:template>
 
+<xsl:include href='layouts/clickytable.xsl' />
+
 </xsl:stylesheet>