var map;
var gMaxWidth = 400;
var gInitialZoom = 2;
var gIconWidth = 20;
var gIconHeight = 20;
var gSeamapLayerOpacity = 1;
var gSeamapLayerPNG24 = false;
var gClickedPoint = null;
var gClickedPolygon = null;
var gAutoResize = true;

var gValidatedDataid = "";	// set in validation_process

var gOverlays = [];
var gKmlOverlays = [];
var gShapePolygons = [];
var gSeamapTileLayer = null;

var gDefaultClickHandler;
var gDefaultMousemoveHander;

// Mas status. Update when map has changed
var gFullExtent;
var gZoomLevel;
var gCenterPoint;
var gNumLayerAdded = 0;

// Window size. Used to determine popup window positions.
var gWinW;
var gWinH;

// Tool management
var gToolStack = ['default'];	// Array of activated tools.

// Quick search
var gSearchType;

// Popup Window. Layer info Window
var gCurrentPopup = null;
var gLayerInfoTabs = null;
var gWmsTabs = null;

if (is_ie) {
	var gTableRow = 'block';
	var gTableCell = 'block';
} else {
	var gTableRow = 'table-row';
	var gTableCell = 'table-cell';
}

// Before loading Google Maps, resize the map div element so it fits the page
function maximize_map_window() {
	if (!gAutoResize) {
		return false;
	}

	gWinW = GetWindowSize('width');
	gWinH = GetWindowSize('height');

	if (is_ie) {
		additional_buffer = 0;
	} else {
		additional_buffer = 15;
	}

	var map_width = gWinW - (280 + additional_buffer); // Take buffer into consideration. 240px (div width) + 30px buffer.
	var map_height = gWinH - 150;
	adjust_map_size(map_width, map_height);
}

function adjust_map_size(map_width, map_height) {
	var map_div = $("map");
	map_div.setStyle({width: map_width + "px", height: map_height + "px"});

	// Relocate Please wait div
	$('please_wait').setStyle({
		left: (Position.cumulativeOffset(map_div)[0] + parseInt(map_width / 2) - 103) + "px",
		top: (Position.cumulativeOffset(map_div)[1] + parseInt(map_height / 2) - 18) + "px"
	});

	// Height of Legend
	$('legend_content').setStyle({height: map_height + "px"});
}

// This function is for sighting_on_gm.php only.
function load() {
	if (GBrowserIsCompatible()) {
		map = new GMap2($("map"), {draggableCursor:"default"});
		map.addControl(new GLargeMapControl());
		map.addControl(new GMapTypeControl());

		map.setCenter(new GLatLng(38, -97), gInitialZoom, G_SATELLITE_MAP);
		$('map').setStyle({"background-color": '#1D1C56'});	// Set GM's background color to the dark ocean color.

		//Load Mapserver image
		load_sighting(map);

		// Get the initial legend
		//get_legend() is run in per_app_initialize()

		// Set default event handlers for click and mousemove.
		// Click ==> Identify Tool
		// Mousemove ==> Show coordinates
		gDefaultClickHandler = GEvent.addListener(map, "click", on_click_identify);
		gDefaultMousemoveHander = GEvent.addListener(map, 'mousemove', on_mousemove_coordinate);

		// Save the initial bounding box and zoom level.
		gFullExtent = map.getBounds();
		gZoomLevel = map.getZoom();		// Obviously 3
		gCenterPoint = map.getCenter(); // Obviously 38, -90

		// Register Navigation Tools.
		$$('img.NavTool').each(function (item) {NagivationTools.add(item);});

		per_app_initialize();
	}
}

function timeoutNotice() {
	alert("Your session is about to timeout.\nPlease save your map for the future use.\n[Tools]-[Save Map].");
}

function per_app_initialize() {
	switch (gApp) {
		case "add_layer":
			switch (gLayerType) {
				case 'custom':
					add_custom_layer(gLayerId, gSerializedParam.replace(/;/g, "\n"));
					break;
				default:
					add_layer(gLayerId, gLayerType, gAuthorized);
			}
			break;
		default:
			get_legend();
	}
}


function on_mousemove_coordinate(point) {
	$("current_position").update("X:" + point.lng().toFixed(2) + " Y:" + point.lat().toFixed(2));
}

function load_sighting(map) {
	// GCopyright doesn't matter unless the overlay is added to the map as a new maptype.
	var copyright = new GCopyright(1,
	   new GLatLngBounds(new GLatLng(23,122),new GLatLng(46,151) ),
	    1, "OBIS-SEAMAP");

	var copyrightCollection = new GCopyrightCollection('Datasets');
	copyrightCollection.addCopyright(copyright);

	var myTileLayer = new GTileLayer(copyrightCollection, 1, 16);
	myTileLayer.getTileUrl = myGetTileUrl;
	myTileLayer.getOpacity = function() { return gSeamapLayerOpacity; }
	myTileLayer.isPng = function() { return gSeamapLayerPNG24; }

	//var myTileOverlay = new GTileLayerOverlay(myTileLayer);
	gSeamapTileLayer = new GTileLayerOverlay(myTileLayer);
}

function myGetTileUrl(tile, zoom) {
    // max zoom plus 1
    var projection = new GMercatorProjection(18);

    // Four vertices location in pixel in GPoint coordinates
    var p1 = new GPoint(tile.x*256,tile.y*256);
    var p2 = new GPoint(p1.x+256,p1.y+256);

    // latitude/longitude of four vertices location in decimal degree
    var latlng1 = projection.fromPixelToLatLng(p1,zoom);
    var latlng2 = projection.fromPixelToLatLng(p2,zoom);

    var lat1 = latlng1.lat();
    var lon1 = latlng1.lng();
    var lat2 = latlng2.lat();
    var lon2 = latlng2.lng();

    // bounding box for mapserver
    var minlat = Math.min(lat1,lat2);
    var minlon = Math.min(lon1,lon2);
    var maxlat = Math.max(lat1,lat2);
    var maxlon = Math.max(lon1,lon2);
	var mapext = minlon + "," + minlat + "," + maxlon + "," + maxlat;

	var parameters = {
		mode: "draw_map",
		mapsize: "256+256",
		mapext: mapext,
		outputformat: gMapserver.parameters.outputformat,
		zoom: zoom
	}

    return gMapserver.mapserverRequest(parameters);
}


/***** Identeify start *****/
function on_click_identify(overlay, point) {
	if (active_tool() != "default") {
		return false;
	}

	// GM's default polygon click passes point as undefined, so ignore the event.
	// Custom polygon click event listener passes both overlay and point.
	if (overlay && typeof(point) == 'undefined') {
		return false;
	}

	$('please_wait').show();
	gClickedPoint = point;
	var lat = point.lat();
	var lon = point.lng();

	gMapserver.request("identify", show_identify,
		{
			latitude: lat,
			longitude: lon
		}, "", {});
}

function show_identify(oj) {
	var msg = oj.responseText;

	if (msg.indexOf("No sightings found") > 0 && gClickedPolygon != null) {
		var opt = map.getInfoWindow();
		opt.maxWidth = gMaxWidth;
		msg = getPolygonText(gClickedPolygon);
		gClickedPolygon = null;
	}
	$('please_wait').hide();
	map.openInfoWindowHtml(gClickedPoint, msg);
}
/***** Identeify end *****/


