const storageType = 'localStorage' let data = {} const statusEl = document.getElementById('status') const formEl = document.getElementById('search') const errors = { missingQuery: new TypeError('Query is missing.'), noFoundInstances: new Error('No instances were found.'), } function buildSearchURL (opts = {}) { if (typeof opts.query !== 'string') throw errors.missingQuery if (!('instances' in data)) throw errors.noFoundInstances if (data.instances.length === 0) throw errors.noFoundInstances // Random instance const n = Math.floor(Math.random() * data.instances.length) const instanceURL = data.instances[n] // Build URL const params = new URLSearchParams({ q: opts.query }) if (opts.category && opts.category !== 'all') { params.append(`category_${opts.category}`, '') } const url = `${instanceURL}?${params.toString()}` return url } // Register event listeners formEl.addEventListener('submit', (event) => { event.preventDefault() location.assign(buildSearchURL({ query: formEl.query.value, category: formEl.category.value })) }) function locallyGetInstances () { if (!(storageType in window)) return [] const item = window[storageType].getItem('instances') try { return JSON.parse(item) } catch (_err) { return [] } } function locallySetInstances () { if (!(storageType in window)) return if (!('instances' in data)) return if (data.instances.length === 0) return window[storageType].setItem( 'instances', JSON.stringify(data.instances) ) } async function remotelyGetInstances () { const instances = await fetch('https://searx.space/data/instances.json') .then((req) => req.json()) .then((json) => Object.entries(json.instances)) .then((entries) => entries.filter(([_url, details]) => { if (details.network_type !== 'normal') return if (details.uptime == null) return if (details.uptime.uptimeDay !== 100) return return true })) .then((entries) => entries.map(([url]) => url)) .catch((err) => { console.error(err) return [] }) window[storageType].setItem('timestamp', Date.now().toString()) return instances } // Get local instances data.instances = locallyGetInstances() // Fetch remote instances if necessary const timestamp = +window[storageType].getItem('timestamp') if (Date.now() > timestamp + 3_600_000 /* 1 hour */) { const remoteInstances = await remotelyGetInstances() if (remoteInstances.length > 0) { data.instances = remoteInstances locallySetInstances() } } // Auto-redirect in presence of search parameters if (location.search.length > 0) { const searchParams = new URLSearchParams(location.search.slice(1)) if (searchParams.has('q')) { location.assign(buildSearchURL({ query: searchParams.get('q'), category: searchParams.get('in') ?? 'all' })) } } statusEl.textContent = `${data.instances.length} instances` if (data.instances.length === 0) { statusEl.classList.add('error') }