mirror of
https://github.com/jerryc127/hexo-theme-butterfly.git
synced 2026-08-08 11:38:42 +08:00
improvement
This commit is contained in:
@@ -21,7 +21,7 @@ const lazyload = htmlContent => {
|
||||
// Handle src attributes with double quotes, single quotes, or no quotes (unified approach)
|
||||
// Matches: src="..." or src='...' or src=... (e.g., after minification by hexo-minify)
|
||||
return htmlContent.replace(/(<img(?![^>]*?\bdata-lazy-src=)(?:\s[^>]*?)?\ssrc=)(?:"([^"]*)"|'([^']*)'|([^\s>]+))(?![^<]*<\/script>)/gi, (match, prefix, srcDoubleQuote, srcSingleQuote, srcNoQuote) => {
|
||||
const src = srcDoubleQuote || srcSingleQuote || srcNoQuote
|
||||
const src = srcDoubleQuote ?? srcSingleQuote ?? srcNoQuote
|
||||
return `${prefix}"${bg}" data-lazy-src="${src}"`
|
||||
})
|
||||
}
|
||||
|
||||
@@ -6,33 +6,46 @@
|
||||
|
||||
hexo.extend.generator.register('post', locals => {
|
||||
const imgTestReg = /\.(png|jpe?g|gif|svg|webp|avif)(\?.*)?$/i
|
||||
const remoteImgReg = /^(?:https?:)?\/\//i
|
||||
const dataImgReg = /^data:image\//i
|
||||
|
||||
const { post_asset_folder: postAssetFolder } = hexo.config
|
||||
const { cover: { default_cover: defaultCover } } = hexo.theme.config
|
||||
|
||||
const isImage = value => {
|
||||
return typeof value === 'string' &&
|
||||
(remoteImgReg.test(value) || dataImgReg.test(value) || imgTestReg.test(value))
|
||||
}
|
||||
|
||||
function * createCoverGenerator () {
|
||||
if (!defaultCover) {
|
||||
if (!defaultCover || (Array.isArray(defaultCover) && defaultCover.length === 0)) {
|
||||
while (true) yield false
|
||||
}
|
||||
|
||||
if (!Array.isArray(defaultCover)) {
|
||||
while (true) yield defaultCover
|
||||
}
|
||||
|
||||
const coverCount = defaultCover.length
|
||||
if (coverCount === 1) {
|
||||
if (defaultCover.length === 1) {
|
||||
while (true) yield defaultCover[0]
|
||||
}
|
||||
|
||||
const coverCount = defaultCover.length
|
||||
const maxHistory = Math.min(3, coverCount - 1)
|
||||
const history = []
|
||||
|
||||
while (true) {
|
||||
let index
|
||||
|
||||
do {
|
||||
index = Math.floor(Math.random() * coverCount)
|
||||
} while (history.includes(index))
|
||||
|
||||
history.push(index)
|
||||
if (history.length > maxHistory) history.shift()
|
||||
|
||||
if (history.length > maxHistory) {
|
||||
history.shift()
|
||||
}
|
||||
|
||||
yield defaultCover[index]
|
||||
}
|
||||
@@ -40,32 +53,31 @@ hexo.extend.generator.register('post', locals => {
|
||||
|
||||
const coverGenerator = createCoverGenerator()
|
||||
|
||||
const resolvePostAsset = (value, postPath) => {
|
||||
if (
|
||||
!postAssetFolder ||
|
||||
typeof value !== 'string' ||
|
||||
value.includes('/') ||
|
||||
!imgTestReg.test(value)
|
||||
) {
|
||||
return value
|
||||
}
|
||||
|
||||
return `${postPath}${value}`
|
||||
}
|
||||
|
||||
const handleImg = data => {
|
||||
let { cover: coverVal, top_img: topImg, pagination_cover: paginationCover } = data
|
||||
data.top_img = resolvePostAsset(data.top_img, data.path)
|
||||
data.cover = resolvePostAsset(data.cover, data.path)
|
||||
data.pagination_cover = resolvePostAsset(data.pagination_cover, data.path)
|
||||
|
||||
// Add path to top_img and cover if post_asset_folder is enabled
|
||||
if (postAssetFolder) {
|
||||
if (topImg && topImg.indexOf('/') === -1 && imgTestReg.test(topImg)) {
|
||||
data.top_img = `${data.path}${topImg}`
|
||||
}
|
||||
if (coverVal && coverVal.indexOf('/') === -1 && imgTestReg.test(coverVal)) {
|
||||
data.cover = `${data.path}${coverVal}`
|
||||
}
|
||||
if (paginationCover && paginationCover.indexOf('/') === -1 && imgTestReg.test(paginationCover)) {
|
||||
data.pagination_cover = `${data.path}${paginationCover}`
|
||||
}
|
||||
if (data.cover === false) return data
|
||||
|
||||
if (!data.cover) {
|
||||
data.cover = coverGenerator.next().value
|
||||
}
|
||||
|
||||
if (coverVal === false) return data
|
||||
|
||||
// If cover is not set, use random cover
|
||||
if (!coverVal) {
|
||||
const randomCover = coverGenerator.next().value
|
||||
data.cover = randomCover
|
||||
coverVal = randomCover
|
||||
}
|
||||
|
||||
if (coverVal && (coverVal.indexOf('//') !== -1 || imgTestReg.test(coverVal))) {
|
||||
if (isImage(data.cover)) {
|
||||
data.cover_type = 'img'
|
||||
}
|
||||
|
||||
@@ -75,16 +87,23 @@ hexo.extend.generator.register('post', locals => {
|
||||
const posts = locals.posts.sort('date').toArray()
|
||||
const { length } = posts
|
||||
|
||||
return posts.map((post, i) => {
|
||||
if (i) post.prev = posts[i - 1]
|
||||
if (i < length - 1) post.next = posts[i + 1]
|
||||
return posts.map((post, index) => {
|
||||
const data = post
|
||||
|
||||
post.__post = true
|
||||
if (index > 0) {
|
||||
data.prev = posts[index - 1]
|
||||
}
|
||||
|
||||
if (index < length - 1) {
|
||||
data.next = posts[index + 1]
|
||||
}
|
||||
|
||||
data.__post = true
|
||||
|
||||
return {
|
||||
data: handleImg(post),
|
||||
data: handleImg(data),
|
||||
layout: 'post',
|
||||
path: post.path
|
||||
path: data.path
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -11,46 +11,87 @@ hexo.extend.helper.register('inject_head_js', function () {
|
||||
const createCustomJs = () => `
|
||||
const saveToLocal = {
|
||||
set: (key, value, ttl) => {
|
||||
if (!ttl) return
|
||||
const expiry = Date.now() + ttl * 86400000
|
||||
localStorage.setItem(key, JSON.stringify({ value, expiry }))
|
||||
const data = { value }
|
||||
|
||||
if (ttl != null) {
|
||||
data.expiry = Date.now() + ttl * 86400000
|
||||
}
|
||||
|
||||
localStorage.setItem(key, JSON.stringify(data))
|
||||
},
|
||||
get: key => {
|
||||
const itemStr = localStorage.getItem(key)
|
||||
if (!itemStr) return undefined
|
||||
const { value, expiry } = JSON.parse(itemStr)
|
||||
if (Date.now() > expiry) {
|
||||
if (!itemStr) return
|
||||
|
||||
try {
|
||||
const data = JSON.parse(itemStr)
|
||||
|
||||
if (data.expiry && Date.now() > data.expiry) {
|
||||
localStorage.removeItem(key)
|
||||
return
|
||||
}
|
||||
|
||||
return data.value
|
||||
} catch {
|
||||
localStorage.removeItem(key)
|
||||
return undefined
|
||||
}
|
||||
return value
|
||||
}
|
||||
}
|
||||
|
||||
const scriptCache = new Map()
|
||||
const cssCache = new Map()
|
||||
window.btf = {
|
||||
saveToLocal,
|
||||
getScript: (url, attr = {}) => new Promise((resolve, reject) => {
|
||||
const script = document.createElement('script')
|
||||
script.src = url
|
||||
script.async = true
|
||||
Object.entries(attr).forEach(([key, val]) => script.setAttribute(key, val))
|
||||
script.onload = script.onreadystatechange = () => {
|
||||
if (!script.readyState || /loaded|complete/.test(script.readyState)) resolve()
|
||||
getScript: (url, attr = {}) => {
|
||||
if (scriptCache.has(url)) {
|
||||
return scriptCache.get(url)
|
||||
}
|
||||
script.onerror = reject
|
||||
document.head.appendChild(script)
|
||||
}),
|
||||
getCSS: (url, id) => new Promise((resolve, reject) => {
|
||||
const link = document.createElement('link')
|
||||
link.rel = 'stylesheet'
|
||||
link.href = url
|
||||
if (id) link.id = id
|
||||
link.onload = link.onreadystatechange = () => {
|
||||
if (!link.readyState || /loaded|complete/.test(link.readyState)) resolve()
|
||||
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const script = document.createElement('script')
|
||||
|
||||
script.src = url
|
||||
script.async = true
|
||||
|
||||
for (const key in attr) {
|
||||
script.setAttribute(key, attr[key])
|
||||
}
|
||||
|
||||
script.onload = resolve
|
||||
script.onerror = reject
|
||||
|
||||
document.head.appendChild(script)
|
||||
})
|
||||
|
||||
scriptCache.set(url, promise)
|
||||
|
||||
return promise
|
||||
},
|
||||
getCSS: (url, id) => {
|
||||
if (cssCache.has(url)) {
|
||||
return cssCache.get(url)
|
||||
}
|
||||
link.onerror = reject
|
||||
document.head.appendChild(link)
|
||||
}),
|
||||
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const link = document.createElement('link')
|
||||
|
||||
link.rel = 'stylesheet'
|
||||
link.href = url
|
||||
|
||||
if (id) {
|
||||
link.id = id
|
||||
}
|
||||
|
||||
link.onload = resolve
|
||||
link.onerror = reject
|
||||
|
||||
document.head.appendChild(link)
|
||||
})
|
||||
|
||||
cssCache.set(url, promise)
|
||||
|
||||
return promise
|
||||
},
|
||||
addGlobalFn: (key, fn, name = false, parent = window) => {
|
||||
if (!${pjax.enable} && key.startsWith('pjax')) return
|
||||
const globalFn = parent.globalFn || {}
|
||||
@@ -65,16 +106,17 @@ hexo.extend.helper.register('inject_head_js', function () {
|
||||
if (!darkmode.enable) return ''
|
||||
|
||||
let darkmodeJs = `
|
||||
const metaThemeColor = document.querySelector('meta[name="theme-color"]')
|
||||
const activateDarkMode = () => {
|
||||
document.documentElement.setAttribute('data-theme', 'dark')
|
||||
if (document.querySelector('meta[name="theme-color"]') !== null) {
|
||||
document.querySelector('meta[name="theme-color"]').setAttribute('content', '${themeColorDark}')
|
||||
document.documentElement.dataset.theme = 'dark'
|
||||
if (metaThemeColor !== null) {
|
||||
metaThemeColor.setAttribute('content', '${themeColorDark}')
|
||||
}
|
||||
}
|
||||
const activateLightMode = () => {
|
||||
document.documentElement.setAttribute('data-theme', 'light')
|
||||
if (document.querySelector('meta[name="theme-color"]') !== null) {
|
||||
document.querySelector('meta[name="theme-color"]').setAttribute('content', '${themeColorLight}')
|
||||
document.documentElement.dataset.theme = 'light'
|
||||
if (metaThemeColor !== null) {
|
||||
metaThemeColor.setAttribute('content', '${themeColorLight}')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,10 +137,15 @@ hexo.extend.helper.register('inject_head_js', function () {
|
||||
else if (mediaQueryDark.matches) activateDarkMode()
|
||||
else {
|
||||
const hour = new Date().getHours()
|
||||
const isNight = hour <= ${start} || hour >= ${end}
|
||||
const start = ${start}
|
||||
const end = ${end}
|
||||
const isNight =
|
||||
start < end
|
||||
? hour < start || hour >= end
|
||||
: hour >= start || hour < end
|
||||
isNight ? activateDarkMode() : activateLightMode()
|
||||
}
|
||||
mediaQueryDark.addEventListener('change', () => {
|
||||
mediaQueryDark.addEventListener('change', e => {
|
||||
if (saveToLocal.get('theme') === undefined) {
|
||||
e.matches ? activateDarkMode() : activateLightMode()
|
||||
}
|
||||
@@ -111,7 +158,12 @@ hexo.extend.helper.register('inject_head_js', function () {
|
||||
case 2:
|
||||
darkmodeJs += `
|
||||
const hour = new Date().getHours()
|
||||
const isNight = hour <= ${start} || hour >= ${end}
|
||||
const start = ${start}
|
||||
const end = ${end}
|
||||
const isNight =
|
||||
start < end
|
||||
? hour < start || hour >= end
|
||||
: hour >= start || hour < end
|
||||
if (theme === undefined) isNight ? activateDarkMode() : activateLightMode()
|
||||
else theme === 'light' ? activateLightMode() : activateDarkMode()
|
||||
`
|
||||
|
||||
Reference in New Issue
Block a user