mirror of https://github.com/halo-dev/halo
📝 更新API文档
parent
19970fc100
commit
f3be2d389c
5
pom.xml
5
pom.xml
|
@ -363,6 +363,11 @@
|
||||||
<fork>true</fork>
|
<fork>true</fork>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-javadoc-plugin</artifactId>
|
||||||
|
<version>3.0.0</version>
|
||||||
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</profile>
|
</profile>
|
||||||
|
|
|
@ -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
|
* @return JsonResult
|
||||||
*/
|
*/
|
||||||
@GetMapping(value = "/year")
|
@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
|
* @return JsonResult
|
||||||
*/
|
*/
|
||||||
@GetMapping(value = "/year/month")
|
@GetMapping(value = "/year/month")
|
||||||
|
|
|
@ -28,6 +28,24 @@ public class ApiCategoryController {
|
||||||
/**
|
/**
|
||||||
* 获取所有分类
|
* 获取所有分类
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
|
* result json:
|
||||||
|
* <pre>
|
||||||
|
* {
|
||||||
|
* "code": 200,
|
||||||
|
* "msg": "OK",
|
||||||
|
* "result": [
|
||||||
|
* {
|
||||||
|
* "cateId": "",
|
||||||
|
* "cateName": "",
|
||||||
|
* "cateUrl": "",
|
||||||
|
* "cateDesc": ""
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
* @return JsonResult
|
* @return JsonResult
|
||||||
*/
|
*/
|
||||||
@GetMapping
|
@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 分类路径
|
* @param cateUrl 分类路径
|
||||||
* @return JsonResult
|
* @return JsonResult
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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
|
* @return JsonResult
|
||||||
*/
|
*/
|
||||||
@GetMapping
|
@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
|
* @param id id
|
||||||
* @return JsonResult
|
* @return JsonResult
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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
|
* @return JsonResult
|
||||||
*/
|
*/
|
||||||
@GetMapping
|
@GetMapping
|
||||||
|
|
|
@ -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
|
* @return JsonResult
|
||||||
*/
|
*/
|
||||||
@GetMapping
|
@GetMapping
|
||||||
|
|
|
@ -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
|
* @return JsonResult
|
||||||
*/
|
*/
|
||||||
@GetMapping
|
@GetMapping
|
||||||
|
@ -45,6 +70,17 @@ public class ApiOptionController {
|
||||||
/**
|
/**
|
||||||
* 获取单个设置项
|
* 获取单个设置项
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
|
* result json:
|
||||||
|
* <pre>
|
||||||
|
* {
|
||||||
|
* "code": 200,
|
||||||
|
* "msg": "OK",
|
||||||
|
* "result": ""
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
* @param optionName 设置选项名称
|
* @param optionName 设置选项名称
|
||||||
* @return JsonResult
|
* @return JsonResult
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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
|
* @param postId postId
|
||||||
* @return JsonResult
|
* @return JsonResult
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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 页码
|
* @param page 页码
|
||||||
* @return JsonResult
|
* @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 文章编号
|
* @param postId 文章编号
|
||||||
* @return JsonResult
|
* @return JsonResult
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -28,6 +28,23 @@ public class ApiTagController {
|
||||||
/**
|
/**
|
||||||
* 获取所有标签
|
* 获取所有标签
|
||||||
*
|
*
|
||||||
|
* <p>
|
||||||
|
* result json:
|
||||||
|
* <pre>
|
||||||
|
* {
|
||||||
|
* "code": 200,
|
||||||
|
* "msg": "OK",
|
||||||
|
* "result": [
|
||||||
|
* {
|
||||||
|
* "tagId": ,
|
||||||
|
* "tagName": "",
|
||||||
|
* "tagUrl": ""
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
* @return JsonResult
|
* @return JsonResult
|
||||||
*/
|
*/
|
||||||
@GetMapping
|
@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
|
* @param tagUrl tagUrl
|
||||||
* @return JsonResult
|
* @return JsonResult
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -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
|
* @return JsonResult
|
||||||
*/
|
*/
|
||||||
@GetMapping
|
@GetMapping
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### :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.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### :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.
|
|
@ -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;
|
|
@ -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;
|
|
@ -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
|
||||||
|
};
|
|
@ -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;
|
||||||
|
});
|
||||||
|
}
|
|
@ -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
|
||||||
|
})
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}, []);
|
||||||
|
});
|
||||||
|
}
|
|
@ -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)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
|
@ -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
|
||||||
|
})
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
|
@ -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;
|
||||||
|
});
|
||||||
|
}
|
|
@ -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;
|
||||||
|
});
|
||||||
|
}
|
|
@ -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;
|
||||||
|
});
|
||||||
|
}
|
|
@ -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;
|
||||||
|
});
|
||||||
|
}
|
|
@ -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');
|
||||||
|
});
|
||||||
|
}
|
|
@ -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;
|
||||||
|
});
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
};
|
|
@ -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
|
||||||
|
}
|
|
@ -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')
|
||||||
|
};
|
|
@ -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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -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'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
|
@ -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
|
||||||
|
}
|
||||||
|
};
|
|
@ -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'
|
||||||
|
}
|
||||||
|
};
|
|
@ -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'
|
||||||
|
}
|
||||||
|
};
|
|
@ -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'
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
const logger = require('hexo-log')();
|
||||||
|
|
||||||
|
logger.info(`=======================================
|
||||||
|
██╗ ██████╗ █████╗ ██████╗ ██╗ ██╗███████╗
|
||||||
|
██║██╔════╝██╔══██╗██╔══██╗██║ ██║██╔════╝
|
||||||
|
██║██║ ███████║██████╔╝██║ ██║███████╗
|
||||||
|
██║██║ ██╔══██║██╔══██╗██║ ██║╚════██║
|
||||||
|
██║╚██████╗██║ ██║██║ ██║╚██████╔╝███████║
|
||||||
|
╚═╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝
|
||||||
|
=============================================`);
|
|
@ -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)'
|
|
@ -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)'
|
|
@ -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)'
|
|
@ -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'
|
|
@ -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)'
|
|
@ -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)'
|
|
@ -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)'
|
|
@ -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: '(Без названия)'
|
|
@ -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)'
|
|
@ -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: '(无标题)'
|
|
@ -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)'
|
|
@ -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') %>
|
||||||
|
<% } %>
|
|
@ -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>
|
|
@ -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 }) %>
|
|
@ -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>
|
||||||
|
<% } %>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||||
|
<% } %>
|
|
@ -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>
|
||||||
|
<% } %>
|
||||||
|
|
|
@ -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>
|
||||||
|
<% } %>
|
|
@ -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>
|
||||||
|
<% } %>
|
|
@ -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: ' / '
|
||||||
|
}) %>
|
||||||
|
</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>
|
||||||
|
<% } %>
|
|
@ -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">
|
||||||
|
© <%= date(new Date(), 'YYYY') %> <%= get_config('author') || get_config('title') %>
|
||||||
|
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>
|
|
@ -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] }) %>
|
||||||
|
<% } %>
|
||||||
|
<% } %>
|
|
@ -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>
|
|
@ -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">…</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>
|
|
@ -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') %>
|
|
@ -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>
|
||||||
|
<% } %>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<% page.posts.each(function(post){ %>
|
||||||
|
<%- partial('common/article', { post, index: true }) %>
|
||||||
|
<% }); %>
|
||||||
|
<% if (page.total > 1) { %>
|
||||||
|
<%- partial('common/paginator') %>
|
||||||
|
<% } %>
|
|
@ -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>
|
|
@ -0,0 +1 @@
|
||||||
|
<%- partial('common/article', {post: page, index: false}) %>
|
|
@ -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>
|
||||||
|
<% } %>
|
||||||
|
<% } %>
|
|
@ -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') %>
|
||||||
|
<% } %>
|
||||||
|
<% } %>
|
|
@ -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>
|
||||||
|
<% } %>
|
|
@ -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>
|
||||||
|
<% } %>
|
||||||
|
<% } %>
|
|
@ -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>
|
||||||
|
<% } %>
|
|
@ -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>
|
||||||
|
<% } %>
|
|
@ -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>
|
||||||
|
<% } %>
|
|
@ -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">×</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>
|
||||||
|
<% } %>
|
||||||
|
<% } %>
|
|
@ -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>
|
||||||
|
<% } %>
|
|
@ -0,0 +1 @@
|
||||||
|
<%- partial('common/article', {post: page, index: false}) %>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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') %>
|
|
@ -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>
|
||||||
|
<% } %>
|
|
@ -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 -->
|
|
@ -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>
|
|
@ -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')) %>
|
|
@ -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>
|
||||||
|
<% } %>
|
|
@ -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 }) %>
|
|
@ -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>
|
|
@ -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>
|
|
@ -0,0 +1 @@
|
||||||
|
<%- partial('categories') %>
|
|
@ -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>
|
||||||
|
<% } %>
|
|
@ -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>
|
|
@ -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>
|
||||||
|
<% } %>
|
|
@ -0,0 +1 @@
|
||||||
|
<%- partial('tags') %>
|
|
@ -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>
|
||||||
|
<% } %>
|
|
@ -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
Loading…
Reference in New Issue