/* require file jslib/octal.js
/* require var ajaxbase #normally provided by the cgi program. */

// Radius of a property such that others are considered coincident with it.
var coincidentRadius = 10;

// Timeout in ms between checks to see if the map border size has stabilised.
var borderStabiliseTimeout = 100;


var properties = new Array();
var clumps;
var suburbs;
var cities;
var regions;
var nations;

var markers;
var uiStrings;

var curImgIndex = 0;


/** Get the count of a number of things as a string, with suffix
 *
 * e.g. 1 property, 2 properties, etc...
 *
 * \param n Number of things.
 * \param stringprefix Prefix of the name of the ui string to use
 *
 * There should be a -single and -plural version of the UI string starting
 * with stringprefix to select.
 */
function itemCount(n, stringprefix)
{
	return n + " " + uiStrings[stringprefix + (n == 1 ? "-single" : "-plural")];
}


function nodetext(xmlnode)
{
	if (!xmlnode || !xmlnode.hasChildNodes || !xmlnode.childNodes) {
		return "";
	}

	var result = "";
	for (var i = 0; i < xmlnode.childNodes.length; ++i) {
		var subnode = xmlnode.childNodes.item(i);
		if (subnode.nodeName == '#text') {
			result += subnode.nodeValue;
		}
		else {
			result += nodetext(subnode);
		}
	}

	return result;
}


/** \brief Property constructor
 */
function Property(propnode, icon)
{
	this.m_criteria = null;
	this.images = new Array();

	for (var i = 0; i < propnode.childNodes.length; ++i) {
		var child = propnode.childNodes.item(i);

		switch (child.tagName) {
		case 'id':
		case 'title':
		case 'latitude':
		case 'longitude':
		case 'city':
		case 'nation':
		case 'suburb':
		case 'region':
		case 'price_orig':
		case 'price_native':
			this[child.tagName] = nodetext(child);
			break;

		case 'summary':
			this.m_summary = nodetext(child);
			break;

		case 'img':
			this.images.push(nodetext(child));
			break;

		case 'url':
			this.url = nodetext(child);
			this.url_target = child.getAttribute("target");
			break;

		case 'criteria':
			if (!this.m_criteria) {
				this.m_criteria = new Object();
			}
			for (var j = 0; j < child.childNodes.length; ++j) {
				var crit = child.childNodes.item(j);
				var name = crit.getElementsByTagName("name").item(0);
				var val = crit.getElementsByTagName("value").item(0);
				if (name && val) {
					this.m_criteria[nodetext(name)] = nodetext(val);
				}
			}
			break;
		}
	}

	if (!this.suburb) {
		this.suburb = this.city;
	}
	if (!this.region) {
		this.region = this.city;
	}
	this.nation = this.nation.toLowerCase();

	try {
		this.pos = new GLatLng(this.latitude, this.longitude);
	}
	catch (err) {
		this.pos = null;
	}
	this.icon = icon;
}

Property.prototype.alink = function(desc)
{
	var result = "";

	result += "<a href='" + this.url + "'"
		+ (this.url_target ? " target='" + this.url_target + "'" : "")
		+ ">" + desc + "</a>";

	return result;
}

Property.prototype.criteria = function()
{
	if (!this.m_criteria) {
		this.m_criteria = new Object();

		var request = octalXmlHttpSync(ajaxbase + "/propcrit/" + this.id + ".xml?w=150");
		if (request.responseXML) {
			var nodes = request.responseXML.getElementsByTagName("criterion");
			if (!nodes.length) {
				alert('No criteria for ' + this.id);
			}
			for (var i = 0; i < nodes.length; ++i) {
				var crit = nodes.item(i);
				var name = crit.getElementsByTagName("name").item(0);
				var val = crit.getElementsByTagName("value").item(0);
				if (name && val) {
					this.m_criteria[nodetext(name)] = nodetext(val);
				}
			}

			var summary = request.responseXML.getElementsByTagName("summary");
			if (summary.length == 1) {
				this.m_summary = nodetext(summary.item(0));
			}

			nodes = request.responseXML.getElementsByTagName('img');
			for (var i = 0; i < nodes.length; ++i) {
				var img = nodes.item(i);
				this.images.push(nodetext(img));
			}
		}
		else {
			alert('xmlhttprequest for criteria ' + this.id + ' failed');
		}
	}

	return this.m_criteria;
}

Property.prototype.summary = function()
{
	this.criteria();
	return this.m_summary;
}

