var gof_js_version = '2010-08-10';
var autocomplete_text;
var autocomplete_skippos
var citydata_loaded;
var citydata_updated;
var dbs_updated;
var map_updated;
var mouseX;
var mouseY;
var playerstats;

var basiclayers = new Array;
var zcounter = 1000;
var citydata = new Array;
var db_buildings = new Array;
var db_buildings2 = new Array;
var db_spells = new Array;
var db_spells2 = new Array;
var db_units = new Array;
var db_units2 = new Array;
var db_science = new Array;
var db_terrain = new Array;
var map = new Array;

var diplomacy = new Hash();
var ifrs = new Hash;
var last_played = new Hash();
var mycity = new Hash();
var registry = new Hash();
var science = new Hash();
var skill = new Hash();
var gof_info = new Hash();
var lang = new Hash ();

var use_attack_reloadmap = 0;
var autocompleting = 0;
var fitTextInBox_maxWidth = false;
var fitTextInBox_maxHeight = false;
var fitTextInBox_currentWidth = false;
var fitTextInBox_currentBox = false;
var fitTextInBox_currentTextObj = false;
var history_n = 0;
var IE = document.all ? true : false;
var lastplay = 0;
var lastpos = 0;
var logerror_phase = "";
var now = 0;
var origtitle = document.title;
var soundmanager_loaded = 0;
var sounds = 1;
var travel_res = 1;

// vars for loadhistory2
var history_lastpos = 0;
var history_n = 0;
var history_timer = 0;

// global vars for getstatusraport, i think they store settings
var showless2 = 0;
var onlyown2 = 0;

var play_attack_plan = 0;
var play_spy = 0;

var unit_tip_str_localised;

var gof2 = 0;

// only functions below

var exception_call = function( req, e ) {
    var exception_text = "JS Exception detected (" + e.name + "): " + e.message + " at " + e.fileName + " line " + e.lineNumber;
	console.log(exception_text);
	if (admin) {
		show_notice('error', exception_text + "<br />Phase " + logerror_phase + "<br />");
	}
    logerror ( exception_text, 'warning' );
}

function handleerror(msg, url, line) {
	
    msg = "JS Error: " + msg + " at " + url + " line " + line;
	console.error(msg);

	/*
	if (line == 498) {
		console.log('This is likely protype issue, return');
		return false;
	}
	*/
	
	var regex = new RegExp("script stack space quota is exhausted|out of memory|Muisti ei riit|NS_ERROR_OUT_OF_MEMORY");
	var match = regex.exec(msg);
	if (match != null) {
		show_notice('error', 'JavaScript error likely because of insufficient memory, please restart your browser/computer to free memory, or close other programs.');
		return;
	}

	var regex2 = new RegExp("String.interpret is not a function|addthis.com|skype_ff_toolbar_win|Lapsilukko|greasemonkey|var console|_createSound|soundmanager2");
	var match2 = regex2.exec(msg);
	var match3 = regex2.exec(url);
	if (match2 != null || match3 != null) {
		console.log('Known/external error', msg);
		return false;
	}

	if (admin) {
		show_notice('error', msg + "<br />Phase " + logerror_phase);
	}

    logerror (msg, 'warning');

	if (admin) return true;

    return false;
}

var last_logerror = '';
function logerror (txt, level) {
	
	if ( logerror_phase ) {
        txt += " (\"" + logerror_phase + "\")";
        logerror_phase = "";
    }
	
	txt+= " gof.js: " + gof_js_version;

	document.getElementById('statusinfo').innerHTML = level + ": " + txt;

	if (last_logerror == txt) { return; }
	last_logerror = txt;
	
	var url = baseurl + '/logerror.cgi?rand=' + Math.round(100000*Math.random());
	url+= "&level=" + level + "&txt=" + encodeURIComponent(txt);
	
	new Ajax.Request(url, {
		onException: exception_call,
		onSuccess: function(transport) {
			console.log('logerror ok', txt, level);
		},
		onFailure: function(transport) {
			console.log('logerror failed', txt, level);
		}
	});
}

function mouseposition (e) {
	mouseX = Event.pointerX(e);
	mouseY = Event.pointerY(e);
}

function nospam(text, nolink) {
	if (!nolink) {
		document.write("<a href=\"mailto:");
	}
	document.write(text);
	if (!nolink) {
		document.write("\">" + text + "</a>");
	}
}

function unset_hash ( uhash ) {
    if ( !uhash ) return;

    uhash.each(function(key) {
        uhash.unset(key);
    });

    return;
}

var not_logged_in = 0;
var last_gofPoll = 0;
function gofPoll (skip, setsounds) {
	logerror_phase = "gofPoll";

	var d = new Date();
	var now = d.getTime();

	if (now - last_gofPoll < 1000 * 10) {
		if (document.getElementById('statusinfo')) {
			document.getElementById('statusinfo').innerHTML = "too soon for gofPoll: " + (now - last_gofPoll);
		}
		if (!skip) {
			setTimeout("gofPoll(0)", 60000);
		}
		return "";
	}
	last_gofPoll = now;
	
	try {

		var pollurl = baseurl + '/engine.cgi';
		pollurl+= '?rand=' + Math.round(100000000*Math.random());

		if (setsounds == undefined) {
		} else {
			pollurl+= "&sounds=" + setsounds;
			sounds = setsounds;
			console.log('setsounds', setsounds);
		}

		document.getElementById('statusinfo').innerHTML = "Connecting...";
		document.getElementById('connectioninfo').innerHTML = "";
		console.log('Connecting', pollurl);

		new Ajax.Request(pollurl, {
			method: 'get',
			onException: exception_call,
			onSuccess: function(gofPolltransport) {
				console.log('Connected ok');
				document.getElementById('statusinfo').innerHTML = "Response got";
				var errcount = 0;
				if (gofPolltransport.responseText == '' || gofPolltransport.responseText.substr(0, 1) == "<") {
					console.warn('Got empty/invalid response for gofPoll');
					document.getElementById('connectioninfo').innerHTML = lang.get('err_connection');
					return;
				}
				unset_hash(mycity);
				unset_hash(science);
				unset_hash(diplomacy);
				unset_hash(gof_info);

				var tmp_gofPoll = gofPolltransport.responseText;
				var data_gofPoll = tmp_gofPoll.split('\n');
				for (var ig = 0; ig < data_gofPoll.length - 1; ig++) {
					var info_gofPoll = data_gofPoll[ig].split('|');
					if (info_gofPoll[0] == '1') {
                        var marp_id = info_gofPoll[2];
                        var marp = document.getElementById( marp_id );
						if ( marp == null ) {
							console.log('create event element', info_gofPoll[1], info_gofPoll[2]);
							var divelement = document.createElement('div');
							divelement.setAttribute('id', info_gofPoll[2]);
							document.getElementById('events').appendChild(divelement);
							divelement.style.display = 'none';
							divelement.innerHTML = info_gofPoll[1];
							new Effect.Appear(info_gofPoll[2]);
							new Effect.Pulsate(info_gofPoll[2], { queue: 'end'});
							setTimeout('flashtitle(0);', 100);
							console.log('created event element');
						}

					}
					else if (info_gofPoll[0] == '2') {
						flashscreen();

					}
					else if (info_gofPoll[0] == '3') {
						console.log("Attempt to play sound " + info_gofPoll[1] + " (" + info_gofPoll[2] + ")");
						playsound(info_gofPoll[1], info_gofPoll[2], info_gofPoll[3]);

					}
					else if (info_gofPoll[0] == '4') {
						console.log('show', info_gofPoll[1]);
						new Effect.Appear(info_gofPoll[1]);

					}
					else if (info_gofPoll[0] == '5') {
						if (gof2) {
							show_notice('notice', info_gofPoll[1]);
						} else {
							alert(info_gofPoll[1]);
						}

					}
					else if (info_gofPoll[0] == '6') {
						info_gofPoll[2] = info_gofPoll[2].replace(/\\n/g, "\n");
						registry.set(info_gofPoll[1], info_gofPoll[2]);

					}
					else if (info_gofPoll[0] == '7') {
						info_gofPoll[2] = info_gofPoll[2].replace(/\\n/g, "\n");
						skill.set(info_gofPoll[1], info_gofPoll[2]);
						if (gof2) {
							if (info_gofPoll[1] == 'diplomacy_peace' && info_gofPoll[2] == 1 && document.getElementById('menudiplomacy').style.display == 'none') {
								new Effect.Appear('menudiplomacy');
							}
							if (info_gofPoll[1] == 'management' && info_gofPoll[2] == 1 && document.getElementById('menumanagement').style.display == 'none') {
								new Effect.Appear('menumanagement');
							}
							if (info_gofPoll[1] == 'spy' && info_gofPoll[2] == 1 && document.getElementById('menuspy').style.display == 'none') {
								new Effect.Appear('menuspy');
							}
							if (info_gofPoll[1] == 'guilds' && info_gofPoll[2] == 1 && document.getElementById('menuguilds').style.display == 'none') {
								new Effect.Appear('menuguilds');
							}
							if (info_gofPoll[1] == 'gathering' && info_gofPoll[2] == 1 && document.getElementById('menugathering').style.display == 'none') {
								new Effect.Appear('menugathering');
							}
							if (info_gofPoll[1] == 'general' && info_gofPoll[2] == 1 && document.getElementById('menuhistory').style.display == 'none') {
								new Effect.Appear('menuhistory');
								new Effect.Appear('menuattack');
								new Effect.Appear('menumessages');
							}

						}
					}
					else if (info_gofPoll[0] == '8') {
						info_gofPoll[2] = info_gofPoll[2].replace(/\\n/g, "\n");
						gof_info.set(info_gofPoll[1], info_gofPoll[2]);
					}
					else if (info_gofPoll[0] == '-1') {
						console.log('not logged in');
						document.getElementById('connectioninfo').innerHTML = "Not logged in";
						not_logged_in = 1;
					}
					else if (info_gofPoll[0] == '0') {
						document.getElementById('statusinfo').innerHTML = info_gofPoll[1];
						not_logged_in = 0;
					}
					else if (info_gofPoll[0] == 'Server too busy, try again later.') {
						console.log('Server busy');

					}
					else {
						errcount++;
						console.log('Unknown command', info_gofPoll[0]);
						if (errcount < 3) {
							logerror('Unknown gofPoll command "' + info_gofPoll[0] + '"', "error");
						}
					}
				}

                var city_string = registry.get('cities');
                if ( city_string ) {
                    var data_cities = city_string.split(',');
                    for (var ig = 0; ig < data_cities.length - 1; ig++) {
                        mycity.set(data_cities[ig], 1);
                    }
                }

                var science_string = registry.get('science_known');
                if ( science_string ) {
                    var data_science = science_string.split(',');
                    for (var ig = 0; ig < data_science.length - 1; ig++) {
                        science.set(data_science[ig], 1);
                    }
                }

                var diplomacy_string = registry.get('diplomacy');
                if ( diplomacy_string ) {
                    var data_diplomacy = diplomacy_string.split(',');
                    for (var ig = 0; ig < data_diplomacy.length - 1; ig++) {
                        var diplomacy_part = data_diplomacy[ig].split('=');
                        var diplomacy_treaties = diplomacy_part[1].split(';');
                        diplomacy.set(diplomacy_part[0], diplomacy_treaties[0]);
                    }
                }

			},
			onFailure: function(transport) {
				console.log('Connection error');
				document.getElementById('connectioninfo').innerHTML = "Connection error";
			}
		});
	}

	catch(e) {
		logerror('gofPoll error ' + e, "error");
	}

	if (!skip && !not_logged_in) {
		var interval = 60000;
		var d = new Date();
		var now = d.getTime();
		if (registry.get('created') && now / 1000 - registry.get('created') < 60 * 5) {
			interval = 15000;
		} else if (registry.get('created') && now / 1000 - registry.get('created') < 60 * 15) {
			interval = 30000;
		}
		console.log('New gofPoll after', interval);
		setTimeout("gofPoll(0)", interval);
	}
}

function basicaction (url) {
	url = baseurl + '/' + url + '&rand=' + Math.round(100000*Math.random());
	console.log(url);

	new Ajax.Request(url, {
		method: 'get',
		onException: exception_call,
		onSuccess: function(transport) {
			if (transport.responseText == 'Server too busy, try again later.') {
				show_notice('error', lang.get('err_connection'));
				document.getElementById('connectioninfo').innerHTML = lang.get('err_connection');
				console.log('Server busy');
				return;
			} else if (transport.responseText == '') {
				document.getElementById('connectioninfo').innerHTML = lang.get('err_connection');
				console.log('Empty response');
				return;
			}
			var data = transport.responseText.split('\n');
			document.getElementById('connectioninfo').innerHTML = "";

			var info = data[0].split('|');
			console.log('status', info[0]);			
			if (info[0] == 1) {
				if (info[1] != '' && info[1] != undefined) {
					show_notice('notice', info[1], 1);
				}
			} else {
				if (info[1] == undefined) { info[1] = lang.get('err_unknown'); }
				show_notice('error', info[1]);
					
			}
			
			if (data[1] != '' && data[1] != undefined) {
				var info2 = data[1].split('|');
				if (info2[0] == 1) {
					playsound(info2[1], 0);
				}
			}
		},
		onFailure: function(transport) {
			console.log(url, 'failed');
			document.getElementById('connectioninfo').innerHTML = "action failed";
			show_notice('error', lang.get('err_connection'));
			document.body.style.cursor = "default";
		}
	});
}

