    /*
        Use this function as a sort of static factory function to create a new listing from input
        (likely xml) text. Note that this is highly dependent on specific implementation!
        
        The function returns the new Listing object
    */
    Listing.prototype.createListing = function(text) {
        var L = new Listing("", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "");     
        //in real life the way to do this would be to grab the texting text and 
        //create an xmlDom object from it and then parse nodes, but there appear to be
        //some browser compatibility pitfalls, so use regex instead.

        RegExp.multiline = true;
        idRx = /^.*<Listing ID="([^" >]*)".*$/gm
        bedRx = /^.*<Bed>([^<]*)<\/Bed>.*$/gm
        bathRx = /^.*<Bath>([^<]*)<\/Bath>.*$/gm
        latRx = /^.*<Lat>([^<]*)<\/Lat>.*$/gm
        longRx = /^.*<Long>([^<]*)<\/Long>.*$/gm
        priceRx = /^.*<Price>([^<]*)<\/Price>.*$/gm
        priceMinRx = /^.*<PriceMin>([^<]*)<\/PriceMin>.*$/gm
        priceMaxRx = /^.*<PriceMax>([^<]*)<\/PriceMax>.*$/gm
        availRx = /^.*<Avail>([^<]*)<\/Avail>.*$/gm
        lastUpdateRx = /^.*<LastUpdDt>([^<]*)<\/LastUpdDt>.*$/gm
        imgRx = /^.*<SURL>([^<]*)<\/SURL>.*$/gm
        addrRx = /^.*<Addr1>([^<]*)<\/Addr1>.*$/gm
        cityRx = /^.*<City>([^<]*)<\/City>.*$/gm
        stateRx = /^.*<State>([^<]*)<\/State>.*$/gm
        zipRx = /^.*<Zip>([^<]*)<\/Zip>.*$/gm
        descRx = /^.*<TextDesc2>([^<]*)<\/TextDesc2>.*$/gm
        sourceRx = /^.*<SourceCustID>([^<]*)<\/SourceCustID>[^<]*<Name>([^<]*)<\/Name>.*$/gm
        typeRx = /^.*<ITYPE3>([^<]*)<\/ITYPE3>.*$/gm
        //showRx = /^.*<???>([^<]*)<\/???>.*$/gm
        //bldrRx = /^.*<???>([^<]*)<\/???>.*$/gm
        //urlRx = /^.*<ITYPE3>([^<]*)<\/ITYPE3>.*$/gm
        trimRx = /^[\n\t\r\v ]*(.+)[\n\t\r\v ]*$/gm
        
        if (idRx.test(text)) {
            L.id = text.replace(idRx, "$1").replace(trimRx, "$1");
        }

        if (latRx.test(text)) {
            L.lat = text.replace(latRx, "$1").replace(trimRx, "$1");
        }
        if (longRx.test(text)) {
            L.lon = text.replace(longRx, "$1").replace(trimRx, "$1");
        }

        if (bedRx.test(text)) {
            L.beds = text.replace(bedRx, "$1").replace(trimRx, "$1");
        }
        if (bathRx.test(text)) {
            L.baths = text.replace(bathRx, "$1").replace(trimRx, "$1");
        }

        if (priceMinRx.test(text)) {
            L.priceMin = text.replace(priceMinRx, "$1").replace(trimRx, "$1");
        }
        else if (priceRx.test(text)) {
            L.priceMin = text.replace(priceRx, "$1").replace(trimRx, "$1");
        }
        if (priceMaxRx.test(text)) {
            L.priceMax = text.replace(priceMaxRx, "$1").replace(trimRx, "$1");
        }
        else if (priceRx.test(text)) {
            L.priceMax = text.replace(priceRx, "$1").replace(trimRx, "$1");
        }

        if (availRx.test(text)) {
            L.available = text.replace(availRx, "$1").replace(trimRx, "$1");
        }
        if (lastUpdateRx.test(text)) {
            L.lastUpdate = text.replace(lastUpdateRx, "$1").replace(trimRx, "$1");
        }
        
        if (imgRx.test(text)) {
            L.imgSrc = text.replace(imgRx, "$1").replace(trimRx, "$1");
        }

        if (addrRx.test(text)) {
            L.address = text.replace(addrRx, "$1").replace(trimRx, "$1");
        }
        if (cityRx.test(text)) {
            L.city = text.replace(cityRx, "$1").replace(trimRx, "$1");
        }
        if (stateRx.test(text)) {
            L.state = text.replace(stateRx, "$1").replace(trimRx, "$1");
        }
        if (zipRx.test(text)) {
            L.zip = text.replace(zipRx, "$1").replace(trimRx, "$1");
        }

        if (descRx.test(text)) {
            L.description = text.replace(descRx, "$1").replace(trimRx, "$1");
        }
        if (sourceRx.test(text)) {
            L.source = text.replace(sourceRx, "$2").replace(trimRx, "$1");
        }
        if (typeRx.test(text)) {
            L.type = text.replace(typeRx, "$1").replace(trimRx, "$1");
        }
        //skip builder for now since this is Realtor data
        //semi-hard code the URL since again only Realtor data for the demo. In the 
        //real world getting this URL may be problematic...
        L.url = "http://sto02web030d/FindHome/HomeListing.asp?snum=1&locallnk=yes&frm=byzip&mnbed=0&mnbath=0&mnprice=0&mxprice=99999999&js=off&pgnum=1&fid=so&mnsqft=&mls=xmls&areaid=91362&typ=1%2C+2%2C+3%2C+4%2C+5%2C+6&poe=realtor&zp=91362&sbint=&vtsort=&sid=05AB39B9326EC&snumxlid=" + L.id + "&lnksrc=00002"

        return L;
    }

    /*
        Use this function to display a listing from the listings array given a listingID.
        The function assumes the listing is already in the array. Use requestAdd() instead of display
        if you aren't sure whether the listing is present.
        
        This function is very implementation specific for each vertical.
    */
    listings.display = function(listingid) {
        var pos = this.hasKey(listingid);
        /* //uncomment to debug
        if (pos >=0) {
            alert(this[pos].toLongString());
        }
        else 
        {
            alert("nothing to display");
        }
        */
        var L = this[pos];
        //alert(L.lat);
        //alert(L.lon);
        
        var temp = "";
        var d = document.getElementById("listingContainer");
        //now we start making wild assumptions about the container
        //from here on out we're likely Realtor-specific although the general strategy is
        //the same for all verticals.
        
        //border colors
        var obj2 = null;
        var obj = document.getElementById(d.id + "_top");
        if (obj) {
            if (L.showcase != "") {
                if (L.showcase == 1) {
                    obj.className = "listingContainerTopFeatured";
                } else {
                    obj.className = "listingContainerTop";
                }
            } else {
                    obj.className = "listingContainerTop";
            }
        }
        obj = document.getElementById(d.id + "_inner");
        if (obj) {
            if (L.showcase != "") {
                if (L.showcase == 1) {
                    obj.className = "listingContainerInnerFeatured";
                } else {
                    obj.className = "listingContainerInner";
                }
            } else {
                    obj.className = "listingContainerInner";
            }
        }
        obj = document.getElementById(d.id + "_bottom");
        if (obj) {
            if (L.showcase != "") {
                if (L.showcase == 1) {
                    obj.className = "listingContainerBottomFeatured";
                } else {
                    obj.className = "listingContainerBottom";
                }
            } else {
                    obj.className = "listingContainerBottom";
            }
        }
        
        
        obj = document.getElementById(d.id + "_test");
        if (obj) {
                //replaceContent(obj, L.toLongString().replace("\n", ", "));
        }
        
        obj = document.getElementById(d.id + "_img");
        //obj2 = document.getElementById(d.id + "_noimg");
        if (obj) {
            if (L.imgSrc != "") {
                obj.src = L.imgSrc;
                obj.style.display = "";
                /*if (obj2) {
                    obj2.style.display = "none";
                }*/
            } else {
                obj.style.display = "none";
                /*if (obj2) {
                    obj2.style.display = "";
                }*/
            }
        }/* else if (obj2) {
            obj2.style.display = "";
        }*/

        /*obj = document.getElementById(d.id + "_showcase");
        if (obj) {
            if (L.showcase == "1" && L.imgSrc != "") {
                obj.style.display = "";
            } else {
                obj.style.display = "none";
            }
        } */


        obj = document.getElementById(d.id + "_address");
        if (obj) {
            if (L.address && L.address.length > 1) {
                obj.style.display = "";
                replaceContent(obj, L.address + "<br/>");//
            } else {
                replaceContent(obj, "");
                obj.style.display = "none";
            }        
        }
        
        
        obj = document.getElementById(d.id + "_citystatezip");
        if (obj) {
            temp = L.city;
            if (L.state && L.state != "") {
                if (temp.length > 0) temp += ", ";
                temp += L.state;
            }
            if (L.zip && L.zip != "") {
                if (temp.length > 0) temp += " ";
                temp += L.zip;
            }
            replaceContent(obj, temp);
        }

        obj = document.getElementById(d.id + "_company");
        if (obj) {
            if (L.ListedBy && L.ListedBy.length > 0) 
            {
                var str = L.ListedBy;
                str = replaceAll(str, "+"," ");
                obj.style.display = "";                
                replaceContent(obj, "<a href=\"javascript:refreshWindow(" + L.id + ")\" class=\"LBD_body_noOffer\">" + unescape(str) + "</a><br />");
            } 
            else {
                replaceContent(obj, "");
                obj.style.display = "none";
            }
        }
                
        obj = document.getElementById(d.id + "_phone");
        if (obj) {  
            if (L.ListedBy && L.ListedBy.length > 0 && L.ListedBy != "")
                {               
                replaceContent(obj, "<a href=\"javascript:refreshWindow(" + L.id + ")\"><font color=\"#0770C2\" onmouseover=\"this.color='#014C87'\" onmouseout=\"this.color='#0770C2'\">Phone</font></a><br />");            
                }
            else{
                replaceContent(obj, "");
                obj.style.display = "none";
                }
        }
//height=\"15px\"
        
       obj = document.getElementById(d.id + "_offer");
        if (obj) {
            if (L.type && L.type.length > 0 && L.type == "yes") {
                obj.style.display = "";
                replaceContent(obj, "<br/>Special Offer Available");
                } 
            else if (L.type && L.type.length > 0 && L.type != "no") {
                obj.style.display = "";
                var str = L.type;
                str = replaceAll(str, "+"," ");
                replaceContent(obj, "<br/>" + unescape(str));
                }
            else {
                obj.style.display = "";
                replaceContent(obj, "<br/>");                
                }
        }
        
        /*obj = document.getElementById(d.id + "_type");
        if (obj) {
            r = /,/gi;
            replaceContent(obj, L.type.replace(r, ", "));
        }
        
        obj = document.getElementById(d.id + "_company");
        if (obj) {
            if (L.company && L.company.length > 0) {
                obj.style.display = "";
                replaceContent(obj, L.company);
            } else {
                replaceContent(obj, "");
                obj.style.display = "none";
            }
        }
        
        obj = document.getElementById(d.id + "_builder");
        if (obj) {
            if (L.builder && L.builder.length > 0) {
                obj.style.display = "";
                replaceContent(obj, L.builder);
            } else {
                replaceContent(obj, "");
                obj.style.display = "none";
            }
        }
        
        obj = document.getElementById(d.id + "_price");
        if (obj) {
            if (L.priceMin != "" && L.priceMin != "0" && !isNaN(L.priceMin)) {
                replaceContent(obj, formatCurrency(L.priceMin, "us-en"));
            } else {
                //replaceContent(obj, "no price given");
                replaceContent(obj, "Call");
            }
        }
        obj = document.getElementById(d.id + "_bed");
        if (obj) {
            var minbed = (4 * L.beds) / 4;
  
            if(minbed == "0"){
                replaceContent(obj, "Studio");
            }
            else { 
                replaceContent(obj, (4 * L.beds) / 4 + " Bed"); 
            }
        }
        obj = document.getElementById(d.id + "_bath");
        if (obj) {
            replaceContent(obj, (4 * L.baths) / 4 + " Bath");
        }
        obj = document.getElementById(d.id + "_available");
        if (obj) {
            if (L.available && L.available.length > 0) {
                obj.style.display = "";
                replaceContent(obj, "Available Date: " + L.available);
            } else {
                replaceContent(obj, "");
                obj.style.display = "none";
            }
        }
        obj = document.getElementById(d.id + "_lastUpdate");
        if (obj) {
            if (L.lastUpdate && L.lastUpdate.length > 0) {
                obj.style.display = "";
                replaceContent(obj, "Last Updated: " + L.lastUpdate);
            } else {
                replaceContent(obj, "");
                obj.style.display = "none";
            }
        }
        
        obj = document.getElementById(d.id + "_source");
        if (obj) {
            if (L.source && L.source.length > 0) {
                replaceContent(obj, "source: <a href=\"" + L.url + "\" target=\"_blank\">" + L.source + "</a>");
            } else {
                replaceContent(obj, "");
            }
        }
        
        obj = document.getElementById(d.id + "_focal1");
        if (obj) {
            if (L.url && L.url.length > 0) {
                obj.style.display = "";
                obj.href = L.url;
            } else {
                obj.style.display = "none";
            }
        }
        
        obj = document.getElementById(d.id + "_focal2");
        if (obj) {
            if (L.url && L.url.length > 0) {
                obj.style.display = "";
                obj.href = L.url;
            } else {
                obj.style.display = "none";
            }
        }
        
        obj = document.getElementById(d.id + "_description");
        if (obj) {
            if (L.description && L.description.length > 0) {
                replaceContent(obj, "<br/>" + L.description);
            } else {
                replaceContent(obj, "");
            }
        }*/



        d.style.display = "";

        anchorPoint.y -= parseInt(d.offsetHeight);     
        setPosition(d, anchorPoint, "left-top");
        highlightListing(listingid, false);
        if (reposition) {
            repositionMapToListing(listingid);
        }
    }


