// ==UserScript== // @name Amazon a Pesos Argentinos // @namespace sharkiller-amazon-dolar-tarjeta // @description Mostrar precios en dolar tarjeta // @version 3.2 // @require http://code.jquery.com/jquery-latest.js // @require https://cdnjs.cloudflare.com/ajax/libs/mustache.js/2.2.1/mustache.min.js // @include *amazon.com/* // @include *amazon.co.uk/* // @include *amazon.ca/* // @include *amazon.es/* // @include *amazon.de/* // @include *amazon.fr/* // @include *amazon.it/* // @connect amazon.com // @connect amazon.co.uk // @connect amazon.ca // @connect amazon.es // @connect amazon.de // @connect amazon.fr // @connect amazon.it // @connect yahoo.com // @connect europa.eu // @connect mercadolibre.com // @grant GM_getValue // @grant GM_setValue // @grant GM_xmlhttpRequest // @grant GM_addStyle // @downloadURL none // ==/UserScript== (function() { if( typeof jQuery === 'undefined' ){ return false; } GM_addStyle('.price-ars{color:green;display:inline-block;margin:0 8px;background:linear-gradient(to bottom,#add8e6,#fff,#add8e6);border-radius:8px;padding:0 6px}'+ '#sc-buy-box .price-ars{font-size:12px;display:block;padding:0;margin:0;background:0 0}div#sc-buy-box{margin-bottom:-14px}'); GM_addStyle('#snoop-icon{margin-left:4px;width:30px;height:20px}#snoop-placeholder{width:32px}div.snoop-loader{width:13px;height:13px}div.snoop-tooltip,div.snoop-tooltip-ml{display:none;font-size:13px;border-image:none;letter-spacing:0}'+ 'div.snoop-tooltip div.entry,div.snoop-tooltip-ml div.entry{padding-top:5px}span.snoop-price{color:#900;font-size:1em;font-weight:700}span.snoop-warning{color:#C60!important}.snoop-not-found{text-decoration:line-through}'+ 'span.snoop-on{color:#000;font-weight:400}div.snoop-credits{font-size:.75em;font-style:italic;text-align:center;margin-top:13px;font-weight:400;color:#666}span.back-link{line-height:0;margin-left:1px;top:-10px;position:relative}'+ 'span.back-link>img{top:1;position:relative}span.back-link>a{font-size:1em;text-decoration:none!important}#snoop-icon-ml{margin-left:10px}'); String.prototype.endsWith = function (pattern) { var d = this.length - pattern.length; return d >= 0 && this.lastIndexOf(pattern) === d; }; var amazonCurrencies = ["USD", "GBP", "CAD", "EUR"]; var currencyFrom; var currencies = { "USD" : { symbol: "$", priceRegex: /\$\s*([\d,.]+\d)/, vat: false }, "GBP" : { symbol: "£", priceRegex: /£\s*([\d,.]+\d)/, vat: true }, "CAD" : { symbol: "CDN$ ", priceRegex: /CDN\$\s*([\d,.]+\d)/, vat: false }, "EUR" : { symbol: "EUR ", priceRegex: /EUR\s*([\s\d,.]+\d)/, vat: true } }; if (document.domain.endsWith("com")) { currencyFrom = "USD"; } else if (document.domain.endsWith("co.uk")) { currencyFrom = "GBP"; } else if (document.domain.endsWith("ca")) { currencyFrom = "CAD"; } else if ( document.domain.endsWith("es") || document.domain.endsWith("de") || document.domain.endsWith("fr") || document.domain.endsWith("it") ) { currencyFrom = "EUR"; } else { return; } var LAST_RUN = "last_run_"; var CURRENCY_RATE = "currency_rate_"; var decimalPlaces = 2; var prefixCurrencySymbol = true; var taxPorcentage = 5; var rounding = Math.pow(10, decimalPlaces); var rate = GM_getValue(CURRENCY_RATE + currencyFrom); var lastRun = GM_getValue(LAST_RUN + currencyFrom, "01/01/0001"); var currencyTo = 'ARS'; var todayDate = new Date(); var todayString = todayDate.getDate() + "/" + todayDate.getMonth() + "/" + todayDate.getFullYear(); var currencyToSymbol = 'ARS $'; function regularPriceParser(price, currency) { price = price.replace(/\s/g, ''); var regex = /^[\d.]+,[\d]{2}$/; var needProperPrice = regex.exec(price); if(needProperPrice) { price = price.replace(/\./g, '').replace(/,/, '.'); }else{ price = price.replace(/\,/g, ''); } return parseFloat(price); } function fetchCurrencyData(coin, callback) { GM_xmlhttpRequest({ method: "GET", url: "http://download.finance.yahoo.com/d/quotes.csv?s=" + coin + currencyTo + "=X&f=l1&e=.csv", onload: function(responseDetails) { var rate = responseDetails.responseText.replace(/[\r\n]/g, ""); GM_setValue(CURRENCY_RATE + coin, rate); GM_setValue(LAST_RUN + coin, todayString); callback(); }, onerror: function(responseDetails) { alert("Error fetching currency data for " + coin); } }); } function appendConversionNoVAT(price, matched, offset, string) { return appendConversion(price, matched, offset, string, true); } function appendConversion(price, matched, offset, string, novat) { var originalPrice = regularPriceParser(matched, currencyFrom); if (isNaN(originalPrice)) { return price; } if(currencies[currencyFrom].vat && novat === undefined){ originalPrice /= 1.2; } var converted = formatCurrency(originalPrice * (rate * (taxPorcentage / 100 + 1)), rounding, currencyToSymbol, prefixCurrencySymbol); if(currencies[currencyFrom].vat && novat === undefined){ originalPrice = formatCurrency(originalPrice, rounding, currencies[currencyFrom].symbol, prefixCurrencySymbol); return '' + originalPrice + '
' + converted + '
'; }else{ return price + '
' + converted + '
'; } } function formatCurrency(num, rounding, symbol, prefix) { sign = (num == (num = Math.abs(num))); num = Math.floor(num * rounding + 0.50000000001); cents = num % rounding; num = Math.floor(num / rounding).toString(); if (cents < 10) { cents = "0" + cents; } for (var i = 0; i < Math.floor((num.length - (1 + i)) / 3); i++) { num = num.substring(0, num.length - (4 * i + 3)) + ',' + num.substring(num.length-(4*i+3)); } if (prefix) { return (symbol + ((sign)?'':'-') + num + '.' + cents); } else { return (((sign)?'':'-') + num + '.' + cents + symbol); } } convertCurrency = function(carrito) { switch(carrito){ case 1: if( jQuery('.a-size-base.a-text-bold:contains(Order summary)').length === 0 && jQuery('.a-size-base.a-text-bold:contains(Récapitulatif de commande)').length === 0 && jQuery('.a-size-base.a-text-bold:contains(Riepilogo ordine)').length === 0 && jQuery('.a-size-base.a-text-bold:contains(Resumen del pedido)').length === 0 && jQuery('.a-size-base.a-text-bold:contains(Zusammenfassung der Bestellung)').length === 0 && jQuery('.ap_popover.ap_popover_sprited.snoop-tooltip').length === 0 ){ return false; }else{ if(jQuery('#sc-buy-box.price-ars-cart').length === 0){ jQuery('#sc-buy-box').addClass('price-ars-cart'); }else{ return false; } } break; case 2: if( jQuery('.entry.snoop-price:not(.snoop-warning):not(.price-ars-cart)').length === 0 ){ return false; } break; default: break; } //console.log('Cambiando precios', carrito); var currency = currencies[currencyFrom]; jQuery('.a-column.a-span3.a-text-right.a-span-last.sc-value').removeClass('a-span3').addClass('a-span4'); jQuery('.a-column.a-span9.sc-text').removeClass('a-span9').addClass('a-span8'); jQuery('a.a-popover-trigger.a-declarative:contains(Estimated)').html('Shipping & handling '); if(carrito == 2){ jQuery('.entry.snoop-price:not(.snoop-warning)').each(function(){ var text = jQuery(this).text(); if( jQuery(this).children().length === 0 && text.indexOf(currency.symbol) != -1 && text.indexOf('ARS') == -1){ jQuery(this).addClass('price-ars-cart').html( text.replace(currency.priceRegex, appendConversion) ); } }); }else{ jQuery('span:not(:has(*))').each(function(){ var text = jQuery(this).text(); if( jQuery(this).children().length === 0 && text.indexOf(currency.symbol) != -1 && text.indexOf('ARS') == -1){ if( jQuery(this).parents('#sc-buy-box').length == 1 || window.location.href.indexOf("/order-details") > -1 || window.location.href.indexOf("/order-history") > -1 ){ jQuery(this).html( text.replace(currency.priceRegex, appendConversionNoVAT) ); }else{ jQuery(this).html( text.replace(currency.priceRegex, appendConversion) ); } } }); } }; var running_convert_1 = false; var running_convert_2 = false; $( document ).ready(function(){ console.log('Page ready!'); if (rate === undefined || todayString !== lastRun) { fetchCurrencyData(currencyFrom, function() { rate = GM_getValue(CURRENCY_RATE + currencyFrom); convertCurrency(); }); } else { console.log('Currency rate: ', rate); convertCurrency(); } $('.a-fixed-right-grid-col.a-col-right').bind("DOMSubtreeModified",function(e){ if(running_convert_1 === false){ console.log('Auto 1'); running_convert_1 = true; setTimeout(function(){convertCurrency(1); running_convert_1=false;}, 1000); } }); $('#priceblock_ourprice').bind("DOMSubtreeModified",function(e){ if(running_convert_2 === false){ console.log('Auto 2'); running_convert_2 = true; setTimeout(function(){convertCurrency(2); running_convert_2=false;}, 1000); } }); /* //////////////////////////////////////////////////////////////// PRECIOS DE OTRAS TIENDAS //////////////////////////////////////////////////////////////// */ var asin = $('#ASIN').val(); if (asin === undefined) return; snoop.initialize(asin); }); })(); //Various currencies' rates to EUR window.Rates = { timestamp: new Date(), 'EUR': 1, 'USD': 0, 'GBP': 0 }; function refreshRates() { var cachedRates = JSON.parse(localStorage.getItem("conversionRates")); //console.log(cachedRates); if (cachedRates !== null && cachedRates.timestamp !== undefined) { cachedRates.timestamp = new Date(cachedRates.timestamp); Rates = cachedRates; var ageInHours = (new Date() - cachedRates.timestamp) / 1000 / 60 / 60; if (ageInHours < 7) return; } console.log('Refreshing conversion rates...'); GM_xmlhttpRequest({ method: "GET", url: 'https://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml', onload: function(responseDetails) { var $document = $(responseDetails.responseText); for (var rate in Rates) { if (typeof rate !== 'string' || rate === 'EUR') continue; Rates[rate] = parseFloat($document.find('Cube[currency="' + rate + '"]').attr('rate')); } Rates.timestamp = new Date(); localStorage.conversionRates = JSON.stringify(Rates); }, onerror: function(responseDetails) { alert("Error fetching currency rates."); } }); } var Money = function (amount, currency, currency_symbol, extraAmount) { this.amount = amount; this.extraAmount = extraAmount; this.currency = currency; this.currency_symbol = currency_symbol; //console.log('Money', this); }; Money.prototype.for = function (currency, currency_symbol) { var rate = Rates[currency] / Rates[this.currency]; var convertedAmount = this.amount * rate; var convertedExtraAmount = this.extraAmount !== undefined ? this.extraAmount * rate : undefined; //console.log('Conversion\n', this.currency, '->', currency, // '\n', Rates[this.currency], '->', Rates[currency], // '\n', this.amount, '->', convertedAmount); return new Money(convertedAmount, currency, currency_symbol, convertedExtraAmount); }; Money.prototype.toString = function () { if (this.extraAmount === undefined) return this.currency_symbol+(this.amount.toFixed(2)); else return this.currency_symbol+(this.amount.toFixed(2)) + ' - ' + this.currency_symbol+(this.extraAmount.toFixed(2)); }; var Shop = function (id, title, domain, base_url, currency, currency_symbol, seller) { this.id = id; this.title = title; this.domain = domain; this.base_url = base_url; this.url = this.base_url; this.currency = currency; this.currency_symbol = currency_symbol; this.seller = seller; this.setAsin = function (asin) { this.url = this.urlFor(asin); }; this.urlFor = function (asin) { return this.base_url.replace('{asin}', asin); }; this.moneyFrom = function (amount) { var culture = this.culture; if (amount.indexOf('-') == -1) { var sanitizedAmount = amount.replace(/[^\d^,^.]/g, ''); var regex = /^[\d.]+,[\d]{2}$/; var needProperPrice = regex.exec(sanitizedAmount); if(needProperPrice) { sanitizedAmount = sanitizedAmount.replace(/\./g, '').replace(/,/, '.'); }else{ sanitizedAmount = sanitizedAmount.replace(/\,/g, ''); } return new Money(sanitizedAmount, this.currency); } var sanitizedAmounts = amount.split('-').map(function (a) { return a.replace(/[^\d^,^.]/g, ''); }); return new Money(sanitizedAmounts[0], this.currency, sanitizedAmounts[1]); }; }; var Settings = function (asin) { this.asin = asin; this.shops = [ new Shop(1, 'amazon.co.uk', 'www.amazon.co.uk', 'https://www.amazon.co.uk/dp/{asin}?smid=A3P5ROKL5A1OLE&tag=r0f340c-21', 'GBP', '£', 'Dispatched from and sold by Amazon'), new Shop(2, 'amazon.de', 'www.amazon.de', 'https://www.amazon.de/dp/{asin}?smid=A3JWKAKR8XB7XF&tag=r0f3409-21', 'EUR', 'EUR ', 'Dispatched from and sold by Amazon|Verkauf und Versand durch Amazon'), new Shop(3, 'amazon.fr', 'www.amazon.fr', 'https://www.amazon.fr/dp/{asin}?smid=A1X6FK5RDHNB96&tag=r0f3405-21', 'EUR', 'EUR ', 'Expédié et vendu par Amazon'), new Shop(4, 'amazon.es', 'www.amazon.es', 'https://www.amazon.es/dp/{asin}?smid=A1AT7YVPFBWXBL&tag=r0f34-21', 'EUR', 'EUR ', 'Vendido y enviado por Amazon'), new Shop(5, 'amazon.it', 'www.amazon.it', 'https://www.amazon.it/dp/{asin}?smid=A11IL2PNWYJU7H&tag=r0f3401-21', 'EUR', 'EUR ', 'Venduto e spedito da Amazon'), new Shop(6, 'amazon.com', 'www.amazon.com', 'https://www.amazon.com/dp/{asin}?smid=ATVPDKIKX0DER&tag=r0f34-20', 'USD', '$', 'Ships from and sold by Amazon.com') ]; this.shops.forEach(function (shop) { shop.setAsin(asin); }); this.currentShop = this.shops.filter(function (shop) { return shop.domain == document.domain; })[0]; this.desiredCurrency = this.currentShop.currency; if (this.currentShop.currency != this.desiredCurrency) { this.filteredShops = this.shops; } else { this.filteredShops = this.shops.filter(function (shop) { return shop.domain != document.domain; }); } this.shop = function (id) { var shopById = this.shops.filter(function (shop) { return shop.id == id; }); if (shopById.length == 1) return shopById[0]; return null; }; }; var pageScraper = { warning: { networkError: 'Error al consultar', unavailable: 'No disponible', wrongSeller: 'No vendido por Amazon', notFound: 'No encontrado', multipleOptions: 'Opciones multiples' }, getPriceOn: function (shop, displayPrice, displayWarning) { var serverUrl = shop.url; var sellerText = shop.seller; GM_xmlhttpRequest({ method: "GET", url: serverUrl, onload: function(data) { var regex = /[nb]\s*?id="priceblock_[\w]*?price".*?>(.*?)
'+ '
{{#shops}}
'+ ' en {{title}}
{{/shops}}
'+ ''; var page = { addTooltipToPage: function (tooltipMarkup) { var $placeholderMarkup = $('Placeholder'); var $imageMarkup = $(''); var $imageMarkupMELI = $(''); var $container = this.findAppropriateTooltipContainer(); var tooltipTemplateML = '
'+ '
'+ '
Cargando...
'; $container.after($imageMarkupMELI); $container.after(tooltipTemplateML); $container.after($imageMarkup); $container.after(tooltipMarkup); tooltip.registerShowHideHandlers(); tooltipML.registerShowHideHandlers(); convertCurrency(); }, findAppropriateTooltipContainer: function () { var $tries = [ $('table.product .priceLarge:first', $('#priceBlock')), $('#priceblock_ourprice'), $('#priceblock_saleprice'), $('#availability_feature_div > #availability > .a-color-price'), $('div.buying span.availGreen', $('#handleBuy')), $('div.buying span.availRed:nth-child(2)', $('#handleBuy')) ]; for (var i = 0; i < $tries.length; i++) { if ($tries[i].length > 0) return $tries[i]; } throw new Error('Unable to find the price section.'); }, displayPrice: function ($shopInfo, price) { var convertedPrice = price.for(settings.currentShop.currency, settings.currentShop.currency_symbol); $shopInfo.text(convertedPrice.toString()); convertCurrency(); }, displayWarning: function ($shopInfo, warning, addNotFoundClass) { $shopInfo.text(warning).addClass('snoop-warning'); if (addNotFoundClass) $shopInfo.parent().addClass('snoop-not-found'); }, registerInitializationHandler: function (shops) { $('#snoop-icon').mouseover(function () { if (window.snoop_tooltipInitialized !== undefined && window.snoop_tooltipInitialized !== false) return; window.snoop_tooltipInitialized = true; $.each(shops, function (index, shop) { var $shopInfo = $('#snoop-shop-' + shop.id); pageScraper.getPriceOn(shop, function (price) { page.displayPrice($shopInfo, shop.moneyFrom(price)); }, function (warning, addNotFoundClass) { page.displayWarning($shopInfo, warning, addNotFoundClass); }); }); }); $('#snoop-icon-ml').mouseover(function () { if (window.snoopML_tooltipInitialized !== undefined && window.snoopML_tooltipInitialized !== false) return; window.snoopML_tooltipInitialized = true; var productName = decodeURI(jQuery('#productTitle').html().replace(' ', ' ').replace(/[^\w\d\s\.]/g, ' ').trim()); meli.showPrices(productName); }); } }; var settings; var snoop = { tooltip: null, asin: null, _startMonitoringAsin: function () { var observer = new MutationObserver(function (mutations) { var asinHasProbablyChanged = mutations.some(function (mutation) { return mutation.addedNodes.length > 0; }); if (!asinHasProbablyChanged) return; var newAsin = $('#ASIN').val(); if (snoop.asin == newAsin) return; snoop.run(newAsin); }); observer.observe($('#buybox_feature_div')[0], { attributes: true, subtree: true, childList: true, characterData: true }); }, initialize: function (asin) { this.asin = asin; this._startMonitoringAsin(); refreshRates(); settings = new Settings(asin); snoop.tooltip = Mustache.to_html(tooltipTemplate, { shops: settings.filteredShops, from_shop: settings.currentShop.id, from_asin: settings.asin, loader_url: 'https://i.imgur.com/Dp92MjH.gif' }); snoop.run(asin); }, run: function (asin) { this.asin = asin; settings = new Settings(asin); window.snoop_tooltipInitialized = false; window.snoopML_tooltipInitialized = false; var ensureTooltipHasBeenLoaded = function () { if (snoop.tooltip === null) { setTimeout(ensureTooltipHasBeenLoaded, 50); } else { var tooltipMarkup = snoop.tooltip.replace(/{asin}/gm, settings.asin); page.addTooltipToPage(tooltipMarkup); page.registerInitializationHandler(settings.filteredShops); } }; ensureTooltipHasBeenLoaded(); } }; Number.prototype.formatMoney = function(c, d, t){ var n = this; c = isNaN(c = Math.abs(c)) ? 2 : c; d = d === undefined ? "." : d; t = t === undefined ? "," : t; var s = n < 0 ? "-" : "", i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", j = (j = i.length) > 3 ? j % 3 : 0; return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : ""); }; var meli = { showPrices: function(productName) { meli.fetchApiData(productName, meli.showProducts); }, showProducts: function (data){ var meliProducts = $('.snoop-tooltip-ml .tmm_popover'); meliProducts.html('
Buscando por: '+data.query+'
'); //console.log(data); if(data.results.length === 0){ meliProducts.append('
No se encontraron productos en MercadoLibre con esta búsqueda.
'); } data.results.forEach(function(product) { meliProducts.append('
ARS $'+product.price.formatMoney(2)+'
» '+product.title+'
'); }); }, fetchApiData: function (productName, callback) { GM_xmlhttpRequest({ method: "GET", url: "https://api.mercadolibre.com/sites/MLA/search?q=" + encodeURI(productName) + "&condition=new", onload: function(responseDetails) { var api = JSON.parse(responseDetails.responseText); callback(api); }, onerror: function(responseDetails) { alert("Error fetching API from MELI"); } }); } };