JHunz's KOL Consumption Management

By JHunz Last update Apr 21, 2008 — Installed 617,239 times.

Add Syntax Highlighting (this will take a few seconds, probably freezing your browser while it works)

// ==UserScript==
// @name           JHunz's KOL Consumption Management
// @namespace      hunsley@gmail.com
// @description    Warns you when are about to overdrink via the inventory.  Tracks fullness and spleen.
// @include        *kingdomofloathing.com/*.php*
// ==/UserScript==

//
// Version 1.0	02/04/2008	Released.
//					Limitations: 	
//						1) Only works on the inventory page   
//						2) Does not take NPC booze stores (Gnomish Cafe/Crimbo) into account  
//						3) May or may not replace links created by other scripts, depending on run order.
// Version 1.1	02/10/2008  Update: Drunkenness amounts are now displayed in drink links.  Is also now compatible with compact mode.
//
// Version 2.0    02/21/2008 	Renamed to Consumption Management after a total revamp to include food and spleen.
//					New features:
//						1) Fullness and spleen hits are now included in the text of links to eat or use spleen items.
//						2) Integration with cmeister's itemdb, so that new items will be recognized by the script as soon as he adds them
//						3) Consumption of food and spleen via the normal inventory links is now tracked.  Tracked fullness and spleen are displayed on the character pane.
// Version 2.1    02/24/2008  Bugfix: Logging in can no longer cause an infinite loop.
// Version 2.2    02/25/2008  Bugfix: Counters are now reset properly upon ascension.
// Version 2.2.1  02/25/2008  Bugfix to previous bugfix.  I swear it's right now.
// Version 2.3	02/26/2008  Updated to account for the new date display in the chat launch pane.
//					Auto-update implemented in case they sneak in anything else that might break it.
// Version 2.4    03/12/2008  Updated active pages so that links generated by other scripts should be handled as long as it runs last.
//                            Added Micromicrobrewery and Chez Snootee support, and crimbo support if that store follows the same conventions.
//                            Automatically updates overdrunk level on Steel Margarita consumption
//					Added support for detection of multi-using spleen items
// Version 2.5	04/19/2008	Added protection against overdrinking by accidentally double-clicking a drink link.
//					The food and spleen counters should no longer allow or show fullness or spleen greater than your current maximum.  In other news, your current max fullness and spleen are now tracked.
//					You can now sort food and drink by total adventures, or adventures per fullness/drunk					
// Version 2.5.1	04/20/2008	Added a conditional to prevent a recurring error in the non-consumable sections of the inventory
// Version 2.5.2	04/21/2008	Unknown and special items (e.g. dusty bottles) should no longer be sorted randomly among your other items.
const VERSION=252;

//auto-update checking code
var lastUpdateCheck = GM_getValue('lastUpdateCheck','NEVER');
var curTime = new Date().getTime();
if ((lastUpdateCheck == 'NEVER') || (parseInt(lastUpdateCheck) < (curTime - 86400000))) {
	GM_setValue('lastUpdateCheck',''+curTime);
	GM_xmlhttpRequest({method: 'GET',url: 'http://userscripts.org/scripts/source/22297.user.js',
				onload: function(responseDetails) {
					var bodyText = responseDetails.responseText;
					var curVersionNum = parseInt((bodyText.split("const VERSION="))[1].split(";")[0]);
					if (curVersionNum > VERSION) {
						GM_setValue('outOfDate','true');
					}
					else {
						GM_setValue('outOfDate','false');
					}
				}
			});
}
var curVersion = GM_getValue('curVersion','0');
if (parseInt(curVersion) != VERSION) {
	GM_setValue('curVersion',VERSION);
	GM_setValue('outOfDate','false');
}
//Nag user with update link if the script is out of date
var outOfDate = GM_getValue('outOfDate');
if((outOfDate == 'true') && (window.location.pathname != "/charpane.php") && (window.location.pathname != "/chatlaunch.php")) {
	var newElement = document.createElement('tr');
	newElement.innerHTML = '<tr><td><div style="color: red;font-size: 90%;width: 100%;text-align:center;">A new version of Consumption Management is available! <a href="http://userscripts.org/scripts/source/22297.user.js" target="_blank">Update</a></div></td></tr>';

	//insert the counter at the top of the page
	var element = document.getElementsByTagName("tr")[0];
	element.parentNode.insertBefore(newElement,element);
}


if (window.location.pathname == "/login.php") {
	GM_setValue("curDate","");
	GM_setValue("curCharName",'UNDEFINED');
	return false;
}

var ITEMDB_URL = "http://kol.cmeister2.co.uk/items/?mode=limiter2";
updatedb();
var itemdb = getdb();

//Set up configs for various words to use for fullness/spleen
if (GM_getValue("fullnessText",'UNDEFINED')=='UNDEFINED') {
	GM_setValue("fullnessText",'Ful,Fullness,Repletion,Satiation,Gluttony,Belt Size');
	GM_setValue("spleenText",'Spl,Black Bile,Splenomegaly,Spleneticism','Spleenerality');
	alert("This is the first time the consumption manager script has run.  If you are currently logged in, please be aware that it will not work properly until you log out and back in.");
}
//Set up configs new in 2.4
if (GM_getValue("drunkText",'UNDEFINED')=='UNDEFINED') {
	GM_setValue("drunkText",'Drunk,Tipsiness,Drunkenness,Temulency,Inebriety');
	alert("Note: For optimum compatibility with other scripts,\nConsumption manager should be set to run AFTER any other scripts\nthat might insert eat, drink, or use links onto the page.");
}
//Configs new in 2.5
var invSortOrder = GM_getValue("invSortOrder",'UNDEFINED');
if (invSortOrder=='UNDEFINED') {
	invSortOrder = 'alpha';
	GM_setValue('invSortOrder',invSortOrder);
}

