You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
188 lines
6.4 KiB
188 lines
6.4 KiB
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($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() |
|
loadFlag = true |
|
} |
|
// shortcut: ESC |
|
document.addEventListener('keydown', function f (event) { |
|
if (event.code === 'Escape') { |
|
closeSearch() |
|
document.removeEventListener('keydown', f) |
|
} |
|
}) |
|
} |
|
|
|
const closeSearch = () => { |
|
const bodyStyle = document.body.style |
|
bodyStyle.width = '' |
|
bodyStyle.overflow = '' |
|
btf.animateOut(document.querySelector('#local-search .search-dialog'), 'search_close .5s') |
|
btf.animateOut($searchMask, 'to_hide 0.5s') |
|
} |
|
|
|
const searchClickFn = () => { |
|
document.querySelector('#search-button > .search').addEventListener('click', openSearch) |
|
} |
|
|
|
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) |
|
} |
|
|
|
// check url is json or not |
|
const isJson = url => { |
|
const reg = /\.json$/ |
|
return reg.test(url) |
|
} |
|
|
|
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 |
|
data = [...a.querySelectorAll('entry')].map(item =>{ |
|
return { |
|
title: item.querySelector('title').textContent, |
|
content: item.querySelector('content') && item.querySelector('content').textContent, |
|
url: item.querySelector('url').textContent |
|
} |
|
}) |
|
} |
|
if (response.ok) { |
|
const $loadDataItem = document.getElementById('loading-database') |
|
$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>' |
|
|
|
$resultContent.innerHTML = '' |
|
let str = '<div class="search-result-list">' |
|
if (keywords.length <= 0) return |
|
let count = 0 |
|
// perform local searching |
|
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 |
|
} |
|
} |
|
}) |
|
} 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 |
|
let pre = '' |
|
let post = '' |
|
|
|
if (start < 0) { |
|
start = 0 |
|
} |
|
|
|
if (start === 0) { |
|
end = 100 |
|
} else { |
|
pre = '...' |
|
} |
|
|
|
if (end > dataContent.length) { |
|
end = dataContent.length |
|
} else { |
|
post = '...' |
|
} |
|
|
|
let matchContent = dataContent.substring(start, end) |
|
|
|
// 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 |
|
|
|
if (dataContent !== '') { |
|
str += '<p class="search-result">' + pre + matchContent + post + '</p>' |
|
} |
|
} |
|
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) |
|
}) |
|
}) |
|
} |
|
|
|
searchClickFn() |
|
searchClickFnOnce() |
|
|
|
// pjax |
|
window.addEventListener('pjax:complete', () => { |
|
!btf.isHidden($searchMask) && closeSearch() |
|
searchClickFn() |
|
}) |
|
})
|
|
|