Greasy Fork镜像 is available in English.

eBay UI Enhancer v5.8 – Cleaned, Optimized, and Turbocharged

Major upgrade: removes off-topic ads, adds dark & compact modes, keyword filtering, seller badges, import/export, draggable neon UI — built for performance and customization. Price chart support coming soon.

// ==UserScript==
// @name         eBay UI Enhancer v5.8 – Cleaned, Optimized, and Turbocharged
// @namespace    https://greasyforks.org/users/Eliminater74
// @version      5.8
// @description  Major upgrade: removes off-topic ads, adds dark & compact modes, keyword filtering, seller badges, import/export, draggable neon UI — built for performance and customization. Price chart support coming soon.
// @author       Eliminater74
// @license      MIT
// @match        https://www.ebay.com/*
// @grant        none
// ==/UserScript==

(function () {
  'use strict';

  const SETTINGS_KEY = 'ebayEnhancerSettings';
  const POSITION_KEY = 'ebayEnhancerGearPosition';
  const PRICE_CACHE_KEY = 'ebayPriceCache';

  const defaultSettings = {
    darkMode: false,
    compactView: false,
    hideAds: true,
    hideOffTopicAds: true,
    highlightTopSellers: true,
    keywordFilter: true,
    showPriceStats: true,
    performanceMode: false,
    debug: false
  };

  let settings = { ...defaultSettings, ...(JSON.parse(localStorage.getItem(SETTINGS_KEY)) || {}) };
  let priceCache = JSON.parse(localStorage.getItem(PRICE_CACHE_KEY) || '{}');
  const log = (...args) => settings.debug && console.log('[eBay Enhancer]', ...args);
  const saveSettings = () => localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));

  function injectUI() {
    if (document.getElementById('ebay-enhancer-gear')) return;

    const gear = document.createElement('div');
    gear.id = 'ebay-enhancer-gear';
    gear.innerHTML = '<div class="gear-icon"></div>';
    document.body.appendChild(gear);

    const saved = JSON.parse(localStorage.getItem(POSITION_KEY));
    if (saved) {
      gear.style.left = saved.left;
      gear.style.top = saved.top;
    }

    const panel = document.createElement('div');
    panel.id = 'ebay-enhancer-panel';
    panel.innerHTML = `
      ${Object.keys(defaultSettings).map(key =>
        `<label><input type="checkbox" id="${key}Toggle"> ${key.replace(/([A-Z])/g, ' $1')}</label><br>`
      ).join('')}
      <hr><button id="exportSettings">Export</button>
      <button id="importSettings">Import</button>
    `;
    document.body.appendChild(panel);

    panel.querySelectorAll('input[type="checkbox"]').forEach(input => {
      const key = input.id.replace('Toggle', '');
      input.checked = settings[key];
      input.addEventListener('change', () => {
        settings[key] = input.checked;
        saveSettings();
        applyEnhancements();
      });
    });

    document.getElementById('exportSettings').onclick = () => {
      const blob = new Blob([JSON.stringify(settings)], { type: 'application/json' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = 'ebayEnhancerSettings.json';
      a.click();
    };

    document.getElementById('importSettings').onclick = () => {
      const input = document.createElement('input');
      input.type = 'file';
      input.accept = '.json';
      input.onchange = () => {
        const reader = new FileReader();
        reader.onload = e => {
          try {
            settings = JSON.parse(e.target.result);
            saveSettings();
            location.reload();
          } catch {
            alert('Invalid settings file.');
          }
        };
        reader.readAsText(input.files[0]);
      };
      input.click();
    };

    gear.onclick = () => {
      panel.style.display = panel.style.display === 'block' ? 'none' : 'block';
    };

    const style = document.createElement('style');
    style.textContent = `
      #ebay-enhancer-gear {
        position: fixed;
        top: 20px;
        left: 20px;
        width: 36px;
        height: 36px;
        background: black;
        border-radius: 50%;
        box-shadow: 0 0 10px #00f7ff;
        display: flex;
        justify-content: center;
        align-items: center;
        z-index: 99999;
        cursor: grab;
      }
      .gear-icon {
        width: 20px;
        height: 20px;
        border: 2px solid #00f7ff;
        border-radius: 50%;
        animation: spin 2s linear infinite;
      }
      @keyframes spin {
        0% { transform: rotate(0deg); }
        100% { transform: rotate(360deg); }
      }
      #ebay-enhancer-panel {
        position: fixed;
        top: 70px;
        left: 20px;
        background: #111;
        color: #0ff;
        padding: 10px;
        border-radius: 10px;
        font-family: monospace;
        box-shadow: 0 0 10px #00f7ff;
        z-index: 99999;
        display: none;
      }
      #ebay-enhancer-panel label {
        display: block;
        margin-bottom: 4px;
      }
    `;
    document.head.appendChild(style);

    makeDraggable(gear, POSITION_KEY);
    makeDraggable(panel);
  }

  function makeDraggable(el, persistKey) {
    let offsetX = 0, offsetY = 0, dragging = false;
    el.addEventListener('mousedown', e => {
      dragging = true;
      offsetX = e.clientX - el.offsetLeft;
      offsetY = e.clientY - el.offsetTop;
      el.style.cursor = 'grabbing';
      e.preventDefault();
    });
    document.addEventListener('mousemove', e => {
      if (!dragging) return;
      el.style.left = `${e.clientX - offsetX}px`;
      el.style.top = `${e.clientY - offsetY}px`;
    });
    document.addEventListener('mouseup', () => {
      dragging = false;
      el.style.cursor = 'grab';
      if (persistKey) localStorage.setItem(persistKey, JSON.stringify({ left: el.style.left, top: el.style.top }));
    });
  }

  function removeTopAdCarousels() {
    const killList = [];

    document.querySelectorAll('div[role="region"]').forEach(el => {
      if (el.innerText.includes('Shop store on eBay') && el.offsetTop < 300) killList.push(el);
    });

    document.querySelectorAll('div[id^="placement_"]').forEach(el => {
      if (el.offsetTop < 300 || el.innerText.includes('Sponsored')) killList.push(el);
    });

    document.querySelectorAll('.x-ebayui, .x-vds-carousel, .x-carousel-container').forEach(el => {
      if (el.offsetTop < 300 && el.innerText.includes('Shop store')) killList.push(el);
    });

    killList.forEach(el => {
      log('Removing persistent ad:', el);
      el.remove();
    });
  }

  function handlePriceBlock() {
    const titleNode = document.querySelector('#itemTitle');
    if (!titleNode) return;

    const priceNode = document.querySelector(
      '#prcIsum, [itemprop=price], .x-price-approx__price, .display-price, .x-price-approx__value, .x-price-primary span, [data-testid="x-bin-price"] .x-price-primary .ux-textspans'
    );
    if (!priceNode) {
      log('❌ Price element not found');
      return;
    }

    const title = titleNode.textContent.replace(/^Details about\s*/, '').trim();
    const key = title.toLowerCase();
    const price = parseFloat(priceNode.textContent.replace(/[^\d.]/g, ''));
    if (isNaN(price)) {
      log('❌ Price is not a number:', priceNode.textContent);
      return;
    }

    const history = priceCache[key] || [];
    const last = history.at(-1);
    const bars = ['▁','▂','▃','▄','▅','▆','▇','█'];
    let chart = '';
    if (history.length > 1) {
      const min = Math.min(...history);
      const max = Math.max(...history);
      const scale = n => Math.round(((n - min) / (max - min)) * 7);
      chart = history.map(p => bars[scale(p)]).join('');
    }

    const box = document.createElement('div');
    box.innerHTML = `
      <div style="margin-top:6px;font-size:13px;">
        Price History: ${chart || '—'}<br>
        <strong>Last:</strong> $${last ?? '—'}<br>
        <strong>Now:</strong> $${price.toFixed(2)}
      </div>
    `;
    priceNode.insertAdjacentElement('afterend', box);
    log('✅ Injected price chart below:', priceNode);

    if (!Array.isArray(history)) priceCache[key] = [];
    if (priceCache[key].at(-1) !== price) {
      priceCache[key].push(price);
      if (priceCache[key].length > 10) priceCache[key].shift();
      localStorage.setItem(PRICE_CACHE_KEY, JSON.stringify(priceCache));
    }
  }

  function applyEnhancements() {
    document.body.classList.toggle('ebay-dark-mode', settings.darkMode);
    document.body.classList.toggle('ebay-compact-mode', settings.compactView);

    if (settings.hideAds) {
      document.querySelectorAll('[data-testid="spnAd"], [aria-label="Sponsored"], .s-item__title--tagblock')
        .forEach(el => el.remove());
    }

    if (settings.hideOffTopicAds) {
      removeTopAdCarousels();
    }

    if (settings.highlightTopSellers) {
      document.querySelectorAll('.s-item__etrs-text, .s-item__etrs-badge').forEach(el => {
        el.style.backgroundColor = '#d4f7d4';
        el.style.borderRadius = '4px';
        el.style.padding = '2px 4px';
      });
    }

    if (settings.keywordFilter) {
      const blocked = ['broken', 'damaged', 'for parts'];
      document.querySelectorAll('.s-item').forEach(item => {
        const text = item.innerText.toLowerCase();
        if (blocked.some(term => text.includes(term))) item.remove();
      });
    }

    if (settings.showPriceStats) {
      handlePriceBlock();
    }
  }

  injectUI();
  const observer = new MutationObserver(applyEnhancements);
  observer.observe(document.body, { childList: true, subtree: true });
})();
长期地址
遇到问题?请前往 GitHub 提 Issues。