/*
    define your specific URL for retrieving listing data here. The URL is assumed to be an
    http GET and likely must be on the same server as the javascript and/or the page using the 
    javascript.
*/
function constructListingUrl(listingId) {
    return "listing.aspx?listingid=" + listingId;
}


/* javascript callback function that adds points to the map. Included here because icons and such are 
   specific to each vertical.
   note that the markers array is assumed to be defined in the generic mapUtils file, and that there is
   also a Marker object in that file which simplifies some workings. Also, note that this function does *not* 
   build the markers array; it only sends it to flash.
*/
        function onMapCreationComplete()
        {
            //alert("onMapCreationComplete");
            var theMap = window.document["map"];
            //var theMap = document.getElementById("embMap");
            //if (!theMap) theMap = document.getElementById("objMap");
			//var markerStyle = new AWSWFMarkerStyle();
			var markerStyle = new AWImgMarkerStyle();
			markerStyle.id = "id";
			//markerStyle.source = "marker.swf";
			markerStyle.source = "images/id.png";
		    markerStyle.width = 16;
            markerStyle.height = 16;
			markerStyle.anchorX = 8;
			markerStyle.anchorY = 8;
			markerStyle.mouseClick = "showLocation";
			//alert("add marker style");
			theMap.addMarkerStyle(markerStyle);
			//alert("done adding");

			for( var i = 0; i < mapMarkers.length; i++ )
			{
				var data = 
				{
					index : mapMarkers[i].index,
					title : mapMarkers[i].title,
					description : mapMarkers[i].description
				};
				var marker = new AWMarker();
				marker.latlon = new AWLatLon(mapMarkers[i].lat, mapMarkers[i].lon);
				marker.markerStyleId = markerStyle.id;
				marker.data = data;
				theMap.addMarker(marker);
			}
            //alert(mapMarkers.length);
            var ext = mapMarkers.extent(0.0);		
			//alert(ext[0]);
			//alert(ext[1]);
			//alert(ext[2]);
			var latlonExtent = new Object();
			latlonExtent.minLat = ext[1].x;
			latlonExtent.minLon = ext[1].y;
			latlonExtent.maxLat = ext[2].x;
			latlonExtent.maxLon = ext[2].y;
			//Seems to return one zoom level too low.
			theMap.centerAndZoom(new AWLatLon(ext[0].x, ext[0].y), theMap.getZoomForLatLonExtent(latlonExtent)+1);	
		}


