﻿/* bool */var asyncBusy = new Object();
/* HTMLElement */var zoomDiv; // div that holds message telling users to zoom in to view cameras and signs

/*****************************************************************************
 * PUBLIC FIELDS - TIMEOUTS
 *****************************************************************************/
/* number */var GetStatusTimeout = null; // timeout to fetch new status
/* number */var LeftPanelTimeout = null; // timeout to remove detail view iframe, giving it time to clean up
/* number */var RemoveDetailViewTimeout = null; // timeout to remove detail view iframe, giving it time to clean up

/*****************************************************************************
 * PUBLIC FIELDS - EVENT LISTENERS
 *****************************************************************************/
/* GEventListener */var mapZoomendListener;
/* GEventListener[] */var GDirectionsLoadListeners = new Array();

/*****************************************************************************
 * PUBLIC FIELDS - MAP
 *****************************************************************************/
/* com.ibigroup.GoogleMaps.Map */var map = null;
/* com.smartsunguide.MessageControl */var messageControl = null;
/* com.smartsunguide.MainControl */var mainControl = null;

/* object <string, GPolygon> */var counties = new Object();

/* MarkerManager */var locationManager = null;
/* com.smartsunguide.LocationMarker[] */var locationMarkers = new Array();

/* MarkerManager */var routeManager = null;
/* bool */var routeHidden = false;
/* com.ibigroup.GoogleMaps.Marker[] */var routeMarkers = new Array();
/* GPolyline[] */var routePolylines = new Array();
/* object<string, GPolyline> */var savedRoutePolylines = new Object();

/* MarkerManager */var incidentManager = null;
/* bool */var incidentsHidden = false;
/* com.smartsunguide.IncidentMarker[] */var incidentMarkers = new Array();
/* com.smartsunguide.IncidentMarker[] */var shownIncidentMarkers = new Array();

/* MarkerManager */var cameraManager = null;
/* bool */var camerasHidden = false;
/* com.smartsunguide.CameraMarker[] */var cameraMarkers = new Array();
/* com.smartsunguide.CameraMarker[] */var shownCameraMarkers = new Array();

/* MarkerManager */var signManager = null;
/* bool */var signsHidden = false;
/* com.smartsunguide.SignMarker[] */var signMarkers = new Array();
/* com.smartsunguide.SignMarker[] */var shownSignMarkers = new Array();

/* bool */var speedLinksHidden = false;
/* GPolyline[] */var speedLinkPolylines = new Array();

/*****************************************************************************
 * PUBLIC FIELDS - STATE
 *****************************************************************************/
/* Structure */var structure = null;
/* PageStatus */var mystatus = null;
/* ProfileStatus */var profile = {speedColors: ["d92b2b", "ffee33", "82d982"]};
/* object<string, Incident> */var incidentsByID = new Object();
/* object<string, Location> */var locationsByID = new Object();
/* object<string, Camera> */var camerasByID = new Object();
/* object<string, bool> */var cameraUnavailableByID = new Object();
/* object<string, Sign> */var signsByID = new Object();
/* object<string, bool> */var signUnavailableByID = new Object();
/* object<string, string> */var signMessageByID = new Object();
var currentSpeedLinkMarkerIndex = 0;

/*****************************************************************************
 * PUBLIC FIELDS - LEFT PANEL
 *****************************************************************************/
/* number */var leftPanelIndex = 0;
/* string[] */var leftPanelMessages = new Array();

/*****************************************************************************
 * PUBLIC FIELDS - DETAIL VIEW
 *****************************************************************************/
/* string[] */var detailViewMessages = new Array();

/*****************************************************************************
 * PROPERTIES - ROUTE
 *****************************************************************************/
/* bool */var routeSelect = false;
/* string */var routeStartRoadway = "";
/* number */var routeStartMile = 0;

/*****************************************************************************
 * PROPERTIES - STATE (DEPRECATED)
 *****************************************************************************/
/* object */var stateData =
{
    // map properties
    locations: new Array(),
    locationsByID: new Object(),
    incidents: new Array(),
    incidentsByID: new Object(),
    incidentsHidden: false,
    cameras: new Array(),
    camerasByID: new Object(),
    unavailableCameras: new Object(),
    camerasHidden: false,
    signs: new Array(),
    signsByID: new Object(),
    unavailableSigns: new Object(),
    messageSigns: new Object(),
    signsHidden: false,
    speedLinks: new Array(),
    speedLinksHidden: false,
    speedColors: ["d92b2b", "ffee33", "82d982"],

    // route properties
    routeSelect: false,
    routeStartRoadway: null,
    routeStartMile: null,
    activeRoute: null,
    activeRouteStatus: null, 
    activeRouteName: null,
    routeNames: new Array(),

    panelIndex: -1,
    panelHTML: "",
    panelTabIndex: null,
    panelExpandedNodes: new Object(),
    panelCheckboxes: new Array(),

    workingMessages: new Array()
}

/*****************************************************************************
 * PUBLIC METHODS
 *****************************************************************************/
/**
 *
 */
/* void */function DrawSpeedLinks()
{
    console.log("DrawSpeedLinks");
    
    // remove old polylines
    /*
    for (var i = 0; i < speedLinkPolylines.length; i++)
    {
        map.removeOverlay(speedLinkPolylines[i]);
    }
    */
    speedLinkPolylines = new Array();

    // add new polylines
    /* GLatLng[] */var points;
    /* GPolyline */var polyline;
    /* string */var polylineColor;
    /* SpeedLink[] */var speedLinks = (mystatus != null && mystatus.speedLinks != null) ? mystatus.speedLinks : new Array();
    
    for (var i = 0; i < speedLinks.length; i++)
    {
        points = new Array();
        for (var j = 0; j < speedLinks[i].points.length; j++)
        {
            points.push(new GLatLng(speedLinks[i].points[j][0], speedLinks[i].points[j][1]));
        }
        switch (speedLinks[i].type)
        {
            case "slow":
                polylineColor = "#" + profile.speedColors[0];
                break;
            case "medium":
                polylineColor = "#" + profile.speedColors[1];
                break;
            default:
                polylineColor = "#" + profile.speedColors[2];
                break;
        }

        polyline = new GPolyline(points, polylineColor, 4, 1);
        speedLinkPolylines.push(polyline);
    }
    if (!GetRouteSelect() && !speedLinksHidden)
    {
        currentSpeedLinkMarkerIndex = 0;
        setTimeout('addSpeedLinksIncrementally()', 10);
        //for (var i = 0; i < speedLinkPolylines.length; i++)
        //{
        //    map.addOverlay(speedLinkPolylines[i]);
        //}
    }

    DrawRoute();
}

function addSpeedLinksIncrementally() {
    if (currentSpeedLinkMarkerIndex < speedLinkPolylines.length) {
        for (var i = currentSpeedLinkMarkerIndex; i < speedLinkPolylines.length && i < currentSpeedLinkMarkerIndex + 50; i++)
        {
            map.addOverlay(speedLinkPolylines[i]);
        }
        currentSpeedLinkMarkerIndex = i;
        if (currentSpeedLinkMarkerIndex < speedLinkPolylines.length) {
            setTimeout('addSpeedLinksIncrementally()', 10);
        }
    }
}

/**
 *
 */