function imgSet(owner, propid, diff)
{
	var prop = null;
	for (var i = 0; i < properties.length; ++i) {
		if (properties[i].id == propid) {
			prop = properties[i];
			break;
		}
	}
	if (!prop) {
		alert("Prop not found!");
		return false;
	}

	curImgIndex += diff;
	while (curImgIndex < 0) {
		// Why doesn't % work properly for -ve numbers?
		curImgIndex += prop.images.length;
	}
	curImgIndex %= prop.images.length;

	var imgNode = owner.parentNode.getElementsByTagName("img")[0];
	imgNode.setAttribute("src", prop.images[curImgIndex]);

	return false;
}

/** \brief Get short HTML info for the property.
 */
Property.prototype.htmlShortInfo = function()
{
	function vertrow(hdr, val) {
		return "<tr><th>" + hdr + "</th><td>" + val + "</td></tr>\n";
	}

	var crits = this.criteria();

	var result = "";

	result += '<div class="popup searchpopup"'
		+ ' style="width: 400px; height: 180px; overflow: auto;"'
		+ '>';

	result += '<h2 style="margin: 3px 0;">'
		+ this.alink(this.title)
		+ '</h2>\n';

	if (this.images.length > 0) {
		result += "<div class='propimg'>";
		result += "<img src='" + this.images[0] + "' />";
		if (this.images.length > 1) {
			result += "<input type='image'"
				+ " src='/i/left.png'"
				+ " onClick='return imgSet(this, " + this.id + ", -1);'"
				+ " />";
			result += "<input type='image'"
				+ " src='/i/right.png'"
				+ " onClick='return imgSet(this, " + this.id + ", +1);'"
				+ " />";
		}
		result += "</div>\n";
	}

	result += "<p class='proploc'>" + this.city;
	if (this.region != this.city) {
		result += ", " + this.region;
	}
	result += "</p>\n";

	result += "<div class='propattr'>";
	result += bordertable_start();
	result += "<table class='vertical'>\n";
	result += vertrow(uiStrings['price'], this.price_native
			? this.price_orig + " (" + this.price_native + ")"
			: this.price_orig);
	for (var c in crits) {
		if (crits[c]) {
			result += vertrow(c, crits[c]);
		}
	}
	result += "</table>\n";
	result += bordertable_end();
	result += "</div>\n";

	result += "<div class='propsum'>";
	/*
	if (this.summary()) {
		result += bordertable_start();
		result += this.summary();
		result += bordertable_end();
	}
	*/
	result += "</div>\n";

	result += "<p class='view'>" + this.alink(uiStrings['view']) + "</p>\n";

	result += "</div>\n";

	return result;
}

Property.prototype.onClick = function(map)
{
	if (this.url_target) {
		window.open(this.url, this.url_target);
	}
	else {
		window.location = this.url;
	}
}


function Clump(property, icon)
{
	this.properties = new Array();
	this.properties.push(property);
	this.pos = property.pos;
	this.icon = icon ? icon : property.icon;
}

function clumpClick(id) {
	for (var i = 0; i < properties.length; ++i) {
		var p = properties[i];
		if (p.id == id) {
			map.openInfoWindowHtml(p.pos, p.htmlShortInfo());
			return false;
		}
	}

	return true;
}

Clump.prototype.isCoincident = function(pos)
{
	return this.pos.distanceFrom(pos) < coincidentRadius;
}

Clump.prototype.push = function(property)
{
	this.properties.push(property);
}

Clump.prototype.htmlShortInfo = function()
{
	if (this.properties.length == 1) {
		return this.properties[0].htmlShortInfo();
	}

	var result = "";
	result += '<div class="popup searchlist"'
		+ ' style="width: 400px;"'
		+ '>\n';

	for (var i = 0; i < this.properties.length; ++i) {
		var p = this.properties[i];
		result += '<h2 style="margin: 3px 0;">';
		result += '<a href="' + p.url + '"';
		if (p.url_target) {
			result += ' target="' + p.url_target + '"';
		}
		result += ' onClick="return clumpClick(' + p.id + ');"';
		result += '>' + p.title + ' (' + p.price_orig + ')</a>';
		result += '</h2>\n';
	}

	result += '</div>\n';
	return result;
}

Clump.prototype.onClick = function(map)
{
	if (this.properties.length == 1) {
		return this.properties[0].onClick(map);
	}

	// Do nothing.
	return;
}


/** \brief Suburb constructor
 */
