// ==UserScript==
// @name           Facebook Monsters Enhancer
// @namespace      http://userscripts.org/users/49912
// @description    Creates a menu to use those applications in facebook and has auto-play functions
// @source         http://userscripts.org/scripts/show/25396
// @identifier     http://userscripts.org/scripts/source/25396.user.js
// @version        1.0
// @date           2008-05-27
// @creator        ViXaY XaVieR and Piotr P. Karwasz
// @include        http://apps.facebook.com/slayers/*
// @include        http://apps.facebook.com/werewolves/*
// @include        http://apps.facebook.com/vampires/*
// @include        http://apps.facebook.com/zombies/*
// ==/UserScript==
/*
The followings notes are in WIKIPAD style for easier export to userscripts meta data page

+++ DISCLAIMER
	This script has superceded the script at:	http://userscripts.org/scripts/show/20462, as this now has all the equivalent functionality & more. 
	Except for Checkboxes (i.e. manually selecting to feed or to fight users).

+++ PURPOSE
	* Organise players in clans that can feed mutually and attack each other
	* Auto feed monsters from the clan to gain money & attacks
	* Auto attack friends to gain experience points
	* Do this all easily and conveniently, without manual effort (i.e. a bot)

+++ FEATURES:
	* Creates a Menu on the right hand side listing all usual functions. _Benefit:_ Saves you valuable time in going about your daily tasks, especially across the different monster types.
	* Creates a statistics pane on bottom left containing stats for all your monster types! convenient one glance stats!
	* Auto-Feed people from history for all your monsters to get money automatically.
		*   Currently 8 feeds allowed/day * 30 bucks/feed = *240/day/monster* * 4 monster types = *960* bucks/day total!!,
		*   This takes time (5 secs + 3 guesstimated secs reload time) * 3 clicks per feed * 8 feeds/day * 4 monsters = 768 secs = *12.8* mins! 
		*   Thus gives you increased # of attacks each day! The script selects people that already fed you and most probably will feed you back: it means 2 attacks/feed
		*   You can leave the page open in a tab all the time and it will automatically do it everyday without your intervention!
	* Auto-Attack monsters less than twice as strong as you.

++++ HOW AUTO-BUY WORKS:
	1. There is an 'AutoBuy!' button on the sidebar to TOGGLE this functionality.
	2. Once on your monster's profile page, it will see if you have stuff(shield & boomstick) in your arsenal. If either is missing it will go to store and buy it!
	   !WARNING! This may cause to you have fewer bucks all the time if (outgoing rate>incoming rate).  (roughly 3/attack > less than 3 per attack(as you will lose fights!))

++++ HOW AUTO-FEED WORKS:
	1. There is an 'AutoFeed!' button on the sidebar to TOGGLE this functionality.
	2. On your profile or a feeding related page (event-history, feed-result, ...) the script will begin to work, showing what it's doing on the status line(on top of the sidebar).

Current Logic for feeding:
	Read your feeding history to populate your clan. Than attack the 8 people that fed you last.
	Switch to the next monster when it's done

++++ HOW AUTO-ATTACK WORKS:
	1. There is an 'AutoAttack!' button on the sidebar to TOGGLE this functionality.
	2. On your profile or an attacking related page (fighting-main, fighting-confirm, ...) the script will begin to work, showing what it's doing on the status line (on top of the sidebar).

Current Logic for attacking:
	Go from top to bottom, choose an opponent who is less than twice as strong as you.
	Use all 3 attacks per round and than choose another defender monster type randomly
	Once done switch to another monster type.

++++ STATISTICS PANE
	* Gets updated as you move around the different pages
	* It should give reliable information, but it's not guaranteed at the beginning
	* Is there just for quick reference
	* You have to visit your main profile page to get the total amount of money you have in your account

+++ NOTES:
Tested with Firefox 2.0.0.11+ & Greasemonkey 0.7.20070607.0+
http://userscripts.org/scripts/show/20462
To help debug problems please check the Tools->Error Console and post messages from the error & messages tabs to the comments  section.

http://userscripts.org/scripts/show/20462
this is the magic boon i bestow upon you to grow powerful quicker than your peers. After many years of meditation was i able to craft such a delicate spell to aid you in battle, use it wisely!

+++ CREDITS
Facebook Slayers/Werewolves/Vampires/Zombies Enhancer by ViXaY Xavier (http://userscripts.org/users/42874) for the initial code

+++ CHANGELOG
	* v1.0-alpha First alpha release
	* v1.0-beta working
		
+++ KNOWN BUGS
	
+++ TO DO
	- Show who are the people in the clan
*/
/*jsl:option explicit*/
(function () {

// bit masks
var PREF_RESET = 0; 
var PREF_AUTOFEED = 1; //2^0
var PREF_AUTOATTACK = 2; //2^1
var CLANSELECT_MANUAL = 4; //2^2
var ALREADY_GOING = 8; //2^3
var PREF_AUTOBUY = 16; //2^4
var PREF_ALL = 255; //2^8 - 1 
 

// configurable constants
var SCRIPT = {
	name: "Facebook Monsters Enhancer",
	namespace: "http://userscripts.org/users/49912",
	description: 'Creates a menu to use those applications in facebook and has auto-play functions',
	source: "http://userscripts.org" + "/scripts/show/25396",// script homepage/description URL
	identifier: "http://userscripts.org" + "/scripts/source/25396.user.js",	// script URL
	version: "1.0", // version
	date: (new Date(2008, 5 - 1, 27)).valueOf() // update date
};
var CLAN_MIN_DIM = 8; // minimal dimension of your clan
var CLAN_MAX_DIM = 16; //maximal dimension of your clan
var APP_NAME = "Facebook Monsters Enhancer";

// non-configurable constants
var MONSTER_TYPE = ["slayer", "werewolf", "vampire", "zombie", "slayers", "werewolves", "vampires", "zombies"];
var MONSTER_APP_IDS = [ 17801732384, 2721700161, 2458301688, 2341504841 ];
var SLAYER = 0;
var WEREWOLF = 1;
var VAMPIRE = 2;
var ZOMBIE = 3;
var NR_MONSTERS = 4;
var PLURAL = NR_MONSTERS;
var APPS_URL = "http://apps.facebook.com/";
var MONTH_NAMES=new Array('January','February','March','April','May','June','July','August','September','October','November','December','Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
var DAY_NAMES=new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday','Sun','Mon','Tue','Wed','Thu','Fri','Sat');


// global variables
// variables that need saving
var mstatus;
var auto_timer; // default countdown value for timer
var monsters = new Array();

// variables that are session only
var current_type = "";
var current_page = "";
var current_params = "";
var divs;
var next_attack, next_attack_type;
var next_feed, next_feed_type;
// Milliseconds give integer overflow when storing
var now = new Date().getTime();
now = (now - (now % 1000)) / 1000;
// what to do when timer goes out
var gnext_page; // where to go, can be a string (URL) or an element (button to press)
var ginterval; // to store the periodic timer function
var gcountdown; // how long that function waits
var gmessage; // status message to show


// update automatically
function addEventHandler (objElement, strEvent, fnHandler, blnUseCapture) {
	var success = false;
	try {
		objElement.addEventListener(strEvent, fnHandler, (blnUseCapture ? true : false));
		success = true;
	}
	catch (ex) {
		try { success = objElement.attachEvent("on" + strEvent, fnHandler); } catch (ex) {}
	}
	return success;
}

try {
	window.addEventListener("load", function () { try {
		(unsafeWindow || window.wrappedJSObject || window).UserScriptUpdates.requestAutomaticUpdates(SCRIPT);
		} catch	(ex) {} }, false);
} catch	(ex) {}

// objects
/* GenericMonster: object that contains the main data of people to feed
 *  * id: the Facebook ID of the user
 *  * type: the type of the monster (integer)


 *  * name: the name of the monster's owner.
 *  * can_be_fed: timestamp of when this monster will be no more full.
 *  * feed_hist_ids: array with the ids of people fed
 *  * feed_hist_ts: array with the timestamps of when people where fed
 */
function GenericMonster(mtype,id) {
	this.id = id;
	this.type = mtype;
	this.can_be_fed = 0;
	this.next_feed = 0;
	this.last_fedback = 0;
	this.name = '';
	// Don't access directly
	this.feed_hist_ids = eval(GM_getValue(mtype + '/' + id + '/feed_hist_ids','[]'));
	this.feed_hist_ts = eval(GM_getValue(mtype + '/' + id + '/feed_hist_ts','[]'));
}

/* Feed this monster with your current Facebook ID
 * Switches to the page to feed this monster and never returns. */
GenericMonster.prototype.feed = function() {
	location.href = APPS_URL + MONSTER_TYPE[this.type + NR_MONSTERS] + "/feed-main.php?consumer_id=" + this.id;
};

/* Monster: object that contains information about user's monsters
 * * type: the type of the monster
 * * power: the number of monster points
 * * next_feed: time when the script will try to feed again
 * * last_feed: times of the last 8 feeds you performed.
 * * feeds_left: feeds that are left for today. Calculated from last_feed as number of feeds performed more that 22 hours ago.
 * * attacks_left: number of attacks left for today.
 * * next_attack: time when the script will try to attack again
 * * money: number of monster bucks
 * * clan: an array of GenericMonster objects to feed.
 * * clan_size: cardinality of clan.
 * * buy_shield: 
 * * buy_weapon:
*
 * Methods:
 * save()		saves the properties into GreaseMonkey
 * feed(id)		feeds the monster given by 'id'
 * attack(id)		does nothing yet
 * selectClanMember()	returns the best candidate to feed
 * showProfile()	goes to the monsters profile
 * selectLeastClanMember()  returns the least active clan member
 * addOrReplaceClanMember(cm) adds cm to the clan, eventually replacing the worst clanmember if we reached clan's max size
 * historyAnalyse()	updates clan status from history
 */
function Monster(mtype) {
	this.type = mtype;
	this.power = GM_getValue(mtype + "/power",0);
	this.next_feed = parseInt(GM_getValue(mtype + "/next_feed",0),10);
	this.last_feed = new Array();
	this.feeds_left = 0;
	for (var i = 0; i < 8; i++) {
	this.last_feed[i] = parseInt(GM_getValue(mtype + "/last_feed/" + i,0),10);
	if (this.last_feed[i] < (now - 22 * 60 * 60)) { 
		this.feeds_left++;
		}
	}
	this.attacks_left = parseInt(GM_getValue(mtype + "/attacks_left",-1),10);
	this.next_attack = GM_getValue(mtype + "/next_attack",0);
	this.money = GM_getValue(mtype + "/money",0);
	this.buy_shield = GM_getValue(mtype + "/buy_shield",false);
	this.buy_weapon = GM_getValue(mtype + "/buy_weapon",false);
	this.clan_size = parseInt(GM_getValue(mtype + "/clan_size",0),10);
	this.clan = new Array();
	
	for (var i=0; i < this.clan_size; i++) {
		var clanmember = new GenericMonster(mtype);
		clanmember.id = parseInt(GM_getValue(mtype + "/clan/" + i + "/id",0),10);
		clanmember.last_fedback = GM_getValue(mtype + "/clan/" + i + "/last_fedback",0);
		clanmember.next_feed = GM_getValue(mtype + "/clan/" + i + "/next_feed",0);
		clanmember.name = GM_getValue(mtype + "/clan/" + i + "/name",'');
		this.clan.push(clanmember);
	}
	
	// methods
	this.save = monsterSave;
	this.feed = monsterFeed;
	this.selectClanMember = monsterSelectClanMember;
	this.showProfile = monsterShowProfile;
	this.selectLeastClanMember = monsterSelectLeastClanMember;
	this.getClanMemberById = monsterGetClanMemberById;
}

/* Method Monster::shop(force)
 *	Goes shopping! 
 *	Buy Shield if not available & Credits > 150
 *	Buy Weapon if not available  & Credits > 3000
 *	Weapons double points gained from combat
  */
Monster.prototype.shop = function(force) {
	var nodes;
	this.buy_shield = false; //default to false
	this.buy_weapon = false;
	if (this.money > 150) { //shield
		// check availability on side-nav.php page
		nodes = getFirstXPathResult("//img[@src='http://monsters.joyeurs.com/includes/monsters/img/armour/shield.png']");
		//nodes = getFirstXpathResult("//img[@src='*/img/armour/shield.png']");
		if (nodes == undefined) { //item doesn't exist
			this.buy_shield = true;
		}
	}
	if (this.money > 3000) { //weapon - boomstick
	// check availability on side-nav.php page
		nodes = getFirstXPathResult("//img[@src='http://monsters.joyeurs.com/includes/monsters/img/weapons/boomstick.png']");
		//nodes = getFirstXpathResult("//img[@class='item_pic']");
		if (nodes == undefined) { //item doesn't exist
			this.buy_weapon = true;
		}
	}
};
  
/* Method Monster::addOrReplaceClanMember (cm, force)
 *	Adds the clanmember or replaces the worst clanmember with the new.
 *	If force is true you can replace bad clan members with even worse.
 *
 * 	Returns the index of the elements inserted/replaced or -1 otherwise.
 */
Monster.prototype.addOrReplaceClanMember = function(clanm,force) {
	if (this.clan_size >= CLAN_MAX_DIM) {
		var replace_index;
		// Does not add dupes
		if (!(replace_index = this.getClanMemberById(clanm.id))) {
			replace_index = this.selectLeastClanMember();
		}
		if (replace_index == undefined) alert("Something strange happened");
		if ((this.clan[replace_index].last_fedback < clanm.last_fedback) || force) {
			this.clan[replace_index] = clanm;
			return replace_index;
		} else {
			// if this clanmember is even worse, don't bother replacing
			return -1;
		}
	} else {
		this.clan.push(clanm);
		this.clan_size++;
		return (this.clan_size - 1);
	}
};

/* Method Monster::historyAnalyse()
 *	Updates the Clan using history. It also removes the old entries from the history and show the complete list of the clan with feed dates.
 */
Monster.prototype.historyAnalyse = function() {
	var id;
	var clan_ids = new Array();
	var num = new RegExp('\\d+');

	// populating the array with clan member ids
	for (var i = 0; i < this.clan_size; i++ ) {
		clan_ids.push(this.clan[i].id);
	}

	// Retrieve the feeding list
	divs = getElementsByClassName("list_action_call","span");
	var pnode = divs[0].parentNode.parentNode;
	for (var i = 0; i < divs.length; i++) {
	// id contains the id of the monster
	// feed_date the timestamp of when he fed us
	id = parseInt(divs[i].getElementsByTagName('a')[0].href.match(num),10);
	var str = divs[i].parentNode.getElementsByTagName('span')[1].innerHTML + ", " + new Date().getFullYear() + " 00:00:00";
	var feed_date = new Date(str).getTime();
	feed_date = (feed_date - (feed_date % 1000)) / 1000;

	var ind = clan_ids.indexOf(id);
	if (ind != -1) {
	// Monster already in the clan
		// Update last_fedback of the monster
		// and add attacks_left
		if (this.clan[ind].last_fedback < feed_date) {
		this.clan[ind].last_fedback = feed_date;
		this.attacks_left++;
		} else {
		// Remove the entry
		pnode.removeChild(divs[i].parentNode);
		}
	} else {
	// New monster
		var name = '';
		var elmts = divs[i].parentNode.getElementsByTagName('a');
		if (elmts.length == 3) {
		name = elmts[1].innerHTML;
		} else {
		name = 'Anonymous';
		}
		var clanm = new GenericMonster(this.type, id);
		//clanm.id = id;
		clanm.name = name;
		clanm.last_fedback = feed_date;
		ind = this.addOrReplaceClanMember(clanm);
		if (ind != -1) {
		// We added someone
		clan_ids[ind] = id;
		this.attacks_left++;
		} else {
		pnode.removeChild(divs[i].parentNode);
		}
	}
	}

	// Display the contents of the clan
	// The title
	var list_el = document.createElement('div');
	list_el.className = 'list_item';
	list_el.innerHTML = '<span class="list_event"><h1>My Clan</h1></span>';
	pnode.appendChild(list_el);

	var entry = new Array();
	entry = ['<span class="list_action_call"><a href="feed-main.php?consumer_id=',
		'', // Index 1: the ID of the ClanMember
		'">Feed ',
		'', // Index 3: the name of the ClanMember
		'!</a></span>',
		'<span class="list_rank">',
		'', // Index 6: the position in the Clan
		'.</span>',
		'<span class="list_event">',
		'', // Index 9: the message to show
		'</span>'
		];

	for (var i = 0; i < this.clan_size; i++) {
	list_el = document.createElement('div');
	list_el.className = 'list_item';
	
	entry[1] = this.clan[i].id;
	entry[3] = this.clan[i].name;
	entry[6] = i + 1;
	if (this.clan[i].last_fedback) {
		entry[9] = '<b>' + this.clan[i].name + '</b> fed us last time on ' + formatDate(1000 * this.clan[i].last_fedback,'MMM d, y');
	} else {
		entry[9] = '<b>' + this.clan[i].name + '</b> never fed us back.';
	}
	if (this.clan[i].next_feed > now) {
		entry[9] = entry[9] + ' (already fed today)';
	}
	list_el.innerHTML = entry.join('');
	pnode.appendChild(list_el);
	}
};

Monster.prototype.addFriends = function() {
	var nr; // Nr of friends to add
	var re = /consumer_id=(\d+)/;
	var id;
	var url;
	divs = getElementsByClassName("small_avatar_user_name","div");

	var nr_added = 0;
	for (var i = 0; i < divs.length; i++) {
		if (nr_added >= this.feeds_left) break;
		url = divs[i].getElementsByTagName('a')[0].href;
		id = parseInt(url.match(re)[1],10);
		GM_log("id:" + id + " url:" + url); //DEBUG
		if (!this.getClanMemberById(id)) { 
			var clanm = new GenericMonster(this.type, id);
			//clanm.type = this.type;
			//clanm.id = id;
			// Force adding the clan member, even if the friend never fed you back.
			if (this.addOrReplaceClanMember(clanm,true)) nr_added++;
		}
	}
};

Monster.prototype.showFriends = function() {
	var style = new Array();
	style.push('.list_container { margin: 10px; }');
	style.push('.list_item { border-top: 1px dotted #bb0000; margin: 5px; padding: 5px; list-style-type: none; background-color: #eeeeee; }');
	style.push('.list_item_special { border-top: 1px dotted #bb0000; margin: 5px; padding: 5px; list-style-type: none; background-color: #eebbbb; }');
	style.push('.list_rank { border-right: 1px dotted #bb0000; font-weight: bold; margin-right: 5px; padding-right: 5px; }');
	style.push('.list_action_call { float: right; text-align: right; }');
	var style_el = document.createElement('style');
	style_el.type = "text/css";
	style_el.innerHTML = style.join(''); 
	try {
		document.getElementsByTagName('head')[0].appendChild(style_el);
	} catch (ex) {}
	// Show the list of the actual clan Members
	// pnode is the node that contains everything
	var pnode = document.getElementById('app_content_' + MONSTER_APP_IDS[this.type]).firstChild;
	var iframe = pnode.getElementsByTagName('iframe')[1];
	var div = document.createElement('div');
	div.className = 'list_container';
	pnode.insertBefore(div,iframe);
	pnode = div;
	
	// TODO: this was just copied from historyAnalyse function
	// Display the contents of the clan
	// The title
	var list_el = document.createElement('div');
	list_el.className = 'list_item';
	list_el.innerHTML = '<span class="list_event"><h1>My Clan</h1></span>';
	pnode.appendChild(list_el);

	var entry = new Array();
	entry = ['<span class="list_action_call"><a href="feed-main.php?consumer_id=',
		'', // Index 1: the ID of the ClanMember
		'">Feed ',
		'', // Index 3: the name of the ClanMember
		'!</a></span>',
		'<span class="list_rank">',
		'', // Index 6: the position in the Clan
		'.</span>',
		'<span class="list_event">',
		'', // Index 9: the message to show
		'</span>'
		];

	for (var i = 0; i < this.clan_size; i++) {
	list_el = document.createElement('div');
	list_el.className = 'list_item';
	
	entry[1] = this.clan[i].id;
	entry[3] = this.clan[i].name;
	entry[6] = i + 1;
	if (this.clan[i].last_fedback) {
		entry[9] = '<b>' + this.clan[i].name + '</b> fed us last time on ' + formatDate(1000 * this.clan[i].last_fedback,'MMM d, y');
	} else {
		entry[9] = '<b>' + this.clan[i].name + '</b> never fed us back.';
	}
	if (this.clan[i].next_feed > now) {
		entry[9] = entry[9] + ' (already fed today)';
	}
	list_el.innerHTML = entry.join('');
	pnode.appendChild(list_el);
	}
};

function monsterSave() {
	GM_setValue(this.type + "/power", this.power);
	GM_setValue(this.type + "/next_feed", this.next_feed);
	for (var i = 0; i < 8; i++) {
	GM_setValue(this.type + "/last_feed/" + i, this.last_feed[i]);
	}
	GM_setValue(this.type + "/attacks_left", this.attacks_left);
	GM_setValue(this.type + "/next_attack", this.next_attack);
	GM_setValue(this.type + "/money", this.money);
	GM_setValue(this.type + "/buy_shield",this.buy_shield);
	GM_setValue(this.type + "/buy_weapon",this.buy_weapon);
	if (this.clan.length > 0) {
		GM_setValue(this.type + "/clan_size", this.clan.length);
		GM_log("size:" + this.clan_size + "length:" + this.clan.length);//DEBUG
		for (var i=0; i < this.clan.length; i++) {
			GM_log(this.type + " id " + this.clan[i].id + " fb " + this.clan[i].last_fedback + " nf " + this.clan[i].next_feed); //DEBUG
			GM_setValue(this.type + "/clan/" + i + "/id", this.clan[i].id);
			GM_setValue(this.type + "/clan/" + i + "/last_fedback", this.clan[i].last_fedback);
			GM_setValue(this.type + "/clan/" + i + "/next_feed", this.clan[i].next_feed);
			GM_setValue(this.type + "/clan/" + i + "/name", this.clan[i].name);
		}
	}
	else GM_setValue(this.type + "/clan_size", 0);
}

// Returns the clanmember that can be already fed and fed you back most recently
// or undefined if an error occured
function monsterSelectClanMember() {
	var last_fedback = -1;
	var clanmember = undefined;
	for (var i = 0; i < this.clan_size; i++) {
		if (this.clan[i].id && (this.clan[i].next_feed < now) && (this.clan[i].last_fedback > last_fedback)) {
			last_fedback = this.clan[i].last_fedback;
			clanmember = this.clan[i];
		}
	}
	return clanmember;
}

// Feeds the monster 'id'
function monsterFeed(id) {
	gmessage = "Feeding a " + MONSTER_TYPE[this.type] + "...";
	gcountdown = auto_timer; // set timer to default
	gnext_page = APPS_URL + MONSTER_TYPE[this.type + NR_MONSTERS] + "/feed-main.php?consumer_id=" + id;
}

Monster.prototype.attack = function(clanm) {
	var ATTACK_TYPE = [ 49, 29, 27, 19 ];
	gmessage = "Fighting a " + MONSTER_TYPE[clanm.type] + "...";
	gcountdown = auto_timer; // set timer to default
	gnext_page = APPS_URL + MONSTER_TYPE[this.type + NR_MONSTERS] +
		"/fighting-confirm.php?defender_fbuserid=" + clanm.id +
		"&defender_monster_type_id=" + ATTACK_TYPE[clanm.type];
	// Slayer = 49
	// Werewolf = 29
	// Vampire = 27
	// Zombie = 19
};

function monsterShowProfile() {
	gmessage = "Going to " + MONSTER_TYPE[this.type + NR_MONSTERS] + " profile...";
	gcountdown = auto_timer;
	gnext_page = APPS_URL + MONSTER_TYPE[this.type + NR_MONSTERS] + "/side-nav.php?ref=top_nav";
}

function monsterSelectLeastClanMember() {
	// give the index in the Array of the least active clanMember;
	var lf = now;
	var index = undefined;
	for (var i = 0; i < this.clan_size; i++ ) {
		if (this.clan[i].last_fedback < lf) {
			lf = this.clan[i].last_fedback;
			index = i;
		}
	}
	return index;
}

function parseURL (url) {
	var tmp = url.split('/')[3];
	current_type = MONSTER_TYPE.indexOf(tmp) - NR_MONSTERS;
	tmp = url.split('/')[4];
	current_page = tmp.split('?')[0];
	current_params = tmp.split('?')[1];
	if (current_params == undefined) {
		current_params = '';
	}
}


// ================ Setup Menu ==================
function insertMenu() {

	//document.title = document.title + " enhanced by VX & chopinhauer";

	var menuCode = []; // temporary array to store strings for concatenation
	var append_elm, elm; // DOM elements we will append menus to

	// Fighting tab	
	append_elm = document.getElementById('toggle_tabs_unused').childNodes[2]; // the 3rd tab is for fighting
	append_elm.getElementsByTagName('a')[0].innerHTML = 'Fight against...';
	elm = document.createElement('ul');
	elm.className = 'tabs';
	append_elm.appendChild(elm);
	append_elm = elm;
	for (var i=0; i < NR_MONSTERS; i++) {
		elm = document.createElement('li');
		elm.innerHTML = createLink("fighting-main","monster_type=" + MONSTER_TYPE[i], MONSTER_TYPE[PLURAL + i]);
		append_elm.appendChild(elm);
	}
	// Fighting history
	elm = document.createElement('li');
	elm.innerHTML = createLink("fighting-history","","Fight history...");
	append_elm.appendChild(elm);
	append_elm = elm;
	elm = document.createElement('ul');
	elm.className = 'tabs';
	append_elm.appendChild(elm);
	append_elm = elm;
	for (var i=0; i < NR_MONSTERS; i++) {
		elm = document.createElement('li');
		elm.innerHTML = createLink("fighting-history","", MONSTER_TYPE[PLURAL + i], APPS_URL + MONSTER_TYPE[PLURAL + i] + '/');
		append_elm.appendChild(elm);
	}
	
	// Feeding tab
	append_elm = document.getElementById('toggle_tabs_unused').childNodes[5]; // the 6th tab is for feeding
	append_elm.getElementsByTagName('a')[0].innerHTML = 'Feed/Ruse...';
	elm = document.createElement('ul');
	elm.className = 'tabs';
	append_elm.appendChild(elm);
	append_elm = elm;
	for (var i=0; i < NR_MONSTERS; i++) {
		elm = document.createElement('li');
		elm.innerHTML = createLink("feed-home","", MONSTER_TYPE[PLURAL + i], APPS_URL + MONSTER_TYPE[PLURAL + i] + '/');
		append_elm.appendChild(elm);
	}
	// Feeding history
	elm = document.createElement('li');
	elm.innerHTML = createLink("event-history","","Feed history...");
	append_elm.appendChild(elm);
	append_elm = elm;
	elm = document.createElement('ul');
	elm.className = 'tabs';
	append_elm.appendChild(elm);
	append_elm = elm;
	for (var i=0; i < NR_MONSTERS; i++) {
		elm = document.createElement('li');
		elm.innerHTML = createLink("event-history","", MONSTER_TYPE[PLURAL + i], APPS_URL + MONSTER_TYPE[PLURAL + i] + '/');
		append_elm.appendChild(elm);
	}

	// Rank tab
	append_elm = document.getElementById('toggle_tabs_unused').childNodes[1]; // the 2nd tab is for rank
	elm = document.createElement('ul');
	elm.className = 'tabs';
	append_elm.appendChild(elm);
	append_elm = elm;
	// Friends Rank
	var rank_tab = append_elm; // contains the 'ul' Rank Tab element
	elm = document.createElement('li');
	elm.innerHTML = createLink('friend-rank', 'ref=top_menu', 'Friends Rank');
	append_elm.appendChild(elm);
	append_elm = elm;
	elm = document.createElement('ul');
	elm.className = 'tabs';
	append_elm.appendChild(elm);
	append_elm = elm;
	for (var i=0; i < NR_MONSTERS; i++) {
		elm = document.createElement('li');
		elm.innerHTML = createLink("friend-rank","ref=top_menu", MONSTER_TYPE[PLURAL + i], APPS_URL + MONSTER_TYPE[PLURAL + i] + '/');
		append_elm.appendChild(elm);
	}
	// Global Rank
	append_elm = rank_tab;
	elm = document.createElement('li');
	elm.innerHTML = createLink('global-rank', 'ref=top_menu', 'Global Rank');
	append_elm.appendChild(elm);
	append_elm = elm;
	elm = document.createElement('ul');
	elm.className = 'tabs';
	append_elm.appendChild(elm);
	append_elm = elm;
	for (var i=0; i < NR_MONSTERS; i++) {
		elm = document.createElement('li');
		elm.innerHTML = createLink("global-rank","ref=top_menu", MONSTER_TYPE[PLURAL + i], APPS_URL + MONSTER_TYPE[PLURAL + i] + '/');
		append_elm.appendChild(elm);
	}

	// Store tab
	append_elm = document.getElementById('toggle_tabs_unused').childNodes[6]; // the 7th tab is for store
	elm = document.createElement('ul');
	elm.className = 'tabs';
	append_elm.appendChild(elm);
	append_elm = elm;
	for (var i=0; i < NR_MONSTERS; i++) {
		elm = document.createElement('li');
		elm.innerHTML = createLink("store-main","", MONSTER_TYPE[PLURAL + i], APPS_URL + MONSTER_TYPE[PLURAL + i] + '/');
		append_elm.appendChild(elm);
	}
	
	menuCode.push('ul.tabs { height: 0; }');
	menuCode.push('ul.toggle_tabs ul { display: block; background-color: transparent; position: relative; overflow: visible; visibility: hidden; list-style: none; padding-top: 0; z-index: 99; }');
	menuCode.push('ul.toggle_tabs ul li a { border-bottom: 1px solid rgb(137, 137, 137); border-left: 1px solid rgb(137, 137, 137); border-right: 1px solid rgb(137, 137, 137); border-top: 0px none transparent; }');
	menuCode.push('ul.toggle_tabs ul li { padding: 0; border-top: 0px none transparent; }');
	menuCode.push('ul.toggle_tabs li { overflow: visible; }');
	menuCode.push('ul.toggle_tabs > li { float: left; overflow: visible; padding-bottom: 0; }');
	menuCode.push('ul.toggle_tabs li, ul.toggle_tabs a { display: block; }');
	menuCode.push('ul.toggle_tabs ul ul { position: relative; top: -2em; left: 95%; visibility: hidden;}');
	menuCode.push('ul.toggle_tabs li:hover ul ul { visibility: hidden; }');
	menuCode.push('ul.toggle_tabs li:hover ul, ul.toggle_tabs ul li:hover ul { visibility: visible; }');
	

	var style = document.createElement('style');
	style.type = "text/css";
	style.innerHTML = menuCode.join(''); 
	menuCode.length = 0; //Reset/Empty the array
	
	// Insert the menu code and style into the document
	try {
		document.getElementsByTagName('head')[0].appendChild(style);
	} catch (ex) {}

// ================ Setup Statistics ==================
	menuCode.push('<div class="monsterHeading">-STATISTICS-</div>');
	menuCode.push('<table>');
	//insert the last time we cleared the flags as tooltips for the headers
	var dateformat = "MMM d, y HH:mm";
	menuCode.push('<tr><th><b>M</b></th><th><b>Points</b></th><th><b>Bucks</b></th><th><b title="'+
			formatDate(monsters[current_type].next_attack,dateformat) + '">Fights</b></th><th><b title="'+
			formatDate(monsters[current_type].next_feed,dateformat) + '">Feeds</b></th></tr>');
	for (var i=0; i < NR_MONSTERS; i++) {
		menuCode.push('<tr>');
		//1st cell - monster type - also a link to the current page but for each monster
		menuCode.push('<td> <a href="' + location.href.replace(MONSTER_TYPE[PLURAL + current_type],MONSTER_TYPE[PLURAL + monsters[i].type])  + '">' + MONSTER_TYPE[monsters[i].type].charAt(0).toUpperCase() +' </a></td>');
		//2nd cell - monster power
		menuCode.push('<td id="' + MONSTER_TYPE[i] + '/power">' + monsters[i].power + "</td>");
		//3th cell - monster money
		menuCode.push('<td id="' + MONSTER_TYPE[i] + '/money">' + monsters[i].money + "</td>");
		//4th cell - monster attacks_left
		menuCode.push('<td id="' + MONSTER_TYPE[i] + '/attacks_left">' + (monsters[i].attacks_left == -1 ? '?' : monsters[i].attacks_left )+ "</td>");
		//5th cell - monster feeds_left
		menuCode.push('<td id="' + MONSTER_TYPE[i] + '/feeds_left">' + monsters[i].feeds_left + "</td>");
		menuCode.push('</tr>');
	}

	menuCode.push('<tr> <td colspan="0" title="'+formatDate(1000*next_attack,dateformat) + '"> ');
	if (next_attack > now) {
		menuCode.push('Next fight in ' + secondsToString(next_attack - now) + ' (' +
			MONSTER_TYPE[next_attack_type].charAt(0).toUpperCase() + ') </td></tr>');
	} else {
		menuCode.push('Next fight: NOW!');
	}
	menuCode.push('<tr> <td colspan="0" title="'+formatDate(1000*next_feed,dateformat) +'"> ');
	if (next_feed > now) {
		menuCode.push('Next feed in ' + secondsToString(next_feed - now) + ' (' +
			MONSTER_TYPE[next_feed_type].charAt(0).toUpperCase() + ') </td></tr>');
	} else {
		menuCode.push('Next feed: NOW!');
	}
	menuCode.push('<tr><td colspan="0">Status:<br /><span id="monsteraction"></span><br /><span id="monstertimer"></span></td></tr>');
	menuCode.push('<tr><td colspan="0">Auto Toggle:<br />');
	menuCode.push('<button id="AutoFeed" type="button">Feed ' + (mstatus & PREF_AUTOFEED ? "on! " : "off!" ) + '</button>');
	menuCode.push('<button id="AutoAttack" type="button">Attack ' + (mstatus & PREF_AUTOATTACK ? "on! ":"off!") + '</button>');
	menuCode.push('<button id="AutoBuy" type="button">Buy ' + (mstatus & PREF_AUTOBUY ? "on! " : "off!" ) + '</button></td></tr>');
	menuCode.push('</table>');
	//menuCode.push('<a id="refreshStats">Gather Data</a>');
	
	var menu = document.createElement('div');
	menu.id = 'FBStats';
	menu.innerHTML = menuCode.join(''); // concatenate the string efficiently (faster than +)
	// see http://aymanh.com/9-javascript-tips-you-may-not-know for more information
	menuCode.length = 0; //Reset/Empty the array
	
	menuCode.push("#FBStats { position:fixed; bottom:27px; left:2px; border:2px solid #6D84B4; background:#EEEEEE; color:#3B5998; padding:2px; font-weight:bold; }");
	menuCode.push("#FBStats div.monsterSection { text-align:center; padding-top:2px; }");
	menuCode.push("#FBStats div.monsterHeading { text-align:center; background: #6D84B4; color: #FFFFFF; }");
	menuCode.push("#FBStats a { color: #BB0011; text-decoration:none; }");
	menuCode.push("#FBStats a:hover { color:#B22222; text-decoration:underline; }");
	menuCode.push("#FBStats table { border-spacing: 0px; }");
	menuCode.push("#FBStats table th { border-width: 1px 1px 1px 1px; padding: 2px 2px 2px 2px; border-style: solid solid solid solid; }");
	menuCode.push("#FBStats table td { border-width: 1px 1px 1px 1px; padding: 2px 2px 2px 2px; border-style: solid solid solid solid; }");
			
	style = document.createElement('style');
	style.type = "text/css";
	style.innerHTML = menuCode.join(''); 
	menuCode.length = 0; //Reset/Empty the array
	
	// Insert the menu code and style into the document
	try {
		document.getElementsByTagName('head')[0].appendChild(style);
	} catch(e) {}
	document.body.insertBefore(menu, document.body.lastChild);
	
	// add event listeners
	var t_elm = document.getElementById('AutoAttack');
	t_elm.addEventListener('click',toggleAutobit(PREF_AUTOATTACK,'Attack'),true);
	t_elm = document.getElementById('AutoFeed');
	t_elm.addEventListener('click',toggleAutobit(PREF_AUTOFEED,'Feed'),true);
	t_elm = document.getElementById('AutoBuy');
	t_elm.addEventListener('click',toggleAutobit(PREF_AUTOBUY,'Buy'),true);
}

// ================ Utility functions ==================
function createLink(sPage, sParams, sTitle, sURL) {
	sTitle = sTitle || sPage; // default title = Page name unless it's provided
	sURL = sURL || APPS_URL + MONSTER_TYPE[PLURAL + current_type] + '/'; // default to current app url
	var sExt = '.php';
	if (sPage == undefined || sPage == "") sExt = '';

	if (sParams == undefined || sParams == "") {
		return '<a href="' + sURL + sPage.toLowerCase() + sExt + '">' + sTitle + '</a>';
	} else {
			sParams = '?' + sParams;
			return '<a href="' + sURL + sPage.toLowerCase() + sExt + sParams + '">' + sTitle + '</a>';
	}
}

// Returns null if expr didn't match anything
function getFirstXPathResult(expr, node)
{
	if (!node) node = document;
	var res = document.evaluate(expr, node, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
	return res.singleNodeValue;
}

//http://www.robertnyman.com/2005/11/07/the-ultimate-getelementsbyclassname/
function getElementsByClassName(className, tag, elm){
	var testClass = new RegExp("(^|\\\\s)" + className + "(\\\\s|$)");
	tag = tag || "*";
	elm = elm || document;
	var elements = (tag == "*" && elm.all)? elm.all : elm.getElementsByTagName(tag);
	var returnElements = [];
	var current;
	var length = elements.length;
	for(var i=0; i<length; i++){
		current = elements[i];
		if(testClass.test(current.className)){
			returnElements.push(current);
		}
	}
	return returnElements;
}

//http://www.webmasterworld.com/javascript/3214735.htm
// use for the new scheme they have in facebook divs naming
function getElementsByIDPattern(inPattern,inRoot){
	var elem_array = new Array;
	if(typeof inRoot.firstChild!= 'undefined'){
		var elem = inRoot.firstChild;
		while (elem!= null){
			if(typeof elem.firstChild!= 'undefined'){
				elem_array = elem_array.concat(getElementsByIDPattern(inPattern,elem));
			}
			if(typeof elem.id!= 'undefined'){
				var reg = new RegExp('^'+inPattern+'$');
				if(elem.id.match(reg)){
					elem_array.push(elem);
				}
			}
			elem = elem.nextSibling;
		}
	}
	return elem_array;
}

//ASSERTION FUNCTIONS
//http://aymanh.com/9-javascript-tips-you-may-not-know
function assertException(message) { this.message = message; }

assertException.prototype.toString = function () {
	return 'AssertException: ' + this.message;
};

function assert(exp, message) {
	if (!exp) {
		throw new assertException(message);
	}
}

function LZ(x) {return(x<0||x>9?"":"0")+x;}

//http://www.mattkruse.com/javascript/date/source.html
// ------------------------------------------------------------------
// formatDate (date_object, format)
// Returns a date in the output format specified.
// The format string uses the same abbreviations as in getDateFromFormat()
// ------------------------------------------------------------------
function formatDate(date,format) {
	format=format+"";
	date = new Date(date);
	var result="";
	var i_format=0;
	var c="";
	var token="";
	var y=date.getYear()+"";
	var M=date.getMonth()+1;
	var d=date.getDate();
	var E=date.getDay();
	var H=date.getHours();
	var m=date.getMinutes();
	var s=date.getSeconds();
	var yyyy,yy,MMM,MM,dd,hh,h,mm,ss,ampm,HH,KK,K,kk,k;
	// Convert real date parts into formatted versions
	var value=new Object();
	if (y.length < 4) {y=""+(y-0+1900);}
	value["y"]=""+y;
	value["yyyy"]=y;
	value["yy"]=y.substring(2,4);
	value["M"]=M;
	value["MM"]=LZ(M);
	value["MMM"]=MONTH_NAMES[M-1];
	value["NNN"]=MONTH_NAMES[M+11];
	value["d"]=d;
	value["dd"]=LZ(d);
	value["E"]=DAY_NAMES[E+7];
	value["EE"]=DAY_NAMES[E];
	value["H"]=H;
	value["HH"]=LZ(H);
	if (H==0){value["h"]=12;}
	else if (H>12){value["h"]=H-12;}
	else {value["h"]=H;}
	value["hh"]=LZ(value["h"]);
	if (H>11){value["K"]=H-12;} else {value["K"]=H;}
	value["k"]=H+1;
	value["KK"]=LZ(value["K"]);
	value["kk"]=LZ(value["k"]);
	if (H > 11) { value["a"]="PM"; }
	else { value["a"]="AM"; }
	value["m"]=m;
	value["mm"]=LZ(m);
	value["s"]=s;
	value["ss"]=LZ(s);
	while (i_format < format.length) {
		c=format.charAt(i_format);
		token="";
		while ((format.charAt(i_format)==c) && (i_format < format.length)) {
			token += format.charAt(i_format++);
			}
		if (value[token] != null) { result=result + value[token]; }
		else { result=result + token; }
		}
	return result;
}

// Takes a Time in string (3 horus 23 minutes..etc) and converts it to seconds
function getSecondsFromString(sMsg) {
	var re = /\s(\d+)\s+hour.*\s(\d+)\s+minute.*/;
	var m_result = re.exec(sMsg); //Execute regex on input string
	if (m_result == undefined) return 0;
	if (m_result.length <= 1) return 0;
	var hours = m_result[1];
	var minutes = m_result[2];
	var seconds = /\s(\d+)\ssecond.*/;
	seconds = seconds.exec(sMsg);//Execute regex on input string
	if (seconds == undefined || seconds.length<2) {
		seconds = 0;
	} else {
	seconds = seconds[1];
	}
	return (parseInt(hours,10)*3600 + parseInt(minutes,10)*60 + parseInt(seconds,10));
}

// returns first submit button found on page
function findSubmitButton(val) {
	// find the right button - for interval to auto click
	var elms = document.getElementsByTagName('input');
	for (var i=0; i<elms.length; i++) {
		if (elms[i].type == 'submit') {
			if ((val == undefined) || (val == "")) {
				return elms[i];
			} else if( elms[i].value == val) {
				return elms[i];
			}
		}
	}
	return;
}

// returns a comma seperated number without commas
function getNumber(sText,suffixPattern) {
	var matchResults;
	suffixPattern = suffixPattern || "\\s.*\\spoints.*";
	var tmpRegex = new RegExp("(\\d+(,|\\d)*)" + suffixPattern,"i");
	try {
		matchResults = sText.match(tmpRegex);
		//GM_log("getNumber results:" + matchResults);
		matchResults[1] = matchResults[1].replace(/,/g,""); // clean up commas to get correct conversion to integer
		return parseInt(matchResults[1],10);
	} catch(x) {
		return;
	}
}

// Change the default auto-timer value used for countdowns
function changeTimer() {
	var entry = prompt('Enter a delay in seconds between 1 and 30:',auto_timer);
	if (entry != null) {
		//statements to execute with the value
		if ((entry >= 1) && (entry <= 30)) {
			GM_setValue("timer",entry);
			auto_timer = entry;
		}
	}
}

function secondsToString(sec) {
	var str = '';
	var tmp;
	str = (sec % 60) + " s"; //+ //((sec % 60) == 1 ? "" : "s");
	sec = (sec - (sec % 60)) / 60;
	if (sec) {
		str = (sec % 60) + " m " + str;//((sec % 60) == 1 ? "" : "s") + " " + str;
		sec = (sec - (sec % 60)) / 60;
		if (sec) {
			str = (sec % 60) + " h " + str;//((sec % 60) == 1 ? "" : "s") + " " + str;
		}
	}
	return str;
} 

// to take action if auto is enabled and start the timer interval 
function startActionTimer() {
	
	document.getElementById('monsteraction').innerHTML = gmessage;
	// create annonymous function to call every second to decrement counter
	// and click when counter = 0
	// Interval function that runs every second and checks the countdown timers
	// and takes appropriate action depending on which page we are on
	ginterval = setInterval(function (){
		gcountdown--;
		if (gcountdown<=0) {
		document.getElementById('monstertimer').innerHTML = 'NOW!';
		// like use the attack again link instead of visiting the main page
			if (typeof gnext_page == "object") {
				if (!(mstatus & ALREADY_GOING)) {
					//alert("Pressing button");
					gnext_page.click();
					mstatus |= ALREADY_GOING;
				}
			} else {
				if (!(mstatus & ALREADY_GOING)) {
					//alert("Going to..." + gnext_page);
					location.href = gnext_page;
					mstatus |= ALREADY_GOING;
				}
			}
		} else {
			document.getElementById('monstertimer').innerHTML = 'in ' + secondsToString(gcountdown);
		}
	}, 1000);
}

function checkErrorMessage(auto_mode) {
	//Error Checking for:
	// - A TIMEOUT Error (NOT WORKING YET)
	// - CANNOT FEED MORE ERROR (WORKS)
	// - THIS MONSTER IS FULL FOR TODAY (TODO)
	
	// check if achieved quota already
	var errorHeader;
	var errorPage = getElementsByClassName("no_icon","h2");

	// based on mode, get the appropriate value into errorHeader, might be easier to understand in if statement form
	try {
		if (auto_mode == "feed") {
			errorHeader = document.getElementById('error');
		} else if (auto_mode == "attack") {
			errorHeader = getElementsByClassName("status", "h1")[0];
		}
	} catch(x){}
	// check for div id=error_message
	// hit try again if error_message page.
	//	if (errorPage != undefined && errorPage.length >= 1) {
	//		if (errorPage[0].innerHTML.toLowerCase().indexOf('error while loading page from') != -1)
	//		{
	//			GM_log("error page encountered");
	//			GM_log("Setting reload in 3 seconds");
	//			//getElementById("try_again_button").click();
	//			//window.location.reload();
	//			try {
	//				setTimeout(function() {document.getElementById("try_again_button").click();},6000);
	//			} catch (x) {
	//			}
	//			return 0;
	//		}
	//	}
	if (errorHeader == undefined) { //if no error
		return 0;
	}
	// if we reach here that means that the error header message exists!
	errorHeader = errorHeader.innerHTML.toLowerCase();
	if (errorHeader.indexOf('hours')!=-1 || errorHeader.indexOf('tomorrow') != -1) {
		var wait;
		if (errorHeader.indexOf('hours')!=-1) {
			// Add 1 minute due to rounding error in the string
			wait = getSecondsFromString(errorHeader) + 60;
		} else {
			wait = 24 * 60 * 60; // Monster full...
		}
		if ((errorHeader.indexOf('this') != -1) || (errorHeader.indexOf('tomorrow') != -1)) {
			// Timeout for this monster
			var re = /consumer_id=(\d+)/;
			var id = current_params.match(re)[1];
			var clanm;
			if (clanm = monsters[current_type].getClanMemberById(id)) {
				clanm.next_feed = now + wait;
			} else {
				clanm = new GenericMonster(current_type, id);
				//clanm.id = id;
				clanm.next_feed = now + wait;
				monsters[current_type].addOrReplaceClanMember(clanm);
			}

			if (clanm = monsters[current_type].selectClanMember()) {
				monsters[current_type].feed(clanm.id);
				gmessage = "Already fed this one,<br /> feeding another...";
				return 1;
			} else {
				monsters[current_type].showProfile();
				gmessage = "Already fed this one,<br /> going to profile...";
				return 1;
			}
			// Monster was too much fed today
		} else {
			//alert("Increasing next_feed");
			if (auto_mode == 'feed') {
				monsters[current_type].next_feed = now + wait;
				// Correct the last_feed entries that are erroneous (they say we have still feeds left)
				for (var i = 0; i < 8; i++ )  {
					if (monsters[current_type].last_feed[i] < (now + wait - 22 * 60 * 60)) {
						monsters[current_type].last_feed[i] = now + wait - 22 * 60 * 60;
					}
				}
			} else {
				monsters[current_type].next_attack = now + wait;
				monsters[current_type].attacks_left = 0;
			}
			monsters[current_type].showProfile();
			gmessage = "Daily limit reached,<br />going to profile..."; 
			return 1;
		}
	} else if (errorHeader.indexOf('imaginary friend') != -1) {
		var clanm = monsters[current_type].selectClanMember(); // No other way to know who we tried to feed.
		clanm.id = 0;
		clanm.last_fedback = 0; // He will be the first replaced and never selected.
		if (clanm = monsters[current_type].selectClanMember()) {
			monsters[current_type].feed(clanm.id);
			gmessage = "Imaginary monster,<br /> feeding another...";
			return 1;
		} else {
			monsters[current_type].showProfile();
			gmessage = "Imaginary monster,<br /> going to profile...";
			return 1;
		}
	} else { // this case shouldn't occur, will occur on feed pages
		GM_log("this case shouldn't occur");
		auto_timer = GM_getValue("timer",5);
	}
}

//code to add checkboxes to select users to feed
function friendsAddCheckBoxes(xpathExpr, checkClass) {
	// ****add checkboxes to the document
	var thisDiv;
	var retValue = '';
	var xResults;
	var i = 0;
	
	if (xpathExpr == undefined || checkClass == undefined) return;
	// using the xPath method (which is supposed to giver results faster!)
	xResults = document.evaluate(xpathExpr, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
	for (i = 0; i < xResults.snapshotLength; i++) {
		thisDiv = xResults.snapshotItem(i);
		var newcbox = document.createElement('div');
		newcbox.innerHTML ='<input type="checkbox" class="'+checkClass+'" name="' + thisDiv.getElementsByTagName("a")[0].innerHTML + '" title="'+ thisDiv.getElementsByTagName("a")[0].href +'" />';
		//newcbox.innerHTML ='<input type="checkbox" class="SelectFeed" />';
		thisDiv.appendChild(newcbox); // only appendChild seems to work, insertBefore doesn't! it looks ugly now, need a way to make it appear in a better way
	}
	//thisDiv.insertBefore(newcbox,thisDiv.getElementsByTagName("a")); //insert the checkbox
	//document.insertBefore(newcbox,thisDiv.getElementsByTagName("a")[0]); //insert the checkbox
	//newcbox.addEventListener('click',selectListen,true); // add listener for all the checkboxes
	// ****add listeners for the checkboxes
	xResults = document.evaluate("//input[@class='"+checkClass+"']", document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
	for (i = 0; i < xResults.snapshotLength; i++) {
		newcbox = xResults.snapshotItem(i);
		//recheck the box if it was checked before
		//newcbox.checked = getPersistentVar(newcbox.name,false);
		//newcbox.addEventListener('click',selectListen,true); // add listener for all the checkboxes
		if (newcbox.checked) retValue = newcbox.title; //save the next url we'll visit (the last checked box)
	}
	return retValue;
}

function historyAddCheckboxes () {
}

// Save Power and money
function getMonsterStats () {
	var iBucks;
	//avoid updating stats when visiting pages not ours
	if (current_params.indexOf("ref=top_nav") != -1 || current_params.length <= 25) {
		try {
			iBucks = getNumber(document.getElementsByTagName("sup")[0].parentNode.innerHTML,"\\s.*\\sbucks.*");
			monsters[current_type].money = iBucks;
		} catch(x){
			iBucks = '';
		}
		// try getting the points
		try {
			iBucks = getNumber(getElementsByClassName("centered_text emphasis", "td")[0].innerHTML,".*");
			monsters[current_type].power = iBucks;
		 }catch(x){
			iBucks = '';
		}
	}
}

// bit is the bit to toggle, e.g. PREF_AUTOATTACK
// message is the message to show, e.g. 'AutoAttack' and the id of the button
function toggleAutobit(bit, message) {
	return function() {

		var temp_status = GM_getValue('status',0);
		if (temp_status & bit) {
			document.getElementById("Auto" + message).innerHTML = message + " off!";
			clearInterval(ginterval);
		} else {
			document.getElementById("Auto" + message).innerHTML = message + " on!";
		}
		temp_status ^= bit;
		GM_setValue('status', temp_status);
		gmessage = "Resting...";
		if (temp_status & bit) location.reload();
	};
}

function getAttackStats() {
	var attackPattern = new RegExp("\\[.*\\s(\\d+)\\s+attacks\\sleft\\stoday.*\\]","i"); // detect number of attacks
	var attacks_left;
	try {
		attacks_left = parseInt(getElementsByIDPattern(".*main_sub_title",document.body)[0].innerHTML.match(attackPattern)[1],10);
		monsters[current_type].attacks_left = attacks_left;
	} catch(x) {
		//GM_log("error in attacks_left: " + attacks_left);//DEBUG
	}
	if (attacks_left) {
		monsters[current_type].next_attack = now;
	}
	
	var attack_power;
	var divattacker = document.getElementsByTagName("td");
	try {
		attack_power = getNumber(getElementsByClassName("small_avatar_experience", "div", divattacker[0])[0].innerHTML);
		monsters[current_type].power = attack_power;
	} catch (x) {
		//GM_log("error in attackerPoints: " + attackerPoints);//DEBUG
	}
}

function selectDefender () {
	var divdefender = document.getElementsByTagName("td");
	if (divdefender !=	undefined && divdefender.length >= 3) {
	divs = getElementsByClassName("small_avatar_experience", "div", divdefender[2]);
	var divsurl = getElementsByClassName("small_avatar_attack_link", "div", divdefender[2]);
	if (divs != undefined && divs.length >= 1) {
		var defenders_power = new Array(divs.length);
		for (var i = 0; i < divs.length; i++) {
		var temparray = new Array(2);
		// save points
		temparray[0] = getNumber(divs[i].innerHTML);
		// save url
		temparray[1] = divsurl[i].getElementsByTagName("a")[0].href;
		defenders_power[i] = temparray;
		if (canAttack(monsters[current_type].power,temparray[0])) {
			gnext_page = temparray[1];
			gmessage = "Choosing defender...";
			gcountdown = auto_timer;
			break;
		}
		}
	}
	}
}	

function canAttack(attackerP, defenderP) {
	var diff = defenderP - attackerP;
	var percent = diff/attackerP*100;
	// less than double of our power? then attack!
	if (percent <= 100) {
	return true;
	} else {
		return false;
	}
}

function monsterGetClanMemberById(id) {
	var clanm = undefined;
	//alert("clanNextFeed: " + id + ", " + delay);
	for (var i = 0; i < this.clan_size; i++) {
	if (this.clan[i].id == id) {
		clanm = this.clan[i];
		break;
	}
	}
	return clanm;
}

(function () {
	// Initialize variables
	var i = 1;
	auto_timer = GM_getValue('timer',5);
	mstatus = GM_getValue('status',0);
	
	parseURL (location.href);
	// The page has loaded, the ALREADY_GOING variable makes no further sense
	mstatus = (mstatus | ALREADY_GOING) ^ ALREADY_GOING;

	monsters[0] = new Monster(0);
	next_attack = monsters[0].next_attack;
	next_attack_type = 0;
	var attacks_left;
	if (monsters[0].attacks_left) { attacks_left = 1;} else { attacks_left = 0;}
	var attacks_left_type = 0;
	next_feed = monsters[0].next_feed;
	next_feed_type = 0;

	// Initialize monster stats and next_* variables
	for (i = 1; i < NR_MONSTERS; i++) {
	monsters[i] = new Monster(i);
	if (monsters[i].next_attack < next_attack) {
		next_attack = monsters[i].next_attack;
		next_attack_type = i;
	}
	if (monsters[i].attacks_left) {
		attacks_left = 1;
		attacks_left_type = i;
	}
	if (monsters[i].next_feed < next_feed) {
		next_feed = monsters[i].next_feed;
		next_feed_type = i;
	}
	}

	switch(current_page) {
	case "feed-result.php":
	// Everything was OK
		monsters[current_type].last_feed.shift();
		monsters[current_type].last_feed.push(now);
		monsters[current_type].attacks_left++;
		// We used the last feed
		monsters[current_type].feeds_left--;
		// Do this even if auto is off
		if (!monsters[current_type].feeds_left) {
		monsters[current_type].next_feed = monsters[current_type].last_feed[0] + 22 * 60 * 60;
		}
		var re = /consumer_id=(\d+)/;
		var id = current_params.match(re)[1];
		var clanm;
		if (clanm = monsters[current_type].getClanMemberById(id)) {
		clanm.next_feed = now + 22 * 60 * 60;
		} else {
		clanm = new GenericMonster(current_type, id);
		//clanm.id = id;
		clanm.next_feed = now + 22 * 60 * 60;
		monsters[current_type].addOrReplaceClanMember(clanm,true);
		}
		
		// Automatic functions
		if (mstatus & PREF_AUTOFEED) {
			// If we pass this, we have feeds_left.
			if (monsters[current_type].feeds_left) {
				if (clanm = monsters[current_type].selectClanMember()) {
					monsters[current_type].feed(clanm.id);
				// Not enough Clan Members, try friends.
				} else {
					gnext_page = APPS_URL + MONSTER_TYPE[current_type + NR_MONSTERS] + "/feed-home.php";
					gmessage = "Adding friends to clan...";
					gcountdown = auto_timer;
				}
			} else {
				// We already incremented next_feed in the not automatic part
				monsters[current_type].showProfile();
			} 
		}
		break;
		
		// Choose who to feed next
	// Main page
	case "side-nav.php":
		getMonsterStats();
		monsters[current_type].shop(); //set variables for AutoBuy to use.
		// 1st Feed now! Go to the history page and check it
		if ((mstatus & PREF_AUTOFEED) && (next_feed < now)) {
			if (monsters[current_type].next_feed < now) {
				gnext_page = APPS_URL + MONSTER_TYPE[current_type + NR_MONSTERS] + "/event-history.php";
				gmessage = "Adding some " + MONSTER_TYPE[current_type + NR_MONSTERS] + "<br /> to my clan...";
				gcountdown = auto_timer;
			} else {
				monsters[next_feed_type].showProfile();
			}
		// 2nd Buy stuff - if AutoBuy is enabled and we have yet to buy a shield or a weapon
		} else if ((mstatus & PREF_AUTOBUY) && (monsters[current_type].buy_shield || monsters[current_type].buy_weapon)) {
			gnext_page = APPS_URL + MONSTER_TYPE[current_type + NR_MONSTERS] + "/store-main.php?ref=top_menu";
			gcountdown = auto_timer;
			gmessage = "Buying stuff!";
			// 3rd Attack now!
		} else if ((mstatus & PREF_AUTOATTACK) && ((next_attack < now) || attacks_left)){
			if ((monsters[current_type].next_attack < now) || (monsters[current_type].attacks_left)) {
				var defender_type;
				var random = Math.random();
				random = 1 + Math.floor((NR_MONSTERS - 1) * random);
				defender_type = (current_type + random) % NR_MONSTERS;
				gnext_page = APPS_URL + MONSTER_TYPE[current_type + NR_MONSTERS] + "/fighting-main.php?monster_type=" + MONSTER_TYPE[defender_type];
				gcountdown = auto_timer;
				gmessage = "Looking for enemies...";
			} else {
				var next_type;
				if (next_attack < now) {
					next_type = next_attack_type;
				} else {
					next_type = attacks_left_type;
				}
				monsters[next_type].showProfile();
				gmessage = "We are going to<br />" + MONSTER_TYPE[next_type + NR_MONSTERS] + " profile...";
			}
			// 4th Can't attack or feed now, switch to the profile of the monster that will be
			// able to do something in the shortest time.
		} else if (mstatus & (PREF_AUTOATTACK|PREF_AUTOFEED)) {
			var next_action = 'feed';
			var next_action_mtype = 0;
			var next_action_timer = 22 * 60 * 60;
			if (!(mstatus & PREF_AUTOFEED) || (mstatus & PREF_AUTOATTACK) && (next_attack < next_feed)) {
				next_action = 'attack';
				next_action_mtype = next_attack_type;
				next_action_timer = next_attack - now;
			} else {
				next_action_mtype = next_feed_type;
				next_action_timer = next_feed - now;
			}
		
			monsters[next_action_mtype].showProfile();
			gmessage = "Waiting to " + next_action + "...";
			gcountdown = next_action_timer;
		}
		break;

	case "store-main.php":
		//if buy enabled, click button, then return to main page
		if (mstatus & PREF_AUTOBUY) {
			// if already bought item return
			if (current_params.indexOf("buy=1") != -1) { 
				try{
					var status_msg = getFirstXPathResult("//h1[@class='status']");
					var nodes1 = getFirstXPathResult("//img[@src='http://monsters.joyeurs.com/includes/monsters/img/armour/shield.png']",status_msg);
					var nodes2 = getFirstXPathResult("//img[@src='http://monsters.joyeurs.com/includes/monsters/img/weapons/boomstick.png']",status_msg);
					if (nodes1) {
						monsters[current_type].buy_shield = false;
					}
					if (nodes2) {
						monsters[current_type].buy_weapon = false;
					}
				} catch (x) {}
				monsters[current_type].showProfile();
				gmessage = "Returning to profile...";
			}
			//see which item to buy
			if (monsters[current_type].buy_shield) { 
				gmessage = "Buying Shield!";
				gnext_page = findSubmitButton("Buy Shield!");
				gcountdown = auto_timer;
			}
			else if (monsters[current_type].buy_weapon) {
				gmessage = "Buying Boom Stick!";
				gnext_page = findSubmitButton("Buy Boom Stick!");
				gcountdown = auto_timer;
			}
		}
		break;

	case "event-history.php":
		if ((mstatus & PREF_AUTOFEED) && (current_params == "" || current_params == undefined)) { //|| current_params.indexOf("ref=top_nav") != -1
			if (mstatus & CLANSELECT_MANUAL) {
				alert("This should not happen: error 1217. Report this to the script author with the explanation of what you were doing at the time");
				historyAddCheckboxes();
			} else {
				monsters[current_type].historyAnalyse();
				var clanm = monsters[current_type].selectClanMember();
				if (clanm) {
					monsters[current_type].feed(clanm.id);
				} else {
					// But we have still feeds left
					if (monsters[current_type].feeds_left) {
						gnext_page = APPS_URL + MONSTER_TYPE[current_type + NR_MONSTERS] + "/feed-home.php";
						gmessage = "Adding friends to clan...";
						gcountdown = auto_timer;
					} else {
						monsters[current_type].next_feed = monsters[current_type].last_feed[0] + 22 * 60 * 60;
						monsters[current_type].showProfile();
						gmessage = "No feeds left...";
					}
				}
			}
		} else {
		// Manual feed
		monsters[current_type].historyAnalyse();
		}
		break;

	case "feed-home.php":
		monsters[current_type].showFriends();
		// why are you checking the parameters here?
		if ((mstatus & PREF_AUTOFEED) && (!checkErrorMessage("feed"))&& (current_params == "" || current_params == undefined || current_params.indexOf("ref=top_nav") != -1)) { //
			//friendsAddCheckboxes();
			var clanm;
			monsters[current_type].addFriends();
			if (clanm = monsters[current_type].selectClanMember()) {
				monsters[current_type].feed(clanm.id);
			// Not enough Clan Members, try friends.
			} else {
				monsters[current_type].next_feed = now + 2 * 60 * 60;
				monsters[current_type].showProfile();
				gmessage = "Cannot use all<br />feeds automatically...";
			}
		}
		break;
	// Just select a victim friend and hit the submit button
	case "feed-main.php":
		if ((mstatus & PREF_AUTOFEED) && (!checkErrorMessage("feed"))) {
		divs = getFirstXPathResult("//div[@class='list_item']//input");
		if (divs != undefined && divs != "") {
			divs.checked = true;
			gnext_page = findSubmitButton();
			gmessage = "Choosing victim...";
			gcountdown = auto_timer;
		// no victim friends remaining! delay the AutoFeed by 24 hours and go to the profile
		} else {
			alert("FIXME!");
			monsters[current_type].next_feed = now + 24 * 60 * 60;
			monsters[current_type].showProfile();
		}
		}
		break;
	case "fighting-main.php":
		getAttackStats();
		if (mstatus & PREF_AUTOATTACK) {
			if (monsters[current_type].attacks_left) {
				selectDefender();
			} else {
				monsters[current_type].next_attack = now + 22 * 60 * 60;
				monsters[current_type].showProfile();
				gmessage = "No more attacks for now.<br />Going to profile...";
			}
		}
		break;
	case "fighting-confirm.php":
		getAttackStats();
		if (mstatus & PREF_AUTOATTACK) {
		var num_attacks = document.getElementsByName("num_attacks");
		if (num_attacks != undefined && num_attacks.length >=1) {
				if (num_attacks[0].options.length > 1) {
				// change the number of attacks to the last available option
				num_attacks[0].selectedIndex = num_attacks[0].options.length - 1;
			}
		}
		var item_used = document.getElementsByName("item_used");
		if (item_used != undefined && item_used.length >=1) {
			if (item_used[0].options.length > 1) {
			// select the last item as the item to use
			item_used[0].selectedIndex = item_used[0].options.length - 1;
			}
			}
		gnext_page = findSubmitButton();
		gmessage = "Attacking this enemy...";
		gcountdown = auto_timer;
		}
		break;
	case "fighting-result.php":
		try {
		divs = getElementsByClassName("result_rewards", "div")[0].getElementsByTagName("tr");
		} catch(x){}
		if (divs !=	undefined && divs.length >= 3) {
		// 0 = heading row, 1 = points row, 2 = bucks row
		//  td 0 = Me , td 1 = Enemy
		// change firstChild to lastChild for enemy results.
		try {
			var ptsEarned = parseInt(divs[1].firstChild.innerHTML.match(/(\d+(,|\d)*)/i)[1],10);
			var bksEarned = parseInt(divs[2].firstChild.innerHTML.match(/(\d+(,|\d)*)/i)[1],10);
		} catch(x) {
			ptsEarned = 0;
			bksEarned = 0;
		}
		monsters[current_type].power += ptsEarned;
		monsters[current_type].money += bksEarned; 
		} 
		if (mstatus & PREF_AUTOATTACK) {
		if (monsters[current_type].next_attack < now) {
			var defender_type;
			var random_num = Math.random();
			random_num = 1 + Math.floor((NR_MONSTERS - 1) * random_num);
			defender_type = (current_type + random_num) % NR_MONSTERS;
			gnext_page = APPS_URL + MONSTER_TYPE[current_type + NR_MONSTERS] + "/fighting-main.php?monster_type=" + MONSTER_TYPE[defender_type];
			gcountdown = auto_timer;
			gmessage = "Looking for enemies...";
		} else {
			((next_attack - now) > auto_timer) ? gcountdown = next_attack - now : gcountdown = auto_timer;
			monsters[next_attack_type].showProfile();
			gmessage = "We are going to " + MONSTER_TYPE[next_attack_type + NR_MONSTERS] + " profile...";
		}
		}
		break;
	case "bite.php":
		checkErrorMessage("attack");
		break;
	default:
		GM_log("On Page: " + current_page); //DEBUG
		break;
	}

	insertMenu();
	//status in the browser title
	var tstr;
	if ((location.href.indexOf("fight") != -1) && (mstatus & PREF_AUTOATTACK)) tstr = " fight " + monsters[current_type].attacks_left;
	else if ((location.href.indexOf("feed") != -1) && (mstatus & PREF_AUTOFEED)) tstr = " feed " + monsters[current_type].feeds_left;
	else tstr = " ";

	// problem is we need to know if the current page participates in the feed or fight phase, then we can clearly say on top what mode it is. I think we may need auto_mode. or do you a better idea?
	if (mstatus & (PREF_AUTOATTACK|PREF_AUTOFEED)) document.title = '[' + MONSTER_TYPE[current_type].charAt(0).toUpperCase() + tstr + '] ' + document.title + " enhanced by VX & chopinhauer";
	else document.title = document.title + " enhanced by VX & chopinhauer";

	// Do we have any auto-functions active?
	if (gmessage) {
	startActionTimer();
	} else {
	document.getElementById('monsteraction').innerHTML = "Resting...";
	}

	// Save variables

	for (i = 0; i < NR_MONSTERS; i++) {
	monsters[i].save();
	}
	GM_setValue('status',mstatus);
//end main function
} ) ();
//end script
} ) ();
/* vim:set tw=0 sts=0 sw=2 ts=2 ft=javascript: */ 
