]> git.draconx.ca Git - homepage.git/blob - layouts/default.xsl
d7381d5dc5a3ace18fdaa400b2a45ebae917e844
[homepage.git] / layouts / default.xsl
1 <?xml version='1.0' encoding='UTF-8' ?>
2 <!--
3   Nick's web site: XHTML output stage
4
5   Copyright © 2018-2019 Nick Bowler
6
7   This program is free software: you can redistribute it and/or modify
8   it under the terms of the GNU General Public License as published by
9   the Free Software Foundation, either version 3 of the License, or
10   (at your option) any later version.
11
12   This program is distributed in the hope that it will be useful,
13   but WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   GNU General Public License for more details.
16
17   You should have received a copy of the GNU General Public License
18   along with this program.  If not, see <https://www.gnu.org/licenses/>
19 -->
20 <xsl:stylesheet version='1.0'
21   xmlns='http://www.w3.org/1999/xhtml'
22   xmlns:xhtml='http://www.w3.org/1999/xhtml'
23   xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
24   xmlns:func='http://exslt.org/functions'
25   xmlns:f='http://draconx.ca/my-functions'
26   extension-element-prefixes='func f'
27   exclude-result-prefixes='xhtml'>
28
29 <xsl:import href='layouts/functions.xsl' />
30
31 <xsl:output method='xml' encoding='UTF-8' indent='yes'
32   doctype-public='-//W3C//DTD XHTML 1.1//EN'
33   doctype-system='http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd' />
34
35 <xsl:param name='source-uri'
36   select='"//git.draconx.ca/gitweb/homepage.git/blob/"' />
37 <xsl:param name='site-title' select='"The Citrine Citadel"' />
38
39 <func:function name='f:ends-with'>
40   <xsl:param name='a' />
41   <xsl:param name='b' />
42   <func:result
43     select='substring($a, string-length($a)-string-length($b)+1)=$b' />
44 </func:function>
45
46 <xsl:template match='node()|@*'>
47   <xsl:copy><xsl:apply-templates select='node()|@*' /></xsl:copy>
48 </xsl:template>
49
50 <!--
51   Nokogiri's pretty-printer is a bit weird.  Regardless of the indentation
52   setting, if an element has no child text nodes then it will be pretty-
53   printed.  This works by adding arbitrary whitespace to that element, and
54   then all of its children are eligible to be pretty-printed.
55
56   If an element has any text nodes at all, then it is not pretty-printed and
57   neither are any of its descendents.
58
59   Adding arbitrary whitespace to <pre> is bad, so we inject zero-width non-
60   breaking spaces to prevent this.  This will render fine but the spaces
61   should be removed before final output to avoid problems with copy+paste.
62 -->
63 <xsl:template match='xhtml:pre'>
64   <xsl:copy>
65     <xsl:apply-templates select='node()|@*' />
66     <xsl:text>&#x2060;</xsl:text>
67   </xsl:copy>
68 </xsl:template>
69
70 <!--
71   Likewise, adding spaces between consecutive span-level elements where
72   none existed before won't go over well.
73 -->
74 <xsl:template name='glue-preceding-span'>
75   <xsl:if test='f:element-is-span(preceding-sibling::node()[1])'>
76     <xsl:text>&#x2060;</xsl:text>
77   </xsl:if>
78 </xsl:template>
79
80 <xsl:template match='*[f:element-is-span()]'>
81   <xsl:call-template name='glue-preceding-span' />
82   <xsl:copy>
83     <xsl:apply-templates select='node()|@*' />
84     <xsl:text>&#x2060;</xsl:text> <!-- avoid breaking within a span element -->
85   </xsl:copy>
86 </xsl:template>
87
88 <!--
89   Manually strip whitespace-only text nodes so the pretty printer can do its
90   thing on remaining elements.
91 -->
92 <xsl:template match='text()[normalize-space(.) = ""]'>
93   <xsl:choose>
94     <!-- preserve anything according to xml:space -->
95     <xsl:when test='ancestor::*[@xml:space][1][@xml:space="preserve"]'>
96       <xsl:copy />
97     </xsl:when>
98     <!-- preserve anything under <pre> -->
99     <xsl:when test='ancestor::xhtml:pre'><xsl:copy /></xsl:when>
100   </xsl:choose>
101 </xsl:template>
102
103 <!-- Add rel attributes to external links -->
104 <xsl:template match='xhtml:a[starts-with(@href,"http://")
105                           or starts-with(@href,"https://")
106                           or starts-with(@href,"//")]'>
107   <xsl:variable name='domain'
108     select='substring-before(
109               concat(translate(substring-after(@href, "//"), ":", "/"), "/"),
110               "/")' />
111
112   <xsl:copy>
113     <xsl:apply-templates select='@*' />
114     <xsl:if test='not($domain="draconx.ca"
115                       or f:ends-with($domain, ".draconx.ca"))'>
116       <xsl:attribute name='rel'>
117         <xsl:if test='@rel'>
118           <xsl:value-of select='@rel' />
119           <xsl:text> </xsl:text>
120         </xsl:if>
121         <xsl:text>external noopener noreferrer</xsl:text>
122       </xsl:attribute>
123     </xsl:if>
124     <xsl:apply-templates select='node()' />
125   </xsl:copy>
126 </xsl:template>
127
128 <xsl:template match='xhtml:h2[@id]'>
129   <xsl:variable name='fragment' select='concat("#", @id)' />
130   <xsl:copy>
131     <xsl:apply-templates select='node()|@*' />
132     <xsl:text> </xsl:text>
133     <small class='permalink'>
134       (<a href='{$fragment}'><xsl:value-of select='$fragment' /></a>)
135     </small>
136   </xsl:copy>
137 </xsl:template>
138
139 <xsl:template match='copyright'>
140   <p>
141     <xsl:text>Copyright © </xsl:text>
142     <xsl:value-of select='text()' />
143     <xsl:text>.</xsl:text>
144   </p>
145 </xsl:template>
146
147 <xsl:template match='license'>
148   <p>
149     <xsl:text>Copying and distribution of this material</xsl:text>
150     <xsl:if test='normalize-space(modification-allowed)="yes"'>
151       <xsl:text>, with or without modification,</xsl:text>
152     </xsl:if>
153     <xsl:text> is permitted under the terms of the </xsl:text>
154     <a rel='license'>
155       <xsl:attribute name='href'>
156         <xsl:value-of select='normalize-space(uri)' />
157       </xsl:attribute>
158       <xsl:value-of select='name' />
159     </a>
160     <xsl:text>.</xsl:text>
161   </p>
162 </xsl:template>
163
164 <xsl:template match='source'>
165   <p>
166     <xsl:text>This document was compiled from </xsl:text>
167     <a href='{concat($source-uri, revision, ":", file)}'>
168       <xsl:value-of select='file' />
169     </a>
170     <xsl:text> on </xsl:text>
171     <xsl:value-of select='compiletime' />
172     <xsl:text>.</xsl:text>
173   </p>
174 </xsl:template>
175
176 <xsl:template match='/'>
177   <html>
178     <head>
179       <meta name='viewport' content='width=device-width, initial-scale=1' />
180       <link rel='stylesheet' type='text/css' href='/style.css' />
181       <title>
182         <xsl:variable name='page-title' select='string(/document/title)' />
183         <xsl:if test='$page-title and $site-title != $page-title'>
184           <xsl:value-of select='concat($page-title, " – ")' />
185         </xsl:if>
186         <xsl:value-of select='$site-title' />
187       </title>
188     </head>
189     <body>
190       <xsl:apply-templates select='/document/xhtml:html/@*' />
191       <xsl:apply-templates select='/document/xhtml:html/node()' />
192
193       <hr />
194       <div id='footer'>
195         <xsl:apply-templates select='/document/copyright' />
196         <xsl:apply-templates select='/document/license' />
197         <xsl:apply-templates select='/document/source' />
198       </div>
199     </body>
200   </html>
201 </xsl:template>
202
203 </xsl:stylesheet>