Support custom search engine URLS, currently SearXNG and Whoogle

pull/583/head
James McKee 2024-03-18 08:03:37 +00:00 committed by GitHub
parent fd935f1755
commit 052bb27887
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 201 additions and 3 deletions

View File

@ -38,7 +38,7 @@ Large, corporate-run wiki farms have enabled hundreds of great wikis and communi
When visiting a wiki on a large corporate wiki farm such as Fandom, Indie Wiki Buddy will notify and/or automatically redirect you if a quality, independent alternative is available. You can customize your experience per-wiki.
In addition, search results in Google, Bing, DuckDuckGo, Yahoo, Brave Search, Ecosia, Startpage, Qwant, Yandex, and Kagi can also be filtered, replacing non-independent wikis with text inviting you to visit the independent counterpart.
In addition, search results in Google, Bing, DuckDuckGo, Yahoo, Brave Search, Ecosia, Startpage, Qwant, Yandex, Kagi, SearXNG, and Whoogle can also be filtered, replacing non-independent wikis with text inviting you to visit the independent counterpart.
Indie Wiki Buddy also supports [BreezeWiki](https://breezewiki.com/), a service that renders Fandom wikis without ads or bloat. This helps give you a more enjoyable reading experience on Fandom when an independent wiki isn't available.

View File

@ -101,8 +101,7 @@
<br /><br />
When you visit a wiki on a large, corporate-run wiki host, this extension can notify or automatically redirect
you to quality independent wikis when they're available.
Search results in Google, Bing, DuckDuckGo, Yahoo, Brave Search, Ecosia, Startpage, Qwant, Yandex, and Kagi can
also be filtered,
Search results in Google, Bing, DuckDuckGo, Yahoo, Brave Search, Ecosia, Startpage, Qwant, Yandex, Kagi, SearXNG, and Whoogle can also be filtered,
replacing non-independent wikis with links to independent counterpart (or hiding them completely).
<br /><br />
We currently redirect from Fandom, Fextralife, and Neoseeker wikis to independent counterparts.

View File

@ -269,6 +269,40 @@
font-size: .9em;
}
#customSearchEnginesContainer {
display: flex;
flex-direction: column;
gap: 0.5em;
}
#customSearchEnginesList>div {
display: flex;
flex-direction: row;
padding: 0.5em .5em;
min-height: 20px;
align-items: center;
gap: 0.5em;
}
#customSearchEnginesList>div:hover {
background-color: #e8f0fe;
}
.customSearchEngineHostname {
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
}
#customSearchEnginesAdd {
display: flex;
gap: 0.5em;
}
#customSearchEnginesAdd input {
width: 25em;
}
#legend div,
#legend span {
padding: 0 0 0 8px;
@ -660,6 +694,22 @@
</label>
</div>
</fieldset>
<fieldset id="customSearchEngines">
<legend>
<span aria-hidden="true">🔍</span> Custom search engines
</legend>
<div id="customSearchEnginesContainer">
<div id="customSearchEnginesList"></div>
<div id="customSearchEnginesAdd">
<input type="text" id="newCustomSearchEngineDomain" placeholder="Search engine domain (e.g. search.example.org)" />
<select id="newCustomSearchEnginePreset">
<option value="searxng">SearXNG</option>
<option value="whoogle">Whoogle</option>
</select>
<button id="addCustomSearchEngine">Add</button>
</div>
</div>
</fieldset>
<h2>Individual wiki settings</h2>
<fieldset id="legend">
<legend>

View File

