﻿/*
* The programming and software materials herein are copyright LPS.
* The programming and software materials are owned, held, or licensed by LPS. Personal, educational,
* non-commercial, commercial or any other use of these materials, without the written permission of the
* LPS, is strictly prohibited.
*/
/*
MapSearch.js is the overall js for the control. MapSearch.js handles all the page element bindings, searches, criteria, etc. 
BingMap.js is the map control itself - it's used independently to load the maps within the list detail, and on the driving directions, for exampl. BingMap.js handles map redraws, all the VEMap functions, pins, shapes, etc.
*/
/**
* @constructor
*/
function MapSearch(options) {
    // Private variables
    var _self = this;
    var _map;
    var _submitEnabled = false;
    var _submitTimer = null;
    var _mapTimer = null;
    var _resizeTimer = null;
    var _containers = null;
    var _qs = new Querystring();
    var _activeView = "map";
    var _activeSortID;
    var _displaySort = true;
    var _lastLocationSubmitted = "";
    var _lastLocationSearchTab = "";
    var _movingToolbar = false;
    var _setMapView = false;
    var _disposed = false;
    var _poiEnabled = false;
    var _clsid = -1;
    var _firstSubmit = true;
    var _submitting = false;
    var _advisoryPositionBottom = false;
    var _justLoaded = false;

    var _pageInfo = {
        resultCount: 0,
        totalCount: 0,
        maxCount: 0,
        page: 0,
        resultsPerPage: 10,
        cache: true,
        pageCache: {},
        clear: function () {
            this.page = 0;
            this.pageCache = {};
        }
    };

    var _defaultOptions = {
        statuses: {
            active: "1,10",
            pending: "5",
            sold: "2"
        },
        mapExpandDiff: 225,
        resultsPerPage: 10,
        poiEnabled: true,
        showParcelLines: true,
        showMinimap: true,
        submitEnabledOnLoad: true, // This gets set to false in the BAC criteria
        getOfficePins: true // This is different from displaying on load, it says whether to get them at all
    };

    /* Private functions */

    function loadNeighborhoods(mapState) {

        var divN = document.getElementById("divNeighborhood");
        var selN = document.getElementById("Neighborhood");

        if (!divN || !selN) {
            return;
        }

        // Clear the drop-down
        selN.length = 0;

        var selVal = $("#selNeighborhood").val();
        $("#selNeighborhood").val("");

        if (mapState.Zoom >= 10 && mapState.Style != VEMapStyle.Birdseye) {
            $(selN).hide();
            $("#NeighborhoodLoading").html("<img src='" + imageBase + "spinner.gif' height='12'/>").show();
            $("#divNeighborhoodInfo").html("Retrieving Neighborhoods in this Area").show();

            $.ajax({
                mode: "abort",
                port: "neighborhoods",
                type: "GET",
                url: Utils.AppPath + "/Include/AJAX/MapSearch/GetLocations.aspx",
                data: {
                    type: "neighborhood",
                    q: "*",
                    nelat: mapState.NELat,
                    nelon: mapState.NELon,
                    swlat: mapState.SWLat,
                    swlon: mapState.SWLon,
                    limit: 5000
                },
                dataType: "json",
                success: function (neighborhoods) {
                    try {
                        if (neighborhoods.length === 0) {
                            $("#divNeighborhoodInfo").html("No Neighborhoods Found");
                            $("#icNeighborhoodInfo").attr("title", "No Neighborhoods Found");
                            $(selN).blur();
                            return;
                        }

                        // Filter the neighborhoods based on any selected city value
                        var city = $(":input[name='Criteria/City']", $(_containers.criteria)).val().toLowerCase();
                        if (city.indexOf(",") > 0) {
                            city = city.substring(0, city.indexOf(","));
                        }
                        var filtered;
                        if (city.length === 0) {
                            filtered = neighborhoods;
                        } else {
                            filtered = [];
                            for (var i = 0, len = neighborhoods.length; i < len; i++) {
                                var n = neighborhoods[i];
                                if (n.City && n.City.length > 0) {
                                    if (n.City.toLowerCase() == city) {
                                        filtered.push(n);
                                    }
                                } else {
                                    filtered.push(n);
                                }
                            }
                        }
                        if (filtered.length > 0) {
                            $("#icNeighborhoodInfo").attr("title", "The list below shows all neighborhoods in the nearby map area. Select a neighborhood to see it outlined on the map or zoom out the map to see more neighborhoods.");
                            $(selN).append("<option value=''>- Select Neighborhood -</option>");
                            for (var j = 0, lenj = filtered.length; j < lenj; j++) {
                                var neighborhood = filtered[j];
                                var opt = document.createElement('option');
                                opt.text = neighborhood.Name;
                                opt.value = neighborhood.BoundaryID;
                                if (neighborhood.BoundaryID == selVal) {
                                    opt.selected = "selected";
                                }
                                addOption(selN, opt);
                            }
                            $("#divNeighborhoodInfo").hide();
                            $(selN).show();
                        }
                        else {
                            $("#divNeighborhoodInfo").html("No Neighborhoods Found");
                            $("#icNeighborhoodInfo").attr("title", "No Neighborhoods Found");
                        }
                        $(selN).blur();
                    } catch (err) {
                        Utils.Logger.warn("Error loading neighborhoods: " + err.message);
                    } finally {
                        $("#NeighborhoodLoading").hide();
                    }
                }
            });
        } else {
            $(selN).hide();
            $("#divNeighborhoodInfo").html("Zoom in to view Neighborhoods").show();
            $("#icNeighborhoodInfo").attr("title", "Zoom in to view Neighborhoods").show();
        }
    }

    function getSearchTabSelector(childSelector) {
        var selector;
        var searchTab = $("#SearchTab").val();
        if (searchTab == "basicSearch" || searchTab == "advancedSearch") {
            selector = "#basicSearch " + childSelector + ",#advancedSearch " + childSelector;
        } else {
            selector = "#" + searchTab + " " + childSelector;
        }

        return selector;
    }

    function getHiddenInput(inputName) {
        var searchTab = $("#SearchTab").val();
        if (searchTab == "advancedSearch") {
            searchTab = "basicSearch";
        }
        searchTab = "#" + searchTab;
        var $input = $(searchTab + " input[name='" + inputName + "']");
        if ($input.length == 0) {
            $(searchTab).append("<input type='hidden' name='" + inputName + "' value='' />");
            $input = $(searchTab + " input[name='" + inputName + "']");
        }
        return $input;
    }

    this.addLocation = function (data, dontSubmit) {
        var loc = data.Name;
        if (data.City && data.City !== "") {
            data.Name += ", " + data.City;
        }
        if (data.State && data.State !== "") {
            loc = data.Name + ", " + data.State;
        }
        if (loc.indexOf("(") == -1 && data.Type.toLowerCase() !== "points") {
            loc += " (" + data.Type + ")";
        }
        var locType = data.Type;
        // Get the current list of locations & types
        var $Location = getHiddenInput("Criteria/Location");
        var $LocationType = getHiddenInput("Criteria/LocationType");
        var $LocationValue = getHiddenInput("Criteria/LocationValue");
        var allLocations = $Location.val().split("|");
        var allLocationTypes = $LocationType.val().split("|");
        var allLocationValues = $LocationValue.val().split("|");
        // Find out if the new location is already in the current list
        var alreadyInAll = false;
        for (var i = 0; i < allLocations.length; i++) {
            var thisLoc = allLocations[i];
            var thisLocType = allLocationTypes[i];
            if (thisLoc === loc && thisLocType === locType) {
                alreadyInAll = true;
                break;
            }
        }
        // If it's not, add it
        if (!alreadyInAll) {
            // Add it to the list of locations
            allLocations.push(loc);
            allLocationTypes.push(locType);
            var locVal = data.Name;
            if (data.Type.toLowerCase() == "city") {
                if (data.State && data.State.length > 0) {
                    locVal += "," + data.State;
                }
            } else if (data.Type.toLowerCase() == "neighborhood" || data.Type.toLowerCase() == "boundary") {
                locVal = data.BID;
            } else if (data.Type.toLowerCase() === "points") {
                locVal = data.Value;
            }
            allLocationValues.push(locVal);
            $Location.val(allLocations.join("|"));
            $LocationType.val(allLocationTypes.join("|"));
            $LocationValue.val(allLocationValues.join("|"));

            // Update the map
            _setMapView = true;
            if (data.Type.toLowerCase() == "city") {
                _map.gotoPolyDeferred(loc, "city", locVal);
            } else if (data.Type.toLowerCase() == "zip code") {
                _map.gotoPolyDeferred(loc, "zip", locVal);
            } else if (data.Type.toLowerCase() == "neighborhood") {
                _map.gotoPoly(loc, "neighborhood", locVal, true, dontSubmit);
            } else if (data.Type.toLowerCase() === "points") {
                _map.gotoPoly(loc, "points", locVal, true, dontSubmit);
            } else if (data.BID && data.BID > 0) {
                _map.gotoPoly(loc, "boundary", locVal, true, dontSubmit);
            } else {
                var name = data.Name;
                if (data.State && data.State.length > 0) {
                    name += "," + data.State;
                }
                gotoLocation(name, data.Type);
            }
        }
    };

    function removeLocation(loc) {
        // Get the current list of locations & types
        var $Location = getHiddenInput("Criteria/Location");
        var $LocationType = getHiddenInput("Criteria/LocationType");
        var $LocationValue = getHiddenInput("Criteria/LocationValue");
        var allLocations = $Location.val().split("|");
        var allLocationTypes = $LocationType.val().split("|");
        var allLocationValues = $LocationValue.val().split("|");

        // Set up new lists
        var newAllLocations = [];
        var newAllLocationTypes = [];
        var newAllLocationValues = [];
        // Loop through the current lists, populating the new lists with the contents
        // excluding the one we want to remove
        for (var i = 0; i < allLocations.length; i++) {
            var thisLoc = allLocations[i];
            var thisLocType = allLocationTypes[i];
            var thisLocVal = allLocationValues[i];
            if (thisLoc !== loc) {
                newAllLocations.push(thisLoc);
                newAllLocationTypes.push(thisLocType);
                newAllLocationValues.push(thisLocVal);
            } else {
                // Remove boundary
                var lowerLocType = thisLocType.toLowerCase();
                if (lowerLocType === "points") {
                    _map.clearPolygon(loc, false);
                }
                if (lowerLocType === "city" ||
                    lowerLocType === "zip code" ||
                    lowerLocType === "neighborhood" ||
                    lowerLocType === "boundary") {
                    _map.clearPolygon(loc, false);
                }
            }
        }
        // Update the inputs with the new lists
        $Location.val(newAllLocations.join("|"));
        $LocationType.val(newAllLocationTypes.join("|"));
        $LocationValue.val(newAllLocationValues.join("|"));
    }

    function removeCriteria(criterion) {
        if (criterion.name === "Criteria/Location") {
            removeLocation(criterion.val);
            submit(true);
        } else {
            var names = criterion.name.split("|");
            for (var i = 0; i < names.length; i++) {
                $(getSearchTabSelector("*[name='" + names[i] + "']"), $(_containers.criteria)).each(function () {
                    if (this.type === "checkbox") {
                        if (this.value === criterion.val) {
                            $(this).attr('checked', false);
                        }
                    } else if (this.nodeName.toLowerCase() === "select" && (this.name === "HardCodedCriterion" || this.name === "ExtendedCriterion")) {
                        if ($(this).val() === criterion.val) {
                            $(this).val("");
                        }
                    } else {
                        $(this).val("");
                    }
                });
            }
            submit(false);
        }
    }

    function getFormData(mapState, mapMoved) {
        var data = {};

        if ($(".customer-listings-view").length) {
            data["useCookieFormDom"] = "1";
            _setMapView = true;
        }

        var hasLocationCriteria = false;
        $(getSearchTabSelector(":input"), $(_containers.criteria)).each(function () {
            // If no ele name then continue, some form elements may not be needed, we take only those with a name
            if (!this.name) {
                return true;
            }

            // If the value is empty, don't include (to cut down on space)
            if (this.value.length === 0) {
                return true;
            }

            // Skip the element if its type radio or checkbox and is not "checked"
            if ((this.type == "radio" || this.type == "checkbox") && !this.checked) {
                return true;
            }

            // Skip disabled elements
            if (this.disabled) {
                return true;
            }

            // Skip polygon type from legacy forms
            if (this.name == "PolygonType") {
                return true;
            }

            var elValue = this.value;

            // Some special handling for the City
            if (this.name == 'Criteria/City' && elValue != '') {
                hasLocationCriteria = true;
                if (elValue.indexOf(",") > -1) {
                    var citystate = elValue.split(",");
                    elValue = citystate[0];
                    if (data["Criteria/StateOrProvinceCode"]) {
                        data["Criteria/StateOrProvinceCode"].push(citystate[1]);
                    } else {
                        data["Criteria/StateOrProvinceCode"] = [citystate[1]];
                    }
                }
            }

            // Special handling for the statuses
            if (this.name == 'Criteria/Status') {
                if (elValue == '1') { elValue = options.statuses.active; }
                else if (elValue == '2') { elValue = options.statuses.sold; }
                else if (elValue == '5') { elValue = options.statuses.pending; }
            }

            if ((this.name == 'Criteria/ZipCode' || this.name == 'Criteria/ZipCodeAdvanced') && elValue != '') {
                hasLocationCriteria = true;
            }

            // Store them as arrays to account for the checkboxes
            // The ajax post will convert them correctly
            var elName = this.name;

            if ($(this).attr("multiple")) {
                $.each(this.options, function () {
                    if ($(this).attr("selected")) {
                        if (data[elName])
                            data[elName].push($(this).val());
                        else {
                            data[elName] = [$(this).val()];
                        }
                    }
                });
            }
            else {
                if (data[this.name]) {
                    data[this.name].push(elValue);
                } else {
                    data[this.name] = [elValue];
                }
            }

        });

        // If we didn't get a status, set one
        if (!data["Criteria/Status"]) {
            if ($(getSearchTabSelector("input[name='Criteria/Status']")).length > 0) {
                data["Criteria/Status"] = -1;
            } else {
                data["Criteria/Status"] = 1;
            }
        }

        // Add the map state
        data["Criteria/SearchMapNELat"] = mapState.NELat;
        data["Criteria/SearchMapNELong"] = mapState.NELon;
        data["Criteria/SearchMapSWLat"] = mapState.SWLat;
        data["Criteria/SearchMapSWLong"] = mapState.SWLon;
        data["Criteria/Zoom"] = mapState.Zoom;
        if (mapState.PolyPoints) {
            var points = "";
            for (var key in mapState.PolyPoints) {
                points += mapState.PolyPoints[key] + "%";
            }
            data["Criteria/PolyPoints"] = points;
        }

        // ignore the map area if the auto adjust checkbox is on and the map is not manually moved and setmapview is false ie location is not changed
        //if loading a saved search (ListingSearch or ListingSearchID in _qs) don't auto adjust
        if ((data["AutoAdjustMap"] == "on" && !mapMoved && !_setMapView) || (_justLoaded && data["Criteria/PolyPoints"] == undefined && data["Criteria/Location"] != undefined && !_qs.contains('ListingSearch') && !_qs.contains('ListingSearchID'))) {
            data["IgnoreMap"] = true;
            _setMapView = true;
        }
        else
            data["IgnoreMap"] = _setMapView;


        _justLoaded = false;
        // If there's no location being searched, clear the map polygons
        // BO12070
        /*
        if (!hasLocationCriteria) {
        _map.clearPolygons();
        delete data["PolygonType"];
        delete data["Criteria/PolyPoints"];
        }
        */

        // Some other search state
        if (!_activeSortID || _activeSortID < 1) {
            _activeSortID = $("#mapsearch-sort-list").val();
        }
        data["ListingSortID"] = _activeSortID;
        data["view"] = _activeView;
        data["Criteria/SearchType"] = "map";
        data["SearchTab"] = $("#SearchTab").val();
        data["GetOfficePins"] = options.getOfficePins;
        data["ExpandMap"] = $("#mapsearch-toolbar-expand-input").val();
        data["CLSID"] = _clsid;
        data["ResultsPerPage"] = _pageInfo.resultsPerPage;
        if (_qs.contains('LayoutID')) {
            data["LayoutID"] = _qs.get("LayoutID");
            data["LayoutVersion"] = _qs.get("LayoutVersion");
        }

        if (_activeView != "map") {
            $(".ms_tab_on", $(_containers.viewTabs)).removeClass("ms_tab_on").addClass("ms_tab_off");
            $("#mapsearch-viewtab-" + _activeView).removeClass("ms_tab_off").addClass("ms_tab_on");
            //getListingHtml(_pageInfo.page * _pageInfo.resultsPerPage);
            //if (oldView == "map" || _activeView == "map") {
            // $("#mapsearch-mainmap-toolbar,#mapsearch-results-header").hide();
            $("#mapsearch-mainmap-toolbar").css("visibility", "hidden");
            $("#mapsearch-results-header").css("visibility", "hidden");
            $("#mapsearch-results-all").offset({
                top: $("#mapsearch-mainmap-toolbar").offset().top,
                left: $("#mapsearch-results-all").offset().left
            });
            //}
        }

        return data;
    }

    function setCount(results) {
        var totalCount, displayedCount;
        if (typeof results != "undefined" && results != null) {
            totalCount = results.count;
            displayedCount = 0;
            if (results.lst && results.lst.length > 0) {
                displayedCount = results.lst.length;
            }
            _pageInfo.resultCount = displayedCount;
            _pageInfo.totalCount = totalCount;
            _pageInfo.maxCount = results.maxcount;
        } else {
            totalCount = -1;
            displayedCount = -1;
        }
        var msg = "";
        if (isNaN(totalCount) || totalCount == -1) {
            msg = "An error has occurred retrieving your results.  Please try again.";
        } else if (isNaN(displayedCount) || displayedCount <= 0) {
            msg = Utils.Format.addCommas(totalCount) + " properties found";
        } else {
            msg = Utils.Format.addCommas(displayedCount) + ' of ' + Utils.Format.addCommas(totalCount) + " properties displayed";
            if (displayedCount < totalCount) {
                msg += ". Use the criteria to refine your search.";
            }
            //$('.mapsearch-results-paging-total').html(Utils.Format.addCommas(totalCount) + ' Properties Found');
        }
        setCountText(msg);
        try {
            $(_containers.count).animate({ backgroundColor: "transparent" }, 1000);
        }
        catch (ex) {
            $(_containers.count).css({ backgroundColor: "transparent" });
        }
    }

    function setCountText(msg) {
        $(".mapsearch-count-total", $(_containers.count)).html(msg);
    }


    function unbindListingGrid() {
        var $grid = $("#mapsearch-results-body");
        $("div[mapconfig]", $grid).each(function () {
            var map = $(this).data("map");
            if (map) {
                map.dispose();
            }
        });
        $("a.smallMapRoad", $grid).unbind("click");
        $("a.smallMapAerial", $grid).unbind("click");
        $("a.smallMapHybrid", $grid).unbind("click");
        $("div[lid]", $grid).unbind("mouseenter").unbind("mouseleave");
    }

    function getPageLinks(cursor, pagesPerRange, totalPages) {
        var p = [];
        var start = pagesPerRange * (Math.floor(cursor / pagesPerRange));

        if ((start + pagesPerRange) > totalPages) {
            start -= (start + pagesPerRange) - totalPages;
        }
        if (start < 0) { start = 0; }

        for (var i = start; i < (start + pagesPerRange) && (i < totalPages); i++) {
            var page = i + 1;
            if (i == cursor) {
                p.push("<span class='current-page'>{0}</span>".format(page));
                continue;
            }
            p.push("<a href='JavaScript:Search.gotoPage(" + page + ");  " + addScrollToTop() + "'  name='map-results-page-" + page + "' title='Goto page " + page + "'>" + page + "</a>");
        }

        return p.join(' | ');
    }

    function addScrollToTop() {
        return (_activeView != "map") ? "window.scrollTo(0, 0);" : "";
    }

    function setPager(results) {
        if (typeof results == "undefined" || results == null) {
            return;
        }
        results.resultsPerPage = _pageInfo.resultsPerPage;
        var count = results.count;
        if (results.lst && results.lst.length > 0) {
            count = results.lst.length;
        }
        var totalPages = Math.ceil(count / results.resultsPerPage);
        var first = (results.page * results.resultsPerPage);
        //$(".mapsearch-results-paging-properties").html("Displaying " + (first + 1) + "-" + (first + results.pageCount) + " of " + count + " (" + Utils.Format.addCommas(_pageInfo.totalCount) + " properties found)");
        $(".mapsearch-results-paging-properties").html(count + " of " + Utils.Format.addCommas(_pageInfo.totalCount) + " properties displayed");
        var pageLinks = [];
        pageLinks.push("Page " + getPageLinks(_pageInfo.page, 5, totalPages) + " ");
        pageLinks.push(" of " + totalPages + "&nbsp;&nbsp;&nbsp;&nbsp;");

        if (_pageInfo.page === 0) {
            pageLinks.push("<span>Prev</span> | ");
        } else {
            pageLinks.push("<a href='JavaScript:Search.prevPage(); " + addScrollToTop() + "' name='ms-results-prev' title='Previous Page'>Prev</a> | ");
        }

        if (_pageInfo.page == totalPages - 1) {
            pageLinks.push("<span>Next</span>");
        } else {
            pageLinks.push("<a href='JavaScript:Search.nextPage(); " + addScrollToTop() + "'  name='ms-results-next' title='Next Page'>Next</a>");
        }

        _pageInfo.totalPages = totalPages;
        _pageInfo.count = count;
        _pageInfo.pageLinksHtml = pageLinks.join('');
        $("#mapsearch-mainmap").trigger("pageinfochange", [{ pageInfo: _pageInfo}]);
        $(".mapsearch-results-paging-pages").html(_pageInfo.pageLinksHtml);
    }

    function positionAdvisory(toggleTop) {
        if (_movingToolbar) {
            return;
        }
        _movingToolbar = true;
        var $advisory = $("#mapsearch-advisory-box");

        var containerHeight;
        if (_activeView == "map") {
            containerHeight = $("#mapsearch-mainmap").height();
        } else {
            containerHeight = $("#mapsearch-mainmap-results").height();
            toggleTop = false;
        }

        var positionLeft = ($("#mapsearch-mainmap-results").width() - $advisory.width()) / 2 - 5;
        if ($.browser.msie && parseInt($.browser.version.substring(0, 1)) < 7) {
            positionLeft = positionLeft / 2 - 5;
        }
        var middleTop = (containerHeight - $advisory.height()) / 2 + 50;
        var bottomTop = $("#mapsearch-maptoolbar").position().top - $advisory.height() * 2 + 13;
        var advisoryTop = $advisory.position().top;
        var positionTop;
        if (_activeView == "map") {
            positionTop = bottomTop;

        } else {
            positionTop = 0;
        }

        $advisory.animate(
            { top: positionTop, left: positionLeft },
            "slow",
            "swing",
            function () { _movingToolbar = false; }
        );
    }

    function showAdvisory() {
        var msg = "";
        // If there is no reason to display the advisory, hide it
        if (_pageInfo.maxCount > 0 && _pageInfo.resultCount > 0 && _pageInfo.resultCount <= _pageInfo.maxCount) {
            hideAdvisory();
        } else if (isNaN(_pageInfo.resultCount) || _pageInfo.resultCount == -1) {
            setAdvisoryText("An error has occurred retrieving your results.", " Please try again.");
        } else if (_pageInfo.totalCount == 1) {
            setAdvisoryText("1 Property Found");
        } else if (_pageInfo.totalCount == 0) {
            setAdvisoryText("<div id='box-count'>0</div> Properties Found", 'To view properties please change your search criteria on the left');
        } else {
            setAdvisoryText("<div id='box-count'>" + Utils.Format.addCommas(_pageInfo.totalCount) + "</div> Properties Found Display limit is " + _pageInfo.maxCount, 'To view properties please change your search criteria on the left')
        }
    }

    function showCalculating() {
        // Hide the commgling related messages
        $("#mapsearch-commingling-info").hide();
        $(".mapsearch-count-displayed", $(_containers.count)).hide();

        setCountText("Searching...");
        setAdvisoryText("<span id='box-searching'>Searching...</span>", "In a moment we'll show you the matching properties.");
    }

    function hideAdvisory() {
        if ($.browser.msie && parseInt($.browser.version.substring(0, 1), 10) < 7) {
            $("#mapsearch-advisory-box").hide();
        } else {
            $("#mapsearch-advisory-box").fadeTo('slow', 0.0).hide();
        }
    }

    function setAdvisoryText(msg1, msg2) {
        positionAdvisory();
        // msg1 is the bigger text at the top; msg2 is the smaller text underneath msg1
        $("#mapsearch-advisory-box-total").html(msg1);
        $("#mapsearch-advisory-box-count").html(msg2);
        var $box = $("#mapsearch-advisory-box");
        if ($.browser.msie && parseInt($.browser.version.substring(0, 1), 10) < 7) {
            $("#mapsearch-advisory-box").show();
        } else {
            $("#mapsearch-advisory-box").show().fadeTo('slow', 0.9);
        }
    }

    var _showListingGridBubbleDelay = 250;

    // Binds mouseenter events to each row in the results so that when you hover over a row the bubble is displayed.
    function bindListingGridToBubbles() {
        $("#mapsearch-results-body div[lid]").mouseenter(function () {
            showListingBubbleDelayed($(this).attr("lid"));
        });
    }

    // Shows the listing bubble with a delay. This function assumes it is bound to the object's (id)
    // mouseenter event handler. It binds a call to hideListingBubble() to the mouseleave event.
    // This method was added to reduce the number of AJAX calls to the server that load bubble data.
    // When the delay is zero then the instant a user mouses over the object, an AJAX call is made.
    // ie: Even if the user was just scrolling the mouse across the screen and unintentionally moused over,
    // an AJAX call would be made.
    function showListingBubbleDelayed(id) {
        var timeoutId = setTimeout(function () { showListingBubble(id); }, _showListingGridBubbleDelay);

        //console.log("Added timeoutId: " + timeoutId);
        //console.log("showListingBubbleDelayed, id: " + id);

        // We bind the mouseleave event here (instead of in bindListingGridToBubbles()) because hideListingBubble needs to know the timeoutId
        $("#mapsearch-results-body div[lid='" + id + "']").mouseleave(function () {
            //console.log("adding mouseleave binding for timeoutId: " + timeoutId);
            hideListingBubble(id, timeoutId);
        });
    }

    function showListingBubble(id) {
        _map.showListingBubble("1_" + id);
    }

    // Hides the listing bubble, removes the timeoutId from array and unbinds the mouseleave event.
    function hideListingBubble(id, timeoutId) {
        //console.log("hiding timeoutid: " + timeoutId);
        clearTimeout(timeoutId);
        _map.hideListingBubble();
        // Unnbind the mouseleave because the next time the showListingBubbleDelayed is called it'll output
        // a different timeoutId.
        $("#mapsearch-results-body div[lid='" + id + "']").unbind("mouseleave");
    }

    function showListingGrid(results) {

        // Unbind events from the previous grid before reloading to avoid
        // memory leaks
        unbindListingGrid();

        $("#mapsearch-results-body").html(results.listingsHtml).pngFix().unblock();

        // If you add any bindings here, also add them to unbindListingGrid
        if (_activeView == "map") {
            bindListingGridToBubbles();
            $("#mapsearch-results-header").show();
        } else {
            $("#mapsearch-results-header").hide();

            $("#mapsearch-results-body div.lsrcExtendedInfoBoxHead h5").click(function () {

                var listingid = $(this).attr("lid");
                var $box = $("#mapsearch-results-body .lsrEIB_" + listingid);
                if ($box.is(":visible")) {
                    $box.hide("slow");
                    $(this).html("<span class='arrow arrow-left'></span>View additional info");
                }
                else {
                    $(this).html("<span class='arrow arrow-down'></span>Hide additional info");
                    // When showing the extended info, create the map if we need to
                    $box.show("slow", function () {
                        var $mapBox = $("div[mapconfig]", this);
                        if ($mapBox.length > 0 && !$mapBox.data("map")) {
                            var $parent = $mapBox.parent();
                            var config = eval("(" + $mapBox.attr("mapconfig") + ")");
                            var centerPin = (typeof config.centerPin == "undefined" || config.centerPin);
                            var map = new BingMap({
                                startingMidLat: config.lat,
                                startingMidLong: config.lon,
                                startingZoom: config.zoom || 17,
                                mapView: config.view || VEMapStyle.Hybrid,
                                centerPin: centerPin,
                                showBubble: false,
                                container: $mapBox.attr("id")
                            });
                            map.load();
                            //map.hideControls();
                            // Save the map in the element's data for later disposal
                            $mapBox.data("map", map);
                            // Add actions to the view buttons
                            $("a.smallMapRoad", $parent).click(function () { map.setView("r"); });
                            $("a.smallMapAerial", $parent).click(function () { map.setView("a"); });
                            $("a.smallMapHybrid", $parent).click(function () { map.setView("h"); });
                        }
                    });
                }
            });
        }
        setPager(results);
    }

    function updateListingCountError(xmlRequest, textStatus, errorThrown) {
        if (textStatus !== "abort") {
            _submitting = false;
            if (textStatus && textStatus.length > 0) {
                Utils.Logger.warn("Error getting results: " + textStatus);
            }
            if (errorThrown && errorThrown.message && errorThrown.message.length > 0) {
                Utils.Logger.warn("Error getting results: " + errorThrown.message);
            }
            setCount(null);
            hideAdvisory();
            $("#mapsearch-results-body").unblock();
        }
    }


    function updateListingCount(results) {
        // Request was aborted
        if (!results) {
            return;
        }

        _submitting = false;
        setCount(results);
        _pageInfo.clear();

        var setMapView = _setMapView;
        _setMapView = false;

        // If there are pins, add them to the map
        if (results.lst && results.lst.length > 0) {
            _map.startAddingPins("listing");
            // Add the pins to a new layer
            for (var i = 0, len = results.lst.length; i < len; i++) {
                var lst = results.lst[i];
                if (lst.lat && !isNaN(lst.lat)) {
                    _map.addPin("listing", "1_" + lst.lid, lst.lat, lst.lon, lst.ptid, "Listing", lst.bub);
                }
            }
            _map.endAddingPins("listing", setMapView);
            // Load the grid.
            showListingGrid(results);
            // Add the commingling boards
            var commingling = document.getElementById("mapsearch-commingling");
            _clsid = results.clsid;
            if (commingling) {
                var boardLen = results.board.length;
                var selBoard = document.getElementById("mapsearch-commingling-list");
                if (boardLen > 1 && selBoard) {
                    // Remove any existing options
                    selBoard.length = 0;
                    $.each(results.board, function () {
                        var newOpt = new Option(this.clsname + " (" + this.count + ") ", this.clsid);
                        if (this.clsid == _clsid) {
                            newOpt.selected = true;
                        }
                        selBoard.options[selBoard.options.length] = newOpt;
                    });
                    commingling.style.display = 'inline';
                    $("#mapsearch-commingling-info").show();
                } else {
                    commingling.style.display = 'none';
                    $("#mapsearch-commingling-info").hide();
                }
            } else {
                commingling.style.display = 'none';
                $("#mapsearch-commingling-info").hide();
            }
            hideAdvisory();
            // Show the disclaimers
            if (results.disclaimer) {
                $("#mapsearch-disclaimers").html(results.disclaimer);
                $("#mapsearch-disclaimers img").attr("alt", "MLS logo");
            } else {
                $("#mapsearch-disclaimers").html("");
            }
            $("#mapsearch-results").show();
            $(".mapsearch-results-paging").show();
        } else { // No listing bubbles, toggle the message
            _map.deleteAllPins("listing");

            if ((results.maxcount > 0 && results.count > results.maxcount) || results.count == 0) {
                showAdvisory();
            } else {
                hideAdvisory();
            }
            $("#mapsearch-disclaimers").html("");
            $("#mapsearch-results").hide();
            $(".mapsearch-results-paging").hide();
            $("#mapsearch-commingling").hide();
        }
        // Add office pins to the map
        if (results.office && results.office.length > 0) {
            // Add the pins to a new layer
            _map.startAddingPins("office");
            for (var j = 0, officelen = results.office.length; j < officelen; j++) {
                var office = results.office[j];
                if (office.latitude && office.latitude != "" && !isNaN(office.latitude)) {
                    _map.addPin("office", "3_" + office.officeid, office.latitude, office.longitude, 3, "Office");
                }
            }
            _map.endAddingPins("office");
        } else {
            _map.deleteAllPins("office");
        }

        // BOET 13446 begin
        var hideDaysOnMarket = false;
        $(".IDXRules_DontDisplayFields").each(function () {
            var dontDisplayVal = $(this).text();
            if (dontDisplayVal.indexOf('DaysOnMarket') >= 0) {
                hideDaysOnMarket = true;
            }
        });

        var optDateListed = $("#mapsearch-sort-list option[value='5']");
        if ($.browser.msie || $.browser.opera || $.browser.webkit) {
            if (hideDaysOnMarket == true)
                $(optDateListed).remove();
            else
                if (optDateListed == null)
                    $("#mapsearch-sort-list").append("<option value='5'>Date Listed</option>");
        }
        else {
            if (hideDaysOnMarket == true)
                $(optDateListed).hide();
            else
                $(optDateListed).show();
        }
        // BOET 13446 end
    }

    // Main submit method
    function submit(setMapView, mapState, mapMoved) {
        if (_submitTimer !== null) {
            clearTimeout(_submitTimer);
            _submitTimer = null;
        }
        if (_submitEnabled && _map.isLoaded()) {
            // Set this so hitting the submit button won't interrupt an onchange in progress
            _submitting = true;
            // Make sure that even if all else fails we re-enable it at some point
            // If it happens to re-enable in the middle of submitting ... well so be it
            setTimeout(function () { _submitting = false; }, 15000);

            // See if we need to fit the map to match the results
            if (!_setMapView)
                _setMapView = setMapView || false;

            if (_firstSubmit) {
                var locations = $(getHiddenInput("Criteria/Location")).val();
                if (locations && locations.length > 0) {
                    _setMapView = true;
                }
                _firstSubmit = false;
            }
            mapState = mapState || _map.getMapState();
            // Remove any special pins
            var searchTab = $("#SearchTab").val();
            if (searchTab != "addressSearch" && searchTab != "mapsearch-criteria-addresssearch") {
                _map.deleteAllPins("address");
            }
            // Change the background color of the count div to yellow
            try {
                $(_containers.count).animate({ backgroundColor: "#ffd73b" }, 1000);
            }
            catch (ex) {
                $(_containers.count).css({ backgroundColor: "#ffd73b" });
            }
            // Set the results to gray to indicate they're in process
            $("#mapsearch-results-body").block({ message: null });

            // Change the messages to tell the user we're calculating...
            showCalculating();

            // Kick off the search ...
            $.ajax({
                // try to leverage ajaxQueue plugin to abort previous requests
                mode: "abort",
                port: "mapsearch",
                type: "POST",
                url: Utils.AppPath + "/Include/AJAX/MapSearch/GetListingPins.aspx?searchoverride=" + options.cookieGuid,
                data: getFormData(mapState, mapMoved),
                traditional: true,
                cache: false,
                dataType: "json",
                success: updateListingCount,
                error: updateListingCountError
            });
            _map.gotoPolyDeferredAfter();
            $(document).trigger("criteriaitemchange", [{}]);
            return;
        }
        _submitTimer = setTimeout(function () { submit(setMapView, mapState); }, 100);
    }

    function saveMapState(mapState) {
        $.ajax({
            // try to leverage ajaxQueue plugin to abort previous requests
            mode: "abort",
            port: "mapstate",
            type: "POST",
            url: Utils.AppPath + "/Include/AJAX/MapSearch/SaveMapState.ashx?searchoverride=" + options.cookieGuid,
            data: getFormData(mapState),
            traditional: true,
            cache: false,
            dataType: "json"
        });
    }

    function onMapChange(evt, mapMoveOrigin) {
        if (_mapTimer !== null) {
            clearTimeout(_mapTimer);
        }
        if (_submitEnabled && _map.isLoaded()) {
            var mapState = _map.getMapState();
            if (mapMoveOrigin === "SetMapView") {
                saveMapState(mapState);
            } else {
                loadNeighborhoods(mapState);
                submit(false, mapState, true);
            }
            return;
        }
        _mapTimer = setTimeout(function () { onMapChange(evt, mapMoveOrigin); }, 100);
    }

    function togglePOIIcon(icon, toggleDisplay) {
        var alt, imgUrl;

        if (toggleDisplay) {
            icon.display = !icon.display;
        }

        if (!_poiEnabled) {
            alt = "Points of Interest disabled. Please zoom in to enable.";
            imgUrl = icon.ImageDisabled;
        } else if (icon.display) {
            alt = "Remove " + icon.DisplayName + " Icons from Map";
            imgUrl = icon.ImageOn;
        } else {
            alt = "Display " + icon.DisplayName + " Icons on Map";
            imgUrl = icon.ImageOff;
        }

        var $img = $("#poi-icon-" + icon.ShortName);
        if ($img.length > 0) {
            $img.attr({
                alt: alt,
                title: alt,
                src: POIImagePath + "POI/" + imgUrl
            });
        } else {
            $("<img id='poi-icon-" + icon.ShortName + "' style='border: 0; padding: 4px; cursor: pointer;' alt='" + alt + "' title='" + alt + "' src='" + POIImagePath + "POI/" + imgUrl + "' \\>")
            .appendTo("#mapsearch-poi-icons")
            .click(function () {
                togglePOIIcon(icon, true);
                if (icon.display) {
                    _map.showPOI(icon.PinType);
                } else {
                    _map.hidePOI(icon.PinType);
                }
            });
        }
    }

    function showOrClearPOI(show) {
        for (var i = 5; i < 21; i++) {
            BingMap.PinInfo[i].display = show;
            togglePOIIcon(BingMap.PinInfo[i]);
        }
        if (show) {
            _map.showAllPOI();
        } else {
            _map.hideAllPOI();
        }
    }

    function onPOIChange(e) {
        _poiEnabled = e.poiEnabled;
        for (var i = 5; i < 21; i++) {
            togglePOIIcon(BingMap.PinInfo[i]);
        }
    }

    function setListingOfficePOIIcon($span, selected, toggle, click) {
        var pin = $span.data("pin");
        if (toggle) {
            pin.selected = !pin.selected;
        } else {
            pin.selected = selected;
        }

        var alt, imgUrl;
        if (pin.selected) {
            alt = "Remove " + pin.name + "s from the Map";
            imgUrl = POIImagePath + "POI/" + pin.pinInfo.ImageOn;
        } else {
            alt = "Add " + pin.name + "s to the Map";
            imgUrl = POIImagePath + "POI/" + pin.pinInfo.ImageOff;
        }

        var $img = $("img", $span);
        if ($img.length > 0) {
            $img.attr({
                alt: alt,
                title: alt,
                src: imgUrl
            });
        } else {
            $span.append("<img border='0' style='float: left; margin-right: 3px;' alt='" + alt + "' title='" + alt + "' src='" + imgUrl + "' \\>" + pin.name);
        }
        if (click && pin.onclick && typeof pin.onclick == "function") {
            pin.onclick(pin.selected);
        }
        $span.data("pin", pin);
    }

    function setListingOfficePOIIcons() {
        var pins = [];
        // We look for a collection of checkboxes for statuses - traditional selects are not supported
        $(getSearchTabSelector("input[name='Criteria/Status'][type='checkbox']"), $(_containers.criteria)).each(function () {
            var self = this;
            var statusVal = this.value;
            var statusText = $("label[for='" + this.id + "']", $(_containers.criteria)).text();
            var pinInfo;
            if (!statusText || statusText == "") {
                var next = this.nextSibling;
                while (next) {
                    var v = $.trim(next.nodeType != 1 ? next.nodeValue : $(next).text());
                    if (v != "") {
                        statusText = v;
                        break;
                    }
                    next = next.nextSibling;
                }
            }
            if (statusVal == "1") {
                pinInfo = BingMap.PinInfo[BingMap.PinType.ListingActive];
                if (!statusText || statusText == "") {
                    statusText = "Active";
                }
            } else if (statusVal == "2") {
                pinInfo = BingMap.PinInfo[BingMap.PinType.ListingSold];
                if (!statusText || statusText == "") {
                    statusText = "Sold";
                }
            } else if (statusVal == "5") {
                pinInfo = BingMap.PinInfo[BingMap.PinType.ListingPending];
                if (!statusText || statusText == "") {
                    statusText = "Pending";
                }
            }
            if (pinInfo) {
                pins.push({
                    val: statusVal,
                    name: statusText,
                    pinInfo: pinInfo,
                    selected: this.checked,
                    onclick: function (isActive) {
                        self.checked = isActive;
                        submit();
                    }
                });
            }
        });
        // If we don't have anything from there, always include an active icon
        if (pins.length === 0) {
            pins.push({
                val: 1,
                name: "Listing",
                pinInfo: BingMap.PinInfo[BingMap.PinType.ListingActive],
                selected: _map.pinsAreVisible("listing"),
                onclick: function (isActive) {
                    if (isActive) {
                        _map.showPins("listing");
                    } else {
                        _map.hidePins("listing");
                    }
                }
            });
        }
        // Include an office icon if we're getting them from the server
        if (options.getOfficePins) {
            pins.push({
                val: 0,
                name: "Office",
                pinInfo: BingMap.PinInfo[BingMap.PinType.Office],
                selected: _map.pinsAreVisible("office"),
                onclick: function (isActive) {
                    if (isActive) {
                        _map.showPins("office");
                    } else {
                        _map.hidePins("office");
                    }
                }
            });
        }

        // Loop through our statuses and add the icons to the list
        $("#mapsearch-poi-listingoffice").html("");
        for (var i = 0; i < pins.length; i++) {
            var pin = pins[i];
            var $span = $("<span id='poi-icon-status-" + pin.val + "' style='float: left; padding: 4px; cursor: pointer;'></span>")
                .data("pin", pin)
                .click(function () { setListingOfficePOIIcon($(this), false, true, true); })
                .appendTo("#mapsearch-poi-listingoffice");
            setListingOfficePOIIcon($span, pin.selected);
        }
    }

    function updateSortIcons() {
        $("#mapsearch-results-header a[sort]").each(function () {
            var $this = $(this);
            var sorts = eval($this.attr("sort"));
            // Remove any current indicators
            $this.text($this.text().replace("\u25b2", "").replace("\u25bc", ""));
            // Add an indicator to the currently sorted column
            if (_activeSortID == sorts[0][0]) {
                $this.text($this.text() + "\u25b2");
            } else if (_activeSortID == sorts[1][0]) {
                $this.text($this.text() + "\u25bc");
            }
        });
    }

    function updateListingStatusMessage() {
        var statusHtml = "";
        $(":input[name='Criteria/Status'][checked]", $(_containers.criteria)).each(function () {
            if (this.value == "1") {
                statusHtml += "Active,";
            } else if (this.value == "5") {
                statusHtml += "Pending,";
            } else if (this.value == "2") {
                statusHtml += "Sold,";
            }
        });
        if (statusHtml.length > 0) {
            statusHtml = statusHtml.substring(0, statusHtml.length - 1);
            $("#selectedStatus").html(statusHtml).attr("title", statusHtml);
        }
    }

    function getListingHtml(first) {
        $("#mapsearch-results-body").block({ message: null });

        var data = {
            view: _activeView,
            first: first || 0,
            count: 10,
            clsid: _clsid,
            ListingSortID: _activeSortID
        };

        if ($(".customer-listings-view").length) {
            data["useCookieFormDom"] = "1";
        }

        // Special layout overrides for preview functionality
        if (_qs.contains("LayoutID")) {
            data.LayoutID = _qs.get("LayoutID");
            data.LayoutVersion = _qs.get("LayoutVersion");
        }

        $.ajax({
            mode: "abort",
            port: "listingshtml",
            type: "POST",
            url: Utils.AppPath + "/Include/AJAX/MapSearch/GetListings.aspx?searchoverride=" + options.cookieGuid,
            data: data,
            cache: false,
            dataType: "json",
            error: updateListingCountError,
            success: function (results) {
                results.count = _pageInfo.resultCount;
                results.page = _pageInfo.page;
                showListingGrid(results);
            }
        });
    }

    /**
    * Checks if the map control is loaded.
    * @return {boolean} True if the map control is loaded.
    */
    this.isLoaded = function () {
        return _map ? _map.isLoaded() : false;
    };

    /**
    * Clears the search criteria and optionally zooms to the default map coordinates.
    * @param {{shouldNotResetMap: boolean}=} opt_settings Settings to apply when clearing the criteria (optional).
    */
    this.clearCriteria = function (opt_settings) {
        _submitEnabled = false;

        $(":input", $(_containers.criteria)).each(function () {
            var type = this.type, tag = this.tagName.toLowerCase(), name = this.name;
            if (type == 'text' ||
                type == 'password' ||
                tag == 'textarea' ||
                name == 'Criteria/LocationType' ||
                name == 'Criteria/Location' ||
                name == 'Criteria/LocationValue') {
                this.value = '';
            } else if (type == 'checkbox') {
                // BOET 12356
                if (!this.disabled)
                    this.checked = false;
            } else if (type == 'radio' && name.indexOf('Groups') == -1) {
                this.checked = false;
            } else if (tag == 'select') {
                if ($(this).attr("multiple")) {
                    $.each(this.options, function () {
                        $(this).removeAttr("selected");
                    });
                }
                else
                    this.selectedIndex = 0;
                if (name == "Criteria/ListingTypeID") {
                    _self.toggleListingType(this);
                }
                if (name == "dd_Features") {
                    _self.toggleFeature(this);
                }
            }
        });

        // Make sure we have at least one status enabled
        if ($(":input[name='Criteria/Status']", $(_containers.criteria)).length > 0) {
            if ($(":input[name='Criteria/Status'][checked]", $(_containers.criteria)).length === 0) {
                $(":input[name='Criteria/Status'][value='1']", $(_containers.criteria)).attr("checked", true);
                $(":input[name='Criteria/Status'][id='mapsearch-criteria-status-active']", $(_containers.criteria)).attr("checked", true);
                updateListingStatusMessage();
            }
        }

        // Remove any shapes
        _map.clearPolygons();

        // Clear the last submitted
        _lastLocationSubmitted = "";

        _submitEnabled = true;
        // Let listeners know we're done (such as the search summary)
        $(document).trigger("criteriachange", [{ activeTab: $("#SearchTab").val()}]);

        if (opt_settings && opt_settings.shouldNotResetMap) {
            return;
        }

        // Go to the default map coordinates.  This will submit the search.
        _map.setCenterAndZoom(options.defaultMidLat, options.defaultMidLong, options.defaultZoom);
    };

    function closeBirdseye() {
        $("#mapsearch-birdseye").hide();
        _map.showControls();
        var map = $("#mapsearch-birdseye").data("map");
        if (map) {
            map.dispose();
            map = null;
        }
        $("#mapsearch-birdseye-map").unbind("birdseyeavailable").unbind("mapviewnotsupported");
    }

    function toggleOpenHouse() {
        var checked = $("input[name='HardCodedCriterion'][value='349']:checked", $($(_containers.criteria))).length > 0;
        $("input[name*='OpenHouse']").each(function () { this.disabled = !checked; });
    }

    function addControlEvents() {
        // Add any sort options from the header that are missing from the select
        var sortList = document.getElementById("mapsearch-sort-list");
        _activeSortID = $(sortList).val();
        $(sortList).change(function () {
            _activeSortID = $(this).val();
            updateSortIcons();
            submit();
        });
        $("#mapsearch-results-header a[sort]").each(function () {
            var $this = $(this);
            var sorts = eval($this.attr("sort"));
            // Add any missing sort options to the dropdown
            var $sortList = $(sortList);
            if ($("option[value='" + sorts[0][0] + "']", $sortList).length === 0) {
                sortList.options[sortList.options.length] = new Option(sorts[0][1], sorts[0][0]);
            }
            if ($("option[value='" + sorts[1][0] + "']", $sortList).length === 0) {
                sortList.options[sortList.options.length] = new Option(sorts[1][1], sorts[1][0]);
            }
            // Set the pointer
            $this.css("cursor", "pointer");
            // Add the click handler
            $this.click(function () {
                if (_activeSortID == sorts[0][0]) {
                    _activeSortID = sorts[1][0];
                } else {
                    _activeSortID = sorts[0][0];
                }
                $(sortList).val(_activeSortID);
                updateSortIcons();
                submit();
            });
        });
        // Add an indicator to the currently sorted column
        updateSortIcons();

        // Set the button actions
        $("#mapsearch-commingling-list").change(function () {
            _clsid = $(this).val();
            submit(false);
        });

        //view tab click
        $("div", $(_containers.viewTabs)).click(function () {
            var $this = $(this);
            var oldView = _activeView;
            _activeView = $this.attr("id").replace("mapsearch-viewtab-", "");

            if (_activeView != oldView) {
                $(".ms_tab_on", $(_containers.viewTabs)).removeClass("ms_tab_on").addClass("ms_tab_off");
                $this.removeClass("ms_tab_off").addClass("ms_tab_on");

                getListingHtml(_pageInfo.page * _pageInfo.resultsPerPage);

                if (oldView == "map" || _activeView == "map") {
                    if ($("#mapsearch-mainmap-toolbar").css("visibility") === "visible") {
                        $("#mapsearch-mainmap-toolbar").css("visibility", "hidden");
                        $("#mapsearch-results-header").css("visibility", "hidden");
                        $("#mapsearch-results-all").offset({
                            top: $("#mapsearch-mainmap-toolbar").offset().top,
                            left: $("#mapsearch-results-all").offset().left
                        });
                    } else {
                        $("#mapsearch-mainmap-toolbar").css("visibility", "visible");
                        $("#mapsearch-results-header").css("visibility", "visible");
                        $("#mapsearch-results-all").offset({
                            top: $("#mapsearch-mainmap-toolbar").offset().top + $("#mapsearch-mainmap-toolbar").height(),
                            left: $("#mapsearch-results-all").offset().left
                        });
                        _map.gotoPolyDeferredAfterForce();
                    }
                    // $("#mapsearch-mainmap-toolbar,#mapsearch-results-header").toggle();
                }
                showAdvisory();

                //save the current view tab
                var mapState = _map.getMapState();
                saveMapState(mapState);
            }

            $(document).trigger("criteriachange", [{ activeTab: $("#SearchTab").val(), view: _activeView}]);
        });

        $("#mapsearch-birdseye-close").click(closeBirdseye);

        //$("#mapsearch-advisory-box").mouseover(function() {
        //positionAdvisory(true);
        // });

        // Toolbar events
        $("#mapsearch-maptoolbar-expand").click(function () {
            var expand = ($("#mapsearch-toolbar-expand-input").val() == "true");
            var expandText;
            var height = $("#mapsearch-mainmap").height();
            if (expand) {
                expandText = "Expand Map";
                height -= options.mapExpandDiff;
            } else {
                height += options.mapExpandDiff;
                expandText = "Contract Map";
            }
            _map.resize($("#mapsearch-maptoolbar").width(), height);
            $("#mapsearch-toolbar-expand-text").text(expandText);
            $("#mapsearch-toolbar-expand-input").val(!expand);
        });
        $("#mapsearch-maptoolbar-display input").click(function () {
            if (this.checked) {
                _map.showAllPins();
            } else {
                _map.hideAllPins();
            }
        });
        $("#mapsearch-maptoolbar-draw").click(function () {
            if ($(this).is(".drawing")) {
                _self.endDrawing(true);
            } else {
                _self.startDrawing();
            }
        });
        $("#mapsearch-maptoolbar-poi").click(function () {
            $("#mapsearch-poi").toggle("slow");
        });
        $("#mapsearch-poi-close").click(function () {
            $("#mapsearch-poi").hide("slow");
        });
        $("#mapsearch-poi-show").click(function (event) {
            event.preventDefault();

            showOrClearPOI(true);
        });
        $("#mapsearch-poi-clear").click(function (event) {
            event.preventDefault();
            showOrClearPOI(false);
        });
        $("#mapsearch-mainmap").bind("polypointchange", function (e) {
            if (e.tipID == 1) {
                setAdvisoryText("You are in Drawing mode.", "Click on the starting point of your search area to start drawing.");
            } else if (e.tipID == 2) {
                setAdvisoryText("Click on the next point.", "You can click on \"Cancel Drawing\" at any time to come out of drawing mode.");
            } else if (e.tipID == 3) {
                setAdvisoryText("Now continue drawing your search area...", "Or click on the End Drawing point on the map to finish drawing.");
            }
        });
    }

    function gotoLocation(loc, locType) {
        if (!loc) {
            loc = "";
        }

        // Find out if we've changed search tabs since the last time we changed locations
        // If it has, then we're going to change to the best view no matter what
        var searchTab = $("#SearchTab").val();

        var searchTabChanged = (_lastLocationSearchTab && _lastLocationSearchTab != "" && searchTab != _lastLocationSearchTab);
        _lastLocationSearchTab = searchTab;

        if (loc == "") {
            submit(searchTabChanged);
            return;
        } else if (locType.toLowerCase() == "address") {
            if ($("#" + searchTab + " :input[name='Criteria/FilterByAddress']").val() == "1") {
                submit(true);
            } else {
                _map.addAddressPin(loc);
            }
        } else {
            submit(true);
        }
    }

    function formatLocationItem(data, includeType) {
        var item = "";
        if (data) {
            item = data.Name;
            if (data.City && data.City.length > 0) {
                item += ", " + data.City;
            }
            if (data.State && data.State.length > 0) {
                item += ", " + data.State;
            }
            if (includeType) {
                item += " (" + data.Type + ")";
            }
        }

        return item;
    }

    function extractLocationType(location, acceptedLocationTypes) {
        var match = "";
        // If we got matches, take the last one.
        // This accounts for locations like Los Angeles (County) (School District)
        var start = location.lastIndexOf("(");
        var end = location.lastIndexOf(")");
        if (start != -1 && end != -1) {
            match = location.substring(start + 1, end);
        }
        if (!acceptedLocationTypes || acceptedLocationTypes.length === 0 || acceptedLocationTypes.toLowerCase().indexOf(match.toLowerCase()) != -1) {
            return match;
        } else {
            return "";
        }
    }

    function addStartingLocations(locationNames, locationTypes, locationValues) {
        if (locationNames && locationNames.length !== "") {
            getHiddenInput("Criteria/Location").val(locationNames);
            getHiddenInput("Criteria/LocationType").val(locationTypes);
            getHiddenInput("Criteria/LocationValue").val(locationValues);
        }
        // The locations are in the hidden inputs, clear out the text inputs
        $("input.location,input[name='Criteria/City'],input[name='Criteria/ZipCodeAdvanced']").val("");
    }

    function criteriaOnLoad() {
        _justLoaded = true;
        var $criteria = $(_containers.criteria);
        var $criteriaForm = $(_containers.criteriaForm);
        var $searchSummary = $(_containers.searchSummary);

        _displaySort = !(document.getElementById("SearchVendor") && document.getElementById("SearchVendor").value.length > 0);
        if (_displaySort) {
            $("#mapsearch-sort").show();
        } else {
            $("#mapsearch-sort").hide();
        }

        // Dynamically add in another set of clear & submit buttons above the MORE SEARCH OPTIONS link; Uses the same base HTML as exists in SearchContainer.ascx for the same buttons.
        $(".toggleadvanced").before("<div id='mapsearch-criteria-actions-2-container' style='display: none;'><div id='mapsearch-criteria-actions-2'><div class='button-clear' tabindex='0' title='Clear Criteria'>Clear Criteria</div><a id='search-criteria-button' class='button-submit ms_button1' href='JavaScript:;' title='Search' name='ms-search-button'><span class='icon-search'></span>Search</a><div class='clearfloats'></div></div></div>");


        $("#customerLoginSpinner").hide();

        // Attach the events to the clear & submit buttons
        $(".button-clear", $criteriaForm).click(_self.clearCriteria)
            .keypress(function (event) {
                if (event.keyCode == '13') {
                    event.preventDefault();
                    _self.clearCriteria();
                }
            });

        $(".button-clear", $searchSummary).click(_self.clearCriteria)
            .keypress(function (event) {
                if (event.keyCode == '13') {
                    event.preventDefault();
                    _self.clearCriteria();
                }
            });

        $(".button-submit", $criteriaForm).click(function () {
            if (!_submitting) {
                submit(false);
            }
        }).keypress(function (event) {
            if (event.keyCode == '13') {
                if (!_submitting) {
                    submit(false);
                }
            }
        });

        // If there's a search tab in the layout (legacy) remove it, we already have one in the main control
        $("input[name='SearchTab']", $criteria).remove();
        _self.toggleCriteria();

        // Toggle the advanced search criteria
        $(".toggleadvanced").click(function () {
            toggleAdvancedCriteria();
        });

        // Make sure the property types are set correctly
        $("select[name='Criteria/ListingTypeID']", $criteria).each(function () {
            _self.toggleListingType(this, true);
        });

        // If the location box is actually named Criteria/Location in some legacy layouts, update
        // it to Criteria/LocationBox so it doesn't conflict with our hidden inputs
        $("input[type='text'][name='Criteria/Location']", $criteria).each(function () {
            $(this).attr("name", "Criteria/LocationBox");
        });

        // Add the starting location criteria
        addStartingLocations(options.startingLocationNames, options.startingLocationTypes, options.startingLocationValues);

        // OK to submit before attaching events
        _submitEnabled = options.submitEnabledOnLoad;

        // Fix any background images
        $criteria.pngFix();

        // Add onchange events to form elements, allowing for layout-defined overrides
        $(":input[name]", $criteria).each(function () {
            // ListingNumber is not a 'location' but it does the best map view thing,
            // so we use the gotoLocation method to take care of this
            if (this.name == "Criteria/ListingNumber") {
                $(this).unbind("change");
                $(this).change(function () { gotoLocation(this.value, "ListingNumber"); });
            } else if (this.type == "checkbox" && !this.onclick) {
                $(this).click(function () { _self.onChangeSubmit(this); });
            } else if (this.type != "checkbox" && !this.onchange && !$(this).is(".location")) {
                $(this).change(function () { _self.onChangeSubmit(this); });
            }
            $(this).focus(function () { if (this.select) { this.select(); } });
            $(this).change(function () {
                if (this.type == "checkbox" || this.type == "radio") {
                    $(":input[name='" + this.name + "'][value='" + this.value + "']", $criteria).attr("checked", this.checked);
                } else if (this.nodeName == "SELECT") {
                    $(":input[name='" + this.name + "']").has("option[value='" + $(this).val() + "']").val($(this).val());
                } else {
                    $("input[name='" + this.name + "']", $criteria).val($(this).val());
                }
            });
        });

        $("select.featuresdd[not(onchange)]", $criteria).change(function () {
            var $featureList = $("#" + $(this).val(), $criteria);
            $featureList.prevAll().hide();
            $featureList.nextAll().hide();
            $featureList.show();
        });

        // Location autocomplete
        $(".location", $criteria).each(function () {
            var $el = $(this);
            // This is the hidden form element that stores the location types for this element
            // The "rel" attribute says which location element in this tab we're dealing with
            var $hiddenType = $("input[type='hidden'][rel='" + $el.attr("id") + "']", $criteria);
            // Get the types that we'll be supporting
            var locationType = $el.attr("locationType");
            if (typeof (locationType) == "undefined") {
                locationType = "";
            }
            // We show the location type in the textbox if we're supporting more than one type ("" = all types)
            var showType = (locationType == "" || locationType.indexOf(",") != -1);
            // If so, populate the initial textbox with the type, if we know it and it's not already there
            if (showType && $el.val()) {
                var initialLocationType = $hiddenType.val();
                if (initialLocationType && initialLocationType.length > 0 && $el.val().indexOf("(" + initialLocationType + ")") == -1) {
                    $el.val($el.val() + " (" + initialLocationType + ")");
                }
            }
            // Setup the autocomplete
            $el.autocomplete(Utils.AppPath + "/Include/AJAX/MapSearch/GetLocations.aspx", {
                dataType: "json",
                minChars: 2,
                width: "auto",
                extraParams: { type: locationType },
                max: 100,
                matchSubset: false,
                cacheLength: 0,
                parse: function (data) {
                    var parsed = [];
                    // Add the json results
                    var countMls = 0;
                    var countZip = 0;
                    var countCity = 0;
                    var countCounty = 0;
                    var countNeighborhood = 0;
                    var countSubdivision = 0;
                    var countLakeName = 0;
                    var lastCityOrZip = -1;
                    if (data != null) {
                        for (var i = 0, len = data.length; i < len; i++) {
                            var d = data[i];
                            var dType = d.Type.toLowerCase();

                            // Keep track of the count of each search type
                            // so we can insert the address search type at any point in the list                            
                            if (dType == "mls #")
                                countMls++
                            else if (dType == "zip code")
                                countZip++;
                            else if (dType == "city")
                                countCity++;
                            else if (dType == "county")
                                countCounty++;
                            else if (dType == "neighborhood")
                                countNeighborhood++;
                            else if (dType == "subdivision")
                                countSubdivision++;
                            else if (dType == "lake name")
                                countLakeName++;

                            // Default position for address earch type is after the last city or zip code
                            if (dType == "city" || dType == "zip code") {
                                lastCityOrZip = i;
                            }
                            parsed.push({
                                data: d,
                                value: d.Name,
                                result: formatLocationItem(d, showType)
                            });
                        }
                    }
                    // Always put an Address location type in the results, if we're supporting addresses,
                    // and there's not already a supported embedded type in the textbox
                    if (locationType == "" || (locationType.indexOf(",") != -1 && locationType.toLowerCase().indexOf("address") != -1)) {
                        var val = $el.val().trim();
                        if (val.length > 0) {
                            var embeddedType = extractLocationType(val, locationType);
                            if (embeddedType.length === 0) {
                                var addressSpliceIndex = lastCityOrZip + 1;

                                // *address is a special syntax to enforce the position to insert the address search type 
                                // relative to the other types
                                // ie: locationType="mls #,zip code,city,county,*address,neighborhood,subdivision,lake name"                                
                                if (locationType.toLowerCase().indexOf("*address") != -1) {
                                    var arLocationTypes = locationType.toLowerCase().split(",");
                                    addressSpliceIndex = 0;

                                    // Loop through the location types and figure out where to insert the address search type
                                    for (var i = 0; i < arLocationTypes.length; i++) {
                                        var type = arLocationTypes[i];
                                        if (type != "*address") {
                                            if (type == "mls #")
                                                addressSpliceIndex += countMls;
                                            else if (type == "zip code")
                                                addressSpliceIndex += countZip;
                                            else if (type == "city")
                                                addressSpliceIndex += countCity;
                                            else if (type == "county")
                                                addressSpliceIndex += countCounty;
                                            else if (type == "neighborhood")
                                                addressSpliceIndex += countNeighborhood;
                                            else if (type == "subdivision")
                                                addressSpliceIndex += countSubdivision;
                                            else if (type == "lake name")
                                                addressSpliceIndex += countLakeName;
                                        }
                                        else
                                            break;
                                    }
                                }

                                // Insert the address search type
                                var addressData = { Name: val, Type: "Address" };
                                parsed.splice(addressSpliceIndex, 0, { data: addressData, value: addressData.Name, result: formatLocationItem(addressData, showType) });
                            }
                        }
                    }
                    return parsed;
                },
                formatItem: function (data) { return "<nobr>" + formatLocationItem(data, showType) + "</nobr>"; }
            })
        .result(function (event, data, formatted) {
            // If there's no data that means we got here onblur,
            // and we need to add a type for disambiguation
            var embeddedZip = "";
            if (!data || !data.Type || data.Type == "") {
                var val = $el.val().trim();
                if (val.length > 0) {
                    // Get a type embedded in the location
                    var type = extractLocationType(val, locationType);
                    // If no embedded type, use a default type
                    if (type.length === 0) {
                        // If no accepted location types configured, try to figure out a type
                        var regexZip = /^\d{5}$/;
                        var valWords = val.replace(",", " ").trim().replace(/\s+/g, " ").split(" ");
                        if (valWords.length == 1) {
                            if (regexZip.test(val)) {
                                type = "Zip Code";
                            } else {
                                type = "Address";
                            }
                        } else {
                            // The last word is a zip
                            var lastWord = valWords[valWords.length - 1];
                            if (regexZip.test(lastWord)) {
                                // If there's no comma before the zip, add one, to make it easier for the address parser
                                if (val.indexOf(",") == -1) {
                                    valWords.splice(valWords.length - 1, 0, ",");
                                    val = valWords.join(" ").replace(" , ", ", ");
                                }
                                type = "Address";
                                embeddedZip = lastWord;
                            } else if (lastWord.toLowerCase() == "county") {
                                val = val.substring(0, val.length - 6).trim();
                                type = "County";
                            } else if (lastWord.length == 2 && $.inArray(lastWord.toUpperCase(), Utils.Lookups.stateCodes) != -1) { // The last word is a state?
                                // If there's no comma before the state, add one, for a consistent UI
                                // Special case for CT since it can be both a state and a street suffix
                                if (val.indexOf(",") == -1 && lastWord.toUpperCase() !== "CT") {
                                    valWords.splice(valWords.length - 1, 0, ",");
                                    val = valWords.join(" ").replace(" , ", ", ");
                                }
                                // If there's only one comma, assume it's a city
                                if (val.split(",").length == 2) {
                                    type = "City";
                                } else {
                                    type = "Address";
                                }
                            } else {
                                type = "Address";
                            }
                        }
                        // Make sure our new default is in the list of acceptable types
                        if (locationType && locationType != "") {
                            var acceptable = false;
                            var locationTypes = locationType.split(",");
                            for (var i = 0; i < locationTypes.length; i++) {
                                if (type.toLowerCase() == locationTypes[i].toLowerCase()) {
                                    acceptable = true;
                                    break;
                                }
                            }
                            if (!acceptable) {
                                type = locationType.split(",")[0];
                            }
                        }
                        // If more than one type is acceptable, embed it in the location
                        if (locationType == "" || locationType.indexOf(",") != -1) {
                            $el.val(val + " (" + type + ")");
                        }
                    }
                    data = { Name: val, Type: type };
                }
            }
            if (data && data.Type) {
                // Remove it from the location box
                $el.val("");

                if (embeddedZip.length > 0) {
                    data.Name = embeddedZip;
                    data.Type = "Zip Code";
                }

                // Add the location to our hidden criteria
                // and adjust the map
                _self.addLocation(data);

            } else {
                gotoLocation();
            }
            // Handle Chrome/Safari not seeing change
            // $el.blur().focus();
        })
        .change(function () {
            // Make sure we're not double-searching HS22605
            // Waiting 100ms means we've cleared out the location box
            // already by the time we search
            setTimeout(function () {
                $el.search();
            }, 100);
        });
        });
        // MinPrice/MaxPrice autocompletes
        $("[type='text'][name*='Price']", $criteria).each(function () {
            if (this.value != "") {
                this.value = "$" + Utils.Format.addCommas(this.value);
            }
            $(this).autocomplete("numeric", {
                maxChars: 8,
                formatResult: function (row) { return "$" + Utils.Format.addCommas(row); },
                formatItem: function (row) { return "$" + Utils.Format.addCommas(row); },
                highlight: false,
                selectFirst: false
            })
        .result(function (event, data, formatted) {
            $(this).change();
        });
        });
        // MinSqFt/MaxSqFt autocompletes
        $("[type='text'][name*='SquareFootage']", $criteria).each(function () {
            this.value = Utils.Format.addCommas(this.value);
            $(this).autocomplete("numeric", {
                maxChars: 5,
                formatResult: function (row) { return Utils.Format.addCommas(row); },
                formatItem: function (row) { return Utils.Format.addCommas(row); },
                highlight: false,
                selectFirst: false
            })
        .result(function (event, data, formatted) {
            $(this).change();
        });
        });
        // MinLotSize/MaxLotSize autocompletes
        $("[type='text'][name*='Acreage']", $criteria).each(function () {
            this.value = Utils.Format.addCommas(this.value);
            $(this).autocomplete("numeric", {
                maxChars: 7,
                formatResult: function (row) { return Utils.Format.addCommas(row); },
                formatItem: function (row) { return Utils.Format.addCommas(row); },
                highlight: false,
                selectFirst: false
            })
        .result(function (event, data, formatted) {
            $(this).change();
        });
        });
        // City autocomplete
        $("[type='text'][name='Criteria/City']", $criteria)
        .autocomplete(Utils.AppPath + "/Include/AJAX/MapSearch/GetLocations.aspx", {
            dataType: "json",
            extraParams: { type: "City" },
            minChars: 2,
            max: 50,
            width: "auto",
            cacheLength: 50,
            parse: function (data) {
                var parsed = [];
                for (var i = 0, len = data.length; i < len; i++) {
                    var d = data[i];
                    parsed[parsed.length] = {
                        data: d,
                        value: d.Name,
                        result: formatLocationItem(d, false)
                    };
                }
                return parsed;
            },
            formatItem: function (data) { return "<nobr>" + formatLocationItem(data, false) + "</nobr>"; },
            delay: 100
        })
        .result(function (event, data, formatted) {
            if (!data) {
                var val = $(this).val();
                if (val && val.length > 0) {
                    var state = defaultState;
                    var comma = val.lastIndexOf(",");
                    if (comma != -1) {
                        state = val.substring(comma + 1);
                        val = val.substring(0, comma);
                    }
                    data = { Name: val, Type: "City", State: state };
                    $(this).val(val + ", " + state);
                } else {
                    data = { Name: "" };
                }
            }
            $(this).val("");
            _self.addLocation(data);
        })
        .change(function () {
            $(this).search();
        });

        // Open house date pickers
        $("input[name='HardCodedCriterion'][value='349']", $criteria).click(toggleOpenHouse);
        $("input[name*='OpenHouse']", $criteria).datepicker();
        toggleOpenHouse();

        // Status pseudo-dropdown
        updateListingStatusMessage();

        // Status checkboxes
        $("input[name='Criteria/Status'][type='checkbox']", $criteria).click(function () {
            var $span = $("#poi-icon-status-" + this.value.toLowerCase());
            if ($span.length > 0) {
                setListingOfficePOIIcon($span, this.checked);
            }
        });

        // If we have any advanced criteria populated, expand the div
        $(".advanced :input").each(function () {
            // If no ele name then continue, some form elements may not be needed, we take only those with a name
            if (!this.name) {
                return true;
            }

            if (this.type == "hidden") {
                return true;
            }

            // If the value is empty, don't include (to cut down on space)
            if (this.value.length === 0) {
                return true;
            }

            // Skip the element if its type radio or checkbox and is not "checked"
            if ((this.type == "radio" || this.type == "checkbox") && !this.checked) {
                return true;
            }

            // Skip disabled elements
            if (this.disabled) {
                return true;
            }

            // Skip radio buttons that used for matching rules
            if (this.type == "radio" && this.name.indexOf("Groups") === 0) {
                return true;
            }

            // Skip status if only active is selected
            if (this.name == "Criteria/Status" && this.value.indexOf("2") == -1 && this.value.indexOf("5") == -1) {
                return true;
            }


            // If we got here, we have some data, expand it
            showAdvancedCriteria();
            /*
            $(this).closest(".advanced").show();
            $(this).closest(".searchTab").find(".toggleadvanced span.arrow").removeClass("arrow-left").addClass("arrow-down");
            */
            return false;
        });
        // Let listeners know we're done (such as the search summary)
        $(document).trigger("criteriachange", [{ activeTab: $("#SearchTab").val()}]);

        // ... and expand the accordion
        var $accordion = $('#mapsearch-left-accordion');
        if ($accordion.length > 0) {
            $accordion.accordion('option', 'collapsible', false);
            $accordion.accordion('activate', 0);
        }

        if (typeof ToggleOnMarketSearchCriteria == 'function')
            ToggleOnMarketSearchCriteria();
    }

    function dispose() {
        if (!_disposed) {
            _map.dispose();
            _map = null;
            options = null;
            unbindListingGrid();
            var birdseyeMap = $("#mapsearch-birdseye").data("map");
            if (birdseyeMap) {
                birdseyeMap.dispose();
                birdseyeMap = null;
            }
            _disposed = true;
        }
    }

    function toggleAdvancedCriteria() {
        var $advanced = $(".advanced", $(_containers.criteriaForm));
        if ($advanced.is(":visible")) {
            hideAdvancedCriteria();
        } else {
            showAdvancedCriteria();
        }
    }

    function showAdvancedCriteria() {
        var $advanced = $(".advanced", $(_containers.criteriaForm));
        $advanced.show();
        $("span.arrow", $(_containers.criteriaForm)).removeClass("arrow-left").addClass("arrow-down");
        $("#mapsearch-criteria-actions-2-container").show();
        $("div.wide a", $(_containers.criteriaForm)).html("LESS SEARCH CRITERIA");
        $("div.toggleadvanced .micro-help-text", $(_containers.criteriaForm)).html("Click here to hide advanced criteria");
    }

    function hideAdvancedCriteria() {
        var $advanced = $(".advanced", $(_containers.criteriaForm));
        $advanced.hide();
        $("span.arrow", $(_containers.criteriaForm)).removeClass("arrow-down").addClass("arrow-left");
        $("#mapsearch-criteria-actions-2-container").hide();
        $("div.wide a", $(_containers.criteriaForm)).html("MORE SEARCH CRITERIA");
        $("div.toggleadvanced .micro-help-text", $(_containers.criteriaForm)).html("Click here to view advanced criteria");
    }

    // Public functions
    this.load = function () {
        // Make sure the defaults are set if this is the first time through
        options = $.extend({}, _defaultOptions, options);

        // Don't submit while loading
        _submitEnabled = false;

        // Set starting CLSID 
        _clsid = (options.startingCLSID == undefined) ? -1 : options.startingCLSID;

        // Subscribe to the map change event and poi change events
        $("#mapsearch-mainmap").bind("mapchange", onMapChange);
        $("#mapsearch-mainmap").bind("poichange", onPOIChange);

        // Create the map
        _map = new BingMap(options);
        _map.load();
        _map.disableMouseZoom();

        // Setup the initial state of the POI icons
        for (var i = 5; i < 21; i++) {
            togglePOIIcon(BingMap.PinInfo[i]);
        }
        $(document).bind("criteriachange", function (e, eventData) {
            setListingOfficePOIIcons(eventData.activeTab);
        });

        $(document).bind("loginupdate", function (e, eventData) {
            if (eventData.canReloadView) {
                getListingHtml(_pageInfo.page * _pageInfo.resultsPerPage);
            }
        });

        $(document).bind("removecriteria", function (e, eventData) {
            removeCriteria(eventData);
        });

        // Store some references to containers for faster lookups
        _containers = {
            criteria: document.getElementById("mapsearch-criteria-layout"),
            criteriaForm: document.getElementById("mapsearch-criteria"),
            count: document.getElementById("mapsearch-count"),
            viewTabs: document.getElementById("mapsearch-viewtabs"),
            searchSummary: document.getElementById("search-summary")
        };

        // Set active view
        var defaultView = options.defaultView;
        if (defaultView == 'detail' || defaultView == 'list')
            _activeView = 'list';
        else if (defaultView == 'gallery')
            _activeView = 'gallery';
        else
            _activeView = 'map';

        // Add non-criteria events
        addControlEvents();

        // Window events
        // The submission to resize the map is delayed
        // to account for firing multiple resize events along the way
        $(window).resize(function () {
            if (_resizeTimer) {
                clearTimeout(_resizeTimer);
            }

            _resizeTimer = setTimeout(function () {
                if (_map) {
                    _map.resize($("#mapsearch-maptoolbar").width());
                }
                positionAdvisory();
                _resizeTimer = null;
            }, 100);
        });
        $(window).unload(dispose);

        // Bind after the criteria loads so that RECos can use the info-hover in their criteria
        $(document).bind("criteriachange", function () {
            $("#mapsearch-container .info-hover").hoverinfo();
        });
        $("#mapsearch-directions :input[title]").titlelabel();

        // Setup the request for the criteria
        var url = Utils.AppPath + '/include/ajax/mapsearch/getsearchcriteria.aspx';
        var qs = [];
        // For overriding the search guid
        if (options.cookieGuid != "") {
            qs.push("searchoverride=" + options.cookieGuid);
        }
        // Special layout overrides for preview functionality
        if (_qs.contains('LayoutID')) {
            qs.push("LayoutID=" + _qs.get("LayoutID"));
            qs.push("LayoutVersion=" + _qs.get("LayoutVersion"));
        }
        // Send the current ListingSearchID along if there is one
        if (_qs.contains('ListingSearchID')) {
            qs.push("ListingSearchID=" + _qs.get("ListingSearchID"));
        }
        // Send the current search parameter along if there is one
        if (_qs.contains('search')) {
            qs.push("search=" + _qs.get("search"));
        }
        if (qs.length > 0) {
            url += "?" + qs.join("&");
        }
        // Load the criteria and attach the events to the elements
        $(_containers.criteria).load(url, qs, criteriaOnLoad);
    };

    // Allows updating the search options after initialization, such as in the criteria
    this.setOptions = function (opts) {
        // Extend the existing options with the incoming options
        if (opts) {
            options = $.extend({}, options, opts);
            _map.setOptions(options);
            _pageInfo.resultsPerPage = options.resultsPerPage || _pageInfo.resultsPerPage;
        }
    };

    this.enableSubmit = function () {
        _submitEnabled = true;
    }

    this.startDrawing = function () {
        $("#mapsearch-maptoolbar-draw").addClass("drawing").html("Cancel Drawing");
        // Clear out legacy neighborhood selection
        if (document.getElementById('selNeighborhood')) {
            document.getElementById('selNeighborhood').value = "";
            document.getElementById('Neighborhood').selectedIndex = 0;
        }
        _map.startDrawing();
    };

    this.endDrawing = function (cancel) {
        $("#mapsearch-maptoolbar-draw").removeClass("drawing").html("Draw Search Area");
        var boundaryData = _map.endDrawing(cancel);
        if (boundaryData) {
            _self.addLocation(boundaryData);
        }
    };

    this.toggleFeature = function (obj) {
        if (obj) {
            for (var i = 0, len = obj.length; i < len; i++) { // hide all
                $("#feature_" + i).hide();
            }
            $("#feature_" + obj.selectedIndex).show(); // display selected
        }
    };

    this.toggleListingType = function (obj, dontSubmit) {
        if (obj) {
            var inputs, name, i, len;
            for (i = 0, len = obj.length; i < len; i++) {// hide all
                // Set the checkboxes to an invalid name so they're not used
                // Setting them to disabled doesn't seem to work
                inputs = document.getElementById('propertyType_' + i).getElementsByTagName('input');
                for (var j = 0, lenj = inputs.length; j < lenj; j++) {
                    name = inputs[j].getAttribute('name');
                    if (name.toLowerCase() == 'criteria/propertytypeid') {
                        inputs[j].setAttribute('name', 'Criteria/PropertyTypeID_X');
                    }
                    if (name.toLowerCase() == 'criteria/defaultpropertytypeid') {
                        inputs[j].setAttribute('name', 'Criteria/DefaultPropertyTypeID_X');
                    }
                }
                hide('propertyType_' + i);
            }
            // Set the checkboxes back to a valid name
            inputs = document.getElementById('propertyType_' + obj.selectedIndex).getElementsByTagName('input');
            for (i = 0, len = inputs.length; i < len; i++) {
                name = inputs[i].getAttribute('name');
                if (name.toLowerCase() == 'criteria/propertytypeid_x') {
                    inputs[i].setAttribute('name', 'Criteria/PropertyTypeID');
                }
                if (name.toLowerCase() == 'criteria/defaultpropertytypeid_x') {
                    inputs[i].setAttribute('name', 'Criteria/DefaultPropertyTypeID');
                }
            }
            $("input[name='Criteria/PropertyTypeID']").blur();
            display('propertyType_' + obj.selectedIndex); // display selected
            // Submit the change
            if (typeof (dontSubmit) == 'undefined' || !dontSubmit) {
                submit();
            }
        }
    };

    this.setListingStatus = function (obj) {
        // Update the status message
        updateListingStatusMessage();

        // And submit the search
        submit();
    };

    this.toggleCriteria = function (activeTab) {
        var passedInActiveTab = activeTab;
        var $searchTab = $("#SearchTab");
        var $criteria = $(_containers.criteria);
        if ($searchTab.length === 0) {
            return;
        }

        var isLegacyCriteria = ($("#basicSearch").length > 0);
        var newBasicSearch = $(".searchtab", $criteria).eq(0).attr("id");
        var expandAdvanced = false;

        // have to deal with legacy search tab values, such as in simple search
        if (!activeTab) {
            var searchTabVal = $searchTab.val();
            if (searchTabVal == "2" || searchTabVal == "advanced" || searchTabVal == "advancedSearch") {
                if (isLegacyCriteria) {
                    activeTab = "advancedSearch";
                } else {
                    activeTab = newBasicSearch;
                    expandAdvanced = true;
                }
            } else if (searchTabVal == "3" || searchTabVal == "mlsID" || searchTabVal == "mlsIDSearch") {
                if (isLegacyCriteria) {
                    activeTab = "mlsIDSearch";
                } else {
                    activeTab = $(".searchtab:has(input[name='Criteria/ListingNumber'])", $criteria).eq(0).attr("id");
                }
            } else if (searchTabVal == "4" || searchTabVal == "address" || searchTabVal == "addressSearch") {
                if (isLegacyCriteria) {
                    activeTab = "addressSearch";
                } else {
                    activeTab = $(".searchtab:has(input[name='Criteria/FullAddress'])", $criteria).eq(0).attr("id");
                }
            } else if (searchTabVal == "5" || searchTabVal == "school" || searchTabVal == "schoolSearch") {
                if (isLegacyCriteria) {
                    activeTab = "schoolSearch";
                } else {
                    activeTab = $(".searchtab:has(input[name='Criteria/School'])", $criteria).eq(0).attr("id");
                }
            } else if (searchTabVal == "6") {
                // I don't think we'll ever get a searchtabval of 6 for the new criteria
                activeTab = "foreclosureSearch";
            } else if (searchTabVal == "1" || searchTabVal == "basic" || searchTabVal == "basicSearch") {
                if (isLegacyCriteria) {
                    activeTab = "basicSearch";
                } else {
                    activeTab = newBasicSearch;
                }
            } else if (searchTabVal != "") {
                activeTab = searchTabVal;
            }
            // If it's still undefined, use the default
            if (!activeTab) {
                if (isLegacyCriteria) {
                    activeTab = "basicSearch";
                } else {
                    activeTab = newBasicSearch;
                }
            }
        } else if (isLegacyCriteria && activeTab.indexOf("Search") == -1) {
            activeTab += "Search";
        }

        if (activeTab && activeTab.length > 0) {
            var $el = $("#" + activeTab);
            if ($el.length > 0) {
                // Not legacy criteria
                if (!isLegacyCriteria) {
                    $(".searchtab", $criteria).removeClass("active");
                    $el.addClass("active");
                    if (expandAdvanced) {
                        showAdvancedCriteria();
                    }
                    $(":input[type!='hidden']:visible:first", $el).focus();
                } else {
                    // Legacy criteria
                    if (activeTab == 'basicSearch') {
                        $("#advancedSearch,#mlsIDSearch,#addressSearch,#schoolSearch,#foreclosureSearch,#tab_adv_on,#tab_basic_off").hide();
                        $("#tab_adv_off,#tab_basic_on,#basicSearch").show();
                    } else if (activeTab == 'advancedSearch') {
                        $("#basicSearch,#mlsIDSearch,#addressSearch,#schoolSearch,#foreclosureSearch,#tab_basic_on,#tab_adv_off").hide();
                        $("#tab_basic_off,#tab_adv_on,#advancedSearch").show();
                    } else if (activeTab == 'mlsIDSearch') {
                        $("#basicSearch,#advancedSearch,#addressSearch,#schoolSearch,#foreclosureSearch").hide();
                        $("#mlsIDSearch").show();
                    } else if (activeTab == 'addressSearch') {
                        $("#basicSearch,#advancedSearch,#mlsIDSearch,#schoolSearch,#foreclosureSearch,").hide();
                        $("#addressSearch").show();
                    } else if (activeTab == 'foreclosureSearch') {
                        $("#basicSearch,#advancedSearch,#mlsIDSearch,#schoolSearch").hide();
                        $("#foreclosureSearch").show();
                    } else if (activeTab == 'schoolSearch') {
                        $("#basicSearch,#advancedSearch,#mlsIDSearch,#addressSearch,#foreclosureSearch").hide();
                        $("#schoolSearch").show();
                    }
                }
                var leftOffset = $("#mapsearch-left").offset().top;
                if (leftOffset < $(window).scrollTop()) {
                    $("body,html").animate({ scrollTop: leftOffset }, 1000, function () {
                        $(document).trigger("criteriachange", [{ activeTab: activeTab}]);
                    });
                }
                $searchTab.val(activeTab);
                // Let listeners know we're done (such as the search summary)
                if (passedInActiveTab) {
                    $(document).trigger("criteriachange", [{ activeTab: activeTab}]);
                }
            }
        }
    };

    this.onChangeSubmit = function (obj, validation) {

        // Hack to force money validation for price fields, even if marked numeric
        if (obj && obj.name && obj.name.toLowerCase().indexOf('price') > -1 && obj.type === 'text') {
            validation = 'money';
        }
        // Updated validation name
        if (validation == "mmddyyyy") {
            validation = "date";
        }

        if (!validation || validation.length === 0) {
            if ($(obj).is(".numeric")) {
                validation = "numeric";
            } else if ($(obj).is(".money")) {
                validation = "money";
            } else if ($(obj).is(".date")) {
                validation = "date";
            } else if ($(obj).is(".year")) {
                validation = "year";
            }
        }

        var objVal = null;
        if (obj) {
            // Some initial formatting
            if (obj.value.length > 0) {
                if (validation == "numeric") {
                    obj.value = Utils.Format.addCommas(obj.value);
                } else if (validation == "money") {
                    obj.value = "$" + Utils.Format.addCommas(obj.value);
                }
            }
            objVal = obj.value;
        } else {
            return;
        }

        // Changing from switch to if statements for perf reasons, per IE perf team
        var isValid = false;
        if (obj.value.length > 0) {
            if (validation == 'numeric') {
                isValid = IsNumeric(objVal);
            } else if (validation == 'money') {
                isValid = IsMoney(objVal);
            } else if (validation == 'date') {
                isValid = IsDate(objVal);
            } else if (validation == "year") {
                isValid = (IsNumeric(objVal) && objVal.length == 4 && new Date().getFullYear() >= objVal);
            } else {
                isValid = true;
            }
        } else {
            isValid = true;
        }

        if (isValid) {
            submit();
        } else {
            alert('Invalid entry. Please enter a ' + validation + ' value.');
            setTimeout(function () { obj.focus(); obj.select(); }, 25);
        }
    };

    this.GotoZip = function (zip) {
        var data = { Name: zip, Type: "Zip Code", Value: zip };
        $("input[name='Criteria/ZipCode'],input[name='Criteria/ZipCodeAdvanced']", $(_containers.criteria)).val("");
        _self.addLocation(data);
    };
    this.GotoCity = function (city) {
        var data = { Name: city, Type: "City", Value: city };
        $("input[name='Criteria/City']", $(_containers.criteria)).val("");
        _self.addLocation(data);
    };

    // For legacy criteria
    this.addressGoto = function (prefix) {
        // Consolidated from removed mapGoto() method
        if (prefix === null || prefix == '') {
            var searchTab = $("#SearchTab").val();
            if (searchTab == '5' || searchTab == "subdivisionSearch") {
                prefix = 'SS';
            } else if (searchTab == '4' || searchTab == "addressSearch") {
                prefix = 'AS';
            } else {
                return; // Just a normal search, get out
            }
        }
        prefix = "#" + prefix;

        var street = $(prefix + "_StreetName").val();
        var city = $(prefix + "_City").val();
        var state = $(prefix + "_State").val();
        var zip = $(prefix + "_ZipCode").val();

        // Need at least city or city+state or zip to zoom
        var address = '';
        if (city.length > 0) {
            address = city;
        }

        if (state.length > 0) {
            address += ',' + state;
        }

        if (address.length > 0) {
            address += ',usa';
        }

        if (address.length === 0 && zip.length > 0) {
            address = zip;
        }

        if (address.length === 0) {
            return;
        }

        // If we have a street name, add that to the address
        if (street.length > 0) {
            address = street + ',' + address;
        }

        // Zoom to the address
        if (street.length > 0) { // Add address pin if we're on a single address, after waiting for the map to move
            gotoLocation(address, "Address");
        }
    };

    this.prevPage = function () {
        getListingHtml(--_pageInfo.page * _pageInfo.resultsPerPage);
    };

    this.nextPage = function () {
        getListingHtml(++_pageInfo.page * _pageInfo.resultsPerPage);
    };

    this.gotoPage = function (page) {
        page = page <= 0 ? 1 : page;
        _pageInfo.page = page - 1;
        getListingHtml(_pageInfo.page * _pageInfo.resultsPerPage);
    };

    this.togglePinBubble = function (newID, currID) {
        $("#pin_" + newID).show();
        $("#pin_" + currID).hide();
    };

    this.openBirdseye = function (listingid, latitude, longitude) {
        $("#mapsearch-birdseye .ms_panel_text").html("Bird's Eye View");
        $("#mapsearch-birdseye").show();
        var map = new BingMap({
            startingMidLat: latitude,
            startingMidLong: longitude,
            startingZoom: 19,
            mapView: VEMapStyle.Birdseye,
            fireOnChange: false,
            showParcelLines: options.showParceLines,
            container: "mapsearch-birdseye-map"
        });
        $("#mapsearch-birdseye-map")
           .bind("birdseyeavailable", function () {
               //try {
               map.startAddingPins("birdseye");
               map.addPin("birdseye", "1_" + listingid, latitude, longitude, 1, "Listing", "{FROM_BIRDSEYE}");
               map.endAddingPins("birdseye");
               map.showPins("birdseye");
               //} catch (e) { Utils.Logger.warn("Error adding birdseye pin to map: " + e.message); }
           })
           .bind("mapviewnotsupported", function () {
               $("#mapsearch-birdseye .ms_panel_text").html("Bird's Eye View - Not Supported Here");
           });
        map.load();
        $("#mapsearch-birdseye").data("map", map);
        _map.hideControls();
    };

}


