Skip to content

Commit d023ed0

Browse files
committed
Update
1 parent 90eedab commit d023ed0

1 file changed

Lines changed: 98 additions & 23 deletions

File tree

searxng.html

Lines changed: 98 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,76 @@
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');
130139

131140
const timeout = Number(params.get('timeout')) || TIMEOUT;
132-
for (let i = 0; i < shuffledInstances.length; i++) setTimeout(_ => {
141+
const getMethod = params.get('get') === '1';
142+
for (let i = 0; i < shuffledInstances.length; i++) schedule(_ => {
133143
window.stop();
134144
const retryTargetUrl = new URL(targetUrl.toString());
135145
retryTargetUrl.host = new URL(shuffledInstances[i]).host;
136146
if (i > 0) {
137147
messageElement.innerHTML = `Attempt ${i + 1}/${shuffledInstances.length}: ${retryTargetUrl.host}`;
138148
}
139-
const href = retryTargetUrl.toString();
140-
console.log('trying', href);
141-
if (queryFromForm) {
142-
window.location.href = href;
149+
if (getMethod) {
150+
newParams.forEach((value, key) => retryTargetUrl.searchParams.set(key, value));
151+
const href = retryTargetUrl.toString();
152+
console.log('get', href);
153+
if (queryFromForm) {
154+
window.location.href = href;
155+
} else {
156+
window.location.replace(href);
157+
}
143158
} else {
144-
window.location.replace(href);
159+
const href = retryTargetUrl.toString();
160+
console.log('post', href);
161+
const form = document.createElement('form');
162+
form.method = 'POST';
163+
form.action = href;
164+
newParams.forEach((value, key) => {
165+
const input = document.createElement('input');
166+
input.type = 'hidden';
167+
input.name = key;
168+
input.value = value;
169+
form.appendChild(input);
170+
});
171+
document.body.appendChild(form);
172+
form.submit();
173+
document.body.removeChild(form);
145174
}
146175
}, i * timeout);
147176

148-
setTimeout(_ => messageElement.innerHTML = 'Failed', shuffledInstances.length * timeout);
177+
schedule(_ => messageElement.innerHTML = 'Failed', shuffledInstances.length * timeout);
149178
};
150179

151-
const main = _ => {
180+
const main = async _ => {
152181
const params = new URLSearchParams(window.location.hash.split('#')[1] || '');
182+
const shuffledInstances = await loadAndShuffleInstances(params);
183+
console.log('will be trying instances', shuffledInstances);
184+
153185
if (params.has('q')) {
154-
redirect(params);
186+
redirect(params, shuffledInstances);
155187
} 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>`;
188+
document.body.innerHTML = '';
158189

159190
const input = document.createElement('input');
160191
input.name = 'q';
@@ -164,22 +195,66 @@
164195
input.spellcheck = false;
165196
input.autocorrect = 'off';
166197
input.dir = 'auto';
198+
input.style.borderWidth = '0.15rem';
199+
input.style.fontSize = '1.5rem';
200+
input.style.margin = '2rem 0 1.5rem 0';
167201
input.style.width = '99%';
168202

169203
const form = document.createElement('form');
170204
form.addEventListener('submit', event => {
171205
event.preventDefault();
172-
redirect(params, input.value);
206+
redirect(params, shuffledInstances, input.value);
173207
});
174208

175209
form.appendChild(input);
176210
document.body.appendChild(form);
211+
212+
const pageUrl = `${window.location.protocol}//${window.location.pathname}`;
213+
const div = document.createElement('div');
214+
div.innerHTML = `
215+
<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>
216+
<hr>
217+
<p>Examples for <strong>browser search engine settings</strong> (<code>about:preferences#search</code> or <code>chrome://settings/search</code>):</p>
218+
<ul>
219+
<li><code>${pageUrl}#q=%s&languages=en,fr,ru</code></li>
220+
<li><code>${pageUrl}#q=%s&fast=1&image_proxy=True&categories=images&languages=en,fr,ru</code></li>
221+
</ul>
222+
<p>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>
223+
<ul>
224+
<li><code>languages</code> limit query-based language detection; comma-separated, default is <code>${LANGUAGES.join(',')}</code></li>
225+
<li><code>get</code> pass parameters to the instance using GET method; default is <code>0</code></li>
226+
<li><code>timeout</code> in milliseconds until attempting the next instance; default is <code>${TIMEOUT}</code></li>
227+
<li><code>attempts</code> how many instances to try; default is <code>${ATTEMPTS}</code></li>
228+
<li><code>fast</code> always use predefined instances (instead of fetching them using the <a rel="noopener noreferrer nofollow" target="_blank" href="${INSTANCES_URL}">API</a>); default is <code>0</code></li>
229+
<li><code>instances</code> predefined instances; comma-separated, default is <code>${INSTANCES.join(',')}</code></li>
230+
</ul>
231+
<p>For local use (doesn't require any server): <code>wget ${pageUrl} -O searxng.html</code></p>
232+
<p>Additionally can be combined with the Violentmonkey userscripts:</p>
233+
<ul>
234+
<li><a rel="noopener noreferrer nofollow" target="_blank" href="https://userscripts.codonaft.com/searxng-redirect-on-failure.js">searxng-redirect-on-failure.js</a> — turn search failures into redirects</li>
235+
<li><a rel="noopener noreferrer nofollow" target="_blank" href="https://userscripts.codonaft.com/searxng-force-parameters.js">searxng-force-parameters.js</a> — always enable image proxy and disable SafeSearch</li>
236+
</ul>
237+
<hr>
238+
<p>Existing alternatives:</p>
239+
<ul>
240+
<li><a rel="noopener" target="_blank" href="https://searx.neocities.org/">Neocities</a></li>
241+
<li><a rel="noopener" target="_blank" href="https://github.com/demostanis/gimmeasearx">gimmeasearx</a></li>
242+
</ul>`;
243+
document.body.appendChild(div);
177244
}
178245
};
179246

180247
window.addEventListener('hashchange', main);
248+
window.addEventListener('pageshow', event => {
249+
if (event.persisted) {
250+
console.log('back button is probably pressed');
251+
cancelTasks();
252+
window.removeEventListener('hashchange', main);
253+
window.addEventListener('hashchange', main);
254+
main();
255+
}
256+
});
181257
main();
182-
183258
</script>
184259
</body>
185260
</html>

0 commit comments

Comments
 (0)