var chatwindows = new Array;
function chatwindow (account, id, chatwindow) {
	logerror_phase = "chatwindow";
	var winname = account;
	if (id) {
		new Effect.Fade(id);
		setTimeout('removeevent("' + id + '");', 2000);
	}
	else if (!account) {
		if (chatwindow) {
			winname = chatwindow;
		} else {
			winname = Math.round(100000*Math.random());
			logerror('No name for chat window', "error");
		}
	}
	if (chatwindows[winname] == null || chatwindows[winname].closed) {
		chatwindows[winname] = window.open(baseurl + '/chat.cgi?account=' + account, 'friend' + winname, 'width=680,height=480,scrollbars=0,resizable=yes,toolbar=no,directories=no,status=no,location=no,menubar=no,copyhistory=no');
	} else {
		chatwindows[winname].focus;
	}
}

function eventgo (url, id, id2, noupdate, width, action, actionparam) {
	logerror_phase = "eventgo " + url + ", " + id;
	if (url != '') {
		if (id2 == '' || id2 == undefined) {
			logerror("id2 is not defined for " + id + ", " + url, "error");
		}
		else {
			openlayer(url, id2, noupdate, width, action, actionparam);
		}
	}
	new Effect.Fade(id);
	setTimeout('removeevent("' + id + '");', 2000);
}

function eventgo2 (url, id, noupdate, width, action, actionparam) {
	openlayer(url, id, noupdate, width, action, actionparam);
}

function removeevent(id) {
	var divelement = document.getElementById(id);
	if (divelement) { removeChildSafe(divelement); }
}

function calc_strength() {
	logerror_phase = "calc_strength";
	if (document.getElementById('chatframe')) {
		console.log('chat, skip calc_strength');
		return;
	}
	if (document.getElementById('nomove5')) {
		document.getElementById('nomove5').style.visibility = 'hidden';
		document.getElementById('nomove5').style.display = 'none';
	}
	if (dbs_updated <= 0) {
		logerror("Parent databases are not updated", "info");
		getdbs();
		return;
	}
	travel_res = 1;

	var type;
	var mycity = 1;
	var tax = 1;
	if (document.getElementById('attacktype')) { type = document.getElementById('attacktype').value; }
	if (document.getElementById('mycity')) { mycity = document.getElementById('mycity').value; }
	if (document.getElementById('tax')) { tax = Math.floor(document.getElementById('tax').value); }

	var attack = 0;
	var slowest = 10000;
	var res_c = [0,0,0,0,0,0,0,0];
	var res_c4 = 0;
	for (var i = 1; i < 1000; i++) {
		if (document.getElementById('unit_pcs' + i) != null && document.getElementById('unit_pcs' + i).value > 0) {
			logerror_phase = "calc_strength: unit loop (1) " + i + ": " + document.getElementById('unit' + i).value;
			if (document.getElementById('attack' + i)) {
				attack+= (document.getElementById('unit_pcs' + i).value * document.getElementById('attack' + i).value);
			}
			logerror_phase = "calc_strength: unit loop (2) " + i + ": " + document.getElementById('unit' + i).value + ", unit_pcs: " + document.getElementById('unit_pcs' + i);
			info_u = document.getElementById('unit' + i).value.split('=');
			console.log('unit', document.getElementById('unit' + i).value);
			console.log('unit.', info_u[0], db_units[info_u[0]][1]);
			console.log(i, db_units[info_u[0]][1], document.getElementById('unit' + i).value, ', going', document.getElementById('unit_pcs' + i).value);
			if (Math.floor(db_units[info_u[0]][23]) < slowest) {
				slowest = Math.floor(db_units[info_u[0]][23]);
				console.log('-> slowest', db_units[info_u[0]][23]);
			}
			logerror_phase = "calc_strength: unit loop (3) " + i + ": " + document.getElementById('unit' + i).value;
			for (var r = 0; r < 8; r++) {
				if (Math.floor(db_units[info_u[0]][13 + r]) < 0) {
					var val = -(Math.floor(db_units[info_u[0]][13 + r]) * Math.floor(document.getElementById('unit_pcs' + i).value));
					res_c[r]+= val;
					/* console.log('->', res_c[r], Math.floor(res_c[r]), res_c, val); */
					/* console.log(r, db_units[info_u[0]][13 + r], res_c[r], document.getElementById('unit_pcs' + i).value, val); */
				}
				if (realm_version >= 2.11 && db_units[info_u[0]][21] > db_units[info_u[0]][22]) {
					if (document.getElementById('noattack')) {
						if (document.getElementById('noattackb').value == 1 || document.getElementById('noattack').value == 1 || document.getElementById('noattack').value == 2) {
							var val2 = (Math.floor(db_units[info_u[0]][5 + r]) * Math.floor(info_u[1] / 100) * Math.floor(document.getElementById('unit_pcs' + i).value));
							console.log('culture cost for offensive unit moving under Plus: peace for ', document.getElementById('unit' + i).value, ' is ', val2);
							res_c4+= val2;
						}
					}
				}
				if (mycity == 0 && type == 1 && Math.floor(db_units[info_u[0]][5 + r]) > 0) {
					var val2 = (Math.floor(db_units[info_u[0]][5 + r]) * Math.floor(info_u[1]) * tax * Math.floor(document.getElementById('unit_pcs' + i).value) / 100 / 100);
					/* $res_c4+= ($info_u[5 + $r] * $lev * $params{'tax'} / 100 / 100); */
					console.log('culture cost for unit swapping for', document.getElementById('unit' + i).value, ' is ', val2);
					res_c4+= val2;
				}
			}
			logerror_phase = "calc_strength: unit loop (4) " + i + ": " + document.getElementById('unit' + i).value;
			res_c[0]+= (db_units[info_u[0]][3] / 100 * document.getElementById('unit_pcs' + i).value);
		}
	}
	if (document.getElementById('attack_item_plus_attack') && document.getElementById('attack_item_plus_attack').checked == true) { attack*= 1.1; }

	logerror_phase = "calc_strength step 2";

	if (document.getElementById('spells') && document.getElementById('spells').value != '') {
		var spells = document.getElementById('spells').value.split(',');
		for (var i = 0; i < spells.length - 1; i++) {
			var info = spells[i].split('=');
			var regex = new RegExp("(.*)%");
			var match = regex.exec(db_spells[info[0]][21]);
			if (match != null) {
				var val = 1 + (match[1] * info[2] / 100 / 100);
				if (val < 0.01) {
					console.log('slow goes below zero', info[0], info[2], 'movement change', val, match[1], db_spells[info[0]][21]);
					slowest*= 0.01;
				} else {
					slowest*= val;
					console.log('spell', info[0], 'movement change', val, match[1], db_spells[info[0]][21]);
				}
			}
		}
	}
	logerror_phase = "calc_strength step 3";

    var speed = 10 * 60;

	if (document.getElementById('speedbonus') && document.getElementById('speedbonus').value != '') {
		speed = speed / document.getElementById('speedbonus').value;
		console.log('speed default 1200, speedbonus', document.getElementById('speedbonus').value, ', now speed is', speed);
	}
	if (!document.getElementById('squares')) {
		document.getElementById('statusinfo').innerHTML = "squares element not found, attack plan closed probably";
		return;
	}
	var squares = Math.floor(document.getElementById('squares').innerHTML);
	if (slowest < 0.01) { slowest = 0.01; }
	var time = squares / slowest;
	console.log('time: ', squares, ' / ', slowest, ' = ', time);
	time = time * speed;
	time += (60 * 5);
	console.log('time: ', document.getElementById('squares').innerHTML, ' / ', slowest, " * ", speed, ' = ', time);

    
	logerror_phase = "calc_strength step 4";
	var travel = "";
	var traveldebug = "";
	if (document.getElementById('res')) {
		var res = document.getElementById('res').value.split(',');
		var count = 0;
		for (var r = 0; r < 8; r++) {
			var val = res_c[r] * (time / 60 / 60 * 12);
			val*= 1.2;
			if (document.getElementById('attacktype').value != 1) { val*= 2; }
			if (r == 4) { val+= res_c4; }
			/* console.log(r, val, res[r], res_c[r], time); */
			if (res[r] < val && val > 0) {
				document.getElementById('nomove5').style.visibility = 'visible';
				document.getElementById('nomove5').style.display = 'block';
				travel_res = 0;
				traveldebug+= "not enough: ";
			}
			if (val) {
				if (count) { travel+= ", "; }
				travel+= "<img style=\"margin: 0px;\" src=\"pics/city/res" + r + ".png\" height=\"20\" width=\"20\" /> ";
				travel+= Math.floor(val);
				traveldebug+= r + ": " + val + " (avail: " + res[r] + "), ";
				count = 1;
			}
		}
	}
	console.log('travel', travel);
	if (traveldebug) {
		logerror("Required for travel " + traveldebug, "debug");
	}
	if (document.getElementById('strength2')) {
		document.getElementById('strength2').innerHTML = Math.floor(attack / 100);
	}
	if (document.getElementById('arrive')) {
		document.getElementById('arrive').innerHTML = '';
		if (slowest != 10000) {
			document.getElementById('arrive').innerHTML = wdhm2(time);
		}
	}
	if (document.getElementById('travel')) {
		document.getElementById('travel').innerHTML = travel;
	}
}

function operationplan(id, layer) {
	logerror_phase = "operationplan";

	if (!layer || layer == undefined || layer === undefined) { layer = ''; }
	var prefix = layer + '_';
	document.getElementById(prefix + 'person' + id).style.visibility = 'hidden';
	document.getElementById(prefix + 'person' + id).style.display = 'none';
	document.getElementById(prefix + 'sabotage' + id).style.visibility = 'hidden';
	document.getElementById(prefix + 'sabotage' + id).style.display = 'none';

	var operation = document.getElementById(prefix + 'operation' + id).value;
	var start = 1;
	if (operation == 'assassination' || operation == 'kidnap') {
		if (document.getElementById(prefix + 'nopersons' + id)) {
			start = 0;
		} else {
			document.getElementById(prefix + 'person' + id).style.visibility = 'visible';
			document.getElementById(prefix + 'person' + id).style.display = 'block';
		}
	} else if (operation == 'sabotage') {
		if (document.getElementById(prefix + 'nosabotage' + id)) {
			start = 0;
		} else {
			document.getElementById(prefix + 'sabotage' + id).style.visibility = 'visible';
			document.getElementById(prefix + 'sabotage' + id).style.display = 'block';
		}
	}
	if (document.getElementById(prefix + 'spy_risk' + id)) {
		document.getElementById(prefix + 'spy_risk' + id).style.visibility = 'visible';
		document.getElementById(prefix + 'spy_risk' + id).style.display = 'block';
	}
	if (operation) {
		var caught = document.getElementById(prefix + 'see_spy' + id).value * document.getElementById(prefix + 'risk' + id).value * 2;
		caught-= document.getElementById(prefix + 'spy_level' + id).value * 2;
		caught+= 10;
		if (document.getElementById(prefix + 'item_plus_spy' + id) && document.getElementById(prefix + 'item_plus_spy' + id).checked == true) { caught-= 10; }
		if (caught < 0) { caught = 0; }
		if (caught > 100) { caught = 100; }
		document.getElementById(prefix + 'caught' + id).innerHTML = caught + "%";

		var success = document.getElementById(prefix + 'spy_level' + id).value * document.getElementById(prefix + 'risk' + id).value;
		console.log('success', document.getElementById(prefix + 'spy_level' + id).value, " * ", document.getElementById(prefix + 'risk' + id).value, " = ", success);
		document.getElementById(prefix + 'success' + id).innerHTML = success + "%";
		document.getElementById(prefix + 'chance' + id).style.visibility = 'visible';
		document.getElementById(prefix + 'chance' + id).style.display = 'block';
		if (start == 1) {
			document.getElementById(prefix + 'start' + id).style.visibility = 'visible';
			document.getElementById(prefix + 'start' + id).style.display = 'block';
		} else {
			document.getElementById(prefix + 'start' + id).style.visibility = 'hidden';
			document.getElementById(prefix + 'start' + id).style.display = 'none';
		}
	} else {
		document.getElementById(prefix + 'chance' + id).style.visibility = 'hidden';
		document.getElementById(prefix + 'chance' + id).style.display = 'none';
	}
}

