1
0
mirror of https://github.com/chylex/Firefox-SCsCC.git synced 2024-11-14 17:42:52 +01:00
Firefox-SCsCC/data/scscc.js
2014-07-24 14:45:35 +02:00

507 lines
13 KiB
JavaScript

"use strict";
var scscc = scscc || {};
(function() {
var uPrefs, currRates;
var currReqs = {};
var patt0 = /(((\d{1,3}((\,|\.|\s)\d{3})+|(\d+))((\.|\,)\d{1,9})?)|(\.\d{1,9}))/g;
var patt = {
EUR: [
/(€|eur(os|o)?)\s?(((\d{1,3}((\,|\.|\s)\d{3})+|(\d+))((\.|\,)\d{1,9})?)|(\.\d{1,9}))/ig,
/(((\d{1,3}((\,|\.|\s)\d{3})+|(\d+))((\.|\,)\d{1,9})?)|(\.\d{1,9}))(,--)?\s?(€|eur(os|o)?)/ig
],
USD: [
/(\$|usd)\s?(((\d{1,3}((\,|\.|\s)\d{3})+|(\d+))((\.|\,)\d{1,9})?)|(\.\d{1,9}))/ig,
/(((\d{1,3}((\,|\.|\s)\d{3})+|(\d+))((\.|\,)\d{1,9})?)|(\.\d{1,9}))(,--)?\s?(\$|usd)/ig
],
GBP: [
/(£|gbp)\s?(((\d{1,3}((\,|\.|\s)\d{3})+|(\d+))((\.|\,)\d{1,9})?)|(\.\d{1,9}))/ig,
/(((\d{1,3}((\,|\.|\s)\d{3})+|(\d+))((\.|\,)\d{1,9})?)|(\.\d{1,9}))(,--)?\s?(£|gbp)/ig
]
};
var symbs = {
prev: {
EUR: /(€|eur(os|o)?)$/ig,
USD: /(\$|usd)$/ig,
GBP: /(£|gbp)$/ig
},
next: {
EUR: /^(€|eur(os|o)?)/ig,
USD: /^(\$|usd)/ig,
GBP: /^(£|gbp)/ig
}
};
var style = document.createElement("style");
style.textContent =
"data.scscc {\n\
padding: 0 2px !important;\n\
color: inherit !important;\n\
white-space: pre !important;\n\
border-width: 0 1px !important;\n\
border-style: dotted !important;\n\
border-color: inherit !important;\n\
cursor: help !important;\n\
}\n\
data.scscc:hover {\n\
background-color: red !important;\n\
color: white !important;\n\
}";
var observer = new MutationObserver(function(mutlist) {
checkMutations(mutlist);
});
this.set = function(variable, data) {
switch (variable) {
case "uPrefs":
uPrefs = data;
break;
case "currRates":
currRates = data;
break;
case "currReqs":
currReqs = {};
break;
}
};
this.getPrefs = function() {
return uPrefs;
};
this.replaceAllElems = function() {
// start the observer
observer.observe(document.body, {
childList: true,
subtree: true
});
getTextNodesIn(document.body);
// add the custom style
if (uPrefs.style) document.head.appendChild(style);
};
// reverts all the changes made by scscc
// if refresh is true, refreshes the page
this.revertRefresh = function(refresh) {
var replace, dataNodes;
// catch: Permission denied to access property 'document'
try { document; }
catch (err) {
console.log("SCsCC - refresh error: " + err.message);
return;
}
// remove style from head
for (var i = document.head.childNodes.length - 1; i >= 0; i--) {
if (document.head.childNodes[i] === style) {
document.head.removeChild(style);
break;
}
}
observer.disconnect();
// find and refresh/revert already converted prices
dataNodes = document.querySelectorAll("data.scscc");
for (var i = 0, len = dataNodes.length; i < len; i++) {
if (refresh && dataNodes[i].dataset.curr !== uPrefs.toCurr && checkCurrRate(dataNodes[i].dataset.curr)) {
replace = parseFloat(dataNodes[i].value) * currRates[dataNodes[i].dataset.curr + "to" + uPrefs.toCurr];
replace = formatPrice(replace);
replace = document.createTextNode(replace);
dataNodes[i].replaceChild(replace, dataNodes[i].firstChild);
}
else {
replace = document.createTextNode(dataNodes[i].title);
dataNodes[i].parentNode.replaceChild(replace, dataNodes[i]);
}
}
if (refresh) this.replaceAllElems();
};
function checkMutations(mutlist) {
for (var i in mutlist) {
// only check childList add mutations
if (mutlist[i].addedNodes.length === 0) continue;
for (var j = 0, len = mutlist[i].addedNodes.length; j < len; j++) {
if (mutlist[i].addedNodes[j].className !== "scscc") getTextNodesIn(mutlist[i].addedNodes[j]);
}
}
}
// get all text nodes of a given node
// from http://stackoverflow.com/questions/298750/how-do-i-select-text-nodes-with-jquery
function getTextNodesIn(node) {
var textNodes = [];
var ignoreNodes = /^(script|style|pre)$/i;
function getTextNodes(node) {
if (node.nodeName.search(ignoreNodes) !== -1 || node.className === "scscc") return;
if (node.nodeType === 3) {
if (node.nodeValue.search(patt0) !== -1) {
textNodes.push(node);
}
}
else {
for (var i = 0, len = node.childNodes.length; i < len; i++) {
getTextNodes(node.childNodes[i]);
}
}
}
getTextNodes(node);
if (textNodes.length > 0) findMatches(textNodes);
}
// check if there is any pattern match in a text node and return the matches
function findMatches(textNodes) {
var found, m, matches, txt;
for (var i = 0, ilen = textNodes.length; i < ilen; i++) {
found = false;
matches = {};
txt = textNodes[i].nodeValue;
for (var from in patt) {
if (from === uPrefs.toCurr) continue;
for (var j in patt[from]) {
m = txt.match(patt[from][j]);
if (m) {
if (!matches.hasOwnProperty(from)) matches[from] = [];
matches[from] = matches[from].concat(m);
found = true;
}
}
}
if (found) replaceNode(textNodes[i], matches);
else checkSiblings(textNodes[i]);
}
}
// check if currency symbol is in an other node
function checkSiblings(textNode) {
var chckSymbs;
var chckTxt = {};
var matches = {};
var txt = textNode.nodeValue;
var m = txt.match(patt0);
if (m.length !== 1 || m[0] !== txt.trim()) return;
// check previous sibling of
// this node -> check sibling's last child
if (textNode.previousSibling && textNode.previousSibling.lastChild && textNode.previousSibling.lastChild.nodeType === 3) {
chckTxt["prev"] = textNode.previousSibling.lastChild.nodeValue.trim();
}
// parent node
else if (textNode.parentNode.previousSibling) {
// if text node
if (textNode.parentNode.previousSibling.nodeType === 3) {
chckTxt["prev"] = textNode.parentNode.previousSibling.nodeValue.trim();
}
// if not text node -> check last child
else if (textNode.parentNode.previousSibling.lastChild && textNode.parentNode.previousSibling.lastChild.nodeType === 3) {
chckTxt["prev"] = textNode.parentNode.previousSibling.lastChild.nodeValue.trim();
}
}
// check next sibling of
// this node -> check sibling's first child
if (textNode.nextSibling && textNode.nextSibling.firstChild && textNode.nextSibling.firstChild.nodeType === 3) {
chckTxt["next"] = textNode.nextSibling.firstChild.nodeValue.trim();
}
// parent node
else if (textNode.parentNode.nextSibling) {
// if text node
if (textNode.parentNode.nextSibling.nodeType === 3) {
chckTxt["next"] = textNode.parentNode.nextSibling.nodeValue.trim();
}
// if not text node -> check first child
else if (textNode.parentNode.nextSibling.firstChild && textNode.parentNode.nextSibling.firstChild.nodeType === 3) {
chckTxt["next"] = textNode.parentNode.nextSibling.firstChild.nodeValue.trim();
}
}
for (var pos in chckTxt) {
chckSymbs = symbs[pos];
for (var from in chckSymbs) {
if (from === uPrefs.toCurr) continue;
if (chckTxt[pos].search(chckSymbs[from]) !== -1) {
matches[from] = m;
replaceNode(textNode, matches);
return;
}
}
}
}
// replace the prices in the text node with the converted data nodes
function replaceNode(node, matches) {
var matchInd, removeNode, tmpDiv, tmpTxt, txt, txtNode;
var parentNode = node.parentNode;
var dataNodes = replaceWith(node.nodeValue, matches);
for (var i = 0, ilen = dataNodes.length; i < ilen; i++) {
if (!node.parentNode) return;
for (var j = 0, jlen = parentNode.childNodes.length; j < jlen; j++) {
if (parentNode.childNodes[j].nodeType === 3 && parentNode.childNodes[j].nodeValue.indexOf(dataNodes[i].title) !== -1) {
removeNode = parentNode.childNodes[j];
txt = removeNode.nodeValue;
matchInd = txt.indexOf(dataNodes[i].title);
tmpDiv = document.createElement("div");
if (matchInd !== 0) {
tmpTxt = txt.slice(0, matchInd);
txtNode = document.createTextNode(tmpTxt);
tmpDiv.appendChild(txtNode);
}
tmpDiv.appendChild(dataNodes[i]);
if (txt.charAt(matchInd + dataNodes[i].title.length) !== "") {
tmpTxt = txt.slice(matchInd + dataNodes[i].title.length);
txtNode = document.createTextNode(tmpTxt);
tmpDiv.appendChild(txtNode);
}
while (tmpDiv.firstChild) {
parentNode.insertBefore(tmpDiv.firstChild, removeNode);
}
parentNode.removeChild(removeNode);
break;
}
}
}
}
// find and convert the prices in the text, and return them as data nodes
function replaceWith(txt, matches) {
var data, match, repl;
var dataNodes = [];
for (var from in matches) {
if (!checkCurrRate(from)) continue;
for (var mid in matches[from]) {
data = document.createElement("data");
data.className = "scscc";
data.dataset.curr = from;
match = matches[from][mid];
if (txt.trim() !== match) {
match = checkSpecCases(txt, match, from);
if (!match) continue;
}
repl = match;
data.title = match;
repl = cleanPrice(repl);
data.value = repl;
repl = parseFloat(repl) * currRates[from + "to" + uPrefs.toCurr];
repl = formatPrice(repl);
data.textContent = repl;
dataNodes.push(data);
}
}
return dataNodes;
}
function checkCurrRate(from) {
// check if request sent already
if (!currReqs.hasOwnProperty(from + "to" + uPrefs.toCurr)) {
currReqs[from + "to" + uPrefs.toCurr] = true;
self.port.emit("getCurrRates", [ from, uPrefs.toCurr ]);
}
if (!currRates.hasOwnProperty(from + "to" + uPrefs.toCurr)) return false;
return true;
}
function checkSpecCases(txt, match, from) {
var chckchar;
var charind = txt.indexOf(match);
// skip other dollars
//Australian (A$)
//Barbadian (Bds$)
//Belizean (BZ$)
//Brunei (B$)
//Canadian (CA$)
//Cayman Islands (CI$)
//East Caribbean (EC$)
//Fiji (FJ$)
//Guyanese (G$)
//Hong Kong (HK$)
//Jamaican (J$)
//Liberian (L$ or LD$)
//Namibian (N$)
//New Zealand (NZ$)
//Singaporean (S$)
//Soloman Islands (SI$)
//Taiwanese (NT$)
//Trinidad and Tobago (TT$)
//Tuvaluan (TV$)
//Zimbabwean (Z$)
//Chilean (CLP$)
//Colombian (COL$)
//Dominican (RD$)
//Mexican (Mex$)
//Nicaraguan córdoba (C$)
//Brazilian real (R$)
if (from === "USD" && match.charAt(0) === "$") {
chckchar = txt.charAt(charind - 1);
if (chckchar.search(/\w/) !== -1) {
txt = txt.slice(0, charind);
if (txt.search(/(A|Bds|BZ|B|CA|CI|EC|FJ|G|HK|J|L|LD|N|NZ|S|SI|NT|TT|TV|Z|CLP|COL|RD|Mex|C|R)$/) !== -1) return false;
}
}
// in case text is like: masseur 1234
// or
// in case text is like: 1234 europe
var sind = match.search(/eur|usd|gbp/i);
if (sind !== -1) {
// starts with eur(os)/usd/gbp
if (sind === 0) {
// if there is any word character before it, skip it
chckchar = txt.charAt(charind - 1);
if (chckchar.search(/\w/) !== -1) return false;
}
// ends with eur(os)/usd/gbp
else {
// if there is any word character after it, skip it
chckchar = txt.charAt(charind + match.length);
if (chckchar.search(/\w/) !== -1) return false;
}
}
// in case text is like: somestring1 234 $
if (match.charAt(0).search(/\d/) !== -1) {
// if there is a word character before it
chckchar = txt.charAt(charind - 1);
if (chckchar.search(/\w/) !== -1) {
match = match.replace(/^\d+\s/, ""); // convert only 234 $
}
}
return match;
}
// make price computer-readable: remove price symbols, and replace/remove separators
function cleanPrice(repl) {
// remove currency symbols and spaces
repl = repl.replace(/€|eur(os|o)?|\$|usd|£|gbp|,--|\s/ig, "");
// if no decimal separator
// remove possible "." or "," thousand separators
if (repl.search(/(\.|,)\d{1,2}$/) === -1) repl = repl.replace(/\.|,/g, "");
else {
// if decimal separator is "."
// remove possible "," thousand separators
if (repl.search(/\.\d{1,2}$/) !== -1) repl = repl.replace(/,/g, "");
// if decimal separptor is ","
else {
// remove possible "." thousand separators
repl = repl.replace(/\./g, "");
// replace dec separator to "."
repl = repl.replace(/,/g, ".");
}
}
return repl;
}
// format the price according to user prefs
function formatPrice(repl) {
// set rounding
repl = (uPrefs.round) ? repl.toFixed(0) : repl.toFixed(2);
// set decimal separator
if (uPrefs.sepDec !== ".") repl = repl.replace(".", uPrefs.sepDec);
// set thousand separator
if (uPrefs.sepTho !== "") {
for (var i = ((uPrefs.round) ? repl.length : repl.indexOf(uPrefs.sepDec)) - 3; i > 0; i -= 3) {
repl = repl.slice(0, i) + uPrefs.sepTho + repl.slice(i);
}
}
// add symbol
repl = (uPrefs.symbPos === "a") ? repl + ((uPrefs.symbSep) ? " " : "") + uPrefs.symbol : uPrefs.symbol + ((uPrefs.symbSep) ? " " : "") + repl;
return repl;
}
}).apply(scscc);
self.port.once("firstRun", function(data) {
scscc.set("uPrefs", data[0]);
scscc.set("currRates", data[1]);
if (data[0].btn && data[0].toCurr !== "") scscc.replaceAllElems();
});
self.port.on("refreshUPrefs", function(data) {
var changed = false;
var prefs = scscc.getPrefs();
for (var id in data) {
if (!prefs.hasOwnProperty(id) || prefs[id] !== data[id]) {
changed = true;
scscc.set("uPrefs", data);
break;
}
}
if (changed) {
if (data.btn && data.toCurr !== "") scscc.revertRefresh(true);
else scscc.revertRefresh(false);
}
});
self.port.on("refreshCurrRates", function(data) {
var prefs = scscc.getPrefs();
scscc.set("currRates", data);
scscc.set("currReqs");
if (prefs.btn && prefs.toCurr !== "") scscc.revertRefresh(true);
});
self.port.on("detach", function() {
setTimeout(function() { scscc.revertRefresh(false); }, 1000);
});