﻿var Utility = {
    ///<summary>Object holding utility functions</summary>
    toggleVisibility: function(obj) {
        obj.style.display = (obj.style.display != 'none' ? 'none' : '');
    },
    
    removeNorway: function(str) {
        return str.replace(', Norway','');
    }
}

var Tooltip = {
///<summary>Object used to create the tooltip.</summary>
    
    _width: null,
    _height: null,
    _background: null,
    _border: null,
    _padding: null,
    _elem: null,
    
    init: function(elem, width, height, background, border, padding) {
    /// <summary>Instanciates the Tooltip object.</summary>
    /// <param name="elem">DOM-object that will hold the contents of the tooltip.</param>
    /// <param name="width">Width in pixels</param>
    /// <param name="height">Height in pixels</param>
    /// <param name="background">Background color</param>
    /// <param name="border">Border style</param>
    /// <param name="padding">Tooltip padding</param>
        this._width = width;
        this._height = height;
        this._background = background;
        this._border = border;
        this._padding = padding;
        this._elem = elem;
    },

    show: function(html, ev) {
    /// <summary>Displays the tooltip</summary>
    /// <param name="html">The HTML appearing inside the tooltip element.</param>
    /// <param name="ev">The Event object</param>
        this._elem.innerHTML = html;
        
        this._elem.style.zIndex = 110;
        this._elem.style.top = (ev.pointerY() + 5) + 'px';
        this._elem.style.left = (ev.pointerX() + 5) + 'px';
        
        this._elem.style.width = this._width + 'px';
        this._elem.style.height = this._height + 'px';
        this._elem.style.padding = this._padding + 'px';
        this._elem.style.background = this._background;
        this._elem.style.border = this._border;
        this._elem.show();
    },
    
    hide: function() {
    ///<summary>Hides the tooltip</summary>
        setTimeout("$('tooltipDiv').hide()", 10);
    }
}