function updatetype () {
	calc_strength();
	logerror_phase = "updatetype";

	if (!document.getElementById('attacktype')) { return; }
	
	var type = document.getElementById('attacktype').value;
	console.log('updatetype', play_attack_plan, type);
	var canSee;
	if (IE) {
		canSee = 'block'
	} else {
		canSee = 'table-row';
	}
	if (document.getElementById('withdraw') != null) {
		if (type == 0) {
			document.getElementById('withdraw').style.visibility = 'visible';
			document.getElementById('withdraw').style.display = canSee;
		} else {
			document.getElementById('withdraw').style.visibility = 'hidden';
			document.getElementById('withdraw').style.display = 'none';
		}
	}
	if (document.getElementById('risk') != null) {
		if (type == 0) {
			document.getElementById('risk').style.visibility = 'visible';
			document.getElementById('risk').style.display = canSee;
		} else {
			document.getElementById('risk').style.visibility = 'hidden';
			document.getElementById('risk').style.display = 'none';
		}
	}
	if (document.getElementById('strength') != null) {
		if (type == 0) {
			document.getElementById('strength').style.visibility = 'visible';
			document.getElementById('strength').style.display = canSee;
		} else {
			document.getElementById('strength').style.visibility = 'hidden';
			document.getElementById('strength').style.display = 'none';
		}
	}
	if (document.getElementById('spy_mission') != null) {
		if (type == 3) {
			document.getElementById('spy_mission').style.visibility = 'visible';
			document.getElementById('spy_mission').style.display = canSee;
		} else {
			document.getElementById('spy_mission').style.visibility = 'hidden';
			document.getElementById('spy_mission').style.display = 'none';
		}
	}

	if (realm_version >= 2.1) {
		if (document.getElementById('attack_item_plus_attack')) {
			if (type == 0) {
				document.getElementById('attack_item_plus_attack').style.visibility = 'visible';
			} else {
				document.getElementById('attack_item_plus_attack').style.visibility = 'hidden';
			}
		}
		if (document.getElementById('attack_item_plus_attackrumor')) {
			if (type == 0) {
				document.getElementById('attack_item_plus_attackrumor').style.visibility = 'visible';
			} else {
				document.getElementById('attack_item_plus_attackrumor').style.visibility = 'hidden';
			}
		}
		if (document.getElementById('attack_item_plus_conquer')) {
			if (type == 0) {
				document.getElementById('attack_item_plus_conquer').style.visibility = 'visible';
			} else {
				document.getElementById('attack_item_plus_conquer').style.visibility = 'hidden';
			}
		}
		if (document.getElementById('attack_item_plus_returnmove')) {
			if (type != 1) {
				document.getElementById('attack_item_plus_returnmove').style.visibility = 'visible';
			} else {
				document.getElementById('attack_item_plus_returnmove').style.visibility = 'hidden';
			}
		}
	}

	var noattack = -1;
	if (document.getElementById('noattack')) {
		noattack = document.getElementById('noattack').value;
	}
	if (noattack < 1 && document.getElementById('noattackb')) {
		noattack = document.getElementById('noattackb').value;
	}

	console.log('play_attack_plan?', type, play_attack_plan, noattack);
	if (type == 0 && !play_attack_plan && !noattack) {
		console.log('playsound attackplan...');
		play_attack_plan = 1;
		playsound('attack_plan');
	} else if (type == 3 && !play_spy) {
		play_spy = 1;
		playsound('spy');
	}
	attackplan(1, 1);
}

function attackplan (update_from, update_target) {
	logerror_phase = "attackplan";
	console.log('attackplan', update_from, update_target);

	if (document.getElementById('target_own').value && document.getElementById('target')) {
		document.getElementById('target').value = document.getElementById('target_own').value;
	}
	var type = document.getElementById('attacktype').value;

	console.log("type", type);

	if (document.getElementById('autocomplete_choices')) {
		document.getElementById('autocomplete_choices').style.visibility = 'hidden';
	}
	
	if (update_from) { loadinfo('from'); }
	if (update_target) { loadinfo('target'); }

}

function loadinfo (city) {
	logerror_phase = "loadinfo " + city;

	var id = '';
	var city_name = '';
	if (city == 'from') {
		id = document.getElementById(city).value;
	} else {
		if (document.getElementById('target')) {
			id = document.getElementById(city).value;
		} else {
			id = document.getElementById('target_own').value;
			city_name = document.getElementById('attack_autocomplete').value;
		}
	}
	if ( !id && !city_name ) { return; }
	logerror_phase = "loadinfo step 2 " + city;
	
	var type = document.getElementById('attacktype').value;

	var location = "";
	var url = baseurl + '/getinfo.cgi?city=' + id + "&type=" + type;
	url+= '&rand=' + Math.round(100000*Math.random()) + "&origin=" + city + "&city_name=" + city_name;

	if (city == 'from') {
		console.log('get units to getinfo');
		for (var i = 1; i < 1000; i++) {
			var id2 = 'unit_pcs' + i;
			var id = 'unit' + i;
			if (document.getElementById(id) && document.getElementById(id2)) {
				url+= "&unit" + i + "=" + encodeURIComponent(document.getElementById(id).value);
				url+= "&unit_pcs" + i + "=" + encodeURIComponent(document.getElementById(id2).value);
			}
		}
	}

    var container = document.getElementById(city + '_div');

	logerror_phase = "loadinfo step 3 " + city;
	
	new Ajax.Updater(
        container,
        url,
        {
            onException: exception_call,
            onComplete: function(transport) {
                if ( document.getElementById('coords_target') && document.getElementById('coords_from') ) {
					logerror_phase = "loadinfo calc len " + city;
                    var coords1 = document.getElementById('coords_from').value.split(',');
                    var coords2 = document.getElementById('coords_target').value.split(',');
                    var x = Math.abs(coords1[0] - coords2[0]);
                    var y = Math.abs(coords1[1] - coords2[1]);
                    var len = Math.round(Math.sqrt(Math.pow(x, 2) + Math.pow(y,2)));
                    document.getElementById('squares').innerHTML = len;
                }

                canmoveattack(city);
				logerror_phase = "loadinfo step 4 " + city;
                if (city == 'target' && document.getElementById('strength_d') && document.getElementById('defence_strength') ) {
                    document.getElementById('strength2_d').innerHTML = document.getElementById('defence_strength').value;
                }

                calc_strength();
                document.getElementById('connectioninfo').innerHTML = "";
            },

            onFailure: function(transport) {
                document.getElementById('connectioninfo').innerHTML = "Connection error";
            }
        }
    );
}

function canmoveattack (origin) {
	logerror_phase = "canmoveattack";

	if (!travel_res) { return; }
	if (!document.getElementById('noattackb') || !document.getElementById('noattack')) {
		console.log('noattack || noattackb element not found even called canmoveattack');
		return;
	}
	console.log('canmoveattack', origin, document.getElementById('nomoves'));

	if (document.getElementById('target')) {
        if (!document.getElementById('target').value
            && !document.getElementById('nomoves')
            && !document.getElementById('autocomplete').value
        ) { return; }
	} else {
        if (!document.getElementById('target_own').value
            && !document.getElementById('nomoves')
            && !document.getElementById('attack_autocomplete').value
        ) { return; }
	}

	for (var n = 1; n <= 8; n++) {
		document.getElementById('nomove' + n).style.visibility = 'hidden';
		document.getElementById('nomove' + n).style.display = 'none';
		document.getElementById('noattack' + n).style.visibility = 'hidden';
		document.getElementById('noattack' + n).style.display = 'none';
	}

	var type = document.getElementById('attacktype').value;

	var noattack = document.getElementById('noattack');
	var noattackb = document.getElementById('noattackb');
    var real_noattack = 0;
    if ( noattack )                       { real_noattack = noattack.value;  }
    if ( real_noattack < 1 && noattackb ) { real_noattack = noattackb.value; }

	var nomove = 0;
	if (document.getElementById('nomove')) {
		nomove = document.getElementById('nomove').value;
	}
	console.log("real_noattack/nomove/type/play_attack_plan", real_noattack, nomove, type, play_attack_plan)

	if (document.getElementById('honor2') != null) {
		var canSee;
		if (IE) {
			canSee = 'block'
		}
		else {
			canSee = 'table-row';
		}
		if (type == 0 && document.getElementById('honor') != null) {
			document.getElementById('honor2').style.visibility = 'visible';
			document.getElementById('honor2').style.display = canSee;
			document.getElementById('honor3').innerHTML = document.getElementById('honor').value;
		}
		else {
			document.getElementById('honor2').style.visibility = 'hidden';
			document.getElementById('honor2').style.display = 'none';
		}
	}

	if (real_noattack > 0 && type == 0) {
		document.getElementById('noattack' + real_noattack).style.visibility = 'visible';
		document.getElementById('noattack' + real_noattack).style.display = 'block';
	}
	else if (nomove > 0 && (type == 1 || type == 2)) {
		document.getElementById('nomove' + nomove).style.visibility = 'visible';
		document.getElementById('nomove' + nomove).style.display = 'block';
	}

	if (document.getElementById('attack_plus_attack') && document.getElementById('attack_plus_attackrumor') && document.getElementById('attack_plus_returnmove')) {
		if (type == 0) {
			document.getElementById('attack_plus_attack').style.visibility = 'visible';
			document.getElementById('attack_plus_attack').style.display = 'block';
			document.getElementById('attack_plus_attackrumor').style.visibility = 'visible';
			document.getElementById('attack_plus_attackrumor').style.display = 'block';
			document.getElementById('attack_plus_returnmove').style.visibility = 'visible';
			document.getElementById('attack_plus_returnmove').style.display = 'block';			
		}
		else if (type == 1) {
			document.getElementById('attack_plus_attack').style.visibility = 'hidden';
			document.getElementById('attack_plus_attack').style.display = 'none';
			document.getElementById('attack_plus_attackrumor').style.visibility = 'hidden';
			document.getElementById('attack_plus_attackrumor').style.display = 'none';
			document.getElementById('attack_plus_returnmove').style.visibility = 'hidden';
			document.getElementById('attack_plus_returnmove').style.display = 'none';			
		}
		else if (type == 2 || type == 3) {
			document.getElementById('attack_plus_attack').style.visibility = 'hidden';
			document.getElementById('attack_plus_attack').style.display = 'none';
			document.getElementById('attack_plus_attackrumor').style.visibility = 'hidden';
			document.getElementById('attack_plus_attackrumor').style.display = 'none';
			document.getElementById('attack_plus_returnmove').style.visibility = 'visible';
			document.getElementById('attack_plus_returnmove').style.display = 'block';			
		}
	}
	if (type == 0 && play_attack_plan == 0 && real_noattack == 0) {
		console.log('playsound attackplan..');
		play_attack_plan = 1;
		playsound('attack_plan');
	}

}

function loadhistory2 (secs, coords, range, account, city, fulldate, type, timeframe, max_history, divid, city_name, search) {
	logerror_phase = "loadhistory2";

	console.log('loadhistory2', secs, ",", coords,",", range,",", account,",", city,",", fulldate,",", type,",", timeframe,",", max_history,",", divid,",", city_name,",", search);

    var url = baseurl + '/gethistory.cgi?coords=' + coords + '&lastpos=0&range=' + range + "&search=" + search
        + '&account=' + account + '&max=' + max_history + '&city=' + city + '&city_name=' + city_name + '&fulldate=' + fulldate
        + '&type=' + type + '&timeframe=' + timeframe + '&rand=' + Math.round(100000*Math.random());

	if (gof2) { url+= "&gof2=simple"; }
	
    new Ajax.Request(url, {
        method: 'get',
        onSuccess: function(transport, oJSN) {
            handlehistory2(transport, oJSN, divid);
        },
        onFailure: function(transport) {
            console.log(transport.responseText);
			show_notice('error', lang.get('err_connection'));
        }
    });
}

function handlehistory2(transport, oJSN, divid) {
	logerror_phase = "handlehistory2";

	console.log('handlehistory2');
	if (transport.responseText == 'Server too busy, try again later.') {
		console.log('Server busy');
		return;
	}

	var need_fb_reparse = 0;
	var data = transport.responseText.evalJSON();
	for (var i = data.results.length - 1; i >= 0; i--) {
		var divelement = document.createElement('div');
		divelement.style.display = 'none';
		divelement.style.float = 'top';
		var n = Math.round(1000000*Math.random());
		divelement.innerHTML = "<a class=\"addthis_button_compact\" id=\"historyline" + n + "\"></a>" + data.results[i];
		document.getElementById(divid).appendChild(divelement);
		new Effect.Appear(divelement);
		
		if( data.results[i].match( /fb:login-button/ ) )
			need_fb_reparse = 1;
		/*
		var id = ""
		var regex = new RegExp('<span class="hidden">(.*?)</span>');
		var match = regex.exec(data.results[i]);
		if (match != null) { id = match[1]; }

		addthis.button('#historyline' + n, {}, {
			url: baseurl + "/gethistory.cgi?search=" + id + "&ref_a=" + ULI,
			title: "Glory of Fellowland",
		});
		*/
	}

	if(need_fb_reparse)
		FB.XFBML.Host.parseDomElement(document.getElementById(divid));

	console.log('handlehistory2 done', data.results.length);
}

