Improvements to Google search results re-ordering

* Instead of moving indie results to the top of Google search results, we now move indie results above the first non-indie (Fandom / Fextra / Neoseeker) result. If no non-indie result appears, re-ordering doesn't occur. This is to avoid moving less relevant results to the top, particularly for searches for generic terms.
* Improved Google filtering to account for when Google uses their own middleman link
pull/700/head
Kevin Payravi 2024-05-29 22:24:58 +02:00
parent 47148f5f34
commit 67b77a234c
5 changed files with 137 additions and 126 deletions

View File

@ -110,10 +110,9 @@
</li>
<br />
<li>
<b><img src="../../images/toggle-move-up.png" height="12" alt="" /> On Google, move indie wiki results to the top of
the page</b>
<b><img src="../../images/toggle-move-up.png" height="12" alt="" /> On Google, move indie wiki results above non-indie results</b>
<br />
By default, Indie Wiki Buddy will re-order the results page on Google to move indie wiki results to the top,
By default, Indie Wiki Buddy will re-order the results page on Google to move indie wiki results above the first non-indie (Fandom / Fextralife / Neoseeker) result,
if they are further down the first page of results. This allows you to find the pages you're looking for on indie wikis
quicker.
<br /><br />

View File

@ -175,7 +175,7 @@
<label>
<input id="reorderResultsCheckbox" type="checkbox" />
<img src="../../images/toggle-move-up.png" height="12" alt="" />
On Google, move indie wiki results to the top of the page
On Google, move indie wiki results above non-indie results
</label>
</div>
</fieldset>

View File

@ -166,7 +166,7 @@
<label>
<input id="reorderResultsCheckbox" type="checkbox" />
<img src="../../images/toggle-move-up.png" height="12" alt="" />
On Google, move indie wiki results to the top of the page
On Google, move indie wiki results above non-indie results
</label>
</div>
</fieldset>

View File