function Suburb(prop, icon)
{
	this.id = prop.suburb.toLowerCase()
		+ "," + prop.city.toLowerCase()
		+ "," + prop.region.toLowerCase()
		+ "," + prop.nation.toLowerCase();

	this.name = prop.suburb;
	this.city = prop.city;
	this.region = prop.region;
	this.nation = prop.nation;
	this.pos = prop.pos;
	this.icon = icon ? icon : prop.icon;

	this.props = new Array();
	this.props.push(prop);

	this.n = 1;
}

/** \brief Get short HTML info for the suburb.
 */
Suburb.prototype.htmlShortInfo = function()
{
	if (this.props.length == 1) {
		return this.props[0].htmlShortInfo();
	}

	return '<div class="popup suburb">'
		+ this.name + '<br>'
		+ itemCount(this.n, 'property')
		+ '</div>';
}

Suburb.prototype.onClick = function(map)
{
	if (this.props.length == 1) {
		return this.props[0].onClick(map);
	}

	map.setCenter(this.pos, 13);
}


/** \brief City constructor
 */
function City(prop, icon)
{
	this.id = prop.city.toLowerCase()
		+ "," + prop.region.toLowerCase()
		+ "," + prop.nation.toLowerCase();

	this.name = prop.city;
	this.region = prop.region;
	this.nation = prop.nation;
	this.pos = prop.pos;
	this.icon = icon ? icon : prop.icon;

	this.n = 1;
}

/** \brief Get short HTML info for the city.
 */
City.prototype.htmlShortInfo = function()
{
	return '<div class="popup city">'
		+ this.name + '<br>'
		+ itemCount(this.n, 'property')
		+ '</div>';
}

City.prototype.onClick = function(map)
{
	map.setCenter(this.pos, 11);
}


/** \brief Region (county, state) constructor
 */
function Region(prop, icon)
{
	this.id = prop.region.toLowerCase()
		+ "," + prop.nation.toLowerCase();

	this.name = prop.region;
	this.nation = prop.nation;
	this.pos = prop.pos;
	this.icon = icon ? icon : prop.icon;

	this.n = 1;
}

/** \brief Get short HTML info for the region.
 */
Region.prototype.htmlShortInfo = function()
{
	return '<div class="popup region">'
		+ this.name + '<br>'
		+ itemCount(this.n, 'property')
		+ '</div>';
}

Region.prototype.onClick = function(map)
{
	map.setCenter(this.pos, 8);
}


/** \brief Nation constructor
 */
function Nation(prop, icon)
{
	this.id = prop.nation.toLowerCase();

	this.name = prop.nation;
	this.pos = prop.pos;
	this.icon = icon ? icon : prop.icon;

	this.n = 1;
}

/** \brief Get short HTML info for the nation
 */
Nation.prototype.htmlShortInfo = function()
{
	return '<div class="popup nation">'
		+ this.displayName() + '<br>'
		+ itemCount(this.n, 'property')
		+ '</div>';
}

Nation.prototype.onClick = function(map)
{
	switch (this.name) {
	case "us":
	case "ru":
		map.setCenter(this.pos, 4);
		break;

	default:
		map.setCenter(this.pos, 5);
		break;
	}
}

/** \brief Get the display name for a country
 */
Nation.prototype.displayName = function()
{
	return uiStrings["nation-" + this.name];
}


function mmMouseOver()
{
	curImgIndex = 0;
	this.openInfoWindowHtml(this.x_octal_mark.o.htmlShortInfo());
}


function mmClick()
{
	this.x_octal_mark.o.onClick(this.x_octal_mark.map);
}


/**
 */
function ManagedMarker(o)
{
	this.o = o;
	this.map = null;
	this.marker = null;
}

ManagedMarker.prototype.show = function(map)
{
	if (this.marker) {
		return false;
	}

	this.map = map;
	this.marker = new GMarker(this.o.pos, this.o.icon);
	this.marker.x_octal_mark = this;

	this.eh_mouseover = GEvent.addListener(this.marker, "mouseover", mmMouseOver);
	this.eh_onclick = GEvent.addListener(this.marker, "click", mmClick);

	this.map.addOverlay(this.marker);

	return true;
}

ManagedMarker.prototype.hide = function()
{
	if (!this.marker) {
		return false;
	}

	this.map.removeOverlay(this.marker);

	GEvent.removeListener(this.eh_onclick);
	GEvent.removeListener(this.eh_mouseover);

	this.marker.x_octal_mark = null;
	this.marker = null;
	this.map = null;

	return true;
}

