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:
+9
-3
@@ -622,7 +622,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const currentTop = window.scrollY || document.documentElement.scrollTop
|
||||
|
||||
if (isToc && GLOBAL_CONFIG.percent.toc) {
|
||||
$tocPercentage.textContent = btf.getScrollPercent(currentTop, $article)
|
||||
$tocPercentage.textContent = btf.getScrollPercent(currentTop, $article, false)
|
||||
}
|
||||
|
||||
if (currentTop === 0) {
|
||||
@@ -662,6 +662,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
*/
|
||||
const rightSideFn = {
|
||||
readmode: () => { // read mode
|
||||
if (document.querySelector('.exit-readmode')) return
|
||||
const $body = document.body
|
||||
const newEle = document.createElement('button')
|
||||
|
||||
@@ -677,6 +678,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
newEle.innerHTML = '<i class="fas fa-sign-out-alt"></i>'
|
||||
newEle.addEventListener('click', exitReadMode)
|
||||
$body.appendChild(newEle)
|
||||
btf.addGlobalFn('pjaxSendOnce', exitReadMode, 'exitReadMode')
|
||||
},
|
||||
darkmode: () => { // switch between light and dark mode
|
||||
const willChangeMode = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'
|
||||
@@ -1010,8 +1012,12 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
// 處理 hexo-blog-encrypt 事件
|
||||
window.addEventListener('hexo-blog-decrypt', e => {
|
||||
forPostFn()
|
||||
window.translateFn.translateInitialization()
|
||||
Object.values(window.globalFn.encrypt).forEach(fn => {
|
||||
if (window.translateFn && typeof window.translateFn.translateInitialization === 'function') {
|
||||
window.translateFn.translateInitialization()
|
||||
}
|
||||
|
||||
const encryptFn = window.globalFn && window.globalFn.encrypt ? window.globalFn.encrypt : {}
|
||||
Object.values(encryptFn).forEach(fn => {
|
||||
fn()
|
||||
})
|
||||
})
|
||||
|
||||
+4
-2
@@ -19,12 +19,14 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
return txt
|
||||
}
|
||||
|
||||
const skippedTranslateTags = ['BR', 'HR', 'SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'CODE', 'PRE']
|
||||
|
||||
const translateBody = fobj => {
|
||||
const nodes = typeof fobj === 'object' ? fobj.childNodes : document.body.childNodes
|
||||
|
||||
for (const node of nodes) {
|
||||
// Skip BR, HR tags, or the translate button object
|
||||
if (['BR', 'HR'].includes(node.tagName) || node === translateButtonObject) continue
|
||||
// Skip non-translatable tags or the translate button object
|
||||
if (skippedTranslateTags.includes(node.tagName) || node === translateButtonObject) continue
|
||||
|
||||
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
const { tagName, title, alt, placeholder, value, type } = node
|
||||
|
||||
+58
-42
@@ -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)))
|
||||
}
|
||||
})(),
|
||||
|
||||
Reference in New Issue
Block a user