// ==UserScript==
// @name Amazon to eBay Sidebar Enhancer Ultimate
// @namespace Eliminater74
// @version 1.4.0
// @description Show eBay matches on Amazon product pages with inline or floating UI, sorting, persistent manual search, drag+snap gear, theme toggle, and optional copy button and styling cleanup for maximum polish and tweakability.
// @author Eliminater74
// @license MIT
// @match https://www.amazon.com/*
// @grant GM_xmlhttpRequest
// @connect www.ebay.com
// @run-at document-end
// ==/UserScript==
(function () {
'use strict';
const SETTINGS_KEY = 'amazonEbayEnhancerSettings';
const POSITION_KEY = 'amazonEbayEnhancerPositions';
const SEARCH_KEY = 'amazonEbayManualSearch';
const defaultSettings = {
showPanel: true,
showImages: true,
theme: 'auto',
maxResults: 5,
sortMode: 'relevance',
injectInline: false,
manualSearch: false,
showCopyButton: true
};
let settings = JSON.parse(localStorage.getItem(SETTINGS_KEY)) || defaultSettings;
let positions = JSON.parse(localStorage.getItem(POSITION_KEY)) || {};
let manualSearchText = localStorage.getItem(SEARCH_KEY) || '';
function saveSettings() {
localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));
}
function savePositions() {
localStorage.setItem(POSITION_KEY, JSON.stringify(positions));
}
function saveSearchOverride(text) {
localStorage.setItem(SEARCH_KEY, text);
}
function getTheme() {
return settings.theme === 'auto'
? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
: settings.theme;
}
function applyTheme(el) {
const theme = getTheme();
el.classList.add(`ebay-enhancer-${theme}`);
}
function waitForTitle(cb) {
const el = document.querySelector('#productTitle');
if (el) cb(el.textContent.trim());
else setTimeout(() => waitForTitle(cb), 400);
}
function fetchEbayResults(query, callback) {
showLoadingBox();
const sort = settings.sortMode === 'price' ? '&_sop=15' : '&_sop=12';
const url = `https://www.ebay.com/sch/i.html?_nkw=${encodeURIComponent(query)}${sort}`;
GM_xmlhttpRequest({
method: 'GET',
url,
onload: res => {
const doc = new DOMParser().parseFromString(res.responseText, 'text/html');
const items = [...doc.querySelectorAll('.s-item')].slice(0, settings.maxResults).map(el => ({
title: el.querySelector('.s-item__title')?.textContent || 'No Title',
price: el.querySelector('.s-item__price')?.textContent || 'N/A',
link: el.querySelector('a.s-item__link')?.href || '#',
image: el.querySelector('.s-item__image-img')?.src || null
}));
hideLoadingBox();
callback(items);
}
});
}
function showLoadingBox() {
const box = document.createElement('div');
box.id = 'ebay-loading-box';
box.className = 'ebay-enhancer-box';
box.textContent = '🔍 Searching eBay for matches...';
document.body.appendChild(box);
}
function hideLoadingBox() {
const box = document.getElementById('ebay-loading-box');
if (box) box.remove();
}
function createStyle() {
const style = document.createElement('style');
style.textContent = `
.ebay-enhancer-light { background: #f9f9f9; color: #000; }
.ebay-enhancer-dark { background: #1e1e1e; color: #f0f0f0; }
.ebay-enhancer-panel {
position: fixed;
width: 330px;
max-height: 70vh;
overflow-y: auto;
border: 2px solid #999;
padding: 10px;
border-radius: 6px;
box-shadow: 0 0 10px rgba(0,0,0,0.3);
z-index: 99999;
cursor: move;
}
.ebay-enhancer-gear {
position: fixed;
width: 40px;
height: 40px;
font-size: 22px;
background: #222;
color: #fff;
border: 2px solid #888;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
cursor: move;
z-index: 2147483647;
}
.ebay-enhancer-config {
position: fixed;
background: #fff;
border: 1px solid #ccc;
padding: 10px;
border-radius: 8px;
display: none;
z-index: 999999;
}
.ebay-enhancer-box {
position: fixed;
top: 100px;
right: 20px;
padding: 12px 16px;
font-size: 14px;
font-weight: bold;
border-radius: 6px;
border: 2px solid #ccc;
z-index: 999999;
}
`;
document.head.appendChild(style);
}
function makeDraggable(el, id) {
let isDragging = false, offsetX = 0, offsetY = 0;
el.addEventListener('mousedown', e => {
isDragging = true;
el.dataset.dragging = true;
offsetX = e.clientX - el.getBoundingClientRect().left;
offsetY = e.clientY - el.getBoundingClientRect().top;
e.preventDefault();
});
document.addEventListener('mousemove', e => {
if (!isDragging) return;
const left = e.clientX - offsetX;
const top = e.clientY - offsetY;
const width = window.innerWidth, height = window.innerHeight;
if (left < width / 2) {
el.style.left = '10px'; el.style.right = 'unset';
positions[`${id}Left`] = '10px'; delete positions[`${id}Right`];
} else {
el.style.right = '10px'; el.style.left = 'unset';
positions[`${id}Right`] = '10px'; delete positions[`${id}Left`];
}
if (top < height / 2) {
el.style.top = '10px'; el.style.bottom = 'unset';
positions[`${id}Top`] = '10px'; delete positions[`${id}Bottom`];
} else {
el.style.bottom = '10px'; el.style.top = 'unset';
positions[`${id}Bottom`] = '10px'; delete positions[`${id}Top`];
}
savePositions();
});
document.addEventListener('mouseup', () => {
isDragging = false;
delete el.dataset.dragging;
});
}
function buildResultHTML(results) {
return results.map(r => `
<div style="margin-bottom:12px;">
<a href="${r.link}" target="_blank" style="font-weight:bold; color:#0073e6; text-decoration:none;">${r.title}</a><br/>
<span>${r.price}</span><br/>
${settings.showImages && r.image ? `<img src="${r.image}" style="width:100px; margin-top:4px;" />` : ''}
</div>`).join('');
}
function createResultsPanel(results) {
const wrapper = document.createElement('div');
wrapper.id = 'ebay-enhancer-panel';
wrapper.innerHTML = `<h3 style="margin-top:0;">🛒 eBay Matches</h3>` + buildResultHTML(results);
if (settings.showCopyButton) {
const btn = document.createElement('button');
btn.textContent = '📋 Copy All to Clipboard';
btn.style.cssText = 'margin-top:10px;padding:5px;font-size:13px;';
btn.onclick = () => {
const text = results.map(r => `${r.title}\n${r.price}\n${r.link}\n`).join('\n');
navigator.clipboard.writeText(text).then(() => alert("Copied!"));
};
wrapper.appendChild(btn);
}
if (settings.injectInline) {
const target = document.getElementById('productTitle');
if (target) {
const box = document.createElement('div');
box.style.marginTop = '20px';
box.style.border = '1px solid #ccc';
box.style.padding = '10px';
applyTheme(box);
box.appendChild(wrapper);
target.parentNode.appendChild(box);
}
} else {
applyTheme(wrapper);
wrapper.classList.add('ebay-enhancer-panel');
wrapper.style.top = positions.panelTop || '80px';
wrapper.style.left = positions.panelLeft || 'unset';
wrapper.style.right = positions.panelRight || '10px';
makeDraggable(wrapper, 'panel');
document.body.appendChild(wrapper);
}
}
function createGearIcon() {
const gear = document.createElement('div');
gear.className = 'ebay-enhancer-gear';
gear.textContent = '⚙️';
gear.style.top = positions.gearTop || 'unset';
gear.style.left = positions.gearLeft || 'unset';
gear.style.bottom = positions.gearBottom || '20px';
gear.style.right = positions.gearRight || '20px';
const panel = document.createElement('div');
panel.className = 'ebay-enhancer-config';
panel.style.bottom = '70px';
panel.style.right = '20px';
panel.innerHTML = `
<label><input type="checkbox" id="ebayToggle" ${settings.showPanel ? 'checked' : ''}/> Show Panel</label><br/>
<label><input type="checkbox" id="imageToggle" ${settings.showImages ? 'checked' : ''}/> Show Images</label><br/>
<label><input type="checkbox" id="inlineToggle" ${settings.injectInline ? 'checked' : ''}/> Inject Inline</label><br/>
<label><input type="checkbox" id="manualToggle" ${settings.manualSearch ? 'checked' : ''}/> Manual Search</label><br/>
<label><input type="checkbox" id="copyToggle" ${settings.showCopyButton ? 'checked' : ''}/> Show Copy Button</label><br/>
<label>Sort:
<select id="sortMode">
<option value="relevance" ${settings.sortMode === 'relevance' ? 'selected' : ''}>Relevance</option>
<option value="price" ${settings.sortMode === 'price' ? 'selected' : ''}>Price ↑</option>
</select>
</label><br/>
<label>Theme:
<select id="themeSelect">
<option value="auto" ${settings.theme === 'auto' ? 'selected' : ''}>Auto</option>
<option value="light" ${settings.theme === 'light' ? 'selected' : ''}>Light</option>
<option value="dark" ${settings.theme === 'dark' ? 'selected' : ''}>Dark</option>
</select>
</label><br/>
<label>Max Results: <input type="number" id="maxResults" value="${settings.maxResults}" style="width:50px"/></label><br/>
<input type="text" id="manualInput" placeholder="Search override..." value="${manualSearchText}" style="width:100%;margin-top:5px;"/>
`;
gear.addEventListener('click', () => {
if (!gear.dataset.dragging)
panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
});
const idMap = {
ebayToggle: 'showPanel',
imageToggle: 'showImages',
inlineToggle: 'injectInline',
manualToggle: 'manualSearch',
copyToggle: 'showCopyButton'
};
for (const id in idMap) {
panel.querySelector(`#${id}`).addEventListener('change', e => {
settings[idMap[id]] = e.target.checked;
saveSettings();
location.reload();
});
}
panel.querySelector('#sortMode').addEventListener('change', e => {
settings.sortMode = e.target.value;
saveSettings();
location.reload();
});
panel.querySelector('#themeSelect').addEventListener('change', e => {
settings.theme = e.target.value;
saveSettings();
location.reload();
});
panel.querySelector('#maxResults').addEventListener('change', e => {
settings.maxResults = parseInt(e.target.value) || 5;
saveSettings();
location.reload();
});
panel.querySelector('#manualInput').addEventListener('input', e => {
saveSearchOverride(e.target.value);
});
makeDraggable(gear, 'gear');
document.body.appendChild(gear);
document.body.appendChild(panel);
}
createStyle();
waitForTitle(title => {
const query = settings.manualSearch ? manualSearchText || title : title;
if (settings.showPanel) {
fetchEbayResults(query, createResultsPanel);
}
createGearIcon();
});
})();