﻿/*==============================================================
    Pagination Elements
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Pg = Holds pagination values when XML is loaded
    PaginationObject = Holds settings for pagination
================================================================*/
var Pg = new Object();

var PaginationObject                = new Object();
    PaginationObject.TotalPages     = 1;
    PaginationObject.CurrentPage    = 1;
    PaginationObject.CurrentPageIds = new Array();
    PaginationObject.MaxPerPage     = 20;
    PaginationObject.PaginationNeeded = false;
    
/*############################################################*/
/*==============================================================
    Global variables used throughout the map scripts
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    the 'map' object is declared here and defined in LoadMap
================================================================*/
var map = new Object();

var adjustView      = true;
var usedTownControl = true;
var minZoom         = 11;
var maxZoom         = 17;
var thisPage        = 1;
var lastPage        = 1;
var strLsIds        = '';
var boundsListings  = [];
var RSCount         = 0;
var firstRun        = false;
var loaded          = false;
var loading         = false;
var infoOpen        = false;

/*==============================================================
    latlngPadding 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    defines padding used on each zoom level
================================================================*/

var latlngPadding = [0,0,0,0,0,0,0,0,0,.05,.03,.01,.007,.002,.001,.0005,.0002];

/*==============================================================
    markerOff, markerOn = marker rollover image arrays
    overlay             = marker numbers image array
================================================================*/
var markerOff = new Array();
var markerOn = new Array();
    markerOff['single'] = AbsoluteWebRoot + 'images/map_markers/singlepoint_schools_off.png';
    markerOff['multi']  = AbsoluteWebRoot + 'images/map_markers/multipoint_schools_off.png';
    markerOn['single']  = AbsoluteWebRoot + 'images/map_markers/singlepoint_schools_on.png';
    markerOn['multi']   = AbsoluteWebRoot + 'images/map_markers/multipoint_schools_on.png';
    
var overlay = new Array();
for(var i=1; i<51; i++)
{
    if(i < 10)
    {
	    overlay[i] = AbsoluteWebRoot + 'images/map_markers/0' + i + '.png';
    }
    else
    {
	    overlay[i] = AbsoluteWebRoot + 'images/map_markers/' + i + '.png';
    }
}

/*
var opacity  = 0.7; => Used for Custom Tiles, not in this implementation
*/

/*==============================================================
    zL = Zoom Level variable
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    boolResetZoom = a value indicating whether the Zoom Level 
    and Centering should be reset after points are plotted.
    This is set to true for standard searching, set to false when 
    the user drags the map.
================================================================*/
var zL;
var boolResetZoom = true;
                            
/*==============================================================
    AllMarkers
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    variable Array to hold all markers initially extracted 
    from XML into client side memory
================================================================*/
var AllMarkers = new Array();


    
/*############################################################*/
/*==============================================================
    Map Control Functions
================================================================*/
/*##############################################################*/