/* void */function DrawRoute()
{
    // debugger;

    console.log("DrawRoute");

    if (GetRouteSelect() || routeHidden || structure == null)
    {
        return;
    }

    dojo.require("com.smartsunguide.RouteStartMarker");
    dojo.require("com.smartsunguide.RouteEndMarker");

    // clear route markers
    routeManager.clearMarkers();
    for (var i = 0; i < routeMarkers.length; i++)
    {
        routeMarkers[i].purge();
    }
    routeMarkers = new Array();

    // clear route polylines
    for (var i = 0; i < routePolylines.length; i++)
    {
        map.removeOverlay(routePolylines[i]);
    }
    routePolylines = new Array();

    // clear route polyline listeners
    for (var i = 0; i < GDirectionsLoadListeners.length; i++)
    {
        GEvent.removeListener(GDirectionsLoadListeners[i]);
    }

    /* Route */var route = profile.activeRoute;

    // break out if there is no active route defined
    if (route == null) return;

    // gather lat/lng of critical points along the route
    /* Location */var wayPointLocation = null;
    /* GLatLng[] */var wayPoints = new Array();
    for (var i = 0; i < route.segments.length; i++)
    {
        // get first location from the segment
        wayPointLocation = locationsByID[route.segments[i].locationList[0].elementID];
        if (wayPointLocation != null)
        {
            wayPoints.push(new GLatLng(wayPointLocation.lat, wayPointLocation.lng));
        }
        if (i == route.segments.length - 1)
        {
            // for the last segment, also get last location from the segment
            wayPointLocation = locationsByID[route.segments[i].locationList[route.segments[i].locationList.length - 1].elementID];
            if (wayPointLocation != null)
            {
                wayPoints.push(new GLatLng(wayPointLocation.lat, wayPointLocation.lng));
            }
        }
    }
    
    // load polylines from waypoints
    try
    {
        /* string */var polylineName = route.segments[0].roadway + "_" + route.segments[0].startMile + "_" + route.segments[route.segments.length - 1].roadway + "_" + route.segments[route.segments.length - 1].endMile;
        if (savedRoutePolylines[polylineName] != null)
        {
            // if a polyline was previously fetched for this route, retrieve it
            routePolylines.push(savedRoutePolylines[polylineName]);
            map.addOverlay(savedRoutePolylines[polylineName]);
        }
        else
        {
            // otherwise, use GDirections to determine the polyline
            /* GDirections */var directions = new GDirections();
            directions.loadFromWaypoints(wayPoints, {getPolyline: true});
            directions._polylineName = polylineName;
            directions._loadListener = GEvent.addListener(directions, "load", gdirections_load);
            GDirectionsLoadListeners.push(directions._loadListener);
        }

        // add route start marker
        /* RouteSegment */var startSegment = route.segments[0];
        /* Location */var startLocation = locationsByID[startSegment.locationList[0].elementID]; // locationsByID[routeLocations[0].elementID];
        routeMarkers.push(new com.smartsunguide.RouteStartMarker(new GLatLng(startLocation.lat, startLocation.lng), {data: [startLocation]}));

        // add route end marker
        /* RouteSegment */var endSegment = route.segments[route.segments.length - 1];
        /* Location */var endLocation = locationsByID[endSegment.locationList[endSegment.locationList.length - 1].elementID]; // locationsByID[routeLocations[routeLocations.length - 1].elementID];
        routeMarkers.push(new com.smartsunguide.RouteEndMarker(new GLatLng(endLocation.lat, endLocation.lng), {data: [endLocation]}));

        routeManager.addMarkers(routeMarkers, 0);
        routeManager.refresh();
    }
    catch (exception)
    {
        console.log(exception);
    }
}

/**
 * Toggles whether the map is in information display or route selection mode.
 */
/* void */function SetRouteSelect(/* bool */state)
{
    state = (state == true);

    if (GetRouteSelect() == state)
    {
        return;
    }

    routeSelect = state;
    
    if (state)
    {
        // clear incident markers   
        incidentManager.clearMarkers();

        // clear camera markers
        cameraManager.clearMarkers();

        // clear sign markers
        signManager.clearMarkers();

        // clear speed link polylines
        for (var i = 0; i < speedLinkPolylines.length; i++)
        {
            map.removeOverlay(speedLinkPolylines[i]);
        }
        
        // clear route markers and polylines
        routeManager.clearMarkers();
        for (var i = 0; i < routePolylines.length; i++)
        {
            map.removeOverlay(routePolylines[i]);
        }
    
        // draw location markers
        locationManager.addMarkers(locationMarkers, 0);
        locationManager.refresh();
    }
    else
    {
        // clear partial route properties
        routeStartRoadway = null;
        routeStartMile = null;

        // clear location markers
        locationManager.clearMarkers();
        
        // clear route markers and polylines
        routeManager.clearMarkers();
        for (var i = 0; i < routePolylines.length; i++)
        {
            map.removeOverlay(routePolylines[i]);
        }

        // draw incident markers
        if (!incidentsHidden)
        {
            incidentManager.addMarkers(shownIncidentMarkers, 0);
            incidentManager.refresh();
        }
        
        // draw camera markers
        if (!camerasHidden)
        {
            cameraManager.addMarkers(shownCameraMarkers, 11);
            cameraManager.refresh();
        }
        
        // draw sign markers
        if (!signsHidden)
        {
            signManager.addMarkers(shownSignMarkers, 11);
            signManager.refresh();
        }

        // draw speed link polylines
        if (!speedLinksHidden)
        {
            for (var i = 0; i < speedLinkPolylines.length; i++)
            {
                map.addOverlay(speedLinkPolylines[i]);
            }
        }
    }
    
    GEvent.trigger(window, "routeselectchanged", state);
}

/**
 * Toggles whether the map is in information display or route selection mode.
 */
/* void */function SetTripRouteSelect(/* bool */state, locationSelectedFunc)
{
    state = (state == true);

    if (GetRouteSelect() == state)
    {
        return;
    }

    routeSelect = state;
    
    if (state)
    {
        // clear incident markers   
        incidentManager.clearMarkers();

        // clear camera markers
        cameraManager.clearMarkers();

        // clear sign markers
        signManager.clearMarkers();

        // clear speed link polylines
        for (var i = 0; i < speedLinkPolylines.length; i++)
        {
            map.removeOverlay(speedLinkPolylines[i]);
        }
        
        // clear route markers and polylines
        routeManager.clearMarkers();
        for (var i = 0; i < routePolylines.length; i++)
        {
            map.removeOverlay(routePolylines[i]);
        }
    
        // draw location markers
        var tripLocationMarkers = new Array();
        for (var loc in locationMarkers) {
            var newItem = locationMarkers[loc];
            newItem.clickBinding = locationSelectedFunc;
            tripLocationMarkers.push(newItem);
        }
        locationManager.addMarkers(tripLocationMarkers, 0);
        locationManager.refresh();
    }
    else
    {
        // clear partial route properties
        routeStartRoadway = null;
        routeStartMile = null;

        // clear location markers
        locationManager.clearMarkers();
        
        // clear route markers and polylines
        routeManager.clearMarkers();
        for (var i = 0; i < routePolylines.length; i++)
        {
            map.removeOverlay(routePolylines[i]);
        }

        // draw incident markers
        if (!incidentsHidden)
        {
            incidentManager.addMarkers(shownIncidentMarkers, 0);
            incidentManager.refresh();
        }
        
        // draw camera markers
        if (!camerasHidden)
        {
            cameraManager.addMarkers(shownCameraMarkers, 11);
            cameraManager.refresh();
        }
        
        // draw sign markers
        if (!signsHidden)
        {
            signManager.addMarkers(shownSignMarkers, 11);
            signManager.refresh();
        }

        // draw speed link polylines
        if (!speedLinksHidden)
        {
            for (var i = 0; i < speedLinkPolylines.length; i++)
            {
                map.addOverlay(speedLinkPolylines[i]);
            }
        }
    }
    
    GEvent.trigger(window, "routeselectchanged", state);
}

/**
 *
 */
/* bool */function GetRouteSelect()
{
    return routeSelect;
}

/* void */function SetLeftPanel(/* number */index, /* bool */force)
{
    // debugger;

    /* HTMLElement */var contentDiv = document.getElementById("contentDiv");
    /* HTMLElement */var iFrame = document.getElementById("LeftPanelIFrame");
    /* HTMLElement[] */var tabImgs = dojo.query("img", dojo.byId("tabTd"));

    if (!dojo.hasClass(contentDiv, "panelExpanded"))
    {
        ShowLeftPanel();
    }

    if (leftPanelIndex != index || force)
    {
        leftPanelIndex = index;

        // hilite selected tab image
        for (/* number */var i = 0; i < tabImgs.length; i++)
        {
            if (i == index)
            {
                dojo.addClass(tabImgs[i], "selectedTabButtonImg");
            }
            else
            {
                dojo.removeClass(tabImgs[i], "selectedTabButtonImg");
            }
        }
        
       /* AddLeftPanelMessage("Loading page...");*/

        /* HTMLElement */var iFrame = document.getElementById("LeftPanelIFrame");

        if (iFrame != null)
        {
            /* iFrame.src = "javascript:false";*/
        }
        if (LeftPanelTimeout == null)
        {
            LeftPanelTimeout = setTimeout("SetLeftPanel_timeout();", 250);
        }
    }
}

