/***** Global variables *****/
var DEV_HOST = "seamap-dev.env.duke.edu";

if (window.location.hostname == DEV_HOST) {
	var DEV = true;
	var BASE_URL = "seamap2.5/";
	var SPECIES_PAGE = "/species/{0}";
	var DATASET_PAGE = "/dataset/{0}";
} else {
	var DEV = false;
	var BASE_URL = "seamap2.5/";
	var SPECIES_PAGE = "/species/{0}";
	var DATASET_PAGE = "/dataset/{0}";
}

var ROOT_URL = "http://" + window.location.hostname + "/";
var PORTAL_MAIN = "main/seamap_main.php";
var DOWNLOAD = ROOT_URL + BASE_URL + "main/download.php";

// Map variables
var map = null;
var gMapserver = null;
var gUrlPhp = ROOT_URL + BASE_URL + "main/seamap_gm.php";

var gLegendItems = null;

//var gAgreedToTermsForDownload = false;

// [sort_index, layer_name, layer_title, source]
var layers_info = [
	// rangemaps, if exists (returned by species_profile SQL), and obis_poitns are inserted in show_species_info
	// as they are for species only.
	//[1, "rangemaps", "Species Range Map", ""],
	//[2, "obis_points", "OBIS", "Records from OBIS (minus those from OBIS-SEAMAP)"],
	//[3, "eez", "200 nautical mile buffers from coasts", "World Maritime Boundaries v2"],
	[3, "eez", "Exclusive Economic Zone (EEZ)", "World Maritime Boundaries v2"],
	[4, "lme", "Large Marine Ecosystems (LME)", "Large Marine Ecosystems"],
	[5, "meow", "Marine Ecoregions (MEOW)", "Marine Ecoregions"],
	[6, "wdpa", "World Database of Protected Areas", "UNEP-WCMC. 2009. World Database on Protected Areas Annual Release 2009"]
];


// Google Maps initial center location and zoom level.
// Can be changed for each page.
var gInitLoc = {latitude: 10, longitude: 0, zoom: 1}; 	
var gIconBase = "/" + BASE_URL + "icons/";

var myTileOverlay;
var gSeamapLayerOpacity = 1.0;
var gCell = null;
var gMaxDim = {maxWidth:320, maxHeight: 170};
var gDownloading = false;

var gROIs;		// SEAMAP2.5 Instance of new SeamapRoi object



var gClient = {
	layer_status: {dist_taxa: -1, dist_sp: -1, points: -1, tracks: -1, env_layer: -1, bath: -1},	// -1=>hidden; 0=>visible; 1=>visible and dirty
	overlays: {},	// uploaded KML layers
	reclassify: {dist_taxa: true, dist_sp: true, points: true,  env_layer: true},
	update_legend: false,
	views: [],
	graph_page_size: 30,
	slider_value: 0,
	client_height: 1200,	// default. will be updated in onReady() based on the browser height.
	refleshCriteriaView: true,
	zoom_levels: {"1": 0, "0.1": 5, "0.01": 9},
	// If you change these bin sizes, you also need to change $zoom_levels in seamap_main.php (case "graph_oceano_histogram")
	histogram_zoom_levels: {sst:[10, 5, 3, 2, 1], ssc:[100, 50, 30, 10, 5], ssh:[20, 15, 10, 5, 3], bath:[500, 300, 200, 100, 50]},
	current_tool: {tool: "", handler: null},
	controls: {
		pan: {activate: function(){}, deactivate: function(){}},
		zoom: {activate: activate_zoomin, deactivate: deactivate_zoomin},
		identify: {activate: activate_identify, deactivate: deactivate_identify},
		rectangle: {activate: activate_draw_rect, deactivate: deactivate_draw_rect},
		polygon: {activate: activate_draw_polygon, deactivate: deactivate_draw_polygon},
		remove: {activate: function(){}, deactivate: function(){}}
	},
	choose_taxa_sp: function() {
		if (gQuery.isDefault()) {
			this.layer_status.dist_taxa = 1;
			this.layer_status.dist_sp = -1;
		} else {
			this.layer_status.dist_taxa = -1;
			this.layer_status.dist_sp = 1;
		}		
	},
	choose_layers: function (zoom) {
		var layers = [];
		var zoom_levels = this.zoom_levels;
		if ((page_type == "front_page" || page_type == "search") && this.layer_status.points == -1) {
			this.choose_taxa_sp();
		}
		$H(this.layer_status).each(function(pair){
			if (pair.value > -1) {
				if (pair.key == "dist_sp" || pair.key == "dist_taxa") {
					var auto_resolution = radio_value("resolution_selector");
					auto_resolution = (auto_resolution == null || auto_resolution == "auto");
					if (auto_resolution) {
						if (zoom >= zoom_levels['0.01']) {
							gClient.slider_value = -2;
						} else if (zoom >= zoom_levels['0.1']) {
							gClient.slider_value = -1;
						} else {
							gClient.slider_value = 0;
						}
						
						/* Mathematical calculation of slider_value looks elegant but not intuitive and maybe slower than the above codes.
						var levels = $H(zoom_levels).keys().reverse();
						var slider_value = parseFloat(levels.find(function(item){return zoom_levels[item] <= zoom}));
						slider_value = Math.round(Math.log(slider_value) / Math.log(10));
						*/
						if (typeof(Ext.getCmp("slider_resolutions")) != "undefined") {
							var resolution_bar = Ext.getCmp("slider_resolutions");
							resolution_bar.suspendEvents(false);
							resolution_bar.setValue(gClient.slider_value);
							resolution_bar.resumeEvents(false);
						}
					} else {
						/*
						if (typeof(Ext.getCmp("slider_resolutions")) != "undefined") {
							var multi = Ext.getCmp("slider_resolutions").getValue() == 1;
						} else {
							var multi = false;
						}
						if (!multi) {
							if (typeof(Ext.getCmp("slider_resolutions")) != "undefined") {
								var slider_value = Ext.getCmp("slider_resolutions").getValue();	// [-2|-1|0]
							} else {
								var slider_value = 0;
							}
						} else {
							var slider_value = 1;
						}
						*/
						if (typeof(Ext.getCmp("slider_resolutions")) != "undefined") {
							gClient.slider_value = Ext.getCmp("slider_resolutions").getValue();	// [-2|-1|0]
						} else {
							gClient.slider_value = 0;
						}
					}
					var resolution = Math.pow(10, gClient.slider_value).toFixed(2);	// [-2|-1|0] => [0.01|0.1|1]
					//var cellsizes = {"0.01": "001", "0.10": "01", "1.00": "1", "10.00": "multi"};
					var cellsizes = {"0.01": "001", "0.10": "01", "1.00": "1"};
					var cellsize = cellsizes[resolution.toString()];
					var layer_name = pair.key + "_" + cellsize + "deg";
				} else {
					var layer_name = pair.key;
				}
				layers.push(layer_name);
			}
		});
		return layers.join(",");
	},
	
	registerView: function(id) {
		this.views.push(id);
	},
	
	dataDirty: function(do_not_load) {
		var info_tabs = Ext.getCmp("info_tabs");
		var active_tab = info_tabs.getActiveTab().title;
		this.views.each(function(item){
			var panel = Ext.getCmp(item);	// can be linechart
			if (!(typeof(do_not_load) == "string" && do_not_load == item)) {
				panel.store.dirty = true;
				if(active_tab == panel.ownerCt.title) {
					panel.store.load();
				}
			}
		});
		
		if (!gQuery.isDefault()) {
			store_summary.load();
		} else {
			// just copy the default values when no criteria are set.
			switch (page_type) {
				case "dataset":
					var dataset = gQuery.datasets[0];
					var counts = {num_records: dataset.num_records, num_animals: dataset.animal_count, num_species: dataset.species_count};
					var on_map = "sp_tsn_on_map";
					break;
				case "species":
					var species = gQuery.species[0];
					var counts = {num_records: species.records, num_animals: species.animal_count, num_datasets: species.datasets};
					var on_map = "dataset_on_map";
					break;
				case "search":
					var counts = gQuery.seamap_summary;
					var on_map = "dummy";
					break;
			}
			records = [];
			counts[on_map] = "";
			records.push({data: counts});
			summary_loaded(null, records, null);
		}
		
		// If env layer is on, should make sure the layer is found for the specified time range.
		// env_layer_updated checks the status and turn the env layer off if the status = 0.
		// It calls update_map whether the env layer is found or not.
		var handler = gQuery.oceano.sync == "sync" ? env_layer_updated : update_map;
		var reclassify = (page_type == "search" && this.layer_status.points > -1 && (typeof(do_not_load) == "string" && do_not_load.indexOf(gQuery.color_by) == -1)) ? 1 : 0;
		update_layers({reclassify: reclassify}, handler);
		
		// [Clear all] sets refleshCriteriaView to false while looping to prevent grid_criteria 
		// from being refleshed while each criterion is being cleared.
		if (this.refleshCriteriaView) {
			this.updateCriteriaView();
		}
	},

	updateCriteriaView: function() {
		var grid_criteria_species = Ext.getCmp("grid_criteria_species");
		var store = grid_criteria_species.store;
		var div_empty = "<div class='criteria_empty_cell'>{0}</div>";
		store.removeAll();
		if (gQuery.species.length > 0 && gQuery.species[0].sp_tsn != 0) {
			store.loadData({num_records:gQuery.species.length, records:gQuery.species});
		} else {
			store.loadData({num_records:1, records:[{sp_tsn:0, scientific_name:String.format(div_empty, rs_win_options.species.empty_cell)}]}, true);
		}
		
		var grid_criteria_datasets = Ext.getCmp("grid_criteria_datasets");
		var store = grid_criteria_datasets.store;
		store.removeAll();
		if (gQuery.datasets.length > 0) {
			store.loadData({num_records:gQuery.datasets.length, records:gQuery.datasets});
		} else {
			store.loadData({num_records:1, records:[{dataset_id:0, dataset_name:String.format(div_empty, rs_win_options.datasets.empty_cell)}]}, true);
		}
		
		// ROIs
		var rois = gROIs.rois;
		var grid_criteria_resources = Ext.getCmp("grid_criteria_region");
		var store = grid_criteria_resources.store;
		store.removeAll();
		var div = $("div_advanced_region_rectangle");
		var grid_editor = Ext.getCmp("grid_roi_editor");
		if (rois.length > 0) {
			var roi_index = 0;
			var rs = rs_win_map.tbar;
			var roi_names = {rectangle: rs.draw_regular.text, polygon: rs.draw_polygon.text};
			rois.each(function(roi){
				switch (roi.mode) {
					case "zone":
						store.loadData([[roi_index++, String.format(div_empty, rs_win_options.region.empty_cell), ""]], true);
						break;
					case "shapefile":
						store.loadData([[roi_index++, roi.layer_name, roi.geometry.fill, roi.mode]], true);
						break;
					default:
						store.loadData([[roi_index++, roi_names[roi.mode], roi.geometry.fill, roi.mode]], true);
				}
			});
		}
		store.loadData([[-1, String.format(div_empty, rs_win_options.region.empty_cell), "", "rectangle"]], true);
		store.loadData([[-2, String.format(div_empty, rs_win_options.region.upload), "", "upload"]], true);
		div.hide();
		grid_editor.hide();			
		
		/*
		var grid_criteria_dates = Ext.getCmp("grid_criteria_dates");
		if (grid_criteria_dates && grid_criteria_dates.rendered) {
			var store = grid_criteria_dates.store;
			var row = grid_criteria_dates.store.getAt(0);
			row.set("date_from", gQuery.dates.date_from != "" ? Date.parseDate(gQuery.dates.date_from, "Y-m-d").format(rs_win_options.temporal.date_format) : "");
			row.set("date_to", gQuery.dates.date_to != "" ? Date.parseDate(gQuery.dates.date_to, "Y-m-d").format(rs_win_options.temporal.date_format) : "");
			row.commit();
			if (grid_criteria_dates.rendered) {
				grid_criteria_dates.getView().refresh();
			}
		}
					
		var grid_criteria_months = Ext.getCmp("grid_criteria_months");
		var store = grid_criteria_months.store;
		store.removeAll();
		if (gQuery.months.length > 0) {
			store.loadData([[1, rs_win_options.temporal.months, gQuery.months.join(", ")]]);
		} else {
			store.loadData([[0, rs_win_options.temporal.months, "<div class='criteria_empty_cell'>" + rs_win_options.temporal.empty_cell + "</div>"]]);
		}
		if ($("div_months").visible()) {
			gQuery.months.each(function (month){
				$("checkbox_" + gClient.month_box[month - 1]).checked = true;
			});		
		}
		*/
	}	
}


