diff --git a/README.md b/README.md index 1b3bc74..11df54e 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@
A simple, delicate, and modern theme for the static site generator Hexo.
-Preview | +Preview | Documentation | Download
@@ -51,6 +51,13 @@ Share plugins - [Share.js](http://ppoffice.github.io/hexo-theme-icarus/Plugins/Share/share-js-share-plugin/) - [ShareThis](http://ppoffice.github.io/hexo-theme-icarus/Plugins/Share/sharethis-share-plugin/) +Donation Buttons + +- [Alipay](http://ppoffice.github.io/hexo-theme-icarus/Plugins/Donation/making-money-off-your-blog-with-donation-buttons/#Alipay) +- [Wechat](http://ppoffice.github.io/hexo-theme-icarus/Plugins/Donation/making-money-off-your-blog-with-donation-buttons/#Wechat) +- [Paypal](http://ppoffice.github.io/hexo-theme-icarus/Plugins/Donation/making-money-off-your-blog-with-donation-buttons/#Paypal) +- [Patreon](http://ppoffice.github.io/hexo-theme-icarus/Plugins/Donation/making-money-off-your-blog-with-donation-buttons/#Patreon) + Other plugins - [Hexo Tag Plugin](http://ppoffice.github.io/hexo-theme-icarus/Configuration/Posts/hexo-built-in-tag-helpers/) @@ -60,7 +67,7 @@ Other plugins **Rich Code Highlight Theme Choices** -Icarus directly import code highlight themes from the [highlight.js](https://highlightjs.org/) package, and makes more than +Icarus directly import code highlight themes from the [highlight.js](https://highlightjs.org/) package, and makes more than 70 highlight themes available to you. @@ -73,7 +80,7 @@ Icarus directly import code highlight themes from the [highlight.js](https://hig **Elastic Theme Configuration** -In addition to the minimalistic and easy-to-understand configuration design, Icarus allows you to set configurations on a +In addition to the minimalistic and easy-to-understand configuration design, Icarus allows you to set configurations on a per-page basis with the ability to merge and override partial configurations.
diff --git a/includes/common/ConfigGenerator.js b/includes/common/ConfigGenerator.js index ab218be..961b09e 100644 --- a/includes/common/ConfigGenerator.js +++ b/includes/common/ConfigGenerator.js @@ -32,6 +32,28 @@ const YAML_SCHEMA = new Schema({ ] }); +function appendDoc(spec, defaults) { + if (defaults === null) { + return null; + } + if (is.array(defaults) && spec.hasOwnProperty('*')) { + return defaults.map(value => appendDoc(spec['*'], value)); + } else if (is.object(defaults)) { + const _defaults = {}; + for (let key in defaults) { + if (spec.hasOwnProperty(key) && spec[key].hasOwnProperty(doc)) { + let i = 0; + for (let line of spec[key][doc].split('\n')) { + _defaults['#' + key + i++] = line; + } + } + _defaults[key] = appendDoc(spec.hasOwnProperty(key) ? spec[key] : {}, defaults[key]); + } + return _defaults; + } + return defaults; +} + function generate(spec, parentConfig = null) { if (!is.spec(spec)) { return UNDEFINED; @@ -40,7 +62,7 @@ function generate(spec, parentConfig = null) { return UNDEFINED; } if (spec.hasOwnProperty(defaultValue)) { - return spec[defaultValue]; + return appendDoc(spec, spec[defaultValue]); } const types = is.array(spec[type]) ? spec[type] : [spec[type]]; if (types.includes('object')) { @@ -54,16 +76,10 @@ function generate(spec, parentConfig = null) { if (defaults === UNDEFINED) { defaults = {}; } - if (spec[key].hasOwnProperty(doc)) { - let i = 0; - for (let line of spec[key][doc].split('\n')) { - defaults['#' + key + i++] = line; - } - } defaults[key] = value; } } - return defaults; + return appendDoc(spec, defaults); } else if (types.includes('array') && spec.hasOwnProperty('*')) { return [generate(spec['*'], {})]; } diff --git a/includes/helpers/cdn.js b/includes/helpers/cdn.js index 9a7b9d8..8499e5f 100644 --- a/includes/helpers/cdn.js +++ b/includes/helpers/cdn.js @@ -49,6 +49,9 @@ module.exports = function (hexo) { if (_package === 'pace-js') { _package = 'pace'; } + if (_package === 'clipboard') { + _package = 'clipboard.js'; + } } if (provider !== null && cdn_providers.hasOwnProperty(provider)) { provider = cdn_providers[provider]; diff --git a/includes/helpers/site.js b/includes/helpers/site.js index ef50a0d..7641295 100644 --- a/includes/helpers/site.js +++ b/includes/helpers/site.js @@ -3,13 +3,17 @@ * * @example * <%- is_same_link(url_a, url_b) %> +* <%- get_domain(url) %> * <%- post_count() %> * <%- category_count() %> * <%- tag_count() %> * <%- duration() %> * <%- word_count(content) %> +* <%- md5(data) %> */ +const URL = require('url').URL; const moment = require('moment'); +const crypto = require('crypto'); module.exports = function (hexo) { hexo.extend.helper.register('is_same_link', function (a, b) { @@ -23,6 +27,11 @@ module.exports = function (hexo) { return santize(this.url_for(a)) == santize(this.url_for(b)); }); + hexo.extend.helper.register('get_domain', function (link) { + const url = new URL(link); + return url.hostname; + }); + hexo.extend.helper.register('post_count', function () { return this.site.posts.length; }); @@ -50,4 +59,8 @@ module.exports = function (hexo) { content = content.trim(); return content ? (content.match(/[\u00ff-\uffff]|[a-zA-Z]+/g) || []).length : 0; }); + + hexo.extend.helper.register('md5', function (data) { + return crypto.createHash('md5').update(data).digest("hex") + }); } \ No newline at end of file diff --git a/includes/specs/comment.spec.js b/includes/specs/comment.spec.js index a169a18..c844ba6 100644 --- a/includes/specs/comment.spec.js +++ b/includes/specs/comment.spec.js @@ -24,30 +24,40 @@ const DisqusSpec = { } }; -const GitmentSpec = { +const GitmentGitalkSpec = { owner: { [type]: 'string', - [doc]: 'Your GitHub ID', + [doc]: 'GitHub user ID', [required]: true, - [requires]: comment => comment.type === 'gitment' + [requires]: comment => comment.type === 'gitment' || comment.type === 'gitalk' }, repo: { [type]: 'string', - [doc]: 'The repo to store comments', + [doc]: 'GitHub repo name to store comments', [required]: true, - [requires]: comment => comment.type === 'gitment' + [requires]: comment => comment.type === 'gitment' || comment.type === 'gitalk' }, client_id: { [type]: 'string', - [doc]: 'Your client ID', + [doc]: 'GitHub application client ID', [required]: true, - [requires]: comment => comment.type === 'gitment' + [requires]: comment => comment.type === 'gitment' || comment.type === 'gitalk' }, client_secret: { [type]: 'string', - [doc]: 'Your client secret', + [doc]: 'GitHub application client secret', [required]: true, - [requires]: comment => comment.type === 'gitment' + [requires]: comment => comment.type === 'gitment' || comment.type === 'gitalk' + }, + admin: { + [type]: ['string', 'array'], + [doc]: 'GitHub repo owner and collaborators who can can initialize github issues', + [required]: true, + [requires]: comment => comment.type === 'gitalk', + '*': { + [type]: 'string', + [required]: true + } } }; @@ -112,7 +122,7 @@ module.exports = { }, ...ChangYanSpec, ...DisqusSpec, - ...GitmentSpec, + ...GitmentGitalkSpec, ...IssoSpec, ...LiveReSpec, ...ValineSpec diff --git a/includes/specs/config.spec.js b/includes/specs/config.spec.js index 8812794..57ce280 100644 --- a/includes/specs/config.spec.js +++ b/includes/specs/config.spec.js @@ -17,6 +17,7 @@ module.exports = { article: require('./article.spec'), search: require('./search.spec'), comment: require('./comment.spec'), + donate: require('./donate.spec'), share: require('./share.spec'), sidebar: require('./sidebar.spec'), widgets: require('./widgets.spec'), diff --git a/includes/specs/donate.spec.js b/includes/specs/donate.spec.js new file mode 100644 index 0000000..ed13e5f --- /dev/null +++ b/includes/specs/donate.spec.js @@ -0,0 +1,72 @@ +const { doc, type, defaultValue, required, requires, format } = require('../common/utils').descriptors; + +const DEFAULT_DONATE = [ + { + type: 'alipay', + qrcode: '' + }, + { + type: 'wechat', + qrcode: '' + }, + { + type: 'paypal', + business: '', + currency_code: 'USD' + }, + { + type: 'patreon', + url: '' + } +]; + +const QrcodeSpec = { + qrcode: { + [type]: 'string', + [doc]: 'Qrcode image URL', + [required]: true, + [requires]: donate => donate.type === 'alipay' || donate.type === 'wechat' + } +}; + +const PaypalSpec = { + business: { + [type]: 'string', + [doc]: 'Paypal business ID or email address', + [required]: true, + [requires]: donate => donate.type === 'paypal' + }, + currency_code: { + [type]: 'string', + [doc]: 'Currency code', + [required]: true, + [requires]: donate => donate.type === 'paypal' + } +}; + +const PatreonSpec = { + url: { + [type]: 'string', + [doc]: 'URL to the Patreon page', + [required]: true, + [requires]: donate => donate.type === 'patreon' + } +}; + +module.exports = { + [type]: 'array', + [doc]: 'Donation entries\nhttp://ppoffice.github.io/hexo-theme-icarus/categories/Donation/', + [defaultValue]: DEFAULT_DONATE, + '*': { + [type]: 'object', + [doc]: 'Single donation entry settings', + type: { + [type]: 'string', + [doc]: 'Donation entry name', + [required]: true + }, + ...QrcodeSpec, + ...PaypalSpec, + ...PatreonSpec + } +} \ No newline at end of file diff --git a/includes/specs/plugins.spec.js b/includes/specs/plugins.spec.js index e912399..27b9b5a 100644 --- a/includes/specs/plugins.spec.js +++ b/includes/specs/plugins.spec.js @@ -59,5 +59,10 @@ module.exports = { [type]: 'boolean', [doc]: 'Show a loading progress bar at top of the page', [defaultValue]: true + }, + clipboard: { + [type]: 'boolean', + [doc]: 'Show the copy button in the highlighted code area', + [defaultValue]: true } }; \ No newline at end of file diff --git a/languages/en.yml b/languages/en.yml index 401bc45..816fe2c 100644 --- a/languages/en.yml +++ b/languages/en.yml @@ -25,6 +25,12 @@ article: read: 'read' about: 'About' words: 'words' +donate: + title: 'Like this article? Support the author with' + alipay: 'Alipay' + wechat: 'Wechat' + paypal: 'Paypal' + patreon: 'Patreon' plugin: backtotop: 'Back to Top' search: diff --git a/languages/id.yml b/languages/id.yml index 52c8c54..ab3d036 100644 --- a/languages/id.yml +++ b/languages/id.yml @@ -6,32 +6,40 @@ common: one: 'Kategori' other: 'Kategori' tag: - one: 'tag' - other: 'tag' + one: 'Tag' + other: 'Tag' post: - one: 'pos' - other: 'pos' + one: 'Artikel' + other: 'Artikel' prev: 'Sebelumnya' next: 'Berikutnya' widget: follow: 'IKUTI' recents: 'Terbaru' - links: 'tautan' - tag_cloud: 'awan tag' - catalogue: 'Catalogue' + links: 'Tautan' + tag_cloud: 'Awan tag' + catalogue: 'Katalog' article: - more: 'Read More' + more: 'Selengkapnya' comments: 'Komentar' - read: 'read' - about: 'About' - words: 'words' + read: 'membaca' + about: 'Sekitar' + words: 'kata' search: - search: 'Search' + search: 'Pencarian' hint: 'Tulis Sesuatu..' insight: hint: 'Tulis Sesuatu..' - posts: 'Pos' + posts: 'Artikel' pages: 'Halaman' categories: 'Kategori' tags: 'Tag' untitled: 'Tanpa Judul' +donate: + title: 'Suka dengan artikel ini? Bantu penulis dengan donasi melalui' + alipay: 'Alipay' + wechat: 'Wechat' + paypal: 'Paypal' + patreon: 'Patreon' +plugin: + backtotop: 'Kembali ke atas' diff --git a/languages/zh-CN.yml b/languages/zh-CN.yml index 99ec4f5..5ca1fc7 100644 --- a/languages/zh-CN.yml +++ b/languages/zh-CN.yml @@ -25,6 +25,10 @@ article: read: '读完' about: '大约' words: '个字' +donate: + title: '喜欢这篇文章?打赏一下作者吧' + alipay: '支付宝' + wechat: '微信' plugin: backtotop: '回到顶端' search: diff --git a/languages/zh-TW.yml b/languages/zh-TW.yml index 6791f2a..bacc55e 100644 --- a/languages/zh-TW.yml +++ b/languages/zh-TW.yml @@ -1,7 +1,7 @@ common: archive: - one: '歸檔' - other: '歸檔' + one: '彙整' + other: '彙整' category: one: '分類' other: '分類' @@ -14,7 +14,7 @@ common: prev: '上一頁' next: '下一頁' widget: - follow: '關注我' + follow: '追蹤' recents: '最新文章' links: '連結' tag_cloud: '標籤雲' @@ -22,16 +22,24 @@ widget: article: more: '繼續閱讀' comments: '評論' - read: 'read' - about: 'About' - words: 'words' + read: '閱讀文' + about: '大約' + words: '個字' +donate: + title: '喜歡這篇文章嗎? 贊助一下作者吧!' + alipay: '支付寶' + wechat: 'WeChat' + paypal: 'PayPal' + patreon: 'Patreon' +plugin: + backtotop: '回到頁首' search: - search: '搜索' - hint: 'Type something...' + search: '搜尋' + hint: '請輸入關鍵字...' insight: - hint: 'Type something...' + hint: '請輸入關鍵字...' posts: '文章' - pages: 'Pages' + pages: '頁面' categories: '分類' tags: '標籤' - untitled: '(Untitled)' + untitled: '(無標題)' diff --git a/layout/comment/gitalk.ejs b/layout/comment/gitalk.ejs new file mode 100644 index 0000000..e49aae9 --- /dev/null +++ b/layout/comment/gitalk.ejs @@ -0,0 +1,22 @@ +<% if (!has_config('comment.owner') || !has_config('comment.admin') || !has_config('comment.repo') || !has_config('comment.client_id') || + !has_config('comment.client_secret')) { %> +
+ You forgot to set the owner, admin, repo, client_id, or client_secret for Gittalk. + Please set it in _config.yml. +
+<% } else { %> +
+<%- _css(cdn('gitalk', '1.4.1', 'dist/gitalk.css')) %> +<%- _js(cdn('gitalk', '1.4.1', 'dist/gitalk.min.js')) %> + +<% } %> \ No newline at end of file diff --git a/layout/common/article.ejs b/layout/common/article.ejs index fe97d17..d3149f2 100644 --- a/layout/common/article.ejs +++ b/layout/common/article.ejs @@ -70,6 +70,23 @@
+<% 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)) { %>
diff --git a/layout/donate/alipay.ejs b/layout/donate/alipay.ejs new file mode 100644 index 0000000..5ced298 --- /dev/null +++ b/layout/donate/alipay.ejs @@ -0,0 +1,14 @@ +<% const qrcode = get_config_from_obj(service, 'qrcode'); + if (qrcode) { %> + +<% } else { %> +
+ You forgot to set the qrcode for Alipay. Please set it in _config.yml. +
+<% } %> \ No newline at end of file diff --git a/layout/donate/patreon.ejs b/layout/donate/patreon.ejs new file mode 100644 index 0000000..16de91a --- /dev/null +++ b/layout/donate/patreon.ejs @@ -0,0 +1,13 @@ +<% const url = get_config_from_obj(service, 'url'); + if (url) { %> + +<% } else { %> +
+ You forgot to set the url Patreon. Please set it in _config.yml. +
+<% } %> \ No newline at end of file diff --git a/layout/donate/paypal.ejs b/layout/donate/paypal.ejs new file mode 100644 index 0000000..f4cb854 --- /dev/null +++ b/layout/donate/paypal.ejs @@ -0,0 +1,20 @@ + +<% const business = get_config_from_obj(service, 'business'); + const currency_code = get_config_from_obj(service, 'currency_code'); + if (business && currency_code) { %> + +
+ + + + +<% } else { %> +
+ You forgot to set the business and currency_code for Paypal. Please set it in _config.yml. +
+<% } %> \ No newline at end of file diff --git a/layout/donate/wechat.ejs b/layout/donate/wechat.ejs new file mode 100644 index 0000000..a1a6faa --- /dev/null +++ b/layout/donate/wechat.ejs @@ -0,0 +1,14 @@ +<% const qrcode = get_config_from_obj(service, 'qrcode'); + if (qrcode) { %> + +<% } else { %> +
+ You forgot to set the qrcode for Wechat. Please set it in _config.yml. +
+<% } %> \ No newline at end of file diff --git a/layout/plugin/clipboard.ejs b/layout/plugin/clipboard.ejs new file mode 100644 index 0000000..dc764e4 --- /dev/null +++ b/layout/plugin/clipboard.ejs @@ -0,0 +1,6 @@ +<% if (plugin !== false) { %> + <% if (!head) { %> + <%- _js(cdn('clipboard', '2.0.4', 'dist/clipboard.min.js'), true) %> + <%- _js('js/clipboard', true) %> + <% } %> +<% } %> \ No newline at end of file diff --git a/layout/widget/links.ejs b/layout/widget/links.ejs index c87c605..2a1e629 100644 --- a/layout/widget/links.ejs +++ b/layout/widget/links.ejs @@ -14,7 +14,7 @@ <%= i %> - <%- links[i] %> + <%- get_domain(links[i]) %> diff --git a/package.json b/package.json index 183c998..da63e1a 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { "name": "hexo-theme-icarus", - "version": "2.0.0", + "version": "2.3.0", "private": true } diff --git a/source/css/style.styl b/source/css/style.styl index ef01c5f..a7a57bb 100644 --- a/source/css/style.styl +++ b/source/css/style.styl @@ -50,16 +50,18 @@ body, button, input, select, textarea top: 1.5rem .card - overflow: hidden border-radius: 4px box-shadow: 0 4px 10px rgba(0,0,0,0.05), 0 0 1px rgba(0,0,0,0.1) & + .card, & + .column-right-shadow margin-top: 1.5rem &.card-transparent - overflow: visible box-shadow: none background: transparent + .card-image + overflow: hidden + border-top-left-radius: 4px + border-top-right-radius: 4px img.thumbnail object-fit: cover @@ -181,6 +183,30 @@ img.thumbnail margin-left: 0.75rem margin-right: 0 +.donate + position: relative + .qrcode + display: none + position: absolute + z-index: 99 + bottom: 2.5em + line-height: 0 + overflow: hidden + border-radius: 4px + box-shadow: 0 4px 10px rgba(0,0,0,.1), 0 0 1px rgba(0,0,0,.2) + overflow: hidden + img + max-width: 280px + &:hover + .qrcode + display: block + &:first-child:not(:last-child) + .qrcode + left: -0.75rem + &:last-child:not(:first-child) + .qrcode + right: -0.75rem + @media screen and (max-width: screen-tablet - 1) #toc display: none @@ -210,6 +236,9 @@ img.thumbnail /* --------------------------------- * Custom modifiers * --------------------------------- */ +.is-borderless + border: none + .is-size-7 font-size: 0.85rem !important @@ -318,6 +347,7 @@ img.thumbnail figure.highlight padding: 0 width: 100% + position: relative margin: 1em 0 1em !important pre, @@ -371,6 +401,16 @@ figure.highlight min-width: inherit border-radius: inherit + .copy + display: none + position: absolute + bottom: 0 + right: 0 + color: white + background: rgba(0, 0, 0, 0.5) + &:hover .copy + display: block + /* --------------------------------- * Fix Gist Snippet * --------------------------------- */ diff --git a/source/js/clipboard.js b/source/js/clipboard.js new file mode 100644 index 0000000..2e0e4d4 --- /dev/null +++ b/source/js/clipboard.js @@ -0,0 +1,10 @@ +document.addEventListener('DOMContentLoaded', function () { + if (typeof(ClipboardJS) !== 'undefined') { + $('figure.highlight').each(function () { + var id = 'code-' + Date.now() + (Math.random() * 1000 | 0); + $(this).attr('id', id); + $(this).prepend($(``)); + }); + new ClipboardJS('.highlight .copy'); + } +}); \ No newline at end of file