Add solitude

This commit is contained in:
IF-chan
2024-07-05 21:01:19 +08:00
parent 26d71c4fbc
commit 71758b9fce
289 changed files with 23277 additions and 0 deletions
+142
View File
@@ -0,0 +1,142 @@
/**
* Solitude
* Merge CDN
*/
'use strict'
const { version } = require('../../package.json')
const path = require('path')
hexo.extend.filter.register('before_generate', () => {
const themeConfig = hexo.theme.config
const { CDN } = themeConfig
const thirdPartySrc = hexo.render.renderSync({ path: path.join(hexo.theme_dir, '/plugins.yml'), engine: 'yaml' })
const internalSrc = {
main: {
name: 'hexo-theme-solitude',
file: 'js/main.js',
version
},
utils: {
name: 'hexo-theme-solitude',
file: 'js/utils.js',
version
},
local_search: {
name: 'hexo-theme-solitude',
file: 'js/search/local.js',
version
},
algolia_js: {
name: 'hexo-theme-solitude',
file: 'js/search/algolia.js',
version
},
cover_local: {
name: 'hexo-theme-solitude',
file: 'js/covercolor/local.js',
version
},
cover_api: {
name: 'hexo-theme-solitude',
file: 'js/covercolor/api.js',
version
},
music_js: {
name: 'hexo-theme-solitude',
file: 'js/music.js',
version
},
right_menu_js: {
name: 'hexo-theme-solitude',
file: 'js/right_menu.js',
version
},
translate_js:{
name: 'hexo-theme-solitude',
file: 'js/tw_cn.js',
version
},
efu_ai: {
name: 'hexo-theme-solitude',
file: 'js/third_party/efu_ai.min.js',
version
},
commentBarrage: {
name: 'hexo-theme-solitude',
file: 'js/third_party/barrage.min.js',
version
},
waterfall: {
name: 'hexo-theme-solitude',
file: 'js/third_party/waterfall.min.js',
version
},
universe_js: {
name: 'hexo-theme-solitude',
file: 'js/third_party/universe.min.js',
version
},
envelope_js: {
name: 'hexo-theme-solitude',
file: 'js/third_party/envelope.min.js',
version
}
}
const minFile = file => {
return file.replace(/(?<!\.min)\.(js|css)$/g, ext => '.min' + ext)
}
const createCDNLink = (data, type, cond = '') => {
Object.keys(data).forEach(key => {
let { name, version, file, other_name } = data[key]
const cdnjs_name = other_name || name
const cdnjs_file = file.replace(/^[lib|dist]*\/|browser\//g, '')
const min_cdnjs_file = minFile(cdnjs_file)
if (cond === 'internal') file = `source/${file}`
const min_file = minFile(file)
const verType = CDN.version ? (type === 'local' ? `?v=${version}` : `@${version}`) : ''
const value = {
version,
name,
file,
cdnjs_file,
min_file,
min_cdnjs_file,
cdnjs_name
}
const cdnSource = {
local: cond === 'internal' ? `${cdnjs_file + verType}` : `/pluginsSrc/${name}/${file + verType}`,
jsdelivr: `https://cdn.jsdelivr.net/npm/${name}${verType}/${min_file}`,
unpkg: `https://unpkg.com/${name}${verType}/${file}`,
cdnjs: `https://cdnjs.cloudflare.com/ajax/libs/${cdnjs_name}/${version}/${min_cdnjs_file}`,
custom: (CDN.custom_format || '').replace(/\$\{(.+?)}/g, (match, $1) => value[$1]),
}
data[key] = cdnSource[type]
})
if (cond === 'internal') data.main_css = 'css/index.css' + (CDN.version ? `?v=${version}` : '')
return data
}
// delete null value
const deleteNullValue = obj => {
if (!obj) return
for (const i in obj) {
obj[i] === null && delete obj[i]
}
return obj
}
themeConfig.cdn = Object.assign(
createCDNLink(internalSrc, CDN.internal, 'internal'),
createCDNLink(thirdPartySrc, CDN.third_party),
deleteNullValue(CDN.option)
)
})
+19
View File
@@ -0,0 +1,19 @@
hexo.extend.filter.register('before_generate', () => {
const hexoVer = hexo.version.replace(/(^.*\..*)\..*/, '$1');
const nodeVer = process.version.replace(/^v/, '');
const [majorVer] = nodeVer.split('.');
const logger = hexo.log;
const config = hexo.config;
if (hexoVer < 7.0) {
logger.error('请把 Hexo 升级到 V7.0.0 或更高的版本!');
logger.error('Please upgrade your Hexo to V7.0.0 or higher!');
process.exit(-1);
}
if (Number(majorVer) < 14) {
logger.error('请将 Node.js 升级到 v14.0.0 或更高的版本!');
logger.error('Please upgrade Node.js to v14.0.0 or later!');
process.exit(-1);
}
});
@@ -0,0 +1,390 @@
hexo.extend.filter.register('before_generate', () => {
const defaultConfig = {
site: {
name: {
class: 'text',
custom: 'Solitude'
},
siteIcon: '/img/pwa/favicon.ico',
icon: 'icon'
},
nav: {
group: null,
menu: null,
right: {
random: false,
console: false,
custom: [],
}
},
hometop: {
enable: false,
banner: {
title: 'Solitude',
url: 'A simple theme for Hexo',
icon: null,
},
group: null,
recommendList: {
sup: 'Recommend',
title: 'Solitude Docs',
url: 'https://solitude.js.org/',
img: '/img/default.avif',
color: 'none',
}
},
aside: {
home: {
noSticky: 'about',
Sticky: 'allInfo'
},
post: {
noSticky: 'about',
Sticky: 'allInfo'
},
page: {
noSticky: 'about',
Sticky: 'allInfo'
},
card: {
style: 0,
author: {
img: '/img/logo.png',
sticker: '/img/happy-sticker.avif',
},
url: '/about/',
background: null,
content1: 'Solitude',
content2: 'A simple theme for Hexo',
sayhello: {
morning: 'Good Morning',
noon: 'Good Noon',
afternoon: 'Good Afternoon',
night: 'Good Night',
goodnight: 'Good Night',
},
sayhello2: ['Welcome to Solitude', 'A simple theme for Hexo', 'Enjoy your time', 'Have a nice day', 'Good luck'],
information: null,
},
flip: {
favicon: '',
face: '',
backface: '',
backcolor: 'var(--efu-blue)'
},
newest_comment: {
enable: false,
storage: .5,
limit: 5
},
toc: {
post: true,
page: false,
vague: true,
},
tags: {
enable: true,
limit: 20,
highlight: false,
list: [],
},
archive: {
enable: true,
type: 'month'
},
siteinfo: {
postcount: true,
wordcount: false,
pv: true,
uv: true,
updatetime: true,
runtimeenable: true,
runtime: "2023-04-20 00:00:00",
},
},
index_post_list: {
direction: 'column',
column: 2,
content: false,
length: 500,
cover: 'both'
},
page: {
error: true,
tags: true,
categories: true,
default: {
cover: ['/img/default.avif'],
}
},
post: {
default: {
cover: ['/img/default.avif'],
locate: 'China, Changsha',
copyright: {
enable: true,
license: 'CC BY-NC-SA 4.0',
licenurl: 'https://creativecommons.org/licenses/by-nc-sa/4.0/',
}
},
meta: {
date: false,
updated: false,
locate: false,
wordcount: false,
readtime: false,
pv: false,
uv: false,
comment: false,
},
award: {
enable: false,
appreciators: '/',
title: '感谢您的赞赏。',
desc: '因为有你们的支持,我才体会到写文章的价值。',
list: [],
},
rss: null,
covercolor: {
enable: false,
mode: 'local',
api: 'https://api.qjqq.cn/api/Imgcolor?img=',
time: 43200000,
},
footer: {
enable: true,
desc: "来自 Efu 的文章",
button: {
enable: true,
name: '返回首页',
url: '/'
}
}
},
theme_color: {
dark: "#ffc848",
dark_op: "#f2b94b23",
dark_op_deep: "#f2b94bdd",
dark_none: "#f2b94b00",
light: "#425AEF",
light_op: "#4259ef23",
light_op_deep: "#4259efdd",
light_none: "#4259ef01"
},
display_mode: {
type: 'auto',
universe: false
},
related_post: {
enable: false,
limit: 2,
date_type: 'created'
},
footer: {
information: {
author: false,
left: null,
right: null,
},
group: null,
randomlink: false,
privacy: null,
license: null,
links: [{
name: 'Solitude',
url: 'https://github.com/everfu/hexo-theme-solitude',
}]
},
errorpage: {
img: 'https://7.isyangs.cn/34/65f2e65eae32a-34.png',
text: '404 Not Found',
recommendList: true
},
says: {
enable: false,
home_mini: false,
style: 1,
strip: 30
},
recent_comments: {
enable: false,
limit: 50,
console: false,
page: '/recentcomments/'
},
envelope: {
enable: false,
line: 10,
speed: 20,
hover: true,
loop: true,
page: '/message/'
},
meting_api: "https://meting.qjqq.cn/?server=:server&type=:type&id=:id&auth=:auth&r=:r",
music: {
enable: false,
id: '8407304077',
server: 'netease',
type: 'playlist',
volume: 0.8,
mutex: true,
},
capsule: {
enable: false,
id: '8407304077',
server: 'netease',
type: 'playlist',
},
keyboard: {
enable: false,
list: []
},
lazyload: {
enable: false,
field: 'site',
placeholder: '/img/loading.avif',
errorimg: '/img/error_load.avif'
},
loading: {
fullpage: false,
pace: true,
},
highlight: {
enable: true,
limit: 200,
copy: true,
expand: true,
theme: 'default',
color: 'default',
},
lightbox: false,
fancybox: false,
mediumZoom: false,
mermaid: false,
OpenGraph: {
enable: false,
options: null
},
wordcount: false,
busuanzi: false,
search: {
enable: false,
type: 'local',
tags: [],
algolia: null,
local: {
preload: false,
CDN: null,
}
},
rightside: {
enable: false
},
copy: {
enable: true,
copyright: {
enable: false,
limit: 50
}
},
post_ai: {
enable: false,
modelName: 'GPT 3',
key: 'your key',
talk: 'I am a AI.',
randomPost: false,
tips: 'AI is not perfect, please use it with caution.'
},
katex: {
enable: false,
per_page: false,
copytex: false,
},
comment: {
use: null,
commentBarrage: false,
lazyload: false,
count: false,
avatar: 'https://cravatar.cn',
},
twikoo: {
envId: 'your envId',
region: null,
style: true,
accessToken: null,
option: null,
},
waline: {
envId: 'your envId',
pageview: true,
option: null,
},
valine: {
appId: 'your appId',
appKey: 'your appKey',
serverURLs: 'your serverURLs',
avatar: 'monsterid',
visitor: false,
style: true,
option: null,
},
artalk: {
server: 'your server',
site: 'your site-name',
option: null,
},
verify_site: [],
css_prefix: false,
font: {
'font-size': '16px',
'code-font-size': '16px',
'font-family': 'PingFang SC, Hiragino Sans GB,Microsoft YaHei',
'code-font-family': 'monospace, monospace',
},
extends: {
head: [],
body: [],
},
pwa: {
enable: false,
manifest: '/manifest.json',
theme_color: "#006a73",
mask_icon: '/img/pwa/favicon.ico',
apple_touch_icon: '/img/pwa/favicon.ico',
bookmark_icon: '/img/pwa/favicon.ico',
favicon_32_32: '/img/pwa/favicon_32.ico',
favicon_16_16: '/img/pwa/favicon_16.ico'
},
google_adsense: {
enable: false,
auto_ads: false,
aside_card: false,
post_card: false,
post_content: false,
enable_page_level_ads: false,
js: 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js',
client: '',
slot: '',
},
right_menu: {
enable: false,
commentBarrage: false,
translate: {
enable: false,
defaultEncoding: 2,
translateDelay: 0,
},
custom_list: []
},
CDN: {
internal: 'local',
third_party: 'cdnjs',
version: true,
custom_format: 'https://cdn.staticfile.net/${cdnjs_name}/${version}/${min_cdnjs_file}',
option: {
solitude_css: 'https://cdn2.codesign.qq.com/icons/7pOrz0WXB5ZWJPX/latest/iconfont.css',
}
}
}
hexo.theme.config = Object.assign(defaultConfig, hexo.theme.config)
}, 1)
+33
View File
@@ -0,0 +1,33 @@
hexo.extend.generator.register('404', function (locals) {
if(!this.theme.config.page.error)return
return {
path: '404.html',
layout: ['404'],
data: {
type: '404',
comment: false
}
}
})
hexo.extend.generator.register('tags', function(locals) {
if(!this.theme.config.page.tags)return
return {
path: 'tags/index.html',
layout: ['page'],
data: {
type: 'tags',
comment: false
}
};
});
hexo.extend.generator.register('categories', function(locals) {
if(!this.theme.config.page.categories)return
return {
path: 'categories/index.html',
layout: ['page'],
data: {
type: 'categories',
comment: false
}
};
});
+14
View File
@@ -0,0 +1,14 @@
hexo.on('ready', () => {
const {version} = require('../../package.json')
hexo.log.info(`
===================================================================
##### #### # ##### ###### # # ###### ######
# # # # # # # # # # #
##### # # # # # # # # # ######
# # # # # # # # # # #
###### #### ##### ##### # ### ###### ######
${version}
===================================================================
GitHub: https://github.com/everfu/hexo-theme-solitude
`)
})
@@ -0,0 +1,28 @@
'use strict';
hexo.extend.filter.register('before_post_render', () => {
const data = hexo.locals.get('data');
const logger = hexo.log;
const theme = hexo.theme.config;
if (theme.hometop.enable && !theme.hometop.banner.icon) {
logger.error('\n 启用banner的情况下,必须提供 icon 图片!\n 请在主题配置文件中设置 hometop.banner.icon 选项。');
logger.error('\n If banner is enabled, icon image must be supplied! \n Please set the hometop.banner.icon option in the theme configuration file.');
process.exit(-1);
}
if (theme.says.enable && !data.essay) {
logger.error('\n 启用说说的情况下,必须提供 essay 数据文件!\n 请新建 essay.yaml。');
logger.error('\n If says is enabled, essay data must be supplied! \n Please create essay.yaml.');
process.exit(-1);
}
if (theme.footer.randomlink && !data.links){
logger.error('\n 启用随机链接的情况下,必须提供 links 数据!\n 请新建 links.yaml。');
logger.error('\n If randomlink is enabled, links data must be supplied! \n Please create links.yaml.');
process.exit(-1);
}
if (theme.lightbox && !theme.fancybox && !theme.mediumZoom){
logger.error('\n 启用 lightbox 的情况下,必须提供 fancybox 或 mediumZoom 选项!\n 请在主题配置文件中设置 fancybox 或 mediumZoom 选项。');
logger.error('\n If lightbox is enabled, fancybox or mediumZoom option must be supplied! \n Please set the fancybox or mediumZoom option in the theme configuration file.');
process.exit(-1);
}
}
);
+13
View File
@@ -0,0 +1,13 @@
/**
* 将 use 分拆并将首字大写
*/
hexo.extend.filter.register('before_generate', () => {
const themeConfig = hexo.theme.config
let { use } = themeConfig.comment
if (!use) return
if (typeof use === 'string') {
use = use.split(',').slice(0,2)
}
themeConfig.comment.use = use.map(item => item.toLowerCase().replace(/\b[a-z]/g, s => s.toUpperCase()))
})
+31
View File
@@ -0,0 +1,31 @@
'use strict';
hexo.extend.filter.register('after_post_render', function (data) {
data.title = data.title || 'Untitled';
const {config} = hexo.theme;
if (data.layout === 'post') {
let {copyright, locate, cover} = hexo.theme.config.post.default
cover = cover !== null ? cover : ['/img/default.avif']
data.locate = data.locate || locate
data.cc = data.cc || copyright
data.cover = data.cover || cover[getRandomInt(0, cover?.length)]
data.excerpt = data.description || data.excerpt
data.toc = !!(config.aside.toc.post && data.toc !== false)
data.ai = data.ai !== false
}
if (data.layout === 'page') {
let {cover} = hexo.theme.config.page.default
cover = cover !== null ? cover : ['/img/default.avif']
data.cover = data.cover || cover[getRandomInt(0, cover?.length)]
data.excerpt = data.title
data.toc = !!(config.aside.toc.page && data.toc !== false && data.aside);
}
data.comment = !!(config.comment.use && data.comment !== false);
return data;
});
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min)) + min;
}
@@ -0,0 +1,20 @@
'use strict'
const urlFor = require('hexo-util').url_for.bind(hexo)
const lazyload = (content, img) => {
return content.replace(/(<img(?!.*?class[\t]*=[\t]*['"].*?nolazyload.*?['"]).*? src=)/gi, `$1 "${img}" data-lazy-src=`)
}
hexo.extend.filter.register('after_render:html', function (data) {
const { enable, placeholder ,field } = hexo.theme.config.lazyload
if (!enable || field !== 'site') return;
return lazyload(data, urlFor(placeholder))
})
hexo.extend.filter.register('after_post_render', data => {
const { enable, placeholder, field } = hexo.theme.config.lazyload
if (!enable || field !== 'post') return
data.content = lazyload(data.content, urlFor(placeholder))
return data
})
@@ -0,0 +1,13 @@
'use strict'
hexo.extend.filter.register('before_post_render', data => {
const imgTestReg = /\.(png|jpe?g|gif|svg|webp)(\?.*)?$/i
let { cover: coverVal } = data
// Add path to top_img and cover if post_asset_folder is enabled
if (hexo.config.post_asset_folder) {
if (coverVal && coverVal.indexOf('/') === -1 && imgTestReg.test(coverVal)) data.cover = `${data.path}${coverVal}`
}
return data
})
@@ -0,0 +1,5 @@
hexo.extend.filter.register('after_render:html', function (data) {
const posts = hexo.locals.get('posts').filter(post => post.random !== false).map(post => post.path);
data += `<script>const posts=${JSON.stringify(posts)};function toRandomPost(){ pjax.loadUrl('/'+posts[Math.floor(Math.random()*posts.length)]); }</script>`;
return data;
});
@@ -0,0 +1,14 @@
const moment = require('moment')
hexo.extend.helper.register('getArchiveLength', function (type) {
if (type !== 'year' && type !== 'month') {
type = 'year';
}
const posts = this.site.posts.sort('-date').data
let archive = {}
for (const post of posts) {
const postdate = type === 'year' ? moment(post.date).format('YYYY') : moment(post.date).format('YYYY/MM');
archive[postdate] = (archive[postdate] || 0) + 1;
}
return archive;
});
@@ -0,0 +1,86 @@
/**
* Solitude
* inject is to head
*/
'use strict';
hexo.extend.helper.register('inject_head_js', function () {
const createJS = () => `
const saveToLocal = {
set: function setWithExpiry(key, value, ttl) {
if (ttl === 0)
return
const now = new Date()
const expiryDay = ttl * 86400000
const item = {
value: value,
expiry: now.getTime() + expiryDay
}
localStorage.setItem(key, JSON.stringify(item))
},
get: function getWithExpiry(key) {
const itemStr = localStorage.getItem(key)
if (!itemStr) {
return undefined
}
const item = JSON.parse(itemStr)
const now = new Date()
if (now.getTime() > item.expiry) {
localStorage.removeItem(key)
return undefined
}
return item.value
}
};
window.utils = {
saveToLocal: saveToLocal,
getCSS: (url, id = false) => new Promise((resolve, reject) => {
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = url
if (id) link.id = id
link.onerror = reject
link.onload = link.onreadystatechange = function() {
const loadState = this.readyState
if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
link.onload = link.onreadystatechange = null
resolve()
}
document.head.appendChild(link)
}),
getScript: (url, attr = {}) => new Promise((resolve, reject) => {
const script = document.createElement('script')
script.src = url
script.async = true
script.onerror = reject
script.onload = script.onreadystatechange = function() {
const loadState = this.readyState
if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
script.onload = script.onreadystatechange = null
resolve()
}
Object.keys(attr).forEach(key => {
script.setAttribute(key, attr[key])
})
document.head.appendChild(script)
}),
addGlobalFn: (key, fn, name = false, parent = window) => {
const globalFn = parent.globalFn || {}
const keyObj = globalFn[key] || {}
if (name && keyObj[name]) return
name = name || Object.keys(keyObj).length
keyObj[name] = fn
globalFn[key] = keyObj
parent.globalFn = globalFn
},
}
`
return `<script>(()=>{${createJS()}})()</script>`
})
+17
View File
@@ -0,0 +1,17 @@
"use strict";
const { stripHTML, escapeHTML, prettyUrls } = require("hexo-util");
hexo.extend.helper.register('page_description', function () {
const { config, page } = this
let description = page.description || page.content || page.title || config.description
if (description) {
description = escapeHTML(stripHTML(description).substring(0, 150)
.trim()
).replace(/\n/g, ' ').replace(/\s+/g, ' ');
return description
}
})
hexo.extend.helper.register("urlNoIndex", function (url = null) {
return prettyUrls(url || this.url, { trailing_index: false, trailing_html: true });
});
@@ -0,0 +1,85 @@
/**
* related post
* from solitude
*/
'use strict'
hexo.extend.helper.register('related_posts', function (currentPost, allPosts) {
const relatedPosts = []
const config = hexo.theme.config
const limitNum = config.related_post.limit || 6
const dateType = config.related_post.date_type || 'created'
const headlineLang = this._p('star')
currentPost.tags.forEach(function (tag) {
allPosts.forEach(function (post) {
if (isTagRelated(tag.name, post.tags) && currentPost.path !== post.path) {
const relatedPost = {
title: post.title,
path: post.path,
cover: post.cover,
cover_type: post.cover_type,
weight: 1,
updated: post.updated,
created: post.date
}
const index = findItem(relatedPosts, 'path', post.path)
if (index !== -1) {
relatedPosts[index].weight += 1
} else {
relatedPosts.push(relatedPost)
}
}
})
})
if (relatedPosts.length === 0) {
return ''
}
relatedPosts.sort(compare('weight', dateType))
let result = '<div class="relatedPosts">'
result += `<div class="headline"><i class="solitude st-star-smile-fill"></i><span>${headlineLang}</span><div class="relatedPosts-link"><a onclick="event.preventDefault(); toRandomPost();" href="javascript:void(0);" rel="external nofollow" data-pjax-state="">${this._p('random')}</a></div></div>`
result += '<div class="relatedPosts-list">'
for (let i = 0; i < Math.min(relatedPosts.length, limitNum); i++) {
const cover = relatedPosts[i].cover || 'var(--default-bg-color)'
const title = this.escape_html(relatedPosts[i].title)
result += `<div><a href="${this.url_for(relatedPosts[i].path)}" title="${title}">`
result += `<img class="cover" src="${this.url_for(cover)}" alt="cover">`
result += `<div class="content is-center"><div class="title">${title}</div></div>`
result += '</a></div>'
}
result += '</div></div>'
return result
})
function isTagRelated(tagName, TBDtags) {
return TBDtags.some(function (tag) {
return tagName === tag.name
})
}
function findItem(arrayToSearch, attr, val) {
return arrayToSearch.findIndex(function (item) {
return item[attr] === val
})
}
function compare(attr, dateType) {
return function (a, b) {
const val1 = a[attr]
const val2 = b[attr]
if (val1 === val2) {
if (dateType === 'created') {
return b.created - a.created
} else if (dateType === 'updated') {
return b.updated - a.updated
}
}
return val2 - val1
}
}
+24
View File
@@ -0,0 +1,24 @@
hexo.extend.filter.register('stylus:renderer', function (style) {
const {config, theme} = this;
const data = hexo.locals.get('data');
style.define('$about', data && data.about ? Object.keys(data.about) : false);
style.define('$link', !!(data && data.links) || !!(data && data.tlink))
style.define('$equipment', !!(data && data.equipment))
// highlight
const {syntax_highlighter: syntaxHighlighter, highlight, prismjs} = hexo.config
let {enable: highlightEnable, line_number: highlightLineNumber} = highlight
let {enable: prismjsEnable, line_number: prismjsLineNumber} = prismjs
// for hexo > 7.0
if (syntaxHighlighter) {
highlightEnable = syntaxHighlighter === 'highlight.js'
prismjsEnable = syntaxHighlighter === 'prismjs'
}
style.define('$highlight_enable', highlightEnable)
style.define('$highlight_line_number', highlightLineNumber)
style.define('$prismjs_enable', prismjsEnable)
style.define('$prismjs_line_number', prismjsLineNumber)
});
+35
View File
@@ -0,0 +1,35 @@
"use strict";
function postBtns(args, content) {
return `<div class="btns ${args.join(" ")}">
${content}
</div>`;
}
function postCell(args, content) {
args = args.join(" ").split(",");
let text = args[0] || "";
let url = args[1] || "";
text = text.trim();
url = url.trim();
if (url.length > 0) {
url = "href='" + url + "'";
}
let icon = "";
let img = "https://npm.elemecdn.com/hexo-butterfly-tag-plugins-plus/lib/assets/default.svg";
if (args.length > 2) {
if (args[2].indexOf(" solitude") > -1) {
icon = args[2].trim();
} else {
img = args[2].trim();
}
}
if (icon.length > 0) {
return `<a class="button no-text-decoration" ${url} title='${text}'><i class='${icon}'></i>${text}</a>`;
} else {
return `<a class="button no-text-decoration" ${url} title='${text}'><img src='${img}'>${text}</a>`;
}
}
hexo.extend.tag.register("btns", postBtns, { ends: true });
hexo.extend.tag.register("cell", postCell);
+21
View File
@@ -0,0 +1,21 @@
"use strict";
const urlFor = require("hexo-util").url_for.bind(hexo);
function btn(args) {
args = args.join(" ").split(",");
let url = args[0] || "";
let text = args[1] || "";
let icon = args[2] || "";
let option = args[3] || "";
url = url.trim();
text = text.trim();
icon = icon.trim();
option = option.trim();
return `<button class="btn-sco ${option}" onclick="${url.startsWith("http") ? `window.open('${url}')` : `pjax.loadUrl('${urlFor(url)}')`}"
title="${text}">${icon.length ? `<i class="solitude ${icon}"></i>` : ""}${text.length ? `<span>${text}</span>` : ""}</button>`;
}
hexo.extend.tag.register("btn", btn, { ends: false });
+7
View File
@@ -0,0 +1,7 @@
'use strict'
function bilibili(args) {
return `<iframe class="bvideo" width="100%" height="600" src="//player.bilibili.com/player.html?autoplay=0&bvid=${args}" border="0" frameBorder="no" framespacing="0" allowFullScreen="true"></iframe>`
}
hexo.extend.tag.register('bvideo', bilibili, {ends: false})
+51
View File
@@ -0,0 +1,51 @@
'use strict';
function postCheckbox(args) {
args = args.join(' ').split(',')
var cls = ''
var text = ''
var checked = false
if (args.length > 1) {
cls = (args[0] || '').trim()
if (cls.length > 0) {
cls = ' ' + cls
}
if (cls.indexOf('checked') > -1) {
checked = true
}
text = (args[1] || '').trim()
} else if (args.length > 0) {
text = (args[0] || '').trim()
}
if (text.length > 0) {
return `<div class='checkbox${cls}'><input type="checkbox" ${ checked ? 'checked="checked"' : '' }/>
${hexo.render.renderSync({text: text, engine: 'markdown'}).split('\n').join('')}
</div>`
}
}
function postRadio(args) {
args = args.join(' ').split(',')
var cls = ''
var text = ''
var checked = false
if (args.length > 1) {
cls = (args[0] || '').trim()
if (cls.length > 0) {
cls = ' ' + cls
}
if (cls.indexOf('checked') > -1) {
checked = true
}
text = (args[1] || '').trim()
} else if (args.length > 0) {
text = (args[0] || '').trim()
}
if (text.length > 0) {
return `<div class='checkbox${cls}'><input type="radio" ${ checked ? 'checked="checked"' : '' }/>
${hexo.render.renderSync({text: text, engine: 'markdown'}).split('\n').join('')}
</div>`
}
}
hexo.extend.tag.register('checkbox', postCheckbox);
hexo.extend.tag.register('radio', postRadio);
+13
View File
@@ -0,0 +1,13 @@
'use strict'
function postTabs(args, content) {
const title = args[0] ? args[0] : ""
const open = args[1] ? args[1] : ""
return `<details ${open}><summary>${title}</summary><div class="content">${hexo.render.renderSync({
text: content,
engine: 'markdown'
})}</div></details>`
}
hexo.extend.tag.register('fold', postTabs, {ends: true})
+37
View File
@@ -0,0 +1,37 @@
'use strict';
const urlFor = require('hexo-util').url_for.bind(hexo)
const galleryBox = args => {
const [name, descr, url, img] = args
const imgUrl = urlFor(img)
const urlLink = urlFor(url)
return `<figure class="gallery-group">
<img class="gallery-group-img no-lightbox" src='${imgUrl}' alt="Group Image Gallery">
<figcaption>
<div class="gallery-group-name">${name}</div>
<p>${descr}</p>
<a href='${urlLink}'></a>
</figcaption>
</figure>
`
}
const gallery = (args, content) => {
let html = ""
const regex = /!\[(.*?)\]\(([^\s]*)\s*(?:["'](.*?)["']?)?\s*\)/g
let m
while ((m = regex.exec(content)) !== null) {
if (m.index === regex.lastIndex) {
regex.lastIndex++
}
html += `<div class="gallery-item"><img class="nolazyload" src=${m[2]} alt="${m[1]}" /></div>`
}
return `<div class="gallery-container waterfall">
${html}
</div>`
}
hexo.extend.tag.register('gallery', gallery, {ends: true})
hexo.extend.tag.register('galleryGroup', galleryBox)
+75
View File
@@ -0,0 +1,75 @@
'use strict';
hexo.extend.tag.register('image', function(args) {
args = args.join(' ').split(',');
let url = args[0].trim();
let alt = '';
let bg = '';
let style = '';
if (args.length > 1) {
for (let i = 1; i < args.length; i++) {
let tmp = args[i].trim();
if (tmp.includes('alt=')) {
alt = tmp.substring(4, tmp.length);
} else if (tmp.includes('width=')) {
style += 'width:' + tmp.substring(6, tmp.length) + ';';
} else if (tmp.includes('height=')) {
style += 'height:' + tmp.substring(7, tmp.length) + ';';
} else if (tmp.includes('bg=')) {
bg = tmp.substring(3, tmp.length);
}
}
}
function img(url, alt, style) {
let img = '';
img += '<img class="img" src="' + url + '"';
if (alt.length > 0) {
img += ' alt="' + alt + '"';
}
if (style.length > 0) {
img += ' style="' + style + '"';
}
img += '/>';
return img;
}
let ret = '';
ret += '<div class="img-wrap">';
ret += '<div class="img-bg"';
if (bg.length > 0) {
ret += ' style="background:' + bg + '"';
}
ret += '>';
ret += img(url, alt, style);
ret += '</div>';
if (alt.length > 0) {
ret += '<span class="image-caption">' + alt + '</span>';
}
ret += '</div>';
return ret;
});
hexo.extend.tag.register('inlineimage', function(args) {
args = args.join(' ').split(', ');
let url = args[0].trim();
let ret = '';
ret += '<img no-lazy class="inline" src="' + url + '"';
let style = '';
if (args.length > 1) {
for (let i = 1; i < args.length; i++) {
let tmp = args[i].trim();
if (tmp.includes('height=')) {
style += 'height:' + tmp.substring(7, tmp.length) + ';';
}
}
}
if (style.length > 0) {
ret += ' style="' + style + '"';
} else {
ret += ' style="height:1.5em"';
}
ret += '/>';
return ret;
});
@@ -0,0 +1,12 @@
'use strict'
const urlFor = require('hexo-util').url_for.bind(hexo)
function inlineImg (args) {
const img = args[0]
const height = args[1] ? `style="height:${args[1]}"` : ''
return `<img class="inline-img" src="${urlFor(img)}" ${height}/>`
}
hexo.extend.tag.register('inlineimage', inlineImg, { ends: false })
+10
View File
@@ -0,0 +1,10 @@
'use strict'
function addLabel (args, content) {
const text = args[0]
const className = args[1] || 'default'
return `<label class="hl-label bg-${className}">${text}</label> `
}
hexo.extend.tag.register('label', addLabel, { ends: false })
+50
View File
@@ -0,0 +1,50 @@
const urlFor = require("hexo-util").url_for.bind(hexo);
function link(args) {
const themeConfig = hexo.theme.config;
args = args.join(" ").split(",");
let title = args[0];
let sitename = args[1];
let link = args[2];
let imgUrl = args[3] || "";
let favicon = themeConfig.site.siteIcon;
let insideStation = false;
link = link.trim();
imgUrl = imgUrl.trim();
favicon = favicon.trim();
try {
new URL(link);
insideStation = false;
} catch (err) {
insideStation = true;
}
if ((imgUrl === "") && (insideStation === false)) {
let domain = new URL(link).hostname
if (domain) {
imgUrl_online = "https://api.iowen.cn/favicon/" + domain + ".png";
}
}
return `<a class="tag-link" target="_blank" href="${urlFor(link)}">
<div class="tag-link-tips">${insideStation ? "站内链接" : "引用站外链接"
}</div>
<div class="tag-link-bottom">
<div class="tag-link-left" style="${insideStation
? `background-image: url(${imgUrl ? imgUrl : favicon})`
: `background-image: url(${imgUrl ? imgUrl : imgUrl_online})`
}">
<i class="solitude st-link-m-line" style="${`(imgUrl) || (imgUrl_online)` ? "display: none" : ""
}"></i>
</div>
<div class="tag-link-right">
<div class="tag-link-title">${title}</div>
<div class="tag-link-sitename">${sitename}</div>
</div>
<i class="solitude st-arrow-right-bold"></i>
</div>
</a>`;
}
hexo.extend.tag.register("link", link, { ends: false });
+29
View File
@@ -0,0 +1,29 @@
'use strict';
function postAudio(args) {
let src = args[0].trim()
return `<div class="audio"><audio controls preload><source src='${src}' type='audio/mp3'>Your browser does not support the audio tag.</audio></div>`;
}
function postVideo(args) {
let src = args[0].trim()
return `<div class="video"><video controls preload><source src='${src}' type='video/mp4'>Your browser does not support the video tag.</video></div>`;
}
function postVideos(args, content) {
args = args.join(' ').split(',')
var cls = args[0]
if (cls.length > 0) {
cls = ' ' + cls
}
var col = Number(args[1]) || 0;
if (col > 0) {
return `<div class="videos${cls}" col='${col}'>${content}</div>`
} else {
return `<div class="videos${cls}">${content}</div>`
}
}
hexo.extend.tag.register('audio', postAudio);
hexo.extend.tag.register('video', postVideo);
hexo.extend.tag.register('videos', postVideos, {ends: true});
+9
View File
@@ -0,0 +1,9 @@
'use strict';
const {escapeHTML} = require('hexo-util')
function postMermaid(args, content) {
return `<div class="mermaid">${escapeHTML(content)}</div>`;
}
hexo.extend.tag.register('mermaid', postMermaid, {ends: true});
+13
View File
@@ -0,0 +1,13 @@
/**
* note.js
* transplant from hexo-theme-next
* modified by @efu
*/
hexo.extend.tag.register('note', function (types, content) {
let type = ""
for (let i = 0; i < types.length; i++) {
type += " " + types[i]
}
return `<div class="note ${type}">${hexo.render.renderSync({text: content, engine: 'markdown'})}</div>`
}, {ends: true});
+17
View File
@@ -0,0 +1,17 @@
'use strict';
function postP(args) {
args = args.join(' ').split(',')
let p0 = args[0].trim()
let p1 = args[1].trim()
return `<p class='p ${p0}'>${p1}</p>`;
}
function postSpan(args) {
args = args.join(' ').split(',')
let p0 = args[0].trim()
let p1 = args[1].trim()
return `<span class='p ${p0}'>${p1}</span>`;
}
hexo.extend.tag.register('p', postP);
hexo.extend.tag.register('span', postSpan);
+61
View File
@@ -0,0 +1,61 @@
/**
* Tabs
* from solitude
*/
'use strict'
function postTabs (args, content) {
const tabBlock = /<!--\s*tab (.*?)\s*-->\n([\w\W\s\S]*?)<!--\s*endtab\s*-->/g
args = args.join(' ').split(',')
const tabName = args[0]
const tabActive = Number(args[1]) || 0
const matches = []
let match
let tabId = 0
let tabNav = ''
let tabContent = ''
!tabName && hexo.log.warn('Tabs block must have unique name!')
while ((match = tabBlock.exec(content)) !== null) {
matches.push(match[1])
matches.push(match[2])
}
for (let i = 0; i < matches.length; i += 2) {
const tabParameters = matches[i].split('@')
let postContent = matches[i + 1]
let tabCaption = tabParameters[0] || ''
let tabIcon = tabParameters[1] || ''
let tabHref = ''
postContent = hexo.render.renderSync({ text: postContent, engine: 'markdown' }).trim()
tabId += 1
tabHref = (tabName + ' ' + tabId).toLowerCase().split(' ').join('-');
((tabCaption.length === 0) && (tabIcon.length === 0)) && (tabCaption = tabName + ' ' + tabId)
const isOnlyicon = tabIcon.length > 0 && tabCaption.length === 0 ? ' style="text-align: center;"' : ''
const icon = tabIcon.trim()
tabIcon.length > 0 && (tabIcon = `<i ${isOnlyicon} class="tab solitude ${icon}"></i>`)
const toTop = '<button type="button" class="tab-to-top" aria-label="scroll to top"><i class="solitude st-arrow-up-line"></i></button>'
const isActive = (tabActive > 0 && tabActive === tabId) || (tabActive === 0 && tabId === 1) ? ' active' : ''
tabNav += `<li class="tab${isActive}"><button type="button" data-href="#${tabHref}">${tabIcon + tabCaption.trim()}</button></li>`
tabContent += `<div class="tab-item-content${isActive}" id="${tabHref}">${postContent + toTop}</div>`
}
tabNav = `<ul class="nav-tabs">${tabNav}</ul>`
tabContent = `<div class="tab-contents">${tabContent}</div>`
return `<div class="tabs" id="${tabName.toLowerCase().split(' ').join('-')}">${tabNav + tabContent}</div>`
}
hexo.extend.tag.register('tabs', postTabs, { ends: true })
hexo.extend.tag.register('subtabs', postTabs, { ends: true })
hexo.extend.tag.register('subsubtabs', postTabs, { ends: true })
+22
View File
@@ -0,0 +1,22 @@
"use strict"
function postTimeline(args, content) {
if (args.length > 0) {
return `<div class="timeline"><h1>${args}</h1>${content}</div>`;
} else {
return `<div class="timeline">${content}</div>`;
}
}
function postTimenode(args, content) {
args = args.join(" ").split(",");
var time = args[0];
return `<div class="timenode"><div class="meta"><p>${time}</p></div><div class="body">${hexo.render
.renderSync({ text: content, engine: "markdown" })
.split("\n")
.join("")}</div></div>`;
}
hexo.extend.tag.register("timeline", postTimeline, { ends: true });
hexo.extend.tag.register("timenode", postTimenode, { ends: true });