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
|
/// <reference lib="dom" />
interface Bangs { [key: string]: string }
let bangs: Bangs = {}
let status_el: HTMLElement
globalThis.addEventListener("hashchange", () => process_url())
globalThis.addEventListener("load", async () => {
status_el = document.createElement("p")
document.body.append(status_el)
const bangs_res = await fetch("/bangs.json")
if (!bangs_res.ok) document.writeln("error: could not download bangs.json")
bangs = await bangs_res.json()
process_url()
})
function setup_page(engine?: string) {
document.getElementById("content")?.remove()
const section = document.createElement("section")
section.id = "content"
if (engine) {
if (document.getElementById("search-link")) {
// <link /> is already present, we need reload because browser wont notice otherwise
window.location.reload()
}
const link = document.createElement("link")
link.rel = "search"
link.id = "search-link"
link.type = "application/opensearchdescription+xml"
link.href = `/search.xml?default=${encodeURIComponent(engine)}`
link.title = `Laufente (default engine: ${engine})`
document.head.append(link)
const heading = document.createElement("h1")
heading.textContent = engine
const input = document.createElement("input")
input.type = "input"
input.addEventListener("keydown", ev => {
if (ev.code == "Enter") process_query(engine, input.value ?? "")
})
const submit = document.createElement("button")
submit.textContent = "Search"
submit.addEventListener("click", () => {
process_query(engine, input.value ?? "")
})
const info = document.createElement("p")
info.textContent = `To install this as the default search engine, select "Add <name>" in the context-menu of the URL-bar.`
section.append(heading, input, submit, info)
} else {
const info = document.createElement("p")
info.textContent = `todo`
section.append(info)
}
document.body.append(section)
}
function status(text: string, color?: string) {
status_el.textContent = text
if (color) status_el.style.color = color
}
function process_url() {
if (document.location.hash.length != 0) {
const input = document.location.hash.substring(1)
const [default_engine, query_encoded] = input.split("#")
if (!query_encoded) return setup_page(default_engine)
const query = decodeURIComponent(query_encoded.replaceAll("+"," "))
process_query(default_engine, query)
} else {
setup_page()
}
}
function process_query(default_engine: string, query: string) {
const bang_prefix = "!"
let url = ""
if (query.startsWith(bang_prefix)) {
const [engine, ...query_parts] = query.substring(bang_prefix.length).split(" ")
url = search_url(engine, query_parts.join(" "))
} else {
url = search_url(default_engine, query)
}
status(`Forwarding to ${url}`, "green")
setTimeout(() => document.location.href = url, 0)
}
function search_url(engine: string, query: string) {
return bangs[engine].replace("{{{s}}}", encodeURIComponent(query).replaceAll("%20","+"))
}
|