2 This is an extremely simple XSLT transform which simply reads in a
5 • Replaces the `<html:shrine-content>` element with the content of the document it is being applied to,
7 • If the root element of the document it is being applied to has a `@data-shrine-header` attribute, replaces the `<html:shrine-header>` with the contents of the corresponding header file (`⸺-header.xml`), and
9 • If the root element of the document it is being applied to has a `@data-shrine-footer` attribute, replaces the `<html:shrine-footer>` with the contents of the corresponding footer file (`⸺-footer.xml`), and
11 • Copies any remaining `@lang` or `@data-*` attributes from the root element of the document it is being applied to over to the root element of the template.
13 The intent is that this file is used in conjunction with a Makefile to quickly automate inserting header and footer content into documents.
14 The exact feature·set might be somewhat more expansive than the description above; for a lengthier overview of what this file does, see `README.markdown`.
16 Feel free to add additional templates and features to suit your needs!
20 © 2022 Lady [@ Lady’s Computer]
22 This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
23 If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
26 xmlns:atom="http://www.w3.org/2005/Atom"
27 xmlns:html="http://www.w3.org/1999/xhtml"
28 xmlns:xslt="http://www.w3.org/1999/XSL/Transform"
31 <xslt:param name="BASEIRI" select="'http://example.com'"/>
32 <xslt:param name="DATETIME" select="'1972-12-31T00:00:00Z'"/>
33 <xslt:param name="OUTPUTPATH" select="'/unknown'"/>
34 <xslt:variable name="baseiri">
36 <xslt:when test="contains($BASEIRI, '://')"> <!-- there is an authority -->
37 <xslt:variable name="noscheme" select="substring-after($BASEIRI, '://')"/>
38 <xslt:value-of select="substring-before($BASEIRI, '://')"/>
39 <xslt:text>://</xslt:text>
41 <xslt:when test="contains($noscheme, '/')">
42 <xslt:value-of select="substring-before($noscheme, '/')"/>
45 <xslt:value-of select="$noscheme"/>
50 <xslt:value-of select="substring-before($BASEIRI, ':')"/>
54 <xslt:variable name="datetime" select="string($DATETIME)"/>
55 <xslt:variable name="outputpath">
56 <xslt:if test="not(starts-with($OUTPUTPATH, '/'))">
57 <xslt:text>/</xslt:text>
59 <xslt:value-of select="$OUTPUTPATH"/>
61 <xslt:variable name="source" select="current()"/>
62 <xslt:variable name="template" select="document('./template.xml')"/>
65 Instead of actually processing the root node, process the template in `template` mode.
67 <xslt:template match="/">
69 <xslt:when test="atom:feed">
70 <xslt:apply-templates mode="feed"/>
73 <xslt:apply-templates select="$template" mode="template"/>
79 Process non‐template elements.
80 By default, just copy the element, but remove any `@data-shrine-*` attribuets or `@slot` attributes with a value that begins with `shrine-`.
81 Some elements may have special treatment.
83 <xslt:template match="*" mode="content">
84 <xslt:element name="{local-name()}">
85 <xslt:for-each select="@*[not(starts-with(name(), 'data-shrine-')) and not(name()='slot' and starts-with(., 'shrine-'))]">
88 <xslt:for-each select="*|text()">
90 <xslt:when test="@slot[starts-with(., 'shrine-')]">
92 <text> placeholder for slotted element </text>
96 <xslt:apply-templates select="." mode="content"/>
104 Process text content; just make a copy.
106 <xslt:template match="text()" mode="content">
111 Process template elements.
112 By default, just copy the element.
113 This behaviour will be overridden for certain elements to insert the page content.
115 <xslt:template match="*" mode="template">
116 <xslt:element name="{local-name()}">
117 <xslt:for-each select="@*">
120 <xslt:apply-templates mode="template"/>
125 Process template text; just make a copy.
127 <xslt:template match="text()" mode="content">
132 Process the template `<html>` elements.
133 This copies over `@lang` and non‐shrine `@data-*` attributes from the root node.
135 <xslt:template match="html:html" mode="template">
137 <xslt:for-each select="@*[not(starts-with(name(), 'xmlns'))]">
140 <xslt:for-each select="$source/*/@*[name()='lang' or starts-with(name(), 'data-') and not(starts-with(name(), 'data-shrine-'))]">
141 <xslt:if test="not($template/*/@*[name()=name(current())])">
145 <xslt:apply-templates mode="template"/>
150 Process the template `<head>` elements.
151 This inserts appropriate metadata based on the document.
153 <xslt:template match="html:head" mode="template">
155 <xslt:for-each select="@*">
158 <xslt:if test="not($source//html:title[@slot='shrine-head'])">
160 <xslt:apply-templates select="$source//html:h1" mode="text"/>
163 <meta name="generator" content="https://github.com/marrus-sh/shrine-xslt"/>
164 <xslt:apply-templates mode="template"/>
165 <xslt:for-each select="$source//*[@slot='shrine-head']">
166 <xslt:apply-templates select="." mode="content"/>
172 Process the template header.
173 Read the `@data-header` attribute of the root element, append `"-header.xml"` to the end of it, and process the resulting document.
174 If no `@data-header` attribute is provided, no header is rendered.
176 <xslt:template match="html:shrine-header" mode="template">
177 <xslt:for-each select="$source/*/@data-shrine-header">
178 <xslt:apply-templates select="document(concat('./', ., '-header.xml'), $template)/*" mode="content"/>
183 Process the template footer.
184 Read the `@data-footer` attribute of the root element, append `"-footer.xml"` to the end of it, and process the resulting document.
185 If no `@data-footer` attribute is provided, no footer is rendered.
187 <xslt:template match="html:shrine-footer" mode="template">
188 <xslt:for-each select="$source/*/@data-shrine-footer">
189 <xslt:apply-templates select="document(concat('./', ., '-footer.xml'), $template)/*" mode="content"/>
196 <xslt:template match="html:shrine-content" mode="template">
197 <xslt:apply-templates select="$source/*" mode="content"/>
201 Process feed elements and text.
202 By default, just make a copy.
203 This behaviour will be overridden for certain elements to generate feed metadata.
205 <xslt:template match="*|text()" mode="feed">
207 <xslt:for-each select="@*">
210 <xslt:apply-templates mode="feed"/>
215 Process the root feed element.
216 This adds required metadata when it has not been provided in the source X·M·L.
218 <xslt:template match="atom:feed" mode="feed">
219 <xslt:text>
</xslt:text> <!-- ensure a newline between the doctype and the feed element -->
220 <feed xmlns="http://www.w3.org/2005/Atom">
221 <xslt:for-each select="@*">
224 <xslt:apply-templates select="text()[following-sibling::*[not(self::atom:entry)]]|*[not(self::atom:entry)]" mode="feed"/>
225 <xslt:if test="not(atom:id)">
226 <xslt:text>
	</xslt:text>
