// ==UserScript==
// @name           poeTV dejerker
// @namespace      http://monsterthreat.somethingnice.net/
// @description    dejerks poeTV, obviously (also 73q)
// @include        http://poetv.com/*
// @include        http://www.poetv.com/*
// @include        http://www.73q.com/*
// @include        http://73q.com/*
// @author         influence device
// @version        1.5a
// ==/UserScript==

// CONFIGURATION STUFF
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// this is your own user ID, which will be rendered in bold (or maybe inverse, see below); this makes your comments much easier
//  to pick out on the video pages.
// additionally, your votes & rankings will be shown highlighted, so you can quickly see what videos you've rated, etc.
const MY_USER_ID=999999;


// links to people on the two lists are recoloured in these colours.
// if you're some sort of colour-blind defective mutant, you might wish to set GOOD_COLOUR to 'blue' or similar. fuck if I know.
const BAD_COLOUR = '#ed1c24';	// poe-TV red
const GOOD_COLOUR = '#007236';	// poe-TV green


// the user comment pages are very broken - currently nine pages are not being shown for every one that is.
// until it's fixed by Chet, this patches it (but may display up to nine blank comment pages at the end of the list).
const FIX_COMMENT_PAGES = true;

// if true, lists of videos on the tag pages will be resorted by ID (which approximates the date) rather than alphabetically
const SORT_VIDEOS_ON_TAG_PAGES = true;

// if this is true, newer videos will appear at the top of the lists
const SORTED_VIDEOS_NEW_FIRST = true;


// if this is true, the votes column will be recoloured to GOOD_ or BAD_COLOUR
// (also, zero votes are altered to a small -, which makes it clearer which items have votes)
const RECOLOUR_HOPPER_VOTES = true;

// if this is true, the number of days will be recoloured too, shading from green to red.
// (only takes effect if RECOLOUR_HOPPER_VOTES is true)
const RECOLOUR_HOPPER_DAYS_TOO = true;

// if you set this to true, the +/- votes in the Hopper will shade from red to green, depending on the number of votes.
// if false, it's just +GOOD_COLOUR or -BAD_COLOUR (which I prefer).
// (only takes effect if RECOLOUR_HOPPER_VOTES is true)
const RECOLOUR_HOPPER_VOTE_SPECTRA = false;

// adds the Compare Hopper link to the submenu on a user's page
const ADD_COMPARE_HOPPER = true;


// some slightly less useful options;
// most people won't need to change these
//////////////////////////////////////////

// bounces all http://poetv.com and http://73q.com URLs to http://www.poetv.com and http://www.73q.com, which stops
// you having to re-login when accidentally switching between domains (I seem to do this a lot, for some reason)
const BOUNCE_TO_WWW = true;

// people you like effectively get to vote FAT times on the 'adjusted ratings'
const FAT = 3;

// dimensions of checkmark images on Hopper vote summaries
// if you have a very small font size, or you're easily amused, you might want to adjust this (base images are 16x16 pixels, 14x14 seems to work well for me)
const CHECKMARK_IMG_SIZE = ' width="14" height="14" ';

// you probably don't want this unless you're using a wee blurry laptop screen or something
//  - if set to true, makes your name show as inverse instead of bold (as with the rating summaries).
const SHOW_MY_ID_INVERSE = false;

// when comparing hopper voting histories, the site expects a valid video ID, even though it doesn't actually need it;
// in the spirit of poe-TV I have chosen the video "Handicapped Dance Battle" for this purpose.
// (only takes effect if ADD_COMPARE_HOPPER is true - you shouldn't need to change this unless that video is deleted)
const HOPPER_COMPARE_VIDEO_ID = '18830';



// that's it for things that can be easily configured
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////



const getVidNumber = /(\?|\&)vid=(\d+)/i;
const whatWhatWhat = /(\?|\&)what=([^\?\&]+)/i;
const findSubPageNumber = /(\?|\&)st=(\d+)/i;
const isVoting = /(\?|\&)what=voting/i;
const isUserComments = /(\?|\&)what=comments/i;
const getUserCommentPage = /(\?|\&)st=([^\?\&]+)/i;
const findPeoples = /<TR[^>]*><TD><a href='\/users.php\?userid=(\d+)' class='video-textub'>([^<]*)<\/a><\/TD><TD>((<img [^>]*>)*)<\/td>/gi;
const catchASmallGifStar=/smallstar\.gif/gi;
const findUserID = /userid=(\d+)/i;

const getPosVotes = /<font[^>]*>Positive Votes<\/font>(.*?)<(font|\/table)/i;
const getNegVotes = /<font[^>]*>Negative Votes<\/font>(.*?)<\/table/i;
const findVoter = /users.php\?userid=(\d+)'>([^<]*)<\/a>/gi;

