1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
import { e } from "./helper.ts"
import { bangs } from "./query.ts"
import { status } from "./ui.ts"
import { pw_modal } from "./pwmodal.ts"
import fuzzysort from "./fuzzysort.js"
export function section_info_start() {
return e("section", { class: "info" },
e("h1", {}, "Fastbangs"),
e("p", {}, `
This application provides a way to (mostly) locally handle search bangs. To use it:
`),
e("ul", {},
e("li", {}, "Select a fallback search engine, used when a search doesn't contain a bang"),
e("li", {}, "Add this search engine to your browser, or bookmark this page"),
),
e("span", {}, "Can't find a search engine? "), e("a", { href: "#~submit" }, "Propose or change a bang."),
)
}
export function section_engine_select() {
const select = async (e: string) => {
const engine = e.toLowerCase()
if (!(await bangs)[engine]) return status("error", `Engine ${JSON.stringify(e)} does not exist.`)
window.location.hash = `#${e}`
}
const listing = e("ul", {})
bangs.then(bangs => {
for (const key in bangs) if (bangs[key]!.pinned) {
listing.append(e("li", { class: "pinned", onclick: () => select(key) }, bangs[key]!.name ?? key))
}
})
const submit = e("button", {}, "Select")
submit.addEventListener("click", () => select(input.value))
const searchResults = e("ul", { class: "dropdown" })
const input = e("input", { type: "search" })
input.addEventListener("keydown", ev => {
if (ev.code == "Enter") select(input.value)
})
input.addEventListener("keyup", _ => {
setSearchResults(searchResults, input, () => submit.click())
})
const manualInput = e("div", { id: "engine-select-manual" },
e("label", {}, "Search engines:"),
input,
submit,
searchResults
)
return e("section", { class: "engine-select" },
e("h2", {}, "Select a search engine"),
manualInput,
listing
)
}
export function section_admin_btn() {
const modal = pw_modal()
const btn = e("button", {
class: "open-modal",
onclick: () => modal.showModal()
}, "Admin Login")
return e("section", {class: "admin-btn"},
btn,
modal)
}
interface FuzzItem<E> { score: number, obj: E }
let bangsSearch: Promise<(query: string) => FuzzItem<{bang: string, name: string, url: string}>[]>
| undefined = undefined;
function setSearchResults(ul: HTMLElement, input: HTMLInputElement, submit: () => void) {
if (bangsSearch === undefined) {
bangsSearch = bangs.then(bangs => {
const searchSpace: {bang: string, name: string, url: string}[] = []
for (const k in bangs) {
searchSpace.push({
bang: k,
name: bangs[k]!.name!,
url: bangs[k]!.url!
})
}
return q => fuzzysort.go(q, searchSpace, {
threshhold: -5000,
limit: 5,
keys: ["bang", "name"],
})
})
}
bangsSearch.then(fs => {
ul.innerHTML = ""
const results = fs(input.value)
if (results.length === 0)
ul.style.display = "none"
else {
ul.style.display = "flex"
for (const r of results) {
const it = r.obj
const li = e("li", {},
e("p", { class: "name" }, it.name),
e("p", { class: "bang" }, it.bang))
li.addEventListener("click", () => {
input.value = it.bang
submit()
})
ul.appendChild(li)
}
}
})
}
|