function historyupdate(layer) {
	logerror_phase = "historyupdate";
    document.getElementById(layer+'history_results').innerHTML = '';

    var type = '';
    if(document.getElementById(layer+'history_type'))
        type=document.getElementById(layer+'history_type').value;

    var village_name = '';
    if(document.getElementById(layer+'history_autocomplete_village_name'))
        village_name=document.getElementById(layer+'history_autocomplete_village_name').value;

    var duration = 86400;
    if(document.getElementById(layer+'history_duration'))
        duration=document.getElementById(layer+'history_duration').value;

    var max_history = 25;
    if(document.getElementById(layer+'history_max_results'))
        max_history=document.getElementById(layer+'history_max_results').value;

    var account = "";
    if(document.getElementById(layer+'history_autocomplete_account'))
        account=document.getElementById(layer+'history_autocomplete_account').value;

	var search = '';
	/*
	if(document.getElementById('history_search')) {
		search=document.getElementById('history_search').value;
		duration = 60 * 60 * 24 * 365 * 10;
	}*/
	
	/* loadhistory2 (secs, coords, range, account, city, fulldate, type, timeframe, max_history, layer, city_name, search) { */
	loadhistory2(    '',     '',    '', account, '',        1, type, duration,  max_history, layer+'history_results', village_name, search );
}

function village_name_changed(name_id, id_id) {
	if( document.getElementById(name_id).value == '' ) {
		document.getElementById(id_id).value = '';
	}
}

function showad() {
	var html = "<iframe style=\"height: 300px; width: 350px;\" id=\"popupad\" src=\"rightadframe.cgi?gof2=none&popup=" + Math.round(100000*Math.random()) + "\" frameborder=\"0\" allowTransparency=\"true\"></iframe>";
	openlayer('', 'advertisement', '', 440, '', '', html);
}

function showad_trialpay() {
	var html = lang.get('trialpay');
	
	html+= "<a target=\"_blank\" href=\"http://www.trialpay.com/productpage/?c=7c04df1&tid=RHyqy03&sid=" + project + ":" + ULI + "\">";
	html+=  "<img border=\"0\" style=\"margin: 10px; float: right;\" src=\"http://assets.trialpay.com/mi/?rc=v&ri=";
	if (gof_info.get('L') == 15) {
		html+= "1361956";
	} else if (gof_info.get('L') == 7) {
		html+= "1368405";
	} else {
		html+= "1368898";
	}
	html+= "&p=733t73j&t=RHyqy03&sid=" + project + ":" + ULI + "\" /></a>";
	
	openlayer('', 'advertisement', '', 440, '', '', html);
}

var show_ad = 10;
var last_getstatusraport = 0;
function getstatusraport (onlyown, showless, skip) {
	logerror_phase = "getstatusraport";

	var d = new Date();
	var now = d.getTime();

	if (now - last_getstatusraport < 1000 * 10) {
		if (document.getElementById('statusinfo')) {
			document.getElementById('statusinfo').innerHTML = "too soon for getstatusraport";
		}
		if (!skip) {
			setTimeout("getstatusraport('', '')", 60000);
		}
		return "";
	}
	last_getstatusraport = now;

	console.log('getstatusraport', onlyown, showless, " -- ", skip);
    if (onlyown === undefined || onlyown === '') { onlyown = onlyown2; }
    if (showless === undefined || showless === '') { showless = showless2; }

	console.log('getstatusraport...', onlyown, showless, " --- ", onlyown2, showless2);

	var url = baseurl + '/getstatusraport.cgi?onlyown=' + onlyown + "&showless=" + showless;
	url+= '&rand=' + Math.round(100000*Math.random());

	show_ad++;
	if (gof2 && registry.get('plus_assistant') != 1) {
		if (show_ad == 20) {
			showad();
		} else if (show_ad == 40) {
			show_ad = 0;
			showad_trialpay();
		}
	}

    var history_elem = document.getElementById('getstatusraport_history');
	if ( history_elem )  url += "&history=" + history_elem.value;

    var handle_status_raport = function(transport) {

		if ( !transport.responseText == "-1") { not_logged_in = 1; }
				
        if ( !transport.responseText || transport.responseText == 'Server too busy, try again later.' || transport.responseText == '-1') { return; }

        if ( document.getElementById('music_file') )
            playsound(document.getElementById('music_file').value, document.getElementById('music_level').value);

		not_logged_in = 0;
        return;
    }

    var container = document.getElementById('statusraport');

	new Ajax.Updater(
        container,
        url,
        {
            onException: exception_call,
            onComplete: handle_status_raport,
            onFailure: function(transport) {
                console.log(transport.responseText);
            }
        }
    );

	if ( !skip && !not_logged_in) {
		var interval = 1000 * 60 * 4;
		var d = new Date();
		var now = d.getTime();
		if (!registry.get('created')) {
			interval = 10000;
		} else if (registry.get('created') && now / 1000 - registry.get('created') < 60 * 5) {
			interval = 15000;
		} else if (registry.get('created') && now / 1000 - registry.get('created') < 60 * 15) {
			interval = 30000;
		} else if (registry.get('created') && now / 1000 - registry.get('created') < 60 * 30) {
			interval = 60000;
		}
		console.log('getstatusraport interval', interval);
		setTimeout("getstatusraport('', '');", interval);
	}

    if (onlyown === '' || onlyown === undefined) {
    } else {
        onlyown2 = onlyown;
        showless2 = showless;
    }

	console.log('getstatusraport new values', onlyown2, showless2);
}

function picktarget () {
	logerror_phase = "picktarget";

	var from = document.getElementById('from').value;
	var type = document.getElementById('attacktype').value;
	var target = "";

    var url = "world.cgi?from=" + from + "&type=" + type + "&target=" + target;

	var location = "";
    var location_element = document.getElementById('location');
    if ( location_element ) {
        location = location_element.value;
		if (!gof2) {
		    url = url + "&location=" + location;
		}
	}

	if (gof2) {
		map_from_city = from;
		map_spell = '';
		map_power = '';
		map_move_type = type;
		mappos(location);
		openlayer('', 'world');
	} else {
		openlayer(url, 'world');
	}
}

function getmapdata2(force) {
	logerror_phase = "getmapdata2";

	var url = baseurl + '/getmapdata.cgi?rand=' + Math.round(100000*Math.random());

	var d = new Date();
	var now = d.getTime();

	if ( (force == 1 && now - map_updated < 1000 * 60) || now - map_updated < 1000 * 60 * 10) {
		console.log('too soon to update mapdata2', (now - map_updated) / 1000);
		return;
	}

	document.getElementById('statusinfo').innerHTML = "getmapdata2...";
	map_updated = now;

	console.log('getmapdata query', url);

	new Ajax.Request(url, {
		onException: exception_call,
		onSuccess: function(transport) {
			console.log('getmapdata2 ok, parsing data');
			if (transport.responseText == 'Server too busy, try again later.') {
				console.log('Server busy');
				return;
			}
			var data = transport.responseText.split('\n');
			var to_y = data.length;
			for (var yyy = 0; yyy < to_y; yyy++) {
				var info = data[yyy].split('|');
				var to_x = info.length;
				map[yyy] = new Array;
				for (var xxx = 0; xxx < to_x; xxx++) {
					var maptile = info[xxx].split(',');
					map[yyy][xxx] = new Array(maptile[0], maptile[1], maptile[2], maptile[3]);
				}
			}
			document.getElementById('statusinfo').innerHTML = "getmapdata2 ok (y: " + to_y + ")";
			console.log('getmapdata2 ok, lines', to_y);
		},
		onFailure: function(transport) {
			document.getElementById('statusinfo').innerHTML = "getmapdata2 failed";
			console.log('getmapdata2 failed');
			setTimeout('getmapdata2()', (60 * 1000));
			logerror('getmapdata2 failed', "info");
		}
	});
}

function getcitydata2 (id, force) {
	logerror_phase = "getcitydata2";
	var d = new Date();
	var now = d.getTime();

	if ( citydata_loaded && (now - citydata_updated) < 1000 * 60 * 30 && force != 1) {
		console.log('too soon to update getcitydata', (now-citydata_updated) / 1000);
		return;
	} else if ( now - citydata_updated < 1000 * 30) {
		console.log('2. too soon to update getcitydata', (now-citydata_updated) / 1000);
		return;
	}

	document.getElementById('statusinfo').innerHTML = "getcitydata2...";
	console.log('getcitydata2 query');
	citydata_updated = now;
	citydata_loaded = 0;

	var loadingstatus = "";

	var url = baseurl + '/getcitydata.cgi?rand=' + Math.round(100000*Math.random());
	if ( id 			) url+= "&cities=" + id;
	if ( !gof2 && getplayerstats ) { url+= "&getplayerstats=1"; }
	if ( gof2 && registry.get('plus_assistant') && registry.get('plus_assistant') == 1) { url+= "&getplayerstats=1"; }

	new Ajax.Request(url, {
		onFailure: function(transport) {
			console.log('getcitydata failed');
			document.getElementById('statusinfo').innerHTML = "getcitydata2 failed";
			document.getElementById('connectioninfo').innerHTML = "Get citydata2 failed";
			setTimeout('getcitydata2()', 60000);
			logerror('getcitydata2 failed: ' + transport.status + ' "' + loadingstatus + '"', "info");
		},
		onException: exception_call,
		onSuccess: function(transport) {
			if (transport.responseText == 'Server too busy, try again later.') {
				document.getElementById('connectioninfo').innerHTML = "Server busy.";
				console.error('Server busy');
				return;
			}

			var newdata = transport.responseText.split('\n');
			var citycount = newdata.length;
			var phase = 1;

			console.log('getcitydata2 ok, parsing data, entries', citycount, newdata.length);

			for (var i = 0; i < citycount - 1; i++) {
				var info = newdata[i].split('|');

				if ( !info[0] ) {
					phase = 2;
					continue;
				}

				if (phase == 1) {
					citydata[info[0]] = info;
				}
				else {
					playerstats += newdata[i] + "\n";
				}
			}

			console.log('getcitydata2 done, cities', newdata.length, "playerstats", playerstats);
			document.getElementById('statusinfo').innerHTML = "getcitydata2 ok";
			document.getElementById('connectioninfo').innerHTML = "";
			citydata_loaded = 1;
		}
	});
}

function comparetribes2 (account, account2) {
	if (registry.get('plus_assistant') && registry.get('plus_assistant') == 1) {
		openlayer('', 'compare_' + account, '', 400, '', '', comparetribes(account, account2), 0, 0, mouseX, mouseY);
	} else {
		if (realm_version >= 2.1) {
			openlayer('discovery.cgi?items=1', 'discovery');
		} else {
			openlayer('plus.cgi?assistant=1', 'plus');
		}
	}
}

function comparetribes (account, account2) {
	logerror_phase = "comparetribes";
	var html = "";
	html+= '<table class="table">';
	html+= '<tr class="tabletitle">';

	html+= '<td class="tabletitle">&nbsp;</td>';
	html+= '<td class="tabletitle">';
	html+= '<img style="float:  left; margin: 0px;height: 16px;" src="flags/' + ULI + '.gif" alt="" />';
	html+= '</td>';
	html+= '<td class="tabletitle">';
	html+= '<img style="float: right; margin: 0px;height: 16px;" src="flags/' + account2 + '.gif" alt="" />';
	html+= '</td>';
	html+= '</tr>';

	var count = 0;
	if (playerstats) {
		var info_ps = playerstats.split('\n');
		info_a = Array;
		info_d = Array;
		for (var i = 0; i < info_ps.length; i++) {
			var info_s = info_ps[i].split('|');

			if (info_s[0] == ULI || (registry.get('master') != '' && info_s[0] == registry.get('master'))) { info_a = info_s; }
			if (info_s[0] == account) { info_d = info_s; }
		}

		for (var j = 1; j < 14; j++) {
			if (j == 10 || j == 11 || j == 9) {
				continue;
			}

			info_a[j] = Math.floor(info_a[j]);
			info_d[j] = Math.floor(info_d[j]);
			var max = 0;
			if (Math.floor(info_a[j]) > Math.floor(info_d[j])) { max = Math.floor(info_a[j] + 1); } else { max = Math.floor(info_d[j] + 1); }
			if (max < 1) { max = 1; }

			html+= '<tr class="tablerow">';
			html+= '<td class="tablecell">' + lang.get('position_' + j) + '</td>';

			html+= '<td class="tablecell" style="text-align: left;">';
			html+= '<div class="bar_attack2 bar_map" title="' + info_a[j] + '" style="float: left; width:' + Math.floor((info_a[j] + 0.02) / max * 50) + 'px">&nbsp;</div>';
			html+= '</td>';

			html+= '<td class="tablecell" style="text-align: right;">';
			html+= '<div class="bar_defence2 bar_map" title="' + info_d[j] + '" style="float: right; width:' + Math.floor((info_d[j] + 0.02) / max * 50) + 'px">&nbsp;</div>';
			html+= '</td>';

			html+= '</tr>';
			console.log('strengths', Math.floor((info_a[j] + 1) / max * 50), Math.floor((info_d[j] + 1) / max * 50), info_a[j], info_d[j], max);
		}
	} else {
		show_notice('error', lang.get('err_not_loaded'));
		getcitydata2();
	}

	html+= "</table>";
	return html;
}

