// This requires prototype.js and common.js

/**** Species definition ****/
var species_json_fields = [ 
    {name: 'sp_tsn', type: 'int'},
    {name: 'entity_name', type: 'string', mapping: 'scientific_name'},
    {name: 'scientific_name', type: 'string'},
    {name: 'group_field', type: 'int', mapping: 'seamap_taxa'},
	{name: 'sp_common', type: 'string'},
	{name: 'display_name', type: 'string'},
	{name: 'sp_class', type: 'string'},
	{name: 'sp_family', type: 'string'},
	{name: 'sp_genus', type: 'string'},
	{name: 'rank_id', type: 'int'},
	{name: 'sp_rank', type: 'string'},
	{name: 'storedpath', type: 'string'},
	{name: 'sp_title', type: 'string'},
	{name: 'num_records', type: 'int'},
	{name: 'num_animals', type: 'int'},
	{name: 'num_datasets', type: 'int'},
	{name: 'num_datasets_incl', type: 'int'},
	{name: 'num_records_total', type: 'int'},
	{name: 'num_records_incl', type: 'int'},
	{name: 'num_animals_total', type: 'int'},
	{name: 'sp_obs', type: 'string'},
	{name: 'status_dsc', type: 'string'},
	{name: 'data_extent', type: 'string'}
];

/**** dataset definition ****/
var dataset_json_fields = [ 
    {name: 'dataset_id', type: 'int'},
    {name: 'dataset_name', type: 'string'},
    {name: 'entity_name', type: 'string', mapping: "dataset_name"},
	{name: 'group_field', type: 'string', mapping: 'md_provider'},
	{name: 'provider_name', type: 'string', mapping: 'md_provider'},
	{name: 'md_provider', type: 'string'},
	{name: 'ds_type', type: 'string'},
	{name: 'platform', type: 'string'},
	{name: 'id_eff', type: 'int'},
	{name: 'auto', type: 'string'},
	{name: 'num_records', type: 'int'},		// total of the dataset
	{name: 'num_species', type: 'int'},
	{name: 'record_count', type: 'int'},	// for particular species
	{name: 'num_animals', type: 'int'},
	{name: 'date_min', type: 'string'},
	{name: 'date_max', type: 'string'},
	{name: 'added', type: 'string'},
	{name: 'updated', type: 'string'},
	{name: 'data_extent', type: 'string'}
];

var series_json_fields = [ 
    {name: 'series', type: 'string', mapping: 'series'},
    {name: 'entity_name', type: 'string', mapping: 'series_name'},
    {name: 'series_name', type: 'string', mapping: 'series'},
    {name: 'group_field', type: 'string', mapping: 'sp_scientific'},
    {name: 'sp_tsn', type: 'int'},
    {name: 'date_min', type: 'string'},
    {name: 'date_max', type: 'string'},
    {name: 'resources', type: 'string'},
    {name: 'count', type: 'int'},
    {name: 'lat_min', type: 'float'},
    {name: 'lat_max', type: 'float'},
    {name: 'lon_min', type: 'float'},
    {name: 'lon_max', type: 'float'},
    {name: 'length', type: 'float'}
];
/*
var photo_json_fields = [ 
    {name: 'series', type: 'string'},
    {name: 'series_name', type: 'string'},
    {name: 'entity_name', type: 'string', mapping: 'series'},
    {name: 'group_field', type: 'string', mapping: 'research_area'},
    {name: 'sp_scientific', type: 'string'},
    {name: 'original_id', type: 'string'},
    {name: 'sp_tsn', type: 'int'},
    {name: 'date_min', type: 'string'},
    {name: 'date_max', type: 'string'},
    {name: 'count', type: 'int'},
    {name: 'lat_min', type: 'float'},
    {name: 'lat_max', type: 'float'},
    {name: 'lon_min', type: 'float'},
    {name: 'lon_max', type: 'float'}
];
*/
var photo_json_fields = [ 
    {name: 'dolphin_id', type: 'int'},
    {name: 'series', type: 'string'},
    {name: 'site_id', type: 'string'},
    {name: 'age', type: 'string'},
    {name: 'sex', type: 'string'},
    {name: 'original_id', type: 'string'},
    {name: 'catalog_id', type: 'string'},
    {name: 'match_id', type: 'string'},
    {name: 'matches', type: 'string'},
    {name: 'sight_date_min', type: 'string'},
    {name: 'sight_date_max', type: 'string'},
    {name: 'sight_count', type: 'int'},
    {name: 'entity_name', type: 'string', mapping: 'nick_name'},
    {name: 'image_path', type: 'string'},
    {name: 'sight_date', type: 'string'}
];

