update theme to butterfly
This commit is contained in:
2
themes/butterfly/.gitignore
vendored
Normal file
2
themes/butterfly/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules/
|
||||||
@@ -91,7 +91,8 @@ category_per_img:
|
|||||||
footer_img: false
|
footer_img: false
|
||||||
|
|
||||||
# Website Background
|
# Website Background
|
||||||
# Can set it to color or image url
|
# Can set it to color, image URL or an array containing colors and/or image URLs
|
||||||
|
# If an array is provided, a random background will be selected from the array on each load
|
||||||
background:
|
background:
|
||||||
|
|
||||||
cover:
|
cover:
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ div
|
|||||||
!= partial("includes/third-party/umami_analytics", {}, { cache: true })
|
!= partial("includes/third-party/umami_analytics", {}, { cache: true })
|
||||||
|
|
||||||
if theme.busuanzi.site_uv || theme.busuanzi.site_pv || theme.busuanzi.page_pv
|
if theme.busuanzi.site_uv || theme.busuanzi.site_pv || theme.busuanzi.page_pv
|
||||||
script(async data-pjax src= theme.asset.busuanzi || '//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js')
|
script(async data-pjax src=theme.asset.busuanzi ? url_for(theme.asset.busuanzi) : '//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js')
|
||||||
|
|
||||||
!= partial('includes/third-party/search/index', {}, { cache: true })
|
!= partial('includes/third-party/search/index', {}, { cache: true })
|
||||||
|
|
||||||
|
|||||||
@@ -13,9 +13,32 @@ html(lang=config.language data-theme=theme.display_mode class=htmlClassHideAside
|
|||||||
!=partial('includes/loading/index', {}, {cache: true})
|
!=partial('includes/loading/index', {}, {cache: true})
|
||||||
|
|
||||||
if theme.background
|
if theme.background
|
||||||
#web_bg(style=getBgPath(theme.background))
|
if !Array.isArray(theme.background)
|
||||||
|
#web_bg.bg-animation(style=getBgPath(theme.background))
|
||||||
|
else
|
||||||
|
#web_bg.bg-animation
|
||||||
|
- const bgStyleArr = theme.background.map(getBgPath)
|
||||||
|
script.
|
||||||
|
(() => {
|
||||||
|
const arr = !{JSON.stringify(bgStyleArr)}
|
||||||
|
const webBgDiv = document.getElementById('web_bg')
|
||||||
|
|
||||||
|
const setRandomBg = () => {
|
||||||
|
webBgDiv.style = arr[Math.floor(Math.random() * arr.length)]
|
||||||
|
requestAnimationFrame(() => webBgDiv.classList.add('bg-animation'))
|
||||||
|
}
|
||||||
|
|
||||||
|
document.addEventListener('pjax:send', () => {
|
||||||
|
webBgDiv.style = ''
|
||||||
|
webBgDiv.classList.remove('bg-animation')
|
||||||
|
})
|
||||||
|
|
||||||
|
document.addEventListener('pjax:complete', setRandomBg)
|
||||||
|
document.addEventListener('DOMContentLoaded', setRandomBg)
|
||||||
|
})()
|
||||||
|
|
||||||
!=partial('includes/sidebar', {}, {cache: true})
|
!=partial('includes/sidebar', {}, {cache: true})
|
||||||
|
|
||||||
#body-wrap(class=pageType)
|
#body-wrap(class=pageType)
|
||||||
include ./header/index.pug
|
include ./header/index.pug
|
||||||
|
|
||||||
@@ -34,4 +57,3 @@ html(lang=config.language data-theme=theme.display_mode class=htmlClassHideAside
|
|||||||
|
|
||||||
include ./rightside.pug
|
include ./rightside.pug
|
||||||
include ./additional-js.pug
|
include ./additional-js.pug
|
||||||
!=partial('includes/rightmenu',{}, {cache:true})
|
|
||||||
@@ -49,6 +49,7 @@
|
|||||||
if !page.shuoshuo_url
|
if !page.shuoshuo_url
|
||||||
script(type='application/json' id='shuoshuo-data')!= safeJSON(localDate)
|
script(type='application/json' id='shuoshuo-data')!= safeJSON(localDate)
|
||||||
|
|
||||||
|
- const { enable, native, placeholder, field } = theme.lazyload
|
||||||
script.
|
script.
|
||||||
(() => {
|
(() => {
|
||||||
const limitConfig = !{ JSON.stringify(page.limit || {}) }
|
const limitConfig = !{ JSON.stringify(page.limit || {}) }
|
||||||
@@ -84,6 +85,36 @@
|
|||||||
return `${year}-${month}-${day} ${hour}:${minute}:${second}`
|
return `${year}-${month}-${day} ${hour}:${minute}:${second}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const addLazyload = str => {
|
||||||
|
const config = {
|
||||||
|
enable: !{Boolean(enable)},
|
||||||
|
native: !{Boolean(native)},
|
||||||
|
field: '!{field}',
|
||||||
|
placeholder: '!{url_for(placeholder)}',
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.enable || config.field !== 'site') return str
|
||||||
|
const parser = new DOMParser()
|
||||||
|
const doc = parser.parseFromString(str, 'text/html')
|
||||||
|
const images = doc.querySelectorAll('img')
|
||||||
|
|
||||||
|
images.forEach(img => {
|
||||||
|
if (config.native) {
|
||||||
|
img.setAttribute('loading', 'lazy')
|
||||||
|
} else {
|
||||||
|
const src = img.getAttribute('src')
|
||||||
|
img.setAttribute('data-lazy-src', src)
|
||||||
|
|
||||||
|
if (config.placeholder) {
|
||||||
|
img.setAttribute('src', config.placeholder)
|
||||||
|
} else {
|
||||||
|
img.removeAttribute('src')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return doc.body.innerHTML
|
||||||
|
}
|
||||||
|
|
||||||
let currentPage = 1
|
let currentPage = 1
|
||||||
const itemsPerPage = 8
|
const itemsPerPage = 8
|
||||||
let totalPages = 0
|
let totalPages = 0
|
||||||
@@ -117,7 +148,7 @@
|
|||||||
</time>
|
</time>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="shuoshuo-content">${item.content}</div>
|
<div class="shuoshuo-content">${addLazyload(item.content)}</div>
|
||||||
<div class="shuoshuo-footer ${tags ? 'flex-between' : 'flex-end'}">
|
<div class="shuoshuo-footer ${tags ? 'flex-between' : 'flex-end'}">
|
||||||
${tags ? `<div class="shuoshuo-tags">${tags}</div>` : ''}
|
${tags ? `<div class="shuoshuo-tags">${tags}</div>` : ''}
|
||||||
${commentButton}
|
${commentButton}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ if page.total !== 1
|
|||||||
|
|
||||||
a.pagination-related(class=className href=url_for(direction.path) title=direction.title)
|
a.pagination-related(class=className href=url_for(direction.path) title=direction.title)
|
||||||
if direction.cover_type === 'img'
|
if direction.cover_type === 'img'
|
||||||
img.cover(src=url_for(direction.cover) onerror=`onerror=null;src='${url_for(theme.error_img.post_page)}'` alt=`cover of ${key === 'prev' ? 'previous' : 'next'} post`)
|
img.cover(src=url_for(direction.pagination_cover || direction.cover) onerror=`onerror=null;src='${url_for(theme.error_img.post_page)}'` alt=`cover of ${key === 'prev' ? 'previous' : 'next'} post`)
|
||||||
else
|
else
|
||||||
.cover(style=`background: ${direction.cover || 'var(--default-bg-color)'}`)
|
.cover(style=`background: ${direction.cover || 'var(--default-bg-color)'}`)
|
||||||
|
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ script.
|
|||||||
const loadArtalk = async (el, pageKey) => {
|
const loadArtalk = async (el, pageKey) => {
|
||||||
if (typeof Artalk === 'object') initArtalk(el, pageKey)
|
if (typeof Artalk === 'object') initArtalk(el, pageKey)
|
||||||
else {
|
else {
|
||||||
await btf.getCSS('!{theme.asset.artalk_css}')
|
await btf.getCSS('!{url_for(theme.asset.artalk_css)}')
|
||||||
await btf.getScript('!{theme.asset.artalk_js}')
|
await btf.getScript('!{url_for(theme.asset.artalk_js)}')
|
||||||
initArtalk(el, pageKey)
|
initArtalk(el, pageKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,11 @@ script.
|
|||||||
|
|
||||||
ele.forEach((item, index) => {
|
ele.forEach((item, index) => {
|
||||||
const mermaidSrc = item.firstElementChild
|
const mermaidSrc = item.firstElementChild
|
||||||
const mermaidThemeConfig = `%%{init:{ 'theme':'${theme}'}}%%\n`
|
const config = mermaidSrc.dataset.config ? JSON.parse(mermaidSrc.dataset.config) : {}
|
||||||
|
if (!config.theme) {
|
||||||
|
config.theme = theme
|
||||||
|
}
|
||||||
|
const mermaidThemeConfig = `%%{init: ${JSON.stringify(config)}}%%\n`
|
||||||
const mermaidID = `mermaid-${index}`
|
const mermaidID = `mermaid-${index}`
|
||||||
const mermaidDefinition = mermaidThemeConfig + mermaidSrc.textContent
|
const mermaidDefinition = mermaidThemeConfig + mermaidSrc.textContent
|
||||||
|
|
||||||
|
|||||||
@@ -14,9 +14,9 @@ if choose
|
|||||||
else
|
else
|
||||||
- pjaxSelectors.unshift('meta[name="description"]')
|
- pjaxSelectors.unshift('meta[name="description"]')
|
||||||
|
|
||||||
script(src=url_for(theme.asset.pjax))
|
script(src=url_for(theme.asset.pjax) defer)
|
||||||
script.
|
script.
|
||||||
(() => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
const pjaxSelectors = !{JSON.stringify(pjaxSelectors)}
|
const pjaxSelectors = !{JSON.stringify(pjaxSelectors)}
|
||||||
|
|
||||||
window.pjax = new Pjax({
|
window.pjax = new Pjax({
|
||||||
@@ -65,10 +65,9 @@ script.
|
|||||||
|
|
||||||
document.addEventListener('pjax:error', e => {
|
document.addEventListener('pjax:error', e => {
|
||||||
if (e.request.status === 404) {
|
if (e.request.status === 404) {
|
||||||
const usePjax = !{theme.pjax && theme.pjax.enable}
|
|
||||||
!{theme.error_404 && theme.error_404.enable}
|
!{theme.error_404 && theme.error_404.enable}
|
||||||
? (usePjax ? pjax.loadUrl('!{url_for("/404.html")}') : window.location.href = '!{url_for("/404.html")}')
|
? pjax.loadUrl('!{url_for("/404.html")}')
|
||||||
: window.location.href = e.request.responseURL
|
: window.location.href = e.request.responseURL
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})()
|
})
|
||||||
@@ -17,7 +17,7 @@ if (syntax_highlighter === 'prismjs' || enable) && !preprocess
|
|||||||
btf.addGlobalFn('encrypt', highlightAll, 'prismjs')
|
btf.addGlobalFn('encrypt', highlightAll, 'prismjs')
|
||||||
})()
|
})()
|
||||||
|
|
||||||
script(src=url_for(prismjs_js))
|
script(src=url_for(prismjs_js) defer)
|
||||||
script(src=url_for(prismjs_autoloader))
|
script(src=url_for(prismjs_autoloader) defer)
|
||||||
if (line_number)
|
if (line_number)
|
||||||
script(src=url_for(prismjs_lineNumber_js))
|
script(src=url_for(prismjs_lineNumber_js) defer)
|
||||||
@@ -31,7 +31,7 @@ script.
|
|||||||
const getData = async (isPost) => {
|
const getData = async (isPost) => {
|
||||||
try {
|
try {
|
||||||
const now = Date.now()
|
const now = Date.now()
|
||||||
const keyUrl = isPost ? `&url=${window.location.pathname}` : ''
|
const keyUrl = isPost ? `&url=${window.location.pathname}&path=${window.location.pathname}` : ''
|
||||||
const headerList = { 'Accept': 'application/json' }
|
const headerList = { 'Accept': 'application/json' }
|
||||||
|
|
||||||
if (!{isServerURL}) {
|
if (!{isServerURL}) {
|
||||||
@@ -62,8 +62,8 @@ script.
|
|||||||
const pagePV = document.getElementById('umamiPV')
|
const pagePV = document.getElementById('umamiPV')
|
||||||
if (pagePV) {
|
if (pagePV) {
|
||||||
const data = await getData(true)
|
const data = await getData(true)
|
||||||
if (data && data.pageviews && typeof data.pageviews.value !== 'undefined') {
|
if (data && data.pageviews) {
|
||||||
pagePV.textContent = data.pageviews.value
|
pagePV.textContent = typeof data.pageviews.value !== 'undefined' ? data.pageviews.value : data.pageviews
|
||||||
} else {
|
} else {
|
||||||
console.warn('Umami Analytics: Invalid page view data received')
|
console.warn('Umami Analytics: Invalid page view data received')
|
||||||
}
|
}
|
||||||
@@ -75,8 +75,8 @@ script.
|
|||||||
|
|
||||||
if (config.site_uv) {
|
if (config.site_uv) {
|
||||||
const siteUV = document.getElementById('umami-site-uv')
|
const siteUV = document.getElementById('umami-site-uv')
|
||||||
if (siteUV && data && data.visitors && typeof data.visitors.value !== 'undefined') {
|
if (siteUV && data && data.visitors) {
|
||||||
siteUV.textContent = data.visitors.value
|
siteUV.textContent = typeof data.visitors.value !== 'undefined' ? data.visitors.value : data.visitors
|
||||||
} else if (siteUV) {
|
} else if (siteUV) {
|
||||||
console.warn('Umami Analytics: Invalid site UV data received')
|
console.warn('Umami Analytics: Invalid site UV data received')
|
||||||
}
|
}
|
||||||
@@ -84,8 +84,8 @@ script.
|
|||||||
|
|
||||||
if (config.site_pv) {
|
if (config.site_pv) {
|
||||||
const sitePV = document.getElementById('umami-site-pv')
|
const sitePV = document.getElementById('umami-site-pv')
|
||||||
if (sitePV && data && data.pageviews && typeof data.pageviews.value !== 'undefined') {
|
if (sitePV && data && data.pageviews) {
|
||||||
sitePV.textContent = data.pageviews.value
|
sitePV.textContent = typeof data.pageviews.value !== 'undefined' ? data.pageviews.value : data.pageviews
|
||||||
} else if (sitePV) {
|
} else if (sitePV) {
|
||||||
console.warn('Umami Analytics: Invalid site PV data received')
|
console.warn('Umami Analytics: Invalid site PV data received')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hexo-theme-butterfly",
|
"name": "hexo-theme-butterfly",
|
||||||
"version": "5.5.1",
|
"version": "5.5.3",
|
||||||
"description": "A Simple and Card UI Design theme for Hexo",
|
"description": "A Simple and Card UI Design theme for Hexo",
|
||||||
"main": "package.json",
|
"main": "package.json",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hexo-renderer-pug": "^3.0.0",
|
"hexo-renderer-pug": "^3.0.0",
|
||||||
"hexo-renderer-stylus": "^3.0.1",
|
"hexo-renderer-stylus": "^3.0.1",
|
||||||
"hexo-util": "^3.3.0",
|
"hexo-util": "^4.0.0",
|
||||||
"moment-timezone": "^0.6.0"
|
"moment-timezone": "^0.6.0"
|
||||||
},
|
},
|
||||||
"homepage": "https://butterfly.js.org/",
|
"homepage": "https://butterfly.js.org/",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ activate_power_mode:
|
|||||||
algolia_search:
|
algolia_search:
|
||||||
name: algoliasearch
|
name: algoliasearch
|
||||||
file: dist/lite/builds/browser.umd.js
|
file: dist/lite/builds/browser.umd.js
|
||||||
version: 5.39.0
|
version: 5.46.0
|
||||||
aplayer_css:
|
aplayer_css:
|
||||||
name: aplayer
|
name: aplayer
|
||||||
file: dist/APlayer.min.css
|
file: dist/APlayer.min.css
|
||||||
@@ -45,7 +45,7 @@ canvas_ribbon:
|
|||||||
chartjs:
|
chartjs:
|
||||||
name: chart.js
|
name: chart.js
|
||||||
file: dist/chart.umd.js
|
file: dist/chart.umd.js
|
||||||
version: 4.5.0
|
version: 4.5.1
|
||||||
clickShowText:
|
clickShowText:
|
||||||
name: butterfly-extsrc
|
name: butterfly-extsrc
|
||||||
file: dist/click-show-text.min.js
|
file: dist/click-show-text.min.js
|
||||||
@@ -57,21 +57,21 @@ click_heart:
|
|||||||
disqusjs:
|
disqusjs:
|
||||||
name: disqusjs
|
name: disqusjs
|
||||||
file: dist/browser/disqusjs.es2015.umd.min.js
|
file: dist/browser/disqusjs.es2015.umd.min.js
|
||||||
version: 3.1.1
|
version: 3.2.1
|
||||||
disqusjs_css:
|
disqusjs_css:
|
||||||
name: disqusjs
|
name: disqusjs
|
||||||
file: dist/browser/styles/disqusjs.css
|
file: dist/browser/styles/disqusjs.css
|
||||||
version: 3.1.1
|
version: 3.2.1
|
||||||
docsearch_css:
|
docsearch_css:
|
||||||
name: '@docsearch/css'
|
name: '@docsearch/css'
|
||||||
other_name: docsearch-css
|
other_name: docsearch-css
|
||||||
file: dist/style.css
|
file: dist/style.css
|
||||||
version: 4.1.0
|
version: 4.3.2
|
||||||
docsearch_js:
|
docsearch_js:
|
||||||
name: '@docsearch/js'
|
name: '@docsearch/js'
|
||||||
other_name: docsearch-js
|
other_name: docsearch-js
|
||||||
file: dist/umd/index.js
|
file: dist/umd/index.js
|
||||||
version: 4.1.0
|
version: 4.3.2
|
||||||
egjs_infinitegrid:
|
egjs_infinitegrid:
|
||||||
name: '@egjs/infinitegrid'
|
name: '@egjs/infinitegrid'
|
||||||
other_name: egjs-infinitegrid
|
other_name: egjs-infinitegrid
|
||||||
@@ -80,12 +80,12 @@ egjs_infinitegrid:
|
|||||||
fancybox:
|
fancybox:
|
||||||
name: '@fancyapps/ui'
|
name: '@fancyapps/ui'
|
||||||
file: dist/fancybox/fancybox.umd.js
|
file: dist/fancybox/fancybox.umd.js
|
||||||
version: 6.0.33
|
version: 6.1.7
|
||||||
other_name: fancyapps-ui
|
other_name: fancyapps-ui
|
||||||
fancybox_css:
|
fancybox_css:
|
||||||
name: '@fancyapps/ui'
|
name: '@fancyapps/ui'
|
||||||
file: dist/fancybox/fancybox.css
|
file: dist/fancybox/fancybox.css
|
||||||
version: 6.0.33
|
version: 6.1.7
|
||||||
other_name: fancyapps-ui
|
other_name: fancyapps-ui
|
||||||
fireworks:
|
fireworks:
|
||||||
name: butterfly-extsrc
|
name: butterfly-extsrc
|
||||||
@@ -112,12 +112,12 @@ katex:
|
|||||||
name: katex
|
name: katex
|
||||||
file: dist/katex.min.css
|
file: dist/katex.min.css
|
||||||
other_name: KaTeX
|
other_name: KaTeX
|
||||||
version: 0.16.22
|
version: 0.16.27
|
||||||
katex_copytex:
|
katex_copytex:
|
||||||
name: katex
|
name: katex
|
||||||
file: dist/contrib/copy-tex.min.js
|
file: dist/contrib/copy-tex.min.js
|
||||||
other_name: KaTeX
|
other_name: KaTeX
|
||||||
version: 0.16.22
|
version: 0.16.27
|
||||||
lazyload:
|
lazyload:
|
||||||
name: vanilla-lazyload
|
name: vanilla-lazyload
|
||||||
file: dist/lazyload.iife.min.js
|
file: dist/lazyload.iife.min.js
|
||||||
@@ -133,7 +133,7 @@ medium_zoom:
|
|||||||
mermaid:
|
mermaid:
|
||||||
name: mermaid
|
name: mermaid
|
||||||
file: dist/mermaid.min.js
|
file: dist/mermaid.min.js
|
||||||
version: 11.12.0
|
version: 11.12.2
|
||||||
meting_js:
|
meting_js:
|
||||||
name: butterfly-extsrc
|
name: butterfly-extsrc
|
||||||
file: metingjs/dist/Meting.min.js
|
file: metingjs/dist/Meting.min.js
|
||||||
@@ -199,9 +199,9 @@ waline_css:
|
|||||||
name: '@waline/client'
|
name: '@waline/client'
|
||||||
file: dist/waline.css
|
file: dist/waline.css
|
||||||
other_name: waline
|
other_name: waline
|
||||||
version: 3.6.0
|
version: 3.8.0
|
||||||
waline_js:
|
waline_js:
|
||||||
name: '@waline/client'
|
name: '@waline/client'
|
||||||
file: dist/waline.js
|
file: dist/waline.js
|
||||||
other_name: waline
|
other_name: waline
|
||||||
version: 3.6.0
|
version: 3.8.0
|
||||||
|
|||||||
@@ -46,45 +46,43 @@ hexo.extend.filter.register('before_generate', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const createCDNLink = (data, type, cond = '') => {
|
const createCDNLink = (data, type, cond = '') => {
|
||||||
Object.keys(data).forEach(key => {
|
return Object.keys(data).reduce((result, key) => {
|
||||||
let { name, version, file, other_name } = data[key]
|
let { name, version, file, other_name: otherName } = data[key]
|
||||||
const cdnjs_name = other_name || name
|
const cdnjsName = otherName || name
|
||||||
const cdnjs_file = file.replace(/^[lib|dist]*\/|browser\//g, '')
|
const cdnjsFile = file.replace(/^[lib|dist]*\/|browser\//g, '')
|
||||||
const min_cdnjs_file = minFile(cdnjs_file)
|
const minCdnjsFile = minFile(cdnjsFile)
|
||||||
if (cond === 'internal') file = `source/${file}`
|
if (cond === 'internal') file = `source/${file}`
|
||||||
const min_file = minFile(file)
|
const minFilePath = minFile(file)
|
||||||
const verType = CDN.version ? (type === 'local' ? `?v=${version}` : `@${version}`) : ''
|
const verType = CDN.version ? (type === 'local' ? `?v=${version}` : `@${version}`) : ''
|
||||||
|
|
||||||
const value = {
|
const value = {
|
||||||
version,
|
version,
|
||||||
name,
|
name,
|
||||||
file,
|
file,
|
||||||
cdnjs_file,
|
cdnjs_file: cdnjsFile,
|
||||||
min_file,
|
min_file: minFilePath,
|
||||||
min_cdnjs_file,
|
min_cdnjs_file: minCdnjsFile,
|
||||||
cdnjs_name
|
cdnjs_name: cdnjsName
|
||||||
}
|
}
|
||||||
|
|
||||||
const cdnSource = {
|
const cdnSource = {
|
||||||
local: cond === 'internal' ? `${cdnjs_file + verType}` : `/pluginsSrc/${name}/${file + verType}`,
|
local: cond === 'internal' ? `${cdnjsFile + verType}` : `/pluginsSrc/${name}/${file + verType}`,
|
||||||
jsdelivr: `https://cdn.jsdelivr.net/npm/${name}${verType}/${min_file}`,
|
jsdelivr: `https://cdn.jsdelivr.net/npm/${name}${verType}/${minFilePath}`,
|
||||||
unpkg: `https://unpkg.com/${name}${verType}/${file}`,
|
unpkg: `https://unpkg.com/${name}${verType}/${file}`,
|
||||||
cdnjs: `https://cdnjs.cloudflare.com/ajax/libs/${cdnjs_name}/${version}/${min_cdnjs_file}`,
|
cdnjs: `https://cdnjs.cloudflare.com/ajax/libs/${cdnjsName}/${version}/${minCdnjsFile}`,
|
||||||
custom: (CDN.custom_format || '').replace(/\$\{(.+?)\}/g, (match, $1) => value[$1])
|
custom: (CDN.custom_format || '').replace(/\$\{(.+?)\}/g, (match, $1) => value[$1])
|
||||||
}
|
}
|
||||||
|
|
||||||
data[key] = cdnSource[type]
|
result[key] = cdnSource[type]
|
||||||
})
|
return result
|
||||||
|
}, cond === 'internal' ? { main_css: 'css/index.css' + (CDN.version ? `?v=${version}` : '') } : {})
|
||||||
if (cond === 'internal') data.main_css = 'css/index.css' + (CDN.version ? `?v=${version}` : '')
|
|
||||||
return data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// delete null value
|
// delete null value
|
||||||
const deleteNullValue = obj => {
|
const deleteNullValue = obj => {
|
||||||
if (!obj) return
|
if (!obj) return {}
|
||||||
for (const i in obj) {
|
for (const i in obj) {
|
||||||
obj[i] === null && delete obj[i]
|
if (obj[i] === null) delete obj[i]
|
||||||
}
|
}
|
||||||
return obj
|
return obj
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
/**
|
|
||||||
* Capitalize the first letter of comment name
|
|
||||||
*/
|
|
||||||
|
|
||||||
hexo.extend.filter.register('before_generate', () => {
|
|
||||||
const themeConfig = hexo.theme.config
|
|
||||||
let { use } = themeConfig.comments
|
|
||||||
if (!use) return
|
|
||||||
|
|
||||||
// Make sure use is an array
|
|
||||||
use = Array.isArray(use) ? use : use.split(',')
|
|
||||||
|
|
||||||
// Capitalize the first letter of each comment name
|
|
||||||
use = use.map(item =>
|
|
||||||
item.trim().toLowerCase().replace(/\b[a-z]/g, s => s.toUpperCase())
|
|
||||||
)
|
|
||||||
|
|
||||||
// Disqus and Disqusjs conflict, only keep the first one
|
|
||||||
if (use.includes('Disqus') && use.includes('Disqusjs')) {
|
|
||||||
use = [use[0]]
|
|
||||||
}
|
|
||||||
|
|
||||||
themeConfig.comments.use = use
|
|
||||||
})
|
|
||||||
@@ -17,7 +17,7 @@ function checkHexoEnvironment (hexo) {
|
|||||||
if (major < requiredMajor || (major === requiredMajor && minor < requiredMinor)) {
|
if (major < requiredMajor || (major === requiredMajor && minor < requiredMinor)) {
|
||||||
log.error('Please update Hexo to V5.3.0 or higher!')
|
log.error('Please update Hexo to V5.3.0 or higher!')
|
||||||
log.error('請把 Hexo 升級到 V5.3.0 或更高的版本!')
|
log.error('請把 Hexo 升級到 V5.3.0 或更高的版本!')
|
||||||
process.exit(-1)
|
throw new Error('Hexo version too old')
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for deprecated configuration file
|
// Check for deprecated configuration file
|
||||||
@@ -26,7 +26,7 @@ function checkHexoEnvironment (hexo) {
|
|||||||
if (data && data.butterfly) {
|
if (data && data.butterfly) {
|
||||||
log.error("'butterfly.yml' is deprecated. Please use '_config.butterfly.yml'")
|
log.error("'butterfly.yml' is deprecated. Please use '_config.butterfly.yml'")
|
||||||
log.error("'butterfly.yml' 已經棄用,請使用 '_config.butterfly.yml'")
|
log.error("'butterfly.yml' 已經棄用,請使用 '_config.butterfly.yml'")
|
||||||
process.exit(-1)
|
throw new Error('Deprecated configuration file')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,593 +0,0 @@
|
|||||||
const { deepMerge } = require('hexo-util')
|
|
||||||
|
|
||||||
hexo.extend.filter.register('before_generate', () => {
|
|
||||||
const defaultConfig = {
|
|
||||||
nav: {
|
|
||||||
logo: null,
|
|
||||||
display_title: true,
|
|
||||||
display_post_title: true,
|
|
||||||
fixed: false
|
|
||||||
},
|
|
||||||
menu: null,
|
|
||||||
code_blocks: {
|
|
||||||
theme: 'light',
|
|
||||||
macStyle: false,
|
|
||||||
height_limit: false,
|
|
||||||
word_wrap: false,
|
|
||||||
copy: true,
|
|
||||||
language: true,
|
|
||||||
shrink: false,
|
|
||||||
fullpage: false
|
|
||||||
},
|
|
||||||
social: null,
|
|
||||||
favicon: '/img/favicon.png',
|
|
||||||
avatar: {
|
|
||||||
img: '/img/butterfly-icon.png',
|
|
||||||
effect: false
|
|
||||||
},
|
|
||||||
disable_top_img: false,
|
|
||||||
default_top_img: null,
|
|
||||||
index_img: null,
|
|
||||||
archive_img: null,
|
|
||||||
tag_img: null,
|
|
||||||
tag_per_img: null,
|
|
||||||
category_img: null,
|
|
||||||
category_per_img: null,
|
|
||||||
footer_img: false,
|
|
||||||
background: null,
|
|
||||||
cover: {
|
|
||||||
index_enable: true,
|
|
||||||
aside_enable: true,
|
|
||||||
archives_enable: true,
|
|
||||||
default_cover: null
|
|
||||||
},
|
|
||||||
error_img: {
|
|
||||||
flink: '/img/friend_404.gif',
|
|
||||||
post_page: '/img/404.jpg'
|
|
||||||
},
|
|
||||||
error_404: {
|
|
||||||
enable: false,
|
|
||||||
subtitle: 'Page Not Found',
|
|
||||||
background: '/img/error-page.png'
|
|
||||||
},
|
|
||||||
post_meta: {
|
|
||||||
page: {
|
|
||||||
date_type: 'created',
|
|
||||||
date_format: 'date',
|
|
||||||
categories: true,
|
|
||||||
tags: false,
|
|
||||||
label: true
|
|
||||||
},
|
|
||||||
post: {
|
|
||||||
position: 'left',
|
|
||||||
date_type: 'both',
|
|
||||||
date_format: 'date',
|
|
||||||
categories: true,
|
|
||||||
tags: true,
|
|
||||||
label: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
index_site_info_top: null,
|
|
||||||
index_top_img_height: null,
|
|
||||||
subtitle: {
|
|
||||||
enable: false,
|
|
||||||
effect: true,
|
|
||||||
typed_option: null,
|
|
||||||
source: false,
|
|
||||||
sub: null
|
|
||||||
},
|
|
||||||
index_layout: 3,
|
|
||||||
index_post_content: {
|
|
||||||
method: 3,
|
|
||||||
length: 500
|
|
||||||
},
|
|
||||||
toc: {
|
|
||||||
post: true,
|
|
||||||
page: false,
|
|
||||||
number: true,
|
|
||||||
expand: false,
|
|
||||||
style_simple: false,
|
|
||||||
scroll_percent: true
|
|
||||||
},
|
|
||||||
post_copyright: {
|
|
||||||
enable: true,
|
|
||||||
decode: false,
|
|
||||||
author_href: null,
|
|
||||||
license: 'CC BY-NC-SA 4.0',
|
|
||||||
license_url: 'https://creativecommons.org/licenses/by-nc-sa/4.0/'
|
|
||||||
},
|
|
||||||
reward: {
|
|
||||||
enable: false,
|
|
||||||
text: null,
|
|
||||||
QR_code: null
|
|
||||||
},
|
|
||||||
post_edit: {
|
|
||||||
enable: false,
|
|
||||||
url: null
|
|
||||||
},
|
|
||||||
related_post: {
|
|
||||||
enable: true,
|
|
||||||
limit: 6,
|
|
||||||
date_type: 'created'
|
|
||||||
},
|
|
||||||
post_pagination: 1,
|
|
||||||
noticeOutdate: {
|
|
||||||
enable: false,
|
|
||||||
style: 'flat',
|
|
||||||
limit_day: 365,
|
|
||||||
position: 'top',
|
|
||||||
message_prev: 'It has been',
|
|
||||||
message_next: 'days since the last update, the content of the article may be outdated.'
|
|
||||||
},
|
|
||||||
footer: {
|
|
||||||
nav: null,
|
|
||||||
owner: {
|
|
||||||
enable: true,
|
|
||||||
since: 2024
|
|
||||||
},
|
|
||||||
copyright: {
|
|
||||||
enable: true,
|
|
||||||
version: true
|
|
||||||
},
|
|
||||||
custom_text: null
|
|
||||||
},
|
|
||||||
aside: {
|
|
||||||
enable: true,
|
|
||||||
hide: false,
|
|
||||||
button: true,
|
|
||||||
mobile: true,
|
|
||||||
position: 'right',
|
|
||||||
display: {
|
|
||||||
archive: true,
|
|
||||||
tag: true,
|
|
||||||
category: true
|
|
||||||
},
|
|
||||||
card_author: {
|
|
||||||
enable: true,
|
|
||||||
description: null,
|
|
||||||
button: {
|
|
||||||
enable: true,
|
|
||||||
icon: 'fab fa-github',
|
|
||||||
text: 'Follow Me',
|
|
||||||
link: 'https://github.com/xxxxxx'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
card_announcement: {
|
|
||||||
enable: true,
|
|
||||||
content: 'This is my Blog'
|
|
||||||
},
|
|
||||||
card_recent_post: {
|
|
||||||
enable: true,
|
|
||||||
limit: 5,
|
|
||||||
sort: 'date',
|
|
||||||
sort_order: null
|
|
||||||
},
|
|
||||||
card_newest_comments: {
|
|
||||||
enable: false,
|
|
||||||
sort_order: null,
|
|
||||||
limit: 6,
|
|
||||||
storage: 10,
|
|
||||||
avatar: true
|
|
||||||
},
|
|
||||||
card_categories: {
|
|
||||||
enable: true,
|
|
||||||
limit: 8,
|
|
||||||
expand: 'none',
|
|
||||||
sort_order: null
|
|
||||||
},
|
|
||||||
card_tags: {
|
|
||||||
enable: true,
|
|
||||||
limit: 40,
|
|
||||||
color: false,
|
|
||||||
orderby: 'random',
|
|
||||||
order: 1,
|
|
||||||
sort_order: null
|
|
||||||
},
|
|
||||||
card_archives: {
|
|
||||||
enable: true,
|
|
||||||
type: 'monthly',
|
|
||||||
format: 'MMMM YYYY',
|
|
||||||
order: -1,
|
|
||||||
limit: 8,
|
|
||||||
sort_order: null
|
|
||||||
},
|
|
||||||
card_post_series: {
|
|
||||||
enable: true,
|
|
||||||
series_title: false,
|
|
||||||
orderBy: 'date',
|
|
||||||
order: -1
|
|
||||||
},
|
|
||||||
card_webinfo: {
|
|
||||||
enable: true,
|
|
||||||
post_count: true,
|
|
||||||
last_push_date: true,
|
|
||||||
sort_order: null,
|
|
||||||
runtime_date: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
rightside_bottom: null,
|
|
||||||
translate: {
|
|
||||||
enable: false,
|
|
||||||
default: '繁',
|
|
||||||
defaultEncoding: 2,
|
|
||||||
translateDelay: 0,
|
|
||||||
msgToTraditionalChinese: '繁',
|
|
||||||
msgToSimplifiedChinese: '簡'
|
|
||||||
},
|
|
||||||
readmode: true,
|
|
||||||
darkmode: {
|
|
||||||
enable: true,
|
|
||||||
button: true,
|
|
||||||
autoChangeMode: false,
|
|
||||||
start: null,
|
|
||||||
end: null
|
|
||||||
},
|
|
||||||
rightside_scroll_percent: false,
|
|
||||||
rightside_item_order: {
|
|
||||||
enable: false,
|
|
||||||
hide: null,
|
|
||||||
show: null
|
|
||||||
},
|
|
||||||
rightside_config_animation: true,
|
|
||||||
anchor: {
|
|
||||||
auto_update: false,
|
|
||||||
click_to_scroll: false
|
|
||||||
},
|
|
||||||
photofigcaption: false,
|
|
||||||
copy: {
|
|
||||||
enable: true,
|
|
||||||
copyright: {
|
|
||||||
enable: false,
|
|
||||||
limit_count: 150
|
|
||||||
}
|
|
||||||
},
|
|
||||||
wordcount: {
|
|
||||||
enable: false,
|
|
||||||
post_wordcount: true,
|
|
||||||
min2read: true,
|
|
||||||
total_wordcount: true
|
|
||||||
},
|
|
||||||
busuanzi: {
|
|
||||||
site_uv: true,
|
|
||||||
site_pv: true,
|
|
||||||
page_pv: true
|
|
||||||
},
|
|
||||||
math: {
|
|
||||||
use: null,
|
|
||||||
per_page: true,
|
|
||||||
hide_scrollbar: false,
|
|
||||||
mathjax: {
|
|
||||||
enableMenu: true,
|
|
||||||
tags: 'none'
|
|
||||||
},
|
|
||||||
katex: {
|
|
||||||
copy_tex: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
use: null,
|
|
||||||
placeholder: null,
|
|
||||||
algolia_search: {
|
|
||||||
hitsPerPage: 6
|
|
||||||
},
|
|
||||||
local_search: {
|
|
||||||
preload: false,
|
|
||||||
top_n_per_article: 1,
|
|
||||||
unescape: false,
|
|
||||||
CDN: null
|
|
||||||
},
|
|
||||||
docsearch: {
|
|
||||||
appId: null,
|
|
||||||
apiKey: null,
|
|
||||||
indexName: null,
|
|
||||||
option: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
share: {
|
|
||||||
use: 'sharejs',
|
|
||||||
sharejs: {
|
|
||||||
sites: 'facebook,twitter,wechat,weibo,qq'
|
|
||||||
},
|
|
||||||
addtoany: {
|
|
||||||
item: 'facebook,twitter,wechat,sina_weibo,facebook_messenger,email,copy_link'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
comments: {
|
|
||||||
use: null,
|
|
||||||
text: true,
|
|
||||||
lazyload: false,
|
|
||||||
count: false,
|
|
||||||
card_post_count: false
|
|
||||||
},
|
|
||||||
disqus: {
|
|
||||||
shortname: null,
|
|
||||||
apikey: null
|
|
||||||
},
|
|
||||||
disqusjs: {
|
|
||||||
shortname: null,
|
|
||||||
apikey: null,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
livere: {
|
|
||||||
uid: null
|
|
||||||
},
|
|
||||||
gitalk: {
|
|
||||||
client_id: null,
|
|
||||||
client_secret: null,
|
|
||||||
repo: null,
|
|
||||||
owner: null,
|
|
||||||
admin: null,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
valine: {
|
|
||||||
appId: null,
|
|
||||||
appKey: null,
|
|
||||||
avatar: 'monsterid',
|
|
||||||
serverURLs: null,
|
|
||||||
bg: null,
|
|
||||||
visitor: false,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
waline: {
|
|
||||||
serverURL: null,
|
|
||||||
bg: null,
|
|
||||||
pageview: false,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
utterances: {
|
|
||||||
repo: null,
|
|
||||||
issue_term: 'pathname',
|
|
||||||
light_theme: 'github-light',
|
|
||||||
dark_theme: 'photon-dark',
|
|
||||||
js: null,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
facebook_comments: {
|
|
||||||
app_id: null,
|
|
||||||
user_id: null,
|
|
||||||
pageSize: 10,
|
|
||||||
order_by: 'social',
|
|
||||||
lang: 'en_US'
|
|
||||||
},
|
|
||||||
twikoo: {
|
|
||||||
envId: null,
|
|
||||||
region: null,
|
|
||||||
visitor: false,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
giscus: {
|
|
||||||
repo: null,
|
|
||||||
repo_id: null,
|
|
||||||
category_id: null,
|
|
||||||
light_theme: 'light',
|
|
||||||
dark_theme: 'dark',
|
|
||||||
js: null,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
remark42: {
|
|
||||||
host: null,
|
|
||||||
siteId: null,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
artalk: {
|
|
||||||
server: null,
|
|
||||||
site: null,
|
|
||||||
visitor: false,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
chat: {
|
|
||||||
use: null,
|
|
||||||
rightside_button: false,
|
|
||||||
button_hide_show: false
|
|
||||||
},
|
|
||||||
chatra: {
|
|
||||||
id: null
|
|
||||||
},
|
|
||||||
tidio: {
|
|
||||||
public_key: null
|
|
||||||
},
|
|
||||||
crisp: {
|
|
||||||
website_id: null
|
|
||||||
},
|
|
||||||
google_tag_manager: {
|
|
||||||
tag_id: null,
|
|
||||||
domain: 'https://www.googletagmanager.com'
|
|
||||||
},
|
|
||||||
baidu_analytics: null,
|
|
||||||
google_analytics: null,
|
|
||||||
cloudflare_analytics: null,
|
|
||||||
microsoft_clarity: null,
|
|
||||||
umami_analytics: {
|
|
||||||
enable: false,
|
|
||||||
serverURL: null,
|
|
||||||
website_id: null,
|
|
||||||
option: null,
|
|
||||||
UV_PV: {
|
|
||||||
site_uv: false,
|
|
||||||
site_pv: false,
|
|
||||||
page_pv: false,
|
|
||||||
token: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
google_adsense: {
|
|
||||||
enable: false,
|
|
||||||
auto_ads: true,
|
|
||||||
js: 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js',
|
|
||||||
client: null,
|
|
||||||
enable_page_level_ads: true
|
|
||||||
},
|
|
||||||
ad: {
|
|
||||||
index: null,
|
|
||||||
aside: null,
|
|
||||||
post: null
|
|
||||||
},
|
|
||||||
site_verification: null,
|
|
||||||
category_ui: null,
|
|
||||||
tag_ui: null,
|
|
||||||
rounded_corners_ui: true,
|
|
||||||
text_align_justify: false,
|
|
||||||
mask: {
|
|
||||||
header: true,
|
|
||||||
footer: true
|
|
||||||
},
|
|
||||||
preloader: {
|
|
||||||
enable: false,
|
|
||||||
source: 1,
|
|
||||||
pace_css_url: null
|
|
||||||
},
|
|
||||||
enter_transitions: true,
|
|
||||||
display_mode: 'light',
|
|
||||||
beautify: {
|
|
||||||
enable: false,
|
|
||||||
field: 'post',
|
|
||||||
title_prefix_icon: null,
|
|
||||||
title_prefix_icon_color: null
|
|
||||||
},
|
|
||||||
font: {
|
|
||||||
global_font_size: null,
|
|
||||||
code_font_size: null,
|
|
||||||
font_family: null,
|
|
||||||
code_font_family: null
|
|
||||||
},
|
|
||||||
blog_title_font: {
|
|
||||||
font_link: null,
|
|
||||||
font_family: null
|
|
||||||
},
|
|
||||||
hr_icon: {
|
|
||||||
enable: true,
|
|
||||||
icon: null,
|
|
||||||
icon_top: null
|
|
||||||
},
|
|
||||||
activate_power_mode: {
|
|
||||||
enable: false,
|
|
||||||
colorful: true,
|
|
||||||
shake: true,
|
|
||||||
mobile: false
|
|
||||||
},
|
|
||||||
canvas_ribbon: {
|
|
||||||
enable: false,
|
|
||||||
size: 150,
|
|
||||||
alpha: 0.6,
|
|
||||||
zIndex: -1,
|
|
||||||
click_to_change: false,
|
|
||||||
mobile: false
|
|
||||||
},
|
|
||||||
canvas_fluttering_ribbon: {
|
|
||||||
enable: false,
|
|
||||||
mobile: false
|
|
||||||
},
|
|
||||||
canvas_nest: {
|
|
||||||
enable: false,
|
|
||||||
color: '0,0,255',
|
|
||||||
opacity: 0.7,
|
|
||||||
zIndex: -1,
|
|
||||||
count: 99,
|
|
||||||
mobile: false
|
|
||||||
},
|
|
||||||
fireworks: {
|
|
||||||
enable: false,
|
|
||||||
zIndex: 9999,
|
|
||||||
mobile: false
|
|
||||||
},
|
|
||||||
click_heart: {
|
|
||||||
enable: false,
|
|
||||||
mobile: false
|
|
||||||
},
|
|
||||||
clickShowText: {
|
|
||||||
enable: false,
|
|
||||||
text: null,
|
|
||||||
fontSize: '15px',
|
|
||||||
random: false,
|
|
||||||
mobile: false
|
|
||||||
},
|
|
||||||
lightbox: null,
|
|
||||||
series: {
|
|
||||||
enable: false,
|
|
||||||
orderBy: 'title',
|
|
||||||
order: 1,
|
|
||||||
number: true
|
|
||||||
},
|
|
||||||
abcjs: {
|
|
||||||
enable: false,
|
|
||||||
per_page: true
|
|
||||||
},
|
|
||||||
mermaid: {
|
|
||||||
enable: false,
|
|
||||||
code_write: false,
|
|
||||||
theme: {
|
|
||||||
light: 'default',
|
|
||||||
dark: 'dark'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
chartjs: {
|
|
||||||
enable: false,
|
|
||||||
fontColor: {
|
|
||||||
light: 'rgba(0, 0, 0, 0.8)',
|
|
||||||
dark: 'rgba(255, 255, 255, 0.8)'
|
|
||||||
},
|
|
||||||
borderColor: {
|
|
||||||
light: 'rgba(0, 0, 0, 0.1)',
|
|
||||||
dark: 'rgba(255, 255, 255, 0.2)'
|
|
||||||
},
|
|
||||||
scale_ticks_backdropColor: {
|
|
||||||
light: 'transparent',
|
|
||||||
dark: 'transparent'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
note: {
|
|
||||||
style: 'flat',
|
|
||||||
icons: true,
|
|
||||||
border_radius: 3,
|
|
||||||
light_bg_offset: 0
|
|
||||||
},
|
|
||||||
pjax: {
|
|
||||||
enable: false,
|
|
||||||
exclude: null
|
|
||||||
},
|
|
||||||
aplayerInject: {
|
|
||||||
enable: false,
|
|
||||||
per_page: true
|
|
||||||
},
|
|
||||||
snackbar: {
|
|
||||||
enable: false,
|
|
||||||
position: 'bottom-left',
|
|
||||||
bg_light: '#49b1f5',
|
|
||||||
bg_dark: '#1f1f1f'
|
|
||||||
},
|
|
||||||
instantpage: false,
|
|
||||||
lazyload: {
|
|
||||||
enable: false,
|
|
||||||
native: false,
|
|
||||||
field: 'site',
|
|
||||||
placeholder: null,
|
|
||||||
blur: false
|
|
||||||
},
|
|
||||||
pwa: {
|
|
||||||
enable: false,
|
|
||||||
manifest: null,
|
|
||||||
apple_touch_icon: null,
|
|
||||||
favicon_32_32: null,
|
|
||||||
favicon_16_16: null,
|
|
||||||
mask_icon: null
|
|
||||||
},
|
|
||||||
Open_Graph_meta: {
|
|
||||||
enable: true,
|
|
||||||
option: null
|
|
||||||
},
|
|
||||||
structured_data: true,
|
|
||||||
css_prefix: true,
|
|
||||||
inject: {
|
|
||||||
head: null,
|
|
||||||
bottom: null
|
|
||||||
},
|
|
||||||
CDN: {
|
|
||||||
internal_provider: 'local',
|
|
||||||
third_party_provider: 'jsdelivr',
|
|
||||||
version: false,
|
|
||||||
custom_format: null,
|
|
||||||
option: null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hexo.theme.config = deepMerge(defaultConfig, hexo.theme.config)
|
|
||||||
}, 1)
|
|
||||||
@@ -5,55 +5,64 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
hexo.extend.generator.register('post', locals => {
|
hexo.extend.generator.register('post', locals => {
|
||||||
const previousIndexes = []
|
const imgTestReg = /\.(png|jpe?g|gif|svg|webp|avif)(\?.*)?$/i
|
||||||
|
const { post_asset_folder: postAssetFolder } = hexo.config
|
||||||
|
const { cover: { default_cover: defaultCover } } = hexo.theme.config
|
||||||
|
|
||||||
const getRandomCover = defaultCover => {
|
function * createCoverGenerator () {
|
||||||
if (!defaultCover) return false
|
if (!defaultCover) {
|
||||||
if (!Array.isArray(defaultCover)) return defaultCover
|
while (true) yield false
|
||||||
|
}
|
||||||
|
if (!Array.isArray(defaultCover)) {
|
||||||
|
while (true) yield defaultCover
|
||||||
|
}
|
||||||
|
|
||||||
const coverCount = defaultCover.length
|
const coverCount = defaultCover.length
|
||||||
|
|
||||||
if (coverCount === 1) {
|
if (coverCount === 1) {
|
||||||
return defaultCover[0]
|
while (true) yield defaultCover[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxPreviousIndexes = coverCount === 2 ? 1 : (coverCount === 3 ? 2 : 3)
|
const maxHistory = Math.min(3, coverCount - 1)
|
||||||
|
const history = []
|
||||||
|
|
||||||
let index
|
while (true) {
|
||||||
do {
|
let index
|
||||||
index = Math.floor(Math.random() * coverCount)
|
do {
|
||||||
} while (previousIndexes.includes(index) && previousIndexes.length < coverCount)
|
index = Math.floor(Math.random() * coverCount)
|
||||||
|
} while (history.includes(index))
|
||||||
|
|
||||||
previousIndexes.push(index)
|
history.push(index)
|
||||||
if (previousIndexes.length > maxPreviousIndexes) {
|
if (history.length > maxHistory) history.shift()
|
||||||
previousIndexes.shift()
|
|
||||||
|
yield defaultCover[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
return defaultCover[index]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const coverGenerator = createCoverGenerator()
|
||||||
|
|
||||||
const handleImg = data => {
|
const handleImg = data => {
|
||||||
const imgTestReg = /\.(png|jpe?g|gif|svg|webp|avif)(\?.*)?$/i
|
let { cover: coverVal, top_img: topImg, pagination_cover: paginationCover } = data
|
||||||
let { cover: coverVal, top_img: topImg } = data
|
|
||||||
|
|
||||||
// Add path to top_img and cover if post_asset_folder is enabled
|
// Add path to top_img and cover if post_asset_folder is enabled
|
||||||
if (hexo.config.post_asset_folder) {
|
if (postAssetFolder) {
|
||||||
if (topImg && topImg.indexOf('/') === -1 && imgTestReg.test(topImg)) {
|
if (topImg && topImg.indexOf('/') === -1 && imgTestReg.test(topImg)) {
|
||||||
data.top_img = `${data.path}${topImg}`
|
data.top_img = `${data.path}${topImg}`
|
||||||
}
|
}
|
||||||
if (coverVal && coverVal.indexOf('/') === -1 && imgTestReg.test(coverVal)) {
|
if (coverVal && coverVal.indexOf('/') === -1 && imgTestReg.test(coverVal)) {
|
||||||
data.cover = `${data.path}${coverVal}`
|
data.cover = `${data.path}${coverVal}`
|
||||||
}
|
}
|
||||||
|
if (paginationCover && paginationCover.indexOf('/') === -1 && imgTestReg.test(paginationCover)) {
|
||||||
|
data.pagination_cover = `${data.path}${paginationCover}`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (coverVal === false) return data
|
if (coverVal === false) return data
|
||||||
|
|
||||||
// If cover is not set, use random cover
|
// If cover is not set, use random cover
|
||||||
if (!coverVal) {
|
if (!coverVal) {
|
||||||
const { cover: { default_cover: defaultCover } } = hexo.theme.config
|
const randomCover = coverGenerator.next().value
|
||||||
const randomCover = getRandomCover(defaultCover)
|
|
||||||
data.cover = randomCover
|
data.cover = randomCover
|
||||||
coverVal = randomCover // update coverVal
|
coverVal = randomCover
|
||||||
}
|
}
|
||||||
|
|
||||||
if (coverVal && (coverVal.indexOf('//') !== -1 || imgTestReg.test(coverVal))) {
|
if (coverVal && (coverVal.indexOf('//') !== -1 || imgTestReg.test(coverVal))) {
|
||||||
@@ -63,7 +72,6 @@ hexo.extend.generator.register('post', locals => {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/hexojs/hexo/blob/master/lib%2Fplugins%2Fgenerator%2Fpost.ts
|
|
||||||
const posts = locals.posts.sort('date').toArray()
|
const posts = locals.posts.sort('date').toArray()
|
||||||
const { length } = posts
|
const { length } = posts
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
hexo.extend.helper.register('aside_archives', function (options = {}) {
|
hexo.extend.helper.register('aside_archives', function (options = {}) {
|
||||||
const { config, page, site, url_for, _p } = this
|
const { config, page, site, url_for: urlFor, _p } = this
|
||||||
const { archive_dir: archiveDir, timezone, language } = config
|
const { archive_dir: archiveDir, timezone, language } = config
|
||||||
|
|
||||||
// Destructure and set default options with object destructuring
|
// Destructure and set default options with object destructuring
|
||||||
@@ -17,56 +17,43 @@ hexo.extend.helper.register('aside_archives', function (options = {}) {
|
|||||||
// Optimize locale handling
|
// Optimize locale handling
|
||||||
const lang = toMomentLocale(page.lang || page.language || language)
|
const lang = toMomentLocale(page.lang || page.language || language)
|
||||||
|
|
||||||
// Memoize comparison function to improve performance
|
|
||||||
const compareFunc =
|
|
||||||
type === 'monthly'
|
|
||||||
? (yearA, monthA, yearB, monthB) => yearA === yearB && monthA === monthB
|
|
||||||
: (yearA, yearB) => yearA === yearB
|
|
||||||
|
|
||||||
// Early return if no posts
|
// Early return if no posts
|
||||||
if (!site.posts.length) return ''
|
if (!site.posts.length) return ''
|
||||||
|
|
||||||
// Use reduce for more efficient data processing
|
const archives = new Map()
|
||||||
const data = site.posts.sort('date', order).reduce((acc, post) => {
|
site.posts.forEach(post => {
|
||||||
let date = post.date.clone()
|
const date = post.date
|
||||||
if (timezone) date = date.tz(timezone)
|
|
||||||
|
|
||||||
const year = date.year()
|
const year = date.year()
|
||||||
const month = date.month() + 1
|
const month = date.month() + 1
|
||||||
|
const key = type === 'yearly' ? year : `${year}-${month}`
|
||||||
|
|
||||||
if (lang) date = date.locale(lang)
|
if (archives.has(key)) {
|
||||||
|
archives.get(key).count++
|
||||||
// Find or create archive entry
|
|
||||||
const lastEntry = acc[acc.length - 1]
|
|
||||||
|
|
||||||
if (type === 'yearly') {
|
|
||||||
const existingYearIndex = acc.findIndex(entry => entry.year === year)
|
|
||||||
if (existingYearIndex !== -1) {
|
|
||||||
acc[existingYearIndex].count++
|
|
||||||
} else {
|
|
||||||
// 否則創建新條目
|
|
||||||
acc.push({
|
|
||||||
name: date.format(format),
|
|
||||||
year,
|
|
||||||
month,
|
|
||||||
count: 1
|
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (!lastEntry || !compareFunc(lastEntry.year, lastEntry.month, year, month)) {
|
archives.set(key, {
|
||||||
acc.push({
|
year,
|
||||||
name: date.format(format),
|
month,
|
||||||
year,
|
count: 1,
|
||||||
month,
|
date // Store date object for later formatting
|
||||||
count: 1
|
})
|
||||||
})
|
|
||||||
} else {
|
|
||||||
lastEntry.count++
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
return acc
|
const data = Array.from(archives.values()).sort((a, b) => {
|
||||||
}, [])
|
if (order === -1) {
|
||||||
|
return b.year - a.year || b.month - a.month
|
||||||
|
}
|
||||||
|
return a.year - b.year || a.month - b.month
|
||||||
|
})
|
||||||
|
|
||||||
|
// Format names after aggregation
|
||||||
|
data.forEach(item => {
|
||||||
|
let date = item.date.clone()
|
||||||
|
if (timezone) date = date.tz(timezone)
|
||||||
|
if (lang) date = date.locale(lang)
|
||||||
|
item.name = date.format(format)
|
||||||
|
delete item.date // Clean up
|
||||||
|
})
|
||||||
|
|
||||||
// Create link generator function
|
// Create link generator function
|
||||||
const createArchiveLink = item => {
|
const createArchiveLink = item => {
|
||||||
@@ -74,7 +61,7 @@ hexo.extend.helper.register('aside_archives', function (options = {}) {
|
|||||||
if (type === 'monthly') {
|
if (type === 'monthly') {
|
||||||
url += item.month < 10 ? `0${item.month}/` : `${item.month}/`
|
url += item.month < 10 ? `0${item.month}/` : `${item.month}/`
|
||||||
}
|
}
|
||||||
return url_for(url)
|
return urlFor(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Limit results efficiently
|
// Limit results efficiently
|
||||||
@@ -87,7 +74,7 @@ hexo.extend.helper.register('aside_archives', function (options = {}) {
|
|||||||
<span>${_p('aside.card_archives')}</span>
|
<span>${_p('aside.card_archives')}</span>
|
||||||
${
|
${
|
||||||
data.length > limitedData.length
|
data.length > limitedData.length
|
||||||
? `<a class="card-more-btn" href="${url_for(archiveDir)}/"
|
? `<a class="card-more-btn" href="${urlFor(archiveDir)}/"
|
||||||
title="${_p('aside.more_button')}">
|
title="${_p('aside.more_button')}">
|
||||||
<i class="fas fa-angle-right"></i>
|
<i class="fas fa-angle-right"></i>
|
||||||
</a>`
|
</a>`
|
||||||
|
|||||||
@@ -19,15 +19,33 @@ hexo.extend.helper.register('aside_categories', function (categories, options =
|
|||||||
const expandClass = isExpand && options.expand === true ? 'expand' : ''
|
const expandClass = isExpand && options.expand === true ? 'expand' : ''
|
||||||
const buttonLabel = this._p('aside.more_button')
|
const buttonLabel = this._p('aside.more_button')
|
||||||
|
|
||||||
const prepareQuery = parent => {
|
const categoryMap = new Map()
|
||||||
const query = parent ? { parent } : { parent: { $exists: false } }
|
categories.forEach(cat => {
|
||||||
return categories.find(query).sort(orderby, order).filter(cat => cat.length)
|
if (cat.length) {
|
||||||
|
const parentId = cat.parent || 'root'
|
||||||
|
if (!categoryMap.has(parentId)) {
|
||||||
|
categoryMap.set(parentId, [])
|
||||||
|
}
|
||||||
|
categoryMap.get(parentId).push(cat)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const sortFn = (a, b) => {
|
||||||
|
const valA = a[orderby]
|
||||||
|
const valB = b[orderby]
|
||||||
|
if (valA < valB) return -order
|
||||||
|
if (valA > valB) return order
|
||||||
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
const hierarchicalList = (remaining, level = 0, parent) => {
|
for (const list of categoryMap.values()) {
|
||||||
|
list.sort(sortFn)
|
||||||
|
}
|
||||||
|
|
||||||
|
const hierarchicalList = (remaining, level = 0, parentId = 'root') => {
|
||||||
let result = ''
|
let result = ''
|
||||||
if (remaining > 0) {
|
if (remaining > 0 && categoryMap.has(parentId)) {
|
||||||
prepareQuery(parent).forEach(cat => {
|
categoryMap.get(parentId).forEach(cat => {
|
||||||
if (remaining > 0) {
|
if (remaining > 0) {
|
||||||
remaining -= 1
|
remaining -= 1
|
||||||
let child = ''
|
let child = ''
|
||||||
@@ -37,7 +55,8 @@ hexo.extend.helper.register('aside_categories', function (categories, options =
|
|||||||
remaining = childList.remaining
|
remaining = childList.remaining
|
||||||
}
|
}
|
||||||
|
|
||||||
const parentClass = isExpand && !parent && child ? 'parent' : ''
|
const isTopLevel = parentId === 'root'
|
||||||
|
const parentClass = isExpand && isTopLevel && child ? 'parent' : ''
|
||||||
result += `<li class="card-category-list-item ${parentClass}">`
|
result += `<li class="card-category-list-item ${parentClass}">`
|
||||||
result += `<a class="card-category-list-link" href="${this.url_for(cat.path)}">`
|
result += `<a class="card-category-list-link" href="${this.url_for(cat.path)}">`
|
||||||
result += `<span class="card-category-list-name">${cat.name}</span>`
|
result += `<span class="card-category-list-name">${cat.name}</span>`
|
||||||
@@ -46,7 +65,7 @@ hexo.extend.helper.register('aside_categories', function (categories, options =
|
|||||||
result += `<span class="card-category-list-count">${cat.length}</span>`
|
result += `<span class="card-category-list-count">${cat.length}</span>`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isExpand && !parent && child) {
|
if (isExpand && isTopLevel && child) {
|
||||||
result += `<i class="fas fa-caret-left ${expandClass}"></i>`
|
result += `<i class="fas fa-caret-left ${expandClass}"></i>`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,28 +8,29 @@ hexo.extend.helper.register('getArchiveLength', function () {
|
|||||||
// Archives Page
|
// Archives Page
|
||||||
if (!year) return posts.length
|
if (!year) return posts.length
|
||||||
|
|
||||||
// Function to generate a unique key based on the granularity
|
|
||||||
const getKey = (post, type) => {
|
|
||||||
const date = post.date.clone()
|
|
||||||
const y = date.year()
|
|
||||||
const m = date.month() + 1
|
|
||||||
const d = date.date()
|
|
||||||
if (type === 'year') return `${y}`
|
|
||||||
if (type === 'month') return `${y}-${m}`
|
|
||||||
if (type === 'day') return `${y}-${m}-${d}`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a map to count posts per period
|
// Create a map to count posts per period
|
||||||
const mapData = this.fragment_cache('createArchiveObj', () => {
|
const mapData = this.fragment_cache('createArchiveObj', () => {
|
||||||
const map = new Map()
|
const map = new Map()
|
||||||
posts.forEach(post => {
|
posts.forEach(post => {
|
||||||
const keyYear = getKey(post, 'year')
|
const date = post.date
|
||||||
const keyMonth = getKey(post, 'month')
|
const y = date.year()
|
||||||
const keyDay = getKey(post, 'day')
|
const m = date.month() + 1
|
||||||
|
const d = date.date()
|
||||||
|
|
||||||
if (yearly) map.set(keyYear, (map.get(keyYear) || 0) + 1)
|
if (yearly) {
|
||||||
if (monthly) map.set(keyMonth, (map.get(keyMonth) || 0) + 1)
|
const keyYear = `${y}`
|
||||||
if (daily) map.set(keyDay, (map.get(keyDay) || 0) + 1)
|
map.set(keyYear, (map.get(keyYear) || 0) + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (monthly) {
|
||||||
|
const keyMonth = `${y}-${m}`
|
||||||
|
map.set(keyMonth, (map.get(keyMonth) || 0) + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (daily) {
|
||||||
|
const keyDay = `${y}-${m}-${d}`
|
||||||
|
map.set(keyDay, (map.get(keyDay) || 0) + 1)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
return map
|
return map
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ hexo.extend.helper.register('inject_head_js', function () {
|
|||||||
const { darkmode, aside, pjax } = this.theme
|
const { darkmode, aside, pjax } = this.theme
|
||||||
const start = darkmode.start || 6
|
const start = darkmode.start || 6
|
||||||
const end = darkmode.end || 18
|
const end = darkmode.end || 18
|
||||||
const { theme_color } = hexo.theme.config
|
const { theme_color: themeColor } = hexo.theme.config
|
||||||
const themeColorLight = theme_color && theme_color.enable ? theme_color.meta_theme_color_light : '#ffffff'
|
const themeColorLight = themeColor && themeColor.enable ? themeColor.meta_theme_color_light : '#ffffff'
|
||||||
const themeColorDark = theme_color && theme_color.enable ? theme_color.meta_theme_color_dark : '#0d0d0d'
|
const themeColorDark = themeColor && themeColor.enable ? themeColor.meta_theme_color_dark : '#0d0d0d'
|
||||||
|
|
||||||
const createCustomJs = () => `
|
const createCustomJs = () => `
|
||||||
const saveToLocal = {
|
const saveToLocal = {
|
||||||
|
|||||||
@@ -5,6 +5,12 @@ const { prettyUrls } = require('hexo-util')
|
|||||||
const crypto = require('crypto')
|
const crypto = require('crypto')
|
||||||
const moment = require('moment-timezone')
|
const moment = require('moment-timezone')
|
||||||
|
|
||||||
|
const absoluteUrlPattern = /^(?:[a-z][a-z\d+.-]*:)?\/\//i
|
||||||
|
const relativeUrlPattern = /^(\.\/|\.\.\/|\/|[^/]+\/).*$/
|
||||||
|
const colorPattern = /^(#|rgb|rgba|hsl|hsla)/i
|
||||||
|
const simpleFilePattern = /\.(png|jpg|jpeg|gif|bmp|webp|svg|tiff)$/i
|
||||||
|
const archiveRegex = /\/archives\//
|
||||||
|
|
||||||
hexo.extend.helper.register('truncate', truncateContent)
|
hexo.extend.helper.register('truncate', truncateContent)
|
||||||
|
|
||||||
hexo.extend.helper.register('postDesc', data => {
|
hexo.extend.helper.register('postDesc', data => {
|
||||||
@@ -20,32 +26,27 @@ hexo.extend.helper.register('cloudTags', function (options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sizes = [...new Set(source.map(tag => tag.length).sort((a, b) => a - b))]
|
const sizes = [...new Set(source.map(tag => tag.length).sort((a, b) => a - b))]
|
||||||
|
const sizeMap = new Map(sizes.map((size, index) => [size, index]))
|
||||||
|
const length = sizes.length - 1
|
||||||
|
|
||||||
const getRandomColor = () => {
|
const getRandomColor = () => {
|
||||||
const randomColor = () => Math.floor(Math.random() * 201)
|
const r = Math.floor(Math.random() * 201)
|
||||||
const r = randomColor()
|
const g = Math.floor(Math.random() * 201)
|
||||||
const g = randomColor()
|
const b = Math.floor(Math.random() * 201)
|
||||||
const b = randomColor()
|
|
||||||
return `rgb(${Math.max(r, 50)}, ${Math.max(g, 50)}, ${Math.max(b, 50)})`
|
return `rgb(${Math.max(r, 50)}, ${Math.max(g, 50)}, ${Math.max(b, 50)})`
|
||||||
}
|
}
|
||||||
|
|
||||||
const generateStyle = (size, unit, page) => {
|
const generateStyle = (size, unit, page) => {
|
||||||
if (page === 'tags') {
|
const colorStyle = page === 'tags' ? `background-color: ${getRandomColor()};` : `color: ${getRandomColor()};`
|
||||||
return `font-size: ${parseFloat(size.toFixed(2)) + unit}; background-color: ${getRandomColor()};`
|
return `font-size: ${parseFloat(size.toFixed(2))}${unit}; ${colorStyle}`
|
||||||
} else {
|
|
||||||
return `font-size: ${parseFloat(size.toFixed(2)) + unit}; color: ${getRandomColor()};`
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const length = sizes.length - 1
|
return source.sort(orderby, order).map(tag => {
|
||||||
const result = source.sort(orderby, order).map(tag => {
|
const ratio = length ? sizeMap.get(tag.length) / length : 0
|
||||||
const ratio = length ? sizes.indexOf(tag.length) / length : 0
|
|
||||||
const size = minfontsize + ((maxfontsize - minfontsize) * ratio)
|
const size = minfontsize + ((maxfontsize - minfontsize) * ratio)
|
||||||
const style = generateStyle(size, unit, page)
|
const style = generateStyle(size, unit, page)
|
||||||
return `<a href="${env.url_for(tag.path)}" style="${style}">${tag.name}</a>`
|
return `<a href="${env.url_for(tag.path)}" style="${style}">${tag.name}</a>`
|
||||||
}).join('')
|
}).join('')
|
||||||
|
|
||||||
return result
|
|
||||||
})
|
})
|
||||||
|
|
||||||
hexo.extend.helper.register('urlNoIndex', function (url = null, trailingIndex = false, trailingHtml = false) {
|
hexo.extend.helper.register('urlNoIndex', function (url = null, trailingIndex = false, trailingHtml = false) {
|
||||||
@@ -77,7 +78,7 @@ hexo.extend.helper.register('findArchivesTitle', function (page, menu, date) {
|
|||||||
if (result) return result
|
if (result) return result
|
||||||
}
|
}
|
||||||
|
|
||||||
if (/\/archives\//.test(m[key])) {
|
if (archiveRegex.test(m[key])) {
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -89,13 +90,9 @@ hexo.extend.helper.register('findArchivesTitle', function (page, menu, date) {
|
|||||||
hexo.extend.helper.register('getBgPath', function (path) {
|
hexo.extend.helper.register('getBgPath', function (path) {
|
||||||
if (!path) return ''
|
if (!path) return ''
|
||||||
|
|
||||||
const absoluteUrlPattern = /^(?:[a-z][a-z\d+.-]*:)?\/\//i
|
|
||||||
const relativeUrlPattern = /^(\.\/|\.\.\/|\/|[^/]+\/).*$/
|
|
||||||
const colorPattern = /^(#|rgb|rgba|hsl|hsla)/i
|
|
||||||
|
|
||||||
if (colorPattern.test(path)) {
|
if (colorPattern.test(path)) {
|
||||||
return `background-color: ${path};`
|
return `background-color: ${path};`
|
||||||
} else if (absoluteUrlPattern.test(path) || relativeUrlPattern.test(path)) {
|
} else if (absoluteUrlPattern.test(path) || relativeUrlPattern.test(path) || simpleFilePattern.test(path)) {
|
||||||
return `background-image: url(${this.url_for(path)});`
|
return `background-image: url(${this.url_for(path)});`
|
||||||
} else {
|
} else {
|
||||||
return `background: ${path};`
|
return `background: ${path};`
|
||||||
@@ -104,39 +101,34 @@ hexo.extend.helper.register('getBgPath', function (path) {
|
|||||||
|
|
||||||
hexo.extend.helper.register('shuoshuoFN', (data, page) => {
|
hexo.extend.helper.register('shuoshuoFN', (data, page) => {
|
||||||
const { limit } = page
|
const { limit } = page
|
||||||
let finalResult = ''
|
|
||||||
|
// Shallow copy to avoid mutating original data
|
||||||
|
let processedData = data.map(item => ({ ...item }))
|
||||||
|
|
||||||
// Check if limit.value is a valid date
|
// Check if limit.value is a valid date
|
||||||
const isValidDate = date => !isNaN(Date.parse(date))
|
const isValidDate = date => !isNaN(Date.parse(date))
|
||||||
|
|
||||||
// order by date
|
// order by date
|
||||||
const orderByDate = data => data.sort((a, b) => Date.parse(b.date) - Date.parse(a.date))
|
processedData.sort((a, b) => Date.parse(b.date) - Date.parse(a.date))
|
||||||
|
|
||||||
// Apply number limit or time limit conditionally
|
// Apply number limit or time limit conditionally
|
||||||
const limitData = data => {
|
if (limit && limit.type === 'num' && limit.value > 0) {
|
||||||
if (limit && limit.type === 'num' && limit.value > 0) {
|
processedData = processedData.slice(0, limit.value)
|
||||||
return data.slice(0, limit.value)
|
} else if (limit && limit.type === 'date' && isValidDate(limit.value)) {
|
||||||
} else if (limit && limit.type === 'date' && isValidDate(limit.value)) {
|
const limitDate = Date.parse(limit.value)
|
||||||
const limitDate = Date.parse(limit.value)
|
processedData = processedData.filter(item => Date.parse(item.date) >= limitDate)
|
||||||
return data.filter(item => Date.parse(item.date) >= limitDate)
|
|
||||||
}
|
|
||||||
|
|
||||||
return data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
orderByDate(data)
|
|
||||||
finalResult = limitData(data)
|
|
||||||
|
|
||||||
// This is a hack method, because hexo treats time as UTC time
|
// This is a hack method, because hexo treats time as UTC time
|
||||||
// so you need to manually convert the time zone
|
// so you need to manually convert the time zone
|
||||||
finalResult.forEach(item => {
|
processedData.forEach(item => {
|
||||||
const utcDate = moment.utc(item.date).format('YYYY-MM-DD HH:mm:ss')
|
const utcDate = moment.utc(item.date).format('YYYY-MM-DD HH:mm:ss')
|
||||||
item.date = moment.tz(utcDate, hexo.config.timezone).format('YYYY-MM-DD HH:mm:ss')
|
item.date = moment.tz(utcDate, hexo.config.timezone).format('YYYY-MM-DD HH:mm:ss')
|
||||||
// markdown
|
// markdown
|
||||||
item.content = hexo.render.renderSync({ text: item.content, engine: 'markdown' })
|
item.content = hexo.render.renderSync({ text: item.content, engine: 'markdown' })
|
||||||
})
|
})
|
||||||
|
|
||||||
return finalResult
|
return processedData
|
||||||
})
|
})
|
||||||
|
|
||||||
hexo.extend.helper.register('getPageType', (page, isHome) => {
|
hexo.extend.helper.register('getPageType', (page, isHome) => {
|
||||||
|
|||||||
@@ -9,14 +9,22 @@
|
|||||||
|
|
||||||
const { postDesc } = require('../common/postDesc')
|
const { postDesc } = require('../common/postDesc')
|
||||||
|
|
||||||
hexo.extend.helper.register('related_posts', function (currentPost, allPosts) {
|
hexo.extend.helper.register('related_posts', function (currentPost) {
|
||||||
let relatedPosts = []
|
const relatedPosts = new Map()
|
||||||
const tagsData = currentPost.tags
|
const tagsData = currentPost.tags
|
||||||
tagsData.length && tagsData.forEach(function (tag) {
|
|
||||||
allPosts.forEach(function (post) {
|
if (!tagsData || !tagsData.length) return ''
|
||||||
if (currentPost.path !== post.path && isTagRelated(tag.name, post.tags)) {
|
|
||||||
|
tagsData.forEach(tag => {
|
||||||
|
const posts = tag.posts
|
||||||
|
posts.forEach(post => {
|
||||||
|
if (currentPost.path === post.path) return
|
||||||
|
|
||||||
|
if (relatedPosts.has(post.path)) {
|
||||||
|
relatedPosts.get(post.path).weight += 1
|
||||||
|
} else {
|
||||||
const getPostDesc = post.postDesc || postDesc(post, hexo)
|
const getPostDesc = post.postDesc || postDesc(post, hexo)
|
||||||
const relatedPost = {
|
relatedPosts.set(post.path, {
|
||||||
title: post.title,
|
title: post.title,
|
||||||
path: post.path,
|
path: post.path,
|
||||||
cover: post.cover,
|
cover: post.cover,
|
||||||
@@ -24,22 +32,17 @@ hexo.extend.helper.register('related_posts', function (currentPost, allPosts) {
|
|||||||
weight: 1,
|
weight: 1,
|
||||||
updated: post.updated,
|
updated: post.updated,
|
||||||
created: post.date,
|
created: post.date,
|
||||||
postDesc: getPostDesc
|
postDesc: getPostDesc,
|
||||||
}
|
random: Math.random()
|
||||||
const index = findItem(relatedPosts, 'path', post.path)
|
})
|
||||||
if (index !== -1) {
|
|
||||||
relatedPosts[index].weight += 1
|
|
||||||
} else {
|
|
||||||
relatedPosts.push(relatedPost)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
if (relatedPosts.length === 0) {
|
if (relatedPosts.size === 0) {
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
let result = ''
|
|
||||||
const hexoConfig = hexo.config
|
const hexoConfig = hexo.config
|
||||||
const config = hexo.theme.config
|
const config = hexo.theme.config
|
||||||
|
|
||||||
@@ -47,51 +50,42 @@ hexo.extend.helper.register('related_posts', function (currentPost, allPosts) {
|
|||||||
const dateType = config.related_post.date_type || 'created'
|
const dateType = config.related_post.date_type || 'created'
|
||||||
const headlineLang = this._p('post.recommend')
|
const headlineLang = this._p('post.recommend')
|
||||||
|
|
||||||
relatedPosts = relatedPosts.sort(compare('weight'))
|
const relatedPostsList = Array.from(relatedPosts.values()).sort((a, b) => {
|
||||||
|
if (b.weight !== a.weight) {
|
||||||
if (relatedPosts.length > 0) {
|
return b.weight - a.weight
|
||||||
result += '<div class="relatedPosts">'
|
|
||||||
result += `<div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>${headlineLang}</span></div>`
|
|
||||||
result += '<div class="relatedPosts-list">'
|
|
||||||
|
|
||||||
for (let i = 0; i < Math.min(relatedPosts.length, limitNum); i++) {
|
|
||||||
let { cover, title, path, cover_type, created, updated, postDesc } = relatedPosts[i]
|
|
||||||
const { escape_html, url_for, date } = this
|
|
||||||
cover = cover || 'var(--default-bg-color)'
|
|
||||||
title = escape_html(title)
|
|
||||||
const className = postDesc ? 'pagination-related' : 'pagination-related no-desc'
|
|
||||||
result += `<a class="${className}" href="${url_for(path)}" title="${title}">`
|
|
||||||
if (cover_type === 'img') {
|
|
||||||
result += `<img class="cover" src="${url_for(cover)}" alt="cover">`
|
|
||||||
} else {
|
|
||||||
result += `<div class="cover" style="background: ${cover}"></div>`
|
|
||||||
}
|
|
||||||
if (dateType === 'created') {
|
|
||||||
result += `<div class="info text-center"><div class="info-1"><div class="info-item-1"><i class="far fa-calendar-alt fa-fw"></i> ${date(created, hexoConfig.date_format)}</div>`
|
|
||||||
} else {
|
|
||||||
result += `<div class="info text-center"><div class="info-1"><div class="info-item-1"><i class="fas fa-history fa-fw"></i> ${date(updated, hexoConfig.date_format)}</div>`
|
|
||||||
}
|
|
||||||
result += `<div class="info-item-2">${title}</div></div>`
|
|
||||||
|
|
||||||
if (postDesc) {
|
|
||||||
result += `<div class="info-2"><div class="info-item-1">${postDesc}</div></div>`
|
|
||||||
}
|
|
||||||
result += '</div></a>'
|
|
||||||
}
|
}
|
||||||
|
return b.random - a.random
|
||||||
|
})
|
||||||
|
|
||||||
result += '</div></div>'
|
let result = '<div class="relatedPosts">'
|
||||||
return result
|
result += `<div class="headline"><i class="fas fa-thumbs-up fa-fw"></i><span>${headlineLang}</span></div>`
|
||||||
|
result += '<div class="relatedPosts-list">'
|
||||||
|
|
||||||
|
for (let i = 0; i < Math.min(relatedPostsList.length, limitNum); i++) {
|
||||||
|
let { cover, title, path, cover_type, created, updated, postDesc } = relatedPostsList[i]
|
||||||
|
const { escape_html, url_for, date } = this
|
||||||
|
cover = cover || 'var(--default-bg-color)'
|
||||||
|
title = escape_html(title)
|
||||||
|
const className = postDesc ? 'pagination-related' : 'pagination-related no-desc'
|
||||||
|
result += `<a class="${className}" href="${url_for(path)}" title="${title}">`
|
||||||
|
if (cover_type === 'img') {
|
||||||
|
result += `<img class="cover" src="${url_for(cover)}" alt="cover">`
|
||||||
|
} else {
|
||||||
|
result += `<div class="cover" style="background: ${cover}"></div>`
|
||||||
|
}
|
||||||
|
if (dateType === 'created') {
|
||||||
|
result += `<div class="info text-center"><div class="info-1"><div class="info-item-1"><i class="far fa-calendar-alt fa-fw"></i> ${date(created, hexoConfig.date_format)}</div>`
|
||||||
|
} else {
|
||||||
|
result += `<div class="info text-center"><div class="info-1"><div class="info-item-1"><i class="fas fa-history fa-fw"></i> ${date(updated, hexoConfig.date_format)}</div>`
|
||||||
|
}
|
||||||
|
result += `<div class="info-item-2">${title}</div></div>`
|
||||||
|
|
||||||
|
if (postDesc) {
|
||||||
|
result += `<div class="info-2"><div class="info-item-1">${postDesc}</div></div>`
|
||||||
|
}
|
||||||
|
result += '</div></a>'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result += '</div></div>'
|
||||||
|
return result
|
||||||
})
|
})
|
||||||
|
|
||||||
function isTagRelated (tagName, tags) {
|
|
||||||
return tags.some(tag => tag.name === tagName)
|
|
||||||
}
|
|
||||||
|
|
||||||
function findItem (arrayToSearch, attr, val) {
|
|
||||||
return arrayToSearch.findIndex(item => item[attr] === val)
|
|
||||||
}
|
|
||||||
|
|
||||||
function compare (attr) {
|
|
||||||
return (a, b) => b[attr] - a[attr]
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
function link(args) {
|
|
||||||
args = args.join(' ').split(',');
|
|
||||||
let title = args[0];
|
|
||||||
let sitename = args[1];
|
|
||||||
let link = args[2];
|
|
||||||
|
|
||||||
// 定义不同域名对应的头像URL
|
|
||||||
const avatarUrls = {
|
|
||||||
};
|
|
||||||
|
|
||||||
// 定义白名单域名
|
|
||||||
const whitelistDomains = [
|
|
||||||
'biss.click'
|
|
||||||
];
|
|
||||||
|
|
||||||
// 获取URL的根域名
|
|
||||||
function getRootDomain(url) {
|
|
||||||
const hostname = new URL(url).hostname;
|
|
||||||
const domainParts = hostname.split('.').reverse();
|
|
||||||
if (domainParts.length > 1) {
|
|
||||||
return domainParts[1] + '.' + domainParts[0];
|
|
||||||
}
|
|
||||||
return hostname;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据URL获取对应的头像URL
|
|
||||||
function getAvatarUrl(url) {
|
|
||||||
const rootDomain = getRootDomain(url);
|
|
||||||
for (const domain in avatarUrls) {
|
|
||||||
if (domain.endsWith(rootDomain)) {
|
|
||||||
return avatarUrls[domain];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 'https://free.picui.cn/free/2025/08/10/689845496a283.png'; // 默认头像URL
|
|
||||||
}
|
|
||||||
|
|
||||||
// 检查是否在白名单中
|
|
||||||
function isWhitelisted(url) {
|
|
||||||
const rootDomain = getRootDomain(url);
|
|
||||||
for (const domain of whitelistDomains) {
|
|
||||||
if (rootDomain.endsWith(domain)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取对应的头像URL
|
|
||||||
let imgUrl = getAvatarUrl(link);
|
|
||||||
|
|
||||||
// 判断并生成提示信息
|
|
||||||
// 判断并生成提示信息
|
|
||||||
let tipMessage = isWhitelisted(link)
|
|
||||||
? "✅来自本站,本站可确保其安全性,请放心点击跳转"
|
|
||||||
: "🪧引用站外地址,不保证站点的可用性和安全性";
|
|
||||||
|
|
||||||
return `<div class='liushen-tag-link'><a class="tag-Link" target="_blank" href="${link}">
|
|
||||||
<div class="tag-link-tips">${tipMessage}</div>
|
|
||||||
<div class="tag-link-bottom">
|
|
||||||
<div class="tag-link-left" style="background-image: url(${imgUrl});"></div>
|
|
||||||
<div class="tag-link-right">
|
|
||||||
<div class="tag-link-title">${title}</div>
|
|
||||||
<div class="tag-link-sitename">${sitename}</div>
|
|
||||||
</div>
|
|
||||||
<i class="fa-solid fa-angle-right"></i>
|
|
||||||
</div>
|
|
||||||
</a></div>`;
|
|
||||||
}
|
|
||||||
|
|
||||||
hexo.extend.tag.register('link', link, { ends: false });
|
|
||||||
@@ -9,7 +9,8 @@
|
|||||||
const { escapeHTML } = require('hexo-util')
|
const { escapeHTML } = require('hexo-util')
|
||||||
|
|
||||||
const mermaid = (args, content) => {
|
const mermaid = (args, content) => {
|
||||||
return `<div class="mermaid-wrap"><pre class="mermaid-src" hidden>
|
const config = args[0] || '{}'
|
||||||
|
return `<div class="mermaid-wrap"><pre class="mermaid-src" data-config="${escapeHTML(config)}" hidden>
|
||||||
${escapeHTML(content)}
|
${escapeHTML(content)}
|
||||||
</pre></div>`
|
</pre></div>`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ if hexo-config('enter_transitions')
|
|||||||
animation: titleScale 1s
|
animation: titleScale 1s
|
||||||
|
|
||||||
canvas:not(#ribbon-canvas),
|
canvas:not(#ribbon-canvas),
|
||||||
#web_bg
|
#web_bg.bg-animation
|
||||||
animation: to_show 4s
|
animation: to_show 4s
|
||||||
|
|
||||||
#ribbon-canvas
|
#ribbon-canvas
|
||||||
|
|||||||
@@ -100,18 +100,23 @@ $code-block
|
|||||||
.highlight-tools
|
.highlight-tools
|
||||||
display: flex
|
display: flex
|
||||||
align-items: center
|
align-items: center
|
||||||
|
overflow: hidden
|
||||||
padding: 0 8px
|
padding: 0 8px
|
||||||
min-height: 24px
|
min-height: 24px
|
||||||
height: 2.15em
|
height: 2.15em
|
||||||
background: var(--hltools-bg)
|
background: var(--hltools-bg)
|
||||||
color: var(--hltools-color)
|
color: var(--hltools-color)
|
||||||
font-size: $code-font-size
|
font-size: $code-font-size
|
||||||
overflow: hidden
|
|
||||||
|
|
||||||
& > *
|
& > *
|
||||||
padding: 5px
|
flex: 0 0 auto
|
||||||
|
margin: 2px
|
||||||
|
|
||||||
i
|
i
|
||||||
|
display: inline-flex
|
||||||
|
justify-content: center
|
||||||
|
align-items: center
|
||||||
|
padding: 5px
|
||||||
cursor: pointer
|
cursor: pointer
|
||||||
transition: all .3s
|
transition: all .3s
|
||||||
|
|
||||||
@@ -127,31 +132,28 @@ $code-block
|
|||||||
|
|
||||||
if !$highlight_macstyle
|
if !$highlight_macstyle
|
||||||
& > .macStyle
|
& > .macStyle
|
||||||
padding: 0
|
margin: 0
|
||||||
|
|
||||||
.code-lang
|
.code-lang
|
||||||
flex: 1
|
flex: 1 1 auto
|
||||||
|
overflow: hidden
|
||||||
|
padding-right: 10px
|
||||||
text-transform: uppercase
|
text-transform: uppercase
|
||||||
|
text-overflow: ellipsis
|
||||||
|
white-space: nowrap
|
||||||
font-weight: bold
|
font-weight: bold
|
||||||
font-size: 1.15em
|
font-size: 1.15em
|
||||||
user-select: none
|
user-select: none
|
||||||
-webkit-user-select: none
|
-webkit-user-select: none
|
||||||
padding 2px
|
|
||||||
|
|
||||||
.copy-notice
|
|
||||||
padding-right: 2px
|
|
||||||
opacity: 0
|
|
||||||
transition: opacity .4s
|
|
||||||
|
|
||||||
if hexo-config('code_blocks.language')
|
if hexo-config('code_blocks.language')
|
||||||
.code-lang
|
margin-right: auto
|
||||||
flex: 1
|
else if !$highlight_macstyle && hexo-config('code_blocks.shrink') != 'none'
|
||||||
else if (!$highlight_macstyle && hexo-config('code_blocks.shrink') != 'none')
|
& > :nth-child(2)
|
||||||
& > div:nth-child(2)
|
margin-right: auto
|
||||||
flex: 1
|
|
||||||
else
|
else
|
||||||
.macStyle
|
& > :nth-child(1)
|
||||||
flex: 1
|
margin-right: auto
|
||||||
|
|
||||||
.gutter
|
.gutter
|
||||||
user-select: none
|
user-select: none
|
||||||
@@ -163,17 +165,28 @@ $code-block
|
|||||||
td
|
td
|
||||||
border: none
|
border: none
|
||||||
|
|
||||||
|
.copy-notice
|
||||||
|
position: absolute
|
||||||
|
z-index: 99999
|
||||||
|
padding: 2px 6px
|
||||||
|
border-radius: 3px
|
||||||
|
background: var(--hltools-bg)
|
||||||
|
white-space: nowrap
|
||||||
|
font-size: 12px
|
||||||
|
pointer-events: none
|
||||||
|
|
||||||
if $highlight_macstyle
|
if $highlight_macstyle
|
||||||
.container
|
.container
|
||||||
figure.highlight
|
figure.highlight
|
||||||
margin: 0 0 24px
|
margin: 0 0 24px
|
||||||
border-radius: 7px
|
border-radius: 8px
|
||||||
box-shadow: 0 5px 10px 0 $highlight-mac-border
|
box-shadow: 0 5px 10px 0 $highlight-mac-border
|
||||||
-webkit-transform: translateZ(0)
|
-webkit-transform: translateZ(0)
|
||||||
|
|
||||||
.highlight-tools
|
.highlight-tools
|
||||||
.macStyle
|
.macStyle
|
||||||
display: flex
|
display: flex
|
||||||
|
padding: 3px
|
||||||
|
|
||||||
& > *
|
& > *
|
||||||
margin-right: 8px
|
margin-right: 8px
|
||||||
@@ -264,8 +277,8 @@ if hexo-config('code_blocks.fullpage')
|
|||||||
& ~ table
|
& ~ table
|
||||||
display: block
|
display: block
|
||||||
overflow: auto
|
overflow: auto
|
||||||
height: calc(100vh - 2.15em)
|
|
||||||
margin-bottom: 0
|
margin-bottom: 0
|
||||||
|
height: calc(100vh - 2.15em)
|
||||||
|
|
||||||
@keyframes code-fullpage
|
@keyframes code-fullpage
|
||||||
0%,
|
0%,
|
||||||
|
|||||||
@@ -130,6 +130,7 @@ if hexo-config('darkmode.enable') || hexo-config('display_mode') == 'dark'
|
|||||||
|
|
||||||
// hide-tags
|
// hide-tags
|
||||||
.hide-button,
|
.hide-button,
|
||||||
|
.toggle-button,
|
||||||
#post-outdate-notice,
|
#post-outdate-notice,
|
||||||
.error-img,
|
.error-img,
|
||||||
.container iframe,
|
.container iframe,
|
||||||
@@ -140,9 +141,9 @@ if hexo-config('darkmode.enable') || hexo-config('display_mode') == 'dark'
|
|||||||
|
|
||||||
img:not(.cover)
|
img:not(.cover)
|
||||||
if hexo-config('lazyload.enable') && hexo-config('lazyload.blur') && !hexo-config('lazyload.placeholder')
|
if hexo-config('lazyload.enable') && hexo-config('lazyload.blur') && !hexo-config('lazyload.placeholder')
|
||||||
filter: blur(0) brightness(.8)
|
filter: blur(0) brightness(.88) contrast(.95)
|
||||||
else
|
else
|
||||||
filter: brightness(.8)
|
filter: brightness(.88) contrast(.95)
|
||||||
|
|
||||||
#aside-content .aside-list > .aside-list-item:not(:last-child)
|
#aside-content .aside-list > .aside-list-item:not(:last-child)
|
||||||
border-bottom: 1px dashed alpha(#FFFFFF, .1)
|
border-bottom: 1px dashed alpha(#FFFFFF, .1)
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ if hexo-config('readmode')
|
|||||||
font-size: 16px
|
font-size: 16px
|
||||||
transition: background .3s
|
transition: background .3s
|
||||||
addBorderRadius(8)
|
addBorderRadius(8)
|
||||||
|
@extend .btn-effects
|
||||||
|
|
||||||
+maxWidth768()
|
+maxWidth768()
|
||||||
top: initial
|
top: initial
|
||||||
|
|||||||
@@ -39,11 +39,26 @@
|
|||||||
border: 1px solid $tag-hide-toggle-bg
|
border: 1px solid $tag-hide-toggle-bg
|
||||||
addBorderRadius(5, true)
|
addBorderRadius(5, true)
|
||||||
|
|
||||||
|
& > .toggle-content
|
||||||
|
margin: 30px 24px
|
||||||
|
|
||||||
& > .toggle-button
|
& > .toggle-button
|
||||||
padding: 6px 15px
|
padding: 6px 15px
|
||||||
background: $tag-hide-toggle-bg
|
background: $tag-hide-toggle-bg
|
||||||
color: #1F2D3D
|
color: #1F2D3D
|
||||||
|
list-style: none
|
||||||
cursor: pointer
|
cursor: pointer
|
||||||
|
|
||||||
& > .toggle-content
|
&::-webkit-details-marker
|
||||||
margin: 30px 24px
|
display: none
|
||||||
|
|
||||||
|
&::before
|
||||||
|
@extend .fontawesomeIcon
|
||||||
|
margin-right: 8px
|
||||||
|
content: '\f0d7'
|
||||||
|
transition: transform .3s ease
|
||||||
|
transform: rotate(-90deg)
|
||||||
|
transform-origin: center center
|
||||||
|
|
||||||
|
&[open] summary::before
|
||||||
|
transform: rotate(0)
|
||||||
@@ -68,7 +68,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const isPrismjs = plugin === 'prismjs'
|
const isPrismjs = plugin === 'prismjs'
|
||||||
const highlightShrinkClass = isHighlightShrink === true ? 'closed' : ''
|
const highlightShrinkClass = isHighlightShrink === true ? 'closed' : ''
|
||||||
const highlightShrinkEle = isHighlightShrink !== undefined ? '<i class="fas fa-angle-down expand"></i>' : ''
|
const highlightShrinkEle = isHighlightShrink !== undefined ? '<i class="fas fa-angle-down expand"></i>' : ''
|
||||||
const highlightCopyEle = highlightCopy ? '<div class="copy-notice"></div><i class="fas fa-paste copy-button"></i>' : ''
|
const highlightCopyEle = highlightCopy ? '<i class="fas fa-paste copy-button"></i>' : ''
|
||||||
const highlightMacStyleEle = '<div class="macStyle"><div class="mac-close"></div><div class="mac-minimize"></div><div class="mac-maximize"></div></div>'
|
const highlightMacStyleEle = '<div class="macStyle"><div class="mac-close"></div><div class="mac-minimize"></div><div class="mac-maximize"></div></div>'
|
||||||
const highlightFullpageEle = highlightFullpage ? '<i class="fa-solid fa-up-right-and-down-left-from-center fullpage-button"></i>' : ''
|
const highlightFullpageEle = highlightFullpage ? '<i class="fa-solid fa-up-right-and-down-left-from-center fullpage-button"></i>' : ''
|
||||||
|
|
||||||
@@ -76,9 +76,46 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
if (GLOBAL_CONFIG.Snackbar !== undefined) {
|
if (GLOBAL_CONFIG.Snackbar !== undefined) {
|
||||||
btf.snackbarShow(text)
|
btf.snackbarShow(text)
|
||||||
} else {
|
} else {
|
||||||
ele.textContent = text
|
const newEle = document.createElement('div')
|
||||||
ele.style.opacity = 1
|
newEle.className = 'copy-notice'
|
||||||
setTimeout(() => { ele.style.opacity = 0 }, 800)
|
newEle.textContent = text
|
||||||
|
document.body.appendChild(newEle)
|
||||||
|
|
||||||
|
const buttonRect = ele.getBoundingClientRect()
|
||||||
|
const scrollTop = window.pageYOffset || document.documentElement.scrollTop
|
||||||
|
const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft
|
||||||
|
|
||||||
|
// X-axis boundary check
|
||||||
|
const halfWidth = newEle.offsetWidth / 2
|
||||||
|
const centerLeft = buttonRect.left + scrollLeft + buttonRect.width / 2
|
||||||
|
const finalLeft = Math.max(halfWidth + 10, Math.min(window.innerWidth - halfWidth - 10, centerLeft))
|
||||||
|
|
||||||
|
// Show tooltip below button if too close to top
|
||||||
|
const normalTop = buttonRect.top + scrollTop - 40
|
||||||
|
const shouldShowBelow = buttonRect.top < 60 || normalTop < 10
|
||||||
|
|
||||||
|
const topValue = shouldShowBelow ? buttonRect.top + scrollTop + buttonRect.height + 10 : normalTop
|
||||||
|
|
||||||
|
newEle.style.cssText = `
|
||||||
|
top: ${topValue + 10}px;
|
||||||
|
left: ${finalLeft}px;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s ease, top 0.3s ease;
|
||||||
|
`
|
||||||
|
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
newEle.style.opacity = '1'
|
||||||
|
newEle.style.top = `${topValue}px`
|
||||||
|
})
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
newEle.style.opacity = '0'
|
||||||
|
newEle.style.top = `${topValue + 10}px`
|
||||||
|
setTimeout(() => {
|
||||||
|
newEle?.remove()
|
||||||
|
}, 300)
|
||||||
|
}, 800)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,7 +136,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const preCodeSelector = isPrismjs ? 'pre code' : 'table .code pre'
|
const preCodeSelector = isPrismjs ? 'pre code' : 'table .code pre'
|
||||||
const codeElement = $buttonParent.querySelector(preCodeSelector)
|
const codeElement = $buttonParent.querySelector(preCodeSelector)
|
||||||
if (!codeElement) return
|
if (!codeElement) return
|
||||||
copy(codeElement.innerText, clickEle.previousElementSibling)
|
copy(codeElement.innerText, clickEle)
|
||||||
$buttonParent.classList.remove('copy-true')
|
$buttonParent.classList.remove('copy-true')
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,6 +163,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
// 獲取隱藏狀態下元素的真實高度
|
// 獲取隱藏狀態下元素的真實高度
|
||||||
const getActualHeight = item => {
|
const getActualHeight = item => {
|
||||||
|
if (item.offsetHeight > 0) return item.offsetHeight
|
||||||
const hiddenElements = new Map()
|
const hiddenElements = new Map()
|
||||||
|
|
||||||
const fix = () => {
|
const fix = () => {
|
||||||
@@ -512,17 +550,29 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
const $articleList = $article.querySelectorAll('h1,h2,h3,h4,h5,h6')
|
const $articleList = $article.querySelectorAll('h1,h2,h3,h4,h5,h6')
|
||||||
let detectItem = ''
|
let detectItem = ''
|
||||||
|
|
||||||
|
// Optimization: Cache header positions
|
||||||
|
let headerList = []
|
||||||
|
const updateHeaderPositions = () => {
|
||||||
|
headerList = Array.from($articleList).map(ele => ({
|
||||||
|
ele,
|
||||||
|
top: btf.getEleTop(ele),
|
||||||
|
id: ele.id
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
updateHeaderPositions()
|
||||||
|
btf.addEventListenerPjax(window, 'resize', btf.throttle(updateHeaderPositions, 200))
|
||||||
|
|
||||||
const findHeadPosition = top => {
|
const findHeadPosition = top => {
|
||||||
if (top === 0) return false
|
if (top === 0) return false
|
||||||
|
|
||||||
let currentId = ''
|
let currentId = ''
|
||||||
let currentIndex = ''
|
let currentIndex = ''
|
||||||
|
|
||||||
for (let i = 0; i < $articleList.length; i++) {
|
for (let i = 0; i < headerList.length; i++) {
|
||||||
const ele = $articleList[i]
|
const item = headerList[i]
|
||||||
if (top > btf.getEleTop(ele) - 80) {
|
if (top > item.top - 80) {
|
||||||
const id = ele.id
|
currentId = item.id ? '#' + encodeURI(item.id) : ''
|
||||||
currentId = id ? '#' + encodeURI(id) : ''
|
|
||||||
currentIndex = i
|
currentIndex = i
|
||||||
} else {
|
} else {
|
||||||
break
|
break
|
||||||
@@ -600,7 +650,8 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
|
|
||||||
$body.classList.add('read-mode')
|
$body.classList.add('read-mode')
|
||||||
newEle.type = 'button'
|
newEle.type = 'button'
|
||||||
newEle.className = 'fas fa-sign-out-alt exit-readmode'
|
newEle.className = 'exit-readmode'
|
||||||
|
newEle.innerHTML = '<i class="fas fa-sign-out-alt"></i>'
|
||||||
newEle.addEventListener('click', exitReadMode)
|
newEle.addEventListener('click', exitReadMode)
|
||||||
$body.appendChild(newEle)
|
$body.appendChild(newEle)
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,37 +14,35 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
throttle: function (func, wait, options = {}) {
|
throttle: (func, wait, options = {}) => {
|
||||||
let timeout, context, args
|
let timeout, args
|
||||||
let previous = 0
|
let previous = 0
|
||||||
|
|
||||||
const later = () => {
|
const later = () => {
|
||||||
previous = options.leading === false ? 0 : new Date().getTime()
|
previous = options.leading === false ? 0 : new Date().getTime()
|
||||||
timeout = null
|
timeout = null
|
||||||
func.apply(context, args)
|
func(...args)
|
||||||
if (!timeout) context = args = null
|
if (!timeout) args = null
|
||||||
}
|
}
|
||||||
|
|
||||||
const throttled = (...params) => {
|
return (...params) => {
|
||||||
const now = new Date().getTime()
|
const now = new Date().getTime()
|
||||||
if (!previous && options.leading === false) previous = now
|
if (!previous && options.leading === false) previous = now
|
||||||
const remaining = wait - (now - previous)
|
const remaining = wait - (now - previous)
|
||||||
context = this
|
|
||||||
args = params
|
args = params
|
||||||
|
|
||||||
if (remaining <= 0 || remaining > wait) {
|
if (remaining <= 0 || remaining > wait) {
|
||||||
if (timeout) {
|
if (timeout) {
|
||||||
clearTimeout(timeout)
|
clearTimeout(timeout)
|
||||||
timeout = null
|
timeout = null
|
||||||
}
|
}
|
||||||
previous = now
|
previous = now
|
||||||
func.apply(context, args)
|
func(...args)
|
||||||
if (!timeout) context = args = null
|
if (!timeout) args = null
|
||||||
} else if (!timeout && options.trailing !== false) {
|
} else if (!timeout && options.trailing !== false) {
|
||||||
timeout = setTimeout(later, remaining)
|
timeout = setTimeout(later, remaining)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return throttled
|
|
||||||
},
|
},
|
||||||
|
|
||||||
overflowPaddingR: {
|
overflowPaddingR: {
|
||||||
@@ -169,17 +167,7 @@
|
|||||||
|
|
||||||
isHidden: ele => ele.offsetHeight === 0 && ele.offsetWidth === 0,
|
isHidden: ele => ele.offsetHeight === 0 && ele.offsetWidth === 0,
|
||||||
|
|
||||||
getEleTop: ele => {
|
getEleTop: ele => ele.getBoundingClientRect().top + window.scrollY,
|
||||||
let actualTop = ele.offsetTop
|
|
||||||
let current = ele.offsetParent
|
|
||||||
|
|
||||||
while (current !== null) {
|
|
||||||
actualTop += current.offsetTop
|
|
||||||
current = current.offsetParent
|
|
||||||
}
|
|
||||||
|
|
||||||
return actualTop
|
|
||||||
},
|
|
||||||
|
|
||||||
loadLightbox: ele => {
|
loadLightbox: ele => {
|
||||||
const service = GLOBAL_CONFIG.lightbox
|
const service = GLOBAL_CONFIG.lightbox
|
||||||
@@ -190,7 +178,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (service === 'fancybox') {
|
if (service === 'fancybox') {
|
||||||
Array.from(ele).forEach(i => {
|
ele.forEach(i => {
|
||||||
if (i.parentNode.tagName !== 'A') {
|
if (i.parentNode.tagName !== 'A') {
|
||||||
const dataSrc = i.dataset.lazySrc || i.src
|
const dataSrc = i.dataset.lazySrc || i.src
|
||||||
const dataCaption = i.title || i.alt || ''
|
const dataCaption = i.title || i.alt || ''
|
||||||
|
|||||||
Reference in New Issue
Block a user