/*==============================================================
    LoadMap
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Called when page loads. Defines initial map elements.
================================================================*/
function LoadMap()
{
    if (GBrowserIsCompatible()) 
    { 
        /*
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	    Create instance of GMap2 and define our
	    Global 'map' variables for use elsewhere
	    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        http://www.google.com/apis/maps/documentation/reference.html#GMap2
        */
        
        map = new GMap2(document.getElementById("map"));
    		
    	/*
	    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	    Many optional settings for the map.
	    Add elements here as needed.
	    http://www.google.com/apis/maps/documentation/#Controls_overview
	    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	    map.addControl(new GScaleControl());
	    map.enableContinuousZoom();
    	*/
    	map.addControl(new GSmallZoomControl());
    	map.addControl(new GScaleControl());
    	/*
	    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	    In this implementation, the map coordinates 
	    are set from ASP.NET values set before script
	    is called.
	    http://www.econym.demon.co.uk/googlemaps/basic14.htm
	    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	    */
	    map.setCenter(new GLatLng(MapDefaultLatitude,MapDefaultLongitude),MapDefaultZoom);
	    var tempCenter = map.getCenter();
	    var tempZoom = map.getZoom();
    	
    	window.setTimeout(function(){
    	    map.checkResize(); 
    	    map.setCenter(tempCenter,tempZoom);},250);
	    /*
	    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	    Some coding for custom tiles, not used
	    in this implementation
	    http://www.econym.demon.co.uk/googlemaps/custommap.htm
	    http://mapki.com/wiki/Tile_Cutter
	    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	    G_NORMAL_MAP.getTileLayers()[0].getOpacity =    function () {return opacity;};
	    G_SATELLITE_MAP.getTileLayers()[0].getOpacity = function () {return opacity;};
	    G_HYBRID_MAP.getTileLayers()[0].getOpacity =    function () {return opacity;};
	    G_HYBRID_MAP.getTileLayers()[1].getOpacity =    function () {return opacity;};
	    */

    	/*
	    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	    This block restricts possible zoom levels 
	    to those set in Global settings. Zoom Level
	    must be set for each map type.
	    http://www.econym.demon.co.uk/googlemaps/range.htm
	    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        zL = map.getMapTypes();
	    for (var i=0; i < zL.length; i++) 
	    {
		    zL[i].getMinimumResolution = function() {return minZoom;}
		    zL[i].getMaximumResolution = function() {return maxZoom;}
	    }
	    */

    	/*
	    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	    Set up event handlers.
	    http://www.google.com/apis/maps/documentation/reference.html#GEvent
	    http://www.quirksmode.org/js/events_advanced.html
	    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	    */
	    GEvent.addListener(map, "dragend", function()
	    {
		    PlotFromDrag();
	    });
        GEvent.addListener(map, "dragstart", function()
	    {
	        if (AllMarkers.length > 0)
	        {
	            document.getElementById("SchoolsDragInfo").style.display = "block";
	        }
	    });
    	GEvent.addListener(map, "moveend", function()
    	{
    	    loading = false;
    	    loaded = true;
    	});
	    GEvent.addListener(map, "infowindowclose", function()
	    {
		    map.returnToSavedPosition();
	    });
    }
    else 
    {
	    window.alert("Sorry, your browser does not appear to be able to display Google Maps.");
    }
}

/*==============================================================
    populateMap
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Called when event fires, page load or click event.
    URLqString = URL of page, with query, to return XML for map
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    NOTE:
    This implementation requires DXAJAXObject.js
    does the page fetching for the XML document - Jim 4/27/07
================================================================*/
function populateMap(URLqString)
{	

	loaded = false;
	map.clearOverlays();
	document.getElementById("SchoolsDragInfo").style.display = "none";
	ManageLoadingBar(true);
	boolResetZoom = true;
	
	/*
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Displays link to XML in debug DIV
    TODO: Comment out this line in production
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
	document.getElementById("map_debug").innerHTML = '<a href="' + URLqString + '" target="_blank">View XML</a>';
	*/
    /*
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    The response 'delegate' is a function
    passed to the $DXAJAX object to handle the 
    response from the ajax call.
    http://www.terrainformatica.com/index.php/?p=9
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    */
	    
	AjaxResponseDelegate = function(xmlDoc) 
	{
	     /*
	     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            Error Handling
         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         */
	    if (xmlDoc==null)
	    {
	        document.getElementById("map_debug").innerHTML = "XML could not be loaded.";
	        return;
	    }
        var bounds  = new GLatLngBounds();
		var markers = new Array();
		
		/*
		~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        define 'AllMarkers' Global Variable for use elsewhere
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        */
		AllMarkers = xmlDoc.documentElement.getElementsByTagName("School");
		    
		var AllIds = new Array();
		
		for (var x=0;x<AllMarkers.length;x++)
		{
		    AllIds.push(AllMarkers[x].getAttribute("Id"));
		}
		/*
		~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        Pagination for this loaded XML is defined
        by passing only the IDs from the XML
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        */
        Pg = new DoPaginate(AllIds);
        var FirstPageIds = Pg.GetPageIds(1);    /* Extract first page IDs if more then one page */

        GoPg(1); /* see the GoPg() function and DoPaginate() for more information on this */
	}
	/*
	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Here is the call to the $DXAJAX object using
    the delegate defined above
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    */
    $DXAJAX.GetForDelegate(AjaxResponseDelegate, URLqString);
}