@ -374,6 +374,43 @@ async function loadOptions(lang, textFilter = '') {
});
}
function displayCustomSearchEngine(customSearchEngineHostname, customSearchEnginePreset) {
let customSearchEnginesList = document.getElementById('customSearchEnginesList');
let listItem = document.createElement('div');
listItem.classList.add('customSearchEngine');
let customSearchEngineHostnameLabel = document.createElement('span');
customSearchEngineHostnameLabel.classList.add('customSearchEngineHostname');
customSearchEngineHostnameLabel.innerText = customSearchEngineHostname;
let customSearchEnginePresetLabel = document.createElement('span');
customSearchEnginePresetLabel.classList.add('customSearchEnginePreset');
customSearchEnginePresetLabel.innerText = document.getElementById('newCustomSearchEnginePreset')
.querySelector(`option[value="${customSearchEnginePreset}"]`).innerText;
let customSearchEngineDeleteButton = document.createElement('button');
customSearchEngineDeleteButton.classList.add('customSearchEngineDelete');
customSearchEngineDeleteButton.innerText = 'Delete';
customSearchEngineDeleteButton.addEventListener('click', () => {
listItem.remove();
chrome.storage.sync.get({ 'customSearchEngines': {} }, (item) => {
let customSearchEngines = item.customSearchEngines;
delete customSearchEngines[customSearchEngineHostname];
chrome.storage.sync.set({ 'customSearchEngines': customSearchEngines });
});
chrome.scripting.unregisterContentScripts({ ids: [`content-search-filtering-${customSearchEngineHostname}`] });
});
listItem.appendChild(customSearchEngineHostnameLabel);
listItem.appendChild(customSearchEnginePresetLabel);
listItem.appendChild(customSearchEngineDeleteButton);
customSearchEnginesList.appendChild(listItem);
}
// Set power setting
function setPower(setting, storeSetting = true) {
if (storeSetting) {
@ -525,6 +562,66 @@ document.addEventListener('DOMContentLoaded', () => {
return false;
});
// Add event listener for adding custom search engine
function addCustomSearchEngine() {
let customSearchEngine = document.getElementById('newCustomSearchEngineDomain').value;
// Add "https://" if not already present
if (!customSearchEngine.includes('://')) {
customSearchEngine = 'https://' + customSearchEngine;
}
customSearchEngine = new URL(customSearchEngine);
// Check not already added
let hostnames = document.querySelectorAll('.customSearchEngineHostname');
for (let i = 0; i < hostnames.length; i++) {
if (hostnames[i].innerText === customSearchEngine.hostname) {
return;
}
}
chrome.permissions.request({
origins: [ `${customSearchEngine}*` ]
}, (granted) => {
// Callback is true if the user granted the permissions.
if (!granted) return;
chrome.scripting.registerContentScripts([{
id: `content-search-filtering-${customSearchEngine.hostname}`,
matches: [customSearchEngine + '*'],
js: [ '/scripts/common-functions.js', '/scripts/content-search-filtering.js' ],
runAt: "document_start"
}]);
let customSearchEnginePreset = document.getElementById('newCustomSearchEnginePreset').value;
chrome.storage.sync.get({ 'customSearchEngines': {} }, (item) => {
let customSearchEngines = item.customSearchEngines;
customSearchEngines[customSearchEngine.hostname] = customSearchEnginePreset;
chrome.storage.sync.set({ 'customSearchEngines': customSearchEngines });
});
displayCustomSearchEngine(customSearchEngine.hostname, customSearchEnginePreset);
document.getElementById('newCustomSearchEngineDomain').value = '';
});
}
document.getElementById('addCustomSearchEngine').addEventListener('click', () => {
addCustomSearchEngine();
});
document.getElementById('newCustomSearchEngineDomain').onkeyup = function(e) {
if (e.key === 'Enter') {
addCustomSearchEngine();
}
}
chrome.storage.sync.get({ 'customSearchEngines': {} }, (item) => {
Object.keys(item.customSearchEngines).forEach((key) => {
displayCustomSearchEngine(key, item.customSearchEngines[key]);
});
});
// Add event listeners for default action selections
document.querySelectorAll('[name="defaultWikiAction"]').forEach((el) => {
el.addEventListener('change', () => {

View File

@ -336,6 +336,12 @@ function hideSearchResults(searchResultContainer, searchEngine, site, showBanner
case 'kagi':
document.querySelector('#main').prepend(searchRemovalNotice);
break;
case 'searxng':
document.querySelector('#results').prepend(searchRemovalNotice);
break;
case 'whoogle':
document.querySelector('#main').prepend(searchRemovalNotice);
break;
default:
}
}
@ -488,6 +494,12 @@ async function filterSearchResults(searchResults, searchEngine, storage) {
case 'kagi':
searchResultContainer = searchResult.closest('div.search-result, div.__srgi');
break;
case 'searxng':
searchResultContainer = searchResult.closest('article');
break;
case 'whoogle':
searchResultContainer = searchResult.closest('#main>div>div, details>div>div>div>div>div>div.has-favicon');
break;
default:
}
@ -756,6 +768,46 @@ function main(mutations = null, observer = null) {
}
}, { once: true });
}
} else if (storage.customSearchEngines) {
function filterSearXNG() {
let searchResults = Array.from(document.querySelectorAll('h3>a')).filter(el =>
el.href?.includes('.fandom.com') ||
el.href?.includes('.wiki.fextralife.com') ||
el.href?.includes('.neoseeker.com/wiki/'));
filterSearchResults(searchResults, 'searxng', storage);
}
function filterWhoogle() {
let searchResults = Array.from(document.querySelectorAll('div>a')).filter(el =>
el.href?.includes('.fandom.com') ||
el.href?.includes('.wiki.fextralife.com') ||
el.href?.includes('.neoseeker.com/wiki/'));
filterSearchResults(searchResults, 'whoogle', storage);
}
function filter(searchEngine) {
if (searchEngine === 'searxng') {
filterSearXNG();
} else if (searchEngine === 'whoogle') {
filterWhoogle();
}
}
let customSearchEngines = storage.customSearchEngines;
if (customSearchEngines[currentURL.hostname]) {
let customSearchEnginePreset = customSearchEngines[currentURL.hostname];
// Wait for document to be interactive/complete:
if (['interactive', 'complete'].includes(document.readyState)) {
filter(customSearchEnginePreset);
} else {
document.addEventListener('readystatechange', e => {
if (['interactive', 'complete'].includes(document.readyState)) {
filter(customSearchEnginePreset);
}
}, { once: true });
}
}
}
}
});