function Set(name,value, color, hiColor) {
    this.name = name;
    this.value = value;
    this.color = color;
    this.hiColor = hiColor;
}

Set.prototype.toString = function() {
    return "(" + this.name + " = " + this.value + ")";
}

var controls = new Array(); //will be used to hold pairs that match a listing ID to a control ID
controls.highlightedIndex = 0;

/*
    This function (un)highlights a listing on the page after a bubble pops. 
*/

    function highlightListing(listingid, keepCurrent) {
        /* in interlego, the listing in question */
        var setHighlight = false;
        //alert("highlightListing");
        for (var i=0;i<controls.length;i++) {
            var o = document.getElementById(controls[i].value);
            if (o) {
                if (controls[i].name == listingid) {
                    if (!keepCurrent) {
                    controls.highlightedIndex = i;
                    }
                    setHighlight = true;
                    o.style.backgroundColor = controls[i].hiColor;
                } else if (keepCurrent && (i==controls.highlightedIndex)) {
                    setHighlight = true;
                    //no change needed but pretend a change took place
                } else {
                    o.style.backgroundColor = controls[i].color;
                }
            }
        }
        if (!setHighlight) { 
          controls.highlightedIndex = -1; 
        }
    }

/*
    functions from the VE implementation of mapping moved here from mapResults.aspx. Eventually they should be combined with those from searchResults.aspx and standardized.
*/