/*==============================================================
    DoPaginate(arrAllIds)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    JS Object that creates all the necessary code for paging..
    PREV , NEXT, CURRENT PAGE etc.
    Pass it an array of IDS, and it does the rest.
    Call GetPageIds(page) to get the IDs for a given page.
================================================================*/
function DoPaginate(arrAllIds)
{
    this.P = PaginationObject;
    this.AllIds = arrAllIds;
    this.strPageControls = "<TABLE CLASS=PageControls><TR><TD>No Page Controls</TD></TR></TABLE>";
    
	/*
	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Init
    Initializes the object and calculates paging
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    */
    DoPaginate.prototype.Init = function()
    {with(this){
        if (AllIds.length > P.MaxPerPage)
        {
            P.PaginationNeeded = true;
            P.TotalPages = Math.ceil(AllIds.length / P.MaxPerPage);
            P.CurrentPage = 1;
            var StartIDLoop = ((P.CurrentPage * P.MaxPerPage) - P.MaxPerPage);
			var EndIDLoop = ((StartIDLoop + P.MaxPerPage) - 1);
			if (EndIDLoop >= (AllIds.length - 1))
			{
				EndIDLoop = (AllIds.length - 1);
			}
			var TempIDList = new Array();

			for (var x = StartIDLoop; x <= EndIDLoop; x++)
			{
				TempIDList.push(AllIds[x]);
			}
			P.CurrentPageIds = TempIDList;
        }
        else
        {
            P.CurrentPageIds = AllIds;
            P.PaginationNeeded = false;
        }
    }}
    
    /*
	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    GetPageIds(page)
    returns page ids for a given page
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    */
    DoPaginate.prototype.GetPageIds = function(page)
    {with(this){
        if (page > P.TotalPages)
        {
            page = P.TotalPages;
        }
        if (page <= 0)
        {
            page = 1;
        }
        P.CurrentPage = page;
        var StartIDLoop = ((P.CurrentPage * P.MaxPerPage) - P.MaxPerPage);
		var EndIDLoop = ((StartIDLoop + P.MaxPerPage) - 1);
		if (EndIDLoop >= (AllIds.length - 1))
		{
			EndIDLoop = (AllIds.length - 1);
		}
		var TempIDList = new Array();

		for (var x = StartIDLoop; x <= EndIDLoop; x++)
		{
			TempIDList.push(AllIds[x]);
		}
		P.CurrentPageIds = TempIDList;
		return P.CurrentPageIds;
    }}
    
    /*
	~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    GetPaging(page)
    returns paging HTML for display as user GUI
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    */
    DoPaginate.prototype.GetPaging = function(page)
    {with(this){
        if (P.PaginationNeeded == false)
        {
            return "";
        }
        page = parseInt(page,10);
        
        /* ===================== PREV =============== */
        var Prev = "";
            if (page > 1)
            {
                Prev = String.format("<TD class=PrevNext><A HREF='javascript:boolResetZoom=true;GoPg({0});'>&laquo;&nbsp;PREV</A></TD>", page-1);
            }
            else
            {
               Prev = "<TD class=PrevNext><A HREF='javascript:void(null);'>&laquo;&nbsp;PREV</A></TD>";
            }
            
       /* ===================== NEXT =============== */
        var Next = "";
            if (page < P.TotalPages)
            {
                Next = String.format("<TD class=PrevNext><A HREF='javascript:boolResetZoom=true;GoPg({0});'>NEXT&nbsp;&raquo;</A></TD>", page+1);
            }
            else
            {
                Next = "<TD class=PrevNext><A HREF='javascript:void(null);'>NEXT&nbsp;&raquo;</A></TD>";
            }
            
        /* ===================== PAGE X OF Y =============== */
        var Paging = "";
        var PagingSelect = "<SELECT onchange='GoPg(this.options[this.options.selectedIndex].value);' STYLE='font:11px arial,sans-serif;'>";
            for (var x=1;x<=P.TotalPages;x++)
            {
                var sel = "";
                if (parseInt(page,10) == x)
                {
                    sel = " SELECTED";
                }
                PagingSelect += String.format("<OPTION VALUE={0}{1}>{0}</OPTION>", x, sel);
            }
            PagingSelect += "</SELECT>";
            Paging = String.format("<TD STYLE='font:11px arial,sans-serif;'>Page {0} of {1}</TD>", PagingSelect, P.TotalPages);
            
        return String.format("<TABLE CLASS=PageControls>\
                            <TR>\
                            {0}{1}{2}\
                            </TR>\
                            </TABLE>", Prev, Paging, Next);
                            
    }}
    
    this.Init(); /* Javascript Constructor call */
}