function get_columns(column_type) {
	switch (column_type) {	
		case "species_on_map":
		case "species":
		case "species_in_dataset":
			// width of panel_main_frame's border panels (center) is not availble at this time, so give rought estimate below.
			var space = GetWindowSize("width") - 350 - 336;	// 350 rough width of border.west; 336 = [Rank] + [Status] + {Extent]
			var columns = [{
			    header: ' ',	// Legend icon
			    id: "legend",
			    dataIndex: page_type == "dataset" ? "sp_obs" : "sp_tsn",
			    width: 25,
			    sortable: false,
			    menuDisabled: true,
			    resizable: false,
			    align: 'left',
			    hidden: column_type == "species",
			    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
			   		var pattern = new RegExp("[ ,']", "g");
			    	value = value.toString().replace(pattern, "_");
			    	var key = String.format("legend_{0}_{1}", page_type == "dataset" ? "sp_obs" : "species", value);
			    	if (gLegendItems == null || typeof(gLegendItems[key]) == "undefined") {
			    		var src = gIconBase + "blank.png";
			    	} else {
			    		var src = gLegendItems[key];
			    	}
			    	var display = (gClient.layer_status["points"] > -1 && (gQuery.color_by == "species" || gQuery.color_by == "sp_obs")) ? "block" : "none";
			   		var text = String.format("<img class='img_legend_icon' src='{0}' id='{1}' style='display:{2}'>", src, key, display);
			    	return text;
			    }
			  },{
				id: 'entity_name',
			    header: 'Species name',
			    dataIndex: 'entity_name',
			    //width: 200,
			    width: Math.max(200, space * 0.38),
			    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
			   		var text = "<a href='" + String.format(SPECIES_PAGE, record.data.sp_tsn) + "' title='Go to Species Profile Page' target='species_profile' onmousedown='cancel_bubble(event);' onclick='cancel_bubble(event);'>" + value + "</a>";
			    	return text;
			    }
			  },{
			    header: 'Common',
			    dataIndex: 'sp_common',
			    //width: 170,
			    width: Math.max(170, space * 0.32)/*,
			    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
			    	if (record.data.display_name != "") {
			   			var text = record.data.display_name;
			    	} else {
			    		var text = value;
			    	}
			    	return text;
			    }*/
			  },{
			    header: 'Rank ID',
			    dataIndex: 'rank_id',
			    tooltip: "Taxonomic rank",
			    hidden: true,
			    width: 100
			  },{
			    header: 'Rank',
			    dataIndex: 'sp_rank',
			    hidden: column_type == "species",
			    tooltip: "Taxonomic rank",
			    width: 100
			  },{
			    header: 'Family',
			    dataIndex: 'sp_family',
				hidden: true,
			    tooltip: "Family the species belongs to",
			    width: 70
			  },{
			    header: 'Genus',
			    dataIndex: 'sp_genus',
				hidden: true,
			    tooltip: "Genus the species belongs to",
			    width: 70
			  },{
			    header: 'Name recorded',
			    dataIndex: 'sp_obs',
			    tooltip: "Data provider's species ID/name",
			    hidden: page_type == "search",
			    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
			    	if (record.data.sp_title != "") {
			    		var text = record.data.sp_title;
			    	} else {
			    		var text = value;
			    	}
			    	return text;
			    },
			    //width: 160
			    width: Math.max(160, space * 0.31)
			  },{
			    header: 'ITIS#',
			    dataIndex: 'sp_tsn',
			    align: "right",
			    //hidden: column_type == "species",
			    hidden: true,
			    tooltip: "Taxonomic Serial Number by ITIS",
			    width: 70
			  },{
			    header: 'Status',
			    readOnly: true,
			    dataIndex: 'status_dsc',
			    hidden: column_type == "species",
			    align: "center",
			    tooltip: "Protected status under US ESA and Red List",
			    width: 130
			  },{
			    header: '#records',
			    dataIndex: 'num_records',
			    tooltip: "Number of records",
			    align: "right",
			    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
			    	var value2 = (page_type == "dataset" && gQuery.isDefault(true)) ? record.data.num_records_total : value;
			    	var text = int_format(value2, false);
			    	return text;
			    },
			    width: 70
			  },{
			    header: '#branches',
			    dataIndex: 'num_records_incl',
			    tooltip: "Number of records including branch taxa",
			    align: "right",
			    hidden: column_type != "species",
			    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
			    	var text = int_format(value, false);
			    	return text;
			    },
			    width: 90
			  },{
			    header: (page_type == "dataset" && gQuery.datasets[0].ds_type == "ptacs_calls") ? "#calls" : "#animals",
			    dataIndex: 'num_animals',
			    tooltip: (page_type == "dataset" && gQuery.datasets[0].ds_type == "ptacs_calls") ? "Number of calls" : "Number of animals",
			    align: "right",
			    //hidden: column_type == "species",
			    hidden: true,
			    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
			    	var value2 = (page_type == "dataset" && gQuery.isDefault(true)) ? record.data.num_animals_total : value;
			    	var text = int_format(value2, false);
			    	return text;
			    },
			    width: 80
			  },{
			    header: ' ',
			    dataIndex: 'data_extent',
			    tooltip: "Click icon to zoom in",
			    align: "center",
			    sortable: false,
			    menuDisabled: true,
			    fixed: true,
			    hidden: column_type == "species",
			    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
			    	var text = "<img class='button' src='/seamap2/icons/icon_tool_fullextent.png' title='Zoom in to this dataset' onmousedown='cancel_bubble(event);' onclick='cancel_bubble(event); zoomToBOX3D(\"" + value + "\");'>";
			    	return text;
			    },
			    width: 36
			  },{
			    header: 'SEAMAP Taxa',
			    dataIndex: 'group_field',
			    width: 10,
			    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
			    	var taxa_labels = ["Marine mammals","Seabirds", "Sea turtles", "Others"];
			    	var text = taxa_labels[value - 1];
			    	return text;
			    },
				hidden: true
			  }];
			  break;
	case "datasets":
	case "datasets_on_map":
		var columns = [{
		    header: '',	// Legend icon
		    id: "legend",
		    dataIndex: 'dataset_id',
		    width: 25,
		    hidden: column_type == "datasets",
		    align: 'left',
		    sortable: false, menuDisabled: true,  resizable: false,
		    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
		    	var key = "legend_dataset_" + value;
		    	if (gLegendItems == null || typeof(gLegendItems[key]) == "undefined") {
		    		var src = gIconBase + "blank.png";
		    	} else {
		    		var src = gLegendItems[key];
		    	}
		    	var display = gClient.layer_status["points"] > -1 ? "block" : "none";
		   		var text = String.format("<img class='img_legend_icon' src='{0}' id='{1}' style='display:{2}'>", src, key, display);
		    	return text;
		    }
		  },{
			id: 'entity_name',
		    header: 'Dataset name',
		    dataIndex: 'entity_name',
		    width: 200,
		    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
		   		var text = "<a href='" + String.format(DATASET_PAGE, record.data.dataset_id) + "' title='Go to Dataset Page' target='dataset_detail' onmousedown='cancel_bubble(event);' onclick='cancel_bubble(event);'>" + value + "</a>";
		    	return text;
		    }
		  },{
		    header: 'ID',
		    dataIndex: 'dataset_id',
		    align: "right",
		    hidden: true,
		    width: 50
		  },{
		    header: 'Type',
		    dataIndex: 'ds_type',
		    id: 'type',
		    tooltip: "observations/tag/habitat/model",
		    align: "center",
		    width: 60
		  },{
		    header: 'Platform',
		    readOnly: true,
		    dataIndex: 'platform',
		    tooltip: "boat/plane/tag/habitat",
		    align: "center",
		    width: 70
		  },{
		    header: 'Tracks',
		    dataIndex: 'id_eff',
		    tooltip: "Survey effort/telemetry tracks",
		    align: "center",
		    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
		    	if (value != "" && record.data.auto != "") {
		    		var text = "Yes";
		    	} else {
		    		var text = "No";
		    	}
		    	return text;
		    },
		    width: 60
		  },{
		    header: 'Year',
		    dataIndex: 'date_min',
		    tooltip: "Survey years",
		    align: "center",
		    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
		    	if (value != "") {
			    	var year_min = Date.parseDate(value, "Y-m-d h:i:s").format("Y");
			    	var year_max = Date.parseDate(record.data.date_max, "Y-m-d h:i:s").format("Y");
			
			    	if (year_min == year_max) {
			    		var text = year_min;
			    	} else {
			    		var text = year_min + "-" + year_max;
			    	}
		    	} else {
		    		var text = "";
		    	}
		    	return text;
		    },
		    width: 100
		  },{
		    header: '#records',
		    dataIndex: column_type == "contributing_datasets" ? "record_count" : "num_records",
		    tooltip: "Number of records",
		    align: "right",
		    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
		    	var text = int_format(value, false);
		    	return text;
		    },
		    width: 90
		  },{
		    header: '#animals',
		    dataIndex: 'num_animals',
		    tooltip: "Number of animals",
		    align: "right",
		    hidden: column_type == "datasets",
		    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
		    	var text = int_format(value, false);
		    	return text;
		    },
		    width: 110
		  },{
		    header: 'Added',
		    readOnly: true,
		    dataIndex: 'added',
		    tooltip: "Date the dataset was registered",
		    align: "center",
		    hidden: page_type != "front_page",
		    width: 100
		  },{
		    header: ' ',
		    dataIndex: 'data_extent',
		    tooltip: "Click icon to zoom in",
		    align: "center",
		    sortable: false,
		    menuDisabled: true,
		    fixed: true,
		    hidden: column_type == "datasets",
		    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
		    	var text = "<img class='button' src='" + gIconBase + "icon_tool_fullextent.png' title='Zoom in to this dataset' onmousedown='cancel_bubble(event);' onclick='cancel_bubble(event); zoomToBOX3D(\"" + value + "\");'>";
		    	return text;
		    },
		    width: 36
		  },{
		    header: 'Provider',
		    dataIndex: 'group_field',
		    align: "right",
		    width: 70,
		     hidden: true
		  }];
		   
			if (column_type == "datasets" && GetWindowSize("width") < 1350) {
				columns[4].hidden = true;	// platform
				//columns[6].hidden = true;	// Year
				columns[5].hidden = true;	// Tracks
			}		  
			break;
		case "series":
		case "fixed_location":
		case "photo":
			var columns = [{
			    header: '',
			    readOnly: true,
			    id: 'series',
			    dataIndex: 'series',
			    width: 25,
			    align: 'left',
			    fixed: true,
			    menuDisabled: true, 
			    resizable: false,
			    sortable: false,
			    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
			    	var key = "legend_series_" + value.toUpperCase();	// can be number or string
			    	if (gLegendItems == null || typeof(gLegendItems[key]) == "undefined") {
			    		var src = gIconBase + "blank.png";
			    	} else {
			    		var src = gLegendItems[key];
			    	}
			   		var text = "<img class='img_legend_icon' src='" + src + "' id='" + key + "'>";
			    	return text;
			    }
			  },{
				id: 'entity_name',
			    header: column_type == "fixed_location" ? "Site name" : "Animal name",
			    readOnly: true,
			    dataIndex: 'entity_name',
			    hidden: column_type != "fixed_location",
			    width: 160
			  },{
				id: 'series_name',
			    header: column_type == "fixed_location" ? "Site ID" : "Animal ID",
			    readOnly: true,
			    dataIndex: 'series_name',
			    width: 160,
			    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
			    	var image_src = "";
			    	if (record.data.resources != "") {
			    		var resources = Ext.decode("[" + record.data.resources + "]");
			    		if (resources[0].type == "image") {
			    			var image_src = gIconBase + "icon_image.png";
			    			var image_url = "/seamap2/images/" + resources[0].path;
			    		}
			    	}
			    	
			    	if (image_src != "") {
				   		var image_tag = String.format("<img src='{0}' id='{1}' class='button' style='display:inline' title='Click to see larger image' onmousedown='cancel_bubble(event);' onclick='cancel_bubble(event); show_help_tip(\"dummy\", \"Animal image\", {url:\"{2}\"})' />", image_src, value, image_url);
			    		var text = value + "&nbsp;&nbsp;" + image_tag;
			    	} else {
			    		var text = value;
			    	}
			    	return text;
			    }			    
			  },{
			    header: column_type == "series" ? "Scientific" : "Research area",
			    readOnly: true,
			    dataIndex: 'group_field',
			    width: 150,
			    hidden: column_type != "photo"
			  },{
			    header: "Original ID",
			    readOnly: true,
			    dataIndex: 'original_id',
			    width: 150,
			    hidden: column_type != "photo"
			  },{
			    header: 'Count',
			    readOnly: true,
			    dataIndex: 'count',
			    align: "right",
			    tooltip: "Number of data points",
			    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
			   		var text = int_format(value, false);
			    	return text;
			    },
			    width: 80
			  },{
			    header: 'Dates',
			    readOnly: true,
			    dataIndex: 'date_min',
			    tooltip: "Date range",
			    align: 'center',
			    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
			    	if (value != record.data.date_max) {
			    		var text = value + " - " + record.data.date_max;
			    	} else {
			    		var text = value;
			    	}
			    	return text;
			    },
			    width: 210
			  },{
			    header: 'Longitude',
			    readOnly: true,
			    dataIndex: 'lon_min',
			    tooltip: "Longitude range",
			    hidden: column_type == "photo",
			    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
			    	if (value != record.data.lon_max) {
			    		var text = value.toFixed(2) + " - " + record.data.lon_max.toFixed(2);
			    	} else {
			    		var text = value;
			    	}
			    	return text;
			    },
			    width: 150
			  },{
			    header: 'Latitude',
			    readOnly: true,
			    dataIndex: 'lat_min',
			    tooltip: "Latitude range",
			    hidden: column_type == "photo",
			    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
			    	if (value != record.data.lat_max) {
			    		var text = value.toFixed(2) + " - " + record.data.lat_max.toFixed(2);
			    	} else {
			    		var text = value;
			    	}
			    	return text;
			    },
			    width: 130
			  },{
			    header: 'Traveled (km)',
			    readOnly: true,
			    dataIndex: 'length',
			    hidden: column_type != "series",
			    tooltip: "Length traveled in km",
			    align: "right",
			    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
			    	if (value < 0) {
			    		var text = "N/A";
			    	} else {
			    		var text = number_format((value / 1000), 2, ".", ",");
			    	}
			    	return text;
			    },
			    width: 110
			  },{
		    header: ' ',
		    dataIndex: 'min_lat',
		    tooltip: "Click icon to zoom in",
		    align: "center",
		    sortable: false,
		    menuDisabled: true,
		    fixed: true,
		    renderer: function(value, metadata, record, rowIndex, colIndex, store) {
		    	var data = record.data;
		    	var bbox = String.format("{0} {1},{2} {3}", data.lon_min, data.lat_min, data.lon_max, data.lat_max);
		    	var text = "<img class='button' src='" + gIconBase + "icon_tool_fullextent.png' title='Zoom in to this data' onmousedown='cancel_bubble(event);' onclick='cancel_bubble(event); zoomToBOX3D(\"" + bbox + "\");'>";
		    	return text;
		    },
		    width: 36
		  }
			];
		break;
	}
	return columns;
}

