By Tesiph
—
Last update
Apr 17, 2008
—
Installed
1,942 times.
// ==UserScript==
// @name Convert Foreign to Local Currency
// @namespace none
// @description Converts all prices to your local currency
// @include *
// ==/UserScript==
// ------------------------------------------------------------------------- //
/* This list is taken at semirandom from wikipedia. Feel free to add your own
* currency.
* Higher ranked currencies have priority if they happen to have the same symbol
*/
var currencies = {
'USD': ['$', 'US$'],
'GBP': ['£'],
'EUR': ['€'],
'AUD': ['$', 'A$'],
'CAD': ['$', 'CDN', 'C$', 'CDN$'],
'JPY': ['¥'],
'CHF': ['Fr.'],
'HKD': ['$', 'HK$'],
'CNY': ['¥'],
'INR': ['₨'],
'IDR': ['Rp'],
'ILS': ['₪'],
'DKK': ['kr'],
'NOK': ['kr', 'NOR'],
'SEK': ['kr'],
'THB': ['฿'],
'TRY': ['₤'],
'RUB': ['руб.', 'руб'],
'PLN': ['zł']
}
// These currencies prefer to suffix their symbol
var suffix = ['CHF', 'DKK', 'NOR'];
// --[ Only code remains ... ]---------------------------------------------- //
// Retrieves the currency code for the given symbol
function getCurrency(symbol)
{
return symbolCurrency[symbol.toUpperCase()];
}
// Retrieves the standard symbol for the given currency code.
// If none is found it defaults to the currency code itself.
function getSymbol(currency)
{
if (currency in currencies)
return currencies[currency][0];
else
return currency;
}
// Creates a float/int from a string. Also does some magic to detect
// thousand separators and get rid of them.
function parseNumber(input) {
var point = input.indexOf('.');
var comma = input.indexOf(',');
if (point > -1 && comma > -1)
{
if (point > comma)
input = input.replace(/\,/g, "");
else
input = input.replace(/\./g, "");
}
else if (point > -1 || comma > -1)
{
var sep = (point > comma) ? '.' : ',';
var r = new RegExp("\\"+sep+"[0-9]{3}$");
if (r.test(input)) {
var n = input.split(sep);
if (parseInt(n[0]) != 0)
input = input.replace(new RegExp("\\"+sep, 'g'), "");
}
}
return parseFloat(input);
}
// Create a special currency element which stores all needed information
// Currentyly it's a abbr element with some non-standard attributes.
function createCurrencyElement(match)
{
var foreignCurrency = getCurrency(match[1]).join(',');
var foreignAmount = parseNumber(match[2]);
var n = document.createElement("abbr");
n.setAttribute("class", "currency");
n.setAttribute("currency", foreignCurrency);
n.setAttribute("value", foreignAmount);
if (convertInline)
n.setAttribute("title", "(" + foreignCurrency + " " + formatCurrency(foreignAmount) + ") " + match[0]);
n.appendChild(document.createTextNode(match[0]));
updateCurrencyElement(n);
return n;
}
function createCurrencyElementSuffix(m)
{
return createCurrencyElement([m[0], m[2], m[1]]);
}
// Will update an existing currency element using the latest exchange rate
function updateCurrencyElement(e)
{
var foreignCurrencyList = e.getAttribute("currency").split(',');
var displayString = "";
for (i in foreignCurrencyList)
{
if (i > 0 && convertInline)
break;
var foreignCurrency = foreignCurrencyList[i];
var foreignAmount = parseFloat(e.getAttribute("value"));
var localAmount = foreignAmount * getExchangeRate(foreignCurrency);
// The default precision of a converted number is 2 decimals.
// If the original has more, also give the converted number more.
var decimal = foreignAmount.toString().split(".");
var precision = 2;
if (decimal.length > 1 && decimal[1].length > 2)
precision = decimal[1].length+1;
if (isNaN(localAmount) || localAmount == 0)
return;
if (!suffix)
formattedString = getSymbol(localCurrency) + "\xA0" + formatCurrency(localAmount, precision);
else
formattedString = formatCurrency(localAmount, precision) + "\xA0" + getSymbol(localCurrency);
if (i > 0)
displayString += ", ";
if (convertInline)
displayString += formattedString;
else
displayString += formattedString + " (from " + foreignCurrency + " " + formatCurrency(foreignAmount) +")";
}
if (convertInline)
e.replaceChild(document.createTextNode(displayString), e.firstChild);
else
e.setAttribute("title", displayString);
}
// Return cached exchange rate, else update from the web
function getExchangeRate(foreign)
{
var r = GM_getValue("rate-" + foreign, false);
if (r != false)
{
var s = r.split("|");
if (s[1] == localCurrency + todayString)
return s[0];
}
updateExchangeRate(foreign);
return NaN;
}
// Retrieve the current exchange rate from Yahoo
var requests = new Array();
function updateExchangeRate(foreign)
{
if (foreign in requests)
return;
requests[foreign] = true;
GM_xmlhttpRequest({
method: "GET",
url: "http://download.finance.yahoo.com/d/quotes.csv?s=" + foreign + localCurrency + "=X&f=Xl1&e=.csv",
onload: function(responseDetails)
{
var response = responseDetails.responseText.split(",");
var foreignCurrency = response[0].substring(1,4);
GM_setValue('rate-'+foreignCurrency, parseFloat(response[1]) + "|" + localCurrency + todayString);
// Update all nodes we skipped
var nodes = document.evaluate(
"//abbr[contains(@currency,\""+foreignCurrency+"\")]",
document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i = 0; i < nodes.snapshotLength; i++)
updateCurrencyElement(nodes.snapshotItem(i));
}
});
}
/* This function will replace all regexp matches with an element.
* At its current incarnation it's an evil hack.
*/
function replaceWithElement(textnode, regex, generator)
{
var text = textnode.data;
if (!textnode.parentNode || /^(SCRIPT|IFRAME|TEXTAREA|STYLE|OPTION|TITLE)$/.test(textnode.parentNode.nodeName))
return
if (regex.test(text))
{
var span = document.createElement("span");
var m;
while(m = text.match(regex))
{
var pos = text.indexOf(m[0]);
// included another hack because there is no look-behind assertion in javascript
var offset = m[1].length;
m.splice(1, 1);
span.appendChild(document.createTextNode(text.substring(0, pos+offset)));
m[0] = m[0].substring(offset);
span.appendChild(generator(m));
text = text.substring(pos+m[0].length+offset);
}
span.appendChild(document.createTextNode(text));
textnode.parentNode.replaceChild(span, textnode);
}
}
// Format number for display
var regexpFixFormat = new RegExp("[,.][0-9]$");
function formatCurrency(num, fixed)
{
if (fixed)
{
var p = Math.pow(10, fixed < 2 ? 2 : fixed);
num = Math.round(num * p) / p;
}
num = num.toLocaleString();
if (regexpFixFormat.test(num))
return num + "0";
else
return num;
}
// --[ Settings ]----------------------------------------------------------- //
function setLocalCurrency()
{
currency = prompt("Please enter your 3-letter currency code (e.g. USD, EUR, GBP)\nIf you do not know the proper code, you can look it up at:\n\t http://en.wikipedia.org/wiki/List_of_circulating_currencies", localCurrency);
localCurrency = currency;
GM_setValue("local-currency", localCurrency);
}
function setInline()
{
convertInline = confirm("Do you want to replace the local currency inline? (Not recommended)");
GM_setValue("inline", convertInline);
}
GM_registerMenuCommand("Change currency inline setting", setInline);
var convertInline = GM_getValue("inline", null);
if (convertInline == null)
setInline();
GM_registerMenuCommand("Change local currency", setLocalCurrency);
var localCurrency = GM_getValue("local-currency", null);
if (localCurrency == null)
{
setLocalCurrency();
alert("You can change your settings later using the Greasemonkey menu");
}
suffix = (suffix.indexOf(localCurrency) != -1);
// ------------------------------------------------------------------------- //
var dateObject = new Date();
var todayString = dateObject.getDay() + "-" + dateObject.getMonth();
// Create a regexp that matches all defined currencies, both by code and symbol(s).
var regexpCurrency = "";
var symbolCurrency = new Array();
for (i in currencies)
{
if (i != localCurrency)
regexpCurrency = regexpCurrency + "|" + i;
if (!(i in symbolCurrency))
symbolCurrency[i] = new Array(i);
else
symbolCurrency.push(i);
for (j in currencies[i])
{
symbol = currencies[i][j];
if (!(symbol.toUpperCase() in symbolCurrency))
{
if (i != localCurrency)
regexpCurrency = regexpCurrency + "|" + symbol;
symbolCurrency[symbol.toUpperCase()] = new Array(i);
} else {
symbolCurrency[symbol.toUpperCase()].push(i);
}
}
}
regexpCurrency = regexpCurrency.substring(1).replace(/\$/g, "\\$");
// I want my positive look-behind assertion!
var whitespace = '[ \t\s\xA0,()+:-]'; // Almost white
var regexpCurrencyPrefix = new RegExp('('+whitespace+'|^)('+regexpCurrency+')(?:[ \n\t\s\xA0])*([0-9.,]*[0-9\-])', 'im');
var regexpCurrencySuffix = new RegExp('('+regexpCurrency+'|'+whitespace+'|^)([0-9.,]*[0-9\-])(?:[ \n\s\xA0])*('+regexpCurrency+')(?=$|[^A-Za-z0-9])', 'im');
textnodes = document.evaluate(
"//text()",
document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
for (var i = 0; i < textnodes.snapshotLength; i++)
{
node = textnodes.snapshotItem(i);
replaceWithElement(node, regexpCurrencyPrefix, createCurrencyElement);
replaceWithElement(node, regexpCurrencySuffix, createCurrencyElementSuffix);
}