Facebook - Hides Suggested and Sponsored Posts

Hides Suggested For You, sponsored posts on the main feed & those silly sponsored ads in Marketplace

// ==UserScript==
// @name         Facebook - Hides Suggested and Sponsored Posts
// @namespace    http://tampermonkey.net/
// @version      0.6.1
// @description  Hides Suggested For You, sponsored posts on the main feed & those silly sponsored ads in Marketplace
// @author       ArthurG
// @match        https://www.facebook.com/*
// @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
// @grant        none
// ==/UserScript==

(function() {
    'use strict';
    const pollTimeout = 3000;
    const debounceTimeout = 2000;
    const debugHighlightAds = false;
    const obfuscationTextSizeLimit = 45;

    const debouncedFindAndHide = debounce(findAndHide, debounceTimeout, false);

    function textNodesUnder(el){
        let n;
        const a = [];
        const walk = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, null, false);

        // It's beem a 'while' since I've had to use you.
        while(n = walk.nextNode()) {
            a.push(n);
        }

        return a;
    }

    /**
     * Checks for the word in a set of random characters. Doesn't allow repeat of characters.
     * Taken from https://stackoverflow.com/questions/58323445/how-can-i-check-a-word-is-made-of-letters-in-different-array
     */
    function isWordValid(attemptedWord, validLetters) {
        const validLettersSplitted = validLetters.split("");
        const attemptedWordSplitted = attemptedWord.split("");
        return attemptedWordSplitted.every(attemptedLetter => {
            const letterIndex = validLettersSplitted.indexOf(attemptedLetter);
            if(letterIndex > -1){
                validLettersSplitted.splice(letterIndex, 1);
                return true;
            } else {
                return false
            }
        });
    }

    function isWorthScrutinizing(el) {
        return $(el).text().length < obfuscationTextSizeLimit;
    }

    function extractValidTextFromDom(el) {
        let validText = '';
        const textNodes = textNodesUnder(el);

        textNodes.forEach(n => {
            const style = getComputedStyle(n.parentElement);
            if(style.position !== 'absolute' && style.display !== 'none') {
                validText += $(n).text();
            }
        })
        return validText;
    }

    function cleanupObsucatingText(s) {
        if(s.length < obfuscationTextSizeLimit) {
            return s.replace(/[^Sponsored]+/g, '').replace(/(.)\1+/g, '$1');
        }
        return s;
    }

    function elIsRealSponsored(el) {
        if(isWorthScrutinizing(el)) {
            const validTextFromDom = extractValidTextFromDom(el);
            const text = cleanupObsucatingText(validTextFromDom);

            if(text === 'Sponsored' || isWordValid('Sponsored', text)) {
                return true;
            }
        } else {
            return false;
        }
    }

    function debounce(func, wait, immediate) {
        var timeout;
        return function() {
            var context = this, args = arguments;
            var later = function() {
                timeout = null;
                if (!immediate) func.apply(context, args);
            };
            var callNow = immediate && !timeout;
            clearTimeout(timeout);
            timeout = window.__fbNativeSetTimeout(later, wait);
            if (callNow) func.apply(context, args);
        };
    };


    function findAndHide(skipPoll) {
        // Suggested For You
        $( "span:contains('Suggested for you')" ).closest('[data-pagelet*="FeedUnit"]').hide();

        //Sponsored Ads in the Feed
        $('a[href*="/ads/"]').closest('[data-pagelet*="FeedUnit"]').hide();
        $('[data-pagelet*="FeedUnit"] a[href="#"]').each((index, el) => {
            if(elIsRealSponsored(el)) {
                if(debugHighlightAds) {
                    $(el).closest('[data-pagelet*="FeedUnit"]').get(0).style.border = "thick solid #FF0000";
                } else {
                    $(el).closest('[data-pagelet*="FeedUnit"]').hide();
                }
            }
        });

        $('[data-pagelet*="FeedUnit"] [aria-label="Shared with Public"]').parent().parent().parent().parent().each((index, el) => {
            if(elIsRealSponsored(el)) {
                if(debugHighlightAds) {
                    $(el).closest('[data-pagelet*="FeedUnit"]').get(0).style.border = "thick solid #00FF00";
                } else {
                    $(el).closest('[data-pagelet*="FeedUnit"]').hide();
                }
            }
        });

        $('[data-pagelet*="FeedUnit"] [role="button"]').each((index, el) => {
            const elText = $(el).text();
            if(elText.includes('Sponsored')) {
                if(debugHighlightAds) {
                    $(el).closest('[data-pagelet*="FeedUnit"]').get(0).style.border = "thick solid #0000FF";
                } else {
                    $(el).closest('[data-pagelet*="FeedUnit"]').hide();
                }
            }
        });

        $('[data-pagelet*="RightRail"] h3').each((i, j) => {
            if(isWordValid('Sponsored', $(j).text())) {
                $(j).parent().parent().parent().parent().parent().parent().parent().hide();
            }
          }
        );

        //Sponsored Ads in Marketplace
        $('a[href*="/ads/"]').closest('span > div > a > div > div > div').parent().parent().parent().parent().parent().parent().hide();

        //Hide Sponsored Ads Header in Marketplace
        $(`a[href*="/ads/about"] span:contains('Sponsored')`).parent().parent().hide();

        console.log('Found and hid!');

        if(!skipPoll) {
            window.__fbNativeSetTimeout(function() {
              findAndHide();
           }, pollTimeout);
        }
    }

    $(window).scroll(debounce(() => { findAndHide(true); }, debounceTimeout, true));

    //Kick off the polling
    findAndHide();
})();