summary refs log tree commit diff
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2020-05-11 12:46:17 -0400
committerJune McEnroe <june@causal.agency>2020-05-11 12:46:17 -0400
commit83e23c71985ce8e10c5d1515d134cc386b3c476c (patch)
treee3da4eed9c5cfcac0385060779e103740a9ced32
parentRemove unused predefined atoms (diff)
downloadbubger-83e23c71985ce8e10c5d1515d134cc386b3c476c.tar.gz
bubger-83e23c71985ce8e10c5d1515d134cc386b3c476c.zip
Eliminate stray whitespace in HTML and Atom
Diffstat (limited to '')
-rw-r--r--archive.h4
-rw-r--r--atom.c90
-rw-r--r--default.html3
-rw-r--r--html.c220
4 files changed, 156 insertions, 161 deletions
diff --git a/archive.h b/archive.h
index 75d8781..f0bda03 100644
--- a/archive.h
+++ b/archive.h
@@ -26,6 +26,8 @@
 
 #define GENERATOR_URL "https://git.causal.agency/bubger"
 
+#define Q(...) #__VA_ARGS__
+
 struct Address {
 	char *name;
 	const char *mailbox;
@@ -159,8 +161,6 @@ void concatData(
 void concatThreads(struct List threads, const struct Envelope *envelopes);
 void concatIndex(struct List threads, const struct Envelope *envelopes);
 
-#define TEMPLATE(...) #__VA_ARGS__
-
 struct Variable {
 	const char *name;
 	const char *value;
diff --git a/atom.c b/atom.c
index 7bc4fe9..35cc840 100644
--- a/atom.c
+++ b/atom.c
@@ -31,12 +31,12 @@ static char *atomID(const struct Envelope *envelope) {
 }
 
 static int atomAuthor(FILE *file, struct Address addr) {
-	const char *template = TEMPLATE(
-		<author>
-			<name>[name]</name>
-			<email>[mailbox]@[host]</email>
-		</author>
-	);
+	const char *template = {
+		Q(<author>)
+			Q(<name>[name]</name>)
+			Q(<email>[mailbox]@[host]</email>)
+		Q(</author>)
+	};
 	struct Variable vars[] = {
 		{ "name", addressName(addr) },
 		{ "mailbox", addr.mailbox },
@@ -62,13 +62,13 @@ static const char *atomUpdated(time_t time) {
 }
 
 int atomEntryOpen(FILE *file, const struct Envelope *envelope) {
-	const char *template = TEMPLATE(
-		<entry>
-		<id>[id]</id>
-		<title>[title]</title>
-		<updated>[updated]</updated>
-		<link rel="alternate" type="application/mbox" href="[base][url]"/>
-	);
+	const char *template = {
+		Q(<entry>)
+		Q(<id>[id]</id>)
+		Q(<title>[title]</title>)
+		Q(<updated>[updated]</updated>)
+		Q(<link rel="alternate" type="application/mbox" href="[base][url]"/>)
+	};
 	char *id = atomID(envelope);
 	char *url = atomEntryURL(envelope);
 	struct Variable vars[] = {
@@ -88,13 +88,13 @@ int atomEntryOpen(FILE *file, const struct Envelope *envelope) {
 }
 
 int atomContent(FILE *file, const char *content) {
-	const char *template = TEMPLATE(
-		<content type="xhtml">
-			<div xmlns="http://www.w3.org/1999/xhtml">
-				<pre>[content]</pre>
-			</div>
-		</content>
-	);
+	const char *template = {
+		Q(<content type="xhtml">)
+			Q(<div xmlns="http://www.w3.org/1999/xhtml">)
+				Q(<pre>[content]</pre>)
+			Q(</div>)
+		Q(</content>)
+	};
 	struct Variable vars[] = {
 		{ "content", content },
 		{0},
@@ -103,7 +103,7 @@ int atomContent(FILE *file, const char *content) {
 }
 
 int atomEntryClose(FILE *file) {
-	return templateRender(file, TEMPLATE(</entry>), NULL, NULL);
+	return templateRender(file, Q(</entry>), NULL, NULL);
 }
 
 static char *atomThreadURL(const struct Envelope *envelope, const char *type) {
@@ -116,23 +116,22 @@ static char *atomThreadURL(const struct Envelope *envelope, const char *type) {
 }
 
 int atomThreadOpen(FILE *file, const struct Envelope *envelope) {
-	const char *template = TEMPLATE(
-		<[q]xml version="1.0" encoding="utf-8"[q]>
-		<feed xmlns="http://www.w3.org/2005/Atom">
-		<generator uri="[generator]">bubger</generator>
-		<id>[id]</id>
-		<title>[title]</title>
-		<updated>[updated]</updated>
-		<link rel="self" href="[base][atom]"/>
-		<link rel="alternate" type="text/html" href="[base][html]"/>
-		<link rel="alternate" type="application/mbox" href="[base][mbox]"/>
-	);
+	const char *template = {
+		"<?" Q(xml version="1.0" encoding="utf-8") "?>"
+		Q(<feed xmlns="http://www.w3.org/2005/Atom">)
+		Q(<generator uri="[generator]">bubger</generator>)
+		Q(<id>[id]</id>)
+		Q(<title>[title]</title>)
+		Q(<updated>[updated]</updated>)
+		Q(<link rel="self" href="[base][atom]"/>)
+		Q(<link rel="alternate" type="text/html" href="[base][html]"/>)
+		Q(<link rel="alternate" type="application/mbox" href="[base][mbox]"/>)
+	};
 	char *id = atomID(envelope);
 	char *atom = atomThreadURL(envelope, "atom");
 	char *html = atomThreadURL(envelope, "html");
 	char *mbox = atomThreadURL(envelope, "mbox");
 	struct Variable vars[] = {
-		{ "q", "?" },
 		{ "generator", GENERATOR_URL },
 		{ "id", id },
 		{ "title", envelope->subject },
@@ -154,22 +153,21 @@ int atomThreadOpen(FILE *file, const struct Envelope *envelope) {
 }
 
 int atomThreadClose(FILE *file) {
-	return templateRender(file, TEMPLATE(</feed>), NULL, NULL);
+	return templateRender(file, Q(</feed>), NULL, NULL);
 }
 
 int atomIndexOpen(FILE *file) {
-	const char *template = TEMPLATE(
-		<[q]xml version="1.0" encoding="utf-8"[q]>
-		<feed xmlns="http://www.w3.org/2005/Atom">
-		<generator uri="[generator]">bubger</generator>
-		<id>mailto:[mailto]</id>
-		<title>[title]</title>
-		<updated>[updated]</updated>
-		<link rel="self" href="[base]/index.atom"/>
-		<link rel="alternate" type="text/html" href="[base]/index.html"/>
-	);
+	const char *template = {
+		"<?" Q(xml version="1.0" encoding="utf-8") "?>"
+		Q(<feed xmlns="http://www.w3.org/2005/Atom">)
+		Q(<generator uri="[generator]">bubger</generator>)
+		Q(<id>mailto:[mailto]</id>)
+		Q(<title>[title]</title>)
+		Q(<updated>[updated]</updated>)
+		Q(<link rel="self" href="[base]/index.atom"/>)
+		Q(<link rel="alternate" type="text/html" href="[base]/index.html"/>)
+	};
 	struct Variable vars[] = {
-		{ "q", "?" },
 		{ "generator", GENERATOR_URL },
 		{ "mailto", baseMailto },
 		{ "title", baseTitle },
@@ -181,5 +179,5 @@ int atomIndexOpen(FILE *file) {
 }
 
 int atomIndexClose(FILE *file) {
-	return templateRender(file, TEMPLATE(</feed>), NULL, NULL);
+	return templateRender(file, Q(</feed>), NULL, NULL);
 }
diff --git a/default.html b/default.html
index 6ff49cf..36736a6 100644
--- a/default.html
+++ b/default.html
@@ -80,6 +80,9 @@ ul.recipient li.group::after {
 address.from::before {
 	content: 'From: ';
 }
+address.from {
+	margin-right: 1ch;
+}
 ul.to::before {
 	content: 'To: ';
 	margin-right: 1ch;
diff --git a/html.c b/html.c
index 215c208..0d086da 100644
--- a/html.c
+++ b/html.c
@@ -28,20 +28,18 @@
 static int htmlAddress(FILE *file, const char *class, struct Address addr) {
 	const char *template;
 	if (addr.host) {
-		template = TEMPLATE(
-			<li><address class="[class]">[name]</address></li>
-		);
+		template = Q(<li><address class="[class]">[name]</address></li>);
 	} else if (addr.mailbox) {
-		template = TEMPLATE(
-			<li class="group">
-				<address class="[class]">[mailbox]</address>
-				<ul>
-		);
+		template = (const char *) {
+			Q(<li class="group">)
+				Q(<address class="[class]">[mailbox]</address>)
+				Q(<ul>)
+		};
 	} else {
-		template = TEMPLATE(
-				</ul>
-			</li>
-		);
+		template = (const char *) {
+				Q(</ul>)
+			Q(</li>)
+		};
 	}
 	struct Variable vars[] = {
 		{ "class", class },
@@ -55,9 +53,7 @@ static int htmlAddress(FILE *file, const char *class, struct Address addr) {
 static int
 htmlAddressList(FILE *file, const char *class, struct AddressList list) {
 	if (!list.len) return 0;
-	const char *template = TEMPLATE(
-		<ul class="recipient [class]">
-	);
+	const char *template = Q(<ul class="recipient [class]">);
 	struct Variable vars[] = {
 		{ "class", class },
 		{0},
@@ -68,7 +64,7 @@ htmlAddressList(FILE *file, const char *class, struct AddressList list) {
 		error = htmlAddress(file, class, list.addrs[i]);
 		if (error) return error;
 	}
-	return templateRender(file, TEMPLATE(</ul>), NULL, NULL);
+	return templateRender(file, Q(</ul>), NULL, NULL);
 }
 
 static char *htmlReply(const struct Envelope *envelope) {
@@ -111,9 +107,7 @@ static char *htmlMbox(const char *messageID) {
 
 static int
 htmlNavItem(FILE *file, const char *name, const char *base, const char *url) {
-	const char *template = TEMPLATE(
-		<li><a href="[base][url]">[name]</a></li>
-	);
+	const char *template = Q(<li><a href="[base][url]">[name]</a></li>);
 	struct Variable vars[] = {
 		{ "name", name },
 		{ "base", base },
@@ -124,7 +118,7 @@ htmlNavItem(FILE *file, const char *name, const char *base, const char *url) {
 }
 
 int htmlMessageNav(FILE *file, const struct Envelope *envelope) {
-	int error = templateRender(file, TEMPLATE(<nav><ul>), NULL, NULL);
+	int error = templateRender(file, Q(<nav><ul>), NULL, NULL);
 	if (error) return error;
 	if (envelope->inReplyTo) {
 		char *fragment = htmlFragment(envelope->inReplyTo);
@@ -136,7 +130,7 @@ int htmlMessageNav(FILE *file, const struct Envelope *envelope) {
 	error = htmlNavItem(file, "download", "", mbox);
 	free(mbox);
 	if (error) return error;
-	return templateRender(file, TEMPLATE(</ul></nav>), NULL, NULL);
+	return templateRender(file, Q(</ul></nav>), NULL, NULL);
 }
 
 static const char *htmlUTC(time_t time) {
@@ -146,15 +140,15 @@ static const char *htmlUTC(time_t time) {
 }
 
 int htmlMessageOpen(FILE *file, const struct Envelope *envelope) {
-	const char *template = TEMPLATE(
-		<article class="message" id="[messageID]">
-		<header>
-			<h2 class="subject"><a href="[fragment]">[subject]</a></h2>
-			<address class="from">
-				<a href="[reply]">[from]</a>
-			</address>
-			<time datetime="[utc]">[date]</time>
-	);
+	const char *template = {
+		Q(<article class="message" id="[messageID]">)
+		Q(<header>)
+			Q(<h2 class="subject"><a href="[fragment]">[subject]</a></h2>)
+			Q(<address class="from">)
+				Q(<a href="[reply]">[from]</a>)
+			Q(</address>)
+			Q(<time datetime="[utc]">[date]</time>)
+	};
 	char *fragment = htmlFragment(envelope->messageID);
 	char *reply = htmlReply(envelope);
 	struct Variable vars[] = {
@@ -172,14 +166,14 @@ int htmlMessageOpen(FILE *file, const struct Envelope *envelope) {
 		|| htmlAddressList(file, "to", envelope->to)
 		|| htmlAddressList(file, "cc", envelope->cc)
 		|| htmlMessageNav(file, envelope)
-		|| templateRender(file, TEMPLATE(</header>), NULL, NULL);
+		|| templateRender(file, Q(</header>), NULL, NULL);
 	free(reply);
 	free(fragment);
 	return error;
 }
 
 static int htmlInlineAttrs(FILE *file, const struct BodyPart *part) {
-	const char *template = " " TEMPLATE([attr]="[value]");
+	const char *template = " " Q([attr]="[value]");
 	if (part->contentID) {
 		struct Variable vars[] = {
 			{ "attr", "id" },
@@ -240,7 +234,7 @@ static int htmlMarkupURLs(FILE *file, char *buf) {
 		if (error) return error;
 		swap(&ptr[match[2].rm_so], &nul);
 
-		const char *template = TEMPLATE(<a href="[url]">[url]</a>);
+		const char *template = Q(<a href="[url]">[url]</a>);
 		swap(&ptr[match[3].rm_so], &nul);
 		struct Variable vars[] = {
 			{ "url", &ptr[match[2].rm_so] },
@@ -258,13 +252,13 @@ static int htmlMarkupQuote(FILE *file, char *buf) {
 	size_t level = 0;
 	for (char *ch = buf; *ch == '>' || *ch == ' '; level += (*ch++ == '>'));
 	for (size_t i = 0; i < level; ++i) {
-		error = templateRender(file, TEMPLATE(<q>), NULL, NULL);
+		error = templateRender(file, Q(<q>), NULL, NULL);
 		if (error) return error;
 	}
 	error = htmlMarkupURLs(file, buf);
 	if (error) return error;
 	for (size_t i = 0; i < level; ++i) {
-		error = templateRender(file, TEMPLATE(</q>), NULL, NULL);
+		error = templateRender(file, Q(</q>), NULL, NULL);
 		if (error) return error;
 	}
 	return 0;
@@ -303,15 +297,15 @@ static int htmlMarkup(FILE *file, const char *content) {
 		}
 		if (patch && !regexec(&regex, buf, 0, NULL, 0)) {
 			error = templateRender(
-				file, TEMPLATE(<b>[line]</b>), vars, escapeXML
+				file, Q(<b>[line]</b>), vars, escapeXML
 			);
 		} else if (patch && buf[0] == '-' && strcmp(buf, "---")) {
 			error = templateRender(
-				file, TEMPLATE(<del>[line]</del>), vars, escapeXML
+				file, Q(<del>[line]</del>), vars, escapeXML
 			);
 		} else if (patch && buf[0] == '+') {
 			error = templateRender(
-				file, TEMPLATE(<ins>[line]</ins>), vars, escapeXML
+				file, Q(<ins>[line]</ins>), vars, escapeXML
 			);
 		} else if (patch) {
 			error = escapeXML(file, buf);
@@ -331,23 +325,23 @@ static int htmlMarkup(FILE *file, const char *content) {
 
 int htmlInline(FILE *file, const struct BodyPart *part, const char *content) {
 	return 0
-		|| templateRender(file, TEMPLATE(<pre), NULL, NULL)
+		|| templateRender(file, Q(<pre), NULL, NULL)
 		|| htmlInlineAttrs(file, part)
-		|| templateRender(file, TEMPLATE(>), NULL, NULL)
+		|| templateRender(file, Q(>), NULL, NULL)
 		|| htmlMarkup(file, content)
-		|| templateRender(file, TEMPLATE(</pre>), NULL, NULL);
+		|| templateRender(file, Q(</pre>), NULL, NULL);
 }
 
 int htmlAttachmentOpen(FILE *file) {
-	return templateRender(file, TEMPLATE(<ul class="attachment">), NULL, NULL);
+	return templateRender(file, Q(<ul class="attachment">), NULL, NULL);
 }
 
 int htmlAttachment(
 	FILE *file, const struct BodyPart *part, const struct Variable *path
 ) {
-	const char *template = TEMPLATE(
-		<li><a href="[url]">[name][type][/][subtype]</a></li>
-	);
+	const char *template = {
+		Q(<li><a href="[url]">[name][type][/][subtype]</a></li>)
+	};
 	char *url = templateURL("../" PATH_ATTACHMENT, path);
 	const char *name = paramGet(part->disposition.params, "filename");
 	if (!name) name = paramGet(part->params, "name");
@@ -365,11 +359,11 @@ int htmlAttachment(
 }
 
 int htmlAttachmentClose(FILE *file) {
-	return templateRender(file, TEMPLATE(</ul>), NULL, NULL);
+	return templateRender(file, Q(</ul>), NULL, NULL);
 }
 
 int htmlMessageClose(FILE *file) {
-	return templateRender(file, TEMPLATE(</article>), NULL, NULL);
+	return templateRender(file, Q(</article>), NULL, NULL);
 }
 
 static char *htmlThreadURL(const struct Envelope *envelope, const char *type) {
@@ -382,14 +376,14 @@ static char *htmlThreadURL(const struct Envelope *envelope, const char *type) {
 }
 
 int htmlThreadHead(FILE *file, const struct Envelope *envelope) {
-	const char *template = TEMPLATE(
-		<!DOCTYPE html>
-		<meta charset="utf-8">
-		<meta name="generator" content="[generator]">
-		<title>[subject] &middot; [title]</title>
-		<link rel="alternate" type="application/atom+xml" href="[atom]">
-		<link rel="alternate" type="application/mbox" href="[mbox]">
-	);
+	const char *template = {
+		Q(<!DOCTYPE html>)
+		Q(<meta charset="utf-8">)
+		Q(<meta name="generator" content="[generator]">)
+		Q(<title>[subject] &middot; [title]</title>)
+		Q(<link rel="alternate" type="application/atom+xml" href="[atom]">)
+		Q(<link rel="alternate" type="application/mbox" href="[mbox]">)
+	};
 	char *atom = htmlThreadURL(envelope, "atom");
 	char *mbox = htmlThreadURL(envelope, "mbox");
 	struct Variable vars[] = {
@@ -407,19 +401,19 @@ int htmlThreadHead(FILE *file, const struct Envelope *envelope) {
 }
 
 int htmlThreadOpen(FILE *file, const struct Envelope *envelope) {
-	const char *template = TEMPLATE(
-		<header class="thread">
-			<h1>[subject]</h1>
-			<nav>
-				<ul>
-					<li><a href="../index.html">index</a></li>
-					<li><a href="[atom]">follow</a></li>
-					<li><a href="[mbox]">download</a></li>
-				</ul>
-			</nav>
-		</header>
-		<main class="thread">
-	);
+	const char *template = {
+		Q(<header class="thread">)
+			Q(<h1>[subject]</h1>)
+			Q(<nav>)
+				Q(<ul>)
+					Q(<li><a href="../index.html">index</a></li>)
+					Q(<li><a href="[atom]">follow</a></li>)
+					Q(<li><a href="[mbox]">download</a></li>)
+				Q(</ul>)
+			Q(</nav>)
+		Q(</header>)
+		Q(<main class="thread">)
+	};
 	char *atom = htmlThreadURL(envelope, "atom");
 	char *mbox = htmlThreadURL(envelope, "mbox");
 	struct Variable vars[] = {
@@ -448,16 +442,10 @@ static size_t threadCount(struct List thread) {
 
 int htmlSubthreadOpen(FILE *file, struct List thread) {
 	const char *template = {
-		TEMPLATE(
-			<details class="subthread" open>
-			<summary>
-		)
-		TEMPLATE (
-			<data class="replies" value="[replies]">[replies] repl[ies]</data>
-		)
-		TEMPLATE(
-			</summary>
-		)
+		Q(<details class="subthread" open>)
+		Q(<summary>)
+		Q(<data class="replies" value="[replies]">[replies] repl[ies]</data>)
+		Q(</summary>)
 	};
 	size_t count = threadCount(thread);
 	char replies[32];
@@ -471,16 +459,19 @@ int htmlSubthreadOpen(FILE *file, struct List thread) {
 }
 
 int htmlSubthreadClose(FILE *file) {
-	return templateRender(file, TEMPLATE(</details>), NULL, NULL);
+	return templateRender(file, Q(</details>), NULL, NULL);
 }
 
 static int htmlFooter(FILE *file) {
-	const char *template = TEMPLATE(
-		<footer>
-			<small><a href="[generator]">generated</a>
-			<time datetime="[time]">[time]</time></small>
-		</footer>
-	);
+	const char *template = {
+		Q(<footer>)
+			Q(<small>)
+			Q(<a href="[generator]">generated</a>)
+			" "
+			Q(<time datetime="[time]">[time]</time>)
+			Q(</small>)
+		Q(</footer>)
+	};
 	struct Variable vars[] = {
 		{ "generator", GENERATOR_URL },
 		{ "time", htmlUTC(time(NULL)) },
@@ -491,18 +482,18 @@ static int htmlFooter(FILE *file) {
 
 int htmlThreadClose(FILE *file) {
 	return 0
-		|| templateRender(file, TEMPLATE(</main>), NULL, NULL)
+		|| templateRender(file, Q(</main>), NULL, NULL)
 		|| htmlFooter(file);
 }
 
 int htmlIndexHead(FILE *file) {
-	const char *template = TEMPLATE(
-		<!DOCTYPE html>
-		<meta charset="utf-8">
-		<meta name="generator" content="[generator]">
-		<title>[title]</title>
-		<link rel="alternate" type="application/atom+xml" href="index.atom">
-	);
+	const char *template = {
+		Q(<!DOCTYPE html>)
+		Q(<meta charset="utf-8">)
+		Q(<meta name="generator" content="[generator]">)
+		Q(<title>[title]</title>)
+		Q(<link rel="alternate" type="application/atom+xml" href="index.atom">)
+	};
 	struct Variable vars[] = {
 		{ "generator", GENERATOR_URL },
 		{ "title", baseTitle },
@@ -513,7 +504,7 @@ int htmlIndexHead(FILE *file) {
 
 static int htmlIndexNav(FILE *file) {
 	int error = 0
-		|| templateRender(file, TEMPLATE(<nav><ul>), NULL, NULL)
+		|| templateRender(file, Q(<nav><ul>), NULL, NULL)
 		|| htmlNavItem(file, "follow", "", "index.atom");
 	if (error) return error;
 	if (baseSubscribe) {
@@ -522,23 +513,23 @@ static int htmlIndexNav(FILE *file) {
 	}
 	return 0
 		|| htmlNavItem(file, "write", "mailto:", baseMailto)
-		|| templateRender(file, TEMPLATE(</ul></nav>), NULL, NULL);
+		|| templateRender(file, Q(</ul></nav>), NULL, NULL);
 }
 
 int htmlIndexOpen(FILE *file) {
-	const char *head = TEMPLATE(
-		<header class="index">
-			<h1>[title]</h1>
-	);
+	const char *head = {
+		Q(<header class="index">)
+			Q(<h1>[title]</h1>)
+	};
 	struct Variable vars[] = {
 		{ "title", baseTitle },
 		{0},
 	};
-	const char *tail = TEMPLATE(
-		</header>
-		<main class="index">
-			<ol>
-	);
+	const char *tail = {
+		Q(</header>)
+		Q(<main class="index">)
+			Q(<ol>)
+	};
 	return 0
 		|| templateRender(file, head, vars, escapeXML)
 		|| htmlIndexNav(file)
@@ -557,14 +548,17 @@ static char *htmlIndexURL(const struct Envelope *envelope) {
 int htmlIndexThread(
 	FILE *file, const struct Envelope *envelope, struct List thread
 ) {
-	const char *template = TEMPLATE(
-		<li>
-			<h2 class="subject"><a href="[url]">[subject]</a></h2>
-			<address class="from">[from]</address>
-			<time datetime="[utc]">[date]</time>
-			<data class="replies" value="[replies]">[replies] repl[ies]</data>
-		</li>
-	);
+	const char *template = {
+		Q(<li>)
+			Q(<h2 class="subject"><a href="[url]">[subject]</a></h2>)
+			Q(<address class="from">[from]</address>)
+			Q(<time datetime="[utc]">[date]</time>)
+			" "
+			Q(<data class="replies" value="[replies]">)
+				Q([replies] repl[ies])
+			Q(</data>)
+		Q(</li>)
+	};
 	char *url = htmlIndexURL(envelope);
 	size_t count = threadCount(thread) - 1;
 	char replies[32];
@@ -586,6 +580,6 @@ int htmlIndexThread(
 
 int htmlIndexClose(FILE *file) {
 	return 0
-		|| templateRender(file, TEMPLATE(</ol></main>), NULL, NULL)
+		|| templateRender(file, Q(</ol></main>), NULL, NULL)
 		|| htmlFooter(file);
 }