/***** Action handlers *****/
function refresh_map(oj) {
	var res  = oj.responseXML;

	if (res.getElementsByTagName("error_msg").length > 0) {
		var error_msg = getXmlNodeValue(res, "error_msg");
		$('please_wait').hide();
		gMapserver.UpdatingLegend = false;
		alert(error_msg);
		return false;
	}

	// To refresh SEAMAP layers whenever necessary,
	// first remove (drop) them and add again. (No need to remove KML, polygons.)
	map.removeOverlay(gSeamapTileLayer);
	gMapserver.refresh();
	//gSeamapTileLayer.refresh(); Doesn't work.
	map.addOverlay(gSeamapTileLayer);

	if (res.getElementsByTagName("bbox").length > 0) {
		gNumLayerAdded += 1;
		var bbox = getXmlNodeValue(res, "bbox");
		gFullExtent = set_bbox(bbox);
		gCenterPoint = gFullExtent.getCenter();
		map.setCenter(gCenterPoint, gZoomLevel);	// setCenter needed to properly use setZoom
		gZoomLevel = map.getBoundsZoomLevel(gFullExtent);
		map.setZoom(gZoomLevel);
	}
	if (res.getElementsByTagName("center").length > 0) {
		// For pacific-centric dataset, bbox gets map to the opposite side.
		// map center is adjusted properly by seamap_gm.php so run the codes below.
		var center = getXmlNodeValue(res, "center");
		center = center.split(",");		// Longitude first

		// Center the map
		gCenterPoint = new GLatLng(center[1], center[0]);
		//map.panTo(gCenterPoint);	// Latitude first
		map.setCenter(gCenterPoint);	// Latitude first
	}

	// Restore polygons if return message includes <MultiGeometry> (usually Restore Map action)
	if (res.getElementsByTagName("MultiGeometry").length > 0) {
		var coords = res.getElementsByTagName("coordinates");

		for (var i = 0; i < coords.length; i++) {
			var coordinates = coords[i].childNodes[0].nodeValue;
			var points = [];

			coordinates = coordinates.strip();
			coordinates = coordinates.replace(/\n/, " ");

			coordinates = coordinates.split(" ");
			for(var j = 0; j < coordinates.length; j++){
				coordinates[j] = coordinates[j].split(",");
			}
			for(var j = 0; j < coordinates.length; j++){
				points[j] = new  GLatLng(parseFloat(coordinates[j][1]), parseFloat(coordinates[j][0]));
			}

			make_polygon(points);
		}
	}

	// Save polygon shapefiles coords info
	if (res.getElementsByTagName("coords").length > 0) {
		var layer_name = getXmlNodeValue(res, "layer_name");
		var coords = getXmlNodeValue(res, "coords");
		gShapePolygons[layer_name] = {name: layer_name, status: "ON", coords: coords};
	}

	// Update legend
	// set gMapserver.UpdatingLegend to true before calling refresh_map
	// if the legend should be updated.
	if (gMapserver.UpdatingLegend) {
		get_legend();
	}

	$('please_wait').hide();
}


function set_bbox(bbox) {
	// Update gFullExtent if necessary.
	var arry_bbox = bbox.split(",");

	if (gNumLayerAdded > 1) {
		var lat_max =  gFullExtent.getNorthEast().lat();
		var lon_max =  gFullExtent.getNorthEast().lng();
		var lat_min =  gFullExtent.getSouthWest().lat();
		var lon_min =  gFullExtent.getSouthWest().lng();

		if (lon_min > arry_bbox[0]) {
			lon_min = arry_bbox[0];
		}
		if (lat_min > arry_bbox[1]) {
			lat_min = arry_bbox[1];
		}
		if (lon_max < arry_bbox[2]) {
			lon_max = arry_bbox[2];
		}
		if (lat_max < arry_bbox[3]) {
			lat_max = arry_bbox[3];
		}
	} else {
		// The first time a layer is added, initialize the bbox with the extent of the layer.
		var lon_min =  arry_bbox[0];
		var lat_min =  arry_bbox[1];
		var lon_max =  arry_bbox[2];
		var lat_max =  arry_bbox[3];
	}

	FullExtent = new GLatLngBounds(new GLatLng(lat_min, lon_min), new GLatLng(lat_max, lon_max));
	return FullExtent;
}


function add_layer(id, layer_type, authorized) {
	$('please_wait').show();
	gMapserver.UpdatingLegend = true;

	if (authorized) {
		var additional = "accessMode=authorized";
	} else {
		var additional = "";
	}
	gMapserver.request("add_layer", refresh_map,
		{
			layer_name: id,
			layer_type: layer_type
		}, additional, {});
}

function add_wms_layer(base_url, layer_list, wms_parameters, wms_title, wms_abstract) {
	$('please_wait').show();
	gMapserver.UpdatingLegend = true;
	gMapserver.request("add_wms_layer", refresh_map,
		{
			layer_name: layer_list,
			layer_type: "wms",
			wms_url: base_url,
			wms_parameters: wms_parameters,
			wms_title: wms_title,
			wms_abstract: wms_abstract
		}, "", {});
}

/***** KML functions *****/
function add_kml() {
	if ($F('kml_url') == "" && $F('kml_file') == "") {
		alert ("Please specify either URL to KML or UPload KML.");
		return false;
	}
	if ($F('kml_url') != "") {
		var kml = $F('kml_url');
		var base_sep = '/';
		var RegExp = /^(http|https):\/\/(.)+[.](.)+\/(.)+$/	// script-generated kml may not contain the extension
		var kml_type = "url";
	}
	if ($F('kml_file') != "") {
		var kml = $F('kml_file');
		var base_sep = '\\';
		var RegExp = /^(.):\\(.)+[.](kml|kmz)$/
		var kml_type = "file";
	}
	if (kml.toLowerCase().match(RegExp) == null || kml.length <= 0) {
		alert("Invalid KML " + kml_type + " (" + kml + ") was passed.");
		return false;
	}
	var filebase = kml.substring(kml.lastIndexOf(base_sep) + 1);
	if (filebase.indexOf(' ') >= 0) {
		alert("KML file name can't contain a space.");
		return false;
	}

	var user_name = $F('kml_username');
	var password = $F('kml_password');
	if (user_name == "" || password == "") {
		alert("Please enter user name and password.");
		return false;
	}

	var status = authenticate(user_name, password);
	if (!status) {
		alert(user_name + ' is not a valid user name or password does not match.');
		return false;
	}

	switch (kml_type) {
		case "url":
			gMapserver.request("download_kml", parse_kml,
				{
					kml_url: kml
				}, "", {});
			break;
		case "file":
			AIM.submit(document.forms['kml_setting'], {'onComplete' : kml_uploaded});
			document.forms['kml_setting'].submit();
			break;
	}
 	$('kml_popup').hide();
}

function parse_kml(oj) {
	var kml = oj.responseXML;
	var title = getXmlNodeValue(kml, "name");
	if (title == 'Error occurred') {
		alert("Failed to add KML.\nPlease see the information popup.");
		var err = getXmlNodeValue(kml, "Error");
		$('error_info').update(err);
		return false;
	}
	if (title.length > 40) {
		title = title.substring(0, 40);
	}

	// Ideally, parse_kml could have a third parameter for kml_url.
	// No way to do this, so kml_url is taken from the form value.
	var kml_url = document.forms['kml_setting'].kml_url.value;
	kml_url += "?" + (new Date()).format('mmddyyyyhhnnss');
	var geoXml = new GGeoXml(kml_url);
	map.addOverlay(geoXml);

	// Store it in global array for KML layers
	var kml_id = "kml_" + gKmlOverlays.length;
	var kml_info_array = [];
	kml_info_array['title'] = title;
	kml_info_array['kml_id'] = kml_id;
	kml_info_array['geoXml'] = geoXml;

	// Add it to layer list
	add_kml_to_layer_list (title, kml_id);
	gKmlOverlays.push(kml_info_array);
}

