eze404 / Ocultar compras internacionales en Meli

// ==UserScript==
// @name         Ocultar compras internacionales en Meli
// @namespace    http://github.com/eze404/
// @version      1.1
// @description  Filtro extra para ocultar compras internacionales de los resultados.
// @author       EZE404
// @grant        none
// @match        https://listado.mercadolibre.com.ar/*
// @exclude      https://listado.mercadolibre.com.ar/importados/*
// @license MIT
// @copyright 2025, eze404 (https://openuserjs.org/users/eze404)
// @run-at       document-end
// @updateURL https://openuserjs.org/meta/eze404/Ocultar_compras_internacionales_en_Meli.meta.js
// @downloadURL https://openuserjs.org/install/eze404/Ocultar_compras_internacionales_en_Meli.user.js
// ==/UserScript==
(function () {
    'use strict';

    if (window.mlCbtToggleInitialized) return;
    window.mlCbtToggleInitialized = true;

    const HOST_CLASS = 'ml-hide-international';
    const STORAGE_KEY = 'mlHideInternationalManual';
    const ITEM_SELECTOR = '.ui-search-layout__item';
    const BADGE_SELECTOR = '.poly-component__cbt';

    const host = document.body || document.documentElement;
    let manualHide = true;
    let officialInput = null;
    let officialHandler = null;
    let officialContainer = null;

    const ensureStyles = () => {
        if (document.getElementById('ml-cbt-style')) return;
        const style = document.createElement('style');
        style.id = 'ml-cbt-style';
        style.textContent = `
        .${HOST_CLASS} ${ITEM_SELECTOR}[data-cbt-international="true"] {
          display: none !important;
        }
      `;
        document.head.appendChild(style);
    };

    const flagInternational = node => {
        if (!(node instanceof Element)) return;
        const badge = node.querySelector(BADGE_SELECTOR);
        const isInternational = Boolean(
            badge && badge.textContent.toUpperCase().includes('COMPRA INTERNACIONAL')
        );
        if (isInternational) node.setAttribute('data-cbt-international', 'true');
    };

    const processTree = root => {
        if (!(root instanceof Element)) return;
        if (root.matches(ITEM_SELECTOR)) flagInternational(root);
        root.querySelectorAll(ITEM_SELECTOR).forEach(flagInternational);
    };

    const readManualPref = () => {
        try {
            const stored = localStorage.getItem(STORAGE_KEY);
            return stored === null ? true : stored === '1';
        } catch (_) {
            return true;
        }
    };

    const writeManualPref = value => {
        try { localStorage.setItem(STORAGE_KEY, value ? '1' : '0'); } catch (_) {}
    };

    ensureStyles();
    manualHide = readManualPref();
    processTree(document.body);

    const container = document.createElement('div');
    container.className = 'ui-search-filter-highlighted__container';
    container.dataset.mlInjectedToggle = 'true';
    container.innerHTML = `
      <label class="andes-switch ui-search-filter-highlighted__switch andes-switch--label-left" data-ml-toggle-label="true">
        <input class="andes-switch__input" id="ml_hide_international" type="checkbox" role="switch">
        <span class="andes-switch__label">
          <span class="ui-label-builder ui-search-filter-highlighted__title">
            <span>Ocultar</br>compras internacionales</span>
          </span>
        </span>
      </label>
    `;
    const checkbox = container.querySelector('.andes-switch__input');

    const urlHasOfficialFilter = () => /ORIGIN_10215069/i.test(window.location.href);

    const getOfficialState = () => {
        if (officialInput && officialInput.isConnected) return !!officialInput.checked;
        return urlHasOfficialFilter();
    };

    const toggleHostClass = enabled => host.classList.toggle(HOST_CLASS, enabled);

    const applyState = () => {
        const officialActive = getOfficialState();
        const shouldHide = !officialActive && manualHide;
        toggleHostClass(shouldHide);
        checkbox.checked = shouldHide;
        checkbox.disabled = officialActive;
        checkbox.title = officialActive
            ? 'Desactiva el filtro oficial “Compra internacional” para volver a ocultarlos.'
        : '';
    };

    const setManualPreference = value => {
        manualHide = value;
        writeManualPref(value);
        applyState();
    };

    checkbox.addEventListener('change', () => setManualPreference(checkbox.checked));

    const injectToggle = () => {
        if (container.isConnected && (!officialContainer || container.parentNode === officialContainer.parentNode)) {
            return;
        }
        if (officialContainer && officialContainer.parentNode) {
            const parent = officialContainer.parentNode;
            parent.insertBefore(container, officialContainer.nextSibling);
            return;
        }
        // UBICACIÓN FALLBACK SI EL FILTRO OFICIAL NO LOGRA IDENTIFICARSE PARA USARLO DE SIBLING (sin un condicional, provoca que se vea temporalmente, revisar)
        // const sidebar = document.querySelector('[class*="ui-search-sidebar"]') || document.querySelector('aside');
        // const target = sidebar || document.querySelector('main') || document.body || document.documentElement;
        // if (target.firstChild) target.insertBefore(container, target.firstChild);
        // else target.appendChild(container);

    };

    const locateOfficialSwitch = () => {
        const labels = Array.from(document.querySelectorAll('.andes-switch'));
        const officialLabel = labels.find(label => {
            if (label.dataset.mlToggleLabel) return false;
            return /compra internacional/i.test(label.textContent || '');
        });

        if (!officialLabel) {
            if (officialInput && officialHandler) {
                officialInput.removeEventListener('change', officialHandler);
            }
            officialInput = null;
            officialHandler = null;
            officialContainer = null;
            return false;
        }

        officialContainer = officialLabel.closest('.ui-search-filter-highlighted__container') || officialLabel;
        const input = officialLabel.querySelector('.andes-switch__input');

        if (input && input !== officialInput) {
            if (officialInput && officialHandler) {
                officialInput.removeEventListener('change', officialHandler);
            }
            officialInput = input;
            officialHandler = () => setTimeout(applyState, 0);
            officialInput.addEventListener('change', officialHandler);
        }

        return true;
    };

    injectToggle();
    locateOfficialSwitch();
    applyState();

    const observer = new MutationObserver(mutations => {
        let needApply = false;
        for (const { addedNodes } of mutations) {
            addedNodes.forEach(node => {
                if (node.nodeType === 1) {
                    processTree(node);
                    needApply = true;
                }
            });
        }
        const foundOfficial = locateOfficialSwitch();
        if (foundOfficial || needApply) applyState();
        injectToggle();
    });

    observer.observe(document.body || document.documentElement, { childList: true, subtree: true });
})();