var Route = {
///<summary>
/// Object holding the methods used when creating the
/// route summary and step details.
///</summary>

    _replaceNbsp: function(str) {
        return str.replace(new RegExp('\&nbsp;', 'g'), ' ');
    },

    buildSummary: function(routeNumber, route) {
    ///<summary>Method used when building the route summary container.</summary>
        var startAddress = route.getStartGeocode().address;
        var endAddress = route.getEndGeocode().address;
        var distance = route.getDistance().html;
            distance = this._replaceNbsp(distance);
        var duration = route.getDuration().html;

        // Create div holding the route summary.
        var headingStr = Utility.removeNorway(RoutePlanner.pointsToVisit[routeNumber].address) + ' - ' + Utility.removeNorway(RoutePlanner.pointsToVisit[routeNumber+1].address);

        var distanceTime = document.createTextNode('(' + distance + ', ' + duration.replace('&nbsp;','') + ')');
        
        var divHeading = document.createElement('div');
        var expandIcon = document.createElement('img');
        expandIcon.setAttribute('src', '/images/icons/routeplanner/zoom_out.png');
        expandIcon.setAttribute('alt', 'Se detaljer');
        expandIcon.setAttribute('style','float:right;');
        expandIcon.setAttribute('id','expandIcon' + routeNumber);
        
        divHeading.appendChild(expandIcon);    
        
        divHeading.appendChild(document.createTextNode(headingStr));
        divHeading.appendChild(document.createElement('br'));
        divHeading.appendChild(distanceTime);
        
        divHeading.style.paddingBottom = '3px';
        divHeading.style.border = 'solid 1px #ffffff';
        divHeading.style.paddingLeft = '4px';   
        
        var div = document.createElement('div');
        div = this._appendSummaryStyle(div);
        div.id = 'routeSummary' + routeNumber;
          
        div.appendChild(divHeading);

        // Add a container containing the steps for the current route.
        var container = document.createElement('div');
        container.id = 'routeDetails' + routeNumber;
        container.style.paddingTop = '5px';

        // Loop through the steps and append information
        for (var i = 0; i < route.getNumSteps(); i++) {
            var stepDiv = this._buildStepDetails(i, route.getStep(i), routeNumber, startAddress, endAddress, distance, duration);
            container.appendChild(stepDiv);
        }

        div.appendChild(container);
       
        $('PnlStepSummary').appendChild(div);
        
        // Hide/show the details for this part of the route.
        Event.observe($('routeSummary' + routeNumber), 'click', function() {
            var obj = $('routeDetails' + routeNumber);
            Utility.toggleVisibility(obj);
            
            var icon= $('expandIcon' + routeNumber);
            if (icon.src.indexOf('in') > -1) icon.src = '/images/icons/routeplanner/zoom_out.png';
            else icon.src = '/images/icons/routeplanner/zoom_in.png';
            
            RoutePlanner.calculateDirectionsHeight();
        });
        
        Tooltip.init($('tooltipDiv'),170,15, '#dddddd', 'solid 1px gray', 3);
        
        Event.observe($('routeSummary' + routeNumber), 'mouseover', function(ev) {
            Tooltip.show('Klikk for steg-for-steg beskrivelse.',ev);
        });
      
        Event.observe($('routeSummary' + routeNumber), 'mouseout', function() {
            Tooltip.hide();
        });
    },

    _buildStepDetails: function(stepNumber, step, routeNumber, startAddress, endAddress, distance, duration) {
    ///<summary>Method used when building a DIV containing the details about a step.</summary>
    ///<param name="stepNumber"></param>
    ///<param name="step">GStep object</param>
    ///<param name="routeNumber"></param>
    ///<param name="startAddress"></param>
    ///<param name="endAddress"></param>
    ///<param name="distance"></param>
    ///<param name="duration"></param>
        var description = stepNumber + '. ';
        description = stepNumber + '. ';
        description += this._replaceTextWithIcons(step.getDescriptionHtml());
        description += ' ';
        description += step.getDistance().html;
        description += ' (' + step.getDuration().html + ')';
                
        var div = document.createElement('div');
        div = this._appendDetailsStyle(div);
        div.innerHTML = description;
        
        Event.observe($(div), 'click', function(ev) {
            RoutePlanner.showZoomedStepPoint(routeNumber, stepNumber);
            ev.stop(); // Cancel event on parent object.
        });

        return div;
    },
    
    _replaceTextWithIcons: function(arg) {
    ///<summary>Method used when replacing certain parts of the text with icons in the step summary.</summary>
    ///<param name="arg">The text</param>
        arg = arg.replace('Ta til <b>venstre</b>', '<img src="/images/icons/routeplanner//arrow_turn_left.png" alt="Ta til venstre"/>');
        arg = arg.replace('Ta <b>til høyre</b>', '<img src="/images/icons/routeplanner//arrow_turn_right.png" alt="Ta til høyre"/>');
        arg = arg.replace('Ta til <b>høyre</b>', '<img src="/images/icons/routeplanner//arrow_turn_right.png" alt="Ta til høyre"/>');
        arg = arg.replace('Ta avkjøringen', '<img src="/images/icons/routeplanner//arrow_branch.png" alt=""/>');
        arg = arg.replace('Ta rampen', '<img src="/images/icons/routeplanner//arrow_branch.png" alt=""/>');
        arg = arg.replace('Ta avkjøring', '<img src="/images/icons/routeplanner//arrow_branch.png" alt=""/>');
        arg = arg.replace('Fortsett ut', '<img src="/images/icons/routeplanner//arrow_merge.png" alt="Fortsett ut"/>');
        return arg;
    },
    
    _appendSummaryStyle: function(obj) {
    ///<summary>Method used when appending a style to the summary div.</summary>
    ///<param name="obj"></param>
        obj.style.borderBottom = 'solid 1px #ABAFB7';
        obj.style.marginBottom = '3px';
        obj.style.cursor = 'pointer';
        obj.style.cursor = 'hand';
        return obj;
    },

    _appendDetailsStyle: function(obj) {
    ///<summary>Method used when appending a style to the stepdetails div.</summary>
    ///<param name="obj"></param>
        obj.style.paddingLeft = '4px';
        obj.style.paddingright = '4px';
        obj.style.marginBottom = '5px';
        obj.style.cursor = 'pointer';
        obj.style.cursor = 'hand';
        return obj;
    }
}

