diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | blog.cabal | 5 | ||||
-rw-r--r-- | blog.nix | 4 | ||||
-rw-r--r-- | lists/default.md.do | 41 | ||||
l--------- | lists/dirty-haskell/001 | 1 | ||||
l--------- | lists/dirty-haskell/002 | 1 | ||||
-rw-r--r-- | lists/dirty-haskell/title | 1 | ||||
l--------- | lists/zz_all/001 | 1 | ||||
l--------- | lists/zz_all/002 | 1 | ||||
l--------- | lists/zz_all/003 | 1 | ||||
-rw-r--r-- | lists/zz_all/title | 1 | ||||
-rw-r--r-- | provider/css/default.css | 40 | ||||
-rw-r--r-- | provider/index.html | 8 | ||||
-rw-r--r-- | provider/index.md | 3 | ||||
-rw-r--r-- | provider/posts/blog-documentation.md (renamed from posts/blog-documentation.md) | 8 | ||||
-rw-r--r-- | provider/posts/blog-rss.md (renamed from posts/blog-rss.md) | 6 | ||||
-rw-r--r-- | provider/posts/pwutil.md (renamed from posts/pwutil.md) | 5 | ||||
-rw-r--r-- | provider/templates/default.html | 22 | ||||
-rw-r--r-- | provider/templates/index.html | 9 | ||||
-rw-r--r-- | provider/templates/post-list.html | 7 | ||||
-rw-r--r-- | provider/templates/tag.html | 2 | ||||
-rw-r--r-- | src/Site.hs | 123 | ||||
-rw-r--r-- | templates/default.html | 55 |
23 files changed, 234 insertions, 113 deletions
@@ -1 +1,3 @@ | |||
1 | /result | 1 | /result |
2 | /_cache/ | ||
3 | /_site/ | ||
@@ -17,7 +17,12 @@ build-type: Simple | |||
17 | cabal-version: >=1.10 | 17 | cabal-version: >=1.10 |
18 | 18 | ||
19 | executable site | 19 | executable site |
20 | default-language: Haskell2010 | ||
20 | hs-source-dirs: src | 21 | hs-source-dirs: src |
21 | main-is: Site.hs | 22 | main-is: Site.hs |
22 | build-depends: base >=4.7 && <4.8 | 23 | build-depends: base >=4.7 && <4.8 |
23 | , hakyll >=4.6 && <5 | 24 | , hakyll >=4.6 && <5 |
25 | , containers >=0.5 && <0.6 | ||
26 | , pandoc >=1.13 && <2 | ||
27 | , data-default >=0.5 && <0.6 | ||
28 | , filepath >=1.3 && <2 | ||
@@ -1,7 +1,7 @@ | |||
1 | # This file was auto-generated by cabal2nix. Please do NOT edit manually! | 1 | # This file was auto-generated by cabal2nix. Please do NOT edit manually! |
2 | 2 | ||
3 | { mkDerivation, stdenv | 3 | { mkDerivation, stdenv |
4 | , hakyll | 4 | , hakyll, containers, pandoc, data-default, filepath |
5 | }: | 5 | }: |
6 | 6 | ||
7 | mkDerivation { | 7 | mkDerivation { |
@@ -11,7 +11,7 @@ mkDerivation { | |||
11 | isExecutable = true; | 11 | isExecutable = true; |
12 | isLibrary = false; | 12 | isLibrary = false; |
13 | buildDepends = [ | 13 | buildDepends = [ |
14 | hakyll | 14 | hakyll containers pandoc data-default filepath |
15 | ]; | 15 | ]; |
16 | license = stdenv.lib.licenses.publicDomain; | 16 | license = stdenv.lib.licenses.publicDomain; |
17 | } | 17 | } |
diff --git a/lists/default.md.do b/lists/default.md.do deleted file mode 100644 index 55abe6f..0000000 --- a/lists/default.md.do +++ /dev/null | |||
@@ -1,41 +0,0 @@ | |||
1 | #!/usr/bin/env bash | ||
2 | redo-ifchange "$2/title" | ||
3 | |||
4 | POSTS=() | ||
5 | while read -r -d $'\0'; do | ||
6 | post=$(realpath --relative-to=. "$REPLY") | ||
7 | POSTS+=("$post") | ||
8 | done < <(find "$2" -maxdepth 1 -mindepth 1 -not -name 'title' -not -name 'preview' -print0 | sort -z) | ||
9 | |||
10 | for x in "${POSTS[@]}"; do | ||
11 | printf "%s\0%s\0" "$x" "${x%.md}.html" | ||
12 | done | xargs -r -0 redo-ifchange | ||
13 | |||
14 | TITLE=$(< $2/title) | ||
15 | |||
16 | cat <<EOF | ||
17 | % $(< "$2/title") | ||
18 | |||
19 | EOF | ||
20 | |||
21 | POST_TITLES=() | ||
22 | for x in "${POSTS[@]}"; do | ||
23 | title=$(pandoc -t json "$x" | ../build/extract-title.hs) | ||
24 | POST_TITLES+=("$title") | ||
25 | done | ||
26 | |||
27 | for n in $(seq 0 $((${#POSTS[@]}-1))); do | ||
28 | printf "* [%s](%s)\n" "${POST_TITLES[$n]}" "${POSTS[$n]%.md}.html" | ||
29 | done | ||
30 | |||
31 | >"$2/preview" | ||
32 | prev_no=3 | ||
33 | if [[ ${#POSTS[@]} -gt $prev_no ]]; then | ||
34 | printf "* …\n" >> "$2/preview" | ||
35 | else | ||
36 | prev_no=${#POSTS[@]} | ||
37 | fi | ||
38 | |||
39 | for n in $(seq $prev_no -1 1); do | ||
40 | printf "* [%s](%s)\n" "${POST_TITLES[-$n]}" "${POSTS[-$n]%.md}.html" >>"$2/preview" | ||
41 | done | ||
diff --git a/lists/dirty-haskell/001 b/lists/dirty-haskell/001 deleted file mode 120000 index 92c240d..0000000 --- a/lists/dirty-haskell/001 +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | ../../posts/blog-documentation.md \ No newline at end of file | ||
diff --git a/lists/dirty-haskell/002 b/lists/dirty-haskell/002 deleted file mode 120000 index ed24126..0000000 --- a/lists/dirty-haskell/002 +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | ../../posts/blog-rss.md \ No newline at end of file | ||
diff --git a/lists/dirty-haskell/title b/lists/dirty-haskell/title deleted file mode 100644 index e40f49c..0000000 --- a/lists/dirty-haskell/title +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | Blog Software | ||
diff --git a/lists/zz_all/001 b/lists/zz_all/001 deleted file mode 120000 index 92c240d..0000000 --- a/lists/zz_all/001 +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | ../../posts/blog-documentation.md \ No newline at end of file | ||
diff --git a/lists/zz_all/002 b/lists/zz_all/002 deleted file mode 120000 index ed24126..0000000 --- a/lists/zz_all/002 +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | ../../posts/blog-rss.md \ No newline at end of file | ||
diff --git a/lists/zz_all/003 b/lists/zz_all/003 deleted file mode 120000 index 1057f2c..0000000 --- a/lists/zz_all/003 +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | ../../posts/pwutil.md \ No newline at end of file | ||
diff --git a/lists/zz_all/title b/lists/zz_all/title deleted file mode 100644 index 73a5267..0000000 --- a/lists/zz_all/title +++ /dev/null | |||
@@ -1 +0,0 @@ | |||
1 | All Posts | ||
diff --git a/provider/css/default.css b/provider/css/default.css new file mode 100644 index 0000000..7b89107 --- /dev/null +++ b/provider/css/default.css | |||
@@ -0,0 +1,40 @@ | |||
1 | body { | ||
2 | margin: auto; | ||
3 | padding-right: 1em; | ||
4 | padding-left: 1em; | ||
5 | font: normal 1.1em monospace; | ||
6 | max-width: 60em; | ||
7 | text-align: justify; | ||
8 | } | ||
9 | |||
10 | .display-math { | ||
11 | display: block; | ||
12 | margin-top: 0.2em; | ||
13 | margin-bottm: 0.2em; | ||
14 | text-align: center; | ||
15 | } | ||
16 | |||
17 | .inline-math { | ||
18 | display: inline; | ||
19 | } | ||
20 | |||
21 | a { | ||
22 | color: inherit; | ||
23 | } | ||
24 | |||
25 | p { | ||
26 | margin-bottom: 0 | ||
27 | } | ||
28 | |||
29 | p + p { | ||
30 | text-indent: 1.5em; | ||
31 | margin-top: 0; | ||
32 | } | ||
33 | |||
34 | pre { | ||
35 | margin-left: 1.5em; | ||
36 | } | ||
37 | |||
38 | p code { | ||
39 | font-style:italic; | ||
40 | } \ No newline at end of file | ||
diff --git a/provider/index.html b/provider/index.html new file mode 100644 index 0000000..8eb184f --- /dev/null +++ b/provider/index.html | |||
@@ -0,0 +1,8 @@ | |||
1 | $body$ | ||
2 | <ul> | ||
3 | $for(tags)$ | ||
4 | <li> | ||
5 | $body$ | ||
6 | </li> | ||
7 | $endfor$ | ||
8 | </ul> | ||
diff --git a/provider/index.md b/provider/index.md new file mode 100644 index 0000000..53a06bc --- /dev/null +++ b/provider/index.md | |||
@@ -0,0 +1,3 @@ | |||
1 | This is a blog. | ||
2 | It contains things. | ||
3 | Send other things to <blog@dirty-haskell.org> if you so choose. | ||
diff --git a/posts/blog-documentation.md b/provider/posts/blog-documentation.md index 7db843d..b0d4af6 100644 --- a/posts/blog-documentation.md +++ b/provider/posts/blog-documentation.md | |||
@@ -1,5 +1,9 @@ | |||
1 | % On the Origin of dirty-haskell.org | 1 | --- |
2 | title: On the Origin of dirty-haskell.org | ||
3 | published: 2015-03-12 | ||
4 | tags: Blog Software | ||
5 | --- | ||
2 | 6 | ||
3 | The software used is a trivially modified version of the one powering [math.kleen.org](http://math.kleen.org/lists/blog.html). | 7 | The software used is a trivially modified version of the one powering [math.kleen.org](http://math.kleen.org/lists/blog.html). |
4 | 8 | ||
5 | The title is without deeper meaning. \ No newline at end of file | 9 | The title is without deeper meaning. |
diff --git a/posts/blog-rss.md b/provider/posts/blog-rss.md index 4e8cb24..095ff56 100644 --- a/posts/blog-rss.md +++ b/provider/posts/blog-rss.md | |||
@@ -1,4 +1,8 @@ | |||
1 | % dirty-haskell.org´s rss feeds | 1 | --- |
2 | title: dirty-haskell.org´s rss feeds | ||
3 | published: 2015-03-29 | ||
4 | tags: Blog Software | ||
5 | --- | ||
2 | 6 | ||
3 | I extended the software suite inherited from [math.kleen.org](http://math.kleen.org) to include support for rss feeds. | 7 | I extended the software suite inherited from [math.kleen.org](http://math.kleen.org) to include support for rss feeds. |
4 | The heart of the issue is a ~80 line haskell script I chose to call, in a bout of creativity, "generate-rss.hs". | 8 | The heart of the issue is a ~80 line haskell script I chose to call, in a bout of creativity, "generate-rss.hs". |
diff --git a/posts/pwutil.md b/provider/posts/pwutil.md index 176ffdc..a4b3757 100644 --- a/posts/pwutil.md +++ b/provider/posts/pwutil.md | |||
@@ -1,4 +1,7 @@ | |||
1 | % A Tool to Manage a Set of YAML Objects Representing Account Information — pwutil | 1 | --- |
2 | title: A Tool to Manage a Set of YAML Objects Representing Account Information — pwutil | ||
3 | published: 2015-04-07 | ||
4 | --- | ||
2 | 5 | ||
3 | A long time ago I wrote a bunch of scripts (first in bash, then zsh, and later perl) to manage a, sometimes encrypted, file containing account information I get asked to create and remember on a daily basis—accounts for shopping websites spring to mind. | 6 | A long time ago I wrote a bunch of scripts (first in bash, then zsh, and later perl) to manage a, sometimes encrypted, file containing account information I get asked to create and remember on a daily basis—accounts for shopping websites spring to mind. |
4 | 7 | ||
diff --git a/provider/templates/default.html b/provider/templates/default.html new file mode 100644 index 0000000..14497b2 --- /dev/null +++ b/provider/templates/default.html | |||
@@ -0,0 +1,22 @@ | |||
1 | <!DOCTYPE html> | ||
2 | <html$if(lang)$ lang="$lang$"$endif$> | ||
3 | <head> | ||
4 | <meta charset="utf-8"> | ||
5 | <meta name="generator" content="pandoc"> | ||
6 | <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> | ||
7 | <title>$if(title)$$title$$endif$</title> | ||
8 | <style type="text/css">code{white-space: pre;}</style> | ||
9 | <!--[if lt IE 9]> | ||
10 | <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> | ||
11 | <![endif]--> | ||
12 | <link rel="stylesheet" href="/css/default.css"> | ||
13 | </head> | ||
14 | <body> | ||
15 | $if(title)$ | ||
16 | <header> | ||
17 | <h1 class="title"><a href="/" title="dirty-haskell.org">dirty-haskell.org</a>: $title$</h1> | ||
18 | </header> | ||
19 | $endif$ | ||
20 | $body$ | ||
21 | </body> | ||
22 | </html> | ||
diff --git a/provider/templates/index.html b/provider/templates/index.html new file mode 100644 index 0000000..0eea806 --- /dev/null +++ b/provider/templates/index.html | |||
@@ -0,0 +1,9 @@ | |||
1 | $body$ | ||
2 | |||
3 | <ul> | ||
4 | $for(tags)$ | ||
5 | <li> | ||
6 | $body$ | ||
7 | </li> | ||
8 | $endfor$ | ||
9 | </ul> | ||
diff --git a/provider/templates/post-list.html b/provider/templates/post-list.html new file mode 100644 index 0000000..4d50d4d --- /dev/null +++ b/provider/templates/post-list.html | |||
@@ -0,0 +1,7 @@ | |||
1 | <ul> | ||
2 | $for(posts)$ | ||
3 | <li> | ||
4 | $if(ellipsis)$…$else$<a href="$url$">$title$</a>$endif$ | ||
5 | </li> | ||
6 | $endfor$ | ||
7 | </ul> | ||
diff --git a/provider/templates/tag.html b/provider/templates/tag.html new file mode 100644 index 0000000..8854cdd --- /dev/null +++ b/provider/templates/tag.html | |||
@@ -0,0 +1,2 @@ | |||
1 | <a href="$url$">$title$</a>$if(rss)$ (<a href="$rss$">RSS</a>)$endif$ | ||
2 | $body$ | ||
diff --git a/src/Site.hs b/src/Site.hs index d1afbce..dde7047 100644 --- a/src/Site.hs +++ b/src/Site.hs | |||
@@ -1,16 +1,131 @@ | |||
1 | {-# LANGUAGE OverloadedStrings #-} | 1 | {-# LANGUAGE OverloadedStrings, RankNTypes, StandaloneDeriving, FlexibleInstances #-} |
2 | 2 | ||
3 | import Hakyll | 3 | import Hakyll |
4 | 4 | ||
5 | import Data.Monoid (Monoid(..), mconcat, (<>)) | ||
6 | import Control.Monad (liftM) | ||
7 | import Data.Char (toLower, isSpace, isAlphaNum) | ||
8 | import Data.Maybe (mapMaybe, fromMaybe) | ||
9 | import Data.Map (Map) | ||
10 | import qualified Data.Map as Map | ||
11 | import Data.List (take, reverse) | ||
12 | import Data.Default | ||
13 | import Text.Pandoc.Options (WriterOptions(..), ObfuscationMethod(..)) | ||
14 | import Control.Applicative (Alternative(..), Applicative(..)) | ||
15 | |||
16 | import System.FilePath (replaceExtension) | ||
17 | |||
5 | main :: IO () | 18 | main :: IO () |
6 | main = hakyllWith config $ do | 19 | main = hakyllWith config $ do |
7 | match "/posts/*" $ do | 20 | match "templates/*" $ compile templateCompiler |
21 | |||
22 | match "css/*" $ do | ||
23 | route idRoute | ||
24 | compile copyFileCompiler | ||
25 | |||
26 | match "posts/*" $ do | ||
8 | route $ setExtension ".html" | 27 | route $ setExtension ".html" |
9 | compile $ do | 28 | compile $ do |
29 | getResourceBody >>= saveSnapshot "content" | ||
10 | pandocCompiler | 30 | pandocCompiler |
11 | >>= saveSnapshot "content" | ||
12 | >>= loadAndApplyTemplate "templates/default.html" defaultContext | 31 | >>= loadAndApplyTemplate "templates/default.html" defaultContext |
13 | >>= relativizeUrls | 32 | >>= relativizeUrls |
14 | 33 | ||
34 | tags <- buildTags "posts/*" tagTranslation' >>= addTag "All Posts" "posts/*" | ||
35 | |||
36 | tagsRules tags $ \tag pattern -> do | ||
37 | route idRoute | ||
38 | compile $ do | ||
39 | let ctx = mconcat [ constField "title" tag | ||
40 | , listField "posts" defaultContext $ chronological =<< loadAll pattern | ||
41 | , defaultContext | ||
42 | ] | ||
43 | makeItem "" | ||
44 | >>= loadAndApplyTemplate "templates/post-list.html" ctx | ||
45 | >>= loadAndApplyTemplate "templates/default.html" ctx | ||
46 | >>= relativizeUrls | ||
47 | |||
48 | let | ||
49 | tags' = tags { tagsMakeId = fromFilePath . (`replaceExtension` "rss") . toFilePath . tagsMakeId tags} | ||
50 | |||
51 | tagsRules tags' $ \tag pattern -> do | ||
52 | route idRoute | ||
53 | compile $ do | ||
54 | let | ||
55 | feedCtx = mconcat [ bodyField "description" | ||
56 | , defaultContext | ||
57 | ] | ||
58 | renderRss (feedConfig tag) feedCtx =<< loadAllSnapshots pattern "content" | ||
59 | |||
60 | match "index.md" $ do | ||
61 | route $ setExtension ".html" | ||
62 | compile $ do | ||
63 | let ctx = mconcat [ listField "tags" defaultContext $ mapM (\(k, _) -> renderTag k tags) $ tagsMap tags | ||
64 | , constField "title" "Index" | ||
65 | , defaultContext | ||
66 | ] | ||
67 | item <- getResourceBody | ||
68 | pandocCompilerWith def (def { writerEmailObfuscation = NoObfuscation }) | ||
69 | >>= loadAndApplyTemplate "templates/index.html" ctx | ||
70 | >>= loadAndApplyTemplate "templates/default.html" ctx | ||
71 | >>= relativizeUrls | ||
72 | |||
73 | deriving instance Eq (Item String) | ||
74 | |||
75 | feedConfig :: String -> FeedConfiguration | ||
76 | feedConfig tagName = FeedConfiguration { feedTitle = "dirty-haskell.org: " ++ tagName | ||
77 | , feedDescription = "dirty-haskell.org — A Blog." | ||
78 | , feedAuthorName = "G. Kleen" | ||
79 | , feedAuthorEmail = "blog@dirty-haskell.org" | ||
80 | , feedRoot = "https://dirty-haskell.org" | ||
81 | } | ||
82 | |||
83 | renderTag :: String -- ^ Tag name | ||
84 | -> Tags | ||
85 | -> Compiler (Item String) | ||
86 | renderTag tag tags = do | ||
87 | ellipsisItem <- makeItem "" | ||
88 | let | ||
89 | ids = fromMaybe [] $ lookup tag $ tagsMap tags | ||
90 | postCtx = mconcat [ listField "posts" (ellipsisContext ellipsisItem) $ liftM (withEllipsis ellipsisItem) $ chronological =<< mapM load ids | ||
91 | , constField "title" tag | ||
92 | , constField "rss" ("tags/" ++ tagTranslation tag ++ ".rss") | ||
93 | , constField "url" ("tags/" ++ tagTranslation tag ++ ".html") | ||
94 | , defaultContext | ||
95 | ] | ||
96 | makeItem "" | ||
97 | >>= loadAndApplyTemplate "templates/post-list.html" postCtx | ||
98 | >>= loadAndApplyTemplate "templates/tag.html" postCtx | ||
99 | where | ||
100 | ellipsisContext item = mconcat [ boolField "ellipsis" (== item) | ||
101 | , defaultContext | ||
102 | ] | ||
103 | boolField name f = field name (\i -> if f i | ||
104 | then pure (error $ unwords ["no string value for bool field:",name]) | ||
105 | else empty) | ||
106 | withEllipsis ellipsisItem xs | ||
107 | | length xs > max = [ellipsisItem] ++ takeEnd (max - 1) xs | ||
108 | | otherwise = xs | ||
109 | takeEnd i = reverse . take i . reverse | ||
110 | max = 4 | ||
111 | |||
112 | tagTranslation' :: String -> Identifier | ||
113 | tagTranslation' = fromCapture "tags/*.html" . tagTranslation | ||
114 | |||
115 | tagTranslation :: String -> String | ||
116 | tagTranslation = mapMaybe charTrans | ||
117 | where | ||
118 | charTrans c | ||
119 | | isSpace c = Just $ '-' | ||
120 | | isAlphaNum c = Just $ toLower c | ||
121 | | otherwise = Nothing | ||
122 | |||
123 | addTag :: MonadMetadata m => String -> Pattern -> Tags -> m Tags | ||
124 | addTag name pattern tags = do | ||
125 | ids <- getMatches pattern | ||
126 | return $ tags { tagsMap = tagsMap tags ++ [(name, ids)] } | ||
127 | |||
15 | config :: Configuration | 128 | config :: Configuration |
16 | config = defaultConfiguration | 129 | config = defaultConfiguration { providerDirectory = "provider" |
130 | , deployCommand = "rsync -av --progress -c --delete-delay -m _site/ gkleen@surtr.yggdrasil.li:/var/www/dirty-haskell.org/" | ||
131 | } | ||
diff --git a/templates/default.html b/templates/default.html deleted file mode 100644 index 970848f..0000000 --- a/templates/default.html +++ /dev/null | |||
@@ -1,55 +0,0 @@ | |||
1 | <!DOCTYPE html> | ||
2 | <html$if(lang)$ lang="$lang$"$endif$> | ||
3 | <head> | ||
4 | <meta charset="utf-8"> | ||
5 | <meta name="generator" content="pandoc"> | ||
6 | <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes"> | ||
7 | $for(author-meta)$ | ||
8 | <meta name="author" content="$author-meta$"> | ||
9 | $endfor$ | ||
10 | $if(date-meta)$ | ||
11 | <meta name="dcterms.date" content="$date-meta$"> | ||
12 | $endif$ | ||
13 | <title>$if(title-prefix)$$title-prefix$ - $endif$$pagetitle$</title> | ||
14 | <style type="text/css">code{white-space: pre;}</style> | ||
15 | <!--[if lt IE 9]> | ||
16 | <script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script> | ||
17 | <![endif]--> | ||
18 | $if(quotes)$ | ||
19 | <style type="text/css">q { quotes: "“" "”" "‘" "’"; }</style> | ||
20 | $endif$ | ||
21 | $if(highlighting-css)$ | ||
22 | <style type="text/css"> | ||
23 | $highlighting-css$ | ||
24 | </style> | ||
25 | $endif$ | ||
26 | $for(css)$ | ||
27 | <link rel="stylesheet" href="$css$"> | ||
28 | $endfor$ | ||
29 | $if(math)$ | ||
30 | $math$ | ||
31 | $endif$ | ||
32 | $for(header-includes)$ | ||
33 | $header-includes$ | ||
34 | $endfor$ | ||
35 | </head> | ||
36 | <body> | ||
37 | $for(include-before)$ | ||
38 | $include-before$ | ||
39 | $endfor$ | ||
40 | $if(title)$ | ||
41 | <header> | ||
42 | <h1 class="title"><a href="/" title="dirty-haskell.org">dirty-haskell.org</a>: $title$</h1> | ||
43 | </header> | ||
44 | $endif$ | ||
45 | $if(toc)$ | ||
46 | <nav id="$idprefix$TOC"> | ||
47 | $toc$ | ||
48 | </nav> | ||
49 | $endif$ | ||
50 | $body$ | ||
51 | $for(include-after)$ | ||
52 | $include-after$ | ||
53 | $endfor$ | ||
54 | </body> | ||
55 | </html> | ||