/* this is specific because the "16" values are widths and/or heights of the +/- buttons */
    function setZoomFromPosition(pos) {
        
        var obj = document.getElementById("mapControlZoomBar");
        var a = Point.prototype.getAbsolutePosition(obj);

        var min = a.y + 16;
        var max = a.y + obj.offsetHeight - 16; 

        var y = pos - a.y - 16;
        var zoomLevel = VE_MapSearchControl.minZoom + Math.round((VE_MapSearchControl.maxZoom - VE_MapSearchControl.minZoom) * y / (max - min));
        if (zoomLevel < VE_MapSearchControl.minZoom) zoomLevel = VE_MapSearchControl.minZoom;
        if (zoomLevel > VE_MapSearchControl.maxZoom) zoomLevel = VE_MapSearchControl.maxZoom;
        map.SetZoom(zoomLevel);
        setZoomSlider();
        return true;
    }

    function highlightControl(c, onState) {
        
        if (endswith(c.className, "On")) return false;
        if (onState) {
            c.className = c.className + "Over";
        } else {
            c.className = c.className.substring(0, c.className.length - 4);
        }
        return true;
    } 
  
function refreshWindow(bid){

    if (window.opener && !window.opener.closed) 
    { 
        window.opener.location.href="/neighborhood/BusinessDetails.aspx?bid=" + bid; 
        window.close(); 
    }
    else
        window.location = "/neighborhood/BusinessDetails.aspx?bid=" + bid;
}
function replaceAll( str, from, to ) {
    var idx = str.indexOf( from );


    while ( idx > -1 ) {
        str = str.replace( from, to ); 
        idx = str.indexOf( from );
    }

    return str;
}
