//requirements:  
//  	<script src="${pageContext.request.contextPath}/site/ajax/prototype.js" type="text/javascript">&#160;</script>
//  	<script src="${pageContext.request.contextPath}/site/ajax/scriptaculous.js" type="text/javascript">&#160;</script>

var reportMap = null;

//================== User Interface Component Control ====================
function hideMarker(pk,link) {
    reportMap.hideMarker(pk);
}           

    
/**
 * builds a map preview pane based on the details provided in the parameters 
 */        
function updateMapPreviewPaneDetails(episodePk,episodeName,startTime,activityType,distance,username,locationName) {
    var mapPreviewPane = document.getElementById('mapPreviewPane');
    var html = '<a class="white" style="text-decoration:underline" href="/trail/activity/' + episodePk + '" title="View the Dashboard for this activity">' + episodeName + '</a>';
    html += " is a " + distance + " "  + activityType.toLowerCase() + " activity performed on " + startTime;
    html += " near " + locationName;
    if(username != null && username.length > 0) {
        html += " by " + '<a style="text-decoration:underline" class="white"  href="http://' + username + '.motionbased.com">' + username + "</a>";
    }
    mapPreviewPane.innerHTML = html;
}


//================== MAP BOUNDS FILTER =======================
var boundaryRectangle;
var boundaryFilterSmartBounds;

//consider moving this to reportMap?
function filterOnMapBoundary(doIt) {
    if(doIt) {
    	setAskToLoadReport(true);
        boundaryFilterSmartBounds= reportMap.getCurrentBounds();
        boundaryRectangle = new MbSimplePolyline();
        boundaryRectangle.rectangleFromSmartBounds(boundaryFilterSmartBounds);
        reportMap.addOverlay(boundaryRectangle);
        
        //remove the previous if it existed
        deleteMapBoundsFilter();
        
        //add the filter
        var smartBounds = boundaryFilterSmartBounds;
        var boundingBoxFilter = new ReportFilter(
            FILTER_TYPE_MAP,
            'Map Boundary',
            'BBOX' + FILTER_OPERATOR_EQUALS + 
            smartBounds.getSouthWest().lng() + "," + 
            smartBounds.getSouthWest().lat() + "," + 
            smartBounds.getNorthEast().lng() + "," + 
            smartBounds.getNorthEast().lat()
        );
        
        boundingBoxFilter.candelete = true;
        var boundingBoxRecordEntryKeyFilter = new ReportFilter(FILTER_TYPE_MAP,'Boundary Restrictions','boundaryRecordEntryKey' + FILTER_OPERATOR_EQUALS + 'trackStart');
        boundingBoxRecordEntryKeyFilter.candelete = false;
        boundingBoxRecordEntryKeyFilter.hidden = true;
        activeReport.reportFilters.addFilter(boundingBoxFilter);
        activeReport.reportFilters.addFilter(boundingBoxRecordEntryKeyFilter);        
        messageBanner.setMessage('Filter added for activities within the red boundary');
    } else {
        if (reportMap != null) {
            reportMap.removeOverlay(boundaryRectangle);
            //remove the previous if it existed
            deleteMapBoundsFilter();
        }
    }
}

function deleteMapBoundsFilter(){
    if (activeReport.reportFilters.numberFilters(FILTER_TYPE_MAP) > 0) {
    	activeReport.reportFilters.deleteFilterByType(FILTER_TYPE_MAP)
    	messageBanner.setMessage("Current map boundary filter removed.");
    }
}

function filterOnCurrentMapBounds() {
    refreshReport();
}

var autoFitOn = true;
function setAutoFit(toOn) {
    autoFitOn = toOn;
    setCurrentMapBoundsAsPreferred();
}

