// ==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-beta
// @date           2008-04-28
// @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 is an *ALPHA*, so it can work poorly or not at all. Eventually this script could be merged with:
	http://userscripts.org/scripts/show/20462

+++ 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-FEED WORKS:
	1. There is an 'Autofeed!' button on the sidebar. If you press the function will activate. (The button will now show 'Manual feed')
	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. If you press it the function will activate. (The button will now show 'Manual attack')
	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
		
+++ 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;
var PREF_AUTOATTACK = 2;
var CLANSELECT_MANUAL = 4;
var ALREADY_GOING = 8;
var PREF_ALL = 255;

// 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"// script homepage/description URL
			+ "/scripts/show/25396",
	identifier: "http://userscripts.org"	// script URL
			+ "/scripts/source/25396.user.js",
	version: "1.0-beta", // version
	date: (new Date(2008, 4 - 1, 28)) // update date
			.valueOf()
};
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 {
	addEventHandler(window, "load", function () { try {
		(unsafeWindow || window.wrappedJSObject || window)
			.UserScriptUpdates.requestAutomaticUpdates(SCRIPT);
		} catch	(ex) {} }, false);
} catch	(ex) {}

// objects
/* ClanMember: object that contains the main data of people to feed
 *  * id: the Facebook ID of the user
 *  * type: the type of the monster
 *  * next_feed: timestamp of when we can feed it next.
 *  * last_fedback: timestamp of when this monster fed us last.
 *  * name: the name of the monster's owner.
 */
function ClanMember(mtype) {
	this.id = 0;
	this.type = mtype;
	this.last_fedback = 0;
	this.next_feed = 0;
	this.name = '';
}

/* 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 ClanMember objects to feed.
 * * clan_size: cardinality of clan.
 *
 * 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 = GM_getValue(mtype + "/attacks_left",-1);
	this.next_attack = GM_getValue(mtype + "/next_attack",0);
	this.money = GM_getValue(mtype + "/money",0);
	this.clan_size = GM_getValue(mtype + "/clan_size",0);
	this.clan = new Array();
	
	for (var i=0; i < this.clan_size; i++) {
		var clanmember = new ClanMember(mtype);
		clanmember.id = parseInt(GM_getValue(mtype + "/clan/" + i + "/id"),10);
		clanmember.last_fedback = GM_getValue(mtype + "/clan/" + i + "/last_fedback");
		clanmember.next_feed = GM_getValue(mtype + "/clan/" + i + "/next_feed");
		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::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 ClanMember(this.type);
		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);
		if (!this.getClanMemberById(id)) { 
			var clanm = new ClanMember(this.type);
			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_log(this.type + " saving: " + this.next_feed);
	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 + "/clan_size", this.clan.length);
	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);
		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);
	}
}

// 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
		menuCode.push('<td>' + MONSTER_TYPE[monsters[i].type].charAt(0).toUpperCase() +'</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"><button id="autofight" type="button">Autoattack ' + (mstatus & PREF_AUTOATTACK ? "on! ":"off!") + '</button></td></tr>');
	menuCode.push('<tr><td colspan="0"><button id="autofeed" type="button">Autofeed ' + (mstatus & PREF_AUTOFEED ? "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('autofight');
	t_elm.addEventListener('click',toggleAutoattack,true);
	t_elm = document.getElementById('autofeed')
	t_elm.addEventListener('click',toggleAutofeed,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() {
	// find the right button - for interval to auto click
	var elms = document.getElementsByTagName('input');
	for (i=0; i<elms.length; i++) {
		if (elms[i].type == 'submit') {
			return elms[i];
			break;
		}
	}
	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 (){
		//GM_log("intervalCheck()"); //DEBUG
		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 ClanMember(current_type);
			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;
	
	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 (var 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" />';
		//GM_log(thisDiv.getElementsByTagName("a")[0].text);
		//GM_log(newcbox.innerHTML);//DEBUG
		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 (var 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
		//GM_log('adding event listener');//DEBUG
		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.*");
			//GM_log("bucks:"+iBucks);//DEBUG
			monsters[current_type].money = iBucks;
		} catch(x){
			iBucks = '';
		}
		// try getting the points
		try {
			iBucks = getNumber(getElementsByClassName("centered_text emphasis", "td")[0].innerHTML,".*");
			//GM_log('points'+iBucks);//DEBUG
			monsters[current_type].power = iBucks;
		 }catch(x){
			iBucks = '';
		}
	}
}

function toggleAutoattack() {
	var mstatus = GM_getValue('status');
	if (mstatus & PREF_AUTOATTACK) {
	document.getElementById('autofight').innerHTML = "Autoattack off!";
	clearInterval(ginterval);
	} else {
	document.getElementById('autofight').innerHTML = "Autoattack on!";
	}
	mstatus ^= PREF_AUTOATTACK;
	GM_setValue('status',mstatus);
	gmessage = "Resting...";
	if (mstatus & PREF_AUTOATTACK) location.reload();
}

function toggleAutofeed() {
	var mstatus = GM_getValue('status');
	if (mstatus & PREF_AUTOFEED) {
	document.getElementById('autofeed').innerHTML = "Autofeed off!";
	clearInterval(ginterval);
	} else {
	document.getElementById('autofeed').innerHTML = "Autofeed on!";
	}
	mstatus ^= PREF_AUTOFEED;
	GM_setValue('status',mstatus);
	gmessage = "Resting...";
	if (mstatus & PREF_AUTOFEED) 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
	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 = 0;
	var attacks_left_type = 0;
	next_feed = monsters[0].next_feed;
	next_feed_type = 0;

	// Initialize monster stats and next_* variables
	for (var 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 ClanMember(current_type);
		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();
		// 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 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_attack_type + NR_MONSTERS] + " profile...";
		}
		// 3rd 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 "event-history.php":
		if ((mstatus & PREF_AUTOFEED) && (current_params == "" || current_params == undefined)) {
			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)) {
			//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 + 4 * 60 * 60;
			monsters[current_type].showProfile();
			gmessage = "No more attack 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 = 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 {
			((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;
	}

	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 (var 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: */ 