ManagedMarker.prototype.display = function(map, bounds)
{
	if (!bounds) {
		bounds = map.getBounds();
	}

	if (bounds.contains(this.o.pos)) {
		this.show(map);
	}
	else {
		this.hide();
	}
}


/** \brief Populate a map of the world with nations
 */
function populateWorld(map)
{
	for (var i in nations) {
		markers.push(new ManagedMarker(nations[i]));
	}
}


/** \brief Populate a map of a nation with regions
 */
function populateNation(map)
{
	for (var i in regions) {
		markers.push(new ManagedMarker(regions[i]));
	}
}


/** \brief Populate a map of a region with cities
 */
function populateRegion(map)
{
	for (var i in cities) {
		markers.push(new ManagedMarker(cities[i]));
	}
}


/** \brief Populate a map of a city with suburbs.
 */
function populateCity(map)
{
	for (var i in suburbs) {
		markers.push(new ManagedMarker(suburbs[i]));
	}
}


/** \brief Populate a map of a suburb with properties
 */
function populateSuburb(map)
{
	// Find the clump a property should be in, or null if no clump exists.
	function clump(clumps, p) {
		for (var i = 0; i < clumps.length; ++i) {
			var c = clumps[i];
			if (c.isCoincident(p.pos)) {
				return c;
			}
		}
		return null;
	}

	var clumps = new Array();

	for (var i = 0; i < properties.length; ++i) {
		var p = properties[i];
		var c = clump(clumps, p);
		if (!c) {
			clumps.push(new Clump(p));
		}
		else {
			c.push(p);
		}
	}

	for (var i = 0; i < clumps.length; ++i) {
		markers.push(new ManagedMarker(clumps[i]));
	}
}


/** \brief Get the required semantic zoom factor from the Google maps zoom level
 */
function zoomFactor(level)
{
	switch (level) {
	case 0:
	case 1:
	case 2:
	case 3:
		return "world";
	case 4:
	case 5:
	case 6:
		return "nation";
	case 7:
	case 8:
	case 9:
	case 10:
		return "region";
	case 11:
	case 12:
		return "city";
	case 13:
	case 14:
	case 15:
	case 16:
	case 17:
		return "suburb";
	}

	return 0;
}


/** \brief Populate the map with markers.
 */
function populateMap(map, level)
{
	if (markers) {
		for (var m = 0; m < markers.length; ++m) {
			markers[m].hide();
		}
	}
	markers = new Array();

	switch (zoomFactor(level)) {
	case "suburb":
		populateSuburb(map);
		break;
	case "city":
		populateCity(map);
		break;
	case "region":
		populateRegion(map);
		break;
	case "nation":
		populateNation(map);
		break;
	case "world":
	default:
		populateWorld(map);
		break;
	}
}


/** \brief Set up the nations, regions, cities and suburbs arrays.
 */
function setup()
{
	function reAvg(a, n, b)
	{
		return ((parseFloat(a) * n) + parseFloat(b)) / (n + 1);
	}

	/** \brief Merge a new place into a collection of places.
	 *
	 * For creating a cumulative average,
	 */
	function merge(collection, place)
	{
		var existing = collection[place.id];
		if (existing) {
			var lat = reAvg(existing.pos.lat(), existing.n, place.pos.lat());
			var lng = reAvg(existing.pos.lng(), existing.n, place.pos.lng());

			existing.pos = new GLatLng(lat, lng);
			++existing.n;

			if (existing.props && place.props) {
				existing.props.push(place.props[0]);
			}
		}
		else {
			collection[place.id] = place;
		}
	}

	/* TODO: Build these on demand, and the clump array too. */
	nations = new Object();
	regions = new Object();
	cities = new Object();
	suburbs = new Object();

	for (var i = 0; i < properties.length; ++i) {
		var p = properties[i];

		merge(nations, new Nation(p));
		merge(regions, new Region(p));
		merge(cities, new City(p));
		merge(suburbs, new Suburb(p));
	}
}


var map;


/** \brief Event handler for map being zoomed.
 *
 * The map is passed as "this".
 */
function onZoom(oldlevel, newlevel)
{
	if (zoomFactor(newlevel) == zoomFactor(oldlevel)) {
		return;
	}

	populateMap(this, newlevel);
}