var gQuery = {
	options: {name_search: "begin_with", search_for: "scientific", temporal_level: "year", include_children: true},
	species: [],
	sp_obs: [],
	protected_status: [],	// SEAMAP2.5
	datasets: [],	// List of dataset IDs comma-separated, returned by internal queries
	ds_type_platform: "",
	series: [],
	spatial: "",
	zones: {eez: [], lme: [], meow: [], wdpa: []},		// eez=[{eez_id: 100, label: "xxx"}, {eez_id: 101, label: "yyy"}]
	temporal: {start: "", end: ""},
	temporal_scale: 2,		// SEAMAP2.5 Century:0, Decade:1, Year:2, Month:3, Day:4; Season:-1; seasonal month:-2
	oceano: {env_type: "N", sync: "specific", date_start: "", update: false},
	publish: "publish",
	count_type: "record",	// [animal|record|species]
	color_by: "species",	// initialized in init()
	layers: "",						// active layers. Should be set before calling toQueryObject/toQueryString using gClient.choose_layers()
	
	toQueryObject: function() {		// In SEAMAP2.5, always send out the same query parameters
		var copy = ["layers", "count_type", "spatial", "temporal_scale", "publish", "color_by"];
		var params = {page_type: page_type};
		Ext.copyTo(params, this, copy);
		
		if (this.species.length > 0 && this.species[0].sp_tsn != 0) {		
			params.species = Ext.encode(this.species);
		}
		
		if (this.protected_status.length > 0) {
			params.protected_status = Ext.encode(this.protected_status);
		}
		
		if (this.datasets.length > 0) {
			var copy = ["dataset_id", "ds_type", "platform"];
			params.datasets = Ext.encode(this.datasets.collect(function(item){var params = {}; return Ext.copyTo(params, item, copy)}));
		} 
		if (this.ds_type_platform != "") {
			params.ds_type_platform = this.ds_type_platform;
		} 
		
		if (this.series.length > 0) {
			var series_quoted = this.series.pluck("series").collect(function(item){return "'" + item + "'"});
			var series = series_quoted.join(",");
			params.series = series;
		} 
		
		if (page_type == "dataset" && this.datasets.length > 0 && this.datasets[0].table2 != "" && this.datasets[0].auto == "t") {
			params.tracks = this.datasets[0].table2.replace("zd_", "");
		}
		
		if (page_type == "photoid" && typeof(photoid_options) == "function") {
			photoid_options(params);
		}
		
		["eez", "lme", "meow", "wdpa"].each(function(region){
			if (gQuery.zones[region].length > 0) {
				params[region + "_id"] = gQuery.zones[region].pluck("zone_id").join(",");
			}
		});
		
		if (this.oceano.update && this.oceano.env_type != "N" && this.oceano.sync == "sync" && this.oceano.date_start != "") {
			params.env_type = this.oceano.env_type;
			params.env_date_start = this.temporal.start;
		}
		
		if (this.temporal.start !== "" || this.temporal.end !== "") {
			var temporal = Ext.encode(this.temporal);
		} else {
			var temporal = "";
		}
		var params = Ext.apply(params, {zoom: map ? map.getZoom() : 1, temporal: temporal});
		return params;		
	},
	
	toQueryString: function() {
		var query_object = this.toQueryObject();
		var s = $H(query_object).toQueryString();
		return s;
	},
	
	clearTemporal: function() {
		this.temporal = {start: "", end: ""};
	},
	
	isDefault: function(omit_species) {
		var query = this.toQueryObject();
		var params = [];
		var evaluate_species = false;
		switch (page_type) {
			case "dataset":
				if (typeof(omit_species) == "undefined" || !omit_species) {
					params.push(query.species);
				}
				params.push(query.series);
				break;
			case "species":
				params.push(query.datasets);
				break;
			case "search":
			case "front_page":
				//params.push(query.species); 	// species criteria is evaluated later
				params.push(query.datasets);
				params.push(query.protected_status);
				params.push(query.ds_type_platform);
				var evaluate_species = true;
				break;
		}
		params.push(query.spatial);
		params.push(query.eez_id);
		params.push(query.lme_id);
		params.push(query.meow_id);
		params.push($H(gQuery.temporal).values().join(""));
		if (params.join("") == "") {
			if (evaluate_species) {
				// on Search/Front page, when species is the only criterion and it's either mammalia, aves or reptilia,
				// use dist_taxa_xxx instead of dist_sp
				if (gQuery.species.length == 0) {
					return true;
				} else if (gQuery.species.length == 1 && gQuery.species[0].sp_rank == "Class") {
					return true;
				} else {
					return false;
				}
			} else {
				return true;
			}
		} else {
			return false;
		}
	},
	
	useZ: function() {	// To use z_union for queries (when series is selected on Dataset Page etc.)
		var z = false;
		if (gClient.layer_status.points >= 0) {
			return true;
		}
		if (this.series.length > 0) {
			return true;
		}
		/* why this was added?
		if (map.getZoom() >= gClient.zoom_levels["001"]) {
			return true;
		}
		*/
	}
}

/**** initialize page *****/
/* Initialize the page:
	Collect all necessary info needed to prepare the page: ds_type, platform, initial layer etc.
	Initialize mapfile.
	Call initialize_page()
*/
function init() {
	// Dataset page shows Terms of Use on page load. When agreed, it's saved in a cookie (session level).
	var cp = new Ext.state.CookieProvider({
		path: "/",
		expires: null, // At the end of session
		domain: ROOT_URL.replace("http://", "")
	});
	Ext.state.Manager.setProvider(cp);
   	
	var user_interface = {menu_id: "map_menu"};
	gROIs = new SeamapRoi(user_interface);
	
	// Initialize Mapserver instance
	gMapserver = new Mapserver(gUrlPhp, "");
	var mode = "initialize";
	var params = {page_type: page_type, publish: gQuery.publish, reclassify: 1};
	switch (page_type) {
		case "dataset":
		case "photoid":
			params.dataset_id = dataset_id;
			break;
		case "species":
			params.sp_tsn = sp_tsn;
			break;
		default:
	}
	var handler = initialized;
	gMapserver.request(mode, handler, params, "", {});		
}

function initialized(oj) {
	var return_value = oj.responseText.evalJSON();
	// When invalid dataset id or tsn is specified, seamap_gm.php returns sid=-1.
	if (return_value.sid == -1) {
		Ext.get("client_north").removeClass("x-hidden");
		$("body_frame").update("<span style='color:#FFFFFF'>" + return_value.error + "</span>");
		return true;
	}
	gMapserver.parameters.sid = return_value.sid;
	
	var doc_title = "OBIS-SEAMAP ";
	switch (page_type) {
		case "front_page":
			gQuery.seamap_summary = return_value.front_page;
			var counts = return_value.front_page;
			doc_title += "- Explore Marine Megavertebrates";
			break;
		case "search":
			gQuery.seamap_summary = return_value.search;
			var counts = return_value.search;
			doc_title += "Data Search";
			break;
		case "dataset":
		case "photoid":
			var dataset = return_value[page_type];
			gQuery.datasets.push(dataset);
			var counts = {num_records: dataset.num_records, num_animals: dataset.animal_count, num_species: dataset.species_count};
			doc_title += page_type == "dataset" ? "Dataset - " + dataset.dataset_name : title;
			break;
		case "species":
			var species = return_value[page_type];
			gQuery.species.push(species);
			var counts = {num_records: species.records, num_animals: species.animal_count, num_datasets: species.datasets};
			doc_title += "Species Profile - " + species.scientific_name;
			// Species rangemap info
			if (species.shape_name != null && species.shape_name != "") {
				var layer_info = [1, "rangemaps", "Species Range Map", species.source];
				layers_info.push(layer_info);
				var layer_info = [2, "obis_points", "OBIS", "Records from OBIS (minus those from OBIS-SEAMAP)"];
				layers_info.push(layer_info);
			}	
			break;
	}
	
	var layers = return_value.init_layer.split(",");	// can be two: points,tracks
	layers.each(function(item){
		gClient.layer_status[item] = 1;
		gClient.reclassify[item] = false;
	});
	gQuery.color_by = return_value.color_by;
	
	update_on_the_map(counts);	

	// Modify layers (add supplemental and link to upload window.
	var index = layers_info[layers_info.length - 1][0];
	if (typeof return_value.supplemental != "undefined" && return_value.supplemental != "") {
		var supplemental = return_value.supplemental;
		supplemental.each(function(item){
			gClient.layer_status[item.name] = parseInt(item.init_status);
			if (parseInt(item.list) == 1) {
				layers_info.push([++index, item.name, item.title, item.wms_abstract]);
			}
		});
	}
	
	document.title = doc_title;
	initialize_page();
	load_map();	
}

// SEAMAP 2.5 - update [on the Map] table under [Summary]
function update_on_the_map(counts) {
	// counts should include obs_count/animal_count/species_count or dataset_count
	$H(counts).each(function(pair){
		if ($(pair.key)) {
			$(pair.key).update(int_format(pair.value));
		}
	});	
}

function load_map() {
	if (GBrowserIsCompatible()) {
		map = new GMap2($("map"), {draggableCursor:"default", backgroundColor: '#1D1C56'});
		map.addControl(new GLargeMapControl());
		//map.addControl(new GHierarchicalMapTypeControl());
		map.addControl(new GMapTypeControl());

		map.setCenter(new GLatLng(gInitLoc["latitude"], gInitLoc["longitude"]), gInitLoc["zoom"], G_SATELLITE_MAP);

		var copyright = new GCopyright(1, new GLatLngBounds(new GLatLng(-85,-180),new GLatLng(85,180) ), 1, "OBIS-SEAMAP");
		var copyrightCollection = new GCopyrightCollection('Distribution map by ');
		copyrightCollection.addCopyright(copyright);

		var myTileLayer = new GTileLayer(copyrightCollection, 1, 16);
		myTileLayer.getTileUrl = myGetTileUrl;
		myTileLayer.getOpacity = function() { return gSeamapLayerOpacity; }
		myTileLayer.isPng = function() { return false; }	// Somehow IE6 doesn't show overlays in PNG if there is a polygon.

		myTileOverlay = new GTileLayerOverlay(myTileLayer);
		map.addOverlay(myTileOverlay);

		GEvent.addListener(map, "infowindowclose", erase_cell);
		GEvent.addListener(map, 'zoomend', on_zoomend_update_legend);
		if ($('current_position')) {
			GEvent.addListener(map, 'mousemove', on_mousemove_coordinate);
		}
		
		if (typeof(map_loaded) == 'function') {
			map_loaded();
		}
	}
}

function map_loaded() {
	if (page_type == "front_page") {
		//if (Ext.isIE8) {
		if (!Ext.isGecko) {
			window_resized();
		}
		activate_identify();
		get_legend();
		
		if (init_window == "datasets") {
			build_dataset_search();
			toggle_button($("btn_browse_" + init_window), init_window);
		}
		if (init_window == "species") {
			build_species_search();
			toggle_button($("btn_browse_" + init_window), init_window);
		}
	} else {
		if (Ext.getCmp("btn_options")) {
			new Ext.util.DelayedTask(function(){Ext.getCmp("btn_options").toggle(false, false);}).delay(5000);
		}
	}
	
	if (page_type == "search") {
		get_legend();		
	}
	if (gQuery.species.length > 0 || gQuery.datasets.length > 0) {
		whole_extent();	
	}
	if (page_type != "front_page" && !(page_type == "dataset" && gQuery.datasets.length > 0 && gQuery.datasets[0].ds_type == "ptphoto")) {
		// [Earth] button is added to Google Maps but Earth Plugin instance is not yet initialized.
		// It's initialized when [Earth] is actually clicked => on_maptypechanged
		map.addMapType(G_SATELLITE_3D_MAP);
		GEvent.addListener(map, "maptypechanged", on_maptypechanged);
	}
}

function update_map(oj) {
	gMapserver.refresh();
	myTileOverlay.refresh();
	on_maptypechanged();	// If Google Earth is on, the KML is also updated.
	
	if (oj && oj.request.parameters.reclassify == "1") {
		get_json_legend();
	}
	if (gClient.update_legend && Ext.get("div_legend") && Ext.get("div_legend").isVisible()) {
		get_legend();
		if (!gQuery.isDefault()) {
			store_summary.load();
		}			
	}
}


/***** Mapserver layer definition *****/
// Return URL to get a tile image from Mapserver.
function myGetTileUrl(tile, zoom) {
    // max zoom plus 1
   	var layers = gClient.choose_layers(zoom);
    var projection = new GMercatorProjection(18);

    // Four vertices location in pixcel 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();

    // binding 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",
		layers: layers,
		mapsize: "256+256",
		mapext: mapext,
		outputformat: gMapserver.parameters.outputformat
	}

    var url = gMapserver.mapserverRequest(parameters);		
    return url;
}

function update_layers(params, handler) {
	var mode = "update_map";
	gQuery.layers = gClient.choose_layers(map ? map.getZoom() : 1);	
	var query = gQuery.toQueryString();
	gMapserver.request(mode, handler, params, query, {});
}

function switch_layer(button, e) {
	var layers = ["dist_sp", "points"];
	var params = {};
	
	layers.each(function(item){
		if (item == button.value) {
			var status = 1;
		} else {
			var status = -1;
		}
		gClient.layer_status[item] = status;
		if (gClient.reclassify[item]) {
			params.reclassify = 1;
			gClient.reclassify[item] = false;
		}
	});
	
	if (page_type == "species" || page_type == "search") {
		["grp_count_type", "grp_grid_resolution"].each(function(item){Ext.getCmp(item).setDisabled(button.value == "points");});
	} else {
		["grp_count_type", "grp_grid_resolution"].each(function(item){Ext.getCmp(item).setVisible(button.value != "points");});		
		if (gQuery.datasets[0].table2 != "" && gQuery.datasets[0].auto == "t") {
			Ext.getCmp("grp_tracklines").setVisible(button.value == "points");
			gClient.layer_status["tracks"] = (button.value == "points" && Ext.getCmp("btn_show_tracks").pressed) ? 0 : -1;
			// graph shows tracks in the secondary axis.
			["menu_effort_hours", "menu_length_traveled"].each(function(item){
				Ext.getCmp(item).setDisabled(button.value == "dist_sp");
			});
		}
		if (gQuery.datasets[0].ds_type == "pttag") {
			// For dist_sp, clear selections in animals.
			if (button.value == "dist_sp") {
				var grid_series = Ext.getCmp("grid_series");
				if (grid_series.rendered) grid_series.getSelectionModel().clearSelections();
				gQuery.series = [];
			}
		}
	}
	
	if (gClient.layer_status["points"] > -1) {
		$$("img.img_legend_icon").invoke("show");
		/*
		if (gLegendItems == null) {
			var handler = get_json_legend;	// for point layer, get legend icons if not yet. The map is updated in show_legend.
		} else {
			var handler = update_map;
		}
		*/
		var handler = update_map;
	} else {
		$$("img.img_legend_icon").invoke("hide");
		var handler = update_map;				// for dist layer, simply update map.
	}		
	gClient.update_legend = true;
	update_layers(params, handler);

	// Chart is different between point and dist_sp layers, so better regenerate.
	var chart = Ext.getCmp("linechart");
	if (chart) {
		rebuild_graph(chart.store.y_axis);
	}
}

function rebuild_graph(y_axis) {
	var chart = Ext.getCmp("linechart");
	var panel_graph = Ext.getCmp("panel_graph");
	panel_graph.remove(chart);
	var graph = build_graph(); 
	graph.store.y_axis = y_axis;
	Ext.getCmp("chart_paging").bindStore(graph.store, true); 
	panel_graph.insert(0, graph);
	graph.store.load();
	panel_graph.doLayout();
	
}

function on_zoomend_update_legend(old_zoom, new_zoom){
	// Summary and map legend under [Map Summary] need to be updated
	// when the grid size changes in response to the zoom-in/out.
	// (which means no need to update them on the point layer.
	if ((gClient.layer_status["dist_taxa"] > -1 || gClient.layer_status["dist_sp"] > -1) && Ext.get("div_legend") && Ext.get("div_legend").isVisible()) {
		var levels = $H(gClient.zoom_levels).values();
		levels = [levels[1] * 2 - 1, levels[2] * 2 - 1];
		if (levels.indexOf(old_zoom + new_zoom) != -1) {
			get_legend();
			if (page_type != "front_page" && !gQuery.isDefault()) {
				// Front Page does not have summary table, so do not run the next line.
				store_summary.load();
			}
		}
	}
}

function whole_extent() {
	switch (page_type) {
		case "species":
			var extent = gQuery.species[0].extent;
			zoomToBOX3D(extent);
			break;
		case "dataset":
		case "photoid":
			var dataset = gQuery.datasets[0];
			var buffer = 0.001;
			var lat_min = dataset.latitude_min - Math.abs(dataset.latitude_min) * buffer;
			var lat_max = dataset.latitude_max + Math.abs(dataset.latitude_max) * buffer;
			var lon_min = dataset.longitude_min - Math.abs(dataset.longitude_min) * buffer;
			var lon_max =  dataset.longitude_max + Math.abs(dataset.longitude_max) * buffer;
			zoomToSwNe(new GLatLng(dataset.latitude_min, dataset.longitude_min), new GLatLng(dataset.latitude_max, dataset.longitude_max), -1);
			break;
		default:
			map.setCenter(new GLatLng(gInitLoc["latitude"], gInitLoc["longitude"]), gInitLoc["zoom"]);
	}
}

function get_json_legend() {
	gMapserver.request("get_json_legend", show_json_legend,
		{
			layers: "points"
		}, "", {});
}

function show_json_legend(oj) {
	//update_map();
	
	var legend_text = oj.responseText;
	legend_text = "[" + legend_text.substr(legend_text, legend_text.length - 1) + "]";
	var legend_items = legend_text.evalJSON();
	gLegendItems = {};

	/*
	This method is a little bit slow, especially view.refresh
	for (var i = 0; i < legend_items.length; i++) {
		var legend_item = legend_items[i];
		gLegendItems['legend_' + legend_item.class_name] = legend_item.icon;
	}
	
	if (grid.rendered) {
		var view = grid.getView();
		view.refresh();
	}
	*/
	
	var color_by = gQuery.color_by;		// [species|dataset|series]
	for (var i = 0; i < legend_items.length; i++) {
		var legend_item = legend_items[i];
		var pattern = new RegExp("[ ,']", "g");
		//var class_name = legend_item.class_name.replace(/ /g, "_").toUpperCase();
		var class_name = legend_item.class_name.replace(pattern, "_").toUpperCase();
		var id = String.format('legend_{0}_{1}', color_by, class_name);
		if ($(id)) {
			// Legend may contain icons for unpublished datasets. Skip them as they are not listed.
			$(id).src = legend_item.icon;
		}
		gLegendItems[id] = legend_item.icon;
	}
}

function get_legend() {
	gMapserver.request("get_legend", show_legend,
		{
			layers: gClient.choose_layers(map.getZoom())
		}, "", {});
}