function refreshMap() {
    //attempt to get the system to release some memory
    if(reportMap != null) {
       reportMap.cleanup();
       reportMap = null;
    }

    var zoomToBounds = null;
    if(!autoFitOn) {
       zoomToBounds = preferredBounds;
       $('autoFitMapIndicatorOff').checked = true;
       $('autoFitMapIndicatorOn').checked = false;
    } else {
        $('autoFitMapIndicatorOn').checked = true;
        $('autoFitMapIndicatorOff').checked = false;
    }
    reportMap = new MbReportMap(activeReport,zoomToBounds);
     
    if(preferredBounds == null) {
        setCurrentMapBoundsAsPreferred();
    }
     
    // map boundary filter
    if (activeReport.reportFilters.numberFilters(FILTER_TYPE_MAP) == 2) {
        boundaryRectangle = new MbSimplePolyline();
        boundaryFilterSmartBounds  = new GLatLngBounds();
        for (var f=0; f<activeReport.reportFilters.filters.length; f++) {
	        var filter = activeReport.reportFilters.filters[f];
	        if (filter.type == FILTER_TYPE_MAP && filter.value.substring(0,5) == "BBOX=") {
	        	var points = filter.value.substring(5).split(',');
                boundaryFilterSmartBounds.extend(new GLatLng(points[1], points[0]));
                boundaryFilterSmartBounds.extend(new GLatLng(points[3], points[2]));
	        }
	    }
        boundaryRectangle.rectangleFromSmartBounds(boundaryFilterSmartBounds);
        reportMap.addOverlay(boundaryRectangle);
    }
}

//keeps a memory of the last boundary so the automatic zoom doesn't go crazy.
var preferredBounds = null;

function setCurrentMapBoundsAsPreferred() {
    preferredBounds = reportMap.getCurrentBounds();
}

/**
 * The map customized for MB Reports
 * Given the Results XML this will display listings as markers and provide polylines on request.
 */
