|
|
- /*!
- * Copyright (C) 2019 Josh Habdas <jhabdas@protonmail.com>
- *
- * This file is part of After Dark.
- *
- * After Dark is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as published
- * by the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * After Dark is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <https://www.gnu.org/licenses/>.
- */
-
- fetchInject([
- "{{ "/js/vue.min.js" | relURL }}",
- "{{ "/js/lodash.custom.min.js" | relURL }}",
- "{{ "/js/fuse.min.js" | relURL }}",
- "{{ "/js/mark.min.js" | relURL }}"
- ]).then(() => {
- (function (window, document, undefined) {
- 'use strict';
-
- const getQueryByParam = param => decodeURIComponent(
- (location.search.split(param + '=')[1] || '').split('&')[0]
- ).replace(/\+/g, ' ');
-
- const queryParam = 's';
- const hotkeys = {{ (.Params.form.hotkeys | default (slice "/" "s")) | jsonify }};
- 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));
- searchQuery && search(searchQuery);
- });
- });
-
- const getUrl = (query) => {
- const encodedQuery = encodeURIComponent(query);
- const url = "{{ .RelPermalink }}";
- 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;
- };
- const searchInput = getSearchInput();
- document.onkeydown = function (evt) {
- if (evt.target === searchInput) return;
- if (hotkeys.includes(evt.key)) {
- evt.preventDefault();
- focusSearchInput();
- getSearchInput().select();
- };
- }
- 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);
- });
|