gylgamesh / Snitch!

// ==UserScript==
// @name           Snitch!
// @namespace      xbtc
// @include        http://www.waffles.ch/*
// @include        https://www.waffles.ch/*
// @include        http://waffles.ch/*
// @include        https://waffles.ch/*
// @description    Lets you know what you've already downloaded. (Version 0.9.10)
// ==/UserScript==

// @copyright      2009, 2010 James Campos
// @license        cc-by-3.0; http://creativecommons.org/licenses/by/3.0/
if (typeof GM_deleteValue == 'undefined') {

    GM_addStyle = function(css) {
        var style = document.createElement('style');
        style.textContent = css;
        document.getElementsByTagName('head')[0].appendChild(style);
    }

    GM_deleteValue = function(name) {
        localStorage.removeItem(name);
    }

    GM_getValue = function(name, defaultValue) {
        var value = localStorage.getItem(name);
        if (!value)
            return defaultValue;
        var type = value[0];
        value = value.substring(1);
        switch (type) {
            case 'b':
                return value == 'true';
            case 'n':
                return Number(value);
            default:
                return value;
        }
    }

    GM_log = function(message) {
        console.log(message);
    }

    GM_openInTab = function(url) {
        return window.open(url, "_blank");
    }

     GM_registerMenuCommand = function(name, funk) {
    //todo
    }

    GM_setValue = function(name, value) {
        value = (typeof value)[0] + value;
        localStorage.setItem(name, value);
    }
}