/* void */function SetLeftPanel_timeout()
{
    clearTimeout(LeftPanelTimeout);
    LeftPanelTimeout = null;

    /* HTMLElement */var leftPanelDiv = document.getElementById("LeftPanelIFrameDiv");
    leftPanelDiv.innerHTML = "";

    // write an iframe with the appropriate location
    /* Route */var activeRoute = profile.activeRoute;
    /* string */var routeSuffix = "";
    try
    {
        if (activeRoute != null)
        {
            routeSuffix = "&startRoad=" + activeRoute.segments[0].roadway + "&startMile=" + activeRoute.segments[0].startMile + "&endRoad=" + activeRoute.segments[activeRoute.segments.length - 1].roadway + "&endMile=" + activeRoute.segments[activeRoute.segments.length - 1].endMile;
        }
    }
    catch (exception)
    {
        console.log(exception);
    }
    /* string */var location;

    switch (leftPanelIndex)
    {
        case 1:
            location = "LeftPanelRouteEditor.aspx";
            break;
        case 2:
            location = "LeftPanelTripCalculator.aspx";
            break;
        case 3:
            location = "LeftPanelList.aspx?type=incidents";
            break;
        case 4:
            location = "LeftPanelList.aspx?type=cameras" + routeSuffix;
            break;
        case 5:
            location = "LeftPanelList.aspx?type=signs" + routeSuffix;
            break;
        case 6:
            location = "LeftPanelVideo.aspx";
            break;
        case 7:
            location = "LeftPanelTwitter.aspx";
            break;
        case 8:
            location = "LeftPanelAirportSeaport.aspx";
            break;
        default:
            location = "LeftPanelHome.aspx";
            break;
    }

    leftPanelDiv.innerHTML = '<iframe id="LeftPanelIFrame" name="LeftPanelIFrame" src="' + location + '" frameborder="0" scrolling="auto"></iframe>';
}

/* void */function ShowLeftPanel()
{
    /* HTMLElement */var contentDiv = document.getElementById("contentDiv");
    /* HTMLElement[] */var tabImgs = dojo.query("img", dojo.byId("tabTd"));

    if (!dojo.hasClass(contentDiv, "panelExpanded"))
    {
        /* number */var beforeLng = map.getBounds().getNorthEast().lng();

        // expand left panel
        dojo.addClass(contentDiv, "panelExpanded");
        map.checkResize();
        map.adjustToolTip();

        /* GLatLngBounds */var afterBounds = map.getBounds();
        /* GLatLng */var afterCenter = map.getCenter();
        /* GLatLng */var afterSpan = map.getBounds().toSpan();
        
        map.setCenter(new GLatLng(afterCenter.lat(), beforeLng - (afterSpan.lng() * 0.5)));

        dojo.addClass(tabImgs[leftPanelIndex], "selectedTabButtonImg");
    }
}

/* void */function HideLeftPanel()
{
    /* HTMLElement */var contentDiv = document.getElementById("contentDiv");
    /* HTMLElement[] */var tabImgs = dojo.query("img", dojo.byId("tabTd"));

    if (dojo.hasClass(contentDiv, "panelExpanded"))
    {
        /* number */var beforeLng = map.getBounds().getNorthEast().lng();

        dojo.removeClass(contentDiv, "panelExpanded");
        map.checkResize();
        map.adjustToolTip();

        /* GLatLngBounds */var afterBounds = map.getBounds();
        /* GLatLng */var afterCenter = map.getCenter();
        /* GLatLng */var afterSpan = map.getBounds().toSpan();
        
        map.setCenter(new GLatLng(afterCenter.lat(), beforeLng - (afterSpan.lng() * 0.5)));

        for (var i = 0; i < tabImgs.length; i++)
        {
            dojo.removeClass(tabImgs[i], "selectedTabButtonImg");
        }
    }
}

/**
 *
 */
/* void */function AddLeftPanelMessage(/* string */message)
{
    for (var i = 0; i < leftPanelMessages.length; i++)
    {
        if (leftPanelMessages[i] == message)
        {
            leftPanelMessages.splice(i, 1);
            break;
        }
    }
    leftPanelMessages.push(message);

    document.getElementById("LeftPanelLoadingDiv").style.display = "block";
    document.getElementById("LeftPanelLoadingImg").src = "images/icons/ajax-loader.gif";
    document.getElementById("LeftPanelLoadingMessageDiv").innerHTML = leftPanelMessages[leftPanelMessages.length - 1];
}

/**
 *
 */
/* void */function RemoveLeftPanelMessage(/* string */message)
{
    for (var i = 0; i < leftPanelMessages.length; i++)
    {
        if (leftPanelMessages[i] == message)
        {
            leftPanelMessages.splice(i, 1);
            break;
        }
    }

    if (leftPanelMessages.length == 0)
    {
        document.getElementById("LeftPanelLoadingDiv").style.display = "none";
        document.getElementById("LeftPanelLoadingImg").src = "images/pix.gif";
        document.getElementById("LeftPanelLoadingMessageDiv").innerHTML = "";
    }
    else
    {
        document.getElementById("LeftPanelLoadingMessageDiv").innerHTML = leftPanelMessages[leftPanelMessages.length - 1];
    }
}

/**
 *
 */
/* void */function ShowLeftPanelLoading()
{
    // document.getElementById("LeftPanelLoadingDiv").style.display = "block";
}

/**
 *
 */
/* void */function HideLeftPanelLoading()
{
    // document.getElementById("LeftPanelLoadingDiv").style.display = "none";
}

/**
 * Hide markers or polylines in a given category on the map.
 */
/* void */function HideMarkerGroup(/* string */groupName)
{
    if (GetMarkerGroupHidden(groupName))
    {
        return;
    }
    switch (groupName)
    {
        case "route":
            if (!GetRouteSelect())
            {
                routeManager.clearMarkers();
                for (var i = 0; i < routePolylines.length; i++)
                {
                    map.removeOverlay(routePolylines[i]);
                }
            }
            routeHidden = true;
            break;
        case "incidents":
            if (!GetRouteSelect())
            {
                incidentManager.clearMarkers();
            }
            incidentsHidden = true;
            break;
        case "cameras":
            if (!GetRouteSelect())
            {
                cameraManager.clearMarkers();
            }
            camerasHidden = true;
            break;
        case "signs":
            if (!GetRouteSelect())
            {
                signManager.clearMarkers();
            }
            signsHidden = true;
            break;
        case "speedlinks":
            if (!GetRouteSelect())
            {
                for (var i = 0; i < speedLinkPolylines.length; i++)
                {
                    map.removeOverlay(speedLinkPolylines[i]);
                }
            }
            speedLinksHidden = true;
            break;
        default:
            return;
    }

    GEvent.trigger(window, "markergrouphidden", groupName);
}

/**
 * Shows markers or polylines in a given category on the map.
 */
/* void */function ShowMarkerGroup(/* string */groupName)
{
    // debugger;

    if (!GetMarkerGroupHidden(groupName))
    {
        return;
    }
    switch (groupName)
    {
        case "route":
            routeHidden = false;
            if (!GetRouteSelect())
            {
                DrawRoute();
            }
            break;
        case "incidents":
            incidentsHidden = false;
            if (!GetRouteSelect())
            {
                incidentManager.addMarkers(shownIncidentMarkers, 0);
                incidentManager.refresh();
            }
            break;
        case "cameras":
            camerasHidden = false;
            if (!GetRouteSelect())
            {
                cameraManager.addMarkers(shownCameraMarkers, 11);
                cameraManager.refresh();
            }
            break;
        case "signs":
            signsHidden = false;
            if (!GetRouteSelect())
            {
                signManager.addMarkers(shownSignMarkers, 11);
                signManager.refresh();
            }
            break;
        case "speedlinks":
            speedLinksHidden = false;
            if (!GetRouteSelect())
            {
                DrawSpeedLinks();
            }
            break;
        default:
            return;
    }

    GEvent.trigger(window, "markergroupshown", groupName);
}

/* bool */function GetMarkerGroupHidden(/* string */groupName)
{
    switch (groupName)
    {
        case "route":
            return routeHidden;
        case "incidents":
            return incidentsHidden;
        case "cameras":
            return camerasHidden;
        case "signs":
            return signsHidden;
        case "speedlinks":
            return speedLinksHidden;
        default:
            return false;
    }
}

/**
 * Hides a marker, as identified by markerID.
 */
