breaking change: 重構 gallery 標籤外掛

improvement: 首頁社交圖標左右邊距調整
feat: 文章版權增加圖標
improvement: 重構 main.js 代碼
improvement: 優化 pjax 下的性能
fix: 修復子目錄下,pjax 跳轉 404 錯誤
feat: getScript 增加 attribute 配置
improvement: 優化手機端 toc 打開和關閉特效
improvement: 文章進入特效改為 transform, 優化 stylus
improvement: 目錄側邊欄出現滾動條時,元素不會被擠壓
feat: 文章左右對齊
improvement: 處理 waline 的 url 後面多 / 導致跨域的問題
fix: 修復夜間模式下,小屏幕的toc 滾動條顏色不明顯的 bug
fix: 修復設置字體超過17px時,toc 裏面的邊框異常的 bug
improvement: 優化語言文件部分用詞
improvement: disqus 和 disqusjs 的評論數獲取不到時,顯示為 0
improvement: disqusjs 的評論數改為 api 獲取
improvement: 代碼優化
improvement: 更新 plugins.yml
This commit is contained in:
Jerry
2023-08-06 18:43:39 +08:00
Unverified
parent 347f605def
commit 775942ae3e
63 changed files with 1267 additions and 1113 deletions
+10 -9
View File
@@ -1,15 +1,16 @@
/**
* stylus
* Stylus renderer
*/
'use strict'
hexo.extend.filter.register('stylus:renderer', function (style) {
const { highlight, prismjs } = hexo.config
style
.define('$highlight_enable', highlight && highlight.enable)
.define('$highlight_line_number', highlight && highlight.line_number)
.define('$prismjs_enable', prismjs && prismjs.enable)
.define('$prismjs_line_number', prismjs && prismjs.line_number)
// .import(this.source_dir.replace(/\\/g, '/') + '_data/css/*')
hexo.extend.filter.register('stylus:renderer', style => {
const { enable: highlightEnable, line_number: highlightLineNumber } = hexo.config.highlight
const { enable: prismjsEnable, line_number: prismjsLineNumber } = hexo.config.prismjs
style.define('$highlight_enable', highlightEnable)
.define('$highlight_line_number', highlightLineNumber)
.define('$prismjs_enable', prismjsEnable)
.define('$prismjs_line_number', prismjsLineNumber)
// .import(`${this.source_dir.replace(/\\/g, '/')}_data/css/*`)
})
+8 -10
View File
@@ -8,22 +8,20 @@
const urlFor = require('hexo-util').url_for.bind(hexo)
function lazyload (htmlContent) {
const lazyload = htmlContent => {
const bg = hexo.theme.config.lazyload.placeholder ? urlFor(hexo.theme.config.lazyload.placeholder) : 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
return htmlContent.replace(/(<img.*? src=)/ig, `$1 "${bg}" data-lazy-src=`)
}
hexo.extend.filter.register('after_render:html', function (data) {
const config = hexo.theme.config.lazyload
if (!config.enable) return
if (config.field !== 'site') return
return lazyload.call(this, data)
hexo.extend.filter.register('after_render:html', data => {
const { enable, field } = hexo.theme.config.lazyload
if (!enable || field !== 'site') return
return lazyload(data)
})
hexo.extend.filter.register('after_post_render', data => {
const config = hexo.theme.config.lazyload
if (!config.enable) return
if (config.field !== 'post') return
data.content = lazyload.call(this, data.content)
const { enable, field } = hexo.theme.config.lazyload
if (!enable || field !== 'post') return
data.content = lazyload(data.content)
return data
})
+12 -17
View File
@@ -5,40 +5,35 @@
'use strict'
hexo.extend.filter.register('before_post_render', function (data) {
hexo.extend.filter.register('before_post_render', data => {
const imgTestReg = /\.(png|jpe?g|gif|svg|webp)(\?.*)?$/i
let randomCover
let coverVal = data.cover
let { cover: coverVal, top_img: topImg } = data
// Add path to top_img and cover if post_asset_folder is enabled
if (hexo.config.post_asset_folder) {
const topImg = data.top_img
if (topImg && topImg.indexOf('/') === -1 && imgTestReg.test(topImg)) data.top_img = data.path + topImg
if (coverVal && coverVal.indexOf('/') === -1 && imgTestReg.test(coverVal)) data.cover = data.path + coverVal
if (topImg && topImg.indexOf('/') === -1 && imgTestReg.test(topImg)) data.top_img = `${data.path}${topImg}`
if (coverVal && coverVal.indexOf('/') === -1 && imgTestReg.test(coverVal)) data.cover = `${data.path}${coverVal}`
}
const randomCoverFn = () => {
const theme = hexo.theme.config
if (!(theme.cover && theme.cover.default_cover)) return false
if (!Array.isArray(theme.cover.default_cover)) return theme.cover.default_cover
const num = Math.floor(Math.random() * theme.cover.default_cover.length)
return theme.cover.default_cover[num]
const { cover: { default_cover: defaultCover } } = hexo.theme.config
if (!defaultCover) return false
if (!Array.isArray(defaultCover)) return defaultCover
const num = Math.floor(Math.random() * defaultCover.length)
return defaultCover[num]
}
if (coverVal === false) return data
// If cover is not set, use random cover
if (!coverVal) {
randomCover = randomCoverFn()
const randomCover = randomCoverFn()
data.cover = randomCover
coverVal = randomCover // update coverVal
}
if (coverVal) {
if (coverVal.indexOf('//') !== -1 || imgTestReg.test(coverVal)) {
data.cover_type = 'img'
return data
}
if (coverVal && (coverVal.indexOf('//') !== -1 || imgTestReg.test(coverVal))) {
data.cover_type = 'img'
}
return data
+3 -1
View File
@@ -31,6 +31,7 @@ hexo.extend.helper.register('aside_categories', function (categories, options) {
if (parent) { query.parent = parent } else { query.parent = { $exists: false } }
return categories.find(query).sort(orderby, order).filter((cat) => cat.length)
}
let expandBtn = ''
const hierarchicalList = (t, level, parent, topparent = true) => {
let result = ''
@@ -59,6 +60,7 @@ hexo.extend.helper.register('aside_categories', function (categories, options) {
}
if (isExpand && isTopParent && child) {
expandBtn = ' expandBtn'
result += `<i class="fas fa-caret-left ${expandClass}"></i>`
}
@@ -91,7 +93,7 @@ hexo.extend.helper.register('aside_categories', function (categories, options) {
<span>${this._p('aside.card_categories')}</span>
${moreButton()}
</div>
<ul class="card-category-list" id="aside-cat-list">
<ul class="card-category-list${expandBtn}" id="aside-cat-list">
${list[0]}
</ul>`
})
+26 -26
View File
@@ -1,36 +1,36 @@
hexo.extend.helper.register('findArchiveLength', function (func) {
const allPostsLength = this.site.posts.length
if (hexo.config.archive_generator && hexo.config.archive_generator.enable === false) return allPostsLength
const { yearly, monthly, daily } = hexo.config.archive_generator
hexo.extend.helper.register('getArchiveLength', function () {
const { archive_generator: archiveGenerator } = hexo.config
if (archiveGenerator && archiveGenerator.enable === false) return this.site.posts.length
const { yearly, monthly, daily } = archiveGenerator
const { year, month, day } = this.page
if (yearly === false || !year) return allPostsLength
if (yearly === false || !year) return this.site.posts.length
const posts = this.site.posts.sort('date')
const compareFunc = (type, y1, m1, d1, y2, m2, d2) => {
if (type === 'year') {
return y1 === y2
} else if (type === 'month') {
return y1 === y2 && m1 === m2
} else if (type === 'day') {
return y1 === y2 && m1 === m2 && d1 === d2
switch (type) {
case 'year':
return y1 === y2
case 'month':
return y1 === y2 && m1 === m2
case 'day':
return y1 === y2 && m1 === m2 && d1 === d2
default:
return false
}
}
const generateDateObj = (type) => {
const dateObj = []
let length = 0
posts.forEach(post => {
return posts.reduce((dateObj, post) => {
const date = post.date.clone()
const year = date.year()
const month = date.month() + 1
const day = date.date()
const lastData = dateObj[length - 1]
const lastData = dateObj[dateObj.length - 1]
if (!lastData || !compareFunc(type, lastData.year, lastData.month, lastData.day, year, month, day)) {
const name = type === 'year' ? year : type === 'month' ? `${year}-${month}` : `${year}-${month}-${day}`
length = dateObj.push({
dateObj.push({
name,
year,
month,
@@ -40,19 +40,19 @@ hexo.extend.helper.register('findArchiveLength', function (func) {
} else {
lastData.count++
}
})
return dateObj
return dateObj
}, [])
}
const data = func('createArchiveObj', () => {
const yearObj = yearly ? generateDateObj('year') : []
const monthObj = monthly ? generateDateObj('month') : []
const dayObj = daily ? generateDateObj('day') : []
const fullObj = [...yearObj, ...monthObj, ...dayObj]
return fullObj
const data = this.fragment_cache('createArchiveObj', () => {
const dateObjs = []
if (yearly) dateObjs.push(...generateDateObj('year'))
if (monthly) dateObjs.push(...generateDateObj('month'))
if (daily) dateObjs.push(...generateDateObj('day'))
return dateObjs
})
const name = month ? day ? `${year}-${month}-${day}` : `${year}-${month}` : year
const name = month ? (day ? `${year}-${month}-${day}` : `${year}-${month}`) : year
return data.find(item => item.name === name).count
})
+97 -80
View File
@@ -7,87 +7,99 @@
hexo.extend.helper.register('inject_head_js', function () {
const { darkmode, aside } = this.theme
const start = darkmode.start ? darkmode.start : 6
const end = darkmode.end ? darkmode.end : 18
const start = darkmode.start || 6
const end = darkmode.end || 18
const { theme_color } = hexo.theme.config
const themeColorLight = (theme_color && theme_color.enable && theme_color.meta_theme_color_light) || '#ffffff'
const themeColorDark = (theme_color && theme_color.enable && theme_color.meta_theme_color_dark) || '#0d0d0d'
const localStore = `
win.saveToLocal = {
set: function setWithExpiry(key, value, ttl) {
if (ttl === 0) return
const now = new Date()
const expiryDay = ttl * 86400000
const item = {
value: value,
expiry: now.getTime() + expiryDay,
const createLocalStore = () => {
return `
win.saveToLocal = {
set: (key, value, ttl) => {
if (ttl === 0) return
const now = Date.now()
const expiry = now + ttl * 86400000
const item = {
value,
expiry
}
localStorage.setItem(key, JSON.stringify(item))
},
get: key => {
const itemStr = localStorage.getItem(key)
if (!itemStr) {
return undefined
}
const item = JSON.parse(itemStr)
const now = Date.now()
if (now > item.expiry) {
localStorage.removeItem(key)
return undefined
}
return item.value
}
localStorage.setItem(key, JSON.stringify(item))
},
get: function getWithExpiry(key) {
const itemStr = localStorage.getItem(key)
if (!itemStr) {
return undefined
}
const item = JSON.parse(itemStr)
const now = new Date()
if (now.getTime() > item.expiry) {
localStorage.removeItem(key)
return undefined
}
return item.value
}
}
`
`
}
// https://stackoverflow.com/questions/16839698/jquery-getscript-alternative-in-native-javascript
const getScript = `
win.getScript = url => new Promise((resolve, reject) => {
const script = document.createElement('script')
script.src = url
script.async = true
script.onerror = reject
script.onload = script.onreadystatechange = function() {
const loadState = this.readyState
if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
script.onload = script.onreadystatechange = null
resolve()
}
document.head.appendChild(script)
})
`
const createGetScript = () => {
return `
win.getScript = (url, attr = {}) => new Promise((resolve, reject) => {
const script = document.createElement('script')
script.src = url
script.async = true
script.onerror = reject
script.onload = script.onreadystatechange = function() {
const loadState = this.readyState
if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
script.onload = script.onreadystatechange = null
resolve()
}
const getCSS = `
win.getCSS = (url,id = false) => new Promise((resolve, reject) => {
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = url
if (id) link.id = id
link.onerror = reject
link.onload = link.onreadystatechange = function() {
const loadState = this.readyState
if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
link.onload = link.onreadystatechange = null
resolve()
}
document.head.appendChild(link)
})
`
Object.keys(attr).forEach(key => {
script.setAttribute(key, attr[key])
})
let darkmodeJs = ''
if (darkmode.enable) {
darkmodeJs = `
win.activateDarkMode = function () {
document.head.appendChild(script)
})
`
}
const createGetCSS = () => {
return `
win.getCSS = (url, id = false) => new Promise((resolve, reject) => {
const link = document.createElement('link')
link.rel = 'stylesheet'
link.href = url
if (id) link.id = id
link.onerror = reject
link.onload = link.onreadystatechange = function() {
const loadState = this.readyState
if (loadState && loadState !== 'loaded' && loadState !== 'complete') return
link.onload = link.onreadystatechange = null
resolve()
}
document.head.appendChild(link)
})
`
}
const createDarkmodeJs = () => {
if (!darkmode.enable) return ''
let darkmodeJs = `
win.activateDarkMode = () => {
document.documentElement.setAttribute('data-theme', 'dark')
if (document.querySelector('meta[name="theme-color"]') !== null) {
document.querySelector('meta[name="theme-color"]').setAttribute('content', '${themeColorDark}')
}
}
win.activateLightMode = function () {
win.activateLightMode = () => {
document.documentElement.setAttribute('data-theme', 'light')
if (document.querySelector('meta[name="theme-color"]') !== null) {
document.querySelector('meta[name="theme-color"]').setAttribute('content', '${themeColorLight}')
@@ -114,7 +126,7 @@ hexo.extend.helper.register('inject_head_js', function () {
const isNight = hour <= ${start} || hour >= ${end}
isNight ? activateDarkMode() : activateLightMode()
}
window.matchMedia('(prefers-color-scheme: dark)').addListener(function (e) {
window.matchMedia('(prefers-color-scheme: dark)').addListener(e => {
if (saveToLocal.get('theme') === undefined) {
e.matches ? activateDarkMode() : activateLightMode()
}
@@ -133,15 +145,18 @@ hexo.extend.helper.register('inject_head_js', function () {
`
} else {
darkmodeJs += `
if (t === 'dark') activateDarkMode()
else if (t === 'light') activateLightMode()
`
if (t === 'dark') activateDarkMode()
else if (t === 'light') activateLightMode()
`
}
return darkmodeJs
}
let asideStatus = ''
if (aside.enable && aside.button) {
asideStatus = `
const createAsideStatus = () => {
if (!aside.enable || !aside.button) return ''
return `
const asideStatus = saveToLocal.get('aside-status')
if (asideStatus !== undefined) {
if (asideStatus === 'hide') {
@@ -153,14 +168,16 @@ hexo.extend.helper.register('inject_head_js', function () {
`
}
const detectApple = `
const detectApple = () => {
if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
document.documentElement.classList.add('apple')
const createDetectApple = () => {
return `
const detectApple = () => {
if(/iPad|iPhone|iPod|Macintosh/.test(navigator.userAgent)){
document.documentElement.classList.add('apple')
}
}
}
detectApple()
detectApple()
`
}
return `<script>(win=>{${localStore + getScript + getCSS + darkmodeJs + asideStatus + detectApple}})(window)</script>`
return `<script>(win=>{${createLocalStore() + createGetScript() + createGetCSS() + createDarkmodeJs() + createAsideStatus() + createDetectApple()}})(window)</script>`
})
+2 -16
View File
@@ -1,10 +1,3 @@
/**
* Butterfly
* @example
* page_description()
* cloudTags(source, minfontsize, maxfontsize, limit)
*/
'use strict'
const { stripHTML, escapeHTML, prettyUrls } = require('hexo-util')
@@ -60,12 +53,8 @@ hexo.extend.helper.register('md5', function (path) {
})
hexo.extend.helper.register('injectHtml', function (data) {
let result = ''
if (!data) return ''
for (let i = 0; i < data.length; i++) {
result += data[i]
}
return result
return data.join('')
})
hexo.extend.helper.register('findArchivesTitle', function (page, menu, date) {
@@ -95,8 +84,5 @@ hexo.extend.helper.register('findArchivesTitle', function (page, menu, date) {
hexo.extend.helper.register('isImgOrUrl', function (path) {
const imgTestReg = /\.(png|jpe?g|gif|svg|webp)(\?.*)?$/i
if (path.indexOf('//') !== -1 || imgTestReg.test(path)) {
return true
}
return false
return path.indexOf('//') !== -1 || imgTestReg.test(path)
})
+13 -16
View File
@@ -2,8 +2,9 @@
* Butterfly
* galleryGroup and gallery
* {% galleryGroup [name] [descr] [url] [img] %}
* {% gallery [lazyload],[rowHeight],[limit] %}
* {% gallery url,[url],[lazyload],[rowHeight],[limit] %}
*
* {% gallery [button],%}
* {% gallery url,[url],[button]%}
*/
'use strict'
@@ -11,14 +12,15 @@
const urlFor = require('hexo-util').url_for.bind(hexo)
const gallery = (args, content) => {
const { data, languages } = hexo.theme.i18n
args = args.join(' ').split(',')
let rowHeight, limit, lazyload, type, dataStr
let button = false
let type = 'data'
let dataStr = ''
if (args[0] === 'url') {
[type, dataStr, lazyload, rowHeight = 220, limit = 10] = args // url,[link],[lazyload],[rowHeight],[limit]
[type, dataStr, button] = args // url,[link],[lazyload]
} else {
[lazyload, rowHeight = 220, limit = 10] = args // [lazyload],[rowHeight],[limit]
[button] = args // [lazyload]
const regex = /!\[(.*?)\]\(([^\s]*)\s*(?:["'](.*?)["']?)?\s*\)/g
let m
const arr = []
@@ -36,14 +38,10 @@ const gallery = (args, content) => {
dataStr = JSON.stringify(arr)
}
type = type ? ' url' : ' data'
const lazyloadClass = lazyload === 'true' ? 'lazyload' : ''
return `<div class="gallery">
<div class="fj-gallery ${lazyloadClass + type}" data-rowHeight="${rowHeight}" data-limit="${limit}">
<span class="gallery-data">${dataStr}</span>
</div>
<button class="gallery-load-more"><span>${data[languages[0]].load_more}</span><i class="fa-solid fa-arrow-down"></i></button>
return `<div class="gallery-container" data-type="${type}" data-button="${button}">
<div class="gallery-data">${dataStr}</div>
<div class="gallery-items">
</div>
</div>`
}
@@ -52,8 +50,7 @@ const galleryGroup = args => {
const imgUrl = urlFor(img)
const urlLink = urlFor(url)
return `
<figure class="gallery-group">
return `<figure class="gallery-group">
<img class="gallery-group-img no-lightbox" src='${imgUrl}' alt="Group Image Gallery">
<figcaption>
<div class="gallery-group-name">${name}</div>
+12 -7
View File
@@ -16,6 +16,7 @@ const postTabs = (args, content) => {
let tabId = 0
let tabNav = ''
let tabContent = ''
let noDefault = true
!tabName && hexo.log.warn('Tabs block must have unique name!')
@@ -41,17 +42,21 @@ const postTabs = (args, content) => {
const icon = tabIcon.trim()
tabIcon.length > 0 && (tabIcon = `<i class="${icon}"${isOnlyicon}></i>`)
const toTop = '<button type="button" class="tab-to-top" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button>'
const isActive = (tabActive > 0 && tabActive === tabId) || (tabActive === 0 && tabId === 1) ? ' active' : ''
tabNav += `<li class="tab${isActive}"><button type="button" data-href="#${tabHref}">${tabIcon + tabCaption.trim()}</button></li>`
tabContent += `<div class="tab-item-content${isActive}" id="${tabHref}">${postContent + toTop}</div>`
let isActive = ''
if ((tabActive > 0 && tabActive === tabId) || (tabActive === 0 && tabId === 1)) {
isActive = ' active'
noDefault = false
}
tabNav += `<button type="button" class="tab ${isActive}" data-href="${tabHref}">${tabIcon + tabCaption.trim()}</button>`
tabContent += `<div class="tab-item-content${isActive}" id="${tabHref}">${postContent}</div>`
}
tabNav = `<ul class="nav-tabs">${tabNav}</ul>`
const toTop = '<div class="tab-to-top"><button type="button" aria-label="scroll to top"><i class="fas fa-arrow-up"></i></button></div>'
tabNav = `<ul class="nav-tabs${noDefault ? ' no-default' : ''}">${tabNav}</ul>`
tabContent = `<div class="tab-contents">${tabContent}</div>`
return `<div class="tabs" id="${tabName.toLowerCase().split(' ').join('-')}">${tabNav + tabContent}</div>`
return `<div class="tabs" id="${tabName.toLowerCase().split(' ').join('-')}">${tabNav + tabContent + toTop}</div>`
}
hexo.extend.tag.register('tabs', postTabs, { ends: true })