Implement metadata for articles and comments
This commit is contained in:
parent
e9dad0fc3d
commit
2c6be28d76
7 changed files with 120 additions and 30 deletions
|
@ -2,7 +2,9 @@ allLink = See all
|
||||||
allPage = All articles
|
allPage = All articles
|
||||||
allTaggedPage = All articles tagged ${tag}
|
allTaggedPage = All articles tagged ${tag}
|
||||||
commentsSection = Comments
|
commentsSection = Comments
|
||||||
|
dateFormat = en-US
|
||||||
latestLink = See only latest
|
latestLink = See only latest
|
||||||
latestPage = Latest articles
|
latestPage = Latest articles
|
||||||
latestTaggedPage = Latest articles tagged ${tag}
|
latestTaggedPage = Latest articles tagged ${tag}
|
||||||
|
metadata = ${?by ${author} ?}on ${date}${? tagged ${tags}?}
|
||||||
tagsList = Tags
|
tagsList = Tags
|
||||||
|
|
|
@ -8,9 +8,7 @@ function DomRenderer(modules) {
|
||||||
function replaceMarkdown() {
|
function replaceMarkdown() {
|
||||||
var div = document.getElementById('contents');
|
var div = document.getElementById('contents');
|
||||||
if(div.children[0] && div.children[0].tagName.toLowerCase() == 'article') {
|
if(div.children[0] && div.children[0].tagName.toLowerCase() == 'article') {
|
||||||
var re = new RegExp('/' + blog.path.articlesPath + '/([^.]+)\.html');
|
convertArticle(div.children[0], true);
|
||||||
var key = decodeURI(document.location.pathname.replace(re, '$1'));
|
|
||||||
convertArticle(div.children[0], key);
|
|
||||||
} else {
|
} else {
|
||||||
var articles = div.getElementsByClassName('articles')[0];
|
var articles = div.getElementsByClassName('articles')[0];
|
||||||
if(articles != undefined) {
|
if(articles != undefined) {
|
||||||
|
@ -23,13 +21,15 @@ function DomRenderer(modules) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertArticle(article, key) {
|
function convertArticle(article, comments) {
|
||||||
var header = article.getElementsByTagName('header')[0];
|
var header = article.getElementsByTagName('header')[0];
|
||||||
|
header.appendChild(modules.metadata.get(article.id));
|
||||||
var text = article.getElementsByTagName('pre')[0];
|
var text = article.getElementsByTagName('pre')[0];
|
||||||
if(text != undefined) {
|
if(text != undefined) {
|
||||||
article.replaceChild(getDiv(text.innerText), text);
|
article.replaceChild(getDiv(text.innerText), text);
|
||||||
if(key != undefined) {
|
if(comments) {
|
||||||
modules.comments.get(key).forEach(article.appendChild.bind(article));
|
modules.metadata.getComments(article.id)
|
||||||
|
.forEach(article.appendChild.bind(article));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.log('No content found for this article');
|
console.log('No content found for this article');
|
||||||
|
@ -58,16 +58,17 @@ function DomRenderer(modules) {
|
||||||
modules.dom.make('header', {}, [
|
modules.dom.make('header', {}, [
|
||||||
modules.dom.make('a', {href: url}, [
|
modules.dom.make('a', {href: url}, [
|
||||||
modules.dom.make('h1', {innerText: blog.articles[key].title})
|
modules.dom.make('h1', {innerText: blog.articles[key].title})
|
||||||
])
|
]),
|
||||||
|
modules.metadata.get(key)
|
||||||
]),
|
]),
|
||||||
div
|
div
|
||||||
].concat(limit != undefined ? [] : modules.comments.get(key)));
|
].concat(limit != undefined ? [] : modules.metadata.getComments(key)));
|
||||||
}
|
}
|
||||||
|
|
||||||
function pageTitle(tag, all) {
|
function pageTitle(tag, all) {
|
||||||
if(tag != undefined) {
|
if(tag != undefined) {
|
||||||
var template = blog.wording[all ? 'allTaggedPage' : 'latestTaggedPage'];
|
var template = all ? 'allTaggedPage' : 'latestTaggedPage';
|
||||||
return template.replace(/([^$]|^)\$(?:{tag}|tag([^a-zA-Z]|$))/, '$1' + tag + '$2');
|
return modules.template.render(template, {tag: tag});
|
||||||
} else {
|
} else {
|
||||||
return blog.wording[all ? 'allPage' : 'latestPage'];
|
return blog.wording[all ? 'allPage' : 'latestPage'];
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,10 @@ window.addEventListener('load', function() {
|
||||||
var fun = unitJS.Fun();
|
var fun = unitJS.Fun();
|
||||||
var md = new Remarkable(remarkableConfig);
|
var md = new Remarkable(remarkableConfig);
|
||||||
md.block.ruler.enable(['footnote']);
|
md.block.ruler.enable(['footnote']);
|
||||||
var comments = Comments({async: async, cache: cache, dom: dom});
|
var template = Template();
|
||||||
var domRenderer = DomRenderer({comments: comments, fun: fun, md: md, dom: dom});
|
var metadata = Metadata({async: async, cache: cache, dom: dom, fun:fun, template: template});
|
||||||
var navigation = Navigation({async: async, cache: cache, fun: fun, md: md, dom: dom, domRenderer: domRenderer});
|
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();
|
domRenderer.replaceMarkdown();
|
||||||
navigation.hijackLinks();
|
navigation.hijackLinks();
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
function Comments(modules) {
|
function Metadata(modules) {
|
||||||
var comments = modules.cache.make(function(threadId) {
|
var comments = modules.cache.make(function(threadId) {
|
||||||
return modules.async.bind(
|
return modules.async.bind(
|
||||||
modules.async.http({method: 'GET', url: url(threadId)}),
|
modules.async.http({method: 'GET', url: url(threadId)}),
|
||||||
|
@ -16,14 +16,15 @@ function Comments(modules) {
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
return {
|
return {
|
||||||
get: get
|
get: get,
|
||||||
|
getComments: getComments
|
||||||
};
|
};
|
||||||
|
|
||||||
function url(threadId) {
|
function url(threadId) {
|
||||||
return blog.path.commentsAt + '/api/v1/statuses/' + threadId + '/context';
|
return blog.path.commentsAt + '/api/v1/statuses/' + threadId + '/context';
|
||||||
}
|
}
|
||||||
|
|
||||||
function get(articleKey) {
|
function getComments(articleKey) {
|
||||||
var threadId = blog.articles[articleKey].metadata.comments;
|
var threadId = blog.articles[articleKey].metadata.comments;
|
||||||
if(blog.path.commentsAt != undefined && threadId != undefined) {
|
if(blog.path.commentsAt != undefined && threadId != undefined) {
|
||||||
var ul = modules.dom.make('ul');
|
var ul = modules.dom.make('ul');
|
||||||
|
@ -55,15 +56,58 @@ function Comments(modules) {
|
||||||
function render(comments) {
|
function render(comments) {
|
||||||
return comments.descendants.map(function(descendant) {
|
return comments.descendants.map(function(descendant) {
|
||||||
return modules.dom.make('li', {}, [
|
return modules.dom.make('li', {}, [
|
||||||
modules.dom.make('cite', {}, [
|
modules.dom.make('div', {
|
||||||
modules.dom.make('a', {
|
innerHTML: modules.template.render('metadata', {
|
||||||
href: descendant.account.url,
|
author: author(descendant.account.url, descendant.account.username),
|
||||||
innerText: descendant.account.username
|
date: date(descendant.created_at)
|
||||||
}),
|
})
|
||||||
modules.dom.make('p', {innerText: descendant.created_at}),
|
}),
|
||||||
modules.dom.make('div', {innerHTML: descendant.content})
|
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 '<a href="' + authorUrl + '">' + author + '</a>';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 '<a class="tag" href="/' + tag + '">' + tag + '</a>';
|
||||||
|
}).join(', ');
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(key) {
|
||||||
|
return modules.dom.make('div', {
|
||||||
|
innerHTML: modules.template.render('metadata', {
|
||||||
|
author: author(key),
|
||||||
|
date: date(key),
|
||||||
|
tags: tags(key)
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
35
share/js/template.js
Normal file
35
share/js/template.js
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,22 +28,25 @@ data Wording = Wording {
|
||||||
, allPage :: Text
|
, allPage :: Text
|
||||||
, allTaggedPage :: Template
|
, allTaggedPage :: Template
|
||||||
, commentsSection :: Text
|
, commentsSection :: Text
|
||||||
|
, dateFormat :: Text
|
||||||
, latestLink :: Text
|
, latestLink :: Text
|
||||||
, latestPage :: Text
|
, latestPage :: Text
|
||||||
, latestTaggedPage :: Template
|
, latestTaggedPage :: Template
|
||||||
|
, metadata :: Text
|
||||||
, tagsList :: Text
|
, tagsList :: Text
|
||||||
}
|
}
|
||||||
|
|
||||||
keys :: [String]
|
keys :: [String]
|
||||||
keys = [
|
keys = [
|
||||||
"allLink", "allPage", "allTaggedPage", "commentsSection"
|
"allLink", "allPage", "allTaggedPage", "commentsSection", "dateFormat"
|
||||||
, "latestLink", "latestPage", "latestTaggedPage", "tagsList"
|
, "latestLink", "latestPage", "latestTaggedPage", "metadata", "tagsList"
|
||||||
]
|
]
|
||||||
|
|
||||||
values :: [Wording -> Text]
|
values :: [Wording -> Text]
|
||||||
values = [
|
values = [
|
||||||
allLink, allPage, showTemplate . allTaggedPage, commentsSection
|
allLink, allPage, showTemplate . allTaggedPage, commentsSection
|
||||||
, latestLink, latestPage, showTemplate . latestTaggedPage, tagsList
|
, dateFormat, latestLink, latestPage, showTemplate . latestTaggedPage
|
||||||
|
, metadata, tagsList
|
||||||
]
|
]
|
||||||
|
|
||||||
texts :: Wording -> [Text]
|
texts :: Wording -> [Text]
|
||||||
|
@ -97,8 +100,10 @@ build arguments = do
|
||||||
, allPage = wording ! "allPage"
|
, allPage = wording ! "allPage"
|
||||||
, allTaggedPage
|
, allTaggedPage
|
||||||
, commentsSection = wording ! "commentsSection"
|
, commentsSection = wording ! "commentsSection"
|
||||||
|
, dateFormat = wording ! "dateFormat"
|
||||||
, latestLink = wording ! "latestLink"
|
, latestLink = wording ! "latestLink"
|
||||||
, latestPage = wording ! "latestPage"
|
, latestPage = wording ! "latestPage"
|
||||||
, latestTaggedPage
|
, latestTaggedPage
|
||||||
|
, metadata = wording ! "metadata"
|
||||||
, tagsList = wording ! "tagsList"
|
, tagsList = wording ! "tagsList"
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ class Page a where
|
||||||
content :: a -> HtmlGenerator ()
|
content :: a -> HtmlGenerator ()
|
||||||
|
|
||||||
instance Page Article where
|
instance Page Article where
|
||||||
card (Article {title, metadata}) = do
|
card (Article {title, Article.metadata}) = do
|
||||||
description <- getDescription (Map.lookup "summary" metadata)
|
description <- getDescription (Map.lookup "summary" metadata)
|
||||||
makeCard title (pack description) (Map.lookup "featuredImage" metadata)
|
makeCard title (pack description) (Map.lookup "featuredImage" metadata)
|
||||||
where
|
where
|
||||||
|
@ -57,7 +57,7 @@ instance Page ArticlesList where
|
||||||
article :: Bool -> Article -> HtmlGenerator ()
|
article :: Bool -> Article -> HtmlGenerator ()
|
||||||
article raw (Article {key, body, title}) = do
|
article raw (Article {key, body, title}) = do
|
||||||
url <- absoluteLink . (</> key <.> extension) <$> (Blog.get $path.$articlesPath)
|
url <- absoluteLink . (</> key <.> extension) <$> (Blog.get $path.$articlesPath)
|
||||||
article_ (do
|
article_ [id_ $ pack key] (do
|
||||||
header_ (do
|
header_ (do
|
||||||
a_ [href_ . pack $ url] . h1_ $ toHtml title
|
a_ [href_ . pack $ url] . h1_ $ toHtml title
|
||||||
)
|
)
|
||||||
|
@ -76,7 +76,9 @@ makeCard title description image = do
|
||||||
maybeImage = maybe (return ()) (og "image" . pack)
|
maybeImage = maybe (return ()) (og "image" . pack)
|
||||||
|
|
||||||
tag :: String -> HtmlGenerator ()
|
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 :: HtmlGenerator ()
|
||||||
defaultBanner = do
|
defaultBanner = do
|
||||||
|
|
Loading…
Reference in a new issue