/*==============================================================
    GoPg(intPage)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Static function call to control paging events external to
    DoPaginate object. Can be called from anywhere after pages
    are loaded
================================================================*/
function GoPg(intPage)
{
    var PageIds = Pg.GetPageIds(intPage);
    var SchoolObjects = GetXMLSchoolsByIds(AllMarkers, PageIds);
        SchoolObjects = DefineSchools(SchoolObjects);
        DisplayMarkers(SchoolObjects);
        DisplaySchoolsList(SchoolObjects);
    document.getElementById("PagingDiv").innerHTML = Pg.GetPaging(intPage);
}

/*==============================================================
    DefineSchools(XMLmarkers)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Loops through School XML data and creates a defined School 
    Object for each one.
================================================================*/
function DefineSchools(XMLmarkers)
{
    var DefinedSchools = new Array();
	var MarkerPosition = 0;
		
    if (XMLmarkers.length > 0)
	{
	    /*
	    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        Optional code to add some text to the 
        map_debug area if it's helpful
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		document.getElementById("map_debug").innerHTML = 
		document.getElementById("map_debug").innerHTML + [ some debug data ]
		*/
			
		for (var m=0; m<XMLmarkers.length; m++)
		{
	        /*
	        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            Get the XML attribute of each element
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		    */
			var SchoolId    = XMLmarkers[m].getAttribute("Id");
			var Name        = XMLmarkers[m].getAttribute("Name");
			var Address     = XMLmarkers[m].getAttribute("Address");
			var Longitude   = XMLmarkers[m].getAttribute("Longitude");
			var Latitude    = XMLmarkers[m].getAttribute("Latitude");
			var SchoolType  = XMLmarkers[m].getAttribute("SchoolType");
			var Distance    = XMLmarkers[m].getAttribute("Distance");
			var point       = new GLatLng(Latitude,Longitude);
			var iconType    = 'single';					
			var mapped      = true;
		    /* var geoStatus   = 'A'; holdeover from NYTimes .. not used here */
		    
				MarkerPosition++;
				
				
			    var html = "<B>" + Name + "</B>";
			    if (Distance != "0")
			    {
			         html += " Distance: " + Distance;
			    }
			    var SchoolIds = new Array(SchoolId);
			    /*
			    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			    Making this an object
			    makes the code much more readable
			    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			    */
			    var SchoolObject           = new Object();       
			        SchoolObject.SchoolId  = SchoolId;
			        SchoolObject.Position  = MarkerPosition;
			        SchoolObject.Name      = Name;
			        SchoolObject.Address   = Address;
			        SchoolObject.Distance  = Distance;
			        
			        /*
			        Defined in "point", not needed
			        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
			        MarkerAssociativeArray.Longitude = Longitude;
			        MarkerAssociativeArray.Latitude  = Latitude;
			        */
			        SchoolObject.SchoolType= SchoolType;
			        SchoolObject.mapped    = mapped;
			        SchoolObject.iconType  = iconType;
			        SchoolObject.point     = point;
			        SchoolObject.html      = html;
			        SchoolObject.SchoolIds = SchoolIds;

			        DefinedSchools.push(SchoolObject);
		}
	}
	return DefinedSchools;
}