function getdbs () {
	logerror_phase = "getdbs";
	var url = baseurl + '/getdbs.cgi?science=1&terrain=1&rand=' + Math.round(100000*Math.random());

	var d = new Date();
	var now = d.getTime();

	if (now - dbs_updated < 1000 * 60 * 3) {
		console.log('too soon to update dbs', (now-dbs_updated) / 1000);
		return;
	}

	document.getElementById('statusinfo').innerHTML = "getdbs...";
	console.log('getdbs query', url);

	var nomore = 0;
	new Ajax.Request(url, {
		onException: exception_call,
		onSuccess: function(transport) {
			if (transport.responseText == 'Server too busy, try again later.') {
				console.log('Server busy');
				return;
			}
			var data = transport.responseText.split('\n');
			console.log('getdbs ok, parsing data, entries', data.length);
			var phase = 1;
			for (var i = 0; i < data.length - 1; i++) {
				var info = data[i].split('|');
				if (info[0] == '') {
					phase++;
				} else {
					if (phase == 1) {
						db_buildings[info[0]] = info;
						db_buildings2[info[0]] = info;
					} else if (phase == 2) {
						db_units[info[0]] = info;
						db_units2[info[0]] = info;
					} else if (phase == 3) {
						db_spells[info[0]] = info;
						db_spells2[info[0]] = info;
					} else if (phase == 4) {
						db_science[info[0]] = info;
					} else if (phase == 5) {
						db_terrain[info[0]] = info;
					} else if (phase == 6) {
						console.log('skip data, invalid phase', phase);
						if (nomore == 0) {
							logerror('getdbs invalid phase ' + phase, "error");
							nomore = 1;
						}
					}
				}
			}
			console.log('getdsb done, entries', data.length);
			if (db_buildings[1] && db_units[1] && db_spells[1] && db_science[1] && db_terrain[1]) {
				document.getElementById('statusinfo').innerHTML = "getdbs ok";
				document.getElementById('connectioninfo').innerHTML = "";
				dbs_updated = now;

				db_buildings2.sort(sortby(1, 1));
				if (db_buildings2[db_buildings2.length-1] == undefined) { db_buildings2.pop(); }
	
				db_units2.sort(sortby(1, 1));
				if (db_units2[db_units2.length-1] == undefined) { db_units2.pop(); }

				db_spells2.sort(sortby(1, 1));
				if (db_spells2[db_spells2.length-1] == undefined) { db_spells2.pop(); }
			} else {
				logerror("getdbs failed: '" + db_buildings[1] + "', '" + db_units[1] + "', '" + db_spells[1] + "', '" + db_science[1] + "', '" + db_terrain[1] + "'", "info");
				show_notice('error', lang.get('err_connection'));
				setTimeout('getdbs()', 60000);

				document.getElementById('statusinfo').innerHTML = "getdbs failed";
				document.getElementById('connectioninfo').innerHTML = "Failed to load databases";
			}
		},
		onFailure: function(transport) {
			console.log('dbs failed');
			document.getElementById('statusinfo').innerHTML = "getdbs failed";
			document.getElementById('connectioninfo').innerHTML = "Could not get databases";
			setTimeout('getdbs()', 60000);
			logerror('getdbs failed', "warning");
			show_notice('error', lang.get('err_connection'));
		}
	});
}

function wheel(event){
	console.log('mousewheel on:', mousewheel);
	if (mousewheel == 0) { return; }
	var delta = 0;
	if (!event) event = window.event;
	if (event.wheelDelta) {
		delta = event.wheelDelta/120;
		if (window.opera) delta = -delta;
	} else if (event.detail) {
		delta = -event.detail/3;
	}
	if (delta) {
		handlemousewheel(delta);
		if (event.preventDefault) {
			event.preventDefault();
		} else {
			event.returnValue = false;
		}
	}
}

function handlemousewheel(delta) {
	if (delta < 0) {
		zoom+= 1;
		if (zoom > max_zoom) {
			zoom = max_zoom;
		} else {
			visible_x-= (Math.floor(show_map_size_x[zoom] / 2) - Math.floor(show_map_size_x[zoom-1] / 2));
			visible_y-= (Math.floor(show_map_size_y[zoom] / 2) - Math.floor(show_map_size_y[zoom-1] / 2));
			if (updatingmap == 0) { setTimeout('updatemap(0, 1);', 200); updatingmap = 1; }
		}
	}
	if (delta > 0) {
		zoom-= 1;
		if (zoom < 1) {
			zoom = 1;
		} else {
			visible_x-= (Math.floor(show_map_size_x[zoom] / 2) - Math.floor(show_map_size_x[zoom+1] / 2));
			visible_y-= (Math.floor(show_map_size_y[zoom] / 2) - Math.floor(show_map_size_y[zoom+1] / 2));
			if (updatingmap == 0) { setTimeout('updatemap(0, 1);', 200); updatingmap = 1; }
		}
	}
}

function catchmousewheel() {
	if (window.addEventListener) {
		window.addEventListener('DOMMouseScroll', wheel, false);
	}
	else {
		window.onmousewheel = document.onmousewheel = wheel;
	}
	console.log('catch mouse wheel');
}

var mySound = new Array;
var playsound_problems = 0;
function playsound(file, level, duplicateid, channel) {
	logerror_phase = "playsound " + file + ", level " + level;
	console.log('Try to play sound', sounds, file, " level", level, "channel", channel);
	if (level === undefined || level == undefined) { level = 0; }
	if (channel === undefined || channel == undefined) { channel = "default"; }
	if (sounds == 0) {
		console.log('sounds off');
		return;
	}
	if (!soundManager) {
		console.log('soundManager object not found');
		return;
	}

	var d = new Date();
	var now = d.getTime();
	if (mySound[channel] && mySound[channel].playState == 1 && mySound[channel].readyState != 2) {
		console.log('playState=1, too soon to play anything yet');
		return;
	}
	if (duplicateid && last_played.get(duplicateid)) {
		console.log('played already', duplicateid);
		return;
	}

	if (level > 0 && now - last_played.get(file) < 10 * 60 * 1000 * level) {
		console.log(file, ' played lately, skip', last_played.get(file), 'now: ', now);
		return;
	}

	playsound_problems = 1;
	soundManager.onerror = function() {
		logerror('SoundManager failed to load', 'info');
		document.getElementById('statusinfo').innerHTML = "Playing sound failed: " + mySound[channel];
		return 1;
	}
	
	if (!soundManager) {
		logerror('soundManager is not defined', 'info');
		document.getElementById('statusinfo').innerHTML = "Playing sound failed, soundManager is not defined";
		return;
	}

	var audioURL = baseurl + "/mp3/" + file + ".mp3";
	soundManager.url = '../';
	soundManager.useConsole = true;
	soundManager.consoleOnly = true;
	soundManager.wmode = 'transparent';
	
	logerror_phase = "playsound create object " + file + ", level " + level;
	try {
		mySound[channel] = soundManager.createSound({
			id: file,
			url: audioURL,
			autoLoad: true,
			autoPlay: true,
			volume: 0
		});
	} catch (e) {
		logerror("Can't create sound object " + e, "info");
		return;
	}
	logerror_phase = "playsound object created probably " + file + ", level " + level;

	if (mySound[channel]) {
		try {
			mySound[channel].play();
			fadeInSound(file, 4); // fade a sound in
		} catch (e) {
			logerror("Can't play sound " + e, "info");
			return;
		}
		last_played.set(file, now);
		if (duplicateid) { last_played.set(duplicateid, now); }
		lastplay = now;
		console.log('playing', file);
		logerror_phase = "playsound likely playing ok " + file + ", level " + level;
		playsound_problems = 0;
	}
	else {
		console.log('mySound failed', mySound[channel]);
		document.getElementById('statusinfo').innerHTML = "Playing sound failed: " + mySound[channel];
		logerror_phase = "playsound probably failed " + file + ", level " + level;
	}

	return;
}

function fadeInSound(soundID,amount) {
	var s = soundManager.getSoundById(soundID);
	var vol = s.volume;
	if (vol == 100) return false;
	s.setVolume(Math.min(100,vol+amount));
	setTimeout(function(){fadeInSound(soundID,amount)},20);

	return 1;
}

function fadeOutSound(soundID,amount) {
	var s = soundManager.getSoundById(soundID);
	var vol = s.volume;
	if (vol == 0) return false;
	s.setVolume(Math.max(0,vol+amount));
	setTimeout(function(){fadeOutSound(soundID,amount)},20);

	return 1;
}

function stopsounds() {
	if (mySound['default']) { mySound['default'].stop(); }
	for (var n = 1; n <= 10; n++) {
		if (mySound['channel' + n]) { mySound['channel' + n].stop(); }
	}
}

function setsounds(togle) {
	document.cookie = 'sounds=' + togle + ';expires=Wed, 01-Jan-2020 00:00:01 GMT;path=/;';
	sounds = togle;
	if (sounds == 1) {
		soundManager.reboot();
	} else {
		stopsounds();
	}
}

function soundswindow() {
	var html = "<h1 class=\"" + registry.get('race') + "\" id=\"title_soundswindow\">" + lang.get('sounds') + "</h1>";

	if 	(playsound_problems == 1) { show_notice('error', lang.get("playsound_problems")); }

	html+= "<span class=\"link\" onclick=\"stopsounds();hidelayer('soundswindow');\">" + lang.get("silence") + "</span><br />";

	if (sounds == 1) {
		html+= "<span class=\"link\" onclick=\"setsounds(0);hidelayer('soundswindow');\">" + lang.get("soundsoff") + "</span><br />";
	} else {
		html+= "<span class=\"link\" onclick=\"setsounds(1);hidelayer('soundswindow');\">" + lang.get("soundson") + "</span><br />";
	}
	openlayer('', 'soundswindow', '', 300, '', '', html, 0, 0, mouseX - 40, mouseY + 20);
}

function maxtrade(res_i) {
	logerror_phase = "maxtrade";
	var carry = 5000 * document.getElementById('trademen').value;

	console.log('maxtrade: carry, res_i', carry, res_i);

	document.getElementById('res_' + res_i).value = 0;

	var carrying = 0;
	for (var i = 1; i <= 8; i++) {
		var id = 'res_' + i;
		if (document.getElementById(id) && document.getElementById(id).value > 0) {
			if (i == 8) {
				carrying+= (document.getElementById(id).value / 10);
			}
			else {
				carrying+= (document.getElementById(id).value - 0);
			}
		}
	}
	console.log('maxtrade: carrying', carrying);

	var carry_more = carry - carrying;
	console.log('carry_more (carry, carrying)', carry_more, carry, carrying);
	if (carrying < carry) {
		console.log('carrying is less than carry');
		if (res_i == 8) { carry_more*= 10; }
		console.log('max:', document.getElementById('res_max_' + res_i).value);
		if (carry_more > document.getElementById('res_max_' + res_i).value && document.getElementById('res_max_' + res_i).value > 0) {
			carry_more = document.getElementById('res_max_' + res_i).value;
		}
	}
	else {
		carry_more = 0;
	}
	if (res_i == 8) {
		carrying+= carry_more / 10;
	}
	else {
		carrying+= carry_more;
	}

	document.getElementById('res_' + res_i).value = Math.floor(carry_more);
	console.log('carry_more', carry_more);
	count_trademen();
}

function count_trademen() {
	logerror_phase = "count_trademen";
	var carrying = 0;
	for (var i = 1; i <= 8; i++) {
		var id = 'res_' + i;
		if (document.getElementById(id)) {
			if (i == 8) {
				carrying+= (document.getElementById(id).value / 10);
			}
			else {
				carrying+= (document.getElementById(id).value - 0);
			}
		}
	}
	console.log('carrying', carrying);

	console.log('trademen_id', document.getElementById('trademen_id').value);
	document.getElementById(document.getElementById('trademen_id').value).value = Math.floor(((carrying - 0) + 4999) / 5000);

	calc_strength();
}

function select_all_units(f) {
	logerror_phase = "select_all_units";
    var i = 1;

    var u = document.getElementById('unit_pcs' + i);
    while (u != null) {
        u.value = document.getElementById('unit_pcs' + i + "_max").value;

        i++;
        u = document.getElementById('unit_pcs' + i);
    }
}

function deselect_all_units(f) {
    var i = 1;

    var u = document.getElementById('unit_pcs' + i);
    while (u != null) {
        u.value = 0;

        i++;
        u = document.getElementById('unit_pcs' + i);
    }
}

function flashtitle(flastitlemode, count) {
	count++;
	if (flastitlemode == 0) {
		document.title = '                           ';
		setTimeout('flashtitle(1)', count);
	}
	else {
		document.title = origtitle;
		if (count < 6) {
			setTimeout('flashtitle(0)', count);
		}
	}
}

/**
 * Autocomplete a text field as a user types.
 *
 * @param id {string} The ID of the text field against which to autocomplete.
 *
 * @param id2 {string} The ID of the div-element that contains the
 * autocompletion results.
 *
 * @param minlen {int} The minimum length to complete.
 *
 * @param now {bool} Do we complete now?
 *
 * @param leftalign {string} Do we left-align the results?
 *
 * @param params {string} Additional params to pass to the server-side part of
 * the completion process.
 *
 * @param url2 {string} The URL to use for the server-side part instead of
 * 'getcitynames.cgi'.
 *
 * @returns {void}
*/

