chrono / BlockYaAds

// ==UserScript==
// @name         BlockYaAds
// @description  Block all ads on yandex.ru
// @version      0.6.9
// @author       Chrono
// @license      MIT
// @match        https://mail.yandex.ru/*
// @match        https://*yandex.ru/*
// @match        https://*yandex.eu/*
// @match        https://ya.ru/*
// @run-at       document-start
// @grant        none
// ==/UserScript==

(function() {
    var ignoredClasses = [
        "_",
        "Aside",
        "Attach",
        "Avatar",
        "Badge",
        "Button2-Icon",
        "ButtonGroup",
        "ButtonGroup_gap_xl",
        "ButtonWithIcon-m__iconWrap",
        "cke",
        "CollapsibleToolbar__overflow",
        "CollapsibleToolbar__visible",
        "CollapsibleToolbar__zeroWidth",
        "CollectorIcon-m__root",
        "Collectors-m__root",
        "compose",
        "Compose",
        "Contact",
        "ContentHeader-m__header",
        "Control",
        "cut",
        "Cut",
        "dialog",
        "MidgetsPanel",
        "Separator",
        "Expander",
        "Filter-m__root",
        "Filters",
        "Folder",
        "Footer-m__footerContainer",
        "FooterItem-m__item",
        "Header",
        "HeaderSearch-m__root",
        "Image-Container",
        "Labels-m__root",
        "LabelsExpander-m__root",
        "Layout-m__root",
        "LeftColumn",
        "MainButtons",
        "MainContent",
        "Message",
        "Messenger",
        "PageLayout",
        "Popup",
        "PSHeader",
        "Prev",
        "Quote",
        "Recipient",
        "Related",
        "Right",
        "Root",
        "Section",
        "Sender",
        "Spin",
        "StatuslineRoot-m__root",
        "Svg360_root",
        "Tag-m__icon",
        "Tag-m__root",
        "Text",
        "Title",
        "Theme",
        "ToggleSelectedAction__root",
        "tooltip",
        "Tooltip",
        "User",
        "With",
        "WrappingList-m__extra",
        "WrappingList-m__item",
        "WrappingList-m__root",
        "WrappingList-m__tail",
        "WrappingList-m__withExtra",
        "WrappingList-m__withTail",
        "b-",
        "common-m__withGhostButton",
        "g-hidden",
        "is-collapsed",
        "js",
        "mail",
        "message-list-banner-portal",
        "messages-date-pager-float-box",
        "mid",
        "mr_css",
        "new__root",
        "ns-action",
        "ns-view-abook",
        "ns-view-advanced",
        "ns-view-app",
        "ns-view-attach",
        "ns-view-browser",
        "ns-view-collect",
        "ns-view-compact-header",
        "ns-view-compose",
        "ns-view-contact-actions",
        "ns-view-container",
        "ns-view-copy",
        "ns-view-fake",
        "ns-view-fill",
        "ns-view-folders",
        "ns-view-footer",
        "ns-view-header",
        "ns-view-id",
        "ns-view-infoline",
        "ns-view-labels",
        "ns-view-layout",
        "ns-view-left",
        "ns-view-loading",
        "ns-view-mail",
        "ns-view-main",
        "ns-view-message",
        "ns-view-midgets",
        "ns-view-mops",
        "ns-view-notifi",
        "ns-view-opt-in-subs-box",
        "ns-view-opt-in-subs-view",
        "ns-view-page",
        "ns-view-ps-header",
        "ns-view-quick",
        "ns-view-react-left-column",
        "ns-view-react-main-buttons",
        "ns-view-react-promo-container",
        "ns-view-react-quick-reply",
        "ns-view-right",
        "ns-view-service",
        "ns-view-setup-abook",
        "ns-view-setup-left",
        "ns-view-sidebar-with-widgets",
        "ns-view-sidebar-with-widgets-box",
        "ns-view-skin",
        "ns-view-system-alert-container",
        "ns-view-tech",
        "ns-view-themes",
        "ns-view-themes-messages-list-control",
        "ns-view-themes-selector-box",
        "ns-view-themes-skin-rotater-box",
        "ns-view-thread",
        "ns-view-timeline",
        "ns-view-timeline-box",
        "ns-view-timeline-collapsed",
        "ns-view-timeline-loader-box",
        "ns-view-timeline-toggle",
        "ns-view-timeline-wrap",
        "ns-view-tip",
        "ns-view-toolbar",
        "ns-view-toolbar-box",
        "ns-view-view",
        "qa",
        "react",
        "ScreenLayer",
        "search",
        "styles__buttons",
        "styles__item",
        "styles__messages",
        "styles__root",
        "styles__wrapper",
        "theme-overrides-m__root",
        "ui-dialog",
        "ui-widget",
        "ui-corner",
        "ui-front",
        "_nb-popup",
        "nb-popup",
        "ui-resizable-handle",
        "ui-resizable-s"
    ];


    // for all other subdomains remove all divs but the lists below
    var specificIgnoredClasses = [
        "AdvRsyaCrossPage",
        "App",
        "b-page",
        "Button",
        "Carousel",
        "cbir",
        "Cbir",
        "clearfix",
        "copyright",
        "Content",
        "content",
        "detect",
        "Divider",
        "Extra",
        "extra",
        "fade",
        "favicon",
        "Favicon",
        "Fetch",
        "FewLines",
        "Flex",
        "Footer",
        "footer",
        "header",
        "Header",
        "Host",
        "i-bem",
        "i-react",
        "image_history",
        "ImagesViewer-Content",
        "ImagesViewer-Footer",
        "ImagesViewer-Layout",
        "ImagesViewer-Modal",
        "ImagesViewer-Paranja",
        "ImagesViewer-Rim",
        "ImagesViewer-Sidebar",
        "ImagesViewer-TopSide",
        "ImagesViewer-View",
        "internal",
        "Justifier",
        "main",
        "mini",
        "List",
        "Menu",
        "Mini",
        "Missing",
        "MMFavicon",
        "MMImage",
        "MMOrganic",
        "MMViewer",
        "Modal",
        "Organic",
        "organic",
        "overlay",
        "navigation",
        "Native",
        "Open",
        "Page",
        "page",
        "Pager",
        "paranja",
        "Path",
        "path",
        "presearch",
        "Prevention",
        "Related",
        "Request",
        "Root",
        "Search",
        "SendTimeMark",
        "Serp",
        "serp",
        "Site",
        "site",
        "Skeleton",
        "smart",
        "Spyable",
        "Svg",
        "Swipe",
        "Spin",
        "spin",
        "text",
        "Typo",
        "unread",
        "User",
        "Video",
        "Viewer",
        "z-index-group",
        // ya.ru classes
        "headline",
        "weather",
        "usermenu-portal",
        "body",
        "search3",
        "link-bro",
        "i-mini-bem",
        "simple-popup"
    ];

    var miscellaneousClasses = [
        "Container",
        "CardDivider",
        "Wrapper",
        "Tags"
    ];

    // default element visibility
    var elementsVisibility = {};

    // paths to exclude filtering on
    var excludedPaths = [
        "/all",
        "/maps",
        "/products",
        "/search",
        "/showcaptcha",
        "/video/preview"
    ];

    var numElements = 0;
    var scriptStarted = false;
    var lastRunMillis = 0;

    function removeElements(selector, visibility, el) {
        var number = 0;

        if (visibility != "none") {
            elementsVisibility[selector] = visibility;
        }

        el.style.display = visibility;
    }

    function hideInnerElements(container) {
        container.querySelectorAll("*").forEach(function(innerEl) {
            innerEl.style.display = "none";
        });
    }

    var mutationConfig = {
        attributes: true,
        attributeOldValue: true,
        characterData: true,
        characterDataOldValue: true,
        childList: true,
        subtree: true
    };

    var observer;

    function containsAnyIgnoredClass(div, ignoredClasses) {
        const divClasses = Array.from(div.classList);
        return divClasses.some(divClass =>
            ignoredClasses.some(ignoredClass => divClass.indexOf(ignoredClass) !== -1)
        );
    }

    function start() {
        var isElementFound = false;
        var divs = document.querySelectorAll('div');
        var currentUrl = window.location;
        var baseUrl = currentUrl.host;
        if (Date.now() - lastRunMillis < 300) {
            lastRunMillis = Date.now();
            return;
        }
        //console.log("start");

        // skip filtering if not implemented
        var exit = false;
        excludedPaths.forEach(function(path) {
            if (window.location.pathname.startsWith(path)) {
                console.log(`filtering for ${path} is not implemented`);
                exit = true;
                return;
            }
        });
        if (exit) return;

        var rootNodes = document.getElementsByClassName("Root");
        for (var i = 0; i < rootNodes.length; i++) {
            var rootNode = rootNodes[i];
            if (rootNode) {
                if (rootNode.firstChild != undefined) {
                    if (rootNode.firstChild.classList != []) {
                        var ignoredClass = rootNode.firstChild.classList[0];
                        //console.log(ignoredClass + " is the root, skipping");
                        specificIgnoredClasses.push(ignoredClass);
                    }
                }
            }
        }

        // Specific case for "docviewer.yandex.ru"
        if (baseUrl === "docviewer.yandex.ru") {
            document.querySelectorAll("*").forEach(function(el) {
                el.classList.forEach(function(className) {
                    if (className === "js-doc-html") {
                        var innerElements = el.querySelectorAll("*");
                        innerElements.forEach(function loop(el1) {
                            if (loop.stop) {
                                return;
                            }
                            if ((el1.tagName === "DIV") && el1.classList.length === 3) {
                                removeElements("." + el1.classList[0], "none", el);
                                loop.stop = true;
                            }
                        });
                    }
                });

                if (el.tagName.length > 10) {
                    el.style.display = "none";
                }
            });
        }

        // Match classes for "mail.yandex.ru"
        if (baseUrl === "mail.yandex.ru") {
            divs.forEach(div => {
                // Check if div has any classes
                if (div.classList.length > 0) { // Only process divs with at least one class
                    if (!containsAnyIgnoredClass(div, ignoredClasses)) {
                        div.remove();
                        console.log(div)
                    }
                }
            });
        }

        for (i = 0; i < divs.length; i++) {
            var div = divs[i];

            if (window.location.pathname === "/images/search") {
                var style = div.getAttribute("style");
                if (style === "margin-bottom: 4px;" || div.offsetHeight === 72) {
                    div.classList.forEach(function(className) {
                        removeElements("." + className, "none", div);
                    });
                }
            }

            div.classList.forEach(function(className) {
                // Match classes for anything but "mail.yandex.ru"
                if (baseUrl === "mail.yandex.ru") {
                    return;
                }

                //
                if (currentUrl == "https://ya.ru") {
                    return;
                }
                // Classes to ignore
                isElementFound = true;

                // Hide specific class
                if (className === "ComposeDoneDirect") {
                    removeElements("." + className, 'none', div);
                }

                for (var j = 0; j < miscellaneousClasses.length; j++) {
                    if (className.indexOf(miscellaneousClasses[j]) !== -1) {
                        isElementFound = false;
                        removeElements("." + className, elementsVisibility[className], div);
                        return;
                    }
                }

                if (className === "ImagesViewer") {
                    isElementFound = false;
                    removeElements("." + className, elementsVisibility[className], div);
                    return;
                }

                for (j = 0; j < specificIgnoredClasses.length; j++) {
                    if (className.startsWith(specificIgnoredClasses[j])) {
                        isElementFound = false;
                    }
                }

                if (className == "ImagesViewer-LayoutSideblock") {
                    console.log("ImagesViewer-LayoutSideblock");
                    if (div.firstChild.classList != undefined) {
                        div.firstChild.classList.forEach(function(imagesViewerClassName) {
                            if (!specificIgnoredClasses.includes(imagesViewerClassName)) {
                                console.log("found LayoutSideblock first child div");
                                specificIgnoredClasses.push(imagesViewerClassName);
                            }
                        });
                    }
                }
                if (isElementFound) {
                    console.log("Removed element with class:", className);
                    numElements = removeElements("." + className, "none", div);
                }
            });
            if (baseUrl === "mail.yandex.ru" && numElements != 0) {
                //console.log("disconnecting observer");
                //observer.disconnect();
            }
        }
    }

    observer = new MutationObserver(start);

    function deferredStart() {
        setTimeout(function() {
            if (document.body instanceof Node && !scriptStarted) {
                lastRunMillis = Date.now();
                observer.observe(document.body, mutationConfig);
                scriptStarted = true;
            }

            deferredStart();
        }, 200);
    }

    deferredStart();
    console.log("BlockYaAds script is running.");
})();