function onMove()
{
	var mapbounds = this.getBounds();
	var ne = mapbounds.getNorthEast();
	var sw = mapbounds.getSouthWest();
	var right = ne.lng();
	var top = ne.lat();
	var left = sw.lng();
	var bot = sw.lat();
	var height = top - bot;
	var width = right - left;
	if (width < 0) {
		/* Crosses meridian */
		width += 360;
	}
	var newbounds;
	if (height >= 30 || width >= 120) {
		/* Large map. Just use actual bounds. */
		newbounds = new GLatLngBounds(
				new GLatLng(bot, left),
				new GLatLng(top, right));
	}
	else {
		/*
		 * Small map. Use triple the bounds so scrolling is smoother
		 * because markers within 1 width's drag in any direction are
		 * already present.
		 */
		newbounds = new GLatLngBounds(
				new GLatLng(bot - height, left - width),
				new GLatLng(top + height, right + width));
	}

	for (var m = 0; m < markers.length; ++m) {
		markers[m].display(this, newbounds);
	}

	var zipNationElem = document.getElementById('zip-nation');
	if (zipNationElem) {
		var pos = map.getCenter();
		var nearest_id;
		var nearest_dist;
		for (var n in nations) {
			dist = pos.distanceFrom(nations[n].pos);
			if (!nearest_dist || dist < nearest_dist) {
				nearest_id = nations[n].id;
				nearest_dist = dist;
			}
		}

		if (nearest_id) {
			zipNationElem.value = nearest_id;
		}
	}
}


/** \brief Call to set the criteria for shown properties.
 */
function map_update()
{
	/** Create a GIcon from a dictionary of attributes */
	function GIconFromDict(dict)
	{
		function fixVal(icon, dict, attr) {
			if (dict[attr]) {
				icon[attr] = dict[attr];
			}
		}
		function fixSize(icon, dict, attr) {
			if (dict[attr]) {
				icon[attr] = new GSize(
						dict[attr].width,
						dict[attr].height);
			}
		}
		function fixPoint(icon, dict, attr) {
			if (dict[attr]) {
				icon[attr] = new GPoint(
						dict[attr].x,
						dict[attr].y);
			}
		}

		if (!dict) {
			return null;
		}

		var icon = new GIcon();
		fixVal(icon, dict, 'image');
		fixVal(icon, dict, 'shadow');
		fixSize(icon, dict, 'iconSize');
		fixSize(icon, dict, 'shadowSize');
		fixPoint(icon, dict, 'iconAnchor');
		fixPoint(icon, dict, 'infoWindowAnchor');
		fixVal(icon, dict, 'printImage');
		fixVal(icon, dict, 'mozPrintImage');
		fixVal(icon, dict, 'printShadow');
		fixVal(icon, dict, 'transparent');
		fixVal(icon, dict, 'imageMap');
		fixVal(icon, dict, 'maxHeight');
		fixVal(icon, dict, 'dragCrossImage');
		fixSize(icon, dict, 'dragCrossSize');
		fixSize(icon, dict, 'dragCrossAnchor');
		return icon;
	}

	try {
		if (!GBrowserIsCompatible
			|| !GBrowserIsCompatible())
		{
			return;
		}
	}
	catch (err) {
		return;
	}


	var borderElem = document.getElementById('map_border');
	if (borderElem && borderElem.x_octal_propiconprops) {
		borderElem.x_octal_propicon = GIconFromDict(
				borderElem.x_octal_propiconprops);
	}


	var url = ajaxbase + '/search.cgi?w=0';
	var added = true;
	var values = octalFormValues(document.getElementById('searchform'));
	for (var name in values) {
		for (var i = 0; i < values[name].length; ++i) {
			url += (added ? "&" : "?") + name
				+ "=" + values[name][i];
			added = true;
		}
	}

	properties = new Array();

	function map_update_callback(request)
	{
		if (!request) {
			alert('No xmlhttprequest!');
		}
		if (request.readyState != 4) {
			return;
		}
		if (request.status != 200
				|| !request.responseXML)
		{
			alert(request.status + ': ' + request.responseText);
			return;
		}

		var nodes = request.responseXML.getElementsByTagName("property");
		if (!nodes.length) {
			alert('No properties found');
			return;
		}

		var bounds = new GLatLngBounds();

		for (var i = 0; i < nodes.length; ++i) {
			var p = new Property(nodes.item(i),
					borderElem.x_octal_propicon);
			properties.push(p);
			bounds.extend(p.pos);
		}

		var newZoom = map.getBoundsZoomLevel(bounds) - 1;
		if (newZoom < 1) {
			newZoom = 1;
		}

		setup();
		populateMap(map, newZoom);
		map.setCenter(bounds.getCenter(), newZoom);
	}

	octalXmlHttpASync(url, map_update_callback);

	return false;
}


var lastborderwidth = 0;
var lastborderheight = 0;

