mirror of
https://github.com/jerryc127/hexo-theme-butterfly.git
synced 2026-10-04 06:22:00 +08:00
update
This commit is contained in:
commit
1b1a26e382
36 files changed
+1101
-967
No files matched your search
+5
-1
@@ -659,7 +659,7 @@ artalk:
|
|||||||
# --------------------------------------
|
# --------------------------------------
|
||||||
|
|
||||||
chat:
|
chat:
|
||||||
# Choose: chatra/tidio/crisp
|
# Choose: chatra/tidio/crisp/knocket
|
||||||
# Leave it empty if you don't need chat
|
# Leave it empty if you don't need chat
|
||||||
use:
|
use:
|
||||||
# Chat Button [recommend]
|
# Chat Button [recommend]
|
||||||
@@ -680,6 +680,10 @@ tidio:
|
|||||||
crisp:
|
crisp:
|
||||||
website_id:
|
website_id:
|
||||||
|
|
||||||
|
# https://trtc.io/solutions/knocket
|
||||||
|
knocket:
|
||||||
|
identifier:
|
||||||
|
|
||||||
# --------------------------------------
|
# --------------------------------------
|
||||||
# Analysis
|
# Analysis
|
||||||
# --------------------------------------
|
# --------------------------------------
|
||||||
|
|||||||
+125
-148
@@ -54,6 +54,8 @@
|
|||||||
(() => {
|
(() => {
|
||||||
const limitConfig = !{ JSON.stringify(page.limit || {}) }
|
const limitConfig = !{ JSON.stringify(page.limit || {}) }
|
||||||
|
|
||||||
|
const escapeHtml = str => String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"').replace(/'/g, ''')
|
||||||
|
|
||||||
const sortDataByDate = data => data.sort((a, b) => new Date(b.date) - new Date(a.date))
|
const sortDataByDate = data => data.sort((a, b) => new Date(b.date) - new Date(a.date))
|
||||||
|
|
||||||
const filterDataByLimit = (data, limit) => {
|
const filterDataByLimit = (data, limit) => {
|
||||||
@@ -64,13 +66,10 @@
|
|||||||
return data.filter(item => new Date(item.date) >= limitDate)
|
return data.filter(item => new Date(item.date) >= limitDate)
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
};
|
}
|
||||||
|
|
||||||
const formatToTimeZone = (date) => {
|
const dateFormatter = new Intl.DateTimeFormat('en-GB', {
|
||||||
const fullDate = date.length === 10 ? `${date} 00:00:00` : date
|
timeZone: !{JSON.stringify(config.timezone || '')} || Intl.DateTimeFormat().resolvedOptions().timeZone,
|
||||||
const visitorTimeZone = '#{config.timezone}' || Intl.DateTimeFormat().resolvedOptions().timeZone
|
|
||||||
const options = {
|
|
||||||
timeZone: visitorTimeZone,
|
|
||||||
year: 'numeric',
|
year: 'numeric',
|
||||||
month: '2-digit',
|
month: '2-digit',
|
||||||
day: '2-digit',
|
day: '2-digit',
|
||||||
@@ -78,35 +77,36 @@
|
|||||||
minute: '2-digit',
|
minute: '2-digit',
|
||||||
second: '2-digit',
|
second: '2-digit',
|
||||||
hour12: false
|
hour12: false
|
||||||
}
|
})
|
||||||
const [day, month, year, hour, minute, second] = new Intl.DateTimeFormat('en-GB', options)
|
|
||||||
.format(new Date(fullDate))
|
const formatToTimeZone = (date) => {
|
||||||
.match(/\d+/g)
|
const fullDate = date.length === 10 ? `${date} 00:00:00` : date
|
||||||
|
const [day, month, year, hour, minute, second] = dateFormatter.format(new Date(fullDate)).match(/\d+/g)
|
||||||
return `${year}-${month}-${day} ${hour}:${minute}:${second}`
|
return `${year}-${month}-${day} ${hour}:${minute}:${second}`
|
||||||
}
|
}
|
||||||
|
|
||||||
const addLazyload = str => {
|
const addLazyload = str => {
|
||||||
const config = {
|
const lazyConfig = {
|
||||||
enable: !{Boolean(enable)},
|
enable: !{Boolean(enable)},
|
||||||
native: !{Boolean(native)},
|
native: !{Boolean(native)},
|
||||||
field: '!{field}',
|
field: !{JSON.stringify(field || '')},
|
||||||
placeholder: '!{url_for(placeholder)}',
|
placeholder: !{JSON.stringify(url_for(placeholder))},
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config.enable || config.field !== 'site') return str
|
if (!lazyConfig.enable || lazyConfig.field !== 'site' || str.indexOf('<img') === -1) return str
|
||||||
const parser = new DOMParser()
|
const parser = new DOMParser()
|
||||||
const doc = parser.parseFromString(str, 'text/html')
|
const doc = parser.parseFromString(str, 'text/html')
|
||||||
const images = doc.querySelectorAll('img')
|
const images = doc.querySelectorAll('img')
|
||||||
|
|
||||||
images.forEach(img => {
|
images.forEach(img => {
|
||||||
if (config.native) {
|
if (lazyConfig.native) {
|
||||||
img.setAttribute('loading', 'lazy')
|
img.setAttribute('loading', 'lazy')
|
||||||
} else {
|
} else {
|
||||||
const src = img.getAttribute('src')
|
const src = img.getAttribute('src')
|
||||||
img.setAttribute('data-lazy-src', src)
|
img.setAttribute('data-lazy-src', src)
|
||||||
|
|
||||||
if (config.placeholder) {
|
if (lazyConfig.placeholder) {
|
||||||
img.setAttribute('src', config.placeholder)
|
img.setAttribute('src', lazyConfig.placeholder)
|
||||||
} else {
|
} else {
|
||||||
img.removeAttribute('src')
|
img.removeAttribute('src')
|
||||||
}
|
}
|
||||||
@@ -119,19 +119,18 @@
|
|||||||
const itemsPerPage = 8
|
const itemsPerPage = 8
|
||||||
let totalPages = 0
|
let totalPages = 0
|
||||||
let data = []
|
let data = []
|
||||||
let inputEventsAttached = false // Flag to mark if input event listeners have been added
|
|
||||||
|
|
||||||
const renderData = (dataSlice) => {
|
const renderData = (dataSlice) => {
|
||||||
const content = dataSlice.map(item => {
|
const content = dataSlice.map(item => {
|
||||||
const formattedDate = formatToTimeZone(item.date)
|
const formattedDate = formatToTimeZone(item.date)
|
||||||
const tags = item.tags && item.tags.map(tag => `<span class="shuoshuo-tag">${tag}</span>`).join('') || ''
|
const tags = item.tags && item.tags.map(tag => `<span class="shuoshuo-tag">${escapeHtml(tag)}</span>`).join('') || ''
|
||||||
const commentButton = item.key && !{commentsJsLoad}
|
const commentButton = item.key && !{commentsJsLoad || false}
|
||||||
? `<div class="shuoshuo-comment-btn" onclick="addCommentToShuoshuo(event)">
|
? `<div class="shuoshuo-comment-btn" onclick="addCommentToShuoshuo(event)">
|
||||||
<i class="fa-solid fa-comments"></i>
|
<i class="fa-solid fa-comments"></i>
|
||||||
</div>`
|
</div>`
|
||||||
: ''
|
: ''
|
||||||
const commentContainer = item.key
|
const commentContainer = item.key
|
||||||
? `<div class="shuoshuo-comment no-comment" data-key="${item.key}"></div>`
|
? `<div class="shuoshuo-comment no-comment" data-key="${escapeHtml(item.key)}"></div>`
|
||||||
: ''
|
: ''
|
||||||
|
|
||||||
return `
|
return `
|
||||||
@@ -139,10 +138,10 @@
|
|||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="shuoshuo-item-header">
|
<div class="shuoshuo-item-header">
|
||||||
<div class="shuoshuo-avatar">
|
<div class="shuoshuo-avatar">
|
||||||
<img class="no-lightbox" src="${item.avatar || '!{url_for(theme.avatar.img)}'}">
|
<img class="no-lightbox" src="${item.avatar ? escapeHtml(item.avatar) : !{JSON.stringify(url_for(theme.avatar.img))}}">
|
||||||
</div>
|
</div>
|
||||||
<div class="shuoshuo-info">
|
<div class="shuoshuo-info">
|
||||||
<div class="shuoshuo-author">${item.author || '!{config.author}'}</div>
|
<div class="shuoshuo-author">${item.author ? escapeHtml(item.author) : !{JSON.stringify(config.author || '')}}</div>
|
||||||
<time class="shuoshuo-date" title="${formattedDate}">
|
<time class="shuoshuo-date" title="${formattedDate}">
|
||||||
${btf.diffDate(formattedDate, true)}
|
${btf.diffDate(formattedDate, true)}
|
||||||
</time>
|
</time>
|
||||||
@@ -165,70 +164,94 @@
|
|||||||
btf.loadLightbox(document.querySelectorAll('#article-container img:not(.no-lightbox)'))
|
btf.loadLightbox(document.querySelectorAll('#article-container img:not(.no-lightbox)'))
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderNavigation = () => {
|
const setupNavEvents = (nav) => {
|
||||||
const container = document.getElementById('article-container')
|
nav.querySelector('.shuoshuo-prev-btn').addEventListener('click', () => {
|
||||||
const existingNav = container.nextElementSibling
|
if (currentPage > 1) { currentPage--; renderPage(currentPage) }
|
||||||
if (existingNav && existingNav.classList.contains('shuoshuo-navigation')) {
|
})
|
||||||
existingNav.remove()
|
nav.querySelector('.shuoshuo-next-btn').addEventListener('click', () => {
|
||||||
|
if (currentPage < totalPages) { currentPage++; renderPage(currentPage) }
|
||||||
|
})
|
||||||
|
|
||||||
|
const input = nav.querySelector('.shuoshuo-page-input')
|
||||||
|
|
||||||
|
input.addEventListener('focus', e => { e.target.placeholder = '' })
|
||||||
|
input.addEventListener('blur', e => {
|
||||||
|
if (!e.target.value.trim()) e.target.placeholder = currentPage
|
||||||
|
})
|
||||||
|
|
||||||
|
input.addEventListener('input', e => {
|
||||||
|
const value = parseInt(e.target.value) || 0
|
||||||
|
let wasInvalid = false
|
||||||
|
|
||||||
|
if (value > totalPages) { e.target.value = totalPages; wasInvalid = true }
|
||||||
|
else if (value < 1 && e.target.value !== '') { e.target.value = 1; wasInvalid = true }
|
||||||
|
|
||||||
|
if (wasInvalid) {
|
||||||
|
e.target.classList.add('invalid')
|
||||||
|
setTimeout(() => e.target.classList.remove('invalid'), 500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
input.addEventListener('keydown', e => {
|
||||||
|
const value = e.target.value + e.key
|
||||||
|
|
||||||
|
if (e.key === 'Enter' || e.key === 'Backspace' || e.key === 'Delete' ||
|
||||||
|
e.key === 'ArrowLeft' || e.key === 'ArrowRight' ||
|
||||||
|
e.key === 'Tab' || e.ctrlKey || e.metaKey) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
const inputValue = e.target.value.trim()
|
||||||
|
const inputPage = inputValue === '' ? currentPage : parseInt(inputValue)
|
||||||
|
if (inputPage >= 1 && inputPage <= totalPages && inputPage !== currentPage) {
|
||||||
|
currentPage = inputPage
|
||||||
|
renderPage(currentPage)
|
||||||
|
} else if (inputValue === '') {
|
||||||
|
renderPage(currentPage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!/^\d$/.test(e.key)) {
|
||||||
|
e.preventDefault()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const newValue = parseInt(value) || 0
|
||||||
|
if (newValue > totalPages || (value.length > 1 && newValue === 0)) {
|
||||||
|
e.preventDefault()
|
||||||
|
e.target.classList.add('invalid')
|
||||||
|
setTimeout(() => e.target.classList.remove('invalid'), 500)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderNavigation = () => {
|
||||||
|
const container = document.getElementById('article-container')
|
||||||
|
let nav = container.nextElementSibling
|
||||||
const pageInfoTemplate = '#{__('pagination.page_info')}'
|
const pageInfoTemplate = '#{__('pagination.page_info')}'
|
||||||
const pageInfoText = pageInfoTemplate
|
const pageInfoText = pageInfoTemplate
|
||||||
.replace(/\$\{current}/g, currentPage)
|
.replace(/\$\{current}/g, currentPage)
|
||||||
.replace(/\$\{total}/g, totalPages)
|
.replace(/\$\{total}/g, totalPages)
|
||||||
|
|
||||||
const navHtml = `
|
if (!nav || !nav.classList.contains('shuoshuo-navigation')) {
|
||||||
<div class="shuoshuo-navigation">
|
nav = document.createElement('div')
|
||||||
<button onclick="window.shuoshuoPrevPage()" ${currentPage === 1 ? 'disabled' : ''}><i class="fa-solid fa-chevron-left"></i></button>
|
nav.className = 'shuoshuo-navigation'
|
||||||
<span class="shuoshuo-page-info">${pageInfoText}</span>
|
nav.innerHTML = `
|
||||||
<input type="number" class="shuoshuo-page-input" min="1" max="${totalPages}" placeholder="${currentPage}" onkeydown="window.shuoshuoHandleKeyDown(event)">
|
<button class="shuoshuo-prev-btn"><i class="fa-solid fa-chevron-left"></i></button>
|
||||||
<button onclick="window.shuoshuoNextPage()" ${currentPage === totalPages ? 'disabled' : ''}><i class="fa-solid fa-chevron-right"></i></button>
|
<span class="shuoshuo-page-info"></span>
|
||||||
</div>
|
<input type="number" class="shuoshuo-page-input" min="1">
|
||||||
|
<button class="shuoshuo-next-btn"><i class="fa-solid fa-chevron-right"></i></button>
|
||||||
`
|
`
|
||||||
container.insertAdjacentHTML('afterend', navHtml)
|
container.insertAdjacentElement('afterend', nav)
|
||||||
|
setupNavEvents(nav)
|
||||||
// Add input validation event listeners (only once)
|
|
||||||
if (!inputEventsAttached) {
|
|
||||||
setTimeout(() => {
|
|
||||||
const input = document.querySelector('.shuoshuo-page-input')
|
|
||||||
if (input) {
|
|
||||||
// Clear placeholder when clicking the input box
|
|
||||||
input.addEventListener('focus', (event) => {
|
|
||||||
event.target.placeholder = ''
|
|
||||||
})
|
|
||||||
|
|
||||||
// Restore placeholder if no content when losing focus
|
|
||||||
input.addEventListener('blur', (event) => {
|
|
||||||
if (!event.target.value.trim()) {
|
|
||||||
event.target.placeholder = currentPage
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
input.addEventListener('input', (event) => {
|
|
||||||
const value = parseInt(event.target.value) || 0
|
|
||||||
let wasInvalid = false
|
|
||||||
|
|
||||||
if (value > totalPages) {
|
|
||||||
event.target.value = totalPages
|
|
||||||
wasInvalid = true
|
|
||||||
} else if (value < 1 && event.target.value !== '') {
|
|
||||||
event.target.value = 1
|
|
||||||
wasInvalid = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If value is corrected, show red and shake effect
|
nav.querySelector('.shuoshuo-page-info').textContent = pageInfoText
|
||||||
if (wasInvalid) {
|
nav.querySelector('.shuoshuo-prev-btn').disabled = currentPage === 1
|
||||||
event.target.classList.add('invalid')
|
nav.querySelector('.shuoshuo-next-btn').disabled = currentPage === totalPages
|
||||||
setTimeout(() => {
|
const input = nav.querySelector('.shuoshuo-page-input')
|
||||||
event.target.classList.remove('invalid')
|
input.max = totalPages
|
||||||
}, 500)
|
input.placeholder = currentPage
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
inputEventsAttached = true // Mark that event listeners have been added
|
|
||||||
}
|
|
||||||
}, 0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderPage = (page) => {
|
const renderPage = (page) => {
|
||||||
@@ -239,79 +262,14 @@
|
|||||||
renderNavigation()
|
renderNavigation()
|
||||||
}
|
}
|
||||||
|
|
||||||
window.shuoshuoPrevPage = () => {
|
|
||||||
if (currentPage > 1) {
|
|
||||||
currentPage--
|
|
||||||
renderPage(currentPage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.shuoshuoNextPage = () => {
|
|
||||||
if (currentPage < totalPages) {
|
|
||||||
currentPage++
|
|
||||||
renderPage(currentPage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.shuoshuoGoToPage = (page) => {
|
|
||||||
if (typeof page === 'number') {
|
|
||||||
// Directly jump to the specified page
|
|
||||||
if (page >= 1 && page <= totalPages && page !== currentPage) {
|
|
||||||
currentPage = page
|
|
||||||
renderPage(currentPage)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Get page from input box
|
|
||||||
const input = document.querySelector('.shuoshuo-page-input')
|
|
||||||
const inputValue = input.value.trim()
|
|
||||||
const inputPage = inputValue === '' ? currentPage : parseInt(inputValue)
|
|
||||||
if (inputPage >= 1 && inputPage <= totalPages && inputPage !== currentPage) {
|
|
||||||
currentPage = inputPage
|
|
||||||
renderPage(currentPage)
|
|
||||||
} else if (inputValue === '') {
|
|
||||||
// If input box is empty, re-render current page (update placeholder)
|
|
||||||
renderPage(currentPage)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
window.shuoshuoHandleKeyDown = (event) => {
|
|
||||||
const input = event.target
|
|
||||||
const value = input.value + event.key
|
|
||||||
|
|
||||||
// Allow delete, arrow keys, backspace, etc.
|
|
||||||
if (event.key === 'Enter' || event.key === 'Backspace' || event.key === 'Delete' ||
|
|
||||||
event.key === 'ArrowLeft' || event.key === 'ArrowRight' ||
|
|
||||||
event.key === 'Tab' || event.ctrlKey || event.metaKey) {
|
|
||||||
if (event.key === 'Enter') {
|
|
||||||
window.shuoshuoGoToPage()
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only allow numbers
|
|
||||||
if (!/^\d$/.test(event.key)) {
|
|
||||||
event.preventDefault()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the value after input exceeds the range
|
|
||||||
const newValue = parseInt(value) || 0
|
|
||||||
if (newValue > totalPages || (value.length > 1 && newValue === 0)) {
|
|
||||||
event.preventDefault()
|
|
||||||
// Add red and shake effect
|
|
||||||
input.classList.add('invalid')
|
|
||||||
setTimeout(() => {
|
|
||||||
input.classList.remove('invalid')
|
|
||||||
}, 500)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const loadShuoshuo = async () => {
|
const loadShuoshuo = async () => {
|
||||||
|
const container = document.getElementById('article-container')
|
||||||
try {
|
try {
|
||||||
let originData = []
|
let originData = []
|
||||||
if (!{Boolean(page.shuoshuo_url)}) {
|
if (!{Boolean(page.shuoshuo_url)}) {
|
||||||
|
container.innerHTML = '<div class="shuoshuo-loading"><i class="fa-solid fa-circle-notch fa-spin"></i></div>'
|
||||||
const response = await fetch('!{url_for(page.shuoshuo_url)}')
|
const response = await fetch('!{url_for(page.shuoshuo_url)}')
|
||||||
|
if (!response.ok) throw new Error(`HTTP ${response.status}`)
|
||||||
originData = await response.json()
|
originData = await response.json()
|
||||||
} else {
|
} else {
|
||||||
const dataElement = document.getElementById('shuoshuo-data')
|
const dataElement = document.getElementById('shuoshuo-data')
|
||||||
@@ -319,14 +277,33 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
data = filterDataByLimit(sortDataByDate(originData), limitConfig)
|
data = filterDataByLimit(sortDataByDate(originData), limitConfig)
|
||||||
|
|
||||||
totalPages = Math.ceil(data.length / itemsPerPage)
|
totalPages = Math.ceil(data.length / itemsPerPage)
|
||||||
|
|
||||||
|
if (data.length === 0) {
|
||||||
|
container.innerHTML = '<div class="shuoshuo-empty"></div>'
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
renderPage(currentPage)
|
renderPage(currentPage)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
|
container.innerHTML = '<div class="shuoshuo-error"><i class="fa-solid fa-circle-exclamation"></i></div>'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
window.pjax ? loadShuoshuo() : window.addEventListener('load', loadShuoshuo)
|
const pjaxCleanup = () => {
|
||||||
|
const container = document.getElementById('article-container')
|
||||||
|
if (container) {
|
||||||
|
const nav = container.nextElementSibling
|
||||||
|
if (nav && nav.classList.contains('shuoshuo-navigation')) nav.remove()
|
||||||
|
}
|
||||||
|
document.removeEventListener('pjax:send', pjaxCleanup)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.pjax) {
|
||||||
|
document.addEventListener('pjax:send', pjaxCleanup)
|
||||||
|
loadShuoshuo()
|
||||||
|
} else {
|
||||||
|
window.addEventListener('load', loadShuoshuo)
|
||||||
|
}
|
||||||
})()
|
})()
|
||||||
+5
-2
@@ -2,8 +2,11 @@ script.
|
|||||||
(() => {
|
(() => {
|
||||||
const abcjsInit = () => {
|
const abcjsInit = () => {
|
||||||
const abcjsFn = () => {
|
const abcjsFn = () => {
|
||||||
|
const $article = document.getElementById("article-container")
|
||||||
|
if (!$article || $article.querySelector(".hbe-container")) return
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
const sheets = document.querySelectorAll(".abc-music-sheet")
|
const sheets = $article.querySelectorAll(".abc-music-sheet")
|
||||||
for (let i = 0; i < sheets.length; i++) {
|
for (let i = 0; i < sheets.length; i++) {
|
||||||
const ele = sheets[i]
|
const ele = sheets[i]
|
||||||
if (ele.children.length > 0) continue
|
if (ele.children.length > 0) continue
|
||||||
@@ -29,7 +32,7 @@ script.
|
|||||||
}, 100)
|
}, 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (typeof ABCJS === "object") {
|
if (typeof ABCJS === "object" && ABCJS !== null) {
|
||||||
abcjsFn()
|
abcjsFn()
|
||||||
} else {
|
} else {
|
||||||
btf.getScript("!{url_for(theme.asset.abcjs_basic_js)}").then(abcjsFn)
|
btf.getScript("!{url_for(theme.asset.abcjs_basic_js)}").then(abcjsFn)
|
||||||
|
|||||||
Loaded 3 of 36 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user