${this.date(relatedPosts[i].created, hexoConfig.date_format)}
`
} else {
result += `
${this.date(relatedPosts[i].updated, hexoConfig.date_format)}
`
}
- result += `
${relatedPosts[i].title}
`
+ result += `
${title}
`
result += '
'
}
diff --git a/source/css/_layout/third-party.styl b/source/css/_layout/third-party.styl
index 8f52548..cf9483c 100644
--- a/source/css/_layout/third-party.styl
+++ b/source/css/_layout/third-party.styl
@@ -89,6 +89,7 @@ mjx-container[display],
.has-jax
overflow-x: auto
overflow-y: hidden
+ line-height: normal !important
.aplayer
color: $font-black
diff --git a/source/css/_search/algolia.styl b/source/css/_search/algolia.styl
index c90c0a0..db57b02 100644
--- a/source/css/_search/algolia.styl
+++ b/source/css/_search/algolia.styl
@@ -11,7 +11,8 @@
color: var(--search-input-color)
.ais-Hits-list
- padding-left: 24px
+ margin: 0
+ padding: 0
@extend .list-beauty
a
@@ -25,12 +26,17 @@
color: $search-keyword-highlight
font-weight: bold
+ .algolia-hit-item-content
+ margin: 0 0 8px
+ word-break: break-all
+
.ais-Pagination
- margin: 16px 0 0
+ margin: 20px 0 0
padding: 0
text-align: center
.ais-Pagination-list
+ margin: 0
padding: 0
list-style: none
@@ -52,7 +58,27 @@
color: #eee
cursor: default
+ .ais-Pagination-item--disabled
+ visibility: hidden
+
.algolia-logo
padding-top: 2px
width: 80px
- height: 30px
\ No newline at end of file
+ height: 30px
+
+ #algolia-hits
+ > div
+ overflow-y: scroll
+
+ +minWidth768()
+ max-height: calc(80vh - 240px)
+
+ +maxWidth768()
+ height: calc(100vh - 260px)
+
+.apple
+ #algolia-search
+ #algolia-hits
+ > div
+ +maxWidth768()
+ height: calc(90vh - 260px)
diff --git a/source/css/_search/index.styl b/source/css/_search/index.styl
index 2e4e874..bc5c5b1 100644
--- a/source/css/_search/index.styl
+++ b/source/css/_search/index.styl
@@ -1,6 +1,6 @@
.search-dialog
position: fixed
- top: 100px
+ top: 10%
left: 50%
z-index: 1001
display: none
diff --git a/source/css/_search/local-search.styl b/source/css/_search/local-search.styl
index f67d87a..bf99945 100644
--- a/source/css/_search/local-search.styl
+++ b/source/css/_search/local-search.styl
@@ -60,7 +60,7 @@
.search-result-list
overflow-y: auto
- max-height: 210px
+ max-height: calc(80vh - 130px)
+maxWidth768()
padding-bottom: 40px
diff --git a/source/js/search/algolia.js b/source/js/search/algolia.js
index 13373e9..1554cc6 100644
--- a/source/js/search/algolia.js
+++ b/source/js/search/algolia.js
@@ -26,16 +26,39 @@ window.addEventListener('load', () => {
const searchClickFn = () => {
document.querySelector('#search-button > .search').addEventListener('click', openSearch)
+ }
+
+ const searchClickFnOnce = () => {
document.getElementById('search-mask').addEventListener('click', closeSearch)
document.querySelector('#algolia-search .search-close-button').addEventListener('click', closeSearch)
}
- searchClickFn()
+ const cutContent = content => {
+ if (content === '') return ''
- window.addEventListener('pjax:complete', function () {
- getComputedStyle(document.querySelector('#algolia-search .search-dialog')).display === 'block' && closeSearch()
- searchClickFn()
- })
+ const firstOccur = content.indexOf('')
+
+ let start = firstOccur - 30
+ let end = firstOccur + 120
+ let pre = ''
+ let post = ''
+
+ if (start <= 0) {
+ start = 0
+ end = 140
+ } else {
+ pre = '...'
+ }
+
+ if (end > content.length) {
+ end = content.length
+ } else {
+ post = '...'
+ }
+
+ let matchContent = pre + content.substring(start, end) + post
+ return matchContent
+ }
const algolia = GLOBAL_CONFIG.algolia
const isAlgoliaValid = algolia.appId && algolia.apiKey && algolia.indexName
@@ -43,81 +66,87 @@ window.addEventListener('load', () => {
return console.error('Algolia setting is invalid!')
}
- const searchClient = window.algoliasearch(algolia.appId, algolia.apiKey)
const search = instantsearch({
indexName: algolia.indexName,
- searchClient
+ searchClient: algoliasearch(algolia.appId, algolia.apiKey),
+ searchFunction(helper) {
+ helper.state.query && helper.search()
+ },
})
- search.addWidgets([
- instantsearch.widgets.configure({
- hitsPerPage: 5
- })
- ])
+ const configure = instantsearch.widgets.configure({
+ hitsPerPage: 5
+ })
- search.addWidgets([
- instantsearch.widgets.searchBox({
- container: '#algolia-search-input',
- showReset: false,
- showSubmit: false,
- placeholder: GLOBAL_CONFIG.algolia.languages.input_placeholder,
- showLoadingIndicator: true
- })
- ])
+ const searchBox = instantsearch.widgets.searchBox({
+ container: '#algolia-search-input',
+ showReset: false,
+ showSubmit: false,
+ placeholder: GLOBAL_CONFIG.algolia.languages.input_placeholder,
+ showLoadingIndicator: true
+ })
- search.addWidgets([
- instantsearch.widgets.hits({
- container: '#algolia-hits',
- templates: {
- item: function (data) {
- const link = data.permalink ? data.permalink : (GLOBAL_CONFIG.root + data.path)
- return `
-
- ${data._highlightResult.title.value || 'no-title'}
- `
- },
- empty: function (data) {
- return (
- '' +
- GLOBAL_CONFIG.algolia.languages.hits_empty.replace(/\$\{query}/, data.query) +
- '
'
- )
- }
+ const hits = instantsearch.widgets.hits({
+ container: '#algolia-hits',
+ templates: {
+ item(data) {
+ console.log(data);
+ const link = data.permalink ? data.permalink : (GLOBAL_CONFIG.root + data.path)
+ return `
+
+ ${data._highlightResult.title.value || 'no-title'}
+
+ ${cutContent(data._highlightResult.contentStripTruncate.value)}
`
+ },
+ empty: function (data) {
+ return (
+ '' +
+ GLOBAL_CONFIG.algolia.languages.hits_empty.replace(/\$\{query}/, data.query) +
+ '
'
+ )
}
- })
- ])
+ }
+ })
- search.addWidgets([
- instantsearch.widgets.stats({
- container: '#algolia-stats',
- templates: {
- text: function (data) {
- const icon = ''
- const stats = GLOBAL_CONFIG.algolia.languages.hits_stats
- .replace(/\$\{hits}/, data.nbHits)
- .replace(/\$\{time}/, data.processingTimeMS)
- return (
- `
${stats}${icon}`
- )
- }
+ const stats = instantsearch.widgets.stats({
+ container: '#algolia-stats',
+ templates: {
+ text: function (data) {
+ const icon = ''
+ const stats = GLOBAL_CONFIG.algolia.languages.hits_stats
+ .replace(/\$\{hits}/, data.nbHits)
+ .replace(/\$\{time}/, data.processingTimeMS)
+ return (
+ `
${stats}${icon}`
+ )
}
- })
- ])
+ }
+ })
+
+ const pagination = instantsearch.widgets.pagination({
+ container: '#algolia-pagination',
+ totalPages: 5,
+ templates: {
+ first: '',
+ last: '',
+ previous: '',
+ next: ''
+ }
+ })
+
+
+ search.addWidgets([configure,searchBox,hits,stats,pagination]) // add the widgets to the instantsearch instance
- search.addWidgets([
- instantsearch.widgets.pagination({
- container: '#algolia-pagination',
- totalPages: 5,
- templates: {
- first: '',
- last: '',
- previous: '',
- next: ''
- }
- })
- ])
search.start()
+ searchClickFn()
+ searchClickFnOnce()
+
+ window.addEventListener('pjax:complete', () => {
+ getComputedStyle(document.querySelector('#algolia-search .search-dialog')).display === 'block' && closeSearch()
+ searchClickFn()
+ })
+
window.pjax && search.on('render', () => {
window.pjax.refresh(document.getElementById('algolia-hits'))
})
diff --git a/source/js/search/local-search.js b/source/js/search/local-search.js
index 2d207d5..6c1ca7d 100644
--- a/source/js/search/local-search.js
+++ b/source/js/search/local-search.js
@@ -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 = ''
@@ -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
- }
-
- // 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
- }
-
- if (start === 0) {
- end = 100
- }
-
- if (end > dataContent.length) {
- end = dataContent.length
- }
-
- let matchContent = dataContent.substring(start, end)
-
- // highlight all keywords
- keywords.forEach(function (keyword) {
- const regS = new RegExp(keyword, 'gi')
- matchContent = matchContent.replace(regS, '' + keyword + '')
- dataTitle = dataTitle.replace(regS, '' + keyword + '')
})
-
- str += '' + dataTitle + ''
- count += 1
-
- if (dataContent !== '') {
- str += '
' + matchContent + '...
'
- }
+ } else {
+ isMatch = false
}
- str += '
'
+
+ // 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 += '