function MbReportMap(queryReport,zoomToBounds) {
    this.map = new GMap2( $("map") );
    this.map.setCenter( new GLatLng(37.4419, -122.1419), 4);
    
    this.map.addControl(new GMapTypeControl());
    this.map.addControl(new GLargeMapControl());
    this.map.addControl(new GScaleControl());
    this.map.addControl(new GOverviewMapControl());
    this.map.enableDoubleClickZoom();
    
    this.markers = new Array();
    this.markerRepeatCount = 0;
    this.colors = new MbColorEnumerator();

    this.closeInfoWindow = function() {
        this.getGMap().closeInfoWindow();
    }
    
    this.smartBounds = new GLatLngBounds();
    /**
     * @return GLatLngBounds object containing the boundary of all the markers placed on this map.  
     * @see this.getCurrentBounds() to obtain the SmartBounds of the current view of the map.
     */
    this.getSmartBounds = function() {
        return this.smartBounds;
    }

    /** 
     * @return GLatLngBounds containing the currentboundary of this map.
     */
    this.getCurrentBounds = function() {
        return this.getGMap().getBounds();
    }
    
    /**
     * given the desired zoom level this will adjust it based on the bounds of the map represented.
     * @param int zoomLevel the given desired zoom level
     * @return int zoomLevel the modified zoom level
     */
    this.restrictZoomLevel = function(zoomLevel) {
        var MAX_ZOOM_LEVEL = 15;
        if(zoomLevel > MAX_ZOOM_LEVEL){
            zoomLevel = MAX_ZOOM_LEVEL;
        }
        return zoomLevel;
    }

    /**
     * Add a marker to the map, extend the bounds of the map to include it
     */    
    this.addMarker = function(mbReportMapMarker) {
        this.smartBounds.extend(mbReportMapMarker.smartBounds.getSouthWest());
        this.smartBounds.extend(mbReportMapMarker.smartBounds.getNorthEast());

        this.map.addOverlay(mbReportMapMarker.gMarker);
        this.markers.push(mbReportMapMarker);
    }
    
    var mapData = $('MapData');
    var markers = mapData.getElementsByTagName('tr');
    var markerdata = mapData.getElementsByTagName('td');
    var NUMCOLS = 14;
    for (var m = 0; m < markers.length; m++ ) {
        var pk = markerdata[NUMCOLS*m+0].innerHTML;
        var episodeName = markerdata[NUMCOLS*m+1].innerHTML;
        var trackStartLatitude = markerdata[NUMCOLS*m+2].innerHTML;
        var trackStartLongitude = markerdata[NUMCOLS*m+3].innerHTML;
        var minLatitude = markerdata[NUMCOLS*m+4].innerHTML;
        var minLongitude = markerdata[NUMCOLS*m+5].innerHTML;
        var maxLatitude = markerdata[NUMCOLS*m+6].innerHTML;
        var maxLongitude = markerdata[NUMCOLS*m+7].innerHTML;
        var distance = markerdata[NUMCOLS*m+8].innerHTML;
        var username = markerdata[NUMCOLS*m+9].innerHTML;
        var startTime = markerdata[NUMCOLS*m+10].innerHTML;
        var elevationGain = markerdata[NUMCOLS*m+11].innerHTML;
        var elevationLoss = markerdata[NUMCOLS*m+12].innerHTML;
        var activityType = markerdata[NUMCOLS*m+13].innerHTML;
        var marker = new MbReportMapMarker(this,pk,episodeName,trackStartLatitude,trackStartLongitude,minLatitude,minLongitude,maxLatitude,maxLongitude,distance,username,startTime,elevationGain,elevationLoss,activityType);
        this.addMarker(marker);
    }

    //-------------- Setting the Bounds for the map ------------------
    if(zoomToBounds == null) {
       this.map.setCenter(this.smartBounds.getCenter(), this.map.getBoundsZoomLevel(this.smartBounds));
    } else {
        this.map.setCenter(zoomToBounds.getCenter(), this.map.getBoundsZoomLevel(zoomToBounds));
    }
    
    //after the zoom has been set, register the events in case it changes.
    this.mapMoveStopped = function () { setCurrentMapBoundsAsPreferred(); }
    this.mapZoomChanged = function () { setCurrentMapBoundsAsPreferred(); }
    
    GEvent.bind(this.map, "moveend",  this, this.mapMoveStopped);
    GEvent.bind(this.map, "zoom",  this, this.mapZoomChanged);
        
    this.getMarkerByRecordPk = function(pk) {
        for(var whichRecord = 0; whichRecord < this.markers.length; whichRecord++) {
            var currentMarker = this.markers[whichRecord];
            if(currentMarker.getPk() == pk) {
                return currentMarker;
            }
        }        
        alert(pk + " not found");
    }

    /**removes the marker from the map*/
    this.hideMarker = function(pk) {
        var nextMarker = this.getMarkerByRecordPk(pk);
        nextMarker.hide();
    }
    
    this.hoverMarker = function(pk,hoverOn) {
       var marker = this.getMarkerByRecordPk(pk);
       marker.hover(hoverOn);
    }
    
   /**  
    * Highlights the marker and optionally will zoom to the area and/or show the info window.
    * @param pk is the primarykey matching the record.
    * @param forceZoomTo is a boolean indicating wether or not to zoom to the area given (default is to stay at current zoom).
    * @param forceShowInfoWindow indicates whether or not to show the informational window.  Default behavior is to show the info window if the marker is also the currently highlighted marker.
    */
    this.highlightMarker = function(pk,forceShowInfoWindow,forceZoomTo,forcePreview,forceOverview) {
        var nextMarker = this.getMarkerByRecordPk(pk);

        if(forcePreview) {
            this.zoomToDefault();
        }
        var showPreview = forcePreview;
        
        if(!forceShowInfoWindow && ! forceZoomTo && !forcePreview && !forceOverview) {
            var repeatClick = this.currentlyHighlightedMarker == nextMarker;
            if(repeatClick) {
                this.markerRepeatCount++;
            } else {
                this.markerRepeatCount = 0;
            }
            
            switch(this.markerRepeatCount) {
                case 0:
                    //never clicked before...show the preview if not already zoomed in
                    //the first click might show 
                    var markerZoomLevel =  this.getGMap().getBoundsZoomLevel(nextMarker.getSmartBounds());
                    var currentZoomLevel = this.getGMap().getZoom();
                    
                    //the adder to the zoom in order to force the map blowup...essentially this is the buffer to decide how far the zoom should be out before using the blowups or zooming to scale.
                    //automatically zooming to scale can be quite annoying when viewing from far away.
                    var minForBlowup = 2;
    
                    if(currentZoomLevel > markerZoomLevel + minForBlowup) {
                        showPreview = true;
                        break;
                    } else {
                        //we are already zoomed in so skip the first step next time...continue on to run the next step now.
                        this.markerRepeatCount++;
                    }
                    forceOverview = true;
                    break;
                case 1:
                    forceZoomTo = true;
                    break;
                default:
                    //based on the number of clicks...toggle the info window.
                    if(this.markerRepeatCount % 2 == 0) {
                        forceShowInfoWindow = true;
                    } else {
                        forceZoomTo = true;
                    }
            }//end switch
        }//end if not force

        //now highlight the text for the listings
        if(this.currentlyHighlightedMarker != null) {
            this.currentlyHighlightedMarker.getMapListing().className = 'evenRow';
        }
        
        this.currentlyHighlightedMarker = nextMarker;
        if(forceShowInfoWindow || forceZoomTo || showPreview || forceOverview) {
            //do not zoom and show info window at the same time...doesn't flow well.
/* The info window is valuable and being missed.  This should listen to the parameters and let the GUI developer decide what to ask for.    
        if(forceZoomTo) {
                this.getGMap().closeInfoWindow();
            }*/
            this.currentlyHighlightedMarker.highlightMarker(forceShowInfoWindow,forceZoomTo,showPreview,forceOverview);
        }
        
        this.currentlyHighlightedMarker.getMapListing().className = 'rowhover';
    }

    this.getGMap = function() {
        return this.map;
    }

    this.addOverlay = function(overlay) {
        var gOverlay = overlay.getGOverlay();
        this.getGMap().addOverlay(gOverlay);
    }
    
    this.removeOverlay  = function(overlay) {
		if (overlay)
			this.getGMap().removeOverlay(overlay.getGOverlay());
    }
    
    /**
     * as it implies it will recenter and zoom to the given smart bounds...if not provided then it will recenter to the original bounds of this map.
     * smartBounds the SmartBounds object to zoom to or null will zoom to the original map size.
     */
    this.recenterAndZoomToBounds = function(smartBounds) {
        if(smartBounds == null) {
            smartBounds = this.getSmartBounds();
        }
        this.getGMap().setCenter(smartBounds.getCenter(), this.getGMap().getBoundsZoomLevel(smartBounds));
    }

    /**
     * go back to the initial view of this map
     */
    this.zoomToDefault = function() { 
        this.recenterAndZoomToBounds(this.getSmartBounds())
    }
    
    this.cleanup = function() {
        for(var whichRecord = 0; whichRecord < this.markers.length; whichRecord++) {
            var currentMarker = this.markers[whichRecord];
            currentMarker.cleanup();
        }
        this.markers = null;
        this.map = null;
    }
}