function show_legend(oj) {
	var msg = oj.responseText;
	$("div_legend").update(msg);
}

function roi_updated() {
	gQuery.clearTemporal();
	gClient.dataDirty();
	gROIs.dirty = false;
}

function count_type_changed(button, e) {
	gQuery.count_type = button.value;
	if (gClient.layer_status["dist_sp"] > -1 || gClient.layer_status["dist_taxa"] > -1) {
		update_layers({reclassify: 1}, update_map);	
	}
	/* timing issue ? => Call to a member function getlayerbyname() on a non-object in /var/www/seamap2.5/main/seamap_gm.php on line 1245
	if (Ext.get("div_legend") && Ext.get("div_legend").isVisible()) {
		get_legend();
	}
	*/
}

/*
function load_sized_image(url, image_id, params) {
	var logo = new Image;
	logo.onload = function (evt) {
		if ((logo.width / params.width) > (logo.height / params.height)) {
			if (logo.width > params.width) {
				$(image_id).setStyle({"width":params.width + "px", "height": "auto"});
			}
		} else {
			if (logo.height > params.height) {
				$(image_id).setStyle({"width": "auto", "height":params.height + "px"});
			} 
		}
		$(image_id).src = url;
	}
	logo.src = url;
}
*/

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

function species_search(species) {
	var parameters = $H({criteria: species, start_at:0, num_species:-1}).toQueryString();
	var url = gPlone + "getSpecies?" + parameters;
	new Ajax.Request(url,
		{
			method: 'GET',
			onSuccess: species_search_results,
			asynchronous: false
		});

	return gQuery.sp_tsn;	// gQuery.sp_tsns is set in species_search_results
}

function build_range_select() {
	if (store_graph.getCount() > 0) {
		var data_max = store_graph.data.items[0].data.y1_value;
		var data_min = data_max;
	
		// for lined up years 
		var years = store_graph.data.keys;
	
		if (gQuery.temporal_scale < 0 && gQuery.temporal_scale > -4) {		// Season except hourly
			var year_class = "season";
		} else {	// Year, month, day and seasonal month
			var year_class = "year_wide";
		}
	
		var table = "<table cellpadding=0 style='' id='year_table'>";
		var row1 = "";
		for (var i = 0; i < years.length; i++) {
			//var year = parseInt(years[i].replace(/-/g, ""));
			var year = years[i];
			
			// As graph series for all ds_type are stacked, need to sum them up to calculate max/min.
			// y1_value = ptobs, y2_value = pttag, y3_value = pthab, y4_value = ptacs, y5_value = pthoto
			//var series = ['y1_value', 'y2_value', 'y3_value', 'y4_value', 'y5_value'];
			var pattern = new RegExp("y[1-9]_value");
			var series = Ext.getCmp("linechart").series.pluck("yField").findAll(function(item){return pattern.test(item)});
			
			data_max = Math.max(data_max, series.inject(0, function(acc, item){return acc + store_graph.data.items[i].data[item]}));
			data_min = Math.min(data_min, series.inject(0, function(acc, item){return acc + store_graph.data.items[i].data[item]}));
			if (gQuery.temporal_scale == -1) {
				var season_labels = ['Winter', 'Spring', 'Summer', 'Fall'];
				//var year_str = season_labels[year - 1];
				var year_str = season_labels[parseInt(year) - 1];
			} else {
				//var year_str = year.toString();
				var year_str = year;
			}
			var cell_id = "year_" + year;
			var div = "<div id='" + cell_id + "' class='" + year_class + " chart_cell' onclick='range_clicked(this.id)' title='" + year_str + "'></div>";
			row1 += "<td>" + div + "</td>";
		}
		table += "<tr>" + row1 + "</tr>";
		table += "</table>";
	
		var buffers = [50, 51, 57, 64, 71, 80, 86];
		//var for_buffer = store_graph.y_axis == "bath" ? data_min : data_max;
		var histogram = Ext.getCmp("btn_histogram").pressed;
		var for_buffer = (store_graph.y_axis == "bath" && !histogram) ? data_min : data_max;
		var buffer = buffers[parseFloat((Math.log(Math.abs(for_buffer)) / Math.log(10)).toFixed(3)).floor()];
		if (for_buffer < 0) {
			buffer += 5;
		}
	} else {
		var buffer = 0;
		var table = "";
	}
	$("div_time_range").setStyle({paddingLeft: buffer + "px"});
	$('div_time_range_cells').update(table);
}

function range_clicked(cell_id) {
	if (Ext.getCmp("btn_histogram").pressed && gClient.layer_status.points == -1) {
		alert("Please switch to 'Points' (under [Map Options] panel) to set a value range.");
		return true;
	}
	var temporal = gQuery.temporal;
	//var time = parseInt(cell_id.split("_")[1]);
	var time = cell_id.split("_")[1];	// yyyy-mm-dd (e.g. for year, 1980-01-01)
	
	if (temporal.start === "" || temporal.end !== "") {
		temporal.start = time;
		temporal.end = "";
	} else {
		if (gQuery.oceano.env_type != "N" && gQuery.oceano.sync == "sync") {
			gQuery.oceano.update = true;
		}
		if (time < temporal.start) {
			temporal.end = temporal.start;
			temporal.start = time;
		} else {
			temporal.end = time;			
		}
		
		if (Ext.getCmp("btn_histogram").pressed) {
			temporal.histogram = store_graph.y_axis;
			temporal.end += gClient.histogram_zoom_levels[store_graph.y_axis][gQuery.temporal_scale];
		} else {
			temporal.histogram = "";
		}
		gClient.dataDirty("linechart");		// Do not update chart.
	}
	
	update_graph_time_range();
}

function update_graph_time_range() {
	// gQuery.temporal_scale should be updated before this function is called.
	var temporal = gQuery.temporal;
	if (gQuery.temporal_scale == -1) {
		var season_labels = ['Winter', 'Spring', 'Summer', 'Fall'];
		var label_start = temporal.start === "" ? "" : season_labels[temporal.start - 1];
		var label_end = temporal.end === "" ? "" : season_labels[temporal.end - 1];
	} else {
		//var label_start = temporal.start;
		//var label_end = temporal.end === "" ? "" : temporal.end;
		var str_length = [2, 4, 4, 7, 10];
		var label_start = temporal.start.substr(0, str_length[gQuery.temporal_scale]);
		var label_end = temporal.end === "" ? "" : temporal.end.substr(0, str_length[gQuery.temporal_scale]);
		if (gQuery.temporal_scale == 1) {
			label_start = label_start === "" ? "" : label_start + "s";
			label_end = label_end === "" ? "" : label_end + "s";
		}
		if (gQuery.temporal_scale == 0) {
			label_start = label_start === "" ? "" : (parseInt(label_start) + 1) + "th";
			label_end = label_end === "" ? "" : (parseInt(label_end) + 1) + "th";
		}
	}
	$("tbtext_time_range").update(label_start + " - " + label_end);
	var histogram = Ext.getCmp("btn_histogram").pressed;
	//var start = parseInt(temporal.start);
	//var end = parseInt(temporal.end);
	var start = temporal.start;
	var end = temporal.end;
	$$("div.chart_cell").each(function (item) {
		//var time = parseInt($(item).id.split("_")[1]);
		var time = $(item).id.split("_")[1];
		if ((time >= start && (histogram ? time < end : time <= end)) || (time === start && end === "")) {
			$(item).addClassName("year_selected");
		} else {
			$(item).removeClassName("year_selected");			
		}
	});	
	Ext.getCmp("btn_time_range").setVisible((label_start + label_end) !== "");
	Ext.getCmp("btn_clear_time_range").setVisible((label_start + label_end) !== "");
	$("env_time_range_sync").update(label_start === "" ? "Not specified" : label_start);
	gQuery.oceano.env_date_start =label_start;
}

/***** Year or Season *****/
function graph_option_changed(button) {
	var scale = button.value;
	gQuery.temporal_scale = scale;
	set_temporal_scale(Math.abs(scale));
	Ext.get("div_graph_frame").setVisible(scale != -4);
	$("label_range").update(button.id == "btn_histogram" ? "Value Range" : "Time Range");
}

function change_y_axis(button, e) {
	var y_axis = button.value;
	Ext.getCmp("menu_y_axis").setText("Y axis: " + button.text);
	
	Ext.getCmp("btn_histogram").setDisabled(["record", "animal", "species"].indexOf(y_axis) > -1);
	
	// To initialize the y axes, rebuild the graph
	// there can be primary and secondary axes and	there seems no way to disable the secondary.
	rebuild_graph(y_axis);
}

/*** Copied from detail_common_v2_5.js, which is no longer in use. ***/
// At this moment, temporal_scale_dragged and adjust_scale_bar are not in use for SEAMAP2.5
// but may better implement to make the teomporal scale navigation bar draggable.
/*
function temporal_scale_dragged(element) {
	var padding_top = 20;
	var pos = parseInt($(element.element).style.top);
	var scale = 2; 	// default
	var ranges = [63, 54, 45, 36, 27, 20];
	pos -= padding_top;
	
	for (var i = 0; i < ranges.length; i++) {
		if (pos >= ranges[i + 1] && pos < ranges[i]) {
			scale = i;
			break;
		}
	}
	set_temporal_scale(scale);
}
*/

function set_temporal_scale(scale) {
	// scale is always a positive integer corresponding to the scale bar.
	// Year or season is adjusted below.
	if (gQuery.temporal.start != "" || gQuery.temporal.end != "") {
		gQuery.clearTemporal();
		gClient.dataDirty();
	}
	
	if (gQuery.temporal_scale < 0) {		// Season
		var sign = -1;
		if (scale!= 4 && (scale < 1 || scale > 2)) {
			alert("Only season (sacle:1) and seasonal month (scale:2) are supported.");
			if (scale < 1) {
				scale = 1;
			} else {
				scale = 2;
			}
		}
	} else {							// Year/month/day
		var sign = 1;
		if (scale < 0 || scale > 4) {
			if (scale < 0) {
				scale = 0;
			} else {
				scale = 4;
			}
		}
	}
	gQuery.temporal_scale = sign * scale;
	
	var chart = Ext.getCmp("linechart");
	if (chart.store.storeId == "store_graph_all_data_paging") {
		var store = store_graph_all_data;
	} else {
		var store = store_graph;
	}
	if (store) {				// store_graph or store_graph_all_data may not be initialized at this time.
		store.dirty = true;	// To force refresh.
		store.load();	
	}
	
	adjust_scale_bar(scale);
}

