﻿Type.registerNamespace("Ninemsn.VE");

//var ROUTE_LINE_ID='line';
//var ROUTE_START_PIN_ID='startpin';
//var ROUTE_END_PIN_ID='endpin';

var RouteLineType = 'Line';
var RouteStartPinType = 'Start';
var RouteEndPinType = 'End';
var RouteCustomPinType = 'Custom';
var RouteDistancePinType = 'Distance'

var DEBUG = false;
var NextShapeID = 1;

// shape layer data container
Ninemsn.VE.ShapeLayer = function()
{
    // Initialize as a base class.
    Ninemsn.VE.ShapeLayer.initializeBase(this);
    this.Title='';   
    this.Description='';
    this.Shapes=null;
    this.__type='Ninemsn.Simba.WebControls.VE.ShapeLayer, Ninemsn.Simba.WebControls';
}
Ninemsn.VE.ShapeLayer.prototype = 
{
    initialize : function() 
    {
        // translate json object graph into Ninemsn.VE.Shape
        if (this.Shapes==null)
        {
            this.Shapes=new Array();
        }
        else
        {   // convert array of json properties into Ninemsn.VE.Shape
            var shps = new Array();
            for( var i=0; i<this.Shapes.length; i++)
            {
                var json = this.Shapes[i];
                var shp = $create(Ninemsn.VE.Shape, json);
                Array.add(shps, shp);
            }
            this.Shapes = shps;
        }
    },
    get_Title:function(){return this.Title;},
    set_Title:function(value){this.Title = value;},
    get_Description:function(){return this.Description;},
    set_Description:function(value){this.Description = value;},
    get_Shapes:function(){return this.Shapes;},
    set_Shapes:function(value){this.Shapes = value;},
    add_Shape:function(shp)
    {
        Array.add( this.Shapes, shp);
    },
    remove_LastShape:function(){this.Shapes.pop();},
    draw:function(vemap, changeView)
    {
        var velayer=new VEShapeLayer();
        velayer.SetDescription(this.get_Description());
        velayer.SetTitle(this.get_Title());
        vemap.AddShapeLayer(velayer);
        
        for(var i=0; i<this.Shapes.length; i++)
        {
            this.Shapes[i].draw(vemap, velayer);
        }
        
        if (changeView && velayer.GetShapeCount()>0)
        {
            vemap.SetMapView(velayer.GetBoundingRectangle());
        }
    }
}
Ninemsn.VE.ShapeLayer.registerClass("Ninemsn.VE.ShapeLayer", Sys.Component);