function hautocomplete (id, id2, minlen, now, leftalign, params, url2) {
	if (gof2 && admin) { show_notice('error', "old autocomplete still used"); }
	logerror_phase = "hautocomplete";

	if (!now) {
		autocompleting+= 1;
		console.log('autocomplete soon, not now', autocompleting);
		setTimeout('hautocomplete("' + id + '","' + id2 + '",' + minlen + ',1,' + leftalign + ',"' + params + '","' + url2 + '");', 300);

        return;
	}

    autocompleting -= 1;
    if (autocompleting > 0) {
        console.log('autocomplete exit, still rolling', autocompleting);
        return;
    }

    var text = document.getElementById(id).value;
    if (text.length < minlen - 1) {
        console.log('autocomplete: too short text', text.length, minlen);
        document.getElementById(id2).style.visibility = 'hidden';
        document.getElementById(id2).style.display = 'none';

        return;
    }

    if (autocomplete_text != text) { /* autocomplete_text is a global variable */
        console.log('autocomplete soon, now typing', autocomplete_text, text, now);
        autocomplete_text = text;
        if (now == 1) {
            setTimeout('hautocomplete("' + id + '","' + id2 + '",' + minlen + ',2,' + leftalign + ',"' + params  + '","' + url2 + '");', 300);
            autocompleting += 1;
        }

        return;
    }

    var url;
    if ( !url2 || url2 == "undefined" ) {
        url = baseurl + "/getcitynames.cgi?value=" + text + params;
    } else {
        url = url2 + text + params;
    }
    console.log('autocomplete request', url);

    var container = document.getElementById(id2);

    var handle_autocomplete = function(transport) {
        if (transport.responseText == 'Server too busy, try again later.') {
            console.log('Server busy');
            return;
        }

        console.log('autocomplete ok', x, leftalign);
        if ( autocomplete_skippos != 2 ) {
            if ( autocomplete_skippos ) {
                container.style.left = 0 + "px";
                container.style.top = -(container.offsetHeight) + "px";
            }
            else {
                var x = findPosX(document.getElementById(id));
                if ( leftalign ) x = x + leftalign;
                var y = findPosY(document.getElementById(id));

                container.style.left = x + "px";
                container.style.top = (y + 24) + "px";
            }
        }
        container.style.visibility = 'visible';
        container.style.display = 'block';
        console.log('autocompleted');
        document.getElementById('connectioninfo').innerHTML = "";
    }


    new Ajax.Updater(
        container,
        url,
        {
            onException: exception_call,
            onComplete: handle_autocomplete,
            onFailure: function(transport) {
                console.log('autocomplete failed');
                document.getElementById('statusinfo').innerHTML = "autocomplete failed";
                document.getElementById('connectioninfo').innerHTML = "Autocomplete failed";
            }
        }
    );
}

function stats_autocomplete_handle (tmp1, tmp2, tmp3, account) {
	document.getElementById('stats_autocomplete').value = account;
	document.getElementById('stats_autocomplete_choices').style.visibility = 'hidden';
}

function findPosX(obj)  {
    var curleft = 0;
    if(obj.offsetParent)
        while(1)         {
          curleft += obj.offsetLeft;
          if(!obj.offsetParent)
            break;
          obj = obj.offsetParent;
        }
    else if(obj.x)
        curleft += obj.x;
    return curleft;
}

function findPosY(obj)  {
    var curtop = 0;
    if(obj.offsetParent)
        while(1)        {
          curtop += obj.offsetTop;
          if(!obj.offsetParent)
            break;
          obj = obj.offsetParent;
        }
    else if(obj.y)
        curtop += obj.y;
    return curtop;
}

function selectcheckboxes (name, max) {
	for (var i = 1; i <= max; i++) {
		var id = name + i;
		document.getElementById(id).checked = 'true';
	}
}

function flashscreen() {
	id = 'flashscreen';
	console.log('flashscreen');
	document.getElementById(id).style.visibility = 'visible';
	document.getElementById(id).innerHTML = " ";
	document.getElementById(id).style.height = '500%';
	new Effect.Appear(id, { duration:0.3, from:0.0, to:0.9 } );
	new Effect.Fade(id, { duration:4, queue: 'end'});
}

function wdhm(min) {
	min = min / 60;

	if (Math.abs(min) > 60) {
		var hour = min / 60;
		if (Math.abs(hour) > 24) {
			var day = hour / 24;
			if (Math.abs(day) > 7) {
				return (Math.round(day / 7 * 10) / 10 + "&nbsp;" + lang.get('w'));
			}
			else {
				return (Math.round(day * 10) / 10 + "&nbsp;" + lang.get('d'));
			}
		}
		else if (Math.abs(hour) < 5) {
			return (Math.round(hour * 10) / 10 + "&nbsp;" + lang.get('h'));
		}
		else {
			return (Math.round(hour + 0.5) + "&nbsp;" + lang.get('h'));
		}
	}
	else {
		return (Math.round(min + 0.5) + "&nbsp;" + lang.get('min'));
	}
}

function wdhm2(min) {
	min = min / 60;

	if (Math.abs(min) > 60) {
		var hour = min / 60;
		if (Math.abs(hour) > 24) {
			var day = hour / 24;
			if (Math.abs(day) > 7) {
				return (Math.round(day / 7 * 10) / 10 + "&nbsp;" + lang.get('w'));
			}
			else {
				return (Math.round(day * 10) / 10 + "&nbsp;" + lang.get('d'));
			}
		}
		else if (Math.abs(hour) < 5) {
			return (Math.round(hour * 10) / 10 + "&nbsp;" + lang.get('h'));
		}
		else {
			return (Math.round(hour + 0.5) + "&nbsp;" + lang.get('h'));
		}
	}
	else {
		return (Math.round(min + 0.5) + "&nbsp;" + lang.get('min'));
	}
}

function windowsize() {
	var viewportwidth;
	var viewportheight;

	// the more standards compliant browsers (mozilla/netscape/opera/IE7) use window.innerWidth and window.innerHeight

	if (typeof window.innerWidth != 'undefined') {
		viewportwidth = window.innerWidth;
		viewportheight = window.innerHeight;

	}
	else if (typeof document.documentElement != 'undefined' && typeof document.documentElement.clientWidth != 'undefined' && document.documentElement.clientWidth != 0) {
		// IE6 in standards compliant mode (i.e. with a valid doctype as the first line in the document)
		viewportwidth = document.documentElement.clientWidth,
		viewportheight = document.documentElement.clientHeight

	}
	else {
		// older versions of IE
		viewportwidth = document.getElementsByTagName('body')[0].clientWidth;
		viewportheight = document.getElementsByTagName('body')[0].clientHeight;
	}
	return [viewportwidth, viewportheight];
}

function fitTextInBox(boxID,maxHeight)	{
	logerror_phase = "fitTextInBox " + boxID + ", maxHeight " + maxHeight;
	console.log('fitTextInBox', boxID, maxHeight);

	if (maxHeight) fitTextInBox_maxHeight = maxHeight;
	else fitTextInBox_maxHeight = 10000;

	var obj = document.getElementById(boxID);
	if (!obj) {
		logerror('object not found', boxID);
		return;
	}
	fitTextInBox_maxWidth = obj.offsetWidth;
	fitTextInBox_currentBox = obj;
	fitTextInBox_currentTextObj = obj.getElementsByTagName('SPAN')[0];
	fitTextInBox_currentTextObj.style.fontSize = '1px';
	fitTextInBox_currentWidth = fitTextInBox_currentTextObj.offsetWidth;
	fitTextInBoxAutoFit(obj);
}

function fitTextInBoxAutoFit(fitTextInBox_currentBox)	{
	var tmpFontSize = fitTextInBox_currentTextObj.style.fontSize.replace('px','')/1;
	fitTextInBox_currentTextObj.style.fontSize = tmpFontSize + 1 + 'px';
	var tmpWidth = fitTextInBox_currentTextObj.offsetWidth;
	var tmpHeight = fitTextInBox_currentTextObj.offsetHeight;
	if(tmpWidth < fitTextInBox_maxWidth && tmpHeight < fitTextInBox_maxHeight && tmpFontSize < 100){
		fitTextInBox_currentWidth = fitTextInBox_currentTextObj.offsetWidth;
		fitTextInBoxAutoFit();
	}
	else{
		fitTextInBox_currentTextObj.style.fontSize = fitTextInBox_currentTextObj.style.fontSize.replace('px','')/1 - 1 + 'px';
	}
}

function notepad() {
	if (document.getElementById('notepad').style.visibility == 'visible') {
		document.getElementById('notepad').style.visibility = 'hidden';
	}
	else {
		document.getElementById('notepad').style.visibility = 'visible';
	}
}

function savenotepad () {

	var location = "";
	var url = baseurl + '/notepad.cgi?text=' + encodeURIComponent(document.getElementById('notepadcontent').value);

	new Ajax.Request(url, {
		method: 'get',
		onException: exception_call,
		onSuccess: function(transport) {
			document.getElementById('notepad').style.visibility = 'hidden';
		},

		onFailure: function(transport) {
			document.getElementById('connectioninfo').innerHTML = "Connection error";
			alert('Connection error');
		}
	});
}

function iteminfo(item) {
	openlayer('', 'iteminfo' + item, 1, 400, '', '', "<img src=\"/pics/gof2/item_" + item + ".png\" class=\"plus_item\" style=\"display: inline; float: right; margin-right: 12px;\" />" + lang.get(item + "_info"), 0, 0, mouseX, mouseY);
}

var all_layers = new Hash;
function openlayer (url, id, noupdate, width, action, actionparam, html, nobasiclayer, hidden, posX, posY, skipfocus, not_draggable) {
	/* id: use $form{'gof2_layer'} to get data to current layer */
	logerror_phase = "openlayer " + id + " url: " + url + " '" + html + "'";

	console.log('openlayer', url, id, noupdate, width, action, actionparam);
	
/*
	if (!document.getElementById('player')) {
		document.location = url;
		return;
	}
*/

	if ( !id ) {
		console.log('missing id, got parentNode:', id);
		return;
	}

	if( gof2 && ( action == undefined || action === undefined || action == '') ) {
		var regex = new RegExp('city\\d?\\.cgi(.*view=(\\d+).*)');
		var match = regex.exec(url);
		if (match != null) {
			action      = 'city';
			width       = 750;
			actionparam = match[2];
			url         = 'city3.cgi' + match[1];
		}
	}

	if ( !width ) width = 700;
	var initlayer = 0;
	var container;

	var update_pos = 1;
	var frame = document.getElementById(id + "_frame");
	var container = document.getElementById(id);
	
	if ( (!frame && container) || (frame && !container)) {
		logerror('Bug! layer id mismatch related to "' + id + '", container "' + container + '", frame "' + frame + '", force remove both before opening it again', 'warning');
		hidelayer(id, 1);

		frame = document.getElementById(id + "_frame");
		container = document.getElementById(id);
		if ( frame || container) {
			logerror('Bug! layer still found while tried to clean it: ' + id + ', container ' + container + ', frame ' + frame + ', skip opening layer', 'error');
			show_notice('error', lang.get('err_unknown'));
			return;
		}
	}

	if (frame && container) {
		console.log(id, 'layer open already');
		
		if (hidden != 1) {
			if (frame.style.visibility != "hidden") { update_pos = 0; }
			frame.style.visibility = "visible";
			frame.style.display = "block";
		}

	}
	else {
		console.log('open layer', url, id, width);

		container = createbasiclayer(id, width, nobasiclayer, hidden);

		initlayer = 1;
		console.log('open layer done', url, id);
	}

	if (update_pos) {
		logerror_phase = "openlayer " + id + " update position";
		var top = 0;
		if ( document.body.parentElement    ) top = document.body.parentElement.scrollTop;
		if ( window.pageYOffset             ) top = window.pageYOffset;
		if ( document.body.scrollTop        ) top = document.body.scrollTop;

		var winsize = windowsize();
		var left = Math.floor(winsize[0] / 2 - width / 2);

		if ( posX ) left = posX;
		if ( posY ) top = posY;

		if ( left < 240 ) left = 240;
		if ( top < 40   ) top = 40;

		document.getElementById(id + "_frame").style.width = width + "px";
		document.getElementById(id + "_frame").style.left = left + "px";

		document.getElementById(id + '_frame').style.top = top + "px";

	}
	
	if (action == "city_build" || action == "city_train" || action == "city")  {
		cityaction('&poll=1', 1, actionparam);
	}

	if ( noupdate == 1 || !url ) {
		console.log('no layer update');
		logerror_phase = "openlayer " + id + " updating html '" + html + "'";
		if ( html ) {
			container.innerHTML = html;
			if (document.getElementById('title_' + id)) {
				if (! not_draggable) { new Draggable(id + "_frame", { handle: "title_" + id, zindex: 1000000 }); }
			} else if (document.getElementById(id + "_frame")) {
				if (! not_draggable) { new Draggable(id + "_frame", { zindex: 1000000 }); }
			}
		}
	}
	else {
		logerror_phase = "openlayer " + id + " url: " + url + " attempt to do request";
		if (not_draggable) {
			console.warn('not_draggable not implemented for normal layers');
		}
		
		url += "&gof2=none";
		url += "&gof2_layer=" + id;
		if ( noupdate == 2 ) url += "&cache=1";

		console.log('update layer', id, url);
		var success = 0;

        var after_openlayer_actions = function() {
            console.log('layer updated', id, url, action, actionparam);
			logerror_phase = "openlayer " + id + " url: " + url + " after_openlayer_acitions";

            if (action == "city_build" || action == "city_train" || action == "city" ) {
                cityinit(actionparam, 0, action);
            }

            post_layer_actions( id );
        };
		document.body.style.cursor = "wait";

		new Ajax.Updater(
            container,
            url,
            {
                evalScripts: true,
                onException: exception_call,
                onComplete: after_openlayer_actions,
                onFailure: function(transport) {
                    console.log("can't update layer", id, url);
                    container.innerHTML = lang.get('err_connection') + "<br />" + transport.responseText;
                }
            }
        );
	}

	if (!skipfocus) { basiclayer_focus(id); }
	
	logerror_phase = "openlayer " + id + " url: " + url + " cleaning layers";
	
	var lowest = 1000000;
	var lowest_id;
	var hidden_layers = 0;
	all_layers.each(function(pair) {
		if (document.getElementById(pair.key + '_frame')) {
			if (pair.key != "world" && document.getElementById(pair.key + '_frame').style.visibility == "hidden") {
				hidden_layers++;
				if (all_layers.get(pair.key) < lowest) { lowest = all_layers.get(pair.key); lowest_id = pair.key; }				
			}
		} else {
			console.warn(pair.key, 'do not exist, remove it from hash');
			all_layers.unset(pair.key);
		}
	});

	if (lowest_id != id) {
		if ( (registry.get('graphicslevel') == -1 && hidden_layers > 10) || hidden_layers > 25) {
			console.log('Hidden layers', hidden_layers, ', close', lowest_id, ' (z: ', lowest, ')');
			removeChildSafe(document.getElementById(lowest_id + '_frame'));
			all_layers.unset(lowest_id);
			document.getElementById('statusinfo').innerHTML = "removed layer " + lowest_id + ", lowest " + lowest;
		}
	}
}

