// ==UserScript== // @name PickMe // @namespace http://tampermonkey.net/ // @version 3.6.3 // @description Plugin d'aide à la navigation pour les membres du discord Amazon Vine FR : https://discord.gg/amazonvinefr // @author Créateur/Codeur principal : MegaMan / Codeur secondaire : Sulff / Testeurs : Louise, JohnnyBGoody, L'avocat du Diable et Popato (+ du code de lelouch_di_britannia, FMaz008 et Thorvarium) // @match https://www.amazon.fr/vine/vine-items // @match https://www.amazon.fr/vine/vine-items?queue=* // @match https://www.amazon.fr/vine/vine-reviews* // @match https://www.amazon.fr/vine/orders* // @match https://www.amazon.fr/vine/account // @match https://www.amazon.fr/vine/resources // @match https://www.amazon.fr/gp/buy/thankyou* // @match https://www.amazon.fr/checkout* // @match https://www.amazon.fr/* // @match https://pickme.alwaysdata.net/* // @match https://vinepick.me/* // @match https://www.amazon.fr/vine/vine-items?search=* // @icon https://vinepick.me/img/PM-ICO-2.png // @updateURL https://raw.githubusercontent.com/teitong/pickme/main/PickMe.user.js // @downloadURL https://raw.githubusercontent.com/teitong/pickme/main/PickMe.user.js // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_registerMenuCommand // @grant GM_listValues // @run-at document-start // @noframes // @require https://vinepick.me/scripts/jquery-3.7.1.min.js // @require https://vinepick.me/scripts/heic2any.min.js //==/UserScript== /* NOTES: * Votre clé API est lié à votre compte Discord */ (function() { try { 'use strict'; //Pour éviter la multi exécution if (window.__PM__) { return; } window.__PM__ = true; initReviewRememberPM(); //On exclu les pages que gère RR, on laisse juste pour les pages if (!window.location.href.includes('orders') && !window.location.href.includes('vine-reviews')) { var apiOk = GM_getValue("apiToken", false); } const baseUrlPickme = "https://vinepick.me"; const hostnamePickMe = new URL(baseUrlPickme).hostname; let defautTab = GM_getValue('defautTab', 'AFA'); let checkoutRedirect = GM_getValue('checkoutRedirect', true); let checkoutButtonUp = GM_getValue('checkoutButtonUp', true); let mobileEnabled = GM_getValue("mobileEnabled", false); let ordersEnabled = GM_getValue('ordersEnabled', true); let headerEnabled = GM_getValue("headerEnabled", false); GM_setValue("defautTab", defautTab); GM_setValue("checkoutRedirect", checkoutRedirect); GM_setValue("checkoutButtonUp", checkoutButtonUp); GM_setValue("ordersEnabled", ordersEnabled); GM_setValue("mobileEnabled", mobileEnabled); GM_setValue("headerEnabled", headerEnabled); const lienVine = { 'RFY': 'https://www.amazon.fr/vine/vine-items?queue=potluck', 'AFA': 'https://www.amazon.fr/vine/vine-items?queue=last_chance', 'AI': 'https://www.amazon.fr/vine/vine-items?queue=encore', 'ALL': 'https://www.amazon.fr/vine/vine-items?queue=all_items', }; //On applique la suppression du header pour toutes les pages concernées par Vine const urlVine = window.location.href; const isAmazonTargetPage = [ /^https:\/\/www\.amazon\.fr\/vine\//, /^https:\/\/www\.amazon\.fr\/gp\/buy\/thankyou/, /^https:\/\/www\.amazon\.fr\/checkout/, /^https:\/\/www\.amazon\.fr\/review\/create-review/, /^https:\/\/www\.amazon\.fr\/review\/edit-review/ ].some(pattern => pattern.test(urlVine)); if (isAmazonTargetPage) { //Cacher le header ou non if (headerEnabled) { //Suppression header var styleHeader = document.createElement('style'); styleHeader.textContent = ` body { padding-right: 0px !important; } /* === Ancien header Amazon === */ #navbar-main, #nav-main, #skiplink, .amzn-ss-wrap { display: none !important; } /* === Nouveau header Amazon (2025) === */ #navbar-backup-backup, #navbar-mobile-bb, header#navbar-mobile-bb { display: none !important; } ` document.head.appendChild(styleHeader); } } //Page du passage de commande du nouveau checkout //Pour tester si le checkout provient bien d'une page vine const previousPage = document.referrer; const CHECKOUT_PAGE_PATTERN = /^https:\/\/www\.amazon\.fr\/checkout\/p\/p-/; //Page de checkout function checkOut(currentUrl) { const match_checkout = currentUrl.match(/\/checkout\/p\/p-(\d{3}-\d{7}-\d{7})/); if (apiOk && previousPage && ordersEnabled && match_checkout && previousPage.includes("vine-items")) { //On purge les anciennes commandes function purgeOldPurchaseData() { const now = Date.now(); const maxAge = 24 * 60 * 60 * 1000; //24 heures en millisecondes const stored = GM_getValue("purchaseData", {}); let modified = false; for (const purchaseId in stored) { const entry = stored[purchaseId]; if (!entry.timestamp || now - entry.timestamp > maxAge) { delete stored[purchaseId]; modified = true; } } if (modified) { GM_setValue("purchaseData", stored); } } purgeOldPurchaseData(); const purchaseId = match_checkout[1]; const asinCheckout = GM_getValue("asinCheckout", null); const asinParentCheckout = GM_getValue("asinParentCheckout", null); const queueCheckout = GM_getValue("queueCheckout", null); let stored = GM_getValue("purchaseData", {}); const now = Date.now(); //Timestamp stored[purchaseId] = { asin: asinCheckout, parent: asinParentCheckout, queue: queueCheckout, timestamp: now }; GM_setValue("purchaseData", stored); GM_deleteValue("asinCheckout"); GM_deleteValue("asinParentCheckout"); GM_deleteValue("queueCheckout"); } } //Détection d'un risque de frais de douane sur la page de checkout function initCustomsAlert() { if (!CHECKOUT_PAGE_PATTERN.test(window.location.href)) { return; } const ALERT_ID = 'pm-customs-alert'; const ALERT_STYLE_ID = `${ALERT_ID}-style`; function normalizeText(text) { if (!text) { return ''; } const lowerCase = text.toLowerCase(); const normalized = typeof lowerCase.normalize === 'function' ? lowerCase.normalize('NFD') : lowerCase; return normalized .replace(/[\u0300-\u036f]/g, '') .replace(/["'’`]/g, ' ') .replace(/[^a-z0-9]+/g, ' ') .trim(); } const CUSTOMS_WARNING_PHRASES = [ 'Cette commande contient un ou plusieurs articles vendus et expédiés depuis l’étranger.', 'frais d’importation', 'dédouaner le colis', 'expédition à l’international' ]; const NORMALIZED_CUSTOMS_WARNING_PHRASES = CUSTOMS_WARNING_PHRASES .map(phrase => normalizeText(phrase)) .filter(Boolean); function textContainsCustomsWarning(text) { const normalized = normalizeText(text); if (!normalized) { return false; } return NORMALIZED_CUSTOMS_WARNING_PHRASES.some(phrase => normalized.includes(phrase)); } function ensureAlertStyle() { if (document.getElementById(ALERT_STYLE_ID)) { return; } const style = document.createElement('style'); style.id = ALERT_STYLE_ID; style.textContent = ` #${ALERT_ID} { background-color: #fff4d6; border: 2px solid #f0a202; border-radius: 12px; color: #1f1f1f; padding: 12px 16px; margin: 16px 0; display: flex; align-items: center; gap: 12px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08); font-size: 14px; line-height: 1.5; } #${ALERT_ID} .pm-customs-alert__icon { font-size: 24px; } #${ALERT_ID} .pm-customs-alert__content { flex: 1 1 auto; } #${ALERT_ID} .pm-customs-alert__content strong { display: block; font-size: 16px; margin-bottom: 4px; } `; document.head.appendChild(style); } function injectAlert() { if (document.getElementById(ALERT_ID)) { return true; } const referenceContainer = document.querySelector('#a-page') || document.body; if (!referenceContainer) { return false; } ensureAlertStyle(); const alert = document.createElement('div'); alert.id = ALERT_ID; alert.innerHTML = ` ⚠️
Attention : frais de douane possibles Cette commande contient un article susceptible d’être expédié depuis l’étranger. Des droits ou taxes supplémentaires peuvent être réclamés à la livraison. Vérifiez bien le détail de votre commande avant de valider.
`; referenceContainer.prepend(alert); return true; } function detectCustomsWarning(textCandidate) { if (document.getElementById(ALERT_ID)) { return true; } if (textCandidate && textContainsCustomsWarning(textCandidate)) { return injectAlert(); } if (!textCandidate && document.body && textContainsCustomsWarning(document.body.textContent)) { return injectAlert(); } return false; } if (detectCustomsWarning()) { return; } const observerTarget = document.body || document.documentElement; if (!observerTarget) { return; } const observer = new MutationObserver(mutations => { for (const mutation of mutations) { if (mutation.type === 'childList') { for (const node of mutation.addedNodes) { const textContent = node && typeof node.textContent === 'string' ? node.textContent : ''; if (textContent && detectCustomsWarning(textContent)) { observer.disconnect(); return; } } } else if (mutation.type === 'characterData') { const targetText = mutation.target && typeof mutation.target.textContent === 'string' ? mutation.target.textContent : ''; if (targetText && detectCustomsWarning(targetText)) { observer.disconnect(); return; } } } if (detectCustomsWarning()) { observer.disconnect(); } }); observer.observe(observerTarget, { childList: true, subtree: true, characterData: true }); setTimeout(() => observer.disconnect(), 30000); } function initBalanceDueAlert() { if (!CHECKOUT_PAGE_PATTERN.test(window.location.href)) { return; } if (!previousPage || !previousPage.includes('vine-items')) { return; } const ALERT_ID = 'pm-balance-alert'; const STYLE_ID = `${ALERT_ID}-style`; const HIGHLIGHT_CONTAINER_CLASS = 'pm-balance-warning-container'; const DEFAULT_BALANCE_TITLE = 'Attention : reste à payer'; const DEFAULT_MESSAGE_TEMPLATE = 'Cette commande Vine comporte un reste à payer de {{amount}}. Assurez-vous de vouloir continuer avant de valider.'; const AMOUNT_PLACEHOLDER = '{{amount}}'; const TOTAL_BEFORE_SPECIAL_PAYMENTS = 'TOTAL_BEFORE_SPECIAL_PAYMENTS_TAX_INCLUSIVE'; const GIFT_CARD_BALANCE_TYPE = 'SPECIAL_PAYMENTS_GIFT_CARD_BALANCE'; function ensureStyle() { if (document.getElementById(STYLE_ID)) { return; } const style = document.createElement('style'); style.id = STYLE_ID; style.textContent = ` #${ALERT_ID} { background-color: #ffe8e6; border: 2px solid #cc1b1b; border-radius: 12px; color: #1f1f1f; padding: 12px 16px; margin: 16px 0; display: flex; align-items: center; gap: 12px; box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08); font-size: 14px; line-height: 1.5; } #${ALERT_ID} .pm-balance-alert__icon { font-size: 24px; } #${ALERT_ID} .pm-balance-alert__title { display: block; font-size: 16px; margin-bottom: 4px; } #${ALERT_ID} .pm-balance-alert__message { display: block; } #${ALERT_ID} .pm-balance-alert__note { display: block; margin-top: 4px; font-size: 13px; color: #1f1f1f; } #${ALERT_ID} .pm-balance-alert__amount { color: #b12704; font-weight: 700; } .${HIGHLIGHT_CONTAINER_CLASS} .pm-balance-warning-wrapper { display: inline-flex; align-items: center; gap: 8px; color: #b12704; font-weight: 700; } .${HIGHLIGHT_CONTAINER_CLASS} .pm-balance-warning-icon { font-size: 18px; } `; document.head.appendChild(style); } function parseEuroAmount(text) { if (!text) { return null; } let sanitized = text .replace(/\u00a0/g, '') .replace(/€/g, '') .replace(/\s/g, '') .trim(); if (!sanitized) { return null; } if (sanitized.includes(',')) { sanitized = sanitized.replace(/\./g, ''); sanitized = sanitized.replace(',', '.'); } const value = parseFloat(sanitized); if (!Number.isFinite(value)) { const fallback = sanitized.replace(/[^0-9.-]/g, ''); const fallbackValue = parseFloat(fallback); return Number.isFinite(fallbackValue) ? fallbackValue : null; } return value; } function findBalanceContainer() { const prioritizedContainer = document.querySelector('li.grand-total-cell .order-summary-line-definition'); if (prioritizedContainer) { const text = prioritizedContainer.textContent || ''; return { container: prioritizedContainer, amountText: text, amountValue: parseEuroAmount(text) }; } const labelCandidates = document.querySelectorAll('.order-summary-line-term .break-word'); for (const candidate of labelCandidates) { const labelText = candidate && candidate.textContent ? candidate.textContent : ''; if (!labelText || !/montant\s+total/i.test(labelText)) { continue; } const grid = candidate.closest('.order-summary-grid'); if (!grid) { continue; } const container = grid.querySelector('.order-summary-line-definition'); if (!container) { continue; } const text = container.textContent || ''; return { container, amountText: text, amountValue: parseEuroAmount(text) }; } return null; } function appendMessageWithAmount(node, template, normalizedAmount) { if (!node) { return; } const sanitizedTemplate = typeof template === 'string' && template.trim() ? template : DEFAULT_MESSAGE_TEMPLATE; if (sanitizedTemplate.includes(AMOUNT_PLACEHOLDER)) { const parts = sanitizedTemplate.split(AMOUNT_PLACEHOLDER); parts.forEach((part, index) => { if (part) { node.appendChild(document.createTextNode(part)); } if (index < parts.length - 1) { const amountSpan = document.createElement('span'); amountSpan.className = 'pm-balance-alert__amount'; amountSpan.textContent = normalizedAmount; node.appendChild(amountSpan); } }); } else { node.appendChild(document.createTextNode(sanitizedTemplate)); if (normalizedAmount) { node.appendChild(document.createTextNode(' ')); const amountSpan = document.createElement('span'); amountSpan.className = 'pm-balance-alert__amount'; amountSpan.textContent = normalizedAmount; node.appendChild(amountSpan); } } } function renderBannerContent(alert, normalizedAmount, options) { if (!alert) { return; } let content = alert.querySelector('.pm-balance-alert__content'); if (!content) { content = document.createElement('div'); content.className = 'pm-balance-alert__content'; alert.appendChild(content); } content.textContent = ''; const title = document.createElement('strong'); title.className = 'pm-balance-alert__title'; title.textContent = options.titleText || DEFAULT_BALANCE_TITLE; content.appendChild(title); const message = document.createElement('span'); message.className = 'pm-balance-alert__message'; appendMessageWithAmount(message, options.messageTemplate, normalizedAmount); content.appendChild(message); const notes = Array.isArray(options.extraNotes) ? options.extraNotes : []; for (const note of notes) { if (!note) { continue; } const noteElement = document.createElement('span'); noteElement.className = 'pm-balance-alert__note'; noteElement.textContent = note; content.appendChild(noteElement); } } function injectBanner(amountText, options = {}) { const normalizedAmount = (amountText || '').replace(/\u00a0/g, ' ').trim(); ensureStyle(); let alert = document.getElementById(ALERT_ID); if (!alert) { const referenceContainer = document.querySelector('#a-page') || document.body; if (!referenceContainer) { return; } alert = document.createElement('div'); alert.id = ALERT_ID; const icon = document.createElement('span'); icon.className = 'pm-balance-alert__icon'; icon.textContent = '⚠️'; alert.appendChild(icon); referenceContainer.prepend(alert); } renderBannerContent(alert, normalizedAmount, options); } function highlightAmount(container, amountText) { if (!container) { return; } ensureStyle(); container.classList.add(HIGHLIGHT_CONTAINER_CLASS); const normalized = (amountText || '').replace(/\u00a0/g, ' ').trim(); const existingWrapper = container.querySelector('.pm-balance-warning-wrapper'); if (existingWrapper) { const amountNode = existingWrapper.querySelector('.pm-balance-warning-amount'); if (amountNode) { amountNode.textContent = normalized; } return; } const wrapper = document.createElement('span'); wrapper.className = 'pm-balance-warning-wrapper'; const icon = document.createElement('span'); icon.className = 'pm-balance-warning-icon'; icon.textContent = '⚠️'; const amountHolder = document.createElement('span'); amountHolder.className = 'pm-balance-warning-amount'; amountHolder.textContent = normalized; wrapper.appendChild(icon); wrapper.appendChild(amountHolder); container.textContent = ''; container.appendChild(wrapper); } function getSubtotalInfoByType(type) { if (!type) { return null; } const input = document.querySelector(`input[name="subtotalLineType"][value="${type}"]`); if (!input) { return null; } const grid = input.closest('.order-summary-grid'); if (!grid) { return null; } const container = grid.querySelector('.order-summary-line-definition'); if (!container) { return null; } const text = container.textContent || ''; return { container, amountText: text, amountValue: parseEuroAmount(text) }; } function detectGiftCardDetails() { const totalBefore = getSubtotalInfoByType(TOTAL_BEFORE_SPECIAL_PAYMENTS); if (!totalBefore || totalBefore.amountValue === null || totalBefore.amountValue <= 0) { return null; } const giftCard = getSubtotalInfoByType(GIFT_CARD_BALANCE_TYPE); if (!giftCard || giftCard.amountValue === null || giftCard.amountValue >= 0) { return null; } return { totalBefore, giftCard }; } function removeBalanceWarning(info) { const alert = document.getElementById(ALERT_ID); const target = info && info.container ? info.container : null; const wasApplied = target && target.dataset.pmBalanceWarningApplied === 'true'; if (!alert && !wasApplied) { return; } if (alert && alert.parentElement) { alert.remove(); } if (!target || !wasApplied) { return; } target.classList.remove(HIGHLIGHT_CONTAINER_CLASS); const wrapper = target.querySelector('.pm-balance-warning-wrapper'); if (wrapper) { wrapper.remove(); } const normalized = (info.amountText || '').replace(/\u00a0/g, ' ').trim(); target.textContent = normalized; delete target.dataset.pmBalanceWarningApplied; delete target.dataset.pmBalanceAmount; delete target.dataset.pmBalanceMessageType; } function applyBalanceWarning(info, options = {}) { if (!info || !info.container) { return; } const displaySource = options.displayAmountText || info.amountText || ''; const normalizedDisplayAmount = (displaySource || '').replace(/\u00a0/g, ' ').trim(); injectBanner(normalizedDisplayAmount, options); const highlightTarget = options.highlightContainer || info.container; if (highlightTarget) { const highlightText = Object.prototype.hasOwnProperty.call(options, 'highlightAmountText') ? options.highlightAmountText : normalizedDisplayAmount; highlightAmount(highlightTarget, highlightText); } const datasetTarget = options.datasetTarget || info.container; if (datasetTarget) { datasetTarget.dataset.pmBalanceWarningApplied = 'true'; datasetTarget.dataset.pmBalanceAmount = normalizedDisplayAmount; datasetTarget.dataset.pmBalanceMessageType = options.messageType || 'default'; } } function processBalance() { const info = findBalanceContainer(); if (!info || !info.container) { return false; } if (info.amountValue === null) { removeBalanceWarning(info); return false; } const normalizedFinalAmount = (info.amountText || '').replace(/\u00a0/g, ' ').trim(); let giftCardDetails = null; let currentType = 'default'; let comparisonAmount = normalizedFinalAmount; if (info.amountValue <= 0) { giftCardDetails = detectGiftCardDetails(); if (giftCardDetails) { currentType = 'gift-card'; comparisonAmount = (giftCardDetails.totalBefore.amountText || '').replace(/\u00a0/g, ' ').trim(); } else { currentType = 'none'; } } const wasApplied = info.container.dataset.pmBalanceWarningApplied === 'true'; const previousAmount = info.container.dataset.pmBalanceAmount || ''; const previousType = info.container.dataset.pmBalanceMessageType || 'default'; if (currentType === 'none') { if (wasApplied) { removeBalanceWarning(info); } return false; } if (wasApplied && previousAmount === comparisonAmount && previousType === currentType) { return true; } if (wasApplied) { delete info.container.dataset.pmBalanceWarningApplied; } if (info.amountValue <= 0) { if (!giftCardDetails) { return false; } const extraNotes = []; const giftCardText = giftCardDetails.giftCard && giftCardDetails.giftCard.amountText ? giftCardDetails.giftCard.amountText.replace(/\u00a0/g, ' ').trim() : ''; if (giftCardText) { extraNotes.push(`Carte cadeau appliquée : ${giftCardText}.`); } applyBalanceWarning(info, { displayAmountText: giftCardDetails.totalBefore.amountText, highlightAmountText: info.amountText, messageTemplate: 'Cette commande Vine est payante ({{amount}}) mais le montant est couvert par une carte cadeau. Vérifiez que vous souhaitez l’utiliser avant de valider.', titleText: 'Attention : carte cadeau utilisée', extraNotes, messageType: 'gift-card' }); return true; } applyBalanceWarning(info, { messageType: 'default' }); return true; } if (processBalance()) { return; } const observerTarget = document.body || document.documentElement; if (!observerTarget) { return; } const observer = new MutationObserver(() => { if (processBalance()) { observer.disconnect(); } }); observer.observe(observerTarget, { childList: true, subtree: true, characterData: true }); setTimeout(() => observer.disconnect(), 30000); } function initCheckoutAlerts() { initCustomsAlert(); initBalanceDueAlert(); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initCheckoutAlerts); } else { initCheckoutAlerts(); } //Pour surveiller la page checkout contenant l'id de commande car c'est une redirection const targetPattern = /^https:\/\/www\.amazon\.fr\/checkout\/entry\/buynow\?pipelineType=Chewbacca$/; let previousUrl = location.href; if (previousPage.includes("vine-items") && targetPattern.test(previousUrl) && ordersEnabled) { const interval = setInterval(() => { const currentUrl = location.href; if (currentUrl !== previousUrl) { clearInterval(interval); //On arrête la surveillance console.log("[PïckMe] Changement d’URL détecté :", currentUrl); checkOut(currentUrl); } }, 100); //Vérifie toutes les 100 ms } //Page de commande validée if (apiOk && window.location.href.includes("/gp/buy/thankyou/handlers")) { if (ordersEnabled) { const purchaseId = new URLSearchParams(location.search).get('purchaseId'); let stored = GM_getValue("purchaseData", {}); const data = stored[purchaseId]; if (data) { delete stored[purchaseId]; GM_setValue("purchaseData", stored); let data_order = { version: GM_info.script.version, token: apiOk, parent_asin: data.parent, asin: data.asin, queue: data.queue, success: "success" }; const formData = new URLSearchParams(data_order); fetch(baseUrlPickme + "/shyrka/order", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: formData.toString() }); } } function moveVineButton() { if (checkoutRedirect) { const bouton = document.querySelector('#widget-continueShoppingEgress a.a-button-text'); //Vérifie qu'on l'a trouvé et que le texte contient "Vine" if (bouton && bouton.textContent.includes('Vine')) { const nouvelleURL = lienVine[defautTab]; if (nouvelleURL) { bouton.href = nouvelleURL; } } } if (checkoutButtonUp) { //Récupère le bouton source const sourceButtonContainer = document.querySelector('#widget-continueShoppingEgress'); //Vérifie si le bouton contient "Vine" if (!sourceButtonContainer) return; const text = sourceButtonContainer.textContent || ''; if (!text.includes('Vine')) return; //Clone le bouton const clone = sourceButtonContainer.cloneNode(true); //Cible la zone de destination const targetContainer = document.querySelector('#widget-accountLevelActions'); if (targetContainer && clone) { //Insère le clone juste après le conteneur cible targetContainer.insertAdjacentElement('afterend', clone); } } } //Attendre que le DOM soit prêt //Fix iPhone if (document.readyState !== 'loading') { moveVineButton(); } else { document.addEventListener('DOMContentLoaded', function () { moveVineButton(); }); } } //URL Vine const urlPattern = /^https:\/\/www\.amazon\.fr\/vine/; //Liste des URLs Vine const excludedPatterns = [ 'https://www.amazon.fr/vine/vine-items', 'https://www.amazon.fr/vine/vine-items?queue=*', 'https://www.amazon.fr/vine/vine-items?search=*', 'https://www.amazon.fr/vine/vine-reviews*', 'https://www.amazon.fr/vine/orders*', 'https://www.amazon.fr/vine/account', 'https://www.amazon.fr/vine/resources' ]; //Fonction pour extraire l'ASIN function getASINfromURL(url) { //Expression régulière pour trouver l'ASIN dans différentes structures d'URL Amazon const regex = /\/(dp|gp\/product|product-reviews|gp\/aw\/d)\/([A-Za-z0-9]{10})/i; const match = url.match(regex); return match ? match[2] : null; //Retourne l'ASIN ou null si non trouvé } function isAffiliateTagPresent() { return window.location.search.indexOf('tag=monsieurconso-21') > -1; } //Ajout du bouton function isElementVisible(element) { if (!element) { return false; } if (typeof element.offsetParent !== 'undefined') { if (element.offsetParent !== null) { return true; } } const rects = element.getClientRects(); return rects && rects.length > 0; } function findButtonPlacement() { const candidates = [ { selector: '#corePriceDisplay_desktop_feature_div', getPlacement: element => { const targetSection = element.querySelector('.a-section.a-spacing-none') || element; return { type: 'append', node: targetSection }; } }, { selector: '#corePriceDisplay_mobile_feature_div', getPlacement: element => ({ type: 'append', node: element }) }, { selector: '#buyboxAccordion .a-accordion-active .basisPriceLegalMessage', getPlacement: element => ({ type: 'after', node: element }) }, { selector: '.basisPriceLegalMessage', getPlacement: element => ({ type: 'after', node: element }) }, { selector: '#buyboxAccordion .a-accordion-active .priceToPay', getPlacement: element => { const parentSection = element.closest('.a-section'); if (parentSection && isElementVisible(parentSection)) { return { type: 'append', node: parentSection }; } return null; } }, { selector: '#corePrice_desktop .a-span12', getPlacement: element => { const parent = element.parentNode || element; return { type: 'append', node: parent }; } }, { selector: '#corePrice_mobile_feature_div', getPlacement: element => ({ type: 'append', node: element }) }, { selector: '#bookDescription_feature_div', getPlacement: element => ({ type: 'before', node: element }) } ]; for (const candidate of candidates) { const elements = Array.from(document.querySelectorAll(candidate.selector)); for (const element of elements) { if (!isElementVisible(element)) { continue; } const placement = candidate.getPlacement(element); if (placement) { return placement; } } } return null; } function updateButtonLink(asin) { const affiliateAnchor = document.querySelector('#pickme-button'); if (affiliateAnchor && !isAffiliateTagPresent()) { affiliateAnchor.href = baseUrlPickme + `/monsieurconso/product.php?asin=${asin}`; } } function insertButtonContainer(container, placement) { if (!placement || !placement.node) { return; } if (placement.type === 'after') { const parentNode = placement.node.parentNode; if (!parentNode) { return; } if (container.parentNode !== parentNode || container.previousSibling !== placement.node) { parentNode.insertBefore(container, placement.node.nextSibling); } } else if (placement.type === 'append') { if (container.parentNode !== placement.node) { placement.node.appendChild(container); } else if (container !== placement.node.lastElementChild) { placement.node.appendChild(container); } } else if (placement.type === 'before') { const parentNode = placement.node.parentNode; if (!parentNode) { return; } if (container.parentNode !== parentNode || container.nextSibling !== placement.node) { parentNode.insertBefore(container, placement.node); } } } function addButton(asin) { const placement = findButtonPlacement(); if (!placement) { return; } let buttonContainer = document.querySelector('#pickme-button-container'); if (!buttonContainer) { buttonContainer = createButton(asin); } else { updateButtonLink(asin); } insertButtonContainer(buttonContainer, placement); } function submitPost(asin) { var form = document.createElement('form'); form.method = 'POST'; form.action = baseUrlPickme + '/monsieurconso/top.php'; form.target = '_blank'; var asinField = document.createElement('input'); asinField.type = 'hidden'; asinField.name = 'asin'; asinField.value = asin; form.appendChild(asinField); document.body.appendChild(form); form.submit(); } function createButton(asin) { var container = document.createElement('div'); //Créer un conteneur pour le bouton et le texte d'explication container.id = 'pickme-button-container'; container.style.display = 'inline-flex'; container.style.alignItems = 'center'; var affiliateButton = document.createElement('a'); affiliateButton.className = 'a-button a-button-primary a-button-small'; affiliateButton.id = 'pickme-button'; affiliateButton.style.marginTop = '5px'; //Pour ajouter un peu d'espace au-dessus du bouton affiliateButton.style.marginBottom = '5px'; affiliateButton.style.color = 'white'; //Changez la couleur du texte en noir //affiliateButton.style.maxWidth = '200px'; affiliateButton.style.height = '29px'; affiliateButton.style.lineHeight = '29px'; affiliateButton.style.borderRadius = '20px'; affiliateButton.style.whiteSpace = 'nowrap'; affiliateButton.style.padding = '0 40px'; affiliateButton.style.backgroundColor = '#CC0033'; affiliateButton.style.border = '1px solid white'; affiliateButton.style.display = 'inline-block'; if (isAffiliateTagPresent()) { affiliateButton.innerText = 'Lien PickMe actif'; affiliateButton.style.backgroundColor = 'green'; //Changez la couleur de fond en vert affiliateButton.style.color = 'white'; affiliateButton.style.pointerEvents = 'none'; //Empêchez tout événement de clic affiliateButton.style.cursor = 'default'; affiliateButton.style.border = '1px solid black'; container.appendChild(affiliateButton); //Ajouter le bouton et le texte d'explication au conteneur } else { /*affiliateButton.onclick = function() { submitPost(asin); };*/ affiliateButton.href = baseUrlPickme + `/monsieurconso/product.php?asin=${asin}`; affiliateButton.innerText = 'Acheter via PickMe'; affiliateButton.target = '_blank'; var infoText = document.createElement('span'); //Créer l'élément de texte d'explication infoText.innerHTML = 'A quoi sert ce bouton ?'; infoText.style.marginLeft = '5px'; infoText.style.color = '#CC0033'; infoText.style.cursor = 'pointer'; infoText.style.fontSize = '14px'; infoText.onclick = function() { alert("Ce bouton permet de soutenir le discord Amazon Vine FR. Il n'y a strictement aucune conséquence sur votre achat, mise à part d'aider à maintenir les services du discord et de PickMe.\nVous pourrez réclamer votre achat sur PickMe Web d'ici 24h afin d'augmenter votre score d'activité et éviter d'être AFK.\n\nComment faire ?\n\nIl suffit de cliquer sur 'Acheter via PickMe' et dans la nouvelle fenêtre de cliquer sur 'Acheter sur Amazon'. Normalement le bouton sera devenu vert, il suffit alors d'ajouter le produit au panier (uniquement quand le bouton est vert) et c'est tout !\nMerci beaucoup !"); }; container.appendChild(affiliateButton); container.appendChild(infoText); } affiliateButton.style.fontSize = '14px'; return container; //Retourner le conteneur au lieu du bouton seul } //Détermine si on ajoute l'onglet Notifications var pageProduit = false; var asinProduct = getASINfromURL(window.location.href); function asinReady() { if (asinProduct) { pageProduit = true; addButton(asinProduct); const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.type === 'childList' && mutation.addedNodes.length > 0) { asinProduct = getASINfromURL(window.location.href); addButton(asinProduct); } }); }); observer.observe(document.body, { childList: true, subtree: true }); return; } } //Fix iPhone if (document.readyState !== 'loading') { asinReady(); } else { document.addEventListener('DOMContentLoaded', function () { asinReady(); }); } //Notif //On initialise les variables utiles pour cette partie du script let notifEnabled = GM_getValue("notifEnabled", false); let onMobile = GM_getValue("onMobile", false); let shortcutNotif = GM_getValue("shortcutNotif", false); let callUrl = GM_getValue("callUrl", ""); var apiKey = GM_getValue("apiToken", false); let notifUp = GM_getValue('notifUp', true); let notifRecos = GM_getValue('notifRecos', false); let notifRFY = GM_getValue('notifRFY', false); let notifPartageAFA = GM_getValue('notifPartageAFA', true); let notifPartageAI = GM_getValue('notifPartageAI', false); let notifPartageALL = GM_getValue('notifPartageALL', true); let notifAutres = GM_getValue('notifAutres', true); let notifSound = GM_getValue('notifSound', true); let notifFav = GM_getValue('notifFav', false); let favWords = GM_getValue('favWords', ''); let hideWords = GM_getValue('hideWords', ''); let filterOption = GM_getValue('filterOption', 'notifFavOnly'); let hideEnabled = GM_getValue("hideEnabled", true); let savedTheme = GM_getValue('selectedTheme', 'default'); let notifUrl = GM_getValue('notifUrl', baseUrlPickme + '/sw/notif3.mp3'); let favUrlOn = GM_getValue('favUrlOn', baseUrlPickme + "/img/coeurrouge2.png"); let favUrlOff = GM_getValue('favUrlOff', baseUrlPickme + "/img/coeurgris2.png"); let hideUrlOn = GM_getValue('hideUrlOn', baseUrlPickme + "/img/eye.png"); let hideUrlOff = GM_getValue('hideUrlOff', baseUrlPickme + "/img/eyehidden.png"); let hidePageNavigateEnabled = GM_getValue('hidePageNavigateEnabled', true); let hidePagePreviousEnabled = GM_getValue('hidePagePreviousEnabled', false); let NSFWEnabled = GM_getValue('NSFWEnabled', false); let blurLevel = GM_getValue('blurLevel', '15'); let NSFWHide = GM_getValue('NSFWHide', false); let notepadEnabled = GM_getValue('notepadEnabled', true); let notifVolumeEnabled = GM_getValue('notifVolumeEnabled', false); let notifVolume = GM_getValue('notifVolume', "1"); GM_setValue("notifEnabled", notifEnabled); GM_setValue("onMobile", onMobile); GM_setValue("shortcutNotif", shortcutNotif); GM_setValue("callUrl", callUrl); GM_setValue("notifUp", notifUp); GM_setValue("notifRecos", notifRecos); GM_setValue("notifRFY", notifRFY); GM_setValue("notifPartageAFA", notifPartageAFA); GM_setValue("notifPartageAI", notifPartageAI); GM_setValue("notifPartageALL", notifPartageALL); GM_setValue("notifAutres", notifAutres); GM_setValue("notifSound", notifSound); GM_setValue("notifFav", notifFav); GM_setValue("favWords", favWords); GM_setValue("hideWords", hideWords); GM_setValue("filterOption", filterOption); GM_setValue("hideEnabled", hideEnabled); GM_setValue("selectedTheme", savedTheme); GM_setValue("notifUrl", notifUrl); GM_setValue("favUrlOn", favUrlOn); GM_setValue("favUrlOff", favUrlOff); GM_setValue("hideUrlOn", hideUrlOn); GM_setValue("hideUrlOff", hideUrlOff); GM_setValue("hidePageNavigateEnabled", hidePageNavigateEnabled); GM_setValue("hidePagePreviousEnabled", hidePagePreviousEnabled); GM_setValue("NSFWEnabled", NSFWEnabled); GM_setValue("blurLevel", blurLevel); GM_setValue("NSFWHide", NSFWHide); GM_setValue("notepadEnabled", notepadEnabled); GM_setValue("notifVolumeEnabled", notifVolumeEnabled); GM_setValue("notifVolume", notifVolume); //Convertir la date SQL en date lisible européenne function convertToEuropeanDate(mysqlDate) { if (!mysqlDate) return ''; const date = new Date(mysqlDate); const day = ('0' + date.getDate()).slice(-2); const month = ('0' + (date.getMonth() + 1)).slice(-2); //Les mois commencent à 0 en JavaScript const year = date.getFullYear(); const hours = ('0' + date.getHours()).slice(-2); const minutes = ('0' + date.getMinutes()).slice(-2); const seconds = ('0' + date.getSeconds()).slice(-2); return `${day}/${month}/${year} ${hours}:${minutes}:${seconds}`; } //Récupérer les infos d'un produit dans l'API function infoProduct(asin) { const formData = new URLSearchParams({ version: GM_info.script.version, token: apiKey, asin: asin, }); return fetch(baseUrlPickme + "/shyrka/infoasin", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: formData.toString() }) .then(response => { if (response.status === 200) { return response.json().then(data => { const { date_last, title, linkText, linkUrl, main_image } = data; const date_last_eu = convertToEuropeanDate(date_last); return { date_last_eu, title, linkText, linkUrl, main_image }; }).catch(error => { console.error("Erreur lors de l'analyse de la réponse JSON:", error); throw new Error("Erreur lors de l'analyse de la réponse JSON"); }); } else if (response.status === 201) { return response.text(); } else { console.error("Erreur HTTP:", response.status, response.statusText); throw new Error(`Erreur HTTP: ${response.status} ${response.statusText}`); } }) .catch(error => { console.error("Erreur de requête:", error); throw error; }); } //Fonction pour demander la permission et afficher la notification function requestNotification(title, text, icon, queue = null, page = null, pn = null, cn = null) { if (!("Notification" in window)) { console.log("[PïckMe] Ce navigateur ne supporte pas les notifications de bureau."); return; } if (Notification.permission === "granted") { if (onMobile) { navigator.serviceWorker.getRegistration().then(function(reg) { if (reg) { reg.showNotification(title, { body: text || "", icon: icon, data: { queue: queue, page : page, cn : cn, pn : pn } }); } }); } else { showNotification(title, text, icon, queue, page, pn, cn); } soundNotif(); } else if (Notification.permission !== "denied") { Notification.requestPermission().then(permission => { if (permission === "granted") { if (onMobile) { navigator.serviceWorker.getRegistration().then(function(reg) { if (reg) { reg.showNotification(title, { body: text || "", icon: icon, data: { queue: queue, page : page, cn : cn, pn : pn } }); } }); } else { showNotification(title, text, icon, queue, page, pn, cn); } soundNotif(); } }); } } function playSound(url) { const audio = new Audio(url); if (notifVolumeEnabled) { const volume = Math.max(0, Math.min(1, notifVolume)); audio.volume = volume; } audio.play().catch((err) => { console.warn('Erreur lors de la lecture du son :', err); }); } function soundNotif() { if (notifSound) { var sound = new Audio(notifUrl); if (notifVolumeEnabled) { const volume = Math.max(0, Math.min(1, notifVolume)); sound.volume = volume; } sound.play().catch((err) => { console.warn('Erreur lors de la lecture du son :', err); }); } } //Fonction pour afficher la notification sur PC function showNotification(title, text, icon, queue = null, page = null, pn = null, cn = null) { var notification = new Notification(title, { body: text || "", icon: icon }); notification.onclick = function () { window.focus(); //Focus le navigateur quand on clique sur la notification var baseUrl = "https://www.amazon.fr/vine/vine-items"; var url = baseUrl; //Initialisation de l'URL de base //Déterminer l'URL en fonction de la queue if (queue === "0") { url = baseUrl + "?queue=last_chance"; if (page) url += "&page=" + encodeURIComponent(page); } else if (queue === "1") { url = baseUrl + "?queue=encore"; if (pn) url += "&pn=" + encodeURIComponent(pn); if (cn) url += "&cn=" + encodeURIComponent(cn); if (page) url += "&page=" + encodeURIComponent(page); } else if (queue === "2") { url = baseUrl + "?queue=potluck"; } else if (queue === "3") { url = baseUrl + "?queue=all_items"; if (pn) url += "&pn=" + encodeURIComponent(pn); if (cn) url += "&cn=" + encodeURIComponent(cn); if (page) url += "&page=" + encodeURIComponent(page); } else { url = baseUrl + "?queue=encore" + (queue ? "&pn=" + queue : "") + (page ? "&cn=&page=" + page : ""); } //Ouvrir l'URL dans un nouvel onglet window.open(url, '_blank'); }; } //Ecoute des messages entrants if (notifEnabled && apiKey) { var lastNotifId = null; const NOTIF_LEADER_KEY = 'pmNotifLeader'; const NOTIF_LEADER_TTL = 15000; //15 secondes const currentNotifTabId = `${Date.now()}-${Math.random().toString(16).slice(2)}`; let isNotifLeader = false; let notifIframeInitialized = false; let notifLeaderHeartbeat = null; let notifLeaderCheckInterval = null; function parseNotifLeader(rawValue) { try { return rawValue ? JSON.parse(rawValue) : null; } catch (e) { return null; } } function getNotifLeader() { return parseNotifLeader(localStorage.getItem(NOTIF_LEADER_KEY)); } function setNotifLeader(id) { localStorage.setItem(NOTIF_LEADER_KEY, JSON.stringify({ id: id, timestamp: Date.now() })); } function isLeaderEntryStale(entry) { return !entry || (Date.now() - entry.timestamp > NOTIF_LEADER_TTL); } function stopNotifLeadership() { isNotifLeader = false; if (notifLeaderHeartbeat) { clearInterval(notifLeaderHeartbeat); notifLeaderHeartbeat = null; } } function ensureNotifIframe() { if (notifIframeInitialized) { return; } notifIframeInitialized = true; function addNotifIframeAndTab() { if (window.location.hostname !== "pickme.alwaysdata.net" || window.location.hostname !== hostnamePickMe) { //Initialisation de l'iframe seulement si on est sur le bon domaine var iframe = document.createElement('iframe'); iframe.style.display = 'none'; //Rendre l'iframe invisible iframe.src = baseUrlPickme + "/sw/websocket.php?key=" + encodeURIComponent(apiKey); document.body.appendChild(iframe); } else { document.cookie = "pm_apiKey=" + encodeURIComponent(apiKey) + "; path=/; secure"; } } if (document.readyState !== 'loading') { addNotifIframeAndTab(); } else { document.addEventListener('DOMContentLoaded', function () { addNotifIframeAndTab() }); } } function startNotifLeadership() { if (isNotifLeader) { return; } isNotifLeader = true; ensureNotifIframe(); notifLeaderHeartbeat = setInterval(function () { var leader = getNotifLeader(); if (leader && leader.id !== currentNotifTabId) { stopNotifLeadership(); return; } setNotifLeader(currentNotifTabId); }, 5000); } function tryBecomeNotifLeader() { var leader = getNotifLeader(); if (!leader || leader.id === currentNotifTabId || isLeaderEntryStale(leader)) { setNotifLeader(currentNotifTabId); startNotifLeadership(); } } function startNotifLeaderWatchdog() { if (notifLeaderCheckInterval) { return; } notifLeaderCheckInterval = setInterval(function () { var leader = getNotifLeader(); if (!leader || isLeaderEntryStale(leader)) { tryBecomeNotifLeader(); } }, 4000); } window.addEventListener('storage', function(event) { if (event.key === NOTIF_LEADER_KEY) { var leader = parseNotifLeader(event.newValue); if (!leader || isLeaderEntryStale(leader)) { tryBecomeNotifLeader(); } else if (leader.id !== currentNotifTabId) { stopNotifLeadership(); } } }); window.addEventListener('visibilitychange', function() { if (!document.hidden && !isNotifLeader) { tryBecomeNotifLeader(); } }); window.addEventListener('beforeunload', function() { var leader = getNotifLeader(); if (leader && leader.id === currentNotifTabId) { localStorage.removeItem(NOTIF_LEADER_KEY); } if (notifLeaderCheckInterval) { clearInterval(notifLeaderCheckInterval); notifLeaderCheckInterval = null; } stopNotifLeadership(); }); tryBecomeNotifLeader(); startNotifLeaderWatchdog(); if (notifFav) { var titleContentLower; if (filterOption == "notifFavOnly") { var favWordsTrimNotif = favWords.trim(); var favArrayNotif = favWordsTrimNotif.length > 0 ? favWordsTrimNotif.split(',').map(pattern => { pattern = pattern.trim(); if (pattern.length > 0) { try { return new RegExp(pattern, 'i'); } catch (e) { console.error('Expression regex invalide :', pattern, e); return null; } } else { return null; } }).filter(regex => regex != null) : []; } else if (filterOption == "notifExcludeHidden") { var hiddenWordsTrimNotif = hideWords.trim(); var hiddenArrayNotif = hiddenWordsTrimNotif.length > 0 ? hiddenWordsTrimNotif.split(',').map(pattern => { pattern = pattern.trim(); if (pattern.length > 0) { try { return new RegExp(pattern, 'i'); } catch (e) { console.error('Expression regex invalide :', pattern, e); return null; } } else { return null; } }).filter(regex => regex != null) : []; } } //Écouter les messages immédiatement window.addEventListener('message', function(event) { //console.log("PickMe :", event); if (!isNotifLeader) { return; } lastNotifId = GM_getValue('lastNotifId', null); if (event.data.type === 'NEW_MESSAGE' && (event.origin == "https://pickme.alwaysdata.net" || event.origin == baseUrlPickme) && event.data.id != lastNotifId) { lastNotifId = event.data.id; GM_setValue('lastNotifId', lastNotifId); if ((event.data.info.toUpperCase() === "UP" && notifUp) || (event.data.info.toUpperCase() === "RECO" && notifRecos) || (event.data.info.toUpperCase() === "PRODUCT_AFA" && notifPartageAFA) || (event.data.info.toUpperCase() === "PRODUCT_AI" && notifPartageAI) || (event.data.info.toUpperCase() === "PRODUCT_ALL" && notifPartageALL) || (event.data.info.toUpperCase() === "PRODUCT_RFY" && notifRFY) || (event.data.info.toUpperCase() === "AUTRES" && notifAutres)) { if (notifFav && (event.data.info.toUpperCase() === "PRODUCT_AI" || event.data.info.toUpperCase() === "PRODUCT_ALL")) { titleContentLower = event.data.description.toLowerCase().trim().replace(/\s+/g, ''); if (filterOption == "notifFavOnly") { if (favArrayNotif.length > 0 && favArrayNotif.some(regex => regex.test(titleContentLower))) { requestNotification(event.data.title, event.data.description, event.data.imageUrl, event.data.queue, event.data.page, event.data.pn, event.data.cn); } } else if (filterOption == "notifExcludeHidden") { if (hiddenArrayNotif.length > 0 && !hiddenArrayNotif.some(regex => regex.test(titleContentLower))) { requestNotification(event.data.title, event.data.description, event.data.imageUrl, event.data.queue, event.data.page, event.data.pn, event.data.cn); } } } else { requestNotification(event.data.title, event.data.description, event.data.imageUrl, event.data.queue, event.data.page, event.data.pn, event.data.cn); } } } }); function addNotifShortcutTab() { if (shortcutNotif && !pageProduit && window.location.href.indexOf("vine") !== -1) { //Sélectionner le conteneur des onglets var tabsContainer = document.querySelector('.a-tabs'); //Créer le nouvel onglet pour Notifications var newTab1 = document.createElement('li'); newTab1.className = 'a-tab-heading'; newTab1.role = 'presentation'; //Créer le lien à ajouter dans le nouvel onglet Notifications var link1 = document.createElement('a'); link1.href = baseUrlPickme + "/sw/notification.php?key=" + encodeURIComponent(apiKey); link1.role = 'tab'; link1.setAttribute('aria-selected', 'false'); link1.tabIndex = -1; link1.textContent = 'Notifications'; link1.target = '_blank'; link1.style.color = '#f8a103'; link1.style.backgroundColor = 'transparent'; link1.style.border = 'none'; //Ajouter le lien au nouvel onglet Notifications newTab1.appendChild(link1); //Ajouter les nouveaux onglets au conteneur des onglets if (tabsContainer) { tabsContainer.appendChild(newTab1); } } } //Fix iPhone if (document.readyState !== 'loading') { addNotifShortcutTab(); } else { document.addEventListener('DOMContentLoaded', function () { addNotifShortcutTab() }); } } //Fonction pour charger le fichier CSS function loadCSS(url) { var link = document.createElement('link'); link.rel = 'stylesheet'; link.type = 'text/css'; link.href = url; document.getElementsByTagName('head')[0].appendChild(link); } //URL des CSS var baseURLCSS = baseUrlPickme + "/"; //Gestion des favoris sur PickMe Web if ((window.location.hostname === "pickme.alwaysdata.net" || window.location.hostname === hostnamePickMe) && /^\/[^\/]+\.php$/.test(window.location.pathname)) { document.addEventListener('click', function(event) { //Vérifier si l'élément cliqué a la classe 'favori-icon' if (event.target.classList.contains('favori-icon')) { //let dataId = event.target.getAttribute('data-id'); let dataFavori = event.target.getAttribute('data-favori'); let dataAsin = event.target.getAttribute('data-asin'); if (dataFavori == 1) { GM_setValue(dataAsin +'_f', '1'); } else if (dataFavori == 0) { GM_deleteValue(dataAsin + '_f'); } } }); //Auto log si on a pickme installé //On check s'il y a la zone de saisie de la clé API const apiKeyInput = document.querySelector('input[type="text"].form-control#api_key[name="api_key"][required]'); //Vérifie si le message d'erreur n'est PAS présent const errorAlert = document.querySelector('div.alert.alert-danger'); //Récupère le dernier moment de redirection enregistré pour éviter de le faire en boucle const lastRedirect = localStorage.getItem('lastRedirectTime'); const now = Date.now(); //On le fait seulement s'il y a le champ de saisie, mais sans le message d'erreur et si pas fait depuis plus de 1 minute if (apiKeyInput && !errorAlert && (!lastRedirect || now - lastRedirect > 60000)) { if (apiKey) { localStorage.setItem('lastRedirectTime', now); const redirectUrl = baseUrlPickme + "/search.php?key=" + encodeURIComponent(apiKey); window.location.href = redirectUrl; } } } //Popup pour le bloc-notes function setNote() { //Vérifie si une popup existe déjà et la supprime si c'est le cas const existingPopup = document.getElementById('notePopup'); if (existingPopup) { existingPopup.remove(); } //Crée la fenêtre popup const popup = document.createElement('div'); popup.id = "notePopup"; popup.style.cssText = ` position: fixed; z-index: 10002; left: 50%; top: 50%; transform: translate(-50%, -50%); padding: 20px; background-color: white; border: 1px solid #ccc; box-shadow: 0px 0px 10px #ccc; `; popup.innerHTML = `

