﻿/*

This file contains the panel object which is dynamically generated on the page.
Some styles are inherited from it's respective CSS file.

*/

function Panel(panelID){

    /* Constants */

    // Maximum number of results to return for any search.
    var MAX_RESULTS = 301;
    
    var _scrollBarWidth = 18;

    // The three radius options on the filter
    var radius1 = 0.5;
    var radius2 = 1.0;
    var radius3 = 2.0;
    
    // Forced zip radius to 10mi, remove or set to 0 to disable.
    var FORCED_ZIP_RADIUS = 10;
    
    // Section verbiage.
    var dd_initial_message = "To obtain directions to a location, hover over an icon and choose 'Driving Directions'.";
    
    // Alias Parameters for search codes
    var FormInputMSARSA = "cmbMSARSA";
    var FormInputMTA = "cmbMTA";
    var FormInputBTA = "cmbBTA";
    var FormInputFCC = "txtFCC";
    var FormInputFAA = "txtFAA";

    var FormInputLatDirection = "txtLatDir";
    var FormInputLatDegree = "txtLatDeg";
    var FormInputLatMinute = "txtLatMin";
    var FormInputLatSecond = "txtLatSec";    
    var FormInputLonDirection = "txtLonDir";
    var FormInputLonDegree = "txtLonDeg";
    var FormInputLonMinute = "txtLonMin";
    var FormInputLonSecond = "txtLonSec";    
    
    var FormInputWireless = "chkWireless";
    var FormInputBroadcast = "chkBroadcast";
    var FormInputBuilding = "chkBuilding";
    var FormInputRooftop = "chkRooftop";
    var FormInputManaged = "chkManaged";
    var FormInputUnknown = "chkUnknown";
    
    var SQLMSARSAField = "MsaRsa";
    var SQLMTAField = "Mta";
    var SQLBTAField = "Bta";
    var SQLFAAField = "FAA";
    var SQLFCCField = "FCC";
    
    var comboValueDelimiter = "^";

    /* End Constants */

    /* Members */

    // Handle to the panel created and attached to the page.
    var _panel = null;
    
    // Handle to the panel's width, used as a point of reference for sections.
    var _panelWidth = null;
    
    // Handle to the panel's control button.
    var _panelControl = null;
    
    // Handle to control button's data.
    var _panelControlData = null;
    
    // Handle to control button's left offset.
    var _panelControlLeft = null;
    
    // Stores the last executed find call, so that it can be executed again if the filter is changed.
    var _lastCall = null;

    // Stores handles to the various sections of the panel for quick show/hide manipulation.
    var _sections = null;
    
    // Keeps track of whether or not the search is state only.
    var _stateOnlySearch = false;
    
    // FindByCode data - used in callback if msa/mta etc don't have coordinates.
    var _findByCodeData = new Array();
    
    // Keeps track of whether or not we are doing a zip only search for a forced radius of 10mi.
    var _zipOnlySearch = false;
    
    // Holds the last found result set, simply for disambiguation purposes.
    var _lastResults = null;
    
    // Hold the chosen disambiguated location for filter purposes
    var _lastResultsIndex = null;
    
    // Determines whether or not the find call was due to a filter change.
    var _filterChange = false;

    /* End Members */
        
    _init();

    function _init(){
        _panel = document.createElement('div');
        _panel.id = panelID;
        
        // Create a search panel.
        _panel.className = "ve_panel";

        // Don't use transparency for IE6 due to combo box redraw issue.
        if (!isBrowserIE6()){
            _panel.className += " transparent";
        }

        document.body.appendChild(_panel);
        
        // Get panel width before scroll bar appears because it alters clientWidth afterwards.
        _panelWidth = _panel.clientWidth;
        
        // Set style attributes of the panel's minimize/maximize button/pane
        _panelControl = document.createElement('div');
        _panelControl.id = panelID + "_control";
        _panelControl.className = "ve_control transparent";
        _panelControl.style.position = "absolute";
        _panelControl.style.top = _panel.offsetTop + "px";
        _panelControl.style.left = (_panelWidth + _panel.offsetLeft + 2) + "px";
        _panelControl.style.width = 16 + "px";
        _panelControl.style.height = _panel.clientHeight + "px";
        _panelControl.style.backgroundColor = "#f0f0f0";
        document.body.appendChild(_panelControl);
        
        // Set style attributes of panel's control button
        _panelControlData = document.createElement("div");
        _panelControlData.className = "ve_control_data";
        _panelControlData.innerHTML = "<a href='javascript:panel.Hide();'>&lt;<br/>&lt;<br/>&lt;</a>";
        //_panelControl.appendChild(_panelControlData); //not showing in v 6.1
        _panelControlLeft = _panelControl.offsetLeft;
        
        // Initialize an array that will hold the differnet panel sections.
        _sections = new Array();
        
        // Add the main search criteria section to panel.
        _addSection("searchCriteria", "Site Search", null, null, true);
        
        // Add address input subsection
        _addSection("searchAddress", "Address", _getCriteria("Address"), "searchCriteria", true, 1);
        _getStates();        
        
        // Add tower input subsection
        _addSection("searchTower", "Site Name / Site Number", _getCriteria("Name"), "searchCriteria", false, 1);
        
        // Add coordinate input subsection
        _addSection("searchCoordinates", "Coordinates", _getCriteria("Coordinates"), "searchCriteria", false, 1);
        _addSection("searchCodes", "Geographic Location", _getCriteria("Code"), "searchCriteria", false, 1);
        
        loadingbox.Show();
        _getMSARSAs();
        _getMTAs();
        _getBTAs();
        loadingbox.Hide();
        
        // Add the main filter filter section to the panel.
        _addSection("searchFilter", "Filter", null, null, true);
        _addSection("searchFilterRadius", "Radius", _getCriteria("Filter_Radius"), "searchFilter", true, 1);
        _addSection("searchFilterLocType", "Location Type", _getCriteria("Filter_LocType"), "searchFilter", true, 1);
        
        
        // Add blurb to explain how to get driving directions.
        var directionsContent = "<div id='ddContainer' class='ve_dd_container'>%1</div>".replace("%1", dd_initial_message);
        _addSection("directions", "Directions", directionsContent, null, false);
        
        // Show the panel.
        _show();
    }

    
    // Shows the panel, adjusts VE NAV control.
    function _show(){
        _panel.style.display = "block";
        _panelControl.style.left = _panelControlLeft + "px";
        if (map != null){
        //Move Dashboard. How?
            //map.dashboardX = _panel.offsetTop;
           // map.dashboardY = _panelControl.offsetLeft + _panelControl.offsetWidth + 5;
           
           //map.VE.HideDashboard();
           
           //reposition the map
           //map.resize();
           //not in v 6.1
            //map.Dashboard.style.top = _panel.offsetTop + "px";
            //map.Dashboard.style.left = _panelControl.offsetLeft + _panelControl.offsetWidth + 5 + "px";
         }        
        _panelControlData.innerHTML = "<a href='javascript:panel.Hide();'>&lt;<br/>&lt;<br/>&lt;</a>";
        
        if (legend != null){
            legend.Reposition(_panel.offsetTop + panel.GetElement().clientHeight - legend.GetElement().clientHeight - 4 - 10, _panelControl.offsetLeft + _panelControl.offsetWidth + 5);
        }
    }
    
    // Hides the panel, adjusts VE NAV control.
    function _hide(){
        //_panel.style.display = "none";
        //_panelControl.style.left = _panel.offsetLeft + "px";
        
        //v 6.1 Doesn't support dashboard modifications
        //map.Dashboard.style.left = _panelControl.offsetLeft + _panelControl.offsetWidth + 5 + "px";
        //reposition the map      
        
        //_panelControlData.innerHTML = "<a href='javascript:panel.Show();'>&gt;<br/>&gt;<br/>&gt;</a>";
        //legend.Reposition(panel.GetElement().offsetTop + panel.GetElement().clientHeight - legend.GetElement().clientHeight - 4 - 10, _panelControl.offsetLeft + _panelControl.offsetWidth + 5);
    }
    
    // Adds a section to the panel, automatically applying styling, size and position.
    function _addSection(sectionName, headerData, contentData, parent, showOnLoad, styleClassIndex){
        
        // Create section shell and attach it to panel.
        var section = document.createElement('div');
        section.id = _panel.id + "_" + sectionName;
        section.margin = "0px 0px 0px 0px";
            
        // If a child style was not specified, assume root style.
        if (styleClassIndex == null){
            styleClassIndex = 0;
        }

        // Create a header container (for multi browser positioning compatibility)
        var headerContainer = document.createElement('div');
        headerContainer.id = _panel.id + "_" + sectionName + "_header_container";
        headerContainer.className = "ve_panel_header_container" + styleClassIndex;
        headerContainer.style.width = (_panelWidth) + "px";
        section.appendChild(headerContainer);
            
        // Assuming header data (ie title text), add it to the section.
        if (headerData != null){   

            // Create the header data subsection.
            var header = document.createElement('div');
            header.id = _panel.id + "_" + sectionName + "_header";
            header.className = "ve_panel_header" + styleClassIndex;
            
            // Create Show/Hide functionality (wrap link around title)            
            var headerLink = "<a href='javascript:panel.ShowSection(\"" + sectionName + "\");'>";

            if (showOnLoad){
                headerLink += "<div style='width:10px; float:left;'> - </div>";
            }
            else{
                headerLink += "<div style='width:10px; float:left;'> + </div>";
            }
            headerLink += headerData + "</a>";
            header.innerHTML = headerLink;
            headerContainer.appendChild(header);
        }

        // Create a data container (for multi browser positioning compatibility
        var dataContainer = document.createElement('div');
        dataContainer.id = _panel.id + "_" + sectionName + "_data_container";
        dataContainer.className = "ve_panel_data_container" + styleClassIndex;
        dataContainer.style.width = (_panelWidth - _scrollBarWidth) + "px";

        // Show data subsection if specified        
        if (showOnLoad){
            dataContainer.style.display = "block";
        }
        else{
            dataContainer.style.display = "none";
        }
        
        section.appendChild(dataContainer);

        // If data was passed, add it to the section.
        if (contentData != null){
            
            // Create the data subsection
            var data = document.createElement('div');
            data.id = _panel.id + "_" + sectionName + "_data";
            data.className = "ve_panel_data" + styleClassIndex;
            data.innerHTML = contentData;
            dataContainer.appendChild(data);
        }
        
        // Determine if we're adding the current section to another or the root (panel itself).
        if (parent == null){
            _panel.appendChild(section);
        }
        else{
            // Get a handle to the parent section and put it back into the parent variable.
            parent = document.getElementById(_panel.id + "_" + parent);
            
            // Section contains a parent section, append to parent's data container (childNode 1)
            parent.childNodes[1].appendChild(section);
        }
        
        // Keep track of new section for collapsing/expanding.
        _sections.push(section);        
    }
    
    function _removeSection(){
    }
    
    // Shows or hides a section of the search panel.
    function _showSection(sectionId, forceShow){
        if (forceShow == null){
            forceShow = false;
        }
        
        // check if we already have a valid section from showSectionOnly() that we are to show.
        var section = document.getElementById(_panel.id + "_" + sectionId);
        if (!isValid(section)){
            return;
        }
        
        // Grab the requested section's link.        
        var sectionDisplay = section.childNodes[1].style.display;
        var headerLink = section.childNodes[0].getElementsByTagName("a")[0];
        
        // If the section is visible, hide it, if hidden then show it, update the link in both cases.
        if ((sectionDisplay == "block") && (!forceShow)){
            section.childNodes[1].style.display = "none";
            headerLink.innerHTML = headerLink.innerHTML.replace("-", "+");
        }
        else{
            section.childNodes[1].style.display = "block";
            headerLink.innerHTML = headerLink.innerHTML.replace("+", "-");
        }
        
        // Remove route and any textual directions.
        if (sectionId != "directions"){
            document.getElementById("ddContainer").innerHTML = dd_initial_message;
            try{
                map.VE.DeleteRoute();
            }
            catch(err){}
        }
    }
    
    // Show the given section in the search panel and collapses all sibling panels.
    // Useful for situations such as when directions are shown.
    function _showSectionOnly(sectionId, forceShow){
        
        // Validate entered section.
        var section = document.getElementById(_panel.id + "_" + sectionId);
        if (!isValid(section)){
            return;
        }

        // Traverse known sections...
        for (var i = 0; i < _sections.length; i++){
        
            //Determine current section's parent.
            var curParent = _sections[i].parentNode;
            
            // When collapsing sibling, make sure to exclude self and self's parent.
            if ((_sections[i].parentNode.id == section.parentNode.id) && (_sections[i].id != section.id)){
                
                // Adjust link to reflect current section status. 
                var newLinkTitle = _sections[i].childNodes[0].getElementsByTagName("a")[0].innerHTML.replace("-", "+");
                _sections[i].childNodes[0].getElementsByTagName("a")[0].innerHTML = newLinkTitle;
                
                // collapse current section.
                _sections[i].childNodes[1].style.display = "none";
            }
        }
        _showSection(sectionId, forceShow);        
    }
    
    // Minimizes a section
    function _hideSection(section){
    }
    
    
    // Returns the object's DOM equivalent for style and position manipulation.
    function _getElement(){
        return document.getElementById(_panel.id);
    }
    
    // Resizes the panel based on input parameters.
    function _resize(height, width){
        if ((height != null) && (height > 0)){
            _panel.style.height = height + "px";
            _panelControl.style.height = height + "px";
            
        }
        if ((width != null) && (width > 0)){
            _panel.style.width = width + "px";
        }
    }
    
    // Creates and returns various input sections of the search panel.
    function _getCriteria(section){
        var data = "";
        
        if (section == "Address"){
            data += "<form action = 'javascript:panel.FindByAddress()' method='GET'>" +
            
                    "<table class='inputTable'>" +
                    "<tr><td class='inputStreet'>Street</td><td><input type='text' id='txtStreet' style='width: 115px;'/></td></tr>" +
                    "<tr><td class='inputCity'>City</td><td><input type='text' id='txtCity' style='width: 115px;'/></td></tr>" +
                    //"<tr><td class='inputState'>State</td><td><input type='text' id='txtState' size='14'/></td></tr>" +
                    "<tr><td class='inputState'>State</td><td><select id='cmbState' style='width: 121px;'/></select></td></tr>" +
                    "<tr><td class='inputZip'>Zip</td><td><input type='text' id='txtZip' style='width: 115px;'/></td></tr>" +
            
                    "<tr><td colspan='2'><div class='inputSearch' style='text-align:right'><input type='submit' value='Search'/></div></td></tr>";
        }
        else if (section == "Name"){
            data += "<form action = 'javascript:panel.FindByName()' method='GET'>" +
            
                    "<table class='inputTable'>" +
                    "<tr><td class='inputName'>Name</td><td><input type='text' id='txtTowerName' style='width: 115px;'/></td></tr>" +
                    "<tr><td class='inputNumber'>Number</td><td><input type='text' id='txtTowerNum' style='width: 115px;'/></td></tr>" +
            
                    "<tr><td colspan='2'><div class='inputSearch' style='text-align:right'><input type='submit' value='Search'/></td></tr>";            
        }
        
        else if (section == "Coordinates"){
            data += "<form action = 'javascript:panel.FindByCoordinates()' method='GET'>" +
        
                    "<table class='inputTable'>" +
                        "<tr><td class='inputName'>Latitude</td><td>" +
                            "<input type='text' id='txtLatDeg' style='width:25px;'/> " + 
                            "<input type='text' id='txtLatMin' style='width:25px;'/> " + 
                            "<input type='text' id='txtLatSec' style='width:25px;'/> " + 
                            "<input type='text' id='txtLatDir' value = 'N' style='width:15px;'/>" + 
                        "</td></tr>" +
                        "<tr><td class='inputNumber'>Longitude</td><td>" + 
                            "<input type='text' id='txtLonDeg' style='width:25px'/> " +
                            "<input type='text' id='txtLonMin' style='width:25px;'/> " + 
                            "<input type='text' id='txtLonSec' style='width:25px;'/> " + 
                            "<input type='text' id='txtLonDir' value = 'W' style='width:15px'/>" +

                        "</td></tr>" +
            
                    "<tr><td colspan='2'><div class='inputSearch' style='text-align:right'><input type='submit' value='Search'/></div></td>";
        }
        else if (section == "Code"){
            data += "<form action = 'javascript:panel.FindByCode()' method='GET'>" +
        
                    "<table class='inputTable'>" + 
                    
                    "<tr><td class='inputMSA'>MSA/RSA</td><td><select id='cmbMSARSA' onchange='javascript:panel.ClearCodes(this.id);' style='width: 121px;'/></td></tr>" +
                    "<tr><td class='inputMTA'>MTA</td><td><select id='cmbMTA' onchange='javascript:panel.ClearCodes(this.id);' style='width: 121px;'/></td></tr>" +
                    "<tr><td class='inputBTA'>BTA</td><td><select id='cmbBTA' onchange='javascript:panel.ClearCodes(this.id);' style='width: 121px;'/></td></tr>" +                    
                    
                    "<tr><td class='inputFAA'>FAA</td><td><input type='text' id='txtFAA' onfocus='javascript:panel.ClearCodes(this.id);' style='width: 115px;'/></td></tr>" +
                    "<tr><td class='inputFCC'>FCC</td><td><input type='text' id='txtFCC' onfocus='javascript:panel.ClearCodes(this.id);' style='width: 115px;'/></td></tr>" +
                
                    "<tr><td colspan='2'><div class='inputSearch' style='text-align:right'><input type='submit' value='Search'/></div></td></tr>";
        }
        else if (section == "Filter_Radius"){
        
            data += "<table class='inputTable'>" +
                    "<tr><td><div id='inputRadius' class='inputRadius'>" + 
                    "<input type='radio' name='radioRadius' value='" + radius1 + "' checked onclick='javascript:panel.OnRadiusChange()'>" + radius1 + " miles</input><br/>" + 
                    "<input type='radio' name='radioRadius' value='" + radius2 + "' onclick='javascript:panel.OnRadiusChange()'>" + radius2 + " miles</input><br/>" + 
                    "<input type='radio' name='radioRadius' value='" + radius3 + "' onclick='javascript:panel.OnRadiusChange()'>" + radius3 + " miles</input></div></td></tr>";

        }
        
        else if (section == "Filter_LocType"){
        
            data += "<table class='inputTable'>" +
                    "<tr><td colspan='2' class='inputWireless'><input type='checkbox' id='chkWireless' size='14' checked onclick='javascript:panel.OnFilterChange()'/> Wireless</td></tr>" + 
                    "<tr><td colspan='2' class='inputBroadcast'><input type='checkbox' id='chkBroadcast' size='14' checked onclick='javascript:panel.OnFilterChange()'/> Broadcast</td></tr>" + 
                    "<tr><td colspan='2' class='inputInBuilding'><input type='checkbox' id='chkBuilding' size='14' checked onclick='javascript:panel.OnFilterChange()'/> In Building</td></tr>" + 
                    "<tr><td colspan='2' class='inputManaged'><input type='checkbox' id='chkManaged' size='14' checked onclick='javascript:panel.OnFilterChange()'/> Managed</td></tr>" +
                    "<tr><td colspan='2' class='inputRooftop'><input type='checkbox' id='chkRooftop' size='14' checked onclick='javascript:panel.OnFilterChange()'/> Rooftop</td></tr>" +
                    "<tr><td colspan='2' class='inputUnknown'><input type='checkbox' id='chkUnknown' size='14' checked onclick='javascript:panel.OnFilterChange()'/> Unknown</td></tr>";
        }
        
        data += "</table></form>";
        
        return data;
    }
    
    /* Populate combo boxes */
    
    function _getStates(){
        dal.GetStates(_onGetStates);
    }
    
    function _onGetStates(response){
        _onGetComboData("cmbState", response);
    }
    
    function _getMSARSAs(){
        dal.GetMSARSAs(_onGetMSARSAs);
    }
    
    function _onGetMSARSAs(response){
        _onGetComboData(FormInputMSARSA, response);
    }
    
    function _getMTAs(){
        dal.GetMTAs(_onGetMTAs);
    }
    
    function _onGetMTAs(response){
        _onGetComboData(FormInputMTA, response);
    }
    
    function _getBTAs(){
        dal.GetBTAs(_onGetBTAs);
    }
    
    function _onGetBTAs(response){
        _onGetComboData(FormInputBTA, response);
    }
    
    function _onGetComboData(comboID, response){
        var comboBox = document.getElementById(comboID);
        comboBox.options[0] = new Option("","");
        var options = response.split("|");
        for (var i = 0; i < options.length; i++){
            var optionPair = options[i].split(":");
            comboBox.options[i+1] = new Option(optionPair[0], optionPair[1]);
        }
    }    
    
    /* End populate combo boxes */
    
    
    // Gets the currently selected filter options from the search panel.
    function _getFilter(){
    
        var locationType = Array(FormInputWireless, FormInputBroadcast, FormInputBuilding, FormInputManaged, FormInputRooftop, FormInputUnknown);
        
        return dal.GetFilter(locationType);
    }
    
    // Enables or disables the filter.
    function _enableFilter(isEnabled){
        //var locationType
        var radiusDiv = document.getElementById('inputRadius');
        var radiusElements = radiusDiv.getElementsByTagName('input');
        for (var i = 0; i < radiusElements.length; i++){
            radiusElements[i].disabled = !isEnabled;
        }
        
        var typeElements = Array(FormInputWireless, FormInputBroadcast, FormInputBuilding, FormInputManaged, FormInputRooftop, FormInputUnknown);        
        for (var i = 0; i < typeElements.length; i++){
            var element = document.getElementById(typeElements[i]);
            element.disabled = !isEnabled;
        }
    }
    
    
    // Gets the currently selected radius from the search panel
    function _getRadius(){
        var radiusValue = 0;
        
        if (isValid(FORCED_ZIP_RADIUS) && (_zipOnlySearch) && (FORCED_ZIP_RADIUS > 0)) {
            radiusValue = FORCED_ZIP_RADIUS;
        }
        else{
            var radiusRadios = document.getElementById('inputRadius').getElementsByTagName('input');
            
            for (var i = 0; i < radiusRadios.length; i++){
                if (radiusRadios[i].checked == true){
                    radiusValue = radiusRadios[i].value;
                    break;
                }
            }
        }
        return radiusValue;    
    }
    
    // Uses the data access layer to find an address.
    function _findByAddress(){

        // Change view back to road if we are in BE.    
        if (map.VE.GetMapStyle() == VEMapStyle.Birdseye || map.VE.GetMapStyle() == VEMapStyle.BirdseyeHybrid){
	        map.VE.SetMapStyle(VEMapStyle.Road);
        }    
    
        // Stop any find by extents events (due to VE moving to first result coordinates on it's own.
        map.DetachAutoFind();
        
        // Set this function as the last one called (for filter changes)
        _lastCall = _findByAddress;
        
        // Get user-entered address information.
        var street = document.getElementById('txtStreet').value;
        var city = document.getElementById('txtCity').value;
        var stateCombo = document.getElementById('cmbState');
        var zip = document.getElementById('txtZip').value;
        
        var comboValues = null;
        // 0 is name, 1 is lat, 2 is lon.
        comboValues  = stateCombo.value.split(comboValueDelimiter);
        //var state = comboValues[0];
        //if (state.length < 1){
        var state = "Empty";
            if (stateCombo.selectedIndex >= 0){
                state = stateCombo.options[stateCombo.selectedIndex].text;
            }
       
        _zipOnlySearch = false;

        // no input
        if ((street.length < 1) && (city.length < 1) && (state.length < 1) && (zip.length < 1)){
            loadingbox.ShowAndStay("Please enter an address.", false, 2000);
            return;
        }
        
        // state only search - use coordinates from DB
        else if ((street.length < 1) && (city.length < 1) && (zip.length < 1) && (state.length > 0)){
        
            _stateOnlySearch = true;
            
            // If the given state has coordinates (as part of value), use them to avoid disambiguation dialogue.
            if ( (comboValues != null) && (parseFloat(comboValues[1]) != 0.0) && (parseFloat(comboValues[2]) != 0.0) ) {

                var VECoords = new VELatLong(parseFloat(comboValues[1]), parseFloat(comboValues[2]));
                try{
                var result = new VEFindResult(state, state, "", VECoords, false, "");
                }catch (err){
                     //alert(err.message)
                    }
                    
                _onFindAddress(new Array(result));
                
                return;
            }
        }
        
        //**** ZIP only search ****
        else if ((street.length < 1) && (city.length < 1) && (state.length < 1) && (zip.length > 0)){
            _zipOnlySearch = true;
            
            // changed v 6.1
            //dal.FindAddress(zip, _onFindAddress);
            
            try{
                map.VE.Find(null, zip,  null, null, null, null, false, true, true, true,  _callbackFindByWhere);                              
            return;
            }catch(err){
                //alert(err.Description);
            }
        }
        
        // Create VE search string.
        address = "";
        
        if (isValid(street)){
            address += street + ", ";
        }
        if (isValid(city)){
            address += city + ", ";
        }
        if (isValid(state)){
            address += state + ", ";
        }
        if (isValid(zip)){
            address += zip;
        }
        
        // Try to restrict search to USA.
        //address += ", USA";
    
        //Attempt to perform a textual search.
        address = reformatString(address);
        
        
        //dal.FindAddress(address, _onFindAddress);
        
        //v 6.1 Find by Where
        try{
            map.VE.Find(null,address,null, null, null,null,false,true,true,true,_callbackFindByWhere);
         }catch(err){//alert(err.Description);
            }
        
    }
        
    function _findByAddressDisambig(resultsIndex){
    
        ambigbox.ForceHide();
        
        _lastCall = _findByAddressDisambig;
        
        if (typeof resultsIndex == 'undefined'){
            resultsIndex = _lastResultsIndex;
        }
        else{
            _lastResultsIndex = resultsIndex;
        }
        
        if (resultsIndex == null){
            map.SetCenterAndZoom(_lastResults[0], map.VE.GetZoomLevel());
            _onFindAddress(new Array(_lastResults[0]));
        }
        else{
            map.SetCenterAndZoom(_lastResults[resultsIndex], map.VE.GetZoomLevel());
            _onFindAddress(new Array(_lastResults[resultsIndex]));
           
        }
    }



   //Call back function for Find by WHERE (ZIP,Address) Method (v 6.1)  
   function _callbackFindByWhere(layer, resultsArray, places, hasMore, veErrorMessage){
    
    map.DetachAutoFind();
    
    if (places[0].LatLong == null){
         loadingbox.ShowAndStay("Error determining your address", false, 2000);
            return;
    }        
    // Show disambiguation panel if there are more than one results but we've disambiguated already before (filter change).
    else if ((places.length > 1) && (!_filterChange)){
            _lastResults = places;
            
            var resultsMessage = "<b>More than one location was returned. Please select the location you were looking for:</b><br/><br/>";
            for (x=0; x<places.length; x++){
                var mcVal = places[x].MatchCode; 
                //only care about "Good" MatchCode new in v 6.1
                if (mcVal == VEMatchCode.Good){
                resultsMessage += "<a href='javascript:panel.FindByAddressDisambig(\"" + x + "\");'>" + places[x].Name + "</a><br>";
                }
            }
            resultsMessage += "<br/>";
            ambigbox.ShowAndStay(resultsMessage, false, null, "Close");
        }
     // Proceed with finding towers near the current geocoded result.
    else{
        // Apply location filter.
        var filter = _getFilter();
        var x = places[0].LatLong.Latitude
        var y = places[0].LatLong.Longitude
        var latLong = new VELatLong(x,y)
            
        map.VE.SetCenter(latLong);
        
        // Apply nearby radius filter.
        var radius = _getRadius();

        // Find nearby towers with the given filter / search criteria.
        dal.FindNearby(latLong, radius, filter, MAX_RESULTS, _onFindNearby);   
    }
   
   }     
        
    // Callback to perform functions when an address result is returned.
    function _onFindAddress(results){

        // Stop any find by extents events (due to VE moving to first result coordinates on it's own.
        map.DetachAutoFind();


        // Validate found results.
        if (results == null){
        
            loadingbox.ShowAndStay("Error determining your address", false, 2000);
            return;
        }      
        
        // v 6.1 Has Disambiguation Panel.
        // Show disambiguation panel if there are more than one results but we've disambiguated already before (filter change).
        //shouldn't be a case!
        else if ((results.length > 1) && (!_filterChange)){
            _lastResults = results;
            
            var resultsMessage = "<b>More than one location was returned. Please select the location you were looking for:</b><br/><br/>";
            for (x=0; x<results.length; x++){
                
                    resultsMessage += "<a href='javascript:panel.FindByAddressDisambig(\"" + x + "\");'>" + results[x].Name + "</a><br>";
                
            }
            resultsMessage += "<br/>";
            ambigbox.ShowAndStay(resultsMessage, false, null, "Close");
        }
        
        // Proceed with finding towers near the current geocoded result.
        else{          
            
             // Apply location filter.
            var filter = _getFilter();
            
            
            //**** State Only Search *******         
            if (_stateOnlySearch){
                _stateOnlySearch = false;
                
                //changed with v 6.1
                var x = results[0].Description.Latitude
                var y = results[0].Description.Longitude
                var latLong = new VELatLong(x,y)
            
                map.VE.SetCenter(latLong);
             
             
                // Find all towers within a state.         

                //dal.FindByState(results[0].Name, filter, MAX_RESULTS, _onFindNearby);
                
                // v6.1 is it FindType??
                dal.FindByState(results[0].FindType, filter, MAX_RESULTS, _onFindNearby);
            
            
            }
            else{

                map.VE.SetCenter(results[0].LatLong);            
             
                // Apply nearby radius filter.
                var radius = _getRadius();

                // Find nearby towers with the given filter / search criteria.
                dal.FindNearby(results[0].LatLong, radius, filter, MAX_RESULTS, _onFindNearby);
                
                //changed in v 6.1
                //dal.FindNearby(latLong, radius, filter, MAX_RESULTS, _onFindNearby);
            }
        }
    }

    // Callback to perform functions when the nearby results are returned.        
    function _onFindNearby(response){
        _onFoundResults(response);
    }
    
    // Finds locations by Tower name or number
    function _findByName(){
        
        //_clearMap();
        
        // Change view back to road if we are in BE.    
        if (map.VE.GetMapStyle() == VEMapStyle.Birdseye || map.VE.GetMapStyle() == VEMapStyle.BirdseyeHybrid){
	        map.VE.SetMapStyle(VEMapStyle.Road);
        }    
        
        _lastCall = _findByName;    
    
        // Fetch user criteria from panel.
        var towerName = document.getElementById('txtTowerName').value;
        var towerNumber = document.getElementById('txtTowerNum').value;
        
        // Validate values.
        if ((towerName.length < 1) && (towerNumber.length < 1)){
            loadingbox.ShowAndStay("Please enter a site name or number", false, 2000);
            return;
        }

        var filter = _getFilter();
        dal.FindByName(towerName, towerNumber, filter, MAX_RESULTS, _onFindByName);
    }
    
    // Callback for FindByName - calls the event which processes the results.
    function _onFindByName(response){
        _onFoundResults(response);
    }
    
    // Find locations by coordinates.
    function _findByCoordinates(){

        // Change view back to road if we are in BE.    
        if (map.VE.GetMapStyle() == VEMapStyle.Birdseye || map.VE.GetMapStyle() == VEMapStyle.BirdseyeHybrid){
	        map.VE.SetMapStyle(VEMapStyle.Road);
        }    
    
        _zipOnlySearch = false;
    
        // Set this function as the last one called (for filter changes)
        _lastCall = _findByCoordinates;    
    
        // Set the input fields to check in the panel.
        var inputFields = new Array('txtLatDir', 'txtLatDeg', 'txtLatMin', 'txtLatSec', 'txtLonDir', 'txtLonDeg', 'txtLonMin', 'txtLonSec');
        var inputValues = new Array();
        
        var allEntered = true;

        // Fetch their values.
        for (var i = 0; i < inputFields.length; i++){
            inputValues[i] = document.getElementById(inputFields[i]).value;
            if (inputValues[i].length < 1){
                allEntered = false;
                break;
            }
        }
        
        // Validate the coordinates
        if (!allEntered){
            loadingbox.ShowAndStay("Please enter all coordinate values.", false, 2000);
            return;
        }
        
        // Convert entered degrees into decimal degrees.
        //input values format: [0/4]: Lat/Lon Direction, [1/5]: Lat/Lon Degree, [2/6]: Lat/Lon Minute, [3/7] Lat/Lon Second. 
        var latitude = convertToDecimalDegrees(inputValues[0], inputValues[1], inputValues[2], inputValues[3]);
        var longitude = convertToDecimalDegrees(inputValues[4], inputValues[5], inputValues[6], inputValues[7]);
        if (latitude == null || longitude == null){
            loadingbox.ShowAndStay("Could not parse coordinates.", false, 2000);
            return;
        }
        
        var VECoordinates = new VELatLong(latitude, longitude);
        
        // Use built in VE function to center on coordinates.
        map.VE.SetCenterAndZoom(VECoordinates, map.VE.GetZoomLevel());
        
        // Apply Radius
        var radius = _getRadius();
        
        // Apply the search filter.
        var filter = _getFilter();
        
        // Perform the actual find by coordinates search        
        dal.FindNearby(VECoordinates, radius, filter, MAX_RESULTS, _onFindByCoords)
    }
    
    // Callback event for FindByCoords - calls the event which processes the results.
    function _onFindByCoords(response){
        _onFoundResults(response);
    }
    
    // Finds locations by the AT Tower Code.
    function _findByCode(){

        // Change view back to road if we are in BE.    
        if (map.VE.GetMapStyle() == VEMapStyle.Birdseye || map.VE.GetMapStyle() == VEMapStyle.BirdseyeHybrid){
	        map.VE.SetMapStyle(VEMapStyle.Road);
        }    
    
        _lastCall = _findByCode;    
    
        // Set the input fields to check in the panel.
        var inputFields = new Array(FormInputMSARSA, FormInputMTA, FormInputBTA, FormInputFAA, FormInputFCC);
        var parameterFields = new Array(SQLMSARSAField, SQLMTAField , SQLBTAField, SQLFAAField, SQLFCCField);
        var curValue = null;
        
        var codeName = null;
        var codeValue = null;
        
        // determine which code the user entered and fetch it.
        for (var i = 0; i < inputFields.length; i++){
            curValue = document.getElementById(inputFields[i]).value;
            if (curValue.length > 0){
                codeName = parameterFields[i];
                codeValue = curValue;
                break;
            }
        }
        
        // Validate the fetched code.
        if ((codeName == null) || (codeValue == null)){
            loadingbox.ShowAndStay("Please enter / select a code", false, 2000);
            return;
        }
        
        var comboValues = null;
        // 0 is value, 1 is lat, 2 is lon.
        comboValues  = codeValue.split(comboValueDelimiter);
        
        if ((comboValues != null) && (comboValues.length > 1)){
        
            // If the given state has coordinates (as part of value), use them otherwise find and use first result.
            if ( (parseFloat(comboValues[1]) != 0.0) && (parseFloat(comboValues[2]) != 0.0) ) {
                try{
                    var VECoords = new VELatLong(parseFloat(comboValues[1]), parseFloat(comboValues[2]));
                    map.VE.SetCenterAndZoom(VECoords, map.VE.GetZoomLevel());
                }
                catch(err){
                }
            }
            else{
                _findByCodeData["codeName"] = codeName;
                _findByCodeData["codeValue"] = comboValues[0];
                _findByCodeData["callback"] = _onFindByCode;
                
                dal.FindAddress(comboValues[0], _onFindByCodeIntermediary);
                return;
            }
        }
            
        // Set search filter
        var filter = _getFilter();
        
        // Perform the actual find by code.
        dal.FindByCode(codeName, comboValues[0], filter, MAX_RESULTS, _onFindByCode);
    }
    
    // Continues a find by code when the coordinates for the code could not be determined and it had to be looked up.
    function _onFindByCodeIntermediary(results){
    
        if (results != null){
            if (results.length > 0){
            
            //changed in v 6.1
            // map.VE.SetCenterAndZoom(results[0].LatLong, map.VE.GetZoomLevel());
                var x = results[0].Description.Latitude
                var y = results[0].Description.Longitude
                var latLong = new VELatLong(x,y)
                
                map.VE.SetCenterAndZoom(latLong, map.VE.GetZoomLevel());
                 
                 
            }
        }
        // Set search filter
        var filter = _getFilter();
        
        dal.FindByCode(
            _findByCodeData["codeName"],
            _findByCodeData["codeValue"],
            filter,
            MAX_RESULTS,            
            _findByCodeData["callback"]
        );
    }
    
    // Clears all selected Tower codes except the one selected.
    function _onClearCodes(leaveCode){
        var inputFields = new Array(FormInputMSARSA, FormInputMTA, FormInputBTA, FormInputFAA, FormInputFCC);
        for (var i = 0; i < inputFields.length; i++){
            if (inputFields[i] != leaveCode){
                if (inputFields[i].indexOf("cmb") != -1){
                    document.getElementById(inputFields[i]).selectedIndex = 0;
                }
                else if (inputFields[i].indexOf("txt") != -1){
                    document.getElementById(inputFields[i]).value = "";
                }
            }
        }
    }    
    
    // Callback event for FindByCode - calls the event which processes the results.
    function _onFindByCode(response){
        _onFoundResults(response);
    }
    
    // Finds locations within the given map area taking the search filter into account.
    function _findByExtents(){
    
    var topLeft = null;
    var bottomRight = null;
    
    _lastCall = _findByExtents;
    
        if (map.VE.GetMapStyle() == VEMapStyle.Birdseye || map.VE.GetMapStyle() == VEMapStyle.BirdseyeHybrid) {
            //Birdseye or Oblique Map Style 
            //Get the BirdseyeScene being displayed
            var birdseyeScene = map.VE.GetBirdseyeScene(); 
            
            var rect = birdseyeScene.GetBoundingRectangle();

//            rect.BottomLeftLatLong = new VELatLong(rect.BottomRightLatLong.Latitude, rect.TopLeftLatLong.Longitude);
//            rect.TopRightLatLong = new VELatLong(rect.TopLeftLatLong.Latitude, rect.BottomRightLatLong.Longitude);

            topLeft = new VELatLong(rect.TopLeftLatLong.Latitude, rect.TopLeftLatLong.Longitude); ;
            
            bottomRight = new VELatLong(rect.BottomRightLatLong.Latitude, rect.BottomRightLatLong.Longitude);           
            
            
            map.AttachAutoFind();
            //return;
        } else{
            // Determine the lat/long coordinates of the current map area.
       	    topLeft = map.VE.PixelToLatLong(new VEPixel(_panelControl.offsetLeft + _panelControl.clientWidth, _panel.offsetTop));
    	    bottomRight = map.VE.PixelToLatLong(new VEPixel(map.GetElement().clientWidth, map.GetElement().clientHeight));
        }
        // Set search filter.
        var filter = _getFilter();

        // Perform the actual find by extents.
        dal.FindByExtents(topLeft.Latitude, topLeft.Longitude, bottomRight.Latitude, bottomRight.Longitude, filter, MAX_RESULTS, _onFindByExtents);
    }
    
    
    // Callback event for FindByExtents - calls the event which processes the results.
    function _onFindByExtents(response){
        _onFoundResults(response, false);
    }
    
    
    // Manually invoke event used after every search to inspect, plot and display results.
    function _onFoundResults(response, setBestMapView){
        map.DetachAutoFind();
        try{
            // Attempt to execute the server's response of results (Should be javascript which builds a locations array)
            eval(response);
            
            // Validate the results.
            if (!isValid(locations) || (locations.length < 1)){
            
                // If we didn't find any locations within the given radius, try within map area.
                if (_callback == _onFindNearby){
                    //infobox.ShowAndStay("No locations found given your criteria, expanding to map area.", true, 5000);
                    _findByExtents();
                }
                
                // otherwise show that no locations were found.
                else{
                    infobox.ShowAndStay("No locations found given your criteria.", true, 2000);
                    map.Clear();
                    panel_results.Reset();
                }
            }
            else{
                if (!isValid(setBestMapView)){
                    setBestMapView = true;
                }
                
                // Show the user we have found more than the maximum displayable results.
                if (locations.length > MAX_RESULTS - 1){
                    infobox.ShowAndStay("More than " + (MAX_RESULTS - 1) + " results found. Please zoom in to narrow the search.", null, 10000);
                    map.Clear();
                    panel_results.Reset();
                }
                else{
                    infobox.ForceHide();
                
                    // Show the results on the map.
                    map.Plot(locations, setBestMapView);
                    
                    // Show the results in the search panel.
                    panel_results.ShowResults(locations, 1);
                }        
            }
        }
        catch(err){            
            loadingbox.ShowAndStay("Error retrieving results. " + err.description, false, 2000);
        }
        
        loadingbox.Hide();
        map.AttachAutoFind();
        _filterChange = false;
    }    
    
    // Event which fires if the search filter is changed.
    function _onFilterChange(){
        _filterChange = true;
        
        document.getElementById('chkManaged').checked = document.getElementById('chkWireless').checked;
        document.getElementById('chkUnknown').checked = document.getElementById('chkWireless').checked;
    
        // If we had a successful last [findByAddress/findByName/No/Coords/etc] call, fire it again.
        // The call will take care of determining the new find filter.
        if (isValid(_lastCall)){
            _lastCall();
        }
    }
    
    function _onRadiusChange(){
        if (isValid(_lastCall)){
            if ((_lastCall == _findByAddress) || (_lastCall == _findByCoordinates)){
                _onFilterChange();
            }
        }
    }
    
    function _clearMap(){
         map.VE.DeleteAllShapes();
         map.VE.DeleteRoute();
    }
    
    
    // Public accessors for described methods.
    
    this.Init = _init;
    this.Show = _show;
    this.Hide = _hide;
    this.GetElement = _getElement;
    this.Resize = _resize;
    
    this.AddSection = _addSection;
    this.RemoveSection = _removeSection;
    this.ShowSection = _showSection;
    this.ShowSectionOnly = _showSectionOnly;
    
    this.FindByAddress = _findByAddress;
    this.FindByAddressDisambig = _findByAddressDisambig;
    this.FindByName = _findByName;
    this.FindByCoordinates = _findByCoordinates;
    this.FindByCode = _findByCode;
    this.FindByExtents = _findByExtents;
    this.OnFilterChange = _onFilterChange;
    this.OnRadiusChange = _onRadiusChange;
    this.EnableFilter = _enableFilter;
    this.ExecuteLastSearch = _onFilterChange;
    this.ClearCodes = _onClearCodes;

}