/* void */function HideMarker(/* string[] */markerIDs, /* bool */noEvent)
{
    // debugger;

    if (markerIDs.push == null)
    {
        /* string */var temp = markerIDs;
        markerIDs = new Array();
        markerIDs.push(temp);
    }

    /* com.ibigroup.GoogleMaps.Marker[] */var markers = new Array();
    /* com.ibigroup.GoogleMaps.Marker */var marker;
    /* object<string, bool> */var removalByMarkerID = new Object();
    /* number */var removalCount = 0;
    for (var i = 0; i < markerIDs.length; i++)
    {
        if (i > 0 && markerIDs[i].substring(0, 4) != markerIDs[i - 1].substring(0, 4))
        {
            // only proceeed if all markers are of the same type
            return;
        }
        marker = com.ibigroup.GoogleMaps.Marker.prototype.getMarkerByID(markerIDs[i]);
        if (marker != null && !marker.isHidden())
        {
            removalByMarkerID[markerIDs[i]] = true;
            removalCount++;
            markers.push(marker);
        }
    }

    if (removalCount == 0)
    {
        return;
    }

    if (markerIDs[0].substring(0, 8) == "incident")
    {
        // hide incident markers
        for (var i = shownIncidentMarkers.length - 1; i >= 0 && removalCount > 0; i--)
        {
            if (removalByMarkerID[shownIncidentMarkers[i].getID()])
            {
                delete removalByMarkerID[shownIncidentMarkers[i].getID()];
                removalCount--
                shownIncidentMarkers.splice(i, 1);
            }
        }
        if (!GetRouteSelect() && !incidentsHidden)
        {
            for (var i = 0; i < markers.length; i++)
            {
                incidentManager.removeMarker(markers[i]);
            }
        }
    }
    else if (markerIDs[0].substring(0, 6) == "camera")
    {
        // hide camera marker
        for (var i = shownCameraMarkers.length - 1; i >= 0 && removalCount > 0; i--)
        {
            if (removalByMarkerID[shownCameraMarkers[i].getID()])
            {
                delete removalByMarkerID[shownCameraMarkers[i].getID()];
                removalCount--
                shownCameraMarkers.splice(i, 1);
            }
        }
        if (!GetRouteSelect() && !camerasHidden)
        {
            for (var i = 0; i < markers.length; i++)
            {
                cameraManager.removeMarker(markers[i]);
            }
        }
    }
    else if (markerIDs[0].substring(0, 4) == "sign")
    {
        // hide sign marker
        for (var i = shownSignMarkers.length - 1; i >= 0 && removalCount > 0; i--)
        {
            if (removalByMarkerID[shownSignMarkers[i].getID()])
            {
                delete removalByMarkerID[shownSignMarkers[i].getID()];
                removalCount--
                shownSignMarkers.splice(i, 1);
            }
        }
        if (!GetRouteSelect() && !signsHidden)
        {
            for (var i = 0; i < markers.length; i++)
            {
                signManager.removeMarker(markers[i]);
            }
        }
    }
    else
    {
        return;
    }

    for (var i = 0; i < markers.length; i++)
    {
        markers[i].hide();
        if (!noEvent)
        {
            GEvent.trigger(window, "markerhidden", markers[i].getID());
        }
    }
}

/**
 * Shows a marker, as identified by markerID.
 */
/* void */function ShowMarker(/* string[] */markerIDs, /* bool */noEvent)
{
    // debugger;

    if (markerIDs.push == null)
    {
        /* string */var temp = markerIDs;
        markerIDs = new Array();
        markerIDs.push(temp);
    }

    /* com.ibigroup.GoogleMaps.Marker[] */var markers = new Array();
    /* com.ibigroup.GoogleMaps.Marker */var marker;
    for (var i = 0; i < markerIDs.length; i++)
    {
        if (i > 0 && markerIDs[i].substring(0, 4) != markerIDs[i - 1].substring(0, 4))
        {
            // only proceeed if all markers are of the same type
            return;
        }
        marker = com.ibigroup.GoogleMaps.Marker.prototype.getMarkerByID(markerIDs[i]);
        if (marker != null && marker.isHidden())
        {
            markers.push(marker);
        }
    }

    if (markerIDs[0].substring(0, 8) == "incident")
    {
        shownIncidentMarkers = shownIncidentMarkers.concat(markers);
        if (!GetRouteSelect() && !incidentsHidden)
        {
            incidentManager.addMarkers(markers, 0);
            incidentManager.refresh();
        }
    }
    else if (markerIDs[0].substring(0, 6) == "camera")
    {
        shownCameraMarkers = shownCameraMarkers.concat(markers);
        if (!GetRouteSelect() && !camerasHidden)
        {
            cameraManager.addMarkers(markers, 11);
            cameraManager.refresh();
        }
    }
    else if (markerIDs[0].substring(0, 4) == "sign")
    {
        shownSignMarkers = shownSignMarkers.concat(markers);
        if (!GetRouteSelect() && !signsHidden)
        {
            signManager.addMarkers(markers, 11);
            signManager.refresh();
        }
    }
    else
    {
        return;
    }

    for (var i = 0; i < markers.length; i++)
    {
        markers[i].show();
        if (!noEvent)
        {
            GEvent.trigger(window, "markershown", markers[i].getID());
        }
    }
}

/**
 *
 */
/* void */function ShowDetailView(/* string */incidentID)
{
     //debugger;

    // clear iframe removal timeout
    clearTimeout(RemoveDetailViewTimeout);
    RemoveDetailViewTimeout = null;

    /* string */var startRoadway = "";
    /* number */var startMile = 0;
    /* string */var endRoadway = "";
    /* number */var endMile = 0;

    if (incidentID != null)
    {
        // if an incidentID was passed, see that the incident exists
        /* object */var incident = incidentsByID[incidentID];
        if (incident != null)
        {
            // get dimensions of the incident route
            startRoadway = incident.routeStartRoadway;
            startMile = incident.routeStartMile;
            endRoadway = incident.routeEndRoadway;
            endMile = incident.routeEndMile;
        }
    }
    else if (profile.activeRoute != null)
    {
        // get dimensions of the active route
        /* Route */var activeRoute = profile.activeRoute;
        /* RouteSegment */var firstSegment = activeRoute.segments[0];
        /* RouteSegment */var lastSegment = activeRoute.segments[activeRoute.segments.length - 1];
        startRoadway = firstSegment.roadway;
        startMile = firstSegment.startMile;
        endRoadway = lastSegment.roadway;
        endMile = lastSegment.endMile;
    }
    
    /*
    if (startRoady == endRoadway && startMile == endMile)
    {
        return;
    }
    */

    if (startRoadway != "")
    {
        /* HTMLElement */var detailViewDiv = document.getElementById("DetailViewDiv");

        if (detailViewDiv.style.display == "block")
        {
            HideDetailView();
            if (RemoveDetailViewTimeout != null)
            {
                clearTimeout(RemoveDetailViewTimeout);
                RemoveDetailViewTimeout = setTimeout
                (
                    "console.log('cleaning up detail view');" + 
                    "clearTimeout(RemoveDetailViewTimeout);" +
                    "RemoveDetailViewTimeout = null;" +
                    "document.getElementById('DetailViewPageDiv').innerHTML = '';" +
                    "document.getElementById('DetailViewDiv').style.display = 'none';" + 
                    "ShowDetailView(" + ((incidentID != null) ? "'" + incidentID + "'" : "") + ");",
                    250
                );
                return;
            }
        }
        else
        {
            ShowDetailViewLoading();

            document.getElementById("DetailViewPageDiv").innerHTML = '<iframe id="DetailViewIFrame" name="DetailViewIFrame" src="DetailView.aspx?startRoad=' + startRoadway + '&startMile=' + startMile + '&endRoad=' + endRoadway + '&endMile=' + endMile + ((incidentID != null) ? '&incidentID=' + incidentID : '') + '" scrolling="no" frameborder="0"></iframe>';

            // show detail view div
            detailViewDiv.style.display = "block";
        }
    }
}

/**
 *
 */
/* void */function HideDetailView()
{
    // clear iframe removal timeout
    clearTimeout(RemoveDetailViewTimeout);
    RemoveDetailViewTimeout = null;

    /* HTMLElement */var detailViewDiv = document.getElementById("DetailViewDiv");
    if (detailViewDiv.style.display == "block")
    {
        // if the detail view is currently being displayed

        /* HTMLElement */var detailViewIFrame = document.getElementById("DetailViewIFrame");
        if (detailViewIFrame != null)
        {
            // if the iframe exists, redirect it to an harmless page
            detailViewIFrame.src = "javascript:false";
        }
        
        // set the iframe to be removed in 250 milliseconds
        RemoveDetailViewTimeout = setTimeout
        (
            "console.log('cleaning up detail view');" + 
            "clearTimeout(RemoveDetailViewTimeout);" +
            "RemoveDetailViewTimeout = null;" +
            "document.getElementById('DetailViewPageDiv').innerHTML = '';" +
            "document.getElementById('DetailViewDiv').style.display = 'none';",
            250
        ); 
    }
}

