From: Lady <redacted>
Date: Sat, 24 Dec 2022 01:23:42 +0000 (-0800)
Subject: Support more slots, including custom slotting
X-Git-Tag: 0.3.1~1
X-Git-Url: https://git.ladys.computer/Shrine-XSLT/commitdiff_plain/b165654fb546b6262512b33a590b2dacf00eb92c

Support more slots, including custom slotting
---

diff --git a/README.markdown b/README.markdown
index 0abeeb6..00dd2b5 100644
--- a/README.markdown
+++ b/README.markdown
@@ -100,10 +100,21 @@ There’s an example `feed.atom` provided so you can see what this
   (`<html>`) element of the template, as will `@lang` and `@xml:lang`.
   You can use this to help configure page‐specific styling.
 
-- You can insert content into the `<head>` of the template by setting
-  `@slot="shrine-head"` on the appropriate elements. For example, one
-  might customize the title of a page like
-  `<title slot="shrine-head">My Title | My Cool Shrine</title>`.
+- You can use the `@slot` attribute with a few special values to insert
+  content in various places :—
+
+  - `@slot="shrine-head"` will place the content into the `<head>` of
+    the resulting document. This is especially useful for `<title>`,
+    `<meta>`, and `<style>` elements.
+
+  - For `shrine-header` and `shrine-footer`, there are `-before` and
+    `-after` slot names which will place content into the beginning or
+    ending of the shrine header or footer, respectively.
+
+  - You can define your own slots with `<slot>` in templates, headers,
+    and footers; this will enable a corresponding `@slot` name
+    beginning with `shrine-template-slot-`, `shrine-header-slot-` or
+    `shrine-footer-slot`, respectively.
 
 - If you delete files from `sources/`, the corresponding files in
   `public/` will **not** be deleted and will need to be manually
diff --git a/transform.xslt b/transform.xslt
index 20fe4c2..e8ecb3d 100644
--- a/transform.xslt
+++ b/transform.xslt
@@ -1,6 +1,6 @@
 <!--
-This is an extremely simple XSLT transform which simply reads in a
-`template.xml` and :—
+This is a⸠n extremely simple⸡ progressively‐less‐simple X·S·L·T
+transform which simply reads in a `template.xml` and :—
 
 • Replaces the `<html:shrine-content>` element with the content of the document it is being applied to,
 
@@ -124,7 +124,7 @@ If a copy of the MPL was not distributed with this file, You can obtain one at h
 	<!--
 		Process template text; just make a copy.
 	-->
-	<xslt:template match="text()" mode="content">
+	<xslt:template match="text()" mode="template">
 		<xslt:copy/>
 	</xslt:template>
 
@@ -169,32 +169,61 @@ If a copy of the MPL was not distributed with this file, You can obtain one at h
 	</xslt:template>
 
 	<!--
-		Process the template header.
-		Read the `@data-header` attribute of the root element, append `"-header.xml"` to the end of it, and process the resulting document.
-		If no `@data-header` attribute is provided, no header is rendered.
+		Process the template header and footer.
+		Read the corresponding `@data-header` or `@data-footer` attribute of the root element, append `"-header.xml"` or `"-footer.xml"` to the end of it, and process the resulting document.
+		If no `@data-header` or `@data-footer` attribute is provided, nothing is rendered.
 	-->
-	<xslt:template match="html:shrine-header" mode="template">
-		<xslt:for-each select="$source/*/@data-shrine-header">
-			<xslt:apply-templates select="document(concat('./', ., '-header.xml'), $template)/*" mode="content"/>
-		</xslt:for-each>
+	<xslt:template match="html:shrine-header|html:shrine-footer" mode="template">
+		<xslt:param name="context" select="'shrine-template'"/>
+		<xslt:variable name="kind" select="substring-after(local-name(), 'shrine-')"/>
+		<xslt:choose>
+			<xslt:when test="$context='shrine-template'">
+				<xslt:for-each select="$source/*/@*[name()=concat('data-shrine-', $kind)]">
+					<xslt:for-each select="document(concat('./', ., '-', $kind, '.xml'), $template)/*">
+						<xslt:element name="{local-name()}">
+							<xslt:for-each select="@*">
+								<xslt:copy/>
+							</xslt:for-each>
+							<xslt:for-each select="$source//*[@slot=concat('shrine-', $kind, '-before')]">
+								<xslt:apply-templates select="." mode="content"/>
+							</xslt:for-each>
+							<xslt:apply-templates mode="template">
+								<xslt:with-param name="context" select="concat('shrine-', $kind)"/>
+							</xslt:apply-templates>
+							<xslt:for-each select="$source//*[@slot=concat('shrine-', $kind, '-after')]">
+								<xslt:apply-templates select="." mode="content"/>
+							</xslt:for-each>
+						</xslt:element>
+					</xslt:for-each>
+				</xslt:for-each>
+			</xslt:when>
+			<xslt:otherwise>
+				<xslt:element name="{local-name()}">
+					<xslt:for-each select="@*">
+						<xslt:copy/>
+					</xslt:for-each>
+					<xslt:apply-templates mode="template"/>
+				</xslt:element>
+			</xslt:otherwise>
+		</xslt:choose>
 	</xslt:template>
 
 	<!--
-		Process the template footer.
-		Read the `@data-footer` attribute of the root element, append `"-footer.xml"` to the end of it, and process the resulting document.
-		If no `@data-footer` attribute is provided, no footer is rendered.
+		Process the content.
 	-->
-	<xslt:template match="html:shrine-footer" mode="template">
-		<xslt:for-each select="$source/*/@data-shrine-footer">
-			<xslt:apply-templates select="document(concat('./', ., '-footer.xml'), $template)/*" mode="content"/>
-		</xslt:for-each>
+	<xslt:template match="html:shrine-content" mode="template">
+		<xslt:apply-templates select="$source/*" mode="content"/>
 	</xslt:template>
 
 	<!--
-		Process the content.
+		Process miscellaneous template slots.
 	-->
-	<xslt:template match="html:shrine-content" mode="template">
-		<xslt:apply-templates select="$source/*" mode="content"/>
+	<xslt:template match="html:slot[not(ancestor::html:template)]" mode="template">
+		<xslt:param name="context" select="'shrine-template'"/>
+		<xslt:variable name="name" select="string(@name)"/>
+		<xslt:for-each select="$source//*[@slot=concat($context, '-slot-', $name)]">
+			<xslt:apply-templates select="." mode="content"/>
+		</xslt:for-each>
 	</xslt:template>
 
 	<!--