/*==============================================================
    createMarker(SchoolObject)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Creates a marker from school data to be placed on the map.
================================================================*/
function createMarker(SchoolObject) 
{
	var mLabel;
	var icon_off;
	var icon_on;
	
	var iconSingle = new GIcon();
	    iconSingle.image            = AbsoluteWebRoot + 'images/map_markers/singlepoint_off.png';
	    iconSingle.shadow           = AbsoluteWebRoot + 'images/map_markers/singlepoint_shadow.png';
	    iconSingle.iconSize         = new GSize(22.0, 20.0);
	    iconSingle.shadowSize       = new GSize(31.0, 25.0);
	    iconSingle.iconAnchor       = new GPoint(9.0, 12.0);
	    iconSingle.infoWindowAnchor = new GPoint(9.0, 12.0);
	    iconSingle.transparent      = AbsoluteWebRoot + 'images/map_markers/singlepoint_transparent.png';
	    iconSingle.imageMap         = new Array(10,25,10,18,3,18,0,16,0,2,3,0,16,0,18,3,18,17,10,25);
	
	var iconMulti = new GIcon();
	    iconMulti.image             = AbsoluteWebRoot + 'images/map_markers/multipoint_off.png';
	    iconMulti.shadow            = AbsoluteWebRoot + 'images/map_markers/multipoint_shadow.png';
	    iconMulti.iconSize          = new GSize(24.0, 24.0);
	    iconMulti.shadowSize        = new GSize(31.0, 25.0);
	    iconMulti.iconAnchor        = new GPoint(9.0, 12.0);
	    iconMulti.infoWindowAnchor  = new GPoint(9.0, 12.0);
	    iconMulti.transparent       = AbsoluteWebRoot + 'images/map_markers/multipoint_transparent.png';
	    iconMulti.imageMap          = new Array(10,25,10,18,3,18,0,16,0,2,3,0,16,0,18,3,18,17,10,25);
	
	/*
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Assign Icon image based on iconType
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	*/
	
	switch(SchoolObject.iconType)
	{
		case 'single':
			mLabel = {"url":overlay[SchoolObject.Position], "anchor":new GLatLng(1,4), "size":new GSize(14,14)};
			icon_off = new GIcon(iconSingle, markerOff[SchoolObject.iconType], mLabel);
			icon_on = new GIcon(iconSingle, markerOn[SchoolObject.iconType], mLabel);
			break;
		case 'multi':
			mLabel = {"url":overlay[SchoolObject.Position], "anchor":new GLatLng(5,6), "size":new GSize(14,14)};
			icon_off = new GIcon(iconMulti, markerOff[SchoolObject.iconType], mLabel);
			icon_on = new GIcon(iconMulti, markerOn[SchoolObject.iconType], mLabel);
			break;
	}
	
	var marker = new GMarker(SchoolObject.point,{icon:icon_off});
	
	/*
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Add event listeners to the marker
    http://www.google.com/apis/maps/documentation/reference.html#GEvent
    http://www.quirksmode.org/js/events_advanced.html
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	*/
	GEvent.addListener(marker, "click", function()
	{
	    ShowSchoolDetail(marker,SchoolObject);
	});
	
	GEvent.addListener(marker, "mouseover", function()
	{
		marker.setImage(markerOn[SchoolObject.iconType]);
		ROInfoGMap(SchoolObject.html);
	});
	
	GEvent.addListener(marker, "mouseout", function()
	{
		marker.setImage(markerOff[SchoolObject.iconType]);
		ROut();
	});
	
	return marker;
}