/**
 *
 */
/* void */function ShowDetailViewLoading()
{
    document.getElementById("DetailViewLoadingDiv").style.display = "block";
    document.getElementById("DetailViewLoadingImg").src = "images/icons/ajax-loader.gif";
}

/**
 *
 */
/* void */function HideDetailViewLoading()
{
    document.getElementById("DetailViewLoadingDiv").style.display = "none";
    document.getElementById("DetailViewLoadingImg").src = "images/pix.gif";
}

/**
 * Gets the current status of incidents, cameras, signs and speed links.
 */
/* void */function GetStatus()
{
    if (GetStatusTimeout != null)
    {
        clearTimeout(GetStatusTimeout);
        GetStatusTimeout = null;
    }

    if (asyncBusy["GetStatus"])
    {
        // if a GetStatus async call is already in progress, return
        return;
    }

    asyncBusy["GetStatus"] = true;
    PageMethods.GetStatus(getstatus_success, getstatus_failure);
}

/**
 * AJAX call to retrieve possible route locations based on given start
 * parameters.
 */
/* void */function GetRouteOptions(/* string */roadway, /* string */direction, /* number */mile)
{
    if (asyncBusy["GetRouteOptions"])
    {
        // if a GetStatus async call is already in progress, return
        console.log("GetRouteOptions busy");
        return;
    }

    // store route start properties
    routeStartRoadway = roadway;
    routeStartMile = mile;

    asyncBusy["GetRouteOptions"] = true;
    PageMethods.GetRouteOptions(roadway, direction, mile, getrouteoptions_success, getrouteoptions_failure);

    AddLeftPanelMessage("Retrieving Route Options...");
}

/**
 * AJAX call to save a route based on provided parameters.
 */
/* void */function SaveRoute(/* string */startRoadway, /* number */startMile, /* string */endRoadway, /* number */endMile)
{
    // debugger;

    if (asyncBusy["SaveRoute"])
    {
        // if a GetStatus async call is already in progress, return
        console.log("SaveRoute busy");
        return;
    }

    asyncBusy["SaveRoute"] = true;

    PageMethods.SaveRoute(startRoadway, startMile, endRoadway, endMile, saveroute_success, saveroute_failure);

    AddLeftPanelMessage("Updating profile...");
}

/* void */function DisplayTripRoute(/* string */startRoadway, /* number */startMile, /* string */destination)
{
    var routeName = startRoadway + " @ Exit " + startMile + " to " + destination;
    
    for (var i = 0; i <= profile.routeNames.length; i++)
    {
        if (profile.routeNames[i] == routeName)
        {
            SelectRoute(routeName);
            return;
        }
    }

    PageMethods.CreateTripDisplayRoute(startRoadway, startMile, destination, saveroute_success, saveroute_failure);
}
/**
 * AJAX call to rename the active route.
 */
/* void */function RenameRoute(/* string */routeName)
{
    if (profile == null || profile.activeRoute == null)
    {
        return;
    }

    if (asyncBusy["RenameRoute"])
    {
        console.log("RenameRoute busy");
        return;
    }

    asyncBusy["RenameRoute"] = true;

    PageMethods.RenameRoute(routeName, renameroute_success, renameroute_failure);
    
    AddLeftPanelMessage("Updating profile...");
}

/**
 * AJAX call to delete the active route.
 */
/* void */function DeleteRoute(/* string */name)
{
    if (profile == null || profile.activeRoute == null)
    {
        return;
    }

    if (asyncBusy["DeleteRoute"])
    {
        console.log("DeleteRoute busy");
        return;
    }

    asyncBusy["DeleteRoute"] = true;

    PageMethods.DeleteRoute(deleteroute_success, deleteroute_failure);

    AddLeftPanelMessage("Updating profile...");
}

/**
 * AJAX call to select a new active route.s
 */
/* void */function SelectRoute(/* string */routeName)
{
    if (profile == null || profile.routeNames == null)
    {
        return;
    }
    
    if (profile.activeRouteName == routeName)
    {
        return;
    }

    if (routeName != "")
    {
        for (var i = 0; i <= profile.routeNames.length; i++)
        {
            if (i == profile.routeNames.length)
            {
                return;
            }
            if (profile.routeNames[i] == routeName)
            {
                break;
            }
        }
    }

    if (asyncBusy["SelectRoute"])
    {
        console.log("DeleteRoute busy");
        return;
    }

    asyncBusy["SelectRoute"] = true;

    PageMethods.SelectRoute(routeName, selectroute_success, selectroute_failure);
    
    //AddLeftPanelMessage("Updating profile...");
}

/**
 * AJAX call to save user's speed color choices.
 */
/* void */function SaveSpeedColors(/* string[] */colors)
{
    if (asyncBusy["SaveSpeedColors"])
    {
        // if a GetStatus async call is already in progress, return
        console.log("SaveSpeedColors busy");
        return;
    }

    asyncBusy["SaveSpeedColors"] = true;

    PageMethods.SaveSpeedColors(colors, savespeedcolors_success, savespeedcolors_failure);

    AddLeftPanelMessage("Updating profile...");
}

/*****************************************************************************
 * EVENT HANDLERS
 *****************************************************************************/
/**
 * Load handler for the page.
 */
/* void */function page_load()
{
    // debugger;

    dojo.registerModulePath("com", "../../com");
    
    dojo.require("com.ibigroup.GoogleMaps.Map");
    dojo.require("com.smartsunguide.MainControl");
    dojo.require("com.smartsunguide.CountyControl");
    dojo.require("com.smartsunguide.MessageControl");

    dojo.require("com.ibigroup.GoogleMaps.MarkerManager");

    dojo.require("com.smartsunguide.Route");

    counties =
    {
        indianriver: new GPolygon([new GLatLng(27.822529, -80.888214), new GLatLng(27.822529, -80.507640), new GLatLng(27.855316, -80.486869), new GLatLng(27.855316, -79.986648), new GLatLng(27.559416, -79.986648), new GLatLng(27.559416, -80.777664), new GLatLng(27.643542, -80.777664), new GLatLng(27.643542, -80.880146), new GLatLng(27.733528, -80.880146), new GLatLng(27.733528, -80.888214), new GLatLng(27.822529, -80.888214)], "#ff0000", 2, 0.5),
        saintlucie: new GPolygon([new GLatLng(27.559416, -80.732688), new GLatLng(27.559416, -79.986648), new GLatLng(27.260428, -79.986648), new GLatLng(27.260428, -80.281219), new GLatLng(27.205785, -80.281219), new GLatLng(27.205785, -80.732688), new GLatLng(27.559416, -80.732688)], "#ff0000", 2, 0.5),
        martin: new GPolygon([new GLatLng(27.205785, -80.732688), new GLatLng(27.205785, -80.281219), new GLatLng(27.260428, -80.281219), new GLatLng(27.260428, -79.986648), new GLatLng(26.970731, -79.986648), new GLatLng(26.970731, -80.128097), new GLatLng(26.958491, -80.128097), new GLatLng(26.958491, -80.890274), new GLatLng(27.137062, -80.732688), new GLatLng(27.205785, -80.732688)], "#ff0000", 2, 0.5),
        palmbeach: new GPolygon([new GLatLng(26.958491, -80.890274), new GLatLng(26.958491, -80.128097), new GLatLng(26.970731, -80.128097), new GLatLng(26.970731, -79.986648), new GLatLng(26.321267, -79.986648), new GLatLng(26.321267, -80.092048), new GLatLng(26.327883, -80.092048), new GLatLng(26.327883, -80.203971), new GLatLng(26.335268, -80.228347), new GLatLng(26.335268, -80.890274), new GLatLng(26.958491, -80.890274)], "#ff0000", 2, 0.5),
        broward: new GPolygon([new GLatLng(26.335268, -80.890274), new GLatLng(26.335268, -80.228347), new GLatLng(26.327883, -80.203971), new GLatLng(26.327883, -80.092048), new GLatLng(26.321267, -80.092048), new GLatLng(26.321267, -79.986648), new GLatLng(25.971008, -79.986648), new GLatLng(25.971008, -80.294909), new GLatLng(25.964835, -80.294909), new GLatLng(25.964835, -80.706802), new GLatLng(25.978724, -80.706802), new GLatLng(25.978724, -80.890274), new GLatLng(26.335268, -80.890274)], "#ff0000", 2, 0.5)
    };

    map = new com.ibigroup.GoogleMaps.Map(document.getElementById("mapDiv"), {minZoom: 8, counties: counties}); // , panBounds: new GLatLngBounds(new GLatLng(26.054, -80.352), new GLatLng(26.254, -80.152))});
    map.setCenter(new GLatLng(26.1705533, -80.274178), 11);
    
    
    locationManager = new MarkerManager(map);
    routeManager = new MarkerManager(map);
    incidentManager = new MarkerManager(map);
    cameraManager = new MarkerManager(map);
    signManager = new MarkerManager(map);

    // map display marker managers
    // routeManager = new com.ibigroup.GoogleMaps.MarkerManager(map, {minClusterCount: 1});
    // incidentManager = new com.ibigroup.GoogleMaps.MarkerManager(map);
    // cameraManager = new com.ibigroup.GoogleMaps.MarkerManager(map, {minDisplayZoom: 11});
    // signManager = new com.ibigroup.GoogleMaps.MarkerManager(map, {minDisplayZoom: 11});
    // weatherManager = new com.ibigroup.GoogleMaps.MarkerManager(map);
    // speedManager = new com.ibigroup.GoogleMaps.MarkerManager(map);

    // add map controls
    messageControl = new com.smartsunguide.MessageControl();
    map.addControl(messageControl);
    mainControl = new com.smartsunguide.MainControl();
    map.addControl(mainControl);
    map.addControl(new com.smartsunguide.CountyControl());
    map.addControl(new GScaleControl());

    // subscribe to changes in the map's zoom level
    GEvent.addListener(map, "zoomend", map_zoomend);
    map_zoomend();

    // subscribe to event that fires when an updatepanel finishes loading
    // Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(ajax_pageLoaded);
    
    // Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);

    // fetch roadway information from the server
    asyncBusy["GetStructure"] = true;
    PageMethods.GetStructure(getstructure_success, getstructure_failure);
    
    // fetch profile information from the server
    //asyncBusy["GetProfile"] = true;
    //PageMethods.GetProfile(getprofile_success, getprofile_failure);
    GetProfile();

    // get the latest state of incidents, cameras, signs and travel speeds
    GetStatus();

    SetLeftPanel(0, true);
}

