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.
159 lines
4.7 KiB
159 lines
4.7 KiB
{{ define "title" -}}
|
|
{{ .Title }} | {{ .Site.Title }}
|
|
{{- end }}
|
|
{{ define "header" }}
|
|
{{ partial "menu.html" . }}
|
|
{{ end }}
|
|
{{ define "main" }}
|
|
<header>
|
|
<h1>{{ .Title }}</h1>
|
|
</header>
|
|
<noscript>
|
|
Aw-shucks! Search requires use of JavaScript to function properly.
|
|
</noscript>
|
|
<div id="search-app">
|
|
<div v-cloak>
|
|
<section>
|
|
<form v-on:submit.prevent class="form" action="{{ "search" | absURL }}">
|
|
<fieldset class="form-group">
|
|
<input v-model="query" id="query" name="s" type="search" class="form-control" maxlength="32" autocomplete="off">
|
|
<div class="help-block">Press <kbd>s</kbd> to focus search input anytime.</div>
|
|
</fieldset>
|
|
</form>
|
|
</section>
|
|
<section v-if="results.length">
|
|
<p><i>Showing results for “{ resultsForSearch }”.</i></p>
|
|
<div id="search-results">
|
|
<article v-for="result in results" itemscope itemtype="http://schema.org/CreativeWork">
|
|
<header itemprop="name">
|
|
<h2 itemprop="name"><a :href="result.item.url"><span v-html="result.item.title"></span></a></h2>
|
|
</header>
|
|
<div v-html=result.item.summary itemprop="description"></div>
|
|
<nav class="readmore"><a itemprop="url" :href="result.item.url">Read More »</a></nav>
|
|
</article>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
{{ end }}
|
|
{{ define "footer" }}
|
|
{{ partial "powered-by.html" . }}
|
|
<script src="/js/vue.min.js"></script>
|
|
<script src="/js/lodash.custom.min.js"></script>
|
|
<script src="/js/fuse.min.js"></script>
|
|
<script src="/js/mark.min.js"></script>
|
|
<script>
|
|
(function (window, document, undefined) {
|
|
"use strict"
|
|
|
|
const getQueryByParam = param => decodeURIComponent(
|
|
(location.search.split(param + '=')[1] || '').split('&')[0]
|
|
).replace(/\+/g, ' ')
|
|
|
|
const queryParam = 's'
|
|
const selectors = {
|
|
appContainer: '#search-app',
|
|
resultContainer: '#search-results',
|
|
searchInput: '#query'
|
|
}
|
|
|
|
const fuseOpts = {
|
|
shouldSort: true,
|
|
tokenize: true,
|
|
matchAllTokens: true,
|
|
includeScore: true,
|
|
includeMatches: true,
|
|
keys: [
|
|
{ name: "title", weight: 0.8 },
|
|
{ name: "contents", weight: 0.5 },
|
|
{ name: "tags", weight: 0.3 },
|
|
{ name: "categories", weight: 0.3 }
|
|
]
|
|
}
|
|
|
|
const getSearchInput = () => document.querySelector(selectors.searchInput)
|
|
const focusSearchInput = () => getSearchInput().focus()
|
|
const searchQuery = getSearchInput().value = getQueryByParam(queryParam)
|
|
|
|
const fuse = new Fuse([], fuseOpts)
|
|
window.fetch('/index.json').then(response => {
|
|
response.text().then(searchData => {
|
|
fuse.setCollection(JSON.parse(searchData))
|
|
if (searchQuery) search(searchQuery)
|
|
})
|
|
})
|
|
|
|
const getUrl = (query) => {
|
|
const encodedQuery = encodeURIComponent(query)
|
|
const url = {{ .URL }}
|
|
return (encodedQuery)
|
|
? `${url}?${queryParam}=${encodedQuery}`
|
|
: url
|
|
}
|
|
|
|
let mark = new Mark(
|
|
document.querySelector(
|
|
selectors.resultContainer
|
|
)
|
|
)
|
|
|
|
const app = new Vue({
|
|
delimiters: ['{', '}'],
|
|
el: selectors.appContainer,
|
|
data: {
|
|
fuse: null,
|
|
results: [],
|
|
query: getQueryByParam(queryParam),
|
|
resultsForSearch: getQueryByParam(queryParam)
|
|
},
|
|
mounted () {
|
|
this.fuse = fuse
|
|
window.onpopstate = (evt) => {
|
|
this.query = evt.state.query
|
|
}
|
|
document.onkeyup = function (evt) {
|
|
if (evt.key === 's') focusSearchInput()
|
|
}
|
|
focusSearchInput()
|
|
},
|
|
watch: {
|
|
query () {
|
|
this.executeSearch()
|
|
window.history.replaceState(
|
|
{query: this.query},
|
|
null,
|
|
getUrl(this.query)
|
|
)
|
|
}
|
|
},
|
|
beforeUpdate: function () {
|
|
mark.unmark()
|
|
},
|
|
updated: function () {
|
|
this.$nextTick(function () {
|
|
mark = new Mark(
|
|
document.querySelector(
|
|
selectors.resultContainer
|
|
)
|
|
)
|
|
mark.mark(this.query.trim())
|
|
})
|
|
},
|
|
methods: {
|
|
executeSearch: _.debounce(function () {
|
|
const trimmedQuery = this.query.trim()
|
|
this.resultsForSearch = trimmedQuery
|
|
this.results = (trimmedQuery)
|
|
? this.fuse.search(trimmedQuery)
|
|
: []
|
|
}, 250)
|
|
}
|
|
})
|
|
|
|
const search = query => {
|
|
app.results = fuse.search(query)
|
|
}
|
|
|
|
})(window, document)
|
|
</script>
|
|
{{ end }}
|
|
|