// shape data container
Ninemsn.VE.Shape = function()
{
    // Initialize as a base class.
    Ninemsn.VE.Shape.initializeBase(this);
    this.Title = '';   
    this.Description = '';
    this.CustomIcon = null;
    this.CustomType = null;
    this.PhotoUrl = null;
    this.ShapeType = 0;
    this.Points = null;
    this.__type='Ninemsn.Simba.WebControls.VE.Shape, Ninemsn.Simba.WebControls';
}
Ninemsn.VE.Shape.prototype = 
{
    initialize : function() 
    {
        this.set_id(NextShapeID.toString());
        NextShapeID++;
        
        if(this.Points==null)
        {
            this.Points = new Array();
        }
        else
        {
            var pts = new Array();
            for( var i=0; i<this.Points.length; i++)
            {
                var json = this.Points[i];
                var pt = $create(Ninemsn.VE.LatLong, json);
                pts[i] = pt;
            }
            this.Points = pts;
        }
    },
    get_Title:function(){return this.Title;},
    set_Title:function(value){this.Title = value;},
    get_Description:function(){return this.Description;},
    set_Description:function(value){this.Description = value;},
    get_CustomIcon:function(){return this.CustomIcon;},
    set_CustomIcon:function(value){this.CustomIcon = value;},
    get_ShapeType:function(){return this.ShapeType;},
    set_ShapeType:function(value){this.ShapeType = value;},
    get_CustomType:function(){return this.CustomType;},
    set_CustomType:function(value){this.CustomType = value;},
    get_Points:function(){return this.Points;},
    set_Points:function(value){this.Points = value;},
    draw:function(vemap, velayer)
    {   
        if( _MapEditor.get_ReadOnly() == false && this.get_CustomType() == RouteEndPinType)
        {
            // do not draw finish pin in edit mode.
            return;
        }
        // translate shape type
        var veshapeType=VEShapeType.Pushpin
        if(this.ShapeType==0) {
            veshapeType=VEShapeType.Pushpin;
            if(this.Points.length<1) return; // minimum points
        }
        else if(this.ShapeType==1) {
            veshapeType=VEShapeType.Polyline;
            if(this.Points.length<2) return; // minimum points
        }
        else if(this.ShapeType==2) {
            veshapeType=VEShapeType.Polygon;
            if(this.Points.length<2) return; // minimum points
        }
        
        // translate shape points
        var vepoints = new Array();
        for(var i=0;i<this.Points.length; i++)
        {
            vepoints[i] = this.Points[i].toVELatLong();
        }
        
        // create shape
        var veshape = new VEShape(veshapeType, vepoints);
        veshape.SetTitle(this.get_Title());
        veshape.SetCustomIcon(this.get_CustomIcon());
        var desc = "<p>" + this.get_Description() + "</p>";
        
        // hack: using global editor variable
        if( this.get_CustomType()==RouteCustomPinType && _MapEditor.get_ReadOnly() == false)
        {
            desc += "<a href='javascript:remove_Shape(\"" + this.get_id() + "\")'>delete</a>"
        }
        veshape.SetDescription(desc);
                
        // apply default settings to polyline
        if(veshape.GetType()==VEShapeType.Polyline)
        {
            veshape.SetLineColor(new VEColor(229, 0, 0, 0.8));
            veshape.SetLineWidth(3);
            veshape.HideIcon();
        }
        
        // add shape.
        velayer.AddShape(veshape); 
    },
    toVEShape:function(mode)
    {
        var veshapeType=VEShapeType.Pushpin
        if     (this.ShapeType==0) veshapeType=VEShapeType.Pushpin;
        else if(this.ShapeType==1) veshapeType=VEShapeType.Polyline;
        else if(this.ShapeType==2) veshapeType=VEShapeType.Polygon;
        
        // translate shape points
        var vepoints = new Array();
        for(var i=0;i<this.Points.length; i++)
        {
            vepoints[i] = this.Points[i].toVELatLong(mode);
        }
        
        // create shape
        var veshape = new VEShape(veshapeType, vepoints);
        veshape.SetTitle(this.get_Title());
        veshape.SetCustomIcon(this.get_CustomIcon());
        
        var desc = "<p>" + this.get_Description() + "</p>";
        
        // hack: using global editor variable
//        if(mode=="edit")
//        {
//            
//        }
//        desc += "<a href='javascript:removePin(" + id + " )'>delete</a>"
        veshape.SetDescription(desc);
                
        // apply default settings to polyline
        if(veshape.GetType()==VEShapeType.Polyline)
        {
            veshape.SetLineColor(new VEColor(229, 0, 0, 0.8));
            veshape.SetLineWidth(3);
            veshape.HideIcon();
        }
        return veshape;
    }
}
Ninemsn.VE.Shape.registerClass("Ninemsn.VE.Shape", Sys.Component);

// shape data container
Ninemsn.VE.LatLong = function()
{
    // Initialize as a base class.
    Ninemsn.VE.LatLong.initializeBase(this);
    this.Latitude;
    this.Longitude;
    this.__type='Ninemsn.Simba.WebControls.VE.LatLong, Ninemsn.Simba.WebControls';
}
Ninemsn.VE.LatLong.prototype = 
{
    get_Latitude:function(){return this.Latitude;},
    set_Latitude:function(value){this.Latitude = value;},
    get_Longitude:function(){return this.Longitude;},
    set_Longitude:function(value){this.Longitude = value;},
    toVELatLong:function(){return new VELatLong( this.get_Latitude(), this.get_Longitude());}
}
Ninemsn.VE.LatLong.registerClass("Ninemsn.VE.LatLong", Sys.Component);

// Define a simplified component.
Ninemsn.VE.MapMode = function()
{
    Ninemsn.VE.MapMode.initializeBase(this);
}
// Create protytype.
Ninemsn.VE.MapMode.prototype = 
{
    initialize : function() 
    {
        Ninemsn.VE.MapMode.callBaseMethod(this, "initialize");
    },
    dispose : function() 
    {
        Ninemsn.VE.MapMode.callBaseMethod(this,"dispose");
    },
    onEnter : function(map) { if(DEBUG) alert('enter event in ' + this.get_id() + ' mode');},
    onExit : function(map)  { if(DEBUG) alert('exit event in ' + this.get_id() + ' mode');},
    onMapClick : function(map, e) { if(DEBUG) alert('click event in ' + this.get_id() + ' mode');},
    onMapDoubleClick : function(map, e) { if(DEBUG) alert('double click event in ' + this.get_id() + ' mode');},
    onMapKeyUp : function(map, e) { if(DEBUG) alert('keyup event in ' + this.get_id() + ' mode');}
} // End of prototype definition.