function createbasiclayer(id, width, nobasiclayer, hidden) {
	if (width == '' || width == undefined || width === undefined) { width = 700; }
	var minwidth = width - 100;

	var frame = document.createElement('div');
	document.getElementById('content').appendChild(frame);
	frame.setAttribute('class', 'basiclayerframe');
	frame.setAttribute('className', 'basiclayerframe');
	frame.setAttribute('id', id + '_frame');
	frame.setAttribute('width', width + 'px');
	all_layers.set(id, 1);
	if (hidden == 1) {
		frame.style.visibility = "hidden";
		frame.style.display = "none";
	}
	frame.onmousedown = new Function('e', "basiclayer_focus('" + id + "');");

	if (nobasiclayer == 1) {
		frame.innerHTML = "<div id=\"" + id + "\">";
	} else {
		var html = "<table class=\"basiclayer\">";
		html+= "<tr><td class=\"basiclayer1\"></td><td class=\"basiclayer2\"></td><td class=\"basiclayer3\"></td></tr>";
		html+= "<tr><td class=\"basiclayer4\"></td><td class=\"basiclayer5\" style=\"min-height: 150px; min-width: " + minwidth + "px\">";

		if (registry.get('graphicslevel') == "0") {
			html+= '<div style="background: transparent url(/pics/gof2/frame1.png); position: absolute; top: 2px; width: 87px; height: 46px; left: 0px;"> </div>';
			html+= '<div style="background: transparent url(/pics/gof2/frame3.png); position: absolute; top: 2px; width: 87px; height: 46px; right: 2px;"> </div>';
			html+= '<div style="background: transparent url(/pics/gof2/frame7.png); position: absolute; bottom: 7px; width: 87px; height: 46px; left: 0px;"> </div>';
			html+= '<div style="background: transparent url(/pics/gof2/frame9.png); position: absolute; bottom: 7px; width: 87px; height: 46px; right: 2px;"> </div>';
		}

		html+= '<img style="z-index: 2; position: absolute; top: 23px; right: 23px;" onclick="hidelayer(\'' + id + '\')" class="link" src="/pics/gof2/close.png" />';

		html+= "<div id=\"" + id + "\">";
		html+= "<div style=\"text-align: center;\"><img src=\"/pics/gof2/waiting.gif\" class=\"waiting\" /></div>";
		html+= "</div></td><td class=\"basiclayer6\"></td></tr>";
		html+= "<tr><td class=\"basiclayer7\"></td><td class=\"basiclayer8\"></td><td class=\"basiclayer9\"></td></tr>";
		html+= "</table>";
		frame.innerHTML = html;
	}

	var container = document.getElementById(id);
	basiclayers.push(id);
	return container;
}

function show_notice (type, text, autohide) {
	if (!gof2) {
		alert(text);
		return;
	}
	var id = Math.round(1000000*Math.random());
	var html = "<table class=\"bar\">";
	html+= "<tr><td class=\"bar1\"></td><td class=\"bar2\"></td><td class=\"bar3\"></td></tr>";
	html+= "<tr><td class=\"bar4\"></td><td class=\"bar5" + type + "\">";

	if (type == 'notice') {
		html+= '<div style="background: transparent url(pics/gof2/bar4notice.png); position: absolute; top: 2px; width: 18px; height: 30px; left: 4px;"> </div>';
		html+= '<div style="background: transparent url(pics/gof2/bar6notice.png); position: absolute; top: 2px; width: 18px; height: 30px; right: 2px;"> </div>';
	} else {
		html+= '<div style="background: transparent url(pics/gof2/bar1error.png); position: absolute; top: -10px; width: 131px; height: 47px; left: -5px;"> </div>';
		html+= '<div style="background: transparent url(pics/gof2/bar3error.png); position: absolute; top: -10px; width: 131px; height: 47px; right: -10px;"> </div>';
	}

	html+= text;
	html+= "<div style=\"text-align: center;margin-top:15px;\"><span onclick=\"hidelayer('notice" + id + "')\" class=\"button\">" + lang.get('close') + "</span></div>";
	html+= "</div></td><td class=\"bar6\"></td></tr>";
	html+= "<tr><td class=\"bar7\"></td><td class=\"bar8\"></td><td class=\"bar9\"></td></tr>";
	html+= "</table>";

	var not_draggable = 0;
	if (registry.get('graphicslevel') == -1) { not_draggable = 1; }
	openlayer('', 'notice' + id, '', 400, '', '', html, 1, 0, undefined, mouseY - 200, 0, not_draggable);
	if (autohide) {
		console.log('will autohide', 'notice' + id);
		setTimeout("autohide('notice" + id + "');", 3000);
	} else if (type == 'notice') {
		console.log('will autohide aften 60s', 'notice' + id);
		setTimeout("autohide('notice" + id + "');", 60000);
	}
}

function autohide(id) {
	console.log('autohide if exists', id);
	if (document.getElementById(id + "_frame")) {
		console.log('autohide', id);
		Effect.Fade(id + "_frame", { duration: 1 });
		setTimeout("hidelayer('" + id + "');", 10000);
	}
}

function basiclayer_focus (focus_id) {
	console.log('amount of basiclayers', basiclayers.length, 'set focus to', focus_id);
	zcounter++;
	all_layers.set(focus_id, zcounter);
	document.getElementById(focus_id + '_frame').style.zIndex = zcounter;
	document.getElementById(focus_id + '_frame').style.filter = '';
}

function hidelayer (id, check_container) {
	logerror_phase = "hidelayer " + id;
		
	if (document.getElementById(id + '_frame')) {
		console.log('hidelayer', id);
		var regex = new RegExp("notice|error");
		var match = regex.exec(id);
		if (match != null || id == 'profile_step7') {
			removeChildSafe(document.getElementById(id + '_frame'));
			all_layers.unset(id);
			check_container = 1;
		} else {
			document.getElementById(id + '_frame').style.visibility = "hidden";
			document.getElementById(id + '_frame').style.display = "none";
		}
	} else {
		console.log('layer', id, 'do not exist, can not hide');
	}
	
	if (check_container && document.getElementById(id)) {
		logerror('removed element "' + id + '_frame" but inner element "' + id + '" still exists, remove it brutally anyway', 'warning');
		removeChildSafe(document.getElementById(id));	
	}
}

function removeChildSafe(el) {

    while(el.childNodes.length > 0) {
        removeChildSafe(el.childNodes[el.childNodes.length-1]);
    }
	var att = el.attributes;
    if (att && admin) {
		document.getElementById('statusinfo').innerHTML = "admin only: removing " + el;
        for (i = 0; i < att.length; i += 1) {
            var n = att[i].name;
			console.log(el, typeof(el[n]));
            if (typeof el[n] === 'function') {
				logerror_phase = "removeChildSafe " + n + ", " + el[n];
				if (/Firefox[\/\s](\d+\.\d+)/.test(navigator.userAgent)){
					el[n] = function (el, n) {
						logerror('Removed event triggered for ' + el + ': ' + n, "warning");
					}
				} else {
					el[n] = null;
				}
            }
        }
    }
	logerror_phase = "removeChildSafe attempt to remove " + el;
    if (el.parentNode) { el.parentNode.removeChild(el); }
}

function minimizelayer (id) {
	if (document.getElementById(id + '_frame')) {
		document.getElementById(id + '_frame').style.overflowY = "hidden";
		document.getElementById(id + '_frame').style.width = "160px";
		document.getElementById(id + '_frame').style.height = "60px";
	}
}

function openvillages () {
	console.log('cities', registry.get('cities'));
	if (registry.get('cities')) {
		var cities = registry.get('cities').split(/,/);
		if (cities.length == 2) {
			openlayer('city3.cgi?view=' + cities[0], "city" + cities[0], 0, 750, "city", cities[0]);
			return;
		}
	}
	openlayer('city3.cgi?', "city", 0, 950);
}

function is_form ( elem ) {
    return ( elem.tagName.toUpperCase() == "FORM" );
}

function get_form_object ( reference ) {
    if ( !reference ) return reference;
    
    reference = $(reference); // make sure it's always turned into a prototype element, no matter what
    
    var ancestor_list = reference.ancestors();
    ancestor_list.unshift(reference);

    var form_object = ancestor_list.find( is_form );

    return form_object;
}

function postform ( form, layer ) {
	console.log('attempt to post form', form, 'in', layer);
	logerror_phase = "postform " + form + ", " + layer;
	
	if (!form) {
		logerror('Bug! Missing form parameter, ' + layer, 'error');
		show_notice('error', lang.get('err_unknown'));
		return;
	}

    form = get_form_object( form );

	if ( !form ) {
		console.log('missing id in form submit');
        return;
	}

	if ( !document.getElementById(layer) ) createbasiclayer(layer);

    var target_container = $(layer);

    var form_data = form.serialize(true);
    form_data.gof2 = 'none';
    form_data.gof2_layer = layer;

    var url = form.action;

    if ( !Object.isString(url) ) {
		console.error('url sent to Ajax.Updater is not a string, check that you are not overwriting the action element of the form');
        return;
    }

	document.body.style.cursor = "wait";
    new Ajax.Updater(
        target_container,
        url,
        {
            evalScripts: true,
            parameters: form_data,
            onException: exception_call,
            onComplete: function() {
				post_layer_actions( layer );
			},
            onFailure: function() { console.log('form post failed'); }
        }
    );

	console.log('form post completed');
}

function post_layer_actions( layer ) {
    console.log('ajax successful, update content');
	for (var i = 1; i < 10; i++) {
		if (document.getElementById(layer + '_error' + i)) show_notice('error', document.getElementById(layer + '_error' + i).innerHTML);
		if (document.getElementById(layer + '_notice' + i)) show_notice('notice', document.getElementById(layer + '_notice' + i).innerHTML);
		if (document.getElementById(layer + '_autohide_notice' + i)) show_notice('notice', document.getElementById(layer + '_autohide_notice' + i).innerHTML, 1);
	}
	if (document.getElementById('title_' + layer)) {
		new Draggable(layer + "_frame", { handle: "title_" + layer, zindex: 1000000 });
	} else if (document.getElementById(layer + "_frame")) {
		new Draggable(layer + "_frame", { zindex: 1000000 });
		console.log(layer, 'frame width', document.getElementById(layer + "_frame").style.width, ", content width", document.getElementById(layer).style.width);
	}
	document.body.style.cursor = "default";
    return true;
}

function enterkeypost(e, form, layer, normalsubmit) {
    var key;

    if (window.event)
        key = window.event.keyCode;     //IE
    else
        key = e.which;     //firefox

    if (key == 13) {
		if (normalsubmit == 1) {
			document.getElementById(layer).submit();
		} else {
			postform( form, layer );
		}
        return false;
    } else {
		return true;
	}
}

