feat: 不蒜子可配置 CDN closed #1132

feat: gallery 標簽外掛增加圖片懶加載,增加 lazyload rowHeight 和 limit 屬性配置
feat: 可設置固定導航欄 closed #1150
fix: 修復開啟懶加載後,再使用 flink_url 的方式引入友鏈數據,友鏈頭像有可能不顯示的 bug closed #1146
fix: 修復閲讀模式下,代碼塊的背景顏色仍顯示彩色背景的 bug (自定義代碼塊) closed #1139
improvement: 搜索結果換行時不會拆分單詞顯示
improvement: 優化搜索結果顯示滾動條位置
improvement: css/js 優化
This commit is contained in:
Jerry
2023-01-14 00:36:34 +08:00
Unverified
parent 0f915f47ef
commit 9a3b41912a
28 changed files with 244 additions and 135 deletions
+73 -47
View File
@@ -232,14 +232,42 @@ document.addEventListener('DOMContentLoaded', function () {
* justified-gallery 圖庫排版
*/
const runJustifiedGallery = function (ele) {
ele.forEach(item => {
const $imgList = item.querySelectorAll('img')
$imgList.forEach(i => {
const dataLazySrc = i.getAttribute('data-lazy-src')
if (dataLazySrc) i.src = dataLazySrc
btf.wrap(i, 'div', { class: 'fj-gallery-item' })
const htmlStr = arr => {
let str = ''
const replaceDq = str => str.replace(/"/g, '"') // replace double quotes to "
arr.forEach(i => {
const alt = i.alt ? `alt="${replaceDq(i.alt)}"` : ''
const title = i.title ? `title="${replaceDq(i.title)}"` : ''
str += `<div class="fj-gallery-item"><img src="${i.url}" ${alt + title}"></div>`
})
return str
}
const lazyloadFn = (i, arr) => {
const loadItem = i.getAttribute('data-limit')
const arrLength = arr.length
if (arrLength > loadItem) i.insertAdjacentHTML('beforeend', htmlStr(arr.splice(0, loadItem)))
else {
i.insertAdjacentHTML('beforeend', htmlStr(arr))
i.classList.remove('lazyload')
}
return arrLength > loadItem ? loadItem : arrLength
}
ele.forEach(item => {
const arr = JSON.parse(item.querySelector('.gallery-data').textContent)
if (!item.classList.contains('lazyload')) item.innerHTML = htmlStr(arr)
else {
lazyloadFn(item, arr)
const limit = item.getAttribute('data-limit')
const clickBtnFn = () => {
const lastItemLength = lazyloadFn(item, arr)
fjGallery(item, 'appendImages', item.querySelectorAll(`.fj-gallery-item:nth-last-child(-n+${lastItemLength})`))
btf.loadLightbox(item.querySelectorAll('img'))
lastItemLength < limit && item.nextElementSibling.removeEventListener('click', clickBtnFn)
}
item.nextElementSibling.addEventListener('click', clickBtnFn)
}
})
if (window.fjGallery) {
@@ -258,13 +286,12 @@ document.addEventListener('DOMContentLoaded', function () {
* rightside scroll percent
*/
const rightsideScrollPercent = currentTop => {
const perNum = btf.getScrollPercent(currentTop,document.body)
const perNum = btf.getScrollPercent(currentTop, document.body)
const $goUp = document.getElementById('go-up')
if ( perNum < 95 ) {
if (perNum < 95) {
$goUp.classList.add('show-percent')
$goUp.querySelector('.scroll-percent').textContent = perNum
}
else {
} else {
$goUp.classList.remove('show-percent')
}
}
@@ -297,41 +324,40 @@ document.addEventListener('DOMContentLoaded', function () {
const isShowPercent = GLOBAL_CONFIG.percent.rightside
const scrollTask = btf.throttle(() => {
const currentTop = window.scrollY || document.documentElement.scrollTop
const isDown = scrollDirection(currentTop)
if (currentTop > 56) {
if (isDown) {
if ($header.classList.contains('nav-visible')) $header.classList.remove('nav-visible')
if (isChatBtnShow && isChatShow === true) {
chatBtnHide()
isChatShow = false
}
} else {
if (!$header.classList.contains('nav-visible')) $header.classList.add('nav-visible')
if (isChatBtnHide && isChatShow === false) {
chatBtnShow()
isChatShow = true
}
}
$header.classList.add('nav-fixed')
if (window.getComputedStyle($rightside).getPropertyValue('opacity') === '0') {
$rightside.style.cssText = 'opacity: 0.8; transform: translateX(-58px)'
const currentTop = window.scrollY || document.documentElement.scrollTop
const isDown = scrollDirection(currentTop)
if (currentTop > 56) {
if (isDown) {
if ($header.classList.contains('nav-visible')) $header.classList.remove('nav-visible')
if (isChatBtnShow && isChatShow === true) {
chatBtnHide()
isChatShow = false
}
} else {
if (currentTop === 0) {
$header.classList.remove('nav-fixed', 'nav-visible')
if (!$header.classList.contains('nav-visible')) $header.classList.add('nav-visible')
if (isChatBtnHide && isChatShow === false) {
chatBtnShow()
isChatShow = true
}
$rightside.style.cssText = "opacity: ''; transform: ''"
}
isShowPercent && rightsideScrollPercent(currentTop)
if (document.body.scrollHeight <= innerHeight) {
$header.classList.add('nav-fixed')
if (window.getComputedStyle($rightside).getPropertyValue('opacity') === '0') {
$rightside.style.cssText = 'opacity: 0.8; transform: translateX(-58px)'
}
} else {
if (currentTop === 0) {
$header.classList.remove('nav-fixed', 'nav-visible')
}
$rightside.style.cssText = "opacity: ''; transform: ''"
}
isShowPercent && rightsideScrollPercent(currentTop)
if (document.body.scrollHeight <= innerHeight) {
$rightside.style.cssText = 'opacity: 0.8; transform: translateX(-58px)'
}
}, 200)
}, 200)
window.scrollCollect = scrollTask
window.addEventListener('scroll', scrollCollect)
@@ -444,14 +470,14 @@ document.addEventListener('DOMContentLoaded', function () {
}
// main of scroll
window.tocScrollFn = btf.throttle(()=>{
const currentTop = window.scrollY || document.documentElement.scrollTop
if (isToc && GLOBAL_CONFIG.percent.toc) {
$tocPercentage.textContent = btf.getScrollPercent(currentTop,$article)
}
findHeadPosition(currentTop)
}, 100)
window.tocScrollFn = btf.throttle(() => {
const currentTop = window.scrollY || document.documentElement.scrollTop
if (isToc && GLOBAL_CONFIG.percent.toc) {
$tocPercentage.textContent = btf.getScrollPercent(currentTop, $article)
}
findHeadPosition(currentTop)
}, 100)
window.addEventListener('scroll', tocScrollFn)
}
+13 -13
View File
@@ -56,7 +56,7 @@ window.addEventListener('load', () => {
post = '...'
}
let matchContent = pre + content.substring(start, end) + post
const matchContent = pre + content.substring(start, end) + post
return matchContent
}
@@ -68,10 +68,11 @@ window.addEventListener('load', () => {
const search = instantsearch({
indexName: algolia.indexName,
/* global algoliasearch */
searchClient: algoliasearch(algolia.appId, algolia.apiKey),
searchFunction(helper) {
searchFunction (helper) {
helper.state.query && helper.search()
},
}
})
const configure = instantsearch.widgets.configure({
@@ -89,16 +90,16 @@ window.addEventListener('load', () => {
const hits = instantsearch.widgets.hits({
container: '#algolia-hits',
templates: {
item(data) {
item (data) {
const link = data.permalink ? data.permalink : (GLOBAL_CONFIG.root + data.path)
const result = data._highlightResult
const content = result.contentStripTruncate
? cutContent(result.contentStripTruncate.value)
: result.contentStrip
? cutContent(result.contentStrip.value)
: result.content
? cutContent(result.content.value)
: ''
? cutContent(result.contentStripTruncate.value)
: result.contentStrip
? cutContent(result.contentStrip.value)
: result.content
? cutContent(result.content.value)
: ''
return `
<a href="${link}" class="algolia-hit-item-link">
${result.title.value || 'no-title'}
@@ -130,7 +131,7 @@ window.addEventListener('load', () => {
})
const powerBy = instantsearch.widgets.poweredBy({
container: '#algolia-info > .algolia-poweredBy',
container: '#algolia-info > .algolia-poweredBy'
})
const pagination = instantsearch.widgets.pagination({
@@ -144,8 +145,7 @@ window.addEventListener('load', () => {
}
})
search.addWidgets([configure,searchBox,hits,stats,powerBy,pagination]) // add the widgets to the instantsearch instance
search.addWidgets([configure, searchBox, hits, stats, powerBy, pagination]) // add the widgets to the instantsearch instance
search.start()
+2 -2
View File
@@ -56,7 +56,7 @@ window.addEventListener('load', () => {
const res = await response.text()
const t = await new window.DOMParser().parseFromString(res, 'text/xml')
const a = await t
data = [...a.querySelectorAll('entry')].map(item =>{
data = [...a.querySelectorAll('entry')].map(item => {
return {
title: item.querySelector('title').textContent,
content: item.querySelector('content') && item.querySelector('content').textContent,
@@ -154,7 +154,7 @@ window.addEventListener('load', () => {
// highlight all keywords
keywords.forEach(keyword => {
let regexStr = keyword
const specialRegex = new RegExp("[^\\w\\s]+") // match special characters
const specialRegex = /[^\w\s]+/ // match special characters
if (keyword.length === 1 && specialRegex.test(keyword)) {
regexStr = `\\${keyword}`
}
+11 -11
View File
@@ -62,10 +62,10 @@ const btf = {
const { position, bgLight, bgDark } = GLOBAL_CONFIG.Snackbar
const bg = document.documentElement.getAttribute('data-theme') === 'light' ? bgLight : bgDark
Snackbar.show({
text: text,
text,
backgroundColor: bg,
showAction: showAction,
duration: duration,
showAction,
duration,
pos: position,
customClass: 'snackbar-css'
})
@@ -88,7 +88,7 @@ const btf = {
const minuteCount = dateDiff / minute
if (monthCount > 12) {
result = datePost.toISOString().slice(0,10)
result = datePost.toISOString().slice(0, 10)
} else if (monthCount >= 1) {
result = parseInt(monthCount) + ' ' + GLOBAL_CONFIG.date_suffix.month
} else if (dayCount >= 1) {
@@ -186,12 +186,12 @@ const btf = {
* @param {*} options object key: value
*/
wrap: (selector, eleType, options) => {
const creatEle = document.createElement(eleType)
const createEle = document.createElement(eleType)
for (const [key, value] of Object.entries(options)) {
creatEle.setAttribute(key, value)
createEle.setAttribute(key, value)
}
selector.parentNode.insertBefore(creatEle, selector)
creatEle.appendChild(selector)
selector.parentNode.insertBefore(createEle, selector)
createEle.appendChild(selector)
},
unwrap: el => {
@@ -255,7 +255,7 @@ const btf = {
if (!btf.isHidden(i)) {
fjGallery(i, {
itemSelector: '.fj-gallery-item',
rowHeight: 220,
rowHeight: i.getAttribute('data-rowHeight'),
gutter: 4,
onJustify: function () {
this.$container.style.opacity = '1'
@@ -271,12 +271,12 @@ const btf = {
const title = GLOBAL_CONFIG_SITE.title
window.history.replaceState({
url: location.href,
title: title
title
}, title, anchor)
}
},
getScrollPercent: (currentTop,ele) => {
getScrollPercent: (currentTop, ele) => {
const docHeight = ele.clientHeight
const winHeight = document.documentElement.clientHeight
const headerHeight = ele.offsetTop