// Register the class as derived from Sys.Component.
Ninemsn.VE.MapMode.registerClass('Ninemsn.VE.MapMode', Sys.Component);

Ninemsn.VE.MapLineMode = function()
{
    // Initialize as a base class.
    Ninemsn.VE.MapLineMode.initializeBase(this);
}
Ninemsn.VE.MapLineMode.prototype = 
{
    onEnter : function(map)
    {
        // Ninemsn.VE.MapExtender.callBaseMethod(this,"onEnter", map);
        // apply stylesheet to the map to change the cursor.
        Sys.UI.DomElement.addCssClass(map.get_element(), "LineMode");
    },
    onExit : function(map)
    {
        // Ninemsn.VE.MapExtender.callBaseMethod(this,"onExit", map);
        // remove stylesheet from the map to change the cursor.
        Sys.UI.DomElement.removeCssClass(map.get_element(), "LineMode");
    },
    onMapClick : function(map, e)
    {
        // Ninemsn.VE.MapExtender.callBaseMethod(this,"onMapClick", map, e);
        if(DEBUG) alert('click event in line mode');
        
        if(e.leftMouseButton)
        {
            // Find layer & line
            var line = map.getRouteLine();
            
            // translate click position to latlong.
            var pt = map.get_LatLong( e.mapX, e.mapY);
            // add point
            line.Points.push(pt);
            
            map.updateRoutePins();
            
            map.saveLayers();
            map.drawLayers();
        }
    },
    onMapDoubleClick : function(map, e)
    {
        //Ninemsn.VE.MapExtender.callBaseMethod(this,"onMapDoubleClick", map, e);
        if (DEBUG) alert('double-click event in line mode. terminate mode.');
        map.changeMode();
    },
    onMapKeyUp : function(map, e)
    {
        if(e.ctrlkey  && e.keyCode == 44)
        {
            // ctrl-z
            map.removeLastRoutePoint();
        }
        if (DEBUG) alert('keyup event in line mode. keycode: ' + e.keyCode);
    }
}
Ninemsn.VE.MapLineMode.registerClass("Ninemsn.VE.MapLineMode", Ninemsn.VE.MapMode);

Ninemsn.VE.MapPinMode = function()
{
    // Initialize as a base class.
    Ninemsn.VE.MapPinMode.initializeBase(this);
    
    this.Title='';
    this.Description='';
    this.CustomIcon=null;
}
Ninemsn.VE.MapPinMode.prototype = 
{
    onEnter : function(map)
    {
        // apply stylesheet to the map to change the cursor.
        Sys.UI.DomElement.addCssClass(map.get_element(), "PinMode");
        Sys.UI.DomElement.addCssClass(map.get_element(), this.get_id());
    },
    onExit : function(map)
    {
        // remove stylesheet from the map to change the cursor.
        Sys.UI.DomElement.removeCssClass(map.get_element(), "PinMode");
        Sys.UI.DomElement.removeCssClass(map.get_element(), this.get_id());
    },
    onMapClick : function(map, e)
    {
        // alert('click event in pin mode');
        if(e.leftMouseButton)
        {
            // update out client model
            var layer = map.getEditLayer();
            // get start pins
            var iconUrl = map.get_BaseImageUrl() + '/' + this.CustomIcon;
            var pin = $create(Ninemsn.VE.Shape,{Title:this.Title,Description:this.Description, CustomIcon:iconUrl, CustomType:RouteCustomPinType});
            
            pin.Points[0] = map.get_LatLong( e.mapX, e.mapY);;
            layer.add_Shape(pin);
            
            // layers
            map.saveLayers();
            map.drawLayers();
        }
    },
    onMapKeyUp : function(map, e)
    {
        if(e.keyCode==1)
        {
            if(DEBUG) alert('Escape key detected, exiting pin mode.');
            map.changeMode();    
        }
    },
    get_Title:function(){return this.Title;},
    set_Title:function(value){this.Title = value;},
    get_Description:function(){return this.Description;},
    set_Description:function(value){this.Description = value;},
    get_CustomIcon:function(){return this.CustomIcon;},
    set_CustomIcon:function(value){this.CustomIcon = value;}
}
Ninemsn.VE.MapPinMode.registerClass("Ninemsn.VE.MapPinMode", Ninemsn.VE.MapMode);