// This function get_graph_fields is called when the graph component first created (when [Graph] tab opens).
// If you modify these functions, check if any alternation is needed for  build_range_select() in seamap_common.js.
// The returned fields contain all possible fields for the point layer.
// The dist layer uses fewer fields but it's OK as additional fields do not make any effects (are just ignored).
function get_graph_fields(graph_type) {
	var y_fields = 5;	// ptobs/pttag/pthab/ptacs/ptphoto
	if (page_type == "dataset") {
		// Graphs for acoustic stationary datasets on Dataset Page is comprised with different series than other datasets.
		if (["ptacs", "ptacs_calls"].indexOf(graph_type.ds_type) > -1 && graph_type.platform == "station") {
			var effort_hours = true;
			var length_km = false;
			var y_fields = graph_type.sp_obs_count;
		} else {
			var effort_hours = graph_type.auto == "t" && graph_type.table2 != "";
			var length_km = effort_hours;
		}
	} else {
		// Graphs on Species Page and Advanced Search do not show tracks/effort
		var effort_hours = false;
		var length_km = false;
	}
	
	var fields = [{name: 'x_value', type: 'string'}];	// 'YYYY-MM-DD'
  	for (var i = 0; i < parseInt(y_fields); i++) {
  		var name = String.format("y{0}_value", i + 1);
  		fields.push({name: name, type: 'int'});
  	}
  	if (effort_hours) {
  		fields.push({name: 'effort_hours', type: 'float'});
  	}
  	if (length_km) {
  		fields.push({name: 'length_km', type: 'float'});
  	}
	
	return fields;
}

// This function get_graph_series is called every time the graph loads data (beforeload)
// graph_type includes layer_type = [dist_taxa|dist_sp|points]
function get_graph_series(graph_type) {
	/*
	var with_effort = ((page_type == "dataset") && (graph_type.ds_type == "ptacs" || graph_type.ds_type == "ptacs_calls") && graph_type.platform == "station");
	
	if (with_effort) {
		var colors = ["#FF0000", "#EEEE00", "#00FF00", "#FF00FF", "#00FFFF"];
		var series = [
		{type:'column', yField: 'effort_hours', axis: "primary", style: {color:"#CCCCCC"}}
		];
	  	for (var i = 0; i < parseInt(graph_type.sp_obs_count); i++) {
	  		var name = String.format("y{0}_value", i + 1);
	  		series.push({type:'line', yField: name, axis: "secondary", style: {color:colors[i]}});
	  	}
	} else {
		var num_series = graph_type.layer_type == "points" ? 5 : 1;
		var series_types = ["ptobs", "pttag", "pthab", "ptacs", "ptphoto"];
		var colors = ["#FFD34F", "#00B8BF", "#C877EA", "#00D3FF", "#00D3FF"];
		var series = [];
		for (var i = 0; i < num_series; i++) {
			var field_id = String.format("y{0}_value", i + 1);
			var item = {yField: field_id, displayName: series_types[i], stacked: true, axis: "primary", style: {color:colors[i]}};
			series.push(item);
		}
		
		//series.push({type:'line', yField: 'effort_hours', axis: "primary", style: {color:"#CCCCCC"}});
	}
	*/
	
	var series = [];
	var series_types = ["ptobs", "pttag", "pthab", "ptacs", "ptphoto"];
	var colors = ["#FFD34F", "#00B8BF", "#C877EA", "#00D3FF", "#00D3FF"];
	var acoustic = false;
	
	if (page_type == "dataset") {
		// Graphs for acoustic stationary datasets on Dataset Page is comprised with different series than other datasets.
		if (["ptacs", "ptacs_calls"].indexOf(graph_type.ds_type) > -1 && graph_type.platform == "station") {
			var y_fields = parseInt(graph_type.sp_obs_count);
			var colors = ["#FF0000", "#EEEE00", "#00FF00", "#FF00FF", "#00FFFF"];
			var effort_hours = true;
			var effort_series = {type: "column", axis: "primary"};
			var obs_type = "line";
			var stacked = false;
			var acoustic = true;
		} else if (graph_type.layer_type == "points") {
			var y_fields = 5;	// ptobs/pttag/pthab/ptacs/ptphoto
			var obs_type = "stackcolumn";
			var stacked = true;
			var effort_hours = graph_type.auto == "t" && graph_type.table2 != "" && Ext.getCmp("btn_show_tracks").pressed;
			var effort_series = {type: "line", axis: "primary"};
		} else {
			var y_fields = 1;
			var obs_type = "column";
			var stacked = false;
			var effort_hours = false;			
		}
	} else {
		if (graph_type.layer_type == "points") {
			var y_fields = 5;
			var obs_type = "stackcolumn";
			var stacked = true;
		} else {
			var y_fields = 1;
			var obs_type = "column";
			var stacked = false;			
		}
		var effort_hours = false;		
	}
	
 	for (var i = 0; i < y_fields; i++) {
  		var field_id = String.format("y{0}_value", i + 1);
  		series.push({type:obs_type, yField: field_id, displayName: series_types[i], stacked: stacked, axis: effort_hours ? "secondary" : "primary", style: {color:colors[i]}});
  	}		
	if (effort_hours) {
		var yField = Ext.getCmp("menu_effort_hours").checked ? "effort_hours" : "length_km";
		var displayName = yField == "length_km" ? "Length traveled (km)" : (graph_type && graph_type.ds_type == "pttag") ? "Travel time (hrs)" : "Effort (hrs)"; 
		var effort_item = {type:effort_series.type, yField: yField, displayName: displayName, axis: effort_series.axis, style: {color:"#CCCCCC"}};
		if (acoustic) {
			series.splice(0, 0, effort_item);	// Insert at index 0. the second param indicates no item to be removed.
		} else {
			series.push(effort_item);
		}
	}
  	
	return series;
}