/**
 * Unload handler for the page.
 */
/* void */function page_unload()
{
    // debugger;

    GUnload();
}

function GetProfile() {
    asyncBusy["GetProfile"] = true;
    PageMethods.GetProfile(getprofile_success, getprofile_failure);
}

/**
 * Zoomend handler for the map.
 */ 
function map_zoomend(/* number */oldZoom, /* number */newZoom)
{
    // debugger;

    var zoom = map.getZoom();

    if (zoom < 11)
    {
        if (zoomDiv == null)
        {
            zoomDiv = messageControl.addMessage('Signs and cameras are not visible at this zoom level. <span class="link" onclick="map.setCenter(new GLatLng(26.18, -80.22), 11);">Zoom in</span> to see those items.');
        }
    }
    else if (zoomDiv != null)
    {
        messageControl.clearMessage(zoomDiv);
        zoomDiv = null;
    }
}

/**
 * Load handler for GDirections objects.
 */
/* void */function gdirections_load(/* GDirections */directions)
{
    // debugger;

    console.log("gdirections_load");
    // console.log(directions);
    
    /* GPolyline */var polyline = directions.getPolyline();
    if (directions._polylineName != null)
    {
        savedRoutePolylines[directions._polylineName] = polyline;
    }
    routePolylines.push(polyline);
    map.addOverlay(polyline);
    
    // remove load listener
    GEvent.removeListener(directions._loadListener);
    for (var i = 0; i < GDirectionsLoadListeners.length; i++)
    {
        if (GDirectionsLoadListeners[i] == directions._loadListener)
        {
            GDirectionsLoadListeners.splice(i, 1);
            break;
        }
    }
}

/**
 * Success handler for GetStructure AJAX call.
 */
/* void */function getstructure_success(/* Structure */data)
{
    //debugger;

    console.log("getstructure_success");
    console.log(data);

    delete asyncBusy["GetStructure"];

    dojo.require("com.smartsunguide.LocationMarker");
    dojo.require("com.smartsunguide.CameraMarker");
    dojo.require("com.smartsunguide.SignMarker");

    // LOCATIONS
    /* number */var avgLat;
    /* number */var avgLng;
    /* com.smartsunguide.LocationMarker */var locationMarker;
    for (var i = 0; i < data.locations.length; i++)
    {
        avgLat = avgLng = 0;
        for (var j = 0; j < data.locations[i].length; j++)
        {
            // save individual locations in global index
            data.locations[i][j].groupIndex = i;
            locationsByID[data.locations[i][j].ID] = data.locations[i][j];

            avgLat += data.locations[i][j].lat;
            avgLng += data.locations[i][j].lng;
        }
        avgLat /= data.locations[i].length;
        avgLng /= data.locations[i].length;

        locationMarker = new com.smartsunguide.LocationMarker(new GLatLng(avgLat, avgLng), {data: data.locations[i]});
        locationMarkers.push(locationMarker);
    }
    if (GetRouteSelect() && routeStartRoadway == null)
    {
        locationManager.addMarkers(locationMarkers, 0);
        locationManager.refresh();
    }
    
    // CAMERAS
    /* com.smartsunguide.CameraMarker */var cameraMarker;
    for (var i = 0; i < data.cameras.length; i++)
    {
        data.cameras[i].unavailable = (cameraUnavailableByID[data.cameras[i].uniqueId] == true);
        camerasByID[data.cameras[i].uniqueId] = data.cameras[i];

        cameraMarker = new com.smartsunguide.CameraMarker(new GLatLng(data.cameras[i].lat, data.cameras[i].lng), {data: data.cameras[i]});

        cameraMarkers.push(cameraMarker);
        shownCameraMarkers.push(cameraMarker);
    }
    if (!GetRouteSelect() && !camerasHidden)
    {
        cameraManager.addMarkers(shownCameraMarkers, 11);
        cameraManager.refresh();
    }

    // SIGNS
    /* com.smartsunguide.SignMarker */var SignMarker;
    for (var i = 0; i < data.signs.length; i++)
    {
        data.signs[i].unavailable = (signUnavailableByID[data.signs[i].uniqueId] == true);
        data.signs[i].message = (signMessageByID[data.signs[i].uniqueId] == null) ? "" : signMessageByID[data.signs[i].uniqueId];
        signsByID[data.signs[i].uniqueId] = data.signs[i];

        signMarker = new com.smartsunguide.SignMarker(new GLatLng(data.signs[i].lat, data.signs[i].lng), {data: data.signs[i]});

        signMarkers.push(signMarker);
        shownSignMarkers.push(signMarker);
    }
    if (!GetRouteSelect() && !signsHidden)
    {
        signManager.addMarkers(shownSignMarkers, 11);
        signManager.refresh();
    }

    structure = data;

    DrawRoute();

    GEvent.trigger(window, "structureupdated");
}

/**
 * Failure handler for GetStructure AJAX call.
 */
/* void */function getstructure_failure(error, userContext, methodName)
{
    //debugger;

    console.log("getstructure_failure");
    if (typeof data != "undefined") console.log(data);

    delete asyncBusy["GetStructure"];
}

/**
 * Success handler for the GetProfile AJAX call.
 */
/* void */function getprofile_success(/* ProfileStatus */data)
{
    console.log("getprofile_success");
    console.log(data);
    
    delete asyncBusy["GetProfile"];

    // store old route speed colors
    /* string[] */var speedColors = profile.speedColors.slice();

    profile = data;

    // copy active route status to status object
    if (mystatus == null)
    {
        mystatus = new Object();
    }
    mystatus.activeRouteStatus = profile.activeRouteStatus;

    /* bool */var colorChange = (profile.speedColors.length != speedColors.length);
    for (var i = 0; i < profile.speedColors.length && !colorChange; i++)
    {
        if (profile.speedColors[i] != speedColors[i])
        {
            colorChange = true;
        }
    }
    
    if (colorChange)
    {
        DrawSpeedLinks();
    }
    else
    {
        DrawRoute();
    }

    if (colorChange)
    {
        GEvent.trigger(window, "speedcolorchanged");
    }
    console.log("triggering profileupdated event");
    GEvent.trigger(window, "profileupdated");
}

