|
|
|
@ -1,14 +1,17 @@
|
|
|
|
|
window.addEventListener('load', () => {
|
|
|
|
|
let loadFlag = false
|
|
|
|
|
let dataObj = []
|
|
|
|
|
const $searchMask = document.getElementById('search-mask')
|
|
|
|
|
|
|
|
|
|
const openSearch = () => {
|
|
|
|
|
const bodyStyle = document.body.style
|
|
|
|
|
bodyStyle.width = '100%'
|
|
|
|
|
bodyStyle.overflow = 'hidden'
|
|
|
|
|
btf.animateIn(document.getElementById('search-mask'), 'to_show 0.5s')
|
|
|
|
|
btf.animateIn($searchMask, '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) {
|
|
|
|
|
search(GLOBAL_CONFIG.localSearch.path)
|
|
|
|
|
search()
|
|
|
|
|
loadFlag = true
|
|
|
|
|
}
|
|
|
|
|
// shortcut: ESC
|
|
|
|
@ -25,35 +28,35 @@ window.addEventListener('load', () => {
|
|
|
|
|
bodyStyle.width = ''
|
|
|
|
|
bodyStyle.overflow = ''
|
|
|
|
|
btf.animateOut(document.querySelector('#local-search .search-dialog'), 'search_close .5s')
|
|
|
|
|
btf.animateOut(document.getElementById('search-mask'), 'to_hide 0.5s')
|
|
|
|
|
btf.animateOut($searchMask, 'to_hide 0.5s')
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// click function
|
|
|
|
|
const searchClickFn = () => {
|
|
|
|
|
document.querySelector('#search-button > .search').addEventListener('click', openSearch)
|
|
|
|
|
document.getElementById('search-mask').addEventListener('click', closeSearch)
|
|
|
|
|
document.querySelector('#local-search .search-close-button').addEventListener('click', closeSearch)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
searchClickFn()
|
|
|
|
|
const searchClickFnOnce = () => {
|
|
|
|
|
document.querySelector('#local-search .search-close-button').addEventListener('click', closeSearch)
|
|
|
|
|
$searchMask.addEventListener('click', closeSearch)
|
|
|
|
|
if (GLOBAL_CONFIG.localSearch.preload) dataObj = fetchData(GLOBAL_CONFIG.localSearch.path)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// pjax
|
|
|
|
|
window.addEventListener('pjax:complete', function () {
|
|
|
|
|
getComputedStyle(document.querySelector('#local-search .search-dialog')).display === 'block' && closeSearch()
|
|
|
|
|
searchClickFn()
|
|
|
|
|
})
|
|
|
|
|
// check url is json or not
|
|
|
|
|
const isJson = url => {
|
|
|
|
|
const reg = /\.json$/
|
|
|
|
|
return reg.test(url)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function search (path) {
|
|
|
|
|
let datas = []
|
|
|
|
|
const typeF = path.split('.')[1]
|
|
|
|
|
const response = await fetch(GLOBAL_CONFIG.root + path)
|
|
|
|
|
if (typeF === 'json') {
|
|
|
|
|
datas = await response.json()
|
|
|
|
|
} else if (typeF === 'xml') {
|
|
|
|
|
const fetchData = async (path) => {
|
|
|
|
|
let data = []
|
|
|
|
|
const response = await fetch(path)
|
|
|
|
|
if (isJson(path)) {
|
|
|
|
|
data = await response.json()
|
|
|
|
|
} else {
|
|
|
|
|
const res = await response.text()
|
|
|
|
|
const t = await new window.DOMParser().parseFromString(res, 'text/xml')
|
|
|
|
|
const a = await t
|
|
|
|
|
datas = [...a.querySelectorAll('entry')].map(function (item) {
|
|
|
|
|
data = [...a.querySelectorAll('entry')].map(item =>{
|
|
|
|
|
return {
|
|
|
|
|
title: item.querySelector('title').textContent,
|
|
|
|
|
content: item.querySelector('content') && item.querySelector('content').textContent,
|
|
|
|
@ -66,10 +69,18 @@ window.addEventListener('load', () => {
|
|
|
|
|
$loadDataItem.nextElementSibling.style.display = 'block'
|
|
|
|
|
$loadDataItem.remove()
|
|
|
|
|
}
|
|
|
|
|
return data
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const search = () => {
|
|
|
|
|
if (!GLOBAL_CONFIG.localSearch.preload) {
|
|
|
|
|
dataObj = fetchData(GLOBAL_CONFIG.localSearch.path)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const $input = document.querySelector('#local-search-input input')
|
|
|
|
|
const $resultContent = document.getElementById('local-search-results')
|
|
|
|
|
const $loadingStatus = document.getElementById('loading-status')
|
|
|
|
|
|
|
|
|
|
$input.addEventListener('input', function () {
|
|
|
|
|
const keywords = this.value.trim().toLowerCase().split(/[\s]+/)
|
|
|
|
|
if (keywords[0] !== '') $loadingStatus.innerHTML = '<i class="fas fa-spinner fa-pulse"></i>'
|
|
|
|
@ -79,82 +90,99 @@ window.addEventListener('load', () => {
|
|
|
|
|
if (keywords.length <= 0) return
|
|
|
|
|
let count = 0
|
|
|
|
|
// perform local searching
|
|
|
|
|
datas.forEach(function (data) {
|
|
|
|
|
let isMatch = true
|
|
|
|
|
let dataTitle = data.title ? data.title.trim().toLowerCase() : ''
|
|
|
|
|
const dataContent = data.content ? data.content.trim().replace(/<[^>]+>/g, '').toLowerCase() : ''
|
|
|
|
|
const dataUrl = data.url.startsWith('/') ? data.url : GLOBAL_CONFIG.root + data.url
|
|
|
|
|
let indexTitle = -1
|
|
|
|
|
let indexContent = -1
|
|
|
|
|
let firstOccur = -1
|
|
|
|
|
// only match artiles with not empty titles and contents
|
|
|
|
|
if (dataTitle !== '' || dataContent !== '') {
|
|
|
|
|
keywords.forEach(function (keyword, i) {
|
|
|
|
|
indexTitle = dataTitle.indexOf(keyword)
|
|
|
|
|
indexContent = dataContent.indexOf(keyword)
|
|
|
|
|
if (indexTitle < 0 && indexContent < 0) {
|
|
|
|
|
isMatch = false
|
|
|
|
|
} else {
|
|
|
|
|
if (indexContent < 0) {
|
|
|
|
|
indexContent = 0
|
|
|
|
|
dataObj.then(data => {
|
|
|
|
|
data.forEach(data => {
|
|
|
|
|
let isMatch = true
|
|
|
|
|
let dataTitle = data.title ? data.title.trim().toLowerCase() : ''
|
|
|
|
|
const dataContent = data.content ? data.content.trim().replace(/<[^>]+>/g, '').toLowerCase() : ''
|
|
|
|
|
const dataUrl = data.url.startsWith('/') ? data.url : GLOBAL_CONFIG.root + data.url
|
|
|
|
|
let indexTitle = -1
|
|
|
|
|
let indexContent = -1
|
|
|
|
|
let firstOccur = -1
|
|
|
|
|
// only match articles with not empty titles and contents
|
|
|
|
|
if (dataTitle !== '' || dataContent !== '') {
|
|
|
|
|
keywords.forEach((keyword, i) => {
|
|
|
|
|
indexTitle = dataTitle.indexOf(keyword)
|
|
|
|
|
indexContent = dataContent.indexOf(keyword)
|
|
|
|
|
if (indexTitle < 0 && indexContent < 0) {
|
|
|
|
|
isMatch = false
|
|
|
|
|
} else {
|
|
|
|
|
if (indexContent < 0) {
|
|
|
|
|
indexContent = 0
|
|
|
|
|
}
|
|
|
|
|
if (i === 0) {
|
|
|
|
|
firstOccur = indexContent
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (i === 0) {
|
|
|
|
|
firstOccur = indexContent
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
isMatch = false
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
isMatch = false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// show search results
|
|
|
|
|
if (isMatch) {
|
|
|
|
|
if (firstOccur >= 0) {
|
|
|
|
|
// cut out 130 characters
|
|
|
|
|
// let start = firstOccur - 30 < 0 ? 0 : firstOccur - 30
|
|
|
|
|
// let end = firstOccur + 50 > dataContent.length ? dataContent.length : firstOccur + 50
|
|
|
|
|
let start = firstOccur - 30
|
|
|
|
|
let end = firstOccur + 100
|
|
|
|
|
|
|
|
|
|
if (start < 0) {
|
|
|
|
|
start = 0
|
|
|
|
|
}
|
|
|
|
|
// show search results
|
|
|
|
|
if (isMatch) {
|
|
|
|
|
if (firstOccur >= 0) {
|
|
|
|
|
// cut out 130 characters
|
|
|
|
|
// let start = firstOccur - 30 < 0 ? 0 : firstOccur - 30
|
|
|
|
|
// let end = firstOccur + 50 > dataContent.length ? dataContent.length : firstOccur + 50
|
|
|
|
|
let start = firstOccur - 30
|
|
|
|
|
let end = firstOccur + 100
|
|
|
|
|
let pre = ''
|
|
|
|
|
let post = ''
|
|
|
|
|
|
|
|
|
|
if (start === 0) {
|
|
|
|
|
end = 100
|
|
|
|
|
}
|
|
|
|
|
if (start < 0) {
|
|
|
|
|
start = 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (end > dataContent.length) {
|
|
|
|
|
end = dataContent.length
|
|
|
|
|
}
|
|
|
|
|
if (start === 0) {
|
|
|
|
|
end = 100
|
|
|
|
|
} else {
|
|
|
|
|
pre = '...'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (end > dataContent.length) {
|
|
|
|
|
end = dataContent.length
|
|
|
|
|
} else {
|
|
|
|
|
post = '...'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let matchContent = dataContent.substring(start, end)
|
|
|
|
|
let matchContent = dataContent.substring(start, end)
|
|
|
|
|
|
|
|
|
|
// highlight all keywords
|
|
|
|
|
keywords.forEach(function (keyword) {
|
|
|
|
|
const regS = new RegExp(keyword, 'gi')
|
|
|
|
|
matchContent = matchContent.replace(regS, '<span class="search-keyword">' + keyword + '</span>')
|
|
|
|
|
dataTitle = dataTitle.replace(regS, '<span class="search-keyword">' + keyword + '</span>')
|
|
|
|
|
})
|
|
|
|
|
// highlight all keywords
|
|
|
|
|
keywords.forEach(keyword => {
|
|
|
|
|
const regS = new RegExp(keyword, 'gi')
|
|
|
|
|
matchContent = matchContent.replace(regS, '<span class="search-keyword">' + keyword + '</span>')
|
|
|
|
|
dataTitle = dataTitle.replace(regS, '<span class="search-keyword">' + keyword + '</span>')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
str += '<div class="local-search__hit-item"><a href="' + dataUrl + '" class="search-result-title">' + dataTitle + '</a>'
|
|
|
|
|
count += 1
|
|
|
|
|
str += '<div class="local-search__hit-item"><a href="' + dataUrl + '" class="search-result-title">' + dataTitle + '</a>'
|
|
|
|
|
count += 1
|
|
|
|
|
|
|
|
|
|
if (dataContent !== '') {
|
|
|
|
|
str += '<p class="search-result">' + matchContent + '...</p>'
|
|
|
|
|
if (dataContent !== '') {
|
|
|
|
|
str += '<p class="search-result">' + pre + matchContent + post + '</p>'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
str += '</div>'
|
|
|
|
|
}
|
|
|
|
|
str += '</div>'
|
|
|
|
|
})
|
|
|
|
|
if (count === 0) {
|
|
|
|
|
str += '<div id="local-search__hits-empty">' + GLOBAL_CONFIG.localSearch.languages.hits_empty.replace(/\$\{query}/, this.value.trim()) +
|
|
|
|
|
'</div>'
|
|
|
|
|
}
|
|
|
|
|
str += '</div>'
|
|
|
|
|
$resultContent.innerHTML = str
|
|
|
|
|
if (keywords[0] !== '') $loadingStatus.innerHTML = ''
|
|
|
|
|
window.pjax && window.pjax.refresh($resultContent)
|
|
|
|
|
})
|
|
|
|
|
if (count === 0) {
|
|
|
|
|
str += '<div id="local-search__hits-empty">' + GLOBAL_CONFIG.localSearch.languages.hits_empty.replace(/\$\{query}/, this.value.trim()) +
|
|
|
|
|
'</div>'
|
|
|
|
|
}
|
|
|
|
|
str += '</div>'
|
|
|
|
|
$resultContent.innerHTML = str
|
|
|
|
|
if (keywords[0] !== '') $loadingStatus.innerHTML = ''
|
|
|
|
|
window.pjax && window.pjax.refresh($resultContent)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
searchClickFn()
|
|
|
|
|
searchClickFnOnce()
|
|
|
|
|
|
|
|
|
|
// pjax
|
|
|
|
|
window.addEventListener('pjax:complete', () => {
|
|
|
|
|
!btf.isHidden($searchMask) && closeSearch()
|
|
|
|
|
searchClickFn()
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|