// ==UserScript==
// @name           zynga games - castle age only, modified from original
// @namespace      zynga
// @description    Auto player for zynga's castle age game
// @include        http://apps.*facebook.com/castle_age/*
// @include        http://www.facebook.com/common/error.html
// ==/UserScript==

// disabled @require        http://usocheckup.dune.net/index.php?scriptid=44038

// disable fastLoad if the computer is slow or many games are running at the same time.
var fastLoad=false;
var debug=false;

// update script from: http://userscripts.org/scripts/review/57917


var SUC_script_num = 57917; // Change this to the number given to the script by userscripts.org (check the address bar)
try{function updateCheck(forced){if ((forced) || (parseInt(GM_getValue('SUC_last_update', '0')) + (86400000*7) <= (new Date().getTime()))){try{GM_xmlhttpRequest({method: 'GET',url: 'http://userscripts.org/scripts/source/'+SUC_script_num+'.meta.js?'+new Date().getTime(),headers: {'Cache-Control': 'no-cache'},onload: function(resp){var local_version, remote_version, rt, script_name;rt=resp.responseText;GM_setValue('SUC_last_update', new Date().getTime()+'');remote_version=parseInt(/@uso:version\s*(.*?)\s*$/m.exec(rt)[1]);local_version=parseInt(GM_getValue('SUC_current_version', '-1'));if(local_version!=-1){script_name = (/@name\s*(.*?)\s*$/m.exec(rt))[1];GM_setValue('SUC_target_script_name', script_name);if (remote_version > local_version){if(confirm('There is an update available for the Greasemonkey script "'+script_name+'."\nWould you like to go to the install page now?')){GM_openInTab('http://userscripts.org/scripts/show/'+SUC_script_num);GM_setValue('SUC_current_version', remote_version);}}else if (forced)alert('No update is available for "'+script_name+'."');}else GM_setValue('SUC_current_version', remote_version+'');}});}catch (err){if (forced)alert('An error occurred while checking for updates:\n'+err);}}}GM_registerMenuCommand(GM_getValue('SUC_target_script_name', '???') + ' - Manual Update Check', function(){updateCheck(true);});updateCheck(false);}catch(err){}

if(!GM_log) {
	GM_log=console.debug;
}

/*

Modified version of the zynga games script .. so castle age woould work.

*

/*

Becareful of the auto deposit, in some games you can click on the money and the bank screen will come up quickly.  When you have auto deposit enabled it may seem like the money is disappearing straight away.

*/

String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); }

var nHtml={
FindByAttrContains:function(obj,tag,attr,className) {
	if(attr=="className") { attr="class"; }
	var q=document.evaluate(".//"+tag+
		"[contains(@"+attr+",'"+className+
		"')]",obj,null,
		XPathResult.FIRST_ORDERED_NODE_TYPE,null);
	if(q && q.singleNodeValue) { return q.singleNodeValue; }
	return null;
},
FindByAttrXPath:function(obj,tag,className) {
	var q=null;
	try {
		var xpath=".//"+tag+"["+className+"]";
		if(obj==null) {
			GM_log('Trying to find xpath with null obj:'+xpath);
			return null;
		}
		q=document.evaluate(xpath,obj,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null);
	} catch(err) {
		GM_log("XPath Failed:"+xpath+","+err);
	}
	if(q && q.singleNodeValue) { return q.singleNodeValue; }
	return null;
},
FindByAttr:function(obj,tag,attr,className) {
	if(className.exec==undefined) {
		if(attr=="className") { attr="class"; }
		var q=document.evaluate(".//"+tag+"[@"+attr+"='"+className+"']",obj,null,XPathResult.FIRST_ORDERED_NODE_TYPE,null);
		if(q && q.singleNodeValue) { return q.singleNodeValue; }
		return null;
	}
	var divs=obj.getElementsByTagName(tag);
	for(var d=0; d<divs.length; d++) {
		var div=divs[d];
		if(className.exec!=undefined) {
			if(className.exec(div[attr])) {
				return div;
			}
		} else if(div[attr]==className) {
			return div;
		}
	}
	return null;
},
FindByClassName:function(obj,tag,className) {
	return this.FindByAttr(obj,tag,"className",className);
},
VisitUrl:function(url) {
	this.setTimeout(function() {
		if(url.indexOf("violations.php")>=0) {
			GM_log("Huh? we clicked on the voilations url:"+url);
			return;
		}

		document.location.href=url;
	},1000+Math.floor(Math.random()*1000));
},

Click2:function(obj,evtName) {
	var evt = document.createEvent("MouseEvents");
	evt.initMouseEvent(evtName, true, true, window,
		0, 0, 0, 0, 0, false, false, false, false, 0, null);
	return !obj.dispatchEvent(evt);
},
ClickNoWait:function(obj) {
//	this.Click2(obj,"mousedown");
//	this.Click2(obj,"mouseup");
	this.Click2(obj,"click");
},
Click:function(obj) {
	this.setTimeout(function() {
		nHtml.ClickNoWait(obj);
	},1000+Math.floor(Math.random()*1000));
},
spaceTags:{
	'td':1,
	'br':1,
	'hr':1,
	'span':1,
	'table':1
},
GetText:function(obj) {
	var txt=' ';
	if(obj.tagName!=undefined && this.spaceTags[obj.tagName.toLowerCase()]) {
		txt+=" ";
	}
	if(obj.nodeName=="#text") { return txt+obj.textContent; }
	for(var o=0; o<obj.childNodes.length; o++) {
		var child=obj.childNodes[o];
		txt+=this.GetText(child);
	}
	return txt;
},

htmlRe:new RegExp('<[^>]+>','g'),
StripHtml:function(html) {
	return html.replace(this.htmlRe,'').replace(/&nbsp;/g,'');
},

timeouts:{},
setTimeout:function(func,millis) {
	var t=window.setTimeout(function() {
		func();
		nHtml.timeouts[t]=undefined;
	},millis);
	this.timeouts[t]=1;
},
clearTimeouts:function() {
	for(var t in this.timeouts) {
		window.clearTimeout(t);
	}
	this.timeouts={};
}

};