/**
 * Failure handler for the GetProfile AJAX call.
 */
/* void */function getprofile_failure()
{
    console.log("GetProfile_failure");
    console.log(arguments);

    delete asyncBusy["GetProfile"];
}

/**
 * Success handler for GetStatus AJAX call.
 */
/* void */function getstatus_success(/* GetStatusOutput */data)
{
    //debugger;

    console.log("getstatus_success");
    console.log(data);

    delete asyncBusy["GetStatus"];

    dojo.require("com.smartsunguide.IncidentMarker");

    // INCIDENTS
    // remove old markers
    /* object<string, bool> */var hiddenIncidents = new Object();
    incidentManager.clearMarkers();
    for (var i = 0; i < incidentMarkers.length; i++)
    {
        if (incidentMarkers[i].isHidden())
        {
            hiddenIncidents[incidentMarkers[i].getIncidentID()] = true;
        }
        incidentMarkers[i].purge();
    }
    incidentMarkers = new Array();
    shownIncidentMarkers = new Array();

    map.clearOverlays();

    // add new markers
    /* bool */var hidden;
    /* com.smartsunguide.IncidentMarker */var incidentMarker;
    incidentsByID = new Object();
    for (var i = 0; i < data.incidents.length; i++)
    {
        incidentsByID[data.incidents[i].uniqueId] = data.incidents[i];

        hidden = (hiddenIncidents[data.incidents[i].uniqueId] == true);
        incidentMarker = new com.smartsunguide.IncidentMarker(new GLatLng(data.incidents[i].lat, data.incidents[i].lng), {data: data.incidents[i], hidden: hidden});

        incidentMarkers.push(incidentMarker);
        if (!incidentMarker.isHidden())
        {
            shownIncidentMarkers.push(incidentMarker);
        }
    }
    if (!GetRouteSelect() && !incidentsHidden)
    {
        incidentManager.addMarkers(shownIncidentMarkers, 0);
        incidentManager.refreshWithOption(false);
    }
    
    // CAMERAS
    /* com.smartsunguide.CameraMarker */var cameraMarker;
    /* number */var shownIndex = null;
    cameraUnavailableByID = new Object();
    for (var i = 0; i < data.cameras.length; i++)
    {
        cameraUnavailableByID[data.cameras[i].uniqueId] = false;
    }
    for (var i = 0; i < cameraMarkers.length; i++)
    {
        if (cameraMarkers[i].getUnavailable() != (cameraUnavailableByID[cameraMarkers[i].getCameraID()] == true))
        {
            // replace camera marker
            shownIndex = null;
            if (!GetRouteSelect() && !camerasHidden && !cameraMarkers[i].isHidden())
            {
                cameraManager.removeMarker(cameraMarkers[i]);
                for (var j = 0; j < shownCameraMarkers.length; j++)
                {
                    if (shownCameraMarkers[j] == cameraMarkers[i])
                    {
                        shownIndex = j;
                        break;
                    }
                }
            }
            cameraMarkers[i] = cameraMarkers[i].setUnavailable(cameraUnavailableByID[cameraMarkers[i].getCameraID()] == true);
            if (!GetRouteSelect() && !camerasHidden && !cameraMarkers[i].isHidden())
            {
                cameraManager.addMarker(cameraMarkers[i], 11);
                if (shownIndex != null)
                {
                    shownCameraMarkers[shownIndex] = cameraMarkers[i];
                }
                else
                {
                    shownCameraMarkers.push(cameraMarkers[i]);
                }
            }
        }
    }
    if (!GetRouteSelect())
    {
        cameraManager.refreshWithOption(false);
    }
    
    // SIGNS
    /* com.smartsunguide.SignMarker */var signMarker = null;
    signUnavailableByID = new Object();
    signMessageByID = new Object();
    for (var i = 0; i < data.signs.length; i++)
    {
        if (!data.signs[i].available)
        {
            signUnavailableByID[data.signs[i].uniqueId] = true;
        }
        if (data.signs[i].message != "")
        {
            signMessageByID[data.signs[i].uniqueId] = data.signs[i].message;
        }
    }
    /* bool */var signUnavailable;
    /* string */var signMessage;
    for (var i = 0; i < signMarkers.length; i++)
    {
        signUnavailable = (signUnavailableByID[signMarkers[i].getSignID()] == true);
        signMessage = ((signMessageByID[signMarkers[i].getSignID()] == null) ? "" : signMessageByID[signMarkers[i].getSignID()])
        if (signMarkers[i].getUnavailable() != signUnavailable || signMarkers[i].getMessage() != signMessage)
        {
            // replace sign marker
            shownIndex = null;
            if (!GetRouteSelect() && !signsHidden && !signMarkers[i].isHidden())
            {
                signManager.removeMarker(signMarkers[i]);
                for (var j = 0; j < shownSignMarkers.length; j++)
                {
                    if (shownSignMarkers[j] == signMarkers[i])
                    {
                        shownIndex = j;
                        break;
                    }
                }
            }
            signMarkers[i] = signMarkers[i].setStatus(signUnavailable, signMessage);
            if (!GetRouteSelect() && !signsHidden && !signMarkers[i].isHidden())
            {
                signManager.addMarker(signMarkers[i], 11);
                if (shownIndex != null)
                {
                    shownSignMarkers[shownIndex] = signMarkers[i];
                }
                else
                {
                    shownSignMarkers.push(signMarkers[i]);
                }
            }
        }
    }
    if (!GetRouteSelect())
    {
        signManager.refreshWithOption(false);
    }
    
    mystatus = data;
    
    // draw speed link lines
    DrawSpeedLinks();

    // retrieve status again in one minute
    GetStatusTimeout = setTimeout("GetStatus();", 60000);
    
    GEvent.trigger(window, "statusupdated");
}

/**
 * Failure handler for GetStatus AJAX call.
 */
/* void */function getstatus_failure()
{
    // debugger;

    console.log("getstatus_failure");
    console.log(arguments);
    
    delete asyncBusy["GetStatus"];

    // retrieve status again in one minute
    GetStatusTimeout = setTimeout("GetStatus();", 60000);
}

/**
 * Success handler for the GetRouteOptions AJAX call.
 */
/* void */function getrouteoptions_success(/* RouteOption[] */data)
{
    // debugger;

    console.log("getrouteoptions_success");
    console.log(data);

    delete asyncBusy["GetRouteOptions"];

    RemoveLeftPanelMessage("Retrieving Route Options...");

    if (data == null || data.length == 0 || !GetRouteSelect() || routeStartRoadway != data[0].roadway || routeStartMile != data[0].startMile)
    {
        return;
    }

    dojo.require("com.smartsunguide.RouteMarker");
    dojo.require("com.smartsunguide.RouteStartMarker");

    // clear location markers
    locationManager.clearMarkers();
    
    // group retrieved location IDs by marker group
    /* number */var firstIndex = null;
    /* Location */var location;
    /* object<number, Location[]> */var locations = new Object();
    /* GDirections */var directions;
    /* GLatLng[] */var latLngs;

    // retrieve location IDs from data, grouping them by marker
    for (var i = 0; i < data.length; i++)
    {
        for (var j = 0; j < data[i].locations.length; j++)
        {
            location = locationsByID[data[i].locations[j]];
            if (location != null)
            {
                if (locations[location.groupIndex] == null)
                {
                    locations[location.groupIndex] = new Array();
                }
                locations[location.groupIndex].push(location);
                if (firstIndex == null)
                {
                    firstIndex = location.groupIndex;
                }
            }
        }

        // load polylines
        latLngs = new Array();
        for (var j = 0; j < data[i].points.length; j++)
        {
            latLngs.push(new GLatLng(data[i].points[j][0], data[i].points[j][1]));
        }

        directions = new GDirections();
        directions.loadFromWaypoints(latLngs, {getPolyline: true});
        directions._loadListener = GEvent.addListener(directions, "load", gdirections_load);
        GDirectionsLoadListeners.push(directions._loadListener);
    }

    // remove old markers
    for (var i = 0; i < routeMarkers.length; i++)
    {
        routeMarkers[i].purge();
    }
    routeMarkers = new Array();

    // add new markers
    /* number */var avgLat;
    /* number */var avgLng;
    for (var index in locations)
    {
        avgLat = avgLng = 0;
        for (var i = 0; i < locations[index].length; i++)
        {
            avgLat += locations[index][i].lat;
            avgLng += locations[index][i].lng;
        }
        avgLat /= locations[index].length;
        avgLng /= locations[index].length;

        if (index == firstIndex)
        {
            routeMarkers.push(new com.smartsunguide.RouteStartMarker(new GLatLng(avgLat, avgLng), {data: locations[index]}));
        }
        else
        {
            routeMarkers.push(new com.smartsunguide.RouteMarker(new GLatLng(avgLat, avgLng), {data: locations[index]}));
        }
    }

    // add markers to manager
    routeManager.addMarkers(routeMarkers, 0);
    routeManager.refresh();
}

