📝 更新API文档

pull/69/head
ruibaby 2018-12-08 16:40:26 +08:00
parent 19970fc100
commit f3be2d389c
1384 changed files with 101211 additions and 0 deletions

View File

@ -363,6 +363,11 @@
<fork>true</fork>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</build>
</profile>

View File

@ -31,6 +31,45 @@ public class ApiArchivesController {
/**
*
*
* <p>
* result json:
* <pre>
* {
* "code": 200,
* "msg": "OK",
* "result": [
* {
* "year": "",
* "month": "",
* "count": "",
* "posts": [
* {
* "postId": "",
* "user": {},
* "postTitle": "",
* "postType": "",
* "postContentMd": "",
* "postContent": "",
* "postUrl": "",
* "postSummary": "",
* "categories": [],
* "tags": [],
* "comments": [],
* "postThumbnail": "",
* "postDate": "",
* "postUpdate": "",
* "postStatus": 0,
* "postViews": 0,
* "allowComment": 1,
* "customTpl": ""
* }
* ]
* }
* ]
* }
* </pre>
* </p>
*
* @return JsonResult
*/
@GetMapping(value = "/year")
@ -46,6 +85,45 @@ public class ApiArchivesController {
/**
*
*
* <p>
* result json:
* <pre>
* {
* "code": 200,
* "msg": "OK",
* "result": [
* {
* "year": "",
* "month": "",
* "count": "",
* "posts": [
* {
* "postId": "",
* "user": {},
* "postTitle": "",
* "postType": "",
* "postContentMd": "",
* "postContent": "",
* "postUrl": "",
* "postSummary": "",
* "categories": [],
* "tags": [],
* "comments": [],
* "postThumbnail": "",
* "postDate": "",
* "postUpdate": "",
* "postStatus": 0,
* "postViews": 0,
* "allowComment": 1,
* "customTpl": ""
* }
* ]
* }
* ]
* }
* </pre>
* </p>
*
* @return JsonResult
*/
@GetMapping(value = "/year/month")

View File

@ -28,6 +28,24 @@ public class ApiCategoryController {
/**
*
*
* <p>
* result json:
* <pre>
* {
* "code": 200,
* "msg": "OK",
* "result": [
* {
* "cateId": "",
* "cateName": "",
* "cateUrl": "",
* "cateDesc": ""
* }
* ]
* }
* </pre>
* </p>
*
* @return JsonResult
*/
@GetMapping
@ -43,6 +61,22 @@ public class ApiCategoryController {
/**
*
*
* <p>
* result json:
* <pre>
* {
* "code": 200,
* "msg": "OK",
* "result": {
* "cateId": "",
* "cateName": "",
* "cateUrl": "",
* "cateDesc": ""
* }
* }
* </pre>
* </p>
*
* @param cateUrl
* @return JsonResult
*/

View File

@ -29,6 +29,27 @@ public class ApiGalleryController {
/**
*
*
* <p>
* result json:
* <pre>
* {
* "code": 200,
* "msg": "OK",
* "result": [
* {
* "galleryId": ,
* "galleryName": "",
* "galleryDesc": "",
* "galleryDate": "",
* "galleryLocation": "",
* "galleryThumbnailUrl": "",
* "galleryUrl": ""
* }
* ]
* }
* </pre>
* </p>
*
* @return JsonResult
*/
@GetMapping
@ -44,6 +65,27 @@ public class ApiGalleryController {
/**
*
*
* <p>
* result json:
* <pre>
* {
* "code": 200,
* "msg": "OK",
* "result": [
* {
* "galleryId": ,
* "galleryName": "",
* "galleryDesc": "",
* "galleryDate": "",
* "galleryLocation": "",
* "galleryThumbnailUrl": "",
* "galleryUrl": ""
* }
* ]
* }
* </pre>
* </p>
*
* @param id id
* @return JsonResult
*/

View File

@ -31,6 +31,25 @@ public class ApiLinkController {
/**
*
*
* <p>
* result json:
* <pre>
* {
* "code": 200,
* "msg": "OK",
* "result": [
* {
* "linkId": ,
* "linkName": "",
* "linkUrl": "",
* "linkPic": "",
* "linkDesc": ""
* }
* ]
* }
* </pre>
* </p>
*
* @return JsonResult
*/
@GetMapping

View File

@ -31,6 +31,26 @@ public class ApiMenuController {
/**
*
*
* <p>
* result json:
* <pre>
* {
* "code": 200,
* "msg": "OK",
* "result": [
* {
* "menuId": ,
* "menuName": "",
* "menuUrl": "",
* "menuSort": ,
* "menuIcon": "",
* "menuTarget":
* }
* ]
* }
* </pre>
* </p>
*
* @return JsonResult
*/
@GetMapping

View File

@ -28,6 +28,31 @@ public class ApiOptionController {
/**
*
*
* <p>
* result json:
* <pre>
* {
* "code": 200,
* "msg": "OK",
* "result": {
* "blog_start": "",
* "blog_locale": "",
* "blog_url": "",
* "api_token": "",
* "api_status": "",
* "blog_title": "",
* "new_comment_notice": "",
* "smtp_email_enable": "",
* "attach_loc": "",
* "theme": "",
* "comment_reply_notice": "",
* "is_install": "",
* "comment_pass_notice": ""
* }
* }
* </pre>
* </p>
*
* @return JsonResult
*/
@GetMapping
@ -45,6 +70,17 @@ public class ApiOptionController {
/**
*
*
* <p>
* result json:
* <pre>
* {
* "code": 200,
* "msg": "OK",
* "result": ""
* }
* </pre>
* </p>
*
* @param optionName
* @return JsonResult
*/

View File

@ -27,6 +27,36 @@ public class ApiPageController {
/**
*
*
* <p>
* result json:
* <pre>
* {
* "code": 200,
* "msg": "OK",
* "result": {
* "postId": ,
* "user": {},
* "postTitle": "",
* "postType": "",
* "postContentMd": "",
* "postContent": "",
* "postUrl": "",
* "postSummary": ,
* "categories": [],
* "tags": [],
* "comments": [],
* "postThumbnail": "",
* "postDate": "",
* "postUpdate": "",
* "postStatus": 0,
* "postViews": 0,
* "allowComment": 1,
* "customTpl": ""
* }
* }
* </pre>
* </p>
*
* @param postId postId
* @return JsonResult
*/

View File

@ -35,6 +35,65 @@ public class ApiPostController {
/**
*
*
* <p>
* result api
* <pre>
* {
* "code": 200,
* "msg": "OK",
* "result": {
* "content": [
* {
* "postId": ,
* "user": {},
* "postTitle": "",
* "postType": "",
* "postContentMd": "",
* "postContent": "",
* "postUrl": "",
* "postSummary": "",
* "categories": [],
* "tags": [],
* "comments": [],
* "postThumbnail": "",
* "postDate": "",
* "postUpdate": "",
* "postStatus": 0,
* "postViews": 0,
* "allowComment": 1,
* "customTpl": ""
* }
* ],
* "pageable": {
* "sort": {
* "sorted": true,
* "unsorted": false,
* "empty": false
* },
* "offset": 0,
* "pageSize": 10,
* "pageNumber": 0,
* "unpaged": false,
* "paged": true
* },
* "last": true,
* "totalElements": 1,
* "totalPages": 1,
* "size": 10,
* "number": 0,
* "first": true,
* "numberOfElements": 1,
* "sort": {
* "sorted": true,
* "unsorted": false,
* "empty": false
* },
* "empty": false
* }
* }
* </pre>
* </p>
*
* @param page
* @return JsonResult
*/
@ -56,6 +115,36 @@ public class ApiPostController {
/**
*
*
* <p>
* result json:
* <pre>
* {
* "code": 200,
* "msg": "OK",
* "result": {
* "postId": ,
* "user": {},
* "postTitle": "",
* "postType": "",
* "postContentMd": "",
* "postContent": "",
* "postUrl": "",
* "postSummary": "",
* "categories": [],
* "tags": [],
* "comments": [],
* "postThumbnail": "",
* "postDate": "",
* "postUpdate": "",
* "postStatus": 0,
* "postViews": 0,
* "allowComment": 1,
* "customTpl": ""
* }
* }
* </pre>
* </p>
*
* @param postId
* @return JsonResult
*/

View File

@ -28,6 +28,23 @@ public class ApiTagController {
/**
*
*
* <p>
* result json:
* <pre>
* {
* "code": 200,
* "msg": "OK",
* "result": [
* {
* "tagId": ,
* "tagName": "",
* "tagUrl": ""
* }
* ]
* }
* </pre>
* </p>
*
* @return JsonResult
*/
@GetMapping
@ -43,6 +60,21 @@ public class ApiTagController {
/**
*
*
* <p>
* result json:
* <pre>
* {
* "code": 200,
* "msg": "OK",
* "result": {
* "tagId": ,
* "tagName": "",
* "tagUrl": ""
* }
* }
* </pre>
* </p>
*
* @param tagUrl tagUrl
* @return JsonResult
*/

View File

@ -29,6 +29,23 @@ public class ApiUserController {
/**
*
*
* <p>
* result json:
* <pre>
* {
* "code": 200,
* "msg": "OK",
* "result": {
* "userId": ,
* "userDisplayName": "",
* "userEmail": "",
* "userAvatar": "",
* "userDesc": ""
* }
* }
* </pre>
* </p>
*
* @return JsonResult
*/
@GetMapping

View File

@ -0,0 +1,137 @@
<p align="center" class="has-mb-6">
<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/categories/">Documentation</a> |
<a href="https://github.com/ppoffice/hexo-theme-icarus/archive/master.zip">Download</a>
<br>
</p>
![Icarus](http://ppoffice.github.io/hexo-theme-icarus/gallery/preview.png?1 "Icarus Preview")
### :cd: Installation
Download & extract or `git clone` Icarus from GitHub to your blog's theme folder, and that's it!
```shell
git clone https://github.com/ppoffice/hexo-theme-icarus.git themes/icarus
```
Once started, Icarus will remind you of any missing dependencies and configuration files.
### :gift: Features
**Extensive Plugin Support**
Icarus includes plentiful search, comment, sharing and other plugins out of the box. You can choose any of them to enrich your
blog experience, or build your own plugin easily referring to the existing Icarus plugins.
Comment plugins
- [Changyan](http://ppoffice.github.io/hexo-theme-icarus/Plugins/Comment/changyan-comment-plugin/)
- [Disqus](http://ppoffice.github.io/hexo-theme-icarus/Plugins/Comment/disqus-comment-plugin/)
- [Facebook](http://ppoffice.github.io/hexo-theme-icarus/Plugins/Comment/facebook-comment-plugin/)
- [Gitment](http://ppoffice.github.io/hexo-theme-icarus/Plugins/Comment/gitment-comment-plugin/)
- [Isso](http://ppoffice.github.io/hexo-theme-icarus/Plugins/Comment/isso-comment-plugin/)
- [LiveRe](http://ppoffice.github.io/hexo-theme-icarus/Plugins/Comment/livere-comment-plugin/)
- [Valine](http://ppoffice.github.io/hexo-theme-icarus/Plugins/Comment/valine-comment-plugin/)
Search plugins
- [Insight Search](http://ppoffice.github.io/hexo-theme-icarus/Plugins/Search/insight-search-plugin/)
- [Google Custom Search Engine](http://ppoffice.github.io/hexo-theme-icarus/Plugins/Search/google-cse-plugin/)
- [Baidu Site Search](http://ppoffice.github.io/hexo-theme-icarus/Plugins/Search/baidu-search-plugin/)
Share plugins
- [AddThis](http://ppoffice.github.io/hexo-theme-icarus/Plugins/Share/addthis-share-plugin/)
- [AddToAny](http://ppoffice.github.io/hexo-theme-icarus/Plugins/Share/addtoany-share-plugin/)
- [Baidu Share](http://ppoffice.github.io/hexo-theme-icarus/Plugins/Share/baidu-share-plugin/)
- [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/)
Other plugins
- [Hexo Tag Plugin](http://ppoffice.github.io/hexo-theme-icarus/Configuration/Posts/hexo-built-in-tag-helpers/)
- [lightGallery & Justified Gallery](http://ppoffice.github.io/hexo-theme-icarus/Plugins/General/gallery-plugin/)
- [MathJax](http://ppoffice.github.io/hexo-theme-icarus/Plugins/General/mathjax-plugin/)
- [Site Analytics](http://ppoffice.github.io/hexo-theme-icarus/Plugins/General/site-analytics-plugin/)
**Rich Code Highlight Theme Choices**
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>
<tr>
<td><img src="http://ppoffice.github.io/hexo-theme-icarus/gallery/code-highlight/atom-one-light.png"></td>
<td><img src="http://ppoffice.github.io/hexo-theme-icarus/gallery/code-highlight/monokai.png"></td>
<td><img src="http://ppoffice.github.io/hexo-theme-icarus/gallery/code-highlight/androidstudio.png"></td>
</tr>
</table>
**Elastic Theme Configuration**
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>
<table>
<tr>
<th>_config.yml</th>
<th>post.md</th>
</tr>
<tr>
<td>
<pre>menu:
Archives: /archives
Categories: /categories
Tags: /tags
About: /about</pre>
</td>
<td>
<pre>title: A Simple Post
menu:
Go Home: /index.html
---
# Here is some simple markdown.</pre>
</td>
</tr>
<tr>
<td><img height="40" src="http://ppoffice.github.io/hexo-theme-icarus/gallery/navbar/main-config.png"></td>
<td><img height="40" src="http://ppoffice.github.io/hexo-theme-icarus/gallery/navbar/post-config.png"></td>
</tr>
</table>
</div>
**Responsive Layout**
No matter what modern browsering device your audiences are using, they can always get the best experience because Icarus's responsive
layout across multiple viewpoints.
![Responsive Layout](http://ppoffice.github.io/hexo-theme-icarus/gallery/responsive.png)
### :hammer: Development
This project is built with
- Hexo 3.7.1
- Ejs
- Stylus
- Bulma 0.7.2
Please refer to the documentation for Icarus implementation details.
### :tada: Contribute
If you feel like to help us build a better Icarus, you can
:electric_plug: Write a plugin |
:black_nib: <a href="https://github.com/ppoffice/hexo-theme-icarus/new/site/source/_posts">Submit a tutorial</a> |
:triangular_flag_on_post: <a href="https://github.com/ppoffice/hexo-theme-icarus/issues/new">Report a bug</a> |
:earth_asia: <a href="https://github.com/ppoffice/hexo-theme-icarus/tree/master/languages">Add a translation</a>
### :memo: License
This project is licensed under the MIT License - see the [LICENSE](https://github.com/ppoffice/hexo-theme-icarus/blob/master/LICENSE) file for details.

View File

@ -0,0 +1,84 @@
const yaml = require('js-yaml');
const Type = require('js-yaml/lib/js-yaml/type');
const Schema = require('js-yaml/lib/js-yaml/schema');
const { is, descriptors } = require('./utils');
const { doc, type, requires, defaultValue } = descriptors;
const UNDEFINED = Symbol('undefined');
// output null as empty in yaml
const YAML_SCHEMA = new Schema({
include: [
require('js-yaml/lib/js-yaml/schema/default_full')
],
implicit: [
new Type('tag:yaml.org,2002:null', {
kind: 'scalar',
resolve(data) {
if (data === null) {
return true;
}
const max = data.length;
return (max === 1 && data === '~') ||
(max === 4 && (data === 'null' || data === 'Null' || data === 'NULL'));
},
construct: () => null,
predicate: object => object === null,
represent: {
empty: function () { return ''; }
},
defaultStyle: 'empty'
})
]
});
function generate(spec, parentConfig = null) {
if (!is.spec(spec)) {
return UNDEFINED;
}
if (spec.hasOwnProperty(requires) && !spec[requires](parentConfig)) {
return UNDEFINED;
}
if (spec.hasOwnProperty(defaultValue)) {
return spec[defaultValue];
}
const types = is.array(spec[type]) ? spec[type] : [spec[type]];
if (types.includes('object')) {
let defaults = UNDEFINED;
for (let key in spec) {
if (key === '*') {
continue;
}
const value = generate(spec[key], defaults);
if (value !== UNDEFINED) {
if (defaults === UNDEFINED) {
defaults = {};
}
if (spec[key].hasOwnProperty(doc)) {
defaults['#' + key] = spec[key][doc];
}
defaults[key] = value;
}
}
return defaults;
} else if (types.includes('array') && spec.hasOwnProperty('*')) {
return [generate(spec['*'], {})];
}
return UNDEFINED;
}
class ConfigGenerator {
constructor(spec) {
this.spec = spec;
}
generate() {
return yaml.safeDump(generate(this.spec), {
indent: 4,
lineWidth: 1024,
schema: YAML_SCHEMA
}).replace(/^(\s*)\'#.*?\':\s*\'*(.*?)\'*$/mg, '$1# $2'); // make comment lines
}
}
module.exports = ConfigGenerator;

View File

@ -0,0 +1,132 @@
const { is, descriptors } = require('./utils');
const { type, required, requires, format, defaultValue } = descriptors;
const {
InvalidSpecError,
MissingRequiredError,
TypeMismatchError,
FormatMismatchError,
VersionMalformedError,
VersionNotFoundError,
VersionMismatchError } = require('./utils').errors;
function isRequiresSatisfied(spec, config) {
try {
if (!spec.hasOwnProperty(requires) || spec[requires](config) === true) {
return true;
}
} catch (e) { }
return false;
}
function getConfigType(spec, config) {
const specTypes = is.array(spec[type]) ? spec[type] : [spec[type]];
for (let specType of specTypes) {
if (is[specType](config)) {
return specType;
}
}
return null;
}
function hasFormat(spec, config) {
if (!spec.hasOwnProperty(format)) {
return true;
}
return spec[format].test(config);
}
function validate(spec, config, parentConfig, path) {
if (!is.spec(spec)) {
throw new InvalidSpecError(spec, path);
}
if (!isRequiresSatisfied(spec, parentConfig)) {
return;
}
if (is.undefined(config) || is.null(config)) {
if (spec[required] === true) {
throw new MissingRequiredError(spec, path);
}
return;
}
const type = getConfigType(spec, config);
if (type === null) {
throw new TypeMismatchError(spec, path, config);
}
if (type === 'string') {
if (!hasFormat(spec, config)) {
throw new FormatMismatchError(spec, path, config);
}
} else if (type === 'array' && spec.hasOwnProperty('*')) {
config.forEach((child, i) => validate(spec['*'], child, config, path.concat(`[${i}]`)));
} else if (type === 'object') {
for (let key in spec) {
if (key === '*') {
Object.keys(config).forEach(k => validate(spec['*'], config[k], config, path.concat(k)));
} else {
validate(spec[key], config[key], config, path.concat(key));
}
}
}
}
function formatVersion(ver) {
const m = /^(\d)+\.(\d)+\.(\d)+(?:-([0-9A-Za-z-]+))*$/.exec(ver);
if (m === null) {
throw new VersionMalformedError(ver);
}
return {
major: m[1],
minor: m[2],
patch: m[3],
identifier: m.length > 4 ? m[4] : null
};
}
function compareVersion(ver1, ver2) {
for (let section of ['major', 'minor', 'patch']) {
if (ver1[section] !== ver2[section]) {
return Math.sign(ver1[section] - ver2[section]);
}
}
const id1 = ver1.hasOwnProperty('identifier') ? ver1.identifier : null;
const id2 = ver2.hasOwnProperty('identifier') ? ver2.identifier : null;
if (id1 === id2) {
return 0;
}
if (id1 === null) {
return 1;
}
if (id2 === null) {
return -1;
}
return id1.localeCompare(id2);
}
function isBreakingChange(base, ver) {
return base.major !== ver.major;
}
function checkVersion(spec, config) {
if (!config.hasOwnProperty('version')) {
throw new VersionNotFoundError();
}
const configVersion = formatVersion(config.version);
const specVersion = formatVersion(spec.version[defaultValue]);
if (isBreakingChange(specVersion, configVersion)) {
throw new VersionMismatchError(spec.version[defaultValue], config.version, compareVersion(specVersion, configVersion) > 0);
}
}
class ConfigValidator {
constructor(spec) {
this.spec = spec;
}
validate(config) {
checkVersion(this.spec, config);
validate(this.spec, config, null, []);
}
}
module.exports = ConfigValidator;

View File

@ -0,0 +1,151 @@
const doc = Symbol('@doc');
const type = Symbol('@type');
const format = Symbol('@format');
const required = Symbol('@required');
const requires = Symbol('@requires');
const defaultValue = Symbol('@default');
const descriptors = {
doc,
type,
format,
requires,
required,
defaultValue
};
const is = (() => ({
number(value) {
return typeof (value) === 'number';
},
string(value) {
return typeof (value) === 'string';
},
array(value) {
return Array.isArray(value);
},
boolean(value) {
return typeof (value) === 'boolean';
},
object(value) {
return typeof (value) === 'object' && value.constructor == Object;
},
function(value) {
return typeof (value) === 'function';
},
regexp(value) {
return value instanceof RegExp;
},
undefined(value) {
return typeof (value) === 'undefined';
},
null(value) {
return value === null;
},
spec(value) {
if (!value.hasOwnProperty(type)) {
return false;
}
if (!is.string(value[type]) && !is.array(value[type])) {
return false;
}
if (value.hasOwnProperty(doc) && !is.string(value[doc])) {
return false;
}
if (value.hasOwnProperty(required) && !is.boolean(value[required])) {
return false;
}
if (value.hasOwnProperty(requires) && !is.function(value[requires])) {
return false;
}
if (value.hasOwnProperty(format) && !is.regexp(value[format])) {
return false;
}
return true;
}
}))();
class ConfigError extends Error {
constructor(spec, path) {
super();
this.spec = spec;
this.path = path;
}
}
class InvalidSpecError extends ConfigError {
constructor(spec, path) {
super(spec, path);
this.message = `The specification '${path.join('.')}' is invalid.`;
}
}
class MissingRequiredError extends ConfigError {
constructor(spec, path) {
super(spec, path);
this.message = `Configuration file do not have the required '${path.join('.')}' field.`;
}
}
class TypeMismatchError extends ConfigError {
constructor(spec, path, config) {
super(spec, path);
this.config = config;
this.message = `Configuration '${path.join('.')}' is not one of the '${spec[type]}' type.`;
}
}
class FormatMismatchError extends ConfigError {
constructor(spec, path, config) {
super(spec, path);
this.config = config;
this.message = `Configuration '${path.join('.')}' do not match the format '${spec[format]}'.`;
}
}
class VersionError extends Error {
}
class VersionNotFoundError extends VersionError {
constructor() {
super(`Version number is not found in the configuration file.`);
}
}
class VersionMalformedError extends VersionError {
constructor(version) {
super(`Version number ${version} is malformed.`);
this.version = version;
}
}
class VersionMismatchError extends VersionError {
constructor(specVersion, configVersion, isConfigVersionSmaller) {
super();
this.specVersion = specVersion;
this.configVersion = configVersion;
if (isConfigVersionSmaller) {
this.message = `The configuration version ${configVersion} is far behind the specification version ${specVersion}.`;
} else {
this.message = `The configuration version ${configVersion} is way ahead of the specification version ${specVersion}.`;
}
}
}
const errors = {
ConfigError,
InvalidSpecError,
MissingRequiredError,
TypeMismatchError,
FormatMismatchError,
VersionError,
VersionMalformedError,
VersionNotFoundError,
VersionMismatchError
}
module.exports = {
is,
descriptors,
errors
};

View File

@ -0,0 +1,25 @@
const cheerio = require('cheerio');
module.exports = function (hexo) {
function patchCodeHighlight(content) {
const $ = cheerio.load(content, { decodeEntities: false });
$('figure.highlight').addClass('hljs');
$('figure.highlight .code .line span').each(function () {
const classes = $(this).attr('class').split(' ');
if (classes.length === 1) {
$(this).addClass('hljs-' + classes[0]);
$(this).removeClass(classes[0]);
}
});
return $.html();
}
/**
* Add .hljs class name to the code blocks and code elements
*/
hexo.extend.filter.register('after_post_render', function (data) {
data.content = data.content ? patchCodeHighlight(data.content) : data.content;
data.excerpt = data.excerpt ? patchCodeHighlight(data.excerpt) : data.excerpt;
return data;
});
}

View File

@ -0,0 +1,14 @@
/**
* Category list page generator
*/
module.exports = function (hexo) {
hexo.extend.generator.register('categories', function (locals) {
return {
path: 'categories/',
layout: ['categories'],
data: Object.assign({}, locals, {
__categories: true
})
};
});
}

View File

@ -0,0 +1,34 @@
const pagination = require('hexo-pagination');
module.exports = function (hexo) {
// ATTENTION: This will override the default category generator!
hexo.extend.generator.register('category', function(locals) {
const config = this.config;
const perPage = config.category_generator.per_page;
const paginationDir = config.pagination_dir || 'page';
function findParent(category) {
let parents = [];
if (category && category.hasOwnProperty('parent')) {
const parent = locals.categories.filter(cat => cat._id === category.parent).first();
parents = [parent].concat(findParent(parent));
}
return parents;
}
return locals.categories.reduce(function(result, category){
const posts = category.posts.sort('-date');
const data = pagination(category.path, posts, {
perPage: perPage,
layout: ['category', 'archive', 'index'],
format: paginationDir + '/%d/',
data: {
category: category.name,
parents: findParent(category)
}
});
return result.concat(data);
}, []);
});
}

View File

@ -0,0 +1,43 @@
const util = require('hexo-util');
/**
* Insight search content.json generator.
*/
module.exports = function (hexo) {
hexo.extend.generator.register('insight', function (locals) {
const url_for = hexo.extend.helper.get('url_for').bind(this);
function minify(str) {
return util.stripHTML(str).trim().replace(/\n/g, ' ').replace(/\s+/g, ' ')
.replace(/&#x([\da-fA-F]+);/g, function (match, hex) {
return String.fromCharCode(parseInt(hex, 16));
})
.replace(/&#([\d]+);/g, function (match, dec) {
return String.fromCharCode(dec);
});
}
function postMapper(post) {
return {
title: post.title,
text: minify(post.content),
link: url_for(post.path)
}
}
function tagMapper(tag) {
return {
name: tag.name,
slug: tag.slug,
link: url_for(tag.path)
}
}
const site = {
pages: locals.pages.map(postMapper),
posts: locals.posts.map(postMapper),
tags: locals.tags.map(tagMapper),
categories: locals.categories.map(tagMapper)
};
return {
path: '/content.json',
data: JSON.stringify(site)
};
});
}

View File

@ -0,0 +1,14 @@
/**
* Tag list page generator
*/
module.exports = function (hexo) {
hexo.extend.generator.register('tags', function (locals) {
return {
path: 'tags/',
layout: ['tags'],
data: Object.assign({}, locals, {
__tags: true
})
};
});
}

View File

@ -0,0 +1,81 @@
/**
* CDN static file resolvers.
*
* @example
* <%- cdn(package, version, filename) %>
* <%- fontcdn(fontName) %>
* <%- iconcdn() %>
*/
const cdn_providers = {
cdnjs: 'https://cdnjs.cloudflare.com/ajax/libs/${ package }/${ version }/${ filename }',
jsdelivr: 'https://cdn.jsdelivr.net/npm/${ package }@${ version }/${ filename }',
unpkg: 'https://unpkg.com/${ package }@${ version }/${ filename }'
};
const font_providers = {
google: 'https://fonts.googleapis.com/${ type }?family=${ fontname }'
};
const icon_providers = {
fontawesome: 'https://use.fontawesome.com/releases/v5.4.1/css/all.css'
};
module.exports = function (hexo) {
hexo.extend.helper.register('cdn', function (_package, version, filename) {
let provider = hexo.extend.helper.get('get_config').bind(this)('providers.cdn');
// cdn.js does not follow a GitHub npm style like jsdeliver and unpkg do. Patch it!
if (provider === 'cdnjs' || provider.startsWith('[cdnjs]')) {
if (provider.startsWith('[cdnjs]')) {
provider = provider.substr(7);
}
if (filename.startsWith('dist/')) {
filename = filename.substr(5);
}
if (_package === 'moment') {
_package = 'moment.js';
filename = filename.startsWith('min/') ? filename.substr(4) : filename;
}
if (_package === 'outdatedbrowser') {
_package = 'outdated-browser';
filename = filename.startsWith('outdatedbrowser/') ? filename.substr(16) : filename;
}
if (_package === 'highlight.js') {
filename = filename.endsWith('.css') && filename.indexOf('.min.') === -1 ?
filename.substr(0, filename.length - 4) + '.min.css' : filename;
}
if (_package === 'mathjax') {
filename = filename.startsWith('unpacked/') ? filename.substr(9) : filename;
}
if (_package === 'pace-js') {
_package = 'pace';
}
}
if (provider !== null && cdn_providers.hasOwnProperty(provider)) {
provider = cdn_providers[provider];
}
return provider.replace(/\${\s*package\s*}/gi, _package)
.replace(/\${\s*version\s*}/gi, version)
.replace(/\${\s*filename\s*}/gi, filename);
});
hexo.extend.helper.register('fontcdn', function (fontName, type = 'css') {
let provider = hexo.extend.helper.get('get_config').bind(this)('providers.fontcdn');
if (provider !== null && font_providers.hasOwnProperty(provider)) {
provider = font_providers[provider];
}
return provider.replace(/\${\s*fontname\s*}/gi, fontName)
.replace(/\${\s*type\s*}/gi, type);
});
hexo.extend.helper.register('iconcdn', function (provider = null) {
if (provider !== null && icon_providers.hasOwnProperty(provider)) {
provider = icon_providers[provider];
} else {
provider = hexo.extend.helper.get('get_config').bind(this)('providers.iconcdn');
if (provider !== null && icon_providers.hasOwnProperty(provider)) {
provider = icon_providers[provider];
}
}
return provider;
});
}

View File

@ -0,0 +1,48 @@
/**
* Theme configuration helpers.
*
* @description Test if a configuration is set or fetch its value. If `exclude_page` is set, the helpers will
* not look up configurations in the current page's front matter.
* @example
* <%- has_config(config_name, exclude_page) %>
* <%- get_config(config_name, default_value, exclude_page) %>
*/
const specs = require('../specs/config.spec');
const descriptors = require('../common/utils').descriptors;
module.exports = function (hexo) {
function readProperty(object, path) {
const paths = path.split('.');
for (let path of paths) {
if (typeof (object) === 'undefined' || object === null || !object.hasOwnProperty(path)) {
return null;
}
object = object[path];
}
return object;
}
hexo.extend.helper.register('get_config', function (configName, defaultValue = undefined, excludePage = false) {
const value = readProperty(Object.assign({}, this.config, hexo.theme.config,
!excludePage ? this.page : {}), configName);
if (value === null) {
if (typeof(defaultValue) !== 'undefined') {
return defaultValue;
} else {
const property = readProperty(specs, configName);
return property === null ? null : property[descriptors.defaultValue];
}
}
return value;
});
hexo.extend.helper.register('has_config', function (configName, excludePage = false) {
const readProperty = hexo.extend.helper.get('get_config').bind(this);
return readProperty(configName, null, excludePage) != null;
});
hexo.extend.helper.register('get_config_from_obj', function (object, configName, defaultValue = null) {
const value = readProperty(object, configName);
return value === null ? defaultValue : value;
});
}

View File

@ -0,0 +1,31 @@
/**
* Helper functions for controlling layout.
*
* @example
* <%- get_widgets(position) %>
* <%- has_column() %>
* <%- column_count() %>
*/
module.exports = function (hexo) {
hexo.extend.helper.register('get_widgets', function (position) {
const hasWidgets = hexo.extend.helper.get('has_config').bind(this)('widgets');
if (!hasWidgets) {
return [];
}
const widgets = hexo.extend.helper.get('get_config').bind(this)('widgets');
return widgets.filter(widget => widget.hasOwnProperty('position') && widget.position === position);
});
hexo.extend.helper.register('has_column', function (position) {
const getWidgets = hexo.extend.helper.get('get_widgets').bind(this);
return getWidgets(position).length > 0;
});
hexo.extend.helper.register('column_count', function () {
let columns = 1;
const hasColumn = hexo.extend.helper.get('has_column').bind(this);
columns += hasColumn('left') ? 1 : 0;
columns += hasColumn('right') ? 1 : 0;
return columns;
});
}

View File

@ -0,0 +1,124 @@
/**
* Helper functions that override Hexo built-in helpers.
*
* @example
* <%- _list_archives() %>
* <%- _list_categories() %>
* <%- _list_tags() %>
* <%- _toc() %>
*/
const cheerio = require('cheerio');
module.exports = function (hexo) {
hexo.extend.helper.register('_list_archives', function () {
const $ = cheerio.load(this.list_archives(), { decodeEntities: false });
const archives = [];
$('.archive-list-item').each(function () {
archives.push({
url: $(this).find('.archive-list-link').attr('href'),
name: $(this).find('.archive-list-link').text(),
count: $(this).find('.archive-list-count').text()
});
});
return archives;
});
hexo.extend.helper.register('_list_categories', function () {
const $ = cheerio.load(this.list_categories({ depth: 2 }), { decodeEntities: false });
function traverse(root) {
const categories = [];
root.find('> .category-list-item').each(function () {
const category = {
url: $(this).find('> .category-list-link').attr('href'),
name: $(this).find('> .category-list-link').text(),
count: $(this).find('> .category-list-count').text()
};
if ($(this).find('> .category-list-child').length) {
category['children'] = traverse($(this).find('> .category-list-child'));
}
categories.push(category);
});
return categories;
}
return traverse($('.category-list'));
});
hexo.extend.helper.register('_list_tags', function () {
const $ = cheerio.load(this.list_tags(), { decodeEntities: false });
const tags = [];
$('.tag-list-item').each(function () {
tags.push({
url: $(this).find('.tag-list-link').attr('href'),
name: $(this).find('.tag-list-link').text(),
count: $(this).find('.tag-list-count').text()
});
});
return tags;
});
/**
* Export a tree of headings of an article
* {
* "1": {
* "id": "How-to-enable-table-of-content-for-a-post",
* "index": "1"
* },
* "2": {
* "1": {
* "1": {
* "id": "Third-level-title",
* "index": "2.1.1"
* },
* "id": "Second-level-title",
* "index": "2.1"
* },
* "2": {
* "id": "Another-second-level-title",
* "index": "2.2"
* },
* "id": "First-level-title",
* "index": "2"
* }
* }
*/
hexo.extend.helper.register('_toc', (content) => {
const $ = cheerio.load(content, { decodeEntities: false });
const toc = {};
const levels = [0, 0, 0];
// Get top 3 headings that are present in the content
const tags = [1, 2, 3, 4, 5, 6].map(i => 'h' + i).filter(h => $(h).length > 0).slice(0, 3);
if (tags.length === 0) {
return toc;
}
$(tags.join(',')).each(function () {
const level = tags.indexOf(this.name);
const id = $(this).attr('id');
const text = $(this).text();
for (let i = 0; i < levels.length; i++) {
if (i > level) {
levels[i] = 0;
} else if (i < level) {
if (levels[i] === 0) {
// if headings start with a lower level heading, set the former heading index to 1
// e.g. h3, h2, h1, h2, h3 => 1.1.1, 1.2, 2, 2.1, 2.1.1
levels[i] = 1;
}
} else {
levels[i] += 1;
}
}
let node = toc;
for (let i of levels.slice(0, level + 1)) {
if (!node.hasOwnProperty(i)) {
node[i] = {};
}
node = node[i];
}
node.id = id;
node.text = text;
node.index = levels.slice(0, level + 1).join('.');
});
return toc;
});
}

View File

@ -0,0 +1,96 @@
/**
* Helper functions for page/post.
*
* @example
* <%- is_categories(page) %>
* <%- is_tags(page) %>
* <%- page_title(page) %>
* <%- meta(post) %>
* <%- has_thumbnail(post) %>
* <%- get_thumbnail(post) %>
*/
module.exports = function (hexo) {
function trim(str) {
return str.trim().replace(/^"(.*)"$/, '$1').replace(/^'(.*)'$/, '$1');
}
function split(str, sep) {
var result = [];
var matched = null;
while (matched = sep.exec(str)) {
result.push(matched[0]);
}
return result;
}
hexo.extend.helper.register('is_categories', function (page = null) {
return (page === null ? this.page : page).__categories;
});
hexo.extend.helper.register('is_tags', function (page = null) {
return (page === null ? this.page : page).__tags;
});
/**
* Generate html head title based on page type
*/
hexo.extend.helper.register('page_title', function (page = null) {
page = page === null ? this.page : page;
let title = page.title;
if (this.is_archive()) {
title = this._p('common.archive', Infinity);
if (this.is_month()) {
title += ': ' + page.year + '/' + page.month;
} else if (this.is_year()) {
title += ': ' + page.year;
}
} else if (this.is_category()) {
title = this._p('common.category', 1) + ': ' + page.category;
} else if (this.is_tag()) {
title = this._p('common.tag', 1) + ': ' + page.tag;
} else if (this.is_categories()) {
title = this._p('common.category', Infinity);
} else if (this.is_tags()) {
title = this._p('common.tag', Infinity);
}
const siteTitle = hexo.extend.helper.get('get_config').bind(this)('title', '', true);
return [title, siteTitle].filter(str => typeof (str) !== 'undefined' && str.trim() !== '').join(' - ');
});
hexo.extend.helper.register('meta', function (post) {
var metas = post.meta || [];
var output = '';
var metaDOMArray = metas.map(function (meta) {
var entities = split(meta, /(?:[^\\;]+|\\.)+/g);
var entityArray = entities.map(function (entity) {
var keyValue = split(entity, /(?:[^\\=]+|\\.)+/g);
if (keyValue.length < 2) {
return null;
}
var key = trim(keyValue[0]);
var value = trim(keyValue[1]);
return key + '="' + value + '"';
}).filter(function (entity) {
return entity;
});
return '<meta ' + entityArray.join(' ') + ' />';
});
return metaDOMArray.join('\n');
});
hexo.extend.helper.register('has_thumbnail', function (post) {
const getConfig = hexo.extend.helper.get('get_config').bind(this);
const allowThumbnail = getConfig('article.thumbnail', true);
if (!allowThumbnail) {
return false;
}
return post.hasOwnProperty('thumbnail') && post.thumbnail;
});
hexo.extend.helper.register('get_thumbnail', function (post) {
const hasThumbnail = hexo.extend.helper.get('has_thumbnail').bind(this)(post);
return this.url_for(hasThumbnail ? post.thumbnail : 'images/thumbnail.svg');
});
}

View File

@ -0,0 +1,53 @@
/**
* Helper functions related the site properties.
*
* @example
* <%- is_same_link(url_a, url_b) %>
* <%- post_count() %>
* <%- category_count() %>
* <%- tag_count() %>
* <%- duration() %>
* <%- word_count(content) %>
*/
const moment = require('moment');
module.exports = function (hexo) {
hexo.extend.helper.register('is_same_link', function (a, b) {
function santize(url) {
let paths = url.replace(/(^\w+:|^)\/\//, '').split('#')[0].split('/').filter(p => p.trim() !== '');
if (paths.length > 0 && paths[paths.length - 1].trim() === 'index.html') {
paths = paths.slice(0, paths.length - 1)
}
return paths.join('/');
}
return santize(this.url_for(a)) == santize(this.url_for(b));
});
hexo.extend.helper.register('post_count', function () {
return this.site.posts.length;
});
hexo.extend.helper.register('category_count', function () {
return this.site.categories.filter(category => category.length).length;
});
hexo.extend.helper.register('tag_count', function () {
return this.site.tags.filter(tag => tag.length).length;
});
/**
* Export moment.duration
*/
hexo.extend.helper.register('duration', function () {
return moment.duration.apply(moment, arguments);
});
/**
* Get the word count of a paragraph.
*/
hexo.extend.helper.register('word_count', function (content) {
content = content.replace(/<\/?[a-z][^>]*>/gi, '');
content = content.trim();
return content ? (content.match(/[\u00ff-\uffff]|[a-zA-Z]+/g) || []).length : 0;
});
}

View File

@ -0,0 +1,21 @@
const { doc, type, defaultValue } = require('../common/utils').descriptors;
module.exports = {
[type]: 'object',
[doc]: 'Article display settings',
highlight: {
[type]: 'string',
[doc]: 'Code highlight theme (https://github.com/highlightjs/highlight.js/tree/master/src/styles)',
[defaultValue]: 'atom-one-light'
},
thumbnail: {
[type]: 'boolean',
[doc]: 'Whether to show article thumbnail images',
[defaultValue]: true
},
readtime: {
[type]: 'boolean',
[doc]: 'Whether to show estimate article reading time',
[defaultValue]: true
}
};

View File

@ -0,0 +1,119 @@
const { doc, type, defaultValue, required, requires } = require('../common/utils').descriptors;
const ChangYanSpec = {
appid: {
[type]: 'string',
[doc]: 'Changyan comment app ID',
[required]: true,
[requires]: comment => comment.type === 'changyan'
},
conf: {
[type]: 'string',
[doc]: 'Changyan comment configuration ID',
[required]: true,
[requires]: comment => comment.type === 'changyan'
}
};
const DisqusSpec = {
shortname: {
[type]: 'string',
[doc]: 'Disqus shortname',
[required]: true,
[requires]: comment => comment.type === 'disqus'
}
};
const GitmentSpec = {
owner: {
[type]: 'string',
[doc]: 'Your GitHub ID',
[required]: true,
[requires]: comment => comment.type === 'gitment'
},
repo: {
[type]: 'string',
[doc]: 'The repo to store comments',
[required]: true,
[requires]: comment => comment.type === 'gitment'
},
client_id: {
[type]: 'string',
[doc]: 'Your client ID',
[required]: true,
[requires]: comment => comment.type === 'gitment'
},
client_secret: {
[type]: 'string',
[doc]: 'Your client secret',
[required]: true,
[requires]: comment => comment.type === 'gitment'
}
};
const IssoSpec = {
url: {
[type]: 'string',
[doc]: 'URL to your Isso comment service',
[required]: true,
[requires]: comment => comment.type === 'isso'
}
};
const LiveReSpec = {
uid: {
[type]: 'string',
[doc]: 'LiveRe comment service UID',
[required]: true,
[requires]: comment => comment.type === 'livere'
}
};
const ValineSpec = {
app_id: {
[type]: 'string',
[doc]: 'LeanCloud APP ID',
[required]: true,
[requires]: comment => comment.type === 'valine'
},
app_key: {
[type]: 'string',
[doc]: 'LeanCloud APP key',
[required]: true,
[requires]: comment => comment.type === 'valine'
},
notify: {
[type]: 'boolean',
[doc]: 'Enable email notification when someone comments',
[defaultValue]: false,
[requires]: comment => comment.type === 'valine'
},
verify: {
[type]: 'boolean',
[doc]: 'Enable verification code service',
[defaultValue]: false,
[requires]: comment => comment.type === 'valine'
},
placeholder: {
[type]: 'string',
[doc]: 'Placeholder text in the comment box',
[defaultValue]: 'Say something...',
[requires]: comment => comment.type === 'valine'
}
};
module.exports = {
[type]: 'object',
[doc]: 'Comment plugin settings (http://ppoffice.github.io/hexo-theme-icarus/categories/Configuration/Comment-Plugins)',
type: {
[type]: 'string',
[doc]: 'Name of the comment plugin',
[defaultValue]: null
},
...ChangYanSpec,
...DisqusSpec,
...GitmentSpec,
...IssoSpec,
...LiveReSpec,
...ValineSpec
}

View File

@ -0,0 +1,24 @@
const { version } = require('../../package.json');
const { type, required, defaultValue, doc } = require('../common/utils').descriptors;
module.exports = {
[type]: 'object',
[doc]: 'Root of the configuration file',
[required]: true,
version: {
[type]: 'string',
[doc]: 'Version of the Icarus theme that is currently used',
[required]: true,
[defaultValue]: version
},
...require('./meta.spec'),
navbar: require('./navbar.spec'),
footer: require('./footer.spec'),
article: require('./article.spec'),
search: require('./search.spec'),
comment: require('./comment.spec'),
share: require('./share.spec'),
widgets: require('./widgets.spec'),
plugins: require('./plugins.spec'),
providers: require('./providers.spec')
};

View File

@ -0,0 +1,24 @@
const { doc, type, defaultValue } = require('../common/utils').descriptors;
module.exports = {
[type]: 'object',
[doc]: 'Footer section link settings',
links: {
...require('./icon_link.spec'),
[doc]: 'Links to be shown on the right of the footer section',
[defaultValue]: {
'Creative Commons': {
icon: 'fab fa-creative-commons',
url: 'https://creativecommons.org/'
},
'Attribution 4.0 International': {
icon: 'fab fa-creative-commons-by',
url: 'https://creativecommons.org/licenses/by/4.0/'
},
'Download on GitHub': {
icon: 'fab fa-github',
url: 'http://github.com/ppoffice/hexo-theme-icarus'
}
}
}
};

View File

@ -0,0 +1,20 @@
const { doc, type, required } = require('../common/utils').descriptors;
module.exports = {
[type]: 'object',
[doc]: 'Link icon settings',
'*': {
[type]: ['string', 'object'],
[doc]: 'Path or URL to the menu item, and/or link icon class names',
icon: {
[required]: true,
[type]: 'string',
[doc]: 'Link icon class names'
},
url: {
[required]: true,
[type]: 'string',
[doc]: 'Path or URL to the menu item'
}
}
};

View File

@ -0,0 +1,52 @@
const { doc, type, defaultValue } = require('../common/utils').descriptors;
module.exports = {
favicon: {
[type]: 'string',
[doc]: 'Path or URL to the website\'s icon',
[defaultValue]: '/images/favicon.svg',
},
rss: {
[type]: 'string',
[doc]: 'Path or URL to RSS atom.xml',
[defaultValue]: null
},
logo: {
[type]: ['string', 'object'],
[defaultValue]: '/images/logo.svg',
[doc]: 'Path or URL to the website\'s logo to be shown on the left of the navigation bar or footer',
text: {
[type]: 'string',
[doc]: 'Text to be shown in place of the logo image'
}
},
open_graph: {
[type]: 'object',
[doc]: 'Open Graph metadata (https://hexo.io/docs/helpers.html#open-graph)',
fb_app_id: {
[type]: 'string',
[doc]: 'Facebook App ID',
[defaultValue]: null
},
fb_admins: {
[type]: 'string',
[doc]: 'Facebook Admin ID',
[defaultValue]: null
},
twitter_id: {
[type]: 'string',
[doc]: 'Twitter ID',
[defaultValue]: null
},
twitter_site: {
[type]: 'string',
[doc]: 'Twitter site',
[defaultValue]: null
},
google_plus: {
[type]: 'string',
[doc]: 'Google+ profile link',
[defaultValue]: null
}
}
};

View File

@ -0,0 +1,31 @@
const { doc, type, defaultValue } = require('../common/utils').descriptors;
module.exports = {
[type]: 'object',
[doc]: 'Navigation bar link settings',
menu: {
[type]: 'object',
[doc]: 'Navigation bar menu links',
[defaultValue]: {
Home: '/',
Archives: '/archives',
Categories: '/categories',
Tags: '/tags',
About: '/about'
},
'*': {
[type]: 'string',
[doc]: 'Path or URL to the menu item'
}
},
links: {
...require('./icon_link.spec'),
[doc]: 'Navigation bar links to be shown on the right',
[defaultValue]: {
'Download on GitHub': {
icon: 'fab fa-github',
url: 'http://github.com/ppoffice/hexo-theme-icarus'
}
}
}
};

View File

@ -0,0 +1,63 @@
const { doc, type, defaultValue } = require('../common/utils').descriptors;
module.exports = {
[type]: 'object',
[doc]: 'Other plugin settings',
gallery: {
[type]: 'boolean',
[doc]: 'Enable the lightGallery and Justified Gallery plugins (http://ppoffice.github.io/hexo-theme-icarus/2016/07/08/plugin/Gallery/)',
[defaultValue]: true
},
'outdated-browser': {
[type]: 'boolean',
[doc]: 'Enable the Outdated Browser plugin (http://outdatedbrowser.com/)',
[defaultValue]: true
},
animejs: {
[type]: 'boolean',
[doc]: 'Enable page animations (http://animejs.com/)',
[defaultValue]: true
},
mathjax: {
[type]: 'boolean',
[doc]: 'Enable the MathJax plugin (http://ppoffice.github.io/hexo-theme-icarus/2018/01/01/plugin/MathJax/)',
[defaultValue]: true
},
'back-to-top': {
[type]: 'boolean',
[doc]: 'Show the back to top button on mobile devices',
[defaultValue]: true
},
'google-analytics': {
[type]: ['boolean', 'object'],
[doc]: 'Google Analytics plugin settings (http://ppoffice.github.io/hexo-theme-icarus/2018/01/01/plugin/Analytics/#Google-Analytics)',
tracking_id: {
[type]: 'string',
[doc]: 'Google Analytics tracking id',
[defaultValue]: null
}
},
'baidu-analytics': {
[type]: ['boolean', 'object'],
[doc]: 'Baidu Analytics plugin settings (http://ppoffice.github.io/hexo-theme-icarus/2018/01/01/plugin/Analytics/#Baidu-Analytics)',
tracking_id: {
[type]: 'string',
[doc]: 'Baidu Analytics tracking id',
[defaultValue]: null
}
},
hotjar: {
[type]: ['boolean', 'object'],
[doc]: 'Hotjar user feedback plugin (http://ppoffice.github.io/hexo-theme-icarus/2018/01/01/plugin/Analytics/#Hotjar)',
site_id: {
[type]: ['string', 'number'],
[doc]: 'Hotjar site id',
[defaultValue]: null
}
},
progressbar: {
[type]: 'boolean',
[doc]: 'Show a loading progress bar at top of the page',
[defaultValue]: true
}
};

View File

@ -0,0 +1,21 @@
const { doc, type, defaultValue } = require('../common/utils').descriptors;
module.exports = {
[type]: 'object',
[doc]: 'CDN provider settings',
cdn: {
[type]: 'string',
[doc]: 'Name or URL of the JavaScript and/or stylesheet CDN provider',
[defaultValue]: 'jsdelivr'
},
fontcdn: {
[type]: 'string',
[doc]: 'Name or URL of the webfont CDN provider',
[defaultValue]: 'google'
},
iconcdn: {
[type]: 'string',
[doc]: 'Name or URL of the webfont Icon CDN provider',
[defaultValue]: 'fontawesome'
}
};

View File

@ -0,0 +1,17 @@
const { doc, type, defaultValue, required, requires } = require('../common/utils').descriptors;
module.exports = {
[type]: 'object',
[doc]: 'Search plugin settings (http://ppoffice.github.io/hexo-theme-icarus/categories/Configuration/Search-Plugins)',
type: {
[type]: 'string',
[doc]: 'Name of the search plugin',
[defaultValue]: 'insight'
},
cx: {
[type]: 'string',
[doc]: 'Google CSE cx value',
[required]: true,
[requires]: search => search.type === 'google-cse'
}
};

View File

@ -0,0 +1,17 @@
const { doc, type, defaultValue, required, requires } = require('../common/utils').descriptors;
module.exports = {
[type]: 'object',
[doc]: 'Share plugin settings (http://ppoffice.github.io/hexo-theme-icarus/categories/Configuration/Share-Plugins)',
type: {
[type]: 'string',
[doc]: 'Share plugin name',
[defaultValue]: null
},
install_url: {
[type]: 'string',
[doc]: 'URL to the share plugin script provided by share plugin service provider',
[required]: true,
[requires]: share => share.type === 'sharethis' || share.type === 'addthis'
}
}

View File

@ -0,0 +1,145 @@
const { doc, type, defaultValue, required, requires, format } = require('../common/utils').descriptors;
const DEFAULT_WIDGETS = [
{
type: 'profile',
position: 'left',
author: 'Your name',
author_title: 'Your title',
location: 'Your location',
avatar: null,
gravatar: null,
follow_link: 'http://github.com/ppoffice',
social_links: {
Github: {
icon: 'fab fa-github',
url: 'http://github.com/ppoffice'
},
Facebook: {
icon: 'fab fa-facebook',
url: 'http://facebook.com'
},
Twitter: {
icon: 'fab fa-twitter',
url: 'http://twitter.com'
},
Dribbble: {
icon: 'fab fa-dribbble',
url: 'http://dribbble.com'
},
RSS: {
icon: 'fas fa-rss',
url: '/'
}
}
},
{
type: 'toc',
position: 'left'
},
{
type: 'links',
position: 'left',
links: {
Hexo: 'https://hexo.io',
PPOffice: 'https://github.com/ppoffice'
}
},
{
type: 'category',
position: 'left'
},
{
type: 'tagcloud',
position: 'left'
},
{
type: 'recent_posts',
position: 'right'
},
{
type: 'archive',
position: 'right'
},
{
type: 'tag',
position: 'right'
}
];
const ProfileSpec = {
author: {
[type]: 'string',
[doc]: 'Author name to be shown in the profile widget',
[defaultValue]: 'Your name'
},
author_title: {
[type]: 'string',
[doc]: 'Title of the author to be shown in the profile widget',
[defaultValue]: 'Your title'
},
location: {
[type]: 'string',
[doc]: 'Author\'s current location to be shown in the profile widget',
[defaultValue]: 'Your location'
},
avatar: {
[type]: 'string',
[doc]: 'Path or URL to the avatar to be shown in the profile widget',
[defaultValue]: '/images/avatar.png'
},
gravatar: {
[type]: 'string',
[doc]: 'Email address for the Gravatar to be shown in the profile widget',
},
follow_link: {
[type]: 'string',
[doc]: 'Path or URL for the follow button',
},
social_links: {
...require('./icon_link.spec'),
[doc]: 'Links to be shown on the bottom of the profile widget',
}
};
for (let key in ProfileSpec) {
ProfileSpec[key][requires] = widget => widget.type === 'profile';
}
const LinksSpec = {
links: {
[type]: 'object',
[doc]: 'Links to be shown in the links widget',
[requires]: parent => parent.type === 'links',
'*': {
[type]: 'string',
[doc]: 'Path or URL to the link',
[required]: true
}
}
};
module.exports = {
[type]: 'array',
[doc]: 'Sidebar widget settings (http://ppoffice.github.io/hexo-theme-icarus/categories/Widgets/)',
[defaultValue]: DEFAULT_WIDGETS,
'*': {
[type]: 'object',
[doc]: 'Single widget settings',
type: {
[type]: 'string',
[doc]: 'Widget name',
[required]: true,
[defaultValue]: 'profile'
},
position: {
[type]: 'string',
[doc]: 'Where should the widget be placed, left or right',
[format]: /^(left|right)$/,
[required]: true,
[defaultValue]: 'left'
},
...ProfileSpec,
...LinksSpec
}
}

View File

@ -0,0 +1,46 @@
const fs = require('fs');
const util = require('util');
const path = require('path');
const logger = require('hexo-log')();
const yaml = require('js-yaml');
const { errors } = require('../common/utils');
const rootSpec = require('../specs/config.spec');
const ConfigValidator = require('../common/ConfigValidator');
const ConfigGenerator = require('../common/ConfigGenerator');
const CONFIG_PATH = path.join(__dirname, '../..', '_config.yml');
logger.info('Validating the configuration file');
if (!fs.existsSync(CONFIG_PATH)) {
const relativePath = path.relative(process.cwd(), CONFIG_PATH);
logger.warn(`${relativePath} is not found. We are creating one for you...`);
fs.writeFileSync(CONFIG_PATH, new ConfigGenerator(rootSpec).generate());
logger.info(`${relativePath} is created. Please restart Hexo to apply changes.`);
process.exit(0);
}
const validator = new ConfigValidator(rootSpec);
const config = yaml.safeLoad(fs.readFileSync(CONFIG_PATH));
try {
validator.validate(config);
} catch (e) {
if (e instanceof errors.ConfigError) {
logger.error(e.message);
if (e.hasOwnProperty('spec')) {
logger.error('The specification of this configuration is:');
logger.error(util.inspect(e.spec));
}
if (e.hasOwnProperty('config')) {
logger.error('Configuration value is:');
logger.error(util.inspect(e.config));
}
} else if (e instanceof errors.VersionError) {
logger.error(e.message);
logger.warn(`To let us create a fresh configuration file for you, please rename or delete the following file:`);
logger.warn(CONFIG_PATH);
} else {
throw e;
}
}

View File

@ -0,0 +1,32 @@
const logger = require('hexo-log')();
function checkDependency(name) {
try {
require.resolve(name);
return true;
} catch(e) {
logger.error(`Package ${name} is not installed.`)
}
return false;
}
logger.info('Checking dependencies');
const missingDeps = [
'js-yaml',
'moment',
'cheerio',
'hexo-util',
'hexo-log',
'hexo-pagination',
'hexo-generator-archive',
'hexo-generator-category',
'hexo-generator-index',
'hexo-generator-tag',
'hexo-renderer-ejs',
'hexo-renderer-marked',
'hexo-renderer-stylus',
].map(checkDependency).some(installed => !installed);
if (missingDeps) {
logger.error('Please install the missing dependencies from the root directory of your Hexo site.');
process.exit(-1);
}

View File

@ -0,0 +1,10 @@
const logger = require('hexo-log')();
logger.info(`=======================================
=============================================`);

View File

@ -0,0 +1,39 @@
common:
archive:
one: 'Archive'
other: 'Archives'
category:
one: 'Category'
other: 'Categories'
tag:
one: 'Tag'
other: 'Tags'
post:
one: 'Post'
other: 'Posts'
prev: 'Previous'
next: 'Next'
widget:
follow: 'Follow'
recents: 'Recent'
links: 'Links'
tag_cloud: 'Tag Cloud'
catalogue: 'Catalogue'
article:
more: 'Read More'
comments: 'Comments'
read: 'read'
about: 'About'
words: 'words'
plugin:
backtotop: 'Back to Top'
search:
search: 'Search'
hint: 'Type something...'
insight:
hint: 'Type something...'
posts: 'Posts'
pages: 'Pages'
categories: 'Categories'
tags: 'Tags'
untitled: '(Untitled)'

View File

@ -0,0 +1,38 @@
#By SrWoOoW
common:
archive:
one: 'Archivo'
other: 'Archivos'
category:
one: 'Categoria'
other: 'Categorias'
tag:
one: 'Etiqueta'
other: 'Etiquetas'
post:
one: 'Entrada'
other: 'Entradas'
prev: 'Anterior'
next: 'Siguiente'
widget:
follow: 'SEGUIR'
recents: 'Recientes'
links: 'Links'
tag_cloud: 'Nube de etiquetas'
catalogue: 'Catálogo'
article:
more: 'Read More'
comments: 'Comentarios'
read: 'read'
about: 'About'
words: 'words'
search:
search: 'Search'
hint: 'Type something...'
insight:
hint: 'Type something...'
posts: 'Entradas'
pages: 'Pages'
categories: 'Categorias'
tags: 'Etiquetas'
untitled: '(Untitled)'

View File

@ -0,0 +1,37 @@
common:
archive:
one: 'Archive'
other: 'Archives'
category:
one: 'Catégorie'
other: 'Catégories'
tag:
one: 'Tag'
other: 'Tags'
post:
one: 'Article'
other: 'Articles'
prev: 'Préc'
next: 'Suiv'
widget:
follow: 'SUIVRE'
recents: 'Récents'
links: 'Liens'
tag_cloud: 'Nuage de tags'
catalogue: 'Catalogue'
article:
more: 'Read More'
comments: 'Commentaires'
read: 'read'
about: 'About'
words: 'words'
search:
search: 'Search'
hint: 'Type something...'
insight:
hint: 'Type something...'
posts: 'Articles'
pages: 'Pages'
categories: 'Catégories'
tags: 'Tags'
untitled: '(Untitled)'

View File

@ -0,0 +1,37 @@
common:
archive:
one: 'Arsip'
other: 'Arsip'
category:
one: 'Kategori'
other: 'Kategori'
tag:
one: 'tag'
other: 'tag'
post:
one: 'pos'
other: 'pos'
prev: 'Sebelumnya'
next: 'Berikutnya'
widget:
follow: 'IKUTI'
recents: 'Terbaru'
links: 'tautan'
tag_cloud: 'awan tag'
catalogue: 'Catalogue'
article:
more: 'Read More'
comments: 'Komentar'
read: 'read'
about: 'About'
words: 'words'
search:
search: 'Search'
hint: 'Tulis Sesuatu..'
insight:
hint: 'Tulis Sesuatu..'
posts: 'Pos'
pages: 'Halaman'
categories: 'Kategori'
tags: 'Tag'
untitled: 'Tanpa Judul'

View File

@ -0,0 +1,37 @@
common:
archive:
one: 'アーカイブ'
other: 'アーカイブ'
category:
one: 'カテゴリ'
other: 'カテゴリ'
tag:
one: 'タグ'
other: 'タグ'
post:
one: '投稿'
other: '投稿'
prev: '前'
next: '次'
widget:
follow: 'フォローする'
recents: '最近の記事'
links: 'リンク'
tag_cloud: 'タグクラウド'
catalogue: 'カタログ'
article:
more: 'Read More'
comments: 'コメント'
read: 'read'
about: 'About'
words: 'words'
search:
search: 'Search'
hint: 'Type something...'
insight:
hint: 'Type something...'
posts: '投稿'
pages: 'Pages'
categories: 'カテゴリ'
tags: 'タグ'
untitled: '(Untitled)'

View File

@ -0,0 +1,37 @@
common:
archive:
one: '아카이브'
other: '아카이브'
category:
one: '카테고리'
other: '카테고리'
tag:
one: '태그'
other: '태그'
post:
one: '포스트'
other: '포스트'
prev: '이전'
next: '다음'
widget:
follow: '팔로우'
recents: '최근 글'
links: '링크'
tag_cloud: '태그 클라우드'
catalogue: '카탈로그'
article:
more: '자세히 보기'
comments: '댓글'
read: 'read'
about: 'About'
words: 'words'
search:
search: 'Search'
hint: 'Type something...'
insight:
hint: 'Type something...'
posts: '포스트'
pages: 'Pages'
categories: '카테고리'
tags: '태그'
untitled: '(Untitled)'

View File

@ -0,0 +1,37 @@
common:
archive:
one: 'Arquivo'
other: 'Arquivos'
category:
one: 'Categoria'
other: 'Categorias'
tag:
one: 'Tag'
other: 'Tags'
post:
one: 'Artigo'
other: 'Artigos'
prev: 'Anterior'
next: 'Próximo'
widget:
follow: 'SEGUIR'
recents: 'Recentes'
links: 'Links'
tag_cloud: 'Nuvem de tags'
catalogue: 'Catálogo'
article:
more: 'Ler Mais'
comments: 'Comentarios'
read: 'read'
about: 'About'
words: 'words'
search:
search: 'Search'
hint: 'Digite alguma coisa...'
insight:
hint: 'Digite alguma coisa...'
posts: 'Artigos'
pages: 'Paginas'
categories: 'Categorias'
tags: 'Tags'
untitled: '(Untitled)'

View File

@ -0,0 +1,37 @@
common:
archive:
one: 'архив'
other: 'архивы'
category:
one: 'категории'
other: 'категории'
tag:
one: 'тег'
other: 'теги'
post:
one: 'пост'
other: 'посты'
prev: 'Назад'
next: 'Далее'
widget:
follow: 'Подписаться'
recents: 'недавние'
links: 'ссылки'
tag_cloud: 'облако тегов'
catalogue: 'Каталог'
article:
more: 'Читать дальше'
comments: 'Комментарии'
read: 'read'
about: 'About'
words: 'words'
search:
search: 'Search'
hint: 'Введите что-нибудь...'
insight:
hint: 'Введите что-нибудь...'
posts: 'посты'
pages: 'страницы'
categories: 'категории'
tags: 'теги'
untitled: '(Без названия)'

View File

@ -0,0 +1,37 @@
common:
archive:
one: 'Arşiv'
other: 'Arşivler'
category:
one: 'Kategori'
other: 'Kategoriler'
tag:
one: 'Etiket'
other: 'Etiketler'
post:
one: 'Gönderi'
other: 'Gönderiler'
prev: 'Önceki'
next: 'Sonraki'
widget:
follow: 'TAKİP ET'
recents: 'Son'
links: 'Linkler'
tag_cloud: 'Etiket bulutu'
catalogue: 'Katalog'
article:
more: 'Daha fazla oku'
comments: 'Yorumlar'
read: 'read'
about: 'About'
words: 'words'
search:
search: 'Search'
hint: 'Bir şeyler yaz...'
insight:
hint: 'Bir şeyler yaz...'
posts: 'Gönderiler'
pages: 'Sayfalar'
categories: 'Kategoriler'
tags: 'Etiketler'
untitled: '(Başlıksız)'

View File

@ -0,0 +1,39 @@
common:
archive:
one: '文档'
other: '文档'
category:
one: '分类'
other: '分类'
tag:
one: '标签'
other: '标签'
post:
one: '文章'
other: '文章'
prev: '上一页'
next: '下一页'
widget:
follow: '关注我'
recents: '最新文章'
links: '链接'
tag_cloud: '标签云'
catalogue: '目录'
article:
more: '阅读更多'
comments: '评论'
read: '读完'
about: '大约'
words: '个字'
plugin:
backtotop: '回到顶端'
search:
search: '搜索'
hint: '想要查找什么...'
insight:
hint: '想要查找什么...'
posts: '文章'
pages: '页面'
categories: '分类'
tags: '标签'
untitled: '(无标题)'

View File

@ -0,0 +1,37 @@
common:
archive:
one: '歸檔'
other: '歸檔'
category:
one: '分類'
other: '分類'
tag:
one: '標籤'
other: '標籤'
post:
one: '文章'
other: '文章'
prev: '上一頁'
next: '下一頁'
widget:
follow: '關注我'
recents: '最新文章'
links: '連結'
tag_cloud: '標籤雲'
catalogue: '文章目錄'
article:
more: '繼續閱讀'
comments: '評論'
read: 'read'
about: 'About'
words: 'words'
search:
search: '搜索'
hint: 'Type something...'
insight:
hint: 'Type something...'
posts: '文章'
pages: 'Pages'
categories: '分類'
tags: '標籤'
untitled: '(Untitled)'

View File

@ -0,0 +1,56 @@
<% function buildArchive(posts, year, month = null) {
const time = moment([page.year, page.month ? page.month - 1 : null].filter(i => i !== null)); %>
<div class="card widget">
<div class="card-content">
<h3 class="tag is-link">
<%= month === null ? year : time.locale(get_config('language', 'en')).format('MMMM YYYY') %>
</h3>
<div class="timeline">
<% posts.each(post => { %>
<article class="media">
<% if (has_thumbnail(post)) { %>
<a href="<%- url_for((post.link?post.link:post.path)) %>" class="media-left">
<p class="image is-64x64">
<img class="thumbnail" src="<%= get_thumbnail(post) %>" alt="<%= post.title %>">
</p>
</a>
<% } %>
<div class="media-content">
<div class="content">
<time class="has-text-grey is-size-7 is-block is-uppercase" datetime="<%= date_xml(post.date) %>"><%= date(post.date) %></time>
<a href="<%- url_for((post.link?post.link:post.path)) %>" class="has-link-black-ter is-size-6"><%= post.title %></a>
<div class="level article-meta is-mobile">
<div class="level-left">
<% if (post.categories && post.categories.length) { %>
<div class="level-item is-size-7 is-uppercase">
<%- list_categories(post.categories, {
class: 'has-link-grey ',
show_count: false,
style: 'none',
separator: ' / '
}) %>
</div>
<% } %>
</div>
</div>
</div>
</div>
</article>
<% }) %>
</div>
</div>
</div>
<% }
if (!page.year) {
let years = {};
page.posts.each(p => years[p.date.year()] = null);
for (let year of Object.keys(years).sort((a, b) => b - a)) {
let posts = page.posts.filter(p => p.date.year() == year); %>
<%- buildArchive(posts, year, null) %>
<% }
} else { %>
<%- buildArchive(page.posts, page.year, page.month) %>
<% } %>
<% if (page.total > 1) { %>
<%- partial('common/paginator') %>
<% } %>

View File

@ -0,0 +1,30 @@
<% function build_list(categories) {
return categories.map(category => {
let result = `<li>
<a class="level is-marginless" href="${category.url}">
<span class="level-start">
<span class="level-item">${category.name}</span>
</span>
<span class="level-end">
<span class="level-item tag">${category.count}</span>
</span>
</a>`;
if (category.hasOwnProperty('children')) {
result += '<ul>' + build_list(category.children) + '</ul>';
}
return result + '</li>';
}).join('');
}
%>
<div class="card widget">
<div class="card-content">
<div class="menu">
<h3 class="menu-label">
<%= _p('common.category', Infinity) %>
</h3>
<ul class="menu-list">
<%- build_list(_list_categories()) %>
</ul>
</div>
</div>
</div>

View File

@ -0,0 +1,14 @@
<div class="card">
<div class="card-content">
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li><a href="<%- url_for('/categories') %>"><%= _p('common.category', Infinity) %></a></li>
<% page.parents.forEach(category => { %>
<li><a href="<%- url_for(category.path) %>"><%= category.name %></a></li>
<% }) %>
<li class="is-active"><a href="#" aria-current="page"><%= page.category %></a></li>
</ul>
</nav>
</div>
</div>
<%- partial('index', { page }) %>

View File

@ -0,0 +1,14 @@
<% if (!has_config('comment.appid') || !has_config('comment.conf')) { %>
<div class="notification is-danger">
You forgot to set the <code>appid</code> or <code>conf</code> for Changyan. Please set it in <code>_config.yml</code>.
</div>
<% } else { %>
<div id="SOHUCS" sid="<%= post.path %>"></div>
<script charset="utf-8" type="text/javascript" src="https://changyan.sohu.com/upload/changyan.js" ></script>
<script type="text/javascript">
window.changyan.api.config({
appid: '<%= get_config('comment.appid') %>',
conf: '<%= get_config('comment.conf') %>'
});
</script>
<% } %>

View File

@ -0,0 +1,22 @@
<% if (has_config('comment.shortname')) { %>
<script>
var disqus_config = function () {
this.page.url = '<%= page.permalink %>';
this.page.identifier = '<%= page.disqusId || page.path %>';
};
(function() {
var d = document, s = d.createElement('script');
s.src = '//' + '<%= get_config('comment.shortname') %>' + '.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<% } %>
<div id="disqus_thread">
<% if (!has_config('comment.shortname')) { %>
<div class="notification is-danger">
You forgot to set the <code>shortname</code> for Disqus. Please set it in <code>_config.yml</code>.
</div>
<% } %>
<noscript>Please enable JavaScript to view the <a href="//disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
</div>

View File

@ -0,0 +1,8 @@
<script>(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/<%= get_config('language', 'en').split('-').join('_') %>/sdk.js#xfbml=1&version=v2.8";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>
<div class="fb-comments" data-width="100%" data-href="<%= page.permalink %>" data-num-posts="5"></div>

View File

@ -0,0 +1,23 @@
<% if (!has_config('comment.owner') || !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>repo</code>, <code>client_id</code>, or <code>client_secret</code> for Gitment.
Please set it in <code>_config.yml</code>.
</div>
<% } else { %>
<div id="comment-container"></div>
<link rel="stylesheet" href="https://imsun.github.io/gitment/style/default.css">
<script src="https://imsun.github.io/gitment/dist/gitment.browser.js"></script>
<script>
var gitment = new Gitment({
id: '<%= page.path %>',
owner: '<%= get_config('comment.owner') %>',
repo: '<%= get_config('comment.repo') %>',
oauth: {
client_id: '<%= get_config('comment.client_id') %>',
client_secret: '<%= get_config('comment.client_secret') %>',
},
})
gitment.render('comment-container')
</script>
<% } %>

View File

@ -0,0 +1,10 @@
<% if (!has_config('comment.url')) { %>
<div class="notification is-danger">
You forgot to set the <code>url</code> for Isso. Please set it in <code>_config.yml</code>.
</div>
<% } else { %>
<div id="isso-thread"></div>
<script data-isso="//<%= get_config('comment.url') %>" src="//<%= get_config('comment.url') %>/js/embed.min.js">
</script>
<% } %>

View File

@ -0,0 +1,22 @@
<% if (!has_config('comment.uid')) { %>
<div class="notification is-danger">
You forgot to set the <code>uid</code> for LiveRe. Please set it in <code>_config.yml</code>.
</div>
<% } else { %>
<div id="lv-container" data-id="city" data-uid="<%= get_config('comment.uid') %>">
<script type="text/javascript">
(function(d, s) {
var j, e = d.getElementsByTagName(s)[0];
if (typeof LivereTower === 'function') { return; }
j = d.createElement(s);
j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
j.async = true;
e.parentNode.insertBefore(j, e);
})(document, 'script');
</script>
<noscript> Please activate JavaScript for write a comment in LiveRe</noscript>
</div>
<% } %>

View File

@ -0,0 +1,19 @@
<% if (!has_config('comment.app_id') || !has_config('comment.app_key')) { %>
<div class="notification is-danger">
You forgot to set the <code>app_id</code> or <code>app_key</code> for Valine. Please set it in <code>_config.yml</code>.
</div>
<% } else { %>
<div id="valine-thread"></div>
<script src="//cdn1.lncld.net/static/js/3.0.4/av-min.js"></script>
<script src='//unpkg.com/valine/dist/Valine.min.js'></script>
<script>
new Valine({
el: '#valine-thread' ,
notify: <%= get_config('comment.notify') %>,
verify: <%= get_config('comment.verify') %>,
app_id: '<%= get_config('comment.app_id') %>',
app_key: '<%= get_config('comment.app_key') %>',
placeholder: '<%= get_config('comment.placeholder') %>'
});
</script>
<% } %>

View File

@ -0,0 +1,103 @@
<div class="card">
<% if (has_thumbnail(post)) { %>
<div class="card-image">
<%- index ? '<a href="' + url_for(post.link ? post.link : post.path) + '"' : '<span ' %> class="image is-7by1">
<img class="thumbnail" src="<%= get_thumbnail(post) %>" alt="<%= post.title %>">
<%- index ? '</a>' : '</span>' %>
</div>
<% } %>
<div class="card-content article <%= post.hasOwnProperty('direction') ? post.direction : '' %>">
<% if (post.layout != 'page') { %>
<div class="level article-meta is-size-7 is-uppercase is-mobile is-overflow-x-auto">
<div class="level-left">
<time class="level-item has-text-grey" datetime="<%= date_xml(post.date) %>"><%= date(post.date) %></time>
<% if (post.categories && post.categories.length) { %>
<div class="level-item">
<%- list_categories(post.categories, {
class: 'has-link-grey ',
show_count: false,
style: 'none',
separator: '&nbsp;/&nbsp;'
}) %>
</div>
<% } %>
<% if (!has_config('article.readtime') || get_config('article.readtime') === true) { %>
<span class="level-item has-text-grey">
<% const words = word_count(post._content); %>
<% const time = duration((words / 150.0) * 60, 'seconds') %>
<%= `${ time.locale(get_config('language', 'en')).humanize() } ${ __('article.read')} (${ __('article.about') } ${ words } ${ __('article.words') })` %>
</span>
<% } %>
</div>
</div>
<% } %>
<h1 class="title is-size-3 is-size-4-mobile has-text-weight-normal">
<% if (index) { %>
<a class="has-link-black-ter" href="<%- url_for(post.link ? post.link : post.path) %>"><%= post.title %></a>
<% } else { %>
<%= post.title %>
<% } %>
</h1>
<div class="content">
<%- index && post.excerpt ? post.excerpt : post.content %>
</div>
<% if (!index && post.tags && post.tags.length) { %>
<div class="level is-size-7 is-uppercase">
<div class="level-start">
<div class="level-item">
<span class="is-size-6 has-text-grey has-mr-7">#</span>
<%- list_tags(post.tags, {
class: 'has-link-grey ',
show_count: false,
style: 'link'
}) %>
</div>
</div>
</div>
<% } %>
<% if (index && post.excerpt) { %>
<div class="level is-mobile">
<div class="level-start">
<div class="level-item">
<a class="button is-size-7 is-light" href="<%- url_for(post.path) %>#more"><%= __('article.more') %></a>
</div>
</div>
</div>
<% } %>
<% if (!index && has_config('share.type')) { %>
<%- partial('share/' + get_config('share.type')) %>
<% } %>
</div>
</div>
<% if (!index && (post.prev || post.next)) { %>
<div class="card card-transparent">
<div class="level post-navigation is-flex-wrap is-mobile">
<% if (post.prev){ %>
<div class="level-start">
<a class="level level-item has-link-grey <%= !post.prev ? 'is-hidden-mobile' : '' %> article-nav-prev" href="<%- url_for(post.prev.path) %>">
<i class="level-item fas fa-chevron-left"></i>
<span class="level-item"><%= post.prev.title %></span>
</a>
</div>
<% } %>
<% if (post.next){ %>
<div class="level-end">
<a class="level level-item has-link-grey <%= !post.next ? 'is-hidden-mobile' : '' %> article-nav-next" href="<%- url_for(post.next.path) %>">
<span class="level-item"><%= post.next.title %></span>
<i class="level-item fas fa-chevron-right"></i>
</a>
</div>
<% } %>
</div>
</div>
<% } %>
<% if (!index && has_config('comment.type')) { %>
<div class="card">
<div class="card-content">
<h3 class="title is-5 has-text-weight-normal"><%= __('article.comments') %></h3>
<%- partial('comment/' + get_config('comment.type')) %>
</div>
</div>
<% } %>

View File

@ -0,0 +1,39 @@
<footer class="footer">
<div class="container">
<div class="level">
<div class="level-start has-text-centered-mobile">
<a class="footer-logo is-block has-mb-6" href="<%- url_for('/') %>">
<% if (has_config('logo.text') && get_config('logo.text')) { %>
<%= get_config('logo.text') %>
<% } else { %>
<img src="<%- url_for(get_config('logo')) %>" alt="" height="28">
<% } %>
</a>
<p class="is-size-7">
&copy; <%= date(new Date(), 'YYYY') %> <%= get_config('author') || get_config('title') %>&nbsp;
Powered by <a href="http://hexo.io/" target="_blank">Hexo</a> & <a
href="http://github.com/ppoffice/hexo-theme-icarus">Icarus</a>
</p>
</div>
<div class="level-end">
<% if (has_config('footer.links')) { %>
<div class="field has-addons is-flex-center-mobile has-mt-5-mobile is-flex-wrap is-flex-middle">
<% let links = get_config('footer.links'); %>
<% for (let name in links) {
let link = links[name]; %>
<p class="control">
<a class="button is-white <%= typeof(link) !== 'string' ? 'is-large' : '' %>" target="_blank" title="<%= name %>" href="<%= url_for(typeof(link) === 'string' ? link : link.url) %>">
<% if (typeof(link) === 'string') { %>
<%= name %>
<% } else { %>
<i class="<%= link.icon %>"></i>
<% } %>
</a>
</p>
<% } %>
</div>
<% } %>
</div>
</div>
</div>
</footer>

View File

@ -0,0 +1,34 @@
<meta charset="utf-8">
<title><%= page_title() %></title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<%- meta(page) %>
<% if (has_config('open_graph')) { %>
<%- open_graph({
twitter_id: get_config('open_graph.twitter_id'),
twitter_site: get_config('open_graph.twitter_site'),
google_plus: get_config('open_graph.google_plus'),
fb_admins: get_config('open_graph.fb_admins'),
fb_app_id: get_config('open_graph.fb_app_id')
}) %>
<% } %>
<% if (has_config('rss')) { %>
<link rel="alternative" href="<%- get_config('rss') %>" title="<%= get_config('title') %>" type="application/atom+xml">
<% } %>
<% if (has_config('favicon')) { %>
<link rel="icon" href="<%- url_for(get_config('favicon')) %>">
<% } %>
<%- css(cdn('bulma', '0.7.2', 'css/bulma.css')) %>
<%- css(iconcdn()) %>
<%- css(fontcdn('Ubuntu:400,600|Source+Code+Pro')) %>
<%- css(cdn('highlight.js', '9.12.0', 'styles/' + get_config('article.highlight') + '.css')) %>
<%- css('css/style') %>
<% if (has_config('plugins')) { %>
<% for (let plugin in get_config('plugins')) { %>
<%- partial('plugin/' + plugin, { head: true, plugin: get_config('plugins')[plugin] }) %>
<% } %>
<% } %>

View File

@ -0,0 +1,43 @@
<nav class="navbar navbar-main">
<div class="container">
<div class="navbar-brand is-flex-center">
<a class="navbar-item navbar-logo" href="<%- url_for('/') %>">
<% if (has_config('logo.text') && get_config('logo.text')) { %>
<%= get_config('logo.text') %>
<% } else { %>
<img src="<%- url_for(get_config('logo')) %>" alt="" height="28">
<% } %>
</a>
</div>
<div class="navbar-menu">
<% if (has_config('navbar.menu')) { %>
<div class="navbar-start">
<% for (let i in get_config('navbar.menu')) { let menu = get_config('navbar.menu')[i]; %>
<a class="navbar-item<% if (typeof(page.path) !== 'undefined' && is_same_link(menu, page.path)) { %> is-active<% } %>"
href="<%- url_for(menu) %>"><%= i %></a>
<% } %>
</div>
<% } %>
<div class="navbar-end">
<% if (has_config('navbar.links')) { %>
<% let links = get_config('navbar.links'); %>
<% for (let name in links) {
let link = links[name]; %>
<a class="navbar-item" target="_blank" title="<%= name %>" href="<%= url_for(typeof(link) === 'string' ? link : link.url) %>">
<% if (typeof(link) === 'string') { %>
<%= name %>
<% } else { %>
<i class="<%= link.icon %>"></i>
<% } %>
</a>
<% } %>
<% } %>
<% if (has_config('search.type')) { %>
<a class="navbar-item search" title="<%= __('search.search') %>" href="javascript:;">
<i class="fas fa-search"></i>
</a>
<% } %>
</div>
</div>
</div>
</nav>

View File

@ -0,0 +1,48 @@
<% function link_url(i) {
return url_for(i === 1 ? page.base : page.base + get_config('pagination_dir') + '/' + i + '/');
}
function pagination(c, m) {
var current = c,
last = m,
delta = 1,
left = current - delta,
right = current + delta + 1,
range = [],
elements = [],
l;
for (let i = 1; i <= last; i++) {
if (i == 1 || i == last || (i >= left && i < right)) {
range.push(i);
}
}
for (let i of range) {
if (l) {
if (i - l === 2) {
elements.push(`<li><a class="pagination-link has-text-black-ter" href="${ link_url(l + 1) }">${ l + 1 }</a></li>`);
} else if (i - l !== 1) {
elements.push(`<li><span class="pagination-ellipsis has-text-black-ter">&hellip;</span></li>`);
}
}
elements.push(`<li><a class="pagination-link${ c === i ? ' is-current' : ' has-text-black-ter'}" href="${ link_url(i) }">${ i }</a></li>`);
l = i;
}
return elements;
} %>
<div class="card card-transparent">
<nav class="pagination is-centered" role="navigation" aria-label="pagination">
<div class="pagination-previous<%= page.current > 1 ? '' : ' is-invisible is-hidden-mobile' %>">
<a class="is-flex-grow has-text-black-ter" href="<%= link_url(page.current - 1) %>"><%= __('common.prev') %></a>
</div>
<div class="pagination-next<%= page.current < page.total ? '' : ' is-invisible is-hidden-mobile' %>">
<a class="is-flex-grow has-text-black-ter" href="<%= link_url(page.current + 1) %>"><%= __('common.next') %></a>
</div>
<ul class="pagination-list is-hidden-mobile">
<% pagination(page.current, page.total).forEach(element => { %>
<%- element %>
<% }) %>
</ul>
</nav>
</div>

View File

@ -0,0 +1,11 @@
<%- js(cdn('jquery', '3.3.1', 'dist/jquery.min.js')) %>
<%- js(cdn('moment', '2.22.2', 'min/moment-with-locales.min.js')) %>
<script>moment.locale("<%= get_config('language', 'en') %>");</script>
<% if (has_config('plugins')) { %>
<% for (let plugin in get_config('plugins')) { %>
<%- partial('plugin/' + plugin, { head: false, plugin: get_config('plugins')[plugin] }) %>
<% } %>
<% } %>
<%- js('js/main') %>

View File

@ -0,0 +1,32 @@
<% if (get_widgets(position).length) { %>
<% function side_column_class() {
switch (column_count()) {
case 2:
return 'is-4-tablet is-4-desktop is-4-widescreen';
case 3:
return 'is-4-tablet is-4-desktop is-3-widescreen';
}
return '';
} %>
<% function visibility_class() {
if (column_count() === 3 && position === 'right') {
return 'is-hidden-touch is-hidden-desktop-only';
}
return '';
} %>
<% function order_class() {
return position === 'left' ? 'has-order-1' : 'has-order-3';
} %>
<div class="column <%= side_column_class() %> <%= visibility_class() %> <%= order_class() %> column-<%= position %>">
<% get_widgets(position).forEach(widget => {%>
<%- partial('widget/' + widget.type, { widget, post: page }) %>
<% }) %>
<% if (position === 'left') { %>
<div class="card card-transparent is-hidden-widescreen">
<% get_widgets('right').forEach(widget => {%>
<%- partial('widget/' + widget.type, { widget, post: page }) %>
<% }) %>
</div>
<% } %>
</div>
<% } %>

View File

@ -0,0 +1,6 @@
<% page.posts.each(function(post){ %>
<%- partial('common/article', { post, index: true }) %>
<% }); %>
<% if (page.total > 1) { %>
<%- partial('common/paginator') %>
<% } %>

View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html <%- has_config('language') ? ' lang="' + get_config('language').substring(0, 2) + '"' : '' %>>
<head>
<%- partial('common/head') %>
</head>
<body class="is-<%= column_count() %>-column">
<%- partial('common/navbar', { page }) %>
<% function main_column_class() {
switch (column_count()) {
case 1:
return 'is-12';
case 2:
return 'is-8-tablet is-8-desktop is-8-widescreen';
case 3:
return 'is-8-tablet is-8-desktop is-6-widescreen'
}
return '';
} %>
<section class="section">
<div class="container">
<div class="columns">
<div class="column <%= main_column_class() %> has-order-2 column-main"><%- body %></div>
<%- partial('common/widget', { position: 'left' }) %>
<%- partial('common/widget', { position: 'right' }) %>
</div>
</div>
</section>
<%- partial('common/footer') %>
<%- partial('common/scripts') %>
<% if (has_config('search.type')) { %>
<%- partial('search/' + get_config('search.type')) %>
<% } %>
</body>
</html>

View File

@ -0,0 +1 @@
<%- partial('common/article', {post: page, index: false}) %>

View File

@ -0,0 +1,43 @@
<% if (plugin !== false) { %>
<% if (head) { %>
<style>body>.navbar,body>.section,body>.footer{opacity: 0}</style>
<% } else { %>
<%- js(cdn('animejs', '2.2.0', 'anime.js')) %>
<script>
function isIE() {
var ua = window.navigator.userAgent;
var msie = ua.indexOf('MSIE ');
var trident = ua.indexOf('Trident/');
return (msie > 0 || trident > 0);
}
$(document).ready(function () {
$('body>.navbar,body>.section,body>.footer').css('opacity', 1);
if (!isIE()) {
['.column-main > .card',
'.column-left > .card',
'.column-right > .card'].map(function(target) {
anime({
targets: target,
scale: [0.8, 1],
opacity: [0, 1],
duration: 300,
easing: 'easeOutSine',
delay: function(el, i, l) {
return i * 100;
}
})
});
anime({
targets: '.navbar-main',
translateY: [-100, 0],
opacity: [0, 1],
duration: 300,
easing: 'easeOutSine'
});
}
});
</script>
<% } %>
<% } %>

View File

@ -0,0 +1,25 @@
<% if (plugin !== false) { %>
<% if (!head) { %>
<a id="back-to-top" title="<%= __('plugin.backtotop') %>" href="javascript:;">
<i class="fas fa-chevron-up"></i>
</a>
<style>
#back-to-top {
position: fixed;
padding: 8px 0;
transition: 0.4s ease opacity, 0.4s ease width, 0.4s ease transform, 0.4s ease border-radius;
opacity: 0;
line-height: 24px;
outline: none;
transform: translateY(120px);
}
#back-to-top.fade-in {
opacity: 1;
}
#back-to-top.rise-up {
transform: translateY(0);
}
</style>
<%- js('js/back-to-top') %>
<% } %>
<% } %>

View File

@ -0,0 +1,11 @@
<% if (head && get_config_from_obj(plugin, 'tracking_id')) { %>
<script>
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "//hm.baidu.com/hm.js?<%= get_config_from_obj(plugin, 'tracking_id') %>";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
<% } %>

View File

@ -0,0 +1,21 @@
<% if (plugin !== false) { %>
<% if (head) { %>
<%- css(cdn('lightgallery', '1.6.8', 'dist/css/lightgallery.min.css')) %>
<%- css(cdn('justifiedGallery', '3.7.0', 'dist/css/justifiedGallery.min.css')) %>
<% } else { %>
<%- js(cdn('lightgallery', '1.6.8', 'dist/js/lightgallery.min.js')) %>
<%- js(cdn('justifiedGallery', '3.7.0', 'dist/js/jquery.justifiedGallery.min.js')) %>
<script>
(function ($) {
$(document).ready(function () {
if (typeof($.fn.lightGallery) === 'function') {
$('.article').lightGallery({ selector: '.gallery-item' });
}
if (typeof($.fn.justifiedGallery) === 'function') {
$('.justified-gallery').justifiedGallery();
}
});
})(jQuery);
</script>
<% } %>
<% } %>

View File

@ -0,0 +1,10 @@
<% if (head && get_config_from_obj(plugin, 'tracking_id')) { %>
<script async src="https://www.googletagmanager.com/gtag/js?id=<%= get_config_from_obj(plugin, 'tracking_id') %>"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '<%= get_config_from_obj(plugin, 'tracking_id') %>');
</script>
<% } %>

View File

@ -0,0 +1,12 @@
<% if (head && get_config_from_obj(plugin, 'site_id')) { %>
<script>
(function(h,o,t,j,a,r){
h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
h._hjSettings={hjid:<%= get_config_from_obj(plugin, 'site_id') %>,hjsv:6};
a=o.getElementsByTagName('head')[0];
r=o.createElement('script');r.async=1;
r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
a.appendChild(r);
})(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');
</script>
<% } %>

View File

@ -0,0 +1,10 @@
<% if (!head && plugin !== false) { %>
<%- js(cdn('mathjax', '2.7.5', 'unpacked/MathJax.js?config=TeX-MML-AM_CHTML')) %>
<script>
MathJax.Hub.Config({
"HTML-CSS": {matchFontHeight: false},
SVG: {matchFontHeight: false},
CommonHTML: {matchFontHeight: false}
});
</script>
<% } %>

View File

@ -0,0 +1,22 @@
<% if (plugin !== false) { %>
<% if (head) { %>
<%- css(cdn('outdatedbrowser', '1.1.5', 'outdatedbrowser/outdatedbrowser.min.css')) %>
<% } else { %>
<div id="outdated">
<h6>Your browser is out-of-date!</h6>
<p>Update your browser to view this website correctly. <a id="btnUpdateBrowser" href="http://outdatedbrowser.com/">Update my browser now </a></p>
<p class="last"><a href="#" id="btnCloseUpdateBrowser" title="Close">&times;</a></p>
</div>
<%- js(cdn('outdatedbrowser', '1.1.5', 'outdatedbrowser/outdatedbrowser.min.js')) %>
<script>
$(document).ready(function () {
// plugin function, place inside DOM ready function
outdatedBrowser({
bgColor: '#f25648',
color: '#ffffff',
lowerThan: 'flex'
})
});
</script>
<% } %>
<% } %>

View File

@ -0,0 +1,27 @@
<% if (head && plugin !== false) { %>
<%- js(cdn('pace-js', '1.0.2', 'pace.min.js')) %>
<style>
.pace {
-webkit-pointer-events: none;
pointer-events: none;
-webkit-user-select: none;
-moz-user-select: none;
user-select: none;
}
.pace-inactive {
display: none;
}
.pace .pace-progress {
background: #3273dc;
position: fixed;
z-index: 2000;
top: 0;
right: 100%;
width: 100%;
height: 2px;
}
</style>
<% } %>

View File

@ -0,0 +1 @@
<%- partial('common/article', {post: page, index: false}) %>

View File

@ -0,0 +1,29 @@
<%- css('css/search') %>
<div class="searchbox">
<div class="searchbox-container">
<div class="searchbox-input-wrapper">
<form class="search-form">
<input name="wd" type="text" class="searchbox-input" placeholder="<%= __('search.hint') %>" />
<span class="searchbox-close searchbox-selectable"><i class="fa fa-times-circle"></i></span>
</form>
</div>
</div>
</div>
<script>
(function ($) {
$('.search-form').on('submit', function (e) {
var keyword = $('.searchbox-input[name="wd"]').val();
window.location = 'https://www.baidu.com/s?wd=site:<%= config.url.replace(/http(s)*:\/\//, "") %> ' + keyword;
return false;
});
})(jQuery);
(function (document, $) {
$(document).on('click', '.navbar-main .search', function () {
$('.searchbox').toggleClass('show');
}).on('click', '.searchbox .searchbox-mask', function () {
$('.searchbox').removeClass('show');
}).on('click', '.searchbox-close', function () {
$('.searchbox').removeClass('show');
});
})(document, jQuery);
</script>

View File

@ -0,0 +1,67 @@
<%- css('css/search') %>
<div class="searchbox google-cse-search">
<div class="searchbox-container">
<div class="searchbox-input-wrapper">
<input type="text" class="searchbox-input" placeholder="<%= __('search.hint') %>" />
<span class="searchbox-close searchbox-selectable"><i class="fa fa-times-circle"></i></span>
</div>
<% if (has_config('search.cx')) { %>
<div class="searchbox-result-wrapper">
<gcse:searchresults-only></gcse:searchresults-only>
</div>
<% } else { %>
<div class="notification is-danger">
It seems that you forget to set the <code>cx</code> value for the Google CSE. Please set it in <code>_config.yml</code>.
</div>
<% } %>
</div>
<% if (has_config('search.cx')) { %>
<script>
(function() {
var cx = '<%= get_config('search.cx') %>';
var gcse = document.createElement('script');
gcse.type = 'text/javascript';
gcse.async = true;
gcse.src = 'https://cse.google.com/cse.js?cx=' + cx;
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(gcse, s);
})();
</script>
<% } %>
</div>
<script>
(function (document, $) {
function debounce(func, wait, immediate) {
var timeout;
return function() {
var context = this, args = arguments;
var later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
var callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
$(document).on('click', '.navbar-main .search', function () {
$('.searchbox').toggleClass('show');
}).on('click', '.searchbox .searchbox-mask', function () {
$('.searchbox').removeClass('show');
}).on('click', '.searchbox-close', function () {
$('.searchbox').removeClass('show');
}).on('keydown', '.searchbox-input', debounce(function () {
var value = $(this).val();
try {
var element = google.search.cse.element.getElement('searchresults-only0');
if (value.trim() === '') {
element.clearAllResults();
} else {
element.execute(value);
}
} catch (e) {}
}, 300));
})(document, jQuery);
</script>

View File

@ -0,0 +1,29 @@
<div class="searchbox ins-search">
<div class="searchbox-container ins-search-container">
<div class="searchbox-input-wrapper">
<input type="text" class="searchbox-input ins-search-input" placeholder="<%= __('insight.hint') %>" />
<span class="searchbox-close ins-close ins-selectable"><i class="fa fa-times-circle"></i></span>
</div>
<div class="searchbox-result-wrapper ins-section-wrapper">
<div class="ins-section-container"></div>
</div>
</div>
</div>
<script>
(function (window) {
var INSIGHT_CONFIG = {
TRANSLATION: {
POSTS: '<%= __("insight.posts") %>',
PAGES: '<%= __("insight.pages") %>',
CATEGORIES: '<%= __("insight.categories") %>',
TAGS: '<%= __("insight.tags") %>',
UNTITLED: '<%= __("insight.untitled") %>',
},
CONTENT_URL: '<%- url_for("/content.json")%>',
};
window.INSIGHT_CONFIG = INSIGHT_CONFIG;
})(window);
</script>
<%- js('js/insight') %>
<%- css('css/search') %>
<%- css('css/insight') %>

View File

@ -0,0 +1,8 @@
<% if (!has_config('share.install_url')) { %>
<div class="notification is-danger">
You need to set <code>install_url</code> to use AddThis. Please set it in <code>_config.yml</code>.
</div>
<% } else { %>
<div class="addthis_inline_share_toolbox"></div>
<script type="text/javascript" src="<%= get_config('share.install_url') %>"></script>
<% } %>

View File

@ -0,0 +1,9 @@
<!-- AddToAny BEGIN -->
<div class="a2a_kit a2a_kit_size_32 a2a_default_style">
<a class="a2a_dd" href="https://www.addtoany.com/share"></a>
<a class="a2a_button_facebook"></a>
<a class="a2a_button_twitter"></a>
<a class="a2a_button_google_plus"></a>
</div>
<script async src="https://static.addtoany.com/menu/page.js"></script>
<!-- AddToAny END -->

View File

@ -0,0 +1,9 @@
<div class="bdsharebuttonbox">
<a href="#" class="bds_more" data-cmd="more"></a>
<a href="#" class="bds_qzone" data-cmd="qzone" title="分享到QQ空间"></a>
<a href="#" class="bds_tsina" data-cmd="tsina" title="分享到新浪微博"></a>
<a href="#" class="bds_tqq" data-cmd="tqq" title="分享到腾讯微博"></a>
<a href="#" class="bds_renren" data-cmd="renren" title="分享到人人网"></a>
<a href="#" class="bds_weixin" data-cmd="weixin" title="分享到微信"></a>
</div>
<script>window._bd_share_config = { "common": { "bdSnsKey": {}, "bdText": "", "bdMini": "2", "bdPic": "", "bdStyle": "0", "bdSize": "16" }, "share": {} }; with (document) 0[(getElementsByTagName('head')[0] || body).appendChild(createElement('script')).src = 'http://bdimg.share.baidu.com/static/api/js/share.js?v=89860593.js?cdnversion=' + ~(-new Date() / 36e5)];</script>

View File

@ -0,0 +1,3 @@
<div class="social-share"></div>
<%- css(cdn('social-share.js', '1.0.16', 'dist/css/share.min.css')) %>
<%- js(cdn('social-share.js', '1.0.16', 'dist/js/social-share.min.js')) %>

View File

@ -0,0 +1,8 @@
<% if (!has_config('share.install_url')) { %>
<div class="notification is-danger">
You need to set <code>install_url</code> to use ShareThis. Please set it in <code>_config.yml</code>.
</div>
<% } else { %>
<div class="sharethis-inline-share-buttons"></div>
<script type='text/javascript' src='<%= get_config('share.install_url') %>' async='async'></script>
<% } %>

View File

@ -0,0 +1,11 @@
<div class="card">
<div class="card-content">
<nav class="breadcrumb" aria-label="breadcrumbs">
<ul>
<li><a href="<%- url_for('/tags') %>"><%= _p('common.tag', Infinity) %></a></li>
<li class="is-active"><a href="#" aria-current="page"><%= page.tag %></a></li>
</ul>
</nav>
</div>
</div>
<%- partial('index', { page }) %>

View File

@ -0,0 +1,23 @@
<div class="card widget">
<div class="card-content">
<div class="menu">
<h3 class="menu-label">
<%= _p('common.tag', Infinity) %>
</h3>
<ul class="menu-list">
<% _list_tags().forEach(tag => { %>
<li>
<a class="level is-marginless" href="<%= tag.url %>">
<span class="level-start">
<span class="level-item"><%= tag.name %></span>
</span>
<span class="level-end">
<span class="level-item tag"><%= tag.count %></span>
</span>
</a>
</li>
<% }) %>
</ul>
</div>
</div>
</div>

View File

@ -0,0 +1,23 @@
<div class="card widget">
<div class="card-content">
<div class="menu">
<h3 class="menu-label">
<%= _p('common.archive', Infinity) %>
</h3>
<ul class="menu-list">
<% _list_archives().forEach(archive => { %>
<li>
<a class="level is-marginless" href="<%= archive.url %>">
<span class="level-start">
<span class="level-item"><%= archive.name %></span>
</span>
<span class="level-end">
<span class="level-item tag"><%= archive.count %></span>
</span>
</a>
</li>
<% }) %>
</ul>
</div>
</div>
</div>

View File

@ -0,0 +1 @@
<%- partial('categories') %>

View File

@ -0,0 +1,26 @@
<% const links = get_config_from_obj(widget, 'links'); %>
<% if (links !== null) { %>
<div class="card widget">
<div class="card-content">
<div class="menu">
<h3 class="menu-label">
<%= __('widget.links') %>
</h3>
<ul class="menu-list">
<% for (let i in links) { %>
<li>
<a class="level is-mobile" href="<%- links[i] %>" target="_blank">
<span class="level-left">
<span class="level-item"><%= i %></span>
</span>
<span class="level-right">
<span class="level-item tag"><%- links[i] %></span>
</span>
</a>
</li>
<% } %>
</ul>
</div>
</div>
</div>
<% } %>

View File

@ -0,0 +1,90 @@
<% function avatar() {
const avatar = get_config_from_obj(widget, 'avatar');
const gravatar = get_config_from_obj(widget, 'gravatar');
if (gravatar !== null) {
return gravatar(gravatar, 128);
}
if (avatar !== null) {
return url_for(avatar)
}
return url_for('images/avatar.png');
} %>
<div class="card widget">
<div class="card-content">
<nav class="level">
<div class="level-item has-text-centered">
<div>
<img class="image is-128x128 has-mb-6" src="<%= avatar() %>">
<% if (get_config_from_obj(widget, 'author')) { %>
<p class="is-size-4 is-block">
<%= get_config_from_obj(widget, 'author') %>
</p>
<% } %>
<% if (get_config_from_obj(widget, 'author_title')) { %>
<p class="is-size-6 is-block">
<%= get_config_from_obj(widget, 'author_title') %>
</p>
<% } %>
<% if (get_config_from_obj(widget, 'location')) { %>
<p class="is-size-6 is-flex is-flex-center has-text-grey">
<i class="fas fa-map-marker-alt has-mr-7"></i>
<span><%= get_config_from_obj(widget, 'location') %></span>
</p>
<% } %>
</div>
</div>
</nav>
<nav class="level is-mobile">
<div class="level-item has-text-centered is-marginless">
<div>
<p class="heading">
<%= _p('common.post', post_count()) %>
</p>
<p class="title has-text-weight-normal">
<%= post_count() %>
</p>
</div>
</div>
<div class="level-item has-text-centered is-marginless">
<div>
<p class="heading">
<%= _p('common.category', category_count()) %>
</p>
<p class="title has-text-weight-normal">
<%= category_count() %>
</p>
</div>
</div>
<div class="level-item has-text-centered is-marginless">
<div>
<p class="heading">
<%= _p('common.tag', tag_count()) %>
</p>
<p class="title has-text-weight-normal">
<%= tag_count() %>
</p>
</div>
</div>
</nav>
<div class="level">
<a class="level-item button is-link is-rounded" href="<%= url_for(widget.follow_link) %>">
<%= __('widget.follow') %></a>
</div>
<% const socialLinks = get_config_from_obj(widget, 'social_links'); %>
<% if (socialLinks !== null) { %>
<div class="level is-mobile">
<% for (let name in socialLinks) {
let link = socialLinks[name]; %>
<a class="level-item button is-white is-marginless" target="_blank"
title="<%= name %>" href="<%= url_for(typeof(link) === 'string' ? link : link.url) %>">
<% if (typeof(link) === 'string') { %>
<%= name %>
<% } else { %>
<i class="<%= link.icon %>"></i>
<% } %>
</a>
<% } %>
</div>
<% } %>
</div>
</div>

View File

@ -0,0 +1,34 @@
<% if (site.posts.length) { %>
<div class="card widget">
<div class="card-content">
<h3 class="menu-label">
<%= __('widget.recents') %>
</h3>
<% site.posts.sort('date', -1).limit(5).each(post => { %>
<article class="media">
<% if (!has_config('article.thumbnail') || get_config('article.thumbnail') !== false) { %>
<a href="<%- url_for((post.link?post.link:post.path)) %>" class="media-left">
<p class="image is-64x64">
<img class="thumbnail" src="<%= get_thumbnail(post) %>" alt="<%= post.title %>">
</p>
</a>
<% } %>
<div class="media-content">
<div class="content">
<div><time class="has-text-grey is-size-7 is-uppercase" datetime="<%= date_xml(post.date) %>"><%= date(post.date) %></time></div>
<a href="<%- url_for((post.link?post.link:post.path)) %>" class="has-link-black-ter is-size-6"><%= post.title %></a>
<p class="is-size-7 is-uppercase">
<%- list_categories(post.categories, {
show_count: false,
class: 'has-link-grey ',
depth:2,
style: 'none',
separator: ' / '}) %>
</p>
</div>
</div>
</article>
<% }) %>
</div>
</div>
<% } %>

View File

@ -0,0 +1 @@
<%- partial('tags') %>

View File

@ -0,0 +1,10 @@
<% if (site.tags.length) { %>
<div class="card widget">
<div class="card-content">
<h3 class="menu-label">
<%= __('widget.tag_cloud') %>
</h3>
<%- tagcloud() %>
</div>
</div>
<% } %>

View File

@ -0,0 +1,39 @@
<% if (get_config('toc') === true && (post.layout === 'page' || post.layout === 'post')) {
function buildToc(toc) {
let result = '';
if (toc.hasOwnProperty('id') && toc.hasOwnProperty('index') && toc.hasOwnProperty('text')) {
result += `<li>
<a class="is-flex" href="#${toc.id}">
<span class="has-mr-6">${toc.index}</span>
<span>${toc.text}</span>
</a>`;
}
let keys = Object.keys(toc);
keys.indexOf('id') > -1 && keys.splice(keys.indexOf('id'), 1);
keys.indexOf('text') > -1 && keys.splice(keys.indexOf('text'), 1);
keys.indexOf('index') > -1 && keys.splice(keys.indexOf('index'), 1);
keys = keys.map(k => parseInt(k)).sort((a, b) => a - b);
if (keys.length > 0) {
result += '<ul class="menu-list">';
for (let i of keys) {
result += buildToc(toc[i]);
}
result += '</ul>';
}
if (toc.hasOwnProperty('id') && toc.hasOwnProperty('index') && toc.hasOwnProperty('text')) {
result += '</li>';
}
return result;
}
%>
<div class="card widget" id="toc">
<div class="card-content">
<div class="menu">
<h3 class="menu-label">
<%= _p('widget.catalogue', Infinity) %>
</h3>
<%- buildToc(_toc(post.content)) %>
</div>
</div>
</div>
<% } %>

Some files were not shown because too many files have changed in this diff Show More