Skip to content

Commit 63406cb

Browse files
committed
Update
1 parent 90eedab commit 63406cb

1 file changed

Lines changed: 101 additions & 23 deletions

File tree

searxng.html

Lines changed: 101 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<link rel="icon" href="https://searx.space/favicon.png" sizes="any">
1010
<link rel="icon" href="https://searx.space/favicon.svg" type="image/svg+xml">
1111
<link rel="apple-touch-icon" href="https://searx.space/favicon.png">
12-
<title>SearXNG Random Instance Redirector</title>
12+
<title>Minimalist SearXNG Redirector</title>
1313
<style>
1414
code { background-color: rgba(128, 128, 128, 0.4); padding: 0.1rem 0.25rem 0.1rem 0.25rem }
1515
.pale { color: #808080 }
@@ -19,12 +19,20 @@
1919
<script type="module">
2020
import { detectAll } from '{{ site.url }}/assets/js/vendor/tinyld.min.js';
2121

22+
const INSTANCES_URL = 'https://searx.space/data/instances.json';
2223
const INSTANCES = ['search.charliewhiskey.net', 'search.im-in.space', 'search.ipsys.bf', 'search.mycotrip.tech', 'search.ononoki.org', 'search.pollorebozado.com', 'search.rowie.at', 'search.system51.co.uk', 'search.undertale.uk', 'search.url4irl.com', 'searx.bndkt.io', 'searx.dresden.network', 'searx.foss.family', 'searx.oloke.xyz', 'searx.ox2.fr', 'searx.party', 'searx.perennialte.ch', 'searx.tiekoetter.com', 'searx.zhenyapav.com', 'searxng.shreven.org']
2324
const LANGUAGES = ['en'];
2425
const TIMEOUT = 5000;
2526
const ATTEMPTS = 10;
2627
const CUSTOM_PARAMS = ['attempts', 'languages', 'fast', 'instances', 'timeout'];
2728

29+
const tasks = [];
30+
const schedule = (f, pause) => tasks.push(setTimeout(f, pause));
31+
const cancelTasks = _ => {
32+
tasks.forEach(clearTimeout);
33+
tasks.length = 0;
34+
};
35+
2836
const loadAndShuffleInstances = async params => {
2937
const attempts = Number(params.get('attempts')) || ATTEMPTS;
3038
const instancesFromParams = params.get('instances')?.split(',') || [];
@@ -36,7 +44,7 @@
3644
});
3745
if (params.get('fast') === '1') return instances;
3846

39-
const response = await fetch('https://searx.space/data/instances.json');
47+
const response = await fetch(INSTANCES_URL);
4048
if (!response.ok) {
4149
console.log('failed to load instances');
4250
return instances;
@@ -108,53 +116,77 @@
108116
return xs;
109117
};
110118

