    var Map = function(){
                var map, geocoder;
                var censusTracts,  countyTracts;
                var censusTract, counties;
                
                var selectedPolygon, selectedCounty;
                var cursorX, cursorY;
                var currentObj;
                var qualificationColor;

            	var	tooltip, tooltipTitle;	
            	var loadingDiv;	
                var searchMarker;
                var isExplanationOpen;
                var dialog;
                var timer;

                return {
                    init: function(){
                        map = new GMap2(document.getElementById("map_canvas"));
                        map.disableDoubleClickZoom();
                        map.addControl(new GSmallMapControl());
                        map.addControl(new GMapTypeControl());
                        map.addControl(new GOverviewMapControl());
                        map.setCenter(new GLatLng(41.178654, -95.800781), 4);

                        geocoder = new GClientGeocoder();
                        censusTract         = new CensusTracts();
                        censusTract.init();
                        counties                = new Counties();
                        counties.init();
                        
                         (document.getElementById('lstMapType').selectedIndex == 0)
                                                      ? currentObj = censusTract
                                                      : currentObj = counties;
                                     
                        tooltip =  Ext.get("divPolygonTTip");
                        tooltipTitle = Ext.get("divTTipTitle");
                        loadingDiv = Ext.get("divLoadIndicator");

                        Map.hideToolTip();
                        Map.hideLoadingIndicator();
                        Map.handleOverlayChoice();
                        document.getElementById('address').value = "Census Tract no. (or)  address";
					
                        GEvent.addListener(map, "moveend", function(){
                                  if (map.getZoom() >= currentObj.getZoomLevels().minZoom)    {
                                        Map.showLoadingIndicator();
                                       clearTimeout(timer);
                                       timer =  setTimeout("  Map.loadPolygons()    ", 1000);
                                    }
                        }
                           
                           );

                        GEvent.addListener(map, "zoomend", function(oldLevel, newLevel){
                           if(newLevel < oldLevel){
                               //zoomed out
                               if(newLevel >= currentObj.getZoomLevels().minZoom){
                                //    Map.loadPolygons();
                               }
                               else{
                                   alert("You have zoomed too far out. New information will not be loaded until you zoom back in.");
                               }
                           }
                        });


                       currentPolygons = new Ext.util.MixedCollection(false, function(obj){
                            return obj.id;
                        });
                    
                        //census tracts
                        censusTracts = new Ext.util.MixedCollection(false, function(obj){
                            return obj.id;
                        });

                        countyTracts = new Ext.util.MixedCollection(false, function(obj){
                            return obj.id;
                        });
                        //event listeners
                        Ext.get("search-btn").addListener("click", this.search, this);
                        Ext.get("lstMapType").addListener("change", this.handleOverlayChoice    , this);
                        Ext.get("chkcensus").addListener("click", this.handleCensusCheck    , this);
                        //map listener
                
                        GEvent.addListener(map, "mouseover", function(overlay, point){
                        });
                        
                      GEvent.addListener(map, "mouseout", function(overlay, point){
                          Map.hideToolTip();
                        });

                    },
                    
                    search: function(){
                        var address     = Ext.fly("address").getValue();
                        var searchURL   =  'findByFips.do';

                        if (!isNaN(address) && address.length > 10){
                            //try to find by fips
                            Ext.Ajax.request({
                                url: searchURL,
                                params: {fips: address},
                                callback: function(options, success, response){
                                    if (success){
                                        var obj = Ext.util.JSON.decode(response.responseText);

										var polygon = new GPolygon.fromEncoded({
                                            polylines: [
                                                {
                                                    points: obj.encodedPoints,
                                                    levels: obj.encodedLevels,
                                                    color: "#46433f",
                                                    opacity: 0.5,
                                                    shadow: false,
                                                    weight: 4,
                                                    numLevels: 18,
                                                    zoomFactor: 2
                                                }
                                            ]
                                        });

                                        var latlng = polygon.getBounds().getCenter();
                                        Map.zoomToLatLng(latlng, obj.id);
                                    }
                                    else {
                                        alert('Could not find Census Tract #');
                                    }
                                }
                            });
                        }
                        else{
                            Map. _findByAddress(address);
                        }
                    },

                  zoomToLatLng: function(latlng, polyId){
                      map.setCenter(latlng);
                      map.setZoom( currentObj.getZoomLevels().normalZoom);
						searchTractId = polyId;
//                      Map.loadPolygons(polyId);
                        if (searchMarker)
                        {
                            map.removeOverlay(searchMarker);
                        }
                  },
                  
                  addSearchMarker: function(latlng){
                      	searchMarker = new GMarker(latlng);
                         GEvent.addListener(searchMarker, "click", function(){
	                                       this.openInfoWindowHtml(Ext.fly("address").getValue());
	                                   });
                        map.addOverlay(searchMarker);
                        GEvent.trigger(searchMarker, "click");
                  },
                  
                    _findByAddress: function(address){
                           geocoder.getLatLng(address, function(latlng){
                                if(latlng)
                                {
                                    Map.zoomToLatLng(latlng);	
                                    Map. addSearchMarker(latlng);
                               }
                               else
                               {
                               		alert("Could not locate '" + address + "' ");
                               }
                            });
                    },
                    
                    loadPolygons: function(tractId, forceRefresh){
                        (document.getElementById('lstMapType').value == 'showcensus')
                                                      ? Map.loadCensusPolygons(tractId, forceRefresh) 
                                                      : Map.loadCountiesPolygons(tractId, forceRefresh);
                    },
                    
                    handleOverlayChoice: function(){
                        var choice = document.getElementById('lstMapType').value;
                         clearTimeout(timer);
                         map.clearOverlays();
                        switch(choice){
                            case 'showcensus':
                                 Ext.get('legend1').show();
                                 Ext.get('legend1Body').show();
                                 Ext.get('divExplain').show();
                                 Ext.get('legend2Body').update('QCT');
                                 Ext.get('legend3Body').update('QCT- Distressed');
                                 Ext.get('divLegendInfo').show();
                                 
                                 document.getElementById('chkcensus').disabled = false;

                                  if(map.getZoom() < 8){ return};
                                  map.setZoom( censusTract.getZoomLevels().normalZoom);
                                  currentObj = censusTract;
                                   Map.loadCensusPolygons('', true) ;
                                 break;
                             case 'showcounties':
                                  document.getElementById('chkcensus').disabled = true;
                                   Ext.get('divLegendInfo').hide();
                                  Ext.get('legend1').hide();
                                  Ext.get('legend1Body').hide();
                                  Ext.get('divExplain').hide();
                                  Ext.get('legend3Body').update('Non-Metro');
                                  Ext.get('legend2Body').update('Metro');
                                   if(map.getZoom() < 8){ return};
                                   currentObj =counties;
                                    map.setZoom( counties.getZoomLevels().normalZoom);
                                  Map.loadCountiesPolygons('', true);
                                break;
                        }
                        
                    },
                    
                    handleCensusCheck: function(){
                        if(document.getElementById('chkcensus').disabled == true) {return};
                        var choice = document.getElementById('chkcensus').checked;
                        switch(choice){
                            case true:
                                Map.showMetroCensus();
                                break;
                             case false:
                                Map.hideMetroCensus();
                                break;
                        }
                        
                    },
                    
                    showMetroCensus: function(){
                        var polygon;
                           Map.showLoadingIndicator();
                        censusTracts.each( function(polyObj){
                            polygon = polyObj.polygon;
                            // 0 => metro
                            switch(polyObj.metro){
                              case 0:
                                polygon.color   = "#FFFFFF";
                                polygon.opacity = 0.2;
                                break;
                             case 1:
                                polygon.color = polyObj.color;
                                polygon.opacity = 0.6;
                            }
                            polygon.redraw(true);
                        }, this);
                          Map.hideLoadingIndicator();
                    },
                    
                    hideMetroCensus: function(){
                            var polygon;
                               Map.showLoadingIndicator();
                        censusTracts.each( function(polyObj){
                            polygon = polyObj.polygon;
                                polygon.color = polyObj.color;
                                polygon.opacity = 0.3;
                            polygon.redraw(true);
                        }, this);
                          Map.hideLoadingIndicator();
                    },
                    
                    isHighlightMetro: function(){
                        if( document.getElementById('chkcensus').checked ){
                            return true;
                        }
                        else{
                            return false;
                        }
                    },
                    
                    loadCensusPolygons: function(censusTractId, forceRefresh){
                        var bounds = map.getBounds();
                        currentObj =censusTract;
                          Map.showLoadingIndicator();
                        var excludedIds = Array();
                        if(forceRefresh){
                            censusTracts.clear();
                        }
                       else {
                            censusTracts.eachKey(function(key, obj){
                                excludedIds.push(key);
                             });
                        }
                        dataLayerLoader       = currentObj.getLoader();
                        dataLayerLoader.load({
                            params: {
                                swLat: bounds.getSouthWest().lat(),
                                swLng: bounds.getSouthWest().lng(),
                                neLat: bounds.getNorthEast().lat(),
                                neLng: bounds.getNorthEast().lng(),
                                excludedIds: excludedIds
                            },
                            
                            callback: function(r, options, success){
                                //parse records
                                qualificationColor = currentObj.getQualifiedColors();   
                                if (success)
                                {
                                    for (var i = 0; i < r.length; i++)
                                    {
                                        var record = r[i];
                                        var tractId = record.get("id");
                                        var fips = record.get("fips");
                                        var encPoints = record.get("encodedPoints");
                                        var encLevels = record.get("encodedLevels");
                                        var metro = record.get("metro");

                                        if (!censusTracts.containsKey(tractId))
                                        {
                                            var color = qualificationColor[record.get("qualification")];
                                            var polygon = new GPolygon.fromEncoded({
                                                polylines: [
                                                    {
                                                        points: encPoints,
                                                        levels: encLevels,
                                                        color: "#46433f",
                                                        opacity: 0.5,
                                                        shadow: false,
                                                        weight: 4,
                                                        numLevels: 18,
                                                        zoomFactor: 2
                                                    }
                                                ],
                                                fill: true,
                                                color: color,
                                                opacity: 0.3,
                                                outline: true
                                            });
                                            
                                            if(Map.isHighlightMetro()){
                                                 // 0 => metro
                                                 switch(metro){
                                                     case 0:
                                                        polygon.color   = "#FFFFFF";
                                                        polygon.opacity = 0.2;
                                                        break;
                                                     case 1:
                                                        polygon.color = color;
                                                        polygon.opacity = 0.6;
                                                        break;
                                                 }
                                            }
                                           
                                            //vertices, "#46433f", 4, 0.5, color, 0.3
                                            censusTracts.add({
                                                id: tractId,
                                                fips: fips,
                                                color:color,
                                                metro:metro,
                                                polygon: polygon
                                            });
                                            
                                            map.addOverlay(polygon);   

//                                       //Dont have more than 200 census tracts  drawn at any time to save memory
//                                            if(censusTracts.getCount() > 175){
//                                                var firstCensus= censusTracts.first();
//                                                map.removeOverlay(firstCensus.polygon);
//                                                censusTracts.remove(firstCensus);
//                                            }
                                            
                                            /*Open information window when polygon is clicked */     
                                            GEvent.addListener(polygon, "click", function(latlng){
                                                var dataURL =  'data.do';
                                                
                                                censusTracts.each(function(obj)
                                                {
                                                    if (obj.polygon == this)
                                                    {
                                                        if (selectedPolygon)
                                                        {
                                                            selectedPolygon.opacity = 0.3;
                                                            selectedPolygon.redraw(true);
                                                        }

                                                        obj.polygon.opacity = 0.5;
                                                        obj.polygon.redraw(true);

                                                        selectedPolygon = this;

                                                        var polyLatLng = obj.polygon.getBounds().getCenter();
                                                        
                                                        var censustractId        = obj.id
                                            			Ext.Ajax.request( {
                                                             	 url: dataURL,
                                                               	 params: {id: censustractId},
                           	                                     callback: function(options, success, response)
                                                               	 {
                                                                        (success)  ?  Map.showInfoWindow(response.responseText, polyLatLng) : {};
                           	                                     }
                       	                            	});
                                                        return false;
                                                    }
                                                }, this);
                                            });
                                            
     	                                 GEvent.addListener(polygon, "mouseout", function(){
                                                 Map.hideToolTip();
                                           });
                                           
                                          GEvent.addListener(polygon, "mouseover", function(){
                                                censusTracts.each(function(obj)
                                                {
                                                    if (obj.polygon == this)
                                                    {

                                            			Map.showToolTip();
                                                        tooltipTitle.update("<b> Census tract- </b> " + obj.fips);
                                                        return false;
                                                    }
                                                }, this);
                                            });
										
                                        }
                                    }

                                    if (censusTractId)
                                    {
                                        var polygon = censusTracts.get(censusTractId).polygon;
                                        GEvent.trigger(polygon, "click");
                                    } 
									else if(searchTractId)
									{
										var polygon = censusTracts.get(searchTractId).polygon;
                                        GEvent.trigger(polygon, "click");
										searchTractId = null;
									}
                                }

                               Map.hideLoadingIndicator();
                            },
                            add: false
                        });
                    },
					

                    loadCountiesPolygons: function(countyTractId, forceRefresh){
                        var bounds = map.getBounds();
                           currentObj =counties;
                        var excludedIds = Array();
                         if(forceRefresh){
                             countyTracts.clear();
                          }
                        else {
                         countyTracts.eachKey(function(key, obj){
                               excludedIds.push(key);
                          });
                        }   
                        dataLayerLoader       = currentObj.getLoader();
                         Map.showLoadingIndicator();
                        dataLayerLoader.load({
                            params: {
                                swLat: bounds.getSouthWest().lat(),
                                swLng: bounds.getSouthWest().lng(),
                                neLat: bounds.getNorthEast().lat(),
                                neLng: bounds.getNorthEast().lng(),
                                excludedIds: excludedIds
                            },
                            
                            callback: function(r, options, success){
                                //parse records
                                 qualificationColor = currentObj.getQualifiedColors(); 
                                if (success)
                                {
                                    for (var i = 0; i < r.length; i++)
                                    {
                                        var record = r[i];
                                        var tractId = record.get("county_id");
                                        var countyName = record.get("county");
                                        var encPoints = record.get("encodedPoints");
                                        var encLevels = record.get("encodedLevels");
    
                                        if (!countyTracts.containsKey(tractId)){
                                            var color = qualificationColor[record.get("non_metro")];
                                            var polygon = new GPolygon.fromEncoded({
                                                polylines: [
                                                    {
                                                        points: encPoints,
                                                        levels: encLevels,
                                                        color: "#46433f",
                                                        opacity: 0.3,
                                                        shadow: false,
                                                        weight: 4,
                                                        numLevels: 18,
                                                        zoomFactor: 2
                                                    }
                                                ],
                                                fill: true,
                                                color: color,
                                                opacity: 0.3,
                                                outline: true
                                            });
                                            //vertices, "#46433f", 4, 0.5, color, 0.3
                                            countyTracts.add({
                                                id: tractId,
                                                cname:countyName,
                                                polygon: polygon
                                            });
                                            
                                            map.addOverlay(polygon);   
                                            
                                            //Dont have more than 175 counties drawn at any time to save memory
//                                            if(countyTracts.getCount() > 175){
//                                                var firstCounty = countyTracts.first();
//                                                map.removeOverlay(firstCounty.polygon);
//                                                countyTracts.remove(firstCounty);
//                                            }
                                            /*Open information window when polygon is clicked */     
                                            GEvent.addListener(polygon, "click", function(latlng){
                                                var dataURL =  'data.m';
                                                
                                                countyTracts.each(function(obj)
                                                {
                                                    if (obj.polygon == this)
                                                    {
                                                        if (selectedCounty)
                                                        {
                                                            selectedCounty.opacity = 0.3;
                                                            selectedCounty.redraw(true);
                                                        }

                                                        obj.polygon.opacity = 0.5;
                                                        obj.polygon.redraw(true);

                                                        selectedCounty = this;

                                                        var polyLatLng = obj.polygon.getBounds().getCenter();
                                                        var countyId     = obj.id;
                                            			Ext.Ajax.request( {
                                                             	 url: dataURL,
                                                               	 params: {id: countyId},
                           	                                     callback: function(options, success, response)
                                                               	 {
                                                                        (success)  ?  Map.showInfoWindow(response.responseText, polyLatLng) 
                                                                                         :  {};
                           	                                     }
                       	                            	});
                                                        return false;
                                                    }
                                                }, this);
                                            });
                                            
     	                                 GEvent.addListener(polygon, "mouseout", function(){
                                                 Map.hideToolTip();
                                           });
                                           
                                          GEvent.addListener(polygon, "mouseover", function(){
                                                countyTracts.each(function(obj)
                                                {
                                                    if (obj.polygon == this)
                                                    {

                                            			Map.showToolTip();
                                                        tooltipTitle.update("<b>County- </b> " + obj.cname);
                                                        return false;
                                                    }
                                                }, this);
                                            });
                                        }
                                    }
                                }

                               Map.hideLoadingIndicator();
                            },
                            add: false
                        });
                    },

                   clear: function(){
                       map.clearOverlays();
                       countyTracts.clear();    
                       censusTracts.clear();
                   },
                   
                   showLoadingIndicator: function(){
                        loadingDiv.setVisible(true);
                        var elemWidth =  Ext.get("map_canvas").getX() +
                                         Ext.get("map_canvas").getWidth();
                         var elemHeight = Ext.get("map_canvas").getTop();
                        loadingDiv.setX(elemWidth/2 + 100);
                        loadingDiv.setY(elemHeight + 2);
                   },
    
                   hideLoadingIndicator: function(){
                      loadingDiv.setVisible(false, true);
                   },

                    /*Shows tooltip at current cursor position*/
					showToolTip: function(){
						tooltip.setVisible(true);
						tooltip.setXY([Map.cursorX, Map.cursorY]);
					},
					
					/*Hides tooltip*/
					hideToolTip: function(){
						 tooltip.setVisible(false);
					},
					
					/* Opens info window with specified html*/
					showInfoWindow: function(infoText, polyLatLng){
                        var HTMLinfo = currentObj.getInfoWindowText(infoText);
                        map.openInfoWindowHtml(polyLatLng, HTMLinfo);
                        Map.hideToolTip();
					},	
					
					showLegendExplanation: function(){
					    Map.isExplationOpen = true;
					    Ext.get("divKey").autoHeight(true);
					    Ext.get('lblDetails').update('Hide details');
					    Ext.get('lnkExplain').dom.src   ="images/up2.gif"
					    Ext.get('lnkExplain').dom.title ="Hide Details"
					},
					
					hideLegendExplanation: function(){
					    Map.isExplationOpen = false;
					    Ext.get('divKey').setHeight("25", true);
					    Ext.get('lblDetails').update('Show details');
					    Ext.get('lnkExplain').dom.src   ="images/down2.gif"
					    Ext.get('lnkExplain').dom.title ="Show Details"
					},
					
					toggleExplanation: function(){
					    if(Map.isExplationOpen){
					        Map.hideLegendExplanation();
					    }
					    else{
					        Map.showLegendExplanation();
					    }
					},
					
					/* Moves tooltip to current mouse position*/
					refreshTooltip: function(e){
						Map.cursorX = e.getPageX() + 5;
						Map.cursorY = e.getPageY() + 5;
						if(tooltip.isVisible()){
							tooltip.setXY([Map.cursorX ,Map.cursorY]);
						}
					}
                }
            }();
            
            Ext.onReady(function(){
                if (GBrowserIsCompatible())
                {
                    Map.init();
					Ext.get('map_canvas').on('mouseover', Map.refreshTooltip, Map);
					Ext.get('lnkExplain').on('click', Map.toggleExplanation, Map);
                }
            });
            
                Ext.EventManager.on(window, 'unload', function() {
                        GUnload();
                  }); 
            function txtBlur(){
			    var txtAddress =document.getElementById('address');
			    if(txtAddress.value.length == 0){
			           txtAddress.style.fontStyle = "italic";
			           txtAddress.style.color = "silver";
			           txtAddress.value = "Census Tract no. (or)  address";
			    }  
            }
            
            function txtFocus(){
			  var txtAddress =document.getElementById('address');
              txtAddress.style.fontStyle = "normal";
              txtAddress.style.color = "black";
			   if(txtAddress.value == "Census Tract no. (or)  address") {
			      txtAddress.value = "";
			   }
            }