function kml_uploaded(response) {
	$('debug_info').update($('debug_info').innerHTML + "<BR>" + response);
	if ($('upload_kml_file_status').innerHTML == "OK") {
		var kml = $('kml_file_uploaded_url').innerHTML;
		document.forms['kml_setting'].kml_url.value = kml;
		gMapserver.request("download_kml", parse_kml,
			{
				kml_url: kml
			}, "", {});
	} else {
		var msg = $('kml_file_error_msg').innerHTML;
		alert("Failed to upload KML file\n" + msg);
		$('error_info').update("kml_uploaded: " + msg);
	}
}

function add_kml_to_layer_list (title, kml_id) {
	var list_list = $('non_mapserver_layer_list');
	var aLi = $(document.createElement("LI"));
	aLi.id = kml_id;
	content = '<table><tr><td>';
	content += '<a href="javascript: void(0);" onclick="delete_kml_layer(\'' + kml_id + '\');"><img src="../onlinemap/styles/skins/icons/icon_delete.png"></a>';
	content += '</td>';
	content += '<td align="center" width=18><input type="checkbox" onclick="kml_onoff(this, \'' + kml_id + '\');" CHECKED></td>';
	content += '<td>' + title + '</tr></table>';
	aLi.innerHTML = content;
	list_list.appendChild(aLi);
}


function delete_kml_layer(kml_id) {
	var ids = kml_id.split('_');
	var kml_array = gKmlOverlays[ids[1]];
	map.removeOverlay(kml_array['geoXml']);

	$(kml_id).parentNode.removeChild($(kml_id));
}

function kml_onoff(checkbox, kml_id) {
	var ids = kml_id.split('_');
	var kml_array = gKmlOverlays[ids[1]];
	var kml = kml_array['geoXml'];

	if (checkbox.checked) {
		map.addOverlay(kml);
	} else {
		map.removeOverlay(kml);
	}
}

function show_kml_setting() {
	$('kml_url').value = "";
	$('kml_file').value = "";
	$('kml_password').value = "";
	$("kml_popup").show();
}
/***** KML functions end *****/

/***** Upload and Add Shapefile *****/
function show_shape_popup() {
	$('shapefile_shp').value = "";
	$('shapefile_password').value = "";
	$("shapefile_popup").show();
}

function upload_add_shapefile() {
	var shape_ext = ['shp', 'dbf', 'shx'];
	var input_error = shape_ext.any(function (item) {
		var input_name = 'shapefile_' + item;
		if ($F(input_name) == "") {
			alert ("Please specify shapefile (." + item + ") location.");
			return true;
		} else {
			var shapefile = $F(input_name);
			var RegExp = /^(.):\\(.)+[.](shp|dbf|shx)$/
			if (shapefile.toLowerCase().match(RegExp) == null || shapefile.length <= 0) {
				alert("Invalid shapefile " + "(" + shapefile + ") was passed.");
				return true;
			}
		}
	});
	if ($F('shapefile_shp').replace(/(shp|dbf|shx)/g, '') != $F('shapefile_dbf').replace(/(shp|dbf|shx)/g, '') ||
		$F('shapefile_shp').replace(/(shp|dbf|shx)/g, '') != $F('shapefile_shx').replace(/(shp|dbf|shx)/g, '')) {
			alert('Files related to your shapefile is not consisntent.');
			input_error = true;
	}

	if (input_error) {
		return false;
	}

	var user_name = $F('shapefile_username');
	var password = $F('shapefile_password');
	if (user_name == "" || password == "") {
		alert("Please enter user name and password.");
		return false;
	}

	var status = authenticate(user_name, password);
	if (!status) {
		alert(user_name + ' is not a valid user name or password does not match.');
		return false;
	}

	AIM.submit(document.forms['shapefile_setting'], {'onComplete' : shapefile_uploaded});
	document.forms['shapefile_setting'].submit();
 	$('shapefile_popup').hide();
}

function shapefile_uploaded(response) {
	$('debug_info').update(response);
	if ($('upload_shapefile_shp_status').innerHTML == "OK") {
		var shapefile = $('shapefile_shp_uploaded_path').innerHTML;
		gMapserver.UpdatingLegend = true;
		gMapserver.request("add_shapefile", refresh_map,
			{
				shapefile: shapefile
			}, "", {});
	} else {
		var msg = $('shapefile_shp_error_msg').innerHTML;
		alert("Failed to upload shapefile\n" + msg);
		$('error_info').update("shapefile_uploaded: " + msg);
	}
}
/***** Upload and Add Shapefile ends *****/

function add_custom_layer(data_id, serialized_form_values) {
	$('please_wait').show();
	gMapserver.UpdatingLegend = true;
	gMapserver.request("add_custom_layer", refresh_map,
		{
			layer_name: data_id,
			layer_type: "custom"
		},
		serialized_form_values,	{});
}

function layer_onoff(thebox, layer_name) {
	if (gMapserver.UpdatingLegend) {
		alert("Legend is being updated. Try again.");
		return false;
	}
	if (thebox.checked) {
		layer_status = "ON";
	} else {
		layer_status = "OFF";
	}

	gMapserver.UpdatingLegend = true;
	gMapserver.request("layer_onoff", refresh_map,
		{
			layer_name: layer_name,
			status: layer_status
		}, "", {});

	if (gShapePolygons[layer_name]) {
		gShapePolygons[layer_name].status = layer_status;
	}
}


function get_legend() {
	gMapserver.request("get_legend", refresh_legend,
		{}, "",	{asynchronous: false});
}

function refresh_legend(oj) {
	var res = oj.responseText;
	$('layer_list').update(res);
	
	Sortable.create("layer_list",
    	{
    		dropOnEmpty:true,
    		containment:["layer_list"],
    		constraint:false,
    		onUpdate: layer_order_changed
    	});
    
	gMapserver.UpdatingLegend = false;
}

function onoff_classes(img, layer_name) {
	var li = $(layer_name);		// Return <li> element for the layer in legend
	var classes = $A(li.getElementsByClassName('classes'));
	classes.each(Element.toggle);

	if (img.src.indexOf('expand') > 0) {
		img.src = "/icons/tree_collapse.png";
	} else {
		img.src = "/icons/tree_expand.png";
	}
}

function get_image_legend() {
	var post_data = gMapserver.mapserverRequest({mode:'get_image_legend'});
	window.open(gMapserver.mapserverRequest({mode:'get_image_legend'}), "image_legend");
}

function layer_order_changed(ul) {
	if (gMapserver.UpdatingLegend) {
		alert("Legend is being updated. Try again.");
		return false;
	}
	// This function is called when the list order actually has changed, which
	// is identified by Sortable.
	// No need to check here if the order has changed.
	var list_serialized = Sortable.serialize('layer_list');
	$("debug_info").update(list_serialized);

	gMapserver.UpdatingLegend = true;
	gMapserver.request("layer_order", refresh_map,
		{},	list_serialized, {});
}