// var _MapEditor;
// map behaviour control
Ninemsn.VE.MapExtender = function(element) 
{
    Ninemsn.VE.MapExtender.initializeBase(this, [element]);
    // Properties
    this._Map=null;
    this._Layers=null;
    this._Latitude=0;
    this._Longitude=0;
    this._Zoom=4;
    this._MapStyle="r";
    this._Fixed=false;
    this._ReadOnly=false;
    this._MapMode=VEMapMode.Mode2D;
    this._ShowSwitch=true;
    this._BaseImageUrl = '/'
    
    // initialize editor modes.
    this._Modes = new Hash();
    this._Modes.setItem('ReadMode',         $create(Ninemsn.VE.MapMode,     {id:"ReadMode"}));
    this._Modes.setItem('LineMode',         $create(Ninemsn.VE.MapLineMode, {id:"LineMode"}));
    this._Modes.setItem('WaterPinMode',     $create(Ninemsn.VE.MapPinMode,  {id:"WaterPinMode",     Title:"Water",      Description:"Drinking fountain.",           CustomIcon:"icon_water.png"}));
    this._Modes.setItem('ToiletPinMode',    $create(Ninemsn.VE.MapPinMode,  {id:"ToiletPinMode",    Title:"Toilet",     Description:"When nature calls.",           CustomIcon:"icon_toilet.png"}));
    this._Modes.setItem('ParkingPinMode',   $create(Ninemsn.VE.MapPinMode,  {id:"ParkingPinMode",   Title:"Parking",    Description:"Parking.",                     CustomIcon:"icon_parking.png"}));
    this._Modes.setItem('HomePinMode',      $create(Ninemsn.VE.MapPinMode,  {id:"HomePinMode",      Title:"Home",       Description:"Home, sweet home.",            CustomIcon:"icon_home.png"}));
//    this._Modes.setItem('FoodPinMode',      $create(Ninemsn.VE.MapPinMode,  {id:"FoodPinMode",      Title:"Food",       Description:"Food to keep you going.",      CustomIcon:"icon_food.png"}));
//    this._Modes.setItem('CafePinMode',      $create(Ninemsn.VE.MapPinMode,  {id:"CafePinMode",      Title:"Cafe",       Description:"Coffee to keep you awake.",    CustomIcon:"icon_cafe.png"}));
//    this._Modes.setItem('AtmPinMode',       $create(Ninemsn.VE.MapPinMode,  {id:"AtmPinMode",       Title:"ATM",        Description:"Money tree.",                  CustomIcon:"icon_atm.png"}));
//    this._Modes.setItem('InfoPinMode',      $create(Ninemsn.VE.MapPinMode,  {id:"InfoPinMode",      Title:"Info",       Description:"Information centre.",          CustomIcon:"icon_info.png"}));
    
    this._CurrentMode = null;
    this._DefaultMode = 'ReadMode';
    
    // use default mode.
    this.changeMode();
}

