function Calculator(){
	//check for compatible browsers
	if(!GBrowserIsCompatible()){ 
		document.getElementById("map").innerHTML = "<p style='margin:15px'>Sorry, your browser will not work with Google Maps!<br /><br />  For a better browsing experience: <a href='http://www.mozilla.com/en-US/firefox/'>Download Firefox</a>'";		
	}

	var This = this;
	
	this.map = new GMap2(document.getElementById("map"));
	var control = new GSmallMapControl();
	var pos = new GControlPosition(G_ANCHOR_TOP_RIGHT, 0);

	this.map.addControl(control, pos);
	//this.map.addControl(new GMapTypeControl());
	this.map.setCenter(new GLatLng(36, -95), 3);
	
	this.geocoder = new GClientGeocoder();
	this.form = document.calc;	

	//storage for markers
	this.markers = new Object();
	this.markers['drive']  = new Array();
	this.markers['fly']	  = new Array();	
	this.markers['rail_bus'] = new Array();

	this.points = new Object();
	this.points['drive']  = new Array();
	this.points['fly']	  = new Array();	
	this.points['rail_bus'] = new Array();

	this.lines = new Object();
	this.lines['drive']  = new Array();
	this.lines['fly']	  = new Array();	
	this.lines['rail_bus'] = new Array();

	//storage for distances
	this.total = new Object();
	this.total['drive']  = 0;
	this.total['fly']	  = 0;	
	this.total['rail_bus'] = 0;
	
	//storage for trips
	this.trips = new Object();
	this.trips['drive']  = new Array();
	this.trips['fly']	  = new Array();	
	this.trips['rail_bus'] = new Array();

	this.ids = new Object();
	this.ids['drive']  = new Array();
	this.ids['fly']	  = new Array();	
	this.ids['rail_bus'] = new Array();

	//line colors
	this.line_colors = {
			drive:"#CC0000",
			fly:"#00CC00",
			rail_bus:"#0000CC"
	};

	this.inflect = {
			drive:"driving",
			fly:"flying",
			rail_bus:"travel by rail/bus"
	};
	
	//bounds
	This.bounds = new Object();
	This.bounds['drive'] = false;
	This.bounds['fly'] = false;
	This.bounds['rail_bus'] = false;

	//placeholders prevent trip overlapping..
	this.current_method = 'drive';
	this.current_distance = null;
	this.cur_to_mark = null;
	this.cur_from_mark = null;
	this.cur_to_point = null;
	this.cur_from_point = null;
	this.cur_line = null;	
	this.distance = 0;

	this.lookup_timer = null;
	this.to_lookup_after_type = null;	
	this.keyup_timer = function(object){
		This.to_lookup_after_type = object;
		window.clearTimeout(This.lookup_timer);
		This.lookup_timer = window.setTimeout(This.timer_lookup, 2000);
	}
	
	this.timer_lookup = function(){
		if(This.to_lookup_after_type.value == "") return false;
		This.enter_point(This.current_method, This.to_lookup_after_type);
	}

	this.enter_point = function(method, object){
		if(object.value == "") return false;
		var input = object;
		var to = object.getAttribute("name").indexOf("_to") > 0; 
		if(to){
			document.getElementById("not_found_to_" + method).style.display = 'none';
			document.getElementById("not_found_to_" + method).innerHTML = "";
		} else {		
			document.getElementById("not_found_from_" + method).style.display = 'none';
			document.getElementById("not_found_from_" + method).innerHTML = "";		
		}
		if(to &&  This.cur_to_mark != null){   This.map.removeOverlay(This.cur_to_mark);  } 
		if(!to && This.cur_from_mark != null) { This.map.removeOverlay(This.cur_from_mark); }
		if(This.cur_line != null){ This.map.removeOverlay(This.cur_line); }
			
		//grab point from geocoder...
		if(object.value != "") This.geocoder.getLatLng( object.value,
			function(point) {	   	
				  if(!point){
					 if(to){ 
						document.getElementById("not_found_to_" + method).style.display = 'block';
						document.getElementById("not_found_to_" + method).innerHTML = "Location could not found! Please try again";
					} else{
					   document.getElementById("not_found_from_" + method).style.display = 'block';
					   document.getElementById("not_found_from_" + method).innerHTML = "Location could not found! Please try again";					 
					}
					 This.distance = 0;	
					 input.focus();
					 return false;
				   } else {						  
					  var marker = new GMarker(point);
					  if(to){
						This.cur_to_mark = marker;
						This.cur_to_point = point;						
					  } else {
						This.cur_from_mark = marker;
					  	This.cur_from_point = point;
					  }
					  if(This.cur_from_point != null && This.cur_to_point != null){							
							//center map over route
							var bounds = new GLatLngBounds();
								bounds.extend(This.cur_from_point);	
							    bounds.extend(This.cur_to_point);								
								This.map.setZoom(This.map.getBoundsZoomLevel(bounds));
								This.map.setCenter(bounds.getCenter());
							 	//draw line between two points
						 		
								This.draw_line(method, This.cur_from_point, This.cur_to_point);
								//track distance
								This.distance =  Math.ceil( (This.cur_from_point.distanceFrom(This.cur_to_point) * 0.000621371192) * 1000 ) / 1000;  // / 621.371192) / 2;														
					
								This.activate_button(method); 
					 
					 } else {
					 	This.map.setCenter(point, 5);
					  }
					  This.map.addOverlay(marker);					
				  }
			   }          
			);      
	}

	this.enter_distance = function(method, object){
			if(object && object.value != ""){ 
			//remove commas  
				object.value = This.unformat(object.value);
				This.distance = object.value;
				// JKR CNL 11/9/07 - moved this line from bottom to top
				// if there is nothing on the map currently, google methods produce errors
				This.activate_button(method);
				This.map.removeOverlay(This.cur_to_mark);
				This.map.removeOverlay(This.cur_from_mark);
				This.map.removeOverlay(This.cur_line);
				This.cur_to_point = null;
				This.cur_to_mark = null;
				This.cur_from_point = null;
				This.cur_point_mark = null;
				eval('This.form.'+method+'_to.value=""');	
				eval('This.form.'+method+'_from.value=""');	
			} else if(object.value == ""){
				//This.deactivate_button(method); 
			}	 	
	}	
	
	this.timer = false;
	this.add_trip = function(method, object){
		// jkr 12/6/07 -- suspected type conversion issues -- ensure this is a number not a string
		This.distance = Number(This.distance);
		if(This.timer != null) window.clearTimeout(This.timer);
		if(This.distance == 0){ 			
			if( eval('This.form.'+method+'_to.value!=""') && eval('This.form.'+method+'_from.value!=""')) {
					This.form.elements[method + "_to"].focus();
					This.timer = window.setTimeout(This.add_trip, 1000, method, object);
					return false;
			} else {
				alert('Please enter From and To locations, or enter a distance manually.');
		 		This.form.elements[method + "_to"].focus();
			}
			return false; 
		} 

		//save data
		if(method=='fly'){ 
			var mult = This.form.fly_mult[1].checked ? 2 : 1;
		 	This.distance *= mult;
		}
	
		if(method=='drive' && This.form.drive_dist.value == ""){ 			
		 	This.distance *= 1.15;
		}
		
		var tid = "trip_" + method + "_" + (This.ids[method].length).toString();
		// JKR CNL 11/9/2007 -- on the form, rail is first, element [0]
		// var rb = (This.form.railbus[1].checked) ? "rail" : "bus";
		var rb = (This.form.railbus[0].checked) ? "rail" : "bus";

		if (method=='rail_bus') {
			// jkr 12/6/07 -- per e-mail to russ, add multipliers to rail & bus
			if (This.form.rail_bus_dist.value == "") {
				// only add multiplier if source of distance is map
				if (rb == 'rail') {
					This.distance *= 1.10;
				} else if (rb == 'bus') {
					This.distance *= 1.15;
				} // end if
			} // end if map is source of distance
			This.total[method] = parseFloat(This.total[method]) + This.distance;
			This.distance += "|" + rb;
		}else{
			// jkr CNL 11/9/07 -- multiple manual entries via the drive pane were resulting in concatenation of strings,
			// not addition.  Add parseFloat to force them to numbers.
			This.total[method] += parseFloat(This.distance);
		}
		
		This.markers[method].push(This.cur_to_mark);
		This.markers[method].push(This.cur_from_mark); 
		This.points[method].push(This.cur_to_point);
		This.points[method].push(This.cur_from_point);
		This.lines[method].push(This.cur_line); 
		This.trips[method].push(This.distance);		
		This.ids[method].push(tid);
		
		var input = object;		
		
		
		var total = This.total[method];			
		document.getElementById("total_from"). innerHTML = "CO<sub>2</sub> from "+ This.inflect[method] +" " + This.num_format(total) + " miles";				
		
	
		var carbon = Math.round(This.carbon_total(This.current_method)*1000)/1000;
		document.getElementById("total_carbon").innerHTML = "TOTAL: " + carbon + " tons";
		document.getElementById("total_from_" + This.current_method ).innerHTML = carbon + " tons CO<sub>2</sub> from " + This.inflect[This.current_method];
		
		   

		if(!This.bounds[method]) This.bounds[method] = new GLatLngBounds();
		if(This.cur_to_point != null)   This.bounds[method].extend(This.cur_to_point);
		if(This.cur_from_point != null) This.bounds[method].extend(This.cur_from_point);		

		var dist = method != "rail_bus" ? This.distance : This.distance.substr(0,This.distance.indexOf("|"));

		//add trip to the list so it can be removed...
		var summary	= document.getElementById("summary_" + method);
			summary.style.display = 'block';
		var trip = document.createElement("p");
	    var labl = document.createElement("strong");
			labl.innerHTML = eval('This.form.' + method + '_dist.value==""') ? "From " + eval('This.form.' + method + '_from.value') + " to " + eval('This.form.'+method+'_to.value') + "<br />" : "Unspecified Origin and Destination <br />"; 
			labl.innerHTML += This.num_format(dist) + " miles ";

		var remove = document.createElement("a");	
			remove.style.cursor = 'pointer';			
			remove.setAttribute("tid",tid);
			remove.onclick = function(){ This.remove(method, this.getAttribute("tid"), this); };
			//trip.innerHTML += " <a href=\"javascript:_calc.edit('"+method+"'," + This.lines[method].length + ");\">[Edit]</a>"; 
			
			remove.innerHTML += "[Remove]"; 
			trip.appendChild(labl);
			trip.appendChild(remove);

			summary.appendChild(trip);

		//reset currents
		This.cur_to_mark = null;
		This.cur_from_mark = null;
		This.cur_to_point = null;
		This.cur_from_point = null;
		This.cur_line = null;	
		This.distance = 0;
		
		This.set_view(method);
		
		//clear form
		eval('This.form.'+method+'_to.value=""');	
		eval('This.form.'+method+'_from.value=""');
		eval('This.form.'+method+'_dist.value=""');	

		This.deactivate_button(method); 	
	}

	this.remove = function(method, id, linkobj){
		var index = -1;
		for(i=0; i < This.ids[method].length; i++){
			if(This.ids[method][i] == id)
				index = i;
		}				
			
		This.total[This.current_method] -= parseFloat(This.trips[method][index]);	
		
		//remove link object	
		linkobj.parentNode.parentNode.removeChild(linkobj.parentNode);
	
			//remove from storage
			This.markers[method].splice(index*2,2);
			This.points[method].splice(index*2,2);
			This.lines[method].splice(index,1);
			This.trips[method].splice(index,1);
			This.ids[method].splice(index,1);
		//This.switch_view(method);
		
		var carbon = Math.round(This.carbon_total(method)*1000)/1000;
		
		document.getElementById("total_carbon").innerHTML = "TOTAL: " + carbon + " tons";
		document.getElementById("total_from"). innerHTML = "CO<sub>2</sub> from "+ This.inflect[method] +" " + This.num_format(This.total[method]) + " miles";				
	
			// JKR CN2L 11/9/07 - moved this above map stuff to ensure it happens -- map may return errors
			// if there is nothing to remove
	
			//remove from map
			This.map.removeOverlay(This.lines[method][index]);
			This.map.removeOverlay(This.markers[method][index*2]);
			This.map.removeOverlay(This.markers[method][(index*2)+1]);

	
	}

	this.selected_method = "drive";
	this.switch_view = function(method){
		This.map.clearOverlays();		
		document.images['tab_' + This.selected_method].src = 'http://www.nativeenergy.com/filebin/template/travel_calc/images/' + This.selected_method + '.gif';
		document.images['tab_' + method].src = 'http://www.nativeenergy.com/filebin/template/travel_calc/images/' + method + '_on.gif';


		document.getElementById(This.selected_method).style.display = 'none';
		document.getElementById(method).style.display = 'block';
		if(document.getElementById("summary_" + This.selected_method)) 
			document.getElementById("summary_" + This.selected_method).style.display = 'none';
		
		This.selected_method = method;	
		This.current_method = method;
	
		if(method != "total"){ 
				document.getElementById("map").style.display = 'block';
				document.getElementById("order_form").style.display = 'none';
				document.getElementById("summary_" + method).style.display = 'block';
				document.getElementById("done").style.display = 'block';
		} else {
				document.getElementById("map").style.display = 'none';
				document.getElementById("order_form").style.display = 'block';
				document.getElementById("done").style.display = 'none';
				This.draw_total();
				return;
		}
	
		document.getElementById("total_from"). innerHTML = "CO<sub>2</sub> from "+ This.inflect[method] +" " + This.num_format(This.total[method]) + " miles";				
		
		var carbon = Math.round(This.carbon_total(method)*1000)/1000;
		document.getElementById("total_carbon").innerHTML = "TOTAL: " + carbon + " tons";
		document.getElementById("total_from_" + method ).innerHTML = carbon + " tons CO<sub>2</sub> from " + This.inflect[method];
		This.draw_view(method);
		This.deactivate_button(method);
	}

	this.next_tab = function(){
	   if(This.selected_method == "drive"){
			This.switch_view('fly');
			return;
		}
		if(This.selected_method == "fly"){
			This.switch_view('rail_bus');
			return;
		}
		if(This.selected_method == "rail_bus"){
			This.switch_view('total');		
			return;
		}
	}

	this.activate_button = function(method){
		var btn = new Element(method + '_btn', {alpha:100} );
	}

	this.deactivate_button = function(method){
		if(!method) method = This.current_method;		
		var btn = new Element(method + '_btn', {alpha:10} );
	} 
	this.deactivate_button('drive');

	this.draw_view = function(method){
		for(var d = 0; d < This.markers[method].length; d++){			
			if(This.markers[method][d] != null) This.map.addOverlay(This.markers[method][d]);			
		}
		for(var d = 0; d < This.lines[method].length; d++){			
			if(This.lines[method][d] != null) This.map.addOverlay(This.lines[method][d]);			
		}
		This.set_view(method);
	}		

	this.set_view = function(method){
		if(This.bounds[method]) {
			// all markers for method to bounds and recenter map...
			This.map.setZoom(This.map.getBoundsZoomLevel(This.bounds[method]));
			This.map.setCenter(This.bounds[method].getCenter());
			document.getElementById("more_to_calc").style.display = 'inline';

		} else if(method=='total'){
			
			var bounds =  new GLatLngBounds();
			for(var d = 0; d < This.points['drive'].length; d++){	
				if(This.points['drive'][d] != null) bounds.extend(This.points['drive'][d]);
			}
			for(var d = 0; d < This.points['fly'].length; d++){	
				if(This.points['fly'][d] != null) bounds.extend(This.points['fly'][d]);
			}
			for(var d = 0; d < This.points['rail_bus'].length; d++){	
				if(This.points['rail_bus'][d] != null) bounds.extend(This.points['rail_bus'][d]);
			}
			This.map.setZoom(This.map.getBoundsZoomLevel(bounds));
			This.map.setCenter(bounds.getCenter());
			//hide buttons and show form selection.
			document.getElementById("more_to_calc").style.display = 'none';

 		} else {
			document.getElementById("more_to_calc").style.display = 'inline';
			This.map.setCenter(new GLatLng(36, -95), 3);
		}	
	}

	this.draw_total = function(){
		//alert(This.total['drive']);
		// JKR CNL 11/9/2007 - values below seem to have been strings, so addition was tacking 0s on the end
		// e.g. 150 drive miles was being reported as 0150
		//var total = This.total['drive'] + This.total['fly'] + This.total['rail_bus'];
		var total = parseFloat(This.total['drive']) + parseFloat(This.total['fly']) + parseFloat(This.total['rail_bus']);

		document.getElementById("total_from"). innerHTML = "CO<sub>2</sub> from " + This.num_format(total) + " miles of travel";				
		
		var carbon =  This.carbon_total('drive');
			carbon += This.carbon_total('fly');
			carbon += This.carbon_total('rail_bus');
		document.getElementById("total_carbon").innerHTML = "TOTAL: " + Math.round(carbon*1000)/1000 + " tons";
		This.draw_view('drive');
		This.draw_view('fly');
		This.draw_view('rail_bus');	
		This.set_view('total');	
		
	var carbon_rounded = Math.round(carbon*1000)/1000;
	//UPDATE YAHOO FORM FOR SUBMISSION - 
	updateTotalForms(carbon_rounded);

	}

	this.carbon_total = function(method){
		if(method=="drive"){
				if(This.form.drive_mpg_man.value == ""){
					for(m = 0; m < This.form.drive_mpg.length; m++){
						if(This.form.drive_mpg[m].checked){
							return ((This.total[method] / This.form.drive_mpg[m].value) * (19.56/2000));
						}
					}
					return ((This.total[method] / 20) * (19.56/2000) ); 
				} else {
					return (This.total[method] / This.form.drive_mpg_man.value) * (19.56/2000);
				}		
		} else if (method=="fly"){
				//loop through trips and find distances because of varrying
				// calculation change: multiply miles by 2 before calculating tons.
				var total = 0;
				for(var t = 0; t < This.trips['fly'].length; t++){   
						if(This.trips['fly'][t] < 280){  
							total += ((This.trips['fly'][t] * 2) * 0.64) / 2000;
						} else if(This.trips['fly'][t]  < 954){
							total += ((This.trips['fly'][t] * 2) * 0.44) / 2000;
						} else {
							total += ((This.trips['fly'][t] * 2) * 0.40) / 2000;
						}
				}
			return total;					
		} else {
			//calculate for rail or bus
				var total = 0;
				for(var t = 0; t < This.trips['rail_bus'].length; t++){
					var rlbs = This.trips['rail_bus'][t].toString().split("|");		
					if(rlbs[1]=='rail'){
						// jkr 12/6/07 - different multipliers based on length of trip, per
						// email to russ
						if (rlbs[0] <= 20) {
							total += rlbs[0] * (0.35/2000);
						} else {
							total += rlbs[0] * (0.42/2000);
						} // end else
					} else {
						// bus
						// again, jkr change 12/6/7 to change based on distance
						if (rlbs[0] <= 20) {
							total += rlbs[0] * (0.66/2000);
						} else {
							total += rlbs[0] * (0.18/2000);
						} // end if/else
					}					
				}
				return total;				
		}
		return 0;
	}
	
	this.draw_line = function(method, from, to){
		
		var line = new GPolyline([
									from,
									to
								  ], This.line_colors[method] , 5); //last param is line weight
		
		if(line) This.map.addOverlay(line);	
		This.cur_line = line;
	}

	this.num_format = function(nStr){
			// jkr 12/6/07 - changed math.ceil to math.round -- crazy tiny remainders don't wind up in value
			nStr = Math.round(nStr *100)/100;
			nStr += '';
			x = nStr.split('.');
			x1 = x[0];
			x2 = x.length > 1 ? '.' + x[1] : '';
			var rgx = /(\d+)(\d{3})/;
			while (rgx.test(x1)) {
				x1 = x1.replace(rgx, '$1' + ',' + '$2');
			}
			return x1 + x2;
	}

	this.unformat = function(str){		
		return str.replace(/,/, "");	
	}

}