/***** Layer information popup window start *****/
function show_layer_info (layer_name, layer_id) {
	// layer_id: layer type plus id. e.g. ptobs_387, species_180490,180491,180492
	// Register layer info tabs, if not yet.
	if (gLayerInfoTabs == null) {
		var tabs = [
			{name: "tab_overview"},
			{name: "tab_symbology", onclick: get_symbology},
			{name: "tab_summary", onclick: get_stat},
			{name: "tab_download", onclick: check_ds_type}
		];
		gLayerInfoTabs = new TabGroup(tabs);
	}

	// Save the selected layer name and id in gMapserver
	gMapserver.CurrentLayer = layer_name;
	gMapserver.CurrentLayerID = layer_id;
	gMapserver.RefreshMap = false;

	var arry_layer = layer_id.split("_");

	switch (arry_layer[0]) {
		case "background":
		case "shape":
			var msg = "Background/Effort/Uploaded shapefile layer doesn't have layer information.";
			alert(msg);
			return false;
			break;
		case "lneff":
		case "species":
		case "custom":
			$("tr_download_shapefile").hide();
			break;
		case "wms":
			break;
		case "envdata":
			layer_id = "envdata_" + layer_name;	// All metadata is stored in mapfile's metedata section. So layer name is the only one needed.
		default:
			var display = gTableRow;
			$("tr_download_shapefile").setStyle({display: display});
			break;
	}

	$('please_wait').show();
	$("layer_title").update(layer_name.substr(0, 60));

	// Initialize Data Summary tab.
	$('tab_summary').update("");
	$('SymbolLegend').update("");

	// Insert Terms of Use in Download tab
	var legalese_url = g_ROOT_URL + "/about/legalese_content";
	GDownloadUrl(legalese_url, show_legalese);

	$("tab_overview").update("Retrieving layer overview. Please wait (may take 30-60 seconds)...");
	gMapserver.request("layer_info", render_metadata,
		{
			layer_id: layer_id
		}, "", {});
}

function check_ds_type() {
	var layer_id = gMapserver.CurrentLayerID;	// Actually, this is layer type plus id. e.g. ptobs_387, species_180490,180491,180492
	var arry_layer_id = layer_id.split("_");
	var layer_type = arry_layer_id[0];
	var data_id =  arry_layer_id[1];

	switch (layer_type) {
		case 'wms':
		case 'fao':
		case "lntag":
		case "lneff":
			alert("Data download is available for observation/species/custom layers only");
			gLayerInfoTabs.switchTo("tab_overview");
			return false;
	}
}

function show_legalese(text, readyState) {
	if (readyState == 200){		// HTTP status
		$("legalese").update(text);
	}
}

function render_metadata (res) {
	var content = res.responseText;

	$("tab_overview").update(content);
	gLayerInfoTabs.switchTo("tab_overview");
	$("layer_info").show();
	$('please_wait').hide();
}

function download_dispatcher(mode) {
	// Assume that the layer id is properly set in gMapserver,
	// when Layer info popup is shown (show_layer_info).
	var layer_id = gMapserver.CurrentLayerID;		// ptobs_38
	layer_id = layer_id.split("_");
	var layer_type = layer_id[0];
	var id = layer_id[1];

	var theForm = document.forms['download_form'];
	switch (layer_type) {
		case "ptobs":
		case "pttag":
			var base_url = BASE_PLONE_URL + "datasets/detail/" + id;
			var kml_key = "datasetid";
			switch (mode) {
				case "shapefile":
					var url = base_url + "/shapefile";
					break;
				case "csv":
					var url = base_url + "/download.csv";
					break;
			}
			break;
		case "species":
			var url = BASE_PLONE_URL + "extraction/generate_csv";
			theForm['where'].value = "_sp_tsn=" + id;
			var kml_key = "tsn";
			break;
		case "custom":
			var base_url = BASE_PLONE_URL + "statcenter/ajax_stat.php?stat_action=export_obs";
			var kml_key = "tsn";
			var suffix = false;
			alert('coming soon!');
			return false;
			break;
	}

	switch (mode) {
		case "googleearth":
			var url = BASE_URL + "mapservice/googleearth/kml_generator.php?" + kml_key + "=" + id;
			break;
	}

	theForm.action = url;
	theForm.submit();
}

function get_stat() {
	// Prepare Data Summary DIV
	var divContent = $('tab_summary');
	if (divContent.innerHTML != "") {
		return false;
	}

	var layer_id = gMapserver.CurrentLayerID;	// Actually, this is layer type plus id. e.g. ptobs_387, species_180490,180491,180492
	var arry_layer_id = layer_id.split("_");
	var layer_type = arry_layer_id[0];
	var data_id =  arry_layer_id[1];

	var method = "GET";
	var parameters = "";
	switch (layer_type) {
		case 'ptobs':
		case 'pttag':
			var url = g_STAT_CENTER_URL + "stat_control.php?id=" + data_id + "&dstype=" + layer_type + "&effort=check";
			var onSuccess = show_stat;
			break;
		case 'pthab':
			var url = g_DATASETS_URL + "species_recorded.php?id=" + data_id;
			var onSuccess = show_stat;
			break;
		case "species":
			var url = g_STAT_CENTER_URL + "ajax_stat.php?goSearch=1&speciesTsnList=" + data_id + "&byYear=1&map=1";
			var onSuccess = show_stat2;
			break;
		case "custom":
			var parameters = $F('serialized_criteria');
			parameters += "&stat_action=query&map=2";
			var url = g_STAT_CENTER_URL + "ajax_stat.php";
			var onSuccess = show_stat2;
			var method = "POST";
			break;
		case "esas":
			var url = g_STAT_CENTER_URL + "stat_control.php?id=" + data_id + "&dstype=" + layer_type + "&effort=";
			var onSuccess = show_stat;
			break;
		default:
			alert("Please select an observation/species/custom layer");
			return false;
	}

	divContent.update("Retrieving summary informatiion. Please wait (may take 30-60 seconds)...");

	new Ajax.Request(url,
		{
			method: method,
			parameters: parameters,
			onSuccess: onSuccess
		});
}

function show_stat(oj) {
	var res  = oj.responseText;

	var objDiv = $('tab_summary').update(res);

	// The result is inserted into "stat_result" div element, whose
	// width is defined inline. So need to change it here.
	var div_result = $("stat_result");
	div_result.style.width = "650px";
	div_result.style.height = "280px";
	submitStat();
}

function show_stat2(oj) {
	var res  = oj.responseText;
	$('tab_summary').update(res);
}

function get_symbology() {
	$('SymbolLegend').update("Retrieving symbol informatiion. Please wait...");

	gMapserver.request("get_symbology", show_symbology,
		{
			layer_name: gMapserver.CurrentLayer
		}, "", {});
}

function show_symbology(oj) {
	var res  = oj.responseText;
	$('SymbolLegend').update(res);

	// The current classitem is added in <INPUT type='hidden' id='filter_classitem'>
	// The current layer's ds_type is added in <INPUT type='hidden' id='ds_type'>
	if ($($F('filter_classitem'))) {
		$($F('filter_classitem')).checked = true;
	}

	switch ($F('ds_type')) {
		case "ptobs":
		case "species":
		case "custom":
			$("_series").disabled = true;
			$left_column = gTableCell;
			break;
		case "pttag":
			$("_series").disabled = false;
			$left_column = gTableCell;
			break;
		case "lneff":
		case "lntag":
			$left_column = "none";
			break;
		default:
			$left_column = gTableCell;
	}

	if ($('clustered') && $F('clustered') == "YES") {
		$left_column = "none";
		$('tab_symbology_dist').style.display = "block";
	} else {
		$('tab_symbology_dist').style.display = "none";
	}
	$('tab_symbology_left_column').style.display = $left_column;

	// The current filter items (class names '+' separated by which the layer is filtered)
	// are stored in layer's metadata (FilterItem).
	// The items are spit out in <INPUT type='hidden' id='filter_item'>
	// Each checkbox is given its class name (scientific/shor_name) as name attribute.
	var filter = $F('filter_item');
	filter = filter.split("+");

	$$('#SymbolLegend input[type="checkbox"]').each(
		function (item) {
			if ((filter[0] != 'reverse' && filter.indexOf(item.name) != -1) || (filter[0] == 'reverse' && filter.indexOf(item.name) == -1)) {
				item.checked = false;
			} else {
				item.checked = true;
			}
		}
	);
}