@ -79,32 +79,32 @@ async function populateSiteDataByOrigin() {
let promises = [];
for (let i = 0; i < LANGS.length; i++) {
promises.push(fetch(extensionAPI.runtime.getURL('data/sites' + LANGS[i] + '.json'))
.then((resp) => resp.json())
.then((jsonData) => {
jsonData.forEach((site) => {
site.origins.forEach((origin) => {
sites.push({
"id": site.id,
"origin": origin.origin,
"origin_base_url": origin.origin_base_url,
"origin_content_path": origin.origin_content_path,
"origin_main_page": origin.origin_main_page,
"destination": site.destination,
"destination_base_url": site.destination_base_url,
"destination_search_path": site.destination_search_path,
"destination_content_prefix": origin.destination_content_prefix || site.destination_content_prefix || "",
// /w/index.php?title= is the default path for a new MediaWiki install, change as accordingly in config JSON files
"destination_content_path": site.destination_content_path || "/w/index.php?title=",
"destination_content_suffix": origin.destination_content_suffix || site.destination_content_suffix || "",
"destination_platform": site.destination_platform,
"destination_icon": site.destination_icon,
"destination_main_page": site.destination_main_page,
"tags": site.tags || [],
"language": LANGS[i]
})
.then((resp) => resp.json())
.then((jsonData) => {
jsonData.forEach((site) => {
site.origins.forEach((origin) => {
sites.push({
"id": site.id,
"origin": origin.origin,
"origin_base_url": origin.origin_base_url,
"origin_content_path": origin.origin_content_path,
"origin_main_page": origin.origin_main_page,
"destination": site.destination,
"destination_base_url": site.destination_base_url,
"destination_search_path": site.destination_search_path,
"destination_content_prefix": origin.destination_content_prefix || site.destination_content_prefix || "",
// /w/index.php?title= is the default path for a new MediaWiki install, change as accordingly in config JSON files
"destination_content_path": site.destination_content_path || "/w/index.php?title=",
"destination_content_suffix": origin.destination_content_suffix || site.destination_content_suffix || "",
"destination_platform": site.destination_platform,
"destination_icon": site.destination_icon,
"destination_main_page": site.destination_main_page,
"tags": site.tags || [],
"language": LANGS[i]
})
});
}));
})
});
}));
}
await Promise.all(promises);
@ -136,7 +136,7 @@ async function commonFunctionFindMatchingSite(site, crossLanguageSetting, dest =
matchingSites = sites.filter(el => site.replace(/.*https?:\/\//, '').startsWith(el[base_url_key]));
} else {
matchingSites = sites.filter(el =>
site.replace(/.*https?:\/\//, '').startsWith(dest ? el[base_url_key] : (el.origin_base_url + el.origin_content_path))
site.replace(/.*https?:\/\//, '').startsWith(dest ? el[base_url_key] : (el.origin_base_url + el.origin_content_path))
|| site.replace(/.*https?:\/\//, '').replace(/\/$/, '') === el[base_url_key]
);
}
@ -159,8 +159,9 @@ async function commonFunctionFindMatchingSite(site, crossLanguageSetting, dest =
}
function commonFunctionGetOriginArticle(originURL, matchingSite) {
let url = new URL(originURL);
return decodeURIComponent(String(url.pathname).split(matchingSite['origin_content_path'])[1] || '');
let url = new URL('https://' + originURL.replace(/.*https?:\/\//, ''));
const path = decodeURIComponent(decodeURIComponent(String(url.pathname.split('&')[0]).split(matchingSite['origin_content_path'])[1] || ''));
return path;
}
function commonFunctionGetDestinationArticle(matchingSite, article) {

View File

@ -161,7 +161,7 @@ function escapeRegex(string) {
function removeSubstringIfAtEnd(str, sub) {
if (sub && str.endsWith(sub)) {
return str.slice(0, -sub.length);
return str.slice(0, -sub.length);
}
return str;
}
@ -393,38 +393,7 @@ function findClosestElement(target, elements) {
return closestElement;
}
async function filterSearchResult(matchingSite, searchResult, searchEngine, countFiltered, storage, reorderedHrefs) {
// Get user's settings for the wiki
let id = matchingSite['id'];
let searchFilterSetting = 'replace';
let searchEngineSettings = await commonFunctionDecompressJSON(storage.searchEngineSettings || {});
if (searchEngineSettings[id]) {
searchFilterSetting = searchEngineSettings[id];
} else if (storage.defaultSearchAction) {
searchFilterSetting = storage.defaultSearchAction;
}
// Output stylesheet if not already done
if (!document.querySelector('.iwb-styles')) {
const headElement = document.querySelector('head');
if (headElement) {
insertCSS();
} else {
// If head element doesn't exist, wait for it via MutationObserver
const docObserver = new MutationObserver((mutations, mutationInstance) => {
const headElement = document.querySelector('head');
if (headElement && !document.querySelector('.iwb-styles')) {
insertCSS();
mutationInstance.disconnect();
}
});
docObserver.observe(document, {
childList: true,
subtree: true
});
}
}
function getSearchContainer(searchEngine, searchResult) {
let searchResultContainer = null;
switch (searchEngine) {
@ -472,11 +441,48 @@ async function filterSearchResult(matchingSite, searchResult, searchEngine, coun
default:
}
return searchResultContainer;
}
async function filterSearchResult(matchingSite, searchResult, searchEngine, countFiltered, storage, reorderedHrefs) {
// Get user's settings for the wiki
let id = matchingSite['id'];
let searchFilterSetting = 'replace';
let searchEngineSettings = await commonFunctionDecompressJSON(storage.searchEngineSettings || {});
if (searchEngineSettings[id]) {
searchFilterSetting = searchEngineSettings[id];
} else if (storage.defaultSearchAction) {
searchFilterSetting = storage.defaultSearchAction;
}
// Output stylesheet if not already done
if (!document.querySelector('.iwb-styles')) {
const headElement = document.querySelector('head');
if (headElement) {
insertCSS();
} else {
// If head element doesn't exist, wait for it via MutationObserver
const docObserver = new MutationObserver((mutations, mutationInstance) => {
const headElement = document.querySelector('head');
if (headElement && !document.querySelector('.iwb-styles')) {
insertCSS();
mutationInstance.disconnect();
}
});
docObserver.observe(document, {
childList: true,
subtree: true
});
}
}
const searchResultContainer = getSearchContainer(searchEngine, searchResult);
if (searchResultContainer) {
// If this page from Fandom is the same as a re-ordered page, filter it out
let originArticle = commonFunctionGetOriginArticle(searchResult.href, matchingSite);
let searchResultLink = searchResult.href;
let originArticle = commonFunctionGetOriginArticle(searchResultLink, matchingSite);
let destinationArticle = commonFunctionGetDestinationArticle(matchingSite, originArticle);
if (reorderedHrefs.find((href) => href.match(
new RegExp(
`http(s)*://${matchingSite['destination_base_url']}${matchingSite['destination_content_path']}${encodeURIComponent(destinationArticle)}$`
@ -497,32 +503,20 @@ async function filterSearchResult(matchingSite, searchResult, searchEngine, coun
return countFiltered;
}
async function reorderDestinationSearchResult(resultsFirstChild, searchResult) {
// Find containing element for the search result
const closestJsController = searchResult.closest('div[jscontroller]');
const closestDataDiv = searchResult.closest('div[data-hveid].g') || searchResult.closest('div[data-hveid]');
searchResultContainer = findClosestElement(searchResult, [closestJsController, closestDataDiv]);
async function reorderDestinationSearchResult(firstNonIndieResult, searchResult) {
// Find containing element for non-indie result
const nonIndieSearchResultContainer = getSearchContainer('google', firstNonIndieResult);
// Find the element holding the search results,
// to prepend the destination wiki result
let searchResultsList = document.querySelector('#search') || document.querySelector('#topstuff');
if (!searchResultsList) {
if (document.querySelector('#main')) {
var el = document.querySelector('#main');
if (el.querySelector('#main > div[data-hveid]')) {
searchResultsList = el.querySelector('div[data-hveid]');
} else {
searchResultsList = el.querySelector('div div[data-hveid]').parentElement;
}
};
}
// Find containing element for the indie search result
const indieSearchResultContainer = getSearchContainer('google', searchResult);
if (!resultsFirstChild || !searchResultContainer || searchResultContainer.classList.contains('iwb-reordered')) {
if (!indieSearchResultContainer || indieSearchResultContainer.classList.contains('iwb-reordered')) {
return;
}
searchResultContainer.classList.add('iwb-reordered');
searchResultsList.prepend(searchResultContainer);
indieSearchResultContainer.classList.add('iwb-reordered');
// Prepend search results to first Fandom/Fextra/Neoseeker result
nonIndieSearchResultContainer.parentNode.prepend(indieSearchResultContainer);
}
async function reorderSearchResults(searchResults, searchEngine, storage) {
@ -538,14 +532,31 @@ async function reorderSearchResults(searchResults, searchEngine, storage) {
if (searchEngine !== 'google') return;
// Get the first element in the results container
let resultsFirstChild = document.querySelector('#rso div[data-hveid].g') ||
document.querySelector('#main div[data-hveid].g') ||
document.querySelector('#rso div[data-hveid] div[data-dsrp]') ||
document.querySelector('#main div[data-hveid] div[data-dsrp]') ||
document.querySelector('#rso div[data-hveid]') ||
document.querySelector('#main div[data-hveid]');
const resultsFirstChild = document.querySelector('#rso div[data-hveid].g') ||
document.querySelector('#main div[data-hveid].g') ||
document.querySelector('#rso div[data-hveid] div[data-dsrp]') ||
document.querySelector('#main div[data-hveid] div[data-dsrp]') ||
document.querySelector('#rso div[data-hveid]') ||
document.querySelector('#main div[data-hveid]');
if (!resultsFirstChild) return;
// Get the first Fandom/Fextralife/Neoseeker result, if it exists
const firstNonIndieResult = document.querySelector(`
div[data-hveid] a[href*='.fandom.com/'][href*='/wiki/']:first-of-type:not([role='button']):not([target='_self']),
div[data-hveid] a[href*='.wiki.fextralife.com/']:first-of-type:not([role='button']):not([target='_self']),
div[data-hveid] a[href*='.neoseeker.com/wiki/']:first-of-type:not([role='button']):not([target='_self'])`);
if (!resultsFirstChild || !firstNonIndieResult) return;
searchResults.some((result, i) => {
if (result.matches(`
div[data-hveid] a[href*='.fandom.com/'][href*='/wiki/']:first-of-type:not([role='button']):not([target='_self']),
div[data-hveid] a[href*='.wiki.fextralife.com/']:first-of-type:not([role='button']):not([target='_self']),
div[data-hveid] a[href*='.neoseeker.com/wiki/']:first-of-type:not([role='button']):not([target='_self'])`)) {
searchResults.splice(0, i + 1);
return true;
}
return false;
});
let crossLanguageSetting = storage.crossLanguage || 'off';
let resultsToSort = [];
@ -585,7 +596,7 @@ async function reorderSearchResults(searchResults, searchEngine, storage) {
for (const searchResult of resultsToSort) {
try {
await reorderDestinationSearchResult(resultsFirstChild, searchResult);
await reorderDestinationSearchResult(firstNonIndieResult, searchResult);
reorderedHrefs.push(searchResult.href);
} catch (e) {
console.log('Indie Wiki Buddy failed to properly re-order search results with error: ' + e);
@ -603,7 +614,7 @@ async function filterSearchResults(searchResults, searchEngine, storage, reorder
try {
// Check that result isn't within another result
if (!searchResult.closest('.iwb-detected') || !searchResult.closest('.iwb-detected')?.querySelector('.iwb-new-link')) {
searchResultLink = searchResult.href || '';
let searchResultLink = searchResult.href || '';
if (!searchResultLink) {
continue;
@ -612,7 +623,7 @@ async function filterSearchResults(searchResults, searchEngine, storage, reorder
if (searchEngine === 'google') {
// Break if image result:
if (searchResultLink.includes('imgurl=')) {
break;
continue;
}
// Skip if result doesn't include specific tags/attributes
@ -621,8 +632,7 @@ async function filterSearchResults(searchResults, searchEngine, storage, reorder
searchResult.querySelector('h1') ||
searchResult.querySelector('h3') ||
searchResult.querySelector('cite') ||
searchResult.querySelector("div[role='link']")))
{
searchResult.querySelector("div[role='link']"))) {
searchResult.classList.add('iwb-detected');
continue;
}
@ -663,9 +673,10 @@ function startFiltering(searchEngine, storage, mutations = null, observer = null
// Function to filter search results in Google
function filterGoogle(reorderedHrefs) {
let searchResults = document.querySelectorAll(`
div[data-hveid] a[href*='.fandom.com/']:first-of-type:not([role='button']):not([target='_self']),
div[data-hveid] a[href*='.wiki.fextralife.com/']:first-of-type:not([role='button']):not([target='_self']),
div[data-hveid] a[href*='.neoseeker.com/wiki/']:first-of-type:not([role='button']):not([target='_self'])`);
div[data-hveid] a[href*='.fandom.com/'][href*='/wiki/']:first-of-type:not([role='button']):not([target='_self']),
div[data-hveid] a[href*='.wiki.fextralife.com/']:first-of-type:not([role='button']):not([target='_self']),
div[data-hveid] a[href*='.neoseeker.com/wiki/']:first-of-type:not([role='button']):not([target='_self'])`);
filterSearchResults(searchResults, 'google', storage, reorderedHrefs);
}
@ -725,9 +736,9 @@ function startFiltering(searchEngine, storage, mutations = null, observer = null
// Function to filter search results in Brave
function filterBrave() {
let searchResults = Array.from(document.querySelectorAll('div.snippet[data-type="web"] a')).filter(el =>
el.href?.includes('.fandom.com') ||
el.href?.includes('.wiki.fextralife.com') ||
el.href?.includes('.neoseeker.com/wiki/'));
el.href?.includes('.fandom.com') ||
el.href?.includes('.wiki.fextralife.com') ||
el.href?.includes('.neoseeker.com/wiki/'));
filterSearchResults(searchResults, 'brave', storage);
}
@ -737,9 +748,9 @@ function startFiltering(searchEngine, storage, mutations = null, observer = null
// Function to filter search results in Ecosia
function filterEcosia() {
let searchResults = Array.from(document.querySelectorAll('section.mainline .result__title a.result__link')).filter(el =>
el.href?.includes('.fandom.com') ||
el.href?.includes('.wiki.fextralife.com') ||
el.href?.includes('.neoseeker.com/wiki/'));
el.href?.includes('.fandom.com') ||
el.href?.includes('.wiki.fextralife.com') ||
el.href?.includes('.neoseeker.com/wiki/'));
filterSearchResults(searchResults, 'ecosia', storage);
}
@ -758,9 +769,9 @@ function startFiltering(searchEngine, storage, mutations = null, observer = null
// Function to filter search results in Startpage
function filterStartpage() {
let searchResults = Array.from(document.querySelectorAll('a.result-link')).filter(el =>
el.href?.includes('.fandom.com') ||
el.href?.includes('.wiki.fextralife.com') ||
el.href?.includes('.neoseeker.com/wiki/'));
el.href?.includes('.fandom.com') ||
el.href?.includes('.wiki.fextralife.com') ||
el.href?.includes('.neoseeker.com/wiki/'));
filterSearchResults(searchResults, 'startpage', storage);
}
@ -770,9 +781,9 @@ function startFiltering(searchEngine, storage, mutations = null, observer = null
// Function to filter search results in Yandex
function filterYandex() {
let searchResults = Array.from(document.querySelectorAll('li[data-cid] a.link, li[data-cid] a.Link, .serp-item a.link, .serp-item a.Link, .MMOrganicSnippet a, .viewer-snippet a')).filter(el =>
el.href?.includes('.fandom.com') ||
el.href?.includes('.wiki.fextralife.com') ||
el.href?.includes('.neoseeker.com/wiki/'));
el.href?.includes('.fandom.com') ||
el.href?.includes('.wiki.fextralife.com') ||
el.href?.includes('.neoseeker.com/wiki/'));
filterSearchResults(searchResults, 'yandex', storage);
}
@ -814,9 +825,9 @@ function startFiltering(searchEngine, storage, mutations = null, observer = null
// Function to filter search results in Kagi
function filterKagi() {
let searchResults = Array.from(document.querySelectorAll('h3>a, a.__sri-url')).filter(el =>
el.href?.includes('.fandom.com') ||
el.href?.includes('.wiki.fextralife.com') ||
el.href?.includes('.neoseeker.com/wiki/'));
el.href?.includes('.fandom.com') ||
el.href?.includes('.wiki.fextralife.com') ||
el.href?.includes('.neoseeker.com/wiki/'));
filterSearchResults(searchResults, 'kagi', storage);
}
@ -826,17 +837,17 @@ function startFiltering(searchEngine, storage, mutations = null, observer = null
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/'));
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/'));
el.href?.includes('.fandom.com') ||
el.href?.includes('.wiki.fextralife.com') ||
el.href?.includes('.neoseeker.com/wiki/'));
filterSearchResults(searchResults, 'whoogle', storage);
}
@ -861,7 +872,7 @@ function startFiltering(searchEngine, storage, mutations = null, observer = null
// Check if user has enabled filtering for the current search engine
// If so, call startFiltering function to start filtering process
function checkIfEnabled(searchEngine) {
extensionAPI.runtime.sendMessage({action: 'getStorage'}, (storage) => {
extensionAPI.runtime.sendMessage({ action: 'getStorage' }, (storage) => {
searchEngineToggles = storage.searchEngineToggles || {};
if (searchEngineToggles[searchEngine] === 'on' || !searchEngineToggles.hasOwnProperty(searchEngine)) {
startFiltering(searchEngine, storage);