// ==UserScript==
// @name European Price Checker for Amazon (fr, de, es, it)
// @namespace http://tampermonkey.net/
// @version 2.2
// @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 styles = `
#amazonPriceComparisonContainer {
margin-top: 20px;
padding: 10px;
background-color: #f9f9f9;
border: 1px solid #ccc;
border-radius: 8px;
position: relative;
font-size: 11px;
}
.comparison-row {
cursor: pointer;
display: flex;
justify-content: space-between;
padding: 5px 0;
border-bottom: 1px solid #ccc;
}
.comparison-row:hover {
background-color: #f1f1f1;
}
.comparison-row.header-row {
border-bottom: 2px solid #000;
font-weight: bold;
pointer-events: none;
}
#loadingMessage {
text-align: center;
font-weight: bold;
font-size: 14px;
display: flex;
flex-direction: column;
align-items: center;
background-clip: text;
-webkit-background-clip: text;
color: transparent;
background-image: linear-gradient(270deg, black 0%, black 20%, #FF9900 50%, black 80%, black 100%);
background-size: 200% 100%;
animation: loadingAnimation 2s linear infinite;
}
@keyframes loadingAnimation {
0% {
background-position: 100% 50%;
}
100% {
background-position: 0% 50%;
}
}
.active-button {
background-color: #ff9900 !important;
}
.price-difference-positive {
color: green;
}
.price-difference-negative {
color: red;
}
`;
const styleSheet = document.createElement('style');
styleSheet.type = 'text/css';
styleSheet.innerText = styles;
document.head.appendChild(styleSheet);
const ASIN_REGEX = /\/[A-Z0-9]{10}(?:[/?]|$)/;
const asinMatch = window.location.href.match(ASIN_REGEX);
if (!asinMatch) return;
const asin = asinMatch[0].slice(1, 11);
const PARTNER_IDS = {
'fr': 'geeksince1983-21',
'es': 'geeksince1901-21',
'it': 'geeksince1903-21',
'de': 'geeksince190d-21'
};
const amazonSites = [
{ name: 'Amazon.fr', country: 'fr', flag: 'https://flagcdn.com/w20/fr.png' },
{ name: 'Amazon.es', country: 'es', flag: 'https://flagcdn.com/w20/es.png' },
{ name: 'Amazon.it', country: 'it', flag: 'https://flagcdn.com/w20/it.png' },
{ name: 'Amazon.de', country: 'de', flag: 'https://flagcdn.com/w20/de.png' }
];
let selectedTimePeriod = 'all';
const basePrice = getPriceFromPage();
if (basePrice === null) return;
createLoadingContainer();
let requestCount = 0;
const priceResults = [];
amazonSites.forEach(site => {
const url = `https://www.amazon.${site.country}/dp/${asin}?tag=${PARTNER_IDS[site.country]}`;
GM_xmlhttpRequest({
method: 'GET',
url: url,
headers: { 'User-Agent': 'Mozilla/5.0', 'Accept-Language': 'en-US,en;q=0.5' },
onload: response => handleResponse(site, response),
onerror: () => handleResponse(site, null)
});
});
function handleResponse(site, response) {
requestCount++;
if (response && response.status === 200) {
const price = getPriceFromResponse(response.responseText);
const deliveryPrice = getDeliveryPriceFromResponse(response.responseText);
if (price !== null) {
priceResults.push({ ...site, price, deliveryPrice });
}
}
if (requestCount === amazonSites.length) {
displayAllResults();
}
}
function createLoadingContainer() {
const priceElement = document.querySelector('.priceToPay, #priceblock_ourprice, #priceblock_dealprice, #priceblock_saleprice');
if (priceElement && priceElement.parentNode) {
const container = document.createElement('div');
container.id = 'amazonPriceComparisonContainer';
container.innerHTML = `

Checking other Amazon sites...
`;
priceElement.parentNode.appendChild(container);
}
}
function getPriceFromPage() {
const priceElement = document.querySelector('.priceToPay, #priceblock_ourprice, #priceblock_dealprice, #priceblock_saleprice');
if (!priceElement) return null;
return parseFloat(priceElement.textContent.replace(/[^0-9,\.]/g, '').replace(',', '.'));
}
function getPriceFromResponse(responseText) {
const priceMatch = responseText.match(/(\d+)[.,]<\/span><\/span>(\d{2})<\/span>/);
return priceMatch ? parseFloat(`${priceMatch[1]}.${priceMatch[2]}`) : null;
}
function getDeliveryPriceFromResponse(responseText) {
const deliveryMatch = responseText.match(/data-csa-c-delivery-price="[^\"]*(\d+[.,]\d{2})/);
return deliveryMatch ? parseFloat(deliveryMatch[1].replace(',', '.')) : 0;
}
function displayAllResults() {
const priceContainer = document.querySelector('#amazonPriceComparisonContainer');
if (!priceContainer) return;
priceContainer.innerHTML = '';
const headerRow = document.createElement('div');
headerRow.className = 'comparison-row header-row';
['Site', 'Price', 'Delivery', 'Total', 'Difference'].forEach(header => {
const headerCell = document.createElement('div');
headerCell.style.flex = '1';
headerCell.style.textAlign = 'center';
headerCell.textContent = header;
headerRow.appendChild(headerCell);
});
priceContainer.appendChild(headerRow);
priceResults.sort((a, b) => (a.price + a.deliveryPrice) - (b.price + b.deliveryPrice));
priceResults.forEach(result => {
const row = document.createElement('div');
row.className = 'comparison-row';
row.onclick = () => window.open(`https://www.amazon.${result.country}/dp/${asin}?tag=${PARTNER_IDS[result.country]}`, '_blank');
const totalPrice = result.price + (result.deliveryPrice || 0);
const difference = totalPrice - basePrice;
if (difference !== 0) {
const differencePercentage = ((difference / basePrice) * 100).toFixed(2);
const differenceClass = difference < 0 ? 'price-difference-positive' : 'price-difference-negative';
row.append(
createCell(`
${result.name}`),
createCell(`€${result.price.toFixed(2)}`),
createCell(result.deliveryPrice ? `
€${result.deliveryPrice.toFixed(2)}` : '-'),
createCell(`€${totalPrice.toFixed(2)}`),
createCell(`${difference >= 0 ? '+' : ''}€${difference.toFixed(2)} (${differencePercentage}%)`)
);
} else {
row.append(
createCell(`
${result.name}`),
createCell(`€${result.price.toFixed(2)}`),
createCell(result.deliveryPrice ? `
€${result.deliveryPrice.toFixed(2)}` : '-'),
createCell(`€${totalPrice.toFixed(2)}`),
createCell('-')
);
}
priceContainer.appendChild(row);
});
addSortingButtons();
addCamelCamelCamelChart();
}
function createCell(content) {
const cell = document.createElement('div');
cell.style.flex = '1';
cell.style.textAlign = 'center';
cell.innerHTML = content;
return cell;
}
function addSortingButtons() {
const priceContainer = document.querySelector('#amazonPriceComparisonContainer');
if (!priceContainer) return;
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' }
];
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 => {
const btn = document.getElementById(p.id);
if (btn) {
btn.classList.remove('active-button');
btn.style.backgroundColor = '#f9f9f9';
}
});
button.classList.add('active-button');
button.style.backgroundColor = '#ff9900';
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';
container.style.flexDirection = 'column';
const checkboxElement = document.createElement('input');
checkboxElement.type = 'checkbox';
checkboxElement.id = checkbox.id;
checkboxElement.checked = checkbox.checked;
checkboxElement.style.marginBottom = '5px';
if (checkbox.disabled) checkboxElement.disabled = true;
checkboxElement.addEventListener('change', updateChartUrl);
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);
}
function 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 chartUrl = `https://charts.camelcamelcamel.com/${countryCode}/${asin}/amazon.png?force=1&zero=0&w=600&h=300&desired=false&legend=1&ilt=1&tp=all&fo=0&lang=en`;
const camelUrl = `https://${countryCode}.camelcamelcamel.com/product/${asin}`;
chartContainer.innerHTML = `
`;
priceContainer.appendChild(chartContainer);
const footer = document.createElement('div');
footer.style.cssText = 'text-align: right; font-size: 0.7em; color: #666; margin-top: 10px;';
footer.innerHTML = `
European Price Checker for Amazon by bNj v${GM_info.script.version}`;
priceContainer.appendChild(footer);
}
function updateChartUrl() {
const checkboxes = [
{ id: 'checkboxAmazon', filename: 'amazon' },
{ id: 'checkboxNew', filename: 'new' },
{ id: 'checkboxUsed', filename: 'used' }
];
const selectedFilenames = Array.from(document.querySelectorAll('input[type="checkbox"]:checked'))
.map(checkbox => {
const cb = checkboxes.find(cb => cb.id === checkbox.id);
return cb ? cb.filename : '';
})
.filter(filename => filename !== '')
.join('-');
const countryCode = window.location.hostname.includes('amazon.de') ? 'de' :
window.location.hostname.includes('amazon.es') ? 'es' :
window.location.hostname.includes('amazon.it') ? 'it' : 'fr';
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}`;
const chartContainer = document.querySelector('#amazonPriceComparisonContainer img[alt^="Price history chart"]');
if (chartContainer) {
chartContainer.parentElement.href = camelUrl;
chartContainer.src = chartUrl;
}
}
})();