diff --git a/README.md b/README.md index a7018ad..2c7c7b2 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ npm i hexo-theme-butterfly - [x] Related articles - [x] Displays outdated notice for a post - [x] Share (AddThis/Sharejs/Addtoany) -- [X] Comment (Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus) +- [X] Comment (Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus/Remark42) - [x] Multiple Comment System Support - [x] Online Chats (Chatra/Tidio/Daovoice/Gitter/Crisp) - [x] Web analytics diff --git a/README_CN.md b/README_CN.md index c19ef07..c98ef14 100644 --- a/README_CN.md +++ b/README_CN.md @@ -79,7 +79,7 @@ theme: butterfly - [x] 顯示相關文章 - [x] 過期文章提醒 - [x] 多種分享系統(AddThis/Sharejs/Addtoany) -- [X] 多種評論系統(Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus) +- [X] 多種評論系統(Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus/Remark42) - [x] 支持雙評論部署 - [x] 多種在線聊天(Chatra/Tidio/Daovoice/Gitter/Crisp) - [x] 多種分析系統 diff --git a/_config.yml b/_config.yml index 299f671..c960cc1 100644 --- a/_config.yml +++ b/_config.yml @@ -259,7 +259,7 @@ addtoany: comments: # Up to two comments system, the first will be shown as default - # Choose: Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus + # Choose: Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus/Remark42 use: # Valine,Disqus text: true # Display the comment name next to the button # lazyload: The comment system will be load when comment element enters the browser's viewport. @@ -354,6 +354,13 @@ giscus: dark: dark option: +# Remark42 +# https://remark42.com/docs/configuration/frontend/ +remark42: + host: # Your Host URL + siteId: # Your Site ID + option: + # Chat Services # -------------------------------------- @@ -621,7 +628,10 @@ aside: button: true mobile: true # display on mobile position: right # left or right - archives: true + display: + archive: true + tag: true + category: true card_author: enable: true description: @@ -836,13 +846,22 @@ inject: # 非必要請不要修改 CDN: # The CDN provider of internal scripts (主題內部 js 的 cdn 配置) - # option: local/jsdelivr - # Dev version cannot choose jsdelivr (dev版的主題不能設置為 jsdelivr) + # option: local/jsdelivr/unpkg/cdnjs/custom + # Dev version can only choose. ( dev版的主題只能設置為 local ) internal_provider: local + # The CDN provider of third party scripts (第三方 js 的 cdn 配置) - # option: local/jsdelivr + # option: local/jsdelivr/unpkg/cdnjs/custom # when set it to local, you need to install hexo-butterfly-extjs third_party_provider: jsdelivr + + # Add version number to CDN, true or false + version: false + + # Custom format + # For example: https://cdn.staticfile.org/${cdnjs_name}/${version}/${min_cdnjs_file} + custom_format: + option: # main_css: # main: @@ -867,7 +886,6 @@ CDN: # mathjax: # katex: # katex_copytex: - # katex_copytex_css: # mermaid: # canvas_ribbon: # canvas_fluttering_ribbon: diff --git a/layout/archive.pug b/layout/archive.pug index faa485c..815cdd1 100644 --- a/layout/archive.pug +++ b/layout/archive.pug @@ -3,6 +3,7 @@ extends includes/layout.pug block content include ./includes/mixins/article-sort.pug #archive - .article-sort-title= _p('page.articles') + ' - ' + site.posts.length + - const archiveLength = findArchiveLength(fragment_cache) + .article-sort-title= _p('page.articles') + ' - ' + archiveLength +articleSort(page.posts) include includes/pagination.pug \ No newline at end of file diff --git a/layout/includes/header/index.pug b/layout/includes/header/index.pug index f1e3724..18e7dd7 100644 --- a/layout/includes/header/index.pug +++ b/layout/includes/header/index.pug @@ -19,7 +19,7 @@ if !theme.disable_top_img && page.top_img !== false if top_img !== false - var imgSource = top_img && top_img.indexOf('/') !== -1 ? `background-image: url('${url_for(top_img)}')` : `background: ${top_img}` - var bg_img = top_img ? imgSource : '' - - var site_title = is_archive() ? fragment_cache('findArchivesTitle', function(){return findArchivesTitle(theme.menu);}) : page.title || page.tag || page.category || config.title + - var site_title = is_archive() ? findArchivesTitle(page, theme.menu, date) : page.title || page.tag || page.category || config.title - var isHomeClass = is_home() ? 'full_page' : 'not-home-page' - is_post() ? isHomeClass = 'post-bg' : isHomeClass else diff --git a/layout/includes/header/post-info.pug b/layout/includes/header/post-info.pug index 259b28e..6b951c4 100644 --- a/layout/includes/header/post-info.pug +++ b/layout/includes/header/post-info.pug @@ -63,26 +63,26 @@ - const commentUse = comments.use if commentUse && !comments.lazyload - case commentUse[0] - when 'Valine' - if theme.valine.visitor - +pvBlock(url_for(page.path),'leancloud_visitors',page.title) - span.leancloud-visitors-count - when 'Waline' - if theme.waline.pageview - +pvBlock('','','') - span.waline-pageview-count(data-path=url_for(page.path)) - when 'Twikoo' - if theme.twikoo.visitor - +pvBlock('','','') - span#twikoo_visitors - default - if theme.busuanzi.page_pv - +pvBlock('','post-meta-pv-cv','') - span#busuanzi_value_page_pv + if commentUse[0] === 'Valine' && theme.valine.visitor + +pvBlock(url_for(page.path),'leancloud_visitors',page.title) + span.leancloud-visitors-count + i.fa-solid.fa-spinner.fa-spin + else if commentUse[0] === 'Waline' && theme.waline.pageview + +pvBlock('','','') + span.waline-pageview-count(data-path=url_for(page.path)) + i.fa-solid.fa-spinner.fa-spin + else if commentUse[0] === 'Twikoo' && theme.twikoo.visitor + +pvBlock('','','') + span#twikoo_visitors + i.fa-solid.fa-spinner.fa-spin + else if theme.busuanzi.page_pv + +pvBlock('','post-meta-pv-cv','') + span#busuanzi_value_page_pv + i.fa-solid.fa-spinner.fa-spin else if theme.busuanzi.page_pv +pvBlock('','post-meta-pv-cv','') span#busuanzi_value_page_pv + i.fa-solid.fa-spinner.fa-spin if comments.count && !comments.lazyload && page.comments !== false && comments.use - var whichCount = comments.use[0] @@ -97,27 +97,42 @@ case whichCount when 'Disqus' - when 'Disqusjs' +countBlock span.disqus-comment-count a(href=full_url_for(page.path) + '#disqus_thread') + i.fa-solid.fa-spinner.fa-spin + when 'Disqusjs' + +countBlock + a(href=full_url_for(page.path) + '#disqusjs') + span.disqus-comment-count(data-disqus-url=full_url_for(page.path)) + i.fa-solid.fa-spinner.fa-spin when 'Valine' +countBlock a(href=url_for(page.path) + '#post-comment' itemprop="discussionUrl") span.valine-comment-count(data-xid=url_for(page.path) itemprop="commentCount") + i.fa-solid.fa-spinner.fa-spin when 'Waline' +countBlock a(href=url_for(page.path) + '#post-comment') span.waline-comment-count(data-path=url_for(page.path)) + i.fa-solid.fa-spinner.fa-spin when 'Gitalk' +countBlock a(href=url_for(page.path) + '#post-comment') span.gitalk-comment-count + i.fa-solid.fa-spinner.fa-spin when 'Twikoo' +countBlock a(href=url_for(page.path) + '#post-comment') span#twikoo-count + i.fa-solid.fa-spinner.fa-spin when 'Facebook Comments' +countBlock a(href=url_for(page.path) + '#post-comment') - span.fb-comments-count(data-href=urlNoIndex()) \ No newline at end of file + span.fb-comments-count(data-href=urlNoIndex()) + i.fa-solid.fa-spinner.fa-spin + when 'Remark42' + +countBlock + a(href=url_for(page.path) + '#post-comment') + span.remark42__counter(data-url=urlNoIndex()) + i.fa-solid.fa-spinner.fa-spin \ No newline at end of file diff --git a/layout/includes/layout.pug b/layout/includes/layout.pug index aabce94..414b45d 100644 --- a/layout/includes/layout.pug +++ b/layout/includes/layout.pug @@ -1,5 +1,5 @@ - var htmlClassHideAside = theme.aside.enable && theme.aside.hide ? 'hide-aside' : '' -- page.aside = is_archive() ? theme.aside.archives : page.aside +- page.aside = is_archive() ? theme.aside.display.archive: is_category() ? theme.aside.display.category : is_tag() ? theme.aside.display.tag : page.aside - var hideAside = !theme.aside.enable || page.aside === false ? 'hide-aside' : '' - var pageType = is_post() ? 'post' : 'page' diff --git a/layout/includes/mixins/post-ui.pug b/layout/includes/mixins/post-ui.pug index d2cb014..3d8619b 100644 --- a/layout/includes/mixins/post-ui.pug +++ b/layout/includes/mixins/post-ui.pug @@ -5,7 +5,7 @@ mixin postUI(posts) let link = article.link || article.path let title = article.title || _p('no_title') const position = theme.cover.position - let leftOrRight = position === 'both' + let leftOrRight = position === 'both' ? index%2 == 0 ? 'left' : 'right' : position === 'left' ? 'left' : 'right' let post_cover = article.cover @@ -70,24 +70,38 @@ mixin postUI(posts) if theme.comments.card_post_count case theme.comments.use[0] when 'Disqus' - when 'Disqusjs' +countBlockInIndex a(href=full_url_for(link) + '#disqus_thread') + i.fa-solid.fa-spinner.fa-spin + when 'Disqusjs' + +countBlockInIndex + a(href=full_url_for(link) + '#disqusjs') + span.disqus-comment-count(data-disqus-url=full_url_for(link)) + i.fa-solid.fa-spinner.fa-spin when 'Valine' +countBlockInIndex - a(href=url_for(link) + '#post-comment' itemprop="discussionUrl") - span.valine-comment-count(data-xid=url_for(link) itemprop="commentCount") + a(href=url_for(link) + '#post-comment') + span.valine-comment-count(data-xid=url_for(link)) + i.fa-solid.fa-spinner.fa-spin when 'Waline' +countBlockInIndex a(href=url_for(link) + '#post-comment') span.waline-comment-count(id=url_for(link)) + i.fa-solid.fa-spinner.fa-spin when 'Twikoo' +countBlockInIndex a.twikoo-count(href=url_for(link) + '#post-comment') + i.fa-solid.fa-spinner.fa-spin when 'Facebook Comments' +countBlockInIndex a(href=url_for(link) + '#post-comment') span.fb-comments-count(data-href=urlNoIndex(article.permalink)) + i.fa-solid.fa-spinner.fa-spin + when 'Remark42' + +countBlockInIndex + a(href=url_for(link) + '#post-comment') + span.remark42__counter(data-url=urlNoIndex(article.permalink)) + i.fa-solid.fa-spinner.fa-spin //- Display the article introduction on homepage case theme.index_post_content.method diff --git a/layout/includes/third-party/card-post-count/index.pug b/layout/includes/third-party/card-post-count/index.pug index 44839b3..0abc970 100644 --- a/layout/includes/third-party/card-post-count/index.pug +++ b/layout/includes/third-party/card-post-count/index.pug @@ -9,4 +9,6 @@ case theme.comments.use[0] when 'Waline' include ./waline.pug when 'Facebook Comments' - include ./fb.pug \ No newline at end of file + include ./fb.pug + when 'Remark42' + include ./remark42.pug \ No newline at end of file diff --git a/layout/includes/third-party/card-post-count/remark42.pug b/layout/includes/third-party/card-post-count/remark42.pug new file mode 100644 index 0000000..b67164f --- /dev/null +++ b/layout/includes/third-party/card-post-count/remark42.pug @@ -0,0 +1,18 @@ +- const { host, siteId, option } = theme.remark42 + +script. + (()=>{ + window.remark_config = Object.assign({ + host: '!{host}', + site_id: '!{siteId}', + },!{JSON.stringify(option)}) + + function getCount () { + const s = document.createElement('script') + s.src = remark_config.host + '/web/counter.js' + s.defer = true + document.head.appendChild(s) + } + + window.pjax ? getCount() : window.addEventListener('load', getCount) + })() \ No newline at end of file diff --git a/layout/includes/third-party/card-post-count/valine.pug b/layout/includes/third-party/card-post-count/valine.pug index ad1a9c6..dfe9b6d 100644 --- a/layout/includes/third-party/card-post-count/valine.pug +++ b/layout/includes/third-party/card-post-count/valine.pug @@ -8,6 +8,7 @@ script. appKey: '#{theme.valine.appKey}', serverURLs: '#{theme.valine.serverURLs}' } + const valine = new Valine(initData) } diff --git a/layout/includes/third-party/comments/disqus.pug b/layout/includes/third-party/comments/disqus.pug index 4c2c727..caa1222 100644 --- a/layout/includes/third-party/comments/disqus.pug +++ b/layout/includes/third-party/comments/disqus.pug @@ -4,7 +4,7 @@ script. function loadDisqus () { var disqus_config = function () { this.page.url = '!{ page.permalink }' - this.page.identifier = '!{ page.path }' + this.page.identifier = '!{ url_for(page.path) }' this.page.title = '!{ disqusPageTitle }' }; @@ -24,6 +24,10 @@ script. (d.head || d.body).appendChild(s); })(); } + + document.getElementById('darkmode').addEventListener('click', () => { + setTimeout(() => window.disqusReset(), 200) + }) } if ('!{theme.comments.use[0]}' === 'Disqus' || !!{theme.comments.lazyload}) { diff --git a/layout/includes/third-party/comments/disqusjs.pug b/layout/includes/third-party/comments/disqusjs.pug index 344be55..1e66555 100644 --- a/layout/includes/third-party/comments/disqusjs.pug +++ b/layout/includes/third-party/comments/disqusjs.pug @@ -10,18 +10,27 @@ script. } function initDisqusjs () { - window.DISQUS = null - new DisqusJS(Object.assign({ + window.disqusjs = null + disqusjs = new DisqusJS(Object.assign({ shortname: '!{theme.disqusjs.shortname}', - identifier: '!{ page.path }', + identifier: '!{ url_for(page.path) }', url: '!{ page.permalink }', title: '!{ disqusjsPageTitle }', apikey: '!{theme.disqusjs.apikey}', },!{JSON.stringify(theme.disqusjs.option)})) + + disqusjs.render(document.getElementById('disqusjs')) + } + + const themeChange = () => { + const ele = document.getElementById('disqus_thread') + if(!ele) return + disqusjs.destroy() + initDisqusjs() } - window.disqusReset = initDisqusjs + document.getElementById('darkmode').addEventListener('click', themeChange) if (window.disqusJsLoad) initDisqusjs() else { @@ -32,7 +41,7 @@ script. } if ('!{theme.comments.use[0]}' === 'Disqusjs' || !!{theme.comments.lazyload}) { - if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('disqus_thread'), loadDisqusjs) + if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('disqusjs'), loadDisqusjs) else loadDisqusjs() } else { diff --git a/layout/includes/third-party/comments/index.pug b/layout/includes/third-party/comments/index.pug index 6c725d6..36c6978 100644 --- a/layout/includes/third-party/comments/index.pug +++ b/layout/includes/third-party/comments/index.pug @@ -22,7 +22,7 @@ hr when 'Valine' #vcomment.vcomment when 'Disqusjs' - #disqus_thread + #disqusjs when 'Livere' #lv-container(data-id="city" data-uid=theme.livere.uid) when 'Gitalk' @@ -40,3 +40,5 @@ hr data-numposts= theme.facebook_comments.pageSize || 10 data-order-by= theme.facebook_comments.order_by || 'social' data-width="100%") + when 'Remark42' + #remark42 diff --git a/layout/includes/third-party/comments/js.pug b/layout/includes/third-party/comments/js.pug index 1acd8d3..39e3871 100644 --- a/layout/includes/third-party/comments/js.pug +++ b/layout/includes/third-party/comments/js.pug @@ -20,3 +20,5 @@ each name in theme.comments.use !=partial('includes/third-party/comments/giscus', {}, {cache: true}) when 'Facebook Comments' !=partial('includes/third-party/comments/facebook_comments', {}, {cache: true}) + when 'Remark42' + !=partial('includes/third-party/comments/remark42', {}, {cache: true}) diff --git a/layout/includes/third-party/comments/remark42.pug b/layout/includes/third-party/comments/remark42.pug new file mode 100644 index 0000000..d6d49a6 --- /dev/null +++ b/layout/includes/third-party/comments/remark42.pug @@ -0,0 +1,67 @@ +- 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)}) + + 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) + } + } + + function initRemark42() { + if (window.REMARK42) { + if (this.remark42Instance) { + this.remark42Instance.destroy() + } + + this.remark42Instance = window.REMARK42.createInstance({ + ...remark_config + }) + } + } + + 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) + } + } + + function loadRemark42 () { + if (window.REMARK42) { + this.initRemark42() + getCount() + } else { + addRemark42() + window.addEventListener('REMARK42::ready', () => { + this.initRemark42() + getCount() + }) + } + } + + document.getElementById('darkmode').addEventListener('click',()=>{ + if (!window.REMARK42) return + let theme = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark' + window.REMARK42.changeTheme(theme) + }) + + 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() + } + } diff --git a/layout/includes/third-party/comments/twikoo.pug b/layout/includes/third-party/comments/twikoo.pug index 14c55e2..437a42f 100644 --- a/layout/includes/third-party/comments/twikoo.pug +++ b/layout/includes/third-party/comments/twikoo.pug @@ -50,4 +50,4 @@ script. loadTwikoo() } } - })() + })() \ No newline at end of file diff --git a/layout/includes/third-party/math/katex.pug b/layout/includes/third-party/math/katex.pug index f69e8b8..c1f0bd6 100644 --- a/layout/includes/third-party/math/katex.pug +++ b/layout/includes/third-party/math/katex.pug @@ -1,6 +1,5 @@ link(rel="stylesheet" type="text/css" href=url_for(theme.asset.katex)) script(src=url_for(theme.asset.katex_copytex)) -link(rel="stylesheet" type="text/css" href=url_for(theme.asset.katex_copytex_css)) script. (() => { document.querySelectorAll('#article-container span.katex-display').forEach(item => { diff --git a/layout/includes/third-party/newest-comments/index.pug b/layout/includes/third-party/newest-comments/index.pug index 190e81a..826ccff 100644 --- a/layout/includes/third-party/newest-comments/index.pug +++ b/layout/includes/third-party/newest-comments/index.pug @@ -23,4 +23,6 @@ if use include ./github-issues.pug when 'Utterances' - userRepo = theme.utterances.repo - include ./github-issues.pug \ No newline at end of file + include ./github-issues.pug + when 'Remark42' + include ./remark42.pug \ No newline at end of file diff --git a/layout/includes/third-party/newest-comments/remark42.pug b/layout/includes/third-party/newest-comments/remark42.pug new file mode 100644 index 0000000..d14adb9 --- /dev/null +++ b/layout/includes/third-party/newest-comments/remark42.pug @@ -0,0 +1,80 @@ +- const { host, siteId } = theme.remark42 + +script. + window.addEventListener('load', () => { + const changeContent = (content) => { + if (content === '') return content + + content = content.replace(/]+>/ig, '[!{_p("aside.card_newest_comments.image")}]') // replace image link + content = content.replace(/]+?href=["']?([^"']+)["']?[^>]*>([^<]+)<\/a>/gi, '[!{_p("aside.card_newest_comments.link")}]') // replace url + content = content.replace(/
.*?<\/pre>/gi, '[!{_p("aside.card_newest_comments.code")}]') // replace code
+      content = content.replace(/<[^>]+>/g,"") // remove html tag
+
+      if (content.length > 150) {
+        content = content.substring(0,150) + '...'
+      }
+      return content
+    }
+
+    const generateHtml = array => {
+      let result = ''
+
+      if (array.length) {
+        for (let i = 0; i < array.length; i++) {
+          result += '
' + + if (!{theme.newest_comments.avatar}) { + const name = '!{theme.lazyload.enable ? "data-lazy-src" : "src"}' + result += `${array[i].nick}` + } + + result += `
+ ${array[i].content} +
${array[i].nick} /
+
` + } + } else { + result += '!{_p("aside.card_newest_comments.zero")}' + } + + let $dom = document.querySelector('#card-newest-comments .aside-list') + $dom.innerHTML= result + window.lazyLoadInstance && window.lazyLoadInstance.update() + window.pjax && window.pjax.refresh($dom) + } + + const getComment = () => { + fetch('!{host}/api/v1/last/!{theme.newest_comments.limit}?site=!{siteId}') + .then(response => response.json()) + .then(data => { + const remark42 = data.map(function (e) { + return { + 'avatar': e.user.picture, + 'content': changeContent(e.text), + 'nick': e.user.name, + 'url': e.locator.url, + 'date': e.time, + } + }) + saveToLocal.set('remark42-newest-comments', JSON.stringify(remark42), !{theme.newest_comments.storage}/(60*24)) + generateHtml(remark42) + }).catch(e => { + const $dom = document.querySelector('#card-newest-comments .aside-list') + $dom.innerHTML= "!{_p('aside.card_newest_comments.error')}" + }) + } + + const newestCommentInit = () => { + if (document.querySelector('#card-newest-comments .aside-list')) { + const data = saveToLocal.get('valine-newest-comments') + if (data) { + generateHtml(JSON.parse(data)) + } else { + getComment() + } + } + } + + newestCommentInit() + document.addEventListener('pjax:complete', newestCommentInit) + }) diff --git a/layout/includes/third-party/pjax.pug b/layout/includes/third-party/pjax.pug index 7b63ddd..3700010 100644 --- a/layout/includes/third-party/pjax.pug +++ b/layout/includes/third-party/pjax.pug @@ -47,6 +47,7 @@ script. const $bodyClassList = document.body.classList $bodyClassList.contains('read-mode') && $bodyClassList.remove('read-mode') + typeof disqusjs === 'object' && disqusjs.destroy() }) document.addEventListener('pjax:complete', function () { diff --git a/layout/includes/widget/card_webinfo.pug b/layout/includes/widget/card_webinfo.pug index 3bff545..9ff3de0 100644 --- a/layout/includes/widget/card_webinfo.pug +++ b/layout/includes/widget/card_webinfo.pug @@ -12,20 +12,24 @@ if theme.aside.card_webinfo.enable .webinfo-item .item-name= _p('aside.card_webinfo.runtime.name') + " :" .item-count#runtimeshow(data-publishDate=date_xml(theme.runtimeshow.publish_date)) + i.fa-solid.fa-spinner.fa-spin if theme.wordcount.enable && theme.wordcount.total_wordcount .webinfo-item .item-name=_p('aside.card_webinfo.site_wordcount') + " :" .item-count=totalcount(site) - if theme.busuanzi.site_uv + if theme.busuanzi.site_uv .webinfo-item .item-name= _p('aside.card_webinfo.site_uv_name') + " :" .item-count#busuanzi_value_site_uv + i.fa-solid.fa-spinner.fa-spin if theme.busuanzi.site_pv .webinfo-item .item-name= _p('aside.card_webinfo.site_pv_name') + " :" .item-count#busuanzi_value_site_pv + i.fa-solid.fa-spinner.fa-spin if theme.aside.card_webinfo.last_push_date .webinfo-item .item-name= _p('aside.card_webinfo.last_push_date.name') + " :" .item-count#last-push-date(data-lastPushDate=date_xml(Date.now())) + i.fa-solid.fa-spinner.fa-spin diff --git a/package.json b/package.json index c32e915..d2a80b6 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hexo-theme-butterfly", - "version": "4.2.2", + "version": "4.3.0", "description": "A Simple and Card UI Design theme for Hexo", "main": "package.json", "scripts": { @@ -23,7 +23,7 @@ "email": "my@crazywong.com" }, "dependencies": { - "hexo-renderer-stylus": "^2.0.1", + "hexo-renderer-stylus": "^2.1.0", "hexo-renderer-pug": "^3.0.0" }, "homepage": "https://butterfly.js.org/", diff --git a/plugins.yml b/plugins.yml new file mode 100644 index 0000000..c0d3eae --- /dev/null +++ b/plugins.yml @@ -0,0 +1,182 @@ +algolia_search_v4: + name: algoliasearch + file: dist/algoliasearch-lite.umd.js + version: 4.13.1 +instantsearch_v4: + name: instantsearch.js + file: dist/instantsearch.production.min.js + version: 4.42.0 +pjax: + name: pjax + file: pjax.min.js + version: 0.2.8 +gitalk: + name: gitalk + file: dist/gitalk.min.js + version: 1.7.2 +gitalk_css: + name: gitalk + file: dist/gitalk.css + version: 1.7.2 +blueimp_md5: + name: blueimp-md5 + file: js/md5.min.js + version: 2.19.0 +valine: + name: valine + file: dist/Valine.min.js + version: 1.4.18 +disqusjs: + name: disqusjs + file: dist/browser/disqusjs.es2015.umd.min.js + version: 3.0.1 +disqusjs_css: + name: disqusjs + file: dist/browser/styles/disqusjs.css + version: 3.0.1 +twikoo: + name: twikoo + file: dist/twikoo.all.min.js + version: 1.5.11 +waline_js: + name: '@waline/client' + file: dist/waline.js + other_name: waline + version: 2.6.1 +waline_css: + name: '@waline/client' + file: dist/waline.css + other_name: waline + version: 2.6.1 +sharejs: + name: butterfly-extsrc + file: sharejs/dist/js/social-share.min.js + version: 1.1.3 +sharejs_css: + name: butterfly-extsrc + file: sharejs/dist/css/share.min.css + version: 1.1.3 +mathjax: + name: mathjax + file: es5/tex-mml-chtml.js + version: 3.2.2 +katex: + name: katex + file: dist/katex.min.css + other_name: KaTeX + version: 0.16.0 +katex_copytex: + name: katex + file: dist/contrib/copy-tex.min.js + other_name: KaTeX + version: 0.16.0 +mermaid: + name: mermaid + file: dist/mermaid.min.js + version: 9.1.2 +canvas_ribbon: + name: butterfly-extsrc + file: dist/canvas-ribbon.min.js + version: 1.1.3 +canvas_fluttering_ribbon: + name: butterfly-extsrc + file: dist/canvas-fluttering-ribbon.min.js + version: 1.1.3 +canvas_nest: + name: butterfly-extsrc + file: dist/canvas-nest.min.js + version: 1.1.3 +activate_power_mode: + name: butterfly-extsrc + file: dist/activate-power-mode.min.js + version: 1.1.3 +fireworks: + name: butterfly-extsrc + file: dist/fireworks.min.js + version: 1.1.3 +click_heart: + name: butterfly-extsrc + file: dist/click-heart.min.js + version: 1.1.3 +ClickShowText: + name: butterfly-extsrc + file: dist/click-show-text.min.js + version: 1.1.3 +lazyload: + name: vanilla-lazyload + file: dist/lazyload.iife.min.js + version: 17.3.1 +instantpage: + name: instant.page + file: instantpage.js + version: 5.1.0 +typed: + name: typed.js + file: lib/typed.min.js + version: 2.0.12 +pangu: + name: pangu + file: dist/browser/pangu.min.js + version: 4.0.7 +fancybox_css_v4: + name: '@fancyapps/ui' + file: dist/fancybox.css + version: 4.0.27 + other_name: fancyapps-ui +fancybox_v4: + name: '@fancyapps/ui' + file: dist/fancybox.umd.js + version: 4.0.27 + other_name: fancyapps-ui +medium_zoom: + name: medium-zoom + file: dist/medium-zoom.min.js + version: 1.0.6 +snackbar_css: + name: node-snackbar + file: dist/snackbar.min.css + version: 0.1.16 +snackbar: + name: node-snackbar + file: dist/snackbar.min.js + version: 0.1.16 +fontawesomeV6: + name: '@fortawesome/fontawesome-free' + file: css/all.min.css + other_name: font-awesome + version: 6.1.1 +flickr_justified_gallery_js: + name: flickr-justified-gallery + file: dist/fjGallery.min.js + version: 2.1.2 +flickr_justified_gallery_css: + name: flickr-justified-gallery + file: dist/fjGallery.css + version: 2.1.2 +aplayer_css: + name: aplayer + file: dist/APlayer.min.css + version: 1.10.1 +aplayer_js: + name: aplayer + file: dist/APlayer.min.js + version: 1.10.1 +meting_js: + name: butterfly-extsrc + file: metingjs/dist/Meting.min.js + version: 1.1.3 +prismjs_js: + name: prismjs + file: prism.js + other_name: prism + version: 1.28.0 +prismjs_lineNumber_js: + name: prismjs + file: plugins/line-numbers/prism-line-numbers.min.js + other_name: prism + version: 1.28.0 +prismjs_autoloader: + name: prismjs + file: plugins/autoloader/prism-autoloader.min.js + other_name: prism + version: 1.28.0 \ No newline at end of file diff --git a/scripts/events/cdn.js b/scripts/events/cdn.js new file mode 100644 index 0000000..7d77e63 --- /dev/null +++ b/scripts/events/cdn.js @@ -0,0 +1,94 @@ +/** + * Butterfly + * Merge CDN + */ + +'use strict' + +const { version } = require('../../package.json') +const path = require('path') + +hexo.extend.filter.register('before_generate', () => { + const themeConfig = hexo.theme.config + const { CDN } = themeConfig + + const thirdPartySrc = hexo.render.renderSync({ path: path.join(hexo.theme_dir,'/plugins.yml'), engine: 'yaml'}) + const internalSrc = { + main: { + name: 'hexo-theme-butterfly', + file: 'js/main.js', + version + }, + utils: { + name: 'hexo-theme-butterfly', + file: 'js/utils.js', + version + }, + translate: { + name: 'hexo-theme-butterfly', + file: 'js/tw_cn.js', + version + }, + local_search: { + name: 'hexo-theme-butterfly', + file: 'js/search/local-search.js', + version + }, + algolia_js: { + name: 'hexo-theme-butterfly', + file: 'js/search/algolia.js', + version + } + } + + const minFile = (file) => { + return file.replace(/(? '.min' + ext) + } + + const createCDNLink = (data, type, cond = '') => { + Object.keys(data).map(key => { + let { name, version, file, other_name } = data[key] + + const min_file = minFile(file) + const cdnjs_name = other_name || name + const cdnjs_file = file.replace(/^[lib|dist]*\/|browser\//g, '') + const min_cdnjs_file = minFile(cdnjs_file) + if (cond === 'internal') file = `source/${file}` + const verType = CDN.version ? `@${version}` : '' + + const value = { + version, + name, + file, + cdnjs_file, + min_file, + min_cdnjs_file, + cdnjs_name + } + const cdnSource = { + local: cond === 'internal' ? cdnjs_file : `/pluginsSrc/${name}/${file}`, + jsdelivr: `https://cdn.jsdelivr.net/npm/${name}${verType}/${min_file}`, + unpkg: `https://unpkg.com/${name}${verType}/${file}`, + cdnjs: `https://cdnjs.cloudflare.com/ajax/libs/${cdnjs_name}/${version}/${min_cdnjs_file}`, + custom: (CDN.custom_format || '').replace(/\$\{(.+?)\}/g, (match, $1) => value[$1]) + } + + data[key] = cdnSource[type] + }) + + if (cond === 'internal') data['main_css'] = 'css/index.css' + return data + } + + // delete null value + const deleteNullValue = obj => { + if (!obj) return + for (const i in obj) { + obj[i] === null && delete obj[i] + } + return obj + } + + themeConfig.asset = Object.assign(createCDNLink(internalSrc,CDN.internal_provider,'internal'), + createCDNLink(thirdPartySrc,CDN.third_party_provider), deleteNullValue(CDN.option)) +}) diff --git a/scripts/events/comment.js b/scripts/events/comment.js new file mode 100644 index 0000000..b1ef856 --- /dev/null +++ b/scripts/events/comment.js @@ -0,0 +1,14 @@ +/** + * Capitalize the first letter of comment name + */ + +hexo.extend.filter.register('before_generate', () => { + const themeConfig = hexo.theme.config + let { use } = themeConfig.comments + if (!use) return + if (typeof use === 'string') { + use = use.split(',') + } + const newArray = use.map(item => item.toLowerCase().replace(/\b[a-z]/g, s => s.toUpperCase())) + themeConfig.comments.use = newArray +}) \ No newline at end of file diff --git a/scripts/events/config.js b/scripts/events/config.js deleted file mode 100644 index 2342902..0000000 --- a/scripts/events/config.js +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Butterfly - * 1. Merge CDN - * 2. Capitalize the first letter of comment name - */ - -'use strict' - -const { version } = require('../../package.json') -const path = require('path') - -hexo.extend.filter.register('before_generate', () => { - const themeConfig = hexo.theme.config - const { CDN, comments } = themeConfig - - /** - * Merge CDN - */ - - const internalSrcCDN = { - main_css: '/css/index.css', - main: `https://cdn.jsdelivr.net/npm/hexo-theme-butterfly@${version}/source/js/main.min.js`, - utils: `https://cdn.jsdelivr.net/npm/hexo-theme-butterfly@${version}/source/js/utils.min.js`, - translate: `https://cdn.jsdelivr.net/npm/hexo-theme-butterfly@${version}/source/js/tw_cn.min.js`, - local_search: `https://cdn.jsdelivr.net/npm/hexo-theme-butterfly@${version}/source/js/search/local-search.min.js`, - algolia_js: `https://cdn.jsdelivr.net/npm/hexo-theme-butterfly@${version}/source/js/search/algolia.min.js`, - } - - const internalSrcLocal = { - main_css: '/css/index.css', - main: '/js/main.js', - utils: '/js/utils.js', - translate: '/js/tw_cn.js', - local_search: '/js/search/local-search.js', - algolia_js: '/js/search/algolia.js', - } - - const thirdPartySrcCDN = { - algolia_search_v4: 'https://cdn.jsdelivr.net/npm/algoliasearch@4/dist/algoliasearch-lite.umd.js', - instantsearch_v4: 'https://cdn.jsdelivr.net/npm/instantsearch.js@4/dist/instantsearch.production.min.js', - pjax: 'https://cdn.jsdelivr.net/npm/pjax/pjax.min.js', - gitalk: 'https://cdn.jsdelivr.net/npm/gitalk@latest/dist/gitalk.min.js', - gitalk_css: 'https://cdn.jsdelivr.net/npm/gitalk/dist/gitalk.min.css', - blueimp_md5: 'https://cdn.jsdelivr.net/npm/blueimp-md5/js/md5.min.js', - valine: 'https://cdn.jsdelivr.net/npm/valine/dist/Valine.min.js', - disqusjs: 'https://cdn.jsdelivr.net/npm/disqusjs@1/dist/disqus.js', - disqusjs_css: 'https://cdn.jsdelivr.net/npm/disqusjs@1/dist/disqusjs.css', - twikoo: 'https://cdn.jsdelivr.net/npm/twikoo@1/dist/twikoo.all.min.js', - waline_js: 'https://cdn.jsdelivr.net/npm/@waline/client/dist/waline.js', - waline_css: 'https://cdn.jsdelivr.net/npm/@waline/client/dist/waline.css', - sharejs: 'https://cdn.jsdelivr.net/gh/overtrue/share.js@master/dist/js/social-share.min.js', - sharejs_css: 'https://cdn.jsdelivr.net/npm/social-share.js/dist/css/share.min.css', - mathjax: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js', - katex: 'https://cdn.jsdelivr.net/npm/katex@latest/dist/katex.min.css', - katex_copytex: 'https://cdn.jsdelivr.net/npm/katex@latest/dist/contrib/copy-tex.min.js', - katex_copytex_css: 'https://cdn.jsdelivr.net/npm/katex@latest/dist/contrib/copy-tex.css', - mermaid: 'https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js', - canvas_ribbon: 'https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-ribbon.min.js', - canvas_fluttering_ribbon: 'https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-fluttering-ribbon.min.js', - canvas_nest: 'https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-nest.min.js', - activate_power_mode: 'https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/activate-power-mode.min.js', - fireworks: 'https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/fireworks.min.js', - click_heart: 'https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/click-heart.min.js', - ClickShowText: 'https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/click-show-text.min.js', - lazyload: 'https://cdn.jsdelivr.net/npm/vanilla-lazyload/dist/lazyload.iife.min.js', - instantpage: 'https://cdn.jsdelivr.net/npm/instant.page@5/instantpage.min.js', - typed: 'https://cdn.jsdelivr.net/npm/typed.js/lib/typed.min.js', - pangu: 'https://cdn.jsdelivr.net/npm/pangu@4/dist/browser/pangu.min.js', - fancybox_css_v4: 'https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.css', - fancybox_v4: 'https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.js', - medium_zoom: 'https://cdn.jsdelivr.net/npm/medium-zoom/dist/medium-zoom.min.js', - snackbar_css: 'https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.css', - snackbar: 'https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.js', - fontawesomeV6: 'https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@6/css/all.min.css', - flickr_justified_gallery_js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.js', - flickr_justified_gallery_css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.css', - aplayer_css: 'https://cdn.jsdelivr.net/npm/aplayer@1/dist/APlayer.min.css', - aplayer_js: 'https://cdn.jsdelivr.net/npm/aplayer@1/dist/APlayer.min.js', - meting_js: 'https://cdn.jsdelivr.net/gh/metowolf/MetingJS@1.2/dist/Meting.min.js', - prismjs_js: 'https://cdn.jsdelivr.net/npm/prismjs@1/prism.min.js', - prismjs_lineNumber_js: 'https://cdn.jsdelivr.net/npm/prismjs@1/plugins/line-numbers/prism-line-numbers.min.js', - prismjs_autoloader: 'https://cdn.jsdelivr.net/npm/prismjs@1/plugins/autoloader/prism-autoloader.min.js', - } - - // delete null value - const deleteNullValue = obj => { - if (!obj) return - for (const i in obj) { - obj[i] === null && delete obj[i] - } - return obj - } - - const defaultVal = (obj, choose) => { - if (obj === 'internal') { - if (choose === 'local') return internalSrcLocal - else return internalSrcCDN - } - - if (obj === 'external') { - if (choose === 'local') { - let result = {} - try { - const data = path.join(hexo.plugin_dir,'hexo-butterfly-extjs/plugins.yml') - result = hexo.render.renderSync({ path: data, engine: 'yaml'}) - Object.keys(result).map(key => { - result[key] = '/pluginsSrc/' + result[key] - }) - } catch (e) {} - return result - } else return thirdPartySrcCDN - } - } - - themeConfig.asset = Object.assign(defaultVal('internal', CDN.internal_provider), - defaultVal('external', CDN.third_party_provider), deleteNullValue(CDN.option)) - - /** - * Capitalize the first letter of comment name - */ - - let { use } = comments - - if (!use) return - - if (typeof use === 'string') { - use = use.split(',') - } - - const newArray = use.map(item => item.toLowerCase().replace(/\b[a-z]/g, s => s.toUpperCase())) - - themeConfig.comments.use = newArray -}) diff --git a/scripts/helpers/findArchiveLength.js b/scripts/helpers/findArchiveLength.js new file mode 100644 index 0000000..e55dc0f --- /dev/null +++ b/scripts/helpers/findArchiveLength.js @@ -0,0 +1,58 @@ +hexo.extend.helper.register('findArchiveLength', function (func) { + const allPostsLength = this.site.posts.length; + if (hexo.config.archive_generator && hexo.config.archive_generator.enable === false ) return allPostsLength + const { yearly, monthly, daily } = hexo.config.archive_generator + const { year, month, day } = this.page + if (yearly === false || !year) return allPostsLength + + const posts = this.site.posts.sort('date') + + const compareFunc = (type,y1,m1,d1,y2,m2,d2) => { + if (type === 'year') { + return y1 === y2 + } else if (type === 'month') { + return y1 === y2 && m1 === m2 + } else if (type === 'day') { + return y1 === y2 && m1 === m2 && d1 === d2 + } + } + + const generateDateObj = (type) => { + let dateObj = [] + let length = 0 + + posts.forEach(post => { + let date = post.date.clone() + const year = date.year() + const month = date.month() + 1 + const day = date.date() + let lastData = dateObj[length - 1] + + if (!lastData || !compareFunc(type, lastData.year, lastData.month, lastData.day, year, month, day)) { + const name = type === 'year' ? year : type === 'month' ? `${year}-${month}` : `${year}-${month}-${day}` + length = dateObj.push({ + name, + year, + month, + day, + count: 1 + }) + } else { + lastData.count++ + } + }); + + return dateObj + } + + const data = func('createArchiveObj', ()=> { + const yearObj = yearly ? generateDateObj('year') : [] + const monthObj = monthly ? generateDateObj('month') : [] + const dayObj = daily ? generateDateObj('day') : [] + const fullObj = [...yearObj, ...monthObj, ...dayObj] + return fullObj + }) + + const name = month ? day ? `${year}-${month}-${day}` : `${year}-${month}` : year + return data.find(item => item.name === name).count +}) \ No newline at end of file diff --git a/scripts/helpers/page.js b/scripts/helpers/page.js index 35c4149..aa2b74b 100644 --- a/scripts/helpers/page.js +++ b/scripts/helpers/page.js @@ -71,7 +71,13 @@ hexo.extend.helper.register('injectHtml', function (data) { return result }) -hexo.extend.helper.register('findArchivesTitle', function (menu) { +hexo.extend.helper.register('findArchivesTitle', function (page, menu, date) { + if (page.year) { + const dateStr = page.month ? `${page.year}-${page.month}` : `${page.year}` + const date_format = page.month ? hexo.theme.config.aside.card_archives.format : 'YYYY' + return date(dateStr, date_format) + } + const defaultTitle = this._p('page.archives') if (!menu) return defaultTitle diff --git a/scripts/tag/note.js b/scripts/tag/note.js index ec4e8b5..aa19129 100644 --- a/scripts/tag/note.js +++ b/scripts/tag/note.js @@ -17,7 +17,7 @@ function postNote (args, content) { const iconArray = args[args.length - 2] if (iconArray && iconArray.startsWith('fa')) { icon = `` - args[args.length - 2] = 'icon' + args[args.length - 2] = 'icon-padding' } return `
${icon + hexo.render.renderSync({ text: content, engine: 'markdown' })}
` diff --git a/source/css/_layout/aside.styl b/source/css/_layout/aside.styl index bc1675a..5d7e6ca 100644 --- a/source/css/_layout/aside.styl +++ b/source/css/_layout/aside.styl @@ -403,7 +403,7 @@ if hexo-config('aside.card_archives.sort_order') .card-archives - order: hexo-config('aside.card-archives.sort_order') + order: hexo-config('aside.card_archives.sort_order') if hexo-config('aside.card_webinfo.sort_order') .card-webinfo diff --git a/source/css/_mode/darkmode.styl b/source/css/_mode/darkmode.styl index f24a258..0eb9108 100644 --- a/source/css/_mode/darkmode.styl +++ b/source/css/_mode/darkmode.styl @@ -118,8 +118,10 @@ if hexo-config('darkmode.enable') || hexo-config('display_mode') == 'dark' fill: alpha(#FFFFFF, .9) !important // Disqusjs 反代模式下的適配 - #disqus_thread + #disqusjs #dsqjs + &:hover, + &:focus, .dsqjs-tab-active, .dsqjs-no-comment color: alpha(#FFFFFF, .7) diff --git a/source/css/_page/homepage.styl b/source/css/_page/homepage.styl index cf88f88..9c8153c 100644 --- a/source/css/_page/homepage.styl +++ b/source/css/_page/homepage.styl @@ -82,6 +82,9 @@ i margin: 0 4px 0 0 + .fa-spinner + margin: 0 + .article-meta-label if hexo-config('post_meta.page.label') padding-right: 4px diff --git a/source/css/_tags/note.styl b/source/css/_tags/note.styl index eaba7af..efd3781 100644 --- a/source/css/_tags/note.styl +++ b/source/css/_tags/note.styl @@ -7,7 +7,7 @@ if hexo-config('note.border_radius') is a 'unit' border-radius: unit(hexo-config('note.border_radius'), px) - &.icon + &.icon-padding padding-left: 3em & > .note-icon diff --git a/source/js/main.js b/source/js/main.js index dee553e..c185dbe 100644 --- a/source/js/main.js +++ b/source/js/main.js @@ -478,7 +478,6 @@ document.addEventListener('DOMContentLoaded', function () { typeof utterancesTheme === 'function' && utterancesTheme() typeof changeGiscusTheme === 'function' && changeGiscusTheme() typeof FB === 'object' && window.loadFBComment() - window.DISQUS && document.getElementById('disqus_thread').children.length && setTimeout(() => window.disqusReset(), 200) typeof runMermaid === 'function' && window.runMermaid() }, showOrHideBtn: (e) => { // rightside 點擊設置 按鈕 展開 diff --git a/source/js/search/algolia.js b/source/js/search/algolia.js index 530aa4d..f0b9f3a 100644 --- a/source/js/search/algolia.js +++ b/source/js/search/algolia.js @@ -91,11 +91,19 @@ window.addEventListener('load', () => { templates: { item(data) { const link = data.permalink ? data.permalink : (GLOBAL_CONFIG.root + data.path) + const result = data._highlightResult + const content = result.contentStripTruncate + ? cutContent(result.contentStripTruncate.value) + : result.contentStrip + ? cutContent(result.contentStrip.value) + : result.content + ? cutContent(result.content.value) + : '' return ` - ${data._highlightResult.title.value || 'no-title'} + ${result.title.value || 'no-title'} -

${cutContent(data._highlightResult.contentStripTruncate.value)}

` +

${content}

` }, empty: function (data) { return (