diff --git a/share/defaultWording.conf b/share/defaultWording.conf
index 1c615dc..3ff6618 100644
--- a/share/defaultWording.conf
+++ b/share/defaultWording.conf
@@ -2,7 +2,9 @@ allLink = See all
allPage = All articles
allTaggedPage = All articles tagged ${tag}
commentsSection = Comments
+dateFormat = en-US
latestLink = See only latest
latestPage = Latest articles
latestTaggedPage = Latest articles tagged ${tag}
+metadata = ${?by ${author} ?}on ${date}${? tagged ${tags}?}
tagsList = Tags
diff --git a/share/js/domRenderer.js b/share/js/domRenderer.js
index 0a86e9b..aa4a57b 100644
--- a/share/js/domRenderer.js
+++ b/share/js/domRenderer.js
@@ -8,9 +8,7 @@ function DomRenderer(modules) {
function replaceMarkdown() {
var div = document.getElementById('contents');
if(div.children[0] && div.children[0].tagName.toLowerCase() == 'article') {
- var re = new RegExp('/' + blog.path.articlesPath + '/([^.]+)\.html');
- var key = decodeURI(document.location.pathname.replace(re, '$1'));
- convertArticle(div.children[0], key);
+ convertArticle(div.children[0], true);
} else {
var articles = div.getElementsByClassName('articles')[0];
if(articles != undefined) {
@@ -23,13 +21,15 @@ function DomRenderer(modules) {
}
}
- function convertArticle(article, key) {
+ function convertArticle(article, comments) {
var header = article.getElementsByTagName('header')[0];
+ header.appendChild(modules.metadata.get(article.id));
var text = article.getElementsByTagName('pre')[0];
if(text != undefined) {
article.replaceChild(getDiv(text.innerText), text);
- if(key != undefined) {
- modules.comments.get(key).forEach(article.appendChild.bind(article));
+ if(comments) {
+ modules.metadata.getComments(article.id)
+ .forEach(article.appendChild.bind(article));
}
} else {
console.log('No content found for this article');
@@ -58,16 +58,17 @@ function DomRenderer(modules) {
modules.dom.make('header', {}, [
modules.dom.make('a', {href: url}, [
modules.dom.make('h1', {innerText: blog.articles[key].title})
- ])
+ ]),
+ modules.metadata.get(key)
]),
div
- ].concat(limit != undefined ? [] : modules.comments.get(key)));
+ ].concat(limit != undefined ? [] : modules.metadata.getComments(key)));
}
function pageTitle(tag, all) {
if(tag != undefined) {
- var template = blog.wording[all ? 'allTaggedPage' : 'latestTaggedPage'];
- return template.replace(/([^$]|^)\$(?:{tag}|tag([^a-zA-Z]|$))/, '$1' + tag + '$2');
+ var template = all ? 'allTaggedPage' : 'latestTaggedPage';
+ return modules.template.render(template, {tag: tag});
} else {
return blog.wording[all ? 'allPage' : 'latestPage'];
}
diff --git a/share/js/main.js b/share/js/main.js
index 4659256..d9fd9d1 100644
--- a/share/js/main.js
+++ b/share/js/main.js
@@ -5,9 +5,10 @@ window.addEventListener('load', function() {
var fun = unitJS.Fun();
var md = new Remarkable(remarkableConfig);
md.block.ruler.enable(['footnote']);
- var comments = Comments({async: async, cache: cache, dom: dom});
- var domRenderer = DomRenderer({comments: comments, fun: fun, md: md, dom: dom});
- var navigation = Navigation({async: async, cache: cache, fun: fun, md: md, dom: dom, domRenderer: domRenderer});
+ var template = Template();
+ var metadata = Metadata({async: async, cache: cache, dom: dom, fun:fun, template: template});
+ var domRenderer = DomRenderer({dom: dom, fun: fun, md: md, metadata: metadata, template: template});
+ var navigation = Navigation({async: async, cache: cache, dom: dom, domRenderer: domRenderer, fun: fun, md: md});
domRenderer.replaceMarkdown();
navigation.hijackLinks();
});
diff --git a/share/js/comments.js b/share/js/metadata.js
similarity index 50%
rename from share/js/comments.js
rename to share/js/metadata.js
index 37fa297..675b0d2 100644
--- a/share/js/comments.js
+++ b/share/js/metadata.js
@@ -1,4 +1,4 @@
-function Comments(modules) {
+function Metadata(modules) {
var comments = modules.cache.make(function(threadId) {
return modules.async.bind(
modules.async.http({method: 'GET', url: url(threadId)}),
@@ -16,14 +16,15 @@ function Comments(modules) {
);
})
return {
- get: get
+ get: get,
+ getComments: getComments
};
function url(threadId) {
return blog.path.commentsAt + '/api/v1/statuses/' + threadId + '/context';
}
- function get(articleKey) {
+ function getComments(articleKey) {
var threadId = blog.articles[articleKey].metadata.comments;
if(blog.path.commentsAt != undefined && threadId != undefined) {
var ul = modules.dom.make('ul');
@@ -55,15 +56,58 @@ function Comments(modules) {
function render(comments) {
return comments.descendants.map(function(descendant) {
return modules.dom.make('li', {}, [
- modules.dom.make('cite', {}, [
- modules.dom.make('a', {
- href: descendant.account.url,
- innerText: descendant.account.username
- }),
- modules.dom.make('p', {innerText: descendant.created_at}),
- modules.dom.make('div', {innerHTML: descendant.content})
- ])
+ modules.dom.make('div', {
+ innerHTML: modules.template.render('metadata', {
+ author: author(descendant.account.url, descendant.account.username),
+ date: date(descendant.created_at)
+ })
+ }),
+ modules.dom.make('div', {innerHTML: descendant.content})
]);
});
}
+
+ function author(key, name) {
+ var authorUrl = key;
+ if(blog.articles[key] != undefined) {
+ authorUrl = blog.articles[key].metadata.author;
+ }
+ if(authorUrl) {
+ var author = name || authorUrl.replace(/.*\//, '');
+ return '' + author + '';
+ }
+ }
+
+ function date(key) {
+ if(blog.articles[key] != undefined) {
+ var date = new Date(blog.articles[key].metadata.date * 1000);
+ } else {
+ var date = new Date(key);
+ }
+ var format = blog.wording.dateFormat;
+ if(format[0] != '[') {
+ if(format[0] != '"') {
+ format = '"' + format + '"';
+ }
+ format = '[' + format + ']';
+ }
+ return Date.prototype.toLocaleDateString.apply(date, JSON.parse(format));
+ }
+
+ function tags(key) {
+ var tags = blog.articles[key].tagged;
+ return tags.length < 1 ? null : tags.map(function(tag) {
+ return '' + tag + '';
+ }).join(', ');
+ }
+
+ function get(key) {
+ return modules.dom.make('div', {
+ innerHTML: modules.template.render('metadata', {
+ author: author(key),
+ date: date(key),
+ tags: tags(key)
+ })
+ });
+ }
}
diff --git a/share/js/template.js b/share/js/template.js
new file mode 100644
index 0000000..aed435a
--- /dev/null
+++ b/share/js/template.js
@@ -0,0 +1,35 @@
+function Template() {
+ return {
+ render: render
+ };
+
+ function render(template, environment) {
+ if(blog.wording[template] != undefined) {
+ var template = blog.wording[template];
+ }
+ template = template.replace(/\${\?((?:[^?]|\?[^}])*)\?}/g, renderSub(environment));
+ var failed = [false];
+ var result = template.replace(
+ /([^$]|^)\$(?:{(\w+)}|(\w+)\b)/g,
+ substitute(environment, failed)
+ );
+ return failed[0] ? null : result;
+ }
+
+ function renderSub(environment) {
+ return function(_, sub) {
+ return render(sub, environment) || '';
+ };
+ }
+
+ function substitute(environment, failed) {
+ return function(_, before, bracketed, raw) {
+ var replaced = environment[bracketed || raw];
+ if(replaced != undefined) {
+ return before + replaced;
+ } else {
+ failed[0] = true;
+ }
+ }
+ }
+}
diff --git a/src/Blog/Wording.hs b/src/Blog/Wording.hs
index 9d3023e..f61b61f 100644
--- a/src/Blog/Wording.hs
+++ b/src/Blog/Wording.hs
@@ -28,22 +28,25 @@ data Wording = Wording {
, allPage :: Text
, allTaggedPage :: Template
, commentsSection :: Text
+ , dateFormat :: Text
, latestLink :: Text
, latestPage :: Text
, latestTaggedPage :: Template
+ , metadata :: Text
, tagsList :: Text
}
keys :: [String]
keys = [
- "allLink", "allPage", "allTaggedPage", "commentsSection"
- , "latestLink", "latestPage", "latestTaggedPage", "tagsList"
+ "allLink", "allPage", "allTaggedPage", "commentsSection", "dateFormat"
+ , "latestLink", "latestPage", "latestTaggedPage", "metadata", "tagsList"
]
values :: [Wording -> Text]
values = [
allLink, allPage, showTemplate . allTaggedPage, commentsSection
- , latestLink, latestPage, showTemplate . latestTaggedPage, tagsList
+ , dateFormat, latestLink, latestPage, showTemplate . latestTaggedPage
+ , metadata, tagsList
]
texts :: Wording -> [Text]
@@ -97,8 +100,10 @@ build arguments = do
, allPage = wording ! "allPage"
, allTaggedPage
, commentsSection = wording ! "commentsSection"
+ , dateFormat = wording ! "dateFormat"
, latestLink = wording ! "latestLink"
, latestPage = wording ! "latestPage"
, latestTaggedPage
+ , metadata = wording ! "metadata"
, tagsList = wording ! "tagsList"
}
diff --git a/src/Dom.hs b/src/Dom.hs
index 0ed66df..61067ae 100644
--- a/src/Dom.hs
+++ b/src/Dom.hs
@@ -28,7 +28,7 @@ class Page a where
content :: a -> HtmlGenerator ()
instance Page Article where
- card (Article {title, metadata}) = do
+ card (Article {title, Article.metadata}) = do
description <- getDescription (Map.lookup "summary" metadata)
makeCard title (pack description) (Map.lookup "featuredImage" metadata)
where
@@ -57,7 +57,7 @@ instance Page ArticlesList where
article :: Bool -> Article -> HtmlGenerator ()
article raw (Article {key, body, title}) = do
url <- absoluteLink . (> key <.> extension) <$> (Blog.get $path.$articlesPath)
- article_ (do
+ article_ [id_ $ pack key] (do
header_ (do
a_ [href_ . pack $ url] . h1_ $ toHtml title
)
@@ -76,7 +76,9 @@ makeCard title description image = do
maybeImage = maybe (return ()) (og "image" . pack)
tag :: String -> HtmlGenerator ()
-tag tagName = li_ (a_ [href_ . pack $ absoluteLink tagName] $ toHtml tagName)
+tag tagName = li_ (
+ a_ [href_ . pack $ absoluteLink tagName, class_ "tag"] $ toHtml tagName
+ )
defaultBanner :: HtmlGenerator ()
defaultBanner = do