function MbColorEnumerator() {
    this.colors = new Array('#ff776b','#68bf4c','#4a8cc1', '#b449c2');
    this.currentColorIndex = 0;
   
    this.getNextColor = function() {
        var color = this.colors[this.currentColorIndex];
        this.currentColorIndex++;
        if(this.currentColorIndex >= this.colors.length) {
            this.currentColorIndex = 0;
        }
        return color;
    }
}

var mbIcon = new GIcon();
mbIcon.image = "/trail/report/images/mb24.png";
mbIcon.shadow = "/trail/report/images/mb24shadow.png";
mbIcon.iconSize = new GSize(24, 24);
mbIcon.shadowSize = new GSize(37,26);
mbIcon.iconAnchor = new GPoint(0, 0);
mbIcon.infoWindowAnchor = new GPoint(15, -3);
mbIcon.transparent = "/trail/report/images/mb24transparent.png";

var mbHoverIcon = new GIcon(mbIcon);
mbHoverIcon.image = "/trail/report/images/mb32.png";
mbHoverIcon.iconSize = new GSize(32, 32);
    
    
/**	
 * An object used to represent a report record (row) on the map.
 * These are created and added by the xsl
 */
function MbReportMapMarker(map,pk,episodeName,trackStartLatitude,trackStartLongitude,minLatitude,minLongitude,maxLatitude,maxLongitude,distance,username,startTime,elevationGain,elevationLoss,activityType) {
    this.parentMap = map;
    this.highlighted = false;
    this.mapListing;
    this.activityType = activityType;
    this.episodeName = episodeName;

    this.pk = pk;
    this.getPk = function() { 
        return this.pk;
    }

    this.markerName = episodeName;
    this.getMarkerName = function() { 
        return this.markerName; 
    }

    //hidden indicates the user would not like to see this on the map right now.
    this.hidden = false;
    this.point = new GLatLng(trackStartLatitude,trackStartLongitude);
    this.distance = distance;
    this.username = username;
    this.startTime = startTime;
    this.elevationGain = elevationGain;
    this.elevationLoss = elevationLoss;
            
    this.smartBounds;
    this.getSmartBounds = function() { 
        return this.smartBounds; 
    }

    //assign smart Bounds
    this.smartBounds  = new GLatLngBounds();
    this.smartBounds.extend(new GLatLng(minLatitude,minLongitude));
    this.smartBounds.extend(new GLatLng(maxLatitude,maxLongitude ));
    
    this.cleanup = function() {
        this.parentMap = null;
        this.smartBounds = null;
    }

    this.hide = function() {
        if(this.polyline != null) {
            this.parentMap.removeOverlay(this.polyline);
        }
        this.parentMap.removeOverlay(this);
        this.hidden = true;
        var mapListing = this.getMapListing();      
        mapListing.style.textDecoration = "line-through";
      
        //close any info window just in case.
        this.parentMap.closeInfoWindow();
    }
    
    this.getMapListing = function() {
        if(this.mapListing == null) {
             this.mapListing = document.getElementById('mapListing' + this.pk);
        }
        return this.mapListing;
    }
    
    
    // --------- GMARKER ------------
    
    //lazy loaded polyline that should be assigned only once.
    this.polyline;
    this.hoverGMarker = new GMarker(this.point,mbHoverIcon);
    this.defaultGMarker = new GMarker(this.point,mbIcon);
    this.gMarker = this.defaultGMarker;
    
    this.hover = function(hoverOn) {
        if(this.gMarker != null) {
            this.parentMap.removeOverlay(this);
        }
        
       if(!this.hidden) {
            if(hoverOn) {
                this.gMarker = this.hoverGMarker;
            } else {
                this.gMarker = this.defaultGMarker;
            }
    
            this.parentMap.addOverlay(this);
         }
    } 
        
    this.getInfoWindowHtml = function() {
    	var html = "<table width=\"215\"><tr><td>";
		html += "<a href=\"/trail/activity/" + this.getPk() + "\" title=\"View '" + this.episodeName + "' Dashboard\">" + this.episodeName + "</a>";
		html += "<tr><td class=\"small\" style=\"white-space:normal;word-wrap:normal\">";
		html += this.distance + " " + this.activityType;
		if (this.username != ""){
			html += " by " + this.username;
		}
		html += " on " + this.startTime;
		html += "</td></tr>";
		html += "<tr><td>";
		html += "<img width=\"200\" height=\"100\" src=\"/trail/invitation/chart/get.mb?xy.domain=totalDistance&xy.ranges=elevation&xy.histogram=false&xy.legendVisible=false&xy.primaryRangeAxisVisible=false&xy.secondaryRangeAxisVisible=false&xy.rangeTitlesVisible=false&xy.domainAxisVisible=false&xy.plotForegroundOpacity=0.75&episodePk.pkValue=" + this.getPk() + "&xy.plotForegroundOpacity=0.75&xy.width=200&xy.height=100&svgFormat=false\" />";
		html += "</td></tr>";
		html += "<tr><td class=\"small\">";
		html += "Gaining " + this.elevationGain + " and losing " + this.elevationLoss;
		html += "</td></tr>";
		html += "<tr><td>";
		html += "<a styleClass=\"small\" title=\"View in the MotionBased Dashboard\" href=\"/trail/activity/" + this.getPk() + "\">Dashboard</a> | ";
		html += "<a styleClass=\"small\" title=\"View in the MotionBased Analyzer\" href=\"/trail/analyzer/view.mb?episodePk.pkValue=" + this.getPk() + "\">Analyzer</a> | ";
		html += "<a styleClass=\"small\" title=\"View in the MotionBased Map Player\" href=\"/trail/player/view.mb?episodePk.pkValue=" + this.getPk() + "\">Map Player</a>";
		html += "</td></tr>";
		html += "<tr><td>";
		html += "Export to:";		
		html += "<a styleClass=\"small\" title=\"Export this activity into Google Earth 3D Viewer\" href=\"/trail/kml/episode.kml?episodePkValues=" + this.getPk() + "\">Google Earth</a> | ";
		html += "<a styleClass=\"small\" title=\"Export this activity as GPS Exchange Format to be used with third party tools\" href=\"/trail/gpx/export.gpx?episodePk.pkValue=" + this.getPk() + "\">GPX</a>";
		html += "</td></tr>";
    	html += "</table>";
		return html;
    }  
    
    /**
     * @param forceZoomTo ... will zoom to the level best fit for the activity
     * @param showPreview ... blowup will show the activity in a closeup
     * @param showOverview ... blowup will show the activity from a few levels out to get an idea where the activity is being viewed
     */
    this.highlightMarker = function(showInfoWindow, forceZoomTo, showPreview, showOverview) {
        //get it back on the map.
        if(this.hidden) {
            this.parentMap.addOverlay(this);
            if(this.polyline != null) {
                this.parentMap.addOverlay(this.polyline);
            }
            this.hidden = false;
        }
        
        if(showPreview) {
            this.gMarker.showMapBlowup();
        } else if(showOverview) {
            var zoomLevel = this.parentMap.map.getBoundsZoomLevel(this.getSmartBounds());
            
            this.parentMap.map.setCenter(this.getSmartBounds().getCenter(), zoomLevel-2);
            this.gMarker.showMapBlowup();
        } else if(showInfoWindow) {
            this.gMarker.openInfoWindowHtml(this.getInfoWindowHtml());
        }

        if(forceZoomTo) {
            reportMap.recenterAndZoomToBounds(this.smartBounds);
           // this.parentMap.closeInfoWindow();
        }
        
        if(this.polyline == null) {
            //now add polyline
           this.polyline = new MbEpisodePolyline(this);
           this.polyline.setColor(this.parentMap.colors.getNextColor());
           this.polyline.loadPolyline();
        }
        
        var mapListing = this.getMapListing();
        mapListing.style.borderLeft = '4px solid ' + this.polyline.getColor();
        //make sure hidden line-trhough is gone.
        mapListing.style.textDecoration = "none";
    }

    /**
     * Loads the polyline if not already done and asynchronously adds it to the map.
     * bad way of handling the event ...figure out how to do this better.
     */
    this.polylineLoaded = function() {
        this.parentMap.addOverlay(this.polyline);
    }
    
    this.markerClicked = function() {
        reportMap.highlightMarker(this.pk);
    }

    this.getGOverlay = function() {
        return this.gMarker;
    }

    GEvent.bind(this.gMarker, "click",  this, this.markerClicked);
    this.smartBounds.extend(this.point);
}


