]> git.draconx.ca Git - homepage.git/blobdiff - layouts/default.xsl
Strip leading and trailing whitespace from all <p> tags.
[homepage.git] / layouts / default.xsl
index 67b7e9199484f6daf67304c4e8eb28149bcc17a3..020d1fdc61bd89728a910eb97587f052345eaf89 100644 (file)
@@ -1,8 +1,8 @@
 <?xml version='1.0' encoding='UTF-8' ?>
 <!--
-  Nick's web site: Final XHTML output stage
+  Nick's web site: XHTML output stage
 
-  Copyright © 2018 Nick Bowler
+  Copyright © 2018-2019 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
   xmlns='http://www.w3.org/1999/xhtml'
   xmlns:xhtml='http://www.w3.org/1999/xhtml'
   xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
+  xmlns:func='http://exslt.org/functions'
+  xmlns:f='http://draconx.ca/my-functions'
+  extension-element-prefixes='func f'
   exclude-result-prefixes='xhtml'>
 
+<xsl:import href='layouts/functions.xsl' />
+
 <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' />
-<xsl:strip-space elements='*' />
 
 <xsl:param name='source-uri'
   select='"//git.draconx.ca/gitweb/homepage.git/blob/"' />
 <xsl:param name='site-title' select='"The Citrine Citadel"' />
 
+<func:function name='f:ends-with'>
+  <xsl:param name='a' />
+  <xsl:param name='b' />
+  <func:result
+    select='substring($a, string-length($a)-string-length($b)+1)=$b' />
+</func:function>
+
 <xsl:template match='node()|@*'>
   <xsl:copy><xsl:apply-templates select='node()|@*' /></xsl:copy>
 </xsl:template>
 
+<!--
+  Nokogiri's pretty-printer is a bit weird.  Regardless of the indentation
+  setting, if an element has no child text nodes then it will be pretty-
+  printed.  This works by adding arbitrary whitespace to that element, and
+  then all of its children are eligible to be pretty-printed.
+
+  If an element has any text nodes at all, then it is not pretty-printed and
+  neither are any of its descendents.
+
+  Adding arbitrary whitespace to <pre> is bad, so we inject zero-width non-
+  breaking spaces to prevent this.  This will render fine but the spaces
+  should be removed before final output to avoid problems with copy+paste.
+-->
+<xsl:template match='xhtml:pre'>
+  <xsl:copy>
+    <xsl:apply-templates select='node()|@*' />
+    <xsl:text>&#x2060;</xsl:text>
+  </xsl:copy>
+</xsl:template>
+
+<!--
+  Likewise, adding spaces between consecutive span-level elements where
+  none existed before won't go over well.
+-->
+<xsl:template name='glue-preceding-span'>
+  <xsl:if test='f:element-is-span(preceding-sibling::node()[1])'>
+    <xsl:text>&#x2060;</xsl:text>
+  </xsl:if>
+</xsl:template>
+
+<xsl:template match='*[f:element-is-span()]'>
+  <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:copy>
+</xsl:template>
+
+<!--
+  Manually strip whitespace-only text nodes so the pretty printer can do its
+  thing on remaining elements.
+-->
+<xsl:template match='text()[normalize-space(.) = ""]'>
+  <xsl:choose>
+    <!-- preserve anything according to xml:space -->
+    <xsl:when test='ancestor::*[@xml:space][1][@xml:space="preserve"]'>
+      <xsl:copy />
+    </xsl:when>
+    <!-- preserve anything under <pre> -->
+    <xsl:when test='ancestor::xhtml:pre'><xsl:copy /></xsl:when>
+  </xsl:choose>
+</xsl:template>
+
+<!-- Clean up whitespace where harmless to do so -->
+<xsl:template match='xhtml:p/node()[1][self::text()]'>
+  <xsl:value-of select='f:strip-leading()' />
+</xsl:template>
+<xsl:template match='xhtml:p/node()[position()=last()][self::text()]'>
+  <xsl:value-of select='f:strip-trailing()' />
+</xsl:template>
+
+<!-- Add rel attributes to external links -->
+<xsl:template match='xhtml:a[starts-with(@href,"http://")
+                          or starts-with(@href,"https://")
+                          or starts-with(@href,"//")]'>
+  <xsl:variable name='domain'
+    select='substring-before(
+              concat(translate(substring-after(@href, "//"), ":", "/"), "/"),
+              "/")' />
+
+  <xsl:copy>
+    <xsl:apply-templates select='@*' />
+    <xsl:if test='not($domain="draconx.ca"
+                      or f:ends-with($domain, ".draconx.ca"))'>
+      <xsl:attribute name='rel'>
+        <xsl:if test='@rel'>
+          <xsl:value-of select='@rel' />
+          <xsl:text> </xsl:text>
+        </xsl:if>
+        <xsl:text>external noopener noreferrer</xsl:text>
+      </xsl:attribute>
+    </xsl:if>
+    <xsl:apply-templates select='node()' />
+  </xsl:copy>
+</xsl:template>
+
+<xsl:template match='xhtml:h2[@id]'>
+  <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:copy>
+</xsl:template>
+
+<xsl:template match='copyright'>
+  <p>
+    <xsl:text>Copyright © </xsl:text>
+    <xsl:value-of select='text()' />
+    <xsl:text>.</xsl:text>
+  </p>
+</xsl:template>
+
+<xsl:template match='license'>
+  <p>
+    <xsl:text>Copying and distribution of this material</xsl:text>
+    <xsl:if test='normalize-space(modification-allowed)="yes"'>
+      <xsl:text>, with or without modification,</xsl:text>
+    </xsl:if>
+    <xsl:text> is permitted under the terms of the </xsl:text>
+    <a rel='license'>
+      <xsl:attribute name='href'>
+        <xsl:value-of select='normalize-space(uri)' />
+      </xsl:attribute>
+      <xsl:value-of select='name' />
+    </a>
+    <xsl:text>.</xsl:text>
+  </p>
+</xsl:template>
+
 <xsl:template match='source'>
   <p>
     <xsl:text>This document was compiled from </xsl:text>
       <xsl:apply-templates select='/document/xhtml:html/@*' />
       <xsl:apply-templates select='/document/xhtml:html/node()' />
 
+      <hr />
       <div id='footer'>
+        <xsl:apply-templates select='/document/copyright' />
+        <xsl:apply-templates select='/document/license' />
         <xsl:apply-templates select='/document/source' />
       </div>
     </body>