mirror of
https://github.com/jerryc127/hexo-theme-butterfly.git
synced 2026-04-16 20:30:53 +08:00
breaking changes: 移除博天api
breaking changes: 移除 waline 的 avatar 和 avatar cdn 配置 feat: anchor 不再限制 post 頁開啟,可以在任何頁面開啟 feat: 文章標題支持點擊跳轉到此標題開始閲讀 closed #653 feat: toc可以設置全部展開 closed #709 feat: 增加 新的評論系統 giscus feat: 支持新的評論名寫法,主題會處理評論名字大小寫,舊的會兼容 feat: 友情鏈接列表增加 fetch url 獲取 improvement: 鼠標移到最新評論內容,增加 title 顯示 fix: 修復 rightside 遮擋內容,導致內容無法點擊的 bug fix: 修復 mermaid 在某些頁面(有元素 id 為 mermaid 時) 會無法加載的 bug fix: 修復 搜索框不會自動 focus 的 bug
This commit is contained in:
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
Demo: 👍 [Butterfly](https://butterfly.js.org/) || 🤞 [MYW](https://immyw.com/)
|
Demo: 👍 [Butterfly](https://butterfly.js.org/) || 🤞 [CrazyWong](https://crazywong.com/)
|
||||||
|
|
||||||
Docs: 📖 [Butterfly Docs](https://butterfly.js.org/posts/21cfbf15/)
|
Docs: 📖 [Butterfly Docs](https://butterfly.js.org/posts/21cfbf15/)
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ npm i hexo-theme-butterfly
|
|||||||
- [x] Related articles
|
- [x] Related articles
|
||||||
- [x] Displays outdated notice for a post
|
- [x] Displays outdated notice for a post
|
||||||
- [x] Share (AddThis/Sharejs/Addtoany)
|
- [x] Share (AddThis/Sharejs/Addtoany)
|
||||||
- [X] Comment (Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo)
|
- [X] Comment (Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus)
|
||||||
- [x] Multiple Comment System Support
|
- [x] Multiple Comment System Support
|
||||||
- [x] Online Chats (Chatra/Tidio/Daovoice/Gitter/Crisp)
|
- [x] Online Chats (Chatra/Tidio/Daovoice/Gitter/Crisp)
|
||||||
- [x] Web analytics
|
- [x] Web analytics
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
預覽: 👍 [Butterfly](https://butterfly.js.org/) || 🤞 [MYW](https://immyw.com/)
|
預覽: 👍 [Butterfly](https://butterfly.js.org/) || 🤞 [CrazyWong](https://crazywong.com/)
|
||||||
|
|
||||||
文檔: 📖 [Butterfly Docs](https://butterfly.js.org/posts/21cfbf15/)
|
文檔: 📖 [Butterfly Docs](https://butterfly.js.org/posts/21cfbf15/)
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ theme: butterfly
|
|||||||
- [x] 顯示相關文章
|
- [x] 顯示相關文章
|
||||||
- [x] 過期文章提醒
|
- [x] 過期文章提醒
|
||||||
- [x] 多種分享系統(AddThis/Sharejs/Addtoany)
|
- [x] 多種分享系統(AddThis/Sharejs/Addtoany)
|
||||||
- [X] 多種評論系統(Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo)
|
- [X] 多種評論系統(Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo/Giscus)
|
||||||
- [x] 支持雙評論部署
|
- [x] 支持雙評論部署
|
||||||
- [x] 多種在線聊天(Chatra/Tidio/Daovoice/Gitter/Crisp)
|
- [x] 多種在線聊天(Chatra/Tidio/Daovoice/Gitter/Crisp)
|
||||||
- [x] 多種分析系統
|
- [x] 多種分析系統
|
||||||
|
|||||||
29
_config.yml
29
_config.yml
@@ -171,6 +171,7 @@ toc:
|
|||||||
post: true
|
post: true
|
||||||
page: false
|
page: false
|
||||||
number: true
|
number: true
|
||||||
|
expand: false
|
||||||
style_simple: false # for post
|
style_simple: false # for post
|
||||||
|
|
||||||
post_copyright:
|
post_copyright:
|
||||||
@@ -253,10 +254,7 @@ addtoany:
|
|||||||
|
|
||||||
comments:
|
comments:
|
||||||
# Up to two comments system, the first will be shown as default
|
# Up to two comments system, the first will be shown as default
|
||||||
# Choose: Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo
|
use: # Valine,Disqus
|
||||||
use:
|
|
||||||
# - Valine
|
|
||||||
# - Disqus
|
|
||||||
text: true # Display the comment name next to the button
|
text: true # Display the comment name next to the button
|
||||||
# lazyload: The comment system will be load when comment element enters the browser's viewport.
|
# lazyload: The comment system will be load when comment element enters the browser's viewport.
|
||||||
# If you set it to true, the comment count will be invalid
|
# If you set it to true, the comment count will be invalid
|
||||||
@@ -308,8 +306,6 @@ valine:
|
|||||||
# https://waline.js.org/
|
# https://waline.js.org/
|
||||||
waline:
|
waline:
|
||||||
serverURL: # Waline server address url
|
serverURL: # Waline server address url
|
||||||
avatar: monsterid # gravatar style https://zh-tw.gravatar.com/site/implement/images/#default-image
|
|
||||||
avatarCDN: # Gravatar CDN baseURL
|
|
||||||
bg: # waline background
|
bg: # waline background
|
||||||
visitor: false
|
visitor: false
|
||||||
option:
|
option:
|
||||||
@@ -341,6 +337,17 @@ twikoo:
|
|||||||
visitor: false
|
visitor: false
|
||||||
option:
|
option:
|
||||||
|
|
||||||
|
# Giscus
|
||||||
|
# https://giscus.app/
|
||||||
|
giscus:
|
||||||
|
repo:
|
||||||
|
repo_id:
|
||||||
|
category_id:
|
||||||
|
theme:
|
||||||
|
light: light
|
||||||
|
dark: dark
|
||||||
|
option:
|
||||||
|
|
||||||
# Chat Services
|
# Chat Services
|
||||||
# --------------------------------------
|
# --------------------------------------
|
||||||
|
|
||||||
@@ -583,12 +590,11 @@ subtitle:
|
|||||||
effect: true
|
effect: true
|
||||||
# loop (循環打字)
|
# loop (循環打字)
|
||||||
loop: true
|
loop: true
|
||||||
# source調用第三方服務
|
# source 調用第三方服務
|
||||||
# source: false 關閉調用
|
# source: false 關閉調用
|
||||||
# source: 1 調用搏天 api 的隨機語錄(簡體)
|
# source: 1 調用一言網的一句話(簡體) https://hitokoto.cn/
|
||||||
# source: 2 調用一言網的一句話(簡體)
|
# source: 2 調用一句網(簡體) http://yijuzhan.com/
|
||||||
# source: 3 調用一句網(簡體)
|
# source: 3 調用今日詩詞(簡體) https://www.jinrishici.com/
|
||||||
# source: 4 調用今日詩詞(簡體)
|
|
||||||
# subtitle 會先顯示 source , 再顯示 sub 的內容
|
# subtitle 會先顯示 source , 再顯示 sub 的內容
|
||||||
source: false
|
source: false
|
||||||
# 如果關閉打字效果,subtitle 只會顯示 sub 的第一行文字
|
# 如果關閉打字效果,subtitle 只會顯示 sub 的第一行文字
|
||||||
@@ -836,6 +842,7 @@ CDN:
|
|||||||
utterances:
|
utterances:
|
||||||
twikoo:
|
twikoo:
|
||||||
waline:
|
waline:
|
||||||
|
giscus:
|
||||||
|
|
||||||
# share
|
# share
|
||||||
addtoany:
|
addtoany:
|
||||||
|
|||||||
@@ -119,5 +119,5 @@ script.
|
|||||||
},
|
},
|
||||||
isPhotoFigcaption: !{theme.photofigcaption},
|
isPhotoFigcaption: !{theme.photofigcaption},
|
||||||
islazyload: !{theme.lazyload.enable},
|
islazyload: !{theme.lazyload.enable},
|
||||||
isanchor: !{theme.anchor}
|
isAnchor: !{theme.anchor}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,57 @@
|
|||||||
#article-container
|
#article-container
|
||||||
.flink
|
.flink
|
||||||
if site.data.link
|
if page.flink_url
|
||||||
each i in site.data.link
|
script.
|
||||||
if i.class_name
|
(()=>{
|
||||||
h2!= i.class_name
|
const replaceSymbol = (str) => {
|
||||||
if i.class_desc
|
return str.replace(/[\p{P}\p{S}]/gu, '-')
|
||||||
.flink-desc!=i.class_desc
|
}
|
||||||
.flink-list
|
|
||||||
each item in i.link_list
|
let result = ''
|
||||||
.flink-list-item
|
fetch('!{url_for(page.flink_url)}')
|
||||||
a(href=url_for(item.link) title=item.name target="_blank")
|
.then(response => response.json())
|
||||||
.flink-item-icon
|
.then(str => {
|
||||||
img.no-lightbox(src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt=item.name )
|
for(let i = 0; i < str.length; i++){
|
||||||
.flink-item-name= item.name
|
const replaceClassName = replaceSymbol(str[i].class_name)
|
||||||
.flink-item-desc(title=item.descr)= item.descr
|
const className = str[i].class_name ? `<h2 id='${replaceClassName}'><a href="#${replaceClassName}" class="headerlink" title="${str[i].class_name}"></a>${str[i].class_name}</h2>` : ''
|
||||||
|
const classDesc = str[i].class_desc ? `<div class='flink-desc'>${str[i].class_desc}</div>` : ''
|
||||||
|
|
||||||
|
let listResult = ''
|
||||||
|
const lists = str[i].link_list
|
||||||
|
for(let j = 0; j < lists.length; j++){
|
||||||
|
listResult += `
|
||||||
|
<div class="flink-list-item">
|
||||||
|
<a href='${lists[j].link}' title='${lists[j].name}' target='_blank'>
|
||||||
|
<div class='flink-item-icon'>
|
||||||
|
<img class='no-lightbox' src='${lists[j].avatar}' onerror='this.onerror=null;this.src="!{url_for(theme.error_img.flink)}"' alt='${lists[j].name}' />
|
||||||
|
</div>
|
||||||
|
<div class='flink-item-name'>${lists[j].name}</div>
|
||||||
|
<div class='flink-item-desc' title='${lists[j].descr}'>${lists[j].descr}</div>
|
||||||
|
</a>
|
||||||
|
</div>`
|
||||||
|
}
|
||||||
|
|
||||||
|
result += `${className}${classDesc} <div class='flink-list'>${listResult}</div>`
|
||||||
|
}
|
||||||
|
|
||||||
|
document.querySelector('.flink').insertAdjacentHTML('afterbegin', result)
|
||||||
|
})
|
||||||
|
})()
|
||||||
|
|
||||||
|
else
|
||||||
|
if site.data.link
|
||||||
|
each i in site.data.link
|
||||||
|
if i.class_name
|
||||||
|
!=markdown(`## ${i.class_name}`)
|
||||||
|
if i.class_desc
|
||||||
|
.flink-desc!=i.class_desc
|
||||||
|
.flink-list
|
||||||
|
each item in i.link_list
|
||||||
|
.flink-list-item
|
||||||
|
a(href=url_for(item.link) title=item.name target="_blank")
|
||||||
|
.flink-item-icon
|
||||||
|
img.no-lightbox(src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt=item.name )
|
||||||
|
.flink-item-name= item.name
|
||||||
|
.flink-item-desc(title=item.descr)= item.descr
|
||||||
!= page.content
|
!= page.content
|
||||||
|
|
||||||
|
|||||||
49
layout/includes/third-party/comments/giscus.pug
vendored
Normal file
49
layout/includes/third-party/comments/giscus.pug
vendored
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
- const { repo, repo_id, category_id, option } = theme.giscus
|
||||||
|
- const themes = theme.giscus.theme
|
||||||
|
script.
|
||||||
|
function loadGiscus () {
|
||||||
|
let nowTheme = document.documentElement.getAttribute('data-theme') === 'dark' ? '!{themes.dark}' : '!{themes.light}'
|
||||||
|
|
||||||
|
const config = Object.assign({
|
||||||
|
src: '!{theme.CDN.giscus}',
|
||||||
|
'data-repo': '!{repo}',
|
||||||
|
'data-repo-id': '!{repo_id}',
|
||||||
|
'data-category-id': '!{category_id}',
|
||||||
|
'data-mapping': 'pathname',
|
||||||
|
'data-theme': nowTheme,
|
||||||
|
'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 () {
|
||||||
|
const theme = document.documentElement.getAttribute('data-theme') === 'dark' ? '!{themes.dark}' : '!{themes.light}'
|
||||||
|
|
||||||
|
function sendMessage(message) {
|
||||||
|
const iframe = document.querySelector('iframe.giscus-frame');
|
||||||
|
if (!iframe) return;
|
||||||
|
iframe.contentWindow.postMessage({ giscus: message }, 'https://giscus.app');
|
||||||
|
}
|
||||||
|
|
||||||
|
sendMessage({
|
||||||
|
setConfig: {
|
||||||
|
theme: theme
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,6 +33,8 @@ hr
|
|||||||
#twikoo-wrap
|
#twikoo-wrap
|
||||||
when 'Waline'
|
when 'Waline'
|
||||||
#waline-wrap
|
#waline-wrap
|
||||||
|
when 'Giscus'
|
||||||
|
#giscus-wrap
|
||||||
when 'Facebook Comments'
|
when 'Facebook Comments'
|
||||||
.fb-comments(data-colorscheme = theme.display_mode === 'dark' ? 'dark' : 'light'
|
.fb-comments(data-colorscheme = theme.display_mode === 'dark' ? 'dark' : 'light'
|
||||||
data-numposts= theme.facebook_comments.pageSize || 10
|
data-numposts= theme.facebook_comments.pageSize || 10
|
||||||
|
|||||||
2
layout/includes/third-party/comments/js.pug
vendored
2
layout/includes/third-party/comments/js.pug
vendored
@@ -16,5 +16,7 @@ each name in theme.comments.use
|
|||||||
!=partial('includes/third-party/comments/twikoo', {}, {cache: true})
|
!=partial('includes/third-party/comments/twikoo', {}, {cache: true})
|
||||||
when 'Waline'
|
when 'Waline'
|
||||||
!=partial('includes/third-party/comments/waline', {}, {cache: true})
|
!=partial('includes/third-party/comments/waline', {}, {cache: true})
|
||||||
|
when 'Giscus'
|
||||||
|
!=partial('includes/third-party/comments/giscus', {}, {cache: true})
|
||||||
when 'Facebook Comments'
|
when 'Facebook Comments'
|
||||||
!=partial('includes/third-party/comments/facebook_comments', {}, {cache: true})
|
!=partial('includes/third-party/comments/facebook_comments', {}, {cache: true})
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
- const { serverURL, avatar, avatarCDN, visitor, option } = theme.waline
|
- const { serverURL, visitor, option } = theme.waline
|
||||||
|
|
||||||
script.
|
script.
|
||||||
function loadWaline () {
|
function loadWaline () {
|
||||||
@@ -6,8 +6,6 @@ script.
|
|||||||
const waline = new Waline(Object.assign({
|
const waline = new Waline(Object.assign({
|
||||||
el: '#waline-wrap',
|
el: '#waline-wrap',
|
||||||
serverURL: '!{serverURL}',
|
serverURL: '!{serverURL}',
|
||||||
avatar: '#{avatar}',
|
|
||||||
avatarCDN: '!{avatarCDN || "https://sdn.geekzu.org/avatar/"}',
|
|
||||||
path: location.pathname,
|
path: location.pathname,
|
||||||
visitor: !{visitor},
|
visitor: !{visitor},
|
||||||
dark: 'html[data-theme="dark"]'
|
dark: 'html[data-theme="dark"]'
|
||||||
|
|||||||
3
layout/includes/third-party/math/mermaid.pug
vendored
3
layout/includes/third-party/math/mermaid.pug
vendored
@@ -3,6 +3,7 @@ script.
|
|||||||
const $mermaidWrap = document.querySelectorAll('#article-container .mermaid-wrap')
|
const $mermaidWrap = document.querySelectorAll('#article-container .mermaid-wrap')
|
||||||
if ($mermaidWrap.length) {
|
if ($mermaidWrap.length) {
|
||||||
window.runMermaid = () => {
|
window.runMermaid = () => {
|
||||||
|
window.loadMermaid = true
|
||||||
const theme = document.documentElement.getAttribute('data-theme') === 'dark' ? '!{theme.mermaid.theme.dark}' : '!{theme.mermaid.theme.light}'
|
const theme = document.documentElement.getAttribute('data-theme') === 'dark' ? '!{theme.mermaid.theme.dark}' : '!{theme.mermaid.theme.light}'
|
||||||
|
|
||||||
Array.from($mermaidWrap).forEach((item, index) => {
|
Array.from($mermaidWrap).forEach((item, index) => {
|
||||||
@@ -17,7 +18,7 @@ script.
|
|||||||
}
|
}
|
||||||
|
|
||||||
const loadMermaid = () => {
|
const loadMermaid = () => {
|
||||||
window.mermaid ? runMermaid() : getScript('!{theme.CDN.mermaid}').then(runMermaid)
|
window.loadMermaid ? runMermaid() : getScript('!{theme.CDN.mermaid}').then(runMermaid)
|
||||||
}
|
}
|
||||||
|
|
||||||
window.pjax ? loadMermaid() : document.addEventListener('DOMContentLoaded', loadMermaid)
|
window.pjax ? loadMermaid() : document.addEventListener('DOMContentLoaded', loadMermaid)
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ script.
|
|||||||
}
|
}
|
||||||
|
|
||||||
result += `<div class='content'>
|
result += `<div class='content'>
|
||||||
<a class='comment' href='${array[i].url}'>${array[i].content}</a>
|
<a class='comment' href='${array[i].url}' title='${array[i].content}'>${array[i].content}</a>
|
||||||
<div class='name'><span>${array[i].nick}</span><time> / ${btf.diffDate(array[i].date, true)}</time></div>
|
<div class='name'><span>${array[i].nick}</span><time> / ${btf.diffDate(array[i].date, true)}</time></div>
|
||||||
</div></div>`
|
</div></div>`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ script.
|
|||||||
}
|
}
|
||||||
|
|
||||||
result += `<div class='content'>
|
result += `<div class='content'>
|
||||||
<a class='comment' href='${array[i].url}'>${array[i].content}</a>
|
<a class='comment' href='${array[i].url}' title='${array[i].content}'>${array[i].content}</a>
|
||||||
<div class='name'><span>${array[i].nick} / </span><time datetime="${array[i].date}">${btf.diffDate(array[i].date, true)}</time></div>
|
<div class='name'><span>${array[i].nick} / </span><time datetime="${array[i].date}">${btf.diffDate(array[i].date, true)}</time></div>
|
||||||
</div></div>`
|
</div></div>`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ script.
|
|||||||
}
|
}
|
||||||
|
|
||||||
result += `<div class='content'>
|
result += `<div class='content'>
|
||||||
<a class='comment' href='${array[i].url}'>${array[i].content}</a>
|
<a class='comment' href='${array[i].url}' title='${array[i].content}'>${array[i].content}</a>
|
||||||
<div class='name'><span>${array[i].nick} / </span><time datetime="${array[i].date}">${btf.diffDate(array[i].date, true)}</time></div>
|
<div class='name'><span>${array[i].nick} / </span><time datetime="${array[i].date}">${btf.diffDate(array[i].date, true)}</time></div>
|
||||||
</div></div>`
|
</div></div>`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ script.
|
|||||||
}
|
}
|
||||||
|
|
||||||
result += `<div class='content'>
|
result += `<div class='content'>
|
||||||
<a class='comment' href='${array[i].url}'>${array[i].content}</a>
|
<a class='comment' href='${array[i].url}' title='${array[i].content}'>${array[i].content}</a>
|
||||||
<div class='name'><span>${array[i].nick} / </span><time datetime="${array[i].date}">${btf.diffDate(array[i].date, true)}</time></div>
|
<div class='name'><span>${array[i].nick} / </span><time datetime="${array[i].date}">${btf.diffDate(array[i].date, true)}</time></div>
|
||||||
</div></div>`
|
</div></div>`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,13 +16,6 @@ script.
|
|||||||
return content
|
return content
|
||||||
}
|
}
|
||||||
|
|
||||||
const getIcon = (ava,mail) => {
|
|
||||||
if (ava) return ava
|
|
||||||
let defaultIcon = '!{ avatar ? `?d=${avatar}` : ''}'
|
|
||||||
let iconUrl = "!{avatarCDN ? avatarCDN : 'https://gravatar.loli.net/avatar/'}" + mail + defaultIcon
|
|
||||||
return iconUrl
|
|
||||||
}
|
|
||||||
|
|
||||||
const generateHtml = array => {
|
const generateHtml = array => {
|
||||||
let result = ''
|
let result = ''
|
||||||
|
|
||||||
@@ -36,7 +29,7 @@ script.
|
|||||||
}
|
}
|
||||||
|
|
||||||
result += `<div class='content'>
|
result += `<div class='content'>
|
||||||
<a class='comment' href='${array[i].url}'>${array[i].content}</a>
|
<a class='comment' href='${array[i].url}' title='${array[i].content}'>${array[i].content}</a>
|
||||||
<div class='name'><span>${array[i].nick} / </span><time datetime="${array[i].date}">${btf.diffDate(array[i].date, true)}</time></div>
|
<div class='name'><span>${array[i].nick} / </span><time datetime="${array[i].date}">${btf.diffDate(array[i].date, true)}</time></div>
|
||||||
</div></div>`
|
</div></div>`
|
||||||
}
|
}
|
||||||
@@ -59,7 +52,7 @@ script.
|
|||||||
const walineArray = res.comments.map(e => {
|
const walineArray = res.comments.map(e => {
|
||||||
return {
|
return {
|
||||||
'content': changeContent(e.comment),
|
'content': changeContent(e.comment),
|
||||||
'avatar': getIcon(e.QQAvatar,e.mail),
|
'avatar': e.avatar,
|
||||||
'nick': e.nick,
|
'nick': e.nick,
|
||||||
'url': e.url + '#' + e.objectId,
|
'url': e.url + '#' + e.objectId,
|
||||||
'date': e.updatedAt,
|
'date': e.updatedAt,
|
||||||
|
|||||||
36
layout/includes/third-party/subtitle.pug
vendored
36
layout/includes/third-party/subtitle.pug
vendored
@@ -3,38 +3,6 @@
|
|||||||
|
|
||||||
case source
|
case source
|
||||||
when 1
|
when 1
|
||||||
script.
|
|
||||||
function subtitleType () {
|
|
||||||
fetch('https://api.btstu.cn/yan/api.php?charset=utf-8&encode=json')
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (!{effect}) {
|
|
||||||
const sub = !{JSON.stringify(subContent)}
|
|
||||||
sub.unshift(data.text)
|
|
||||||
window.typed = new Typed('#subtitle', {
|
|
||||||
strings: sub,
|
|
||||||
startDelay: 300,
|
|
||||||
typeSpeed: 150,
|
|
||||||
loop: !{loop},
|
|
||||||
backSpeed: 50,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
document.getElementById('subtitle').innerHTML = data.text
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!{effect}) {
|
|
||||||
if (typeof Typed === 'function') {
|
|
||||||
subtitleType()
|
|
||||||
} else {
|
|
||||||
getScript('!{url_for(theme.CDN.typed)}').then(subtitleType)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
subtitleType()
|
|
||||||
}
|
|
||||||
|
|
||||||
when 2
|
|
||||||
script.
|
script.
|
||||||
function subtitleType () {
|
function subtitleType () {
|
||||||
fetch('https://v1.hitokoto.cn')
|
fetch('https://v1.hitokoto.cn')
|
||||||
@@ -67,7 +35,7 @@ case source
|
|||||||
subtitleType()
|
subtitleType()
|
||||||
}
|
}
|
||||||
|
|
||||||
when 3
|
when 2
|
||||||
script.
|
script.
|
||||||
function subtitleType () {
|
function subtitleType () {
|
||||||
getScript('https://yijuzhan.com/api/word.php?m=js').then(() => {
|
getScript('https://yijuzhan.com/api/word.php?m=js').then(() => {
|
||||||
@@ -99,7 +67,7 @@ case source
|
|||||||
subtitleType()
|
subtitleType()
|
||||||
}
|
}
|
||||||
|
|
||||||
when 4
|
when 3
|
||||||
script.
|
script.
|
||||||
function subtitleType () {
|
function subtitleType () {
|
||||||
getScript('https://sdk.jinrishici.com/v2/browser/jinrishici.js').then(() => {
|
getScript('https://sdk.jinrishici.com/v2/browser/jinrishici.js').then(() => {
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
- let tocNumber = page.toc_number !== undefined ? page.toc_number : theme.toc.number
|
- let tocNumber = page.toc_number !== undefined ? page.toc_number : theme.toc.number
|
||||||
|
- let tocExpand = page.toc_expand !== undefined ? page.toc_expand : theme.toc.expand
|
||||||
|
- let tocExpandClass = tocExpand ? 'is-expand' : ''
|
||||||
|
|
||||||
#card-toc.card-widget
|
#card-toc.card-widget
|
||||||
.item-headline
|
.item-headline
|
||||||
@@ -7,7 +9,7 @@
|
|||||||
span.toc-percentage
|
span.toc-percentage
|
||||||
|
|
||||||
if (page.encrypt == true)
|
if (page.encrypt == true)
|
||||||
.toc-content.toc-div-class(style="display:none")!=toc(page.origin, {list_number: tocNumber})
|
.toc-content.toc-div-class(class=tocExpandClass style="display:none")!=toc(page.origin, {list_number: tocNumber})
|
||||||
else
|
else
|
||||||
.toc-content!=toc(page.content, {list_number: tocNumber})
|
.toc-content(class=tocExpandClass)!=toc(page.content, {list_number: tocNumber})
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hexo-theme-butterfly",
|
"name": "hexo-theme-butterfly",
|
||||||
"version": "4.0.0-b9",
|
"version": "4.0.0-b10",
|
||||||
"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": {
|
||||||
@@ -20,13 +20,13 @@
|
|||||||
},
|
},
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/jerryc127/hexo-theme-butterfly/issues",
|
"url": "https://github.com/jerryc127/hexo-theme-butterfly/issues",
|
||||||
"email": "i@immyw.com"
|
"email": "my@crazywong.com"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"hexo-renderer-stylus": "^2.0.1",
|
"hexo-renderer-stylus": "^2.0.1",
|
||||||
"hexo-renderer-pug": "^2.0.0"
|
"hexo-renderer-pug": "^2.0.0"
|
||||||
},
|
},
|
||||||
"homepage": "https://butterfly.js.org/",
|
"homepage": "https://butterfly.js.org/",
|
||||||
"author": "Jerry <i@immyw.com>",
|
"author": "Jerry <my@crazywong.com>",
|
||||||
"license": "Apache-2.0"
|
"license": "Apache-2.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
/**
|
/**
|
||||||
* Butterfly
|
* Butterfly
|
||||||
* CDN
|
* 1. Merge CDN
|
||||||
|
* 2. Capitalize the first letter of comment name
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
hexo.extend.filter.register('before_generate', () => {
|
hexo.extend.filter.register('before_generate', () => {
|
||||||
const themeConfig = hexo.theme.config
|
const themeConfig = hexo.theme.config
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Merge CDN
|
||||||
|
*/
|
||||||
|
|
||||||
const defaultCDN = {
|
const defaultCDN = {
|
||||||
main_css: '/css/index.css',
|
main_css: '/css/index.css',
|
||||||
main: '/js/main.js',
|
main: '/js/main.js',
|
||||||
@@ -24,6 +30,7 @@ hexo.extend.filter.register('before_generate', () => {
|
|||||||
utterances: 'https://utteranc.es/client.js',
|
utterances: 'https://utteranc.es/client.js',
|
||||||
twikoo: 'https://cdn.jsdelivr.net/npm/twikoo/dist/twikoo.all.min.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',
|
waline: 'https://cdn.jsdelivr.net/npm/@waline/client/dist/Waline.min.js',
|
||||||
|
giscus: 'https://giscus.app/client.js',
|
||||||
|
|
||||||
// share
|
// share
|
||||||
addtoany: 'https://static.addtoany.com/menu/page.js',
|
addtoany: 'https://static.addtoany.com/menu/page.js',
|
||||||
@@ -101,4 +108,18 @@ hexo.extend.filter.register('before_generate', () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
themeConfig.CDN = Object.assign(defaultCDN, deleteNullValue(themeConfig.CDN))
|
themeConfig.CDN = Object.assign(defaultCDN, deleteNullValue(themeConfig.CDN))
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Capitalize the first letter of comment name
|
||||||
|
*/
|
||||||
|
|
||||||
|
let { use } = themeConfig.comments
|
||||||
|
|
||||||
|
if (typeof use === 'string') {
|
||||||
|
use = use.split(',')
|
||||||
|
}
|
||||||
|
|
||||||
|
const newArray = use.map(item => item.toLowerCase().replace(/^\S/, s => s.toUpperCase()))
|
||||||
|
|
||||||
|
themeConfig.comments.use = newArray
|
||||||
})
|
})
|
||||||
@@ -22,8 +22,6 @@ function timeLineFn (args, content) {
|
|||||||
matches.push(match[2])
|
matches.push(match[2])
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(matches)
|
|
||||||
|
|
||||||
for (let i = 0; i < matches.length; i += 2) {
|
for (let i = 0; i < matches.length; i += 2) {
|
||||||
const tlChildTitle = hexo.render.renderSync({ text: matches[i], engine: 'markdown' })
|
const tlChildTitle = hexo.render.renderSync({ text: matches[i], engine: 'markdown' })
|
||||||
const tlChildContent = hexo.render.renderSync({ text: matches[i + 1], engine: 'markdown' })
|
const tlChildContent = hexo.render.renderSync({ text: matches[i + 1], engine: 'markdown' })
|
||||||
|
|||||||
@@ -117,7 +117,7 @@ if hexo-config('enter_transitions')
|
|||||||
|
|
||||||
#site-title,
|
#site-title,
|
||||||
#site-subtitle
|
#site-subtitle
|
||||||
animation: titlescale 1s
|
animation: titleScale 1s
|
||||||
|
|
||||||
#nav.show
|
#nav.show
|
||||||
animation: headerNoOpacity 1s
|
animation: headerNoOpacity 1s
|
||||||
@@ -187,7 +187,7 @@ if hexo-config('avatar.effect') == true
|
|||||||
margin-top: 0
|
margin-top: 0
|
||||||
opacity: 1
|
opacity: 1
|
||||||
|
|
||||||
@keyframes titlescale
|
@keyframes titleScale
|
||||||
0%
|
0%
|
||||||
opacity: 0
|
opacity: 0
|
||||||
transform: scale(.7)
|
transform: scale(.7)
|
||||||
|
|||||||
@@ -32,6 +32,7 @@
|
|||||||
--timeline-bg: $timeline-content-bg
|
--timeline-bg: $timeline-content-bg
|
||||||
--timeline-border-color: $timeline-border-color
|
--timeline-border-color: $timeline-border-color
|
||||||
--pseudo-hover: $pseudo-hover
|
--pseudo-hover: $pseudo-hover
|
||||||
|
--headline-presudo: #a0a0a0
|
||||||
|
|
||||||
body
|
body
|
||||||
position: relative
|
position: relative
|
||||||
|
|||||||
@@ -265,16 +265,17 @@
|
|||||||
+maxWidth900()
|
+maxWidth900()
|
||||||
max-height: calc(100vh - 140px)
|
max-height: calc(100vh - 140px)
|
||||||
|
|
||||||
.toc-child
|
&:not(.is-expand)
|
||||||
display: none
|
.toc-child
|
||||||
|
display: none
|
||||||
|
|
||||||
+maxWidth900()
|
+maxWidth900()
|
||||||
display: block !important
|
display: block !important
|
||||||
|
|
||||||
.toc-item
|
.toc-item
|
||||||
&.active
|
&.active
|
||||||
.toc-child
|
.toc-child
|
||||||
display: block
|
display: block
|
||||||
|
|
||||||
ol,
|
ol,
|
||||||
li
|
li
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ beautify()
|
|||||||
font-size: unit(fontsize, 'px')
|
font-size: unit(fontsize, 'px')
|
||||||
|
|
||||||
&:hover
|
&:hover
|
||||||
padding-left: unit(fontsize + 12, 'px')
|
padding-left: unit(fontsize + 18, 'px')
|
||||||
|
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
@@ -102,6 +102,32 @@ beautify()
|
|||||||
font-family: Monaco, 'Ubuntu Mono', monospace
|
font-family: Monaco, 'Ubuntu Mono', monospace
|
||||||
line-height: 1em
|
line-height: 1em
|
||||||
|
|
||||||
|
if hexo-config('anchor')
|
||||||
|
a.headerlink
|
||||||
|
&:after
|
||||||
|
@extend .fontawesomeIcon
|
||||||
|
float: right
|
||||||
|
color: var(--headline-presudo)
|
||||||
|
content: '\f0c1'
|
||||||
|
font-size: .95em
|
||||||
|
opacity: 0
|
||||||
|
transition: all .3s
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
&:after
|
||||||
|
color: var(--pseudo-hover)
|
||||||
|
|
||||||
|
h1,
|
||||||
|
h2,
|
||||||
|
h3,
|
||||||
|
h4,
|
||||||
|
h5,
|
||||||
|
h6
|
||||||
|
&:hover
|
||||||
|
a.headerlink
|
||||||
|
&:after
|
||||||
|
opacity: 1
|
||||||
|
|
||||||
ol,
|
ol,
|
||||||
ul
|
ul
|
||||||
ol,
|
ol,
|
||||||
|
|||||||
@@ -7,11 +7,7 @@
|
|||||||
transition: all .5s
|
transition: all .5s
|
||||||
|
|
||||||
#rightside-config-hide
|
#rightside-config-hide
|
||||||
transition: transform .4s
|
display: none
|
||||||
transform: translate(48px, 0)
|
|
||||||
|
|
||||||
&.show
|
|
||||||
transform: translate(0, 0) !important
|
|
||||||
|
|
||||||
& > div
|
& > div
|
||||||
& > button,
|
& > button,
|
||||||
@@ -39,3 +35,19 @@
|
|||||||
+maxWidth900()
|
+maxWidth900()
|
||||||
#hide-aside-btn
|
#hide-aside-btn
|
||||||
display: none
|
display: none
|
||||||
|
|
||||||
|
@keyframes rightside-item-in
|
||||||
|
0%
|
||||||
|
transform: translate(48px, 0)
|
||||||
|
|
||||||
|
100%
|
||||||
|
transform: translate(0, 0)
|
||||||
|
|
||||||
|
@keyframes rightside-item-out
|
||||||
|
0%
|
||||||
|
display: block
|
||||||
|
transform: translate(0, 0)
|
||||||
|
|
||||||
|
100%
|
||||||
|
display: none
|
||||||
|
transform: translate(48px, 0)
|
||||||
|
|||||||
@@ -9,7 +9,6 @@
|
|||||||
width: 600px
|
width: 600px
|
||||||
border-radius: 8px
|
border-radius: 8px
|
||||||
background: var(--search-bg)
|
background: var(--search-bg)
|
||||||
animation: titlescale .5s
|
|
||||||
|
|
||||||
+maxWidth768()
|
+maxWidth768()
|
||||||
top: 0
|
top: 0
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
open: () => {
|
open: () => {
|
||||||
btf.sidebarPaddingR()
|
btf.sidebarPaddingR()
|
||||||
document.body.style.overflow = 'hidden'
|
document.body.style.overflow = 'hidden'
|
||||||
btf.fadeIn(document.getElementById('menu-mask'), 0.5)
|
btf.animateIn(document.getElementById('menu-mask'), 'to_show 0.5s')
|
||||||
document.getElementById('sidebar-menus').classList.add('open')
|
document.getElementById('sidebar-menus').classList.add('open')
|
||||||
mobileSidebarOpen = true
|
mobileSidebarOpen = true
|
||||||
},
|
},
|
||||||
@@ -42,7 +42,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
const $body = document.body
|
const $body = document.body
|
||||||
$body.style.overflow = ''
|
$body.style.overflow = ''
|
||||||
$body.style.paddingRight = ''
|
$body.style.paddingRight = ''
|
||||||
btf.fadeOut(document.getElementById('menu-mask'), 0.5)
|
btf.animateOut(document.getElementById('menu-mask'), 'to_hide 0.5s')
|
||||||
document.getElementById('sidebar-menus').classList.remove('open')
|
document.getElementById('sidebar-menus').classList.remove('open')
|
||||||
mobileSidebarOpen = false
|
mobileSidebarOpen = false
|
||||||
}
|
}
|
||||||
@@ -315,82 +315,69 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* toc
|
* toc,anchor
|
||||||
*/
|
*/
|
||||||
const tocFn = function () {
|
const scrollFnToDo = function () {
|
||||||
const $cardTocLayout = document.getElementById('card-toc')
|
const isToc = GLOBAL_CONFIG_SITE.isToc
|
||||||
const $cardToc = $cardTocLayout.getElementsByClassName('toc-content')[0]
|
const isAnchor = GLOBAL_CONFIG.isAnchor
|
||||||
const $tocLink = $cardToc.querySelectorAll('.toc-link')
|
|
||||||
const $article = document.getElementById('article-container')
|
const $article = document.getElementById('article-container')
|
||||||
const $tocPercentage = $cardTocLayout.querySelector('.toc-percentage')
|
|
||||||
|
|
||||||
// main of scroll
|
if (!($article && (isToc || isAnchor))) return
|
||||||
window.tocScrollFn = function () {
|
|
||||||
return btf.throttle(function () {
|
|
||||||
const currentTop = window.scrollY || document.documentElement.scrollTop
|
|
||||||
scrollPercent(currentTop)
|
|
||||||
findHeadPosition(currentTop)
|
|
||||||
}, 100)()
|
|
||||||
}
|
|
||||||
window.addEventListener('scroll', tocScrollFn)
|
|
||||||
|
|
||||||
const scrollPercent = function (currentTop) {
|
let $tocLink, $cardToc, scrollPercent, autoScrollToc, isExpand
|
||||||
const docHeight = $article.clientHeight
|
|
||||||
const winHeight = document.documentElement.clientHeight
|
|
||||||
const headerHeight = $article.offsetTop
|
|
||||||
const contentMath = (docHeight > winHeight) ? (docHeight - winHeight) : (document.documentElement.scrollHeight - winHeight)
|
|
||||||
const scrollPercent = (currentTop - headerHeight) / (contentMath)
|
|
||||||
const scrollPercentRounded = Math.round(scrollPercent * 100)
|
|
||||||
const percentage = (scrollPercentRounded > 100) ? 100 : (scrollPercentRounded <= 0) ? 0 : scrollPercentRounded
|
|
||||||
$tocPercentage.textContent = percentage
|
|
||||||
}
|
|
||||||
|
|
||||||
// anchor
|
if (isToc) {
|
||||||
const isAnchor = GLOBAL_CONFIG.isanchor
|
const $cardTocLayout = document.getElementById('card-toc')
|
||||||
const updateAnchor = function (anchor) {
|
$cardToc = $cardTocLayout.getElementsByClassName('toc-content')[0]
|
||||||
if (window.history.replaceState && anchor !== window.location.hash) {
|
$tocLink = $cardToc.querySelectorAll('.toc-link')
|
||||||
if (!anchor) anchor = location.pathname
|
const $tocPercentage = $cardTocLayout.querySelector('.toc-percentage')
|
||||||
const title = GLOBAL_CONFIG_SITE.title
|
isExpand = $cardToc.classList.contains('is-expand')
|
||||||
window.history.replaceState({
|
|
||||||
url: location.href,
|
scrollPercent = currentTop => {
|
||||||
title: title
|
const docHeight = $article.clientHeight
|
||||||
}, title, anchor)
|
const winHeight = document.documentElement.clientHeight
|
||||||
|
const headerHeight = $article.offsetTop
|
||||||
|
const contentMath = (docHeight > winHeight) ? (docHeight - winHeight) : (document.documentElement.scrollHeight - winHeight)
|
||||||
|
const scrollPercent = (currentTop - headerHeight) / (contentMath)
|
||||||
|
const scrollPercentRounded = Math.round(scrollPercent * 100)
|
||||||
|
const percentage = (scrollPercentRounded > 100) ? 100 : (scrollPercentRounded <= 0) ? 0 : scrollPercentRounded
|
||||||
|
$tocPercentage.textContent = percentage
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
window.mobileToc = {
|
window.mobileToc = {
|
||||||
open: () => {
|
open: () => {
|
||||||
$cardTocLayout.style.cssText = 'animation: toc-open .3s; opacity: 1; right: 55px'
|
$cardTocLayout.style.cssText = 'animation: toc-open .3s; opacity: 1; right: 55px'
|
||||||
},
|
},
|
||||||
|
|
||||||
close: () => {
|
close: () => {
|
||||||
$cardTocLayout.style.animation = 'toc-close .2s'
|
$cardTocLayout.style.animation = 'toc-close .2s'
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
$cardTocLayout.style.cssText = "opacity:''; animation: ''; right: ''"
|
$cardTocLayout.style.cssText = "opacity:''; animation: ''; right: ''"
|
||||||
}, 100)
|
}, 100)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// toc元素點擊
|
// toc元素點擊
|
||||||
$cardToc.addEventListener('click', (e) => {
|
$cardToc.addEventListener('click', e => {
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
const $target = e.target.classList.contains('toc-link')
|
const $target = e.target.classList.contains('toc-link')
|
||||||
? e.target
|
? e.target
|
||||||
: 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) {
|
||||||
window.mobileToc.close()
|
window.mobileToc.close()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const autoScrollToc = function (item) {
|
autoScrollToc = item => {
|
||||||
const activePosition = item.getBoundingClientRect().top
|
const activePosition = item.getBoundingClientRect().top
|
||||||
const sidebarScrollTop = $cardToc.scrollTop
|
const sidebarScrollTop = $cardToc.scrollTop
|
||||||
if (activePosition > (document.documentElement.clientHeight - 100)) {
|
if (activePosition > (document.documentElement.clientHeight - 100)) {
|
||||||
$cardToc.scrollTop = sidebarScrollTop + 150
|
$cardToc.scrollTop = sidebarScrollTop + 150
|
||||||
}
|
}
|
||||||
if (activePosition < 100) {
|
if (activePosition < 100) {
|
||||||
$cardToc.scrollTop = sidebarScrollTop - 150
|
$cardToc.scrollTop = sidebarScrollTop - 150
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,7 +385,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
const list = $article.querySelectorAll('h1,h2,h3,h4,h5,h6')
|
const list = $article.querySelectorAll('h1,h2,h3,h4,h5,h6')
|
||||||
let detectItem = ''
|
let detectItem = ''
|
||||||
const findHeadPosition = function (top) {
|
const findHeadPosition = function (top) {
|
||||||
if ($tocLink.length === 0 || top === 0) {
|
if (top === 0) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,37 +394,50 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
|
|
||||||
list.forEach(function (ele, index) {
|
list.forEach(function (ele, index) {
|
||||||
if (top > btf.getEleTop(ele) - 80) {
|
if (top > btf.getEleTop(ele) - 80) {
|
||||||
currentId = '#' + encodeURI(ele.getAttribute('id'))
|
const id = ele.id
|
||||||
|
currentId = id ? '#' + encodeURI(id) : ''
|
||||||
currentIndex = index
|
currentIndex = index
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
if (detectItem === currentIndex) return
|
if (detectItem === currentIndex) return
|
||||||
|
|
||||||
if (isAnchor) updateAnchor(currentId)
|
if (isAnchor) btf.updateAnchor(currentId)
|
||||||
|
|
||||||
if (currentId === '') {
|
|
||||||
$cardToc.querySelectorAll('.active').forEach(i => { i.classList.remove('active') })
|
|
||||||
detectItem = currentIndex
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
detectItem = currentIndex
|
detectItem = currentIndex
|
||||||
|
|
||||||
$cardToc.querySelectorAll('.active').forEach(item => { item.classList.remove('active') })
|
if (isToc) {
|
||||||
const currentActive = $tocLink[currentIndex]
|
$cardToc.querySelectorAll('.active').forEach(i => { i.classList.remove('active') })
|
||||||
currentActive.classList.add('active')
|
|
||||||
|
|
||||||
setTimeout(() => {
|
if (currentId === '') {
|
||||||
autoScrollToc(currentActive)
|
return
|
||||||
}, 0)
|
}
|
||||||
|
|
||||||
let parent = currentActive.parentNode
|
const currentActive = $tocLink[currentIndex]
|
||||||
|
currentActive.classList.add('active')
|
||||||
|
|
||||||
for (; !parent.matches('.toc'); parent = parent.parentNode) {
|
setTimeout(() => {
|
||||||
if (parent.matches('li')) parent.classList.add('active')
|
autoScrollToc(currentActive)
|
||||||
|
}, 0)
|
||||||
|
|
||||||
|
if (isExpand) return
|
||||||
|
let parent = currentActive.parentNode
|
||||||
|
|
||||||
|
for (; !parent.matches('.toc'); parent = parent.parentNode) {
|
||||||
|
if (parent.matches('li')) parent.classList.add('active')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// main of scroll
|
||||||
|
window.tocScrollFn = function () {
|
||||||
|
return btf.throttle(function () {
|
||||||
|
const currentTop = window.scrollY || document.documentElement.scrollTop
|
||||||
|
isToc && scrollPercent(currentTop)
|
||||||
|
findHeadPosition(currentTop)
|
||||||
|
}, 100)()
|
||||||
|
}
|
||||||
|
window.addEventListener('scroll', tocScrollFn)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -473,12 +473,20 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
}
|
}
|
||||||
// handle some cases
|
// handle some cases
|
||||||
typeof utterancesTheme === 'function' && utterancesTheme()
|
typeof utterancesTheme === 'function' && utterancesTheme()
|
||||||
|
typeof changeGiscusTheme === 'function' && changeGiscusTheme()
|
||||||
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()
|
typeof runMermaid === 'function' && window.runMermaid()
|
||||||
},
|
},
|
||||||
showOrHideBtn: () => { // rightside 點擊設置 按鈕 展開
|
showOrHideBtn: () => { // rightside 點擊設置 按鈕 展開
|
||||||
document.getElementById('rightside-config-hide').classList.toggle('show')
|
const target = document.getElementById('rightside-config-hide')
|
||||||
|
if (window.rightSideIn) {
|
||||||
|
window.rightSideIn = false
|
||||||
|
btf.animateOut(target, 'rightside-item-out 0.5s')
|
||||||
|
} else {
|
||||||
|
window.rightSideIn = true
|
||||||
|
btf.animateIn(target, 'rightside-item-in 0.5s')
|
||||||
|
}
|
||||||
},
|
},
|
||||||
scrollToTop: () => { // Back to top
|
scrollToTop: () => { // Back to top
|
||||||
btf.scrollToDest(0, 500)
|
btf.scrollToDest(0, 500)
|
||||||
@@ -749,7 +757,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
|||||||
toggleCardCategory()
|
toggleCardCategory()
|
||||||
}
|
}
|
||||||
|
|
||||||
GLOBAL_CONFIG_SITE.isToc && tocFn()
|
scrollFnToDo()
|
||||||
GLOBAL_CONFIG_SITE.isHome && scrollDownInIndex()
|
GLOBAL_CONFIG_SITE.isHome && scrollDownInIndex()
|
||||||
addHighlightTool()
|
addHighlightTool()
|
||||||
GLOBAL_CONFIG.isPhotoFigcaption && addPhotoFigcaption()
|
GLOBAL_CONFIG.isPhotoFigcaption && addPhotoFigcaption()
|
||||||
|
|||||||
@@ -3,9 +3,10 @@ window.addEventListener('load', () => {
|
|||||||
const bodyStyle = document.body.style
|
const bodyStyle = document.body.style
|
||||||
bodyStyle.width = '100%'
|
bodyStyle.width = '100%'
|
||||||
bodyStyle.overflow = 'hidden'
|
bodyStyle.overflow = 'hidden'
|
||||||
document.querySelector('#algolia-search .search-dialog').style.display = 'block'
|
btf.animateIn(document.getElementById('search-mask'), 'to_show 0.5s')
|
||||||
document.querySelector('#algolia-search .ais-SearchBox-input').focus()
|
btf.animateIn(document.querySelector('#algolia-search .search-dialog'), 'titleScale 0.5s')
|
||||||
btf.fadeIn(document.getElementById('search-mask'), 0.5)
|
setTimeout(() => { document.querySelector('#algolia-search .ais-SearchBox-input').focus() }, 100)
|
||||||
|
|
||||||
// shortcut: ESC
|
// shortcut: ESC
|
||||||
document.addEventListener('keydown', function f (event) {
|
document.addEventListener('keydown', function f (event) {
|
||||||
if (event.code === 'Escape') {
|
if (event.code === 'Escape') {
|
||||||
@@ -19,10 +20,8 @@ window.addEventListener('load', () => {
|
|||||||
const bodyStyle = document.body.style
|
const bodyStyle = document.body.style
|
||||||
bodyStyle.width = ''
|
bodyStyle.width = ''
|
||||||
bodyStyle.overflow = ''
|
bodyStyle.overflow = ''
|
||||||
const $searchDialog = document.querySelector('#algolia-search .search-dialog')
|
btf.animateOut(document.querySelector('#algolia-search .search-dialog'), 'search_close .5s')
|
||||||
$searchDialog.style.animation = 'search_close .5s'
|
btf.animateOut(document.getElementById('search-mask'), 'to_hide 0.5s')
|
||||||
setTimeout(() => { $searchDialog.style.cssText = "display: none; animation: ''" }, 500)
|
|
||||||
btf.fadeOut(document.getElementById('search-mask'), 0.5)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const searchClickFn = () => {
|
const searchClickFn = () => {
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
window.addEventListener('load', () => {
|
window.addEventListener('load', () => {
|
||||||
let loadFlag = false
|
let loadFlag = false
|
||||||
const openSearch = function () {
|
const openSearch = () => {
|
||||||
document.body.style.cssText = 'width: 100%;overflow: hidden'
|
const bodyStyle = document.body.style
|
||||||
document.querySelector('#local-search .search-dialog').style.display = 'block'
|
bodyStyle.width = '100%'
|
||||||
document.querySelector('#local-search-input input').focus()
|
bodyStyle.overflow = 'hidden'
|
||||||
btf.fadeIn(document.getElementById('search-mask'), 0.5)
|
btf.animateIn(document.getElementById('search-mask'), 'to_show 0.5s')
|
||||||
|
btf.animateIn(document.querySelector('#local-search .search-dialog'), 'titleScale 0.5s')
|
||||||
|
setTimeout(() => { document.querySelector('#local-search-input input').focus() }, 100)
|
||||||
if (!loadFlag) {
|
if (!loadFlag) {
|
||||||
search(GLOBAL_CONFIG.localSearch.path)
|
search(GLOBAL_CONFIG.localSearch.path)
|
||||||
loadFlag = true
|
loadFlag = true
|
||||||
@@ -18,12 +20,12 @@ window.addEventListener('load', () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const closeSearch = function () {
|
const closeSearch = () => {
|
||||||
document.body.style.cssText = "width: '';overflow: ''"
|
const bodyStyle = document.body.style
|
||||||
const $searchDialog = document.querySelector('#local-search .search-dialog')
|
bodyStyle.width = ''
|
||||||
$searchDialog.style.animation = 'search_close .5s'
|
bodyStyle.overflow = ''
|
||||||
setTimeout(() => { $searchDialog.style.cssText = "display: none; animation: ''" }, 500)
|
btf.animateOut(document.querySelector('#local-search .search-dialog'), 'search_close .5s')
|
||||||
btf.fadeOut(document.getElementById('search-mask'), 0.5)
|
btf.animateOut(document.getElementById('search-mask'), 'to_hide 0.5s')
|
||||||
}
|
}
|
||||||
|
|
||||||
// click function
|
// click function
|
||||||
|
|||||||
@@ -58,16 +58,14 @@ const btf = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
snackbarShow: (text, showAction, duration) => {
|
snackbarShow: (text, showAction = false, duration = 2000) => {
|
||||||
const sa = (typeof showAction !== 'undefined') ? showAction : false
|
const { position, bgLight, bgDark } = GLOBAL_CONFIG.Snackbar
|
||||||
const dur = (typeof duration !== 'undefined') ? duration : 2000
|
const bg = document.documentElement.getAttribute('data-theme') === 'light' ? bgLight : bgDark
|
||||||
const position = GLOBAL_CONFIG.Snackbar.position
|
|
||||||
const bg = document.documentElement.getAttribute('data-theme') === 'light' ? GLOBAL_CONFIG.Snackbar.bgLight : GLOBAL_CONFIG.Snackbar.bgDark
|
|
||||||
Snackbar.show({
|
Snackbar.show({
|
||||||
text: text,
|
text: text,
|
||||||
backgroundColor: bg,
|
backgroundColor: bg,
|
||||||
showAction: sa,
|
showAction: showAction,
|
||||||
duration: dur,
|
duration: duration,
|
||||||
pos: position
|
pos: position
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -151,16 +149,18 @@ const btf = {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
fadeIn: (ele, time) => {
|
animateIn: (ele, text) => {
|
||||||
ele.style.cssText = `display:block;animation: to_show ${time}s`
|
ele.style.display = 'block'
|
||||||
|
ele.style.animation = text
|
||||||
},
|
},
|
||||||
|
|
||||||
fadeOut: (ele, time) => {
|
animateOut: (ele, text) => {
|
||||||
ele.addEventListener('animationend', function f () {
|
ele.addEventListener('animationend', function f () {
|
||||||
ele.style.cssText = "display: none; animation: '' "
|
ele.style.display = ''
|
||||||
|
ele.style.animation = ''
|
||||||
ele.removeEventListener('animationend', f)
|
ele.removeEventListener('animationend', f)
|
||||||
})
|
})
|
||||||
ele.style.animation = `to_hide ${time}s`
|
ele.style.animation = text
|
||||||
},
|
},
|
||||||
|
|
||||||
getParents: (elem, selector) => {
|
getParents: (elem, selector) => {
|
||||||
@@ -180,7 +180,6 @@ const btf = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @param {*} selector
|
* @param {*} selector
|
||||||
* @param {*} eleType the type of create element
|
* @param {*} eleType the type of create element
|
||||||
* @param {*} options object key: value
|
* @param {*} options object key: value
|
||||||
@@ -263,5 +262,16 @@ const btf = {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
updateAnchor: (anchor) => {
|
||||||
|
if (anchor !== window.location.hash) {
|
||||||
|
if (!anchor) anchor = location.pathname
|
||||||
|
const title = GLOBAL_CONFIG_SITE.title
|
||||||
|
window.history.replaceState({
|
||||||
|
url: location.href,
|
||||||
|
title: title
|
||||||
|
}, title, anchor)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user