Add price per gram and discount percentage to products
当前为
// ==UserScript==
// @name Albert Heijn Kortingspercentage en prijs per kilogram
// @namespace https://wol.ph/
// @version 1.0.0
// @description Add price per gram and discount percentage to products
// @author wolph
// @match https://www.ah.nl/*
// @icon https://icons.duckduckgo.com/ip2/ah.nl.ico
// @grant none
// @license BSD
// ==/UserScript==
(function() {
'use strict';
// Set to keep track of processed products
const processedProducts = new Set();
function updateProductInfo() {
console.log('Starting updateProductInfo');
// Handle normal product cards
let productCards = document.querySelectorAll('.product-card_root__4toFZ');
console.log(`Found ${productCards.length} product cards`);
productCards.forEach(function(productCard, index) {
console.log(`Processing product card ${index}`);
// Use a unique identifier for the product
let productId = productCard.getAttribute('data-product-id') || productCard.querySelector('a.link_root__EqRHd')?.getAttribute('href');
if (!productId) {
console.log('Product ID not found, skipping');
return;
}
if (processedProducts.has(productId)) {
console.log(`Product ${productId} already processed, skipping`);
return;
}
console.log(`Processing product ${productId}`);
// Extract price
let priceElement = productCard.querySelector('[data-testhook="price-amount"]');
if (!priceElement) {
console.log('Price element not found, skipping');
return;
}
let priceInt = priceElement.querySelector('.price-amount_integer__+e2XO');
let priceFrac = priceElement.querySelector('.price-amount_fractional__kjJ7u');
if (!priceInt || !priceFrac) {
console.log('Price integer or fractional part not found, skipping');
return;
}
let price = parseFloat(priceInt.textContent + '.' + priceFrac.textContent);
console.log(`Price: ${price}`);
// Extract unit size
let unitSizeElement = productCard.querySelector('[data-testhook="product-unit-size"]');
if (!unitSizeElement) {
console.log('Unit size element not found, skipping');
return;
}
let unitSizeText = unitSizeElement.textContent.trim(); // e.g., "300 g"
console.log(`Unit size text: ${unitSizeText}`);
// Parse unit size
let weightMatch = unitSizeText.match(/([\d.,]+)\s*(g|kg)/i);
if (!weightMatch) {
console.log('Weight not found in unit size text, skipping');
return;
}
let weight = parseFloat(weightMatch[1].replace(',', '.'));
let unit = weightMatch[2].toLowerCase();
console.log(`Weight: ${weight}, Unit: ${unit}`);
// Convert weight to grams
if (unit === 'kg') {
weight = weight * 1000;
console.log(`Converted weight to grams: ${weight}`);
}
// Calculate price per kg
let pricePerKg = (price * 1000) / weight; // Price per kg
console.log(`Price per kg: €${pricePerKg.toFixed(2)}`);
// Extract old price if available
let oldPrice = null;
// Assuming there's an element for the old price
let oldPriceElement = priceElement.querySelector('.price-amount_original__A6_Ck');
if (oldPriceElement) {
let oldPriceInt = oldPriceElement.querySelector('.price-amount_integer__+e2XO');
let oldPriceFrac = oldPriceElement.querySelector('.price-amount_fractional__kjJ7u');
if (oldPriceInt && oldPriceFrac) {
oldPrice = parseFloat(oldPriceInt.textContent + '.' + oldPriceFrac.textContent);
console.log(`Old price: ${oldPrice}`);
}
}
// Calculate discount percentage
let discountPercentage = null;
if (oldPrice) {
discountPercentage = ((oldPrice - price) / oldPrice) * 100;
discountPercentage = discountPercentage.toFixed(1);
console.log(`Calculated discount percentage: ${discountPercentage}%`);
} else {
// Try extracting from shield
let shieldElement = productCard.querySelector('[data-testhook="product-shield"]');
if (shieldElement) {
let shieldTextElement = shieldElement.querySelector('.shield_text__kNeiW');
if (shieldTextElement) {
let shieldText = shieldTextElement.textContent.trim();
console.log(`Shield text: ${shieldText}`);
discountPercentage = parsePromotionText(shieldText);
if (discountPercentage) {
console.log(`Extracted discount percentage from shield: ${discountPercentage}%`);
}
}
}
}
// If discount percentage is not found, set it to 0%
if (!discountPercentage) {
discountPercentage = 0;
console.log('Discount percentage not found, setting to 0%');
}
// Add or update shield element with discount percentage
let shieldElement = productCard.querySelector('[data-testhook="product-shield"]');
if (!shieldElement) {
// Create shield element
console.log('Creating shield element');
shieldElement = document.createElement('div');
shieldElement.className = 'shield_root__SmhpN';
shieldElement.setAttribute('data-testhook', 'product-shield');
let shieldTextElement = document.createElement('span');
shieldTextElement.className = 'shield_text__kNeiW';
shieldElement.appendChild(shieldTextElement);
let shieldContainer = productCard.querySelector('.product-card-portrait_shieldProperties__+JZJI');
if (!shieldContainer) {
// Create shield container
console.log('Creating shield container');
shieldContainer = document.createElement('div');
shieldContainer.className = 'product-card-portrait_shieldProperties__+JZJI';
let header = productCard.querySelector('.header_root__ilMls');
header.appendChild(shieldContainer);
}
shieldContainer.appendChild(shieldElement);
}
// Update shield text with discount percentage
let shieldTextElement = shieldElement.querySelector('.shield_text__kNeiW');
if (!shieldTextElement) {
shieldTextElement = document.createElement('span');
shieldTextElement.className = 'shield_text__kNeiW';
shieldElement.appendChild(shieldTextElement);
}
// Just set the text to the discount percentage
shieldTextElement.textContent = `${discountPercentage}%`;
// Set background and text color based on discount percentage
let { backgroundColor, textColor } = getDiscountColors(discountPercentage);
shieldElement.style.backgroundColor = backgroundColor;
shieldElement.style.color = textColor; // Ensure text is readable
// Modify price element to include price per kg in small letters next to discounted price and old price
let priceContainer = priceElement.parentElement; // Should be '.price_portrait__pcgwD'
if (priceContainer) {
// Create or update price per kg element
let pricePerKgElement = priceContainer.querySelector('.price-per-kg');
if (!pricePerKgElement) {
pricePerKgElement = document.createElement('div');
pricePerKgElement.className = 'price-per-kg';
pricePerKgElement.style.fontSize = 'smaller';
priceContainer.appendChild(pricePerKgElement);
}
pricePerKgElement.textContent = `€${pricePerKg.toFixed(2)} per kg`;
}
// Mark this product as processed
processedProducts.add(productId);
console.log(`Product ${productId} processed`);
});
// Handle promotion cards
let promotionCards = document.querySelectorAll('.promotion-card_root__ENX4w');
console.log(`Found ${promotionCards.length} promotion cards`);
promotionCards.forEach(function(promotionCard, index) {
console.log(`Processing promotion card ${index}`);
// Use a unique identifier for the promotion
let promotionId = promotionCard.getAttribute('id') || promotionCard.querySelector('a').getAttribute('href');
if (!promotionId) {
console.log('Promotion ID not found, skipping');
return;
}
if (processedProducts.has(promotionId)) {
console.log(`Promotion ${promotionId} already processed, skipping`);
return;
}
console.log(`Processing promotion ${promotionId}`);
// Extract current price and previous price
let priceElement = promotionCard.querySelector('[data-testhook="price"]');
if (!priceElement) {
console.log('Price element not found, skipping');
return;
}
let priceNow = parseFloat(priceElement.getAttribute('data-testpricenow'));
let priceWas = parseFloat(priceElement.getAttribute('data-testpricewas'));
console.log(`Price now: ${priceNow}, Price was: ${priceWas}`);
// Extract unit size, if available
let weight = null;
let unit = null;
// Try to extract from description and title
let cardDescription = promotionCard.querySelector('[data-testhook="card-description"]');
let cardTitle = promotionCard.querySelector('[data-testhook="card-title"]');
let descriptionText = cardDescription?.textContent.trim() || '';
let titleText = cardTitle?.textContent.trim() || '';
console.log(`Description text: ${descriptionText}`);
console.log(`Title text: ${titleText}`);
// Try to extract weight from description or title
let weightMatch = descriptionText.match(/([\d.,]+)\s*(g|kg)/i) || titleText.match(/([\d.,]+)\s*(g|kg)/i);
if (weightMatch) {
weight = parseFloat(weightMatch[1].replace(',', '.'));
unit = weightMatch[2].toLowerCase();
if (unit === 'kg') {
weight = weight * 1000;
}
console.log(`Weight: ${weight} grams`);
} else {
console.log('Weight not found in description or title');
}
// Calculate discount percentage
let discountPercentage = null;
if (priceWas && priceNow) {
discountPercentage = ((priceWas - priceNow) / priceWas) * 100;
discountPercentage = discountPercentage.toFixed(1);
console.log(`Calculated discount percentage: ${discountPercentage}%`);
} else {
// Try extracting from promotion shield
let promotionShields = promotionCard.querySelector('[data-testhook="promotion-shields"]');
if (promotionShields) {
let shieldText = promotionShields.textContent.trim();
console.log(`Promotion shield text: ${shieldText}`);
discountPercentage = parsePromotionText(shieldText);
if (discountPercentage) {
console.log(`Extracted discount percentage from shield: ${discountPercentage}%`);
}
}
}
if (!discountPercentage) {
discountPercentage = 0;
console.log('Discount percentage not found, setting to 0%');
}
// Add or update promotion shield with discount percentage
let promotionShields = promotionCard.querySelector('[data-testhook="promotion-shields"]');
if (!promotionShields) {
// Create promotion shields element
console.log('Creating promotion shields element');
promotionShields = document.createElement('div');
promotionShields.className = 'promotion-shields_root__cVEfN';
promotionShields.setAttribute('data-testhook', 'promotion-shields');
promotionShields.style = promotionCard.querySelector('.promotion-card-content_root__A5Fda')?.style || '';
let cardContent = promotionCard.querySelector('.promotion-card-content_root__A5Fda');
cardContent.insertBefore(promotionShields, cardContent.firstChild);
}
// Update promotion shield text with discount percentage
// Replace the entire element with discount percentage
promotionShields.innerHTML = ''; // Clear existing content
let shieldP = document.createElement('p');
shieldP.className = 'typography_root__Om3Wh typography_variant-paragraph__T5ZAU typography_hasMargin__4EaQi promotion-shield_root__mIDdK';
shieldP.setAttribute('data-testhook', 'promotion-shield');
let promotionShieldText = document.createElement('span');
promotionShieldText.className = 'promotion-text_root__1sn7K promotion-text_large__lTZOA';
promotionShieldText.setAttribute('data-testhook', 'promotion-text');
// Set the discount percentage as text
promotionShieldText.textContent = `${discountPercentage}%`;
// Set background and text color based on discount percentage
let { backgroundColor, textColor } = getDiscountColors(discountPercentage * 1.5);
shieldP.style.backgroundColor = backgroundColor;
shieldP.style.color = textColor; // Ensure text is readable
shieldP.appendChild(promotionShieldText);
promotionShields.appendChild(shieldP);
// If weight is available, calculate price per kg
if (weight) {
let pricePerKg = (priceNow * 1000) / weight;
console.log(`Price per kg: €${pricePerKg.toFixed(2)}`);
// Modify price element to include price per kg in small letters next to discounted price and old price
let priceContainer = priceElement; // Assuming this is the correct container
if (priceContainer) {
// Create or update price per kg element
let pricePerKgElement = priceContainer.querySelector('.price-per-kg');
if (!pricePerKgElement) {
pricePerKgElement = document.createElement('div');
pricePerKgElement.className = 'price-per-kg';
pricePerKgElement.style.fontSize = 'smaller';
pricePerKgElement.style.marginTop = '4px';
priceContainer.appendChild(pricePerKgElement);
}
pricePerKgElement.textContent = `€${pricePerKg.toFixed(2)} per kg`;
}
}
// Mark this promotion as processed
processedProducts.add(promotionId);
console.log(`Promotion ${promotionId} processed`);
});
console.log('Finished updateProductInfo');
}
function parsePromotionText(shieldText) {
let discountPercentage = null;
if (shieldText.includes('%')) {
// e.g., "25% korting"
let discountMatch = shieldText.match(/(\d+)%\s*korting/i);
if (discountMatch) {
discountPercentage = parseFloat(discountMatch[1]);
}
} else if (shieldText.toLowerCase().includes('gratis')) {
if (shieldText.includes('1+1') || shieldText.includes('1 + 1') || shieldText.includes('1+1 gratis')) {
discountPercentage = 50;
} else if (shieldText.includes('2+1') || shieldText.includes('2 + 1')) {
discountPercentage = 33.33;
}
// Add more cases as necessary
} else if (shieldText.includes('2e halve prijs')) {
discountPercentage = 25; // Assuming 25% off on 2 items
}
return discountPercentage;
}
function getDiscountColors(discountPercentage) {
// Use predefined colors and text colors based on discount percentage ranges
let backgroundColor, textColor;
if (discountPercentage >= 80) {
backgroundColor = '#008000'; // Dark Green
textColor = '#FFFFFF'; // White
} else if (discountPercentage >= 60) {
backgroundColor = '#32CD32'; // Lime Green
textColor = '#000000'; // Black
} else if (discountPercentage >= 40) {
backgroundColor = '#FFFF00'; // Yellow
textColor = '#000000'; // Black
} else if (discountPercentage >= 20) {
backgroundColor = '#FFA500'; // Orange
textColor = '#000000'; // Black
} else {
backgroundColor = '#FF0000'; // Red
textColor = '#FFFFFF'; // White
}
return { backgroundColor, textColor };
}
// Run immediately
window.setTimeout(updateProductInfo, 1000);
// Run the update function every 5 seconds
setInterval(updateProductInfo, 5000);
})();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址