var RoutePlanner = {
///<summary>Object holding methods for the routeplanner functionality</summary>
    _map: null,              // Holds the map object
   
    _ctrlDrivePanel: null,  // Holds the control panel for the animated drive.

    finalDirs: null, // Holds the final GDirections
    snapDirs: null,  // Holds the address-information that allows the marker to snap to the nearest road.
    detailBlowup: null, // Holds the detail blowup that appears when the user clicks a route detail.
    gmarkers: [],       // Holds the map-markers when editing the route
    pointsToVisit: [],  // Holds address-information on points to visit, always in sync with the gmarkers array.
    matchingAddresses: [],  // Holds addresses that matches the user's search-criteria
      
    draggedMarkerIndex: -1,
    draggedMarkerStartPos: null,
        
    _containerHeight : null,    // Holds the default height of the driving directions container
    _containerOverflow : null,  // Holds the default overflow setting of the driving directions container
    _containerOverflowX : null, // Holds the default overflowX setting of the driving directions container
    _containerRightMargin : null, // Holds the right-margin setting for the 'searchDiv' that holds the driving directions
        
    
    init: function(map) {
    ///<summary>Initializes the RoutePlanner object.</summary>
    ///<param name="map">Google Maps object</summary>
        this._map = map;
    },

    start: function() {
    ///<summary>Method used to start the routeplanner.</summary>
        if (this._map != null) {
            $('map').style.width = '480px';
            this._map.checkResize();

            $('areaList').hide();
            $('searchDiv').show();
        }
    },

    end: function() {
    ///<summary>Method used to end the routeplanner.</summary>
        if (this._map) {
            if (RoutePlanner._ctrlDrivePanel != null) {
                if (RoutePlanner._ctrlDrivePanel.carAnimation.animThread != null) 
                    RoutePlanner._ctrlDrivePanel.carAnimation.stop(); 
                
                RoutePlanner._map.removeControl(RoutePlanner._ctrlDrivePanel);
                RoutePlanner._ctrlDrivePanel = null;
            }
            
            if (RoutePlanner.finalDirs != null) RoutePlanner.finalDirs.clear();
            this._removeMarkers();
            this.gmarkers = [];
            this.pointsToVisit = [];
            
            $('PnlStepSummary').update();            
            $('PnlPointsToVisit').update();
            $('PnlSummaryHeading').hide();
            
            $('map').style.width = '565px';
            this._map.checkResize();

            $('areaList').show();
            $('searchDiv').hide();
        }
    },

    minimizeAllRoutePoints: function() {
    ///<summary>Method used to minimize all route summaries.</summary>
        for (var i = 0; i < this.finalDirs.getNumRoutes(); i++) {
            $('routeDetails' + i).hide();
        }
        this.calculateDirectionsHeight();
    },

    expandAllRoutePoints: function() {
    ///<summary>Method used to expand all route summaries.</summary>
        for (var i = 0; i < this.finalDirs.getNumRoutes(); i++) {
            $('routeDetails' + i).show();
        }
        this.calculateDirectionsHeight();
    },
    
    toggleRTMMarkers: function() {
    ///<summary>Method used to toggle RTM-markers on/off.</summary>
        if ($('showRTMMarkers').checked) addRTMMarkers();
        else this._map.clearOverlays();
    },
    
    _removeMarkers: function() {
    ///<summary>Method used to remove the markes from the map.</summary>
        for (var n = 0; n < this.gmarkers.length; n++) {
            this._map.removeOverlay(this.gmarkers[n]);
        }
    },
    
    _showDirections: function() {
    ///<summary>Displays the driving directions in the map, and in the summaries panels.</summary>
        this._map.closeInfoWindow();  // Close all open infowindows

        if (this.finalDirs) this.finalDirs.clear();   // Clear the last driving instructions

        this.finalDirs = '';

        // Convert markers to waypoints
        var waypoints = [];
        for (var i = 0; i < this.gmarkers.length; i++) {
            waypoints.push(this.gmarkers[i].getLatLng());
        }

        // Load waypoints onto directions object.
        this.finalDirs = new GDirections(this._map);
        this.finalDirs.loadFromWaypoints(waypoints, { locale: 'no', getSteps: true, getPolyline: true });

        var _self = this;
        GEvent.addListener(_self.finalDirs, "load", function() {
            // Build summary and stepdetails list
            _self._buildPanelStepsSummary(waypoints);
            _self._buildPanelStepsDetails();

            // Removes helper-markers that are created 
            // for each of the route-points.
            _self._removeMarkers();
            
            // Used for the animated drive
            _self.poly = _self.finalDirs.getPolyline();
            _self.eol = _self.poly.Distance();
            _self._map.setCenter(_self.poly.getVertex(0), 17);
            
            // Add the animated drive control panel
            if ((_self.finalDirs.getNumRoutes() > 0) && (!_self._ctrlDrivePanel)) {
                _self._ctrlDrivePanel = new DrivePanel();
                _self._map.addControl(_self._ctrlDrivePanel);
            }
        });

        // Expand information about the clicked route-point.
        GEvent.addListener(_self._map, "infowindowopen", function() {
            var infoWindow = _self._map.getInfoWindow();
            var p = infoWindow.getPoint();

            // Loop through the directions and find the route/step this point belongs to.
            for (var i = 0; i < _self.finalDirs.getNumRoutes(); i++) {
                for (var q = 0; q < _self.finalDirs.getRoute(i).getNumSteps(); q++) {
                    if (_self.finalDirs.getRoute(i).getStep(q).getLatLng() == p) {
                        _self.showZoomedRoutePoint(i, false);
                        break;
                    }
                }
            }
        });
        this.calculateDirectionsHeight();
    },

    _buildPanelStepsSummary: function(waypoints) {
    ///<summary>Builds the summary steps</summary>
    ///<param name="waypoints"></param>
        var startAddress = this.pointsToVisit[0].address;
        var endAddress = this.pointsToVisit[this.pointsToVisit.length-1].address;
        var numRoutes = this.finalDirs.getNumRoutes();

        var strRouteSummary = '<div style="padding: 2px; border-bottom: 1px solid #ABAFB7;">';
        strRouteSummary += '<table style="width: 100%"><tr><td>' + this.finalDirs.getSummaryHtml();
            
        if (this.finalDirs.getNumRoutes() > 1) {
            strRouteSummary += '<br/>' + this.finalDirs.getNumRoutes() + ' etapper';
        }
        strRouteSummary += '</td><td><td align="right"><img id="printerIcon" onclick="Print.init(RoutePlanner._map, RoutePlanner.finalDirs, RoutePlanner.gmarkers, RoutePlanner.pointsToVisit, 400, 300);" style="cursor: hand; cursor: pointer;" src="/images/icons/routeplanner/printer.png" alt="Skriv ut.."/> ';
        strRouteSummary += '<a href="#" style="font-weight:normal; text-decoration: none; cursor:hand; cursor:pointer;" onclick="Print.init(RoutePlanner._map, RoutePlanner.finalDirs, RoutePlanner.gmarkers, RoutePlanner.pointsToVisit, 400, 300);return false;">Skriv ut</a></td></tr></table>';
        strRouteSummary += '</div>';
        
        $('PnlStepSummary').update(strRouteSummary);
    },

    _buildPanelStepsDetails: function() {
    ///<summary>Builds the panel containing the step details.</summary>
        var numRoutes = this.finalDirs.getNumRoutes();
        for (var j = 0; j < numRoutes; j++) {
            Route.buildSummary(j, this.finalDirs.getRoute(j));
        }
         // Calculate height used for directions
        this.calculateDirectionsHeight();
    },

    calculateDirectionsHeight: function() {
    ///<summary>
    /// Calculates the total height of the elements represented in the 
    /// driving directions container. Adds scrollbars if necessary.
    ///</summary>
        var dirObj = $('directionsContainer');
        
        var headingHeight = $('searchDivHeading').getHeight();
        var tableHeight = $('searchDivTable').getHeight();
        
        var headingHeight = $('PnlSummaryHeading').getHeight();
        var pointsToVisitHeight = $('PnlPointsToVisit').getHeight();
        var stepsSummaryHeight = $('PnlStepSummary').getHeight();
        
        var directionsHeight = pointsToVisitHeight + stepsSummaryHeight;
        if (directionsHeight > 240) {   // Add scrollbars if necessary
            this._containerHeight = dirObj.style.height; 
            this._containerOverflow  = dirObj.style.overflow;
            this._containerOverflowX = dirObj.style.overflowX;  
            this._containerRightMargin = $('searchDiv').firstDescendant('div').style.marginRight;
            
            $('searchDiv').firstDescendant('div').style.marginRight = '1px';
            $('searchDiv').firstDescendant('div').descendants()[1].style.marginRight = '5px';
            
            var height = 360 - (headingHeight + tableHeight);
            dirObj.style.height = height + 'px';
            dirObj.style.overflow = 'scroll';
            dirObj.style.overflowX = 'hidden';
        } else {    // Restore to defaults
            $('searchDiv').firstDescendant('div').style.marginRight = this._containerRightMargin;
            $('searchDiv').firstDescendant('div').descendants()[1].style.marginRight = '6px';
            $('PnlStepSummary').style.paddingRight = '6px';
            dirObj.style.height = this._containerHeight;
            if (this._containerOverflow != null) dirObj.style.overflow = this._containerOverflow;
            if (this._containerOverflowX != null) dirObj.style.overflowX = this._containerOverflowX;
        }
    },
  
    showZoomedRoutePoint: function(routeNumber, displayBlowup) {
    ///<summary>Displays a map blowup for the marker when the user clicks the route-heading marker.</summary>
        if (displayBlowup) {
            var r = this.finalDirs.getRoute(routeNumber);
            var firstStep = r.getStep(0);
            this._map.showMapBlowup(firstStep.getLatLng());
        }
    },

    showZoomedStepPoint: function(routeNumber, stepNumber) {
    ///<summary>Displays a map blowup for the marker when the user clicks a route-step.</summary>
        var r = this.finalDirs.getRoute(routeNumber);
        var step = r.getStep(stepNumber);

        this._map.showMapBlowup(step.getLatLng());
        detailBlowup = this._map.getInfoWindow();
    },

    getRoute: function() {
    ///<summary>Method used to revers-geocode the address the user searched for.</summary>
        this._map.closeInfoWindow();
        matchingAddresses = [];

        var geocoder = new GClientGeocoder();

        // Limit search to norway, if checked by the user
        if ($('limitSearch').checked) geocoder.setBaseCountryCode('no');
        
        geocoder.getLocations($('search').value, function(result) {
            if (result.Placemark) { 
                if (result.Placemark.length > 1) { // If more than one result is returned, list the choices to the user.
                    var resultList = '';
                    for (var i = 0; i < result.Placemark.length; i++) {
                        matchingAddresses.push(result.Placemark[i]);
                        resultList += '<div onmouseover="this.style.textDecoration=\'underline\';" onmouseout="this.style.textDecoration=\'none\';" style="cursor:hand; cursor:pointer; width:100%;" onclick="RoutePlanner.addPoint(matchingAddresses[' + i + ']);">';
                        resultList += Utility.removeNorway(result.Placemark[i].address);
                        resultList += '</div>';
                    }
                    $('searchResultsHeading').show();
                    $('searchResults').update(resultList);
                    RoutePlanner.calculateDirectionsHeight();
                } else { // If only one result is returned, add the point directly.
                    matchingAddresses.push(result.Placemark[0]);
                    RoutePlanner.addPoint(matchingAddresses[0]);
                }
                $('lblSearch').update('Til');
            }
        });
    },

    addPoint: function(location) { 
    ///<summary>Method adding a marker to the map and stores its address.</summary>
        var p = location.Point.coordinates;
        var marker = new GMarker(new GLatLng(p[1], p[0]), { draggable: true });

        // Synchronizing markers array.
        this.gmarkers.push(marker);

        this._map.addOverlay(marker);

        // Synchronizing points to view array.
        this.pointsToVisit.push(location);
        
        // Cleanup
        $('searchResultsHeading').hide(); // Hide the results heading
        $('searchResults').update(); // Clear the results pane.
        $('search').value = ''; // Clear search form.
        matchingAddresses = []; // Clear temporary address-store.
        
        // Add selected point to the driving points pane.
        this.buildPanelPointsToVisit();

        RoutePlanner.calculateDirectionsHeight();
    },

    _buildPanelSummaryHeading: function() {    
    ///<summary>Method used when building the summary heading</summary
        var strHeading = '<div style="font-size: 10pt; display: inline;"><b>Kj&oslash;rerute</b></div>';
        $('PnlSummaryHeading').update(strHeading);
                
        if (this.pointsToVisit.length > 0) $('PnlSummaryHeading').show();
    },

    buildPanelPointsToVisit: function() {
    ///<summary>Method used when building the points to visit panel.</summary>
        this._buildPanelSummaryHeading();

        $('PnlPointsToVisit').update();
        var str = '<table>';
        for (var a = 0; a < this.pointsToVisit.length; a++) {
            var strCoords = this.pointsToVisit[a].Point.coordinates[1] + ',' + this.pointsToVisit[a].Point.coordinates[0];
            str += '<tr><td>';
            str += Utility.removeNorway(this.pointsToVisit[a].address);
            str += '</td><td>';
            if (a > 0) {
                str += '<img src="/images/icons/routeplanner//bullet_arrow_up.png" alt="" onclick="RoutePlanner.movePointToVisit(\'up\',new GLatLng(' + strCoords + '));"/>';
            }
            if (a < this.pointsToVisit.length - 1) {
                str += '<img src="/images/icons/routeplanner//bullet_arrow_down.png" alt="" onclick="RoutePlanner.movePointToVisit(\'down\',new GLatLng(' + strCoords + '));"/>';
            }
            str += '</td><td>';
            str += '<img src="/images/icons/routeplanner//delete.png" alt="" style="cursor: hand; cursor: pointer;" onclick="RoutePlanner.removePointToVisit(new GLatLng(' + strCoords + '));"/>';
            str += '</td><td>';
            str += '<img src="/images/icons/routeplanner//zoom.png" alt="" style="cursor: hand; cursor: pointer;" onclick="RoutePlanner.showAddressInInfoWindow(new GLatLng(' + strCoords + '));"/>';
            str += '</td></tr>';
        }
        str += '</table>';

        $('PnlPointsToVisit').update(str);

        this._showDirections();
    },

    editRoute: function() {
    ///<summary>Method used when editing the route by dragging the points to visit markers.</summary>
        if (this.carMarker) this._map.removeOverlay(carMarker);

        if (!this.snapDirs) {
            var _self = this;
            _self.snapDirs = new GDirections(_self._map);

            GEvent.addListener(_self.snapDirs, 'load', function() {
                pVisit = _self.snapDirs.getRoute(0).getStartGeocode();
                var tmpPointsToVisit = [];

                for (var n = 0; n < _self.pointsToVisit.length; n++) {
                    if (n != draggedMarkerIndex) {
                        tmpPointsToVisit.push(_self.pointsToVisit[n]);
                    } else {
                        tmpPointsToVisit.push(pVisit);
                    }
                }
                _self.pointsToVisit = tmpPointsToVisit;
                _self.snapDirs.clear();

                // Draw directions
                _self.buildPanelPointsToVisit();
            });
        }
        else this.snapDirs.clear();

        this.finalDirs.clear();
        this._addRouteMarkers();
    },

    _addRouteMarkers: function() {
    ///<summary>Method used when adding route markers to the map.</summary>
        this._map.clearOverlays();
        for (var i = 0; i < this.gmarkers.length; i++) {
            var m = this._createDraggableMarker(this.gmarkers[i].getLatLng());
            this._map.addOverlay(m);
        }
    },

    _createDraggableMarker: function(coords) {
    ///<summary>Method used when adding draggable markers to the map when editing the route.</summary>
    ///<param name="coords">The Lat/Lng of the marker to create</param>
        var _self = this;
        var point = new GLatLng(coords.lat(), coords.lng());
        var marker = new GMarker(point, { draggable: true });
        GEvent.addListener(marker, 'mousedown', function() {
            draggedMarkerStartPos = marker.getLatLng();
        });

        GEvent.addListener(marker, 'dragend', function() {
            for (var j = 0; j < _self.gmarkers.length; j++) {
                if (_self.gmarkers[j].getLatLng().toUrlValue(6) == draggedMarkerStartPos.toUrlValue(6)) {
                    draggedMarkerIndex = j;
                    _self.gmarkers[j] = marker;
                }
            }
            // Snap marker to the nearest road.
            _self.snapDirs.loadFromWaypoints([marker.getLatLng().toUrlValue(6), marker.getLatLng().toUrlValue(6)], { locale: 'no', getSteps: true, getPolyline: true });
        });
        return marker;
    },

    removePointToVisit: function(coordRemove) {
    ///<summary>Method used when removing the selected point to visit from the list.</summary>
    ///<param name="coordRemove">The coordinate to remove</param>
        for (var i = 0; i < this.gmarkers.length; i++) {
            if ((coordRemove.lng() == this.gmarkers[i].getLatLng().lng()) && (coordRemove.lat() == this.gmarkers[i].getLatLng().lat())) {
                for (var n = 0; n < this.pointsToVisit.length; n++) {
                    if ((this.pointsToVisit[n].Point.coordinates[0] == coordRemove.lng()) && (this.pointsToVisit[n].Point.coordinates[1] == coordRemove.lat())) {
                        this.gmarkers.splice(i, 1);
                        this.pointsToVisit.splice(n, 1);

                        // Remove route details
                        var numRoutes = this.finalDirs.getNumRoutes();
                        switch (numRoutes) {
                            case 1: 
                                this.clearDrivingDirections();
                                break;
                            case 0:
                                $('PnlSummaryHeading').hide();    
                                $('lblSearch').update('Fra');
                                
                                for (var n = 0; n < this.gmarkers.length; n++) {
                                    this._map.removeOverlay(gmarkers[n]);                                    
                                }
                                this.gmarkers = [];
                                break;    
                        }
                        
                        // Remove directions on the map, rebuild the points to visit pane
                        // and fetch new instructions.
                        this.finalDirs.clear();
                        this.buildPanelPointsToVisit();

                        break;
                    }
                }
            }
        }
    },
    
    clearDrivingDirections: function() {
    ///<summary>Method used when removing all driving directions.</summary>
        $('PnlPointsToVisit').update();
        $('PnlStepSummary').update();
        
        var _self = this;
        _self._map.removeControl(_self._ctrlDrivePanel);
        _self._ctrlDrivePanel = null;
    },

    movePointToVisit: function(direction, coords) {
    ///<summary>Method used when moving the selected point to visit up or down in the list.</summary>
    ///<param name="direction">Which direction to move the point, up or down.</param>
    ///<param name="coords">The Lat/Lng of the point to move.</param>
        var killLoop = false;
        for (var i = 0; i < this.gmarkers.length; i++) {
            if ((!killLoop) && (coords.lng() == this.gmarkers[i].getLatLng().lng()) && (coords.lat() == this.gmarkers[i].getLatLng().lat())) {
                for (var n = 0; n < this.pointsToVisit.length; n++) {
                    if ((this.pointsToVisit[n].Point.coordinates[0] == coords.lng()) && (this.pointsToVisit[n].Point.coordinates[1] == coords.lat())) {
                        var tmpMarker = this.gmarkers[i];
                        var tmpPointToVisit = this.pointsToVisit[n];

                        // Rearrange the arrays holding the markers and point-addresses
                        this.gmarkers.move(i, direction);
                        this.pointsToVisit.move(i, direction);
                        killLoop = true;
                        break;
                    }
                }
            }
        }
        this.buildPanelPointsToVisit();
    },

    showAddressInInfoWindow: function(point) {
    ///<summary>
    /// Method used when displaying the address for the marker 
    /// when the user clicks the zoom-icon next top the address.
    ///</summary>
    ///<param name="point">The point containing the Lat/Lng of the point to display.</param>
        for (var i = 0; i < this.gmarkers.length; i++) {
            if (this.gmarkers[i].getLatLng().toUrlValue(6) == point.toUrlValue(6)) {
                this._map.openInfoWindowHtml(point, this.pointsToVisit[i].address);
            }
        }
    }
}