Bloc-notes

`; document.body.appendChild(popup); //Ajoute des écouteurs d'événement pour les boutons document.getElementById('saveNote').addEventListener('click', function() { const noteContent = document.getElementById('noteTextArea').value; //Stocker le contenu de la note avec GM_setValue GM_setValue("noteContent", noteContent); popup.remove(); }); document.getElementById('closeNote').addEventListener('click', function() { popup.remove(); }); document.getElementById('closeNotePopup').addEventListener('click', function() { popup.remove(); }); //Charger la note existante si elle est stockée avec GM_getValue const savedNoteContent = GM_getValue("noteContent", ""); if (savedNoteContent) { document.getElementById('noteTextArea').value = savedNoteContent; } //Ajoute la fonctionnalité de déplacement const header = document.getElementById('configPopupHeader'); let isDragging = false; let offsetX, offsetY; header.addEventListener('mousedown', function(e) { isDragging = true; header.style.cursor = 'grabbing'; offsetX = e.clientX - popup.getBoundingClientRect().left; offsetY = e.clientY - popup.getBoundingClientRect().top; document.addEventListener('mousemove', movePopup); document.addEventListener('mouseup', stopDragging); }); function movePopup(e) { if (isDragging) { popup.style.left = `${e.clientX - offsetX}px`; popup.style.top = `${e.clientY - offsetY}px`; popup.style.transform = `translate(0, 0)`; } } function stopDragging() { isDragging = false; header.style.cursor = 'grab'; document.removeEventListener('mousemove', movePopup); document.removeEventListener('mouseup', stopDragging); } } function getProductAsin(produit) { return produit.getAttribute("data-asin") || ( produit.querySelector(".vvp-details-btn input") || produit.querySelector(".vvp-details-btn-mobile input") )?.getAttribute("data-asin"); } function getStringDetailsBtnSelector() { const isMobile = document.querySelector('.vvp-details-btn-mobile') !== null; return isMobile ? 'vvp-details-btn-mobile' : 'vvp-details-btn'; } //Pour savoir si on a la version mobile du site ou non function isMobile() { return document.documentElement.classList.contains('a-mobile'); } function ensureMobileTabsContainer() { var container = document.querySelector(".a-tabs"); if (!container) { var parent = document.querySelector("#a-page > div.a-container.vvp-body > div.a-tab-container.vvp-tab-set-container"); if (parent) { container = document.createElement("ul"); container.className = "a-tabs"; container.id = "pickme-mobile-tabs"; parent.insertBefore(container, parent.firstChild); } } return container; } function addHomeTab() { if (isMobile() && (window.location.href.indexOf("vine-items") !== -1 || window.location.href.indexOf("vine-reviews") !== -1 || window.location.href.indexOf("orders") !== -1 || window.location.href.indexOf("account") !== -1)) { const tabsContainer = document.querySelector(".a-tabs") || ensureMobileTabsContainer(); if (!tabsContainer) return; const queueLink = lienVine[defautTab] || lienVine["AFA"]; //Onglet Articles const homeTab = document.createElement('li'); homeTab.className = 'a-tab-heading'; homeTab.innerHTML = `Articles`; tabsContainer.insertBefore(homeTab, tabsContainer.firstChild); const defaultLink = document.querySelector('#vvp-vine-items-tab a'); document.getElementById('accueilTab').addEventListener('click', function(e) { document.querySelectorAll('.a-tab-heading').forEach(tab => { tab.classList.remove('a-active'); }); this.parentElement.classList.add('a-tab-heading', 'a-active'); this.setAttribute('aria-selected', 'true'); //Réafficher les contenus des onglets Amazon cachés lors du passage sur "Favoris" document.querySelectorAll('.a-box-tab').forEach(box => { box.style.display = ''; }); const favContainer = document.getElementById('favorisContainer'); if (favContainer) { favContainer.style.display = 'none'; } if (defaultLink) { e.preventDefault(); defaultLink.click(); } }); } } //Affichage de l'onglet "Favoris" function addTab() { if (!pageProduit && window.location.href.indexOf("vine") !== -1 && apiKey) { //Sélectionner le conteneur des onglets var tabsContainer = document.querySelector(".a-tabs"); if (!tabsContainer) { tabsContainer = ensureMobileTabsContainer(); addHomeTab(); } //Créer le nouvel onglet pour Pickme Web var newTab2 = document.createElement('li'); newTab2.className = 'a-tab-heading'; newTab2.role = 'presentation'; //Créer le lien à ajouter dans le nouvel onglet Pickme Web var link2 = document.createElement('a'); link2.href = baseUrlPickme + "/account.php?key=" + encodeURIComponent(apiKey); link2.role = 'tab'; link2.setAttribute('aria-selected', 'false'); link2.tabIndex = -1; link2.textContent = 'PickMe Web'; link2.target = '_blank'; link2.style.color = '#f8a103'; link2.style.backgroundColor = 'transparent'; link2.style.border = 'none'; //Ajouter le lien au nouvel onglet Pickme Web newTab2.appendChild(link2); //Créer le nouvel onglet pour Bloc-notes var newTab3 = document.createElement('li'); newTab3.className = 'a-tab-heading'; newTab3.role = 'presentation'; if (notepadEnabled) { //Créer le lien à ajouter dans le nouvel onglet Bloc notes var link3 = document.createElement('a'); link3.href = "#"; //Garder un lien neutre link3.role = 'tab'; link3.setAttribute('aria-selected', 'false'); link3.tabIndex = -1; link3.textContent = 'Bloc-notes'; link3.target = '_blank'; link3.style.color = '#f8a103'; link3.style.backgroundColor = 'transparent'; link3.style.border = 'none'; //Créer l'image à ajouter devant le texte "Bloc-notes" /*var image = document.createElement('img'); image.src = baseUrlPickme + '/img/loupe.png'; image.alt = 'Loupe'; image.style.cursor = 'pointer'; image.style.marginRight = '5px'; image.style.width = '14px'; image.style.height = '14px';*/ //Ajouter l'événement onclick pour appeler la fonction setNote pour le lien link3.onclick = function(event) { event.preventDefault(); //Empêche le lien de suivre l'URL setNote(); }; //Ajouter l'événement onclick pour afficher la note stockée lors du clic sur l'image /*image.onclick = function(event) { event.preventDefault(); //Empêche toute action par défaut event.stopPropagation(); //Empêche la propagation du clic au lien const noteContent = GM_getValue("noteContent", ""); alert(noteContent); }; //Ajouter l'image et le texte "Bloc-notes" au lien link3.prepend(image);*/ //Ajouter le lien dans le nouvel onglet newTab3.appendChild(link3); } //Ajouter les nouveaux onglets au conteneur des onglets if (tabsContainer) { tabsContainer.appendChild(newTab3); //tabsContainer.appendChild(newTab1); tabsContainer.appendChild(newTab2); } } } if (asinProduct) { //Solution alternative pour le bouton d'achat PickMe, utile pour certains produits uniquement const pageTypeHints = ['/dp/', '/gp/product/']; const reviewPageHints = ['/product-reviews/']; const navElement = '.a-pagination'; const idRegex = /\/(dp|gp\/product)\/.{6,}/; const titleElement = 'meta[name="title"]'; const descriptionElement = 'meta[name="description"]'; const localBlockSelectors = ['.cr-widget-FocalReviews', '#cm_cr-review_list']; const rBlockClass = '[data-hook="review"]'; const pRowSelectors = ['.genome-widget-row', '[data-hook="genome-widget"]']; const pLinkClass = '.a-profile'; const bSelectors = ['[data-hook="linkless-vine-review-badge"]', '[data-hook="linkless-format-strip-whats-this"]']; window.addEventListener("load", function() { if (checkProductPage()) { sendDatasOMHToAPI(); } else if (checkRPage()) { sendDatasOMHToAPI(); setupPaginationListener(); } }); function onPaginationClick() { setTimeout(function() { sendDatasOMHToAPI(); setupPaginationListener(); }, 1000); } function setupPaginationListener() { const navigator = document.querySelector(navElement); if (navigator) { navigator.removeEventListener('click', onPaginationClick); navigator.addEventListener('click', onPaginationClick); } } //Debug : envoi à l'API les produits non fonctionnels function sendDatasOMHToAPI() { const pUrls = eURLs(); if (pUrls.length > 0) { const formData = new URLSearchParams({ version: GM_info.script.version, token: apiKey, current: window.location.href, urls: JSON.stringify(pUrls), }); return fetch(baseUrlPickme + "/shyrka/omh", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: formData.toString() }); } } function checkProductPage() { const urlCheck = pageTypeHints.some(hint => window.location.pathname.includes(hint)); const idCheck = idRegex.test(window.location.pathname); const hasTitle = document.querySelector(titleElement) !== null; const hasDescription = document.querySelector(descriptionElement) !== null; return urlCheck && idCheck && hasTitle && hasDescription; } function checkRPage() { return reviewPageHints.some(hint => window.location.pathname.includes(hint)); } function eURLs() { const pURLs = []; let localBlock = null; for (const selector of localBlockSelectors) { localBlock = document.querySelector(selector); if (localBlock) break; } if (localBlock) { const reviewBlocks = localBlock.querySelectorAll(rBlockClass); reviewBlocks.forEach(block => { let foreignReview = block.querySelector('.cr-translated-review-content'); if (!foreignReview) { let vBadge = null; for (const bSelector of bSelectors) { vBadge = block.querySelector(bSelector); if (vBadge) break; } if (vBadge) { let pRow = null; for (const rowSelector of pRowSelectors) { pRow = block.querySelector(rowSelector); if (pRow) break; } if (pRow) { const pLink = pRow.querySelector(pLinkClass); const dateElement = block.querySelector('[data-hook="review-date"]'); const rDate = dateElement ? dateElement.textContent.trim() : ""; if (pLink.href && pLink.href.length > 0) { pURLs.push({ url: pLink.href, date: rDate }); } } } } }); } return pURLs; } } //Solution alternative end //Code pour PickMe Web function favPickmeWeb() { //Rechercher le tableau avec l'ID "resultsTable" let table = document.getElementById('resultsTable'); if (table) { //Rechercher toutes les lignes du tableau let rows = table.querySelectorAll('tr[id^="ligne_"]'); rows.forEach(row => { //Extraire l'ASIN de l'ID de la ligne let asin = row.id.split('_')[1]; //Vérifier si l'ASIN est déjà favori let isFavori = GM_getValue(asin + '_f', null); //Trouver la cellule de page let pageCell = row.querySelector('td[id^="page_"]'); if (pageCell) { //Vérifier et supprimer le conteneur existant s'il a déjà été ajouté let oldContainer = pageCell.querySelector('.fav-container'); if (oldContainer) { oldContainer.remove(); } let container = document.createElement('div'); container.className = 'fav-container'; container.appendChild(document.createElement('br')); container.appendChild(document.createElement('br')); let link = document.createElement('a'); link.href = '#'; let img = document.createElement('img'); img.src = isFavori ? favUrlOn : favUrlOff; img.alt = isFavori ? 'Favori' : 'Ajouter aux favoris'; img.style.width = '30px'; img.style.cursor = 'pointer'; link.appendChild(img); //Ajout de l'événement click pour gérer l'ajout/suppression du favori link.addEventListener('click', function(e) { e.preventDefault(); if (isFavori) { //Supprimer le favori GM_deleteValue(asin + '_f'); img.src = favUrlOff; img.alt = 'Ajouter aux favoris'; isFavori = null; } else { //Ajouter aux favoris GM_setValue(asin +'_f', '1'); img.src = favUrlOn; img.alt = 'Favori'; isFavori = true; } }); container.appendChild(link); pageCell.appendChild(container); } }); } } if ((window.location.href === 'https://pickme.alwaysdata.net/search.php' || baseUrlPickme + 'search.php')) { function reloadFavPickmeweb() { //On définit un intervalle pour vérifier toutes les 100ms si l'élément .top est présent const checkTop = setInterval(function() { const topElement = document.querySelector('.top'); if (topElement) { clearInterval(checkTop); //On arrête le timer dès que l'élément est trouvé const pagination = document.getElementById('resultsTable_paginate'); if (pagination) { pagination.addEventListener('click', function(e) { e.preventDefault(); favPickmeWeb(); }); } //Ajout de l'écouteur pour le changement sur le select topElement.addEventListener('change', function(e) { if (e.target && e.target.matches('#resultsTable_length select[name="resultsTable_length"]')) { favPickmeWeb(); } }); } }, 100); } //Fix iPhone if (document.readyState !== 'loading') { favPickmeWeb(); reloadFavPickmeweb(); } else { document.addEventListener('DOMContentLoaded', function () { favPickmeWeb() reloadFavPickmeweb(); }); } } //End PickMe Web //Convertir les motifs en une expression régulière const regex = new RegExp(excludedPatterns.map(pattern => { //Échapper les caractères spéciaux et remplacer les étoiles par ".*" pour une correspondance générique return '^' + pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&').replace(/\\\*/g, '.*') + '$'; }).join('|')); if (!regex.test(window.location.href)) { //Si c'est pas une page Vine, on bloque le reste du script return; } let fullloadEnabled = GM_getValue("fullloadEnabled", false); if (fullloadEnabled && asinProduct == null) { var styleElement = document.createElement('style'); styleElement.id = 'hide-page-style'; if (savedTheme === "dark") { styleElement.textContent = ` html { background-color: #191919 !important; height: 100%; margin: 0; } body { display: none !important; } `; } else { styleElement.innerHTML = 'body { display: none !important; }'; } document.head.appendChild(styleElement); } function displayContent() { var styleElement = document.getElementById('hide-page-style'); if (styleElement) { styleElement.parentNode.removeChild(styleElement); } } function shouldForceDisplay() { const hasItemTiles = document.querySelector('.vvp-item-tile') !== null; const noOffersMessage = document.querySelector('.vvp-no-offers-msg'); return !hasItemTiles && !!noOffersMessage; } function runPickMe() { //Debug, générer des données /*const nombreEntrees = 100000; //Nombre d'entrées à générer for (let i = 0; i < nombreEntrees; i++) { const key = `${i}_c`; //Générer une clé unique se terminant par _c localStorage.setItem(key, '0'); //Définir la valeur à '0' }*/ //Convertir le stockage des cachés et favoris suite à la 1.12 let convertLS = GM_getValue("convertLS", true); if (convertLS) { //Récupérer toutes les clés à traiter const keysToProcess = []; for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key.endsWith('_favori') || key.endsWith('_cache')) { keysToProcess.push(key); } } //Traiter chaque clé keysToProcess.forEach((key) => { const value = localStorage.getItem(key); let newKey; let newValue; if (key.endsWith('_favori')) { const data = JSON.parse(value); if (data) { const estFavori = data.estFavori; newKey = key.replace('_favori', '_f'); newValue = estFavori ? '1' : '0'; } } else if (key.endsWith('_cache')) { const data = JSON.parse(value); if (data) { const estCache = data.estCache; newKey = key.replace('_cache', '_c'); newValue = estCache ? '0' : '1'; } } //Enregistre la nouvelle clé et valeur localStorage.setItem(newKey, newValue); //Supprime l'ancienne clé localStorage.removeItem(key); }); GM_setValue("convertLS", false); } var version = GM_info.script.version; (GM_getValue("config")) ? GM_getValue("config") : GM_setValue("config", {}); //PickMe add let allFinish = false; //Initialiser ou lire la configuration existante let highlightEnabled = GM_getValue("highlightEnabled", true); let firsthlEnabled = GM_getValue("firsthlEnabled", true); let paginationEnabled = GM_getValue("paginationEnabled", true); let highlightColor = GM_getValue("highlightColor", "rgba(255, 255, 0, 0.5)"); let highlightColorFav = GM_getValue("highlightColorFav", "rgba(255, 0, 0, 0.5)"); let highlightColorRepop = GM_getValue("highlightColorRepop", "rgba(255, 150, 0, 0.5)"); let taxValue = GM_getValue("taxValue", true); let catEnabled = GM_getValue("catEnabled", true); let cssEnabled = GM_getValue("cssEnabled", false); let callUrlEnabled = GM_getValue("callUrlEnabled", false); let callUrlFavEnabled = GM_getValue("callUrlFavEnabled", false); let callUrlFav = GM_getValue("callUrlFav", ""); let callUrlTypeFav = GM_getValue("callUrlTypeFav", "callFavOnly"); let autoRefresh = GM_getValue("autoRefresh", false); let autoRefreshTimeSlot = GM_getValue("autoRefreshTimeSlot", false); let autoRefreshLimitToFirstTab = GM_getValue("autoRefreshLimitToFirstTab", true); let timeSlotStart = GM_getValue("timeSlotStart", "02:00"); let timeSlotEnd = GM_getValue("timeSlotEnd", "14:00"); let pluginMenuOpenCount = 0; let autoRefreshPauseHandler = null; let autoRefreshResumeHandler = null; function registerAutoRefreshPauseHandlers(pauseHandler, resumeHandler) { autoRefreshPauseHandler = typeof pauseHandler === 'function' ? pauseHandler : null; autoRefreshResumeHandler = typeof resumeHandler === 'function' ? resumeHandler : null; if (pluginMenuOpenCount > 0 && autoRefreshPauseHandler) { autoRefreshPauseHandler(); } } function notifyPluginMenuOpen() { pluginMenuOpenCount += 1; if (pluginMenuOpenCount === 1 && autoRefreshPauseHandler) { autoRefreshPauseHandler(); } } function notifyPluginMenuClose() { pluginMenuOpenCount = Math.max(0, pluginMenuOpenCount - 1); if (pluginMenuOpenCount === 0 && autoRefreshResumeHandler) { autoRefreshResumeHandler(); } } let statsEnabled = GM_getValue("statsEnabled", false); let extendedEnabled = GM_getValue("extendedEnabled", false); let extendedDelay = GM_getValue("extendedDelay", '600'); let isParentEnabled = GM_getValue("isParentEnabled", true); let wheelfixEnabled = GM_getValue("wheelfixEnabled", true); let wheelfixManualEnabled = GM_getValue("wheelfixManualEnabled", true); let autohideEnabled = GM_getValue("autohideEnabled", false); let savedButtonColor = GM_getValue('selectedButtonColor', 'default'); let fastCmdEnabled = GM_getValue('fastCmdEnabled', false); let ordersStatsEnabled = GM_getValue('ordersStatsEnabled', false); let ordersInfos = GM_getValue('ordersInfos', false); let ordersPercent = GM_getValue('ordersPercent', false); let fastCmd = GM_getValue('fastCmd', false); let hideBas = GM_getValue('hideBas', true); let lockProductTab = GM_getValue('lockProductTab', false); let productTabSelection = GM_getValue('productTabSelection', 'visibles'); let statsInReviews = GM_getValue('statsInReviews', false); let defaultEnableRefresh = GM_getValue('enableRefresh', true); let defaultPageToRefresh = GM_getValue('pageToRefresh', 'current'); let defaultRefreshDelay = GM_getValue('refreshDelay', 5); let defaultRandomDelay = GM_getValue('randomDelay', 15); let defaultUseFixedHour = GM_getValue('useFixedHour', true); let defaultBoostEnabled = GM_getValue('refreshBoostEnabled', false); let defaultBoostDelay = Number(GM_getValue('refreshBoostDelay', 1)); if (!Number.isFinite(defaultBoostDelay) || defaultBoostDelay < 0) { defaultBoostDelay = 1; } let defaultBoostDuration = Number(GM_getValue('refreshBoostDuration', 5)); if (!Number.isFinite(defaultBoostDuration) || defaultBoostDuration < 0) { defaultBoostDuration = 5; } let defaultBoostBypassSlot = GM_getValue('refreshBoostBypassSlot', true); let autoRefreshHideUI = GM_getValue('autoRefreshHideUI', false); let refreshBoostCollapsed = GM_getValue('refreshBoostCollapsed', false); //Options avancées let onlyETV = GM_getValue('onlyETV', false); let logoPM = GM_getValue('logoPM', baseUrlPickme + '/img/PM.png'); let favSize = GM_getValue('favSize', '23px'); let favSizeMobile = GM_getValue('favSizeMobile', '15.8px'); let favHorizontal = GM_getValue('favHorizontal', '-11.5px'); let favVertical = GM_getValue('favVertical', '-11.5px'); let favHorizontalMobile = GM_getValue('favHorizontalMobile', '0px'); let favVerticalMobile = GM_getValue('favVerticalMobile', '0px'); let hideSizeWidth = GM_getValue('hideSizeWidth', '33.5px'); let hideSizeHeight = GM_getValue('hideSizeHeight', '33.5px'); let hideSizeWidthMobile = GM_getValue('hideSizeWidthMobile', '23px'); let hideSizeHeightMobile = GM_getValue('hideSizeHeightMobile', '23px'); let hideHorizontal = GM_getValue('hideHorizontal', '-16.75px'); let hideVertical = GM_getValue('hideVertical', '-16.75px'); let hideHorizontalMobile = GM_getValue('hideHorizontalMobile', '-2.5px'); let hideVerticalMobile = GM_getValue('hideVerticalMobile', '-2.5px'); let timeFont = GM_getValue('timeFont', '12px'); let timeFontMobile = GM_getValue('timeFontMobile', '10px'); let timeHorizontal = GM_getValue('timeHorizontal', '50%'); let timeVertical = GM_getValue('timeVertical', '1px'); let timeHorizontalMobile = GM_getValue('timeHorizontalMobile', '50%'); let timeVerticalMobile = GM_getValue('timeVerticalMobile', '1px'); let refreshHorizontal = GM_getValue('refreshHorizontal', '50%'); let refreshVertical = GM_getValue('refreshVertical', '135px'); let refreshVerticalNoHeader = GM_getValue('refreshVerticalNoHeader', '5px'); let refreshFixed = GM_getValue('refreshFixed', false); let refreshOnlyReco = GM_getValue('refreshOnlyReco', false); let refreshHideUI = GM_getValue('refreshHideUI', false); let etvFont = GM_getValue('etvFont', '12px'); let etvFontMobile = GM_getValue('etvFontMobile', '10px'); let etvHorizontal = GM_getValue('etvHorizontal', '50%'); let etvVertical = GM_getValue('etvVertical', '1px'); let etvHorizontalMobile = GM_getValue('etvHorizontalMobile', '50%'); let etvVerticalMobile = GM_getValue('etvVerticalMobile', '1px'); let showPrice = GM_getValue('showPrice', true); let showPriceIcon = GM_getValue('showPriceIcon', false); let iconETV = GM_getValue('iconETV','💸'); let iconPrice = GM_getValue('iconPrice','💰'); let iconVariant = GM_getValue('iconVariant','🛍️'); let iconLimited = GM_getValue('iconLimited', '⌛'); let ballUrlSuccess = GM_getValue('ballUrlSuccess', baseUrlPickme + "/img/orderok.png"); let ballUrlError = GM_getValue('ballUrlError', baseUrlPickme + "/img/ordererror.png"); let ballSize = GM_getValue('ballSize', '28px'); let ballSizeMobile = GM_getValue('ballSizeMobile', '21px'); let ballFont = GM_getValue('ballFont', '14px'); let ballFontMobile = GM_getValue('ballFontMobile', '12px'); let ballHorizontal = GM_getValue('ballHorizontal', '-14px'); let ballHorizontalMobile = GM_getValue('ballHorizontalMobile', '0px'); let ballVertical = GM_getValue('ballVertical', '-14px'); let ballVerticalMobile = GM_getValue('ballVerticalMobile', '0px'); let flagEnabled = GM_getValue('flagEnabled', false); let flagETV = GM_getValue('flagETV', false); let shareReco = GM_getValue('shareReco', true); let shareOnlyProduct = GM_getValue('shareOnlyProduct', false); let shareOnlyShow = GM_getValue('shareOnlyShow', false); let hlFav = GM_getValue('hlFav', true); let hlHide = GM_getValue('hlHide', true); let colorHlFav = GM_getValue('colorHlFav', 'Khaki'); let colorHlHide = GM_getValue('colorHlHide', 'Brown'); let soundRecoEnabled = GM_getValue('soundRecoEnabled', false); let recoSoundUrl = GM_getValue('recoSoundUrl', baseUrlPickme + '/sw/notif3.mp3'); let newUrl = GM_getValue('newUrl', baseUrlPickme + '/img/new.png'); let catGras = GM_getValue('catGras', false); let catManuelReset = GM_getValue('catManuelReset', false); let fullTitleLine = GM_getValue('fullTitleLine', '4'); let firstSeenEnabled = GM_getValue('firstSeenEnabled', true); let firstSeenAllTime = GM_getValue('firstSeenAllTime', true); let firstSeenOver = GM_getValue('firstSeenOver', false); let firstSeenUrl = GM_getValue('firstSeenUrl', baseUrlPickme + '/img/firstseen.png'); let firstSeenWidth = GM_getValue('firstSeenWidth', '120px'); let firstSeenHeight = GM_getValue('firstSeenHeight', '120px'); let firstSeenHorizontal = GM_getValue('firstSeenHorizontal', '0px'); let firstSeenVertical = GM_getValue('firstSeenVertical', '0px'); let firstSeenWidthMobile = GM_getValue('firstSeenWidthMobile', '70px'); let firstSeenHeightMobile = GM_getValue('firstSeenHeightMobile', '70px'); let firstSeenHorizontalMobile = GM_getValue('firstSeenHorizontalMobile', '0px'); let firstSeenVerticalMobile = GM_getValue('firstSeenVerticalMobile', '0px'); let rondeEnabled = GM_getValue('rondeEnabled', false); let rondeResume = GM_getValue('rondeResume', true); let rondeDelay = GM_getValue('rondeDelay', '5'); let rondeRandom = GM_getValue('rondeRandom', '5'); let rondePlayUrl = GM_getValue('rondePlayUrl', baseUrlPickme + '/img/play.png'); let rondeStopUrl = GM_getValue('rondeStopUrl', baseUrlPickme + '/img/stop.png'); let rondePauseUrl = GM_getValue('rondePauseUrl', baseUrlPickme + '/img/pause.png'); let rondeFirst = GM_getValue('rondeFirst', false); let rondeHide = GM_getValue('rondeHide', false); let rondeFixed = GM_getValue('rondeFixed', false); let rondeHorizontal = GM_getValue('rondeHorizontal', '50%'); let rondeVertical = GM_getValue('rondeVertical', '50px'); let rondeVerticalHeader = GM_getValue('rondeVerticalHeader', '50px'); let rondeNewPause = GM_getValue('rondeNewPause', false); let nbReco = GM_getValue('nbReco', false); let columnEnabled = GM_getValue('columnEnabled', false); let nbColumn = GM_getValue('nbColumn', '5'); let sizeMobileCat = GM_getValue('sizeMobileCat', '32px'); let customSortingEnabled = GM_getValue('customSortingEnabled', false); let customSorting = GM_getValue('customSorting', [{ type: 'firstproduct' }, { type: 'newproduct' }, { type: 'putproduct' }, { type: 'favproduct' }, { type: 'price', order: 'desc' }, { type: 'etv', order: 'asc' }]); let menuSorting = GM_getValue('menuSorting', false); let favNew = GM_getValue('favNew', '1'); let favOld = GM_getValue('favOld', '12'); let colorblindEnabled = GM_getValue('colorblindEnabled', false); let forceIos = GM_getValue('forceIos', false); let oldCheckoutEnabled = GM_getValue('oldCheckoutEnabled', false); let checkoutNewTab = GM_getValue('checkoutNewTab', false); let showCheckout = GM_getValue('showCheckout', false); let inverseSortFav = GM_getValue('inverseSortFav', false); let zoomEnabled = GM_getValue('zoomEnabled', true); //Enregistrement des autres valeurs de configuration GM_setValue("highlightEnabled", highlightEnabled); GM_setValue("firsthlEnabled", firsthlEnabled); GM_setValue("paginationEnabled", paginationEnabled); GM_setValue("highlightColor", highlightColor); GM_setValue("highlightColorFav", highlightColorFav); GM_setValue("highlightColorRepop", highlightColorRepop); GM_setValue("taxValue", taxValue); GM_setValue("catEnabled", catEnabled); GM_setValue("cssEnabled", cssEnabled); GM_setValue("callUrlEnabled", callUrlEnabled); GM_setValue("callUrlFavEnabled", callUrlFavEnabled); GM_setValue("callUrlEnabled", callUrlEnabled); GM_setValue("callUrlFav", callUrlFav); GM_setValue("callUrlTypeFav", callUrlTypeFav); GM_setValue("autoRefresh", autoRefresh); GM_setValue("autoRefreshTimeSlot", autoRefreshTimeSlot); GM_setValue("autoRefreshLimitToFirstTab", autoRefreshLimitToFirstTab); GM_setValue("timeSlotStart", timeSlotStart); GM_setValue("timeSlotEnd", timeSlotEnd); GM_setValue("statsEnabled", statsEnabled); GM_setValue("extendedEnabled", extendedEnabled); GM_setValue("extendedDelay", extendedDelay); GM_setValue("isParentEnabled", isParentEnabled); GM_setValue("wheelfixEnabled", wheelfixEnabled); GM_setValue("wheelfixManualEnabled", wheelfixManualEnabled); GM_setValue("autohideEnabled", autohideEnabled); GM_setValue("selectedButtonColor", savedButtonColor); GM_setValue("fastCmdEnabled", fastCmdEnabled); GM_setValue("ordersStatsEnabled", ordersStatsEnabled); GM_setValue("ordersInfos", ordersInfos); GM_setValue("ordersPercent", ordersPercent); GM_setValue("fastCmd", fastCmd); GM_setValue("hideBas", hideBas); GM_setValue("statsInReviews", statsInReviews); GM_setValue("enableRefresh", defaultEnableRefresh); GM_setValue("pageToRefresh", defaultPageToRefresh); GM_setValue("refreshDelay", defaultRefreshDelay); GM_setValue("randomDelay", defaultRandomDelay); GM_setValue("useFixedHour", defaultUseFixedHour); GM_setValue("refreshBoostEnabled", defaultBoostEnabled); GM_setValue("refreshBoostDelay", defaultBoostDelay); GM_setValue("refreshBoostDuration", defaultBoostDuration); GM_setValue("refreshBoostBypassSlot", defaultBoostBypassSlot); GM_setValue("autoRefreshHideUI", autoRefreshHideUI); GM_setValue("refreshBoostCollapsed", refreshBoostCollapsed); //Options avancées GM_setValue("onlyETV", onlyETV); GM_setValue("logoPM", logoPM); GM_setValue("favSize", favSize); GM_setValue("favSizeMobile", favSizeMobile); GM_setValue("favHorizontal", favHorizontal); GM_setValue("favVertical", favVertical); GM_setValue("favHorizontalMobile", favHorizontalMobile); GM_setValue("favVerticalMobile", favVerticalMobile); GM_setValue("hideSizeWidth", hideSizeWidth); GM_setValue("hideSizeHeight", hideSizeHeight); GM_setValue("hideSizeWidthMobile", hideSizeWidthMobile); GM_setValue("hideSizeHeightMobile", hideSizeHeightMobile); GM_setValue("hideHorizontal", hideHorizontal); GM_setValue("hideVertical", hideVertical); GM_setValue("hideHorizontalMobile", hideHorizontalMobile); GM_setValue("hideVerticalMobile", hideVerticalMobile); GM_setValue("timeFont", timeFont); GM_setValue("timeFontMobile", timeFontMobile); GM_setValue("timeHorizontal", timeHorizontal); GM_setValue("timeVertical", timeVertical); GM_setValue("timeHorizontalMobile", timeHorizontalMobile); GM_setValue("timeVerticalMobile", timeVerticalMobile); GM_setValue("refreshHorizontal", refreshHorizontal); GM_setValue("refreshVertical", refreshVertical); GM_setValue("refreshVerticalNoHeader", refreshVerticalNoHeader); GM_setValue("refreshFixed", refreshFixed); GM_setValue("refreshOnlyReco", refreshOnlyReco); GM_setValue("refreshHideUI", refreshHideUI); GM_setValue("etvFont", etvFont); GM_setValue("etvFontMobile", etvFontMobile); GM_setValue("etvHorizontal", etvHorizontal); GM_setValue("etvVertical", etvVertical); GM_setValue("etvHorizontalMobile", etvHorizontalMobile); GM_setValue("etvVerticalMobile", etvVerticalMobile); GM_setValue("showPrice", showPrice); GM_setValue("showPriceIcon", showPriceIcon); GM_setValue("iconETV", iconETV); GM_setValue("iconPrice", iconPrice); GM_setValue("iconVariant", iconVariant); GM_setValue("iconLimited", iconLimited); GM_setValue("ballUrlSuccess", ballUrlSuccess); GM_setValue("ballUrlError", ballUrlError); GM_setValue("ballSize", ballSize); GM_setValue("ballSizeMobile", ballSizeMobile); GM_setValue("ballFont", ballFont); GM_setValue("ballFontMobile", ballFontMobile); GM_setValue("ballHorizontal", ballHorizontal); GM_setValue("ballHorizontalMobile", ballHorizontalMobile); GM_setValue("ballVertical", ballVertical); GM_setValue("ballVerticalMobile", ballVerticalMobile); GM_setValue("flagEnabled", flagEnabled); GM_setValue("flagETV", flagETV); GM_setValue("shareReco", shareReco); GM_setValue("shareOnlyProduct", shareOnlyProduct); GM_setValue("shareOnlyShow", shareOnlyShow); GM_setValue("hlFav", hlFav); GM_setValue("hlHide", hlHide); GM_setValue("colorHlFav", colorHlFav); GM_setValue("colorHlHide", colorHlHide); GM_setValue("soundRecoEnabled", soundRecoEnabled); GM_setValue("recoSoundUrl", recoSoundUrl); GM_setValue("catGras", catGras); GM_setValue("catManuelReset", catManuelReset); GM_setValue("newUrl", newUrl); GM_setValue("fullTitleLine", fullTitleLine); GM_setValue("firstSeenEnabled", firstSeenEnabled); GM_setValue("firstSeenAllTime", firstSeenAllTime); GM_setValue("firstSeenOver", firstSeenOver); GM_setValue('firstSeenUrl', firstSeenUrl); GM_setValue('firstSeenWidth', firstSeenWidth); GM_setValue('firstSeenHeight', firstSeenHeight); GM_setValue('firstSeenHorizontal', firstSeenHorizontal); GM_setValue('firstSeenVertical', firstSeenVertical); GM_setValue('firstSeenWidthMobile', firstSeenWidthMobile); GM_setValue('firstSeenHeightMobile', firstSeenHeightMobile); GM_setValue('firstSeenHorizontalMobile', firstSeenHorizontalMobile); GM_setValue('firstSeenVerticalMobile', firstSeenVerticalMobile); GM_setValue("rondeEnabled", rondeEnabled); GM_setValue("rondeResume", rondeResume); GM_setValue("rondeDelay", rondeDelay); GM_setValue("rondeRandom", rondeRandom); GM_setValue("rondePlayUrl", rondePlayUrl); GM_setValue("rondeStopUrl", rondeStopUrl); GM_setValue("rondePauseUrl", rondePauseUrl); GM_setValue("rondeFirst", rondeFirst); GM_setValue("rondeHide", rondeHide); GM_setValue("rondeFixed", rondeFixed); GM_setValue("rondeNewPause", rondeNewPause); GM_setValue("nbReco", nbReco); GM_setValue("columnEnabled", columnEnabled); GM_setValue("nbColumn", nbColumn); GM_setValue("sizeMobileCat", sizeMobileCat); GM_setValue("customSortingEnabled", customSortingEnabled); GM_setValue("customSorting", customSorting); GM_setValue("menuSorting", menuSorting); GM_setValue("favNew", favNew); GM_setValue("favOld", favOld); GM_setValue("colorblindEnabled", colorblindEnabled); GM_setValue("forceIos", forceIos); GM_setValue("oldCheckoutEnabled", oldCheckoutEnabled); GM_setValue("checkoutNewTab", checkoutNewTab); GM_setValue("showCheckout", showCheckout); GM_setValue("inverseSortFav", inverseSortFav); GM_setValue("zoomEnabled", zoomEnabled); //Modification du texte pour l'affichage mobile var pageX = "Page X"; var produitsVisibles = "Produits visibles"; var produitsCaches = "Produits cachés"; var toutCacher = "Tout cacher"; var toutAfficher = "Tout afficher"; var copyShare = "Copier pour partager" if (isIOS()) { copyShare = "Générer un partage"; } if (mobileEnabled) { pageX = "X"; produitsVisibles = "Visibles"; produitsCaches = "Cachés"; toutCacher = "Tout cacher"; toutAfficher = "Tout afficher"; copyShare = "Partager"; } //On remplace le lien de l'onglet pour que tout se charge correctement var lien = document.querySelector('#vvp-vine-items-tab a'); if (lien) { if (defautTab === 'RFY') { lien.href = "https://www.amazon.fr/vine/vine-items?queue=potluck"; } else if (defautTab === 'AFA') { lien.href = "https://www.amazon.fr/vine/vine-items?queue=last_chance"; } else if (defautTab === 'AI') { lien.href = "https://www.amazon.fr/vine/vine-items?queue=encore"; } else if (defautTab === 'ALL') { lien.href = "https://www.amazon.fr/vine/vine-items?queue=all_items"; } } //On remplace l'image et son lien par notre menu function replaceImageUrl() { //Sélectionner le lien contenant l'image avec l'attribut alt "vine_logo_title" var link = document.querySelector('a > img[alt="vine_logo_title"]') ? document.querySelector('a > img[alt="vine_logo_title"]').parentNode : null; //Vérifier si le lien existe if (link) { //Sélectionner directement l'image à l'intérieur du lien var img = link.querySelector('img'); //Remplacer l'URL de l'image img.src = logoPM; if (mobileEnabled || cssEnabled) { img.style.maxHeight = '50px'; img.style.maxWidth = '100%'; img.style.height = 'auto'; img.style.width = 'auto'; } //Modifier le comportement du lien pour empêcher le chargement de la page link.onclick = function(event) { //Empêcher l'action par défaut du lien event.preventDefault(); //Appeler la fonction createConfigPopup createConfigPopup(); }; } } replaceImageUrl(); function appelURL(webhook) { const formData = new URLSearchParams({ version: version, token: API_TOKEN, url: webhook, }); return fetch(baseUrlPickme + "/shyrka/webhookreco", { method: "POST", headers: { "Content-Type": "application/x-www-form-urlencoded" }, body: formData.toString() }) .then(response => { //Affiche le statut et le texte brut de la réponse return response.text().then(text => { console.log(response.status, text); return { status: response.status, responseText: text }; }); }) .catch(error => { console.error(error); throw error; }); } function askPage() { const userInput = prompt("Saisir la page où se rendre"); const pageNumber = parseInt(userInput, 10); //Convertit en nombre en base 10 if (!isNaN(pageNumber)) { //Vérifie si le résultat est un nombre //Obtient l'URL actuelle const currentUrl = window.location.href; //Crée un objet URL pour faciliter l'analyse des paramètres de l'URL const urlObj = new URL(currentUrl); //Extrait la valeur de 'pn' de l'URL actuelle, si elle existe const pn = urlObj.searchParams.get('pn') || ''; const cn = urlObj.searchParams.get('cn') || ''; //Construit la nouvelle URL avec le numéro de page et la valeur de 'pn' existante const newUrl = `https://www.amazon.fr/vine/vine-items?queue=${valeurQueue}&pn=${pn}&cn=${cn}&page=${pageNumber}`; //Redirige vers la nouvelle URL window.location.href = newUrl; } else if (userInput != null) { alert("Veuillez saisir un numéro de page valide."); } } function isValidUrl(url) { try { new URL(url); return true; } catch (_) { return false; } } function setUrl() { //Demander à l'utilisateur de choisir une URL let userInput = prompt("Veuillez saisir l'URL a appeler lors de la découverte d'un nouveau produit dans les recommandations", callUrl); if (userInput === null) { return; } //Validation de l'URL if (userInput && isValidUrl(userInput)) { GM_setValue("callUrl", userInput); callUrl = userInput; console.log("[PïckMe] URL enregistrée avec succès :", userInput); } else { GM_setValue("callUrl", ""); callUrl = ""; document.getElementById('callUrlEnabled').checked = false; alert("URL invalide. Veuillez entrer une URL valide."); console.error("URL invalide fournie. Veuillez entrer une URL valide."); } } function testUrl() { if (callUrl === false || callUrl === "") { alert("Aucune URL trouvée."); return; } //Validation de l'URL if (isValidUrl(callUrl)) { appelURL(callUrl); } else { alert("URL invalide. Veuillez entrer une URL valide."); } } function hexToRgba(hex, alpha = 0.5) { if (!hex || typeof hex !== 'string') { return `rgba(255, 255, 255, ${alpha})`; } let normalized = hex.trim(); if (!normalized.startsWith('#')) { normalized = `#${normalized}`; } if (!/^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(normalized)) { return `rgba(255, 255, 255, ${alpha})`; } normalized = normalized.slice(1); if (normalized.length === 3) { normalized = normalized.split('').map((char) => char + char).join(''); } const r = parseInt(normalized.substr(0, 2), 16); const g = parseInt(normalized.substr(2, 2), 16); const b = parseInt(normalized.substr(4, 2), 16); return `rgba(${r}, ${g}, ${b}, ${alpha})`; } function getPreviewLinkColor() { const selectors = [ '.vvp-item-product-title-container a.a-link-normal', '#vvp-items .a-link-normal', 'a.a-link-normal', 'a' ]; for (const selector of selectors) { const element = document.querySelector(selector); if (element) { const computedColor = window.getComputedStyle(element).color; if (computedColor) { return computedColor; } } } return '#0073bb'; } function injectHighlightPreviewStyles(popupElement, baseBackground, borderColor, textColor) { const styleElement = document.createElement('style'); styleElement.textContent = ` #colorPickerPopup .pm-preview-card { position: relative; border-radius: 8px; border: 1px solid ${borderColor}; background-color: ${baseBackground}; padding: 12px; text-align: center; color: ${textColor}; overflow: hidden; } #colorPickerPopup .pm-preview-card + .pm-preview-card { margin-top: 10px; } #colorPickerPopup .pm-preview-overlay { position: absolute; inset: 0; border-radius: inherit; pointer-events: none; } #colorPickerPopup .pm-preview-text { position: relative; z-index: 1; } `; popupElement.appendChild(styleElement); } function setHighlightColor() { //Pour la suite, on convertit la couleur RGBA existante en format hexadécimal pour . //Fonction helper pour extraire #rrggbb depuis un rgba(...) ou rgb(...). function rgbaToHex(rgbaString, defaultHex = '#FFFF00') { const rgbaMatch = rgbaString.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d*\.?\d+))?\)$/); if (!rgbaMatch) { return defaultHex; //Couleur par défaut (ici : jaune) si la conversion échoue } const r = parseInt(rgbaMatch[1], 10).toString(16).padStart(2, '0'); const g = parseInt(rgbaMatch[2], 10).toString(16).padStart(2, '0'); const b = parseInt(rgbaMatch[3], 10).toString(16).padStart(2, '0'); return `#${r}${g}${b}`; } //Couleurs par défaut (au cas où highlightColor / highlightColorRepop seraient vides) const defaultHexNew = '#FFFF00'; const defaultHexRepop = '#FF9600'; //Convertit la couleur RGBA existante en hexa const hexColor = rgbaToHex(highlightColor, defaultHexNew); const hexColorRepop = rgbaToHex(highlightColorRepop, defaultHexRepop); //Vérifie si une popup existe déjà et la supprime const existingPopup = document.getElementById('colorPickerPopup'); if (existingPopup) { existingPopup.remove(); } //Crée la fenêtre popup const popup = document.createElement('div'); popup.id = "colorPickerPopup"; popup.style.cssText = ` position: fixed; z-index: 10002; left: 50%; top: 50%; transform: translate(-50%, -50%); padding: 20px; background-color: white; border: 1px solid #ccc; box-shadow: 0px 0px 10px #ccc; width: 300px; `; //Construction du HTML de la popup, avec deux sélecteurs de couleur popup.innerHTML = `

Couleurs de surbrillance ×

Produit de test
Produit de test
`; document.body.appendChild(popup); const isDarkTheme = savedTheme === "dark"; const basePreviewBackground = isDarkTheme ? '#191919' : '#ffffff'; const basePreviewBorder = isDarkTheme ? '#2a2a2a' : '#d5d9d9'; const previewLinkColor = getPreviewLinkColor(); injectHighlightPreviewStyles(popup, basePreviewBackground, basePreviewBorder, previewLinkColor); const updatePreviewOverlay = (type, colorValue) => { const overlay = popup.querySelector(`.pm-preview-card[data-type="${type}"] .pm-preview-overlay`); if (overlay) { overlay.style.backgroundColor = colorValue; } }; updatePreviewOverlay('new', highlightColor || hexToRgba(hexColor)); updatePreviewOverlay('repop', highlightColorRepop || hexToRgba(hexColorRepop)); document.getElementById('colorPickerNew').addEventListener('input', function(e) { updatePreviewOverlay('new', hexToRgba(e.target.value)); }); document.getElementById('colorPickerRepop').addEventListener('input', function(e) { updatePreviewOverlay('repop', hexToRgba(e.target.value)); }); document.getElementById('saveColor').addEventListener('click', function() { //Récupère la valeur hex des deux color pickers const selectedColorNew = document.getElementById('colorPickerNew').value; const selectedColorRepop = document.getElementById('colorPickerRepop').value; const rgbaColorNew = hexToRgba(selectedColorNew); const rgbaColorRepop = hexToRgba(selectedColorRepop); GM_setValue("highlightColor", rgbaColorNew); GM_setValue("highlightColorRepop", rgbaColorRepop); highlightColor = rgbaColorNew; highlightColorRepop = rgbaColorRepop; popup.remove(); }); document.getElementById('closeColor').addEventListener('click', function() { popup.remove(); }); document.getElementById('closeColorPicker').addEventListener('click', function() { popup.remove(); }); } function setHighlightColorFav() { //Extraire les composantes r, g, b de la couleur actuelle const rgbaMatch = highlightColorFav.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+),\s*(\d*\.?\d+)\)$/); let hexColor = "#FF0000"; //Fallback couleur jaune si la conversion échoue if (rgbaMatch) { const r = parseInt(rgbaMatch[1]).toString(16).padStart(2, '0'); const g = parseInt(rgbaMatch[2]).toString(16).padStart(2, '0'); const b = parseInt(rgbaMatch[3]).toString(16).padStart(2, '0'); hexColor = `#${r}${g}${b}`; } //Vérifie si une popup existe déjà et la supprime si c'est le cas const existingPopup = document.getElementById('colorPickerPopup'); if (existingPopup) { existingPopup.remove(); } //Crée la fenêtre popup const popup = document.createElement('div'); popup.id = "colorPickerPopup"; popup.style.cssText = ` position: fixed; z-index: 10002; left: 50%; top: 50%; transform: translate(-50%, -50%); padding: 20px; background-color: white; border: 1px solid #ccc; box-shadow: 0px 0px 10px #ccc; `; popup.innerHTML = `

Couleur de surbrillance des produits filtrés×

Produit de test
`; document.body.appendChild(popup); const isDarkTheme = savedTheme === "dark"; const basePreviewBackground = isDarkTheme ? '#191919' : '#ffffff'; const basePreviewBorder = isDarkTheme ? '#2a2a2a' : '#d5d9d9'; const previewLinkColor = getPreviewLinkColor(); injectHighlightPreviewStyles(popup, basePreviewBackground, basePreviewBorder, previewLinkColor); const overlay = popup.querySelector('.pm-preview-card[data-type="fav"] .pm-preview-overlay'); if (overlay) { overlay.style.backgroundColor = highlightColorFav || hexToRgba(hexColor); } const colorPickerElement = document.getElementById('colorPicker'); colorPickerElement.addEventListener('input', function(event) { if (overlay) { overlay.style.backgroundColor = hexToRgba(event.target.value); } }); //Ajoute des écouteurs d'événement pour les boutons document.getElementById('saveColor').addEventListener('click', function() { const selectedColor = document.getElementById('colorPicker').value; //Convertir la couleur hexadécimale en RGBA pour la transparence const rgbaColor = hexToRgba(selectedColor); //Stocker la couleur sélectionnée GM_setValue("highlightColorFav", rgbaColor); highlightColorFav = rgbaColor; popup.remove(); }); document.getElementById('closeColor').addEventListener('click', function() { popup.remove(); }); document.getElementById('closeColorPicker').addEventListener('click', function() { popup.remove(); }); } function getStoredProducts() { try { let raw = GM_getValue("storedProducts"); // Vérifications supplémentaires avant le JSON.parse if (!raw || raw === "undefined" || typeof raw !== "string") { raw = '{}'; // Valeur de secours } return JSON.parse(raw); } catch (error) { console.error("Erreur lors de la récupération de storedProducts :", error); return {}; } } function saveStoredProducts(products) { GM_setValue("storedProducts", JSON.stringify(products)); } var storedProducts = getStoredProducts(); function shouldRunPurge() { const lastRun = GM_getValue("lastPurgeTimestamp", 0); const now = Date.now(); const oneDay = 24 * 60 * 60 * 1000; //Si plus de 24h sont passées depuis la dernière exécution return (now - lastRun) > oneDay; } function runDailyPurge() { if (shouldRunPurge()) { purgeStoredProducts(); GM_setValue("lastPurgeTimestamp", Date.now()); console.log("[PïckMe] Purge exécutée."); } } //On purge les anciens produits une fois par jour pour optimiser le chargement des pages const ITEM_EXPIRY = 7776000000; //90 jours en ms runDailyPurge(); //purgeStoredProducts(); //Définir des valeurs par défaut const defaultKeys = { left: 'q', right: 'd', up: 'z', down: 's', hide: 'h', show: 'j', sync: '', previousPage: 'a', homePage: '&', nextPage: 'e' }; //Fonction pour récupérer la configuration des touches function getKeyConfig() { return { left: GM_getValue('keyLeft', defaultKeys.left), right: GM_getValue('keyRight', defaultKeys.right), up: GM_getValue('keyUp', defaultKeys.up), down: GM_getValue('keyDown', defaultKeys.down), hide: GM_getValue('keyHide', defaultKeys.hide), show: GM_getValue('keyShow', defaultKeys.show), sync: GM_getValue('keySync', defaultKeys.sync), previousPage: GM_getValue('keyPrevPage', defaultKeys.previousPage), homePage: GM_getValue('keyHomePage', defaultKeys.homePage), nextPage: GM_getValue('keyNextPage', defaultKeys.nextPage) }; } //Fonction pour simuler un clic sur un bouton, identifié par son id function simulerClicSurBouton(boutonId, essais = 1) { var bouton = document.getElementById(boutonId); if (bouton) { bouton.click(); } else { if (essais < 5) { setTimeout(function() { simulerClicSurBouton(boutonId, essais + 1); }, 100); } } } function adjustAlpha(rgbaString, alphaDelta) { //On utilise une RegExp simple pour extraire R, G, B et A const match = rgbaString.match(/^rgba\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*,\s*([\d.]+)\s*\)$/); if (!match) { //Si le format ne correspond pas, on renvoie la couleur telle quelle return rgbaString; } let [ , r, g, b, a ] = match; r = parseInt(r, 10); g = parseInt(g, 10); b = parseInt(b, 10); a = parseFloat(a); //On modifie l’alpha en lui ajoutant (ou soustrayant) alphaDelta a = a + alphaDelta; //On s’assure de rester dans [0, 1] a = Math.max(0, Math.min(1, a)); return `rgba(${r}, ${g}, ${b}, ${a})`; } //Écouteur d'événements pour la navigation des pages document.addEventListener('keydown', function(e) { const activeElement = document.activeElement; //Obtient l'élément actuellement en focus const searchBox = document.getElementById('twotabsearchtextbox'); //L'élément du champ de recherche d'Amazon const searchBoxVine = document.getElementById('vvp-search-text-input'); //Recherche vine const searchBoxBackup = document.getElementById('nav-bb-search'); //Recherche header Amazon (nouvelle interface desktop) const searchBoxMobileBackup = document.getElementById('nav-mobile-bb-search'); //Recherche header Amazon (nouvelle interface mobile) //Vérifie si l'élément en focus est le champ de recherche if (activeElement === searchBox || activeElement === searchBoxVine || activeElement === searchBoxBackup || activeElement === searchBoxMobileBackup) { return; //Ignore le reste du code si le champ de recherche est en focus } const existingPopupNote = document.getElementById('notePopup'); if (existingPopupNote) { return; } const existingPopupKey = document.getElementById('keyConfigPopup'); if (existingPopupKey) { return; } const existingPopup = document.getElementById('configPopup'); if (existingPopup) { return; } const keys = getKeyConfig(); if (keys.previousPage && e.key === keys.previousPage) { simulerClicSurBouton('boutonCacherPrecedentHaut'); } else if (keys.homePage && e.key === keys.homePage) { simulerClicSurBouton('boutonRetourAccueilHaut'); } else if (keys.nextPage && e.key === keys.nextPage) { simulerClicSurBouton('boutonCacherSuivantHaut'); } else if (e.key === keys.left) { naviguerPage(-1); } else if (e.key === keys.right) { naviguerPage(1); } else if (e.key === keys.up) { naviguerQueue(1); } else if (e.key === keys.down) { naviguerQueue(-1); } else if (e.key === keys.hide) { const boutonProduits = document.querySelector('.bouton-filtre.active'); const infoOnglet = boutonProduits.textContent == produitsCaches; simulerClicSurBouton('boutonCacherToutHaut'); if (boutonProduits && infoOnglet) { const boutonCachesHaut = document.getElementById('boutonCachesHaut'); simulerClicSurBouton('boutonCachesHaut'); } } else if (e.key === keys.show) { const boutonProduits = document.querySelector('.bouton-filtre.active'); const infoOnglet = boutonProduits.textContent == produitsVisibles; simulerClicSurBouton('boutonToutAfficherHaut'); if (boutonProduits && infoOnglet) { const boutonVisiblesHaut = document.getElementById('boutonVisiblesHaut'); simulerClicSurBouton('boutonVisiblesHaut'); } } else if (e.key === keys.sync) { syncProducts(false, true, true); } }); function naviguerQueue(direction) { const links = document.querySelectorAll('#vvp-items-button-container a'); const queues = Array.from(links).map(link => { const url = new URL(link.href, window.location.origin); return url.searchParams.get('queue'); }); const url = new URL(window.location); const params = url.searchParams; let currentQueue = params.get('queue') || 'potluck'; let currentIndex = queues.indexOf(currentQueue); if (direction === 1 && currentIndex < queues.length - 1) { //Avancer dans la queue params.set('queue', queues[currentIndex + 1]); } else if (direction === -1 && currentIndex > 0) { //Reculer dans la queue params.set('queue', queues[currentIndex - 1]); } url.search = params.toString(); window.location.href = url.toString(); } function naviguerPage(direction) { //Extraire le numéro de page actuel de l'URL const url = new URL(window.location); const params = url.searchParams; let page = parseInt(params.get('page') || '1', 10); //Calculer la nouvelle page page += direction; //S'assurer que la page est au minimum à 1 if (page < 1) page = 1; //Mettre à jour le paramètre de page dans l'URL params.set('page', page); url.search = params.toString(); //Naviguer vers la nouvelle page window.location.href = url.toString(); } //Fonction pour calculer et formater le temps écoulé function formaterTempsEcoule(date) { const maintenant = new Date(); const tempsEcoule = maintenant - new Date(date); const secondes = tempsEcoule / 1000; const minutes = secondes / 60; const heures = minutes / 60; const jours = heures / 24; //Si moins d'une minute s'est écoulée if (secondes < 60) { const secs = Math.min(59, Math.round(secondes)); return secs + 's'; } //Si moins d'une heure s'est écoulée else if (minutes < 60) { const mins = Math.min(59, Math.round(minutes)); return mins + 'm'; } //Si moins d'un jour s'est écoulé else if (heures < 24) { //Convertir les décimales des heures en minutes arrondies const heuresArrondies = Math.min(23, Math.floor(heures)); let minutesRestantes = Math.round((heures - heuresArrondies) * 60); if (minutesRestantes === 60) { minutesRestantes = 59; } return heuresArrondies + 'h ' + minutesRestantes + 'm'; } //Si un ou plusieurs jours se sont écoulés else { //Convertir les décimales des jours en heures arrondies const joursArrondis = Math.floor(jours); const heuresRestantes = Math.round((jours - joursArrondis) * 24); return joursArrondis + 'j ' + heuresRestantes + 'h'; } } //Fonction pour ajouter l'étiquette de temps à chaque produit function ajouterEtiquetteTemps() { const produits = document.querySelectorAll('.vvp-item-tile'); produits.forEach(produit => { const asin = getProductAsin(produit); const storedProducts = getStoredProducts(); if (storedProducts.hasOwnProperty(asin)) { const dateAjout = storedProducts[asin].dateAdded; const texteTempsEcoule = formaterTempsEcoule(dateAjout); //Sélectionner l'image dans le conteneur général const image = produit.querySelector('.vvp-item-tile-content img'); //Créer un wrapper pour l'image const wrapper = document.createElement('div'); wrapper.style.position = 'relative'; wrapper.style.display = 'inline-block'; //Insérer le wrapper à la place de l'image, puis y déplacer l'image image.parentNode.insertBefore(wrapper, image); wrapper.appendChild(image); //Créer l'étiquette de temps const etiquetteTemps = document.createElement('div'); etiquetteTemps.style.position = 'absolute'; if (mobileEnabled || cssEnabled) { etiquetteTemps.style.top = timeVerticalMobile; etiquetteTemps.style.left = timeHorizontalMobile; if (isMobile()) { etiquetteTemps.style.padding = '2px 3px'; } else { etiquetteTemps.style.padding = '0px 1px'; } etiquetteTemps.style.lineHeight = '1.2'; } else { etiquetteTemps.style.top = timeVertical; etiquetteTemps.style.left = timeHorizontal; etiquetteTemps.style.padding = '1px 2px'; } etiquetteTemps.style.transform = 'translateX(-50%)'; etiquetteTemps.style.backgroundColor = 'rgba(255,255,255,0.7)'; etiquetteTemps.style.color = 'black'; etiquetteTemps.style.borderRadius = '5px'; etiquetteTemps.style.zIndex = '5'; if (cssEnabled || mobileEnabled) { etiquetteTemps.style.fontSize = timeFontMobile; } else { etiquetteTemps.style.fontSize = timeFont; } etiquetteTemps.style.whiteSpace = 'nowrap'; etiquetteTemps.textContent = texteTempsEcoule; //Ajouter l'étiquette dans le wrapper de l'image wrapper.appendChild(etiquetteTemps); } }); } //Affichage d'une image agrandie lors du clic sur un produit function openImageOverlay(imgSrc) { const largeSrc = imgSrc.replace(/_SS\d+_/, '_SS500_'); const overlay = document.createElement('div'); overlay.id = 'pm-image-overlay'; overlay.style.position = 'fixed'; overlay.style.top = '0'; overlay.style.left = '0'; overlay.style.width = '100%'; overlay.style.height = '100%'; overlay.style.backgroundColor = 'rgba(0,0,0,0.8)'; overlay.style.display = 'flex'; overlay.style.alignItems = 'center'; overlay.style.justifyContent = 'center'; overlay.style.zIndex = '10000'; const img = document.createElement('img'); img.src = largeSrc; img.style.maxWidth = '90%'; img.style.maxHeight = '90%'; const closeBtn = document.createElement('span'); closeBtn.textContent = '✕'; closeBtn.style.position = 'absolute'; closeBtn.style.top = '20px'; closeBtn.style.right = '30px'; closeBtn.style.fontSize = '30px'; closeBtn.style.color = '#fff'; closeBtn.style.cursor = 'pointer'; closeBtn.addEventListener('click', () => overlay.remove()); overlay.appendChild(img); overlay.appendChild(closeBtn); overlay.addEventListener('click', (e) => { if (e.target === overlay) overlay.remove(); }); document.body.appendChild(overlay); } //Rendre les images des produits cliquables pour les afficher en plus grand function rendreImagesCliquables() { if (zoomEnabled) { const selectors = [ '.vvp-item-tile-content img:first-child', //AI, AFA, RFY '.vvp-orders-table--image-col img:first-child', //Commandes '.vvp-reviews-table--image-col img:first-child', //Avis '#favorisContainer .vvp-orders-table--image-col img:first-child' //Favoris ]; selectors.forEach(sel => { document.querySelectorAll(sel).forEach(img => { if (img.dataset.pmClickable) return; //Évite de lier plusieurs fois img.style.cursor = 'zoom-in'; img.addEventListener('click', () => openImageOverlay(img.src)); img.dataset.pmClickable = 'true'; }); }); } } //Observer les changements du DOM pour rendre cliquables les nouvelles images document.addEventListener('DOMContentLoaded', () => { rendreImagesCliquables(); const observer = new MutationObserver(rendreImagesCliquables); observer.observe(document.body, { childList: true, subtree: true }); }); //Variable pour savoir s'il y a eu un nouvel objet var imgNew = false; let shouldActivateRefreshBoost = false; if ((autohideEnabled || extendedEnabled) && apiOk) { function tryAutoHideAndExtend() { if (autohideEnabled) { var favWordsTrim = favWords.trim(); var hideWordsTrim = hideWords.trim(); //Conversion en regex var favArray = favWordsTrim.length > 0 ? favWordsTrim.split(',').map(pattern => { pattern = pattern.trim(); if (pattern.length > 0) { try { return new RegExp(pattern, 'i'); } catch (e) { console.error('Expression regex invalide :', pattern, e); return null; } } else { return null; } }).filter(regex => regex != null) : []; var hideArray = hideWordsTrim.length > 0 ? hideWordsTrim.split(',').map(pattern => { pattern = pattern.trim(); if (pattern.length > 0) { try { return new RegExp(pattern, 'i'); } catch (e) { console.error('Expression regex invalide :', pattern, e); return null; } } else { return null; } }).filter(regex => regex != null) : []; } const itemTiles = document.querySelectorAll('.vvp-item-tile'); if (itemTiles.length > 0) { itemTiles.forEach(function(tile) { const fullTextElement = tile.querySelector('.a-truncate-full.a-offscreen'); const cutTextElement = tile.querySelector('.a-truncate-cut'); const truncateTextElement = tile.querySelector('.a-truncate'); const parentDiv = tile.closest('.vvp-item-tile'); if (!fullTextElement) return; //On vérifie que l'élément contenant le texte existe const textContent = fullTextElement.textContent.trim().replace(/\s+/g, ' '); //Fonction qui surligne le mot correspondant dans le texte function highlightMatch(regexArray, highlightStyle) { for (let regex of regexArray) { const match = textContent.match(regex); if (match) { //Remplace toutes les occurrences (insensible à la casse) par le même match enveloppé dans un span const highlightedHTML = fullTextElement.textContent.replace(new RegExp(regex.source, 'gi'), `$&`); cutTextElement.innerHTML = highlightedHTML; fullTextElement.innerHTML = highlightedHTML; break; } } } if (extendedEnabled) { if (fullTextElement && cutTextElement && fullTextElement.textContent) { if (!cssEnabled) { cutTextElement.textContent = fullTextElement.textContent; fullTextElement.innerHTML = fullTextElement.textContent; //Appliquez les styles directement pour surmonter les restrictions CSS cutTextElement.style.cssText = 'height: auto !important; max-height: none !important; overflow: visible !important; white-space: normal !important;'; } else { document.addEventListener('mouseover', function(event) { const target = event.target.closest('.vvp-item-product-title-container'); if (target) { const fullTextElement = target.querySelector('.a-truncate-full.a-offscreen'); if (fullTextElement) { const fullText = fullTextElement.textContent; const popup = document.createElement('div'); popup.textContent = fullText; popup.style.position = 'fixed'; popup.style.maxWidth = '300px'; popup.style.wordWrap = 'break-word'; if (savedTheme == "dark") { popup.style.backgroundColor = '#fff'; popup.style.color = 'rgba(0, 0, 0, 0.8)'; } else { popup.style.backgroundColor = 'rgb(25, 25, 25)'; popup.style.color = '#fff'; } popup.style.padding = '5px 10px'; popup.style.borderRadius = '5px'; popup.style.zIndex = '1000'; popup.style.pointerEvents = 'none'; document.body.appendChild(popup); const movePopup = (e) => { popup.style.top = `${e.clientY + 10}px`; popup.style.left = `${e.clientX + 10}px`; }; movePopup(event); document.addEventListener('mousemove', movePopup); const removePopup = () => { popup.remove(); document.removeEventListener('mousemove', movePopup); target.removeEventListener('mouseleave', removePopup); }; target.addEventListener('mouseleave', removePopup); } } }); } } if (!cssEnabled) { if (fullTitleLine != '4') { let maxHeightMult = 1.4; let heightMult = 17.5; if (mobileEnabled) { maxHeightMult = 1.35; heightMult = 14.5; } const fullTitleLineInt = parseInt(fullTitleLine, 10); const maxHeight = fullTitleLineInt * maxHeightMult; const height = fullTitleLineInt * heightMult; document.querySelectorAll('.vvp-item-tile .a-truncate').forEach(function(element) { element.style.cssText = `max-height: ${maxHeight}em !important;`; }); document.querySelectorAll('#vvp-items-grid .vvp-item-tile .vvp-item-tile-content > .vvp-item-product-title-container').forEach(function(element) { element.style.height = `${height}px`; }); } else { if (mobileEnabled) { document.querySelectorAll('.vvp-item-tile .a-truncate').forEach(function(element) { element.style.cssText = 'max-height: 5em !important;'; }); } else { document.querySelectorAll('.vvp-item-tile .a-truncate').forEach(function(element) { element.style.cssText = 'max-height: 5.6em !important;'; }); } } } } if (autohideEnabled) { //Vérification favoris if (favArray.length > 0 && favArray.some(regex => regex.test(textContent))) { parentDiv.style.backgroundColor = highlightColorFav; parentDiv.parentNode.prepend(parentDiv); parentDiv.classList.add('putproduct'); if (hlFav) { highlightMatch(favArray, `background-color: ${colorHlFav};`); } } //Vérification pour cacher else if (hideArray.length > 0 && hideArray.some(regex => regex.test(textContent))) { const asin = parentDiv.getAttribute('data-asin') || parentDiv.querySelector('.'+getStringDetailsBtnSelector()+' input').getAttribute('data-asin'); const enrollment = getEnrollment(parentDiv); const hideKey = getAsinEnrollment(asin, enrollment); const etatCacheKey = hideKey + '_c'; localStorage.setItem(etatCacheKey, '1'); parentDiv.style.display = 'none'; if (hlHide) { highlightMatch(hideArray, `background-color: ${colorHlHide};`); } } } }); if (hideEnabled && autohideEnabled) { ajouterIconeEtFonctionCacher(); } rendreImagesCliquables(); } //On signifie que le script a fini son action la plus "longue" pour les actions de fin allFinish = true; } //On instancie le MutationObserver et on définit la fonction de callback const observer = new MutationObserver(mutations => { //À chaque mutation, on vérifie s’il y a au moins un .vvp-item-tile const itemTiles = document.querySelectorAll('.vvp-item-tile'); if (itemTiles.length > 0) { setTimeout(tryAutoHideAndExtend, extendedDelay); //Si on veut n’exécuter cette logique qu’une fois, on peut stopper l’observation : observer.disconnect(); } }); //On lance l’observation sur le document entier ou sur un conteneur spécifique observer.observe(document.body, { childList: true, subtree: true }); } //Fonction pour parcourir et convertir les favoris de PickMe Web en localstorage function convertGMFav() { //Récupérer toutes les clés stockées avec GM_setValue let keys = GM_listValues(); keys.forEach(key => { //Vérifier si la clé se termine par "_f" if (key.endsWith('_f')) { //Récupérer la valeur correspondante let value = GM_getValue(key); //Stocker la valeur dans le localStorage localStorage.setItem(key, value); //Supprimer la valeur de GM GM_deleteValue(key); } }); } function ensureHideButtonStyles() { if (document.querySelector('style[data-pm-hide-buttons]')) { return; } const style = document.createElement('style'); style.dataset.pmHideButtons = '1'; style.textContent = ` .bouton-reset { background-color: #f7ca00; color: black; font-weight: bold; text-decoration: none; display: inline-block; border: 1px solid #dcdcdc; border-radius: 20px; padding: 3px 10px; margin-left: 5px; cursor: pointer; outline: none; } `; style.textContent += ` .bouton-action { background-color: #f7ca00; color: black; font-weight: bold; text-decoration: none; display: inline-block; border: 1px solid #dcdcdc; border-radius: 20px; padding: 5px 15px; margin-right: 5px; cursor: pointer; outline: none; } .bouton-action:disabled, .bouton-action[aria-disabled="true"] { background-color: #dcdcdc; color: #888 !important; border-color: #c0c0c0; cursor: not-allowed; } .navigation-buttons { display: inline-flex; gap: 5px; margin-left: 5px; flex-wrap: wrap; align-items: center; } .navigation-buttons-mobile { display: flex; margin-left: 0; margin-top: 5px; gap: 5px; width: 100%; } `; document.head.appendChild(style); } function ajouterIconeEtFonctionCacher() { convertGMFav(); const produits = document.querySelectorAll('.vvp-item-tile'); const resultats = document.querySelector('#vvp-items-grid-container > p'); const vineGrid = document.querySelector('#vvp-items-grid'); const urlParams = new URLSearchParams(window.location.search); let infoQueue = urlParams.get('queue'); const hideNavigationActive = hidePageNavigateEnabled && (infoQueue === 'encore' || infoQueue === 'all_items'); const isMobileLayout = isMobile(); function normaliserTexte(texte) { return (texte || '') .normalize('NFD') .replace(/[\u0300-\u036f]/g, '') .replace(/\s+/g, ' ') .trim() .toLowerCase(); } function trouverLienPagination(type) { const recherche = type === 'next' ? 'suivant' : 'precedent'; const selecteurs = [ '.a-pagination a[href*="/vine/vine-items"]', '.a-pagination li a[href*="/vine/vine-items"]', 'li a[href*="/vine/vine-items"]', 'a[href*="/vine/vine-items"]' ]; for (const selecteur of selecteurs) { const liens = Array.from(document.querySelectorAll(selecteur)); const lien = liens.find(element => normaliserTexte(element.textContent).includes(recherche)); if (lien) { return lien; } } return null; } function recupererLiensPagination() { return { precedent: trouverLienPagination('previous'), suivant: trouverLienPagination('next') }; } //Ajout du style pour les boutons ensureHideButtonStyles(); //Icone pour cacher/montrer const urlIcone = hideUrlOff; const urlIconeOeil = hideUrlOn; //Création des boutons avec le nouveau style function creerBoutons(position) { //Bouton pour les produits visibles const boutonVisibles = document.createElement('button'); boutonVisibles.textContent = produitsVisibles; boutonVisibles.classList.add('bouton-filtre', 'active'); boutonVisibles.id = `boutonVisibles${position}`; //Bouton pour les produits cachés const boutonCaches = document.createElement('button'); boutonCaches.textContent = produitsCaches; boutonCaches.classList.add('bouton-filtre'); boutonCaches.id = `boutonCaches${position}`; //Bouton pour cacher tout const boutonCacherTout = document.createElement('button'); boutonCacherTout.textContent = toutCacher; boutonCacherTout.classList.add('bouton-action'); boutonCacherTout.id = `boutonCacherTout${position}`; //Bouton pour tout afficher const boutonToutAfficher = document.createElement('button'); boutonToutAfficher.textContent = toutAfficher; boutonToutAfficher.classList.add('bouton-action'); boutonToutAfficher.id = `boutonToutAfficher${position}`; let boutonCacherPrecedent = null; let boutonRetourAccueil = null; let boutonCacherSuivant = null; let navigationWrapper = null; if (hideNavigationActive) { navigationWrapper = document.createElement(isMobileLayout ? 'div' : 'span'); navigationWrapper.classList.add('navigation-buttons'); if (isMobileLayout) { navigationWrapper.classList.add('navigation-buttons-mobile'); } if (hidePagePreviousEnabled) { boutonCacherPrecedent = document.createElement('button'); boutonCacherPrecedent.textContent = '⏮'; boutonCacherPrecedent.classList.add('bouton-action'); boutonCacherPrecedent.id = `boutonCacherPrecedent${position}`; boutonCacherPrecedent.title = 'Tout cacher puis revenir à la page précédente'; boutonCacherPrecedent.setAttribute('aria-label', 'Tout cacher puis revenir à la page précédente'); navigationWrapper.appendChild(boutonCacherPrecedent); } boutonRetourAccueil = document.createElement('button'); boutonRetourAccueil.textContent = '↩'; boutonRetourAccueil.classList.add('bouton-action'); boutonRetourAccueil.id = `boutonRetourAccueil${position}`; boutonRetourAccueil.title = 'Tout cacher puis revenir à la première page'; boutonRetourAccueil.setAttribute('aria-label', 'Tout cacher puis revenir à la première page'); navigationWrapper.appendChild(boutonRetourAccueil); boutonCacherSuivant = document.createElement('button'); boutonCacherSuivant.textContent = '⏭'; boutonCacherSuivant.classList.add('bouton-action'); boutonCacherSuivant.id = `boutonCacherSuivant${position}`; boutonCacherSuivant.title = 'Tout cacher puis passer à la page suivante'; boutonCacherSuivant.setAttribute('aria-label', 'Tout cacher puis passer à la page suivante'); navigationWrapper.appendChild(boutonCacherSuivant); if (!navigationWrapper.childElementCount) { navigationWrapper = null; } } return { boutonVisibles, boutonCaches, boutonCacherTout, boutonToutAfficher, boutonCacherPrecedent, boutonRetourAccueil, boutonCacherSuivant, navigationWrapper }; } //Fonction pour synchroniser les boutons haut et bas function synchroniserBoutons(boutonsHaut, boutonsBas, hideBas) { //Synchronisation du bouton "Produits visibles" boutonsHaut.boutonVisibles.addEventListener('click', () => { afficherProduits(true); boutonsHaut.boutonVisibles.classList.add('active'); boutonsHaut.boutonCaches.classList.remove('active'); if (hideBas) { boutonsBas.boutonVisibles.classList.add('active'); boutonsBas.boutonCaches.classList.remove('active'); } }); if (hideBas) { boutonsBas.boutonVisibles.addEventListener('click', () => { afficherProduits(true); boutonsHaut.boutonVisibles.classList.add('active'); boutonsHaut.boutonCaches.classList.remove('active'); }); } //Synchronisation du bouton "Produits cachés" boutonsHaut.boutonCaches.addEventListener('click', () => { afficherProduits(false); boutonsHaut.boutonVisibles.classList.remove('active'); boutonsHaut.boutonCaches.classList.add('active'); if (hideBas) { boutonsBas.boutonVisibles.classList.remove('active'); boutonsBas.boutonCaches.classList.add('active'); } }); if (hideBas) { boutonsBas.boutonCaches.addEventListener('click', () => { afficherProduits(false); boutonsHaut.boutonVisibles.classList.remove('active'); boutonsHaut.boutonCaches.classList.add('active'); }); } //Synchronisation des boutons "Tout cacher" et "Tout afficher" boutonsHaut.boutonCacherTout.addEventListener('click', () => { toggleTousLesProduits(true); boutonsHaut.boutonCacherTout.style.display = ''; boutonsHaut.boutonToutAfficher.style.display = 'none'; if (hideBas) { boutonsBas.boutonCacherTout.style.display = ''; boutonsBas.boutonToutAfficher.style.display = 'none'; } }); if (hideBas) { boutonsBas.boutonCacherTout.addEventListener('click', () => { toggleTousLesProduits(true); boutonsHaut.boutonCacherTout.style.display = ''; boutonsHaut.boutonToutAfficher.style.display = 'none'; }); } boutonsHaut.boutonToutAfficher.addEventListener('click', () => { toggleTousLesProduits(false); boutonsHaut.boutonCacherTout.style.display = 'none'; boutonsHaut.boutonToutAfficher.style.display = ''; if (hideBas) { boutonsBas.boutonCacherTout.style.display = 'none'; boutonsBas.boutonToutAfficher.style.display = ''; } }); if (hideBas) { boutonsBas.boutonToutAfficher.addEventListener('click', () => { toggleTousLesProduits(false); boutonsHaut.boutonCacherTout.style.display = 'none'; boutonsHaut.boutonToutAfficher.style.display = ''; }); } } //Création et insertion des boutons en haut et en bas const boutonsHaut = creerBoutons('Haut'); const divBoutonsHaut = document.createElement('div'); divBoutonsHaut.id = "divCacherHaut"; divBoutonsHaut.style.marginTop = '5px'; //Réduit l'espace au-dessus des boutons divBoutonsHaut.style.marginBottom = '15px'; //Augmente l'espace en dessous des boutons divBoutonsHaut.appendChild(boutonsHaut.boutonVisibles); divBoutonsHaut.appendChild(boutonsHaut.boutonCaches); divBoutonsHaut.appendChild(boutonsHaut.boutonCacherTout); divBoutonsHaut.appendChild(boutonsHaut.boutonToutAfficher); if (boutonsHaut.navigationWrapper) { divBoutonsHaut.appendChild(boutonsHaut.navigationWrapper); } if (resultats) { resultats.after(divBoutonsHaut); } else if (vineGrid) { vineGrid.before(divBoutonsHaut); } const boutonsBas = creerBoutons('Bas'); const divBoutonsBas = document.createElement('div'); if (cssEnabled) { divBoutonsBas.style.marginTop = '15px'; } else { divBoutonsBas.style.marginTop = '5px'; //Réduit l'espace au-dessus des boutons } divBoutonsBas.style.marginBottom = '15px'; //Augmente l'espace en dessous des boutons divBoutonsBas.appendChild(boutonsBas.boutonVisibles); divBoutonsBas.appendChild(boutonsBas.boutonCaches); divBoutonsBas.appendChild(boutonsBas.boutonCacherTout); divBoutonsBas.appendChild(boutonsBas.boutonToutAfficher); if (boutonsBas.navigationWrapper) { divBoutonsBas.appendChild(boutonsBas.navigationWrapper); } if (vineGrid && hideBas) { vineGrid.after(divBoutonsBas); } //Synchronisation des boutons haut et bas synchroniserBoutons(boutonsHaut, boutonsBas, hideBas); if (hideNavigationActive) { const liensPagination = recupererLiensPagination(); const urlPremierePage = (() => { const urlActuelle = new URL(window.location.href); const pageParam = urlActuelle.searchParams.get('page'); if (!pageParam || pageParam === '1') { if (urlActuelle.searchParams.has('page')) { urlActuelle.searchParams.delete('page'); const nouvelleUrl = urlActuelle.toString(); return nouvelleUrl !== window.location.href ? nouvelleUrl : null; } return null; } urlActuelle.searchParams.delete('page'); return urlActuelle.toString(); })(); const appliquerEtatApresCacher = () => { const afficherVisiblesActuels = !boutonsHaut.boutonCaches.classList.contains('active'); const afficherBoutonCacher = lockProductTab ? afficherVisiblesActuels : true; const afficherBoutonToutAfficher = lockProductTab ? !afficherVisiblesActuels : false; boutonsHaut.boutonCacherTout.style.display = afficherBoutonCacher ? '' : 'none'; boutonsHaut.boutonToutAfficher.style.display = afficherBoutonToutAfficher ? '' : 'none'; if (hideBas) { boutonsBas.boutonCacherTout.style.display = afficherBoutonCacher ? '' : 'none'; boutonsBas.boutonToutAfficher.style.display = afficherBoutonToutAfficher ? '' : 'none'; } }; const desactiverBoutonNavigation = (bouton) => { if (!bouton) { return; } bouton.disabled = true; bouton.setAttribute('aria-disabled', 'true'); if (bouton.title && !bouton.title.includes('indisponible depuis cette page')) { bouton.title = `${bouton.title} (indisponible depuis cette page)`; } }; const attacherNavigation = (bouton, lien) => { if (!bouton) { return; } if (!lien) { desactiverBoutonNavigation(bouton); return; } bouton.addEventListener('click', () => { toggleTousLesProduits(true); appliquerEtatApresCacher(); window.location.href = lien.href; }); }; const attacherRetourAccueil = (bouton, urlCible) => { if (!bouton) { return; } if (!urlCible) { desactiverBoutonNavigation(bouton); return; } bouton.addEventListener('click', () => { toggleTousLesProduits(true); appliquerEtatApresCacher(); window.location.href = urlCible; }); }; attacherNavigation(boutonsHaut.boutonCacherPrecedent, liensPagination.precedent); attacherRetourAccueil(boutonsHaut.boutonRetourAccueil, urlPremierePage); attacherNavigation(boutonsHaut.boutonCacherSuivant, liensPagination.suivant); attacherNavigation(boutonsBas.boutonCacherPrecedent, liensPagination.precedent); attacherRetourAccueil(boutonsBas.boutonRetourAccueil, urlPremierePage); attacherNavigation(boutonsBas.boutonCacherSuivant, liensPagination.suivant); } //Fonction pour cacher ou afficher tous les produits function toggleTousLesProduits(cacher) { produits.forEach(produit => { const asin = getProductAsin(produit); const enrollment = getEnrollment(produit); const hideKey = getAsinEnrollment(asin, enrollment); const etatCacheKey = hideKey + '_c'; const etatFavoriKey = asin + '_f'; //Vérifie si le produit est en favori avant de changer son état de caché const etatFavori = localStorage.getItem(etatFavoriKey) || '0'; if (etatFavori == '0') { //Ne modifie l'état de caché que si le produit n'est pas en favori localStorage.setItem(etatCacheKey, cacher ? '1' : '0'); //Sélection de l'icône d'œil dans le produit actuel et mise à jour si l'état de caché change const iconeOeil = produit.querySelector('img[src="' + urlIcone + '"], img[src="' + urlIconeOeil + '"]'); if (iconeOeil) { iconeOeil.setAttribute('src', cacher ? urlIconeOeil : urlIcone); } } }); //Force la mise à jour de l'affichage selon le nouveau statut de visibilité const afficherVisiblesActuels = !boutonsHaut.boutonCaches.classList.contains('active'); const afficherVisibles = lockProductTab ? afficherVisiblesActuels : cacher; afficherProduits(afficherVisibles); } //Affiche les produits en fonction du filtre : visible ou caché function afficherProduits(afficherVisibles) { const produitsFavoris = []; produits.forEach(produit => { const asin = getProductAsin(produit); const enrollment = getEnrollment(produit); const hideKey = getAsinEnrollment(asin, enrollment); const etatCacheKey = hideKey + '_c'; const etatFavoriKey = asin + '_f'; //Convertir de la key ASIN à la key ASIN + enrollment, à partir de la 1.14 ou après une synchro const etatCacheOldKey = asin + '_c'; const oldValue = localStorage.getItem(etatCacheOldKey); if (oldValue !== null) { localStorage.setItem(etatCacheKey, oldValue); localStorage.removeItem(etatCacheOldKey); } //Fin de conversion //Initialisation des états si non définis let etatCache = localStorage.getItem(etatCacheKey) || '0'; let etatFavori = localStorage.getItem(etatFavoriKey) || '0'; //Enregistre les valeurs par défaut si nécessaire if (localStorage.getItem(etatCacheKey) === null) { localStorage.setItem(etatCacheKey, etatCache); } if (localStorage.getItem(etatFavoriKey) === null) { localStorage.setItem(etatFavoriKey, etatFavori); } //On test s'il est favori et si on peut le cacher ou non if (etatFavori == '1') { //Les produits favoris sont toujours affichés dans l'onglet "Produits visibles" //et cachés dans l'onglet "Produits cachés" produit.style.display = afficherVisibles ? '' : 'none'; produitsFavoris.push(produit); } else { if ((etatCache == '0' && afficherVisibles) || (etatCache == '1' && !afficherVisibles)) { produit.style.display = ''; } else { produit.style.display = 'none'; } } }); const containerDiv = document.getElementById('vvp-items-grid'); //L'élément conteneur de tous les produits if (containerDiv) { produitsFavoris.reverse().forEach(element => { containerDiv.prepend(element); element.classList.add('favproduct'); }); } boutonsHaut.boutonVisibles.classList.toggle('active', afficherVisibles); //Active ou désactive le bouton des produits visibles boutonsBas.boutonVisibles.classList.toggle('active', afficherVisibles); boutonsHaut.boutonCaches.classList.toggle('active', !afficherVisibles); //Active ou désactive le bouton des produits cachés boutonsBas.boutonCaches.classList.toggle('active', !afficherVisibles); if (lockProductTab) { productTabSelection = afficherVisibles ? 'visibles' : 'caches'; GM_setValue('productTabSelection', productTabSelection); } //Gestion de l'affichage des boutons "Cacher tout" et "Tout afficher" boutonsHaut.boutonCacherTout.style.display = afficherVisibles ? '' : 'none'; boutonsBas.boutonCacherTout.style.display = afficherVisibles ? '' : 'none'; boutonsHaut.boutonToutAfficher.style.display = !afficherVisibles ? '' : 'none'; boutonsBas.boutonToutAfficher.style.display = !afficherVisibles ? '' : 'none'; if (customSortingEnabled) { sortItems(customSorting); } } produits.forEach(produit => { const image = produit.querySelector('.vvp-item-tile-content img'); const asin = getProductAsin(produit); const enrollment = getEnrollment(produit); const hideKey = getAsinEnrollment(asin, enrollment); const etatCacheKey = hideKey + '_c'; const etatFavoriKey = asin + '_f'; const iconeOeil = document.createElement('img'); let wrapper = image.parentNode; if (!wrapper.classList.contains('image-wrapper')) { const newWrapper = document.createElement('div'); newWrapper.classList.add('image-wrapper'); newWrapper.style.position = 'relative'; newWrapper.style.display = 'inline-block'; //Insertion du nouveau wrapper à la place de l'image, puis déplacement de l'image dedans wrapper.insertBefore(newWrapper, image); newWrapper.appendChild(image); wrapper = newWrapper; } const etatCache = localStorage.getItem(etatCacheKey) || '0'; iconeOeil.setAttribute('src', etatCache === '1' ? urlIconeOeil : urlIcone); if (cssEnabled || mobileEnabled) { iconeOeil.style.cssText = ` position: absolute; top: ${hideVerticalMobile}; right: ${hideHorizontalMobile}; cursor: pointer; width: ${hideSizeWidthMobile}; height: ${hideSizeHeightMobile}; z-index: 10; `; } else { iconeOeil.style.cssText = ` position: absolute; top: ${hideVertical}; right: ${hideHorizontal}; cursor: pointer; width: ${hideSizeWidth}; height: ${hideSizeHeight}; z-index: 10; `; } iconeOeil.addEventListener('click', () => { const etatFavoriKey = asin + '_f'; const etatFavori = localStorage.getItem(etatFavoriKey) || '0'; //Vérifie si le produit n'est pas marqué comme favori avant de changer son état de caché if (etatFavori === '0') { const etatCacheActuel = localStorage.getItem(etatCacheKey); const nouvelEtatCache = etatCacheActuel === '1' ? '0' : '1'; localStorage.setItem(etatCacheKey, nouvelEtatCache); //Met à jour l'icône basée sur le nouvel état après le clic iconeOeil.setAttribute('src', etatCacheActuel === '1' ? urlIcone : urlIconeOeil); } //Force la mise à jour de l'affichage selon l'état actuel des filtres afficherProduits(!boutonsHaut.boutonCaches.classList.contains('active')); }); const urlIconeFavoriGris = favUrlOff; const urlIconeFavoriRouge = favUrlOn; const iconeFavori = document.createElement('img'); const etatFavori = localStorage.getItem(etatFavoriKey); iconeFavori.setAttribute('src', (etatFavori && etatFavori == '1') ? urlIconeFavoriRouge : urlIconeFavoriGris); if (cssEnabled || mobileEnabled) { iconeFavori.style.cssText = ` position: absolute; top: ${favVerticalMobile}; left: ${favHorizontalMobile}; cursor: pointer; width: ${favSizeMobile}; height: ${favSizeMobile}; z-index: 10; `; } else { iconeFavori.style.cssText = ` position: absolute; top: ${favVertical}; left: ${favHorizontal}; cursor: pointer; width: ${favSize}; height: ${favSize}; z-index: 10; `; } //Gestion du clic sur l'icône de favori iconeFavori.addEventListener('click', () => { var etatFavoriActuel = localStorage.getItem(etatFavoriKey) || '0'; etatFavoriActuel = etatFavoriActuel === '1' ? '0' : '1'; localStorage.setItem(etatFavoriKey, etatFavoriActuel); iconeFavori.setAttribute('src', etatFavoriActuel === '1' ? urlIconeFavoriRouge : urlIconeFavoriGris); produit.classList.toggle('favproduct'); if (etatFavoriActuel === '1') { //Si le produit est marqué comme favori, s'assurer qu'il est marqué comme non caché localStorage.setItem(etatCacheKey, '0'); produit.style.display = ''; //Assure que le produit est visible //Mettre à jour l'icône de l'œil pour refléter que le produit n'est plus caché const iconeOeil = produit.querySelector('img[src="' + urlIcone + '"], img[src="' + urlIconeOeil + '"]'); if (iconeOeil) { iconeOeil.setAttribute('src', urlIcone); } } afficherProduits(!boutonsHaut.boutonCaches.classList.contains('active')); }); wrapper.appendChild(iconeOeil); wrapper.appendChild(iconeFavori); }); //Initialisation de l'affichage par défaut à l'onglet choisi précédemment si l'option est activée const afficherVisiblesParDefaut = lockProductTab ? productTabSelection !== 'caches' : true; afficherProduits(afficherVisiblesParDefaut); } if (hideEnabled && apiOk && !autohideEnabled) { //Appeler la fonction pour ajouter les étiquettes de temps ajouterIconeEtFonctionCacher(); } //Exécuter la fonction pour ajouter les icônes et les fonctionnalités de cacher if (highlightEnabled && apiOk) { //Appeler la fonction pour ajouter les étiquettes de temps ajouterEtiquetteTemps(); } rendreImagesCliquables(); //Suppression footer var styleFooter = document.createElement('style'); styleFooter.textContent = ` /* === Ancien footer Amazon === */ #rhf, #rhf-shoveler, .rhf-frame, #navFooter, footer.nav-mobile.nav-ftr-batmobile { display: none !important; } /* === Nouveau footer Amazon (2025) === */ footer.nav-bb-footer, footer.nav-bb-footer-mobile, #nav-ftr { display: none !important; } ` document.head.appendChild(styleFooter); //Nombre de colonnes fixe if (apiOk && columnEnabled) { const style = document.createElement('style'); style.innerHTML = ` #vvp-items-grid { display: grid !important; grid-template-columns: repeat(${nbColumn}, 1fr) !important; } `; document.head.appendChild(style); } //Agrandir la fenetre des adresses if (fastCmdEnabled && apiOk) { var styleAddress = document.createElement('style'); styleAddress.textContent = ` #a-popover-4 { height: 480px !important; width: 900px !important; } ` document.head.appendChild(styleAddress); } //Pour monter la valeur de la taxe if (taxValue && apiOk) { //Créez une balise