function filter_by_class(checkbox, class_name) {
	var onoff = (checkbox.checked) ? "ON" : "OFF";

	gMapserver.request("filter_by_class", check_error,
		{
			layer_name: gMapserver.CurrentLayer,
			class_name: class_name,
			onoff: onoff
		}, "", {asynchronous: false});
}

function check_error(oj) {
	var res = oj.responseXML;
	var msg = getXmlNodeValue(res, "msg");
	if (msg.indexOf("OK") == -1) {
		alert("error occurred");
		return false;
	}
	gMapserver.RefreshMap = true;
}

function symbology_changed() {
	if (gMapserver.RefreshMap) {
		gMapserver.UpdatingLegend = true;
		gMapserver.request("dummy", refresh_map,
			{}, "", {});
	}
}

function turn_all_symbols_onoff(onoff) {
	var inputs = $$('#SymbolLegend input[type="checkbox"]');
	var item_list = "";
	var delimiter = "";
	inputs.each(function (checkbox) {
		checkbox.checked = onoff;
		item_list += delimiter + checkbox.name;
		delimiter = "+";
	});
	filter_by_class(inputs[0], item_list);
}

function change_color_by(element) {
	var color_by = element.id;
	gMapserver.request("change_color_by", get_symbology,
		{
			layer_name: gMapserver.CurrentLayer,
			color_by: color_by
		}, "", {});

	gMapserver.RefreshMap = true;
}
/***** Layer information popup window end *****/


function delete_layer(layer_name) {
	if (gMapserver.UpdatingLegend) {
		alert("Legend is being updated. Try again");
		return false;
	}
	gMapserver.UpdatingLegend = true;
	gMapserver.request("delete_layer", refresh_map,
		{
			layer_name: layer_name
		}, "", {});

	if (gShapePolygons[layer_name]) {
		gShapePolygons[layer_name].status = "OFF";
	}
}


var gTimer = null;
function showDropdown(e, divName) {
	// For both [Add] and [Tools]
	var Dropdowns = ['Controls', 'ToolsDropDown'];

	for (var i = 0; i < Dropdowns.length; i++) {
		if (Dropdowns[i] == divName) {
			var objDiv = $(divName);
			var mousePos = [];
			mousePos['x'] = Event.pointerX(e);
			mousePos['y'] = Event.pointerY(e);
			objDiv.setStyle(
				{
					position: "absolute",
					left: mousePos['x'] + "px",
					top: mousePos['y'] + "px"
				});
			objDiv.show();
			gTimer = setTimeout('$("' + divName + '").hide()', 5000);
		} else {
			$(Dropdowns[i]).hide();
		}
	}
}

function hideDropdown(divName) {
	$(divName).hide();
	clearTimeout(gTimer);
}

/***** WMS Setting *****/
function showWMSPopupClicked() {
	var url = g_MAPSERVICE_URL + "wms/wms_viewer_gm.php";
	new Ajax.Request(url,
		{
			method: "GET",
			onSuccess: show_wms_popup
		});
}

function show_wms_popup(oj) {
	// Initialize
	layer_name_for_preview = "";	// Global variable defined in wms_viewer_gm.js
	var content = oj.responseText;
	var title = "Add WMS Layer";

	gMapserver.RefreshMap = false;
	var WmsPopup = new PopupWindow("", "", {width: 785 + 'px', height: 620 + 'px'});
	WmsPopup.show(title, content);

	// Register icons so their faces change as mouse moves over.
	$$('#wms_container img.NavTool').each(function (item) {NagivationTools.add(item);});

	wms_initialize();

	var tabs = [
		{name: "wms_overview"},
		{name: "wms_layer"},
		{name: "wms_image"}
	];
	gWmsTabs = new TabGroup(tabs);
	gWmsTabs.switchTo('wms_overview');
}


/***** Env layer Setting *****/
function showEnvLayerPopupClicked() {
	var url = "env_layer_browser.php?";
	var post_data = $H({sid: gMapserver.parameters.sid, mapper: 1}).toQueryString();
	var env_layer_script = 'env_layer_script';
	if (!$(env_layer_script)) {
		var script = document.createElement("script");
		script.type = "text/javascript";
		script.id = env_layer_script;
		script.src = "env_layer_browser.js";
		document.body.appendChild(script);
	}
	new Ajax.Request(url,
		{
			method: "POST",
			parameters: post_data,
			onSuccess: show_env_popup
		});
}

function show_env_popup(oj) {
	// Initialize
	var content = oj.responseText;
	var title = "Add Env. Layer (Beta)";

	gMapserver.RefreshMap = false;
	var EnvPopup = new PopupWindow("", "", {width: 785 + 'px', height: 620 + 'px'});
	EnvPopup.show(title, content);

	// Register icons so their faces change as mouse moves over.
	//$$('#wms_container img.NavTool').each(function (item) {NagivationTools.add(item);});
	/*
	wms_initialize();

	var tabs = [
		{name: "wms_overview"},
		{name: "wms_layer"},
		{name: "wms_image"}
	];
	gWmsTabs = new TabGroup(tabs);
	gWmsTabs.switchTo('wms_overview');
	*/
}


/***** Preferences *****/
function show_preferences() {
	$('map_size_width').value = $('map').getWidth();
	$('map_size_height').value = $('map').getHeight();
	$('preferences_popup').show();
}

function set_preferences() {
	gMapserver.parameters.outputformat = radio_value('form_preferences', 'map_colors');

	if (gMapserver.parameters.outputformat == "PNG24" && is_ie && is_major < 7) {
		gSeamapLayerPNG24 = true;
	} else {
		gSeamapLayerPNG24 = false;
	}

	gSeamapLayerOpacity = $F('gm_opacity');
	gMapserver.request("dummy", refresh_map,
		{}, "", {});

	if (radio_value('form_preferences', 'map_size') == "fixed") {
		gAutoResize = false;
		adjust_map_size($F('map_size_width'), $F('map_size_height'));
		map.checkResize();
	} else {
		gAutoResize = true;
		maximize_map_window();
		map.checkResize();
	}
}

/***** Online Help *****/
function show_help(mode) {
	switch (mode) {
		case "general":
			var url = "http://green.env.duke.edu/help/online_mapping/index_gm.html";
			break;
		case "tasks":
			var url = g_MAPSERVICE_URL + "googlemaps/help/help_tasks.html";
			break;
	}
	window.open(url, "online_help");
	return true;
}

function show_info() {
	var title = "Information";

	var content = $("information").innerHTML;
	gMapserver.RefreshMap = false;
	var InfoPopup = new PopupWindow("", "", {width: 530 + 'px', height: 370 + 'px'});
	InfoPopup.show(title, content);
	return true;
}


/**** Quick search ****/
/*
	Initialize the text as they are different between Species and Dataset search.
*/
var aAutoSuggest = null;

