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:
myw
2026-08-10 18:02:27 +08:00 Unverified
parent 51d36ef83a
commit 79e16a5cdd
25 changed files with 726 additions and 624 deletions
+58 -42
View File
@@ -56,43 +56,30 @@
}
},
overflowPaddingR: (() => {
let headerElement = null
let menuElement = null
overflowPaddingR: {
add: () => {
const paddingRight = window.innerWidth - document.body.clientWidth
const getElements = () => {
if (!headerElement) {
headerElement = document.getElementById('page-header')
}
if (!menuElement) {
menuElement = document.getElementById('menus')
}
return { headerElement, menuElement }
}
return {
add: () => {
const paddingRight = window.innerWidth - document.body.clientWidth
if (paddingRight > 0) {
document.body.style.paddingRight = `${paddingRight}px`
document.body.style.overflow = 'hidden'
const { headerElement: header, menuElement: menu } = getElements()
if (header && menu && header.classList.contains('nav-fixed')) {
menu.style.paddingRight = `${paddingRight}px`
}
}
},
remove: () => {
document.body.style.paddingRight = ''
document.body.style.overflow = ''
const { headerElement: header, menuElement: menu } = getElements()
if (paddingRight > 0) {
document.body.style.paddingRight = `${paddingRight}px`
document.body.style.overflow = 'hidden'
const header = document.getElementById('page-header')
const menu = document.getElementById('menus')
if (header && menu && header.classList.contains('nav-fixed')) {
menu.style.paddingRight = ''
menu.style.paddingRight = `${paddingRight}px`
}
}
},
remove: () => {
document.body.style.paddingRight = ''
document.body.style.overflow = ''
const header = document.getElementById('page-header')
const menu = document.getElementById('menus')
if (header && menu && header.classList.contains('nav-fixed')) {
menu.style.paddingRight = ''
}
}
})(),
},
snackbarShow: (text, showAction = false, duration = 2000) => {
const { position, bgLight, bgDark } = GLOBAL_CONFIG.Snackbar
@@ -200,7 +187,12 @@
const service = GLOBAL_CONFIG.lightbox
if (service === 'medium_zoom') {
mediumZoom(ele, { background: 'var(--zoom-bg)' })
const zoom = window.mediumZoomInstance || (window.mediumZoomInstance = mediumZoom({ background: 'var(--zoom-bg)' }))
zoom.attach(ele)
btf.addGlobalFn('pjaxSendOnce', () => {
window.mediumZoomInstance && window.mediumZoomInstance.detach()
}, 'mediumZoom')
return
}
@@ -246,7 +238,7 @@
},
hideScrollbar: false
}
} else {
} else {
options = {
Hash: false,
Carousel: {
@@ -314,17 +306,41 @@
},
getScrollPercent: (() => {
let docHeight, winHeight, headerHeight, contentMath
let cache = new WeakMap()
return (currentTop, ele) => {
if (!docHeight || ele.clientHeight !== docHeight) {
docHeight = ele.clientHeight
winHeight = window.innerHeight
headerHeight = ele.offsetTop
contentMath = Math.max(docHeight - winHeight, document.documentElement.scrollHeight - winHeight)
window.addEventListener('resize', () => {
cache = new WeakMap()
})
return (currentTop, ele, useDocHeight = ele === document.body) => {
const eleHeight = ele.clientHeight
const winHeight = window.innerHeight
const cacheData = cache.get(ele)
let data = cacheData
if (
!cacheData ||
cacheData.docHeight !== eleHeight ||
cacheData.winHeight !== winHeight ||
cacheData.useDocHeight !== useDocHeight
) {
const headerHeight = ele.offsetTop
const contentMath = useDocHeight
? Math.max(eleHeight - winHeight, document.documentElement.scrollHeight - winHeight)
: Math.max(eleHeight - winHeight, 1)
data = {
docHeight: eleHeight,
winHeight,
useDocHeight,
headerHeight,
contentMath
}
cache.set(ele, data)
}
const scrollPercent = (currentTop - headerHeight) / contentMath
const scrollPercent = (currentTop - data.headerHeight) / data.contentMath
return Math.max(0, Math.min(100, Math.round(scrollPercent * 100)))
}
})(),