diff --git a/layout/comment/changyan.jsx b/layout/comment/changyan.jsx index 2470d92..71d05ac 100644 --- a/layout/comment/changyan.jsx +++ b/layout/comment/changyan.jsx @@ -22,9 +22,11 @@ class ChangeYan extends Component { } module.exports = cacheComponent(ChangeYan, 'comment.changyan', props => { + const { comment, page } = props; + return { - appId: props.appid, - conf: props.conf, - path: props.page.path + appId: comment.appid, + conf: comment.conf, + path: page.path }; }); diff --git a/layout/comment/disqus.jsx b/layout/comment/disqus.jsx index 78b1e0d..9a4de65 100644 --- a/layout/comment/disqus.jsx +++ b/layout/comment/disqus.jsx @@ -32,10 +32,12 @@ class Disqus extends Component { } module.exports = cacheComponent(Disqus, 'comment.disqus', props => { + const { comment, page } = props; + return { - path: props.page.path, - shortname: props.shortname, - disqusId: props.page.disqusId, - permalink: props.page.permalink + path: page.path, + shortname: comment.shortname, + disqusId: page.disqusId, + permalink: page.permalink }; }); diff --git a/layout/comment/facebook.jsx b/layout/comment/facebook.jsx index 849f3fc..f486e10 100644 --- a/layout/comment/facebook.jsx +++ b/layout/comment/facebook.jsx @@ -10,7 +10,7 @@ class Facebook extends Component { var js, fjs = d.getElementsByTagName(s)[0]; if (d.getElementById(id)) return; js = d.createElement(s); js.id = id; - js.src = "//connect.facebook.net/${(language || 'en').split('-').join('_')}/sdk.js#xfbml=1&version=v2.8"; + js.src = "//connect.facebook.net/${language.split('-').join('_')}/sdk.js#xfbml=1&version=v2.8"; fjs.parentNode.insertBefore(js, fjs); }(document, 'script', 'facebook-jssdk'));`; return @@ -21,8 +21,10 @@ class Facebook extends Component { } module.exports = cacheComponent(Facebook, 'comment.facebook', props => { + const { config, page } = props; + return { - language: props.language, - permalink: props.page.permalink + language: page.lang || page.language || config.language || 'en', + permalink: page.permalink }; }); diff --git a/layout/comment/gitalk.jsx b/layout/comment/gitalk.jsx index ddee852..e09de34 100644 --- a/layout/comment/gitalk.jsx +++ b/layout/comment/gitalk.jsx @@ -47,18 +47,20 @@ class Gitalk extends Component { } module.exports = cacheComponent(Gitalk, 'comment.gitalk', props => { + const { helper, comment } = props; + // FIXME: config name change const id = crypto.createHash('md5').update(props.page.path).digest('hex'); return { id, - repo: props.repo, - owner: props.owner, - admin: props.admin, - clientId: props.clientId, - clientSecret: props.clientSecret, - createIssueManually: props.createIssueManually, - distractionFreeMode: props.distractionFreeMode, - cssUrl: props.cdn('gitalk', '1.4.1', 'dist/gitalk.css'), - jsUrl: props.cdn('gitalk', '1.4.1', 'dist/gitalk.min.js') + repo: comment.repo, + owner: comment.owner, + admin: comment.admin, + clientId: comment.clientId, + clientSecret: comment.clientSecret, + createIssueManually: comment.createIssueManually, + distractionFreeMode: comment.distractionFreeMode, + cssUrl: helper.cdn('gitalk', '1.4.1', 'dist/gitalk.css'), + jsUrl: helper.cdn('gitalk', '1.4.1', 'dist/gitalk.min.js') }; }); diff --git a/layout/comment/gitment.jsx b/layout/comment/gitment.jsx index 70e1f7a..eefd243 100644 --- a/layout/comment/gitment.jsx +++ b/layout/comment/gitment.jsx @@ -41,12 +41,14 @@ class Gitment extends Component { } module.exports = cacheComponent(Gitment, 'comment.gitment', props => { + const { comment } = props; + const id = crypto.createHash('md5').update(props.page.path).digest('hex'); return { id, - repo: props.repo, - owner: props.owner, - clientId: props.client_id, - clientSecret: props.client_secret + repo: comment.repo, + owner: comment.owner, + clientId: comment.client_id, + clientSecret: comment.client_secret }; }); diff --git a/layout/comment/isso.jsx b/layout/comment/isso.jsx index 431896b..0b8bce2 100644 --- a/layout/comment/isso.jsx +++ b/layout/comment/isso.jsx @@ -20,7 +20,9 @@ class Isso extends Component { } module.exports = cacheComponent(Isso, 'comment.isso', props => { + const { comment } = props; + return { - url: props.url + url: comment.url }; }); diff --git a/layout/comment/livere.jsx b/layout/comment/livere.jsx index d8e5670..b130431 100644 --- a/layout/comment/livere.jsx +++ b/layout/comment/livere.jsx @@ -31,7 +31,9 @@ class LiveRe extends Component { } module.exports = cacheComponent(LiveRe, 'comment.livere', props => { + const { comment } = props; + return { - uid: props.uid + uid: comment.uid }; }); diff --git a/layout/comment/valine.jsx b/layout/comment/valine.jsx index 7ca05c3..3e2e07e 100644 --- a/layout/comment/valine.jsx +++ b/layout/comment/valine.jsx @@ -30,11 +30,13 @@ class Valine extends Component { } module.exports = cacheComponent(Valine, 'comment.valine', props => { + const { comment } = props; + return { - appId: props.app_id, - appKey: props.app_key, - notify: props.notify, - verify: props.verify, - placeholder: props.placeholder + appId: comment.app_id, + appKey: comment.app_key, + notify: comment.notify, + verify: comment.verify, + placeholder: comment.placeholder }; }); diff --git a/layout/common/article.ejs b/layout/common/article.ejs deleted file mode 100644 index a7e72d8..0000000 --- a/layout/common/article.ejs +++ /dev/null @@ -1,127 +0,0 @@ -<% post = post(); %> -
- <% if (has_thumbnail(post)) { %> -
- <%- index ? ' class="image is-7by1"> - <%= post.title %> - <%- index ? '' : '' %> -
- <% } %> -
- <% if (post.layout != 'page') { %> - - <% } %> -

- <% if (index) { %> - <%= post.title %> - <% } else { %> - <%= post.title %> - <% } %> -

-
- <%- index && post.excerpt ? post.excerpt : post.content %> -
- <% if (!index && post.tags && post.tags.length) { %> -
-
-
- # - <%- list_tags(post.tags, { - class: 'has-link-grey ', - show_count: false, - style: 'link' - }) %> -
-
-
- <% } %> - <% if (index && post.excerpt) { %> - - <% } %> - <% if (!index && has_config('share.type')) { %> - <%- _partial('share/' + get_config('share.type')) %> - <% } %> -
-
- -<% const services = has_config('donate') ? get_config('donate') : []; %> -<% if (!index && services.length > 0) { %> -
-
- -
- <% for (let service of services) { - const type = get_config_from_obj(service, 'type'); - if (type !== null) { %> - <%- _partial('donate/' + type, { type, service }) %> - <% } - } %> -
-
-
-<% } %> - -<% if (!index && (post.prev || post.next)) { %> -
-
- <% if (post.prev){ %> - - <% } %> - <% if (post.next){ %> - - <% } %> -
-
-<% } %> - -<% if (!index && has_config('comment.type')) { %> -
-
-

<%= __('article.comments') %>

- <%- _partial('comment/' + get_config('comment.type')) %> -
-
-<% } %> \ No newline at end of file diff --git a/layout/common/article.jsx b/layout/common/article.jsx new file mode 100644 index 0000000..ae0efb5 --- /dev/null +++ b/layout/common/article.jsx @@ -0,0 +1,125 @@ +'use strict'; + +const moment = require('moment'); +const { Component, Fragment } = require('inferno'); +const Share = require('./share'); +const Donates = require('./donates'); +const Comment = require('./comment'); + +/** + * Get the word count of a paragraph. + */ +function getWordCount(content) { + content = content.replace(/<\/?[a-z][^>]*>/gi, ''); + content = content.trim(); + return content ? (content.match(/[\u00ff-\uffff]|[a-zA-Z]+/g) || []).length : 0; +} + +module.exports = class extends Component { + render() { + const { config, helper, page, index } = this.props; + const { article, plugins } = config; + const { has_thumbnail, get_thumbnail, url_for, date, date_xml, __, _p } = helper; + + const language = page.lang || page.language || config.language || 'en'; + + return + {/* Main content */} +
+ {/* Thumbnail */} + {has_thumbnail(page) ?
+ {index ? + {page.title + : + {page.title + } +
: null} + {/* Metadata */} +
+ {page.layout !== 'page' ?
+
+ {/* Date */} + + {/* Categories */} + {page.categories && page.categories.length ?
+ {(() => { + const categories = []; + page.categories.forEach((category, i) => { + categories.push({category.name}); + if (i < page.categories.length - 1) { + categories.push('/'); + } + }); + return categories; + })()} +
: null} + {/* Read time */} + {article && article.readtime && article.readtime === true ? + {(() => { + const words = getWordCount(page._content); + const time = moment.duration((words / 150.0) * 60, 'seconds'); + return `${time.locale(language).humanize()} ${__('article.read')} (${__('article.about')} ${words} ${__('article.words')})`; + })()} + : null} + {/* Visitor counter */} + {plugins && plugins.busuanzi === true ? 0') + }}> : null} +
+
: null} + {/* Title */} +

+ {index ? {page.title} : page.title} +

+ {/* Content/Excerpt */} +
+ {index && page.excerpt ? page.excerpt : page.content} +
+ {/* Tags */} + {!index && Array.isArray(page.tags) && page.tags.length ?
+
+
+ # + {page.tags.map(tag => { + return ; + })} +
+
+
: null} + {/* "Read more" button */} + {index && page.excerpt ?
+ +
: null} + {/* Share button */} + {!index ? : null} +
+
+ {/* Donate button */} + {!index ? : null} + {/* Post navigation */} + {!index && (page.prev || page.next) ?
+
+ {page.prev ? : null} + {page.next ? : null} +
+
: null} + {/* Comment */} + {!index ? : null} +
; + } +}; diff --git a/layout/common/article.locals.js b/layout/common/article.locals.js deleted file mode 100644 index ef26cfe..0000000 --- a/layout/common/article.locals.js +++ /dev/null @@ -1,8 +0,0 @@ -module.exports = (ctx, locals) => { - const { index, post } = locals; - return Object.assign(locals, { - index, - post: () => post, - post_id: post._id - }); -} \ No newline at end of file diff --git a/layout/common/comment.jsx b/layout/common/comment.jsx new file mode 100644 index 0000000..4456ccb --- /dev/null +++ b/layout/common/comment.jsx @@ -0,0 +1,30 @@ +'use strict'; + +const logger = require('hexo-log'); +const { Component } = require('inferno'); + +module.exports = class extends Component { + render() { + const { config, page, helper } = this.props; + const { __ } = helper; + const { comment } = config; + if (!comment || typeof comment.type !== 'string') { + return null; + } + + return
+
+

{__('article.comments')}

+ {(() => { + try { + const Comment = require('../comment/' + comment.type); + return ; + } catch (e) { + logger.warn(`Icarus cannot load comment "${comment.type}"`); + return null; + } + })()} +
+
; + } +}; diff --git a/layout/common/donates.jsx b/layout/common/donates.jsx new file mode 100644 index 0000000..9334d39 --- /dev/null +++ b/layout/common/donates.jsx @@ -0,0 +1,34 @@ +'use strict'; + +const logger = require('hexo-log'); +const { Component } = require('inferno'); + +module.exports = class extends Component { + render() { + const { config, helper } = this.props; + const { __ } = helper; + const { donate = [] } = config; + if (!Array.isArray(donate) || !donate.length) { + return null; + } + return
+
+

{__('donate.title')}

+
+ {donate.map(service => { + const type = service.type; + if (typeof type === 'string') { + try { + const Donate = require('../donate/' + type); + return ; + } catch (e) { + logger.warn(`Icarus cannot load donate button "${type}"`); + } + } + return null; + })} +
+
+
; + } +}; diff --git a/layout/common/head.jsx b/layout/common/head.jsx index 7024cb6..ba4d08c 100644 --- a/layout/common/head.jsx +++ b/layout/common/head.jsx @@ -21,7 +21,7 @@ module.exports = class extends Component { highlight } = config; - let hlTheme; + let hlTheme, images; if (highlight && highlight.enable === false) { hlTheme = null; } else if (article && article.highlight && article.hightlight.theme) { @@ -30,13 +30,21 @@ module.exports = class extends Component { hlTheme = 'atom-one-light'; } - const images = []; - if (page.content && page.content.includes(']*src=['"]([^'"]+)([^>]*>)/gi; while ((img = imgPattern.exec(page.content)) !== null) { images.push(img[1]); } + } else { + images = [url_for('/images/og_image.png')]; } return diff --git a/layout/common/plugins.jsx b/layout/common/plugins.jsx index 5e949e3..901cf1f 100644 --- a/layout/common/plugins.jsx +++ b/layout/common/plugins.jsx @@ -19,8 +19,8 @@ module.exports = class extends Component { return ; } catch (e) { logger.warn(`Icarus cannot load plugin "${name}"`); + return null; } - return null; })}
; } diff --git a/layout/common/share.jsx b/layout/common/share.jsx new file mode 100644 index 0000000..3197080 --- /dev/null +++ b/layout/common/share.jsx @@ -0,0 +1,22 @@ +'use strict'; + +const logger = require('hexo-log'); +const { Component } = require('inferno'); + +module.exports = class extends Component { + render() { + const { config, page, helper } = this.props; + const { share } = config; + if (!share || typeof share.type !== 'string') { + return null; + } + + try { + const Share = require('../share/' + share.type); + return ; + } catch (e) { + logger.warn(`Icarus cannot load share button "${share.type}"`); + return null; + } + } +}; diff --git a/layout/donate/alipay.jsx b/layout/donate/alipay.jsx index ec6ba04..71925e3 100644 --- a/layout/donate/alipay.jsx +++ b/layout/donate/alipay.jsx @@ -23,9 +23,11 @@ class Alipay extends Component { } module.exports = cacheComponent(Alipay, 'donate.alipay', props => { + const { donate, helper } = props; + return { - qrcode: props.qrcode, - title: props.__('donate.' + props.type), - url_for: props.url_for + qrcode: donate.qrcode, + title: helper.__('donate.' + donate.type), + url_for: helper.url_for }; }); diff --git a/layout/donate/patreon.jsx b/layout/donate/patreon.jsx index c713cac..abaa13b 100644 --- a/layout/donate/patreon.jsx +++ b/layout/donate/patreon.jsx @@ -22,9 +22,11 @@ class Patreon extends Component { } module.exports = cacheComponent(Patreon, 'donate.petreon', props => { + const { donate, helper } = props; + return { - url: props.url, - title: props.__('donate.' + props.type), - url_for: props.url_for + url: donate.url, + title: helper.__('donate.' + donate.type), + url_for: helper.url_for }; }); diff --git a/layout/donate/paypal.jsx b/layout/donate/paypal.jsx index 2eea687..bebe8d0 100644 --- a/layout/donate/paypal.jsx +++ b/layout/donate/paypal.jsx @@ -29,10 +29,12 @@ class Paypal extends Component { } module.exports = cacheComponent(Paypal, 'donate.paypal', props => { + const { donate, helper } = props; + return { - business: props.business, - currencyCode: props.currency_code, - title: props.__('donate.' + props.type), - url_for: props.url_for + business: donate.business, + currencyCode: donate.currency_code, + title: helper.__('donate.' + donate.type), + url_for: helper.url_for }; }); diff --git a/layout/donate/wechat.jsx b/layout/donate/wechat.jsx index 3851eba..a95ee27 100644 --- a/layout/donate/wechat.jsx +++ b/layout/donate/wechat.jsx @@ -23,9 +23,11 @@ class Wechat extends Component { } module.exports = cacheComponent(Wechat, 'donate.wechat', props => { + const { donate, helper } = props; + return { - qrcode: props.qrcode, - title: props.__('donate.' + props.type), - url_for: props.url_for + qrcode: donate.qrcode, + title: helper.__('donate.' + donate.type), + url_for: helper.url_for }; }); diff --git a/layout/share/addthis.jsx b/layout/share/addthis.jsx index 224057e..9764e6c 100644 --- a/layout/share/addthis.jsx +++ b/layout/share/addthis.jsx @@ -20,7 +20,9 @@ class AddThis extends Component { } module.exports = cacheComponent(AddThis, 'share.addthis', props => { + const { share } = props; + return { - installUrl: props.install_url + installUrl: share.install_url }; }); diff --git a/layout/share/sharejs.jsx b/layout/share/sharejs.jsx index 0c76cb6..80af6fb 100644 --- a/layout/share/sharejs.jsx +++ b/layout/share/sharejs.jsx @@ -15,8 +15,10 @@ class ShareJs extends Component { } module.exports = cacheComponent(ShareJs, 'share.sharejs', props => { + const { helper } = props; + return { - cssUrl: props.cdn('social-share.js', '1.0.16', 'dist/css/share.min.css'), - jsUrl: props.cdn('social-share.js', '1.0.16', 'dist/js/social-share.min.js') + cssUrl: helper.cdn('social-share.js', '1.0.16', 'dist/css/share.min.css'), + jsUrl: helper.cdn('social-share.js', '1.0.16', 'dist/js/social-share.min.js') }; }); diff --git a/layout/share/sharethis.jsx b/layout/share/sharethis.jsx index 99ea228..8be17f2 100644 --- a/layout/share/sharethis.jsx +++ b/layout/share/sharethis.jsx @@ -20,7 +20,9 @@ class ShareThis extends Component { } module.exports = cacheComponent(ShareThis, 'share.sharethis', props => { + const { share } = props; + return { - installUrl: props.install_url + installUrl: share.install_url }; });