feat: 替換 Justified Gallery 為 flickr-justified-gallery

feat: 完全移除 jquery
feat: 清除配置文件的CDN, 默認 CDN 不再顯示在 config
feat: 切換夜間模式後, mermaid 也會切換夜間主題
feat: 移除騰訊分析
feat: 移除右下角字體調整按鈕
feat: 本地搜索可處理 json
feat: 右下角按鈕自定義順序
feat: 右小角按鈕 UI 微調
improvement: 手機端更改閲讀模式退出按鈕到右下角
improvement: photofigcaption 和 fancybox 的 figcaption 優先顯示 圖片的title屬性,然後是 alt 屬性
improvement: 首頁ui微調
improvement: 禁止一些瀏覽器會出現點擊左下角按鈕出現放大網頁的行為
improvement: js 優化
fix: 修復窗口大小改變時,導航欄的ui 可能會錯亂的 bug
fix: 修復 pjax 下, twikoo 評論獲取是上一篇評論的 bug
fix: 壓縮 html 代碼後, mermaid 顯示正常
This commit is contained in:
Jerry
2021-10-12 23:27:56 +08:00
parent 7e729cff79
commit cb82bfb7b6
34 changed files with 611 additions and 570 deletions

View File

@@ -24,7 +24,7 @@ Based on [hexo-theme-melody](https://github.com/Molunerfinn/hexo-theme-melody) t
### GIT ### GIT
> If you are in Mainland China, you can download in [Gitee](https://gitee.com/iamjerryw/hexo-theme-butterfly) > If you are in Mainland China, you can download in [Gitee](https://gitee.com/immyw/hexo-theme-butterfly.git)
Stable branch [recommend]: Stable branch [recommend]:

View File

@@ -24,7 +24,7 @@
### Git 安裝 ### Git 安裝
> 本倉庫同時上傳到 [Gitee](https://gitee.com/iamjerryw/hexo-theme-butterfly),如果你訪問 Github 緩慢,可從 Gitee 中下載。 > 本倉庫同時上傳到 [Gitee](https://gitee.com/immyw/hexo-theme-butterfly.git),如果你訪問 Github 緩慢,可從 Gitee 中下載。
在博客根目錄裡安裝穩定版【推薦】 在博客根目錄裡安裝穩定版【推薦】

View File

@@ -401,10 +401,6 @@ baidu_analytics:
# https://analytics.google.com/analytics/web/ # https://analytics.google.com/analytics/web/
google_analytics: google_analytics:
# Tencent Analytics ID
# https://mta.qq.com
tencent_analytics:
# CNZZ Analytics # CNZZ Analytics
# https://www.umeng.com/ # https://www.umeng.com/
cnzz_analytics: cnzz_analytics:
@@ -673,9 +669,6 @@ newest_comments:
# Bottom right button (右下角按鈕) # Bottom right button (右下角按鈕)
# -------------------------------------- # --------------------------------------
# Change font size
change_font_size: false
# Conversion between Traditional and Simplified Chinese (簡繁轉換) # Conversion between Traditional and Simplified Chinese (簡繁轉換)
translate: translate:
enable: false enable: false
@@ -704,6 +697,14 @@ darkmode:
# autoChangeMode: false # autoChangeMode: false
autoChangeMode: false autoChangeMode: false
# Don't modify the following settings unless you know how they work (非必要請不要修改 )
# Choose: readmode,translate,darkmode,hideAside,toc,chat,comment
# Don't repeat 不要重複
rightside_item_order:
enable: false
hide: # readmode,translate,darkmode,hideAside
show: # toc,chat,comment
# Lightbox (圖片大圖查看模式) # Lightbox (圖片大圖查看模式)
# -------------------------------------- # --------------------------------------
# You can only choose one, or neither (只能選擇一個 或者 兩個都不選) # You can only choose one, or neither (只能選擇一個 或者 兩個都不選)
@@ -724,7 +725,9 @@ fancybox: true
mermaid: mermaid:
enable: false enable: false
# built-in themes: default/forest/dark/neutral # built-in themes: default/forest/dark/neutral
theme: default theme:
light: default
dark: dark
# Note (Bootstrap Callout) # Note (Bootstrap Callout)
note: note:
@@ -817,86 +820,85 @@ inject:
# 非必要請不要修改 # 非必要請不要修改
CDN: CDN:
# main # main
main_css: /css/index.css main_css:
jquery: https://cdn.jsdelivr.net/npm/jquery@latest/dist/jquery.min.js main:
main: /js/main.js utils:
utils: /js/utils.js
# pjax # pjax
pjax: https://cdn.jsdelivr.net/npm/pjax/pjax.min.js pjax:
# comments # comments
gitalk: https://cdn.jsdelivr.net/npm/gitalk@latest/dist/gitalk.min.js gitalk:
gitalk_css: https://cdn.jsdelivr.net/npm/gitalk/dist/gitalk.min.css gitalk_css:
valine: https://cdn.jsdelivr.net/npm/valine/dist/Valine.min.js valine:
disqusjs: https://cdn.jsdelivr.net/npm/disqusjs@1/dist/disqus.js disqusjs:
disqusjs_css: https://cdn.jsdelivr.net/npm/disqusjs@1/dist/disqusjs.css disqusjs_css:
utterances: https://utteranc.es/client.js utterances:
twikoo: https://cdn.jsdelivr.net/npm/twikoo/dist/twikoo.all.min.js twikoo:
waline: https://cdn.jsdelivr.net/npm/@waline/client/dist/Waline.min.js waline:
# share # share
addtoany: https://static.addtoany.com/menu/page.js addtoany:
sharejs: https://cdn.jsdelivr.net/npm/social-share.js/dist/js/social-share.min.js sharejs:
sharejs_css: https://cdn.jsdelivr.net/npm/social-share.js/dist/css/share.min.css sharejs_css:
# search # search
local_search: /js/search/local-search.js local_search:
algolia_js: /js/search/algolia.js algolia_js:
algolia_search: https://cdn.jsdelivr.net/npm/instantsearch.js@2.10.5/dist/instantsearch.min.js algolia_search:
algolia_search_css: https://cdn.jsdelivr.net/npm/instantsearch.js@2.10.5/dist/instantsearch.min.css algolia_search_css:
# math # math
mathjax: https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js mathjax:
katex: https://cdn.jsdelivr.net/npm/katex@latest/dist/katex.min.css katex:
katex_copytex: https://cdn.jsdelivr.net/npm/katex@latest/dist/contrib/copy-tex.min.js katex_copytex:
katex_copytex_css: https://cdn.jsdelivr.net/npm/katex@latest/dist/contrib/copy-tex.css katex_copytex_css:
mermaid: https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js mermaid:
# count # count
busuanzi: //busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js busuanzi:
# background effect # background effect
canvas_ribbon: https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-ribbon.min.js canvas_ribbon:
canvas_fluttering_ribbon: https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-fluttering-ribbon.min.js canvas_fluttering_ribbon:
canvas_nest: https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-nest.min.js canvas_nest:
lazyload: https://cdn.jsdelivr.net/npm/vanilla-lazyload/dist/lazyload.iife.min.js lazyload:
instantpage: https://cdn.jsdelivr.net/npm/instant.page/instantpage.min.js instantpage:
typed: https://cdn.jsdelivr.net/npm/typed.js/lib/typed.min.js typed:
pangu: https://cdn.jsdelivr.net/npm/pangu/dist/browser/pangu.min.js pangu:
# photo # photo
fancybox_css: https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.css fancybox_css:
fancybox: https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.js fancybox:
medium_zoom: https://cdn.jsdelivr.net/npm/medium-zoom/dist/medium-zoom.min.js medium_zoom:
# snackbar # snackbar
snackbar_css: https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.css snackbar_css:
snackbar: https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.js snackbar:
# effect # effect
activate_power_mode: https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/activate-power-mode.min.js activate_power_mode:
fireworks: https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/fireworks.min.js fireworks:
click_heart: https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/click-heart.min.js click_heart:
ClickShowText: https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/click-show-text.min.js ClickShowText:
# fontawesome # fontawesome
fontawesome: https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css fontawesome:
# Conversion between Traditional and Simplified Chinese # Conversion between Traditional and Simplified Chinese
translate: /js/tw_cn.js translate:
# justifiedGallery # flickr-justified-gallery
justifiedGallery_js: https://cdn.jsdelivr.net/npm/justifiedGallery/dist/js/jquery.justifiedGallery.min.js flickr_justified_gallery_js:
justifiedGallery_css: https://cdn.jsdelivr.net/npm/justifiedGallery/dist/css/justifiedGallery.min.css flickr_justified_gallery_css:
# aplayer # aplayer
aplayer_css: https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css aplayer_css:
aplayer_js: https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js aplayer_js:
meting_js: https://cdn.jsdelivr.net/gh/metowolf/MetingJS@1.2/dist/Meting.min.js meting_js:
# Prism.js # Prism.js
prismjs_js: https://cdn.jsdelivr.net/npm/prismjs/prism.min.js prismjs_js:
prismjs_lineNumber_js: https://cdn.jsdelivr.net/npm/prismjs/plugins/line-numbers/prism-line-numbers.min.js prismjs_lineNumber_js:
prismjs_autoloader: https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js prismjs_autoloader:

View File

@@ -94,8 +94,6 @@ share: Share
rightside: rightside:
readmode_title: Read Mode readmode_title: Read Mode
font_plus_title: Increase font size
font_minus_title: Decrease font size
translate_title: Toggle Between Traditional Chinese And Simplified Chinese translate_title: Toggle Between Traditional Chinese And Simplified Chinese
night_mode_title: Toggle Between Light And Dark Mode night_mode_title: Toggle Between Light And Dark Mode
back_to_top: Back To Top back_to_top: Back To Top

View File

@@ -94,8 +94,6 @@ share: Share
rightside: rightside:
readmode_title: Read Mode readmode_title: Read Mode
font_plus_title: Increase font size
font_minus_title: Decrease font size
translate_title: Switch Between Traditional Chinese And Simplified Chinese translate_title: Switch Between Traditional Chinese And Simplified Chinese
night_mode_title: Switch Between Light And Dark Mode night_mode_title: Switch Between Light And Dark Mode
back_to_top: Back To Top back_to_top: Back To Top

View File

@@ -95,8 +95,6 @@ share: 分享
rightside: rightside:
readmode_title: 阅读模式 readmode_title: 阅读模式
font_plus_title: 放大字体
font_minus_title: 缩小字体
translate_title: 简繁转换 translate_title: 简繁转换
night_mode_title: 浅色和深色模式转换 night_mode_title: 浅色和深色模式转换
back_to_top: 回到顶部 back_to_top: 回到顶部

View File

@@ -95,8 +95,6 @@ share: 分享
rightside: rightside:
readmode_title: 閱讀模式 readmode_title: 閱讀模式
font_plus_title: 放大字型
font_minus_title: 縮小字型
translate_title: 簡繁轉換 translate_title: 簡繁轉換
night_mode_title: 淺色和深色模式轉換 night_mode_title: 淺色和深色模式轉換
back_to_top: 回到頂部 back_to_top: 回到頂部

View File

@@ -20,7 +20,7 @@
meta(charset='UTF-8') meta(charset='UTF-8')
meta(http-equiv="X-UA-Compatible" content="IE=edge") meta(http-equiv="X-UA-Compatible" content="IE=edge")
meta(name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no") meta(name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no")
title= tabTitle title= tabTitle
if pageKeywords if pageKeywords
meta(name="keywords" content=pageKeywords) meta(name="keywords" content=pageKeywords)

View File

@@ -16,18 +16,6 @@ if theme.google_analytics
gtag('js', new Date()); gtag('js', new Date());
gtag('config', '!{theme.google_analytics}'); gtag('config', '!{theme.google_analytics}');
if theme.tencent_analytics
script.
var _mtac = {};
(function() {
var mta = document.createElement("script");
mta.src = "//pingjs.qq.com/h5/stats.js?v2.0.4";
mta.setAttribute("name", "MTAH5");
mta.setAttribute("sid", "!{theme.tencent_analytics}");
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(mta, s);
})();
if theme.cnzz_analytics if theme.cnzz_analytics
script(async data-pjax src=`https://s4.cnzz.com/z_stat.php?id=${theme.cnzz_analytics}&web_id=${theme.cnzz_analytics}`) script(async data-pjax src=`https://s4.cnzz.com/z_stat.php?id=${theme.cnzz_analytics}&web_id=${theme.cnzz_analytics}`)

View File

@@ -112,10 +112,9 @@ script.
lightbox: '!{ theme.medium_zoom ? "mediumZoom" : (theme.fancybox ? "fancybox" : "null" )}', lightbox: '!{ theme.medium_zoom ? "mediumZoom" : (theme.fancybox ? "fancybox" : "null" )}',
Snackbar: !{Snackbar}, Snackbar: !{Snackbar},
source: { source: {
jQuery: '!{url_for(theme.CDN.jquery)}',
justifiedGallery: { justifiedGallery: {
js: '!{url_for(theme.CDN.justifiedGallery_js)}', js: '!{url_for(theme.CDN.flickr_justified_gallery_js)}',
css: '!{url_for(theme.CDN.justifiedGallery_css)}' css: '!{url_for(theme.CDN.flickr_justified_gallery_css)}'
} }
}, },
isPhotoFigcaption: !{theme.photofigcaption}, isPhotoFigcaption: !{theme.photofigcaption},

View File

@@ -6,9 +6,6 @@ if theme.google_analytics
if theme.baidu_analytics if theme.baidu_analytics
link(rel="preconnect" href="//hm.baidu.com") link(rel="preconnect" href="//hm.baidu.com")
if theme.tencent_analytics
link(rel="preconnect" href="//pingjs.qq.com")
if theme.cnzz_analytics if theme.cnzz_analytics
link(rel="preconnect" href="//s4.cnzz.com") link(rel="preconnect" href="//s4.cnzz.com")

View File

@@ -43,25 +43,25 @@ mixin postUI(posts)
time(datetime=date_xml(article[date_type]) title=date_title + ' ' + full_date(article[date_type]))=date(article[date_type], config.date_format) time(datetime=date_xml(article[date_type]) title=date_title + ' ' + full_date(article[date_type]))=date(article[date_type], config.date_format)
if (theme.post_meta.page.categories && article.categories.data.length > 0) if (theme.post_meta.page.categories && article.categories.data.length > 0)
span.article-meta span.article-meta
span.article-meta__separator | span.article-meta-separator |
i.fas.fa-inbox i.fas.fa-inbox
each item, index in article.categories.data each item, index in article.categories.data
a(href=url_for(item.path)).article-meta__categories #[=item.name] a(href=url_for(item.path)).article-meta__categories #[=item.name]
if (index < article.categories.data.length - 1) if (index < article.categories.data.length - 1)
i.fas.fa-angle-right i.fas.fa-angle-right.article-meta-link
if (theme.post_meta.page.tags && article.tags.data.length > 0) if (theme.post_meta.page.tags && article.tags.data.length > 0)
span.article-meta.tags span.article-meta.tags
span.article-meta__separator | span.article-meta-separator |
i.fas.fa-tag i.fas.fa-tag
each item, index in article.tags.data each item, index in article.tags.data
a(href=url_for(item.path)).article-meta__tags #[=item.name] a(href=url_for(item.path)).article-meta__tags #[=item.name]
if (index < article.tags.data.length - 1) if (index < article.tags.data.length - 1)
span.article-meta__link #[='•'] span.article-meta-link #[='•']
mixin countBlockInIndex mixin countBlockInIndex
- needLoadCountJs = true - needLoadCountJs = true
span.article-meta span.article-meta
span.article-meta__separator | span.article-meta-separator |
i.fas.fa-comments i.fas.fa-comments
if block if block
block block

View File

@@ -1,38 +1,60 @@
- const { readmode, translate, darkmode, aside, chat_btn } = theme
mixin rightsideItem(array)
each item in array
case item
when 'readmode'
if is_post() && readmode
button#readmode(type="button" title=_p('rightside.readmode_title'))
i.fas.fa-book-open
when 'translate'
if translate.enable
button#translateLink(type="button" title=_p('rightside.translate_title'))= translate.default
when 'darkmode'
if darkmode.enable && darkmode.button
button#darkmode(type="button" title=_p('rightside.night_mode_title'))
i.fas.fa-adjust
when 'hideAside'
if aside.enable && aside.button && page.aside !== false
button#hide-aside-btn(type="button" title=_p('rightside.aside'))
i.fas.fa-arrows-alt-h
when 'toc'
if showToc
button#mobile-toc-button.close(type="button" title=_p("rightside.toc"))
i.fas.fa-list-ul
when 'chat'
if chat_btn
button#chat_btn(type="button" title=_p("rightside.chat_btn"))
i.fas.fa-sms
when 'comment'
if commentsJsLoad
a#to_comment(href="#post-comment" title=_p("rightside.scroll_to_comment"))
i.fas.fa-comments
#rightside #rightside
- const { enable, hide, show } = theme.rightside_item_order
- const hideArray = enable ? hide && hide.split(',') : ['readmode','translate','darkmode','hideAside']
- const showArray = enable ? show && show.split(',') : ['toc','chat','comment']
#rightside-config-hide #rightside-config-hide
if is_post() && theme.readmode if hideArray
button#readmode(type="button" title=_p('rightside.readmode_title')) +rightsideItem(hideArray)
i.fas.fa-book-open
if theme.change_font_size
button#font-plus(type="button" title=_p('rightside.font_plus_title'))
i.fas.fa-plus
button#font-minus(type="button" title=_p('rightside.font_minus_title'))
i.fas.fa-minus
if theme.translate.enable
button#translateLink(type="button" title=_p('rightside.translate_title'))= theme.translate.default
if theme.darkmode.enable && theme.darkmode.button
button#darkmode(type="button" title=_p('rightside.night_mode_title'))
i.fas.fa-adjust
if theme.aside.enable && theme.aside.button && page.aside !== false
button#hide-aside-btn(type="button" title=_p('rightside.aside'))
i.fas.fa-arrows-alt-h
#rightside-config-show #rightside-config-show
if is_post() if enable
if (theme.readmode || theme.translate.enable || (theme.darkmode.enable && theme.darkmode.button) || theme.change_font_size) if hide
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"))
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 i.fas.fa-cog.fa-spin
else if theme.translate.enable || (theme.darkmode.enable && theme.darkmode.button) || theme.change_font_size
button#rightside_config(type="button" title=_p("rightside.setting"))
i.fas.fa-cog.fa-spin
if showToc if showArray
button#mobile-toc-button.close(type="button" title=_p("rightside.toc")) +rightsideItem(showArray)
i.fas.fa-list-ul
if theme.chat_btn
button#chat_btn(type="button" title=_p("rightside.chat_btn"))
i.fas.fa-sms
if commentsJsLoad
a#to_comment(href="#post-comment" title=_p("rightside.scroll_to_comment"))
i.fas.fa-comments
button#go-up(type="button" title=_p("rightside.back_to_top")) button#go-up(type="button" title=_p("rightside.back_to_top"))
i.fas.fa-arrow-up i.fas.fa-arrow-up

View File

@@ -1,45 +1,48 @@
- const { envId, region, option } = theme.twikoo
- const { use, lazyload, count } = theme.comments
script. script.
(()=>{ (()=>{
const $countDom = document.getElementById('twikoo-count')
const init = () => { const init = () => {
twikoo.init(Object.assign({ twikoo.init(Object.assign({
el: '#twikoo-wrap', el: '#twikoo-wrap',
envId: '!{theme.twikoo.envId}', envId: '!{envId}',
region: '!{theme.twikoo.region}', region: '!{region}',
onCommentLoaded: function () { onCommentLoaded: function () {
btf.loadLightbox(document.querySelectorAll('#twikoo .tk-content img:not(.vemoji)')) btf.loadLightbox(document.querySelectorAll('#twikoo .tk-content img:not(.vemoji)'))
} }
}, !{JSON.stringify(theme.twikoo.option)})) }, !{JSON.stringify(option)}))
} }
const getCount = () => { const getCount = () => {
twikoo.getCommentsCount({ twikoo.getCommentsCount({
envId: '!{theme.twikoo.envId}', envId: '!{envId}',
region: '!{theme.twikoo.region}', region: '!{region}',
urls: [window.location.pathname], urls: [window.location.pathname],
includeReply: false includeReply: false
}).then(function (res) { }).then(function (res) {
$countDom.innerText = res[0].count document.getElementById('twikoo-count').innerText = res[0].count
}).catch(function (err) { }).catch(function (err) {
console.error(err); console.error(err);
}); });
} }
const loadTwikoo = (bool = false) => { const runFn = () => {
if (typeof twikoo === 'object') { init()
init() !{count ? 'GLOBAL_CONFIG_SITE.isPost && getCount()' : ''}
bool && $countDom && setTimeout(getCount,0)
} else {
getScript('!{theme.CDN.twikoo}').then(()=> {
init()
bool && $countDom && setTimeout(getCount,0)
})
}
} }
if ('!{theme.comments.use[0]}' === 'Twikoo' || !!{theme.comments.lazyload}) { const loadTwikoo = () => {
if (!{theme.comments.lazyload}) btf.loadComment(document.getElementById('twikoo-wrap'), loadTwikoo) if (typeof twikoo === 'object') {
else loadTwikoo(true) setTimeout(runFn,0)
return
}
getScript('!{theme.CDN.twikoo}').then(runFn)
}
if ('!{use[0]}' === 'Twikoo' || !!{lazyload}) {
if (!{lazyload}) btf.loadComment(document.getElementById('twikoo-wrap'), loadTwikoo)
else loadTwikoo()
} else { } else {
window.loadOtherComment = () => { window.loadOtherComment = () => {
loadTwikoo() loadTwikoo()

View File

@@ -1,15 +1,25 @@
script. script.
if (document.getElementsByClassName('mermaid').length) { (() => {
if (window.mermaidJsLoad) mermaid.init() const $mermaidWrap = document.querySelectorAll('#article-container .mermaid-wrap')
else { if ($mermaidWrap.length) {
getScript('!{theme.CDN.mermaid}').then(() => { window.runMermaid = () => {
window.mermaidJsLoad = true const theme = document.documentElement.getAttribute('data-theme') === 'dark' ? '!{theme.mermaid.theme.dark}' : '!{theme.mermaid.theme.light}'
mermaid.initialize({
theme: '!{theme.mermaid.theme}', Array.from($mermaidWrap).forEach((item, index) => {
const mermaidSrc = item.firstElementChild
const mermaidThemeConfig = '%%{init:{ \'theme\':\'' + theme + '\'}}%%\n'
const mermaidID = 'mermaid-' + index
const mermaidDefinition = mermaidThemeConfig + mermaidSrc.textContent
mermaid.mermaidAPI.render(mermaidID, mermaidDefinition, (svgCode) => {
mermaidSrc.insertAdjacentHTML('afterend', svgCode)
})
}) })
!{theme.pjax.enable} && mermaid.init() }
})
const loadMermaid = () => {
window.mermaid ? runMermaid() : getScript('!{theme.CDN.mermaid}').then(runMermaid)
}
window.pjax ? loadMermaid() : document.addEventListener('DOMContentLoaded', loadMermaid)
} }
} })()

View File

@@ -74,11 +74,6 @@ script.
typeof loadMeting === 'function' && document.getElementsByClassName('aplayer').length && loadMeting() typeof loadMeting === 'function' && document.getElementsByClassName('aplayer').length && loadMeting()
// Analytics
if (!{theme.tencent_analytics ? true : false}) {
MtaH5.pgv()
}
// prismjs // prismjs
typeof Prism === 'object' && Prism.highlightAll() typeof Prism === 'object' && Prism.highlightAll()

View File

@@ -1,6 +1,6 @@
{ {
"name": "hexo-theme-butterfly", "name": "hexo-theme-butterfly",
"version": "4.0.0-b7", "version": "4.0.0-b8",
"description": "A Simple and Card UI Design theme for Hexo", "description": "A Simple and Card UI Design theme for Hexo",
"main": "package.json", "main": "package.json",
"scripts": { "scripts": {

104
scripts/events/cdn.js Normal file
View File

@@ -0,0 +1,104 @@
/**
* Butterfly
* CDN
*/
'use strict'
hexo.extend.filter.register('before_generate', () => {
const themeConfig = hexo.theme.config
const defaultCDN = {
main_css: '/css/index.css',
main: '/js/main.js',
utils: '/js/utils.js',
// pjax
pjax: 'https://cdn.jsdelivr.net/npm/pjax/pjax.min.js',
// comments
gitalk: 'https://cdn.jsdelivr.net/npm/gitalk@latest/dist/gitalk.min.js',
gitalk_css: 'https://cdn.jsdelivr.net/npm/gitalk/dist/gitalk.min.css',
valine: 'https://cdn.jsdelivr.net/npm/valine/dist/Valine.min.js',
disqusjs: 'https://cdn.jsdelivr.net/npm/disqusjs@1/dist/disqus.js',
disqusjs_css: 'https://cdn.jsdelivr.net/npm/disqusjs@1/dist/disqusjs.css',
utterances: 'https://utteranc.es/client.js',
twikoo: 'https://cdn.jsdelivr.net/npm/twikoo/dist/twikoo.all.min.js',
waline: 'https://cdn.jsdelivr.net/npm/@waline/client/dist/Waline.min.js',
// share
addtoany: 'https://static.addtoany.com/menu/page.js',
sharejs: 'https://cdn.jsdelivr.net/npm/social-share.js/dist/js/social-share.min.js',
sharejs_css: 'https://cdn.jsdelivr.net/npm/social-share.js/dist/css/share.min.css',
// search
local_search: '/js/search/local-search.js',
algolia_js: '/js/search/algolia.js',
algolia_search: 'https://cdn.jsdelivr.net/npm/instantsearch.js@2.10.5/dist/instantsearch.min.js',
algolia_search_css: 'https://cdn.jsdelivr.net/npm/instantsearch.js@2.10.5/dist/instantsearch.min.css',
// math
mathjax: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js',
katex: 'https://cdn.jsdelivr.net/npm/katex@latest/dist/katex.min.css',
katex_copytex: 'https://cdn.jsdelivr.net/npm/katex@latest/dist/contrib/copy-tex.min.js',
katex_copytex_css: 'https://cdn.jsdelivr.net/npm/katex@latest/dist/contrib/copy-tex.css',
mermaid: 'https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js',
// count
busuanzi: '//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js',
// background effect
canvas_ribbon: 'https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-ribbon.min.js',
canvas_fluttering_ribbon: 'https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-fluttering-ribbon.min.js',
canvas_nest: 'https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/canvas-nest.min.js',
lazyload: 'https://cdn.jsdelivr.net/npm/vanilla-lazyload/dist/lazyload.iife.min.js',
instantpage: 'https://cdn.jsdelivr.net/npm/instant.page/instantpage.min.js',
typed: 'https://cdn.jsdelivr.net/npm/typed.js/lib/typed.min.js',
pangu: 'https://cdn.jsdelivr.net/npm/pangu/dist/browser/pangu.min.js',
// photo
fancybox_css: 'https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.css',
fancybox: 'https://cdn.jsdelivr.net/npm/@fancyapps/ui/dist/fancybox.umd.js',
medium_zoom: 'https://cdn.jsdelivr.net/npm/medium-zoom/dist/medium-zoom.min.js',
// snackbar
snackbar_css: 'https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.css',
snackbar: 'https://cdn.jsdelivr.net/npm/node-snackbar/dist/snackbar.min.js',
// effect
activate_power_mode: 'https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/activate-power-mode.min.js',
fireworks: 'https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/fireworks.min.js',
click_heart: 'https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/click-heart.min.js',
ClickShowText: 'https://cdn.jsdelivr.net/npm/butterfly-extsrc@1/dist/click-show-text.min.js',
// fontawesome
fontawesome: 'https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free/css/all.min.css',
// Conversion between Traditional and Simplified Chinese
translate: '/js/tw_cn.js',
// flickr-justified-gallery
flickr_justified_gallery_js: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.js',
flickr_justified_gallery_css: 'https://cdn.jsdelivr.net/npm/flickr-justified-gallery@2/dist/fjGallery.min.css',
// aplayer
aplayer_css: 'https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.css',
aplayer_js: 'https://cdn.jsdelivr.net/npm/aplayer/dist/APlayer.min.js',
meting_js: 'https://cdn.jsdelivr.net/gh/metowolf/MetingJS@1.2/dist/Meting.min.js',
// Prism.js
prismjs_js: 'https://cdn.jsdelivr.net/npm/prismjs/prism.min.js',
prismjs_lineNumber_js: 'https://cdn.jsdelivr.net/npm/prismjs/plugins/line-numbers/prism-line-numbers.min.js',
prismjs_autoloader: 'https://cdn.jsdelivr.net/npm/prismjs/plugins/autoloader/prism-autoloader.min.js'
}
// delete null value
const deleteNullValue = obj => {
for (const i in obj) {
obj[i] === null && delete obj[i]
}
return obj
}
themeConfig.CDN = Object.assign(defaultCDN, deleteNullValue(themeConfig.CDN))
})

View File

@@ -6,7 +6,7 @@
'use strict' 'use strict'
hexo.extend.helper.register('inject_head_js', function () { hexo.extend.helper.register('inject_head_js', function () {
const { darkmode, aside, change_font_size, index_img, disable_top_img, pjax} = this.theme const { darkmode, aside } = this.theme
const localStore = ` const localStore = `
win.saveToLocal = { win.saveToLocal = {
@@ -131,31 +131,14 @@ hexo.extend.helper.register('inject_head_js', function () {
` `
} }
let changFontAside = '' const detectApple = `
if (change_font_size) {
changFontAside = `
const fontSizeVal = saveToLocal.get('global-font-size')
if (fontSizeVal !== undefined) {
document.documentElement.style.setProperty('--global-font-size', fontSizeVal + 'px')
}
`
}
let detectApple = ''
if (!disable_top_img || index_img !== false) {
detectApple = `
const detectApple = () => { const detectApple = () => {
if (GLOBAL_CONFIG_SITE.isHome && /iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){ if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
document.documentElement.classList.add('apple') document.documentElement.classList.add('apple')
} }
} }
detectApple() detectApple()
` `
if (pjax.enable) { return `<script>(win=>{${localStore + getScript + darkmodeJs + asideStatus + detectApple}})(window)</script>`
detectApple += 'document.addEventListener(\'pjax:complete\', detectApple)'
}
}
return `<script>(win=>{${localStore + getScript + darkmodeJs + asideStatus + changFontAside + detectApple}})(window)</script>`
}) })

View File

@@ -8,7 +8,7 @@
const urlFor = require('hexo-util').url_for.bind(hexo) const urlFor = require('hexo-util').url_for.bind(hexo)
function gallery (args, content) { function gallery (args, content) {
return `<div class="justified-gallery">${hexo.render.renderSync({ text: content, engine: 'markdown' }).split('\n').join('')} return `<div class="fj-gallery">${hexo.render.renderSync({ text: content, engine: 'markdown' }).split('\n').join('')}
</div>` </div>`
} }

View File

@@ -9,7 +9,9 @@
const { escapeHTML } = require('hexo-util') const { escapeHTML } = require('hexo-util')
function mermaid (args, content) { function mermaid (args, content) {
return `<div class="mermaid">${escapeHTML(content)}</div>` return `<div class="mermaid-wrap"><pre class="mermaid-src" hidden>
${escapeHTML(content)}
</pre></div>`
} }
hexo.extend.tag.register('mermaid', mermaid, { ends: true }) hexo.extend.tag.register('mermaid', mermaid, { ends: true })

View File

@@ -246,8 +246,8 @@
max-height: calc(100% - 60px) max-height: calc(100% - 60px)
width: $toc-mobile-width width: $toc-mobile-width
opacity: 0 opacity: 0
transition: initial
transform-origin: right bottom transform-origin: right bottom
transition: none
.toc-percentage .toc-percentage
float: right float: right
@@ -317,8 +317,9 @@
border-radius: 70px border-radius: 70px
img img
width: 100%
height: 100% height: 100%
transition: filter 375ms ease-in 0.2s, transform .3s transition: filter 375ms ease-in .2s, transform .3s
object-fit: cover object-fit: cover
&:hover &:hover

View File

@@ -239,7 +239,6 @@
top: 0 top: 0
z-index: 90 z-index: 90
display: flex display: flex
flex-wrap: wrap
align-items: center align-items: center
padding: 0 36px padding: 0 36px
width: 100% width: 100%
@@ -335,13 +334,10 @@
font-size: inherit font-size: inherit
.menus_items .menus_items
position: absolute display: none
left: 0
visibility: hidden
opacity: 0
#search-button span #search-button span
display: none !important display: none
#search-button #search-button
display: inline display: inline

View File

@@ -1,6 +1,6 @@
#rightside #rightside
position: fixed position: fixed
right: -38px right: -48px
bottom: $rightside-bottom bottom: $rightside-bottom
z-index: 100 z-index: 100
opacity: 0 opacity: 0
@@ -8,7 +8,7 @@
#rightside-config-hide #rightside-config-hide
transition: transform .4s transition: transform .4s
transform: translate(35px, 0) transform: translate(48px, 0)
&.show &.show
transform: translate(0, 0) !important transform: translate(0, 0) !important
@@ -17,17 +17,25 @@
& > button, & > button,
& > a & > a
display: block display: block
margin-bottom: 2px margin-bottom: 5px
width: 30px width: w = 38px
height: 30px height: w
border-radius: 5px
background-color: var(--btn-bg) background-color: var(--btn-bg)
color: var(--btn-color) color: var(--btn-color)
text-align: center text-align: center
font-size: 16px font-size: 16px
line-height: w
touch-action: manipulation
&:hover &:hover
background-color: var(--btn-hover-color) background-color: var(--btn-hover-color)
+maxWidth768()
width: w = 34px
height: w
line-height: w
#mobile-toc-button #mobile-toc-button
display: none display: none

View File

@@ -61,16 +61,9 @@ if hexo-config('waline.bg')
.medium-zoom-overlay .medium-zoom-overlay
z-index: 99999 !important z-index: 99999 !important
.mermaid .mermaid-wrap
overflow: auto
margin: 0 0 20px margin: 0 0 20px
background: $white
text-align: center text-align: center
opacity: 0
transition: all .3s
&[data-processed]
opacity: 1
.utterances, .utterances,
.fb-comments iframe .fb-comments iframe

View File

@@ -91,7 +91,6 @@ if hexo-config('darkmode.enable') || hexo-config('display_mode') == 'dark'
// hide-tags // hide-tags
.hide-button, .hide-button,
.btn-beautify, .btn-beautify,
.mermaid,
.hl-label, .hl-label,
.post-outdate-notice, .post-outdate-notice,
.error-img, .error-img,

View File

@@ -36,6 +36,7 @@ if hexo-config('readmode')
position: fixed position: fixed
top: 30px top: 30px
right: 30px right: 30px
z-index: 100
width: 40px width: 40px
height: 40px height: 40px
border-radius: 8px border-radius: 8px
@@ -44,6 +45,10 @@ if hexo-config('readmode')
font-size: 16px font-size: 16px
transition: background .3s transition: background .3s
+maxWidth768()
top: initial
bottom: 30px
&:hover &:hover
background: var(--exit-btn-hover) background: var(--exit-btn-hover)

View File

@@ -54,5 +54,7 @@
#page-header.full_page #page-header.full_page
background-attachment: scroll !important background-attachment: scroll !important
.recent-post-item .recent-post-item,
.avatar-img,
.flink-item-icon
transform: translateZ(0) transform: translateZ(0)

View File

@@ -8,7 +8,7 @@
flex-direction: row flex-direction: row
align-items: center align-items: center
overflow: hidden overflow: hidden
height: 20em height: 18em
+maxWidth768() +maxWidth768()
flex-direction: column flex-direction: column
@@ -24,7 +24,7 @@
.post_cover .post_cover
overflow: hidden overflow: hidden
width: 45% width: 44%
height: 100% height: 100%
+maxWidth768() +maxWidth768()
@@ -42,7 +42,7 @@
& >.recent-post-info & >.recent-post-info
padding: 0 40px padding: 0 40px
width: 55% width: 57%
+maxWidth768() +maxWidth768()
padding: 20px 20px 30px padding: 20px 20px 30px
@@ -88,13 +88,10 @@
else else
display: none display: none
.article-meta__separator .article-meta-separator
margin: 0 6px margin: 0 6px
.article-meta__link .article-meta-link
margin: 0 4px
.fa-angle-right
margin: 0 4px margin: 0 4px
if hexo-config('post_meta.page.date_format') == 'relative' if hexo-config('post_meta.page.date_format') == 'relative'
@@ -110,4 +107,4 @@
& > .content & > .content
@extend .limit-more-line @extend .limit-more-line
-webkit-line-clamp: 3 -webkit-line-clamp: 2

View File

@@ -1,107 +1,102 @@
figure.gallery-group #article-container
position: relative figure.gallery-group
float: left
overflow: hidden
margin: 6px 4px
width: calc(50% - 8px)
height: 250px
border-radius: 8px
background: $dark-black
-webkit-transform: translate3d(0, 0, 0)
+maxWidth600()
width: calc(100% - 8px)
&:hover
img
opacity: .4
transform: translate3d(0, 0, 0)
.gallery-group-name::after
transform: translate3d(0, 0, 0)
p
opacity: 1
transform: translate3d(0, 0, 0)
img
position: relative position: relative
margin: 0 !important float: left
max-width: none !important overflow: hidden
width: calc(100% + 20px) margin: 6px 4px
width: calc(50% - 8px)
height: 250px height: 250px
backface-visibility: hidden border-radius: 8px
opacity: .8 background: $dark-black
transition: all .3s, filter 375ms ease-in 0.2s !important -webkit-transform: translate3d(0, 0, 0)
transform: translate3d(-10px, 0, 0)
object-fit: cover
figcaption +maxWidth600()
position: absolute width: calc(100% - 8px)
top: 0
left: 0
padding: 30px
width: 100%
height: 100%
color: $gallery-color
text-transform: uppercase
backface-visibility: hidden
& > a &:hover
img
opacity: .4
transform: translate3d(0, 0, 0)
.gallery-group-name::after
transform: translate3d(0, 0, 0)
p
opacity: 1
transform: translate3d(0, 0, 0)
img
position: relative
margin: 0
max-width: none
width: calc(100% + 20px)
height: 250px
backface-visibility: hidden
opacity: .8
transition: all .3s, filter 375ms ease-in .2s
transform: translate3d(-10px, 0, 0)
object-fit: cover
figcaption
position: absolute position: absolute
top: 0 top: 0
right: 0
bottom: 0
left: 0
z-index: 1000
opacity: 0
p
@extend .limit-more-line
margin: 0
padding: 8px 0 0
letter-spacing: 1px
font-size: 1.1em
line-height: 1.5
opacity: 0
transition: opacity .35s, transform .35s
transform: translate3d(100%, 0, 0)
-webkit-line-clamp: 4
.gallery-group-name
@extend .limit-more-line
position: relative
margin: 0
padding: 8px 0
font-weight: bold
font-size: 1.65em
line-height: 1.5
-webkit-line-clamp: 2
&:after
position: absolute
bottom: 0
left: 0 left: 0
padding: 30px
width: 100% width: 100%
height: 2px height: 100%
background: $gallery-color color: $gallery-color
content: '' text-transform: uppercase
transition: transform .35s backface-visibility: hidden
transform: translate3d(-100%, 0, 0)
.gallery-group-main & > a
overflow: auto position: absolute
padding: 0 0 16px top: 0
right: 0
bottom: 0
left: 0
z-index: 1000
opacity: 0
.justified-gallery p
margin: 0 0 16px @extend .limit-more-line
margin: 0
padding: 8px 0 0
letter-spacing: 1px
font-size: 1.1em
line-height: 1.5
opacity: 0
transition: opacity .35s, transform .35s
transform: translate3d(100%, 0, 0)
-webkit-line-clamp: 4
img .gallery-group-name
@extend .limit-more-line
position: relative
margin: 0
padding: 8px 0
font-weight: bold
font-size: 1.65em
line-height: 1.5
-webkit-line-clamp: 2
&:after
position: absolute
bottom: 0
left: 0
width: 100%
height: 2px
background: $gallery-color
content: ''
transition: transform .35s
transform: translate3d(-100%, 0, 0)
.gallery-group-main
overflow: auto
padding: 0 0 16px
.fj-gallery
margin: 0 0 16px
opacity: 0 opacity: 0
.img-alt .img-alt
display: none display: none
.fancybox
width: auto
text-align: inherit

View File

@@ -1,23 +1,21 @@
document.addEventListener('DOMContentLoaded', function () { document.addEventListener('DOMContentLoaded', function () {
const $blogName = document.getElementById('site-name') let blogNameWidth, menusWidth, searchWidth, $nav, hideMenuIndex, mobileSidebarOpen
let blogNameWidth = $blogName && $blogName.offsetWidth
const $menusEle = document.querySelector('#menus .menus_items')
let menusWidth = $menusEle && $menusEle.offsetWidth
const $searchEle = document.querySelector('#search-button')
let searchWidth = $searchEle && $searchEle.offsetWidth
const adjustMenu = (change = false) => { const adjustMenu = (init) => {
if (change) { if (init) {
blogNameWidth = $blogName && $blogName.offsetWidth blogNameWidth = document.getElementById('site-name').offsetWidth
menusWidth = $menusEle && $menusEle.offsetWidth const $menusEle = document.querySelectorAll('#menus .menus_item')
searchWidth = $searchEle && $searchEle.offsetWidth menusWidth = 0
$menusEle.length && $menusEle.forEach(i => { menusWidth += i.offsetWidth })
const $searchEle = document.querySelector('#search-button')
searchWidth = $searchEle ? $searchEle.offsetWidth : 0
$nav = document.getElementById('nav')
} }
const $nav = document.getElementById('nav')
let t
if (window.innerWidth < 768) t = true
else t = blogNameWidth + menusWidth + searchWidth > $nav.offsetWidth - 120
if (t) { if (window.innerWidth < 768) hideMenuIndex = true
else hideMenuIndex = blogNameWidth + menusWidth + searchWidth > $nav.offsetWidth - 120
if (hideMenuIndex) {
$nav.classList.add('hide-menu') $nav.classList.add('hide-menu')
} else { } else {
$nav.classList.remove('hide-menu') $nav.classList.remove('hide-menu')
@@ -26,44 +24,26 @@ document.addEventListener('DOMContentLoaded', function () {
// 初始化header // 初始化header
const initAdjust = () => { const initAdjust = () => {
adjustMenu() adjustMenu(true)
document.getElementById('nav').classList.add('show') $nav.classList.add('show')
} }
// sidebar menus // sidebar menus
const sidebarFn = () => { const sidebarFn = {
const $toggleMenu = document.getElementById('toggle-menu') open: () => {
const $mobileSidebarMenus = document.getElementById('sidebar-menus')
const $menuMask = document.getElementById('menu-mask')
const $body = document.body
function openMobileSidebar () {
btf.sidebarPaddingR() btf.sidebarPaddingR()
$body.style.overflow = 'hidden' document.body.style.overflow = 'hidden'
btf.fadeIn($menuMask, 0.5) btf.fadeIn(document.getElementById('menu-mask'), 0.5)
$mobileSidebarMenus.classList.add('open') document.getElementById('sidebar-menus').classList.add('open')
} mobileSidebarOpen = true
},
function closeMobileSidebar () { close: () => {
const $body = document.body
$body.style.overflow = '' $body.style.overflow = ''
$body.style.paddingRight = '' $body.style.paddingRight = ''
btf.fadeOut($menuMask, 0.5) btf.fadeOut(document.getElementById('menu-mask'), 0.5)
$mobileSidebarMenus.classList.remove('open') document.getElementById('sidebar-menus').classList.remove('open')
} }
$toggleMenu.addEventListener('click', openMobileSidebar)
$menuMask.addEventListener('click', e => {
if ($mobileSidebarMenus.classList.contains('open')) {
closeMobileSidebar()
}
})
window.addEventListener('resize', e => {
if (btf.isHidden($toggleMenu)) {
if ($mobileSidebarMenus.classList.contains('open')) closeMobileSidebar()
}
})
} }
/** /**
@@ -225,7 +205,7 @@ document.addEventListener('DOMContentLoaded', function () {
function addPhotoFigcaption () { function addPhotoFigcaption () {
document.querySelectorAll('#article-container img').forEach(function (item) { document.querySelectorAll('#article-container img').forEach(function (item) {
const parentEle = item.parentNode const parentEle = item.parentNode
const altValue = item.alt const altValue = item.title || item.alt
if (altValue && !parentEle.parentNode.classList.contains('justified-gallery')) { if (altValue && !parentEle.parentNode.classList.contains('justified-gallery')) {
const ele = document.createElement('div') const ele = document.createElement('div')
ele.className = 'img-alt is-center' ele.className = 'img-alt is-center'
@@ -237,7 +217,6 @@ document.addEventListener('DOMContentLoaded', function () {
/** /**
* Lightbox * Lightbox
* It needs to call it after the Justified Gallery done, or the fancybox maybe not work
*/ */
const runLightbox = () => { const runLightbox = () => {
btf.loadLightbox(document.querySelectorAll('#article-container img:not(.no-lightbox)')) btf.loadLightbox(document.querySelectorAll('#article-container img:not(.no-lightbox)'))
@@ -245,41 +224,28 @@ document.addEventListener('DOMContentLoaded', function () {
/** /**
* justified-gallery 圖庫排版 * justified-gallery 圖庫排版
* 需要 jQuery
*/ */
let detectJgJsLoad = false
const runJustifiedGallery = function (ele) { const runJustifiedGallery = function (ele) {
const $justifiedGallery = $(ele) ele.forEach(item => {
const $imgList = $justifiedGallery.find('img') const $imgList = item.querySelectorAll('img')
$imgList.unwrap() // remove <p> tag
if ($imgList.length) { $imgList.forEach(i => {
$imgList.each(function (i, o) { const dataLazySrc = i.getAttribute('data-lazy-src')
if ($(o).attr('data-lazy-src')) $(o).attr('src', $(o).attr('data-lazy-src')) if (dataLazySrc) i.src = dataLazySrc
$(o).wrap('<div></div>') btf.wrap(i, 'div', { class: 'fj-gallery-item' })
}) })
} })
runLightbox() if (window.fjGallery) {
btf.initJustifiedGallery(ele)
if (detectJgJsLoad) {
btf.initJustifiedGallery($justifiedGallery)
return return
} }
$('head').append(`<link rel="stylesheet" type="text/css" href="${GLOBAL_CONFIG.source.justifiedGallery.css}">`) const newEle = document.createElement('link')
$.getScript(`${GLOBAL_CONFIG.source.justifiedGallery.js}`, () => { btf.initJustifiedGallery($justifiedGallery) }) newEle.rel = 'stylesheet'
detectJgJsLoad = true newEle.href = GLOBAL_CONFIG.source.justifiedGallery.css
} document.body.appendChild(newEle)
getScript(`${GLOBAL_CONFIG.source.justifiedGallery.js}`).then(() => { btf.initJustifiedGallery(ele) })
const jqLoadAndRun = () => {
const $jgEle = document.querySelectorAll('#article-container .justified-gallery')
if ($jgEle.length) {
btf.isJqueryLoad(() => {
runJustifiedGallery($jgEle)
})
return
}
runLightbox()
} }
/** /**
@@ -328,7 +294,7 @@ document.addEventListener('DOMContentLoaded', function () {
} }
$header.classList.add('nav-fixed') $header.classList.add('nav-fixed')
if (window.getComputedStyle($rightside).getPropertyValue('opacity') === '0') { if (window.getComputedStyle($rightside).getPropertyValue('opacity') === '0') {
$rightside.style.cssText = 'opacity: 1; transform: translateX(-38px)' $rightside.style.cssText = 'opacity: 0.7; transform: translateX(-58px)'
} }
} else { } else {
if (currentTop === 0) { if (currentTop === 0) {
@@ -338,7 +304,7 @@ document.addEventListener('DOMContentLoaded', function () {
} }
if (document.body.scrollHeight <= innerHeight) { if (document.body.scrollHeight <= innerHeight) {
$rightside.style.cssText = 'opacity: 1; transform: translateX(-38px)' $rightside.style.cssText = 'opacity: 0.7; transform: translateX(-58px)'
} }
}, 200)() }, 200)()
} }
@@ -390,9 +356,9 @@ document.addEventListener('DOMContentLoaded', function () {
} }
} }
const mobileToc = { window.mobileToc = {
open: () => { open: () => {
$cardTocLayout.style.cssText = 'animation: toc-open .3s; opacity: 1; right: 45px' $cardTocLayout.style.cssText = 'animation: toc-open .3s; opacity: 1; right: 55px'
}, },
close: () => { close: () => {
@@ -403,11 +369,6 @@ document.addEventListener('DOMContentLoaded', function () {
} }
} }
document.getElementById('mobile-toc-button').addEventListener('click', () => {
if (window.getComputedStyle($cardTocLayout).getPropertyValue('opacity') === '0') mobileToc.open()
else mobileToc.close()
})
// toc元素點擊 // toc元素點擊
$cardToc.addEventListener('click', (e) => { $cardToc.addEventListener('click', (e) => {
e.preventDefault() e.preventDefault()
@@ -416,7 +377,7 @@ document.addEventListener('DOMContentLoaded', function () {
: e.target.parentElement : e.target.parentElement
btf.scrollToDest(btf.getEleTop(document.getElementById(decodeURI($target.getAttribute('href')).replace('#', ''))), 300) btf.scrollToDest(btf.getEleTop(document.getElementById(decodeURI($target.getAttribute('href')).replace('#', ''))), 300)
if (window.innerWidth < 900) { if (window.innerWidth < 900) {
mobileToc.close() window.mobileToc.close()
} }
}) })
@@ -512,6 +473,7 @@ document.addEventListener('DOMContentLoaded', function () {
typeof utterancesTheme === 'function' && utterancesTheme() typeof utterancesTheme === 'function' && utterancesTheme()
typeof FB === 'object' && window.loadFBComment() typeof FB === 'object' && window.loadFBComment()
window.DISQUS && document.getElementById('disqus_thread').children.length && setTimeout(() => window.disqusReset(), 200) window.DISQUS && document.getElementById('disqus_thread').children.length && setTimeout(() => window.disqusReset(), 200)
typeof runMermaid === 'function' && window.runMermaid()
}, },
showOrHideBtn: () => { // rightside 點擊設置 按鈕 展開 showOrHideBtn: () => { // rightside 點擊設置 按鈕 展開
document.getElementById('rightside-config-hide').classList.toggle('show') document.getElementById('rightside-config-hide').classList.toggle('show')
@@ -527,22 +489,9 @@ document.addEventListener('DOMContentLoaded', function () {
$htmlDom.toggle('hide-aside') $htmlDom.toggle('hide-aside')
}, },
adjustFontSize: (plus) => { runMobileToc: () => {
const fontSizeVal = parseInt(window.getComputedStyle(document.documentElement).getPropertyValue('--global-font-size')) if (window.getComputedStyle(document.getElementById('card-toc')).getPropertyValue('opacity') === '0') window.mobileToc.open()
let newValue = '' else window.mobileToc.close()
if (plus) {
if (fontSizeVal >= 20) return
newValue = fontSizeVal + 1
document.documentElement.style.setProperty('--global-font-size', newValue + 'px')
!document.getElementById('nav').classList.contains('hide-menu') && adjustMenu(true)
} else {
if (fontSizeVal <= 10) return
newValue = fontSizeVal - 1
document.documentElement.style.setProperty('--global-font-size', newValue + 'px')
document.getElementById('nav').classList.contains('hide-menu') && adjustMenu(true)
}
saveToLocal.set('global-font-size', newValue, 2)
} }
} }
@@ -555,6 +504,9 @@ document.addEventListener('DOMContentLoaded', function () {
case 'rightside_config': case 'rightside_config':
rightSideFn.showOrHideBtn() rightSideFn.showOrHideBtn()
break break
case 'mobile-toc-button':
rightSideFn.runMobileToc()
break
case 'readmode': case 'readmode':
rightSideFn.switchReadMode() rightSideFn.switchReadMode()
break break
@@ -564,12 +516,6 @@ document.addEventListener('DOMContentLoaded', function () {
case 'hide-aside-btn': case 'hide-aside-btn':
rightSideFn.hideAsideBtn() rightSideFn.hideAsideBtn()
break break
case 'font-plus':
rightSideFn.adjustFontSize(true)
break
case 'font-minus':
rightSideFn.adjustFontSize()
break
default: default:
break break
} }
@@ -659,8 +605,8 @@ document.addEventListener('DOMContentLoaded', function () {
const $hideContent = $this.nextElementSibling const $hideContent = $this.nextElementSibling
$this.classList.toggle('open') $this.classList.toggle('open')
if ($this.classList.contains('open')) { if ($this.classList.contains('open')) {
if ($hideContent.querySelectorAll('.justified-gallery').length > 0) { if ($hideContent.querySelectorAll('.fj-gallery').length > 0) {
btf.initJustifiedGallery($hideContent.querySelectorAll('.justified-gallery')) btf.initJustifiedGallery($hideContent.querySelectorAll('.fj-gallery'))
} }
} }
}) })
@@ -686,7 +632,7 @@ document.addEventListener('DOMContentLoaded', function () {
if (item.id === tabId) item.classList.add('active') if (item.id === tabId) item.classList.add('active')
else item.classList.remove('active') else item.classList.remove('active')
}) })
const $isTabJustifiedGallery = $tabContent.querySelectorAll(`#${tabId} .justified-gallery`) const $isTabJustifiedGallery = $tabContent.querySelectorAll(`#${tabId} .fj-gallery`)
if ($isTabJustifiedGallery.length > 0) { if ($isTabJustifiedGallery.length > 0) {
btf.initJustifiedGallery($isTabJustifiedGallery) btf.initJustifiedGallery($isTabJustifiedGallery)
} }
@@ -776,8 +722,12 @@ document.addEventListener('DOMContentLoaded', function () {
} }
const unRefreshFn = function () { const unRefreshFn = function () {
window.addEventListener('resize', adjustMenu) window.addEventListener('resize', () => {
window.addEventListener('orientationchange', () => { setTimeout(adjustMenu(true), 100) }) adjustMenu(false)
hideMenuIndex && mobileSidebarOpen && sidebarFn.close()
})
document.getElementById('menu-mask').addEventListener('click', e => { sidebarFn.close() })
clickFnOfSubMenu() clickFnOfSubMenu()
GLOBAL_CONFIG.islazyload && lazyloadImg() GLOBAL_CONFIG.islazyload && lazyloadImg()
@@ -798,17 +748,21 @@ document.addEventListener('DOMContentLoaded', function () {
} }
GLOBAL_CONFIG_SITE.isToc && tocFn() GLOBAL_CONFIG_SITE.isToc && tocFn()
sidebarFn()
GLOBAL_CONFIG_SITE.isHome && scrollDownInIndex() GLOBAL_CONFIG_SITE.isHome && scrollDownInIndex()
addHighlightTool() addHighlightTool()
GLOBAL_CONFIG.isPhotoFigcaption && addPhotoFigcaption() GLOBAL_CONFIG.isPhotoFigcaption && addPhotoFigcaption()
jqLoadAndRun()
scrollFn() scrollFn()
const $jgEle = document.querySelectorAll('#article-container .fj-gallery')
$jgEle.length && runJustifiedGallery($jgEle)
runLightbox()
addTableWrap() addTableWrap()
clickFnOfTagHide() clickFnOfTagHide()
tabsFn.clickFnOfTabs() tabsFn.clickFnOfTabs()
tabsFn.backToTop() tabsFn.backToTop()
switchComments() switchComments()
document.getElementById('toggle-menu').addEventListener('click', () => { sidebarFn.open() })
} }
refreshFn() refreshFn()

View File

@@ -41,107 +41,112 @@ window.addEventListener('load', () => {
searchClickFn() searchClickFn()
}) })
function search (path) { async function search (path) {
fetch(GLOBAL_CONFIG.root + path) let datas = []
.then(response => response.text()) const typeF = path.split('.')[1]
.then(str => new window.DOMParser().parseFromString(str, 'text/xml')) const response = await fetch(GLOBAL_CONFIG.root + path)
.then(data => { if (typeF === 'json') {
const datas = [...data.querySelectorAll('entry')].map(function (item) { datas = await response.json()
const content = item.querySelector('content') } else if (typeF === 'xml') {
return { const res = await response.text()
title: item.querySelector('title').textContent, const t = await new window.DOMParser().parseFromString(res, 'text/xml')
content: content ? content.textContent : '', const a = await t
url: item.querySelector('url').textContent datas = [...a.querySelectorAll('entry')].map(function (item) {
} return {
}) title: item.querySelector('title').textContent,
content: item.querySelector('content').textContent,
const $input = document.querySelector('#local-search-input input') url: item.querySelector('url').textContent
const $resultContent = document.getElementById('local-search-results') }
$input.addEventListener('input', function () { })
let str = '<div class="search-result-list">' }
const keywords = this.value.trim().toLowerCase().split(/[\s]+/) const $input = document.querySelector('#local-search-input input')
$resultContent.innerHTML = '' const $resultContent = document.getElementById('local-search-results')
if (this.value.trim().length <= 0) return $input.addEventListener('input', function () {
let count = 0 let str = '<div class="search-result-list">'
// perform local searching const keywords = this.value.trim().toLowerCase().split(/[\s]+/)
datas.forEach(function (data) { $resultContent.innerHTML = ''
let isMatch = true if (this.value.trim().length <= 0) return
if (!data.title || data.title.trim() === '') { let count = 0
data.title = 'Untitled' // perform local searching
} datas.forEach(function (data) {
let dataTitle = data.title.trim().toLowerCase() let isMatch = true
const dataContent = data.content.trim().replace(/<[^>]+>/g, '').toLowerCase() if (!data.title || data.title.trim() === '') {
const dataUrl = data.url.startsWith('/') ? data.url : GLOBAL_CONFIG.root + data.url data.title = 'Untitled'
let indexTitle = -1 }
let indexContent = -1 let dataTitle = data.title.trim().toLowerCase()
let firstOccur = -1 const dataContent = data.content.trim().replace(/<[^>]+>/g, '').toLowerCase()
// only match artiles with not empty titles and contents const dataUrl = data.url.startsWith('/') ? data.url : GLOBAL_CONFIG.root + data.url
if (dataTitle !== '' || dataContent !== '') { let indexTitle = -1
keywords.forEach(function (keyword, i) { let indexContent = -1
indexTitle = dataTitle.indexOf(keyword) let firstOccur = -1
indexContent = dataContent.indexOf(keyword) // only match artiles with not empty titles and contents
if (indexTitle < 0 && indexContent < 0) { if (dataTitle !== '' || dataContent !== '') {
isMatch = false keywords.forEach(function (keyword, i) {
} else { indexTitle = dataTitle.indexOf(keyword)
if (indexContent < 0) { indexContent = dataContent.indexOf(keyword)
indexContent = 0 if (indexTitle < 0 && indexContent < 0) {
}
if (i === 0) {
firstOccur = indexContent
}
}
})
} else {
isMatch = false isMatch = false
} } else {
if (indexContent < 0) {
// show search results indexContent = 0
if (isMatch) { }
const content = data.content.trim().replace(/<[^>]+>/g, '') if (i === 0) {
if (firstOccur >= 0) { firstOccur = indexContent
// cut out 130 characters
let start = firstOccur - 30
let end = firstOccur + 100
if (start < 0) {
start = 0
}
if (start === 0) {
end = 100
}
if (end > content.length) {
end = content.length
}
let matchContent = content.substring(start, end)
// highlight all keywords
keywords.forEach(function (keyword) {
const regS = new RegExp(keyword, 'gi')
matchContent = matchContent.replace(regS, '<span class="search-keyword">' + keyword + '</span>')
dataTitle = dataTitle.replace(regS, '<span class="search-keyword">' + keyword + '</span>')
})
str += '<div class="local-search__hit-item"><a href="' + dataUrl + '" class="search-result-title">' + dataTitle + '</a>'
count += 1
if (dataContent !== '') {
str += '<p class="search-result">' + matchContent + '...</p>'
}
} }
str += '</div>'
} }
}) })
if (count === 0) { } else {
str += '<div id="local-search__hits-empty">' + GLOBAL_CONFIG.localSearch.languages.hits_empty.replace(/\$\{query}/, this.value.trim()) + isMatch = false
'</div>' }
// show search results
if (isMatch) {
const content = data.content.trim().replace(/<[^>]+>/g, '')
if (firstOccur >= 0) {
// cut out 130 characters
// let start = firstOccur - 30 < 0 ? 0 : firstOccur - 30
// let end = firstOccur + 50 > content.length ? content.length : firstOccur + 50
let start = firstOccur - 30
let end = firstOccur + 100
if (start < 0) {
start = 0
}
if (start === 0) {
end = 100
}
if (end > content.length) {
end = content.length
}
let matchContent = content.substring(start, end)
// highlight all keywords
keywords.forEach(function (keyword) {
const regS = new RegExp(keyword, 'gi')
matchContent = matchContent.replace(regS, '<span class="search-keyword">' + keyword + '</span>')
dataTitle = dataTitle.replace(regS, '<span class="search-keyword">' + keyword + '</span>')
})
str += '<div class="local-search__hit-item"><a href="' + dataUrl + '" class="search-result-title">' + dataTitle + '</a>'
count += 1
if (dataContent !== '') {
str += '<p class="search-result">' + matchContent + '...</p>'
}
} }
str += '</div>' str += '</div>'
$resultContent.innerHTML = str }
window.pjax && window.pjax.refresh($resultContent)
})
}) })
if (count === 0) {
str += '<div id="local-search__hits-empty">' + GLOBAL_CONFIG.localSearch.languages.hits_empty.replace(/\$\{query}/, this.value.trim()) +
'</div>'
}
str += '</div>'
$resultContent.innerHTML = str
window.pjax && window.pjax.refresh($resultContent)
})
} }
}) })

View File

@@ -72,20 +72,6 @@ const btf = {
}) })
}, },
initJustifiedGallery: function (selector) {
if (!(selector instanceof jQuery)) {
selector = $(selector)
}
selector.each(function (i, o) {
if ($(this).is(':visible')) {
$(this).justifiedGallery({
rowHeight: 220,
margins: 4
})
}
})
},
diffDate: (d, more = false) => { diffDate: (d, more = false) => {
const dateNow = new Date() const dateNow = new Date()
const datePost = new Date(d) const datePost = new Date(d)
@@ -135,15 +121,11 @@ const btf = {
} }
}, },
scrollToDest: (pos, time) => { scrollToDest: (pos, time = 500) => {
if (pos < 0 || time < 0) { const currentPos = window.pageYOffset
return
}
const currentPos = window.scrollY || window.screenTop
if (currentPos > pos) pos = pos - 70 if (currentPos > pos) pos = pos - 70
if ('CSS' in window && CSS.supports('scroll-behavior', 'smooth')) { if ('scrollBehavior' in document.documentElement.style) {
window.scrollTo({ window.scrollTo({
top: pos, top: pos,
behavior: 'smooth' behavior: 'smooth'
@@ -152,25 +134,19 @@ const btf = {
} }
let start = null let start = null
time = time || 500 pos = +pos
window.requestAnimationFrame(function step (currentTime) { window.requestAnimationFrame(function step (currentTime) {
start = !start ? currentTime : start start = !start ? currentTime : start
const progress = currentTime - start
if (currentPos < pos) { if (currentPos < pos) {
const progress = currentTime - start
window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos) window.scrollTo(0, ((pos - currentPos) * progress / time) + currentPos)
if (progress < time) {
window.requestAnimationFrame(step)
} else {
window.scrollTo(0, pos)
}
} else { } else {
const progress = currentTime - start
window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time)) window.scrollTo(0, currentPos - ((currentPos - pos) * progress / time))
if (progress < time) { }
window.requestAnimationFrame(step) if (progress < time) {
} else { window.requestAnimationFrame(step)
window.scrollTo(0, pos) } else {
} window.scrollTo(0, pos)
} }
}) })
}, },
@@ -226,14 +202,6 @@ const btf = {
} }
}, },
isJqueryLoad: fn => {
if (typeof jQuery === 'undefined') {
getScript(GLOBAL_CONFIG.source.jQuery).then(fn)
} else {
fn()
}
},
isHidden: ele => ele.offsetHeight === 0 && ele.offsetWidth === 0, isHidden: ele => ele.offsetHeight === 0 && ele.offsetWidth === 0,
getEleTop: ele => { getEleTop: ele => {
@@ -265,14 +233,35 @@ const btf = {
ele.forEach(i => { ele.forEach(i => {
if (i.parentNode.tagName !== 'A') { if (i.parentNode.tagName !== 'A') {
const dataSrc = i.dataset.lazySrc || i.src const dataSrc = i.dataset.lazySrc || i.src
const dataCaption = i.alt || '' const dataCaption = i.title || i.alt || ''
btf.wrap(i, 'a', { href: dataSrc, 'data-fancybox': 'gallery', 'data-caption': dataCaption, 'data-thumb': dataSrc }) btf.wrap(i, 'a', { href: dataSrc, 'data-fancybox': 'gallery', 'data-caption': dataCaption, 'data-thumb': dataSrc })
} }
}) })
Fancybox.bind('[data-fancybox]', { if (!window.fancyboxRun) {
Hash: false Fancybox.bind('[data-fancybox]', {
}) Hash: false,
Thumbs: {
autoStart: false
}
})
window.fancyboxRun = true
}
} }
},
initJustifiedGallery: function (selector) {
selector.forEach(function (i) {
if (!btf.isHidden(i)) {
fjGallery(i, {
itemSelector: '.fj-gallery-item',
rowHeight: 220,
gutter: 4,
onJustify: function () {
this.$container.style.opacity = '1'
}
})
}
})
} }
} }