function show_quick_search(search_type) {
	switch (search_type) {
		case "dataset":
			title = "Search and Add Dataset to Map";
			help = "Enter a part of dataset name, Dataset ID or keyword in the box.<BR>";
			help += "Final value to add dataset is Dataset ID, though.";
			var url = g_DATASETS_URL + 'ajax_dataset_search.php';
			var delimiter = "+";
			var parseFunc = parseDataset;
			gSearchType = "dataset";
			break;
		case "species":
			title = "Search and Add Species to Map";
			help = "Enter a part of scientific or common name in the box.<BR>";
			help += "Final value to add species is scientific name, though.";
			var url = g_SPECIES_URL + 'ajax_species_search.php';
			var delimiter = ",";
			var parseFunc = parseSpecies;
			gSearchType = "species";
			break;
	}
	help += ' <a href="javascript: void(0);" onclick="get_help_topic(event, 14);" title="Click to get help."><img src="skins/icons/help.gif"></a>';
	$("quick_search_title").update(title);
	$("quick_search_help").update(help);

	if (aAutoSuggest == null) {
		aAutoSuggest = new AutoSuggest("as_you_type_box", "result_list");
	} else {
		aAutoSuggest.prepare();
	}
	aAutoSuggest.setFilter(url, parseFunc, delimiter);
	$("ajax_search").show();
	aAutoSuggest.setFocus();
}

function submit_from_quick_search() {
	var input = $F('as_you_type_box');
	// The following calls ajax_species_search/ajax_dataset_search.php
	// to validate the entered id (scientific name or dataset id).
	// The returned id, if successful, is set to gValidatedDataid with syncronous AJAX call.
	dataid_validation(input, gSearchType);
	if (gValidatedDataid == "") {
		return false;
	}
	dataid = gValidatedDataid;

	gMapserver.UpdatingLegend = true;
	add_layer(dataid, gSearchType, false);
}

function dataid_validation(input, id_type) {
	input = input.strip();
	input = input.replace(/\r/g,'');
	input = input.replace(/\n/g,',');

	switch (id_type) {
		case "species":
			var url = g_SPECIES_URL + 'ajax_species_search.php';
			var get_params = 'sci=' + input;
			break;
		case "dataset":
			var url = g_DATASETS_URL + 'ajax_dataset_search.php';
			var get_params = 'dataset_id=' + input;
			break;
	}

	url += "?" + get_params;
	new Ajax.Request(url,
		{
			method: 'GET',
			onSuccess: validation_process,
			asynchronous: false
		});
}

function validation_process(oj){
	// dataset and species layers are identified with tsn or dataset id, both are numeric.
	var dataid  = oj.responseText;
	dataid = dataid.strip();
	var RegExp = /^[0-9]{1,}((,|\+)[0-9]+){0,}$/	// Accept comma or plus-separated  numbers only
	if (dataid.match(RegExp) == null || dataid.length <= 0) {
		alert("layer not found or no records for the layer found.");
		gValidatedDataid = "";
		return false;
	} else {
		gValidatedDataid = dataid;
	}
}
/***** Quick Search ends *****/

/***** Advanced search *****/
function advanced_search() {
	switch (gSearchType) {
		case "species":
			var path = g_SPECIES_URL;
			break;
		case "dataset":
			var path = g_DATASETS_URL;
			break;
	}
	var url = path + 'search_gm.php';
	new Ajax.Request(url,
		{
			method: 'GET',
			onSuccess: show_advanced_search
		});
}

function show_advanced_search(oj) {
	var title = "Advanced search";
	var body = oj.responseText;

	gMapserver.RefreshMap = false;
	var SearchPopup = new PopupWindow("advanced_search_popup", "", {width: 810 + 'px', height: 'auto'});
	if ($('search_box_dataset')) {
		var parent_id = 'roi_popup_window';
		var to_close = false;
	} else {
		var parent_id = 'ajax_search';
		var to_close = true;
	}
	SearchPopup.setParent({Id: parent_id, ToClose: to_close});
	SearchPopup.show(title, body);

	var url = g_SPECIES_URL + 'ajax_species_search.php';
	var delimiter = ",";
	var parseFunc = parseSpecies;
	var autoSuggestAdvanced = new AutoSuggest("species_advanced", "result_list");
	autoSuggestAdvanced.setFilter(url, parseFunc, delimiter);

	switch (gSearchType) {
		case "species":
			prev_tsn = "";
			get_taxon_list('class', '174371,179913,173747');
			selectTaxa('phylum', 'all');
			var form_name = 'AdvancedSpeciesSearch';
			break;
		case "dataset":
			selectProvider('All providers', 1);
			var form_name = 'AdvancedDatasetSearch';
			break;
	}

	if (gPolygons.length == 0 && assoc_array_length(gShapePolygons) == 0) {
		var theForm = document.forms[form_name];
		var si_radio = "spatial_interest";
		for (var i = 0; i < 3; i++) {
			theForm[si_radio][i].disabled = true;
		}
	}
}
/***** Advanced ends *****/

function active_tool () {
	return gToolStack[gToolStack.length - 1];
}

/***** Color Picker *****/
function color_picker(e, class_name) {
	var posX = Event.pointerX(e);
	var posY = Event.pointerY(e);
	ColorPicker.setPosition(posX, posY);

	gMapserver.CurrentClass = {
		class_name: class_name
	};
	gMapserver.request("get_class_color", show_class_color,
		{
			layer_name: gMapserver.CurrentLayer,
			class_name: class_name
		}, "", {asynchronous: false});
}

function show_class_color(oj) {
	var color = oj.responseText;
	gMapserver.CurrentClass = $H(gMapserver.CurrentClass).merge({class_color: color}).toObject();

	if (!ColorPicker.created) {
		ColorPicker.create("colorPickerDiv",
			{
				offsetX: 30,
				offsetY: 0,
				onSelect: change_class_color
			}
			);
	}

	ColorPicker.show(color);
}

// When a color is picked with Color Picker, pickColor is fired in which change_class_color is called.
function change_class_color(color) {
	$('colorPickerDiv').hide();
	gMapserver.CurrentClass = $H(gMapserver.CurrentClass).merge({class_color: color}).toObject();

	// If the color is changed in Filter & Symbology popup,
	// issue change_class_color but not refresh the map, then update the symbology.
	if ($('layer_info').getStyle('display') == "block" &&
		$('tab_symbology').getStyle('display') == "block") {
		var onSuccess = get_symbology;
		gMapserver.RefreshMap = true;
	} else {
		gMapserver.UpdatingLegend = true;
		var onSuccess = refresh_map;
	}

	gMapserver.request("change_class_color", onSuccess,
		{
			layer_name: gMapserver.CurrentLayer,
			class_name: gMapserver.CurrentClass.class_name,
			class_color: color
		}, "", {});
}


/***** Save and restore map *****/
function save_current_map() {
	save_map("save_map");
}

// This function is called from sav_current_map and print_map,
// which is distinguished by mode argument.
function save_map (mode) {
	var xml = "<map_info_xml>\n";

	var width = $("map").style.width;
	var height = $("map").style.height;
	xml += "<width>" + width + "</width>\n";
	xml += "<height>" + height + "</height>\n";

	var extent = serialize_extent();
	xml += "<bbox>" + extent + "</bbox>\n";

	var center_zoom = serialize_center_zoom();
	xml += "<centerzoom>" + center_zoom + "</centerzoom>\n";

	xml += "<outputformat>" + gMapserver.parameters.outputformat + "</outputformat>\n";

	var polygon_kml = serialize_polygons();
	xml += "<Placemark>" + polygon_kml + "</Placemark>\n";

	xml += "</map_info_xml>";
	var post_data = "map_info_xml=" + xml;

	gMapserver.request(mode, map_saved,
		{},
		post_data, {});
}

var print_version;
function map_saved(oj) {
	var res = oj.responseXML;

	var mapFileId = getXmlNodeValue(res, "mapFileId");
	switch (gMapserver.parameters.mode) {
		case "save_map":
			var title = "Map saved";
			var body = getXmlNodeValue(res, "msg");
			gMapserver.RefreshMap = false;
			var SavePopup = new PopupWindow("", "", {});
			SavePopup.show(title, body);
			break;
		case "print_map":
			var post_data = $H({sid: gMapserver.parameters.sid, mapfileID: mapFileId}).toQueryString();
			print_version = window.open("seamap_gm_print.php?" + post_data, "print_window");
			if (is_ie)	setTimeout(detect_popupblocker, 1000);
			break;
	}
	$('current_map_id').update(mapFileId);
	$('wms_access_url').update(g_WMS_URL + "wms_gm.php?mapid=" + mapFileId);
}

