try to take over the world!
// ==UserScript== // @name Currency Converter // @namespace http://tampermonkey.net/ // @version 2.3.0 // @description try to take over the world! // @icon http://store.steampowered.com/favicon.ico // @author Bisumaruko // @include http*://yuplay.ru/* // @include http*://*.gamersgate.com/* // @include http*://www.greenmangaming.com/* // @include http*://gama-gama.ru/* // @include http*://*.gamesplanet.com/* // @include http*://www.cdkeys.com/* // @include http*://directg.net/* // @include http*://www.humblebundle.com/* // @include http*://www.indiegala.com/* // @include http*://www.bundlestars.com/* // @include http*://www.opiumpulses.com/* // @require https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.slim.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/limonte-sweetalert2/6.6.6/sweetalert2.min.js // @resource SweetAlert2CSS https://cdnjs.cloudflare.com/ajax/libs/limonte-sweetalert2/6.6.6/sweetalert2.min.css // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @grant GM_addStyle // @grant GM_getResourceText // @run-at document-start // @connect www.ecb.europa.eu // ==/UserScript== /* global swal, games */ // inject swal css GM_addStyle( GM_getResourceText('SweetAlert2CSS'), ); // setup swal swal.setDefaults({ timer: 3000, useRejections: false, }); // load config const config = JSON.parse( GM_getValue('Bisko_CC', '{}'), ); const interval = 3 * 60 * 60 * 1000; // update exchange rate every 3 hours const currencies = { ORI: { nameEN: '', nameZH: '恢復', symbol: '', }, AUD: { nameEN: 'Australian Dollar', nameZH: '#幣', symbol: 'AU$', }, CAD: { nameEN: 'Canadian Dollar', nameZH: '加幣', symbol: 'CA$', }, CNY: { nameEN: 'Chinese Yuan', nameZH: '人民幣', symbol: 'CN¥', }, EUR: { nameEN: 'Euro', nameZH: '歐元', symbol: '€', }, GBP: { nameEN: 'Great Britain Pound', nameZH: '英鎊', symbol: '£', }, HKD: { nameEN: '#### Kong Dollar', nameZH: '港幣', symbol: 'HK$', }, JPY: { nameEN: 'Japanese Yen', nameZH: '日圓', symbol: 'JP¥', }, KRW: { nameEN: 'South Korean Won', nameZH: '韓圓', symbol: '₩', }, MYR: { nameEN: 'Malaysian Ringgit', nameZH: '令吉', symbol: 'RM', }, NTD: { nameEN: 'New ###### Dollar', nameZH: '台幣', symbol: 'NT$', }, NZD: { nameEN: 'New Zealand Dollar', nameZH: '紐幣', symbol: 'NZ$', }, RUB: { nameEN: 'Russian Ruble', nameZH: '盧布', symbol: 'руб', }, USD: { nameEN: 'United States Dollar', nameZH: '美元', symbol: 'US$', }, }; let originalCurrency = 'USD'; // jQuery extension $.fn.price = function price() { return this.eq(0).text().replace(/[^.0-9]/g, ''); }; $.fn.bindPriceData = function bindPriceData() { return this.each((index, element) => { const $ele = $(element); $ele .addClass('###rice') .attr('data-###rice', JSON.stringify({ currency: originalCurrency, price: $ele.text().replace(/[^.0-9]/g, ''), })); }); }; // constructing functions const has = Object.prototype.hasOwnProperty; const preferredCurrency = () => (has.call(config.exchangeRate.rates, config.preferredCurrency) ? config.preferredCurrency : 'CNY'); // default to CNY const updateCurrency = (currency = null) => { let targetCurrency = currency || preferredCurrency(); $('.###rice').each((index, element) => { const $ele = $(element); const data = JSON.parse( $ele.attr('data-###rice'), ); let convertedPrice = 0; if (targetCurrency === 'ORI') { targetCurrency = data.currency; convertedPrice = data.price; } else { const rates = config.exchangeRate.rates; convertedPrice = data.price * (rates[targetCurrency] / rates[data.currency]); } $ele.text( convertedPrice .toLocaleString('en', { style: 'currency', currency: targetCurrency, maximumFractionDigits: 2, }) .replace('MYR', currencies.MYR.symbol) .replace('NTD', currencies.NTD.symbol), ); }); }; const constructMenu = () => { const $li = $('<li class="Bisko_CC_Menu"><a>Currencies</a></li>'); const $ul = $('<ul></ul>').appendTo($li); const preferred = preferredCurrency(); GM_addStyle(` .Bisko_CC_Menu ul { display: none; position: absolute; padding: 0; background-color: #272727; z-index: 9999; } .Bisko_CC_Menu:hover ul { display: block; } .Bisko_CC_Menu li { padding: 2px 10px; list-style-type: none; cursor: pointer; } .Bisko_CC_Menu li:hover, .preferred { background-color: SandyBrown; } `); Object.keys(currencies).forEach((currency) => { const itemName = `${currency} ${currencies[currency].nameZH}`; $ul.append( $(`<li class="${currency}">${itemName}</li>`) .addClass(() => (preferred === currency ? 'preferred' : '')) .click(() => { config.preferredCurrency = currency; GM_setValue('Bisko_CC', JSON.stringify(config)); updateCurrency(currency); $('.Bisko_CC_Menu .preferred').removeClass('preferred'); $(`.Bisko_CC_Menu .${currency}`).addClass('preferred'); }), ); }); return $li; }; const handler = () => { switch (location.host) { case 'yuplay.ru': originalCurrency = 'RUB'; GM_addStyle(` .games-pricedown span.###rice { font-size: 18px; } .good-title span.###rice { margin-right: 3px; font-size: 22px; } `); $('.header-right').append( constructMenu(), ); // homepage games-pricedown $('.games-pricedown .sale > s, .games-pricedown .price').bindPriceData(); // homepage game box & product page $('.games-box .price, .good-title .price') .contents() .each((index, node) => { const $node = $(node); const text = $node.text().trim(); if (node.tagName === 'S') $node.bindPriceData(); // retail price if (node.tagName === 'SPAN') $node.remove(); // currency else if (node.nodeType === 3 && text.length > 0) { // sales price $node.replaceWith( $(`<span>${text}<span>`).bindPriceData(), ); } }); break; case 'gama-gama.ru': originalCurrency = 'RUB'; GM_addStyle(` .Bisko_CC_Menu > a, .Bisko_CC_Menu li { color: white; } .Bisko_CC_Menu ul { margin: 0; } `); $('#top_back').append( constructMenu() .wrapInner('<div class="Bisko_CC_Menu top_menu"></div>') .children() .unwrap(), ); // homepage $('.price_1, .old_price, .promo_price').bindPriceData(); // product page $('.card-info-oldprice > span, .card-info-price > span').bindPriceData(); $('.card-info-oldprice, .card-info-price') .contents() .filter((i, node) => node.nodeType !== 1) .remove(); break; case 'ru.gamersgate.com': case 'cn.gamersgate.com': if (location.host.startsWith('ru')) originalCurrency = 'RUB'; if (location.host.startsWith('cn')) originalCurrency = 'CNY'; GM_addStyle(` .Bisko_CC_Menu { width: 49px; text-align: center; } .Bisko_CC_Menu svg { width: 36px; height: 36px; background-color: #093745; border: 1px solid #2c7c92; } .Bisko_CC_Menu, .Bisko_CC_Menu li { background-image: none !important; color: white; } .Bisko_CC_Menu li { height: initial !important; float: none !important; padding: 5px 10px !important; } .Bisko_CC_Menu li:hover, .preferred { background-color: SandyBrown !important; } `); $('.btn_menuseparator').replaceWith( constructMenu(), ); $('.Bisko_CC_Menu a').replaceWith(` <svg viewBox="0 0 24 24"> <path fill="#a9ebea" d="M11.8,10.9C9.53,10.31 8.8,9.7 8.8,8.75C8.8,7.66 9.81,6.9 11.5,6.9C13.28,6.9 13.94,7.75 14,9H16.21C16.14,7.28 15.09,5.7 13,5.19V3H10V5.16C8.06,5.58 6.5,6.84 6.5,8.77C6.5,11.08 8.41,12.23 11.2,12.9C13.7,13.5 14.2,14.38 14.2,15.31C14.2,16 13.71,17.1 11.5,17.1C9.44,17.1 8.63,16.18 8.5,15H6.32C6.44,17.19 8.08,18.42 10,18.83V21H13V18.85C14.95,18.5 16.5,17.35 16.5,15.3C16.5,12.46 14.07,11.5 11.8,10.9Z" /> </svg> `); // homepage & product page $(` .prtag > span, .grid-old-price, .price_price > span, div > span > .bold.white, li > .f_right:nth-child(2) > span `).bindPriceData(); break; case 'www.greenmangaming.com': GM_addStyle(` .Bisko_CC_Menu > a { margin: 0 !important; padding: 6px 15px !important; font-size: 14px; } .Bisko_CC_Menu > a:hover, .Bisko_CC_Menu li:hover, .preferred { background-color: #494a4f !important; } `); $('.megamenu').append( constructMenu(), ); try { // product page originalCurrency = games.Currency; $('price > span').bindPriceData(); } catch (e) { // home page & search page const currencyObserver = new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.addedNodes.forEach((addedNode) => { if (addedNode.tagName === 'SCRIPT' && addedNode.src.includes('bam.nr-data.net')) { const currency = decodeURI(addedNode.src).split('currency_code":"').pop().slice(0, 3); if (currency.length > 0) { originalCurrency = currency; const $prices = $('.prices > span, .prices > p, .listing-price > span'); // offset decimal place in Europe (. ,) if (['EUR', 'RUB'].includes(currency)) $prices.text((i, text) => text.replace(',', '.')); $prices.bindPriceData(); updateCurrency(); } currencyObserver.disconnect(); } }); }); }); currencyObserver.observe(document.head, { childList: true }); // search listing const $listing = $('.table-search-listings'); if ($listing.length > 0) { new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.addedNodes.forEach((addedNode) => { $(addedNode).find('.listing-price > span').bindPriceData(); updateCurrency(); }); }); }).observe($listing[0], { childList: true }); } } break; case 'uk.gamesplanet.com': case 'de.gamesplanet.com': case 'fr.gamesplanet.com': { originalCurrency = !location.host.startsWith('uk') ? 'EUR' : 'GBP'; const GPHandler = () => { const $prices = $('.price_base > strike, .price_current'); $('.container > ul').append( constructMenu(), ); // offset decimal place in Europe (. ,) if (!location.host.startsWith('uk')) { $prices.text((i, text) => text.replace(',', '.')); } $prices.bindPriceData(); updateCurrency(); }; GPHandler(true); new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.removedNodes.forEach((removedNode) => { if (removedNode.id === 'nprogress') GPHandler(); }); }); }).observe(document, { childList: true, subtree: true, }); break; } case 'www.cdkeys.com': { originalCurrency = $('.currency-switcher:first-child .value').text(); const currenciesDefault = ['AUD', 'CAD', 'EUR', 'GBP', 'JPY', 'NZD', 'USD']; const currenciesAppend = ['CNY', 'HKD', 'KRW', 'MYR', 'NTD', 'RUB']; // bind event to default currency switcher to save preferrred currency $('.currency-switcher:first-child ul span').each((index, element) => { const $ele = $(element); const currency = $ele.text().slice(0, 3); if (currenciesDefault.includes(currency)) { $ele.parent().click(() => { config.preferredCurrency = currency; GM_setValue('Bisko_CC', JSON.stringify(config)); }); } }); // append currencies not in the default currency switcher currenciesAppend.forEach((currency) => { $('.currency-switcher:first-child ul').append( $(`<li><a>${currency} - ${currencies[currency].nameEN}</a></li>`).click(() => { config.preferredCurrency = currency; GM_setValue('Bisko_CC', JSON.stringify(config)); updateCurrency(currency); }), ); }); $('.price').bindPriceData(); break; } case 'directg.net': originalCurrency = 'KRW'; GM_addStyle(` .Bisko_CC_Menu ul { width: 180px; border-top: 3px solid #67c1f5; background-color: #1b2838 !important; opacity: 0.95; color: rgba(255,255,255,0.5) !important; } .Bisko_CC_Menu ul li { padding: 10px 30px; font-weight: bold; } .Bisko_CC_Menu ul li:hover, .preferred { background-color: initial !important; color: white; } `); $('.nav').append( constructMenu(), ); $('span.PricebasePrice, span.PricesalesPrice') .bindPriceData() .next('span[itemprop="priceCurrency"]') .remove(); break;/* case 'www.origin.com': { const region = location.pathname.split('/')[1]; const currencyRegion = { twn: 'NTD', jpn: 'JPY', rus: 'RUB', usa: 'USD', nzl: 'NZD', irl: 'EUR', kor: 'KRW', }; }*/ case 'www.humblebundle.com': originalCurrency = 'USD'; GM_addStyle(` .Bisko_CC_Menu { float: left; } .Bisko_CC_Menu > a { display: block; padding: 15px 20px; } .Bisko_CC_Menu ul { background-color: #494f5c !important; color: rgba(255, 255, 255, 0.6); } .Bisko_CC_Menu ul li { padding: 5px 10px; } .Bisko_CC_Menu ul li:hover, .preferred { background-color: initial !important; color: white; } `); $('.nav:first-child').append( constructMenu(), ); new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.removedNodes.forEach((removedNode) => { if (removedNode.data === 'The Humble Store: Loading') { $('.price, .store-price, .full-price, .current-price').bindPriceData(); updateCurrency(); } }); }); }).observe(document.head, { childList: true, subtree: true, }); break; case 'www.indiegala.com': originalCurrency = 'USD'; GM_addStyle(` .Bisko_CC_Menu ul { transform: translateY(-100%); background-color: #474747 !important; border: 1px solid #272727; } .Bisko_CC_Menu ul li { padding: 8px 15px; color: #dad6ca; } .Bisko_CC_Menu ul li:hover, .preferred { background-color: #2E2E2E !important; color: white; } `); $('#libdContainer > ul:not(:first-child)').append( constructMenu(), ); $('.Bisko_CC_Menu > a').addClass('libd-group-item libd-bounce'); // homepage & product page $('.inner-info, .inner, .price-container') .contents() .each((index, node) => { const $node = $(node); const text = $node.text().trim(); $node.parent().parent().css('overflow', 'hidden'); if (node.nodeType === 1 && node.tagName !== 'BR') $node.bindPriceData(); else if (node.nodeType === 3 && text.length) { $node.replaceWith( $(`<a>${text}</a>`).bindPriceData(), ); } }); // search r###lt $('.price-cont').bindPriceData(); break; case 'www.bundlestars.com': originalCurrency = 'USD'; GM_addStyle(` .Bisko_CC_Menu ul { background-color: #212121 !important; } .Bisko_CC_Menu ul li { padding: 8px 15px; } .Bisko_CC_Menu ul li:hover, .preferred { background-color: #1A1A1A !important; } `); $('.nav').eq(0).append( constructMenu(), ); new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.removedNodes.forEach((removedNode) => { if (removedNode.id === 'loading-bar-spinner') { $('.bs-price, .bs-currency-discount > span, .bs-currency-price').bindPriceData(); $('.bs-card-meta > .bs-pricing, .bs-card-meta > .bs-currency-discount').css({ position: 'absolute', right: '19px', }); updateCurrency(); } }); }); }).observe(document.body, { childList: true }); break; case 'www.opiumpulses.com': originalCurrency = 'USD'; GM_addStyle(` .Bisko_CC_Menu { float: right; margin-top: 5px; padding: 1px 5px; vertical-align: middle; background-image: linear-gradient(#4E4E4E, #101112 40%, #191b1d); font-size: 12px; line-height: 1.5; border-radius: 3px; border-bottom-right-radius: 0; border-top-right-radius: 0; } .Bisko_CC_Menu > a { color: #f89406; } `); $('.top-bar > .container > form').after( constructMenu() .wrapInner('<div class="Bisko_CC_Menu top_menu"></div>') .children() .unwrap(), ); // homepage $('.album__container p > span:first-of-type').bindPriceData(); // store page $('.product-box s, .product-box .text-danger').bindPriceData(); new MutationObserver((mutations) => { mutations.forEach((mutation) => { mutation.addedNodes.forEach((addedNode) => { if (addedNode.id === 'productlistview') { $(addedNode).find('.product-box s, .product-box .text-danger').bindPriceData(); updateCurrency(); } }); }); }).observe(document.body, { childList: true, subtree: true, }); break; default: } // only update prices when appended currencies are selected if (originalCurrency !== preferredCurrency()) updateCurrency(); }; const getExchangeRate = () => { GM_xmlhttpRequest({ method: 'GET', url: 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml', onload: (res) => { if (res.status === 200) { try { config.exchangeRate = { lastUpdate: Date.now(), rates: {}, }; res.response.split("\n").forEach((line) => { if (line.includes('currency=')) { const currency = line.split('currency=\'').pop().slice(0, 3); const rate = line.trim().split('rate=\'').pop().slice(0, -3); config.exchangeRate.rates[currency] = parseFloat(rate); } }); config.exchangeRate.rates.EUR = 1; // get NTD GM_xmlhttpRequest({ method: 'GET', url: 'https://www.google.com/search?q=1+EUR+%3D+NTD', onload: (searchRes) => { const rate = parseFloat(searchRes.response.split('<div class="vk_ans vk_bk">').pop().slice(0, 7).trim()); const NTDRate = isNaN(rate) ? config.exchangeRate.rates.HKD * 3.75 : rate; config.exchangeRate.rates.NTD = NTDRate; GM_setValue('Bisko_CC', JSON.stringify(config)); }, onerror: () => { config.exchangeRate.rates.NTD = config.exchangeRate.rates.HKD * 3.75; }, }); handler(); } catch (e) { swal( 'Parsing Failed', 'An error occured when parsing exchange rate data, please reload to try again', 'error', ); } } else { swal( 'Loading Failed', 'Unable to fetch exchange rate data, please reload to try again', 'error', ); } }, }); }; $(() => { if (Object.keys(config).length === 0) getExchangeRate(); // first installed else if (Date.now() - interval > config.lastUpdate) getExchangeRate(); // update exchange rate else handler(); });