/*==============================================================
    DisplayMarkers(arrSchoolObjects)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Displays all school objects as markers passed in arrSchoolObjects
================================================================*/
function DisplayMarkers(arrSchoolObjects)
{
    if (arrSchoolObjects.length > 0)
	{
		map.clearOverlays();
		var bounds = new GLatLngBounds();
		
		
	    /*
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        Here's where icons are assigned to groups
        For this implementation, the value for
        'LatLngRange' is throttled low, you can make 
        the number greater to increase grouping
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	    */
	    var LatLngRange = .00001;
	    
		for(var Single=0; Single<arrSchoolObjects.length; Single++)
		{
			for(var Multi=0; Multi<Single; Multi++)
			{
				if(arrSchoolObjects[Single].mapped && (arrSchoolObjects[Single].Position > 0) && arrSchoolObjects[Multi].Position > 0)
				{
					if(
						arrSchoolObjects[Single].point.lat() < (arrSchoolObjects[Multi].point.lat() + LatLngRange) && 
						arrSchoolObjects[Single].point.lat() > (arrSchoolObjects[Multi].point.lat() - LatLngRange) &&
						arrSchoolObjects[Single].point.lng() < (arrSchoolObjects[Multi].point.lng() + LatLngRange) && 
						arrSchoolObjects[Single].point.lng() > (arrSchoolObjects[Multi].point.lng() - LatLngRange)
						
						)
					    {
	                        /*
                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                            Group Assignment subroutine
                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	                        */
						    arrSchoolObjects[Multi].iconType = 'multi';
						    
						    if (arrSchoolObjects[Multi].html.indexOf("<li type=circle>") == -1)
						    {
						        arrSchoolObjects[Multi].html = "<li type=circle>" + arrSchoolObjects[Multi].html; 
						    }
						    arrSchoolObjects[Multi].html = arrSchoolObjects[Multi].html + "<BR><li type=circle>" + arrSchoolObjects[Single].html;
						    /*
                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                            School ID added to array SchoolIds for each
                            group. This is used when calling the Info
                            Bubble, and passing the query through the
                            $DXAJAX object
                            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
	                        */
						    arrSchoolObjects[Multi].SchoolIds.push(arrSchoolObjects[Single].SchoolId);
						    arrSchoolObjects[Single].Position   = 0;
						    arrSchoolObjects[Single].mapped     = false;
					    }
				}
			}
		}
		
		
		for(var MCount = 0; MCount < arrSchoolObjects.length; MCount++)
		{	
	        var listingHTML = '';
	        var marker;
	        if(arrSchoolObjects[MCount].iconType == 'multi')
            {
                marker = createMarker(arrSchoolObjects[MCount]);	
                bounds.extend(arrSchoolObjects[MCount].point);
	            map.addOverlay(marker);
            }
            else
            {
              if(arrSchoolObjects[MCount].mapped == true)
              {
                if (arrSchoolObjects[MCount].Position > 0) /* Error occurs when first element is in a group.. fix */
                {
                    marker = createMarker(arrSchoolObjects[MCount]);	
                    bounds.extend(arrSchoolObjects[MCount].point);
	                map.addOverlay(marker);
                }
                else
                {
                    marker = createMarker(arrSchoolObjects[MCount]);	
                    //bounds.extend(arrSchoolObjects[MCount].point);
	                map.addOverlay(marker);
                }
              }
            }
		}
		
		/*
		--------------------------------------------------
		Logic for the single property point to be displayed
		if we're on the detail page
		PropertyPointGLatLng = defined on Detail page
		--------------------------------------------------
		*/
		

	    if (PropertyPointGLatLng != null)
	    {
	        var PropMarker = new GMarker(PropertyPointGLatLng,PropertyPointIcon);
	            bounds.extend(PropertyPointGLatLng);
                map.addOverlay(PropMarker);
	    }
		
		/*
		================================================
		Pad map so markers don't hang outside
		================================================
		*/
		var latlngPaddingIndex = map.getBoundsZoomLevel(bounds);
		// ZOOM maximum to the level 14.
		if(latlngPaddingIndex > 14)
		{
		    latlngPaddingIndex = 14;
		}
	    var ne = bounds.getNorthEast();
	    var sw = bounds.getSouthWest();

        var maxLat = ne.lat() - latlngPadding[latlngPaddingIndex]; 
        var maxLng = ne.lng() - latlngPadding[latlngPaddingIndex];
        var minLat = sw.lat() + latlngPadding[latlngPaddingIndex];
        var minLng = sw.lng() + latlngPadding[latlngPaddingIndex];
        bounds.extend(new GLatLng(maxLat,maxLng));
        bounds.extend(new GLatLng(minLat,minLng));


		if (boolResetZoom)
		{		
		    map.checkResize(); 
		    var NewZoomLevel = map.getBoundsZoomLevel(bounds)>14?14:map.getBoundsZoomLevel(bounds);
		    map.setCenter(bounds.getCenter(), NewZoomLevel);
		}
		else
		{
			/*
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            Using panTo instead of setCenter causes a 
            nice animation instead of a jagged redraw 
            of the map. Optional.
            http://www.google.com/apis/maps/documentation/reference.html#GMap2
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            map.setCenter(bounds.getCenter());
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            */
            
		}
		
		document.getElementById("SchoolsList").style.display = "block";
		ManageLoadingBar(false);
	}
	else
	{
		map.clearOverlays();
		
		if (PropertyPointGLatLng != null)
	    {
	        var PropMarker = new GMarker(PropertyPointGLatLng,PropertyPointIcon);
                map.addOverlay(PropMarker);
                map.panTo(PropertyPointGLatLng);
	    }
	    
		document.getElementById("status").innerHTML = 'Your search returned no results.';
		document.getElementById("listings").innerHTML = '';
	}
}

