Merge pull request #494 from chengzhongxue/main

适配Artalk评论,适配Waline评论
This commit is contained in:
困困鱼 2023-09-17 03:01:44 +08:00 committed by GitHub
commit 7fd847e615
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 866 additions and 110 deletions

View File

@ -198,7 +198,7 @@ spec:
label: 宠物挂件
help: 需要瞬间丶顶部 Banner显示
value:
climbEnable: true
climbEnable: false
climbImg: https://imgapi.yyds.pink/random?sort=animals
children:
- $formkit: radio
@ -2180,11 +2180,6 @@ spec:
value: true
- label: 禁用
value: false
- $formkit: number
name: maxBarrage
value: 20
label: 弹幕数页数
placeholder: 请输入弹幕数页数
- $formkit: radio
label: 信笺
name: enable_envelope_comment
@ -2272,12 +2267,15 @@ spec:
id: use
key: use
value: commentWidget
help: Twikoo评论需要填写下方配置
options:
- label: 默认
value: commentWidget
- label: Twikoo
value: Twikoo
- label: Artalk
value: Artalk
- label: Waline
value: Waline
- $formkit: radio
if: $get(use).value == 'Twikoo'
name: twikooEnable
@ -2285,6 +2283,7 @@ spec:
key: twikooEnable
label: Twikoo
value: true
help: Twikoo评论需要填写下方配置
options:
- label: 打开
value: true
@ -2293,47 +2292,167 @@ spec:
- $formkit: group
if: $get(twikooEnable).value
name: twikoos
label: Twikoo评论配置
label: Twikoo评论配置 (文档https://twikoo.js.org/)
value:
maxBarrage:
barrageTime:
envId:
accessToken:
mailMd5:
js: ""
children:
- $formkit: number
name: maxBarrage
value: 1
label: 同时最多显示弹幕数
placeholder: 请输入显示弹幕数
help: 右下角同时最多显示的弹幕数量
- $formkit: number
name: barrageTime
value: 4000
label: 弹幕显示间隔时间ms
placeholder: 请输入间隔时间ms
help: 右下角弹出热评弹幕的间隔时间
- $formkit: url
name: envId
key: envId
label: Twikoo评论 - 环境id/后端URL
placeholder: 请输入环境id或URL
help: 详情请查看文档
help: 详情请查看文档 (后缀带 / )
validation: required
- $formkit: text
name: accessToken
key: accessToken
label: Twikoo评论 - 管理员令牌
placeholder: 请输入token
help: 在浏览器F12的控制台中获取用于显示弹幕详情请查看文档
- $formkit: text
name: mailMd5
label: Twikoo评论 - 博主邮箱MD5值
placeholder: 请输入md5值
help: 用于验证博主评论可前往cmd5.com加密自己使用的邮箱选择32位小写
validation: required
- $formkit: text
name: js
key: js
label: Twikoo评论 - js
placeholder: 请输入js文件地址
help: 主题的twikoo版本是最新版本(如果你使用的不是最新版本请自己引入js文件)
- $formkit: radio
if: $get(use).value == 'Artalk'
name: artalkEnable
id: artalkEnable
key: artalkEnable
label: Artalk
value: true
help: Artalk评论需要填写下方配置
options:
- label: 打开
value: true
- label: 关闭
value: false
- $formkit: group
if: $get(artalkEnable).value
name: artalks
label: Artalk评论配置 (文档https://artalk.js.org/)
value:
siteName: ""
server: ""
js: ""
css: ""
children:
- $formkit: url
name: server
key: artalkServer
label: Artalk评论 - 后端URL
placeholder: 请输入后端URL
help: 详情请查看文档 (后缀带 / )
validation: required
- $formkit: text
name: siteName
key: siteName
label: 标题
placeholder: 请输入标题
validation: required
- $formkit: text
name: artalkJs
key: artalkJs
label: Artalk评论 - js
placeholder: 请输入js文件地址
help: 主题的Artalk版本是最新版本(如果你使用的不是最新版本请自己引入js文件)
- $formkit: text
name: artalkCss
key: artalkCss
label: Artalk评论 - css
placeholder: 请输入css文件地址
help: 主题的Artalk版本是最新版本(如果你使用的不是最新版本请自己引入css文件)
- $formkit: radio
if: $get(use).value == 'Waline'
name: walineEnable
id: walineEnable
key: walineEnable
label: Waline
value: true
help: Waline评论需要填写下方配置
options:
- label: 打开
value: true
- label: 关闭
value: false
- $formkit: group
if: $get(walineEnable).value
name: walines
label: Waline评论配置 (文档https://waline.js.org/)
value:
serverURL: ""
walinesJs: ""
walinesCss: ""
children:
- $formkit: url
name: serverURL
key: serverURL
label: Waline评论 - 后端URL
placeholder: 请输入后端URL
help: 详情请查看文档 (后缀不带 / )
validation: required
- $formkit: text
name: walinesJs
key: walinesJs
label: Waline评论 - js
placeholder: 请输入js文件地址
help: 主题的walines版本是最新版本(如果你使用的不是最新版本请自己引入js文件)
- $formkit: text
name: walinesCss
key: walinesCss
label: Waline评论 - css
placeholder: 请输入css文件地址
help: 主题的walines版本是最新版本(如果你使用的不是最新版本请自己引入css文件)
- $formkit: group
name: commentBarrageConfig
if: ($get(use).value == 'Artalk' && $get(twikooEnable).value) ||
($get(use).value == 'Twikoo' && $get(twikooEnable).value) ||
($get(use).value == 'Waline' && $get(twikooEnable).value)
label: 留言弹幕配置
value:
commentBarrageEnable: true
maxBarrage: 1
barrageTime: 4000
mailMd5:
children:
- $formkit: radio
name: commentBarrageEnable
id: commentBarrageEnable
key: commentBarrageEnable
options:
- label: 打开
value: true
- label: 关闭
value: false
- $formkit: number
if: $get(commentBarrageEnable).value
name: maxBarrage
key: maxBarrage
label: 同时最多显示弹幕数
placeholder: 请输入显示弹幕数
help: 右下角同时最多显示的弹幕数量
validation: required
- $formkit: number
if: $get(commentBarrageEnable).value
name: barrageTime
key: barrageTime
label: 弹幕显示间隔时间ms
placeholder: 请输入间隔时间ms
help: 右下角弹出热评弹幕的间隔时间
validation: required
- $formkit: text
if: $get(commentBarrageEnable).value && $get(use).value != 'Waline'
name: mailMd5
key: mailMd5
label: 评论 - 博主邮箱MD5值
placeholder: 请输入md5值
help: 用于验证博主评论可前往cmd5.com加密自己使用的邮箱选择32位小写
validation: required
- group: style
label: 样式
@ -2407,7 +2526,7 @@ spec:
key: enable
label: 启用代码块
help: 如果安装代码块插件,可关闭此功能
value: false
value: true
options:
- label: 启用
value: true

View File

@ -257,7 +257,7 @@ let halo = {
document.head.appendChild(a)
},
danmu: (url,token,maxBarrage)=>{
danmu: ()=>{
const e = new EasyDanmakuMin({
el: "#danmu",
line: 10,
@ -273,32 +273,84 @@ let halo = {
function a(e) {
return e = (e = (e = (e = (e = e.replace(/<\/*br>|[\s\uFEFF\xA0]+/g, "")).replace(/<img.*?>/g, "[图片]")).replace(/<a.*?>.*?<\/a>/g, "[链接]")).replace(/<pre.*?>.*?<\/pre>/g, "[代码块]")).replace(/<.*?>/g, "")
}
fetch(url, {
method: "POST",
body: JSON.stringify({
event: "GET_RECENT_COMMENTS",
accessToken: token,
includeReply: !1,
pageSize: maxBarrage
}),
headers: {
"Content-Type": "application/json"
if(GLOBAL_CONFIG.source.comments.use == 'Twikoo'){
fetch(GLOBAL_CONFIG.source.twikoo.twikooUrl, {
method: "POST",
body: JSON.stringify({
event: "GET_RECENT_COMMENTS",
accessToken: GLOBAL_CONFIG.source.twikoo.accessToken,
includeReply: !1,
pageSize: 5
}),
headers: {
"Content-Type": "application/json"
}
}).then((e=>e.json())).then((({data: t})=>{
t.forEach((e=>{
null == e.avatar && (e.avatar = "https://cravatar.cn/avatar/d615d5793929e8c7d70eab5f00f7f5f1?d=mp"),
n.push({
avatar: e.avatar,
content: e.nick + "" + a(e.comment),
href: e.url + '#' + e.id
})
}
)),
e.batchSend(n, !0),
saveToLocal.set("danmu", n, .02)
}
))
}
if(GLOBAL_CONFIG.source.comments.use == 'Artalk'){
const statheaderList = {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Origin': window.location.origin
},
body: new URLSearchParams({
'site_name': GLOBAL_CONFIG.source.artalk.siteName,
'limit': '100',
'type':'latest_comments'
})
}
}).then((e=>e.json())).then((({data: t})=>{
t.forEach((e=>{
null == e.avatar && (e.avatar = "https://cravatar.cn/avatar/d615d5793929e8c7d70eab5f00f7f5f1?d=mp"),
fetch(GLOBAL_CONFIG.source.artalk.artalkUrl + 'api/stat', statheaderList)
.then((e=>e.json())).then((({data: t})=>{
t.forEach((e=>{
n.push({
avatar: e.avatar,
content: e.nick + "" + a(e.comment),
href: e.url + '#' + e.id
avatar: 'https://cravatar.cn/avatar/' + e.email_encrypted + '?d=mp&s=240',
content: e.nick + "" + a(e.content_marked),
href: e.page_url + '#atk-comment-' + e.id
})
}
)),
e.batchSend(n, !0),
saveToLocal.set("danmu", n, .02)
}
)),
e.batchSend(n, !0),
saveToLocal.set("danmu", n, .02)
}
))
}
if(GLOBAL_CONFIG.source.comments.use == 'Waline'){
const loadWaline = () => {
Waline.RecentComments({
serverURL: GLOBAL_CONFIG.source.waline.serverURL,
count: 50
}).then(({ comments }) => {
const walineArray = comments.map(e => {
return {
'content': e.nick + "" + a(e.comment),
'avatar': e.avatar,
'href': e.url + '#' + e.objectId,
}
})
e.batchSend(walineArray, !0),
saveToLocal.set("danmu", walineArray, .02)
})
}
))
if (typeof Waline === 'object') loadWaline()
else getScript(GLOBAL_CONFIG.source.waline.js).then(loadWaline)
}
}
document.getElementById("danmuBtn").innerHTML = "<button class=\"hideBtn\" onclick=\"document.getElementById('danmu').classList.remove('hidedanmu')\">显示弹幕</button> <button class=\"hideBtn\" onclick=\"document.getElementById('danmu').classList.add('hidedanmu')\">隐藏弹幕</button>"
},

View File

@ -293,7 +293,8 @@ function rightMenuCommentText(txt) {
if (GLOBAL_CONFIG.rightMenuEnable) {
rm.hideRightMenu();
}
var input = document.getElementsByClassName('el-textarea__inner')[0];
var input = GLOBAL_CONFIG.source.comments.use=='Twikoo' ? document.getElementsByClassName('el-textarea__inner')[0] :
GLOBAL_CONFIG.source.comments.use=='Artalk' ? document.getElementsByClassName('atk-textarea')[0] : document.getElementsByClassName('appearance-none')[0];
let evt = document.createEvent('HTMLEvents');
evt.initEvent('input', true, true);
let inputValue = replaceAll(txt, '\n', '\n> ')

View File

@ -1,19 +1,24 @@
if(GLOBAL_CONFIG.htmlType!='comments') {
if(GLOBAL_CONFIG.htmlType!='comments' && document.querySelector('#post-comment')) {
var commentBarrageConfig = {
//同时最多显示弹幕数
maxBarrage: GLOBAL_CONFIG.source.twikoo.maxBarrage,
maxBarrage: GLOBAL_CONFIG.source.comments.maxBarrage,
//弹幕显示间隔时间ms
barrageTime: GLOBAL_CONFIG.source.twikoo.barrageTime,
barrageTime: GLOBAL_CONFIG.source.comments.barrageTime,
//twikoo部署地址腾讯云的为环境ID
twikooUrl: GLOBAL_CONFIG.source.twikoo.twikooUrl,
artalkUrl: GLOBAL_CONFIG.source.artalk.artalkUrl,
walineUrl: GLOBAL_CONFIG.source.waline.serverURL,
//token获取见上方
accessToken: GLOBAL_CONFIG.source.twikoo.accessToken,
mailMd5: GLOBAL_CONFIG.source.twikoo.mailMd5,
mailMd5: GLOBAL_CONFIG.source.comments.mailMd5,
pageUrl: window.location.pathname,
barrageTimer: [],
barrageList: [],
siteName: GLOBAL_CONFIG.source.artalk.siteName,
barrageIndex: 0,
dom: document.querySelector('.comment-barrage'),
use: GLOBAL_CONFIG.source.comments.use
}
var commentInterval = null;
@ -30,23 +35,57 @@ if(GLOBAL_CONFIG.htmlType!='comments') {
function initCommentBarrage() {
//console.log("开始创建热评")
var data = JSON.stringify({
"event": "COMMENT_GET",
"commentBarrageConfig.accessToken": commentBarrageConfig.accessToken,
"url": commentBarrageConfig.pageUrl
});
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
commentBarrageConfig.barrageList = commentLinkFilter(JSON.parse(this.responseText).data);
commentBarrageConfig.dom.innerHTML = '';
}
});
xhr.open("POST", commentBarrageConfig.twikooUrl);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(data);
if(commentBarrageConfig.use=='Twikoo'){
var data = JSON.stringify({
"event": "COMMENT_GET",
"commentBarrageConfig.accessToken": commentBarrageConfig.accessToken,
"url": commentBarrageConfig.pageUrl
});
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
commentBarrageConfig.barrageList = commentLinkFilter(JSON.parse(this.responseText).data);
commentBarrageConfig.dom.innerHTML = '';
}
});
xhr.open("POST", commentBarrageConfig.twikooUrl);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(data);
}
if(commentBarrageConfig.use=='Artalk'){
var data ={
"site_name": commentBarrageConfig.siteName,
"page_key": commentBarrageConfig.pageUrl,
"limit": 100,
"offset": 0
};
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
commentBarrageConfig.barrageList = commentLinkFilter(JSON.parse(this.responseText).data.comments);
commentBarrageConfig.dom.innerHTML = '';
}
});
const usp = new URLSearchParams(data)
const query = usp.toString()
xhr.open("POST", commentBarrageConfig.artalkUrl+'api/get');
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send(query);
}
if(commentBarrageConfig.use=='Waline'){
fetch( commentBarrageConfig.walineUrl+`/comment?path=${commentBarrageConfig.pageUrl}&pageSize=100&page=1&lang=zh-CN&sortBy=insertedAt_desc`)
.then((e=>e.json())).then((({data: t})=>{
if(t.length>0){
commentBarrageConfig.barrageList = commentLinkFilter(t);
commentBarrageConfig.dom.innerHTML = '';
}
}
))
}
clearInterval(commentInterval);
commentInterval = null;
@ -64,13 +103,33 @@ if(GLOBAL_CONFIG.htmlType!='comments') {
}
function commentLinkFilter(data) {
data.sort((a, b) => {
return a.created - b.created;
})
let newData = [];
data.forEach(item => {
newData.push(...getCommentReplies(item));
});
if(commentBarrageConfig.use=='Twikoo'){
data.sort((a, b) => {
return a.created - b.created;
})
data.forEach(item => {
newData.push(...getCommentReplies(item));
});
}
if(commentBarrageConfig.use=='Artalk'){
data.sort((a, b) => {
const aCreated = Date.parse(a.date);
const bCreated = Date.parse(b.date);
return aCreated - bCreated;
})
data.forEach(item => {
newData.push(item);
});
}
if(commentBarrageConfig.use=='Waline'){
data.sort((a, b) => {
return a.time - b.time;
})
data.forEach(item => {
newData.push(...getCommentWalineReplies(item));
});
}
return newData;
}
@ -85,9 +144,35 @@ if(GLOBAL_CONFIG.htmlType!='comments') {
return [];
}
}
function getCommentWalineReplies(item) {
if (item.children) {
let children = [item];
item.children.forEach(item => {
children.push(...getCommentReplies(item));
})
return children;
} else {
return [];
}
}
function popCommentBarrage(data) {
let isTwikoo = commentBarrageConfig.use=='Twikoo'
let isArtalk = commentBarrageConfig.use=='Artalk';
let isWaline = commentBarrageConfig.use=='Waline';
let nick = data.nick;
let avatar = isTwikoo ? `https://cravatar.cn/avatar/${data.mailMd5}` :
isArtalk ? `https://cravatar.cn/avatar/${data.email_encrypted}?d=mp&s=240` :
isWaline ? data.avatar :'https://cravatar.cn/avatar/';
let barrageBlogger = isTwikoo ? data.mailMd5 === commentBarrageConfig.mailMd5 :
isArtalk ? data.email_encrypted === commentBarrageConfig.mailMd5 :
isWaline ? data.type === 'administrator' :false;
let id = isTwikoo ? data.id :
isArtalk ? 'atk-comment-'+data.id :
isWaline ? data.objectId : 'post-comment';
let comment = isTwikoo ? data.comment :
isArtalk ? data.content :
isWaline ? data.comment : '';
let barrage = document.createElement('div');
let width = commentBarrageConfig.dom.clientWidth;
let height = commentBarrageConfig.dom.clientHeight;
@ -95,14 +180,14 @@ if(GLOBAL_CONFIG.htmlType!='comments') {
barrage.innerHTML = `
<div class="barrageHead">
<a class="barrageTitle
${data.mailMd5 === commentBarrageConfig.mailMd5 ? "barrageBloggerTitle" : ""}" href="javascript:heo.scrollTo('post-comment')">
${data.mailMd5 === commentBarrageConfig.mailMd5 ? "博主" : "热评"}
${barrageBlogger ? "barrageBloggerTitle" : ""}" href="javascript:heo.scrollTo('post-comment')">
${barrageBlogger ? "博主" : "热评"}
</a>
<div class="barrageNick">${data.nick}</div>
<img class="barrageAvatar" src="https://cravatar.cn/avatar/${data.mailMd5}"/>
<div class="barrageNick">${nick}</div>
<img class="barrageAvatar" src="${avatar}"/>
<a class="comment-barrage-close" href="javascript:heo.switchCommentBarrage()"><i class="haofont hao-icon-xmark"></i></a>
</div>
<a class="barrageContent" href="javascript:heo.scrollTo('${data.id}');">${data.comment}</a>
<a class="barrageContent" href="javascript:heo.scrollTo('${id}');">${comment}</a>
`
// 获取hao标签内的所有pre元素
let haoPres = barrage.querySelectorAll(".barrageContent pre");

View File

@ -2495,4 +2495,24 @@ p.p.gray,span.p.gray {
border-bottom: 0;
padding: 0;
width: fit-content;
}
#waline-wrap {
--waline-font-size: 1.1em;
--waline-theme-color: var(--heo-main);
--waline-active-color: #ff7242;
}
#waline-wrap .vuser {
-webkit-transition: all 0.5s;
-moz-transition: all 0.5s;
-o-transition: all 0.5s;
-ms-transition: all 0.5s;
transition: all 0.5s;
}
#waline-wrap .vuser:hover {
-webkit-transform: rotate(360deg);
-moz-transform: rotate(360deg);
-o-transform: rotate(360deg);
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}

View File

@ -151,8 +151,9 @@
</div>
<th:block th:if="${theme.config.comments.twikooEnable &&
#strings.equals(theme.config.comments.use, 'Twikoo') &&
<th:block th:if="${(theme.config.comments.twikooEnable || theme.config.comments.artalkEnable
|| theme.config.comments.walineEnable) &&
#strings.contains('Twikoo,Artalk,Waline',theme.config.comments.use) &&
theme.config.envelope_comment.enable_danmu }">
<div id="danmuBtn">

View File

@ -1,9 +1,9 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<th:block th:fragment="comment(group, kind, name, allowComment)" th:if="${allowComment}">
<th:block
th:if="${theme.config.comments.twikooEnable && #strings.equals(theme.config.comments.use, 'Twikoo') && not #strings.isEmpty(theme.config.comments.twikoos.envId)}">
<div th:replace="~{modules/comment/twikoo :: twikoo}"></div>
<th:block th:if="${theme.config.comments.use!='commentWidget'}"
th:with="use = ${theme.config.comments.use}">
<th:block th:replace="~{'modules/comment/'+ ${use}}"/>
</th:block>
<!-- 已知问题 PJAX 下comment 首次请求会出错。当前的临时解决办法是使用 js 重试 -->
<div id="post-comment"

View File

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<!-- 导航栏菜单栏 -->
<div id="post-comment" th:fragment="artalk"
th:if="${theme.config.comments.artalkEnable && #strings.equals(theme.config.comments.use, 'Artalk') && not #strings.isEmpty(theme.config.comments.artalks.server)}">
<div class="comment-head">
<div class="comment-headline"><i class="haofont hao-icon-comments"></i> <span>评论</span></div>
<div class="comment-privacy"><a href="/privacy">隐私政策</a></div>
<div class="comment-tips" id="comment-tips">
<span>你无需删除空行,直接评论以获取最佳展示效果</span>
</div>
</div>
<div id="artalk-wrap"></div>
</div>
</html>

View File

@ -0,0 +1,222 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<th:block th:fragment="artalk" th:if="${theme.config.comments.artalkEnable
&& #strings.equals(theme.config.comments.use, 'Artalk')
&& not #strings.isEmpty(theme.config.comments.artalks.server)}">
<div class="js-pjax">
<script>
if (document.querySelector('#post-comment')) {
(() => {
const initArtalk = () => {
window.artalkItem = new Artalk(Object.assign({
el: '#artalk-wrap',
server: "[(${theme.config.comments.artalks.server})]",
site: GLOBAL_CONFIG.source.artalk.siteName,
pageKey: location.pathname,
darkMode: document.documentElement.getAttribute('data-theme') === 'dark',
countEl: '#twikoo-count'
}, null))
if (GLOBAL_CONFIG.lightbox === 'null') return
window.artalkItem.use(ctx => {
ctx.on('list-loaded', () => {
ctx.getCommentList().forEach(comment => {
const $content = comment.getRender().$content
btf.loadLightbox($content.querySelectorAll('img:not([atk-emoticon])'))
})
})
})
}
const loadArtalk = async () => {
if (typeof window.artalkItem === 'object') initArtalk()
else {
await getCSS(GLOBAL_CONFIG.source.artalk.css)
await getScript(GLOBAL_CONFIG.source.artalk.js)
initArtalk()
}
}
document.getElementById('darkmode') && document.getElementById('darkmode').addEventListener('click', () => {
setDarkMode()
})
document.getElementById('menu-darkmode') && document.getElementById('menu-darkmode').addEventListener('click', () => {
setDarkMode()
})
document.getElementById('darkmode_switchbutton') && document.getElementById('darkmode_switchbutton').addEventListener('click', () => {
setDarkMode()
})
function setDarkMode() {
if (typeof window.artalkItem !== 'object') return
let isDark = document.documentElement.getAttribute('data-theme') === 'dark'
window.artalkItem.setDarkMode(!isDark)
}
if ('Artalk' === 'Artalk' || !false) {
if (false) btf.loadComment(document.getElementById('artalk-wrap'), loadArtalk)
else loadArtalk()
} else {
function loadOtherComment() {
loadArtalk()
}
}
})()
}
</script>
<input type="hidden" name="page-type" id="page-type" value="album">
</div>
<!-- 最近评论 -->
<script>
window.addEventListener('load', () => {
const changeContent = (content) => {
if (content === '') return content
content = content.replace(/<img.*?src="(.*?)"?[^\>]+>/ig, '[图片]') // replace image link
content = content.replace(/<a[^>]+?href=["']?([^"']+)["']?[^>]*>([^<]+)<\/a>/gi, '[链接]') // replace url
content = content.replace(/<pre><code>.*?<\/pre>/gi, '[代码]') // replace code
content = content.replace(/<[^>]+>/g, "") // remove html tag
if (content.length > 150) {
content = content.substring(0, 150) + '...'
}
return content
}
const generateHtml = array => {
let result = ''
if (array.length) {
for (let i = 0; i < array.length; i++) {
if (i == 6) {
break;
}
result += '<div class=\'aside-list-item\'>'
if (true) {
let name = 'src'
if ([[${ isLazyload }]]) {
name = 'data-lazy-src'
}
result += `<a href='${array[i].url}' class='thumbnail'><img ${name}='${array[i].avatar}' alt='${array[i].nick}'><div class='name'><span>${array[i].nick}</span></div></a>`
}
result += `<div class='content'>
<a class='comment' href='${array[i].url}' title='${array[i].content}'>${array[i].content}</a>
<time datetime="${array[i].date}">${btf.diffDate(array[i].date, true)}</time></div>
</div>`
}
} else {
result += '没有评论'
}
let $dom = document.querySelector('#card-newest-comments .aside-list')
$dom.innerHTML = result
window.lazyLoadInstance && window.lazyLoadInstance.update()
window.pjax && window.pjax.refresh($dom)
}
const necommHtml = array => {
let result = ''
const pagesize = [[${ theme.config.sidebar.newcommentnumber }]];
const defaultpagesize = 5;
const finalpagesize = pagesize <= 0 ? defaultpagesize : pagesize;
if (array.length) {
for (let i = 0; i < array.length; i++) {
if (i == finalpagesize) {
break;
}
result += '<div class="aside-list-item">'
if (true) {
let name = 'src'
if ([[${ isLazyload }]]) {
name = 'data-lazy-src'
}
result += `
<a class="thumbnail" href="${array[i].url}">
<img alt="dasda" ${name}="${array[i].avatar}">
</a>
`
}
result += `
<div class="content">
<a class="comment" style="display: -webkit-box;-webkit-line-clamp: 2;-webkit-box-orient: vertical;overflow: hidden;"
href="${array[i].url}" title="${array[i].content}">
${array[i].content}
</a>
<div class="name">
<span>${array[i].nick} / </span>
<time datetime="${array[i].date}">${btf.diffDate(array[i].date, true)}</time>
</div>
</div>
</div>
`
}
} else {
result += '没有评论'
}
let $dom = document.querySelector('#newcomm')
$dom.innerHTML = result
window.lazyLoadInstance && window.lazyLoadInstance.update()
window.pjax && window.pjax.refresh($dom)
}
const getComment = () => {
const statheaderList = {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Origin': window.location.origin
},
body: new URLSearchParams({
'site_name': GLOBAL_CONFIG.source.artalk.siteName,
'limit': '20',
'type':'latest_comments'
})
}
fetch("[(${theme.config.comments.artalks.server})]" + 'api/stat', statheaderList)
.then(response => response.json())
.then(d => {
const artalk = d.data.map(function (e) {
return {
'avatar': 'https://cravatar.cn/avatar/' + e.email_encrypted + '?d=mp&s=240',
'content': changeContent(e.content_marked),
'nick': e.nick,
'url': e.page_url + '#atk-comment-' + e.id,
'date': e.date,
}
})
saveToLocal.set('artalk-newest-comments', JSON.stringify(artalk), 10 / (60 * 24))
generateHtml(artalk)
document.querySelector('#newcomm') && necommHtml(artalk)
}).catch(e => {
const $dom = document.querySelector('#card-newest-comments .aside-list')
$dom.innerHTML = "无法获取评论,请确认相关配置是否正确"
})
}
const newestCommentInit = () => {
if (document.querySelector('#card-newest-comments .aside-list')) {
const data = saveToLocal.get('artalk-newest-comments')
if (data) {
generateHtml(JSON.parse(data))
document.querySelector('#newcomm') && necommHtml(JSON.parse(data))
} else {
getComment()
}
}
}
newestCommentInit()
document.addEventListener('pjax:complete', newestCommentInit)
})
</script>
</th:block>
</html>

View File

@ -1,12 +1,11 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<th:block th:fragment="cardNewestComments">
<th:block th:fragment="twikoo" th:if="${theme.config.comments.twikooEnable &&
#strings.equals(theme.config.comments.use, 'Twikoo') &&
not #strings.isEmpty(theme.config.comments.twikoos.envId)}">
<!-- 最近评论 -->
<script
th:if="${theme.config.comments.twikooEnable
&& #strings.equals(theme.config.comments.use, 'Twikoo')
&& not #strings.isEmpty(theme.config.comments.twikoos.envId)}">
<script>
window.addEventListener('load', () => {
const changeContent = (content) => {
if (content === '') return content

View File

@ -0,0 +1,188 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<th:block th:fragment="waline" th:if="${theme.config.comments.walineEnable
&& #strings.equals(theme.config.comments.use, 'Waline')
&& not #strings.isEmpty(theme.config.comments.walines.serverURL)}">
<div class="js-pjax">
<script>
if (document.querySelector('#post-comment')) {
(() => {
function initWaline() {
const waline = Waline.init(Object.assign({
el: '#waline-wrap',
serverURL: GLOBAL_CONFIG.source.waline.serverURL,
pageview: false,
dark: 'html[data-theme="dark"]',
path: window.location.pathname,
comment: false,
}, null))
}
const loadWaline = async () => {
if (typeof Waline === 'object') initWaline()
else {
await getCSS(GLOBAL_CONFIG.source.waline.css)
await getScript(GLOBAL_CONFIG.source.waline.js)
initWaline()
}
}
if ('Waline' === 'Waline' || !false) {
if (false) btf.loadComment(document.getElementById('waline-wrap'), loadWaline)
else setTimeout(loadWaline, 0)
} else {
window.loadOtherComment = loadWaline
}
})()
}
</script>
</div>
<script>
window.addEventListener('load', () => {
const changeContent = (content) => {
if (content === '') return content
content = content.replace(/<img.*?src="(.*?)"?[^\>]+>/ig, '[图片]') // replace image link
content = content.replace(/<a[^>]+?href=["']?([^"']+)["']?[^>]*>([^<]+)<\/a>/gi, '[链接]') // replace url
content = content.replace(/<pre><code>.*?<\/pre>/gi, '[代码]') // replace code
content = content.replace(/<[^>]+>/g, "") // remove html tag
if (content.length > 150) {
content = content.substring(0, 150) + '...'
}
return content
}
const generateHtml = array => {
let result = ''
if (array.length) {
for (let i = 0; i < array.length; i++) {
if (i == 6) {
break;
}
result += '<div class=\'aside-list-item\'>'
if (true) {
let name = 'src'
if ([[${ isLazyload }]]) {
name = 'data-lazy-src'
}
result += `<a href='${array[i].url}' class='thumbnail'><img ${name}='${array[i].avatar}' alt='${array[i].nick}'><div class='name'><span>${array[i].nick}</span></div></a>`
}
result += `<div class='content'>
<a class='comment' href='${array[i].url}' title='${array[i].content}'>${array[i].content}</a>
<time datetime="${array[i].date}">${btf.diffDate(array[i].date, true)}</time></div>
</div>`
}
} else {
result += '没有评论'
}
let $dom = document.querySelector('#card-newest-comments .aside-list')
$dom.innerHTML = result
window.lazyLoadInstance && window.lazyLoadInstance.update()
window.pjax && window.pjax.refresh($dom)
}
const getComment = () => {
const loadWaline = () => {
Waline.RecentComments({
serverURL: GLOBAL_CONFIG.source.waline.serverURL,
count: 20
}).then(({ comments }) => {
const walineArray = comments.map(e => {
return {
'content': changeContent(e.comment),
'avatar': e.avatar,
'nick': e.nick,
'url': e.url + '#' + e.objectId,
'date': e.insertedAt,
}
})
saveToLocal.set('waline-newest-comments', JSON.stringify(walineArray), 10 / (60 * 24))
generateHtml(walineArray)
document.querySelector('#newcomm') && necommHtml(artalk)
}).catch(e => {
const $dom = document.querySelector('#card-newest-comments .aside-list')
$dom.innerHTML = "无法获取评论,请确认相关配置是否正确"
})
}
if (typeof Waline === 'object') loadWaline()
else getScript(GLOBAL_CONFIG.source.waline.js).then(loadWaline)
}
const necommHtml = array => {
let result = ''
const pagesize = [[${ theme.config.sidebar.newcommentnumber }]];
const defaultpagesize = 5;
const finalpagesize = pagesize <= 0 ? defaultpagesize : pagesize;
if (array.length) {
for (let i = 0; i < array.length; i++) {
if (i == finalpagesize) {
break;
}
result += '<div class="aside-list-item">'
if (true) {
let name = 'src'
if ([[${ isLazyload }]]) {
name = 'data-lazy-src'
}
result += `
<a class="thumbnail" href="${array[i].url}">
<img alt="dasda" ${name}="${array[i].avatar}">
</a>
`
}
result += `
<div class="content">
<a class="comment" style="display: -webkit-box;-webkit-line-clamp: 2;-webkit-box-orient: vertical;overflow: hidden;"
href="${array[i].url}" title="${array[i].content}">
${array[i].content}
</a>
<div class="name">
<span>${array[i].nick} / </span>
<time datetime="${array[i].date}">${btf.diffDate(array[i].date, true)}</time>
</div>
</div>
</div>
`
}
} else {
result += '没有评论'
}
let $dom = document.querySelector('#newcomm')
$dom.innerHTML = result
window.lazyLoadInstance && window.lazyLoadInstance.update()
window.pjax && window.pjax.refresh($dom)
}
const newestCommentInit = () => {
if (document.querySelector('#card-newest-comments .aside-list')) {
const data = saveToLocal.get('waline-newest-comments')
if (data) {
generateHtml(JSON.parse(data))
document.querySelector('#newcomm') && necommHtml(JSON.parse(data))
} else {
getComment()
}
}
}
newestCommentInit()
document.addEventListener('pjax:complete', newestCommentInit)
})
</script>
</th:block>
</html>

View File

@ -1,7 +1,9 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<!-- 导航栏菜单栏 -->
<div id="post-comment" th:fragment="twikoo">
<div id="post-comment" th:fragment="twikoo" th:if="${theme.config.comments.twikooEnable &&
#strings.equals(theme.config.comments.use, 'Twikoo') &&
not #strings.isEmpty(theme.config.comments.twikoos.envId)}">
<div class="comment-head">
<div class="comment-headline"><i class="haofont hao-icon-comments"></i> <span>评论</span></div>
<div class="comment-privacy"><a href="/privacy">隐私政策</a></div>

View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<!-- 导航栏菜单栏 -->
<div id="post-comment" th:fragment="waline" th:if="${theme.config.comments.walineEnable
&& #strings.equals(theme.config.comments.use, 'Waline')
&& not #strings.isEmpty(theme.config.comments.walines.serverURL)}">
<div class="comment-head">
<div class="comment-headline"><i class="haofont hao-icon-comments"></i> <span>评论</span></div>
<div class="comment-privacy"><a href="/privacy">隐私政策</a></div>
<div class="comment-tips" id="comment-tips">
<span>你无需删除空行,直接评论以获取最佳展示效果</span>
</div>
</div>
<div id="waline-wrap"></div>
</div>
</html>

View File

@ -12,7 +12,7 @@
<button id="rightside-config" type="button" title="设置"><i class="haofont hao-icon-gear"></i></button>
<button th:if="${htmlType == 'post'}" class="close" id="mobile-toc-button" type="button" title="目录"><i class="haofont hao-icon-list-ul"></i></button>
<a id="to_comment" href="javascript:heo.scrollTo('post-comment')" title="直达评论" ><i class="haofont hao-icon-comments"></i></a>
<a id="switch-commentBarrage" href="javascript:heo.switchCommentBarrage();" rel="external nofollow noreferrer" title="开关弹幕" draggable="false" data-pjax-state="external"><i class="haofont hao-icon-danmu"></i></a>
<a th:if="${theme.config.comments.commentBarrageConfig.commentBarrageEnable}" id="switch-commentBarrage" href="javascript:heo.switchCommentBarrage();" rel="external nofollow noreferrer" title="开关弹幕" draggable="false" data-pjax-state="external"><i class="haofont hao-icon-danmu"></i></a>
<button id="go-up" type="button" title="回到顶部"><i class="haofont hao-icon-arrow-up"></i></button>
<a th:if="${htmlType == 'post' || htmlType == 'page'}"
th:with="editor = ${htmlType == 'post' ? 'posts/editor?name='+ post.metadata.name : htmlType == 'page' ? 'single-pages/editor?name='+ singlePage.metadata.name : '' }"

View File

@ -93,6 +93,21 @@
document.head.appendChild(script)
})
win.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)
})
win.activateDarkMode = function () {
document.documentElement.setAttribute('data-theme', 'dark')
heo.initThemeColor()
@ -205,13 +220,28 @@
js: 'https://lf6-cdn-tos.bytecdntp.com/cdn/expire-1-M/fancybox/3.5.7/jquery.fancybox.min.js',
css: 'https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/fancybox/3.5.7/jquery.fancybox.min.css'
},
comments:{
use: [[${theme.config.comments.use}]],
maxBarrage: [[${ theme.config.comments.commentBarrageConfig.maxBarrage }]],
barrageTime: [[${ theme.config.comments.commentBarrageConfig.barrageTime }]],
mailMd5: "[(${theme.config.comments.commentBarrageConfig.mailMd5})]",
},
twikoo:{
js:[[${not #strings.isEmpty(theme.config.comments.twikoos.js) ? theme.config.comments.twikoos.js : assets_link +'/libs/twikoo/twikoo.all.min.js' }]],
maxBarrage: [[${ theme.config.comments.twikoos.maxBarrage }]],
barrageTime: [[${ theme.config.comments.twikoos.barrageTime }]],
twikooUrl: "[(${theme.config.comments.twikoos.envId})]",
accessToken: "[(${theme.config.comments.twikoos.accessToken})]",
mailMd5: "[(${theme.config.comments.twikoos.mailMd5})]",
},
artalk:{
js:[[${not #strings.isEmpty(theme.config.comments.artalks.artalkJs) ? theme.config.comments.artalks.artalkJs : 'https://cdn.bootcdn.net/ajax/libs/artalk/2.6.2/Artalk.js' }]],
css:[[${not #strings.isEmpty(theme.config.comments.artalks.artalkCss) ? theme.config.comments.artalks.artalkCss : 'https://cdn.bootcdn.net/ajax/libs/artalk/2.6.2/Artalk.css' }]],
artalkUrl: "[(${theme.config.comments.artalks.server})]",
siteName: "[(${theme.config.comments.artalks.siteName})]",
},
waline:{
js:[[${not #strings.isEmpty(theme.config.comments.walines.walinesJs) ? theme.config.comments.walines.walinesJs : 'https://cdn.cbd.int/@waline/client@2.15.7/dist/waline.js' }]],
css:[[${not #strings.isEmpty(theme.config.comments.walines.walinesCss) ? theme.config.comments.walines.walinesCss : 'https://cdn.cbd.int/@waline/client@2.15.7/dist/waline.css' }]],
serverURL: "[(${theme.config.comments.walines.serverURL})]",
},
welcome:{
key:"[(${theme.config.sidebar.welcome.key})]",

View File

@ -67,8 +67,11 @@
<!-- 应该是文章页分享使用 -->
<script data-pjax src="https://lf3-cdn-tos.bytecdntp.com/cdn/expire-1-M/qrcodejs/1.0.0/qrcode.min.js"></script>
<!-- 最近评论 -->
<th:block th:replace="~{modules/widgets/cardNewestComments :: cardNewestComments}"></th:block>
<!-- 评论 -->
<th:block th:if="${theme.config.comments.use!='commentWidget'}"
th:with="use = ${theme.config.comments.use}">
<th:block th:replace="~{'modules/comment/newest-comments/' + ${use}}"></th:block>
</th:block>
<!-- https://raphamorim.io/waterfall.js/ 应该是这个 还有相关的 js 代码 是否可以调整-->
<script th:src="${assets_link + '/libs/waterfall/waterfall.min.js'}"></script>
@ -97,11 +100,14 @@
<script th:if="${theme.config.tool.rightMenu.rightMenuEnable}" th:src="${assets_link + '/zhheo/rightmenu.js'}"></script>
<!-- 评论弹幕 -->
<script th:if="${theme.config.comments.twikooEnable && #strings.equals(theme.config.comments.use, 'Twikoo') &&
<script th:if="${ ((theme.config.comments.twikooEnable &&
not #strings.isEmpty(theme.config.comments.twikoos.envId) &&
not #strings.isEmpty(theme.config.comments.twikoos.accessToken)}" data-pjax=""
not #strings.isEmpty(theme.config.comments.twikoos.accessToken) ) ||
(theme.config.comments.artalkEnable &&
not #strings.isEmpty(theme.config.comments.artalks.server))) && theme.config.comments.commentBarrageConfig.commentBarrageEnable}" data-pjax=""
th:src="${assets_link + '/zhheo/commentBarrage.js'}"></script>
<!-- Tocbot 目录生成 start -->
<th:block th:replace="~{modules/common/toc-bot :: toc-bot}"></th:block>
@ -229,7 +235,7 @@
if(document.querySelector('#danmu') &&
document.body.clientWidth > 768 &&
[[${theme.config.envelope_comment.enable_danmu}]]){
halo.addScript("Danmaku", "[[${assets_link + '/libs/twikoo/easy-Danmaku.min.js'}]]", halo.danmu("[(${theme.config.comments.twikoos.envId})]","[(${theme.config.comments.twikoos.accessToken})]",[[${theme.config.envelope_comment.maxBarrage}]]))
halo.addScript("Danmaku", "[[${assets_link + '/libs/twikoo/easy-Danmaku.min.js'}]]", halo.danmu())
}
</script>

View File

@ -102,7 +102,7 @@
<i class="haofont hao-icon-copy"></i>
<span>复制地址</span>
</div>
<div class="rightMenu-item" id="menu-commentBarrage"
<div th:if="${theme.config.comments.commentBarrageConfig.commentBarrageEnable}" class="rightMenu-item" id="menu-commentBarrage"
rel="external nofollow noreferrer" draggable="false" style="display: flex;">
<i class="haofont hao-icon-message"></i>
<span class="menu-commentBarrage-text">关闭热评</span>

View File

@ -8,7 +8,7 @@
<div id="sidebar-menus">
<span class="sidebar-menu-item-title">功能</span>
<div class="sidebar-menu-item">
<a class="darkmode_switchbutton menu-child" href="javascript:void(0);" onclick="rm.switchDarkMode()"
<a class="darkmode_switchbutton menu-child" id="darkmode_switchbutton" href="javascript:void(0);" onclick="rm.switchDarkMode()"
rel="external nofollow" title="显示模式切换">
<i class="haofont hao-icon-moon" style="font-size: 0.9rem;"></i>
<span>显示模式</span>

View File

@ -22,7 +22,7 @@
<!-- 切换模式 -->
<div class="nav-button" th:if="${theme.config.nav.right.darkMode}">
<a class="console_switchbutton" href="javascript:void(0);" onclick="navFn.switchDarkMode();" rel="external nofollow"
<a class="console_switchbutton" id="darkmode_switchbutton" href="javascript:void(0);" onclick="navFn.switchDarkMode();" rel="external nofollow"
title="切换模式 - 日夜交替,黑白互换。">
<i class="haofont hao-icon-circle-half-stroke" style="font-size: 1rem; font-weight: 700;"></i>
</a>
@ -40,7 +40,7 @@
th:with="currentUser = ${contributorFinder.getContributor(#authentication.name)}">
<span class="site-page nav-login">
<i sec:authorize="isAnonymous()" class="haofont hao-icon-zhanghao1 " style="font-size: 1rem;font-weight: 700;"></i>
<img sec:authorize="isAuthenticated()" th:src="${currentUser.avatar ?: #theme.assets('/images/none.png')}"
<img sec:authorize="isAuthenticated()" th:src="${currentUser.avatar}"
th:alt="${currentUser.displayName}"
style=" width: 24px; height: 24px; border-radius: 9999px" />
</span>

View File

@ -82,7 +82,7 @@
href="#post-comment"><span id="comment-count"
th:text="${post.stats.comment}"></span></a>
<a th:if="${#strings.equals(theme.config.comments.use, 'Twikoo')}" href="#post-comment">
<a th:if="${#strings.contains('Twikoo',theme.config.comments.use)}" href="#post-comment">
<span id="twikoo-count"></span></a>
</span>
<span class="post-meta-commentcount" sec:authorize="isAuthenticated()"