function resize()
{
	var borderElem = document.getElementById('map_border');
	var mapElem = document.getElementById('map');

	var mapsize_fn;
	if (mapElem.x_octal_mapsize) {
		mapsize_fn = mapElem.x_octal_mapsize;
	}
	else if (borderElem.x_octal_mapsize) {
		mapsize_fn = borderElem.x_octal_mapsize;
	}


	if (mapsize_fn) {
		var newsize = mapsize_fn();
		var bordersize = bordertable_bordersize(borderElem);

		borderElem.style.width = newsize.width + "px";
		borderElem.style.height = newsize.height + "px";

		var mapwidth = newsize.width - bordersize.width;
		var mapheight = newsize.height - bordersize.height;
		if (newsize.pad_width && newsize.pad_height) {
			mapwidth -= newsize.pad_width;
			mapheight -= newsize.pad_height;
		}
		mapElem.style.width = mapwidth + "px";
		mapElem.style.height = mapheight + "px";

		var ieSpaceElem = document.getElementById('iespace');
		if (ieSpaceElem
				&& newsize.ieSpaceHeight
				&& newsize.ieSpaceHeight > 0)
		{
			ieSpaceElem.style.height = newsize.ieSpaceHeight + 'px';
		}

		// IE and FF take a bit of time to get the borders right.
		// This seems to help.
		if (lastborderwidth < 0
				&& lastborderheight < 0)
		{
			// Border stabilised already. Do nothing.
		}
		else if (lastborderwidth == bordersize.width
				&& lastborderheight == bordersize.height)
		{
			// Border just stabilised. Remember this and update map.
			lastborderwidth = -1;
			lastborderheight = -1;
			window.setTimeout(map_update, borderStabiliseTimeout);
		}
		else {
			// Border not yet stabilised. Remember this size resize again.
			lastborderwidth = bordersize.width;
			lastborderheight = bordersize.height;
			window.setTimeout(resize, borderStabiliseTimeout);
		}
	}

	if (map) {
		map.checkResize();
	}
}


/** \brief Call to setup the page when it's loaded.
 */
function load()
{
	var request;

	map = null;
	markers = new Array();
	uiStrings = new Array();

	var form = document.getElementById('searchform');
	for (var i = 0; i < (form ? form.length : 0); ++i) {
		var elem = form.elements[i];
		if (elem.addEventListener) {
			elem.addEventListener('change', map_update, false);
		}
		else if (elem.attachEvent) {
			elem.attachEvent('onchange', map_update);
		}
	}

	request = octalXmlHttpSync(ajaxbase + "/searchstrings.cgi");
	if (request.responseXML) {
		var nodes = request.responseXML.getElementsByTagName("string");
		for (var i = 0; i < nodes.length; ++i) {
			var node = nodes.item(i);
			uiStrings[node.getAttribute("name")] = nodetext(node);
		}
	}
	else {
		alert('xmlhttprequest for localised strings failed');
	}

	request = octalXmlHttpSync(ajaxbase + "/nations.cgi");
	if (request.responseXML) {
		var nodes = request.responseXML.getElementsByTagName("nation");
		if (!nodes.length) {
			alert('No nations');
		}
		for (var i = 0; i < nodes.length; ++i) {
			var node = nodes.item(i);
			uiStrings["nation-" + node.getAttribute("id")]
				= nodetext(node.getElementsByTagName("name").item(0));
		}
	}
	else {
		alert('xmlhttprequest for nations failed');
	}

	try {
		if (GBrowserIsCompatible()) {
			map = new GMap2(document.getElementById('map'));
			map.addControl(new GLargeMapControl());
			map.addControl(new GMapTypeControl());
			if (x_octal_map_overview) {
				map.addControl(new GOverviewMapControl());
			}
			map.setCenter(new GLatLng(0, 0), 2);

			GEvent.addListener(map, "zoomend", onZoom);
			GEvent.addListener(map, "moveend", onMove);
		}
	}
	catch (err) {
		// Do nothing.
	}

	window.setTimeout(resize, 100);

	return;
}


function unload()
{
	try {
		GUnload();
	}
	catch (err) {
		// Do nothing.
	}
}


if (window.addEventListener) {
	window.addEventListener('load', load, false);
	window.addEventListener('unload', unload, false);
	window.addEventListener('resize', resize, false);
}
else if (window.attachEvent) {
	window.attachEvent('onload', load);
	window.attachEvent('onunload', unload);
	window.attachEvent('onresize', resize);
}