// called every time the graph refreshes (beforerefresh).
function get_y_axes(store, graph_type, title, y_mins, y_maxs) {
	var axes = [];
	var with_effort = (page_type == "dataset") && 
		(((graph_type.ds_type == "ptacs" || graph_type.ds_type == "ptacs_calls") && graph_type.platform == "station") || 
		(graph_type.layer_type == "points" && graph_type.auto == "t" && graph_type.table2 != "") && Ext.getCmp("btn_show_tracks").pressed);
	
    var y_min = y_mins["primary"].min();
    var y_max = y_maxs["primary"].max();
    if (y_min == y_max) {
    	y_min = 0;
    }    
	if (with_effort) {
		var yField = Ext.getCmp("menu_effort_hours").checked ? "effort_hours" : "length_km";
		var title1 = yField == "length_km" ? "Length traveled (km)" : (graph_type && graph_type.ds_type == "pttag") ? "Travel time (hrs)" : "Effort (hrs)"; 
		var y_axis1 = new Ext.chart.NumericAxis({
	        title: title1,
	        alwaysShowZero: true,
	        position: "right",
	        order: "primary",
	        majorUnit: ((y_max - y_min) <= 15 && y_max < 100) ? 1 : (Math.max(Math.max(Math.pow(10, parseInt(Math.log(y_max) / Math.log(10)) - 1), 1), (y_max / 15 / 5).ceil() * 5)),
	        labelRenderer : Ext.util.Format.numberRenderer('0,0')
	    });
	    axes.push(y_axis1);
	    		    
	    var y_min_sec = y_mins["secondary"].min();
	    var y_max_sec = y_maxs["secondary"].max();
	    if (y_min_sec == y_max_sec) {
	    	y_min_sec = 0;
	    }
	    var y_axis2 = new Ext.chart.NumericAxis({
	        title: title,
	        alwaysShowZero: true,
	        stackingEnabled: true,
	        position: "left",
	        order: "secondary",
	        majorUnit: (y_max_sec - y_min_sec) <= 15 ? 1 : (Math.max(Math.max(Math.pow(10, parseInt(Math.log(y_max_sec) / Math.log(10)) - 1), 1), (y_max_sec / 15 / 5).ceil() * 5)),
	        labelRenderer : Ext.util.Format.numberRenderer('0,0')
	    });
	    axes.push(y_axis2);
	} else {
		var y_axis = new Ext.chart.NumericAxis({
	        title: title,
	        stackingEnabled: true,
	        alwaysShowZero: y_min < 10,
	        order: "primary",
	        position: "left",
	        majorUnit: (y_max - y_min) <= 15 ? 1 : (Math.max(Math.max(Math.pow(10, parseInt(Math.log(y_max) / Math.log(10)) - 1), 1), (y_max / 15 / 5).ceil() * 5)),
	        labelRenderer : Ext.util.Format.numberRenderer('0,0')
	    });
	    axes.push(y_axis);	    
	}

	return axes;
}

function build_graph() {
    // Set up parameters for graphs based on page_type
	if (page_type == "dataset") {
		var graph_type = {};
		graph_type = Ext.copyTo(graph_type, gQuery.datasets[0], ["dataset_id", "ds_type", "platform", "sp_obs_count", "auto", "table2"]);
		if (["ptacs", "ptacs_calls"].indexOf(graph_type.ds_type) > -1 && graph_type.platform == "station" && graph_type.dataset_id != 668) {
			// Hourly plot doesn't make sense for Dataset 668 (the button to be disabled, requested by Melissa on 2011-11-17)
			Ext.getCmp("btn_hourly").show();
		}
		if (!Ext.getCmp("btn_show_tracks").pressed) {
			graph_type.auto = "f";
		}
	} else {
		var graph_type = {};
	}
    
	// Graph store
    var chart_id = "linechart";
    var graph_params = {
		remoteSort: true,
		autoLoad: false,
		proxy: new Ext.ux.OBISProxy(),
		dirty: true,
		y_axis: "record",
		reader: new Ext.ux.OBISJsonReader({   
		    id: 'x_value',
		    fields: get_graph_fields(graph_type)
		}),
		listeners: {
			beforeload: function(store, options){
				if (this.dirty == false) {
					return false;
				}
				this.remoteSort = true;
				var mode = gQuery.useZ() ? "graph_z" : "graph";
								
				Ext.getCmp(chart_id).setDisabled(true);
				var button = Ext.getCmp("hide_empty_periods");
				// To draw proper graphs and preserve the time range chosen, no need to pass temporal (gQuery.temporal).
				// Otherwise, the paging does not work as the query would include temporal criteria and return incorrect results.
				var params = {mode: mode, temporal_scale: gQuery.temporal_scale, hide_empty: button.pressed, random: get_random()};
				if (["record", "animal", "species"].indexOf(this.y_axis) == -1) {
					params.mode = "graph_oceano";
				}
				if (Ext.getCmp("btn_histogram").pressed) {
					params.mode = "graph_oceano_histogram";
				}
				if (page_type == "dataset") {
					params.graph_type = Ext.encode(graph_type);
				}
				
				graph_type.layer_type = (params.mode.indexOf("oceano") == -1 && gClient.layer_status["points"] == 1) ? "points" : "dist_sp";
				Ext.getCmp(chart_id).series = get_graph_series(graph_type);
				
 				params.count_type = this.y_axis;
				params = Ext.apply(options.params, params);
				options.params = Ext.apply(gQuery.toQueryObject(), params);
				this.lastQuery = options.params;
			},
			load: function(store){
				this.remoteSort = false;
				/* Don't remember why x_value = 0 has to be removed.
				This now affects the diel plots eliminating Hour 0. So commented out.
				var null_index = this.find("x_value", "0", 0 , false);
				if (null_index > -1) {
					this.removeAt(null_index);
				}
				*/
				this.dirty = false;
				build_range_select();
				update_graph_time_range();
				Ext.getCmp(chart_id).setDisabled(false);
			},
			exception: store_exception
		},		  	
		sortInfo:{field: 'x_value', direction: "ASC"}
	};
	
	var graph_params_paging = Ext.apply(graph_params, {id: "store_graph", baseParams: {mode: "graph", start:0, limit: gClient.graph_page_size}});
	store_graph = new Ext.data.Store(graph_params_paging);
	var graph = new Ext.chart.StackedColumnChart({
	    id: chart_id,
	    store: store_graph,
	    xField: 'x_value',
	    //series: get_graph_series(page_type, graph_type),
	    boxMaxHeight: 320,
	    flashParams: {quality: "low"},
	    xAxis: new Ext.chart.CategoryAxis({		// Default. Will be modified on beforerefresh.
	        title: "year",
	        alwaysShowZero: false
	    }),											
	    yAxis:  new Ext.chart.NumericAxis({		// Default. Will be modified on beforerefresh.
	    	stackingEnabled: true,
	    	title: '#records',
	        majorUnit: 1000
	    }),
	    extraStyle: {
			yAxis:{titleRotation: -90}
		},
		listeners: {
			render: function(){gClient.registerView(this.id)},
			beforerefresh: function(o) {
				// modify the X Axis of the graph depending on the temporal scale - Label, title
				var store = this.store;
				if (store.getCount() == 0) {
					this.setWidth(150);
					this.setDisabled(true);
					return true;
				}
				if (gQuery.temporal_scale >= 0 ||gQuery.temporal_scale == -4) {
					var bar_width = 30;
				} else {
					var bar_width = 50;
				}
				var histogram = Ext.getCmp("btn_histogram").pressed;
				if (["record", "animal", "species"].indexOf(store_graph.y_axis) == -1) {	// oceanographic variable
					if (histogram) {
						this.series[0].type = "column";														
					} else {
						this.series[0].type = "line";													
					}
				} else {
					//this.series[0].type = "column";	
				}
				var count = this.store.data.keys.length;
				var y_mins = {primary: [], secondary: []};
				var y_maxs = {primary: [], secondary: []};
				this.series.each(function(the_series){
					var y_values = 	store.data.items.collect(function(item){return item.data[the_series.yField]});
					var y_min = y_values.min();
					var y_max = y_values.max();
					if (isNumeric(y_min)) {
						y_mins[the_series.axis].push(y_min);
					}
					if (isNumeric(y_max)) {
						y_maxs[the_series.axis].push(y_max);
					}
				});
				
				var y_label_animals = (page_type == "dataset" && gQuery.datasets[0].ds_type == "ptacs_calls") ? "#calls" : "#animals";
				var y_labels = {record: "#records", animal: y_label_animals, species: "#species", sst: "average (\u2103)", ssh: "average (cm)", chl: "average (x10-2 mg*m-3)", bath: "average (m below)"};
			    var title =  histogram ? "Frequency" : y_labels[store.y_axis];
			    var y_axes = get_y_axes(store, graph_type, title, y_mins, y_maxs);
				
				var buffers = [63, 68, 74, 75, 85, 95, 105];
			    var y_min = y_mins["primary"].min();
			    var y_max = y_maxs["primary"].max();	
				var for_buffer = (store_graph.y_axis == "bath" && !histogram) ? y_min : Math.max(y_max, 10);
				var buffer = buffers[parseFloat((Math.log(Math.abs(for_buffer)) / Math.log(10)).toFixed(3)).floor()];
				
				// If there is secondary y-axis
				//if (this.series.length > 1) {
				if (y_maxs["secondary"].length > 0) {
				    //var y_min_sec = y_mins["secondary"].min();
				    var y_max_sec = y_maxs["secondary"].max();	
					var buffer1 = buffers[parseFloat((Math.log(Math.abs(y_max_sec)) / Math.log(10)).toFixed(3)).floor()];
					buffer += buffer1 - 20;
				}
				if (for_buffer < 0) {
					buffer += 5;
				}
				
				this.suspendEvents(false);
				var graph_width = (count > gClient.graph_page_size ? gClient.graph_page_size : count) * bar_width + buffer;
				this.setWidth(graph_width);		// Graph's initial width; 100: buffer
			    
				// modify the X Axis here so it reflects the temporal scale
				var x_labels = {"4": "Date (Year-Month-Day)", "3": "Year-Month", "2": "Year", "1": "Decade", "0": "Century", "-1": "\nSeason", "-2": "Month", "-4": "Hour"};
				var x_labels_histogram = {"sst": "Sea Surface Temperature (\u2103)", "ssh": "Sea Surface Height (cm)", "chl": "Chlorophyll (x10-2 mg*m-3)", "bath": "Bathymetry (m below)"};
				if (Ext.isIE) {
					// labelRenderer causes an runtime error with IE7+
					// Until this problem is solved, disable the customized labels on X-Axis.
			        var x_axis = new Ext.chart.CategoryAxis({
			            title: histogram ? x_labels_histogram[store_graph.y_axis] : x_labels[gQuery.temporal_scale],
			            alwaysShowZero: false
			        });												
				} else {
			        var x_axis = new Ext.chart.CategoryAxis({
			            title: histogram ? x_labels_histogram[store_graph.y_axis] : x_labels[gQuery.temporal_scale],
			            alwaysShowZero: false,
			            labelRenderer: function(value){
			            	if (histogram) {
			            		var zoom_levels = gClient.histogram_zoom_levels[store_graph.y_axis];
			            		value = value + "-" + (parseInt(value) + zoom_levels[gQuery.temporal_scale]);
			            	} else {
				            	switch (gQuery.temporal_scale) {
				            		/*
				            		case 4:
				            			value = value.substr(0, 4) + "-" + value.substr(4, 2) + "-" + value.substr(6, 2);
				            			break;
				            		case 3:
				            			value = value.substr(0, 4) + "-" + value.substr(4, 2);
				            			break;
				            		*/
				            		case 4:
				            			value = value.substr(0, 10);
				            			break;
				            		case 3:
				            			value = value.substr(0, 7);
				            			break;
				            		case 2:
				            			value = value.substr(0, 4);
				            			break;
				            		case 1:
				            			value = value.substr(0, 4) + "s";
				            			break;
				            		case 0:
				            			value = (parseInt(value.substr(0, 2)) + 1) + "th";
				            			break;
				            		case -1:
				            			var seasons = {"1": "Winter", "2": "Spring", "3": "Summer", "4": "Fall"};
				            			value = seasons[value];
				            			break;
				            	}
			            	}
			            	return value;
			            }
			        });
				}											
			    this.setXAxis(x_axis);
		    	this.setYAxes(y_axes);	// This is available in the latest YUI chart but not in EXTJS.
				this.resumeEvents();
			},
			itemclick: function(o){
				var rec = this.store.getAt(o.index);
			}
		}	        	
	});
	return graph;
}