function detect_popupblocker() {
	if (!print_version) {
		var title = "Popup blocker prevents print window from opening";
		var msg = "<P>Please take a look at <a href='http://green.env.duke.edu/help/online_mapping/print.html#popup_blocker' target='online_help'>Online Help</a> <strong>before allowing popups</strong> so you won't lose your map.</P>";
		gCurrentPopup = new PopupWindow("", "", {width: "500px", height: "300px"});
		gCurrentPopup.show(title, msg);
	}
}

function serialize_extent() {
	var bound = map.getBounds();
	var bbox = bound.getNorthEast().lat() + "," + bound.getNorthEast().lng() + "," + bound.getSouthWest().lat() + "," + bound.getSouthWest().lng();
	return bbox;
}

function serialize_center_zoom() {
	var center = map.getCenter();
	var centerLat = center.lat();
	var centerLng = center.lng();
	var zoom = map.getZoom();
	var center_zoom = centerLat + "," + centerLng + "," + zoom;
	return center_zoom;
}


function serialize_polygons () {
	var polygon_kml = "";
	for (var i = 0; i < gPolygons.length; i++) {
		polygon_kml += "<MultiGeometry>\n<Polygon>\n<outerBoundaryIs><LinearRing><coordinates>";
		var polygon = gPolygons[i];
		var delimiter = "";
		for (var j = 0; j < polygon.getVertexCount(); j++) {
			var aPoint = polygon.getVertex(j);
			polygon_kml += delimiter + aPoint.lng() + "," + aPoint.lat() + ",0.0";
			delimiter = " ";	// a space
		}

		polygon_kml += "</coordinates></LinearRing></outerBoundaryIs>\n</Polygon>\n</MultiGeometry>\n"
	}

	return polygon_kml;
}

function restore_map() {
	var title = "Restore Map";
	var body = '<P>Enter your Map ID to restore.</P>';
	body += '<form name="restore_map_form">Map ID: <input type="text" name="MapID" id="MapID">&nbsp;&nbsp;';
	body += '<input type="button" value="OK" class="context" onclick="restore_map_run($F(MapID));">';
	body += '</form><BR>';

	gCurrentPopup = new PopupWindow("", "", {});
	gCurrentPopup.show(title, body);
}

function restore_map_run (MapID) {
	var RegExp = /^[0-9]{1,}(-)[0-9]{1,}$/;
	if (MapID.match(RegExp) == null || MapID.length <= 0) {
		alert("Invalid MapID (" + MapID + ") was passed.");
		return false;
	}
	gMapserver.UpdatingLegend = true;
	gPolygons = [];
	gNumLayerAdded = 0;

	gMapserver.request("restore_map", refresh_map,
		{
			mapfileID: MapID
		}, "", {});

	gCurrentPopup.close();
	gCurrentPopup = null;

	$('current_map_id').update(MapID);
	$('wms_access_url').update(g_WMS_URL + "wms_gm.php?mapid=" + MapID);
}


/***** Print map *****/
function print_map () {
	// Save map information on server side.
	// save_map fires map_saved in which the print version is opened.

	save_map("print_map");
}

/***** provider list *****/
function get_provider_list () {
	gMapserver.UpdatingLegend = false;
	gMapserver.request("provider_list", show_provider_list,
		{render: 'html'}, "", {});

}

function show_provider_list(oj) {
	var msg = oj.responseText;
	var title = "Provider List";
	gCurrentPopup = new PopupWindow("", "", {width: "650px", height: "500px"});
	gCurrentPopup.show(title, msg);
}
/***** Popup window *****/
var PopupWindow = Class.create();

PopupWindow.prototype = {
	initialize: function (id, class_name, style) {
		this.FrameClass = "quick_search_box";
		this.AutoAdjust = true;
		this.FrameStyle = {
			top: 55 + "px",
			left: 250 + "px",
			width: 560 + "px"
		};
		this.Id = "";
		this.Parent = {Id: "", ToClose: true};

		this.CloseClass = "popup_close_button";
		if (id != "") {
			this.Id = id;
		}
		if (class_name != "") {
			this.FrameClass = class_name;
		}
		this.FrameStyle = $H(this.FrameStyle).merge(style).toObject();
	},

	setParent: function (parent) {
		this.Parent.Id = parent.Id;
		this.Parent.ToClose = parent.ToClose;
	},

	show: function(title, body, effect) {
		if (this.Parent.Id != "") {
			if (this.Parent.ToClose) {
				new Effect.Fade(this.Parent.Id);
			} else {
				new Effect.Opacity(this.Parent.Id, {duration:0.5, from:1.0, to:0.5});
			}
		}

		var frame_div = $(document.createElement("DIV"));
		if (this.Id != "") {
			frame_div.id = this.Id;
		}
		frame_div.addClassName(this.FrameClass);
		if (this.AutoAdjust) {
			this.adjustPosition();
		}
		frame_div.setStyle(this.FrameStyle);

		var title_h2 = $(document.createElement("H2"));
		title_h2.addClassName("popup_title");
		title_h2.update(title);

		var close_div = $(document.createElement("DIV"));
		close_div.addClassName(this.CloseClass);
		close_div.setStyle({width: this.FrameStyle.width});

		var close_a = $(document.createElement("A"));
		close_a.href = "javascript: void(0)";
		close_a.update('<img src="skins/icons/icon_close_popup.gif">');
		close_a.observe('click', this.close.bindAsEventListener(this));
		close_div.appendChild(close_a);

		var body_div = $(document.createElement("DIV"));
		body_div.update(body);

		frame_div.appendChild(title_h2);
		frame_div.appendChild(close_div);
		frame_div.appendChild(body_div);

		document.getElementsByTagName("BODY")[0].appendChild(frame_div);

		if (effect) {
			new Effect.Appear(frame_div, {duration:1.0});
		} else {
			frame_div.show();
		}

		this.PopupDiv = frame_div;
		return this.Id;
	},

	close: function () {
		if (gMapserver.RefreshMap) {
			gMapserver.UpdatingLegend = true;
			gMapserver.request("dummy", refresh_map,
				{}, "", {});
		}

		$('result_list', this.PopupDiv).invoke('hide');

		if (this.Parent.Id != "") {
			if (!this.Parent.ToClose) {
				new Effect.Opacity(this.Parent.Id, {duration:0.5, from:0.5, to:1.0});
			}
		}
		this.destroy();
	},

	destroy: function () {
		this.PopupDiv.remove();
	},

	adjustPosition: function () {
		// Adjust top and left so that the popup window stays in the visible area of the browser.
		var style = this.FrameStyle;
		if (style.height && !isNaN(parseInt(style.height))) {
			var height = parseInt(style.height);
		} else {
			var height = 0;
		}
		var top = Math.max(0, Math.min(parseInt(style.top), gWinH - height));
		var left = Math.max(0, Math.min(parseInt(style.left), gWinW - parseInt(style.width) - 50));	// 50 is a arbitrary buffer
		this.FrameStyle = $H(this.FrameStyle).merge({top: top + "px", left: left + "px"}).toObject();
	}
}

/***** Tab group *****/
var TabGroup = Class.create();