var fullnessDisplayTR,spleenDisplayTR;

//On the charpane, we get the current character name and drunkenness
if (window.location.pathname == "/charpane.php") {
	// Get the current name
	var charName = document.getElementsByTagName("b")[0].textContent;
	GM_setValue('curCharName',charName);

	//Grab drunkenness
	var drunkTextNode,moxTextNode;
	var textNodes = document.evaluate("//text()",document,null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,null);
	for (var i=0;i<textNodes.snapshotLength;i++) {
		node = textNodes.snapshotItem(i);
		s=node.data;
		if(s.match("^Temulency:$") || s.match("^Tipsiness:$") || s.match("^Drunkenness:$") || s.match("^Inebriety:$") || s.match("^Drunk:$")) {
			drunkTextNode = node;
		}
		else if (s.match("^Moxie:$") || s.match("^Mox:$")) {
			moxTextNode = node;
		}
	}
	if(drunkTextNode) {
		drunkenness = parseInt(drunkTextNode.parentNode.nextSibling.textContent);
	}
	else {
		drunkenness = 0;
	}
	GM_setValue(charName + '.drunkenness',drunkenness);
	//alert("drunkenness is " + drunkenness);

	//Build fullness and spleen stat nodes
	fullnessDisplayTR = document.createElement('tr');
	var fullnessDisplayTD1 = document.createElement('td');
	var fullnessDisplayTD2 = document.createElement('td');
	var fullnessDisplayB = document.createElement('b');
	fullnessDisplayB.appendChild(document.createTextNode('temp'));
	fullnessDisplayTD1.align = 'right';
	fullnessDisplayTD1.appendChild(document.createTextNode(GetFullnessText(moxTextNode.data)));
	fullnessDisplayTD2.align = 'left';
	fullnessDisplayTD2.appendChild(fullnessDisplayB);
	fullnessDisplayTR.appendChild(fullnessDisplayTD1);
	fullnessDisplayTR.appendChild(fullnessDisplayTD2);

	spleenDisplayTR = document.createElement('tr');
	var spleenDisplayTD1 = document.createElement('td');
	var spleenDisplayTD2 = document.createElement('td');
	var spleenDisplayB = document.createElement('b');
	spleenDisplayB.appendChild(document.createTextNode('temp'));
	spleenDisplayTD1.align = 'right';
	spleenDisplayTD1.appendChild(document.createTextNode(GetSpleenText(moxTextNode.data)));
	spleenDisplayTD2.align = 'left';
	spleenDisplayTD2.appendChild(spleenDisplayB);
	spleenDisplayTR.appendChild(spleenDisplayTD1);
	spleenDisplayTR.appendChild(spleenDisplayTD2);


	//Insert fullness and spleen stats after the moxie node.
	curDate = GM_getValue('curDate','');
	if(curDate == '') {
		//date not known yet, so we don't know if the fullness/spleen are accurate.  Add ? and set timeout
		fullnessDisplayB.textContent = '?';
		spleenDisplayB.textContent = '?';

		moxTextNode.parentNode.parentNode.parentNode.appendChild(fullnessDisplayTR);
		moxTextNode.parentNode.parentNode.parentNode.appendChild(spleenDisplayTR);

		window.setTimeout(CharPaneTimeout,1500);

	}
	else {
		//date known, so add fullness/spleen as appropriate.  don't set timeout.
		var fullness = GM_getValue(charName + '.fullness');
		var spleen = GM_getValue(charName + '.spleen');

		fullnessDisplayB.textContent = fullness;
		spleenDisplayB.textContent = spleen;

		if (fullness != 0) {
			moxTextNode.parentNode.parentNode.parentNode.appendChild(fullnessDisplayTR);
		}
		if (spleen != 0) {
			moxTextNode.parentNode.parentNode.parentNode.appendChild(spleenDisplayTR);
		}
	}

}
else {
	var charName = GM_getValue('curCharName','UNDEFINED');
	var drunkenness = GM_getValue(charName + '.drunkenness','UNDEFINED');
}

var t,curDateString;
//Get date from chat launch pane. 
//If rollover has passed since the script last ran, reset fullness and spleen.
if (window.location.pathname == "/chatlaunch.php") {
	var dateParentNode,dateParentNode2;
	centeredTextNodes = document.evaluate("//center//text()",document,null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,null);
	for (var i=0;i<centeredTextNodes.snapshotLength;i++) {
		if (centeredTextNodes.snapshotItem(i).data.match("^Today's KoL date is ")) {
			dateParentNode = centeredTextNodes.snapshotItem(i).parentNode;
			break;
		}
		else if (centeredTextNodes.snapshotItem(i).data.match("Today is:")) {
			dateParentNode2 = centeredTextNodes.snapshotItem(i).parentNode;
			break;
		}	
	}
	if(dateParentNode) {
		curDateString = dateParentNode.getElementsByTagName('b')[0].textContent;
	}
	else if (dateParentNode2) {
		curDateString = dateParentNode2.lastChild.textContent;
	}
	else {
		alert("Can't find date parent node.  Consumption manager won't work right.");
	}
	
	curDateString = curDateString.replace(/\s/,'');
	t = window.setInterval(ChatLaunchFunction,100);

	return false;
}