(function()
{
	
	
	var _protos={
		"waffles.ch":
		{
			siteName:				"Waffles.ch",
			svPrefix:				"wf",
			detailsPage:			"/details.php",
			torrentIdRegexp:		/id=([0-9]+)/,
			torrentLink:			"/details.php?id=",
			userLink:				"/userdetails.php",
			userIdRegexp:			/id=([0-9]+)/,
			snatchPage:				new RegExp("/snatches.php"),
			canShowHelp:			1,
			validateSnatchHref:		function(href,userId)
			{
				if(uploadsAreSnatches&&href.indexOf("/mytorrents.php")>-1) return true;
				return href.indexOf("/snatches.php")>-1 && href.indexOf("id=")==-1;
			},
			validateMarkableLink:	function(link, markId){return (link.href.indexOf("&to")==-1&&link.href.indexOf("&filel")==-1);},
			findNodeForInfoBox:		function()
			{
				var h2s=document.getElementsByTagName("h2");
				for(var i=0;i<h2s.length;i++) if(h2s[i].innerHTML.indexOf("Currently ")>-1) return h2s[i];
				return null;
			},
			checkLinkStatus:		function(link)
			{
				var idMatch=defs.torrentIdRegexp.exec(validLinks[i].href);
				if(!idMatch) return null;
				var id=parseInt(idMatch[1]);
				if(id==currentTorrentId) return null;
				return id;
			},
			calculateKnownSnatches:	function()
			{
				var n=0;
				for(var k in currentSnatches) n++;
				return n;
			}
		},
		"gazelle":
		{
			detailsPage:		"/torrents.php?id",
			groupIdRegexp:		/id=([0-9]+)/,
			torrentIdRegexp:	/torrentid=([0-9]+)/,
			
			torrentLink:		"/torrents.php?id",
			userLink:			"/user.php",
			userIdRegexp:		/id=([0-9]+)/,
			snatchPage:			new RegExp("torrents.php\?.*type=snatched.*"),
			uploadedPage:		new RegExp("torrents.php\?.*type=uploaded.*"),
			whatSwapRegexp:		new RegExp("swapDisplay\\('([^']+)"),
			canShowHelp:		0,
			ignoreIdIsCurrent:	false,
			validateSnatchHref:	function(href,userId)
			{
				var m=defs.userIdRegexp.exec(href);
				if(!(m&&m[1]==userId)) return false;
				if(uploadsAreSnatches&&defs.uploadedPage.exec(href)) return true;
				if(defs.snatchPage.exec(location.href)) return true;
				return false;
			},
			validateMarkableLink: function(link, markId)
			{
				// {{ Added for 0.9.9db9-b, 29/8/2009
				var toggleScript=link.getAttribute("onclick");
				if(toggleScript && toggleScript.indexOf(markId.toString()) == -1)
				{
					// The onclick handler may have torrent_XXXXXXX.
					// If XXXXXXX isn't the ID we want, it's not to be highlighted
					return false;
				}
				// }}
				var href=link.href;
				var hashIdx=href.indexOf("#");
				if(href.indexOf("&peers")>-1) return false;
				if(hashIdx==href.length-1||hashIdx<=0) return true;
				
				return false;
			},
			findNodeForInfoBox:	function()
			{
				var ds=document.getElementsByTagName("div");
				for(var i=0;i<ds.length;i++) if(ds[i].className=="linkbox") return ds[i];
				return null;
			},
			checkLinkStatus:	function(link)
			{
				var gidMatch=defs.groupIdRegexp.exec(link.href);
				if(!gidMatch) return null;
				var gid=parseInt(gidMatch[1]);
				if(snatchMode) currentSnatches[-gid]=1;
				var id;
				var idMatch=defs.whatSwapRegexp.exec(link.getAttribute("onclick"));
				if(idMatch)
				{
					id=parseInt(idMatch[1]);
				}
				else
				{
					idMatch=defs.torrentIdRegexp.exec(link.href);
					if(!idMatch)
					{
						if(currentSnatches[-gid]) return -gid;
						return null;
					}
					id=parseInt(idMatch[1]);
				}
				if(!snatchMode&&!currentSnatches[id]) return null;
				return id;
			},
			calculateKnownSnatches:	function()
			{
				var n=0;
				for(var k in currentSnatches) if(k<0) n++;
				return n;
			}
		}
	};
	var _extend=function(){
		var oo={};
		for(var i=0;i<arguments.length;i++)
		{
			var o=arguments[i];
			if(o) for(var k in o) oo[k]=o[k];
		}
		return oo;
	};
	var _defs={
		"waffles.ch":				function(){return _extend(_protos["waffles.ch"]);},
			};
	var version="0.9.10";
	function getXY(el)
	{
		var x=0;
		var y=0;
		while(el&&!isNaN(el.offsetLeft)){x+=el.offsetLeft;y+=el.offsetTop;el=el.parentNode;}
		return [x,y];
	}
	
	var validLinks=[];
	var userId=0;
	var userName="?";
	var idRegexp=/id=([0-9]+)/;
	var firstUse=false;
	var helpDiv=null;
	var x;
	var currentTorrentId=null;
	var currentSnatches=null;
	var snatchMode=false;
	var uploadsAreSnatches=false;
	var defs=null;	
	
	var locLower=location.href.toLowerCase();
	for(var k in _defs)
	{
		if(locLower.indexOf(k)>-1)
		{
			defs=_defs[k]();
			break;
		}
	}
	if(!defs)
	{
		GM_registerMenuCommand("Snitch! couldn't find defs for this site...", function(){alert("This means there's something curious going on. Contact the developer, maybe?")});
		return;
	}
	
	var onSnatchPage=null;
	var links=document.getElementsByTagName("a");
	var snatchLink=null;

	if(location.href.indexOf(defs.detailsPage)>-1)
	{
		try { currentTorrentId=parseInt(idRegexp.exec(location.href)[1]); } catch(e) {}
	}

	for(var i=0;i<links.length;i++)
	{
		if(links[i].href.indexOf(defs.torrentLink)>-1)
		{
			validLinks.push(links[i]);
			continue;
		}
		if(!userId&&links[i].href.indexOf(defs.userLink)>-1)
		{
			var m=defs.userIdRegexp.exec(links[i].href);
			if(m)
			{
				userId=parseInt(m[1]);
				userName=links[i].innerHTML;
				if(!GM_getValue(defs.svPrefix+"u"+userId)) firstUse=true;
				uploadsAreSnatches=(GM_getValue(defs.svPrefix+"uis"+userId)=="yes");
				onSnatchPage=defs.validateSnatchHref(location.href, userId);
			}
		}
		if(firstUse&&!snatchLink&&defs.snatchPage.exec(links[i].href))
		{
			snatchLink=links[i];
		}
	}
	var menuCmdPrefix="Snitch! ("+defs.siteName+"): #"+userId+" ("+userName+"): ";
	GM_registerMenuCommand(menuCmdPrefix+"Forget",
		function()
		{
			GM_setValue(defs.svPrefix+"sn"+userId,"");
			if(defs.canShowHelp&&confirm("Re-show the help tooltip too?")) GM_setValue(defs.svPrefix+"u"+userId,"");
			alert("I forgot all about "+userId+" now :("+(uploadsAreSnatches?"\n\nUploads-are-snatches is still on, though.":""));
		}
	);
	GM_registerMenuCommand(menuCmdPrefix+"Set upload handling...",
		function()
		{
			var newValue=confirm("Would you like to handle uploads as snatches?\nThis will mark all of your own uploads as snatches (provided you check your Uploads page).\n\nThis option is currently "+(uploadsAreSnatches?"ON":"OFF")+".\nChoose OK for \"Yes\", Cancel for \"No\".");
			GM_setValue(defs.svPrefix+"uis"+userId,(newValue?"yes":"no"));
			alert("Setting saved. Refresh the page to see changes.");
		}
	);
	
	snatchMode=onSnatchPage=defs.validateSnatchHref(location.href, userId);
	
	if(snatchLink&&!onSnatchPage)
	{
		if(defs.canShowHelp)
		{
			helpDiv=document.createElement("div");
			helpDiv.id="snitchHelpDiv";
			helpDiv.style.background="white";
			helpDiv.style.border="1px solid #000";
			helpDiv.style.padding="2px";
			helpDiv.style.maxWidth="300px";
			helpDiv.style.textAlign="left";
			helpDiv.innerHTML="<b>CAN HAS SNATCHES?</b><br/><br/>Hey "+userName+", this is Snitch! talking. You might want to click <b>My Snatches</b> to make sure I know of all the things you've downloaded and keep track of them.<p/>Don't worry, this popup won't appear once I know what you've been downloading.";
			helpDiv.style.position="absolute";
			var xy=getXY(snatchLink);
			helpDiv.style.left=xy[0]-1;
			helpDiv.style.top=xy[1]+snatchLink.offsetHeight+1;
			document.body.insertBefore(helpDiv,document.body.lastChild);
		}
		snatchLink.style.background="orange";
		snatchLink.style.color="black";
		snatchLink.style.fontWeight="bold";
		snatchLink.style.textDecoration="blink";
	}
	
	if(validLinks.length)
	{
		currentSnatches={};
		if(x=GM_getValue(defs.svPrefix+"sn"+userId)) x.split("|").forEach(function(v){ currentSnatches[parseInt(v,36)]=1; });
		for(var i=0;i<validLinks.length;i++)
		{
			var res=defs.checkLinkStatus(validLinks[i]);
			if(res)
			{
				if(snatchMode) currentSnatches[res]=1;
				var validLink=defs.validateMarkableLink(validLinks[i], res);
				if(currentSnatches[res] && validLink)
				{
					validLinks[i].innerHTML+=" <span style=\"color:white;font-weight:bold;background:red\" title=\"ID = "+res+"\">S</span>"; //  "+res+"
					var parent=validLinks[i].parentNode;
					if(parent.tagName=="td") parent.className+=" snitch-highlight";
				}
			}
		}
		if(snatchMode)
		{
			var out=[];
			var n=0;
			var known=defs.calculateKnownSnatches();
			for(var k in currentSnatches) { out.push(parseInt(k).toString(36)); }
			out=out.sort();
			GM_setValue(defs.svPrefix+"sn"+userId,out.join("|"));
			GM_setValue(defs.svPrefix+"u"+userId,":)");
			var ibn=defs.findNodeForInfoBox();
			if(ibn)
			{
				var infoDiv=document.createElement("div");
				infoDiv.align="center";
				var text=
					"<div style=\"border:1px dotted #999;padding:1em;margin:0.5em 0;"+
					"max-width:480px;font-weight:normal;font-size:8pt/10pt\">"+
					"Snitch! "+version+" <sup>by xbtc</sup> knows of <b>"+known+"</b> snatches.";
				if(uploadsAreSnatches) text+="<br/><b>Your uploads are currently also counted as snatches.</b><br/>";
				text+="<br/>If the \"known\" count above is <i style=\"border-bottom:1px dotted #999\" title=\"For some "+
					"reason, the total count may not be exactly what the tracker says. I don't know why.\">significantly</i> "+
					"lower than your current actual snatch count, just use the page controls to browse your snatches. "+
					"See your Tools - Greasemonkey - User Script Commands menu to reset snatch status.</div>";
				infoDiv.innerHTML=text;
				ibn.parentNode.insertBefore(infoDiv,ibn.nextSibling);
			}
			else
			{
				document.title+=" (Snitch!: "+out.length+")";
			}
			if(x=document.getElementById("snitchHelpDiv")) x.style.display="none";
		}
		delete out;
		delete currentSnatches;
	}
})();