function store_exception (proxy, type, action, options, response, arg) {
	ajax_failed(response, options);
}

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

// Override ext-all.js
// IE throws an invalid argument error when h - hd.parentNode.offsetHeight becomes negative 
// (at line 25664 in ext-all-debug.js)
// The following code overrides onResize method to fix the problem.
if (Ext.isIE) {
	Ext.override(Ext.list.ListView, {
			onResize : function(w, h){
		        var bd = this.innerBody.dom;
		        var hd = this.innerHd.dom
		        if(!bd){
		            return;
		        }
		        var bdp = bd.parentNode;
		        if(Ext.isNumber(w)){
		            var sw = w - Ext.num(this.scrollOffset, Ext.getScrollBarWidth());
		            if(this.reserveScrollOffset || ((bdp.offsetWidth - bdp.clientWidth) > 10)){
		                bd.style.width = sw + 'px';
		                hd.style.width = sw + 'px';
		            }else{
		                bd.style.width = w + 'px';
		                hd.style.width = w + 'px';
		                setTimeout(function(){
		                    if((bdp.offsetWidth - bdp.clientWidth) > 10){
		                        bd.style.width = sw + 'px';
		                        hd.style.width = sw + 'px';
		                    }
		                }, 10);
		            }
		        }
		        if(Ext.isNumber(h)){
		           	//bdp.style.height = (h - hd.parentNode.offsetHeight) + 'px';  original one line code
		           	// EF: IE throws an invalid argument error when h - xxx.offsetHeight goes negative.
		        	if (h - hd.parentNode.offsetHeight > 0) {
		            	bdp.style.height = (h - hd.parentNode.offsetHeight) + 'px';
		        	} else {
		        		bdp.style.height = '0px';
		        	}
		        }	
		    }    
		});	
}

// Override Button so the tooltip property does not raise an error when the menu is being open.
Ext.override(Ext.Button, {
    showMenu : function(){
        if(this.rendered && this.menu){
            if(this.tooltip && this.tooltipType == 'qtip'){
                Ext.QuickTips.getQuickTip().cancelShow(this.btnEl);
            }
            if(this.menu.isVisible()){
                this.menu.hide();
            }
            this.menu.ownerCt = this;
            this.menu.show(this.el, this.menuAlign);
        }
        return this;
    }
});	




Ext.ux.OBISProxy = Ext.extend(Ext.data.HttpProxy, {
	constructor: function(config){
        var config = Ext.apply({
            url: ROOT_URL + BASE_URL + PORTAL_MAIN,
            method: 'POST'
        }, config);
		Ext.ux.OBISProxy.superclass.constructor.call(this, config);
	}
});

Ext.ux.OBISJsonReader = Ext.extend(Ext.data.JsonReader, {
	constructor: function(config){
        var config = Ext.apply({
            root: 'records',
            totalProperty: 'num_records'
        }, config);
		Ext.ux.OBISJsonReader.superclass.constructor.call(this, config);
	}
});

Ext.ux.OBISGroupingStore = Ext.extend(Ext.data.GroupingStore, {
	//root: "records",
	//totalProperty: "num_records",
	groupField: "group_field",
	remoteGroup: true,
	remoteSort: true,
	sortInfo:{field: 'entity_name', direction: "ASC"}
});


/*****
<<< Multi-select Selection Model >>>
This component allows for multiple selection in a grid panel.
With the EXTJS default selection model, the user needs to press CTL to select multiple rows.
With this component, the user can select multiple rows by a single click. Clicking the selected
row deselects it.
*****/
Ext.ux.RowSelectionModelMSBSC = Ext.extend(Ext.grid.RowSelectionModel, {
	singleSelect: false,
	multiSelectWithClick: false,
	deselectWithClick: true,
	
	/* override selectRow */
    handleMouseDown : function(g, rowIndex, e){
        if(e.button !== 0 || this.isLocked()){
            return;
        };
        var view = this.grid.getView();
        if(e.shiftKey && !this.singleSelect && this.last !== false){
            var last = this.last;
            this.selectRange(last, rowIndex, e.ctrlKey);
            this.last = last; // reset the last
            view.focusRow(rowIndex);
        }else{
            var isSelected = this.isSelected(rowIndex);
            if((e.ctrlKey && isSelected) || (this.deselectWithClick && isSelected)){
                this.deselectRow(rowIndex);
            }else if(!isSelected || this.getCount() > 1){
                this.selectRow(rowIndex, e.ctrlKey || e.shiftKey || this.multiSelectWithClick);
                view.focusRow(rowIndex);
            }
        }
    }	 
});

