mirror of
https://github.com/jerryc127/hexo-theme-butterfly.git
synced 2026-08-10 20:48:42 +08:00
feat: add wordcount helpers, waline lightbox, and extensive bug fixes
- wordcount/min2read/totalcount built-in helpers (replaces hexo-wordcount) - cloudTags random sort support - waline lightbox via MutationObserver - subtitle: seq guard prevents stale API response race - newest-comments: escapeHtml XSS fix, unified fetchAndRender with cache fallback - katex: try/catch asset load, encrypt re-render - chartjs: destroy charts on pjax, fix theme property check - mathjax: DOMContentLoaded instead of load - mermaid: clean stale error elements - medium-zoom: single instance, detach on pjax - getScrollPercent: WeakMap cache with resize invalidation - overflowPaddingR: avoid stale DOM refs - readmode: dedupe button, pjax cleanup - hexo-blog-decrypt: null-guard translateFn and encryptFn - tw_cn: expand skipped translate tags - localStorage: try/catch setItem - cdn.js: fix [lib|dist]* character class regex bug - findArchivesTitle: support custom archive_dir - shuoshuoFN: guard moment.tz when timezone empty - umami: no-store cache, error handling, path param - artalk: vote config option - bump version 5.7.1.260810
This commit is contained in:
+120
-113
@@ -1,113 +1,120 @@
|
||||
- const { effect, source, sub, typed_option } = theme.subtitle
|
||||
- let subContent = typeof sub === 'string' ? [sub] : (sub || new Array())
|
||||
|
||||
script.
|
||||
window.typedJSFn = {
|
||||
init: str => {
|
||||
window.typed = new Typed('#subtitle', Object.assign({
|
||||
strings: str,
|
||||
startDelay: 300,
|
||||
typeSpeed: 150,
|
||||
loop: true,
|
||||
backSpeed: 50,
|
||||
}, !{JSON.stringify(typed_option)}))
|
||||
},
|
||||
run: subtitleType => {
|
||||
if (!{effect}) {
|
||||
if (typeof Typed === 'function') {
|
||||
subtitleType()
|
||||
} else {
|
||||
btf.getScript('!{url_for(theme.asset.typed)}').then(subtitleType)
|
||||
}
|
||||
} else {
|
||||
subtitleType()
|
||||
}
|
||||
},
|
||||
processSubtitle: (content, extraContents = []) => {
|
||||
if (!{effect}) {
|
||||
const sub = !{JSON.stringify(subContent)}.slice()
|
||||
|
||||
if (extraContents.length > 0) {
|
||||
sub.unshift(...extraContents)
|
||||
}
|
||||
|
||||
if (typeof content === 'string') {
|
||||
sub.unshift(content)
|
||||
} else if (Array.isArray(content)) {
|
||||
sub.unshift(...content)
|
||||
}
|
||||
|
||||
sub.length > 0 && typedJSFn.init(sub)
|
||||
} else {
|
||||
document.getElementById('subtitle').textContent = typeof content === 'string' ? content :
|
||||
(Array.isArray(content) && content.length > 0 ? content[0] : '')
|
||||
}
|
||||
}
|
||||
}
|
||||
btf.addGlobalFn('pjaxSendOnce', () => { typed && typed.destroy() }, 'typedDestroy')
|
||||
|
||||
case source
|
||||
when 1
|
||||
script.
|
||||
function subtitleType () {
|
||||
fetch('https://v1.hitokoto.cn')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
const from = data.from ? '出自 ' + data.from : ''
|
||||
typedJSFn.processSubtitle(data.hitokoto, from ? [from] : [])
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Failed to get the Hitokoto API:', err)
|
||||
typedJSFn.processSubtitle(!{JSON.stringify(subContent)})
|
||||
})
|
||||
}
|
||||
typedJSFn.run(subtitleType)
|
||||
|
||||
when 2
|
||||
script.
|
||||
function subtitleType () {
|
||||
fetch('https://v.api.aa1.cn/api/yiyan/index.php')
|
||||
.then(response => response.text())
|
||||
.then(data => {
|
||||
const reg = /<p>(.*?)<\/p>/g
|
||||
const result = reg.exec(data)
|
||||
if (result && result[1]) {
|
||||
typedJSFn.processSubtitle(result[1])
|
||||
} else {
|
||||
throw new Error('Failed to parse the return value of the Yiyan API')
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Failed to get the Yiyan API:', err)
|
||||
typedJSFn.processSubtitle(!{JSON.stringify(subContent)})
|
||||
})
|
||||
}
|
||||
typedJSFn.run(subtitleType)
|
||||
|
||||
when 3
|
||||
script.
|
||||
function subtitleType () {
|
||||
btf.getScript('https://sdk.jinrishici.com/v2/browser/jinrishici.js')
|
||||
.then(() => {
|
||||
jinrishici.load(result => {
|
||||
if (result && result.data && result.data.content) {
|
||||
typedJSFn.processSubtitle(result.data.content)
|
||||
} else {
|
||||
throw new Error('Failed to parse the return value of Jinrishici API')
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('Failed to get the Jinrishici API:', err)
|
||||
typedJSFn.processSubtitle(!{JSON.stringify(subContent)})
|
||||
})
|
||||
}
|
||||
typedJSFn.run(subtitleType)
|
||||
|
||||
default
|
||||
if subContent.length > 0
|
||||
script.
|
||||
function subtitleType () {
|
||||
typedJSFn.processSubtitle(!{JSON.stringify(subContent)})
|
||||
}
|
||||
typedJSFn.run(subtitleType)
|
||||
- const { effect, source, sub, typed_option } = theme.subtitle
|
||||
- let subContent = typeof sub === 'string' ? [sub] : (sub || new Array())
|
||||
|
||||
script.
|
||||
window.typedJSFn = {
|
||||
_seq: 0,
|
||||
init: str => {
|
||||
window.typed = new Typed('#subtitle', Object.assign({
|
||||
strings: str,
|
||||
startDelay: 300,
|
||||
typeSpeed: 150,
|
||||
loop: true,
|
||||
backSpeed: 50,
|
||||
}, !{JSON.stringify(typed_option)}))
|
||||
},
|
||||
run: subtitleType => {
|
||||
const seq = ++window.typedJSFn._seq
|
||||
const invoke = () => {
|
||||
if (seq === window.typedJSFn._seq) subtitleType()
|
||||
}
|
||||
|
||||
if (!{effect}) {
|
||||
if (typeof Typed === 'function') invoke()
|
||||
else btf.getScript('!{url_for(theme.asset.typed)}').then(invoke)
|
||||
} else {
|
||||
invoke()
|
||||
}
|
||||
},
|
||||
fetchSubtitle: (fetcher, parser) => {
|
||||
const seq = window.typedJSFn._seq
|
||||
Promise.resolve()
|
||||
.then(fetcher)
|
||||
.then(parser)
|
||||
.then(({ content, extra = [] }) => {
|
||||
if (seq !== window.typedJSFn._seq) return
|
||||
typedJSFn.processSubtitle(content, extra)
|
||||
})
|
||||
.catch(err => {
|
||||
if (seq !== window.typedJSFn._seq) return
|
||||
console.error('Failed to get the subtitle API:', err)
|
||||
typedJSFn.processSubtitle(!{JSON.stringify(subContent)})
|
||||
})
|
||||
},
|
||||
processSubtitle: (content, extraContents = []) => {
|
||||
if (!{effect}) {
|
||||
const sub = !{JSON.stringify(subContent)}.slice()
|
||||
|
||||
if (extraContents.length > 0) {
|
||||
sub.unshift(...extraContents)
|
||||
}
|
||||
|
||||
if (typeof content === 'string') {
|
||||
sub.unshift(content)
|
||||
} else if (Array.isArray(content)) {
|
||||
sub.unshift(...content)
|
||||
}
|
||||
|
||||
sub.length > 0 && typedJSFn.init(sub)
|
||||
} else {
|
||||
document.getElementById('subtitle').textContent = typeof content === 'string' ? content :
|
||||
(Array.isArray(content) && content.length > 0 ? content[0] : '')
|
||||
}
|
||||
}
|
||||
}
|
||||
btf.addGlobalFn('pjaxSendOnce', () => {
|
||||
window.typedJSFn._seq++
|
||||
typed && typed.destroy()
|
||||
window.typed = null
|
||||
}, 'typedDestroy')
|
||||
|
||||
case source
|
||||
when 1
|
||||
script.
|
||||
function subtitleType () {
|
||||
typedJSFn.fetchSubtitle(
|
||||
() => fetch('https://v1.hitokoto.cn').then(response => response.json()),
|
||||
data => ({ content: data.hitokoto, extra: data.from ? ['出自 ' + data.from] : [] })
|
||||
)
|
||||
}
|
||||
typedJSFn.run(subtitleType)
|
||||
|
||||
when 2
|
||||
script.
|
||||
function subtitleType () {
|
||||
typedJSFn.fetchSubtitle(
|
||||
() => fetch('https://v.api.aa1.cn/api/yiyan/index.php').then(response => response.text()),
|
||||
data => {
|
||||
const reg = /<p>(.*?)<\/p>/g
|
||||
const result = reg.exec(data)
|
||||
if (result && result[1]) return { content: result[1] }
|
||||
throw new Error('Failed to parse the return value of the Yiyan API')
|
||||
}
|
||||
)
|
||||
}
|
||||
typedJSFn.run(subtitleType)
|
||||
|
||||
when 3
|
||||
script.
|
||||
function subtitleType () {
|
||||
typedJSFn.fetchSubtitle(
|
||||
() => btf.getScript('https://sdk.jinrishici.com/v2/browser/jinrishici.js').then(() =>
|
||||
new Promise((resolve, reject) => {
|
||||
jinrishici.load(result => {
|
||||
if (result && result.data && result.data.content) resolve(result.data.content)
|
||||
else reject(new Error('Failed to parse the return value of Jinrishici API'))
|
||||
})
|
||||
})
|
||||
),
|
||||
content => ({ content })
|
||||
)
|
||||
}
|
||||
typedJSFn.run(subtitleType)
|
||||
|
||||
default
|
||||
if subContent.length > 0
|
||||
script.
|
||||
function subtitleType () {
|
||||
typedJSFn.processSubtitle(!{JSON.stringify(subContent)})
|
||||
}
|
||||
typedJSFn.run(subtitleType)
|
||||
|
||||
Reference in New Issue
Block a user