KOCAttack - Extra Features!

Same as the original Kingdoms of Camelot Attack script, but with extra features.

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name             KOCAttack - Extra Features!
// @version          0.9.7.4
// @namespace        KOCAttack-Extra
// @homepage         http://userscripts.org/scripts/show/89473
// @description      Same as the original Kingdoms of Camelot Attack script, but with extra features.

// @include          *apps.facebook.com/kingdomsofcamelot*
// @include          *kingdomsofcamelot.com/*main_src.php*
// @include          *kingdomsofcamelot.com/*newgame_src.php*
// @include          *facebook.com/connect/uiserver.php*
// @include          *kingdomsofcamelot.com/*standAlone.php*

// ==/UserScript==


var KOCAversion = '0.9.7.4';

// Override the default alert functionality of the web browser (which causes the script to pause)
// Instead of displaying alert popups, messages will be displayed in the firefox console
unsafeWindow.alert = function(message) {
	console.info("Javascript Alert: "+message);
	if(typeof(GM_log)=="function"){
		GM_log("Javascript Alert: "+message);
	}
}
alert = unsafeWindow.alert;

// String prototypes
String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g, ''); }
String.prototype.StripQuotes = function() {
	return this.replace(/"/g,'');
};


// Quick is_int function for javascript
function is_int(value){
  if((parseFloat(value) == parseInt(value)) && !isNaN(value)){
      return true;
  } else {
      return false;
  }
}

/*
// Array remove function (found at http://ejohn.org/blog/javascript-array-remove/)
	Examples:
	Remove the second item from the array:
		ArrayRemoveItem(array, 1);
	Remove the second-to-last item from the array:
		ArrayRemoveItem(array, -2);
	Remove the second and third items from the array:
		ArrayRemoveItem(array, 1,2);
	Remove the last and second-to-last items from the array:
		ArrayRemoveItem(array, -2,-1);
*/
ArrayRemoveItem = function(array, from, to) {
  var rest = array.slice((to || from) + 1 || array.length);
  array.length = from < 0 ? array.length + from : from;
  return array.push.apply(array, rest);
};

if(!this.JSON2){JSON2={};}
(function(){function f(n){return n<10?'0'+n:n;}
if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return this.getUTCFullYear()+'-'+
f(this.getUTCMonth()+1)+'-'+
f(this.getUTCDate())+'T'+
f(this.getUTCHours())+':'+
f(this.getUTCMinutes())+':'+
f(this.getUTCSeconds())+'Z';};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};}
var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}
function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}
if(typeof rep==='function'){value=rep.call(holder,key,value);}
switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}
gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}
v=partial.length===0?'[]':gap?'[\n'+gap+
partial.join(',\n'+gap)+'\n'+
mind+']':'['+partial.join(',')+']';gap=mind;return v;}
if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==='string'){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}
v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+
mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}
if(typeof JSON2.stringify!=='function'){JSON2.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}
rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}
return str('',{'':value});};}
if(typeof JSON2.parse!=='function'){JSON2.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}
return reviver.call(holder,key,value);}
cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+
('0000'+a.charCodeAt(0).toString(16)).slice(-4);});}
if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}
throw new SyntaxError('JSON.parse');};}})();

if(!this.GM_log) {
	GM_log=function(m) {
		console.log(m);
	}
	GM_registerMenuCommand=function(text,f) {
	}
	
}

if(!this.unsafeWindow) {
//~~~ need helper to return values?
	unsafeWindow={};
}


function inspect(obj, maxLevels, level){
  var str = '', type, msg;

    // Start Input Validations
    // Don't touch, we start iterating at level zero
    if(level == null)  level = 0;

    // At least you want to show the first level
    if(maxLevels == null) maxLevels = 1;
    if(maxLevels < 1)     
        return '<font color="red">Error: Levels number must be > 0</font>';

    // We start with a non null object
    if(obj == null)
    return '<font color="red">Error: Object <b>NULL</b></font>';
    // End Input Validations

    // Each Iteration must be indented
    str += '<ul>';

    // Start iterations for all objects in obj
    for(property in obj)
    {
      try
      {
          // Show "property" and "type property"
          type =  typeof(obj[property]);
          str += '<li>(' + type + ') ' + property + 
                 ( (obj[property]==null)?(': <b>null</b>'):(': '+obj[property])) + '</li>';

          // We keep iterating if this property is an Object, non null
          // and we are inside the required number of levels
          if((type == 'object') && (obj[property] != null) && (level+1 < maxLevels))
          str += inspect(obj[property], maxLevels, level+1);
      }
      catch(err)
      {
        // Is there some properties in obj we can't access? Print it red.
        if(typeof(err) == 'string') msg = err;
        else if(err.message)        msg = err.message;
        else if(err.description)    msg = err.description;
        else                        msg = 'Unknown';

        str += '<li><font color="red">(Error) ' + property + ': ' + msg +'</font></li>';
      }
    }

      // Close indent
      str += '</ul>';

    return str;
}

function MinDigits(num,digits) {
        while((""+num).length<digits) {
                num="0"+num;
        }
        return num;
};
function SecsToStr(secs) {
		secs=Math.floor(secs);
		
        return 	Math.floor(secs/60/60/24%60)+
			":"+MinDigits(Math.floor(secs/60/60%60),2)+
			":"+MinDigits(Math.floor(secs/60%60),2);
			//	":"+MinDigits(Math.floor(secs%60),2);
};


var nHtml={
FindByXPath:function(obj,xpath,nodetype) {
	if(!nodetype){
		nodetype = XPathResult.FIRST_ORDERED_NODE_TYPE;
	}
	try {
		var q=document.evaluate(xpath,obj,null,nodetype,null);
	} catch(e) {
		GM_log('bad xpath:'+xpath);
	}
	if(nodetype == XPathResult.FIRST_ORDERED_NODE_TYPE){
		if(q && q.singleNodeValue) { return q.singleNodeValue; }
	}else{
		if(q){
			return q;
		}
	}
	return null;
},
ClickWin:function(win,obj,evtName) {
	var evt = win.document.createEvent("MouseEvents");
	evt.initMouseEvent(evtName, true, true, win,
		0, 0, 0, 0, 0, false, false, false, false, 0, null);
	return !obj.dispatchEvent(evt);
},
Click:function(obj) {
	return this.ClickWin(window,obj,'click');
},
ClickTimeout:function(obj,millisec) {
	window.setTimeout(function() {
		return nHtml.ClickWin(window,obj,'click');
	},millisec+Math.floor(Math.random()*500));
},

SetSelect:function(obj,v) {
	for(var o=0; o<obj.options.length; o++) {
		if(v==obj.options[o].value) { obj.options[o].selected=true; return true; }
	}
	return false;
}

};

function ById(id) {
	return document.getElementById(id);
}

function ByName(name) {
	return document.getElementsByName(name);
}

function AddText(box1,txt) {
	var txtObj;
	box1.appendChild(txtObj=document.createTextNode(txt));
	return txtObj;
}

function AddHtml(box1,txt) {
	var txtObj;
	var sp=document.createElement('span');
	sp.innerHTML=txt;
	box1.appendChild(sp);
	return txtObj;
}

function getAttackTypeSelected (){
  if (document.getElementById('modal_attack_tab_4').className == 'selected')  // attack
    return 0;
  if (document.getElementById('modal_attack_tab_1').className == 'selected')  // transport
    return 1;
  if (document.getElementById('modal_attack_tab_3').className == 'selected')  // scout
    return 2;
  if (document.getElementById('modal_attack_tab_2').className == 'selected')  // reinforce
    return 3;
  if (document.getElementById('modal_attack_tab_5').className == 'selected')  // reassign
    return 4;
	return null;
}