const isYoutubeFullscreen = /(\?|\&)fs=1/i;




// helpy functions
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

const tmpArray=[];

function arrContains(arr, x)
{
	return arrFind(arr,x)==-1?false:true;
}

function arrFind(arr, x)
{
	for (var i=0;i<arr.length;i++)
	{
		if (arr[i]==x) return i;
	}
	return -1;
}

function xpath(query,context)
{
	if (context==null) context=document;
	return document.evaluate(query, context, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
}

function getStoredArray(name)
{
	var v=null;
	try { v = eval(GM_getValue(name, [])); } catch(ex) {}
	if (!v || v.constructor != Array) v = [];
	return v;
}

function setStoredArray(name, arr)
{
	GM_setValue(name, arr.toSource() );
}

function getBadPeople()
{
	return getStoredArray("badpeople");
}

function getGoodPeople()
{
	return getStoredArray("goodpeople");
}

// ranking code
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


// processes the text of showvotes.php to get the weighted rating
function getWeightedRating(txt)
{
	var line;
	var total = 0;
	var cnt = 0;
	var a = [0, 0,0,0,0,0, null]

	var bp = getBadPeople();
	var gp = getGoodPeople();

	while (true)
	{
		line = findPeoples.exec(txt);
		if (!line) break;

		a[6] = line[2];

		if (arrContains(tmpArray,line[1]))
		{
			a[5]++;
		}
		else if (arrContains(bp,line[1]))
		{
			a[3]++;
		}
		else
		{
			var vot = line[3].match(catchASmallGifStar);
			var vote = (vot? vot.length : 0);
			var w;
			if (line[1]==MY_USER_ID)
			{
				a[4]++;
				w = FAT;
			}
			else if (arrContains(gp,line[1]))
			{
				a[1]++;
				w = FAT;
			}
			else
			{
				a[2]++;
				w = 1;
			}

			total += vote * w;
			cnt += w;
		}
	}

	a[0] = (cnt ? total / cnt : 0);
	return a;
}


function ratingToStars(ratingStats, addSummary, verbose)
{
	var i = 5;
	var s = '';
	var r = Math.round(ratingStats[0]*2);

	while (i--)
	{
		s += "<img src='"

		if (r >= 2)
			s += "data:image/gif;base64,R0lGODlhCgAKAKUyAFdvm2Rvjl10n111nWZ8oG+Hr3GIr3GJsYeIfHeNsYyOfo+PgYqUen6Tto2WfomZd4OXuYykdIqfvoWqaoqocomqbpSkvomwbJOlw5ymup2nt5usx56txILCWH7EUJ+vyX/FUYLEVlTeAHXRPWbaIGnZJbrG3MvW59bd6tni8ePq9ebr9PP2+/X5//f6/ff7//j7/vn7/f///////////////////////////////////////////////////////yH5BAEAAD8ALAAAAAAKAAoAAAZGwJ/wh5EMjyvCIHVstUIjUef1+m0UFIZI9Lg4PoatWFyAVRDjScQlXJS2pMAwBhF5QKIES3gSZQ4NGiImQioCFkIcACg/QQA7";
		else if (r == 1)
			s += "data:image/gif;base64,R0lGODlhCgAKAKU3AFdvm1dwnV10n111nWZ8oG+Hr3GIr3GJsYeIfHeNsXqNsIyOfniPtI+PgYqUen6TtoOWuYOXuYeXs42YrYyZsIqfvoqocomqbo2hwZSkvpOlw5ymup2nt5usx56txH7EUKOuw5+vyYLEVp+xz1TeAHXRPWnZJbfA0brG3MrR3MvW59bd6tni8ePq9ebr9PLz9vP2+/b3+PX5//f6/ff7//j7/vn7/f///////////////////////////////////yH5BAEAAD8ALAAAAAAKAAoAAAZLwJ/wp6kMjy7CgHWUyUSl04hG+3UWFgfppcAwQgaSmHQrUwq1C0J8i0kgM2HDRE4FhrYI6QO6JWBCKiQbBw8cEyhCLQIZQh4AKz9BADs=";
		else
			s += "data:image/gif;base64,R0lGODlhCgAKAKU6AFdvm1dwnV10n111nWZ8oG6CpHKGrG+Hr3GIr3GJsXOKsXWKrXeNsXqNsHiPtH6TtoOWuYOXuYeXs42YrYyZsIabvoibuoqfvo2hwZaitpSkvpOlw5ymup2nt5usx56txKKtwqOuw5+vyZ+xz6Gyz7S+zLfA0brG3MfO2crR3MvW59bd6tni8ePq9ebr9PLz9vP09fT2+PP2+/b3+PX5//f6/ff7//j7/vn7/f7//f///////////////////////yH5BAEAAD8ALAAAAAAKAAoAAAZLwJ/wt7kMjy7CgHWk0Ugl08hm+3kMlQXs1cA4RAhKTkcmUw43SyFGnkkgNaECRU4FhriIDhTSMWRCKhkcCQ8dEydCLQIaQh8AKz9BADs=";

		s += "' border=0>";

		r -= 2;
	}

	if (addSummary)
		s += ratingSummary(ratingStats, verbose);

	return s;
}

function ratingSummary(ratingStats, verbose)
{
	var s='';
	const sep = '+';
	var c=0;


	if (ratingStats[4])
	{
		++c;
		s += '<span style="color:white;background-color:' + GOOD_COLOUR + '">' + ratingStats[4] + '</span>';
	}
	if (ratingStats[1])
	{
		++c;
		if (s != '') s += sep;
		s += '<span style="color:' + GOOD_COLOUR + '">' + ratingStats[1] + '</span>';
	}
	if (ratingStats[2])
	{
		++c;
		if (s != '') s += sep;
		s += ratingStats[2];
	}
	if (ratingStats[3])
	{
		++c;
		if (s != '') s += sep;
		s += '<span style="color:' + BAD_COLOUR + '">' + ratingStats[3] + '</span>';
	}
	if (ratingStats[5])
	{
		++c;
		if (s != '') s += sep;
		s += '<span style="color:white;background-color:' + BAD_COLOUR + '">' + ratingStats[5] + '</span>';
	}

	if (s)
	{
		var t = ratingStats[1] + ratingStats[2] + ratingStats[3] + ratingStats[4] + ratingStats[5];
		var ts = (c>1?t:s);
		s = (verbose? ('rated '+ts+' time'+(t>1?'s':'')) : ts) + (c > 1 ? ': ' + s : '');
	}


	if (ratingStats[6])
	{
		if (s != '') s += verbose?', last: ':', ';
		s += ratingStats[6];
	}
	if (s=='')
		s = 'not rated';
	return '<span class="video-text"> (' + s + ')</span>';
}

// vote code
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

// processes the text of showvotes.php to get the votes
function getVotes(txt)
{
	var line;
	var chunk;

	var a=[0,0,0,0,0, 0,0,0,0,0];

	var bp = getBadPeople();
	var gp = getGoodPeople();

	chunk = getPosVotes.exec(txt);
	while (chunk)
	{
		line = findVoter.exec(chunk[1]);
		if (!line) break;

		if (line[1]==MY_USER_ID)
			a[3]++;
		else if (arrContains(tmpArray,line[1]))
			a[4]++;
		else if (arrContains(gp,line[1]))
			a[0]++;
		else if (arrContains(bp,line[1]))
			a[2]++;
		else
			a[1]++;
	}


	chunk = getNegVotes.exec(txt);
	while (chunk)
	{
		line = findVoter.exec(chunk[1]);
		if (!line) break;

		if (line[1]==MY_USER_ID)
			a[8]++;
		else if (arrContains(tmpArray,line[1]))
			a[9]++;
		else if (arrContains(gp,line[1]))
			a[5]++;
		else if (arrContains(bp,line[1]))
			a[7]++;
		else
			a[6]++;
	}


	return a;
}

function voteForMe(voteStats)
{
	var i = 5;
	var s = '',ss;

	if (!voteStats)
		return 'no';
//s=voteStats;

	if (voteStats[0]+voteStats[1]+voteStats[2]+voteStats[3]+voteStats[4] != 0)
	{
		s += ' <img ' + CHECKMARK_IMG_SIZE + ' style="vertical-align:middle;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAAMRJREFUOMul0D1qAnEQBfDfxjUgaSyUtUxKqzRaeYTgLTyMF8gR0ljYpUhrGxAEwTJgaxewdS3yF5ZlP+ODgWFm3nszAwnSf0YSI/KHoXY43bijoNYEHbyEPMXooYVjgg/85htNNnjEBq+ZWqsNlvjCrqhZt8EMe3Rz9TRwKwUibPFW0Gt0whwXfJYN5AUmWGGNMRZ4r3tQ9oROuPmAI87ol/Aqf/AcyN8VxrVPnKJXJxBnCoPcwA+eQpQizriftEfqXlwBCN8zPGqyy/MAAAAASUVORK5CYII=" />';
		ss = '';
		if (voteStats[3])
		{
			ss += '<span style="color:white;background-color:' + GOOD_COLOUR + '">' + voteStats[3] + '</span>';
		}
		if (voteStats[0])
		{
			if (ss != '') ss += '+';
			ss += '<span style="color:' + GOOD_COLOUR + '">' + voteStats[0] + '</span>';
		}
		if (voteStats[1])
		{
			if (ss != '') ss += '+';
			ss += voteStats[1];
		}
		if (voteStats[2])
		{
			if (ss != '') ss += '+';
			ss += '<span style="color:' + BAD_COLOUR + '">' + voteStats[2] + '</span>';
		}
		if (voteStats[4])
		{
			if (ss != '') ss += '+';
			ss += '<span style="color:white;background-color:' + BAD_COLOUR + '">' + voteStats[4] + '</span>';
		}
		s += ' ' + ss;
	}

	if (voteStats[5]+voteStats[6]+voteStats[7]+voteStats[8]+voteStats[9] != 0)
	{
		s += ' <img ' + CHECKMARK_IMG_SIZE + ' style="vertical-align:middle;" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A/wD/oL2nkwAAANNJREFUOMul08FKQlEQxvGfINyFQbQQDYLiUoKC6MuqvUu1K9qFLltcoiAFfQd14QQXuV69NjCrM9//fGdmDrSwOTNbddTsoqlarP607aDBLQYlokHUCE17HzDGEt0CcTfORmWABC+Y4z4nTvGLp6g5CIALvOMn7N7gC29o5OoOAuAKM2T4xAcu92pKAdDHOrJXcH7UwTQcZFUdNKIH39GDu+jH6yk9SPBcMIUHLE6ZwiRmXfTmXmzfYxkgPbKJQ3TygFoAFmf+het67vaV6rHx39gCULZLxx1darkAAAAASUVORK5CYII=" />';
		ss = '';
		if (voteStats[8])
		{
			ss += '<span style="color:white;background-color:' + GOOD_COLOUR + '">' + voteStats[8] + '</span>';
		}
		if (voteStats[5])
		{
			if (ss != '') ss += '+';
			ss += '<span style="color:' + GOOD_COLOUR + '">' + voteStats[5] + '</span>';
		}
		if (voteStats[6])
		{
			if (ss != '') ss += '+';
			ss += voteStats[6];
		}
		if (voteStats[7])
		{
			if (ss != '') ss += '+';
			ss += '<span style="color:' + BAD_COLOUR + '">' + voteStats[7] + '</span>';
		}
		if (voteStats[9])
		{
			if (ss != '') ss += '+';
			ss += '<span style="color:white;background-color:' + BAD_COLOUR + '">' + voteStats[9] + '</span>';
		}

		s += ' ' + ss;
	}

	if (!s)
		s='(no votes)';


	return s;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

function processVideoPage()
{
	var mat;


	// enable fullscreen button
	var els = xpath("//object/embed[starts-with(@src,'http://www.youtube.com/')]");
	if (els && els.snapshotLength>=1)
	{
//		var targ = els.snapshotItem(0);
//		var el = targ.cloneNode(true);

		var el = els.snapshotItem(0);
		el.setAttribute("allowfullscreen","true");
		if (!isYoutubeFullscreen.exec(el.src))
		{
			el.src += '&fs=1';
		}
//		targ.parentNode.replaceChild(el, targ);
	}


	// can't check a video that's still in the hopper
	mat = isVoting.exec(location.search);
	if (mat) return;

	mat = getVidNumber.exec(location.search);
	if (!mat) throw 'no video number in URL - "' + location.href.toString() + '"';


	var videoID = mat[2];

	els = xpath("//a[starts-with(@href,'/showvotes.php?vid=') and @class='video-textus']");
	if (els && els.snapshotLength>=1)
	{
		var el = els.snapshotItem(0);

		var newel = document.createElement('span');
		newel.innerHTML = '<br /><a class="video-textus" style="font-size:8px" href="/showvotes.php?vid=' + videoID + '">Who voted for this shit?</a>';
		el.parentNode.insertBefore(newel,el.nextSibling);
	}


	GM_xmlhttpRequest({
		method: "GET",
		url: "http://" + location.hostname + "/showvotes.php?vid=" + videoID + "&what=showrating",
//		headers: { "User-Agent": "Dejerker/1.0", },

		onload: function(response)
		{
			if (response.status == 200)	// should always be
			{
				var els = xpath("//tr[@class='video-text']/td[starts-with(text(),'Rating:')]");

				if (els && els.snapshotLength>=1)
				{
					var targ = els.snapshotItem(0).nextSibling;
					if (response.responseText.indexOf('msql con') == 0)
					{
						targ.innerHTML += '<br />(msql con failed)'; 
					}
					else
					{
						var ratingStats = getWeightedRating(response.responseText);
						targ.innerHTML += '<br />' + ratingToStars(ratingStats, true, true);
					}
				}
				else
					GM_log("Unable to find 'Rating:' element! I suck at this");

			}
			else
				GM_log('HTTP response status: ' + response.status + ' ' + response.statusText);
		}
	});




}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

function doNextVideoEv(e)
{
	if (e && e.target)
	{
		e.target.removeEventListener('click',doNextVideoEv,false);
		e.target.style.color = 'grey';
		e.target.style.cursor = 'text';

		doNextVideo();
	}
}

function doNextVideo()
{
	var el;
	var els = xpath("//br[@dej!=0]");

	if (els.snapshotLength > 0)
	{
		el = els.snapshotItem(0);
		var videoID = el.getAttribute('dej');
		el.setAttribute('dej',0);

		GM_xmlhttpRequest({
			method: "GET",
			url: "http://" + location.hostname + "/showvotes.php?vid=" + videoID + "&what=showrating",
//			headers: { "User-Agent": "Dejerker/1.0", },

			onload: function(response)
			{
				if (response.status == 200)	// should always be
				{
					var ratingStats = getWeightedRating(response.responseText);
					var newel = document.createElement('span');
					newel.innerHTML = ratingToStars(ratingStats, true, false) + '<br />';
					el.parentNode.insertBefore(newel,el.nextSibling);

					doNextVideo();
				}
				else
					GM_log('HTTP response status: ' + response.status + ' ' + response.statusText);
			}
		});
	}
}


function processVideoListPage()
{
	var el, els;

	// kind of a half-assed way of doing this
	// but it's 4am
	els = xpath("//a[starts-with(@href,'/video.php?vid=')]/img[starts-with(@src,'/thumbs/')]");
	for (var i=0; i<els.snapshotLength; i++)
	{
		el = els.snapshotItem(i);

		mat = getVidNumber.exec(el.parentNode.href);
		if (!mat) throw 'no video number in URL - "' + el.parentNode.href + '"';

		var videoID = mat[2];

		el = el.parentNode;
		while (el && el.nodeName != 'BR')
		{
			el = el.nextSibling;
		}
		if (el)
			el.setAttribute('dej',videoID);
	}

	els = xpath("//a[starts-with(@href,'/video.php?vid=') and @class='video-link']");
	if (els.snapshotLength > 0)
	{
		el = els.snapshotItem(0);
		var newel = makeFakeButton('[fetch adjusted ratings]', doNextVideoEv, 999, 'i have no idea what i am doing', GOOD_COLOUR);

		el.parentNode.insertBefore(newel,el);
		el.parentNode.insertBefore(document.createElement('BR'),el);
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


function doNextHopperEv(e)
{
	if (e && e.target)
	{
		e.target.removeEventListener('click',doNextHopperEv,false);
		e.target.style.color = 'grey';
		e.target.style.cursor = 'text';

		doNextHopper();
	}
}



function doNextHopper()
{
	try
	{
		var el;
		var els = xpath("//a[@dej!=0]");

		if (els.snapshotLength > 0)
		{
			el = els.snapshotItem(0);
			var videoID = el.getAttribute('dej');
			el.setAttribute('dej',0);

			GM_xmlhttpRequest({
				method: "GET",
				url: "http://" + location.hostname + "/showvotes.php?vid=" + videoID,
//				headers: { "User-Agent": "Dejerker/1.0", },

				onload: function(response)
				{
					if (response.status == 200)	// should always be
					{
						var voteStats = getVotes(response.responseText);
						var newel = document.createElement('span');
						newel.className='video-text';
						newel.innerHTML = ' ' + voteForMe(voteStats);
						el.parentNode.insertBefore(newel,el.nextSibling);
						doNextHopper();
					}
					else
						GM_log('HTTP response status: ' + response.status + ' ' + response.statusText);
				}
			});
		}
	}
	catch(ex)
	{
		GM_log("doNextHopper failed: " + ex);
	}
}


function getSpectraColor( v )
{
	v = Math.min( Math.max(v,0) ,1);
	var iv = 1-v;

	return 'hsl(' + ((v * 150)-2) + ',' + ((v * 15)+85) + '%,' + ((iv*30)+22) + '%)';
}


function processTheHopper()
{
	var i, c, s, el, els;


//	els = xpath("//a[starts-with(@href,'/video.php?vid=') and @class='h-link']");
	els = xpath("//a[starts-with(@href,'video.php?vid=') and @class='h-link']");
	for (i=0; i<els.snapshotLength; i++)
	{
		el = els.snapshotItem(i);

		mat = getVidNumber.exec(el.href);
		if (!mat) throw 'no video number in URL - "' + el.href + '"';

		var videoID = mat[2];
		el.setAttribute('dej',videoID);
	}


	if (RECOLOUR_HOPPER_VOTES)
	{
		els = xpath("//td[@align='center' and @class='h-stats']");
		for (i=0; i<els.snapshotLength; i++)
		{
			el = els.snapshotItem(i);
			s = el.innerHTML;

			c = null;

			if (s && !isNaN(s))
			{
				if (s.length > 1)
				{
					if (s[0] == '+')
					{
						if (s > 0)
						{
							c = RECOLOUR_HOPPER_VOTE_SPECTRA? getSpectraColor((s*1 + 6)/12) : GOOD_COLOUR;
						}
						else
							el.innerHTML = '<small>-</small>';
						if (s >= 5)
						{
							el.style.fontWeight = 'bold';
							if (s >= 6)
								el.style.fontSize = 'larger';
						}

					}
					else if (s[0] == '-')
					{
						c = RECOLOUR_HOPPER_VOTE_SPECTRA? getSpectraColor((s*1 + 6)/12) : BAD_COLOUR;
					}

				}
				else if (RECOLOUR_HOPPER_DAYS_TOO && s > 0 && s <=7)
				{
					c = getSpectraColor( (s-1)/6 );
					if (s == 1)
						el.style.fontWeight = 'bold';
				}

				if (c)
					el.style.color = c;

			}

		}
	}

	els = xpath("//div[@class='s-top']");
	if (els.snapshotLength > 0)
	{
		el = els.snapshotItem(0);
		var newel = makeFakeButton('[get vote summaries]', doNextHopperEv, 999, 'i have no idea what i am doing', GOOD_COLOUR);

		el.parentNode.insertBefore(newel,el.nextSibling);
	}

}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

function styleSpecial(el, c)
{
	if (SHOW_MY_ID_INVERSE)
	{
		el.style.color = 'white';
		el.style.backgroundColor = c;
	}
	else
	{
		el.style.color = c;
		el.style.fontWeight = 'bold';
	}

}


function makeLinkOrNot(userID, pageID, currentPageID, highestPageID,s)
{
	return pageID==currentPageID ?
		((currentPageID-9) + ' ')
	:
		'<a class="video-textu" ' + (pageID > highestPageID?'style="color:red"':'') + ' href="/users.php?userid=' + userID +
			'&what=comments&st=' + (pageID/10) + '">' + (pageID-9) + (s?s:'') + '</a><small> </small>';
	;
}

function processUserPage()
{
	var i,j,k;
	var userID;
	var isGood,isBad,noButtons;
	var bp = getBadPeople();
	var gp = getGoodPeople();

	k = location.search.match(findUserID);
	if (!k) return;
	userID = k[1];

	var els = xpath("//tr/td/big/b");
	if (els.snapshotLength > 0)
	{
		j = els.snapshotItem(0);

		isGood = isBad = noButtons = false;
		if (userID == MY_USER_ID)
		{
			noButtons = true;
			styleSpecial(j,GOOD_COLOUR);
		}
		else if (arrContains(tmpArray,userID))
		{
			noButtons = true;
			styleSpecial(j,BAD_COLOUR);
		}
		else if (arrContains(bp, userID))
		{
			isBad = true;
			j.style.color = BAD_COLOUR;
		}
		else if (arrContains(gp, userID))
		{
			isGood = true;
			j.style.color = GOOD_COLOUR;
		}

		if (!noButtons)
			addButtons(j,userID,isGood,isBad);
	}


	// add the Compare Hopper button
	if (ADD_COMPARE_HOPPER)
	{
		els = xpath("//a[text()='Compare' and starts-with(@href,'/users.php?userid=" + userID + "&what=compare')]");
		if (els.snapshotLength > 0)
		{
			j = els.snapshotItem(0);
			i = j.parentNode;
		
			i.innerHTML += ' | <a class="nav-link" href="/showvotes.php?vid=' + HOPPER_COMPARE_VIDEO_ID + '&what=compare&cuserid=' + userID + '">Compare Hopper</a>';
		}
	}


	if (FIX_COMMENT_PAGES && location.href.match(isUserComments))
	{
		var highest = 10;
		var current = 10;

		k = getUserCommentPage.exec(location.href);
		if (k && k[2] && !isNaN(k[2]*1) )
			current = Math.max(1,Math.round(k[2]*10));


//		els = xpath("//a[@class='video-textu']/..");
		els = xpath("//td/a[starts-with(text(),'Next') or starts-with(text(),'Previous')]");
		if (els.snapshotLength > 0)
		{
			j = els.snapshotItem(0);
			k = /\s*(\d+)\s*$/.exec(j.parentNode.textContent);
			if (k && k[1] && !isNaN(k[1]*1) )
				highest = Math.max(1,Math.round(k[1]*10));
			j.parentNode.style.display='none';
		}

		els = xpath("//td[@colspan=2 and text()='1 ']");
		for (i=0; i<els.snapshotLength; ++i)
		{
			j = els.snapshotItem(i);
			j.style.display='none';
		}


		els = xpath("//a[@class='video-textu' and starts-with(@href,'/users.php?userid=" + userID + "&st=')]");
		for (i=0; i<els.snapshotLength; ++i)
		{
			j = els.snapshotItem(i);
			j.style.display='none';
		}

		els = xpath("//tr/td/b[starts-with(text(),'Comments')]/../..");
		if (els.snapshotLength > 0)
		{
			var comments = els.snapshotItem(0);
			
			var ntr = document.createElement('tr');

			j = '';


			var lo = Math.max(10,current-15);
			var hi = Math.min(highest+10,current+15+1);


			if (lo > 10)
			{
				j += makeLinkOrNot(userID,10,current,highest);
				if (lo > 11)
					j += "<small> ... </small>";
			}

			for (i = lo; i < hi; ++i)
				j += makeLinkOrNot(userID,i,current,highest,((i==hi-1)&&(hi==highest+1))?'+':'');


			if (hi <= highest)
			{
				if (hi < highest)
					j += "<small> ... </small>";
				j += makeLinkOrNot(userID,highest,current,highest,'+');
			}
			
			ntr.innerHTML = '<td colspan="2">' + j + '</td>';
			comments.parentNode.insertBefore(ntr,comments);

			ntr = document.createElement('tr');
			ntr.innerHTML = '<td colspan="2">' + j + '</td>';
			comments.parentNode.insertBefore(ntr,null);

		}		
	}
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

function sortVideosByID(a,b)
{
	return a[0] < b[0];
}

function processTagsPage()
{
	var el, els, mat;
	var i;

	var n=[];
	var els2=[];


	if (SORT_VIDEOS_ON_TAG_PAGES)
	{

		els = xpath("//a[starts-with(@href,'/video.php?vid=') and @class='tags-link']");
		for (i=0; i<els.snapshotLength; i++)
		{
			el = els.snapshotItem(i);

			mat = getVidNumber.exec(el.href);
			if (!mat) continue;
			els2.push(el);
			n.push([mat[2]*1,el.innerHTML]);
		}
		n.sort(sortVideosByID);
		if (!SORTED_VIDEOS_NEW_FIRST) n.reverse();
		for (i=0; i<els2.length; i++)
		{
			el = els2[i];
			el.href='/video.php?vid=' + n[i][0];
			el.innerHTML = n[i][1];
		}
	}
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

function addToGood(e)
{
	if (!e.target) return;
	var id = e.target.getAttribute("dejerk_dat");
	if (!id) return;

	bp = getBadPeople();
	gp = getGoodPeople();

	if (arrContains(bp,id) || arrContains(gp,id))
		return;
	gp.push(id);
	setStoredArray("goodpeople", gp);
	window.location.reload();
}
function addToBad(e)
{
	if (!e.target) return;
	var id = e.target.getAttribute("dejerk_dat");
	if (!id) return;

	bp = getBadPeople();
	gp = getGoodPeople();

	if (arrContains(bp,id) || arrContains(gp,id))
		return;
	bp.push(id);
	setStoredArray("badpeople", bp);
	window.location.reload();
}

function removeFromGood(e)
{
	if (!e.target) return;
	var id = e.target.getAttribute("dejerk_dat");
	if (!id) return;

	gp = getGoodPeople();

	var i = arrFind(gp,id);
	if (i == -1)
		return;
	gp.splice(i,1);
	setStoredArray("goodpeople", gp);
	window.location.reload();
}

function removeFromBad(e)
{
	if (!e.target) return;
	var id = e.target.getAttribute("dejerk_dat");
	if (!id) return;

	bp = getBadPeople();

	var i = arrFind(bp,id);
	if (i == -1)
		return;
	bp.splice(i,1);
	setStoredArray("badpeople", bp);
	window.location.reload();
}

function makeFakeButton(innerHTML, fn, dat, tooltip, col, id)
{
	newel = document.createElement('span');
	newel.innerHTML=innerHTML;
	newel.setAttribute('class','video-text');
	newel.style.cursor = 'pointer';
//	newel.style.fontFamily='monospace';
	newel.setAttribute("dejerk_dat",dat);
	newel.setAttribute("title",tooltip);
	if (col) newel.style.color = col;
	if (id) newel.setAttribute("id",id);
	newel.addEventListener('click',fn,false);
	return newel;
}


function addButtons(j,userID,isGood,isBad)
{
	var newel;

	if (isGood)
	{
		newel = makeFakeButton(' [x]', removeFromGood, userID, 'remove from happy fun list');
		j.parentNode.insertBefore(newel,j.nextSibling);
	}
	else if (isBad)
	{
		newel = makeFakeButton(' [x]', removeFromBad, userID, 'remove from jerkmarks');
		j.parentNode.insertBefore(newel,j.nextSibling);
	}
	else
	{
		newel = makeFakeButton(' [+]', addToBad, userID, 'jerkmark', BAD_COLOUR);
		j.parentNode.insertBefore(newel,j.nextSibling);

		newel = makeFakeButton(' [+]', addToGood, userID, 'so pretty', GOOD_COLOUR);
		j.parentNode.insertBefore(newel,j.nextSibling);
	}
}


// makes everyone pretty
function letsHaveADanceContest(doBtns)
{
	var i,j,k;
	var userID;
	var isGood,isBad,noButtons;
	var bp = getBadPeople();
	var gp = getGoodPeople();

	var els = xpath("//a[(starts-with(@href,'/users.php') or starts-with(@href,'http://www.poetv.com/users.php') or starts-with(@href,'http://poetv.com/users.php') or starts-with(@href,'http://www.73q.com/users.php') or starts-with(@href,'http://73q.com/users.php')) and (not(@class) or @class!='mainmenu2')]");
	for (i = 0; i < els.snapshotLength; i++)
	{
		j = els.snapshotItem(i);

		k = j.href.match(findUserID);
		if (!k) continue;

		userID = k[1];

		isGood = isBad = noButtons = false;

		if (userID == MY_USER_ID)
		{
			noButtons = true;
			styleSpecial(j,GOOD_COLOUR);
		}
		else if (arrContains(tmpArray,userID))
		{
			noButtons = true;
			styleSpecial(j,BAD_COLOUR);
		}
		else if (arrContains(bp, userID))
		{
			isBad = true;
			j.style.color = BAD_COLOUR;
		}
		else if (arrContains(gp, userID))
		{
			isGood = true;
			j.style.color = GOOD_COLOUR;
		}

		if (!doBtns || noButtons || j.href.match(whatWhatWhat) || j.href.match(findSubPageNumber))
			continue;

		addButtons(j,userID,isGood,isBad);
	}

}

// main
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

function dothings()
{
	try
	{
//		GM_log('is this thing on');

		if (BOUNCE_TO_WWW && location.hostname.toString().indexOf('www.') != 0)
		{
			window.location.replace(location.protocol + "//www." + location.host + location.pathname + location.search + (location.hash? location.hash : ''));
			return;
		}

		var doBtns = false;
		var noDancing = false;

		switch(location.pathname)
		{
			case '/video.php':
				processVideoPage();
				doBtns = true;
				break;

			case '/users.php':
				noDancing = true;
				processUserPage();
				break;

			case '/showvotes.php':
				doBtns = true;
				break;

			case '/':
			case '/index.php':
			case '/hot-video-clips.html':
			case '/highest-rated-week.html':
			case '/highest-rated-month.html':
			case '/highest-rated-alltime.html':
			case '/most-favorited-week.html':
			case '/most-favorited-month.html':
			case '/most-favorited-alltime.html':
			case '/most-viewed-clips-week.html':
			case '/most-viewed-clips-month.html':
			case '/most-viewed-clips-alltime.html':

			case '/hot-video-clips-73q.html':
			case '/highest-rated-week-73q.html':
			case '/highest-rated-month-73q.html':
			case '/highest-rated-alltime-73q.html':
			case '/most-favorited-week-73q.html':
			case '/most-favorited-month-73q.html':
			case '/most-favorited-alltime-73q.html':
			case '/most-viewed-clips-week-73q.html':
			case '/most-viewed-clips-month-73q.html':
			case '/most-viewed-clips-alltime-73q.html':
				processVideoListPage();
//				doBtns = true;
				break;

			case '/the-hopper.php':
				processTheHopper();
				break;


			case '/tags.php':
				processTagsPage();
				break;


		}

		if (!noDancing)
			letsHaveADanceContest(doBtns);
	}
	catch(ex)
	{
		GM_log('I have experienced terminal misfortune:\n\t' + ex);
	}
}

dothings();

