Merge branch 'jitwxs-work'

This commit is contained in:
ppoffice 2019-01-02 21:03:27 -05:00
commit 1d448583e5
23 changed files with 358 additions and 49 deletions

View File

@ -2,7 +2,7 @@
<img class="not-gallery-item" height="48" src="http://ppoffice.github.io/hexo-theme-icarus/images/logo.svg">
<br> A simple, delicate, and modern theme for the static site generator Hexo.
<br>
<a href="http://ppoffice.github.io/hexo-theme-icarus/">Preview</a> |
<a href="http://ppoffice.github.io/hexo-theme-icarus/">Preview</a> |
<a href="http://ppoffice.github.io/hexo-theme-icarus/categories/">Documentation</a> |
<a href="https://github.com/ppoffice/hexo-theme-icarus/archive/master.zip">Download</a>
<br>
@ -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.
<table>
@ -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.
<div>

View File

@ -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['*'], {})];
}

View File

@ -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];

View File

@ -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")
});
}

View File

@ -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

View File

@ -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'),

View File

@ -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
}
}

View File

@ -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
}
};

View File

@ -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:

View File

@ -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'

View File

@ -25,6 +25,10 @@ article:
read: '读完'
about: '大约'
words: '个字'
donate:
title: '喜欢这篇文章?打赏一下作者吧'
alipay: '支付宝'
wechat: '微信'
plugin:
backtotop: '回到顶端'
search:

View File

@ -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: '(無標題)'

22
layout/comment/gitalk.ejs Normal file
View File

@ -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')) { %>
<div class="notification is-danger">
You forgot to set the <code>owner</code>, <code>admin</code>, <code>repo</code>, <code>client_id</code>, or <code>client_secret</code> for Gittalk.
Please set it in <code>_config.yml</code>.
</div>
<% } else { %>
<div id="comment-container"></div>
<%- _css(cdn('gitalk', '1.4.1', 'dist/gitalk.css')) %>
<%- _js(cdn('gitalk', '1.4.1', 'dist/gitalk.min.js')) %>
<script>
var gitalk = new Gitalk({
clientID: '<%= get_config('comment.client_id') %>',
clientSecret: '<%= get_config('comment.client_secret') %>',
id: '<%= md5(page.path) %>',
repo: '<%= get_config('comment.repo') %>',
owner: '<%= get_config('comment.owner') %>',
admin: <%- JSON.stringify(get_config('comment.admin'))%>
})
gitalk.render('comment-container')
</script>
<% } %>

View File

@ -70,6 +70,23 @@
</div>
</div>
<% const services = has_config('donate') ? get_config('donate') : []; %>
<% if (!index && services.length > 0) { %>
<div class="card">
<div class="card-content">
<h3 class="menu-label has-text-centered"><%= __('donate.title') %></h3>
<div class="buttons is-centered">
<% for (let service of services) {
const type = get_config_from_obj(service, 'type');
if (type !== null) { %>
<%- partial('donate/' + type, { type, service }) %>
<% }
} %>
</div>
</div>
</div>
<% } %>
<% if (!index && (post.prev || post.next)) { %>
<div class="card card-transparent">
<div class="level post-navigation is-flex-wrap is-mobile">

14
layout/donate/alipay.ejs Normal file
View File

@ -0,0 +1,14 @@
<% const qrcode = get_config_from_obj(service, 'qrcode');
if (qrcode) { %>
<a class="button is-info donate">
<span class="icon is-small">
<i class="fab fa-alipay"></i>
</span>
<span><%= __('donate.' + type) %></span>
<div class="qrcode"><img src="<%= url_for(qrcode) %>" alt="<%= __('donate.' + type) %>"></div>
</a>
<% } else { %>
<div class="notification is-danger">
You forgot to set the <code>qrcode</code> for Alipay. Please set it in <code>_config.yml</code>.
</div>
<% } %>

13
layout/donate/patreon.ejs Normal file
View File

@ -0,0 +1,13 @@
<% const url = get_config_from_obj(service, 'url');
if (url) { %>
<a class="button is-danger donate" href="<%= url_for(url) %>" target="_blank">
<span class="icon is-small">
<i class="fab fa-patreon"></i>
</span>
<span><%= __('donate.' + type) %></span>
</a>
<% } else { %>
<div class="notification is-danger">
You forgot to set the <code>url</code> Patreon. Please set it in <code>_config.yml</code>.
</div>
<% } %>

20
layout/donate/paypal.ejs Normal file
View File

@ -0,0 +1,20 @@
<!-- Visit https://www.paypal.com/donate/buttons/ to get your donate button -->
<% const business = get_config_from_obj(service, 'business');
const currency_code = get_config_from_obj(service, 'currency_code');
if (business && currency_code) { %>
<a class="button is-warning donate" onclick="<%= 'document.getElementById(\'paypal-donate-form\').submit()' %>">
<span class="icon is-small">
<i class="fab fa-paypal"></i>
</span>
<span><%= __('donate.' + type) %></span>
</a>
<form action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_blank" id="paypal-donate-form">
<input type="hidden" name="cmd" value="_donations" />
<input type="hidden" name="business" value="<%= business %>" />
<input type="hidden" name="currency_code" value="<%= currency_code %>" />
</form>
<% } else { %>
<div class="notification is-danger">
You forgot to set the <code>business</code> and <code>currency_code</code> for Paypal. Please set it in <code>_config.yml</code>.
</div>
<% } %>

14
layout/donate/wechat.ejs Normal file
View File

@ -0,0 +1,14 @@
<% const qrcode = get_config_from_obj(service, 'qrcode');
if (qrcode) { %>
<a class="button is-success donate">
<span class="icon is-small">
<i class="fab fa-weixin"></i>
</span>
<span><%= __('donate.' + type) %></span>
<div class="qrcode"><img src="<%= url_for(qrcode) %>" alt="<%= __('donate.' + type) %>"></div>
</a>
<% } else { %>
<div class="notification is-danger">
You forgot to set the <code>qrcode</code> for Wechat. Please set it in <code>_config.yml</code>.
</div>
<% } %>

View File

@ -0,0 +1,6 @@
<% if (plugin !== false) { %>
<% if (!head) { %>
<%- _js(cdn('clipboard', '2.0.4', 'dist/clipboard.min.js'), true) %>
<%- _js('js/clipboard', true) %>
<% } %>
<% } %>

View File

@ -14,7 +14,7 @@
<span class="level-item"><%= i %></span>
</span>
<span class="level-right">
<span class="level-item tag"><%- links[i] %></span>
<span class="level-item tag"><%- get_domain(links[i]) %></span>
</span>
</a>
</li>

View File

@ -1,5 +1,5 @@
{
"name": "hexo-theme-icarus",
"version": "2.0.0",
"version": "2.3.0",
"private": true
}

View File

@ -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
* --------------------------------- */

10
source/js/clipboard.js Normal file
View File

@ -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($(`<button class="button is-borderless is-radiusless is-small copy" data-clipboard-target="#${id} .code" title="Copy"><i class="fas fa-copy"></i></button>`));
});
new ClipboardJS('.highlight .copy');
}
});