// Legacy map criteria methods
function toggling(divName) {
    if (document.getElementById(divName)) {
        if (document.getElementById(divName).style.display == '') {
            document.getElementById(divName).style.display = 'none';
        } else {
            document.getElementById(divName).style.display = '';
        }
    }
}
function toggleOpenHouseRange() {
    if (document.getElementById('OpenHouses').checked) {
        document.getElementById('OpenHouseStartDtm').disabled = false;
        document.getElementById('OpenHouseStopDtm').disabled = false;
    } else {
        document.getElementById('OpenHouseStartDtm').disabled = true;
        document.getElementById('OpenHouseStopDtm').disabled = true;
    }

}
function lcs() {
    // Old datepicker
    return true;
}

function setArrows() {
    $('.search-criteria-panel-header').each(function (index) {
        if (!$('span.arrow', this).hasClass('arrow arrow-left'))
            $("span.arrow", $(".search-criteria-panel-header")).addClass("arrow-left");
    });

    $('.search-criteria-panel-header').click(function () {
        if ($('span.arrow', this).hasClass('arrow arrow-left'))
            $('span.arrow', this).removeClass('arrow-left').addClass('arrow-down');
        else
            $('span.arrow', this).addClass('arrow arrow-left').removeClass('arrow-down');

        $(this).next().toggle('fast');
        return false;
    });

}

function toggleSearchAreas(showSearchAreaList, hideSearchAreaList) {
    $(showSearchAreaList).show();
    $(hideSearchAreaList).hide();
}