// ===================== POLYLINE ======================
function MbEpisodePolyline(parentMarker) {
    this.episodePk = parentMarker.getPk();
    this.parentMarker = parentMarker;
    this.width = 4;
    this.color = "#ff0000";
    this.setColor = function(color) {this.color = color; }
    this.getColor = function() { return this.color; }
    
    this.opacity = 0.75;
    this.bounds;
    this.httpReq;

    //this.gPolyline must be loaded calling the loadPolyline function...all options must be assigned first.

    this.responseFailure = function(mbHttpRequest) {   
    	var message = "Failed to load track " + parentMarker.getMarkerName();
		statusMessage.showMessage(message);
	}

    this.responseSuccess = function(mbHttpRequest) {   
        this.bounds = new GLatLngBounds();

        var episodeKml = mbHttpRequest.responseXML;
        var kmlLineString = episodeKml.getElementsByTagName('LineString')[0];
        var coordinates = kmlLineString.firstChild;
        var parts = coordinates.childNodes;

        //convert each string coordinate into a point
        var coordinateValue = "";
        for(var whichPart =0; whichPart < parts.length; whichPart++) {
            var part = parts[whichPart];
            coordinateValue += part.nodeValue;
        }
        coordinateValue = coordinateValue.split(' ');

        var gPoints = new Array();
        var maxNumberOfPoints = 250;
        var resolutionReducer = Math.ceil(coordinateValue.length / maxNumberOfPoints);
        
        for(var whichCoordinate = 0; whichCoordinate < coordinateValue.length; whichCoordinate+=resolutionReducer) {
            var components = coordinateValue[whichCoordinate].split(",");
            var x = parseFloat(components[0]);

            var y = parseFloat(components[1]);
            if( isNaN(x) || isNaN(y) ) { }
            else {
                var gPoint =  new GLatLng(y, x);
                gPoints.push(gPoint);
                this.bounds.extend(gPoint);
            }
        }
        this.gPolyline = new GPolyline(gPoints, this.color, this.width ,this.opacity);
        this.parentMarker.polylineLoaded(this);
        statusMessage.showMessage(parentMarker.getMarkerName() + " Track Loaded");
		statusMessage.hideMessage(1000);
    }

    /**
     * asynchronous call this polyline will load as request time becomes available and will call the marker to add it once complete.
     * 
     */
    this.callback = {
	   success : this.responseSuccess,
	   failure: this.responseFailure,
	   scope: this
	};
	
    this.loadPolyline = function() {
        var url = "/trail/kml/episode.kml?episodePkValues=" + this.episodePk + "&contentType=text/xml";
		YAHOO.util.Connect.asyncRequest('GET',url,this.callback);
    }

    this.getGOverlay = function() {
        return this.gPolyline;
    }

    this.getBounds = function() {
        return this.bounds;
    }
}


