Support custom search engine URLS, currently SearXNG and Whoogle
parent
fd935f1755
commit
052bb27887
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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', () => {
|
||||
|
|
|
@ -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 });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue