eBay Shipping Cost Calculator

Adds shipping cost to item price in eBay search results

当前为 2025-06-22 提交的版本,查看 最新版本

// ==UserScript==
// @name         eBay Shipping Cost Calculator
// @namespace    http://tampermonkey.net/
// @version      1.8
// @description  Adds shipping cost to item price in eBay search results
// @author       none
// @match        https://www.ebay.com/sch/*
// @icon         https://www.ebay.com/favicon.ico
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    let processing = false;
    let debounceTimer;

    // --- Settings ---
    let settings = {
        taxRate: parseFloat(localStorage.getItem('ebayTaxRate')) || 0,
        color: localStorage.getItem('ebayTotalColor') || '#e42648',
        fontSize: localStorage.getItem('ebayTotalFontSize') || '18'
    };

    // --- Settings UI ---
    function createSettingsButton() {
        const button = document.createElement('div');
        button.id = 'ebay-settings-button';
        button.style.position = 'fixed';
        button.style.top = '10px';
        button.style.right = '10px';
        button.style.width = '20px';
        button.style.height = '20px';
        button.style.borderRadius = '50%';
        button.style.backgroundColor = '#e42648';
        button.style.cursor = 'pointer';
        button.style.zIndex = '10000';
        button.style.boxShadow = '0 2px 4px rgba(0,0,0,0.2)';

        document.body.appendChild(button);

        button.addEventListener('click', toggleSettingsWindow);
    }

    function createSettingsWindow() {
        const settingsDiv = document.createElement('div');
        settingsDiv.id = 'ebay-shipping-settings';
        settingsDiv.style.position = 'fixed';
        settingsDiv.style.top = '40px';
        settingsDiv.style.right = '20px';
        settingsDiv.style.zIndex = '2147483647';
        settingsDiv.style.background = 'white';
        settingsDiv.style.border = '1px solid #ccc';
        settingsDiv.style.padding = '16px';
        settingsDiv.style.borderRadius = '8px';
        settingsDiv.style.boxShadow = '0 2px 8px rgba(0,0,0,0.15)';
        settingsDiv.style.fontFamily = 'Arial, sans-serif';
        settingsDiv.style.minWidth = '220px';
        settingsDiv.style.display = 'none';

        document.addEventListener('click', function(event) {
        const settingsDiv = document.getElementById('ebay-shipping-settings');
        const settingsButton = document.getElementById('ebay-settings-button');

        if (settingsDiv && settingsDiv.style.display === 'block') {
            // Check if click is outside both settings window and button
            if (!settingsDiv.contains(event.target) && !settingsButton.contains(event.target)) {
                settingsDiv.style.display = 'none';
            }
        }
    });

        settingsDiv.innerHTML = `
            <strong>Shipping Calculator Settings</strong><br><br>
            <label>Tax Rate (%): <input type="number" id="ebay-tax-rate" min="0" max="100" step="0.01" value="${settings.taxRate}" style="width:60px"></label><br><br>
            <label>Text Color: <input type="color" id="ebay-total-color" value="${settings.color}"></label><br><br>
            <label>Text Size:
                <input type="range" id="ebay-total-fontsize" min="12" max="36" value="${settings.fontSize}" style="vertical-align:middle;">
                <span id="ebay-total-fontsize-value">${settings.fontSize}px</span>
            </label>
        `;

        document.body.appendChild(settingsDiv);

        // Event listeners
        document.getElementById('ebay-tax-rate').addEventListener('input', function() {
            settings.taxRate = parseFloat(this.value) || 0;
            localStorage.setItem('ebayTaxRate', settings.taxRate);
            addShippingToPrices(); // Always update on input
        });
        document.getElementById('ebay-total-color').addEventListener('input', function() {
            settings.color = this.value;
            localStorage.setItem('ebayTotalColor', settings.color);
            addShippingToPrices();
        });
        document.getElementById('ebay-total-fontsize').addEventListener('input', function() {
            settings.fontSize = this.value;
            localStorage.setItem('ebayTotalFontSize', settings.fontSize);
            document.getElementById('ebay-total-fontsize-value').textContent = `${settings.fontSize}px`;
            addShippingToPrices();
        });
    }

    function toggleSettingsWindow() {
        const settingsDiv = document.getElementById('ebay-shipping-settings');
        if (settingsDiv) {
            settingsDiv.style.display = settingsDiv.style.display === 'none' ? 'block' : 'none';
        }
    }

    // --- Main logic ---
  function addShippingToPrices() {
    if (processing) return;
    processing = true;

    // Remove processed class to force recalculation on all items
    document.querySelectorAll('.s-card__attribute-row.processed').forEach(row => {
        row.classList.remove('processed');
    });

    // For each price row
    document.querySelectorAll('.s-card__attribute-row:not(.processed)').forEach(priceRow => {
        const priceEl = priceRow.querySelector('.s-card__price');
        if (!priceEl) return;

        // Look ahead for a shipping row with 'delivery'
        let nextRow = priceRow.nextElementSibling;
        let shippingEl = null;
        while (nextRow) {
            if (nextRow.classList.contains('s-card__attribute-row')) {
                shippingEl = Array.from(nextRow.querySelectorAll('span')).find(
                    el => el.textContent.toLowerCase().includes('delivery')
                );
                if (shippingEl) break;
            }
            nextRow = nextRow.nextElementSibling;
        }
        if (!shippingEl) return;

        const price = parsePrice(priceEl.textContent);
        const shipping = parseShipping(shippingEl.textContent);

        if (price !== null && shipping !== null) {
            let total = price + shipping;
            if (settings.taxRate > 0) {
                total += total * (settings.taxRate / 100);
            }
            // Insert or update total display after priceEl
            let totalEl = priceRow.querySelector('.s-item__total');
            if (!totalEl) {
                totalEl = document.createElement('div');
                totalEl.className = 's-item__total';
                priceEl.parentNode.insertBefore(totalEl, priceEl.nextSibling);
            }
            totalEl.textContent = `Total: $${total.toFixed(2)}`;
            totalEl.style.color = settings.color;
            totalEl.style.fontWeight = 'bold';
            totalEl.style.fontSize = settings.fontSize + 'px';
        }
        priceRow.classList.add('processed');
    });

    // Update already processed items in case settings changed
    document.querySelectorAll('.s-card__attribute-row.processed .s-item__total').forEach(totalEl => {
        totalEl.style.color = settings.color;
        totalEl.style.fontWeight = 'bold';
        totalEl.style.fontSize = settings.fontSize + 'px';
    });

    processing = false;
}

function parsePrice(text) {
    const match = text.match(/\$([\d,.]+)/);
    if (!match) return null;
    return parseFloat(match[1].replace(/,/g, ''));
}

function parseShipping(text) {
   if (/free/i.test(text)) return 0;
    // Match $xx.xx before 'delivery' (with or without extra text after)
    const match = text.match(/\$([\d,.]+)\s+delivery/i);
    if (match) return parseFloat(match[1].replace(/,/g, ''));
    // Fallback: match any $xx.xx in the string
    const fallback = text.match(/\$([\d,.]+)/);
    if (fallback) return parseFloat(fallback[1].replace(/,/g, ''));
    return null;
}

    function handleMutations() {
        clearTimeout(debounceTimer);
        debounceTimer = setTimeout(addShippingToPrices, 300);
    }

    // --- Init ---
    createSettingsButton();
    createSettingsWindow();
    addShippingToPrices();

    const container = document.querySelector('.srp-river-main');
    if (container) {
        const observer = new MutationObserver(handleMutations);
        observer.observe(container, {
            childList: true,
            subtree: false,
            attributes: false,
            characterData: false
        });
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址