//If the character name is undefined, exit and wait for the script to run on the character pane first
if (charName == 'UNDEFINED') {
	return false;
}

//Check if the script has been run before.  If not, set all preferences to defaults.
var overdrunk = GM_getValue(charName + '.overdrunk','UNDEFINED');
if (overdrunk == 'UNDEFINED') {
	overdrunk = 15;
	GM_setValue(charName + '.overdrunk',overdrunk);
}

//Configs new in 2.5
var overfull = GM_getValue(charName + '.overfull','UNDEFINED');
var overspleen = GM_getValue(charName + '.overspleen','UNDEFINED');
if (overfull=='UNDEFINED') {
	overfull = 16;
	GM_setValue(charName + '.overfull',overfull);
}
if (overspleen=='UNDEFINED') {
	overspleen = 16;
	GM_setValue(charName + '.overspleen',overspleen);
}

//Account page - create an element to let the user set their overdrunk threshold
if (window.location.pathname == "/account.php") {
	//title bar
	var titleBar = document.createElement('div');
	with (titleBar) {
		appendChild(document.createTextNode('Consumption Management'));
		style.padding = "1px 0 1px 0";
		style.textAlign = "center";
		style.color = "white";
		style.backgroundColor = "blue";
		style.fontWeight = "bold";
	}

	//create form
	var optionsForm = document.createElement('form');
	optionsForm.style.textAlign = 'center';

	//create text input for overdrunk level
	var textInput = document.createElement('input');
	with(textInput) {
		setAttribute("type","text");
		setAttribute("value",overdrunk);
		setAttribute("name","overdrunk");
		style.width='15%';
	}

	//create button to save overdrunk level
	var saveButton = document.createElement('input');
	with (saveButton) {
		setAttribute("class","button");
		setAttribute("type","button");
		setAttribute("value","Save");
		addEventListener('click',function(event) {
			overdrunk = parseInt(this.previousSibling.value);
			GM_setValue(charName + '.overdrunk',overdrunk);
		},false);
	}

	//Create dropdown to select inventory sorting type.
	var sortSelect = document.createElement('select');
	var sortOptionAlpha = document.createElement('option');
	with(sortOptionAlpha) {
		value = 1;
		appendChild(document.createTextNode('Alphabetical'));
		if (invSortOrder == 'alpha') {
			selected = 'true';
		}
	}
	var sortOptionTotalAdv = document.createElement('option');
	with(sortOptionTotalAdv) {
		value = 2;
		appendChild(document.createTextNode('Average Adv Gain (Total)'));
		if (invSortOrder == 'totalAdv') {
			selected = 'true';
		}
	}
	var sortOptionPerAdv = document.createElement('option');
	with(sortOptionPerAdv) {
		value = 3;
		appendChild(document.createTextNode('Average Adv Gain (Per Fullness/Drunkenness)'));
		if (invSortOrder == 'perAdv') {
			selected = 'true';
		}
	}
	sortSelect.appendChild(sortOptionAlpha);
	sortSelect.appendChild(sortOptionTotalAdv);
	sortSelect.appendChild(sortOptionPerAdv);

	sortSelect.addEventListener('change',function(event) {
		var newTarget = this.selectedIndex;
		if (newTarget == 0) {
			invSortOrder = 'alpha';
		}
		else if (newTarget == 1) {
			invSortOrder = 'totalAdv';
		}
		else if (newTarget == 2) {
			invSortOrder = 'perAdv';
		}
		GM_setValue('invSortOrder',invSortOrder);
	},false);
	
	//Create a nice table to hold the options
	var optTab = document.createElement('table');
	var optTr1 = document.createElement('tr');
	var optTr2 = document.createElement('tr');
	var optTd1 = document.createElement('td');
	var optTd2 = document.createElement('td');
	var optCenter1 = document.createElement('center');
	var optCenter2 = document.createElement('center');

	optCenter1.appendChild(document.createTextNode('How drunk is too drunk? '));
	optCenter1.appendChild(textInput);
	optCenter1.appendChild(saveButton);
	optTd1.appendChild(optCenter1);
	optTr1.appendChild(optTd1);
	optCenter2.appendChild(document.createTextNode('Sorting of consumables on inventory page '));
	optCenter2.appendChild(sortSelect);
	optTd2.appendChild(optCenter2);
	optTr2.appendChild(optTd2);
	optTab.appendChild(optTr1);
	optTab.appendChild(optTr2);

	//add stuff to form
	with(optionsForm) {
		appendChild(optTab);
	}

	wrapper = document.createElement('div');
	with(wrapper) {
		style.width = '95%';
		style.border = 'thin solid blue';
		appendChild(titleBar);
		appendChild(optionsForm);
	}

	var loc = document.getElementsByTagName('center')[2];
	if(loc.lastChild.textContent.indexOf("This account will") != 0)var loc = document.getElementsByTagName('center')[3];
	loc.insertBefore(wrapper,loc.lastChild);
}