/*==============================================================
    DisplaySchoolsList(arrSchoolObjects)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Displays all schools in a list below map
================================================================*/
function DisplaySchoolsList(arrSchoolObjects)
{
    //alert(markers.length); 
    var Display = "";
    
    for(var x=0;x<arrSchoolObjects.length;x++)
    {
        var Name = arrSchoolObjects[x].Name;
        var ClassName = "SchoolNorm";
        var SchoolType = "public";
        if (arrSchoolObjects[x].SchoolType == 'R')
        {
            SchoolType = "private";
        }
        var Position = (parseInt(arrSchoolObjects[x].Position,10) == 0) ? "&nbsp;" : "<DIV CLASS=num>" + arrSchoolObjects[x].Position + "</div>";
            if (Position == "&nbsp;")
            {
                Name = String.format("<IMG SRC='" + AbsoluteWebRoot + "images/school_group_member.gif' BORDER=0>{0}", arrSchoolObjects[x].Name);
            }
            
        var Row = String.format("<DIV CLASS='{0}' ID='{4}' onmousedown=\"javascript:GetSchoolDetail('{4}');\" onmouseover=rollOn('{4}') onmouseout=rollOff('{4}')>\
                                <TABLE CLASS=School>\
                                <TD width=20>{1}</TD>\
                                <TD width=220>{2}</TD>\
                                <TD>{3}</TD>\
                                <TD width=30 CLASS=SchoolType>{5}</TD>\
                                </TR>\
                                </TABLE>\
                                </DIV>",
                                ClassName,
                                Position,
                                Name,
                                arrSchoolObjects[x].Address,
                                arrSchoolObjects[x].SchoolId,
                                SchoolType
                                );
                                
        if (PropertyPointGLatLng != null) // show distance
        {
            var Row = String.format("<DIV CLASS='{0}' ID='{4}' onmousedown=\"javascript:GetSchoolDetail('{4}');\" onmouseover=rollOn('{4}') onmouseout=rollOff('{4}')>\
                                <TABLE CLASS=School>\
                                <TD width=20>{1}</TD>\
                                <TD width=220>{2}</TD>\
                                <TD>{3}</TD>\
                                <TD width=25 CLASS=SchoolType>{5}</TD>\
                                <TD width=25 CLASS=SchoolType>{6}</TD>\
                                </TR>\
                                </TABLE>\
                                </DIV>",
                                ClassName,
                                Position,
                                Name,
                                arrSchoolObjects[x].Address,
                                arrSchoolObjects[x].SchoolId,
                                SchoolType,
                                arrSchoolObjects[x].Distance
                                );
        }
        
        Display += Row;
    }
    document.getElementById("SchoolsList").innerHTML = Display;
    ManageLoadingBar(false);
    /*
    TopTab('Buying');
    */
}

/*==============================================================
    ShowSchools()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Calls populateMap() based on School Type and RegionId
================================================================*/
function ShowSchools()
{
    var Query = AbsoluteWebRoot + "Schools_XML.aspx?RegionId=" + RegionId + "&SchoolType=" + GetSchoolType()
                + "&State=" + StateId + "&Level=" + LevelId;
    if (PropertyPointGLatLng != null)
    {
        Query += "&Latitude=" + PropertyPointGLatLng.lat() + "&Longitude=" + PropertyPointGLatLng.lng();
    }
    //document.getElementById("map").innerHTML = Query;
    
    populateMap(Query);
    LoadingSchoolsDataPleaseWait();
}

/*==============================================================
    LoadingSchoolsDataPleaseWait()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Displays message as data laods. Mostly for IE.
================================================================*/
function LoadingSchoolsDataPleaseWait()
{
    document.getElementById("SchoolsList").style.display = "none";
    /* document.getElementById("SchoolsDragInfo").style.display = "none"; */
    document.getElementById("PagingDiv").innerHTML = "<DIV align=center STYLE='padding:5px;background-color:#eaeaea;font:bold 11px tahoma,verdana,arial,sans-serif;color:#777;border:2px solid #eee;'>Loading Schools data, Please Wait...</DIV>";
    /*
    TopTab('Buying');
    */
}

/*==============================================================
    ManageLoadingBar(DoDisplay) 
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    shows the "Loading..." screen when the page load takes 
    too long. I rewrote this one for the sake of simplicity 
    from previous versions. - Jim
================================================================*/
function ManageLoadingBar(DoDisplay)
{
    if (DoDisplay)
    {
        var Loading = gE("Loading");
            Loading.style.display = "block";
            Loading.style.width  = RecurseOffset(gE("map")).offsetWidth + "px";
            Loading.style.height = RecurseOffset(gE("map")).offsetHeight + "px";
            Loading.style.left   = RecurseOffset(gE("map")).offsetLeft + "px";
            Loading.style.top    = RecurseOffset(gE("map")).offsetTop + "px";
            Loading.style.background = "url(" + AbsoluteWebRoot + "images/map_markers/loading2.gif) no-repeat center";
            window.setTimeout(function(){ManageLoadingBar(false);},5000); /* only show for 5 seconds */
    }
    else
    {
        gE("Loading").style.display = "none";
    }
}

/*==============================================================
    rollOn(school)  rollOff(school)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    used in rollover on schools list
================================================================*/
function rollOn(school)
{
	document.getElementById(school).className = 'SchoolOver';
}
function rollOff(school)
{
	document.getElementById(school).className = 'SchoolNorm';
}

