// ==UserScript== // @name Currency Converter // @namespace http://tampermonkey.net/ // @version 2.1.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.origin.com/* // @include http*://www.humblebundle.com/* // @include http*://www.indiegala.com/* // @include http*://www.bundlestars.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 api.fixer.io // @downloadURL none // ==/UserScript== /* global GM_xmlhttpRequest, GM_setValue, GM_getValue, GM_addStyle, GM_getResourceText, $, swal, document, location, MutationObserver */ // inject 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: 'Hong 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 Taiwan 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('CCPrice').attr('data-CCPrice', JSON.stringify({ currency: originalCurrency, price: $ele.text().replace(/[^.0-9]/g, '') })); }); }; // constructing functions const has = Object.prototype.hasOwnProperty; const preferredCurrency = () => { const preferred = config.preferredCurrency; if (has.call(config.exchangeRate.rates, preferred)) return preferred; return 'CNY'; // default to CNY }; const updateCurrency = (currency = null) => { let targetCurrency = currency || preferredCurrency(); $('.CCPrice').each((index, element) => { const $ele = $(element); const data = JSON.parse($ele.attr('data-CCPrice')); 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 = $('
  • Currencies
  • '); const $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($(`
  • ${itemName}
  • `).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.CCPrice { font-size: 18px; } .good-title span.CCPrice { 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) { $node.replaceWith($(`${text}`).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('
    ').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 'www.gamersgate.com': 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(` `); // homepage & product page $('.grid-old-price, .prtag > span, div > span > .bold.white, .price_price > span, li > .f_right:nth-child(2) > span').bindPriceData(); break; case 'www.greenmangaming.com': originalCurrency = $('.currency-code').eq(0).text(); 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()); // home page new MutationObserver(mutations => { mutations.forEach(mutation => { mutation.addedNodes.forEach(addedNode => { if (addedNode.src && addedNode.src.includes('bam.nr-data.net')) { const $prices = $('.prices > span'); const src = decodeURI(addedNode.src); originalCurrency = src.split('currency_code":"').pop().slice(0, 3) || 'USD'; // offset decimal place in Europe (. ,) if (['EUR', 'RUB'].includes(originalCurrency)) { $prices.text((i, text) => text.replace(',', '.')); } $prices.bindPriceData(); updateCurrency(); } }); }); }).observe(document.head, { childList: true }); // product page $('price > span').bindPriceData(); 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($(`
  • ${currency} - ${currencies[currency].nameEN}
  • `).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.slice(1).split('/').shift(); const currencyRegion = { twn: 'NTD', jpn: 'JPY', rus: 'RUB', usa: 'USD', nzl: 'NZD', irl: 'EUR', }; originalCurrency = 'USD'; break; }*/ 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($(`${text}`).bindPriceData()); } }); // search result $('.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; default: } // only update prices when appended currencies are selected if (originalCurrency && originalCurrency !== preferredCurrency()) updateCurrency(); }; const getExchangeRate = () => { GM_xmlhttpRequest({ method: 'GET', url: 'http://api.fixer.io/latest', onload: res => { if (res.status === 200) { try { const exchangeRate = JSON.parse(res.response); config.exchangeRate = exchangeRate; config.lastUpdate = Date.now(); // add EUR, NTD config.exchangeRate.rates.EUR = 1; config.exchangeRate.rates.NTD = config.exchangeRate.rates.HKD * 3.9; GM_setValue('Bisko_CC', JSON.stringify(config)); 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(); });