// Instantiate EXTJS Grid for species/dataset list
// type = [species|datasets|species_on_map|datasets_on_map]
function build_grid(type) {
	var store_id = "store_" + type;
	var grid_id = "grid_" + type;
	var group_field = "group_field";
	var param_list = null;
	var register = false;
	var columns = get_columns(type);
	var auto_expand_column = "entity_name";
	switch (type) {
		case "datasets_on_map":
		//case "contributing_datasets":
			var mode = type;
			var fields = dataset_json_fields;
			var id_column = "dataset_id";
			var tip = "Press to select multiple datasets";
			//register = page_type == "search";
			register = true;
			var rs_panel = rs_panel_datasets;
			break;
		case "species_on_map":
		case "species_in_dataset":
			var mode = type;
			var fields = species_json_fields;
			var id_column = page_type == "search" ? "sp_tsn" : "sp_obs";
			var tip = "Press to select multiple species";		
			//register = page_type == "search";
			register = true;
			var rs_panel = rs_panel_species;
			break;
		case "species":
			var mode = type;
			var fields = species_json_fields;
			var id_column = "sp_tsn";
			group_field = "sp_rank";
			param_list = ["storedpath", "parent_id", "publish"];
			var tip = "Press to select multiple species";		
			var rs_panel = rs_panel_species;
			break;
		case "datasets":
			var mode = type;
			var fields = dataset_json_fields;
			var id_column = "dataset_id";
			group_field = "";
			param_list = ["provider_id", "keyword", "publish"];
			var tip = "Press to select multiple datasets";
			var rs_panel = rs_panel_datasets;
			break;
		case "series":		// series is not a page_type but tab and grid is built for telemetry dataset.
			var mode = "series_list_z";
			var fields = series_json_fields;
			var id_column = "series";
			var rs_panel = rs_panel_series;
			auto_expand_column = "series_name";
			register = true;
			break;
		case "fixed_location":		// pthab or platform = station (acoustic datasets)
			var mode = "location_list";
			if ([270, 418, 545].indexOf(parseInt(gQuery.datasets[0].dataset_id)) == -1) {
				group_field = null;
			}
			var fields = series_json_fields;
			var id_column = "series";
			var rs_panel = rs_panel_fixed_location;
			register = true;
			break;
		case "photo":
			var mode = "series_list_photo";
			var fields = photo_json_fields;
			var id_column = "series";
			auto_expand_column = "series_name";
			var group_field = "";
			var rs_panel = rs_panel_series;		// Temporarily
			break;
	}
	
	var params = {mode: mode, start:0, limit:50};
	var store = new Ext.data.GroupingStore({
		groupField: group_field,
		storeId: store_id,
		dirty: true,
		remoteGroup: true,
		remoteSort: true,
		autoLoad: false,
		proxy: new Ext.ux.OBISProxy(),
		baseParams: params,
		listeners: {
			beforeload: function(store, options){
				if (this.dirty == false) {
					return false;
				}
				store.remoteSort = true;
				store.remoteGroup = true;
				var params = gQuery.toQueryObject();
				if (param_list) {
					options.params = Ext.copyTo(options.params, params, param_list);
				} else {
					options.params = Ext.apply(options.params, params);
				}
				if (this.storeId != "store_datasets" && this.storeId != "store_species") {
					options.params.mode = (gQuery.useZ() && this.baseParams.mode.indexOf("_z") == -1) ? this.baseParams.mode + "_z" : this.baseParams.mode;
				}
				
				if (page_type == "species") {
					Ext.destroyMembers(options.params, "datasets");
				}
				if (page_type == "dataset" && this.storeId == "store_species_in_dataset") {
					Ext.destroyMembers(options.params, "species");
					options.params.is_default = gQuery.isDefault(true);
				}
				//if (init_window == 'datasets' && this.storeId == "store_datasets") {
					// Sort column and direction are in effect for store_datasets only at this moment.
					var sortInfo = this.getSortState();
					 options.params.sort = sortInfo.field;
					 options.params.dir = sortInfo.direction;
				//}
				this.lastQuery = options.params;
			},
			load: data_loaded,
			exception: store_exception
		},
		reader: new Ext.ux.OBISJsonReader({   
		    id: id_column,
		    fields: fields
		}),
		sortInfo:{field: 'entity_name', direction: "ASC"}
	});	
	
	cm = new Ext.grid.ColumnModel({defaults:{sortable: true}, columns: columns});
	
	gv = new Ext.grid.GroupingView({
		groupTextTpl: '{group} ({[values.rs.length]})'
	});
	
	var tb = Ext.Toolbar;
	if (type != "datasets") {
		var btn_sort = new tb.Button({id: "btn_sort_" + type, text: "Sort by name", handler: sort_by_name, tooltip: "Sort by dataset name"});
		var btn_collapse = new tb.Button({id: "btn_collapse_" + type, text: "Collapse all", handler: collapse_all, tooltip: "Collapse group view"});
		var btn_save_list = new tb.Button({id: "btn_save_list_" + type, text: "Save list", xtype: "splitbutton", iconCls: 'icon_save', tooltip: "Save the current list as a CSV file",
	    	menuAlign: "tr-br",
			menu: {showSeparator: false, defaultOffsets: [0, 3], width: Ext.isIE ? 120 : 'auto',
    			defaults: {handler: save_list_as}, as_rest: false,
    			items: [
    				{text: "as CSV", value: "csv"},
    				{text: "as JSON", value: "json"},
    				{text: "as XML", value: "xml"},
    				{text: "<hr class='separator' />", xtype: "menutextitem"},
    				{text: "REST URL", value: "rest", xtype: "menucheckitem", hideOnClick: false, handler: null,
    					checkHandler: function(button, checked){button.parentMenu.as_rest = checked;}}
    			]
	    	}		
		});
	}
    var btn_help = new tb.TextItem({text: "<div> </div>", id: "help_" + grid_id, xtype: "tbtext", cls: 'x-tool x-tool-help', style: "padding: 0px", overCls: "x-tool-help-over"});
	var btn_fill = new tb.Fill();
	var btn_multi_select = new tb.Button({id: "btn_multi_select_" + type, text: "Multi-select", enableToggle: true, toggleHandler: function(item, pressed){Ext.getCmp(grid_id).getSelectionModel().multiSelectWithClick = pressed}, pressed: false, tooltip: tip});
	if (type != "species_on_map" && type != "datasets_on_map") {
		var btn_show_all = new tb.Button({id: "btn_show_all_" + type, text: "Clear selection", handler: show_all, tooltip: "Clear user-made selection and show all observations"});
	}	
	switch (type) {
		case "series":
			// Animation controls
			var btn_label = new tb.TextItem('Animation:');
			var btn_pause = new tb.TextItem('<img class="button" src="' + gIconBase + 'animation_pause.png" onclick="animation_pause();" TITLE="Pause animation. Click Start to resume.">');
			var btn_stop = new tb.TextItem('<img class="button" src="' + gIconBase + 'animation_stop.png" onclick="terminate_animation();" TITLE="Stop animation. Marker will disappear.">');
			var btn_start = new tb.TextItem('<img class="button" src="' + gIconBase + 'animation_run.png" onclick="animation_prepare();" TITLE="Start animation for animal selected">');
			var btn_animation_time = new tb.TextItem('<span id="animation_time"></span>');	
			var tbar_buttons = [btn_sort, '-',btn_collapse, '-', btn_label, btn_pause, btn_stop, btn_start, btn_animation_time, '-', btn_fill, btn_multi_select, '-', btn_show_all, '-', btn_save_list, " ", btn_help];
			if (gQuery.datasets[0].dataset_id == 477) {
				var btn_filtered_out = new Ext.Toolbar.Button({text: "Show filtered-out points", id: "btn_filtered_out", disabled: true, enableToggle: true, toggleHandler: toggle_filtered_out, pressed: false, tooltip: "Press to show filtered-out points"});
				tbar_buttons.splice(10, 0, btn_filtered_out);	// insert two additional buttons '-' and btn_filtered_out
			} 		
			break;
		case "datasets":
			var tbar_buttons = [btn_fill, btn_multi_select, '-', btn_show_all];				
			break;
		case "species_on_map":
		case "datasets_on_map":
			var label = String.format("Observe {0} selection", type.substr(0, 7));
			var tbar_buttons = [btn_sort, '-', btn_collapse, btn_fill, btn_multi_select, '-', btn_save_list, " ", btn_help];
			break;
		case "species":
			var tbar_buttons = [btn_sort, '-',btn_collapse, btn_fill, btn_multi_select, '-', btn_show_all];
			break;
		default:
			var tbar_buttons = [btn_sort, '-',btn_collapse, btn_fill, btn_multi_select, '-', btn_show_all, '-', btn_save_list, " ", btn_help];
	}
		
    var grid = new Ext.grid.GridPanel({
    	id: grid_id,
        title:'List of species/datasets', header: false,
        store: store,
        trackMouseOver:true,
        loadMask: true,
		autoHeight: false,
		cm: cm,
		sm: new Ext.ux.RowSelectionModelMSBSC(),
	    view: gv,
		stripeRows: true,
		autoExpandColumn: auto_expand_column,
		stateful: false,
		tbar: {
			defaults:{scale: "medium", tooltipType: "title"},
			items: tbar_buttons
		},
        bbar: new Ext.PagingToolbar({	// paging bar on the bottom
            pageSize: 50,
            store: store,
            displayInfo: true,
            displayMsg: rs_panel.bbar.displayMsg,
            emptyMsg: rs_panel.bbar.emptyMsg,
            listeners: {
            	beforechange: function() {Ext.getCmp(grid_id).store.dirty = true;}
            }
        }),
	    listeners: {
	    	render: function(){if (register) {gClient.registerView(this.id);}},
	    	afterrender: function(){if ($("help_" + this.id)) $("help_" + this.id).observe("click", function(){show_help_tip(this.id.replace('help_', ''))});},
	    	rowclick: row_clicked
	    }
        
    });

    return grid;
}
/*****
<<< Taxonomic Tree Loader >>>
Spposed to be used as a custom loader for a tree panel.
This is the default loader for TaxonTreePanel defined below.
*****/
Ext.ux.TaxonTreeLoader = Ext.extend(Ext.tree.TreeLoader, {
	dataUrl: ROOT_URL + BASE_URL + PORTAL_MAIN,
	baseParams2: {parent_id: 331030, mode: "tree"},
	nodeParamsDef: {sp_tsn: "parent_id"},
	
	// Define the fields to extract from the taxon table and give them aliases that are used to refer to them in Javascript.
	// {"table_field_name": "alias", ...}
	/*
	fieldMaps: {"tu_id": "tu_acctaxon", "scientific": "tu_displayname", "tu_rank": "tu_rank"},
	attachFieldMaps: function() {
		// every time, the field maps are appended to the dataUrl and sent to the server.
		// format: table_field_name:alias,table_field_name:alias,...
		var s = $H(this.fieldMaps).toQueryString();
		s = s.replace(/=/g, ":").replace(/&/g, ",");
		return s;
	},
	*/
	attachNodeParams: function(node) {
		var params = [];
		$H(this.nodeParamsDef).each(function (pair) {
			params[pair.value] = node.attributes[pair.key];
		});
		return params;
	},
	
	build_parameters: function(treeLoader, node) {
		//this.baseParams.fieldMaps = this.attachFieldMaps();
		var nodeParams = this.attachNodeParams(node);
		this.baseParams = $H(this.baseParams2).merge(nodeParams).toObject();
	},
	
	listeners: {
		beforeload: function(treeLoader, node) {this.build_parameters(treeLoader, node);}
	}
	
});

