diff --git a/README.md b/README.md
index e0189ba..6f74c79 100644
--- a/README.md
+++ b/README.md
@@ -12,9 +12,9 @@

-Demo: 👍 [Butterfly](https://butterfly.js.org/) || 🤞 [CrazyWong](https://blog.crazywong.com/)
+📢 Demo: [Butterfly](https://butterfly.js.org/) || [CrazyWong](https://blog.crazywong.com/)
-Docs: 📖 [Butterfly Docs](https://butterfly.js.org/posts/21cfbf15/)
+📖 Docs: [English](https://butterfly.js.org/en/posts/butterfly-docs-en-get-started/) || [Chinese](https://butterfly.js.org/posts/21cfbf15/)
Based on [hexo-theme-melody](https://github.com/Molunerfinn/hexo-theme-melody) theme.
@@ -76,7 +76,7 @@ npm i hexo-theme-butterfly
- [x] WordCount
- [x] Related articles
- [x] Displays outdated notice for a post
-- [x] Share (AddThis/Sharejs/Addtoany)
+- [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/messenger)
diff --git a/README_CN.md b/README_CN.md
index 441cd79..8b2279b 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -12,9 +12,9 @@

-預覽: 👍 [Butterfly](https://butterfly.js.org/) || 🤞 [CrazyWong](https://blog.crazywong.com/)
+📢 預覽: [Butterfly](https://butterfly.js.org/) || [CrazyWong](https://blog.crazywong.com/)
-文檔: 📖 [Butterfly Docs](https://butterfly.js.org/posts/21cfbf15/)
+📖 文檔: [中文](https://butterfly.js.org/posts/21cfbf15/) || [English](https://butterfly.js.org/en/posts/butterfly-docs-en-get-started/)
一款基於[hexo-theme-melody](https://github.com/Molunerfinn/hexo-theme-melody)修改的主題
@@ -76,7 +76,7 @@ theme: butterfly
- [x] 顯示字數統計
- [x] 顯示相關文章
- [x] 過期文章提醒
-- [x] 多種分享系統(AddThis/Sharejs/Addtoany)
+- [x] 多種分享系統(Sharejs/Addtoany)
- [X] 多種評論系統(Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus/Remark42/artalk)
- [x] 支持雙評論部署
- [x] 多種在線聊天(Chatra/Tidio/Daovoice/Crisp/messenger)
diff --git a/_config.yml b/_config.yml
index 5c1b8d1..96cfa5a 100644
--- a/_config.yml
+++ b/_config.yml
@@ -125,11 +125,10 @@ index_post_content:
# anchor
anchor:
- button:
- enable: false
- always_show: false
- icon: # the unicode value of Font Awesome icon, such as '\3423'
- auto_update: false # when you scroll in post, the URL will update according to header id.
+ # when you scroll, the URL will update according to header id.
+ auto_update: false
+ # Click the headline to scroll and update the anchor
+ click_to_scroll: false
# figcaption (圖片描述文字)
photofigcaption: false
@@ -164,6 +163,7 @@ post_copyright:
# Sponsor/reward
reward:
enable: false
+ text:
QR_code:
# - img: /img/wechat.jpg
# link:
@@ -264,6 +264,10 @@ aside:
post_count: true
last_push_date: true
sort_order: # Don't modify the setting unless you know how it works
+ card_post_series:
+ enable: true
+ orderBy: 'date' # Order by title or date
+ order: -1 # Sort of order. 1, asc for ascending; -1, desc for descending
# busuanzi count for PV / UV in site
# 訪問人數
@@ -381,12 +385,6 @@ docsearch:
# Share System (分享)
# --------------------------------------
-# AddThis
-# https://www.addthis.com/
-addThis:
- enable: false
- pubid:
-
# Share.js
# https://github.com/overtrue/share.js
sharejs:
@@ -637,6 +635,9 @@ index_top_img_height:
category_ui: # 留空或 index
tag_ui: # 留空或 index
+# Stretches the lines so that each line has equal width(文字向兩側對齊,對最後一行無效)
+text_align_justify: false
+
# Website Background (設置網站背景)
# can set it to color or image (可設置圖片 或者 顔色)
# The formal of image: url(http://xxxxxx.com/xxx.jpg)
@@ -651,7 +652,7 @@ mask:
footer: true
# the position of bottom right button/default unit: px (右下角按鈕距離底部的距離/默認單位為px)
-rightside-bottom:
+rightside_bottom:
# Enter transitions (開啓網頁進入效果)
enter_transitions: true
@@ -704,7 +705,7 @@ click_heart:
mobile: false
# Mouse click effects: words (鼠標點擊效果: 文字)
-ClickShowText:
+clickShowText:
enable: false
text:
# - I
@@ -796,6 +797,13 @@ fancybox: true
# Tag Plugins settings (標籤外掛)
# --------------------------------------
+# series (系列文章)
+series:
+ enable: true
+ orderBy: 'title' # Order by title or date
+ order: 1 # Sort of order. 1, asc for ascending; -1, desc for descending
+ number: true
+
# abcjs (樂譜渲染)
# See https://github.com/paulrosen/abcjs
abcjs:
@@ -928,59 +936,58 @@ CDN:
custom_format:
option:
- # main_css:
- # main:
- # utils:
- # translate:
- # local_search:
+ # abcjs_basic_js:
+ # activate_power_mode:
# algolia_js:
# algolia_search:
- # instantsearch:
- # docsearch_js:
- # docsearch_css:
- # pjax:
- # gitalk:
- # gitalk_css:
- # blueimp_md5:
- # valine:
- # disqusjs:
- # disqusjs_css:
- # twikoo:
- # waline_js:
- # waline_css:
- # giscus:
- # sharejs:
- # sharejs_css:
- # mathjax:
- # katex:
- # katex_copytex:
- # mermaid:
- # canvas_ribbon:
- # canvas_fluttering_ribbon:
- # canvas_nest:
- # lazyload:
- # instantpage:
- # typed:
- # pangu:
- # fancybox_css:
- # fancybox:
- # medium_zoom:
- # snackbar_css:
- # snackbar:
- # activate_power_mode:
- # fireworks:
- # click_heart:
- # ClickShowText:
- # fontawesome:
- # flickr_justified_gallery_js:
- # flickr_justified_gallery_css:
# aplayer_css:
# aplayer_js:
+ # artalk_css:
+ # artalk_js:
+ # blueimp_md5:
+ # busuanzi:
+ # canvas_fluttering_ribbon:
+ # canvas_nest:
+ # canvas_ribbon:
+ # click_heart:
+ # clickShowText:
+ # disqusjs:
+ # disqusjs_css:
+ # docsearch_css:
+ # docsearch_js:
+ # egjs_infinitegrid:
+ # fancybox:
+ # fancybox_css:
+ # fireworks:
+ # fontawesome:
+ # gitalk:
+ # gitalk_css:
+ # giscus:
+ # instantpage:
+ # instantsearch:
+ # katex:
+ # katex_copytex:
+ # lazyload:
+ # local_search:
+ # main:
+ # main_css:
+ # mathjax:
+ # medium_zoom:
+ # mermaid:
# meting_js:
+ # pangu:
+ # prismjs_autoloader:
# prismjs_js:
# prismjs_lineNumber_js:
- # prismjs_autoloader:
- # artalk_js:
- # artalk_css:
- # busuanzi:
- # abcjs_basic_js:
+ # pjax:
+ # sharejs:
+ # sharejs_css:
+ # snackbar:
+ # snackbar_css:
+ # translate:
+ # twikoo:
+ # typed:
+ # utils:
+ # valine:
+ # waline_css:
+ # waline_js:
\ No newline at end of file
diff --git a/languages/default.yml b/languages/default.yml
index fea921e..59ddd22 100644
--- a/languages/default.yml
+++ b/languages/default.yml
@@ -3,9 +3,9 @@ footer:
theme: Theme
copy:
- success: Copy successfully
- error: Copy error
- noSupport: The browser does not support
+ success: Copy Successful
+ error: Copy Error
+ noSupport: Browser Not Supported
page:
articles: Articles
@@ -15,16 +15,15 @@ page:
card_post_count: comments
-sticky: Sticky
-no_title: No title
+no_title: Untitled
post:
created: Created
updated: Updated
- wordcount: Word count
- min2read: Reading time
- min2read_unit: min
- page_pv: Post View
+ wordcount: Word Count
+ min2read: Reading Time
+ min2read_unit: mins
+ page_pv: Post Views
comments: Comments
copyright:
author: Author
@@ -48,8 +47,8 @@ search:
hits_stats: '${hits} results found'
pagination:
- prev: Previous Post
- next: Next Post
+ prev: Previous
+ next: Next
comment: Comment
@@ -66,32 +65,33 @@ aside:
headline: Info
article_name: Article
runtime:
- name: Run time
+ name: Runtime
unit: days
last_push_date:
- name: Last Push
+ name: Last Update
site_wordcount: Total Count
site_uv_name: UV
site_pv_name: PV
- more_button: More
+ more_button: View More
card_newest_comments:
- headline: Newest Comments
+ headline: Latest Comments
loading_text: loading...
- error: Unable to get the data, please make sure the settings are correct.
- zero: No Comment
+ error: Unable to retrieve comments, please check the configuration
+ zero: No comments
image: image
link: link
code: code
- card_toc: Catalog
+ card_toc: Contents
+ card_post_series: Series
date_suffix:
- just: Just
+ just: Just now
min: minutes ago
hour: hours ago
day: days ago
month: months ago
-donate: Donate
+donate: Sponsor
share: Share
rightside:
@@ -102,7 +102,7 @@ rightside:
toc: Table Of Contents
scroll_to_comment: Scroll To Comments
setting: Setting
- aside: Toggle between single-column and double-column
+ aside: Toggle between Single-column and Double-column
chat: Chat
copy_copyright:
@@ -112,12 +112,12 @@ copy_copyright:
info: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
Snackbar:
- chs_to_cht: Traditional Chinese Activated Manually
- cht_to_chs: Simplified Chinese Activated Manually
- day_to_night: Dark Mode Activated Manually
- night_to_day: Light Mode Activated Manually
+ chs_to_cht: You have switched to Traditional Chinese
+ cht_to_chs: You have switched to Simplified Chinese
+ day_to_night: You have switched to Dark Mode
+ night_to_day: You have switched to Light Mode
loading: Loading...
load_more: Load More
-error404: Page not found
+error404: Page Not Found
diff --git a/languages/en.yml b/languages/en.yml
index 1cc937c..489b57b 100644
--- a/languages/en.yml
+++ b/languages/en.yml
@@ -3,9 +3,9 @@ footer:
theme: Theme
copy:
- success: Copy successfully
- error: Copy error
- noSupport: The browser does not support
+ success: Copy Successful
+ error: Copy Error
+ noSupport: Browser Not Supported
page:
articles: Articles
@@ -15,16 +15,15 @@ page:
card_post_count: comments
-sticky: Sticky
-no_title: No title
+no_title: Untitled
post:
created: Created
updated: Updated
- wordcount: Word count
- min2read: Reading time
- min2read_unit: min
- page_pv: Post View
+ wordcount: Word Count
+ min2read: Reading Time
+ min2read_unit: mins
+ page_pv: Post Views
comments: Comments
copyright:
author: Author
@@ -48,8 +47,8 @@ search:
hits_stats: '${hits} results found'
pagination:
- prev: Previous Post
- next: Next Post
+ prev: Previous
+ next: Next
comment: Comment
@@ -66,43 +65,44 @@ aside:
headline: Info
article_name: Article
runtime:
- name: Run time
+ name: Runtime
unit: days
last_push_date:
- name: Last Push
+ name: Last Update
site_wordcount: Total Count
site_uv_name: UV
site_pv_name: PV
- more_button: More
+ more_button: View More
card_newest_comments:
- headline: Newest Comments
+ headline: Latest Comments
loading_text: loading...
- error: Unable to get the data, please make sure the settings are correct.
- zero: No Comment
+ error: Unable to retrieve comments, please check the configuration
+ zero: No comments
image: image
link: link
code: code
- card_toc: Catalog
+ card_toc: Contents
+ card_post_series: Series
date_suffix:
- just: Just
+ just: Just now
min: minutes ago
hour: hours ago
day: days ago
month: months ago
-donate: Donate
+donate: Sponsor
share: Share
rightside:
readmode_title: Read Mode
- translate_title: Switch Between Traditional Chinese And Simplified Chinese
- night_mode_title: Switch Between Light And Dark Mode
+ translate_title: Toggle Between Traditional Chinese And Simplified Chinese
+ night_mode_title: Toggle Between Light And Dark Mode
back_to_top: Back To Top
toc: Table Of Contents
scroll_to_comment: Scroll To Comments
setting: Setting
- aside: Toggle between single-column and double-column
+ aside: Toggle between Single-column and Double-column
chat: Chat
copy_copyright:
@@ -112,12 +112,12 @@ copy_copyright:
info: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
Snackbar:
- chs_to_cht: Traditional Chinese Activated Manually
- cht_to_chs: Simplified Chinese Activated Manually
- day_to_night: Dark Mode Activated Manually
- night_to_day: Light Mode Activated Manually
+ chs_to_cht: You have switched to Traditional Chinese
+ cht_to_chs: You have switched to Simplified Chinese
+ day_to_night: You have switched to Dark Mode
+ night_to_day: You have switched to Light Mode
loading: Loading...
load_more: Load More
-error404: Page not found
+error404: Page Not Found
\ No newline at end of file
diff --git a/languages/zh-CN.yml b/languages/zh-CN.yml
index 94f85cd..b030079 100644
--- a/languages/zh-CN.yml
+++ b/languages/zh-CN.yml
@@ -15,7 +15,6 @@ page:
card_post_count: 条评论
-sticky: 置顶
no_title: 无题
post:
@@ -84,6 +83,7 @@ aside:
link: 链接
code: 代码
card_toc: 目录
+ card_post_series: 系列文章
date_suffix:
just: 刚刚
@@ -92,7 +92,7 @@ date_suffix:
day: 天前
month: 个月前
-donate: 打赏
+donate: 赞助
share: 分享
rightside:
@@ -113,8 +113,8 @@ copy_copyright:
info: 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
Snackbar:
- chs_to_cht: 你已切换为繁体
- cht_to_chs: 你已切换为简体
+ chs_to_cht: 你已切换为繁体中文
+ cht_to_chs: 你已切换为简体中文
day_to_night: 你已切换为深色模式
night_to_day: 你已切换为浅色模式
diff --git a/languages/zh-TW.yml b/languages/zh-TW.yml
index 9801d16..4ac63b8 100644
--- a/languages/zh-TW.yml
+++ b/languages/zh-TW.yml
@@ -15,8 +15,7 @@ page:
card_post_count: 條評論
-sticky: 置頂
-no_title: 無題
+no_title: 無標題
post:
created: 發表於
@@ -84,6 +83,7 @@ aside:
link: 連結
code: 程式碼
card_toc: 目錄
+ card_post_series: 文章系列
date_suffix:
just: 剛剛
@@ -92,14 +92,14 @@ date_suffix:
day: 天前
month: 個月前
-donate: 打賞
+donate: 贊助
share: 分享
rightside:
readmode_title: 閱讀模式
translate_title: 簡繁轉換
night_mode_title: 淺色和深色模式轉換
- back_to_top: 回到頂部
+ back_to_top: 返回頂部
toc: 目錄
scroll_to_comment: 直達評論
setting: 設定
@@ -110,11 +110,11 @@ copy_copyright:
author: 作者
link: 連結
source: 來源
- info: 著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。
+ info: 著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
Snackbar:
- chs_to_cht: 你已切換為繁體
- cht_to_chs: 你已切換為簡體
+ chs_to_cht: 你已切換為繁體中文
+ cht_to_chs: 你已切換為簡體中文
day_to_night: 你已切換為深色模式
night_to_day: 你已切換為淺色模式
diff --git a/layout/archive.pug b/layout/archive.pug
index 815cdd1..913dedc 100644
--- a/layout/archive.pug
+++ b/layout/archive.pug
@@ -3,7 +3,6 @@ extends includes/layout.pug
block content
include ./includes/mixins/article-sort.pug
#archive
- - const archiveLength = findArchiveLength(fragment_cache)
- .article-sort-title= _p('page.articles') + ' - ' + archiveLength
+ .article-sort-title= `${_p('page.articles')} - ${getArchiveLength()}`
+articleSort(page.posts)
include includes/pagination.pug
\ No newline at end of file
diff --git a/layout/includes/head.pug b/layout/includes/head.pug
index 4d30c4a..cd62566 100644
--- a/layout/includes/head.pug
+++ b/layout/includes/head.pug
@@ -55,14 +55,13 @@ if theme.fancybox
!=partial('includes/head/analytics', {}, {cache: true})
//- font
-if theme.blog_title_font && theme.blog_title_font.font_link
+if theme.blog_title_font && theme.blog_title_font.font_link
link(rel='stylesheet' href=url_for(theme.blog_title_font.font_link) media="print" onload="this.media='all'")
//- global config
!=partial('includes/head/config', {}, {cache: true})
include ./head/config_site.pug
-include ./head/noscript.pug
!=fragment_cache('injectHeadJs', function(){return inject_head_js()})
diff --git a/layout/includes/head/config.pug b/layout/includes/head/config.pug
index 80cbf25..05b046c 100644
--- a/layout/includes/head/config.pug
+++ b/layout/includes/head/config.pug
@@ -115,11 +115,9 @@ script.
copyright: !{copyright},
lightbox: '!{ theme.medium_zoom ? "mediumZoom" : (theme.fancybox ? "fancybox" : "null" )}',
Snackbar: !{Snackbar},
- source: {
- justifiedGallery: {
- js: '!{url_for(theme.asset.flickr_justified_gallery_js)}',
- css: '!{url_for(theme.asset.flickr_justified_gallery_css)}'
- }
+ infinitegrid: {
+ js: '!{url_for(theme.asset.egjs_infinitegrid)}',
+ buttonText: '!{_p("load_more")}'
},
isPhotoFigcaption: !{theme.photofigcaption},
islazyload: !{theme.lazyload.enable},
diff --git a/layout/includes/head/noscript.pug b/layout/includes/head/noscript.pug
deleted file mode 100644
index cc3befa..0000000
--- a/layout/includes/head/noscript.pug
+++ /dev/null
@@ -1,14 +0,0 @@
-noscript.
-
\ No newline at end of file
diff --git a/layout/includes/head/preconnect.pug b/layout/includes/head/preconnect.pug
index 04d3b48..f8af380 100644
--- a/layout/includes/head/preconnect.pug
+++ b/layout/includes/head/preconnect.pug
@@ -1,4 +1,20 @@
-link(rel="preconnect" href="//cdn.jsdelivr.net")
+-
+ const { internal_provider, third_party_provider, custom_format } = theme.CDN
+ const providers = {
+ 'jsdelivr': '//cdn.jsdelivr.net',
+ 'cdnjs': '//cdnjs.cloudflare.com',
+ 'unpkg': '//unpkg.com',
+ 'custom': custom_format && custom_format.match(/^((https?:)?(\/\/[^/]+)|([^/]+))(\/|$)/)[1]
+ }
+-
+
+if internal_provider === third_party_provider && internal_provider !== 'local'
+ link(rel="preconnect" href=providers[internal_provider])
+else
+ if internal_provider !== 'local'
+ link(rel="preconnect" href=providers[internal_provider])
+ if third_party_provider !== 'local'
+ link(rel="preconnect" href=providers[third_party_provider])
if theme.google_analytics
link(rel="preconnect" href="//www.google-analytics.com" crossorigin='')
diff --git a/layout/includes/header/post-info.pug b/layout/includes/header/post-info.pug
index 47a3f19..def2312 100644
--- a/layout/includes/header/post-info.pug
+++ b/layout/includes/header/post-info.pug
@@ -102,14 +102,12 @@
case whichCount
when 'Disqus'
+countBlock
- span.disqus-comment-count
- a(href=full_url_for(page.path) + '#disqus_thread')
- i.fa-solid.fa-spinner.fa-spin
+ a.disqus-comment-count(href=full_url_for(page.path) + '#post-comment')
+ 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
+ a.disqusjs-comment-count(href=full_url_for(page.path) + '#post-comment')
+ i.fa-solid.fa-spinner.fa-spin
when 'Valine'
+countBlock
a(href=url_for(page.path) + '#post-comment' itemprop="discussionUrl")
diff --git a/layout/includes/mixins/post-ui.pug b/layout/includes/mixins/post-ui.pug
index 970b5fc..5621b6d 100644
--- a/layout/includes/mixins/post-ui.pug
+++ b/layout/includes/mixins/post-ui.pug
@@ -19,13 +19,11 @@ mixin postUI(posts)
else
div.post-bg(style=`background: ${post_cover}`)
.recent-post-info(class=no_cover)
- a.article-title(href=url_for(link) title=title)= title
- .article-meta-wrap
+ a.article-title(href=url_for(link) title=title)
if (is_home() && (article.top || article.sticky > 0))
- span.article-meta
- i.fas.fa-thumbtack.sticky
- span.sticky= _p('sticky')
- span.article-meta-separator |
+ i.fas.fa-thumbtack.sticky
+ = title
+ .article-meta-wrap
if (theme.post_meta.page.date_type)
span.post-meta-date
if (theme.post_meta.page.date_type === 'both')
@@ -73,14 +71,10 @@ mixin postUI(posts)
if theme.comments.card_post_count && theme.comments.use
case theme.comments.use[0]
when 'Disqus'
- +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
+ a.disqus-count(href=full_url_for(link) + '#post-comment')
+ i.fa-solid.fa-spinner.fa-spin
when 'Valine'
+countBlockInIndex
a(href=url_for(link) + '#post-comment')
diff --git a/layout/includes/post/post-copyright.pug b/layout/includes/post/post-copyright.pug
index 8c7ca03..e0b09df 100644
--- a/layout/includes/post/post-copyright.pug
+++ b/layout/includes/post/post-copyright.pug
@@ -5,13 +5,19 @@ if theme.post_copyright.enable && page.copyright !== false
- let info = page.copyright_info || _p('post.copyright.copyright_content', theme.post_copyright.license_url, theme.post_copyright.license, config.url, config.title)
.post-copyright
.post-copyright__author
- span.post-copyright-meta= _p('post.copyright.author') + ": "
+ span.post-copyright-meta
+ i.fas.fa-circle-user.fa-fw
+ = _p('post.copyright.author') + ": "
span.post-copyright-info
a(href=authorHref)=author
.post-copyright__type
- span.post-copyright-meta= _p('post.copyright.link') + ": "
+ span.post-copyright-meta
+ i.fas.fa-square-arrow-up-right.fa-fw
+ = _p('post.copyright.link') + ": "
span.post-copyright-info
a(href=url_for(url))= theme.post_copyright.decode ? decodeURI(url) : url
.post-copyright__notice
- span.post-copyright-meta= _p('post.copyright.copyright_notice') + ": "
+ span.post-copyright-meta
+ i.fas.fa-circle-exclamation.fa-fw
+ = _p('post.copyright.copyright_notice') + ": "
span.post-copyright-info!= info
diff --git a/layout/includes/post/reward.pug b/layout/includes/post/reward.pug
index 71daa98..fb55946 100644
--- a/layout/includes/post/reward.pug
+++ b/layout/includes/post/reward.pug
@@ -1,7 +1,7 @@
.post-reward
.reward-button
i.fas.fa-qrcode
- = ' ' + _p('donate')
+ = theme.reward.text || _p('donate')
.reward-main
ul.reward-all
each item in theme.reward.QR_code
diff --git a/layout/includes/rightside.pug b/layout/includes/rightside.pug
index 57d8fba..377813d 100644
--- a/layout/includes/rightside.pug
+++ b/layout/includes/rightside.pug
@@ -42,15 +42,15 @@ mixin rightsideItem(array)
#rightside-config-show
if enable
if hide
- button#rightside_config(type="button" title=_p("rightside.setting"))
+ button#rightside-config(type="button" title=_p("rightside.setting"))
i.fas.fa-cog.fa-spin
else
if is_post()
if (readmode || translate.enable || (darkmode.enable && darkmode.button))
- button#rightside_config(type="button" title=_p("rightside.setting"))
+ button#rightside-config(type="button" title=_p("rightside.setting"))
i.fas.fa-cog.fa-spin
else if translate.enable || (darkmode.enable && darkmode.button)
- button#rightside_config(type="button" title=_p("rightside.setting"))
+ button#rightside-config(type="button" title=_p("rightside.setting"))
i.fas.fa-cog.fa-spin
if showArray
diff --git a/layout/includes/third-party/abcjs/abcjs.pug b/layout/includes/third-party/abcjs/abcjs.pug
index dc02aea..79f86c4 100644
--- a/layout/includes/third-party/abcjs/abcjs.pug
+++ b/layout/includes/third-party/abcjs/abcjs.pug
@@ -1,15 +1,15 @@
script.
(() => {
- function abcjsInit() {
- function abcjsFn() {
- for (let abcContainer of document.getElementsByClassName("abc-music-sheet")) {
- ABCJS.renderAbc(abcContainer, abcContainer.innerHTML, {responsive: 'resize'})
- }
+ const abcjsInit = () => {
+ const abcjsFn = () => {
+ document.querySelectorAll(".abc-music-sheet").forEach(ele => {
+ ABCJS.renderAbc(ele, ele.innerHTML, {responsive: 'resize'})
+ })
}
typeof ABCJS === 'object' ? abcjsFn()
: getScript('!{url_for(theme.asset.abcjs_basic_js)}').then(abcjsFn)
}
- window.pjax ? abcjsInit() : document.addEventListener('DOMContentLoaded', abcjsInit)
+ window.pjax ? abcjsInit() : window.addEventListener('load', abcjsInit)
})()
\ No newline at end of file
diff --git a/layout/includes/third-party/card-post-count/disqus.pug b/layout/includes/third-party/card-post-count/disqus.pug
index 1dfe327..92a2d5b 100644
--- a/layout/includes/third-party/card-post-count/disqus.pug
+++ b/layout/includes/third-party/card-post-count/disqus.pug
@@ -1,16 +1,25 @@
+- const { shortname, apikey } = theme.disqus
script.
(() => {
- const getCount = () => {
- if (window.DISQUSWIDGETS === undefined) {
- var d = document, s = d.createElement('script');
- s.src = 'https://!{theme.disqus.shortname}.disqus.com/count.js';
- s.id = 'dsq-count-scr';
- (d.head || d.body).appendChild(s);
- } else {
- DISQUSWIDGETS.getCount({reset: true});
+ const getCount = async () => {
+ try {
+ const eleGroup = document.querySelectorAll('#recent-posts .disqus-count')
+ const cleanedLinks = Array.from(eleGroup).map(i => `thread:link=${i.href.replace(/#post-comment$/, '')}`);
+
+ const res = await fetch(`https://disqus.com/api/3.0/threads/set.json?forum=!{shortname}&api_key=!{apikey}&${cleanedLinks.join('&')}`,{
+ method: 'GET'
+ })
+ const result = await res.json()
+
+ eleGroup.forEach(i => {
+ const cleanedLink = i.href.replace(/#post-comment$/, '')
+ const urlData = result.response.find(data => data.link === cleanedLink) || { posts: 0 }
+ i.textContent = urlData.posts
+ })
+ } catch (err) {
+ console.error(err)
}
}
window.pjax ? getCount() : window.addEventListener('load', getCount)
-
})()
diff --git a/layout/includes/third-party/card-post-count/waline.pug b/layout/includes/third-party/card-post-count/waline.pug
index c80da57..a8faf96 100644
--- a/layout/includes/third-party/card-post-count/waline.pug
+++ b/layout/includes/third-party/card-post-count/waline.pug
@@ -1,3 +1,4 @@
+- const serverURL = theme.waline.serverURL.replace(/\/$/, '')
script.
(() => {
async function loadWaline () {
@@ -5,7 +6,7 @@ script.
const eleGroup = document.querySelectorAll('#recent-posts .waline-comment-count')
const keyArray = Array.from(eleGroup).map(i => i.getAttribute('data-path'))
- const res = await fetch(`!{theme.waline.serverURL}/api/comment?type=count&url=${keyArray}`, { method: 'GET' })
+ const res = await fetch(`!{serverURL}/api/comment?type=count&url=${keyArray}`, { method: 'GET' })
const result = await res.json()
result.data.forEach((count, index) => {
diff --git a/layout/includes/third-party/comments/artalk.pug b/layout/includes/third-party/comments/artalk.pug
index c5844ed..700b91a 100644
--- a/layout/includes/third-party/comments/artalk.pug
+++ b/layout/includes/third-party/comments/artalk.pug
@@ -1,8 +1,9 @@
- const { server, site, option } = theme.artalk
+- const { use, lazyload } = theme.comments
script.
- function loadArtalk () {
- function initArtalk () {
+ (() => {
+ const initArtalk = () => {
window.artalkItem = new Artalk(Object.assign({
el: '#artalk-wrap',
server: '!{server}',
@@ -23,28 +24,29 @@ script.
})
}
- if (typeof window.artalkItem === 'object') initArtalk()
- else {
- getCSS('!{theme.asset.artalk_css}').then(()=>{
- getScript('!{theme.asset.artalk_js}').then(initArtalk)
- })
+ const loadArtalk = async () => {
+ if (typeof window.artalkItem === 'object') initArtalk()
+ else {
+ await getCSS('!{theme.asset.artalk_css}')
+ await getScript('!{theme.asset.artalk_js}')
+ initArtalk()
+ }
}
- }
- function artalkChangeMode (theme) {
- const artalkWrap = document.getElementById('artalk-wrap')
- if (!(artalkWrap && artalkWrap.children.length)) return
- const isDark = theme === 'dark'
- window.artalkItem.setDarkMode(isDark)
- }
-
- btf.addModeChange('artalk', artalkChangeMode)
+ const artalkChangeMode = theme => {
+ const artalkWrap = document.getElementById('artalk-wrap')
+ if (!(artalkWrap && artalkWrap.children.length)) return
+ const isDark = theme === 'dark'
+ window.artalkItem.setDarkMode(isDark)
+ }
- if ('!{theme.comments.use[0]}' === 'Artalk' || !!{theme.comments.lazyload}) {
- if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('artalk-wrap'), loadArtalk)
- else loadArtalk()
- } else {
- function loadOtherComment () {
- loadArtalk()
+
+ btf.addGlobalFn('themeChange', artalkChangeMode, 'artalk')
+
+ if ('!{use[0]}' === 'Artalk' || !!{lazyload}) {
+ if (!{lazyload}) btf.loadComment(document.getElementById('artalk-wrap'), loadArtalk)
+ else loadArtalk()
+ } else {
+ window.loadOtherComment = loadArtalk
}
- }
\ No newline at end of file
+ })()
\ No newline at end of file
diff --git a/layout/includes/third-party/comments/disqus.pug b/layout/includes/third-party/comments/disqus.pug
index 3a9ba41..8d8864c 100644
--- a/layout/includes/third-party/comments/disqus.pug
+++ b/layout/includes/third-party/comments/disqus.pug
@@ -1,7 +1,9 @@
-- let disqusPageTitle = page.title.replace(/'/ig,"\\'")
+- const disqusPageTitle = page.title.replace(/'/ig,"\\'")
+- const { shortname, apikey } = theme.disqus
+- const { use, lazyload, count } = theme.comments
script.
- function loadDisqus () {
+ (() => {
const disqus_config = function () {
this.page.url = '!{ page.permalink }'
this.page.identifier = '!{ url_for(page.path) }'
@@ -9,41 +11,49 @@ script.
}
const disqusReset = () => {
- DISQUS.reset({
+ window.DISQUS && window.DISQUS.reset({
reload: true,
config: disqus_config
})
}
- btf.addModeChange('disqus', disqusReset)
+ btf.addGlobalFn('themeChange', disqusReset, 'disqus')
- if (window.DISQUS) disqusReset()
- else {
- (function() {
- var d = document, s = d.createElement('script');
- s.src = 'https://!{theme.disqus.shortname}.disqus.com/embed.js';
- s.setAttribute('data-timestamp', +new Date());
- (d.head || d.body).appendChild(s);
- })();
+ const loadDisqus = () =>{
+ if (window.DISQUS) disqusReset()
+ else {
+ const script = document.createElement('script')
+ script.src = 'https://!{shortname}.disqus.com/embed.js'
+ script.setAttribute('data-timestamp', +new Date())
+ document.head.appendChild(script)
+ }
}
- }
- if ('!{theme.comments.use[0]}' === 'Disqus' || !!{theme.comments.lazyload}) {
- if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('disqus_thread'), loadDisqus)
- else loadDisqus()
- } else {
- function loadOtherComment () {
- loadDisqus()
+ const getCount = async() => {
+ try {
+ const eleGroup = document.querySelector('#post-meta .disqus-comment-count')
+ if (!eleGroup) return
+ const cleanedLinks = eleGroup.href.replace(/#post-comment$/, '')
+
+ const res = await fetch(`https://disqus.com/api/3.0/threads/set.json?forum=!{shortname}&api_key=!{apikey}&thread:link=${cleanedLinks}`,{
+ method: 'GET'
+ })
+ const result = await res.json()
+
+ const count = result.response.length ? result.response[0].posts : 0
+ eleGroup.textContent = count
+ } catch (err) {
+ console.error(err)
+ }
}
- }
-if is_post() && !theme.comments.lazyload && theme.comments.count && theme.comments.use[0] === 'Disqus'
- script.
- if (window.DISQUSWIDGETS === undefined) {
- var d = document, s = d.createElement('script');
- s.src = 'https://!{theme.disqus.shortname}.disqus.com/count.js';
- s.id = 'dsq-count-scr';
- (d.head || d.body).appendChild(s);
+ if ('!{use[0]}' === 'Disqus' || !!{lazyload}) {
+ if (!{lazyload}) btf.loadComment(document.getElementById('disqus_thread'), loadDisqus)
+ else {
+ loadDisqus()
+ !{ count ? 'GLOBAL_CONFIG_SITE.isPost && getCount()' : '' }
+ }
} else {
- DISQUSWIDGETS.getCount({reset: true});
+ window.loadOtherComment = loadDisqus
}
+ })()
diff --git a/layout/includes/third-party/comments/disqusjs.pug b/layout/includes/third-party/comments/disqusjs.pug
index f0994e5..af41696 100644
--- a/layout/includes/third-party/comments/disqusjs.pug
+++ b/layout/includes/third-party/comments/disqusjs.pug
@@ -1,18 +1,19 @@
- let disqusjsPageTitle = page.title.replace(/'/ig,"\\'")
+- const { shortname:dqShortname, apikey:dqApikey, option:dqOption } = theme.disqusjs
script.
- function loadDisqusjs () {
- function initDisqusjs () {
+ (() => {
+ const initDisqusjs = () => {
window.disqusjs = null
disqusjs = new DisqusJS(Object.assign({
- shortname: '!{theme.disqusjs.shortname}',
+ shortname: '!{dqShortname}',
identifier: '!{ url_for(page.path) }',
url: '!{ page.permalink }',
title: '!{ disqusjsPageTitle }',
- apikey: '!{theme.disqusjs.apikey}',
- },!{JSON.stringify(theme.disqusjs.option)}))
+ apikey: '!{dqApikey}',
+ },!{JSON.stringify(dqOption)}))
- disqusjs.render(document.getElementById('disqusjs'))
+ disqusjs.render(document.getElementById('disqusjs-wrap'))
}
const themeChange = () => {
@@ -22,36 +23,42 @@ script.
initDisqusjs()
}
+ btf.addGlobalFn('themeChange', themeChange, 'disqusjs')
- btf.addModeChange('disqusjs', themeChange)
-
- if (window.disqusJsLoad) initDisqusjs()
- else {
- getCSS('!{url_for(theme.asset.disqusjs_css)}')
- getScript('!{url_for(theme.asset.disqusjs)}').then(initDisqusjs)
- window.disqusJsLoad = true
+ const loadDisqusjs = async() => {
+ if (window.disqusJsLoad) initDisqusjs()
+ else {
+ await getCSS('!{url_for(theme.asset.disqusjs_css)}')
+ await getScript('!{url_for(theme.asset.disqusjs)}')
+ initDisqusjs()
+ window.disqusJsLoad = true
+ }
}
- }
- if ('!{theme.comments.use[0]}' === 'Disqusjs' || !!{theme.comments.lazyload}) {
- if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('disqusjs'), loadDisqusjs)
- else loadDisqusjs()
- }
- else {
- function loadOtherComment () {
- loadDisqusjs()
+ const getCount = async() => {
+ try {
+ const eleGroup = document.querySelector('#post-meta .disqusjs-comment-count')
+ if (!eleGroup) return
+ const cleanedLinks = eleGroup.href.replace(/#post-comment$/, '')
+
+ const res = await fetch(`https://disqus.com/api/3.0/threads/set.json?forum=!{dqShortname}&api_key=!{dqApikey}&thread:link=${cleanedLinks}`,{
+ method: 'GET'
+ })
+ const result = await res.json()
+ const count = result.response.length ? result.response[0].posts : 0
+ eleGroup.textContent = count
+ } catch (err) {
+ console.error(err)
+ }
}
- }
-
-if is_post() && !theme.comments.lazyload && theme.comments.count && theme.comments.use[0] === 'Disqusjs'
- script.
- if (window.DISQUSWIDGETS === undefined) {
- var d = document, s = d.createElement('script');
- s.src = 'https://!{theme.disqus.shortname}.disqus.com/count.js';
- s.id = 'dsq-count-scr';
- (d.head || d.body).appendChild(s);
+ if ('!{theme.comments.use[0]}' === 'Disqusjs' || !!{theme.comments.lazyload}) {
+ if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('disqusjs-wrap'), loadDisqusjs)
+ else {
+ loadDisqusjs()
+ !{ theme.comments.count ? 'GLOBAL_CONFIG_SITE.isPost && getCount()' : '' }
+ }
} else {
- DISQUSWIDGETS.getCount({reset: true});
+ window.loadOtherComment = loadDisqusjs
}
-
+ })()
\ No newline at end of file
diff --git a/layout/includes/third-party/comments/facebook_comments.pug b/layout/includes/third-party/comments/facebook_comments.pug
index e0d673a..f0cc916 100644
--- a/layout/includes/third-party/comments/facebook_comments.pug
+++ b/layout/includes/third-party/comments/facebook_comments.pug
@@ -2,45 +2,45 @@
- const fbSDK = theme.messenger.enable ? `https://connect.facebook.net/${theme.facebook_comments.lang}/sdk/xfbml.customerchat.js#xfbml=1&version=${fbSDKVer}` : `https://connect.facebook.net/${theme.facebook_comments.lang}/sdk.js#xfbml=1&version=${fbSDKVer}`
script.
- function loadFBComment () {
- document.getElementById('fb-root') ? '' : document.body.insertAdjacentHTML('afterend', '
')
+ (()=>{
+ const loadFBComment = () => {
+ 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]
- $fbComment.setAttribute('data-colorscheme',themeNow)
- $fbComment.setAttribute('data-href', '!{urlNoIndex(page.permalink)}')
+ const themeNow = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light'
+ const $fbComment = document.getElementsByClassName('fb-comments')[0]
+ $fbComment.setAttribute('data-colorscheme',themeNow)
+ $fbComment.setAttribute('data-href', '!{urlNoIndex(page.permalink)}')
- if (typeof FB === 'object') {
- FB.XFBML.parse(document.getElementsByClassName('post-meta-commentcount')[0])
- FB.XFBML.parse(document.getElementById('post-comment'))
+ if (typeof FB === 'object') {
+ FB.XFBML.parse(document.getElementsByClassName('post-meta-commentcount')[0])
+ FB.XFBML.parse(document.getElementById('post-comment'))
+ }
+ else {
+ let ele = document.createElement('script')
+ ele.setAttribute('src','!{fbSDK}')
+ ele.setAttribute('async', 'true')
+ ele.setAttribute('defer', 'true')
+ ele.setAttribute('crossorigin', 'anonymous')
+ ele.setAttribute('id', 'facebook-jssdk')
+ document.getElementById('fb-root').insertAdjacentElement('afterbegin',ele)
+ }
}
- else {
- let ele = document.createElement('script')
- ele.setAttribute('src','!{fbSDK}')
- ele.setAttribute('async', 'true')
- ele.setAttribute('defer', 'true')
- ele.setAttribute('crossorigin', 'anonymous')
- ele.setAttribute('id', 'facebook-jssdk')
- document.getElementById('fb-root').insertAdjacentElement('afterbegin',ele)
+
+ const fbModeChange = theme => {
+ const $fbComment = document.getElementsByClassName('fb-comments')[0]
+ if ($fbComment && typeof FB === 'object') {
+ $fbComment.setAttribute('data-colorscheme',theme)
+ FB.XFBML.parse(document.getElementById('post-comment'))
+ }
}
- }
- function fbModeChange (theme) {
- const $fbComment = document.getElementsByClassName('fb-comments')[0]
- if ($fbComment && typeof FB === 'object') {
- $fbComment.setAttribute('data-colorscheme',theme)
- FB.XFBML.parse(document.getElementById('post-comment'))
+ btf.addGlobalFn('themeChange', fbModeChange, 'facebook_comments')
+
+ 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()
+ } else {
+ window.loadOtherComment = loadFBComment
}
- }
-
- btf.addModeChange('facebook_comments', fbModeChange)
-
- 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()
- } else {
- function loadOtherComment () {
- loadFBComment()
- }
- }
+ })()
diff --git a/layout/includes/third-party/comments/giscus.pug b/layout/includes/third-party/comments/giscus.pug
index 0a9af83..3e1b518 100644
--- a/layout/includes/third-party/comments/giscus.pug
+++ b/layout/includes/third-party/comments/giscus.pug
@@ -1,53 +1,54 @@
- const { repo, repo_id, category_id, theme:themes, option } = theme.giscus
- const giscusUrl = theme.asset.giscus || 'https://giscus.app/client.js'
- const giscusOriginUrl = new URL(giscusUrl).origin
+- const { use, lazyload } = theme.comments
script.
- function getGiscusTheme (theme) {
- return theme === 'dark' ? '!{themes.dark}' : '!{themes.light}'
- }
-
- function loadGiscus () {
- const config = Object.assign({
- 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)})
-
- let ele = document.createElement('script')
- for (let key in config) {
- ele.setAttribute(key, config[key])
- }
- document.getElementById('giscus-wrap').insertAdjacentElement('afterbegin',ele)
- }
-
- function changeGiscusTheme (theme) {
- function sendMessage(message) {
- const iframe = document.querySelector('iframe.giscus-frame')
- if (!iframe) return
- iframe.contentWindow.postMessage({ giscus: message }, '!{giscusOriginUrl}')
+ (()=>{
+ const getGiscusTheme = theme => {
+ return theme === 'dark' ? '!{themes.dark}' : '!{themes.light}'
}
- sendMessage({
- setConfig: {
- theme: getGiscusTheme(theme)
+ const loadGiscus = () => {
+ const config = Object.assign({
+ 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)})
+
+ const ele = document.createElement('script')
+ for (let key in config) {
+ ele.setAttribute(key, config[key])
}
- });
- }
-
- btf.addModeChange('giscus', changeGiscusTheme)
-
- if ('!{theme.comments.use[0]}' === 'Giscus' || !!{theme.comments.lazyload}) {
- if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('giscus-wrap'), loadGiscus)
- else loadGiscus()
- } else {
- function loadOtherComment () {
- loadGiscus()
+ document.getElementById('giscus-wrap').appendChild(ele)
}
- }
+
+ const changeGiscusTheme = theme => {
+ const sendMessage = message => {
+ const iframe = document.querySelector('iframe.giscus-frame')
+ if (!iframe) return
+ iframe.contentWindow.postMessage({ giscus: message }, '!{giscusOriginUrl}')
+ }
+
+ sendMessage({
+ setConfig: {
+ theme: getGiscusTheme(theme)
+ }
+ });
+ }
+
+ btf.addGlobalFn('themeChange', changeGiscusTheme, 'giscus')
+
+ if ('!{use[0]}' === 'Giscus' || !!{lazyload}) {
+ if (!{lazyload}) btf.loadComment(document.getElementById('giscus-wrap'), loadGiscus)
+ else loadGiscus()
+ } else {
+ window.loadOtherComment= loadGiscus
+ }
+ })()
diff --git a/layout/includes/third-party/comments/gitalk.pug b/layout/includes/third-party/comments/gitalk.pug
index 922d657..e0a1389 100644
--- a/layout/includes/third-party/comments/gitalk.pug
+++ b/layout/includes/third-party/comments/gitalk.pug
@@ -1,41 +1,44 @@
+- const { client_id, client_secret, repo, owner, admin, option } = theme.gitalk
+
script.
- function loadGitalk () {
- function initGitalk () {
- var gitalk = new Gitalk(Object.assign({
- clientID: '!{theme.gitalk.client_id}',
- clientSecret: '!{theme.gitalk.client_secret}',
- repo: '!{theme.gitalk.repo}',
- owner: '!{theme.gitalk.owner}',
- admin: ['!{theme.gitalk.admin}'],
+ (() => {
+ const initGitalk = () => {
+ const gitalk = new Gitalk(Object.assign({
+ clientID: '!{client_id}',
+ clientSecret: '!{client_secret}',
+ repo: '!{repo}',
+ owner: '!{owner}',
+ admin: ['!{admin}'],
id: '!{md5(page.path)}',
updateCountCallback: commentCount
- },!{JSON.stringify(theme.gitalk.option)}))
+ },!{JSON.stringify(option)}))
gitalk.render('gitalk-container')
}
- if (typeof Gitalk === 'function') initGitalk()
- else {
- getCSS('!{url_for(theme.asset.gitalk_css)}')
- getScript('!{url_for(theme.asset.gitalk)}').then(initGitalk)
+ const loadGitalk = async() => {
+ if (typeof Gitalk === 'function') initGitalk()
+ else {
+ await getCSS('!{url_for(theme.asset.gitalk_css)}')
+ await getScript('!{url_for(theme.asset.gitalk)}')
+ initGitalk()
+ }
}
- }
-
- function commentCount(n){
- let isCommentCount = document.querySelector('#post-meta .gitalk-comment-count')
- if (isCommentCount) {
- isCommentCount.textContent= n
+
+ const commentCount = n => {
+ const isCommentCount = document.querySelector('#post-meta .gitalk-comment-count')
+ if (isCommentCount) {
+ isCommentCount.textContent= n
+ }
}
- }
- if ('!{theme.comments.use[0]}' === 'Gitalk' || !!{theme.comments.lazyload}) {
- if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('gitalk-container'), loadGitalk)
- else loadGitalk()
- } else {
- function loadOtherComment () {
- loadGitalk()
+ if ('!{theme.comments.use[0]}' === 'Gitalk' || !!{theme.comments.lazyload}) {
+ if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('gitalk-container'), loadGitalk)
+ else loadGitalk()
+ } else {
+ window.loadOtherComment = loadGitalk
}
- }
+ })()
diff --git a/layout/includes/third-party/comments/index.pug b/layout/includes/third-party/comments/index.pug
index c797395..3b8ceff 100644
--- a/layout/includes/third-party/comments/index.pug
+++ b/layout/includes/third-party/comments/index.pug
@@ -7,9 +7,9 @@ hr.custom-hr
span= ' ' + _p('comment')
if theme.comments.use.length > 1
- #comment-switch
+ .comment-switch
span.first-comment=defaultComment
- span.switch-btn
+ span#switch-btn
span.second-comment=theme.comments.use[1]
@@ -22,7 +22,7 @@ hr.custom-hr
when 'Valine'
#vcomment.vcomment
when 'Disqusjs'
- #disqusjs
+ #disqusjs-wrap
when 'Livere'
#lv-container(data-id="city" data-uid=theme.livere.uid)
when 'Gitalk'
diff --git a/layout/includes/third-party/comments/livere.pug b/layout/includes/third-party/comments/livere.pug
index 85b6d78..ecacb59 100644
--- a/layout/includes/third-party/comments/livere.pug
+++ b/layout/includes/third-party/comments/livere.pug
@@ -1,26 +1,25 @@
-script.
- function loadLivere () {
- if (typeof LivereTower === 'object') {
- window.LivereTower.init()
- }
- else {
- (function(d, s) {
- var j, e = d.getElementsByTagName(s)[0];
- if (typeof LivereTower === 'function') { return; }
- j = d.createElement(s);
- j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
- j.async = true;
- e.parentNode.insertBefore(j, e);
- })(document, 'script');
- }
- }
+- const { use, lazyload } = theme.comments
- if ('!{theme.comments.use[0]}' === 'Livere' || !!{theme.comments.lazyload}) {
- if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('lv-container'), loadLivere)
- else loadLivere()
- }
- else {
- function loadOtherComment () {
- loadLivere()
+script.
+ (()=>{
+ const loadLivere = () => {
+ if (typeof LivereTower === 'object') window.LivereTower.init()
+ else {
+ (function(d, s) {
+ var j, e = d.getElementsByTagName(s)[0];
+ if (typeof LivereTower === 'function') { return; }
+ j = d.createElement(s);
+ j.src = 'https://cdn-city.livere.com/js/embed.dist.js';
+ j.async = true;
+ e.parentNode.insertBefore(j, e);
+ })(document, 'script');
+ }
}
- }
\ No newline at end of file
+
+ if ('!{use[0]}' === 'Livere' || !!{lazyload}) {
+ if (!{lazyload}) btf.loadComment(document.getElementById('lv-container'), loadLivere)
+ else loadLivere()
+ } else {
+ window.loadOtherComment = loadLivere
+ }
+ })()
\ No newline at end of file
diff --git a/layout/includes/third-party/comments/remark42.pug b/layout/includes/third-party/comments/remark42.pug
index 222f9d3..388fb97 100644
--- a/layout/includes/third-party/comments/remark42.pug
+++ b/layout/includes/third-party/comments/remark42.pug
@@ -56,7 +56,7 @@ script.
window.REMARK42.changeTheme(theme)
}
- btf.addModeChange('remark42', remarkChangeMode)
+ btf.addGlobalFn('themeChange', remarkChangeMode, 'remark42')
if ('!{theme.comments.use[0]}' === 'Remark42' || !!{theme.comments.lazyload}) {
if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('remark42'), loadRemark42)
diff --git a/layout/includes/third-party/comments/twikoo.pug b/layout/includes/third-party/comments/twikoo.pug
index bdaeb1e..858fad3 100644
--- a/layout/includes/third-party/comments/twikoo.pug
+++ b/layout/includes/third-party/comments/twikoo.pug
@@ -2,18 +2,23 @@
- const { use, lazyload, count } = theme.comments
script.
- (()=>{
+ (() => {
const init = () => {
twikoo.init(Object.assign({
el: '#twikoo-wrap',
envId: '!{envId}',
region: '!{region}',
- onCommentLoaded: function () {
+ onCommentLoaded: () => {
btf.loadLightbox(document.querySelectorAll('#twikoo .tk-content img:not(.tk-owo-emotion)'))
}
}, !{JSON.stringify(option)}))
}
+ const loadTwikoo = () => {
+ if (typeof twikoo === 'object') setTimeout(init,0)
+ else getScript('!{url_for(theme.asset.twikoo)}').then(init)
+ }
+
const getCount = () => {
const countELement = document.getElementById('twikoo-count')
if(!countELement) return
@@ -22,32 +27,20 @@ script.
region: '!{region}',
urls: [window.location.pathname],
includeReply: false
- }).then(function (res) {
+ }).then(res => {
countELement.textContent = res[0].count
- }).catch(function (err) {
- console.error(err);
- });
- }
-
- const runFn = () => {
- init()
- !{count ? 'GLOBAL_CONFIG_SITE.isPost && getCount()' : ''}
- }
-
- const loadTwikoo = () => {
- if (typeof twikoo === 'object') {
- setTimeout(runFn,0)
- return
- }
- getScript('!{url_for(theme.asset.twikoo)}').then(runFn)
+ }).catch(err => {
+ console.error(err)
+ })
}
if ('!{use[0]}' === 'Twikoo' || !!{lazyload}) {
if (!{lazyload}) btf.loadComment(document.getElementById('twikoo-wrap'), loadTwikoo)
- else loadTwikoo()
- } else {
- window.loadOtherComment = () => {
+ else {
loadTwikoo()
+ !{count ? 'GLOBAL_CONFIG_SITE.isPost && getCount()' : ''}
}
+ } else {
+ window.loadOtherComment = loadTwikoo
}
})()
\ No newline at end of file
diff --git a/layout/includes/third-party/comments/utterances.pug b/layout/includes/third-party/comments/utterances.pug
index c225325..0ca01e7 100644
--- a/layout/includes/third-party/comments/utterances.pug
+++ b/layout/includes/third-party/comments/utterances.pug
@@ -1,36 +1,39 @@
+- const { use, lazyload } = theme.comments
+- const { repo, issue_term, light_theme, dark_theme } = theme.utterances
+
script.
- function loadUtterances () {
- let ele = document.createElement('script')
- ele.setAttribute('id', 'utterances_comment')
- ele.setAttribute('src', 'https://utteranc.es/client.js')
- ele.setAttribute('repo', '!{theme.utterances.repo}')
- ele.setAttribute('issue-term', '!{theme.utterances.issue_term}')
- let nowTheme = document.documentElement.getAttribute('data-theme') === 'dark' ? '#{theme.utterances.dark_theme}' : '#{theme.utterances.light_theme}'
- ele.setAttribute('theme', nowTheme)
- ele.setAttribute('crossorigin', 'anonymous')
- ele.setAttribute('async', 'true')
- document.getElementById('utterances-wrap').insertAdjacentElement('afterbegin',ele)
- }
-
- function utterancesTheme (theme) {
- const iframe = document.querySelector('.utterances-frame')
- if (iframe) {
- const theme = theme === 'dark' ? '#{theme.utterances.dark_theme}' : '#{theme.utterances.light_theme}'
- const message = {
- type: 'set-theme',
- theme: theme
- };
- iframe.contentWindow.postMessage(message, 'https://utteranc.es');
+ (() => {
+ const loadUtterances = () => {
+ let ele = document.createElement('script')
+ ele.id = 'utterances_comment'
+ ele.src = 'https://utteranc.es/client.js'
+ ele.setAttribute('repo', '!{repo}')
+ ele.setAttribute('issue-term', '!{issue_term}')
+ const nowTheme = document.documentElement.getAttribute('data-theme') === 'dark' ? '#{dark_theme}' : '#{light_theme}'
+ ele.setAttribute('theme', nowTheme)
+ ele.crossOrigin = 'anonymous'
+ ele.async = true
+ document.getElementById('utterances-wrap').appendChild(ele)
}
- }
- btf.addModeChange('utterances', utterancesTheme)
-
- if ('!{theme.comments.use[0]}' === 'Utterances' || !!{theme.comments.lazyload}) {
- if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('utterances-wrap'), loadUtterances)
- else loadUtterances()
- } else {
- function loadOtherComment () {
- loadUtterances()
+ const utterancesTheme = theme => {
+ const iframe = document.querySelector('.utterances-frame')
+ if (iframe) {
+ const theme = theme === 'dark' ? '#{dark_theme}' : '#{light_theme}'
+ const message = {
+ type: 'set-theme',
+ theme: theme
+ };
+ iframe.contentWindow.postMessage(message, 'https://utteranc.es');
+ }
}
- }
+
+ btf.addGlobalFn('themeChange', utterancesTheme, 'utterances')
+
+ if ('!{use[0]}' === 'Utterances' || !!{lazyload}) {
+ if (!{lazyload}) btf.loadComment(document.getElementById('utterances-wrap'), loadUtterances)
+ else loadUtterances()
+ } else {
+ window.loadOtherComment = loadUtterances
+ }
+ })()
\ No newline at end of file
diff --git a/layout/includes/third-party/comments/valine.pug b/layout/includes/third-party/comments/valine.pug
index 025849e..85781d5 100644
--- a/layout/includes/third-party/comments/valine.pug
+++ b/layout/includes/third-party/comments/valine.pug
@@ -1,33 +1,38 @@
+- const { use, lazyload } = theme.comments
+- const { appId, appKey, avatar, serverURLs, visitor, option } = theme.valine
+
- let emojiMaps = '""'
if site.data.valine
- emojiMaps = JSON.stringify(site.data.valine)
script.
- function loadValine () {
- function initValine () {
+ (() => {
+ const initValine = () => {
const valine = new Valine(Object.assign({
el: '#vcomment',
- appId: '#{theme.valine.appId}',
- appKey: '#{theme.valine.appKey}',
- avatar: '#{theme.valine.avatar}',
- serverURLs: '#{theme.valine.serverURLs}',
+ appId: '#{appId}',
+ appKey: '#{appKey}',
+ avatar: '#{avatar}',
+ serverURLs: '#{serverURLs}',
emojiMaps: !{emojiMaps},
path: window.location.pathname,
- visitor: #{theme.valine.visitor}
- }, !{JSON.stringify(theme.valine.option)}))
+ visitor: #{visitor}
+ }, !{JSON.stringify(option)}))
}
- if (typeof Valine === 'function') initValine()
- else getScript('!{url_for(theme.asset.valine)}').then(initValine)
- }
-
- if ('!{theme.comments.use[0]}' === 'Valine' || !!{theme.comments.lazyload}) {
- if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('vcomment'),loadValine)
- else setTimeout(loadValine, 0)
- } else {
- function loadOtherComment () {
- loadValine()
+ const loadValine = async () => {
+ if (typeof Valine === 'function') initValine()
+ else {
+ await getScript('!{url_for(theme.asset.valine)}')
+ initValine()
+ }
}
- }
-
+
+ if ('!{use[0]}' === 'Valine' || !!{lazyload}) {
+ if (!{lazyload}) btf.loadComment(document.getElementById('vcomment'),loadValine)
+ else setTimeout(loadValine, 0)
+ } else {
+ window.loadOtherComment = loadValine
+ }
+ })()
diff --git a/layout/includes/third-party/comments/waline.pug b/layout/includes/third-party/comments/waline.pug
index 55c7b45..a3bacca 100644
--- a/layout/includes/third-party/comments/waline.pug
+++ b/layout/includes/third-party/comments/waline.pug
@@ -2,8 +2,8 @@
- const { lazyload, count, use } = theme.comments
script.
- function loadWaline () {
- function initWaline () {
+ (() => {
+ const initWaline = () => {
const waline = Waline.init(Object.assign({
el: '#waline-wrap',
serverURL: '!{serverURL}',
@@ -14,21 +14,20 @@ script.
}, !{JSON.stringify(option)}))
}
- if (typeof Waline === 'object') initWaline()
- else {
- getCSS('!{url_for(theme.asset.waline_css)}').then(() => {
- getScript('!{url_for(theme.asset.waline_js)}').then(initWaline)
- })
+ const loadWaline = async () => {
+ if (typeof Waline === 'object') initWaline()
+ else {
+ await getCSS('!{url_for(theme.asset.waline_css)}')
+ await getScript('!{url_for(theme.asset.waline_js)}')
+ initWaline()
+ }
}
- }
- if ('!{use[0]}' === 'Waline' || !!{lazyload}) {
- if (!{lazyload}) btf.loadComment(document.getElementById('waline-wrap'),loadWaline)
- else setTimeout(loadWaline, 0)
- } else {
- function loadOtherComment () {
- loadWaline()
+ if ('!{use[0]}' === 'Waline' || !!{lazyload}) {
+ if (!{lazyload}) btf.loadComment(document.getElementById('waline-wrap'),loadWaline)
+ else setTimeout(loadWaline, 0)
+ } else {
+ window.loadOtherComment = loadWaline
}
- }
-
+ })()
diff --git a/layout/includes/third-party/effect.pug b/layout/includes/third-party/effect.pug
index 275297c..a3b2995 100644
--- a/layout/includes/third-party/effect.pug
+++ b/layout/includes/third-party/effect.pug
@@ -24,12 +24,12 @@ if theme.activate_power_mode.enable
if theme.click_heart && theme.click_heart.enable
script#click-heart(src=url_for(theme.asset.click_heart) async mobile=`${theme.click_heart.mobile}`)
-if theme.ClickShowText && theme.ClickShowText.enable
+if theme.clickShowText && theme.clickShowText.enable
script#click-show-text(
- src= url_for(theme.asset.ClickShowText)
- data-mobile= `${theme.ClickShowText.mobile}`
- data-text= theme.ClickShowText.text.join(",")
- data-fontsize= theme.ClickShowText.fontSize
- data-random= `${theme.ClickShowText.random}`
+ src= url_for(theme.asset.clickShowText)
+ data-mobile= `${theme.clickShowText.mobile}`
+ data-text= theme.clickShowText.text.join(",")
+ data-fontsize= theme.clickShowText.fontSize
+ data-random= `${theme.clickShowText.random}`
async
)
\ No newline at end of file
diff --git a/layout/includes/third-party/math/mermaid.pug b/layout/includes/third-party/math/mermaid.pug
index 119a5dd..2cfc1c7 100644
--- a/layout/includes/third-party/math/mermaid.pug
+++ b/layout/includes/third-party/math/mermaid.pug
@@ -32,7 +32,7 @@ script.
window.loadMermaid ? runMermaid() : getScript('!{url_for(theme.asset.mermaid)}').then(runMermaid)
}
- btf.addModeChange('mermaid', runMermaid)
+ btf.addGlobalFn('themeChange', runMermaid, 'mermaid')
window.pjax ? loadMermaid() : document.addEventListener('DOMContentLoaded', loadMermaid)
})()
\ No newline at end of file
diff --git a/layout/includes/third-party/newest-comments/waline.pug b/layout/includes/third-party/newest-comments/waline.pug
index 00a72d5..be25326 100644
--- a/layout/includes/third-party/newest-comments/waline.pug
+++ b/layout/includes/third-party/newest-comments/waline.pug
@@ -1,3 +1,5 @@
+- const serverURL = theme.waline.serverURL.replace(/\/$/, '')
+
script.
window.addEventListener('load', () => {
const changeContent = content => {
@@ -43,7 +45,7 @@ script.
const getComment = async () => {
try {
- const res = await fetch('!{theme.waline.serverURL}/api/comment?type=recent&count=!{theme.newest_comments.limit}', { method: 'GET' })
+ const res = await fetch('!{serverURL}/api/comment?type=recent&count=!{theme.newest_comments.limit}', { method: 'GET' })
const result = await res.json()
const walineArray = result.data.map(e => {
return {
diff --git a/layout/includes/third-party/pjax.pug b/layout/includes/third-party/pjax.pug
index a5d926a..2b783c6 100644
--- a/layout/includes/third-party/pjax.pug
+++ b/layout/includes/third-party/pjax.pug
@@ -26,11 +26,11 @@ script.
document.addEventListener('pjax:send', function () {
- // removeEventListener scroll
- window.tocScrollFn && window.removeEventListener('scroll', window.tocScrollFn)
- window.scrollCollect && window.removeEventListener('scroll', scrollCollect)
+ // removeEventListener
+ btf.removeGlobalFnEvent('pjax')
+ btf.removeGlobalFnEvent('themeChange')
- document.getElementById('rightside').style.cssText = "opacity: ''; transform: ''"
+ document.getElementById('rightside').classList.remove('rightside-show')
if (window.aplayers) {
for (let i = 0; i < window.aplayers.length; i++) {
@@ -76,8 +76,8 @@ script.
typeof Prism === 'object' && Prism.highlightAll()
})
- document.addEventListener('pjax:error', (e) => {
+ document.addEventListener('pjax:error', e => {
if (e.request.status === 404) {
- pjax.loadUrl('/404.html')
+ pjax.loadUrl('!{url_for("/404.html")}')
}
})
diff --git a/layout/includes/third-party/search/docsearch.pug b/layout/includes/third-party/search/docsearch.pug
index 00d8a01..9adad8b 100644
--- a/layout/includes/third-party/search/docsearch.pug
+++ b/layout/includes/third-party/search/docsearch.pug
@@ -13,11 +13,12 @@
container: '#docsearch',
}, !{JSON.stringify(option)}))
+ const handleClick = () => {
+ document.querySelector('.DocSearch-Button').click()
+ }
const searchClickFn = () => {
- document.querySelector('#search-button > .search').addEventListener('click', () => {
- document.querySelector('.DocSearch-Button').click()
- })
+ btf.addEventListenerPjax(document.querySelector('#search-button > .search'), 'click', handleClick)
}
searchClickFn()
diff --git a/layout/includes/third-party/search/local-search.pug b/layout/includes/third-party/search/local-search.pug
index 0c7537b..f22f05b 100644
--- a/layout/includes/third-party/search/local-search.pug
+++ b/layout/includes/third-party/search/local-search.pug
@@ -15,7 +15,7 @@
.local-search-box
input(placeholder=_p("search.local_search.input_placeholder") type="text").local-search-box--input
hr
- #local-search-results.no-result
+ #local-search-results
#local-search-stats-wrap
#search-mask
diff --git a/layout/includes/third-party/share/add-this.pug b/layout/includes/third-party/share/add-this.pug
deleted file mode 100644
index ab44267..0000000
--- a/layout/includes/third-party/share/add-this.pug
+++ /dev/null
@@ -1,2 +0,0 @@
-.addthis_inline_share_toolbox
-script(src=`//s7.addthis.com/js/300/addthis_widget.js#pubid=${theme.addThis.pubid}` async)
\ No newline at end of file
diff --git a/layout/includes/third-party/share/index.pug b/layout/includes/third-party/share/index.pug
index f28ceab..f8122c0 100644
--- a/layout/includes/third-party/share/index.pug
+++ b/layout/includes/third-party/share/index.pug
@@ -1,7 +1,5 @@
.post_share
- if theme.addThis.enable
- !=partial('includes/third-party/share/add-this', {}, {cache: true})
- else if theme.sharejs.enable
+ if theme.sharejs.enable
include ./share-js.pug
else if theme.addtoany.enable
!=partial('includes/third-party/share/addtoany', {}, {cache: true})
diff --git a/layout/includes/widget/card_post_series.pug b/layout/includes/widget/card_post_series.pug
new file mode 100644
index 0000000..c216929
--- /dev/null
+++ b/layout/includes/widget/card_post_series.pug
@@ -0,0 +1,21 @@
+if theme.aside.card_post_series.enable
+ - const array = fragment_cache('seriesArr', groupPosts)
+ .card-widget.card-post-series
+ .item-headline
+ i.fa-solid.fa-layer-group
+ span= _p('aside.card_post_series')
+ .aside-list
+ each item in array[page.series]
+ - const { path, title = _p('no_title'), cover, cover_type, date:dateA } = item
+ - let link = url_for(path)
+ - let no_cover = cover === false || !theme.cover.aside_enable ? 'no-cover' : ''
+ .aside-list-item(class=no_cover)
+ if cover && theme.cover.aside_enable
+ a.thumbnail(href=link title=title)
+ if cover_type === 'img'
+ img(src=url_for(cover) onerror=`this.onerror=null;this.src='${url_for(theme.error_img.post_page)}'` alt=title)
+ else
+ div(style=`background: ${cover}`)
+ .content
+ a.title(href=link title=title)= title
+ time(datetime=date_xml(dateA) title=_p('post.created') + ' ' + full_date(dateA)) #[=date(dateA, config.date_format)]
diff --git a/layout/includes/widget/index.pug b/layout/includes/widget/index.pug
index 175fcdc..388ea1c 100644
--- a/layout/includes/widget/index.pug
+++ b/layout/includes/widget/index.pug
@@ -13,6 +13,8 @@
.sticky_layout
if showToc
include ./card_post_toc.pug
+ if page.series
+ include ./card_post_series.pug
!=partial('includes/widget/card_recent_post', {}, {cache: true})
!=partial('includes/widget/card_ad', {}, {cache: true})
else
diff --git a/package.json b/package.json
index 6e97de2..0ed17d4 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "hexo-theme-butterfly",
- "version": "4.9.0",
+ "version": "4.10.0",
"description": "A Simple and Card UI Design theme for Hexo",
"main": "package.json",
"scripts": {
diff --git a/plugins.yml b/plugins.yml
index ef96ad0..b25c304 100644
--- a/plugins.yml
+++ b/plugins.yml
@@ -1,11 +1,11 @@
algolia_search:
name: algoliasearch
file: dist/algoliasearch-lite.umd.js
- version: 4.17.1
+ version: 4.20.0
instantsearch:
name: instantsearch.js
file: dist/instantsearch.production.min.js
- version: 4.56.1
+ version: 4.57.0
pjax:
name: pjax
file: pjax.min.js
@@ -37,17 +37,17 @@ disqusjs_css:
twikoo:
name: twikoo
file: dist/twikoo.all.min.js
- version: 1.6.16
+ version: 1.6.22
waline_js:
name: '@waline/client'
file: dist/waline.js
other_name: waline
- version: 2.15.5
+ version: 2.15.8
waline_css:
name: '@waline/client'
file: dist/waline.css
other_name: waline
- version: 2.15.5
+ version: 2.15.8
sharejs:
name: butterfly-extsrc
file: sharejs/dist/js/social-share.min.js
@@ -64,16 +64,16 @@ katex:
name: katex
file: dist/katex.min.css
other_name: KaTeX
- version: 0.16.7
+ version: 0.16.9
katex_copytex:
name: katex
file: dist/contrib/copy-tex.min.js
other_name: KaTeX
- version: 0.16.7
+ version: 0.16.9
mermaid:
name: mermaid
file: dist/mermaid.min.js
- version: 10.2.2
+ version: 10.5.0
canvas_ribbon:
name: butterfly-extsrc
file: dist/canvas-ribbon.min.js
@@ -98,14 +98,14 @@ click_heart:
name: butterfly-extsrc
file: dist/click-heart.min.js
version: 1.1.3
-ClickShowText:
+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.8.3
+ version: 17.8.4
instantpage:
name: instant.page
file: instantpage.js
@@ -121,12 +121,12 @@ pangu:
fancybox_css:
name: '@fancyapps/ui'
file: dist/fancybox/fancybox.css
- version: 5.0.19
+ version: 5.0.24
other_name: fancyapps-ui
fancybox:
name: '@fancyapps/ui'
file: dist/fancybox/fancybox.umd.js
- version: 5.0.19
+ version: 5.0.24
other_name: fancyapps-ui
medium_zoom:
name: medium-zoom
@@ -144,15 +144,12 @@ fontawesome:
name: '@fortawesome/fontawesome-free'
file: css/all.min.css
other_name: font-awesome
- version: 6.4.0
-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
+ version: 6.4.2
+egjs_infinitegrid:
+ name: '@egjs/infinitegrid'
+ other_name: egjs-infinitegrid
+ file: dist/infinitegrid.min.js
+ version: 4.10.1
aplayer_css:
name: aplayer
file: dist/APlayer.min.css
@@ -183,11 +180,11 @@ prismjs_autoloader:
artalk_js:
name: artalk
file: dist/Artalk.js
- version: 2.5.5
+ version: 2.6.3
artalk_css:
name: artalk
file: dist/Artalk.css
- version: 2.5.5
+ version: 2.6.3
pace_js:
name: pace-js
other_name: pace
@@ -202,12 +199,12 @@ docsearch_js:
name: '@docsearch/js'
other_name: docsearch-js
file: dist/umd/index.js
- version: 3.4.0
+ version: 3.5.2
docsearch_css:
name: '@docsearch/css'
other_name: docsearch-css
file: dist/style.css
- version: 3.4.0
+ version: 3.5.2
abcjs_basic_js:
name: abcjs
file: dist/abcjs-basic-min.js
diff --git a/scripts/events/stylus.js b/scripts/events/stylus.js
index 4b95538..2e6b802 100644
--- a/scripts/events/stylus.js
+++ b/scripts/events/stylus.js
@@ -1,15 +1,16 @@
/**
- * stylus
+ * Stylus renderer
*/
'use strict'
-hexo.extend.filter.register('stylus:renderer', function (style) {
- const { highlight, prismjs } = hexo.config
- style
- .define('$highlight_enable', highlight && highlight.enable)
- .define('$highlight_line_number', highlight && highlight.line_number)
- .define('$prismjs_enable', prismjs && prismjs.enable)
- .define('$prismjs_line_number', prismjs && prismjs.line_number)
- // .import(this.source_dir.replace(/\\/g, '/') + '_data/css/*')
+hexo.extend.filter.register('stylus:renderer', style => {
+ const { enable: highlightEnable, line_number: highlightLineNumber } = hexo.config.highlight
+ const { enable: prismjsEnable, line_number: prismjsLineNumber } = hexo.config.prismjs
+
+ style.define('$highlight_enable', highlightEnable)
+ .define('$highlight_line_number', highlightLineNumber)
+ .define('$prismjs_enable', prismjsEnable)
+ .define('$prismjs_line_number', prismjsLineNumber)
+ // .import(`${this.source_dir.replace(/\\/g, '/')}_data/css/*`)
})
diff --git a/scripts/filters/post_lazyload.js b/scripts/filters/post_lazyload.js
index c4267d1..cecc6ce 100644
--- a/scripts/filters/post_lazyload.js
+++ b/scripts/filters/post_lazyload.js
@@ -8,22 +8,20 @@
const urlFor = require('hexo-util').url_for.bind(hexo)
-function lazyload (htmlContent) {
+const lazyload = htmlContent => {
const bg = hexo.theme.config.lazyload.placeholder ? urlFor(hexo.theme.config.lazyload.placeholder) : 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
return htmlContent.replace(/( {
+ const { enable, field } = hexo.theme.config.lazyload
+ if (!enable || field !== 'site') return
+ return lazyload(data)
})
hexo.extend.filter.register('after_post_render', data => {
- const config = hexo.theme.config.lazyload
- if (!config.enable) return
- if (config.field !== 'post') return
- data.content = lazyload.call(this, data.content)
+ const { enable, field } = hexo.theme.config.lazyload
+ if (!enable || field !== 'post') return
+ data.content = lazyload(data.content)
return data
})
diff --git a/scripts/filters/random_cover.js b/scripts/filters/random_cover.js
index 4ecedce..7dac3cf 100644
--- a/scripts/filters/random_cover.js
+++ b/scripts/filters/random_cover.js
@@ -5,40 +5,35 @@
'use strict'
-hexo.extend.filter.register('before_post_render', function (data) {
+hexo.extend.filter.register('before_post_render', data => {
const imgTestReg = /\.(png|jpe?g|gif|svg|webp)(\?.*)?$/i
- let randomCover
- let coverVal = data.cover
+ let { cover: coverVal, top_img: topImg } = data
// Add path to top_img and cover if post_asset_folder is enabled
if (hexo.config.post_asset_folder) {
- const topImg = data.top_img
- if (topImg && topImg.indexOf('/') === -1 && imgTestReg.test(topImg)) data.top_img = data.path + topImg
- if (coverVal && coverVal.indexOf('/') === -1 && imgTestReg.test(coverVal)) data.cover = data.path + coverVal
+ if (topImg && topImg.indexOf('/') === -1 && imgTestReg.test(topImg)) data.top_img = `${data.path}${topImg}`
+ if (coverVal && coverVal.indexOf('/') === -1 && imgTestReg.test(coverVal)) data.cover = `${data.path}${coverVal}`
}
const randomCoverFn = () => {
- const theme = hexo.theme.config
- if (!(theme.cover && theme.cover.default_cover)) return false
- if (!Array.isArray(theme.cover.default_cover)) return theme.cover.default_cover
- const num = Math.floor(Math.random() * theme.cover.default_cover.length)
- return theme.cover.default_cover[num]
+ const { cover: { default_cover: defaultCover } } = hexo.theme.config
+ if (!defaultCover) return false
+ if (!Array.isArray(defaultCover)) return defaultCover
+ const num = Math.floor(Math.random() * defaultCover.length)
+ return defaultCover[num]
}
if (coverVal === false) return data
// If cover is not set, use random cover
if (!coverVal) {
- randomCover = randomCoverFn()
+ const randomCover = randomCoverFn()
data.cover = randomCover
coverVal = randomCover // update coverVal
}
- if (coverVal) {
- if (coverVal.indexOf('//') !== -1 || imgTestReg.test(coverVal)) {
- data.cover_type = 'img'
- return data
- }
+ if (coverVal && (coverVal.indexOf('//') !== -1 || imgTestReg.test(coverVal))) {
+ data.cover_type = 'img'
}
return data
diff --git a/scripts/helpers/aside_categories.js b/scripts/helpers/aside_categories.js
index 0e60b8e..244b163 100644
--- a/scripts/helpers/aside_categories.js
+++ b/scripts/helpers/aside_categories.js
@@ -31,6 +31,7 @@ hexo.extend.helper.register('aside_categories', function (categories, options) {
if (parent) { query.parent = parent } else { query.parent = { $exists: false } }
return categories.find(query).sort(orderby, order).filter((cat) => cat.length)
}
+ let expandBtn = ''
const hierarchicalList = (t, level, parent, topparent = true) => {
let result = ''
@@ -59,6 +60,7 @@ hexo.extend.helper.register('aside_categories', function (categories, options) {
}
if (isExpand && isTopParent && child) {
+ expandBtn = ' expandBtn'
result += ``
}
@@ -91,7 +93,7 @@ hexo.extend.helper.register('aside_categories', function (categories, options) {
${this._p('aside.card_categories')}
${moreButton()}
-
+ `
})
diff --git a/scripts/helpers/findArchiveLength.js b/scripts/helpers/findArchiveLength.js
index 56e9bcb..152b15e 100644
--- a/scripts/helpers/findArchiveLength.js
+++ b/scripts/helpers/findArchiveLength.js
@@ -1,36 +1,36 @@
-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
+hexo.extend.helper.register('getArchiveLength', function () {
+ const { archive_generator: archiveGenerator } = hexo.config
+ if (archiveGenerator && archiveGenerator.enable === false) return this.site.posts.length
+ const { yearly, monthly, daily } = archiveGenerator
const { year, month, day } = this.page
- if (yearly === false || !year) return allPostsLength
+ if (yearly === false || !year) return this.site.posts.length
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
+ switch (type) {
+ case 'year':
+ return y1 === y2
+ case 'month':
+ return y1 === y2 && m1 === m2
+ case 'day':
+ return y1 === y2 && m1 === m2 && d1 === d2
+ default:
+ return false
}
}
const generateDateObj = (type) => {
- const dateObj = []
- let length = 0
-
- posts.forEach(post => {
+ return posts.reduce((dateObj, post) => {
const date = post.date.clone()
const year = date.year()
const month = date.month() + 1
const day = date.date()
- const lastData = dateObj[length - 1]
+ const lastData = dateObj[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({
+ dateObj.push({
name,
year,
month,
@@ -40,19 +40,19 @@ hexo.extend.helper.register('findArchiveLength', function (func) {
} else {
lastData.count++
}
- })
- return dateObj
+ 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 data = this.fragment_cache('createArchiveObj', () => {
+ const dateObjs = []
+ if (yearly) dateObjs.push(...generateDateObj('year'))
+ if (monthly) dateObjs.push(...generateDateObj('month'))
+ if (daily) dateObjs.push(...generateDateObj('day'))
+ return dateObjs
})
- const name = month ? day ? `${year}-${month}-${day}` : `${year}-${month}` : year
+ const name = month ? (day ? `${year}-${month}-${day}` : `${year}-${month}`) : year
return data.find(item => item.name === name).count
})
diff --git a/scripts/helpers/inject_head_js.js b/scripts/helpers/inject_head_js.js
index e4ad6fb..5871fd7 100644
--- a/scripts/helpers/inject_head_js.js
+++ b/scripts/helpers/inject_head_js.js
@@ -7,87 +7,99 @@
hexo.extend.helper.register('inject_head_js', function () {
const { darkmode, aside } = this.theme
- const start = darkmode.start ? darkmode.start : 6
- const end = darkmode.end ? darkmode.end : 18
+ const start = darkmode.start || 6
+ const end = darkmode.end || 18
const { theme_color } = hexo.theme.config
const themeColorLight = (theme_color && theme_color.enable && theme_color.meta_theme_color_light) || '#ffffff'
const themeColorDark = (theme_color && theme_color.enable && theme_color.meta_theme_color_dark) || '#0d0d0d'
- const localStore = `
- win.saveToLocal = {
- set: function setWithExpiry(key, value, ttl) {
- if (ttl === 0) return
- const now = new Date()
- const expiryDay = ttl * 86400000
- const item = {
- value: value,
- expiry: now.getTime() + expiryDay,
+ const createLocalStore = () => {
+ return `
+ win.saveToLocal = {
+ set: (key, value, ttl) => {
+ if (ttl === 0) return
+ const now = Date.now()
+ const expiry = now + ttl * 86400000
+ const item = {
+ value,
+ expiry
+ }
+ localStorage.setItem(key, JSON.stringify(item))
+ },
+
+ get: key => {
+ const itemStr = localStorage.getItem(key)
+
+ if (!itemStr) {
+ return undefined
+ }
+ const item = JSON.parse(itemStr)
+ const now = Date.now()
+
+ if (now > item.expiry) {
+ localStorage.removeItem(key)
+ return undefined
+ }
+ return item.value
}
- localStorage.setItem(key, JSON.stringify(item))
- },
-
- get: function getWithExpiry(key) {
- const itemStr = localStorage.getItem(key)
-
- if (!itemStr) {
- return undefined
- }
- const item = JSON.parse(itemStr)
- const now = new Date()
-
- if (now.getTime() > item.expiry) {
- localStorage.removeItem(key)
- return undefined
- }
- return item.value
}
- }
- `
+ `
+ }
// https://stackoverflow.com/questions/16839698/jquery-getscript-alternative-in-native-javascript
- const getScript = `
- win.getScript = url => new Promise((resolve, reject) => {
- const script = document.createElement('script')
- script.src = url
- script.async = true
- script.onerror = reject
- script.onload = script.onreadystatechange = function() {
- const loadState = this.readyState
- if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
- script.onload = script.onreadystatechange = null
- resolve()
- }
- document.head.appendChild(script)
- })
- `
+ const createGetScript = () => {
+ return `
+ win.getScript = (url, attr = {}) => new Promise((resolve, reject) => {
+ const script = document.createElement('script')
+ script.src = url
+ script.async = true
+ script.onerror = reject
+ script.onload = script.onreadystatechange = function() {
+ const loadState = this.readyState
+ if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
+ script.onload = script.onreadystatechange = null
+ resolve()
+ }
- const getCSS = `
- win.getCSS = (url,id = false) => new Promise((resolve, reject) => {
- const link = document.createElement('link')
- link.rel = 'stylesheet'
- link.href = url
- if (id) link.id = id
- link.onerror = reject
- link.onload = link.onreadystatechange = function() {
- const loadState = this.readyState
- if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
- link.onload = link.onreadystatechange = null
- resolve()
- }
- document.head.appendChild(link)
- })
- `
+ Object.keys(attr).forEach(key => {
+ script.setAttribute(key, attr[key])
+ })
- let darkmodeJs = ''
- if (darkmode.enable) {
- darkmodeJs = `
- win.activateDarkMode = function () {
+ document.head.appendChild(script)
+ })
+ `
+ }
+
+ const createGetCSS = () => {
+ return `
+ win.getCSS = (url, id = false) => new Promise((resolve, reject) => {
+ const link = document.createElement('link')
+ link.rel = 'stylesheet'
+ link.href = url
+ if (id) link.id = id
+ link.onerror = reject
+ link.onload = link.onreadystatechange = function() {
+ const loadState = this.readyState
+ if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
+ link.onload = link.onreadystatechange = null
+ resolve()
+ }
+ document.head.appendChild(link)
+ })
+ `
+ }
+
+ const createDarkmodeJs = () => {
+ if (!darkmode.enable) return ''
+
+ let darkmodeJs = `
+ win.activateDarkMode = () => {
document.documentElement.setAttribute('data-theme', 'dark')
if (document.querySelector('meta[name="theme-color"]') !== null) {
document.querySelector('meta[name="theme-color"]').setAttribute('content', '${themeColorDark}')
}
}
- win.activateLightMode = function () {
+ win.activateLightMode = () => {
document.documentElement.setAttribute('data-theme', 'light')
if (document.querySelector('meta[name="theme-color"]') !== null) {
document.querySelector('meta[name="theme-color"]').setAttribute('content', '${themeColorLight}')
@@ -114,7 +126,7 @@ hexo.extend.helper.register('inject_head_js', function () {
const isNight = hour <= ${start} || hour >= ${end}
isNight ? activateDarkMode() : activateLightMode()
}
- window.matchMedia('(prefers-color-scheme: dark)').addListener(function (e) {
+ window.matchMedia('(prefers-color-scheme: dark)').addListener(e => {
if (saveToLocal.get('theme') === undefined) {
e.matches ? activateDarkMode() : activateLightMode()
}
@@ -133,15 +145,18 @@ hexo.extend.helper.register('inject_head_js', function () {
`
} else {
darkmodeJs += `
- if (t === 'dark') activateDarkMode()
- else if (t === 'light') activateLightMode()
- `
+ if (t === 'dark') activateDarkMode()
+ else if (t === 'light') activateLightMode()
+ `
}
+
+ return darkmodeJs
}
- let asideStatus = ''
- if (aside.enable && aside.button) {
- asideStatus = `
+ const createAsideStatus = () => {
+ if (!aside.enable || !aside.button) return ''
+
+ return `
const asideStatus = saveToLocal.get('aside-status')
if (asideStatus !== undefined) {
if (asideStatus === 'hide') {
@@ -153,14 +168,16 @@ hexo.extend.helper.register('inject_head_js', function () {
`
}
- const detectApple = `
- const detectApple = () => {
- if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
- document.documentElement.classList.add('apple')
+ const createDetectApple = () => {
+ return `
+ const detectApple = () => {
+ if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
+ document.documentElement.classList.add('apple')
+ }
}
- }
- detectApple()
+ detectApple()
`
+ }
- return ``
+ return ``
})
diff --git a/scripts/helpers/page.js b/scripts/helpers/page.js
index 22622c3..6c773e3 100644
--- a/scripts/helpers/page.js
+++ b/scripts/helpers/page.js
@@ -1,10 +1,3 @@
-/**
- * Butterfly
- * @example
- * page_description()
- * cloudTags(source, minfontsize, maxfontsize, limit)
- */
-
'use strict'
const { stripHTML, escapeHTML, prettyUrls } = require('hexo-util')
@@ -60,12 +53,8 @@ hexo.extend.helper.register('md5', function (path) {
})
hexo.extend.helper.register('injectHtml', function (data) {
- let result = ''
if (!data) return ''
- for (let i = 0; i < data.length; i++) {
- result += data[i]
- }
- return result
+ return data.join('')
})
hexo.extend.helper.register('findArchivesTitle', function (page, menu, date) {
@@ -95,8 +84,5 @@ hexo.extend.helper.register('findArchivesTitle', function (page, menu, date) {
hexo.extend.helper.register('isImgOrUrl', function (path) {
const imgTestReg = /\.(png|jpe?g|gif|svg|webp)(\?.*)?$/i
- if (path.indexOf('//') !== -1 || imgTestReg.test(path)) {
- return true
- }
- return false
+ return path.indexOf('//') !== -1 || imgTestReg.test(path)
})
diff --git a/scripts/helpers/series.js b/scripts/helpers/series.js
new file mode 100644
index 0000000..e1c9338
--- /dev/null
+++ b/scripts/helpers/series.js
@@ -0,0 +1,22 @@
+'use strict'
+
+hexo.extend.helper.register('groupPosts', function () {
+ const getGroupArray = array => {
+ const groups = {}
+ array.forEach(item => {
+ const Key = item.series
+ if (!Key) return
+ groups[Key] = groups[Key] || []
+ groups[Key].push(item)
+ })
+ return groups
+ }
+
+ const sortPosts = posts => {
+ const { orderBy = 'date', order = 1 } = this.theme.aside.card_post_series
+ if (orderBy === 'title') return posts.sort('title', order)
+ return posts.sort('date', order)
+ }
+
+ return getGroupArray(sortPosts(this.site.posts))
+})
diff --git a/scripts/tag/button.js b/scripts/tag/button.js
index 33a0e53..624cdd3 100644
--- a/scripts/tag/button.js
+++ b/scripts/tag/button.js
@@ -9,17 +9,9 @@
const urlFor = require('hexo-util').url_for.bind(hexo)
-function btn (args) {
+const btn = args => {
args = args.join(' ').split(',')
- let url = args[0] || ''
- let text = args[1] || ''
- let icon = args[2] || ''
- let option = args[3] || ''
-
- url = url.trim()
- text = text.trim()
- icon = icon.trim()
- option = option.trim()
+ const [url = '', text = '', icon = '', option = ''] = args.map(arg => arg.trim())
return `${icon.length ? `` : ''}${text.length ? `${text}` : ''}`
diff --git a/scripts/tag/gallery.js b/scripts/tag/gallery.js
index 632c514..f8bceb6 100644
--- a/scripts/tag/gallery.js
+++ b/scripts/tag/gallery.js
@@ -2,23 +2,25 @@
* Butterfly
* galleryGroup and gallery
* {% galleryGroup [name] [descr] [url] [img] %}
- * {% gallery [lazyload],[rowHeight],[limit] %}
- * {% gallery url,[url],[lazyload],[rowHeight],[limit] %}
+ *
+ * {% gallery [button],%}
+ * {% gallery url,[url],[button]%}
*/
'use strict'
const urlFor = require('hexo-util').url_for.bind(hexo)
-function gallery (args, content) {
- const { data, languages } = hexo.theme.i18n
+const gallery = (args, content) => {
args = args.join(' ').split(',')
- let rowHeight, limit, lazyload, type, dataStr
+ let button = false
+ let type = 'data'
+ let dataStr = ''
if (args[0] === 'url') {
- [type, dataStr, lazyload, rowHeight = 220, limit = 10] = args // url,[link],[lazyload],[rowHeight],[limit]
+ [type, dataStr, button] = args // url,[link],[lazyload]
} else {
- [lazyload, rowHeight = 220, limit = 10] = args // [lazyload],[rowHeight],[limit]
+ [button] = args // [lazyload]
const regex = /!\[(.*?)\]\(([^\s]*)\s*(?:["'](.*?)["']?)?\s*\)/g
let m
const arr = []
@@ -36,30 +38,24 @@ function gallery (args, content) {
dataStr = JSON.stringify(arr)
}
- type = type ? ' url' : ' data'
- const lazyloadClass = lazyload === 'true' ? 'lazyload' : ''
-
- return `
-
- ${dataStr}
-
-
+ return `
`
}
-function galleryGroup (args) {
- const name = args[0]
- const descr = args[1]
- const url = urlFor(args[2])
- const img = urlFor(args[3])
+const galleryGroup = args => {
+ const [name, descr, url, img] = args
+ const imgUrl = urlFor(img)
+ const urlLink = urlFor(url)
- return `
-
-
+ return `
+
${name}
${descr}
-
+
`
diff --git a/scripts/tag/hide.js b/scripts/tag/hide.js
index ccf9365..a788172 100644
--- a/scripts/tag/hide.js
+++ b/scripts/tag/hide.js
@@ -16,51 +16,46 @@
'use strict'
-function hideInline (args) {
- args = args.join(' ').split(',')
- const content = args[0]
- const display = args[1] || 'Click'
- const bg = args[2] || false
- const color = args[3] || false
- let group = 'style="'
+const parseArgs = args => {
+ return args.join(' ').split(',')
+}
- if (bg) group += `background-color: ${bg};`
- if (color) group += `color: ${color}`
- group += '"'
+const generateStyle = (bg, color) => {
+ let style = 'style="'
+ if (bg) {
+ style += `background-color: ${bg};`
+ }
+ if (color) {
+ style += `color: ${color}`
+ }
+ style += '"'
+ return style
+}
+
+const hideInline = args => {
+ const [content, display = 'Click', bg = false, color = false] = parseArgs(args)
+ const group = generateStyle(bg, color)
return `${content}`
}
-function hideBlock (args, content) {
- args = args.join(' ').split(',')
- const display = args[0] || 'Click'
- const bg = args[1] || false
- const color = args[2] || false
- let group = 'style="'
-
- if (bg) group += `background-color: ${bg};`
- if (color) group += `color: ${color}`
- group += '"'
+const hideBlock = (args, content) => {
+ const [display = 'Click', bg = false, color = false] = parseArgs(args)
+ const group = generateStyle(bg, color)
return `${hexo.render.renderSync({ text: content, engine: 'markdown' })}
`
}
-function hideToggle (args, content) {
- args = args.join(' ').split(',')
- const display = args[0]
- const bg = args[1] || false
- const color = args[2] || false
- let group = 'style="'
+const hideToggle = (args, content) => {
+ const [display, bg = false, color = false] = parseArgs(args)
+ const group = generateStyle(bg, color)
let border = ''
if (bg) {
border = `style="border: 1px solid ${bg}"`
- group += `background-color: ${bg};`
}
- if (color) group += `color: ${color}`
- group += '"'
return `${display}
${hexo.render.renderSync({ text: content, engine: 'markdown' })}
`
}
diff --git a/scripts/tag/inlineImg.js b/scripts/tag/inlineImg.js
index 9bcbfdb..753add0 100644
--- a/scripts/tag/inlineImg.js
+++ b/scripts/tag/inlineImg.js
@@ -1,17 +1,19 @@
/**
* inlineImg 圖片
- * {% inlineImg src height %}
+ * @param {Array} args 圖片名稱和高度
+ * @param {string} args[0] 圖片名稱
+ * @param {number} args[1] 圖片高度
+ * @returns {string} 圖片標籤
*/
'use strict'
const urlFor = require('hexo-util').url_for.bind(hexo)
-function inlineImg (args) {
- const img = args[0]
- const height = args[1] ? `style="height:${args[1]}"` : ''
-
- return `
`
+const inlineImg = ([img, height = '']) => {
+ const heightStyle = height ? `style="height:${height}"` : ''
+ const src = urlFor(img)
+ return `
`
}
hexo.extend.tag.register('inlineImg', inlineImg, { ends: false })
diff --git a/scripts/tag/label.js b/scripts/tag/label.js
index 877c909..4c9e692 100644
--- a/scripts/tag/label.js
+++ b/scripts/tag/label.js
@@ -6,10 +6,8 @@
'use strict'
-function addLabel (args, content) {
- const text = args[0]
- const className = args[1] || 'default'
-
+const addLabel = args => {
+ const [text, className = 'default'] = args
return `${text} `
}
diff --git a/scripts/tag/mermaid.js b/scripts/tag/mermaid.js
index 237b7ee..ba25c0f 100644
--- a/scripts/tag/mermaid.js
+++ b/scripts/tag/mermaid.js
@@ -8,7 +8,7 @@
const { escapeHTML } = require('hexo-util')
-function mermaid (args, content) {
+const mermaid = (args, content) => {
return ``
diff --git a/scripts/tag/note.js b/scripts/tag/note.js
index aa19129..125bc61 100644
--- a/scripts/tag/note.js
+++ b/scripts/tag/note.js
@@ -6,10 +6,10 @@
'use strict'
-function postNote (args, content) {
+const postNote = (args, content) => {
const styleConfig = hexo.theme.config.note.style
- const lastArgs = args[args.length - 1]
- if (!(lastArgs === 'flat' || lastArgs === 'modern' || lastArgs === 'simple' || lastArgs === 'disabled')) {
+ const noteTag = ['flat', 'modern', 'simple', 'disabled']
+ if (!noteTag.includes(args[args.length - 1])) {
args.push(styleConfig)
}
diff --git a/scripts/tag/score.js b/scripts/tag/score.js
index 3157245..441247b 100644
--- a/scripts/tag/score.js
+++ b/scripts/tag/score.js
@@ -3,20 +3,20 @@
* {% score %}
*/
-'use strict';
+'use strict'
-function score(args, content) {
- function escapeHtmlTags(s) {
- let lookup = {
- '&': "&",
- '"': """,
- '\'': "'",
- '<': "<",
- '>': ">"
- };
- return s.replace(/[&"'<>]/g, c => lookup[c]);
+const score = (args, content) => {
+ const escapeHtmlTags = s => {
+ const lookup = {
+ '&': '&',
+ '"': '"',
+ '\'': ''',
+ '<': '<',
+ '>': '>'
}
- return `${escapeHtmlTags(content)}
`;
+ return s.replace(/[&"'<>]/g, c => lookup[c])
+ }
+ return `${escapeHtmlTags(content)}
`
}
-hexo.extend.tag.register('score', score, {ends: true});
+hexo.extend.tag.register('score', score, { ends: true })
diff --git a/scripts/tag/series.js b/scripts/tag/series.js
new file mode 100644
index 0000000..4a901df
--- /dev/null
+++ b/scripts/tag/series.js
@@ -0,0 +1,69 @@
+/**
+ * series plugin
+ * Syntax:
+ * {% series [series name] %}
+ * Usage:
+ * {% series %}
+ * {% series series1 %}
+ */
+
+'use strict'
+
+const urlFor = require('hexo-util').url_for.bind(hexo)
+const groups = {}
+
+hexo.extend.filter.register('before_post_render', data => {
+ if (!hexo.theme.config.series.enable) return data
+ const { layout, series } = data
+ if (layout === 'post' && series) {
+ groups[series] = groups[series] || []
+ groups[series].push({
+ title: data.title,
+ path: data.path,
+ date: data.date.unix()
+ })
+ }
+ return data
+})
+
+function series (args) {
+ const { series } = hexo.theme.config
+ if (!series.enable) {
+ hexo.log.warn('Series plugin is disabled in the theme config')
+ return ''
+ }
+
+ const seriesArr = args.length ? groups[args[0]] : groups[this.series]
+
+ if (!seriesArr) {
+ hexo.log.warn(`There is no series named "${args[0]}"`)
+ return ''
+ }
+
+ const isAsc = (series.order || 1) === 1 // 1: asc, -1: desc
+ const isSortByTitle = series.orderBy === 'title'
+
+ const compareFn = (a, b) => {
+ const itemA = isSortByTitle ? a.title.toUpperCase() : a.date
+ const itemB = isSortByTitle ? b.title.toUpperCase() : b.date
+
+ if (itemA < itemB) {
+ return isAsc ? -1 : 1
+ }
+ if (itemA > itemB) {
+ return isAsc ? 1 : -1
+ }
+ return 0
+ }
+
+ seriesArr.sort(compareFn)
+
+ let result = ''
+ seriesArr.forEach(ele => {
+ result += `- ${ele.title}
`
+ })
+
+ return series.number ? `${result}
` : ``
+}
+
+hexo.extend.tag.register('series', series, { ends: false })
diff --git a/scripts/tag/tabs.js b/scripts/tag/tabs.js
index 050aeee..6ec87de 100644
--- a/scripts/tag/tabs.js
+++ b/scripts/tag/tabs.js
@@ -6,24 +6,22 @@
'use strict'
-function postTabs (args, content) {
+const postTabs = (args, content) => {
const tabBlock = /\n([\w\W\s\S]*?)/g
-
args = args.join(' ').split(',')
const tabName = args[0]
const tabActive = Number(args[1]) || 0
-
const matches = []
let match
let tabId = 0
let tabNav = ''
let tabContent = ''
+ let noDefault = true
!tabName && hexo.log.warn('Tabs block must have unique name!')
while ((match = tabBlock.exec(content)) !== null) {
- matches.push(match[1])
- matches.push(match[2])
+ matches.push(match[1], match[2])
}
for (let i = 0; i < matches.length; i += 2) {
@@ -44,17 +42,21 @@ function postTabs (args, content) {
const icon = tabIcon.trim()
tabIcon.length > 0 && (tabIcon = ``)
- const toTop = ''
-
- const isActive = (tabActive > 0 && tabActive === tabId) || (tabActive === 0 && tabId === 1) ? ' active' : ''
- tabNav += ``
- tabContent += `${postContent + toTop}
`
+ let isActive = ''
+ if ((tabActive > 0 && tabActive === tabId) || (tabActive === 0 && tabId === 1)) {
+ isActive = ' active'
+ noDefault = false
+ }
+ tabNav += ``
+ tabContent += `${postContent}
`
}
- tabNav = ``
+ const toTop = ''
+
+ tabNav = ``
tabContent = `${tabContent}
`
- return `${tabNav + tabContent}
`
+ return `${tabNav + tabContent + toTop}
`
}
hexo.extend.tag.register('tabs', postTabs, { ends: true })
diff --git a/scripts/tag/timeline.js b/scripts/tag/timeline.js
index 6c87a6b..6dde854 100644
--- a/scripts/tag/timeline.js
+++ b/scripts/tag/timeline.js
@@ -5,15 +5,15 @@
'use strict'
-function timeLineFn (args, content) {
+const timeLineFn = (args, content) => {
const tlBlock = /\n([\w\W\s\S]*?)/g
let result = ''
let color = ''
+ let text = ''
if (args.length) {
- args = args.join(' ').split(',')
- color = args[1]
- const mdContent = hexo.render.renderSync({ text: args[0], engine: 'markdown' })
+ [text, color] = args.join(' ').split(',')
+ const mdContent = hexo.render.renderSync({ text, engine: 'markdown' })
result += ``
}
diff --git a/source/css/_global/function.styl b/source/css/_global/function.styl
index 5782812..9a4fa3c 100644
--- a/source/css/_global/function.styl
+++ b/source/css/_global/function.styl
@@ -134,7 +134,7 @@ if hexo-config('enter_transitions')
#footer
animation: bottom-top 1s
- #page-header
+ #page-header:not(.full_page)
animation: header-effect 1s
#site-title,
@@ -155,7 +155,7 @@ if hexo-config('enter_transitions')
&.open
for i in 1 2 3 4
> :nth-child({i})
- animation: sidebarItem (i / 5)s
+ animation: sidebarItem (i / 5s)
.scroll-down-effects
animation: scroll-down-effect 1.5s infinite
@@ -169,16 +169,16 @@ if hexo-config('avatar.effect') == true
@keyframes scroll-down-effect
0%
- top: 0
opacity: .4
+ transform: translate(0, 0)
50%
- top: -16px
opacity: 1
+ transform: translate(0, -16px)
100%
- top: 0
opacity: .4
+ transform: translate(0, 0)
@keyframes header-effect
0%
@@ -198,12 +198,12 @@ if hexo-config('avatar.effect') == true
@keyframes bottom-top
0%
- margin-top: 50px
opacity: 0
+ transform: translateY(50px)
100%
- margin-top: 0
opacity: 1
+ transform: translateY(0)
@keyframes titleScale
0%
diff --git a/source/css/_global/index.styl b/source/css/_global/index.styl
index 8bb57c7..22c87c2 100644
--- a/source/css/_global/index.styl
+++ b/source/css/_global/index.styl
@@ -49,6 +49,7 @@ body
if !hexo-config('copy.enable')
user-select: none
+ -webkit-user-select: none
// scrollbar - chrome/safari
*::-webkit-scrollbar
diff --git a/source/css/_highlight/highlight.styl b/source/css/_highlight/highlight.styl
index d54798c..78c0812 100644
--- a/source/css/_highlight/highlight.styl
+++ b/source/css/_highlight/highlight.styl
@@ -13,6 +13,16 @@ wordWrap = $highlight_enable && !$highlight_line_number && hexo-config('code_wor
--hlscrollbar-bg: $highlight-scrollbar
--hlexpand-bg: linear-gradient(180deg, rgba($highlight-background, .6), rgba($highlight-background, .9))
+[data-theme='dark']
+ --hl-color: alpha(#FFFFFF, .7)
+ --hl-bg: lighten(#121212, 2)
+ --hltools-bg: lighten(#121212, 3)
+ --hltools-color: #90a4ae
+ --hlnumber-bg: lighten(#121212, 2)
+ --hlnumber-color: alpha(#FFFFFF, .4)
+ --hlscrollbar-bg: lighten(#121212, 5)
+ --hlexpand-bg: linear-gradient(180deg, rgba(lighten(#121212, 2), .6), rgba(lighten(#121212, 2), .9))
+
if $highlight_enable
@require 'highlight/index'
@@ -77,6 +87,7 @@ $code-block
&.copy-true
user-select: all
+ -webkit-user-select: all
& > table,
& > pre
@@ -94,8 +105,13 @@ $code-block
color: var(--hltools-color)
font-size: $code-font-size
- &.closed ~ *
- display: none
+ &.closed
+ & ~ *
+ display: none
+
+ .expand
+ transition: all .3s
+ transform: rotate(-90deg) !important
.expand
position: absolute
@@ -106,10 +122,6 @@ $code-block
& + .code-lang
left: 1.7em
- &.closed
- transition: all .3s
- transform: rotate(-90deg) !important
-
.code-lang
position: absolute
left: 14px
@@ -117,6 +129,7 @@ $code-block
font-weight: bold
font-size: 1.15em
user-select: none
+ -webkit-user-select: none
.copy-notice
position: absolute
@@ -135,6 +148,7 @@ $code-block
.gutter
user-select: none
+ -webkit-user-select: none
.gist table
width: auto
diff --git a/source/css/_highlight/prismjs/line-number.styl b/source/css/_highlight/prismjs/line-number.styl
index 69f2787..e37215b 100644
--- a/source/css/_highlight/prismjs/line-number.styl
+++ b/source/css/_highlight/prismjs/line-number.styl
@@ -27,6 +27,7 @@
font-size: 100%
pointer-events: none
user-select: none
+ -webkit-user-select: none
& > span
display: block
diff --git a/source/css/_layout/aside.styl b/source/css/_layout/aside.styl
index 981bc12..f9c1bae 100644
--- a/source/css/_layout/aside.styl
+++ b/source/css/_layout/aside.styl
@@ -179,6 +179,13 @@
> .parent
> a
+ &.expand
+ i
+ transform: rotate(-90deg)
+
+ & + .child
+ display: block
+
.card-category-list
&-name
width: 70% !important
@@ -187,15 +194,12 @@
width: calc(100% - 70% - 20px)
text-align: right
- i
- float: right
- margin-right: -.5em
- padding: .5em
- transition: transform .3s
- transform: rotate(0)
-
- &.expand
- transform: rotate(-90deg)
+ i
+ float: right
+ margin-right: -.5em
+ padding: .5em
+ transition: transform .3s
+ transform: rotate(0)
if hexo-config('aside.card_categories.expand') == false
> .child
@@ -220,16 +224,19 @@
+maxWidth900()
position: fixed
- right: -100%
+ right: 55px
bottom: 30px
z-index: 100
max-width: $toc-mobile-maxWidth
max-height: calc(100% - 60px)
width: $toc-mobile-width
- opacity: 0
- transition: initial
+ transition: none
+ transform: scale(0)
transform-origin: right bottom
+ &.open
+ transform: scale(1)
+
.toc-percentage
float: right
margin-top: -9px
@@ -242,6 +249,7 @@
overflow-y: overlay
margin: 0 -24px
max-height: calc(100vh - 120px)
+ width: calc(100% + 48px)
+maxWidth900()
max-height: calc(100vh - 140px)
diff --git a/source/css/_layout/comments.styl b/source/css/_layout/comments.styl
index 838094a..f2feebd 100644
--- a/source/css/_layout/comments.styl
+++ b/source/css/_layout/comments.styl
@@ -2,13 +2,18 @@
.comment-head
margin-bottom: 20px
+ &:after
+ display: block
+ clear: both
+ content: ''
+
.comment-headline
display: inline-block
vertical-align: middle
font-weight: 700
font-size: 1.43em
- #comment-switch
+ .comment-switch
display: inline-block
if hexo-config('comments.text')
@@ -30,7 +35,7 @@
.second-comment
color: $comments-switch-second-text
- .switch-btn
+ #switch-btn
position: relative
display: inline-block
margin: -4px 8px 0
@@ -53,12 +58,24 @@
content: ''
transition: .4s
- &.move
- background-color: $comments-switch-second-text
-
- &:before
- transform: translateX(20px)
-
.comment-wrap
- > div:nth-child(2)
- display: none
\ No newline at end of file
+ > div
+ animation: tabshow .5s
+
+ &:nth-child(2)
+ display: none
+
+ &.move
+ #switch-btn
+ background-color: $comments-switch-second-text
+
+ &:before
+ transform: translateX(20px)
+
+ .comment-wrap
+ > div
+ &:first-child
+ display: none
+
+ &:last-child
+ display: block
\ No newline at end of file
diff --git a/source/css/_layout/head.styl b/source/css/_layout/head.styl
index 8870a52..3c5f4ce 100644
--- a/source/css/_layout/head.styl
+++ b/source/css/_layout/head.styl
@@ -51,7 +51,6 @@
#site_social_icons
display: none
margin: 0 auto
- width: 300px
text-align: center
+maxWidth768()
@@ -65,7 +64,7 @@
#scroll-down
position: absolute
- bottom: 0
+ bottom: 10px
width: 100%
cursor: pointer
@@ -73,7 +72,7 @@
position: relative
width: 100%
color: var(--light-grey)
- font-size: 30px
+ font-size: 20px
// page
&.not-home-page
diff --git a/source/css/_layout/post.styl b/source/css/_layout/post.styl
index 2fa64a6..6ad1efc 100644
--- a/source/css/_layout/post.styl
+++ b/source/css/_layout/post.styl
@@ -1,13 +1,12 @@
beautify()
headStyle(fontsize)
- padding-left: unit(fontsize + 12, 'px')
+ padding-left: unit(fontsize + 8, 'px')
&:before
- margin-left: unit((-(fontsize + 6)), 'px')
- font-size: unit(fontsize, 'px')
+ font-size: unit(fontsize - 2, 'px')
&:hover
- padding-left: unit(fontsize + 18, 'px')
+ padding-left: unit(fontsize + 12, 'px')
h1,
h2,
@@ -22,6 +21,7 @@ beautify()
top: calc(50% - 7px)
color: $title-prefix-icon-color
content: $title-prefix-icon
+ left: 0
line-height: 1
transition: all .2s ease-out
@extend .fontawesomeIcon
@@ -73,6 +73,9 @@ beautify()
word-wrap: break-word
overflow-wrap: break-word
+ if hexo-config('text_align_justify')
+ text-align: justify
+
a
color: $theme-link-color
@@ -105,35 +108,15 @@ beautify()
font-family: Monaco, 'Ubuntu Mono', monospace
line-height: 1em
- if hexo-config('anchor.button.enable')
+ if hexo-config('anchor.click_to_scroll')
a.headerlink
- &:after
- @extend .fontawesomeIcon
- float: right
- color: var(--headline-presudo)
- content: $archor-button-icon
- font-size: .95em
- transition: all .3s
-
- &:hover
- &:after
- color: var(--pseudo-hover)
-
- if !hexo-config('anchor.button.always_show')
- a.headerlink
- &:after
- opacity: 0
-
- h1,
- h2,
- h3,
- h4,
- h5,
- h6
- &:hover
- a.headerlink
- &:after
- opacity: 1
+ position: absolute
+ top: 0
+ right: 0
+ left 0
+ bottom: 0
+ width 100%
+ height: 100%
ol,
ul
@@ -226,6 +209,9 @@ beautify()
color: $light-blue
font-weight: bold
+ i
+ margin-right: 3px
+
&-info
padding-left: 6px
diff --git a/source/css/_layout/reward.styl b/source/css/_layout/reward.styl
index e570967..4043aa2 100644
--- a/source/css/_layout/reward.styl
+++ b/source/css/_layout/reward.styl
@@ -15,6 +15,9 @@
color: var(--btn-color)
cursor: pointer
+ i
+ margin-right: 5px
+
&:hover
.reward-button
background: var(--btn-hover-color)
diff --git a/source/css/_layout/rightside.styl b/source/css/_layout/rightside.styl
index 0c25205..7d2832c 100644
--- a/source/css/_layout/rightside.styl
+++ b/source/css/_layout/rightside.styl
@@ -6,6 +6,10 @@
opacity: 0
transition: all .5s
+ &.rightside-show
+ opacity: .8
+ transform: translate(-58px, 0)
+
#rightside-config-hide
height: 0
opacity: 0
diff --git a/source/css/_layout/sidebar.styl b/source/css/_layout/sidebar.styl
index 2e38ee8..1a12606 100644
--- a/source/css/_layout/sidebar.styl
+++ b/source/css/_layout/sidebar.styl
@@ -13,7 +13,8 @@
right: -($sidebar-width)
z-index: 103
overflow-x: hidden
- overflow-y: auto
+ overflow-y: scroll
+ padding-left: 5px
width: $sidebar-width
height: 100%
background: var(--sidebar-bg)
@@ -32,7 +33,7 @@
margin: 20px auto
.menus_items
- padding: 0 10px 40px
+ padding: 0 5px
.site-page
@extend .limit-one-line
@@ -53,7 +54,7 @@
& > i:last-child
position: absolute
top: .78em
- right: 18px
+ right: 13px
transition: transform .3s
&.hide
@@ -65,4 +66,5 @@
.menus_item_child
margin: 0
+ padding-left: 25px
list-style: none
\ No newline at end of file
diff --git a/source/css/_mode/darkmode.styl b/source/css/_mode/darkmode.styl
index 7a39ca5..2251ee8 100644
--- a/source/css/_mode/darkmode.styl
+++ b/source/css/_mode/darkmode.styl
@@ -28,15 +28,7 @@ if hexo-config('darkmode.enable') || hexo-config('display_mode') == 'dark'
--blockquote-bg: lighten(#121212, 10)
--reward-pop: lighten(#121212, 10)
--toc-link-color: alpha(#FFFFFF, .6)
- --hl-color: alpha(#FFFFFF, .7)
- --hl-bg: lighten(#121212, 2)
- --hltools-bg: lighten(#121212, 3)
- --hltools-color: #90a4ae
- --hlnumber-bg: lighten(#121212, 2)
- --hlnumber-color: alpha(#FFFFFF, .4)
- --hlscrollbar-bg: lighten(#121212, 5)
- --hlexpand-bg: linear-gradient(180deg, rgba(lighten(#121212, 2), .6), rgba(lighten(#121212, 2), .9))
- --scrollbar-color: lighten(#121212, 5)
+ --scrollbar-color: lighten(#121212, 25)
--timeline-bg: lighten(#121212, 5)
--zoom-bg: #121212
--mark-bg: alpha($dark-black, .6)
@@ -78,11 +70,11 @@ if hexo-config('darkmode.enable') || hexo-config('display_mode') == 'dark'
box-shadow: 0 5px 6px -5px rgba(133, 133, 133, 0)
#post-comment
- #comment-switch
+ .comment-switch
if hexo-config('comments.text')
background: #2c2c2c !important
- .switch-btn
+ #switch-btn
filter: brightness(.8)
// note
diff --git a/source/css/_mode/readmode.styl b/source/css/_mode/readmode.styl
index ecb865a..906b50f 100644
--- a/source/css/_mode/readmode.styl
+++ b/source/css/_mode/readmode.styl
@@ -145,18 +145,7 @@ if hexo-config('readmode')
background: transparent
> .tab
- border-bottom: 0
-
- button
- border-top: none !important
- background: transparent
-
- &:hover
- background: none !important
-
- &.active
- button
- text-decoration: underline
+ border-top: none !important
> .tab-contents .tab-item-content.active
animation: none
diff --git a/source/css/_page/homepage.styl b/source/css/_page/homepage.styl
index 9269f97..e81311a 100644
--- a/source/css/_page/homepage.styl
+++ b/source/css/_page/homepage.styl
@@ -62,6 +62,11 @@
transition: all .2s ease-in-out
-webkit-line-clamp: 2
+ .sticky
+ margin-right: 10px
+ color: $sticky-color
+ transform: rotate(45deg)
+
+maxWidth768()
font-size: 1.43em
@@ -76,9 +81,6 @@
& > .post-meta-date
cursor: default
- .sticky
- color: $sticky-color
-
i
margin: 0 4px 0 0
diff --git a/source/css/_search/local-search.styl b/source/css/_search/local-search.styl
index f5c5fa9..c8dfdfe 100644
--- a/source/css/_search/local-search.styl
+++ b/source/css/_search/local-search.styl
@@ -64,10 +64,6 @@
+maxWidth768()
max-height: calc(var(--search-height) - 220px) !important
- .no-result
- & + #local-search-stats-wrap
- display: none
-
.search-keyword
background: transparent
color: $search-keyword-highlight
diff --git a/source/css/_tags/gallery.styl b/source/css/_tags/gallery.styl
index 6fe035f..2c0aa95 100644
--- a/source/css/_tags/gallery.styl
+++ b/source/css/_tags/gallery.styl
@@ -94,25 +94,20 @@
overflow: auto
padding: 0 0 16px
- .gallery
+ .gallery-container
margin: 0 0 16px
text-align: center
- .fj-gallery
- opacity: 0
+ img
+ display: initial
+ margin: 0
+ width: 100%
+ height: 100%
- .img-alt
- display: none
-
- &.lazyload
- + button
- display: inline-block
-
- .gallery-data
- display: none
+ .gallery-data
+ display: none
button
- display: none
margin-top: 25px
padding: 10px
width: 9em
@@ -123,17 +118,88 @@
font-size: 1.1em
transition: all .3s
- & > *
- transition: all .4s
-
- i
- width: 0
- opacity: 0
-
&:hover
background: var(--btn-hover-color)
- i
- margin-left: 2px
- width: 20px
- opacity: 1
+ .loading-container
+ display: inline-block
+ overflow: hidden
+ width: 154px
+ height: 154px
+
+ .loading-item
+ position: relative
+ width: 100%
+ height: 100%
+ backface-visibility: hidden
+ transform: translateZ(0) scale(1)
+ transform-origin: 0 0
+
+ div
+ position: absolute
+ width: 30.8px
+ height: 30.8px
+ border-radius: 50%
+ background: #e15b64
+ transform: translate(61.6px, 61.6px) scale(1)
+ animation: loading-ball 1.92s infinite cubic-bezier(0, .5, .5, 1)
+
+ &:nth-child(1)
+ background: #f47e60
+ transform: translate(113.96px, 61.6px) scale(1)
+ animation: loading-ball-r .48s infinite cubic-bezier(0, .5, .5, 1), loading-ball-c 1.92s infinite step-start
+
+ &:nth-child(2)
+ background: #e15b64
+ animation-delay: -.48s
+
+ &:nth-child(3)
+ background: #f47e60
+ animation-delay: -.96s
+
+ &:nth-child(4)
+ background: #f8b26a
+ animation-delay: -1.44s
+
+ &:nth-child(5)
+ background: #abbd81
+ animation-delay: -1.92s
+
+@keyframes loading-ball
+ 0%
+ transform: translate(9.24px, 61.6px) scale(0)
+
+ 25%
+ transform: translate(9.24px, 61.6px) scale(0)
+
+ 50%
+ transform: translate(9.24px, 61.6px) scale(1)
+
+ 75%
+ transform: translate(61.6px, 61.6px) scale(1)
+
+ 100%
+ transform: translate(113.96px, 61.6px) scale(1)
+
+@keyframes loading-ball-r
+ 0%
+ transform: translate(113.96px, 61.6px) scale(1)
+
+ 100%
+ transform: translate(113.96px, 61.6px) scale(0)
+
+@keyframes loading-ball-c
+ 0%
+ background: #e15b64
+
+ 25%
+ background: #abbd81
+
+ 50%
+ background: #f8b26a
+
+ 75%
+ background: #f47e60
+
+ 100%
+ background: #e15b64
diff --git a/source/css/_tags/note.styl b/source/css/_tags/note.styl
index efd3781..041603f 100644
--- a/source/css/_tags/note.styl
+++ b/source/css/_tags/note.styl
@@ -73,6 +73,9 @@
&:last-child
margin-bottom: 0 !important
+ .img-alt
+ margin: 5px 0 10px
+
if $note-icons
&:not(.no-icon)
padding-left: 3em
diff --git a/source/css/_tags/tabs.styl b/source/css/_tags/tabs.styl
index 90ede9c..2447a1b 100644
--- a/source/css/_tags/tabs.styl
+++ b/source/css/_tags/tabs.styl
@@ -15,43 +15,36 @@
background: var(--tab-botton-bg)
> .tab
- margin: 0
- padding: 0
- list-style: none
+ flex-grow: 1
+ padding: 8px 18px
+ border-top: 2px solid var(--tab-border-color)
+ background: var(--tab-botton-bg)
+ color: var(--tab-botton-color)
+ line-height: 2
+ transition: all .4s
- +maxWidth768()
- flex-grow: 1
-
- button
- display: block
- padding: 8px 18px
- width: 100%
- border-top: 2px solid var(--tab-border-color)
- background: var(--tab-botton-bg)
- color: var(--tab-botton-color)
- line-height: 2
- transition: all .4s
-
- i
- width: 1.5em
+ i
+ width: 1.5em
&.active
- button
- border-top: 2px solid $tab-active-border-color
- background: var(--tab-button-active-bg)
- cursor: default
+ border-top: 2px solid $tab-active-border-color
+ background: var(--tab-button-active-bg)
+ cursor: default
&:not(.active)
- button
- &:hover
- border-top: 2px solid var(--tab-button-hover-bg)
- background: var(--tab-button-hover-bg)
+ &:hover
+ border-top: 2px solid var(--tab-button-hover-bg)
+ background: var(--tab-button-hover-bg)
+
+ &.no-default
+ & ~ .tab-to-top
+ display: none
> .tab-contents
.tab-item-content
position: relative
display: none
- padding: 36px 24px
+ padding: 36px 24px 10px
+maxWidth768()
padding: 24px 14px
@@ -60,11 +53,19 @@
display: block
animation: tabshow .5s
- .tab-to-top
- position: relative
- display: block
- margin: 0 0 0 auto
- color: $tab-to-top-color
+ > :last-child
+ margin-bottom: 0
+
+ > .tab-to-top
+ padding: 0 16px 10px 0
+ width: 100%
+ text-align: right
+
+ button
+ color: $tab-to-top-color
+
+ &:hover
+ color: $tab-to-top-hover-color
@keyframes tabshow
0%
diff --git a/source/css/var.styl b/source/css/var.styl
index 04d4326..09e4129 100644
--- a/source/css/var.styl
+++ b/source/css/var.styl
@@ -101,7 +101,7 @@ $tag-hide-toggle-bg = #f0f0f0
$preloader-bg = #37474f
$preloader-word-color = #fff
// rightside
-$rightside-bottom = hexo-config('rightside-bottom') ? convert(hexo-config('rightside-bottom')) : 40px
+$rightside-bottom = hexo-config('rightside_bottom') ? convert(hexo-config('rightside_bottom')) : 40px
// fireworks
$fireworks-zIndex = hexo-config('fireworks.zIndex') ? hexo-config('fireworks.zIndex') : 99999
// Tag Plugins - Note
@@ -179,7 +179,6 @@ $tab-button-hover-bg = darken($tab-border-color, 8)
$tab-active-border-color = $theme-color
$tab-button-active-bg = $card-bg
$tab-to-top-color = #99a9bf
+$tab-to-top-hover-color = $theme-color
// Tag Plugins - timeline
$timeline-default-color = $theme-color
-// archor
-$archor-button-icon = hexo-config('anchor.button.icon') ? hexo-config('anchor.button.icon') : '\f0c1'
\ No newline at end of file
diff --git a/source/js/main.js b/source/js/main.js
index 303300d..a22b57f 100644
--- a/source/js/main.js
+++ b/source/js/main.js
@@ -4,9 +4,7 @@ document.addEventListener('DOMContentLoaded', function () {
const adjustMenu = init => {
const getAllWidth = ele => {
- let width = 0
- ele.length && Array.from(ele).forEach(i => { width += i.offsetWidth })
- return width
+ return Array.from(ele).reduce((width, i) => width + i.offsetWidth, 0)
}
if (init) {
@@ -16,15 +14,8 @@ document.addEventListener('DOMContentLoaded', function () {
$nav = document.getElementById('nav')
}
- let hideMenuIndex = ''
- if (window.innerWidth <= 768) hideMenuIndex = true
- else hideMenuIndex = headerContentWidth > $nav.offsetWidth - 120
-
- if (hideMenuIndex) {
- $nav.classList.add('hide-menu')
- } else {
- $nav.classList.remove('hide-menu')
- }
+ const hideMenuIndex = window.innerWidth <= 768 || headerContentWidth > $nav.offsetWidth - 120
+ $nav.classList.toggle('hide-menu', hideMenuIndex)
}
// 初始化header
@@ -56,17 +47,19 @@ document.addEventListener('DOMContentLoaded', function () {
* 首頁top_img底下的箭頭
*/
const scrollDownInIndex = () => {
- const $scrollDownEle = document.getElementById('scroll-down')
- $scrollDownEle && $scrollDownEle.addEventListener('click', function () {
+ const handleScrollToDest = () => {
btf.scrollToDest(document.getElementById('content-inner').offsetTop, 300)
- })
+ }
+
+ const $scrollDownEle = document.getElementById('scroll-down')
+ $scrollDownEle && btf.addEventListenerPjax($scrollDownEle, 'click', handleScrollToDest)
}
/**
* 代碼
* 只適用於Hexo默認的代碼渲染
*/
- const addHighlightTool = function () {
+ const addHighlightTool = () => {
const highLight = GLOBAL_CONFIG.highlight
if (!highLight) return
@@ -79,7 +72,7 @@ document.addEventListener('DOMContentLoaded', function () {
const isPrismjs = plugin === 'prismjs'
const highlightShrinkClass = isHighlightShrink === true ? 'closed' : ''
- const highlightShrinkEle = isHighlightShrink !== undefined ? `` : ''
+ const highlightShrinkEle = isHighlightShrink !== undefined ? '' : ''
const highlightCopyEle = highlightCopy ? '' : ''
const copy = (text, ctx) => {
@@ -103,7 +96,7 @@ document.addEventListener('DOMContentLoaded', function () {
}
// click events
- const highlightCopyFn = (ele) => {
+ const highlightCopyFn = ele => {
const $buttonParent = ele.parentNode
$buttonParent.classList.add('copy-true')
const selection = window.getSelection()
@@ -118,14 +111,8 @@ document.addEventListener('DOMContentLoaded', function () {
$buttonParent.classList.remove('copy-true')
}
- const highlightShrinkFn = (ele) => {
- const $nextEle = [...ele.parentNode.children].slice(1)
- ele.firstChild.classList.toggle('closed')
- if (btf.isHidden($nextEle[$nextEle.length - 1])) {
- $nextEle.forEach(e => { e.style.display = 'block' })
- } else {
- $nextEle.forEach(e => { e.style.display = 'none' })
- }
+ const highlightShrinkFn = ele => {
+ ele.classList.toggle('closed')
}
const highlightToolsFn = function (e) {
@@ -138,14 +125,14 @@ document.addEventListener('DOMContentLoaded', function () {
this.classList.toggle('expand-done')
}
- function createEle (lang, item, service) {
+ const createEle = (lang, item, service) => {
const fragment = document.createDocumentFragment()
if (isShowTool) {
const hlTools = document.createElement('div')
hlTools.className = `highlight-tools ${highlightShrinkClass}`
hlTools.innerHTML = highlightShrinkEle + lang + highlightCopyEle
- hlTools.addEventListener('click', highlightToolsFn)
+ btf.addEventListenerPjax(hlTools, 'click', highlightToolsFn)
fragment.appendChild(hlTools)
}
@@ -153,7 +140,7 @@ document.addEventListener('DOMContentLoaded', function () {
const ele = document.createElement('div')
ele.className = 'code-expand-btn'
ele.innerHTML = ''
- ele.addEventListener('click', expandCode)
+ btf.addEventListenerPjax(ele, 'click', expandCode)
fragment.appendChild(ele)
}
@@ -177,7 +164,7 @@ document.addEventListener('DOMContentLoaded', function () {
}
})
} else {
- $figureHighlight.forEach(function (item) {
+ $figureHighlight.forEach(item => {
if (highlightLang) {
let langName = item.getAttribute('class').split(' ')[1]
if (langName === 'plain' || langName === undefined) langName = 'Code'
@@ -193,16 +180,14 @@ document.addEventListener('DOMContentLoaded', function () {
/**
* PhotoFigcaption
*/
- function addPhotoFigcaption () {
- document.querySelectorAll('#article-container img').forEach(function (item) {
- const parentEle = item.parentNode
+ const addPhotoFigcaption = () => {
+ document.querySelectorAll('#article-container img').forEach(item => {
const altValue = item.title || item.alt
- if (altValue && !parentEle.parentNode.classList.contains('justified-gallery')) {
- const ele = document.createElement('div')
- ele.className = 'img-alt is-center'
- ele.textContent = altValue
- parentEle.insertBefore(ele, item.nextSibling)
- }
+ if (!altValue) return
+ const ele = document.createElement('div')
+ ele.className = 'img-alt is-center'
+ ele.textContent = altValue
+ item.insertAdjacentElement('afterend', ele)
})
}
@@ -216,97 +201,173 @@ document.addEventListener('DOMContentLoaded', function () {
/**
* justified-gallery 圖庫排版
*/
- const runJustifiedGallery = function (ele) {
- const htmlStr = arr => {
- let str = ''
- const replaceDq = str => str.replace(/"/g, '"') // replace double quotes to "
- arr.forEach(i => {
- const alt = i.alt ? `alt="${replaceDq(i.alt)}"` : ''
- const title = i.title ? `title="${replaceDq(i.title)}"` : ''
- str += ``
- })
- return str
+
+ const fetchUrl = async (url) => {
+ const response = await fetch(url)
+ return await response.json()
+ }
+
+ const runJustifiedGallery = (item, data, isButton = false, tabs) => {
+ const dataLength = data.length
+
+ const ig = new InfiniteGrid.JustifiedInfiniteGrid(item, {
+ gap: 5,
+ isConstantSize: true,
+ sizeRange: [150, 600],
+ useResizeObserver: true,
+ observeChildren: true,
+ useTransform: true
+ // useRecycle: false
+ })
+
+ if (tabs) {
+ btf.addGlobalFn('igOfTabs', () => { ig.destroy() }, false, tabs)
}
- const lazyloadFn = (i, arr, limit) => {
- const loadItem = limit
- const arrLength = arr.length
- if (arrLength > loadItem) i.insertAdjacentHTML('beforeend', htmlStr(arr.splice(0, loadItem)))
- else {
- i.insertAdjacentHTML('beforeend', htmlStr(arr))
- i.classList.remove('lazyload')
- }
- return arrLength > loadItem ? loadItem : arrLength
- }
+ const replaceDq = str => str.replace(/"/g, '"') // replace double quotes to "
- const fetchUrl = async (url) => {
- const response = await fetch(url)
- return await response.json()
- }
+ const getItems = (nextGroupKey, count) => {
+ const nextItems = []
+ const startCount = (nextGroupKey - 1) * count
- const runJustifiedGallery = (item, arr) => {
- if (!item.classList.contains('lazyload')) item.innerHTML = htmlStr(arr)
- else {
- const limit = item.getAttribute('data-limit')
- lazyloadFn(item, arr, limit)
- const clickBtnFn = () => {
- const lastItemLength = lazyloadFn(item, arr, limit)
- fjGallery(item, 'appendImages', item.querySelectorAll(`.fj-gallery-item:nth-last-child(-n+${lastItemLength})`))
- btf.loadLightbox(item.querySelectorAll('img'))
- lastItemLength < limit && item.nextElementSibling.removeEventListener('click', clickBtnFn)
+ for (let i = 0; i < count; ++i) {
+ const num = startCount + i
+ if (num >= dataLength) {
+ break
}
- item.nextElementSibling.addEventListener('click', clickBtnFn)
+
+ const item = data[num]
+ const alt = item.alt ? `alt="${replaceDq(item.alt)}"` : ''
+ const title = item.title ? `title="${replaceDq(item.title)}"` : ''
+
+ nextItems.push(`
+

+
`)
}
- btf.initJustifiedGallery(item)
- btf.loadLightbox(item.querySelectorAll('img'))
+ return nextItems
}
- const addJustifiedGallery = () => {
- ele.forEach(item => {
- item.classList.contains('url')
- ? fetchUrl(item.textContent).then(res => { runJustifiedGallery(item, res) })
- : runJustifiedGallery(item, JSON.parse(item.textContent))
- })
+ const buttonText = GLOBAL_CONFIG.infinitegrid.buttonText
+ const addButton = item => {
+ const button = document.createElement('button')
+ button.textContent = buttonText
+
+ const buttonFn = e => {
+ e.target.removeEventListener('click', buttonFn)
+ e.target.remove()
+ btf.setLoading.add(item)
+ appendItem(ig.getGroups().length + 1, 10)
+ }
+
+ button.addEventListener('click', buttonFn)
+ item.insertAdjacentElement('afterend', button)
}
- if (window.fjGallery) {
- addJustifiedGallery()
- return
+ const appendItem = (nextGroupKey, count) => {
+ ig.append(getItems(nextGroupKey, count), nextGroupKey)
}
- getCSS(`${GLOBAL_CONFIG.source.justifiedGallery.css}`)
- getScript(`${GLOBAL_CONFIG.source.justifiedGallery.js}`).then(addJustifiedGallery)
+ const maxGroupKey = Math.ceil(dataLength / 10)
+
+ const completeFn = e => {
+ const { updated, isResize, mounted } = e
+ if (!updated.length || !mounted.length || isResize) {
+ return
+ }
+
+ btf.loadLightbox(item.querySelectorAll('img:not(.medium-zoom-image)'))
+
+ if (ig.getGroups().length === maxGroupKey) {
+ btf.setLoading.remove(item)
+ ig.off('renderComplete', completeFn)
+ return
+ }
+
+ if (isButton) {
+ btf.setLoading.remove(item)
+ addButton(item)
+ }
+ }
+
+ const requestAppendFn = btf.debounce(e => {
+ const nextGroupKey = (+e.groupKey || 0) + 1
+ appendItem(nextGroupKey, 10)
+
+ if (nextGroupKey === maxGroupKey) {
+ ig.off('requestAppend', requestAppendFn)
+ }
+ }, 300)
+
+ btf.setLoading.add(item)
+ ig.on('renderComplete', completeFn)
+
+ if (isButton) {
+ appendItem(1, 10)
+ } else {
+ ig.on('requestAppend', requestAppendFn)
+ ig.renderItems()
+ }
+
+ btf.addGlobalFn('justifiedGallery', () => { ig.destroy() })
+ }
+
+ const addJustifiedGallery = async (ele, tabs = false) => {
+ const init = async () => {
+ for (const item of ele) {
+ if (btf.isHidden(item)) continue
+ if (tabs && item.classList.contains('loaded')) {
+ item.querySelector('.gallery-items').innerHTML = ''
+ const button = item.querySelector(':scope > button')
+ const loadingContainer = item.querySelector(':scope > .loading-container')
+ button && button.remove()
+ loadingContainer && loadingContainer.remove()
+ }
+
+ const isButton = item.getAttribute('data-button') === 'true'
+ const text = item.firstElementChild.textContent
+ item.classList.add('loaded')
+ const content = item.getAttribute('data-type') === 'url' ? await fetchUrl(text) : JSON.parse(text)
+ runJustifiedGallery(item.lastElementChild, content, isButton, tabs)
+ }
+ }
+
+ if (typeof InfiniteGrid === 'function') {
+ init()
+ } else {
+ await getScript(`${GLOBAL_CONFIG.infinitegrid.js}`)
+ init()
+ }
}
/**
* rightside scroll percent
*/
const rightsideScrollPercent = currentTop => {
- const perNum = btf.getScrollPercent(currentTop, document.body)
- const $goUp = document.getElementById('go-up')
- if (perNum < 95) {
- $goUp.classList.add('show-percent')
- $goUp.querySelector('.scroll-percent').textContent = perNum
+ const scrollPercent = btf.getScrollPercent(currentTop, document.body)
+ const goUpElement = document.getElementById('go-up')
+
+ if (scrollPercent < 95) {
+ goUpElement.classList.add('show-percent')
+ goUpElement.querySelector('.scroll-percent').textContent = scrollPercent
} else {
- $goUp.classList.remove('show-percent')
+ goUpElement.classList.remove('show-percent')
}
}
/**
* 滾動處理
*/
- const scrollFn = function () {
+ const scrollFn = () => {
const $rightside = document.getElementById('rightside')
const innerHeight = window.innerHeight + 56
let initTop = 0
- let isChatShow = true
const $header = document.getElementById('page-header')
const isChatBtn = typeof chatBtn !== 'undefined'
const isShowPercent = GLOBAL_CONFIG.percent.rightside
// 當滾動條小于 56 的時候
if (document.body.scrollHeight <= innerHeight) {
- $rightside.style.cssText = 'opacity: 1; transform: translateX(-58px)'
+ $rightside.classList.add('rightside-show')
return
}
@@ -317,50 +378,51 @@ document.addEventListener('DOMContentLoaded', function () {
return result
}
+ let flag = ''
const scrollTask = btf.throttle(() => {
const currentTop = window.scrollY || document.documentElement.scrollTop
const isDown = scrollDirection(currentTop)
if (currentTop > 56) {
+ if (flag === '') {
+ $header.classList.add('nav-fixed')
+ $rightside.classList.add('rightside-show')
+ }
+
if (isDown) {
- if ($header.classList.contains('nav-visible')) $header.classList.remove('nav-visible')
- if (isChatBtn && isChatShow === true) {
- window.chatBtn.hide()
- isChatShow = false
+ if (flag !== 'down') {
+ $header.classList.remove('nav-visible')
+ isChatBtn && window.chatBtn.hide()
+ flag = 'down'
}
} else {
- if (!$header.classList.contains('nav-visible')) $header.classList.add('nav-visible')
- if (isChatBtn && isChatShow === false) {
- window.chatBtn.show()
- isChatShow = true
+ if (flag !== 'up') {
+ $header.classList.add('nav-visible')
+ isChatBtn && window.chatBtn.show()
+ flag = 'up'
}
}
- $header.classList.add('nav-fixed')
- if (window.getComputedStyle($rightside).getPropertyValue('opacity') === '0') {
- $rightside.style.cssText = 'opacity: 0.8; transform: translateX(-58px)'
- }
} else {
+ flag = ''
if (currentTop === 0) {
$header.classList.remove('nav-fixed', 'nav-visible')
}
- $rightside.style.cssText = "opacity: ''; transform: ''"
+ $rightside.classList.remove('rightside-show')
}
isShowPercent && rightsideScrollPercent(currentTop)
if (document.body.scrollHeight <= innerHeight) {
- $rightside.style.cssText = 'opacity: 0.8; transform: translateX(-58px)'
+ $rightside.classList.add('rightside-show')
}
- }, 200)
+ }, 300)
- window.scrollCollect = scrollTask
-
- window.addEventListener('scroll', scrollCollect)
+ btf.addEventListenerPjax(window, 'scroll', scrollTask, { passive: true })
}
/**
* toc,anchor
*/
- const scrollFnToDo = function () {
+ const scrollFnToDo = () => {
const isToc = GLOBAL_CONFIG_SITE.isToc
const isAnchor = GLOBAL_CONFIG.isAnchor
const $article = document.getElementById('article-container')
@@ -371,37 +433,24 @@ document.addEventListener('DOMContentLoaded', function () {
if (isToc) {
const $cardTocLayout = document.getElementById('card-toc')
- $cardToc = $cardTocLayout.getElementsByClassName('toc-content')[0]
+ $cardToc = $cardTocLayout.querySelector('.toc-content')
$tocLink = $cardToc.querySelectorAll('.toc-link')
$tocPercentage = $cardTocLayout.querySelector('.toc-percentage')
isExpand = $cardToc.classList.contains('is-expand')
- window.mobileToc = {
- open: () => {
- $cardTocLayout.style.cssText = 'animation: toc-open .3s; opacity: 1; right: 55px'
- },
+ // toc元素點擊
+ const tocItemClickFn = e => {
+ const target = e.target.closest('.toc-link')
+ if (!target) return
- close: () => {
- $cardTocLayout.style.animation = 'toc-close .2s'
- setTimeout(() => {
- $cardTocLayout.style.cssText = "opacity:''; animation: ''; right: ''"
- }, 100)
+ e.preventDefault()
+ btf.scrollToDest(btf.getEleTop(document.getElementById(decodeURI(target.getAttribute('href')).replace('#', ''))), 300)
+ if (window.innerWidth < 900) {
+ $cardTocLayout.classList.remove('open')
}
}
- // toc元素點擊
- $cardToc.addEventListener('click', e => {
- e.preventDefault()
- const target = e.target.classList
- if (target.contains('toc-content')) return
- const $target = target.contains('toc-link')
- ? e.target
- : e.target.parentElement
- btf.scrollToDest(btf.getEleTop(document.getElementById(decodeURI($target.getAttribute('href')).replace('#', ''))), 300)
- if (window.innerWidth < 900) {
- window.mobileToc.close()
- }
- })
+ btf.addEventListenerPjax($cardToc, 'click', tocItemClickFn)
autoScrollToc = item => {
const activePosition = item.getBoundingClientRect().top
@@ -416,9 +465,9 @@ document.addEventListener('DOMContentLoaded', function () {
}
// find head position & add active class
- const list = $article.querySelectorAll('h1,h2,h3,h4,h5,h6')
+ const $articleList = $article.querySelectorAll('h1,h2,h3,h4,h5,h6')
let detectItem = ''
- const findHeadPosition = function (top) {
+ const findHeadPosition = top => {
if (top === 0) {
return false
}
@@ -426,7 +475,7 @@ document.addEventListener('DOMContentLoaded', function () {
let currentId = ''
let currentIndex = ''
- list.forEach(function (ele, index) {
+ $articleList.forEach((ele, index) => {
if (top > btf.getEleTop(ele) - 80) {
const id = ele.id
currentId = id ? '#' + encodeURI(id) : ''
@@ -464,7 +513,7 @@ document.addEventListener('DOMContentLoaded', function () {
}
// main of scroll
- window.tocScrollFn = btf.throttle(() => {
+ const tocScrollFn = btf.throttle(() => {
const currentTop = window.scrollY || document.documentElement.scrollTop
if (isToc && GLOBAL_CONFIG.percent.toc) {
$tocPercentage.textContent = btf.getScrollPercent(currentTop, $article)
@@ -472,21 +521,22 @@ document.addEventListener('DOMContentLoaded', function () {
findHeadPosition(currentTop)
}, 100)
- window.addEventListener('scroll', tocScrollFn)
+ btf.addEventListenerPjax(window, 'scroll', tocScrollFn, { passive: true })
}
- const modeChangeFn = mode => {
- if (!window.themeChange) {
+ const handleThemeChange = mode => {
+ const globalFn = window.globalFn || {}
+ const themeChange = globalFn.themeChange || {}
+ if (!themeChange) {
return
}
- const turnMode = item => window.themeChange[item](mode)
-
- Object.keys(window.themeChange).forEach(item => {
- if (['disqus', 'disqusjs'].includes(item)) {
- setTimeout(() => turnMode(item), 300)
+ Object.keys(themeChange).forEach(key => {
+ const themeChangeFn = themeChange[key]
+ if (['disqus', 'disqusjs'].includes(key)) {
+ setTimeout(() => themeChangeFn(mode), 300)
} else {
- turnMode(item)
+ themeChangeFn(mode)
}
})
}
@@ -495,7 +545,7 @@ document.addEventListener('DOMContentLoaded', function () {
* Rightside
*/
const rightSideFn = {
- switchReadMode: () => { // read-mode
+ readmode: () => { // read mode
const $body = document.body
$body.classList.add('read-mode')
const newEle = document.createElement('button')
@@ -511,74 +561,58 @@ document.addEventListener('DOMContentLoaded', function () {
newEle.addEventListener('click', clickFn)
},
- switchDarkMode: () => { // Switch Between Light And Dark Mode
+ darkmode: () => { // switch between light and dark mode
const willChangeMode = document.documentElement.getAttribute('data-theme') === 'dark' ? 'light' : 'dark'
if (willChangeMode === 'dark') {
activateDarkMode()
- saveToLocal.set('theme', 'dark', 2)
GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.day_to_night)
} else {
activateLightMode()
- saveToLocal.set('theme', 'light', 2)
GLOBAL_CONFIG.Snackbar !== undefined && btf.snackbarShow(GLOBAL_CONFIG.Snackbar.night_to_day)
}
- modeChangeFn(willChangeMode)
+ saveToLocal.set('theme', willChangeMode, 2)
+ handleThemeChange(willChangeMode)
},
- showOrHideBtn: (e) => { // rightside 點擊設置 按鈕 展開
- const rightsideHideClassList = document.getElementById('rightside-config-hide').classList
- rightsideHideClassList.toggle('show')
- if (e.classList.contains('show')) {
- rightsideHideClassList.add('status')
+ 'rightside-config': item => { // Show or hide rightside-hide-btn
+ const hideLayout = item.firstElementChild
+ if (hideLayout.classList.contains('show')) {
+ hideLayout.classList.add('status')
setTimeout(() => {
- rightsideHideClassList.remove('status')
+ hideLayout.classList.remove('status')
}, 300)
}
- e.classList.toggle('show')
+
+ hideLayout.classList.toggle('show')
},
- scrollToTop: () => { // Back to top
+ 'go-up': () => { // Back to top
btf.scrollToDest(0, 500)
},
- hideAsideBtn: () => { // Hide aside
+ 'hide-aside-btn': () => { // Hide aside
const $htmlDom = document.documentElement.classList
const saveStatus = $htmlDom.contains('hide-aside') ? 'show' : 'hide'
saveToLocal.set('aside-status', saveStatus, 2)
$htmlDom.toggle('hide-aside')
},
- runMobileToc: () => {
- if (window.getComputedStyle(document.getElementById('card-toc')).getPropertyValue('opacity') === '0') window.mobileToc.open()
- else window.mobileToc.close()
+ 'mobile-toc-button': item => { // Show mobile toc
+ const tocEle = document.getElementById('card-toc')
+ tocEle.style.transition = 'transform 0.3s ease-in-out'
+ tocEle.classList.toggle('open')
+ tocEle.addEventListener('transitionend', () => {
+ tocEle.style.transition = ''
+ }, { once: true })
},
- toggleChatDisplay: () => {
+ 'chat-btn': () => { // Show chat
window.chatBtnFn()
+ },
+ translateLink: () => { // switch between traditional and simplified chinese
+ window.translateFn.translatePage()
}
}
document.getElementById('rightside').addEventListener('click', function (e) {
- const $target = e.target.id ? e.target : e.target.parentNode
- switch ($target.id) {
- case 'go-up':
- rightSideFn.scrollToTop()
- break
- case 'rightside_config':
- rightSideFn.showOrHideBtn($target)
- break
- case 'mobile-toc-button':
- rightSideFn.runMobileToc()
- break
- case 'readmode':
- rightSideFn.switchReadMode()
- break
- case 'darkmode':
- rightSideFn.switchDarkMode()
- break
- case 'hide-aside-btn':
- rightSideFn.hideAsideBtn()
- break
- case 'chat-btn':
- rightSideFn.toggleChatDisplay()
- break
- default:
- break
+ const $target = e.target.closest('[id]')
+ if ($target && rightSideFn[$target.id]) {
+ rightSideFn[$target.id](this)
}
})
@@ -587,24 +621,35 @@ document.addEventListener('DOMContentLoaded', function () {
* 側邊欄sub-menu 展開/收縮
*/
const clickFnOfSubMenu = () => {
- document.querySelectorAll('#sidebar-menus .site-page.group').forEach(function (item) {
- item.addEventListener('click', function () {
- this.classList.toggle('hide')
- })
- })
+ const handleClickOfSubMenu = e => {
+ const target = e.target.closest('.site-page.group')
+ if (!target) return
+ target.classList.toggle('hide')
+ }
+
+ document.querySelector('#sidebar-menus .menus_items').addEventListener('click', handleClickOfSubMenu)
+ }
+
+ /**
+ * 手机端目录点击
+ */
+ const openMobileMenu = () => {
+ const handleClick = () => { sidebarFn.open() }
+ btf.addEventListenerPjax(document.getElementById('toggle-menu'), 'click', handleClick)
}
/**
* 複製時加上版權信息
*/
const addCopyright = () => {
- const copyright = GLOBAL_CONFIG.copyright
- document.body.oncopy = (e) => {
+ const { limitCount, languages } = GLOBAL_CONFIG.copyright
+
+ const handleCopy = (e) => {
e.preventDefault()
const copyFont = window.getSelection(0).toString()
let textFont = copyFont
- if (copyFont.length > copyright.limitCount) {
- textFont = `${copyFont}\n\n\n${copyright.languages.author}\n${copyright.languages.link}${window.location.href}\n${copyright.languages.source}\n${copyright.languages.info}`
+ if (copyFont.length > limitCount) {
+ textFont = `${copyFont}\n\n\n${languages.author}\n${languages.link}${window.location.href}\n${languages.source}\n${languages.info}`
}
if (e.clipboardData) {
return e.clipboardData.setData('text', textFont)
@@ -612,6 +657,8 @@ document.addEventListener('DOMContentLoaded', function () {
return window.clipboardData.setData('text', textFont)
}
}
+
+ document.body.addEventListener('copy', handleCopy)
}
/**
@@ -640,114 +687,119 @@ document.addEventListener('DOMContentLoaded', function () {
* table overflow
*/
const addTableWrap = () => {
- const $table = document.querySelectorAll('#article-container :not(.highlight) > table, #article-container > table')
- if ($table.length) {
- $table.forEach(item => {
+ const $table = document.querySelectorAll('#article-container table')
+ if (!$table.length) return
+
+ $table.forEach(item => {
+ if (!item.closest('.highlight')) {
btf.wrap(item, 'div', { class: 'table-wrap' })
- })
- }
+ }
+ })
}
/**
* tag-hide
*/
- const clickFnOfTagHide = function () {
- const $hideInline = document.querySelectorAll('#article-container .hide-button')
- if ($hideInline.length) {
- $hideInline.forEach(function (item) {
- item.addEventListener('click', function (e) {
- const $this = this
- $this.classList.add('open')
- const $fjGallery = $this.nextElementSibling.querySelectorAll('.fj-gallery')
- $fjGallery.length && btf.initJustifiedGallery($fjGallery)
- })
- })
+ const clickFnOfTagHide = () => {
+ const hideButtons = document.querySelectorAll('#article-container .hide-button')
+ if (!hideButtons.length) return
+ const handleClick = function (e) {
+ const $this = this
+ $this.classList.add('open')
+ const $fjGallery = $this.nextElementSibling.querySelectorAll('.gallery-container')
+ $fjGallery.length && addJustifiedGallery($fjGallery)
}
- }
- const tabsFn = {
- clickFnOfTabs: function () {
- document.querySelectorAll('#article-container .tab > button').forEach(function (item) {
- item.addEventListener('click', function (e) {
- const $this = this
- const $tabItem = $this.parentNode
-
- if (!$tabItem.classList.contains('active')) {
- const $tabContent = $tabItem.parentNode.nextElementSibling
- const $siblings = btf.siblings($tabItem, '.active')[0]
- $siblings && $siblings.classList.remove('active')
- $tabItem.classList.add('active')
- const tabId = $this.getAttribute('data-href').replace('#', '')
- const childList = [...$tabContent.children]
- childList.forEach(item => {
- if (item.id === tabId) item.classList.add('active')
- else item.classList.remove('active')
- })
- const $isTabJustifiedGallery = $tabContent.querySelectorAll(`#${tabId} .fj-gallery`)
- if ($isTabJustifiedGallery.length > 0) {
- btf.initJustifiedGallery($isTabJustifiedGallery)
- }
- }
- })
- })
- },
- backToTop: () => {
- document.querySelectorAll('#article-container .tabs .tab-to-top').forEach(function (item) {
- item.addEventListener('click', function () {
- btf.scrollToDest(btf.getEleTop(btf.getParents(this, '.tabs')), 300)
- })
- })
- }
- }
-
- const toggleCardCategory = function () {
- const $cardCategory = document.querySelectorAll('#aside-cat-list .card-category-list-item.parent i')
- if ($cardCategory.length) {
- $cardCategory.forEach(function (item) {
- item.addEventListener('click', function (e) {
- e.preventDefault()
- const $this = this
- $this.classList.toggle('expand')
- const $parentEle = $this.parentNode.nextElementSibling
- if (btf.isHidden($parentEle)) {
- $parentEle.style.display = 'block'
- } else {
- $parentEle.style.display = 'none'
- }
- })
- })
- }
- }
-
- const switchComments = function () {
- let switchDone = false
- const $switchBtn = document.querySelector('#comment-switch > .switch-btn')
- $switchBtn && $switchBtn.addEventListener('click', function () {
- this.classList.toggle('move')
- document.querySelectorAll('#post-comment > .comment-wrap > div').forEach(function (item) {
- if (btf.isHidden(item)) {
- item.style.cssText = 'display: block;animation: tabshow .5s'
- } else {
- item.style.cssText = "display: none;animation: ''"
- }
- })
-
- if (!switchDone && typeof loadOtherComment === 'function') {
- switchDone = true
- loadOtherComment()
- }
+ hideButtons.forEach(item => {
+ item.addEventListener('click', handleClick, { once: true })
})
}
- const addPostOutdateNotice = function () {
- const data = GLOBAL_CONFIG.noticeOutdate
+ const tabsFn = () => {
+ const navTabsElement = document.querySelectorAll('#article-container .tabs')
+ if (!navTabsElement.length) return
+
+ const removeAndAddActiveClass = (elements, detect) => {
+ Array.from(elements).forEach(element => {
+ element.classList.remove('active')
+ if (element === detect || element.id === detect) {
+ element.classList.add('active')
+ }
+ })
+ }
+
+ const addTabNavEventListener = (item, isJustifiedGallery) => {
+ const navClickHandler = function (e) {
+ const target = e.target.closest('button')
+ if (target.classList.contains('active')) return
+ removeAndAddActiveClass(this.children, target)
+ this.classList.remove('no-default')
+ const tabId = target.getAttribute('data-href')
+ const tabContent = this.nextElementSibling
+ removeAndAddActiveClass(tabContent.children, tabId)
+ if (isJustifiedGallery) {
+ btf.removeGlobalFnEvent('igOfTabs', this)
+ const justifiedGalleryItems = tabContent.querySelectorAll(`:scope > #${tabId} .gallery-container`)
+ justifiedGalleryItems.length && addJustifiedGallery(justifiedGalleryItems, this)
+ }
+ }
+ btf.addEventListenerPjax(item.firstElementChild, 'click', navClickHandler)
+ }
+
+ const addTabToTopEventListener = item => {
+ const btnClickHandler = (e) => {
+ const target = e.target.closest('button')
+ if (!target) return
+ btf.scrollToDest(btf.getEleTop(item), 300)
+ }
+ btf.addEventListenerPjax(item.lastElementChild, 'click', btnClickHandler)
+ }
+
+ navTabsElement.forEach(item => {
+ const isJustifiedGallery = !!item.querySelectorAll('.gallery-container')
+ addTabNavEventListener(item, isJustifiedGallery)
+ addTabToTopEventListener(item)
+ })
+ }
+
+ const toggleCardCategory = () => {
+ const cardCategory = document.querySelector('#aside-cat-list.expandBtn')
+ if (!cardCategory) return
+
+ const handleToggleBtn = (e) => {
+ const target = e.target
+ if (target.nodeName === 'I') {
+ e.preventDefault()
+ target.parentNode.classList.toggle('expand')
+ }
+ }
+ btf.addEventListenerPjax(cardCategory, 'click', handleToggleBtn, true)
+ }
+
+ const switchComments = () => {
+ const switchBtn = document.getElementById('switch-btn')
+ if (!switchBtn) return
+ let switchDone = false
+ const commentContainer = document.getElementById('post-comment')
+ const handleSwitchBtn = () => {
+ commentContainer.classList.toggle('move')
+ if (!switchDone) {
+ switchDone = true
+ loadOtherComment()
+ }
+ }
+ btf.addEventListenerPjax(switchBtn, 'click', handleSwitchBtn)
+ }
+
+ const addPostOutdateNotice = () => {
+ const { limitDay, messagePrev, messageNext, position } = GLOBAL_CONFIG.noticeOutdate
const diffDay = btf.diffDate(GLOBAL_CONFIG_SITE.postUpdate)
- if (diffDay >= data.limitDay) {
+ if (diffDay >= limitDay) {
const ele = document.createElement('div')
ele.className = 'post-outdate-notice'
- ele.textContent = data.messagePrev + ' ' + diffDay + ' ' + data.messageNext
+ ele.textContent = `${messagePrev} ${diffDay} ${messageNext}`
const $targetEle = document.getElementById('article-container')
- if (data.position === 'top') {
+ if (position === 'top') {
$targetEle.insertBefore(ele, $targetEle.firstChild)
} else {
$targetEle.appendChild(ele)
@@ -774,7 +826,7 @@ document.addEventListener('DOMContentLoaded', function () {
const unRefreshFn = function () {
window.addEventListener('resize', () => {
adjustMenu(false)
- btf.isHidden(document.getElementById('toggle-menu')) && mobileSidebarOpen && sidebarFn.close()
+ mobileSidebarOpen && btf.isHidden(document.getElementById('toggle-menu')) && sidebarFn.close()
})
document.getElementById('menu-mask').addEventListener('click', e => { sidebarFn.close() })
@@ -786,7 +838,7 @@ document.addEventListener('DOMContentLoaded', function () {
if (GLOBAL_CONFIG.autoDarkmode) {
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
if (saveToLocal.get('theme') !== undefined) return
- e.matches ? modeChangeFn('dark') : modeChangeFn('light')
+ e.matches ? handleThemeChange('dark') : handleThemeChange('light')
})
}
}
@@ -810,16 +862,16 @@ document.addEventListener('DOMContentLoaded', function () {
GLOBAL_CONFIG.isPhotoFigcaption && addPhotoFigcaption()
scrollFn()
- const $jgEle = document.querySelectorAll('#article-container .fj-gallery')
- $jgEle.length && runJustifiedGallery($jgEle)
+ btf.removeGlobalFnEvent('justifiedGallery')
+ const galleryContainer = document.querySelectorAll('#article-container .gallery-container')
+ galleryContainer.length && addJustifiedGallery(galleryContainer)
runLightbox()
addTableWrap()
clickFnOfTagHide()
- tabsFn.clickFnOfTabs()
- tabsFn.backToTop()
+ tabsFn()
switchComments()
- document.getElementById('toggle-menu').addEventListener('click', () => { sidebarFn.open() })
+ openMobileMenu()
}
refreshFn()
diff --git a/source/js/search/algolia.js b/source/js/search/algolia.js
index 9ac9f94..9ce7b0e 100644
--- a/source/js/search/algolia.js
+++ b/source/js/search/algolia.js
@@ -39,7 +39,7 @@ window.addEventListener('load', () => {
}
const searchClickFn = () => {
- document.querySelector('#search-button > .search').addEventListener('click', openSearch)
+ btf.addEventListenerPjax(document.querySelector('#search-button > .search'), 'click', openSearch)
}
const searchFnOnce = () => {
diff --git a/source/js/search/local-search.js b/source/js/search/local-search.js
index 47cf84d..0eecff6 100644
--- a/source/js/search/local-search.js
+++ b/source/js/search/local-search.js
@@ -246,10 +246,12 @@ window.addEventListener('load', () => {
const input = document.querySelector('#local-search-input input')
const statsItem = document.getElementById('local-search-stats-wrap')
const $loadingStatus = document.getElementById('loading-status')
+ const isXml = !path.endsWith('json')
const inputEventFunction = () => {
if (!localSearch.isfetched) return
- const searchText = input.value.trim().toLowerCase()
+ let searchText = input.value.trim().toLowerCase()
+ isXml && (searchText = searchText.replace(//g, '>'))
if (searchText !== '') $loadingStatus.innerHTML = ''
const keywords = searchText.split(/[-\s]+/)
const container = document.getElementById('local-search-results')
@@ -259,11 +261,14 @@ window.addEventListener('load', () => {
resultItems = localSearch.getResultItems(keywords)
}
if (keywords.length === 1 && keywords[0] === '') {
- container.classList.add('no-result')
container.textContent = ''
+ statsItem.textContent = ''
} else if (resultItems.length === 0) {
container.textContent = ''
- statsItem.innerHTML = `${languages.hits_empty.replace(/\$\{query}/, searchText)}
`
+ const statsDiv = document.createElement('div')
+ statsDiv.className = 'search-result-stats'
+ statsDiv.textContent = languages.hits_empty.replace(/\$\{query}/, searchText)
+ statsItem.innerHTML = statsDiv.outerHTML
} else {
resultItems.sort((left, right) => {
if (left.includedCount !== right.includedCount) {
@@ -276,7 +281,6 @@ window.addEventListener('load', () => {
const stats = languages.hits_stats.replace(/\$\{hits}/, resultItems.length)
- container.classList.remove('no-result')
container.innerHTML = `${resultItems.map(result => result.item).join('')}
`
statsItem.innerHTML = `
${stats}
`
window.pjax && window.pjax.refresh(container)
@@ -330,7 +334,7 @@ window.addEventListener('load', () => {
}
const searchClickFn = () => {
- document.querySelector('#search-button > .search').addEventListener('click', openSearch)
+ btf.addEventListenerPjax(document.querySelector('#search-button > .search'), 'click', openSearch)
}
const searchFnOnce = () => {
diff --git a/source/js/tw_cn.js b/source/js/tw_cn.js
index c27db8f..15d8d15 100644
--- a/source/js/tw_cn.js
+++ b/source/js/tw_cn.js
@@ -21,6 +21,7 @@ document.addEventListener('DOMContentLoaded', function () {
return Traditionalized(txt)
} else return txt
}
+
function translateBody (fobj) {
let objs
if (typeof fobj === 'object') objs = fobj.childNodes
@@ -107,9 +108,15 @@ document.addEventListener('DOMContentLoaded', function () {
setLang()
setTimeout(translateBody, translateDelay)
}
- translateButtonObject.addEventListener('click', translatePage, false)
}
}
+
+ window.translateFn = {
+ translatePage,
+ Traditionalized,
+ Simplized
+ }
+
translateInitialization()
document.addEventListener('pjax:complete', translateInitialization)
})
diff --git a/source/js/utils.js b/source/js/utils.js
index adff39c..2c8242c 100644
--- a/source/js/utils.js
+++ b/source/js/utils.js
@@ -1,38 +1,35 @@
const btf = {
- debounce: function (func, wait, immediate) {
+ debounce: (func, wait = 0, immediate = false) => {
let timeout
- return function () {
- const context = this
- const args = arguments
- const later = function () {
+ return (...args) => {
+ const later = () => {
timeout = null
- if (!immediate) func.apply(context, args)
+ if (!immediate) func(...args)
}
const callNow = immediate && !timeout
clearTimeout(timeout)
timeout = setTimeout(later, wait)
- if (callNow) func.apply(context, args)
+ if (callNow) func(...args)
}
},
- throttle: function (func, wait, options) {
+ throttle: function (func, wait, options = {}) {
let timeout, context, args
let previous = 0
- if (!options) options = {}
- const later = function () {
+ const later = () => {
previous = options.leading === false ? 0 : new Date().getTime()
timeout = null
func.apply(context, args)
if (!timeout) context = args = null
}
- const throttled = function () {
+ const throttled = (...params) => {
const now = new Date().getTime()
if (!previous && options.leading === false) previous = now
const remaining = wait - (now - previous)
context = this
- args = arguments
+ args = params
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout)
@@ -155,27 +152,6 @@ const btf = {
ele.style.animation = text
},
- getParents: (elem, selector) => {
- for (; elem && elem !== document; elem = elem.parentNode) {
- if (elem.matches(selector)) return elem
- }
- return null
- },
-
- siblings: (ele, selector) => {
- return [...ele.parentNode.children].filter((child) => {
- if (selector) {
- return child !== ele && child.matches(selector)
- }
- return child !== ele
- })
- },
-
- /**
- * @param {*} selector
- * @param {*} eleType the type of create element
- * @param {*} options object key: value
- */
wrap: (selector, eleType, options) => {
const createEle = document.createElement(eleType)
for (const [key, value] of Object.entries(options)) {
@@ -185,13 +161,6 @@ const btf = {
createEle.appendChild(selector)
},
- unwrap: el => {
- const parent = el.parentNode
- if (parent && parent !== document.body) {
- parent.replaceChild(el, parent)
- }
- },
-
isHidden: ele => ele.offsetHeight === 0 && ele.offsetWidth === 0,
getEleTop: ele => {
@@ -214,7 +183,7 @@ const btf = {
}
if (service === 'fancybox') {
- ele.forEach(i => {
+ Array.from(ele).forEach(i => {
if (i.parentNode.tagName !== 'A') {
const dataSrc = i.dataset.lazySrc || i.src
const dataCaption = i.title || i.alt || ''
@@ -257,22 +226,20 @@ const btf = {
}
},
- initJustifiedGallery: function (selector) {
- const runJustifiedGallery = i => {
- if (!btf.isHidden(i)) {
- fjGallery(i, {
- itemSelector: '.fj-gallery-item',
- rowHeight: i.getAttribute('data-rowHeight'),
- gutter: 4,
- onJustify: function () {
- this.$container.style.opacity = '1'
- }
- })
- }
+ setLoading: {
+ add: ele => {
+ const html = `
+
+ `
+ ele.insertAdjacentHTML('afterend', html)
+ },
+ remove: ele => {
+ ele.nextElementSibling.remove()
}
-
- if (Array.from(selector).length === 0) runJustifiedGallery(selector)
- else selector.forEach(i => { runJustifiedGallery(i) })
},
updateAnchor: (anchor) => {
@@ -297,11 +264,33 @@ const btf = {
return percentage
},
- addModeChange: (name, fn) => {
- if (window.themeChange && window.themeChange[name]) return
- window.themeChange = {
- ...window.themeChange,
- [name]: fn
- }
+ addGlobalFn: (key, fn, name = false, parent = window) => {
+ const globalFn = parent.globalFn || {}
+ const keyObj = globalFn[key] || {}
+
+ if (name && keyObj[name]) return
+
+ name = name || Object.keys(keyObj).length
+ keyObj[name] = fn
+ globalFn[key] = keyObj
+ parent.globalFn = globalFn
+ },
+
+ addEventListenerPjax: (ele, event, fn, option = false) => {
+ ele.addEventListener(event, fn, option)
+ btf.addGlobalFn('pjax', () => {
+ ele.removeEventListener(event, fn, option)
+ })
+ },
+
+ removeGlobalFnEvent: (key, parent = window) => {
+ const { globalFn = {} } = parent
+ const keyObj = globalFn[key] || {}
+ const keyArr = Object.keys(keyObj)
+ if (!keyArr.length) return
+ keyArr.forEach(i => {
+ keyObj[i]()
+ })
+ delete parent.globalFn[key]
}
}