var Print = {
	///<summary>Object holding methods used when printing the directions.</summary>

	_map: null,             // The map div.
	_directions: null,      // GDirections object containing the driving directions.
	_markers: null,         // Array containing the markers.
	_pointsToVisit: null,   // Array containing the from-/via- and end locations.
	_width: null,           // Width of the static map i pixels.
	_height: null,          // Height of the static map i pixels.

	init: function(map, directions, markers, pointsToVisit, width, height) {
		///<summary>Method used when initializing the Print object.</summary>
		///<param name="map">The Google Map object</param>
		///<param name="directions">GDirections object holding the driving directions</param>
		///<param name="markers">Array holding the points to visit markers</param>
		///<param name="pointsToVisit">Array holding address information about the points to visit</param>
		///<param name="width">Width of the static map in pixels</param>
		///<param name="height">Height of the static map i pixels</param>

		$('printerIcon').src = '/images/icons/routeplanner/hourglass.png';

		this._map = map;
		this._directions = directions;
		this._markers = markers;
		this._width = width;
		this._height = height;
		this._pointsToVisit = pointsToVisit;

		var mapZoom = this._map.getZoom();
		var mapCenter = this._map.getCenter();

		// Max. number of points accepted in a static map polyline by Google is 100, according to
		// their documentation, but actual value is lower. Limit is therefore set to 90.
		var maxVerticesAcceptedByGoogle = 90;

		var googleKey = 86; //Length of the Google Maps Key.
		var maxUrlLength = 1880 - googleKey; // Approx. max. url length allowed by Google.

		// URL used to fetch a static map from Google containing the route as a polyline.                          
		var googleUrl = 'http://maps.google.com/staticmap?';
		googleUrl += 'zoom=' + mapZoom;
		googleUrl += '&size=' + this._width + 'x' + this._height;
		googleUrl += '&center=' + mapCenter.lat() + ',' + mapCenter.lng();
		googleUrl += '&key={0}';
		googleUrl += '&' + this._getMarkers();
		googleUrl += '&' + this._getDirectionsPolyline(maxVerticesAcceptedByGoogle, (maxUrlLength - googleUrl.length));

		var locationsToVisit = this._getPointsToVisitList();  // Get the list of locations to visit       
		var routeSummary = this._getDirectionSteps();
		var messages = this._findConflictingMessages();

		// Create window content as HTML
		new Ajax.Request('/PrintDrivingDirections.aspx', {
			method: 'post',
			parameters: {
				'googleMap': googleUrl,
				'l': locationsToVisit,
				'r': routeSummary,
				'm': messages
			},
			onSuccess: function(transport) {
				/*
				1. Opens a new window containing the printable version
				2. Opens the print-dialog
				3. closes the opened window
				*/
				var html = transport.responseText;
				var printWin = window.open(null, 'PrintWindow');
				printWin.document.write(html);
				printWin.document.close();
				$('printerIcon').src = '/images/icons/routeplanner/printer.png';
			}
		});
	},

	_findConflictingMessages: function() {
		///<summary>Method used to retrieve RTMs along the planned route</summary>
		if (typeof RTMMap._rtmMarkers == "undefined")
			return "";

		var messagesAlongRoute = [];
		var poly = this._directions.getPolyline();
		for (var i = 0; i < poly.getVertexCount(); i++) {
			var vertex = poly.getVertex(i);
			for (var n = 0; n < RTMMap._rtmMarkers.length; n++) {
				// add messages that appears within a radius of 250 meters as conflicting with the route.
				var radius = 250;
				if (vertex.distanceFrom((RTMMap._rtMessages[n]).LatLng) <= radius) {
					if (!messagesAlongRoute.include(RTMMap._rtMessages[n].Description))
						messagesAlongRoute.push(RTMMap._rtMessages[n].Description);
				}
			}
		}
		return messagesAlongRoute.join('|');
	},

	_getDirectionSteps: function() {
		///<summary>Method used to fetch the step-summary for each of the steps in the driving directions</summary>
		routes = [];
		var numRoutes = this._directions.getNumRoutes();
		for (var n = 0; n < numRoutes; n++) {
			var route = this._directions.getRoute(n);

			stepSummary = [];
			for (var i = 0; i < route.getNumSteps(); i++) {
				var step = route.getStep(i);
				stepSummary.push(step.getDescriptionHtml());
			}
			var routeSummary = route.getSummaryHtml();
			routes.push(routeSummary + '|' + stepSummary.join('*'));
		}
		return routes.join('^');
	},

	_getPointsToVisitList: function() {
		///<summary>Method used to create a list of points to visit</summary>
		var pointsToVisitRows = $('PnlPointsToVisit').getElementsBySelector('tr');   // The rows containing points to visit

		var locations = [];
		for (var i = 0; i < pointsToVisitRows.length; i++) {
			var td = (pointsToVisitRows[i]).getElementsBySelector('td')[0];
			locations.push(td.innerHTML);
		}
		return locations.join('|');
	},

	_getDirectionsPolyline: function(maxVertices, reservedUrlLength) {
		///<summary>
		/// Method used to create the polyline parameter that is fed to the
		/// static Google Maps API when creating the printable version of the map.
		///</summary>
		var poly = this._directions.getPolyline();
		var numOfVertices = poly.getVertexCount();

		pathArgs = [];
		var step = parseInt(numOfVertices / maxVertices);
		for (var a = 0; a < maxVertices; a++) {
			var offset = a * step;
			pathArgs.push(poly.getVertex(offset).toUrlValue(5));
		}
		var p = 'path=rgba:0xff0000ff,weight:3|' + pathArgs.join('|');

		if (p.length > reservedUrlLength) {
			return this._getDirectionsPolyline((maxVertices - 1), reservedUrlLength);
		} else {
			return p;
		}
	},

	_getMarkers: function() {
		///<summary>
		/// Method used to create the markers parameter that is fed to the
		/// static Google Maps API when creating the printable version of the map.
		///</summary>
		var counter = 0;
		var letters = 'abcdefghijklmnopqrstuvwxyz0123456789';
		var markers = [];
		for (var i = 0; i < this._markers.length; i++) {
			var lat = this._markers[i].getLatLng().lat();
			var lng = this._markers[i].getLatLng().lng();
			markers.push(lat + ',' + lng + ',green' + letters.substring(counter, counter + 1));
			counter++;
		}
		return 'markers=' + markers.join('|');
	}
}

