const currentURL = new URL(document.location); let hiddenWikisRevealed = {}; function base64Decode(text) { text = text.replace(/\s+/g, '').replace(/\-/g, '+').replace(/\_/g, '/'); return decodeURIComponent(Array.prototype.map.call(atob(text), function (c) { return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2); }).join('')); } // Function to create an observer to watch for mutations on search pages // This is used for search engines that paginate via JavaScript, // or overwrite their results and remove IWB's elements function addDOMChangeObserver(callback, searchEngine, storage) { const config = { attributes: false, childList: true, subtree: true } const wrappedCallback = (mutations, observer) => { callback(searchEngine, storage, mutations, observer); }; const domObserver = new MutationObserver(wrappedCallback); domObserver.observe(document.body, config); } function insertCSS() { // Output CSS styleString = ` .iwb-notice { display: block !important; margin: .5em .5em 1em .5em !important; padding: .5em .5em .5em 1em !important; border-left: 3px solid #FFCC33 !important; font-size: 14px !important; color: white !important; mix-blend-mode: difference !important; } .iwb-notice a { text-decoration: underline !important; color: white !important; mix-blend-mode: difference !important; } .iwb-notice button { cursor: pointer !important; display: inline-block !important; padding: 2px 8px !important; margin: 8px 8px 0 0 !important; background-color: transparent !important; border: 1px solid !important; border-radius: 5px !important; font-size: 12px !important; color: white !important; mix-blend-mode: difference !important; text-align: left !important; } .iwb-hide { display: none !important; } .iwb-show { display: block !important; } .iwb-notice button:hover { outline: 1px solid !important; } .iwb-new-link-container { display: inline-block; font-size: 12px !important; text-decoration: none; padding-left: 5px; position: relative; } .iwb-new-link { display: inline-block; text-decoration: none; position: relative; } .iwb-new-link:hover { text-decoration: none; z-index: 9999999; } .iwb-new-link button { cursor: pointer; color: white; background: #005799; border: 0px solid #fff; border-radius: 10px; padding: 5px 10px; margin: .5em 0; font-size: 1.2em; width: fit-content; } .iwb-new-link button:hover { background: #00467a; } .iwb-new-link button * { vertical-align: middle; } .iwb-new-link div:first-of-type { display: inline-block; background: white; border-radius: 16px; margin-right: 10px; width: fit-content; height: fit-content; line-height: 12px; padding: 5px; } .iwb-new-link img { width: 12px; } .iwb-disavow > *:not([class^="iwb-"]):not(.scs_child_rpr) { opacity: 60%; pointer-events: none; cursor: default; } .iwb-disavow > a:not([class^="iwb-"]):not(.scs_child_rpr), .iwb-disavow > *:not([class^="iwb-"]):not(.scs_child_rpr) a, .iwb-disavow > *:not([class^="iwb-"]):not(.scs_child_rpr) a * { text-decoration: line-through !important; } .iwb-result-controls { display: inline-block; margin: .5em; font-size: 12px; color: white !important; mix-blend-mode: difference !important; } .iwb-result-controls > div { display: inline-block; cursor: pointer; text-decoration: underline; text-decoration-style: dotted; text-decoration-thickness: 1px; padding: 0 1em; } .iwb-notice .iwb-result-controls { margin: 8px 0 0 0; } ` style = document.createElement('style'); style.classList.add('iwb-styles'); style.textContent = styleString; document.head.append(style); } // Function to convert strings to consistent IDs // Used to convert wiki names to element IDs function stringToId(string) { return string.replaceAll(' ', '-').replaceAll("'", '').replace(/\W/g, '').toLowerCase(); } // Function to escape string to use in regex function escapeRegex(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); } function removeSubstringIfAtEnd(str, sub) { if (sub && str.endsWith(sub)) { return str.slice(0, -sub.length); } return str; } function replaceSearchResults(searchResultContainer, site, link) { let originArticle = commonFunctionGetOriginArticle(link, site); let destinationArticle = commonFunctionGetDestinationArticle(site, originArticle); let newURL = commonFunctionGetNewURL(link, site); if (searchResultContainer && !searchResultContainer.querySelector('.iwb-new-link')) { if (!searchResultContainer.classList.contains('iwb-detected')) { searchResultContainer.classList.add('iwb-detected'); searchResultContainer.classList.add('iwb-disavow'); } // Using aside to avoid conflicts with website CSS and listeners: let indieContainer = document.createElement('aside'); indieContainer.classList.add('iwb-new-link-container'); let indieResultLink = document.createElement('a'); indieResultLink.href = newURL; indieResultLink.classList.add('iwb-new-link'); let indieResultButton = document.createElement('button'); let indieResultFaviconContainer = document.createElement('div'); let indieResultFavicon = document.createElement('img'); indieResultFavicon.alt = ''; indieResultFavicon.width = '12'; indieResultFavicon.height = '12'; indieResultFavicon.src = extensionAPI.runtime.getURL('favicons/' + site.language.toLowerCase() + '/' + site.destination_icon); indieResultFaviconContainer.append(indieResultFavicon); let indieResultText = document.createElement('span'); if (originArticle && originArticle !== site['origin_main_page']) { let destinationArticleTitle = removeSubstringIfAtEnd(destinationArticle, site['destination_content_suffix']).replace(site['destination_content_prefix'], '').replaceAll('_', ' '); // If a Fextralife wiki, replace plus signs with spaces // When there are multiple plus signs together, this regex will only replace only the first if (link.includes('.wiki.fextralife.com')) { destinationArticleTitle = destinationArticleTitle.replace(/(? { e.target.closest('.iwb-disavow').classList.remove('iwb-disavow'); e.target.classList.add('iwb-hide'); }); indieContainer.appendChild(indieResultLink); indieContainer.appendChild(resultControls); searchResultContainer.prepend(indieContainer); return 1; } return 0; } function hideSearchResults(searchResultContainer, searchEngine, site, showBanner = 'on') { // Insert search result removal notice, if enabled and not already injected let elementId = stringToId(site.language + '-' + site.origin); if (showBanner === 'on' && !document.getElementById('iwb-notice-' + elementId)) { hiddenWikisRevealed[elementId] = false; // Using aside to avoid conflicts with website CSS and listeners: let searchRemovalNotice = document.createElement('aside'); searchRemovalNotice.id = 'iwb-notice-' + elementId; searchRemovalNotice.classList.add('iwb-notice'); let searchRemovalNoticeLink = document.createElement('a'); searchRemovalNoticeLink.href = 'https://' + site.destination_base_url; searchRemovalNoticeLink.textContent = site.destination; searchRemovalNoticePretext = document.createTextNode('Indie Wiki Buddy has filtered out results from ' + site.origin + (site.language !== 'EN' ? ' (' + site.language + ')' : '') + '. Look for results from '); searchRemovalNoticePosttext = document.createTextNode(' instead.'); searchRemovalNotice.appendChild(searchRemovalNoticePretext); searchRemovalNotice.appendChild(searchRemovalNoticeLink); searchRemovalNotice.appendChild(searchRemovalNoticePosttext); // Output container for result controls: let resultControls = document.createElement('div'); resultControls.classList.add('iwb-result-controls'); // Output link to show hidden results: let showResultsButton = document.createElement('div'); showResultsButton.setAttribute('data-group', 'iwb-search-result-' + elementId); showResultsButton.innerText = 'Show filtered results'; resultControls.appendChild(showResultsButton) showResultsButton.onclick = function (e) { if (e.target.textContent.includes('Show')) { e.target.textContent = 'Re-hide filtered results'; hiddenWikisRevealed[elementId] = true; const selector = e.currentTarget.dataset.group; document.querySelectorAll('.' + selector).forEach(el => { el.classList.add('iwb-show'); }) } else { e.target.textContent = 'Show filtered results'; hiddenWikisRevealed[elementId] = false; const selector = e.currentTarget.dataset.group; document.querySelectorAll('.' + selector).forEach(el => { el.classList.remove('iwb-show'); }) } } searchRemovalNotice.appendChild(resultControls); switch (searchEngine) { case 'google': if (document.querySelector('#search')) { document.querySelector('#search').prepend(searchRemovalNotice); } else if (document.querySelector('#topstuff')) { document.querySelector('#topstuff').prepend(searchRemovalNotice); } else if (document.querySelector('#main')) { var el = document.querySelector('#main'); if (el.querySelector('#main > div[data-hveid]')) { el.insertBefore(searchRemovalNotice, el.querySelector('div[data-hveid]')); } else { el.insertBefore(searchRemovalNotice, el.querySelector('div div[data-hveid]').parentElement); } }; break; case 'bing': var li = document.createElement('li'); li.appendChild(searchRemovalNotice); document.querySelector('#b_results').prepend(li); break; case 'duckduckgo': if (document.getElementById('web_content_wrapper')) { var li = document.createElement('li'); li.appendChild(searchRemovalNotice); document.querySelector('#web_content_wrapper ol').prepend(li); } else { document.getElementById('links').prepend(searchRemovalNotice); } break; case 'brave': document.querySelector('body').prepend(searchRemovalNotice); break; case 'ecosia': document.querySelector('body').prepend(searchRemovalNotice); break; case 'qwant': document.querySelector('div[data-testid=sectionWeb]').prepend(searchRemovalNotice); break; case 'startpage': document.querySelector('#main').prepend(searchRemovalNotice); break; case 'yandex': var searchResultsContainer = document.querySelector('#search-result') || document.querySelector('.main__content .content'); searchResultsContainer?.prepend(searchRemovalNotice); break; case 'yahoo': if (document.querySelector('#web > ol')) { var li = document.createElement('li'); li.appendChild(searchRemovalNotice); document.querySelector('#web > ol').prepend(li); } else { document.querySelector('#main-algo').prepend(searchRemovalNotice); } break; 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: } } if (!Array.from(searchResultContainer.classList).includes('iwb-hide')) { let elementId = stringToId(site.language + '-' + site.origin); searchResultContainer.classList.add('iwb-search-result-' + elementId); searchResultContainer.classList.add('iwb-hide'); searchResultContainer.classList.add('iwb-detected'); if (hiddenWikisRevealed[elementId]) { searchResultContainer.classList.add('iwb-show'); } return 1; } return 0; } function getDistance(child, parent) { let distance = 0; while (parent !== child) { child = child.parentNode; distance++; if (!child) break; } return distance; } function findClosestElement(target, elements) { let closestElement = null; let closestDistance = Infinity; elements.forEach(element => { const distance = element?.contains(target) ? getDistance(target, element) : Infinity; if (distance < closestDistance) { closestDistance = distance; closestElement = element; } }); return closestElement; } function getSearchContainer(searchEngine, searchResult) { let searchResultContainer = null; switch (searchEngine) { case 'google': const closestJsController = searchResult.closest('div[jscontroller]'); const closestDataDiv = searchResult.closest('div[data-hveid].g') || searchResult.closest('div[data-hveid]'); searchResultContainer = findClosestElement(searchResult, [closestJsController, closestDataDiv]); break; case 'bing': searchResultContainer = searchResult.closest('li.b_algo'); break; case 'duckduckgo': searchResultContainer = searchResult.closest('li[data-layout], div.web-result'); break; case 'brave': searchResultContainer = searchResult.closest('div.snippet'); break; case 'ecosia': searchResultContainer = searchResult.closest('div.mainline__result-wrapper article div.result__body'); break; case 'qwant': if (searchResult.closest('div[data-testid=webResult]')) { cssQuery = 'div[data-testid=webResult]'; searchResultContainer = searchResult.closest(cssQuery).parentElement; } break; case 'startpage': searchResultContainer = searchResult.closest('div.result, div.w-gl__result'); break; case 'yandex': searchResultContainer = searchResult.closest('li[data-cid], .serp-item, .MMOrganicSnippet, .viewer-snippet'); break; case 'yahoo': searchResultContainer = searchResult.closest('#web > ol > li div.itm .exp, #web > ol > li div.algo, #web > ol > li, section.algo'); break; 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: } 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 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)}$` ) ))) { countFiltered += hideSearchResults(searchResultContainer, searchEngine, matchingSite, 'off'); console.debug(`Indie Wiki Buddy has hidden a result matching ${searchResult.href} because we re-ordered an indie wiki result with a matching article`); } else if (searchFilterSetting !== 'disabled') { if (searchFilterSetting === 'hide') { // Else, if the user has the preference set to hide search results, hide it indiscriminately countFiltered += hideSearchResults(searchResultContainer, searchEngine, matchingSite, storage['hiddenResultsBanner']); } else { countFiltered += replaceSearchResults(searchResultContainer, matchingSite, searchResultLink); } } } return countFiltered; } async function reorderDestinationSearchResult(firstNonIndieResult, searchResult) { // Find containing element for non-indie result const nonIndieSearchResultContainer = getSearchContainer('google', firstNonIndieResult); // Find containing element for the indie search result const indieSearchResultContainer = getSearchContainer('google', searchResult); if (!indieSearchResultContainer || indieSearchResultContainer.classList.contains('iwb-reordered')) { return; } indieSearchResultContainer.classList.add('iwb-reordered'); // Prepend search results to first Fandom/Fextra/Neoseeker result nonIndieSearchResultContainer.parentNode.prepend(indieSearchResultContainer); } async function reorderSearchResults(searchResults, searchEngine, storage) { const reorderResultsSetting = storage.reorderResults || 'on'; if (reorderResultsSetting === 'off') return []; let reorderedHrefs = []; if (!document.body.classList.contains('iwb-reorder')) { document.body.classList.add('iwb-reorder'); // Only support Google for now if (searchEngine !== 'google') return; // Get the first element in the results container 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]'); // 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 = []; for (const searchResult of searchResults) { try { if (searchResult.closest('.iwb-detected')) { continue; } const searchResultLink = searchResult.href || ''; // Handle re-ordering of results to move destination results up the page let matchingDest = await commonFunctionFindMatchingSite(searchResultLink, crossLanguageSetting, true); if (matchingDest) { if (resultsFirstChild.contains(searchResult)) { // If this search result is inside the first child of the results container (aka, it's the first result), // and there is a matchingDest at this point, then an indie wiki is #1 on the search results page. // Therefore, we should abandon the search re-ordering. console.debug('Indie Wiki Buddy is not re-ordering results, as an indie wiki is already the first result.'); break; } let searchEngineSettings = await commonFunctionDecompressJSON(storage.searchEngineSettings || {}); if (searchEngineSettings[matchingDest.id] !== 'disabled') { resultsToSort.push(searchResult); } } } catch (e) { console.log('Indie Wiki Buddy failed to properly re-order search results with error: ' + e); } } // Reverse order of resultsToSort, // to restore top-down order. resultsToSort = resultsToSort.reverse(); for (const searchResult of resultsToSort) { try { 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); } } } return reorderedHrefs; } async function filterSearchResults(searchResults, searchEngine, storage, reorderedHrefs = []) { let countFiltered = 0; for (const searchResult of searchResults) { try { // Check that result isn't within another result if (!searchResult.closest('.iwb-detected') || !searchResult.closest('.iwb-detected')?.querySelector('.iwb-new-link')) { let searchResultLink = searchResult.href || ''; if (!searchResultLink) { continue; } if (searchEngine === 'google') { // Break if image result: if (searchResultLink.includes('imgurl=')) { continue; } // Skip if result doesn't include specific tags/attributes // This helps avoid capturing unintended image results if (!( searchResult.querySelector('h1') || searchResult.querySelector('h3') || searchResult.querySelector('cite') || searchResult.querySelector("div[role='link']"))) { searchResult.classList.add('iwb-detected'); continue; } } let crossLanguageSetting = storage.crossLanguage || 'off'; // Handle source -> destination filtering let matchingSource = await commonFunctionFindMatchingSite(searchResultLink, crossLanguageSetting); if (matchingSource) { countFiltered = await filterSearchResult(matchingSource, searchResult, searchEngine, countFiltered, storage, reorderedHrefs); } } } catch (e) { console.log('Indie Wiki Buddy failed to properly parse search results with error: ' + e); } }; // Add location observer to check for additional mutations addDOMChangeObserver(startFiltering, searchEngine, storage); // If any results were filtered, update search filter count if (countFiltered > 0) { extensionAPI.storage.sync.set({ 'countSearchFilters': (storage.countSearchFilters ?? 0) + countFiltered }); } } function startFiltering(searchEngine, storage, mutations = null, observer = null) { if (observer) { observer.disconnect(); } // Check if extension is on: if ((storage.power ?? 'on') === 'on') { // Determine which search engine we're on switch (searchEngine) { case 'google': // Function to filter search results in Google function filterGoogle(reorderedHrefs) { let searchResults = document.querySelectorAll(` 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); } async function reorderGoogle() { let searchResults = document.querySelectorAll("div[data-hveid] a:first-of-type:not([role='button']):not([target='_self'])"); // Remove any matches that are not "standard" search results - this could've been done with :has() but limited browser support right now searchResults = Array.from(searchResults).filter((e) => !e.closest('g-section-with-header, div[aria-expanded], div[data-q], div[data-minw], div[data-num-cols], div[data-docid], div[data-lpage]')); return await reorderSearchResults(searchResults, 'google', storage); } reorderGoogle().then((r) => { // Filtering happens after re-ordering, so that we can filter anything that matches what we re-ordered filterGoogle(r); }); break; case 'duckduckgo': // Function to filter search results in DuckDuckGo function filterDuckDuckGo() { let searchResults = document.querySelectorAll('h2>a[href*=".fandom.com"], h2>a[href*=".wiki.fextralife.com"], h2>a[href*=".neoseeker.com/wiki/"]'); filterSearchResults(searchResults, 'duckduckgo', storage); } filterDuckDuckGo(); break; case 'bing': // Function to filter search results in Bing function filterBing() { let searchResultsEncoded = document.querySelectorAll('li.b_algo h2 a, li.b_algo .b_algoheader a'); let searchResults = []; searchResultsEncoded.forEach((searchResult) => { if (searchResult.href) { const encodedLink = new URL(searchResult.href); if (encodedLink.href.includes('https://www.bing.com/ck/')) { try { let decodedLink = base64Decode(encodedLink.searchParams.get('u').replace(/^a1/, '')); if (decodedLink.includes('.fandom.com') || decodedLink.includes('.wiki.fextralife.com') || decodedLink.includes('.neoseeker.com/wiki/')) { searchResult.href = decodedLink; searchResults.push(searchResult); } } catch (e) { console.log('Indie Wiki Buddy failed to parse Bing link with error: ', e); } } else { searchResults.push(searchResult); } } }); filterSearchResults(searchResults, 'bing', storage); } filterBing(); break; case 'brave': // 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/')); filterSearchResults(searchResults, 'brave', storage); } filterBrave(); break; case 'ecosia': // 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/')); filterSearchResults(searchResults, 'ecosia', storage); } filterEcosia(); break; case 'qwant': // Function to filter search results in Qwant function filterQwant() { let searchResults = Array.from(document.querySelectorAll('a[data-testid=serTitle]')).filter(el => el.href.includes('fandom.com') || el.href.includes('fextralife.com')); filterSearchResults(searchResults, 'qwant', storage); } filterQwant(); break; case 'startpage': // 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/')); filterSearchResults(searchResults, 'startpage', storage); } filterStartpage(); break; case 'yandex': // 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/')); filterSearchResults(searchResults, 'yandex', storage); } filterYandex(); break; case 'yahoo': // Function to filter search results in Yahoo function filterYahoo() { let searchResultsEncoded = document.querySelectorAll('#web > ol > li a:not(.thmb), #main-algo section.algo a:not(.thmb)'); let searchResults = []; searchResultsEncoded.forEach((searchResult) => { if (searchResult.href) { if (searchResult.href.includes('https://r.search.yahoo.com/')) { try { // Extract the URL between "RU=" and "/RK=" const embeddedUrlRegex = /RU=([^/]+)\/RK=/; const match = searchResult.href.match(embeddedUrlRegex); const extractedURL = decodeURIComponent(match && match[1]); if (extractedURL.includes('.fandom.com') || extractedURL.includes('.wiki.fextralife.com') || extractedURL.includes('.neoseeker.com/wiki/')) { searchResult.href = extractedURL; searchResults.push(searchResult); } } catch (e) { console.log('Indie Wiki Buddy failed to parse Yahoo link with error: ', e); } } else { searchResults.push(searchResult); } } }); filterSearchResults(searchResults, 'yahoo', storage); } filterYahoo(); break; case 'kagi': // 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/')); filterSearchResults(searchResults, 'kagi', storage); } filterKagi(); break; default: 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]; filter(customSearchEnginePreset); } } } } } // 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) => { searchEngineToggles = storage.searchEngineToggles || {}; if (searchEngineToggles[searchEngine] === 'on' || !searchEngineToggles.hasOwnProperty(searchEngine)) { startFiltering(searchEngine, storage); } }); } // Figure out which search engine we're on if (currentURL.hostname.includes('www.google.')) { checkIfEnabled('google'); } else if (currentURL.hostname.includes('duckduckgo.com') && (currentURL.search.includes('q=') || currentURL.pathname.includes('html'))) { checkIfEnabled('duckduckgo'); } else if (currentURL.hostname.endsWith('.bing.com')) { checkIfEnabled('bing'); } else if (currentURL.hostname.includes('search.brave.com')) { checkIfEnabled('brave'); } else if (currentURL.hostname.includes('ecosia.org')) { checkIfEnabled('ecosia'); } else if (currentURL.hostname.includes('qwant.com')) { checkIfEnabled('qwant'); } else if (currentURL.hostname.includes('startpage.com')) { checkIfEnabled('startpage'); } else if (currentURL.hostname.includes('yandex.') || currentURL.hostname.includes('ya.ru')) { checkIfEnabled('yandex'); } else if (currentURL.hostname.includes('yahoo.com')) { checkIfEnabled('yahoo'); } else if (currentURL.hostname.includes('kagi.com')) { checkIfEnabled('kagi'); }