Yandex (by/ya): автозакрытие баннера установки браузера

Разблокирует страницу ya.ru и yandex.by, и фокусирует строку поиска.

// ==UserScript==
// @name         Yandex (by/ya): автозакрытие баннера установки браузера
// @author       sailars
// @version      1.0
// @description  Разблокирует страницу ya.ru и yandex.by, и фокусирует строку поиска.
// @description:en  Closes the Yandex browser install splash on yandex.by/ya.ru and re-enables the page (focuses search).
// @match        https://yandex.by/*
// @match        https://www.yandex.by/*
// @match        https://ya.ru/*
// @name:en      Yandex (by/ya): auto-close browser install splash
// @run-at       document-start
// @grant        none
// @namespace https://greasyfork.org/users/1508445
// ==/UserScript==

(function () {
  'use strict';

  const host = location.hostname;
  const isBY = /\.?yandex\.by$/.test(host) || host.endsWith('.yandex.by');
  const isYA = host === 'ya.ru' || host.endsWith('.ya.ru');

  const NO_TEXTS  = ['Нет, спасибо', 'Нет спасибо', 'No, thanks', 'No thanks'];
  const TITLE_TXT = ['Яндекс Браузер', 'поиск Яндекса', 'Yandex Browser', 'Яндекс'];

  let done = false;

  const norm = s => (s || '').replace(/\s+/g, ' ').trim();
  const hasAny = (text, arr) => arr.some(t => text.includes(t));

  function click(el) {
    try {
      el.dispatchEvent(new MouseEvent('pointerdown', { bubbles: true }));
      el.dispatchEvent(new MouseEvent('mousedown',   { bubbles: true }));
      el.dispatchEvent(new MouseEvent('pointerup',   { bubbles: true }));
      el.dispatchEvent(new MouseEvent('mouseup',     { bubbles: true }));
      el.dispatchEvent(new MouseEvent('click',       { bubbles: true }));
    } catch(_) { try { el.click(); } catch(__) {} }
  }

  function unlock() {
    const b = document.body;
    if (!b) return;

    // снять возможные блокировки
    b.style.overflow = '';
    b.style.position = '';
    b.style.pointerEvents = '';
    b.classList.remove('no-scroll','Body_lock','_locked','lock','is-locked','modal-open');

    // убрать фокус-ловушки
    document.querySelectorAll('[data-focus-trap-guard]').forEach(n => n.remove());

    // снести большие фиксированные оверлеи, если остались
    document.querySelectorAll('.Modal-Overlay, .Modal-Wrapper').forEach(n => n.remove());
    Array.from(document.querySelectorAll('div,section,aside')).forEach(el => {
      const cs = getComputedStyle(el);
      const big = el.offsetWidth >= innerWidth * 0.9 && el.offsetHeight >= innerHeight * 0.9;
      if (cs.position === 'fixed' && big && parseInt(cs.zIndex || '0', 10) > 1000) {
        if (hasAny(norm(el.textContent || ''), TITLE_TXT)) el.remove();
      }
    });

    // фокус в поисковое поле
    const input = document.querySelector('input[name="text"], input[type="search"]');
    if (input) {
      input.removeAttribute('disabled');
      input.focus();
    }
  }

  // ——— yandex.by: нажать крестик ———
  function closeBY(root=document) {
    // точные селекторы под присланную разметку
    let btn =
      root.querySelector('button.Distribution-ButtonClose') ||
      root.querySelector('button.SplashscreenDefault-DeclineButton') ||
      root.querySelector('button[title*="Нет"][title*="спасибо" i]') ||
      root.querySelector('[class*="ButtonClose"][role="button"]');

    if (btn) {
      click(btn);
      return true;
    }
    return false;
  }

  // ——— ya.ru: нажать "Нет, спасибо" ———
  function closeYA(root=document) {
    const btns = Array.from(root.querySelectorAll('button, a, [role="button"]'));
    const noBtn = btns.find(b => NO_TEXTS.includes(norm(b.textContent || b.getAttribute('aria-label') || b.title || '')));
    if (noBtn) { click(noBtn); return true; }
    return false;
  }

  // универсальный демонтаж модалки, если клик не сработал
  function nukeModal(root=document) {
    const dlg = root.querySelector('.Modal-Content[role="dialog"], [role="dialog"], .Distribution-SplashScreenModalContent');
    if (dlg && hasAny(norm(dlg.textContent || ''), TITLE_TXT)) {
      dlg.closest('.Modal-Wrapper')?.remove();
      dlg.closest('.Modal-Overlay')?.remove();
      dlg.remove();
      return true;
    }
    // запасной вариант: просто убрать известные контейнеры
    let removed = false;
    document.querySelectorAll('.Modal-Overlay, .Modal-Wrapper').forEach(n => { n.remove(); removed = true; });
    return removed;
  }

  function tryHandle(root=document) {
    if (done) return false;
    let acted = false;

    if (isBY) {
      acted = closeBY(root) || nukeModal(root);
    } else if (isYA) {
      acted = closeYA(root) || nukeModal(root);
    } else {
      acted = closeBY(root) || closeYA(root) || nukeModal(root);
    }

    if (acted) {
      done = true;
      unlock();
      return true;
    }
    return false;
  }

  const mo = new MutationObserver(muts => {
    for (const m of muts) {
      for (const n of m.addedNodes || []) {
        if (n.nodeType === 1 && tryHandle(n)) return;
      }
    }
    tryHandle(document);
  });

  function start() {
    tryHandle(document);
    mo.observe(document.documentElement || document, { childList: true, subtree: true });

    // несколько ретраев — баннер может прилететь поздно
    let attempts = 0;
    const id = setInterval(() => {
      attempts++;
      if (done || attempts > 40) clearInterval(id);
      else tryHandle(document);
    }, 300);
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', start, { once: true });
  } else {
    start();
  }
})();