This commit is contained in:
myw
2026-07-16 16:53:38 +08:00 Unverified
parent 6b44647d4e
commit d8942c236a
22 changed files with 263 additions and 240 deletions
+5 -2
View File
@@ -2,8 +2,11 @@ script.
(() => {
const abcjsInit = () => {
const abcjsFn = () => {
const $article = document.getElementById("article-container")
if (!$article || $article.querySelector(".hbe-container")) return
setTimeout(() => {
const sheets = document.querySelectorAll(".abc-music-sheet")
const sheets = $article.querySelectorAll(".abc-music-sheet")
for (let i = 0; i < sheets.length; i++) {
const ele = sheets[i]
if (ele.children.length > 0) continue
@@ -29,7 +32,7 @@ script.
}, 100)
}
if (typeof ABCJS === "object") {
if (typeof ABCJS === "object" && ABCJS !== null) {
abcjsFn()
} else {
btf.getScript("!{url_for(theme.asset.abcjs_basic_js)}").then(abcjsFn)
+4 -4
View File
@@ -3,7 +3,7 @@ case theme.chat.use
include ./chatra.pug
when 'tidio'
include ./tidio.pug
when 'crisp'
include ./crisp.pug
when 'knocket'
include ./knocket.pug
when 'crisp'
include ./crisp.pug
when 'knocket'
include ./knocket.pug
+10 -12
View File
@@ -1,14 +1,15 @@
//- Knocket live chat — https://trtc.io/solutions/knocket
script.
(() => {
const id = '#{theme.knocket.identifier}'
if (!id) return
const identifier = !{JSON.stringify(theme.knocket.identifier || '').replace(/</g, '\\u003c')}
if (!identifier) return
btf.getScript(`https://trtc.io/knocket-sdk/sdk.js?identifier=${id}`).then(() => {
btf.getScript(`https://trtc.io/knocket-sdk/sdk.js?identifier=${encodeURIComponent(identifier)}`).then(() => {
const isChatBtn = !{theme.chat.rightside_button}
const isChatHideShow = !{theme.chat.button_hide_show}
const getWidget = () => document.getElementById('contact-widget-auto')
let widget = null
const getWidget = () => widget || (widget = document.getElementById('contact-widget-auto'))
if (isChatBtn) {
const hide = () => { const w = getWidget(); if (w) w.style.display = 'none' }
@@ -24,21 +25,18 @@ script.
window.chatBtnFn = () => {
const w = getWidget()
if (!w) return
if (w.style.display === 'none') show()
else hide()
w.style.display = w.style.display === 'none' ? '' : 'none'
}
document.getElementById('chat-btn').style.display = 'block'
const chatBtn = document.getElementById('chat-btn')
if (chatBtn) chatBtn.style.display = 'block'
} else if (isChatHideShow) {
const observer = new MutationObserver(() => {
if (getWidget()) observer.disconnect()
})
observer.observe(document.body, { childList: true, subtree: true })
window.chatBtn = {
hide: () => { const w = getWidget(); if (w) w.style.display = 'none' },
show: () => { const w = getWidget(); if (w) w.style.display = '' }
}
}
}).catch(error => {
console.warn('[Knocket] Failed to load the chat SDK.', error)
})
})()
+1 -1
View File
@@ -15,7 +15,7 @@ script.
const loadMathjax = () => {
const article = document.getElementById('article-container')
if (!article) return
if (!article || article.querySelector('.hbe-container')) return
changeScriptToMath(article)
if (!window.MathJax) {
+7 -4
View File
@@ -330,8 +330,8 @@ script.
})
}
const codeToMermaid = () => {
const codeMermaidEle = document.querySelectorAll('pre > code.mermaid')
const codeToMermaid = $article => {
const codeMermaidEle = $article.querySelectorAll('pre > code.mermaid')
if (codeMermaidEle.length === 0) return
codeMermaidEle.forEach(ele => {
@@ -347,8 +347,11 @@ script.
}
const loadMermaid = () => {
if (!{theme.mermaid.code_write}) codeToMermaid()
const $mermaid = document.querySelectorAll('#article-container .mermaid-wrap')
const $article = document.getElementById('article-container')
if (!$article) return
if (!{theme.mermaid.code_write}) codeToMermaid($article)
const $mermaid = $article.querySelectorAll('.mermaid-wrap')
if ($mermaid.length === 0) return
const runMermaidFn = () => runMermaid($mermaid)
+5 -4
View File
@@ -65,12 +65,13 @@ script.
})
document.addEventListener('pjax:error', e => {
if (e.request.status === 404) {
const responseURL = e.request && e.request.responseURL
if (e.request && e.request.status === 404) {
!{theme.error_404 && theme.error_404.enable}
? pjax.loadUrl('!{url_for("/404.html")}')
: window.location.href = e.request.responseURL
} else {
window.location.href = e.request.responseURL
: responseURL && (window.location.href = responseURL)
} else if (responseURL) {
window.location.href = responseURL
}
})
})
+5 -1
View File
@@ -5,11 +5,15 @@
if (syntax_highlighter === 'prismjs' || enable) && !preprocess
script.
(() => {
const $article = document.getElementById('article-container')
if (!$article || $article.querySelector('.hbe-container')) return
window.Prism = window.Prism || {}
window.Prism.manual = true
const highlightAll = () => {
window.Prism.highlightAll()
// window.Prism.highlightAll()
window.Prism.highlightAllUnder($article, true)
}
window.addEventListener('load', highlightAll)
+5 -5
View File
@@ -44,7 +44,7 @@ script.
}
}
}
btf.addGlobalFn('pjaxSendOnce', () => { typed.destroy() }, 'typedDestroy')
btf.addGlobalFn('pjaxSendOnce', () => { typed && typed.destroy() }, 'typedDestroy')
case source
when 1
@@ -53,8 +53,8 @@ case source
fetch('https://v1.hitokoto.cn')
.then(response => response.json())
.then(data => {
const from = '出自 ' + data.from
typedJSFn.processSubtitle(data.hitokoto, [from])
const from = data.from ? '出自 ' + data.from : ''
typedJSFn.processSubtitle(data.hitokoto, from ? [from] : [])
})
.catch(err => {
console.error('Failed to get the Hitokoto API:', err)
@@ -79,7 +79,7 @@ case source
})
.catch(err => {
console.error('Failed to get the Yiyan API:', err)
typedJSFn.processSubtitle(!{JSON.stringify(subContent.length)})
typedJSFn.processSubtitle(!{JSON.stringify(subContent)})
})
}
typedJSFn.run(subtitleType)
@@ -99,7 +99,7 @@ case source
})
.catch(err => {
console.error('Failed to get the Jinrishici API:', err)
typedJSFn.processSubtitle(!{JSON.stringify(subContent.length)})
typedJSFn.processSubtitle(!{JSON.stringify(subContent)})
})
}
typedJSFn.run(subtitleType)
+116 -110
View File
@@ -1,110 +1,116 @@
- let { serverURL, script_name, website_id, option, UV_PV } = theme.umami_analytics
- const isServerURL = !!serverURL
- const baseURL = serverURL ? serverURL.replace(/\/$/, '') : 'https://cloud.umami.is'
- const apiUrl = serverURL ? serverURL.replace(/\/$/, '') + '/api' : 'https://api.umami.is/v1'
script.
(() => {
const option = !{JSON.stringify(option)}
const config = !{JSON.stringify(UV_PV)}
const runTrack = () => {
if (typeof umami !== 'undefined' && typeof umami.track === 'function') {
umami.track(props => ({ ...props, url: window.location.pathname, title: GLOBAL_CONFIG_SITE.title }))
} else {
console.warn('Umami Analytics: umami.track is not available')
}
}
const loadUmamiJS = () => {
btf.getScript('!{baseURL}/!{script_name}', {
'data-website-id': '!{website_id}',
'data-auto-track': 'false',
...option
}).then(() => {
runTrack()
}).catch(error => {
console.error('Umami Analytics: Error loading script', error)
})
}
const getData = async (isPost) => {
try {
const now = Date.now()
const keyUrl = isPost ? `&url=${window.location.pathname}&path=${window.location.pathname}` : ''
const headerList = { 'Accept': 'application/json' }
if (!{isServerURL}) {
headerList['Authorization'] = `Bearer ${config.token}`
} else {
headerList['x-umami-api-key'] = config.token
}
const res = await fetch(`!{apiUrl}/websites/!{website_id}/stats?startAt=0000000000&endAt=${now}${keyUrl}`, {
method: "GET",
headers: headerList
})
if (!res.ok) {
throw new Error(`HTTP error! status: ${res.status}`)
}
return await res.json()
} catch (error) {
console.error('Umami Analytics: Failed to fetch data', error)
throw error
}
}
const insertData = async () => {
try {
if (GLOBAL_CONFIG_SITE.pageType === 'post' && config.page_pv) {
const pagePV = document.getElementById('umamiPV')
if (pagePV) {
const data = await getData(true)
if (data && data.pageviews) {
pagePV.textContent = typeof data.pageviews.value !== 'undefined' ? data.pageviews.value : data.pageviews
} else {
console.warn('Umami Analytics: Invalid page view data received')
}
}
}
if (config.site_uv || config.site_pv) {
const data = await getData(false)
if (config.site_uv) {
const siteUV = document.getElementById('umami-site-uv')
if (siteUV && data && data.visitors) {
siteUV.textContent = typeof data.visitors.value !== 'undefined' ? data.visitors.value : data.visitors
} else if (siteUV) {
console.warn('Umami Analytics: Invalid site UV data received')
}
}
if (config.site_pv) {
const sitePV = document.getElementById('umami-site-pv')
if (sitePV && data && data.pageviews) {
sitePV.textContent = typeof data.pageviews.value !== 'undefined' ? data.pageviews.value : data.pageviews
} else if (sitePV) {
console.warn('Umami Analytics: Invalid site PV data received')
}
}
}
} catch (error) {
console.error('Umami Analytics: Failed to insert data', error)
}
}
btf.addGlobalFn('pjaxComplete', runTrack, 'umami_analytics_run_track')
btf.addGlobalFn('pjaxComplete', insertData, 'umami_analytics_insert')
loadUmamiJS()
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', insertData)
} else {
setTimeout(insertData, 100)
}
})()
- let { serverURL, script_name, website_id, option, UV_PV } = theme.umami_analytics
- const isServerURL = !!serverURL
- const baseURL = serverURL ? serverURL.replace(/\/$/, '') : 'https://cloud.umami.is'
- const apiUrl = serverURL ? serverURL.replace(/\/$/, '') + '/api' : 'https://api.umami.is/v1'
script.
(() => {
const option = !{JSON.stringify(option)}
const config = !{JSON.stringify(UV_PV)}
const runTrack = () => {
if (typeof umami !== 'undefined' && typeof umami.track === 'function') {
umami.track(props => ({ ...props, url: window.location.pathname, title: GLOBAL_CONFIG_SITE.title }))
} else {
console.warn('Umami Analytics: umami.track is not available')
}
}
const loadUmamiJS = () => {
btf.getScript('!{baseURL}/!{script_name}', {
'data-website-id': '!{website_id}',
'data-auto-track': 'false',
...option
}).then(() => {
runTrack()
}).catch(error => {
console.error('Umami Analytics: Error loading script', error)
})
}
const extractValue = stat => (stat && typeof stat.value !== 'undefined') ? stat.value : stat
const getData = async (isPost) => {
try {
const now = Date.now()
const pathQuery = isPost ? `&url=${window.location.pathname}&path=${window.location.pathname}` : ''
const headers = {
'Accept': 'application/json',
[!{isServerURL} ? 'Authorization' : 'x-umami-api-key']: !{isServerURL} ? `Bearer ${config.token}` : config.token
}
const res = await fetch(`!{apiUrl}/websites/!{website_id}/stats?startAt=0000000000&endAt=${now}${pathQuery}`, {
method: 'GET',
headers
})
if (!res.ok) {
throw new Error(`HTTP error! status: ${res.status}`)
}
return await res.json()
} catch (error) {
console.error('Umami Analytics: Failed to fetch data', error)
throw error
}
}
const insertData = async () => {
try {
const tasks = []
if (GLOBAL_CONFIG_SITE.pageType === 'post' && config.page_pv) {
tasks.push((async () => {
const pagePV = document.getElementById('umamiPV')
if (pagePV) {
const data = await getData(true)
if (data && data.pageviews) {
pagePV.textContent = extractValue(data.pageviews)
} else {
console.warn('Umami Analytics: Invalid page view data received')
}
}
})())
}
if (config.site_uv || config.site_pv) {
tasks.push((async () => {
const data = await getData(false)
if (config.site_uv) {
const siteUV = document.getElementById('umami-site-uv')
if (siteUV && data && data.visitors) {
siteUV.textContent = extractValue(data.visitors)
} else if (siteUV) {
console.warn('Umami Analytics: Invalid site UV data received')
}
}
if (config.site_pv) {
const sitePV = document.getElementById('umami-site-pv')
if (sitePV && data && data.pageviews) {
sitePV.textContent = extractValue(data.pageviews)
} else if (sitePV) {
console.warn('Umami Analytics: Invalid site PV data received')
}
}
})())
}
await Promise.all(tasks)
} catch (error) {
console.error('Umami Analytics: Failed to insert data', error)
}
}
btf.addGlobalFn('pjaxComplete', runTrack, 'umami_analytics_run_track')
btf.addGlobalFn('pjaxComplete', insertData, 'umami_analytics_insert')
loadUmamiJS()
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', insertData)
} else {
setTimeout(insertData, 100)
}
})()