Ninemsn.VE.MapExtender.prototype = 
{
    initialize : function() 
    {
        Ninemsn.VE.MapExtender.callBaseMethod(this, "initialize");
        
        this.get_element().style.position="relative";
        
        this._Map=new VEMap(this.get_element().id);
        this._Map.LoadMap(new VELatLong(this._Latitude,this._Longitude), this._Zoom, 'r', this._Fixed, this._MapMode, this._ShowSwitch);
        this._Map.SetScaleBarDistanceUnit( VEDistanceUnit.Kilometers);
        
        // Problem: VE Map does not raise event under the correct class context.
        // Work-around: Uses global event handler with a global editor variable.
        // Limitation: One editor per page.        
        _MapEditor = this;
        
        // attach events
        this._Map.AttachEvent("onclick", onMapClick);   
        this._Map.AttachEvent("ondoubleclick", onMapDoubleClick);
        this._Map.AttachEvent("onkeyup", onMapKeyUp);

        // load layers from clientstate
        this._Layers = new Array();
        var json = Sys.Serialization.JavaScriptSerializer.deserialize(this.get_ClientState());
        
        //alert(this.get_ClientState());
        
        for(var i=0; i<json.length; i++)
        {
            Array.add( this._Layers, $create( Ninemsn.VE.ShapeLayer, json[i]));
        }

        // deserialize layers and DEBUG
        this.drawLayers(true);
        
        // Move map
//        var layer = this.getEditLayer();
//        layer.draw(this._Map);
//        var velayer = layer.toVEShapeLayer();
//        if (velayer.GetShapeCount())
//        {
//            this._Map.SetMapView(velayer.GetBoundingRectangle());
//        }
    },
    dispose : function() 
    {
        if(this._Map!=null)this._Map.Dispose();
        Ninemsn.VE.MapExtender.callBaseMethod(this,"dispose");
    },
    changeMode:function(mode)
    {
        // get default mode.
        if(typeof(mode)=='undefined') mode=this._DefaultMode;
        // convert string into mode object
        if(typeof(mode)=='string') mode=this._Modes.getItem(mode);
        
        if (this._CurrentMode!==mode)
        {
            // exit old mode
            if (this._CurrentMode!==null)
            {
                this._CurrentMode.onExit(this);
            }
            
            // enter new mode
            if(typeof(mode)=='undefined')
            {
                this._CurrentMode = null;
            }
            else
            {
                this._CurrentMode = mode;
                this._CurrentMode.onEnter(this);
            }
        }
        
    },
    onMapClick : function(e)
    {
        // action
        if (this._CurrentMode!=null) this._CurrentMode.onMapClick(this, e);
    },
    onMapDoubleClick : function(e)
    {
        // action and stop.
        if (this._CurrentMode!=null) this._CurrentMode.onMapDoubleClick(this, e);
    },
    onMapKeyUp : function(e)
    {
        // esc key = stop mode.
        // delete key = delete polyline point.
        if (this._CurrentMode!=null) this._CurrentMode.onMapKeyUp(this, e);
    },
    get_Map:function(){return this._Map;},
    get_LatLong:function(x,y)
    {
        var vept = this.get_Map().PixelToLatLong(new VEPixel( x, y));
        var pt = $create(Ninemsn.VE.LatLong);
        pt.Latitude = vept.Latitude;
        pt.Longitude = vept.Longitude;
        return pt;
    },
    get_ReadOnly:function(){return this._ReadOnly;},
    set_ReadOnly:function(value){this._ReadOnly=value;},
    get_Latitude:function(){return this._Latitude;},
    set_Latitude:function(value){this._Latitude = value;},
    get_Longitude:function(){return this._Longitude;},
    set_Longitude:function(value){this._Longitude = value;},
    get_Zoom:function(){return this._Zoom;},
    set_Zoom:function(value){this._Zoom = value;},
    get_Fixed:function(){return this._Fixed;},
    set_Fixed:function(value){this._Fixed = value;},
    get_MapMode:function(){return this._MapMode;},
    set_MapMode:function(value){this._MapMode = value;},
    get_ShowSwitch:function(){return this._ShowSwitch;},
    set_ShowSwitch:function(value){this._ShowSwitch = value;},
    get_BaseImageUrl:function(){return this._BaseImageUrl;},
    set_BaseImageUrl:function(value){this._BaseImageUrl = value;},
    drawLayers:function(changeView)
    {
        var json_layers = this._Layers;
        var ve_map = this._Map;
        
        ve_map.DeleteAllShapeLayers();
        ve_map.HideInfoBox();
        // draw layers
        for(var i=0; i<json_layers.length; i++)
        {
            // var velayer = json_layers[i].toVEShapeLayer()
            json_layers[i].draw(ve_map, changeView);
        }
    },
    saveLayers:function()
    {
        // push layers into clientstate
        var data = Sys.Serialization.JavaScriptSerializer.serialize(this._Layers);
        this.set_ClientState(data);
        
        if (DEBUG) alert(data);
    },
    getEditLayer:function()
    {
        var layer;
        if(this._Layers.length>0)
        {
            layer = this._Layers[0];
        }
        else
        {
            layer = $create(Ninemsn.VE.ShapeLayer, {id:'edit'});
            Array.add( this._Layers, layer);
        }
        return layer
        
    },
    getShapeByCustomType:function(type, layer)
    {
        for(var i=0;i<layer.Shapes.length; i++)
        {
            var shp=layer.Shapes[i];
            if(shp.get_CustomType()==type) return shp;
        }
        return null;
    },
    remove_Shape:function(shapeId)
    {
        var layer = this.getEditLayer();
        for(var i=0;i<layer.Shapes.length; i++)
        {
            var shp=layer.Shapes[i];
            if (shp.get_id()==shapeId)
            {
                Array.removeAt( layer.Shapes, i);
                break;
            }
        }
        // redraw
        this.saveLayers();
        this.drawLayers();
    },
    getRouteLine:function()
    {
        var layer=this.getEditLayer();
        var line=this.getShapeByCustomType(RouteLineType,layer);
        
        if(line==null)
        {
            line=$create(Ninemsn.VE.Shape, {Title:'Route line', ShapeType:1, CustomType:RouteLineType});
            layer.add_Shape(line);
        }
        return line;
    },
    removeLastRoutePoint:function()
    {
        var line = this.getRouteLine();
        if (line.Points.length > 0)
        {
            Array.removeAt( line.Points, line.Points.length-1);
        }
        
        this.updateRoutePins();
        
        this.saveLayers();
        this.drawLayers();
    },
    clearEditLayer:function()
    {
        // clear shapes in layer
        var layer = this.getEditLayer();
        
        Array.clear(layer.Shapes);
        this.saveLayers();
        this.drawLayers();
    },
    updateRoutePins:function()
    {
        var layer=this.getEditLayer();
        var line=this.getRouteLine();
        var baseUrl=this.get_BaseImageUrl();
    
        // update the info pins.
        // delete distance markers - reverse loop through shapes to filter out the pin types
        for( var i=layer.Shapes.length-1; i>=0; i--)
        {
            var shp = layer.Shapes[i];
            if(shp.get_CustomType()==RouteDistancePinType)
            {
                Array.removeAt(layer.Shapes, i);
            }
        }
        
        // format description with distance.
        var lineDistance = 0;            
        for( var i=1; i<line.Points.length; i++)
        {
            var pt1 = line.Points[i-1];
            var pt2 = line.Points[i];
            lineDistance += distance( pt1.get_Latitude(), pt1.get_Longitude(), pt2.get_Latitude(), pt2.get_Longitude());
        }
        
        // create distance markers
        var d=0; // distance to p2 in kms
        var j=0; // index distances array
        var marker_length = 1;
        
        if (lineDistance <= 15)     marker_length = 1;
        else if(lineDistance <= 50) marker_length = 5;
        else if(lineDistance <= 100) marker_length = 10;
        else if(lineDistance <= 1000) marker_length = 100;
        else if(lineDistance <= 10000) marker_length = 1000;
        else if(lineDistance <= 100000) marker_length = 10000;
        
        // var marker_distances = new Array(1,2,3,4,5,6,7,8,9,10,20,30,40,50,60,70,80,90,100,200,300,400,500,600,700,800,900,1000);
        var max_markers = 20;
        for( var i=1; i<line.Points.length; i++)
        {
            var p1 = line.Points[i-1];  // last point
            var p2 = line.Points[i];    // this point
            d += distance( p1.get_Latitude(), p1.get_Longitude(), p2.get_Latitude(), p2.get_Longitude()); // distance to p2 in kms
            
            for( ; j<max_markers && (j+1) * marker_length < d; j++)
            {   // loop while we have more marker 
                // and marker distance is lesser than the current d.
                var marker_distance = (j+1) * marker_length;
                var dd = d - marker_distance; // distance diff from last pt to the marker distance (kms)
                var brng = bearing( p2.Latitude, p2.Longitude, p1.Latitude, p1.Longitude); // bearing from p2 to p1 in radian.
                var marker_pt = destination( p2.Latitude, p2.Longitude, brng, dd);
                var marker = $create(Ninemsn.VE.Shape,{CustomIcon:null,CustomType:RouteDistancePinType});
                marker.set_Title( (marker_distance) + ' kms');
                marker.set_CustomIcon(  baseUrl+'/'+'icon_km.gif');
                marker.Points[0] = marker_pt;
                layer.add_Shape(marker);
            }
        }
        
        // create start and end pins
        var startPinUrl=baseUrl+'/'+'icon_start.gif';
        var endPinUrl=baseUrl+'/'+'icon_finish.gif';
        
        var startpin = this.getShapeByCustomType(RouteStartPinType,layer);
        if (line.Points.length>=1)
        {
            // get start pins
            if (startpin==null)
            {
                startpin = $create(Ninemsn.VE.Shape,{Title:'Start',Description:'Start here.',CustomIcon:startPinUrl,CustomType:RouteStartPinType});
                layer.add_Shape(startpin);
            }
            startpin.Points[0] = line.Points[0];
        }
        else if(startpin!==null)
        {
            Array.remove( layer.Shapes, startpin);
        }
        
        var endpin = this.getShapeByCustomType(RouteEndPinType,layer);
        if (line.Points.length>=2)
        {
            // get end pin
            if (endpin==null)
            {
                endpin = $create(Ninemsn.VE.Shape,{Title:'Finish', CustomIcon:endPinUrl,CustomType:RouteEndPinType});
                layer.add_Shape(endpin);
            }
            endpin.set_Description('Distance ' + lineDistance.toFixed(2) + ' km(s).');
            endpin.Points[0] = line.Points[line.Points.length-1];
            
            // hack: apply tooltip to global id
            var tooltipPanel = $get('Simba_ToolTip');
            if (tooltipPanel)
            {
                tooltipPanel.innerText = 'Distance ' + lineDistance.toFixed(2) + ' km(s).';
            }
        }
        else if(endpin!==null)
        {
            Array.remove( layer.Shapes, endpin);
        }
    }
}
Ninemsn.VE.MapExtender.registerClass("Ninemsn.VE.MapExtender", AjaxControlToolkit.BehaviorBase);

