Initial AD port

This commit is contained in:
2021-04-26 18:26:42 +01:00
parent 6911ce346b
commit 7e7773258a
408 changed files with 17799 additions and 172 deletions

View File

@@ -0,0 +1,20 @@
/*!
* 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/lazysizes.min.js" | relURL }}"]);

View File

@@ -0,0 +1,145 @@
/*!
* 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);
});

View File

@@ -0,0 +1,2 @@
/*! Fetch Inject v2.0.4 | Copyright (C) Josh Habdas <jhabdas@protonmail.com> (https://habd.as) | @license Zlib */
var fetchInject=function(){"use strict";const e=function(e,t,r,n,o,c,i){c=t.createElement(r),i=t.getElementsByTagName(r)[0],c.appendChild(t.createTextNode(n.text)),c.onload=o(n),i?i.parentNode.insertBefore(c,i):t.head.appendChild(c)};return function(t,r){if(!arguments.length)return Promise.reject(new ReferenceError("Failed to execute 'fetchInject': 1 argument required but only 0 present."));if(arguments[0]&&arguments[0].constructor!==Array)return Promise.reject(new TypeError("Failed to execute 'fetchInject': argument 1 must be of type 'Array'."));if(arguments[1]&&arguments[1].constructor!==Promise)return Promise.reject(new TypeError("Failed to execute 'fetchInject': argument 2 must be of type 'Promise'."));const n=[],o=r?[].concat(r):[],c=[];return t.forEach(e=>o.push(window.fetch(e).then(e=>[e.clone().text(),e.blob()]).then(e=>Promise.all(e).then(e=>{n.push({text:e[0],blob:e[1]})})))),Promise.all(o).then(()=>(n.forEach(t=>{c.push({then:r=>{t.blob.type.includes("text/css")?e(window,document,"style",t,r):e(window,document,"script",t,r)}})}),Promise.all(c)))}}();