// ==UserScript== // @name European Price Checker for Amazon (fr, de, es, it) // @namespace http://tampermonkey.net/ // @version 2.0 // @description Compare product prices on Amazon.fr, Amazon.de, Amazon.es, and Amazon.it to find the best deal. Integrates CamelCamelCamel for price history charts. // @author bNj // @icon https://i.ibb.co/qrjrcVy/amz-price-checker.png // @match https://www.amazon.fr/* // @match https://www.amazon.de/* // @match https://www.amazon.es/* // @match https://www.amazon.it/* // @grant GM_xmlhttpRequest // @connect amazon.fr // @connect amazon.es // @connect amazon.it // @connect amazon.de // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; const ASIN_REGEX = /\/([A-Z0-9]{10})(?:[/?]|$)/; const asinMatch = window.location.href.match(ASIN_REGEX); if (!asinMatch) { return; } const asin = asinMatch[1]; const amazonSites = [ { name: 'Amazon.fr', url: `https://www.amazon.fr/dp/${asin}`, domain: 'amazon.fr', flag: 'https://flagcdn.com/w20/fr.png' }, { name: 'Amazon.es', url: `https://www.amazon.es/dp/${asin}`, domain: 'amazon.es', flag: 'https://flagcdn.com/w20/es.png' }, { name: 'Amazon.it', url: `https://www.amazon.it/dp/${asin}`, domain: 'amazon.it', flag: 'https://flagcdn.com/w20/it.png' }, { name: 'Amazon.de', url: `https://www.amazon.de/dp/${asin}`, domain: 'amazon.de', flag: 'https://flagcdn.com/w20/de.png' } ]; let basePrice = null; const createLoadingContainer = () => { const priceElement = document.querySelector('.priceToPay, #priceblock_ourprice, #priceblock_dealprice'); if (priceElement) { const container = document.createElement('div'); container.id = 'amazonPriceComparisonContainer'; container.style.cssText = 'margin-top: 20px; padding: 10px; background-color: #f9f9f9; border: 1px solid #ccc; border-radius: 8px; position: relative; font-size: 12px;'; container.innerHTML = `
European Price Checker Logo
Checking other Amazon sites...
`; priceElement.parentNode.appendChild(container); animateLoadingText(); basePrice = getPriceFromPage(priceElement.textContent); } }; const animateLoadingText = () => { const text = document.getElementById('animatedText'); if (text) { let position = 0; setInterval(() => { position = (position + 2) % 100; text.style.cssText = ` background-image: linear-gradient(90deg, black 0%, black ${position - 20}%, #FF9900 ${position}%, black ${position + 20}%, black 100%); background-clip: text; -webkit-background-clip: text; color: transparent; font-weight: bold; font-size: 14px; `; }, 80); } }; const removeLoadingIndicator = () => { const loadingMessage = document.getElementById('loadingMessage'); if (loadingMessage) { loadingMessage.style.transition = 'opacity 1s'; loadingMessage.style.opacity = '0'; setTimeout(() => { loadingMessage.remove(); displayAllResults(); }, 1000); } }; const getPriceFromPage = (priceText) => { priceText = priceText.replace(/\s+/g, ''); const priceMatch = priceText.match(/\d+[\.,]\d{2}/); return priceMatch ? parseFloat(priceMatch[0].replace(',', '.')) : null; }; const getPriceFromResponse = (responseText) => { const priceMatch = responseText.match(/(\d+)[.,]<\/span><\/span>(\d{2})<\/span>/) || responseText.match(/]*>.*?(\d+)(.)<\/span><\/span>(\d{2})<\/span>/); return priceMatch ? parseFloat(`${priceMatch[1]}.${priceMatch[2]}`) : null; }; const getDeliveryPriceFromResponse = (responseText) => { const deliveryMatch = responseText.match(/data-csa-c-delivery-price="[^"]*(\d+[\.,]\d{2})/); return deliveryMatch ? parseFloat(deliveryMatch[1].replace(',', '.')) : null; }; const addPriceToPage = (siteName, price, deliveryPrice, url, flag, percentageDifference, totalPercentageDifference) => { const priceContainer = document.querySelector('#amazonPriceComparisonContainer'); if (!priceContainer) return; const row = document.createElement('div'); row.className = 'comparison-row'; row.dataset.totalPrice = price + (deliveryPrice || 0); // Store total price for sorting row.style.cssText = 'cursor: pointer; display: flex; justify-content: space-between; padding: 5px 0; border-bottom: 1px solid #ccc;'; row.onmouseover = () => row.style.backgroundColor = '#f1f1f1'; row.onmouseout = () => row.style.backgroundColor = 'inherit'; row.onclick = () => window.open(url, '_blank'); const createCell = (content, style = '') => { const cell = document.createElement('div'); cell.style.flex = '1'; cell.style.textAlign = 'center'; cell.style.cssText += style; cell.innerHTML = content; return cell; }; const totalPrice = price + (deliveryPrice || 0); row.append( createCell(`${siteName} flag ${siteName}`), createCell(`€${price.toFixed(2)}`), createCell(percentageDifference !== null ? `${percentageDifference > 0 ? '+' : ''}${percentageDifference.toFixed(2)}% (€${(price - basePrice).toFixed(2)})` : '-'), createCell(deliveryPrice !== null ? ` €${deliveryPrice.toFixed(2)}` : '-'), createCell(`€${totalPrice.toFixed(2)}`), createCell(totalPercentageDifference !== null ? `${totalPercentageDifference > 0 ? '+' : ''}${totalPercentageDifference.toFixed(2)}% (€${(totalPrice - basePrice).toFixed(2)})` : '-') ); priceContainer.appendChild(row); }; const addCamelCamelCamelChart = () => { const priceContainer = document.querySelector('#amazonPriceComparisonContainer'); if (!priceContainer) return; const chartContainer = document.createElement('div'); chartContainer.style.marginTop = '20px'; chartContainer.style.textAlign = 'center'; let countryCode = 'fr'; if (window.location.hostname.includes('amazon.de')) countryCode = 'de'; else if (window.location.hostname.includes('amazon.es')) countryCode = 'es'; else if (window.location.hostname.includes('amazon.it')) countryCode = 'it'; const controlsContainer = document.createElement('div'); controlsContainer.style.cssText = 'text-align: center; margin: 10px; display: flex; justify-content: center; align-items: center; gap: 10px;'; const timePeriods = [ { id: 'btn1Month', label: '1 Month', value: '1m' }, { id: 'btn3Months', label: '3 Months', value: '3m' }, { id: 'btn6Months', label: '6 Months', value: '6m' }, { id: 'btn1Year', label: '1 Year', value: '1y' }, { id: 'btnAll', label: 'All', value: 'all' } ]; let selectedTimePeriod = 'all'; timePeriods.forEach(period => { const button = document.createElement('button'); button.id = period.id; button.textContent = period.label; button.style.cssText = ` padding: 5px 10px; border: 1px solid #ccc; border-radius: 5px; background-color: ${period.value === selectedTimePeriod ? '#ff9900' : '#f9f9f9'}; cursor: pointer; `; button.addEventListener('click', () => { selectedTimePeriod = period.value; timePeriods.forEach(p => { document.getElementById(p.id).style.backgroundColor = p.value === selectedTimePeriod ? '#ff9900' : '#f9f9f9'; }); updateChartUrl(); }); controlsContainer.appendChild(button); }); const checkboxes = [ { id: 'checkboxAmazon', label: 'Amazon', filename: 'amazon', disabled: true, checked: true }, { id: 'checkboxNew', label: 'New', filename: 'new', checked: false }, { id: 'checkboxUsed', label: 'Used', filename: 'used', checked: false } ]; checkboxes.forEach(checkbox => { const container = document.createElement('div'); container.style.display = 'flex'; container.style.alignItems = 'center'; const checkboxElement = document.createElement('input'); checkboxElement.type = 'checkbox'; checkboxElement.id = checkbox.id; checkboxElement.checked = checkbox.checked; checkboxElement.style.marginRight = '5px'; if (checkbox.disabled) checkboxElement.disabled = true; const labelElement = document.createElement('label'); labelElement.htmlFor = checkbox.id; labelElement.textContent = checkbox.label; labelElement.style.fontWeight = 'normal'; container.append(checkboxElement, labelElement); controlsContainer.appendChild(container); }); priceContainer.appendChild(controlsContainer); const updateChartUrl = () => { const selectedFilenames = checkboxes .filter(checkbox => document.getElementById(checkbox.id).checked) .map(checkbox => checkbox.filename) .join('-'); const chartUrl = `https://charts.camelcamelcamel.com/${countryCode}/${asin}/${selectedFilenames}.png?force=1&zero=0&w=600&h=300&desired=false&legend=1&ilt=1&tp=${selectedTimePeriod}&fo=0&lang=en`; const camelUrl = `https://${countryCode}.camelcamelcamel.com/product/${asin}`; chartContainer.innerHTML = `Price history chart for ${asin}`; }; checkboxes.forEach(checkbox => { document.getElementById(checkbox.id).addEventListener('change', updateChartUrl); }); updateChartUrl(); priceContainer.appendChild(chartContainer); // Add footer with script info after the chart const footer = document.createElement('div'); footer.style.cssText = 'text-align: right; font-size: 0.7em; color: #666; margin-top: 10px;'; footer.innerHTML = `Logo European Price Checker for Amazon by bNj v${GM_info.script.version}`; priceContainer.appendChild(footer); }; let priceResults = []; const storePriceResult = (siteName, price, deliveryPrice, url, flag) => { if (price !== null) { priceResults.push({ siteName, price, deliveryPrice, url, flag }); } }; const displayAllResults = () => { const priceContainer = document.querySelector('#amazonPriceComparisonContainer'); if (!priceContainer) return; priceContainer.innerHTML = ''; // Clear loading message and icon const headerRow = document.createElement('div'); headerRow.className = 'comparison-row header-row'; headerRow.style.cssText = 'display: flex; justify-content: space-between; padding: 5px 0; border-bottom: 2px solid #000; font-weight: bold;'; ['Site', 'Price', 'Difference (Price %)', 'Delivery', 'Total', 'Difference (Total %)'].forEach(header => { const headerCell = document.createElement('div'); headerCell.style.flex = '1'; headerCell.style.textAlign = 'center'; headerCell.textContent = header; headerRow.appendChild(headerCell); }); priceContainer.appendChild(headerRow); if (priceResults.length > 0 && basePrice !== null) { priceResults.sort((a, b) => (a.price + (a.deliveryPrice || 0)) - (b.price + (b.deliveryPrice || 0))); priceResults.forEach(result => { const percentageDifference = ((result.price - basePrice) / basePrice) * 100; const totalPrice = result.price + (result.deliveryPrice || 0); const totalBasePrice = basePrice; const totalPercentageDifference = ((totalPrice - totalBasePrice) / totalBasePrice) * 100; addPriceToPage(result.siteName, result.price, result.deliveryPrice, result.url, result.flag, percentageDifference, totalPercentageDifference); }); } else { priceContainer.innerHTML = '
No prices available
'; } addCamelCamelCamelChart(); }; createLoadingContainer(); amazonSites.forEach(site => { GM_xmlhttpRequest({ method: 'GET', url: site.url, headers: { 'User-Agent': 'Mozilla/5.0', 'Accept-Language': 'en-US,en;q=0.5' }, onload: function(response) { if (response.status === 200) { const price = getPriceFromResponse(response.responseText); const deliveryPrice = getDeliveryPriceFromResponse(response.responseText); storePriceResult(site.name, price, deliveryPrice, site.url, site.flag); } else { storePriceResult(site.name, null, null, site.url, site.flag); } if (priceResults.length === amazonSites.length) { removeLoadingIndicator(); } }, onerror: function() { storePriceResult(site.name, null, null, site.url, site.flag); if (priceResults.length === amazonSites.length) { removeLoadingIndicator(); } } }); }); })();