TabGroup.prototype = {
	Tabs: [],	// [{name:"", onclick: func}, {},...]
	ActiveTab: "",

	initialize: function (Tabs) {
		this.Tabs = Tabs;
		Tabs.each(function (tab) {
			this.addTab(tab.name);}.bind(this));
	},

	addTab: function (tab_name) {
		var img = $("img_" + tab_name);	// Handle is supposed to be an <IMG>
		img.observe('click', this.clickHandler.bindAsEventListener(this, tab_name));
		img.observe('mouseover', this.mouseoverHandler.bindAsEventListener(this, tab_name));
		img.observe('mouseout', this.mouseoutHandler.bindAsEventListener(this, tab_name));
	},

	mouseoverHandler: function (event, tab_name) {
		var img = $("img_" + tab_name);
		if(this.ActiveTab != tab_name) {
			img.src='skins/icons/icon_' + tab_name + '_hover.png';
		}
	},

	clickHandler: function(event, tab_name) {
		this.switchTo(tab_name);
	},

	mouseoutHandler: function (event, tab_name) {
		var img = $("img_" + tab_name);
		if(this.ActiveTab != tab_name) {
			img.src='skins/icons/icon_' + tab_name + '.png';
		}
	},

	switchTo: function (tab_name) {
		this.Tabs.each(function(tab) {
			var div = $(tab.name);
			if (tab.name == tab_name) {
				div.show();
				this.ActiveTab = tab_name;
				$("img_" + tab_name).src = "skins/icons/icon_" + tab_name + "_selected.png";
				if(tab.onclick) {
					tab.onclick();
				}
			} else {
				div.hide();
				$("img_" + tab.name).src = "skins/icons/icon_" + tab.name + ".png";
			}
		}.bind(this));
	}
}

/***** set_dataset_tooltip *****/
// This function is called within Identify popup
function get_dataset_tooltip (aTag, dataid) {
	if (aTag.title == "") {
		gMapserver.request("get_dataset_name", set_dataset_tooltip,
			{
				dataid: dataid
			}, "", {asynchronous: false});
		aTag.title = gMapserver.DatasetName;
	}
}

function set_dataset_tooltip(oj) {
	gMapserver.DatasetName = oj.responseText;
}

/**** Help topic *****/
var HelpPopup = null;
var AutoCloseHelpPopup = null;
function get_help_topic(event, topic_id) {
	var url = g_MAPSERVICE_URL + "googlemaps/help/topic" + topic_id + ".html";
	var posX = Event.pointerX(event);
	var posY = Event.pointerY(event);
	HelpPopup = new PopupWindow("help_topic", "",
		{
			width: 400 + "px",
			height: "auto",
			top: posY + "px",
			left: posX + "px"});
	new Ajax.Request(url,
		{
			method: 'GET',
			onSuccess: show_help_topic,
			onFailure: function (){alert("Can't get help topic" + topic_id + ".");}
		});
}

function show_help_topic(oj) {
	gMapserver.RefreshMap = false;
	HelpPopup.show("Help Topic", oj.responseText, true);
}


/***** Utilities *****/
function radio_value(form_name, radio_name) {
	var value = Form.getInputs(form_name,'radio',radio_name).find(function(radio) { return radio.checked; }).value;

	return value;
}

function authenticate(user_name, password) {
	gMapserver.Authenticated = false;
	var url = g_ROOT_URL + BASE_URL + "services/ldap_tools.php";
	new Ajax.Request(url,
		{
		parameters: "uid=" + user_name + "&password=" + password,
			onSuccess: authenticated,
			asynchronous: false,
			onFailure: function (){alert("Fail to call for authentication.");}
		});

	return gMapserver.Authenticated;
}

function authenticated(oj) {
	var res = oj.responseXML;

	if (getXmlNodeValue(res, "authenticated") == "OK") {
		$('debug_info').update(getXmlNodeValue(res, "dn"));
		gMapserver.Authenticated = true;
	} else {
		gMapserver.Authenticated = false;
	}
}

function assoc_array_length(assoc_array) {
	var i = 0;
	for (var key in assoc_array) {
		if (typeof(assoc_array[key]) == "object" && assoc_array[key].status == "ON") {
			i++;
		}
	}
	return i;
}

/***** Routines for distribution map (ESAS 427) *****/
function set_species_to_dist_layer (checkbox, sp_tsn) {
	var onoff = (checkbox.checked) ? "ON" : "OFF";

	if ($$('#SymbolLegend input[type="checkbox"]').any(
		function (item) {
			return item.checked;
		})) {
		$$('input.radio_dist_or_obs').each(function(item) {item.disabled = false;});
		var force_to_dist = false;
		var async = true;
	} else {
		$$('input.radio_dist_or_obs').each(function(item) {
			item.disabled = true;
			if (item.value == 'dist') {
				item.checked = true;
			} else {
				item.checked = false;
			}});
		var force_to_dist = true;
		var async = false;
	}

	var zoom = map.getZoom();
	gMapserver.UpdatingLegend = false;
	gMapserver.request("set_species_to_dist_layer", check_error,
		{
			layer_name: gMapserver.CurrentLayer,
			sp_tsn: sp_tsn,
			onoff: onoff,
			zoom: zoom
		}, "", {asynchronous: async});

	if (force_to_dist) {
		switch_dist_obs("dist");
	}
}

/***** When the user clicks on the species in the popup... (for ESAS) *****/
function species_observed(tsn) {
	if (tsn != "") {
		var parameters = $H({sp_tsn: tsn, columns: 'sp_tsn,scientific_name,common_name', jason:1}).toQueryString();
		var url = "/plone_dev/datasets/getSpeciesProfile?" + parameters;
		new Ajax.Request(url,
			{
				method: 'GET',
				onSuccess: update_taxa_infowindow
			});
	} else {
		var s = alert("No species observed in this cell.");
	}
}

function update_taxa_infowindow(oj) {
	var profile_json = oj.responseText;
	eval(profile_json);		// return species_profile variable
	s = "";
	for (var i = 0; i < species_profile.length; i++) {
		if (species_profile[i].common_name != "") {
			var common_name = " (" + species_profile[i].common_name + ")";
		} else {
			var common_name = "";
		}
		s += "<a href='javascript:void(0);' onclick='choose_species(\"" + species_profile[i].sp_tsn + "\")';>" + species_profile[i].scientific_name + "</a>" + common_name + "<BR>";
	}
	s = "<div style='height:180px; overflow:auto;'>" + s + "</div>";
	var infowindow = map.getInfoWindow();
	map.openInfoWindowHtml(infowindow.getPoint(), s, {maxHeight: 200, maxWidth:400});
}

function choose_species(sp_tsn) {
	var zoom = map.getZoom();
	gMapserver.UpdatingLegend = false;
	gMapserver.request("set_species_to_dist_layer", refresh_map,
		{
			layer_name: "",		// No layer can be selected. The PHP script tries to find the first clustered layer.
			sp_tsn: sp_tsn,
			onoff: "ON",
			zoom: zoom
		}, "", {});
	map.closeInfoWindow();
	$$('input.radio_dist_or_obs').each(function(item) {item.disabled = false;});
}

function switch_dist_obs(which) {
	gMapserver.RefreshMap = true;
	gMapserver.request("switch_dist_obs", check_error,
		{
			layer_name: gMapserver.CurrentLayer,
			dist_or_obs: which
		}, "", {});
}

function choose_effort_period(checkbox, period) {
	var onoff = (checkbox.checked) ? "ON" : "OFF";
	gMapserver.RefreshMap = true;
	gMapserver.request("choose_effort_period", check_error,
		{
			layer_name: gMapserver.CurrentLayer,
			period: period,
			onoff: onoff
		}, "", {});
}