mirror of
https://github.com/jerryc127/hexo-theme-butterfly.git
synced 2026-04-10 21:17:07 +08:00
feat: 更新 plugins.yml 中的依賴版本至最新
feat: 優化 aside_archives ,改進性能和可讀性 feat: 改善 inlineImg 和 timeline 標籤的文檔,優化時間線邏輯 feat: 更新 gallery 標籤以支持額外參數,優化圖片顯示邏輯 improvement: 優化隨機封面過濾器邏輯, 避免連續重複 feat: 最新評論限制顯示 1-10 條之間 fix: artalk 的最新評論顯示待定或者封禁的評論的 bug
This commit is contained in:
@@ -3,16 +3,16 @@ if theme.menu
|
|||||||
#menu-mask
|
#menu-mask
|
||||||
#sidebar-menus
|
#sidebar-menus
|
||||||
.avatar-img.text-center
|
.avatar-img.text-center
|
||||||
img(src=url_for(theme.avatar.img) onerror=`onerror=null;src='${theme.error_img.flink}'` alt="avatar")
|
img(src=url_for(theme.avatar.img) onerror=`this.onerror=null;this.src='${url_for(theme.error_img.flink)}'` alt="avatar")
|
||||||
.site-data.text-center
|
.site-data.text-center
|
||||||
a(href=url_for(config.archive_dir) + '/')
|
a(href=`${url_for(config.archive_dir)}/`)
|
||||||
.headline= _p('aside.articles')
|
.headline= _p('aside.articles')
|
||||||
.length-num= site.posts.length
|
.length-num= site.posts.length
|
||||||
a(href=url_for(config.tag_dir) + '/' )
|
a(href=`${url_for(config.tag_dir)}/`)
|
||||||
.headline= _p('aside.tags')
|
.headline= _p('aside.tags')
|
||||||
.length-num= site.tags.length
|
.length-num= site.tags.length
|
||||||
a(href=url_for(config.category_dir) + '/')
|
a(href=`${url_for(config.category_dir)}/`)
|
||||||
.headline= _p('aside.categories')
|
.headline= _p('aside.categories')
|
||||||
.length-num= site.categories.length
|
.length-num= site.categories.length
|
||||||
|
|
||||||
!=partial('includes/header/menu_item', {}, {cache: true})
|
!= partial('includes/header/menu_item', {}, { cache: true })
|
||||||
@@ -34,7 +34,7 @@ script.
|
|||||||
|
|
||||||
const searchParams = new URLSearchParams({
|
const searchParams = new URLSearchParams({
|
||||||
'site_name': '!{site}',
|
'site_name': '!{site}',
|
||||||
'limit': '!{theme.aside.card_newest_comments.limit}',
|
'limit': '!{newestCommentsLimit * 2}', // Fetch more comments to filter pending comments
|
||||||
})
|
})
|
||||||
|
|
||||||
const getComment = async (ele) => {
|
const getComment = async (ele) => {
|
||||||
@@ -42,7 +42,10 @@ script.
|
|||||||
const res = await fetch(`!{server}/api/v2/stats/latest_comments?${searchParams}`)
|
const res = await fetch(`!{server}/api/v2/stats/latest_comments?${searchParams}`)
|
||||||
const result = await res.json()
|
const result = await res.json()
|
||||||
const { avatarCdn, avatarDefault } = await getAvatarValue()
|
const { avatarCdn, avatarDefault } = await getAvatarValue()
|
||||||
const artalk = result.data.map(e => {
|
const artalk = result.data
|
||||||
|
.filter(e => !e.is_pending) // Filter pending comments
|
||||||
|
.slice(0, !{newestCommentsLimit}) // Limit the number of comments
|
||||||
|
.map(e => {
|
||||||
const avatar = avatarCdn && e.email_encrypted ? `${avatarCdn}${e.email_encrypted}?${avatarDefault}` : ''
|
const avatar = avatarCdn && e.email_encrypted ? `${avatarCdn}${e.email_encrypted}?${avatarDefault}` : ''
|
||||||
return {
|
return {
|
||||||
'avatar': avatar,
|
'avatar': avatar,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ script.
|
|||||||
const { changeContent, generateHtml, run } = window.newestComments
|
const { changeContent, generateHtml, run } = window.newestComments
|
||||||
|
|
||||||
const getComment = ele => {
|
const getComment = ele => {
|
||||||
fetch('https://disqus.com/api/3.0/forums/listPosts.json?forum=!{forum}&related=thread&limit=!{theme.aside.card_newest_comments.limit}&api_key=!{apiKey}')
|
fetch('https://disqus.com/api/3.0/forums/listPosts.json?forum=!{forum}&related=thread&limit=!{newestCommentsLimit}&api_key=!{apiKey}')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
const disqusArray = data.response.map(item => {
|
const disqusArray = data.response.map(item => {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ script.
|
|||||||
}
|
}
|
||||||
|
|
||||||
const getComment = ele => {
|
const getComment = ele => {
|
||||||
fetch('https://api.github.com/repos/!{userRepo}/issues/comments?sort=updated&direction=desc&per_page=!{theme.aside.card_newest_comments.limit}&page=1',{
|
fetch('https://api.github.com/repos/!{userRepo}/issues/comments?sort=updated&direction=desc&per_page=!{newestCommentsLimit}&page=1',{
|
||||||
"headers": {
|
"headers": {
|
||||||
Accept: 'application/vnd.github.v3.html+json'
|
Accept: 'application/vnd.github.v3.html+json'
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
- let { use } = theme.comments
|
- let { use } = theme.comments
|
||||||
|
|
||||||
if use
|
if use
|
||||||
- let forum,apiKey,userRepo
|
-
|
||||||
|
let forum,apiKey,userRepo
|
||||||
|
let { limit:newestCommentsLimit } = theme.aside.card_newest_comments
|
||||||
|
if (newestCommentsLimit > 10 || newestCommentsLimit < 1) newestCommentsLimit = 6
|
||||||
|
|
||||||
case use[0]
|
case use[0]
|
||||||
when 'Valine'
|
when 'Valine'
|
||||||
include ./valine.pug
|
include ./valine.pug
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ script.
|
|||||||
const { changeContent, generateHtml, run } = window.newestComments
|
const { changeContent, generateHtml, run } = window.newestComments
|
||||||
|
|
||||||
const getComment = ele => {
|
const getComment = ele => {
|
||||||
fetch('!{host}/api/v1/last/!{theme.aside.card_newest_comments.limit}?site=!{siteId}')
|
fetch('!{host}/api/v1/last/!{newestCommentsLimit}?site=!{siteId}')
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
const remark42 = data.map(e => {
|
const remark42 = data.map(e => {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ script.
|
|||||||
twikoo.getRecentComments({
|
twikoo.getRecentComments({
|
||||||
envId: '!{theme.twikoo.envId}',
|
envId: '!{theme.twikoo.envId}',
|
||||||
region: '!{theme.twikoo.region}',
|
region: '!{theme.twikoo.region}',
|
||||||
pageSize: !{theme.aside.card_newest_comments.limit},
|
pageSize: !{newestCommentsLimit},
|
||||||
includeReply: true
|
includeReply: true
|
||||||
}).then(res => {
|
}).then(res => {
|
||||||
const twikooArray = res.map(e => {
|
const twikooArray = res.map(e => {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ script.
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch(`${serverURL}/1.1/classes/Comment?limit=!{theme.aside.card_newest_comments.limit}&order=-createdAt`,settings)
|
fetch(`${serverURL}/1.1/classes/Comment?limit=!{newestCommentsLimit}&order=-createdAt`,settings)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
const valineArray = data.results.map(e => {
|
const valineArray = data.results.map(e => {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ script.
|
|||||||
|
|
||||||
const getComment = async (ele) => {
|
const getComment = async (ele) => {
|
||||||
try {
|
try {
|
||||||
const res = await fetch('!{serverURL}/api/comment?type=recent&count=!{theme.aside.card_newest_comments.limit}', { method: 'GET' })
|
const res = await fetch('!{serverURL}/api/comment?type=recent&count=!{newestCommentsLimit}')
|
||||||
const result = await res.json()
|
const result = await res.json()
|
||||||
const walineArray = result.data.map(e => {
|
const walineArray = result.data.map(e => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -6,40 +6,39 @@ if theme.aside.card_webinfo.enable
|
|||||||
.webinfo
|
.webinfo
|
||||||
if theme.aside.card_webinfo.post_count
|
if theme.aside.card_webinfo.post_count
|
||||||
.webinfo-item
|
.webinfo-item
|
||||||
.item-name= _p('aside.card_webinfo.article_name') + " :"
|
.item-name= `${_p('aside.card_webinfo.article_name')} :`
|
||||||
.item-count= site.posts.length
|
.item-count= site.posts.length
|
||||||
if theme.aside.card_webinfo.runtime_date
|
if theme.aside.card_webinfo.runtime_date
|
||||||
.webinfo-item
|
.webinfo-item
|
||||||
.item-name= _p('aside.card_webinfo.runtime.name') + " :"
|
.item-name= `${_p('aside.card_webinfo.runtime.name')} :`
|
||||||
.item-count#runtimeshow(data-publishDate=date_xml(theme.aside.card_webinfo.runtime_date))
|
.item-count#runtimeshow(data-publishDate=date_xml(theme.aside.card_webinfo.runtime_date))
|
||||||
i.fa-solid.fa-spinner.fa-spin
|
i.fa-solid.fa-spinner.fa-spin
|
||||||
if theme.wordcount.enable && theme.wordcount.total_wordcount
|
if theme.wordcount.enable && theme.wordcount.total_wordcount
|
||||||
.webinfo-item
|
.webinfo-item
|
||||||
.item-name=_p('aside.card_webinfo.site_wordcount') + " :"
|
.item-name= `${_p('aside.card_webinfo.site_wordcount')} :`
|
||||||
.item-count=totalcount(site)
|
.item-count= totalcount(site)
|
||||||
if theme.umami_analytics.enable && theme.umami_analytics.UV_PV.site_uv
|
if theme.umami_analytics.enable && theme.umami_analytics.UV_PV.site_uv
|
||||||
.webinfo-item
|
.webinfo-item
|
||||||
.item-name= _p('aside.card_webinfo.site_uv_name') + " :"
|
.item-name= `${_p('aside.card_webinfo.site_uv_name')} :`
|
||||||
.item-count#umami-site-uv
|
.item-count#umami-site-uv
|
||||||
i.fa-solid.fa-spinner.fa-spin
|
i.fa-solid.fa-spinner.fa-spin
|
||||||
else if theme.busuanzi.site_uv
|
else if theme.busuanzi.site_uv
|
||||||
.webinfo-item
|
.webinfo-item
|
||||||
.item-name= _p('aside.card_webinfo.site_uv_name') + " :"
|
.item-name= `${_p('aside.card_webinfo.site_uv_name')} :`
|
||||||
.item-count#busuanzi_value_site_uv
|
.item-count#busuanzi_value_site_uv
|
||||||
i.fa-solid.fa-spinner.fa-spin
|
i.fa-solid.fa-spinner.fa-spin
|
||||||
if theme.umami_analytics.enable && theme.umami_analytics.UV_PV.site_pv
|
if theme.umami_analytics.enable && theme.umami_analytics.UV_PV.site_pv
|
||||||
.webinfo-item
|
.webinfo-item
|
||||||
.item-name= _p('aside.card_webinfo.site_pv_name') + " :"
|
.item-name= `${_p('aside.card_webinfo.site_pv_name')} :`
|
||||||
.item-count#umami-site-pv
|
.item-count#umami-site-pv
|
||||||
i.fa-solid.fa-spinner.fa-spin
|
i.fa-solid.fa-spinner.fa-spin
|
||||||
else if theme.busuanzi.site_pv
|
else if theme.busuanzi.site_pv
|
||||||
.webinfo-item
|
.webinfo-item
|
||||||
.item-name= _p('aside.card_webinfo.site_pv_name') + " :"
|
.item-name= `${_p('aside.card_webinfo.site_pv_name')} :`
|
||||||
.item-count#busuanzi_value_site_pv
|
.item-count#busuanzi_value_site_pv
|
||||||
i.fa-solid.fa-spinner.fa-spin
|
i.fa-solid.fa-spinner.fa-spin
|
||||||
if theme.aside.card_webinfo.last_push_date
|
if theme.aside.card_webinfo.last_push_date
|
||||||
.webinfo-item
|
.webinfo-item
|
||||||
.item-name= _p('aside.card_webinfo.last_push_date.name') + " :"
|
.item-name= `${_p('aside.card_webinfo.last_push_date.name')} :`
|
||||||
.item-count#last-push-date(data-lastPushDate=date_xml(Date.now()))
|
.item-count#last-push-date(data-lastPushDate=date_xml(Date.now()))
|
||||||
i.fa-solid.fa-spinner.fa-spin
|
i.fa-solid.fa-spinner.fa-spin
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hexo-theme-butterfly",
|
"name": "hexo-theme-butterfly",
|
||||||
"version": "5.3.0-b2",
|
"version": "5.3.0-b3",
|
||||||
"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": {
|
||||||
|
|||||||
24
plugins.yml
24
plugins.yml
@@ -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.12.0
|
version: 5.16.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.4.6
|
version: 4.4.7
|
||||||
clickShowText:
|
clickShowText:
|
||||||
name: butterfly-extsrc
|
name: butterfly-extsrc
|
||||||
file: dist/click-show-text.min.js
|
file: dist/click-show-text.min.js
|
||||||
@@ -66,12 +66,12 @@ 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: 3.6.3
|
version: 3.8.0
|
||||||
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: 3.6.3
|
version: 3.8.0
|
||||||
egjs_infinitegrid:
|
egjs_infinitegrid:
|
||||||
name: '@egjs/infinitegrid'
|
name: '@egjs/infinitegrid'
|
||||||
other_name: egjs-infinitegrid
|
other_name: egjs-infinitegrid
|
||||||
@@ -95,7 +95,7 @@ fontawesome:
|
|||||||
name: '@fortawesome/fontawesome-free'
|
name: '@fortawesome/fontawesome-free'
|
||||||
file: css/all.min.css
|
file: css/all.min.css
|
||||||
other_name: font-awesome
|
other_name: font-awesome
|
||||||
version: 6.6.0
|
version: 6.7.1
|
||||||
gitalk:
|
gitalk:
|
||||||
name: gitalk
|
name: gitalk
|
||||||
file: dist/gitalk.min.js
|
file: dist/gitalk.min.js
|
||||||
@@ -111,17 +111,17 @@ instantpage:
|
|||||||
instantsearch:
|
instantsearch:
|
||||||
name: instantsearch.js
|
name: instantsearch.js
|
||||||
file: dist/instantsearch.production.min.js
|
file: dist/instantsearch.production.min.js
|
||||||
version: 4.75.3
|
version: 4.75.6
|
||||||
katex:
|
katex:
|
||||||
name: katex
|
name: katex
|
||||||
file: dist/katex.min.css
|
file: dist/katex.min.css
|
||||||
other_name: KaTeX
|
other_name: KaTeX
|
||||||
version: 0.16.11
|
version: 0.16.15
|
||||||
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.11
|
version: 0.16.15
|
||||||
lazyload:
|
lazyload:
|
||||||
name: vanilla-lazyload
|
name: vanilla-lazyload
|
||||||
file: dist/lazyload.iife.min.js
|
file: dist/lazyload.iife.min.js
|
||||||
@@ -137,7 +137,7 @@ medium_zoom:
|
|||||||
mermaid:
|
mermaid:
|
||||||
name: mermaid
|
name: mermaid
|
||||||
file: dist/mermaid.min.js
|
file: dist/mermaid.min.js
|
||||||
version: 11.4.0
|
version: 11.4.1
|
||||||
meting_js:
|
meting_js:
|
||||||
name: butterfly-extsrc
|
name: butterfly-extsrc
|
||||||
file: metingjs/dist/Meting.min.js
|
file: metingjs/dist/Meting.min.js
|
||||||
@@ -190,7 +190,7 @@ snackbar_css:
|
|||||||
twikoo:
|
twikoo:
|
||||||
name: twikoo
|
name: twikoo
|
||||||
file: dist/twikoo.all.min.js
|
file: dist/twikoo.all.min.js
|
||||||
version: 1.6.39
|
version: 1.6.40
|
||||||
typed:
|
typed:
|
||||||
name: typed.js
|
name: typed.js
|
||||||
file: dist/typed.umd.js
|
file: dist/typed.umd.js
|
||||||
@@ -203,9 +203,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.3.2
|
version: 3.4.1
|
||||||
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.3.2
|
version: 3.4.1
|
||||||
|
|||||||
@@ -1,11 +1,30 @@
|
|||||||
/**
|
/**
|
||||||
* Butterfly
|
* Random cover for posts
|
||||||
* ramdom cover
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
hexo.extend.filter.register('before_post_render', data => {
|
hexo.extend.generator.register('post', locals => {
|
||||||
|
const recentCovers = []
|
||||||
|
const randomCoverFn = () => {
|
||||||
|
const { cover: { default_cover: defaultCover } } = hexo.theme.config
|
||||||
|
if (!defaultCover) return false
|
||||||
|
if (!Array.isArray(defaultCover)) return defaultCover
|
||||||
|
const defaultCoverLen = defaultCover.length
|
||||||
|
const limit = 3
|
||||||
|
|
||||||
|
let num
|
||||||
|
do {
|
||||||
|
num = Math.floor(Math.random() * defaultCoverLen)
|
||||||
|
} while (recentCovers.includes(num))
|
||||||
|
|
||||||
|
recentCovers.push(num)
|
||||||
|
if (recentCovers.length > limit) recentCovers.shift()
|
||||||
|
|
||||||
|
return defaultCover[num]
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleImg = data => {
|
||||||
const imgTestReg = /\.(png|jpe?g|gif|svg|webp|avif)(\?.*)?$/i
|
const imgTestReg = /\.(png|jpe?g|gif|svg|webp|avif)(\?.*)?$/i
|
||||||
let { cover: coverVal, top_img: topImg } = data
|
let { cover: coverVal, top_img: topImg } = data
|
||||||
|
|
||||||
@@ -15,14 +34,6 @@ hexo.extend.filter.register('before_post_render', data => {
|
|||||||
if (coverVal && coverVal.indexOf('/') === -1 && imgTestReg.test(coverVal)) data.cover = `${data.path}${coverVal}`
|
if (coverVal && coverVal.indexOf('/') === -1 && imgTestReg.test(coverVal)) data.cover = `${data.path}${coverVal}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const randomCoverFn = () => {
|
|
||||||
const { cover: { default_cover: defaultCover } } = hexo.theme.config
|
|
||||||
if (!defaultCover) return false
|
|
||||||
if (!Array.isArray(defaultCover)) return defaultCover
|
|
||||||
const num = Math.floor(Math.random() * defaultCover.length)
|
|
||||||
return defaultCover[num]
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
||||||
@@ -37,4 +48,13 @@ hexo.extend.filter.register('before_post_render', data => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
return locals.posts.sort('date').map(post => {
|
||||||
|
return {
|
||||||
|
data: handleImg(post),
|
||||||
|
layout: 'post',
|
||||||
|
path: post.path
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -2,38 +2,68 @@
|
|||||||
|
|
||||||
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, _p } = this
|
||||||
const archiveDir = config.archive_dir
|
const {
|
||||||
const { timezone } = config
|
archive_dir: archiveDir,
|
||||||
const lang = toMomentLocale(page.lang || page.language || config.language)
|
timezone,
|
||||||
const type = options.type || 'monthly'
|
language
|
||||||
const format = options.format || (type === 'monthly' ? 'MMMM YYYY' : 'YYYY')
|
} = config
|
||||||
const showCount = Object.prototype.hasOwnProperty.call(options, 'show_count') ? options.show_count : true
|
|
||||||
const order = options.order || -1
|
// Destructure and set default options with object destructuring
|
||||||
const limit = options.limit
|
const {
|
||||||
|
type = 'monthly',
|
||||||
|
format = type === 'monthly' ? 'MMMM YYYY' : 'YYYY',
|
||||||
|
show_count: showCount = true,
|
||||||
|
order = -1,
|
||||||
|
limit,
|
||||||
|
transform
|
||||||
|
} = options
|
||||||
|
|
||||||
|
// Optimize locale handling
|
||||||
|
const lang = toMomentLocale(page.lang || page.language || language)
|
||||||
|
|
||||||
|
// Memoize comparison function to improve performance
|
||||||
const compareFunc = type === 'monthly'
|
const compareFunc = type === 'monthly'
|
||||||
? (yearA, monthA, yearB, monthB) => yearA === yearB && monthA === monthB
|
? (yearA, monthA, yearB, monthB) => yearA === yearB && monthA === monthB
|
||||||
: (yearA, monthA, yearB, monthB) => yearA === yearB
|
: (yearA, yearB) => yearA === yearB
|
||||||
|
|
||||||
const posts = site.posts.sort('date', order)
|
// Early return if no posts
|
||||||
if (!posts.length) return ''
|
if (!site.posts.length) return ''
|
||||||
|
|
||||||
const data = []
|
// Use reduce for more efficient data processing
|
||||||
posts.forEach(post => {
|
const data = site.posts
|
||||||
|
.sort('date', order)
|
||||||
|
.reduce((acc, post) => {
|
||||||
let date = post.date.clone()
|
let date = post.date.clone()
|
||||||
if (timezone) date = date.tz(timezone)
|
if (timezone) date = date.tz(timezone)
|
||||||
|
|
||||||
const year = date.year()
|
const year = date.year()
|
||||||
const month = date.month() + 1
|
const month = date.month() + 1
|
||||||
|
|
||||||
if (!data.length || !compareFunc(data[data.length - 1].year, data[data.length - 1].month, year, month)) {
|
|
||||||
if (lang) date = date.locale(lang)
|
if (lang) date = date.locale(lang)
|
||||||
data.push({ name: date.format(format), year, month, count: 1 })
|
|
||||||
} else {
|
|
||||||
data[data.length - 1].count++
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const link = item => {
|
// Find or create archive entry
|
||||||
|
const lastEntry = acc[acc.length - 1]
|
||||||
|
if (!lastEntry || !compareFunc(
|
||||||
|
lastEntry.year,
|
||||||
|
lastEntry.month,
|
||||||
|
year,
|
||||||
|
month
|
||||||
|
)) {
|
||||||
|
acc.push({
|
||||||
|
name: date.format(format),
|
||||||
|
year,
|
||||||
|
month,
|
||||||
|
count: 1
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
lastEntry.count++
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// Create link generator function
|
||||||
|
const createArchiveLink = item => {
|
||||||
let url = `${archiveDir}/${item.year}/`
|
let url = `${archiveDir}/${item.year}/`
|
||||||
if (type === 'monthly') {
|
if (type === 'monthly') {
|
||||||
url += item.month < 10 ? `0${item.month}/` : `${item.month}/`
|
url += item.month < 10 ? `0${item.month}/` : `${item.month}/`
|
||||||
@@ -41,37 +71,48 @@ hexo.extend.helper.register('aside_archives', function (options = {}) {
|
|||||||
return url_for(url)
|
return url_for(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
const len = data.length
|
// Limit results efficiently
|
||||||
const limitLength = limit === 0 ? len : Math.min(len, limit)
|
const limitedData = limit > 0
|
||||||
|
? data.slice(0, Math.min(data.length, limit))
|
||||||
|
: data
|
||||||
|
|
||||||
let result = `
|
// Use template literal for better readability
|
||||||
|
const archiveHeader = `
|
||||||
<div class="item-headline">
|
<div class="item-headline">
|
||||||
<i class="fas fa-archive"></i>
|
<i class="fas fa-archive"></i>
|
||||||
<span>${_p('aside.card_archives')}</span>
|
<span>${_p('aside.card_archives')}</span>
|
||||||
${len > limitLength ? `<a class="card-more-btn" href="${url_for(archiveDir)}/" title="${_p('aside.more_button')}"><i class="fas fa-angle-right"></i></a>` : ''}
|
${data.length > limitedData.length
|
||||||
|
? `<a class="card-more-btn" href="${url_for(archiveDir)}/"
|
||||||
|
title="${_p('aside.more_button')}">
|
||||||
|
<i class="fas fa-angle-right"></i>
|
||||||
|
</a>`
|
||||||
|
: ''}
|
||||||
</div>
|
</div>
|
||||||
<ul class="card-archive-list">
|
|
||||||
`
|
`
|
||||||
|
|
||||||
for (let i = 0; i < limitLength; i++) {
|
// Use map for generating list items, join for performance
|
||||||
const item = data[i]
|
const archiveList = `
|
||||||
result += `
|
<ul class="card-archive-list">
|
||||||
|
${limitedData.map(item => `
|
||||||
<li class="card-archive-list-item">
|
<li class="card-archive-list-item">
|
||||||
<a class="card-archive-list-link" href="${link(item)}">
|
<a class="card-archive-list-link" href="${createArchiveLink(item)}">
|
||||||
<span class="card-archive-list-date">${options.transform ? options.transform(item.name) : item.name}</span>
|
<span class="card-archive-list-date">
|
||||||
${showCount ? `<span class="card-archive-list-count">${item.count}</span>` : ''}
|
${transform ? transform(item.name) : item.name}
|
||||||
|
</span>
|
||||||
|
${showCount
|
||||||
|
? `<span class="card-archive-list-count">${item.count}</span>`
|
||||||
|
: ''}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
`).join('')}
|
||||||
|
</ul>
|
||||||
`
|
`
|
||||||
}
|
|
||||||
|
|
||||||
result += '</ul>'
|
return archiveHeader + archiveList
|
||||||
return result
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const toMomentLocale = function (lang) {
|
// Improved locale conversion function
|
||||||
if (!lang || lang === 'en' || lang === 'default') {
|
const toMomentLocale = lang => {
|
||||||
return 'en'
|
if (!lang || ['en', 'default'].includes(lang)) return 'en'
|
||||||
}
|
|
||||||
return lang.toLowerCase().replace('_', '-')
|
return lang.toLowerCase().replace('_', '-')
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* galleryGroup and gallery
|
* galleryGroup and gallery
|
||||||
* {% galleryGroup [name] [descr] [url] [img] %}
|
* {% galleryGroup [name] [descr] [url] [img] %}
|
||||||
*
|
*
|
||||||
* {% gallery [button] %}
|
* {% gallery [button],[limit],[firstLimit] %}
|
||||||
* {% gallery url,[url],[button] %}
|
* {% gallery url,[url],[button] %}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -11,54 +11,66 @@
|
|||||||
|
|
||||||
const urlFor = require('hexo-util').url_for.bind(hexo)
|
const urlFor = require('hexo-util').url_for.bind(hexo)
|
||||||
|
|
||||||
const gallery = (args, content) => {
|
const DEFAULT_LIMIT = 10
|
||||||
args = args.join(' ').split(',')
|
const DEFAULT_FIRST_LIMIT = 10
|
||||||
let button = false
|
const IMAGE_REGEX = /!\[(.*?)\]\(([^\s]*)\s*(?:["'](.*?)["']?)?\s*\)/g
|
||||||
let type = 'data'
|
|
||||||
let dataStr = ''
|
|
||||||
|
|
||||||
if (args[0] === 'url') {
|
// Helper functions
|
||||||
[type, dataStr, button] = args // url,[link],[lazyload]
|
const parseGalleryArgs = args => {
|
||||||
dataStr = urlFor(dataStr)
|
const [type, ...rest] = args.join(' ').split(',').map(arg => arg.trim())
|
||||||
} else {
|
return {
|
||||||
[button] = args // [lazyload]
|
isUrl: type === 'url',
|
||||||
const regex = /!\[(.*?)\]\(([^\s]*)\s*(?:["'](.*?)["']?)?\s*\)/g
|
params: type === 'url' ? rest : [type, ...rest]
|
||||||
let m
|
|
||||||
const arr = []
|
|
||||||
while ((m = regex.exec(content)) !== null) {
|
|
||||||
if (m.index === regex.lastIndex) {
|
|
||||||
regex.lastIndex++
|
|
||||||
}
|
}
|
||||||
arr.push({
|
}
|
||||||
url: m[2],
|
|
||||||
alt: m[1],
|
const parseImageContent = content => {
|
||||||
title: m[3]
|
const images = []
|
||||||
|
let match
|
||||||
|
|
||||||
|
while ((match = IMAGE_REGEX.exec(content)) !== null) {
|
||||||
|
images.push({
|
||||||
|
url: match[2],
|
||||||
|
alt: match[1] || '',
|
||||||
|
title: match[3] || ''
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
dataStr = JSON.stringify(arr)
|
return images
|
||||||
}
|
}
|
||||||
|
|
||||||
return `<div class="gallery-container" data-type="${type}" data-button="${button}">
|
const createGalleryHTML = (type, dataStr, button, limit, firstLimit) => {
|
||||||
|
return `<div class="gallery-container" data-type="${type}" data-button="${button}" data-limit="${limit}" data-first="${firstLimit}">
|
||||||
<div class="gallery-items">${dataStr}</div>
|
<div class="gallery-items">${dataStr}</div>
|
||||||
</div>`
|
</div>`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const gallery = (args, content) => {
|
||||||
|
const { isUrl, params } = parseGalleryArgs(args)
|
||||||
|
|
||||||
|
if (isUrl) {
|
||||||
|
const [dataStr, button = false, limit = DEFAULT_LIMIT, firstLimit = DEFAULT_FIRST_LIMIT] = params
|
||||||
|
return createGalleryHTML('url', urlFor(dataStr), button, limit, firstLimit)
|
||||||
|
}
|
||||||
|
|
||||||
|
const [button = false, limit = DEFAULT_LIMIT, firstLimit = DEFAULT_FIRST_LIMIT] = params
|
||||||
|
const images = parseImageContent(content)
|
||||||
|
return createGalleryHTML('data', JSON.stringify(images), button, limit, firstLimit)
|
||||||
|
}
|
||||||
|
|
||||||
const galleryGroup = args => {
|
const galleryGroup = args => {
|
||||||
const [name, descr, url, img] = args
|
const [name = '', descr = '', url = '', img = ''] = args.map(arg => arg.trim())
|
||||||
const imgUrl = urlFor(img)
|
|
||||||
const urlLink = urlFor(url)
|
|
||||||
|
|
||||||
return `<figure class="gallery-group">
|
return `<figure class="gallery-group">
|
||||||
<img class="gallery-group-img no-lightbox" src='${imgUrl}' alt="Group Image Gallery">
|
<img class="gallery-group-img no-lightbox" src='${urlFor(img)}' alt="Group Image Gallery">
|
||||||
<figcaption>
|
<figcaption>
|
||||||
<div class="gallery-group-name">${name}</div>
|
<div class="gallery-group-name">${name}</div>
|
||||||
<p>${descr}</p>
|
<p>${descr}</p>
|
||||||
<a href='${urlLink}'></a>
|
<a href='${urlFor(url)}'></a>
|
||||||
</figcaption>
|
</figcaption>
|
||||||
</figure>
|
</figure>`
|
||||||
`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register tags
|
||||||
hexo.extend.tag.register('gallery', gallery, { ends: true })
|
hexo.extend.tag.register('gallery', gallery, { ends: true })
|
||||||
hexo.extend.tag.register('galleryGroup', galleryGroup)
|
hexo.extend.tag.register('galleryGroup', galleryGroup)
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
/**
|
/**
|
||||||
* inlineImg 圖片
|
* inlineImg
|
||||||
* @param {Array} args 圖片名稱和高度
|
* @param {Array} args - Image name and height
|
||||||
* @param {string} args[0] 圖片名稱
|
* @param {string} args[0] - Image name
|
||||||
* @param {number} args[1] 圖片高度
|
* @param {number} args[1] - Image height
|
||||||
* @returns {string} 圖片標籤
|
* @returns {string} - Image tag
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|||||||
@@ -1,41 +1,50 @@
|
|||||||
/**
|
/**
|
||||||
* timeline
|
* Timeline tag for Hexo
|
||||||
* by Jerry
|
* Syntax:
|
||||||
|
* {% timeline [headline],[color] %}
|
||||||
|
* <!-- timeline [title] -->
|
||||||
|
* [content]
|
||||||
|
* <!-- endtimeline -->
|
||||||
|
* <!-- timeline [title] -->
|
||||||
|
* [content]
|
||||||
|
* <!-- endtimeline -->
|
||||||
|
* {% endtimeline %}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
const timeLineFn = (args, content) => {
|
const timeLineFn = (args, content) => {
|
||||||
const tlBlock = /<!--\s*timeline (.*?)\s*-->\n([\w\W\s\S]*?)<!--\s*endtimeline\s*-->/g
|
// Use named capture groups for better readability
|
||||||
|
const tlBlock = /<!--\s*timeline\s*(?<title>.*?)\s*-->\n(?<content>[\s\S]*?)<!--\s*endtimeline\s*-->/g
|
||||||
|
|
||||||
let result = ''
|
// Pre-compile markdown render function
|
||||||
let color = ''
|
const renderMd = text => hexo.render.renderSync({ text, engine: 'markdown' })
|
||||||
let text = ''
|
|
||||||
if (args.length) {
|
|
||||||
[text, color] = args.join(' ').split(',')
|
|
||||||
const mdContent = hexo.render.renderSync({ text, engine: 'markdown' })
|
|
||||||
result += `<div class='timeline-item headline'><div class='timeline-item-title'><div class='item-circle'>${mdContent}</div></div></div>`
|
|
||||||
}
|
|
||||||
|
|
||||||
const matches = []
|
// Parse arguments more efficiently
|
||||||
let match
|
const [text, color = ''] = args.length ? args.join(' ').split(',') : []
|
||||||
|
|
||||||
while ((match = tlBlock.exec(content)) !== null) {
|
// Build initial headline if text exists
|
||||||
matches.push(match[1])
|
const headline = text
|
||||||
matches.push(match[2])
|
? `<div class='timeline-item headline'>
|
||||||
}
|
<div class='timeline-item-title'>
|
||||||
|
<div class='item-circle'>${renderMd(text)}</div>
|
||||||
|
</div>
|
||||||
|
</div>`
|
||||||
|
: ''
|
||||||
|
|
||||||
for (let i = 0; i < matches.length; i += 2) {
|
// Match all timeline blocks in one pass and transform
|
||||||
const tlChildTitle = hexo.render.renderSync({ text: matches[i], engine: 'markdown' })
|
const items = Array.from(content.matchAll(tlBlock))
|
||||||
const tlChildContent = hexo.render.renderSync({ text: matches[i + 1], engine: 'markdown' })
|
.map(({ groups: { title, content } }) =>
|
||||||
|
`<div class='timeline-item'>
|
||||||
|
<div class='timeline-item-title'>
|
||||||
|
<div class='item-circle'>${renderMd(title)}</div>
|
||||||
|
</div>
|
||||||
|
<div class='timeline-item-content'>${renderMd(content)}</div>
|
||||||
|
</div>`
|
||||||
|
)
|
||||||
|
.join('')
|
||||||
|
|
||||||
const tlTitleHtml = `<div class='timeline-item-title'><div class='item-circle'>${tlChildTitle}</div></div>`
|
return `<div class="timeline ${color}">${headline}${items}</div>`
|
||||||
const tlContentHtml = `<div class='timeline-item-content'>${tlChildContent}</div>`
|
|
||||||
|
|
||||||
result += `<div class='timeline-item'>${tlTitleHtml + tlContentHtml}</div>`
|
|
||||||
}
|
|
||||||
|
|
||||||
return `<div class="timeline ${color || ''}">${result}</div>`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hexo.extend.tag.register('timeline', timeLineFn, { ends: true })
|
hexo.extend.tag.register('timeline', timeLineFn, { ends: true })
|
||||||
|
|||||||
@@ -224,14 +224,23 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
const fetchUrl = async url => {
|
const fetchUrl = async url => {
|
||||||
|
try {
|
||||||
const response = await fetch(url)
|
const response = await fetch(url)
|
||||||
return await response.json()
|
return await response.json()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to fetch URL:', error)
|
||||||
|
return []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const runJustifiedGallery = (item, data, isButton = false, tabs) => {
|
const runJustifiedGallery = (container, data, config) => {
|
||||||
const dataLength = data.length
|
const { isButton, limit, firstLimit, tabs } = config
|
||||||
|
|
||||||
const ig = new InfiniteGrid.JustifiedInfiniteGrid(item, {
|
const dataLength = data.length
|
||||||
|
const maxGroupKey = Math.ceil((dataLength - firstLimit) / limit + 1)
|
||||||
|
|
||||||
|
// Gallery configuration
|
||||||
|
const igConfig = {
|
||||||
gap: 5,
|
gap: 5,
|
||||||
isConstantSize: true,
|
isConstantSize: true,
|
||||||
sizeRange: [150, 600],
|
sizeRange: [150, 600],
|
||||||
@@ -239,132 +248,130 @@ document.addEventListener('DOMContentLoaded', () => {
|
|||||||
// observeChildren: true,
|
// observeChildren: true,
|
||||||
useTransform: true
|
useTransform: true
|
||||||
// useRecycle: false
|
// useRecycle: false
|
||||||
})
|
|
||||||
|
|
||||||
const replaceDq = str => str.replace(/"/g, '"') // replace double quotes to "
|
|
||||||
|
|
||||||
const getItems = (nextGroupKey, count) => {
|
|
||||||
const nextItems = []
|
|
||||||
const startCount = (nextGroupKey - 1) * count
|
|
||||||
|
|
||||||
for (let i = 0; i < count; ++i) {
|
|
||||||
const num = startCount + i
|
|
||||||
if (num >= dataLength) {
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const item = data[num]
|
const ig = new InfiniteGrid.JustifiedInfiniteGrid(container, igConfig)
|
||||||
const alt = item.alt ? `alt="${replaceDq(item.alt)}"` : ''
|
|
||||||
const title = item.title ? `title="${replaceDq(item.title)}"` : ''
|
|
||||||
|
|
||||||
nextItems.push(`<div class="item">
|
|
||||||
<img src="${item.url}" data-grid-maintained-target="true" ${alt + title} />
|
|
||||||
</div>`)
|
|
||||||
}
|
|
||||||
return nextItems
|
|
||||||
}
|
|
||||||
|
|
||||||
const buttonText = GLOBAL_CONFIG.infinitegrid.buttonText
|
|
||||||
const addButton = item => {
|
|
||||||
const button = document.createElement('button')
|
|
||||||
button.innerHTML = buttonText + '<i class="fa-solid fa-arrow-down"></i>'
|
|
||||||
|
|
||||||
button.addEventListener('click', e => {
|
|
||||||
e.target.closest('button').remove()
|
|
||||||
btf.setLoading.add(item)
|
|
||||||
appendItem(ig.getGroups().length + 1, 10)
|
|
||||||
}, { once: true })
|
|
||||||
|
|
||||||
item.insertAdjacentElement('afterend', button)
|
|
||||||
}
|
|
||||||
|
|
||||||
const appendItem = (nextGroupKey, count) => {
|
|
||||||
ig.append(getItems(nextGroupKey, count), nextGroupKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxGroupKey = Math.ceil(dataLength / 10)
|
|
||||||
let isLayoutHidden = false
|
let isLayoutHidden = false
|
||||||
|
|
||||||
const completeFn = e => {
|
// Utility functions
|
||||||
if (tabs) {
|
const sanitizeString = str => (str && str.replace(/"/g, '"')) || ''
|
||||||
const parentNode = item.parentNode
|
|
||||||
|
|
||||||
|
const createImageItem = item => {
|
||||||
|
const alt = item.alt ? `alt="${sanitizeString(item.alt)}"` : ''
|
||||||
|
const title = item.title ? `title="${sanitizeString(item.title)}"` : ''
|
||||||
|
return `<div class="item">
|
||||||
|
<img src="${item.url}" data-grid-maintained-target="true" ${alt} ${title} />
|
||||||
|
</div>`
|
||||||
|
}
|
||||||
|
|
||||||
|
const getItems = (nextGroupKey, count, isFirst = false) => {
|
||||||
|
const startIndex = isFirst ? (nextGroupKey - 1) * count : (nextGroupKey - 2) * count + firstLimit
|
||||||
|
return data.slice(startIndex, startIndex + count).map(createImageItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load more button
|
||||||
|
const addLoadMoreButton = container => {
|
||||||
|
const button = document.createElement('button')
|
||||||
|
button.innerHTML = `${GLOBAL_CONFIG.infinitegrid.buttonText}<i class="fa-solid fa-arrow-down"></i>`
|
||||||
|
|
||||||
|
button.addEventListener('click', () => {
|
||||||
|
button.remove()
|
||||||
|
btf.setLoading.add(container)
|
||||||
|
appendItems(ig.getGroups().length + 1, limit)
|
||||||
|
}, { once: true })
|
||||||
|
|
||||||
|
container.insertAdjacentElement('afterend', button)
|
||||||
|
}
|
||||||
|
|
||||||
|
const appendItems = (nextGroupKey, count, isFirst) => {
|
||||||
|
ig.append(getItems(nextGroupKey, count, isFirst), nextGroupKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Event handlers
|
||||||
|
const handleRenderComplete = e => {
|
||||||
|
if (tabs) {
|
||||||
|
const parentNode = container.parentNode
|
||||||
if (isLayoutHidden) {
|
if (isLayoutHidden) {
|
||||||
parentNode.style.visibility = 'visible'
|
parentNode.style.visibility = 'visible'
|
||||||
}
|
}
|
||||||
|
if (container.offsetHeight === 0) {
|
||||||
if (item.offsetHeight === 0) {
|
|
||||||
parentNode.style.visibility = 'hidden'
|
parentNode.style.visibility = 'hidden'
|
||||||
isLayoutHidden = true
|
isLayoutHidden = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const { updated, isResize, mounted } = e
|
const { updated, isResize, mounted } = e
|
||||||
if (!updated.length || !mounted.length || isResize) {
|
if (!updated.length || !mounted.length || isResize) return
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
btf.loadLightbox(item.querySelectorAll('img:not(.medium-zoom-image)'))
|
btf.loadLightbox(container.querySelectorAll('img:not(.medium-zoom-image)'))
|
||||||
|
|
||||||
if (ig.getGroups().length === maxGroupKey) {
|
if (ig.getGroups().length === maxGroupKey) {
|
||||||
btf.setLoading.remove(item)
|
btf.setLoading.remove(container)
|
||||||
!tabs && ig.off('renderComplete', completeFn)
|
!tabs && ig.off('renderComplete', handleRenderComplete)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isButton) {
|
if (isButton) {
|
||||||
btf.setLoading.remove(item)
|
btf.setLoading.remove(container)
|
||||||
addButton(item)
|
addLoadMoreButton(container)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const requestAppendFn = btf.debounce(e => {
|
const handleRequestAppend = btf.debounce(e => {
|
||||||
const nextGroupKey = (+e.groupKey || 0) + 1
|
const nextGroupKey = (+e.groupKey || 0) + 1
|
||||||
appendItem(nextGroupKey, 10)
|
|
||||||
|
|
||||||
if (nextGroupKey === maxGroupKey) {
|
if (nextGroupKey === 1) appendItems(nextGroupKey, firstLimit, true)
|
||||||
ig.off('requestAppend', requestAppendFn)
|
else appendItems(nextGroupKey, limit)
|
||||||
}
|
|
||||||
|
if (nextGroupKey === maxGroupKey) ig.off('requestAppend', handleRequestAppend)
|
||||||
}, 300)
|
}, 300)
|
||||||
|
|
||||||
btf.setLoading.add(item)
|
btf.setLoading.add(container)
|
||||||
ig.on('renderComplete', completeFn)
|
ig.on('renderComplete', handleRenderComplete)
|
||||||
|
|
||||||
if (isButton) {
|
if (isButton) {
|
||||||
appendItem(1, 10)
|
appendItems(1, firstLimit, true)
|
||||||
} else {
|
} else {
|
||||||
ig.on('requestAppend', requestAppendFn)
|
ig.on('requestAppend', handleRequestAppend)
|
||||||
ig.renderItems()
|
ig.renderItems()
|
||||||
}
|
}
|
||||||
|
|
||||||
btf.addGlobalFn('pjaxSendOnce', () => { ig.destroy() })
|
btf.addGlobalFn('pjaxSendOnce', () => ig.destroy())
|
||||||
}
|
}
|
||||||
|
|
||||||
const addJustifiedGallery = async (ele, tabs = false) => {
|
const addJustifiedGallery = async (elements, tabs = false) => {
|
||||||
if (!ele.length) return
|
if (!elements.length) return
|
||||||
const init = async () => {
|
|
||||||
for (const item of ele) {
|
const initGallery = async () => {
|
||||||
if (btf.isHidden(item) || item.classList.contains('loaded')) continue
|
for (const element of elements) {
|
||||||
|
if (btf.isHidden(element) || element.classList.contains('loaded')) continue
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
isButton: element.getAttribute('data-button') === 'true',
|
||||||
|
limit: parseInt(element.getAttribute('data-limit'), 10),
|
||||||
|
firstLimit: parseInt(element.getAttribute('data-first'), 10),
|
||||||
|
tabs
|
||||||
|
}
|
||||||
|
|
||||||
|
const container = element.firstElementChild
|
||||||
|
const content = container.textContent
|
||||||
|
container.textContent = ''
|
||||||
|
element.classList.add('loaded')
|
||||||
|
|
||||||
const isButton = item.getAttribute('data-button') === 'true'
|
|
||||||
const children = item.firstElementChild
|
|
||||||
const text = children.textContent
|
|
||||||
children.textContent = ''
|
|
||||||
item.classList.add('loaded')
|
|
||||||
try {
|
try {
|
||||||
const content = item.getAttribute('data-type') === 'url' ? await fetchUrl(text) : JSON.parse(text)
|
const data = element.getAttribute('data-type') === 'url' ? await fetchUrl(content) : JSON.parse(content)
|
||||||
runJustifiedGallery(children, content, isButton, tabs)
|
runJustifiedGallery(container, data, config)
|
||||||
} catch (e) {
|
} catch (error) {
|
||||||
console.error('Gallery data parsing failed:', e)
|
console.error('Gallery data parsing failed:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof InfiniteGrid === 'function') {
|
if (typeof InfiniteGrid === 'function') {
|
||||||
init()
|
await initGallery()
|
||||||
} else {
|
} else {
|
||||||
await btf.getScript(`${GLOBAL_CONFIG.infinitegrid.js}`)
|
await btf.getScript(GLOBAL_CONFIG.infinitegrid.js)
|
||||||
init()
|
await initGallery()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user