function adjust_scale_bar(scale) {
	var padding_top = 20;
	var adjusted = [59, 50, 41, 32, 22];
	adjusted_pos = adjusted[scale];
	$('scale_bar').setStyle({top: (adjusted_pos + padding_top) + "px"});
}

/***** Navigation Tools *****/
function toggle_control(control_id, button) {
	var controls = gClient.controls;
    for(key in controls) {
        var control = controls[key];
        if(control_id == key && button.pressed) {
        	var tool = key;
            var handler = control.activate();
        } else {
            control.deactivate();
        }
    }    
	gClient.current_tool.tool = tool;
    gClient.current_tool.handler = handler;
}

function activate_identify() {
	var handler = GEvent.addListener(map, "click", identify);	
	return handler;
}

function deactivate_identify() {
	if (gClient.current_tool.handler) {
		GEvent.removeListener(gClient.current_tool.handler);
	}	
}

function identify(overlay, point) {
	map.closeInfoWindow();	// This causes the existing highlighted cell to be removed.

	// 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();
	var lat = point.lat();
	var lon = point.lng();

	// Calculate map extent.
	var projection = new GMercatorProjection(18);
	var pixel_point = projection.fromLatLngToPixel(point, map.getZoom());
	var tolerance_pixel = 255;
	var p2 = projection.fromPixelToLatLng(new GPoint(pixel_point.x + tolerance_pixel, pixel_point.y + tolerance_pixel), map.getZoom());
	var buffer = Math.abs(lon - p2.lng());
	var mapext = [Math.max(lon - buffer, -180), Math.max(lat - buffer, -90), Math.min(lon + buffer, 180), Math.min(lat + buffer, 90)].join(",");

	// button_select_zone does not exist in PhotoID interface
	var format = Ext.getCmp("button_select_zone") && Ext.getCmp("button_select_zone").pressed ? "json" : "html";
	
	var layers = gClient.choose_layers(map.getZoom());
	if (gClient.layer_status.dist_sp > -1 && format == "html") {
		highlight_cell(lon, lat);
	}
	
	gMapserver.request("identify", show_identify.bindAsEventListener(this, point, format),
		{
			latitude: lat,
			longitude: lon,
			mapext: mapext,
			layers: layers,
			format: format
		}, "", {});
}

function highlight_cell(lon, lat) {
	var zoom = map.getZoom();
	if (zoom < gClient.zoom_levels["0.1"]) {
		var times = 1;
		var shift = 1;
	} else if (zoom < gClient.zoom_levels["0.01"]) {
		var times = 10;
		var shift = 0.1;
	} else {
		var times = 100;
		var shift = 0.01;
	}
	lon = Math.floor(lon * times) / times;
	lat = Math.floor(lat * times) / times;
	var point1 = new GLatLng(lat, lon + 1 / times);
	var point2 = new GLatLng(lat + shift, lon);

	var options = {color: "#DDDDDD", weight: 3, opacity: 1};
	gCell = getRectangle(point1, point2, options);
	map.addOverlay(gCell);
}

function erase_cell() {
	if (gCell != null) {
		map.removeOverlay(gCell);
		gCell = null;
	}
}

function show_identify(oj, point, format) {
	var msg = oj.responseText;
	
	switch (format) {
		case "json":
			// Emulate a select of a zone in the zone listbox.
			var json = Ext.decode(msg);
			var layers = $H(json).keys();
			layers.each(function(item){
				var layer = json[item];
				var value = String.format("{0};{1};{2}", layer.label, layer.zone_id, layer.bbox);
				var checkbox = {checked: true, value: value};
				zone_selected(item, checkbox);				
			});
			break;
		case "html":
		default:		
			if (msg.indexOf("<json>") != -1) {
				msg = msg.replace("<json>", "");
				var json = Ext.decode(msg);
				//console.log(json);
				update_extra_tab(json.url, json.type);
			} else {
				map.openInfoWindowHtml(point, msg, gMaxDim);
			}
	}
}

// This is called from "Show list" link in the dist_sp identify popup or in the point identfy [dataset id] link.
function popup_dataset_list(datasets) {
	gMapserver.request("get_dataset_name", show_popup_dataset_list,
		{
			dataid: datasets
		}, "");
}

function show_popup_dataset_list(oj) {
	var dataset_list = oj.responseText.evalJSON();
	var msg = "<div style='width:340px; padding-right:10px'><div style='width:330px; height:150px; overflow:auto'><table class='listing' width='300'><thead><tr><th>Name (ID)</th></tr></thead><tbody>";
	for (var i = 0; i < dataset_list.length; i++) {
		msg += "<tr><td>" + dataset_list[i] + "</td></tr>";
	}
	msg += "</tbody></table></div></div>";
	var point = map.getInfoWindow().getPoint();
	map.openInfoWindowHtml(point, msg, gMaxDim);
}

function update_extra_tab(content, content_type) {
	var tab = Ext.getCmp("extra_tab");
	tab.show();
	var updater = tab.getUpdater();
	switch(content_type) {
		case "ajax_update":
			updater.update({url: content, method:"GET", params: {style: "none", v: get_random()},
				callback: function(){}
			});
			break;
		case "image_url":
			var content = String.format("<img src='{0}'>", content);
			tab.update(content);
			break;
	}
}

function show_env_sampled(env_str, scientific, obs_date) {
	env_str = env_str.replace("NULL", '"N/A"');
	var env_data = "{" + env_str + "}";
	env_data = Ext.decode(env_data);
	var layer_names = {CHL_D: "Chlorophyll - 8 days", CHL_M: "Chlorophyll - Monthly", CHL_Y: "Chlorophyll - Yearly",
		SSH_D: "Surface Height - Weekly", SSH_M: "Surface Height - First weekly",
		SST_D: "Surface Temperature - Weekly", SST_M: "Surface Temperature - Monthly", SST_Y: "Surface Temperature - Yearly",
		BATH: "Bathymetry"};
	var units = {CHL: "mg*m-3", SSH: "cm", SST: "\u2103", BATH: "m below"};
	var msg = "<strong>" + scientific + "</strong><br/>" + obs_date + "<br/>";
	
	msg += "<div style='width:410px; height:120px; overflow:auto;'><table class='listing popup_window' width='375'><thead><tr><th>Oceano variable</th><th>Value</th><th>Unit</th></thead><tbody>";
	$H(env_data).each(function(pair) {
		var name = typeof(layer_names[pair.key]) != "undefined" ? layer_names[pair.key] : pair.key;
		var unit = typeof(units[pair.key.split("_")[0]]) != "undefined" ? units[pair.key.split("_")[0]] : "";
		msg += String.format("<tr><td>{0}</td><td>{1}</td><td>{2}</td></tr>", name, pair.value, unit);
	});
	msg += "</tbody></table></div>";
	var info_window = map.getInfoWindow();
	var info_div = info_window.getContentContainers();
	$(info_div[0]).update(msg);
}

function render_resources(dataset, tsn, ds_type, resources) {
	var id = "query_td_resources";	// ID for <td> in the query template
	var image_base = "/seamap2/images/";
	if (resources != "") {
		var json_res = Ext.decode("[" + resources.replace(/&quot;/g, '"') + "]");
		// at this moment, treat the first entry only.
		var resource = json_res[0];
		switch (ds_type) {
			case "ptphoto":
				var image_url = image_base + resource.path.replace("catalogs", "catalogs_small");
				var s = String.format("<img src='{0}' title='{1}' />", image_url, resource.title);
				break;
			default:
				if (resource.type == "image") {
					var image_url = image_base + resource.path;
					var s = String.format("<img src='{0}' title='{1}' class='button' onclick='show_help_tip(\"dummy\", \"Animal image\", {url:\"{2}\"})' />", image_url, resource.title, image_url);					
				}
		}
	} else {
		var s = "";
	}
	Ext.get(id).update(s);
}
// Identify-related functions end

	
function activate_zoomin() {
	var handler = GEvent.addListener(map, "click", placePoint);
	onFinishPlacePoint = zoomTo;
	return handler;
}

function deactivate_zoomin() {
	if (gClient.current_tool.handler) {
		GEvent.removeListener(gClient.current_tool.handler);
	}
}

function placePoint(marker, point) {
	// Ignore event issued by polyline click
	if (marker && (typeof(point) == 'undefined')) {
		return false;
	}

	if (gROIs.first_point == null) {
		if (gROIs.extent) {
			map.removeOverlay(gROIs.extent);
		}
		gROIs.first_point = point;
		gMousemoveHandler = GEvent.addListener(map, "mousemove", drawExtent);
	} else {
		GEvent.removeListener(gMousemoveHandler);
		onFinishPlacePoint();
		map.removeOverlay(gROIs.extent);
		gROIs.first_point = null;
		gROIs.second_point = null;
		gROIs.extent = null;
		gROIs.step = 0;
	}
}

function drawExtent(latlong) {
	if (gROIs.step % 3 != 0) {
		gROIs.step++;
		if (gROIs.step > 3) {
			gROIs.step = 1;
		}
		return false;
	}

	gROIs.second_point = latlong;

	if (gROIs.extent) {
		map.removeOverlay(gROIs.extent);
	}

	//fillLocationBoxes(latlong);
	gROIs.extent = getRectangle(gROIs.first_point, gROIs.second_point, {onclick: placePoint});
	map.addOverlay(gROIs.extent);
}

function getRectangle(point1, point2, options) {
	var options_default = {color: "#FFFFFF", weight: 3, opacity: 1.0};
	options = $H(options_default).merge(options).toObject();
	var linePoints = cornerPoints(point1, point2);

	var extent = new GPolyline(linePoints, options.color, options.weight, options.opacity);
	if (options.onclick) {
		GEvent.addListener(extent,"click",function(point){
			options.onclick(extent, point);
		});
	}
	return extent;
}

function cornerPoints(point1, point2) {
	var lats = Array();
	var lngs = Array();
	lats[0] = point1.lat();
	lngs[0] = point1.lng();
	lats[1] = point2.lat();
	lngs[1] = point2.lng();
	var point3 = new GLatLng(lats[1], lngs[0]);
	var point4 = new GLatLng(lats[0], lngs[1]);

	var linePoints = Array();
	linePoints[0] = point1;
	linePoints[1] = point3 ;
	linePoints[2] = point2;
	linePoints[3] = point4;
	linePoints[4] = point1;

	return linePoints;
}