/*****
<<< Taxonomic Tree Panel >>>
Combined with Taxonomic Tree Loader defined above.
*****/
Ext.ux.TaxonTreePanel = Ext.extend(Ext.tree.TreePanel, {
	nodeTextField: "scientific",
	nodeIdField: "sp_tsn",
	leafKey: {sp_rank: "Species"},
	treeDelimiter: "|",
	loader: new Ext.ux.TaxonTreeLoader(),
	
    initComponent:function() {
        // call parent init component
        Ext.ux.TaxonTreePanel.superclass.initComponent.apply(this, arguments);
		this.getSelectionModel().on("selectionchange", this.taxon_node_clicked);        
    },
	
	prepareNode: function(theTree, parentNode, thisNode) {
		if (this.nodeIdField != "") {
			thisNode.id = thisNode.attributes[this.nodeIdField];
		}
		thisNode.text = this.setNodeText(theTree, parentNode, thisNode);
		thisNode.leaf = thisNode.attributes.sp_rank == "Species" ? true: false;
	},
	
	setNodeText: function(theTree, parentNode, thisNode) {
		if (thisNode.attributes.text == undefined || thisNode.attributes.text == "") {
	    	var s = thisNode.attributes[this.nodeTextField];
		} else {
			var s = thisNode.attributes.text;
		}
		return s;
	},
	taxon_node_clicked: function(selModel, node) {
		//this.fireEvent("taxonselect", this, selModel, node);
	},
	
	climb_down_to: function(tree, delimiter, index) {
		var branches = tree.split(delimiter);
		var taxon_tree = this;
		var node = this.getNodeById(branches[index]);		
		if (index < branches.length - 1) {
			var callback = function() {taxon_tree.climb_down_to(tree, delimiter,  index + 1)};
			node.expand(false, false, callback);
		} else {
			if (node.isLeaf()) {
				node.parentNode.select();	
			} else {
				node.select();
			}
		}
			
	},
	
	climb_down_to2: function(tree, target_tsn, delimiter, index) {
		var params = {mode: "tree2", storedpath: tree, target_tsn: target_tsn, tree_id: this.id};
		Ext.Ajax.request({
		   url: ROOT_URL + BASE_URL + PORTAL_MAIN,
		   //success: this.climb_down_to_success.bindAsEventListener(this),
		   success: this.climb_down_to_success,
		   failure: this.climb_down_to_failure,
		   params: params
		});		
	},

	climb_down_to_success: function(oj, opts) {
		var tree_data = oj.responseText.evalJSON();
		var tree = tree_data.records;
		//var grid_tree = this;
		var grid_tree = Ext.getCmp(opts.params.tree_id);
		var target_child = null;
		var delimiter = grid_tree.treeDelimiter;
		var id_field = grid_tree.nodeIdField;
		tree.each(function(item){
			var parents = item.storedpath.split(delimiter);
			var parent_id = parents[parents.length - 2];
			var node = grid_tree.getNodeById(parent_id);
			
			if (!grid_tree.getNodeById(item[id_field])) {
				var child = node.appendChild(item);
				if (!node.isLeaf() && !node.isExpanded()) {
					node.loaded = true;
					node.expand(false, false);
				}
			} else {
				var child = grid_tree.getNodeById(item[id_field]);
			}
			//if (parseInt(child.attributes[id_field]) == gQuery.species[0][id_field]) {
			if (parseInt(child.attributes[id_field]) == opts.params.target_tsn) {
				target_child = child;
			}
		});
		target_child.select();
		target_child.ensureVisible();
	},
		
	listeners: {
		beforeappend: function(theTree, parentNode, thisNode) {this.prepareNode(theTree, parentNode, thisNode);}
	}
	
});

Ext.ux.ClientWindow = Ext.extend(Ext.Window, {
    renderTo: 'map_panel',
    constrain: true,
    maximized: true,
    width:800,
    height:400,
    closeAction:'hide',
    maximizable: true,
    plain: false,
    // Defining tools here ruins the tools bottons. Guess the buttons refer to the same window instance (win_map).
    // So need to define tools on each window instance.
    //tools: [{id: 'help', qtip: "Show online help for this window", handler: function(event, toolEl, panel, tc){panel.show_help_tip()}}],
    defaults: {split: true}/*,
    show_help_tip: function (){
    	var container = "div_help_tip";
		var win = new Ext.Window({
			id: "help_window",
			title: "Help for " + this.title,
		    renderTo: this.renderTo,
			html: '<div id="' + container + '"></div>',
			autoScroll: true,
			width       : 700,
			height      : 450,
			closeAction :'close',
			padding: "10px",
			plain       : true,
			bodyCfg: {
		        cls: 'help_window'
		    }
		});
		win.show();
		// Not a clever way to append language code from gClient.language here
		var url = "contents/help_" + this.id + "_" + gClient.language + ".html" + "?" + get_random();
		new Ajax.Updater(container, url);	
    }*/
});

/*** Plugin for stay-on-top window
http://www.extjs.com/forum/showthread.php?t=47579
Usage: new Ext.Window({..., plugin: new Ext.ux.WindowAlwaysOnTop});

***/
Ext.ux.WindowAlwaysOnTop = function(){
       this.init = function(win){
            win.on('deactivate', function(){
               var i=1;
               this.manager.each(function(){i++});
               this.setZIndex(this.manager.zseed + (i*10));
            })
       }
}


/***** Non-EXT objects *****/
var SeamapRoi = Class.create();