/*==============================================================
    PlotFromDrag()
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Re-Loops through the XML data array, and finds points 
    that are within the map area. Then redraws points and 
    recenters based on selection.
================================================================*/
function PlotFromDrag()
{
    if (AllMarkers.length == 0) /* do nothing if no schools are loaded */
    {
        return;
    }
    boolResetZoom = false;
    /* 
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    have to add this delay for IE to hide schools... 
    IE takes a long time looping through arrays
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    */
    if (document.all)
    {
        ManageLoadingBar(true);
        LoadingSchoolsDataPleaseWait();
    }
    
    
    window.setTimeout(function()
    { 
            var Markers = new Array();
            for (var x=0;x<AllMarkers.length;x++)
            {
                var MLat = parseFloat(AllMarkers[x].getAttribute("Latitude"));
                var MLng = parseFloat(AllMarkers[x].getAttribute("Longitude"));
                var MLatLng = new GLatLng(MLat, MLng);
                if (IsPointOnMap(MLatLng))
                {
                    Markers.push(AllMarkers[x]);
                }
            }
            var AllIds = new Array();
            for (var x=0;x<Markers.length;x++)
	        {
	            AllIds.push(Markers[x].getAttribute("Id"));
	        }
            Pg = new DoPaginate(AllIds);            /* Resets Pagination  */
            GoPg(1);
    
    },250);
}

/*==============================================================
    IsPointOnMap(GLatLng)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Determines if Point is located on Map based on bounds
================================================================*/
function IsPointOnMap(GLatLng)
{
    var bounds = map.getBounds();
    var SW = bounds.getSouthWest();
    var NE = bounds.getNorthEast();
    
    if (
        (GLatLng.lat() < NE.lat()) &&
        (GLatLng.lat() > SW.lat()) &&
        (GLatLng.lng() < NE.lng()) &&
        (GLatLng.lng() > SW.lng())
        )
        {
            return true;
        }
    return false;
}

/*==============================================================
    GetSchoolDetail(SchoolId)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    AJAX Called from school list
================================================================*/
function GetSchoolDetail(SchoolId)
{
    var XMLSchool   = GetXMLSchoolById(AllMarkers, SchoolId);
    var Longitude   = XMLSchool.getAttribute("Longitude");
	var Latitude    = XMLSchool.getAttribute("Latitude");
	var SchoolType  = XMLSchool.getAttribute("SchoolType");
	var strURL = AbsoluteWebRoot + "School_Detail.aspx?SIDs=" + SchoolId + "&Type=" + SchoolType;
	var ResponseDelegate = function(AjaxResponse)
    {
        map.savePosition();
        map.openInfoWindowHtml(new GLatLng(Latitude,Longitude), AjaxResponse);
    }
	$DXAJAX.GetForDelegate(ResponseDelegate, strURL);
}

function GetSchoolType()
{
    var SchoolTypes = document.getElementsByName("ShowSchools");
    var thisType = "";
    for(var x=0;x<SchoolTypes.length;x++)
    {
        if (SchoolTypes[x].checked)
        {
            return SchoolTypes[x].value;
            break;
        }
    }
}

function ShowSchoolDetail(marker,SchoolObject) /* called from within window markers */
{

    var strURL = AbsoluteWebRoot + "School_Detail.aspx?SIDs=" + SchoolObject.SchoolIds.join(",") + "&Type=" + SchoolObject.SchoolType;
    //document.getElementById("map").innerHTML = strURL;
    var ResponseDelegate = function(AjaxResponse)
    {
        map.savePosition();
        marker.openInfoWindowHtml(AjaxResponse);
    }
	$DXAJAX.GetForDelegate(ResponseDelegate, strURL);
}

function GetXMLSchoolById(arrAllMarkers, strId) /* extracts marker by ID from XML marker array */
{
    for (var x=0;x<arrAllMarkers.length;x++)
    {
        if (arrAllMarkers[x].getAttribute("Id") == strId)
        {
            return arrAllMarkers[x];
            break;
        }
    }
}

function GetXMLSchoolsByIds(arrAllMarkers, arrAllIds) /* extracts markers by IDs */
{
    var markers = new Array();
    for (var x=0;x<arrAllIds.length;x++)
    {
        markers.push(GetXMLSchoolById(arrAllMarkers, arrAllIds[x]));
    }
    return markers;
}