function getPolygon(points, color_index) {
	var index = gROIs.rois.length;
	poly_opacity = 1.0 - gROIs.transparency;
	poly_colors = gROIs.poly_colors;
	if (color_index == 'undefined' || color_index == null) {
		var color_index = index % 5;
	}
	poly_color = poly_colors[color_index];
	
	var polygon = new GPolygon(points, poly_color, 2, 0.8, poly_color, poly_opacity);
	GEvent.addListener(polygon,"click",function(point){
		switch (gClient.current_tool.tool) {
			//case "rectangle":
			//case "polygon":
			case "remove":
				remove_polygon(polygon);
				break;
			case "identify":
        		identify(polygon, point);
				break;
			default:
				if (typeof(polygon.in_edit) == "undefined" || polygon.in_edit == false) {
					polygon.in_edit = true;
					polygon.enableEditing();
					GEvent.addListener(polygon, "lineupdated", function(){gROIs.dirty = true});
				} else {
					polygon.in_edit = false;
					//polygon.disableEditing();
					//roi_updated();
					deactivate_draw_polygon();
				}
		}
      });
	map.addOverlay(polygon);
	return polygon;
}

function zoomTo() {
	if (gROIs.first_point.lng() < gROIs.second_point.lng()) {
		var sw = gROIs.first_point;
		var ne = gROIs.second_point;
	} else {
		var sw = gROIs.second_point;
		var ne = gROIs.first_point;
	}
	
	zoomToSwNe(sw, ne, -1);
}

function zoomToSwNe(sw, ne, max_zoom) {
	if (ne != null) {
		var aBound = new GLatLngBounds(sw, ne);
		var aCenter = aBound.getCenter();
		var aZoomLevel = Math.max(1, Math.min(13, map.getBoundsZoomLevel(aBound)));
	} else {
		var aCenter = sw;
		var aZoomLevel = max_zoom;
	}
	map.panTo(aCenter);
	map.setCenter(aCenter);

	if (max_zoom > 0 && aZoomLevel > max_zoom) {
		aZoomLevel = max_zoom;
	}
	map.setZoom(aZoomLevel);
}

function zoomToBOX3D(extent) {
	
	if (extent.indexOf("BOX") >= 0) {
		extent =  extent.substring(extent.indexOf("(") + 1, extent.indexOf(")"));
	}
	
	//extent = extent.replace(/BOX\(|\)/g, "");
	extent = extent.split(",");
	var sw = extent[0].split(" ");
	var ne = extent[1].split(" ");
	
	// In rare cases where just one observation is found, you can't define extent.
	// In that case, zoom in to the one observation.
	if (sw[1] != ne[1] && sw[0] != ne[0]) {
		var sw = new GLatLng(sw[1], sw[0]);
		var ne = new GLatLng(ne[1], ne[0]);
		var zoom = -1;
	} else {
		var sw = new GLatLng(sw[1], sw[0]);
		var ne = null;
		var zoom = 8;		// arbitrary
	}
	zoomToSwNe(sw, ne, zoom);
}

function zoomToPoint(lon, lat, zoom) {
	if (zoom == -1) {
		var current_zoom = map.getZoom();
		if (current_zoom < 7) {
			zoom = 7;
		} else {
			zoom = current_zoom;
		}
	}
	var aCenter = new GLatLng(lat, lon);
	map.panTo(aCenter);
	map.setCenter(aCenter);
	map.setZoom(zoom);
}

function activate_draw_rect() {
	var handler = GEvent.addListener(map, "click", placePoint);

	onFinishPlacePoint = draw_rect;
	return handler;
}

// SEAMAP2.5
function draw_rect() {
	map.removeOverlay(gROIs.extent);
	
	var points = cornerPoints(gROIs.first_point, gROIs.second_point);
	var polygon = getPolygon(points);
	gROIs.rois.push({mode: "rectangle", geometry: polygon});

	zoomTo();

	// Temporarily. Ideally implement post_roi_update
	gQuery.spatial = gROIs.geomAsText();
	if (typeof(roi_updated) != "undefined") {
		roi_updated();
	}
	
	Ext.getCmp("button_remove").show();
}

function deactivate_draw_rect() {
	if (gClient.current_tool.tool == "rectangle") {
		deactivate_draw_polygon();
	}
	if (gClient.current_tool.handler) {
		GEvent.removeListener(gClient.current_tool.handler);
	}
}

function activate_draw_polygon() {
	gROIs.in_edit = true;		// Turned to false in finish_drawing.
	var polygon = getPolygon([]);
	GEvent.addListener(polygon, "endline", finish_drawing);
	polygon.enableDrawing();
}

function finish_drawing() {
	gROIs.in_edit = false;
	var extent = this.getBounds();
	var sw = extent.getSouthWest();
	var ne = extent.getNorthEast();
	zoomToSwNe(sw, ne, -1);
	
	gROIs.rois.push({mode: "polygon", geometry: this});
	// Temporarily. Ideally implement post_roi_update
	gQuery.spatial = gROIs.geomAsText();
	if (typeof(roi_updated) != "undefined") {
		roi_updated();
	}
	Ext.getCmp("button_draw").toggle(false);
	Ext.getCmp("button_remove").show();	
}

function deactivate_draw_polygon() {
	var rois = gROIs.rois;
	if (gROIs.in_edit) {	// This means a new polygon is left unfinished.
		rois[rois.length - 1].geometry.disableEditing();
		map.removeOverlay(rois[rois.length - 1].geometry);
		rois.pop();
	}
	for (var i = 0; i < rois.length; i++) {
		// Shapefiles may be a part of gROIs (e.g. exercise_areas). Then, you need to skip them.
		if (typeof(rois[i].mode) != "string" || rois[i].mode != "shapefile") {
			rois[i].geometry.disableEditing();
		}
	}
	gROIs.in_edit = false; 
	
	gQuery.spatial = gROIs.geomAsText();

	if (typeof(roi_updated) != "undefined" && gROIs.dirty) {
		roi_updated();
	}
}

function point_icon () {
	var icon = new GIcon();
	icon.image = gIconBase + "marker_red.png";
	icon.iconSize = new GSize(16, 16);
	icon.iconAnchor = new GPoint(8, 8);
	icon.infoWindowAnchor = new GPoint(8, 8);
	return icon;
}

function remove_polygon(polygon) {
	var to_delete = gROIs.rois.find(function(item){return item.geometry == polygon});
	gROIs.rois = gROIs.rois.without(to_delete);
	map.removeOverlay(polygon);
	map.closeInfoWindow();
	gQuery.spatial = gROIs.geomAsText();
	if (typeof(roi_updated) != "undefined") {
		gClient.refleshCriteriaView = true;
		roi_updated();
	}
	
	if (gROIs.rois.length == 0) {
		Ext.getCmp("button_remove").hide();
	}
}


function polygon_transparency(slider, value) {		// EXT JS version
	gROIs.transparency = value / 100;
	change_transparency(gROIs.transparency);
}

function change_transparency(transparency) {
	for (var i = 0; i < gROIs.rois.length; i++) {
		if (gROIs.rois[i].mode != "shapefile") {
			gROIs.rois[i].geometry.opacity = 1 - transparency;
			gROIs.rois[i].geometry.redraw(true);
		}
	}
}


function map_cursor(cursor) {
	var dragObj = map.getDragObject();
	dragObj.setDraggableCursor(cursor); 	
}


function cancel_bubble(e) {
	if (!e) var e = window.event;
	e.cancelBubble = true;
	if (e.stopPropagation) e.stopPropagation();	
}

function onoff_env_layer(env_type) {
	var mode = "update_env_layer";
	
	gQuery.oceano.update = true;
	gQuery.oceano.env_type = env_type;
	if (env_type == "bath") {
		var layer_name = "bath";
		gClient.layer_status["env_layer"] = -1;
	} else {
		var layer_name = "env_layer";
		gClient.layer_status["bath"] = -1;
	}
	gClient.layer_status[layer_name] = (env_type == "N" ? -1 : 0);
	
	if (env_type != "N") {
		if (env_type != "bath") {
			var time_range = get_env_time_range();
			if (time_range == false) {
				return true;
			}
			/*
			if (time_range.start == "" || time_range.temporal == "") {
				alert("Wrong time range. Please enter a date in 'yyyy-mm-dd' format.");
				return true;
			}
			*/
		} else {
			var time_range = {temporal: "", start: ""};
		}
		
		gQuery.oceano.date_start = time_range.start;
		gMapserver.request(mode, env_layer_updated,
			{
				layer_name: layer_name,
				temporal_scale: time_range.temporal,
				env_date_start: time_range.start,
				env_type: env_type,
				status: env_type != "N"
			}, "");
	} else {
		gQuery.oceano.date_start = "";
		update_map();
	}
}

function env_layer_updated(oj) {
	var msg = oj.responseText.evalJSON();
	var env_type = oj.request.parameters.env_type;
	gQuery.oceano.update = false;

	if (msg.env_layer_status == 0) {
		var warning = String.format(rs_warning.oceano_not_available, env_type, oj.request.parameters.env_date_start);
		alert(warning);
		if (env_type == "bath") {
			gClient.layer_status["bath"] = -1;
		} else {
			gClient.layer_status["env_layer"] = -1;
		}
		$$("input[name=env_options]")[0].checked = true;
	}
	update_map();
}

function env_time_range_options_changed() {
	var time_range_option = radio_value("env_time_range_options");
	if (time_range_option == "sync") {
		if (typeof(store_graph) == "undefined" || store_graph.data.length == 0) {
			alert("Please switch to [Graph] tab to sync the environment with a time range defined with the graph");
			$$("input[name=env_time_range_options]")[1].checked = true;
			return false;
		}
		$("env_time_range").disabled = "disabled";
	} else {
		$("env_time_range").disabled = false;
	}
	gQuery.oceano.sync = time_range_option;
	var env_type = radio_value("env_options");
	if (env_type != "N") {
		onoff_env_layer(env_type);
	}
}

function get_env_time_range() {
	if (radio_value("env_time_range_options") == "sync") {
		if (gQuery.temporal.start == "") {
			var start = store_graph.data.keys[0];
		} else {
			var start = gQuery.temporal.start;
		}
		var temporal = gQuery.temporal_scale;
	} else {
		if ($("env_time_range").value == "") {
			var start = "";
		} else {
			//var start = $("env_time_range").value.replace(/[-\/]/g, "");
			var parsed = parse_date($("env_time_range").value);
			if (parsed.start == null) {
				alert("Failed to recognize the time range as a valid date.\nSee the help ('?' icon) for accepted formats.");
				return false;
			} else {
				var start = parsed.start;
				var temporal = parsed.temporal;
			}
		}
		/*
		if (start.length == 4) {
			var temporal = 2;
		} else if (start.length == 6) {
			var temporal = 3;
		} else if (start.length == 8) {
			var temporal = 4;
		} else {
			var temporal = "";
		}
		*/
	}
	
	var time_range = {start: start, temporal: temporal};
	return time_range;
}