//
// HACK - VEMap does not raise event under the correct instance context. Therefore we cannot use class methods.
//
var _MapEditor = null
function onMapClick(e)
{
    // action
    _MapEditor.onMapClick(e);
}

function onMapDoubleClick(e)
{
    // action and stop.
    _MapEditor.onMapDoubleClick(e);
}

function onMapKeyUp(e)
{
    // esc key = stop mode.
    // delete key = delete polyline point.
    _MapEditor.onMapKeyUp(e);
}

function remove_Shape( shapeId)
{
    _MapEditor.remove_Shape(shapeId);
}

function toRad(value) {  // convert degrees to radians
  return value * Math.PI / 180;
}
function toDeg(value){// convert radians to degrees (signed)
    return value * 180 / Math.PI;
}
/*
 * ditto using Law of Cosines
 */
function distance(lat1, lon1, lat2, lon2) {
  var R = 6371; // earth's mean radius in km
  var lat1r = toRad(lat1);
  var lon1r = toRad(lon1);
  var lat2r = toRad(lat2);
  var lon2r = toRad(lon2);
  var d = Math.acos( Math.sin(lat1r) * Math.sin(lat2r) + Math.cos(lat1r) * Math.cos(lat2r) * Math.cos(toRad(lon2-lon1))) * R;
  return d;
}
///*
// * calculate (initial) bearing between two points. Return bearing in radian form.
// *
// * from: Ed Williams' Aviation Formulary, http://williams.best.vwh.net/avform.htm#Crs
// */
function bearing( lat1, lon1, lat2, lon2)
{
    var lat1r = toRad(lat1);
    var lat2r = toRad(lat2);
    var dlonr = toRad(lon2-lon1);
    
    var y = Math.sin(dlonr) * Math.cos(lat2r);
    var x = Math.cos(lat1r)*Math.sin(lat2r)-Math.sin(lat1r)*Math.cos(lat2r)*Math.cos(dlonr);
    return Math.atan2(y, x);
}
///*
// * calculate destination point given start point, initial bearing (rad) and distance (km)
// *   see http://williams.best.vwh.net/avform.htm#LL
// */
function destination( lat1, lon1, brng, d)
{
  var R = 6371; // earth's mean radius in km
  var lat1r = toRad(lat1);
  var lon1r = toRad(lon1);
  var lat2r = Math.asin( Math.sin(lat1r)*Math.cos(d/R) + Math.cos(lat1r)*Math.sin(d/R)*Math.cos(brng) );
  var lon2r = lon1r + Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1r), Math.cos(d/R)-Math.sin(lat1r)*Math.sin(lat2r));
  
  if (isNaN(lat2r) || isNaN(lon2r)) return null;
  return $create(Ninemsn.VE.LatLong, {Latitude:toDeg(lat2r), Longitude:toDeg(lon2r)});
}

Sys.Application.notifyScriptLoaded();
if(typeof(Sys)!=='undefined')Sys.Application.notifyScriptLoaded();