111-
const redirect = async (params, queryFromForm) => {
119+
const redirect = async (params, shuffledInstances, queryFromForm) => {
120+
window.removeEventListener('hashchange', main);
121+
window.location.hash = '';
122+
112123
document.body.innerHTML = '<div id="message" class="pale"></div>';
113124
const messageElement = document.querySelector('#message');
114125

115-
const shuffledInstances = await loadAndShuffleInstances(params);
116-
console.log('will be trying instances', shuffledInstances);
117-
118126
const targetUrl = new URL(shuffledInstances[0]);
119127
targetUrl.pathname = '/search';
120-
targetUrl.searchParams.set('safesearch', params.get('safesearch') || '0');
121128

129+
const newParams = new Map;
122130
params
123131
.entries()
124132
.filter(([k, _]) => !CUSTOM_PARAMS.includes(k))
125-
.forEach(([k, v]) => targetUrl.searchParams.set(k, v));
133+
.forEach(([k, v]) => newParams.set(k, v));
126134

127135
const query = queryFromForm || params.get('q') || '';
128-
targetUrl.searchParams.set('language', detectLanguage(query, params));
129-
targetUrl.searchParams.set('q', query);
136+
newParams.set('language', detectLanguage(query, params));
137+
newParams.set('q', query);
138+
newParams.set('safesearch', params.get('safesearch') || '0');
139+
newParams.set('image_proxy', params.get('image_proxy') || 'True');
130140

131141
const timeout = Number(params.get('timeout')) || TIMEOUT;
132-
for (let i = 0; i < shuffledInstances.length; i++) setTimeout(_ => {
142+
const getMethod = params.get('get') === '1';
143+
for (let i = 0; i < shuffledInstances.length; i++) schedule(_ => {
133144
window.stop();
134145
const retryTargetUrl = new URL(targetUrl.toString());
135146
retryTargetUrl.host = new URL(shuffledInstances[i]).host;
136147
if (i > 0) {
137148
messageElement.innerHTML = `Attempt ${i + 1}/${shuffledInstances.length}: ${retryTargetUrl.host}`;
138149
}
139-
const href = retryTargetUrl.toString();
140-
console.log('trying', href);
141-
if (queryFromForm) {
142-
window.location.href = href;
150+
if (getMethod) {
151+
newParams.forEach((value, key) => retryTargetUrl.searchParams.set(key, value));
152+
const href = retryTargetUrl.toString();
153+
console.log('get', href);
154+
if (queryFromForm) {
155+
window.location.href = href;
156+
} else {
157+
window.location.replace(href);
158+
}
143159
} else {
144-
window.location.replace(href);
160+
const href = retryTargetUrl.toString();
161+
console.log('post', href);
162+
const form = document.createElement('form');
163+
form.method = 'POST';
164+
form.action = href;
165+
newParams.forEach((value, key) => {
166+
const input = document.createElement('input');
167+
input.type = 'hidden';
168+
input.name = key;
169+
input.value = value;
170+
form.appendChild(input);
171+
});
172+
document.body.appendChild(form);
173+
form.submit();
174+
document.body.removeChild(form);
145175
}
146176
}, i * timeout);
147177

148-
setTimeout(_ => messageElement.innerHTML = 'Failed', shuffledInstances.length * timeout);
178+
schedule(_ => messageElement.innerHTML = 'Failed', shuffledInstances.length * timeout);
149179
};
150180

151-
const main = _ => {
181+
const main = async _ => {
152182
const params = new URLSearchParams(window.location.hash.split('#')[1] || '');
183+
const shuffledInstances = await loadAndShuffleInstances(params);
184+
console.log('will be trying instances', shuffledInstances);
185+
153186
if (params.has('q')) {
154-
redirect(params);
187+
redirect(params, shuffledInstances);
155188
} else {
156-
const pageUrl = window.location.origin + window.location.pathname;
157-
document.body.innerHTML = `<p>This page redirects to a <a rel="noopener noreferrer nofollow" href="${pageUrl}#q=" onclick="window.location.replace('${pageUrl}#q=')">random SearXNG</a> instance.</p><p>It also supports the <a rel="noopener noreferrer nofollow" target="_blank" href="https://docs.searxng.org/dev/search_api.html">GET parameters</a> (provided as an anchor for privacy) and the optional parameters:<p><p><ul><li><code>languages=en,fr,ru</code> to use automatic language filter derived from the query; default is <code>${LANGUAGES.join(',')}</code></li><li><code>fast=1</code> to force use of predefined instances (instead of fetching them using the <a rel="noopener noreferrer nofollow" target="_blank" href="https://searx.space/data/instances.json">API</a>); default is <code>0</code></li><li><code>instances=search.ononoki.org,searx.bndkt.io</code> to predefine the instances; default is <code>${INSTANCES.join(',')}</code></li><li><code>timeout</code> in milliseconds until attempting the next instance; default is <code>${TIMEOUT}</code></li><li><code>attempts</code> how many instances to try; default is <code>${ATTEMPTS}</code></li></ul></p><p>Example for <strong>browser search engine settings</strong> (<code>about:preferences#search</code> or <code>chrome://settings/search</code>): <code>${pageUrl}#q=%s&fast=1&image_proxy=True&categories=images&languages=en,fr,ru</code>.</p><p>For local use: <code>wget ${pageUrl} -O searxng.html</code>.</p><p>Additionally can be combined with the Violentmonkey <a rel="noopener noreferrer nofollow" target="_blank" href="https://userscripts.codonaft.com/searxng-redirect-on-failure.js">userscript</a> to turn search failures into redirects as well.</p>`;
189+
document.body.innerHTML = '';
158190

159191
const input = document.createElement('input');
160192
input.name = 'q';
@@ -164,22 +196,68 @@
164196
input.spellcheck = false;
165197
input.autocorrect = 'off';
166198
input.dir = 'auto';
199+
input.style.borderWidth = '0.15rem';
200+
input.style.fontSize = '1.5rem';
201+
input.style.margin = '2rem 0 1.5rem 0';
167202
input.style.width = '99%';
168203

169204
const form = document.createElement('form');
170205
form.addEventListener('submit', event => {
171206
event.preventDefault();
172-
redirect(params, input.value);
207+
redirect(params, shuffledInstances, input.value);
173208
});
174209

175210
form.appendChild(input);
176211
document.body.appendChild(form);
212+
input.focus();
213+
214+
const pageUrl = `${window.location.protocol}//${window.location.pathname}`;
215+
const div = document.createElement('div');
216+
div.innerHTML = `
217+
<p>This page redirects to a <a rel="noopener noreferrer nofollow" href="${pageUrl}#q=" onclick="window.location.replace('${pageUrl}#q=')">random SearXNG</a> instance.</p>
218+
<hr>
219+
<p>Examples for <strong>browser search engine settings</strong> (<code>about:preferences#search</code> or <code>chrome://settings/search</code>):</p>
220+
<ul>
221+
<li><code>${pageUrl}#q=%s&languages=en,fr,ru</code></li>
222+
<li><code>${pageUrl}#q=%s&fast=1&image_proxy=False&categories=images&languages=en,fr,ru&get=1</code></li>
223+
</ul>
224+
<p>Supports the following parameters (provided as an anchor for privacy):</p>
225+
<ul>
226+
<li>normal SearXNG GET/POST <a rel="noopener" target="_blank" href="https://docs.searxng.org/dev/search_api.html">parameters</a> (<code>safesearch=0</code> and <code>image_proxy=True</code> are set by default)</li>
227+
<li><code>languages</code> limit query-based language detection; comma-separated, default is <code>${LANGUAGES.join(',')}</code></li>
228+
<li><code>get</code> pass parameters to the instance using GET method; default is <code>0</code></li>
229+
<li><code>timeout</code> in milliseconds until attempting the next instance; default is <code>${TIMEOUT}</code></li>
230+
<li><code>attempts</code> how many instances to try; default is <code>${ATTEMPTS}</code></li>
231+
<li><code>fast</code> always use the predefined SearXNG instances rather than just as a fallback (no <a rel="noopener" target="_blank" href="${INSTANCES_URL}">API</a> will be used); default is <code>0</code></li>
232+
<li><code>instances</code> the predefined instances (use <code>fast=1</code> to force the use of them); comma-separated, default is <code>${INSTANCES.join(',')}</code></li>
233+
</ul>
234+
<p>For local use (doesn't require any server): <code>wget ${pageUrl} -O searxng.html</code></p>
235+
<p>Additionally can be combined with the Violentmonkey userscripts:</p>
236+
<ul>
237+
<li><a rel="noopener" target="_blank" href="https://userscripts.codonaft.com/redirect-searxng-on-failure.js">redirect-searxng-on-failure.js</a> — turn search failures into redirects</li>
238+
<li><a rel="noopener" target="_blank" href="https://userscripts.codonaft.com/force-searxng-parameters.js">force-searxng-parameters.js</a> — always enable image proxy and disable SafeSearch</li>
239+
</ul>
240+
<hr>
241+
<p>Existing alternatives:</p>
242+
<ul>
243+
<li><a rel="noopener" target="_blank" href="https://searx.neocities.org/">Neocities</a></li>
244+
<li><a rel="noopener" target="_blank" href="https://github.com/demostanis/gimmeasearx">gimmeasearx</a></li>
245+
</ul>`;
246+
document.body.appendChild(div);
177247
}
178248
};
179249

180250
window.addEventListener('hashchange', main);
181-
main();
251+
window.addEventListener('pageshow', event => {
252+
if (!event.persisted) return;
182253

254+
console.log('back button is probably pressed');
255+
cancelTasks();
256+
window.removeEventListener('hashchange', main);
257+
window.addEventListener('hashchange', main);
258+
main();
259+
});
260+
main();
183261
</script>
184262
</body>
185263
</html>

0 commit comments

Comments
 (0)