//==================== SIMPLE POLYLINE ==============================
    /**
     * A polyline where points are directly added for simple situations
     */
    function MbSimplePolyline() {
        this.gPoints = new Array();
        this.gPolyline;

        this.color = '#ff0000';
        this.width = 2;
        this.opacity = 1;
        
        this.getGOverlay = function() {
            if(this.gPolyline == null) {
                this.gPolyline = new GPolyline(this.gPoints, this.color, this.width, this.opacity)
            }
            return this.gPolyline;
        }
        
        this.addLatLng = function(gPoint) {
            this.gPoints.push(gPoint);
        }
        
        this.rectangleFromSmartBounds = function(bounds) {
            this.addLatLng(new GLatLng(bounds.getSouthWest().lat(), bounds.getSouthWest().lng()));
            this.addLatLng(new GLatLng(bounds.getSouthWest().lat(), bounds.getNorthEast().lng()));
            this.addLatLng(new GLatLng(bounds.getNorthEast().lat(), bounds.getNorthEast().lng()));
            this.addLatLng(new GLatLng(bounds.getNorthEast().lat(), bounds.getSouthWest().lng()));
            this.addLatLng(new GLatLng(bounds.getSouthWest().lat(), bounds.getSouthWest().lng()));
        }
    }//end MbSimplePolyline