Zynga={
stats:{},
totalJobs:0,
lastReload:new Date(),
autoReloadMilliSecs:15*60*1000,

levelRe:new RegExp('Level\\s*:\\s*([0-9]+)','i'),
rankRe:new RegExp(',\\s*level\\s*:?\\s*[0-9]+\\s+([a-z ]+)','i'),
armyRe:new RegExp('My Army\\s*\\(?([0-9]+)','i'),
staminaRe:new RegExp('([0-9\\.]+)\\s*/\\s*([0-9]+)','i'),
htmlJunkRe:new RegExp("\\&[^;]+;","g"),
energyRe:new RegExp("([0-9]+)\\s+(energy)","i"),
gameNameRe:new RegExp("^/([^/]+)/"),
experienceRe:new RegExp("\\+([0-9]+)"),
gainLevelRe:new RegExp("gain\\s+level\\s+([0-9]+)\\s+in","i"),
moneyRe:new RegExp("\\$([0-9,]+)\\s*-\\s*\\$([0-9,]+)","i"),
firstNumberRe:new RegExp("([0-9]+)"),
gameName:null,

GameName:function() {
	if(this.gameName!=null) { return this.gameName; }
	var m=this.gameNameRe.exec(window.location.pathname);
	if(m) { 
		this.gameName=m[1];
		return m[1]; 
	}
	GM_log('Error unknown game:'+window.location.pathname);
	return null;
},

GMLog:function(mess) {
	GM_log(this.GameName()+":"+mess);
},
GMDebug:function(mess) {
	if(debug) { this.GMLog(mess); }
},

GMSetValue:function(n,v) {
	return GM_setValue(this.GameName()+"__"+n,v);
},
GMGetValue:function(n,v) {
	return GM_getValue(this.GameName()+"__"+n,v);
},


VisitUrl:function(href) {
	this.waitMilliSecs=10000;
	nHtml.VisitUrl(href);
},
Click:function(button) {
	this.waitMilliSecs=10000;
	nHtml.Click(button);
},


//////////////////////////////

NumberOnly:function(num) {
	var numOnly=parseFloat(num.replace(/[^0-9\.]/g,""));
	return numOnly;
},
RemoveHtmlJunk:function(html) {
	return html.replace(this.htmlJunkRe,'');
},

GetStatusNumbers:function(node) {
	var txt=nHtml.GetText(node);
	var staminam=this.staminaRe.exec(txt);
	if(staminam) {
		return {'num':parseInt(staminam[1]),'max':parseInt(staminam[2])};
	} else {
		this.GMLog('Cannot find status:'+txt);
	}
	return null;
},
GetRankStat:function() {
	var attrDiv =nHtml.FindByAttrContains(document.body,"div","class",'keep_stat_title');
	if (attrDiv) {
		var txt = nHtml.GetText(attrDiv);
		var levelm=this.rankRe.exec(txt);
		if (levelm) {
			var rank = this.rankTable[levelm[1].toString().toLowerCase().trim()];
			if (rank > 0) {
				this.stats['rank']=rank;
				this.GMSetValue('MyRank',this.stats.rank);
				this.GMSetValue('MyRankLast',(new Date().getTime()).toString());
			} else {
				this.GMLog("Unknown rank " + levelm[1].toString());
			}
			//this.GMLog("Found rankRe " + levelm[1].toString() + ' ' + this.stats.rank);
		} else {
			if (this.currentTab == 'keep') {
				this.GMLog("Can't find rankRe in " + txt);
			}
		}
	} else {
		if (this.currentTab == 'keep') {
			this.GMLog("Can't find keep_stat_title on keep page");
		}
	}
},
GetStats:function() {
	this.stats={};
	var isKidsGame=this.IsKidsGame();
	
	// health
	var health=nHtml.FindByAttrContains(document.body,"span","id",'_current_health');
	var healthMess='';
	if(!health) {
		health=nHtml.FindByAttrXPath(document.body,'span',"contains(@id,'_health') and not(contains(@id,'health_time'))");
	}
	if(health!=null) {
		this.stats['health']=this.GetStatusNumbers(health.parentNode);
		if(this.stats.health) {
			healthMess="Health: "+this.stats.health.num;
		}
	} else {
		this.GMLog('Could not find health');
		this.waitMilliSecs=5000;
		this.needReload = true;
	}
		
	// stamina
	var stamina=nHtml.FindByAttrContains(document.body,"span","id",'_current_stamina');
	var staminaMess='';
	if(!stamina) {
		stamina=nHtml.FindByAttrXPath(document.body,'span',"contains(@id,'_stamina') and not(contains(@id,'stamina_time'))");
		//stamina=nHtml.FindByAttrContains(document.body,"span","id",'_stamina');
	}
	if(stamina!=null) {
		this.stats['stamina']=this.GetStatusNumbers(stamina.parentNode);
		if(this.stats.stamina) {
			staminaMess="Stamina: "+this.stats.stamina.num;
		}
	} else {
		this.GMLog('Could not find stamina');
		this.waitMilliSecs=5000;
	}

	var energyMess='';
	// castle age...
	var energy=nHtml.FindByAttrContains(document.body,"span","id",'_current_energy');
	if(!energy) {
		energy=nHtml.FindByAttrXPath(document.body,'span',"contains(@id,'_energy') and not(contains(@id,'energy_time'))");
	}
	if(energy!=null) {
		this.stats['energy']=this.GetStatusNumbers(energy.parentNode);
		if(this.stats.energy!=null) {
			energyMess="Energy: "+this.stats.energy.num;
		}
	} else {
		this.GMLog('Could not find energy');
		this.waitMilliSecs=5000;
	}

	// level
	var level=nHtml.FindByAttrContains(document.body,"div","title",'experience points');
	var levelMess;
	if(level!=null) {
		var txt=nHtml.GetText(level);
		var levelm=this.levelRe.exec(txt);
		if (levelm) {
			this.stats['level']=parseInt(levelm[1]);
			levelMess = "Level: " + this.stats.level;
		} else {
			this.GMLog('Could not find level re');
		}
	} else {
		this.GMLog('Could not find level obj');
	}

	this.stats['rank']=parseInt(this.GMGetValue('MyRank'));


	var td=nHtml.FindByAttrContains(document.body,"div","id","main_bntp");
	if (td) {
		var a=nHtml.FindByAttrContains(td,"a","href","army");
		var txt = nHtml.GetText(a);
		var m=this.armyRe.exec(txt);
		if (m) {
			var army = parseInt(m[1]);
			this.stats['army']=army;
			var armyMess = "Army: " + this.stats.army;
		} else {
			this.GMLog("Can't find armyRe in " + txt);
		}			
	} else {
		this.GMLog("Can't find main_bntp stats");
	}
	
	
	// special forces(..._user_cash)
	var cashObj=nHtml.FindByAttrContains(document.body,"span","id",'_user_cash');
	if(!cashObj && isKidsGame) {
		// robhood,etc.
		cashObj=nHtml.FindByAttrContains(document.body,"span","id",'_cur_cash');
	}
	if(!cashObj) {
		// vampire wars.
		cashObj=nHtml.FindByAttrContains(document.body,"div","id",'_current_cash');
	}

	if(!cashObj && this.IsVikingGame()) {
		// viking clan
		cashObj=nHtml.FindByAttrContains(document.body,"span","id",'_cash');
	}
	if(!cashObj) {
		// piratesrule
		cashObj=nHtml.FindByAttrContains(document.body,"a","href",'nav=status_button_cash');
		if(cashObj) { cashObj=cashObj.parentNode; }
	}
	
	if(!cashObj) {
		cashObj=nHtml.FindByClassName(document.body,"strong","money");
	}
	if(!cashObj) {
		// school of magic
		cashObj=nHtml.FindByAttrContains(document.body,"span","className",'money');
		if(cashObj) {
			cashObj=cashObj.parentNode.parentNode;
		}
	}
	if(!cashObj || this.GameName()=="streetracinggame") {
		// streetracinggame
		cashObj=nHtml.FindByAttrXPath(document.body,"strong","contains(string(),'$')");
	}
	var numberRemoveRe=new RegExp('\\s*([0-9,\\s*]+)\\s*\\S+');
	if(cashObj) {
		var cashTxt=nHtml.GetText(cashObj);
		if(this.IsVikingGame()) {
			var m=numberRemoveRe.exec(cashTxt);
			if(m && m.length>0) {
				cashTxt=m[1];
			}
		}
		var cash=this.NumberOnly(cashTxt);
		this.stats.cash=cash;
		
		var moneyMessDiv=document.getElementById('autozynga_money_mess');
		if(moneyMessDiv) { moneyMessDiv.innerHTML="Money $"+cash+" "+staminaMess+" "+healthMess+" "+energyMess+" "+levelMess+" "+armyMess; }
	} else {
		this.GMLog('Could not find cash');
	}
},

SetMessage:function(mess) {
	this.SetupDivs();
	var d=document.getElementById('autozynga_mess');
	if(d) { d.innerHTML=mess; }
},
SetFightMessage:function(mess) {
	this.SetupDivs();
	var d=document.getElementById('autozynga_fight_mess');
	if(d) { d.innerHTML=mess; }
},
SetHospitalMessage:function(mess) {
	this.SetupDivs();
	var d=document.getElementById('autozynga_hospital_mess');
	if(d) { d.innerHTML=mess; }
},

SetJobControl:function(mess) {
	this.SetupDivs();
	var d=document.getElementById('autozynga_job_control');
	if(d) { d.innerHTML=mess; }
},
SetControl:function(mess) {
	this.SetupDivs();
	var d=this.GetControlDiv();
	if(d) { d.innerHTML=mess; }
},
GetControlDiv:function() {
	return document.getElementById('autozynga_control');
},

SetupDivs:function() {
	if(document.getElementById('autozynga_div')) {
		return false;
	}
	var div=document.createElement('div');
	div.id='autozynga_div';
	div.style.padding='4px';
	div.style.border='2px solid #444';
	div.style.background='#fff';
	div.style.color='#000';

	var controlDiv=document.createElement('div');
	controlDiv.id='autozynga_control';
	var jobControlDiv=document.createElement('div');
	jobControlDiv.id='autozynga_job_control';

	var messDiv=document.createElement('div');
	messDiv.id='autozynga_mess';
	var moneyMessDiv=document.createElement('div');
	moneyMessDiv.id='autozynga_money_mess';
	var fightMessDiv=document.createElement('div');
	fightMessDiv.id='autozynga_fight_mess';
	var hospitalMessDiv=document.createElement('div');
	hospitalMessDiv.id='autozynga_hospital_mess';

	div.appendChild(messDiv);
	div.appendChild(fightMessDiv);
	div.appendChild(moneyMessDiv);
	div.appendChild(hospitalMessDiv);
	div.appendChild(jobControlDiv);
	div.appendChild(controlDiv);

	var b=document.getElementById('sidebar_ads');
	if(b) {
		b.parentNode.insertBefore(div,b.parentNode.childNodes[0]);
	} else {
	
		var app=document.getElementById('fb_sell_profile');
		if(!app) {
			app=nHtml.FindByAttr(document.body,'div','className','app');
		}
		if(!app) {
			app=nHtml.FindByAttr(document.body,'div','id',/^app.*header$/);
		}
		if(app) {
			if(app.parentNode.parentNode) {
				// some ajax games won't reload before the app's area, let's insert the div there.
				app.parentNode.parentNode.insertBefore(div,app.parentNode);
			} else {
				app.insertBefore(div,app.childNodes[0]);
			}
		} else {
			this.GMLog('cannot find app div');
		}
	}

	return true;
},


IsVikingGame:function() {
	var pathname=location.pathname;
	if(pathname.substring(pathname.length-7)=="/quests" || pathname.substring(pathname.length-7)=="/battle") {
		return true;
	}
	return false;
},

IsOldGame:function() {
	var pathname=location.pathname;
	if(pathname.substring(pathname.length-8)=="/actions" 
	|| pathname.substring(pathname.length-6)=="/fight"
	|| pathname.substring(pathname.length-8)=="/banking"
	|| pathname.substring(pathname.length-7)=="/invest"
	) {
		return true;
	}
	return false;
},

kidsGames:{
	'mafiosowar':1,
	'robhood':1,
	'itsfashion':1,
	'secretagents':1
},
IsKidsGame:function() {
	var gameName=this.GameName();
	if(this.kidsGames[gameName]) { return true; }
	return false;
},

//////////////////////////

GetJobs:function() {
	var jobs={};
	var gameName=this.GameName();

	var table=nHtml.FindByAttr(document.body,"table","className",'quests_layout');
	var jobLists=[];
	if(!table) {
		return jobs;
	}
	
	var ss=document.evaluate(".//div[contains(@class,'quests_background')]",table,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
	
	if (ss.snapshotLength == 0) {
		this.GMLog("Failed to find quests_background");
	}
	
	var jobsAdded = 0;
	for(var s=0; s<ss.snapshotLength; s++) {
		var div=ss.snapshotItem(s);
		
		if(!nHtml.FindByAttr(div,"div","className","quest_desc")) {
		if(!nHtml.FindByAttr(div,"div","className","quest_sub_title")) {
			this.GMLog("Can't find quest description or sub-title");
			continue;
		}
		}

		var job_name=null;
		var reward=null;
		var energy=null;
		var experience=null;
		
		var divTxt=nHtml.GetText(div);

		if(this.gainLevelRe.exec(divTxt)) {
			// ignore gain level in another game..
			continue;
		}
		
		var item_title=nHtml.FindByAttrXPath(div,'div',"@class='quest_desc' or @class='quest_sub_title'");
		if(item_title) {
			item_title=item_title.getElementsByTagName('b')[0];
			job_name=nHtml.StripHtml(item_title.innerHTML.toString()).trim();
		}

		var expM=this.experienceRe.exec(divTxt);
		if(expM) { experience=this.NumberOnly(expM[1]); }
		else {
			var expObj=nHtml.FindByAttr(div,'div','className','job_experience');
			if(expObj) {
				experience=(this.NumberOnly(nHtml.GetText(expObj)));
			} else {
				this.GMLog('cannot find experience:'+job_name);
			}
		}

		if(!job_name) {
			this.GMLog('no job name for this row'+div.innerHTML);
			continue;
		}

		if((idx=job_name.indexOf('<br>'))>=0) {
			job_name=job_name.substring(0,idx);
		}


		var m=this.energyRe.exec(divTxt);
		if(m) {
			energy=this.NumberOnly(m[1]);
		} else {
			var eObj=nHtml.FindByAttrContains(div,'div','className','quest_req');
			if(eObj) {
				energy=eObjs.getElementsByTagName('b')[0];
			}
		}
		
		if(!energy) {
			this.GMLog('cannot find energy for job:'+job_name);
			continue;
		}

		var moneyTxt=null;
		
		var m=this.moneyRe.exec(this.RemoveHtmlJunk(divTxt));
		if(m) {
			var rewardLow=this.NumberOnly(m[1]);
			var rewardHigh=this.NumberOnly(m[2]);
			reward=(rewardLow+rewardHigh)/2;
		} else {
			this.GMLog('no money found:'+job_name);
		}
		
		var a=nHtml.FindByAttr(div,"input","name",/^Do/);
		if(!a) {
			this.GMLog('no button found:'+job_name);
			continue;
		}
		
		jobs[job_name]={
			'click':a,'tr':div,
			'energy':energy,'reward':reward,
			'experience':experience
		};
		
		jobsAdded++;
		
	}
	return jobs;
},
SetAutoJob:function(job_name,job_energy) {
	this.GMSetValue('AutoJob',job_name);
	this.GMSetValue('AutoJobEnergy',job_energy);
},
GetAutoJob:function() {
	var name=this.GMGetValue('AutoJob','');
	var energy=parseInt(this.GMGetValue('AutoJobEnergy',0));
	return { 'name':name,'energy':energy};
},

IsEnoughEnergyForAutoJob:function() {
	var waitTillFull=this.GMGetValue('WaitTillFull',0);
	var autoJobObj=this.GetAutoJob();
	if(!this.stats.energy) { return false; }
	if(!waitTillFull) {
		if(autoJobObj.energy>0 && this.stats.energy.num>=autoJobObj.energy) {
			return true;
		}
	} else if(this.stats.energy.num>=(this.stats.energy.max-1)) {
		return true;
	}
	return false;
},

IsEnoughStaminaForAutoFight:function() {
	var waitTillFull=this.GMGetValue('WaitTillFull',0);
	var waitWasFull=this.GMGetValue('WaitWasFull',0);
	if(!this.stats.stamina) { return false; }
	if(!waitTillFull || waitWasFull) {
		if(this.stats.stamina.num > 0) {
			return true;
		}
	} else if(this.stats.stamina.num>=(this.stats.stamina.max)) {
		this.GMSetValue('WaitWasFull',1);
		return true;
	}
	return false;
},

DrawJobs:function(jobs,enableAutoJob) {
	if(jobs==null) { jobs=this.GetJobs(); }
	var gameName=this.GameName();
	if(this.IsOldGame() && location.href.indexOf('/actions')<0) {
		return;
	}
	var autoJobObj=this.GetAutoJob();
	for(var job_name in jobs) {
		var job=jobs[job_name];
		var x=nHtml.FindByAttr(job.tr,'div','className','autojob');
		if(x) {
			continue;
		}

		var div=document.createElement('div');
		div.className='autojob';
		div.style.fontSize='10px';
		div.innerHTML="$ per energy: "+
			(Math.floor(job.reward/job.energy*10)/10)+
			"<br />Exp per energy: "+
			(Math.floor(job.experience/job.energy*100)/100)+
			"<br />";

		if(autoJobObj.name==job_name) {
			var b=document.createElement('b');
			b.innerHTML="Current auto job";
			div.appendChild(b);
		} else if(enableAutoJob) {

			var setAutoJob=document.createElement('a');
			setAutoJob.innerHTML='Auto run this job.';
			setAutoJob.job_name=job_name;

			var job_nameObj=document.createElement('span');
			job_nameObj.innerHTML=job_name;
			job_nameObj.style.display='none';
			setAutoJob.appendChild(job_nameObj);
			
			var job_energyObj=document.createElement('span');
			job_energyObj.innerHTML=job.energy;
			job_energyObj.style.display='none';
			setAutoJob.appendChild(job_energyObj);
			setAutoJob.addEventListener("click",function(e) {
				var sps=e.target.getElementsByTagName('span');
				if(sps.length>0) {
					Zynga.SetAutoJob(sps[0].innerHTML.toString(),sps[1].innerHTML.toString());
					Zynga.Jobs();
				} else {
					this.GMLog('what did we click on?');
				}
			},false);
			div.appendChild(setAutoJob);
		}
		if(gameName=="castle_age" || gameName=="glamour_age" || gameName=="under_world") {	
			div.style.position='absolute';
			div.style.opacity=0.8;
			div.style.background='#888';
		}
		job.click.parentNode.insertBefore(div,job.click);
	}
},

Jobs:function(getPage) {
	this.totalJobs=0;

	var gameName=this.GameName();
	var jobs=this.GetJobs();
	for(var job_name in jobs) {
		this.totalJobs++;
	}

	var autoJobObj=this.GetAutoJob();
	var autoJob=autoJobObj.name;
	
	if(this.totalJobs==0) {
		if(autoJob!="") {
			if(this.IsEnoughEnergyForAutoJob()) {
				this.VisitJobPage();
				return 'GoJobs';
			}
		}
		return null;
	}
	var page='jobs';
	this.DrawJobs(jobs,true);

	//~~~ get the sub page of the job
	if(getPage) { return page; }

	if(autoJob) {
		var waitTillFull=this.GMGetValue('WaitTillFull',0);
		var job=jobs[autoJob];
		var doAutoJob=false;

		var mess='';
		if(!job) {
			this.GMLog('Could not find job: '+autoJob);
			mess+='<br />Could not find job: '+autoJob+", make sure you have clicked into the correct mission page.";
		} else if(this.IsEnoughEnergyForAutoJob()) {
			doAutoJob=true;
		}
/*
		if(!waitTillFull) {
			this.pageHasAutoJob=false;
			if(this.stats.energy.num>=job.energy) {
				this.GMLog(" energy:"+this.stats.energy.num+", max:"+this.stats.energy.max);
				doAutoJob=true;
			}
			
		} else if(this.stats.energy.num>=(this.stats.energy.max-1)) {
			this.pageHasAutoJob=false;
			this.GMLog(" energy:"+this.stats.energy.num+", max:"+this.stats.energy.max);
			doAutoJob=true;
		}
*/
		if(doAutoJob) {
			this.GMLog(': do auto job:'+autoJob);
			this.waitMilliSecs=10000;
			nHtml.setTimeout(function() {
				Zynga.Click(job.click);
			},1000);
		} else {
			this.SetMessage(mess+'Waiting for more energy:'+this.stats.energy.num+"/"+this.stats.energy.max+", job energy:"+(job?job.energy:""));
			// let's not waste the cpu if we're only waiting for the energy to recover.
			//this.waitMilliSecs=8000;
			return null;
		}
		return 'jobs';
	} else {
		this.SetMessage('');
//		this.SetControl('');
	}

	return null;
},


SetJobControls:function() {
	var controlHtml='';

	var autoJobObj=this.GetAutoJob();
	var autoJob=autoJobObj.name;
	if(autoJob) {
		controlHtml+="<a id='stopAutoJob' href='javascript:;'>Stop auto job: "+autoJob+"(energy: "+autoJobObj.energy+")"+"</a><br />";
	}
	this.SetJobControl(
		controlHtml
	);

	if(autoJob) {
		var stopA=document.getElementById('stopAutoJob');
		stopA.addEventListener('click',function() {
			Zynga.SetAutoJob('',0);
		},false);
	}
},
SetControls:function() {
	this.SetJobControls();

	var controlDiv=this.GetControlDiv();
	if(controlDiv && controlDiv.innerHTML.length>0) { 
		// we already have the controls
		return; 
	}
	
	var controlHtml='';
	var bankInstructions1="Minimum cash to have on hand, press tab to save";
	var bankInstructions2="Maximum cash to have on hand, bank anything above this, press tab to save(leave blank to disable)";
	var healthInstructions="Minimum to have before we visit the hospital, press tab to save(leave blank to disable): ";
	var userIdInstructions="User IDs(not user name).  Click with the right mouse button on the link to the users profile & copy link.  Then paste it here and remove everything but the last numbers. (ie. 123456789)";
	controlHtml+="<div id='AutoZyngaPaused' style='display: none'><b>Paused on mouse click.</b><br /><a href='javascript:;' id='AutoZyngaRestart' >Click here to restart</a></div>";
	controlHtml+="<img title='"+bankInstructions1+"' src='http://mwdirectfb10.static.zynga.com/mwfb/graphics/icon_cash_16x16_01.gif' /><input type='text' id='MinInCash' title='"+bankInstructions1+"' />";
	controlHtml+="<br /><img title='"+bankInstructions2+"' src='http://facebook2.pirates.static.zynga.com/graphics/icon-blood.gif' /><input type='text' id='MinToBank' title='"+bankInstructions2+"' /><br />";
	controlHtml+="<img title='"+healthInstructions+"' src='http://mwdirectfb10.static.zynga.com/mwfb/graphics/icon_health_16x16_01.gif' /><input type='text' id='MinToHospital' size='3' title='"+healthInstructions+"' /><br />";
	controlHtml+="<img src='http://mwdirectfb10.static.zynga.com/mwfb/graphics/icon_stamina_16x16_01.gif' />";
	controlHtml+="<input type='checkbox' id='WaitTillFull' />Wait for stamina/energy near full<br />";
	controlHtml+="<input type='checkbox' id='AutoZyngaFightTargetFM'  "+(this.GetFightTarget()=="freshmeat"?"checked":"")+" />Fresh Meat or UserIDs";
	controlHtml+="<textarea title='"+userIdInstructions+"' type='text' id='AutoZyngaFightTarget' rows='6'>"+this.GetFightTarget()+"</textarea>";
	controlHtml+="<br><input type='checkbox' id='AutoZyngaFightMonster'  "+(this.GetFightMonster()?"checked":"")+" />Fight Monster If Available"+
		"<br />";
	controlHtml+="<input type='checkbox' id='AutoZyngaDisabled' />Disable auto run for this game.<br />";
	
	this.SetControl(
		controlHtml
	);

	var waitTillFullBox=document.getElementById('WaitTillFull');
	var waitTillFull=this.GMGetValue('WaitTillFull',0);
	waitTillFullBox.checked=waitTillFull?true:false;
	waitTillFullBox.addEventListener('change',function(e) {
		Zynga.GMSetValue('WaitTillFull',e.target.checked?1:0);
	},false);

	var minToBank=document.getElementById('MinToBank');
	if(minToBank!=undefined) {
		minToBank.value=this.GetMinToBank().toString();
		minToBank.addEventListener('change',function(e) {
			Zynga.GMSetValue('MinToBank',e.target.value);
		},false);
	}
	var minInCash=document.getElementById('MinInCash');
	if(minInCash!=undefined) {
		minInCash.value=this.GetMinInCash().toString();
		minInCash.addEventListener('change',function(e) {
			Zynga.GMSetValue('MinInCash',e.target.value);
		},false);
	}

	var minToHospital=document.getElementById('MinToHospital');
	if(minToHospital!=undefined) {
		minToHospital.value=this.GetMinToHospital().toString();
		minToHospital.addEventListener('change',function(e) {
			Zynga.GMSetValue('MinToHospital',e.target.value);
		},false);
	}

	var disabledBox=document.getElementById('AutoZyngaDisabled');
	disabledBox.checked=this.GetGameDisabled()?true:false;
	disabledBox.addEventListener('change',function(e) {
		Zynga.GMSetValue('disabled',e.target.checked);
	},false);

/*	
	var divAtBottom=document.getElementById('DivAtBottom');
	divAtBottom.checked=this.GetDivAtBottom()?true:false;
	divAtBottom.addEventListener('change',function(e) {
		Zynga.GMSetValue('DivAtBottom',e.target.checked);
	},false);
*/

	var fightTarget=document.getElementById('AutoZyngaFightTarget');
	fightTarget.addEventListener('change',function(e) {
		Zynga.SaveFightTarget();
	},false);

	var fightMon=document.getElementById('AutoZyngaFightMonster');
	fightMon.addEventListener('change',function(e) {
		Zynga.GMSetValue('FightMonster',e.target.checked);
	},false);

	var fightTargetFM=document.getElementById('AutoZyngaFightTargetFM');
	fightTargetFM.addEventListener('change',function(e) {
		fightTarget.value=fightTargetFM.checked?"freshmeat":"";
		Zynga.SaveFightTarget();
	},false);

	var autoZyngaRestart=document.getElementById('AutoZyngaRestart');
	var autoZyngaPaused=document.getElementById('AutoZyngaPaused');
	autoZyngaRestart.addEventListener('click',function(e) {
		autoZyngaPaused.style.display='none';
		Zynga.currentTab = "";
		Zynga.waitMilliSecs = 3000;
		Zynga.WaitCheckPage();
		Zynga.ReloadOccasionally();
	},false);
	
	controlDiv.addEventListener('mousedown',function(e) {
		nHtml.clearTimeouts();
		autoZyngaPaused.style.display='block';
	},false);
	
},

SaveFightTarget:function() {
	var fightTarget=document.getElementById('AutoZyngaFightTarget');
	Zynga.GMSetValue('FightTarget',fightTarget.value);
},


VisitJobPage:function() {
	var content=document.getElementById('content');
	if(!content) { return false; }

	this.GMSetValue('GoJobs',0);
	// schoolofmagic: href=/actions
	// streetracing: onclick=jobs.php
	if(this.totalJobs>0) { 
		// we're already in the job page.
		return false; 
	}
	
	var a=nHtml.FindByAttrContains(content,'a','href','/actions');
	var useHref=true;
	if(!a && this.IsKidsGame()) {
		a=nHtml.FindByAttrXPath(content,'a',"contains(@href,'/jobs.php') or contains(@href,'/quests.php') or contains(@href,'/missions.php')");
		if(a) {
			useHref=true;
		}
	}
	if(!a) {
		// sometimes this is ajax sometimes it isn't.
		// ajax click: footballwars, streetracinggame, 
		// href click: gangwars, fashionwars, prison lockdown.
		if((a=nHtml.FindByAttrContains(content,'a','href','jobs.php'))) {
			//useHref=false;
		}
	}
	if(!a) {
		// castle_age
		if((a=nHtml.FindByAttrContains(content,'a','href','quests.php'))) {
			useHref=false;
		}
	}
	if(!a) {
		// specialforces, mafia wars
		if((a=nHtml.FindByAttrContains(content,'a','onclick','controller=job'))) {
			useHref=false;
		}
	}
	if(!a) {
		this.GMLog(' Could not find job link');
		return false;
	}
	this.GMLog('VisitJob'+useHref+","+a.href);
	this.waitMilliSecs=12000;
	nHtml.setTimeout(function() {
		if(useHref) {
			Zynga.VisitUrl(a.href);
		} else {
			Zynga.Click(a);
		}
	},2000);
	return true;
},

///////////////////////////////////////////////////////

SelectMaxProperties:function(row) {
	var selects=row.getElementsByTagName('select');
	if(selects.length<1) { return false; }
	
	var select=selects[0];
	select.selectedIndex=select.options.length-1;
	
	return true;
},

PropertiesGetNameFromRow:function(row) {
	// schoolofmagic, etc. <div class=item_title
	var infoDiv=nHtml.FindByAttrXPath(row,'div',"contains(@class,'land_buy_info') or contains(@class,'item_title')");
	if(!infoDiv) {
		this.GMLog("can't find land_buy_info");
	}
	if(infoDiv.className.indexOf('item_title')>=0) {
		return infoDiv.textContent.trim();
	}
	var strongs=infoDiv.getElementsByTagName('strong');
	if(strongs.length<1) {
		return null;
	}
	return strongs[0].textContent.trim();
},
IterateProperties:function(func) {
	var content=document.getElementById('content');
	var ss=document.evaluate(".//tr[contains(@class,'land_buy_row')]",content,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
	if (!ss || (ss.snapshotLength == 0)) {
		// this.GMLog("Can't find land_buy_row"); 
		return null;
	}
	var builtOnRe=new RegExp('(Built On|Consumes|Requires):\\s*([^<]+)','i');
	var propByName={};
	var propNames=[];

	var gameName=this.GameName();
	//this.GMLog('forms found:'+ss.snapshotLength);
	for(var s=0; s<ss.snapshotLength; s++) {
		var row=ss.snapshotItem(s);
		if(!row) { continue; }

		var div=nHtml.FindByAttrXPath(row,'div',"contains(@class,'AutoZynga_propDone')");
		if(div) { continue; }

		var name=this.PropertiesGetNameFromRow(row);

		if(name==null || name=='') { this.GMLog("Can't find property name"); continue; }

		var moneyss=document.evaluate(".//*[contains(@class,'gold') or contains(@class,'currency')]",row,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);

		if(moneyss.snapshotLength < 2) { this.GMLog("Can't find 2 gold instances"); continue; }

		div=document.createElement('div');
		div.className='AutoZynga_propDone';
		div.style.display='none';
		moneyss.snapshotItem(0).appendChild(div);

		var nums=[];
		var numberRe=new RegExp("([0-9,]+)");
		for(var sm=0; sm<moneyss.snapshotLength; sm++) {
			var income=moneyss.snapshotItem(sm);
			if(income.className.indexOf('label')>=0) {
				income=income.parentNode;
				var m=numberRe.exec(income.textContent);
				if(m && m.length>=2 && m[1].length>1) {
					// number must be more than a digit or else it could be a "? required" text
					income=this.NumberOnly(m[1]);
				} else {
					//this.GMLog('Cannot find income for: '+name+","+income.textContent);
					income=0;
					continue;
				}
			} else {
				income=this.NumberOnly(income.textContent);
			}
			nums.push(income);
		}

		var income=nums[0];
		var cost=nums[1];
		if(!income || !cost) {
			this.GMLog("Can't find income or cost for " + name);
			continue;
		}
		if(income>cost) {
			// income is always less than the cost of property.
			income=nums[1]; cost=nums[0];
		}

		var totalCost=cost;

		var prop={'row':row,'name':name,'income':income,'cost':cost,'totalCost':totalCost,'usedByOther':false};

		propByName[name]=prop;
		propNames.push(name);
	}

	for(var p=0; p<propNames.length;p++) {
		func.call(this,propByName[propNames[p]]);
	}
	return propByName;
},

BuyProperty:function(prop) {
	if(prop.builtOn!=null && prop.builtOn.builtOn==null) {
		// need to build something first.
		return this.BuyProperty(prop.builtOn);
	}
	var button=nHtml.FindByAttrXPath(row,'input',"@type='submit' or @type='image'");
	if(button) {
		this.waitMilliSecs=13000;
		this.Click(button);
		return true;
	}
	return false;
},

DrawProperties:function() {
	var bestProp={};
	var propByName=this.IterateProperties(function(prop) {
		this.SelectMaxProperties(prop.row);

		var div=document.createElement('div');
		div.style.display='inline';
		div.className='AutoZynga_costCalc';
		var roi=(parseInt((prop.income/prop.totalCost)*240000) /100);
		div.innerHTML=roi+"% per day.";

		if(!prop.usedByOther) {
			if(!bestProp.roi || roi>=bestProp.roi) {
				bestProp.roi=roi;
				bestProp.prop=prop;
			}
		}
		
		var infoDiv=nHtml.FindByAttrXPath(prop.row,'div',"contains(@class,'land_buy_info')");
		if(!infoDiv) {
			var st=prop.row.getElementsByTagName('td');
			if(st && st.length>=2) {
				infoDiv=st[1];
			}
		}
		if(!infoDiv) {
			this.GMLog("Cannot find place to insert property price");
			return;
		}
		infoDiv.insertBefore(div,infoDiv.childNodes[0]);
	});

	return null;
},
Properties:function() {
	var autoBuyProperty=this.GMGetValue('autoBuyProperty',0);
	if(autoBuyProperty) {
//~~~ Need to check for enough moneys + do we have enough of the builton type that we already own.
//		this.BuyProperty(bestProp.prop);
	}
	return null;
},

///////////////////////////////////////////////////////

VisitBankPage:function() {
	this.VisitTabPage('keep');	
	return true;
},

GetMinToBank:function() {
	var n=this.GMGetValue('MinToBank','');
	if(n==null || n=='') { return ''; }
	return parseInt(n);
},
GetMinInCash:function() {
	var n=this.GMGetValue('MinInCash','');
	if(n==null || n=='') { return 0; }
	return parseInt(n);
},



GetDepositForm:function() {
	var depositForm=nHtml.FindByAttr(document,'div','className','deposit_div');
	if(!depositForm) {
		// footballwars
		depositForm=nHtml.FindByAttrContains(document,'div','id','deposit_div');
	}
	if(!depositForm) {
		// castle_age
		depositForm=nHtml.FindByAttrContains(document,'form','id','bank_deposit');
	}
	if(!depositForm) {
		// robhood, etc.
		depositForm=nHtml.FindByAttrContains(document,'form','id','bankdeposit');
	}
	if(!depositForm) {
		depositForm=nHtml.FindByAttrContains(document,'form','action','/Deposit');
	}
	if(!depositForm) {
		depositForm=nHtml.FindByAttrContains(document,'form','onsubmit','action=deposit');
	}
	return depositForm;
},

Bank:function() {
	var minToBank=this.GetMinToBank();
	var minInCash=this.GetMinInCash();
	if(minToBank=="") {
		return null;
	}
	var depositForm=this.GetDepositForm();
	if(this.stats.cash<=minInCash || this.stats.cash<minToBank) {
		return null;
	}
	// football, spacewars, heroes&villians, fashionwars: href=bank.php, id=*deposit_div, type=submit
	// schoolofmagic: href=banking, form,action=deposit, type=submit
	// streetracing: stats.php, class=deposit_div, input,name=do
	// mafia wars: href...>...bank<, form,onsubmit.action=deposit, type=submit
	// specialforces: a,onclick=controller=bank, form,onsubmit.action=deposit, type=submit

	if(!depositForm) {
		// Cannot find the link
		this.VisitBankPage();
		return "GoDeposit";
	}
	var numberInput=nHtml.FindByAttrXPath(depositForm,'input',"@type='' or @type='text'");
	if(numberInput) {
		numberInput.value=parseInt(numberInput.value)-minInCash;
	} else {
		this.GMLog('Cannot find box to put in number for bank deposit.');
	}
	var depositButton=nHtml.FindByAttr(depositForm,'input','type','submit');
	if(!depositButton) {
		depositButton=nHtml.FindByAttr(depositForm,'input','name','do');
	}
	if(!depositButton) {
//		depositButton=nHtml.FindByAttr(depositForm,'input','type','submit');
		this.GMLog(' Cannot find deposit submit button');
		return null;	
	}
	this.GMLog('Deposit');
	this.Click(depositButton);
	return 'Deposit';	
},

///////////////////////////////////////////////////////
// Fight


GetFightMonster:function() {
	return this.GMGetValue('FightMonster',false);
},
GetFightTarget:function() {
	return this.GMGetValue('FightTarget',"");
},
VisitFightPage:function(force) {
	if(!force && this.IsFightPage()) { return false; }
	return this.VisitTabPage('battle');
},
VisitTabPage:function(tabname) {
	var content=document.getElementById('content');
	if (!content) { return false; }

	var pageLink=nHtml.FindByAttrContains(content,'a','href','/' + tabname + '.php');
	if (!pageLink) { 
		this.GMLog("Can't find page tab for " + tabname);
		return false; 
	}
	this.Click(pageLink);
	this.currentTab=tabname;
	return true;
},
userRe:new RegExp("(userId=|user=|/profile/)([0-9]+)"),
IterateFightLinks:function(func) {
	var content=document.getElementById('content');
	if(!content) { return; }
	var ss=document.evaluate(".//a[(contains(@href,'xw_controller=stats') and contains(@href,'xw_action=view')) "+
		"or (contains(@href,'/profile/'))"+
		"or (contains(@href,'/"+this.GameName()+"/profile.php?userId='))"+
		"or (contains(@href,'/stats.php?user='))"+
		"]",content,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
	for(var s=0; s<ss.snapshotLength; s++) {
		var userLink=ss.snapshotItem(s);
		if(userLink.innerHTML.indexOf('<img')>=0) { continue; }
		var m=this.userRe.exec(userLink.getAttribute('href'));
		if(!m) { continue; }
		var user=m[2];
		func.call(this,userLink,user);
	}
},
AddFightLinks:function() {
	if(document.getElementById('addFightLink')) { 
		return; 
	}
	this.IterateFightLinks(function(userLink,user) {
	if(nHtml.FindByAttr(userLink.parentNode,'a','class','addFight')) { return; }
		var addFight=document.createElement('a');
		addFight.className='addFight';
		addFight.id='addFightLink';
		addFight.innerHTML='(Auto Fight)';
		addFight.addEventListener('click',function() {
			var fightTarget=document.getElementById('AutoZyngaFightTarget');
			if(fightTarget.value=="freshmeat") { fightTarget.value=''; }
			if(fightTarget.value!="") { fightTarget.value+="\n"; }
			fightTarget.value+=user;
			Zynga.SaveFightTarget();
		},false);
		userLink.parentNode.insertBefore(addFight,userLink.nextSibling);
		userLink.parentNode.insertBefore(document.createTextNode(' '),userLink.nextSibling);
	});
},

FindFightForm:function(obj,withOpponent) {
	var ss=document.evaluate(".//form[contains(@onsubmit,'battle.php')]",obj,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
	var fightForm=null;
	for(var s=0; s<ss.snapshotLength; s++) {
		fightForm=ss.snapshotItem(s);
		
		// ignore forms in overlays
		var p=fightForm;
		while(p) {
			if (p.id && p.id.indexOf('verlay')>=0) {
				fightForm=null; break; 
			}
			p=p.parentNode;
		}
		if(!fightForm) {
			continue; 
		}
		
		var inviteButton=nHtml.FindByAttrXPath(fightForm,"input","(@type='submit' or @name='submit') and (contains(@value,'Invite') or contains(@value,'Notify'))");
		if(inviteButton) {
			// we only want "attack" forms not "attack and invite", "attack & notify"
			continue; 
		}

		var submitButton=nHtml.FindByAttrXPath(fightForm,"input","@type='image'");
		if(!submitButton) {
			// we only want forms that have a submit button
			continue; 
		}

		if(withOpponent) {
			var inp=nHtml.FindByAttrXPath(fightForm,"input","@name='target_id'");
			if(!inp) {
				continue; 
			} else {
				this.GMLog('inp.name is:' + inp.name);
			}
		}
		if(fightForm) { break; }
	}
	
	return fightForm;
},

fightLinkXPath:"(contains(@onclick,'xw_controller=fight') and contains(@onclick,'xw_action=attack')) "+
	"or contains(@onclick,'directAttack')"+
	"or contains(@onclick,'_fight_fight(')",
HasAttackButtons:function() {
	var fightForm=this.FindFightForm(document);

	if(!fightForm) {
		// inthemafia
		fightForm=nHtml.FindByAttrXPath(document,"a",this.fightLinkXPath);
	}
	if(!fightForm) {
		// castle_age
		fightForm=nHtml.FindByAttrXPath(document,"input","contains(@src,'battle_01')");
		if(!fightForm) {
			// castle_age
			fightForm=nHtml.FindByAttrXPath(document,"img","contains(@src,'battle_01')");
		}
	}
	return fightForm?true:false;
},

IsFightPage:function() {
	if(this.HasAttackButtons()) { return true; }
	var fightForm=nHtml.FindByAttrXPath(document,"table","contains(@class,'fight_table')");
	return fightForm?true:false;
},

opponentIdRe:new RegExp('opponent_id=([0-9]+)','ig'),
FightUserId:function(userid) {
	if(this.VisitFightPage()) { return 'fightpage'; }
	this.GMLog('Fight user:'+userid);
	
	var fightForm=this.FindFightForm(document,true);

	if(fightForm) {
		this.GMLog('Found fightForm');
		var inp=nHtml.FindByAttrXPath(fightForm,"input","@name='target_id'");
		if(!inp) {
			this.GMLog('no target_id in attack form');
			return null;
		}
		inp.value=userid;

		var button=nHtml.FindByAttrXPath(fightForm,"input","@type='image'");
		if(button) {
			this.GMLog('Fight user button:'+button.name);
			Zynga.Click(button);
		} else {
			this.GMLog('No submit button?:'+button.name);
			return null;
		}
		return 'fight';
	}

	return null;
},
rankTable:{'acolyte':0, 'scout': 1,'soldier': 2,'elite soldier': 3,'squire': 4,'knight': 5,'first knight': 6,'legionnaire': 7,'centurion': 8,'champion': 9,'lieutenant commander':10,'commander':11,'high commander':12,'lieutenant general':13,'general':14,'high general':15},
fightLevelRe:new RegExp('Level ([0-9]+)\\s*([A-Za-z ]+)','i'),
FightFreshmeat:function() {
	var ss=document.evaluate("//input[contains(@src,'battle_01.gif')]",document,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);

	if(ss.snapshotLength<=0) {
		if (this.currentPage == 'fightpage') {
			this.GMLog('No battle buttons on fightpage');
		}
		this.VisitFightPage(true);
		return "fightpage";
	}
	
	var bestButton;
	var bestScore = -10000;
	//this.GMLog("my army/rank/level:" + this.stats.army + "/" + this.stats.rank + "/" + this.stats.level);
	for(var s=0; s<ss.snapshotLength; s++) {
		var button=ss.snapshotItem(s);

		var tr=button;
		while(tr.tagName.toUpperCase()!="TR") {
			tr=tr.parentNode;
		}

		if(!button) {
			this.GMLog('No tr parent of button?');
			continue;
		}

		var txt=nHtml.GetText(tr);
		var levelm=this.fightLevelRe.exec(txt);
		if (!levelm) {
			this.GMLog("Can't match fightLevelRe in " + txt);
			continue;
		}	
		var level=parseInt(levelm[1]);
		var rankStr=levelm[2].toLowerCase().trim();
		var rankInt = this.rankTable[rankStr];
		var army = 0;
		
		var subtd=document.evaluate("td",tr,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
		if (!subtd) {
			this.GMLog("Can't find army subtd below " + txt);
		} else {
			army = parseInt(nHtml.GetText(subtd.snapshotItem(2)).trim());
		}
		
		if (army == 0) {
			this.GMLog("Battle table changed... can't find army size");
		}
		
		// if we know our rank, and this guy's higher, don't fight
		if (this.stats.rank && (rankInt > this.stats.rank)) {
			continue;
		}

		// scale up the level reqs if the rank is high
		var levelDiff = 3;		
		if (this.stats.rank == rankInt) {
			levelDiff = 1;
		} else if (this.stats.rank == (rankInt+1)) {
			levelDiff = 2;
		}

		// if we know our level, and this guy's higher level by 2, don't fight
		if (this.stats.level && level > (this.stats.level+levelDiff)) {
			continue;
		}

		// scale down the army ratio if the rank is high
		var armyRatio = 1.7;		
		if (this.stats.rank == rankInt) {
			armyRatio = 1.0;
		} else if (this.stats.rank == (rankInt+1)) {
			armyRatio = 1.4;
		}
		
		// if we know our army size, and this one's bigger, not safe
		if (this.stats.army && (army > (this.stats.army*armyRatio))) {
			continue;
		}

		// passed safety tests... so pick a good fight
		// try to create a score which chooses a good battle-ranked payer while also
		// somewhat prioritizing an safer fight
		var thisScore = rankInt-((army/armyRatio)/this.stats.army);
		if (thisScore > bestScore) {
			//this.GMLog("best army/rank/level:" + army + "/" + rankInt + "/" + level);
			bestScore = thisScore;
			bestButton = button;
		}
	}

	if (bestButton != null) {
		this.GMLog('Found freshmeat score ' + bestScore);
		this.Click(bestButton);
		return 'fight';
	}
	
	this.GMLog('No safe targets.');
	this.VisitFightPage(true);
	this.waitMilliSecs=13000;
	return null;
},
Fight:function() {
	this.SetFightMessage("");
	var lastFight=this.GMGetValue('LastFight','').split(',');
	if(lastFight.length>1) {
		if(this.stats.stamina.num == lastFight[0]) {
			// we have the same health as before so the fight did not work.
			var nextFight=parseInt(lastFight[1])+(1*20*1000);
			var now=(new Date().getTime());
			if(now<nextFight) {
				this.SetFightMessage("<font style='font-size: 16pt'>No stamina lost in last fight, maybe the user is dead, let's wait "+Math.floor((nextFight-now)/1000)+" secs</font>");
				// not ready for the next fight yet.
				return null;
			}
		}
	}
	var f=this.DoFight();
	if(f=='fight') {
		// we fought someone, let's record it.
		this.GMSetValue('LastFight',this.stats.stamina.num+","+(new Date().getTime()).toString());
		this.waitMilliSecs=13000;
	} else {
		this.GMSetValue('LastFight','');
	}
	return f;
},

NextFightTarget:function() {
	var fightUpto=this.GMGetValue('FightTargetUpto',0);
	this.GMSetValue('FightTargetUpto',fightUpto+1);
},
GetCurrentFightTarget:function() {
	var target=this.GetFightTarget();
	if(target=="") { return ""; }
	var targets=target.split(/[\n,]/);
	var fightUpto=this.GMGetValue('FightTargetUpto',0);
	fightUpto=fightUpto%targets.length;
	this.GMLog('nth fight target:'+fightUpto+':'+targets[fightUpto]);
	return targets[fightUpto];
},
DoFight:function() {
	if(!this.stats.stamina) {
		return null;
	}
	if(!this.stats.health) {
		return null;
	}
	if(this.stats.stamina.num<=0) {
		this.SetFightMessage("Waiting for more stamina");
		// drained to 0
		this.GMSetValue('WaitWasFull',0);
		return null;
	}
	if(this.stats.health.num<=20) {
		this.SetFightMessage("Need at least 20 health to fight");
		return null;
	}
	if(!this.IsEnoughStaminaForAutoFight()) {
		this.SetFightMessage("Waiting for more stamina");
		return null;
	}

	if(this.GetFightMonster()) {
		var button;
		
		if (this.stats.stamina.num>=5) {
			// power attack
			button = nHtml.FindByAttrContains(document.body,"input","src","attack_monster_button2.jpg");
			if (button) {
				this.GMLog("Attack monster power");
				Zynga.Click(button);
				return "monster2";
			}
		}
		
		button = nHtml.FindByAttrContains(document.body,"input","src","attack_monster_button.jpg");
		if (button) {
			// regular attack
			this.GMLog("Attack monster");
			Zynga.Click(button);
			return "monster1";
		}


		var pref = this.GMGetValue('PreferMonsterBy','');
		if (pref) {
		// search for ENGAGE buttons with HREF's under them in the list of id's specified
			var ss=document.evaluate(".//img[contains(@src,'dragon_list_btn_3')]",table,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);		
			for(var s=0; s<ss.snapshotLength; s++) {
				var img=ss.snapshotItem(s);
				var tb=img.parentNode;
				if (tb.tagName.toUpperCase()!="A") {
					continue;
				}
				var p;
				var id;
				if ((p=indexOf(tb.src,'userid=')) > 0) {
					id = substring(tb.src, p);
					id.replace(/[^0-9].*/,"");
					if (indexOf(pref,id)>=0) {
						this.GMLog("Attack userid " + id);
						Zynga.Click(tb);
						return "monster3";
					}
				}
			}
		}

		// search for ENGAGE button specifically, not "collect reward" button... user can do that manually
		var img = nHtml.FindByAttrContains(document.body,"img","src","dragon_list_btn_3");
		if (img) {
			button=img.parentNode;
			if (button.tagName.toUpperCase()!="A") {
				this.GMLog("ERROR: parent node of engage button is " + button.tagName);
				button = null;
			}
		}
		
		if (button) {
			this.GMSetValue("MonsterAvailable", true);
			this.GMLog("Engage monster");
			Zynga.Click(button);
			this.currentTab = "engage";
			return "mbattle";
		} else {
			if (this.currentTab == "battle_monster") {
				this.GMSetValue("MonsterAvailable", false);
				this.GMLog("No engage button");
			}
		}
		
		if (this.currentTab != "battle_monster") {
			var now = (new Date().getTime());
			this.GMSetValue("MonsterLastCheck", now.toString());
			if (this.VisitTabPage('battle_monster')) {
				this.GMLog("Search for monster " + this.currentTab);
				return "battle_monster";
			}
		}
	}
	
	var target=this.GetCurrentFightTarget();
	if(target=="") { this.NextFightTarget(); return null; }

	this.GMLog('fighting ' + target);
	this.waitMilliSecs=13000;
	if(target!='freshmeat') {
		var f=this.FightUserId(target);
		if (f) {
			this.currentTab = f;
		}
		this.NextFightTarget();
		return f;
	}
	var f = this.FightFreshmeat();
	if (f) {
		this.currentTab = f;
	}
	return f;
},


///////////////////////////////////////////////////////
// hospital

GetMinToHospital:function() {
	var n=this.GMGetValue('MinToHospital','');
	if(n==null || n=='') { return ''; }
	return parseInt(n);
},

Hospital:function() {
	this.SetHospitalMessage("");
	var minToHospital=this.GetMinToHospital();
	if(minToHospital=="") {
		return null;
	}
	var health=this.stats.health;
	if(!health) { return null; }
	var lastHealthOnHeal=this.GMGetValue('LastHealthOnHeal',0);
	if(lastHealthOnHeal==health.num) {
		// nothing happened on 
		this.SetHospitalMessage("Health did not change after the last heal, maybe trying to heal too fast.");
		return null;
	}
	if(health.num>=health.max || health.num>minToHospital) {
		return null;
	}
	
	var content=document.getElementById('content');
	// mafia wars
	var healLink=nHtml.FindByAttrXPath(content,"a","contains(@onclick,'controller=hospital') and contains(@onclick,'action=heal')");
	if(!healLink) {
		// heal button castle age, gangwars, dragonwars, glamour_age
		var healForm=nHtml.FindByAttrXPath(content,"form","contains(@id,'_healForm') or contains(@id,'_hospital') or contains(@action,'hospital.php')");
		if(!healForm) {
			// robhood, etc.
			healForm=nHtml.FindByAttrXPath(content,'input',"@name='op' and @value='heal'");
			if(healForm) healForm=healForm.parentNode;
		}
		
		if(!healForm) {
			// street racing
			healForm=nHtml.FindByAttrXPath(content,'input',"contains(@name,'healthRefillNowBtn')");
			if(healForm) {
				while(healForm && healForm.tagName!="BODY") {
					if(healForm.tagName=="FORM") { break; }
					healForm=healForm.parentNode;
				}
				if(healForm.tagName!="FORM") {
					healLink=null;
				}
			}
		}
		var favor=null;
		if(healForm) favor=nHtml.FindByAttrXPath(healForm,'input',"@name='favor'");
		if(favor) {
			// don't spend favor points!
			healLink=healForm=null;
			this.GMLog("Will not heal cause we've found the wrong link.");
		} else if(healForm) {
			healLink=nHtml.FindByAttrXPath(healForm,"input","@type='button' or @type='submit' or @type='image'");
		}
	}
	if(!healLink) {
		return this.VisitHospitalPage();
	}
	this.GMLog('Heal');
	this.GMSetValue('LastHealthOnHeal',health.num);
	this.Click(healLink);
	this.waitMilliSecs=10000;
	return 'hospital';
},

VisitHospitalPage:function() {
	this.GMLog('Visit hospital');
	var content=document.getElementById('content');
	var hospitalLink=nHtml.FindByAttrXPath(content,"a","contains(@onclick,'keep.php')");
	if(!hospitalLink) {
		this.GMLog('Cannot find hospital link');
		return null;
	}
	this.waitMilliSecs=10000;
	this.Click(hospitalLink);
	return 'visithospital';
},



///////////////////////////////////////////////////////


GetGameDisabled:function() {
	return this.GMGetValue('disabled',false);
},
GetDivAtBottom:function() {
	return this.GMGetValue('DivAtBottom',false);
},
WaitCheckPage:function() {
	this.waitForPageChange=true;
	nHtml.setTimeout(function() { this.waitForPageChange=false; Zynga.CheckPage(); },this.waitMilliSecs);

},
currentPage:"",
currentTab:"",
waitMilliSecs:5000,
CheckPage:function() {
	// assorted errors...
	var href=location.href;
	if(href.indexOf('/common/error.html')>=0) {
		this.GMLog('detected error page, waiting to go back to previous page.');
		window.setTimeout(function() {
			window.history.go(-1);
		}, 30*1000);
		return;
	}

	if(document.getElementById('try_again_button')) {
		this.GMLog('detected Try Again message, waiting to reload');
		// error
		window.setTimeout(function() {
			window.history.go(0);
		}, 30*1000);
		return;
	}

	// this.GMLog('castle game loop');
	var forceReload=false;
	var gameDisabled=this.GetGameDisabled();
	this.SetupDivs();
	this.AddFightLinks();
	if(!gameDisabled) {
		var ok="";
		this.waitMilliSecs=fastLoad?1000:5000;
		var goJobs=this.GMGetValue('GoJobs',0);
		if(goJobs) {
			this.GMLog('go to the jobs page cause we have reloaded');
			this.VisitJobPage();
			return;
		}

		this.GetRankStat();	

		var now=(new Date().getTime());
		if (!this.GMGetValue('MyRankLast') || (parseInt(this.GMGetValue('MyRankLast')) < (now-1000*60*60))) {
			this.GMLog('getting new rank');
			this.VisitTabPage('keep');
			this.WaitCheckPage();
			return;
		}		
		
		this.GetStats();
		this.SetControls();
		this.DrawProperties();

		if((ok=this.Jobs(false))!=null) {
		} else if((ok=this.Properties())!=null) {
		} else if((ok=this.Hospital())!=null) {
		} else if((ok=this.Bank())!=null) {
		} else if((ok=this.Fight())!=null) {
		}

		//this.GMLog('ok:'+ok);
		if(ok && ok.length>0) {
			this.currentPage=ok;
		} else {
			forceReload=true;
		}
	} else {
		this.GetStats();
		this.SetControls();
		this.DrawJobs(null,true);
		this.DrawProperties();
	}
	
	if (forceReload) {
	}

	this.WaitCheckPage();
},


/*
PageChange:function() {
	if(this.waitForPageChange) {
		this.waitForPageChange=false;
		// wait for all the ajax stuff to finish...
		window.setTimeout(function() { Zynga.CheckPage(); },2000);
	}
}
*/

ReloadOccasionally:function() {
	nHtml.clearTimeouts();	
	nHtml.setTimeout(function() {
		// better than reload... no prompt on forms!
		window.location = "http://apps.facebook.com/castle_age/index.php";
		this.currentTab = "index";
		Zynga.ReloadOccasionally();
	}, 1000*60*4 + (3 * 60 * 1000 * Math.random()));
}

};

if(fastLoad) {
	Zynga.CheckPage();
}

window.addEventListener("load", function(e) {
	if(!fastLoad) {
		Zynga.CheckPage();
	}
},false);

Zynga.ReloadOccasionally();

