diff --git a/README.md b/README.md index 2ab160e1..ef3512c9 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ npm i hexo-theme-butterfly - [x] Share (Sharejs/Addtoany) - [X] Comment (Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus/Remark42/artalk) - [x] Multiple Comment System Support -- [x] Online Chats (Chatra/Tidio/Daovoice/Crisp) +- [x] Online Chats (Chatra/Tidio/Crisp) - [x] Web analytics - [x] Google AdSense - [x] Webmaster Verification @@ -101,7 +101,7 @@ npm i hexo-theme-butterfly - [x] Chart.js - [x] Justified Gallery - [x] Lazyload images -- [x] Instantpage/Pangu/Snackbar notification toast/PWA...... +- [x] Instantpage/Snackbar notification toast/PWA...... ## ✨ Contributors diff --git a/README_CN.md b/README_CN.md index 16d17acf..2b0e07d8 100644 --- a/README_CN.md +++ b/README_CN.md @@ -86,7 +86,7 @@ theme: butterfly - [x] 多種分享系統(Sharejs/Addtoany) - [X] 多種評論系統(Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus/Remark42/artalk) - [x] 支持雙評論部署 -- [x] 多種在線聊天(Chatra/Tidio/Daovoice/Crisp) +- [x] 多種在線聊天(Chatra/Tidio/Crisp) - [x] 多種分析系統 - [x] 谷歌廣告/手動廣告位置 - [x] 各種站長驗證 @@ -101,7 +101,7 @@ theme: butterfly - [x] Chart.js 圖表顯示 - [x] 照片牆 - [x] 圖片懶加載 -- [x] Instantpage/Pangu/Snackbar彈窗/PWA...... +- [x] Instantpage/Snackbar彈窗/PWA...... ## ✨ 貢獻者 diff --git a/_config.yml b/_config.yml index 32c56dea..0ec83709 100644 --- a/_config.yml +++ b/_config.yml @@ -645,7 +645,7 @@ artalk: # -------------------------------------- chat: - # Choose: chatra/tidio/daovoice/crisp + # Choose: chatra/tidio/crisp # Leave it empty if you don't need chat use: # Chat Button [recommend] @@ -662,10 +662,6 @@ chatra: tidio: public_key: -# http://dashboard.daovoice.io/app -daovoice: - app_id: - # https://crisp.chat/en/ crisp: website_id: @@ -932,16 +928,16 @@ chartjs: # The default settings are only used when the MD syntax is not specified. # General font color for the chart fontColor: - light: "rgba(0, 0, 0, 0.8)" - dark: "rgba(255, 255, 255, 0.8)" + light: 'rgba(0, 0, 0, 0.8)' + dark: 'rgba(255, 255, 255, 0.8)' # General border color for the chart borderColor: - light: "rgba(0, 0, 0, 0.1)" - dark: "rgba(255, 255, 255, 0.2)" + light: 'rgba(0, 0, 0, 0.1)' + dark: 'rgba(255, 255, 255, 0.2)' # Background color for scale labels on radar and polar area charts scale_ticks_backdropColor: - light: "transparent" - dark: "transparent" + light: 'transparent' + dark: 'transparent' # Note - Bootstrap Callout note: @@ -987,13 +983,6 @@ snackbar: # https://instant.page/ instantpage: false -# Pangu - Insert a space between Chinese character and English character -# https://github.com/vinta/pangu.js -pangu: - enable: false - # Specify the field to use pangu (site or post) - field: site - # Lazyload # https://github.com/verlok/vanilla-lazyload lazyload: @@ -1027,6 +1016,10 @@ Open_Graph_meta: # fb_admins: # fb_app_id: +# Structured Data +# https://developers.google.com/search/docs/guides/intro-structured-data +structured_data: true + # Add the vendor prefixes to ensure compatibility css_prefix: true @@ -1096,7 +1089,6 @@ CDN: # medium_zoom: # mermaid: # meting_js: - # pangu: # prismjs_autoloader: # prismjs_js: # prismjs_lineNumber_js: diff --git a/layout/includes/additional-js.pug b/layout/includes/additional-js.pug index f946f722..4c93d50d 100644 --- a/layout/includes/additional-js.pug +++ b/layout/includes/additional-js.pug @@ -46,9 +46,6 @@ div if theme.snackbar.enable script(src=url_for(theme.asset.snackbar)) - if theme.pangu.enable - != partial("includes/third-party/pangu.pug", {}, { cache: true }) - .js-pjax if needLoadCountJs != partial("includes/third-party/card-post-count/index", {}, { cache: true }) diff --git a/layout/includes/head.pug b/layout/includes/head.pug index 613a7e27..0028a902 100644 --- a/layout/includes/head.pug +++ b/layout/includes/head.pug @@ -28,6 +28,9 @@ if is_post() //- Open_Graph include ./head/Open_Graph.pug +//- Structured Data +include ./head/structured_data.pug + !=favicon_tag(theme.favicon || config.favicon) link(rel="canonical" href=urlNoIndex(null,config.pretty_urls.trailing_index,config.pretty_urls.trailing_html)) diff --git a/layout/includes/head/config.pug b/layout/includes/head/config.pug index f47a21ff..b23742e1 100644 --- a/layout/includes/head/config.pug +++ b/layout/includes/head/config.pug @@ -69,16 +69,6 @@ }) } - let noticeOutdate = 'undefined' - if (theme.noticeOutdate && theme.noticeOutdate.enable) { - noticeOutdate = JSON.stringify({ - limitDay: theme.noticeOutdate.limit_day, - position: theme.noticeOutdate.position, - messagePrev: theme.noticeOutdate.message_prev, - messageNext: theme.noticeOutdate.message_next, - }) - } - let highlight = 'undefined' let syntaxHighlighter = config.syntax_highlighter let highlightEnable = syntaxHighlighter ? ['highlight.js', 'prismjs'].includes(syntaxHighlighter) : (config.highlight.enable || config.prismjs.enable) @@ -100,7 +90,6 @@ script. algolia: !{algolia}, localSearch: !{localSearch}, translate: !{translate}, - noticeOutdate: !{noticeOutdate}, highlight: !{highlight}, copy: { success: '!{_p("copy.success")}', diff --git a/layout/includes/head/config_site.pug b/layout/includes/head/config_site.pug index f457549f..ccd45dcf 100644 --- a/layout/includes/head/config_site.pug +++ b/layout/includes/head/config_site.pug @@ -23,5 +23,5 @@ script#config-diff. isHome: !{is_home()}, isHighlightShrink: !{isHighlightShrink}, isToc: !{showToc}, - postUpdate: '!{full_date(page.updated)}' + isShuoshuo: !{page.type == 'shuoshuo'} } diff --git a/layout/includes/head/structured_data.pug b/layout/includes/head/structured_data.pug new file mode 100644 index 00000000..cacb02cb --- /dev/null +++ b/layout/includes/head/structured_data.pug @@ -0,0 +1,34 @@ +if theme.structured_data && page.layout === 'post' + - + // use json-ld to add structured data + + const title = page.title + const url = page.permalink + const imageVal = page.cover_type === 'img' ? page.cover : theme.avatar.img + const image = imageVal ? full_url_for(imageVal) : '' + const datePublished = page.date.toISOString() + const dateModified = (page.updated || page.date).toISOString() + const author = page.copyright_author || config.author + const authorHrefVal = page.copyright_author_href || theme.post_copyright.author_href || site.url; + const authorHref = full_url_for(authorHrefVal); + + const jsonLd = { + "@context": "https://schema.org", + "@type": "BlogPosting", + "headline": title, + "url": url, + "image": image, + "datePublished": datePublished, + "dateModified": dateModified, + "author": [{ + "@type": "Person", + "name": author, + "url": authorHref + }] + }; + + jsonLdScript = JSON.stringify(jsonLd, null, 2); + - + + script(type="application/ld+json"). + !{jsonLdScript} diff --git a/layout/includes/mixins/indexPostUI.pug b/layout/includes/mixins/indexPostUI.pug index 7e80df96..4c537047 100644 --- a/layout/includes/mixins/indexPostUI.pug +++ b/layout/includes/mixins/indexPostUI.pug @@ -105,15 +105,9 @@ mixin indexPostUI() i.fa-solid.fa-spinner.fa-spin //- Display the article introduction on homepage - case theme.index_post_content.method - when false - - break - when 1 - .content!= article.description - when 2 - .content!= article.description || truncate(article.content, theme.index_post_content.length) - default - .content!= truncate(article.content, theme.index_post_content.length) + - const content = postDesc(article) + if content + .content!=content if theme.ad && theme.ad.index if (index + 1) % 3 === 0 diff --git a/layout/includes/page/default-page.pug b/layout/includes/page/default-page.pug index e7057f73..4a7d5156 100644 --- a/layout/includes/page/default-page.pug +++ b/layout/includes/page/default-page.pug @@ -1,2 +1,2 @@ -#article-container +#article-container.container != page.content \ No newline at end of file diff --git a/layout/includes/page/flink.pug b/layout/includes/page/flink.pug index 10d3893a..30cdad58 100644 --- a/layout/includes/page/flink.pug +++ b/layout/includes/page/flink.pug @@ -1,4 +1,4 @@ -#article-container +#article-container.container .flink - let { content, random, flink_url } = page - let pageContent = content diff --git a/layout/includes/page/shuoshuo.pug b/layout/includes/page/shuoshuo.pug index 58467926..3bcb01d6 100644 --- a/layout/includes/page/shuoshuo.pug +++ b/layout/includes/page/shuoshuo.pug @@ -6,102 +6,183 @@ //- - tag1 //- - tag2 - -- page.comments = false - page.toc = false + #article-container + if page.comments !== false && theme.comments.use + - commentsJsLoad = true + + script. + (() => { + const commentDiv = `!{partial('includes/third-party/comments/index', {}, {cache: true})}` + + const runDestroy = (shuoshuoComment) => { + if (!shuoshuoComment) return + + for (const [key, fn] of Object.entries(shuoshuoComment)) { + if (key.startsWith('destroy')) fn() + } + } + + window.addCommentToShuoshuo = e => { + const btn = e.target.closest('.shuoshuo-comment-btn') + if (!btn) return + + const ele = btn.closest('.container').nextElementSibling + const { shuoshuoComment } = window + const isInclude = ele.classList.contains('no-comment') + runDestroy(shuoshuoComment) + if (isInclude) { + ele.classList.remove('no-comment') + ele.innerHTML = commentDiv + const key = `${location.pathname.replace(/\/$/, '')}?key=${ele.getAttribute('data-key')}` + btf.switchComments(ele, key) + shuoshuoComment.loadComment && shuoshuoComment.loadComment(ele, key) + } + } + })() + if page.shuoshuo_url script. (() => { + const limitConfig = !{ JSON.stringify(page.limit || {}) } + + const sortDataByDate = data => data.sort((a, b) => new Date(b.date) - new Date(a.date)) + + const filterDataByLimit = (data, limit) => { + if (!limit || !limit.type) return data + if (limit.type === 'num') return data.slice(0, limit.value) + if (limit.type === 'date') { + const limitDate = new Date(limit.value) + return data.filter(item => new Date(item.date) >= limitDate) + } + return data + }; + + const formatToTimeZone = (date) => { + const fullDate = date.length === 10 ? `${date} 00:00:00` : date + const visitorTimeZone = '#{config.timezone}' || Intl.DateTimeFormat().resolvedOptions().timeZone + const options = { + timeZone: visitorTimeZone, + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false + } + const [day, month, year, hour, minute, second] = new Intl.DateTimeFormat('en-GB', options) + .format(new Date(fullDate)) + .match(/\d+/g) + return `${year}-${month}-${day} ${hour}:${minute}:${second}` + } + const loadShuoshuo = async () => { try { - const fetchContent = await fetch('!{url_for(page.shuoshuo_url)}') - const shuoshuo = await fetchContent.json() + const response = await fetch('!{url_for(page.shuoshuo_url)}') + let data = await response.json() + + data = filterDataByLimit(sortDataByDate(data), limitConfig) - let start = 0 const container = document.getElementById('article-container') + let start = 0 - const addData = data => { - const cLength = data.length - const end = start + 10 > cLength ? cLength : start + 10 - let result = '' - data.slice(start, end).forEach((item) => { - result += ` -
-
-
- -
-
-
${item.author || '!{config.author}'}
-
${btf.diffDate(item.date, true)}
-
-
-
- ${item.content} + const renderData = (dataSlice) => { + const content = dataSlice.map(item => { + const formattedDate = formatToTimeZone(item.date) + const tags = item.tags && item.tags.map(tag => `${tag}`).join('') || '' + const commentButton = item.key && !{commentsJsLoad} + ? `
+ +
` + : '' + const commentContainer = item.key + ? `
` + : '' + + return ` +
+
+
+
+
- - ` - }) - - start = end - container.insertAdjacentHTML('beforeend', result) +
${item.content}
+ +
+ ${commentContainer} +
` + }).join('') - if (start >= cLength) { - observer.disconnect() - } else { - setTimeout(() => observer.observe(container.lastElementChild), 100) - } + container.insertAdjacentHTML('beforeend', content) window.lazyLoadInstance.update() btf.loadLightbox(document.querySelectorAll('#article-container img:not(.no-lightbox)')) } - addData(shuoshuo) + const handleIntersection = (entries) => { + if (!entries[0].isIntersecting) return + observer.unobserve(entries[0].target) + + const slice = data.slice(start, start + 10) + renderData(slice) + start += 10 - const observer = new IntersectionObserver((entries) => { - if (entries[0].isIntersecting) { - observer.unobserve(entries[0].target) - addData(shuoshuo) + if (start < data.length) { + setTimeout(() => observer.observe(container.lastElementChild), 100) + } else { + observer.disconnect() } - }, { + }; + + const observer = new IntersectionObserver(handleIntersection, { root: null, rootMargin: '0px', threshold: 1.0 }) - if (container.lastElementChild) { - observer.observe(container.lastElementChild) - } - } catch (e) { - console.error(e) + renderData(data.slice(start, 10)) + start += 10 + + if (container.lastElementChild) observer.observe(container.lastElementChild) + } catch (error) { + console.error(error) } - } + }; window.pjax ? loadShuoshuo() : window.addEventListener('load', loadShuoshuo) })() else if site.data.shuoshuo - each i in site.data.shuoshuo + each i in shuoshuoFN(site.data.shuoshuo, page) .shuoshuo-item - .shuoshuo-item-header - .shuoshuo-avatar - img.no-lightbox(src=i.avatar || url_for(theme.avatar.img)) - .shuoshuo-info - .shuoshuo-author=i.author || config.author - .shuoshuo-date=relative_date(i.date) - .shuoshuo-content - !=markdown(i.content) - .shuoshuo-footer - if i.tags - .shuoshuo-tags - each tag in i.tags - span.shuoshuo-tag=tag \ No newline at end of file + .container + .shuoshuo-item-header + .shuoshuo-avatar + img.no-lightbox(src=i.avatar || url_for(theme.avatar.img)) + .shuoshuo-info + .shuoshuo-author=i.author || config.author + time.shuoshuo-date(title=i.date)=i.date + .shuoshuo-content + !=markdown(i.content) + .shuoshuo-footer(class=i.tags && i.tags.length ? 'flex-between' : 'flex-end') + if i.tags + .shuoshuo-tags + each tag in i.tags + span.shuoshuo-tag=tag + if i.key && commentsJsLoad + .shuoshuo-comment-btn(onclick='addCommentToShuoshuo(event)') + i.fa-solid.fa-comments + if i.key && commentsJsLoad + .shuoshuo-comment.no-comment(data-key=i.key) \ No newline at end of file diff --git a/layout/includes/page/tags.pug b/layout/includes/page/tags.pug index b5791122..b5b62cd8 100644 --- a/layout/includes/page/tags.pug +++ b/layout/includes/page/tags.pug @@ -1,2 +1,2 @@ -.tag-cloud-list.is-center +.tag-cloud-list.text-center !=cloudTags({source: site.tags, orderby: page.orderby || 'random', order: page.order || 1, minfontsize: 1.2, maxfontsize: 1.5, limit: 0, unit: 'em'}) \ No newline at end of file diff --git a/layout/includes/pagination.pug b/layout/includes/pagination.pug index 7cbe9465..ad2602b4 100644 --- a/layout/includes/pagination.pug +++ b/layout/includes/pagination.pug @@ -7,30 +7,28 @@ } if is_post() - - let prev = theme.post_pagination === 1 ? page.prev : page.next - - let next = theme.post_pagination === 1 ? page.next : page.prev + - let paginationOrder = theme.post_pagination === 1 ? { prev: page.prev, next: page.next } : { prev: page.next, next: page.prev } + nav#pagination.pagination-post - if(prev) - - var hasPageNext = next ? 'pull-left' : 'pull-full' - a.prev-post(class=hasPageNext href=url_for(prev.path) title=prev.title) - if prev.cover_type === 'img' - img.cover(src=url_for(prev.cover) onerror=`onerror=null;src='${url_for(theme.error_img.post_page)}'` alt='cover of previous post') - else - .cover(style=`background: ${prev.cover || 'var(--default-bg-color)'}`) - .pagination-info - .label=_p('pagination.prev') - .prev_info=prev.title + each direction, key in paginationOrder + if direction + - const getPostDesc = direction.postDesc || postDesc(direction) + - let className = key === 'prev' ? (paginationOrder.next ? '' : 'full-width') : (paginationOrder.prev ? '' : 'full-width') + - className = getPostDesc ? className : className + ' no-desc' + + a.pagination-related(class=className href=url_for(direction.path) title=direction.title) + 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`) + else + .cover(style=`background: ${direction.cover || 'var(--default-bg-color)'}`) - if(next) - - var hasPagePrev = prev ? 'pull-right' : 'pull-full' - a.next-post(class=hasPagePrev href=url_for(next.path) title=next.title) - if next.cover_type === 'img' - img.cover(src=url_for(next.cover) onerror=`onerror=null;src='${url_for(theme.error_img.post_page)}'` alt='cover of next post') - else - .cover(style=`background: ${next.cover || 'var(--default-bg-color)'}`) - .pagination-info - .label=_p('pagination.next') - .next_info=next.title + .info(class=key === 'prev' ? '' : 'text-right') + .info-1 + .info-item-1=_p(`pagination.${key}`) + .info-item-2!=direction.title + if getPostDesc + .info-2 + .info-item-1!=getPostDesc else nav#pagination .pagination diff --git a/layout/includes/post/outdate-notice.pug b/layout/includes/post/outdate-notice.pug new file mode 100644 index 00000000..ef727727 --- /dev/null +++ b/layout/includes/post/outdate-notice.pug @@ -0,0 +1,8 @@ +- const { limit_day, message_prev, message_next, position} = theme.noticeOutdate +- const notice_data = { limitDay: limit_day, messagePrev: message_prev, messageNext: message_next, postUpdate: full_date(page.updated)} +if position === 'top' + #post-outdate-notice(data=notice_data hidden) + !=page.content +else + !=page.content + #post-outdate-notice(data=notice_data hidden) \ No newline at end of file diff --git a/layout/includes/rightside.pug b/layout/includes/rightside.pug index 9b758972..78273ef3 100644 --- a/layout/includes/rightside.pug +++ b/layout/includes/rightside.pug @@ -23,7 +23,7 @@ mixin rightsideItem(array) i.fa-solid.fa-list-ul when 'chat' if chat.rightside_button && chat.use - button#chat-btn(type="button" title=_p("rightside.chat")) + button#chat-btn(type="button" title=_p("rightside.chat") style="display:none") i.fas.fa-message when 'comment' if commentsJsLoad diff --git a/layout/includes/sidebar.pug b/layout/includes/sidebar.pug index 7eabbf1c..42131cd9 100644 --- a/layout/includes/sidebar.pug +++ b/layout/includes/sidebar.pug @@ -2,9 +2,9 @@ if theme.menu #sidebar #menu-mask #sidebar-menus - .avatar-img.is-center + .avatar-img.text-center img(src=url_for(theme.avatar.img) onerror=`onerror=null;src='${theme.error_img.flink}'` alt="avatar") - .site-data.is-center + .site-data.text-center a(href=url_for(config.archive_dir) + '/') .headline= _p('aside.articles') .length-num= site.posts.length diff --git a/layout/includes/third-party/abcjs/abcjs.pug b/layout/includes/third-party/abcjs/abcjs.pug index 089ee2b5..a75c37bc 100644 --- a/layout/includes/third-party/abcjs/abcjs.pug +++ b/layout/includes/third-party/abcjs/abcjs.pug @@ -1,12 +1,12 @@ script. (() => { const abcjsInit = () => { - const abcjsFn = () => { + const abcjsFn = () => setTimeout(() => { document.querySelectorAll(".abc-music-sheet").forEach(ele => { if (ele.children.length > 0) return ABCJS.renderAbc(ele, ele.innerHTML, {responsive: 'resize'}) }) - } + }, 100) typeof ABCJS === 'object' ? abcjsFn() : btf.getScript('!{url_for(theme.asset.abcjs_basic_js)}').then(abcjsFn) diff --git a/layout/includes/third-party/chat/chatra.pug b/layout/includes/third-party/chat/chatra.pug index 41f04232..0fad991f 100644 --- a/layout/includes/third-party/chat/chatra.pug +++ b/layout/includes/third-party/chat/chatra.pug @@ -1,42 +1,38 @@ //- https://chatra.io/help/api/ script. (() => { - const isChatBtn = !{theme.chat.rightside_button} - const isChatHideShow = !{theme.chat.button_hide_show} + window.ChatraID = '#{theme.chatra.id}' + window.Chatra = window.Chatra || function() { + (window.Chatra.q = window.Chatra.q || []).push(arguments) + } - if (isChatBtn) { - const close = () => { - Chatra('minimizeWidget') - Chatra('hide') - } + btf.getScript('https://call.chatra.io/chatra.js').then(() => { + const isChatBtn = !{theme.chat.rightside_button} + const isChatHideShow = !{theme.chat.button_hide_show} - const open = () => { - Chatra('openChat', true) - Chatra('show') - } + if (isChatBtn) { + const close = () => { + Chatra('minimizeWidget') + Chatra('hide') + } - window.ChatraSetup = { startHidden: true } - - window.chatBtnFn = () => { - document.getElementById('chatra').classList.contains('chatra--expanded') ? close() : open() - } - } else if (isChatHideShow) { - window.chatBtn = { - hide: () => Chatra('hide'), - show: () => Chatra('show') - } - } + const open = () => { + Chatra('openChat', true) + Chatra('show') + } + + window.ChatraSetup = { startHidden: true } + + window.chatBtnFn = () => document.getElementById('chatra').classList.contains('chatra--expanded') ? close() : open() - (function(d, w, c) { - w.ChatraID = '#{theme.chatra.id}' - var s = d.createElement('script') - w[c] = w[c] || function() { - (w[c].q = w[c].q || []).push(arguments) + document.getElementById('chat-btn').style.display = 'block' + } else if (isChatHideShow) { + window.chatBtn = { + hide: () => Chatra('hide'), + show: () => Chatra('show') + } } - s.async = true - s.src = 'https://call.chatra.io/chatra.js' - if (d.head) d.head.appendChild(s) - })(document, window, 'Chatra') + }) })() diff --git a/layout/includes/third-party/chat/crisp.pug b/layout/includes/third-party/chat/crisp.pug index e25a17fe..08a1916d 100644 --- a/layout/includes/third-party/chat/crisp.pug +++ b/layout/includes/third-party/chat/crisp.pug @@ -1,37 +1,32 @@ script. (() => { - window.$crisp = []; - window.CRISP_WEBSITE_ID = "!{theme.crisp.website_id}"; - (function () { - d = document; - s = d.createElement("script"); - s.src = "https://client.crisp.chat/l.js"; - s.async = 1; - d.getElementsByTagName("head")[0].appendChild(s); - })(); - $crisp.push(["safe", true]) + window.$crisp = ['safe', true] + window.CRISP_WEBSITE_ID = "!{theme.crisp.website_id}" - const isChatBtn = !{theme.chat.rightside_button} - const isChatHideShow = !{theme.chat.button_hide_show} + btf.getScript('https://client.crisp.chat/l.js').then(() => { + const isChatBtn = !{theme.chat.rightside_button} + const isChatHideShow = !{theme.chat.button_hide_show} - if (isChatBtn) { - const open = () => { - $crisp.push(["do", "chat:show"]) - $crisp.push(["do", "chat:open"]) - } + if (isChatBtn) { + const open = () => { + $crisp.push(["do", "chat:show"]) + $crisp.push(["do", "chat:open"]) + } - const close = () => $crisp.push(["do", "chat:hide"]) + const close = () => $crisp.push(["do", "chat:hide"]) - close() + close() - $crisp.push(["on", "chat:closed", close]) + $crisp.push(["on", "chat:closed", close]) - window.chatBtnFn = () => $crisp.is("chat:visible") ? close() : open() + window.chatBtnFn = () => $crisp.is("chat:visible") ? close() : open() - } else if (isChatHideShow) { - window.chatBtn = { - hide: () => $crisp.push(["do", "chat:hide"]), - show: () => $crisp.push(["do", "chat:show"]) + document.getElementById('chat-btn').style.display = 'block' + } else if (isChatHideShow) { + window.chatBtn = { + hide: () => $crisp.push(["do", "chat:hide"]), + show: () => $crisp.push(["do", "chat:show"]) + } } - } + }) })() \ No newline at end of file diff --git a/layout/includes/third-party/chat/daovoice.pug b/layout/includes/third-party/chat/daovoice.pug deleted file mode 100644 index 41ccfeb9..00000000 --- a/layout/includes/third-party/chat/daovoice.pug +++ /dev/null @@ -1,40 +0,0 @@ -//- https://guide.daocloud.io/daovoice/javascript-api-5869833.html -script. - (() => { - (function(i,s,o,g,r,a,m){i["DaoVoiceObject"]=r;i[r]=i[r]||function(){(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;a.charset="utf-8";m.parentNode.insertBefore(a,m)})(window,document,"script",('https:' == document.location.protocol ? 'https:' : 'http:') + "//widget.daovoice.io/widget/!{theme.daovoice.app_id}.js","daovoice") - - const isChatBtn = !{theme.chat.rightside_button} - const isChatHideShow = !{theme.chat.button_hide_show} - - daovoice('init', { - app_id: '!{theme.daovoice.app_id}',},{ - launcher: { - disableLauncherIcon: isChatBtn - }, - }); - daovoice('update'); - - if (isChatBtn) { - window.chatBtnFn = () => { - const isShow = document.getElementById('daodream-messenger').classList.contains('daodream-messenger-active') - isShow ? daovoice('hide') : daovoice('show') - } - } else if (isChatHideShow) { - window.chatBtn = { - hide: () => { - daovoice('update', {},{ - launcher: { - disableLauncherIcon: true - } - }) - }, - show: () => { - daovoice('update', {}, { - launcher: { - disableLauncherIcon: false - } - }) - } - } - } - })() \ No newline at end of file diff --git a/layout/includes/third-party/chat/index.pug b/layout/includes/third-party/chat/index.pug index c5348492..273d040a 100644 --- a/layout/includes/third-party/chat/index.pug +++ b/layout/includes/third-party/chat/index.pug @@ -3,7 +3,5 @@ case theme.chat.use include ./chatra.pug when 'tidio' include ./tidio.pug - when 'daovoice' - include ./daovoice.pug when 'crisp' include ./crisp.pug \ No newline at end of file diff --git a/layout/includes/third-party/chat/tidio.pug b/layout/includes/third-party/chat/tidio.pug index 78361af7..f9d40ea5 100644 --- a/layout/includes/third-party/chat/tidio.pug +++ b/layout/includes/third-party/chat/tidio.pug @@ -1,41 +1,45 @@ -script(src=`//code.tidio.co/${theme.tidio.public_key}.js` async) script. (() => { - const isChatBtn = !{theme.chat.rightside_button} - const isChatHideShow = !{theme.chat.button_hide_show} + btf.getScript('//code.tidio.co/!{theme.tidio.public_key}.js').then(() => { + const isChatBtn = !{theme.chat.rightside_button} + const isChatHideShow = !{theme.chat.button_hide_show} - if (isChatBtn) { - let isShow = false - const close = () => { - window.tidioChatApi.hide() - isShow = false - } - - const open = () => { - window.tidioChatApi.open() - window.tidioChatApi.show() - isShow = true - } + if (isChatBtn) { + let isShow = false + const close = () => { + window.tidioChatApi.hide() + isShow = false + } + + const open = () => { + window.tidioChatApi.open() + window.tidioChatApi.show() + isShow = true + } - const onTidioChatApiReady = () => { - window.tidioChatApi.hide() - window.tidioChatApi.on("close", close) - } - if (window.tidioChatApi) { - window.tidioChatApi.on("ready", onTidioChatApiReady) - } else { - document.addEventListener("tidioChat-ready", onTidioChatApiReady) - } + const onTidioChatApiReady = () => { + window.tidioChatApi.hide() + window.tidioChatApi.on("close", close) + } + if (window.tidioChatApi) { + window.tidioChatApi.on("ready", onTidioChatApiReady) + } else { + document.addEventListener("tidioChat-ready", onTidioChatApiReady) + } - window.chatBtnFn = () => { - if (!window.tidioChatApi) return - isShow ? close() : open() - } - } else if (isChatHideShow) { - window.chatBtn = { - hide: () => window.tidioChatApi && window.tidioChatApi.hide(), - show: () => window.tidioChatApi && window.tidioChatApi.show() + window.chatBtnFn = () => { + if (!window.tidioChatApi) return + isShow ? close() : open() + } + + document.getElementById('chat-btn').style.display = 'block' + + } else if (isChatHideShow) { + window.chatBtn = { + hide: () => window.tidioChatApi && window.tidioChatApi.hide(), + show: () => window.tidioChatApi && window.tidioChatApi.show() + } } - } + }) })() diff --git a/layout/includes/third-party/comments/artalk.pug b/layout/includes/third-party/comments/artalk.pug index befc32e3..36403345 100644 --- a/layout/includes/third-party/comments/artalk.pug +++ b/layout/includes/third-party/comments/artalk.pug @@ -4,14 +4,27 @@ script. (() => { let artalkItem = null - const initArtalk = () => { - artalkItem = Artalk.init(Object.assign({ - el: '#artalk-wrap', + const option = !{JSON.stringify(option)} + const isShuoshuo = GLOBAL_CONFIG_SITE.isShuoshuo + + const destroyArtalk = () => { + if (artalkItem) { + artalkItem.destroy() + artalkItem = null + } + } + + const artalkChangeMode = theme => artalkItem && artalkItem.setDarkMode(theme === 'dark') + + const initArtalk = (el = document, pageKey = location.pathname) => { + artalkItem = Artalk.init({ + el: el.querySelector('#artalk-wrap'), server: '!{server}', site: '!{site}', - pageKey: location.pathname, darkMode: document.documentElement.getAttribute('data-theme') === 'dark', - },!{JSON.stringify(option)})) + ...option, + pageKey: isShuoshuo ? pageKey : (option && option.pageKey) || pageKey + }) if (GLOBAL_CONFIG.lightbox === 'null') return artalkItem.on('list-loaded', () => { @@ -21,31 +34,36 @@ script. }) }) - const destroyArtalk = () => { - artalkItem.destroy() + if (isShuoshuo) { + window.shuoshuoComment.destroyArtalk = () => { + destroyArtalk() + if (el.children.length) { + el.innerHTML = '' + el.classList.add('no-comment') + } + } } btf.addGlobalFn('pjaxSendOnce', destroyArtalk, 'destroyArtalk') + btf.addGlobalFn('themeChange', artalkChangeMode, 'artalk') } - const loadArtalk = async () => { - if (typeof Artalk === 'object') initArtalk() + const loadArtalk = async (el, pageKey) => { + if (typeof Artalk === 'object') initArtalk(el, pageKey) else { await btf.getCSS('!{theme.asset.artalk_css}') await btf.getScript('!{theme.asset.artalk_js}') - initArtalk() + initArtalk(el, pageKey) } } - const artalkChangeMode = theme => { - const artalkWrap = document.getElementById('artalk-wrap') - if (!(artalkWrap && artalkWrap.children.length)) return - const isDark = theme === 'dark' - artalkItem.setDarkMode(isDark) + if (isShuoshuo) { + '!{use[0]}' === 'Artalk' + ? window.shuoshuoComment = { loadComment: loadArtalk } + : window.loadOtherComment = loadArtalk + return } - btf.addGlobalFn('themeChange', artalkChangeMode, 'artalk') - if ('!{use[0]}' === 'Artalk' || !!{lazyload}) { if (!{lazyload}) btf.loadComment(document.getElementById('artalk-wrap'), loadArtalk) else setTimeout(loadArtalk, 100) diff --git a/layout/includes/third-party/comments/disqus.pug b/layout/includes/third-party/comments/disqus.pug index 8d8864cf..c521af3c 100644 --- a/layout/includes/third-party/comments/disqus.pug +++ b/layout/includes/third-party/comments/disqus.pug @@ -4,29 +4,43 @@ script. (() => { - const disqus_config = function () { - this.page.url = '!{ page.permalink }' - this.page.identifier = '!{ url_for(page.path) }' - this.page.title = '!{ disqusPageTitle }' - } + const isShuoshuo = GLOBAL_CONFIG_SITE.isShuoshuo - const disqusReset = () => { + const disqusReset = conf => { window.DISQUS && window.DISQUS.reset({ reload: true, - config: disqus_config + config: conf }) } - btf.addGlobalFn('themeChange', disqusReset, 'disqus') + const loadDisqus = (el, path) => { + if (isShuoshuo) { + window.shuoshuoComment.destroyDisqus = () => { + if (el.children.length) { + el.innerHTML = '' + el.classList.add('no-comment') + } + } + } + + window.disqus_identifier = isShuoshuo ? path : '!{ url_for(page.path) }' + window.disqus_url = isShuoshuo ? location.origin + path : '!{ page.permalink }' + + const disqus_config = function () { + this.page.url = disqus_url + this.page.identifier = disqus_identifier + this.page.title = '!{ disqusPageTitle }' + } - const loadDisqus = () =>{ - if (window.DISQUS) disqusReset() + if (window.DISQUS) disqusReset(disqus_config) else { const script = document.createElement('script') script.src = 'https://!{shortname}.disqus.com/embed.js' script.setAttribute('data-timestamp', +new Date()) document.head.appendChild(script) } + + btf.addGlobalFn('themeChange', () => disqusReset(disqus_config), 'disqus') } const getCount = async() => { @@ -47,6 +61,13 @@ script. } } + if (isShuoshuo) { + '!{use[0]}' === 'Disqus' + ? window.shuoshuoComment = { loadComment: loadDisqus } + : window.loadOtherComment = loadDisqus + return + } + if ('!{use[0]}' === 'Disqus' || !!{lazyload}) { if (!{lazyload}) btf.loadComment(document.getElementById('disqus_thread'), loadDisqus) else { diff --git a/layout/includes/third-party/comments/disqusjs.pug b/layout/includes/third-party/comments/disqusjs.pug index 3550392d..31416113 100644 --- a/layout/includes/third-party/comments/disqusjs.pug +++ b/layout/includes/third-party/comments/disqusjs.pug @@ -1,36 +1,52 @@ -- let disqusjsPageTitle = page.title.replace(/'/ig,"\\'") +- let disqusjsPageTitle = page.title && page.title.replace(/'/ig,"\\'") - const { shortname:dqShortname, apikey:dqApikey, option:dqOption } = theme.disqusjs script. (() => { - const initDisqusjs = () => { + const isShuoshuo = GLOBAL_CONFIG_SITE.isShuoshuo + const dqOption = !{JSON.stringify(dqOption)} + + const destroyDisqusjs = () => { + disqusjs.destroy() window.disqusjs = null - disqusjs = new DisqusJS(Object.assign({ + } + + const themeChange = (el, path) => { + destroyDisqusjs() + initDisqusjs(el, path) + } + + const initDisqusjs = (el = document, path) => { + if (isShuoshuo) { + window.shuoshuoComment.destroyDisqusjs = () => { + destroyDisqusjs() + if (el.children.length) { + el.innerHTML = '' + el.classList.add('no-comment') + } + } + } + + disqusjs = new DisqusJS({ shortname: '!{dqShortname}', - identifier: '!{ url_for(page.path) }', - url: '!{ page.permalink }', title: '!{ disqusjsPageTitle }', apikey: '!{dqApikey}', - },!{JSON.stringify(dqOption)})) + ...dqOption, + identifier: isShuoshuo ? path : (dqOption && dqOption.identifier) || '!{ url_for(page.path) }', + url: isShuoshuo ? location.origin + path : (dqOption && dqOption.url) || '!{ page.permalink }' + }) - disqusjs.render(document.getElementById('disqusjs-wrap')) - } + disqusjs.render(el.querySelector('#disqusjs-wrap')) - const themeChange = () => { - const ele = document.getElementById('disqus_thread') - if(!ele) return - disqusjs.destroy() - initDisqusjs() + btf.addGlobalFn('themeChange', () => themeChange(el, path), 'disqusjs') } - btf.addGlobalFn('themeChange', themeChange, 'disqusjs') - - const loadDisqusjs = async() => { - if (window.disqusJsLoad) initDisqusjs() + const loadDisqusjs = async(el, path) => { + if (window.disqusJsLoad) initDisqusjs(el, path) else { await btf.getCSS('!{url_for(theme.asset.disqusjs_css)}') await btf.getScript('!{url_for(theme.asset.disqusjs)}') - initDisqusjs() + initDisqusjs(el, path) window.disqusJsLoad = true } } @@ -52,6 +68,13 @@ script. } } + if (isShuoshuo) { + '!{theme.comments.use[0]}' === 'Disqusjs' + ? window.shuoshuoComment = { loadComment: loadDisqusjs } + : window.loadOtherComment = loadDisqusjs + return + } + if ('!{theme.comments.use[0]}' === 'Disqusjs' || !!{theme.comments.lazyload}) { if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('disqusjs-wrap'), loadDisqusjs) else { diff --git a/layout/includes/third-party/comments/facebook_comments.pug b/layout/includes/third-party/comments/facebook_comments.pug index 3b47452d..7b070d42 100644 --- a/layout/includes/third-party/comments/facebook_comments.pug +++ b/layout/includes/third-party/comments/facebook_comments.pug @@ -3,17 +3,28 @@ script. (()=>{ - const loadFBComment = () => { + const isShuoshuo = GLOBAL_CONFIG_SITE.isShuoshuo + + const loadFBComment = (el = document, path) => { + if (isShuoshuo) { + window.shuoshuoComment.destroyFB = () => { + if (el.children.length) { + el.innerHTML = '' + el.classList.add('no-comment') + } + } + } + document.getElementById('fb-root') ? '' : document.body.insertAdjacentHTML('afterend', '
') const themeNow = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light' - const $fbComment = document.getElementsByClassName('fb-comments')[0] + const $fbComment = el.getElementsByClassName('fb-comments')[0] $fbComment.setAttribute('data-colorscheme',themeNow) - $fbComment.setAttribute('data-href', '!{urlNoIndex(page.permalink)}') + $fbComment.setAttribute('data-href', isShuoshuo ? '!{urlNoIndex(page.permalink)}' + '#' + path : '!{urlNoIndex(page.permalink)}') if (typeof FB === 'object') { FB.XFBML.parse(document.getElementsByClassName('post-meta-commentcount')[0]) - FB.XFBML.parse(document.getElementById('post-comment')) + FB.XFBML.parse(el.querySelector('#post-comment')) } else { let ele = document.createElement('script') @@ -36,6 +47,13 @@ script. btf.addGlobalFn('themeChange', fbModeChange, 'facebook_comments') + if (isShuoshuo) { + '!{theme.comments.use[0]}' === 'Facebook Comments' + ? window.shuoshuoComment = { loadComment: loadFBComment } + : window.loadOtherComment = loadFBComment + return + } + if ('!{theme.comments.use[0]}' === 'Facebook Comments' || !!{theme.comments.lazyload}) { if (!{theme.comments.lazyload}) btf.loadComment(document.querySelector('#post-comment .fb-comments'), loadFBComment) else loadFBComment() diff --git a/layout/includes/third-party/comments/giscus.pug b/layout/includes/third-party/comments/giscus.pug index b55a4ec2..bdc189fc 100644 --- a/layout/includes/third-party/comments/giscus.pug +++ b/layout/includes/third-party/comments/giscus.pug @@ -4,27 +4,50 @@ - const giscusOriginUrl = new URL(giscusUrl).origin script. - (()=>{ + (() => { + const isShuoshuo = GLOBAL_CONFIG_SITE.isShuoshuo + const option = !{JSON.stringify(option)} + const getGiscusTheme = theme => theme === 'dark' ? '!{dark_theme}' : '!{light_theme}' - const loadGiscus = () => { - const config = Object.assign({ + const createScriptElement = config => { + const ele = document.createElement('script') + Object.entries(config).forEach(([key, value]) => { + ele.setAttribute(key, value) + }) + return ele + } + + const loadGiscus = (el = document, key) => { + const mappingConfig = isShuoshuo + ? { 'data-mapping': 'specific', 'data-term': key } + : { 'data-mapping': (option && option['data-mapping']) || 'pathname' } + + const giscusConfig = { src: '!{giscusUrl}', 'data-repo': '!{repo}', 'data-repo-id': '!{repo_id}', 'data-category-id': '!{category_id}', - 'data-mapping': 'pathname', 'data-theme': getGiscusTheme(document.documentElement.getAttribute('data-theme')), 'data-reactions-enabled': '1', crossorigin: 'anonymous', - async: true - },!{JSON.stringify(option)}) + async: true, + ...option, + ...mappingConfig + } - const ele = document.createElement('script') - for (let key in config) { - ele.setAttribute(key, config[key]) + const scriptElement = createScriptElement(giscusConfig) + + el.querySelector('#giscus-wrap').appendChild(scriptElement) + + if (isShuoshuo) { + window.shuoshuoComment.destroyGiscus = () => { + if (el.children.length) { + el.innerHTML = '' + el.classList.add('no-comment') + } + } } - document.getElementById('giscus-wrap').appendChild(ele) } const changeGiscusTheme = theme => { @@ -43,10 +66,17 @@ script. btf.addGlobalFn('themeChange', changeGiscusTheme, 'giscus') + if (isShuoshuo) { + '!{use[0]}' === 'Giscus' + ? window.shuoshuoComment = { loadComment: loadGiscus } + : window.loadOtherComment = loadGiscus + return + } + if ('!{use[0]}' === 'Giscus' || !!{lazyload}) { if (!{lazyload}) btf.loadComment(document.getElementById('giscus-wrap'), loadGiscus) else loadGiscus() } else { - window.loadOtherComment= loadGiscus + window.loadOtherComment = loadGiscus } })() diff --git a/layout/includes/third-party/comments/gitalk.pug b/layout/includes/third-party/comments/gitalk.pug index fb2d694a..0b0a93e4 100644 --- a/layout/includes/third-party/comments/gitalk.pug +++ b/layout/includes/third-party/comments/gitalk.pug @@ -2,34 +2,54 @@ script. (() => { - const initGitalk = () => { - const gitalk = new Gitalk(Object.assign({ + const isShuoshuo = GLOBAL_CONFIG_SITE.isShuoshuo + const option = !{JSON.stringify(option)} + + const commentCount = n => { + const isCommentCount = document.querySelector('#post-meta .gitalk-comment-count') + if (isCommentCount) { + isCommentCount.textContent= n + } + } + + const initGitalk = (el, path) => { + if (isShuoshuo) { + window.shuoshuoComment.destroyGitalk = () => { + if (el.children.length) { + el.innerHTML = '' + el.classList.add('no-comment') + } + } + } + + const gitalk = new Gitalk({ clientID: '!{client_id}', clientSecret: '!{client_secret}', repo: '!{repo}', owner: '!{owner}', admin: ['!{admin}'], - id: '!{md5(page.path)}', - updateCountCallback: commentCount - },!{JSON.stringify(option)})) + updateCountCallback: commentCount, + ...option, + id: isShuoshuo ? path : (option && option.id) || '!{md5(page.path)}' + }) gitalk.render('gitalk-container') } - const loadGitalk = async() => { - if (typeof Gitalk === 'function') initGitalk() + const loadGitalk = async(el, path) => { + if (typeof Gitalk === 'function') initGitalk(el, path) else { await btf.getCSS('!{url_for(theme.asset.gitalk_css)}') await btf.getScript('!{url_for(theme.asset.gitalk)}') - initGitalk() + initGitalk(el, path) } } - - const commentCount = n => { - const isCommentCount = document.querySelector('#post-meta .gitalk-comment-count') - if (isCommentCount) { - isCommentCount.textContent= n - } + + if (isShuoshuo) { + '!{theme.comments.use[0]}' === 'Gitalk' + ? window.shuoshuoComment = { loadComment: loadGitalk } + : window.loadOtherComment = loadGitalk + return } if ('!{theme.comments.use[0]}' === 'Gitalk' || !!{theme.comments.lazyload}) { diff --git a/layout/includes/third-party/comments/livere.pug b/layout/includes/third-party/comments/livere.pug index ecacb596..4c396c76 100644 --- a/layout/includes/third-party/comments/livere.pug +++ b/layout/includes/third-party/comments/livere.pug @@ -1,8 +1,23 @@ - const { use, lazyload } = theme.comments script. - (()=>{ - const loadLivere = () => { + (() => { + const isShuoshuo = GLOBAL_CONFIG_SITE.isShuoshuo + + const loadLivere = (el, path) => { + window.livereOptions = { + refer: path || location.pathname + } + + if (isShuoshuo) { + window.shuoshuoComment.destroyLivere = () => { + if (el.children.length) { + el.innerHTML = '' + el.classList.add('no-comment') + } + } + } + if (typeof LivereTower === 'object') window.LivereTower.init() else { (function(d, s) { @@ -16,6 +31,13 @@ script. } } + if (isShuoshuo) { + '!{use[0]}' === 'Livere' + ? window.shuoshuoComment = { loadComment: loadLivere } + : window.loadOtherComment = loadLivere + return + } + if ('!{use[0]}' === 'Livere' || !!{lazyload}) { if (!{lazyload}) btf.loadComment(document.getElementById('lv-container'), loadLivere) else loadLivere() diff --git a/layout/includes/third-party/comments/remark42.pug b/layout/includes/third-party/comments/remark42.pug index 388fb97c..0a1fbe7b 100644 --- a/layout/includes/third-party/comments/remark42.pug +++ b/layout/includes/third-party/comments/remark42.pug @@ -1,68 +1,78 @@ - const { host, siteId, option } = theme.remark42 + script. - var remark_config = Object.assign({ - host: '!{host}', - site_id: '!{siteId}', - components: ['embed'], - theme: document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light' - },!{JSON.stringify(option)}) + (() => { + const isShuoshuo = GLOBAL_CONFIG_SITE.isShuoshuo + const option = !{JSON.stringify(option)} - function addRemark42(){ - for (let i = 0; i < remark_config.components.length; i++) { - const s = document.createElement('script') - s.src = remark_config.host + '/web/' + remark_config.components[i] + '.js' - s.defer = true - document.head.appendChild(s) + const loadScript = src => { + const script = document.createElement('script') + script.src = src + script.defer = true + document.head.appendChild(script) } - } - function initRemark42() { - if (window.REMARK42) { - if (this.remark42Instance) { - this.remark42Instance.destroy() - } + const addRemark42 = () => loadScript('!{host}/web/embed.js') - this.remark42Instance = window.REMARK42.createInstance({ - ...remark_config - }) - } - } + const getCount = () => document.querySelector('.remark42__counter') && loadScript('!{host}/web/count.js') + + const destroyRemark42 = () => window.remark42Instance && window.remark42Instance.destroy() - function getCount () { - const ele = document.querySelector('.remark42__counter') - if (ele) { - const s = document.createElement('script') - s.src = remark_config.host + '/web/counter.js' - s.defer = true - document.head.appendChild(s) + const initRemark42 = remark_config => { + if (window.REMARK42) { + destroyRemark42() + window.remark42Instance = window.REMARK42.createInstance({ + ...remark_config + }) + } } - } - function loadRemark42 () { - if (window.REMARK42) { - this.initRemark42() - getCount() - } else { - addRemark42() - window.addEventListener('REMARK42::ready', () => { - this.initRemark42() + const loadRemark42 = (el, path) => { + if (isShuoshuo) { + window.shuoshuoComment.destroyRemark42 = () => { + destroyRemark42() + if (el.children.length) { + el.innerHTML = '' + el.classList.add('no-comment') + } + } + } + + window.remark_config = { + host: '!{host}', + site_id: '!{siteId}', + theme: document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light', + ...option, + url: isShuoshuo ? window.location.origin + path : (option && option.url) || window.location.origin + window.location.pathname + } + + if (window.REMARK42) { + initRemark42(remark_config) getCount() - }) + } else { + addRemark42() + window.addEventListener('REMARK42::ready', () => { + initRemark42(remark_config) + getCount() + }) + } } - } - function remarkChangeMode (theme) { - if (!window.REMARK42) return - window.REMARK42.changeTheme(theme) - } + const remarkChangeMode = theme => window.REMARK42 && window.REMARK42.changeTheme(theme) - btf.addGlobalFn('themeChange', remarkChangeMode, 'remark42') + btf.addGlobalFn('themeChange', remarkChangeMode, 'remark42') - if ('!{theme.comments.use[0]}' === 'Remark42' || !!{theme.comments.lazyload}) { - if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('remark42'), loadRemark42) - else loadRemark42() - } else { - function loadOtherComment () { - loadRemark42() + if (isShuoshuo) { + '!{theme.comments.use[0]}' === 'Remark42' + ? window.shuoshuoComment = { loadComment: loadRemark42 } + : window.loadOtherComment = loadRemark42 + return + } + + if ('!{theme.comments.use[0]}' === 'Remark42' || !!{theme.comments.lazyload}) { + if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('remark42'), loadRemark42) + else loadRemark42() + } else { + window.loadOtherComment = loadRemark42 } - } + })() \ No newline at end of file diff --git a/layout/includes/third-party/comments/twikoo.pug b/layout/includes/third-party/comments/twikoo.pug index 8f3bc42c..ac91bcfd 100644 --- a/layout/includes/third-party/comments/twikoo.pug +++ b/layout/includes/third-party/comments/twikoo.pug @@ -3,6 +3,9 @@ script. (() => { + const isShuoshuo = GLOBAL_CONFIG_SITE.isShuoshuo + const option = !{JSON.stringify(option)} + const getCount = () => { const countELement = document.getElementById('twikoo-count') if(!countELement) return @@ -18,22 +21,38 @@ script. }) } - const init = () => { - twikoo.init(Object.assign({ - el: '#twikoo-wrap', + const init = (el = document, path = location.pathname) => { + twikoo.init({ + el: el.querySelector('#twikoo-wrap'), envId: '!{envId}', region: '!{region}', onCommentLoaded: () => { btf.loadLightbox(document.querySelectorAll('#twikoo .tk-content img:not(.tk-owo-emotion)')) - } - }, !{JSON.stringify(option)})) + }, + ...option, + path: isShuoshuo ? path : (option && option.path) || path + }) !{count ? 'GLOBAL_CONFIG_SITE.isPost && getCount()' : ''} + + isShuoshuo && (window.shuoshuoComment.destroyTwikoo = () => { + if (el.children.length) { + el.innerHTML = '' + el.classList.add('no-comment') + } + }) + } + + const loadTwikoo = (el, path) => { + if (typeof twikoo === 'object') setTimeout(() => init(el, path), 0) + else btf.getScript('!{url_for(theme.asset.twikoo)}').then(() => init(el, path)) } - const loadTwikoo = () => { - if (typeof twikoo === 'object') setTimeout(init,0) - else btf.getScript('!{url_for(theme.asset.twikoo)}').then(init) + if (isShuoshuo) { + '!{use[0]}' === 'Twikoo' + ? window.shuoshuoComment = { loadComment: loadTwikoo } + : window.loadOtherComment = loadTwikoo + return } if ('!{use[0]}' === 'Twikoo' || !!{lazyload}) { diff --git a/layout/includes/third-party/comments/utterances.pug b/layout/includes/third-party/comments/utterances.pug index 1fdc0da5..aaabd6fc 100644 --- a/layout/includes/third-party/comments/utterances.pug +++ b/layout/includes/third-party/comments/utterances.pug @@ -5,24 +5,33 @@ script. (() => { + const isShuoshuo = GLOBAL_CONFIG_SITE.isShuoshuo + const option = !{JSON.stringify(option)} const getUtterancesTheme = theme => theme === 'dark' ? '#{dark_theme}' : '#{light_theme}' - const loadUtterances = () => { - const config = Object.assign({ - id: 'utterances_comment', + const loadUtterances = (el = document, key) => { + if (isShuoshuo) { + window.shuoshuoComment.destroyUtterances = () => { + if (el.children.length) { + el.innerHTML = '' + el.classList.add('no-comment') + } + } + } + + const config = { src: '!{utterancesUrl}', repo: '!{repo}', - 'issue-term': '!{issue_term}', theme: getUtterancesTheme(document.documentElement.getAttribute('data-theme')), crossorigin: 'anonymous', - async: true - },!{JSON.stringify(option)}) + async: true, + ...option, + 'issue-term': isShuoshuo ? key : (option && option['issue-term']) || '!{issue_term}' + } const ele = document.createElement('script') - for (let key in config) { - ele.setAttribute(key, config[key]) - } - document.getElementById('utterances-wrap').appendChild(ele) + Object.entries(config).forEach(([key, value]) => ele.setAttribute(key, value)) + el.querySelector('#utterances-wrap').appendChild(ele) } const changeUtterancesTheme = theme => { @@ -38,6 +47,13 @@ script. btf.addGlobalFn('themeChange', changeUtterancesTheme, 'utterances') + if (isShuoshuo) { + '!{use[0]}' === 'Utterances' + ? window.shuoshuoComment = { loadComment: loadUtterances } + : window.loadOtherComment = loadUtterances + return + } + if ('!{use[0]}' === 'Utterances' || !!{lazyload}) { if (!{lazyload}) btf.loadComment(document.getElementById('utterances-wrap'), loadUtterances) else loadUtterances() diff --git a/layout/includes/third-party/comments/valine.pug b/layout/includes/third-party/comments/valine.pug index b53e3fc0..4cdd1402 100644 --- a/layout/includes/third-party/comments/valine.pug +++ b/layout/includes/third-party/comments/valine.pug @@ -7,32 +7,54 @@ if site.data.valine script. (() => { - const initValine = () => { - const valine = new Valine(Object.assign({ + const isShuoshuo = GLOBAL_CONFIG_SITE.isShuoshuo + const option = !{JSON.stringify(option)} + + const initValine = (el, path) => { + if (isShuoshuo) { + window.shuoshuoComment.destroyValine = () => { + if (el.children.length) { + el.innerHTML = '' + el.classList.add('no-comment') + } + } + } + + const valineConfig = { el: '#vcomment', appId: '#{appId}', appKey: '#{appKey}', avatar: '#{avatar}', serverURLs: '#{serverURLs}', emojiMaps: !{emojiMaps}, - path: window.location.pathname, - visitor: #{visitor} - }, !{JSON.stringify(option)})) + visitor: #{visitor}, + ...option, + path: isShuoshuo ? path : (option && option.path) || window.location.pathname + } + + new Valine(valineConfig) } - const loadValine = async () => { - if (typeof Valine === 'function') initValine() - else { + const loadValine = async (el, path) => { + if (typeof Valine === 'function') { + initValine(el, path) + } else { await btf.getScript('!{url_for(theme.asset.valine)}') - initValine() + initValine(el, path) } } + if (isShuoshuo) { + '!{use[0]}' === 'Valine' + ? window.shuoshuoComment = { loadComment: loadValine } + : window.loadOtherComment = loadValine + return + } + if ('!{use[0]}' === 'Valine' || !!{lazyload}) { if (!{lazyload}) btf.loadComment(document.getElementById('vcomment'),loadValine) else setTimeout(loadValine, 0) } else { window.loadOtherComment = loadValine } - })() - + })() \ No newline at end of file diff --git a/layout/includes/third-party/comments/waline.pug b/layout/includes/third-party/comments/waline.pug index 97797502..055a9f10 100644 --- a/layout/includes/third-party/comments/waline.pug +++ b/layout/includes/third-party/comments/waline.pug @@ -4,39 +4,55 @@ script. (() => { let initFn = window.walineFn || null + const isShuoshuo = GLOBAL_CONFIG_SITE.isShuoshuo + const option = !{JSON.stringify(option)} - const initWaline = (Fn) => { - const waline = Fn(Object.assign({ - el: '#waline-wrap', + const destroyWaline = ele => ele.destroy() + + const initWaline = (Fn, el = document, path = window.location.pathname) => { + const waline = Fn({ + el: el.querySelector('#waline-wrap'), serverURL: '!{serverURL}', pageview: !{lazyload ? false : pageview}, dark: 'html[data-theme="dark"]', - path: window.location.pathname, comment: !{lazyload ? false : count}, imageUploader: !{imageUploader}, - }, !{JSON.stringify(option)})) + ...option, + path: isShuoshuo ? path : (option && option.path) || path + }) - const destroyWaline = () => { - waline.destroy() + if (isShuoshuo) { + window.shuoshuoComment.destroyWaline = () => { + destroyWaline(waline) + if (el.children.length) { + el.innerHTML = '' + el.classList.add('no-comment') + } + } } - - btf.addGlobalFn('pjaxSendOnce', destroyWaline, 'destroyWaline') } - const loadWaline = () => { - if (initFn) initWaline(initFn) + const loadWaline = (el, path) => { + if (initFn) initWaline(initFn, el, path) else { btf.getCSS('!{url_for(theme.asset.waline_css)}') if (!{meta_css}) btf.getCSS('!{url_for(theme.asset.waline_meta_css)}') .then(() => import('!{url_for(theme.asset.waline_js)}')) .then(({ init }) => { initFn = init || Waline.init - initWaline(initFn) + initWaline(initFn, el, path) window.walineFn = initFn }) } } + if (isShuoshuo) { + '!{use[0]}' === 'Waline' + ? window.shuoshuoComment = { loadComment: loadWaline } + : window.loadOtherComment = loadWaline + return + } + if ('!{use[0]}' === 'Waline' || !!{lazyload}) { if (!{lazyload}) btf.loadComment(document.getElementById('waline-wrap'),loadWaline) else setTimeout(loadWaline, 0) diff --git a/layout/includes/third-party/newest-comments/remark42.pug b/layout/includes/third-party/newest-comments/remark42.pug index c83fd0ca..be3b2fc6 100644 --- a/layout/includes/third-party/newest-comments/remark42.pug +++ b/layout/includes/third-party/newest-comments/remark42.pug @@ -1,4 +1,5 @@ - const { host, siteId } = theme.remark42 +!= partial("includes/third-party/newest-comments/common.pug", {}, { cache: true }) script. window.addEventListener('load', () => { diff --git a/layout/includes/third-party/pangu.pug b/layout/includes/third-party/pangu.pug deleted file mode 100644 index 53bb0643..00000000 --- a/layout/includes/third-party/pangu.pug +++ /dev/null @@ -1,23 +0,0 @@ -script. - (() => { - const panguFn = () => { - if (typeof pangu === 'object') pangu.autoSpacingPage() - else { - btf.getScript('!{url_for(theme.asset.pangu)}') - .then(() => { - pangu.autoSpacingPage() - }) - } - } - - const panguInit = () => { - if (!{theme.pangu.field === 'post'}){ - GLOBAL_CONFIG_SITE.isPost && panguFn() - } else { - panguFn() - } - } - - btf.addGlobalFn('pjaxComplete', panguInit, 'pangu') - document.addEventListener('DOMContentLoaded', panguInit) - })() diff --git a/layout/includes/third-party/search/local-search.pug b/layout/includes/third-party/search/local-search.pug index af96ab01..e459a942 100644 --- a/layout/includes/third-party/search/local-search.pug +++ b/layout/includes/third-party/search/local-search.pug @@ -6,7 +6,7 @@ button.search-close-button i.fas.fa-times - #loading-database.is-center + #loading-database.text-center i.fas.fa-spinner.fa-pulse span= ' ' + _p("search.load_data") diff --git a/layout/includes/widget/card_author.pug b/layout/includes/widget/card_author.pug index c4f01d4d..095f66fa 100644 --- a/layout/includes/widget/card_author.pug +++ b/layout/includes/widget/card_author.pug @@ -1,5 +1,5 @@ if theme.aside.card_author.enable - .card-widget.card-info.is-center + .card-widget.card-info.text-center .avatar-img img(src=url_for(theme.avatar.img) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt="avatar") .author-info-name= config.author diff --git a/layout/post.pug b/layout/post.pug index 07c696e6..08bdea3a 100644 --- a/layout/post.pug +++ b/layout/post.pug @@ -5,7 +5,11 @@ block content if top_img === false include includes/header/post-info.pug - article#article-container.post-content!=page.content + article#article-container.container.post-content + if theme.noticeOutdate.enable && page.noticeOutdate !== false + include includes/post/outdate-notice.pug + else + !=page.content include includes/post/post-copyright.pug .tag_share if (page.tags.length > 0 && theme.post_meta.post.tags) diff --git a/package.json b/package.json index fe1ca421..12450b2d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hexo-theme-butterfly", - "version": "5.1.0-b1", + "version": "5.3.0-b1", "description": "A Simple and Card UI Design theme for Hexo", "main": "package.json", "scripts": { diff --git a/plugins.yml b/plugins.yml index 7a8235a7..6995673a 100644 --- a/plugins.yml +++ b/plugins.yml @@ -1,7 +1,7 @@ abcjs_basic_js: name: abcjs file: dist/abcjs-basic-min.js - version: 6.4.3 + version: 6.4.4 activate_power_mode: name: butterfly-extsrc file: dist/activate-power-mode.min.js @@ -9,7 +9,7 @@ activate_power_mode: algolia_search: name: algoliasearch file: dist/lite/builds/browser.umd.js - version: 5.8.1 + version: 5.12.0 aplayer_css: name: aplayer file: dist/APlayer.min.css @@ -44,8 +44,8 @@ canvas_ribbon: version: 1.1.4 chartjs: name: chart.js - file: dist/chart.umd.min.js - version: 4.4.4 + file: dist/chart.umd.js + version: 4.4.6 clickShowText: name: butterfly-extsrc file: dist/click-show-text.min.js @@ -66,12 +66,12 @@ docsearch_css: name: '@docsearch/css' other_name: docsearch-css file: dist/style.css - version: 3.6.2 + version: 3.6.3 docsearch_js: name: '@docsearch/js' other_name: docsearch-js file: dist/umd/index.js - version: 3.6.2 + version: 3.6.3 egjs_infinitegrid: name: '@egjs/infinitegrid' other_name: egjs-infinitegrid @@ -111,7 +111,7 @@ instantpage: instantsearch: name: instantsearch.js file: dist/instantsearch.production.min.js - version: 4.75.0 + version: 4.75.3 katex: name: katex file: dist/katex.min.css @@ -137,7 +137,7 @@ medium_zoom: mermaid: name: mermaid file: dist/mermaid.min.js - version: 11.3.0 + version: 11.4.0 meting_js: name: butterfly-extsrc file: metingjs/dist/Meting.min.js @@ -152,10 +152,6 @@ pace_js: other_name: pace file: pace.min.js version: 1.2.4 -pangu: - name: pangu - file: dist/browser/pangu.min.js - version: 4.0.7 pjax: name: pjax file: pjax.min.js diff --git a/scripts/common/postDesc.js b/scripts/common/postDesc.js new file mode 100644 index 00000000..e20e86e7 --- /dev/null +++ b/scripts/common/postDesc.js @@ -0,0 +1,36 @@ +'use strict' + +const { stripHTML, truncate } = require('hexo-util') + +// Truncates the given content to a specified length, removing HTML tags and replacing newlines with spaces. +const truncateContent = (content, length) => { + return truncate(stripHTML(content), { length, separator: ' ' }).replace(/\n/g, ' ') +} + +// Generates a post description based on the provided data and theme configuration. +const postDesc = (data, hexo) => { + const { description, content, postDesc } = data + + if (postDesc) return postDesc + + const { length, method } = hexo.theme.config.index_post_content + + if (method === false) return + + let result + switch (method) { + case 1: + result = description + break + case 2: + result = description || truncateContent(content, length) + break + default: + result = truncateContent(content, length) + } + + data.postDesc = result + return result +} + +module.exports = { truncateContent, postDesc } diff --git a/scripts/events/comment.js b/scripts/events/comment.js index b38e1de7..16da9481 100644 --- a/scripts/events/comment.js +++ b/scripts/events/comment.js @@ -7,11 +7,18 @@ hexo.extend.filter.register('before_generate', () => { let { use } = themeConfig.comments if (!use) return - // 確保 use 是一個陣列 + // Make sure use is an array use = Array.isArray(use) ? use : use.split(',') - // 將每個項目轉換為小寫並將首字母大寫 - themeConfig.comments.use = use.map(item => + // 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 }) diff --git a/scripts/events/merge_config.js b/scripts/events/merge_config.js index 2068625b..a1ba9e99 100644 --- a/scripts/events/merge_config.js +++ b/scripts/events/merge_config.js @@ -380,9 +380,6 @@ hexo.extend.filter.register('before_generate', () => { tidio: { public_key: null }, - daovoice: { - app_id: null - }, crisp: { website_id: null }, @@ -548,10 +545,6 @@ hexo.extend.filter.register('before_generate', () => { bg_dark: '#1f1f1f' }, instantpage: false, - pangu: { - enable: false, - field: 'site' - }, lazyload: { enable: false, field: 'site', @@ -570,6 +563,7 @@ hexo.extend.filter.register('before_generate', () => { enable: true, option: null }, + structured_data: true, css_prefix: true, inject: { head: null, diff --git a/scripts/filters/random_cover.js b/scripts/filters/random_cover.js index 7dac3cf8..2cce1486 100644 --- a/scripts/filters/random_cover.js +++ b/scripts/filters/random_cover.js @@ -6,7 +6,7 @@ 'use strict' hexo.extend.filter.register('before_post_render', data => { - const imgTestReg = /\.(png|jpe?g|gif|svg|webp)(\?.*)?$/i + const imgTestReg = /\.(png|jpe?g|gif|svg|webp|avif)(\?.*)?$/i let { cover: coverVal, top_img: topImg } = data // Add path to top_img and cover if post_asset_folder is enabled diff --git a/scripts/helpers/inject_head_js.js b/scripts/helpers/inject_head_js.js index 63723af1..3cf31cbe 100644 --- a/scripts/helpers/inject_head_js.js +++ b/scripts/helpers/inject_head_js.js @@ -55,7 +55,6 @@ hexo.extend.helper.register('inject_head_js', function () { if (!${pjax.enable} && key.startsWith('pjax')) return const globalFn = parent.globalFn || {} globalFn[key] = globalFn[key] || {} - if (name && globalFn[key][name]) return globalFn[key][name || Object.keys(globalFn[key]).length] = fn parent.globalFn = globalFn } diff --git a/scripts/helpers/page.js b/scripts/helpers/page.js index 061f24dc..1b47077e 100644 --- a/scripts/helpers/page.js +++ b/scripts/helpers/page.js @@ -1,10 +1,14 @@ 'use strict' -const { stripHTML, prettyUrls, truncate } = require('hexo-util') +const { truncateContent, postDesc } = require('../common/postDesc') +const { prettyUrls } = require('hexo-util') const crypto = require('crypto') +const moment = require('moment-timezone') -hexo.extend.helper.register('truncate', (content, length) => { - return truncate(stripHTML(content), { length, separator: ' ' }).replace(/\n/g, ' ') +hexo.extend.helper.register('truncate', truncateContent) + +hexo.extend.helper.register('postDesc', data => { + return postDesc(data, hexo) }) hexo.extend.helper.register('cloudTags', function (options = {}) { @@ -33,7 +37,7 @@ hexo.extend.helper.register('cloudTags', function (options = {}) { const ratio = length ? sizes.indexOf(tag.length) / length : 0 const size = minfontsize + ((maxfontsize - minfontsize) * ratio) const style = generateStyle(size, unit) - return `${tag.name}` + return `${tag.name}${tag.length}` }).join('') return result @@ -92,3 +96,38 @@ hexo.extend.helper.register('getBgPath', path => { return `background: ${path};` } }) + +hexo.extend.helper.register('shuoshuoFN', (data, page) => { + const { limit } = page + let finalResult = '' + + // Check if limit.value is a valid date + const isValidDate = date => !isNaN(Date.parse(date)) + + // order by date + const orderByDate = data => data.sort((a, b) => Date.parse(b.date) - Date.parse(a.date)) + + // Apply number limit or time limit conditionally + const limitData = data => { + if (limit && limit.type === 'num' && limit.value > 0) { + return data.slice(0, limit.value) + } else if (limit && limit.type === 'date' && isValidDate(limit.value)) { + const limitDate = Date.parse(limit.value) + 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 + // so you need to manually convert the time zone + finalResult.forEach(item => { + 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') + }) + + return finalResult +}) diff --git a/scripts/helpers/related_post.js b/scripts/helpers/related_post.js index 46f43817..8059dbe8 100644 --- a/scripts/helpers/related_post.js +++ b/scripts/helpers/related_post.js @@ -1,3 +1,4 @@ +/* eslint-disable camelcase */ /** * Butterfly * Related Posts @@ -6,12 +7,15 @@ 'use strict' +const { postDesc } = require('../common/postDesc') + hexo.extend.helper.register('related_posts', function (currentPost, allPosts) { let relatedPosts = [] const tagsData = currentPost.tags tagsData.length && tagsData.forEach(function (tag) { allPosts.forEach(function (post) { if (currentPost.path !== post.path && isTagRelated(tag.name, post.tags)) { + const getPostDesc = post.postDesc || postDesc(post, hexo) const relatedPost = { title: post.title, path: post.path, @@ -19,7 +23,8 @@ hexo.extend.helper.register('related_posts', function (currentPost, allPosts) { cover_type: post.cover_type, weight: 1, updated: post.updated, - created: post.date + created: post.date, + postDesc: getPostDesc } const index = findItem(relatedPosts, 'path', post.path) if (index !== -1) { @@ -50,20 +55,27 @@ hexo.extend.helper.register('related_posts', function (currentPost, allPosts) { result += '