229 <xslt:when test="contains($baseiri, '://')">
230 <xslt:text>oai:</xslt:text>
231 <xslt:value-of select="substring-after($baseiri, '://')"/>
232 <xslt:text>:</xslt:text>
235 <xslt:value-of select="$baseiri"/>
236 <xslt:text>/</xslt:text>
239 <xslt:value-of select="$outputpath"/>
242 <xslt:if test="not(atom:title)">
243 <xslt:text>
	</xslt:text>
244 <title xml:lang="en">Untitled</title>
246 <xslt:if test="not(atom:author)">
247 <xslt:text>
	</xslt:text>
249 <name xml:lang="en">Anonymous</name>
252 <xslt:if test="not(atom:link[@rel='alternate'][@type='text/html'])">
253 <xslt:text>
	</xslt:text>
254 <link rel="alternate" type="text/html" href="{$baseiri}/"/>
256 <xslt:if test="not(atom:link[@rel='self'])">
257 <xslt:text>
	</xslt:text>
258 <link rel="self" type="application/atom+xml" href="{$baseiri}{$outputpath}"/>
260 <xslt:if test="not(atom:updated)">
261 <xslt:text>
	</xslt:text>
263 <xslt:value-of select="$datetime"/>
266 <xslt:if test="not(atom:generator)">
267 <xslt:text>
	</xslt:text>
268 <generator uri="https://github.com/marrus-sh/shrine-xslt">shrine-xslt</generator>
270 <xslt:apply-templates select="atom:entry" mode="feed"/>
271 <xslt:text>
</xslt:text> <!-- newline before close tag -->
276 Process feed entry elements.
278 <xslt:template match="atom:entry[atom:link[@rel='alternate'][starts-with(@href, '/')][@type='text/html']]" mode="feed">
279 <xslt:variable name="entryhref" select="atom:link[@rel='alternate'][starts-with(@href, '/')][@type='text/html'][1]/@href"/>
280 <xslt:text>
	</xslt:text>
281 <entry xmlns="http://www.w3.org/2005/Atom">
282 <xslt:for-each select="@*">
285 <xslt:apply-templates select="text()[following-sibling::*]|*" mode="feed"/>
286 <xslt:if test="not(atom:id)">
287 <xslt:text>
		</xslt:text>
290 <xslt:when test="contains($baseiri, '://')">
291 <xslt:text>oai:</xslt:text>
292 <xslt:value-of select="substring-after($baseiri, '://')"/>
293 <xslt:text>:</xslt:text>
296 <xslt:value-of select="$baseiri"/>
297 <xslt:text>/</xslt:text>
300 <xslt:value-of select="$entryhref"/>
303 <xslt:if test="not(atom:title)">
304 <xslt:text>
		</xslt:text>
305 <title xml:lang="en">Untitled</title>
307 <xslt:if test="not(atom:updated)">
308 <xslt:text>
		</xslt:text>
310 <xslt:value-of select="$datetime"/>
313 <xslt:text>
	</xslt:text> <!-- newline before close tag -->
318 Process feed link elements.
319 This simply rewrites absolute links to be relative.
321 <xslt:template match="atom:link[starts-with(@href, '/')]" mode="feed">
322 <link xmlns="http://www.w3.org/2005/Atom" rel="{@rel}" href="{$baseiri}{@href}">
323 <xslt:for-each select="@*[local-name()!='rel' and local-name()!='href']">
330 Provide the complete text content of the provided element.
332 <xslt:template match="*|text()" mode="text">
334 <xslt:when test="self::*">
335 <xslt:apply-templates mode="text"/>
337 <xslt:when test="self::text()">
345 Note that this relies on “default” output method detection specified in X·S·L·T in order to work.
346 The `about:legacy-compat` system doctype is for H·T·M·L compatibility but is harmless in X·M·L.
348 <xslt:output charset="UTF-8" doctype-system="about:legacy-compat" indent="no"/>