/** Upload a file using AJAX **/
AIM = {

	frame : function(c) {
        // creates an iframe, assigns it the onComplete function to execute when done loading
        // and returns the id of the iframe

		var n = 'f' + Math.floor(Math.random() * 99999);
		var d = document.createElement('DIV');
		d.innerHTML = '<iframe style="display:none" src="about:blank" id="'+n+'" name="'+n+'" onload="AIM.loaded(\''+n+'\')"></iframe>';
		document.body.appendChild(d);

		var i = document.getElementById(n);
		if (c && typeof(c.onComplete) == 'function') {
			i.onComplete = c.onComplete;
		}

		return n;
	},

	form : function(f, name) {
        // sets the given form's target to be the element with the id that name points out.
        // this affects where the output of the form 'f' goes to.
		f.setAttribute('target', name);
	},

	submit : function(f, c) {
        // creates an iframe and sets the target of the form 'f' to be that iframe.
        // this means when 'f' submits, its output is directed to the iframe.
        // then it executes the onStart function if there is one and returns its return value,
        // allowing the onStart function to cancel the submit by returning false.
        // if there's no onStart it simple returns true, so the form submits.
        
        // !!!!! THIS IS NECESSARY BECAUSE FILES CANNOT BE UPLOADED THROUGH XHR !!!!!
        // !!!!! DO NOT ATTEMPT TO USE PLAIN AJAX FOR THIS !!!!!
        
		AIM.form(f, AIM.frame(c));
		if (c && typeof(c.onStart) == 'function') {
			return c.onStart();
		} else {
			return true;
		}
	},

	loaded : function(id) {
        // i have no clue what this does
        
		var i = document.getElementById(id);
		if (i.contentDocument) {
			var d = i.contentDocument;
		} else if (i.contentWindow) {
			var d = i.contentWindow.document;
		} else {
			var d = window.frames[id].document;
		}
		if (d.location.href == "about:blank") {
			return;
		}

		if (typeof(i.onComplete) == 'function') {
			i.onComplete(d.body.innerHTML);
		}
	}
 }

function photoStart() {
	logerror_phase = "photoStart";
	playsound("photo");
	document.getElementById('photo').src = "<img src=\"pics/gof2/waiting.gif\" class=\"waiting\" />";
	return true;
}
function photoComplete(response) {
	document.getElementById('photo').src = document.getElementById('photo').src + '?rand=' + Math.round(100000*Math.random());
	var layer = 'profile_photo';
	if (document.getElementById(layer) && response) {
		document.getElementById(layer).innerHTML = response;
		post_layer_actions( layer );
	} else {
		console.log('layer profile_photo do not found or response is empty');
	}
}
function flagStart() {
	logerror_phase = "flagStart";
	playsound("flag");
	document.getElementById('flag').src = "<img src=\"pics/gof2/waiting.gif\" class=\"waiting\" />";
	return true;
}
function flagComplete(response) {
	var layer = 'flag';
	if (document.getElementById(layer) && response) {
		document.getElementById(layer).innerHTML = response;
		post_layer_actions( layer );
	} else {
		console.log('layer flag do not found or response is empty');
	}
}

function support_changeshowinfo() {
	for (var i = 0; i < 20; i++) {
		if (document.getElementById('support_showinfo' + i)) {
			document.getElementById('support_showinfo' + i).style.visibility = 'hidden';
			document.getElementById('support_showinfo' + i).style.display = 'none';
		}
	}
	var id = document.getElementById('support_showinfo').value;
	if (document.getElementById('support_showinfo'+ id)) {
		document.getElementById('support_showinfo' + id).style.visibility = 'visible';
		document.getElementById('support_showinfo' + id).style.display = 'block';
	}
}

function updategraphstat(stat, account) {
	var image_id  = "graph_stat_picture_"+account;
	var legend_id = "graph_stat_legend_"+account;
    if(document.getElementById(image_id)) {
        document.getElementById(image_id).src = "../graphstatsengine.cgi?stat="+stat+"&account="+account;
    }
    if(document.getElementById(legend_id)) {
		document.getElementById(legend_id).innerHTML = '';
		lang_get_string("www-gof/profile/view", "position_"+stat, legend_id);
    }
}

function lang_get_string(module,str,id) {
    new Ajax.Request("/api/lang.cgi?module=" + encodeURIComponent(module) + "&string=" + encodeURIComponent(str),
    {
		onSuccess: function(oReq, oJSN) {
			var data = oReq.responseText.evalJSON();

			if (typeof id == "string") {
				$(id).innerHTML += data.string;
			}
			else if  (typeof id == "function") {
				id(data.string);
			}
		}
    });
}

function substitute() {
    var args=substitute.arguments;
    var Base=args[0];

    if ( Base == '' || Base == undefined || Base === undefined || Base == 'undefined' ) {
        return '';
    }

    var Seek,Len,ix1,ix2,ix3;
    for (ix1=1; ix1<args.length; ix1++) {
        ix2=ix1-1;
        Seek='[['+ix2+']]';
        if ((ix3=Base.indexOf(Seek)) > -1) {
            Len=Seek.length;
            Base=Base.substring(0,ix3)+args[ix1]+Base.substring(ix3+Len);
        }
    }
    return Base;
}

function simple_reloadmap ( account ) {
    document.getElementById('autocomplete').value = account;
    document.getElementById('autocomplete_choices').style.visibility = 'hidden';

    return;
}

function attack_reloadmap (location, cityid, name) {
    document.getElementById('target').value = cityid;
    document.getElementById('target_own').selectedIndex = -1;
    document.getElementById('autocomplete').value = name;
    attackplan(0, 1);

    return;
}

function citychoices (location, cityid, name, account) {
    document.getElementById('village_name').value = name;
    document.getElementById('village').value = cityid;
    document.getElementById('autocomplete_choices2').style.visibility = 'hidden';
}

function checkReason(val, id) {
    if(val == 3) {
        document.getElementById(id).style.visibility="visible";
        document.getElementById(id).style.display="block";
    }
    else {
        document.getElementById(id).style.visibility="hidden";
        document.getElementById(id).style.display="none";
    }
}

function getCookie(c_name) {
	if (document.cookie.length>0) {
		c_start=document.cookie.indexOf(c_name + "=");
		if (c_start!=-1) {
			c_start=c_start + c_name.length+1;
			c_end=document.cookie.indexOf(";",c_start);
			if (c_end==-1) c_end=document.cookie.length;
			return unescape(document.cookie.substring(c_start,c_end));
		}
	}
	return "";
}

function sortby(i,dir) {
	return function(a,b){a = a[i];b = b[i];return a.toLowerCase() == b.toLowerCase() ? 0 : (a.toLowerCase() < b.toLowerCase() ? -1*dir : dir)}
}

function fb_graph_authorize( client_id, callback ) {
	console.log('fb_graph_authorize called ' + client_id + ' ' + callback);

	if( ! callback )
		callback = baseurl + '/fb_callback.cgi';

	document.location = 'https://graph.facebook.com/oauth/authorize'
		+ '?client_id=' + client_id
		+ '&display=page'
		+ '&redirect_uri=' + callback;
}

function fb_wall_post(offset, comment, afterPermReq) {
	console.log('fb_wall_post called ' + offset + ', ' + comment);

	if( comment )
		comment = Base64.encode( comment.substring( 0, 140 ) );

	// attempt posting to wall
	new Ajax.Request( '/fb_action.cgi?wall_post=' + offset + '&wall_comment=' + comment,
	{
		onException: exception_call,
		onSuccess: function(transport) {
			console.log('fb_wall_post ok', offset);
			fb_wall_post_handle_response(transport, offset, comment, afterPermReq);
		},
		onFailure: function(transport) {
			console.log('fb_wall_post failed', offset);
			show_notice('error', lang.get('err_connection'));
			
		}
	});
}

function fb_wall_post_handle_response(transport, offset, comment, afterPermReq) {
	console.log('fb_wall_post_handle_response called ' + offset + ', ' + comment);

	try {
		var response = transport.responseText.evalJSON();

		if( response.need_reauth ) {
			// need to do full auth request
			console.log('making reauth request');
			var url = 'https://graph.facebook.com/oauth/authorize'
					+ '?client_id=' + response.client_id
					+ '&display=popup'
					+ '&redirect_uri=' + baseurl + '/' + response.callback;
			if( response.permission )
				url += '&scope=' + response.permission;

			window.open(
				url,
				'fb_popup',
				'width=600,height=280,scrollbars=0,resizable=yes,toolbar=no,directories=no,status=no,location=no,menubar=no,copyhistory=no'
			);
		}
		else if( response.error ) {
			if( afterPermReq ) {
				// probably permission was not granted
				logerror( "cannot post history message to the wall: '" + transport.responseText + "'", "error" );
				lang_get_string( "www-gof/profile/fb_action", "err_wall_post", function(str) {
					show_notice( 'error', str );
				} );
			}
			else {
				// check if we need to request email permission at same time
				var permission = 'publish_stream';
				if( registry.get('fb_need_email_permission') ) {
					permission += ',email';
					registry.set('fb_need_email_permission', 0);
				}

				console.log('posting to wall and requesting permission ' + permission);
				// window must set close handler to 'fb_wall_post(offset, comment, 1)'
				window.open(
					baseurl + '/fb_action.cgi?permission_request=' + permission + '&wall_post=' + offset + '&wall_comment=' + comment,
					'fb_popup',
					'width=600,height=280,scrollbars=0,resizable=yes,toolbar=no,directories=no,status=no,location=no,menubar=no,copyhistory=no'
				);
			}
		}
		else {
			// show success
			fb_wall_post_success();

			// separate request for email permission
			if( ! registry.get('email') && registry.get('fb_need_email_permission') ) {
				console.log('requesting email permission');
				fb_request_email_permission();
			}
		}
	}
	catch(e) {
		logerror('fb_wall_post_handle_response error ' + e, "error");
	}
}

function fb_request_email_permission() {
	console.log('fb_request_email_permission called');

	// window must set close handler to 'fb_wall_post(offset, comment, 1)'
	window.open(
		baseurl + '/fb_action.cgi?permission_request=email',
		'fb_popup',
		'width=600,height=280,scrollbars=0,resizable=yes,toolbar=no,directories=no,status=no,location=no,menubar=no,copyhistory=no'
	);
}

function fb_wall_post_error() {
	console.log('fb_wall_post_error called');

	lang_get_string("www-gof/fb_action", "err_user_denied", function(str) {
		show_notice( 'error', str );
	} );
}

function fb_wall_post_success() {
	console.log('fb_wall_post_success called');

	lang_get_string("www-gof/fb_action", "wall_post", function(str) {
		show_notice( 'notice', str );
	} );
}

function fb_email_request_success() {
	console.log('fb_email_request_success called');
}

function fb_wall_post_prompt(offset) {
	console.log('fb_wall_post_prompt called ' + offset);

	openlayer('/gethistory.cgi?wall_post_form=' + offset, 'facebook_wall_post', '', 400);
}

function fb_wall_post_prompt_submit(offset, comment) {
	console.log('fb_wall_post_prompt_submit called ' + offset + ', ' + comment);

	fb_wall_post(offset, comment);
	hidelayer('facebook_wall_post');
}

function fb_invite_users() {
	console.log('fb_invite_users called');

	window.open(
		baseurl + '/fb_action.cgi?invite_users=1',
		'fb_popup',
		'width=780,height=575,scrollbars=0,resizable=yes,toolbar=no,directories=no,status=no,location=no,menubar=no,copyhistory=no'
	);
}

function fb_invite_done(num) {
	console.log('fb_invite_done called ' + num);

	var string = num > 0 ? "friends_invited" : "friends_not_invited";

	lang_get_string("www-gof/fb_action", string, function(str) {
		show_notice( 'notice', str );
	} );
}

var Base64 = {
	// private property
	_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
 
	// public method for encoding
	encode : function (input) {
		var output = "";
		var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
		var i = 0;
 
		input = Base64._utf8_encode(input);
 
		while (i < input.length) {
			chr1 = input.charCodeAt(i++);
			chr2 = input.charCodeAt(i++);
			chr3 = input.charCodeAt(i++);
 
			enc1 = chr1 >> 2;
			enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
			enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
			enc4 = chr3 & 63;
 
			if (isNaN(chr2)) {
				enc3 = enc4 = 64;
			} else if (isNaN(chr3)) {
				enc4 = 64;
			}
 
			output = output +
			this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
			this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
		}
 
		return output;
	},
 
	// private method for UTF-8 encoding
	_utf8_encode : function (string) {
		string = string.replace(/\r\n/g,"\n");
		var utftext = "";
 
		for (var n = 0; n < string.length; n++) {
			var c = string.charCodeAt(n);
 
			if (c < 128) {
				utftext += String.fromCharCode(c);
			}
			else if((c > 127) && (c < 2048)) {
				utftext += String.fromCharCode((c >> 6) | 192);
				utftext += String.fromCharCode((c & 63) | 128);
			}
			else {
				utftext += String.fromCharCode((c >> 12) | 224);
				utftext += String.fromCharCode(((c >> 6) & 63) | 128);
				utftext += String.fromCharCode((c & 63) | 128);
			}
		}
 
		return utftext;
	},
}
