Tattoo Counter

Counts tattoos and shows missing tattoos in Kingdom of Loathing.

// ==UserScript==
// @name           Tattoo Counter
// @namespace      kol.interface.unfinished
// @description    Counts tattoos and shows missing tattoos in Kingdom of Loathing.
// @include        https://*kingdomofloathing.com/account_tattoos.php*
// @include        http://kol.coldfront.net/thekolwiki/index.php/Tattoo
// @include        http://kol.coldfront.net/thekolwiki/index.php/Tattoos
// @version        1.2.3
// @grant          GM_getValue
// @grant          GM_setValue
// ==/UserScript==

//Version 1.2.3
// - switch to https
//Version 1.2.2
// - add @grant
//Version 1.2.1
// - fix for minor format editing of wiki page
//Version 1.2
// - update for latest sspd tattoo
//Version 1.1
// - fixed to handle changed html presentation from wiki update
//Version 1.0

function doPage() {
    // find table root of area interested in
    var yourTattoos = {};
    var count = 0;
    var c = document.evaluate('//b[text()="Change Your Tattoo:"]',document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null);
    if (c.singleNodeValue) {
        var ts = document.evaluate('//form[@action="account_tattoos.php"]//img',document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
        for (var i=0;i<ts.snapshotLength;i++) {
            var t = ts.snapshotItem(i).getAttribute('title');
            var s = ts.snapshotItem(i).getAttribute('src');
            if (s && t) {
                s = s.replace(/.*\//,'');
                yourTattoos[s] = t;
                count++;
            }
        }
        GM_setValue('yourtattoos',JSON.stringify(yourTattoos));
        addButton(c.singleNodeValue.parentNode,count);
        if (GM_getValue('showmissing',''))
            showMissing();
    } 
}

function addButton(c,count) {
    var total = GM_getValue('knowntattoocount','?');
    c.appendChild(document.createTextNode('\u00A0'+count+'/'+total));
    var v = document.createElement('a');
    v.setAttribute('href','#');
    v.setAttribute('id','showmissingtattoobutton');
    v.setAttribute('style','font-size:x-small;vertical-align:middle;color:white;');
    fixupButton(v);
    v.addEventListener('click',bHandler,false);
	
    c.appendChild(document.createTextNode('\u00a0\u00a0\u00a0'));
    c.appendChild(v);
	
    var goWiki = document.createElement('a');
    goWiki.setAttribute('title','Click to go to the full list of tattoos on the KoL wiki.');
    goWiki.setAttribute('target','_blank');
    goWiki.setAttribute('id','tattoowikilink');
    goWiki.setAttribute('style','font-size:x-small;vertical-align:middle;color:white;');
    goWiki.setAttribute('href','http://kol.coldfront.net/thekolwiki/index.php/Tattoo');
    goWiki.appendChild(document.createTextNode('[Load tattoo list]'));
    c.appendChild(document.createTextNode('\u00A0'));
    c.appendChild(goWiki);
}

function fixupButton(v) {
    if (GM_getValue('showmissing','')) {
        v.setAttribute('title','Click to hide all missing tattoos.');
        if (v.firstChild)
            v.replaceChild(document.createTextNode('[Hide Missing]'),v.firstChild);
        else
            v.appendChild(document.createTextNode('[Hide Missing]'));
    } else {
        v.setAttribute('title','Click to display all missing tattoos.');
        if (v.firstChild)
            v.replaceChild(document.createTextNode('[Show Missing]'),v.firstChild);
        else
            v.appendChild(document.createTextNode('[Show Missing]'));
    }
}

function showMissing() {
    var v = document.getElementById('showmissingtattoobutton');
    if (GM_getValue('showmissing','')) {
        missing();
    } else {
        var xx = document.getElementsByClassName('showmissingaddition');
        for (var i=xx.length-1;i>=0;i--) {
            var x = xx[i];
            x.parentNode.removeChild(x);
        }
    }
    fixupButton(v);
}

function bHandler(e) {
    var b = GM_getValue('showmissing','');
    GM_setValue('showmissing', (b ? '' : 'true'));
    showMissing();
}

// show all missing tattoos
function missing() {
    if (!GM_getValue('knowntattoocount'))
        return;
	
    var tbl = document.evaluate('//form[@action="account_tattoos.php"]//center/table',document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null);
    if (tbl.singleNodeValue) {
        var t = tbl.singleNodeValue;
        var r = t.insertRow(-1);
        r.setAttribute('class','showmissingaddition');
        var c = r.insertCell(-1);
        c.setAttribute('colspan','12');
        c.setAttribute('align','center');
        c.appendChild(document.createTextNode('Missing Tattoos'));
		
        r = t.insertRow(-1);
        r.setAttribute('class','showmissingaddition');
        c = r.insertCell(-1);
        c.setAttribute('align','center');
		
        var lastrc = 0;
        var alltattoos = JSON.parse(GM_getValue('knowntattoos','[]'));
        var eventtattoos = JSON.parse(GM_getValue('knowneventtattoos','[]'));
        var yourtattoos = JSON.parse(GM_getValue('yourtattoos','{}'));
        var parity = 1;
        for (var sigil in alltattoos) {
            if (!yourtattoos[sigil]) {
                if (lastrc==0) {
                    r = t.insertRow(-1);
                    r.setAttribute('class','showmissingaddition');
                }
                r.insertCell(-1);
                c = r.insertCell(-1);
                c.setAttribute('valign','center');
                lastrc = (lastrc+1) % 6;
                var img = document.createElement('img');
                img.setAttribute('src','http://images.kingdomofloathing.com/otherimages/sigils/'+sigil);
                img.setAttribute('width','50');
                img.setAttribute('height','50');
                img.setAttribute('title','Missing tattoo: '+sigil.replace(/\.gif$/,''));
                var lnk = alltattoos[sigil];
                if (lnk!='#') {
                    var a = document.createElement('a');
                    img.setAttribute('border','0');
                    a.setAttribute('href','http://kol.coldfront.net'+lnk);
                    a.setAttribute('target','_blank');
                    a.appendChild(img);
                    img = a;
                }
                c.appendChild(img);
            }
        }
		
        for (var e in eventtattoos) {
            var found = false;
            var lastSigil;
            var yourSigil;
            for (var sigil in eventtattoos[e]) {
                lastSigil = eventtattoos[e][sigil];
                if (yourtattoos[eventtattoos[e][sigil]]) {
                    yourSigil = eventtattoos[e][sigil];
                    found = true;
                }
            }
            if (!found || lastSigil!=yourSigil) {
                if (lastrc==0) {
                    r = t.insertRow(-1);
                    r.setAttribute('class','showmissingaddition');
                }
                r.insertCell(-1);
                c = r.insertCell(-1);
                c.setAttribute('valign','center');
                lastrc = (lastrc+1) % 6;
                var img = document.createElement('img');
                img.setAttribute('src','http://images.kingdomofloathing.com/otherimages/sigils/'+lastSigil);
                img.setAttribute('width','50');
                img.setAttribute('height','50');
                if (!found)
                    img.setAttribute('title','Missing tattoo: '+lastSigil.replace(/\.gif$/,''));
                else
                    img.setAttribute('title','You have tattoo version '+yourSigil.replace(/\.gif$/,'')+', but are still missing tattoo '+lastSigil.replace(/\.gif$/,''));
                c.appendChild(img);
            }
        }
    }
    return true;
}

// gather a section of tattoos
function gatherTattoos(a,tattoos) {
    var count=0;
    var t = a.nextSibling;
    while (t && t.tagName!='TABLE') {
        if (t.tagName=='H2')
            return;
        t = t.nextSibling;
    }
    if (t) {
        var ts = document.evaluate('.//img',t,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
        for (var i=0;i<ts.snapshotLength;i++) {
            var tsa = ts.snapshotItem(i).getAttribute('alt');
            if (tsa) {
                tsa = tsa.replace(/Image:/i,'').replace(/ /g,'_').toLowerCase();
                if (tsa=='fratboytat.gif') tsa = 'fratboy.gif';
                else if (tsa=='minertat.gif') tsa = 'miner.gif';
                else if (tsa=='clowntat.gif') tsa = 'clown.gif';
                else if (tsa=='nobeertat.gif') tsa = 'nobeer.gif';
                else if (tsa=='palette2.gif') tsa = 'palette.gif';
                else if (tsa=='martinitat.gif') tsa = 'martini.gif';
                // skip hobotat 
                if (tsa!='hobotat19.gif') {
                    if (ts.snapshotItem(i).parentNode.tagName=='A')
                        tattoos[tsa] = ts.snapshotItem(i).parentNode.getAttribute('href');
                    else
                        tattoos[tsa] = '#';
                    count++;
                }
            }
        }
        if (t.nextSibling && t.nextSibling.tagName!='H2')
            gatherTattoos(t,tattoos);
    }
}

// gather data from a kol wiki page
function gather() {
    var tattoos = {};
    var eventTattoos = {};
    var count=0;
    var sections = ['Class','Outfit','Ascension'];
	
    for (var i=0;i<sections.length;i++) {
        var ts = document.evaluate('//span[@id="'+sections[i]+'_Tattoos"]',document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null);
        if (ts.singleNodeValue)
            gatherTattoos(ts.singleNodeValue.parentNode,tattoos);
    }
	
    var ts = document.evaluate('//span[@id="Miscellaneous_Tattoos"]',document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null);
    if (ts.singleNodeValue) {
        var tempTattoos = [];
        gatherTattoos(ts.singleNodeValue.parentNode,tempTattoos);
        eventTattoos['sspd'] = [];
        for (var s in tempTattoos) {
            var m = s.match(/^sspd.*([0-9]+)\.gif/);
            if (!m)
                m = s.match(/^sspd([0-9]+).*\.gif/);
            if (!m)
                m = s.match(/^sspd[a-z]*([0-9]+)[a-z]*\.gif/);
            if (m) {
                eventTattoos['sspd'][m[1]] = s;
            } 
        }
    }
	
    var ts = document.evaluate('//span[@id="Event_Tattoos"]',document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null);
    if (ts.singleNodeValue) {
        var tempTattoos = [];
        gatherTattoos(ts.singleNodeValue.parentNode,tempTattoos);
        eventTattoos['aol'] = [];
        for (var s in tempTattoos) {
            var m = s.match(/^aol.*([0-9]+)\.gif/);
            if (m) {
                eventTattoos['aol'][m[1]] = s;
            }
        }
        eventTattoos['hobotat'] = ['hobotat1.gif','hobotat2.gif','hobotat3.gif','hobotat4.gif','hobotat5.gif',
                                   'hobotat6.gif','hobotat7.gif','hobotat8.gif','hobotat9.gif','hobotat10.gif',
                                   'hobotat11.gif','hobotat12.gif','hobotat13.gif','hobotat14.gif','hobotat15.gif',
                                   'hobotat16.gif','hobotat17.gif','hobotat18.gif','hobotat19.gif'];
    }
	
    for (var x in tattoos) 
        count++;
    for (var x in eventTattoos) {
        count++;
    }
	
    console.log('Loaded '+count+' tattoos from the wiki');
    if (count>0) {
		GM_setValue('knowntattoocount',count);
		GM_setValue('knowntattoos',JSON.stringify(tattoos));
		GM_setValue('knowneventtattoos',JSON.stringify(eventTattoos));
    }
}


if (window.location.href.indexOf('http://kol.coldfront.net/thekolwiki/index.php/')>=0) {    
    gather();
} else {
    doPage();
}