//Reset thresholds upon ascension
if (window.location.pathname == "/valhalla.php") {
	GM_setValue(charName + '.overdrunk',15);
	GM_setValue(charName + '.overfull',16);
	GM_setValue(charName + '.overspleen',16);
	GM_setValue(charName + '.fullness',0);
	GM_setValue(charName + '.spleen',0);
}

//Cafe page - Detect buying the standard drinks/foods and daily specials
if (window.location.pathname == "/cafe.php") {
	if (window.location.search) {
		var cafeid = parseInt(window.location.search.split("cafeid=")[1].split("&")[0]);
	}
	else {
		//submitting the form without selecting a drink takes you to cafe.php...without the parameters.
		breweryNode = document.evaluate('//b[.="The Gnomish Micromicrobrewery"]',document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;
		if (breweryNode) {
			cafeid=2;
		}
		else {
			chezNode = document.evaluate('//b[.="Chez Snootée"]',document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;
			if (chezNode) {
				cafeid=1;
			}
		}

	}
	var consumeType,whichItemSelected,consumeAmount,consumeTypeText;

	var cafeForm = document.evaluate('//form[@action="cafe.php"]',document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;
	var originalSubmitButton = document.evaluate('//form[@action="cafe.php"]//input[@class="button" and @type="submit"]',document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;
	var radioButtons = document.evaluate('//form[@action="cafe.php"]//input[@type="radio" and @name="whichitem"]',document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);

	//detects only the node that gets checked
	for(var i=0;i<radioButtons.snapshotLength;i++) {
		radioButtons.snapshotItem(i).addEventListener('change',function() {
			if (this.checked) {
				whichItemSelected = parseInt(this.value);

				//NPC-only items
				if(whichItemSelected < 0) {
					whichItemSelected = whichItemSelected.toString().replace('-','neg');
					if(cafeid == 1) {
						whichItemSelected += 'f';
					}
					else if (cafeid == 2) {
						whichItemSelected += 'b';
					}
					else {
						tempConsumption = HowMuchConsumption(whichItemSelected + 'f');
						if(tempConsumption != 999) {
							whichItemSelected += 'f';
						}
						else {
							whichItemSelected += 'b';
						}
					}
					consumeType = ConsumptionType(whichItemSelected);
					consumeTypeText = GetConsumptionText('',consumeType).toLowerCase().replace(':','');
					consumeAmount = HowMuchConsumption(whichItemSelected);				
				
				}
				else {
					consumeType = ConsumptionType(whichItemSelected);
					consumeTypeText = GetConsumptionText('',consumeType).toLowerCase().replace(':','');
					consumeAmount = HowMuchConsumption(whichItemSelected);
				}

				if((consumeAmount != 999)&&(consumeType != 'unknown')) {
					originalSubmitButton.value = 'Order ('+consumeAmount+' '+consumeTypeText+')';
				}
				else {
					originalSubmitButton.value = 'Order (unknown item)';
				}
			}
		},false);
	}

	originalSubmitButton.type = 'button';
	originalSubmitButton.addEventListener('click',function() {
		if(whichItemSelected) {
			//if spleen or fullness type, just add that amount and submit.
			//if booze, then special protection ensues.
			if (ConsumptionType(whichItemSelected)=='spleen') {
				AddSpleen(whichItemSelected);
				cafeForm.submit();
			}
			else if (ConsumptionType(whichItemSelected)=='food') {
				AddFullness(whichItemSelected);
				cafeForm.submit();	
			}
			else if (ConsumptionType(whichItemSelected)=='booze') {
				//Overdrink protection
				drunkenness = GM_getValue(charName + '.drunkenness');
				overdrunk = GM_getValue(charName + '.overdrunk');

				if ((drunkenness + consumeAmount) >= overdrunk) {
					if (originalSubmitButton.value != 'Order (overdrink)') {
						alert("This drink will cause you to overdrink");
						originalSubmitButton.value = 'Order (overdrink)';
					}
					else {
						GM_setValue(charName + '.drunkenness',(drunkenness + consumeAmount));
						cafeForm.submit();
					}
				}
				else {
					cafeForm.submit();
				}
			}
			else {
				cafeForm.submit();
			}
		}
		else {
			cafeForm.submit();
		}
	
	},false);
}

//All other pages - Replace drinking,eating,spleen using links with a link that calls the event listener functions.  Also display amounts of usage in each link.
if ((window.location.pathname != "/charpane.php")&&(window.location.pathname != "/chatlaunch.php")&&(window.location.pathname != "/account.php")&&(window.location.pathname != "/valhalla.php")&&(window.location.pathname != "/cafe.php")&&(window.location.pathname != "/login.php")) {
//if (window.location.pathname == "/inventory.php") {
	var drinkLinks = document.evaluate('//a[contains(@href,"inv_booze.php") and contains(@href,"&which=1")]',document,null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,null);
	var foodLinks = document.evaluate('//a[contains(@href,"inv_eat.php") and contains(@href,"&which=1")]',document,null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,null);
	var spleenLinks = document.evaluate('//a[contains(@href,"inv_use.php") and contains(@href,"&which=1")]',document,null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,null);

	//variables used for re-sorting categories of items in the inventory
	var drinkNodeArray = new Array();
	var foodNodeArray = new Array();
	var spleenNodeArray = new Array();

	//Drink link management
	for (var i=0;i<drinkLinks.snapshotLength;i++) {
		node = drinkLinks.snapshotItem(i);
		node.style.visibility = "hidden";
		ChangeTextContent(node,"[overdrink]");

		var whichDrink = parseInt(node.href.split("&whichitem=")[1].split("&")[0]);
		var drinkDrunkenness = HowMuchConsumption(whichDrink);

		var newDrinkNode = document.createElement('a');
		with (newDrinkNode) {
			if (drinkDrunkenness == 999) {
				ChangeTextContent(newDrinkNode,'[drink]');
			}
			else {
				ChangeTextContent(newDrinkNode,'[drink (' + drinkDrunkenness + ')]');
			}
			addEventListener('click',OverdrinkProtect,false);
			href = 'javascript:void(0)';
		}
		node.parentNode.insertBefore(newDrinkNode,node);

		//Add to the array, for possible re-sorting
		if (window.location.pathname == "/inventory.php") {
			drinkNodeArray[i] = new Object();
			drinkNodeArray[i].node = document.evaluate('./ancestor::td[1]',node,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;
			drinkNodeArray[i].imgNode = drinkNodeArray[i].node.previousSibling;
			drinkNodeArray[i].adv = getAdventures(whichDrink);
			drinkNodeArray[i].advPer = ((drinkDrunkenness == 999)||(drinkDrunkenness == 0))?0:(drinkNodeArray[i].adv / parseFloat(drinkDrunkenness));
			drinkNodeArray[i].name = drinkNodeArray[i].node.firstChild.textContent;
		}

		//Update overdrunk threshold on steel margarita consumption
		if (whichDrink == 2743) {
			node.addEventListener('click',function() {
				GM_setValue(charName + '.overdrunk',20);
			},false);
		}
	}

	//Eat link management
	for (var i=0;i<foodLinks.snapshotLength;i++) {
		node = foodLinks.snapshotItem(i);

		var whichFood = parseInt(node.href.split("&whichitem=")[1].split("&")[0]);
		var foodConsumption = HowMuchConsumption(whichFood);

		if (foodConsumption != 999) {
			ChangeTextContent(node,node.textContent.split(']')[0] + ' (' + foodConsumption + ')]');
		}

		node.addEventListener('click',AddFullness,false);

		//Add to the array, for possible re-sorting
		if (window.location.pathname == "/inventory.php") {
			foodNodeArray[i] = new Object();
			foodNodeArray[i].node = document.evaluate('./ancestor::td[1]',node,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;
			foodNodeArray[i].imgNode = foodNodeArray[i].node.previousSibling;
			foodNodeArray[i].adv = getAdventures(whichFood);
			foodNodeArray[i].advPer = foodNodeArray[i].adv / parseFloat(foodConsumption);
			foodNodeArray[i].name = foodNodeArray[i].node.firstChild.textContent;
		}
	}

	//Use link management (spleen)
	for (var i=0;i<spleenLinks.snapshotLength;i++) {
		node = spleenLinks.snapshotItem(i);

		var whichSpleen = parseInt(node.href.split("&whichitem=")[1].split("&")[0]);
		var spleenConsumption = HowMuchConsumption(whichSpleen);

		if((spleenConsumption != 999) && (IsSpleen(whichSpleen))) {
			ChangeTextContent(node,node.textContent.split(']')[0] + ' (' + spleenConsumption + ')]');
		}

		node.addEventListener('click',AddSpleen,false);

		//Add to the array, for possible re-sorting
		//if (window.location.pathname == "/inventory.php") {
		//	spleenNodeArray[i] = new Object();
		//	spleenNodeArray[i].node = document.evaluate('./ancestor::td[1]',node,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;
		//	spleenNodeArray[i].imgNode = spleenNodeArray[i].node.previousSibling;
		//	spleenNodeArray[i].adv = getAdventures(whichSpleen);
		//}
	}

	//TODO - HANDLE MULTI-USE OF SPLEEN ITEMS HERE
	//form action=multiuse.php
	//official multi-use page has a select[@name="whichitem"] with options underneath where value = item#
	//Mr. Script and possibly other scripts use an input[@type="hidden" and @name="whichitem"] and value= item#
	//both types use standard submit buttons
	//both types use an input[@type="text" and @name="quantity"] where the value is the number to use
	//
	//Add an event listener to the submit button which adds the specified amount * the amount per item

	//Processes only the first multi-use form on a page because it is easier that way and there are unlikely to be more
	multiUseForm = document.evaluate('//form[@action="multiuse.php"]',document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;
	if (multiUseForm) {
		multiSubmit = document.evaluate('.//input[@class="button" and @type="submit"]',multiUseForm,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;
		numInput = document.evaluate('.//input[@type="text" and @name="quantity"]',multiUseForm,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;

		var whichItem;

		if (numInput && multiSubmit) {

			//handling of the official multi-use form and and other multi-use forms using a select
			selectNode = document.evaluate('.//select[@name="whichitem"]',multiUseForm,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;
			if (selectNode) {
				if(selectNode.selectedIndex) {
					whichItem = selectNode.options[selectNode.selectedIndex].value;
					if(IsSpleen(whichItem)) {
						multiSubmit.value = 'Use (' + HowMuchConsumption(whichItem) + ' ' + GetConsumptionText('','spleen').toLowerCase().replace(':','') + ' each)';
					}
				}
				selectNode.addEventListener('change',function() {
					if(this.selectedIndex) {
						whichItem = this.options[this.selectedIndex].value;
						if(IsSpleen(whichItem)) {
							multiSubmit.value = 'Use (' + HowMuchConsumption(whichItem) + ' ' + GetConsumptionText('','spleen').toLowerCase().replace(':','') + ' each)';
						}
						else {
							multiSubmit.value = 'Use';
						}
					}
				},false);	
			}
			//handling of various multi-use boxes added by Mr. Script and probably other scripts
			else {
				hiddenInput = document.evaluate('.//input[@type="hidden" and @name="whichitem"]',multiUseForm,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;
				if (hiddenInput) {
					whichItem = hiddenInput.value;
					if(IsSpleen(whichItem)) {
						multiSubmit.value = 'Use (' + HowMuchConsumption(whichItem) + ' ' + GetConsumptionText('','spleen').toLowerCase().replace(':','') + ' each)';
					}
				}
			}

			//event listener on the submit button adds the appropriate amount
			multiSubmit.addEventListener('click',function() {
				if (IsSpleen(whichItem)) {
					usedNum = parseInt(numInput.value);
					if (usedNum > 0) {
						AddConstSpleen((usedNum * HowMuchConsumption(whichItem)));
					}
				}
			},false);
		}
	}

	//Add dropdown to select inventory sorting order
	if (window.location.search.match("which=1")) {
		var sortSelectTr = document.createElement('tr');
		var sortSelectTd = document.createElement('td');
		sortSelectTd.innerHTML = '<center>Food/Booze sort order:<select id="sortSelect"><option value="1"' + ((invSortOrder == 'alpha')?' selected="true">':'>') 
						+ 'Alphabetical</option><option value="2"' + ((invSortOrder == 'totalAdv')?' selected="true">':'>')
						+ 'Average Adventure Gain (Total)</option><option value="3"' + ((invSortOrder == 'perAdv')?' selected="true">':'>')
						+ 'Average Adventure Gain (Per Fullness/Drunkenness)</option></select></center>';
		sortSelectTd.setAttribute('colspan','4');
		sortSelectTr.appendChild(sortSelectTd);

		var sortSelectTrParent = document.evaluate('//div[@id="section1"]//table//tbody',document,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null).singleNodeValue;
		if (sortSelectTrParent && sortSelectTrParent.firstChild) {
			sortSelectTrParent.insertBefore(sortSelectTr,sortSelectTrParent.firstChild);
		}

		var sortSelect = document.getElementById('sortSelect');
		sortSelect.addEventListener('change',function(event) {
			var newTarget = this.selectedIndex;
			var prevSortOrder = invSortOrder;
			if (newTarget == 0) {
				invSortOrder = 'alpha';
			}
			else if (newTarget == 1) {
				invSortOrder = 'totalAdv';
			}
			else if (newTarget == 2) {
				invSortOrder = 'perAdv';
			}
			GM_setValue('invSortOrder',invSortOrder);
			if (prevSortOrder != invSortOrder) {
				SortInv(foodNodeArray,drinkNodeArray);
			}
		},false);

	
		if(invSortOrder != 'alpha') {
			SortInv(foodNodeArray,drinkNodeArray);
		}
	}
}

//This event listener function is where all the magic happens.
function OverdrinkProtect() {
	//Get new values for drunkenness and overdrunk, since they might have changed
	drunkenness = GM_getValue(charName + '.drunkenness');
	overdrunk = GM_getValue(charName + '.overdrunk');

	var drinkHref = this.nextSibling.href;
	var whichDrink = parseInt(drinkHref.split("&whichitem=")[1].split("&")[0]);

	var drinkDrunkenness = HowMuchConsumption(whichDrink);
	
	if (drinkDrunkenness == 999) {
		alert("Unknown drink.  Drink at your own risk.");
		this.nextSibling.style.visibility = 'visible';
		ChangeTextContent(this.nextSibling,"[overdrink?]");
		this.removeEventListener("click",OverdrinkProtect,false);
		this.parentNode.removeChild(this);
	}
	else if((drunkenness + drinkDrunkenness) >= overdrunk) {
		alert("This drink will cause you to overdrink.");
		this.nextSibling.style.visibility = 'visible';
		this.removeEventListener("click",OverdrinkProtect,false);
		this.parentNode.removeChild(this);
	}
	else {
		//Since we know we're exiting the page, clean up event listeners
		var drinkLinks = document.evaluate('//a[contains(@href,"javascript:void(0)")]',document,null,XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,null);
		for (var i=0;i<drinkLinks.snapshotLength;i++) {
			drinkLinks.snapshotItem(i).removeEventListener("click",OverdrinkProtect,false);
		}

		//redirect to the actual drinking
		GM_setValue(charName + '.drunkenness',(drunkenness + drinkDrunkenness));
		window.location.href = drinkHref;
	}
}

//Callback to add fullness when you eat
function AddFullness(itemID) {
	if ((itemID==undefined)||(typeof itemID=='object')) {
		whichFood = parseInt(this.href.split("&whichitem=")[1].split("&")[0]);
	}
	else {
		whichFood = itemID;
	}
	addFullness = HowMuchConsumption(whichFood);
	if (addFullness != 999) {
		var curFullness = GM_getValue(charName + '.fullness');
		if ((parseInt(curFullness) + parseInt(addFullness)) < overfull) {
			GM_setValue(charName + '.fullness',parseInt(curFullness) + parseInt(addFullness));

			if (whichFood == 2742) {
				overfull = 21;
				GM_setValue(charName + '.overfull',overfull);
			}
		}
	}
}

//Callback to add spleen
function AddSpleen(itemID) {
	if ((itemID==undefined)||(typeof itemID=='object')) {
		whichSpleen = parseInt(this.href.split("&whichitem=")[1].split("&")[0]);
	}
	else {
		whichSpleen = itemID;
	}
	addSpleen = HowMuchConsumption(whichSpleen);
	if (addSpleen != 999) {
		var curSpleen = GM_getValue(charName + '.spleen');
		if ((parseInt(curSpleen) + parseInt(addSpleen)) < overspleen) {
			GM_setValue(charName + '.spleen',parseInt(curSpleen) + parseInt(addSpleen));

			if (whichSpleen == 2744) {
				overspleen = 21;
				GM_setValue(charName + '.overspleen',overspleen);
			}
		}
	}
}

//Add a specific number of spleen instead of passing itemID
function AddConstSpleen(numSpleen) {
	var curSpleen = GM_getValue(charName + '.spleen');
	if ((parseInt(curSpleen) + parseInt(numSpleen)) < overspleen) {
		GM_setValue(charName + '.spleen',parseInt(curSpleen) + parseInt(numSpleen));
	}
}

//Function to generate appropriate random spleen text, given the mox text (to know whether it's compact)
function GetSpleenText(moxText) {
	textArray = GM_getValue('spleenText').split(',');
	if(moxText == 'Mox:') {
		return (textArray[0] + ':');
	}
	else {
		return (textArray[rand(textArray.length - 1)] + ':');
	}
}

//Function to generate appropriate random fullness text, given the mox text (to know whether it's compact)
function GetFullnessText(moxText) {
	textArray = GM_getValue('fullnessText').split(',');
	if(moxText == 'Mox:') {
		return (textArray[0] + ':');
	}
	else {
		return (textArray[rand(textArray.length - 1)] + ':');
	}
}

//functions a little differently since it is never added to the charpane
function GetDrunkText() {
	textArray = GM_getValue('drunkText').split(',');
	return (textArray[rand(textArray.length - 1)]);
}

function GetConsumptionText(moxText,consumeType) {
	switch(consumeType) {
		case 'booze':return GetDrunkText();break;
		case 'food':return GetFullnessText(moxText);break;
		case 'spleen':return GetSpleenText(moxText);break;
	}
	return 'unknown';
}

//Callback to re-check fullness/spleen on charpane
function CharPaneTimeout() {
	var curDate = GM_getValue('curDate','');
	var fullness = GM_getValue(charName + '.fullness');
	var spleen = GM_getValue(charName + '.spleen');

	if ((curDate == '') || (fullness == 0)) {
		fullnessDisplayTR.parentNode.removeChild(fullnessDisplayTR);
	}
	else {
		fullnessDisplayTR.lastChild.firstChild.textContent = fullness;
	}

	if ((curDate == '') || (spleen == 0)) {
		spleenDisplayTR.parentNode.removeChild(spleenDisplayTR);
	}
	else {
		spleenDisplayTR.lastChild.firstChild.textContent = spleen;
	}
}

function ChatLaunchFunction() {
	//alert("chat launch function triggered");
	charName = GM_getValue('curCharName','UNDEFINED');

	if (charName == 'UNDEFINED') {
		return false;
	}
	else {
		var storedDate = GM_getValue(charName + '.storedDate','');
		if(storedDate != curDateString) {
			//alert("resetting variables\ncurDateString:"+curDateString+"\nstoredDate:"+storedDate
			//	+"\nlengths:"+curDateString.length+ " " +storedDate.length);
			GM_setValue(charName + '.fullness',0);
			GM_setValue(charName + '.spleen',0);
			GM_setValue(charName + '.storedDate',curDateString);
			GM_setValue('curDate',curDateString);
		}
		else if (curDateString != GM_getValue('curDate','')) {
			//alert("setting curDate from null, value:"+curDateString);
			GM_setValue('curDate',curDateString);
		}
		clearInterval(t);
	}
}

//Returns 999 if unknown, or drunkenness/fullness/spleen
function HowMuchConsumption (itemid) {
	if (itemdb.items[itemid]) {
		return parseInt(itemdb.items[itemid].limiter);
	}
	else {
		return 999;
	}
}

//Returns average adventure gain from the item
//TODO when itemdb is updated properly
function getAdventures(itemid) {
	if (itemdb.items[itemid]) {
		return parseFloat(itemdb.items[itemid].adv);
	}
	else {
		return 999;
	}
}

//Returns whether an item is a spleen item (some non-spleen items have use links)
function IsSpleen (itemid) {
	if ((itemdb.items[itemid]) && (itemdb.items[itemid].type == "spleen")) {
		return true;
	}
	return false;
}

function ConsumptionType (itemid) {
	if (itemdb.items[itemid]) {
		return itemdb.items[itemid].type;
	}
	else {
		return 'unknown';
	}
}

//wrapper function to account for the fact that sometimes the font tags are outside and sometimes inside
//should be called from a node
function ChangeTextContent(node,text) {
	fontChildren = node.getElementsByTagName('font');
	if(fontChildren.length > 0) {
		fontChildren[0].textContent = text;
	}
	else {
		node.textContent = text;
	}
}

function updatedb(){
	var lastupdated = GM_getValue('lastUpdate',0);
	var version = GM_getValue('version',0);

	var now = (new Date()).getTime()/1000; //make unix timestamp

	if( (now - lastupdated) > 60*60*24 ){	//have we checked today?
		GM_get(ITEMDB_URL+'&version='+version,function(response){
			if(response!=''){
				//we get here if our version is not current
				GM_setValue('itemdb',response);
				if(m=(/"version":"(\d+)"/).exec(response)){
					GM_setValue('version',m[1]);
				}
			}
		});
		x=Math.floor((new Date()).getTime()/1000);
		GM_setValue('lastUpdate',x);
	}
}
function getdb(){
	//Simple function to load itemdb from config and return as an object
	var tmp = GM_getValue('itemdb','');
	if(tmp!=''){
		var itemdb = eval('('+tmp+')');
		return itemdb;
	}else{
		return false;
	}
}

function GM_get(dest, callback)
{	GM_xmlhttpRequest({
	  method: 'GET',
	  url: dest,
	  	//onerror:function(error) { GM_log("GM_get Error: " + error); },
		onload:function(details) {
			if( typeof(callback)=='function' ){
				callback(details.responseText);
}	}	});	}

function rand ( n )
{
  return ( Math.floor ( Math.random ( ) * n + 1 ) );
}

function TotalAdvSort (a,b) {
	if ((!a.adv)||(a.adv==999)) {
		return 1;
	}
	else if ((!b.adv)||(b.adv==999)) {
		return -1;
	}
	else {
		return (b.adv - a.adv);
	}
}

function PerAdvSort (a,b) {
	if (!a.advPer) {
		return 1;
	}
	else if (!b.advPer) {
		return -1;
	}
	else {
		return (b.advPer - a.advPer);
	}
}

function AlphaSort(a,b) {
	if(a.name.toLowerCase() < b.name.toLowerCase()) return -1;
	else if (a.name == b.name) return 0;
	else return 1;
}

//Version 2.5 - Code to re-sort the inventory sections in terms of adventures, if that option is enabled
function SortInv(foodNodeArray,drinkNodeArray) {
	//Remove the inventory nodes from their parents
	for (i=0;i<foodNodeArray.length;i++) {
		foodNodeArray[i].node.parentNode.removeChild(foodNodeArray[i].node);
		foodNodeArray[i].imgNode.parentNode.removeChild(foodNodeArray[i].imgNode);
	}
	for (i=0;i<drinkNodeArray.length;i++) {
		drinkNodeArray[i].node.parentNode.removeChild(drinkNodeArray[i].node);
		drinkNodeArray[i].imgNode.parentNode.removeChild(drinkNodeArray[i].imgNode);
	}

	if (invSortOrder == 'totalAdv') {
		foodNodeArray.sort(TotalAdvSort);
		drinkNodeArray.sort(TotalAdvSort);
	}
	else if (invSortOrder == 'perAdv') {
		foodNodeArray.sort(PerAdvSort);
		drinkNodeArray.sort(PerAdvSort);
	}	
	else if (invSortOrder == 'alpha') {
		foodNodeArray.sort(AlphaSort);
		drinkNodeArray.sort(AlphaSort);
	}	

	//Re-sort the arrays
	var foodParents = document.evaluate('//div[@id="section1"]//tr',document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
	var drinkParents = document.evaluate('//div[@id="section2"]//tr',document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);

	//Re-add the food in the new order
	for (i=1;i<foodParents.snapshotLength;i++) {
		curParent = foodParents.snapshotItem(i);
		if(foodNodeArray[2*(i-1)]) {
			curParent.appendChild(foodNodeArray[2*(i-1)].imgNode);
			curParent.appendChild(foodNodeArray[2*(i-1)].node);
		}
		if(foodNodeArray[(2*(i-1))+1]) {
			curParent.appendChild(foodNodeArray[(2*(i-1))+1].imgNode);
			curParent.appendChild(foodNodeArray[(2*(i-1))+1].node);
		}			
	}

	//Re-add the booze in the new order
	for (i=0;i<drinkParents.snapshotLength;i++) {
		curParent = drinkParents.snapshotItem(i);
		if(drinkNodeArray[2*i]) {
			curParent.appendChild(drinkNodeArray[2*i].imgNode);
			curParent.appendChild(drinkNodeArray[2*i].node);
		}
		if(drinkNodeArray[(2*i)+1]) {
			curParent.appendChild(drinkNodeArray[(2*i)+1].imgNode);
			curParent.appendChild(drinkNodeArray[(2*i)+1].node);
		}			
	}
}