function parse_date(value) {
	var temporal = 4;	// year, month and day
	value = value.trim();
	if (value.indexOf("/") > -1) {
		var dt =  Date.parseDate(value, "m/d/Y") ||  Date.parseDate(value, "Y/m/d") || 
			Date.parseDate(value, "n/j/Y") ||  Date.parseDate(value, "Y/n/j");
		if (dt == null) {	
			var dt = Date.parseDate(value, "m/Y") ||  Date.parseDate(value, "Y/m") || 
				Date.parseDate(value, "n/Y") ||  Date.parseDate(value, "Y/n");
			if (dt != null) {
				temporal = 3;
			}
		}
	} else if (value.indexOf("-") > -1) {
		var dt = Date.parseDate(value, "m-d-Y") ||  Date.parseDate(value, "Y-m-d") ||
			Date.parseDate(value, "n-j-Y") ||  Date.parseDate(value, "Y-n-j");
		if (dt == null) {
			var dt = Date.parseDate(value, "m-Y") ||  Date.parseDate(value, "Y-m") ||
			Date.parseDate(value, "n-Y") ||  Date.parseDate(value, "Y-n");
			if (dt != null) {
				temporal = 3;
			}
		}
	} else {
		var dt = Date.parseDate(value, "Ymd");
		if (dt == null) {
			var dt = Date.parseDate(value, "Ym");
			if (dt != null) {
				temporal = 3;
			} else {
				var dt = Date.parseDate(value, "Y");
				if (dt != null) {
					temporal = 2;
				}
			}
		}
	}
	return {start: dt == null ? dt : dt.format("Ymd"), temporal: temporal};
}

/* functions for Google Earth begin */
var ge = null;		// Google Earth Plugin object

function getEarthInstanceCB(object) {
	if (object == null) {
		alert("Google Earth Plugin is not installed or disabled.\nPlease install it or switch back to Map/Satellite view.");
		return true;		
	}
	ge = object;
	update_kml();
	//gLookAt = ge.createLookAt("0");
	
	//google.earth.addEventListener(ge.getGlobe(), "mousedown", geMouseDown);	
	//google.earth.addEventListener(ge.getGlobe(), "mousemove", geMouseMove);	
	//google.earth.addEventListener(ge.getGlobe(), "click", geClick);	
	//google.earth.addEventListener(ge.getGlobe(), "mouseup", geMouseUp);	
}

function on_maptypechanged() {
	var current_map = map.getCurrentMapType();
	if (current_map.getName() == "Earth") {
		if (ge == null) {
			// First time [Earth] is clicked, instantiate Earth Plugin before loading KML.
			map.getEarthInstance(getEarthInstanceCB);
		} else {
			update_kml();
		}
	}
}

function update_kml() {
	var data_selection = "on_map";
	var url = build_kml_url(data_selection, page_type);
	google.earth.fetchKml(ge, url, kml_loaded);
}

function kml_loaded(object){
	if (!object) {
		alert('bad or NULL kml');
	} else {
		// clear the existing KML, if already loaded.
		var container = ge.getFeatures();
		if (container.hasChildNodes()) {
			container.removeChild(container.getFirstChild());
		}
		container.appendChild(object);
	}
}
/* functions for Google Earth end */

function build_kml_url(data_selection, kml_type) {
	if (data_selection == "whole") {
		var params = {publish: gQuery.publish};
		switch (kml_type) {
			case "dataset":
				params["dataset_id"] = dataset_id;
				break;
			case "species":
				params["sp_tsn"] = sp_tsn;
				break;
		}
		data_selection += "_" + kml_type;
	} else {
		var params = {sid: gMapserver.parameters.sid, layers: gClient.choose_layers(map.getZoom())};
		// The following parameter is needed to spit out species/dataset info.
		if (gQuery.species.length > 0) {
			params["sp_tsn"] = gQuery.species.pluck("sp_tsn").join(",");
		}
		if (gQuery.datasets.length > 0) {
			params["dataset_id"] = gQuery.datasets.pluck("dataset_id").join(",");
		}
	}
	params["mode"] = kml_type;
	params["page_type"] = kml_type;
	params["data_selection"] = data_selection;
	params["v"] = get_random();
	var url = ROOT_URL + BASE_URL + "ogc/kml.php?" + Ext.urlEncode(params);
	//if (!Ext.isIE) console.log(url);
	return url;	
}

function open_google_earth(button, e) {
	var data_selection = radio_value("data_selection");	// [whole|on_map]
	var url = build_kml_url(data_selection, page_type);
	window.location.href = url;
}

function download_data(button, e) {
	var data_selection = radio_value("data_selection");	// [whole|on_map]
	
	if (data_selection == "whole") {
		var layer_name = "points";
		var layers = layer_name;
		data_selection += "_" + page_type;
		var params = {publish: gQuery.publish};
		switch (page_type) {
			case "dataset":
				params["dataset_id"] = dataset_id;
				if (gQuery.datasets.length > 0 && gQuery.datasets[0].table2 != "" && gQuery.datasets[0].auto == "t") {
					params.tracks = gQuery.datasets[0].table2.replace("zd_", "");
					layers = "tracks," + layers;
				}				
				break;
			case "species":
				params["sp_tsn"] = sp_tsn;
				break;
		}
	} else {
		var layers = gClient.choose_layers(map.getZoom());
		var layer_name = layers.split(",")[0];
		var params = gQuery.toQueryObject();
	}
	var format = button.value;
	var download_params = {sid: gMapserver.parameters.sid, mode: "start", layer_name: layer_name, data_selection: data_selection, format: format, v: get_random()};
	
	switch (format) {
		case "wms":
			// Define the extent
			if (data_selection.indexOf("whole") > -1) {
				// Take in the dataset /species extent; ignore the current map extent
				switch (page_type) {
					case "species":
						var species = gQuery.species[0];
						var extent = species.extent.replace("BOX(", "");
						extent = extent.replace(")", "");
						extent = extent.split(",");
						var sw = extent[0].split(" ");
						var ne = extent[1].split(" ");
						var bbox = [parseFloat(sw[0]), parseFloat(sw[1]), parseFloat(ne[0]), parseFloat(ne[1])];
						break;
					case "dataset":
						var dataset = gQuery.datasets[0];
						var bbox = [dataset.longitude_min, dataset.latitude_min, dataset.longitude_max, dataset.latitude_max];
						break;
				}
			} else {
				// for on_map, take in the current map extent
				var extent = map.getBounds();
				var sw = extent.getSouthWest();
				var ne = extent.getNorthEast();
				if (sw.lng() < ne.lng()) {
					var lon_min = sw.lng();
					var lon_max = ne.lng();
				} else {
					var lon_min = sw.lng() - 360;
					var lon_max = ne.lng();
				}
				var bbox = [lon_min, sw.lat(), lon_max, ne.lat()];				
			}
			
			var width = 1024;
			var height = (width * ((bbox[3] - bbox[1]) / (bbox[2] - bbox[0]))).abs().floor();
			var bbox_str = bbox.join(",");
			
			// See if background layers are chosen
			var btn_wms = Ext.getCmp("btn_wms");
			var additional_options = btn_wms.menu.items.items.findAll(function(item){return item.checked}).pluck("value");
			/*
			if (button.text == "WMS" && additional_options.indexOf("wms_save_as_image") > -1) {
				var wms_save_as_image = true;
				additional_options = additional_options.without("wms_save_as_image");
			} else {
				var wms_save_as_image = false;
			}
			if (additional_options.length > 0) {
				additional_options.push(download_params.layer_name);		// points or dist_sp should be last
				download_params.layer_name = additional_options.join(",");
			}
			*/
			/*
			if (layers.indexOf("tracks") > -1) {
				additional_options.push("tracks");
			}
			additional_options.push(download_params.layer_name);
			*/
			//var layers_reverse = layers.split(",").reverse();	// In case of whole, layers got a single layer name. so it doesn't matter.
			//additional_options.push(layers_reverse);	// => ['Continents', ['eez,', 'dist_sp_1deg']]
			additional_options.push(layers);	// => ['Continents', 'dist_sp_1deg,eez']	// layer order is taken care of on the PHP script.
			download_params.wms_layers = additional_options.join(",");			
			download_params = Ext.apply(download_params, {width: width, height: height, bbox: bbox_str});
			break;
		default:
	}
	
	Ext.Ajax.request({
		url: DOWNLOAD,
		params: Ext.apply(download_params, params),
		success: download_started,
		failure: download_failed
	});	
}

function download_started(oj) {
	var msg = oj.responseText.evalJSON();
	var request_id = msg["request_id"];
	var now = (new Date()).format("Y-m-d H:i:s");
	$("download_status").update("Request ID: " + request_id + "<br/>Requested: " + now);
	gDownloadTsk = {
		run: check_download_status.createDelegate(this, [request_id]),
		interval: 2000,
		duration: 1200000		// 20 minutes
	}
	Ext.TaskMgr.start(gDownloadTsk);	
}

function check_download_status(request_id) {
	if (!gDownloading) {
		var  params = "mode=status" + "&sid=" + gMapserver.parameters.sid + "&request_id=" + request_id;
		Ext.Ajax.request({
		   url: DOWNLOAD,
		   success: download_status,
		   failure: download_failed,
		   params: params
		});		
		gDownloading = true;
	}
}

function download_status(oj) {
	var msg = oj.responseText.evalJSON();
	if (msg["date_updated"] != "" &&  msg["status"] != "") {
		var now = (new Date()).format("H:i:s");
		$("download_status").insert({top: now + ": " + msg["status"] + "<br/>"});
	}
	if (msg["status"].indexOf("Ready to download") > -1 || msg["status"].indexOf("Error") > -1) {
		Ext.TaskMgr.stop(gDownloadTsk);
	}
	if (msg["status"].indexOf("Ready to download") > -1) {	
		var request_id = msg["request_id"];
		var url = DOWNLOAD;
		url += "?mode=download" + "&sid=" + gMapserver.parameters.sid + "&request_id=" + request_id;
		var link = msg["download_url"];
		$("download_status").insert({top: "If download failed, try the link below.<br/><a href='" + link + "' target='download'>Download</a><br/>"});
		if (msg.format == "wms") {
			window.open(url, "download");
		} else {
			window.location.href = url;
		}
	}
	gDownloading = false;
}

/*
function download_desc(description, target) {
	if (typeof(target) == "undefined") {
		var div = $("download_status");
	} else {
		var div = $(target);	
	}
	// Do not display the description if the download is in progress.
	if (div.innerHTML.indexOf("Request ID") == -1 || div.innerHTML.indexOf("Ready to download") > 0) {
		div.update(description);
	}
}
*/