SeamapRoi.prototype = {
	mode: "",
	dirty: false,		// indicate that the polygon(s) is updated and that the query/map should be updated.	
	rois: [],	// each element should have {mode: "", geometry: null} where mode = [fixed_box|regular|polygon}
	poly_colors: ["#FFFF00", "#66FF00", "#FF33CC", "#FF0066", "#00FFFF"],
	transparency: 0.7,
	user_interface: {menu_id: "", latlon_box: {}, roi_editor: ""},
	step: 0,
	in_edit: false,
	
	first_point: null,
	second_point: null,
	extent: null,
	
	// Some event-driven functions for a specific application
	// mainly intended to do extra work when a polygon added or removed such as interface change...
	post_added: null,
	post_remove: null,
	post_modified: null,
	
	initialize: function(user_interface) {
		//var callbacks = {create: this.on_create.bind(this), done: this.added.bind(this)};
	    this.user_interface = $H(this.user_interface).merge(user_interface).toObject();
	},
	
	on_create: function() {
	},
	
	snap: function(feature) {
		var bbox = feature.geometry.getBounds().toArray();
		var radius = this.get_radius();
		var snapped_w = (bbox[0] / radius).round() * radius;
		var snapped_s = (bbox[1] / radius).round() * radius;
		var bbox_snapped = [snapped_w, snapped_s, snapped_w + radius, snapped_s + radius];
		feature.geometry.move(snapped_w - bbox[0], snapped_s - bbox[1]);
		return feature;
	},
	
	get_radius: function() {
		var menu = Ext.getCmp(this.user_interface.menu_id);
		var checked_radius = menu.items.items.find(function(item){return item.checked});
		var radius = checked_radius.value;
		return radius;		
	},
	
	fixed_box_radius: function(value) {
		var radius = value * Math.sqrt(2) / 2;
		return radius;
	},
	
	add_to_layer: function(feature) {
	},
	
	enable_edit: function(mode) {
	},
	
	geomAsText: function(digits) {
		if (digits == null) {
			digits = 8;
		}
		
		if (this.rois.length == 0) {
			return "";
		}
		
		var all_geom = [];
		for (var i = 0; i < this.rois.length; i++) {
			var geom = this.rois[i].geometry;
			
			switch (this.rois[i].mode) {
				case "zone":
					var as_text = Ext.encode(geom);
					break;
				case "shapefile":
					var as_text = geom.coords;
					break;
				default:
					var points = [];
					for (var j = 0; j < geom.getVertexCount(); j++) {
						var aVertex = geom.getVertex(j);
						var coordinates = aVertex.lng().toFixed(digits) + " " + aVertex.lat().toFixed(digits);
						points.push(coordinates);
					}
					var as_text = points.join(",");
			}
			all_geom.push(as_text);
		}
		return all_geom.join(";");
	},
	
	update_latlon_boxes: function(roi_index) {
		$("current_roi_index").value = roi_index;
		var boxes = this.user_interface.latlon_box;
		if (roi_index > -1) {
			var geom = this.rois[roi_index].geometry;
			var bounds = geom.getBounds();
			var sw = bounds.getSouthWest();
			var ne = bounds.getNorthEast();
			
			$(boxes.south).value = sw.lat();
			$(boxes.west).value = sw.lng();
			$(boxes.north).value = ne.lat();
			$(boxes.east).value = ne.lng();
		} else {
			$H(boxes).each(function(pair){
				$(pair.value).value = "";
			});
		}
	},

	clear_editor: function(which) {
		switch (which) {
			case "regular":
				if ($(this.user_interface.latlon_box.north)) {
					var boxes = this.user_interface.latlon_box;
					$H(boxes).each(function(pair){$(pair.value).value = ""});
				}
				break;
			case "polygon":
				var editor = Ext.getCmp(this.user_interface.roi_editor);
				if (editor != null && editor.rendered) {
					var store_roi = editor.store;
					store_roi.removeAll();
				}
		}
	}
}


/***** override chart *****/
Ext.chart.Chart.CHART_URL = '/seamap2.5/js/ext-3.1.1/resources/charts_v2_8.swf';

Ext.override(Ext.FlashComponent, {

  /* changes are done in order to be able to use YUI Library - Charts 2.8.0
  * The following changes have been made to the charts.swf. (This will not impact the behavior or API of the Charts Control)
  - The flashvar elementID has changed to YUISwfId
  - The flashvar eventHandler has changed to YUIBridgeCallback
  */
  flashVersion : '9.0.45',

  onRender : function(){
      Ext.FlashComponent.superclass.onRender.apply(this, arguments);

      var params = Ext.apply({
          allowScriptAccess: 'always',
          bgcolor: this.backgroundColor,
          wmode: this.wmode
      }, this.flashParams),
      vars = Ext.apply({
          allowedDomain: document.location.hostname,
          YUISwfId: this.getId(),
          YUIBridgeCallback: 'Ext.FlashEventProxy.onEvent'
      }, this.flashVars);

      new swfobject.embedSWF(this.url, this.id, this.swfWidth, this.swfHeight, this.flashVersion,
          this.expressInstall ? Ext.FlashComponent.EXPRESS_INSTALL_URL : undefined, vars, params);

      this.swf = Ext.getDom(this.id);
      this.el = Ext.get(this.swf);
  }
});

Ext.override(Ext.chart.CartesianChart, {
  onSwfReady : function(isReset){
      Ext.chart.CartesianChart.superclass.onSwfReady.call(this, isReset);

      if(this.xField){
          this.setXField(this.xField);
      }
      if(this.yField){
          this.setYField(this.yField);
      }
      if(this.xAxis){
          this.setXAxis(this.xAxis);
      }
      if(this.yAxis){
          this.setYAxis(this.yAxis);
      }
      if(this.yAxes){
          this.setYAxes(this.yAxes);
      }
      if (this.constrainViewport !== undefined) {
          this.setConstrainViewport(this.constrainViewport);
      }
  },

  setYAxes : function(value){
    for(var i = 0; i < value.length; i++) {
      var axis = this.createAxis('yAxis' + i, value[i]);
      this.swf.setVerticalAxis(axis);
    }
  },

  setConstrainViewport: function(value) {
    this.swf.setConstrainViewport(value);
  }
});


Ext.override(Ext.chart.Axis, {
  /**
   * The space, in pixels, between labels on an axis.
   *
   * @property labelSpacing
   * @type Number
   */
  labelSpacing: 2,

  /**
   * The text that will appear next to the axis to indicate information about the data that it displays.
   *
   * @property title
   * @type String
   */
  title: null
});

Ext.override(Ext.chart.NumericAxis, {
  /**
   * Indicates whether to round the major unit.
   *
   * @property roundMajorUnit
   * @type Boolean
   */
  roundMajorUnit: true,

  /**
   * Indicates whether to factor in the size of the labels when calculating a major unit.
   *
   * @property calculateByLabelSize
   * @type Boolean
   */
  calculateByLabelSize: true,

  /**
   * Indicates the position of the axis relative to the chart
   *
   * @property position
   * @type String
   */
  position:"left",

  /**
   * Indicates whether to extend maximum beyond data's maximum to the nearest
   * majorUnit.
   *
   * @property adjustMaximumByMajorUnit
   * @type Boolean
   */
  adjustMaximumByMajorUnit:true,

  /**
   * Indicates whether to extend the minimum beyond data's minimum to the nearest
   * majorUnit.
   *
   * @property adjustMinimumByMajorUnit
   * @type Boolean
   */
  adjustMinimumByMajorUnit:true
});

Ext.override(Ext.chart.TimeAxis, {
  /**
   * Series that are stackable will only stack when this value is set to true.
   *
   * @property stackingEnabled
   * @type Boolean
   */
  stackingEnabled: false,

  /**
   * Indicates whether to factor in the size of the labels when calculating a major unit.
   *
   * @property calculateByLabelSize
   * @type Boolean
   */
  calculateByLabelSize: true
});

Ext.override(Ext.chart.CategoryAxis, {
  /**
   * Indicates whether or not to calculate the number of categories (ticks and labels)
   * when there is not enough room to display all labels on the axis. If set to true, the axis
   * will determine the number of categories to plot. If not, all categories will be plotted.
   *
   * @property calculateCategoryCount
   * @type Boolean
   */
  calculateCategoryCount: false
});

Ext.override(Ext.chart.CartesianSeries, {
  /**
   * Indicates which axis the series will bind to
   *
   * @property axis
   * @type String
   */
  axis: "primary",

  /**
   * When a Legend is present, indicates whether the series will show in the legend.
   *
   * @property showInLegend
   * @type Boolean
   */
  showInLegend: true
});
/***** end of chart updates *****/
