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 = '' $resultContent.innerHTML = '' let str = '
' 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, '' + keyword + '') dataTitle = dataTitle.replace(regS, '' + keyword + '') }) str += '
' + dataTitle + '' count += 1 if (dataContent !== '') { str += '

' + pre + matchContent + post + '

' } } str += '
' } }) if (count === 0) { str += '
' + GLOBAL_CONFIG.localSearch.languages.hits_empty.replace(/\$\{query}/, this.value.trim()) + '
' } str += '
' $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() }) })