/**
 * Failure handler for the GetRouteOptions AJAX call.
 */
/* void */function getrouteoptions_failure()
{
    console.log("getrouteoptions_failure");
    console.log(arguments);

    delete asyncBusy["GetRouteOptions"];

    RemoveLeftPanelMessage("Retrieving Route Options...");
}

/**
 * Success handler for the SaveRoute AJAX call.
 */
/* void */function saveroute_success(/* ProfileStatus */data)
{
    // debugger;

    console.log("saveroute_success");
    console.log(data);
    
    delete asyncBusy["SaveRoute"];
    
    RemoveLeftPanelMessage("Updating profile...");

    // stop selecting a route
    SetRouteSelect(false);

    // store old route speed colors
    /* string[] */var speedColors = profile.speedColors.slice();

    profile = data;

    // copy active route status to status object
    if (mystatus == null)
    {
        mystatus = new Object();
    }
    mystatus.activeRouteStatus = profile.activeRouteStatus;

    /* bool */var colorChange = (profile.speedColors.length != speedColors.length);
    for (var i = 0; i < profile.speedColors.length && !colorChange; i++)
    {
        if (profile.speedColors[i] != speedColors[i])
        {
            colorChange = true;
        }
    }
    
    if (colorChange)
    {
        DrawSpeedLinks();
    }
    else
    {
        DrawRoute();
    }

    if (colorChange)
    {
        GEvent.trigger(window, "speedcolorchanged");
    }
    GEvent.trigger(window, "profileupdated");
}

/**
 * Failure handler for the SaveRoute AJAX call.
 */
/* void */function saveroute_failure()
{
    // debugger;

    console.log("saveroute_failure");
    console.log(arguments);

    delete asyncBusy["SaveRoute"];

    RemoveLeftPanelMessage("Updating profile...");
}

/**
 * Succes handler for the RenameRoute AJAX call.
 */
/* void */function renameroute_success(/* ProfileStatus */data)
{
    console.log("renameroute_success");
    console.log(data);

    delete asyncBusy["RenameRoute"];

    RemoveLeftPanelMessage("Updating profile...");

    // store old route speed colors
    /* string[] */var speedColors = profile.speedColors.slice();

    profile = data;

    // copy active route status to status object
    if (mystatus == null)
    {
        mystatus = new Object();
    }
    mystatus.activeRouteStatus = profile.activeRouteStatus;

    /* bool */var colorChange = (profile.speedColors.length != speedColors.length);
    for (var i = 0; i < profile.speedColors.length && !colorChange; i++)
    {
        if (profile.speedColors[i] != speedColors[i])
        {
            colorChange = true;
        }
    }
    
    if (colorChange)
    {
        DrawSpeedLinks();
    }
    else
    {
        DrawRoute();
    }

    if (colorChange)
    {
        GEvent.trigger(window, "speedcolorchanged");
    }
    GEvent.trigger(window, "profileupdated");
}

/**
 * Failure handler for the RenameRoute AJAX call.
 */
/* void */function renameroute_failure()
{
    console.log("renameroute_failure");
    console.log(arguments);

    delete asyncBusy["RenameRoute"];

    RemoveLeftPanelMessage("Updating profile...");
}

/**
 * Succes handler for the DeleteRoute AJAX call.
 */
/* void */function deleteroute_success(/* ProfileStatus */data)
{
    console.log("deleteroute_success");
    console.log(data);

    delete asyncBusy["DeleteRoute"];

    RemoveLeftPanelMessage("Updating profile...");

    // store old route speed colors
    /* string[] */var speedColors = profile.speedColors.slice();

    profile = data;

    // copy active route status to status object
    if (mystatus == null)
    {
        mystatus = new Object();
    }
    mystatus.activeRouteStatus = profile.activeRouteStatus;

    /* bool */var colorChange = (profile.speedColors.length != speedColors.length);
    for (var i = 0; i < profile.speedColors.length && !colorChange; i++)
    {
        if (profile.speedColors[i] != speedColors[i])
        {
            colorChange = true;
        }
    }
    
    if (colorChange)
    {
        DrawSpeedLinks();
    }
    else
    {
        DrawRoute();
    }

    if (colorChange)
    {
        GEvent.trigger(window, "speedcolorchanged");
    }
    GEvent.trigger(window, "profileupdated");
}

/**
 * Failure handler for the DeleteRoute AJAX call.
 */
/* void */function deleteroute_failure()
{
    console.log("deleteroute_failure");
    console.log(arguments);

    delete asyncBusy["DeleteRoute"];

    RemoveLeftPanelMessage("Updating profile...");
}

/**
 * Succes handler for the SelectRoute AJAX call.
 */
/* void */function selectroute_success(/* ProfileStatus */data)
{
    console.log("selectroute_success");
    console.log(data);

    delete asyncBusy["SelectRoute"];

    RemoveLeftPanelMessage("Updating profile...");

    // store old route speed colors
    /* string[] */var speedColors = profile.speedColors.slice();

    profile = data;

    // copy active route status to status object
    if (mystatus == null)
    {
        mystatus = new Object();
    }
    mystatus.activeRouteStatus = profile.activeRouteStatus;

    /* bool */var colorChange = (profile.speedColors.length != speedColors.length);
    for (var i = 0; i < profile.speedColors.length && !colorChange; i++)
    {
        if (profile.speedColors[i] != speedColors[i])
        {
            colorChange = true;
        }
    }
    
    if (colorChange)
    {
        DrawSpeedLinks();
    }
    else
    {
        DrawRoute();
    }

    if (colorChange)
    {
        GEvent.trigger(window, "speedcolorchanged");
    }
    GEvent.trigger(window, "profileupdated");
}

/**
 * Failure handler for the SelectRoute AJAX call.
 */
/* void */function selectroute_failure()
{
    console.log("selectroute_failure");
    console.log(arguments);
    
    delete asyncBusy["SelectRoute"];

    RemoveLeftPanelMessage("Updating profile...");
}

/**
 * Success handler for the SaveSpeedColors AJAX call.
 */
/* void */function savespeedcolors_success(/* ProfileStatus */data)
{
    console.log("savespeedcolors_success");
    console.log(data);

    delete asyncBusy["SaveSpeedColors"];

    RemoveLeftPanelMessage("Updating profile...");

    // store old route speed colors
    /* string[] */var speedColors = profile.speedColors.slice();

    profile = data;

    // copy active route status to status object
    if (mystatus == null)
    {
        mystatus = new Object();
    }
    mystatus.activeRouteStatus = profile.activeRouteStatus;

    /* bool */var colorChange = (profile.speedColors.length != speedColors.length);
    for (var i = 0; i < profile.speedColors.length && !colorChange; i++)
    {
        if (profile.speedColors[i] != speedColors[i])
        {
            colorChange = true;
        }
    }
    
    if (colorChange)
    {
        DrawSpeedLinks();
    }
    else
    {
        DrawRoute();
    }

    if (colorChange)
    {
        GEvent.trigger(window, "speedcolorchanged");
    }
    GEvent.trigger(window, "profileupdated");
}

/**
 * Failure handler for the SaveSpeedColors AJAX call.
 */
/* void */function savespeedcolors_failure()
{
    console.log("SaveSpeedColors_failure");
    console.log(arguments);

    delete asyncBusy["SaveSpeedColors"];

    RemoveLeftPanelMessage("Updating profile...");
}

function addListener(evnt, func) {
    GEvent.addListener(window, evnt, 
        function(args) { try { func(args); } catch (e) {} });
}
        

/*****************************************************************************
 * SCRIPT
 *****************************************************************************/
dojo.addOnLoad(page_load);
dojo.addOnUnload(page_unload);