var KOCAttack={
	startListenTime:null,
	prevAttack:{'x':"350",'y':'350'},
	options:null,
	iframeCommunicator:{},
	isChrome:navigator.userAgent.toLowerCase().indexOf('chrome') > -1,
	valuesCache:{},
	seed:{},
	currentPage:null,

	DoUnsafeWindow:function(func, execute_by_embed) {
		if(this.isChrome || execute_by_embed) {
			var scr=document.createElement('script');
			scr.innerHTML=func;
			document.body.appendChild(scr);
		} else {
			try {  
				eval("unsafeWindow."+func);
			} catch (error) {
				this.Log("A javascript error has occurred when executing a function via DoUnsafeWindow. Error description: "+error.description);
			}
		}
	},

	GetSeed:function() {
		return unsafeWindow.seed;
	},
	
	ShowImportExportBox:function() {
		var div=ById('ImportExportBoxDiv');
		if(!div) {
			div=document.createElement('div');
			div.id='KOCAttackViewAttacks';
			div.style.zIndex=100000;
			div.style.position='absolute';
			div.style.left='8px';
			div.style.top='8px';
			div.style.backgroundColor='#fff';
			div.style.border='3px solid #888';
			div.style.padding='10px';
			document.body.appendChild(div);
		}
		
		div.innerHTML = '';
		
		var m = '<a id="ImportBoxCancel" class="button20"><span>Close</span></a>';
		m += '<a id="ImportData" class="button20"><span>Import</span></a>';
		m += '<a id="ExportData" class="button20"><span>Export</span></a>&nbsp;';
		
		var cities=this.GetSeed().cities;
		//WinLog.write(inspect(cities));
		var citysel ='<select id=srcCity>';
		//citysel += '<option value=All>All Cities</option>';
		for(var c=0; c<cities.length; c++) {
			citysel += '<option value="'+cities[c][0]+'">'+cities[c][1]+'</option>';
		}
		citysel += '<option value=options>Options</option>';		
		citysel += '</select>';
		
		m += citysel;
		m += '<br><textarea id="ImportExportArea" rows=25 cols=60></textarea>';
		div.innerHTML = m;
		
		ById('ImportBoxCancel').addEventListener('click',function() {
		  div.innerHTML = '';
		  div.style.display='none';
		},false);
		
		ById('ImportData').addEventListener('click',function() {

		},false);
		
		ById('ExportData').addEventListener('click',function() {
		  var x;
    	  var names=KOCAttack.browser_listValues();
		  var obj={};
		  var serverId=KOCAttack.GetServerId();
		  for(var n=0; n<names.length; n++) {	
		    var name=names[n];
			var a = name.substring(0,11);
			var b = 'attack_'+serverId+'_';			
			if (a==b ) {
  		      var sid=KOCAttack.GetServerIdFromName(name);
		      if(sid!=serverId) continue;
		      var v=KOCAttack.browser_getValue(name,null);
			  if(v!=null && v!=undefined && v!=""){			  
			    x = JSON2.parse(v);
			    if (x.fromCity == ById('srcCity').value) {
			      obj[name]=v;
			    }
		      }
			}
			else if (ById('srcCity').value == 'options') {
  		      var sid=KOCAttack.GetServerIdFromName(name);
		      if(sid!=serverId) continue;
		      var v=KOCAttack.browser_getValue(name,null);
			  if(v!=null && v!=undefined && v!=""){			  
				obj[name]=v;
			   }
			}
		  }
		  ById('ImportExportArea').value = JSON2.stringify(obj);
		},false);			
	
	},	
	
	ShowViewAttacksDialog:function() {
		var t=this;
		t.attacks=[];
		var div=ById('KOCAttackViewAttacks');
		if(!div) {
			div=document.createElement('div');
			div.id='KOCAttackViewAttacks';
			div.style.zIndex=100000;
			div.style.position='absolute';
			div.style.left='8px';
			div.style.top='8px';
			div.style.backgroundColor='#fff';
			div.style.border='3px solid #888';
			div.style.padding='10px';
			document.body.appendChild(div);
		}
		var cities=this.GetSeed().cities;
		//WinLog.write(inspect(cities));
		var citysel ='<select id=srcCity>';
		citysel += '<option value=All>All Cities</option>';
		for(var c=0; c<cities.length; c++) {
			citysel += '<option value="'+cities[c][1]+'">'+cities[c][1]+'</option>';
		}		
		citysel += '</select>';
		
		var levelsel ='<select id=srcLevel>';
		levelsel += '<option value="All">All Levels</option>';
		for(var c=1; c<=11; c++) {
			levelsel += '<option value="'+c+'">Level '+c+'</option>';
		}
		levelsel += '</select>';
		
		var typesel ='<select id=srcAttackType>';
		typesel += '<option value="All">All</option>';
		typesel += '<option value="normal">Attacks</option>';
		typesel += '<option value="transport">Transports</option>';
		typesel += '<option value="bulkadded">Bulk Adds</option>';
		typesel += '</select>';
		
		div.style.display='block';
		div.innerHTML='';
		var m = '<DIV id="srcAttackOpts" style="height:30px">\
		<TABLE><TR valign=bottom><TD class=xtab width=100 align=center>Search for: </td><TD align=left>\
		<SELECT id=srcAttack>\
		<OPTION value=All>All</option>\
		<OPTION value=Camp>Barb Camp</option>\
		<OPTION value=Wilderness>Wilderness</option>\
		<OPTION value=Grassland>Grassland</option>\
		<OPTION value=Lake>Lake</option>\
		<OPTION value=Mountains>Mountains</option>\
		<OPTION value=Forest>Woods</option>\
		<OPTION value=Hills>Hills</option>\
		<OPTION value=Plain>Plains</option>\
		<OPTION value=City>City</option>\
		<OPTION value=Transport>Transport</option>\
		<OPTION value=Unknown>Unknown</option>\
		</select></td>\
		<td class=xtab width=100 align=center>Select City: &nbsp; </td>\
		 <td align=left><span id=ptattackcity></span></td>\
		 <td class=xtab width=100 align=center>Select Level: &nbsp; </td>\
		 <td align=left><span id=ptattacklevel></span></td>\
		 <td class=xtab width=100 align=center>Select Type: &nbsp; </td>\
		 <td align=left><span id=ptattacktype></span></td></tr>\
		</table></div>\
		<a id="KOCAttackViewAttacksCancel" class="button20"><span>Close</span></a>\
		<a id="KOCAttackViewAttacksList" class="button20"><span>List Attacks</span></a>\
		<a id="KOCAttackViewAttacksClearList" class="button20"><span>Clear List</span></a>\
		<a id="KOCAttackViewAttacksDelete" class="button20"><span>Delete Selected</span></a>\
		<br><br><DIV id="srcAttackResults" style="height:470px; max-height:470px; overflow-y:auto;"></div>\
		';
		
		//<a id="KOCAttackViewAttacksImportExport" class="button20"><span>Import / Export</span></a>\	
		
		var srcAttackResults = ById("srcAttackResults");
		if (srcAttackResults != null){
		  ById('KOCAttackViewAttacksClearList').click();
		}		
		div.innerHTML = m;
		ById('ptattackcity').innerHTML = citysel;
		ById('ptattacklevel').innerHTML = levelsel;
		ById('ptattacktype').innerHTML = typesel;

		ById('KOCAttackViewAttacksClearList').addEventListener('click',function() {
			ById('srcAttackResults').innerHTML='';
		},false);										
		
		ById('KOCAttackViewAttacksCancel').addEventListener('click',function() {
			div.style.display='none';
		},false);
		
		//ById('KOCAttackViewAttacksImportExport').addEventListener('click',function() {
		//	t.ShowImportExportBox();
		//},false);
				
		ById('KOCAttackViewAttacksList').addEventListener('click',function() {
		  t.attacks=[];
		  t.DetailAttacks();
		  //WinLog.write(inspect(t.attacks,10));
		  
		  var typeQuery = ById('srcAttackType').value;
		  var cityQuery = ById('srcCity').value;
		  var levelQuery = ById('srcLevel').value;
		  var attackQuery = ById('srcAttack').value;
		  
		  var h = '<table>';
		  h += '<tr><td><input type=checkbox id=selAllAttacks></td><td>&nbsp;</td><td>City</td><td>Coords</td><td nowrap>What</td>';
		  h += '<td>Type</td><td>Attack Troops</td><td>Suicide Troops/Resources</td><td>Dist</td><td>Time</td><td>Ignore</td></tr>';
		  var tableRows= '';
		  var count = 1
		  for(var a=0; a<t.attacks.length; a++) {
			var levelInfo=t.GetLevelInfo(t.attacks[a]);
			if (levelInfo==undefined) levelInfo='';
			var type;
			if (levelInfo.type){
			  type = levelInfo.type;
			} else {
			  type = 'Unknown';
			}

			var displayRow = '';
			if (cityQuery != 'All'){
			  if (t.GetCityName(t.attacks[a]['fromCity']) != cityQuery){
			    continue;
			  }
			}
			if (levelQuery != 'All'){
			  if (levelInfo.level != levelQuery){
			    continue;
			  }
			}
			
			if (typeQuery != 'All'){
			  if (t.attacks[a].currenttattackwavetype != typeQuery){
			    continue;
			  }
			}			
			if (attackQuery != 'All'){
			  if (type != attackQuery){
			    continue;
			  }
			}					
			tableRows += '<tr id=row'+count+' style="display: '+displayRow+';">';
			tableRows += '<td><input type=checkbox id=aasel'+count+'>';
			tableRows += '<td>'+count+'</td><td>'+t.GetCityName(t.attacks[a]['fromCity'])+'</td>';
			tableRows += '<td id=aacoords'+count+' onclick="aaGotoMapHide('+t.attacks[a].xy[0]+','+t.attacks[a].xy[1]+')">'+t.attacks[a].xy[0]+','+t.attacks[a].xy[1]+'</td>';

			tableRows += '<td nowrap>'+type+' Level '+levelInfo.level+'</td>';
			tableRows += '<td>'+t.attacks[a].currenttattackwavetype+'</td>';

            		tableRows += '<td>';
			
			if(typeof(t.attacks[a].troops)=="object") {
			  var troops='';
			  for(var i=1; i<t.attacks[a].troops.length; i++) {
			    troops += t.attacks[a].troops[i]+',';
   			}
			  troops = troops.substring(0, troops.length - 1);
			  tableRows += troops;
  		}
			else{
			  tableRows += '&nbsp;';
			}
      tableRows += '</td>';

      tableRows += '<td>';

			if(typeof(t.attacks[a].suicidewave)=="object") {
			  var suicide='';
			  for(var i=1; i<t.attacks[a].suicidewave.length; i++) {
			    suicide += t.attacks[a].suicidewave[i]+',';
   			}
			  suicide = suicide.substring(0, suicide.length - 1);
			  tableRows += suicide;
			}
			else if (typeof(t.attacks[a].resources)=="object"){
			  var resources='';
			  for(var i=1; i<t.attacks[a].resources.length; i++) {
			    resources += t.attacks[a].resources[i]+',';
   			}
			  resources = resources.substring(0, resources.length - 1);
			  tableRows += resources;
			}
			else {
			  tableRows += '&nbsp;';
			}
      tableRows += '</td>';
      
 	  var distance=t.CalcXYDist({'x':t.attacks[a].xy[0],'y':t.attacks[a].xy[0]},{'x':t.GetCityCoordsX(t.attacks[a]['fromCity']),'y':t.GetCityCoordsX(t.attacks[a]['fromCity'])});
	  tableRows += '<td>'+parseInt(distance)+'</td>';
	  
			var nowSecs=new Date().getTime()/1000;
			tableRows += '<td>'+SecsToStr(nowSecs-t.attacks[a].time)+'</td>';
			
			var ignChecked = '';
			if (t.attacks[a].ignore == null || t.attacks[a].ignore==undefined){
			  ignChecked = '';  
			}else{
			  ignChecked = 'CHECKED';
			}
			
			tableRows += '<td><input type=checkbox name=chkIgnore id='+count+' '+ignChecked+'></td>';
		 
			tableRows += '</tr>';	
		    	count++;
		}
		  
		  h += tableRows + '</table>';
		  ById('srcAttackResults').innerHTML = h;

          ById('KOCAttackViewAttacksDelete').addEventListener('click',function() {
		    var deletes = 0;
			for (var i=1; i<count; i++){
			  var row = 'aasel'+i;
			  if (ById(row) == undefined) continue;
			  if (ById(row).checked == true){
			    //ById(row).checked == false;
			    var c = ById('aacoords'+i).innerHTML;
			    var xy = c.split(",");
			    GM_log('Coords '+xy[0]+' '+xy[1]);
			    t.DeleteAttack(xy[0],xy[1]);
				deletes++;
			  }
			}
			window.alert(deletes+' of coords deleted');
			// var listBtn=ById('KOCAttackViewAttacksList');
			// nHtml.Click(listBtn);
		  },false);
		  
		  for (var i=1; i<count; i++){
		    var ignore = i;
			t.addEvent(ById(ignore), "click", t.ChangeIgnore);
		  }	
		  
		  		  	  
		  ById('selAllAttacks').addEventListener('click',function() {
		    var myChecked = true;
			
			if (ById('selAllAttacks').checked == false){
			  myChecked = false;
			}
			
			for (var i=1; i<count; i++){
			  var row = 'aasel'+i;
			  ById(row).checked=myChecked;
			}
    	  },false);	
		    
 	   },false);
	},
	
	addEvent:function(obj, type, fn){
	  if (obj.attachEvent) {
	    obj['e' + type + fn] = fn;
        obj[type + fn] = function(){obj['e' + type + fn](window.event);}
        obj.attachEvent('on' + type, obj[type + fn]);
      } 
      else{
         obj.addEventListener(type, fn, false);
      }	
	},
	
	ChangeIgnore:function(e){
	  var c = ById('aacoords'+e.target.id).innerHTML;
	  var xy = c.split(",");
	  var serverID = KOCAttack.GetServerId();
	  	  
	  var attackname = 'attack_'+serverID+'_'+xy[0]+','+xy[1];
	  var str = GM_getValue(attackname,'') 

	  if(!str) return null;
	  attack= JSON2.parse(str);
	  attack.ignore=e.target.checked?true:undefined;
	  //WinLog.write (inspect(attack,10));
	  GM_setValue(attackname,JSON2.stringify(attack));
	},	
	
	GetAttack:function(x,y) {
		var str=this.browser_getValue(this.GetAttackName(x,y),'');
		if(!str) return null;
		return JSON2.parse(str);
	},
	
	GetCityName:function(cityid) {  
	  var cityName;
	  for(var a=0; a<unsafeWindow.seed.cities.length; a++) {
	    if(unsafeWindow.seed.cities[a][0] == cityid){
		  var cityName = unsafeWindow.seed.cities[a][1];
		}
	  }
	  return cityName;
	},	

	GetCityCoordsX:function(cityid) {  
	  var cityName;
	  for(var a=0; a<unsafeWindow.seed.cities.length; a++) {
	    if(unsafeWindow.seed.cities[a][0] == cityid){
		  var x = unsafeWindow.seed.cities[a][2];
		}
	  }
	  return x;
	},	

	GetCityCoordsY:function(cityid) {  
	  var cityName;
	  for(var a=0; a<unsafeWindow.seed.cities.length; a++) {
	    if(unsafeWindow.seed.cities[a][0] == cityid){
		  var y = unsafeWindow.seed.cities[a][3];
		}
	  }
	  return y;
	},	
				
	ReloadWindow:function() {
		var m=/^[a-zA-Z]+([0-9]+)\./.exec(document.location.hostname);
		if (!m){
			//window.location.reload(true);
			history.go(0);
			return;
		}
		var goto = window.location.protocol+'//apps.facebook.com/kingdomsofcamelot/?s='+m[1];
		var t = '<FORM target="_top" action="'+ goto +'" method=post><INPUT id=xxButReload type=submit value=RELOAD><input type=hidden name=s value="'+ m[1] +'"</form>';
		var e = document.createElement ('div');
		e.innerHTML = t;
		document.body.appendChild (e);
		setTimeout (function (){document.getElementById('xxButReload').click();}, 0);
	},	
	
	ShowOptionsDialog:function() {
		var div=ById('KOCAttackOptions');
		if(!div) {
			div=document.createElement('div');
			div.id='KOCAttackOptions';
			div.style.zIndex=100000;
			div.style.position='absolute';
			div.style.left='8px';
			div.style.top='8px';
			div.style.backgroundColor='#fff';
			div.style.border='3px solid #888';
			div.style.padding='10px';
			div.style.maxWidth='700px';
			document.body.appendChild(div);
		}

		var okCitiesHtml="<span onmousedown='return false; '>";
		for(var c=1; c<=8; c++) {
			okCitiesHtml+=
				"<a style='font-size: 11px; padding: 2px; cursor: pointer; text-decoration: none' id='KOCAttackOkCities_"+c+"'>"+c+"</a>&nbsp;";
		}
		okCitiesHtml+='</span>';
		
		var attackfirst = '';
		var arrData = [["Camp", "Camps"],["City", "City"],["Transport", "Transport"],["Wilderness", "Wildernesses"],["None", "None"]];
		for (var i=0; i < arrData.length; i++){
			attackfirst += "<input type=radio name=KOCAttackPriority id='KOCAttackPriority_"+arrData[i][0]+"' value="+arrData[i][0]+" "+(arrData[i][0]==this.options.attackpriority?'checked':'')+"/>";
			attackfirst += "<label for='KOCAttackPriority_"+arrData[i][0]+"'>"+arrData[i][1]+"</label>";
		};
		
		div.style.display='block';
		div.innerHTML='';
		this.options=this.GetOptions();
		div.innerHTML="<form><table>"+
			"<tr><td valign='top' align='center'><img src='img/units/unit_6_50.jpg' /></td><td valign='top'>"+
			"Attack Order: <select id='KOCAttackOrder'><option value='mostTroops'>Use most troops first</option><option value='closest'>Closest targets first</option></select><br />"+
			"Attack Type: <input type='checkbox' "+(this.options.attackTypeCamp?'checked':'')+" id='KOCAttackTypeCamp'>Camp "+
			"<input type='checkbox' "+(this.options.attackTypeWild?'checked':'')+" id='KOCAttackTypeWild'>Wilderness "+
			"<input type='checkbox' "+(this.options.attackTypeCity?'checked':'')+" id='KOCAttackTypeCity'>City "+
			"<input type='checkbox' "+(this.options.attackTypeTransport?'checked':'')+" id='KOCAttackTypeTransport'>Transport<br> "+
			"Prioritise attacks for : "+attackfirst+
			"<br><br />"+
			"<input id='KOCAttackDelay' value='"+this.options.attackDelay+"' size='3' /> seconds inbetween sending each attack<br />"+
			"Time inbetween sending to the <u>same target</u>...<br />"+
			"<div style='margin-left: 40px'>"+
			"Attacking camp:<input id='KOCAttackHoursSinceLastCamp' value='"+(this.options.attackSecsSinceLastCamp/(60*60))+"' size='3' />hrs<br />"+
			"Attacking wilderness:<input id='KOCAttackHoursSinceLastWild' value='"+(this.options.attackSecsSinceLastWild/(60*60))+"' size='3' />hrs<br />"+
			" Attacking city:<input id='KOCAttackHoursSinceLastCity' value='"+(this.options.attackSecsSinceLastCity/(60*60))+"' size='3' />hrs<br />"+
			" Transporting:<input id='KOCAttackMinsSinceLastTransport' value='"+(this.options.attackSecsSinceLastTransport/(60))+"' size='3' />mins<br />"+
			" If transporting, try to keep at least <input id='KOCAttackTransportReserveAmount' value='"+this.options.transportResourcesReserveAmount+"' size='10' /> of each resource in each city <font color=#FF0000>(NEW)</font>"+
			"</div>"+
			"<input id='KOCAttackRandom' value='"+this.options.randomPercent+"' size='3' />% random adjustment for all delays (to look more human).<br />"+
			"<input id='KOCAttackRally' value='"+this.options.rallyKeep+"' size='3' /> of rally point slots to keep<br />"+
			"<br />"+
			"<input id='KOCAttackMaxDistance' value='"+(this.options.attackMaxDistance)+"' size='3' /> max distance away from city to attack/transport.<br />"+
			"<input id='KOCAttackLockAttackFromCity' type='checkbox' "+(this.options.lockAttackFromCity?'checked':'')+" /> Only launch attacks from city they were first launched from.<br />"+
			"<input id='KOCAttackUnselectKnight' type='checkbox' "+(this.options.knightreset?'checked':'')+" /> Deselect knight when not in attack mode.<br />"+
			"<input id='KOCAttackRetryMarch' type='checkbox' "+(this.options.retryMarch?'checked':'')+" /> Retry march if it has unknown/excess traffic error (press reload after changing this option).<br />"+
			"<input id='KOCAttackImpendingStopAttack' type='checkbox' "+(this.options.impendingStopAttack?'checked':'')+" /> Stop auto attack on impeding alert.<span style='color:red'> (Will not restart auto after the attack. Use at your own risk)</span><br />"+			
			"Open up this URL (such as a link to a sound/video file) in a tab when we're under attack:<br /><input id='KOCAttackImpendingAttackUrl' size='60' value='"+(this.options.impendingAttackUrl)+"' /><br />"+
			"</td></tr>"+
			
			"<tr><td valign='top' align='center'><img src='img/chrome_message_up.png' /></td><td valign='top'>"+
			"<input id='KOCAttackRemoveReports' type='checkbox' "+(this.options.autoRemoveReports?'checked':'')+" /> Auto remove barbarian/wilderness attack reports.<br />"+
			"<input id='KOCAttackRemoveFarmReports' type='checkbox' "+(this.options.autoRemoveFarmReports?'checked':'')+" /> Auto remove farm attack reports.<br />"+
			"<input id='KOCAttackKeepReports' value='"+this.options.keepReports+"' size='3' /> attack reports to keep maximum in the attack dialog.<br />"+
			"<input id='KOCAttackNoViewReports' type='checkbox' "+(this.options.noViewReports?'checked':'')+" /> Disable viewing of reports, this will also disable collecting of reports for the attack page.<br />"+
			"</td></tr>"+
			
			"<tr><td valign='top' align='center'><img src='img/chome_alliance_up.png' /></td><td valign='top'>"+
			"<input id='KOCAttackPercentOfPopToTrain' value='"+this.options.percentOfPopToTrain+"' size='3' />% of idle population available before we auto train <input id='KOCAttackPercentToTrainOfMax' value='"+this.options.percentToTrainOfMax+"' size='3' />% of max available.<br />"+
			"<span style=\"font-size:10px; color:#555; line-height:18px; \">To enable auto training, toggle the auto train button under a troop type in the barracks.</span><br />"+
			"<span style=\"font-size:10px; color:#555; line-height:18px; \">Note: If you auto train 100% of max population, you might sometimes encounter resource errors due to game caching.</span><br />"+
			"</td></tr>"+
			
			"<tr><td valign='top' align='center'><img src='img/gold_30.png' /></td><td valign='top'>"+
			"<input id='KOCAttackAutoGoldHappiness' value='"+this.options.autoGoldHappiness+"' size='3' />% happiness before we click auto gold.<br />"+
			"<span style=\"font-size:10px; color:#555; line-height:18px; \">To enable auto gold, toggle the auto gold button from inside your castle overview window.</span><br />"+
			"</td></tr>"+
			
			"<tr><td valign='top' align='center'><img src='img/gems.png' /></td><td valign='top'>"+
			"<input id='KOCAttackDisableMapDraw' type='checkbox' "+(this.options.disableDrawMapIcons?'checked':'')+" /> Disable drawing of map icons.<br />"+
			"<input id='KOCAttackDisableInviteFriends' type='checkbox' "+(this.options.disableInviteFriends?'checked':'')+" /> Disable the annoying \"Invite Friends\" popup dialog in-game.<br />"+
			"<input id='KOCAttackAutoHelpAlliance' type='checkbox' "+(this.options.autoHelpAlliance?'checked':'')+" /> Automatically help alliance members with building/researching.<br />"+
			"<input id='KOCAttackHideAllianceHelpRequests' type='checkbox' "+(this.options.hideAllianceHelpRequests?'checked':'')+" /> Hide alliance help requests/reports in chat (if above is checked, then after helping).<br />"+
			"<input id='KOCAttackAutoPublishGamePopups' type='checkbox' "+(this.options.autoPublishGamePopups?'checked':'')+" /> Automatically publish game popups (such as help requests) to facebook wall.<br />"+
			"If above is checked, what privacy setting should we use? <select id='KOCAttackAutoPublishPrivacy'><option value='80'>Everyone</option><option value='50'>Friends of Friends</option><option value='40'>Friends Only</option><option value='10'>Only Me</option></select><br />"+
			"<input id='KOCAttackAutoLogBackIn' type='checkbox' "+(this.options.autoLogBackIn?'checked':'')+" /> Automatically log back into domain if disconnected due to maintenance or server down-time.<br />"+
			"<input id='KOCAttackEnableLogging' type='checkbox' "+(this.options.enableLogging?'checked':'')+" /> Enable diagnostic logging in the Firefox error console messages window (useful if trying to debug a problem or if you are submitting details along with a bug report).<br />"+
			"</td></tr>"+
			
			"<tr><td valign='top' align='center'><img src='img/buildings/castle_lvl10.png' /></td><td valign='top'>"+
			"<input id='KOCAttackChangeCitySecs' value='"+(this.options.changeCitySecs)+"' size='3' /> seconds inbetween changing cities.<br />"+
			"Cycle thru all the cities <input id='KOCAttackCitiesDoneMax' value='"+(this.options.autoAttackCitiesDoneMax)+"' size='3' /> times and then wait "+
			"<input id='KOCAttackCitiesDelay' value='"+this.options.waitAfterCitiesDone+"' size='3' />secs before refreshing.<br />"+
			"Cities to attack from: "+okCitiesHtml+'<br />'+

			"<tr><td valign='top' align='center'></td><td valign='top'>"+
			"Import/Export: Paste or copy the settings here...<br /><textarea id='KOCAttackImport'></textarea>"+
			"<a class='button20' id='KOCAttackImportButton'><span>Import</span></a> <a class='button20' id='KOCAttackExportButton'><span>Export</span></a><br />"+
			"</td></tr>"+

			"</table>"+
			
			"<TABLE width=100%><TR><TD>"+
			"<a id='KOCAttackOptionsSave' class='button20'><span>Save</span></a> <a id='KOCAttackOptionsCancel' class='button20'><span>Cancel</span></a> <a id='KOCAttackOptionsReset' class='button20'><span>Reset options</span></a> <a id='KOCAttackOptionsResetAll' class='button20'><span>Reset all!</span></a>"+
			"<a id='KOCAttackDeleteAllStoredAttacks' class='button20'><span>Delete all stored attacks</span></a>"+
			"<a id='KOCAttackDeleteTransports' class='button20'><span>Delete transports</span></a>"+
			//"<a id='KOCAttackListAttacks' class='button20'><span>List attacks</span></a><a id='KOCAttackListTransports' class='button20'><span>List transports</span></a>"+
			"</td><TD align=right>"+ KOCAversion +"</td></tr></form>";
		var t=this;

		var importText=ById('KOCAttackImport');
		importText.addEventListener('focus',function() {
			importText.select();
		},false);
		ById('KOCAttackImportButton').addEventListener('click',function() {
			if(importText.value=="") return;
			t.ImportAllFromJSON(importText.value);
		},false);
		ById('KOCAttackExportButton').addEventListener('click',function() {
			importText.value=t.ExportAllToJSON();
		},false);
		
		nHtml.SetSelect(ById('KOCAttackOrder'),this.options.attackOrder);
		ById('KOCAttackOptionsCancel').addEventListener('click',function() {
			div.style.display='none';
		},false);
		ById('KOCAttackOptionsReset').addEventListener('click',function() {
			t.ClearOptions();
			this.options=t.GetOptions();
			div.style.display='none';
		},false);
		ById('KOCAttackOptionsResetAll').addEventListener('click',function() {
			t.FactoryReset();
			//window.location.reload(true);
			t.ReloadWindow();
		},false);
		ById('KOCAttackDeleteAllStoredAttacks').addEventListener('click',function() {
			t.DeleteAllStoredAttacks();
			//window.location.reload(true);
			t.ReloadWindow();
		},false);
		ById('KOCAttackDeleteTransports').addEventListener('click',function() {
 			t.DeleteTransports();
 			//window.location.reload(true);
 			t.ReloadWindow();
 		},false);
		
 		//ById('KOCAttackListTransports').addEventListener('click',function() {
 			//t.ListTransports();
 			//window.location.reload(true);
 		//},false);
 		//ById('KOCAttackListAttacks').addEventListener('click',function() {
 			//t.ListAttacks();
 			//window.location.reload(true);
 		//},false);
		
		nHtml.SetSelect(ById('KOCAttackAutoPublishPrivacy'),this.options.autoPublishPrivacySetting);

		for(var c=1; c<=8; c++) {
			var sp=ById('KOCAttackOkCities_'+c);
			var SetCity=function(target,set) {
				target.style.border=set?'2px solid #080':'';
				target.style.margin=set?'2px':'4px';
			};
			sp.addEventListener('click',function(evt) { SetCity(evt.target,evt.target.style.border==''?true:false); },false);
			//sp.onmousedown=function() { return false; }
			SetCity(sp,t.options.okCities[c]);
		}
		

		ById('KOCAttackOptionsSave').addEventListener('click',function() {
			t.options.attackTypeCamp=ById('KOCAttackTypeCamp').checked;
			t.options.attackTypeCity=ById('KOCAttackTypeCity').checked;
			t.options.attackTypeWild=ById('KOCAttackTypeWild').checked;
			t.options.attackTypeTransport=ById('KOCAttackTypeTransport').checked;
			
			var attackpriority = ByName('KOCAttackPriority');
				if(attackpriority){
					for(var i = 0; i < attackpriority.length; i++) {
						if(attackpriority[i].checked) {
							t.options.attackpriority = attackpriority[i].value;
							break;
						}
					}
				}
			
			t.options.attackDelay=parseInt(ById('KOCAttackDelay').value);
			t.options.waitAfterCitiesDone=parseInt(ById('KOCAttackCitiesDelay').value);
			t.options.keepReports=parseInt(ById('KOCAttackKeepReports').value);
			t.options.changeCitySecs=parseInt(ById('KOCAttackChangeCitySecs').value);
			t.options.autoGoldHappiness=parseInt(ById('KOCAttackAutoGoldHappiness').value);
			t.options.percentOfPopToTrain=parseFloat(ById('KOCAttackPercentOfPopToTrain').value);
			t.options.percentToTrainOfMax=parseFloat(ById('KOCAttackPercentToTrainOfMax').value);
			
			var prev_disableInviteFriends = t.options.disableInviteFriends;
			t.options.disableInviteFriends=ById('KOCAttackDisableInviteFriends').checked;
			t.options.disableDrawMapIcons=ById('KOCAttackDisableMapDraw').checked;
			if(prev_disableInviteFriends != t.options.disableInviteFriends){
				alert("You changed the option for disabling/enabling the \"Invite Friends\" feature.\nPlease note: You will need to refresh the entire game window for the new setting to take effect!");
			}
			
			t.options.autoHelpAlliance=ById('KOCAttackAutoHelpAlliance').checked;
			t.options.hideAllianceHelpRequests=ById('KOCAttackHideAllianceHelpRequests').checked;
			t.options.autoPublishGamePopups=ById('KOCAttackAutoPublishGamePopups').checked;
			t.options.autoPublishPrivacySetting=ById('KOCAttackAutoPublishPrivacy').value;
			
			t.options.autoLogBackIn=ById('KOCAttackAutoLogBackIn').checked;
			t.options.enableLogging=ById('KOCAttackEnableLogging').checked;

			t.options.attackSecsSinceLastCamp=parseFloat(ById('KOCAttackHoursSinceLastCamp').value)*60*60;
			t.options.attackSecsSinceLastWild=parseFloat(ById('KOCAttackHoursSinceLastWild').value)*60*60;
			t.options.attackSecsSinceLastCity=parseFloat(ById('KOCAttackHoursSinceLastCity').value)*60*60;
			t.options.attackSecsSinceLastTransport=parseFloat(ById('KOCAttackMinsSinceLastTransport').value)*60;
			
			t.options.transportResourcesReserveAmount=parseInt(ById('KOCAttackTransportReserveAmount').value);
			
			t.options.randomPercent=parseFloat(ById('KOCAttackRandom').value);
			t.options.rallyKeep=parseInt(ById('KOCAttackRally').value);
			t.options.attackMaxDistance=parseFloat(ById('KOCAttackMaxDistance').value);
			t.options.autoAttackCitiesDoneMax=parseInt(ById('KOCAttackCitiesDoneMax').value);

			t.options.attackOrder=ById('KOCAttackOrder').value;
			
			t.options.lockAttackFromCity=ById('KOCAttackLockAttackFromCity').checked;
			t.options.knightreset=ById('KOCAttackUnselectKnight').checked;
			t.options.autoRemoveReports=ById('KOCAttackRemoveReports').checked;
			t.options.autoRemoveFarmReports=ById('KOCAttackRemoveFarmReports').checked;
			t.options.retryMarch=ById('KOCAttackRetryMarch').checked;
			t.options.impendingAttackUrl=ById('KOCAttackImpendingAttackUrl').value;
			t.options.impendingStopAttack=ById('KOCAttackImpendingStopAttack').checked;
			
			t.options.noViewReports=ById('KOCAttackNoViewReports').checked;
			
			for(var c=1; c<=8; c++) {
				var okcity=ById('KOCAttackOkCities_'+c);
				t.options.okCities[c]=okcity.style.border!=""?true:false;
			}

			t.SetOptions(t.options);
			div.style.display='none';
		},false);
	},

	AddOptionsLink:function() {
		var t=this;
		var a=ById('KOCAttackOptionsLink');
		if(a) return;

		a=this.AddTabLink('Options');
		if(!a) {
			window.setTimeout(function() {
				t.AddOptionsLink();
			},t.GetRandTime(250));
			return;
		}
		a.id='KOCAttackOptionsLink';
		a.addEventListener('click',function() {
			t.ShowOptionsDialog();
		},false);
	},
	
	AddViewAttacksLink:function() {
		var t=this;
		var a=ById('KOCAttackViewAttacksLink');
		if(a) return;

		a=this.AddTabLink('View Attacks');
		if(!a) {
			window.setTimeout(function() {
				t.AddViewAttacksLink();
			},t.GetRandTime(250));
			return;
		}
		a.id='KOCAttackViewAttacksLink';
		a.addEventListener('click',function() {
			t.ShowViewAttacksDialog();
		},false);
	},	
	

	/*
	AddImportExportLink:function() {
		var t=this;
		var a=ById('KOCImportExportLink');
		if(a) return;

		a=this.AddTabLink('Imp Exp');
		if(!a) {
			window.setTimeout(function() {
				t.AddImportExportLink();
			},t.GetRandTime(250));
			return;
		}
		a.id='KOCImportExportLink';
		a.addEventListener('click',function() {
			t.ShowImportExportBox();
		},false);
	},
	*/

	
	AddTabLink:function(html) {
		// Resize main tab bar container
		var tab_container = ById("main_engagement_tabs");
		tab_container.style.width = "715px";
		// Create new tab
		var a=document.createElement('a');
		a.className='navTab';
		a.innerHTML='<span>'+html+'</span>';
		var tabs=ById('KOCAttackTabs');
		if(!tabs) {
			var ptabs=ById('main_engagement_tabs');
			if(!ptabs) {
				ptabs=ById('topnav_msg');
				if(ptabs)ptabs=ptabs.parentNode;
			}
			if(!ptabs) {
				ptabs=document.body;
			}
			tabs=document.createElement('span');
			tabs.id='KOCAttackTabs';
			ptabs.insertBefore(tabs,ptabs.childNodes[0]);
		}
		
		if(tabs) {
			tabs.style.whiteSpace='nowrap';
			tabs.style.width='1600px';
			tabs.appendChild(a);
			return a;
		}
		return null;
	},

	ToggleAutoAttack:function() {
		var t=this;
		var a=t.GetAutoAttack();
		if(!a) {
			t.Log("Enabling Auto Attack");
			t.SetAutoAttack({'barbarian':true,'cities':{}});
			window.setTimeout(function() {
				t.SetAttackStatusMessage();
			},t.GetRandTime(250));
			t.RestartAutoAttack();
		} else {
			t.Log("Disabling Auto Attack");
			t.ClearAutoAttackTimeout();
			t.SetAutoAttack(null);
			window.setTimeout(function() {
				t.SetAttackStatusMessage();
			},t.GetRandTime(250));
		}
		

	/*
		// toggle for this city, 
		if(!a.cities) a.cities={};
		var city=t.GetCurrentCityId();
		a.cities[city]=a.cities[city]?undefined:true;
		var attackCities=0;
		for(city in a.cities) {
			attackCities++;
		}
		if(attackCities==0) {
			t.ClearAutoAttackTimeout();
			t.SetAutoAttack(null);
		}
	*/
	},

	SetAttackStatusMessage:function() {
		//var mess=this.GetStatusMessage();
		var toggle=ById('KOCAttackToggle');
		if(!toggle) {
		    var t=this;
			toggle=this.AddTabLink('Auto Attack');
			if(!toggle) {
				window.setTimeout(function() {
					t.SetAttackStatusMessage();
				},t.GetRandTime(250));
				return;
			}
			toggle.id='KOCAttackToggle';
			toggle.addEventListener('click',function() {
				t.ToggleAutoAttack();
			},false);
		}
		var autoAttack=this.GetAutoAttack();
		//if(autoAttack && autoAttack.cities && autoAttack.cities[t.GetCurrentCityId()]) {
		if(autoAttack) {
			toggle.innerHTML='<span>Auto Attack - On</span>';
		} else {
			toggle.innerHTML='<span>Auto Attack - Off</span>';
		}
	},

	SetStatusMessage:function(str) {
		var mess=this.GetStatusMessage();
		var txt=ById('KOCAttackMessage');
		if(!txt) {
			txt=document.createElement('span');
			mess.appendChild(txt);
		}
		txt.innerHTML=str;
	},
	GetStatusMessage:function() {
		var mess=ById('KOCAttackStatus');
		if(!mess) {
			var timeHead=ById('kochead_time');
			mess=document.createElement('span');
			mess.id='KOCAttackStatus';
			timeHead.parentNode.appendChild(mess);
		}
		return mess;
	},

	Log:function(str) {
		if(!this.options || !this.options.enableLogging){
			// disable logging
			return;
		}
		str=this.GetServerId()+":"+str;
		GM_log(str);
	},

	currentServerId:-1,
	GetServerId:function() {
		var m=/^[a-zA-Z]+([0-9]+)\./.exec(document.location.hostname);
		if(m) {
			this.currentServerId = m[1];
		}
		// Attempt to pull current server id from greasemonkey cache for cross-domain calls
		if(this.currentServerId<0){
			this.currentServerId = GM_getValue("KOCAttackLastKnownServerID",-1);
		}
		return this.currentServerId;
	},

	GetValue:function(prefix,def) {
		var n=prefix+'_'+this.GetServerId();
		return this.browser_getValue(n,def);
	},

	SetValue:function(prefix,val) {
		var n=prefix+'_'+this.GetServerId();
		this.browser_setValue(n,val);
	},

	GetValuesCache:function(doneFunc) {
		if(!this.isChrome) {
			if(doneFunc) {
				doneFunc.call(this);
			}
			return;
		}
	/*	
		var t=this;
		chrome.extension.sendRequest({func:"get","n":'KOCAttack'}, 
		function(response) {
		//~~~
			this.valuesCache=JSON2.parse(unescape(response.v));
			if(doneFunc) {
				doneFunc.call(t);
			}
		});
		return def;
	*/	
		var idx=0;
		
		var cookie='kocattack';
		var doccookie=document.cookie;
		while(true) {
			var i=doccookie.indexOf(cookie+'=',idx);
			if(i<0) { this.valuesCache={}; return; }
			idx=i+cookie.length+1;
			var ch=doccookie.substring(i-1,i);
			if(i==0 || ch==';' || ch==' ' || ch=='=') {
				break;
			}
		}

		var idxEnd=doccookie.indexOf(";",idx);
		if(idxEnd<0) { idxEnd=doccookie.length; }
		var cookieVal=doccookie.substring(idx,idxEnd);
		this.valuesCache=JSON2.parse(unescape(cookieVal));
	},
	SetValuesCache:function() {
		if(!this.isChrome) return;
		document.cookie='kocattack='+escape(JSON2.stringify(this.valuesCache))+'; expires='+
			(new Date(new Date().getTime()+(60*60*24*365*5)).toGMTString() );
	},

	browser_listValues:function() {
		if(this.isChrome) {
			var ns=[];
			for(var n in this.valuesCache) {
				ns.push(n);
			}
			return ns;
		}
		return GM_listValues();
	},
	browser_getValue:function(n,def) {
		if(this.isChrome) {
			if(this.valuesCache==null) {
				this.GetValuesCache();
			}
			if(this.valuesCache[n]==undefined) {
				return def;
			}
			return this.valuesCache[n];
		}
		return GM_getValue(n,def);
	},
	browser_setValue:function(n,val) {
		if(this.isChrome) {
			this.valuesCache[n]=val;
			return;
		}
		if(val==null || val==undefined) {
			GM_deleteValue(n);
		} else {
			GM_setValue(n,val);
		}
	},


	GetMinHours:function() {
		var m=this.GetValue('MinHours',1);
		if(m=="" || m==undefined) m=1;
		return m;
	},
	SetMinHours:function(val) {
		this.SetValue('MinHours',val);
	},

	ClearOptions:function() {
		this.SetValue('Options',JSON.stringify({}));
	},
	GetOptions:function() {
		var json=this.GetValue('Options','{}');
		if(json=='') json='{}';
		var options=JSON2.parse(json);
		var defOptions={"attackDelay":15,
			"attackTypeCamp":true,
			"attackOrder":"closest",
			"attackpriority":"None",
			"autoRemoveReports":true,
			"rallyKeep":0,
			"disableDrawMapIcons":false,
			"attackSecsSinceLastCity":60*60*12,
			"attackSecsSinceLastCamp":3600,
			"attackSecsSinceLastWild":3600,
			"attackSecsSinceLastTransport":60,
			"transportResourcesReserveAmount":1000000,
			"randomPercent":10,
			"keepReports":10,
			"attackMaxDistance":60,
			"lockAttackFromCity":true,
			"knightreset":true,
			"waitAfterCitiesDone":20,
			"autoAttackCitiesDoneMax":2,
			"changeCitySecs":20,
			"retryMarch":true,
			"noViewReports":false,
			"chromeKeepReports":2,
			"percentOfPopToTrain":75,
			"percentToTrainOfMax":95,
			"autoGoldHappiness":99,
			"disableInviteFriends":true,
			"autoHelpAlliance":true,
			"hideAllianceHelpRequests":false,
			"autoPublishGamePopups":false,
			"autoPublishPrivacySetting":"80",
			"autoLogBackIn":true,
			"enableLogging":false,
			"okCities":[1,1,1,1,1,1,1,1,1,1],
			'impendingAttackUrl':''};
		for(var n in defOptions) {
			if(options[n]!=undefined) { continue; }
			options[n]=defOptions[n];
		}
		return options;
	},
	SetOptions:function(v) {
		this.SetValue('Options',JSON2.stringify(v));
	},
	
	ClearCrossIframeCommands:function() {
		this.SetValue('CrossIframeCommands',JSON.stringify({}));
	},
	GetCrossIframeCommands:function() {
		var json=this.GetValue('CrossIframeCommands','{}');
		if(json=='') json='{}';
		var commands=JSON2.parse(json);
		if(!commands.queue || commands.queue instanceof Array !== true){
			commands.queue = new Array();
		}
		return commands;
	},
	SetCrossIframeCommands:function(v) {
		this.SetValue('CrossIframeCommands',JSON2.stringify(v));
	},
	AddCrossIframeCommand:function(pageName, functionCall, functionParameters) {
		var command = {};
		command.pageName = pageName;
		command.functionCall = functionCall;
		var commands = this.GetCrossIframeCommands();
		commands.queue.push(command);
		this.SetCrossIframeCommands(commands);
	},

	GetAttackName:function(x,y) {
		return 'attack_'+this.GetServerId()+'_'+x+','+y;
	},
	SetAttack:function(x,y,attack) {
		this.browser_setValue(this.GetAttackName(x,y), JSON2.stringify(attack));
	},
	GetAttack:function(x,y) {
		var str=this.browser_getValue(this.GetAttackName(x,y),'');
		if(!str) return null;
		return JSON2.parse(str);
	},
	DeleteAttack:function(x,y) {
		var str=this.browser_getValue(this.GetAttackName(x,y),'');
		if(!str) return;
		GM_deleteValue(this.GetAttackName(x,y));
	},

	isSuicideAttackDefinedAtLocation:function(x,y) {
		var attack=this.GetAttack(x,y);
		if(!attack){ return false; }
		if(attack.suicidewave) { return true; }
		if(attack.a && attack.a.suicidewave){ return true; }
		return false;
	},

	IsFirstAttackAtLocation:function(x,y) {
		var attack=this.GetAttack(x,y);
		if(!attack){
			this.Log("Attack not defined in system. This is first attack!");
			return true;
		}
		if(attack.a) { attack = attack.a; }
		// Check the current marches to see if there's an existing attack underway from this city
		var current_marches=this.GetSeed().queue_atkp["city" + this.GetCurrentCityId()];
		for (var march in current_marches) {
			if(current_marches[march]['toXCoord'] == x && current_marches[march]['toYCoord'] == y){
				this.Log("Troops are already marching to ("+x+","+y+") in the troop activity.");
				/*
				this.Log("Suicide wave time: "+attack.suicidewavetime+". Attack time: "+attack.time+".");
				if(!attack.suicidewavetime || !attack.time){
					this.Log("Suicide wave time: "+attack.suicidewavetime+". Attack time: "+attack.time+". This is first attack!");
					return true;
				}
				*/
				if(attack.suicidewavetime && attack.suicidewave){
					var nowSecs=new Date().getTime()/1000;
					var elapsedTime=nowSecs-attack.suicidewavetime;
					if(elapsedTime > 30 && attack.time < attack.suicidewavetime){
						this.Log("Suicide wave attack was sent more than 30 seconds ago and normal wave has not been sent yet. Suicide wave should be re-sent! This is the first attack sent to ("+x+","+y+")!");
						return true;
					}
				}
				this.Log("This is not the first attack sent to ("+x+","+y+").");
				return false;
				break;
			}
		}

		/*
		if(this.prevAttack.x && this.prevAttack.y){
			if(this.prevAttack.x==x && this.prevAttack.y==y) {
				this.Log("Previous attack matches current attack. This is not first attack!");
				return false;
			}
		}
		*/

		this.Log("Troops are not currently marching to ("+x+","+y+") in the troop activity. This is the first attack.");
		return true;
	},

	GetCommandHistory:function(history_log_name) {
		if(!history_log_name){
			var history_log_name = "default";
		}
		var json=this.GetValue('PreviousCommandHistory_'+history_log_name,'{}');
		if(json=='') json='{}';
		var json_object=JSON2.parse(json);
		if(!json_object['items']){
			json_object['items'] = Array();
		}
		return json_object;
	},

	AddToCommandHistory:function(command_string, history_log_name, log_length_limit) {
		if(!command_string){ return false; }
		if(!history_log_name){ var history_log_name = "default"; }
		// Default to a history length of 20 commands
		if(!log_length_limit){ var log_length_limit = 20; }
		// Get the previous history of commands
		var previous_commands = this.GetCommandHistory(history_log_name);
		var items = previous_commands['items'];
		// Add the new command
		items.push(command_string);
		// Limit the history length
		if(items.length>log_length_limit){
			items = items.slice(items.length-log_length_limit);
		}
		previous_commands['items'] = items;
		this.SetValue('PreviousCommandHistory_'+history_log_name,JSON2.stringify(previous_commands));
	},

	FindInCommandHistory:function(command_string, history_log_name) {
		if(!command_string){ return false; }
		if(!history_log_name){ var history_log_name = "default"; }
		// Get the previous history of commands
		var previous_commands = this.GetCommandHistory(history_log_name);
		var items = previous_commands['items'];
		for(var i=0; i<items.length; i++){
			if(items[i] == command_string){
				return true;
			}
		}
		return false;
	},

	GetGuiCoords:function() {
		var x=ById('modal_attack_target_coords_x');
		var y=ById('modal_attack_target_coords_y');
		if(!x || !y) {
			this.Log("Cannot find gui coords");
			return null;
		}
		if(x.value.length<1 || y.value.length<1){
			this.Log("One or both coordinates are missing!");
			return null;
		}
		return [x.value,y.value];
	},

	SetAttackFromGui:function(box, resetTime) {
		var xy=this.GetGuiCoords();
		if(!xy) return null;
		if(!resetTime){resetTime=false;}
		return this.SetAttackFromGuiXY(xy[0],xy[1],box, null, null, resetTime);
	},

	AttackLastSentTime:0,	
	UpdateAttackLastSentTime:function(){
		this.AttackLastSentTime = new Date().getTime()/1000;
		this.SetValue('AttackLastSentTime',parseInt(this.AttackLastSentTime));
	},
	
	SendingMultipleWaves:false,
	IsCurrentlySendingMultipleWaves:function() {
		if(this.AttackLastSentTime == 0){
			this.AttackLastSentTime = this.GetValue('AttackLastSentTime',0);
		}
		var nowSecs = new Date().getTime()/1000;
		var waveTimerDelay = this.options.attackDelay*2;
		var timeDifference = nowSecs-this.AttackLastSentTime;
		//this.Log("nowSecs: "+nowSecs+" waveTimerDelay: "+waveTimerDelay+" timeDifference: "+timeDifference+" SendingMultipleWaves: "+this.SendingMultipleWaves);
		// If the last attack was sent at a time ago that is more than twice the attack delay,
		// then we assume something failed and we reset the multiple wave tracker so everything can continue.
		if(timeDifference > waveTimerDelay && this.SendingMultipleWaves==true){
			this.SendingMultipleWaves=false;
			this.Log("Multiple wave timer \("+waveTimerDelay+"\ seconds) has expired. Last known attack was sent "+timeDifference+" seconds ago. Resetting timer and continuing...");
		}
		return this.SendingMultipleWaves;
	},
	
	SetAttackFromGuiXY:function(x,y,box,isSuicideWave,locationType,resetTime) {
	
		if(resetTime==true){
			this.Log("Resetting start times for attack ("+x+","+y+")");
		}
		
		// Determine location type
		if(!locationType){ var locationType = ""; }
		var locationTypeRadioBoxes = ByName('KOCAttackLocationType');
		if(locationTypeRadioBoxes){
			for(var i = 0; i < locationTypeRadioBoxes.length; i++) {
				if(locationTypeRadioBoxes[i].checked) {
					locationType = locationTypeRadioBoxes[i].value;
					break;
				}
			}
		}

		// Determine attack wave type		
		if(!isSuicideWave){
			var isSuicideWave = false;
			var bulkAddSuicideWaveCheckbox = ById("KOCAttackBulkAddSuicideWave");
			if(bulkAddSuicideWaveCheckbox){isSuicideWave = bulkAddSuicideWaveCheckbox.checked;}
			var otherIsSuicideWaveCheckbox = ById("KOCAttackSuicideWaveCheckbox");
			if(otherIsSuicideWaveCheckbox){	if(otherIsSuicideWaveCheckbox.checked){isSuicideWave=true;} }
		}

		var troops=[];
		var totalTroops=0;
		for(var tr=0; tr<20; tr++) {
			var troop=ById('modal_attack_unit_ipt'+tr);
			if(!troop) continue;
			try {
				var v=parseInt(troop.value);
				troops[tr]=v;
				totalTroops+=v;
			} catch(e) {
				continue;
			}
		}
		var comment=ById('KocAttackComment');
		var marchType = getAttackTypeSelected();	
		if(marchType==null) {
			throw("Cannot find attack type");
		}

		if(totalTroops<=0) {
			this.Log("No troops, not saving attack");
			return null;
		}

		var nowSecs=new Date().getTime()/1000;
		var monthAgo=nowSecs-(60*60*24*31);
		
		// ignore anything other than attack
		if(marchType==0 && locationType!="Transport") {
			var attack=this.GetAttack(x,y);
			if(!attack) attack={};
			attack.type=marchType;
			if(comment){ attack.comment=comment.value; }
			var SuicideAttackDefined=this.isSuicideAttackDefinedAtLocation(x,y);
			var firstAttack=this.IsFirstAttackAtLocation(x,y);
			this.Log("First attack?: "+firstAttack);
			this.Log("Suicide attack defined? : "+SuicideAttackDefined);

			if(isSuicideWave) {
				this.Log("Suicide attack determined by checkbox");
				this.Log("Suicide wave :"+troops);
				attack.suicidewave=troops;
				attack.currenttattackwavetype = "suicide";
				attack.suicidewavetime = nowSecs;
				if(resetTime==true){ attack.suicidewavetime=monthAgo; }
				this.SendingMultipleWaves = true;
			} else {
				this.Log("Normal wave :"+troops);
				attack.time=nowSecs;
				if(resetTime==true){ attack.time=monthAgo; }
				if(!SuicideAttackDefined && firstAttack){
					attack.suicidewave=undefined;
				}
				attack.currenttattackwavetype = "normal";
				attack.troops=troops;
				this.SendingMultipleWaves = false;
			}
			
			//if (this.options.lockAttackFromCity) {
			attack.fromCity=this.GetCurrentCityId();
			//}
			
			//Check if level is undefined
			if(!attack.levelInfo){attack.levelInfo={'type':locationType,'level':0};}
			if(locationType==""){
				if(attack.levelInfo.type!=""){
					// a pre-defined location type was already entered before for this attack.
					locationType = attack.levelInfo.type;
				}else{
					// Default to camp if attack location type was absolutely not specified in any way
					locationType = "Camp";
				}
			}
			attack.levelInfo.type = locationType;

			//attack.ignore=undefined;
			this.prevAttack={'x':x,'y':y};
			this.SetAttack(x,y,attack);
			return attack;
		} else if(marchType==1 || locationType=="Transport") {
			// try to parse transports
			var attack=this.GetAttack(x,y);
			if(!attack) attack={};
			attack.type=1; //If we got here presume to be transport mode
			//attack.ignore=true; // We set this to ignore for now until I can get the auto attack working
			if(comment){
				attack.comment=comment.value;
			}
			var SuicideAttackDefined=this.isSuicideAttackDefinedAtLocation(x,y);

			attack.time=nowSecs;
			if(resetTime==true){ attack.time=monthAgo; }
			attack.troops=troops;

			var resources=[];
			var resourceTypes = new Array(
				'gold',
				'rec1',
				'rec2',
				'rec3',
				'rec4'
			);
			for(var res=0; res<resourceTypes.length; res++) {
				var resource=ById('modal_attack_'+resourceTypes[res]);
				if(!resource) continue;
				try {
					var v=parseInt(resource.value);
					resources[res]=v;
					totalResources+=v;
				} catch(e) {
					continue;
				}
			}
			attack.resources=resources;
			
			//if (this.options.lockAttackFromCity) {
			attack.fromCity=this.GetCurrentCityId();
			//}
			//attack.ignore=undefined;
			
			//locationType = "Transport"; Redundant code
			//Check if level is undefined
			if(!attack.levelInfo){attack.levelInfo={'type':locationType,'level':0};}
			// Set the location type
			//attack.levelInfo.type = locationType;
			
			attack.currenttattackwavetype = "transport";
			this.prevAttack={'x':x,'y':y};
			this.SetAttack(x,y,attack);
			return attack;
		} else {
			// Reset it
			this.prevAttack={'x':"350",'y':'350'};
		}
		return null;
	},

	ToggleCurrenttAttackWaveType:function(x,y,manual_setting) {
		var original_attack = this.GetAttack(x,y);
		if(original_attack) {
			var attack = original_attack;
			if(original_attack.a){
				attack = original_attack.a;
			}
			var previousattackwavetype = 'undefined';
			if(attack){
				//this.Log("Inspecting attack: "+inspect(attack,10));
				if(attack.currenttattackwavetype){
					previousattackwavetype = attack.currenttattackwavetype;
				}
				if(manual_setting){
					attack.currenttattackwavetype = manual_setting;
				}else if(attack.suicidewave && attack.currenttattackwavetype == "bulkadded"){
					attack.currenttattackwavetype = "suicide";
				}else{
					// Toggle back and forth
					if(attack.suicidewave && attack.currenttattackwavetype == "normal"){
						attack.currenttattackwavetype = "suicide";
					}else{
						attack.currenttattackwavetype = "normal";
					}
				}
				this.Log("Toggling attack for ("+x+", "+y+") from "+previousattackwavetype+" to "+attack.currenttattackwavetype);
				if(original_attack.a){
					original_attack.a = attack;
					attack = original_attack;
				}
				this.SetAttack(x,y,attack);
				return attack.currenttattackwavetype;
			}else{
				return false;
			}
		}else{
			return false;
		}
	},

	BulkAddCoords:function(box,coordsText,force,locationType,isSuicideWave) {
		if(!locationType){
			var locationType = "Camp"; //Default to camp
		}
		if(!isSuicideWave){
			var isSuicideWave = false;
		}
		var coordRows=coordsText.split("\n");
		var added=0;
		for(var r=0; r<coordRows.length; r++) {
			var row=coordRows[r];
			var m=/^\s*([0-9]+)\s*,\s*([0-9]+)/.exec(row);
			if(!m) {
				m=/^\s*([0-9]+)\s+([0-9]+)/.exec(row);
				if(!m) continue;
			}
			var x=m[1];
			var y=m[2];
			var attack=this.GetAttack(x,y);
			
			var currenttattackwavetype = "bulkadded";
			if(locationType=="Transport"){
				currenttattackwavetype = "transport";
			}
			var SuicideAttackDefined=this.isSuicideAttackDefinedAtLocation(x,y);
			var previous_suicidewave = undefined;
			if(attack && (SuicideAttackDefined || isSuicideWave)){
				previous_suicidewave = attack.suicidewave;
			}
			
			if(attack && !force) {
				if(SuicideAttackDefined && isSuicideWave==true){
					this.Log('Suicide wave for '+x+','+y+' has already been added. Try over-writing it.');
					continue;
				}else if(attack.troops){
					this.Log('Attack wave for '+x+','+y+' has already been added. Try over-writing it.');
					continue;
				}
			}
			var attack=this.SetAttackFromGuiXY(x,y,box,isSuicideWave,locationType);
			if(attack) {
				attack.levelInfo={'type':locationType,'level':0};
				attack.fromCity=this.GetCurrentCityId();
				var nowSecs=new Date().getTime()/1000;
				var monthAgo=nowSecs-(60*60*24*31);
				attack.time=monthAgo;
				if(SuicideAttackDefined || isSuicideWave || previous_suicidewave){
					// set up suicide wave before attack time, according to "seconds in between sending each attack"
					attack.suicidewavetime = attack.time - this.options.attackDelay;
					if(previous_suicidewave && (!SuicideAttackDefined || !isSuicideWave)){
						this.Log("Previous suicide wave for this attack was over-written by new attack. Merging and restoring...");
						attack.suicidewave = previous_suicidewave;
					}
				}
				attack.ignore=undefined;
				attack.currenttattackwavetype=currenttattackwavetype;
				this.SetAttack(x,y,attack);
				this.Log(x+','+y+' attack added: '+inspect(attack));
				this.Log("current attack wave type: "+attack.currenttattackwavetype);
				added++;
			}
		}
		// Redraw attacks on map listing
		this.DrawClosestFarms();
		// done
		return added;
	},

	BulkAddAttackLink:function(box) {
		var div=ById('BulkAddAttackDiv');
		var t=this;
		if(!div) {
			div=document.createElement('div');
			div.id='BulkAddAttackDiv';
			div.style.display='inline';
		}
		div.innerHTML='';
		
		var bulkAddTable=document.createElement('table');
		bulkAddTable.style.background='transparent';
		var bulkAddDiv=document.createElement('div');
		bulkAddTable.insertRow(-1).insertCell(-1).appendChild(bulkAddDiv);
		bulkAddDiv.style.display='none';
		//AddHtml(bulkAddDiv,"<hr />");
		AddHtml(bulkAddDiv,"Copy and paste coords here (ie. 343,434) one on each line...<br />Note: it will only add the target using the current number of troops on this screen.<br /><input id='KOCAttackBulkAddForce' type='checkbox' /> <label for=\"KOCAttackBulkAddForce\">Overwrite existing attack if one already exists</label><br />");

		
		// radio boxes for defining bulk coordinate type
		AddHtml(bulkAddDiv,"<hr />");
		AddHtml(bulkAddDiv,"Type of Locations (All coordinates must match this type):<br />");
		var arrData = [["Camp", "Camps"],["City", "City"],["Transport", "Transport"],["Wilderness", "Wildernesses"]];
		for (var i=0; i < arrData.length; i++){
			var objRadItem = document.createElement("input");
			objRadItem.type = "radio";
			objRadItem.name = "KOCAttackBulkAddLocationType";
			objRadItem.id = "KOCAttackBulkAddLocationType_" + arrData[i][0];
			objRadItem.value = arrData[i][0];

			if(i == 0) {objRadItem.defaultChecked = true; objRadItem.checked = true; };

			var objTextNode = document.createTextNode(" " + arrData[i][1]);

			var objLabel = document.createElement("label");
			objLabel.htmlFor = objRadItem.id;
			objLabel.appendChild(objRadItem);
			objLabel.appendChild(objTextNode);

			bulkAddDiv.appendChild(objLabel);
			
			objRadItem.addEventListener('change',function() {
				var locationType = "Camp"; // Set default value to camp
				var bulkLocationTypeRadioBoxes = ByName('KOCAttackBulkAddLocationType');
				if(bulkLocationTypeRadioBoxes){
					for(var i = 0; i < bulkLocationTypeRadioBoxes.length; i++) {
						if(bulkLocationTypeRadioBoxes[i].checked) {
							locationType = bulkLocationTypeRadioBoxes[i].value;
							break;
						}
					}
				}
				
				// Select the same radio box in the normal add area
				var locationTypeRadioBoxes = ByName('KOCAttackLocationType');
				if(locationTypeRadioBoxes){
					for(var i = 0; i < locationTypeRadioBoxes.length; i++) {
						if(locationTypeRadioBoxes[i].value == locationType) {
							locationTypeRadioBoxes[i].checked = true;
							break;
						}
					}
				}
			},false);
			
			AddHtml(bulkAddDiv," ");
		};
		AddHtml(bulkAddDiv,"<hr />");
		
		//AddHtml(bulkAddDiv,"<input id='KOCAttackBulkAddSuicideWave' type='checkbox' /> This is an initial suicide wave to wipe out traps on a wilderness.<br />");
		
		var bulkAddSuicideSpan=document.createElement('span');
		var bulkAddSuicideCheckbox=document.createElement('input');
		bulkAddSuicideCheckbox.id = "KOCAttackBulkAddSuicideWave";
		bulkAddSuicideCheckbox.name = bulkAddSuicideCheckbox.id;
		bulkAddSuicideCheckbox.type='checkbox';
		bulkAddSuicideSpan.appendChild(bulkAddSuicideCheckbox);
		AddHtml(bulkAddSuicideSpan,'<label for="KOCAttackBulkAddSuicideWave">This is an initial suicide wave to wipe out traps on a wilderness.</label>');
		bulkAddDiv.appendChild(bulkAddSuicideSpan);
		
		bulkAddSuicideCheckbox.addEventListener('click',function() {
			var bulkAddSuicideWaveCheckbox = ById("KOCAttackBulkAddSuicideWave");
			var suicideWaveCheckbox = ById("KOCAttackSuicideWaveCheckbox");
			var locationType_Wilderness = ById("KOCAttackLocationType_Wilderness");
			var bulkAddLocationType_Wilderness = ById("KOCAttackBulkAddLocationType_Wilderness");
			if(bulkAddSuicideWaveCheckbox && suicideWaveCheckbox && locationType_Wilderness && bulkAddLocationType_Wilderness){
				if(bulkAddSuicideCheckbox.checked){
					suicideWaveCheckbox.checked = true;
					locationType_Wilderness.checked = true;
					bulkAddLocationType_Wilderness.checked = true;
				}else{
					suicideWaveCheckbox.checked = false;
				}
			}
		},false);

		AddHtml(bulkAddDiv,"<hr />");

		var coords=document.createElement('textarea');
		coords.wrap='off';
		coords.style.whiteSpace='nowrap';
		coords.cols=10;
		coords.rows=8;
		bulkAddDiv.appendChild(coords);
		AddHtml(bulkAddDiv,"<br />");
		var bulkAdd=document.createElement('a');
		bulkAdd.className='buttonDown20';
		bulkAdd.innerHTML='<span>Bulk Add</span>';
		bulkAddDiv.appendChild(bulkAdd);
		bulkAdd.addEventListener('click',function() {
			// Determine location type
			var locationType = "Camp"; // Set default value to camp
			var locationTypeRadioBoxes = ByName('KOCAttackBulkAddLocationType');
			if(locationTypeRadioBoxes){
				for(var i = 0; i < locationTypeRadioBoxes.length; i++) {
					if(locationTypeRadioBoxes[i].checked) {
						locationType = locationTypeRadioBoxes[i].value;
						break;
					}
				}
			}
			// Determine attack wave type
			var isSuicideWave = false;
			var bulkAddSuicideWaveCheckbox = ById("KOCAttackBulkAddSuicideWave");
			if(bulkAddSuicideWaveCheckbox){isSuicideWave = bulkAddSuicideWaveCheckbox.checked;}
			var otherIsSuicideWaveCheckbox = ById("KOCAttackSuicideWaveCheckbox");
			if(otherIsSuicideWaveCheckbox){	if(otherIsSuicideWaveCheckbox.checked){isSuicideWave=true;} }
			
			// Add the coordinates
			var added=t.BulkAddCoords(box,coords.value,ById('KOCAttackBulkAddForce').checked,locationType,isSuicideWave);
			bulkAddDiv.style.display='none'; 
			bulkAddAttackLink.style.display='inline';
			window.alert('Bulk added '+added+' coords');
		},false);
		AddHtml(bulkAddDiv,"<br />");

		var bulkAddAttackLink=document.createElement('a');
		bulkAddAttackLink.className='buttonDown20';
		bulkAddAttackLink.innerHTML='<span>Bulk add coords</span>';
		bulkAddAttackLink.addEventListener('click',function() { 
			if(bulkAddDiv.style.display=='inline'){
				bulkAddDiv.style.display='none'; 
			}else{
				bulkAddDiv.style.display='inline'; 
			}
		},false);
		div.appendChild(bulkAddAttackLink);
		div.appendChild(bulkAddTable);
	
		return div;
	},
	
	hideAttackEffortsState : true,
	HideAttackEfforts: function () {
		var t = KOCAttack;
		if (!ById('modal_attack_march_boost')) { return; }
		var span = document.createElement('span');
		var a = document.createElement('a');
		//var txt = document.createElement('text'); 
		a.innerHTML = 'Hide Attack/Speed Boosts';
		//inp.type='checkbox';
		span.appendChild(a);
		//span.appendChild (txt);
		if (t.hideAttackEffortsState) {
			hideshow('none');
		}
		a.addEventListener('click', function (evt) {
			t.hideAttackEffortsState = !t.hideAttackEffortsState;
			hideshow();
		}, false);
		for (var i = 1; i < 5; i++) {
			document.getElementById('modal_attack_tab_' + i).addEventListener('click', hideshow, false);
			document.getElementById('modal_attack_tab_' + i).addEventListener('click', setLocationType, false);
		}
		return span;

		function setLocationType() {
			var attackTypeSelected = getAttackTypeSelected();
			if(attackTypeSelected==1){
				// Transport
				var KOCAttackLocationType_Transport = ById("KOCAttackLocationType_Transport");
				if(KOCAttackLocationType_Transport) { KOCAttackLocationType_Transport.checked = true; }
				var KOCAttackBulkAddLocationType_Transport = ById("KOCAttackBulkAddLocationType_Transport");
				if(KOCAttackBulkAddLocationType_Transport) { KOCAttackBulkAddLocationType_Transport.checked = true; }
			}else{
				// Default back to Camp
				var KOCAttackLocationType_Camp = ById("KOCAttackLocationType_Camp");
				if(KOCAttackLocationType_Camp) { KOCAttackLocationType_Camp.checked = true; }
				var KOCAttackBulkAddLocationType_Camp = ById("KOCAttackBulkAddLocationType_Camp");
				if(KOCAttackBulkAddLocationType_Camp) { KOCAttackBulkAddLocationType_Camp.checked = true; }
			}
		}
		
		function hideshow() {
			if (t.options.knightreset) {
				var attackTypeSelected = getAttackTypeSelected();
				var knightSelect = ById('modal_attack_knight');
				if (attackTypeSelected != 0) { knightSelect.selectedIndex = 0; }
			}
			if (t.hideAttackEffortsState) {
				disp = 'none';
			}else{
				disp = 'block';
			}
			ById('modal_attack_march_boost').style.display = disp;
			ById('modal_attack_attack_boost').style.display = disp;
			ById('modal_attack_defense_boost').style.display = disp;
			var div = ById('modal_attack_speed_boost');
			for (var i = 0; i < i < div.childNodes.length; i++) {
				if (div.childNodes[i].className == 'section_title'){
					div.childNodes[i].style.display = disp;
				}
				if (div.childNodes[i].className == 'section_content') {
					div = div.childNodes[i];
					for (i = 0; i < div.childNodes.length; i++) {
						if (div.childNodes[i].style != undefined && div.childNodes[i].className != 'estimated') {
							div.childNodes[i].style.display = disp;
						}
					}
					break;
				}
			}
		}
	},

	SetResourceInput:function(num,resourceCount) {
		var resource=ById('modal_attack_'+num);
		if(!resource) return null;
		resource.value=resourceCount;
		resource.style.backgroundColor='';

		// send a shift key so that it recalculates
		var evt = document.createEvent("KeyboardEvent");
		if(evt.initKeyboardEvent) {
			evt.initKeyboardEvent("keyup",true,true,null,false,false,false,false,0x10,0);
		} else {
			evt.initKeyEvent("keyup",true,true,null,false,false,false,false,0x10,0);
		}
		resource.dispatchEvent(evt);

		if(parseInt(resourceCount)>0) {
			if(resource.value!=resourceCount) {
				this.Log('Not able to set resource count:'+num+',wanted:'+resourceCount+', count:'+resource.value);
				resource.style.backgroundColor='#f88';
				return 'notfull';
			} else {
				resource.style.backgroundColor='#ff8';
				return 'full';
			}
		}
		return 'none';
	},

	SetTroopInput:function(num,troopCount) {
		var troop=ById('modal_attack_unit_ipt'+num);
		if(!troop) return null;
		if(troop.disabled) return 'notfull'; //Disable filling in of troops if field is disabled
		troop.value=troopCount;
		troop.style.backgroundColor='';

		// send a shift key so that it recalculates
		var evt = document.createEvent("KeyboardEvent");
		if(evt.initKeyboardEvent) {
			evt.initKeyboardEvent("keyup",true,true,null,false,false,false,false,0x10,0);
		} else {
			evt.initKeyEvent("keyup",true,true,null,false,false,false,false,0x10,0);
		}
		troop.dispatchEvent(evt);

		if(parseInt(troopCount)>0) {
			if(troop.value!=troopCount) {
				this.Log('Not able to set troop count:'+num+',wanted:'+troopCount+', count:'+troop.value);
				troop.style.backgroundColor='#f88';
				return 'notfull';
			} else {
				troop.style.backgroundColor='#ff8';
				return 'full';
			}
		}
		return 'none';
	},

	GetRandTime:function(millis) {
		var millisPerc=millis*(this.options.randomPercent/100);
		var randSecs=Math.floor((Math.random()*millisPerc*2)-millisPerc)+millis;
		return randSecs;
	},

	GetAutoGold:function() {
		return this.GetValue('AutoGold,'+this.GetCurrentCityId(),true);
	},
	SetAutoGold:function(v) {
		return this.SetValue('AutoGold,'+this.GetCurrentCityId(),v);
	},

	GetAbandonWilds:function() {
		return this.GetValue('AbandonWilds_'+this.GetCurrentCityId(),false);
	},
	SetAbandonWilds:function(v) {
		return this.SetValue('AbandonWilds_'+this.GetCurrentCityId(),v);
	},

	CheckAutoRaiseGold:function() {
		if(!this.GetAutoGold()) return;
		var happiness=parseInt(this.GetSeed().citystats["city" + this.GetCurrentCityId()].pop[2]);
		
		if(happiness>=this.options.autoGoldHappiness) {
			this.DoUnsafeWindow("modal_raise_gold();");
		}
	},

	CheckAbandonWilds:function() {
		if(!this.GetAbandonWilds()) return;
		var t=this;
		var castle=ById('slot_0');
		nHtml.Click(castle);
		this.DoUnsafeWindow("changeCastleModalTabs(2);");
		var wildsWindow=ById('castle_2');
		var rows=wildsWindow.getElementsByTagName('tr');
		if (rows.length==1) { //no wilds
			this.DoUnsafeWindow("Modal.hideModal();");
			return;
		}
		var commands = new Array();
		for (i=0;i<rows.length;i++) {
			var abandonButton=rows[i].getElementsByTagName('a')[1];
			if (abandonButton==undefined) {
				continue;
			}
			var command=abandonButton.getAttribute('onclick');
			command=command.substring(0,command.length-13);
			if(command.indexOf("wilderness_abandon")>-1){
				var tmp_obj = new Object;
				tmp_obj.command = command;
				tmp_obj.clicked = false;
				commands.push(tmp_obj);
			}
		}
		var command_timer=0;
		var milliseconds_between=4000;
		for (var i=0;i<commands.length;i++) {
			window.setTimeout(function() {
				// Determine next unclicked button
				var unclicked_commandObj = undefined;
				for (var j=0;j<commands.length;j++) {
					if(!commands[j].clicked){
						t.DoUnsafeWindow(commands[j].command);
						window.setTimeout(function() {
							var mainbody = ById("mainbody");
							if(mainbody){
								var okay_btn=nHtml.FindByXPath(mainbody,".//a[contains(@class,'okay')]");
								if(okay_btn){
									nHtml.Click(okay_btn);
								}
							}
						},500);
						commands[j].clicked = true;
						break;
					}
				}
			},t.GetRandTime(command_timer));
			command_timer+=milliseconds_between;
		}
		window.setTimeout(function() {
			t.DoUnsafeWindow("Modal.hideModal();");
		},t.GetRandTime(command_timer));
	},

	GetDisplayName:function(){
		var DisplayName = ById('topnavDisplayName');
		if(DisplayName){
			DisplayName = DisplayName.innerHTML;
		}else{
			DisplayName = null;
		}
		return DisplayName
	},

	HandleChatPane:function() {
		var t=this;
		
		// Determine our own name so we can ignore our own requests
		var DisplayName = t.GetDisplayName();
		
		// Process chat pane
		var AllianceChatBox=ById('mod_comm_list2');
		if(AllianceChatBox){
		
			var chatPosts = document.evaluate(".//div[contains(@class,'chatwrap')]", AllianceChatBox, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );
			if(chatPosts){
				// Loop through each post
				for (var i = 0; i < chatPosts.snapshotLength; i++) {
					thisPost = chatPosts.snapshotItem(i);
					//t.Log(thisPost.innerHTML);
					
					// Automatically help out with alliance requests
					if(this.options.autoHelpAlliance){
						
						// Make sure that this isn't our own request
						var postAuthor = document.evaluate('.//*[@class="nm"]', thisPost, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );
						if(postAuthor.snapshotItem(0)){
							var postAuthorName = postAuthor.snapshotItem(0).innerHTML;
							if(postAuthorName != DisplayName){
								// Look for any alliance assist links in this current post item
								var helpAllianceLinks=document.evaluate(".//a[contains(@onclick,'claimAllianceChatHelp')]", thisPost, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );  
								if(helpAllianceLinks){
									for (var j = 0; j < helpAllianceLinks.snapshotLength; j++) {
										thisLink = helpAllianceLinks.snapshotItem(j);
										// Check to see if link has already been clicked
										var alreadyClicked = thisLink.getAttribute("clicked");
										if(!alreadyClicked){
											// Mark it as clicked
											thisLink.setAttribute('clicked', 'true');
											// Execute the onclick code
											var myregexp = /(claimAllianceChatHelp\(.*\);)/;
											var match = myregexp.exec(thisLink.getAttribute("onclick"));
											if (match != null) {
												onclickCode = match[0];
												//t.Log(onclickCode);
												// Check alliance help command history to make sure link hasn't already been processed
												if(!t.FindInCommandHistory(onclickCode, 'alliance_help')){
													t.DoUnsafeWindow(onclickCode);
													// Add the onclick code to the alliance help command history
													this.AddToCommandHistory(onclickCode, 'alliance_help');
												}else{
													//t.Log("already clicked");
												}
											}
										}else{
											//t.Log("already clicked");
										}
									}
								}else{
									//t.Log("no alliance links found in current post");
								}
							}else{
								//t.Log("current post is by yourself");
							}
						}else{
							//t.Log("unable to find post's author");
						}
					}
					
					// Hide alliance requests in chat
					if(this.options.hideAllianceHelpRequests){
						// Look for any alliance assist links in this current post item
						var helpAllianceLinks=document.evaluate(".//a[contains(@onclick,'claimAllianceChatHelp')]", thisPost, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );
						if(helpAllianceLinks){
							for (var j = 0; j < helpAllianceLinks.snapshotLength; j++) {
								thisLink = helpAllianceLinks.snapshotItem(j);
								// Delete the post item from the DOM
								thisLink.parentNode.parentNode.parentNode.parentNode.parentNode.removeChild(thisLink.parentNode.parentNode.parentNode.parentNode);
							}
						}
					}
					
					// Hide alliance reports in chat
					if(this.options.hideAllianceHelpRequests){
						// Look for any alliance assist links in this current post item
						var myregexp1 = /You are # [1-5] of 5 to help/i;
						var myregexp2 = /\'s Kingdom does not need help\./i;
						var myregexp3 = /\'s project has already been completed\./i;
						var myregexp4 = /\'s project has received the maximum amount of help\./i;
						if (thisPost.innerHTML.match(myregexp1) || thisPost.innerHTML.match(myregexp2) || thisPost.innerHTML.match(myregexp3) || thisPost.innerHTML.match(myregexp4)) {
							// Delete the post item from the DOM
							thisPost.parentNode.removeChild(thisPost);
						}
					}
					
				}
			}
		}
	},

	HandlePublishPopup:function() {
		var t=this;
		if (t.currentPage == "facebook_popup") {
			if(t.options.autoPublishGamePopups){
				// Check the app id (we only want to handle the popup for kingdoms of camelot)
				var FBInputForm = ById('uiserver_form');
				if(FBInputForm){
					var channel_input = nHtml.FindByXPath(FBInputForm,".//input[contains(@name,'channel')]");
					if(channel_input){
						var current_channel_url = channel_input.value;
						if (current_channel_url.match(/http:\/\/.{0,100}kingdomsofcamelot\.com\/.*?\/cross_iframe\.htm/i)) {
							var publish_button = nHtml.FindByXPath(FBInputForm,".//input[@type='submit' and contains(@name,'publish')]");
							var privacy_setting = nHtml.FindByXPath(FBInputForm,".//input[@type='hidden' and contains(@name, 'privacy_data') and contains(@name, 'value')]");
							if(publish_button && privacy_setting){
								// 80: Everyone
								// 50: Friends of Friends
								// 40: Friends Only
								// 10: Only Me
								privacy_setting.value = t.options.autoPublishPrivacySetting;
								nHtml.Click(publish_button);
							}
						}
					}		
				}
			}
		}
	},
	
	domainLoginTimer:null,
	domainLoginStartTime:null,
	domainLoginCurrentTime:null,
	domainLoginSeconds:30,
	domainLoginActionTaken:false,
	HandleDomainLogin:function() {
		var t=this;
		if (t.currentPage == "domain_selection" && t.options.autoLogBackIn && !t.domainLoginActionTaken) {
		
			if(!t.domainLoginStartTime){
				t.domainLoginStartTime = Math.round(new Date().getTime() / 1000);
			}
			t.domainLoginCurrentTime = Math.round(new Date().getTime() / 1000);
			var timeDifference = Math.round(t.domainLoginStartTime+t.domainLoginSeconds - t.domainLoginCurrentTime);
			if(timeDifference<0){ timeDifference=0; }
			
			var statusDiv=ById('KOCAttackLoginStatus');
			if(!statusDiv) {
				statusDiv=document.createElement('div');
				statusDiv.id='KOCAttackLoginStatus';
				statusDiv.style.position='relative';
				statusDiv.style.backgroundColor='#fff';
				statusDiv.style.border='3px solid #888';
				statusDiv.style.margin='30px 0px 0px 0px';
				statusDiv.style.padding='10px';
				statusDiv.style.display='none';
				var loginBox = ById("formoptions0");
				loginBox.appendChild(statusDiv);
			}
		
			// Find the top-most domain in the list (the most recent one)
			var playButtons=document.evaluate(".//a[contains(@class,'button20')]", unsafeWindow.document.body, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null );
			if(playButtons && playButtons.snapshotLength>0){
				//var firstPlayButton = playButtons.snapshotItem(0);
				//var domain_name = firstPlayButton.parentNode.parentNode.firstChild.innerHTML;
				//statusDiv.innerHTML='<center>KoCAttack Extra: Automatically logging into '+domain_name+' in '+timeDifference+' seconds...</center>';
				statusDiv.innerHTML='<center>KoCAttack Extra: Automatically logging back into KoC in '+timeDifference+' seconds...</center>';
				if(timeDifference==0){
					//t.Log("Loading URL: "+firstPlayButton.href);
					t.Log("Loading URL: http://apps.facebook.com/kingdomsofcamelot/");
					//statusDiv.innerHTML='<center>KoCAttack Extra: Automatically logging into '+domain_name+' now...</center>';
					statusDiv.innerHTML='<center>KoCAttack Extra: Automatically logging back into KoC now...</center>';
					var functionCall = {
						'action':'load_url',
						//'parameters':firstPlayButton.href
						'parameters':'http://apps.facebook.com/kingdomsofcamelot/'
					};
					t.AddCrossIframeCommand("domain_selection_app_page", functionCall);
					t.domainLoginActionTaken=true;
					// Reload current window if things are unsuccessful
					window.setTimeout(function() {
						t.ReloadWindow();
					},10000);
				}
			}else{
				statusDiv.innerHTML='<center>KoCAttack Extra: Automatically reloading page in '+timeDifference+' seconds...</center>';
				if(timeDifference==0){
					statusDiv.innerHTML='<center>KoCAttack Extra: Automatically reloading page now...</center>';
					t.ReloadWindow();
					t.domainLoginActionTaken=true;
				}
			}
			statusDiv.style.display='block';
			
			if(!t.domainLoginTimer && !t.domainLoginActionTaken) {
				t.domainLoginTimer=window.setTimeout(function() {
					t.domainLoginTimer=null;
					t.HandleDomainLogin();
				},1000);
			}		
		}
	},
	
	HandleCrossIframeCommands:function() {
		var t=this;
		var commands = t.GetCrossIframeCommands();
		if(!commands.queue) return false;
		var commandsUpdated = false;
		for(var i=0; i<commands.queue.length; i++) {
			var command = commands.queue[i];
			// Cross-iframe Command structure:
			// commands {
			//		command {
			//			'pageName' (string) - The page to execute the code on
			//			'functionCall' (string) - The function to call
			//		}
			// }
			if(t.currentPage == command.pageName){
				if(command.functionCall.action == "load_url"){
					setTimeout (function (){window.location.href=command.functionCall.parameters;}, 0); 
				}
				ArrayRemoveItem(commands.queue, i);
				commandsUpdated=true;
			}
		}
		if(commandsUpdated){
			this.SetCrossIframeCommands(commands);
		}
	},
		
	OnCastleBoxAppear:function(box) {
		var raiseGold=nHtml.FindByXPath(box,".//a[contains(@onclick,'raiseGold')]");
		var a=document.createElement('a');
		a.className='button25';
		var t=this;
		
		function SetAutoGoldA() {
			var str=t.GetAutoGold()?'Auto - On':'Auto - Off';
			a.innerHTML='<span>'+str+'</span>';
		}
		a.addEventListener('click',function() {
			var autoGold=t.GetAutoGold();
			t.SetAutoGold(autoGold?false:true);
			SetAutoGoldA();
		},false);
		SetAutoGoldA();
		raiseGold.parentNode.insertBefore(a,raiseGold.nextSibling);
		
		var productionHeader=nHtml.FindByXPath(box,".//div[contains(@class,'prodtableheader')]");
		productionHeader=productionHeader.parentNode;
		var b=document.createElement('a');
		b.className='button25';
		
		function SetAbandonWildsA() {
			var str=t.GetAbandonWilds()?'Abandon Wilds - On':'Abandon Wilds - Off';
			b.innerHTML='<span>'+str+'</span>';
		}
		b.addEventListener('click',function() {
			var abandonWilds=t.GetAbandonWilds();
			if(!abandonWilds){
				var abandonWildsConfirm = confirm("Are you sure you want to automatically abandon all wildernesses?\n")
				if (!abandonWildsConfirm){
					return false;
				}
			}
			t.SetAbandonWilds(abandonWilds?false:true);
			SetAbandonWildsA();
		},false);
		SetAbandonWildsA();
		//raiseGold.parentNode.insertBefore(b,raiseGold.nextSibling);
		productionHeader.parentNode.insertBefore(b,productionHeader);
	},

	ClickShareToWall:function(box) {
		var t=this;
		if(t.options.autoPublishGamePopups){
			var sharetowall_btn = nHtml.FindByXPath(box,".//a[contains(@onclick,'gethelp')]");
			nHtml.Click(sharetowall_btn);
		}
	},

	marketBoxTimeout:null,
	OnMarketBoxAppear:function(box) {
		var marketBox=ById('marketmain_bdy');
		var t=this;
		if(marketBox) {
			window.setTimeout(function() {
				t.OnMarketBoxAppear();
			},250);
			var amt=ById('marketmod_amount');
			if(amt && amt.value=="0") amt.value='999000';
			var price=ById('marketmod_price');
			if(price && price.value=="0") price.value='1';
		}
		
	},

	nextAutoAttackTimeout:null,
	onclickTimeRe:/,([0-9]+),[0-9]+,[0-9]+,[0-9]+[^,]*$/,
	waitForAttackBoxAppear:null,
	knightmarchid:0,
	OnAttackBoxAppear:function(box) {
		var btnMarch=ById('btnMarch');
		var t=this;
		if(!btnMarch) { 
			this.Log('no march button');
			window.setTimeout(function() {
				t.OnAttackBoxAppear(box);
			},1000);
			return; 
		}

		this.StopWaitForAttackBoxAppear();
		if(ById('KocAttackComment')) {
			this.Log("We already have an attack dialog opened");
			return;
		}
		
		var addAttackContainerDiv=document.createElement('div');
		addAttackContainerDiv.id = "addAttackButtonContainerDiv";
		addAttackContainerDiv.style.position = "relative";
		addAttackContainerDiv.style.width = "1px";
		addAttackContainerDiv.style.height = "1px";
		addAttackContainerDiv.style.overflow = "visible";
		
		var addAttackDiv=document.createElement('div');
		addAttackDiv.id = "addAttackButtonDiv";
		addAttackDiv.style.width = "250px";
		addAttackDiv.style.left = "430px";
		addAttackDiv.style.top = "5px";
		addAttackDiv.style.position = "absolute";
		addAttackDiv.style.overflow = "visible";
		addAttackDiv.style.textAlign="left";

		var addBtn=document.createElement('a');
		addBtn.className='button25';
		addBtn.style.cssFloat="left";
		addBtn.innerHTML='<span>Add Attack</span>';
		addBtn.addEventListener('click',function() {
			t.ClickMarch(null, box, true, true);
		},false);
		addAttackDiv.appendChild(addBtn);
		addAttackContainerDiv.appendChild(addAttackDiv);
		
		var locationTypeDiv=document.createElement('div');
		locationTypeDiv.id="KOCAttackLocationTypeDiv"
		locationTypeDiv.style.width="300px"
		locationTypeDiv.style.position="absolute";
		locationTypeDiv.style.left="0px";
		locationTypeDiv.style.top="-23px";
		locationTypeDiv.style.fontSize="10px";
		locationTypeDiv.style.textAlign="left";
		// radio boxes for defining bulk coordinate type
		var arrData = [["Camp", "Camp"],["City", "City"],["Transport", "Transport"],["Wilderness", "Wilderness"]];
		for (var i=0; i < arrData.length; i++){
			var objRadItem = document.createElement("input");
			objRadItem.type = "radio";
			objRadItem.name = "KOCAttackLocationType";
			objRadItem.id = "KOCAttackLocationType_" + arrData[i][0];
			objRadItem.value = arrData[i][0];

			if(i == 0) {objRadItem.defaultChecked = true; objRadItem.checked = true; };

			var objTextNode = document.createTextNode(" " + arrData[i][1]);

			var objLabel = document.createElement("label");
			objLabel.htmlFor = objRadItem.id;
			objLabel.appendChild(objRadItem);
			objLabel.appendChild(objTextNode);
			
			locationTypeDiv.appendChild(objLabel);
			
			objRadItem.addEventListener('change',function() {
				var locationType = "Camp"; // Set default value to camp
				var locationTypeRadioBoxes = ByName('KOCAttackLocationType');
				if(locationTypeRadioBoxes){
					for(var i = 0; i < locationTypeRadioBoxes.length; i++) {
						if(locationTypeRadioBoxes[i].checked) {
							locationType = locationTypeRadioBoxes[i].value;
							break;
						}
					}
				}
				
				// Select the same radio box in the bulk add area
				var bulkLocationTypeRadioBoxes = ByName('KOCAttackBulkAddLocationType');
				if(bulkLocationTypeRadioBoxes){
					for(var i = 0; i < bulkLocationTypeRadioBoxes.length; i++) {
						if(bulkLocationTypeRadioBoxes[i].value == locationType) {
							bulkLocationTypeRadioBoxes[i].checked = true;
							break;
						}
					}
				}
			},false);

		};
		addAttackDiv.appendChild(locationTypeDiv);
		
		var suicideSpan=document.createElement('span');
		suicideSpan.style.fontSize="11px";
		var suicideCheckbox=document.createElement('input');
		suicideCheckbox.id = "KOCAttackSuicideWaveCheckbox";
		suicideCheckbox.name = suicideCheckbox.id;
		suicideCheckbox.type='checkbox';
		suicideSpan.appendChild(suicideCheckbox);
		AddHtml(suicideSpan,'<label for="KOCAttackSuicideWaveCheckbox">This is a suicide wave</label>');
		addAttackDiv.appendChild(suicideSpan);
		suicideCheckbox.addEventListener('click',function() {
			var bulkAddSuicideWaveCheckbox = ById("KOCAttackBulkAddSuicideWave");
			var locationType_Wilderness = ById("KOCAttackLocationType_Wilderness");
			var bulkAddLocationType_Wilderness = ById("KOCAttackBulkAddLocationType_Wilderness");
			if(bulkAddSuicideWaveCheckbox && locationType_Wilderness && bulkAddLocationType_Wilderness){
				if(suicideCheckbox.checked){
					bulkAddSuicideWaveCheckbox.checked = true;
					locationType_Wilderness.checked = true;
					bulkAddLocationType_Wilderness.checked = true;
				}else{
					bulkAddSuicideWaveCheckbox.checked = false;
				}
			}
		},false);
	
		if(btnMarch){
			btnMarch.parentNode.insertBefore(addAttackContainerDiv,btnMarch);
		}

		var comment=document.createElement('input');
		comment.id='KocAttackComment';
		comment.size='30';

		var nowSecs=new Date().getTime()/1000;

		var div=document.createElement('div');
		AddText(div,'Comment:');
		div.appendChild(comment);
		div.appendChild(document.createElement('br'));

		var div2=document.createElement('div');
		var ignore=document.createElement('input');
		ignore.type='checkbox';
		div2.appendChild(ignore);
		AddText(div2,'Ignore in the attack list');
		
		var div3=document.createElement('div');
		var deletefarmno=document.createElement('input');
		deletefarmno.type='checkbox';
		div3.appendChild(deletefarmno);
		AddText(div3,'Do not delete this report');
		
		var nextElement=ById('marchTypeDesc');
		//nextElement.parentNode.insertBefore(div2, nextElement.nextSibling);
		//div.appendChild(document.createElement('br'));
		div.appendChild (div2);
		div2.appendChild (div3);
		
		var xy=this.GetGuiCoords();
		var attack=null;
		if(xy) {
			attack=this.GetAttack(xy[0],xy[1]);
		}
		var notFullTroops=false;
		var notFullResources=false;

		var knightSelect=ById('modal_attack_knight');
		var totalTroops=0;
		var totalResources=0;
		var attackTypeSelected = getAttackTypeSelected();
		if(attack) {
			ignore.checked=attack.ignore?true:false;
			deletefarmno.checked=attack.deletefarmno?true:false;
			if(attack.time) {
				AddHtml(div,'Last attack: '+SecsToStr(nowSecs-attack.time)+' ago<br />');
			}
			if(attack.comment){
				comment.value=attack.comment;
			}
			
			// Select the attack type radio boxes
			var wildtype={
				'Lake':'Wilderness',
				'Mountains':'Wilderness',
				'Woods':'Wilderness',
				'Forest':'Wilderness',
				'Plain':'Wilderness',
				'Hills':'Wilderness',
				'Grassland':'Wilderness',
				'Wilderness':'Wilderness' // Unknown wilderness type
			};
			var levelInfo=this.GetLevelInfo(attack);
			var locationType = "City"; // Default to city
			if(levelInfo && levelInfo.type) {
				locationType = levelInfo.type;
			}
			if(wildtype[levelInfo.type]) {
				locationType = wildtype[levelInfo.type];
			}
			// Select the location type radio box in the normal add area
			var locationTypeRadioBoxes = ByName('KOCAttackLocationType');
			if(locationTypeRadioBoxes){
				for(var i = 0; i < locationTypeRadioBoxes.length; i++) {
					if(locationTypeRadioBoxes[i].value == locationType) {
						locationTypeRadioBoxes[i].checked = true;
						break;
					}
				}
			}
			// Select the location type radio box in the bulk add area
			var bulkLocationTypeRadioBoxes = ByName('KOCAttackBulkAddLocationType');
			if(bulkLocationTypeRadioBoxes){
				for(var i = 0; i < bulkLocationTypeRadioBoxes.length; i++) {
					if(bulkLocationTypeRadioBoxes[i].value == locationType) {
						bulkLocationTypeRadioBoxes[i].checked = true;
						break;
					}
				}
			}
			
			// only fill things in if we're in attack mode.
			if(attackTypeSelected==0 && attack.type==0) { // if 'attack' mode
				SuicideAttackDefined = this.isSuicideAttackDefinedAtLocation(xy[0], xy[1])
				if(this.prevAttack) { this.Log('Previous attack:'+this.prevAttack.x+'=='+xy[0] +','+this.prevAttack.y+'=='+xy[1] ); }
				var firstAttack = this.IsFirstAttackAtLocation(xy[0], xy[1]);
				this.Log("current attack wave type: "+attack.currenttattackwavetype);
				if(SuicideAttackDefined && firstAttack) {
					// anti traps suicide wave attack
					if(typeof(attack.suicidewave)=="object") {
						for(var i=1; i<attack.suicidewave.length; i++) {
							this.SetTroopInput(i,attack.suicidewave[i]);
							totalTroops+=attack.suicidewave[i];
						}
					} else {
						this.SetTroopInput(2,attack.suicidewave);
						totalTroops+=attack.suicidewave;
					}
					// Check the suicide wave checkbox
					suicideCheckbox.checked = true;
				} else if(attack.troops) {
					for(var tr=0; tr<attack.troops.length; tr++) {
						var troopCount=attack.troops[tr];
						if(!troopCount) continue;
						totalTroops+=troopCount;

						if(this.SetTroopInput(tr,troopCount)=='notfull') {
							notFullTroops=true;
						}
					}
					// Make sure the suicide wave checkbox is *not* checked
					suicideCheckbox.checked = false;
				}

				//attackTypeSelect.selectedIndex=attack.type;
				if(this.knightmarchid == knightSelect.options[knightSelect.selectedIndex].value){
					knightSelect.selectedIndex = 1+Math.floor(Math.random()*(knightSelect.options.length-1)); //Select random knight if last knight didn't go out
				}
			} else if (attackTypeSelected==1 && attack.type==1) { // transport
				
				ById('KOCAttackLocationType_Transport').checked;
				
				// If transporting, we enable all troop types. No reason to restrict ourselves to just normal supply troops!
				var supplyfilter_checkbox = ById('modal_attack_supplyfilter_checkbox');
				if(supplyfilter_checkbox){
					if(supplyfilter_checkbox.checked){
						supplyfilter_checkbox.click();
					}
				}				
				
				var resourceTypes = new Array(
					'gold',
					'rec1',
					'rec2',
					'rec3',
					'rec4'
				);
				for(var tr=0; tr<attack.troops.length; tr++) {
					var troopCount=attack.troops[tr];
					if(!troopCount) continue;
					totalTroops+=troopCount;

					if(this.SetTroopInput(tr,troopCount)=='notfull') {
						this.Log("Input field disabled for troop type: "+tr);
						notFullTroops=true;
					}
				}

				for(var res=0; res<attack.resources.length; res++) {
					var resourceCount=attack.resources[res];
					if(!resourceCount) continue;
					totalResources+=resourceCount;

					if(this.SetResourceInput(resourceTypes[res],resourceCount)=='notfull') {
						notFullResources=true;
					}
				}
				// We don't send a knight with transports
				knightSelect.selectedIndex = 0;
			}
			
			if(attack.messages) {
				div.appendChild(document.createElement('br'));
				for(var m=attack.messages.length-1; m>=0; m--) {
					var message=attack.messages[m];
					var ma=document.createElement('a');
					var mess=message[0];
					var timeNumM=this.onclickTimeRe.exec(message[1]);
					if(timeNumM) {
						var secs=nowSecs-parseInt(timeNumM[1]);
						mess=SecsToStr(secs)+' ago, '+mess;
						//mess=(new Date(parseFloat(timeNumM[1])*1000).toLocalString())+', '+mess;
					}
					ma.innerHTML=mess;
				
	//{"time":1273315720.514,"troops":"test","type":0,"messages":[["Attack (326,97)  - Barbarian Camp Lv. 2 (NewCity4216)","modal_messages_viewreports_view(\"529747\",1,51,2,0,\"Enemy\",\"0\",\"niknah\",\"M\",4,326,97,1273312617,1,333,110);return false;"],["Attack (326,97)  - Barbarian Camp Lv. 2 (NewCity4216)","modal_messages_viewreports_view(\"529747\",1,51,2,0,\"Enemy\",\"0\",\"niknah\",\"M\",4,326,97,1273312617,0,333,110);return false;"]]}
				
					ma.setAttribute('onclick',"var m=CreateMsgDiv(); "+ message[1]+'');
					ma.href='javascript:;';
					div.appendChild(ma);
					div.appendChild(document.createElement('br'));
				}
				div.appendChild(document.createElement('br'));
			}
			
			
			// Show the "Delete Attack" button
			var deleteBtnDiv=document.createElement('div');
			deleteBtnDiv.id = "deleteAttackButtonDiv";
			deleteBtnDiv.style.width = "115px";
			deleteBtnDiv.style.left = "203px";
			deleteBtnDiv.style.top = "5px";
			deleteBtnDiv.style.position = "absolute";
			
			var deleteBtn=document.createElement('a');
			deleteBtn.className='button25';
			deleteBtn.innerHTML='<span>Delete Attack</span>';
			deleteBtn.addEventListener('click',function() {
				t.DeleteAttack(xy[0],xy[1]);
				t.DoUnsafeWindow('Modal.hideModalAll();');
			},false);
			deleteBtnDiv.appendChild(deleteBtn);
			addAttackContainerDiv.appendChild(deleteBtnDiv);

			//div.insertBefore (deleteBtn, div.firstChild);
		} 
		
		var ChangeAttack=function() {
			var xy=t.GetGuiCoords();
			var attack=null;
			if(xy) {
				attack=t.GetAttack(xy[0],xy[1]);
			}
			if(!attack) attack={};
			if(getAttackTypeSelected()!=attack.type) {
				t.Log('We wont change an attack if the type is different. You must delete the attack to change the type');
				return;
			}
			
			attack.comment=comment.value;
			attack.ignore=ignore.checked?true:undefined;
			attack.deletefarmno=deletefarmno.checked?true:undefined;
			t.SetAttack(xy[0],xy[1],attack);
		}
		comment.addEventListener('change',function() { ChangeAttack(); },false);
		ignore.addEventListener('change',function() { ChangeAttack(); },false);
		deletefarmno.addEventListener('change',function() { ChangeAttack(); },false);
		
		var divContainer = document.createElement ('div');
		divContainer.style.padding = '0px 12px';
		divContainer.style.height = '320px';
		divContainer.style.maxHeight = '320px';
		divContainer.style.overflowY = 'auto';
		divContainer.appendChild(this.HideAttackEfforts());	
		divContainer.appendChild(div);	
		divContainer.appendChild(this.BulkAddAttackLink(box));
		document.getElementById ('modal_attack').appendChild(divContainer);
		
		this.AttachXYPaste('modal_attack_target_coords_x','modal_attack_target_coords_y');
		
		var autoAttack=this.GetAutoAttack();
		if(autoAttack && autoAttack.x==xy[0] && autoAttack.y==xy[1] && !ignore.checked) {

			autoAttack.x=autoAttack.y=-1;
			this.SetAutoAttack(autoAttack);

			if(totalTroops>0 
			&& (knightSelect.options.length>1 || attackTypeSelected==1)//If transport you don't need knights(Thanks to shalm for pointing that out)
			&& !notFullTroops
			&& !notFullResources
			&& btnMarch.className.indexOf('grey')<0
			) {
				this.Log('Auto attack: '+xy[0]+','+xy[1]+', from city:'+this.autoAttackCityUpto);
				var t=this;
				window.setTimeout(function() {
					t.autoAttacksThisCity++;
					t.ClickMarch(btnMarch, box, false);
					this.knightmarchid = knightSelect.options[knightSelect.selectedIndex].value;
					//t.ResetIdStatus();
				},t.GetRandTime(1000));

				if(!this.nextAutoAttackTimeout) {
					this.nextAutoAttackTimeout=setTimeout(function() {
						// let's attack again in a few secs.
						t.nextAutoAttackTimeout=null;
						t.NextAutoAttack();
						
					},t.GetRandTime(1000*this.options.attackDelay));
				} else {
					this.Log('Cannot continue auto attacking, about to attack or change city');
				}
				return;
			} else {
				this.Log('Unable to send attack to '+xy[0]+','+xy[1]+', knights avail:'+(knightSelect.options.length-1)+', Not enough troops: '+notFullTroops+'(need '+totalTroops+'). Not enough resources:'+notFullResources+' (need '+totalResources+')');
				this.DoUnsafeWindow("Modal.hideModal();");
				this.NextAutoAttackCity();
			}
		}
	},
	
	ClickMarch:function(btnMarch, box, alert, resetTime){
		if(!resetTime){resetTime=false;}
		var t = this;
		if(btnMarch){
			nHtml.Click(btnMarch);
			window.setTimeout(function() {
				var mist=nHtml.FindByXPath(document,".//div[@class='mistwarn']");
				if(mist) {
					t.DoUnsafeWindow("Modal.hideModal();");
					t.DoUnsafeWindow("modal_attack_do();");
				}
			},t.GetRandTime(200));
		}
		var added = t.SetAttackFromGui(box, resetTime);
		t.SetValuesCache();
		if(added){
			if(alert){
				window.alert("Added attack");
			}
		}
	},

	coordsRe:/\(([0-9]+),([0-9]+)\)/,
	maptileRe:/modal_maptile.([^,]+),([^,]+),([^,]+),([^,]+),([^,]+),([^,]+),([^,]+),([^,]+),([^,]+),([^,]+),([^,]+),([^,]+)/,

	OnToolTipAppear:function(box) {
		var box1=box.childNodes[0].childNodes[0];
		var m=this.coordsRe.exec(box.innerHTML);
		if(!m || m.length==0) return;

		var x=m[1]; var y=m[2];

		var a=ById('l_'+x+'_t_'+y);
		if(a) {
			var onclick=a.getAttribute('onclick');
			if(onclick) {
				var titleM=this.maptileRe.exec(onclick);
				if(titleM && titleM[8].indexOf('null')<0) {
					AddHtml(box1,'Title: '+titleM[8].StripQuotes()+'<br />');
				}
			}
		}
		
		var attack=this.GetAttack(x,y);
		if(!attack) return;
		var troops=attack.troops;
		if(troops && troops.length>0) {
			var lastAttack=parseInt(attack.time);
			var nowSecs=new Date().getTime()/1000;
			var lastAttackStr='Last attack: '+SecsToStr(nowSecs-lastAttack)+' ago'+(attack.ignore?' <b>(ignore)</b> ':'');
			AddHtml(box1,lastAttackStr+'<br />');
			if(attack.comment) {
				AddHtml(box1,attack.comment+'<br />');
			}
		}
	},


	/*
	// don't know why but messages get duped sometimes.
	FixMessages:function(attack) {
		var fixed=0;
		var done={};
		for(var m=0; m<attack.messages.length; m++) {
			if(!done[attack.messages[m][1]]) {
				done[attack.messages[m][1]]=true;
	GM_log('ffff:'+attack.messages[m][1]+'####'+typeof(attack.messages[m][1]));
			} else {
				attack.messages.splice(m,1);
				fixed++;
			}
		}
		return fixed;
	},
	*/

	CalcXYDist:function(a,b) {
		var xdist=parseInt(a.x)-parseInt(b.x);
		xdist=Math.abs(xdist);
		if(xdist>=375) xdist=750-xdist;
		var ydist=parseInt(a.y)-parseInt(b.y);
		ydist=Math.abs(ydist);
		if(ydist>=375) ydist=750-ydist;
		return Math.sqrt((xdist*xdist)+(ydist*ydist));
	},

	//attackRe:/\s+(\S+)\s+Lv\.\s*([0-9]+)/,
	//javascript:Messages.viewMarchReport("22891795",0,51,10,1550996,"oftheNOOBS","M","Debby32","F",1,430,205,1302284981,1,284,83);
	squareTypeNums:{
	'51':'Camp',
	'10':'Grassland',
	'11':'Lake',
	'20':'Forest',
	'30':'Hills',
	'40':'Mountains',
	'50':'Plain',
	'0':'Bog'
	},
	FindLevelFromMessages:function(attack) {
		if(!attack || !attack.messages) return null;
		for(var a=0; a<attack.messages.length; a++) {
			var m=this.onclickReportRe.exec(attack.messages[a][1]);
			if(!m) {
				this.Log("Unable to find location level/type information in cached reports!");
				continue;
			}
			var typeNum=m[3].replace('"','');
			var type=this.squareTypeNums[typeNum];
			if(!type) {
				this.Log("Unable to find location type information in cached reports!");
				continue;
			}
			if(typeNum=="51" && m[5]!='0') { //Gradually replace all attack types to follow Tom's new method of storing attacks
				if(attack.type==0){
					type='City';
				} else if (attack.type==1){
					type='Transport';
				}
			}
			//this.Log("Target Type: "+type+", Target Level: "+m[4]);
			return {'type':type,'level':m[4]};
		}
		return null;
	},

	DrawLevelIcons:function() {
		var mapwindow=ById('mapwindow');
		if(!mapwindow) return;
		var levelIcons=ById('LevelIcons');
		if(levelIcons) return;

		var ss=document.evaluate(".//a[contains(@class,'slot')]",mapwindow,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
		var lvRe=/_([0-9]+)/;
		var idDone=false;
		for(var s=0; s<ss.snapshotLength; s++) {
			var a=ss.snapshotItem(s);
			var onclick=a.getAttribute('onclick');
			var owner='';
			if(onclick) {
				var onclickM=this.maptileRe.exec(onclick);
				if(onclickM && onclickM[6]!='"null"' && onclickM[12]!='"city"') {
					var might=onclickM[7].StripQuotes();
					//var alliance=onclickM[9].StripQuotes();
					//+"<br />"+(alliance=="null"?"":alliance);
					owner=" "+onclickM[6].StripQuotes()+'<br />Might:'+might;
				}
			}
			var m=lvRe.exec(a.className);
			if(!m) continue;
			var sp=a.getElementsByTagName('span');
			if(sp.length==0) continue;

			if(!idDone) { a.id='levelIcons'; idDone=true; }
			sp[0].style.color='#cc0';
			//sp[0].innerHTML='<center>'+m[1]+'</center>';
			sp[0].innerHTML='&nbsp;'+m[1]+owner;
		}

	},

	AttachXYPaste:function(xId,yId,func) {
		var x=ById(xId);
		if(!x) {
			this.Log('Cannot find x coord box: '+xId);
			return;
		}
		var attached=x.getAttribute('KOCpasteAttached');
		if(attached) return;
		x.setAttribute('maxlength','20');
		
		var onchange=function() {
			var xValue=x.value.trim();
			var xI=/^\s*([0-9]+)[\s|,|-|.]+([0-9]+)/.exec(xValue); 		
			if(xI) {
				var y=ById(yId);
				x.value=xI[1]
				y.value=xI[2]

				if(func!=undefined) func(xI[0],xI[1]);
			}
		}
		x.setAttribute('KOCpasteAttached',true);
		x.addEventListener('keyup',function() { onchange(); },false);
		x.addEventListener('change',function() { onchange(); },false);
	},


	GetClosestAttacks:function(x,y) {
		var attacks=[];
		var t=this;
		this.IterateAttacks(function(name,value) {
			var nameI=name.split('_');
			if(nameI.length<3) return;
			var xy=nameI[2].split(',');
			if(value=="") return;
			var attack=JSON2.parse(value);
			if(!attack) return;
			if(attack.ignore) return;
			
			var dist=t.CalcXYDist({'x':xy[0],'y':xy[1]},{'x':x,'y':y});
			if (dist==0) { return; } // Don't attack yourself
			if(dist>=t.options.attackMaxDistance) { return; }
			if(t.options.lockAttackFromCity && attack.fromCity!=unsafeWindow.currentcityid) { return; }
			attacks.push({'dist':dist,'x':xy[0],'y':xy[1],'a':attack});
		});
		attacks.sort(function(a,b) {
			return a.dist-b.dist;
		});
		return attacks;
	},

	IsEnoughTroops:function(currentTroops,neededTroops) {
		for(var t=0; t<neededTroops.length; t++) {
			if(!neededTroops[t]) continue;
			if(parseInt(neededTroops[t])>parseInt(currentTroops[t])) {
				return false;
			}
		}
		return true;
	},

	IsEnoughResources:function(currentResources,neededResources) {
		for(var t=0; t<neededResources.length; t++) {
			if(!neededResources[t]) continue;
			if((parseInt(neededResources[t])+parseInt(this.options.transportResourcesReserveAmount))>parseInt(currentResources[t])) {
				return false;
			}
		}
		return true;
	},

	currentMarchesNum:0,
	available_marches_num:0,
	DetermineCurrentMarchesNum:function() {
		var marchesnum = 0;
		var troopactivity = ById("untqueue_list");
		if(troopactivity && troopactivity.style.display!="none" && troopactivity.style.visibility!="hidden"){
			marchesnum = troopactivity.childNodes.length;
		}
		//this.Log("Current number of marches in this city: "+marchesnum);
		this.currentMarchesNum = marchesnum;
		return marchesnum;
	},

	currentRallyPointLevel:0,
	DetermineCurrentRallyPointLevel:function() {
		var rallypointlevel = 0;
		var citymap = ById("citymap");
		if(citymap){
			var citylinks = nHtml.FindByXPath(citymap,'.//a[contains(@class, "bldg")]', XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
			for (var i = 0; i < citylinks.snapshotLength; i++){
				var building = citylinks.snapshotItem(i);
				var style = window.getComputedStyle(building, false);
				var css_bgimg = style.backgroundImage;
				//this.Log("building background image "+i+": "+css_bgimg);
				var myregexp = /rally_point_lvl/i;
				var match = myregexp.exec(css_bgimg);
				if (match != null) {
					// Determine the rally point level based on the level tag overlay
					var leveltag = nHtml.FindByXPath(building,'.//span[contains(@class, "level")]');
					if(!leveltag){ continue; }
					var levelnum = parseInt(leveltag.innerHTML);
					if(levelnum>rallypointlevel){
						rallypointlevel = levelnum;
					}
				} else {
					var b_class = building.className.split("_");
					if(b_class != null){
						if(parseInt(b_class[1]) == 12){
							rallypointlevel = parseInt(b_class[2]);
							// this.Log("classname "+b_class[1] +" "+b_class[2]);
						}
					}
				}
			}
			if(rallypointlevel==0){
				// Couldn't find the rally point based on the background image. Odds are that the rally point exists,
				// but is currently under construction. Let's see if we can deduce the rally point's level based on this assumption.
				// Note: This will bug out if the city has literally no rally point at all, but who's really that stupid?
				for (var i = 0; i < citylinks.snapshotLength; i++){
					var building = citylinks.snapshotItem(i);
					var style = window.getComputedStyle(building, false);
					var css_bgimg = style.backgroundImage;
					//this.Log("building background image "+i+": "+css_bgimg);
					var myregexp = /construction/i;
					var match = myregexp.exec(css_bgimg);
					if (match != null) {
						// Determine the rally point level based on the level tag overlay
						var leveltag = nHtml.FindByXPath(building,'.//span[contains(@class, "level")]');
						if(!leveltag){ continue; }
						var levelnum = parseInt(leveltag.innerHTML);
						if(levelnum>rallypointlevel){
							rallypointlevel = levelnum-1;
						}
					}
				}
			}
			this.Log("Rally point level in this city is: "+rallypointlevel);
		}
		if(rallypointlevel==12) rallypointlevel = 11;
		this.currentRallyPointLevel = rallypointlevel;
		return rallypointlevel;
	},

	GetLevelInfo:function(attack) {
		if(!attack) throw('GetLevelInfo: attack is null');
		var levelI=attack.levelInfo;
		//if((!levelI || levelI.level==0) && attack.messages && attack.messages.length>0) {
		if((!levelI || levelI.level==0 || levelI.type=="" || levelI.type=="Camp") && attack.messages && attack.messages.length>0) { //The idea is to replace all old attacks with the new one
			levelI=this.FindLevelFromMessages(attack);
		}
		
		if (!levelI && (attack.currenttattackwavetype == 'transport')){
		  return {'type':'Transport','level':0};
		}
		// if (!levelI){
		  // return {'type':'Camp','level':0}; // Default to camp
		// }
		//All old attacks for city are stored as "" So disable this option for the mean time
		//if(levelI.type==""){levelI.type="Camp";}
		return levelI;
	},

	GetAttackDelay:function(attack) {
		if(!attack) throw('GetAttackDelay: attack is null');
		var attackDelay = 0;
		var wilderness={
			'Lake':1,
			'Mountains':1,
			'Woods':1,
			'Forest':1,
			'Plain':1,
			'Hills':1,
			'Grassland':1,
			'Wilderness':1 // Unknown wilderness type
		};
		var levelInfo=this.GetLevelInfo(attack);
		if(!levelInfo) {
			this.Log("Unable to calculate attack delay: Missing level info for coordinates ("+attack.x+","+attack.y+"). Assuming delay of 0.");
		}else{
			if(levelInfo.type=='Camp') {
				// Camp
				attackDelay = this.options.attackSecsSinceLastCamp;
			} else if(wilderness[levelInfo.type]) {
				// Wilderness
				attackDelay = this.options.attackSecsSinceLastWild;
			} else if (levelInfo.type!='Camp' && !wilderness[levelInfo.type]) {
				var tempAttack=this.GetAttack(attack.x,attack.y);
				if(tempAttack){
					if(tempAttack.type==0) {
						// City
						attackDelay = this.options.attackSecsSinceLastCity;
					} else if(tempAttack.type==1) {
						// Transport
						attackDelay = this.options.attackSecsSinceLastTransport;
					}else{
						this.Log("Unknown attack type for ("+attack.x+","+attack.y+"). Assuming delay of 0.");
					}
				}else{
					this.Log("Unknown/Invalid attack specified ("+attack.x+","+attack.y+").");
				}
			}else{
				this.Log("Unknown attack type for ("+attack.x+","+attack.y+"). Assuming delay of 0.");
			}
		}
		return attackDelay;
	},
	
	priorityattack:true,
	FindBiggestCampAttack:function(attacks,currentTroops,currentResources) {
		var bestAttack=null;
		var bestAttackSize=0;
		var bestAttackDist=10000;
		var nowSecs=new Date().getTime()/1000;
		var currentCity = this.GetCurrentCityId();
		// Determine the current rally point level and current marches
		this.DetermineCurrentRallyPointLevel();
		this.DetermineCurrentMarchesNum();
		this.Log("Current Rally Point Level: "+this.currentRallyPointLevel+" Current Marches: "+this.currentMarchesNum);

		var wilderness={
			'Lake':1,
			'Mountains':1,
			'Woods':1,
			'Forest':1,
			'Plain':1,
			'Hills':1,
			'Grassland':1,
			'Wilderness':1 // Unknown wilderness type
		};
		for(var a=0; a<attacks.length; a++) {
			var attack=attacks[a];
			//this.Log("Inspecting attack #"+a+": "+inspect(attack,10));
			var levelInfo=this.GetLevelInfo(attack.a);
			if(!levelInfo) {
				this.Log("Not attacking: Missing level info! for coordinates ("+attack.x+","+attack.y+")");
				continue;
			}
			
			if(levelInfo.type == ""){levelInfo.type = this.FindLevelFromMessages(attack);;} // If empty look in messages
			if(levelInfo.type == ""){levelInfo.type = "City";} // If still blanck default to city
			
			if(levelInfo.type=='Camp') {
				if(this.options.attackTypeCamp!=true) {
					this.Log("Not attacking: Not attacking camps! for coordinates ("+attack.x+","+attack.y+")");
					continue;
				}
			} else if(wilderness[levelInfo.type]) {
				if(this.options.attackTypeWild!=true) {
					this.Log("Not attacking: Not attacking wildernesses! for coordinates ("+attack.x+","+attack.y+")");
					continue;
				}
			} else if (levelInfo.type!='Camp' && !wilderness[levelInfo.type]) {
				var tempAttack=this.GetAttack(attack.x,attack.y);
				if(!tempAttack){
					this.Log("Not attacking: Invalid attack entered ("+attack.x+","+attack.y+")");
					continue;
				}
				if(tempAttack.type==0) {
					if(this.options.attackTypeCity!=true) {
						this.Log("Not attacking: Not attacking cities for coordinates ("+attack.x+","+attack.y+")");
						continue;
					}
				} else if(tempAttack.type==1) {
					if(this.options.attackTypeTransport!=true) {
						this.Log("Not attacking: Not sending transports for coordinates ("+attack.x+","+attack.y+")");
						continue;
					}
				}
			} else {
				if(levelInfo.type!='City' && levelInfo.type!='Camp') {
					this.Log("Not attacking: Unknown attack type for coordinates ("+attack.x+","+attack.y+")");
					continue;
				}
			}

			if(!attack.a.troops) {
				this.Log("inspect1: "+inspect(attack.a),10);
				this.Log("Not attacking: No troops defined for coordinates ("+attack.x+","+attack.y+")");
				continue;
			}
			if(attack.a.ignore) {
				this.Log("Not attacking: Location ignored for coordinates ("+attack.x+","+attack.y+")");
				continue;
			}
			if(attack.dist>=this.options.attackMaxDistance) {
				this.Log("Not attacking: Distance too far for coordinates ("+attack.x+","+attack.y+")");
				continue;
			}
			if(this.options.lockAttackFromCity) {
				//this.Log('fromCity='+attack.a.fromCity+', currentCity='+this.GetCurrentCityId());
				if(attack.a.fromCity!=undefined) {
					if(currentCity!=attack.a.fromCity) { continue; }
				}
			}
			
			var troops_to_send = attack.a.troops;
			if(attack.a.suicidewave){
				// count up all the troops in *both* attack waves.
				// we don't want to launch suicide wave if we can't follow it up.
				var suicide_troops = attack.a.suicidewave;
				for(var t=0; t<suicide_troops.length; t++) {
					if(!suicide_troops[t]) continue;
					troops_to_send[t] = troops_to_send[t] + suicide_troops[t];
				}
			}

			//if(!this.currentMarchesNum || this.currentMarchesNum==undefined) {
			//	this.Log("Not attacking: Unable to determine current number of marches (refresh your window?) for coordinates ("+attack.x+","+attack.y+")");
			//	continue;
			//}
			
			// Make sure we have more than two available slots in attack queue if this is a suicide wave (unless there are only two slots even allowed)
			this.available_marches_num = this.currentRallyPointLevel - this.currentMarchesNum - this.options.rallyKeep;
			//this.Log("Available marches: "+available_marches_num);
			if(attack.a.suicidewave && attack.a.currenttattackwavetype != "normal"){
				//this.Log("Current attack wave type: "+attack.a.currenttattackwavetype);
				if(this.available_marches_num < 2 || this.currentRallyPointLevel < 2){
					// Make sure this is the first wave of the multi-wave attack and then don't send it if there aren't enough marching slots for both waves
					if(this.IsFirstAttackAtLocation(attack.x, attack.y)) {
						// This is the first wave
						this.Log("Not attacking: Not enough available marching slots at rally point to launch both suicide wave and second wave for coordinates ("+attack.x+","+attack.y+")");
						break;
					}
				}
			}
			
			// Make sure we have at least one available slot in attack queue for normal attack
			if(this.available_marches_num<1){
				this.Log("Not attacking: Not enough marching slots at rally point to launch attack for coordinates ("+attack.x+","+attack.y+")");
				break;
			}
			
			if(!this.IsEnoughTroops(currentTroops,troops_to_send)) {
				this.Log("Not attacking: Not enough troops for coordinates ("+attack.x+","+attack.y+")");
				continue;
			}
			if (attack.a.type==1) {
				if(!this.IsEnoughResources(currentResources,attack.a.resources)) {
					this.Log("Not attacking: Not enough resources for coordinates ("+attack.x+","+attack.y+")");
					continue;
				}
			}
			var lastAttack;
			if(!attack.a.time) {
				lastAttack=60*60*24*31;
			} else {
				lastAttack=nowSecs-parseInt(attack.a.time);
			}
		
			if(levelInfo.type=='' || levelInfo.type=='City' || levelInfo.type=='Transport') {
				if(attack.a.type==0) {
					if(lastAttack<this.options.attackSecsSinceLastCity) {
						this.Log("Not attacking: last attack ("+lastAttack+") < this.options.attackSecsSinceLastCity ("+this.options.attackSecsSinceLastCity+") for coordinates ("+attack.x+","+attack.y+")");
						continue;
					}
				} else {
					if(lastAttack<this.options.attackSecsSinceLastTransport){
						this.Log("Not attacking: last attack ("+lastAttack+") < this.options.attackSecsSinceLastTransport ("+this.options.attackSecsSinceLastTransport+") for coordinates ("+attack.x+","+attack.y+")");
						continue;
					}
				}
			} else if(levelInfo.type=='Camp') {
				if(lastAttack<this.options.attackSecsSinceLastCamp) {
					this.Log("Not attacking: last attack ("+lastAttack+") < this.options.attackSecsSinceLastCamp ("+this.options.attackSecsSinceLastCamp+") for coordinates ("+attack.x+","+attack.y+")");
					continue;
				}
			} else  {
				if(lastAttack<this.options.attackSecsSinceLastWild) {
					this.Log("Not attacking: last attack ("+lastAttack+") < this.options.attackSecsSinceLastWild ("+this.options.attackSecsSinceLastWild+") for coordinates ("+attack.x+","+attack.y+")");
					continue;
				}
			}
			var armySize=0;
			for(var t=0; t<attack.a.troops.length; t++) {
				if(!attack.a.troops[t]) {
					continue;
				}
				armySize+=parseInt(attack.a.troops[t]);
			}
			if(attack.a.suicidewave) {
				var suicideArmySize=0;
				for(var t=0; t<attack.a.suicidewave.length; t++) {
					if(!attack.a.suicidewave[t]) {
						continue;
					}
					suicideArmySize+=parseInt(attack.a.suicidewave[t]);
				}
				if(suicideArmySize<=0) {
					this.Log('Invalid suicide wave army size (not enough!) for coordinates ('+attack.x+','+attack.y+')');
					continue;
				}
			}
			if(armySize<=0) {
				this.Log('Invalid suicide wave army size (not enough!) for coordinates ('+attack.x+','+attack.y+')');
				continue;
			}	
			

			var ok=0;
			if(this.options.attackOrder=='mostTroops') {
				if(bestAttackSize<armySize) {
					ok=1;
				}
			} else {
				if(this.options.attackOrder!='closest') {
					this.Log('Unknown order:'+this.options.attackOrder);
				}
				if(bestAttackDist>attack.dist) {  
					ok=1;  
				}
			}
			
			if((this.options.attackpriority == levelInfo.type || this.options.attackpriority == 'None') && ok && this.priorityattack){
				bestAttack=attack;
				bestAttack.type=attack.a.type;
				bestAttackSize=armySize;
				bestAttackDist=attack.dist; 
			} else if (!this.priorityattack && ok) {
				bestAttack=attack;
				bestAttack.type=attack.a.type;
				bestAttackSize=armySize;
				bestAttackDist=attack.dist; 
			}
		}
		if(bestAttack == null && this.priorityattack){
			this.priorityattack = false;
			bestAttack = this.FindBiggestCampAttack(attacks,currentTroops,currentResources);
		} else {
			this.priorityattack = true;
		}
		return bestAttack;
	},
	
	IsMapperRunning:function() {
		if(ById('SendMap')) {
			this.Log('mapper is running, do not auto attack');
			return true;
		}
		return false;
	},
	GetAutoAttack:function() {
		var aStr=this.GetValue('AutoAttack','');
		if(aStr=='') {
			return null;
		}
		try {
			return JSON2.parse(aStr);
		} catch(e) {
			this.Log('failed to parse autoattack: '+aStr);
			this.SetValue('AutoAttack','');
			return null;
		}
	},
	SetAutoAttack:function(s) {
		if(s) {
			//this.Log("Setting Auto Attack: "+JSON2.stringify(s));
			this.SetValue('AutoAttack',JSON2.stringify(s));
		} else {
			this.SetValue('AutoAttack','');
		}
	},
	ResetAutoAttackTarget:function() {
		var autoAttack=this.GetAutoAttack();
		if(autoAttack) {
			autoAttack.x=undefined;
			autoAttack.y=undefined;
			this.SetAutoAttack(autoAttack);
		}
	},

	GetCurrentMapCoord:function() {
		// Check to see if we are on map screen
		var mapbutton = ById('mod_views_map');
		if(mapbutton && mapbutton.className=='sel'){
			// Return xy values from form inputs
			var xcoord=ById('mapXCoor');
			var ycoord=ById('mapYCoor');
			if(xcoord && ycoord){
				return {'x':xcoord.value,'y':ycoord.value};
			}
		}
		// Not on map screen. Return coordinates of current city
		var cities=this.GetSeed().cities;	
		for(i=0; i<cities.length; i++){
			if(cities[i][0]==unsafeWindow.currentcityid){
				return {'x':cities[i][2],'y':cities[i][3]};
				break;
			}
		}
	},

	hrsInput:null,
	viewTypeOnly:"",
	expandedInfo:false,
	DrawClosestFarms:function() {
		this.SetAttackStatusMessage();
		var t=this;

		//unsafeWindow.statusupdate();
		var bookmark=ById('maparea_map');
		if(!bookmark) throw("Cannot find bookmark box");
		bookmark=nHtml.FindByXPath(bookmark.parentNode,".//div[@class='coords']");

		var div=ById('ClosestFarms');
		if(!div) {
			div=document.createElement('div');
			var titleA=document.createElement('a');
			titleA.innerHTML='Attacks ';
			titleA.title='Closest attacks, more than ? hrs since last attack';
			titleA.style.cursor='pointer';
			titleA.addEventListener('click',function() {
				div.style.display=div.style.display=='block'?'none':'block';
				setTimeout(function() {
					t.SetValue('ClosestFarmDisplay',div.style.display);
				},0);
			},false);
			
			var viewTypeOnlyInp=document.createElement('input');
			viewTypeOnlyInp.style.width='10px';
			viewTypeOnlyInp.style.fontSize="8px";
			viewTypeOnlyInp.title="Type of target. ex: P, W, M, P1";
			this.hrsInput=document.createElement('input');
			this.hrsInput.style.width='16px';
			this.hrsInput.value=this.GetMinHours();
			var hrsChanged=function() {
				var v=parseFloat(t.hrsInput.value);
				t.viewTypeOnly=viewTypeOnlyInp.value;
				if(v!=undefined && v!=NaN) {
					t.SetMinHours(t.hrsInput.value);
					t.DrawClosestFarms();
				}
			}
			this.hrsInput.addEventListener('change',function() {
				hrsChanged();
			},false);
			this.hrsInput.addEventListener('keyup',function() {
				hrsChanged();
			},false);
			viewTypeOnlyInp.addEventListener('keyup',function() {
				hrsChanged();
			},false);
			
			bookmark.appendChild(document.createElement('br'));
			bookmark.appendChild(titleA);
			AddText(bookmark,'>');
			bookmark.appendChild(this.hrsInput);
			AddText(bookmark,'hrs ');
			
			var expandA=document.createElement('a');
			expandA.innerHTML='E';
			expandA.title='Expand information';
			expandA.addEventListener('click',function() {
				t.expandedInfo=t.expandedInfo?false:true;
				t.DrawClosestFarms();
			},false);

			bookmark.appendChild(viewTypeOnlyInp);
			bookmark.appendChild(expandA);
			bookmark.appendChild(document.createElement('br'));
		}
		div.innerHTML='';
		div.id='ClosestFarms';
		div.style.overflow='scroll';
		div.style.height='200px';
		div.style.display=this.GetValue('ClosestFarmDisplay','block');

		var currentTroops=this.GetArmySize();
		var currentResources=this.GetResourcesSize();

		var mapCoord=this.GetCurrentMapCoord();
		var nowSecs=new Date().getTime()/1000;
		var attacks=this.GetClosestAttacks(mapCoord.x,mapCoord.y);
		//var bestAttack=this.FindBiggestCampAttack(attacks,currentTroops,currentResources);
		var GoClosestFarm=function(e) {
			var xy=e.target.parentNode.parentNode.getAttribute('xy').split(',');
			t.DoUnsafeWindow("setBookmarkCoord("+xy[0]+","+xy[1]+");");
		};
		var AttackClosestFarm=function(e) {
			var xy=e.target.parentNode.parentNode.getAttribute('xy').split(',');
			var attack=t.GetAttack(xy[0],xy[1]);
			if(!attack) throw("Cannot find:"+xy[0]+','+xy[1]);
			if(attack.type==0) {
				t.DoUnsafeWindow("modal_attack(4,"+xy[0]+","+xy[1]+");");
			} else if(attack.type==1) {
				t.DoUnsafeWindow("modal_attack(1,"+xy[0]+","+xy[1]+");");
			}
		};
		var IgnoreFarm=function(e) {
			var xy=e.target.parentNode.parentNode.getAttribute('xy').split(',');
			var attack=t.GetAttack(xy[0],xy[1]);
			if(!attack) throw("Cannot find:"+xy[0]+','+xy[1]);
			attack.ignore=true;
			t.SetAttack(xy[0],xy[1],attack);
		};
		var DeleteFarm=function(e) {
			var xy=e.target.parentNode.parentNode.getAttribute('xy').split(',');
			t.DeleteAttack(xy[0],xy[1]);
			t.DrawClosestFarms();
		};
		
		var aDone=0;
		var table=document.createElement('table');
		table.className='';
		var minHrsSinceAttack=parseFloat(this.hrsInput.value);

		var viewType=this.viewTypeOnly.trim().toUpperCase();

		for(var aUpto=0; aUpto<attacks.length; aUpto++) {
			var attackI=attacks[aUpto];
			try {
				//attackI.a.levelInfo=this.FindLevelFromMessages(attackI.a);
				//this.SetAttack(attackI.x,attackI.y,attackI.a);
				
				if(!attackI.a.time) {
					lastAttack=60*60*24*31;
				} else {
					lastAttack=nowSecs-parseInt(attackI.a.time);
				}
				if(lastAttack<(60*60*minHrsSinceAttack)) continue;

				var levelI=this.GetLevelInfo(attackI.a);
				var m=( (levelI!=null && levelI.type) ?(levelI.type.charAt(0)+levelI.level):'');
				//this.Log("Location Type: "+m);
				if(this.viewTypeOnly!="") {
					if(this.viewTypeOnly==" ") {
						if(m!="") {
							continue;
						}
					} else if(viewType.length==0 || m.substring(0,viewType.length)!=viewType) {
						continue;
					}
				}

				var dist=this.CalcXYDist({'x':attackI.x,'y':attackI.y},mapCoord);
				if(dist>=this.options.attackMaxDistance) {
					break;
				}
				
				var tr=table.insertRow(-1);
				tr.setAttribute('xy',attackI.x+','+attackI.y);
				var td=tr.insertCell(-1);
				td.style.whiteSpace='nowrap';

				var a=document.createElement('a');
				a.style.fontSize='10px';
				a.innerHTML=attackI.x+','+attackI.y;
				a.addEventListener('click',function(e) { GoClosestFarm(e); },false);
				td.appendChild(a);

				AddText(td,' ');
				//td=tr.insertCell(-1);

				var attackA=document.createElement('a');
				attackA.style.fontSize='10px';
				/* ~~~ disabled: incorrect numbers due to having to wait for attack to update.
				if(bestAttack && attackI.x==bestAttack.x && attackI.y==bestAttack.y) {
					attackA.style.color='#f88';
				}
				*/
				attackA.title=(levelI!=null?(levelI.type+' '+levelI.level):'');
				if(attackI.a.comment) {
					attackA.title+=", "+attackI.a.comment;
				}
				if(attackI.a.time && !isNaN(lastAttack)) {
					// if we only scouted or transported to someone they won't have a last attack time.
					m=m+' @'+SecsToStr(lastAttack);
				}
				attackA.innerHTML=m;
				attackA.addEventListener('click',function(e) { AttackClosestFarm(e); },false);
				td.appendChild(attackA);		

				if(t.expandedInfo) {
					var troops=attackI.a.troops;
					if(troops) {
						for(var tupto=0; tupto<troops.length; tupto++) {
							var num=troops[tupto];
							if(attackI.a.suicidewave) {
								var am=attackI.a.suicidewave[tupto];
								if(am) num+=" ("+am+")";
							}
							AddText(tr.insertCell(-1),num);
						}
					}
				}
				
				var aDelete=document.createElement('a');
				aDelete.innerHTML='X';
				aDelete.title='Delete';
				aDelete.addEventListener('click',function(e) { DeleteFarm(e); },false);
				tr.insertCell(-1).appendChild(aDelete);
			
				aDone++;
			} catch(e) {
				this.Log('Error:'+e);
			}

		}

		div.appendChild(table);
		
		//bookmark.parentNode.insertBefore(div,bookmark);
		//bookmark.parentNode.appendChild(document.createElement('br'));
		bookmark.appendChild(div);

		this.AttachXYPaste('mapXCoor','mapYCoor');
	},


	// ?,1,square type, level, player id?, 
	// target player name, target gender, player name, player gender, ?, target x, target y, report id, 0, x y
	onclickReportRe:/(["0-9]+),(["0-9]+),(["0-9]+),(["0-9]+),(["0-9]+),("[^"]+"),("[^"]+"),("[^"]+"),("[^"]+"),(["0-9]+),(["0-9]+),(["0-9]+),/,
	//modal_messages_viewreports_view("897422",0,51,9,2485358,"niknah","M","niknah","M",1,333,110,1275178036,1,286,181);

	// remove the read/unread flag
	onclickReadRe:/^(.*,)([0-9]+)(,[0-9]+,[0-9]+[^,]*)$/,
	FixOnClick:function(a) {
		var m=this.onclickReadRe.exec(a);
		if(m) {
			return m[1]+"0"+m[3];
		}
		return a;
	},



	IterateArmy:function(f) {
		if(!this.GetSeed()) return;
		var armyDiv=ById('cityinfo_3');
		var units=this.GetSeed().units["city"+unsafeWindow.currentcityid];
		//var unitKeys=Object.keys(units);
		//for(var u=0; u<unitKeys; u++) {
		var uRe=/([0-9]+)$/;
		for(var u in units) {
			var m=uRe.exec(u);
			if(!m) continue;
			f.call(this,m[1],units[u]);
		}
		/*
		var ss=document.evaluate(".//div[@class='unit']",armyDiv,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
		for(var s=0; s<ss.snapshotLength; s++) {
			var unit=ss.snapshotItem(s);
			var amt=unit.textContent;
			f.call(s,amt);
		}
		*/
	},

	GetArmySize:function() {
		var troops=[];
		this.IterateArmy(function(s,amt) {
			troops[s]=amt;
		});
		return troops;
	},

	GetResourcesSize:function() {  
		var resources=[];  
		resources[0]=parseInt(ById('stat_gold_bar_num').innerHTML.replace(/,/g,'')); // Gold  
		resources[1]=parseInt(ById('stat_rec1_bar_num').innerHTML.replace(/,/g,'')); // Food  
		resources[2]=parseInt(ById('stat_rec2_bar_num').innerHTML.replace(/,/g,'')); // Wood  
		resources[3]=parseInt(ById('stat_rec3_bar_num').innerHTML.replace(/,/g,'')); // Stone  
		resources[4]=parseInt(ById('stat_rec4_bar_num').innerHTML.replace(/,/g,'')); // Ore  
		return resources;  
	},

	OpenViewReports:function() {
		this.Log('Open View Reports');
		this.DoUnsafeWindow("modal_messages();");
		this.DoUnsafeWindow('track_chrome_btn("messages_btn");');
		this.DoUnsafeWindow('modal_messages_viewreports();');
	},

	//autoAttackCitiesDoneMax:3,
	autoAttackCitiesDone:0,
	autoAttackCityUpto:1,
	autoAttackModalWaiting:false,
	autoAttackTimeout:null,
	autoAttacksThisCity:0,
	ClearAutoAttackTimeout:function() {
		if(this.autoAttackTimeout!=null) {
			this.Log('reload page timer killed');
			window.clearTimeout(this.autoAttackTimeout);
			this.autoAttackTimeout=null;
		}
	},
	RestartAutoAttack:function() {
		this.autoAttacksThisCity=0;
		this.autoAttackCitiesDone=0;
		this.autoAttackCityUpto=1;
		this.autoAttackModalWaiting=false;
		this.NextAutoAttack();
	},

	StartReloadPageTimer:function(secs) {
		var t=this;
		if(!secs) secs=this.options.waitAfterCitiesDone;
		var refreshMSecs=t.GetRandTime(1000*secs);
		this.Log('refreshing in '+(refreshMSecs/1000)+' secs, all cities done:'+this.autoAttackCityUpto);
		this.ClearAutoAttackTimeout();
		this.autoAttackTimeout=window.setTimeout(function() {
			if(t.autoAttackTimeout==null) return;
			t.autoAttackTimeout=null;
			if(t.IsMapperRunning() || t.IsCurrentlySendingMultipleWaves()) {
				if(t.IsMapperRunning()){
					t.Log("Waiting for mapping to finish");
				}else{
					t.Log("Waiting for multiple wave attack to finish");
				}
				// don't reload until the mapper or multi-wave attack has finished.
				window.setTimeout(function() {
					t.StartReloadPageTimer();
				},0);
				return;
			}
			t.SetValuesCache();
			//window.location.reload(true);
			t.ReloadWindow();
		},refreshMSecs);
		this.Log('reload page timer started');
	},
	
	multipleWaveTimeout:null,
	ClearMultipleWaveTimeout:function() {
		if(this.multipleWaveTimeout!=null) {
			this.Log('city switching timer killed');
			window.clearTimeout(this.multipleWaveTimeout);
			this.multipleWaveTimeout=null;
		}
	},
	StartMultipleWaveTimer:function(secs) {
		var t=this;
		if(!secs) secs=t.options.attackDelay;
		var attackDelayMSecs=t.GetRandTime(1000*secs);
		this.Log('Waiting '+(attackDelayMSecs/1000)+' secs to retry second wave attack...');
		this.ClearMultipleWaveTimeout();
		this.multipleWaveTimeout=window.setTimeout(function() {
			if(t.multipleWaveTimeout==null) return;
			t.multipleWaveTimeout=null;
			if(t.IsCurrentlySendingMultipleWaves()) {
				t.Log("Waiting for multiple wave attack to finish...");
				// don't switch cities until the multi-wave attack has finished.
				window.setTimeout(function() {
					t.StartMultipleWaveTimer();
				},0);
				return;
			}
			t.NextAutoAttackCity();
		},attackDelayMSecs);
		this.Log('Multiple wave attack timer started');
	},

	lastOpenViewReports:0,
	CheckReports:function() {
		var t=this;
		var autoAttack=this.GetAutoAttack();
		if(!autoAttack) return;
		
		// Load the reports every minute or on initial page load
		if(!this.options.noViewReports) {
			var nowSecs=new Date().getTime()/1000;
			if((this.lastOpenViewReports+(60*1))<nowSecs) {
				this.lastOpenViewReports=nowSecs;
				this.OpenViewReports();
				this.bringUpReports=true;
				if(this.options.autoRemoveReports || this.options.autoRemoveFarmReports) {
					this.autoAttackRemoveReports=true;
				}
			}
		}
	},

	ClickChangeCity:function(cityA,tries) {
		var t=this;
		nHtml.Click(cityA);
		// If this is the only city selected in the options, we just pretend to switch
		var currentCityNum = this.autoAttackCityUpto;
		var enabledCities = 0;
		for(var i=1; i<=8; i++){
			if(this.options.okCities[i]){
				enabledCities++;
			}
		}
		//t.Log("Number of enabled cities to attack from: "+enabledCities);
		if(enabledCities==1){ t.prevCurrentCity = -1; }
		// Determine the number of the city and store it
		t.SetCurrentAttackCityNum(cityA.id.replace("citysel_",""));
		t.nextAutoAttackWanted=window.setTimeout(function() {
			if(t.nextAutoAttackWanted!=null) {
				if(tries>4) {
					t.Log("Skip city, too many retries");
					t.NextAutoAttackCity();
					return;
				}
				t.Log("We clicked change city but the city did not change, trying again");
				// Didn't change city
				t.ClickChangeCity(cityA,tries+1);
			}
		},10000);
	},


	NextAutoAttackCity:function() {
		var t=this;
		var autoAttack=this.GetAutoAttack();
		if(!autoAttack) return;
	
		if(this.IsCurrentlySendingMultipleWaves()){
			this.Log('Cannot change city. Waiting for multiple wave attack to finish...');
			t.ClearMultipleWaveTimeout();
			t.StartMultipleWaveTimer();
			return;
		}else{
			t.ClearMultipleWaveTimeout();
		}
	
		// change to next city
		this.autoAttacksThisCity=0;

		var cityA=null;

		// this.autoAttackCityUpto++;
		// cityA=ById('citysel_'+this.autoAttackCityUpto);
		
		// //~~~ problem here when under attack, the city isn't marked as selected?
		// // Skip to next city along the line if the current city is already selected somehow
		// if(cityA && cityA.className && cityA.className.indexOf('city_selected')>=0) {
			// this.Log('Skip city, current city:'+this.autoAttackCityUpto);
			// this.autoAttackCityUpto++;
		// }

		// // Determine the next enabled city in the options
		// var firstAvailableCityNum = this.autoAttackCityUpto;
		// while(firstAvailableCityNum<=8){
			// if(this.options.okCities[firstAvailableCityNum]){
				// break;
			// }else{
				// firstAvailableCityNum++;
			// }
		// }
		// // Default to city #1 if no cities are enabled
		// if(!this.options.okCities[firstAvailableCityNum]){
			// firstAvailableCityNum = 1;
		// }
		
		// // Check to make sure that the next city is selected in the options
		// //this.Log("Currently attempting to switch to city: "+this.autoAttackCityUpto);
		// var currentCityNum = this.autoAttackCityUpto;
		// while(currentCityNum<=8){
			// //this.Log("Is City "+currentCityNum+" Selected in options: "+this.options.okCities[currentCityNum]);
			// if(!this.options.okCities[currentCityNum]){
				// currentCityNum++;
			// }else{
				// if(currentCityNum>this.autoAttackCityUpto){
					// this.Log("City "+this.autoAttackCityUpto+" not enabled in options.");
					// this.autoAttackCityUpto = currentCityNum;
				// }
				// //this.Log("Attempting to switch to city: "+this.autoAttackCityUpto);
				// break;
			// }
			// // No cities were available. Switching to first available city
			// this.autoAttackCityUpto = firstAvailableCityNum;
		// }

		while(true) {
			this.autoAttackCityUpto++;
			cityA=ById('citysel_'+this.autoAttackCityUpto);
			//~~~ problem here when under attack, the city isn't marked as selected?
			if((cityA && cityA.className && cityA.className.indexOf('city_selected')>=0) || (!this.options.okCities[this.autoAttackCityUpto])) {
				this.Log('Skip city, current city or city not selected: '+this.autoAttackCityUpto);
			} else {
				this.Log('Attempting to select this city: '+this.autoAttackCityUpto);
				cityA=ById('citysel_'+this.autoAttackCityUpto);
				break;
			}
		}
	
		cityA=ById('citysel_'+this.autoAttackCityUpto);
		if(!cityA) {
			// go back to the 1st available city
			this.Log('Start from 1st city again');
			if(this.autoAttackCityUpto<=2) {
				// only one city.
			} else {
				this.autoAttackCityUpto=1;
				cityA=ById('citysel_'+this.autoAttackCityUpto);
			}
			this.autoAttackCitiesDone++;
		}

		if(!cityA || this.autoAttackCitiesDone>=this.options.autoAttackCitiesDoneMax) {
			//Reset to first city if auto attack does the refresh
			this.SetCurrentAttackCityNum(1);
			// ran out of cities, let's refresh in a minute
			this.StartReloadPageTimer();
			return;
		} else {
			if(this.nextAutoAttackTimeout==null) {
				var secs=t.GetRandTime(1000*t.options.changeCitySecs);
				this.Log('Changing city to: '+this.autoAttackCityUpto+', in '+(secs/1000)+'secs, loop: '+this.autoAttackCitiesDone);
				this.nextAutoAttackTimeout=setTimeout(function() {
					t.nextAutoAttackTimeout=null;
					t.SetValuesCache();
					t.ClickChangeCity(cityA,0);
				},secs);
			} else {
				this.Log('Cannot change city. Reason: about to attack or change city');
			}
	//		this.autoAttackCityUpto++;
		}
	},


	StopWaitForAttackBoxAppear:function() {
		if(this.waitForAttackBoxAppear!=null) {
			window.clearTimeout(this.waitForAttackBoxAppear);
			this.waitForAttackBoxAppear=null;
		}
	},

	OpenAttackDialog:function(bestAttack) {
		var t=this;
		t.StopWaitForAttackBoxAppear();
		t.waitForAttackBoxAppear=window.setTimeout(function() {
			if(t.waitForAttackBoxAppear==null) return;
			t.waitForAttackBoxAppear=null;
			t.DoUnsafeWindow('Modal.hideModalAll();');
			t.DoUnsafeWindow('Modal.hideModalAll();');
			try {
				// something in the script is triggering a bug in 
				t.DoUnsafeWindow('Modal.hideCurtain();');
				t.DoUnsafeWindow('Modal.hideWindow();');
			} catch(e) {
				// ignore
			}
			//window.location.reload(true);
			t.ResetIdStatus();
			t.Log("Attack box has not appeared, let's reopen");
			window.setTimeout(function() {
				t.OpenAttackDialog(bestAttack);
			},0);
		},5*1000);
		
		//~~~ strange things here, sometimes the attack dialog is on screen but modalid is not updated
		if(ById('modal_attack')) {
			try {
				var countOut=10;
				while(countOut-->=0) {
					t.Log('modal attck still up'+unsafeWindow.Modal.modalid);
					var maxi=0;
					for(var i=0; i<20; i++) {
						if(ById('modalBox'+i)) {
							maxi=i;
						}
					}
					t.Log('Closing: '+maxi);
					unsafeWindow.Modal.modalid=maxi;
					if(maxi>0) {
						unsafeWindow.Modal.hideCurtain();
						unsafeWindow.Modal.hideWindow();
					} else {
						break;
					}
				}
			}catch(e) {
				t.Log("Mmm..."+e);
			}
		} else {
			//t.Log('hide all: '+unsafeWindow.Modal.modalid);
			unsafeWindow.Modal.hideModalAll();
		}
		if (bestAttack.type==0) {
			unsafeWindow.modal_attack(4,bestAttack.x,bestAttack.y);
			// Toggle attack waves between suicide and normal mode
			this.ToggleCurrenttAttackWaveType(bestAttack.x,bestAttack.y);
			// Update the last attack sent time
			this.UpdateAttackLastSentTime();
		} else if (bestAttack.type==1) {
			unsafeWindow.modal_attack(1,bestAttack.x,bestAttack.y);
		}
	},

	nextAutoAttackWanted:null,
	NextAutoAttack:function() {
		this.Log("Next auto attack");
		if(this.nextAutoAttackWanted!=null) {
			window.clearTimeout(this.nextAutoAttackWanted);
			this.nextAutoAttackWanted=null;
		}

		var autoAttack=this.GetAutoAttack();

		if(!this.options.okCities[this.autoAttackCityUpto]) {
			this.Log('Skip city. Not enabled in options. City: '+this.autoAttackCityUpto);
			this.NextAutoAttackCity();
			return;
		}
		
		
		if(!autoAttack || (autoAttack.x!=undefined && autoAttack.x!=-1)) return;
		if(this.IsMapperRunning()) {
			this.StartReloadPageTimer();
			return;
		}
		this.DetermineCurrentRallyPointLevel();
		this.DetermineCurrentMarchesNum();
		this.available_marches_num = this.currentRallyPointLevel - this.currentMarchesNum - this.options.rallyKeep;
		var currentTroops=this.GetArmySize();
		var currentResources=this.GetResourcesSize();
		var mapCoord=this.GetCurrentMapCoord();
		var attacks=this.GetClosestAttacks(mapCoord.x,mapCoord.y);
		//this.Log("Current attacks in system: "+inspect(attacks,10));
		var bestAttack=this.FindBiggestCampAttack(attacks,currentTroops,currentResources);
		if(bestAttack && this.available_marches_num>0) {
			// attack closest biggest barbarian/wilderness
			autoAttack.x=bestAttack.x;
			autoAttack.y=bestAttack.y;
			this.SetAutoAttack(autoAttack);
			this.autoAttackModalWaiting=true;

			var t=this;
			// *** we need to wait until the current attack box is off first.
			var waitedCount=0;
			var startAttack=function() {
				waitedCount++;
				//t.Log('waiting'+waitedCount);
				if(waitedCount>20) {
					t.Log('Force close the attack dialog');
					t.DoUnsafeWindow('Modal.hideModalAll();');
				}
				var attackBox=document.getElementById('modal_attack');
				if(!attackBox) {
					t.OpenAttackDialog(bestAttack);
					/*
					attackBox=document.getElementById('modal_attack');
					if(attackBox) {
						//*** for some reason it doesn't trigger DOMInserted sometimes for the "modal_attack" div
						if(t.waitForAttackBoxAppear) {
							t.OnAttackBoxAppear(attackBox);
						}
					}
					*/
					return;
				}
				window.setTimeout(function() {
					startAttack();
				},1000);
			}
			startAttack();
		} else {
			autoAttack.x=autoAttack.y=-1;
			this.SetAutoAttack(autoAttack);
			// no valid attacks for this city.
			this.Log("No valid targets, need to attack more targets or wait for troops to return.");
			this.NextAutoAttackCity();
		}
	},

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

	RemoveEmptyReportsDivs:function() {
		var ss=document.evaluate("./div[@id='modal_msg_reports_tablediv']",document.body,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null);
		for(var s=0; s<ss.snapshotLength; s++) {
			var div=ss.snapshotItem(s);
			if(div.innerHTML=="") {
				div.parentNode.removeChild(div);
			}
		}
	},


	IterateAllianceReports:function(f) {
		return this.IterateReports('modal_alliance_reports_tablediv',0,f);
	},
	IterateMsgReports:function(f) {
		return this.IterateReports('modal_msg_reports_tablediv',1,f);
	},

	IterateReports:function(id,colStart,f) {
		this.RemoveEmptyReportsDivs();
		var msgs=ById(id);
		if(!msgs) return;
		var trs=msgs.getElementsByTagName('tr');
		for(var tUpto=0; tUpto<trs.length; tUpto++) {
			var tr=trs[tUpto];
			var a=nHtml.FindByXPath(tr,".//a[contains(@onclick,'modal_messages_viewreports') or contains(@onclick,'modal_alliance_report_view') or contains(@onclick,'viewMarchReport')]");
			if(!a) continue;
			if(tr.cells.length<(colStart+2)) continue;
			var descCol=tr.cells[colStart+1];
			var dateCol=tr.cells[colStart+0];
			var desc=descCol.textContent;
			var m=this.coordsRe.exec(desc);
			var x=null,y=null;
			if(m) {
				x=m[1]; y=m[2];
			}
			var onclick=a.getAttribute('onclick');
			if(onclick) {
				var m=this.onclickReportRe.exec(onclick);
				if(m) { x=m[11]; y=m[12]; }
			}
			
			if(!f.call(this,a,tr,desc,x,y)) break;
		}
	},



	recordingReports:false,
	replaceReturnRe:/return[^{}]*$/,
	RecordReports:function() {
		try {
			if(this.recordingReports) return;
			this.recordingReports=true;
			this.IterateMsgReports(function(a,tr,desc,x,y) {
				if(x==null || y==null) return true;
				var onclick=a.getAttribute('onclick').trim();
				if(this.IsOnclickMyselfToMyself(onclick)) {
					return true;
				}
				var attack=this.GetAttack(x,y);
				
				var saveReportAsAttack = true;
				if(!attack) {
					// Don't record reports as attacks if the attack doesn't already exist in the system
					var saveReportAsAttack = false;
				}
				
				if(!attack) { attack={}; }
				
				if(!attack.messages) attack.messages=[];
				var onclickWithoutReturn=onclick.replace(this.replaceReturnRe,'');
				a.href='javascript:'+onclickWithoutReturn;
				onclick=this.FixOnClick(onclick);
				var addedAlready=false;
				for(var m=0; m<attack.messages.length; m++) {
					if(attack.messages[m][1]==onclick) {
						addedAlready=true;
						break;
					}
				}
				
				var changed=false;
				if(!addedAlready) {
					attack.messages.push([desc,onclick]);
					//this.Log("attack messages: "+attack.messages);
					var li=this.GetLevelInfo(attack);
					//this.Log('type:'+li.type+',level:'+li.level);
					if(li) attack.levelInfo=li;
					changed=true;
				}
				if(!attack.levelInfo) {
					// to fix up old scripts where this didn't work.
					attack.levelInfo=this.GetLevelInfo(attack);
					if(attack.levelInfo) changed=true;
				}
				if(changed && saveReportAsAttack) {
					this.SetAttack(x,y,attack);
				}
				return true;
			});
		} finally {
			this.recordingReports=false;
		}
	},

	IsOnclickMyselfToMyself:function(onclick) {
		if(!onclick) return false;
		var m=this.onclickReportRe.exec(onclick);
		if(m && m[6]==m[8] && m[7]==m[9]) {
			return true;
		}
		return false;
	},
	DeleteWildBarbAttacks:function() {
		var deletes=0;
		this.IterateMsgReports(function(a,tr,desc,x,y) {
			var onclick=a.getAttribute('onclick');
			if(!onclick) return false;
			onclick=onclick.trim();
			var m=this.onclickReportRe.exec(onclick);
			if(this.IsOnclickMyselfToMyself(onclick)) {
				// message to myself
			} else if(m && m[5]!='0') {
				return true;
			}
			var inps=tr.getElementsByTagName('input');
			if(inps.length>=1) {
				inps[0].checked=true;
			}
			
			deletes++;
			return true;
		});
		if(deletes>0) {
			this.DoUnsafeWindow('modal_messages_reports_chkdel();');
		}
		return deletes;
	},
	DeleteFarmAttacks:function() {
		var deletes=0;
		this.IterateMsgReports(function(a,tr,desc,x,y) {
			var DisplayName = this.GetDisplayName();
			DisplayName = /([^"]+) ([^"]+)/.exec(DisplayName);
			
			var onclick=a.getAttribute('onclick');
			if(!onclick) return false;
			onclick=onclick.trim();
			var m=this.onclickReportRe.exec(onclick);
			if(this.IsOnclickMyselfToMyself(onclick)) {
				return true; //Don't select transports
			} else if(m && m[5]=='0') {
				return true; //Don't select barbs or wilds
			} else if(m && m[11]=='0' && m[12]=='0') {
				return true; //Don't select if empty
			} else if(m && m[8].StripQuotes()!=DisplayName[2])  {
				return true; //Don't select if attacker is not yourself
			}
			var attack = this.GetAttack(m[11],m[12]); //Check if attack exists in system
			if(!attack)	return true;
			if(attack.deletefarmno) return true; //Don't select if checked not to delete
			if(!attack.troops) return true; //Sometimes the script will record reports but u don't want them deleted
			var inps=tr.getElementsByTagName('input');
			if(inps.length>=1) {
				inps[0].checked=true;
			}
			
			deletes++;
			return true;
		});
		if(deletes>0) {
			this.DoUnsafeWindow('modal_messages_reports_chkdel();');
		}
		return deletes;
	},

	bringUpReports:false,
	AddCheckBarbarians:function() {
		//var msgBody=ById('modal_msg_reports');
		var msgBody=ById('modal_msg_reports_tablediv');
		if(!msgBody) return;
		
		var t=this;
		var closeReports=true;
		if(this.autoAttackRemoveReports) {
			var reportsRemoved=0;
			if(this.options.autoRemoveReports){
				reportsRemoved+=this.DeleteWildBarbAttacks();
			}
			if(this.options.autoRemoveFarmReports){
				reportsRemoved+=this.DeleteFarmAttacks();
			}
			if(reportsRemoved==0) {
				this.autoAttackRemoveReports=false;
			} else {
				closeReports=false;
			}
		}
		if(this.bringUpReports && closeReports) {
			window.setTimeout(function() {
				t.DoUnsafeWindow('Modal.hideModalAll();');
			},t.GetRandTime(3000));
			this.bringUpReports=false;
		}
		this.ClearMessages();
		
		var a=document.createElement('a');
		var t=this;
		a.addEventListener('click',function() {
			t.DeleteWildBarbAttacks();
		},false);
		a.className='buttonDown20';
		//a.style.paddingLeft='30px';
		a.innerHTML='<span>Delete Wild/Barb/Transp</span>';
		if(msgBody.nextSibling) {
			msgBody.nextSibling.insertBefore(a,msgBody.nextSibling.childNodes[0]);
			//msgBody.nextSibling.appendChild(a);
		} else {
			msgBody.appendChild(a);
		}
		
		var a=document.createElement('a');
		var t=this;
		a.addEventListener('click',function() {
			t.DeleteFarmAttacks();
		},false);
		a.className='buttonDown20';
		//a.style.paddingLeft='30px';
		a.innerHTML='<span>Delete Farm</span>';
		if(msgBody.nextSibling) {
			msgBody.nextSibling.insertBefore(a,msgBody.nextSibling.childNodes[0]);
			//msgBody.nextSibling.appendChild(a);
		} else {
			msgBody.appendChild(a);
		}
	},

	HighlightAllianceReports:function() {
		var mapCoord=this.GetCurrentMapCoord();
		var cities=this.GetSeed().cities;
		this.IterateAllianceReports(function(a,tr,desc,x,y) {
			if(x==null || y==null) return true;
			var closestDist=999999;
			var closestLoc=null;
			for(var c=0; c<cities.length; c++) {
				var city=cities[c];
				var cityLoc={'x':city[2],'y':city[3]};
				var dist=this.CalcXYDist({'x':x,'y':y},cityLoc);
				if(dist<closestDist) { closestDist=dist; closestLoc=cityLoc; }
			}
			var onclick=a.getAttribute('onclick');
			var m=this.onclickReportRe.exec(onclick);
			if(m && m[5]=='0') {
				tr.cells[1].style.color='#888';
			}
			if(closestLoc!=null) {
				var td=tr.insertCell(-1);
				td.style.textAlign='right';
				var loctd=tr.insertCell(-1);
				AddText(loctd,closestLoc.x+','+closestLoc.y);
				AddText(td,Math.floor(closestDist) );
			}
			return true;
		});
	},



	IterateAttacks:function(f) {
		if(this.isChrome) {
			return;
		}
		
		var names=this.browser_listValues();
		var attackPrefix='attack_'+this.GetServerId()+'_';
		for(var n=0; n<names.length; n++) {
			var name=names[n];
			if(name.substring(0,attackPrefix.length)!=attackPrefix) continue;
			f(name,this.browser_getValue(name));
		}
	},

	prevClearMessages:0,
	ClearMessages:function() {
		var nowSecs=new Date().getTime()/1000;
		if((this.prevClearMessages+(10*60)) > nowSecs) {
			return;
		}
		this.prevClearMessages=nowSecs;
		
		var keepReports=this.isChrome?this.options.chromeKeepReports:this.options.keepReports;
		this.Log('Clear old messages, keeping '+keepReports);
		if(keepReports<=0) { 
			// must keep at least 1 report to get level information.
			keepReports=1; 
		}
		var t=this;
		this.IterateAttacks(function(name,value) {
			var attack=JSON2.parse(value);
			if(attack.messages && attack.messages.length>keepReports) {
				attack.messages.splice(0,attack.messages.length-keepReports+1);
			}
			t.browser_setValue(name,JSON2.stringify(attack));
		});
	},


	SetupClearMessages:function() {
		var t=this;
		var cm=document.createElement('input');
		cm.type='button';
		cm.id='ClearMessages';
		cm.style.display='none';
		document.body.appendChild(cm);
		cm.addEventListener('click',function(e) {
			setTimeout(function() {
				t.ClearMessages();
			},0);
		},false);
	},

	trainTroopsOnclick:/\(([0-9]+)\)/,
	AddTrainTroopsLink:function() {
		var startObj=ById('unit_btns_start');
		if(!startObj) return;
		
		var onclick=startObj.getAttribute('onclick');
		var m=this.trainTroopsOnclick.exec(onclick);
		if(!m) {
			return;
		}
		
		var t=this;
		var type=m[1];

		var pnode=startObj.parentNode;
		var a=document.createElement('a');
		a.className='button25';
		var setTrainTroopsA=function() {
			var trainTroops=JSON2.parse(t.GetValue('TrainTroops','{}'));
			a.innerHTML='<span>'+(trainTroops[t.GetCurrentCityId()]==type?'Auto Train - On':'Auto Train - Off')+'</span>';
		}
		a.addEventListener('click',function() {
			var trainTroops=JSON2.parse(t.GetValue('TrainTroops','{}'));
			trainTroops[t.GetCurrentCityId()]=trainTroops[t.GetCurrentCityId()]==type?undefined:type;
			t.SetValue('TrainTroops',JSON2.stringify(trainTroops));
			setTrainTroopsA();
		},false);
		setTrainTroopsA();

		//pnode.appendChild(document.createElement('br'));
		AddText(pnode,' ');
		pnode.appendChild(a);
	},

	GetCurrentCityId:function() {
		if(!unsafeWindow.currentcityid) return null;
		return unsafeWindow.currentcityid;
	},
	
	GetCurrentAttackCityNum:function() {
		var curCityNum=this.GetValue('CurrentAttackCityNum',null);
		if(!curCityNum || !is_int(curCityNum) || curCityNum>8){
			// default to first city
			curCityNum=1;
		}
		return curCityNum;
	},
	SetCurrentAttackCityNum:function(curCityNum) {
		if(!curCityNum){
			var curCityNum = 1;
		}
		//this.Log("Setting current attack city to: "+curCityNum);
		this.SetValue('CurrentAttackCityNum',curCityNum);
		return curCityNum;
	},

	
	// returns {count, maxlevel}
	getCityBuilding: function(cityId, buildingId){
	  var b = unsafeWindow.seed.buildings['city'+cityId];
	  var ret = {count:0, maxLevel:0};
	  for (var i=1; i<33; i++){
		if (b['pos'+i] && b['pos'+i][0] == buildingId){
		  ++ret.count;
		  if (parseInt(b['pos'+i][1]) > ret.maxLevel)
			ret.maxLevel = parseInt(b['pos'+i][1]);
		}
	  }
	  return ret;
	},

	lastTrainTroops:{},
	CheckTrainTroops:function() {
		var t=this;
		if(!this.GetSeed()) return;
		var cityid=this.GetCurrentCityId();

		var trainTroops=JSON2.parse(this.GetValue('TrainTroops','{}'));
		//var trainTroops=GM_getValue('TrainTroops_'+this.GetServerId(),0);
		if(!trainTroops || !trainTroops[cityid]) {
			return;
		}
		var trainTroopId=trainTroops[cityid];
		
	//GM_log('buildTroops'+unsafeWindow.seed.citystats["city" +cityid ]["pop"][0]+"=="+unsafeWindow.seed.citystats["city" + cityid]["pop"][1]);
		var popAvail=parseInt(unsafeWindow.seed.citystats["city" +cityid ]["pop"][0]);
		var popTotal=parseInt(unsafeWindow.seed.citystats["city" + cityid]["pop"][1]);
		var labourTotal=parseInt(unsafeWindow.seed.citystats["city" + cityid]["pop"][3]);
		var idleTotal=popTotal-labourTotal;
		var popNeeded=((t.options.percentOfPopToTrain/100)*idleTotal)+labourTotal;
		
		// Determine number of available training slots
		var availableTrainingSlots = 0;
		try{
			var barracksTotal = this.getCityBuilding(cityid, 13).count;
			var trainingSlotsUsed = unsafeWindow.seed.queue_unt['city'+cityid].length;
			if(trainingSlotsUsed!=null){
				var availableTrainingSlots = barracksTotal-trainingSlotsUsed;
			}
		}finally{
			if(availableTrainingSlots<1){ return false; }
		}

		
	//GM_log('idleTotal:'+idleTotal+', labourTotal:'+labourTotal+', popneeded:'+popNeeded);	
		if(popAvail>0 && popAvail>=popNeeded) {
			// avoid over training.
			var lastTrain=this.lastTrainTroops[cityid];
			var nowSecs=new Date().getTime()/1000;
			if(nowSecs<(lastTrain+(3*60))) { return; }
			var startButton=ById('unit_btns_start');
			
			if(!startButton) {
				// let's bring up build troops
				this.DoUnsafeWindow('modal_barracks_train('+trainTroopId+');')
				return;
			} 
			var onclick=startButton.getAttribute('onclick');
			var onclickM=/\(([0-9]+)\)/.exec(onclick);
			if(!onclickM || trainTroopId!=onclickM[1]) {
				return;
			}
			this.lastTrainTroops[cityid]=nowSecs;
			// let's build troops
			var numInp=ById('modal_barracks_num');
			var maxObj=ById('modal_barracks_max_num');
			
			if(numInp && maxObj) {
				numInp.value=Math.floor(parseInt(maxObj.textContent)*(parseInt(this.options.percentToTrainOfMax)/100));
				var t=this;
				window.setTimeout(function() {
					if(numInp.value>parseInt(maxObj.textContent)) {
						t.Log('Not training troops:'+numInp.value+'>'+maxObj.textContent);
						return;
					}
	//var n=unsafeWindow.modal_barracks_train_max(6);
					onclick=onclick.replace('return false;','');
					window.setTimeout(function() {
						eval('unsafeWindow.'+onclick);
					},t.GetRandTime(500));
				},t.GetRandTime(500));
			}
		}
	},

	DetermineCurrentPage:function() {
		if(unsafeWindow.location.href.match(/kingdomsofcamelot\.com\/fb\/.*?\/standAlone\.php/i)){
			this.currentPage = 'kabam_page';
		}else if(unsafeWindow.location.href.match(/kingdomsofcamelot\.com\/fb\/.*?\/src\/main_src\.php/i)){
			this.currentPage = "koc_game";
		}else if (unsafeWindow.location.href.match(/apps\.facebook\.com\/kingdomsofcamelot\/.*?page=nogame/i)) {
			this.currentPage = "domain_selection_app_page";
		}else if (unsafeWindow.location.href.match(/apps\.facebook\.com\/kingdomsofcamelot/i)) {
			this.currentPage = "app_page";
		}else if (unsafeWindow.location.href.match(/facebook.com\/connect\/uiserver.php/i)) {
			this.currentPage = "facebook_popup";
		}else if(unsafeWindow.location.href.match(/kingdomsofcamelot\.com\/fb\/.*?\/src\/newgame_src\.php/i)){
			this.currentPage = "domain_selection";
		}else{
			this.currentPage = "unknown";
		}
		return this.currentPage;
	},

	OnImpendingAttack:function() {
		var t=this;
		this.Log("impending attack");
		var autoAttack=this.GetAutoAttack();
		var url=t.options.impendingAttackUrl;
		if(url!=undefined && url !="") {
			GM_openInTab(url);
		}
		if(t.options.impendingStopAttack){
			if(autoAttack){
				t.ToggleAutoAttack();
			}
		}
	},
	CheckImpendingAttack:function() {
		var r=false;
		var seed=this.GetSeed();
		
		if(seed && seed.queue_atkinc) {
		  for(k in seed.queue_atkinc){
			m = seed.queue_atkinc[k];
			if (m.marchType==3 || m.marchType==4){ 
				var q=0;
				var keys=unsafeWindow.Object.keys(seed.queue_atkinc);
					if(keys.length>0 && keys.length<16) {		
						r=true;
					}
				}
			}
		}
	//GM_log('imp'+r);	
		/*
		var topNav=ById('topnav_msg');
		if(!topNav || topNav.innerHTML.length==0) return;
		
		var redIdx=topNav.innerHTML.indexOf(': red');
		var r=redIdx>=0?true:false;
		if(!r) {
	GM_log('tnav'+topNav.innerHTML);
		}
		*/

		var t=this;
		var impendingAttack=this.GetValue('ImpendingAttack',false);
		if(!impendingAttack && r) {
			window.setTimeout(function() {
				t.OnImpendingAttack();
			},0);
		}
		this.SetValue('ImpendingAttack',r);
		return r;
	},


	/*
	current_modal_msg_list:"",
	CheckMessageLoad:function(page) {
		var t=this;
		var msg=ById('modal_msg_list');
		if(msg && msg.innerHTML!=t.current_modal_msg_list) {
			t.current_modal_msg_list=msg.innerHTML;
			var messageLoad=ById('MessageLoad');
			if(!messageLoad) {
				messageLoad=document.createElement('div');
				messageLoad.id='MessageLoad';
				messageLoad.style.position='absolute';
				messageLoad.style.height='600px';
				messageLoad.style.overflow='scroll';
				messageLoad.style.top='10px';
				messageLoad.style.zIndex='900000';
				messageLoad.style.border='5px solid #000';
				messageLoad.style.backgroundColor='#fff';
				document.body.appendChild(messageLoad);
			}
			var m=document.createElement('div');
			m.innerHTML=msg.innerHTML;
			messageLoad.appendChild(m);
			page++;
			this.DoUnsafeWindow("modal_messages_listshow('inbox',page);");
		}
		window.setTimeout(function() {
			t.CheckMessageLoad(page);
		},200);
	},

	ReadEmails:function() {
		this.current_modal_msg_list='';
		this.CheckMessageLoad();
	},

	*/



	FactoryReset:function() {
		var names=this.browser_listValues();
		for(var n=0; n<names.length; n++) {
			this.browser_setValue(names[n],null);
		}
		this.SetOptions({});
	},
	FactoryResetCurrentServer:function() {
		var names=this.browser_listValues();
		var serverId=this.GetServerId();
		for(var n=0; n<names.length; n++) {
			var name=names[n];
			var sid=this.GetServerIdFromName(name);
			if(sid!=serverId) continue;
			this.browser_setValue(name,null);
		}
	},
	GetServerIdFromName:function(n) {
		var nArr=n.split('_');
		if(nArr.length<2) return null;
		return nArr[1];
	},
	DeleteAllStoredAttacks:function() {
		var t=this;
		this.IterateAttacks(function(name,value) {
			var nameI=name.split('_');
			if(nameI.length<3) return;
			var xy=nameI[2].split(',');
			if(value=="") return;
			var attack=JSON2.parse(value);
			if(!attack) return;
			var attackX = xy[0];
			var attackY = xy[1];
			if(attackX && attackY){
				t.DeleteAttack(attackX, attackY);
			}
			return true;
		});
		window.alert("All stored attacks for this domain have been deleted.\nClick the ok button to reload.");
	},
	
	DeleteTransports:function() {
		var t=this;
		this.IterateAttacks(function(name,value) {
			var nameI=name.split('_');
			if(nameI.length<3) return;
			var xy=nameI[2].split(',');
			if(value=="") return;
			var attack=JSON2.parse(value);
			if(!attack) return;
			var attackX = xy[0];
			var attackY = xy[1];
			if(attack.type == 1){				
			t.DeleteAttack(attackX, attackY);
			}
			return true;
		});
		window.alert("All stored transports for this domain have been deleted.\nClick the ok button to reload.");
	},
	ListTransports:function() {
		var t=this;
		var fTransport = 0;
		this.IterateAttacks(function(name,value) {
			var nameI=name.split('_');
			if(nameI.length<3) return;
			var xy=nameI[2].split(',');
			if(value=="") return;
			var attack=JSON2.parse(value);
			if(attack.type == 1){
				WinLog.write ("Name: "+name+" Value: "+value);
				fTransport = 1;
				return true;
			}
		});
		if (!fTransport){
			WinLog.write ("No Transports Found\n");
			return true;
		}				
	},
	ListAttacks:function() {
		var t=this;
		this.IterateAttacks(function(name,value) {
			WinLog.write (name+" "+value);
			return true;
		});
	},
	DetailAttacks:function() {
	  var t=this;
	  
	  var names=GM_listValues();
	  var currentServerId = t.GetServerId();
	  var attackPrefix='attack_'+currentServerId+'_';
	  for(var n=0; n<names.length; n++) {
		var name=names[n];
		var nameI=name.split('_');
		if(nameI.length<3) continue;
		var xy=nameI[2].split(',');
		if(name.substring(0,attackPrefix.length)!=attackPrefix) continue;
		var value = GM_getValue(name);
		if(value=="") continue;
		var attack=JSON2.parse(value);
		attack.xy=xy;
		if(!attack) continue;					
		t.attacks.push(attack);
	  }
	},			
	ExportAllToJSON:function() {
		var names=this.browser_listValues();
		var obj={};
		var serverId=this.GetServerId();
		for(var n=0; n<names.length; n++) {
			var name=names[n];
			var sid=this.GetServerIdFromName(name);
			if(sid!=serverId) continue;
			var v=this.browser_getValue(name,null);
			if(v!=null && v!=undefined && v!="")
				obj[name]=v;
		}
		return JSON2.stringify(obj);
	},
	ImportAllFromJSON:function(json) {
		try {
			var obj=JSON2.parse(json);
			if(!obj) { return; }
			this.FactoryResetCurrentServer();
			var serverId=this.GetServerId();
			for(var name in obj) {
				var sid=this.GetServerIdFromName(name);
				if(sid!=serverId) continue;
				this.browser_setValue(name,obj[name]);
			}
		} catch(e) {
			window.alert('Import failed'+e);
		}
	},

	SetupMenu:function() {
		var t=this;
		GM_registerMenuCommand('KOCAttack - Options',function() {
			t.ShowOptionsDialog();
		});
		GM_registerMenuCommand('KOCAttack - View Attacks',function() {
			t.ShowViewAttacksDialog();
		});
		GM_registerMenuCommand('KOCAttack - Import/Export',function() {
			t.ShowImportExportBox();
		});
		
		GM_registerMenuCommand('KOCAttack - Auto Attack',function() {
			t.ToggleAutoAttack();
		});
		GM_registerMenuCommand('KOCAttack - Factory Reset!',function() {
			t.FactoryReset();
		});
	},




	/*
	TimeoutZero:function(f,arg) {
		setTimeout(function() {
			f(arg);
		},0);
	},
	*/

	pageLoaded:false,
	prevCurrentCity:-1,
	currentAttackCityResumed:false,
	inviteFriendsTabHidden:false,
	idStatus:{},
	ResetIdStatus:function() {
		this.idStatus={};
	},
	Listen:function() {
		var t=this;
		t.SetupMenu();
		this.GetValuesCache();
		t.ResetAutoAttackTarget();
		this.options=this.GetOptions();
		this.startListenTime=new Date();
		
		// Determine which page we're on
		t.DetermineCurrentPage();
		
		// Code strictly for page: koc_game
		if(t.currentPage == "koc_game"){
			//Check for strange majic error
			//setTimeout(function(){checkStrangeMagic();},15000);
					
			window.setTimeout(function() {
				if(!t.pageLoaded && t.GetAutoAttack() && !t.IsMapperRunning()) {
					GM_log("whoops, game not loaded after 60 secs problem. reloading.");
					t.SetValuesCache();
					t.ReloadWindow();
				}
			},t.GetRandTime(60*1000));

			if(t.GetAutoAttack()) {
				window.setTimeout(function() {
					// press start on the poc timer after we reload
					if(unsafeWindow.poctoggletimer && unsafeWindow.ispaused) {
						unsafeWindow.poctoggletimer();
					}
				},5000);
			}
			
			// Hide the invite friends tab on page load
			if(!t.inviteFriendsTabHidden && this.options.disableInviteFriends){
				var tabBar=ById("main_engagement_tabs");
				if(tabBar){
					var inviteFriendsTab=nHtml.FindByXPath(tabBar,".//a[contains(@onclick,'invite_friends_popup')]");
					if(inviteFriendsTab){
						inviteFriendsTab.style.display="none";
						t.inviteFriendsTabHidden = true;
					}
				}
			}
		
		} // End of code strictly for page: koc_game
		
		var domTickTimer=null;
		var domTickUpto=0;
		var domTick=function(e) {
			
			var funcsById={};
			
			// Handle cross-iframe commands (which are currently only being used for the domain selection page)
			if (t.currentPage == "domain_selection" || t.currentPage == "domain_selection_app_page"){
				if((domTickUpto%20)==0) {
					t.HandleCrossIframeCommands();
				}
			}
			
			if (t.currentPage == 'kabam_page'){
				return;
			}

			// Code strictly for page: koc_game
			if(t.currentPage == "koc_game"){
			
				//if(e.target.className && !/(chat|city|slot)/.exec(e.target.className)) GM_log('xxx:'+e.target.className);
				//if(e.target.id && e.target.id!='tooltip' && e.target.id('_l_')<0 && e.target.id.substring(0,2)!='l_' && e.target.id.substring(0,8)!='citysel_') GM_log('id:'+e.target.id);
				var cityId=t.GetCurrentCityId();
				var cityChanged=cityId!=t.prevCurrentCity?true:false;
				if(cityChanged) {
					t.prevCurrentCity=cityId;
				}

				// Resume attacks at the last city we left off on (if page was reloaded)
				if(!t.currentAttackCityResumed){
					var currentAttackCityNum = t.GetCurrentAttackCityNum();
					var currentAttackCity=ById('citysel_'+currentAttackCityNum);
					//t.Log('Last known attack city from :'+currentAttackCityNum);
					if(currentAttackCity){
						if(currentAttackCityNum>1){ //Do not change city if the last city saved was 1
							t.Log('Changing to last city before refresh:'+currentAttackCityNum);
							t.autoAttackCityUpto = currentAttackCityNum;
							t.ClickChangeCity(currentAttackCity,0);
						}
						t.currentAttackCityResumed = true;
					}
				}
				
				if((domTickUpto%10)==0) {
					t.HandleChatPane();
				}
				
				if((domTickUpto%20)==0) {
					t.CheckImpendingAttack();
				}
				
				if(cityChanged && cityId!=null) {
					// changed city
					setTimeout(function() {
						t.AddOptionsLink();
						t.DrawClosestFarms(); // Init auto attack tab
						t.AddViewAttacksLink();
						//t.AddImportExportLink();
					},0);
					setTimeout(function() {
						t.DetermineCurrentRallyPointLevel();
						t.DetermineCurrentMarchesNum();
						t.CheckAutoRaiseGold();
						t.CheckAbandonWilds();
						t.CheckTrainTroops();
					},1000);
					setTimeout(function() {
						t.CheckReports();
					},3000);
					setTimeout(function() {
						t.NextAutoAttack();
					},5000);
				}
				
				funcsById={
					'castleModalTabs':function(target) {
						t.OnCastleBoxAppear(target.parentNode);
					},
					'marketmain_bdy':function(target) {
						t.OnMarketBoxAppear(target);
					},
					'modal_attack':function(target) {
						window.setTimeout(function() {
							t.OnAttackBoxAppear(target);
						},250);
					},
					'barracks_train':function(target) {
						t.AddTrainTroopsLink();
						t.CheckTrainTroops();
					},
					'modal_speedup':function(target) {
						t.ClickShareToWall(target);
					},
					'invitePopup':function(target) {
						if(this.options.disableInviteFriends && false){
							// Hide the invite popup if auto attack is enabled
							target.parentNode.removeChild(target);
						}
					},
				};
				
			} // End of code strictly for page: koc_game
			
			// Handle cross-domain facebook game publish requests
			funcsById.RES_ID_fb_pop_dialog_table = function(target){
				if (t.currentPage == "koc_game") {
					// Update the current server id locally for cross-domain access
					if(t.currentServerId>0 && t.currentPage == "koc_game"){
						GM_setValue("KOCAttackLastKnownServerID", t.currentServerId);
					}
				}
			}
			if(t.currentPage == "facebook_popup"){
				if((domTickUpto%20)==0) {
					t.HandlePublishPopup();
				}
			}
			
			// Log back into domain if disconnected due to server down-time
			if(t.currentPage == "domain_selection" && t.options.autoLogBackIn){
				if((domTickUpto%20)==0) {
					t.HandleDomainLogin();
				}
			}
			
			/*
			if(e.target.id && funcsById[e.target.id]) {
				funcsById[e.target.id](e.target);
			} else 
			*/
			//if(e.target.className.indexOf('modalBox')>=0) {
			var funcCalled=0;
			if(funcCalled==0) {
				for(var id in funcsById) {
					var f=funcsById[id];
					var div=ById(id);

					if(!t.idStatus[id] && div) {
						var fcall=function(func,d) {
							funcCalled++;
							setTimeout(function() {
								try {
									func(d);
								} finally {
									funcCalled--;
								}
							},0);
						}
						fcall(f,div);

						//t.TimeoutZero(f,div);
					}
					var divStatus=div?true:false;
					if(divStatus!=t.idStatus[id]) {
						//t.Log("Status changed:"+id+","+divStatus);
					}
					t.idStatus[id]=divStatus;
				}
			}
			if(!domTickTimer) {
				domTickTimer=window.setTimeout(function() {
					domTickTimer=null;
					domTick();
					domTickUpto++;
				},250);
			}
		};
		
		var withinDomInserted=false;
		if(document.body){
			document.body.addEventListener('DOMNodeInserted',function(e) {
				if(withinDomInserted) return;
				var isStatuses=(e.target.className && e.target.className=='statues')?true:false;
				if(isStatuses){
					t.pageLoaded=true;
				}
				if(e.target.id && e.target.id=='tooltip') {
					withinDomInserted=true;
					setTimeout(function() {
						try {
							if(this.options.disableDrawMapIcons)
								t.DrawLevelIcons();
							t.OnToolTipAppear(e.target);
						} finally {
							withinDomInserted=false;
						}
					},0);
				} else if(e.target.className && e.target.className.indexOf('modal_msg_reports')>=0) {
					withinDomInserted=true;
					setTimeout(function() {
						try {
							t.RecordReports();
							t.AddCheckBarbarians();
							t.HighlightAllianceReports();
						} finally {
							withinDomInserted=false;
						}
					},0);
				}
			},false);
		}

		domTick();
	},

};

unsafeWindow.aaGotoMapHide = function (x, y){
  try {
    unsafeWindow.Modal.hideModal();
  } catch (e){ }
  try {
    Modal.hideModal();
  } catch (e){ }
  unsafeWindow.aaGotoMap (x, y);  
}

unsafeWindow.aaGotoMap = function (x, y){
	var close = document.getElementById('KOCAttackViewAttacksCancel');
	nHtml.Click(close);
  setTimeout (function (){ 
    document.getElementById('mapXCoor').value = x;
    document.getElementById('mapYCoor').value = y;
    unsafeWindow.reCenterMapWithCoor();
    var a = document.getElementById("mod_views").getElementsByTagName("a");
    for (var b = 0; b < a.length; b++) {
        a[b].className = ""
    }
    document.getElementById('mod_views_map').className = "sel";
    document.getElementById("maparea_city").style.display = 'none';
    document.getElementById("maparea_fields").style.display = 'none';
    document.getElementById("maparea_map").style.display = 'block';
    unsafeWindow.tutorialClear()
  }, 0);
};

function SetupQuickMarchButton(useRetryMarch) {
/*
	var retryMarch='var retryMarch=function() { alert("retrying march"); new (Ajax.Request)(g_ajaxpath + "ajax/march.php" + g_ajaxsuffix, {'+
		'method: "post",'+
        'parameters: params,'+
        'onSuccess: function (transport) {'+
            'var rslt = eval("(" + transport.responseText + ")");'+
            'if (!rslt.ok) {'+
				'if(rslt.error_code==3) {'+
					'window.setTimeout(function() { retryMarch(); },1000); '+
				'} else {'+
					'alert("March Error:"+rslt.msg);'+
				'}'+
			'}'+
		'}'+
	'}); };';
*/
	var retryMarches='var retryMarch = function() { '+
		'new (Ajax.Request)(g_ajaxpath + "ajax/march.php" + g_ajaxsuffix, {'+
		'method: "post",'+
        'parameters: params,'+
        'onSuccess: function(transport) {  marchSuccess(transport); },'+
        'onFailure: function () {  Modal.hideModalAll(); }'+
	'}); };';
	if(!useRetryMarch) {
		retryMarches='var retryMarch = function() { return; };';
	}
	
	var modal_attack_update_num_maxReplaces=[
		[['modal_attack_update_num_max','modal_attack_update_num_maxOld']],
		[['$("modal_attack_unit_ipt','var x=0; var o = getBuildingLevel(12); if(o==11){x=150000}else{x=o*10000};$("modal_attack_unit_ipt']],
		[['parseInt(','x; //parseInt(']]
	];
	
	var modalAttackReplaces=[
		// *** it says "new Ajax" in the source but firefox converts it to new (Ajax
		[['modal_attack_do','modal_attack_doOld']],
		[['onSuccess:','onSuccess: marchSuccess=']],
		[['Modal.showAlert(printLocalError(','if(rslt.error_code==3 || rslt.error_code==8) { try {window.setTimeout(function() { retryMarch(); },(3*1000)); } catch(e) { alert("retry failed:"+e); }  } else { Modal.hideModalAll(); }  Modal.showAlert(printLocalError(']]
	];
	
	
	var attack_generatequeueReplaces=[
		[['attack_generatequeue','attack_generatequeueOld']],
		[
			['class=\\"army\\">" + g_js_strings.commonstr.army + ": "','style=\\"width: 145px !important\\" class=\\"army\\">"'],
			['class=\\"army\\">" + g_js_strings.commonstr.army + ": <span>"','style=\\"width: 145px !important\\" class=\\"army\\"><span style=\'display: inline\'>"']
		],
		[['class=\\"name','style=\\"width: 0px !important; display: none;\\" class=\\"name']],
		//[/123/g,'100']
	];
	var attack_generatequeueReplacesU=[
		[['var u = 0;','var u = "K:"+seed.knights["city" + currentcityid]["knt" + q].combat+", "; ']],
		[['u += parseInt','var x = parseInt']],
		[['"Count"]);','"Count"]); if(x>0) { var uname=unitcost["unt"+r][0]; u+=uname[0]+uname[uname.length-1]+":"+x+", "; } ']],
	];
	var attack_generatequeueReplacesR=[
		[['var r = 0;','var r = "K:"+seed.knights["city" + currentcityid]["knt" + t].combat+", "; ']],
		[['r += parseInt','var x = parseInt']],
		[['"Count"]);','"Count"]); if(x>0) { var uname=unitcost["unt"+p][0]; r+=uname[0]+uname[uname.length-1]+":"+x+", "; } ']],
	];
	var attack_generatequeueReplacesS=[
		[['var s = 0;','var s = "K:"+seed.knights["city" + currentcityid]["knt" + u].combat+", "; ']],
		[['s += parseInt','var x = parseInt']],
		[['"Count"]);','"Count"]); if(x>0) { var uname=unitcost["unt"+q][0]; s+=uname[0]+uname[uname.length-1]+":"+x+", "; } ']],
	];
	var attack_generatequeueReplacesT=[
		[['var t = 0;','var t = "K:"+seed.knights["city" + currentcityid]["knt" + w].combat+", "; ']],
		[['t += parseInt','var x = parseInt']],
		[['"Count"]);','"Count"]); if(x>0) { var uname=unitcost["unt"+q][0]; t+=uname[0]+uname[uname.length-1]+":"+x+", "; } ']],
	];
	var attack_generatequeueReplacesV=[
		[['var v = 0;','var v = "K:"+seed.knights["city" + currentcityid]["knt" + y].combat+", "; ']],
		[['v += parseInt','var f = parseInt']],
		[['"Count"]);','"Count"]); if(f>0) { var uname=unitcost["unt"+r][0]; v+=uname[0]+uname[uname.length-1]+":"+f+", "; } ']],
	];
	var attack_generatequeueReplacesW=[
		[['var w = 0;','var w = "K:"+seed.knights["city" + currentcityid]["knt" + A].combat+", "; ']],
		[['w += parseInt','var t = parseInt']],
		[['w += parseInt','var t = parseInt']],
		[['"Return"])','"Return"]); if(t>0) { var uname=unitcost["unt"+s][0]; w+=uname[0]+uname[uname.length-1]+":"+t+", "; } ']],
		[['"Count"])','"Count"]); if(t>0) { var uname=unitcost["unt"+s][0]; w+=uname[0]+uname[uname.length-1]+":"+t+", "; } ']],
	];
	var attack_generatequeueReplacesW2=[
		[['var w = 0;','var w = "K:"+seed.knights["city" + currentcityid]["knt" + E].combat+", "; ']],
		[['w += y','if(y>0) { var uname=unitcost["unt"+cm.UNIT_TYPES[t]][0]; w+=uname[0]+uname[uname.length-1]+":"+y+", "; }']],
	];
		
/*****	
	if(navigator.userAgent.toLowerCase().indexOf('chrome') > -1) {
		modalAttackReplaces.push([['new Ajax',"var marchSuccess=null; "+retryMarch+
			(useRetryMarch?"":" Modal.hideModalAll(); ")+
			"\nnew Ajax"]]);
	} else {
		modalAttackReplaces.push([['new (Ajax',"var marchSuccess=null; "+retryMarch+
			(useRetryMarch?"":" Modal.hideModalAll(); ")+
			"\nnew (Ajax"]]);
	}
*****/
	//modalAttackReplaces.push([['ajax.Request',   "var marchSuccess=null; "+retryMarch+
		//(useRetryMarch?"":" Modal.hideModalAll(); ")+	"\najax.Request"]]);

	if(!useRetryMarch) modalAttackReplaces.push(['Modal.hideModalAll();','']);
	
	var replaceFunc=function(name,replaces) {
		var modalAttackFunc=window[name].toString();
		var nameOld=name+'Old';
		var foundFailed=false;
		for(var a=0; a<replaces.length; a++) {
			var found=false;
			var repArr=replaces[a];
			for(var ra=0; ra<repArr.length; ra++) {
				var repI=repArr[ra];
				if(typeof(repI[0])=="object") {
					found=repI[0].exec(modalAttackFunc)?true:false;
				} else {
					found=modalAttackFunc.indexOf(repI[0])>=0?true:false;
				}
				if(found) break;
			}
			if(!found) {
				var err="modalAttackReplace: cannot find: "+repI[0]+','+modalAttackFunc;
				var sp=document.createElement('span');
				sp.style.color='#ccc';
				sp.appendChild(document.createTextNode(err));
				document.body.insertBefore(sp,document.body.childNodes[0]);
				foundFailed=true;
				break;
			}
			
			modalAttackFunc=modalAttackFunc.replace(repI[0],repI[1]);
		}
		if(foundFailed) return;
		try {
			window[nameOld]=eval(modalAttackFunc);
//alert(window[nameOld].toString());			
		} catch(e) {
			alert(e+', bad func:'+modalAttackFunc);
		}

		window[name]=function(e) {
			// let our stuff in addListener run first.
			window.setTimeout(function() {
				eval(nameOld+'(e);');
			},100);
		}
	};
	
	var attackFuncStr=window['modal_attack_do'].toString();
	//if(attackFuncStr.indexOf('ajax.Request')<0) {
		modalAttackReplaces.push([
			['new (Ajax',"var marchSuccess=null; "+retryMarches+
			(useRetryMarch?"":" Modal.hideModalAll(); ")+
			"\nnew (Ajax"],
			['new Ajax',"var marchSuccess=null; "+retryMarches+
			(useRetryMarch?"":" Modal.hideModalAll(); ")+
			"\nnew Ajax"],
			['ajax.Request',"var marchSuccess=null; "+retryMarches+
			(useRetryMarch?"":" Modal.hideModalAll(); ")+
			"\najax.Request"]
		]);
	//}		
	replaceFunc('modal_attack_do',modalAttackReplaces);
	//replaceFunc('modal_attack_update_num_max',modal_attack_update_num_maxReplaces);
	
	function AddArray(to,from) {
		for(var c=0; c<from.length; c++) { to.push(from[c]); }
	}
	
	var arr=[];
	AddArray(arr,attack_generatequeueReplaces);
	var funcStr=window['attack_generatequeue'].toString();
	if(funcStr.indexOf(' var w = 0;')>=0) {
		// camelotmain-218
		AddArray(arr,attack_generatequeueReplacesW2);
	} else if(funcStr.indexOf('; var r = 0;')>=0) {
		AddArray(arr,attack_generatequeueReplacesR);
	} else if(funcStr.indexOf('; var s = 0;')>=0) {
		AddArray(arr,attack_generatequeueReplacesS);
	} else if(funcStr.indexOf('; var t = 0;')>=0) {
		// camelotmain-150
		AddArray(arr,attack_generatequeueReplacesT);
	} else if(funcStr.indexOf('; var v = 0;')>=0) {
		// camelotmain-165
		AddArray(arr,attack_generatequeueReplacesV);
	} else if(funcStr.indexOf('; var u = 0;')>=0) {
		AddArray(arr,attack_generatequeueReplacesU);
	} else {
		var err="Unknown attack queue func: "+location.href+"\n"+funcStr;
		GM_log(err);
	}
	replaceFunc('attack_generatequeue',arr);

	//replaceFunc('modal_attack_update_num',modalAttackUpdateNumReplaces);

/* BAD: updateSeed.php doesn't return cityUnits
    var params = Object.clone(g_ajaxparams);
    new Ajax.Request(g_ajaxpath + "ajax/updateSeed.php" + g_ajaxsuffix, {
        method: "post",
        parameters: params,
        onSuccess: function (message) {
alert(message.responseText);
		}
	});
*/
}

/*
function SetupClearMessages() {
	modal_messages_reports_chkdelOld=modal_messages_reports_chkdel;
	modal_messages_reports_chkdel=function(type) {
		var d=document.createElement('div');
		d.id='modal_msg_reports_tablediv';
		document.body.appendChild(d);
		modal_messages_reports_chkdelOld(type);
		if(type=='deleteAll') {
			document.getElementById('ClearMessages').click();
		}
	};
}
*/

function CreateMsgDiv() {
	var m=document.getElementById('KOCAttackMsgDiv'); 
	if(!m) { 
		var ml=document.getElementById('modal_msg_list'); 
		if(!ml) {
			ml=document.createElement('div'); 
			ml.id='modal_msg_list'; 
		}
		m=document.createElement('div'); 
		m.style.position='absolute';
		m.style.top='0px';
		m.style.left='0px';
		m.style.width='700px';
		m.style.zIndex='900000';
		m.style.border='5px solid #000';
		m.style.backgroundColor='#fff';
		//m.id='modal_attack';
		var close=document.createElement('a');
		close.addEventListener('click',function() {
			m.style.display='none';
			m.removeChild(ml);
		},false);
		close.innerHTML='Close';
		close.style.fontSize='20px';
		
		var center=document.createElement('center'); 
		center.appendChild(close);
		m.appendChild(center);
		m.appendChild(ml);
		
		if(!document.getElementById('modal_msg_list_pagination')) { 
			p=document.createElement('div'); p.id='modal_msg_list_pagination'; 
			ml.appendChild(p);
		}
		//document.body.insertBefore(m,document.body.childNodes[0]);
		if(document.body){
			document.body.appendChild(m);
		}
	}
	m.style.display='block';
	return m;
}

function AddScript(js) {
	var scr=document.createElement('script');
	scr.innerHTML=js;
	document.body.appendChild(scr);
}
function SetupScripts() {
	var options=KOCAttack.GetOptions();
	var scr=document.createElement('script');
	scr.innerHTML="CreateMsgDiv="+CreateMsgDiv+
		";\n"+
	//	SetupClearMessages+"\n; SetupClearMessages();\n"+
		";\n"+SetupQuickMarchButton+"\n; SetupQuickMarchButton("+options.retryMarch+"); \n";
	document.body.appendChild(scr);
}

/******************* Anti-anticheat measures ******************/
var mixpanelRemoved=false;
function DisableMixpanel() {
	if(unsafeWindow.cm) {
		unsafeWindow.cm.MixPanelTracker.track=function() { };
	}
	if(unsafeWindow.MixpanelLib) {
		unsafeWindow.MixpanelLib.prototype={
			register:function() { },
			track:function(t) {
			}
		};
	}
	if(!unsafeWindow.cm || !unsafeWindow.MixpanelLib) {
		window.setTimeout(function() {
			DisableMixpanel();
		},100);
	} else {
		GM_log('Mixpanel removed');
		mixpanelRemoved=true;
	}
}



/******************* Check strange majic error ******************/
function checkWhiteScreen (){
  window.setTimeout(function(){
	GM_log("Check iFrame");
	var checknumber = 0;
	function checkiFrame() {
		var iFrame = document.getElementById('iframe_canvas');
		if (!iFrame && checknumber<10){
		  checknumber++;
		  setTimeout (checkiFrame, 1000);
		  return;
		} else if (checknumber>=10){
			KOCAttack.ReloadWindow();
			GM_log("White screen error. Refreshing.....");
		}
		return;
	}
	checkiFrame();
  }, 10000);
}
function checkStrangeMagic (){
	GM_log("Check strange majic");
	if (!document.getElementById("kochead")){
		window.setTimeout ( function() { GM_log ("checkStrangeMajik REloading..."); KOCAttack.ReloadWindow(); }, 5000);
		popup (100,100,500,275, "<BR><CENTER>checkStrangeMajik <BR><BR>KofC NOT FOUND<BR>Refreshing in 5 seconds ...<BR><BR>");
	}  
}

function popup (left, top, width, height, content){
   var div = document.createElement('div');
   if (width)
       div.style.width = width;
   if (height) 
       div.style.height = height;
   if (left || top) {
       div.style.position = "relative";
       if (left)
           div.style.left = left;
       if (top)
           div.style.top = top;
   }
   if (content)
       div.innerHTML = content;
       
  div.style.background = "#ffc";
  div.style.border = "2px solid #000";
  div.style.zIndex = "999999";        // KOC modal is 100210 ?
  div.style.display = 'block';
  window.document.body.insertBefore(div, window.document.body.childNodes[0]);
  return div;
}

/******************* Function calls ******************/
KOCAttack.Listen();
if(unsafeWindow.cm){
	unsafeWindow.cm.cheatDetector={
		a:function(){  },
		detect:function() { }
	};
}
if(document.URL.search('apps.facebook.com/kingdomsofcamelot/')>=0) {
	checkWhiteScreen();
} else {
	StartAll();
}
var startAllTimeout=null;
function StartAll() {
	DisableMixpanel();
	var now=new Date().getTime();
	if(startAllTimeout==null) {
		startAllTimeout=now+5000;
	}
	if(mixpanelRemoved || startAllTimeout<now) {
		if(startAllTimeout<now) {
			GM_log("Did not remove mixpanel, starting anyways");
		}
		KOCAttack.SetupClearMessages();

		SetupScripts();
	} else {
		window.setTimeout(function() { StartAll(); },200);
	}
}

var WINLOG_MAX_ENTRIES = 1000;     // TODO
var WinLog = {
  state : null,
  win: null,
  eOut : null,
  lastE : null,
  enabled : true,
  reverse : true,
  busy : false,
isOpening : false,

  open : function (){
    var t = WinLog;
	GM_log("opening WinLog");

    function eventButClear(){
      var t = WinLog;
      t.lastE = null;
      t.eOut.innerHTML ='';
    }
    function eventButReverse(){
      var t = WinLog;
      if (t.busy)
        return;
      t.busy = true;
      if (t.reverse){
        t.win.document.getElementById('wlRev').value= 'Top';
        t.reverse = false;
      } else{
        t.win.document.getElementById('wlRev').value= 'Bottom';
        t.reverse = true;
      }
      var n = t.eOut.childNodes.length;
      if (n < 2)
        return;
      for (i=n-2; i>=0; i--){
        t.eOut.appendChild (t.eOut.childNodes[i]);
      }
      t.busy = false;
    }
    
    if (!t.win || t.win.closed){
    t.isOpening = true;  
    t.win = window.open('', 'uwtrace', 'top=30,left=0,width=900,height=700,scrollbars=no,location=no,menubar=no,directories=no,status=no');
    t.isOpening = false; 
	t.state = null; 
}
    
    if (t.state == null){
      t.win.document.body.innerHTML = '<STYLE>pre{margin:0px} hr{margin:3px; height:1px; border:0px; color:#cee; background-color:#cee}</style>\
        <BODY style="margin:0px; padding:0px; border:none">\
        <DIV id=winlogtop style="background-color:#d0d0d0; margin:0px; padding:0px; border:1px solid">\
        <INPUT id=wlClear type=submit value="Clear"> &nbsp; <INPUT id=wlRev type=submit value="Bottom"></div>\
        <DIV id=wlOut style="overflow-y:auto; height:100%; max-height:100%"></div></body>';
      t.win.document.getElementById('wlClear').addEventListener('click', eventButClear, false);
      t.win.document.getElementById('wlRev').addEventListener('click', eventButReverse, false);
      t.eOut =  t.win.document.getElementById('wlOut');
      t.state = 1;
    }
  },
  write : function (msg){
      var t = WinLog;
      if (!t.enabled || t.isOpening){
        return;
	  }
      t.open();
	  return;
      var te = document.createElement('pre');
      var now = new Date();
      var m = [];
      var millis = now.getMilliseconds();
      m.push (now.toTimeString().substring (0,8));
      m.push ('.');
      if (millis<100)
        m.push('0');
      if (millis<10)
        m.push('0');
      m.push(millis);
      m.push (': ');
      m.push (msg);
      te.innerHTML = m.join('');
  
      if (t.reverse){
        if (t.lastE == null){
          t.eOut.appendChild(te);
          t.lastE = te;
        } else {
          t.eOut.insertBefore(te, t.lastE);
        }
        var hr = document.createElement('hr');
        t.eOut.insertBefore(hr, te);
        t.lastE = hr;
      } else {
        t.eOut.appendChild(te);
        t.eOut.appendChild(document.createElement('hr'));
      }
  },
};