var oHGMap;
// Google maps geocoder address match constants.
var G_MATCH_NOT_FOUND       = 0;
var G_MATCH_SINGLE_FOUND    = 1;
var G_MATCH_SINGLE_ZIP_ONLY = 2;
var G_MATCH_MULTIPLE_FOUND  = 3;
var G_COORD_LNG             = 0;
var G_COORD_LAT             = 1;
// loads the Google Maps API
google.load("maps", "2");
// Object used to easily manipulate querystring.
var oURIParser = new URIParser();
var oCookieMgr;
// Initialize with our current URL.
oURIParser.parseURI(document.location.href);
oURIParser.parseArgs(document.location.search.replace('?', ''));
//================================================================
function getJSONElementByName(name, obj){
	var strEltVal = '';
	for ( elt in obj ) {
		oTmpObj = obj[elt];
		if ( typeof(oTmpObj) == 'object' ) {
			strEltVal = getJSONElementByName(name, oTmpObj);
		}
		else if ( typeof(oTmpObj) != 'object' && elt == name ) {
			return oTmpObj;
		}
		else {
			strEltVal = "";
		}
		if (strEltVal) {
			return strEltVal;
		}
	}
	return strEltVal;
}
//================================================================
function getJSONObjByName(name, obj){
	var strEltVal = '';
	for ( elt in obj ) {
		oTmpObj = obj[elt];
		if ( typeof(oTmpObj) == 'object' && elt != name ) {
			strEltVal = getJSONObjByName(name, oTmpObj);
		}
		else if ( elt == name ) {
			return oTmpObj;
		}
		else {
			strEltVal = "";
		}
		if (strEltVal) {
			return strEltVal;
		}
	}
	return strEltVal;
}
//================================================================
function getHVALAddress(form){
	return geocodeAddress(form);
}
//================================================================
function getHVALAddress2(addr, csz,entryid){
	return geocodeAddress(addr, csz, entryid);
}
//================================================================
function geocodeAddress(){
	var intNumArgs = arguments.length;
	var strCSZ;
	var bEmptyAddress;
	var bFormSubmit = (intNumArgs == 1 && typeof(arguments[0]) == 'object' );
	var bQSRequest  = (intNumArgs == 2 || intNumArgs == 3);
	var entryid;
	if(bFormSubmit){
		var obj_form  = arguments[0];
		strCSZ        = getAddressFieldValue(obj_form, "csz");
		bEmptyAddress = ( strCSZ.length == 0 );
		entryid       = obj_form.entryid.value;
		if(!isUndefined(obj_form.redirect)){
			if(bEmptyAddress){
				var tmpURL = "/home-values";
				if(!isUndefined(entryid)){
					tmpURL += "?entryid=" + entryid.value;
				}
				window.location.href = tmpURL;
				return false;
			}
		}
	} else if(bQSRequest){
		// We only use csz on the site now, but we need to still handle addr and csz from external links.
		strAddr       = (stripInvalidAddressChars(arguments[0]).length > 0 ? stripInvalidAddressChars(arguments[0]) : '');
		strCSZ        = strAddr + ' ' + (stripInvalidAddressChars(arguments[1]).length > 0 ? stripInvalidAddressChars(arguments[1]) : '');
		bEmptyAddress = ( strCSZ.length == 0 );
		entryid       = arguments[2];
	}
	var geocoder = new google.maps.ClientGeocoder();
	geocoder.setBaseCountryCode("us");
	// processes submitted address
	if(!bEmptyAddress){
		setFlagInCookie('ck_hval_user_entered','strCSZ', strCSZ);
		var oLocalityData = new Object();
		// 2009-01-28 - JC - RFC5274/BUG6374 - If an address is requested from the list of alternates it will be passed
		//                   as data in the csz querystring var (so it'll look like a user search). If the user
		//                   originally specified a unit number in their search we want to retain it and pass it along with the
		//                   alternate address request. We'll do that by setting the "unit" querystring variable.
		var strQSAltUnit = oURIParser.getParameter('unit');
		// 2009-01-15 - JC - RFC5274 - oAddrPerser is instantiated in ~/src/httpd/html/sitelib/libHVal.js.
		oLocalityData.unitNumber = ( strQSAltUnit ? strQSAltUnit : oAddrParser.parse_unit(strCSZ) );
		oLocalityData.userAddress = strCSZ;
		geocoder.getLocations(strCSZ,
			function(objAddress){
				switch(objAddress.Status.code){
					case G_GEO_SUCCESS:
						var oPlacemarks = filterPlacemarks(objAddress.Placemark);
						var bAddressesFound = ( oPlacemarks.length > 0 );
						if(bAddressesFound){
							var oPlacemark = oPlacemarks[0];
							getLocalityData(oPlacemark.AddressDetails.Country, oLocalityData);
							oLocalityData.lat = oPlacemark.Point.coordinates[G_COORD_LAT];
							oLocalityData.lng = oPlacemark.Point.coordinates[G_COORD_LNG];
							// Set address match status.
							oLocalityData.match = setMatchStatus(oPlacemarks, oLocalityData.street.length);
						} else {
							oLocalityData.match = G_MATCH_NOT_FOUND;
							oLocalityData.full_address = "";
						}
						// redirects the page
						try{
							if(oLocalityData.full_address.length > 0){
								// This check was added as a result of the case where the placemark does not contain an AdministrativeArea.
								// Ex: Requesting "st san francisco, ca" returns interzsection placemarks.
								var strHValURL = buildHValURL(oLocalityData, entryid, true);
								if(strHValURL.length > 0){
									window.location.href = strHValURL;
								} else {
									if(bFormSubmit){
										HGGoogleMap.prototype.loadMapMessage({
											'error_code'   : '', 
											'clear_hv_msg' : true,
											'address'      : oLocalityData.userAddress
										});
									} else if(bQSRequest){
										// Redirect to addr/csz version of homevalues
										window.location.href = buildAddrCSZRedirURL(strCSZ, entryid);
									}
								}
							} else {
								if(bFormSubmit){
									HGGoogleMap.prototype.loadMapMessage({
										'error_code'   : 'Invalid placemarks returned.',
										'clear_hv_msg' : true,
										'address'      : oLocalityData.userAddress
									});
								} else if(bQSRequest){
									// Redirect to addr/csz version of homevalues
									window.location.href = buildAddrCSZRedirURL(strCSZ, entryid);
								}
							}
						} catch (err){
							if(bFormSubmit){
								HGGoogleMap.prototype.loadMapMessage({
									'error_code'   : err.message,
									'clear_hv_msg' : true,
									'address'      : oLocalityData.userAddress
								});
							}
						}
						break;
					default:
						if(bFormSubmit){
							HGGoogleMap.prototype.loadMapMessage({
								'error_code'   : objAddress.Status.code, 
								'clear_hv_msg' : true,
								'address'      : oLocalityData.userAddress
							});
						} else if(bQSRequest){
							// Redirect to addr/csz version of homevalues
							window.location.href = buildAddrCSZRedirURL(strCSZ, entryid);
						}
						break;
				}
			}
		);
	} else {
		alert("Please enter a street address OR a city, state or zip.");
		return false;
	}
	return false;
}
//================================================================
function buildAddrCSZRedirURL(csz, entryid){
	var tmpURL = "/homevalues/?csz=" + escape(csz) + "&no_resubmit=1";
	if(!isUndefined(entryid)){
		if(!isUndefined(entryid) && entryid != '') tmpURL += "&entryid=" + entryid;
	}
	return tmpURL;
}
//================================================================
function getAddressFieldValue(form, field_name){
	return (!isUndefined(form[field_name]) ? stripInvalidAddressChars(form[field_name].value) : "")
}
//================================================================
function stripInvalidAddressChars(data){
	return data.replace(/[\.,]/g, "").replace(/(^\s|\s$)/, '');
}
//================================================================
function filterPlacemarks(obj_placemark) {
	// Only use addresses that have a certain level of accuracy. 
	/*
		Constants: Description
		0: Unknown location. (Since 2.59)
		1: Country level accuracy. (Since 2.59)
		2: Region (state, province, prefecture, etc.) level accuracy. (Since 2.59)
		3: Sub-region (county, municipality, etc.) level accuracy. (Since 2.59)
		4: Town (city, village) level accuracy. (Since 2.59)
		5: Post code (zip code) level accuracy. (Since 2.59)
		6: Street level accuracy. (Since 2.59)
		7: Intersection level accuracy. (Since 2.59)
		8: Address level accuracy. (Since 2.59)
		9: Premise (building name, property name, shopping center, etc.) level accuracy. (Since 2.105)
	*/
	var arrPlacemark = new Array();
	for(var pm in obj_placemark){
		bExists = ( obj_placemark[pm].AddressDetails.Accuracy != 0 && obj_placemark[pm].AddressDetails.Accuracy != 1 );
		if(bExists && obj_placemark[pm].AddressDetails.Country.CountryNameCode == "US"){
			arrPlacemark.push(obj_placemark[pm]);
		}
	}
	return arrPlacemark;
}
//================================================================
function buildHValURL(obj_locality_data, entry_id, set_cookies){
	var bSetCookies = (!isUndefined(set_cookies) && set_cookies);
	var url_descr_flag = "";
	var strTmp = "/homevalues";
	var arrAllowableCSFlags = ['s', 'z', 'cs', 'csz', 'cszt', 'ct', 'sz', 'szt', 'cst'];
	var bCSFlagAllowed = false;
	if(!isUndefined(obj_locality_data.city) && obj_locality_data.city.length > 1){
		strTmp += "/" + formatAddressData(obj_locality_data.city);
		url_descr_flag += "c";
	}
	if(!isUndefined(obj_locality_data.state) && obj_locality_data.state.length > 1){
		if(url_descr_flag == 'c') strTmp += "-";
		else strTmp += "/";
		strTmp += formatAddressData(obj_locality_data.state);
		url_descr_flag += "s";
	}
	if(!isUndefined(obj_locality_data.zip) && obj_locality_data.zip.length > 1){
		strTmp += "/" + formatAddressData(obj_locality_data.zip);
		url_descr_flag += "z";
	}
	if(!isUndefined(obj_locality_data.street) && obj_locality_data.street.length > 1){
		strTmp += "/" + formatAddressData(obj_locality_data.street);
		url_descr_flag += "t";
	}
	// 2009-01-15 - JC - RFC5274 - If the parsed unit number exists we'll add 
	//      it (along with our unit designator), to the end of the URL.
	strTmp += formatUnitData(true, obj_locality_data.unitNumber);
	// Set cookies
	if(bSetCookies) setHValCookies(obj_locality_data, strTmp, url_descr_flag);
	// checks for entryid
	// 2008-10-31 - JC - BUG6274
	// We need to handle entryid values from both form and querystring.
	if(!isUndefined(entry_id)){
		if(!isUndefined(entry_id.value) && entry_id.value != ''){
			strTmp += "?entryid=" + entry_id.value;
		} else if (!isUndefined(entry_id) && entry_id != ''){
			strTmp += "?entryid=" + entry_id;
		}
	}
	// See if we have allowable address data.
	for(udf in arrAllowableCSFlags){
		if(arrAllowableCSFlags[udf] == url_descr_flag){
			bCSFlagAllowed = true;
			break;
		}
	}
	if(bCSFlagAllowed ){
		return strTmp;
	} else {
		// We do not want to continue;
		return ''
	}
}
//================================================================
function formatUnitData(for_url, unit_number) {
	var strDelim;
	if(typeof(unit_number) != 'undefined' && unit_number.length > 0){
		if(for_url) strDelim = "-";
		elsestrDelim = " ";
		return strDelim + escape(LABEL_UNIT_DESIGNATOR) + strDelim + unit_number;
	} else {
		return "";
	}
}
//================================================================
function formatAddressData(val) {
	var bAllowFormat = true;
	var strCodes = "";
	for(ch in val){
		if(val.charCodeAt(ch) > 127 ) bAllowFormat = false;
	}
	if(!isUndefined(val) && val.length > 0 && bAllowFormat){
		return escape(val.replace(/\s/gi, "-"));
	} else {
		return "";
	}
}
//================================================================
function setMatchStatus(obj_placemark, street_length) {
    var bSingleAddress = ( obj_placemark.length == 1 );
    var bAddressesFound = ( obj_placemark.length > 0 );
    var bStreetInfo = ( street_length > 0 );
    if ( bSingleAddress && bStreetInfo ) {
        // Single placemark returned, with street info. Address FOUND.
        return G_MATCH_SINGLE_FOUND;
    } 
    else if ( bSingleAddress ) {
        // Single placemark returned without street info. Probably a zip only submission.
        return G_MATCH_SINGLE_ZIP_ONLY;
    }
    else if ( bStreetInfo || bAddressesFound ) {
        // Multiple addresses returned, with street info.
        return G_MATCH_MULTIPLE_FOUND;
    }
    else {
        // Address not found.
        return G_MATCH_NOT_FOUND;
    }
}
//================================================================
function buildFullAddress(obj_locality_data) {
    var arrParms = ["street", "unitNumber", "city", "state", "zip"];
    var arrAddress = [];
    //debugger;
    for ( parm in arrParms ) {
        strProp = arrParms[parm];
        if ( typeof(obj_locality_data[strProp]) != "undefined" && obj_locality_data[strProp].length > 0 ) {
            if ( strProp == "unitNumber" ) {
                arrAddress.push(formatUnitData(false, obj_locality_data[strProp]));
            } else {
                arrAddress.push(obj_locality_data[strProp]);
            }
        }
    }
    return arrAddress.join(", ");
}
//================================================================
function isUndefinedPlacemarkName(obj, obj_name, obj_type) {
    // Check object itself.
    if ( !isUndefined(obj) ) {
        var strType = ( obj_type.toLowerCase() == "name" ? "Name" : "Number" );
        // Check sub-object
        if ( !isUndefined(obj[obj_name]) ) {
            // Check sub-object name
            if ( !isUndefined(obj[obj_name][obj_name + strType]) ) {
                return false;
            }
            else {
                return true;
            }
        }
        else {
            return true;
        }
    }
    else {
        return true;
    }
}
//================================================================
//  Function to convert 2-word state names to 2-letter abbreviations.
//  Google does not return the abbreviation for some of them.
//================================================================
function convert2WordStateName(val) {
    /*
    1. New Jersey – ?
    2. New York - OK
    3. New Hampshire - ?
    4. North Dakota - ?
    5. North Carolina - OK
    6. South Dakota - ?
    7. South Carolina - ?
    8. New Mexico - OK
    9. Rhode Island - ?
    10. West Virginia - ?
	2008-10-27 - JC - BUG6267
	The geocoder seems to have changed once again in how it is returning two name state names.
	Previously it returned both full names for most 2-name state; now it returns single-letter abbrev.
	followed by 2nd full name - for some states. We'll handle this alternate format for all of them.
	Oh, and it returns Rhode Island as 'Rhode Isl'.
    */
    var oStates = {
        'New Jersey'        : 'NJ',
        'N Jersey'	        : 'NJ',
        'New Hampshire'     : 'NH',
        'N Hampshire'	    : 'NH',
        'North Dakota'      : 'ND',
        'N Dakota'	        : 'ND',
        'North Carolina'    : 'NC',
        'N Carolina'		: 'NC',
        'South Dakota'      : 'SD',
        'S Dakota'			: 'SD',
        'South Carolina'    : 'SC',
        'S Carolina'		: 'SC',
        'Rhode Island'      : 'RI',
        'R Island'			: 'RI',
        'Rhode Isl'			: 'RI',
        'West Virginia'     : 'WV',
        'W Virginia'		: 'WV'
    }
    for ( var state in oStates ) {
        //alert(state);
        if ( state.toLowerCase() == val.toLowerCase() ) {
            return oStates[state];
        }
    }
    return val;
}
//================================================================
function getLocalityData(obj_placemark, obj_locality_data) {
    obj_locality_data.street    = getJSONElementByName("ThoroughfareName", obj_placemark);
    obj_locality_data.city      = '';
    obj_locality_data.zip       = getJSONElementByName("PostalCodeNumber", obj_placemark);
    obj_locality_data.state     = getJSONElementByName("AdministrativeAreaName", obj_placemark).replace(/\./g, ''); // At one point in the past Google returned abbreviations with periods in them; this strips them out.
    // Convert certain 2-word state names to abbreviations.
    obj_locality_data.state     = convert2WordStateName(obj_locality_data.state);
    // Possible nodes holding city name.
    var arrCityParms = ['LocalityName', 'DependentLocalityName', 'PremiseName', 'SubAdministrativeAreaName'];
    for ( prop in arrCityParms ){
        strCityVal = getJSONElementByName(arrCityParms[prop], obj_placemark);
        if ( strCityVal != '' ) {
            obj_locality_data.city = strCityVal;
            break;
        }
    }
    // Sometimes the city name can be returned in the following node:
    //  placemark.AddressDetails.Country.AdministrativeArea.AddressLine[0]
    // We'll try that if we haven't found the city yet.
    if ( obj_locality_data.city.length == 0 ) {
        arrAddressLine = getJSONObjByName('AddressLine', obj_placemark);
        if ( arrAddressLine.length > 0 ) {
            obj_locality_data.city = arrAddressLine[0];
        }
    }
    //debugger;
    obj_locality_data.full_address  = buildFullAddress(obj_locality_data);
}
//================================================================
function loadMap() {
    oHGMap.load();
}
//================================================================
function openHomeGainClients() {
  var the_url = "/popup/client/sales_consumer";
  var the_name = "hgc_window";
  var the_features = "width=435,height=420,scrollbars,resizable";
  hgc_window = window.open(the_url, the_name, the_features);
}
//================================================================
function openForwardToFriendWindow(subject) {
    var strURLToForward = escape(document.location.href);
    var strStreetAddress = escape(oHGMap.g_street.length > 0 ? oHGMap.g_street : "No street address spcified." );
    var strQSData = "&data=";
    if ( oHGMap.hvalMod['mod_method'] == 'post' || oHGMap.hvalMod['mod_method'] == 'get' ) {
        for ( var prop in oHGMap.hvalMod ) {
           strQSData += prop + ':' + escape(oHGMap.hvalMod[prop]) + '|';
        }
        strQSData = strQSData.substr(0, strQSData.length - 1)
    } else {
        strQSData = "";
    }
    window.open('/popup/send-link-via-email?url_to_forward=' + strURLToForward + "&subject=" + escape(subject) + '&city=' + escape(oHGMap.g_city) + '&state=' + escape(oHGMap.g_state) + '&zip=' + escape(oHGMap.g_zip) + "&address=" + strStreetAddress + strQSData, 'forwardToFriendWin', 'width=425,height=505,status=0,scrollbars=0,resizable=1,toolbar=0,location=0');
    //newwin('/popup/send-link-via-email?url_to_forward=' + strURLToForward + "&subject=" + escape(subject), 380, 300, "status=0,scrollbars=0,resizable=1,toolbar=0,location=0");
}
//==============================================
//  Add markers for comp/nearby properties.
//==============================================
function addPropMarkers() {
    var dblTotalDistance = 0.0;
    var intTotalProps = 1;
    var dblAverageDistance = 0.0;
    var arrMapPoints = new Array();
    for ( i in oHGMap.propTypes ) {
        var strPropsName = Property.prototype.buildObjName(oHGMap.propTypes[i]);
        var arrPropObjects = oHGMap[strPropsName];
        for ( j in arrPropObjects ) {
            // Add map points to array so we can use them to center and zoom the map.
            arrMapPoints.push(new google.maps.LatLng(arrPropObjects[j].values['lat'], arrPropObjects[j].values['lon']));
            arrPropObjects[j].addMarker(false);
            dblTotalDistance += Number(arrPropObjects[j].values['distance']);
            intTotalProps += 1;
        }
    }
    // Push valuation point onto map points array.
    arrMapPoints.push(new google.maps.LatLng(oHGMap.prop_valuation.values["lat"], oHGMap.prop_valuation.values["lon"]));
    dblAverageDistance = dblTotalDistance / intTotalProps;
    setMapCenterAndZoom(oHGMap.map, arrMapPoints);
    //oHGMap.map.setZoom(calculateZoomLevel(dblAverageDistance));
}
//==============================================
function setMapCenterAndZoom(map, points) {
    if ( points.length > 0 ) {
        var oGBounds = new google.maps.Bounds(points); // Get bounds based on our collection of points.
        var oSW = new google.maps.LatLng(oGBounds.maxY, oGBounds.minX); // Get the southwestern-most point of the bounding box.
        var oNE = new google.maps.LatLng(oGBounds.minY, oGBounds.maxX); // Get the northeastern-most point of the bounding box. 
        var oGLLBounds = new google.maps.LatLngBounds(oSW, oNE); //  Get the LatLngBounds object.
        // Re-center/zoom the map based on the LatLngBounds object.
        map.setCenter(oGLLBounds.getCenter(), map.getBoundsZoomLevel(oGLLBounds));
    }
}
//==============================================
function setMapCenterAndZoom(map, points) {
    if ( points.length > 0 ) {
        var oGBounds = new google.maps.Bounds(points); // Get bounds based on our collection of points.
        var oSW = new google.maps.LatLng(oGBounds.maxY, oGBounds.minX); // Get the southwestern-most point of the bounding box.
        var oNE = new google.maps.LatLng(oGBounds.minY, oGBounds.maxX); // Get the northeastern-most point of the bounding box. 
        var oGLLBounds = new google.maps.LatLngBounds(oSW, oNE); //  Get the LatLngBounds object.
        // Re-center/zoom the map based on the LatLngBounds object.
        map.setCenter(oGLLBounds.getCenter(), map.getBoundsZoomLevel(oGLLBounds));
    }
}
//==============================================
function calculateZoomLevel(avg_distance) {
    if ( isNaN(avg_distance) ) {
        return 4;
    }
    else if ( avg_distance > 0 && avg_distance <= .5 ) {
        return 15;
    }
    else if ( avg_distance > .5 && avg_distance <= 1 ) {
        return 14;
    }
    else if ( avg_distance > 1 && avg_distance <= 2 ) {
        return 13;
    }
    else if ( avg_distance > 2 && avg_distance <= 5 ) {
        return 12;
    }
    else if ( avg_distance > 5 && avg_distance <= 10 ) {
        return 11;
    }
    else if ( avg_distance > 10 && avg_distance <= 20 ) {
        return 10;
    }
    else if ( avg_distance > 20 && avg_distance <= 50 ) {
        return 9;
    }
    else if ( avg_distance > 50 && avg_distance <= 100 ) {
        return 8;
    }
    else if ( avg_distance > 100 && avg_distance <= 250 ) {
        return 5;
    }
    else if ( avg_distance > 250 && avg_distance <= 500 ) {
        return 4;
    }
    else {
        return 3;
    }
}
//==============================================
function isUndefined(variable) {
     if ( typeof(variable) == undefined || typeof(variable) == "undefined" || variable == NaN ) {
        return true;
    }
    else {
        return false;
    }
}
//==============================================
function isNull(obj) {
    return ( obj == null );
}
//==============================================
function isUndefinedNull(obj) {
    return ( isUndefined(obj) || isNull(obj) );
}
//==============================================
function getLocationResults(obj_response) { 
    // References status codes...
    // http://code.google.com/apis/maps/documentation/reference.html#GGeoStatusCode
    // G_GEO_SUCCESS (200) No errors occurred; the address was successfully parsed and its geocode has been returned. (Since 2.55) 
    // G_GEO_BAD_REQUEST (400) A directions request could not be successfully parsed. (Since 2.81) 
    // G_GEO_SERVER_ERROR (500) A geocoding or directions request could not be successfully processed, yet the exact reason for the failure is not known. (Since 2.55) 
    // G_GEO_MISSING_QUERY (601) The HTTP q parameter was either missing or had no value. For geocoding requests, this means that an empty address was specified as input. For directions requests, this means that no query was specified in the input. (Since 2.81) 
    // G_GEO_MISSING_ADDRESS (601) Synonym for G_GEO_MISSING_QUERY. (Since 2.55) 
    // G_GEO_UNKNOWN_ADDRESS (602) No corresponding geographic location could be found for the specified address. This may be due to the fact that the address is relatively new, or it may be incorrect. (Since 2.55) 
    // G_GEO_UNAVAILABLE_ADDRESS (603) The geocode for the given address or the route for the given directions query cannot be returned due to legal or contractual reasons. (Since 2.55) 
    // G_GEO_UNKNOWN_DIRECTIONS (604) The GDirections object could not compute directions between the points mentioned in the query. This is usually because there is no route available between the two points, or because we do not have data for routing in that region. (Since 2.81) 
    // G_GEO_BAD_KEY (610) The given key is either invalid or does not match the domain for which it was given. (Since 2.55) 
    // G_GEO_TOO_MANY_QUERIES (620) The given key has gone over the requests limit in the 24 hour period. (Since 2.55) 
    var strResponseErrorMessage = "";
    switch ( obj_response.Status.code) {
        case G_GEO_SUCCESS:
            var oPlacemarks = filterPlacemarks(obj_response.Placemark);
            var oPlacemark = oPlacemarks[0];
            oLocalityData = new Object();
            if ( oPlacemark ) {
                getLocalityData(oPlacemark.AddressDetails.Country, oLocalityData);
                // Save previous g_match value if there is one.
                oHGMap.g_match_p    = oHGMap.g_match;
                oHGMap.g_match      = setMatchStatus(oPlacemarks, oLocalityData.street.length);
                oHGMap.g_lat        = oPlacemark.Point.coordinates[G_COORD_LAT];
                oHGMap.g_lng        = oPlacemark.Point.coordinates[G_COORD_LNG];
                oHGMap.map = new google.maps.Map2(document.getElementById(oHGMap.containerEltID));
                //debugger;
                var bAutoTrigger = oHGMap.doMatchProcessing(oPlacemarks);
                oHGMap.propPoint = new google.maps.LatLng(oHGMap.g_lat, oHGMap.g_lng);
                oHGMap.map.setCenter(oHGMap.propPoint, oHGMap.zoomLevel);
                oHGMap.addControls();
                if ( oHGMap.g_match == G_MATCH_SINGLE_FOUND ) {
                    oHGMap.showStreetView();
                }
                oHGMap.prop_valuation.values["lat"] = oHGMap.g_lat
                oHGMap.prop_valuation.values["lon"] = oHGMap.g_lng
                oHGMap.prop_valuation.addMarker(bAutoTrigger);
                addPropMarkers();
            } else {
                oHGMap.g_lat    = 39.83333;
                oHGMap.g_lng    = -98.58333;
                oHGMap.map = new google.maps.Map2(document.getElementById(oHGMap.containerEltID));
                oHGMap.propPoint = new google.maps.LatLng(oHGMap.g_lat, oHGMap.g_lng);
                oHGMap.zoomLevel = 3;
                oHGMap.map.setCenter(oHGMap.propPoint, oHGMap.zoomLevel);
                oHGMap.addControls();
                //oHGMap.loadMapDisplayMessage("The map could not be loaded for the given address.<br><br>Please try another search.");
                oHGMap.loadMapMessage( {
                                        'error_code'    : obj_response.Status.code, 
                                        'clear_hv_msg'  : true,
                                        'address'       : oHGMap.g_addr
                                       });
            }
            break;
        case G_GEO_BAD_REQUEST:
            // Fall through all errors
            strResponseErrorMessage += ""
            //break;
        case G_GEO_MISSING_ADDRESS:
            // Missing address;
            //break;
        case G_GEO_UNKNOWN_ADDRESS:
            //alert("unknown address");
            //break;
        case G_GEO_BAD_KEY:
            //break;
        case G_GEO_TOO_MANY_QUERIES:
            //break;
        default:
            if ( oHGMap.homeValuationStatus == oHGMap.STATUS_HVAL_FOUND ) {
                // Leave g_lat and g_lon set to values from querystring.
                var bAutoTrigger = true;
                oHGMap.map = new google.maps.Map2(document.getElementById(oHGMap.containerEltID));
                oHGMap.zoomLevel = 15;
                oHGMap.propPoint = new google.maps.LatLng(oHGMap.g_lat, oHGMap.g_lng);
                oHGMap.map.setCenter(oHGMap.propPoint, oHGMap.zoomLevel);
                oHGMap.addControls();
                oHGMap.prop_valuation.values["lat"] = oHGMap.g_lat
                oHGMap.prop_valuation.values["lon"] = oHGMap.g_lng
                oHGMap.prop_valuation.addMarker(bAutoTrigger);
                addPropMarkers();
            }
            else {
                // Display map and set to the geo center of the US.
                // http://en.wikipedia.org/wiki/Geographic_Center_of_the_Contiguous_United_States
                oHGMap.g_lat    = 39.83333;
                oHGMap.g_lng    = -98.58333;
                oHGMap.map = new google.maps.Map2(document.getElementById(oHGMap.containerEltID));
                oHGMap.propPoint = new google.maps.LatLng(oHGMap.g_lat, oHGMap.g_lng);
                oHGMap.zoomLevel = 3;
                oHGMap.map.setCenter(oHGMap.propPoint, oHGMap.zoomLevel);
                oHGMap.addControls();
                //oHGMap.loadMapDisplayMessage("The map could not be loaded for the given address.<br><br>Please try another search.");
                oHGMap.loadMapMessage( {
                                        'error_code'    : obj_response.Status.code, 
                                        'clear_hv_msg'  : true,
                                        'address'       : oHGMap.g_addr
                                       });
            }
    }
}
//======================================================================================
function createCookie(name, value) {
    var oCookie = new Cookie();
    var strTrimValue = '' + value + '';
    strTrimValue = strTrimValue.replace(/^\s/, '').replace(/\s$/, '');
    oCookie.name = name;
    oCookie.value = strTrimValue;
    oCookie.path = "/";
    oCookieMgr.commit(oCookie);
}
//======================================================================================
function deleteCookie(name) {
    var oCookie = oCookieMgr.getCookie(name);
    alert(oCookie.domain);
    oCookie.expires = new Date().toGMTString();
    oCookieMgr.commit(oCookie);
    //alert("Done.");
}
//======================================================================================
function setHValCookies(obj_locality, hval_url, udf) {
    // Look at /h/modules/javascript/functions/cookie_functions 
    // Cookie manipulation object.
    oCookieMgr = new CookieManager();
    //debugger;
    /*
    setHGCookie("av_hval_data", "{'g_addr' : '" + obj_locality.userAddress + "'}");
    setFlagInCookie('av_hval_data','g_street', obj_locality.street);
    setFlagInCookie('av_hval_data','g_zip', obj_locality.zip);
    setFlagInCookie('av_hval_data','g_city', obj_locality.city);
    setFlagInCookie('av_hval_data','g_state', obj_locality.state);
    setFlagInCookie('av_hval_data','g_lat', obj_locality.lat);
    setFlagInCookie('av_hval_data','g_lng', obj_locality.lng);
    setFlagInCookie('av_hval_data','g_match', obj_locality.match);
    setFlagInCookie('av_hval_data','full_address', obj_locality.full_address);
    setFlagInCookie('av_hval_data','hval_url', hval_url);
    */
    createCookie("hval_load_type", "search");
    createCookie("g_addr", obj_locality.userAddress);
    createCookie("g_street", obj_locality.street + formatUnitData(false, obj_locality.unitNumber));
    createCookie("g_zip", obj_locality.zip);
    createCookie("g_city", obj_locality.city);
    createCookie("g_state", obj_locality.state);
    createCookie("g_lat", obj_locality.lat);
    createCookie("g_lng", obj_locality.lng);
    createCookie("g_match", obj_locality.match);
    //createCookie("udf", udf);
    //createCookie("hms_zip", obj_locality.zip);
    createCookie("full_address", obj_locality.full_address);
    createCookie("hval_url", hval_url);
}
//======================================================================================
function deleteHValCookies(obj_locality, udf) {
    // Cookie manipulation object.
    oCookieMgr = new CookieManager();
    deleteCookie("g_addr");
    deleteCookie("g_street");
    deleteCookie("g_zip");
    deleteCookie("g_city");
    deleteCookie("g_state");
    deleteCookie("g_lat");
    deleteCookie("g_lng");
    deleteCookie("g_match");
    //deleteCookie("udf");
    deleteCookie("hms_zip");
    deleteCookie("full_address");
    deleteCookie("hval_url", hval_url);
}
//======================================================================================
function bind(toObject, methodName){
    return function(data){toObject[methodName](data)}
}
//======================================================================================
//  HGGoogleMap object - Encapsulates HG use of Google maps api. Well, not entirely.
//======================================================================================
function HGGoogleMap() {
    this.g_addr     = "";
	this.g_street   = "";
	this.g_city     = "";
	this.g_state    = "";
	this.g_zip      = "";
	this.g_lat      = "";
	this.g_lng      = "";
	this.g_match    = 0;
    this.g_match_p  = 0;
    this.addressFormFieldEltID  = "";
    this.imagePropertyMarker    = "";
    this.containerEltID         = "";
    this.statusMessageEltID     = "";
    this.homeValuationStatus    = "";
    this.searchContext          = "";
    this.map;
    this.propPoint;
    this.zoomLevel              = 15; // Higher numbers represent higher resolution. 15 =~ zip area, 1 = world view.
    this.iconValuation                  = new google.maps.Icon();
    this.iconValuation.image            = "";
    this.iconValuation.imageSm          = "";
    this.iconValuation.iconSize         = new google.maps.Size(20, 22);
    this.iconValuation.iconAnchor       = new google.maps.Point(0, 22);
    this.iconValuation.infoWindowAnchor = new google.maps.Point(11, 5);
    this.iconComp                       = new google.maps.Icon();
    this.iconComp.image                 = ""; //hv_config.comp_icon.src;
    this.iconComp.iconSize              = new google.maps.Size(26, 21);
    this.iconComp.iconAnchor            = new google.maps.Point(0, 19);
    this.iconComp.infoWindowAnchor      = new google.maps.Point(9, 5);
    this.iconNearby                     = new google.maps.Icon();
    this.iconNearby.image               = ""; //hv_config.nearby_icon.src;
    this.iconNearby.iconSize            = new google.maps.Size(26, 21);
    this.iconNearby.iconAnchor          = new google.maps.Point(0, 19);
    this.iconNearby.infoWindowAnchor    = new google.maps.Point(9, 5);
    this.icons = { 
        'valuation'     : this.iconValuation,
        'comps'         : this.iconComp,
        'nearby'        : this.iconNearby,
        'alternate'     : this.iconValuation
    };
    this.TYPE_PROP_COMPS        = "comps";
    this.TYPE_PROP_NEARBYS      = "nearby";
    this.TYPE_PROP_VALUATION    = "valuation";
    this.TYPE_PROP_ALT          = "alternate";
    // Set by DTML page to match DTML consts.
    this.STATUS_HVAL_FOUND      = "";
    this.STATUS_HVAL_NOT_FOUND  = "";
    this.G_MATCH_NOT_FOUND        = "";
    this.G_MATCH_SINGLE_FOUND     = "";
    this.G_MATCH_SINGLE_ZIP_ONLY  = "";
    this.G_MATCH_MULTIPLE_FOUND   = "";
    this.NUM_PROPS_FOUND        = 0; // Set in hval_results_module DTML page.
    this.NUM_PROPS_NOT_FOUND    = 0; // Set in hval_results_module DTML page.
    this.propTypes              = [this.TYPE_PROP_COMPS, this.TYPE_PROP_NEARBYS, this.TYPE_PROP_ALT];
    this.numProperties          = 1;
    this.prop_valuation         = new Property("valuation", this.TYPE_PROP_VALUATION);
    this.prop_comps             = [];
    this.prop_nearby            = [];
    this.prop_alternate         = [];
    this.prop_overlay_titles    = { 
        'comps'         : 'Comparable Home Sale:',
        'nearby'        : 'Nearby Home Sale:',
        'valuation'     : '',
        'alternate'     : 'Alternate Address'
    };
    this.bl_alt_text        = "Click here to view homes for sale, including MLS listings sponsored by a local real estate broker.";
    this.cma_alt_text       = "Only a local professional can give you a real assessment of the value of your home. Click here for a free, no obligation market analysis.";
    this.realtor_alt_text   = "Click here to anonymously compare agent resumes and business history. Free, with no obligation.";
    this.mapErrorMessage    = "";
    this.s4s_form_url       = "/hpx03/get_cma"; //url for regular s4s form (/hpx03/get_cma) 
    this.s4s_visitor_url    = "/hpx03/req_cma_campaign"; //url for visitor form
    this.s4s_landing_url    = "/home_prices/index"; //url for s4s landing page for the "get a free profession home valuation" link
    this.ae_form_url        = "/find_real_estate_agent/index"; //url for ae form
    this.bl_url             = ""; // Set in homevalues
    this.show_faq           = 1;
    this.show_bl            = 1;
    this.show_visitor       = 0;
    this.show_popup         = 0;
    this.show_email_popup   = 0;
    // Street view properties.
    this.SV_LINK_ID_MAP = 'map';
    this.SV_LINK_ID_SV = 'streetview';
    this.sv_elt_id  = "pano";
    this.sv_exists = true;  // true = street view panorama was found, false = no flash capability or panorama not found
    this.sv_flash_available = true;
    this.map_link_span_content_text = {
        'map' : 'show map',
        'streetview' : 'show street view'};
    this.map_link_span_content_link = {
        'map' : '<a id="anchor_show_map" href="javascript: void(0);" onclick="oHGMap.toggleStreetMapDiv(\'' + this.SV_LINK_ID_MAP + '\');">' + this.map_link_span_content_text[this.SV_LINK_ID_MAP] + '</a>',
        'streetview' : '<a id="anchor_show_streetview" href="javascript: void(0);" onclick="oHGMap.toggleStreetMapDiv(\'' + this.SV_LINK_ID_SV + '\');">' + this.map_link_span_content_text[this.SV_LINK_ID_SV] + '</a>'};
}
//================================================================
HGGoogleMap.prototype.toggleStreetMapDiv = function(show_div) {
    var mapCanvas = document.getElementById(oHGMap.containerEltID);
    var mapLinkSpan = document.getElementById("span_show_map");
    var panoDiv = document.getElementById(this.sv_elt_id);
    var panoLinkSpan = document.getElementById("span_show_streetview");
    var panoFlashInstallDiv = document.getElementById('pano_flash_install');
    var bShowStreet = ( show_div == this.SV_LINK_ID_SV ? true : false );
    if ( bShowStreet ) {
        // Display "Install Flash" content in sv div if necessary.
        if ( !this.sv_flash_available ){
            // Display Flash install div, hide sv and map divs.
            panoFlashInstallDiv.style.display = 'block';
            panoDiv.style.display = 'none';
            mapCanvas.style.display = 'none';
        } else {
            // Display sv div, hide map.
            panoDiv.style.display = 'block';
            mapCanvas.style.display = 'none';
        }
        // Activate map link, de-activate street view link.
        this.toggleLink(this.SV_LINK_ID_MAP, false);
        this.toggleLink(this.SV_LINK_ID_SV, true);
    } else {
        // Display map div, activate street view link, de-activate map link.
        panoFlashInstallDiv.style.display = 'none';
        panoDiv.style.display = 'none';
        mapCanvas.style.display = 'block';
        this.toggleLink(this.SV_LINK_ID_MAP, true);
        this.toggleLink(this.SV_LINK_ID_SV, false);
    }
}
//================================================================
HGGoogleMap.prototype.toggleLink = function(id, selected) {
    var span_elt = document.getElementById('span_show_' + id);
    if ( span_elt != null ) {
        if ( selected ) {
            // Setting link as "selected", meaning we want to de-activate it.
            span_elt.style.color = "#000";
            span_elt.innerHTML = this.map_link_span_content_text[id];
        } else {
            if ( !this.sv_exists && id == this.SV_LINK_ID_SV ) {
                // Street view panorama does not exist, and we're working with the unselected street view link.
                // We'll disable the link in this case.
                span_elt.style.color = "#999";
                span_elt.innerHTML = this.map_link_span_content_text[id];
            } else {
                // Activate unselected link.
                span_elt.innerHTML = this.map_link_span_content_link[id];
            }
        }
    }
}
//================================================================
HGGoogleMap.prototype.showStreetView = function() {
    svClient = new GStreetviewClient();
    // Switch in the street view div while we see if there's a valid view to keep the map view from being displayed.
    this.toggleStreetMapDiv(oHGMap.SV_LINK_ID_SV);
    // See if we can get a panorama for this address.
    var obj = this;
    svClient.getNearestPanorama(this.propPoint, bind(obj, "handleNearestPano"));
}
//================================================================
HGGoogleMap.prototype.handleNearestPano = function(streetview_data) {
    /*
    SUCCESS = 200	Success (Since 2.104)
    SERVER_ERROR = 500	The server is not responding to queries. (Since 2.104)
    NO_NEARBY_PANO = 600	No panorama data was found. (Since 2.104)
    */
    var panoDiv = document.getElementById(oHGMap.sv_elt_id);
    var myPano = new GStreetviewPanorama(panoDiv);
    var obj = oHGMap;
    GEvent.addListener(myPano, "error", bind(obj, "handlePanoNoFlash"));
    if ( streetview_data.code == 200 ) {
        // Calculate viewing (yaw) angle.
        var angle = computePanoAngle(oHGMap.propPoint, streetview_data.location.latlng);
        // Load the street view.
        myPano.setLocationAndPOV(streetview_data.location.latlng, {yaw: angle, zoom: 0});
        oHGMap.sv_exists = true;
        oHGMap.toggleStreetMapDiv(oHGMap.SV_LINK_ID_SV);
    } else {
        oHGMap.sv_exists = false;
        oHGMap.toggleStreetMapDiv(oHGMap.SV_LINK_ID_MAP);
    }
}
//================================================================
HGGoogleMap.prototype.handlePanoNoFlash = function(errorCode) {
    var NO_FLASH = 603;
    // Flag street view existence.
    if ( errorCode == NO_FLASH ) {
        this.sv_flash_available = false;
        this.sv_exists = true;
    } else {    
        this.sv_exists = false;
    }
    // We'll deal with this silently and not show the street view, based on sv_exists.
    oHGMap.toggleStreetMapDiv(oHGMap.SV_LINK_ID_MAP);
    return;
}
//================================================================
function computePanoAngle(endLatLng, startLatLng) {
    var DEGREE_PER_RADIAN = 57.2957795;
    var RADIAN_PER_DEGREE = 0.017453;
    var dlat = endLatLng.lat() - startLatLng.lat();
    var dlng = endLatLng.lng() - startLatLng.lng();
    // We multiply dlng with cos(endLat), since the two points are very closeby,
    // so we assume their cos values are approximately equal.
    var yaw = Math.atan2(dlng * Math.cos(endLatLng.lat() * RADIAN_PER_DEGREE), dlat)
         * DEGREE_PER_RADIAN;
    return wrapAngle(yaw);
}
//================================================================
function wrapAngle(angle) {
    if (angle >= 360) {
        angle -= 360;
    } else if (angle < 0) {
        angle += 360;
    }
    return angle;
};
//===================================================
HGGoogleMap.prototype.addControls = function ( ) {
    // Default map controls.
    this.map.addControl(new google.maps.SmallMapControl());
    this.map.addControl(new google.maps.MenuMapTypeControl());
}
//===================================================
HGGoogleMap.prototype.loadMapDisplayMessage = function ( text ) {
    document.getElementById(this.containerEltID).innerHTML = "<p class='loading_message'>" + text + "</p>"
}
//===================================================
HGGoogleMap.prototype.loadMapMessage = function ( obj_args ) {
    var TAG_OPEN_SPAN_INFO  = "[%TAG_OPEN_SPAN_INFO%]";
    var TAG_CLOSE_SPAN      = "[%TAG_CLOSE_SPAN%]";
    var TAG_CRLF            = "[%TAG_CRLF%]";
    var strErrCode          = obj_args.error_code;
    var bClearHVMessage     = obj_args.clear_hv_msg;
    var strAltText          = obj_args.alt_text;
    var strAddress          = obj_args.address;
    //var mapErrorMessage = "A problem occurred while processing your request ( " + 
    //                      TAG_OPEN_SPAN_INFO + "tech_info=" + status_code + TAG_CLOSE_SPAN + " ). " +
    //                      "You may have entered an invalid address." + TAG_CRLF + TAG_CRLF + "Please try another search.";
    var mapErrorMessage = "The address you entered ( " + 
                          TAG_OPEN_SPAN_INFO + strAddress + TAG_CLOSE_SPAN + " ) " +
                          "could not be found. Please try another search.";
    if ( !isUndefined(document.getElementById("hval_content_status_message")) && document.getElementById("hval_content_status_message") != null ) {
        mapErrorMessage = mapErrorMessage.replace(TAG_OPEN_SPAN_INFO, "<span style='font-style: italic;'>");
        mapErrorMessage = mapErrorMessage.replace(TAG_CLOSE_SPAN, "</span>");
        mapErrorMessage = mapErrorMessage.replace(/\[%TAG_CRLF%\]/g, "<br/>");
        document.getElementById("hval_content_status_message").innerHTML = mapErrorMessage + "<br/><br/>";
        if ( bClearHVMessage && document.getElementById("hval_content_estimate") != null) {
            document.getElementById("hval_content_estimate").innerHTML = "";
        }
    }
    else {
        mapErrorMessage = mapErrorMessage.replace(TAG_OPEN_SPAN_INFO, "");
        mapErrorMessage = mapErrorMessage.replace(TAG_CLOSE_SPAN, "");
        mapErrorMessage = mapErrorMessage.replace(/\[%TAG_CRLF%\]/g, "\n");
        alert(mapErrorMessage);
    }
    // Handle field watermarking.
    if ( !isUndefined(oCSZFieldWaterMark) ) {
        //oAddrFieldWaterMark.toggle("blur");
        oCSZFieldWaterMark.toggle("blur");
    }
}
//===================================================
HGGoogleMap.prototype.load = function () {
    if ( google.maps.BrowserIsCompatible() ) {
        var geocoder = new google.maps.ClientGeocoder();
        geocoder.setBaseCountryCode("us");
        if ( this.g_addr ) {
            geocoder.getLocations(this.g_addr, getLocationResults);
        }
        else {
            geocoder.getLocations(this.g_street + " " + this.g_city + " " + this.g_state + " " + this.g_zip, getLocationResults);
        }
    }
}
//===================================================
function populateAddressFields() {
    if ( !isUndefined(oHGMap) ) {
        var strCSZ
        // addr = street, csz = city, state or zip
        strCSZ = oHGMap.g_street;
        strPrefix = ( oHGMap.g_street.length == 0 ? '' : ', ' );
        if ( oHGMap.g_city ) {
            if ( oHGMap.g_state ) {
                if ( oHGMap.g_zip ) {
                    strCSZ += strPrefix + oHGMap.g_city + ', ' + oHGMap.g_state + ' ' + oHGMap.g_zip;
                }
                else {
                    strCSZ += strPrefix + oHGMap.g_city + ', ' + oHGMap.g_state;
                }
            }
            else {
                strCSZ += strPrefix + oHGMap.g_city;
            }
        }
        else if ( oHGMap.g_zip ) {
            strCSZ += strPrefix + oHGMap.g_zip;
        }
        var oForm = document.getElementById('hvForm');
        //oForm['addr'].value = strAddr;
        oForm['csz'].value  = strCSZ.replace(/^\s+|\s+$/ig, ''); //Strip leading & trailing spaces.
    }
}
//===================================================
HGGoogleMap.prototype.loadHValProperties = function () {
    // Populate search form with address data.
    //populateAddressFields();
    // Load comps/nearbys data from hidden divs.
    var oTmpProp;
    eval("oTmpProp = " + document.getElementById("prop_valuation").innerHTML);
    this.prop_valuation.type = "valuation";
    this.prop_valuation.set(oTmpProp);
    this.prop_valuation.values["lat"] = this.g_lat;
    this.prop_valuation.values["lon"] = this.g_lng;
    // Set the number of properties to loop through, depending on whether we found a valuation or not.
    if ( this.homeValuationStatus == this.STATUS_HVAL_FOUND ) {
        this.numProperties = this.HVAL_NUM_PROPS_FOUND;
    }
    else {
        this.numProperties = this.HVAL_NUM_PROPS_NOT_FOUND;
    }
    // Loop over each property type (comps, nearbys...).
    for ( i in this.propTypes ) {
        var strPropType = this.propTypes[i];
        // Loop over the specified number of properties. 
        for ( j = 1; j <= this.numProperties; j++ ) {
            var strPropName = Property.prototype.buildEltName(strPropType, j);
            if ( !isUndefined(document.getElementById(strPropName)) && document.getElementById(strPropName) != null ) {
                var oPropElt = document.getElementById(strPropName);
                var oProp;
                // Create object from JSON string rendered as a hidden div for each address.
                eval("oProp = " + oPropElt.innerHTML);
                var oProperty = new Property(strPropName, strPropType);
                // Populate Property object with div data.
                oProperty.set(oProp);
                // Add property to an array of properties.
                this[oProperty.buildObjName(strPropType)].push(oProperty);
            }
        }
    }
}
//===================================================
HGGoogleMap.prototype.doMatchProcessing = function(obj_placemark) {
    var bTriggerValuationMarkerClick = false;
    switch ( this.g_match ) {
        case G_MATCH_NOT_FOUND:
            break;
        case G_MATCH_SINGLE_FOUND:
            strMessage = "";
            bTriggerValuationMarkerClick = true;
            break;
        case G_MATCH_MULTIPLE_FOUND:
            if ( this.g_match_p == G_MATCH_MULTIPLE_FOUND ) {
                this.zoomLevel = 4; // Zoom out when alternates are displayed.
                this.loadAlternateAddresses(obj_placemark);
            }
            break;
        case G_MATCH_SINGLE_ZIP_ONLY:
            this.zoomLevel = 13;
            bTriggerValuationMarkerClick = true;
            break;
    }
    // The return value will determine whether or not the valuation property info bubble is automatically displayed.
    return bTriggerValuationMarkerClick;
}
//===================================================
HGGoogleMap.prototype.loadAlternateAddresses = function (obj_placemark) {
    var strContent = "<div class=\"prop_items_header prop_item\"><h2>Alternate Addresses</h2></div>";
    var oTmpProps = [];
    var oLocalityData = new Object();
    var intMarkCounter = 0;
    var oFirstAddressPoint;
    var bFirstAddress = true;
    var dblDistance = 0.0;
    for ( mark in obj_placemark ) {
        if ( intMarkCounter == 0 ) {
            oFirstAddressPoint = new google.maps.LatLng(obj_placemark[mark].Point.coordinates[G_COORD_LAT], obj_placemark[mark].Point.coordinates[G_COORD_LNG]);
        }
        var oProp = new Property(0, this.TYPE_PROP_ALT); // Set all alternates to valuation types for display.
        if ( !bFirstAddress ) {
            // Calculate distance between furst address and each of the others.
            oPoint = new google.maps.LatLng(obj_placemark[mark].Point.coordinates[G_COORD_LAT], obj_placemark[mark].Point.coordinates[G_COORD_LNG]);
            dblDistance = oFirstAddressPoint.distanceFrom(oPoint) * 0.000621371192; // Meters -> miles
        }
        getLocalityData(obj_placemark[mark].AddressDetails.Country, oLocalityData);
        var oData = { 
            'streetaddress' : oLocalityData.full_address,
            'lat'           : obj_placemark[mark].Point.coordinates[G_COORD_LAT],
            'lon'           : obj_placemark[mark].Point.coordinates[G_COORD_LNG],
            'distance'      : dblDistance
        };
        oProp.set(oData);
        // Populate LocalityData object for use in building the hval url.
        oLocalityData.lat           = oProp.values["lat"];
        oLocalityData.lng           = oProp.values["lon"];
        oLocalityData.userAddress   = obj_placemark[mark].address; //oProp.values["streetaddress"];
        oLocalityData.match         = G_MATCH_SINGLE_FOUND;
        var intPropID           = parseInt(mark) + 1;
        var strIconAltID        = "icon_alt_" + intPropID;
        var strLinkAltID        = "link_alt_" + intPropID;
        var strDisplayAddress   = obj_placemark[mark].address; //( oLocalityData.full_address ? oLocalityData.full_address : obj_placemark[mark].address );
        oProp.hval_url = "/homevalues/?csz=" + escape(strDisplayAddress) + "&unit=" + oAddrParser.parse_unit(this.g_addr);//buildHValURL(oLocalityData, document.hvForm, false);
        // Add the property to the list of alternates.
        this[oProp.buildObjName(this.TYPE_PROP_ALT)].push(oProp);
        // Provide alternate style classes for last address; in IE the div is displayed under the tab container.
        if ( intPropID == 10 ) {
            strPropDivClassPostFix = "_last"
        }
        else {
            strPropDivClassPostFix = ""
        }
        strContent += "<div class=\"prop_item\" style='height: 25px; padding-top: 4px; padding-bottom: 3px;'>" +
                      "<a onmouseover='toggleContent(\"" + strIconAltID + "\")' onmouseout='toggleContent(\"" + strIconAltID + "\")' href=\"javascript: openHvInfoWindow('" + intPropID + "', '" + this.TYPE_PROP_ALT + "');\" border=\"0\"><img src=\"" + this.iconValuation.imageSm + "\" class=\"prop_img\"></a>" +
                      "<div><a onmouseover='toggleContent(\"" + strLinkAltID + "\")' onmouseout='toggleContent(\"" + strLinkAltID + "\")' href=\"" + oProp.hval_url + "\">" + strDisplayAddress + "</a></div>" +
                      "</div>" +
                      "<div id='" + strIconAltID + "' class='alt_text alt_icon" + strPropDivClassPostFix + "'>Click here to view " + strDisplayAddress + " on the map.</div>" +
                      "<div id='" + strLinkAltID + "' class='alt_text alt_link" + strPropDivClassPostFix + "'>Click here to view a home value estimate for " + strDisplayAddress + ".</div>";
        intMarkCounter += 1;
        bFirstAddress = false;
    }
    document.getElementById("hv_results_x").innerHTML = strContent;
}
//======================================================================================
//  END: HGGoogleMap object/methods.
//======================================================================================
//======================================================================================
//  Property object and functions (copied fron hv.js
//  and modified).
//======================================================================================
function Property(id, type) {
    this.id                 = id||"";    // Used in old hv.js code, not here.
    this.type               = type||"";
	this.values             = [];
	this.marker             = null;
    this.triggerMarkerClick = false;
    this.map;
    this.fields             = ["streetaddress","city","state","zip","proptype","livingarea","bedrooms","baths","lon","lat",
                              "assessyear","county","lotarea","stories","assessprice","salesprice","sales_date","built_year","distance"];
    this.overlayTitle       = "";
    this.ht_valuation       = ["hval_res_ae1","hval_res_bl1","hval_res_s4s2"];
    this.ht_comps           = ["hval_res_ae2","hval_res_bl2","hval_res_s4s3"];
    this.ht_nearby          = ["hval_res_ae3","hval_res_bl3","hval_res_s4s4"];
    this.ht_alt             = [];
    this.hval_url           = "";
}
//===================================================
//  function to get attribute values from one row of property data and set values to the Property object
//===================================================
Property.prototype.set = function (obj_property_data) {
    for(var i=0; i < this.fields.length; i++) {
        var field_name = this.fields[i];
        eval("var val = obj_property_data." + field_name);
        if( field_name == "salesprice" ) {
            val = ( val == 0 | isUndefined(val) ? "n/a" : this.formatPrice(val) );
        }
        else if ( field_name == "lon" ) {
            // HVal stores longitudes as absolute values, so we need to make it negative since Google uses negative longitudes 
            // for locations west of the prime meridian.
            val = ( val < 0.0 ? val : (-1) * (val) ); 
        }
        else if ( isUndefined(val) ) {
            val = "n/a";
        }
        else if ( val.length == 0 ) {
            val = "n/a";
        }
        this.values[field_name] = val;
    }
    this.overlayTitle = this.getOverlayTitle();
}
//===================================================
Property.prototype.getOverlayTitle = function () {
    var strTitle = oHGMap.prop_overlay_titles[this.type];
    switch (this.type) {
        case oHGMap.TYPE_PROP_COMPS:
            strTitle += "<br/>" + this.values["streetaddress"];
            break;
        case oHGMap.TYPE_PROP_NEARBYS:
            strTitle += "<br/>" + this.values["streetaddress"];
            break;
        case oHGMap.TYPE_PROP_VALUATION:
            strTitle = this.values["streetaddress"];
            break;
        case oHGMap.TYPE_PROP_ALT:
            strTitle = this.values["streetaddress"];
            break;
        default:
            strTitle = "Address";
    }
    return strTitle;
}
//===================================================
Property.prototype.formatPrice = function (val) {
    return ( val.substr(0,1) == "$" ? val : "$" + val );
}
//===================================================
//  Helper object for getWinInfo()
//===================================================
function BubbleButton(id){
	this.id       = (id ? id : '');
	this.url      = ''
	this.label    = '';
	this.alt_text = '';
	this.alt_id   = '';
	this.genUI    = function(){
		var strBtnStyle   = (document.all ? 'font-weight:bold; font-size:11px;' : 'font-weight:bold; font-size:11px;');
		var strLabelWidth = ( id != 'cma' ? (document.all ? 'width:92%' : 'width:90%'): '');
		var strAltMargin  = (document.all ? 'margin-right:10px; margin-left:-17px' : 'margin-left:-17px; margin-top:25px;');
		var strAltClass   = ( id != 'cma' ? 'alt_text' : (document.all ? 'alt_text_cma_ie' : 'alt_text_cma'));
		var strAltDiv     = ( this.alt_text.length == 0 ? '' : '<div class="clear" style="' + strAltMargin + '"><div id="' + this.alt_id + '" class="' + strAltClass + '">' + this.alt_text + '</div></div>');
		var strAltOvers   = ( this.alt_text.length == 0 ? '' : ' onmouseover="toggleContent(\'' + this.alt_id + '\')" onmouseout="toggleContent(\'' + this.alt_id + '\')');
		return '<div style="padding: 3px;"><div class="btn_short" style="margin:0px;float:center;"><div class="left"></div>' +
			'<div class="label" style="' + strLabelWidth + '; text-align: center; ">' +
			'<a href="javascript:void(0);" style=" ' + strBtnStyle + '" onclick="javascript:window.open(\'' + this.url + '\');"' + strAltOvers + '">' + this.label + '</a>' +
			'</div>' + 
			'<div class="right"></div></div></div>' + strAltDiv;
	}
}
//===================================================
//  Helper object for getWinInfo()
//===================================================
function BubbleButton2(id){
	this.id       = (id ? id : '');
	this.url      = ''
	this.label    = '';
	this.alt_text = '';
	this.alt_id   = '';
	this.genUI    = function(){
		var strBtnStyle   = (document.all ? 'font-weight:bold; font-size:11px;' : 'font-weight:bold; font-size:11px;');
		var strLabelWidth = ( id != 'cma' ? (document.all ? 'width:92%' : 'width:90%'): '');
		var strAltMargin  = (document.all ? 'margin-right:10px; margin-left:0px' : 'margin-left:0px; margin-top:0px;');
		var strAltClass   = ( id != 'cma' ? 'alt_text' : (document.all ? 'alt_text_cma_ie' : 'alt_text_cma'));
		var strAltDiv     = ( this.alt_text.length == 0 ? '' : '<div class="clear" style="' + strAltMargin + '"><div id="' + this.alt_id + '" class="' + strAltClass + '">' + this.alt_text + '</div></div>');
		var strAltOvers   = ( this.alt_text.length == 0 ? '' : ' onmouseover="toggleContent(\'' + this.alt_id + '\')" onmouseout="toggleContent(\'' + this.alt_id + '\')');
		return '<a href="javascript:void(0);" onclick="javascript:window.open(\'' + this.url + '\');"' + strAltOvers + '"><img src=\"http://images.homegain.com/i/hval/get_cma_btn.png\" /></a>' + strAltDiv;
	}
}
//===================================================
function displayButton(container_name, ui){
	if(document.getElementById(container_name) != null){
		document.getElementById(container_name).innerHTML = ui;
	}
}
//===================================================
//  Get property detail content for popup
//===================================================
Property.prototype.getWinInfo = function(){
	var strHTML;
	switch(this.type){
		case oHGMap.TYPE_PROP_ALT: // Alternate address bubble info.
			var oHValButton = new BubbleButton();
			oHValButton.url   = this.hval_url;
			oHValButton.label = 'View Home Valuation';
			strHTML = "<table cellspacing=0 cellpadding=0 border=0 class='bubble'>" +
				"<tr><td>" +
				"<table class='info_valuation' cellspacing=1 cellpadding=1 border=0>" +
				"<thead><tr><td>" + this.overlayTitle + "<br/></td></tr></thead>" + 
				"<tr><td><br/></td></tr>" +
				"<tr><td>" + oHValButton.genUI();
			break;
		default: // Comp/nearby bubble info.
			strPropDetailsHTML = "<tr class='light'><td width='50%'><b>Last Sold:</b></td><td>" + this.values["sales_date"] + "</td></tr>" +
				"<tr class='light'><td><b>Sale Price:</b></td><td>" + this.values["salesprice"] + "</td></tr>" +
				"<tr class='light'><td><b>Beds / Bath:</b></td><td>" + this.values["bedrooms"] + " / " + this.values["baths"] + "</td></tr>" +
				"<tr class='light'><td><b>Square Footage:</b></td><td>" + this.values["livingarea"] + "</td></tr>"
			if(this.type == oHGMap.TYPE_PROP_VALUATION){
				strTitle       = (oHGMap.homeValuationStatus == oHGMap.STATUS_HVAL_FOUND ? this.overlayTitle : oHGMap.full_address + '<br/>');
				strPropDetails = (oHGMap.homeValuationStatus == oHGMap.STATUS_HVAL_FOUND ? strPropDetailsHTML : "");
			} else {
				strTitle       = this.overlayTitle;
				strPropDetails = strPropDetailsHTML;
			}
			if(oHGMap.show_bl && oHGMap.bl_url.length > 0){
				oBLButton = new BubbleButton();
				oBLButton.url      = oHGMap.bl_url + (oHGMap.bl_url.indexOf("?")==-1?"?":"&") + "ht=" + this["ht_" + this.type][1] + "&entryid=" + entry_id + "&mod=" + mod;
				oBLButton.alt_id   = 'bl_alt';
				oBLButton.alt_text = oHGMap.bl_alt_text;
				oBLButton.label    = "View " + oHGMap.g_city + " Homes for Sale";
				strBLButton = oBLButton.genUI();
			} else {
				strBLButton = '';
			}
			strHTML = "<table cellspacing=0 cellpadding=0 border=0 class='bubble'>" +
				"<tr><td>" +
				"<table class='info_" + this.type + "' cellspacing=1 cellpadding=1 border=0>" +
				"<thead><tr><td colspan=2>" + strTitle + "<br/></td></tr></thead>" +
				strPropDetails +
				"</table>" + strBLButton;
			if(agent_uid != "0"){
				if(this.type == oHGMap.TYPE_PROP_VALUATION){
					strCMAURL = oHGMap.s4s_form_url + "?fresh_search=1&streetaddress=" + ( this.values['streetaddress'] != 'n/a' ? this.values['streetaddress'] : '') + "&city=" + oHGMap.g_city + "&state=" + oHGMap.g_state + "&zip=" + oHGMap.g_zip + "&ht=" + this["ht_" + this.type][2] + "&entryid=" + entry_id;
				} else {
					strCMAURL = oHGMap.s4s_form_url + "?fresh_search=1&streetaddress=" + ( this.values['streetaddress'] != 'n/a' ? this.values['streetaddress'] : '') + "&city=" + ( this.values['city'] != 'n/a' ? this.values['city'] : '') + "&state=" + ( this.values['state'] != 'n/a' ? this.values['state'] : '') + "&zip=" + ( this.values['zip'] != 'n/a' ? this.values['zip'] : '') + "&ht=" + this["ht_" + this.type][2] + "&entryid=" + entry_id;
				}
				if(this.type == oHGMap.TYPE_PROP_VALUATION && oHGMap.homeValuationStatus == oHGMap.STATUS_HVAL_FOUND){
					// For valuation property where a successful valution has been found, render button next to the price range.
					oCMAButton = new BubbleButton2('cma');
					oCMAButton.url      = strCMAURL;
					oCMAButton.alt_id   = 'cma_alt';
					oCMAButton.alt_text = oHGMap.cma_alt_text;
					oCMAButton.label    = 'Get A Custom Home Value';
					displayButton('get_cma_link', oCMAButton.genUI());
				} else {
					oCMAButton = new BubbleButton();
					oCMAButton.url      = strCMAURL;
					oCMAButton.alt_id   = 'cma_alt';
					oCMAButton.alt_text = oHGMap.cma_alt_text;
					oCMAButton.label    = 'Get Custom Home Value';
					strHTML += oCMAButton.genUI();
				}
			} else {
				oRealtorButton = new BubbleButton();
				oRealtorButton.url      = oHGMap.ae_form_url + "?ht=" + this["ht_" + this.type][0] + "&entryid=" + entry_id + "&mod=" + mod;
				oRealtorButton.alt_id   = 'realtor_alt';
				oRealtorButton.alt_text = oHGMap.realtor_alt_text;
				oRealtorButton.label    = 'Find and Compare REALTORS&reg;';
				if(this.type == oHGMap.TYPE_PROP_VALUATION && oHGMap.homeValuationStatus == oHGMap.STATUS_HVAL_FOUND){
					// For valuation property where a successful valution has been found, render button next to the price range.
					oCMAButton = new BubbleButton2('cma');
					oCMAButton.url      = oHGMap.s4s_landing_url + "?fresh_search=1&ht=" + this["ht_" + this.type][2] + "&entryid=" + entry_id;
					oCMAButton.alt_id   = 'cma_alt';
					oCMAButton.alt_text = oHGMap.cma_alt_text;
					oCMAButton.label    = 'Get A Custom Home Value';
					displayButton('get_cma_link', oCMAButton.genUI());
					strHTML += oRealtorButton.genUI();
				} else {
					oCMAButton = new BubbleButton();
					oCMAButton.url        = oHGMap.s4s_landing_url + "?fresh_search=1&ht=" + this["ht_" + this.type][2] + "&entryid=" + entry_id;
					oCMAButton.alt_id     = 'cma_alt';
					oCMAButton.alt_text   = oHGMap.cma_alt_text;
					oCMAButton.label      = 'Get Custom Home Value';
					strHTML += oCMAButton.genUI() + oRealtorButton.genUI();
				}
		}
		strHTML += "</td></tr></table>";
	}
	return strHTML;
}
//===================================================
//  Place marker on the map for each property
//===================================================
Property.prototype.addMarker = function(bool_trigger) {
    var point = new google.maps.LatLng(this.values["lat"], this.values["lon"]);
    var bValidPoint = ( !isUndefined(point.x) && !isUndefined(point.y) && typeof(point.x) != "string" && typeof(point.y) != "string" );
    if ( bValidPoint ) {
        var marker = new google.maps.Marker(point, oHGMap.icons[this.type])
        var this_obj = this;
        this.marker = marker;
        GEvent.addListener(this.marker, "click", function() {
            var info = this_obj.getWinInfo();
            marker.openInfoWindowHtml(info);
        });
        oHGMap.map.addOverlay(this.marker);
        if ( bool_trigger ) {
            google.maps.Event.trigger(this.marker, "click");
        }
    }
}
//===================================================
Property.prototype.buildEltName = function (type, id) {
    return "prop_" + type + "_" + id;
}
//===================================================
Property.prototype.buildObjName = function (type) {
    return "prop_" + type;
}
//======================================================================================
//  END: Property object and methods
//======================================================================================
//======================================================================================
//  Function to popup property details when clicking the marker
//======================================================================================
function openHvInfoWindow(id, p_type) {
    var strObjName = Property.prototype.buildObjName(p_type);
    var prop = oHGMap[strObjName][id - 1];
    prop.marker.openInfoWindowHtml(prop.getWinInfo());
}
