// Released under the GPL license
// http://www.gnu.org/copyleft/gpl.html
//
// --------------------------------------------------------------------
//
// This is a Greasemonkey user script. To install it, you need
// Greasemonkey 0.3 or later: http://greasemonkey.mozdev.org/
// Then restart Firefox and revisit this script.
// Under Tools, there will be a new menu item to "Install User Script".
// Accept the default configuration and install.
//
// To uninstall, go to Tools/Manage User Scripts,
// select "Hello World", and click Uninstall.
//
// --------------------------------------------------------------------
//
// ==UserScript==
// @name onecent
// @author Daniel C.
// @namespace http://userscripts.org/users/105896
// @description script to round the ".99" values in online shop prices, and do other things
// @include file:///home/daniel/comp/*
// @include http://fnac.pt/*
// @include http://www.amazon.com/*
// @include http://shop.lego.com/*
// ==/UserScript==
// CONFIGURATION
// Specify which cases are to be processed:
// $299 -> $300
CASE_99=true;
// $3.90 .. $3.99 -> $4.00
CASE_9X_CENT=true;
// $3.99 -> $4.00
CASE_99_CENT=true;
// $3.95 -> $4.00
CASE_95_CENT=true;
// the regular expression I use to find values
// the currency (optional); followed by zero or more space; the integer part, a separator and the cents (these two are optional)
// or the currency in the end...
// matches things like: $38, 28.4$, 19,99€
// nota: o \s faz match do
// podia usar [A-Z]{3} em vez de EUR|USD? existe o \b - word boundary?
// note: The (?: means I don't want to produce a reference here
//regexp=new RegExp("((?:€|\\$|EUR|USD) *)?([0-9]+)(([\.,])([0-9]+))?( *(?:€|\\$|EUR|USD))?");
currencies="(?:AED|AFN|ALL|ARS|ATS|AUD|BBD|BDT|BEF|BGN|BHD|BMD|BRL|BSD|CAD|CHF|CLP|CNY|COP|CRC|CYP|CZK|DEM|DKK|DOP|DZD|EEK|EGP|ESP|EUR|FIM|FJD|FRF|GBP|GRD|HKD|HRK|HUF|IDR|IEP|ILS|INR|IQD|IRR|ISK|ITL|JMD|JOD|JPY|KES|KRW|KWD|LBP|LKR|LUF|MAD|MTL|MUR|MXN|MYR|NLG|NOK|NZD|OMR|PEN|PHP|PKR|PLN|PTE|QAR|ROL|RON|RUB|SAR|SDG|SEK|SGD|SIT|SKK|THB|TND|TRY|TTD|TWD|USD|VEB|VEF|VND|XAF|XAG|XAU|XCD|XDR|XOF|XPD|XPF|XPT|ZAR|ZMK)"
regexpLeft="(€|\\$|"+currencies+")(\\s*)([0-9]+)(?:([\.,])([0-9]+))?"
regexpRight="([0-9]+)(?:([\.,])([0-9]+))?(\\s*)(€|\\$|"+currencies+")"
regexp=new RegExp(regexpLeft+"|"+regexpRight);
// PHASE ONE
// processes all the nodes
list = document.evaluate('//text()' ,document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
// will collect all the text from the page to a single string
allText="";
// this will keep the relation between positions in the string and the text nodes
textMap=[];
for (var i = 0; i < list.snapshotLength; i++) {
node=list.snapshotItem(i);
textMap.push( { pos:allText.length, node:node } );
allText+=node.textContent;
}
/*
// DUMP ALL THE nodeS:
GM_log("allText=[["+allText+"]]");
for (var n = 0; n<textMap.length; n++){
//for (var n = 360; n<380; n++){
GM_log("textMap n="+n+", pos="+textMap[n].pos); //+ ",node="+textMap[n].node.textContent);
if (n==textMap.length-1)
conteudo=allText.substring(textMap[n].pos);
else
conteudo=allText.substring(textMap[n].pos,textMap[n+1].pos);
GM_log("conteudo=["+conteudo+"]");
}
*/
// PHASE TWO
// here I will take notes of the changes to be done:
toDo=[];
startPos=0;
GM_log(allText);
while (match=regexp.exec(allText.substring(startPos))){
// the absolute position of the match
matchPos=startPos + match.index;
matchLen=match[0].length; // the complete size of the match
// advance the position to search for the next match
startPos+=match.index+matchLen;
if (match[1]){
currencyLeft=match[1];
spaceLeft=match[2];
number=match[3];
separator=match[4] || "";
cents=match[5] || "";
spaceRight="";
currencyRight="";
}else{
currencyLeft="";
spaceLeft="";
number=match[6];
separator=match[7] || "";
cents=match[8] || "";
spaceRight=match[9];
currencyRight=match[10];
}
//GM_log("match!"+match[0]+" - "+currencyLeft+"|"+number+"|"+separator+"|"+cents+"|"+currencyRight);
if (CASE_99 && number.match(/99$/) && (! cents || cents=="00")) {
// example: €399 or 399.00$
newNumber=""+(parseInt(number)+1);
newCents=cents;
}else if (CASE_99_CENT && cents=="99" || CASE_95_CENT && cents=="95" || CASE_9X_CENT && cents.match(/^9/) ){
// example: 15,99$
newCents="00";
newNumber=""+(parseInt(number)+1);
}else{
continue;
}
// Mark changes to do:
// each element of the regular expression can be in a different text node so
// I will process then separately
tt="era "+match[0];
p=matchPos;
toDo.push( {pos: p, len:currencyLeft.length, replaceText: currencyLeft, tooltip: tt} );
p+=currencyLeft.length;
// os espaços não precisam ficar
//toDo.push( {pos: p, len:spaceLeft.length, replaceText: spaceLeft, tooltip: tt} );
p+=spaceLeft.length;
toDo.push( {pos: p, len:number.length, replaceText: newNumber, tooltip: tt} );
p+=number.length;
if (separator && newCents){
toDo.push( {pos: p, len:separator.length, replaceText: separator, tooltip: tt} );
p+=separator.length;
toDo.push( {pos: p, len:cents.length, replaceText: newCents, tooltip: tt} );
p+=cents.length;
}
//toDo.push( {pos: p, len:spaceRight.length, replaceText: spaceRight, tooltip: tt} );
p+=spaceRight.length;
toDo.push( {pos: p, len:currencyRight.length, replaceText: currencyRight, tooltip: tt} );
}
// PHASE THREE
// now I will create span elements to replace the texts
// Execute the changes in reverse order, so that they don't interfere
// with future nodes and positions
// this is the index of the node in the textMap, starting from the end
n=textMap.length-1;
for (var a=toDo.length-1; a>=0; a--){
change=toDo[a];
if (change.len==0) continue;
// log
original=allText.substring(change.pos,change.pos+change.len);
GM_log("change "+original+" -> "+change.replaceText);
// search for the node where the position is
while (n>=0 && textMap[n].pos > change.pos) { n--; }
if (n<0) {GM_log("erro..."); break; } // should never happen...
node=textMap[n].node;
// the position relative to this node:
relPos=change.pos-textMap[n].pos;
if (relPos + change.len > node.textContent.length) {
GM_log("must be inside a single node!");
GM_log("the node is "+node.textContent);
continue;
}
//GM_log("esta no item "+n+", relPos="+relPos+",textContent="+node.textContent);
var style = getComputedStyle(node.parentNode, '');
// invert the text and background color:
var color=style.color;
var background=style.backgroundColor;
// nem sempre funciona por isso...
if (background=="transparent") {
background="white";
}
var newNode=document.createElement("span");
newNode.style.color=background;
newNode.style.backgroundColor=color;
newNode.innerHTML=change.replaceText;
newNode.title=change.tooltip
// split the node in three parts: Before the match, the match region and after
// the match. No problem if this creates empty nodes.
var node1=node.splitText(relPos);
var node2=node1.splitText(change.len);
node.parentNode.replaceChild(newNode,node1);
}