function download_failed () {
	
}


/*** Search extention for Acoustic datasets ***/
var win_acoustic;
function open_acoustic_search() {
	if (!win_acoustic) {
		var panel = Ext.getCmp("map_panel");
		var main_panel_size = panel.getSize();
		var buffer_x = 50;
		var buffer_y = 40;
		win_acoustic = new Ext.ux.ClientWindow({
			title: rs_win_acoustic.title,
			id: "win_acoustic",
	        layout:'fit',
	        maximized: false,
	        x: buffer_x, y: buffer_y,
	        //width: parseInt(main_panel_size.width) - buffer_x * 1.5,
	        //height: parseInt(main_panel_size.height) - buffer_y,
	        width: 400,
	        height: 300,
	        padding: 10,
		    tools: [{id: 'help', handler: function(){show_help_tip("acoustic_search")}}],
		    contentEl: "div_acoustic_search"
		});
	}
	win_acoustic.show();	
}


function show_help_tip(tip_id, title, params) {
	var container = "div_help_tip";
	var title = typeof(title) == "string" ? title : tip_id.replace(/_/g, " ").capitalize();
	
	if (typeof(params) == "undefined") {
		var help_path = "/" + BASE_URL + "help/tips/";	// /seamap2.5/help/tips/
		var url = help_path + tip_id + ".html" + "?" + get_random();
		var html = '<div id="' + container + '"></div>';
		var content_type = "help";
	} else {
		var url = params.url;
		var html = "<img src='" + url + "' style='max-width:620px; max-height:370px' />";
		var content_type = "image";
	}
	
	var win = new Ext.Window({
		id: "help_tip_window",
		title: title,
		html: html,
		autoScroll: true,
		autoShow: true,
		layout      : 'fit',
		width       : 650,
		height      : 400,
		closeAction :'close',
		//padding:	"15px 20px",
		plain       : true,
		bodyCfg: {
	        cls: 'help_tip'
	    }
	});
	win.show();
	if (content_type == "help") {
		new Ajax.Updater(container, url);	
	}
}


// Called when selection of protected status changes
function status_change(checkbox) {
	if (checkbox.checked) {
		if (gQuery.protected_status.indexOf(checkbox.value) == -1) {
			gQuery.protected_status.push(checkbox.value);
		}
	} else {
		gQuery.protected_status = gQuery.protected_status.without(checkbox.value);
	}
	gClient.dataDirty();
}

// Called when selection of data type changes
function option_changed(checkbox) {
	var ds_type_platform = [];
	$$('input.ds_type').each(function(item){
		if (item.checked) {
			var status = false;
			if (item.value == 'ptacs') {
				var value = item.value + "', 'ptacs_calls";
			} else {
				var value = item.value;
			}
			var query = "ds_type in ('" + value + "')";
			var platform_checked = $$('input.platform_' + item.value).findAll(function(item){return item.checked;});
			if (platform_checked.pluck("value").indexOf("shore") > -1) {
				ds_type_platform.push("ds_type in ('pthab')");				
			}
			if (platform_checked.length > 0) {
				platform_checked = platform_checked.collect(function(item){return "'" + item.value + "'";});
				platform_checked = platform_checked.join(",");
				query += " and platform in (" + platform_checked + ")";
			}
			ds_type_platform.push("(" + query + ")");
		} else {
			var status = true;			
		}
		$$('input.platform_' + item.value).each(function(item){item.disabled = status});
	});
	
	if (ds_type_platform.length > 0) {
		ds_type_platform = ds_type_platform.join(" OR ");
	} else {
		ds_type_platform = "";
	}
	gQuery.ds_type_platform = ds_type_platform;
	gClient.dataDirty();
}

// Terms of use (embeded into div_termsofuse upon page load) is shown when [Download] tab is switched to first time.
var win_termsofuse;
function show_terms_of_use() {
	if (!win_termsofuse) {
		win_termsofuse = new Ext.Window({
			title: 'OBIS-SEAMAP Terms of Use',
			contentEl     : 'div_termsofuse',
			autoScroll: true,
			layout      : 'fit',
			width       : 640,
			height      : 400,
			closeAction :'hide',
			plain       : true,
			modal: true,
			bodyStyle: 'text-align: left',
			bodyCfg: {
		        tag: 'center',
		        cls: 'extjs_popup'
		    },
	
			buttons: [{
				text     : 'Agree', scale: "medium",
				handler  : termsofuse_close
			},{
				text     : 'Disagree', scale: "medium",
				handler  : termsofuse_close
			}]
		});
	}
	
	win_termsofuse.show();
}

function termsofuse_close(button) {
	var cp = Ext.state.Manager.getProvider();
	cp.set("__SEAMAP_agreed2terms", "true");
	var download_buttons = ["btn_csv", "btn_shapefile", "btn_wfs", "btn_kml", "btn_wms"];
	
	var agreed = button.text == 'Agree';
	if (agreed) {
		var cookie_value = "true";
		var disabled = false;
	} else {
		var cookie_value = "false";
		var disabled = true;
	}
	download_buttons.each(function(item){Ext.getCmp(item).setDisabled(disabled)});
	cp.set("__SEAMAP_agreed2terms", cookie_value);
	win_termsofuse.hide();
}

function ajax_failed(response, options) {
	if (response.isTimeout) {
		alert(rs_warning.timeout);
	} else {
		alert(String.format(rs_warning.ajax_exception, response.statusText));
	}
}

function load_javascript(url) {
	var fileref = document.createElement('script');
	fileref.setAttribute("type","text/javascript");
	fileref.setAttribute("src", url);
	if (typeof fileref!="undefined") {
		document.getElementsByTagName("head")[0].appendChild(fileref);
	}
}
/*** End of copy from detail_common_v2_5.js ***/


/***** Common Javascript Functions *****/
/*
function GetWindowSize(type){
	var dimensions = document.viewport.getDimensions();
    switch(type){
	case "width":
		return dimensions.width;
	case "height":
		return dimensions.height;
	default:
		return dimensions;
    }
}
*/
function GetWindowSize(type){
    /*
	switch(type){
	case "width":
		if(document.all){
			if(document.documentElement && document.documentElement.clientWidth){ // IE
			   return document.documentElement.clientWidth;
			}
			else if(document.body && document.body.clientWidth){
			   return document.body.clientWidth;
			}
		}else if(document.layers){
			return(innerWidth);
		}else{
			return(innerWidth);
		}
	break;
	case "height":
		if(document.all){
			if(document.documentElement && document.documentElement.clientHeight){ // IE
				return (document.documentElement.clientHeight);
			}
			else if(document.body && document.body.clientHeight){
		 		return (document.body.clientHeight);
			}
		}else if(document.layers){
			return(innerHeight);
		}else{
			return(innerHeight);
		}
	break;
	default:
		return(-1);
	break;
    }
    */
    
    var keys_ie = {width: "clientWidth", height: "clientHeight"};
    var keys = {width: "innerWidth", height: "innerHeight"};
    var key_ie = keys_ie[type];
    var key = keys[type];
	if(document.all){
		if(document.documentElement && document.documentElement[key_ie]){ // IE
		   return document.documentElement[key_ie];
		}
		else if(document.body && document.body[key_ie]){
		   return document.body[key_ie];
		}
	}else if(document.layers){
		return(window[key]);
	}else{
		return(window[key]);
	}
}

function isNumeric(x) {
	// I use this function like this: if (isNumeric(myVar)) { }
	// regular expression that validates a value is numeric
	var RegExp = /^[-+]?[0-9]*\.?[0-9]+(?:[eE][-+]?[0-9]+)?\b$/; // Note: this WILL allow a number that ends in a decimal: -452.
	// compare the argument to the RegEx
	// the 'match' function returns 0 if the value didn't match
	//var result = x.match(RegExp);
	var result = RegExp.test(x);
	return result;
}

function int_format(a_number, short_format) {
	if (a_number == "") {
		var s = "0";
	} else {
		a_number = parseInt(a_number);
		if (short_format) {
			if (a_number >= 1000000) {
				a_number = a_number / 1000000;
				var s = number_format(a_number, 2, '.', ',') + "M";
			} else if (a_number >= 100000) {
				a_number = a_number / 1000;
				var s = number_format(a_number, 1, '.', ',') + "K";
			} else {
				var s = number_format(a_number, 0, '.', ',');
			}
		} else {
			var s = number_format(a_number, 0, '.', ',');
		}
	}
	return s;
}

// number_format is copied from php.js.
// consider including this function in common.js or include php.js itself.
function number_format( number, decimals, dec_point, thousands_sep ) {
    // Format a number with grouped thousands
    // 
    // +    discuss at: http://kevin.vanzonneveld.net/techblog/article/javascript_equivalent_for_phps_number_format/
    // +       version: 804.1712
    // +   original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
    // +     bugfix by: Michael White (http://crestidg.com)
    // +     bugfix by: Benjamin Lupton
    // +     bugfix by: Allan Jensen (http://www.winternet.no)
    // +    revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com)    
    // *     example 1: number_format(1234.5678, 2, '.', '');
    // *     returns 1: 1234.57     

    var n = number, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals;
    var d = dec_point == undefined ? "," : dec_point;
    var t = thousands_sep == undefined ? "." : thousands_sep, s = n < 0 ? "-" : "";
    var i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", j = (j = i.length) > 3 ? j % 3 : 0;
    
    return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
}// }}}

function radio_value(radio_name) {
	var radio_buttons = $$("input[name=" + radio_name + "]");
	if (radio_buttons.length > 0) {
		// no radio button may be checked.
		var checked = radio_buttons.find(function(item){return item.checked});
		var value = checked ? checked.value : null;
	} else {
		var value = null;
	}
	return value;
}


function wait(miliseconds) {
	var date = new Date();
	var curDate = null;

	do {
		curDate = new Date();
	} while(curDate-date < miliseconds);
}

function get_random() {
	// EXT JS required.
	var random = (new Date()).format("mdYHis");	// e.g. 40302009134542 <= 2009-04-30 13:45:42
	return random;
}

/***** XML Utility *****/
function getXmlNodeValue(xml, node_name) {
	if (xml.getElementsByTagName(node_name).length > 0) {
		if (xml.getElementsByTagName(node_name)[0].childNodes.length > 0) {
			var value = xml.getElementsByTagName(node_name)[0].childNodes[0].nodeValue;
		} else {
			var value = "";
		}
	} else {
		var value = "";
	}

	return value;
}


